implement esil stop/halt ops
This commit is contained in:
parent
a7b3fa98be
commit
da49a72631
66
gb/esil.c
66
gb/esil.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
include/gb.h
11
include/gb.h
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user