mod system; mod memory; mod math; mod clock; mod input; mod screen; mod stream; mod file; 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 { 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; } }