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, // 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 { 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 } }