mirror of
https://github.com/FranLMSP/snes.git
synced 2026-10-03 07:55:17 -04:00
Fix a lot of ALU issues
This commit is contained in:
+45
-40
@@ -10,6 +10,7 @@ pub struct Bus {
|
|||||||
pub rom: Box<dyn ROM>,
|
pub rom: Box<dyn ROM>,
|
||||||
pub internal_registers: InternalRegisters,
|
pub internal_registers: InternalRegisters,
|
||||||
pub dma: DMA,
|
pub dma: DMA,
|
||||||
|
pub force_cart_lookup: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
@@ -30,6 +31,7 @@ impl Bus {
|
|||||||
rom: Box::new(LoROM::new()),
|
rom: Box::new(LoROM::new()),
|
||||||
internal_registers: InternalRegisters::new(),
|
internal_registers: InternalRegisters::new(),
|
||||||
dma: DMA::new(),
|
dma: DMA::new(),
|
||||||
|
force_cart_lookup: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,8 +43,10 @@ impl Bus {
|
|||||||
self.wram[(address & 0xFFFF) as usize] = value;
|
self.wram[(address & 0xFFFF) as usize] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_address(address: u32) -> MemoryMap {
|
fn map_address(&self, address: u32) -> MemoryMap {
|
||||||
return MemoryMap::Cartridge;
|
if self.force_cart_lookup {
|
||||||
|
return MemoryMap::Cartridge;
|
||||||
|
}
|
||||||
let (bank, sub_address) = {
|
let (bank, sub_address) = {
|
||||||
let bank = (address >> 16) as u8;
|
let bank = (address >> 16) as u8;
|
||||||
let sub_address = address as u16;
|
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,
|
/// This function is meant to be used by external parts of the code,
|
||||||
/// for example, to render register info without mutating them
|
/// for example, to render register info without mutating them
|
||||||
pub fn read_external(&self, address: u32) -> u8 {
|
pub fn read_external(&self, address: u32) -> u8 {
|
||||||
let section = Bus::map_address(address);
|
let section = self.map_address(address);
|
||||||
match section {
|
match section {
|
||||||
MemoryMap::WRAM => self.read_wram(address),
|
MemoryMap::WRAM => self.read_wram(address),
|
||||||
MemoryMap::PPU => self.ppu.registers.read_external(address as u16),
|
MemoryMap::PPU => self.ppu.registers.read_external(address as u16),
|
||||||
@@ -80,7 +84,7 @@ impl Bus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&mut self, address: u32) -> u8 {
|
pub fn read(&mut self, address: u32) -> u8 {
|
||||||
let section = Bus::map_address(address);
|
let section = self.map_address(address);
|
||||||
match section {
|
match section {
|
||||||
MemoryMap::WRAM => self.read_wram(address),
|
MemoryMap::WRAM => self.read_wram(address),
|
||||||
MemoryMap::PPU => self.ppu.registers.read(address as u16),
|
MemoryMap::PPU => self.ppu.registers.read(address as u16),
|
||||||
@@ -95,7 +99,7 @@ impl Bus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&mut self, address: u32, value: u8) {
|
pub fn write(&mut self, address: u32, value: u8) {
|
||||||
let section = Bus::map_address(address);
|
let section = self.map_address(address);
|
||||||
match section {
|
match section {
|
||||||
MemoryMap::WRAM => self.write_wram(address, value),
|
MemoryMap::WRAM => self.write_wram(address, value),
|
||||||
MemoryMap::PPU => self.ppu.registers.write(address as u16, value),
|
MemoryMap::PPU => self.ppu.registers.write(address as u16, value),
|
||||||
@@ -124,44 +128,45 @@ mod bus_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_memory_map() {
|
fn test_memory_map() {
|
||||||
assert_eq!(Bus::map_address(0x7E0000), MemoryMap::WRAM);
|
let bus = Bus::new();
|
||||||
assert_eq!(Bus::map_address(0x7F0000), MemoryMap::WRAM);
|
assert_eq!(bus.map_address(0x7E0000), MemoryMap::WRAM);
|
||||||
assert_eq!(Bus::map_address(0x7E0500), MemoryMap::WRAM);
|
assert_eq!(bus.map_address(0x7F0000), MemoryMap::WRAM);
|
||||||
assert_eq!(Bus::map_address(0x000000), MemoryMap::WRAM);
|
assert_eq!(bus.map_address(0x7E0500), MemoryMap::WRAM);
|
||||||
assert_eq!(Bus::map_address(0x3F0000), MemoryMap::WRAM);
|
assert_eq!(bus.map_address(0x000000), MemoryMap::WRAM);
|
||||||
assert_eq!(Bus::map_address(0x3F1FFF), MemoryMap::WRAM);
|
assert_eq!(bus.map_address(0x3F0000), MemoryMap::WRAM);
|
||||||
assert_eq!(Bus::map_address(0x3F0000), MemoryMap::WRAM);
|
assert_eq!(bus.map_address(0x3F1FFF), MemoryMap::WRAM);
|
||||||
assert_eq!(Bus::map_address(0x800000), MemoryMap::WRAM);
|
assert_eq!(bus.map_address(0x3F0000), MemoryMap::WRAM);
|
||||||
assert_eq!(Bus::map_address(0xBF0000), MemoryMap::WRAM);
|
assert_eq!(bus.map_address(0x800000), MemoryMap::WRAM);
|
||||||
assert_eq!(Bus::map_address(0xBF1FFF), MemoryMap::WRAM);
|
assert_eq!(bus.map_address(0xBF0000), MemoryMap::WRAM);
|
||||||
assert_eq!(Bus::map_address(0x3F0000), 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(0x002100), MemoryMap::PPU);
|
||||||
assert_eq!(Bus::map_address(0x0021FF), MemoryMap::PPU);
|
assert_eq!(bus.map_address(0x0021FF), MemoryMap::PPU);
|
||||||
assert_eq!(Bus::map_address(0x3F2100), MemoryMap::PPU);
|
assert_eq!(bus.map_address(0x3F2100), MemoryMap::PPU);
|
||||||
assert_eq!(Bus::map_address(0x3F21FF), MemoryMap::PPU);
|
assert_eq!(bus.map_address(0x3F21FF), MemoryMap::PPU);
|
||||||
assert_eq!(Bus::map_address(0x802100), MemoryMap::PPU);
|
assert_eq!(bus.map_address(0x802100), MemoryMap::PPU);
|
||||||
assert_eq!(Bus::map_address(0x8021FF), MemoryMap::PPU);
|
assert_eq!(bus.map_address(0x8021FF), MemoryMap::PPU);
|
||||||
assert_eq!(Bus::map_address(0xBF2100), MemoryMap::PPU);
|
assert_eq!(bus.map_address(0xBF2100), MemoryMap::PPU);
|
||||||
assert_eq!(Bus::map_address(0xBF21FF), MemoryMap::PPU);
|
assert_eq!(bus.map_address(0xBF21FF), MemoryMap::PPU);
|
||||||
|
|
||||||
assert_eq!(Bus::map_address(0x004200), MemoryMap::CPU);
|
assert_eq!(bus.map_address(0x004200), MemoryMap::CPU);
|
||||||
assert_eq!(Bus::map_address(0x00420F), MemoryMap::CPU);
|
assert_eq!(bus.map_address(0x00420F), MemoryMap::CPU);
|
||||||
assert_eq!(Bus::map_address(0x3F4200), MemoryMap::CPU);
|
assert_eq!(bus.map_address(0x3F4200), MemoryMap::CPU);
|
||||||
assert_eq!(Bus::map_address(0x3F420F), MemoryMap::CPU);
|
assert_eq!(bus.map_address(0x3F420F), MemoryMap::CPU);
|
||||||
assert_eq!(Bus::map_address(0x804200), MemoryMap::CPU);
|
assert_eq!(bus.map_address(0x804200), MemoryMap::CPU);
|
||||||
assert_eq!(Bus::map_address(0x80420F), MemoryMap::CPU);
|
assert_eq!(bus.map_address(0x80420F), MemoryMap::CPU);
|
||||||
assert_eq!(Bus::map_address(0xBF4200), MemoryMap::CPU);
|
assert_eq!(bus.map_address(0xBF4200), MemoryMap::CPU);
|
||||||
assert_eq!(Bus::map_address(0xBF420F), MemoryMap::CPU);
|
assert_eq!(bus.map_address(0xBF420F), MemoryMap::CPU);
|
||||||
|
|
||||||
assert_eq!(Bus::map_address(0x004016), MemoryMap::Joypad);
|
assert_eq!(bus.map_address(0x004016), MemoryMap::Joypad);
|
||||||
assert_eq!(Bus::map_address(0x004017), MemoryMap::Joypad);
|
assert_eq!(bus.map_address(0x004017), MemoryMap::Joypad);
|
||||||
assert_eq!(Bus::map_address(0x3F4016), MemoryMap::Joypad);
|
assert_eq!(bus.map_address(0x3F4016), MemoryMap::Joypad);
|
||||||
assert_eq!(Bus::map_address(0x3F4017), MemoryMap::Joypad);
|
assert_eq!(bus.map_address(0x3F4017), MemoryMap::Joypad);
|
||||||
assert_eq!(Bus::map_address(0x804016), MemoryMap::Joypad);
|
assert_eq!(bus.map_address(0x804016), MemoryMap::Joypad);
|
||||||
assert_eq!(Bus::map_address(0x804017), MemoryMap::Joypad);
|
assert_eq!(bus.map_address(0x804017), MemoryMap::Joypad);
|
||||||
assert_eq!(Bus::map_address(0xBF4016), MemoryMap::Joypad);
|
assert_eq!(bus.map_address(0xBF4016), MemoryMap::Joypad);
|
||||||
assert_eq!(Bus::map_address(0xBF4017), MemoryMap::Joypad);
|
assert_eq!(bus.map_address(0xBF4017), MemoryMap::Joypad);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ mod cpu_instructions_tests {
|
|||||||
assert_eq!(registers.a, 0x0001); // check A is not affected
|
assert_eq!(registers.a, 0x0001); // check A is not affected
|
||||||
assert_eq!(registers.pc, 0x02);
|
assert_eq!(registers.pc, 0x02);
|
||||||
assert_eq!(registers.cycles, 2);
|
assert_eq!(registers.cycles, 2);
|
||||||
assert!(!registers.get_carry_flag());
|
assert!(registers.get_carry_flag());
|
||||||
assert!(registers.get_zero_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.a, 0x0050); // check A is not affected
|
||||||
assert_eq!(registers.pc, 0x02);
|
assert_eq!(registers.pc, 0x02);
|
||||||
assert_eq!(registers.cycles, 2);
|
assert_eq!(registers.cycles, 2);
|
||||||
assert!(registers.get_carry_flag());
|
assert!(!registers.get_carry_flag());
|
||||||
assert!(!registers.get_zero_flag());
|
assert!(!registers.get_zero_flag());
|
||||||
assert!(!registers.get_overflow_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.x, 0x01);
|
||||||
assert_eq!(registers.pc, 0x02);
|
assert_eq!(registers.pc, 0x02);
|
||||||
assert_eq!(registers.cycles, 2);
|
assert_eq!(registers.cycles, 2);
|
||||||
assert!(!registers.get_carry_flag());
|
assert!(registers.get_carry_flag());
|
||||||
assert!(registers.get_zero_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.x, 0x50); // check X is not affected
|
||||||
assert_eq!(registers.pc, 0x03);
|
assert_eq!(registers.pc, 0x03);
|
||||||
assert_eq!(registers.cycles, 3);
|
assert_eq!(registers.cycles, 3);
|
||||||
assert!(registers.get_carry_flag());
|
assert!(!registers.get_carry_flag());
|
||||||
assert!(!registers.get_zero_flag());
|
assert!(!registers.get_zero_flag());
|
||||||
assert!(!registers.get_overflow_flag());
|
assert!(!registers.get_overflow_flag());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ mod cpu_instructions_tests {
|
|||||||
assert_eq!(registers.y, 0x01);
|
assert_eq!(registers.y, 0x01);
|
||||||
assert_eq!(registers.pc, 0x02);
|
assert_eq!(registers.pc, 0x02);
|
||||||
assert_eq!(registers.cycles, 2);
|
assert_eq!(registers.cycles, 2);
|
||||||
assert!(!registers.get_carry_flag());
|
assert!(registers.get_carry_flag());
|
||||||
assert!(registers.get_zero_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.y, 0x50); // check Y is not affected
|
||||||
assert_eq!(registers.pc, 0x03);
|
assert_eq!(registers.pc, 0x03);
|
||||||
assert_eq!(registers.cycles, 3);
|
assert_eq!(registers.cycles, 3);
|
||||||
assert!(registers.get_carry_flag());
|
assert!(!registers.get_carry_flag());
|
||||||
assert!(!registers.get_zero_flag());
|
assert!(!registers.get_zero_flag());
|
||||||
assert!(!registers.get_overflow_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,
|
direct_page_register: registers.d,
|
||||||
stack_pointer: registers.sp,
|
stack_pointer: registers.sp,
|
||||||
x: registers.x, y: registers.y,
|
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,
|
direct_page_register: registers.d,
|
||||||
stack_pointer: registers.sp,
|
stack_pointer: registers.sp,
|
||||||
x: registers.x, y: registers.y,
|
x: registers.x, y: registers.y,
|
||||||
|
dbr: registers.dbr,
|
||||||
},
|
},
|
||||||
bus,
|
bus,
|
||||||
)
|
)
|
||||||
@@ -36,6 +38,7 @@ pub fn read_16bit_from_address(registers: &Registers, bus: &mut Bus, addressing_
|
|||||||
direct_page_register: registers.d,
|
direct_page_register: registers.d,
|
||||||
stack_pointer: registers.sp,
|
stack_pointer: registers.sp,
|
||||||
x: registers.x, y: registers.y,
|
x: registers.x, y: registers.y,
|
||||||
|
dbr: registers.dbr,
|
||||||
},
|
},
|
||||||
bus,
|
bus,
|
||||||
)
|
)
|
||||||
@@ -51,6 +54,7 @@ pub fn write_8bit_to_address(registers: &mut Registers, bus: &mut Bus, addressin
|
|||||||
direct_page_register: registers.d,
|
direct_page_register: registers.d,
|
||||||
stack_pointer: registers.sp,
|
stack_pointer: registers.sp,
|
||||||
x: registers.x, y: registers.y,
|
x: registers.x, y: registers.y,
|
||||||
|
dbr: registers.dbr,
|
||||||
},
|
},
|
||||||
bus,
|
bus,
|
||||||
value,
|
value,
|
||||||
@@ -67,6 +71,7 @@ pub fn write_16bit_to_address(registers: &mut Registers, bus: &mut Bus, addressi
|
|||||||
direct_page_register: registers.d,
|
direct_page_register: registers.d,
|
||||||
stack_pointer: registers.sp,
|
stack_pointer: registers.sp,
|
||||||
x: registers.x, y: registers.y,
|
x: registers.x, y: registers.y,
|
||||||
|
dbr: registers.dbr,
|
||||||
},
|
},
|
||||||
bus,
|
bus,
|
||||||
value,
|
value,
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ mod cpu_instructions_tests {
|
|||||||
assert_eq!(registers.pc, 0x02);
|
assert_eq!(registers.pc, 0x02);
|
||||||
assert_eq!(registers.cycles, 2);
|
assert_eq!(registers.cycles, 2);
|
||||||
assert!(registers.get_zero_flag());
|
assert!(registers.get_zero_flag());
|
||||||
assert!(!registers.get_carry_flag());
|
assert!(registers.get_carry_flag());
|
||||||
|
|
||||||
let mut registers = Registers::new();
|
let mut registers = Registers::new();
|
||||||
let mut bus = Bus::new();
|
let mut bus = Bus::new();
|
||||||
@@ -188,7 +188,7 @@ mod cpu_instructions_tests {
|
|||||||
assert_eq!(registers.pc, 0x03);
|
assert_eq!(registers.pc, 0x03);
|
||||||
assert_eq!(registers.cycles, 3);
|
assert_eq!(registers.cycles, 3);
|
||||||
assert!(registers.get_zero_flag());
|
assert!(registers.get_zero_flag());
|
||||||
assert!(!registers.get_carry_flag());
|
assert!(registers.get_carry_flag());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ impl PPURegisters {
|
|||||||
fn _read(&self, address: u16) -> u8 {
|
fn _read(&self, address: u16) -> u8 {
|
||||||
match address {
|
match address {
|
||||||
0x2100..=0x213F => self.data[(address as usize) - 0x2100],
|
0x2100..=0x213F => self.data[(address as usize) - 0x2100],
|
||||||
_ => 0xFF,
|
_ => 0x00,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use crate::cpu::bus::Bus;
|
|||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct ResolveAddressParams {
|
pub struct ResolveAddressParams {
|
||||||
pub pc_addr: u32,
|
pub pc_addr: u32,
|
||||||
|
pub dbr: u8,
|
||||||
pub direct_page_register: u16,
|
pub direct_page_register: u16,
|
||||||
pub stack_pointer: u16,
|
pub stack_pointer: u16,
|
||||||
pub x: u16,
|
pub x: u16,
|
||||||
@@ -19,15 +20,14 @@ pub fn immediate(pc_addr: u32) -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// OPCODE addr
|
/// OPCODE addr
|
||||||
pub fn absolute(bus: &mut Bus, pc_addr: u32) -> u32 {
|
pub fn absolute(bus: &mut Bus, pc_addr: u32, dbr: u8) -> u32 {
|
||||||
(pc_addr & 0xFF0000) |
|
let addr = (bus.read(pc_addr + 1) as u32) | ((bus.read(pc_addr + 2) as u32) << 8);
|
||||||
(bus.read(pc_addr + 1) as u32) |
|
((dbr as u32) << 16) | addr
|
||||||
((bus.read(pc_addr + 2) as u32) << 8)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// OPCODE (addr)
|
/// OPCODE (addr)
|
||||||
pub fn absolute_indirect(bus: &mut Bus, pc_addr: u32) -> u32 {
|
pub fn absolute_indirect(bus: &mut Bus, pc_addr: u32, dbr: u8) -> u32 {
|
||||||
let addr = absolute(bus, pc_addr);
|
let addr = absolute(bus, pc_addr, dbr);
|
||||||
let dbr = pc_addr & 0xFF0000;
|
let dbr = pc_addr & 0xFF0000;
|
||||||
dbr | ((bus.read(addr) as u32) << 8) | (bus.read(addr + 1) as u32)
|
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)
|
/// OPCODE (addr)
|
||||||
pub fn absolute_indirect_long(bus: &mut Bus, pc_addr: u32) -> u32 {
|
pub fn absolute_indirect_long(bus: &mut Bus, pc_addr: u32, dbr: u8) -> u32 {
|
||||||
let addr = absolute(bus, pc_addr);
|
let addr = absolute(bus, pc_addr, dbr);
|
||||||
((bus.read(addr) as u32) << 16) |
|
((bus.read(addr) as u32) << 16) |
|
||||||
((bus.read(addr + 1) as u32) << 8) |
|
((bus.read(addr + 1) as u32) << 8) |
|
||||||
(bus.read(addr + 2) as u32)
|
(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,X
|
||||||
/// OPCODE addr,Y
|
/// OPCODE addr,Y
|
||||||
pub fn absolute_indexed(bus: &mut Bus, pc_addr: u32, xy: u16) -> u32 {
|
pub fn absolute_indexed(bus: &mut Bus, pc_addr: u32, xy: u16, dbr: u8) -> u32 {
|
||||||
absolute(bus, pc_addr) + (xy as u32)
|
absolute(bus, pc_addr, dbr) + (xy as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// OPCODE (addr)
|
/// OPCODE (addr)
|
||||||
pub fn absolute_indexed_indirect(bus: &mut Bus, pc_addr: u32, xy: u16) -> u32 {
|
pub fn absolute_indexed_indirect(bus: &mut Bus, pc_addr: u32, xy: u16, dbr: u8) -> u32 {
|
||||||
let addr = absolute_indexed(bus, pc_addr, xy);
|
let addr = absolute_indexed(bus, pc_addr, xy, dbr);
|
||||||
let dbr = pc_addr & 0xFF0000;
|
let dbr = pc_addr & 0xFF0000;
|
||||||
dbr | ((bus.read(addr) as u32) << 8) | (bus.read(addr + 1) as u32)
|
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
|
/// OPCODE sr,S
|
||||||
pub fn stack_relative(bus: &mut Bus, pc_addr: u32, stack_pointer: u16) -> u32 {
|
pub fn stack_relative(bus: &mut Bus, pc_addr: u32, stack_pointer: u16, dbr: u8) -> u32 {
|
||||||
absolute_indexed(bus, pc_addr, stack_pointer)
|
absolute_indexed(bus, pc_addr, stack_pointer, dbr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// OPCODE (sr,S),X
|
/// OPCODE (sr,S),X
|
||||||
/// OPCODE (sr,S),Y
|
/// OPCODE (sr,S),Y
|
||||||
pub fn stack_relative_indirect_indexed(bus: &mut Bus, pc_addr: u32, stack_pointer: u16, xy: u16) -> 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) + (xy as u32)
|
absolute_indexed(bus, pc_addr, stack_pointer, dbr) + (xy as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
@@ -167,22 +167,22 @@ impl AddressingMode {
|
|||||||
match self {
|
match self {
|
||||||
Self::Accumulator => p.pc_addr,
|
Self::Accumulator => p.pc_addr,
|
||||||
Self::Immediate => immediate(p.pc_addr),
|
Self::Immediate => immediate(p.pc_addr),
|
||||||
Self::Absolute => absolute(bus, p.pc_addr),
|
Self::Absolute => absolute(bus, p.pc_addr, p.dbr),
|
||||||
Self::AbsoluteIndirect => absolute_indirect(bus, p.pc_addr),
|
Self::AbsoluteIndirect => absolute_indirect(bus, p.pc_addr, p.dbr),
|
||||||
Self::AbsoluteIndirectLong => absolute_indirect_long(bus, p.pc_addr),
|
Self::AbsoluteIndirectLong => absolute_indirect_long(bus, p.pc_addr, p.dbr),
|
||||||
Self::AbsoluteLong => absolute_long(bus, p.pc_addr),
|
Self::AbsoluteLong => absolute_long(bus, p.pc_addr),
|
||||||
Self::DirectPage => direct_page(bus, p.pc_addr, p.direct_page_register),
|
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::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::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::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}),
|
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::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::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::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::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::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::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}),
|
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;
|
let pc_addr = 0x000000;
|
||||||
bus.write(pc_addr + 1, 0x01);
|
bus.write(pc_addr + 1, 0x01);
|
||||||
bus.write(pc_addr + 2, 0x02);
|
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 mut bus = Bus::new();
|
||||||
let pc_addr = 0x7F0000;
|
let pc_addr = 0x7F0000;
|
||||||
bus.write(pc_addr + 1, 0x01);
|
bus.write(pc_addr + 1, 0x01);
|
||||||
bus.write(pc_addr + 2, 0x02);
|
bus.write(pc_addr + 2, 0x02);
|
||||||
assert_eq!(absolute(&mut bus, pc_addr), 0x7F0201);
|
assert_eq!(absolute(&mut bus, pc_addr, 0x7F), 0x7F0201);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -241,21 +241,21 @@ mod addressing_modes_tests {
|
|||||||
bus.write(pc_addr + 1, addr);
|
bus.write(pc_addr + 1, addr);
|
||||||
bus.write(addr as u32, 0x02);
|
bus.write(addr as u32, 0x02);
|
||||||
bus.write((addr + 1) as u32, 0x01);
|
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 pc_addr = 0x7E0010;
|
||||||
let addr = 0x55;
|
let addr = 0x55;
|
||||||
bus.write(pc_addr + 1, addr);
|
bus.write(pc_addr + 1, addr);
|
||||||
bus.write(addr as u32, 0x02);
|
bus.write(addr as u32, 0x02);
|
||||||
bus.write((addr + 1) as u32, 0x01);
|
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 pc_addr = 0x7E0010;
|
||||||
let addr = 0x55;
|
let addr = 0x55;
|
||||||
bus.write(pc_addr + 1, addr);
|
bus.write(pc_addr + 1, addr);
|
||||||
bus.write(addr as u32, 0x02);
|
bus.write(addr as u32, 0x02);
|
||||||
bus.write((addr + 1) as u32, 0x01);
|
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]
|
#[test]
|
||||||
@@ -284,7 +284,7 @@ mod addressing_modes_tests {
|
|||||||
bus.write(addr as u32, 0x03);
|
bus.write(addr as u32, 0x03);
|
||||||
bus.write((addr + 1) as u32, 0x02);
|
bus.write((addr + 1) as u32, 0x02);
|
||||||
bus.write((addr + 2) as u32, 0x01);
|
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;
|
let pc_addr = 0x000000;
|
||||||
bus.write(pc_addr + 1, 0x01);
|
bus.write(pc_addr + 1, 0x01);
|
||||||
bus.write(pc_addr + 2, 0x02);
|
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 mut bus = Bus::new();
|
||||||
let pc_addr = 0x7F0000;
|
let pc_addr = 0x7F0000;
|
||||||
bus.write(pc_addr + 1, 0x01);
|
bus.write(pc_addr + 1, 0x01);
|
||||||
bus.write(pc_addr + 2, 0x02);
|
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]
|
#[test]
|
||||||
@@ -370,7 +370,7 @@ mod addressing_modes_tests {
|
|||||||
bus.write(pc_addr + 1, addr);
|
bus.write(pc_addr + 1, addr);
|
||||||
bus.write(addr as u32, 0x02);
|
bus.write(addr as u32, 0x02);
|
||||||
bus.write((addr + 1) as u32, 0x01);
|
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]
|
#[test]
|
||||||
@@ -480,13 +480,13 @@ mod addressing_modes_tests {
|
|||||||
let pc_addr = 0x000000;
|
let pc_addr = 0x000000;
|
||||||
bus.write(pc_addr + 1, 0x01);
|
bus.write(pc_addr + 1, 0x01);
|
||||||
bus.write(pc_addr + 2, 0x02);
|
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 mut bus = Bus::new();
|
||||||
let pc_addr = 0x7F0000;
|
let pc_addr = 0x7F0000;
|
||||||
bus.write(pc_addr + 1, 0x01);
|
bus.write(pc_addr + 1, 0x01);
|
||||||
bus.write(pc_addr + 2, 0x02);
|
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]
|
#[test]
|
||||||
@@ -495,13 +495,13 @@ mod addressing_modes_tests {
|
|||||||
let pc_addr = 0x000000;
|
let pc_addr = 0x000000;
|
||||||
bus.write(pc_addr + 1, 0x01);
|
bus.write(pc_addr + 1, 0x01);
|
||||||
bus.write(pc_addr + 2, 0x02);
|
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 mut bus = Bus::new();
|
||||||
let pc_addr = 0x7F0000;
|
let pc_addr = 0x7F0000;
|
||||||
bus.write(pc_addr + 1, 0x01);
|
bus.write(pc_addr + 1, 0x01);
|
||||||
bus.write(pc_addr + 2, 0x02);
|
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]
|
#[test]
|
||||||
@@ -512,7 +512,7 @@ mod addressing_modes_tests {
|
|||||||
bus.write(pc_addr + 2, 0x10);
|
bus.write(pc_addr + 2, 0x10);
|
||||||
bus.write(0x001020, 0xFE);
|
bus.write(0x001020, 0xFE);
|
||||||
let val = AddressingMode::Absolute.read_8bit(
|
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,
|
&mut bus,
|
||||||
);
|
);
|
||||||
assert_eq!(val, 0xFE);
|
assert_eq!(val, 0xFE);
|
||||||
@@ -524,7 +524,7 @@ mod addressing_modes_tests {
|
|||||||
bus.write(0x001020, 0xFF);
|
bus.write(0x001020, 0xFF);
|
||||||
bus.write(0x001021, 0xEE);
|
bus.write(0x001021, 0xEE);
|
||||||
let val = AddressingMode::Absolute.read_16bit(
|
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,
|
&mut bus,
|
||||||
);
|
);
|
||||||
assert_eq!(val, 0xEEFF);
|
assert_eq!(val, 0xEEFF);
|
||||||
|
|||||||
+68
-50
@@ -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]) {
|
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 original_value = value;
|
||||||
let nibble_bytes = target.bytes() * 2;
|
let nibble_bytes = target.bytes() * 2;
|
||||||
let mut is_carry = carry;
|
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 bcd_six_add = 0x06;
|
||||||
let mut carry_shift = 0;
|
let mut carry_shift = 0;
|
||||||
|
|
||||||
|
let mut is_overflow = false;
|
||||||
for _ in 0..nibble_bytes {
|
for _ in 0..nibble_bytes {
|
||||||
result = (target & value_mask) + (value & value_mask) + ((is_carry as u32) << carry_shift) + (result & result_mask);
|
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 {
|
if result > carry_compare {
|
||||||
result = result.wrapping_add(bcd_six_add);
|
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);
|
let result = T::from_u32(result);
|
||||||
(result, [
|
(result, [
|
||||||
Negative(result.is_negative()),
|
Negative(result.is_negative()),
|
||||||
Overflow(original_target.is_overflow(original_value, result)),
|
Overflow(is_overflow),
|
||||||
Zero(result.is_zero()),
|
Zero(result.is_zero()),
|
||||||
Carry(is_carry),
|
Carry(is_carry),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sbc_bin<T: SnesNum>(target: T, value: T, carry: bool) -> (T, [Flags; 4]) {
|
pub fn sbc_bin<T: SnesNum>(target: T, value: T, carry: bool) -> (T, [Flags; 4]) {
|
||||||
let result = target.sbc_snes(value, !carry);
|
adc_bin(target, value.invert(), carry)
|
||||||
(result, [
|
|
||||||
Negative(result.is_negative()),
|
|
||||||
Overflow(target.is_overflow(value, result)),
|
|
||||||
Zero(result.is_zero()),
|
|
||||||
Carry(!carry),
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sbc_bcd<T: SnesNum>(target: T, value: T, carry: bool) -> (T, [Flags; 4]) {
|
pub fn sbc_bcd<T: SnesNum>(target: T, value: T, carry: bool) -> (T, [Flags; 4]) {
|
||||||
let original_target = value;
|
if target.bytes() == 1 {
|
||||||
let original_value = value;
|
let target = ((target.to_u32() as u16) as i16) & 0xFF;
|
||||||
let nibble_bytes = target.bytes() * 2;
|
let value = !(value.to_u32() as u16) as i16;
|
||||||
let mut is_carry = carry;
|
let mut carry = if carry {1} else {0};
|
||||||
let target = target.to_u32();
|
let mut result: i16 = (target & 0x000F) + (value & 0x000F) + carry;
|
||||||
let value = value.invert().to_u32();
|
if result <= 0x000F {
|
||||||
let mut result = 0;
|
result -= 0x06;
|
||||||
|
}
|
||||||
let mut value_mask = 0x0F;
|
carry = if result > 0x000F {1} else {0};
|
||||||
let mut result_mask = 0x00;
|
result = (target & 0x00F0) + (value & 0x00F0) + (carry << 4) + (result & 0x000F);
|
||||||
let mut carry_compare = 0x0F;
|
let is_overflow = !(target ^ value) & (target ^ result) & 0x80 != 0;
|
||||||
let mut bcd_six_sub = 0x06;
|
if result <= 0x00FF {
|
||||||
let mut carry_shift = 0;
|
result -= 0x60;
|
||||||
|
}
|
||||||
for _ in 0..nibble_bytes {
|
let is_carry = result > 0xFF;
|
||||||
result = (target & value_mask) + (value & value_mask) + ((is_carry as u32) << carry_shift) + (result & result_mask);
|
let result = T::from_u32(result as u32);
|
||||||
if result <= carry_compare {
|
(result, [
|
||||||
result = result.wrapping_sub(bcd_six_sub);
|
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);
|
carry = if result > 0x00FF {1} else {0};
|
||||||
(result, [
|
result = (target & 0x0F00) + (value & 0x0F00) + (carry << 8) + (result & 0x00FF);
|
||||||
Negative(result.is_negative()),
|
if result <= 0x0FFF {
|
||||||
Overflow(original_target.is_overflow(original_value, result)),
|
result -= 0x0600;
|
||||||
Zero(result.is_zero()),
|
}
|
||||||
Carry(is_carry),
|
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]) {
|
pub fn and<T: SnesNum>(target: T, value: T) -> (T, [Flags; 2]) {
|
||||||
@@ -276,33 +294,33 @@ mod alu_tests {
|
|||||||
// 8 bit
|
// 8 bit
|
||||||
let (result, affected_flags) = sbc_bin(1_u8, 1_u8, true);
|
let (result, affected_flags) = sbc_bin(1_u8, 1_u8, true);
|
||||||
assert_eq!(result, 0);
|
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);
|
let (result, affected_flags) = sbc_bin(0_u8, 1_u8, true);
|
||||||
assert_eq!(result, 0b11111111);
|
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);
|
let (result, affected_flags) = sbc_bin(0_u8, 1_u8, false);
|
||||||
assert_eq!(result, 0b11111110);
|
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
|
// overflow
|
||||||
let (result, affected_flags) = sbc_bin(0x50_u8, 0xB0_u8, true);
|
let (result, affected_flags) = sbc_bin(0x50_u8, 0xB0_u8, true);
|
||||||
assert_eq!(result, 0xA0);
|
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
|
// 16 bit
|
||||||
let (result, affected_flags) = sbc_bin(1_u16, 1_u16, true);
|
let (result, affected_flags) = sbc_bin(1_u16, 1_u16, true);
|
||||||
assert_eq!(result, 0);
|
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);
|
let (result, affected_flags) = sbc_bin(0_u16, 1_u16, true);
|
||||||
assert_eq!(result, 0b11111111_11111111);
|
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);
|
let (result, affected_flags) = sbc_bin(0_u16, 1_u16, false);
|
||||||
assert_eq!(result, 0b11111111_11111110);
|
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]
|
#[test]
|
||||||
@@ -314,7 +332,7 @@ mod alu_tests {
|
|||||||
|
|
||||||
let (result, affected_flags) = sbc_bcd(0x49_u8, 0x50_u8, true);
|
let (result, affected_flags) = sbc_bcd(0x49_u8, 0x50_u8, true);
|
||||||
assert_eq!(result, 0x99);
|
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
|
// 16 bit
|
||||||
let (result, affected_flags) = sbc_bcd(0x4999_u16, 0x4998_u16, false);
|
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);
|
let (result, affected_flags) = sbc_bcd(0x4999_u16, 0x5000_u16, true);
|
||||||
assert_eq!(result, 0x9999);
|
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]
|
#[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 add_will_carry(&self, v: Self, carry: bool) -> bool;
|
||||||
fn sbc_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;
|
fn is_overflow(&self, v: Self, r: Self) -> bool;
|
||||||
|
|||||||
@@ -36,9 +36,11 @@ pub struct TestSuite {
|
|||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct TestSuiteList(Vec<TestSuite>);
|
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!("FAILED!");
|
||||||
eprintln!("----------");
|
eprintln!("----------");
|
||||||
|
eprintln!("Initial:");
|
||||||
|
eprintln!("{:?}", initial_state);
|
||||||
eprintln!("Expected:");
|
eprintln!("Expected:");
|
||||||
eprintln!("{:?}", expected_state);
|
eprintln!("{:?}", expected_state);
|
||||||
eprintln!("Result:");
|
eprintln!("Result:");
|
||||||
@@ -61,7 +63,7 @@ fn print_failed(expected_state: &TestState, bus: &Bus, cpu_registers: &Registers
|
|||||||
};
|
};
|
||||||
eprintln!("{:?}", result_state);
|
eprintln!("{:?}", result_state);
|
||||||
eprintln!("----------");
|
eprintln!("----------");
|
||||||
std::process::exit(1);
|
// std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
@@ -82,9 +84,14 @@ fn main() -> Result<()> {
|
|||||||
let tests: TestSuiteList = serde_json::from_str(&buff)?;
|
let tests: TestSuiteList = serde_json::from_str(&buff)?;
|
||||||
|
|
||||||
let mut emulator = Emulator::new();
|
let mut emulator = Emulator::new();
|
||||||
|
emulator.bus.force_cart_lookup = true;
|
||||||
emulator.bus.rom = Box::new(SpecialRAMCart::new());
|
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);
|
println!("running test case {}", test.name);
|
||||||
|
|
||||||
emulator.cpu.registers.pc = test.initial.pc as u16;
|
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.pbr = test.initial.pbr as u8;
|
||||||
emulator.cpu.registers.emulation_mode = test.initial.e == 1;
|
emulator.cpu.registers.emulation_mode = test.initial.e == 1;
|
||||||
|
|
||||||
for (address, value) in test.initial.ram {
|
for (address, value) in &test.initial.ram {
|
||||||
emulator.bus.write(address as u32, value as u8);
|
emulator.bus.write(*address as u32, *value as u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator.tick();
|
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
|
// compare the results
|
||||||
if
|
if
|
||||||
emulator.cpu.registers.pc as usize != test.r#final.pc ||
|
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.p as usize != test.r#final.p ||
|
||||||
emulator.cpu.registers.a as usize != test.r#final.a ||
|
emulator.cpu.registers.a as usize != test.r#final.a ||
|
||||||
emulator.cpu.registers.x as usize != test.r#final.x ||
|
if is_16index {emulator.cpu.registers.x as usize} else {emulator.cpu.registers.x as u8 as usize} != test.r#final.x ||
|
||||||
emulator.cpu.registers.y as usize != test.r#final.y ||
|
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.dbr as usize != test.r#final.dbr ||
|
||||||
emulator.cpu.registers.d as usize != test.r#final.d ||
|
emulator.cpu.registers.d as usize != test.r#final.d ||
|
||||||
emulator.cpu.registers.pbr as usize != test.r#final.pbr ||
|
emulator.cpu.registers.pbr as usize != test.r#final.pbr ||
|
||||||
emulator.cpu.registers.emulation_mode != (test.r#final.e == 1)
|
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 {
|
for (address, value) in &test.r#final.ram {
|
||||||
if *value != emulator.bus.read_external(*address as u32) as usize {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user