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