use crate::*;
use bedrock_core::*;
use phosphor::*;
use std::time::*;
const FRAME: Duration = Duration::from_micros(16666);
pub struct BedrockEmulator {
vm: Processor<StandardDevices>,
process_mark: Instant,
frame_mark: Instant,
debug_mark: Instant,
waiting_to_start: bool,
is_paused: bool,
scale: u32,
cycles_since_debug: usize,
}
impl BedrockEmulator {
pub fn new(bytecode: &[u8]) -> Self {
let mut vm = Processor::new(StandardDevices::new());
vm.load_program(bytecode);
Self {
vm,
process_mark: Instant::now(),
frame_mark: Instant::now(),
debug_mark: Instant::now(),
waiting_to_start: true,
is_paused: false,
scale: 2,
cycles_since_debug: 0,
}
}
pub fn debug(&mut self) {
macro_rules! yellow {()=>{eprint!("\x1b[33m")};}
macro_rules! normal {()=>{eprint!("\x1b[0m")};}
macro_rules! print_stack {
($stack:expr, $len:expr) => {
for i in 0..$len {
if i == $stack.sp as usize { yellow!(); } else { normal!(); }
eprint!("{:02x} ", $stack.mem[i]);
}
normal!();
};
}
eprintln!("\n PC: 0x{:04x} Cycles: {} (+{} in {:.2?})",
self.vm.mem.pc, self.vm.cycles,
self.vm.cycles - self.cycles_since_debug,
self.debug_mark.elapsed());
eprint!("WST: ");
print_stack!(self.vm.wst, 0x10);
eprint!("\nRST: ");
print_stack!(self.vm.rst, 0x10);
eprintln!();
self.cycles_since_debug = self.vm.cycles;
self.debug_mark = Instant::now();
}
pub fn run(self) -> ! {
let mut wm = WindowManager::new();
wm.add_window(Box::new(self));
wm.run();
}
}
impl WindowController for BedrockEmulator {
fn title(&self) -> String {
String::from("Bedrock emulator")
}
fn exact_size(&self) -> Option<Dimensions> {
match self.vm.dev.screen.is_resizable {
true => None,
false => Some(Dimensions::new(
self.vm.dev.screen.dimensions.width as u32,
self.vm.dev.screen.dimensions.height as u32,
)),
}
}
fn is_cursor_visible(&self) -> bool {
let pos = self.vm.dev.input.mouse_position;
let dim = self.vm.dev.screen.dimensions;
pos.x >= dim.width || pos.y >= dim.height
}
fn pixel_scale(&self) -> NonZeroU32 {
NonZeroU32::new(self.scale).unwrap()
}
fn on_resize(&mut self, dimensions: Dimensions) {
self.vm.dev.screen.resize(ScreenDimensions {
width: dimensions.width as u16,
height: dimensions.height as u16,
});
self.waiting_to_start = false;
}
fn on_cursor_move(&mut self, position: Point) {
self.vm.dev.input.move_mouse(ScreenPosition::new(position.x as u16, position.y as u16));
}
fn on_cursor_exit(&mut self) {
self.vm.dev.input.move_mouse(ScreenPosition::new(0x7FFF, 0x7FFF));
}
fn on_left_mouse_button(&mut self, action: Action) {
self.vm.dev.input.mouse_button_action(0x80, action);
}
fn on_middle_mouse_button(&mut self, action: Action) {
self.vm.dev.input.mouse_button_action(0x40, action);
}
fn on_right_mouse_button(&mut self, action: Action) {
self.vm.dev.input.mouse_button_action(0x20, action);
}
fn on_line_scroll_horizontal(&mut self, delta: f64) {
self.vm.dev.input.on_scroll_horizontal(delta * 20.0);
}
fn on_line_scroll_vertical(&mut self, delta: f64) {
self.vm.dev.input.on_scroll_vertical(delta * 20.0);
}
fn on_pixel_scroll_horizontal(&mut self, delta: f64) {
self.vm.dev.input.on_scroll_horizontal(delta);
}
fn on_pixel_scroll_vertical(&mut self, delta: f64) {
self.vm.dev.input.on_scroll_vertical(delta);
}
fn on_character_input(&mut self, input: char) {
self.vm.dev.input.on_character_input(input);
}
fn on_keyboard_input(&mut self, input: KeyboardInput) {
self.vm.dev.input.on_keyboard_input(input);
if input.action.is_pressed() {
match input.key {
KeyCode::F1 => self.scale = std::cmp::max(1, self.scale - 1),
KeyCode::F2 => self.scale = std::cmp::min(8, self.scale + 1),
_ => (),
}
}
}
fn on_keyboard_modifier_change(&mut self, modifiers: ModifiersState) {
self.vm.dev.input.on_modifier_change(modifiers);
}
fn on_process(&mut self) {
let frame_remaining = match self.frame_mark.elapsed() < FRAME {
true => FRAME.saturating_sub(self.frame_mark.elapsed()),
false => FRAME,
};
if self.waiting_to_start || (self.is_paused && !self.vm.dev.can_wake()) {
let sleep_duration = match self.vm.dev.clock.shortest_active_timer() {
Some(ms) => std::cmp::min(frame_remaining, ms),
None => frame_remaining,
};
std::thread::sleep(sleep_duration);
self.process_mark = Instant::now();
return;
}
self.is_paused = false;
while self.process_mark.elapsed() < frame_remaining {
// std::thread::sleep(std::time::Duration::from_micros(1 * 1000 as u64));
if let Some(signal) = self.vm.evaluate(1000) {
match signal {
Signal::Debug => self.debug(),
Signal::Pause => { self.is_paused = true; break },
Signal::Halt => exit(0),
}
}
}
std::thread::sleep(frame_remaining.saturating_sub(self.process_mark.elapsed()));
self.process_mark = Instant::now();
}
fn render_request(&mut self) -> RenderRequest {
if self.vm.dev.screen.is_dirty {
match self.is_paused {
true => RenderRequest::UPDATE,
false => match self.frame_mark.elapsed() >= 6 * FRAME {
true => RenderRequest::UPDATE,
false => RenderRequest::NONE,
}
}
} else {
self.frame_mark = Instant::now();
RenderRequest::NONE
}
}
fn on_render(&mut self, buffer: &mut Buffer, _hint: RenderHint) {
self.vm.dev.screen.render(buffer);
self.frame_mark = Instant::now();
}
}