diff options
Diffstat (limited to 'src/emulator.rs')
-rw-r--r-- | src/emulator.rs | 293 |
1 files changed, 0 insertions, 293 deletions
diff --git a/src/emulator.rs b/src/emulator.rs deleted file mode 100644 index d983378..0000000 --- a/src/emulator.rs +++ /dev/null @@ -1,293 +0,0 @@ -use crate::*; - -use bedrock_core::*; -use phosphor::*; - -use std::cmp::{min, max}; -use std::time::*; -use std::thread::sleep; - -const FRAME: Duration = Duration::from_micros(8_000); -const RENDER_WAIT: Duration = Duration::from_micros(500_000); -const LINE_HEIGHT: f64 = 20.0; - -macro_rules! u16 { ($u32:expr) => { $u32.try_into().unwrap_or(u16::MAX) }; } - - -pub struct BedrockEmulator { - vm: Processor<StandardDevices>, - title: String, - initialising: bool, - sleeping: bool, - pixel_scale: u32, - fullscreen: bool, - start_of_process: Instant, - end_of_render: Instant, - debug_mark: Instant, - cycles_elapsed: usize, -} - -impl BedrockEmulator { - pub fn new(bytecode: &[u8]) -> Self { - let mut vm = Processor::new(StandardDevices::new()); - vm.dev.screen.resize(ScreenDimensions::new(256, 192)); - vm.dev.memory.page_limit = 256; - vm.load_program(bytecode); - - Self { - vm, - title: String::from("Bedrock emulator"), - initialising: true, - sleeping: false, - pixel_scale: 3, - fullscreen: false, - start_of_process: Instant::now(), - end_of_render: Instant::now(), - debug_mark: Instant::now(), - cycles_elapsed: 0, - } - } - - pub fn with_title(mut self, title: &str) -> Self { - self.title = title.to_string(); - self - } - - pub fn run(self) -> ! { - let mut wm = WindowManager::new(); - wm.add_window(Box::new(self)); - wm.run(); - } - - pub fn debug(&mut self, variant: DebugVariant) { - self.vm.dev.stream.flush_local(); - - 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!(); - }; - } - match variant { - DebugVariant::DB1 => { - eprintln!("\n PC: 0x{:04x} Cycles: {} (+{} in {:.2?})", - self.vm.mem.pc, self.vm.cycles, - self.vm.cycles - self.cycles_elapsed, - self.debug_mark.elapsed()); - eprint!("WST: "); - print_stack!(self.vm.wst, 0x10); - eprint!("\nRST: "); - print_stack!(self.vm.rst, 0x10); - eprintln!(); - } - DebugVariant::DB2 => { - eprintln!("{:>8.2?}ms ({:>8} cycles)", - self.debug_mark.elapsed().as_micros() as f64 / 1000.0, - self.vm.cycles - self.cycles_elapsed) - } - DebugVariant::DB3 => { - // Only resets the debug timer - } - DebugVariant::DB4 => { - if self.vm.wst.sp == 1 - && self.vm.rst.sp == 0 - && self.vm.wst.mem[0] == 0xff { - print!(".") - } else { - print!("X") - } - } - _ => (), - } - self.cycles_elapsed = self.vm.cycles; - self.debug_mark = Instant::now(); - } -} - -impl WindowController for BedrockEmulator { - fn title(&self) -> String { - self.title.clone() - } - - fn exact_size(&self) -> Option<Dimensions> { - match self.vm.dev.screen.resizable { - true => None, - false => Some(Dimensions::new( - self.vm.dev.screen.dimensions.width as u32, - self.vm.dev.screen.dimensions.height as u32, - )), - } - } - - fn fullscreen(&self) -> bool { - self.fullscreen - } - - fn cursor(&mut self) -> Option<CursorIcon> { - let pos = self.vm.dev.input.pointer_position; - let dim = self.vm.dev.screen.dimensions; - match pos.x >= dim.width || pos.y >= dim.height { - true => Some(CursorIcon::Default), - false => None, - } - } - - fn pixel_scale(&self) -> NonZeroU32 { - NonZeroU32::new(self.pixel_scale).unwrap() - } - - fn on_resize(&mut self, dimensions: Dimensions) { - let width = u16!(dimensions.width); - let height = u16!(dimensions.height); - self.vm.dev.screen.resize(ScreenDimensions { width, height }); - self.initialising = false; - } - - fn on_cursor_move(&mut self, position: Point) { - let x = position.x as u16; - let y = position.y as u16; - self.vm.dev.input.on_pointer_move(ScreenPosition::new(x, y)); - self.vm.dev.input.pointer_active = true; - } - - fn on_cursor_enter(&mut self) { - self.vm.dev.input.pointer_active = true; - self.vm.dev.input.wake_flag = true; - } - - fn on_cursor_exit(&mut self) { - self.vm.dev.input.pointer_active = false; - self.vm.dev.input.wake_flag = true; - } - - fn on_left_mouse_button(&mut self, action: Action) { - self.vm.dev.input.on_pointer_button(0x80, action); - } - - fn on_middle_mouse_button(&mut self, action: Action) { - self.vm.dev.input.on_pointer_button(0x20, action); - } - - fn on_right_mouse_button(&mut self, action: Action) { - self.vm.dev.input.on_pointer_button(0x40, action); - } - - fn on_line_scroll_horizontal(&mut self, delta: f64) { - self.vm.dev.input.on_scroll_horizontal(delta); - } - - fn on_line_scroll_vertical(&mut self, delta: f64) { - self.vm.dev.input.on_scroll_vertical(delta); - } - - fn on_pixel_scroll_horizontal(&mut self, delta: f64) { - self.vm.dev.input.on_scroll_horizontal(delta / LINE_HEIGHT); - } - - fn on_pixel_scroll_vertical(&mut self, delta: f64) { - self.vm.dev.input.on_scroll_vertical(delta / LINE_HEIGHT); - } - - 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::F5 => self.pixel_scale = max(1, self.pixel_scale - 1), - KeyCode::F6 => self.pixel_scale = min(8, self.pixel_scale + 1), - KeyCode::F11 => self.fullscreen = !self.fullscreen, - _ => (), - } - } - } - - fn on_keyboard_modifier_change(&mut self, modifiers: ModifiersState) { - self.vm.dev.input.on_modifier_change(modifiers); - } - - fn on_process(&mut self) { - // Prevent program starting before the window receives an initial size. - if self.initialising { - sleep(FRAME); - return; - } - - if self.sleeping { - // Pause the processor until the current frame is rendered. This - // prevents the current frame from being overdrawn before rendering. - if self.vm.dev.screen.dirty { - sleep(FRAME); - return; - } - // Ensure a minimum delay of FRAME between the start of consecutive - // process frames when asleep, unless a timer has expired. - let frame_start = self.start_of_process + FRAME; - let time_to_frame_start = frame_start.duration_since(Instant::now()); - if !time_to_frame_start.is_zero() { - let time_to_sleep = match self.vm.dev.clock.time_to_next_wake() { - Some(ms) => min(ms, time_to_frame_start), - None => time_to_frame_start, - }; - sleep(time_to_sleep); - return; - } - // Stay asleep if there are no pending wake events. - if !self.vm.dev.can_wake() { - sleep(FRAME); - return; - } - } - - // Run the processor for the remainder of the frame. - self.start_of_process = Instant::now(); - self.sleeping = false; - let frame_end = Instant::now() + FRAME; - - while Instant::now() < frame_end { - if let Some(signal) = self.vm.evaluate(1000) { - match signal { - Signal::Debug(var) => self.debug(var), - Signal::Sleep => { - self.sleeping = true; - break; - }, - Signal::Halt => { - self.vm.dev.stream.flush_local(); - self.vm.dev.file.flush_entry(); - exit(0); - }, - } - } - } - self.vm.dev.stream.flush_local(); - self.vm.dev.file.flush_entry(); - } - - fn render_request(&mut self) -> RenderRequest { - if self.vm.dev.screen.dirty { - match self.sleeping { - true => RenderRequest::UPDATE, - false => match self.end_of_render.elapsed() >= RENDER_WAIT { - true => RenderRequest::UPDATE, - false => RenderRequest::NONE, - } - } - } else { - self.end_of_render = Instant::now(); - RenderRequest::NONE - } - } - - fn on_render(&mut self, buffer: &mut Buffer, _hint: RenderHint) { - self.vm.dev.screen.render(buffer); - self.end_of_render = Instant::now(); - } -} |