implement esil stop/halt ops

This commit is contained in:
condret 2024-11-17 03:08:39 +01:00
parent a7b3fa98be
commit da49a72631
5 changed files with 100 additions and 8 deletions

View File

@ -1,11 +1,73 @@
#include <gb.h> #include <gb.h>
#include <r_esil.h> #include <r_esil.h>
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; 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; return true;
} }

View File

@ -32,6 +32,7 @@ typedef struct gb_timers_t {
} GBTimers; } GBTimers;
bool gb_timers_init(GBTimers *timers, RIO *io); bool gb_timers_init(GBTimers *timers, RIO *io);
void gb_timers_reset_div(GBTimers *timers);
void gb_timers_fini(GBTimers *timers, RIO *io); void gb_timers_fini(GBTimers *timers, RIO *io);
typedef struct gb_joypad_t { typedef struct gb_joypad_t {
@ -72,6 +73,8 @@ enum {
GB_INTERRUPT_JOYPAD_ENABLED, GB_INTERRUPT_JOYPAD_ENABLED,
GB_INTERRUPT_ENABLED, //general enable of all interrupts GB_INTERRUPT_ENABLED, //general enable of all interrupts
GB_INTERRUPT_ENABLE_WAIT, //wait 1 instruction befor enabling 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_WAIT_TRIGGER, //gb was put in wait state on previous call
GB_INTERRUPT_SEEK, //2 bits for seek GB_INTERRUPT_SEEK, //2 bits for seek
GB_INTERRUPT_PENDING = GB_INTERRUPT_SEEK + 2, //3 bits for pending interrupt 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; ut32 oam_mapid;
} GBPPU; } GBPPU;
enum {
GB_MODE_STOP,
GB_MODE_HALT_ENTER,
GB_MODE_HALT,
GB_MODE_HALT_DOUBLE_BYTE_FETCH,
};
typedef struct gameboy_t { typedef struct gameboy_t {
RIO *io; RIO *io;
RArch *arch; RArch *arch;
@ -234,6 +244,7 @@ typedef struct gameboy_t {
ut64 addr; ut64 addr;
int cartrigde_fd; int cartrigde_fd;
ut32 pause_cycles; ut32 pause_cycles;
ut32 mode;
// bool double_speed; // bool double_speed;
} GB; } GB;

View File

@ -118,18 +118,26 @@ void gb_interrupts_fini(GBInterrupts *ints, RIO *io) {
void gb_interrupts_continue(GB *gb) { void gb_interrupts_continue(GB *gb) {
const bool ime = !!r_reg_get_value (gb->anal->reg, gb->interrupts.ime); 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)) { if (gb->interrupts.flags & (0x1 << GB_INTERRUPT_ENABLE_WAIT)) {
gb->interrupts.flags ^= (0x1 << GB_INTERRUPT_ENABLE_WAIT); gb->interrupts.flags ^= (0x1 << GB_INTERRUPT_ENABLE_WAIT);
if (!ime) { if (!ime) {
return; return;
} }
gb->interrupts.flags |= (0x1 << GB_INTERRUPT_ENABLED); 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))) { } else if (ime && !(gb->interrupts.flags & (0x1 << GB_INTERRUPT_ENABLED))) {
//wait 1 instruction //wait 1 instruction
gb->interrupts.flags |= (0x1 << GB_INTERRUPT_ENABLE_WAIT); gb->interrupts.flags |= (0x1 << GB_INTERRUPT_ENABLE_WAIT);
return; return;
} else if (!ime) { } 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; return;
} }
if (!(gb->interrupts.flags & (0x1 << GB_INTERRUPT_WAIT_TRIGGER))) { if (!(gb->interrupts.flags & (0x1 << GB_INTERRUPT_WAIT_TRIGGER))) {
@ -144,18 +152,24 @@ void gb_interrupts_continue(GB *gb) {
//save pending interrupt index //save pending interrupt index
gb->interrupts.flags &= ~(0x7 << GB_INTERRUPT_PENDING); gb->interrupts.flags &= ~(0x7 << GB_INTERRUPT_PENDING);
gb->interrupts.flags |= (i << 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;
} }
} }
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; gb->interrupts.flags ^= 0x1 << GB_INTERRUPT_WAIT_TRIGGER;
const ut32 interrupt = (gb->interrupts.flags & (0x7 << GB_INTERRUPT_PENDING)) >> GB_INTERRUPT_PENDING; const ut32 interrupt = (gb->interrupts.flags & (0x7 << GB_INTERRUPT_PENDING)) >> GB_INTERRUPT_PENDING;
gb->interrupts.flags &= ~(0x1 << interrupt); gb->interrupts.flags &= ~(0x1 << interrupt);
const ut64 interrupt_vector = 0x40 + (interrupt << 3); const ut64 interrupt_vector = 0x40 + (interrupt << 3);
//maybe make this a static buffer? //maybe make this a static buffer?
char *interrupt_expr = r_str_newf ( char *interrupt_expr = r_str_newf (dec_pc ?
"0,ime,:=,2,sp,-=,pc,sp,=[2],0x%"PFMT64x",pc,:=", interrupt_vector); "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); r_esil_parse (gb->esil, interrupt_expr);
free (interrupt_expr); free (interrupt_expr);
} }

