From 98c2c90c08dcd5f3ba1d96898993f2ae050ebf37 Mon Sep 17 00:00:00 2001 From: Franco Date: Sat, 30 Dec 2023 16:34:08 -0500 Subject: [PATCH] Refactor CPU instructions (#2) --- snes-core/src/cpu/cpu.rs | 48 +- snes-core/src/cpu/instructions.rs | 3121 ----------------- snes-core/src/cpu/instructions/adc.rs | 207 ++ snes-core/src/cpu/instructions/and.rs | 119 + snes-core/src/cpu/instructions/asl.rs | 116 + snes-core/src/cpu/instructions/bcc.rs | 57 + snes-core/src/cpu/instructions/bcs.rs | 57 + snes-core/src/cpu/instructions/beq.rs | 57 + snes-core/src/cpu/instructions/bit.rs | 128 + snes-core/src/cpu/instructions/bit_common.rs | 14 + snes-core/src/cpu/instructions/bmi.rs | 57 + snes-core/src/cpu/instructions/bne.rs | 57 + snes-core/src/cpu/instructions/bpl.rs | 57 + snes-core/src/cpu/instructions/bra.rs | 53 + .../src/cpu/instructions/branch_common.rs | 27 + snes-core/src/cpu/instructions/brk.rs | 36 + snes-core/src/cpu/instructions/brl.rs | 59 + snes-core/src/cpu/instructions/bvc.rs | 57 + snes-core/src/cpu/instructions/bvs.rs | 57 + snes-core/src/cpu/instructions/clc.rs | 40 + snes-core/src/cpu/instructions/cld.rs | 40 + snes-core/src/cpu/instructions/cli.rs | 40 + snes-core/src/cpu/instructions/clv.rs | 40 + snes-core/src/cpu/instructions/cmp.rs | 119 + snes-core/src/cpu/instructions/comp_common.rs | 12 + snes-core/src/cpu/instructions/cop.rs | 36 + snes-core/src/cpu/instructions/cpx.rs | 126 + snes-core/src/cpu/instructions/cpy.rs | 126 + snes-core/src/cpu/instructions/dec.rs | 112 + snes-core/src/cpu/instructions/dec_common.rs | 23 + .../src/cpu/instructions/decoder_common.rs | 128 + snes-core/src/cpu/instructions/dex.rs | 104 + snes-core/src/cpu/instructions/dey.rs | 104 + snes-core/src/cpu/instructions/eor.rs | 113 + snes-core/src/cpu/instructions/inc.rs | 112 + snes-core/src/cpu/instructions/inx.rs | 104 + snes-core/src/cpu/instructions/iny.rs | 104 + snes-core/src/cpu/instructions/jmp.rs | 67 + snes-core/src/cpu/instructions/jsr.rs | 77 + snes-core/src/cpu/instructions/lda.rs | 126 + snes-core/src/cpu/instructions/ldx.rs | 126 + snes-core/src/cpu/instructions/ldy.rs | 126 + snes-core/src/cpu/instructions/lsr.rs | 152 + snes-core/src/cpu/instructions/mapper.rs | 356 ++ snes-core/src/cpu/instructions/mod.rs | 108 + snes-core/src/cpu/instructions/move_common.rs | 29 + snes-core/src/cpu/instructions/mvn.rs | 28 + snes-core/src/cpu/instructions/mvp.rs | 28 + snes-core/src/cpu/instructions/nop.rs | 37 + snes-core/src/cpu/instructions/ora.rs | 113 + snes-core/src/cpu/instructions/pea.rs | 46 + snes-core/src/cpu/instructions/pei.rs | 48 + snes-core/src/cpu/instructions/per.rs | 51 + snes-core/src/cpu/instructions/pha.rs | 48 + snes-core/src/cpu/instructions/phb.rs | 42 + snes-core/src/cpu/instructions/phd.rs | 44 + snes-core/src/cpu/instructions/phk.rs | 43 + snes-core/src/cpu/instructions/php.rs | 42 + snes-core/src/cpu/instructions/phx.rs | 50 + snes-core/src/cpu/instructions/phy.rs | 50 + snes-core/src/cpu/instructions/pla.rs | 56 + snes-core/src/cpu/instructions/plb.rs | 47 + snes-core/src/cpu/instructions/pld.rs | 49 + snes-core/src/cpu/instructions/plp.rs | 44 + snes-core/src/cpu/instructions/plx.rs | 56 + snes-core/src/cpu/instructions/ply.rs | 56 + snes-core/src/cpu/instructions/pull_common.rs | 22 + snes-core/src/cpu/instructions/push_common.rs | 9 + .../src/cpu/instructions/read_write_common.rs | 67 + snes-core/src/cpu/instructions/rep.rs | 45 + snes-core/src/cpu/instructions/rol.rs | 110 + snes-core/src/cpu/instructions/ror.rs | 112 + snes-core/src/cpu/instructions/rti.rs | 36 + snes-core/src/cpu/instructions/rtl.rs | 47 + snes-core/src/cpu/instructions/rts.rs | 45 + snes-core/src/cpu/instructions/sbc.rs | 211 ++ snes-core/src/cpu/instructions/sec.rs | 40 + snes-core/src/cpu/instructions/sed.rs | 40 + snes-core/src/cpu/instructions/sei.rs | 40 + snes-core/src/cpu/instructions/sep.rs | 44 + snes-core/src/cpu/instructions/sta.rs | 105 + snes-core/src/cpu/instructions/stp.rs | 40 + snes-core/src/cpu/instructions/stx.rs | 105 + snes-core/src/cpu/instructions/sty.rs | 105 + snes-core/src/cpu/instructions/stz.rs | 106 + snes-core/src/cpu/instructions/tax.rs | 106 + snes-core/src/cpu/instructions/tay.rs | 107 + snes-core/src/cpu/instructions/tcd.rs | 44 + snes-core/src/cpu/instructions/tcs.rs | 44 + snes-core/src/cpu/instructions/tdc.rs | 44 + snes-core/src/cpu/instructions/trb.rs | 82 + snes-core/src/cpu/instructions/tsb.rs | 80 + snes-core/src/cpu/instructions/tsc.rs | 44 + snes-core/src/cpu/instructions/tsx.rs | 102 + snes-core/src/cpu/instructions/txa.rs | 102 + snes-core/src/cpu/instructions/txs.rs | 102 + snes-core/src/cpu/instructions/txy.rs | 102 + snes-core/src/cpu/instructions/tya.rs | 101 + snes-core/src/cpu/instructions/tyx.rs | 102 + snes-core/src/cpu/instructions/wai.rs | 40 + snes-core/src/cpu/instructions/wdm.rs | 37 + snes-core/src/cpu/instructions/xba.rs | 42 + snes-core/src/cpu/instructions/xce.rs | 38 + snes-core/src/cpu/registers.rs | 6 + snes-core/src/cpu/vectors.rs | 17 +- snes-core/src/emulator.rs | 4 +- snes-core/src/utils/addressing.rs | 12 + snes-frontend/src/cpu.rs | 405 +-- 108 files changed, 7564 insertions(+), 3540 deletions(-) delete mode 100644 snes-core/src/cpu/instructions.rs create mode 100644 snes-core/src/cpu/instructions/adc.rs create mode 100644 snes-core/src/cpu/instructions/and.rs create mode 100644 snes-core/src/cpu/instructions/asl.rs create mode 100644 snes-core/src/cpu/instructions/bcc.rs create mode 100644 snes-core/src/cpu/instructions/bcs.rs create mode 100644 snes-core/src/cpu/instructions/beq.rs create mode 100644 snes-core/src/cpu/instructions/bit.rs create mode 100644 snes-core/src/cpu/instructions/bit_common.rs create mode 100644 snes-core/src/cpu/instructions/bmi.rs create mode 100644 snes-core/src/cpu/instructions/bne.rs create mode 100644 snes-core/src/cpu/instructions/bpl.rs create mode 100644 snes-core/src/cpu/instructions/bra.rs create mode 100644 snes-core/src/cpu/instructions/branch_common.rs create mode 100644 snes-core/src/cpu/instructions/brk.rs create mode 100644 snes-core/src/cpu/instructions/brl.rs create mode 100644 snes-core/src/cpu/instructions/bvc.rs create mode 100644 snes-core/src/cpu/instructions/bvs.rs create mode 100644 snes-core/src/cpu/instructions/clc.rs create mode 100644 snes-core/src/cpu/instructions/cld.rs create mode 100644 snes-core/src/cpu/instructions/cli.rs create mode 100644 snes-core/src/cpu/instructions/clv.rs create mode 100644 snes-core/src/cpu/instructions/cmp.rs create mode 100644 snes-core/src/cpu/instructions/comp_common.rs create mode 100644 snes-core/src/cpu/instructions/cop.rs create mode 100644 snes-core/src/cpu/instructions/cpx.rs create mode 100644 snes-core/src/cpu/instructions/cpy.rs create mode 100644 snes-core/src/cpu/instructions/dec.rs create mode 100644 snes-core/src/cpu/instructions/dec_common.rs create mode 100644 snes-core/src/cpu/instructions/decoder_common.rs create mode 100644 snes-core/src/cpu/instructions/dex.rs create mode 100644 snes-core/src/cpu/instructions/dey.rs create mode 100644 snes-core/src/cpu/instructions/eor.rs create mode 100644 snes-core/src/cpu/instructions/inc.rs create mode 100644 snes-core/src/cpu/instructions/inx.rs create mode 100644 snes-core/src/cpu/instructions/iny.rs create mode 100644 snes-core/src/cpu/instructions/jmp.rs create mode 100644 snes-core/src/cpu/instructions/jsr.rs create mode 100644 snes-core/src/cpu/instructions/lda.rs create mode 100644 snes-core/src/cpu/instructions/ldx.rs create mode 100644 snes-core/src/cpu/instructions/ldy.rs create mode 100644 snes-core/src/cpu/instructions/lsr.rs create mode 100644 snes-core/src/cpu/instructions/mapper.rs create mode 100644 snes-core/src/cpu/instructions/mod.rs create mode 100644 snes-core/src/cpu/instructions/move_common.rs create mode 100644 snes-core/src/cpu/instructions/mvn.rs create mode 100644 snes-core/src/cpu/instructions/mvp.rs create mode 100644 snes-core/src/cpu/instructions/nop.rs create mode 100644 snes-core/src/cpu/instructions/ora.rs create mode 100644 snes-core/src/cpu/instructions/pea.rs create mode 100644 snes-core/src/cpu/instructions/pei.rs create mode 100644 snes-core/src/cpu/instructions/per.rs create mode 100644 snes-core/src/cpu/instructions/pha.rs create mode 100644 snes-core/src/cpu/instructions/phb.rs create mode 100644 snes-core/src/cpu/instructions/phd.rs create mode 100644 snes-core/src/cpu/instructions/phk.rs create mode 100644 snes-core/src/cpu/instructions/php.rs create mode 100644 snes-core/src/cpu/instructions/phx.rs create mode 100644 snes-core/src/cpu/instructions/phy.rs create mode 100644 snes-core/src/cpu/instructions/pla.rs create mode 100644 snes-core/src/cpu/instructions/plb.rs create mode 100644 snes-core/src/cpu/instructions/pld.rs create mode 100644 snes-core/src/cpu/instructions/plp.rs create mode 100644 snes-core/src/cpu/instructions/plx.rs create mode 100644 snes-core/src/cpu/instructions/ply.rs create mode 100644 snes-core/src/cpu/instructions/pull_common.rs create mode 100644 snes-core/src/cpu/instructions/push_common.rs create mode 100644 snes-core/src/cpu/instructions/read_write_common.rs create mode 100644 snes-core/src/cpu/instructions/rep.rs create mode 100644 snes-core/src/cpu/instructions/rol.rs create mode 100644 snes-core/src/cpu/instructions/ror.rs create mode 100644 snes-core/src/cpu/instructions/rti.rs create mode 100644 snes-core/src/cpu/instructions/rtl.rs create mode 100644 snes-core/src/cpu/instructions/rts.rs create mode 100644 snes-core/src/cpu/instructions/sbc.rs create mode 100644 snes-core/src/cpu/instructions/sec.rs create mode 100644 snes-core/src/cpu/instructions/sed.rs create mode 100644 snes-core/src/cpu/instructions/sei.rs create mode 100644 snes-core/src/cpu/instructions/sep.rs create mode 100644 snes-core/src/cpu/instructions/sta.rs create mode 100644 snes-core/src/cpu/instructions/stp.rs create mode 100644 snes-core/src/cpu/instructions/stx.rs create mode 100644 snes-core/src/cpu/instructions/sty.rs create mode 100644 snes-core/src/cpu/instructions/stz.rs create mode 100644 snes-core/src/cpu/instructions/tax.rs create mode 100644 snes-core/src/cpu/instructions/tay.rs create mode 100644 snes-core/src/cpu/instructions/tcd.rs create mode 100644 snes-core/src/cpu/instructions/tcs.rs create mode 100644 snes-core/src/cpu/instructions/tdc.rs create mode 100644 snes-core/src/cpu/instructions/trb.rs create mode 100644 snes-core/src/cpu/instructions/tsb.rs create mode 100644 snes-core/src/cpu/instructions/tsc.rs create mode 100644 snes-core/src/cpu/instructions/tsx.rs create mode 100644 snes-core/src/cpu/instructions/txa.rs create mode 100644 snes-core/src/cpu/instructions/txs.rs create mode 100644 snes-core/src/cpu/instructions/txy.rs create mode 100644 snes-core/src/cpu/instructions/tya.rs create mode 100644 snes-core/src/cpu/instructions/tyx.rs create mode 100644 snes-core/src/cpu/instructions/wai.rs create mode 100644 snes-core/src/cpu/instructions/wdm.rs create mode 100644 snes-core/src/cpu/instructions/xba.rs create mode 100644 snes-core/src/cpu/instructions/xce.rs diff --git a/snes-core/src/cpu/cpu.rs b/snes-core/src/cpu/cpu.rs index 565980c..ae4e4c8 100644 --- a/snes-core/src/cpu/cpu.rs +++ b/snes-core/src/cpu/cpu.rs @@ -1,19 +1,53 @@ -use super::registers::Registers; +use super::{registers::Registers, bus::Bus, cycles, dma, instructions::mapper::map_opcode_to_instruction}; pub struct CPU { pub registers: Registers, - pub cycles: usize, - pub is_stopped: bool, - pub is_waiting_interrupt: bool, } impl CPU { pub fn new() -> Self { Self { registers: Registers::new(), - cycles: 0, - is_stopped: false, - is_waiting_interrupt: false, } } + + fn check_running_state(&mut self, bus: &mut Bus) -> bool { + // Each byte in a DMA transfer takes 8 master cycles. + // And each CPU can take either 6, 8 or 12 master cycles depending + // on what's being read from memory. So this won't be accurate. + if bus.dma.is_active() { + let pending_bus_writes = bus.dma.tick(); + for (src, dst) in pending_bus_writes { + let byte = bus.read(src); + bus.write(dst, byte); + let (bytes, cycles) = cycles::increment_cycles_while_stopped(); + self.registers.increment_pc(bytes); self.registers.cycles += cycles; + } + if !bus.dma.is_active() { + bus.write(dma::MDMAEN as u32, 0x00) + } + return false; + } + if self.registers.is_cpu_stopped { + let (bytes, cycles) = cycles::increment_cycles_while_stopped(); + self.registers.increment_pc(bytes); self.registers.cycles += cycles; + return false; + } + if self.registers.is_cpu_waiting_interrupt { + // TODO: check for interrupts here + let (bytes, cycles) = cycles::increment_cycles_while_stopped(); + self.registers.increment_pc(bytes); self.registers.cycles += cycles; + return false; + } + return true; + } + + pub fn tick(&mut self, bus: &mut Bus) { + if !self.check_running_state(bus) { + return; + } + let opcode = bus.read(self.registers.get_pc_address()); + let instruction = map_opcode_to_instruction(opcode); + instruction.execute(&mut self.registers, bus); + } } diff --git a/snes-core/src/cpu/instructions.rs b/snes-core/src/cpu/instructions.rs deleted file mode 100644 index a56a307..0000000 --- a/snes-core/src/cpu/instructions.rs +++ /dev/null @@ -1,3121 +0,0 @@ -use super::cpu::CPU; -use crate::cpu::bus::Bus; -use crate::cpu::dma; -use crate::cpu::cycles; -use crate::utils::addressing::{AddressingMode, IndexRegister}; -use crate::utils::alu; -use crate::utils::num_trait::SnesNum; -use crate::common::flags::Flags; - -/// TODO: separate this into different files, for example -/// arithmetic instructions in one file, transfers in another file, etc - -impl CPU { - fn get_effective_address(&self, bus: &mut Bus, addressing_mode: AddressingMode) -> u32 { - addressing_mode.effective_address( - bus, - self.registers.get_pc_address(), - self.registers.d, - self.registers.sp, - self.registers.x, self.registers.y - ) - } - - fn get_8bit_from_address(&self, bus: &mut Bus, addressing_mode: AddressingMode) -> u8 { - 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: &mut Bus, addressing_mode: AddressingMode) -> u16 { - 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: &mut Bus, addressing_mode: AddressingMode) { - let carry_flag = self.registers.get_carry_flag(); - let is_decimal_mode = self.registers.get_decimal_mode_flag(); - let is_16bit = self.registers.is_16bit_mode(); - let target = self.registers.a; - if is_16bit { - let value = self.get_16bit_from_address(bus, addressing_mode); - let (result, affected_flags) = match is_decimal_mode { - true => alu::adc_bcd(target, value, carry_flag), - false => alu::adc_bin(target, value, carry_flag), - }; - self.registers.a = result; - self.registers.set_flags(&affected_flags); - } else { - let value = self.get_8bit_from_address(bus, addressing_mode); - let (result, affected_flags) = match is_decimal_mode { - true => alu::adc_bcd(target as u8, value, carry_flag), - false => alu::adc_bin(target as u8, value, carry_flag), - }; - self.registers.set_low_a(result as u8); - self.registers.set_flags(&affected_flags); - } - let (bytes, cycles) = cycles::increment_cycles_arithmetic(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn sbc(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - let carry_flag = self.registers.get_carry_flag(); - let is_decimal_mode = self.registers.get_decimal_mode_flag(); - let is_16bit = self.registers.is_16bit_mode(); - let target = self.registers.a; - if is_16bit { - let value = self.get_16bit_from_address(bus, addressing_mode); - let (result, affected_flags) = match is_decimal_mode { - true => alu::sbc_bcd(target, value, carry_flag), - false => alu::sbc_bin(target, value, carry_flag), - }; - self.registers.a = result; - self.registers.set_flags(&affected_flags); - } else { - let value = self.get_8bit_from_address(bus, addressing_mode); - let (result, affected_flags) = match is_decimal_mode { - true => alu::sbc_bcd(target as u8, value, carry_flag), - false => alu::sbc_bin(target as u8, value, carry_flag), - }; - self.registers.set_low_a(result as u8); - self.registers.set_flags(&affected_flags); - } - let (bytes, cycles) = cycles::increment_cycles_arithmetic(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn do_dec(&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); - } - let (bytes, cycles) = cycles::increment_cycles_inc_dec(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - 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); - } - let (bytes, cycles) = cycles::increment_cycles_inc_dec_index(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - 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); - } - let (bytes, cycles) = cycles::increment_cycles_inc_dec_index(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn do_inc(&mut self, target: T) -> T { - let (result, affected_flags) = alu::adc_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 inc(&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_inc(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_inc(value).to_u32() as u8; - self.set_8bit_to_address(bus, addressing_mode, result); - } - let (bytes, cycles) = cycles::increment_cycles_inc_dec(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn inx(&mut self) { - if self.registers.is_16bit_index() { - self.registers.x = self.do_inc(self.registers.x); - } else { - let result = self.do_inc(self.registers.x).to_u32() as u8; - self.registers.set_low_x(result); - } - let (bytes, cycles) = cycles::increment_cycles_inc_dec_index(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn iny(&mut self) { - if self.registers.is_16bit_index() { - self.registers.y = self.do_inc(self.registers.y); - } else { - let result = self.do_inc(self.registers.y).to_u32() as u8; - self.registers.set_low_y(result); - } - let (bytes, cycles) = cycles::increment_cycles_inc_dec_index(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn do_comp(&mut self, target: T, value: T) { - let (_, affected_flags) = alu::sbc_bin(target, value, false); - for flag in affected_flags { - match flag { - Flags::Overflow(_) => {}, - _ => self.registers.set_flags(&[flag]), - } - } - } - - fn cmp(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - let is_16bit = self.registers.is_16bit_mode(); - let target = self.registers.a; - if is_16bit { - let value = self.get_16bit_from_address(bus, addressing_mode); - self.do_comp(target, value); - } else { - let value = self.get_8bit_from_address(bus, addressing_mode); - self.do_comp(target as u8, value); - } - let (bytes, cycles) = cycles::increment_cycles_arithmetic(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn cpx(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - let is_16bit = self.registers.is_16bit_index(); - let target = self.registers.x; - if is_16bit { - let value = self.get_16bit_from_address(bus, addressing_mode); - self.do_comp(target, value); - } else { - let value = self.get_8bit_from_address(bus, addressing_mode); - self.do_comp(target as u8, value); - } - let (bytes, cycles) = cycles::increment_cycles_comp_index(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn cpy(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - let is_16bit = self.registers.is_16bit_index(); - let target = self.registers.y; - if is_16bit { - let value = self.get_16bit_from_address(bus, addressing_mode); - self.do_comp(target, value); - } else { - let value = self.get_8bit_from_address(bus, addressing_mode); - self.do_comp(target as u8, value); - } - let (bytes, cycles) = cycles::increment_cycles_comp_index(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn and(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - let target = self.registers.a; - if self.registers.is_16bit_mode() { - let value = self.get_16bit_from_address(bus, addressing_mode); - let (result, affected_flags) = alu::and(target, value); - self.registers.a = result; - self.registers.set_flags(&affected_flags); - } else { - let value = self.get_8bit_from_address(bus, addressing_mode); - let (result, affected_flags) = alu::and(target as u8, value); - self.registers.set_low_a(result); - self.registers.set_flags(&affected_flags); - } - let (bytes, cycles) = cycles::increment_cycles_bitwise(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn ora(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - let target = self.registers.a; - if self.registers.is_16bit_mode() { - let value = self.get_16bit_from_address(bus, addressing_mode); - let (result, affected_flags) = alu::ora(target, value); - self.registers.a = result; - self.registers.set_flags(&affected_flags); - } else { - let value = self.get_8bit_from_address(bus, addressing_mode); - let (result, affected_flags) = alu::ora(target as u8, value); - self.registers.set_low_a(result); - self.registers.set_flags(&affected_flags); - } - let (bytes, cycles) = cycles::increment_cycles_bitwise(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn eor(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - let target = self.registers.a; - if self.registers.is_16bit_mode() { - let value = self.get_16bit_from_address(bus, addressing_mode); - let (result, affected_flags) = alu::eor(target, value); - self.registers.a = result; - self.registers.set_flags(&affected_flags); - } else { - let value = self.get_8bit_from_address(bus, addressing_mode); - let (result, affected_flags) = alu::eor(target as u8, value); - self.registers.set_low_a(result); - self.registers.set_flags(&affected_flags); - } - let (bytes, cycles) = cycles::increment_cycles_bitwise(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - 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() { - true => self.get_16bit_from_address(bus, addressing_mode), - false => self.get_8bit_from_address(bus, addressing_mode) as u16, - } - }; - if self.registers.is_16bit_mode() { - let (result, affected_flags) = alu::asl(target); - 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.set_8bit_to_address(bus, addressing_mode, result); - self.registers.set_flags(&affected_flags); - } - let (bytes, cycles) = cycles::increment_cycles_shift(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn lsr(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - let target = match addressing_mode { - AddressingMode::Accumulator => self.registers.a, - _ => match self.registers.is_16bit_mode() { - true => self.get_16bit_from_address(bus, addressing_mode), - false => self.get_8bit_from_address(bus, addressing_mode) as u16, - } - }; - if self.registers.is_16bit_mode() { - let (result, affected_flags) = alu::lsr(target); - self.set_16bit_to_address(bus, addressing_mode, result); - self.registers.set_flags(&affected_flags); - } else { - let (result, affected_flags) = alu::lsr(target as u8); - self.set_8bit_to_address(bus, addressing_mode, result); - self.registers.set_flags(&affected_flags); - } - let (bytes, cycles) = cycles::increment_cycles_shift(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn do_bit(&mut self, accumulator: T, value: T, addressing_mode: AddressingMode) { - let (result, _) = alu::and(accumulator, value); - // Immediate addressing affects only the zero flag - match addressing_mode { - AddressingMode::Immediate => self.registers.set_zero_flag(result.is_zero()), - _ => { - self.registers.set_zero_flag(result.is_zero()); - self.registers.set_negative_flag(value.is_negative()); - self.registers.set_overflow_flag(value.next_to_highest_bit()); - } - }; - } - - fn bit(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - if self.registers.is_16bit_mode() { - let value = self.get_16bit_from_address(bus, addressing_mode); - self.do_bit(self.registers.a, value, addressing_mode); - } else { - let value = self.get_8bit_from_address(bus, addressing_mode); - self.do_bit(self.registers.a as u8, value, addressing_mode); - } - let (bytes, cycles) = cycles::increment_cycles_bit(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn do_branch(&mut self, nearlabel: u8) -> bool { - let is_negative = (nearlabel >> 7) != 0; - let old_pc = self.registers.get_pc_address(); - if is_negative { - let nearlabel = !nearlabel + 1; - self.registers.decrement_pc(nearlabel as u16); - } else { - self.registers.increment_pc(nearlabel as u16); - } - let new_pc = self.registers.get_pc_address(); - let page_boundary_crossed = (old_pc & 0xFF00) != (new_pc & 0xFF00); - return page_boundary_crossed - } - - fn bcc(&mut self, bus: &mut Bus) { - let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1)); - let (bytes, cycles) = cycles::increment_cycles_branch(); - self.registers.increment_pc(bytes); self.cycles += cycles; - if !self.registers.get_carry_flag() { - let page_boundary_crossed = self.do_branch(nearlabel); - let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - } - - fn bcs(&mut self, bus: &mut Bus) { - let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1)); - let (bytes, cycles) = cycles::increment_cycles_branch(); - self.registers.increment_pc(bytes); self.cycles += cycles; - if self.registers.get_carry_flag() { - let page_boundary_crossed = self.do_branch(nearlabel); - let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - } - - fn beq(&mut self, bus: &mut Bus) { - let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1)); - let (bytes, cycles) = cycles::increment_cycles_branch(); - self.registers.increment_pc(bytes); self.cycles += cycles; - if self.registers.get_zero_flag() { - let page_boundary_crossed = self.do_branch(nearlabel); - let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - } - - fn bne(&mut self, bus: &mut Bus) { - let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1)); - let (bytes, cycles) = cycles::increment_cycles_branch(); - self.registers.increment_pc(bytes); self.cycles += cycles; - if !self.registers.get_zero_flag() { - let page_boundary_crossed = self.do_branch(nearlabel); - let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - } - - fn bmi(&mut self, bus: &mut Bus) { - let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1)); - let (bytes, cycles) = cycles::increment_cycles_branch(); - self.registers.increment_pc(bytes); self.cycles += cycles; - if self.registers.get_negative_flag() { - let page_boundary_crossed = self.do_branch(nearlabel); - let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - } - - fn bpl(&mut self, bus: &mut Bus) { - let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1)); - let (bytes, cycles) = cycles::increment_cycles_branch(); - self.registers.increment_pc(bytes); self.cycles += cycles; - if !self.registers.get_negative_flag() { - let page_boundary_crossed = self.do_branch(nearlabel); - let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - } - - fn bra(&mut self, bus: &mut Bus) { - let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1)); - let (bytes, cycles) = cycles::increment_cycles_branch(); - self.registers.increment_pc(bytes); self.cycles += cycles; - let page_boundary_crossed = self.do_branch(nearlabel); - let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn brl(&mut self, bus: &mut Bus) { - let label = bus.read(self.registers.get_pc_address()) as u16 | - ((bus.read(self.registers.get_pc_address() + 1) as u16) << 8); - let is_negative = (label >> 15) != 0; - if is_negative { - let label = !label + 1; - self.registers.decrement_pc(label); - } else { - self.registers.increment_pc(label); - } - let (bytes, cycles) = cycles::increment_cycles_branch_long(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn bvc(&mut self, bus: &mut Bus) { - let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1)); - let (bytes, cycles) = cycles::increment_cycles_branch(); - self.registers.increment_pc(bytes); self.cycles += cycles; - if !self.registers.get_overflow_flag() { - let page_boundary_crossed = self.do_branch(nearlabel); - let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - } - - fn bvs(&mut self, bus: &mut Bus) { - let nearlabel = bus.read(self.registers.get_pc_address().wrapping_add(1)); - let (bytes, cycles) = cycles::increment_cycles_branch(); - self.registers.increment_pc(bytes); self.cycles += cycles; - if self.registers.get_overflow_flag() { - let page_boundary_crossed = self.do_branch(nearlabel); - let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - } - - fn clc(&mut self) { - self.registers.set_carry_flag(false); - let (bytes, cycles) = cycles::increment_cycles_clear(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn cld(&mut self) { - self.registers.set_decimal_mode_flag(false); - let (bytes, cycles) = cycles::increment_cycles_clear(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn cli(&mut self) { - self.registers.set_irq_disable_flag(false); - let (bytes, cycles) = cycles::increment_cycles_clear(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn clv(&mut self) { - self.registers.set_overflow_flag(false); - let (bytes, cycles) = cycles::increment_cycles_clear(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn nop(&mut self) { - let (bytes, cycles) = cycles::increment_cycles_nop(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn jmp(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - let effective_address = self.get_effective_address(bus, addressing_mode); - let is_long = match addressing_mode { - AddressingMode::AbsoluteLong | - AddressingMode::AbsoluteIndirectLong => true, - _ => false, - }; - self.registers.pc = effective_address as u16; - if is_long { - self.registers.pbr = (effective_address >> 16) as u8; - } - let (bytes, cycles) = cycles::increment_cycles_jmp(addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - pub fn do_push(&mut self, bus: &mut Bus, bytes: &[u8]) { - for byte in bytes { - let address = self.registers.sp as u32; - bus.write(address, *byte); - self.registers.decrement_sp(1); - } - } - - fn brk(&mut self, bus: &mut Bus) { - self.do_push(bus, &[self.registers.pbr]); - self.do_push(bus, &[(self.registers.pc >> 8) as u8, self.registers.pc as u8]); - self.do_push(bus, &[self.registers.p]); - self.registers.set_decimal_mode_flag(false); - self.registers.set_irq_disable_flag(true); - let (bytes, cycles) = cycles::increment_cycles_brk(self.registers.emulation_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn cop(&mut self, bus: &mut Bus) { - self.do_push(bus, &[self.registers.pbr]); - self.do_push(bus, &[(self.registers.pc >> 8) as u8, self.registers.pc as u8]); - self.do_push(bus, &[self.registers.p]); - self.registers.set_decimal_mode_flag(false); - self.registers.set_irq_disable_flag(true); - let (bytes, cycles) = cycles::increment_cycles_brk(self.registers.emulation_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn pea(&mut self, bus: &mut Bus) { - let address = self.get_effective_address(bus, AddressingMode::Absolute); - self.do_push(bus, &[(address >> 8) as u8, address as u8]); - let (bytes, cycles) = cycles::increment_cycles_pea(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn pei(&mut self, bus: &mut Bus) { - let address = self.get_effective_address(bus, AddressingMode::DirectPageIndirect); - self.do_push(bus, &[(address >> 8) as u8, address as u8]); - let (bytes, cycles) = cycles::increment_cycles_pei(&self.registers); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn per(&mut self, bus: &mut Bus) { - let label = self.get_effective_address(bus, AddressingMode::Absolute) as u16; - let is_negative = (label>> 15) == 1; - let (bytes, cycles) = cycles::increment_cycles_per(); - self.registers.increment_pc(bytes); self.cycles += cycles; - let address = match is_negative { - true => self.registers.pc.wrapping_sub(!label + 1), - false=> self.registers.pc.wrapping_add(label), - }; - self.do_push(bus, &[(address >> 8) as u8, address as u8]); - } - - fn pha(&mut self, bus: &mut Bus) { - let value = self.registers.a; - if self.registers.is_16bit_mode() { - self.do_push(bus, &[(value >> 8) as u8, value as u8]); - } else { - self.do_push(bus, &[value as u8]); - } - let (bytes, cycles) = cycles::increment_cycles_pha(self.registers.is_16bit_mode()); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn phb(&mut self, bus: &mut Bus) { - self.do_push(bus, &[self.registers.dbr]); - let (bytes, cycles) = cycles::increment_cycles_phb(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn phd(&mut self, bus: &mut Bus) { - let value = self.registers.d; - self.do_push(bus, &[(value >> 8) as u8, value as u8]); - let (bytes, cycles) = cycles::increment_cycles_phd(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - pub fn phk(&mut self, bus: &mut Bus) { - self.do_push(bus, &[self.registers.pbr]); - let (bytes, cycles) = cycles::increment_cycles_phk(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - pub fn php(&mut self, bus: &mut Bus) { - self.do_push(bus, &[self.registers.p]); - let (bytes, cycles) = cycles::increment_cycles_php(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn phx(&mut self, bus: &mut Bus) { - let value = self.registers.x; - if self.registers.is_16bit_index() { - self.do_push(bus, &[(value >> 8) as u8, value as u8]); - } else { - self.do_push(bus, &[value as u8]); - } - let (bytes, cycles) = cycles::increment_cycles_push_index(self.registers.is_16bit_index()); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn phy(&mut self, bus: &mut Bus) { - let value = self.registers.y; - if self.registers.is_16bit_index() { - self.do_push(bus, &[(value >> 8) as u8, value as u8]); - } else { - self.do_push(bus, &[value as u8]); - } - let (bytes, cycles) = cycles::increment_cycles_push_index(self.registers.is_16bit_index()); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn jsr(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - let effective_address = self.get_effective_address(bus, addressing_mode); - let is_long = match addressing_mode { - AddressingMode::AbsoluteLong | - AddressingMode::AbsoluteIndirectLong => true, - _ => false, - }; - // We need to push the *next* instruction onto the stack - let (bytes, cycles) = cycles::increment_cycles_jsr(addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - let value = self.registers.get_pc_address(); - if is_long { - self.do_push(bus, &[ - (value >> 16) as u8, - (value >> 8) as u8, - value as u8, - ]); - } else { - self.do_push(bus, &[ - (value >> 8) as u8, - value as u8, - ]); - } - self.registers.pc = effective_address as u16; - if is_long { - self.registers.pbr = (effective_address >> 16) as u8; - } - } - - fn lda(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - if self.registers.is_16bit_mode() { - let value = self.get_16bit_from_address(bus, addressing_mode); - self.registers.a = value; - self.registers.set_flags(&[ - Flags::Negative(value >> 15 == 1), - Flags::Zero(value == 0), - ]); - } else { - let value = self.get_8bit_from_address(bus, addressing_mode); - self.registers.set_flags(&[ - Flags::Negative(value >> 7 == 1), - Flags::Zero(value == 0), - ]); - self.registers.set_low_a(value); - self.do_bit(self.registers.a as u8, value, addressing_mode); - } - let (bytes, cycles) = cycles::increment_cycles_lda(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn do_ld_index(&mut self, bus: &mut Bus, index: IndexRegister, addressing_mode: AddressingMode) { - if self.registers.is_16bit_index() { - let value = self.get_16bit_from_address(bus, addressing_mode); - match index { - IndexRegister::X => self.registers.x = value, - IndexRegister::Y => self.registers.y = value, - } - self.registers.set_flags(&[ - Flags::Negative(value >> 15 == 1), - Flags::Zero(value == 0), - ]); - } else { - let value = self.get_8bit_from_address(bus, addressing_mode); - match index { - IndexRegister::X => self.registers.set_low_x(value), - IndexRegister::Y => self.registers.set_low_y(value), - } - self.registers.set_flags(&[ - Flags::Negative(value >> 7 == 1), - Flags::Zero(value == 0), - ]); - } - let (bytes, cycles) = cycles::increment_cycles_ld_index(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn ldx(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - self.do_ld_index(bus, IndexRegister::X, addressing_mode); - } - - fn ldy(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - self.do_ld_index(bus, IndexRegister::Y, addressing_mode); - } - - fn do_pull(&mut self, bus: &mut Bus, count: usize) -> Vec { - let mut bytes = vec![]; - let mut is_zero = true; - for _ in 0..count { - self.registers.increment_sp(1); - let byte = bus.read(self.registers.sp as u32); - if byte != 0 { - is_zero = false; - } - bytes.push(byte); - } - self.registers.set_zero_flag(is_zero); - if bytes.len() > 0 { - // Low byte is pulled first, so we need to check - // for the last byte that we pull - self.registers.set_negative_flag((bytes[bytes.len() - 1] >> 7) == 1); - } - bytes - } - - fn pla(&mut self, bus: &mut Bus) { - if self.registers.is_16bit_mode() { - let bytes = self.do_pull(bus, 2); - self.registers.a = (bytes[0] as u16) | ((bytes[1] as u16) << 8); - } else { - let bytes = self.do_pull(bus, 1); - self.registers.set_low_a(bytes[0]); - } - let (bytes, cycles) = cycles::increment_cycles_pla(self.registers.is_16bit_mode()); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn plb(&mut self, bus: &mut Bus) { - self.registers.dbr = self.do_pull(bus, 1)[0]; - let (bytes, cycles) = cycles::increment_cycles_plb(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn pld(&mut self, bus: &mut Bus) { - let bytes = self.do_pull(bus, 2); - self.registers.d = (bytes[0] as u16) | ((bytes[1] as u16) << 8); - let (bytes, cycles) = cycles::increment_cycles_pld(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn plp(&mut self, bus: &mut Bus) { - let bytes = self.do_pull(bus, 1); - self.registers.p = bytes[0]; - let (bytes, cycles) = cycles::increment_cycles_plp(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn plx(&mut self, bus: &mut Bus) { - if self.registers.is_16bit_index() { - let bytes = self.do_pull(bus, 2); - self.registers.x = (bytes[0] as u16) | ((bytes[1] as u16) << 8); - } else { - let bytes = self.do_pull(bus, 1); - self.registers.set_low_x(bytes[0]); - } - let (bytes, cycles) = cycles::increment_cycles_pl_index(self.registers.is_16bit_index()); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn ply(&mut self, bus: &mut Bus) { - if self.registers.is_16bit_index() { - let bytes = self.do_pull(bus, 2); - self.registers.y = (bytes[0] as u16) | ((bytes[1] as u16) << 8); - } else { - let bytes = self.do_pull(bus, 1); - self.registers.set_low_y(bytes[0]); - } - let (bytes, cycles) = cycles::increment_cycles_pl_index(self.registers.is_16bit_index()); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn rep(&mut self, bus: &mut Bus) { - let byte = self.get_8bit_from_address(bus, AddressingMode::Immediate); - self.registers.reset_rep_byte(byte); - let (bytes, cycles) = cycles::increment_cycles_rep(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn rol(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - let target = match addressing_mode { - AddressingMode::Accumulator => self.registers.a, - _ => match self.registers.is_16bit_mode() { - true => self.get_16bit_from_address(bus, addressing_mode), - false => self.get_8bit_from_address(bus, addressing_mode) as u16, - } - }; - if self.registers.is_16bit_mode() { - let (result, affected_flags) = alu::rol(target, self.registers.get_carry_flag()); - self.set_16bit_to_address(bus, addressing_mode, result); - self.registers.set_flags(&affected_flags); - } else { - let (result, affected_flags) = alu::rol(target as u8, self.registers.get_carry_flag()); - self.set_8bit_to_address(bus, addressing_mode, result); - self.registers.set_flags(&affected_flags); - } - let (bytes, cycles) = cycles::increment_cycles_shift(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn ror(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - let target = match addressing_mode { - AddressingMode::Accumulator => self.registers.a, - _ => match self.registers.is_16bit_mode() { - true => self.get_16bit_from_address(bus, addressing_mode), - false => self.get_8bit_from_address(bus, addressing_mode) as u16, - } - }; - if self.registers.is_16bit_mode() { - let (result, affected_flags) = alu::ror(target, self.registers.get_carry_flag()); - self.set_16bit_to_address(bus, addressing_mode, result); - self.registers.set_flags(&affected_flags); - } else { - let (result, affected_flags) = alu::ror(target as u8, self.registers.get_carry_flag()); - self.set_8bit_to_address(bus, addressing_mode, result); - self.registers.set_flags(&affected_flags); - } - let (bytes, cycles) = cycles::increment_cycles_shift(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn rtl(&mut self, bus: &mut Bus) { - let bytes = self.do_pull(bus, 3); - // Low byte of PC is pulled first, then high byte and then PBR - self.registers.pc = (bytes[0] as u16) | ((bytes[1] as u16) << 8); - self.registers.pbr = bytes[2]; - let (bytes, cycles) = cycles::increment_cycles_return_subroutine(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn rts(&mut self, bus: &mut Bus) { - let bytes = self.do_pull(bus, 2); - // Low byte of PC is pulled first, then high byte - self.registers.pc = (bytes[0] as u16) | ((bytes[1] as u16) << 8); - let (bytes, cycles) = cycles::increment_cycles_return_subroutine(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn sec(&mut self) { - self.registers.set_carry_flag(true); - let (bytes, cycles) = cycles::increment_cycles_set_flag(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn sed(&mut self) { - self.registers.set_decimal_mode_flag(true); - let (bytes, cycles) = cycles::increment_cycles_set_flag(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn sei(&mut self) { - self.registers.set_irq_disable_flag(true); - let (bytes, cycles) = cycles::increment_cycles_set_flag(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn sep(&mut self, bus: &mut Bus) { - let byte = self.get_8bit_from_address(bus, AddressingMode::Immediate); - self.registers.set_sep_byte(byte); - let (bytes, cycles) = cycles::increment_cycles_sep(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn sta(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - if self.registers.is_16bit_mode() { - self.set_16bit_to_address(bus, addressing_mode, self.registers.a); - } else { - self.set_8bit_to_address(bus, addressing_mode, self.registers.a as u8); - } - let (bytes, cycles) = cycles::increment_cycles_sta(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn stx(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - if self.registers.is_16bit_index() { - self.set_16bit_to_address(bus, addressing_mode, self.registers.x); - } else { - self.set_8bit_to_address(bus, addressing_mode, self.registers.x as u8); - } - let (bytes, cycles) = cycles::increment_cycles_st_index(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn sty(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - if self.registers.is_16bit_index() { - self.set_16bit_to_address(bus, addressing_mode, self.registers.y); - } else { - self.set_8bit_to_address(bus, addressing_mode, self.registers.y as u8); - } - let (bytes, cycles) = cycles::increment_cycles_st_index(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn stz(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { - if self.registers.is_16bit_mode() { - self.set_16bit_to_address(bus, addressing_mode, 0); - } else { - self.set_8bit_to_address(bus, addressing_mode, 0); - } - let (bytes, cycles) = cycles::increment_cycles_st_index(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn stp(&mut self) { - self.is_stopped = true; - let (bytes, cycles) = cycles::increment_cycles_stp(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn tax(&mut self) { - if self.registers.is_16bit_index() { - self.registers.x = self.registers.a; - self.registers.set_negative_flag((self.registers.x >> 15) == 1); - self.registers.set_zero_flag(self.registers.x == 0); - } else { - let result = self.registers.a as u8; - self.registers.set_low_x(result); - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); - } - let (bytes, cycles) = cycles::increment_cycles_transfer(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn tay(&mut self) { - if self.registers.is_16bit_index() { - self.registers.y = self.registers.a; - self.registers.set_negative_flag((self.registers.y >> 15) == 1); - self.registers.set_zero_flag(self.registers.y == 0); - } else { - let result = self.registers.a as u8; - self.registers.set_low_y(result); - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); - } - let (bytes, cycles) = cycles::increment_cycles_transfer(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn tcd(&mut self) { - let result = self.registers.a; - self.registers.d = result; - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); - let (bytes, cycles) = cycles::increment_cycles_transfer(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn tcs(&mut self) { - let result = self.registers.a; - self.registers.sp = result; - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); - let (bytes, cycles) = cycles::increment_cycles_transfer(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn tdc(&mut self) { - let result = self.registers.d; - self.registers.a = result; - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); - let (bytes, cycles) = cycles::increment_cycles_transfer(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn tsc(&mut self) { - let result = self.registers.sp; - self.registers.a = result; - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); - let (bytes, cycles) = cycles::increment_cycles_transfer(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn tsx(&mut self) { - if self.registers.is_16bit_index() { - let result = self.registers.sp; - self.registers.x = result; - self.registers.set_negative_flag((result >> 15) == 1); - self.registers.set_zero_flag(result == 0); - } else { - let result = self.registers.sp as u8; - self.registers.set_low_x(result); - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); - } - let (bytes, cycles) = cycles::increment_cycles_transfer(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn txa(&mut self) { - if self.registers.is_16bit_mode() { - self.registers.a = self.registers.x; - self.registers.set_negative_flag((self.registers.a >> 15) == 1); - self.registers.set_zero_flag(self.registers.a == 0); - } else { - let result = self.registers.x as u8; - self.registers.set_low_a(result); - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); - } - let (bytes, cycles) = cycles::increment_cycles_transfer(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn txs(&mut self) { - if self.registers.is_16bit_index() { - self.registers.sp = self.registers.x; - self.registers.set_negative_flag((self.registers.a >> 15) == 1); - self.registers.set_zero_flag(self.registers.a == 0); - } else { - let result = self.registers.x as u8; - self.registers.set_low_sp(result); - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); - } - let (bytes, cycles) = cycles::increment_cycles_transfer(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn txy(&mut self) { - if self.registers.is_16bit_index() { - self.registers.y = self.registers.x; - self.registers.set_negative_flag((self.registers.x >> 15) == 1); - self.registers.set_zero_flag(self.registers.x == 0); - } else { - let result = self.registers.x as u8; - self.registers.set_low_y(result); - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); - } - let (bytes, cycles) = cycles::increment_cycles_transfer(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn tya(&mut self) { - if self.registers.is_16bit_mode() { - self.registers.a = self.registers.y; - self.registers.set_negative_flag((self.registers.a >> 15) == 1); - self.registers.set_zero_flag(self.registers.a == 0); - } else { - let result = self.registers.y as u8; - self.registers.set_low_a(result); - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); - } - let (bytes, cycles) = cycles::increment_cycles_transfer(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn tyx(&mut self) { - if self.registers.is_16bit_index() { - self.registers.x = self.registers.y; - self.registers.set_negative_flag((self.registers.y >> 15) == 1); - self.registers.set_zero_flag(self.registers.y == 0); - } else { - let result = self.registers.y as u8; - self.registers.set_low_x(result); - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); - } - let (bytes, cycles) = cycles::increment_cycles_transfer(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn xba(&mut self) { - self.registers.a = (self.registers.a << 8) | (self.registers.a >> 8); - self.registers.set_negative_flag(((self.registers.a as u8) >> 7) == 1); - self.registers.set_zero_flag((self.registers.a as u8) == 0); - let (bytes, cycles) = cycles::increment_cycles_xba(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn do_move(&mut self, bus: &mut Bus, is_next: bool) { - let pc = self.registers.get_pc_address(); - let source_bank = bus.read(pc + 2); - let dest_bank = bus.read(pc + 1); - let mut count = 0; - while self.registers.a != 0xFFFF { - let (x, y) = match self.registers.is_16bit_index() { - true => (self.registers.x, self.registers.y), - false => (self.registers.x & 0x00FF, self.registers.y & 0x00FF), - }; - let source_address = ((source_bank as u32) << 16) | (x as u32); - let dest_address = ((dest_bank as u32) << 16) | (y as u32); - let byte = bus.read(source_address); - bus.write(dest_address, byte); - self.registers.a = self.registers.a.wrapping_sub(1); - if is_next { - self.registers.x = self.registers.x.wrapping_add(1); - self.registers.y = self.registers.y.wrapping_add(1); - } else { - self.registers.x = self.registers.x.wrapping_sub(1); - self.registers.y = self.registers.y.wrapping_sub(1); - } - count += 1; - } - let (bytes, cycles) = cycles::increment_cycles_move(count); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn mvn(&mut self, bus: &mut Bus) { - self.do_move(bus, true); - } - - fn mvp(&mut self, bus: &mut Bus) { - self.do_move(bus, true); - } - - fn rti(&mut self, bus: &mut Bus) { - self.registers.p = self.do_pull(bus, 1)[0]; - let pc_bytes = self.do_pull(bus, 2); - self.registers.pc = (pc_bytes[0] as u16) | ((pc_bytes[1] as u16) << 8); - if !self.registers.emulation_mode { - self.registers.pbr = self.do_pull(bus, 1)[0]; - } - let (bytes, cycles) = cycles::increment_cycles_return_interrupt(self.registers.emulation_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn trb(&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.registers.a & value; - self.set_16bit_to_address(bus, addressing_mode, result); - self.registers.set_zero_flag(result == 0); - } else { - let value = self.get_8bit_from_address(bus, addressing_mode); - let result = (self.registers.a as u8) & value; - self.set_8bit_to_address(bus, addressing_mode, result); - self.registers.set_zero_flag(result == 0); - } - let (bytes, cycles) = cycles::increment_cycles_test(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn tsb(&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.registers.a | value; - self.set_16bit_to_address(bus, addressing_mode, result); - self.registers.set_zero_flag(result == 0); - } else { - let value = self.get_8bit_from_address(bus, addressing_mode); - let result = (self.registers.a as u8) | value; - self.set_8bit_to_address(bus, addressing_mode, result); - self.registers.set_zero_flag(result == 0); - } - let (bytes, cycles) = cycles::increment_cycles_test(&self.registers, addressing_mode); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn wai(&mut self) { - self.is_waiting_interrupt = true; - let (bytes, cycles) = cycles::increment_cycles_stp(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - - fn wdm(&mut self) { - let (bytes, cycles) = cycles::increment_cycles_wdm(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - - fn xce(&mut self) { - self.registers.exchange_carry_and_emulation(); - let (bytes, cycles) = cycles::increment_cycles_exchange(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - - fn check_running_state(&mut self, bus: &mut Bus) -> bool { - // Each byte in a DMA transfer takes 8 master cycles. - // And each CPU can take either 6, 8 or 12 master cycles depending - // on what's being read from memory. So this won't be accurate. - if bus.dma.is_active() { - let pending_bus_writes = bus.dma.tick(); - for (src, dst) in pending_bus_writes { - let byte = bus.read(src); - bus.write(dst, byte); - let (bytes, cycles) = cycles::increment_cycles_while_stopped(); - self.registers.increment_pc(bytes); self.cycles += cycles; - } - if !bus.dma.is_active() { - bus.write(dma::MDMAEN as u32, 0x00) - } - return false; - } - if self.is_stopped { - let (bytes, cycles) = cycles::increment_cycles_while_stopped(); - self.registers.increment_pc(bytes); self.cycles += cycles; - return false; - } - if self.is_waiting_interrupt { - // TODO: check for interrupts here - let (bytes, cycles) = cycles::increment_cycles_while_stopped(); - self.registers.increment_pc(bytes); self.cycles += cycles; - return false; - } - return true; - } - - pub fn tick(&mut self, bus: &mut Bus) { - if !self.check_running_state(bus) { - return; - } - let opcode = bus.read(self.registers.get_pc_address()); - self.execute_opcode(opcode, bus); - } - - pub fn execute_opcode(&mut self, opcode: u8, bus: &mut Bus) { - type A = AddressingMode; - type I = IndexRegister; - match opcode { - // ADC - 0x69 => self.adc(bus, A::Immediate), - 0x6D => self.adc(bus, A::Absolute), - 0x6F => self.adc(bus, A::AbsoluteLong), - 0x65 => self.adc(bus, A::DirectPage), - 0x72 => self.adc(bus, A::DirectPageIndirect), - 0x67 => self.adc(bus, A::DirectPageIndirectLong), - 0x7D => self.adc(bus, A::AbsoluteIndexed(I::X)), - 0x7F => self.adc(bus, A::AbsoluteLongIndexed(I::X)), - 0x79 => self.adc(bus, A::AbsoluteIndexed(I::Y)), - 0x75 => self.adc(bus, A::DirectPageIndexed(I::X)), - 0x61 => self.adc(bus, A::DirectPageIndexedIndirect(I::X)), - 0x71 => self.adc(bus, A::DirectPageIndirectIndexed(I::Y)), - 0x77 => self.adc(bus, A::DirectPageIndirectLongIndexed(I::Y)), - 0x63 => self.adc(bus, A::StackRelative), - 0x73 => self.adc(bus, A::StackRelativeIndirectIndexed(I::Y)), - // AND - 0x29 => self.and(bus, A::Immediate), - 0x2D => self.and(bus, A::Absolute), - 0x2F => self.and(bus, A::AbsoluteLong), - 0x25 => self.and(bus, A::DirectPage), - 0x32 => self.and(bus, A::DirectPageIndirect), - 0x27 => self.and(bus, A::DirectPageIndirectLong), - 0x3D => self.and(bus, A::AbsoluteIndexed(I::X)), - 0x3F => self.and(bus, A::AbsoluteLongIndexed(I::X)), - 0x39 => self.and(bus, A::AbsoluteIndexed(I::Y)), - 0x35 => self.and(bus, A::DirectPageIndexed(I::X)), - 0x21 => self.and(bus, A::DirectPageIndexedIndirect(I::X)), - 0x31 => self.and(bus, A::DirectPageIndirectIndexed(I::Y)), - 0x37 => self.and(bus, A::DirectPageIndirectLongIndexed(I::Y)), - 0x23 => self.and(bus, A::StackRelative), - 0x33 => self.and(bus, A::StackRelativeIndirectIndexed(I::Y)), - // ASL - 0x0A => self.asl(bus, A::Accumulator), - 0x0E => self.asl(bus, A::Absolute), - 0x06 => self.asl(bus, A::DirectPage), - 0x1E => self.asl(bus, A::AbsoluteIndexed(I::X)), - 0x16 => self.asl(bus, A::DirectPageIndexed(I::X)), - // BCC - 0x90 => self.bcc(bus), - // BCS - 0xB0 => self.bcs(bus), - // BEQ - 0xF0 => self.beq(bus), - // BNE - 0xD0 => self.bne(bus), - // BMI - 0x30 => self.bmi(bus), - // BPL - 0x10 => self.bpl(bus), - // BRA - 0x80 => self.bra(bus), - // BRK - 0x00 => self.brk(bus), - // BRL - 0x82 => self.brl(bus), - // BVC - 0x50 => self.bvc(bus), - // BVS - 0x70 => self.bvs(bus), - // BIT - 0x89 => self.bit(bus, A::Immediate), - 0x2C => self.bit(bus, A::Absolute), - 0x24 => self.bit(bus, A::DirectPage), - 0x3C => self.bit(bus, A::AbsoluteIndexed(I::X)), - 0x34 => self.bit(bus, A::DirectPageIndexed(I::X)), - // CLC - 0x18 => self.clc(), - // CLD - 0xD8 => self.cld(), - // CLI - 0x58 => self.cli(), - // CLV - 0xB8 => self.clv(), - // CMP - 0xC9 => self.cmp(bus, A::Immediate), - 0xCD => self.cmp(bus, A::Absolute), - 0xCF => self.cmp(bus, A::AbsoluteLong), - 0xC5 => self.cmp(bus, A::DirectPage), - 0xD2 => self.cmp(bus, A::DirectPageIndirect), - 0xC7 => self.cmp(bus, A::DirectPageIndirectLong), - 0xDD => self.cmp(bus, A::AbsoluteIndexed(I::X)), - 0xDF => self.cmp(bus, A::AbsoluteLongIndexed(I::X)), - 0xD9 => self.cmp(bus, A::AbsoluteIndexed(I::Y)), - 0xD5 => self.cmp(bus, A::DirectPageIndexed(I::X)), - 0xC1 => self.cmp(bus, A::DirectPageIndexedIndirect(I::X)), - 0xD1 => self.cmp(bus, A::DirectPageIndirectIndexed(I::Y)), - 0xD7 => self.cmp(bus, A::DirectPageIndirectLongIndexed(I::Y)), - 0xC3 => self.cmp(bus, A::StackRelative), - 0xD3 => self.cmp(bus, A::StackRelativeIndirectIndexed(I::Y)), - // COP - 0x02 => self.cop(bus), - // CPX - 0xE0 => self.cpx(bus, A::Immediate), - 0xEC => self.cpx(bus, A::Absolute), - 0xE4 => self.cpx(bus, A::DirectPage), - // CPY - 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(), - // EOR - 0x49 => self.eor(bus, A::Immediate), - 0x4D => self.eor(bus, A::Absolute), - 0x4F => self.eor(bus, A::AbsoluteLong), - 0x45 => self.eor(bus, A::DirectPage), - 0x52 => self.eor(bus, A::DirectPageIndirect), - 0x47 => self.eor(bus, A::DirectPageIndirectLong), - 0x5D => self.eor(bus, A::AbsoluteIndexed(I::X)), - 0x5F => self.eor(bus, A::AbsoluteLongIndexed(I::X)), - 0x59 => self.eor(bus, A::AbsoluteIndexed(I::Y)), - 0x55 => self.eor(bus, A::DirectPageIndexed(I::X)), - 0x41 => self.eor(bus, A::DirectPageIndexedIndirect(I::X)), - 0x51 => self.eor(bus, A::DirectPageIndirectIndexed(I::Y)), - 0x57 => self.eor(bus, A::DirectPageIndirectLongIndexed(I::Y)), - 0x43 => self.eor(bus, A::StackRelative), - 0x53 => self.eor(bus, A::StackRelativeIndirectIndexed(I::Y)), - // INC - 0x1A => self.inc(bus, A::Accumulator), - 0xEE => self.inc(bus, A::Absolute), - 0xE6 => self.inc(bus, A::DirectPage), - 0xFE => self.inc(bus, A::AbsoluteIndexed(I::X)), - 0xF6 => self.inc(bus, A::DirectPageIndexed(I::X)), - // INX - 0xE8 => self.inx(), - // INY - 0xC8 => self.iny(), - // JMP - 0x4C => self.jmp(bus, A::Absolute), - 0x6C => self.jmp(bus, A::AbsoluteIndirect), - 0x7C => self.jmp(bus, A::AbsoluteIndexedIndirect(I::X)), - 0x5C => self.jmp(bus, A::AbsoluteLong), - 0xDC => self.jmp(bus, A::AbsoluteIndirectLong), - // JSR - 0x20 => self.jsr(bus, A::Absolute), - 0xFC => self.jsr(bus, A::AbsoluteIndexedIndirect(I::X)), - 0x22 => self.jsr(bus, A::AbsoluteLong), // same as JSL - // LDA - 0xA9 => self.lda(bus, A::Immediate), - 0xAD => self.lda(bus, A::Absolute), - 0xAF => self.lda(bus, A::AbsoluteLong), - 0xA5 => self.lda(bus, A::DirectPage), - 0xB2 => self.lda(bus, A::DirectPageIndirect), - 0xA7 => self.lda(bus, A::DirectPageIndirectLong), - 0xBD => self.lda(bus, A::AbsoluteIndexed(I::X)), - 0xBF => self.lda(bus, A::AbsoluteLongIndexed(I::X)), - 0xB9 => self.lda(bus, A::AbsoluteIndexed(I::Y)), - 0xB5 => self.lda(bus, A::DirectPageIndexed(I::X)), - 0xA1 => self.lda(bus, A::DirectPageIndexedIndirect(I::X)), - 0xB1 => self.lda(bus, A::DirectPageIndirectIndexed(I::Y)), - 0xB7 => self.lda(bus, A::DirectPageIndirectLongIndexed(I::Y)), - 0xA3 => self.lda(bus, A::StackRelative), - 0xB3 => self.lda(bus, A::StackRelativeIndirectIndexed(I::Y)), - // LDX - 0xA2 => self.ldx(bus, A::Immediate), - 0xAE => self.ldx(bus, A::Absolute), - 0xA6 => self.ldx(bus, A::DirectPage), - 0xBE => self.ldx(bus, A::AbsoluteIndexed(I::Y)), - 0xB6 => self.ldx(bus, A::DirectPageIndexed(I::Y)), - // LDY - 0xA0 => self.ldy(bus, A::Immediate), - 0xAC => self.ldy(bus, A::Absolute), - 0xA4 => self.ldy(bus, A::DirectPage), - 0xB4 => self.ldy(bus, A::AbsoluteIndexed(I::Y)), - 0xBC => self.ldy(bus, A::DirectPageIndexed(I::Y)), - // LSR - 0x4A => self.lsr(bus, A::Accumulator), - 0x4E => self.lsr(bus, A::Absolute), - 0x46 => self.lsr(bus, A::DirectPage), - 0x5E => self.lsr(bus, A::AbsoluteIndexed(I::X)), - 0x56 => self.lsr(bus, A::DirectPageIndexed(I::X)), - // MVN - 0x54 => self.mvn(bus), - // MVP - 0x44 => self.mvp(bus), - // NOP - 0xEA => self.nop(), - // ORA - 0x09 => self.ora(bus, A::Immediate), - 0x0D => self.ora(bus, A::Absolute), - 0x0F => self.ora(bus, A::AbsoluteLong), - 0x05 => self.ora(bus, A::DirectPage), - 0x12 => self.ora(bus, A::DirectPageIndirect), - 0x07 => self.ora(bus, A::DirectPageIndirectLong), - 0x1D => self.ora(bus, A::AbsoluteIndexed(I::X)), - 0x1F => self.ora(bus, A::AbsoluteLongIndexed(I::X)), - 0x19 => self.ora(bus, A::AbsoluteIndexed(I::Y)), - 0x15 => self.ora(bus, A::DirectPageIndexed(I::X)), - 0x01 => self.ora(bus, A::DirectPageIndexedIndirect(I::X)), - 0x11 => self.ora(bus, A::DirectPageIndirectIndexed(I::Y)), - 0x17 => self.ora(bus, A::DirectPageIndirectLongIndexed(I::Y)), - 0x03 => self.ora(bus, A::StackRelative), - 0x13 => self.ora(bus, A::StackRelativeIndirectIndexed(I::Y)), - // PEA - 0xF4 => self.pea(bus), - // PEI - 0xD4 => self.pei(bus), - // PER - 0x62 => self.per(bus), - // PHA - 0x48 => self.pha(bus), - // PHB - 0x8B => self.phb(bus), - // PHD - 0x0B => self.phd(bus), - // PHK - 0x4B => self.phk(bus), - // PHP - 0x08 => self.php(bus), - // PHX - 0xDA => self.phx(bus), - // PHY - 0x5A => self.phy(bus), - // PLA - 0x68 => self.pla(bus), - // PLB - 0xAB => self.plb(bus), - // PLD - 0x2B => self.pld(bus), - // PLP - 0x28 => self.plp(bus), - // PLX - 0xFA => self.plx(bus), - // PLY - 0x7A => self.ply(bus), - // REP - 0xC2 => self.rep(bus), - // ROL - 0x2A => self.rol(bus, AddressingMode::Accumulator), - 0x2E => self.rol(bus, AddressingMode::Absolute), - 0x26 => self.rol(bus, AddressingMode::DirectPage), - 0x3E => self.rol(bus, AddressingMode::AbsoluteIndexed(I::X)), - 0x36 => self.rol(bus, AddressingMode::DirectPageIndexed(I::X)), - // ROR - 0x6A => self.ror(bus, AddressingMode::Accumulator), - 0x6E => self.ror(bus, AddressingMode::Absolute), - 0x66 => self.ror(bus, AddressingMode::DirectPage), - 0x7E => self.ror(bus, AddressingMode::AbsoluteIndexed(I::X)), - 0x76 => self.ror(bus, AddressingMode::DirectPageIndexed(I::X)), - // RTI - 0x40 => self.rti(bus), - // RTL - 0x6B => self.rtl(bus), - // RTS - 0x60 => self.rts(bus), - // SBC - 0xE9 => self.sbc(bus, A::Immediate), - 0xED => self.sbc(bus, A::Absolute), - 0xEF => self.sbc(bus, A::AbsoluteLong), - 0xE5 => self.sbc(bus, A::DirectPage), - 0xF2 => self.sbc(bus, A::DirectPageIndirect), - 0xE7 => self.sbc(bus, A::DirectPageIndirectLong), - 0xFD => self.sbc(bus, A::AbsoluteIndexed(I::X)), - 0xFF => self.sbc(bus, A::AbsoluteLongIndexed(I::X)), - 0xF9 => self.sbc(bus, A::AbsoluteIndexed(I::Y)), - 0xF5 => self.sbc(bus, A::DirectPageIndexed(I::X)), - 0xE1 => self.sbc(bus, A::DirectPageIndexedIndirect(I::X)), - 0xF1 => self.sbc(bus, A::DirectPageIndirectIndexed(I::Y)), - 0xF7 => self.sbc(bus, A::DirectPageIndirectLongIndexed(I::Y)), - 0xE3 => self.sbc(bus, A::StackRelative), - 0xF3 => self.sbc(bus, A::StackRelativeIndirectIndexed(I::Y)), - // SEC - 0x38 => self.sec(), - // SED - 0xF8 => self.sed(), - // SEI - 0x78 => self.sei(), - // SEP - 0xE2 => self.sep(bus), - // STA - 0x8D => self.sta(bus, A::Absolute), - 0x8F => self.sta(bus, A::AbsoluteLong), - 0x85 => self.sta(bus, A::DirectPage), - 0x92 => self.sta(bus, A::DirectPageIndirect), - 0x87 => self.sta(bus, A::DirectPageIndirectLong), - 0x9D => self.sta(bus, A::AbsoluteIndexed(I::X)), - 0x9F => self.sta(bus, A::AbsoluteLongIndexed(I::X)), - 0x99 => self.sta(bus, A::AbsoluteIndexed(I::Y)), - 0x95 => self.sta(bus, A::DirectPageIndexed(I::X)), - 0x81 => self.sta(bus, A::DirectPageIndexedIndirect(I::X)), - 0x91 => self.sta(bus, A::DirectPageIndirectIndexed(I::Y)), - 0x97 => self.sta(bus, A::DirectPageIndirectLongIndexed(I::Y)), - 0x83 => self.sta(bus, A::StackRelative), - 0x93 => self.sta(bus, A::StackRelativeIndirectIndexed(I::Y)), - // STP - 0xDB => self.stp(), - // STX - 0x8E => self.stx(bus, A::Absolute), - 0x86 => self.stx(bus, A::DirectPage), - 0x96 => self.stx(bus, A::DirectPageIndexed(I::Y)), - // STY - 0x8C => self.sty(bus, A::Absolute), - 0x84 => self.sty(bus, A::DirectPage), - 0x94 => self.sty(bus, A::DirectPageIndexed(I::X)), - // STZ - 0x9C => self.stz(bus, A::Absolute), - 0x64 => self.stz(bus, A::DirectPage), - 0x9E => self.stz(bus, A::AbsoluteIndexed(I::X)), - 0x74 => self.stz(bus, A::DirectPageIndexed(I::X)), - // TAX - 0xAA => self.tax(), - // TAY - 0xA8 => self.tay(), - // TCD - 0x5B => self.tcd(), - // TCS - 0x1B => self.tcs(), - // TCD - 0x7B => self.tdc(), - // TRB - 0x1C => self.trb(bus, A::Absolute), - 0x14 => self.trb(bus, A::DirectPage), - // TSB - 0x0C => self.tsb(bus, A::Absolute), - 0x04 => self.tsb(bus, A::DirectPage), - // TSC - 0x3B => self.tsc(), - // TSX - 0xBA => self.tsx(), - // TXA - 0x8A => self.txa(), - // TXS - 0x9A => self.txs(), - // TXY - 0x9B => self.txy(), - // TYA - 0x98 => self.tya(), - // TYX - 0xBB => self.tyx(), - // WAI - 0xCB => self.wai(), - // WDM - 0x42 => self.wdm(), - // XBA - 0xEB => self.xba(), - // XCE - 0xFB => self.xce(), - } - } -} - -#[cfg(test)] -mod cpu_instructions_tests { - use super::*; - - #[test] - fn test_adc() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.a = 0x0000; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.set_memory_select_flag(true); - bus.write(0x000001, 0x40); - cpu.adc(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.a, 0x40); - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - assert!(!cpu.registers.get_carry_flag()); - } - - #[test] - fn test_eor() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.a = 0x0F; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.set_16bit_mode(false); - bus.write(0x000001, 0xF0); - cpu.eor(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.a, 0xFF); - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - assert!(cpu.registers.get_negative_flag()); - assert!(!cpu.registers.get_zero_flag()); - } - - #[test] - fn test_sbc() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.a = 0x0001; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.set_memory_select_flag(true); - bus.write(0x000001, 1); - cpu.sbc(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.a, 0); - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - assert!(!cpu.registers.get_carry_flag()); - assert!(cpu.registers.get_zero_flag()); - } - - #[test] - fn test_and() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.a = 0x0101; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.set_memory_select_flag(true); - bus.write(0x000001, 0x01); - bus.write(0x000002, 0x01); - cpu.and(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.a, 0x0101); - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - assert!(!cpu.registers.get_carry_flag()); - assert!(!cpu.registers.get_zero_flag()); - } - - #[test] - fn test_ora() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.a = 0x0F; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.set_memory_select_flag(true); - bus.write(0x000001, 0xF0); - cpu.ora(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.a, 0xFF); - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - assert!(!cpu.registers.get_zero_flag()); - assert!(cpu.registers.get_negative_flag()); - } - - #[test] - fn test_asl() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.a = 0b01010000_00000000; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.set_memory_select_flag(false); - cpu.asl(&mut bus, AddressingMode::Accumulator); - assert_eq!(cpu.registers.a, 0b10100000_00000000); - assert_eq!(cpu.registers.pc, 0x01); - assert_eq!(cpu.cycles, 4); - assert!(!cpu.registers.get_carry_flag()); - assert!(!cpu.registers.get_zero_flag()); - assert!(cpu.registers.get_negative_flag()); - } - - #[test] - fn test_lsr() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.a = 0b00000000_00000011; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.set_memory_select_flag(false); - cpu.registers.set_negative_flag(true); - cpu.registers.set_carry_flag(false); - cpu.lsr(&mut bus, AddressingMode::Accumulator); - assert_eq!(cpu.registers.a, 0b00000000_00000001); - assert_eq!(cpu.registers.pc, 0x01); - assert_eq!(cpu.cycles, 4); - assert!(cpu.registers.get_carry_flag()); - assert!(!cpu.registers.get_zero_flag()); - assert!(!cpu.registers.get_negative_flag()); - } - - #[test] - fn test_bit() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.a = 0b1111_0000; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.p = 0x00; - bus.write(0x000001, 0b0000_1111); - cpu.registers.set_16bit_mode(false); - cpu.bit(&mut bus, AddressingMode::Immediate); - // Check that it only affects the zero flag on immediate mode - assert_eq!(cpu.registers.a, 0b1111_0000); // Check that A is not altered - assert_eq!(cpu.registers.p, 0b0010_0010); // Only zero flag was altered (bit 6 is memory select mode) - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - assert!(cpu.registers.get_zero_flag()); - - cpu.registers.a = 0b00110000_00000000; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.p = 0x00; - cpu.cycles = 0; - // Write absolute address - bus.write(0x000001, 0x04); - bus.write(0x000002, 0x00); - // Write effective value of address - bus.write(0x000004, 0x00); - bus.write(0x000005, 0b1100_0000); - cpu.registers.set_16bit_mode(true); - cpu.bit(&mut bus, AddressingMode::Absolute); - // Check that it only affects the zero flag on immediate mode - assert_eq!(cpu.registers.a, 0b00110000_00000000); // Check that A is not altered - assert_eq!(cpu.registers.p, 0b1100_0010); - assert_eq!(cpu.registers.pc, 0x03); - assert_eq!(cpu.cycles, 5); - assert!(cpu.registers.get_zero_flag()); - assert!(cpu.registers.get_negative_flag()); - assert!(cpu.registers.get_overflow_flag()); - } - - #[test] - fn test_clc() { - let mut cpu = CPU::new(); - cpu.registers.set_carry_flag(true); - cpu.registers.pc = 0x0000; - cpu.clc(); - assert!(!cpu.registers.get_carry_flag()); - assert_eq!(cpu.registers.pc, 1); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_cld() { - let mut cpu = CPU::new(); - cpu.registers.set_decimal_mode_flag(true); - cpu.registers.pc = 0x0000; - cpu.cld(); - assert!(!cpu.registers.get_decimal_mode_flag()); - assert_eq!(cpu.registers.pc, 1); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_cli() { - let mut cpu = CPU::new(); - cpu.registers.set_irq_disable_flag(true); - cpu.registers.pc = 0x0000; - cpu.cli(); - assert!(!cpu.registers.get_irq_disable_flag()); - assert_eq!(cpu.registers.pc, 1); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_clv() { - let mut cpu = CPU::new(); - cpu.registers.set_overflow_flag(true); - cpu.registers.pc = 0x0000; - cpu.clv(); - assert!(!cpu.registers.get_overflow_flag()); - assert_eq!(cpu.registers.pc, 1); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_bcc() { - // test with positive nearlabel - // branch not taken - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_carry_flag(true); - bus.write(0x02, 0b00001111); - cpu.bcc(&mut bus); - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - // branch taken - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_carry_flag(false); - bus.write(0x01, 0b00001111); - cpu.bcc(&mut bus); - assert_eq!(cpu.registers.pc, 0x02 + 0b00001111); - assert_eq!(cpu.cycles, 3); - // test with negative nearlabel and boundary cross - cpu.registers.pc = 0x0100; - cpu.cycles = 0; - cpu.registers.set_carry_flag(false); - bus.write(0x101, 0xFB); // write -5 - cpu.bcc(&mut bus); - assert_eq!(cpu.registers.pc, 0xFD); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_bcs() { - // test with positive nearlabel - // branch not taken - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_carry_flag(false); - bus.write(0x02, 0b00001111); - cpu.bcs(&mut bus); - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - // branch taken - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_carry_flag(true); - bus.write(0x01, 0b00001111); - cpu.bcs(&mut bus); - assert_eq!(cpu.registers.pc, 0x02 + 0b00001111); - assert_eq!(cpu.cycles, 3); - // test with negative nearlabel and boundary cross - cpu.registers.pc = 0x0100; - cpu.cycles = 0; - cpu.registers.set_carry_flag(true); - bus.write(0x101, 0xFB); // write -5 - cpu.bcs(&mut bus); - assert_eq!(cpu.registers.pc, 0xFD); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_beq() { - // test with positive nearlabel - // branch not taken - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_zero_flag(false); - bus.write(0x02, 0b00001111); - cpu.beq(&mut bus); - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - // branch taken - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_zero_flag(true); - bus.write(0x01, 0b00001111); - cpu.beq(&mut bus); - assert_eq!(cpu.registers.pc, 0x02 + 0b00001111); - assert_eq!(cpu.cycles, 3); - // test with negative nearlabel and boundary cross - cpu.registers.pc = 0x0100; - cpu.cycles = 0; - cpu.registers.set_zero_flag(true); - bus.write(0x101, 0xFB); // write -5 - cpu.beq(&mut bus); - assert_eq!(cpu.registers.pc, 0xFD); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_bne() { - // test with positive nearlabel - // branch not taken - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_zero_flag(true); - bus.write(0x02, 0b00001111); - cpu.bne(&mut bus); - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - // branch taken - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_zero_flag(false); - bus.write(0x01, 0b00001111); - cpu.bne(&mut bus); - assert_eq!(cpu.registers.pc, 0x02 + 0b00001111); - assert_eq!(cpu.cycles, 3); - // test with negative nearlabel and boundary cross - cpu.registers.pc = 0x0100; - cpu.cycles = 0; - cpu.registers.set_zero_flag(false); - bus.write(0x101, 0xFB); // write -5 - cpu.bne(&mut bus); - assert_eq!(cpu.registers.pc, 0xFD); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_bmi() { - // test with positive nearlabel - // branch not taken - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_negative_flag(false); - bus.write(0x02, 0b00001111); - cpu.bmi(&mut bus); - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - // branch taken - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_negative_flag(true); - bus.write(0x01, 0b00001111); - cpu.bmi(&mut bus); - assert_eq!(cpu.registers.pc, 0x02 + 0b00001111); - assert_eq!(cpu.cycles, 3); - // test with negative nearlabel and boundary cross - cpu.registers.pc = 0x0100; - cpu.cycles = 0; - cpu.registers.set_negative_flag(true); - bus.write(0x101, 0xFB); // write -5 - cpu.bmi(&mut bus); - assert_eq!(cpu.registers.pc, 0xFD); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_bpl() { - // test with positive nearlabel - // branch not taken - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_negative_flag(true); - bus.write(0x02, 0b00001111); - cpu.bpl(&mut bus); - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - // branch taken - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_negative_flag(false); - bus.write(0x01, 0b00001111); - cpu.bpl(&mut bus); - assert_eq!(cpu.registers.pc, 0x02 + 0b00001111); - assert_eq!(cpu.cycles, 3); - // test with negative nearlabel and boundary cross - cpu.registers.pc = 0x0100; - cpu.cycles = 0; - cpu.registers.set_negative_flag(false); - bus.write(0x101, 0xFB); // write -5 - cpu.bpl(&mut bus); - assert_eq!(cpu.registers.pc, 0xFD); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_bra() { - // test with positive nearlabel - // branch always taken - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - bus.write(0x01, 0b00001111); - cpu.bra(&mut bus); - assert_eq!(cpu.registers.pc, 0x02 + 0b00001111); - assert_eq!(cpu.cycles, 3); - // test with negative nearlabel and boundary cross - cpu.registers.pc = 0x0100; - cpu.cycles = 0; - bus.write(0x101, 0xFB); // write -5 - cpu.bra(&mut bus); - assert_eq!(cpu.registers.pc, 0xFD); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_brl() { - // test with positive nearlabel - // branch always taken - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0001; - cpu.cycles = 0; - bus.write(0x01, 0b00000000); - bus.write(0x02, 0b00001111); - cpu.brl(&mut bus); - assert_eq!(cpu.registers.pc, 0x04 + 0b00001111_00000000); - assert_eq!(cpu.cycles, 4); - // test with negative nearlabel and boundary cross - cpu.registers.pc = 0x00FD; - cpu.cycles = 0; - bus.write(0xFD, 0xFF); // write -1 - bus.write(0xFE, 0xFF); // write -1 - cpu.brl(&mut bus); - assert_eq!(cpu.registers.pc, 0xFF); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_bvc() { - // test with positive nearlabel - // branch not taken - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_overflow_flag(true); - bus.write(0x02, 0b00001111); - cpu.bvc(&mut bus); - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - // branch taken - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_overflow_flag(false); - bus.write(0x01, 0b00001111); - cpu.bvc(&mut bus); - assert_eq!(cpu.registers.pc, 0x02 + 0b00001111); - assert_eq!(cpu.cycles, 3); - // test with negative nearlabel and boundary cross - cpu.registers.pc = 0x0100; - cpu.cycles = 0; - cpu.registers.set_overflow_flag(false); - bus.write(0x101, 0xFB); // write -5 - cpu.bvc(&mut bus); - assert_eq!(cpu.registers.pc, 0xFD); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_bvs() { - // test with positive nearlabel - // branch not taken - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_overflow_flag(false); - bus.write(0x02, 0b00001111); - cpu.bvs(&mut bus); - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - // branch taken - cpu.registers.pc = 0x0000; - cpu.cycles = 0; - cpu.registers.set_overflow_flag(true); - bus.write(0x01, 0b00001111); - cpu.bvs(&mut bus); - assert_eq!(cpu.registers.pc, 0x02 + 0b00001111); - assert_eq!(cpu.cycles, 3); - // test with negative nearlabel and boundary cross - cpu.registers.pc = 0x0100; - cpu.cycles = 0; - cpu.registers.set_overflow_flag(true); - bus.write(0x101, 0xFB); // write -5 - cpu.bvs(&mut bus); - assert_eq!(cpu.registers.pc, 0xFD); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_cmp() { - // CMP is basically an SBC instruction but it doesn't - // store the result nor it affects the overflow flag - 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); - bus.write(0x000001, 1); - cpu.cmp(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.a, 0x0001); // check A is not affected - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - assert!(!cpu.registers.get_carry_flag()); - assert!(cpu.registers.get_zero_flag()); - - // check overflow flag is not affected - cpu.cycles = 0; - cpu.registers.a = 0x0050; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.set_16bit_mode(false); - cpu.registers.set_overflow_flag(false); - bus.write(0x000001, 0xB0); - cpu.cmp(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.a, 0x0050); // check A is not affected - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - assert!(cpu.registers.get_carry_flag()); - assert!(!cpu.registers.get_zero_flag()); - assert!(!cpu.registers.get_overflow_flag()); - } - - #[test] - fn test_cpx() { - // CMP is basically an SBC instruction but it doesn't - // store the result nor it affects the overflow flag - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.x = 0x01; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.set_16bit_index(false); - bus.write(0x000001, 1); - cpu.cpx(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.x, 0x01); // check A is not affected - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - assert!(!cpu.registers.get_carry_flag()); - assert!(cpu.registers.get_zero_flag()); - - // check overflow flag is not affected - cpu.cycles = 0; - cpu.registers.x = 0x50; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.emulation_mode = false; - cpu.registers.set_16bit_index(true); - cpu.registers.set_overflow_flag(false); - bus.write(0x000002, 0xB0); - bus.write(0x000001, 0x00); - cpu.cpx(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.x, 0x50); // check X is not affected - assert_eq!(cpu.registers.pc, 0x03); - assert_eq!(cpu.cycles, 3); - assert!(cpu.registers.get_carry_flag()); - assert!(!cpu.registers.get_zero_flag()); - assert!(!cpu.registers.get_overflow_flag()); - } - - #[test] - fn test_cpy() { - // CMP is basically an SBC instruction but it doesn't - // store the result nor it affects the overflow flag - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.y = 0x01; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.set_16bit_index(false); - bus.write(0x000001, 1); - cpu.cpy(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.y, 0x01); // check A is not affected - assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 2); - assert!(!cpu.registers.get_carry_flag()); - assert!(cpu.registers.get_zero_flag()); - - // check overflow flag is not affected - cpu.cycles = 0; - cpu.registers.y = 0x50; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.set_16bit_index(true); - cpu.registers.set_overflow_flag(false); - bus.write(0x000002, 0xB0); - bus.write(0x000001, 0x00); - cpu.cpy(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.y, 0x50); // check X is not affected - assert_eq!(cpu.registers.pc, 0x03); - assert_eq!(cpu.cycles, 3); - assert!(cpu.registers.get_carry_flag()); - 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()); - } - - #[test] - fn test_inc() { - 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.inc(&mut bus, AddressingMode::Accumulator); - assert_eq!(cpu.registers.a, 2); - 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_inx() { - let mut cpu = CPU::new(); - cpu.registers.x = 0x0001; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.inx(); - assert_eq!(cpu.registers.x, 2); - 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_iny() { - let mut cpu = CPU::new(); - cpu.registers.y = 0x0001; - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.iny(); - assert_eq!(cpu.registers.y, 2); - 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_nop() { - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.nop(); - assert_eq!(cpu.registers.pc, 0x01); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_jmp() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - bus.write(0x000002, 0xAA); - bus.write(0x000001, 0xBB); - cpu.jmp(&mut bus, AddressingMode::Absolute); - assert_eq!(cpu.registers.pc, 0xAABB); - assert_eq!(cpu.cycles, 3); - - // Test a long address - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.pbr = 0x00; - bus.write(0x000003, 0xAA); - bus.write(0x000002, 0xBB); - bus.write(0x000001, 0xCC); - cpu.jmp(&mut bus, AddressingMode::AbsoluteLong); - assert_eq!(cpu.registers.pbr, 0xAA); - assert_eq!(cpu.registers.pc, 0xBBCC); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_jsr() { - // Test a long address - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x1234; - cpu.registers.pbr = 0x00; - cpu.registers.sp = 0x1FC; - bus.write(cpu.registers.get_pc_address() + 3, 0xAA); - bus.write(cpu.registers.get_pc_address() + 2, 0xBB); - bus.write(cpu.registers.get_pc_address() + 1, 0xCC); - // write next instruction - cpu.jsr(&mut bus, AddressingMode::AbsoluteLong); - assert_eq!(bus.read(0x1FC), 0x00); - assert_eq!(bus.read(0x1FB), 0x12); - assert_eq!(bus.read(0x1FA), 0x38); // we should store the NEXT instruction - assert_eq!(cpu.registers.pbr, 0xAA); - assert_eq!(cpu.registers.pc, 0xBBCC); - assert_eq!(cpu.registers.sp, 0x1F9); - assert_eq!(cpu.cycles, 8); - } - - #[test] - fn test_lda() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.a = 0x0000; - cpu.registers.pc = 0x0000; - cpu.registers.pbr = 0x00; - cpu.registers.set_negative_flag(false); - cpu.registers.set_zero_flag(true); - cpu.registers.set_16bit_mode(false); - bus.write(0x0001, 0xFF); - cpu.lda(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.pc, 0x0002); - assert_eq!(cpu.registers.a, 0x00FF); - assert_eq!(cpu.cycles, 2); - assert!(cpu.registers.get_negative_flag()); - assert!(!cpu.registers.get_zero_flag()); - } - - #[test] - fn test_ldx() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.x = 0x0000; - cpu.registers.pc = 0x0000; - cpu.registers.pbr = 0x00; - cpu.registers.set_negative_flag(false); - cpu.registers.set_zero_flag(true); - cpu.registers.set_16bit_index(false); - bus.write(0x0001, 0xFF); - cpu.ldx(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.pc, 0x0002); - assert_eq!(cpu.registers.x, 0x00FF); - assert_eq!(cpu.cycles, 2); - assert!(cpu.registers.get_negative_flag()); - assert!(!cpu.registers.get_zero_flag()); - } - - #[test] - fn test_ldy() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.y = 0x0000; - cpu.registers.pc = 0x0000; - cpu.registers.pbr = 0x00; - cpu.registers.set_negative_flag(false); - cpu.registers.set_zero_flag(true); - cpu.registers.set_16bit_index(false); - bus.write(0x0001, 0xFF); - cpu.ldy(&mut bus, AddressingMode::Immediate); - assert_eq!(cpu.registers.pc, 0x0002); - assert_eq!(cpu.registers.y, 0x00FF); - assert_eq!(cpu.cycles, 2); - assert!(cpu.registers.get_negative_flag()); - assert!(!cpu.registers.get_zero_flag()); - } - - #[test] - fn test_pea() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.sp = 0x1FC; - bus.write(0x000002, 0xAA); - bus.write(0x000001, 0xBB); - cpu.pea(&mut bus); - assert_eq!(bus.read(0x1FC), 0xAA); - assert_eq!(bus.read(0x1FB), 0xBB); - assert_eq!(cpu.registers.pc, 0x0003); - assert_eq!(cpu.cycles, 5); - } - - #[test] - fn test_pei() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.sp = 0x1FC; - cpu.registers.d = 0x00; - bus.write(0x000001, 0x02); // Direct page address - bus.write(0x000002, 0xAA); - bus.write(0x000003, 0xBB); - cpu.pei(&mut bus); - assert_eq!(bus.read(0x1FC), 0xAA); - assert_eq!(bus.read(0x1FB), 0xBB); - assert_eq!(cpu.registers.pc, 0x0002); - assert_eq!(cpu.cycles, 6); - } - - #[test] - fn test_per() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.sp = 0x1FC; - bus.write(0x000002, 0x00); - bus.write(0x000001, 0x01); - cpu.per(&mut bus); - assert_eq!(bus.read(0x1FC), 0x00); - assert_eq!(bus.read(0x1FB), 0x04); - assert_eq!(cpu.registers.pc, 0x0003); - assert_eq!(cpu.cycles, 6); - } - - #[test] - fn test_pha() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.sp = 0x1FC; - cpu.registers.a = 0x1234; - cpu.registers.set_16bit_mode(false); - cpu.pha(&mut bus); - assert_eq!(bus.read(0x1FC), 0x34); - assert_eq!(cpu.registers.sp, 0x1FB); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 3); - } - - #[test] - fn test_phb() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.sp = 0x1FC; - cpu.registers.dbr = 0x12; - cpu.phb(&mut bus); - assert_eq!(bus.read(0x1FC), 0x12); - assert_eq!(cpu.registers.sp, 0x1FB); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 3); - } - - #[test] - fn test_phd() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.sp = 0x1FC; - cpu.registers.d = 0x1234; - cpu.phd(&mut bus); - assert_eq!(bus.read(0x1FC), 0x12); - assert_eq!(bus.read(0x1FB), 0x34); - assert_eq!(cpu.registers.sp, 0x1FA); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_phk() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.pbr = 0x00; - cpu.registers.sp = 0x1FC; - bus.write(0x1FC, 0xFF); - cpu.phk(&mut bus); - assert_eq!(bus.read(0x1FC), 0x00); - assert_eq!(cpu.registers.sp, 0x1FB); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 3); - } - - #[test] - fn test_php() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.p = 0x12; - cpu.registers.sp = 0x1FC; - cpu.php(&mut bus); - assert_eq!(bus.read(0x1FC), 0x12); - assert_eq!(cpu.registers.sp, 0x1FB); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 3); - } - - #[test] - fn test_phx() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.set_16bit_index(true); - cpu.registers.pc = 0x0000; - cpu.registers.x = 0x1234; - cpu.registers.sp = 0x1FC; - cpu.phx(&mut bus); - assert_eq!(bus.read(0x1FC), 0x12); - assert_eq!(bus.read(0x1FB), 0x34); - assert_eq!(cpu.registers.sp, 0x1FA); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_phy() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.set_16bit_index(true); - cpu.registers.pc = 0x0000; - cpu.registers.y = 0x1234; - cpu.registers.sp = 0x1FC; - cpu.phy(&mut bus); - assert_eq!(bus.read(0x1FC), 0x12); - assert_eq!(bus.read(0x1FB), 0x34); - assert_eq!(cpu.registers.sp, 0x1FA); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_pla() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.pc = 0x0000; - cpu.registers.y = 0x1234; - cpu.registers.set_16bit_mode(true); - cpu.registers.set_negative_flag(true); - cpu.registers.set_zero_flag(true); - bus.write(0x1FB, 0x34); - bus.write(0x1FC, 0x12); - cpu.registers.sp = 0x1FA; - cpu.pla(&mut bus); - assert_eq!(cpu.registers.a, 0x1234); - assert_eq!(cpu.registers.sp, 0x1FC); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.registers.get_negative_flag(), false); - assert_eq!(cpu.registers.get_zero_flag(), false); - assert_eq!(cpu.cycles, 5); - } - - #[test] - fn test_plb() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.dbr = 0x00; - cpu.registers.set_negative_flag(true); - cpu.registers.set_zero_flag(true); - bus.write(0x1FC, 0x12); - cpu.registers.sp = 0x1FB; - cpu.plb(&mut bus); - assert_eq!(cpu.registers.dbr, 0x12); - assert_eq!(cpu.registers.sp, 0x1FC); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.registers.get_negative_flag(), false); - assert_eq!(cpu.registers.get_zero_flag(), false); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_pld() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.d = 0x1234; - cpu.registers.set_negative_flag(true); - cpu.registers.set_zero_flag(true); - bus.write(0x1FB, 0x34); - bus.write(0x1FC, 0x12); - cpu.registers.sp = 0x1FA; - cpu.pld(&mut bus); - assert_eq!(cpu.registers.d, 0x1234); - assert_eq!(cpu.registers.sp, 0x1FC); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.registers.get_negative_flag(), false); - assert_eq!(cpu.registers.get_zero_flag(), false); - assert_eq!(cpu.cycles, 5); - } - - #[test] - fn test_plp() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.p = 0x00; - bus.write(0x1FC, 0xFF); - cpu.registers.sp = 0x1FB; - cpu.plp(&mut bus); - assert_eq!(cpu.registers.p, 0xFF); - assert_eq!(cpu.registers.sp, 0x1FC); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_plx() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.pc = 0x0000; - cpu.registers.x = 0x1234; - cpu.registers.set_16bit_index(true); - cpu.registers.set_negative_flag(true); - cpu.registers.set_zero_flag(true); - bus.write(0x1FB, 0x34); - bus.write(0x1FC, 0x12); - cpu.registers.sp = 0x1FA; - cpu.plx(&mut bus); - assert_eq!(cpu.registers.x, 0x1234); - assert_eq!(cpu.registers.sp, 0x1FC); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.registers.get_negative_flag(), false); - assert_eq!(cpu.registers.get_zero_flag(), false); - assert_eq!(cpu.cycles, 5); - } - - #[test] - fn test_ply() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.pc = 0x0000; - cpu.registers.y = 0x1234; - cpu.registers.set_16bit_index(true); - cpu.registers.set_negative_flag(true); - cpu.registers.set_zero_flag(true); - bus.write(0x1FB, 0x34); - bus.write(0x1FC, 0x12); - cpu.registers.sp = 0x1FA; - cpu.ply(&mut bus); - assert_eq!(cpu.registers.y, 0x1234); - assert_eq!(cpu.registers.sp, 0x1FC); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.registers.get_negative_flag(), false); - assert_eq!(cpu.registers.get_zero_flag(), false); - assert_eq!(cpu.cycles, 5); - } - - #[test] - fn test_rep() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.pc = 0x0000; - cpu.registers.p = 0xFF; - bus.write(0x0001, 0xFF); - cpu.rep(&mut bus); - assert_eq!(cpu.registers.p, 0x00); - assert_eq!(cpu.registers.pc, 0x0002); - assert_eq!(cpu.cycles, 3); - } - - #[test] - fn test_rol() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.set_16bit_mode(false); - cpu.registers.a = 0b0100_0000; - cpu.registers.pc = 0x0000; - cpu.rol(&mut bus, AddressingMode::Accumulator); - assert_eq!(cpu.registers.get_negative_flag(), true); - assert_eq!(cpu.registers.get_zero_flag(), false); - assert_eq!(cpu.registers.a, 0b1000_0000); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_ror() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.emulation_mode = false; - cpu.registers.set_16bit_mode(false); - cpu.registers.set_carry_flag(true); - cpu.registers.a = 0x00; - cpu.registers.pc = 0x0000; - cpu.ror(&mut bus, AddressingMode::Accumulator); - assert_eq!(cpu.registers.get_carry_flag(), false); - assert_eq!(cpu.registers.get_zero_flag(), false); - assert_eq!(cpu.registers.a, 0b1000_0000); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_rtl() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.sp = 0x1F9; - bus.write(0x1FC, 0x12); - bus.write(0x1FB, 0x34); - bus.write(0x1FA, 0x56); - cpu.rtl(&mut bus); - assert_eq!(cpu.registers.pbr, 0x12); - assert_eq!(cpu.registers.pc, 0x3456); - assert_eq!(cpu.cycles, 6); - } - - #[test] - fn test_rts() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pbr = 0x00; - cpu.registers.pc = 0x0000; - cpu.registers.sp = 0x1FA; - bus.write(0x1FC, 0x12); - bus.write(0x1FB, 0x34); - cpu.rts(&mut bus); - assert_eq!(cpu.registers.pbr, 0x00); - assert_eq!(cpu.registers.pc, 0x1234); - assert_eq!(cpu.cycles, 6); - } - - #[test] - fn test_sec() { - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.registers.set_carry_flag(false); - cpu.sec(); - assert_eq!(cpu.registers.get_carry_flag(), true); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_sed() { - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.registers.set_decimal_mode_flag(false); - cpu.sed(); - assert_eq!(cpu.registers.get_decimal_mode_flag(), true); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_sei() { - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.registers.set_irq_disable_flag(false); - cpu.sei(); - assert_eq!(cpu.registers.get_irq_disable_flag(), true); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_sep() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.p = 0x00; - bus.write(0x0001, 0xFF); - cpu.sep(&mut bus); - assert_eq!(cpu.registers.p, 0xFF); - assert_eq!(cpu.registers.pc, 0x0002); - assert_eq!(cpu.cycles, 3); - } - - #[test] - fn test_sta() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.a = 0x12; - cpu.registers.set_16bit_mode(false); - bus.write(0x0002, 0x00); - bus.write(0x0001, 0x03); - cpu.sta(&mut bus, AddressingMode::Absolute); - assert_eq!(bus.read(0x0003), 0x12); - assert_eq!(cpu.registers.pc, 0x0003); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_stp() { - let mut cpu = CPU::new(); - cpu.is_stopped = false; - cpu.registers.pc = 0x0000; - cpu.stp(); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.is_stopped, true); - assert_eq!(cpu.cycles, 3); - } - - #[test] - fn test_stx() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.x = 0x12; - cpu.registers.set_16bit_index(false); - bus.write(0x0002, 0x00); - bus.write(0x0001, 0x03); - cpu.stx(&mut bus, AddressingMode::Absolute); - assert_eq!(bus.read(0x0003), 0x12); - assert_eq!(cpu.registers.pc, 0x0003); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_sty() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.y = 0x12; - cpu.registers.set_16bit_index(false); - bus.write(0x0002, 0x00); - bus.write(0x0001, 0x03); - cpu.sty(&mut bus, AddressingMode::Absolute); - assert_eq!(bus.read(0x0003), 0x12); - assert_eq!(cpu.registers.pc, 0x0003); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_stz() { - let mut cpu = CPU::new(); - let mut bus = Bus::new(); - cpu.registers.pc = 0x0000; - cpu.registers.set_16bit_index(false); - bus.write(0x0002, 0x00); - bus.write(0x0001, 0x03); - bus.write(0x0003, 0xFF); - cpu.stz(&mut bus, AddressingMode::Absolute); - assert_eq!(bus.read(0x0003), 0x00); - assert_eq!(cpu.registers.pc, 0x0003); - assert_eq!(cpu.cycles, 4); - } - - #[test] - fn test_tax() { - let mut cpu = CPU::new(); - cpu.registers.emulation_mode = false; - cpu.registers.pc = 0x0000; - cpu.registers.a = 0xF0F0; - cpu.registers.x = 0x0000; - cpu.registers.set_16bit_mode(true); - cpu.registers.set_16bit_index(true); - cpu.tax(); - assert_eq!(cpu.registers.get_negative_flag(), true); - assert_eq!(cpu.registers.get_zero_flag(), false); - assert_eq!(cpu.registers.x, 0xF0F0); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_tay() { - let mut cpu = CPU::new(); - cpu.registers.emulation_mode = false; - cpu.registers.pc = 0x0000; - cpu.registers.a = 0xF0F0; - cpu.registers.y = 0x0000; - cpu.registers.set_16bit_mode(true); - cpu.registers.set_16bit_index(true); - cpu.tay(); - assert_eq!(cpu.registers.get_negative_flag(), true); - assert_eq!(cpu.registers.get_zero_flag(), false); - assert_eq!(cpu.registers.y, 0xF0F0); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_tcd() { - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.registers.a = 0xF0F0; - cpu.registers.d = 0x0000; - cpu.tcd(); - assert_eq!(cpu.registers.d, 0xF0F0); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_tcs() { - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.registers.a = 0xF0F0; - cpu.registers.sp = 0x0000; - cpu.tcs(); - assert_eq!(cpu.registers.sp, 0xF0F0); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_tdc() { - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.registers.a = 0x0000; - cpu.registers.d = 0xF0F0; - cpu.tdc(); - assert_eq!(cpu.registers.a, 0xF0F0); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_tsc() { - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.registers.a = 0x0000; - cpu.registers.sp = 0xF0F0; - cpu.tsc(); - assert_eq!(cpu.registers.a, 0xF0F0); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_tsx() { - let mut cpu = CPU::new(); - cpu.registers.emulation_mode = false; - cpu.registers.pc = 0x0000; - cpu.registers.x = 0x0000; - cpu.registers.sp = 0xF0F0; - cpu.registers.set_16bit_index(true); - cpu.tsx(); - assert_eq!(cpu.registers.x, 0xF0F0); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_txa() { - let mut cpu = CPU::new(); - cpu.registers.emulation_mode = false; - cpu.registers.pc = 0x0000; - cpu.registers.a = 0x0000; - cpu.registers.x = 0xF0F0; - cpu.registers.set_16bit_mode(true); - cpu.txa(); - assert_eq!(cpu.registers.a, 0xF0F0); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_txs() { - let mut cpu = CPU::new(); - cpu.registers.emulation_mode = false; - cpu.registers.pc = 0x0000; - cpu.registers.sp = 0x0000; - cpu.registers.x = 0xF0F0; - cpu.registers.set_16bit_index(true); - cpu.txs(); - assert_eq!(cpu.registers.sp, 0xF0F0); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_txy() { - let mut cpu = CPU::new(); - cpu.registers.emulation_mode = false; - cpu.registers.pc = 0x0000; - cpu.registers.y = 0x0000; - cpu.registers.x = 0xF0F0; - cpu.registers.set_16bit_index(true); - cpu.txy(); - assert_eq!(cpu.registers.y, 0xF0F0); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_tya() { - let mut cpu = CPU::new(); - cpu.registers.emulation_mode = false; - cpu.registers.pc = 0x0000; - cpu.registers.a = 0x0000; - cpu.registers.y = 0xF0F0; - cpu.registers.set_16bit_mode(true); - cpu.tya(); - assert_eq!(cpu.registers.a, 0xF0F0); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_tyx() { - let mut cpu = CPU::new(); - cpu.registers.emulation_mode = false; - cpu.registers.pc = 0x0000; - cpu.registers.x = 0x0000; - cpu.registers.y = 0xF0F0; - cpu.registers.set_16bit_index(true); - cpu.tyx(); - assert_eq!(cpu.registers.x, 0xF0F0); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_wai() { - let mut cpu = CPU::new(); - cpu.is_waiting_interrupt = false; - cpu.registers.pc = 0x0000; - cpu.wai(); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.is_waiting_interrupt, true); - assert_eq!(cpu.cycles, 3); - } - - #[test] - fn test_wdm() { - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.wdm(); - assert_eq!(cpu.registers.pc, 0x0002); - assert_eq!(cpu.cycles, 2); - } - - #[test] - fn test_xba() { - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.registers.a = 0x11FF; - cpu.xba(); - assert_eq!(cpu.registers.a, 0xFF11); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 3); - } - - #[test] - fn test_xce() { - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.xce(); - assert_eq!(cpu.registers.pc, 0x0001); - assert_eq!(cpu.cycles, 2); - } -} diff --git a/snes-core/src/cpu/instructions/adc.rs b/snes-core/src/cpu/instructions/adc.rs new file mode 100644 index 0000000..d487898 --- /dev/null +++ b/snes-core/src/cpu/instructions/adc.rs @@ -0,0 +1,207 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}}; + +use crate::cpu::cycles; +use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "ADC"; + +pub struct ADC { + pub addressing_mode: AddressingMode, +} + +impl ADC { + fn determine_instruction(&self, registers: &Registers) -> Box { + let is_decimal_mode = registers.get_decimal_mode_flag(); + match registers.is_16bit_mode() { + true => match is_decimal_mode { + true => Box::new(ADC16BCD{addressing_mode: self.addressing_mode}), + false => Box::new(ADC16BIN{addressing_mode: self.addressing_mode}), + } + false => match is_decimal_mode { + true => Box::new(ADC8BCD{addressing_mode: self.addressing_mode}), + false => Box::new(ADC8BIN{addressing_mode: self.addressing_mode}), + } + } + } +} + +impl CPUInstruction for ADC { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct ADC8BIN { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for ADC8BIN { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let (result, affected_flags) = alu::adc_bin( + registers.a as u8, + read_8bit_from_address(registers, bus, self.addressing_mode), + registers.get_carry_flag(), + ); + registers.set_low_a(result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct ADC16BIN { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for ADC16BIN { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let (result, affected_flags) = alu::adc_bin( + registers.a, + read_16bit_from_address(registers, bus, self.addressing_mode), + registers.get_carry_flag(), + ); + registers.a = result; + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct ADC8BCD { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for ADC8BCD { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let (result, affected_flags) = alu::adc_bcd( + registers.a as u8, + read_8bit_from_address(registers, bus, self.addressing_mode), + registers.get_carry_flag(), + ); + registers.set_low_a(result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct ADC16BCD { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for ADC16BCD { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let (result, affected_flags) = alu::adc_bcd( + registers.a, + read_16bit_from_address(registers, bus, self.addressing_mode), + registers.get_carry_flag(), + ); + registers.a = result; + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_adc_bin_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x0000; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(true); + bus.write(0x000001, 0x40); + let instruction = ADC8BIN{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x40); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_carry_flag()); + } + + #[test] + fn test_adc_bin_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x0000; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(false); + bus.write(0x000001, 0x00); + bus.write(0x000002, 0x40); + let instruction = ADC16BIN{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x4000); + assert_eq!(registers.pc, 0x03); + assert_eq!(registers.cycles, 3); + assert!(!registers.get_carry_flag()); + } + + #[test] + fn test_adc_bcd_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x0000; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(true); + bus.write(0x000001, 0x40); + let instruction = ADC8BCD{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x40); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_carry_flag()); + } + + #[test] + fn test_adc_bcd_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x0000; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(false); + bus.write(0x000001, 0x00); + bus.write(0x000002, 0x40); + let instruction = ADC16BCD{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x4000); + assert_eq!(registers.pc, 0x03); + assert_eq!(registers.cycles, 3); + assert!(!registers.get_carry_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/and.rs b/snes-core/src/cpu/instructions/and.rs new file mode 100644 index 0000000..683ecb0 --- /dev/null +++ b/snes-core/src/cpu/instructions/and.rs @@ -0,0 +1,119 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}}; + +use crate::cpu::cycles; +use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "AND"; + +pub struct AND { + pub addressing_mode: AddressingMode, +} + +impl AND { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(AND16{addressing_mode: self.addressing_mode}), + false => Box::new(AND8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for AND { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct AND8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for AND8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let (result, affected_flags) = alu::and( + registers.a as u8, + read_8bit_from_address(registers, bus, self.addressing_mode), + ); + registers.set_low_a(result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_bitwise(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct AND16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for AND16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let (result, affected_flags) = alu::and( + registers.a, + read_16bit_from_address(registers, bus, self.addressing_mode), + ); + registers.a = result; + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_bitwise(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_and_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x0101; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(true); + bus.write(0x000001, 0x00); + let instruction = AND8{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x0100); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_carry_flag()); + assert!(registers.get_zero_flag()); + } + + #[test] + fn test_and_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x0101; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(false); + bus.write(0x000001, 0x01); + bus.write(0x000002, 0x01); + let instruction = AND16{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x0101); + assert_eq!(registers.pc, 0x03); + assert_eq!(registers.cycles, 3); + assert!(!registers.get_carry_flag()); + assert!(!registers.get_zero_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/asl.rs b/snes-core/src/cpu/instructions/asl.rs new file mode 100644 index 0000000..cbf7dae --- /dev/null +++ b/snes-core/src/cpu/instructions/asl.rs @@ -0,0 +1,116 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}}; + +use crate::cpu::cycles; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "ASL"; + +pub struct ASL { + pub addressing_mode: AddressingMode, +} + +impl ASL { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(ASL8{addressing_mode: self.addressing_mode}), + false => Box::new(ASL16{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for ASL { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct ASL8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for ASL8 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let (result, affected_flags) = alu::asl( + registers.a as u8, + ); + registers.set_low_a(result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_bitwise(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct ASL16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for ASL16 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let (result, affected_flags) = alu::asl( + registers.a, + ); + registers.a = result; + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_bitwise(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_asl_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0b01010000; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(true); + let instruction = ASL8{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0b10100000); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_carry_flag()); + assert!(!registers.get_zero_flag()); + assert!(registers.get_negative_flag()); + } + + #[test] + fn test_asl_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0b01010000_00000000; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(false); + let instruction = ASL16{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0b10100000_00000000); + assert_eq!(registers.pc, 0x03); + assert_eq!(registers.cycles, 3); + assert!(!registers.get_carry_flag()); + assert!(!registers.get_zero_flag()); + assert!(registers.get_negative_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/bcc.rs b/snes-core/src/cpu/instructions/bcc.rs new file mode 100644 index 0000000..d75d7d8 --- /dev/null +++ b/snes-core/src/cpu/instructions/bcc.rs @@ -0,0 +1,57 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; +use super::branch_common; + +static INSTR_NAME: &'static str = "BCC"; + +pub struct BCC {} + +impl CPUInstruction for BCC { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + branch_common::do_branch_instr(registers, bus, !registers.get_carry_flag()); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let instruction = BCC{}; + // test with positive nearlabel + // branch not taken + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_carry_flag(true); + bus.write(0x02, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + // branch taken + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_carry_flag(false); + bus.write(0x01, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02 + 0b00001111); + assert_eq!(registers.cycles, 3); + // test with negative nearlabel and boundary cross + registers.pc = 0x0100; + registers.cycles = 0; + registers.set_carry_flag(false); + bus.write(0x101, 0xFB); // write -5 + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0xFD); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/bcs.rs b/snes-core/src/cpu/instructions/bcs.rs new file mode 100644 index 0000000..5189a4e --- /dev/null +++ b/snes-core/src/cpu/instructions/bcs.rs @@ -0,0 +1,57 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; +use super::branch_common; + +static INSTR_NAME: &'static str = "BCS"; + +pub struct BCS {} + +impl CPUInstruction for BCS { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + branch_common::do_branch_instr(registers, bus, registers.get_carry_flag()); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let instruction = BCS{}; + // test with positive nearlabel + // branch not taken + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_carry_flag(false); + bus.write(0x02, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + // branch taken + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_carry_flag(true); + bus.write(0x01, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02 + 0b00001111); + assert_eq!(registers.cycles, 3); + // test with negative nearlabel and boundary cross + registers.pc = 0x0100; + registers.cycles = 0; + registers.set_carry_flag(true); + bus.write(0x101, 0xFB); // write -5 + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0xFD); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/beq.rs b/snes-core/src/cpu/instructions/beq.rs new file mode 100644 index 0000000..a3c347b --- /dev/null +++ b/snes-core/src/cpu/instructions/beq.rs @@ -0,0 +1,57 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; +use super::branch_common; + +static INSTR_NAME: &'static str = "BEQ"; + +pub struct BEQ {} + +impl CPUInstruction for BEQ { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + branch_common::do_branch_instr(registers, bus, registers.get_zero_flag()); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let instruction = BEQ{}; + // test with positive nearlabel + // branch not taken + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_zero_flag(false); + bus.write(0x02, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + // branch taken + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_zero_flag(true); + bus.write(0x01, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02 + 0b00001111); + assert_eq!(registers.cycles, 3); + // test with negative nearlabel and boundary cross + registers.pc = 0x0100; + registers.cycles = 0; + registers.set_zero_flag(true); + bus.write(0x101, 0xFB); // write -5 + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0xFD); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/bit.rs b/snes-core/src/cpu/instructions/bit.rs new file mode 100644 index 0000000..f48aba3 --- /dev/null +++ b/snes-core/src/cpu/instructions/bit.rs @@ -0,0 +1,128 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::addressing::AddressingMode}; + +use crate::cpu::cycles; +use super::{CPUInstruction, bit_common, read_write_common::{read_8bit_from_address, read_16bit_from_address}}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "BIT"; + +pub struct BIT { + pub addressing_mode: AddressingMode, +} + +impl BIT { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(BIT16{addressing_mode: self.addressing_mode}), + false => Box::new(BIT8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for BIT { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct BIT8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for BIT8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + bit_common::do_bit( + registers, + registers.a as u8, + read_8bit_from_address(registers, bus, self.addressing_mode), + self.addressing_mode, + ); + let (bytes, cycles) = cycles::increment_cycles_bit(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct BIT16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for BIT16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + bit_common::do_bit( + registers, + registers.a, + read_16bit_from_address(registers, bus, self.addressing_mode), + self.addressing_mode, + ); + let (bytes, cycles) = cycles::increment_cycles_bit(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0b1111_0000; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.p = 0x00; + bus.write(0x000001, 0b0000_1111); + registers.set_16bit_mode(false); + let instruction = BIT8{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + // Check that it only affects the zero flag on immediate mode + assert_eq!(registers.a, 0b1111_0000); // Check that A is not altered + assert_eq!(registers.p, 0b0010_0010); // Only zero flag was altered (bit 6 is memory select mode) + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(registers.get_zero_flag()); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0b00110000_00000000; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.p = 0x00; + registers.cycles = 0; + // Write absolute address + bus.write(0x000001, 0x04); + bus.write(0x000002, 0x00); + // Write effective value of address + bus.write(0x000004, 0x00); + bus.write(0x000005, 0b1100_0000); + registers.set_16bit_mode(true); + let instruction = BIT16{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + // Check that it only affects the zero flag on immediate mode + assert_eq!(registers.a, 0b00110000_00000000); // Check that A is not altered + assert_eq!(registers.p, 0b0000_0010); + assert_eq!(registers.pc, 0x03); + assert_eq!(registers.cycles, 3); + assert!(registers.get_zero_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/bit_common.rs b/snes-core/src/cpu/instructions/bit_common.rs new file mode 100644 index 0000000..5043a28 --- /dev/null +++ b/snes-core/src/cpu/instructions/bit_common.rs @@ -0,0 +1,14 @@ +use crate::{cpu::registers::Registers, utils::{num_trait::SnesNum, addressing::AddressingMode, alu}}; + +pub fn do_bit(registers: &mut Registers, accumulator: T, value: T, addressing_mode: AddressingMode) { + let (result, _) = alu::and(accumulator, value); + // Immediate addressing affects only the zero flag + match addressing_mode { + AddressingMode::Immediate => registers.set_zero_flag(result.is_zero()), + _ => { + registers.set_zero_flag(result.is_zero()); + registers.set_negative_flag(value.is_negative()); + registers.set_overflow_flag(value.next_to_highest_bit()); + } + }; +} diff --git a/snes-core/src/cpu/instructions/bmi.rs b/snes-core/src/cpu/instructions/bmi.rs new file mode 100644 index 0000000..b512d2d --- /dev/null +++ b/snes-core/src/cpu/instructions/bmi.rs @@ -0,0 +1,57 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; +use super::branch_common; + +static INSTR_NAME: &'static str = "BMI"; + +pub struct BMI {} + +impl CPUInstruction for BMI { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + branch_common::do_branch_instr(registers, bus, registers.get_negative_flag()); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let instruction = BMI{}; + // test with positive nearlabel + // branch not taken + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_negative_flag(false); + bus.write(0x02, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + // branch taken + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_negative_flag(true); + bus.write(0x01, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02 + 0b00001111); + assert_eq!(registers.cycles, 3); + // test with negative nearlabel and boundary cross + registers.pc = 0x0100; + registers.cycles = 0; + registers.set_negative_flag(true); + bus.write(0x101, 0xFB); // write -5 + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0xFD); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/bne.rs b/snes-core/src/cpu/instructions/bne.rs new file mode 100644 index 0000000..3735d06 --- /dev/null +++ b/snes-core/src/cpu/instructions/bne.rs @@ -0,0 +1,57 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; +use super::branch_common; + +static INSTR_NAME: &'static str = "BNE"; + +pub struct BNE {} + +impl CPUInstruction for BNE { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + branch_common::do_branch_instr(registers, bus, !registers.get_zero_flag()); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let instruction = BNE{}; + // test with positive nearlabel + // branch not taken + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_zero_flag(true); + bus.write(0x02, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + // branch taken + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_zero_flag(false); + bus.write(0x01, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02 + 0b00001111); + assert_eq!(registers.cycles, 3); + // test with negative nearlabel and boundary cross + registers.pc = 0x0100; + registers.cycles = 0; + registers.set_zero_flag(false); + bus.write(0x101, 0xFB); // write -5 + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0xFD); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/bpl.rs b/snes-core/src/cpu/instructions/bpl.rs new file mode 100644 index 0000000..ef29f3f --- /dev/null +++ b/snes-core/src/cpu/instructions/bpl.rs @@ -0,0 +1,57 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; +use super::branch_common; + +static INSTR_NAME: &'static str = "BPL"; + +pub struct BPL {} + +impl CPUInstruction for BPL { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + branch_common::do_branch_instr(registers, bus, !registers.get_negative_flag()); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let instruction = BPL{}; + // test with positive nearlabel + // branch not taken + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_negative_flag(true); + bus.write(0x02, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + // branch taken + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_negative_flag(false); + bus.write(0x01, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02 + 0b00001111); + assert_eq!(registers.cycles, 3); + // test with negative nearlabel and boundary cross + registers.pc = 0x0100; + registers.cycles = 0; + registers.set_negative_flag(false); + bus.write(0x101, 0xFB); // write -5 + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0xFD); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/bra.rs b/snes-core/src/cpu/instructions/bra.rs new file mode 100644 index 0000000..6e923d3 --- /dev/null +++ b/snes-core/src/cpu/instructions/bra.rs @@ -0,0 +1,53 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; +use super::branch_common; +use crate::cpu::cycles; + +static INSTR_NAME: &'static str = "BRA"; + +pub struct BRA {} + +impl CPUInstruction for BRA { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let nearlabel = bus.read(registers.get_pc_address().wrapping_add(1)); + let (bytes, cycles) = cycles::increment_cycles_branch(); + registers.increment_pc(bytes); registers.cycles += cycles; + let page_boundary_crossed = branch_common::do_branch(nearlabel, registers); + let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let instruction = BRA{}; + // test with positive nearlabel + // branch always taken + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.cycles = 0; + bus.write(0x01, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02 + 0b00001111); + assert_eq!(registers.cycles, 3); + // test with negative nearlabel and boundary cross + registers.pc = 0x0100; + registers.cycles = 0; + bus.write(0x101, 0xFB); // write -5 + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0xFD); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/branch_common.rs b/snes-core/src/cpu/instructions/branch_common.rs new file mode 100644 index 0000000..f46cba9 --- /dev/null +++ b/snes-core/src/cpu/instructions/branch_common.rs @@ -0,0 +1,27 @@ +use crate::cpu::{registers::Registers, bus::Bus}; +use crate::cpu::cycles; + +pub fn do_branch_instr(registers: &mut Registers, bus: &mut Bus, condition: bool) { + let nearlabel = bus.read(registers.get_pc_address().wrapping_add(1)); + let (bytes, cycles) = cycles::increment_cycles_branch(); + registers.increment_pc(bytes); registers.cycles += cycles; + if condition { + let page_boundary_crossed = do_branch(nearlabel, registers); + let (bytes, cycles) = cycles::increment_cycles_branch_taken(page_boundary_crossed); + registers.increment_pc(bytes); registers.cycles += cycles; + } +} + +pub fn do_branch(nearlabel: u8, registers: &mut Registers) -> bool { + let is_negative = (nearlabel >> 7) != 0; + let old_pc = registers.get_pc_address(); + if is_negative { + let nearlabel = !nearlabel + 1; + registers.decrement_pc(nearlabel as u16); + } else { + registers.increment_pc(nearlabel as u16); + } + let new_pc = registers.get_pc_address(); + let page_boundary_crossed = (old_pc & 0xFF00) != (new_pc & 0xFF00); + return page_boundary_crossed +} diff --git a/snes-core/src/cpu/instructions/brk.rs b/snes-core/src/cpu/instructions/brk.rs new file mode 100644 index 0000000..3d6673b --- /dev/null +++ b/snes-core/src/cpu/instructions/brk.rs @@ -0,0 +1,36 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; +use super::push_common; +use crate::cpu::cycles; + +static INSTR_NAME: &'static str = "BRK"; + +pub struct BRK {} + +impl CPUInstruction for BRK { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + push_common::do_push(registers, bus, &[registers.pbr]); + push_common::do_push(registers, bus, &[(registers.pc >> 8) as u8, registers.pc as u8]); + push_common::do_push(registers, bus, &[registers.p]); + registers.set_decimal_mode_flag(false); + registers.set_irq_disable_flag(true); + let (bytes, cycles) = cycles::increment_cycles_brk(registers.emulation_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + } +} diff --git a/snes-core/src/cpu/instructions/brl.rs b/snes-core/src/cpu/instructions/brl.rs new file mode 100644 index 0000000..b945ebf --- /dev/null +++ b/snes-core/src/cpu/instructions/brl.rs @@ -0,0 +1,59 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; +use crate::cpu::cycles; + +static INSTR_NAME: &'static str = "BRL"; + +pub struct BRL {} + +impl CPUInstruction for BRL { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let label = bus.read(registers.get_pc_address()) as u16 | + ((bus.read(registers.get_pc_address() + 1) as u16) << 8); + let is_negative = (label >> 15) != 0; + if is_negative { + let label = !label + 1; + registers.decrement_pc(label); + } else { + registers.increment_pc(label); + } + let (bytes, cycles) = cycles::increment_cycles_branch_long(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_absolute(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let instruction = BRL{}; + // test with positive nearlabel + // branch always taken + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0001; + registers.cycles = 0; + bus.write(0x01, 0b00000000); + bus.write(0x02, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x04 + 0b00001111_00000000); + assert_eq!(registers.cycles, 4); + // test with negative nearlabel and boundary cross + registers.pc = 0x00FD; + registers.cycles = 0; + bus.write(0xFD, 0xFF); // write -1 + bus.write(0xFE, 0xFF); // write -1 + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0xFF); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/bvc.rs b/snes-core/src/cpu/instructions/bvc.rs new file mode 100644 index 0000000..a81dc59 --- /dev/null +++ b/snes-core/src/cpu/instructions/bvc.rs @@ -0,0 +1,57 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; +use super::branch_common; + +static INSTR_NAME: &'static str = "BVC"; + +pub struct BVC {} + +impl CPUInstruction for BVC { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + branch_common::do_branch_instr(registers, bus, !registers.get_overflow_flag()); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let instruction = BVC{}; + // test with positive nearlabel + // branch not taken + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_overflow_flag(true); + bus.write(0x02, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + // branch taken + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_overflow_flag(false); + bus.write(0x01, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02 + 0b00001111); + assert_eq!(registers.cycles, 3); + // test with negative nearlabel and boundary cross + registers.pc = 0x0100; + registers.cycles = 0; + registers.set_overflow_flag(false); + bus.write(0x101, 0xFB); // write -5 + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0xFD); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/bvs.rs b/snes-core/src/cpu/instructions/bvs.rs new file mode 100644 index 0000000..d5f74cd --- /dev/null +++ b/snes-core/src/cpu/instructions/bvs.rs @@ -0,0 +1,57 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; +use super::branch_common; + +static INSTR_NAME: &'static str = "BVS"; + +pub struct BVS {} + +impl CPUInstruction for BVS { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + branch_common::do_branch_instr(registers, bus, registers.get_overflow_flag()); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_branch_nearlabel(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let instruction = BVS{}; + // test with positive nearlabel + // branch not taken + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_overflow_flag(false); + bus.write(0x02, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + // branch taken + registers.pc = 0x0000; + registers.cycles = 0; + registers.set_overflow_flag(true); + bus.write(0x01, 0b00001111); + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x02 + 0b00001111); + assert_eq!(registers.cycles, 3); + // test with negative nearlabel and boundary cross + registers.pc = 0x0100; + registers.cycles = 0; + registers.set_overflow_flag(true); + bus.write(0x101, 0xFB); // write -5 + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0xFD); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/clc.rs b/snes-core/src/cpu/instructions/clc.rs new file mode 100644 index 0000000..3a23e02 --- /dev/null +++ b/snes-core/src/cpu/instructions/clc.rs @@ -0,0 +1,40 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "CLC"; + +pub struct CLC {} + +impl CPUInstruction for CLC { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.set_carry_flag(false); + let (bytes, cycles) = cycles::increment_cycles_clear(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.set_carry_flag(true); + registers.pc = 0x0000; + let instruction = CLC{}; + instruction.execute(&mut registers, &mut bus); + assert!(!registers.get_carry_flag()); + assert_eq!(registers.pc, 1); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/cld.rs b/snes-core/src/cpu/instructions/cld.rs new file mode 100644 index 0000000..338c6a9 --- /dev/null +++ b/snes-core/src/cpu/instructions/cld.rs @@ -0,0 +1,40 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "CLD"; + +pub struct CLD {} + +impl CPUInstruction for CLD { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.set_decimal_mode_flag(false); + let (bytes, cycles) = cycles::increment_cycles_clear(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.set_decimal_mode_flag(true); + registers.pc = 0x0000; + let instruction = CLD{}; + instruction.execute(&mut registers, &mut bus); + assert!(!registers.get_decimal_mode_flag()); + assert_eq!(registers.pc, 1); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/cli.rs b/snes-core/src/cpu/instructions/cli.rs new file mode 100644 index 0000000..befef7e --- /dev/null +++ b/snes-core/src/cpu/instructions/cli.rs @@ -0,0 +1,40 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "CLI"; + +pub struct CLI {} + +impl CPUInstruction for CLI { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.set_irq_disable_flag(false); + let (bytes, cycles) = cycles::increment_cycles_clear(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.set_irq_disable_flag(true); + registers.pc = 0x0000; + let instruction = CLI{}; + instruction.execute(&mut registers, &mut bus); + assert!(!registers.get_irq_disable_flag()); + assert_eq!(registers.pc, 1); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/clv.rs b/snes-core/src/cpu/instructions/clv.rs new file mode 100644 index 0000000..c402a5a --- /dev/null +++ b/snes-core/src/cpu/instructions/clv.rs @@ -0,0 +1,40 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "CLV"; + +pub struct CLV {} + +impl CPUInstruction for CLV { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.set_overflow_flag(false); + let (bytes, cycles) = cycles::increment_cycles_clear(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.set_overflow_flag(true); + registers.pc = 0x0000; + let instruction = CLV{}; + instruction.execute(&mut registers, &mut bus); + assert!(!registers.get_overflow_flag()); + assert_eq!(registers.pc, 1); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/cmp.rs b/snes-core/src/cpu/instructions/cmp.rs new file mode 100644 index 0000000..988ec80 --- /dev/null +++ b/snes-core/src/cpu/instructions/cmp.rs @@ -0,0 +1,119 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::addressing::AddressingMode}; + +use crate::cpu::cycles; +use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}}; +use super::decoder_common; +use super::comp_common; + +static INSTR_NAME: &'static str = "CMP"; + +pub struct CMP { + pub addressing_mode: AddressingMode, +} + +impl CMP { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(CMP16{addressing_mode: self.addressing_mode}), + false => Box::new(CMP8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for CMP { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct CMP8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for CMP8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + comp_common::do_comp( + registers, + registers.a as u8, + read_8bit_from_address(registers, bus, self.addressing_mode), + ); + let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct CMP16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for CMP16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + comp_common::do_comp( + registers, + registers.a, + read_16bit_from_address(registers, bus, self.addressing_mode), + ); + let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + // CMP is basically an SBC instruction but it doesn't + // store the result nor it affects the overflow flag + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.a = 0x0001; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(true); + bus.write(0x000001, 1); + let instruction = CMP8{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + 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_zero_flag()); + } + + #[test] + fn test_and_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.a = 0x0050; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_16bit_mode(false); + registers.set_overflow_flag(false); + bus.write(0x000001, 0xB0); + let instruction = CMP16{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x0050); // check A is not affected + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(registers.get_carry_flag()); + assert!(!registers.get_zero_flag()); + assert!(!registers.get_overflow_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/comp_common.rs b/snes-core/src/cpu/instructions/comp_common.rs new file mode 100644 index 0000000..70f8cbe --- /dev/null +++ b/snes-core/src/cpu/instructions/comp_common.rs @@ -0,0 +1,12 @@ +use crate::{cpu::registers::Registers, utils::{num_trait::SnesNum, alu}, common::flags::Flags}; + + +pub fn do_comp(registers: &mut Registers, target: T, value: T) { + let (_, affected_flags) = alu::sbc_bin(target, value, false); + for flag in affected_flags { + match flag { + Flags::Overflow(_) => {}, + _ => registers.set_flags(&[flag]), + } + } +} \ No newline at end of file diff --git a/snes-core/src/cpu/instructions/cop.rs b/snes-core/src/cpu/instructions/cop.rs new file mode 100644 index 0000000..3037eab --- /dev/null +++ b/snes-core/src/cpu/instructions/cop.rs @@ -0,0 +1,36 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; +use super::push_common; +use crate::cpu::cycles; + +static INSTR_NAME: &'static str = "COP"; + +pub struct COP {} + +impl CPUInstruction for COP { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + push_common::do_push(registers, bus, &[registers.pbr]); + push_common::do_push(registers, bus, &[(registers.pc >> 8) as u8, registers.pc as u8]); + push_common::do_push(registers, bus, &[registers.p]); + registers.set_decimal_mode_flag(false); + registers.set_irq_disable_flag(true); + let (bytes, cycles) = cycles::increment_cycles_brk(registers.emulation_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + } +} diff --git a/snes-core/src/cpu/instructions/cpx.rs b/snes-core/src/cpu/instructions/cpx.rs new file mode 100644 index 0000000..c7f9ffe --- /dev/null +++ b/snes-core/src/cpu/instructions/cpx.rs @@ -0,0 +1,126 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::addressing::AddressingMode}; + +use crate::cpu::cycles; +use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}}; +use super::decoder_common; +use super::comp_common; + +static INSTR_NAME: &'static str = "CPX"; + +pub struct CPX { + pub addressing_mode: AddressingMode, +} + +impl CPX { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(CPX16{addressing_mode: self.addressing_mode}), + false => Box::new(CPX8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for CPX { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct CPX8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for CPX8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + comp_common::do_comp( + registers, + registers.x as u8, + read_8bit_from_address(registers, bus, self.addressing_mode), + ); + let (bytes, cycles) = cycles::increment_cycles_comp_index(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct CPX16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for CPX16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + comp_common::do_comp( + registers, + registers.x, + read_16bit_from_address(registers, bus, self.addressing_mode), + ); + let (bytes, cycles) = cycles::increment_cycles_comp_index(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + // CMP is basically an SBC instruction but it doesn't + // store the result nor it affects the overflow flag + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x10; + registers.x = 0x01; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_16bit_index(false); + bus.write(0x000001, 1); + let instruction = CPX8{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.x, 0x01); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_carry_flag()); + assert!(registers.get_zero_flag()); + } + + #[test] + fn test_and_16bit() { + // check overflow flag is not affected + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.cycles = 0; + registers.a = 0x10; + registers.x = 0x50; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.emulation_mode = false; + registers.set_16bit_index(true); + registers.set_overflow_flag(false); + bus.write(0x000002, 0xB0); + bus.write(0x000001, 0x00); + let instruction = CPX16{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + 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_zero_flag()); + assert!(!registers.get_overflow_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/cpy.rs b/snes-core/src/cpu/instructions/cpy.rs new file mode 100644 index 0000000..bfebe60 --- /dev/null +++ b/snes-core/src/cpu/instructions/cpy.rs @@ -0,0 +1,126 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::addressing::AddressingMode}; + +use crate::cpu::cycles; +use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}}; +use super::decoder_common; +use super::comp_common; + +static INSTR_NAME: &'static str = "CPY"; + +pub struct CPY { + pub addressing_mode: AddressingMode, +} + +impl CPY { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(CPY16{addressing_mode: self.addressing_mode}), + false => Box::new(CPY8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for CPY { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct CPY8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for CPY8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + comp_common::do_comp( + registers, + registers.y as u8, + read_8bit_from_address(registers, bus, self.addressing_mode), + ); + let (bytes, cycles) = cycles::increment_cycles_comp_index(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct CPY16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for CPY16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + comp_common::do_comp( + registers, + registers.y, + read_16bit_from_address(registers, bus, self.addressing_mode), + ); + let (bytes, cycles) = cycles::increment_cycles_comp_index(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + // CMP is basically an SBC instruction but it doesn't + // store the result nor it affects the overflow flag + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x10; + registers.y = 0x01; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_16bit_index(false); + bus.write(0x000001, 1); + let instruction = CPY8{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.y, 0x01); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_carry_flag()); + assert!(registers.get_zero_flag()); + } + + #[test] + fn test_and_16bit() { + // check overflow flag is not affected + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.cycles = 0; + registers.a = 0x10; + registers.y = 0x50; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.emulation_mode = false; + registers.set_16bit_index(true); + registers.set_overflow_flag(false); + bus.write(0x000002, 0xB0); + bus.write(0x000001, 0x00); + let instruction = CPY16{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + 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_zero_flag()); + assert!(!registers.get_overflow_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/dec.rs b/snes-core/src/cpu/instructions/dec.rs new file mode 100644 index 0000000..e61ff1c --- /dev/null +++ b/snes-core/src/cpu/instructions/dec.rs @@ -0,0 +1,112 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::addressing::AddressingMode}; + +use crate::cpu::cycles; +use super::{CPUInstruction, dec_common, read_write_common::{read_8bit_from_address, write_8bit_to_address, read_16bit_from_address, write_16bit_to_address}}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "DEC"; + +pub struct DEC { + pub addressing_mode: AddressingMode, +} + +impl DEC { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(DEC16{addressing_mode: self.addressing_mode}), + false => Box::new(DEC8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for DEC { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct DEC8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for DEC8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let result = dec_common::do_dec( + registers, + read_8bit_from_address(registers, bus, self.addressing_mode), + ) as u8; + write_8bit_to_address(registers, bus, self.addressing_mode, result); + let (bytes, cycles) = cycles::increment_cycles_inc_dec(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct DEC16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for DEC16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let result = dec_common::do_dec( + registers, + read_16bit_from_address(registers, bus, self.addressing_mode), + ) as u16; + write_16bit_to_address(registers, bus, self.addressing_mode, result); + let (bytes, cycles) = cycles::increment_cycles_inc_dec(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.a = 0x0001; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(true); + let instruction = DEC8{addressing_mode: AddressingMode::Accumulator}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_negative_flag()); + assert!(registers.get_zero_flag()); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.a = 0x0001; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(true); + let instruction = DEC16{addressing_mode: AddressingMode::Accumulator}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_negative_flag()); + assert!(registers.get_zero_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/dec_common.rs b/snes-core/src/cpu/instructions/dec_common.rs new file mode 100644 index 0000000..e9f39db --- /dev/null +++ b/snes-core/src/cpu/instructions/dec_common.rs @@ -0,0 +1,23 @@ +use crate::{cpu::registers::Registers, utils::{num_trait::SnesNum, alu}, common::flags::Flags}; + +pub fn do_dec(registers: &mut Registers, 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(_) => registers.set_flags(&[flag]), + _ => {}, + } + } + result +} + +pub fn do_inc(registers: &mut Registers, target: T) -> T { + let (result, affected_flags) = alu::adc_bin(target, T::from_u32(1), false); + for flag in affected_flags { + match flag { + Flags::Negative(_) | Flags::Zero(_) => registers.set_flags(&[flag]), + _ => {}, + } + } + result +} diff --git a/snes-core/src/cpu/instructions/decoder_common.rs b/snes-core/src/cpu/instructions/decoder_common.rs new file mode 100644 index 0000000..1838eea --- /dev/null +++ b/snes-core/src/cpu/instructions/decoder_common.rs @@ -0,0 +1,128 @@ +use crate::cpu::registers::Registers; +use crate::cpu::bus::Bus; +use crate::utils::addressing::{IndexRegister, AddressingMode}; + +pub fn mnemonic_arithmetic(is_16bit: bool, opcode: u8, instr_name: &str, addressing_mode: AddressingMode, registers: &Registers, bus: &Bus) -> String { + type A = AddressingMode; + match addressing_mode { + A::Accumulator => mnemonic_accumulator(opcode, instr_name), + A::Immediate => match is_16bit { + true => mnemonic_16bit_immediate(opcode, instr_name, registers, bus), + false => mnemonic_8bit_immediate(opcode, instr_name, registers, bus), + }, + A::Absolute => mnemonic_absolute(opcode, instr_name, registers, bus), + A::AbsoluteLong => mnemonic_absolute_long(opcode, instr_name, registers, bus), + A::DirectPage => mnemonic_direct_page(opcode, instr_name, registers, bus), + A::DirectPageIndirect => mnemonic_direct_page_indirect(opcode, instr_name, registers, bus), + A::DirectPageIndirectLong => mnemonic_direct_page_indirect_long(opcode, instr_name, registers, bus), + A::AbsoluteIndexed(idx) => mnemonic_absolute_indexed(opcode, instr_name, idx, registers, bus), + A::AbsoluteLongIndexed(idx) => mnemonic_absolute_long_indexed(opcode, instr_name, idx, registers, bus), + A::DirectPageIndexed(idx) => mnemonic_direct_page_indexed(opcode, instr_name, idx, registers, bus), + A::DirectPageIndexedIndirect(idx) => mnemonic_direct_page_indexed_indirect(opcode, instr_name, idx, registers, bus), + A::DirectPageIndirectLongIndexed(idx) => mnemonic_direct_page_indirect_long_indexed(opcode, instr_name, idx, registers, bus), + A::StackRelative => mnemonic_stack_relative(opcode, instr_name, registers, bus), + A::StackRelativeIndirectIndexed(idx)=> mnemonic_stack_relative_indirect_indexed(opcode, instr_name, idx, registers, bus), + _ => unreachable!(), + } +} + +pub fn mnemonic_accumulator(opcode: u8, instr_name: &str) -> String { + format!("{:02X} __ __ __ | {} A", opcode, instr_name) +} + +pub fn mnemonic_8bit_immediate(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + format!("{:02X} {:02X} __ __ | {} #${:04X}", opcode, next_byte, instr_name, next_byte) +} + +pub fn mnemonic_16bit_immediate(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + let next_second_byte = bus.read_external(registers.get_pc_address() + 2); + let word = (next_byte as u16) | ((next_byte as u16) << 8); + format!("{:02X} {:02X} {:02X} __ | {} #${:04X}", opcode, next_byte, next_second_byte, instr_name, word) +} + +pub fn mnemonic_absolute(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + let next_second_byte = bus.read_external(registers.get_pc_address() + 2); + let word = (next_byte as u16) | ((next_byte as u16) << 8); + format!("{:02X} {:02X} {:02X} __ | {} ${:04X}", opcode, next_byte, next_second_byte, instr_name, word) +} + +pub fn mnemonic_absolute_long(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + let next_second_byte = bus.read_external(registers.get_pc_address() + 2); + let next_third_byte = bus.read_external(registers.get_pc_address() + 3); + let word_long = (next_byte as u32) | ((next_second_byte as u32) << 8) | ((next_third_byte as u32) << 16); + format!("{:02X} {:02X} {:02X} {:02X} | {} ${:06X}", opcode, next_byte, next_second_byte, next_third_byte, instr_name, word_long) +} + +pub fn mnemonic_direct_page(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + format!("{:02X} {:02X} __ __ | {} ${:02X} | dp", opcode, next_byte, instr_name, next_byte) +} + +pub fn mnemonic_direct_page_indirect(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + format!("{:02X} {:02X} __ __ | {} (${:02X})", opcode, next_byte, instr_name, next_byte) +} + +pub fn mnemonic_direct_page_indirect_long(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + format!("{:02X} {:02X} __ __ | {} [${:02X}]", opcode, next_byte, instr_name, next_byte) +} + +pub fn mnemonic_absolute_indexed(opcode: u8, instr_name: &str, index: IndexRegister, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + let next_second_byte = bus.read_external(registers.get_pc_address() + 2); + let word = (next_byte as u16) | ((next_byte as u16) << 8); + format!("{:02X} {:02X} {:02X} __ | {} ${:04X}, {}", opcode, next_byte, next_second_byte, instr_name, word, index) +} + +pub fn mnemonic_absolute_long_indexed(opcode: u8, instr_name: &str, index: IndexRegister, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + let next_second_byte = bus.read_external(registers.get_pc_address() + 2); + let next_third_byte = bus.read_external(registers.get_pc_address() + 3); + let word_long = (next_byte as u32) | ((next_second_byte as u32) << 8) | ((next_third_byte as u32) << 16); + format!("{:02X} {:02X} {:02X} {:02X} | {} ${:06X}, {}", opcode, next_byte, next_second_byte, next_third_byte, instr_name, word_long, index) +} + +pub fn mnemonic_direct_page_indexed(opcode: u8, instr_name: &str, index: IndexRegister, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + format!("{:02X} {:02X} __ __ | {} ${:02X}, {} | dp", opcode, next_byte, instr_name, next_byte, index) +} + +pub fn mnemonic_direct_page_indexed_indirect(opcode: u8, instr_name: &str, index: IndexRegister, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + format!("{:02X} {:02X} __ __ | {} (${:02X}, {}) | dp", opcode, next_byte, instr_name, next_byte, index) +} + +pub fn mnemonic_direct_page_indirect_long_indexed(opcode: u8, instr_name: &str, index: IndexRegister, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + format!("{:02X} {:02X} __ __ | {} [${:02X}], {} | dp", opcode, next_byte, instr_name, next_byte, index) +} + +pub fn mnemonic_stack_relative(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + format!("{:02X} {:02X} __ __ | {} ${:02X}, S", opcode, next_byte, instr_name, next_byte) +} + +pub fn mnemonic_stack_relative_indirect_indexed(opcode: u8, instr_name: &str, index: IndexRegister, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + format!("{:02X} {:02X} __ __ | {} (${:02X}, S), {}", opcode, next_byte, instr_name, next_byte, index) +} + +pub fn mnemonic_branch_nearlabel(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String { + let nearlabel = bus.read_external(registers.get_pc_address() + 1); + format!("{:02X} {:02X} __ __ | {} ${:02X}", opcode, nearlabel, instr_name, nearlabel) +} + +pub fn mnemonic_single_byte_instr(opcode: u8, instr_name: &str) -> String { + format!("{:02X} __ __ __ | {}", opcode, instr_name) +} + +pub fn mnemonic_move(opcode: u8, instr_name: &str, registers: &Registers, bus: &Bus) -> String { + let next_byte = bus.read_external(registers.get_pc_address() + 1); + let next_second_byte = bus.read_external(registers.get_pc_address() + 2); + format!("{:02X} {:02X} {:02X} __ | {} {:02X},{:02X}", opcode, next_byte, next_second_byte, instr_name, next_second_byte, next_byte) +} diff --git a/snes-core/src/cpu/instructions/dex.rs b/snes-core/src/cpu/instructions/dex.rs new file mode 100644 index 0000000..ea6e576 --- /dev/null +++ b/snes-core/src/cpu/instructions/dex.rs @@ -0,0 +1,104 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, dec_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "DEX"; + +pub struct DEX {} + +impl DEX { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(DEX16{}), + false => Box::new(DEX8{}), + } + } +} + +impl CPUInstruction for DEX { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct DEX8 {} + +impl CPUInstruction for DEX8 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = dec_common::do_dec( + registers, + registers.x, + ) as u8; + registers.set_low_x(result); + let (bytes, cycles) = cycles::increment_cycles_inc_dec_index(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + +pub struct DEX16 {} + +impl CPUInstruction for DEX16 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = dec_common::do_dec( + registers, + registers.x, + ) as u16; + registers.x = result; + let (bytes, cycles) = cycles::increment_cycles_inc_dec_index(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.x = 0x0001; + registers.pbr = 0x00; + registers.pc = 0x0000; + let instruction = DEX8{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.x, 0); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_negative_flag()); + assert!(registers.get_zero_flag()); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.x = 0x0001; + registers.pbr = 0x00; + registers.pc = 0x0000; + let instruction = DEX16{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.x, 0); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_negative_flag()); + assert!(registers.get_zero_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/dey.rs b/snes-core/src/cpu/instructions/dey.rs new file mode 100644 index 0000000..28659fa --- /dev/null +++ b/snes-core/src/cpu/instructions/dey.rs @@ -0,0 +1,104 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, dec_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "DEY"; + +pub struct DEY {} + +impl DEY { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(DEY16{}), + false => Box::new(DEY8{}), + } + } +} + +impl CPUInstruction for DEY { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct DEY8 {} + +impl CPUInstruction for DEY8 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = dec_common::do_dec( + registers, + registers.y, + ) as u8; + registers.set_low_y(result); + let (bytes, cycles) = cycles::increment_cycles_inc_dec_index(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + +pub struct DEY16 {} + +impl CPUInstruction for DEY16 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = dec_common::do_dec( + registers, + registers.y, + ) as u16; + registers.y = result; + let (bytes, cycles) = cycles::increment_cycles_inc_dec_index(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.y = 0x0001; + registers.pbr = 0x00; + registers.pc = 0x0000; + let instruction = DEY8{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.y, 0); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_negative_flag()); + assert!(registers.get_zero_flag()); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.y = 0x0001; + registers.pbr = 0x00; + registers.pc = 0x0000; + let instruction = DEY16{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.y, 0); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_negative_flag()); + assert!(registers.get_zero_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/eor.rs b/snes-core/src/cpu/instructions/eor.rs new file mode 100644 index 0000000..94023a8 --- /dev/null +++ b/snes-core/src/cpu/instructions/eor.rs @@ -0,0 +1,113 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}}; + +use crate::cpu::cycles; +use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "EOR"; + +pub struct EOR { + pub addressing_mode: AddressingMode, +} + +impl EOR { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(EOR16{addressing_mode: self.addressing_mode}), + false => Box::new(EOR8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for EOR { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct EOR8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for EOR8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_8bit_from_address(registers, bus, self.addressing_mode); + let (result, affected_flags) = alu::eor(registers.a as u8, value); + registers.set_low_a(result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_bitwise(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct EOR16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for EOR16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_16bit_from_address(registers, bus, self.addressing_mode); + let (result, affected_flags) = alu::eor(registers.a, value); + registers.a = result; + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_bitwise(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.a = 0x0F; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_16bit_mode(false); + bus.write(0x000001, 0xF0); + let instruction = EOR8{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0xFF); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } + + #[test] + fn test_and_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.a = 0x0FFF; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_16bit_mode(false); + bus.write(0x000002, 0xF0); + bus.write(0x000001, 0x00); + let instruction = EOR16{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0xFFFF); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/inc.rs b/snes-core/src/cpu/instructions/inc.rs new file mode 100644 index 0000000..75cc0c7 --- /dev/null +++ b/snes-core/src/cpu/instructions/inc.rs @@ -0,0 +1,112 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::addressing::AddressingMode}; + +use crate::cpu::cycles; +use super::{CPUInstruction, dec_common, read_write_common::{read_8bit_from_address, write_8bit_to_address, read_16bit_from_address, write_16bit_to_address}}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "INC"; + +pub struct INC { + pub addressing_mode: AddressingMode, +} + +impl INC { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(INC16{addressing_mode: self.addressing_mode}), + false => Box::new(INC8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for INC { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct INC8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for INC8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let result = dec_common::do_inc( + registers, + read_8bit_from_address(registers, bus, self.addressing_mode), + ) as u8; + write_8bit_to_address(registers, bus, self.addressing_mode, result); + let (bytes, cycles) = cycles::increment_cycles_inc_dec(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct INC16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for INC16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let result = dec_common::do_inc( + registers, + read_16bit_from_address(registers, bus, self.addressing_mode), + ) as u16; + write_16bit_to_address(registers, bus, self.addressing_mode, result); + let (bytes, cycles) = cycles::increment_cycles_inc_dec(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.a = 0x0001; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(true); + let instruction = INC8{addressing_mode: AddressingMode::Accumulator}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 2); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.a = 0x0001; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(false); + let instruction = INC16{addressing_mode: AddressingMode::Accumulator}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 2); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/inx.rs b/snes-core/src/cpu/instructions/inx.rs new file mode 100644 index 0000000..d5f2082 --- /dev/null +++ b/snes-core/src/cpu/instructions/inx.rs @@ -0,0 +1,104 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, dec_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "INX"; + +pub struct INX {} + +impl INX { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(INX16{}), + false => Box::new(INX8{}), + } + } +} + +impl CPUInstruction for INX { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct INX8 {} + +impl CPUInstruction for INX8 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = dec_common::do_inc( + registers, + registers.x, + ) as u8; + registers.set_low_x(result); + let (bytes, cycles) = cycles::increment_cycles_inc_dec_index(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + +pub struct INX16 {} + +impl CPUInstruction for INX16 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = dec_common::do_inc( + registers, + registers.x, + ) as u16; + registers.x = result; + let (bytes, cycles) = cycles::increment_cycles_inc_dec_index(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.x = 0x0001; + registers.pbr = 0x00; + registers.pc = 0x0000; + let instruction = INX8{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.x, 2); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.x = 0x0001; + registers.pbr = 0x00; + registers.pc = 0x0000; + let instruction = INX16{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.x, 2); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/iny.rs b/snes-core/src/cpu/instructions/iny.rs new file mode 100644 index 0000000..7b24dd6 --- /dev/null +++ b/snes-core/src/cpu/instructions/iny.rs @@ -0,0 +1,104 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, dec_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "INY"; + +pub struct INY {} + +impl INY { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(INY16{}), + false => Box::new(INY8{}), + } + } +} + +impl CPUInstruction for INY { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct INY8 {} + +impl CPUInstruction for INY8 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = dec_common::do_inc( + registers, + registers.y, + ) as u8; + registers.set_low_y(result); + let (bytes, cycles) = cycles::increment_cycles_inc_dec_index(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + +pub struct INY16 {} + +impl CPUInstruction for INY16 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = dec_common::do_inc( + registers, + registers.y, + ) as u16; + registers.y = result; + let (bytes, cycles) = cycles::increment_cycles_inc_dec_index(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.y = 0x0001; + registers.pbr = 0x00; + registers.pc = 0x0000; + let instruction = INY8{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.y, 2); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.y = 0x0001; + registers.pbr = 0x00; + registers.pc = 0x0000; + let instruction = INY16{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.y, 2); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/jmp.rs b/snes-core/src/cpu/instructions/jmp.rs new file mode 100644 index 0000000..ff9e3c8 --- /dev/null +++ b/snes-core/src/cpu/instructions/jmp.rs @@ -0,0 +1,67 @@ +use crate::cpu::{bus::Bus, registers::Registers}; +use crate::utils::addressing::AddressingMode; + +use super::read_write_common::get_effective_address; +use super::CPUInstruction; +use super::decoder_common; +use crate::cpu::cycles; + +static INSTR_NAME: &'static str = "JMP"; + +pub struct JMP { + pub addressing_mode: AddressingMode, +} + +impl CPUInstruction for JMP { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let effective_address = get_effective_address(®isters, bus, self.addressing_mode); + let is_long = match self.addressing_mode { + AddressingMode::AbsoluteLong | + AddressingMode::AbsoluteIndirectLong => true, + _ => false, + }; + registers.pc = effective_address as u16; + if is_long { + registers.pbr = (effective_address >> 16) as u8; + } + let (bytes, cycles) = cycles::increment_cycles_jmp(self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + bus.write(0x000002, 0xAA); + bus.write(0x000001, 0xBB); + let instruction = JMP{addressing_mode: AddressingMode::Absolute}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0xAABB); + assert_eq!(registers.cycles, 3); + + // Test a long address + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.pbr = 0x00; + bus.write(0x000003, 0xAA); + bus.write(0x000002, 0xBB); + bus.write(0x000001, 0xCC); + let instruction = JMP{addressing_mode: AddressingMode::AbsoluteLong}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pbr, 0xAA); + assert_eq!(registers.pc, 0xBBCC); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/jsr.rs b/snes-core/src/cpu/instructions/jsr.rs new file mode 100644 index 0000000..b87f061 --- /dev/null +++ b/snes-core/src/cpu/instructions/jsr.rs @@ -0,0 +1,77 @@ +use crate::cpu::{bus::Bus, registers::Registers}; +use crate::utils::addressing::AddressingMode; + +use super::read_write_common::get_effective_address; +use super::{CPUInstruction, push_common}; +use super::decoder_common; +use crate::cpu::cycles; + +static INSTR_NAME: &'static str = "JSR"; + +pub struct JSR { + pub addressing_mode: AddressingMode, +} + +impl CPUInstruction for JSR { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let effective_address = get_effective_address(registers, bus, self.addressing_mode); + let is_long = match self.addressing_mode { + AddressingMode::AbsoluteLong | + AddressingMode::AbsoluteIndirectLong => true, + _ => false, + }; + // We need to push the *next* instruction onto the stack + let (bytes, cycles) = cycles::increment_cycles_jsr(self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + let value = registers.get_pc_address(); + if is_long { + push_common::do_push(registers, bus, &[ + (value >> 16) as u8, + (value >> 8) as u8, + value as u8, + ]); + } else { + push_common::do_push(registers, bus, &[ + (value >> 8) as u8, + value as u8, + ]); + } + registers.pc = effective_address as u16; + if is_long { + registers.pbr = (effective_address >> 16) as u8; + } + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + // Test a long address + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x1234; + registers.pbr = 0x00; + registers.sp = 0x1FC; + bus.write(registers.get_pc_address() + 3, 0xAA); + bus.write(registers.get_pc_address() + 2, 0xBB); + bus.write(registers.get_pc_address() + 1, 0xCC); + // write next instruction + let instruction = JSR{addressing_mode: AddressingMode::AbsoluteLong}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x1FC), 0x00); + assert_eq!(bus.read(0x1FB), 0x12); + assert_eq!(bus.read(0x1FA), 0x38); // we should store the NEXT instruction + assert_eq!(registers.pbr, 0xAA); + assert_eq!(registers.pc, 0xBBCC); + assert_eq!(registers.sp, 0x1F9); + assert_eq!(registers.cycles, 8); + } +} diff --git a/snes-core/src/cpu/instructions/lda.rs b/snes-core/src/cpu/instructions/lda.rs new file mode 100644 index 0000000..99e04c5 --- /dev/null +++ b/snes-core/src/cpu/instructions/lda.rs @@ -0,0 +1,126 @@ +use crate::common::flags::Flags; +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; +use crate::utils::addressing::AddressingMode; + +use super::read_write_common::{read_8bit_from_address, read_16bit_from_address}; +use super::{CPUInstruction, bit_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "LDA"; + +pub struct LDA { + pub addressing_mode: AddressingMode, +} + +impl LDA { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(LDA16{addressing_mode: self.addressing_mode}), + false => Box::new(LDA8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for LDA { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct LDA8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for LDA8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_8bit_from_address(registers, bus, self.addressing_mode); + registers.set_flags(&[ + Flags::Negative(value >> 7 == 1), + Flags::Zero(value == 0), + ]); + registers.set_low_a(value); + bit_common::do_bit(registers, registers.a as u8, value, self.addressing_mode); + let (bytes, cycles) = cycles::increment_cycles_lda(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct LDA16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for LDA16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_16bit_from_address(registers, bus, self.addressing_mode); + registers.a = value; + registers.set_flags(&[ + Flags::Negative(value >> 15 == 1), + Flags::Zero(value == 0), + ]); + let (bytes, cycles) = cycles::increment_cycles_lda(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.a = 0x0000; + registers.pc = 0x0000; + registers.pbr = 0x00; + registers.set_negative_flag(false); + registers.set_zero_flag(true); + registers.set_16bit_mode(false); + bus.write(0x0001, 0xFF); + let instruction = LDA8{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x0002); + assert_eq!(registers.a, 0x00FF); + assert_eq!(registers.cycles, 2); + assert!(registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.a = 0x0000; + registers.pc = 0x0000; + registers.pbr = 0x00; + registers.emulation_mode = false; + registers.set_negative_flag(false); + registers.set_zero_flag(true); + registers.set_16bit_mode(true); + bus.write(0x0001, 0xFF); + bus.write(0x0002, 0xFF); + let instruction = LDA16{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x0003); + assert_eq!(registers.a, 0xFFFF); + assert_eq!(registers.cycles, 3); + assert!(registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/ldx.rs b/snes-core/src/cpu/instructions/ldx.rs new file mode 100644 index 0000000..886d6a7 --- /dev/null +++ b/snes-core/src/cpu/instructions/ldx.rs @@ -0,0 +1,126 @@ +use crate::common::flags::Flags; +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; +use crate::utils::addressing::AddressingMode; + +use super::read_write_common::{read_8bit_from_address, read_16bit_from_address}; +use super::{CPUInstruction, bit_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "LDX"; + +pub struct LDX { + pub addressing_mode: AddressingMode, +} + +impl LDX { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(LDX16{addressing_mode: self.addressing_mode}), + false => Box::new(LDX8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for LDX { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct LDX8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for LDX8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_8bit_from_address(registers, bus, self.addressing_mode); + registers.set_flags(&[ + Flags::Negative(value >> 7 == 1), + Flags::Zero(value == 0), + ]); + registers.set_low_x(value); + bit_common::do_bit(registers, registers.x as u8, value, self.addressing_mode); + let (bytes, cycles) = cycles::increment_cycles_ld_index(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct LDX16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for LDX16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_16bit_from_address(registers, bus, self.addressing_mode); + registers.x = value; + registers.set_flags(&[ + Flags::Negative(value >> 15 == 1), + Flags::Zero(value == 0), + ]); + let (bytes, cycles) = cycles::increment_cycles_ld_index(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.x = 0x0000; + registers.pc = 0x0000; + registers.pbr = 0x00; + registers.set_negative_flag(false); + registers.set_zero_flag(true); + registers.set_16bit_index(false); + bus.write(0x0001, 0xFF); + let instruction = LDX8{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x0002); + assert_eq!(registers.x, 0x00FF); + assert_eq!(registers.cycles, 2); + assert!(registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.x = 0x0000; + registers.pc = 0x0000; + registers.pbr = 0x00; + registers.emulation_mode = false; + registers.set_negative_flag(false); + registers.set_zero_flag(true); + registers.set_16bit_index(true); + bus.write(0x0001, 0xFF); + bus.write(0x0002, 0xFF); + let instruction = LDX16{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x0003); + assert_eq!(registers.x, 0xFFFF); + assert_eq!(registers.cycles, 3); + assert!(registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/ldy.rs b/snes-core/src/cpu/instructions/ldy.rs new file mode 100644 index 0000000..5c664fd --- /dev/null +++ b/snes-core/src/cpu/instructions/ldy.rs @@ -0,0 +1,126 @@ +use crate::common::flags::Flags; +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; +use crate::utils::addressing::AddressingMode; + +use super::read_write_common::{read_8bit_from_address, read_16bit_from_address}; +use super::{CPUInstruction, bit_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "LDY"; + +pub struct LDY { + pub addressing_mode: AddressingMode, +} + +impl LDY { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(LDY16{addressing_mode: self.addressing_mode}), + false => Box::new(LDY8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for LDY { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct LDY8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for LDY8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_8bit_from_address(registers, bus, self.addressing_mode); + registers.set_flags(&[ + Flags::Negative(value >> 7 == 1), + Flags::Zero(value == 0), + ]); + registers.set_low_y(value); + bit_common::do_bit(registers, registers.y as u8, value, self.addressing_mode); + let (bytes, cycles) = cycles::increment_cycles_ld_index(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct LDY16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for LDY16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_16bit_from_address(registers, bus, self.addressing_mode); + registers.y = value; + registers.set_flags(&[ + Flags::Negative(value >> 15 == 1), + Flags::Zero(value == 0), + ]); + let (bytes, cycles) = cycles::increment_cycles_ld_index(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.y = 0x0000; + registers.pc = 0x0000; + registers.pbr = 0x00; + registers.set_negative_flag(false); + registers.set_zero_flag(true); + registers.set_16bit_index(false); + bus.write(0x0001, 0xFF); + let instruction = LDY8{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x0002); + assert_eq!(registers.y, 0x00FF); + assert_eq!(registers.cycles, 2); + assert!(registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.y = 0x0000; + registers.pc = 0x0000; + registers.pbr = 0x00; + registers.emulation_mode = false; + registers.set_negative_flag(false); + registers.set_zero_flag(true); + registers.set_16bit_index(true); + bus.write(0x0001, 0xFF); + bus.write(0x0002, 0xFF); + let instruction = LDY16{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x0003); + assert_eq!(registers.y, 0xFFFF); + assert_eq!(registers.cycles, 3); + assert!(registers.get_negative_flag()); + assert!(!registers.get_zero_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/lsr.rs b/snes-core/src/cpu/instructions/lsr.rs new file mode 100644 index 0000000..b512c70 --- /dev/null +++ b/snes-core/src/cpu/instructions/lsr.rs @@ -0,0 +1,152 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}}; + +use crate::cpu::cycles; +use super::{CPUInstruction, read_write_common::{write_8bit_to_address, read_8bit_from_address, read_16bit_from_address, write_16bit_to_address}}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "LSR"; + +pub struct LSR { + pub addressing_mode: AddressingMode, +} + +impl LSR { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(LSR16{addressing_mode: self.addressing_mode}), + false => Box::new(LSR8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for LSR { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct LSR8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for LSR8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let (result, affected_flags) = alu::lsr(read_8bit_from_address(registers, bus, self.addressing_mode)); + write_8bit_to_address(registers, bus, self.addressing_mode, result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_shift(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct LSR16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for LSR16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let (result, affected_flags) = alu::lsr(read_16bit_from_address(registers, bus, self.addressing_mode)); + write_16bit_to_address(registers, bus, self.addressing_mode, result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_shift(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0b00000000_00000011; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_16bit_mode(false); + registers.set_negative_flag(true); + registers.set_carry_flag(false); + let instruction = LSR8{addressing_mode: AddressingMode::Accumulator}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0b00000000_00000001); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(registers.get_carry_flag()); + assert!(!registers.get_zero_flag()); + assert!(!registers.get_negative_flag()); + + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0b00000001_10000011; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_16bit_mode(false); + registers.set_negative_flag(true); + registers.set_carry_flag(false); + let instruction = LSR8{addressing_mode: AddressingMode::Accumulator}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0b00000001_01000001); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + assert!(registers.get_carry_flag()); + assert!(!registers.get_zero_flag()); + assert!(!registers.get_negative_flag()); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0b00000000_00000011; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_16bit_mode(true); + registers.set_negative_flag(true); + registers.set_carry_flag(false); + let instruction = LSR16{addressing_mode: AddressingMode::Accumulator}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0b00000000_00000001); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 4); + assert!(registers.get_carry_flag()); + assert!(!registers.get_zero_flag()); + assert!(!registers.get_negative_flag()); + + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0b10000000_00000011; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_16bit_mode(true); + registers.set_negative_flag(true); + registers.set_carry_flag(true); + let instruction = LSR16{addressing_mode: AddressingMode::Accumulator}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0b01000000_00000001); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 4); + assert!(registers.get_carry_flag()); + assert!(!registers.get_zero_flag()); + assert!(!registers.get_negative_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/mapper.rs b/snes-core/src/cpu/instructions/mapper.rs new file mode 100644 index 0000000..89ed58f --- /dev/null +++ b/snes-core/src/cpu/instructions/mapper.rs @@ -0,0 +1,356 @@ +use crate::{utils::addressing::{AddressingMode, IndexRegister}, cpu::instructions::{adc::ADC, and::AND, asl::ASL, bcc::BCC, bcs::BCS, beq::BEQ, bne::BNE, bmi::BMI, bpl::BPL, bra::BRA, brk::BRK, brl::BRL, bvc::BVC, bvs::BVS, bit::BIT, clc::CLC, cld::CLD, cli::CLI, clv::CLV, cmp::CMP, cop::COP, cpx::CPX, cpy::CPY, dec::DEC, dex::DEX, dey::DEY, eor::EOR, inc::INC, iny::INY, inx::INX, jmp::JMP, jsr::JSR, lda::LDA, ldx::LDX, ldy::LDY, lsr::LSR, mvn::MVN, mvp::MVP, nop::NOP, ora::ORA, pea::PEA, pei::PEI, per::PER, pha::PHA, phb::PHB, phd::PHD, phk::PHK, php::PHP, phx::PHX, phy::PHY, pla::PLA, plb::PLB, pld::PLD, plp::PLP, plx::PLX, ply::PLY, rep::REP, rol::ROL, ror::ROR, rti::RTI, rtl::RTL, rts::RTS, sbc::SBC, sec::SEC, sed::SED, sei::SEI, sep::SEP, sta::STA, stp::STP, stx::STX, sty::STY, stz::STZ, tax::TAX, tay::TAY, tcd::TCD, tcs::TCS, trb::TRB, tsb::TSB, tdc::TDC, tsc::TSC, tsx::TSX, txa::TXA, txs::TXS, txy::TXY, tya::TYA, tyx::TYX, wai::WAI, wdm::WDM, xba::XBA, xce::XCE}}; + +use super::CPUInstruction; + +pub fn map_opcode_to_instruction(opcode: u8) -> Box { + type A = AddressingMode; + type I = IndexRegister; + match opcode { + // ADC + 0x69 => Box::new(ADC{addressing_mode: A::Immediate}), + 0x6D => Box::new(ADC{addressing_mode: A::Absolute}), + 0x6F => Box::new(ADC{addressing_mode: A::AbsoluteLong}), + 0x65 => Box::new(ADC{addressing_mode: A::DirectPage}), + 0x72 => Box::new(ADC{addressing_mode: A::DirectPageIndirect}), + 0x67 => Box::new(ADC{addressing_mode: A::DirectPageIndirectLong}), + 0x7D => Box::new(ADC{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0x7F => Box::new(ADC{addressing_mode: A::AbsoluteLongIndexed(I::X)}), + 0x79 => Box::new(ADC{addressing_mode: A::AbsoluteIndexed(I::Y)}), + 0x75 => Box::new(ADC{addressing_mode: A::DirectPageIndexed(I::X)}), + 0x61 => Box::new(ADC{addressing_mode: A::DirectPageIndexedIndirect(I::X)}), + 0x71 => Box::new(ADC{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}), + 0x77 => Box::new(ADC{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}), + 0x63 => Box::new(ADC{addressing_mode: A::StackRelative}), + 0x73 => Box::new(ADC{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}), + // AND + 0x29 => Box::new(AND{addressing_mode: A::Immediate}), + 0x2D => Box::new(AND{addressing_mode: A::Absolute}), + 0x2F => Box::new(AND{addressing_mode: A::AbsoluteLong}), + 0x25 => Box::new(AND{addressing_mode: A::DirectPage}), + 0x32 => Box::new(AND{addressing_mode: A::DirectPageIndirect}), + 0x27 => Box::new(AND{addressing_mode: A::DirectPageIndirectLong}), + 0x3D => Box::new(AND{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0x3F => Box::new(AND{addressing_mode: A::AbsoluteLongIndexed(I::X)}), + 0x39 => Box::new(AND{addressing_mode: A::AbsoluteIndexed(I::Y)}), + 0x35 => Box::new(AND{addressing_mode: A::DirectPageIndexed(I::X)}), + 0x21 => Box::new(AND{addressing_mode: A::DirectPageIndexedIndirect(I::X)}), + 0x31 => Box::new(AND{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}), + 0x37 => Box::new(AND{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}), + 0x23 => Box::new(AND{addressing_mode: A::StackRelative}), + 0x33 => Box::new(AND{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}), + // ASL + 0x0A => Box::new(ASL{addressing_mode: A::Accumulator}), + 0x0E => Box::new(ASL{addressing_mode: A::Absolute}), + 0x06 => Box::new(ASL{addressing_mode: A::DirectPage}), + 0x1E => Box::new(ASL{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0x16 => Box::new(ASL{addressing_mode: A::DirectPageIndexed(I::X)}), + // BCC + 0x90 => Box::new(BCC{}), + // BCS + 0xB0 => Box::new(BCS{}), + // BEQ + 0xF0 => Box::new(BEQ{}), + // BNE + 0xD0 => Box::new(BNE{}), + // BMI + 0x30 => Box::new(BMI{}), + // BPL + 0x10 => Box::new(BPL{}), + // BRA + 0x80 => Box::new(BRA{}), + // BRK + 0x00 => Box::new(BRK{}), + // BRL + 0x82 => Box::new(BRL{}), + // BVC + 0x50 => Box::new(BVC{}), + // BVS + 0x70 => Box::new(BVS{}), + // BIT + 0x89 => Box::new(BIT{addressing_mode: A::Immediate}), + 0x2C => Box::new(BIT{addressing_mode: A::Absolute}), + 0x24 => Box::new(BIT{addressing_mode: A::DirectPage}), + 0x3C => Box::new(BIT{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0x34 => Box::new(BIT{addressing_mode: A::DirectPageIndexed(I::X)}), + // CLC + 0x18 => Box::new(CLC{}), + // CLD + 0xD8 => Box::new(CLD{}), + // CLI + 0x58 => Box::new(CLI{}), + // CLV + 0xB8 => Box::new(CLV{}), + // CMP + 0xC9 => Box::new(CMP{addressing_mode: A::Immediate}), + 0xCD => Box::new(CMP{addressing_mode: A::Absolute}), + 0xCF => Box::new(CMP{addressing_mode: A::AbsoluteLong}), + 0xC5 => Box::new(CMP{addressing_mode: A::DirectPage}), + 0xD2 => Box::new(CMP{addressing_mode: A::DirectPageIndirect}), + 0xC7 => Box::new(CMP{addressing_mode: A::DirectPageIndirectLong}), + 0xDD => Box::new(CMP{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0xDF => Box::new(CMP{addressing_mode: A::AbsoluteLongIndexed(I::X)}), + 0xD9 => Box::new(CMP{addressing_mode: A::AbsoluteIndexed(I::Y)}), + 0xD5 => Box::new(CMP{addressing_mode: A::DirectPageIndexed(I::X)}), + 0xC1 => Box::new(CMP{addressing_mode: A::DirectPageIndexedIndirect(I::X)}), + 0xD1 => Box::new(CMP{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}), + 0xD7 => Box::new(CMP{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}), + 0xC3 => Box::new(CMP{addressing_mode: A::StackRelative}), + 0xD3 => Box::new(CMP{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}), + // COP + 0x02 => Box::new(COP{}), + // CPX + 0xE0 => Box::new(CPX{addressing_mode: A::Immediate}), + 0xEC => Box::new(CPX{addressing_mode: A::Absolute}), + 0xE4 => Box::new(CPX{addressing_mode: A::DirectPage}), + // CPY + 0xC0 => Box::new(CPY{addressing_mode: A::Immediate}), + 0xCC => Box::new(CPY{addressing_mode: A::Absolute}), + 0xC4 => Box::new(CPY{addressing_mode: A::DirectPage}), + // DEC + 0x3A => Box::new(DEC{addressing_mode: A::Accumulator}), + 0xCE => Box::new(DEC{addressing_mode: A::Absolute}), + 0xC6 => Box::new(DEC{addressing_mode: A::DirectPage}), + 0xDE => Box::new(DEC{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0xD6 => Box::new(DEC{addressing_mode: A::DirectPageIndexed(I::X)}), + // DEX + 0xCA => Box::new(DEX{}), + // DEY + 0x88 => Box::new(DEY{}), + // EOR + 0x49 => Box::new(EOR{addressing_mode: A::Immediate}), + 0x4D => Box::new(EOR{addressing_mode: A::Absolute}), + 0x4F => Box::new(EOR{addressing_mode: A::AbsoluteLong}), + 0x45 => Box::new(EOR{addressing_mode: A::DirectPage}), + 0x52 => Box::new(EOR{addressing_mode: A::DirectPageIndirect}), + 0x47 => Box::new(EOR{addressing_mode: A::DirectPageIndirectLong}), + 0x5D => Box::new(EOR{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0x5F => Box::new(EOR{addressing_mode: A::AbsoluteLongIndexed(I::X)}), + 0x59 => Box::new(EOR{addressing_mode: A::AbsoluteIndexed(I::Y)}), + 0x55 => Box::new(EOR{addressing_mode: A::DirectPageIndexed(I::X)}), + 0x41 => Box::new(EOR{addressing_mode: A::DirectPageIndexedIndirect(I::X)}), + 0x51 => Box::new(EOR{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}), + 0x57 => Box::new(EOR{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}), + 0x43 => Box::new(EOR{addressing_mode: A::StackRelative}), + 0x53 => Box::new(EOR{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}), + // INC + 0x1A => Box::new(INC{addressing_mode: A::Accumulator}), + 0xEE => Box::new(INC{addressing_mode: A::Absolute}), + 0xE6 => Box::new(INC{addressing_mode: A::DirectPage}), + 0xFE => Box::new(INC{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0xF6 => Box::new(INC{addressing_mode: A::DirectPageIndexed(I::X)}), + // INX + 0xE8 => Box::new(INX{}), + // INY + 0xC8 => Box::new(INY{}), + // JMP + 0x4C => Box::new(JMP{addressing_mode: A::Absolute}), + 0x6C => Box::new(JMP{addressing_mode: A::AbsoluteIndirect}), + 0x7C => Box::new(JMP{addressing_mode: A::AbsoluteIndexedIndirect(I::X)}), + 0x5C => Box::new(JMP{addressing_mode: A::AbsoluteLong}), + 0xDC => Box::new(JMP{addressing_mode: A::AbsoluteIndirectLong}), + // JSR + 0x20 => Box::new(JSR{addressing_mode: A::Absolute}), + 0xFC => Box::new(JSR{addressing_mode: A::AbsoluteIndexedIndirect(I::X)}), + 0x22 => Box::new(JSR{addressing_mode: A::AbsoluteLong}), // same as JSL + // LDA + 0xA9 => Box::new(LDA{addressing_mode: A::Immediate}), + 0xAD => Box::new(LDA{addressing_mode: A::Absolute}), + 0xAF => Box::new(LDA{addressing_mode: A::AbsoluteLong}), + 0xA5 => Box::new(LDA{addressing_mode: A::DirectPage}), + 0xB2 => Box::new(LDA{addressing_mode: A::DirectPageIndirect}), + 0xA7 => Box::new(LDA{addressing_mode: A::DirectPageIndirectLong}), + 0xBD => Box::new(LDA{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0xBF => Box::new(LDA{addressing_mode: A::AbsoluteLongIndexed(I::X)}), + 0xB9 => Box::new(LDA{addressing_mode: A::AbsoluteIndexed(I::Y)}), + 0xB5 => Box::new(LDA{addressing_mode: A::DirectPageIndexed(I::X)}), + 0xA1 => Box::new(LDA{addressing_mode: A::DirectPageIndexedIndirect(I::X)}), + 0xB1 => Box::new(LDA{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}), + 0xB7 => Box::new(LDA{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}), + 0xA3 => Box::new(LDA{addressing_mode: A::StackRelative}), + 0xB3 => Box::new(LDA{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}), + // LDX + 0xA2 => Box::new(LDX{addressing_mode: A::Immediate}), + 0xAE => Box::new(LDX{addressing_mode: A::Absolute}), + 0xA6 => Box::new(LDX{addressing_mode: A::DirectPage}), + 0xBE => Box::new(LDX{addressing_mode: A::AbsoluteIndexed(I::Y)}), + 0xB6 => Box::new(LDX{addressing_mode: A::DirectPageIndexed(I::Y)}), + // LDY + 0xA0 => Box::new(LDY{addressing_mode: A::Immediate}), + 0xAC => Box::new(LDY{addressing_mode: A::Absolute}), + 0xA4 => Box::new(LDY{addressing_mode: A::DirectPage}), + 0xB4 => Box::new(LDY{addressing_mode: A::AbsoluteIndexed(I::Y)}), + 0xBC => Box::new(LDY{addressing_mode: A::DirectPageIndexed(I::Y)}), + // LSR + 0x4A => Box::new(LSR{addressing_mode: A::Accumulator}), + 0x4E => Box::new(LSR{addressing_mode: A::Absolute}), + 0x46 => Box::new(LSR{addressing_mode: A::DirectPage}), + 0x5E => Box::new(LSR{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0x56 => Box::new(LSR{addressing_mode: A::DirectPageIndexed(I::X)}), + // MVN + 0x54 => Box::new(MVN{}), + // MVP + 0x44 => Box::new(MVP{}), + // NOP + 0xEA => Box::new(NOP{}), + // ORA + 0x09 => Box::new(ORA{addressing_mode: A::Immediate}), + 0x0D => Box::new(ORA{addressing_mode: A::Absolute}), + 0x0F => Box::new(ORA{addressing_mode: A::AbsoluteLong}), + 0x05 => Box::new(ORA{addressing_mode: A::DirectPage}), + 0x12 => Box::new(ORA{addressing_mode: A::DirectPageIndirect}), + 0x07 => Box::new(ORA{addressing_mode: A::DirectPageIndirectLong}), + 0x1D => Box::new(ORA{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0x1F => Box::new(ORA{addressing_mode: A::AbsoluteLongIndexed(I::X)}), + 0x19 => Box::new(ORA{addressing_mode: A::AbsoluteIndexed(I::Y)}), + 0x15 => Box::new(ORA{addressing_mode: A::DirectPageIndexed(I::X)}), + 0x01 => Box::new(ORA{addressing_mode: A::DirectPageIndexedIndirect(I::X)}), + 0x11 => Box::new(ORA{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}), + 0x17 => Box::new(ORA{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}), + 0x03 => Box::new(ORA{addressing_mode: A::StackRelative}), + 0x13 => Box::new(ORA{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}), + // PEA + 0xF4 => Box::new(PEA{}), + // PEI + 0xD4 => Box::new(PEI{}), + // PER + 0x62 => Box::new(PER{}), + // PHA + 0x48 => Box::new(PHA{}), + // PHB + 0x8B => Box::new(PHB{}), + // PHD + 0x0B => Box::new(PHD{}), + // PHK + 0x4B => Box::new(PHK{}), + // PHP + 0x08 => Box::new(PHP{}), + // PHX + 0xDA => Box::new(PHX{}), + // PHY + 0x5A => Box::new(PHY{}), + // PLA + 0x68 => Box::new(PLA{}), + // PLB + 0xAB => Box::new(PLB{}), + // PLD + 0x2B => Box::new(PLD{}), + // PLP + 0x28 => Box::new(PLP{}), + // PLX + 0xFA => Box::new(PLX{}), + // PLY + 0x7A => Box::new(PLY{}), + // REP + 0xC2 => Box::new(REP{}), + // ROL + 0x2A => Box::new(ROL{addressing_mode: AddressingMode::Accumulator}), + 0x2E => Box::new(ROL{addressing_mode: AddressingMode::Absolute}), + 0x26 => Box::new(ROL{addressing_mode: AddressingMode::DirectPage}), + 0x3E => Box::new(ROL{addressing_mode: AddressingMode::AbsoluteIndexed(I::X)}), + 0x36 => Box::new(ROL{addressing_mode: AddressingMode::DirectPageIndexed(I::X)}), + // ROR + 0x6A => Box::new(ROR{addressing_mode: AddressingMode::Accumulator}), + 0x6E => Box::new(ROR{addressing_mode: AddressingMode::Absolute}), + 0x66 => Box::new(ROR{addressing_mode: AddressingMode::DirectPage}), + 0x7E => Box::new(ROR{addressing_mode: AddressingMode::AbsoluteIndexed(I::X)}), + 0x76 => Box::new(ROR{addressing_mode: AddressingMode::DirectPageIndexed(I::X)}), + // RTI + 0x40 => Box::new(RTI{}), + // RTL + 0x6B => Box::new(RTL{}), + // RTS + 0x60 => Box::new(RTS{}), + // SBC + 0xE9 => Box::new(SBC{addressing_mode: A::Immediate}), + 0xED => Box::new(SBC{addressing_mode: A::Absolute}), + 0xEF => Box::new(SBC{addressing_mode: A::AbsoluteLong}), + 0xE5 => Box::new(SBC{addressing_mode: A::DirectPage}), + 0xF2 => Box::new(SBC{addressing_mode: A::DirectPageIndirect}), + 0xE7 => Box::new(SBC{addressing_mode: A::DirectPageIndirectLong}), + 0xFD => Box::new(SBC{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0xFF => Box::new(SBC{addressing_mode: A::AbsoluteLongIndexed(I::X)}), + 0xF9 => Box::new(SBC{addressing_mode: A::AbsoluteIndexed(I::Y)}), + 0xF5 => Box::new(SBC{addressing_mode: A::DirectPageIndexed(I::X)}), + 0xE1 => Box::new(SBC{addressing_mode: A::DirectPageIndexedIndirect(I::X)}), + 0xF1 => Box::new(SBC{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}), + 0xF7 => Box::new(SBC{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}), + 0xE3 => Box::new(SBC{addressing_mode: A::StackRelative}), + 0xF3 => Box::new(SBC{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}), + // SEC + 0x38 => Box::new(SEC{}), + // SED + 0xF8 => Box::new(SED{}), + // SEI + 0x78 => Box::new(SEI{}), + // SEP + 0xE2 => Box::new(SEP{}), + // STA + 0x8D => Box::new(STA{addressing_mode: A::Absolute}), + 0x8F => Box::new(STA{addressing_mode: A::AbsoluteLong}), + 0x85 => Box::new(STA{addressing_mode: A::DirectPage}), + 0x92 => Box::new(STA{addressing_mode: A::DirectPageIndirect}), + 0x87 => Box::new(STA{addressing_mode: A::DirectPageIndirectLong}), + 0x9D => Box::new(STA{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0x9F => Box::new(STA{addressing_mode: A::AbsoluteLongIndexed(I::X)}), + 0x99 => Box::new(STA{addressing_mode: A::AbsoluteIndexed(I::Y)}), + 0x95 => Box::new(STA{addressing_mode: A::DirectPageIndexed(I::X)}), + 0x81 => Box::new(STA{addressing_mode: A::DirectPageIndexedIndirect(I::X)}), + 0x91 => Box::new(STA{addressing_mode: A::DirectPageIndirectIndexed(I::Y)}), + 0x97 => Box::new(STA{addressing_mode: A::DirectPageIndirectLongIndexed(I::Y)}), + 0x83 => Box::new(STA{addressing_mode: A::StackRelative}), + 0x93 => Box::new(STA{addressing_mode: A::StackRelativeIndirectIndexed(I::Y)}), + // STP + 0xDB => Box::new(STP{}), + // STX + 0x8E => Box::new(STX{addressing_mode: A::Absolute}), + 0x86 => Box::new(STX{addressing_mode: A::DirectPage}), + 0x96 => Box::new(STX{addressing_mode: A::DirectPageIndexed(I::Y)}), + // STY + 0x8C => Box::new(STY{addressing_mode: A::Absolute}), + 0x84 => Box::new(STY{addressing_mode: A::DirectPage}), + 0x94 => Box::new(STY{addressing_mode: A::DirectPageIndexed(I::X)}), + // STZ + 0x9C => Box::new(STZ{addressing_mode: A::Absolute}), + 0x64 => Box::new(STZ{addressing_mode: A::DirectPage}), + 0x9E => Box::new(STZ{addressing_mode: A::AbsoluteIndexed(I::X)}), + 0x74 => Box::new(STZ{addressing_mode: A::DirectPageIndexed(I::X)}), + // TAX + 0xAA => Box::new(TAX{}), + // TAY + 0xA8 => Box::new(TAY{}), + // TCD + 0x5B => Box::new(TCD{}), + // TCS + 0x1B => Box::new(TCS{}), + // TDC + 0x7B => Box::new(TDC{}), + // TRB + 0x1C => Box::new(TRB{addressing_mode: A::Absolute}), + 0x14 => Box::new(TRB{addressing_mode: A::DirectPage}), + // TSB + 0x0C => Box::new(TSB{addressing_mode: A::Absolute}), + 0x04 => Box::new(TSB{addressing_mode: A::DirectPage}), + // TSC + 0x3B => Box::new(TSC{}), + // TSX + 0xBA => Box::new(TSX{}), + // TXA + 0x8A => Box::new(TXA{}), + // TXS + 0x9A => Box::new(TXS{}), + // TXY + 0x9B => Box::new(TXY{}), + // TYA + 0x98 => Box::new(TYA{}), + // TYX + 0xBB => Box::new(TYX{}), + // WAI + 0xCB => Box::new(WAI{}), + // WDM + 0x42 => Box::new(WDM{}), + // XBA + 0xEB => Box::new(XBA{}), + // XCE + 0xFB => Box::new(XCE{}), + } +} \ No newline at end of file diff --git a/snes-core/src/cpu/instructions/mod.rs b/snes-core/src/cpu/instructions/mod.rs new file mode 100644 index 0000000..f4d4ee9 --- /dev/null +++ b/snes-core/src/cpu/instructions/mod.rs @@ -0,0 +1,108 @@ +use crate::cpu::bus::Bus; +use crate::cpu::registers::Registers; + +pub mod adc; +pub mod and; +pub mod asl; +pub mod bcc; +pub mod bcs; +pub mod beq; +pub mod bne; +pub mod bmi; +pub mod bpl; +pub mod bra; +pub mod brk; +pub mod brl; +pub mod bvc; +pub mod bvs; +pub mod bit; +pub mod clc; +pub mod cld; +pub mod cli; +pub mod clv; +pub mod cmp; +pub mod cop; +pub mod cpx; +pub mod cpy; +pub mod dec; +pub mod dex; +pub mod dey; +pub mod eor; +pub mod inc; +pub mod inx; +pub mod iny; +pub mod jmp; +pub mod jsr; +pub mod lda; +pub mod ldx; +pub mod ldy; +pub mod lsr; +pub mod mvn; +pub mod mvp; +pub mod nop; +pub mod ora; +pub mod pea; +pub mod pei; +pub mod per; +pub mod pha; +pub mod phb; +pub mod phd; +pub mod phk; +pub mod php; +pub mod phx; +pub mod phy; +pub mod pla; +pub mod plb; +pub mod pld; +pub mod plp; +pub mod plx; +pub mod ply; +pub mod rep; +pub mod rol; +pub mod ror; +pub mod rti; +pub mod rtl; +pub mod rts; +pub mod sbc; +pub mod sec; +pub mod sed; +pub mod sei; +pub mod sep; +pub mod sta; +pub mod stx; +pub mod sty; +pub mod stp; +pub mod stz; +pub mod tax; +pub mod tay; +pub mod tcd; +pub mod tcs; +pub mod tdc; +pub mod trb; +pub mod tsb; +pub mod tsc; +pub mod tsx; +pub mod txa; +pub mod txs; +pub mod txy; +pub mod tya; +pub mod tyx; +pub mod wai; +pub mod wdm; +pub mod xba; +pub mod xce; +pub mod bit_common; +pub mod dec_common; +pub mod decoder_common; +pub mod branch_common; +pub mod push_common; +pub mod pull_common; +pub mod comp_common; +pub mod move_common; +pub mod read_write_common; +pub mod mapper; + +pub trait CPUInstruction { + fn execute(&self, registers: &mut Registers, bus: &mut Bus); + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String; +} diff --git a/snes-core/src/cpu/instructions/move_common.rs b/snes-core/src/cpu/instructions/move_common.rs new file mode 100644 index 0000000..ad81847 --- /dev/null +++ b/snes-core/src/cpu/instructions/move_common.rs @@ -0,0 +1,29 @@ +use crate::cpu::{registers::Registers, bus::Bus, cycles}; + +pub fn do_move(registers: &mut Registers, bus: &mut Bus, is_next: bool) { + let pc = registers.get_pc_address(); + let source_bank = bus.read(pc + 2); + let dest_bank = bus.read(pc + 1); + let mut count = 0; + while registers.a != 0xFFFF { + let (x, y) = match registers.is_16bit_index() { + true => (registers.x, registers.y), + false => (registers.x & 0x00FF, registers.y & 0x00FF), + }; + let source_address = ((source_bank as u32) << 16) | (x as u32); + let dest_address = ((dest_bank as u32) << 16) | (y as u32); + let byte = bus.read(source_address); + bus.write(dest_address, byte); + registers.a = registers.a.wrapping_sub(1); + if is_next { + registers.x = registers.x.wrapping_add(1); + registers.y = registers.y.wrapping_add(1); + } else { + registers.x = registers.x.wrapping_sub(1); + registers.y = registers.y.wrapping_sub(1); + } + count += 1; + } + let (bytes, cycles) = cycles::increment_cycles_move(count); + registers.increment_pc(bytes); registers.cycles += cycles; +} diff --git a/snes-core/src/cpu/instructions/mvn.rs b/snes-core/src/cpu/instructions/mvn.rs new file mode 100644 index 0000000..a21af5d --- /dev/null +++ b/snes-core/src/cpu/instructions/mvn.rs @@ -0,0 +1,28 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::{CPUInstruction, move_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "MVN"; + +pub struct MVN {} + +impl CPUInstruction for MVN { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + move_common::do_move(registers, bus, true); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_move(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + } +} diff --git a/snes-core/src/cpu/instructions/mvp.rs b/snes-core/src/cpu/instructions/mvp.rs new file mode 100644 index 0000000..1c50e5e --- /dev/null +++ b/snes-core/src/cpu/instructions/mvp.rs @@ -0,0 +1,28 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::{CPUInstruction, move_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "MVP"; + +pub struct MVP {} + +impl CPUInstruction for MVP { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + move_common::do_move(registers, bus, false); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_move(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + } +} diff --git a/snes-core/src/cpu/instructions/nop.rs b/snes-core/src/cpu/instructions/nop.rs new file mode 100644 index 0000000..d933d25 --- /dev/null +++ b/snes-core/src/cpu/instructions/nop.rs @@ -0,0 +1,37 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "NOP"; + +pub struct NOP {} + +impl CPUInstruction for NOP { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let (bytes, cycles) = cycles::increment_cycles_nop(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + let instruction = NOP{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x01); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/ora.rs b/snes-core/src/cpu/instructions/ora.rs new file mode 100644 index 0000000..c1cfa09 --- /dev/null +++ b/snes-core/src/cpu/instructions/ora.rs @@ -0,0 +1,113 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}}; + +use crate::cpu::cycles; +use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "ORA"; + +pub struct ORA { + pub addressing_mode: AddressingMode, +} + +impl ORA { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(ORA16{addressing_mode: self.addressing_mode}), + false => Box::new(ORA8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for ORA { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct ORA8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for ORA8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_8bit_from_address(registers, bus, self.addressing_mode); + let (result, affected_flags) = alu::ora(registers.a as u8, value); + registers.set_low_a(result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_bitwise(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct ORA16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for ORA16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_16bit_from_address(registers, bus, self.addressing_mode); + let (result, affected_flags) = alu::ora(registers.a, value); + registers.a = result; + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_bitwise(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.a = 0x0F; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(true); + bus.write(0x000001, 0xF0); + let instruction = ORA8{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0xFF); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(!registers.get_zero_flag()); + assert!(registers.get_negative_flag()); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.a = 0x00FF; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.emulation_mode = false; + registers.set_memory_select_flag(false); + bus.write(0x000002, 0x11); + bus.write(0x000001, 0x00); + let instruction = ORA16{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x11FF); + assert_eq!(registers.pc, 0x03); + assert_eq!(registers.cycles, 3); + assert!(!registers.get_zero_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/pea.rs b/snes-core/src/cpu/instructions/pea.rs new file mode 100644 index 0000000..23b24fa --- /dev/null +++ b/snes-core/src/cpu/instructions/pea.rs @@ -0,0 +1,46 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use crate::utils::addressing::AddressingMode; +use super::read_write_common::get_effective_address; +use super::{CPUInstruction, push_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PEA"; + +pub struct PEA {} + +impl CPUInstruction for PEA { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let address = get_effective_address(registers, bus, AddressingMode::Absolute); + push_common::do_push(registers, bus, &[(address >> 8) as u8, address as u8]); + let (bytes, cycles) = cycles::increment_cycles_pea(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_absolute(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.sp = 0x1FC; + bus.write(0x000002, 0xAA); + bus.write(0x000001, 0xBB); + let instruction = PEA{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x1FC), 0xAA); + assert_eq!(bus.read(0x1FB), 0xBB); + assert_eq!(registers.pc, 0x0003); + assert_eq!(registers.cycles, 5); + } +} diff --git a/snes-core/src/cpu/instructions/pei.rs b/snes-core/src/cpu/instructions/pei.rs new file mode 100644 index 0000000..79068bb --- /dev/null +++ b/snes-core/src/cpu/instructions/pei.rs @@ -0,0 +1,48 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use crate::utils::addressing::AddressingMode; +use super::read_write_common::get_effective_address; +use super::{CPUInstruction, push_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PEI"; + +pub struct PEI {} + +impl CPUInstruction for PEI { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let address = get_effective_address(registers, bus, AddressingMode::DirectPageIndirect); + push_common::do_push(registers, bus, &[(address >> 8) as u8, address as u8]); + let (bytes, cycles) = cycles::increment_cycles_pei(registers); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_direct_page_indirect(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.sp = 0x1FC; + registers.d = 0x00; + bus.write(0x000001, 0x02); // Direct page address + bus.write(0x000002, 0xAA); + bus.write(0x000003, 0xBB); + let instruction = PEI{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x1FC), 0xAA); + assert_eq!(bus.read(0x1FB), 0xBB); + assert_eq!(registers.pc, 0x0002); + assert_eq!(registers.cycles, 6); + } +} diff --git a/snes-core/src/cpu/instructions/per.rs b/snes-core/src/cpu/instructions/per.rs new file mode 100644 index 0000000..213af2c --- /dev/null +++ b/snes-core/src/cpu/instructions/per.rs @@ -0,0 +1,51 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use crate::utils::addressing::AddressingMode; +use super::read_write_common::get_effective_address; +use super::{CPUInstruction, push_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PER"; + +pub struct PER {} + +impl CPUInstruction for PER { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let label = get_effective_address(registers, bus, AddressingMode::Absolute) as u16; + let is_negative = (label>> 15) == 1; + let (bytes, cycles) = cycles::increment_cycles_per(); + registers.increment_pc(bytes); registers.cycles += cycles; + let address = match is_negative { + true => registers.pc.wrapping_sub(!label + 1), + false => registers.pc.wrapping_add(label), + }; + push_common::do_push(registers, bus, &[(address >> 8) as u8, address as u8]); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_absolute(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.sp = 0x1FC; + bus.write(0x000002, 0x00); + bus.write(0x000001, 0x01); + let instruction = PER{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x1FC), 0x00); + assert_eq!(bus.read(0x1FB), 0x04); + assert_eq!(registers.pc, 0x0003); + assert_eq!(registers.cycles, 6); + } +} diff --git a/snes-core/src/cpu/instructions/pha.rs b/snes-core/src/cpu/instructions/pha.rs new file mode 100644 index 0000000..912ac58 --- /dev/null +++ b/snes-core/src/cpu/instructions/pha.rs @@ -0,0 +1,48 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, push_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PHA"; + +pub struct PHA {} + +impl CPUInstruction for PHA { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = registers.a; + if registers.is_16bit_mode() { + push_common::do_push(registers, bus, &[(value >> 8) as u8, value as u8]); + } else { + push_common::do_push(registers, bus, &[value as u8]); + } + let (bytes, cycles) = cycles::increment_cycles_pha(registers.is_16bit_mode()); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.sp = 0x1FC; + registers.a = 0x1234; + registers.set_16bit_mode(false); + let instruction = PHA{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x1FC), 0x34); + assert_eq!(registers.sp, 0x1FB); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 3); + } +} diff --git a/snes-core/src/cpu/instructions/phb.rs b/snes-core/src/cpu/instructions/phb.rs new file mode 100644 index 0000000..34e0953 --- /dev/null +++ b/snes-core/src/cpu/instructions/phb.rs @@ -0,0 +1,42 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, push_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PHB"; + +pub struct PHB {} + +impl CPUInstruction for PHB { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + push_common::do_push(registers, bus, &[registers.dbr]); + let (bytes, cycles) = cycles::increment_cycles_phb(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.sp = 0x1FC; + registers.dbr = 0x12; + let instruction = PHB{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x1FC), 0x12); + assert_eq!(registers.sp, 0x1FB); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 3); + } +} diff --git a/snes-core/src/cpu/instructions/phd.rs b/snes-core/src/cpu/instructions/phd.rs new file mode 100644 index 0000000..55f8373 --- /dev/null +++ b/snes-core/src/cpu/instructions/phd.rs @@ -0,0 +1,44 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, push_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PHD"; + +pub struct PHD {} + +impl CPUInstruction for PHD { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = registers.d; + push_common::do_push(registers, bus, &[(value >> 8) as u8, value as u8]); + let (bytes, cycles) = cycles::increment_cycles_phd(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.sp = 0x1FC; + registers.d = 0x1234; + let instruction = PHD{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x1FC), 0x12); + assert_eq!(bus.read(0x1FB), 0x34); + assert_eq!(registers.sp, 0x1FA); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/phk.rs b/snes-core/src/cpu/instructions/phk.rs new file mode 100644 index 0000000..b567309 --- /dev/null +++ b/snes-core/src/cpu/instructions/phk.rs @@ -0,0 +1,43 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, push_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PHK"; + +pub struct PHK {} + +impl CPUInstruction for PHK { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + push_common::do_push(registers, bus, &[registers.pbr]); + let (bytes, cycles) = cycles::increment_cycles_phk(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.pbr = 0x00; + registers.sp = 0x1FC; + bus.write(0x1FC, 0xFF); + let instruction = PHK{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x1FC), 0x00); + assert_eq!(registers.sp, 0x1FB); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 3); + } +} diff --git a/snes-core/src/cpu/instructions/php.rs b/snes-core/src/cpu/instructions/php.rs new file mode 100644 index 0000000..2170915 --- /dev/null +++ b/snes-core/src/cpu/instructions/php.rs @@ -0,0 +1,42 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, push_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PHP"; + +pub struct PHP {} + +impl CPUInstruction for PHP { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + push_common::do_push(registers, bus, &[registers.p]); + let (bytes, cycles) = cycles::increment_cycles_php(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.p = 0x12; + registers.sp = 0x1FC; + let instruction = PHP{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x1FC), 0x12); + assert_eq!(registers.sp, 0x1FB); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 3); + } +} diff --git a/snes-core/src/cpu/instructions/phx.rs b/snes-core/src/cpu/instructions/phx.rs new file mode 100644 index 0000000..a5c60db --- /dev/null +++ b/snes-core/src/cpu/instructions/phx.rs @@ -0,0 +1,50 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, push_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PHX"; + +pub struct PHX {} + +impl CPUInstruction for PHX { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = registers.x; + if registers.is_16bit_index() { + push_common::do_push(registers, bus, &[(value >> 8) as u8, value as u8]); + } else { + push_common::do_push(registers, bus, &[value as u8]); + } + let (bytes, cycles) = cycles::increment_cycles_push_index(registers.is_16bit_index()); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.set_16bit_index(true); + registers.pc = 0x0000; + registers.x = 0x1234; + registers.sp = 0x1FC; + let instruction = PHX{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x1FC), 0x12); + assert_eq!(bus.read(0x1FB), 0x34); + assert_eq!(registers.sp, 0x1FA); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/phy.rs b/snes-core/src/cpu/instructions/phy.rs new file mode 100644 index 0000000..76eed53 --- /dev/null +++ b/snes-core/src/cpu/instructions/phy.rs @@ -0,0 +1,50 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, push_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PHY"; + +pub struct PHY {} + +impl CPUInstruction for PHY { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = registers.y; + if registers.is_16bit_index() { + push_common::do_push(registers, bus, &[(value >> 8) as u8, value as u8]); + } else { + push_common::do_push(registers, bus, &[value as u8]); + } + let (bytes, cycles) = cycles::increment_cycles_push_index(registers.is_16bit_index()); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.set_16bit_index(true); + registers.pc = 0x0000; + registers.y = 0x1234; + registers.sp = 0x1FC; + let instruction = PHY{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x1FC), 0x12); + assert_eq!(bus.read(0x1FB), 0x34); + assert_eq!(registers.sp, 0x1FA); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/pla.rs b/snes-core/src/cpu/instructions/pla.rs new file mode 100644 index 0000000..7963904 --- /dev/null +++ b/snes-core/src/cpu/instructions/pla.rs @@ -0,0 +1,56 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, pull_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PLA"; + +pub struct PLA {} + +impl CPUInstruction for PLA { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + if registers.is_16bit_mode() { + let bytes = pull_common::do_pull(registers, bus, 2); + registers.a = (bytes[0] as u16) | ((bytes[1] as u16) << 8); + } else { + let bytes = pull_common::do_pull(registers, bus, 1); + registers.set_low_a(bytes[0]); + } + let (bytes, cycles) = cycles::increment_cycles_pla(registers.is_16bit_mode()); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.y = 0x1234; + registers.set_16bit_mode(true); + registers.set_negative_flag(true); + registers.set_zero_flag(true); + bus.write(0x1FB, 0x34); + bus.write(0x1FC, 0x12); + registers.sp = 0x1FA; + let instruction = PLA{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x1234); + assert_eq!(registers.sp, 0x1FC); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.get_negative_flag(), false); + assert_eq!(registers.get_zero_flag(), false); + assert_eq!(registers.cycles, 5); + } +} diff --git a/snes-core/src/cpu/instructions/plb.rs b/snes-core/src/cpu/instructions/plb.rs new file mode 100644 index 0000000..0040916 --- /dev/null +++ b/snes-core/src/cpu/instructions/plb.rs @@ -0,0 +1,47 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, pull_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PLB"; + +pub struct PLB {} + +impl CPUInstruction for PLB { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + registers.dbr = pull_common::do_pull(registers, bus, 1)[0]; + let (bytes, cycles) = cycles::increment_cycles_plb(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.dbr = 0x00; + registers.set_negative_flag(true); + registers.set_zero_flag(true); + bus.write(0x1FC, 0x12); + registers.sp = 0x1FB; + let instruction = PLB{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.dbr, 0x12); + assert_eq!(registers.sp, 0x1FC); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.get_negative_flag(), false); + assert_eq!(registers.get_zero_flag(), false); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/pld.rs b/snes-core/src/cpu/instructions/pld.rs new file mode 100644 index 0000000..0122480 --- /dev/null +++ b/snes-core/src/cpu/instructions/pld.rs @@ -0,0 +1,49 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, pull_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PLD"; + +pub struct PLD {} + +impl CPUInstruction for PLD { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let bytes = pull_common::do_pull(registers, bus, 2); + registers.d = (bytes[0] as u16) | ((bytes[1] as u16) << 8); + let (bytes, cycles) = cycles::increment_cycles_pld(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.d = 0x1234; + registers.set_negative_flag(true); + registers.set_zero_flag(true); + bus.write(0x1FB, 0x34); + bus.write(0x1FC, 0x12); + registers.sp = 0x1FA; + let instruction = PLD{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.d, 0x1234); + assert_eq!(registers.sp, 0x1FC); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.get_negative_flag(), false); + assert_eq!(registers.get_zero_flag(), false); + assert_eq!(registers.cycles, 5); + } +} diff --git a/snes-core/src/cpu/instructions/plp.rs b/snes-core/src/cpu/instructions/plp.rs new file mode 100644 index 0000000..7a81a6c --- /dev/null +++ b/snes-core/src/cpu/instructions/plp.rs @@ -0,0 +1,44 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, pull_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PLP"; + +pub struct PLP {} + +impl CPUInstruction for PLP { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let bytes = pull_common::do_pull(registers, bus, 1); + registers.p = bytes[0]; + let (bytes, cycles) = cycles::increment_cycles_plp(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.p = 0x00; + bus.write(0x1FC, 0xFF); + registers.sp = 0x1FB; + let instruction = PLP{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.p, 0xFF); + assert_eq!(registers.sp, 0x1FC); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/plx.rs b/snes-core/src/cpu/instructions/plx.rs new file mode 100644 index 0000000..248cf96 --- /dev/null +++ b/snes-core/src/cpu/instructions/plx.rs @@ -0,0 +1,56 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, pull_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PLX"; + +pub struct PLX {} + +impl CPUInstruction for PLX { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + if registers.is_16bit_index() { + let bytes = pull_common::do_pull(registers, bus, 2); + registers.x = (bytes[0] as u16) | ((bytes[1] as u16) << 8); + } else { + let bytes = pull_common::do_pull(registers, bus, 1); + registers.set_low_x(bytes[0]); + } + let (bytes, cycles) = cycles::increment_cycles_pl_index(registers.is_16bit_index()); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.x = 0x1234; + registers.set_16bit_index(true); + registers.set_negative_flag(true); + registers.set_zero_flag(true); + bus.write(0x1FB, 0x34); + bus.write(0x1FC, 0x12); + registers.sp = 0x1FA; + let instruction = PLX{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.x, 0x1234); + assert_eq!(registers.sp, 0x1FC); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.get_negative_flag(), false); + assert_eq!(registers.get_zero_flag(), false); + assert_eq!(registers.cycles, 5); + } +} diff --git a/snes-core/src/cpu/instructions/ply.rs b/snes-core/src/cpu/instructions/ply.rs new file mode 100644 index 0000000..b86d7a6 --- /dev/null +++ b/snes-core/src/cpu/instructions/ply.rs @@ -0,0 +1,56 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, pull_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "PLY"; + +pub struct PLY {} + +impl CPUInstruction for PLY { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + if registers.is_16bit_index() { + let bytes = pull_common::do_pull(registers, bus, 2); + registers.y = (bytes[0] as u16) | ((bytes[1] as u16) << 8); + } else { + let bytes = pull_common::do_pull(registers, bus, 1); + registers.set_low_y(bytes[0]); + } + let (bytes, cycles) = cycles::increment_cycles_pl_index(registers.is_16bit_index()); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.y = 0x1234; + registers.set_16bit_index(true); + registers.set_negative_flag(true); + registers.set_zero_flag(true); + bus.write(0x1FB, 0x34); + bus.write(0x1FC, 0x12); + registers.sp = 0x1FA; + let instruction = PLY{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.y, 0x1234); + assert_eq!(registers.sp, 0x1FC); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.get_negative_flag(), false); + assert_eq!(registers.get_zero_flag(), false); + assert_eq!(registers.cycles, 5); + } +} diff --git a/snes-core/src/cpu/instructions/pull_common.rs b/snes-core/src/cpu/instructions/pull_common.rs new file mode 100644 index 0000000..1adf47d --- /dev/null +++ b/snes-core/src/cpu/instructions/pull_common.rs @@ -0,0 +1,22 @@ +use crate::cpu::{registers::Registers, bus::Bus}; + + +pub fn do_pull(registers: &mut Registers, bus: &mut Bus, count: usize) -> Vec { + let mut bytes = vec![]; + let mut is_zero = true; + for _ in 0..count { + registers.increment_sp(1); + let byte = bus.read(registers.sp as u32); + if byte != 0 { + is_zero = false; + } + bytes.push(byte); + } + registers.set_zero_flag(is_zero); + if bytes.len() > 0 { + // Low byte is pulled first, so we need to check + // for the last byte that we pull + registers.set_negative_flag((bytes[bytes.len() - 1] >> 7) == 1); + } + bytes +} \ No newline at end of file diff --git a/snes-core/src/cpu/instructions/push_common.rs b/snes-core/src/cpu/instructions/push_common.rs new file mode 100644 index 0000000..62c648c --- /dev/null +++ b/snes-core/src/cpu/instructions/push_common.rs @@ -0,0 +1,9 @@ +use crate::cpu::{registers::Registers, bus::Bus}; + +pub fn do_push(registers: &mut Registers, bus: &mut Bus, bytes: &[u8]) { + for byte in bytes { + let address = registers.sp as u32; + bus.write(address, *byte); + registers.decrement_sp(1); + } +} diff --git a/snes-core/src/cpu/instructions/read_write_common.rs b/snes-core/src/cpu/instructions/read_write_common.rs new file mode 100644 index 0000000..77a9926 --- /dev/null +++ b/snes-core/src/cpu/instructions/read_write_common.rs @@ -0,0 +1,67 @@ +use crate::{cpu::{registers::Registers, bus::Bus}, utils::addressing::AddressingMode}; + +pub fn get_effective_address(registers: &Registers, bus: &mut Bus, addressing_mode: AddressingMode) -> u32 { + addressing_mode.effective_address( + bus, + registers.get_pc_address(), + registers.d, + registers.sp, + registers.x, registers.y, + ) +} + +pub fn read_8bit_from_address(registers: &Registers, bus: &mut Bus, addressing_mode: AddressingMode) -> u8 { + match addressing_mode { + AddressingMode::Accumulator => registers.a as u8, + _ => addressing_mode.value_8bit( + bus, + registers.get_pc_address(), + registers.d, + registers.sp, + registers.x, + registers.y, + ) + } +} + +pub fn read_16bit_from_address(registers: &Registers, bus: &mut Bus, addressing_mode: AddressingMode) -> u16 { + match addressing_mode { + AddressingMode::Accumulator => registers.a, + _ => addressing_mode.value_16bit( + bus, + registers.get_pc_address(), + registers.d, + registers.sp, + registers.x, + registers.y, + ) + } +} + +pub fn write_8bit_to_address(registers: &mut Registers, bus: &mut Bus, addressing_mode: AddressingMode, value: u8) { + match addressing_mode { + AddressingMode::Accumulator => registers.set_low_a(value), + _ => addressing_mode.store_8bit( + bus, + registers.get_pc_address(), + registers.d, + registers.sp, + registers.x, registers.y, + value, + ), + }; +} + +pub fn write_16bit_to_address(registers: &mut Registers, bus: &mut Bus, addressing_mode: AddressingMode, value: u16) { + match addressing_mode { + AddressingMode::Accumulator => registers.a = value, + _ => addressing_mode.store_16bit( + bus, + registers.get_pc_address(), + registers.d, + registers.sp, + registers.x, registers.y, + value, + ), + }; +} \ No newline at end of file diff --git a/snes-core/src/cpu/instructions/rep.rs b/snes-core/src/cpu/instructions/rep.rs new file mode 100644 index 0000000..9040bae --- /dev/null +++ b/snes-core/src/cpu/instructions/rep.rs @@ -0,0 +1,45 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use crate::utils::addressing::AddressingMode; +use super::read_write_common::read_8bit_from_address; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "REP"; + +pub struct REP {} + +impl CPUInstruction for REP { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let byte = read_8bit_from_address(registers, bus, AddressingMode::Immediate); + registers.reset_rep_byte(byte); + let (bytes, cycles) = cycles::increment_cycles_rep(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_8bit_immediate(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.p = 0xFF; + bus.write(0x0001, 0xFF); + let instruction = REP{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.p, 0x00); + assert_eq!(registers.pc, 0x0002); + assert_eq!(registers.cycles, 3); + } +} diff --git a/snes-core/src/cpu/instructions/rol.rs b/snes-core/src/cpu/instructions/rol.rs new file mode 100644 index 0000000..995f3bd --- /dev/null +++ b/snes-core/src/cpu/instructions/rol.rs @@ -0,0 +1,110 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}}; + +use crate::cpu::cycles; +use super::{CPUInstruction, read_write_common::{read_8bit_from_address, write_8bit_to_address, read_16bit_from_address, write_16bit_to_address}}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "ROL"; + +pub struct ROL { + pub addressing_mode: AddressingMode, +} + +impl ROL { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(ROL16{addressing_mode: self.addressing_mode}), + false => Box::new(ROL8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for ROL { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct ROL8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for ROL8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let target = read_8bit_from_address(registers, bus, self.addressing_mode); + let (result, affected_flags) = alu::rol(target, registers.get_carry_flag()); + write_8bit_to_address(registers, bus, self.addressing_mode, result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_shift(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct ROL16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for ROL16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let target = read_16bit_from_address(registers, bus, self.addressing_mode); + let (result, affected_flags) = alu::rol(target, registers.get_carry_flag()); + write_16bit_to_address(registers, bus, self.addressing_mode, result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_shift(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.set_16bit_mode(false); + registers.a = 0b0100_0000; + registers.pc = 0x0000; + let instruction = ROL8{addressing_mode: AddressingMode::Accumulator}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.get_negative_flag(), true); + assert_eq!(registers.get_zero_flag(), false); + assert_eq!(registers.a, 0b1000_0000); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } + + #[test] + fn test_and_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.set_16bit_mode(true); + registers.a = 0b01000000_00000000; + registers.pc = 0x0000; + let instruction = ROL16{addressing_mode: AddressingMode::Accumulator}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.get_negative_flag(), true); + assert_eq!(registers.get_zero_flag(), false); + assert_eq!(registers.a, 0b10000000_00000000); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/ror.rs b/snes-core/src/cpu/instructions/ror.rs new file mode 100644 index 0000000..4f86ace --- /dev/null +++ b/snes-core/src/cpu/instructions/ror.rs @@ -0,0 +1,112 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}}; + +use crate::cpu::cycles; +use super::{CPUInstruction, read_write_common::{read_8bit_from_address, write_8bit_to_address, read_16bit_from_address, write_16bit_to_address}}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "ROR"; + +pub struct ROR { + pub addressing_mode: AddressingMode, +} + +impl ROR { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(ROR16{addressing_mode: self.addressing_mode}), + false => Box::new(ROR8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for ROR { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct ROR8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for ROR8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let target = read_8bit_from_address(registers, bus, self.addressing_mode); + let (result, affected_flags) = alu::ror(target, registers.get_carry_flag()); + write_8bit_to_address(registers, bus, self.addressing_mode, result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_shift(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct ROR16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for ROR16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let target = read_16bit_from_address(registers, bus, self.addressing_mode); + let (result, affected_flags) = alu::ror(target, registers.get_carry_flag()); + write_16bit_to_address(registers, bus, self.addressing_mode, result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_shift(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.set_16bit_mode(false); + registers.set_carry_flag(true); + registers.a = 0x00; + registers.pc = 0x0000; + let instruction = ROR8{addressing_mode: AddressingMode::Accumulator}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.get_carry_flag(), false); + assert_eq!(registers.get_zero_flag(), false); + assert_eq!(registers.a, 0b1000_0000); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } + + #[test] + fn test_and_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.set_16bit_mode(true); + registers.set_carry_flag(true); + registers.a = 0b00000000_00000000; + registers.pc = 0x0000; + let instruction = ROR16{addressing_mode: AddressingMode::Accumulator}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.get_negative_flag(), true); + assert_eq!(registers.get_zero_flag(), false); + assert_eq!(registers.a, 0b10000000_00000000); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/rti.rs b/snes-core/src/cpu/instructions/rti.rs new file mode 100644 index 0000000..aa244b2 --- /dev/null +++ b/snes-core/src/cpu/instructions/rti.rs @@ -0,0 +1,36 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, pull_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "RTI"; + +pub struct RTI {} + +impl CPUInstruction for RTI { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + registers.p = pull_common::do_pull(registers, bus, 1)[0]; + let pc_bytes = pull_common::do_pull(registers, bus, 2); + registers.pc = (pc_bytes[0] as u16) | ((pc_bytes[1] as u16) << 8); + if !registers.emulation_mode { + registers.pbr = pull_common::do_pull(registers, bus, 1)[0]; + } + let (bytes, cycles) = cycles::increment_cycles_return_interrupt(registers.emulation_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + } +} diff --git a/snes-core/src/cpu/instructions/rtl.rs b/snes-core/src/cpu/instructions/rtl.rs new file mode 100644 index 0000000..c4e2e1d --- /dev/null +++ b/snes-core/src/cpu/instructions/rtl.rs @@ -0,0 +1,47 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, pull_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "RTL"; + +pub struct RTL {} + +impl CPUInstruction for RTL { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let bytes = pull_common::do_pull(registers, bus, 3); + // Low byte of PC is pulled first, then high byte and then PBR + registers.pc = (bytes[0] as u16) | ((bytes[1] as u16) << 8); + registers.pbr = bytes[2]; + let (bytes, cycles) = cycles::increment_cycles_return_subroutine(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.sp = 0x1F9; + bus.write(0x1FC, 0x12); + bus.write(0x1FB, 0x34); + bus.write(0x1FA, 0x56); + let instruction = RTL{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pbr, 0x12); + assert_eq!(registers.pc, 0x3456); + assert_eq!(registers.cycles, 6); + } +} diff --git a/snes-core/src/cpu/instructions/rts.rs b/snes-core/src/cpu/instructions/rts.rs new file mode 100644 index 0000000..aa189ce --- /dev/null +++ b/snes-core/src/cpu/instructions/rts.rs @@ -0,0 +1,45 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::{CPUInstruction, pull_common}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "RTS"; + +pub struct RTS {} + +impl CPUInstruction for RTS { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let bytes = pull_common::do_pull(registers, bus, 2); + // Low byte of PC is pulled first, then high byte + registers.pc = (bytes[0] as u16) | ((bytes[1] as u16) << 8); + let (bytes, cycles) = cycles::increment_cycles_return_subroutine(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.sp = 0x1FA; + bus.write(0x1FC, 0x12); + bus.write(0x1FB, 0x34); + let instruction = RTS{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pbr, 0x00); + assert_eq!(registers.pc, 0x1234); + assert_eq!(registers.cycles, 6); + } +} diff --git a/snes-core/src/cpu/instructions/sbc.rs b/snes-core/src/cpu/instructions/sbc.rs new file mode 100644 index 0000000..d17558c --- /dev/null +++ b/snes-core/src/cpu/instructions/sbc.rs @@ -0,0 +1,211 @@ +use crate::{cpu::{bus::Bus, registers::Registers}, utils::{alu, addressing::AddressingMode}}; + +use crate::cpu::cycles; +use super::{CPUInstruction, read_write_common::{read_8bit_from_address, read_16bit_from_address}}; +use super::decoder_common; + +static INSTR_NAME: &'static str = "SBC"; + +pub struct SBC { + pub addressing_mode: AddressingMode, +} + +impl SBC { + fn determine_instruction(&self, registers: &Registers) -> Box { + let is_decimal_mode = registers.get_decimal_mode_flag(); + match registers.is_16bit_mode() { + true => match is_decimal_mode { + true => Box::new(SBC16BCD{addressing_mode: self.addressing_mode}), + false => Box::new(SBC16BIN{addressing_mode: self.addressing_mode}), + } + false => match is_decimal_mode { + true => Box::new(SBC8BCD{addressing_mode: self.addressing_mode}), + false => Box::new(SBC8BIN{addressing_mode: self.addressing_mode}), + } + } + } +} + +impl CPUInstruction for SBC { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct SBC8BIN { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for SBC8BIN { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let (result, affected_flags) = alu::sbc_bin( + registers.a as u8, + read_8bit_from_address(registers, bus, self.addressing_mode), + registers.get_carry_flag(), + ); + registers.set_low_a(result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct SBC16BIN { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for SBC16BIN { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let (result, affected_flags) = alu::sbc_bin( + registers.a, + read_16bit_from_address(registers, bus, self.addressing_mode), + registers.get_carry_flag(), + ); + registers.a = result; + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct SBC8BCD { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for SBC8BCD { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let (result, affected_flags) = alu::sbc_bcd( + registers.a as u8, + read_8bit_from_address(registers, bus, self.addressing_mode), + registers.get_carry_flag(), + ); + registers.set_low_a(result); + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct SBC16BCD { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for SBC16BCD { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let (result, affected_flags) = alu::sbc_bcd( + registers.a, + read_16bit_from_address(registers, bus, self.addressing_mode), + registers.get_carry_flag(), + ); + registers.a = result; + registers.set_flags(&affected_flags); + let (bytes, cycles) = cycles::increment_cycles_arithmetic(®isters, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_sbc_bin_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x0040; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(true); + bus.write(0x000001, 0x40); + let instruction = SBC8BIN{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x00); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(registers.get_zero_flag()); + assert!(!registers.get_carry_flag()); + } + + #[test] + fn test_sbc_bin_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x4000; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_memory_select_flag(false); + bus.write(0x000001, 0x00); + bus.write(0x000002, 0x40); + let instruction = SBC16BIN{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x0000); + assert_eq!(registers.pc, 0x03); + assert_eq!(registers.cycles, 3); + assert!(registers.get_zero_flag()); + assert!(!registers.get_carry_flag()); + } + + #[test] + fn test_sbc_bcd_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x0049; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_16bit_mode(false); + bus.write(0x000001, 0x48); + let instruction = SBC8BCD{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x00); + assert_eq!(registers.pc, 0x02); + assert_eq!(registers.cycles, 2); + assert!(registers.get_zero_flag()); + assert!(registers.get_carry_flag()); + } + + #[test] + fn test_sbc_bcd_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.a = 0x0049; + registers.pbr = 0x00; + registers.pc = 0x0000; + registers.set_16bit_mode(true); + bus.write(0x000001, 0x48); + bus.write(0x000002, 0x00); + let instruction = SBC16BCD{addressing_mode: AddressingMode::Immediate}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x0000); + assert_eq!(registers.pc, 0x03); + assert_eq!(registers.cycles, 3); + assert!(registers.get_zero_flag()); + assert!(registers.get_carry_flag()); + } +} diff --git a/snes-core/src/cpu/instructions/sec.rs b/snes-core/src/cpu/instructions/sec.rs new file mode 100644 index 0000000..6b91a7d --- /dev/null +++ b/snes-core/src/cpu/instructions/sec.rs @@ -0,0 +1,40 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "SEC"; + +pub struct SEC {} + +impl CPUInstruction for SEC { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.set_carry_flag(true); + let (bytes, cycles) = cycles::increment_cycles_set_flag(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.set_carry_flag(false); + let instruction = SEC{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.get_carry_flag(), true); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/sed.rs b/snes-core/src/cpu/instructions/sed.rs new file mode 100644 index 0000000..a4f7c9c --- /dev/null +++ b/snes-core/src/cpu/instructions/sed.rs @@ -0,0 +1,40 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "SED"; + +pub struct SED {} + +impl CPUInstruction for SED { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.set_decimal_mode_flag(true); + let (bytes, cycles) = cycles::increment_cycles_set_flag(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.set_decimal_mode_flag(false); + let instruction = SED{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.get_decimal_mode_flag(), true); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/sei.rs b/snes-core/src/cpu/instructions/sei.rs new file mode 100644 index 0000000..dd1d043 --- /dev/null +++ b/snes-core/src/cpu/instructions/sei.rs @@ -0,0 +1,40 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "SEI"; + +pub struct SEI {} + +impl CPUInstruction for SEI { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.set_irq_disable_flag(true); + let (bytes, cycles) = cycles::increment_cycles_set_flag(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.set_irq_disable_flag(false); + let instruction = SEI{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.get_irq_disable_flag(), true); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/sep.rs b/snes-core/src/cpu/instructions/sep.rs new file mode 100644 index 0000000..a11f31c --- /dev/null +++ b/snes-core/src/cpu/instructions/sep.rs @@ -0,0 +1,44 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use crate::utils::addressing::AddressingMode; +use super::read_write_common::read_8bit_from_address; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "SEP"; + +pub struct SEP {} + +impl CPUInstruction for SEP { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let byte = read_8bit_from_address(registers, bus, AddressingMode::Immediate); + registers.set_sep_byte(byte); + let (bytes, cycles) = cycles::increment_cycles_sep(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_8bit_immediate(opcode, INSTR_NAME, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.p = 0x00; + bus.write(0x0001, 0xFF); + let instruction = SEP{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.p, 0xFF); + assert_eq!(registers.pc, 0x0002); + assert_eq!(registers.cycles, 3); + } +} diff --git a/snes-core/src/cpu/instructions/sta.rs b/snes-core/src/cpu/instructions/sta.rs new file mode 100644 index 0000000..e5090d5 --- /dev/null +++ b/snes-core/src/cpu/instructions/sta.rs @@ -0,0 +1,105 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; +use crate::utils::addressing::AddressingMode; + +use super::read_write_common::{write_8bit_to_address, write_16bit_to_address}; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "STA"; + +pub struct STA { + pub addressing_mode: AddressingMode, +} + +impl STA { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(STA16{addressing_mode: self.addressing_mode}), + false => Box::new(STA8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for STA { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct STA8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for STA8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + write_8bit_to_address(registers, bus, self.addressing_mode, registers.a as u8); + let (bytes, cycles) = cycles::increment_cycles_sta(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct STA16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for STA16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + write_16bit_to_address(registers, bus, self.addressing_mode, registers.a); + let (bytes, cycles) = cycles::increment_cycles_sta(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.a = 0x12; + registers.set_16bit_mode(false); + bus.write(0x0002, 0x00); + bus.write(0x0001, 0x03); + let instruction = STA8{addressing_mode: AddressingMode::Absolute}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x0003), 0x12); + assert_eq!(registers.pc, 0x0003); + assert_eq!(registers.cycles, 4); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.a = 0x1234; + registers.set_16bit_mode(true); + bus.write(0x0002, 0x00); + bus.write(0x0001, 0x03); + let instruction = STA16{addressing_mode: AddressingMode::Absolute}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x0003), 0x34); + assert_eq!(bus.read(0x0004), 0x12); + assert_eq!(registers.pc, 0x0003); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/stp.rs b/snes-core/src/cpu/instructions/stp.rs new file mode 100644 index 0000000..e644edf --- /dev/null +++ b/snes-core/src/cpu/instructions/stp.rs @@ -0,0 +1,40 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "STP"; + +pub struct STP {} + +impl CPUInstruction for STP { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.is_cpu_stopped = true; + let (bytes, cycles) = cycles::increment_cycles_stp(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.is_cpu_stopped = false; + registers.pc = 0x0000; + let instruction = STP{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.is_cpu_stopped, true); + assert_eq!(registers.cycles, 3); + } +} diff --git a/snes-core/src/cpu/instructions/stx.rs b/snes-core/src/cpu/instructions/stx.rs new file mode 100644 index 0000000..a73962f --- /dev/null +++ b/snes-core/src/cpu/instructions/stx.rs @@ -0,0 +1,105 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; +use crate::utils::addressing::AddressingMode; + +use super::read_write_common::{write_8bit_to_address, write_16bit_to_address}; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "STX"; + +pub struct STX { + pub addressing_mode: AddressingMode, +} + +impl STX { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(STX16{addressing_mode: self.addressing_mode}), + false => Box::new(STX8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for STX { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct STX8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for STX8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + write_8bit_to_address(registers, bus, self.addressing_mode, registers.x as u8); + let (bytes, cycles) = cycles::increment_cycles_st_index(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct STX16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for STX16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + write_16bit_to_address(registers, bus, self.addressing_mode, registers.x); + let (bytes, cycles) = cycles::increment_cycles_st_index(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.x = 0x12; + registers.set_16bit_mode(false); + bus.write(0x0002, 0x00); + bus.write(0x0001, 0x03); + let instruction = STX8{addressing_mode: AddressingMode::Absolute}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x0003), 0x12); + assert_eq!(registers.pc, 0x0003); + assert_eq!(registers.cycles, 4); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.x = 0x1234; + registers.set_16bit_mode(true); + bus.write(0x0002, 0x00); + bus.write(0x0001, 0x03); + let instruction = STX16{addressing_mode: AddressingMode::Absolute}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x0003), 0x34); + assert_eq!(bus.read(0x0004), 0x12); + assert_eq!(registers.pc, 0x0003); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/sty.rs b/snes-core/src/cpu/instructions/sty.rs new file mode 100644 index 0000000..45418b7 --- /dev/null +++ b/snes-core/src/cpu/instructions/sty.rs @@ -0,0 +1,105 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; +use crate::utils::addressing::AddressingMode; + +use super::read_write_common::{write_8bit_to_address, write_16bit_to_address}; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "STY"; + +pub struct STY { + pub addressing_mode: AddressingMode, +} + +impl STY { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(STY16{addressing_mode: self.addressing_mode}), + false => Box::new(STY8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for STY { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct STY8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for STY8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + write_8bit_to_address(registers, bus, self.addressing_mode, registers.y as u8); + let (bytes, cycles) = cycles::increment_cycles_st_index(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct STY16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for STY16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + write_16bit_to_address(registers, bus, self.addressing_mode, registers.y); + let (bytes, cycles) = cycles::increment_cycles_st_index(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.y = 0x12; + registers.set_16bit_mode(false); + bus.write(0x0002, 0x00); + bus.write(0x0001, 0x03); + let instruction = STY8{addressing_mode: AddressingMode::Absolute}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x0003), 0x12); + assert_eq!(registers.pc, 0x0003); + assert_eq!(registers.cycles, 4); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.y = 0x1234; + registers.set_16bit_mode(true); + bus.write(0x0002, 0x00); + bus.write(0x0001, 0x03); + let instruction = STY16{addressing_mode: AddressingMode::Absolute}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x0003), 0x34); + assert_eq!(bus.read(0x0004), 0x12); + assert_eq!(registers.pc, 0x0003); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/stz.rs b/snes-core/src/cpu/instructions/stz.rs new file mode 100644 index 0000000..73c4d4b --- /dev/null +++ b/snes-core/src/cpu/instructions/stz.rs @@ -0,0 +1,106 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; +use crate::utils::addressing::AddressingMode; + +use super::read_write_common::{write_8bit_to_address, write_16bit_to_address}; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "STZ"; + +pub struct STZ { + pub addressing_mode: AddressingMode, +} + +impl STZ { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(STZ16{addressing_mode: self.addressing_mode}), + false => Box::new(STZ8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for STZ { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct STZ8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for STZ8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + write_8bit_to_address(registers, bus, self.addressing_mode, 0); + let (bytes, cycles) = cycles::increment_cycles_st_index(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct STZ16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for STZ16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + write_16bit_to_address(registers, bus, self.addressing_mode, 0); + let (bytes, cycles) = cycles::increment_cycles_st_index(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.set_16bit_mode(false); + bus.write(0x0002, 0x00); + bus.write(0x0001, 0x03); + bus.write(0x0003, 0xFF); + let instruction = STZ8{addressing_mode: AddressingMode::Absolute}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x0003), 0x00); + assert_eq!(registers.pc, 0x0003); + assert_eq!(registers.cycles, 4); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.set_16bit_mode(true); + bus.write(0x0002, 0x00); + bus.write(0x0001, 0x03); + bus.write(0x0003, 0xFF); + bus.write(0x0004, 0xFF); + let instruction = STZ16{addressing_mode: AddressingMode::Absolute}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(bus.read(0x0003), 0x00); + assert_eq!(bus.read(0x0004), 0x00); + assert_eq!(registers.pc, 0x0003); + assert_eq!(registers.cycles, 4); + } +} diff --git a/snes-core/src/cpu/instructions/tax.rs b/snes-core/src/cpu/instructions/tax.rs new file mode 100644 index 0000000..8450a1d --- /dev/null +++ b/snes-core/src/cpu/instructions/tax.rs @@ -0,0 +1,106 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TAX"; + +pub struct TAX {} + +impl TAX { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(TAX16{}), + false => Box::new(TAX8{}), + } + } +} + +impl CPUInstruction for TAX { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} +pub struct TAX8 {} + +impl CPUInstruction for TAX8 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.a as u8; + registers.set_low_x(result); + registers.set_negative_flag((result >> 7) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + +pub struct TAX16 {} + +impl CPUInstruction for TAX16 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.x = registers.a; + registers.set_negative_flag((registers.x >> 15) == 1); + registers.set_zero_flag(registers.x == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.a = 0xFFFF; + registers.x = 0x0000; + registers.set_16bit_mode(false); + registers.set_16bit_index(false); + let instruction = TAX8{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.get_negative_flag(), true); + assert_eq!(registers.get_zero_flag(), false); + assert_eq!(registers.x, 0x00FF); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.a = 0xF0F0; + registers.x = 0x0000; + registers.set_16bit_mode(true); + registers.set_16bit_index(true); + let instruction = TAX16{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.get_negative_flag(), true); + assert_eq!(registers.get_zero_flag(), false); + assert_eq!(registers.x, 0xF0F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/tay.rs b/snes-core/src/cpu/instructions/tay.rs new file mode 100644 index 0000000..f08e6b6 --- /dev/null +++ b/snes-core/src/cpu/instructions/tay.rs @@ -0,0 +1,107 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TAY"; + +pub struct TAY {} + +impl TAY { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(TAY16{}), + false => Box::new(TAY8{}), + } + } +} + +impl CPUInstruction for TAY { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct TAY8 {} + +impl CPUInstruction for TAY8 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.a as u8; + registers.set_low_y(result); + registers.set_negative_flag((result >> 7) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + +pub struct TAY16 {} + +impl CPUInstruction for TAY16 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.y = registers.a; + registers.set_negative_flag((registers.y >> 15) == 1); + registers.set_zero_flag(registers.y == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.a = 0xFFFF; + registers.y = 0x0000; + registers.set_16bit_mode(false); + registers.set_16bit_index(false); + let instruction = TAY8{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.get_negative_flag(), true); + assert_eq!(registers.get_zero_flag(), false); + assert_eq!(registers.y, 0x00FF); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.a = 0xF0F0; + registers.y = 0x0000; + registers.set_16bit_mode(true); + registers.set_16bit_index(true); + let instruction = TAY16{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.get_negative_flag(), true); + assert_eq!(registers.get_zero_flag(), false); + assert_eq!(registers.y, 0xF0F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/tcd.rs b/snes-core/src/cpu/instructions/tcd.rs new file mode 100644 index 0000000..a337478 --- /dev/null +++ b/snes-core/src/cpu/instructions/tcd.rs @@ -0,0 +1,44 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TCD"; + +pub struct TCD {} + +impl CPUInstruction for TCD { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.a; + registers.d = result; + registers.set_negative_flag((result >> 7) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.a = 0xF0F0; + registers.d = 0x0000; + let instruction = TCD{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.d, 0xF0F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/tcs.rs b/snes-core/src/cpu/instructions/tcs.rs new file mode 100644 index 0000000..5e619ae --- /dev/null +++ b/snes-core/src/cpu/instructions/tcs.rs @@ -0,0 +1,44 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TCS"; + +pub struct TCS {} + +impl CPUInstruction for TCS { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.a; + registers.sp = result; + registers.set_negative_flag((result >> 7) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.a = 0xF0F0; + registers.sp = 0x0000; + let instruction = TCS{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.sp, 0xF0F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/tdc.rs b/snes-core/src/cpu/instructions/tdc.rs new file mode 100644 index 0000000..16049f4 --- /dev/null +++ b/snes-core/src/cpu/instructions/tdc.rs @@ -0,0 +1,44 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TDC"; + +pub struct TDC {} + +impl CPUInstruction for TDC { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.d; + registers.a = result; + registers.set_negative_flag((result >> 7) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.a = 0x0000; + registers.d = 0xF0F0; + let instruction = TDC{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0xF0F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/trb.rs b/snes-core/src/cpu/instructions/trb.rs new file mode 100644 index 0000000..5b205f4 --- /dev/null +++ b/snes-core/src/cpu/instructions/trb.rs @@ -0,0 +1,82 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; +use crate::utils::addressing::AddressingMode; + +use super::read_write_common::{read_8bit_from_address, write_8bit_to_address, read_16bit_from_address, write_16bit_to_address}; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TRB"; + +pub struct TRB { + pub addressing_mode: AddressingMode, +} + +impl TRB { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(TRB16{addressing_mode: self.addressing_mode}), + false => Box::new(TRB8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for TRB { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct TRB8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for TRB8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_8bit_from_address(registers, bus, self.addressing_mode); + let result = (registers.a as u8) & value; + write_8bit_to_address(registers, bus, self.addressing_mode, result); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_test(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct TRB16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for TRB16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_16bit_from_address(registers, bus, self.addressing_mode); + let result = (registers.a) & value; + write_16bit_to_address(registers, bus, self.addressing_mode, result); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_test(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + } +} diff --git a/snes-core/src/cpu/instructions/tsb.rs b/snes-core/src/cpu/instructions/tsb.rs new file mode 100644 index 0000000..e329b95 --- /dev/null +++ b/snes-core/src/cpu/instructions/tsb.rs @@ -0,0 +1,80 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; +use crate::utils::addressing::AddressingMode; + +use super::read_write_common::{read_8bit_from_address, write_8bit_to_address, read_16bit_from_address, write_16bit_to_address}; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TSB"; + +pub struct TSB { + pub addressing_mode: AddressingMode, +} + +impl TSB { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(TSB16{addressing_mode: self.addressing_mode}), + false => Box::new(TSB8{addressing_mode: self.addressing_mode}), + } + } +} + +impl CPUInstruction for TSB { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct TSB8 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for TSB8 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_8bit_from_address(registers, bus, self.addressing_mode); + let result = (registers.a as u8) | value; + write_8bit_to_address(registers, bus, self.addressing_mode, result); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_test(registers, self.addressing_mode); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(false, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + +pub struct TSB16 { + addressing_mode: AddressingMode, +} + +impl CPUInstruction for TSB16 { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let value = read_16bit_from_address(registers, bus, self.addressing_mode); + let result = registers.a & value; + write_16bit_to_address(registers, bus, self.addressing_mode, result); + registers.set_zero_flag(result == 0); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + } +} diff --git a/snes-core/src/cpu/instructions/tsc.rs b/snes-core/src/cpu/instructions/tsc.rs new file mode 100644 index 0000000..c8f8222 --- /dev/null +++ b/snes-core/src/cpu/instructions/tsc.rs @@ -0,0 +1,44 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TSC"; + +pub struct TSC {} + +impl CPUInstruction for TSC { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.sp; + registers.a = result; + registers.set_negative_flag((result >> 7) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.a = 0x0000; + registers.sp = 0xF0F0; + let instruction = TSC{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0xF0F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/tsx.rs b/snes-core/src/cpu/instructions/tsx.rs new file mode 100644 index 0000000..43bf220 --- /dev/null +++ b/snes-core/src/cpu/instructions/tsx.rs @@ -0,0 +1,102 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TSX"; + +pub struct TSX {} + +impl TSX { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(TSX16{}), + false => Box::new(TSX8{}), + } + } +} + +impl CPUInstruction for TSX { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct TSX8 {} + +impl CPUInstruction for TSX8 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.sp as u8; + registers.set_low_x(result); + registers.set_negative_flag((result >> 7) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + +pub struct TSX16 {} + +impl CPUInstruction for TSX16 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.sp; + registers.x = result; + registers.set_negative_flag((result >> 15) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.x = 0x0000; + registers.sp = 0xF0F0; + registers.set_16bit_index(false); + let instruction = TSX8{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.x, 0x00F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.x = 0x0000; + registers.sp = 0xF0F0; + registers.set_16bit_index(true); + let instruction = TSX16{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.x, 0xF0F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/txa.rs b/snes-core/src/cpu/instructions/txa.rs new file mode 100644 index 0000000..db009a1 --- /dev/null +++ b/snes-core/src/cpu/instructions/txa.rs @@ -0,0 +1,102 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TXA"; + +pub struct TXA {} + +impl TXA { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(TXA16{}), + false => Box::new(TXA8{}), + } + } +} + +impl CPUInstruction for TXA { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct TXA8 {} + +impl CPUInstruction for TXA8 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.x as u8; + registers.set_low_a(result); + registers.set_negative_flag((result >> 7) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + +pub struct TXA16 {} + +impl CPUInstruction for TXA16 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.x; + registers.a = result; + registers.set_negative_flag((result >> 15) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.a = 0x0000; + registers.x = 0xF0F0; + registers.set_16bit_mode(false); + let instruction = TXA8{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x00F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.a = 0x0000; + registers.x = 0xF0F0; + registers.set_16bit_mode(true); + let instruction = TXA16{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0xF0F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/txs.rs b/snes-core/src/cpu/instructions/txs.rs new file mode 100644 index 0000000..dc2d1c8 --- /dev/null +++ b/snes-core/src/cpu/instructions/txs.rs @@ -0,0 +1,102 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TXS"; + +pub struct TXS {} + +impl TXS { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(TXS16{}), + false => Box::new(TXS8{}), + } + } +} + +impl CPUInstruction for TXS { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct TXS8 {} + +impl CPUInstruction for TXS8 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.x as u8; + registers.set_low_sp(result); + registers.set_negative_flag((result >> 7) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + +pub struct TXS16 {} + +impl CPUInstruction for TXS16 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.x; + registers.sp = result; + registers.set_negative_flag((result >> 15) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.sp = 0x0000; + registers.x = 0xF0F0; + registers.set_16bit_index(false); + let instruction = TXS8{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.sp, 0x00F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.sp = 0x0000; + registers.x = 0xF0F0; + registers.set_16bit_index(true); + let instruction = TXS16{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.sp, 0xF0F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/txy.rs b/snes-core/src/cpu/instructions/txy.rs new file mode 100644 index 0000000..fd7f2c6 --- /dev/null +++ b/snes-core/src/cpu/instructions/txy.rs @@ -0,0 +1,102 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TXY"; + +pub struct TXY {} + +impl TXY { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(TXY16{}), + false => Box::new(TXY8{}), + } + } +} + +impl CPUInstruction for TXY { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct TXY8 {} + +impl CPUInstruction for TXY8 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.x as u8; + registers.set_low_y(result); + registers.set_negative_flag((result >> 7) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + +pub struct TXY16 {} + +impl CPUInstruction for TXY16 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.x; + registers.y = result; + registers.set_negative_flag((result >> 15) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.y = 0x0000; + registers.x = 0xF0F0; + registers.set_16bit_index(false); + let instruction = TXY8{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.y, 0x00F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.y = 0x0000; + registers.x = 0xF0F0; + registers.set_16bit_index(true); + let instruction = TXY16{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.y, 0xF0F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/tya.rs b/snes-core/src/cpu/instructions/tya.rs new file mode 100644 index 0000000..981140f --- /dev/null +++ b/snes-core/src/cpu/instructions/tya.rs @@ -0,0 +1,101 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TYA"; + +pub struct TYA {} + +impl TYA { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_mode() { + true => Box::new(TYA16{}), + false => Box::new(TYA8{}), + } + } +} + +impl CPUInstruction for TYA { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct TYA8 {} + +impl CPUInstruction for TYA8 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.y as u8; + registers.set_low_a(result); + registers.set_negative_flag((result >> 7) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + +pub struct TYA16 {} + +impl CPUInstruction for TYA16 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.a = registers.y; + registers.set_negative_flag((registers.a >> 15) == 1); + registers.set_zero_flag(registers.a == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.a = 0x0000; + registers.y = 0xF0F0; + registers.set_16bit_mode(false); + let instruction = TYA8{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0x00F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.a = 0x0000; + registers.y = 0xF0F0; + registers.set_16bit_mode(true); + let instruction = TYA16{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0xF0F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/tyx.rs b/snes-core/src/cpu/instructions/tyx.rs new file mode 100644 index 0000000..33b3a89 --- /dev/null +++ b/snes-core/src/cpu/instructions/tyx.rs @@ -0,0 +1,102 @@ +use crate::cpu::cycles; +use crate::cpu::{bus::Bus, registers::Registers}; + +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "TYX"; + +pub struct TYX {} + +impl TYX { + fn determine_instruction(&self, registers: &Registers) -> Box { + match registers.is_16bit_index() { + true => Box::new(TYX16{}), + false => Box::new(TYX8{}), + } + } +} + +impl CPUInstruction for TYX { + fn execute(&self, registers: &mut Registers, bus: &mut Bus) { + let instruction = self.determine_instruction(registers); + instruction.execute(registers, bus); + } + + fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String { + let instruction = self.determine_instruction(registers); + instruction.mnemonic(registers, bus, opcode) + } +} + +pub struct TYX8 {} + +impl CPUInstruction for TYX8 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.y as u8; + registers.set_low_x(result); + registers.set_negative_flag((result >> 7) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + +pub struct TYX16 {} + +impl CPUInstruction for TYX16 { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let result = registers.y; + registers.x = result; + registers.set_negative_flag((result >> 15) == 1); + registers.set_zero_flag(result == 0); + let (bytes, cycles) = cycles::increment_cycles_transfer(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test_8bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.x = 0x0000; + registers.y = 0xF0F0; + registers.set_16bit_index(false); + let instruction = TYX8{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.x, 0x00F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } + + #[test] + fn test_16bit() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.emulation_mode = false; + registers.pc = 0x0000; + registers.x = 0x0000; + registers.y = 0xF0F0; + registers.set_16bit_index(true); + let instruction = TYX16{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.x, 0xF0F0); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/wai.rs b/snes-core/src/cpu/instructions/wai.rs new file mode 100644 index 0000000..9d9f4b2 --- /dev/null +++ b/snes-core/src/cpu/instructions/wai.rs @@ -0,0 +1,40 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "WAI"; + +pub struct WAI {} + +impl CPUInstruction for WAI { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.is_cpu_waiting_interrupt = true; + let (bytes, cycles) = cycles::increment_cycles_stp(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.is_cpu_waiting_interrupt = false; + registers.pc = 0x0000; + let instruction = WAI{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.is_cpu_waiting_interrupt, true); + assert_eq!(registers.cycles, 3); + } +} diff --git a/snes-core/src/cpu/instructions/wdm.rs b/snes-core/src/cpu/instructions/wdm.rs new file mode 100644 index 0000000..c0b2519 --- /dev/null +++ b/snes-core/src/cpu/instructions/wdm.rs @@ -0,0 +1,37 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "WDM"; + +pub struct WDM {} + +impl CPUInstruction for WDM { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let (bytes, cycles) = cycles::increment_cycles_wdm(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + let instruction = WDM{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x0002); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/instructions/xba.rs b/snes-core/src/cpu/instructions/xba.rs new file mode 100644 index 0000000..90fe6d8 --- /dev/null +++ b/snes-core/src/cpu/instructions/xba.rs @@ -0,0 +1,42 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "XBA"; + +pub struct XBA {} + +impl CPUInstruction for XBA { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.a = (registers.a << 8) | (registers.a >> 8); + registers.set_negative_flag(((registers.a as u8) >> 7) == 1); + registers.set_zero_flag((registers.a as u8) == 0); + let (bytes, cycles) = cycles::increment_cycles_xba(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.a = 0x11FF; + let instruction = XBA{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.a, 0xFF11); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 3); + } +} diff --git a/snes-core/src/cpu/instructions/xce.rs b/snes-core/src/cpu/instructions/xce.rs new file mode 100644 index 0000000..aea1182 --- /dev/null +++ b/snes-core/src/cpu/instructions/xce.rs @@ -0,0 +1,38 @@ +use crate::cpu::{bus::Bus, registers::Registers}; + +use crate::cpu::cycles; +use super::CPUInstruction; +use super::decoder_common; + +static INSTR_NAME: &'static str = "XCE"; + +pub struct XCE {} + +impl CPUInstruction for XCE { + fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + registers.exchange_carry_and_emulation(); + let (bytes, cycles) = cycles::increment_cycles_exchange(); + registers.increment_pc(bytes); registers.cycles += cycles; + } + + fn mnemonic(&self, _registers: &Registers, _bus: &Bus, opcode: u8) -> String { + decoder_common::mnemonic_single_byte_instr(opcode, INSTR_NAME) + } +} + + +#[cfg(test)] +mod cpu_instructions_tests { + use super::*; + + #[test] + fn test() { + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + let instruction = XCE{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + } +} diff --git a/snes-core/src/cpu/registers.rs b/snes-core/src/cpu/registers.rs index 78bd177..22c608e 100644 --- a/snes-core/src/cpu/registers.rs +++ b/snes-core/src/cpu/registers.rs @@ -11,6 +11,9 @@ pub struct Registers { pub dbr: u8, // Data bank register pub pc: u16, // Program counter pub emulation_mode: bool, + pub is_cpu_stopped: bool, + pub is_cpu_waiting_interrupt: bool, + pub cycles: usize, } impl Registers { @@ -26,6 +29,9 @@ impl Registers { dbr: 0, pc: 0, emulation_mode: true, + is_cpu_stopped: false, + is_cpu_waiting_interrupt: false, + cycles: 0, } } diff --git a/snes-core/src/cpu/vectors.rs b/snes-core/src/cpu/vectors.rs index 7fae4bc..81e10b2 100644 --- a/snes-core/src/cpu/vectors.rs +++ b/snes-core/src/cpu/vectors.rs @@ -1,4 +1,4 @@ -use super::cpu::CPU; +use super::{cpu::CPU, instructions::{phk::PHK, CPUInstruction, php::PHP, push_common}}; use crate::cpu::bus::Bus; #[derive(Copy, Clone)] @@ -35,7 +35,7 @@ impl CPU { let base_address = Vector::Reset.get_base_address(); let reset_vector = CPU::get_vector(base_address, bus); self.registers.pc = reset_vector; - self.is_stopped = false; + self.registers.is_cpu_stopped = false; } fn get_vector_from_interrupts(&self) -> Option { @@ -47,13 +47,14 @@ impl CPU { fn push_emulation_interrupt(&mut self, bus: &mut Bus) { if !self.registers.emulation_mode { - self.phk(bus); + PHK{}.execute(&mut self.registers, bus); } - self.do_push(bus, &[ + let values = [ (self.registers.pc >> 8) as u8, self.registers.pc as u8, - ]); - self.php(bus); + ]; + push_common::do_push(&mut self.registers, bus, &values); + PHP{}.execute(&mut self.registers, bus); } pub fn handle_interrupts(&mut self, bus: &mut Bus) { @@ -75,9 +76,9 @@ mod cpu_vectors_tests { fn test_reset_vector() { let mut cpu = CPU::new(); let mut bus = Bus::new(); - cpu.is_stopped = true; + cpu.registers.is_cpu_stopped = true; // TODO: test that the PC register got the right vector cpu.reset_vector(&mut bus); - assert_eq!(cpu.is_stopped, false); + assert_eq!(cpu.registers.is_cpu_stopped, false); } } \ No newline at end of file diff --git a/snes-core/src/emulator.rs b/snes-core/src/emulator.rs index a51d922..f305bd8 100644 --- a/snes-core/src/emulator.rs +++ b/snes-core/src/emulator.rs @@ -20,9 +20,9 @@ impl Emulator { pub fn tick(&mut self) { self.cpu.tick(&mut self.bus); - self.bus.ppu.tick(self.cpu.cycles); + self.bus.ppu.tick(self.cpu.registers.cycles); - self.cpu.cycles = 0; + self.cpu.registers.cycles = 0; } pub fn loop_frame(&mut self) { diff --git a/snes-core/src/utils/addressing.rs b/snes-core/src/utils/addressing.rs index 440ec57..6f45982 100644 --- a/snes-core/src/utils/addressing.rs +++ b/snes-core/src/utils/addressing.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use crate::cpu::bus::Bus; /// OPCODE #const @@ -114,6 +116,16 @@ pub enum IndexRegister { X, Y, } +impl Display for IndexRegister { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let name = match self { + IndexRegister::X => "X", + IndexRegister::Y => "Y", + }; + write!(f, "{}", name) + } +} + #[derive(Copy, Clone)] pub enum AddressingMode { Accumulator, diff --git a/snes-frontend/src/cpu.rs b/snes-frontend/src/cpu.rs index 364711a..f19950f 100644 --- a/snes-frontend/src/cpu.rs +++ b/snes-frontend/src/cpu.rs @@ -1,5 +1,5 @@ extern crate snes_core; -use snes_core::emulator::Emulator; +use snes_core::{emulator::Emulator, cpu::instructions::mapper::map_opcode_to_instruction}; pub struct CPUDisassembler { _history_limit: usize, @@ -9,406 +9,7 @@ pub struct CPUDisassembler { impl CPUDisassembler { pub fn get_next_instruction(emulator: &Emulator) -> String { let opcode = emulator.bus.read_external(emulator.cpu.registers.get_pc_address()); - let is_cpu_16bit = emulator.cpu.registers.is_16bit_mode(); - let is_index_16bit = emulator.cpu.registers.is_16bit_index(); - let next_byte = emulator.bus.read_external(emulator.cpu.registers.get_pc_address() + 1); - let next_second_byte = emulator.bus.read_external(emulator.cpu.registers.get_pc_address() + 2); - let next_third_byte = emulator.bus.read_external(emulator.cpu.registers.get_pc_address() + 3); - let next_word = { - (emulator.bus.read_external(emulator.cpu.registers.get_pc_address() + 1) as u16) | - ((emulator.bus.read_external(emulator.cpu.registers.get_pc_address() + 2) as u16) << 8) - }; - let next_word_long = { - (emulator.bus.read_external(emulator.cpu.registers.get_pc_address() + 1) as u32) | - ((emulator.bus.read_external(emulator.cpu.registers.get_pc_address() + 2) as u32) << 8) | - ((emulator.bus.read_external(emulator.cpu.registers.get_pc_address() + 3) as u32) << 16) - }; - match opcode { - // ADC - 0x69 => match is_cpu_16bit { - true => format!("{:02X} {:02X} {:02X} __ | ADC #${:04X}", opcode, next_byte, next_second_byte, next_word), - false => format!("{:02X} {:02X} __ __ | ADC #${:02X}", opcode, next_byte, next_byte), - }, - 0x6D => format!("{:02X} {:02X} {:02X} __ | ADC ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x6F => format!("{:02X} {:02X} {:02X} {:02X} | ADC ${:06X}", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0x65 => format!("{:02X} {:02X} __ __ | ADC ${:02X} | dp", opcode, next_byte, next_byte), - 0x72 => format!("{:02X} {:02X} __ __ | ADC (${:02X})", opcode, next_byte, next_byte), - 0x67 => format!("{:02X} {:02X} __ __ | ADC [${:02X}]", opcode, next_byte, next_byte), - 0x7D => format!("{:02X} {:02X} {:02X} __ | ADC ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0x7F => format!("{:02X} {:02X} {:02X} {:02X} | ADC ${:06X}, X", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0x79 => format!("{:02X} {:02X} {:02X} __ | ADC ${:04X}, Y", opcode, next_byte, next_second_byte, next_word), - 0x75 => format!("{:02X} {:02X} __ __ | ADC ${:02X}, X | dp", opcode, next_byte, next_byte), - 0x61 => format!("{:02X} {:02X} __ __ | ADC (${:02X}, X) | dp", opcode, next_byte, next_byte), - 0x71 => format!("{:02X} {:02X} __ __ | ADC (${:02X}), Y | dp", opcode, next_byte, next_byte), - 0x77 => format!("{:02X} {:02X} __ __ | ADC [${:02X}], Y | dp", opcode, next_byte, next_byte), - 0x63 => format!("{:02X} {:02X} __ __ | ADC {:02X}, S | sr", opcode, next_byte, next_byte), - 0x73 => format!("{:02X} {:02X} __ __ | ADC ({:02X}, S), Y | sr", opcode, next_byte, next_byte), - // AND - 0x29 => match is_cpu_16bit { - true => format!("{:02X} {:02X} {:02X} __ | AND #${:04X}", opcode, next_byte, next_second_byte, next_word), - false => format!("{:02X} {:02X} __ __ | AND #${:02X}", opcode, next_byte, next_byte), - }, - 0x2D => format!("{:02X} {:02X} {:02X} __ | AND ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x2F => format!("{:02X} {:02X} {:02X} {:02X} | AND ${:06X}", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0x25 => format!("{:02X} {:02X} __ __ | AND ${:02X} | dp", opcode, next_byte, next_byte), - 0x32 => format!("{:02X} {:02X} __ __ | AND (${:02X})", opcode, next_byte, next_byte), - 0x27 => format!("{:02X} {:02X} __ __ | AND [${:02X}]", opcode, next_byte, next_byte), - 0x3D => format!("{:02X} {:02X} {:02X} __ | AND ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0x3F => format!("{:02X} {:02X} {:02X} {:02X} | AND ${:06X}, X", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0x39 => format!("{:02X} {:02X} {:02X} __ | AND ${:04X}, Y", opcode, next_byte, next_second_byte, next_word), - 0x35 => format!("{:02X} {:02X} __ __ | AND ${:02X}, X | dp", opcode, next_byte, next_byte), - 0x21 => format!("{:02X} {:02X} __ __ | AND (${:02X}, X) | dp", opcode, next_byte, next_byte), - 0x31 => format!("{:02X} {:02X} __ __ | AND (${:02X}), Y | dp", opcode, next_byte, next_byte), - 0x37 => format!("{:02X} {:02X} __ __ | AND [${:02X}], Y | dp", opcode, next_byte, next_byte), - 0x23 => format!("{:02X} {:02X} __ __ | AND {:02X}, S | sr", opcode, next_byte, next_byte), - 0x33 => format!("{:02X} {:02X} __ __ | AND ({:02X}, S), Y | sr", opcode, next_byte, next_byte), - // ASL - 0x0A => format!("{:02X} __ __ __ | ASL A", opcode), - 0x0E => format!("{:02X} {:02X} {:02X} __ | ASL ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x06 => format!("{:02X} {:02X} __ __ | ASL ${:02X} | dp", opcode, next_byte, next_byte), - 0x1E => format!("{:02X} {:02X} {:02X} __ | ASL ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0x16 => format!("{:02X} {:02X} __ __ | ASL ${:02X}, X | dp", opcode, next_byte, next_byte), - // BCC - 0x90 => format!("{:02X} {:02X} __ __ | BCC ${:02X}", opcode, next_byte, next_byte), - // BCS - 0xB0 => format!("{:02X} {:02X} __ __ | BCS ${:02X}", opcode, next_byte, next_byte), - // BEQ - 0xF0 => format!("{:02X} {:02X} __ __ | BEQ ${:02X}", opcode, next_byte, next_byte), - // BNE - 0xD0 => format!("{:02X} {:02X} __ __ | BNE ${:02X}", opcode, next_byte, next_byte), - // BMI - 0x30 => format!("{:02X} {:02X} __ __ | BMI ${:02X}", opcode, next_byte, next_byte), - // BPL - 0x10 => format!("{:02X} {:02X} __ __ | BPL ${:02X}", opcode, next_byte, next_byte), - // BRA - 0x80 => format!("{:02X} {:02X} __ __ | BRA ${:02X}", opcode, next_byte, next_byte), - // BRK - 0x00 => format!("{:02X} __ __ __ | BRK", opcode), - // BRL - 0x82 => format!("{:02X} {:02X} {:02X} __ | BRL ${:04X}", opcode, next_byte, next_second_byte, next_word), - // BVC - 0x50 => format!("{:02X} {:02X} {:02X} __ | BVC ${:04X}", opcode, next_byte, next_second_byte, next_word), - // BVS - 0x70 => format!("{:02X} {:02X} {:02X} __ | BVS ${:04X}", opcode, next_byte, next_second_byte, next_word), - // BIT - 0x89 => match is_cpu_16bit { - true => format!("{:02X} {:02X} {:02X} __ | BIT #${:04X}", opcode, next_byte, next_second_byte, next_word), - false => format!("{:02X} {:02X} __ __ | BIT #${:02X}", opcode, next_byte, next_byte), - }, - 0x2C => format!("{:02X} {:02X} {:02X} __ | BIT ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x24 => format!("{:02X} {:02X} __ __ | BIT ${:02X} | dp", opcode, next_byte, next_byte), - 0x3C => format!("{:02X} {:02X} {:02X} __ | ADC ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0x34 => format!("{:02X} {:02X} __ __ | AND ${:02X}, X | dp", opcode, next_byte, next_byte), - // CLC - 0x18 => format!("{:02X} __ __ __ | CLC", opcode), - // CLD - 0xD8 => format!("{:02X} __ __ __ | CLD", opcode), - // CLI - 0x58 => format!("{:02X} __ __ __ | CLI", opcode), - // CLV - 0xB8 => format!("{:02X} __ __ __ | CLV", opcode), - // CMP - 0xC9 => match is_cpu_16bit { - true => format!("{:02X} {:02X} {:02X} __ | CMP #${:04X}", opcode, next_byte, next_second_byte, next_word), - false => format!("{:02X} {:02X} __ __ | CMP #${:02X}", opcode, next_byte, next_byte), - }, - 0xCD => format!("{:02X} {:02X} {:02X} __ | CMP ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0xCF => format!("{:02X} {:02X} {:02X} {:02X} | CMP ${:06X}", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0xC5 => format!("{:02X} {:02X} __ __ | CMP ${:02X} | dp", opcode, next_byte, next_byte), - 0xD2 => format!("{:02X} {:02X} __ __ | CMP (${:02X})", opcode, next_byte, next_byte), - 0xC7 => format!("{:02X} {:02X} __ __ | CMP [${:02X}]", opcode, next_byte, next_byte), - 0xDD => format!("{:02X} {:02X} {:02X} __ | CMP ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0xDF => format!("{:02X} {:02X} {:02X} {:02X} | CMP ${:06X}, X", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0xD9 => format!("{:02X} {:02X} {:02X} __ | CMP ${:04X}, Y", opcode, next_byte, next_second_byte, next_word), - 0xD5 => format!("{:02X} {:02X} __ __ | CMP ${:02X}, X | dp", opcode, next_byte, next_byte), - 0xC1 => format!("{:02X} {:02X} __ __ | CMP (${:02X}, X) | dp", opcode, next_byte, next_byte), - 0xD1 => format!("{:02X} {:02X} __ __ | CMP (${:02X}), Y | dp", opcode, next_byte, next_byte), - 0xD7 => format!("{:02X} {:02X} __ __ | CMP [${:02X}], Y | dp", opcode, next_byte, next_byte), - 0xC3 => format!("{:02X} {:02X} __ __ | CMP {:02X}, S | sr", opcode, next_byte, next_byte), - 0xD3 => format!("{:02X} {:02X} __ __ | CMP ({:02X}, S), Y | sr", opcode, next_byte, next_byte), - // COP - 0x02 => match is_index_16bit { - true => format!("{:02X} {:02X} {:02X} __ | COP #${:04X}", opcode, next_byte, next_second_byte, next_word), - false => format!("{:02X} {:02X} __ __ | COP #${:02X}", opcode, next_byte, next_byte), - }, - // CPX - 0xE0 => match is_index_16bit { - true => format!("{:02X} {:02X} {:02X} __ | CPX #${:04X}", opcode, next_byte, next_second_byte, next_word), - false => format!("{:02X} {:02X} __ __ | CPX #${:02X}", opcode, next_byte, next_byte), - }, - 0xEC => format!("{:02X} {:02X} {:02X} __ | CPX ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0xE4 => format!("{:02X} {:02X} __ __ | CPX ${:02X} | dp", opcode, next_byte, next_byte), - // CPY - 0xC0 => match is_index_16bit { - true => format!("{:02X} {:02X} {:02X} __ | CPY #${:04X}", opcode, next_byte, next_second_byte, next_word), - false => format!("{:02X} {:02X} __ __ | CPY #${:02X}", opcode, next_byte, next_byte), - }, - 0xCC => format!("{:02X} {:02X} {:02X} __ | CPY ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0xC4 => format!("{:02X} {:02X} __ __ | CPY ${:02X} | dp", opcode, next_byte, next_byte), - // DEC - 0x3A => format!("{:02X} __ __ __ | DEC A", opcode), - 0xCE => format!("{:02X} {:02X} {:02X} __ | DEC ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0xC6 => format!("{:02X} {:02X} __ __ | DEC ${:02X} | dp", opcode, next_byte, next_byte), - 0xDE => format!("{:02X} {:02X} {:02X} __ | DEC ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0xD6 => format!("{:02X} {:02X} __ __ | DEC ${:02X}, X | dp", opcode, next_byte, next_byte), - // DEX - 0xCA => format!("{:02X} __ __ __ | DEX", opcode), - // DEY - 0x88 => format!("{:02X} __ __ __ | DEY", opcode), - // EOR - 0x49 => match is_cpu_16bit { - true => format!("{:02X} {:02X} {:02X} __ | EOR #${:04X}", opcode, next_byte, next_second_byte, next_word), - false => format!("{:02X} {:02X} __ __ | EOR #${:02X}", opcode, next_byte, next_byte), - }, - 0x4D => format!("{:02X} {:02X} {:02X} __ | EOR ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x4F => format!("{:02X} {:02X} {:02X} {:02X} | EOR ${:06X}", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0x45 => format!("{:02X} {:02X} __ __ | EOR ${:02X} | dp", opcode, next_byte, next_byte), - 0x52 => format!("{:02X} {:02X} __ __ | EOR (${:02X})", opcode, next_byte, next_byte), - 0x47 => format!("{:02X} {:02X} __ __ | EOR [${:02X}]", opcode, next_byte, next_byte), - 0x5D => format!("{:02X} {:02X} {:02X} __ | EOR ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0x5F => format!("{:02X} {:02X} {:02X} {:02X} | EOR ${:06X}, X", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0x59 => format!("{:02X} {:02X} {:02X} __ | EOR ${:04X}, Y", opcode, next_byte, next_second_byte, next_word), - 0x55 => format!("{:02X} {:02X} __ __ | EOR ${:02X}, X | dp", opcode, next_byte, next_byte), - 0x41 => format!("{:02X} {:02X} __ __ | EOR (${:02X}, X) | dp", opcode, next_byte, next_byte), - 0x51 => format!("{:02X} {:02X} __ __ | EOR (${:02X}), Y | dp", opcode, next_byte, next_byte), - 0x57 => format!("{:02X} {:02X} __ __ | EOR [${:02X}], Y | dp", opcode, next_byte, next_byte), - 0x43 => format!("{:02X} {:02X} __ __ | EOR {:02X}, S | sr", opcode, next_byte, next_byte), - 0x53 => format!("{:02X} {:02X} __ __ | EOR ({:02X}, S), Y | sr", opcode, next_byte, next_byte), - // INC - 0x1A => format!("{:02X} __ __ __ | INC A", opcode), - 0xEE => format!("{:02X} {:02X} {:02X} __ | INC ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0xE6 => format!("{:02X} {:02X} __ __ | INC ${:02X} | dp", opcode, next_byte, next_byte), - 0xFE => format!("{:02X} {:02X} {:02X} __ | INC ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0xF6 => format!("{:02X} {:02X} __ __ | INC ${:02X}, X | dp", opcode, next_byte, next_byte), - // INX - 0xE8 => format!("{:02X} __ __ __ | INX", opcode), - // INY - 0xC8 => format!("{:02X} __ __ __ | INY", opcode), - // JMP - 0x4C => format!("{:02X} {:02X} {:02X} __ | JMP ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x6C => format!("{:02X} {:02X} {:02X} __ | JMP (${:04X})", opcode, next_byte, next_second_byte, next_word), - 0x7C => format!("{:02X} {:02X} {:02X} __ | JMP (${:04X}, X)", opcode, next_byte, next_second_byte, next_word), - 0x5C => format!("{:02X} {:02X} {:02X} {:02X} | JMP ${:06X}", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0xDC => format!("{:02X} {:02X} {:02X} __ | JMP [${:04X}]", opcode, next_byte, next_second_byte, next_word), - // JSR - 0x20 => format!("{:02X} {:02X} {:02X} __ | JSR ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0xFC => format!("{:02X} {:02X} {:02X} __ | JSR (${:04X}, X)", opcode, next_byte, next_second_byte, next_word), - 0x22 => format!("{:02X} {:02X} {:02X} {:02X} | JSR ${:06X}", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - // LDA - 0xA9 => match is_cpu_16bit { - true => format!("{:02X} {:02X} {:02X} __ | LDA #${:04X}", opcode, next_byte, next_second_byte, next_word), - false => format!("{:02X} {:02X} __ __ | LDA #${:02X}", opcode, next_byte, next_byte), - }, - 0xAD => format!("{:02X} {:02X} {:02X} __ | LDA ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0xAF => format!("{:02X} {:02X} {:02X} {:02X} | LDA ${:06X}", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0xA5 => format!("{:02X} {:02X} __ __ | LDA ${:02X} | dp", opcode, next_byte, next_byte), - 0xB2 => format!("{:02X} {:02X} __ __ | LDA (${:02X})", opcode, next_byte, next_byte), - 0xA7 => format!("{:02X} {:02X} __ __ | LDA [${:02X}]", opcode, next_byte, next_byte), - 0xBD => format!("{:02X} {:02X} {:02X} __ | LDA ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0xBF => format!("{:02X} {:02X} {:02X} {:02X} | LDA ${:06X}, X", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0xB9 => format!("{:02X} {:02X} {:02X} __ | LDA ${:04X}, Y", opcode, next_byte, next_second_byte, next_word), - 0xB5 => format!("{:02X} {:02X} __ __ | LDA ${:02X}, X | dp", opcode, next_byte, next_byte), - 0xA1 => format!("{:02X} {:02X} __ __ | LDA (${:02X}, X) | dp", opcode, next_byte, next_byte), - 0xB1 => format!("{:02X} {:02X} __ __ | LDA (${:02X}), Y | dp", opcode, next_byte, next_byte), - 0xB7 => format!("{:02X} {:02X} __ __ | LDA [${:02X}], Y | dp", opcode, next_byte, next_byte), - 0xA3 => format!("{:02X} {:02X} __ __ | LDA {:02X}, S | sr", opcode, next_byte, next_byte), - 0xB3 => format!("{:02X} {:02X} __ __ | LDA ({:02X}, S), Y | sr", opcode, next_byte, next_byte), - // LDX - 0xA2 => match is_index_16bit { - true => format!("{:02X} {:02X} {:02X} __ | LDX #${:04X}", opcode, next_byte, next_second_byte, next_word), - false => format!("{:02X} {:02X} __ __ | LDX #${:02X}", opcode, next_byte, next_byte), - }, - 0xAE => format!("{:02X} {:02X} {:02X} __ | LDX ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0xA6 => format!("{:02X} {:02X} __ __ | LDX ${:02X} | dp", opcode, next_byte, next_byte), - 0xBE => format!("{:02X} {:02X} {:02X} __ | LDX ${:04X}, Y", opcode, next_byte, next_second_byte, next_word), - 0xB6 => format!("{:02X} {:02X} __ __ | LDX ${:02X}, Y | dp", opcode, next_byte, next_byte), - // LDY - 0xA0 => match is_index_16bit { - true => format!("{:02X} {:02X} {:02X} __ | LDY #${:04X}", opcode, next_byte, next_second_byte, next_word), - false => format!("{:02X} {:02X} __ __ | LDY #${:02X}", opcode, next_byte, next_byte), - }, - 0xAC => format!("{:02X} {:02X} {:02X} __ | LDY ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0xA4 => format!("{:02X} {:02X} __ __ | LDY ${:02X} | dp", opcode, next_byte, next_byte), - 0xB4 => format!("{:02X} {:02X} {:02X} __ | LDY ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0xBC => format!("{:02X} {:02X} __ __ | LDY ${:02X}, X | dp", opcode, next_byte, next_byte), - // LSR - 0x4A => format!("{:02X} __ __ __ | LSR A", opcode), - 0x4E => format!("{:02X} {:02X} {:02X} __ | LSR ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x46 => format!("{:02X} {:02X} __ __ | LSR ${:02X} | dp", opcode, next_byte, next_byte), - 0x5E => format!("{:02X} {:02X} {:02X} __ | LSR ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0x56 => format!("{:02X} {:02X} __ __ | LSR ${:02X}, X | dp", opcode, next_byte, next_byte), - // MVN - 0x54 => format!("{:02X} {:02X} {:02X} __ | MVN ${:02X},${:02X}", opcode, next_byte, next_second_byte, next_second_byte, next_byte), - // MVP - 0x44 => format!("{:02X} {:02X} {:02X} __ | MVP ${:02X},${:02X}", opcode, next_byte, next_second_byte, next_second_byte, next_byte), - // NOP - 0xEA => format!("{:02X} __ __ __ | NOP", opcode), - // ORA - 0x09 => match is_cpu_16bit { - true => format!("{:02X} {:02X} {:02X} __ | ORA #${:04X}", opcode, next_byte, next_second_byte, next_word), - false => format!("{:02X} {:02X} __ __ | ORA #${:02X}", opcode, next_byte, next_byte), - }, - 0x0D => format!("{:02X} {:02X} {:02X} __ | ORA ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x0F => format!("{:02X} {:02X} {:02X} {:02X} | ORA ${:06X}", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0x05 => format!("{:02X} {:02X} __ __ | ORA ${:02X} | dp", opcode, next_byte, next_byte), - 0x12 => format!("{:02X} {:02X} __ __ | ORA (${:02X})", opcode, next_byte, next_byte), - 0x07 => format!("{:02X} {:02X} __ __ | ORA [${:02X}]", opcode, next_byte, next_byte), - 0x1D => format!("{:02X} {:02X} {:02X} __ | ORA ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0x1F => format!("{:02X} {:02X} {:02X} {:02X} | ORA ${:06X}, X", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0x19 => format!("{:02X} {:02X} {:02X} __ | ORA ${:04X}, Y", opcode, next_byte, next_second_byte, next_word), - 0x15 => format!("{:02X} {:02X} __ __ | ORA ${:02X}, X | dp", opcode, next_byte, next_byte), - 0x01 => format!("{:02X} {:02X} __ __ | ORA (${:02X}, X) | dp", opcode, next_byte, next_byte), - 0x11 => format!("{:02X} {:02X} __ __ | ORA (${:02X}), Y | dp", opcode, next_byte, next_byte), - 0x17 => format!("{:02X} {:02X} __ __ | ORA [${:02X}], Y | dp", opcode, next_byte, next_byte), - 0x03 => format!("{:02X} {:02X} __ __ | ORA {:02X}, S | sr", opcode, next_byte, next_byte), - 0x13 => format!("{:02X} {:02X} __ __ | ORA ({:02X}, S), Y | sr", opcode, next_byte, next_byte), - // PEA - 0xF4 => format!("{:02X} {:02X} {:02X} __ | PEA ${:04X}", opcode, next_byte, next_second_byte, next_word), - // PEI - 0xD4 => format!("{:02X} {:02X} __ __ | PEI (${:02X}) | dp", opcode, next_byte, next_byte), - // PER - 0x62 => format!("{:02X} {:02X} {:02X} __ | PER ${:04X}", opcode, next_byte, next_second_byte, next_word), - // PHA - 0x48 => format!("{:02X} __ __ __ | PHA", opcode), - // PHB - 0x8B => format!("{:02X} __ __ __ | PHB", opcode), - // PHD - 0x0B => format!("{:02X} __ __ __ | PHD", opcode), - // PHK - 0x4B => format!("{:02X} __ __ __ | PHK", opcode), - // PHP - 0x08 => format!("{:02X} __ __ __ | PHP", opcode), - // PHX - 0xDA => format!("{:02X} __ __ __ | PHX", opcode), - // PHY - 0x5A => format!("{:02X} __ __ __ | PHY", opcode), - // PLA - 0x68 => format!("{:02X} __ __ __ | PLA", opcode), - // PLB - 0xAB => format!("{:02X} __ __ __ | PLB", opcode), - // PLD - 0x2B => format!("{:02X} __ __ __ | PLD", opcode), - // PLP - 0x28 => format!("{:02X} __ __ __ | PLP", opcode), - // PLX - 0xFA => format!("{:02X} __ __ __ | PLX", opcode), - // PLY - 0x7A => format!("{:02X} __ __ __ | PLY", opcode), - // REP - 0xC2 => format!("{:02X} {:02X} __ __ | REP #${:02X}", opcode, next_byte, next_byte), - // ROL - 0x2A => format!("{:02X} __ __ __ | ROL A", opcode), - 0x2E => format!("{:02X} {:02X} {:02X} __ | ROL ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x26 => format!("{:02X} {:02X} __ __ | ROL ${:02X} | dp", opcode, next_byte, next_byte), - 0x3E => format!("{:02X} {:02X} {:02X} __ | ROL ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0x36 => format!("{:02X} {:02X} __ __ | ROL ${:02X}, X | dp", opcode, next_byte, next_byte), - // ROR - 0x6A => format!("{:02X} __ __ __ | ROR A", opcode), - 0x6E => format!("{:02X} {:02X} {:02X} __ | ROR ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x66 => format!("{:02X} {:02X} __ __ | ROR ${:02X} | dp", opcode, next_byte, next_byte), - 0x7E => format!("{:02X} {:02X} {:02X} __ | ROR ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0x76 => format!("{:02X} {:02X} __ __ | ROR ${:02X}, X | dp", opcode, next_byte, next_byte), - // RTI - 0x40 => format!("{:02X} __ __ __ | RTI", opcode), - // RTL - 0x6B => format!("{:02X} __ __ __ | RTL", opcode), - // RTS - 0x60 => format!("{:02X} __ __ __ | RTS", opcode), - // SBC - 0xE9 => match is_cpu_16bit { - true => format!("{:02X} {:02X} {:02X} __ | SBC #${:04X}", opcode, next_byte, next_second_byte, next_word), - false => format!("{:02X} {:02X} __ __ | SBC #${:02X}", opcode, next_byte, next_byte), - }, - 0xED => format!("{:02X} {:02X} {:02X} __ | SBC ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0xEF => format!("{:02X} {:02X} {:02X} {:02X} | SBC ${:06X}", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0xE5 => format!("{:02X} {:02X} __ __ | SBC ${:02X} | dp", opcode, next_byte, next_byte), - 0xF2 => format!("{:02X} {:02X} __ __ | SBC (${:02X})", opcode, next_byte, next_byte), - 0xE7 => format!("{:02X} {:02X} __ __ | SBC [${:02X}]", opcode, next_byte, next_byte), - 0xFD => format!("{:02X} {:02X} {:02X} __ | SBC ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0xFF => format!("{:02X} {:02X} {:02X} {:02X} | SBC ${:06X}, X", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0xF9 => format!("{:02X} {:02X} {:02X} __ | SBC ${:04X}, Y", opcode, next_byte, next_second_byte, next_word), - 0xF5 => format!("{:02X} {:02X} __ __ | SBC ${:02X}, X | dp", opcode, next_byte, next_byte), - 0xE1 => format!("{:02X} {:02X} __ __ | SBC (${:02X}, X) | dp", opcode, next_byte, next_byte), - 0xF1 => format!("{:02X} {:02X} __ __ | SBC (${:02X}), Y | dp", opcode, next_byte, next_byte), - 0xF7 => format!("{:02X} {:02X} __ __ | SBC [${:02X}], Y | dp", opcode, next_byte, next_byte), - 0xE3 => format!("{:02X} {:02X} __ __ | SBC {:02X}, S | sr", opcode, next_byte, next_byte), - 0xF3 => format!("{:02X} {:02X} __ __ | SBC ({:02X}, S), Y | sr", opcode, next_byte, next_byte), - // SEC - 0x38 => format!("{:02X} __ __ __ | SEC", opcode), - // SED - 0xF8 => format!("{:02X} __ __ __ | SED", opcode), - // SEI - 0x78 => format!("{:02X} __ __ __ | SEI", opcode), - // SEP - 0xE2 => format!("{:02X} {:02X} __ __ | SEP #${:02X}", opcode, next_byte, next_byte), - // STA - 0x8D => format!("{:02X} {:02X} {:02X} __ | STA ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x8F => format!("{:02X} {:02X} {:02X} {:02X} | STA ${:06X}", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0x85 => format!("{:02X} {:02X} __ __ | STA ${:02X} | dp", opcode, next_byte, next_byte), - 0x92 => format!("{:02X} {:02X} __ __ | STA (${:02X})", opcode, next_byte, next_byte), - 0x87 => format!("{:02X} {:02X} __ __ | STA [${:02X}]", opcode, next_byte, next_byte), - 0x9D => format!("{:02X} {:02X} {:02X} __ | STA ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0x9F => format!("{:02X} {:02X} {:02X} {:02X} | STA ${:06X}, X", opcode, next_byte, next_second_byte, next_third_byte, next_word_long), - 0x99 => format!("{:02X} {:02X} {:02X} __ | STA ${:04X}, Y", opcode, next_byte, next_second_byte, next_word), - 0x95 => format!("{:02X} {:02X} __ __ | STA ${:02X}, X | dp", opcode, next_byte, next_byte), - 0x81 => format!("{:02X} {:02X} __ __ | STA (${:02X}, X) | dp", opcode, next_byte, next_byte), - 0x91 => format!("{:02X} {:02X} __ __ | STA (${:02X}), Y | dp", opcode, next_byte, next_byte), - 0x97 => format!("{:02X} {:02X} __ __ | STA [${:02X}], Y | dp", opcode, next_byte, next_byte), - 0x83 => format!("{:02X} {:02X} __ __ | STA {:02X}, S | sr", opcode, next_byte, next_byte), - 0x93 => format!("{:02X} {:02X} __ __ | STA ({:02X}, S), Y | sr", opcode, next_byte, next_byte), - // STP - 0xDB => format!("{:02X} __ __ __ | STP", opcode), - // STX - 0x8E => format!("{:02X} {:02X} {:02X} __ | STX ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x86 => format!("{:02X} {:02X} __ __ | STX ${:02X} | dp", opcode, next_byte, next_byte), - 0x96 => format!("{:02X} {:02X} __ __ | STX ${:02X}, Y | dp", opcode, next_byte, next_byte), - // STY - 0x8C => format!("{:02X} {:02X} {:02X} __ | STY ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x84 => format!("{:02X} {:02X} __ __ | STY ${:02X} | dp", opcode, next_byte, next_byte), - 0x94 => format!("{:02X} {:02X} __ __ | STY ${:02X}, X | dp", opcode, next_byte, next_byte), - // STZ - 0x9C => format!("{:02X} {:02X} {:02X} __ | STZ ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x64 => format!("{:02X} {:02X} __ __ | STZ ${:02X} | dp", opcode, next_byte, next_byte), - 0x9E => format!("{:02X} {:02X} {:02X} __ | STZ ${:04X}, X", opcode, next_byte, next_second_byte, next_word), - 0x74 => format!("{:02X} {:02X} __ __ | STZ ${:02X}, X | dp", opcode, next_byte, next_byte), - // TAX - 0xAA => format!("{:02X} __ __ __ | TAX", opcode), - // TAY - 0xA8 => format!("{:02X} __ __ __ | TAY", opcode), - // TCD - 0x5B => format!("{:02X} __ __ __ | TCD", opcode), - // TCS - 0x1B => format!("{:02X} __ __ __ | TCS", opcode), - // TCD - 0x7B => format!("{:02X} __ __ __ | TDC", opcode), - // TRB - 0x1C => format!("{:02X} {:02X} {:02X} __ | TRB ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x14 => format!("{:02X} {:02X} __ __ | TRB ${:02X} | dp", opcode, next_byte, next_byte), - // TSB - 0x0C => format!("{:02X} {:02X} {:02X} __ | TSB ${:04X}", opcode, next_byte, next_second_byte, next_word), - 0x04 => format!("{:02X} {:02X} __ __ | TSB ${:02X} | dp", opcode, next_byte, next_byte), - // TSC - 0x3B => format!("{:02X} __ __ __ | TSC", opcode), - // TSX - 0xBA => format!("{:02X} __ __ __ | TSX", opcode), - // TXA - 0x8A => format!("{:02X} __ __ __ | TXA", opcode), - // TXS - 0x9A => format!("{:02X} __ __ __ | TXS", opcode), - // TXY - 0x9B => format!("{:02X} __ __ __ | TXY", opcode), - // TYA - 0x98 => format!("{:02X} __ __ __ | TYA", opcode), - // TYX - 0xBB => format!("{:02X} __ __ __ | TYX", opcode), - // WAI - 0xCB => format!("{:02X} __ __ __ | WAI", opcode), - // WDM - 0x42 => format!("{:02X} __ __ __ | WDM", opcode), - // XBA - 0xEB => format!("{:02X} __ __ __ | XBA", opcode), - // XCE - 0xFB => format!("{:02X} __ __ __ | XCE", opcode), - } + let instruction = map_opcode_to_instruction(opcode); + instruction.mnemonic(&emulator.cpu.registers, &emulator.bus, opcode) } }