diff --git a/src/r6502/LICENSE b/LICENSE similarity index 100% rename from src/r6502/LICENSE rename to LICENSE diff --git a/src/r6502/addressing_modes.rs b/src/r6502/addressing_modes.rs index 9358ede..868dfe9 100644 --- a/src/r6502/addressing_modes.rs +++ b/src/r6502/addressing_modes.rs @@ -3,6 +3,25 @@ use super::{R6502, Bus}; +#[derive(Clone, Copy, PartialEq)] +pub enum ModeID +{ + IMP, // Implied + ACM, // Accumulator - Not using, IMP might cover this + IMM, // Immediate + ZP0, // Zero Page + ZPX, // Zero Page, X + ZPY, // Zero Page, Y + REL, // Relative + ABS, // Absolute + ABX, // Absolute, X + ABY, // Aboslute, Y + IND, // Indirect + IZX, // Indirect, X + IZY, // Indirect, Y + +} + // Instruction decoding: // https://llx.com/Neil/a2/opcodes.html @@ -27,7 +46,7 @@ use super::{R6502, Bus}; pub struct AddressingModes; impl AddressingModes { - pub const GROUP_ONE_ADDRS: [fn(&mut R6502, &mut dyn Bus); 8] = [ + pub const GROUP_ONE_ADDRS: [fn(&mut R6502, &mut dyn Bus) -> ModeID; 8] = [ AddressingModes::IZX, AddressingModes::ZP0, AddressingModes::IMM, @@ -44,98 +63,120 @@ impl AddressingModes impl AddressingModes { // This is also the accumulator mode - pub fn IMP(cpu: &mut R6502, bus: &mut dyn Bus) + pub fn IMP(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID { cpu.working_data = cpu.a as u16; + ModeID::IMP } - pub fn IMM(cpu: &mut R6502, bus: &mut dyn Bus) + pub fn IMM(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID { cpu.working_data = bus.read(cpu.pc) as u16; cpu.pc += 1; + + ModeID::IMM } - pub fn ZP0(cpu: &mut R6502, bus: &mut dyn Bus) + pub fn ZP0(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID { - let working_addr = bus.read(cpu.pc) as u16 & 0x00FF; + cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF; cpu.pc += 1; - cpu.working_data = bus.read(working_addr) as u16; + cpu.working_data = bus.read(cpu.working_addr) as u16; + + ModeID::ZP0 } - pub fn ZPX(cpu: &mut R6502, bus: &mut dyn Bus) + pub fn ZPX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID { - let mut working_addr = bus.read(cpu.pc) as u16 & 0x00FF; - working_addr += cpu.x as u16; + cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF; + cpu.working_addr += cpu.x as u16; cpu.pc += 1; - cpu.working_data = bus.read(working_addr) as u16; + cpu.working_data = bus.read(cpu.working_addr) as u16; + + ModeID::ZPX } - pub fn ZPY(cpu: &mut R6502, bus: &mut dyn Bus) + pub fn ZPY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID { - let mut working_addr = bus.read(cpu.pc) as u16 & 0x00FF; - working_addr += cpu.y as u16; + cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF; + cpu.working_addr += cpu.y as u16; cpu.pc += 1; - cpu.working_data = bus.read(working_addr) as u16; + cpu.working_data = bus.read(cpu.working_addr) as u16; + + ModeID::ZPY } - pub fn REL(cpu: &mut R6502, bus: &mut dyn Bus) + pub fn REL(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID { // NOTE: Not sure if we can use the working_data variable for this. // if any instruction using this address mode needs extra data read // then we need another variable to store this address + // + // Use working_addr to just like the other modes cpu.working_data = bus.read(cpu.pc) as u16; cpu.pc += 1; + + ModeID::REL } - pub fn ABS(cpu: &mut R6502, bus: &mut dyn Bus) + pub fn ABS(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID { - cpu.working_data = bus.read(cpu.pc) as u16; + cpu.working_addr = bus.read(cpu.pc) as u16; cpu.pc += 1; - cpu.working_data |= (bus.read(cpu.pc) as u16) << 8; + cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8; cpu.pc += 1; - cpu.working_data = bus.read(cpu.working_data) as u16 & 0x00FF; + cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; + + ModeID::ABS } - pub fn ABX(cpu: &mut R6502, bus: &mut dyn Bus) + pub fn ABX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID { - cpu.working_data = bus.read(cpu.pc) as u16; + cpu.working_addr = bus.read(cpu.pc) as u16; cpu.pc += 1; - cpu.working_data |= (bus.read(cpu.pc) as u16) << 8; + cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8; cpu.pc += 1; - cpu.working_data += cpu.x as u16; + cpu.working_addr += cpu.x as u16; - cpu.working_data = bus.read(cpu.working_data) as u16 & 0x00FF; + cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; + + ModeID::ABX } - pub fn ABY(cpu: &mut R6502, bus: &mut dyn Bus) + pub fn ABY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID { - cpu.working_data = bus.read(cpu.pc) as u16; + cpu.working_addr = bus.read(cpu.pc) as u16; cpu.pc += 1; - cpu.working_data |= (bus.read(cpu.pc) as u16) << 8; + cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8; cpu.pc += 1; - cpu.working_data += cpu.y as u16; + cpu.working_addr += cpu.y as u16; + + cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; - cpu.working_data = bus.read(cpu.working_data) as u16 & 0x00FF; + ModeID::ABY } - pub fn IND(cpu: &mut R6502, bus: &mut dyn Bus) + pub fn IND(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID { + ModeID::IND } - pub fn IZX(cpu: &mut R6502, bus: &mut dyn Bus) + pub fn IZX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID { + ModeID::IZX } - pub fn IZY(cpu: &mut R6502, bus: &mut dyn Bus) + pub fn IZY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID { + ModeID::IZY } } \ No newline at end of file diff --git a/src/r6502/instructions.rs b/src/r6502/instructions.rs index d817881..57f4c4d 100644 --- a/src/r6502/instructions.rs +++ b/src/r6502/instructions.rs @@ -87,8 +87,10 @@ impl Instructions // https://github.com/OneLoneCoder/olcNES/blob/master/Part%232%20-%20CPU/olc6502.cpp#L659 pub fn ADC(cpu: &mut R6502, bus: &mut dyn Bus) { + let carry = cpu.check_flag(Flags::C) as u16; + // 16 bit addition to capture the carry easier - let temp: u16 = cpu.a as u16 + cpu.working_data; + let temp: u16 = cpu.a as u16 + cpu.working_data + carry; if temp > 255 { @@ -118,7 +120,7 @@ impl Instructions pub fn STA(cpu: &mut R6502, bus: &mut dyn Bus) { - bus.write(cpu.working_data, cpu.a); + bus.write(cpu.working_addr, cpu.a); } pub fn LDA(cpu: &mut R6502, bus: &mut dyn Bus) @@ -139,14 +141,77 @@ impl Instructions pub fn CMP(cpu: &mut R6502, bus: &mut dyn Bus) { + let data = cpu.working_data as u8; + if cpu.a >= data + { + cpu.set_flag(Flags::C); + } + else + { + cpu.clear_flag(Flags::C); + } + + if cpu.a == data + { + cpu.set_flag(Flags::Z); + } + else + { + cpu.clear_flag(Flags::Z); + } + if cpu.a < data + { + cpu.set_flag(Flags::N); + } + else + { + cpu.clear_flag(Flags::N); + } } + // Using a technique written javidx9 + // The code in this function falls under the License (OLC-3) SEE LICENSE FILE + // https://github.com/OneLoneCoder/olcNES/blob/master/Part%232%20-%20CPU/olc6502.cpp#L714 pub fn SBC(cpu: &mut R6502, bus: &mut dyn Bus) { + // Seem to need the + 1 here for javidx9's algorithm to work + let value = (cpu.working_data ^ 0x00FF) + 1; + let carry = cpu.check_flag(Flags::C) as u16; + + let temp: u16 = cpu.a as u16 + value + carry; + if temp > 255 + { + cpu.set_flag(Flags::C); + } + + if temp == 0 + { + cpu.set_flag(Flags::Z); + } + + let did_overflow = (!((cpu.a as u16) ^ (value)) & ((cpu.a as u16) ^ temp)) & 0x0080; + cpu.clear_flag(Flags::V); + if did_overflow > 0 + { + cpu.set_flag(Flags::V); + } + + cpu.clear_flag(Flags::N); + if temp & 0x80 > 0 + { + cpu.set_flag(Flags::N); + } + + cpu.a = (temp & 0x00FF) as u8; } /////////////////////////////////////////////////////////// // GROUP TWO + + /////////////////////////////////////////////////////////// + // GROUP THREE + + } \ No newline at end of file diff --git a/src/r6502/mod.rs b/src/r6502/mod.rs index d93b7c6..bf93083 100644 --- a/src/r6502/mod.rs +++ b/src/r6502/mod.rs @@ -4,7 +4,7 @@ mod addressing_modes; mod instructions; -use addressing_modes::AddressingModes; +use addressing_modes::{AddressingModes, ModeID}; use instructions::Instructions; pub trait Bus @@ -56,8 +56,9 @@ pub struct R6502 cycles: u32, // Track cycles // Helper Vars + addr_mode: ModeID, working_data: u16, // value fetched for the ALU - // working_addr: u16, + working_addr: u16, } impl R6502 @@ -65,7 +66,7 @@ impl R6502 // constructor pub fn new() -> R6502 { - R6502 { a: 0, x: 0, y: 0, pc: 0, sp: 0, status: 0, cycles: 0, working_data: 0 } + R6502 { a: 0, x: 0, y: 0, pc: 0, sp: 0, status: 0, cycles: 0, addr_mode: ModeID::IMP, working_data: 0, working_addr: 0 } } // Debug Access @@ -157,9 +158,14 @@ impl R6502 self.status &= !(bit as u8); } - pub fn check_flag(&mut self, bit: Flags) -> bool + pub fn check_flag(&mut self, bit: Flags) -> u8 { - self.status & (bit as u8) > 0 + if self.status & (bit as u8) > 0 + { + return 1; + } + + return 0; } } @@ -187,7 +193,7 @@ fn exe_group_one(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) let addr_mask = (instruction & 0x1C) >> 2; let op_mask = (instruction & 0xE0) >> 5; - AddressingModes::GROUP_ONE_ADDRS[addr_mask as usize](cpu, bus); + cpu.addr_mode = AddressingModes::GROUP_ONE_ADDRS[addr_mask as usize](cpu, bus); Instructions::GROUP_ONE_OPS[op_mask as usize](cpu, bus); } diff --git a/src/tests/instructions/ADC.rs b/src/tests/instructions/ADC.rs new file mode 100644 index 0000000..2553655 --- /dev/null +++ b/src/tests/instructions/ADC.rs @@ -0,0 +1,216 @@ + +#![allow(dead_code, non_snake_case)] + +use crate::tests::test_bus::RAMBus; +use crate::r6502::{R6502, Bus, Registers, Flags}; + +#[test] +fn IMM() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Write the program to memory + // ADC 6 + bus.write(addr, 0x69); // ADC - Immediate mode + bus.write(addr + 1, 0x06); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x04); + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0A in the A register? + assert_eq!(0x0A, cpu.debug_get_reg(Registers::A), "Failed basic addition"); + + /////////////////////////////////////////// + // EXTRA TESTING + + // Add with carry in + // Write the program to memory + // ADC 6 + bus.write(addr, 0x69); // ADC - Immediate mode + bus.write(addr + 1, 0x06); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu internals + cpu.debug_set_reg(Registers::A, 0x04); + cpu.set_flag(Flags::C); + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0B in the A register? + assert_eq!(0x0B, cpu.debug_get_reg(Registers::A), "Failed addition with carry"); + + // Add with overflow + // Write the program to memory + // ADC 126 + bus.write(addr, 0x69); // ADC - Immediate mode + bus.write(addr + 1, 126); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu internals + cpu.debug_set_reg(Registers::A, 0x04); + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 130 in the A register? + assert_eq!(130, cpu.debug_get_reg(Registers::A)); + + // Is the overflow bit set? + assert_eq!(1, cpu.check_flag(Flags::V), "Failed addition with overflow"); + +} + +#[test] +fn ZP0() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x04 into memory in the zero page + bus.write(0x000B, 0x04); + + // ADC #6 + bus.write(addr, 0x65); // ADC - Zero Page mode + bus.write(addr + 1, 0x0B); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x06); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0A in the A register? + assert_eq!(0x0A, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ZPX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x0A into memory in the zero page + bus.write(0x000B, 0x04); + + // ADC #A + bus.write(addr, 0x75); // ADC - Zero Page, X mode + bus.write(addr + 1, 0x0A); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x06); + cpu.debug_set_reg(Registers::X, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0A in the A register? + assert_eq!(0x0A, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ABS() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x0A into memory in the zero page + bus.write(0x010B, 0x06); + + // AND $10B + bus.write(addr, 0x6D); // ADC - Absolute mode + bus.write(addr + 1, 0x0B); // Argument + bus.write(addr + 2, 0x01); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x04); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x09 in the A register? + assert_eq!(0x0A, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ABX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x0A into memory + bus.write(0x010C, 0x04); + + // ADC $10C + bus.write(addr, 0x7D); // ADC - Absolute, X mode + bus.write(addr + 1, 0x0B); // Argument + bus.write(addr + 2, 0x01); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x06); + cpu.debug_set_reg(Registers::X, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0A in the A register? + assert_eq!(0x0A, cpu.debug_get_reg(Registers::A)); +} \ No newline at end of file diff --git a/src/tests/instructions/AND.rs b/src/tests/instructions/AND.rs new file mode 100644 index 0000000..de89a8b --- /dev/null +++ b/src/tests/instructions/AND.rs @@ -0,0 +1,207 @@ + +#![allow(dead_code, non_snake_case)] + +use crate::tests::test_bus::RAMBus; +use crate::r6502::{R6502, Bus, Registers, Flags}; + +#[test] +fn IMM() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // AND #3 + bus.write(addr, 0x29); // AND - Immediate mode + bus.write(addr + 1, 0x03); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x02 in the A register? + assert_eq!(0x02, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ZP0() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x0A into memory in the zero page + bus.write(0x000B, 0x03); + + // AND #3 + bus.write(addr, 0x25); // AND - Zero Page mode + bus.write(addr + 1, 0x0B); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x02 in the A register? + assert_eq!(0x02, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ZPX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x0A into memory in the zero page + bus.write(0x000B, 0x03); + + // AND #3 + bus.write(addr, 0x35); // AND - Zero Page, X mode + bus.write(addr + 1, 0x0A); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + cpu.debug_set_reg(Registers::X, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x02 in the A register? + assert_eq!(0x02, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ABS() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x0A into memory in the zero page + bus.write(0x010B, 0x03); + + // AND #3 + bus.write(addr, 0x2D); // AND - Absolute mode + bus.write(addr + 1, 0x0B); // Argument + bus.write(addr + 2, 0x01); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + //cpu.debug_set_reg(Registers::X, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x02 in the A register? + assert_eq!(0x02, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ABX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x0A into memory in the zero page + bus.write(0x010C, 0x03); + + // AND #3 + bus.write(addr, 0x3D); // AND - Absolute, X mode + bus.write(addr + 1, 0x0B); // Argument + bus.write(addr + 2, 0x01); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + cpu.debug_set_reg(Registers::X, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x02 in the A register? + assert_eq!(0x02, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ABY() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x0A into memory in the zero page + bus.write(0x010C, 0x03); + + // AND #3 + bus.write(addr, 0x39); // AND - Absolute, X mode + bus.write(addr + 1, 0x0B); // Argument + bus.write(addr + 2, 0x01); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + cpu.debug_set_reg(Registers::Y, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x02 in the A register? + assert_eq!(0x02, cpu.debug_get_reg(Registers::A)); +} \ No newline at end of file diff --git a/src/tests/instructions/CMP.rs b/src/tests/instructions/CMP.rs new file mode 100644 index 0000000..9c6cc30 --- /dev/null +++ b/src/tests/instructions/CMP.rs @@ -0,0 +1,569 @@ + +#![allow(dead_code, non_snake_case)] + +use crate::tests::test_bus::RAMBus; +use crate::r6502::{R6502, Bus, Registers, Flags}; + +////////////////////////////////////////////////////////////////////////////// +/// IMM IMM IMM IMM IMM IMM IMM IMM IMM +////////////////////////////////////////////////////////////////////////////// + +#[test] +fn IMM() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + /////////////////////// + // Parameter is less than A reg + + // Program to compare 0x10 with 0x05 (0x05 will be the argument) + bus.write(addr, 0xC9); // CMP - Immediate mode + bus.write(addr + 1, 0x05); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Flag should be 1, Z N Flags should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); + + + /////////////////////// + // Parameter is equal to the A reg + + // Program to compare 0x10 with 0x10 + bus.write(addr, 0xC9); // CMP - Immediate mode + bus.write(addr + 1, 0x10); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 1, N Flag should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(1, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); + + + /////////////////////// + // Parameter is greater than A reg + + // Program to compare 0x05 with 0x10 + bus.write(addr, 0xC9); // CMP - Immediate mode + bus.write(addr + 1, 0x10); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x05); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 0, N Flag should be 1 + assert_eq!(0, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(1, cpu.check_flag(Flags::N)); + + + +} + + +////////////////////////////////////////////////////////////////////////////// +/// ZP0 ZP0 ZP0 ZP0 ZP0 ZP0 ZP0 ZP0 ZP0 +////////////////////////////////////////////////////////////////////////////// + +#[test] +fn ZP0() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + + /////////////////////// + // Parameter is less than A reg + + // Manually put 0x05 into memory in the zero page + bus.write(0x000A, 0x05); + + // Program to compare 0x10 with 0x05 (0x05 will be the argument) + bus.write(addr, 0xC5); // CMP - Zero Page mode + bus.write(addr + 1, 0x0A); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Flag should be 1, Z N Flags should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); + + + /////////////////////// + // Parameter is equal to the A reg + + // Manually put 0x05 into memory in the zero page + bus.write(0x000A, 0x10); + + // Program to compare 0x10 with 0x10 + bus.write(addr, 0xC5); // CMP - Zero Page mode + bus.write(addr + 1, 0x0A); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 1, N Flag should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(1, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); + + /////////////////////// + // Parameter is greater than A reg + + // Manually put 0x05 into memory in the zero page + bus.write(0x000A, 0x10); + + // Program to compare 0x10 with 0x10 + bus.write(addr, 0xC5); // CMP - Zero Page mode + bus.write(addr + 1, 0x0A); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x05); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 0, N Flag should be 1 + assert_eq!(0, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(1, cpu.check_flag(Flags::N)); + +} + + +////////////////////////////////////////////////////////////////////////////// +/// ZPX ZPX ZPX ZPX ZPX ZPX ZPX ZPX ZPX +////////////////////////////////////////////////////////////////////////////// +#[test] +fn ZPX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + + /////////////////////// + // Parameter is less than A reg + + // Manually put 0x05 into memory in the zero page + bus.write(0x000A, 0x05); + + // Program to compare 0x05 and 0x10 + bus.write(addr, 0xD5); // CMP - Zero Page, X mode + bus.write(addr + 1, 0x04); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x10); + cpu.debug_set_reg(Registers::X, 0x06); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Flag should be 1, Z N Flags should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); + + /////////////////////// + // Parameter is equal to the A reg + + // Manually put 0x10 into memory in the zero page + bus.write(0x000A, 0x10); + + // Program to compare 0x05 and 0x10 + bus.write(addr, 0xD5); // CMP - Zero Page, X mode + bus.write(addr + 1, 0x04); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x10); + cpu.debug_set_reg(Registers::X, 0x06); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 1, N Flag should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(1, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); + + /////////////////////// + // Parameter is greater than A reg + + // Manually put 0x10 into memory in the zero page + bus.write(0x000A, 0x10); + + // Program to compare 0x05 and 0x10 + bus.write(addr, 0xD5); // CMP - Zero Page, X mode + bus.write(addr + 1, 0x04); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x05); + cpu.debug_set_reg(Registers::X, 0x06); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 0, N Flag should be 1 + assert_eq!(0, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(1, cpu.check_flag(Flags::N)); +} + +////////////////////////////////////////////////////////////////////////////// +/// ABS ABS ABS ABS ABS ABS ABS ABS ABS +////////////////////////////////////////////////////////////////////////////// +#[test] +fn ABS() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + + + /////////////////////// + // Parameter is less than A reg + + // Manually put 0x05 into memory + bus.write(0x010A, 0x05); + + // Program to compare 0x05 and 0x10 + bus.write(addr, 0xCD); // CMP - Absolute mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Flag should be 1, Z N Flags should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); + + /////////////////////// + // Parameter is equal to the A reg + + // Manually put 0x10 into memory + bus.write(0x010A, 0x10); + + // Program to compare 0x10 and 0x10 + bus.write(addr, 0xCD); // LDA - Absolute mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 1, N Flag should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(1, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); + + /////////////////////// + // Parameter is greater than A reg + + // Manually put 0x10 into memory + bus.write(0x010A, 0x10); + + // Program to compare 0x05 and 0x10 + bus.write(addr, 0xCD); // LDA - Absolute mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x05); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 0, N Flag should be 1 + assert_eq!(0, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(1, cpu.check_flag(Flags::N)); +} + +////////////////////////////////////////////////////////////////////////////// +/// ABX ABX ABX ABX ABX ABX ABX ABX ABX +////////////////////////////////////////////////////////////////////////////// +#[test] +fn ABX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + + /////////////////////// + // Parameter is less than A reg + + // Manually put 0x05 into memory + bus.write(0x010B, 0x05); + + // Program to compare 0x05 to 0x10 + bus.write(addr, 0xDD); // CMP - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x01); + cpu.debug_set_reg(Registers::A, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Flag should be 1, Z N Flags should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); + + /////////////////////// + // Parameter is equal to the A reg + + + // Manually put 0x10 into memory + bus.write(0x010B, 0x10); + + // Program to compare 0x05 to 0x10 + bus.write(addr, 0xDD); // CMP - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x01); + cpu.debug_set_reg(Registers::A, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 1, N Flag should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(1, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); + + /////////////////////// + // Parameter is greater than A reg + + + // Manually put 0x05 into memory + bus.write(0x010B, 0x10); + + // Program to compare 0x05 to 0x10 + bus.write(addr, 0xDD); // CMP - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x01); + cpu.debug_set_reg(Registers::A, 0x05); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 0, N Flag should be 1 + assert_eq!(0, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(1, cpu.check_flag(Flags::N)); +} + +////////////////////////////////////////////////////////////////////////////// +/// ABY ABY ABY ABY ABY ABY ABY ABY ABY +////////////////////////////////////////////////////////////////////////////// +#[test] +fn ABY() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + + /////////////////////// + // Parameter is less than A reg + + // Manually put 0x05 into memory + bus.write(0x010B, 0x05); + + // Program to compare 0x05 to 0x10 + bus.write(addr, 0xD9); // CMP - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::Y, 0x01); + cpu.debug_set_reg(Registers::A, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Flag should be 1, Z N Flags should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); + + /////////////////////// + // Parameter is equal to the A reg + + + // Manually put 0x10 into memory + bus.write(0x010B, 0x10); + + // Program to compare 0x05 to 0x10 + bus.write(addr, 0xD9); // CMP - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::Y, 0x01); + cpu.debug_set_reg(Registers::A, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 1, N Flag should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(1, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); + + /////////////////////// + // Parameter is greater than A reg + + + // Manually put 0x05 into memory + bus.write(0x010B, 0x10); + + // Program to compare 0x05 to 0x10 + bus.write(addr, 0xD9); // CMP - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::Y, 0x01); + cpu.debug_set_reg(Registers::A, 0x05); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 0, N Flag should be 1 + assert_eq!(0, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(1, cpu.check_flag(Flags::N)); +} \ No newline at end of file diff --git a/src/tests/instructions/EOR.rs b/src/tests/instructions/EOR.rs new file mode 100644 index 0000000..a42c10e --- /dev/null +++ b/src/tests/instructions/EOR.rs @@ -0,0 +1,207 @@ + +#![allow(dead_code, non_snake_case)] + +use crate::tests::test_bus::RAMBus; +use crate::r6502::{R6502, Bus, Registers}; + +#[test] +fn IMM() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // AND #3 + bus.write(addr, 0x49); // EOR - Immediate mode + bus.write(addr + 1, 0x03); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x09 in the A register? + assert_eq!(0x09, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ZP0() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x0A into memory in the zero page + bus.write(0x000B, 0x03); + + // AND #3 + bus.write(addr, 0x45); // EOR - Zero Page mode + bus.write(addr + 1, 0x0B); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x09 in the A register? + assert_eq!(0x09, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ZPX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x0A into memory in the zero page + bus.write(0x000B, 0x03); + + // AND #3 + bus.write(addr, 0x55); // EOR - Zero Page, X mode + bus.write(addr + 1, 0x0A); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + cpu.debug_set_reg(Registers::X, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x09 in the A register? + assert_eq!(0x09, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ABS() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x0A into memory in the zero page + bus.write(0x010B, 0x03); + + // AND #3 + bus.write(addr, 0x4D); // EOR - Absolute mode + bus.write(addr + 1, 0x0B); // Argument + bus.write(addr + 2, 0x01); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + //cpu.debug_set_reg(Registers::X, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x09 in the A register? + assert_eq!(0x09, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ABX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x03 into memory + bus.write(0x010C, 0x03); + + // EOR $10B + bus.write(addr, 0x5D); // EOR - Absolute, X mode + bus.write(addr + 1, 0x0B); // Argument + bus.write(addr + 2, 0x01); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + cpu.debug_set_reg(Registers::X, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x09 in the A register? + assert_eq!(0x09, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ABY() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x0A into memory in the zero page + bus.write(0x010C, 0x03); + + // AND #3 + bus.write(addr, 0x59); // EOR - Absolute, X mode + bus.write(addr + 1, 0x0B); // Argument + bus.write(addr + 2, 0x01); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + cpu.debug_set_reg(Registers::Y, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x09 in the A register? + assert_eq!(0x09, cpu.debug_get_reg(Registers::A)); +} \ No newline at end of file diff --git a/src/tests/instructions/LDA.rs b/src/tests/instructions/LDA.rs new file mode 100644 index 0000000..08eb715 --- /dev/null +++ b/src/tests/instructions/LDA.rs @@ -0,0 +1,194 @@ + +#![allow(dead_code, non_snake_case)] + +use crate::tests::test_bus::RAMBus; +use crate::r6502::{R6502, Bus, Registers}; + +#[test] +fn IMM() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xA9); // LDA - Immediate mode + bus.write(addr + 1, 0x08); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ZP0() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x08 into memory in the zero page + bus.write(0x000A, 0x08); + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xA5); // LDA - Zero Page mode + bus.write(addr + 1, 0x0A); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ZPX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x08 into memory in the zero page + bus.write(0x000A, 0x08); + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xB5); // LDA - Zero Page, X mode + bus.write(addr + 1, 0x04); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x06); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ABS() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x08 into memory in the zero page + bus.write(0x010A, 0x08); + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xAD); // LDA - Absolute mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ABX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x08 into memory in the zero page + bus.write(0x010B, 0x08); + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xBD); // LDA - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ABY() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x08 into memory in the zero page + bus.write(0x010B, 0x08); + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xB9); // LDA - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::Y, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); +} \ No newline at end of file diff --git a/src/tests/instructions/ORA.rs b/src/tests/instructions/ORA.rs new file mode 100644 index 0000000..d38be88 --- /dev/null +++ b/src/tests/instructions/ORA.rs @@ -0,0 +1,227 @@ + + + +#![allow(dead_code, non_snake_case)] + +// mod test_bus; + +// #[cfg(test)] + +use crate::tests::test_bus::RAMBus; +use crate::r6502::{R6502, Bus, Registers}; + +#[test] +fn IMM() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // ORA #2 + bus.write(addr, 0x09); // ORA - Immediate mode + bus.write(addr + 1, 0x02); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x09); + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0B in the A register? + assert_eq!(0x0B, cpu.debug_get_reg(Registers::A)); + +} + +#[test] +fn ZP0() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x02 into memory in the zero page + bus.write(0x000A, 0x02); + + // ORA #2 + bus.write(addr, 0x05); // ORA - Zero Page mode + bus.write(addr +1, 0x0A); // Argument (memory address of the value we want to OR with) + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x09); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0B in the A register? + assert_eq!(0x0B, cpu.debug_get_reg(Registers::A)); + +} + +#[test] +fn ZPX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x02 into memory in the zero page + bus.write(0x000A, 0x02); + + // ORA #2 + bus.write(addr, 0x15); // ORA - Zero Page, X mode + bus.write(addr + 1, 0x04); // Argument (memory address of the value we want to OR with) + + // Restart cpu + cpu.reset(&mut bus); + + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x06); + cpu.debug_set_reg(Registers::A, 0x09); + + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0B in the A register? + assert_eq!(0x0B, cpu.debug_get_reg(Registers::A)); + +} + +#[test] +fn ABS() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x02 into memory in the zero page + bus.write(0x010A, 0x02); + + // ORA #2 + bus.write(addr, 0x0D); // ORA - Absolute mode + bus.write(addr + 1, 0x0A); // Argument (memory address of the value we want to OR with) + bus.write(addr + 2, 0x01); // Argument (memory address of the value we want to OR with) + + // Restart cpu + cpu.reset(&mut bus); + + + // manually setup the cpu registers + // cpu.debug_set_reg(Registers::X, 0x06); + cpu.debug_set_reg(Registers::A, 0x09); + + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0B in the A register? + assert_eq!(0x0B, cpu.debug_get_reg(Registers::A)); + +} + +#[test] +fn ABX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x02 into memory in the zero page + bus.write(0x010B, 0x02); + + // ORA #2 + bus.write(addr, 0x1D); // ORA - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument (memory address of the value we want to OR with) + bus.write(addr + 2, 0x01); // Argument (memory address of the value we want to OR with) + + // Restart cpu + cpu.reset(&mut bus); + + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x01); + cpu.debug_set_reg(Registers::A, 0x09); + + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0B in the A register? + assert_eq!(0x0B, cpu.debug_get_reg(Registers::A)); + +} + +#[test] +fn ABY() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x02 into memory in the zero page + bus.write(0x010B, 0x02); + + // ORA #2 + bus.write(addr, 0x19); // ORA - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument (memory address of the value we want to OR with) + bus.write(addr + 2, 0x01); // Argument (memory address of the value we want to OR with) + + // Restart cpu + cpu.reset(&mut bus); + + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::Y, 0x01); + cpu.debug_set_reg(Registers::A, 0x09); + + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0B in the A register? + assert_eq!(0x0B, cpu.debug_get_reg(Registers::A)); + +} diff --git a/src/tests/instructions/SBC.rs b/src/tests/instructions/SBC.rs new file mode 100644 index 0000000..cf68e30 --- /dev/null +++ b/src/tests/instructions/SBC.rs @@ -0,0 +1,233 @@ + +#![allow(dead_code, non_snake_case)] + +use crate::tests::test_bus::RAMBus; +use crate::r6502::{R6502, Bus, Registers}; + +////////////////////////////////////////////////////////////////////////////// +/// IMM IMM IMM IMM IMM IMM IMM IMM IMM +////////////////////////////////////////////////////////////////////////////// + +#[test] +fn IMM() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Program to subtract 0x06 from 0x0A + bus.write(addr, 0xE9); // SBC - Immediate mode + bus.write(addr + 1, 0x06); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x04 in the A register? + assert_eq!(0x04, cpu.debug_get_reg(Registers::A)); +} + + +////////////////////////////////////////////////////////////////////////////// +/// ZP0 ZP0 ZP0 ZP0 ZP0 ZP0 ZP0 ZP0 ZP0 +////////////////////////////////////////////////////////////////////////////// + +#[test] +fn ZP0() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x06 into memory in the zero page + bus.write(0x000A, 0x06); + + // Program to + bus.write(addr, 0xE5); // SBC - Zero Page mode + bus.write(addr + 1, 0x0A); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0A); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x04 in the A register? + assert_eq!(0x04, cpu.debug_get_reg(Registers::A)); +} + + +////////////////////////////////////////////////////////////////////////////// +/// ZPX ZPX ZPX ZPX ZPX ZPX ZPX ZPX ZPX +////////////////////////////////////////////////////////////////////////////// + +#[test] +fn ZPX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x06 into memory in the zero page + bus.write(0x000A, 0x06); + + // Program to + bus.write(addr, 0xF5); // - Zero Page, X mode + bus.write(addr + 1, 0x04); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x06); + cpu.debug_set_reg(Registers::A, 0x0A); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x04 in the A register? + assert_eq!(0x04, cpu.debug_get_reg(Registers::A)); +} + + +////////////////////////////////////////////////////////////////////////////// +/// ABS ABS ABS ABS ABS ABS ABS ABS ABS +////////////////////////////////////////////////////////////////////////////// + +#[test] +fn ABS() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x06 into memory in the zero page + bus.write(0x010A, 0x06); + + // Program to + bus.write(addr, 0xED); // SBC - Absolute mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + cpu.debug_set_reg(Registers::A, 0x0A); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x04 in the A register? + assert_eq!(0x04, cpu.debug_get_reg(Registers::A)); +} + + +////////////////////////////////////////////////////////////////////////////// +/// ABX ABX ABX ABX ABX ABX ABX ABX ABX +////////////////////////////////////////////////////////////////////////////// + +#[test] +fn ABX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x06 into memory in the zero page + bus.write(0x010B, 0x06); + + // Program to + bus.write(addr, 0xFD); // SBC - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x01); + cpu.debug_set_reg(Registers::A, 0x0A); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x04 in the A register? + assert_eq!(0x04, cpu.debug_get_reg(Registers::A)); +} + +////////////////////////////////////////////////////////////////////////////// +/// ABY ABY ABY ABY ABY ABY ABY ABY ABY +////////////////////////////////////////////////////////////////////////////// + +#[test] +fn ABY() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x08 into memory in the zero page + bus.write(0x010B, 0x06); + + // Program to + bus.write(addr, 0xF9); // - Absolute, Y mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::Y, 0x01); + cpu.debug_set_reg(Registers::A, 0x0A); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x04 in the A register? + assert_eq!(0x04, cpu.debug_get_reg(Registers::A)); +} \ No newline at end of file diff --git a/src/tests/instructions/STA.rs b/src/tests/instructions/STA.rs new file mode 100644 index 0000000..3fc1d6c --- /dev/null +++ b/src/tests/instructions/STA.rs @@ -0,0 +1,185 @@ +#![allow(dead_code, non_snake_case)] + +use crate::tests::test_bus::RAMBus; +use crate::r6502::{R6502, Bus, Registers}; + + + +#[test] +fn ZP0() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x0A into memory in the zero page + //bus.write(0x000B, 0x0A); + + // STA $0A + bus.write(addr, 0x85); // STA - Zero Page mode + bus.write(addr + 1, 0x0A); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x0F); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0F at memory address 0x0A? + assert_eq!(0x0F, bus.read(0x0A)); + +} + +#[test] +fn ZPX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // STA + bus.write(addr, 0x95); // STA - Zero Page, X mode + bus.write(addr + 1, 0x04); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x06); + cpu.debug_set_reg(Registers::A, 0x0F); + + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0F at memory address 0x0A? + assert_eq!(0x0F, bus.read(0x0A)); + +} + +#[test] +fn ABS() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x02 into memory in the zero page + bus.write(0x010A, 0x02); + + // STA + bus.write(addr, 0x8D); // STA - Absolute mode + bus.write(addr + 1, 0x0A); // Argument + bus.write(addr + 2, 0x01); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + + // manually setup the cpu registers + // cpu.debug_set_reg(Registers::X, 0x06); + cpu.debug_set_reg(Registers::A, 0x0F); + + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0F at memory address 0x010A? + assert_eq!(0x0F, bus.read(0x010A)); + +} + +#[test] +fn ABX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // STA + bus.write(addr, 0x9D); // STA - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument + bus.write(addr + 2, 0x01); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x01); + cpu.debug_set_reg(Registers::A, 0x0F); + + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0F at memory address 0x010B? + assert_eq!(0x0F, bus.read(0x010B)); + +} + +#[test] +fn ABY() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x02 into memory in the zero page + bus.write(0x010B, 0x02); + + // STA + bus.write(addr, 0x99); // STA - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument + bus.write(addr + 2, 0x01); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::Y, 0x01); + cpu.debug_set_reg(Registers::A, 0x0F); + + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0F at memory address 0x010B? + assert_eq!(0x0F, bus.read(0x010B)); + +} \ No newline at end of file diff --git a/src/tests/instructions/mod.rs b/src/tests/instructions/mod.rs new file mode 100644 index 0000000..d4af1c0 --- /dev/null +++ b/src/tests/instructions/mod.rs @@ -0,0 +1,24 @@ + +#[cfg(test)] +mod ORA; + +#[cfg(test)] +mod AND; + +#[cfg(test)] +mod EOR; + +#[cfg(test)] +mod ADC; + +#[cfg(test)] +mod LDA; + +#[cfg(test)] +mod STA; + +#[cfg(test)] +mod CMP; + +#[cfg(test)] +mod SBC; \ No newline at end of file diff --git a/src/tests/mod.rs b/src/tests/mod.rs index aa5eeb2..7e6c2f4 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -4,628 +4,7 @@ mod test_bus; -#[cfg(test)] -mod instructions -{ - use super::test_bus::RAMBus; - use crate::r6502::{R6502, Bus, Registers}; - - ////////////////////////////////////////////////////////////////// - /// ORA ORA ORA ORA - ////////////////////////////////////////////////////////////////// - - #[test] - fn ORA_IMM() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // ORA #2 - bus.write(addr, 0x09); // ORA - Immediate mode - bus.write(addr + 1, 0x02); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::A, 0x09); - - // Clock the cpu twice (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x0B in the A register? - assert_eq!(0x0B, cpu.debug_get_reg(Registers::A)); - - } - - #[test] - fn ORA_ZP0() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x02 into memory in the zero page - bus.write(0x000A, 0x02); - - // ORA #2 - bus.write(addr, 0x05); // ORA - Zero Page mode - bus.write(addr +1, 0x0A); // Argument (memory address of the value we want to OR with) - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::A, 0x09); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x0B in the A register? - assert_eq!(0x0B, cpu.debug_get_reg(Registers::A)); - - } - - #[test] - fn ORA_ZPX() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x02 into memory in the zero page - bus.write(0x000A, 0x02); - - // ORA #2 - bus.write(addr, 0x15); // ORA - Zero Page, X mode - bus.write(addr + 1, 0x04); // Argument (memory address of the value we want to OR with) - - // Restart cpu - cpu.reset(&mut bus); - - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::X, 0x06); - cpu.debug_set_reg(Registers::A, 0x09); - - - // Clock the cpu twice (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x0B in the A register? - assert_eq!(0x0B, cpu.debug_get_reg(Registers::A)); - - } - - #[test] - fn ORA_ABS() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x02 into memory in the zero page - bus.write(0x010A, 0x02); - - // ORA #2 - bus.write(addr, 0x0D); // ORA - Absolute mode - bus.write(addr + 1, 0x0A); // Argument (memory address of the value we want to OR with) - bus.write(addr + 2, 0x01); // Argument (memory address of the value we want to OR with) - - // Restart cpu - cpu.reset(&mut bus); - - - // manually setup the cpu registers - // cpu.debug_set_reg(Registers::X, 0x06); - cpu.debug_set_reg(Registers::A, 0x09); - - - // Clock the cpu twice (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x0B in the A register? - assert_eq!(0x0B, cpu.debug_get_reg(Registers::A)); - - } - - #[test] - fn ORA_ABX() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x02 into memory in the zero page - bus.write(0x010B, 0x02); - - // ORA #2 - bus.write(addr, 0x1D); // ORA - Absolute, X mode - bus.write(addr + 1, 0x0A); // Argument (memory address of the value we want to OR with) - bus.write(addr + 2, 0x01); // Argument (memory address of the value we want to OR with) - - // Restart cpu - cpu.reset(&mut bus); - - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::X, 0x01); - cpu.debug_set_reg(Registers::A, 0x09); - - - // Clock the cpu twice (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x0B in the A register? - assert_eq!(0x0B, cpu.debug_get_reg(Registers::A)); - - } - - #[test] - fn ORA_ABY() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x02 into memory in the zero page - bus.write(0x010B, 0x02); - - // ORA #2 - bus.write(addr, 0x19); // ORA - Absolute, X mode - bus.write(addr + 1, 0x0A); // Argument (memory address of the value we want to OR with) - bus.write(addr + 2, 0x01); // Argument (memory address of the value we want to OR with) - - // Restart cpu - cpu.reset(&mut bus); - - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::Y, 0x01); - cpu.debug_set_reg(Registers::A, 0x09); - - - // Clock the cpu twice (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x0B in the A register? - assert_eq!(0x0B, cpu.debug_get_reg(Registers::A)); - - } - - ////////////////////////////////////////////////////////////////// - /// AND AND AND AND - ////////////////////////////////////////////////////////////////// - - #[test] - fn AND_IMM() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // AND #3 - bus.write(addr, 0x29); // AND - Immediate mode - bus.write(addr + 1, 0x03); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::A, 0x0A); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x02 in the A register? - assert_eq!(0x02, cpu.debug_get_reg(Registers::A)); - } - - #[test] - fn AND_ZP0() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x0A into memory in the zero page - bus.write(0x000B, 0x03); - - // AND #3 - bus.write(addr, 0x25); // AND - Zero Page mode - bus.write(addr + 1, 0x0B); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::A, 0x0A); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - // Is 0x02 in the A register? - assert_eq!(0x02, cpu.debug_get_reg(Registers::A)); - } - - #[test] - fn AND_ZPX() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x0A into memory in the zero page - bus.write(0x000B, 0x03); - - // AND #3 - bus.write(addr, 0x35); // AND - Zero Page, X mode - bus.write(addr + 1, 0x0A); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::A, 0x0A); - cpu.debug_set_reg(Registers::X, 0x01); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x02 in the A register? - assert_eq!(0x02, cpu.debug_get_reg(Registers::A)); - } - - #[test] - fn AND_ABS() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x0A into memory in the zero page - bus.write(0x010B, 0x03); - - // AND #3 - bus.write(addr, 0x2D); // AND - Absolute mode - bus.write(addr + 1, 0x0B); // Argument - bus.write(addr + 2, 0x01); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::A, 0x0A); - //cpu.debug_set_reg(Registers::X, 0x01); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x02 in the A register? - assert_eq!(0x02, cpu.debug_get_reg(Registers::A)); - } - - #[test] - fn AND_ABX() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x0A into memory in the zero page - bus.write(0x010C, 0x03); - - // AND #3 - bus.write(addr, 0x3D); // AND - Absolute, X mode - bus.write(addr + 1, 0x0B); // Argument - bus.write(addr + 2, 0x01); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::A, 0x0A); - cpu.debug_set_reg(Registers::X, 0x01); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x02 in the A register? - assert_eq!(0x02, cpu.debug_get_reg(Registers::A)); - } - - #[test] - fn AND_ABY() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x0A into memory in the zero page - bus.write(0x010C, 0x03); - - // AND #3 - bus.write(addr, 0x39); // AND - Absolute, X mode - bus.write(addr + 1, 0x0B); // Argument - bus.write(addr + 2, 0x01); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::A, 0x0A); - cpu.debug_set_reg(Registers::Y, 0x01); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x02 in the A register? - assert_eq!(0x02, cpu.debug_get_reg(Registers::A)); - } - - ////////////////////////////////////////////////////////////////// - /// LDA LDA LDA LDA - ////////////////////////////////////////////////////////////////// - - #[test] - fn LDA_IMM() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xA9); // LDA - Immediate mode - bus.write(addr + 1, 0x08); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); - } - - #[test] - fn LDA_ZP0() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x08 into memory in the zero page - bus.write(0x000A, 0x08); - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xA5); // LDA - Zero Page mode - bus.write(addr + 1, 0x0A); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); - } - - #[test] - fn LDA_ZPX() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x08 into memory in the zero page - bus.write(0x000A, 0x08); - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xB5); // LDA - Zero Page, X mode - bus.write(addr + 1, 0x04); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::X, 0x06); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); - } - - #[test] - fn LDA_ABS() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x08 into memory in the zero page - bus.write(0x010A, 0x08); - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xAD); // LDA - Absolute mode - bus.write(addr + 1, 0x0A); // Argument lo word - bus.write(addr + 2, 0x01); // Argument hi word - - // Restart cpu - cpu.reset(&mut bus); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); - } - - #[test] - fn LDA_ABX() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x08 into memory in the zero page - bus.write(0x010B, 0x08); - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xBD); // LDA - Absolute, X mode - bus.write(addr + 1, 0x0A); // Argument lo word - bus.write(addr + 2, 0x01); // Argument hi word - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::X, 0x01); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); - } - - #[test] - fn LDA_ABY() - { - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x08 into memory in the zero page - bus.write(0x010B, 0x08); - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xB9); // LDA - Absolute, X mode - bus.write(addr + 1, 0x0A); // Argument lo word - bus.write(addr + 2, 0x01); // Argument hi word - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::Y, 0x01); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); +#[cfg(test)] +mod instructions; - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); - } -} \ No newline at end of file diff --git a/todo/todo.todo b/todo/todo.todo index af79cc8..a72a1fc 100644 --- a/todo/todo.todo +++ b/todo/todo.todo @@ -36,8 +36,8 @@ Instructions: ✔ 011 ADC @done(23-11-06 19:59) ✔ 100 STA @done(23-11-07 15:04) ✔ 101 LDA @done(23-11-06 18:55) - ☐ 110 CMP - ☐ 111 SBC + ✔ 110 CMP @done(23-11-09 13:24) + ✔ 111 SBC @done(23-11-09 13:24) GROUP TWO: ☐ 000 ASL