diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index c35bd29..9fb9e23 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -201,6 +201,8 @@ namespace ComSquare::CPU switch (opcode) { case Instructions::BRK: this->BRK(); return 7 + !this->_isEmulationMode; + case Instructions::COP: this->COP(); return 7 + !this->_isEmulationMode; + case Instructions::RTI: this->RTI(); return 6 + !this->_isEmulationMode; case Instructions::ADC_IM: this->ADC(this->_getImmediateAddr()); return 2 + !this->_registers.p.m; diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 6276732..855e89f 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -186,6 +186,7 @@ namespace ComSquare::CPU enum Instructions { BRK = 0x00, + COP = 0x02, RTI = 0x40, ADC_DPXi = 0x61, @@ -365,6 +366,8 @@ namespace ComSquare::CPU //! @brief Break instruction - Causes a software break. The PC is loaded from a vector table. void BRK(); + //! @brief Co-Processor Enable instruction - Causes a software break. The PC is loaded from a vector table. + void COP(); //! @brief Return from Interrupt - Used to return from a interrupt handler. void RTI(); //! @brief Add with carry - Adds operand to the Accumulator; adds an additional 1 if carry is set. diff --git a/sources/CPU/Instructions/Interrupts.cpp b/sources/CPU/Instructions/Interrupts.cpp index 9ffaa09..1c5d021 100644 --- a/sources/CPU/Instructions/Interrupts.cpp +++ b/sources/CPU/Instructions/Interrupts.cpp @@ -43,6 +43,28 @@ namespace ComSquare::CPU } } + void CPU::COP() + { + if (this->_isEmulationMode) { + this->_registers.pc += 2; + this->_push(this->_registers.pc); + this->_push(this->_registers.p.flags); + this->_registers.p.i = true; + this->_registers.p.d = false; + this->_registers.pc = this->_cartridgeHeader.emulationInterrupts.cop; + + } else { + this->_push(this->_registers.pbr); + this->_registers.pc += 2; + this->_push(this->_registers.pc); + this->_push(this->_registers.p.flags); + this->_registers.p.i = true; + this->_registers.p.d = false; + this->_registers.pbr = 0x0; + this->_registers.pc = this->_cartridgeHeader.nativeInterrupts.cop; + } + } + void CPU::RTI() { this->_registers.p.flags = this->_pop(); diff --git a/sources/Debugger/CPUDebug.cpp b/sources/Debugger/CPUDebug.cpp index 873e53b..20a5040 100644 --- a/sources/Debugger/CPUDebug.cpp +++ b/sources/Debugger/CPUDebug.cpp @@ -4,6 +4,7 @@ #include "CPUDebug.hpp" #include "../Utility/Utility.hpp" +#include "../Exceptions/InvalidOpcode.hpp" using namespace ComSquare::CPU; @@ -24,14 +25,20 @@ namespace ComSquare::Debugger unsigned CPUDebug::update() { - if (!this->isVisible()) { - this->_snes.disableCPUDebugging(); - return 0; - } + try { + if (!this->isVisible()) { + this->_snes.disableCPUDebugging(); + return 0; + } - if (this->_isPaused) + if (this->_isPaused) + return 0xFF; + return CPU::update(); + } catch (InvalidOpcode &e) { + this->pause(); + this->_ui.logger->append(e.what()); return 0xFF; - return CPU::update(); + } } unsigned CPUDebug::_executeInstruction(uint8_t opcode) @@ -106,6 +113,8 @@ namespace ComSquare::Debugger switch (opcode) { case Instructions::BRK: return "BRK"; + case Instructions::COP: return "COP"; + case Instructions::RTI: return "RTI"; case Instructions::ADC_IM: return "ADC"; diff --git a/tests/CPU/testInterupts.cpp b/tests/CPU/testInterupts.cpp index 9780e15..3773285 100644 --- a/tests/CPU/testInterupts.cpp +++ b/tests/CPU/testInterupts.cpp @@ -49,4 +49,45 @@ Test(CPU_native, BRK) cr_assert_eq(data, 0x158u, "The program counter should be incremented by two and pushed on the stack but it was 0x%X (expected 0x158).", data); data = pair.second.cpu->_pop(); cr_assert_eq(data, 0x15, "The program bank register should be pushed on the stack but it was 0x%X (expected 0x15).", data); +} + +Test(CPU_emulated, COP) +{ + auto pair = Init(); + pair.second.cpu->_isEmulationMode = true; + pair.second.cartridge->header.emulationInterrupts.cop = 0x123u; + pair.second.cpu->_registers.p.flags = 0x0F; + pair.second.cpu->_registers.pc = 0x156u; + pair.second.cpu->_registers.pbr = 0x15; + pair.second.cpu->COP(); + cr_assert_eq(pair.second.cpu->_registers.pc, 0x123u, "The program counter should be 0x123u but it was 0x%X", pair.second.cpu->_registers.pc); + cr_assert_eq(pair.second.cpu->_registers.pbr, 0x15, "The PBR should be 0x15 but it was 0x%X", pair.second.cpu->_registers.pbr); + cr_assert_eq(pair.second.cpu->_registers.p.d, false, "The decimal flag should not be set."); + cr_assert_eq(pair.second.cpu->_registers.p.i, true, "The Interrupt disable flag should be set."); + cr_assert_eq(pair.second.cpu->_registers.p.x_b, false, "The break flag should not be set."); + int data = pair.second.cpu->_pop(); + cr_assert_eq(data, 0x0F, "The Status Registers should be pushed into the stack with the value 0x0F but it was 0x%X (expected 0xF1).", data); + data = pair.second.cpu->_pop16(); + cr_assert_eq(data, 0x158u, "The program counter should be incremented by two and pushed on the stack but it was 0x%X (expected 0x158).", data); +} + +Test(CPU_native, COP) +{ + auto pair = Init(); + pair.second.cpu->_isEmulationMode = false; + pair.second.cartridge->header.nativeInterrupts.cop = 0x123u; + pair.second.cpu->_registers.p.flags = 0xF1; + pair.second.cpu->_registers.pc = 0x156u; + pair.second.cpu->_registers.pbr = 0x15; + pair.second.cpu->COP(); + cr_assert_eq(pair.second.cpu->_registers.pc, 0x123u, "The program counter should be 0x123u but it was 0x%X", pair.second.cpu->_registers.pc); + cr_assert_eq(pair.second.cpu->_registers.pbr, 0x0, "The PBR should be 0x0 but it was 0x%X", pair.second.cpu->_registers.pbr); + cr_assert_eq(pair.second.cpu->_registers.p.d, false, "The decimal flag should not be set."); + cr_assert_eq(pair.second.cpu->_registers.p.i, true, "The Interrupt disable flag should be set."); + int data = pair.second.cpu->_pop(); + cr_assert_eq(data, 0xF1, "The Status Registers should be pushed into the stack with the value 0xF1 but it was 0x%X (expected 0xF1).", data); + data = pair.second.cpu->_pop16(); + cr_assert_eq(data, 0x158u, "The program counter should be incremented by two and pushed on the stack but it was 0x%X (expected 0x158).", data); + data = pair.second.cpu->_pop(); + cr_assert_eq(data, 0x15, "The program bank register should be pushed on the stack but it was 0x%X (expected 0x15).", data); } \ No newline at end of file