mirror of
https://github.com/FranLMSP/snes.git
synced 2026-01-01 07:21:35 -05:00
Fix ADC and SBC instructions and add test runner script (#5)
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
[workspace]
|
||||
members = ["snes-core", "snes-frontend"]
|
||||
members = ["snes-core", "snes-cpu-test-runner", "snes-frontend"]
|
||||
resolver = "2"
|
||||
|
||||
@@ -10,6 +10,7 @@ pub struct Bus {
|
||||
pub rom: Box<dyn ROM>,
|
||||
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]
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@ use crate::{cpu::registers::Registers, utils::{num_trait::SnesNum, alu}, common:
|
||||
|
||||
|
||||
pub fn do_comp<T: SnesNum>(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]),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{cpu::registers::Registers, utils::{num_trait::SnesNum, alu}, common::flags::Flags};
|
||||
|
||||
pub fn do_dec<T: SnesNum>(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]),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -174,7 +174,7 @@ impl PPURegisters {
|
||||
fn _read(&self, address: u16) -> u8 {
|
||||
match address {
|
||||
0x2100..=0x213F => self.data[(address as usize) - 0x2100],
|
||||
_ => 0xFF,
|
||||
_ => 0x00,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod lo_rom;
|
||||
pub mod special_ram_cart;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
36
snes-core/src/rom/special_ram_cart.rs
Normal file
36
snes-core/src/rom/special_ram_cart.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use super::ROM;
|
||||
|
||||
pub struct SpecialRAMCart {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl SpecialRAMCart {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: vec![0x00; 0x1000000],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ROM for SpecialRAMCart {
|
||||
fn load(&mut self, _filename: &str) -> std::io::Result<bool> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -12,7 +12,7 @@ pub fn adc_bin<T: SnesNum>(target: T, value: T, carry: bool) -> (T, [Flags; 4])
|
||||
}
|
||||
|
||||
pub fn adc_bcd<T: SnesNum>(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<T: SnesNum>(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<T: SnesNum>(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<T: SnesNum>(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<T: SnesNum>(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;
|
||||
}
|
||||
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;
|
||||
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 result = T::from_u32(result);
|
||||
let is_carry = result > 0xFF;
|
||||
let result = T::from_u32(result as u32);
|
||||
(result, [
|
||||
Negative(result.is_negative()),
|
||||
Overflow(original_target.is_overflow(original_value, result)),
|
||||
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;
|
||||
}
|
||||
|
||||
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<T: SnesNum>(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]
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
snes-cpu-test-runner/Cargo.toml
Normal file
12
snes-cpu-test-runner/Cargo.toml
Normal file
@@ -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"
|
||||
161
snes-cpu-test-runner/src/main.rs
Normal file
161
snes-cpu-test-runner/src/main.rs
Normal file
@@ -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<usize>, String)>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct TestSuiteList(Vec<TestSuite>);
|
||||
|
||||
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<String> = 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(())
|
||||
}
|
||||
Reference in New Issue
Block a user