mirror of
https://github.com/FranLMSP/snes.git
synced 2026-08-03 08:30:01 -04:00
DEC, DEX and DEY instructions
This commit is contained in:
@@ -198,6 +198,14 @@ impl CPU {
|
||||
let (bytes, cycles) = self.common_conditions(addressing_mode, &COMP_INDEX_CONDITIONS);
|
||||
self.registers.increment_pc(bytes); self.cycles += cycles;
|
||||
}
|
||||
|
||||
pub fn increment_cycles_dec(&mut self, addressing_mode: AddressingMode) {
|
||||
self.increment_cycles_shift(addressing_mode);
|
||||
}
|
||||
|
||||
pub fn increment_cycles_dec_index(&mut self) {
|
||||
self.registers.increment_pc(1); self.cycles += 2;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -7,23 +7,57 @@ use crate::common::flags::Flags;
|
||||
|
||||
impl CPU {
|
||||
fn get_8bit_from_address(&self, bus: &Bus, addressing_mode: AddressingMode) -> u8 {
|
||||
addressing_mode.value_8bit(
|
||||
bus,
|
||||
self.registers.get_pc_address(),
|
||||
self.registers.d,
|
||||
self.registers.sp,
|
||||
self.registers.x, self.registers.y
|
||||
)
|
||||
match addressing_mode {
|
||||
AddressingMode::Accumulator => self.registers.a as u8,
|
||||
_ => addressing_mode.value_8bit(
|
||||
bus,
|
||||
self.registers.get_pc_address(),
|
||||
self.registers.d,
|
||||
self.registers.sp,
|
||||
self.registers.x, self.registers.y
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_16bit_from_address(&self, bus: &Bus, addressing_mode: AddressingMode) -> u16 {
|
||||
addressing_mode.value_16bit(
|
||||
bus,
|
||||
self.registers.get_pc_address(),
|
||||
self.registers.d,
|
||||
self.registers.sp,
|
||||
self.registers.x, self.registers.y
|
||||
)
|
||||
match addressing_mode {
|
||||
AddressingMode::Accumulator => self.registers.a,
|
||||
_ => addressing_mode.value_16bit(
|
||||
bus,
|
||||
self.registers.get_pc_address(),
|
||||
self.registers.d,
|
||||
self.registers.sp,
|
||||
self.registers.x, self.registers.y
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_8bit_to_address(&mut self, bus: &mut Bus, addressing_mode: AddressingMode, value: u8) {
|
||||
match addressing_mode {
|
||||
AddressingMode::Accumulator => self.registers.set_low_a(value),
|
||||
_ => addressing_mode.store_8bit(
|
||||
bus,
|
||||
self.registers.get_pc_address(),
|
||||
self.registers.d,
|
||||
self.registers.sp,
|
||||
self.registers.x, self.registers.y,
|
||||
value,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
fn set_16bit_to_address(&mut self, bus: &mut Bus, addressing_mode: AddressingMode, value: u16) {
|
||||
match addressing_mode {
|
||||
AddressingMode::Accumulator => self.registers.a = value,
|
||||
_ => addressing_mode.store_16bit(
|
||||
bus,
|
||||
self.registers.get_pc_address(),
|
||||
self.registers.d,
|
||||
self.registers.sp,
|
||||
self.registers.x, self.registers.y,
|
||||
value,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
fn adc(&mut self, bus: &Bus, addressing_mode: AddressingMode) {
|
||||
@@ -76,6 +110,50 @@ impl CPU {
|
||||
self.increment_cycles_arithmetic(addressing_mode);
|
||||
}
|
||||
|
||||
fn do_dec<T: SnesNum>(&mut self, target: T) -> T {
|
||||
let (result, affected_flags) = alu::sbc_bin(target, T::from_u32(1), false);
|
||||
for flag in affected_flags {
|
||||
match flag {
|
||||
Flags::Negative(_) | Flags::Zero(_) => self.registers.set_flags(&[flag]),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn dec(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
||||
if self.registers.is_16bit_mode() {
|
||||
let value = self.get_16bit_from_address(bus, addressing_mode);
|
||||
let result = self.do_dec(value).to_u32() as u16;
|
||||
self.set_16bit_to_address(bus, addressing_mode, result);
|
||||
} else {
|
||||
let value = self.get_8bit_from_address(bus, addressing_mode);
|
||||
let result = self.do_dec(value).to_u32() as u8;
|
||||
self.set_8bit_to_address(bus, addressing_mode, result);
|
||||
}
|
||||
self.increment_cycles_dec(addressing_mode);
|
||||
}
|
||||
|
||||
fn dex(&mut self) {
|
||||
if self.registers.is_16bit_index() {
|
||||
self.registers.x = self.do_dec(self.registers.x);
|
||||
} else {
|
||||
let result = self.do_dec(self.registers.x).to_u32() as u8;
|
||||
self.registers.set_low_x(result);
|
||||
}
|
||||
self.increment_cycles_dec_index();
|
||||
}
|
||||
|
||||
fn dey(&mut self) {
|
||||
if self.registers.is_16bit_index() {
|
||||
self.registers.y = self.do_dec(self.registers.y);
|
||||
} else {
|
||||
let result = self.do_dec(self.registers.y).to_u32() as u8;
|
||||
self.registers.set_low_y(result);
|
||||
}
|
||||
self.increment_cycles_dec_index();
|
||||
}
|
||||
|
||||
fn do_comp<T: SnesNum>(&mut self, target: T, value: T) {
|
||||
let (_, affected_flags) = alu::sbc_bin(target, value, false);
|
||||
for flag in affected_flags {
|
||||
@@ -142,7 +220,7 @@ impl CPU {
|
||||
self.increment_cycles_bitwise(addressing_mode);
|
||||
}
|
||||
|
||||
fn asl(&mut self, bus: &Bus, addressing_mode: AddressingMode) {
|
||||
fn asl(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) {
|
||||
let target = match addressing_mode {
|
||||
AddressingMode::Accumulator => self.registers.a,
|
||||
_ => match self.registers.is_16bit_mode() {
|
||||
@@ -152,11 +230,11 @@ impl CPU {
|
||||
};
|
||||
if self.registers.is_16bit_mode() {
|
||||
let (result, affected_flags) = alu::asl(target);
|
||||
self.registers.a = result;
|
||||
self.set_16bit_to_address(bus, addressing_mode, result);
|
||||
self.registers.set_flags(&affected_flags);
|
||||
} else {
|
||||
let (result, affected_flags) = alu::asl(target as u8);
|
||||
self.registers.set_low_a(result);
|
||||
self.set_8bit_to_address(bus, addressing_mode, result);
|
||||
self.registers.set_flags(&affected_flags);
|
||||
}
|
||||
self.increment_cycles_shift(addressing_mode);
|
||||
@@ -304,7 +382,7 @@ impl CPU {
|
||||
self.increment_cycles_clear();
|
||||
}
|
||||
|
||||
pub fn execute_opcode(&mut self, opcode: u8, bus: &Bus) {
|
||||
pub fn execute_opcode(&mut self, opcode: u8, bus: &mut Bus) {
|
||||
type A = AddressingMode;
|
||||
type I = IndexRegister;
|
||||
match opcode {
|
||||
@@ -424,6 +502,16 @@ impl CPU {
|
||||
0xC0 => self.cpy(bus, A::Immediate),
|
||||
0xCC => self.cpy(bus, A::Absolute),
|
||||
0xC4 => self.cpy(bus, A::DirectPage),
|
||||
// DEC
|
||||
0x3A => self.dec(bus, A::Accumulator),
|
||||
0xCE => self.dec(bus, A::Absolute),
|
||||
0xC6 => self.dec(bus, A::DirectPage),
|
||||
0xDE => self.dec(bus, A::AbsoluteIndexed(I::X)),
|
||||
0xD6 => self.dec(bus, A::DirectPageIndexed(I::X)),
|
||||
// DEX
|
||||
0xCA => self.dex(),
|
||||
// DEY
|
||||
0x88 => self.dey(),
|
||||
_ => println!("Invalid opcode: {:02X}", opcode),
|
||||
}
|
||||
}
|
||||
@@ -487,12 +575,12 @@ mod cpu_instructions_tests {
|
||||
#[test]
|
||||
fn test_asl() {
|
||||
let mut cpu = CPU::new();
|
||||
let bus = Bus::new();
|
||||
let mut bus = Bus::new();
|
||||
cpu.registers.a = 0b01010000_00000000;
|
||||
cpu.registers.pbr = 0x00;
|
||||
cpu.registers.pc = 0x0000;
|
||||
cpu.registers.set_memory_select_flag(false);
|
||||
cpu.asl(&bus, AddressingMode::Accumulator);
|
||||
cpu.asl(&mut bus, AddressingMode::Accumulator);
|
||||
assert_eq!(cpu.registers.a, 0b10100000_00000000);
|
||||
assert_eq!(cpu.registers.pc, 0x01);
|
||||
assert_eq!(cpu.cycles, 4);
|
||||
@@ -984,4 +1072,48 @@ mod cpu_instructions_tests {
|
||||
assert!(!cpu.registers.get_zero_flag());
|
||||
assert!(!cpu.registers.get_overflow_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dec() {
|
||||
let mut cpu = CPU::new();
|
||||
let mut bus = Bus::new();
|
||||
cpu.registers.a = 0x0001;
|
||||
cpu.registers.pbr = 0x00;
|
||||
cpu.registers.pc = 0x0000;
|
||||
cpu.registers.set_memory_select_flag(true);
|
||||
cpu.dec(&mut bus, AddressingMode::Accumulator);
|
||||
assert_eq!(cpu.registers.a, 0);
|
||||
assert_eq!(cpu.registers.pc, 0x01);
|
||||
assert_eq!(cpu.cycles, 2);
|
||||
assert!(!cpu.registers.get_negative_flag());
|
||||
assert!(cpu.registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dex() {
|
||||
let mut cpu = CPU::new();
|
||||
cpu.registers.x = 0x0001;
|
||||
cpu.registers.pbr = 0x00;
|
||||
cpu.registers.pc = 0x0000;
|
||||
cpu.dex();
|
||||
assert_eq!(cpu.registers.x, 0);
|
||||
assert_eq!(cpu.registers.pc, 0x01);
|
||||
assert_eq!(cpu.cycles, 2);
|
||||
assert!(!cpu.registers.get_negative_flag());
|
||||
assert!(cpu.registers.get_zero_flag());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dey() {
|
||||
let mut cpu = CPU::new();
|
||||
cpu.registers.y = 0x0001;
|
||||
cpu.registers.pbr = 0x00;
|
||||
cpu.registers.pc = 0x0000;
|
||||
cpu.dey();
|
||||
assert_eq!(cpu.registers.y, 0);
|
||||
assert_eq!(cpu.registers.pc, 0x01);
|
||||
assert_eq!(cpu.cycles, 2);
|
||||
assert!(!cpu.registers.get_negative_flag());
|
||||
assert!(cpu.registers.get_zero_flag());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,14 @@ impl Registers {
|
||||
self.a = (self.a & 0xFF00) | (val as u16);
|
||||
}
|
||||
|
||||
pub fn set_low_x(&mut self, val: u8) {
|
||||
self.x = (self.x & 0xFF00) | (val as u16);
|
||||
}
|
||||
|
||||
pub fn set_low_y(&mut self, val: u8) {
|
||||
self.y = (self.y & 0xFF00) | (val as u16);
|
||||
}
|
||||
|
||||
pub fn get_carry_flag(&self) -> bool {
|
||||
(self.p & 0b0000_0001) == 1
|
||||
}
|
||||
@@ -212,6 +220,22 @@ mod registers_tests {
|
||||
assert_eq!(registers.a, 0xA1FF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_low_x() {
|
||||
let mut registers = Registers::new();
|
||||
registers.x = 0xA1A1;
|
||||
registers.set_low_x(0xFF);
|
||||
assert_eq!(registers.x, 0xA1FF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_low_y() {
|
||||
let mut registers = Registers::new();
|
||||
registers.y = 0xA1A1;
|
||||
registers.set_low_y(0xFF);
|
||||
assert_eq!(registers.y, 0xA1FF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_direct_page_log() {
|
||||
let mut registers = Registers::new();
|
||||
|
||||
@@ -142,6 +142,17 @@ impl AddressingMode {
|
||||
let address = self.effective_address(bus, pc_addr, direct_page_register, stack_pointer, x, y);
|
||||
return (bus.read(address) as u16) | ((bus.read(address + 1) as u16) << 8);
|
||||
}
|
||||
|
||||
pub fn store_8bit(self, bus: &mut Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, x: u16, y: u16, value: u8) {
|
||||
let address = self.effective_address(bus, pc_addr, direct_page_register, stack_pointer, x, y);
|
||||
bus.write(address, value);
|
||||
}
|
||||
|
||||
pub fn store_16bit(self, bus: &mut Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, x: u16, y: u16, value: u16) {
|
||||
let address = self.effective_address(bus, pc_addr, direct_page_register, stack_pointer, x, y);
|
||||
bus.write(address, value as u8);
|
||||
bus.write(address + 1, (value >> 8) as u8);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user