mirror of
https://github.com/FranLMSP/snes.git
synced 2026-01-01 07:21:35 -05:00
Fix a lot of ALU issues
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -11,4 +11,4 @@ pub fn do_comp<T: SnesNum>(registers: &mut Registers, target: T, value: T) {
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -174,7 +174,7 @@ impl PPURegisters {
|
||||
fn _read(&self, address: u16) -> u8 {
|
||||
match address {
|
||||
0x2100..=0x213F => self.data[(address as usize) - 0x2100],
|
||||
_ => 0xFF,
|
||||
_ => 0x00,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(!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]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user