View File

@ -104,7 +104,7 @@ bool gb_joypad_init (GBJoypad *joypad, RIO *io) {
joypad->b = SDL_SCANCODE_K; joypad->b = SDL_SCANCODE_K;
joypad->start = SDL_SCANCODE_SPACE; joypad->start = SDL_SCANCODE_SPACE;
joypad->select = SDL_SCANCODE_KP_ENTER; joypad->select = SDL_SCANCODE_KP_ENTER;
return true;; return true;
} }
void gb_joypad_continue(GB *gb) { void gb_joypad_continue(GB *gb) {
@ -113,7 +113,7 @@ void gb_joypad_continue(GB *gb) {
joypad->odata |= joypad->data & 0x3f; joypad->odata |= joypad->data & 0x3f;
SDL_PumpEvents (); SDL_PumpEvents ();
ut8 ndata = 0; ut8 ndata = 0;
if (!(joypad->data & 0x8)) { //d-pad if (!(joypad->data & 0x10)) { //d-pad
const ut8 data = (~joypad->data) & 0xf; const ut8 data = (~joypad->data) & 0xf;
ndata = !!joypad->keys[joypad->right]; ndata = !!joypad->keys[joypad->right];
ndata |= (!!joypad->keys[joypad->left]) << 1; ndata |= (!!joypad->keys[joypad->left]) << 1;
@ -127,7 +127,7 @@ void gb_joypad_continue(GB *gb) {
ndata = (ndata & 0x3) | (data & 0xc); ndata = (ndata & 0x3) | (data & 0xc);
} }
} }
if (!(joypad->data & 0x10)) { //buttons if (!(joypad->data & 0x20)) { //buttons
ndata |= !!joypad->keys[joypad->a]; ndata |= !!joypad->keys[joypad->a];
ndata |= (!!joypad->keys[joypad->b]) << 1; ndata |= (!!joypad->keys[joypad->b]) << 1;
ndata |= (!!joypad->keys[joypad->select]) << 2; ndata |= (!!joypad->keys[joypad->select]) << 2;

View File

@ -177,3 +177,8 @@ void gb_timers_continue (GB *gb, ut32 cycles) {
gb_interrupts_request (gb, GB_INTERRUPT_TIMER); gb_interrupts_request (gb, GB_INTERRUPT_TIMER);
} }
} }
void gb_timers_reset_div (GBTimers *timers) {
timers->buf[GB_TIMERS_DIV] = 0;
timers->div = 0;
}