|
|
@ -40,19 +40,13 @@ impl Cpu { |
|
|
|
format!("{:?}", decode(self.code[self.IP.0 as usize])) |
|
|
|
} |
|
|
|
|
|
|
|
fn load(&mut self, reg: Register, addr: u4) { |
|
|
|
match reg { |
|
|
|
Register::R0 => self.R0 = Wrapping(self.data[u8::from(addr) as usize]), |
|
|
|
Register::R1 => self.R1 = Wrapping(self.data[u8::from(addr) as usize]), |
|
|
|
} |
|
|
|
fn load(&mut self) { |
|
|
|
self.R0 = Wrapping(self.data[(self.R1.0 & 0xF) as usize]); |
|
|
|
self.IP += Wrapping(1); |
|
|
|
} |
|
|
|
|
|
|
|
fn store(&mut self, reg: Register, addr: u4) { |
|
|
|
match reg { |
|
|
|
Register::R0 => self.data[u8::from(addr) as usize] = self.R0.0, |
|
|
|
Register::R1 => self.data[u8::from(addr) as usize] = self.R1.0, |
|
|
|
} |
|
|
|
fn store(&mut self) { |
|
|
|
self.data[(self.R1.0 & 0xF) as usize] = self.R0.0; |
|
|
|
self.IP += Wrapping(1); |
|
|
|
} |
|
|
|
|
|
|
@ -335,8 +329,8 @@ impl Cpu { |
|
|
|
self.num_cycles += 1; |
|
|
|
let insn = decode(self.code[self.IP.0 as usize]); |
|
|
|
match insn { |
|
|
|
Instruction::Load { reg, addr } => self.load(reg, addr), |
|
|
|
Instruction::Store { reg, addr } => self.store(reg, addr), |
|
|
|
Instruction::Load => self.load(), |
|
|
|
Instruction::Store => self.store(), |
|
|
|
Instruction::LoadImmediate { low, reg, val } => self.load_imm(low, reg, val), |
|
|
|
Instruction::NearJumpBackward { use_carry, offset } => { |
|
|
|
self.near_jump(use_carry, false, u8::from(offset)) |
|
|
@ -367,8 +361,6 @@ impl Cpu { |
|
|
|
Instruction::BitShiftVar { right, mode } => self.bit_shift_var(right, mode), |
|
|
|
Instruction::Copy { reg_from } => self.copy(reg_from), |
|
|
|
Instruction::Setc { carry } => self.setc(carry), |
|
|
|
Instruction::Nop => self.nop(), |
|
|
|
Instruction::Halt => return true, |
|
|
|
Instruction::Cload { reg } => self.cload(reg), |
|
|
|
Instruction::Zero { reg } => self.zero(reg), |
|
|
|
Instruction::Div => self.div(), |
|
|
@ -377,6 +369,8 @@ impl Cpu { |
|
|
|
Instruction::PortReady => self.port_ready(), |
|
|
|
Instruction::ReadPort => self.read_port(), |
|
|
|
Instruction::WritePort => self.write_port(), |
|
|
|
Instruction::Halt => return true, |
|
|
|
Instruction::Nop | Instruction::Reserved => self.nop(), |
|
|
|
} |
|
|
|
false |
|
|
|
} |
|
|
@ -414,106 +408,78 @@ mod tests { |
|
|
|
use std::num::Wrapping; |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_load_r0() { |
|
|
|
let mut cpu = Cpu::new(&[0b00000001]); |
|
|
|
cpu.data[0b1] = 0x45; |
|
|
|
fn insn_load() { |
|
|
|
let mut cpu = Cpu::new(&[0b00000000]); |
|
|
|
cpu.R1 = Wrapping(0x2); |
|
|
|
cpu.data[0x2] = 0x77; |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.R0.0, 0x45); // data is loaded
|
|
|
|
assert_eq!(cpu.R0.0, 0x77); // data is loaded
|
|
|
|
assert_eq!(cpu.IP.0, 0x01); // instruction pointer has advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_load_r1() { |
|
|
|
let mut cpu = Cpu::new(&[0b00010101]); |
|
|
|
cpu.data[0b101] = 0x54; |
|
|
|
fn insn_load_ignores_high_order_bytes_in_address() { |
|
|
|
let mut cpu = Cpu::new(&[0b00000000]); |
|
|
|
cpu.R1 = Wrapping(0x72); |
|
|
|
cpu.data[0x2] = 0x77; |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.R1.0, 0x54); // data is loaded
|
|
|
|
assert_eq!(cpu.R0.0, 0x77); // data is loaded
|
|
|
|
assert_eq!(cpu.IP.0, 0x01); // instruction pointer has advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_load_r0_r1() { |
|
|
|
let mut cpu = Cpu::new(&[0b00001011, 0b00011100]); |
|
|
|
cpu.data[0b1011] = 0x12; |
|
|
|
cpu.data[0b1100] = 0x78; |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.R0.0, 0x12); // data is loaded
|
|
|
|
assert_eq!(cpu.IP.0, 0x01); // instruction pointer has advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.R1.0, 0x78); // data is loaded
|
|
|
|
assert_eq!(cpu.IP.0, 0x02); // instruction pointer has advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_store_r0() { |
|
|
|
let mut cpu = Cpu::new(&[0b00100100]); |
|
|
|
cpu.R0 = Wrapping(0x42); |
|
|
|
fn insn_store() { |
|
|
|
let mut cpu = Cpu::new(&[0b00100000]); |
|
|
|
cpu.R0 = Wrapping(0x12); |
|
|
|
cpu.R1 = Wrapping(0x3); |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.data[0b0100], 0x42); // data is stored
|
|
|
|
assert_eq!(cpu.data[0x3], 0x12); // data is stored
|
|
|
|
assert_eq!(cpu.IP.0, 0x01); // instruction pointer has advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_store_r1() { |
|
|
|
let mut cpu = Cpu::new(&[0b00110010]); |
|
|
|
cpu.R1 = Wrapping(0x29); |
|
|
|
fn insn_store_ignores_high_order_bytes_in_address() { |
|
|
|
let mut cpu = Cpu::new(&[0b00100000]); |
|
|
|
cpu.R0 = Wrapping(0x12); |
|
|
|
cpu.R1 = Wrapping(0xD3); |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.data[0b0010], 0x29); // data is stored
|
|
|
|
assert_eq!(cpu.data[0x3], 0x12); // data is stored
|
|
|
|
assert_eq!(cpu.IP.0, 0x01); // instruction pointer has advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_store_r0_r1() { |
|
|
|
let mut cpu = Cpu::new(&[0b00101111, 0b00110010]); |
|
|
|
cpu.R0 = Wrapping(0xA4); |
|
|
|
cpu.R1 = Wrapping(0xB3); |
|
|
|
fn insn_store_load() { |
|
|
|
let mut cpu = Cpu::new(&[0b00100000, 0b00000000]); |
|
|
|
{ |
|
|
|
cpu.R0 = Wrapping(0x44); |
|
|
|
cpu.R1 = Wrapping(0x1); |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.data[0b1111], 0xA4); // data is stored
|
|
|
|
assert_eq!(cpu.data[0x1], 0x44); // data is stored
|
|
|
|
assert_eq!(cpu.IP.0, 0x01); // instruction pointer has advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
{ |
|
|
|
cpu.R0 = Wrapping(0); |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.data[0b0010], 0xB3); // data is stored
|
|
|
|
assert_eq!(cpu.R0.0, 0x44); // data is loaded
|
|
|
|
assert_eq!(cpu.IP.0, 0x02); // instruction pointer has advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_store_and_load() { |
|
|
|
let mut cpu = Cpu::new(&[0b00100101, 0b00010101]); |
|
|
|
cpu.R0 = Wrapping(0xF3); |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.data[0b0101], 0xF3); // data is stored
|
|
|
|
} |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.R1.0, 0xF3); // same data is loaded
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_load_imm_r0() { |
|
|
|
let mut cpu = Cpu::new(&[0b01000101, 0b01100101]); |
|
|
|
{ |
|
|
|