diff options
Diffstat (limited to 'src/emulator.rs')
-rw-r--r-- | src/emulator.rs | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/src/emulator.rs b/src/emulator.rs new file mode 100644 index 0000000..91d33e9 --- /dev/null +++ b/src/emulator.rs @@ -0,0 +1,194 @@ +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(); + } +} |