Emulator: Add interrupt handling
This commit is contained in:
+58
@@ -55,8 +55,66 @@ CPU::~CPU()
|
|||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void CPU::handleInterrupt(uint32_t interrupt_flag, uint8_t interrupt_source, uint8_t address)
|
||||||
|
{
|
||||||
|
// Clear interrupt
|
||||||
|
m_ime = 0;
|
||||||
|
Emu::the().writeMemory(0xff0f, interrupt_flag & (~interrupt_source));
|
||||||
|
|
||||||
|
// Call
|
||||||
|
|
||||||
|
m_wait_cycles += 20;
|
||||||
|
|
||||||
|
// Push address of the program counter on the stack, such that RET can pop it later
|
||||||
|
m_sp = (m_sp - 1) & 0xffff;
|
||||||
|
write(m_sp, m_pc >> 8); // msb(PC)
|
||||||
|
m_sp = (m_sp - 1) & 0xffff;
|
||||||
|
write(m_sp, m_pc & 0xff); // lsb(PC)
|
||||||
|
|
||||||
|
// Jump to address
|
||||||
|
m_pc = address;
|
||||||
|
}
|
||||||
|
|
||||||
void CPU::update()
|
void CPU::update()
|
||||||
{
|
{
|
||||||
|
// -------------------------------------
|
||||||
|
// Interrupt Service Routine
|
||||||
|
|
||||||
|
bool effective_ime = m_ime;
|
||||||
|
|
||||||
|
// IME only becomes active after the instruction following EI
|
||||||
|
if (m_should_enable_ime) {
|
||||||
|
m_ime = 1;
|
||||||
|
m_should_enable_ime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (effective_ime) {
|
||||||
|
// Get the 5 lower bits of the IE (interrupt enable) address
|
||||||
|
uint32_t interrupt_enabled = Emu::the().readMemory(0xffff) & 0x1f;
|
||||||
|
// Get the 5 lower bits of the IF (interrupt flag) address
|
||||||
|
uint32_t interrupt_flag = Emu::the().readMemory(0xff0f) & 0x1f;
|
||||||
|
|
||||||
|
uint32_t interrupt = interrupt_enabled & interrupt_flag;
|
||||||
|
if (interrupt & InterruptRequest::VBlank) {
|
||||||
|
handleInterrupt(interrupt_flag, InterruptRequest::VBlank, 0x40);
|
||||||
|
}
|
||||||
|
else if (interrupt & InterruptRequest::STAT) {
|
||||||
|
handleInterrupt(interrupt_flag, InterruptRequest::STAT, 0x48);
|
||||||
|
}
|
||||||
|
else if (interrupt & InterruptRequest::Timer) {
|
||||||
|
handleInterrupt(interrupt_flag, InterruptRequest::Timer, 0x50);
|
||||||
|
}
|
||||||
|
else if (interrupt & InterruptRequest::Serial) {
|
||||||
|
handleInterrupt(interrupt_flag, InterruptRequest::Serial, 0x58);
|
||||||
|
}
|
||||||
|
else if (interrupt & InterruptRequest::Joypad) {
|
||||||
|
handleInterrupt(interrupt_flag, InterruptRequest::Joypad, 0x60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------
|
||||||
|
// Run opcodes
|
||||||
|
|
||||||
m_wait_cycles--;
|
m_wait_cycles--;
|
||||||
// TODO: convert to early-return
|
// TODO: convert to early-return
|
||||||
if (m_wait_cycles <= 0) {
|
if (m_wait_cycles <= 0) {
|
||||||
|
|||||||
@@ -13,6 +13,19 @@
|
|||||||
|
|
||||||
#include "processing-unit.h"
|
#include "processing-unit.h"
|
||||||
#include "ruc/format/formatter.h"
|
#include "ruc/format/formatter.h"
|
||||||
|
#include "ruc/meta/core.h"
|
||||||
|
|
||||||
|
namespace InterruptRequest {
|
||||||
|
|
||||||
|
enum Enum : uint8_t {
|
||||||
|
VBlank = BIT(0),
|
||||||
|
STAT = BIT(1),
|
||||||
|
Timer = BIT(2),
|
||||||
|
Serial = BIT(3),
|
||||||
|
Joypad = BIT(4),
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace InterruptRequest
|
||||||
|
|
||||||
class CPU final : public ProcessingUnit {
|
class CPU final : public ProcessingUnit {
|
||||||
private:
|
private:
|
||||||
@@ -22,6 +35,7 @@ public:
|
|||||||
explicit CPU(uint32_t frequency);
|
explicit CPU(uint32_t frequency);
|
||||||
virtual ~CPU();
|
virtual ~CPU();
|
||||||
|
|
||||||
|
void handleInterrupt(uint32_t interrupt_flag, uint8_t interrupt_source, uint8_t address);
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
@@ -151,7 +165,9 @@ private:
|
|||||||
uint32_t m_nf { 0 }; // Subtraction flag (BCD)
|
uint32_t m_nf { 0 }; // Subtraction flag (BCD)
|
||||||
uint32_t m_hf { 0 }; // Half Carry flag (BCD)
|
uint32_t m_hf { 0 }; // Half Carry flag (BCD)
|
||||||
uint32_t m_cf { 0 }; // Carry flag
|
uint32_t m_cf { 0 }; // Carry flag
|
||||||
|
uint32_t m_ime { 0 }; // Interrupt Master Enable flag
|
||||||
|
|
||||||
|
bool m_should_enable_ime { 0 };
|
||||||
int8_t m_wait_cycles { 0 };
|
int8_t m_wait_cycles { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user