From 5f0ed5f5614f1f36b7552a4317ad19a41ae98253 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Thu, 14 May 2020 17:37:06 +0200 Subject: [PATCH] Adding the WAI and handling NMI & IRQ interrupts --- sources/CPU/CPU.cpp | 35 ++++++++++++++++++++--- sources/CPU/CPU.hpp | 24 ++++++++++++++-- sources/CPU/Instructions/Interrupts.cpp | 38 ++++++++++++------------- 3 files changed, 71 insertions(+), 26 deletions(-) diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index 4f2a727..88ce592 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -205,13 +205,40 @@ namespace ComSquare::CPU { unsigned cycles = 0; - if (this->_isStopped) - return 0xFF; - for (int i = 0; i < 0xFF; i++) - cycles += this->_executeInstruction(this->readPC()); + for (int i = 0; i < 0xFF; i++) { + if (this->_isStopped) { + cycles += 1; + continue; + } + + this->_checkInterrupts(); + + if (!this->_isWaitingForInterrupt) + cycles += this->_executeInstruction(this->readPC()); + } return cycles; } + void CPU::_checkInterrupts() + { + if (!this->IsNMIRequested && !this->IsIRQRequested && !this->IsAbortRequested) + return; + this->_isWaitingForInterrupt = false; + + if (this->IsNMIRequested) { + this->_runInterrupt( + this->_cartridgeHeader.nativeInterrupts.nmi, + this->_cartridgeHeader.emulationInterrupts.nmi); + return; + } + if (this->IsIRQRequested && !this->_registers.p.i) { + this->_runInterrupt( + this->_cartridgeHeader.nativeInterrupts.irq, + this->_cartridgeHeader.emulationInterrupts.irq); + return; + } + } + uint24_t CPU::_getValueAddr(Instruction &instruction) { switch (instruction.addressingMode) { diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 2b95861..808796b 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -186,12 +186,16 @@ namespace ComSquare::CPU protected: //! @brief All the registers of the CPU Registers _registers{}; + //! @brief Internal registers of the CPU (accessible from the bus via addr $4200 to $421F). + InternalRegisters _internalRegisters{}; + //! @brief Is the CPU running in emulation mode (in 8bits) bool _isEmulationMode = true; //! @brief If the processor is stopped (using an STP instruction), the clock is stopped and no instruction will be run until a manual reset. bool _isStopped = false; - //! @brief Internal registers of the CPU (accessible from the bus via addr $4200 to $421F). - InternalRegisters _internalRegisters{}; + //! @brief Is the processor waiting for an interrupt (if true, instructions are not run until an interrupt is requested). + bool _isWaitingForInterrupt = false; + //! @brief The memory bus to use for read/write. std::shared_ptr _bus; //! @brief The cartridge header (stored for interrupt vectors.. @@ -258,6 +262,11 @@ namespace ComSquare::CPU //! @brief Return the data at the program bank concatenated with the program counter. It also increment the program counter (the program bank is not incremented on overflows). uint8_t readPC(); + //! @brief Check if an interrupt is requested and handle it. + void _checkInterrupts(); + //! @brief Run an interrupt (save state of the processor and jump to the interrupt handler) + void _runInterrupt(uint24_t nativeHandler, uint24_t emulationHandler); + //! @brief Execute a single instruction. //! @return The number of CPU cycles that the instruction took. virtual unsigned _executeInstruction(uint8_t opcode); @@ -443,6 +452,8 @@ namespace ComSquare::CPU int PEA(uint24_t, AddressingMode); //! @brief Stop the processor int STP(uint24_t, AddressingMode); + //! @brief Wait for Interrupt + int WAI(uint24_t, AddressingMode); //! @brief WDM Reserved for Future Expansion (used as a code breakpoint) int WDM(uint24_t, AddressingMode); //! @brief Block Move Next. This instruction is special: it takes parameter in the registers @@ -662,7 +673,7 @@ namespace ComSquare::CPU {&CPU::INY, 2, "iny", AddressingMode::Implied, 1}, // C8 {&CPU::CMP, 2, "cmp", AddressingMode::ImmediateForA, 2}, // C9 {&CPU::DEX, 2, "dex", AddressingMode::Implied, 1}, // CA - {&CPU::BRK, 7, "wai #-#", AddressingMode::Implied, 2}, // CB + {&CPU::WAI, 3, "wai", AddressingMode::Implied, 1}, // CB {&CPU::CPY, 4, "cpy", AddressingMode::Absolute, 3}, // CC {&CPU::CMP, 4, "cmp", AddressingMode::Absolute, 3}, // CD {&CPU::DEC, 6, "dec", AddressingMode::Absolute, 3}, // CE @@ -744,6 +755,13 @@ namespace ComSquare::CPU //! @brief Reset interrupt - Called on boot and when the reset button is pressed. virtual int RESB(); + //! @brief Is an NMI (non-maskable interrupt) requested. + bool IsNMIRequested = false; + //! @brief Is an interrupt (maskable) requested. + bool IsIRQRequested = false; + //! @brief Is an abort requested + bool IsAbortRequested = false; + //! @brief Return true if the CPU is overloaded with debugging features. virtual bool isDebugger(); diff --git a/sources/CPU/Instructions/Interrupts.cpp b/sources/CPU/Instructions/Interrupts.cpp index 8f8d515..f084ce0 100644 --- a/sources/CPU/Instructions/Interrupts.cpp +++ b/sources/CPU/Instructions/Interrupts.cpp @@ -22,7 +22,7 @@ namespace ComSquare::CPU return 0; } - int CPU::BRK(uint24_t, AddressingMode) + void CPU::_runInterrupt(uint24_t nativeHandler, uint24_t emulationHandler) { if (this->_isEmulationMode) { this->_push(this->_registers.pc); @@ -30,7 +30,7 @@ namespace ComSquare::CPU this->_registers.p.i = true; this->_registers.p.d = false; this->_registers.pbr = 0x0; - this->_registers.pc = this->_cartridgeHeader.emulationInterrupts.brk; + this->_registers.pc = emulationHandler; } else { this->_push(this->_registers.pbr); this->_push(this->_registers.pc); @@ -38,29 +38,23 @@ namespace ComSquare::CPU this->_registers.p.i = true; this->_registers.p.d = false; this->_registers.pbr = 0x0; - this->_registers.pc = this->_cartridgeHeader.nativeInterrupts.brk; + this->_registers.pc = nativeHandler; } + } + + int CPU::BRK(uint24_t, AddressingMode) + { + this->_runInterrupt( + this->_cartridgeHeader.nativeInterrupts.brk, + this->_cartridgeHeader.emulationInterrupts.brk); return !this->_isEmulationMode; } int CPU::COP(uint24_t, AddressingMode) { - if (this->_isEmulationMode) { - 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.emulationInterrupts.cop; - } else { - this->_push(this->_registers.pbr); - 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; - } + this->_runInterrupt( + this->_cartridgeHeader.nativeInterrupts.cop, + this->_cartridgeHeader.emulationInterrupts.cop); return !this->_isEmulationMode; } @@ -73,4 +67,10 @@ namespace ComSquare::CPU this->_registers.pbr = this->_pop16(); return !this->_isEmulationMode; } + + int CPU::WAI(uint24_t, AddressingMode) + { + this->_isWaitingForInterrupt = true; + return 0; + } } \ No newline at end of file