Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd2673909c | ||
|
|
5b4dbdc9a4 | ||
|
|
5b08c18127 | ||
|
|
857865dfb9 |
+76
-6
@@ -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()
|
||||
{
|
||||
// -------------------------------------
|
||||
// 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--;
|
||||
// TODO: convert to early-return
|
||||
if (m_wait_cycles <= 0) {
|
||||
@@ -69,8 +127,6 @@ void CPU::update()
|
||||
switch (opcode) {
|
||||
|
||||
case 0x10: /* TODO */ m_pc += 2; break;
|
||||
case 0xf3: /* TODO */ m_pc += 1; break;
|
||||
case 0xfb: /* TODO */ m_pc += 1; break;
|
||||
|
||||
case 0x00: nop(); break;
|
||||
case 0x01: ldi16(); break;
|
||||
@@ -305,12 +361,14 @@ void CPU::update()
|
||||
case 0xf0: ldff8(); break;
|
||||
case 0xf1: pop(); break;
|
||||
case 0xf2: ldff8(); break;
|
||||
case 0xf3: misc(); break;
|
||||
case 0xf5: push(); break;
|
||||
case 0xf6: or8(); break;
|
||||
case 0xf7: rst(); break;
|
||||
case 0xf8: ldr16(); break;
|
||||
case 0xf9: ldr16(); break;
|
||||
case 0xfa: lda8(); break;
|
||||
case 0xfb: misc(); break;
|
||||
case 0xfe: cp(); break;
|
||||
case 0xff: rst(); break;
|
||||
|
||||
@@ -548,6 +606,8 @@ void CPU::daa()
|
||||
if (!m_nf) {
|
||||
if (m_cf || m_a > 0x99) {
|
||||
m_a += 0x60;
|
||||
// Carry flag
|
||||
m_cf = 1;
|
||||
}
|
||||
|
||||
if (m_hf || (m_a & 0xf) > 0x9) {
|
||||
@@ -564,9 +624,6 @@ void CPU::daa()
|
||||
}
|
||||
}
|
||||
|
||||
// Carry flag
|
||||
m_cf = (m_a & 0x100) == 0x100;
|
||||
|
||||
m_a = m_a & 0xff;
|
||||
|
||||
// Set flags
|
||||
@@ -1544,8 +1601,8 @@ void CPU::ret()
|
||||
case 0xd8: /* RET C,i16 */ function_return(m_cf); break;
|
||||
case 0xd9: /* RETI */ {
|
||||
// Return from subroutine
|
||||
// TODO: and enable interrupts.
|
||||
function_return(true);
|
||||
m_ime = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -1612,6 +1669,19 @@ void CPU::misc()
|
||||
// Invert carry
|
||||
m_cf = (m_cf) ? 0 : 1;
|
||||
break;
|
||||
case 0xf3: // DI
|
||||
m_wait_cycles += 4;
|
||||
|
||||
// Disable Interrupts by clearing the IME flag
|
||||
m_ime = 0;
|
||||
break;
|
||||
case 0xfb: // EI
|
||||
m_wait_cycles += 4;
|
||||
|
||||
// Enable Interrupts by setting the IME flag.
|
||||
// The flag is only set after the instruction following EI
|
||||
m_should_enable_ime = true;
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,19 @@
|
||||
|
||||
#include "processing-unit.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 {
|
||||
private:
|
||||
@@ -22,6 +35,7 @@ public:
|
||||
explicit CPU(uint32_t frequency);
|
||||
virtual ~CPU();
|
||||
|
||||
void handleInterrupt(uint32_t interrupt_flag, uint8_t interrupt_source, uint8_t address);
|
||||
void update() override;
|
||||
|
||||
// -------------------------------------
|
||||
@@ -147,11 +161,13 @@ private:
|
||||
uint32_t m_sp { 0 }; // Stack Pointer
|
||||
|
||||
// Flags
|
||||
uint32_t m_zf { 0 }; // Zero flag
|
||||
uint32_t m_nf { 0 }; // Subtraction flag (BCD)
|
||||
uint32_t m_hf { 0 }; // Half Carry flag (BCD)
|
||||
uint32_t m_cf { 0 }; // Carry flag
|
||||
uint32_t m_zf { 0 }; // Zero flag
|
||||
uint32_t m_nf { 0 }; // Subtraction flag (BCD)
|
||||
uint32_t m_hf { 0 }; // Half Carry flag (BCD)
|
||||
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 };
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
* Copyright (C) 2022 Th3FrankXD
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user