use super::*;
struct Sys {
ram: Box<[u8]>,
res: bool,
irq: bool,
nmi: bool,
abort: bool,
}
impl Default for Sys {
fn default() -> Self {
Self {
ram: vec![0xEAu8; 0x1000000].into_boxed_slice(),
res: true,
irq: false,
nmi: false,
abort: false,
}
}
}
impl Sys {
fn res(&mut self) {
self.res = true;
}
fn nmi(&mut self) {
self.nmi = true;
}
fn irq(&mut self) {
self.irq = true;
}
fn write_code(&mut self, addr: u32, code: &[u8]) {
self.ram[addr as usize..addr as usize + code.len()].copy_from_slice(code);
}
}
impl System for Sys {
fn read(&mut self, a: u32, _at: AddressType, _signals: &Signals) -> u8 {
let d = self.ram[(a & 0xffffff) as usize];
println!("{:06x} -> {:02x}", a, d);
d
}
fn write(&mut self, a: u32, d: u8, at: AddressType, _signals: &Signals) {
println!("{:06x} <- {:02x}", a, d);
if at != AddressType::Invalid {
self.ram[(a & 0xffffff) as usize] = d;
}
}
fn res(&mut self) -> bool {
let x = self.res;
if x {
self.res = false;
}
x
}
fn nmi(&mut self) -> bool {
let x = self.nmi;
if x {
self.nmi = false;
}
x
}
fn irq(&mut self) -> bool {
let x = self.irq;
if x {
self.irq = false;
}
x
}
}
#[test]
fn reset() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
for _ in 0..7 {
cpu.cycle(&mut sys);
}
assert_eq!(cpu.pc, 0x8000, "CPU reset improperly");
assert_eq!(cpu.dbr, 00, "dbr");
assert_eq!(cpu.pbr, 00, "pbr");
assert_eq!(cpu.d, 0x0000, "d");
assert_eq!(cpu.s & 0xff00, 0x0100, "s");
assert_eq!(cpu.x & 0xff00, 0x0000, "x");
assert_eq!(cpu.y & 0xff00, 0x0000, "y");
assert_eq!(cpu.signals.e, cpu.flags.emulation, "emulation");
assert!(cpu.signals.e, "emulation");
assert!(cpu.signals.mx(false), "mx:m");
assert!(cpu.signals.mx(true), "mx:x");
assert!(cpu.flags.mem_sel, "m");
assert!(cpu.flags.index_sel, "x");
assert!(!cpu.flags.decimal, "d");
assert!(cpu.flags.interrupt_disable, "i");
assert!(cpu.flags.carry, "c");
}
#[bench]
fn nops_bench(b: &mut test::Bencher) {
use test::black_box;
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x02;
for _ in 0..7 {
cpu.cycle(&mut sys);
}
b.iter(|| {
let mut cpu = cpu.clone();
for _ in 0..2 {
cpu.cycle(&mut sys);
black_box(());
}
});
}
#[test]
fn init() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0x8000] = 0xA9;
sys.ram[0x8001] = 0x84;
sys.ram[0x8002] = 0xA0;
sys.ram[0x8003] = 0x95;
sys.ram[0x8004] = 0xA2;
sys.ram[0x8005] = 0x23;
for _ in 0..7 + 2 * 3 {
cpu.cycle(&mut sys);
}
assert_eq!(cpu.pc, 0x8006, "CPU reset improperly");
assert_eq!(cpu.dbr, 00, "dbr");
assert_eq!(cpu.pbr, 00, "pbr");
assert_eq!(cpu.d, 0x0000, "d");
assert_eq!(cpu.s & 0xff00, 0x0100, "s");
assert_eq!(cpu.a & 0x00ff, 0x0084, "a");
assert_eq!(cpu.x, 0x0023, "x");
assert_eq!(cpu.y, 0x0095, "y");
assert!(cpu.signals.e && cpu.flags.emulation, "emulation");
assert!(cpu.signals.e, "emulation");
assert!(cpu.signals.mx(false), "mx:m");
assert!(cpu.signals.mx(true), "mx:x");
assert!(cpu.flags.mem_sel, "m");
assert!(cpu.flags.index_sel, "x");
assert!(!cpu.flags.decimal, "d");
assert!(cpu.flags.interrupt_disable, "i");
assert!(cpu.flags.carry, "c");
assert!(!cpu.flags.zero, "z");
assert!(!cpu.flags.negative, "n");
}
#[test]
fn runs_forever_with_nop() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
for _ in 0..7 + (0xFFFF - 0xEAEA + 0x0100) * 2 {
cpu.cycle(&mut sys);
println!("{:02x}{:04x}: {:?}", cpu.pbr, cpu.pc, cpu.state);
}
assert_eq!(cpu.pc, 0x00ff);
assert_eq!(cpu.dbr, 00, "dbr");
assert_eq!(cpu.pbr, 00, "pbr");
assert_eq!(cpu.d, 0x0000, "d");
assert_eq!(cpu.s & 0xff00, 0x0100, "s");
assert_eq!(cpu.x & 0xff00, 0x0000, "x");
assert_eq!(cpu.y & 0xff00, 0x0000, "y");
assert!(cpu.signals.e && cpu.flags.emulation, "emulation");
assert!(cpu.signals.mx(false), "mx:m");
assert!(cpu.signals.mx(true), "mx:x");
assert!(cpu.flags.mem_sel, "m");
assert!(cpu.flags.index_sel, "x");
assert!(!cpu.flags.decimal, "d");
assert!(cpu.flags.interrupt_disable, "i");
assert!(cpu.flags.carry, "c");
}
#[test]
fn lda() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0x4000] = 12;
sys.ram[0x4001] = 34;
sys.write_code(
0x008000,
&[
0xA9, 0x00, 0xA0, 0x00, 0xA2, 0x00, 0x18, 0xFB, 0x18, 0xC2, 0x30, 0xAD, 0x00, 0x40, ],
);
for i in 0..7 + 20 {
cpu.cycle(&mut sys);
}
assert_eq!(cpu.pc, 0x800e);
assert_eq!(cpu.dbr, 00, "dbr");
assert_eq!(cpu.pbr, 00, "pbr");
assert_eq!(cpu.d, 0x0000, "d");
assert_eq!(cpu.s & 0xff00, 0x0100, "s");
assert_eq!(cpu.a, 0x220c, "a");
assert_eq!(cpu.x & 0xff00, 0x0000, "x");
assert_eq!(cpu.y & 0xff00, 0x0000, "y");
assert!(!(cpu.signals.e && cpu.flags.emulation), "emulation");
assert!(!cpu.signals.mx(false), "mx:m");
assert!(!cpu.signals.mx(true), "mx:x");
assert!(!cpu.flags.mem_sel, "m");
assert!(!cpu.flags.index_sel, "x");
assert!(!cpu.flags.decimal, "d");
assert!(cpu.flags.interrupt_disable, "i");
assert!(!cpu.flags.carry, "c");
assert!(!cpu.flags.zero, "z");
}
#[test]
fn st_zp() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0x4000] = 12;
sys.ram[0x4001] = 34;
const CODE: &[u8] = &[
0xA9, 0x00, 0x18, 0xFB, 0x18, 0xC2, 0x30, 0xAD, 0x00, 0x40, 0xE2, 0x30, 0x85, 0, ];
sys.write_code(0x008000, CODE);
for _ in 0..7 + 9 + 5 + 2 + 4 + 2 {
cpu.cycle(&mut sys);
}
assert_eq!(cpu.pc, 0x8000 + CODE.len() as u16);
assert_eq!(sys.ram[0], 12, "sta");
assert_eq!(sys.ram[1], 0xEA, "sta");
assert_eq!(cpu.dbr, 00, "dbr");
assert_eq!(cpu.pbr, 00, "pbr");
assert_eq!(cpu.d, 0x0000, "d");
assert_eq!(cpu.s & 0xff00, 0x0100, "s");
assert_eq!(cpu.a, 0x220c, "a");
assert_eq!(cpu.x & 0xff00, 0x0000, "x");
assert_eq!(cpu.y & 0xff00, 0x0000, "y");
assert!(!(cpu.signals.e && cpu.flags.emulation), "emulation");
assert!(cpu.signals.mx(false), "mx:m");
assert!(cpu.signals.mx(true), "mx:x");
assert!(cpu.flags.mem_sel, "m");
assert!(cpu.flags.index_sel, "x");
assert!(!cpu.flags.decimal, "d");
assert!(cpu.flags.interrupt_disable, "i");
assert!(!cpu.flags.carry, "c");
assert!(!cpu.flags.zero, "z");
}
#[test]
fn wai_irq_i_special_behavior() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
const CODE: &[u8] = &[
0x18, 0xA9, 0x00, 0xCB, 0xA9, 0xff, ];
sys.write_code(0x008000, CODE);
for _ in 0..7 + 2 + 5 {
cpu.cycle(&mut sys);
}
assert!(cpu.wai, "wai");
assert_eq!(cpu.a & 0xff, 0x00, "a");
for _ in 0..10 {
cpu.cycle(&mut sys);
}
sys.irq();
for _ in 0..2 {
cpu.cycle(&mut sys);
}
assert_eq!(cpu.pc, 0x8000 + CODE.len() as u16);
assert_eq!(cpu.dbr, 00, "dbr");
assert_eq!(cpu.pbr, 00, "pbr");
assert_eq!(cpu.d, 0x0000, "d");
assert_eq!(cpu.s & 0xff00, 0x0100, "s");
assert_eq!(cpu.a & 0xff, 0xff, "a");
assert_eq!(cpu.x & 0xff00, 0x0000, "x");
assert_eq!(cpu.y & 0xff00, 0x0000, "y");
assert!((cpu.signals.e && cpu.flags.emulation), "emulation");
assert!(cpu.signals.mx(false), "mx:m");
assert!(cpu.signals.mx(true), "mx:x");
assert!(cpu.flags.mem_sel, "m");
assert!(cpu.flags.index_sel, "x");
assert!(!cpu.flags.decimal, "d");
assert!(cpu.flags.interrupt_disable, "i");
assert!(!cpu.flags.carry, "c");
assert!(!cpu.flags.zero, "z");
}
#[test]
fn irq() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0xfffe] = 0x00;
sys.ram[0xffff] = 0x90;
const CODE: &[u8] = &[
0x18, 0xA9, 0x00, 0x58, 0xCB, 0xA9, 0xff, ];
const CODE2: &[u8] = &[
0xA9, 0x0F, 0x40, ];
sys.write_code(0x008000, CODE);
sys.write_code(0x009000, CODE2);
for _ in 0..7 + 2 + 2 + 5 {
println!("{:?}", cpu.state);
cpu.cycle(&mut sys);
}
println!("{:?}", cpu.state);
assert!(cpu.wai, "wai");
assert_eq!(cpu.a & 0xff, 0x00, "a");
for _ in 0..10 {
cpu.cycle(&mut sys);
}
sys.irq();
for _ in 0..7 {
cpu.cycle(&mut sys);
println!("{:?}", cpu.state);
}
assert_eq!(cpu.pc, 0x9000, "pc");
assert!(cpu.flags.interrupt_disable, "i");
for _ in 0..8 {
cpu.cycle(&mut sys);
println!("{:?} {:02x}", cpu.state, sys.ram[cpu.s as usize]);
}
assert_eq!(cpu.pc, 0x8000 + CODE.len() as u16 - 2);
assert_eq!(cpu.dbr, 00, "dbr");
assert_eq!(cpu.pbr, 00, "pbr");
assert_eq!(cpu.d, 0x0000, "d");
assert_eq!(cpu.s & 0xff00, 0x0100, "s");
assert_eq!(cpu.a & 0xff, 0x0f, "a");
assert_eq!(cpu.x & 0xff00, 0x0000, "x");
assert_eq!(cpu.y & 0xff00, 0x0000, "y");
assert!((cpu.signals.e && cpu.flags.emulation), "emulation");
assert!(cpu.signals.mx(false), "mx:m");
assert!(cpu.signals.mx(true), "mx:x");
assert!(cpu.flags.mem_sel, "m");
assert!(cpu.flags.index_sel, "x");
assert!(!cpu.flags.decimal, "d");
assert!(!cpu.flags.interrupt_disable, "i");
assert!(!cpu.flags.carry, "c");
assert!(cpu.flags.zero, "z");
}
fn run_cmp_imm_8(a: u8, m: u8) -> (bool, bool, bool) {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.write_code(0x008000, &[0xA9, a, 0xC9, m]);
for _ in 0..7 + 2 + 2 {
cpu.cycle(&mut sys);
}
(cpu.flags.negative, cpu.flags.zero, cpu.flags.carry)
}
fn run_cmp_imm_16(a: u16, m: u16) -> (bool, bool, bool) {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.write_code(
0x008000,
&[
0x18, 0xFB, 0x18, 0xC2, 0x30, 0xA9, (a & 0xff) as u8, (a >> 8) as u8, 0xC9, (m & 0xff) as u8, (m >> 8) as u8, ],
);
for _ in 0..7 + 2 + 2 + 2 + 3 + 3 + 3 {
cpu.cycle(&mut sys);
}
(cpu.flags.negative, cpu.flags.zero, cpu.flags.carry)
}
#[test]
fn cmp_immediate_8bit() {
let (n, z, c) = run_cmp_imm_8(0x40, 0x60);
assert!(n, "n: A<M");
assert!(!z, "z: A<M");
assert!(!c, "c: A<M");
let (n, z, c) = run_cmp_imm_8(0x50, 0x50);
assert!(!n, "n: A==M");
assert!(z, "z: A==M");
assert!(c, "c: A==M");
let (n, z, c) = run_cmp_imm_8(0x70, 0x50);
assert!(!n, "n: A>M");
assert!(!z, "z: A>M");
assert!(c, "c: A>M");
}
#[test]
fn cmp_immediate_16bit() {
let (n, z, c) = run_cmp_imm_16(0x1234, 0x2000);
assert!(n, "n: A<M");
assert!(!z, "z: A<M");
assert!(!c, "c: A<M");
let (n, z, c) = run_cmp_imm_16(0x1234, 0x1234);
assert!(!n, "n: A==M");
assert!(z, "z: A==M");
assert!(c, "c: A==M");
let (n, z, c) = run_cmp_imm_16(0x8000, 0x1000);
assert!(!n, "n: A>M");
assert!(!z, "z: A>M");
assert!(c, "c: A>M");
}
fn run_rol_acc_8(a: u8, carry_in: bool) -> (u8, bool, bool, bool) {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
let mut code = vec![0xA9u8, a]; if carry_in {
code.push(0x38); } else {
code.push(0x18); }
code.push(0x2A); sys.write_code(0x008000, &code);
for _ in 0..7 + 2 + 2 + 2 {
cpu.cycle(&mut sys);
}
(
(cpu.a & 0xff) as u8,
cpu.flags.carry,
cpu.flags.zero,
cpu.flags.negative,
)
}
fn run_ror_acc_8(a: u8, carry_in: bool) -> (u8, bool, bool, bool) {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
let mut code = vec![0xA9u8, a]; if carry_in {
code.push(0x38); } else {
code.push(0x18); }
code.push(0x6A); sys.write_code(0x008000, &code);
for _ in 0..7 + 2 + 2 + 2 {
cpu.cycle(&mut sys);
}
(
(cpu.a & 0xff) as u8,
cpu.flags.carry,
cpu.flags.zero,
cpu.flags.negative,
)
}
#[test]
fn rol_accumulator_8bit() {
let (a, c, z, n) = run_rol_acc_8(0x02, false);
assert_eq!(a, 0x04, "a");
assert!(!c, "c");
assert!(!z, "z");
assert!(!n, "n");
let (a, c, z, n) = run_rol_acc_8(0x02, true);
assert_eq!(a, 0x05, "a carry-in");
assert!(!c, "c carry-in");
assert!(!z, "z carry-in");
assert!(!n, "n carry-in");
let (a, c, z, n) = run_rol_acc_8(0x80, false);
assert_eq!(a, 0x00, "a carry-out");
assert!(c, "c carry-out");
assert!(z, "z carry-out");
assert!(!n, "n carry-out");
let (a, c, z, n) = run_rol_acc_8(0x40, false);
assert_eq!(a, 0x80, "a negative");
assert!(!c, "c negative");
assert!(!z, "z negative");
assert!(n, "n negative");
}
#[test]
fn ror_accumulator_8bit() {
let (a, c, z, n) = run_ror_acc_8(0x08, false);
assert_eq!(a, 0x04, "a");
assert!(!c, "c");
assert!(!z, "z");
assert!(!n, "n");
let (a, c, z, n) = run_ror_acc_8(0x02, true);
assert_eq!(a, 0x81, "a carry-in");
assert!(!c, "c carry-in");
assert!(!z, "z carry-in");
assert!(n, "n carry-in");
let (a, c, z, n) = run_ror_acc_8(0x01, false);
assert_eq!(a, 0x00, "a carry-out");
assert!(c, "c carry-out");
assert!(z, "z carry-out");
assert!(!n, "n carry-out");
let (a, c, z, n) = run_ror_acc_8(0x00, true);
assert_eq!(a, 0x80, "a negative");
assert!(!c, "c negative");
assert!(!z, "z negative");
assert!(n, "n negative");
}
#[test]
fn rol_accumulator_16bit() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.write_code(
0x008000,
&[
0x18, 0xFB, 0xC2, 0x20, 0xA9, 0x00, 0x40, 0x38, 0x2A, ],
);
for _ in 0..7 + 2 + 2 + 3 + 3 + 2 + 2 {
cpu.cycle(&mut sys);
}
assert_eq!(cpu.a, 0x8001, "a 16-bit rol");
assert!(!cpu.flags.carry, "c");
assert!(!cpu.flags.zero, "z");
assert!(cpu.flags.negative, "n");
}
#[test]
fn ror_accumulator_16bit() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.write_code(
0x008000,
&[
0x18, 0xFB, 0xC2, 0x20, 0xA9, 0x02, 0x00, 0x18, 0x6A, ],
);
for _ in 0..7 + 2 + 2 + 3 + 3 + 2 + 2 {
cpu.cycle(&mut sys);
}
assert_eq!(cpu.a, 0x0001, "a 16-bit ror");
assert!(!cpu.flags.carry, "c");
assert!(!cpu.flags.zero, "z");
assert!(!cpu.flags.negative, "n");
}
#[test]
fn rol_direct_8bit() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0x0010] = 0x42;
sys.write_code(0x008000, &[0x18, 0x26, 0x10]);
for _ in 0..7 + 2 + 5 {
cpu.cycle(&mut sys);
}
assert_eq!(sys.ram[0x0010], 0x84, "mem after rol direct");
assert!(!cpu.flags.carry, "c");
assert!(!cpu.flags.zero, "z");
assert!(cpu.flags.negative, "n");
}
#[test]
fn ror_direct_8bit() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0x0010] = 0x42;
sys.write_code(0x008000, &[0x38, 0x66, 0x10]);
for _ in 0..7 + 2 + 5 {
cpu.cycle(&mut sys);
}
assert_eq!(sys.ram[0x0010], 0xA1, "mem after ror direct");
assert!(!cpu.flags.carry, "c");
assert!(!cpu.flags.zero, "z");
assert!(cpu.flags.negative, "n");
}
#[test]
fn lsr_accumulator_8bit() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.write_code(0x008000, &[0xA9, 0x80, 0x4A]); for _ in 0..7 + 2 + 2 {
cpu.cycle(&mut sys);
}
assert_eq!((cpu.a & 0xff) as u8, 0x40, "a");
assert!(!cpu.flags.carry, "c");
assert!(!cpu.flags.zero, "z");
assert!(!cpu.flags.negative, "n");
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.write_code(0x008000, &[0xA9, 0x01, 0x4A]); for _ in 0..7 + 2 + 2 {
cpu.cycle(&mut sys);
}
assert_eq!((cpu.a & 0xff) as u8, 0x00, "a carry-out");
assert!(cpu.flags.carry, "c carry-out");
assert!(cpu.flags.zero, "z carry-out");
assert!(!cpu.flags.negative, "n always clear");
}
#[test]
fn tsb_direct_8bit() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0x0010] = 0x0F;
sys.write_code(0x008000, &[0xA9, 0xF0, 0x04, 0x10]); for _ in 0..7 + 2 + 5 {
cpu.cycle(&mut sys);
}
assert_eq!(sys.ram[0x0010], 0xFF, "mem after tsb");
assert!(cpu.flags.zero, "z (no overlap)");
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0x0010] = 0xFF;
sys.write_code(0x008000, &[0xA9, 0x0F, 0x04, 0x10]); for _ in 0..7 + 2 + 5 {
cpu.cycle(&mut sys);
}
assert_eq!(sys.ram[0x0010], 0xFF, "mem after tsb overlap");
assert!(!cpu.flags.zero, "z (overlap)");
}
#[test]
fn xba_test() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.write_code(0x008000, &[0xEB]); for _ in 0..7 {
cpu.cycle(&mut sys); }
cpu.a = 0xBBAA;
for _ in 0..3 {
cpu.cycle(&mut sys);
}
assert_eq!(cpu.a, 0xAABB, "a after xba");
assert!(cpu.flags.negative, "n flag (0xAA bit7=1)");
assert!(!cpu.flags.zero, "z flag");
}
#[test]
fn xba_zero_flag() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.write_code(0x008000, &[0xEB]); for _ in 0..7 {
cpu.cycle(&mut sys);
}
cpu.a = 0x00AA; for _ in 0..3 {
cpu.cycle(&mut sys);
}
assert_eq!(cpu.a, 0xAA00, "a after xba zero");
assert!(cpu.flags.zero, "z flag set when new A=0");
assert!(!cpu.flags.negative, "n flag clear");
}
#[test]
fn brl_zero_offset() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.write_code(0x008000, &[0x82, 0x00, 0x00]); for _ in 0..7 + 1 + 3 {
cpu.cycle(&mut sys);
}
assert_eq!(cpu.pc, 0x8003, "pc after brl +0");
assert_eq!(cpu.pbr, 0x00, "pbr unchanged");
}
#[test]
fn brl_forward_offset() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.write_code(0x008000, &[0x82, 0x10, 0x00]); for _ in 0..7 + 1 + 3 {
cpu.cycle(&mut sys);
}
assert_eq!(cpu.pc, 0x8013, "pc after brl +16");
}
#[test]
fn brl_backward_offset() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.write_code(0x008000, &[0x82, 0xFD, 0xFF]); for _ in 0..7 + 1 + 3 {
cpu.cycle(&mut sys);
}
assert_eq!(cpu.pc, 0x8000, "pc after brl -3");
}
#[test]
fn jml_indirect_long() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0x002000] = 0x34;
sys.ram[0x002001] = 0x12;
sys.ram[0x002002] = 0x05;
sys.write_code(0x008000, &[0xDC, 0x00, 0x20]); for _ in 0..7 + 1 + 5 {
cpu.cycle(&mut sys);
}
assert_eq!(cpu.pc, 0x1234, "pc after jml");
assert_eq!(cpu.pbr, 0x05, "pbr after jml");
}
#[test]
fn per_zero_offset() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.write_code(0x008000, &[0x62, 0x00, 0x00]); for _ in 0..7 + 1 + 5 {
cpu.cycle(&mut sys);
}
let s = cpu.s;
let lo = sys.ram[s as usize + 1] as u16;
let hi = sys.ram[s as usize + 2] as u16;
let pushed = (hi << 8) | lo;
assert_eq!(pushed, 0x8003, "per pushed value");
}
#[test]
fn trb_direct_8bit() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0x0010] = 0xFF;
sys.write_code(0x008000, &[0xA9, 0x0F, 0x14, 0x10]); for _ in 0..7 + 2 + 5 {
cpu.cycle(&mut sys);
}
assert_eq!(sys.ram[0x0010], 0xF0, "mem after trb");
assert!(!cpu.flags.zero, "z (overlap)");
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0x0010] = 0x0F;
sys.write_code(0x008000, &[0xA9, 0xF0, 0x14, 0x10]); for _ in 0..7 + 2 + 5 {
cpu.cycle(&mut sys);
}
assert_eq!(sys.ram[0x0010], 0x0F, "mem after trb no-overlap");
assert!(cpu.flags.zero, "z (no overlap)");
}
#[test]
fn mvn_basic() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0x010100] = 0xAA;
sys.ram[0x010101] = 0xBB;
sys.ram[0x010102] = 0xCC;
sys.write_code(0x008000, &[0x54, 0x02, 0x01]); for _ in 0..7 {
cpu.cycle(&mut sys); }
cpu.set_e(false);
cpu.set_p(0x00);
cpu.x = 0x0100;
cpu.y = 0x0200;
cpu.a = 0x0002; for _ in 0..7 * 3 {
cpu.cycle(&mut sys);
}
assert_eq!(sys.ram[0x020200], 0xAA, "byte 0");
assert_eq!(sys.ram[0x020201], 0xBB, "byte 1");
assert_eq!(sys.ram[0x020202], 0xCC, "byte 2");
assert_eq!(cpu.x, 0x0103, "x after mvn");
assert_eq!(cpu.y, 0x0203, "y after mvn");
assert_eq!(cpu.a, 0xFFFF, "c after mvn");
assert_eq!(cpu.dbr, 0x02, "dbr after mvn");
assert_eq!(cpu.pc, 0x8003, "pc after mvn");
}
#[test]
fn mvn_single_byte() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0x000100] = 0x42;
sys.write_code(0x008000, &[0x54, 0x00, 0x00]); for _ in 0..7 {
cpu.cycle(&mut sys);
}
cpu.set_e(false);
cpu.set_p(0x00);
cpu.x = 0x0100;
cpu.y = 0x0200;
cpu.a = 0x0000; for _ in 0..7 {
cpu.cycle(&mut sys);
}
assert_eq!(sys.ram[0x000200], 0x42, "byte copied");
assert_eq!(cpu.x, 0x0101, "x incremented");
assert_eq!(cpu.y, 0x0201, "y incremented");
assert_eq!(cpu.a, 0xFFFF, "c wraps to 0xFFFF");
assert_eq!(cpu.pc, 0x8003, "pc after mvn");
}
#[test]
fn mvp_basic() {
let mut cpu = CPU::new();
let mut sys = Sys::default();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
sys.ram[0x010100] = 0xAA;
sys.ram[0x010101] = 0xBB;
sys.ram[0x010102] = 0xCC;
sys.write_code(0x008000, &[0x44, 0x02, 0x01]); for _ in 0..7 {
cpu.cycle(&mut sys);
}
cpu.set_e(false);
cpu.set_p(0x00);
cpu.x = 0x0102; cpu.y = 0x0202; cpu.a = 0x0002; for _ in 0..7 * 3 {
cpu.cycle(&mut sys);
}
assert_eq!(sys.ram[0x020202], 0xCC, "byte 0 (from src end)");
assert_eq!(sys.ram[0x020201], 0xBB, "byte 1");
assert_eq!(sys.ram[0x020200], 0xAA, "byte 2 (from src start)");
assert_eq!(cpu.x, 0x00FF, "x after mvp (decremented 3)");
assert_eq!(cpu.y, 0x01FF, "y after mvp (decremented 3)");
assert_eq!(cpu.a, 0xFFFF, "c after mvp");
assert_eq!(cpu.dbr, 0x02, "dbr after mvp");
assert_eq!(cpu.pc, 0x8003, "pc after mvp");
}
fn boot(sys: &mut Sys) -> CPU {
let mut cpu = CPU::new();
sys.ram[0xfffc] = 0x00;
sys.ram[0xfffd] = 0x80;
for _ in 0..7 {
cpu.cycle(sys);
}
cpu
}
#[test]
fn sta_abs_long() {
let mut sys = Sys::default();
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x8F, 0x00, 0x50, 0x02]);
cpu.a = 0x0042;
for _ in 0..5 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x025000], 0x42, "sta abs long");
assert_eq!(cpu.pc, 0x8004);
}
#[test]
fn sta_abs_long_indexed_x() {
let mut sys = Sys::default();
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x9F, 0xFE, 0x4F, 0x02]);
cpu.a = 0x0043;
cpu.x = 0x0002;
for _ in 0..5 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x025000], 0x43, "sta abs long indexed x");
assert_eq!(cpu.pc, 0x8004);
}
#[test]
fn sta_abs_indexed_x() {
let mut sys = Sys::default();
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x9D, 0x00, 0x50]);
cpu.a = 0x0044;
cpu.x = 0x0004;
for _ in 0..5 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x005004], 0x44, "sta abs indexed x");
assert_eq!(cpu.pc, 0x8003);
}
#[test]
fn sta_abs_indexed_y() {
let mut sys = Sys::default();
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x99, 0x00, 0x50]);
cpu.a = 0x0045;
cpu.y = 0x0003;
for _ in 0..5 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x005003], 0x45, "sta abs indexed y");
assert_eq!(cpu.pc, 0x8003);
}
#[test]
fn sta_direct_indexed_x() {
let mut sys = Sys::default();
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x95, 0x10]);
cpu.a = 0x0046;
cpu.x = 0x0005;
for _ in 0..4 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x0015], 0x46, "sta direct indexed x");
assert_eq!(cpu.pc, 0x8002);
}
#[test]
fn sty_direct_indexed_x() {
let mut sys = Sys::default();
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x94, 0x10]);
cpu.y = 0x0047;
cpu.x = 0x0005;
for _ in 0..4 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x0015], 0x47, "sty direct indexed x");
assert_eq!(cpu.pc, 0x8002);
}
#[test]
fn stz_direct_indexed_x() {
let mut sys = Sys::default();
sys.ram[0x0015] = 0xFF;
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x74, 0x10]);
cpu.x = 0x0005;
for _ in 0..4 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x0015], 0x00, "stz direct indexed x");
assert_eq!(cpu.pc, 0x8002);
}
#[test]
fn stx_direct_indexed_y() {
let mut sys = Sys::default();
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x96, 0x10]);
cpu.x = 0x0048;
cpu.y = 0x0006;
for _ in 0..4 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x0016], 0x48, "stx direct indexed y");
assert_eq!(cpu.pc, 0x8002);
}
#[test]
fn sta_direct_indirect() {
let mut sys = Sys::default();
sys.ram[0x0010] = 0x00;
sys.ram[0x0011] = 0x50;
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x92, 0x10]);
cpu.a = 0x0049;
for _ in 0..5 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x005000], 0x49, "sta direct indirect");
assert_eq!(cpu.pc, 0x8002);
}
#[test]
fn sta_direct_indirect_long() {
let mut sys = Sys::default();
sys.ram[0x0010] = 0x00;
sys.ram[0x0011] = 0x50;
sys.ram[0x0012] = 0x02;
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x87, 0x10]);
cpu.a = 0x004A;
for _ in 0..6 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x025000], 0x4A, "sta direct indirect long");
assert_eq!(cpu.pc, 0x8002);
}
#[test]
fn sta_direct_indirect_long_indexed_y() {
let mut sys = Sys::default();
sys.ram[0x0010] = 0x00;
sys.ram[0x0011] = 0x50;
sys.ram[0x0012] = 0x02;
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x97, 0x10]);
cpu.a = 0x004B;
cpu.y = 0x0002;
for _ in 0..6 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x025002], 0x4B, "sta direct indirect long indexed y");
assert_eq!(cpu.pc, 0x8002);
}
#[test]
fn sta_direct_indirect_x() {
let mut sys = Sys::default();
sys.ram[0x0012] = 0x00;
sys.ram[0x0013] = 0x50;
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x81, 0x10]);
cpu.a = 0x004C;
cpu.x = 0x0002;
for _ in 0..6 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x005000], 0x4C, "sta direct indirect x");
assert_eq!(cpu.pc, 0x8002);
}
#[test]
fn sta_direct_indirect_indexed_y() {
let mut sys = Sys::default();
sys.ram[0x0010] = 0xFE;
sys.ram[0x0011] = 0x4F;
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x91, 0x10]);
cpu.a = 0x004D;
cpu.y = 0x0002;
for _ in 0..6 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x005000], 0x4D, "sta direct indirect indexed y");
assert_eq!(cpu.pc, 0x8002);
}
#[test]
fn sta_stack_rel_indirect_indexed_y() {
let mut sys = Sys::default();
sys.ram[0x01F0] = 0x00;
sys.ram[0x01F1] = 0x30;
let mut cpu = boot(&mut sys);
sys.write_code(0x008000, &[0x93, 0x10]);
cpu.a = 0x004E;
cpu.y = 0x0005;
cpu.s = (cpu.s & 0xFF00) | 0x00E0;
for _ in 0..7 { cpu.cycle(&mut sys); }
assert_eq!(sys.ram[0x003005], 0x4E, "sta stack rel indirect indexed y");
assert_eq!(cpu.pc, 0x8002);
}