Fix a lot of ALU issues

This commit is contained in:
2024-01-28 17:50:22 -05:00
parent ce66fa728d
commit 8e90c531f1
12 changed files with 208 additions and 150 deletions

View File

@@ -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,8 +43,10 @@ impl Bus {
self.wram[(address & 0xFFFF) as usize] = value;
}
fn map_address(address: u32) -> MemoryMap {
return MemoryMap::Cartridge;
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;
@@ -65,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),
@@ -80,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),
@@ -95,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),
@@ -124,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]

View File

@@ -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());
}
@@ -113,7 +113,7 @@ mod cpu_instructions_tests {
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());
}

View File

@@ -11,4 +11,4 @@ pub fn do_comp<T: SnesNum>(registers: &mut Registers, target: T, value: T) {
_ => {},
}
}
}
}

View File

@@ -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());
}

View File

@@ -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());
}

View File

@@ -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,

View File

@@ -148,7 +148,7 @@ 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();
@@ -188,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]

View File

@@ -174,7 +174,7 @@ impl PPURegisters {
fn _read(&self, address: u16) -> u8 {
match address {
0x2100..=0x213F => self.data[(address as usize) - 0x2100],
_ => 0xFF,
_ => 0x00,
}
}

View File

@@ -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);

View File

@@ -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(!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;
}
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<T: SnesNum>(target: T, value: T) -> (T, [Flags; 2]) {
@@ -276,33 +294,33 @@ mod alu_tests {
// 8 bit
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)]);
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(true), Zero(false), Carry(true)]);
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(true), 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, true);
assert_eq!(result, 0xA0);
assert_eq!(affected_flags, [Negative(true), Overflow(false), 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, true);
assert_eq!(result, 0);
assert_eq!(affected_flags, [Negative(false), Overflow(false), Zero(true), Carry(false)]);
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(true), Zero(false), Carry(true)]);
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(true), Zero(false), Carry(true)]);
assert_eq!(affected_flags, [Negative(true), Overflow(false), Zero(false), Carry(false)]);
}
#[test]
@@ -314,7 +332,7 @@ mod alu_tests {
let (result, affected_flags) = sbc_bcd(0x49_u8, 0x50_u8, true);
assert_eq!(result, 0x99);
assert_eq!(affected_flags, [Negative(true), Overflow(true), Zero(false), Carry(false)]);
assert_eq!(affected_flags, [Negative(true), Overflow(false), Zero(false), Carry(false)]);
// 16 bit
let (result, affected_flags) = sbc_bcd(0x4999_u16, 0x4998_u16, false);
@@ -323,7 +341,7 @@ mod alu_tests {
let (result, affected_flags) = sbc_bcd(0x4999_u16, 0x5000_u16, true);
assert_eq!(result, 0x9999);
assert_eq!(affected_flags, [Negative(true), Overflow(true), Zero(false), Carry(false)]);
assert_eq!(affected_flags, [Negative(true), Overflow(false), Zero(false), Carry(false)]);
}
#[test]

View File

@@ -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;

View File

@@ -36,9 +36,11 @@ pub struct TestSuite {
#[derive(Serialize, Deserialize)]
pub struct TestSuiteList(Vec<TestSuite>);
fn print_failed(expected_state: &TestState, bus: &Bus, cpu_registers: &Registers) {
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:");
@@ -61,7 +63,7 @@ fn print_failed(expected_state: &TestState, bus: &Bus, cpu_registers: &Registers
};
eprintln!("{:?}", result_state);
eprintln!("----------");
std::process::exit(1);
// std::process::exit(1);
}
fn main() -> Result<()> {
@@ -82,9 +84,14 @@ fn main() -> Result<()> {
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());
for test in tests.0 {
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;
@@ -98,34 +105,57 @@ fn main() -> Result<()> {
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);
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 ||
emulator.cpu.registers.sp as usize != test.r#final.s ||
(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 ||
emulator.cpu.registers.x as usize != test.r#final.x ||
emulator.cpu.registers.y as usize != test.r#final.y ||
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.r#final, &emulator.bus, &emulator.cpu.registers);
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.r#final, &emulator.bus, &emulator.cpu.registers);
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!("PASSED!");
println!("----------");
println!("TOTAL PASSED: {}", total_passed);
println!("TOTAL FAILED: {}", total_failed);
Ok(())
}