From bc980ec1e52548c22faf9d23483c584e35bcdd39 Mon Sep 17 00:00:00 2001 From: Matthew Laux Date: Mon, 25 Jul 2022 22:55:13 -0500 Subject: [PATCH] add more instructions, fix INC/DEC [HL], first attempt at lcd "mode" timing --- cli/imgui_example.cpp | 25 ++++++++++++++----------- src/cpu.c | 32 ++++++++++++++++++++++++++++++-- src/dmg.c | 41 +++++++++++++++++++++++++++++++++-------- src/lcd.c | 12 ++++++++++++ src/lcd.h | 7 +++++++ 5 files changed, 96 insertions(+), 21 deletions(-) diff --git a/cli/imgui_example.cpp b/cli/imgui_example.cpp index a2da6e0..5bd25ff 100644 --- a/cli/imgui_example.cpp +++ b/cli/imgui_example.cpp @@ -27,7 +27,6 @@ static const char *PC_FORMAT = "PC: 0x%02x"; unsigned char output_image[256 * 256 * 4]; unsigned char vram_tiles[256 * 96 * 4]; -unsigned char full_address_space[0x10000]; struct key_input { int scancode; @@ -60,13 +59,15 @@ GLuint make_output_texture() { return image_texture; } +unsigned char default_palette[] = { 0, 0x55, 0xaa, 0xff }; + void convert_output(struct lcd *lcd) { int x, y; int out_index = 0; for (y = 0; y < 256; y++) { for (x = 0; x < 256; x++) { int val = lcd->buf[y * 256 + x]; - int fill = 255 - val * 85; + int fill = default_palette[val]; //int fill = val ? 255 : 0; output_image[out_index++] = fill; output_image[out_index++] = fill; @@ -103,12 +104,14 @@ void convert_vram(struct dmg *dmg) { } } -void fill_memory_editor(struct dmg *dmg) +static ImU8 read_mem(const ImU8* data, size_t off) { - int k; - for (k = 0; k < 0x10000; k++) { - full_address_space[k] = dmg_read(dmg, k); - } + return dmg_read((void *) data, (u16) off); +} + +static void write_mem(ImU8 *data, size_t off, ImU8 d) +{ + dmg_write(data, (u16) off, d); } // Main code @@ -193,6 +196,9 @@ int main(int argc, char *argv[]) GLuint texture = make_output_texture(); GLuint vram_texture = make_output_texture(); + editor.ReadFn = read_mem; + editor.WriteFn = write_mem; + // for flag checkboxes bool z_flag = false; bool n_flag = false; @@ -318,10 +324,7 @@ int main(int argc, char *argv[]) ImGui::End(); } - fill_memory_editor(&dmg); - - editor.DrawWindow("Memory", full_address_space, 0x10000, 0x0000); - editor.DrawWindow("ROM", dmg.rom->data, 0x8000, 0); + editor.DrawWindow("Memory", &dmg, 0x10000, 0x0000); // Rendering ImGui::Render(); diff --git a/src/cpu.c b/src/cpu.c index 5b37d80..ba3ce61 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -515,8 +515,8 @@ void cpu_step(struct cpu *cpu) case 0x2d: dec_with_carry(cpu, &cpu->l); break; case 0x33: cpu->sp++; break; - case 0x34: temp = read8(cpu, read_hl(cpu)); inc_with_carry(cpu, &temp); break; - case 0x35: temp = read8(cpu, read_hl(cpu)); dec_with_carry(cpu, &temp); break; + case 0x34: temp = read8(cpu, read_hl(cpu)); inc_with_carry(cpu, &temp); write8(cpu, read_hl(cpu), temp); break; + case 0x35: temp = read8(cpu, read_hl(cpu)); dec_with_carry(cpu, &temp); write8(cpu, read_hl(cpu), temp); break; case 0x3b: cpu->sp--; break; case 0x3c: inc_with_carry(cpu, &cpu->a); break; @@ -684,6 +684,20 @@ void cpu_step(struct cpu *cpu) case 0xc3: // JP a16 cpu->pc = read16(cpu, cpu->pc); break; + case 0xc4: // CALL NZ, u16 + temp16 = read16(cpu, cpu->pc); + cpu->pc += 2; + if (!flag_isset(cpu, FLAG_ZERO)) { + push(cpu, cpu->pc); + cpu->pc = temp16; + } + break; + case 0xd0: // RET NC + if (!flag_isset(cpu, FLAG_CARRY)) { + cpu->pc = pop(cpu); + cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles; + } + break; case 0xd2: // JP NC,a16 temp16 = read16(cpu, cpu->pc); cpu->pc += 2; @@ -692,6 +706,12 @@ void cpu_step(struct cpu *cpu) cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles; } break; + case 0xd8: // RET C + if (flag_isset(cpu, FLAG_CARRY)) { + cpu->pc = pop(cpu); + cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles; + } + break; case 0x80: add(cpu, cpu->b, 0); break; case 0x81: add(cpu, cpu->c, 0); break; @@ -736,6 +756,11 @@ void cpu_step(struct cpu *cpu) case 0x9e: subtract(cpu, read8(cpu, read_hl(cpu)), 1, 0); break; case 0x9f: subtract(cpu, cpu->a, 1, 0); break; + case 0xd6: // SUB A, u8 + subtract(cpu, read8(cpu, cpu->pc), 0, 0); + cpu->pc++; + break; + // AND case 0xa0: and(cpu, cpu->b); break; case 0xa1: and(cpu, cpu->c); break; @@ -793,6 +818,9 @@ void cpu_step(struct cpu *cpu) case 0xf7: push(cpu, cpu->pc); cpu->pc = 0x30; break; case 0xff: push(cpu, cpu->pc); cpu->pc = 0x38; break; + case 0x27: // DAA + break; + case 0x76: // HALT break; diff --git a/src/dmg.c b/src/dmg.c index b7f6bde..bd975ad 100644 --- a/src/dmg.c +++ b/src/dmg.c @@ -86,7 +86,7 @@ u8 dmg_read(void *_dmg, u16 address) // not sure about any of this yet // commented out bc of memory view window // fprintf(stderr, "don't know how to read 0x%04x\n", address); - return 0; + return 0xff; } } @@ -119,8 +119,8 @@ void dmg_write(void *_dmg, u16 address, u8 data) } else if (address >= 0xff80 && address <= 0xfffe) { dmg->zero_page[address - 0xff80] = data; } else if (address == 0xff00) { - dmg->joypad_selected = data & (1 << 4); - dmg->action_selected = data & (1 << 5); + dmg->joypad_selected = !(data & (1 << 4)); + dmg->action_selected = !(data & (1 << 5)); } else if (address == 0xff0f) { dmg->interrupt_requested = data; } else if (address == 0xffff) { @@ -144,22 +144,33 @@ void dmg_step(void *_dmg) cpu_step(dmg->cpu); // each line takes 456 cycles - if (dmg->cpu->cycle_count - dmg->last_lcd_update >= 456) { + + int cycle_diff = dmg->cpu->cycle_count - dmg->last_lcd_update; + + if (cycle_diff >= 456) { dmg->last_lcd_update = dmg->cpu->cycle_count; int next_scanline = lcd_step(dmg->lcd); // update LYC if (next_scanline == lcd_read(dmg->lcd, REG_LYC)) { lcd_set_bit(dmg->lcd, REG_STAT, STAT_FLAG_MATCH); - dmg_request_interrupt(dmg, INT_LCDSTAT); + if (lcd_isset(dmg->lcd, REG_STAT, STAT_INTR_SOURCE_MATCH)) { + dmg_request_interrupt(dmg, INT_LCDSTAT); + } } else { lcd_clear_bit(dmg->lcd, REG_STAT, STAT_FLAG_MATCH); } + if (next_scanline >= 144 && next_scanline < 154) { + lcd_set_mode(dmg->lcd, 1); + } + if (next_scanline == 144) { // vblank has started, draw all the stuff from ram into the lcd dmg_request_interrupt(dmg, INT_VBLANK); - dmg_request_interrupt(dmg, INT_SERIAL); + if (lcd_isset(dmg->lcd, REG_STAT, STAT_INTR_SOURCE_VBLANK)) { + dmg_request_interrupt(dmg, INT_LCDSTAT); + } int lcdc = lcd_read(dmg->lcd, REG_LCDC); int bg_base = (lcdc & LCDC_BG_TILE_MAP) ? 0x9c00 : 0x9800; @@ -186,8 +197,8 @@ void dmg_step(void *_dmg) int data1 = dmg_read(dmg, eff_addr + b); int data2 = dmg_read(dmg, eff_addr + b + 1); for (i = 7; i >= 0; i--) { - dmg->lcd->buf[off] = ((data1 & (1 << i)) ? 1 : 0);// << 1; - //dmg->lcd->buf[off] |= (data1 & (1 << i)) ? 1 : 0; + dmg->lcd->buf[off] = ((data1 & (1 << i)) ? 1 : 0) << 1; + dmg->lcd->buf[off] |= (data2 & (1 << i)) ? 1 : 0; off++; } off += 248; @@ -199,5 +210,19 @@ void dmg_step(void *_dmg) lcd_copy(dmg->lcd); lcd_draw(dmg->lcd); } + } else { + int scan = lcd_read(dmg->lcd, REG_LY); + if (scan < 144) { + if (cycle_diff < 80) { + lcd_set_mode(dmg->lcd, 2); + } else if (cycle_diff < 230) { + // just midpoint between 168 to 291, todo improve + lcd_set_mode(dmg->lcd, 3); + } else { + lcd_set_mode(dmg->lcd, 0); + } + } else { + // in vblank. mode should stay as 1 + } } } \ No newline at end of file diff --git a/src/lcd.c b/src/lcd.c index c2a7a1d..3e09440 100644 --- a/src/lcd.c +++ b/src/lcd.c @@ -15,6 +15,18 @@ void lcd_clear_bit(struct lcd *lcd, u16 addr, u8 bit) lcd_write(lcd, addr, lcd_read(lcd, addr) & ~(1 << bit)); } +int lcd_isset(struct lcd *lcd, u16 addr, u8 bit) +{ + u8 val = lcd_read(lcd, addr); + return val & (1 << bit); +} + +void lcd_set_mode(struct lcd *lcd, int mode) +{ + u8 val = lcd_read(lcd, REG_STAT); + lcd_write(lcd, REG_STAT, (val & 0xfc) | mode); +} + void lcd_new(struct lcd *lcd) { lcd->buf = malloc(256 * 256); diff --git a/src/lcd.h b/src/lcd.h index cb9e9b4..98bde4b 100644 --- a/src/lcd.h +++ b/src/lcd.h @@ -24,6 +24,11 @@ #define REG_LCD_LAST REG_WX #define STAT_FLAG_MATCH 2 +#define STAT_INTR_SOURCE_HBLANK 3 +#define STAT_INTR_SOURCE_VBLANK 4 +#define STAT_INTR_SOURCE_MODE2 5 +#define STAT_INTR_SOURCE_MATCH 6 + #define LCDC_ENABLE_BG (1 << 0) #define LCDC_ENABLE_OBJ (1 << 1) @@ -50,6 +55,8 @@ void lcd_put_pixel(struct lcd *lcd, u8 x, u8 y, u8 value); void lcd_set_bit(struct lcd *lcd, u16 addr, u8 bit); void lcd_clear_bit(struct lcd *lcd, u16 addr, u8 bit); +int lcd_isset(struct lcd *lcd, u16 addr, u8 bit); +void lcd_set_mode(struct lcd *lcd, int mode); // i feel like i'm going to need to call this every cycle and update regs int lcd_step(struct lcd *lcd);