From 93e16f0d024c5c2053ac44a22b11c78e72c9a2c8 Mon Sep 17 00:00:00 2001 From: Joey Pollack Date: Tue, 28 Nov 2023 15:16:41 -0500 Subject: [PATCH] Adds (Indirect, X) addressing mode --- src/r6502/addressing_modes.rs | 27 ++++++++- src/tests/instructions/ADC.rs | 37 ++++++++++++ src/tests/instructions/AND.rs | 38 +++++++++++++ src/tests/instructions/CMP.rs | 103 ++++++++++++++++++++++++++++++++++ src/tests/instructions/EOR.rs | 39 +++++++++++++ src/tests/instructions/LDA.rs | 37 ++++++++++++ src/tests/instructions/ORA.rs | 42 ++++++++++++++ src/tests/instructions/SBC.rs | 42 ++++++++++++++ src/tests/instructions/STA.rs | 41 ++++++++++++++ 9 files changed, 405 insertions(+), 1 deletion(-) diff --git a/src/r6502/addressing_modes.rs b/src/r6502/addressing_modes.rs index 868dfe9..a3d9f5d 100644 --- a/src/r6502/addressing_modes.rs +++ b/src/r6502/addressing_modes.rs @@ -168,9 +168,34 @@ impl AddressingModes ModeID::IND } + + // Indexed Indirect Addressing (IND, X) + // In indexed indirect addressing (referred to as (Indirect, X)), the second byte of the + // instruction is added to the contents of the X register, discarding the carry. + // The result of the addition points to a memory location on the Zero Page which contains + // the low order byte of the effective address. The next memory location in page zero, + // contains the high order byte of the effective address. Both memory locations specifying + // the effective address must be in the Zero Page. + // + // Info from: + // https://web.archive.org/web/20221112231348if_/http://archive.6502.org/datasheets/rockwell_r650x_r651x.pdf pub fn IZX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID { - + let offset = bus.read(cpu.pc) as u16; + cpu.pc += 1; + let mut pointer = cpu.x as u16 + offset; + + // discard the carry and wrap + // If the addition goes beyond the Zero Page + // it should wrap around back to the beginning + pointer = pointer & 0x00FF; + + let lo_byte = bus.read(pointer) as u16; + let hi_byte = bus.read(pointer + 1) as u16; + cpu.working_addr = (hi_byte << 0x08) | lo_byte; + + cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; + ModeID::IZX } diff --git a/src/tests/instructions/ADC.rs b/src/tests/instructions/ADC.rs index 2553655..6c53606 100644 --- a/src/tests/instructions/ADC.rs +++ b/src/tests/instructions/ADC.rs @@ -211,6 +211,43 @@ fn ABX() // 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 IZX() +{ + 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 + bus.write(0x010C, 0x04); + + // Manually put 0x010C into the Zero Page + bus.write(0x00B, 0x0C); + bus.write(0x00C, 0x01); + + // ADC $10C + bus.write(addr, 0x61); // ADC - Indirect, X mode + bus.write(addr + 1, 0x0A); // Argument pointer into the Zero Page + // 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); // Zero Page pointer offset + + // 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 index de89a8b..36b4a5f 100644 --- a/src/tests/instructions/AND.rs +++ b/src/tests/instructions/AND.rs @@ -202,6 +202,44 @@ fn ABY() // 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 IZX() +{ + 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, 0x03); + + // Manually put 0x010C into the Zero Page + bus.write(0x000B, 0x0C); + bus.write(0x000C, 0x01); + + // AND #3 + bus.write(addr, 0x21); // AND - Indirect, X mode + bus.write(addr + 1, 0x0A); // Argument - Pointer into the Zero Page + + // 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); // Zero Page pointer offset + + // 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 index 9c6cc30..1da17bf 100644 --- a/src/tests/instructions/CMP.rs +++ b/src/tests/instructions/CMP.rs @@ -562,6 +562,109 @@ fn ABY() // 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)); +} + +////////////////////////////////////////////////////////////////////////////// +/// IZX IZX IZX IZX IZX IZX IZX IZX IZX +////////////////////////////////////////////////////////////////////////////// +#[test] +fn IZX() +{ + 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); + + // Manually put 0x010B into the Zero Page + bus.write(0x000B, 0x0B); + bus.write(0x000C, 0x01); + + // Program to compare 0x05 to 0x10 + bus.write(addr, 0xC1); // CMP - Indirect, X mode + bus.write(addr + 1, 0x0A); // Argument lo 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); + + // Manually put 0x010B into the Zero Page + bus.write(0x000B, 0x0B); + bus.write(0x000C, 0x01); + + // Program to compare 0x05 to 0x10 + bus.write(addr, 0xC1); // CMP - Indirect, X mode + bus.write(addr + 1, 0x0A); // Argument lo 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, 0xC1); // CMP - Indirect, X mode + bus.write(addr + 1, 0x0A); // Argument lo 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)); diff --git a/src/tests/instructions/EOR.rs b/src/tests/instructions/EOR.rs index a42c10e..575cc70 100644 --- a/src/tests/instructions/EOR.rs +++ b/src/tests/instructions/EOR.rs @@ -202,6 +202,45 @@ fn ABY() // 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 IZX() +{ + 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); + + // Manually put 0x010C in the Zero Page + bus.write(0x000B, 0x0C); + bus.write(0x000C, 0x01); + + // AND #3 + bus.write(addr, 0x41); // EOR - Indirect, 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)); } \ No newline at end of file diff --git a/src/tests/instructions/LDA.rs b/src/tests/instructions/LDA.rs index 08eb715..f8a5d56 100644 --- a/src/tests/instructions/LDA.rs +++ b/src/tests/instructions/LDA.rs @@ -189,6 +189,43 @@ fn ABY() // 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 IZX() +{ + 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 + bus.write(0x010B, 0x08); + + // Manuall put 0x010B into the Zero page at 0x000A + bus.write(0x000A, 0x0B); // Pointer lo byte + bus.write(0x000B, 0x01); // Pointer hi byte + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xA1); // LDA - Indirect, X mode + bus.write(addr + 1, 0x09); // Argument - gets added to X reg + + // 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)); } \ No newline at end of file diff --git a/src/tests/instructions/ORA.rs b/src/tests/instructions/ORA.rs index d38be88..d0b80c3 100644 --- a/src/tests/instructions/ORA.rs +++ b/src/tests/instructions/ORA.rs @@ -218,6 +218,48 @@ fn ABY() 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 IZX() +{ + 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); + + // Manually put 0x010B into the Zero Page + bus.write(0x000B, 0x0B); + bus.write(0x000C, 0x01); + + // ORA #2 + bus.write(addr, 0x01); // ORA - Indirect, X mode + bus.write(addr + 1, 0x0A); // Argument - Pointer to Zero Page + + // Restart cpu + cpu.reset(&mut bus); + + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x01); // Zero page pointer offset + cpu.debug_set_reg(Registers::A, 0x09); + + // Clock the cpu twice (Clock essentially runs one full instruction) cpu.clock(&mut bus); diff --git a/src/tests/instructions/SBC.rs b/src/tests/instructions/SBC.rs index cf68e30..82fdcae 100644 --- a/src/tests/instructions/SBC.rs +++ b/src/tests/instructions/SBC.rs @@ -228,6 +228,48 @@ fn ABY() // 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)); +} + +////////////////////////////////////////////////////////////////////////////// +/// IZX IZX IZX IZX IZX IZX IZX IZX IZX +////////////////////////////////////////////////////////////////////////////// + +#[test] +fn IZX() +{ + 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); + + // Manually put 0x010B into the Zero page + bus.write(0x000B, 0x0B); + bus.write(0x000C, 0x01); + + // Program to + bus.write(addr, 0xE1); // - Indirect, X mode + bus.write(addr + 1, 0x0A); // Argument - Pointer into the Zero Page + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x01); // Zero Page Pointer offset + 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 index 3fc1d6c..308e3c0 100644 --- a/src/tests/instructions/STA.rs +++ b/src/tests/instructions/STA.rs @@ -176,6 +176,47 @@ fn ABY() 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 IZX() +{ + 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); + + // Manuall put 0x010B into the Zero Page + bus.write(0x000B, 0x0B); + bus.write(0x000C, 0x01); + + // STA + bus.write(addr, 0x81); // STA - Indirect, X mode + bus.write(addr + 1, 0x0A); // Argument - Pointer into the Zero page + + // Restart cpu + cpu.reset(&mut bus); + + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x01); // Zero Page pointer offset + cpu.debug_set_reg(Registers::A, 0x0F); + + // Clock the cpu twice (Clock essentially runs one full instruction) cpu.clock(&mut bus);