diff options
Diffstat (limited to 'src/devices/memory_device.rs')
-rw-r--r-- | src/devices/memory_device.rs | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/src/devices/memory_device.rs b/src/devices/memory_device.rs new file mode 100644 index 0000000..7c1aeda --- /dev/null +++ b/src/devices/memory_device.rs @@ -0,0 +1,152 @@ +use bedrock_core::*; + +type Page = [u8; 256]; + +macro_rules! fn_read_head { + ($fn_name:ident($offset:ident, $address:ident)) => { + pub fn $fn_name(&mut self) -> u8 { + let page_i = (self.$offset + (self.$address / 256)) as usize; + let byte_i = (self.$address % 256) as usize; + self.$address = self.$address.wrapping_add(1); + match self.pages.get(page_i) { + Some(page) => page[byte_i], + None => 0, + } + } + }; +} + +macro_rules! fn_write_head { + ($fn_name:ident($offset:ident, $address:ident)) => { + pub fn $fn_name(&mut self, byte: u8) { + let page_i = (self.$offset + (self.$address / 256)) as usize; + let byte_i = (self.$address % 256) as usize; + self.$address = self.$address.wrapping_add(1); + match self.pages.get_mut(page_i) { + Some(page) => page[byte_i] = byte, + None => if page_i < self.provisioned { + self.pages.resize(page_i + 1, [0; 256]); + self.pages[page_i][byte_i] = byte; + } + } + } + }; +} + + +pub struct MemoryDevice { + pub limit: u16, // maximum provisionable number of pages + pub requested: u16, // number of pages requested by program + pub provisioned: usize, // number of pages provisioned for use + pub pages: Vec<Page>, // all allocated pages + + pub offset_1: u16, + pub address_1: u16, + pub offset_2: u16, + pub address_2: u16, + + pub copy_length: u16, +} + +impl MemoryDevice { + pub fn new() -> Self { + Self { + limit: 0, + requested: 0, + provisioned: 0, + pages: Vec::new(), + + offset_1: 0, + address_1: 0, + offset_2: 0, + address_2: 0, + + copy_length: 0, + } + } + + fn_read_head! { read_head_1( offset_1, address_1) } + fn_read_head! { read_head_2( offset_2, address_2) } + fn_write_head!{ write_head_1(offset_1, address_1) } + fn_write_head!{ write_head_2(offset_2, address_2) } + + pub fn provision(&mut self) { + self.provisioned = std::cmp::min(self.requested, self.limit) as usize; + // Defer allocation of new pages. + self.pages.truncate(self.provisioned as usize); + } + + pub fn copy(&mut self) { + let src = self.offset_2 as usize; + let dest = self.offset_1 as usize; + let count = self.copy_length as usize; + + // Pre-allocate destination pages as needed. + let pages_needed = std::cmp::min(dest + count, self.provisioned); + if pages_needed > self.pages.len() { + self.pages.resize(pages_needed, [0; 256]); + } + + for i in 0..count { + let src_page = match self.pages.get(src + i) { + Some(src_page) => src_page.to_owned(), + None => [0; 256], + }; + match self.pages.get_mut(dest + i) { + Some(dest) => *dest = src_page, + None => break, + }; + } + } +} + +impl Device for MemoryDevice { + fn read(&mut self, port: u8) -> u8 { + match port { + 0x0 => self.read_head_1(), + 0x1 => self.read_head_1(), + 0x2 => read_h!(self.offset_1), + 0x3 => read_l!(self.offset_1), + 0x4 => read_h!(self.address_1), + 0x5 => read_l!(self.address_1), + 0x6 => read_h!(self.provisioned), + 0x7 => read_l!(self.provisioned), + 0x8 => self.read_head_2(), + 0x9 => self.read_head_2(), + 0xa => read_h!(self.offset_2), + 0xb => read_l!(self.offset_2), + 0xc => read_h!(self.address_2), + 0xd => read_l!(self.address_2), + 0xe => 0x00, + 0xf => 0x00, + _ => unreachable!(), + } + } + + fn write(&mut self, port: u8, value: u8) -> Option<Signal> { + match port { + 0x0 => self.write_head_1(value), + 0x1 => self.write_head_1(value), + 0x2 => write_h!(self.offset_1, value), + 0x3 => write_l!(self.offset_1, value), + 0x4 => write_h!(self.address_1, value), + 0x5 => write_l!(self.address_1, value), + 0x6 => write_h!(self.requested, value), + 0x7 => { write_l!(self.requested, value); self.provision(); }, + 0x8 => self.write_head_2(value), + 0x9 => self.write_head_2(value), + 0xa => write_h!(self.offset_2, value), + 0xb => write_l!(self.offset_2, value), + 0xc => write_h!(self.address_2, value), + 0xd => write_l!(self.address_2, value), + 0xe => write_h!(self.copy_length, value), + 0xf => { write_l!(self.copy_length, value); self.copy(); }, + _ => unreachable!(), + }; + return None; + } + + fn wake(&mut self) -> bool { + false + } +} |