mirror of
https://github.com/zoriya/ComSquare.git
synced 2026-05-29 17:02:21 +00:00
Adding the WAI and handling NMI & IRQ interrupts
This commit is contained in:
+31
-4
@@ -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) {
|
||||
|
||||
+21
-3
@@ -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<Memory::MemoryBus> _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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user