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.
This commit is contained in:
Tim Allen 2010-12-29 22:03:42 +11:00
parent e0a9f1cf2c
commit 1c3c7fe0a7
22 changed files with 1072 additions and 127 deletions

View File

@ -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/)

View File

@ -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();
}
}

View File

@ -1,5 +1,6 @@
class Cartridge : property<Cartridge> {
public:
struct Cartridge : MMIO, property<Cartridge> {
#include "mmio/mmio.hpp"
struct Information {
string name;
uint8 cgbflag;
@ -11,9 +12,20 @@ public:
readonly<bool> 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;

10
gameboy/cartridge/mmio/mmio.cpp Executable file
View File

@ -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

View File

@ -0,0 +1,2 @@
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);

View File

@ -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<unsigned x, unsigned y> void CPU::op_ld_r_r() {
r[x] = r[y];
}
template<unsigned x> void CPU::op_ld_r_n() {
r[x] = op_read(r[PC]++);
}
template<unsigned x> 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<unsigned x, unsigned y> void CPU::op_ld_rr_nn() {
r[y] = op_read(r[PC]++);
r[x] = op_read(r[PC]++);
template<unsigned x> void CPU::op_ld_rr_nn() {
r[x] = op_read(r[PC]++) << 0;
r[x] |= op_read(r[PC]++) << 8;
}
template<unsigned x> void CPU::op_push_rr() {
op_write(--r[SP], r[x] >> 8);
op_write(--r[SP], r[x] >> 0);
op_io();
}
template<unsigned x> void CPU::op_pop_rr() {
r[x] = op_read(r[SP]++) << 0;
r[x] |= op_read(r[SP]++) << 8;
}
//8-bit arithmetic commands
template<unsigned x> 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<unsigned x> 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<unsigned x> void CPU::op_xor_r() {
r[A] ^= r[x];
r.f.z = r[A] == 0;
@ -43,11 +120,87 @@ template<unsigned x> void CPU::op_xor_r() {
r.f.c = 0;
}
template<unsigned x> 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<unsigned x> 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<unsigned x> 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<unsigned x> 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<unsigned x> void CPU::op_inc_rr() {
r[x]++;
}
template<unsigned x> void CPU::op_dec_rr() {
r[x]--;
}
//rotate/shift commands
template<unsigned x> 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<unsigned b, unsigned x> void CPU::op_bit_n_r() {
r.f.z = (r[x] & (1 << b)) == 0;
r.f.n = 0;
r.f.h = 1;
}
template<unsigned b, unsigned x> void CPU::op_set_n_r() {
r[x] |= 1 << b;
}
template<unsigned b, unsigned x> void CPU::op_res_n_r() {
r[x] &= ~(1 << b);
}
//control commands
@ -55,6 +208,14 @@ template<unsigned x> 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<unsigned x, bool y> void CPU::op_jr_f_n() {
int8 n = op_read(r[PC]++);
if(r.f[x] == y) {
@ -72,20 +237,359 @@ template<unsigned x, bool y> 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<unsigned n> 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<BC>;
opcode_table[0x03] = &CPU::op_inc_rr<BC>;
opcode_table[0x05] = &CPU::op_dec_r<B>;
opcode_table[0x06] = &CPU::op_ld_r_n<B>;
opcode_table[0x09] = &CPU::op_add_hl_rr<BC>;
opcode_table[0x0b] = &CPU::op_dec_rr<BC>;
opcode_table[0x0c] = &CPU::op_inc_r<C>;
opcode_table[0x0d] = &CPU::op_dec_r<C>;
opcode_table[0x0e] = &CPU::op_ld_r_n<C>;
opcode_table[0x11] = &CPU::op_ld_rr_nn<DE>;
opcode_table[0x13] = &CPU::op_inc_rr<DE>;
opcode_table[0x16] = &CPU::op_ld_r_n<D>;
opcode_table[0x19] = &CPU::op_add_hl_rr<DE>;
opcode_table[0x1b] = &CPU::op_dec_rr<DE>;
opcode_table[0x1e] = &CPU::op_ld_r_n<E>;
opcode_table[0x20] = &CPU::op_jr_f_n<ZF, 0>;
opcode_table[0x21] = &CPU::op_ld_rr_nn<H, L>;
opcode_table[0x21] = &CPU::op_ld_rr_nn<HL>;
opcode_table[0x23] = &CPU::op_inc_rr<HL>;
opcode_table[0x26] = &CPU::op_ld_r_n<H>;
opcode_table[0x29] = &CPU::op_add_hl_rr<HL>;
opcode_table[0x2a] = &CPU::op_ldi_a_hl;
opcode_table[0x2b] = &CPU::op_dec_rr<HL>;
opcode_table[0x2e] = &CPU::op_ld_r_n<L>;
opcode_table[0x2f] = &CPU::op_cpl;
opcode_table[0x31] = &CPU::op_ld_rr_nn<SP>;
opcode_table[0x32] = &CPU::op_ldd_hl_a;
opcode_table[0x33] = &CPU::op_inc_rr<SP>;
opcode_table[0x36] = &CPU::op_ld_hl_n;
opcode_table[0x39] = &CPU::op_add_hl_rr<SP>;
opcode_table[0x3b] = &CPU::op_dec_rr<SP>;
opcode_table[0x3e] = &CPU::op_ld_r_n<A>;
opcode_table[0x40] = &CPU::op_ld_r_r<B, B>;
opcode_table[0x41] = &CPU::op_ld_r_r<B, C>;
opcode_table[0x42] = &CPU::op_ld_r_r<B, D>;
opcode_table[0x43] = &CPU::op_ld_r_r<B, E>;
opcode_table[0x44] = &CPU::op_ld_r_r<B, H>;
opcode_table[0x45] = &CPU::op_ld_r_r<B, L>;
opcode_table[0x46] = &CPU::op_ld_r_hl<B>;
opcode_table[0x47] = &CPU::op_ld_r_r<B, A>;
opcode_table[0x48] = &CPU::op_ld_r_r<C, B>;
opcode_table[0x49] = &CPU::op_ld_r_r<C, C>;
opcode_table[0x4a] = &CPU::op_ld_r_r<C, D>;
opcode_table[0x4b] = &CPU::op_ld_r_r<C, E>;
opcode_table[0x4c] = &CPU::op_ld_r_r<C, H>;
opcode_table[0x4d] = &CPU::op_ld_r_r<C, L>;
opcode_table[0x4e] = &CPU::op_ld_r_hl<C>;
opcode_table[0x4f] = &CPU::op_ld_r_r<C, A>;
opcode_table[0x50] = &CPU::op_ld_r_r<D, B>;
opcode_table[0x51] = &CPU::op_ld_r_r<D, C>;
opcode_table[0x52] = &CPU::op_ld_r_r<D, D>;
opcode_table[0x53] = &CPU::op_ld_r_r<D, E>;
opcode_table[0x54] = &CPU::op_ld_r_r<D, H>;
opcode_table[0x55] = &CPU::op_ld_r_r<D, L>;
opcode_table[0x56] = &CPU::op_ld_r_hl<D>;
opcode_table[0x57] = &CPU::op_ld_r_r<D, A>;
opcode_table[0x58] = &CPU::op_ld_r_r<E, B>;
opcode_table[0x59] = &CPU::op_ld_r_r<E, C>;
opcode_table[0x5a] = &CPU::op_ld_r_r<E, D>;
opcode_table[0x5b] = &CPU::op_ld_r_r<E, E>;
opcode_table[0x5c] = &CPU::op_ld_r_r<E, H>;
opcode_table[0x5d] = &CPU::op_ld_r_r<E, L>;
opcode_table[0x5e] = &CPU::op_ld_r_hl<E>;
opcode_table[0x5f] = &CPU::op_ld_r_r<E, A>;
opcode_table[0x60] = &CPU::op_ld_r_r<H, B>;
opcode_table[0x61] = &CPU::op_ld_r_r<H, C>;
opcode_table[0x62] = &CPU::op_ld_r_r<H, D>;
opcode_table[0x63] = &CPU::op_ld_r_r<H, E>;
opcode_table[0x64] = &CPU::op_ld_r_r<H, H>;
opcode_table[0x65] = &CPU::op_ld_r_r<H, L>;
opcode_table[0x66] = &CPU::op_ld_r_hl<H>;
opcode_table[0x67] = &CPU::op_ld_r_r<H, A>;
opcode_table[0x68] = &CPU::op_ld_r_r<L, B>;
opcode_table[0x69] = &CPU::op_ld_r_r<L, C>;
opcode_table[0x6a] = &CPU::op_ld_r_r<L, D>;
opcode_table[0x6b] = &CPU::op_ld_r_r<L, E>;
opcode_table[0x6c] = &CPU::op_ld_r_r<L, H>;
opcode_table[0x6d] = &CPU::op_ld_r_r<L, L>;
opcode_table[0x6e] = &CPU::op_ld_r_hl<L>;
opcode_table[0x6f] = &CPU::op_ld_r_r<L, A>;
opcode_table[0x78] = &CPU::op_ld_r_r<A, B>;
opcode_table[0x79] = &CPU::op_ld_r_r<A, C>;
opcode_table[0x7a] = &CPU::op_ld_r_r<A, D>;
opcode_table[0x7b] = &CPU::op_ld_r_r<A, E>;
opcode_table[0x7c] = &CPU::op_ld_r_r<A, H>;
opcode_table[0x7d] = &CPU::op_ld_r_r<A, L>;
opcode_table[0x7e] = &CPU::op_ld_r_hl<E>;
opcode_table[0x7f] = &CPU::op_ld_r_r<A, A>;
opcode_table[0x80] = &CPU::op_add_a_r<B>;
opcode_table[0x81] = &CPU::op_add_a_r<C>;
opcode_table[0x82] = &CPU::op_add_a_r<D>;
opcode_table[0x83] = &CPU::op_add_a_r<E>;
opcode_table[0x84] = &CPU::op_add_a_r<H>;
opcode_table[0x85] = &CPU::op_add_a_r<L>;
opcode_table[0x87] = &CPU::op_add_a_r<A>;
opcode_table[0xa0] = &CPU::op_and_r<B>;
opcode_table[0xa1] = &CPU::op_and_r<C>;
opcode_table[0xa2] = &CPU::op_and_r<D>;
opcode_table[0xa3] = &CPU::op_and_r<E>;
opcode_table[0xa4] = &CPU::op_and_r<H>;
opcode_table[0xa5] = &CPU::op_and_r<L>;
opcode_table[0xa7] = &CPU::op_and_r<A>;
opcode_table[0xa8] = &CPU::op_xor_r<B>;
opcode_table[0xa9] = &CPU::op_xor_r<C>;
opcode_table[0xaa] = &CPU::op_xor_r<D>;
opcode_table[0xab] = &CPU::op_xor_r<E>;
opcode_table[0xac] = &CPU::op_xor_r<H>;
opcode_table[0xad] = &CPU::op_xor_r<L>;
opcode_table[0xaf] = &CPU::op_xor_r<A>;
opcode_table[0xb0] = &CPU::op_or_r<B>;
opcode_table[0xb1] = &CPU::op_or_r<C>;
opcode_table[0xb2] = &CPU::op_or_r<D>;
opcode_table[0xb3] = &CPU::op_or_r<E>;
opcode_table[0xb4] = &CPU::op_or_r<H>;
opcode_table[0xb5] = &CPU::op_or_r<L>;
opcode_table[0xb7] = &CPU::op_or_r<A>;
opcode_table[0xc1] = &CPU::op_pop_rr<BC>;
opcode_table[0xc3] = &CPU::op_jp_nn;
opcode_table[0xc5] = &CPU::op_push_rr<BC>;
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<DE>;
opcode_table[0xd5] = &CPU::op_push_rr<DE>;
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<HL>;
opcode_table[0xe2] = &CPU::op_ld_ffc_a;
opcode_table[0xe5] = &CPU::op_push_rr<HL>;
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<AF>;
opcode_table[0xf3] = &CPU::op_di;
opcode_table[0xf5] = &CPU::op_push_rr<AF>;
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<A>;
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

View File

@ -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<unsigned x, unsigned y> void op_ld_r_r();
template<unsigned x> void op_ld_r_n();
template<unsigned x> 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<unsigned x, unsigned y> void op_ld_rr_nn();
template<unsigned x> void op_ld_rr_nn();
template<unsigned x> void op_push_rr();
template<unsigned x> void op_pop_rr();
//8-bit arithmetic commands
template<unsigned x> void op_add_a_r();
template<unsigned x> void op_and_r();
void op_and_n();
template<unsigned x> void op_xor_r();
template<unsigned x> void op_or_r();
void op_cp_n();
template<unsigned x> void op_inc_r();
template<unsigned x> void op_dec_r();
void op_cpl();
//16-bit arithmetic commands
template<unsigned x> void op_add_hl_rr();
template<unsigned x> void op_inc_rr();
template<unsigned x> void op_dec_rr();
//rotate/shift commands
template<unsigned x> void op_swap_r();
//single-bit commands
template<unsigned b, unsigned x> void op_bit_n_r();
template<unsigned b, unsigned x> void op_set_n_r();
template<unsigned b, unsigned x> 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<unsigned x, bool y> void op_jr_f_n();
void op_call_nn();
void op_ret();
template<unsigned n> void op_rst_n();
//disassembler.cpp
string disassemble(uint16 pc);
string disassemble_opcode(uint16 pc);
string disassemble_opcode_cb(uint16 pc);

View File

@ -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

View File

@ -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; }

View File

@ -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() {

View File

@ -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();

15
gameboy/cpu/mmio/mmio.cpp Executable file
View File

@ -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

2
gameboy/cpu/mmio/mmio.hpp Executable file
View File

@ -0,0 +1,2 @@
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);

View File

@ -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();
}

View File

@ -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 <system/system.hpp>
#include <scheduler/scheduler.hpp>
#include <cartridge/cartridge.hpp>
#include <memory/memory.hpp>
#include <cartridge/cartridge.hpp>
#include <cpu/cpu.hpp>
#include <lcd/lcd.hpp>
};

21
gameboy/lcd/lcd.cpp Executable file
View File

@ -0,0 +1,21 @@
#include <gameboy.hpp>
#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;
}
}

15
gameboy/lcd/lcd.hpp Executable file
View File

@ -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;

26
gameboy/lcd/mmio/mmio.cpp Executable file
View File

@ -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

2
gameboy/lcd/mmio/mmio.hpp Executable file
View File

@ -0,0 +1,2 @@
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);

View File

@ -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() {
}
}

View File

@ -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;

View File

@ -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();
}