diff --git a/Cargo.toml b/Cargo.toml index 0fae707..91663e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["snes-core", "snes-frontend"] +members = ["snes-core", "snes-cpu-test-runner", "snes-frontend"] resolver = "2" diff --git a/snes-core/src/cpu/bus.rs b/snes-core/src/cpu/bus.rs index a960f51..7bc1c01 100644 --- a/snes-core/src/cpu/bus.rs +++ b/snes-core/src/cpu/bus.rs @@ -10,6 +10,7 @@ pub struct Bus { pub rom: Box, pub internal_registers: InternalRegisters, pub dma: DMA, + pub force_cart_lookup: bool, } #[derive(PartialEq, Debug)] @@ -30,6 +31,7 @@ impl Bus { rom: Box::new(LoROM::new()), internal_registers: InternalRegisters::new(), dma: DMA::new(), + force_cart_lookup: false, } } @@ -41,7 +43,10 @@ impl Bus { self.wram[(address & 0xFFFF) as usize] = value; } - fn map_address(address: u32) -> MemoryMap { + fn map_address(&self, address: u32) -> MemoryMap { + if self.force_cart_lookup { + return MemoryMap::Cartridge; + } let (bank, sub_address) = { let bank = (address >> 16) as u8; let sub_address = address as u16; @@ -64,7 +69,7 @@ impl Bus { /// This function is meant to be used by external parts of the code, /// for example, to render register info without mutating them pub fn read_external(&self, address: u32) -> u8 { - let section = Bus::map_address(address); + let section = self.map_address(address); match section { MemoryMap::WRAM => self.read_wram(address), MemoryMap::PPU => self.ppu.registers.read_external(address as u16), @@ -79,7 +84,7 @@ impl Bus { } pub fn read(&mut self, address: u32) -> u8 { - let section = Bus::map_address(address); + let section = self.map_address(address); match section { MemoryMap::WRAM => self.read_wram(address), MemoryMap::PPU => self.ppu.registers.read(address as u16), @@ -94,7 +99,7 @@ impl Bus { } pub fn write(&mut self, address: u32, value: u8) { - let section = Bus::map_address(address); + let section = self.map_address(address); match section { MemoryMap::WRAM => self.write_wram(address, value), MemoryMap::PPU => self.ppu.registers.write(address as u16, value), @@ -123,44 +128,45 @@ mod bus_tests { #[test] fn test_memory_map() { - assert_eq!(Bus::map_address(0x7E0000), MemoryMap::WRAM); - assert_eq!(Bus::map_address(0x7F0000), MemoryMap::WRAM); - assert_eq!(Bus::map_address(0x7E0500), MemoryMap::WRAM); - assert_eq!(Bus::map_address(0x000000), MemoryMap::WRAM); - assert_eq!(Bus::map_address(0x3F0000), MemoryMap::WRAM); - assert_eq!(Bus::map_address(0x3F1FFF), MemoryMap::WRAM); - assert_eq!(Bus::map_address(0x3F0000), MemoryMap::WRAM); - assert_eq!(Bus::map_address(0x800000), MemoryMap::WRAM); - assert_eq!(Bus::map_address(0xBF0000), MemoryMap::WRAM); - assert_eq!(Bus::map_address(0xBF1FFF), MemoryMap::WRAM); - assert_eq!(Bus::map_address(0x3F0000), MemoryMap::WRAM); + let bus = Bus::new(); + assert_eq!(bus.map_address(0x7E0000), MemoryMap::WRAM); + assert_eq!(bus.map_address(0x7F0000), MemoryMap::WRAM); + assert_eq!(bus.map_address(0x7E0500), MemoryMap::WRAM); + assert_eq!(bus.map_address(0x000000), MemoryMap::WRAM); + assert_eq!(bus.map_address(0x3F0000), MemoryMap::WRAM); + assert_eq!(bus.map_address(0x3F1FFF), MemoryMap::WRAM); + assert_eq!(bus.map_address(0x3F0000), MemoryMap::WRAM); + assert_eq!(bus.map_address(0x800000), MemoryMap::WRAM); + assert_eq!(bus.map_address(0xBF0000), MemoryMap::WRAM); + assert_eq!(bus.map_address(0xBF1FFF), MemoryMap::WRAM); + assert_eq!(bus.map_address(0x3F0000), MemoryMap::WRAM); - assert_eq!(Bus::map_address(0x002100), MemoryMap::PPU); - assert_eq!(Bus::map_address(0x0021FF), MemoryMap::PPU); - assert_eq!(Bus::map_address(0x3F2100), MemoryMap::PPU); - assert_eq!(Bus::map_address(0x3F21FF), MemoryMap::PPU); - assert_eq!(Bus::map_address(0x802100), MemoryMap::PPU); - assert_eq!(Bus::map_address(0x8021FF), MemoryMap::PPU); - assert_eq!(Bus::map_address(0xBF2100), MemoryMap::PPU); - assert_eq!(Bus::map_address(0xBF21FF), MemoryMap::PPU); + assert_eq!(bus.map_address(0x002100), MemoryMap::PPU); + assert_eq!(bus.map_address(0x0021FF), MemoryMap::PPU); + assert_eq!(bus.map_address(0x3F2100), MemoryMap::PPU); + assert_eq!(bus.map_address(0x3F21FF), MemoryMap::PPU); + assert_eq!(bus.map_address(0x802100), MemoryMap::PPU); + assert_eq!(bus.map_address(0x8021FF), MemoryMap::PPU); + assert_eq!(bus.map_address(0xBF2100), MemoryMap::PPU); + assert_eq!(bus.map_address(0xBF21FF), MemoryMap::PPU); - assert_eq!(Bus::map_address(0x004200), MemoryMap::CPU); - assert_eq!(Bus::map_address(0x00420F), MemoryMap::CPU); - assert_eq!(Bus::map_address(0x3F4200), MemoryMap::CPU); - assert_eq!(Bus::map_address(0x3F420F), MemoryMap::CPU); - assert_eq!(Bus::map_address(0x804200), MemoryMap::CPU); - assert_eq!(Bus::map_address(0x80420F), MemoryMap::CPU); - assert_eq!(Bus::map_address(0xBF4200), MemoryMap::CPU); - assert_eq!(Bus::map_address(0xBF420F), MemoryMap::CPU); + assert_eq!(bus.map_address(0x004200), MemoryMap::CPU); + assert_eq!(bus.map_address(0x00420F), MemoryMap::CPU); + assert_eq!(bus.map_address(0x3F4200), MemoryMap::CPU); + assert_eq!(bus.map_address(0x3F420F), MemoryMap::CPU); + assert_eq!(bus.map_address(0x804200), MemoryMap::CPU); + assert_eq!(bus.map_address(0x80420F), MemoryMap::CPU); + assert_eq!(bus.map_address(0xBF4200), MemoryMap::CPU); + assert_eq!(bus.map_address(0xBF420F), MemoryMap::CPU); - assert_eq!(Bus::map_address(0x004016), MemoryMap::Joypad); - assert_eq!(Bus::map_address(0x004017), MemoryMap::Joypad); - assert_eq!(Bus::map_address(0x3F4016), MemoryMap::Joypad); - assert_eq!(Bus::map_address(0x3F4017), MemoryMap::Joypad); - assert_eq!(Bus::map_address(0x804016), MemoryMap::Joypad); - assert_eq!(Bus::map_address(0x804017), MemoryMap::Joypad); - assert_eq!(Bus::map_address(0xBF4016), MemoryMap::Joypad); - assert_eq!(Bus::map_address(0xBF4017), MemoryMap::Joypad); + assert_eq!(bus.map_address(0x004016), MemoryMap::Joypad); + assert_eq!(bus.map_address(0x004017), MemoryMap::Joypad); + assert_eq!(bus.map_address(0x3F4016), MemoryMap::Joypad); + assert_eq!(bus.map_address(0x3F4017), MemoryMap::Joypad); + assert_eq!(bus.map_address(0x804016), MemoryMap::Joypad); + assert_eq!(bus.map_address(0x804017), MemoryMap::Joypad); + assert_eq!(bus.map_address(0xBF4016), MemoryMap::Joypad); + assert_eq!(bus.map_address(0xBF4017), MemoryMap::Joypad); } #[test] diff --git a/snes-core/src/cpu/cycles.rs b/snes-core/src/cpu/cycles.rs index b7f336a..2e41b86 100644 --- a/snes-core/src/cpu/cycles.rs +++ b/snes-core/src/cpu/cycles.rs @@ -108,6 +108,7 @@ fn common_conditions(cpu_registers: &Registers, addressing_mode: AddressingMode, fn common_bytes_cycles_arithmetic(addressing_mode: AddressingMode) -> (u16, usize) { match addressing_mode { + A::Accumulator => (1, 2), A::Immediate => (2, 2), A::Absolute => (3, 4), A::AbsoluteLong => (4, 5), diff --git a/snes-core/src/cpu/instructions/adc.rs b/snes-core/src/cpu/instructions/adc.rs index 906e961..056b790 100644 --- a/snes-core/src/cpu/instructions/adc.rs +++ b/snes-core/src/cpu/instructions/adc.rs @@ -147,6 +147,25 @@ mod cpu_instructions_tests { assert_eq!(registers.pc, 0x02); assert_eq!(registers.cycles, 2); assert!(!registers.get_carry_flag()); + + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x007F; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_16bit_mode(false); + registers.set_carry_flag(true); + bus.write(0x000001, 0x7F); + let instruction = ADC8BIN{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0xFF); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(registers.get_negative_flag()); + assert!(registers.get_overflow_flag()); + assert!(!registers.get_zero_flag()); + assert!(!registers.get_carry_flag()); } #[test] diff --git a/snes-core/src/cpu/instructions/cmp.rs b/snes-core/src/cpu/instructions/cmp.rs index 5dfbc24..0bcf94c 100644 --- a/snes-core/src/cpu/instructions/cmp.rs +++ b/snes-core/src/cpu/instructions/cmp.rs @@ -93,7 +93,7 @@ mod cpu_instructions_tests { assert_eq!(registers.a, 0x0001); // check A is not affected assert_eq!(registers.pc, 0x02); assert_eq!(registers.cycles, 2); - assert!(!registers.get_carry_flag()); + assert!(registers.get_carry_flag()); assert!(registers.get_zero_flag()); } @@ -106,13 +106,14 @@ mod cpu_instructions_tests { registers.pc = 0x0000; registers.set_16bit_mode(false); registers.set_overflow_flag(false); + registers.set_carry_flag(true); bus.write(0x000001, 0xB0); let instruction = CMP16{addressing_mode: AddressingMode::Immediate}; instruction.execute(&mut registers, &mut bus); assert_eq!(registers.a, 0x0050); // check A is not affected assert_eq!(registers.pc, 0x02); assert_eq!(registers.cycles, 2); - assert!(registers.get_carry_flag()); + assert!(!registers.get_carry_flag()); assert!(!registers.get_zero_flag()); assert!(!registers.get_overflow_flag()); } diff --git a/snes-core/src/cpu/instructions/comp_common.rs b/snes-core/src/cpu/instructions/comp_common.rs index 70f8cbe..d9e0b8c 100644 --- a/snes-core/src/cpu/instructions/comp_common.rs +++ b/snes-core/src/cpu/instructions/comp_common.rs @@ -2,11 +2,13 @@ use crate::{cpu::registers::Registers, utils::{num_trait::SnesNum, alu}, common: pub fn do_comp(registers: &mut Registers, target: T, value: T) { - let (_, affected_flags) = alu::sbc_bin(target, value, false); + let (_, affected_flags) = alu::sbc_bin(target, value, true); for flag in affected_flags { match flag { - Flags::Overflow(_) => {}, - _ => registers.set_flags(&[flag]), + Flags::Negative(_) | + Flags::Zero(_) | + Flags::Carry(_) => registers.set_flags(&[flag]), + _ => {}, } } -} \ No newline at end of file +} diff --git a/snes-core/src/cpu/instructions/cpx.rs b/snes-core/src/cpu/instructions/cpx.rs index e4e7af9..2664d54 100644 --- a/snes-core/src/cpu/instructions/cpx.rs +++ b/snes-core/src/cpu/instructions/cpx.rs @@ -95,7 +95,7 @@ mod cpu_instructions_tests { assert_eq!(registers.x, 0x01); assert_eq!(registers.pc, 0x02); assert_eq!(registers.cycles, 2); - assert!(!registers.get_carry_flag()); + assert!(registers.get_carry_flag()); assert!(registers.get_zero_flag()); } @@ -119,7 +119,7 @@ mod cpu_instructions_tests { assert_eq!(registers.x, 0x50); // check X is not affected assert_eq!(registers.pc, 0x03); assert_eq!(registers.cycles, 3); - assert!(registers.get_carry_flag()); + assert!(!registers.get_carry_flag()); assert!(!registers.get_zero_flag()); assert!(!registers.get_overflow_flag()); } diff --git a/snes-core/src/cpu/instructions/cpy.rs b/snes-core/src/cpu/instructions/cpy.rs index fbd45e6..436fd11 100644 --- a/snes-core/src/cpu/instructions/cpy.rs +++ b/snes-core/src/cpu/instructions/cpy.rs @@ -95,7 +95,7 @@ mod cpu_instructions_tests { assert_eq!(registers.y, 0x01); assert_eq!(registers.pc, 0x02); assert_eq!(registers.cycles, 2); - assert!(!registers.get_carry_flag()); + assert!(registers.get_carry_flag()); assert!(registers.get_zero_flag()); } @@ -119,7 +119,7 @@ mod cpu_instructions_tests { assert_eq!(registers.y, 0x50); // check Y is not affected assert_eq!(registers.pc, 0x03); assert_eq!(registers.cycles, 3); - assert!(registers.get_carry_flag()); + assert!(!registers.get_carry_flag()); assert!(!registers.get_zero_flag()); assert!(!registers.get_overflow_flag()); } diff --git a/snes-core/src/cpu/instructions/dec_common.rs b/snes-core/src/cpu/instructions/dec_common.rs index e9f39db..a68af73 100644 --- a/snes-core/src/cpu/instructions/dec_common.rs +++ b/snes-core/src/cpu/instructions/dec_common.rs @@ -1,7 +1,7 @@ use crate::{cpu::registers::Registers, utils::{num_trait::SnesNum, alu}, common::flags::Flags}; pub fn do_dec(registers: &mut Registers, target: T) -> T { - let (result, affected_flags) = alu::sbc_bin(target, T::from_u32(1), false); + let (result, affected_flags) = alu::sbc_bin(target, T::from_u32(1), true); for flag in affected_flags { match flag { Flags::Negative(_) | Flags::Zero(_) => registers.set_flags(&[flag]), diff --git a/snes-core/src/cpu/instructions/read_write_common.rs b/snes-core/src/cpu/instructions/read_write_common.rs index 8f1a8ed..b1e59ef 100644 --- a/snes-core/src/cpu/instructions/read_write_common.rs +++ b/snes-core/src/cpu/instructions/read_write_common.rs @@ -8,6 +8,7 @@ pub fn get_effective_address(registers: &Registers, bus: &mut Bus, addressing_mo direct_page_register: registers.d, stack_pointer: registers.sp, x: registers.x, y: registers.y, + dbr: registers.dbr, } ) } @@ -21,6 +22,7 @@ pub fn read_8bit_from_address(registers: &Registers, bus: &mut Bus, addressing_m direct_page_register: registers.d, stack_pointer: registers.sp, x: registers.x, y: registers.y, + dbr: registers.dbr, }, bus, ) @@ -36,6 +38,7 @@ pub fn read_16bit_from_address(registers: &Registers, bus: &mut Bus, addressing_ direct_page_register: registers.d, stack_pointer: registers.sp, x: registers.x, y: registers.y, + dbr: registers.dbr, }, bus, ) @@ -51,6 +54,7 @@ pub fn write_8bit_to_address(registers: &mut Registers, bus: &mut Bus, addressin direct_page_register: registers.d, stack_pointer: registers.sp, x: registers.x, y: registers.y, + dbr: registers.dbr, }, bus, value, @@ -67,6 +71,7 @@ pub fn write_16bit_to_address(registers: &mut Registers, bus: &mut Bus, addressi direct_page_register: registers.d, stack_pointer: registers.sp, x: registers.x, y: registers.y, + dbr: registers.dbr, }, bus, value, diff --git a/snes-core/src/cpu/instructions/sbc.rs b/snes-core/src/cpu/instructions/sbc.rs index c69b733..2381777 100644 --- a/snes-core/src/cpu/instructions/sbc.rs +++ b/snes-core/src/cpu/instructions/sbc.rs @@ -139,7 +139,8 @@ mod cpu_instructions_tests { registers.a = 0x0040; registers.pbr = 0x00; registers.pc = 0x0000; - registers.set_memory_select_flag(true); + registers.set_16bit_mode(false); + registers.set_carry_flag(true); bus.write(0x000001, 0x40); let instruction = SBC8BIN{addressing_mode: AddressingMode::Immediate}; instruction.execute(&mut registers, &mut bus); @@ -147,7 +148,26 @@ mod cpu_instructions_tests { assert_eq!(registers.pc, 0x02); assert_eq!(registers.cycles, 2); assert!(registers.get_zero_flag()); - assert!(!registers.get_carry_flag()); + assert!(registers.get_carry_flag()); + + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x007F; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_16bit_mode(false); + registers.set_carry_flag(false); + bus.write(0x000001, 0x7E); + let instruction = SBC8BIN{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x00); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_negative_flag()); + assert!(!registers.get_overflow_flag()); + assert!(registers.get_zero_flag()); + assert!(registers.get_carry_flag()); } #[test] @@ -159,6 +179,7 @@ mod cpu_instructions_tests { registers.pbr = 0x00; registers.pc = 0x0000; registers.set_memory_select_flag(false); + registers.set_carry_flag(true); bus.write(0x000001, 0x00); bus.write(0x000002, 0x40); let instruction = SBC16BIN{addressing_mode: AddressingMode::Immediate}; @@ -167,7 +188,7 @@ mod cpu_instructions_tests { assert_eq!(registers.pc, 0x03); assert_eq!(registers.cycles, 3); assert!(registers.get_zero_flag()); - assert!(!registers.get_carry_flag()); + assert!(registers.get_carry_flag()); } #[test] diff --git a/snes-core/src/cpu/internal_registers.rs b/snes-core/src/cpu/internal_registers.rs index dd44fc3..306c94e 100644 --- a/snes-core/src/cpu/internal_registers.rs +++ b/snes-core/src/cpu/internal_registers.rs @@ -58,9 +58,6 @@ impl InternalRegisters { fn read_vblank_nmi_mut(&self, ppu_registers: &mut PPURegisters) -> u8 { let result = self.read_vblank_nmi(ppu_registers); - if result == 0x80 { - println!("nmi set"); - } // When register is read, bit 7 is cleared ppu_registers.vblank_nmi = false; result diff --git a/snes-core/src/ppu/registers.rs b/snes-core/src/ppu/registers.rs index 1c5886d..8b2e610 100644 --- a/snes-core/src/ppu/registers.rs +++ b/snes-core/src/ppu/registers.rs @@ -174,7 +174,7 @@ impl PPURegisters { fn _read(&self, address: u16) -> u8 { match address { 0x2100..=0x213F => self.data[(address as usize) - 0x2100], - _ => 0xFF, + _ => 0x00, } } diff --git a/snes-core/src/rom/mod.rs b/snes-core/src/rom/mod.rs index 7853840..4539b5b 100644 --- a/snes-core/src/rom/mod.rs +++ b/snes-core/src/rom/mod.rs @@ -1,4 +1,5 @@ pub mod lo_rom; +pub mod special_ram_cart; use std::fs::File; use std::io::Read; diff --git a/snes-core/src/rom/special_ram_cart.rs b/snes-core/src/rom/special_ram_cart.rs new file mode 100644 index 0000000..8b42ed9 --- /dev/null +++ b/snes-core/src/rom/special_ram_cart.rs @@ -0,0 +1,36 @@ +use super::ROM; + +pub struct SpecialRAMCart { + data: Vec, +} + +impl SpecialRAMCart { + pub fn new() -> Self { + Self { + data: vec![0x00; 0x1000000], + } + } +} + +impl ROM for SpecialRAMCart { + fn load(&mut self, _filename: &str) -> std::io::Result { + Ok(true) + } + + fn read(&self, address: u32) -> u8 { + match self.data.get(address as usize) { + Some(byte) => *byte, + None => 0x00, + } + } + + fn write(&mut self, address: u32, value: u8) { + self.data[address as usize] = value; + } +} + +impl Default for SpecialRAMCart { + fn default() -> Self { + Self::new() + } +} diff --git a/snes-core/src/utils/addressing.rs b/snes-core/src/utils/addressing.rs index 8057c52..192a50a 100644 --- a/snes-core/src/utils/addressing.rs +++ b/snes-core/src/utils/addressing.rs @@ -6,6 +6,7 @@ use crate::cpu::bus::Bus; #[derive(Copy, Clone)] pub struct ResolveAddressParams { pub pc_addr: u32, + pub dbr: u8, pub direct_page_register: u16, pub stack_pointer: u16, pub x: u16, @@ -19,15 +20,14 @@ pub fn immediate(pc_addr: u32) -> u32 { } /// OPCODE addr -pub fn absolute(bus: &mut Bus, pc_addr: u32) -> u32 { - (pc_addr & 0xFF0000) | - (bus.read(pc_addr + 1) as u32) | - ((bus.read(pc_addr + 2) as u32) << 8) +pub fn absolute(bus: &mut Bus, pc_addr: u32, dbr: u8) -> u32 { + let addr = (bus.read(pc_addr + 1) as u32) | ((bus.read(pc_addr + 2) as u32) << 8); + ((dbr as u32) << 16) | addr } /// OPCODE (addr) -pub fn absolute_indirect(bus: &mut Bus, pc_addr: u32) -> u32 { - let addr = absolute(bus, pc_addr); +pub fn absolute_indirect(bus: &mut Bus, pc_addr: u32, dbr: u8) -> u32 { + let addr = absolute(bus, pc_addr, dbr); let dbr = pc_addr & 0xFF0000; dbr | ((bus.read(addr) as u32) << 8) | (bus.read(addr + 1) as u32) } @@ -40,8 +40,8 @@ pub fn absolute_long(bus: &mut Bus, pc_addr: u32) -> u32 { } /// OPCODE (addr) -pub fn absolute_indirect_long(bus: &mut Bus, pc_addr: u32) -> u32 { - let addr = absolute(bus, pc_addr); +pub fn absolute_indirect_long(bus: &mut Bus, pc_addr: u32, dbr: u8) -> u32 { + let addr = absolute(bus, pc_addr, dbr); ((bus.read(addr) as u32) << 16) | ((bus.read(addr + 1) as u32) << 8) | (bus.read(addr + 2) as u32) @@ -69,13 +69,13 @@ pub fn direct_page_indirect_long(bus: &mut Bus, pc_addr: u32, direct_page_regist /// OPCODE addr,X /// OPCODE addr,Y -pub fn absolute_indexed(bus: &mut Bus, pc_addr: u32, xy: u16) -> u32 { - absolute(bus, pc_addr) + (xy as u32) +pub fn absolute_indexed(bus: &mut Bus, pc_addr: u32, xy: u16, dbr: u8) -> u32 { + absolute(bus, pc_addr, dbr) + (xy as u32) } /// OPCODE (addr) -pub fn absolute_indexed_indirect(bus: &mut Bus, pc_addr: u32, xy: u16) -> u32 { - let addr = absolute_indexed(bus, pc_addr, xy); +pub fn absolute_indexed_indirect(bus: &mut Bus, pc_addr: u32, xy: u16, dbr: u8) -> u32 { + let addr = absolute_indexed(bus, pc_addr, xy, dbr); let dbr = pc_addr & 0xFF0000; dbr | ((bus.read(addr) as u32) << 8) | (bus.read(addr + 1) as u32) } @@ -112,14 +112,14 @@ pub fn direct_page_indirect_long_indexed(bus: &mut Bus, pc_addr: u32, direct_pag } /// OPCODE sr,S -pub fn stack_relative(bus: &mut Bus, pc_addr: u32, stack_pointer: u16) -> u32 { - absolute_indexed(bus, pc_addr, stack_pointer) +pub fn stack_relative(bus: &mut Bus, pc_addr: u32, stack_pointer: u16, dbr: u8) -> u32 { + absolute_indexed(bus, pc_addr, stack_pointer, dbr) } /// OPCODE (sr,S),X /// OPCODE (sr,S),Y -pub fn stack_relative_indirect_indexed(bus: &mut Bus, pc_addr: u32, stack_pointer: u16, xy: u16) -> u32 { - absolute_indexed(bus, pc_addr, stack_pointer) + (xy as u32) +pub fn stack_relative_indirect_indexed(bus: &mut Bus, pc_addr: u32, stack_pointer: u16, xy: u16, dbr: u8) -> u32 { + absolute_indexed(bus, pc_addr, stack_pointer, dbr) + (xy as u32) } #[derive(Copy, Clone, PartialEq)] @@ -167,22 +167,22 @@ impl AddressingMode { match self { Self::Accumulator => p.pc_addr, Self::Immediate => immediate(p.pc_addr), - Self::Absolute => absolute(bus, p.pc_addr), - Self::AbsoluteIndirect => absolute_indirect(bus, p.pc_addr), - Self::AbsoluteIndirectLong => absolute_indirect_long(bus, p.pc_addr), + Self::Absolute => absolute(bus, p.pc_addr, p.dbr), + Self::AbsoluteIndirect => absolute_indirect(bus, p.pc_addr, p.dbr), + Self::AbsoluteIndirectLong => absolute_indirect_long(bus, p.pc_addr, p.dbr), Self::AbsoluteLong => absolute_long(bus, p.pc_addr), Self::DirectPage => direct_page(bus, p.pc_addr, p.direct_page_register), Self::DirectPageIndirect => direct_page_indirect(bus, p.pc_addr, p.direct_page_register), Self::DirectPageIndirectLong => direct_page_indirect_long(bus, p.pc_addr, p.direct_page_register), - Self::AbsoluteIndexed(idx) => absolute_indexed(bus, p.pc_addr, if idx == X {p.x} else {p.y}), - Self::AbsoluteIndexedIndirect(idx) => absolute_indexed_indirect(bus, p.pc_addr, if idx == X {p.x} else {p.y}), + Self::AbsoluteIndexed(idx) => absolute_indexed(bus, p.pc_addr, if idx == X {p.x} else {p.y}, p.dbr), + Self::AbsoluteIndexedIndirect(idx) => absolute_indexed_indirect(bus, p.pc_addr, if idx == X {p.x} else {p.y}, p.dbr), Self::AbsoluteLongIndexed(idx) => absolute_long_indexed(bus, p.pc_addr, if idx == X {p.x} else {p.y}), Self::DirectPageIndexed(idx) => direct_page_indexed(bus, p.pc_addr, p.direct_page_register, if idx == X {p.x} else {p.y}), Self::DirectPageIndexedIndirect(idx) => direct_page_indexed_indirect(bus, p.pc_addr, p.direct_page_register, if idx == X {p.x} else {p.y}), Self::DirectPageIndirectIndexed(idx) => direct_page_indirect_indexed(bus, p.pc_addr, p.direct_page_register, if idx == X {p.x} else {p.y}), Self::DirectPageIndirectLongIndexed(idx) => direct_page_indirect_long_indexed(bus, p.pc_addr, p.direct_page_register, if idx == X {p.x} else {p.y}), - Self::StackRelative => stack_relative(bus, p.pc_addr, p.stack_pointer), - Self::StackRelativeIndirectIndexed(idx) => stack_relative_indirect_indexed(bus, p.pc_addr, p.stack_pointer, if idx == X {p.x} else {p.y}), + Self::StackRelative => stack_relative(bus, p.pc_addr, p.stack_pointer, p.dbr), + Self::StackRelativeIndirectIndexed(idx) => stack_relative_indirect_indexed(bus, p.pc_addr, p.stack_pointer, if idx == X {p.x} else {p.y}, p.dbr), } } @@ -224,13 +224,13 @@ mod addressing_modes_tests { let pc_addr = 0x000000; bus.write(pc_addr + 1, 0x01); bus.write(pc_addr + 2, 0x02); - assert_eq!(absolute(&mut bus, pc_addr), 0x000201); + assert_eq!(absolute(&mut bus, pc_addr, 0x00), 0x000201); let mut bus = Bus::new(); let pc_addr = 0x7F0000; bus.write(pc_addr + 1, 0x01); bus.write(pc_addr + 2, 0x02); - assert_eq!(absolute(&mut bus, pc_addr), 0x7F0201); + assert_eq!(absolute(&mut bus, pc_addr, 0x7F), 0x7F0201); } #[test] @@ -241,21 +241,21 @@ mod addressing_modes_tests { bus.write(pc_addr + 1, addr); bus.write(addr as u32, 0x02); bus.write((addr + 1) as u32, 0x01); - assert_eq!(absolute_indirect(&mut bus, pc_addr), 0x000201); + assert_eq!(absolute_indirect(&mut bus, pc_addr, 0x00), 0x000201); let pc_addr = 0x7E0010; let addr = 0x55; bus.write(pc_addr + 1, addr); bus.write(addr as u32, 0x02); bus.write((addr + 1) as u32, 0x01); - assert_eq!(absolute_indirect(&mut bus, pc_addr), 0x7E0201); + assert_eq!(absolute_indirect(&mut bus, pc_addr, 0x00), 0x7E0201); let pc_addr = 0x7E0010; let addr = 0x55; bus.write(pc_addr + 1, addr); bus.write(addr as u32, 0x02); bus.write((addr + 1) as u32, 0x01); - assert_eq!(absolute_indirect(&mut bus, pc_addr), 0x7E0201); + assert_eq!(absolute_indirect(&mut bus, pc_addr, 0x00), 0x7E0201); } #[test] @@ -284,7 +284,7 @@ mod addressing_modes_tests { bus.write(addr as u32, 0x03); bus.write((addr + 1) as u32, 0x02); bus.write((addr + 2) as u32, 0x01); - assert_eq!(absolute_indirect_long(&mut bus, pc_addr), 0x030201); + assert_eq!(absolute_indirect_long(&mut bus, pc_addr, 0x00), 0x030201); } @@ -353,13 +353,13 @@ mod addressing_modes_tests { let pc_addr = 0x000000; bus.write(pc_addr + 1, 0x01); bus.write(pc_addr + 2, 0x02); - assert_eq!(absolute_indexed(&mut bus, pc_addr, 0x02), 0x000203); + assert_eq!(absolute_indexed(&mut bus, pc_addr, 0x02, 0x00), 0x000203); let mut bus = Bus::new(); let pc_addr = 0x7F0000; bus.write(pc_addr + 1, 0x01); bus.write(pc_addr + 2, 0x02); - assert_eq!(absolute_indexed(&mut bus, pc_addr, 0x02), 0x7F0203); + assert_eq!(absolute_indexed(&mut bus, pc_addr, 0x02, 0x7F), 0x7F0203); } #[test] @@ -370,7 +370,7 @@ mod addressing_modes_tests { bus.write(pc_addr + 1, addr); bus.write(addr as u32, 0x02); bus.write((addr + 1) as u32, 0x01); - assert_eq!(absolute_indexed_indirect(&mut bus, pc_addr, 0), 0x000201); + assert_eq!(absolute_indexed_indirect(&mut bus, pc_addr, 0, 0x00), 0x000201); } #[test] @@ -480,13 +480,13 @@ mod addressing_modes_tests { let pc_addr = 0x000000; bus.write(pc_addr + 1, 0x01); bus.write(pc_addr + 2, 0x02); - assert_eq!(stack_relative(&mut bus, pc_addr, 0x02), 0x000203); + assert_eq!(stack_relative(&mut bus, pc_addr, 0x02, 0x00), 0x000203); let mut bus = Bus::new(); let pc_addr = 0x7F0000; bus.write(pc_addr + 1, 0x01); bus.write(pc_addr + 2, 0x02); - assert_eq!(stack_relative(&mut bus, pc_addr, 0x02), 0x7F0203); + assert_eq!(stack_relative(&mut bus, pc_addr, 0x02, 0x7F), 0x7F0203); } #[test] @@ -495,13 +495,13 @@ mod addressing_modes_tests { let pc_addr = 0x000000; bus.write(pc_addr + 1, 0x01); bus.write(pc_addr + 2, 0x02); - assert_eq!(stack_relative_indirect_indexed(&mut bus, pc_addr, 0x02, 0x02), 0x000205); + assert_eq!(stack_relative_indirect_indexed(&mut bus, pc_addr, 0x02, 0x02, 0x00), 0x000205); let mut bus = Bus::new(); let pc_addr = 0x7F0000; bus.write(pc_addr + 1, 0x01); bus.write(pc_addr + 2, 0x02); - assert_eq!(stack_relative_indirect_indexed(&mut bus, pc_addr, 0x02, 0x02), 0x7F0205); + assert_eq!(stack_relative_indirect_indexed(&mut bus, pc_addr, 0x02, 0x02, 0x7F), 0x7F0205); } #[test] @@ -512,7 +512,7 @@ mod addressing_modes_tests { bus.write(pc_addr + 2, 0x10); bus.write(0x001020, 0xFE); let val = AddressingMode::Absolute.read_8bit( - ResolveAddressParams {pc_addr, direct_page_register: 0x00, stack_pointer: 0x00, x: 0x00, y: 0x00}, + ResolveAddressParams {pc_addr, direct_page_register: 0x00, stack_pointer: 0x00, x: 0x00, y: 0x00, dbr: 0x00}, &mut bus, ); assert_eq!(val, 0xFE); @@ -524,7 +524,7 @@ mod addressing_modes_tests { bus.write(0x001020, 0xFF); bus.write(0x001021, 0xEE); let val = AddressingMode::Absolute.read_16bit( - ResolveAddressParams {pc_addr, direct_page_register: 0x00, stack_pointer: 0x00, x: 0x00, y: 0x00}, + ResolveAddressParams {pc_addr, direct_page_register: 0x00, stack_pointer: 0x00, x: 0x00, y: 0x00, dbr: 0x00}, &mut bus, ); assert_eq!(val, 0xEEFF); diff --git a/snes-core/src/utils/alu.rs b/snes-core/src/utils/alu.rs index 1de606f..5f9a44d 100644 --- a/snes-core/src/utils/alu.rs +++ b/snes-core/src/utils/alu.rs @@ -12,7 +12,7 @@ pub fn adc_bin(target: T, value: T, carry: bool) -> (T, [Flags; 4]) } pub fn adc_bcd(target: T, value: T, carry: bool) -> (T, [Flags; 4]) { - let original_target = value; + let original_target = target; let original_value = value; let nibble_bytes = target.bytes() * 2; let mut is_carry = carry; @@ -26,8 +26,10 @@ pub fn adc_bcd(target: T, value: T, carry: bool) -> (T, [Flags; 4]) let mut bcd_six_add = 0x06; let mut carry_shift = 0; + let mut is_overflow = false; for _ in 0..nibble_bytes { result = (target & value_mask) + (value & value_mask) + ((is_carry as u32) << carry_shift) + (result & result_mask); + is_overflow = original_target.is_overflow(original_value, T::from_u32(result)); if result > carry_compare { result = result.wrapping_add(bcd_six_add); } @@ -42,57 +44,73 @@ pub fn adc_bcd(target: T, value: T, carry: bool) -> (T, [Flags; 4]) let result = T::from_u32(result); (result, [ Negative(result.is_negative()), - Overflow(original_target.is_overflow(original_value, result)), + Overflow(is_overflow), Zero(result.is_zero()), Carry(is_carry), ]) } pub fn sbc_bin(target: T, value: T, carry: bool) -> (T, [Flags; 4]) { - let result = target.sbc_snes(value, carry); - (result, [ - Negative(result.is_negative()), - Overflow(target.is_overflow(value, result)), - Zero(result.is_zero()), - Carry(target.sbc_will_carry(value, carry)), - ]) + adc_bin(target, value.invert(), carry) } pub fn sbc_bcd(target: T, value: T, carry: bool) -> (T, [Flags; 4]) { - let original_target = value; - let original_value = value; - let nibble_bytes = target.bytes() * 2; - let mut is_carry = carry; - let target = target.to_u32(); - let value = value.invert().to_u32(); - let mut result = 0; - - let mut value_mask = 0x0F; - let mut result_mask = 0x00; - let mut carry_compare = 0x0F; - let mut bcd_six_sub = 0x06; - let mut carry_shift = 0; - - for _ in 0..nibble_bytes { - result = (target & value_mask) + (value & value_mask) + ((is_carry as u32) << carry_shift) + (result & result_mask); - if result <= carry_compare { - result = result.wrapping_sub(bcd_six_sub); + if target.bytes() == 1 { + let target = ((target.to_u32() as u16) as i16) & 0xFF; + let value = !(value.to_u32() as u16) as i16; + let mut carry = if carry {1} else {0}; + let mut result: i16 = (target & 0x000F) + (value & 0x000F) + carry; + if result <= 0x000F { + result -= 0x06; + } + carry = if result > 0x000F {1} else {0}; + result = (target & 0x00F0) + (value & 0x00F0) + (carry << 4) + (result & 0x000F); + let is_overflow = !(target ^ value) & (target ^ result) & 0x80 != 0; + if result <= 0x00FF { + result -= 0x60; + } + let is_carry = result > 0xFF; + let result = T::from_u32(result as u32); + (result, [ + Negative(result.is_negative()), + Overflow(is_overflow), + Zero(result.is_zero()), + Carry(is_carry), + ]) + } else { + let target = ((target.to_u32() as u16) as i32) & 0xFFFF; + let value = !(value.to_u32() as u16) as i32; + let mut carry = if carry {1} else {0}; + let mut result: i32 = (target & 0x000F) + (value & 0x000F) + carry; + if result <= 0x000F { + result -= 0x0006; + } + carry = if result > 0x000F {1} else {0}; + result = (target & 0x00F0) + (value & 0x00F0) + (carry << 4) + (result & 0x000F); + if result <= 0x00FF { + result -= 0x0060; } - is_carry = result > carry_compare; - value_mask <<= 4; - bcd_six_sub <<= 4; - carry_shift += 4; - result_mask = (result_mask << 4) | 0xF; - carry_compare = (carry_compare << 4) | 0xF; - } - let result = T::from_u32(result); - (result, [ - Negative(result.is_negative()), - Overflow(original_target.is_overflow(original_value, result)), - Zero(result.is_zero()), - Carry(is_carry), - ]) + carry = if result > 0x00FF {1} else {0}; + result = (target & 0x0F00) + (value & 0x0F00) + (carry << 8) + (result & 0x00FF); + if result <= 0x0FFF { + result -= 0x0600; + } + carry = if result > 0x0FFF {1} else {0}; + result = (target & 0xF000) + (value & 0xF000) + (carry << 12) + (result & 0x0FFF); + let is_overflow = !(target ^ value) & (target ^ result) & 0x8000 != 0; + if result <= 0xFFFF { + result -= 0x6000; + } + let is_carry = result > 0xFFFF; + let result = T::from_u32((result) as u32); + (result, [ + Negative(result.is_negative()), + Overflow(is_overflow), + Zero(result.is_zero()), + Carry(is_carry), + ]) + } } pub fn and(target: T, value: T) -> (T, [Flags; 2]) { @@ -170,11 +188,11 @@ mod alu_tests { let (result, affected_flags) = adc_bin(200_u8, 155_u8, false); assert_eq!(result, 99); - assert_eq!(affected_flags, [Negative(false), Overflow(false), Zero(false), Carry(true)]); + assert_eq!(affected_flags, [Negative(false), Overflow(true), Zero(false), Carry(true)]); let (result, affected_flags) = adc_bin(200_u8, 155_u8, true); assert_eq!(result, 100); - assert_eq!(affected_flags, [Negative(false), Overflow(false), Zero(false), Carry(true)]); + assert_eq!(affected_flags, [Negative(false), Overflow(true), Zero(false), Carry(true)]); let (result, affected_flags) = adc_bin(200_u8, 54_u8, true); assert_eq!(result, 255); @@ -184,6 +202,14 @@ mod alu_tests { assert_eq!(result, 255); assert_eq!(affected_flags, [Negative(true), Overflow(false), Zero(false), Carry(false)]); + let (result, affected_flags) = adc_bin(0x7F_u8, 0x81_u8, false); + assert_eq!(result, 0x00); + assert_eq!(affected_flags, [Negative(false), Overflow(false), Zero(true), Carry(true)]); + + let (result, affected_flags) = adc_bin(0x7F_u8, 0x7F_u8, true); + assert_eq!(result, 0xFF); + assert_eq!(affected_flags, [Negative(true), Overflow(true), Zero(false), Carry(false)]); + // 16 bits let (result, affected_flags) = adc_bin(0_u16, 0_u16, false); assert_eq!(result, 0); @@ -195,11 +221,11 @@ mod alu_tests { let (result, affected_flags) = adc_bin(65530_u16, 10_u16, false); assert_eq!(result, 4); - assert_eq!(affected_flags, [Negative(false), Overflow(true), Zero(false), Carry(true)]); + assert_eq!(affected_flags, [Negative(false), Overflow(false), Zero(false), Carry(true)]); let (result, affected_flags) = adc_bin(65530_u16, 10_u16, true); assert_eq!(result, 5); - assert_eq!(affected_flags, [Negative(false), Overflow(true), Zero(false), Carry(true)]); + assert_eq!(affected_flags, [Negative(false), Overflow(false), Zero(false), Carry(true)]); let (result, affected_flags) = adc_bin(65530_u16, 4_u16, true); assert_eq!(result, 65535); @@ -266,35 +292,35 @@ mod alu_tests { #[test] fn test_dec_bin() { // 8 bit - let (result, affected_flags) = sbc_bin(1_u8, 1_u8, false); + let (result, affected_flags) = sbc_bin(1_u8, 1_u8, true); assert_eq!(result, 0); - assert_eq!(affected_flags, [Negative(false), Overflow(false), Zero(true), Carry(false)]); - - let (result, affected_flags) = sbc_bin(0_u8, 1_u8, false); - assert_eq!(result, 0b11111111); - assert_eq!(affected_flags, [Negative(true), Overflow(false), Zero(false), Carry(true)]); + assert_eq!(affected_flags, [Negative(false), Overflow(false), Zero(true), Carry(true)]); let (result, affected_flags) = sbc_bin(0_u8, 1_u8, true); + assert_eq!(result, 0b11111111); + assert_eq!(affected_flags, [Negative(true), Overflow(false), Zero(false), Carry(false)]); + + let (result, affected_flags) = sbc_bin(0_u8, 1_u8, false); assert_eq!(result, 0b11111110); - assert_eq!(affected_flags, [Negative(true), Overflow(false), Zero(false), Carry(true)]); + assert_eq!(affected_flags, [Negative(true), Overflow(false), Zero(false), Carry(false)]); // overflow - let (result, affected_flags) = sbc_bin(0x50_u8, 0xB0_u8, false); + let (result, affected_flags) = sbc_bin(0x50_u8, 0xB0_u8, true); assert_eq!(result, 0xA0); - assert_eq!(affected_flags, [Negative(true), Overflow(true), Zero(false), Carry(true)]); + assert_eq!(affected_flags, [Negative(true), Overflow(true), Zero(false), Carry(false)]); // 16 bit - let (result, affected_flags) = sbc_bin(1_u16, 1_u16, false); + let (result, affected_flags) = sbc_bin(1_u16, 1_u16, true); assert_eq!(result, 0); - assert_eq!(affected_flags, [Negative(false), Overflow(false), Zero(true), Carry(false)]); - - let (result, affected_flags) = sbc_bin(0_u16, 1_u16, false); - assert_eq!(result, 0b11111111_11111111); - assert_eq!(affected_flags, [Negative(true), Overflow(false), Zero(false), Carry(true)]); + assert_eq!(affected_flags, [Negative(false), Overflow(false), Zero(true), Carry(true)]); let (result, affected_flags) = sbc_bin(0_u16, 1_u16, true); + assert_eq!(result, 0b11111111_11111111); + assert_eq!(affected_flags, [Negative(true), Overflow(false), Zero(false), Carry(false)]); + + let (result, affected_flags) = sbc_bin(0_u16, 1_u16, false); assert_eq!(result, 0b11111111_11111110); - assert_eq!(affected_flags, [Negative(true), Overflow(false), Zero(false), Carry(true)]); + assert_eq!(affected_flags, [Negative(true), Overflow(false), Zero(false), Carry(false)]); } #[test] diff --git a/snes-core/src/utils/num_trait.rs b/snes-core/src/utils/num_trait.rs index 0630f24..b7d6f1a 100644 --- a/snes-core/src/utils/num_trait.rs +++ b/snes-core/src/utils/num_trait.rs @@ -1,4 +1,4 @@ -pub trait SnesNum: Copy + Clone + Sized + Eq + PartialEq { +pub trait SnesNum: Copy + Clone + Sized + Eq + PartialEq + PartialOrd { fn add_will_carry(&self, v: Self, carry: bool) -> bool; fn sbc_will_carry(&self, v: Self, carry: bool) -> bool; fn is_overflow(&self, v: Self, r: Self) -> bool; @@ -46,10 +46,10 @@ macro_rules! define_operation { macro_rules! define_is_overflow { ($t:ty) => { fn is_overflow(&self, v: $t, r: $t) -> bool { - let target = (*self).is_negative(); - let value = v.is_negative(); - let result = r.is_negative(); - (target ^ result) && (target ^ value) + let target_msb = (*self).is_negative(); + let value_msb = v.is_negative(); + let result_msb = r.is_negative(); + (target_msb ^ result_msb) && !(target_msb ^ value_msb) } } } diff --git a/snes-cpu-test-runner/Cargo.toml b/snes-cpu-test-runner/Cargo.toml new file mode 100644 index 0000000..ea44668 --- /dev/null +++ b/snes-cpu-test-runner/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "snes-cpu-test-runner" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +snes-core = { path = "../snes-core" } + +serde = { version = "1.0.195", features = ["derive"] } +serde_json = "1.0.111" diff --git a/snes-cpu-test-runner/src/main.rs b/snes-cpu-test-runner/src/main.rs new file mode 100644 index 0000000..26e7172 --- /dev/null +++ b/snes-cpu-test-runner/src/main.rs @@ -0,0 +1,161 @@ +use snes_core::cpu::bus::Bus; +use snes_core::cpu::registers::Registers; +/// https://github.com/TomHarte/ProcessorTests/tree/main/65816 + +use snes_core::emulator::Emulator; +use serde::{Deserialize, Serialize}; +use serde_json::Result; +use snes_core::rom::special_ram_cart::SpecialRAMCart; + +use std::fs::File; +use std::io::Read; + +#[derive(Serialize, Deserialize, Debug)] +pub struct TestState { + pc: usize, + s: usize, + p: usize, + a: usize, + x: usize, + y: usize, + dbr: usize, + d: usize, + pbr: usize, + e: usize, + ram: Vec<(usize, usize)>, +} + +#[derive(Serialize, Deserialize)] +pub struct TestSuite { + name: String, + initial: TestState, + r#final: TestState, + cycles: Vec<(usize, Option, String)>, +} + +#[derive(Serialize, Deserialize)] +pub struct TestSuiteList(Vec); + +fn print_failed(initial_state: &TestState, expected_state: &TestState, bus: &Bus, cpu_registers: &Registers) { + eprintln!("FAILED!"); + eprintln!("----------"); + eprintln!("Initial:"); + eprintln!("{:?}", initial_state); + eprintln!("Expected:"); + eprintln!("{:?}", expected_state); + eprintln!("Result:"); + let mut result_ram = vec![]; + for (address, _) in &expected_state.ram { + result_ram.push((*address, bus.read_external(*address as u32) as usize)) + } + let result_state = TestState { + pc: cpu_registers.pc as usize, + s: cpu_registers.sp as usize, + p: cpu_registers.p as usize, + a: cpu_registers.a as usize, + x: cpu_registers.x as usize, + y: cpu_registers.y as usize, + dbr: cpu_registers.dbr as usize, + d: cpu_registers.d as usize, + pbr: cpu_registers.pbr as usize, + e: if cpu_registers.emulation_mode {1} else {0}, + ram: result_ram, + }; + eprintln!("{:?}", result_state); + eprintln!("----------"); + // std::process::exit(1); +} + +fn main() -> Result<()> { + let args: Vec = std::env::args().collect(); + if args.len() < 2 { + eprintln!("A test file must be provided"); + std::process::exit(1); + } + let filename = args.get(1).unwrap(); + if filename.is_empty() { + eprintln!("A test file must be provided"); + std::process::exit(1); + } + let mut file = File::open(filename).unwrap(); + let mut buff = String::new(); + file.read_to_string(&mut buff).unwrap(); + + let tests: TestSuiteList = serde_json::from_str(&buff)?; + + let mut emulator = Emulator::new(); + emulator.bus.force_cart_lookup = true; + emulator.bus.rom = Box::new(SpecialRAMCart::new()); + + let mut total_failed = 0; + let mut total_passed = 0; + + for test in &tests.0 { + let mut did_test_fail = false; + println!("running test case {}", test.name); + + emulator.cpu.registers.pc = test.initial.pc as u16; + emulator.cpu.registers.sp = test.initial.s as u16; + emulator.cpu.registers.p = test.initial.p as u8; + emulator.cpu.registers.a = test.initial.a as u16; + emulator.cpu.registers.x = test.initial.x as u16; + emulator.cpu.registers.y = test.initial.y as u16; + emulator.cpu.registers.dbr = test.initial.dbr as u8; + emulator.cpu.registers.d = test.initial.d as u16; + emulator.cpu.registers.pbr = test.initial.pbr as u8; + emulator.cpu.registers.emulation_mode = test.initial.e == 1; + + for (address, value) in &test.initial.ram { + emulator.bus.write(*address as u32, *value as u8); + } + + emulator.tick(); + + let is_emu_mode = emulator.cpu.registers.emulation_mode; + let is_16index = emulator.cpu.registers.is_16bit_index(); + + let emu_mode_sp = (emulator.cpu.registers.sp as usize & 0xFF) | 0x100; + + // compare the results + if + emulator.cpu.registers.pc as usize != test.r#final.pc || + (if is_emu_mode {emu_mode_sp} else {emulator.cpu.registers.sp as usize}) != test.r#final.s || + emulator.cpu.registers.p as usize != test.r#final.p || + emulator.cpu.registers.a as usize != test.r#final.a || + if is_16index {emulator.cpu.registers.x as usize} else {emulator.cpu.registers.x as u8 as usize} != test.r#final.x || + if is_16index {emulator.cpu.registers.y as usize} else {emulator.cpu.registers.y as u8 as usize} != test.r#final.y || + emulator.cpu.registers.dbr as usize != test.r#final.dbr || + emulator.cpu.registers.d as usize != test.r#final.d || + emulator.cpu.registers.pbr as usize != test.r#final.pbr || + emulator.cpu.registers.emulation_mode != (test.r#final.e == 1) + { + print_failed(&test.initial, &test.r#final, &emulator.bus, &emulator.cpu.registers); + did_test_fail = true; + } + for (address, value) in &test.r#final.ram { + if *value != emulator.bus.read_external(*address as u32) as usize { + print_failed(&test.initial, &test.r#final, &emulator.bus, &emulator.cpu.registers); + did_test_fail = true; + } + } + + if did_test_fail { + total_failed += 1; + } else { + total_passed += 1; + } + + // Cleanup RAM + for (address, _) in &test.initial.ram { + emulator.bus.write(*address as u32, 0x00); + } + for (address, _) in &test.r#final.ram { + emulator.bus.write(*address as u32, 0x00); + } + } + + println!("----------"); + println!("TOTAL PASSED: {}", total_passed); + println!("TOTAL FAILED: {}", total_failed); + Ok(()) +}