From b6029d0ef5dda645215507118d916f2df3c196f2 Mon Sep 17 00:00:00 2001 From: joeyrp Date: Wed, 10 Jan 2024 18:11:07 -0500 Subject: [PATCH] Adds the rest of group three instructions and begins work on branching --- src/r6502/addressing_modes.rs | 5 -- src/r6502/instructions.rs | 97 ++++++++++++++++++++++++++- src/r6502/mod.rs | 38 +++++++++-- src/tests/instructions/mod.rs | 123 ++++++++++++++++++++++++++++++++++ todo/todo.todo | 14 ++-- 5 files changed, 259 insertions(+), 18 deletions(-) diff --git a/src/r6502/addressing_modes.rs b/src/r6502/addressing_modes.rs index 075c838..363abf1 100644 --- a/src/r6502/addressing_modes.rs +++ b/src/r6502/addressing_modes.rs @@ -152,11 +152,6 @@ impl AddressingModes 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; diff --git a/src/r6502/instructions.rs b/src/r6502/instructions.rs index cd8c7e5..701bc33 100644 --- a/src/r6502/instructions.rs +++ b/src/r6502/instructions.rs @@ -53,6 +53,17 @@ impl Instructions Instructions::CPY, // 110 CPY Instructions::CPX, // 111 CPX ]; + + pub const GROUP_BRANCHING_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ + Instructions::BPL, + Instructions::BMI, + Instructions::BVC, + Instructions::BVS, + Instructions::BCC, + Instructions::BCS, + Instructions::BNE, + Instructions::BEQ, + ]; @@ -474,26 +485,110 @@ impl Instructions pub fn STY(cpu: &mut R6502, bus: &mut dyn Bus) { - + bus.write(cpu.working_addr, cpu.y); } pub fn LDY(cpu: &mut R6502, bus: &mut dyn Bus) { + let data = cpu.working_data as u8; + cpu.y = data; + + if cpu.y == 0 + { + cpu.set_flag(Flags::Z); + } + if cpu.y & 0x80 != 0 + { + cpu.set_flag(Flags::N); + } } pub fn CPY(cpu: &mut R6502, bus: &mut dyn Bus) { + cpu.clear_flag(Flags::C); + if cpu.y as u16 >= cpu.working_data + { + cpu.set_flag(Flags::C); + } + + cpu.clear_flag(Flags::Z); + if cpu.y == cpu.working_data as u8 + { + cpu.set_flag(Flags::Z); + } + cpu.clear_flag(Flags::N); + if (cpu.y as u16 - cpu.working_data) & 0x80 > 0 + { + cpu.set_flag(Flags::N); + } } pub fn CPX(cpu: &mut R6502, bus: &mut dyn Bus) { + cpu.clear_flag(Flags::C); + if cpu.x as u16 >= cpu.working_data + { + cpu.set_flag(Flags::C); + } + + cpu.clear_flag(Flags::Z); + if cpu.x == cpu.working_data as u8 + { + cpu.set_flag(Flags::Z); + } + + cpu.clear_flag(Flags::N); + if (cpu.x as u16 - cpu.working_data) & 0x80 > 0 + { + cpu.set_flag(Flags::N); + } } /////////////////////////////////////////////////////////// // BRANCHING + fn BPL(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + fn BMI(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + fn BVC(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + fn BVS(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + fn BCC(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + fn BCS(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + fn BNE(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + fn BEQ(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + /////////////////////////////////////////////////////////// diff --git a/src/r6502/mod.rs b/src/r6502/mod.rs index 834a5bd..f5d52bd 100644 --- a/src/r6502/mod.rs +++ b/src/r6502/mod.rs @@ -175,15 +175,32 @@ fn execute(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) { // Instruction decoding: // https://llx.com/Neil/a2/opcodes.html + + // Check if this is a branch instruction + // The conditional branch instructions all have the form xxy10000 + // 0x1F == 11111 + // 0x10 == 10000 + const BRANCH_OP: u8 = 0x10; + const BRANCH_MASK: u8 = 0x1F; + if instruction & BRANCH_MASK == BRANCH_OP + { + exe_branch(instruction, cpu, bus); + return; + } + + // Single byte instructions + // Instructions with arguments + const GROUP_ONE_OP: u8 = 0x01; + const GROUP_TWO_OP: u8 = 0x02; + const GROUP_THREE_OP: u8 = 0x00; + let group_code = instruction & 0x03; // group one has a bit pattern of xxxxxx01 match group_code { - 0x01 => exe_group_one(instruction, cpu, bus), - 0x02 => exe_group_two(instruction, cpu, bus), - 0x00 => exe_group_three(instruction, cpu, bus), - - // TODO: Conditionals and specially formatted instructions + GROUP_ONE_OP => exe_group_one(instruction, cpu, bus), + GROUP_TWO_OP => exe_group_two(instruction, cpu, bus), + GROUP_THREE_OP => exe_group_three(instruction, cpu, bus), _ => panic!("UNKNOWN INSTRUCTION ADDRESS MODE: {}", group_code) } @@ -255,4 +272,15 @@ fn exe_group_three(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) } Instructions::GROUP_THREE_OPS[op_mask as usize](cpu, bus); +} + +fn exe_branch(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) +{ + let pc_offset = AddressingModes::REL(cpu, bus); + + // TODO: Decode instruction + // Need to map: 10 30 50 70 90 B0 D0 F0 - op code + // to: 0 1 2 3 4 5 6 7 - method index + + // ChatGPT says: Index = (Value−16​) / 32 } \ No newline at end of file diff --git a/src/tests/instructions/mod.rs b/src/tests/instructions/mod.rs index 4144c69..fd615b0 100644 --- a/src/tests/instructions/mod.rs +++ b/src/tests/instructions/mod.rs @@ -534,4 +534,127 @@ fn JMP() // Is the program counter now 0x1234? assert_eq!(0x1234, cpu.debug_get_reg(Registers::PC)); +} + + +#[test] +fn STY() +{ + 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 + + + // STY $0A + bus.write(addr, 0x84); // STY - 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::Y, 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 LDY() +{ + 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 Y Register + bus.write(addr, 0xA0); // LDY - 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 Y register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::Y)); +} + +#[test] +fn CPY() +{ + // TODO: More tests for CPY and CPX + + 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 compare 0x10 and 0x10 + bus.write(addr, 0xC0); // CPY - Immediate mode + bus.write(addr + 1, 0x10); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // Preload the Y register with value 0x10 + cpu.debug_set_reg(Registers::Y, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is the Z flag set? + assert_eq!(1, cpu.check_flag(Flags::Z)); +} + +#[test] +fn CPX() +{ + 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 compare 0x10 and 0x10 + bus.write(addr, 0xE0); // CPX - Immediate mode + bus.write(addr + 1, 0x10); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // Preload the X register with value 0x10 + cpu.debug_set_reg(Registers::X, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is the Z flag set? + assert_eq!(1, cpu.check_flag(Flags::Z)); } \ No newline at end of file diff --git a/todo/todo.todo b/todo/todo.todo index 59f57ff..c02f264 100644 --- a/todo/todo.todo +++ b/todo/todo.todo @@ -24,9 +24,9 @@ Addressing modes: ✔ 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 + ✔ IND - Indirect @done(24-01-10 15:26) + ✔ IZX - Indirect, X @done(24-01-10 15:26) + ✔ IZY - Indirect, Y @done(24-01-10 15:26) Instructions: GROUP ONE: @@ -53,10 +53,10 @@ Instructions: ✔ 001 BIT @done(24-01-09 16:16) ✔ 010 JMP @done(24-01-09 16:16) ✔ 011 JMP (abs) @done(24-01-09 16:16) - ☐ 100 STY - ☐ 101 LDY - ☐ 110 CPY - ☐ 111 CPX + ✔ 100 STY @done(24-01-10 14:10) + ✔ 101 LDY @done(24-01-10 14:10) + ✔ 110 CPY @done(24-01-10 15:26) + ✔ 111 CPX @done(24-01-10 15:26) CONDITIONALS: ☐ 10 BPL