Rename Address to Word
This commit is contained in:
+19
-29
@@ -2,65 +2,55 @@
|
|||||||
pub const MEM_SIZE: u32 = 128 * 1024;
|
pub const MEM_SIZE: u32 = 128 * 1024;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub struct Address(u32);
|
pub struct Word(u32);
|
||||||
|
|
||||||
impl From<Address> for usize {
|
impl From<u32> for Word {
|
||||||
fn from(a: Address) -> Self { (a.0 & (MEM_SIZE-1)) as usize }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u32> for Address {
|
|
||||||
fn from(a: u32) -> Self { Self(a & 0xffffff) }
|
fn from(a: u32) -> Self { Self(a & 0xffffff) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<u32> for Address {
|
impl Into<u32> for Word {
|
||||||
fn into(self) -> u32 { self.0 }
|
fn into(self) -> u32 { self.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Add<i32> for Address {
|
impl std::ops::Add<i32> for Word {
|
||||||
type Output = Address;
|
type Output = Word;
|
||||||
fn add(self, rhs: i32) -> Self::Output {
|
fn add(self, rhs: i32) -> Self::Output {
|
||||||
Address::from((self.0 as i32).overflowing_add(rhs).0 as u32)
|
Word::from((self.0 as i32).overflowing_add(rhs).0 as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Sub<i32> for Address {
|
impl std::ops::Sub<i32> for Word {
|
||||||
type Output = Address;
|
type Output = Word;
|
||||||
fn sub(self, rhs: i32) -> Self::Output { self + -rhs }
|
fn sub(self, rhs: i32) -> Self::Output { self + -rhs }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::SubAssign<i32> for Address {
|
impl std::ops::SubAssign<i32> for Word {
|
||||||
fn sub_assign(&mut self, rhs: i32) { *self = *self - rhs; }
|
fn sub_assign(&mut self, rhs: i32) { *self = *self - rhs; }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::AddAssign<i32> for Address {
|
impl std::ops::AddAssign<i32> for Word {
|
||||||
fn add_assign(&mut self, rhs: i32) { *self = *self + rhs; }
|
fn add_assign(&mut self, rhs: i32) { *self = *self + rhs; }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_address_truncation() {
|
fn test_address_truncation() {
|
||||||
let a: Address = 0x11223344.into();
|
let a: Word = 0x11223344.into();
|
||||||
assert_eq!(a, 0x00223344.into());
|
assert_eq!(a, 0x00223344.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_addressing_arrays() {
|
|
||||||
let a: usize = Address::from(0xffffff).into();
|
|
||||||
assert_eq!(a, 0x01ffff as usize);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_address_overflows() {
|
fn test_address_overflows() {
|
||||||
let a = Address::from(0xfffffa);
|
let a = Word::from(0xfffffa);
|
||||||
assert_eq!(a + 10, Address(4));
|
assert_eq!(a + 10, Word(4));
|
||||||
|
|
||||||
let b = Address::from(3);
|
let b = Word::from(3);
|
||||||
assert_eq!(b - 10, Address(0xfffff9));
|
assert_eq!(b - 10, Word(0xfffff9));
|
||||||
|
|
||||||
let mut c = Address::from(5);
|
let mut c = Word::from(5);
|
||||||
c += 3;
|
c += 3;
|
||||||
assert_eq!(c, Address(8));
|
assert_eq!(c, Word(8));
|
||||||
|
|
||||||
let mut d = Address::from(5);
|
let mut d = Word::from(5);
|
||||||
d -= 3;
|
d -= 3;
|
||||||
assert_eq!(d, Address(2));
|
assert_eq!(d, Word(2));
|
||||||
}
|
}
|
||||||
+12
-12
@@ -1,16 +1,16 @@
|
|||||||
use crate::opcodes::Opcode;
|
use crate::opcodes::Opcode;
|
||||||
use crate::opcodes::InvalidOpcode;
|
use crate::opcodes::InvalidOpcode;
|
||||||
use crate::memory::Memory;
|
use crate::memory::Memory;
|
||||||
use crate::address::Address;
|
use crate::address::Word;
|
||||||
use crate::memory::PeekPoke;
|
use crate::memory::PeekPoke;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
struct CPU {
|
struct CPU {
|
||||||
memory: Memory, // Main memory, all of it
|
memory: Memory, // Main memory, all of it
|
||||||
pc: Address, // program counter, address of the low byte of the instruction
|
pc: Word, // program counter, address of the low byte of the instruction
|
||||||
dp: Address, // data pointer, address of the low byte of one cell above the data stack
|
dp: Word, // data pointer, address of the low byte of one cell above the data stack
|
||||||
sp: Address, // stack pointer, address of the low byte of the return stack
|
sp: Word, // stack pointer, address of the low byte of the return stack
|
||||||
iv: Address, // interrupt vector
|
iv: Word, // interrupt vector
|
||||||
int_enabled: bool, // interrupt enable bit
|
int_enabled: bool, // interrupt enable bit
|
||||||
halted: bool, // Whether the CPU is halted
|
halted: bool, // Whether the CPU is halted
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&mut self, instruction: Instruction) -> Address {
|
fn execute(&mut self, instruction: Instruction) -> Word {
|
||||||
if let Some(arg) = instruction.arg {
|
if let Some(arg) = instruction.arg {
|
||||||
self.push_data(arg)
|
self.push_data(arg)
|
||||||
}
|
}
|
||||||
@@ -251,7 +251,7 @@ mod tests {
|
|||||||
impl CPU {
|
impl CPU {
|
||||||
fn get_stack(&self) -> Vec<u32> {
|
fn get_stack(&self) -> Vec<u32> {
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
let mut curr = Address::from(256);
|
let mut curr = Word::from(256);
|
||||||
while curr < self.dp {
|
while curr < self.dp {
|
||||||
v.push(self.memory.peek24(curr));
|
v.push(self.memory.peek24(curr));
|
||||||
curr += 3
|
curr += 3
|
||||||
@@ -261,7 +261,7 @@ mod tests {
|
|||||||
|
|
||||||
fn get_call(&self) -> Vec<u32> {
|
fn get_call(&self) -> Vec<u32> {
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
let mut curr = Address::from(1024);
|
let mut curr = Word::from(1024);
|
||||||
while curr > self.sp {
|
while curr > self.sp {
|
||||||
curr -= 3;
|
curr -= 3;
|
||||||
v.push(self.memory.peek24(curr));
|
v.push(self.memory.peek24(curr));
|
||||||
@@ -287,7 +287,7 @@ mod tests {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_stack_opcode_test(given: Vec<u32>, given_r: Vec<u32>, opcode: Opcode, expected: Vec<u32>, expected_r: Vec<u32>, pc: Address) {
|
fn call_stack_opcode_test(given: Vec<u32>, given_r: Vec<u32>, opcode: Opcode, expected: Vec<u32>, expected_r: Vec<u32>, pc: Word) {
|
||||||
predicate_opcode_test(opcode, |cpu| {
|
predicate_opcode_test(opcode, |cpu| {
|
||||||
for i in given.into_iter() { cpu.push_data(i) }
|
for i in given.into_iter() { cpu.push_data(i) }
|
||||||
for i in given_r.into_iter() { cpu.push_call(i) }
|
for i in given_r.into_iter() { cpu.push_call(i) }
|
||||||
@@ -298,7 +298,7 @@ mod tests {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn control_flow_opcode_test<A>(given: Vec<u32>, opcode: Opcode, expected_pc: A) where A: Into<Address> {
|
fn control_flow_opcode_test<A>(given: Vec<u32>, opcode: Opcode, expected_pc: A) where A: Into<Word> {
|
||||||
predicate_opcode_test(opcode, |cpu| {
|
predicate_opcode_test(opcode, |cpu| {
|
||||||
for i in given.into_iter() { cpu.push_data(i) }
|
for i in given.into_iter() { cpu.push_data(i) }
|
||||||
}, |cpu| {
|
}, |cpu| {
|
||||||
@@ -311,13 +311,13 @@ mod tests {
|
|||||||
|cpu| {
|
|cpu| {
|
||||||
for i in given.into_iter() { cpu.push_data(i) }
|
for i in given.into_iter() { cpu.push_data(i) }
|
||||||
for (offset, byte) in given_memory.into_iter().enumerate() {
|
for (offset, byte) in given_memory.into_iter().enumerate() {
|
||||||
cpu.memory.poke(Address::from(2048 + offset as u32), byte)
|
cpu.memory.poke(Word::from(2048 + offset as u32), byte)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|cpu| {
|
|cpu| {
|
||||||
if let Some(expected_memory) = expected_memory {
|
if let Some(expected_memory) = expected_memory {
|
||||||
for (offset, byte) in expected_memory.into_iter().enumerate() {
|
for (offset, byte) in expected_memory.into_iter().enumerate() {
|
||||||
let actual = cpu.memory.peek(Address::from(2048 + offset as u32));
|
let actual = cpu.memory.peek(Word::from(2048 + offset as u32));
|
||||||
assert_eq!(byte, actual, "At address 2048 + {}", offset)
|
assert_eq!(byte, actual, "At address 2048 + {}", offset)
|
||||||
}
|
}
|
||||||
assert_eq!(cpu.get_stack(), expected)
|
assert_eq!(cpu.get_stack(), expected)
|
||||||
|
|||||||
+36
-12
@@ -1,5 +1,5 @@
|
|||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use crate::address::Address;
|
use crate::address::Word;
|
||||||
use crate::address::MEM_SIZE;
|
use crate::address::MEM_SIZE;
|
||||||
|
|
||||||
pub struct Memory([u8; MEM_SIZE as usize]);
|
pub struct Memory([u8; MEM_SIZE as usize]);
|
||||||
@@ -8,6 +8,13 @@ impl Default for Memory {
|
|||||||
fn default() -> Self { Self([0u8; MEM_SIZE as usize]) }
|
fn default() -> Self { Self([0u8; MEM_SIZE as usize]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Word> for usize {
|
||||||
|
fn from(w: Word) -> Self {
|
||||||
|
let w: u32 = w.into();
|
||||||
|
(w & (MEM_SIZE-1)) as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<R: Rng> From<R> for Memory {
|
impl<R: Rng> From<R> for Memory {
|
||||||
fn from(mut rng: R) -> Self {
|
fn from(mut rng: R) -> Self {
|
||||||
let mut mem = Memory::default();
|
let mut mem = Memory::default();
|
||||||
@@ -18,25 +25,31 @@ impl<R: Rng> From<R> for Memory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Index<Address> for Memory {
|
impl std::ops::Index<Word> for Memory {
|
||||||
type Output = u8;
|
type Output = u8;
|
||||||
fn index(&self, index: Address) -> &Self::Output { &self.0[usize::from(index)] }
|
fn index(&self, index: Word) -> &Self::Output {
|
||||||
|
&self.0[usize::from(index)]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::IndexMut<Address> for Memory {
|
impl std::ops::IndexMut<Word> for Memory {
|
||||||
fn index_mut(&mut self, index: Address) -> &mut Self::Output { &mut self.0[usize::from(index)] }
|
fn index_mut(&mut self, index: Word) -> &mut Self::Output {
|
||||||
|
&mut self.0[usize::from(index)]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PeekPoke {
|
pub trait PeekPoke {
|
||||||
fn peek<A: Into<Address>>(&self, addr: A) -> u8;
|
fn peek<A: Into<Word>>(&self, addr: A) -> u8;
|
||||||
fn poke<A: Into<Address>>(&mut self, addr: A, val: u8);
|
fn poke<A: Into<Word>>(&mut self, addr: A, val: u8);
|
||||||
|
|
||||||
fn peek24<A: Into<Address>>(&self, addr: A) -> u32 {
|
fn peek24<A: Into<Word>>(&self, addr: A) -> u32 {
|
||||||
let addr = addr.into();
|
let addr = addr.into();
|
||||||
(self.peek(addr) as u32) | ((self.peek(addr + 1) as u32) << 8) | ((self.peek(addr + 2) as u32) << 16)
|
(self.peek(addr) as u32)
|
||||||
|
| ((self.peek(addr + 1) as u32) << 8)
|
||||||
|
| ((self.peek(addr + 2) as u32) << 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poke24<A: Into<Address>>(&mut self, addr: A, val: u32) {
|
fn poke24<A: Into<Word>>(&mut self, addr: A, val: u32) {
|
||||||
let addr = addr.into();
|
let addr = addr.into();
|
||||||
self.poke(addr, val as u8);
|
self.poke(addr, val as u8);
|
||||||
self.poke(addr + 1, (val >> 8) as u8);
|
self.poke(addr + 1, (val >> 8) as u8);
|
||||||
@@ -45,10 +58,14 @@ pub trait PeekPoke {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PeekPoke for Memory {
|
impl PeekPoke for Memory {
|
||||||
fn peek<A: Into<Address>>(&self, addr: A) -> u8 { self[addr.into()] }
|
fn peek<A: Into<Word>>(&self, addr: A) -> u8 { self[addr.into()] }
|
||||||
fn poke<A: Into<Address>>(&mut self, addr: A, val: u8) { self[addr.into()] = val; }
|
fn poke<A: Into<Word>>(&mut self, addr: A, val: u8) { self[addr.into()] = val; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mem_peek_poke() {
|
fn test_mem_peek_poke() {
|
||||||
let mut mem = Memory::default();
|
let mut mem = Memory::default();
|
||||||
@@ -68,3 +85,10 @@ fn test_mem_word_fns() {
|
|||||||
assert_eq!(mem.peek24(10), 0x123456);
|
assert_eq!(mem.peek24(10), 0x123456);
|
||||||
assert_eq!(mem.peek24(11), 0x001234);
|
assert_eq!(mem.peek24(11), 0x001234);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_addressing_arrays() {
|
||||||
|
let a: usize = Word::from(0xffffff).into();
|
||||||
|
assert_eq!(a, 0x01ffff as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user