From fb8cee8d526c0919b95cf0f86298dccb5b9b6c4e Mon Sep 17 00:00:00 2001 From: Ross Andrews Date: Sat, 19 Feb 2022 11:13:27 -0600 Subject: [PATCH] First part of Bus --- src/address.rs | 5 +++ src/bus.rs | 111 +++++++++++++++++++++++++++++++++++++++++++++++++ src/cpu.rs | 24 +++++------ src/main.rs | 1 + src/memory.rs | 39 +++++++++-------- 5 files changed, 150 insertions(+), 30 deletions(-) create mode 100644 src/bus.rs diff --git a/src/address.rs b/src/address.rs index 2e92d9b..eb04474 100644 --- a/src/address.rs +++ b/src/address.rs @@ -24,6 +24,11 @@ impl std::ops::Sub for Word { fn sub(self, rhs: i32) -> Self::Output { self + -rhs } } +impl std::ops::Sub for Word { + type Output = Word; + fn sub(self, rhs: Word) -> Self::Output { Word(self.0 - rhs.0) } +} + impl std::ops::SubAssign for Word { fn sub_assign(&mut self, rhs: i32) { *self = *self - rhs; } } diff --git a/src/bus.rs b/src/bus.rs new file mode 100644 index 0000000..30fb061 --- /dev/null +++ b/src/bus.rs @@ -0,0 +1,111 @@ +use crate::memory::PeekPoke; +use crate::address::Word; + +pub trait Device { + fn tick(&mut self); + fn reset(&mut self); +} + +pub struct Bus { + start: Word, + end: Word, + device: A, + rest: B +} + +impl Bus { + fn new(start: u32, end: u32, device: A, rest: B) -> Self { + Self { start: start.into(), end: end.into(), device, rest } + } + + fn at(addr: u32, device: A, rest: B) -> Self { + Self::new(addr, addr, device, rest) + } +} + +impl PeekPoke for Bus { + fn peek(&self, addr: Word) -> u8 { + if addr >= self.start && addr <= self.end { + self.device.peek(addr - self.start) + } else { + self.rest.peek(addr) + } + } + + fn poke(&mut self, addr: Word, val: u8) { + if addr >= self.start && addr <= self.end { + self.device.poke(addr - self.start, val) + } else { + self.rest.poke(addr, val) + } + } +} + +impl Device for Bus { + fn tick(&mut self) { + self.device.tick(); + self.rest.tick(); + } + + fn reset(&mut self) { + self.device.reset(); + self.rest.reset(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + struct TestDevice(i32); + impl Device for TestDevice { + fn tick(&mut self) { self.0 += 1 } + fn reset(&mut self) { self.0 = 10 } + } + + struct ArrayDevice([u8; 10]); + impl PeekPoke for ArrayDevice { + fn peek(&self, addr: Word) -> u8 { self.0[usize::from(addr)] } + fn poke(&mut self, addr: Word, val: u8) { self.0[usize::from(addr)] = val } + } + + #[test] + fn test_tick() { + let device1 = TestDevice(5); + let device2 = TestDevice(6); + let device3 = TestDevice(7); + let mut bus = Bus::at(5, device1, Bus::at(6, device2, device3)); + + for _ in 0..5 { bus.tick() } + assert_eq!(bus.device.0, 10); + assert_eq!(bus.rest.device.0, 11); + assert_eq!(bus.rest.rest.0, 12); + } + + #[test] + fn test_reset() { + let device1 = TestDevice(5); + let device2 = TestDevice(6); + let mut bus = Bus::at(5, device1, device2); + + bus.reset(); + assert_eq!(bus.device.0, 10); + assert_eq!(bus.rest.0, 10); + } + + #[test] + fn test_poke_peek() { + let mut bus = Bus::new(5, 10, ArrayDevice([0u8; 10]), ArrayDevice([0u8; 10])); + bus.poke_u32(2, 2); // Goes into the 2nd device + bus.poke_u32(6, 6); // Goes into the first device... + assert_eq!(bus.device.0[1], 6); // At index 1 + assert_eq!(bus.rest.0[2], 2); // Second device gets the other write + + // Neither device sees the other's write: + assert_eq!(bus.device.0[2], 0); + assert_eq!(bus.rest.0[1], 0); + + assert_eq!(bus.peek_u32(2), 2); // Reading from the first device + assert_eq!(bus.peek_u32(6), 6); // And the second + } +} \ No newline at end of file diff --git a/src/cpu.rs b/src/cpu.rs index 87cf70a..011bb80 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -141,8 +141,8 @@ impl CPU { self.push_data(x); self.push_data(y) } - Opcode::Store => { self.memory.poke(x, y as u8) } - Opcode::Storew => { self.memory.poke24(x, y) } + Opcode::Store => { self.memory.poke(x.into(), y as u8) } + Opcode::Storew => { self.memory.poke24(x.into(), y) } Opcode::Setsdp => { self.dp = x.into(); self.sp = y.into() @@ -189,11 +189,11 @@ impl CPU { Opcode::Hlt => { self.halted = true } Opcode::Load => { let x = self.pop_data(); - self.push_data(self.memory.peek(x) as u32) + self.push_data(self.memory.peek(x.into()) as u32) } Opcode::Loadw => { let x = self.pop_data(); - self.push_data(self.memory.peek24(x)) + self.push_data(self.memory.peek24(x.into())) } Opcode::Inton => { self.int_enabled = true } Opcode::Intoff => { self.int_enabled = false } @@ -436,8 +436,8 @@ mod tests { let mut cpu = CPU::new(Memory::default()); cpu.push_data(37u32); cpu.push_data(45u32); - assert_eq!(cpu.memory.peek24(256), 37); - assert_eq!(cpu.memory.peek24(259), 45); + assert_eq!(cpu.memory.peek24_u32(256), 37); + assert_eq!(cpu.memory.peek24_u32(259), 45); cpu.push_call(12u32); cpu.push_call(34u32); @@ -458,12 +458,12 @@ mod tests { #[test] fn test_cpu_fetch() { let mut cpu = CPU::new(Memory::default()); - cpu.memory.poke(0x400, 0x01); // nop 1 arg - cpu.memory.poke(0x401, 0x02); // 2 - cpu.memory.poke(0x402, 0x07); // add 3 arg - cpu.memory.poke24(0x403, 0x123456); // 3-byte arg - cpu.memory.poke(0x406, 29 << 2); // hlt - cpu.memory.poke(0x407, 0xfc); // gibberish + cpu.memory.poke_u32(0x400, 0x01); // nop 1 arg + cpu.memory.poke_u32(0x401, 0x02); // 2 + cpu.memory.poke_u32(0x402, 0x07); // add 3 arg + cpu.memory.poke24_u32(0x403, 0x123456); // 3-byte arg + cpu.memory.poke_u32(0x406, 29 << 2); // hlt + cpu.memory.poke_u32(0x407, 0xfc); // gibberish assert_eq!(cpu.fetch(), Ok(Instruction { opcode: Opcode::Nop, arg: Some(2), length: 2 })); diff --git a/src/main.rs b/src/main.rs index 4788258..8e919c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ mod memory; mod address; mod opcodes; mod cpu; +mod bus; fn main() { println!("Hello, world!"); diff --git a/src/memory.rs b/src/memory.rs index 67e59a4..2f680e4 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -39,27 +39,30 @@ impl std::ops::IndexMut for Memory { } pub trait PeekPoke { - fn peek>(&self, addr: A) -> u8; - fn poke>(&mut self, addr: A, val: u8); + fn peek(&self, addr: Word) -> u8; + fn poke(&mut self, addr: Word, val: u8); - fn peek24>(&self, addr: A) -> u32 { - let addr = addr.into(); + fn peek24(&self, addr: Word) -> u32 { (self.peek(addr) as u32) | ((self.peek(addr + 1) as u32) << 8) | ((self.peek(addr + 2) as u32) << 16) } - fn poke24>(&mut self, addr: A, val: u32) { - let addr = addr.into(); + fn poke24(&mut self, addr: Word, val: u32) { self.poke(addr, val as u8); self.poke(addr + 1, (val >> 8) as u8); self.poke(addr + 2, (val >> 16) as u8); } + + fn peek_u32(&self, addr: u32) -> u8 { self.peek(addr.into()) } + fn poke_u32(&mut self, addr: u32, val: u8) { self.poke(addr.into(), val) } + fn peek24_u32(&mut self, addr: u32) -> u32 { self.peek24(addr.into()) } + fn poke24_u32(&mut self, addr: u32, val: u32) { self.poke24(addr.into(), val) } } impl PeekPoke for Memory { - fn peek>(&self, addr: A) -> u8 { self[addr.into()] } - fn poke>(&mut self, addr: A, val: u8) { self[addr.into()] = val; } + fn peek(&self, addr: Word) -> u8 { self[addr.into()] } + fn poke(&mut self, addr: Word, val: u8) { self[addr.into()] = val; } } #[cfg(test)] @@ -69,21 +72,21 @@ mod tests { #[test] fn test_mem_peek_poke() { let mut mem = Memory::default(); - assert_eq!(mem.peek(35), 0); - mem.poke(35, 45); - assert_eq!(mem.peek(35), 45); - assert_eq!(mem.peek(36), 0); + assert_eq!(mem.peek_u32(35), 0); + mem.poke_u32(35, 45); + assert_eq!(mem.peek_u32(35), 45); + assert_eq!(mem.peek_u32(36), 0); } #[test] fn test_mem_word_fns() { let mut mem = Memory::default(); - mem.poke24(10, 0x123456); - assert_eq!(mem.peek(10), 0x56); - assert_eq!(mem.peek(11), 0x34); - assert_eq!(mem.peek(12), 0x12); - assert_eq!(mem.peek24(10), 0x123456); - assert_eq!(mem.peek24(11), 0x001234); + mem.poke24(10.into(), 0x123456); + assert_eq!(mem.peek_u32(10), 0x56); + assert_eq!(mem.peek_u32(11), 0x34); + assert_eq!(mem.peek_u32(12), 0x12); + assert_eq!(mem.peek24(10.into()), 0x123456); + assert_eq!(mem.peek24(11.into()), 0x001234); } #[test]