use crate::*; use bedrock_core::*; use phosphor::*; use std::time::*; const FRAME: Duration = Duration::from_micros(16666); pub struct BedrockEmulator { vm: Processor, 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 { 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(); } }