From da49a726311b57142b944c1c7d2679e9d57f5bd5 Mon Sep 17 00:00:00 2001 From: condret Date: Sun, 17 Nov 2024 03:08:39 +0100 Subject: [PATCH] implement esil stop/halt ops --- gb/esil.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++-- include/gb.h | 11 +++++++++ io/interrupts.c | 20 ++++++++++++--- io/joypad.c | 6 ++--- io/timers.c | 5 ++++ 5 files changed, 100 insertions(+), 8 deletions(-) diff --git a/gb/esil.c b/gb/esil.c index a9488fc..841bea9 100644 --- a/gb/esil.c +++ b/gb/esil.c @@ -1,11 +1,73 @@ #include #include -bool gb_custom_op_halt (REsil *esil) { +static bool gb_custom_op_halt (REsil *esil) { + GB *gb = esil->user; + ut64 ime = 0ULL; + if (R_UNLIKELY (!r_esil_reg_read (esil, "ime", &ime, NULL))) { + return false; + } + if (!ime) { + //pandocs says to also check for pending interrupts + //but https://github.com/nitro2k01/little-things-gb/tree/main/double-halt-cancel doesn't mention this + //so, wat du? + gb->mode = GB_MODE_HALT_DOUBLE_BYTE_FETCH; + return true; + } + if (gb->interrupts.flags & (0x1 << GB_INTERRUPT_ENABLE_WAIT)) { + gb->mode = GB_MODE_HALT_ENTER; + return true; + } + gb->mode = GB_MODE_HALT; return true; } -bool gb_custom_op_stop (REsil *esil) { +static bool gb_custom_op_stop (REsil *esil) { + GB *gb = esil->user; + //check if any button is held + if (((gb->joypad.data & 0x30) ^ 0x30) && ((gb->joypad.data & 0x0f) ^ 0x0f)) { + //check for pending interrupts + ut32 i; + for (i = 0; i < GB_INTERRUPT_N; i++) { + const ut32 imask = (0x1 << i) | (0x1 << (i + GB_INTERRUPT_N)); + if ((gb->interrupts.flags & imask) == imask) { + return true; + } + } +#if 0 + gb->mode = GB_MODE_HALT; + return r_reg_setv (esil->anal->reg, "pc" + r_reg_getv (esil->anal->reg, "pc") + 1); +#else + ut64 pc = 0ULL; + if (R_UNLIKELY (!r_esil_reg_read (esil, "pc", &pc, NULL))) { + return false; + } + pc = (pc + 1) & 0xffff; + if (R_UNLIKELY (!r_esil_reg_write (esil, "pc", pc))) { + return false; + } + gb->mode = GB_MODE_HALT; + return true; +#endif + } + gb->mode = GB_MODE_STOP; + gb_timers_reset_div (&gb->timers); + ut32 i; + for (i = 0; i < GB_INTERRUPT_N; i++) { + const ut32 imask = (0x1 << i) | (0x1 << (i + GB_INTERRUPT_N)); + if ((gb->interrupts.flags & imask) == imask) { + return true; + } + } + ut64 pc = 0ULL; + if (R_UNLIKELY (!r_esil_reg_read (esil, "pc", &pc, NULL))) { + return false; + } + pc = (pc + 1) & 0xffff; + if (R_UNLIKELY (!r_esil_reg_write (esil, "pc", pc))) { + return false; + } return true; } diff --git a/include/gb.h b/include/gb.h index b934450..7422cd6 100644 --- a/include/gb.h +++ b/include/gb.h @@ -32,6 +32,7 @@ typedef struct gb_timers_t { } GBTimers; bool gb_timers_init(GBTimers *timers, RIO *io); +void gb_timers_reset_div(GBTimers *timers); void gb_timers_fini(GBTimers *timers, RIO *io); typedef struct gb_joypad_t { @@ -72,6 +73,8 @@ enum { GB_INTERRUPT_JOYPAD_ENABLED, GB_INTERRUPT_ENABLED, //general enable of all interrupts GB_INTERRUPT_ENABLE_WAIT, //wait 1 instruction befor enabling interrupts + GB_INTERRUPT_DEC_RET, //decrement return address when entering interrupt handler + //to honor halt instruction bug GB_INTERRUPT_WAIT_TRIGGER, //gb was put in wait state on previous call GB_INTERRUPT_SEEK, //2 bits for seek GB_INTERRUPT_PENDING = GB_INTERRUPT_SEEK + 2, //3 bits for pending interrupt @@ -221,6 +224,13 @@ typedef struct gb_dmg_ppu_t { ut32 oam_mapid; } GBPPU; +enum { + GB_MODE_STOP, + GB_MODE_HALT_ENTER, + GB_MODE_HALT, + GB_MODE_HALT_DOUBLE_BYTE_FETCH, +}; + typedef struct gameboy_t { RIO *io; RArch *arch; @@ -234,6 +244,7 @@ typedef struct gameboy_t { ut64 addr; int cartrigde_fd; ut32 pause_cycles; + ut32 mode; // bool double_speed; } GB; diff --git a/io/interrupts.c b/io/interrupts.c index f447948..eacee87 100644 --- a/io/interrupts.c +++ b/io/interrupts.c @@ -118,18 +118,26 @@ void gb_interrupts_fini(GBInterrupts *ints, RIO *io) { void gb_interrupts_continue(GB *gb) { const bool ime = !!r_reg_get_value (gb->anal->reg, gb->interrupts.ime); + bool dec_pc = false; if (gb->interrupts.flags & (0x1 << GB_INTERRUPT_ENABLE_WAIT)) { gb->interrupts.flags ^= (0x1 << GB_INTERRUPT_ENABLE_WAIT); if (!ime) { return; } gb->interrupts.flags |= (0x1 << GB_INTERRUPT_ENABLED); + if (gb->mode == GB_MODE_HALT_ENTER) { + dec_pc = true; + } } else if (ime && !(gb->interrupts.flags & (0x1 << GB_INTERRUPT_ENABLED))) { //wait 1 instruction gb->interrupts.flags |= (0x1 << GB_INTERRUPT_ENABLE_WAIT); return; } else if (!ime) { - gb->interrupts.flags &= 0x3ff; + gb->interrupts.flags &= ~((0x1 << GB_INTERRUPT_ENABLED) | + (0x1 << GB_INTERRUPT_ENABLE_WAIT) | + (0x1 << GB_INTERRUPT_PENDING) | + (0x1 << (GB_INTERRUPT_PENDING + 1)) | + (0x1 << (GB_INTERRUPT_PENDING + 2))); return; } if (!(gb->interrupts.flags & (0x1 << GB_INTERRUPT_WAIT_TRIGGER))) { @@ -144,18 +152,24 @@ void gb_interrupts_continue(GB *gb) { //save pending interrupt index gb->interrupts.flags &= ~(0x7 << GB_INTERRUPT_PENDING); gb->interrupts.flags |= (i << GB_INTERRUPT_PENDING); + if (R_UNLIKELY (dec_pc)) { + gb->interrupts.flags |= (0x1 << GB_INTERRUPT_DEC_RET); + } return; } } return; } + dec_pc = !!(gb->interrupts.flags & (0x1 << GB_INTERRUPT_DEC_RET)); + gb->interrupts.flags &= ~(0x1 << GB_INTERRUPT_DEC_RET); gb->interrupts.flags ^= 0x1 << GB_INTERRUPT_WAIT_TRIGGER; const ut32 interrupt = (gb->interrupts.flags & (0x7 << GB_INTERRUPT_PENDING)) >> GB_INTERRUPT_PENDING; gb->interrupts.flags &= ~(0x1 << interrupt); const ut64 interrupt_vector = 0x40 + (interrupt << 3); //maybe make this a static buffer? - char *interrupt_expr = r_str_newf ( - "0,ime,:=,2,sp,-=,pc,sp,=[2],0x%"PFMT64x",pc,:=", interrupt_vector); + char *interrupt_expr = r_str_newf (dec_pc ? + "0,ime,:=,2,sp,-=,1,pc,-,sp,=[2],0x%"PFMT64x",pc,;=" + :"0,ime,:=,2,sp,-=,pc,sp,=[2],0x%"PFMT64x",pc,:=", interrupt_vector); r_esil_parse (gb->esil, interrupt_expr); free (interrupt_expr); } diff --git a/io/joypad.c b/io/joypad.c index 4eb2085..807ee9e 100644 --- a/io/joypad.c +++ b/io/joypad.c @@ -104,7 +104,7 @@ bool gb_joypad_init (GBJoypad *joypad, RIO *io) { joypad->b = SDL_SCANCODE_K; joypad->start = SDL_SCANCODE_SPACE; joypad->select = SDL_SCANCODE_KP_ENTER; - return true;; + return true; } void gb_joypad_continue(GB *gb) { @@ -113,7 +113,7 @@ void gb_joypad_continue(GB *gb) { joypad->odata |= joypad->data & 0x3f; SDL_PumpEvents (); ut8 ndata = 0; - if (!(joypad->data & 0x8)) { //d-pad + if (!(joypad->data & 0x10)) { //d-pad const ut8 data = (~joypad->data) & 0xf; ndata = !!joypad->keys[joypad->right]; ndata |= (!!joypad->keys[joypad->left]) << 1; @@ -127,7 +127,7 @@ void gb_joypad_continue(GB *gb) { ndata = (ndata & 0x3) | (data & 0xc); } } - if (!(joypad->data & 0x10)) { //buttons + if (!(joypad->data & 0x20)) { //buttons ndata |= !!joypad->keys[joypad->a]; ndata |= (!!joypad->keys[joypad->b]) << 1; ndata |= (!!joypad->keys[joypad->select]) << 2; diff --git a/io/timers.c b/io/timers.c index dd8f89e..1680eb5 100644 --- a/io/timers.c +++ b/io/timers.c @@ -177,3 +177,8 @@ void gb_timers_continue (GB *gb, ut32 cycles) { gb_interrupts_request (gb, GB_INTERRUPT_TIMER); } } + +void gb_timers_reset_div (GBTimers *timers) { + timers->buf[GB_TIMERS_DIV] = 0; + timers->div = 0; +}