From 37cf4aa7f5714fea03646aa4946410649ae7fe1e Mon Sep 17 00:00:00 2001 From: matheusgomes28 Date: Tue, 15 Oct 2024 21:15:44 +0100 Subject: [PATCH] Adding support for remaining CMP opcodes: + Zeropage indexed + Absolute indexed + Indirect indexed + Indexed indirect --- README.md | 16 +++--- emulator/emulator.cpp | 116 +++++++++++++++++++++++++++++++++++------- 2 files changed, 107 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 8a9c378..3624896 100644 --- a/README.md +++ b/README.md @@ -302,12 +302,12 @@ Total number of `ROL` instructions: `5`. |------------|---------------------|------------------|-----------|------------|--------------------| | CMP | Immediate | 0xC9 | 2 | 2 | :white_check_mark: | | CMP | Zero Page | 0xC5 | 2 | 3 | :white_check_mark: | -| CMP | Zero Page, X | 0xD5 | 2 | 4 | :x: | -| CMP | Absolute | 0xCD | 3 | 4 | :x: | -| CMP | Absolute, X | 0xDD | 3 | 4* | :x: | -| CMP | Absolute, Y | 0xD9 | 3 | 4* | :x: | -| CMP | (Indirect, X) | 0xC1 | 2 | 6 | :x: | -| CMP | (Indirect), Y | 0xD1 | 2 | 5* | :x: | +| CMP | Zero Page, X | 0xD5 | 2 | 4 | :ok: | +| CMP | Absolute | 0xCD | 3 | 4 | :ok: | +| CMP | Absolute, X | 0xDD | 3 | 4* | :ok: | +| CMP | Absolute, Y | 0xD9 | 3 | 4* | :ok: | +| CMP | (Indirect, X) | 0xC1 | 2 | 6 | :ok: | +| CMP | (Indirect), Y | 0xD1 | 2 | 5* | :ok: | Total number of `CMP` instructions: `8`. @@ -317,7 +317,7 @@ Total number of `CMP` instructions: `8`. |------------|---------------------|------------------|-----------|------------|--------------------| | CPX | Immediate | 0xE0 | 2 | 2 | :white_check_mark: | | CPX | Zero Page | 0xE4 | 2 | 3 | :white_check_mark: | -| CPX | Absolute | 0xEC | 3 | 4 | :x: | +| CPX | Absolute | 0xEC | 3 | 4 | :ok: | Total number of `CPX` instructions: `3`. @@ -327,7 +327,7 @@ Total number of `CPX` instructions: `3`. |------------|---------------------|------------------|-----------|------------|--------------------| | CPY | Immediate | 0xC0 | 2 | 2 | :white_check_mark: | | CPY | Zero Page | 0xC4 | 2 | 3 | :white_check_mark: | -| CPY | Absolute | 0xCC | 3 | 4 | :x: | +| CPY | Absolute | 0xCC | 3 | 4 | :ok: | Total number of `CPY` instructions: `3`. diff --git a/emulator/emulator.cpp b/emulator/emulator.cpp index 9b28c3c..c604c06 100644 --- a/emulator/emulator.cpp +++ b/emulator/emulator.cpp @@ -1089,6 +1089,14 @@ std::optional sta_index_indirect(emulator::Cpu& cpu, std::spa } // Compare instructions here +void cmp_operation(emulator::Cpu& cpu, std::uint8_t emulator::Registers::*reg, std::uint8_t val) +{ + auto const comparison = (cpu.reg).*reg - val; + cpu.flags.n = comparison & 0b1000'0000; // Negative flag + cpu.flags.z = comparison == 0; // Negative flag + cpu.flags.c = (cpu.reg).*reg >= val; +} + /// Compares whichever register was given to the immediate /// value in the next address in the program array @@ -1102,12 +1110,7 @@ Instruction cmp_immediate_reg(std::uint8_t emulator::Registers::*reg) return std::nullopt; } - auto const value = program[cpu.reg.pc + 1]; - auto const comparison = (cpu.reg).*reg - value; - cpu.flags.n = comparison & 0b1000'0000; // Negative flag - cpu.flags.z = comparison == 0; // Negative flag - cpu.flags.c = (cpu.reg).*reg >= value; - + cmp_operation(cpu, reg, program[cpu.reg.pc + 1]); return std::make_optional(2); }; } @@ -1123,17 +1126,91 @@ Instruction cmp_zeropage_reg(std::uint8_t emulator::Registers::*reg) return std::nullopt; } - auto const memory = program[cpu.reg.pc + 1]; - auto const value = cpu.mem[memory]; - auto const comparison = (cpu.reg).*reg - value; - cpu.flags.n = comparison & 0b1000'0000; // Negative flag - cpu.flags.z = comparison == 0; // Negative flag - cpu.flags.n = (cpu.reg).*reg >= value; - + auto const memory = program[cpu.reg.pc + 1]; + cmp_operation(cpu, reg, cpu.mem[memory]); return std::make_optional(2); }; } +std::optional cmp_zp_indexed(emulator::Cpu& cpu, std::span program) +{ + ENABLE_PROFILER(cpu); + if ((cpu.reg.pc + 1) >= program.size()) + { + return std::nullopt; + } + + auto const pos = zeropage_indexed(cpu, cpu.mem[cpu.reg.pc + 1], &emulator::Registers::x); + cmp_operation(cpu, &emulator::Registers::a, cpu.mem[pos]); + return std::make_optional(2, 4); +} + +Instruction cmp_absolute(std::uint8_t emulator::Registers::*reg) +{ + return [=](emulator::Cpu& cpu, std::span program) -> std::optional + { + ENABLE_PROFILER(cpu); + if ((cpu.reg.pc + 2) >= program.size()) + { + return std::nullopt; + } + + // (hsb << 8) + lsb convert little endian to the address + auto const lsb = program[cpu.reg.pc + 1]; + auto const hsb = program[cpu.reg.pc + 2]; + auto const addr = static_cast((hsb << 8) | lsb); + auto const memory = program[addr]; + + cmp_operation(cpu, reg, cpu.mem[memory]); + return std::make_optional(3, 4); + }; +} + +Instruction cmp_abs_indexed(std::uint8_t emulator::Registers::*index) +{ + return [=](emulator::Cpu& cpu, std::span program) -> std::optional + { + ENABLE_PROFILER(cpu); + if ((cpu.reg.pc + 2) >= program.size()) + { + return std::nullopt; + } + + auto const pos = absolute_indexed(cpu, program[cpu.reg.pc + 1], program[cpu.reg.pc + 2], index); + cmp_operation(cpu, &emulator::Registers::a, cpu.mem[pos]); + + // TODO : Cycle add should reflect the boundary checks + std::size_t const cycle_add = 0; + return std::make_optional(3, 4 + cycle_add); + }; +} + +std::optional cmp_indexed_indirect(emulator::Cpu& cpu, std::span program) +{ + ENABLE_PROFILER(cpu); + if ((cpu.reg.pc + 1) >= program.size()) + { + return std::nullopt; + } + + auto const pos = indexed_indirect(cpu, cpu.mem[cpu.reg.pc + 1]); + cmp_operation(cpu, &emulator::Registers::a, cpu.mem[pos]); + return std::make_optional(2, 6); +} + +std::optional cmp_indirect_indexed(emulator::Cpu& cpu, std::span program) +{ + ENABLE_PROFILER(cpu); + if ((cpu.reg.pc + 1) >= program.size()) + { + return std::nullopt; + } + + auto const pos = indirect_indexed(cpu, cpu.mem[cpu.reg.pc + 1]); + cmp_operation(cpu, &emulator::Registers::a, cpu.mem[pos]); + return std::make_optional(2, 6); +} + // Branching functions here std::optional bne(emulator::Cpu& cpu, std::span program) { @@ -1547,15 +1624,21 @@ std::array get_instructions() supported_instructions[0xbc] = ld_absolute_plus_reg(&emulator::Registers::y, &emulator::Registers::x); supported_instructions[0xac] = ld_absolute(&emulator::Registers::y); - // This is the immediate mode compares to registers + // CMP, CPX, CPY opcodes supported_instructions[0xc9] = cmp_immediate_reg(&emulator::Registers::a); // TODO : test supported_instructions[0xc0] = cmp_immediate_reg(&emulator::Registers::y); supported_instructions[0xe0] = cmp_immediate_reg(&emulator::Registers::x); - - // TODO : support for compare zeropage simple supported_instructions[0xc5] = cmp_zeropage_reg(&emulator::Registers::a); supported_instructions[0xe4] = cmp_zeropage_reg(&emulator::Registers::x); // TODO : test supported_instructions[0xc4] = cmp_zeropage_reg(&emulator::Registers::y); // TODO : test + supported_instructions[0xcd] = cmp_absolute(&emulator::Registers::a); + supported_instructions[0xec] = cmp_absolute(&emulator::Registers::x); + supported_instructions[0xcc] = cmp_absolute(&emulator::Registers::y); + supported_instructions[0xd5] = cmp_zp_indexed; + supported_instructions[0xdd] = cmp_abs_indexed(&emulator::Registers::x); + supported_instructions[0xd9] = cmp_abs_indexed(&emulator::Registers::y); + supported_instructions[0xc1] = cmp_indexed_indirect; + supported_instructions[0xd1] = cmp_indirect_indexed; supported_instructions[0xf0] = branch_flag_value(&emulator::Flags::z); supported_instructions[0xd0] = branch_flag_value(&emulator::Flags::z); @@ -1689,7 +1772,6 @@ std::optional execute_next( export namespace emulator { - std::size_t execute(Cpu& cpu, std::span program) { std::size_t n_cycles = 0;