203 lines
4.7 KiB
C++
203 lines
4.7 KiB
C++
![]() |
#include <gameboy/gameboy.hpp>
|
||
|
|
||
|
#define CPU_CPP
|
||
|
namespace GameBoy {
|
||
|
|
||
|
#include "core/core.cpp"
|
||
|
#include "mmio/mmio.cpp"
|
||
|
#include "timing/timing.cpp"
|
||
|
#include "serialization.cpp"
|
||
|
CPU cpu;
|
||
|
|
||
|
void CPU::Main() {
|
||
|
cpu.main();
|
||
|
}
|
||
|
|
||
|
void CPU::main() {
|
||
|
while(true) {
|
||
|
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
|
||
|
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||
|
}
|
||
|
|
||
|
if(trace) print(disassemble(r[PC]), "\n");
|
||
|
interrupt_test();
|
||
|
uint8 opcode = op_read(r[PC]++);
|
||
|
(this->*opcode_table[opcode])();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CPU::interrupt_raise(CPU::Interrupt id) {
|
||
|
if(id == Interrupt::Vblank) {
|
||
|
status.interrupt_request_vblank = 1;
|
||
|
if(status.interrupt_enable_vblank) status.halt = false;
|
||
|
}
|
||
|
|
||
|
if(id == Interrupt::Stat) {
|
||
|
status.interrupt_request_stat = 1;
|
||
|
if(status.interrupt_enable_stat) status.halt = false;
|
||
|
}
|
||
|
|
||
|
if(id == Interrupt::Timer) {
|
||
|
status.interrupt_request_timer = 1;
|
||
|
if(status.interrupt_enable_timer) status.halt = false;
|
||
|
}
|
||
|
|
||
|
if(id == Interrupt::Serial) {
|
||
|
status.interrupt_request_serial = 1;
|
||
|
if(status.interrupt_enable_serial) status.halt = false;
|
||
|
}
|
||
|
|
||
|
if(id == Interrupt::Joypad) {
|
||
|
status.interrupt_request_joypad = 1;
|
||
|
if(status.interrupt_enable_joypad) status.halt = status.stop = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CPU::interrupt_test() {
|
||
|
if(status.ime) {
|
||
|
if(status.interrupt_request_vblank && status.interrupt_enable_vblank) {
|
||
|
status.interrupt_request_vblank = 0;
|
||
|
return interrupt_exec(0x0040);
|
||
|
}
|
||
|
|
||
|
if(status.interrupt_request_stat && status.interrupt_enable_stat) {
|
||
|
status.interrupt_request_stat = 0;
|
||
|
return interrupt_exec(0x0048);
|
||
|
}
|
||
|
|
||
|
if(status.interrupt_request_timer && status.interrupt_enable_timer) {
|
||
|
status.interrupt_request_timer = 0;
|
||
|
return interrupt_exec(0x0050);
|
||
|
}
|
||
|
|
||
|
if(status.interrupt_request_serial && status.interrupt_enable_serial) {
|
||
|
status.interrupt_request_serial = 0;
|
||
|
return interrupt_exec(0x0058);
|
||
|
}
|
||
|
|
||
|
if(status.interrupt_request_joypad && status.interrupt_enable_joypad) {
|
||
|
status.interrupt_request_joypad = 0;
|
||
|
return interrupt_exec(0x0060);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CPU::interrupt_exec(uint16 pc) {
|
||
|
status.ime = 0;
|
||
|
op_write(--r[SP], r[PC] >> 8);
|
||
|
op_write(--r[SP], r[PC] >> 0);
|
||
|
r[PC] = pc;
|
||
|
op_io();
|
||
|
op_io();
|
||
|
op_io();
|
||
|
}
|
||
|
|
||
|
void CPU::power() {
|
||
|
create(Main, 4 * 1024 * 1024);
|
||
|
|
||
|
for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM
|
||
|
for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror)
|
||
|
for(unsigned n = 0xff80; n <= 0xfffe; n++) bus.mmio[n] = this; //HRAM
|
||
|
|
||
|
bus.mmio[0xff00] = this; //JOYP
|
||
|
bus.mmio[0xff01] = this; //SB
|
||
|
bus.mmio[0xff02] = this; //SC
|
||
|
bus.mmio[0xff04] = this; //DIV
|
||
|
bus.mmio[0xff05] = this; //TIMA
|
||
|
bus.mmio[0xff06] = this; //TMA
|
||
|
bus.mmio[0xff07] = this; //TAC
|
||
|
bus.mmio[0xff0f] = this; //IF
|
||
|
bus.mmio[0xff46] = this; //DMA
|
||
|
bus.mmio[0xffff] = this; //IE
|
||
|
|
||
|
if(system.cgb()) {
|
||
|
bus.mmio[0xff4d] = this; //KEY1
|
||
|
bus.mmio[0xff51] = this; //HDMA1
|
||
|
bus.mmio[0xff52] = this; //HDMA2
|
||
|
bus.mmio[0xff53] = this; //HDMA3
|
||
|
bus.mmio[0xff54] = this; //HDMA4
|
||
|
bus.mmio[0xff55] = this; //HDMA5
|
||
|
bus.mmio[0xff56] = this; //RP
|
||
|
bus.mmio[0xff6c] = this; //???
|
||
|
bus.mmio[0xff70] = this; //SVBK
|
||
|
bus.mmio[0xff72] = this; //???
|
||
|
bus.mmio[0xff73] = this; //???
|
||
|
bus.mmio[0xff74] = this; //???
|
||
|
bus.mmio[0xff75] = this; //???
|
||
|
bus.mmio[0xff76] = this; //???
|
||
|
bus.mmio[0xff77] = this; //???
|
||
|
}
|
||
|
|
||
|
for(auto &n : wram) n = 0x00;
|
||
|
for(auto &n : hram) n = 0x00;
|
||
|
|
||
|
r[PC] = 0x0000;
|
||
|
r[SP] = 0x0000;
|
||
|
r[AF] = 0x0000;
|
||
|
r[BC] = 0x0000;
|
||
|
r[DE] = 0x0000;
|
||
|
r[HL] = 0x0000;
|
||
|
|
||
|
status.clock = 0;
|
||
|
status.halt = false;
|
||
|
status.stop = false;
|
||
|
status.ei = false;
|
||
|
status.ime = 0;
|
||
|
|
||
|
status.p15 = 0;
|
||
|
status.p14 = 0;
|
||
|
status.joyp = 0;
|
||
|
status.mlt_req = 0;
|
||
|
|
||
|
status.serial_data = 0;
|
||
|
status.serial_bits = 0;
|
||
|
|
||
|
status.serial_transfer = 0;
|
||
|
status.serial_clock = 0;
|
||
|
|
||
|
status.div = 0;
|
||
|
|
||
|
status.tima = 0;
|
||
|
|
||
|
status.tma = 0;
|
||
|
|
||
|
status.timer_enable = 0;
|
||
|
status.timer_clock = 0;
|
||
|
|
||
|
status.interrupt_request_joypad = 0;
|
||
|
status.interrupt_request_serial = 0;
|
||
|
status.interrupt_request_timer = 0;
|
||
|
status.interrupt_request_stat = 0;
|
||
|
status.interrupt_request_vblank = 0;
|
||
|
|
||
|
status.speed_double = 0;
|
||
|
status.speed_switch = 0;
|
||
|
|
||
|
status.dma_source = 0;
|
||
|
status.dma_target = 0;
|
||
|
|
||
|
status.dma_mode = 0;
|
||
|
status.dma_length = 0;
|
||
|
|
||
|
status.ff6c = 0;
|
||
|
status.ff72 = 0;
|
||
|
status.ff73 = 0;
|
||
|
status.ff74 = 0;
|
||
|
status.ff75 = 0;
|
||
|
|
||
|
status.wram_bank = 1;
|
||
|
|
||
|
status.interrupt_enable_joypad = 0;
|
||
|
status.interrupt_enable_serial = 0;
|
||
|
status.interrupt_enable_timer = 0;
|
||
|
status.interrupt_enable_stat = 0;
|
||
|
status.interrupt_enable_vblank = 0;
|
||
|
}
|
||
|
|
||
|
CPU::CPU() : trace(false) {
|
||
|
initialize_opcode_table();
|
||
|
}
|
||
|
|
||
|
}
|