From 1c3c7fe0a7b2dacc7db0c7bfd1d950c781a2e9e5 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Wed, 29 Dec 2010 22:03:42 +1100 Subject: [PATCH] Update to release v000r02. byuu says: 314 of 512 opcodes implemented, can execute the first 67,450 instructions of Tetris. I also added an MMIO bus, ala bsnes, so that I can map and access individual registers with a single indirection. --- gameboy/Makefile | 5 +- gameboy/cartridge/cartridge.cpp | 34 +- gameboy/cartridge/cartridge.hpp | 16 +- gameboy/cartridge/mmio/mmio.cpp | 10 + gameboy/cartridge/mmio/mmio.hpp | 2 + gameboy/cpu/core/core.cpp | 518 +++++++++++++++++++++++++++++- gameboy/cpu/core/core.hpp | 42 ++- gameboy/cpu/core/disassembler.cpp | 338 ++++++++++++++++++- gameboy/cpu/core/registers.hpp | 1 + gameboy/cpu/cpu.cpp | 7 +- gameboy/cpu/cpu.hpp | 8 +- gameboy/cpu/mmio/mmio.cpp | 15 + gameboy/cpu/mmio/mmio.hpp | 2 + gameboy/cpu/timing/timing.cpp | 6 +- gameboy/gameboy.hpp | 5 +- gameboy/lcd/lcd.cpp | 21 ++ gameboy/lcd/lcd.hpp | 15 + gameboy/lcd/mmio/mmio.cpp | 26 ++ gameboy/lcd/mmio/mmio.hpp | 2 + gameboy/memory/memory.cpp | 98 +----- gameboy/memory/memory.hpp | 22 +- gameboy/system/system.cpp | 6 + 22 files changed, 1072 insertions(+), 127 deletions(-) create mode 100755 gameboy/cartridge/mmio/mmio.cpp create mode 100755 gameboy/cartridge/mmio/mmio.hpp create mode 100755 gameboy/cpu/mmio/mmio.cpp create mode 100755 gameboy/cpu/mmio/mmio.hpp create mode 100755 gameboy/lcd/lcd.cpp create mode 100755 gameboy/lcd/lcd.hpp create mode 100755 gameboy/lcd/mmio/mmio.cpp create mode 100755 gameboy/lcd/mmio/mmio.hpp diff --git a/gameboy/Makefile b/gameboy/Makefile index ecb87ae3..b50d3ad4 100755 --- a/gameboy/Makefile +++ b/gameboy/Makefile @@ -1,7 +1,7 @@ gameboy_objects := libco gameboy_objects += gameboy-system gameboy-scheduler -gameboy_objects += gameboy-cartridge gameboy-memory -gameboy_objects += gameboy-cpu +gameboy_objects += gameboy-memory gameboy-cartridge +gameboy_objects += gameboy-cpu gameboy-lcd objects += $(gameboy_objects) obj/libco.o: libco/libco.c libco/* @@ -11,3 +11,4 @@ obj/gameboy-scheduler.o: $(gameboy)/scheduler/scheduler.cpp $(call rwildcard,$(g obj/gameboy-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/) obj/gameboy-memory.o: $(gameboy)/memory/memory.cpp $(call rwildcard,$(gameboy)/memory/) obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/) +obj/gameboy-lcd.o: $(gameboy)/lcd/lcd.cpp $(call rwildcard,$(gameboy)/lcd/) diff --git a/gameboy/cartridge/cartridge.cpp b/gameboy/cartridge/cartridge.cpp index b900cbbc..611b35f5 100755 --- a/gameboy/cartridge/cartridge.cpp +++ b/gameboy/cartridge/cartridge.cpp @@ -3,22 +3,24 @@ #define CARTRIDGE_CPP namespace GameBoy { +#include "mmio/mmio.cpp" Cartridge cartridge; void Cartridge::load(uint8_t *data, unsigned size) { - bus.cartrom.copy(data, size); + romdata = new uint8[romsize = size]; + memcpy(romdata, data, size); char name[17]; - memcpy(name, bus.cartrom.data + 0x0134, 16); + memcpy(name, romdata + 0x0134, 16); name[16] = 0; info.name = name; info.name.rtrim(); - info.cgbflag = bus.cartrom[0x0143]; - info.sgbflag = bus.cartrom[0x0146]; - info.type = bus.cartrom[0x0147]; + info.cgbflag = romdata[0x0143]; + info.sgbflag = romdata[0x0146]; + info.type = romdata[0x0147]; - switch(bus.cartrom[0x0148]) { default: + switch(romdata[0x0148]) { default: case 0x00: info.romsize = 2 * 16 * 1024; break; case 0x01: info.romsize = 4 * 16 * 1024; break; case 0x02: info.romsize = 8 * 16 * 1024; break; @@ -33,7 +35,7 @@ void Cartridge::load(uint8_t *data, unsigned size) { } //TODO: MBC2 always stores 0x00 here; yet it has 512x4-bits RAM - switch(bus.cartrom[0x0149]) { default: + switch(romdata[0x0149]) { default: case 0x00: info.ramsize = 0 * 1024; break; case 0x01: info.ramsize = 2 * 1024; break; case 0x02: info.ramsize = 8 * 1024; break; @@ -46,12 +48,28 @@ void Cartridge::load(uint8_t *data, unsigned size) { void Cartridge::unload() { if(loaded == false) return; - bus.cartrom.free(); + if(romdata) { delete[] romdata; romdata = 0; } + if(ramdata) { delete[] ramdata; ramdata = 0; } loaded = false; } +void Cartridge::power() { + for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this; + + reset(); +} + +void Cartridge::reset() { +} + Cartridge::Cartridge() { loaded = false; + romdata = 0; + ramdata = 0; +} + +Cartridge::~Cartridge() { + unload(); } } diff --git a/gameboy/cartridge/cartridge.hpp b/gameboy/cartridge/cartridge.hpp index a19b3b7d..4868fc56 100755 --- a/gameboy/cartridge/cartridge.hpp +++ b/gameboy/cartridge/cartridge.hpp @@ -1,5 +1,6 @@ -class Cartridge : property { -public: +struct Cartridge : MMIO, property { + #include "mmio/mmio.hpp" + struct Information { string name; uint8 cgbflag; @@ -11,9 +12,20 @@ public: readonly loaded; + uint8_t *romdata; + unsigned romsize; + + uint8_t *ramdata; + unsigned ramsize; + void load(uint8_t *data, unsigned size); void unload(); + + void power(); + void reset(); + Cartridge(); + ~Cartridge(); }; extern Cartridge cartridge; diff --git a/gameboy/cartridge/mmio/mmio.cpp b/gameboy/cartridge/mmio/mmio.cpp new file mode 100755 index 00000000..749b5895 --- /dev/null +++ b/gameboy/cartridge/mmio/mmio.cpp @@ -0,0 +1,10 @@ +#ifdef CARTRIDGE_CPP + +uint8 Cartridge::mmio_read(uint16 addr) { + if(addr >= 0x0000 && addr <= 0x7fff) return romdata[addr]; +} + +void Cartridge::mmio_write(uint16 addr, uint8 data) { +} + +#endif diff --git a/gameboy/cartridge/mmio/mmio.hpp b/gameboy/cartridge/mmio/mmio.hpp new file mode 100755 index 00000000..92b8f614 --- /dev/null +++ b/gameboy/cartridge/mmio/mmio.hpp @@ -0,0 +1,2 @@ +uint8 mmio_read(uint16 addr); +void mmio_write(uint16 addr, uint8 data); diff --git a/gameboy/cpu/core/core.cpp b/gameboy/cpu/core/core.cpp index d81d9f71..296c3391 100755 --- a/gameboy/cpu/core/core.cpp +++ b/gameboy/cpu/core/core.cpp @@ -10,17 +10,57 @@ void CPU::op_unknown() { "CPU: unknown opcode [", hex<2>(opcode), "]\n", "af:", hex<4>(r[AF]), " bc:", hex<4>(r[BC]), " de:", hex<4>(r[DE]), " hl:", hex<4>(r[HL]), " ", "sp:", hex<4>(r[SP]), " pc:", hex<4>(r[PC]), "\n", - "ly:", decimal<3, ' '>(status.lycounter), " exec:", opcode_counter, "\n" + "ly:", decimal<3, ' '>(lcd.status.ly), " exec:", opcode_counter, "\n" ); while(true) scheduler.exit(); } +void CPU::op_cb() { + uint8 opcode = op_read(r[PC]++); + (this->*opcode_table_cb[opcode])(); +} + //8-bit load commands +template void CPU::op_ld_r_r() { + r[x] = r[y]; +} + template void CPU::op_ld_r_n() { r[x] = op_read(r[PC]++); } +template void CPU::op_ld_r_hl() { + r[x] = op_read(r[HL]); +} + +void CPU::op_ld_hl_n() { + op_write(r[HL], op_read(r[PC]++)); +} + +void CPU::op_ld_nn_a() { + uint8 lo = op_read(r[PC]++); + uint8 hi = op_read(r[PC]++); + op_write((hi << 8) | (lo << 0), r[A]); +} + +void CPU::op_ld_a_ffn() { + r[A] = op_read(0xff00 + op_read(r[PC]++)); +} + +void CPU::op_ld_ffn_a() { + op_write(0xff00 + op_read(r[PC]++), r[A]); +} + +void CPU::op_ld_ffc_a() { + op_write(0xff00 + r[C], r[A]); +} + +void CPU::op_ldi_a_hl() { + r[A] = op_read(r[HL]); + r[HL]++; +} + void CPU::op_ldd_hl_a() { op_write(r[HL], r[A]); r[HL]--; @@ -28,13 +68,50 @@ void CPU::op_ldd_hl_a() { //16-bit load commands -template void CPU::op_ld_rr_nn() { - r[y] = op_read(r[PC]++); - r[x] = op_read(r[PC]++); +template void CPU::op_ld_rr_nn() { + r[x] = op_read(r[PC]++) << 0; + r[x] |= op_read(r[PC]++) << 8; +} + +template void CPU::op_push_rr() { + op_write(--r[SP], r[x] >> 8); + op_write(--r[SP], r[x] >> 0); + op_io(); +} + +template void CPU::op_pop_rr() { + r[x] = op_read(r[SP]++) << 0; + r[x] |= op_read(r[SP]++) << 8; } //8-bit arithmetic commands +template void CPU::op_add_a_r() { + uint16 rb = (r[A] + r[x]); + uint16 rn = (r[A] & 0x0f) + (r[x] & 0x0f); + r[A] = rb; + r.f.z = (uint8)rb == 0; + r.f.n = 0; + r.f.h = rn > 0x0f; + r.f.c = rb > 0xff; +} + +template void CPU::op_and_r() { + r[A] &= r[x]; + r.f.z = r[A] == 0; + r.f.n = 0; + r.f.h = 1; + r.f.c = 0; +} + +void CPU::op_and_n() { + r[A] &= op_read(r[PC]++); + r.f.z = r[A] == 0; + r.f.n = 0; + r.f.h = 1; + r.f.c = 0; +} + template void CPU::op_xor_r() { r[A] ^= r[x]; r.f.z = r[A] == 0; @@ -43,11 +120,87 @@ template void CPU::op_xor_r() { r.f.c = 0; } +template void CPU::op_or_r() { + r[A] |= r[x]; + r.f.z = r[A] == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = 0; +} + +void CPU::op_cp_n() { + uint8 data = op_read(r[PC]++); + uint16 rb = (r[A] - data); + uint16 rn = (r[A] & 0x0f) - (data & 0x0f); + r.f.z = (uint8)rb == 0; + r.f.n = 1; + r.f.h = rn > 0x0f; + r.f.c = rb > 0xff; +} + +template void CPU::op_inc_r() { + r[x]++; + r.f.z = r[x] == 0; + r.f.n = 0; + r.f.h = (r[x] & 0x0f) == 0x00; +} + template void CPU::op_dec_r() { r[x]--; r.f.z = r[x] == 0; - r.f.n = 0; //??? - r.f.h = 0; //??? + r.f.n = 1; + r.f.h = (r[x] & 0x0f) == 0x0f; +} + +void CPU::op_cpl() { + r[A] ^= 0xff; + r.f.n = 1; + r.f.h = 1; +} + +//16-bit arithmetic commands + +template void CPU::op_add_hl_rr() { + uint32 rb = (r[HL] + r[x]); + uint32 rn = (r[HL] & 0xfff) + (r[x] & 0xfff); + r[HL] = rb; + r.f.n = 0; + r.f.h = rn > 0x0fff; + r.f.c = rb > 0xffff; +} + +template void CPU::op_inc_rr() { + r[x]++; +} + +template void CPU::op_dec_rr() { + r[x]--; +} + +//rotate/shift commands + +template void CPU::op_swap_r() { + r[x] = (r[x] << 4) | (r[x] >> 4); + r.f.z = r[x] == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = 0; +} + +//single-bit commands + +template void CPU::op_bit_n_r() { + r.f.z = (r[x] & (1 << b)) == 0; + r.f.n = 0; + r.f.h = 1; +} + +template void CPU::op_set_n_r() { + r[x] |= 1 << b; +} + +template void CPU::op_res_n_r() { + r[x] &= ~(1 << b); } //control commands @@ -55,6 +208,14 @@ template void CPU::op_dec_r() { void CPU::op_nop() { } +void CPU::op_di() { + status.ime = 0; +} + +void CPU::op_ei() { + status.ime = 1; +} + //jump commands void CPU::op_jp_nn() { @@ -64,6 +225,10 @@ void CPU::op_jp_nn() { op_io(); } +void CPU::op_jp_hl() { + r[PC] = r[HL]; +} + template void CPU::op_jr_f_n() { int8 n = op_read(r[PC]++); if(r.f[x] == y) { @@ -72,20 +237,359 @@ template void CPU::op_jr_f_n() { } } +void CPU::op_call_nn() { + uint16 dest = r[PC] + 2; + op_write(--r[SP], dest >> 8); + op_write(--r[SP], dest >> 0); + uint8 lo = op_read(r[PC]++); + uint8 hi = op_read(r[PC]++); + r[PC] = (hi << 8) | (lo << 0); + op_io(); +} + +void CPU::op_ret() { + uint8 lo = op_read(r[SP]++); + uint8 hi = op_read(r[SP]++); + r[PC] = (hi << 8) | (lo << 0); + op_io(); +} + +template void CPU::op_rst_n() { + op_write(--r[SP], r[PC] >> 8); + op_write(--r[SP], r[PC] >> 0); + r[PC] = n; + op_io(); +} + +//opcode tables + void CPU::initialize_opcode_table() { for(unsigned n = 0; n < 256; n++) opcode_table[n] = &CPU::op_unknown; + for(unsigned n = 0; n < 256; n++) opcode_table_cb[n] = &CPU::op_unknown; opcode_table[0x00] = &CPU::op_nop; + opcode_table[0x01] = &CPU::op_ld_rr_nn; + opcode_table[0x03] = &CPU::op_inc_rr; opcode_table[0x05] = &CPU::op_dec_r; opcode_table[0x06] = &CPU::op_ld_r_n; + opcode_table[0x09] = &CPU::op_add_hl_rr; + opcode_table[0x0b] = &CPU::op_dec_rr; + opcode_table[0x0c] = &CPU::op_inc_r; opcode_table[0x0d] = &CPU::op_dec_r; opcode_table[0x0e] = &CPU::op_ld_r_n; + opcode_table[0x11] = &CPU::op_ld_rr_nn; + opcode_table[0x13] = &CPU::op_inc_rr; + opcode_table[0x16] = &CPU::op_ld_r_n; + opcode_table[0x19] = &CPU::op_add_hl_rr; + opcode_table[0x1b] = &CPU::op_dec_rr; + opcode_table[0x1e] = &CPU::op_ld_r_n; opcode_table[0x20] = &CPU::op_jr_f_n; - opcode_table[0x21] = &CPU::op_ld_rr_nn; + opcode_table[0x21] = &CPU::op_ld_rr_nn; + opcode_table[0x23] = &CPU::op_inc_rr; + opcode_table[0x26] = &CPU::op_ld_r_n; + opcode_table[0x29] = &CPU::op_add_hl_rr; + opcode_table[0x2a] = &CPU::op_ldi_a_hl; + opcode_table[0x2b] = &CPU::op_dec_rr; + opcode_table[0x2e] = &CPU::op_ld_r_n; + opcode_table[0x2f] = &CPU::op_cpl; + opcode_table[0x31] = &CPU::op_ld_rr_nn; opcode_table[0x32] = &CPU::op_ldd_hl_a; + opcode_table[0x33] = &CPU::op_inc_rr; + opcode_table[0x36] = &CPU::op_ld_hl_n; + opcode_table[0x39] = &CPU::op_add_hl_rr; + opcode_table[0x3b] = &CPU::op_dec_rr; opcode_table[0x3e] = &CPU::op_ld_r_n; + opcode_table[0x40] = &CPU::op_ld_r_r; + opcode_table[0x41] = &CPU::op_ld_r_r; + opcode_table[0x42] = &CPU::op_ld_r_r; + opcode_table[0x43] = &CPU::op_ld_r_r; + opcode_table[0x44] = &CPU::op_ld_r_r; + opcode_table[0x45] = &CPU::op_ld_r_r; + opcode_table[0x46] = &CPU::op_ld_r_hl; + opcode_table[0x47] = &CPU::op_ld_r_r; + opcode_table[0x48] = &CPU::op_ld_r_r; + opcode_table[0x49] = &CPU::op_ld_r_r; + opcode_table[0x4a] = &CPU::op_ld_r_r; + opcode_table[0x4b] = &CPU::op_ld_r_r; + opcode_table[0x4c] = &CPU::op_ld_r_r; + opcode_table[0x4d] = &CPU::op_ld_r_r; + opcode_table[0x4e] = &CPU::op_ld_r_hl; + opcode_table[0x4f] = &CPU::op_ld_r_r; + opcode_table[0x50] = &CPU::op_ld_r_r; + opcode_table[0x51] = &CPU::op_ld_r_r; + opcode_table[0x52] = &CPU::op_ld_r_r; + opcode_table[0x53] = &CPU::op_ld_r_r; + opcode_table[0x54] = &CPU::op_ld_r_r; + opcode_table[0x55] = &CPU::op_ld_r_r; + opcode_table[0x56] = &CPU::op_ld_r_hl; + opcode_table[0x57] = &CPU::op_ld_r_r; + opcode_table[0x58] = &CPU::op_ld_r_r; + opcode_table[0x59] = &CPU::op_ld_r_r; + opcode_table[0x5a] = &CPU::op_ld_r_r; + opcode_table[0x5b] = &CPU::op_ld_r_r; + opcode_table[0x5c] = &CPU::op_ld_r_r; + opcode_table[0x5d] = &CPU::op_ld_r_r; + opcode_table[0x5e] = &CPU::op_ld_r_hl; + opcode_table[0x5f] = &CPU::op_ld_r_r; + opcode_table[0x60] = &CPU::op_ld_r_r; + opcode_table[0x61] = &CPU::op_ld_r_r; + opcode_table[0x62] = &CPU::op_ld_r_r; + opcode_table[0x63] = &CPU::op_ld_r_r; + opcode_table[0x64] = &CPU::op_ld_r_r; + opcode_table[0x65] = &CPU::op_ld_r_r; + opcode_table[0x66] = &CPU::op_ld_r_hl; + opcode_table[0x67] = &CPU::op_ld_r_r; + opcode_table[0x68] = &CPU::op_ld_r_r; + opcode_table[0x69] = &CPU::op_ld_r_r; + opcode_table[0x6a] = &CPU::op_ld_r_r; + opcode_table[0x6b] = &CPU::op_ld_r_r; + opcode_table[0x6c] = &CPU::op_ld_r_r; + opcode_table[0x6d] = &CPU::op_ld_r_r; + opcode_table[0x6e] = &CPU::op_ld_r_hl; + opcode_table[0x6f] = &CPU::op_ld_r_r; + opcode_table[0x78] = &CPU::op_ld_r_r; + opcode_table[0x79] = &CPU::op_ld_r_r; + opcode_table[0x7a] = &CPU::op_ld_r_r; + opcode_table[0x7b] = &CPU::op_ld_r_r; + opcode_table[0x7c] = &CPU::op_ld_r_r; + opcode_table[0x7d] = &CPU::op_ld_r_r; + opcode_table[0x7e] = &CPU::op_ld_r_hl; + opcode_table[0x7f] = &CPU::op_ld_r_r; + opcode_table[0x80] = &CPU::op_add_a_r; + opcode_table[0x81] = &CPU::op_add_a_r; + opcode_table[0x82] = &CPU::op_add_a_r; + opcode_table[0x83] = &CPU::op_add_a_r; + opcode_table[0x84] = &CPU::op_add_a_r; + opcode_table[0x85] = &CPU::op_add_a_r; + opcode_table[0x87] = &CPU::op_add_a_r; + opcode_table[0xa0] = &CPU::op_and_r; + opcode_table[0xa1] = &CPU::op_and_r; + opcode_table[0xa2] = &CPU::op_and_r; + opcode_table[0xa3] = &CPU::op_and_r; + opcode_table[0xa4] = &CPU::op_and_r; + opcode_table[0xa5] = &CPU::op_and_r; + opcode_table[0xa7] = &CPU::op_and_r; + opcode_table[0xa8] = &CPU::op_xor_r; + opcode_table[0xa9] = &CPU::op_xor_r; + opcode_table[0xaa] = &CPU::op_xor_r; + opcode_table[0xab] = &CPU::op_xor_r; + opcode_table[0xac] = &CPU::op_xor_r; + opcode_table[0xad] = &CPU::op_xor_r; opcode_table[0xaf] = &CPU::op_xor_r; + opcode_table[0xb0] = &CPU::op_or_r; + opcode_table[0xb1] = &CPU::op_or_r; + opcode_table[0xb2] = &CPU::op_or_r; + opcode_table[0xb3] = &CPU::op_or_r; + opcode_table[0xb4] = &CPU::op_or_r; + opcode_table[0xb5] = &CPU::op_or_r; + opcode_table[0xb7] = &CPU::op_or_r; + opcode_table[0xc1] = &CPU::op_pop_rr; opcode_table[0xc3] = &CPU::op_jp_nn; + opcode_table[0xc5] = &CPU::op_push_rr; + opcode_table[0xc7] = &CPU::op_rst_n<0x00>; + opcode_table[0xc9] = &CPU::op_ret; + opcode_table[0xcb] = &CPU::op_cb; + opcode_table[0xcd] = &CPU::op_call_nn; + opcode_table[0xcf] = &CPU::op_rst_n<0x08>; + opcode_table[0xd1] = &CPU::op_pop_rr; + opcode_table[0xd5] = &CPU::op_push_rr; + opcode_table[0xd7] = &CPU::op_rst_n<0x10>; + opcode_table[0xdf] = &CPU::op_rst_n<0x18>; + opcode_table[0xe0] = &CPU::op_ld_ffn_a; + opcode_table[0xe1] = &CPU::op_pop_rr; + opcode_table[0xe2] = &CPU::op_ld_ffc_a; + opcode_table[0xe5] = &CPU::op_push_rr; + opcode_table[0xe6] = &CPU::op_and_n; + opcode_table[0xe7] = &CPU::op_rst_n<0x20>; + opcode_table[0xe9] = &CPU::op_jp_hl; + opcode_table[0xea] = &CPU::op_ld_nn_a; + opcode_table[0xef] = &CPU::op_rst_n<0x28>; + opcode_table[0xf0] = &CPU::op_ld_a_ffn; + opcode_table[0xf1] = &CPU::op_pop_rr; + opcode_table[0xf3] = &CPU::op_di; + opcode_table[0xf5] = &CPU::op_push_rr; + opcode_table[0xf7] = &CPU::op_rst_n<0x30>; + opcode_table[0xfb] = &CPU::op_ei; + opcode_table[0xfe] = &CPU::op_cp_n; + opcode_table[0xff] = &CPU::op_rst_n<0x38>; + + opcode_table_cb[0x37] = &CPU::op_swap_r; + opcode_table_cb[0x40] = &CPU::op_bit_n_r<0, B>; + opcode_table_cb[0x41] = &CPU::op_bit_n_r<0, C>; + opcode_table_cb[0x42] = &CPU::op_bit_n_r<0, D>; + opcode_table_cb[0x43] = &CPU::op_bit_n_r<0, E>; + opcode_table_cb[0x44] = &CPU::op_bit_n_r<0, H>; + opcode_table_cb[0x45] = &CPU::op_bit_n_r<0, L>; + opcode_table_cb[0x47] = &CPU::op_bit_n_r<0, A>; + opcode_table_cb[0x48] = &CPU::op_bit_n_r<1, B>; + opcode_table_cb[0x49] = &CPU::op_bit_n_r<1, C>; + opcode_table_cb[0x4a] = &CPU::op_bit_n_r<1, D>; + opcode_table_cb[0x4b] = &CPU::op_bit_n_r<1, E>; + opcode_table_cb[0x4c] = &CPU::op_bit_n_r<1, H>; + opcode_table_cb[0x4d] = &CPU::op_bit_n_r<1, L>; + opcode_table_cb[0x4f] = &CPU::op_bit_n_r<1, A>; + opcode_table_cb[0x50] = &CPU::op_bit_n_r<2, B>; + opcode_table_cb[0x51] = &CPU::op_bit_n_r<2, C>; + opcode_table_cb[0x52] = &CPU::op_bit_n_r<2, D>; + opcode_table_cb[0x53] = &CPU::op_bit_n_r<2, E>; + opcode_table_cb[0x54] = &CPU::op_bit_n_r<2, H>; + opcode_table_cb[0x55] = &CPU::op_bit_n_r<2, L>; + opcode_table_cb[0x57] = &CPU::op_bit_n_r<2, A>; + opcode_table_cb[0x58] = &CPU::op_bit_n_r<3, B>; + opcode_table_cb[0x59] = &CPU::op_bit_n_r<3, C>; + opcode_table_cb[0x5a] = &CPU::op_bit_n_r<3, D>; + opcode_table_cb[0x5b] = &CPU::op_bit_n_r<3, E>; + opcode_table_cb[0x5c] = &CPU::op_bit_n_r<3, H>; + opcode_table_cb[0x5d] = &CPU::op_bit_n_r<3, L>; + opcode_table_cb[0x5f] = &CPU::op_bit_n_r<3, A>; + opcode_table_cb[0x60] = &CPU::op_bit_n_r<4, B>; + opcode_table_cb[0x61] = &CPU::op_bit_n_r<4, C>; + opcode_table_cb[0x62] = &CPU::op_bit_n_r<4, D>; + opcode_table_cb[0x63] = &CPU::op_bit_n_r<4, E>; + opcode_table_cb[0x64] = &CPU::op_bit_n_r<4, H>; + opcode_table_cb[0x65] = &CPU::op_bit_n_r<4, L>; + opcode_table_cb[0x67] = &CPU::op_bit_n_r<4, A>; + opcode_table_cb[0x68] = &CPU::op_bit_n_r<5, B>; + opcode_table_cb[0x69] = &CPU::op_bit_n_r<5, C>; + opcode_table_cb[0x6a] = &CPU::op_bit_n_r<5, D>; + opcode_table_cb[0x6b] = &CPU::op_bit_n_r<5, E>; + opcode_table_cb[0x6c] = &CPU::op_bit_n_r<5, H>; + opcode_table_cb[0x6d] = &CPU::op_bit_n_r<5, L>; + opcode_table_cb[0x6f] = &CPU::op_bit_n_r<5, A>; + opcode_table_cb[0x70] = &CPU::op_bit_n_r<6, B>; + opcode_table_cb[0x71] = &CPU::op_bit_n_r<6, C>; + opcode_table_cb[0x72] = &CPU::op_bit_n_r<6, D>; + opcode_table_cb[0x73] = &CPU::op_bit_n_r<6, E>; + opcode_table_cb[0x74] = &CPU::op_bit_n_r<6, H>; + opcode_table_cb[0x75] = &CPU::op_bit_n_r<6, L>; + opcode_table_cb[0x77] = &CPU::op_bit_n_r<6, A>; + opcode_table_cb[0x78] = &CPU::op_bit_n_r<7, B>; + opcode_table_cb[0x79] = &CPU::op_bit_n_r<7, C>; + opcode_table_cb[0x7a] = &CPU::op_bit_n_r<7, D>; + opcode_table_cb[0x7b] = &CPU::op_bit_n_r<7, E>; + opcode_table_cb[0x7c] = &CPU::op_bit_n_r<7, H>; + opcode_table_cb[0x7d] = &CPU::op_bit_n_r<7, L>; + opcode_table_cb[0x7f] = &CPU::op_bit_n_r<7, A>; + opcode_table_cb[0x80] = &CPU::op_res_n_r<0, B>; + opcode_table_cb[0x81] = &CPU::op_res_n_r<0, C>; + opcode_table_cb[0x82] = &CPU::op_res_n_r<0, D>; + opcode_table_cb[0x83] = &CPU::op_res_n_r<0, E>; + opcode_table_cb[0x84] = &CPU::op_res_n_r<0, H>; + opcode_table_cb[0x85] = &CPU::op_res_n_r<0, L>; + opcode_table_cb[0x87] = &CPU::op_res_n_r<0, A>; + opcode_table_cb[0x88] = &CPU::op_res_n_r<1, B>; + opcode_table_cb[0x89] = &CPU::op_res_n_r<1, C>; + opcode_table_cb[0x8a] = &CPU::op_res_n_r<1, D>; + opcode_table_cb[0x8b] = &CPU::op_res_n_r<1, E>; + opcode_table_cb[0x8c] = &CPU::op_res_n_r<1, H>; + opcode_table_cb[0x8d] = &CPU::op_res_n_r<1, L>; + opcode_table_cb[0x8f] = &CPU::op_res_n_r<1, A>; + opcode_table_cb[0x90] = &CPU::op_res_n_r<2, B>; + opcode_table_cb[0x91] = &CPU::op_res_n_r<2, C>; + opcode_table_cb[0x92] = &CPU::op_res_n_r<2, D>; + opcode_table_cb[0x93] = &CPU::op_res_n_r<2, E>; + opcode_table_cb[0x94] = &CPU::op_res_n_r<2, H>; + opcode_table_cb[0x95] = &CPU::op_res_n_r<2, L>; + opcode_table_cb[0x97] = &CPU::op_res_n_r<2, A>; + opcode_table_cb[0x98] = &CPU::op_res_n_r<3, B>; + opcode_table_cb[0x99] = &CPU::op_res_n_r<3, C>; + opcode_table_cb[0x9a] = &CPU::op_res_n_r<3, D>; + opcode_table_cb[0x9b] = &CPU::op_res_n_r<3, E>; + opcode_table_cb[0x9c] = &CPU::op_res_n_r<3, H>; + opcode_table_cb[0x9d] = &CPU::op_res_n_r<3, L>; + opcode_table_cb[0x9f] = &CPU::op_res_n_r<3, A>; + opcode_table_cb[0xa0] = &CPU::op_res_n_r<4, B>; + opcode_table_cb[0xa1] = &CPU::op_res_n_r<4, C>; + opcode_table_cb[0xa2] = &CPU::op_res_n_r<4, D>; + opcode_table_cb[0xa3] = &CPU::op_res_n_r<4, E>; + opcode_table_cb[0xa4] = &CPU::op_res_n_r<4, H>; + opcode_table_cb[0xa5] = &CPU::op_res_n_r<4, L>; + opcode_table_cb[0xa7] = &CPU::op_res_n_r<4, A>; + opcode_table_cb[0xa8] = &CPU::op_res_n_r<5, B>; + opcode_table_cb[0xa9] = &CPU::op_res_n_r<5, C>; + opcode_table_cb[0xaa] = &CPU::op_res_n_r<5, D>; + opcode_table_cb[0xab] = &CPU::op_res_n_r<5, E>; + opcode_table_cb[0xac] = &CPU::op_res_n_r<5, H>; + opcode_table_cb[0xad] = &CPU::op_res_n_r<5, L>; + opcode_table_cb[0xaf] = &CPU::op_res_n_r<5, A>; + opcode_table_cb[0xb0] = &CPU::op_res_n_r<6, B>; + opcode_table_cb[0xb1] = &CPU::op_res_n_r<6, C>; + opcode_table_cb[0xb2] = &CPU::op_res_n_r<6, D>; + opcode_table_cb[0xb3] = &CPU::op_res_n_r<6, E>; + opcode_table_cb[0xb4] = &CPU::op_res_n_r<6, H>; + opcode_table_cb[0xb5] = &CPU::op_res_n_r<6, L>; + opcode_table_cb[0xb7] = &CPU::op_res_n_r<6, A>; + opcode_table_cb[0xb8] = &CPU::op_res_n_r<7, B>; + opcode_table_cb[0xb9] = &CPU::op_res_n_r<7, C>; + opcode_table_cb[0xba] = &CPU::op_res_n_r<7, D>; + opcode_table_cb[0xbb] = &CPU::op_res_n_r<7, E>; + opcode_table_cb[0xbc] = &CPU::op_res_n_r<7, H>; + opcode_table_cb[0xbd] = &CPU::op_res_n_r<7, L>; + opcode_table_cb[0xbf] = &CPU::op_res_n_r<7, A>; + opcode_table_cb[0xc0] = &CPU::op_set_n_r<0, B>; + opcode_table_cb[0xc1] = &CPU::op_set_n_r<0, C>; + opcode_table_cb[0xc2] = &CPU::op_set_n_r<0, D>; + opcode_table_cb[0xc3] = &CPU::op_set_n_r<0, E>; + opcode_table_cb[0xc4] = &CPU::op_set_n_r<0, H>; + opcode_table_cb[0xc5] = &CPU::op_set_n_r<0, L>; + opcode_table_cb[0xc7] = &CPU::op_set_n_r<0, A>; + opcode_table_cb[0xc8] = &CPU::op_set_n_r<1, B>; + opcode_table_cb[0xc9] = &CPU::op_set_n_r<1, C>; + opcode_table_cb[0xca] = &CPU::op_set_n_r<1, D>; + opcode_table_cb[0xcb] = &CPU::op_set_n_r<1, E>; + opcode_table_cb[0xcc] = &CPU::op_set_n_r<1, H>; + opcode_table_cb[0xcd] = &CPU::op_set_n_r<1, L>; + opcode_table_cb[0xcf] = &CPU::op_set_n_r<1, A>; + opcode_table_cb[0xd0] = &CPU::op_set_n_r<2, B>; + opcode_table_cb[0xd1] = &CPU::op_set_n_r<2, C>; + opcode_table_cb[0xd2] = &CPU::op_set_n_r<2, D>; + opcode_table_cb[0xd3] = &CPU::op_set_n_r<2, E>; + opcode_table_cb[0xd4] = &CPU::op_set_n_r<2, H>; + opcode_table_cb[0xd5] = &CPU::op_set_n_r<2, L>; + opcode_table_cb[0xd7] = &CPU::op_set_n_r<2, A>; + opcode_table_cb[0xd8] = &CPU::op_set_n_r<3, B>; + opcode_table_cb[0xd9] = &CPU::op_set_n_r<3, C>; + opcode_table_cb[0xda] = &CPU::op_set_n_r<3, D>; + opcode_table_cb[0xdb] = &CPU::op_set_n_r<3, E>; + opcode_table_cb[0xdc] = &CPU::op_set_n_r<3, H>; + opcode_table_cb[0xdd] = &CPU::op_set_n_r<3, L>; + opcode_table_cb[0xdf] = &CPU::op_set_n_r<3, A>; + opcode_table_cb[0xe0] = &CPU::op_set_n_r<4, B>; + opcode_table_cb[0xe1] = &CPU::op_set_n_r<4, C>; + opcode_table_cb[0xe2] = &CPU::op_set_n_r<4, D>; + opcode_table_cb[0xe3] = &CPU::op_set_n_r<4, E>; + opcode_table_cb[0xe4] = &CPU::op_set_n_r<4, H>; + opcode_table_cb[0xe5] = &CPU::op_set_n_r<4, L>; + opcode_table_cb[0xe7] = &CPU::op_set_n_r<4, A>; + opcode_table_cb[0xe8] = &CPU::op_set_n_r<5, B>; + opcode_table_cb[0xe9] = &CPU::op_set_n_r<5, C>; + opcode_table_cb[0xea] = &CPU::op_set_n_r<5, D>; + opcode_table_cb[0xeb] = &CPU::op_set_n_r<5, E>; + opcode_table_cb[0xec] = &CPU::op_set_n_r<5, H>; + opcode_table_cb[0xed] = &CPU::op_set_n_r<5, L>; + opcode_table_cb[0xef] = &CPU::op_set_n_r<5, A>; + opcode_table_cb[0xf0] = &CPU::op_set_n_r<6, B>; + opcode_table_cb[0xf1] = &CPU::op_set_n_r<6, C>; + opcode_table_cb[0xf2] = &CPU::op_set_n_r<6, D>; + opcode_table_cb[0xf3] = &CPU::op_set_n_r<6, E>; + opcode_table_cb[0xf4] = &CPU::op_set_n_r<6, H>; + opcode_table_cb[0xf5] = &CPU::op_set_n_r<6, L>; + opcode_table_cb[0xf7] = &CPU::op_set_n_r<6, A>; + opcode_table_cb[0xf8] = &CPU::op_set_n_r<7, B>; + opcode_table_cb[0xf9] = &CPU::op_set_n_r<7, C>; + opcode_table_cb[0xfa] = &CPU::op_set_n_r<7, D>; + opcode_table_cb[0xfb] = &CPU::op_set_n_r<7, E>; + opcode_table_cb[0xfc] = &CPU::op_set_n_r<7, H>; + opcode_table_cb[0xfd] = &CPU::op_set_n_r<7, L>; + opcode_table_cb[0xff] = &CPU::op_set_n_r<7, A>; + + unsigned missing = 0; + for(unsigned n = 0; n < 256; n++) { + if(opcode_table[n] == &CPU::op_unknown) missing++; + if(opcode_table_cb[n] == &CPU::op_unknown) missing++; + } + + print("CPU opcodes: ", 512 - missing, " implemented, ", missing, " remaining.\n"); } #endif diff --git a/gameboy/cpu/core/core.hpp b/gameboy/cpu/core/core.hpp index 86c9ee04..82a0ed03 100755 --- a/gameboy/cpu/core/core.hpp +++ b/gameboy/cpu/core/core.hpp @@ -1,27 +1,67 @@ #include "registers.hpp" void (CPU::*opcode_table[256])(); +void (CPU::*opcode_table_cb[256])(); void initialize_opcode_table(); void op_unknown(); +void op_cb(); + //8-bit load commands +template void op_ld_r_r(); template void op_ld_r_n(); +template void op_ld_r_hl(); +void op_ld_hl_n(); +void op_ld_nn_a(); +void op_ld_a_ffn(); +void op_ld_ffn_a(); +void op_ld_ffc_a(); +void op_ldi_a_hl(); void op_ldd_hl_a(); //16-bit load commands -template void op_ld_rr_nn(); +template void op_ld_rr_nn(); +template void op_push_rr(); +template void op_pop_rr(); //8-bit arithmetic commands +template void op_add_a_r(); +template void op_and_r(); +void op_and_n(); template void op_xor_r(); +template void op_or_r(); +void op_cp_n(); +template void op_inc_r(); template void op_dec_r(); +void op_cpl(); + +//16-bit arithmetic commands +template void op_add_hl_rr(); +template void op_inc_rr(); +template void op_dec_rr(); + +//rotate/shift commands +template void op_swap_r(); + +//single-bit commands +template void op_bit_n_r(); +template void op_set_n_r(); +template void op_res_n_r(); //control commands void op_nop(); +void op_di(); +void op_ei(); //jump commands void op_jp_nn(); +void op_jp_hl(); template void op_jr_f_n(); +void op_call_nn(); +void op_ret(); +template void op_rst_n(); //disassembler.cpp string disassemble(uint16 pc); string disassemble_opcode(uint16 pc); +string disassemble_opcode_cb(uint16 pc); diff --git a/gameboy/cpu/core/disassembler.cpp b/gameboy/cpu/core/disassembler.cpp index 85e8e551..c8c69fa9 100755 --- a/gameboy/cpu/core/disassembler.cpp +++ b/gameboy/cpu/core/disassembler.cpp @@ -1,7 +1,24 @@ #ifdef CPU_CPP string CPU::disassemble(uint16 pc) { - return { hex<4>(pc), " ", disassemble_opcode(pc) }; + char output[80]; + memset(output, ' ', sizeof output); + output[79] = 0; + + string opcode = disassemble_opcode(pc); + string registers = { + " AF:", hex<4>(r[AF]), + " BC:", hex<4>(r[BC]), + " DE:", hex<4>(r[DE]), + " HL:", hex<4>(r[HL]), + " SP:", hex<4>(r[SP]) + }; + + memcpy(output + 0, hex<4>(pc), 4); + memcpy(output + 6, opcode, opcode.length()); + memcpy(output + 23, registers, registers.length()); + output[63] = 0; + return output; } string CPU::disassemble_opcode(uint16 pc) { @@ -12,19 +29,334 @@ string CPU::disassemble_opcode(uint16 pc) { switch(opcode) { case 0x00: return { "nop" }; + case 0x01: return { "ld bc,$", hex<2>(p1), hex<2>(p0) }; + case 0x03: return { "inc bc" }; case 0x05: return { "dec b" }; case 0x06: return { "ld b,$", hex<2>(p0) }; + case 0x09: return { "add hl,bc" }; + case 0x0b: return { "dec bc" }; + case 0x0c: return { "inc c" }; case 0x0d: return { "dec c" }; case 0x0e: return { "ld c,$", hex<2>(p0) }; - case 0x20: return { "jp nz,$", hex<2>(p0) }; + case 0x11: return { "ld de,$", hex<2>(p1), hex<2>(p0) }; + case 0x13: return { "inc de" }; + case 0x16: return { "ld d,$", hex<2>(p0) }; + case 0x19: return { "add hl,de" }; + case 0x1b: return { "dec de" }; + case 0x1e: return { "ld e,$", hex<2>(p0) }; + case 0x20: return { "jp nz,$", hex<4>(r[PC] + 2 + (int8)p0) }; case 0x21: return { "ld hl,$", hex<2>(p1), hex<2>(p0) }; + case 0x23: return { "inc hl" }; + case 0x26: return { "ld h,$", hex<2>(p0) }; + case 0x29: return { "add hl,hl" }; + case 0x2a: return { "ldi a,(hl)" }; + case 0x2b: return { "dec hl" }; + case 0x2e: return { "ld l,$", hex<2>(p0) }; + case 0x2f: return { "cpl" }; + case 0x31: return { "ld sp,$", hex<2>(p1), hex<2>(p0) }; case 0x32: return { "ldd (hl),a" }; + case 0x33: return { "inc sp" }; + case 0x36: return { "ld (hl),$", hex<2>(p0) }; + case 0x39: return { "add hl,sp" }; + case 0x3b: return { "dec sp" }; case 0x3e: return { "ld a,$", hex<2>(p0) }; + case 0x40: return { "ld b,b" }; + case 0x41: return { "ld b,c" }; + case 0x42: return { "ld b,d" }; + case 0x43: return { "ld b,e" }; + case 0x44: return { "ld b,h" }; + case 0x45: return { "ld b,l" }; + case 0x46: return { "ld b,(hl)" }; + case 0x47: return { "ld b,a" }; + case 0x48: return { "ld c,b" }; + case 0x49: return { "ld c,c" }; + case 0x4a: return { "ld c,d" }; + case 0x4b: return { "ld c,e" }; + case 0x4c: return { "ld c,h" }; + case 0x4d: return { "ld c,l" }; + case 0x4e: return { "ld c,(hl)" }; + case 0x4f: return { "ld c,a" }; + case 0x50: return { "ld d,b" }; + case 0x51: return { "ld d,c" }; + case 0x52: return { "ld d,d" }; + case 0x53: return { "ld d,e" }; + case 0x54: return { "ld d,h" }; + case 0x55: return { "ld d,l" }; + case 0x56: return { "ld d,(hl)" }; + case 0x57: return { "ld d,a" }; + case 0x58: return { "ld e,b" }; + case 0x59: return { "ld e,c" }; + case 0x5a: return { "ld e,d" }; + case 0x5b: return { "ld e,e" }; + case 0x5c: return { "ld e,h" }; + case 0x5d: return { "ld e,l" }; + case 0x5e: return { "ld e,(hl)" }; + case 0x5f: return { "ld e,a" }; + case 0x60: return { "ld h,b" }; + case 0x61: return { "ld h,c" }; + case 0x62: return { "ld h,d" }; + case 0x63: return { "ld h,e" }; + case 0x64: return { "ld h,h" }; + case 0x65: return { "ld h,l" }; + case 0x66: return { "ld h,(hl)" }; + case 0x67: return { "ld h,a" }; + case 0x68: return { "ld l,b" }; + case 0x69: return { "ld l,c" }; + case 0x6a: return { "ld l,d" }; + case 0x6b: return { "ld l,e" }; + case 0x6c: return { "ld l,h" }; + case 0x6d: return { "ld l,l" }; + case 0x6e: return { "ld l,(hl)" }; + case 0x6f: return { "ld l,a" }; + case 0x78: return { "ld a,b" }; + case 0x79: return { "ld a,c" }; + case 0x7a: return { "ld a,d" }; + case 0x7b: return { "ld a,e" }; + case 0x7c: return { "ld a,h" }; + case 0x7d: return { "ld a,l" }; + case 0x7e: return { "ld a,(hl)" }; + case 0x7f: return { "ld a,a" }; + case 0x80: return { "add a,b" }; + case 0x81: return { "add a,c" }; + case 0x82: return { "add a,d" }; + case 0x83: return { "add a,e" }; + case 0x84: return { "add a,h" }; + case 0x85: return { "add a,l" }; + case 0x87: return { "add a,a" }; + case 0xa0: return { "and b" }; + case 0xa1: return { "and c" }; + case 0xa2: return { "and d" }; + case 0xa3: return { "and e" }; + case 0xa4: return { "and h" }; + case 0xa5: return { "and l" }; + case 0xa7: return { "and a" }; + case 0xa8: return { "xor b" }; + case 0xa9: return { "xor c" }; + case 0xaa: return { "xor d" }; + case 0xab: return { "xor e" }; + case 0xac: return { "xor h" }; + case 0xad: return { "xor l" }; case 0xaf: return { "xor a" }; + case 0xb0: return { "or b" }; + case 0xb1: return { "or c" }; + case 0xb2: return { "or d" }; + case 0xb3: return { "or e" }; + case 0xb4: return { "or h" }; + case 0xb5: return { "or l" }; + case 0xb7: return { "or a" }; + case 0xc1: return { "pop bc" }; case 0xc3: return { "jp $", hex<2>(p1), hex<2>(p0) }; + case 0xc5: return { "push bc" }; + case 0xc7: return { "rst $0000" }; + case 0xc9: return { "ret" }; + case 0xcb: return disassemble_opcode_cb(pc + 1); + case 0xcd: return { "call $", hex<2>(p1), hex<2>(p0) }; + case 0xcf: return { "rst $0008" }; + case 0xd1: return { "pop de" }; + case 0xd5: return { "push de" }; + case 0xd7: return { "rst $0010" }; + case 0xdf: return { "rst $0018" }; + case 0xe0: return { "ld ($ff", hex<2>(p0), "),a" }; + case 0xe1: return { "pop hl" }; + case 0xe2: return { "ld ($ff00+c),a" }; + case 0xe5: return { "push hl" }; + case 0xe6: return { "and $", hex<2>(p0) }; + case 0xe7: return { "rst $0020" }; + case 0xe9: return { "jp hl" }; + case 0xea: return { "ld ($", hex<2>(p1), hex<2>(p0), "),a" }; + case 0xef: return { "rst $0028" }; + case 0xf0: return { "ld a,($ff", hex<2>(p0), ")" }; + case 0xf1: return { "pop af" }; + case 0xf3: return { "di" }; + case 0xf5: return { "push af" }; + case 0xf7: return { "rst $0030" }; + case 0xfb: return { "ei" }; + case 0xfe: return { "cp $", hex<2>(p0) }; + case 0xff: return { "rst $0038" }; } - return { "???? [", hex<2>(opcode), ",", hex<2>(p1), ",", hex<2>(p0), "]" }; + return { "??? [", hex<2>(opcode), ",", hex<2>(p1), ",", hex<2>(p0), "]" }; +} + +string CPU::disassemble_opcode_cb(uint16 pc) { + uint8 opcode = bus.read(pc); + uint8 p0 = bus.read(pc + 1); + uint8 p1 = bus.read(pc + 2); + uint8 p2 = bus.read(pc + 3); + + switch(opcode) { + case 0x37: return { "swap a" }; + case 0x40: return { "bit 0,b" }; + case 0x41: return { "bit 0,c" }; + case 0x42: return { "bit 0,d" }; + case 0x43: return { "bit 0,e" }; + case 0x44: return { "bit 0,h" }; + case 0x45: return { "bit 0,l" }; + case 0x47: return { "bit 0,a" }; + case 0x48: return { "bit 1,b" }; + case 0x49: return { "bit 1,c" }; + case 0x4a: return { "bit 1,d" }; + case 0x4b: return { "bit 1,e" }; + case 0x4c: return { "bit 1,h" }; + case 0x4d: return { "bit 1,l" }; + case 0x4f: return { "bit 1,a" }; + case 0x50: return { "bit 2,b" }; + case 0x51: return { "bit 2,c" }; + case 0x52: return { "bit 2,d" }; + case 0x53: return { "bit 2,e" }; + case 0x54: return { "bit 2,h" }; + case 0x55: return { "bit 2,l" }; + case 0x57: return { "bit 2,a" }; + case 0x58: return { "bit 3,b" }; + case 0x59: return { "bit 3,c" }; + case 0x5a: return { "bit 3,d" }; + case 0x5b: return { "bit 3,e" }; + case 0x5c: return { "bit 3,h" }; + case 0x5d: return { "bit 3,l" }; + case 0x5f: return { "bit 3,a" }; + case 0x60: return { "bit 4,b" }; + case 0x61: return { "bit 4,c" }; + case 0x62: return { "bit 4,d" }; + case 0x63: return { "bit 4,e" }; + case 0x64: return { "bit 4,h" }; + case 0x65: return { "bit 4,l" }; + case 0x67: return { "bit 4,a" }; + case 0x68: return { "bit 5,b" }; + case 0x69: return { "bit 5,c" }; + case 0x6a: return { "bit 5,d" }; + case 0x6b: return { "bit 5,e" }; + case 0x6c: return { "bit 5,h" }; + case 0x6d: return { "bit 5,l" }; + case 0x6f: return { "bit 5,a" }; + case 0x70: return { "bit 6,b" }; + case 0x71: return { "bit 6,c" }; + case 0x72: return { "bit 6,d" }; + case 0x73: return { "bit 6,e" }; + case 0x74: return { "bit 6,h" }; + case 0x75: return { "bit 6,l" }; + case 0x77: return { "bit 6,a" }; + case 0x78: return { "bit 7,b" }; + case 0x79: return { "bit 7,c" }; + case 0x7a: return { "bit 7,d" }; + case 0x7b: return { "bit 7,e" }; + case 0x7c: return { "bit 7,h" }; + case 0x7d: return { "bit 7,l" }; + case 0x7f: return { "bit 7,a" }; + case 0x80: return { "res 0,b" }; + case 0x81: return { "res 0,c" }; + case 0x82: return { "res 0,d" }; + case 0x83: return { "res 0,e" }; + case 0x84: return { "res 0,h" }; + case 0x85: return { "res 0,l" }; + case 0x87: return { "res 0,a" }; + case 0x88: return { "res 1,b" }; + case 0x89: return { "res 1,c" }; + case 0x8a: return { "res 1,d" }; + case 0x8b: return { "res 1,e" }; + case 0x8c: return { "res 1,h" }; + case 0x8d: return { "res 1,l" }; + case 0x8f: return { "res 1,a" }; + case 0x90: return { "res 2,b" }; + case 0x91: return { "res 2,c" }; + case 0x92: return { "res 2,d" }; + case 0x93: return { "res 2,e" }; + case 0x94: return { "res 2,h" }; + case 0x95: return { "res 2,l" }; + case 0x97: return { "res 2,a" }; + case 0x98: return { "res 3,b" }; + case 0x99: return { "res 3,c" }; + case 0x9a: return { "res 3,d" }; + case 0x9b: return { "res 3,e" }; + case 0x9c: return { "res 3,h" }; + case 0x9d: return { "res 3,l" }; + case 0x9f: return { "res 3,a" }; + case 0xa0: return { "res 4,b" }; + case 0xa1: return { "res 4,c" }; + case 0xa2: return { "res 4,d" }; + case 0xa3: return { "res 4,e" }; + case 0xa4: return { "res 4,h" }; + case 0xa5: return { "res 4,l" }; + case 0xa7: return { "res 4,a" }; + case 0xa8: return { "res 5,b" }; + case 0xa9: return { "res 5,c" }; + case 0xaa: return { "res 5,d" }; + case 0xab: return { "res 5,e" }; + case 0xac: return { "res 5,h" }; + case 0xad: return { "res 5,l" }; + case 0xaf: return { "res 5,a" }; + case 0xb0: return { "res 6,b" }; + case 0xb1: return { "res 6,c" }; + case 0xb2: return { "res 6,d" }; + case 0xb3: return { "res 6,e" }; + case 0xb4: return { "res 6,h" }; + case 0xb5: return { "res 6,l" }; + case 0xb7: return { "res 6,a" }; + case 0xb8: return { "res 7,b" }; + case 0xb9: return { "res 7,c" }; + case 0xba: return { "res 7,d" }; + case 0xbb: return { "res 7,e" }; + case 0xbc: return { "res 7,h" }; + case 0xbd: return { "res 7,l" }; + case 0xbf: return { "res 7,a" }; + case 0xc0: return { "set 0,b" }; + case 0xc1: return { "set 0,c" }; + case 0xc2: return { "set 0,d" }; + case 0xc3: return { "set 0,e" }; + case 0xc4: return { "set 0,h" }; + case 0xc5: return { "set 0,l" }; + case 0xc7: return { "set 0,a" }; + case 0xc8: return { "set 1,b" }; + case 0xc9: return { "set 1,c" }; + case 0xca: return { "set 1,d" }; + case 0xcb: return { "set 1,e" }; + case 0xcc: return { "set 1,h" }; + case 0xcd: return { "set 1,l" }; + case 0xcf: return { "set 1,a" }; + case 0xd0: return { "set 2,b" }; + case 0xd1: return { "set 2,c" }; + case 0xd2: return { "set 2,d" }; + case 0xd3: return { "set 2,e" }; + case 0xd4: return { "set 2,h" }; + case 0xd5: return { "set 2,l" }; + case 0xd7: return { "set 2,a" }; + case 0xd8: return { "set 3,b" }; + case 0xd9: return { "set 3,c" }; + case 0xda: return { "set 3,d" }; + case 0xdb: return { "set 3,e" }; + case 0xdc: return { "set 3,h" }; + case 0xdd: return { "set 3,l" }; + case 0xdf: return { "set 3,a" }; + case 0xe0: return { "set 4,b" }; + case 0xe1: return { "set 4,c" }; + case 0xe2: return { "set 4,d" }; + case 0xe3: return { "set 4,e" }; + case 0xe4: return { "set 4,h" }; + case 0xe5: return { "set 4,l" }; + case 0xe7: return { "set 4,a" }; + case 0xe8: return { "set 5,b" }; + case 0xe9: return { "set 5,c" }; + case 0xea: return { "set 5,d" }; + case 0xeb: return { "set 5,e" }; + case 0xec: return { "set 5,h" }; + case 0xed: return { "set 5,l" }; + case 0xef: return { "set 5,a" }; + case 0xf0: return { "set 6,b" }; + case 0xf1: return { "set 6,c" }; + case 0xf2: return { "set 6,d" }; + case 0xf3: return { "set 6,e" }; + case 0xf4: return { "set 6,h" }; + case 0xf5: return { "set 6,l" }; + case 0xf7: return { "set 6,a" }; + case 0xf8: return { "set 7,b" }; + case 0xf9: return { "set 7,c" }; + case 0xfa: return { "set 7,d" }; + case 0xfb: return { "set 7,e" }; + case 0xfc: return { "set 7,h" }; + case 0xfd: return { "set 7,l" }; + case 0xff: return { "set 7,a" }; + } + + return { "cb? [", hex<2>(opcode), ",", hex<2>(p0), ",", hex<2>(p1), "]" }; } #endif diff --git a/gameboy/cpu/core/registers.hpp b/gameboy/cpu/core/registers.hpp index db4cb602..fbbcbd80 100755 --- a/gameboy/cpu/core/registers.hpp +++ b/gameboy/cpu/core/registers.hpp @@ -18,6 +18,7 @@ enum { struct Register { virtual operator unsigned() const = 0; virtual unsigned operator=(unsigned x) = 0; + Register& operator=(const Register &x) { operator=((unsigned)x); return *this; } unsigned operator++(int) { unsigned r = *this; operator=(*this + 1); return r; } unsigned operator--(int) { unsigned r = *this; operator=(*this - 1); return r; } diff --git a/gameboy/cpu/cpu.cpp b/gameboy/cpu/cpu.cpp index b0646f32..f0c09f6e 100755 --- a/gameboy/cpu/cpu.cpp +++ b/gameboy/cpu/cpu.cpp @@ -4,6 +4,7 @@ namespace GameBoy { #include "core/core.cpp" +#include "mmio/mmio.cpp" #include "timing/timing.cpp" CPU cpu; @@ -23,6 +24,10 @@ void CPU::main() { } void CPU::power() { + 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 + reset(); } @@ -36,7 +41,7 @@ void CPU::reset() { r[DE] = 0x0000; r[HL] = 0x0000; - status.lycounter = 0; + status.ime = 0; } CPU::CPU() { diff --git a/gameboy/cpu/cpu.hpp b/gameboy/cpu/cpu.hpp index fa28f0c3..d969145a 100755 --- a/gameboy/cpu/cpu.hpp +++ b/gameboy/cpu/cpu.hpp @@ -1,11 +1,15 @@ -struct CPU : Processor { +struct CPU : Processor, MMIO { #include "core/core.hpp" + #include "mmio/mmio.hpp" #include "timing/timing.hpp" struct Status { - unsigned lycounter; + bool ime; } status; + uint8 wram[8192]; + uint8 hram[128]; + static void Main(); void main(); void power(); diff --git a/gameboy/cpu/mmio/mmio.cpp b/gameboy/cpu/mmio/mmio.cpp new file mode 100755 index 00000000..edb7b8f6 --- /dev/null +++ b/gameboy/cpu/mmio/mmio.cpp @@ -0,0 +1,15 @@ +#ifdef CPU_CPP + +uint8 CPU::mmio_read(uint16 addr) { + if(addr >= 0xc000 && addr <= 0xdfff) return wram[addr & 0x1fff]; + if(addr >= 0xe000 && addr <= 0xfdff) return wram[addr & 0x1fff]; + if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f]; +} + +void CPU::mmio_write(uint16 addr, uint8 data) { + if(addr >= 0xc000 && addr <= 0xdfff) { wram[addr & 0x1fff] = data; return; } + if(addr >= 0xe000 && addr <= 0xfdff) { wram[addr & 0x1fff] = data; return; } + if(addr >= 0xff80 && addr <= 0xfffe) { hram[addr & 0x7f] = data; return; } +} + +#endif diff --git a/gameboy/cpu/mmio/mmio.hpp b/gameboy/cpu/mmio/mmio.hpp new file mode 100755 index 00000000..92b8f614 --- /dev/null +++ b/gameboy/cpu/mmio/mmio.hpp @@ -0,0 +1,2 @@ +uint8 mmio_read(uint16 addr); +void mmio_write(uint16 addr, uint8 data); diff --git a/gameboy/cpu/timing/timing.cpp b/gameboy/cpu/timing/timing.cpp index becf6feb..7f7e8ef9 100755 --- a/gameboy/cpu/timing/timing.cpp +++ b/gameboy/cpu/timing/timing.cpp @@ -16,12 +16,12 @@ void CPU::add_clocks(unsigned clocks) { void CPU::scanline() { clock -= 456; - status.lycounter++; - if(status.lycounter >= 154) frame(); + lcd.status.ly++; + if(lcd.status.ly >= 154) frame(); } void CPU::frame() { - status.lycounter -= 154; + lcd.status.ly -= 154; scheduler.exit(); } diff --git a/gameboy/gameboy.hpp b/gameboy/gameboy.hpp index c9c6948b..c5c229b3 100755 --- a/gameboy/gameboy.hpp +++ b/gameboy/gameboy.hpp @@ -5,7 +5,7 @@ namespace GameBoy { namespace Info { static const char Name[] = "bgameboy"; - static const char Version[] = "000.01"; + static const char Version[] = "000.02"; } } @@ -45,7 +45,8 @@ namespace GameBoy { #include #include - #include #include + #include #include + #include }; diff --git a/gameboy/lcd/lcd.cpp b/gameboy/lcd/lcd.cpp new file mode 100755 index 00000000..fef0a795 --- /dev/null +++ b/gameboy/lcd/lcd.cpp @@ -0,0 +1,21 @@ +#include + +#define LCD_CPP +namespace GameBoy { + +#include "mmio/mmio.cpp" +LCD lcd; + +void LCD::power() { + for(unsigned n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this; //VRAM + for(unsigned n = 0xff40; n <= 0xff4f; n++) bus.mmio[n] = this; //MMIO + for(unsigned n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM + + reset(); +} + +void LCD::reset() { + status.ly = 0; +} + +} diff --git a/gameboy/lcd/lcd.hpp b/gameboy/lcd/lcd.hpp new file mode 100755 index 00000000..07a2ae16 --- /dev/null +++ b/gameboy/lcd/lcd.hpp @@ -0,0 +1,15 @@ +struct LCD : Processor, MMIO { + #include "mmio/mmio.hpp" + + struct Status { + unsigned ly; + } status; + + uint8 vram[8192]; + uint8 oam[160]; + + void power(); + void reset(); +}; + +extern LCD lcd; diff --git a/gameboy/lcd/mmio/mmio.cpp b/gameboy/lcd/mmio/mmio.cpp new file mode 100755 index 00000000..51aeb634 --- /dev/null +++ b/gameboy/lcd/mmio/mmio.cpp @@ -0,0 +1,26 @@ +#ifdef LCD_CPP + +uint8 LCD::mmio_read(uint16 addr) { + if(addr >= 0xa000 && addr <= 0xbfff) return vram[addr & 0x1fff]; + if(addr >= 0xfe00 && addr <= 0xfe9f) return oam[addr & 0xff]; + + //LY + if(addr == 0xff44) { + return status.ly; + } + + return 0x00; +} + +void LCD::mmio_write(uint16 addr, uint8 data) { + if(addr >= 0xa000 && addr <= 0xbfff) { vram[addr & 0x1fff] = data; return; } + if(addr >= 0xfe00 && addr <= 0xfe9f) { oam[addr & 0xff] = data; return; } + + //LY + if(addr == 0xff44) { + status.ly = 0; + return; + } +} + +#endif diff --git a/gameboy/lcd/mmio/mmio.hpp b/gameboy/lcd/mmio/mmio.hpp new file mode 100755 index 00000000..92b8f614 --- /dev/null +++ b/gameboy/lcd/mmio/mmio.hpp @@ -0,0 +1,2 @@ +uint8 mmio_read(uint16 addr); +void mmio_write(uint16 addr, uint8 data); diff --git a/gameboy/memory/memory.cpp b/gameboy/memory/memory.cpp index 9bc3bc98..00998f19 100755 --- a/gameboy/memory/memory.cpp +++ b/gameboy/memory/memory.cpp @@ -3,6 +3,7 @@ #define MEMORY_CPP namespace GameBoy { +Unmapped unmapped; Bus bus; uint8_t& Memory::operator[](unsigned addr) { @@ -41,100 +42,19 @@ Memory::~Memory() { // uint8 Bus::read(uint16 addr) { - if(/* addr >= 0x0000 && */ addr <= 0x7fff) { //Cartridge ROM - return cartrom[addr]; - } - - if(/* addr >= 0x8000 && */ addr <= 0x9fff) { //VRAM - return vram[addr & 0x1fff]; - } - - if(/* addr >= 0xa000 && */ addr <= 0xbfff) { //Cartridge RAM - if(cartram.size == 0) return 0x00; - return cartram[addr & 0x1fff]; - } - - if(/* addr >= 0xc000 && */ addr <= 0xdfff) { //WRAM - return wram[addr & 0x1fff]; - } - - if(/* addr >= 0xe000 && */ addr <= 0xfdff) { //WRAM (mirror) - return wram[addr & 0x1fff]; - } - - if(/* addr >= 0xfe00 && */ addr <= 0xfe9f) { //OAM - return oam[addr & 0xff]; - } - - if(/* addr >= 0xfea0 && */ addr <= 0xfeff) { //unmapped - return 0x00; - } - - if(/* addr >= 0xff00 && */ addr <= 0xff7f) { //MMIO - return 0x00; - } - - if(/* addr >= 0xff80 && */ addr <= 0xfffe) { //HRAM - return hram[addr & 0x7f]; - } - - //addr == 0xffff (interrupt enable register) - return 0x00; + return mmio[addr]->mmio_read(addr); } void Bus::write(uint16 addr, uint8 data) { - if(/* addr >= 0x0000 && */ addr <= 0x7fff) { //Cartridge ROM - return; - } - - if(/* addr >= 0x8000 && */ addr <= 0x9fff) { //VRAM - vram[addr & 0x1fff] = data; - return; - } - - if(/* addr >= 0xa000 && */ addr <= 0xbfff) { //Cartridge RAM - if(cartram.size == 0) return; - cartram[addr & 0x1fff] = data; - return; - } - - if(/* addr >= 0xc000 && */ addr <= 0xdfff) { //WRAM - wram[addr & 0x1fff] = data; - return; - } - - if(/* addr >= 0xe000 && */ addr <= 0xfdff) { //WRAM (mirror) - wram[addr & 0x1fff] = data; - return; - } - - if(/* addr >= 0xfe00 && */ addr <= 0xfe9f) { //OAM - oam[addr & 0xff] = data; - return; - } - - if(/* addr >= 0xfea0 && */ addr <= 0xfeff) { //unmapped - return; - } - - if(/* addr >= 0xff00 && */ addr <= 0xff7f) { //MMIO - return; - } - - if(/* addr >= 0xff80 && */ addr <= 0xfffe) { //HRAM - hram[addr & 0x7f] = data; - return; - } - - //addr == 0xffff (interrupt enable register) - return; + mmio[addr]->mmio_write(addr, data); } -Bus::Bus() { - vram.allocate(8192); - wram.allocate(8192); - oam.allocate ( 160); - hram.allocate( 128); +void Bus::power() { + for(unsigned n = 0; n < 65536; n++) mmio[n] = &unmapped; + reset(); +} + +void Bus::reset() { } } diff --git a/gameboy/memory/memory.hpp b/gameboy/memory/memory.hpp index 4c94bb8d..0b9ca99c 100755 --- a/gameboy/memory/memory.hpp +++ b/gameboy/memory/memory.hpp @@ -10,19 +10,27 @@ struct Memory { ~Memory(); }; -class Bus { -public: +struct MMIO { + virtual uint8 mmio_read(uint16 addr) = 0; + virtual void mmio_write(uint16 addr, uint8 data) = 0; +}; + +struct Unmapped : MMIO { + uint8 mmio_read(uint16) { return 0x00; } + void mmio_write(uint16, uint8) {} +}; + +struct Bus { Memory cartrom; Memory cartram; - Memory vram; - Memory wram; - Memory oam; - Memory hram; + MMIO *mmio[65536]; uint8 read(uint16 addr); void write(uint16 addr, uint8 data); - Bus(); + void power(); + void reset(); }; +extern Unmapped unmapped; extern Bus bus; diff --git a/gameboy/system/system.cpp b/gameboy/system/system.cpp index 2a8b0aa1..1fb68c9a 100755 --- a/gameboy/system/system.cpp +++ b/gameboy/system/system.cpp @@ -10,12 +10,18 @@ void System::init(Interface *interface_) { } void System::power() { + bus.power(); + cartridge.power(); cpu.power(); + lcd.power(); scheduler.init(); } void System::reset() { + bus.reset(); + cartridge.power(); cpu.reset(); + lcd.reset(); scheduler.init(); }