diff options
author | Ben Bridle <bridle.benjamin@gmail.com> | 2024-10-28 20:25:01 +1300 |
---|---|---|
committer | Ben Bridle <bridle.benjamin@gmail.com> | 2024-10-28 20:29:12 +1300 |
commit | 1a830a3d1b9d99653322d5ae49ea8165de7ed9d0 (patch) | |
tree | 798e77b6fcf2438b1c2538a67efe856a2f7cb979 /src/devices.rs | |
parent | 03c4b069e1806af256730639cefdae115b24401a (diff) | |
download | bedrock-pc-1a830a3d1b9d99653322d5ae49ea8165de7ed9d0.zip |
Rewrite emulatorv1.0.0-alpha1
This is a complete rewrite and restructure of the entire emulator
project, as part of the effort in locking down the Bedrock specification
and in creating much better tooling for creating and using Bedrock
programs.
This commit adds a command-line argument scheme, an embedded assembler,
a headless emulator for use in non-graphical environments, deferred
window creation for programs that do not access the screen device,
and new versions of phosphor and bedrock-core. The new version of
phosphor supports multi-window programs, which will make it possible to
implement program forking in the system device later on, and the new
version of bedrock-core implements the final core specification.
Diffstat (limited to 'src/devices.rs')
-rw-r--r-- | src/devices.rs | 384 |
1 files changed, 19 insertions, 365 deletions
diff --git a/src/devices.rs b/src/devices.rs index 1d69cc7..2221152 100644 --- a/src/devices.rs +++ b/src/devices.rs @@ -1,366 +1,20 @@ -mod system; -mod memory; -mod math; -mod clock; -mod input; -mod screen; -mod stream; -mod file; +mod system_device; +mod memory_device; +mod math_device; +mod clock_device; +mod input_device; +mod screen_device; +mod local_device; +mod remote_device; +mod file_device; + +pub use system_device::SystemDevice; +pub use memory_device::MemoryDevice; +pub use math_device::MathDevice; +pub use clock_device::ClockDevice; +pub use input_device::InputDevice; +pub use screen_device::ScreenDevice; +pub use local_device::LocalDevice; +pub use remote_device::RemoteDevice; +pub use file_device::FileDevice; -use bedrock_core::*; -pub use system::ReadOnlyTextBuffer; -pub use screen::{ScreenDimensions, ScreenPosition}; - -const LEN_PROGRAM_MEMORY: u16 = 0xffff; -const LEN_WORKING_STACK: u8 = 0xff; -const LEN_RETURN_STACK: u8 = 0xff; -const CONNECTED_DEVICES: u16 = 0b_1111_1100_1100_0000; - - -pub struct StandardDevices { - pub memory: memory::MemoryDevice, - pub math: math::MathDevice, - pub clock: clock::ClockDevice, - pub input: input::InputDevice, - pub screen: screen::ScreenDevice, - pub stream: stream::StreamDevice, - pub file: file::FileDevice, - - pub name: ReadOnlyTextBuffer, - - pub wake_mask: u16, - pub wake_id: u8, -} - -impl StandardDevices { - pub fn new() -> Self { - Self { - memory: memory::MemoryDevice::new(), - math: math::MathDevice::new(), - clock: clock::ClockDevice::new(), - input: input::InputDevice::new(), - screen: screen::ScreenDevice::new(), - stream: stream::StreamDevice::new(), - file: file::FileDevice::new(), - - name: ReadOnlyTextBuffer::from_text("Bedrock for PC"), - - wake_mask: 0x0000, - wake_id: 0x00, - } - } - - pub fn can_wake(&mut self) -> bool { - macro_rules! test_wake { - ($flag:expr, $id:expr, $mask:expr) => { - if $flag && self.wake_mask & $mask != 0 { - $flag = false; self.wake_id = $id; return true } - }; - } - self.clock.update_timers(); - // The order here is important. If clock comes first, a fast - // enough recurring timer could block out all other events. - // It might be preferable to implement a queue system, so that - // the flags are tested in reverse order to how recently - // they were matched. - test_wake!(self.input.wake_flag, 0x4, 0x0800); - test_wake!(self.screen.wake_flag, 0x5, 0x0400); - test_wake!(self.stream.wake_flag, 0x8, 0x0080); - test_wake!(self.clock.wake_flag, 0x3, 0x1000); - return false; - } -} - -impl DeviceBus for StandardDevices { - fn read_u8(&mut self, port: u8) -> u8 { - macro_rules! read_hh { ($v:expr) => { ($v>>24) as u8 }; } - macro_rules! read_hl { ($v:expr) => { ($v>>16) as u8 }; } - macro_rules! read_lh { ($v:expr) => { ($v>>8) as u8 }; } - macro_rules! read_ll { ($v:expr) => { $v as u8 }; } - macro_rules! read_h { ($v:expr) => { ($v>>8) as u8 }; } - macro_rules! read_l { ($v:expr) => { $v as u8 }; } - macro_rules! read_b { ($b:expr) => { if $b { 0xff } else { 0x00 } }; } - macro_rules! no_read { () => { 0x00 }; } - - match port { - // System - 0x00 => self.name.read_byte(), - 0x01 => self.wake_id, - 0x02 => no_read!(), - 0x03 => no_read!(), - 0x04 => 0x00, - 0x05 => 0x00, - 0x06 => 0x00, - 0x07 => 0x00, - 0x08 => read_h!(LEN_PROGRAM_MEMORY), - 0x09 => read_l!(LEN_PROGRAM_MEMORY), - 0x0A => LEN_WORKING_STACK, - 0x0B => LEN_RETURN_STACK, - 0x0C => read_h!(CONNECTED_DEVICES), - 0x0D => read_l!(CONNECTED_DEVICES), - 0x0E => no_read!(), - 0x0F => no_read!(), - // Memory - 0x10 => read_h!(self.memory.page_1), - 0x11 => read_l!(self.memory.page_1), - 0x12 => read_h!(self.memory.address_1), - 0x13 => read_l!(self.memory.address_1), - 0x14 => self.memory.read_from_head_1(), - 0x15 => self.memory.read_from_head_1(), - 0x16 => read_h!(self.memory.page_limit), - 0x17 => read_l!(self.memory.page_limit), - 0x18 => read_h!(self.memory.page_2), - 0x19 => read_l!(self.memory.page_2), - 0x1A => read_h!(self.memory.address_2), - 0x1B => read_l!(self.memory.address_2), - 0x1C => self.memory.read_from_head_2(), - 0x1D => self.memory.read_from_head_2(), - 0x1E => no_read!(), - 0x1F => no_read!(), - // Math - 0x20 => no_read!(), - 0x21 => no_read!(), - 0x22 => no_read!(), - 0x23 => no_read!(), - 0x24 => no_read!(), - 0x25 => no_read!(), - 0x26 => no_read!(), - 0x27 => no_read!(), - 0x28 => read_h!(self.math.multiply_high()), - 0x29 => read_l!(self.math.multiply_high()), - 0x2A => read_h!(self.math.multiply_low()), - 0x2B => read_l!(self.math.multiply_low()), - 0x2C => read_h!(self.math.divide()), - 0x2D => read_l!(self.math.divide()), - 0x2E => read_h!(self.math.modulo()), - 0x2F => read_l!(self.math.modulo()), - // Clock - 0x30 => self.clock.year(), - 0x31 => self.clock.month(), - 0x32 => self.clock.day(), - 0x33 => self.clock.hour(), - 0x34 => self.clock.minute(), - 0x35 => self.clock.second(), - 0x36 => read_h!(self.clock.update_uptime()), - 0x37 => read_l!(self.clock.uptime), - 0x38 => read_h!(self.clock.update_timer_1()), - 0x39 => read_l!(self.clock.timer_1), - 0x3A => read_h!(self.clock.update_timer_2()), - 0x3B => read_l!(self.clock.timer_2), - 0x3C => read_h!(self.clock.update_timer_3()), - 0x3D => read_l!(self.clock.timer_3), - 0x3E => read_h!(self.clock.update_timer_4()), - 0x3F => read_l!(self.clock.timer_4), - // Input - 0x40 => read_h!(self.input.pointer_position.x), - 0x41 => read_l!(self.input.pointer_position.x), - 0x42 => read_h!(self.input.pointer_position.y), - 0x43 => read_l!(self.input.pointer_position.y), - 0x44 => read_b!(self.input.pointer_active), - 0x45 => self.input.pointer_buttons, - 0x46 => self.input.read_horizontal_scroll(), - 0x47 => self.input.read_vertical_scroll(), - 0x48 => 0xff, - 0x49 => self.input.text_queue.pop_front().unwrap_or(0), - 0x4A => self.input.modifiers, - 0x4B => self.input.navigation, - 0x4C => self.input.controller_1, - 0x4D => self.input.controller_2, - 0x4E => self.input.controller_3, - 0x4F => self.input.controller_4, - // Screen - 0x50 => read_h!(self.screen.cursor.x), - 0x51 => read_l!(self.screen.cursor.x), - 0x52 => read_h!(self.screen.cursor.y), - 0x53 => read_l!(self.screen.cursor.y), - 0x54 => read_h!(self.screen.dimensions.width), - 0x55 => read_l!(self.screen.dimensions.width), - 0x56 => read_h!(self.screen.dimensions.height), - 0x57 => read_l!(self.screen.dimensions.height), - 0x58 => no_read!(), - 0x59 => no_read!(), - 0x5A => no_read!(), - 0x5B => no_read!(), - 0x5C => no_read!(), - 0x5D => no_read!(), - 0x5E => no_read!(), - 0x5F => no_read!(), - // Stream - // 0x80 => todo!(), - // 0x81 => todo!(), - // 0x82 => todo!(), - // 0x83 => todo!(), - // 0x84 => todo!(), - // 0x85 => todo!(), - // 0x86 => todo!(), - // 0x87 => todo!(), - // 0x88 => todo!(), - // 0x89 => todo!(), - // 0x8A => todo!(), - // 0x8B => todo!(), - // 0x8C => todo!(), - // 0x8D => todo!(), - // 0x8E => todo!(), - // 0x8F => todo!(), - // File - 0x90 => read_b!(self.file.entry.is_some()), - 0x91 => read_b!(self.file.success), - 0x92 => self.file.name_buffer.read_byte(), - 0x93 => read_b!(self.file.entry_type()), - 0x94 => self.file.read_byte(), - 0x95 => self.file.read_byte(), - 0x96 => self.file.read_child_name(), - 0x97 => read_b!(self.file.child_type()), - 0x98 => read_hh!(self.file.pointer()), - 0x99 => read_hl!(self.file.pointer()), - 0x9A => read_lh!(self.file.pointer()), - 0x9B => read_ll!(self.file.pointer()), - 0x9C => read_hh!(self.file.length()), - 0x9D => read_hl!(self.file.length()), - 0x9E => read_lh!(self.file.length()), - 0x9F => read_ll!(self.file.length()), - - _ => unimplemented!("Reading from device port 0x{port:02x}"), - } - } - - fn write_u8(&mut self, val: u8, port: u8) -> Option<Signal> { - macro_rules! write_hh { ($v:expr) => { $v = $v & 0x00ffffff | ((val as u32) << 24) }; } - macro_rules! write_hl { ($v:expr) => { $v = $v & 0xff00ffff | ((val as u32) << 16) }; } - macro_rules! write_lh { ($v:expr) => { $v = $v & 0x00ff | ((val as u32) << 8) }; } - macro_rules! write_ll { ($v:expr) => { $v = $v & 0xff00 | (val as u32) }; } - macro_rules! write_h { ($v:expr) => { $v = $v & 0x00ff | ((val as u16) << 8) }; } - macro_rules! write_l { ($v:expr) => { $v = $v & 0xff00 | (val as u16) }; } - macro_rules! no_write { () => { () }; } - - match port { - // System - 0x00 => self.name.reset_pointer(), - 0x01 => no_write!(), - 0x02 => write_h!(self.wake_mask), - 0x03 => { write_l!(self.wake_mask); return Some(Signal::Sleep) }, - 0x04 => no_write!(), - 0x05 => no_write!(), - 0x06 => no_write!(), - 0x07 => no_write!(), - 0x08 => no_write!(), - 0x09 => no_write!(), - 0x0A => no_write!(), - 0x0B => no_write!(), - 0x0C => no_write!(), - 0x0D => no_write!(), - 0x0E => no_write!(), - 0x0F => no_write!(), - // Memory - 0x10 => write_h!(self.memory.page_1), - 0x11 => write_l!(self.memory.page_1), - 0x12 => write_h!(self.memory.address_1), - 0x13 => write_l!(self.memory.address_1), - 0x14 => self.memory.write_to_head_1(val), - 0x15 => self.memory.write_to_head_1(val), - 0x16 => no_write!(), - 0x17 => no_write!(), - 0x18 => write_h!(self.memory.page_2), - 0x19 => write_l!(self.memory.page_2), - 0x1A => write_h!(self.memory.address_2), - 0x1B => write_l!(self.memory.address_2), - 0x1C => self.memory.write_to_head_2(val), - 0x1D => self.memory.write_to_head_2(val), - 0x1E => write_h!(self.memory.copy_length), - 0x1F => { write_l!(self.memory.copy_length); self.memory.copy() }, - // Math - 0x20 => write_h!(self.math.operand_1), - 0x21 => write_l!(self.math.operand_1), - 0x22 => write_h!(self.math.operand_2), - 0x23 => write_l!(self.math.operand_2), - 0x24 => no_write!(), - 0x25 => no_write!(), - 0x26 => no_write!(), - 0x27 => no_write!(), - 0x28 => no_write!(), - 0x29 => no_write!(), - 0x2A => no_write!(), - 0x2B => no_write!(), - 0x2C => no_write!(), - 0x2D => no_write!(), - 0x2E => no_write!(), - 0x2F => no_write!(), - // Clock - 0x30 => no_write!(), - 0x31 => no_write!(), - 0x32 => no_write!(), - 0x33 => no_write!(), - 0x34 => no_write!(), - 0x35 => no_write!(), - 0x36 => no_write!(), - 0x37 => no_write!(), - 0x38 => write_h!(self.clock.timer_1), - 0x39 => { write_l!(self.clock.timer_1); self.clock.set_timer_1() }, - 0x3A => write_h!(self.clock.timer_2), - 0x3B => { write_l!(self.clock.timer_2); self.clock.set_timer_2() }, - 0x3C => write_h!(self.clock.timer_3), - 0x3D => { write_l!(self.clock.timer_3); self.clock.set_timer_3() }, - 0x3E => write_h!(self.clock.timer_4), - 0x3F => { write_l!(self.clock.timer_4); self.clock.set_timer_4() }, - // Input - 0x40 => no_write!(), - 0x41 => no_write!(), - 0x42 => no_write!(), - 0x43 => no_write!(), - 0x44 => no_write!(), - 0x45 => no_write!(), - 0x46 => no_write!(), - 0x47 => no_write!(), - 0x48 => no_write!(), - 0x49 => self.input.text_queue.clear(), - 0x4A => no_write!(), - 0x4B => no_write!(), - 0x4C => no_write!(), - 0x4D => no_write!(), - 0x4E => no_write!(), - 0x4F => no_write!(), - // Screen - 0x50 => write_h!(self.screen.cursor.x), - 0x51 => write_l!(self.screen.cursor.x), - 0x52 => write_h!(self.screen.cursor.y), - 0x53 => write_l!(self.screen.cursor.y), - 0x54 => write_h!(self.screen.dimensions.width), - 0x55 => { write_l!(self.screen.dimensions.width); self.screen.set_size() }, - 0x56 => write_h!(self.screen.dimensions.height), - 0x57 => { write_l!(self.screen.dimensions.height); self.screen.set_size() }, - 0x58 => self.screen.set_palette_high(val), - 0x59 => self.screen.set_palette_low(val), - 0x5A => self.screen.sprite_buffer.set_colour_high(val), - 0x5B => self.screen.sprite_buffer.set_colour_low(val), - 0x5C => self.screen.sprite_buffer.push(val), - 0x5D => self.screen.sprite_buffer.push(val), - 0x5E => self.screen.draw(val), - 0x5F => self.screen.shunt(val), - // Stream - 0x86 => self.stream.write_stdout(val), - 0x87 => self.stream.write_stdout(val), - // File - 0x90 => self.file.write_to_open_port(val), - 0x91 => self.file.write_to_move_port(val), - 0x92 => self.file.set_name_pointer(val), - 0x93 => self.file.ascend_to_parent(), - 0x94 => self.file.write_byte(val), - 0x95 => self.file.write_byte(val), - 0x96 => self.file.set_child_name_pointer(val), - 0x97 => self.file.descend_to_child(), - 0x98 => write_hh!(self.file.pointer), - 0x99 => write_hl!(self.file.pointer), - 0x9A => write_lh!(self.file.pointer), - 0x9B => { write_ll!(self.file.pointer); self.file.commit_pointer() }, - 0x9C => write_hh!(self.file.length), - 0x9D => write_hl!(self.file.length), - 0x9E => write_lh!(self.file.length), - 0x9F => { write_ll!(self.file.length); self.file.commit_length() }, - - _ => unimplemented!("Writing to device port 0x{port:02x}"), - }; - - return None; - - } -} |