#[cfg(test)]
use super::regs::ADDR_MASK;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BusCommand {
B,
C,
D,
}
pub trait Bus {
fn read_nibble(&mut self, addr: u32) -> u8;
fn read_data(&mut self, addr: u32) -> u8 {
self.read_nibble(addr)
}
fn write_nibble(&mut self, addr: u32, nibble: u8);
fn read_in(&mut self) -> u16 {
0
}
fn write_out(&mut self, _out: u16) {}
fn config(&mut self, _addr: u32) {}
fn unconfig(&mut self, _addr: u32) {}
fn read_id(&mut self) -> u32 {
0
}
fn reset(&mut self) {}
fn shutdown(&mut self) {}
fn service_request(&mut self) -> u8 {
0
}
fn bus_command(&mut self, _cmd: BusCommand) {}
fn interrupt_pending(&mut self) -> bool {
false
}
}
#[cfg(test)]
pub const ADDRESS_SPACE: usize = 1 << 20;
#[cfg(test)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FlatMemory {
nibbles: Vec<u8>,
}
#[cfg(test)]
impl Default for FlatMemory {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
impl FlatMemory {
pub fn new() -> Self {
Self::with_size(ADDRESS_SPACE)
}
pub fn with_size(size: usize) -> Self {
Self {
nibbles: vec![0; size.min(ADDRESS_SPACE)],
}
}
pub fn len(&self) -> usize {
self.nibbles.len()
}
pub fn load(&mut self, addr: u32, data: &[u8]) {
for (i, &n) in data.iter().enumerate() {
let a = addr.wrapping_add(i as u32) & ADDR_MASK;
self.write_nibble(a, n);
}
}
}
#[cfg(test)]
impl Bus for FlatMemory {
fn read_nibble(&mut self, addr: u32) -> u8 {
self.nibbles
.get((addr & ADDR_MASK) as usize)
.copied()
.unwrap_or(0)
}
fn write_nibble(&mut self, addr: u32, nibble: u8) {
if let Some(slot) = self.nibbles.get_mut((addr & ADDR_MASK) as usize) {
*slot = nibble & 0xF;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flat_memory_bounds() {
let mut m = FlatMemory::with_size(16);
m.write_nibble(3, 0x1A);
assert_eq!(m.read_nibble(3), 0xA);
m.write_nibble(100, 5);
assert_eq!(m.read_nibble(100), 0);
assert_eq!(m.len(), 16);
let mut full = FlatMemory::new();
full.load(0xF_FFFF, &[1, 2]);
assert_eq!(full.read_nibble(0xF_FFFF), 1);
assert_eq!(full.read_nibble(0), 2);
}
}