diff --git a/cli/imgui_example.cpp b/cli/imgui_example.cpp index 0e3c721..e76cc3d 100644 --- a/cli/imgui_example.cpp +++ b/cli/imgui_example.cpp @@ -103,6 +103,54 @@ void fill_memory_editor(struct dmg *dmg) } } +struct key_input { + int scancode; + int button; + int field; +}; + +struct key_input key_inputs[] = { + { SDL_SCANCODE_D, BUTTON_RIGHT, FIELD_JOY }, + { SDL_SCANCODE_A, BUTTON_LEFT, FIELD_JOY }, + { SDL_SCANCODE_W, BUTTON_UP, FIELD_JOY }, + { SDL_SCANCODE_S, BUTTON_DOWN, FIELD_JOY }, + { SDL_SCANCODE_L, BUTTON_A, FIELD_ACTION }, + { SDL_SCANCODE_K, BUTTON_B, FIELD_ACTION }, + { SDL_SCANCODE_N, BUTTON_SELECT, FIELD_ACTION }, + { SDL_SCANCODE_M, BUTTON_START, FIELD_ACTION }, + { 0 } +}; +int scancode_to_joy[] = { + SDL_SCANCODE_D, + SDL_SCANCODE_A, + SDL_SCANCODE_W, + SDL_SCANCODE_S, + SDL_SCANCODE_L, + SDL_SCANCODE_K, + SDL_SCANCODE_N, + SDL_SCANCODE_M +}; +int scancode_buttons[] = { + BUTTON_RIGHT, + BUTTON_LEFT, + BUTTON_UP, + BUTTON_DOWN, + BUTTON_A, + BUTTON_B, + BUTTON_SELECT, + BUTTON_START, +}; +int scancode_fields[] = { + FIELD_JOY, + FIELD_JOY, + FIELD_JOY, + FIELD_JOY, + FIELD_ACTION, + FIELD_ACTION, + FIELD_ACTION, + FIELD_ACTION, +}; + // Main code int main(int argc, char *argv[]) { @@ -112,6 +160,8 @@ int main(int argc, char *argv[]) struct lcd lcd = { 0 }; int executed; + int paused = 0; + int pause_next = 0; if (argc < 2) { printf("no rom specified\n"); @@ -206,7 +256,12 @@ int main(int argc, char *argv[]) unsigned int lastDrawTime = 0, currentTime; while (!done) { - dmg_step(&dmg); + if (!paused) { + dmg_step(&dmg); + } + if (pause_next) { + paused = 1; + } currentTime = SDL_GetTicks(); if (currentTime >= lastDrawTime + 16) { @@ -219,10 +274,25 @@ int main(int argc, char *argv[]) while (SDL_PollEvent(&event)) { ImGui_ImplSDL2_ProcessEvent(&event); - if (event.type == SDL_QUIT) + if (event.type == SDL_QUIT) { done = true; - if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) + } + + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE + && event.window.windowID == SDL_GetWindowID(window)) { done = true; + } + + if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) { + struct key_input *key = key_inputs; + while (key->scancode) { + if (key->scancode == event.key.keysym.scancode) { + dmg_set_button(&dmg, key->field, key->button, event.type == SDL_KEYDOWN); + break; + } + key++; + } + } } // Start the Dear ImGui frame @@ -261,6 +331,20 @@ int main(int argc, char *argv[]) ImGui::SameLine(); ImGui::Checkbox("C", &c_flag); + if (ImGui::Button("Run")) { + paused = 0; + pause_next = 0; + } + ImGui::SameLine(); + if (ImGui::Button("Stop")) { + paused = 1; + } + ImGui::SameLine(); + if (ImGui::Button("Step")) { + paused = 0; + pause_next = 1; + } + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); ImGui::End(); } @@ -271,7 +355,7 @@ int main(int argc, char *argv[]) convert_output(dmg.lcd); glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, output_image); - ImGui::Image((void*)(intptr_t) texture, ImVec2(256, 256)); + ImGui::Image((void*)(intptr_t) texture, ImVec2(512, 512)); ImGui::End(); } @@ -290,6 +374,7 @@ int main(int argc, char *argv[]) fill_memory_editor(&dmg); editor.DrawWindow("Memory", full_address_space, 0x10000, 0x0000); + editor.DrawWindow("ROM", dmg.rom->data, 0x8000, 0); // Rendering ImGui::Render(); diff --git a/src/cpu.c b/src/cpu.c index 0814bac..7d7324b 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -91,6 +91,7 @@ static void inc_with_carry(struct cpu *regs, u8 *reg) clear_flag(regs, FLAG_SIGN); if(*reg == 0xff || *reg == 0x0f) set_flag(regs, FLAG_HALF_CARRY); + else clear_flag(regs, FLAG_HALF_CARRY); (*reg)++; if(*reg == 0) set_flag(regs, FLAG_ZERO); @@ -102,6 +103,7 @@ static void dec_with_carry(struct cpu *regs, u8 *reg) set_flag(regs, FLAG_SIGN); if(*reg == 0x00 || *reg == 0x10) set_flag(regs, FLAG_HALF_CARRY); + else clear_flag(regs, FLAG_HALF_CARRY); (*reg)--; if(*reg == 0) set_flag(regs, FLAG_ZERO); @@ -218,10 +220,22 @@ static void add(struct cpu *cpu, u8 value, int with_carry) sum_full++; } sum_trunc = (u8) sum_full; - set_flag(cpu, sum_trunc == 0 ? FLAG_ZERO : 0); + if (sum_trunc == 0) { + set_flag(cpu, FLAG_ZERO); + } else { + clear_flag(cpu, FLAG_ZERO); + } clear_flag(cpu, FLAG_SIGN); - set_flag(cpu, sum_full > sum_trunc ? FLAG_CARRY : 0); - // TODO H + if (sum_full > sum_trunc) { + set_flag(cpu, FLAG_CARRY); + } else { + clear_flag(cpu, FLAG_CARRY); + } + if (((cpu->a & 0xf) + (value & 0xf)) & 0x10) { + set_flag(cpu, FLAG_HALF_CARRY); + } else { + clear_flag(cpu, FLAG_HALF_CARRY); + } cpu->a = sum_trunc; } @@ -239,12 +253,16 @@ static void subtract(struct cpu *cpu, u8 value, int with_carry, int just_compare clear_flag(cpu, FLAG_ZERO); } set_flag(cpu, FLAG_SIGN); - if (sum_full < sum_trunc) { + if (value > cpu->a) { set_flag(cpu, FLAG_CARRY); } else { clear_flag(cpu, FLAG_CARRY); } - // TODO H + if (((cpu->a & 0xf) - (value & 0xf)) & 0x10) { + set_flag(cpu, FLAG_HALF_CARRY); + } else { + clear_flag(cpu, FLAG_HALF_CARRY); + } if (!just_compare) { cpu->a = sum_trunc; @@ -382,14 +400,50 @@ static void conditional_jump(struct cpu *cpu, u8 opc, u8 neg_op, int flag) { } } +static u16 handlers[] = { 0x40, 0x48, 0x50, 0x58, 0x60 }; + +static u16 check_interrupts(struct cpu *cpu) +{ + int k; + + if (!cpu->interrupt_enable) { + return 0; + } + + u16 enabled = cpu->mem_read(cpu->mem_model, 0xffff); + u16 requested = cpu->mem_read(cpu->mem_model, 0xff0f); + + for (k = 0; k < NUM_INTERRUPTS; k++) { + int check = 1 << k; + if ((enabled & check) && (requested & check)) { + // clear request flag for this interrupt and disable all further + // interrupts until service routine executes EI or RETI + cpu->mem_write(cpu->mem_model, 0xff0f, requested & ~check); + cpu->interrupt_enable = 0; + return handlers[k]; + } + } + + return 0; +} + void cpu_step(struct cpu *cpu) { u8 temp; - u16 temp16; + u16 temp16, intr_dest; + + intr_dest = check_interrupts(cpu); + if (intr_dest) { + push(cpu, cpu->pc); + cpu->pc = intr_dest; + return; + } + u8 opc = cpu->mem_read(cpu->mem_model, cpu->pc); #ifdef GB6_DEBUG printf("0x%04x %s\n", cpu->pc, instructions[opc].format); #endif + cpu->pc++; cpu->cycle_count += instructions[opc].cycles; switch (opc) { @@ -727,8 +781,10 @@ void cpu_step(struct cpu *cpu) } break; case 0xca: // JP Z, u16 + temp16 = read16(cpu, cpu->pc); + cpu->pc += 2; if (flag_isset(cpu, FLAG_ZERO)) { - cpu->pc = read16(cpu, cpu->pc); + cpu->pc = temp16; cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles; } break; @@ -742,6 +798,10 @@ void cpu_step(struct cpu *cpu) case 0xd5: // PUSH DE push(cpu, read_de(cpu)); break; + case 0xd9: // RETI + cpu->pc = pop(cpu); + cpu->interrupt_enable = 1; + break; case 0xe0: // LD (a8),A write8(cpu, 0xff00 + read8(cpu, cpu->pc), cpu->a); cpu->pc++; @@ -773,6 +833,7 @@ void cpu_step(struct cpu *cpu) cpu->a = read8(cpu, 0xff00 + cpu->c); break; case 0xf3: // DI + cpu->interrupt_enable = 0; break; case 0xf5: // PUSH AF push(cpu, read_af(cpu)); @@ -782,6 +843,7 @@ void cpu_step(struct cpu *cpu) cpu->pc += 2; break; case 0xfb: // EI + cpu->interrupt_enable = 1; break; default: printf("unknown opcode 0x%02x %s\n", opc, instructions[opc].format); diff --git a/src/cpu.h b/src/cpu.h index 9f31561..9f0b659 100644 --- a/src/cpu.h +++ b/src/cpu.h @@ -16,6 +16,7 @@ struct cpu u16 sp; u16 pc; u32 cycle_count; + u8 interrupt_enable; u8 (*mem_read)(void *, u16); void (*mem_write)(void *, u16, u8); @@ -37,4 +38,11 @@ int flag_isset(struct cpu *cpu, int flag); #define FLAG_HALF_CARRY 0x20 #define FLAG_CARRY 0x10 +#define INT_VBLANK (1 << 0) +#define INT_LCDSTAT (1 << 1) +#define INT_TIMER (1 << 2) +#define INT_SERIAL (1 << 3) +#define INT_JOYPAD (1 << 4) +#define NUM_INTERRUPTS 5 + #endif diff --git a/src/dmg.c b/src/dmg.c index 6463bbe..ed58ca7 100644 --- a/src/dmg.c +++ b/src/dmg.c @@ -13,6 +13,33 @@ void dmg_new(struct dmg *dmg, struct cpu *cpu, struct rom *rom, struct lcd *lcd) dmg->cpu = cpu; dmg->rom = rom; dmg->lcd = lcd; + + dmg->joypad = 0xf; // nothing pressed + dmg->action_buttons = 0xf; +} + +void dmg_set_button(struct dmg *dmg, int field, int button, int pressed) +{ + u8 *mod; + if (field == FIELD_JOY) { + mod = &dmg->joypad; + } else if (field == FIELD_ACTION) { + mod = &dmg->action_buttons; + } else { + printf("setting invalid button state\n"); + return; + } + + if (pressed) { + *mod &= ~button; + } else { + *mod |= button; + } +} + +static u8 get_button_state(struct dmg *dmg) +{ + return dmg->action_selected ? dmg->action_buttons : dmg->joypad; } u8 dmg_read(void *_dmg, u16 address) @@ -37,6 +64,12 @@ u8 dmg_read(void *_dmg, u16 address) return lcd_read(dmg->lcd, address); } else if (address >= 0xff80 && address <= 0xfffe) { return dmg->zero_page[address - 0xff80]; + } else if (address == 0xff00) { + return get_button_state(dmg); + } else if (address == 0xff0f) { + return dmg->interrupt_requested; + } else if (address == 0xffff) { + return dmg->interrupt_enabled; } else { // not sure about any of this yet // commented out bc of memory view window @@ -65,11 +98,21 @@ void dmg_write(void *_dmg, u16 address, u8 data) lcd_write(dmg->lcd, address, data); } else if (address >= 0xff80 && address <= 0xfffe) { dmg->zero_page[address - 0xff80] = data; + } else if (address == 0xff00) { + dmg->action_selected = data & (1 << 5); + } else if (address == 0xff0f) { + dmg->interrupt_requested = data; + } else if (address == 0xffff) { + dmg->interrupt_enabled = data; } else { // not sure about any of this yet } } -void exit(int); + +void dmg_request_interrupt(struct dmg *dmg, int nr) +{ + dmg->interrupt_requested |= nr; +} void dmg_step(void *_dmg) { @@ -85,6 +128,7 @@ void dmg_step(void *_dmg) int next_scanline = lcd_step(dmg->lcd); if (next_scanline == 144) { // vblank has started, draw all the stuff from ram into the lcd + dmg_request_interrupt(dmg, INT_VBLANK); int lcdc = lcd_read(dmg->lcd, REG_LCDC); int bg_base = (lcdc & LCDC_BG_TILE_MAP) ? 0x9c00 : 0x9800; @@ -92,8 +136,6 @@ void dmg_step(void *_dmg) int use_unsigned = lcdc & LCDC_BG_TILE_DATA; int tilebase = use_unsigned ? 0x8000 : 0x9000; - printf("tile map: %04x, tile data: %04x\n", bg_base, tilebase); - int k = 0, off = 0; int tile_y = 0, tile_x = 0; for (tile_y = 0; tile_y < 32; tile_y++) { diff --git a/src/dmg.h b/src/dmg.h index 61788fe..ca48da2 100644 --- a/src/dmg.h +++ b/src/dmg.h @@ -5,6 +5,18 @@ #include "rom.h" #include "lcd.h" +#define FIELD_JOY 1 +#define FIELD_ACTION 2 + +#define BUTTON_RIGHT (1 << 0) +#define BUTTON_LEFT (1 << 1) +#define BUTTON_UP (1 << 2) +#define BUTTON_DOWN (1 << 3) +#define BUTTON_A (1 << 0) +#define BUTTON_B (1 << 1) +#define BUTTON_SELECT (1 << 2) +#define BUTTON_START (1 << 3) + struct dmg { struct cpu *cpu; struct rom *rom; @@ -13,9 +25,16 @@ struct dmg { u8 video_ram[0x2000]; u8 zero_page[0x80]; u32 last_lcd_update; + int action_selected; // non-0 if A/B/start/select selected, 0 for directions + u8 interrupt_enabled; + u8 interrupt_requested; + + u8 joypad; + u8 action_buttons; }; void dmg_new(struct dmg *dmg, struct cpu *cpu, struct rom *rom, struct lcd *lcd); +void dmg_set_button(struct dmg *dmg, int field, int button, int pressed); // why did i make these void * u8 dmg_read(void *dmg, u16 address);