diff --git a/src/main.rs b/src/main.rs index 08ad611..19ac3f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ - +mod tests; mod r6502; use r6502::{R6502, Flags, Bus}; @@ -42,9 +42,11 @@ fn main() cpu.clear_flag(Flags::I); println!("\nI Flag is: {}", cpu.check_flag(Flags::I)); - // run_simple_or_program(&mut cpu); + run_simple_or_program(&mut cpu); run_addition_test(&mut cpu); + + println!("\nFinished."); } @@ -52,7 +54,7 @@ fn run_addition_test(cpu: &mut R6502) { println!("\nRunning a simple addition test with no overflow or carry: 8 + 23"); - ////////////////////////////////// + ////////////////////////////////// // Setup Bus with program address ////////////////////////////////// @@ -77,6 +79,11 @@ fn run_addition_test(cpu: &mut R6502) bus.write(addr + 2, 0x69); // ADC - Immediate mode bus.write(addr + 3, 0x17); // Argument + // Store result into memory to also test the STA instruction + bus.write(addr + 4, 0x85); // STA - Zero Page mode + bus.write(addr + 5, 0x02); // Argument + + //////////////////// // Run the program! //////////////////// @@ -84,11 +91,14 @@ fn run_addition_test(cpu: &mut R6502) // Restart cpu cpu.reset(&mut bus); - // Clock the cpu twice (Clock essentially runs one full instruction) + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); cpu.clock(&mut bus); cpu.clock(&mut bus); - println!("\nProgram result, A register: {}", cpu.a); + println!("\nProgram result, A register: {}", cpu.debug_get_reg(r6502::Registers::A)); + let result = bus.read(0x0002); + println!("\nValue at 0x0002: {}", result); } @@ -132,5 +142,5 @@ fn run_simple_or_program(cpu: &mut R6502) cpu.clock(&mut bus); cpu.clock(&mut bus); - println!("\nProgram result, A register: {}", cpu.a); + println!("\nProgram result, A register: {}", cpu.debug_get_reg(r6502::Registers::A)); } \ No newline at end of file diff --git a/src/r6502/addressing_modes.rs b/src/r6502/addressing_modes.rs index ea6f6c0..9358ede 100644 --- a/src/r6502/addressing_modes.rs +++ b/src/r6502/addressing_modes.rs @@ -1,5 +1,5 @@ -#![allow(dead_code, non_snake_case)] +#![allow(unused_variables, dead_code, non_snake_case)] use super::{R6502, Bus}; @@ -46,48 +46,82 @@ impl AddressingModes // This is also the accumulator mode pub fn IMP(cpu: &mut R6502, bus: &mut dyn Bus) { - + cpu.working_data = cpu.a as u16; } pub fn IMM(cpu: &mut R6502, bus: &mut dyn Bus) { - cpu.working_data = bus.read(cpu.pc); + cpu.working_data = bus.read(cpu.pc) as u16; cpu.pc += 1; } pub fn ZP0(cpu: &mut R6502, bus: &mut dyn Bus) { - + let working_addr = bus.read(cpu.pc) as u16 & 0x00FF; + cpu.pc += 1; + + cpu.working_data = bus.read(working_addr) as u16; } pub fn ZPX(cpu: &mut R6502, bus: &mut dyn Bus) { - + let mut working_addr = bus.read(cpu.pc) as u16 & 0x00FF; + working_addr += cpu.x as u16; + cpu.pc += 1; + + cpu.working_data = bus.read(working_addr) as u16; } pub fn ZPY(cpu: &mut R6502, bus: &mut dyn Bus) { + let mut working_addr = bus.read(cpu.pc) as u16 & 0x00FF; + working_addr += cpu.y as u16; + cpu.pc += 1; + cpu.working_data = bus.read(working_addr) as u16; } pub fn REL(cpu: &mut R6502, bus: &mut dyn Bus) { - + // 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 + cpu.working_data = bus.read(cpu.pc) as u16; + cpu.pc += 1; } pub fn ABS(cpu: &mut R6502, bus: &mut dyn Bus) { - + cpu.working_data = bus.read(cpu.pc) as u16; + cpu.pc += 1; + cpu.working_data |= (bus.read(cpu.pc) as u16) << 8; + cpu.pc += 1; + + cpu.working_data = bus.read(cpu.working_data) as u16 & 0x00FF; } pub fn ABX(cpu: &mut R6502, bus: &mut dyn Bus) { - + cpu.working_data = bus.read(cpu.pc) as u16; + cpu.pc += 1; + cpu.working_data |= (bus.read(cpu.pc) as u16) << 8; + cpu.pc += 1; + + cpu.working_data += cpu.x as u16; + + cpu.working_data = bus.read(cpu.working_data) as u16 & 0x00FF; } pub fn ABY(cpu: &mut R6502, bus: &mut dyn Bus) { - + cpu.working_data = bus.read(cpu.pc) as u16; + cpu.pc += 1; + cpu.working_data |= (bus.read(cpu.pc) as u16) << 8; + cpu.pc += 1; + + cpu.working_data += cpu.y as u16; + + cpu.working_data = bus.read(cpu.working_data) as u16 & 0x00FF; } pub fn IND(cpu: &mut R6502, bus: &mut dyn Bus) diff --git a/src/r6502/instructions.rs b/src/r6502/instructions.rs index 033f201..d817881 100644 --- a/src/r6502/instructions.rs +++ b/src/r6502/instructions.rs @@ -39,7 +39,8 @@ impl Instructions // GROUP ONE pub fn ORA(cpu: &mut R6502, _bus: &mut dyn Bus) { - cpu.a = cpu.a | cpu.working_data; + let data = cpu.working_data as u8; + cpu.a = cpu.a | data; if cpu.a == 0 { cpu.set_flag(Flags::Z); @@ -53,7 +54,8 @@ impl Instructions pub fn AND(cpu: &mut R6502, bus: &mut dyn Bus) { - cpu.a = cpu.a & cpu.working_data; + let data = cpu.working_data as u8; + cpu.a = cpu.a & data; if cpu.a == 0 { cpu.set_flag(Flags::Z); @@ -67,7 +69,8 @@ impl Instructions pub fn EOR(cpu: &mut R6502, bus: &mut dyn Bus) { - cpu.a = cpu.a ^ cpu.working_data; + let data = cpu.working_data as u8; + cpu.a = cpu.a ^ data; if cpu.a == 0 { cpu.set_flag(Flags::Z); @@ -85,7 +88,7 @@ impl Instructions pub fn ADC(cpu: &mut R6502, bus: &mut dyn Bus) { // 16 bit addition to capture the carry easier - let temp: u16 = cpu.a as u16 + cpu.working_data as u16; + let temp: u16 = cpu.a as u16 + cpu.working_data; if temp > 255 { @@ -115,12 +118,13 @@ impl Instructions pub fn STA(cpu: &mut R6502, bus: &mut dyn Bus) { - + bus.write(cpu.working_data, cpu.a); } pub fn LDA(cpu: &mut R6502, bus: &mut dyn Bus) { - cpu.a = cpu.working_data; + let data = cpu.working_data as u8; + cpu.a = data; if cpu.a == 0 { diff --git a/src/r6502/mod.rs b/src/r6502/mod.rs index 141b97f..d93b7c6 100644 --- a/src/r6502/mod.rs +++ b/src/r6502/mod.rs @@ -1,5 +1,5 @@ -#![allow(dead_code)] +#![allow(unused_variables, dead_code, non_snake_case)] mod addressing_modes; mod instructions; @@ -32,9 +32,20 @@ pub enum Flags N = (1 << 7), // Negative Flag } +pub enum Registers +{ + A, + X, + Y, + PC, + SP, + STATUS, +} + +#[derive(Clone, Copy, PartialEq)] pub struct R6502 { - pub a: u8, // Accumulator + a: u8, // Accumulator x: u8, // X Register y: u8, // Y Register @@ -45,9 +56,8 @@ pub struct R6502 cycles: u32, // Track cycles // Helper Vars - working_data: u8, // value fetched for the ALU - addr_abs: u16, - addr_rel: u16, + working_data: u16, // value fetched for the ALU + // working_addr: u16, } impl R6502 @@ -55,7 +65,38 @@ 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, addr_abs: 0, addr_rel: 0 } + R6502 { a: 0, x: 0, y: 0, pc: 0, sp: 0, status: 0, cycles: 0, working_data: 0 } + } + + // Debug Access + pub fn debug_get_reg(&self, reg: Registers) -> u16 + { + match reg + { + Registers::A => self.a as u16, + Registers::X => self.x as u16, + Registers::Y => self.y as u16, + + Registers::PC => self.pc, + Registers::SP => self.sp as u16, + + Registers::STATUS => self.status as u16, + } + } + + pub fn debug_set_reg(&mut self, reg: Registers, value: u16) + { + match reg + { + Registers::A => self.a = value as u8, + Registers::X => self.x = value as u8, + Registers::Y => self.y = value as u8, + + Registers::PC => self.pc = value, + Registers::SP => self.sp = value as u8, + + Registers::STATUS => self.status = value as u8, + } } // signals @@ -90,8 +131,7 @@ impl R6502 // internal helper variables self.working_data = 0; - self.addr_abs = 0; - self.addr_rel = 0; + // self.working_addr = 0; self.cycles = 8; } diff --git a/src/tests/mod.rs b/src/tests/mod.rs new file mode 100644 index 0000000..aa5eeb2 --- /dev/null +++ b/src/tests/mod.rs @@ -0,0 +1,631 @@ + +#![allow(dead_code, non_snake_case)] + +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); + + // 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/test_bus.rs b/src/tests/test_bus.rs new file mode 100644 index 0000000..91ac7b2 --- /dev/null +++ b/src/tests/test_bus.rs @@ -0,0 +1,28 @@ +use crate::r6502::Bus; + +// All-RAM bus for testing +pub struct RAMBus +{ + ram: [u8; 64 * 1024] +} + +impl RAMBus +{ + pub fn new() -> RAMBus + { + RAMBus { ram: [0; 64 * 1024] } + } +} + +impl Bus for RAMBus +{ + fn read(&self, addr: u16) -> u8 + { + self.ram[addr as usize] + } + + fn write(&mut self, addr: u16, value: u8) + { + self.ram[addr as usize] = value; + } +} \ No newline at end of file diff --git a/todo/todo.todo b/todo/todo.todo index d725a28..af79cc8 100644 --- a/todo/todo.todo +++ b/todo/todo.todo @@ -1,19 +1,29 @@ General: + ✔ Add unit tests for each instruction and address mode @done(23-11-07 19:57) ☐ Fully implement clock cycle tracking ☐ Add a disassembler for debugging ☐ Debug data lookup for instructions +CPU: + Signals: + ✔ Clock @done(23-11-07 20:45) + ✔ Reset @done(23-11-07 20:46) + ☐ irq function + ☐ nmi function + Addressing modes: - ☐ IMP - Implied + ✔ IMP - Implied @done(23-11-07 14:01) ✔ IMM - Immediate @done(23-11-06 19:01) - ☐ ZP0 - Zero Page - ☐ ZPX - Zero Page, X - ☐ ZPY - Zero Page, Y + ✔ ZP0 - Zero Page @done(23-11-07 14:40) + ✔ ZPX - Zero Page, X @done(23-11-07 14:40) + ✔ ZPY - Zero Page, Y @done(23-11-07 14:40) ☐ REL - Relative - ☐ ABS - Absolute - ☐ ABX - Absolute, X - ☐ ABY - Absolute, Y + ✔ Code @done(23-11-07 20:44) + ☐ Test + ✔ ABS - Absolute @done(23-11-07 20:25) + ✔ ABX - Absolute, X @done(23-11-07 20:25) + ✔ ABY - Absolute, Y @done(23-11-07 20:25) ☐ IND - Indirect ☐ IZX - Indirect, X ☐ IZY - Indirect, Y @@ -21,10 +31,10 @@ Addressing modes: Instructions: GROUP ONE: ✔ 000 ORA @done(23-11-06 18:55) - ☐ 001 AND - ☐ 010 EOR - ☐ 011 ADC - ☐ 100 STA + ✔ 001 AND @done(23-11-07 13:43) + ✔ 010 EOR @done(23-11-07 13:43) + ✔ 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