|
|
@ -69,15 +69,15 @@ impl Cpu { |
|
|
|
self.IP += Wrapping(1); |
|
|
|
} |
|
|
|
|
|
|
|
fn near_jump(&mut self, use_carry: bool, forward: bool, offset: u8) { |
|
|
|
fn near_jump(&mut self, forward: bool, use_carry: bool, offset: u4) { |
|
|
|
if use_carry && !self.C { |
|
|
|
self.IP += Wrapping(1); |
|
|
|
return; |
|
|
|
} |
|
|
|
if forward { |
|
|
|
self.IP += Wrapping(offset); |
|
|
|
self.IP += Wrapping(u8::from(offset) + 1); |
|
|
|
} else { |
|
|
|
self.IP -= Wrapping(offset); |
|
|
|
self.IP -= Wrapping(u8::from(offset) + 1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -331,13 +331,12 @@ impl Cpu { |
|
|
|
match insn { |
|
|
|
Instruction::Load => self.load(), |
|
|
|
Instruction::Store => self.store(), |
|
|
|
Instruction::NearJump { |
|
|
|
forward, |
|
|
|
use_carry, |
|
|
|
offset, |
|
|
|
} => self.near_jump(forward, use_carry, offset), |
|
|
|
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)) |
|
|
|
} |
|
|
|
Instruction::NearJumpForward { use_carry, offset } => { |
|
|
|
self.near_jump(use_carry, true, u8::from(offset)) |
|
|
|
} |
|
|
|
Instruction::FarJump { reg, use_carry } => self.far_jump(use_carry, reg), |
|
|
|
Instruction::CmpEqual => self.cmp_equal(), |
|
|
|
Instruction::CmpGreater => self.cmp_gt(), |
|
|
@ -408,8 +407,74 @@ mod tests { |
|
|
|
use std::num::Wrapping; |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_near_jump_backward_unconditional() { |
|
|
|
let mut cpu = Cpu::new(&[]); |
|
|
|
cpu.code[20] = 0b00100010; |
|
|
|
cpu.IP = Wrapping(20); |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.IP.0, 17); // instruction pointer has advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_near_jump_backward_conditional_must() { |
|
|
|
let mut cpu = Cpu::new(&[]); |
|
|
|
cpu.code[40] = 0b00110010; |
|
|
|
cpu.IP = Wrapping(40); |
|
|
|
cpu.C = true; |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.IP.0, 37); // instruction pointer had advanced
|
|
|
|
assert!(cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_near_jump_backward_conditional_must_not() { |
|
|
|
let mut cpu = Cpu::new(&[]); |
|
|
|
cpu.code[40] = 0b00110010; |
|
|
|
cpu.IP = Wrapping(40); |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.IP.0, 41); // instruction pointer had advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_near_jump_forward_unconditional() { |
|
|
|
let mut cpu = Cpu::new(&[0b00000010]); |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.IP.0, 0b11); // instruction pointer had advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
#[test] |
|
|
|
fn insn_near_jump_forward_conditional_must() { |
|
|
|
let mut cpu = Cpu::new(&[0b00010010]); |
|
|
|
cpu.C = true; |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.IP.0, 0b11); // instruction pointer had advanced
|
|
|
|
assert!(cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
#[test] |
|
|
|
fn insn_near_jump_forward_conditional_must_not() { |
|
|
|
let mut cpu = Cpu::new(&[0b00010010]); |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.IP.0, 1); // instruction pointer had advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_load() { |
|
|
|
let mut cpu = Cpu::new(&[0b00000000]); |
|
|
|
let mut cpu = Cpu::new(&[0b10001000]); |
|
|
|
cpu.R1 = Wrapping(0x2); |
|
|
|
cpu.data[0x2] = 0x77; |
|
|
|
{ |
|
|
@ -422,7 +487,7 @@ mod tests { |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_load_ignores_high_order_bytes_in_address() { |
|
|
|
let mut cpu = Cpu::new(&[0b00000000]); |
|
|
|
let mut cpu = Cpu::new(&[0b10001000]); |
|
|
|
cpu.R1 = Wrapping(0x72); |
|
|
|
cpu.data[0x2] = 0x77; |
|
|
|
{ |
|
|
@ -435,7 +500,7 @@ mod tests { |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_store() { |
|
|
|
let mut cpu = Cpu::new(&[0b00100000]); |
|
|
|
let mut cpu = Cpu::new(&[0b10001001]); |
|
|
|
cpu.R0 = Wrapping(0x12); |
|
|
|
cpu.R1 = Wrapping(0x3); |
|
|
|
{ |
|
|
@ -448,7 +513,7 @@ mod tests { |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_store_ignores_high_order_bytes_in_address() { |
|
|
|
let mut cpu = Cpu::new(&[0b00100000]); |
|
|
|
let mut cpu = Cpu::new(&[0b10001001]); |
|
|
|
cpu.R0 = Wrapping(0x12); |
|
|
|
cpu.R1 = Wrapping(0xD3); |
|
|
|
{ |
|
|
@ -461,7 +526,7 @@ mod tests { |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_store_load() { |
|
|
|
let mut cpu = Cpu::new(&[0b00100000, 0b00000000]); |
|
|
|
let mut cpu = Cpu::new(&[0b10001001, 0b10001000]); |
|
|
|
{ |
|
|
|
cpu.R0 = Wrapping(0x44); |
|
|
|
cpu.R1 = Wrapping(0x1); |
|
|
@ -514,74 +579,6 @@ mod tests { |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_near_jump_backward_unconditional() { |
|
|
|
let mut cpu = Cpu::new(&[]); |
|
|
|
cpu.code[20] = 0b10001010; |
|
|
|
cpu.IP = Wrapping(20); |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.IP.0, 18); // instruction pointer had advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_near_jump_backward_conditional_must() { |
|
|
|
let mut cpu = Cpu::new(&[]); |
|
|
|
cpu.code[40] = 0b10001110; |
|
|
|
cpu.IP = Wrapping(40); |
|
|
|
cpu.C = true; |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.IP.0, 38); // instruction pointer had advanced
|
|
|
|
assert!(cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_near_jump_backward_conditional_must_not() { |
|
|
|
let mut cpu = Cpu::new(&[]); |
|
|
|
cpu.code[40] = 0b10001110; |
|
|
|
cpu.IP = Wrapping(40); |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.IP.0, 41); // instruction pointer had advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_near_jump_forward_unconditional() { |
|
|
|
let mut cpu = Cpu::new(&[0b10010101]); |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.IP.0, 0b101); // instruction pointer had advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_near_jump_forward_conditional_must() { |
|
|
|
let mut cpu = Cpu::new(&[0b10011011]); |
|
|
|
cpu.C = true; |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.IP.0, 0b011); // instruction pointer had advanced
|
|
|
|
assert!(cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_near_jump_forward_conditional_must_not() { |
|
|
|
let mut cpu = Cpu::new(&[0b10011011]); |
|
|
|
{ |
|
|
|
assert!(!cpu.step()); // CPU has not halted
|
|
|
|
assert_eq!(cpu.IP.0, 1); // instruction pointer had advanced
|
|
|
|
assert!(!cpu.C); // carry is not affected
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn insn_far_jump_unconditional_r0() { |
|
|
|
let mut cpu = Cpu::new(&[0b10000000]); |
|
|
|
cpu.R0 = Wrapping(0xA0); |
|
|
|