summaryrefslogtreecommitdiff
path: root/src/emulator.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/emulator.rs')
-rw-r--r--src/emulator.rs293
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();
- }
-}