diff options
-rw-r--r-- | gb.h | 27 | ||||
-rwxr-xr-x | gen.rb | 13 | ||||
-rw-r--r-- | ops.yaml | 750 | ||||
-rw-r--r-- | test.c | 95 |
4 files changed, 485 insertions, 400 deletions
@@ -28,7 +28,23 @@ typedef enum { // frame size, in bytes #define GB_FRAME_SIZE (3 * 160 * 144) +typedef struct gb_t_ gb_t; + typedef struct { + void (*on_set_rom_bank)(const gb_t *, const uint16_t); + void (*on_set_ram_bank)(const gb_t *, const uint16_t); + void (*on_vblank)(const gb_t *); + void (*on_hblank)(const gb_t *); + void (*on_set_cpu_state)(const gb_t *, const gb_cpu_state_t); + void (*on_timer)(const gb_t *); + void (*on_gpu_step)(const gb_t *); + void (*on_gpu_set_mode)(const gb_t *, const uint8_t); + void *cb_data; +} gb_config_t; + +struct gb_t_ { + const gb_config_t *config; + struct { // _Bool in_bios; @@ -116,12 +132,19 @@ typedef struct { // interrupt master enable _Bool ime; } cpu; -} gb_t; +}; + +void gb_init( + gb_t * const ctx, + const gb_config_t * const config, + const uint8_t *rom, + const uint32_t rom_size +); -void gb_init(gb_t * const, const uint8_t *rom, const uint32_t rom_size); void gb_set_buttons(gb_t * const, const uint8_t); void gb_frame(gb_t * const); void gb_step(gb_t * const); +_Bool gb_disasm(gb_t * const, char * const, size_t * const); const uint8_t *gb_get_rgb_frame(const gb_t * const); #ifdef __cplusplus @@ -7,7 +7,17 @@ require 'erb' DATA = YAML.load_file(File.join(__dir__, 'ops.yaml')) switches = Hash.new { |h, k| h[k] = [] } +op_meta = [] DATA['ops'].each do |set_id, ops| + op_meta += ops.map { |op| + '{ "%s", %d }, /* 0x%02x + 0x%s */' % [ + op['id'], + op['len'] || 0, + (set_id == 'main') ? 0 : 255, + op['hex'].to_s(16).upcase.rjust(2, '0'), + ] + } + switches[set_id] = ops.select { |op| op['op'] != 'PREFIX' }.map { |op| @@ -63,5 +73,8 @@ DATA['ops'].each do |set_id, ops| }.join("\n") end +# build complete ops list +ops = op_meta.join("\n") + t = ERB.new(DATA['templates']['main']) puts t.run(binding) @@ -761,6 +761,7 @@ ops: c: code: | cpu_wb(ctx, RB_A, cpu_rb(ctx, RB_A) ^ 0xFF); + cpu_wf(ctx, F_N | F_H, F_N | F_H); - id: JR NC, r8 hex: 0x30 cat: "jumps/calls" @@ -3746,7 +3747,8 @@ ops: h: c: code: | - cpu_ww(ctx, RW_PC, mmu_rw(ctx, cpu_rw(ctx, RW_HL))); + // cpu_ww(ctx, RW_PC, mmu_rw(ctx, cpu_rw(ctx, RW_HL))); + cpu_ww(ctx, RW_PC, cpu_rw(ctx, RW_HL)); - id: LD (a16), A hex: 0xEA cat: "8-bit load/store/move" @@ -8073,6 +8075,7 @@ templates: #include <string.h> // memset() #include <stdint.h> // uint8_t ,etc #include <stdbool.h> // bool + #include <stdio.h> // snprintf() #include "gb.h" #define UNUSED(a) ((void) (a)) @@ -8227,7 +8230,7 @@ templates: (((ctx->mmu.mbc1.rom_ram_mode ? 0 : ctx->mmu.mbc1.ram_bank) & 0x3) << 19) | // B: rom_bank (5 bits, low bit set) - (((ctx->mmu.mbc1.rom_bank & 0x1F) | 0x1) << 15) | + (((ctx->mmu.mbc1.rom_bank & 0x1F) | 0x1) << 14) | // C: address (14 bits) ((addr - 0x4000) & 0x3FFF) @@ -8262,9 +8265,11 @@ templates: case 0x6000: case 0x7000: // ROM1 (16k, banked) - // FIXME: clamp to rom size? - // mbc1 rom1 banking (TCAGBD.pdf, 11.2.2) - return ctx->mmu.rom[mbc1_rom_get_addr(ctx, addr)]; + { + // mbc1 rom1 banking (TCAGBD.pdf, 11.2.2) + const uint32_t addr_ofs = mbc1_rom_get_addr(ctx, addr); + return addr_ofs < ctx->mmu.rom_size ? ctx->mmu.rom[addr_ofs] : 0; + } default: // never reached return 0; @@ -8285,13 +8290,24 @@ templates: break; case 0x2000: case 0x3000: - // set rom bank ctx->mmu.mbc1.rom_bank = val & 0x1F; + + if (ctx->config && ctx->config->on_set_rom_bank) { + // notify callback + ctx->config->on_set_rom_bank(ctx, val & 0x1F); + } + break; case 0x4000: case 0x5000: // set ram_bank/upper_rom_bank bits - ctx->mmu.mbc1.ram_bank = val & 0x3; + ctx->mmu.mbc1.ram_bank = val & 0x03; + + if (ctx->config && ctx->config->on_set_ram_bank) { + // notify callback + ctx->config->on_set_ram_bank(ctx, val & 0x03); + } + break; case 0x6000: case 0x7000: @@ -8393,8 +8409,10 @@ templates: case 0x6000: case 0x7000: // ROM1 (16k, banked) - // FIXME: clamp to rom size? - return ctx->mmu.rom[mbc2_rom_get_addr(ctx, addr)]; + { + const uint32_t addr_ofs = mbc2_rom_get_addr(ctx, addr); + return addr_ofs < ctx->mmu.rom_size ? ctx->mmu.rom[addr_ofs] : 0; + } default: // never reached return 0; @@ -8633,6 +8651,9 @@ templates: } } + // forward reference for mmu_wp() + static void gpu_set_line(gb_t * const ctx, const uint8_t line); + static void mmu_wp( gb_t * const ctx, @@ -8700,8 +8721,9 @@ templates: break; case PORT_LY: - // FIXME: according to URL below writing to this register zeroes it: + // NOTE: according to URL below writing to this register zeroes it: // (src: http://gbdev.gg8.se/wiki/articles/Video_Display) + gpu_set_line(ctx, 0); break; case PORT_LYC: @@ -8782,7 +8804,7 @@ templates: const gb_t * const ctx, const uint16_t addr ) { - switch (ctx->gpu.mode) { + switch (ctx->gpu.mode & 0x3) { case GPU_MODE_VRAM: return 0xFF; default: @@ -8796,7 +8818,7 @@ templates: const gb_t * const ctx, const uint16_t addr ) { - switch (ctx->gpu.mode) { + switch (ctx->gpu.mode & 0x3) { case GPU_MODE_HBLANK: case GPU_MODE_VBLANK: // oam memory (160 bytes): @@ -8932,7 +8954,7 @@ templates: const uint16_t addr, const uint8_t val ) { - switch (ctx->gpu.mode) { + switch (ctx->gpu.mode & 0x3) { case GPU_MODE_HBLANK: case GPU_MODE_VBLANK: case GPU_MODE_OAM: @@ -8951,7 +8973,7 @@ templates: const uint16_t addr, const uint8_t val ) { - switch (ctx->gpu.mode) { + switch (ctx->gpu.mode & 0x3) { case GPU_MODE_HBLANK: case GPU_MODE_VBLANK: // oam memory (160 bytes): @@ -9171,7 +9193,12 @@ templates: const gb_cpu_state_t state ) { ctx->cpu.state = state; + UNUSED(pc); + + if (ctx->config && ctx->config->on_set_cpu_state) { + ctx->config->on_set_cpu_state(ctx, state); + } } static void @@ -9274,13 +9301,12 @@ templates: } static void - add_rb( + add( gb_t * const ctx, - const rb_t reg + const uint8_t n ) { // get old and new value - const uint8_t o = cpu_rb(ctx, RB_A), - n = cpu_rb(ctx, reg); + const uint8_t o = cpu_rb(ctx, RB_A); const uint16_t v = o + n; // write value @@ -9295,44 +9321,26 @@ templates: } static void + add_rb( + gb_t * const ctx, + const rb_t reg + ) { + add(ctx, cpu_rb(ctx, reg)); + } + + static void add_d8( gb_t * const ctx, const uint16_t addr ) { - // get old and new value - const uint8_t o = cpu_rb(ctx, RB_A), - n = mmu_rb(ctx, addr); - const uint16_t v = o + n; - - // write value - cpu_wb(ctx, RB_A, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - ((v & 0xFF) ? 0 : F_Z) | - ((((o & 0x0F) + (n & 0x0F)) & 0xF0) ? F_H : 0) | - ((v & 0x100) ? F_C : 0) - )); + add(ctx, mmu_rb(ctx, addr)); } static void add_hl_ptr( gb_t * const ctx ) { - // get old and new value - const uint8_t o = cpu_rb(ctx, RB_A), - n = mmu_rw(ctx, cpu_rw(ctx, RW_HL)); - const uint16_t v = o + n; - - // write value - cpu_wb(ctx, RB_A, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - ((v & 0xFF) ? 0 : F_Z) | - ((((o & 0x0F) + (n & 0x0F)) & 0xF0) ? F_H : 0) | - ((v & 0x100) ? F_C : 0) - )); + add(ctx, mmu_rb(ctx, cpu_rw(ctx, RW_HL))); } static void @@ -9359,11 +9367,11 @@ templates: static void ld_hl_sp_r8( gb_t * const ctx, - const uint16_t addr + const uint16_t ofs_addr ) { // get src values const uint16_t a = cpu_rw(ctx, RW_SP); - const int8_t b = (int8_t) mmu_rb(ctx, addr); + const int8_t b = (int8_t) mmu_rb(ctx, ofs_addr); const uint16_t v = a + b; // write dst value @@ -9377,13 +9385,12 @@ templates: } static void - adc_rb( + adc( gb_t * const ctx, - const rb_t reg + const uint8_t n ) { - // get old and new value + // get old value and carry flag const uint8_t o = cpu_rb(ctx, RB_A), - n = cpu_rb(ctx, reg), c = FLAG(ctx, C) ? 1 : 0; const uint16_t v = o + n + c; @@ -9399,24 +9406,18 @@ templates: } static void + adc_rb( + gb_t * const ctx, + const rb_t reg + ) { + adc(ctx, cpu_rb(ctx, reg)); + } + + static void adc_hl_ptr( gb_t * const ctx ) { - // get old and new value - const uint8_t o = cpu_rb(ctx, RB_A), - n = cpu_rb(ctx, mmu_rb(ctx, cpu_rw(ctx, RW_HL))), - c = FLAG(ctx, C) ? 1 : 0; - const uint16_t v = o + n + c; - - // write value - cpu_wb(ctx, RB_A, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - ((v & 0xFF) ? 0 : F_Z) | - ((((o & 0x0F) + (n & 0x0F) + c) & 0xF0) ? F_H : 0) | - ((v & 0x100) ? F_C : 0) - )); + adc(ctx, mmu_rb(ctx, cpu_rw(ctx, RW_HL))); } static void @@ -9424,31 +9425,16 @@ templates: gb_t * const ctx, const uint16_t addr ) { - // get old and new value - const uint8_t o = cpu_rb(ctx, RB_A), - n = mmu_rb(ctx, addr), - c = FLAG(ctx, C) ? 1 : 0; - const uint16_t v = o + n + c; - - // write value - cpu_wb(ctx, RB_A, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - ((v & 0xFF) ? 0 : F_Z) | - ((((o & 0x0F) + (n & 0x0F) + c) & 0xF0) ? F_H : 0) | - ((v & 0x100) ? F_C : 0) - )); + adc(ctx, mmu_rb(ctx, addr)); } static void - sub_rb( + sub( gb_t * const ctx, - const rb_t reg + const uint8_t n ) { // get old and new value const uint8_t o = cpu_rb(ctx, RB_A), - n = cpu_rb(ctx, reg), v = o - n; // write value @@ -9464,24 +9450,18 @@ templates: } static void + sub_rb( + gb_t * const ctx, + const rb_t reg + ) { + sub(ctx, cpu_rb(ctx, reg)); + } + + static void sub_hl_ptr( gb_t * const ctx ) { - // get old and new value - const uint8_t o = cpu_rb(ctx, RB_A), - n = mmu_rb(ctx, cpu_rw(ctx, RW_HL)), - v = o - n; - - // write value - cpu_wb(ctx, RB_A, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) | - F_N | - ((((o & 0x0F) - (n & 0x0F)) & 0xF0) ? F_H : 0) | - ((n > o) ? F_C : 0) - )); + sub(ctx, mmu_rb(ctx, cpu_rw(ctx, RW_HL))); } static void @@ -9489,31 +9469,16 @@ templates: gb_t * const ctx, const uint16_t addr ) { - // get old and new value - const uint8_t o = cpu_rb(ctx, RB_A), - n = mmu_rb(ctx, addr), - v = o - n; - - // write value - cpu_wb(ctx, RB_A, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) | - F_N | - ((((o & 0x0F) - (n & 0x0F)) & 0xF0) ? F_H : 0) | - ((n > o) ? F_C : 0) - )); + sub(ctx, mmu_rb(ctx, addr)); } static void - sbc_rb( + sbc( gb_t * const ctx, - const rb_t reg + const uint8_t n ) { // get old and new value const uint8_t o = cpu_rb(ctx, RB_A), - n = cpu_rb(ctx, reg), c = FLAG(ctx, C) ? 1 : 0, v = o - (n + c); @@ -9530,34 +9495,27 @@ templates: } static void + sbc_rb( + gb_t * const ctx, + const rb_t reg + ) { + sbc(ctx, cpu_rb(ctx, reg)); + } + + static void sbc_hl_ptr( gb_t * const ctx ) { - // get old and new value - const uint8_t o = cpu_rb(ctx, RB_A), - n = mmu_rb(ctx, cpu_rw(ctx, RW_HL)), - c = FLAG(ctx, C) ? 1 : 0, - v = o - (n + c); - - // write value - cpu_wb(ctx, RB_A, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) | - F_N | - ((((o & 0x0F) - (n & 0x0F)) & 0xF0) ? F_H : 0) | - (((((uint16_t) n) + c) > o) ? F_C : 0) // FIXME: is this right for sub/sbc? - )); + sbc(ctx, mmu_rb(ctx, cpu_rw(ctx, RW_HL))); } static void - and_rb( + and( gb_t * const ctx, - const rb_t reg + const uint8_t val ) { // get value - const uint8_t v = cpu_rb(ctx, RB_A) & cpu_rb(ctx, reg); + const uint8_t v = cpu_rb(ctx, RB_A) & val; // write value cpu_wb(ctx, RB_A, v); @@ -9570,20 +9528,18 @@ templates: } static void + and_rb( + gb_t * const ctx, + const rb_t reg + ) { + and(ctx, cpu_rb(ctx, reg)); + } + + static void and_hl_ptr( gb_t * const ctx ) { - // get value - const uint8_t v = cpu_rb(ctx, RB_A) & mmu_rb(ctx, cpu_rb(ctx, RW_HL)); - - // write value - cpu_wb(ctx, RB_A, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) | - F_H - )); + and(ctx, mmu_rb(ctx, cpu_rb(ctx, RW_HL))); } static void @@ -9591,8 +9547,16 @@ templates: gb_t * const ctx, const uint16_t addr ) { + and(ctx, mmu_rb(ctx, addr)); + } + + static void + xor( + gb_t * const ctx, + const uint8_t val + ) { // get value - const uint8_t v = cpu_rb(ctx, RB_A) & mmu_rb(ctx, addr); + const uint8_t v = cpu_rb(ctx, RB_A) ^ val; // write value cpu_wb(ctx, RB_A, v); @@ -9608,32 +9572,14 @@ templates: gb_t * const ctx, const rb_t reg ) { - // get value - const uint8_t v = cpu_rb(ctx, RB_A) ^ cpu_rb(ctx, reg); - - // write value - cpu_wb(ctx, RB_A, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) - )); + xor(ctx, cpu_rb(ctx, reg)); } static void xor_hl_ptr( gb_t * const ctx ) { - // get value - const uint8_t v = cpu_rb(ctx, RB_A) ^ mmu_rb(ctx, cpu_rw(ctx, RW_HL)); - - // write value - cpu_wb(ctx, RB_A, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) - )); + xor(ctx, mmu_rb(ctx, cpu_rw(ctx, RW_HL))); } static void @@ -9641,8 +9587,15 @@ templates: gb_t * const ctx, const uint16_t addr ) { - // get value - const uint8_t v = cpu_rb(ctx, RB_A) ^ mmu_rb(ctx, addr); + xor(ctx, mmu_rb(ctx, addr)); + } + + static void + or( + gb_t * const ctx, + const uint8_t val + ) { + const uint8_t v = cpu_rb(ctx, RB_A) | val; // write value cpu_wb(ctx, RB_A, v); @@ -9658,30 +9611,14 @@ templates: gb_t * const ctx, const rb_t reg ) { - const uint8_t v = cpu_rb(ctx, RB_A) | cpu_rb(ctx, reg); - // write value - cpu_wb(ctx, RB_A, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) - )); + or(ctx, cpu_rb(ctx, reg)); } static void or_hl_ptr( gb_t * const ctx ) { - // get value - const uint8_t v = cpu_rb(ctx, RB_A) | mmu_rb(ctx, cpu_rw(ctx, RW_HL)); - - // write value - cpu_wb(ctx, RB_A, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) - )); + or(ctx, mmu_rb(ctx, cpu_rw(ctx, RW_HL))); } static void @@ -9689,26 +9626,16 @@ templates: gb_t * const ctx, const uint16_t addr ) { - // get value - const uint8_t v = cpu_rb(ctx, RB_A) | mmu_rb(ctx, addr); - - // write value - cpu_wb(ctx, RB_A, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) - )); + or(ctx, mmu_rb(ctx, addr)); } static void - cp_rb( + cp( gb_t * const ctx, - const rb_t reg + const uint8_t n ) { // get old and new value const uint8_t o = cpu_rb(ctx, RB_A), - n = cpu_rb(ctx, reg), v = o - n; // set flags @@ -9721,21 +9648,18 @@ templates: } static void + cp_rb( + gb_t * const ctx, + const rb_t reg + ) { + cp(ctx, cpu_rb(ctx, reg)); + } + + static void cp_hl_ptr( gb_t * const ctx ) { - // get old and new value - const uint8_t o = cpu_rb(ctx, RB_A), - n = mmu_rb(ctx, cpu_rw(ctx, RW_HL)), - v = o - n; - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) | - F_N | - ((((o & 0x0F) - (n & 0x0F)) & 0xF0) ? F_H : 0) | - ((n > o) ? F_C : 0) - )); + cp(ctx, mmu_rb(ctx, cpu_rw(ctx, RW_HL))); } static void @@ -9743,18 +9667,7 @@ templates: gb_t * const ctx, const uint16_t addr ) { - // get old and new value - const uint8_t o = cpu_rb(ctx, RB_A), - n = mmu_rb(ctx, addr), - v = o - n; - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) | - F_N | - ((((o & 0x0F) - (n & 0x0F)) & 0xF0) ? F_H : 0) | - ((n > o) ? F_C : 0) - )); + cp(ctx, mmu_rb(ctx, addr)); } static void @@ -9787,42 +9700,56 @@ templates: cpu_ww(ctx, RW_SP, sp + 2); } - static void - rlc_rb( + static uint8_t + rlc( gb_t * const ctx, - const rb_t reg + const uint8_t o ) { - // get old value, carry bit, and new value - const uint8_t o = cpu_rb(ctx, reg), - c = (o & 0x80) ? 1 : 0, + // get carry bit and new value + const uint8_t c = (o & 0x80) ? 1 : 0, n = ((o & 0x7F) << 1) + c; - // set value - cpu_wb(ctx, reg, n); - // set flags cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( (n ? 0 : F_Z) | (c ? F_C : 0) )); + + // return new value + return n; + } + + static void + rlc_rb( + gb_t * const ctx, + const rb_t reg + ) { + cpu_wb(ctx, reg, rlc(ctx, cpu_rb(ctx, reg))); } static void rlc_hl( gb_t * const ctx ) { - // get old value, carry bit, and new value const uint16_t addr = cpu_rw(ctx, RW_HL); - const uint8_t o = mmu_rb(ctx, addr), - c = (o & 0x80) ? 1 : 0, - n = ((o & 0x7F) << 1) + c; - // set value - mmu_wb(ctx, addr, n); + mmu_wb(ctx, addr, rlc(ctx, mmu_rb(ctx, addr))); + } + static uint8_t + rrc( + gb_t * const ctx, + const uint8_t o + ) { + // get carry bit and new value + const uint8_t c = (o & 0x01) ? 1 : 0, + n = (o >> 1) | (c ? 0x80 : 0); // set flags cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( (n ? 0 : F_Z) | (c ? F_C : 0) )); + + // return new value + return n; } static void @@ -9830,37 +9757,32 @@ templates: gb_t * const ctx, const rb_t reg ) { - // get old value, carry bit, and new value - const uint8_t o = cpu_rb(ctx, reg), - c = (o & 0x01) ? 1 : 0, - n = (o >> 1) | (c ? 0x80 : 0); - // set value - cpu_wb(ctx, reg, n); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (n ? 0 : F_Z) | - (c ? F_C : 0) - )); + cpu_wb(ctx, reg, rrc(ctx, cpu_rb(ctx, reg))); } static void rrc_hl_ptr( gb_t * const ctx ) { - // get old value, carry bit, and new value const uint16_t addr = cpu_rw(ctx, RW_HL); - const uint8_t o = mmu_rb(ctx, addr), - c = (o & 0x01) ? 1 : 0, - n = (o >> 1) | (c ? 0x80 : 0); - // set value - mmu_wb(ctx, addr, n); + mmu_wb(ctx, addr, rrc(ctx, mmu_rb(ctx, addr))); + } + + static uint8_t + rl( + gb_t * const ctx, + const uint8_t o + ) { + const uint8_t n = (o << 1) + (FLAG(ctx, C) ? 1 : 0); // set flags cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( (n ? 0 : F_Z) | - (c ? F_C : 0) + ((o & 0x80) ? F_C : 0) )); + + // return result + return n; } static void @@ -9868,35 +9790,32 @@ templates: gb_t * const ctx, const rb_t reg ) { - // get old value and new value - const uint8_t o = cpu_rb(ctx, reg), - n = (o << 1) + (FLAG(ctx, C) ? 1 : 0); - // set value - cpu_wb(ctx, reg, n); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (n ? 0 : F_Z) | - ((o & 0x80) ? F_C : 0) - )); + cpu_wb(ctx, reg, rl(ctx, cpu_rb(ctx, reg))); } static void rl_hl_ptr( gb_t * const ctx ) { - // get old value and new value const uint16_t addr = cpu_rw(ctx, RW_HL); - const uint8_t o = mmu_rb(ctx, addr), - n = (o << 1) + (FLAG(ctx, C) ? 1 : 0); - // set value - mmu_wb(ctx, addr, n); + mmu_wb(ctx, addr, rl(ctx, mmu_rb(ctx, addr))); + } + + static uint8_t + rr( + gb_t * const ctx, + const uint8_t o + ) { + const uint8_t n = (FLAG(ctx, C) ? 0x80 : 0) | (o >> 1); // set flags cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( (n ? 0 : F_Z) | - ((o & 0x80) ? F_C : 0) + ((o & 0x01) ? F_C : 0) )); + + // return result + return n; } static void @@ -9904,35 +9823,32 @@ templates: gb_t * const ctx, const rb_t reg ) { - // get old value and new value - const uint8_t o = cpu_rb(ctx, reg), - n = (FLAG(ctx, C) ? 0x80 : 0) | (o >> 1); - // set value - cpu_wb(ctx, reg, n); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (n ? 0 : F_Z) | - ((o & 0x01) ? F_C : 0) - )); + cpu_wb(ctx, reg, rr(ctx, cpu_rb(ctx, reg))); } static void rr_hl_ptr( gb_t * const ctx ) { - // get old value and new value const uint16_t addr = cpu_rw(ctx, RW_HL); - const uint8_t o = mmu_rb(ctx, addr), - n = (FLAG(ctx, C) ? 0x80 : 0) | (o >> 1); - // set value - mmu_wb(ctx, addr, n); + mmu_wb(ctx, addr, rr(ctx, mmu_rb(ctx, addr))); + } + + static uint8_t + sla( + gb_t * const ctx, + const uint8_t o + ) { + const uint8_t v = o << 1; // set flags cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (n ? 0 : F_Z) | - ((o & 0x01) ? F_C : 0) + (v ? 0 : F_Z) | + ((o & 0x80) ? F_C : 0) )); + + // return value + return v; } static void @@ -9940,17 +9856,7 @@ templates: gb_t * const ctx, const rb_t reg ) { - const uint8_t o = cpu_rb(ctx, reg), - v = o << 1; - - // set value - cpu_wb(ctx, reg, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) | - ((o & 0x80) ? F_C : 0) - )); + cpu_wb(ctx, reg, sla(ctx, cpu_rb(ctx, reg))); } static void @@ -9958,17 +9864,24 @@ templates: gb_t * const ctx ) { const uint16_t addr = cpu_rw(ctx, RW_HL); - const uint8_t o = mmu_rb(ctx, addr), - v = o << 1; + mmu_wb(ctx, addr, sla(ctx, mmu_rb(ctx, addr))); + } - // set value - mmu_wb(ctx, addr, v); + static uint8_t + sra( + gb_t * const ctx, + const uint8_t o + ) { + const uint8_t v = ((o & 0x80) ? 0x80 : 0) | (o >> 1); // set flags cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( (v ? 0 : F_Z) | - ((o & 0x80) ? F_C : 0) + ((o & 0x01) ? F_C : 0) )); + + // return result + return v; } static void @@ -9976,17 +9889,7 @@ templates: gb_t * const ctx, const rb_t reg ) { - const uint8_t o = cpu_rb(ctx, reg), - v = ((o & 0x80) ? 0x80 : 0) | (o >> 1); - - // set value - cpu_wb(ctx, reg, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) | - ((o & 0x01) ? F_C : 0) - )); + cpu_wb(ctx, reg, sra(ctx, cpu_rb(ctx, reg))); } static void @@ -9994,17 +9897,24 @@ templates: gb_t * const ctx ) { const uint16_t addr = cpu_rw(ctx, RW_HL); - const uint8_t o = mmu_rb(ctx, addr), - v = ((o & 0x80) ? 0x80 : 0) | (o >> 1); + mmu_wb(ctx, addr, sra(ctx, mmu_rb(ctx, addr))); + } - // set value - mmu_wb(ctx, addr, v); + static uint8_t + srl( + gb_t * const ctx, + const uint8_t o + ) { + const uint8_t v = (o >> 1); // set flags cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( (v ? 0 : F_Z) | ((o & 0x01) ? F_C : 0) )); + + // return result + return v; } static void @@ -10012,17 +9922,7 @@ templates: gb_t * const ctx, const rb_t reg ) { - const uint8_t o = cpu_rb(ctx, reg), - v = (o >> 1); - - // set value - cpu_wb(ctx, reg, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) | - ((o & 0x01) ? F_C : 0) - )); + cpu_wb(ctx, reg, srl(ctx, cpu_rb(ctx, reg))); } static void @@ -10030,17 +9930,7 @@ templates: gb_t * const ctx ) { const uint16_t addr = cpu_rw(ctx, RW_HL); - const uint8_t o = mmu_rb(ctx, addr), - v = (o >> 1); - - // set value - mmu_wb(ctx, addr, v); - - // set flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) | - ((o & 0x01) ? F_C : 0) - )); + mmu_wb(ctx, addr, srl(ctx, mmu_rb(ctx, addr))); } static void @@ -10121,21 +10011,25 @@ templates: mmu_wb(ctx, addr, mmu_rb(ctx, addr) | ~(bit ? (1 << bit) : 1)); } - static void - swap_rb( + static uint8_t + swap( gb_t * const ctx, - const rb_t reg + const uint8_t v ) { - // get old value - const uint8_t v = cpu_rb(ctx, reg); - - // write value - cpu_wb(ctx, reg, ((v & (0x0F)) << 4) | ((v & 0xF0) >> 4)); - // write flags cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( (v ? 0 : F_Z) )); + + return ((v & (0x0F)) << 4) | ((v & 0xF0) >> 4); + } + + static void + swap_rb( + gb_t * const ctx, + const rb_t reg + ) { + cpu_wb(ctx, reg, swap(ctx, cpu_rb(ctx, reg))); } static void @@ -10143,15 +10037,7 @@ templates: gb_t * const ctx ) { const uint16_t addr = cpu_rw(ctx, RW_HL); - const uint8_t v = mmu_rb(ctx, addr); - - // write value - mmu_wb(ctx, addr, ((v & (0x0F)) << 4) | ((v & 0xF0) >> 4)); - - // write flags - cpu_wf(ctx, F_Z | F_N | F_H | F_C, ( - (v ? 0 : F_Z) - )); + mmu_wb(ctx, addr, swap(ctx, mmu_rb(ctx, addr))); } static void @@ -10203,6 +10089,13 @@ templates: } } + static uint8_t + gpu_get_mode( + const gb_t * const ctx + ) { + return ctx->gpu.mode & 0x3; + } + static void gpu_set_mode( gb_t * const ctx, @@ -10210,18 +10103,29 @@ templates: ) { ctx->gpu.mode = mode; - switch (mode) { + if (ctx->config && ctx->config->on_gpu_set_mode) { + ctx->config->on_gpu_set_mode(ctx, mode); + } + + switch (gpu_get_mode(ctx)) { case GPU_MODE_OAM: if (ctx->gpu.stat & (1 << 5)) { - // vblank interrupt is enabled, trigger LCDC interrupt + // lcd status interrupt is enabled, trigger LCDC interrupt mmu_wb(ctx, PORT_IF, mmu_rb(ctx, PORT_IF) & (1 << 1)); } break; + case GPU_MODE_VRAM: + // do nothing + break; case GPU_MODE_HBLANK: if (ctx->gpu.stat & (1 << 3)) { - // hblank interrupt is enabled, trigger LCDC interrupt + // lcd status interrupt is enabled, trigger LCDC interrupt mmu_wb(ctx, PORT_IF, mmu_rb(ctx, PORT_IF) & (1 << 1)); + + if (ctx->config && ctx->config->on_hblank) { + ctx->config->on_hblank(ctx); + } } break; @@ -10229,12 +10133,13 @@ templates: if (ctx->gpu.stat & (1 << 4)) { // vblank interrupt is enabled, trigger vblank interrupt mmu_wb(ctx, PORT_IF, mmu_rb(ctx, PORT_IF) & 1); + + if (ctx->config && ctx->config->on_vblank) { + ctx->config->on_vblank(ctx); + } } break; - default: - // do nothing - break; } } @@ -10332,19 +10237,21 @@ templates: // increment clock ctx->gpu.clock += clock; - switch (ctx->gpu.mode) { + if (ctx->config && ctx->config->on_gpu_step) { + ctx->config->on_gpu_step(ctx); + } + + switch (gpu_get_mode(ctx)) { case GPU_MODE_OAM: - if (ctx->gpu.clock > 80) { + if (ctx->gpu.clock > 79) { // clear clock, set mode ctx->gpu.clock = 0; - ctx->gpu.mode = GPU_MODE_VRAM; + gpu_set_mode(ctx, GPU_MODE_VRAM); } break; case GPU_MODE_VRAM: - if (ctx->gpu.clock > 172) { - // TODO: write scanline - + if (ctx->gpu.clock > 171) { // clear clock, set mode ctx->gpu.clock = 0; gpu_set_mode(ctx, GPU_MODE_HBLANK); @@ -10352,13 +10259,13 @@ templates: break; case GPU_MODE_HBLANK: - if (ctx->gpu.clock > 204) { + if (ctx->gpu.clock > 203) { // clear clock, draw line, increment line ctx->gpu.clock = 0; gpu_draw(ctx); gpu_set_line(ctx, ctx->gpu.line + 1); - if (ctx->gpu.line < 143) { + if (ctx->gpu.line < 144) { // set mode gpu_set_mode(ctx, GPU_MODE_OAM); } else { @@ -10386,9 +10293,6 @@ templates: } break; - default: - /* do nothing, unknown gpu mode */ - break; } } @@ -10412,6 +10316,11 @@ templates: // trigger timer interrupt mmu_wb(ctx, PORT_IF, mmu_rb(ctx, PORT_IF) & (1 << 2)); + + if (ctx->config && ctx->config->on_timer) { + // notify callback + ctx->config->on_timer(ctx); + } } } } @@ -10450,8 +10359,8 @@ templates: // return 4 clock step return 4; } else { - // return 4 clock step - return 0; + // return 0 clock step + return 1; } } @@ -10461,7 +10370,7 @@ templates: ) { if (ctx->cpu.state == GB_CPU_STATE_RUN) { if (ctx->cpu.ime) { - // get interrupt vector (masked against enabled interrupts) + // get interrupt flags, masked against enabled interrupts const uint8_t iv = mmu_rb(ctx, PORT_IE) & mmu_rb(ctx, PORT_IF); bool done = false; @@ -10546,6 +10455,59 @@ templates: gpu_step(ctx, clock); } + static const struct { + const char * const name; + const uint8_t len; + } OPS[] = { + <%= ops %> + }; + + bool + gb_disasm( + gb_t * const ctx, + char * const dst_buf, + size_t * const dst_len + ) { + if (!dst_buf || !dst_len) { + return false; + } + + const uint8_t op = mmu_rb(ctx, cpu_rw(ctx, RW_PC)); + const uint16_t ofs = (op == 0xCB) ? (255 + mmu_rb(ctx, cpu_rw(ctx, RW_PC) + 1)) : op; + int len = -1; + + switch (OPS[ofs].len) { + case 1: + len = snprintf(dst_buf, *dst_len, "%s; %02x", + OPS[ofs].name, + op + ); + + break; + case 2: + len = snprintf(dst_buf, *dst_len, "%s; %02x %02x", + OPS[ofs].name, + op, + mmu_rb(ctx, cpu_rw(ctx, RW_PC) + 1) + ); + + break; + case 3: + len = snprintf(dst_buf, *dst_len, "%s; %02x %02x %02x", + OPS[ofs].name, + op, + mmu_rb(ctx, cpu_rw(ctx, RW_PC) + 1), + mmu_rb(ctx, cpu_rw(ctx, RW_PC) + 2) + ); + + break; + default: + len = snprintf(dst_buf, *dst_len, "%s", OPS[ofs].name); + } + + return (len > 0) && (len < (int) *dst_len); + } + void gb_set_buttons( gb_t * const ctx, @@ -10584,9 +10546,6 @@ templates: cpu_init( gb_t * const ctx ) { - // set cpu state - cpu_set_state(ctx, 0, GB_CPU_STATE_RUN); - // init cpu registers // (src: TCAGBD.pdf, 3.2) // TODO: this varies by model (DGB/CGB/SGB/GBA/etc) @@ -10596,16 +10555,23 @@ templates: ctx->cpu.rs[RW_HL] = 0x014D; ctx->cpu.rs[RW_SP] = 0xFFFE; ctx->cpu.rs[RW_PC] = 0x0100; + + // set cpu state + cpu_set_state(ctx, 0, GB_CPU_STATE_RUN); } void gb_init( gb_t * const ctx, - const uint8_t * const rom, + const gb_config_t * const config, + const uint8_t *rom, const uint32_t rom_size ) { // clear context memset(ctx, 0, sizeof(gb_t)); + // save config + ctx->config = config; + // init cpu cpu_init(ctx); @@ -10614,6 +10580,6 @@ templates: ctx->mmu.rom_size = rom_size; // init gpu - ctx->gpu.mode = GPU_MODE_OAM; - ctx->gpu.line = 0; + gpu_set_mode(ctx, GPU_MODE_OAM); + gpu_set_line(ctx, 0); } @@ -11,7 +11,8 @@ #define NUM_FRAMES 600 // #define SKIP_STEPS 1000000 -#define NUM_STEPS 200000 +#define NUM_STEPS 300000 +#define UNUSED(a) ((void) (a)) static uint32_t file_size( @@ -63,6 +64,81 @@ load( static uint8_t frames[NUM_FRAMES * GB_FRAME_SIZE]; static void +on_set_rom_bank( + const gb_t * const ctx, + const uint16_t bank +) { + printf("set rom bank: bank = %u, PC = %04x\n", bank, ctx->cpu.rs[5]); +} + +static void +on_set_ram_bank( + const gb_t * const ctx, + const uint16_t bank +) { + printf("set ram bank: bank = %u, PC = %04x\n", bank, ctx->cpu.rs[5]); +} + +static void +on_vblank( + const gb_t * const ctx +) { + printf("vblank: PC = %04x\n", ctx->cpu.rs[5]); +} + +static void +on_hblank( + const gb_t * const ctx +) { + printf("hblank: PC = %04x\n", ctx->cpu.rs[5]); +} + +static void +on_timer( + const gb_t * const ctx +) { + printf("timer: PC = %04x\n", ctx->cpu.rs[5]); +} + +/* + * static void + * on_gpu_step( + * const gb_t * const ctx + * ) { + * printf("gpu step: gpu clock = 0x%04x\n", ctx->gpu.clock); + * } + */ + +static void +on_gpu_set_mode( + const gb_t * const ctx, + const uint8_t mode +) { + UNUSED(ctx); + printf("gpu mode: %u, line = %u\n", mode, ctx->gpu.line); +} + +static void +on_set_cpu_state( + const gb_t * const ctx, + const gb_cpu_state_t state +) { + printf("cpu state: PC = %04x, state = %d\n", ctx->cpu.rs[5], state); +} + +static const gb_config_t +EXECUTE_CONFIG = { + .on_set_rom_bank = on_set_rom_bank, + .on_set_ram_bank = on_set_ram_bank, + .on_vblank = on_vblank, + .on_hblank = on_hblank, + .on_timer = on_timer, + // .on_gpu_step = on_gpu_step, + .on_gpu_set_mode = on_gpu_set_mode, + .on_set_cpu_state = on_set_cpu_state, +}; + +static void test_render_frames( const char * const png_path, const char * const rom_path @@ -74,7 +150,7 @@ test_render_frames( // init context gb_t ctx; - gb_init(&ctx, rom_data, rom_size); + gb_init(&ctx, NULL, rom_data, rom_size); fprintf(stderr, "gb context initialized\n"); // render frames @@ -109,12 +185,19 @@ const char * const RWS[] = { static void gb_dump_context( - const gb_t * const ctx + gb_t * const ctx ) { for (int i = 0; RWS[i]; i++) { - printf("%s%s: 0x%04X", (!i) ? "" : ", ", RWS[i], ctx->cpu.rs[i]); + printf("%s:%04X ", RWS[i], ctx->cpu.rs[i]); + } + + char buf[128]; + size_t buf_len = sizeof(buf); + if (gb_disasm(ctx, buf, &buf_len)) { + printf("%s\n", buf); + } else { + printf("\n"); } - printf("\n"); } static void @@ -128,7 +211,7 @@ test_execute_steps( // init context gb_t ctx; - gb_init(&ctx, rom_data, rom_size); + gb_init(&ctx, &EXECUTE_CONFIG, rom_data, rom_size); fprintf(stderr, "gb context initialized\n"); #ifdef SKIP_STEPS |