Implement interrupt handling

This commit is contained in:
condret 2024-11-09 18:10:11 +01:00
parent 27bc6bcc39
commit e94520f6cd
4 changed files with 86 additions and 40 deletions

View File

@ -53,28 +53,31 @@ bool gb_joypad_init(GBJoypad *joypad, RIO *io);
void gb_joypad_fini(GBJoypad *joypad, RIO *io); void gb_joypad_fini(GBJoypad *joypad, RIO *io);
typedef struct gb_interrupts_t { typedef struct gb_interrupts_t {
RRegItem *ime;
int fd; int fd;
ut16 flags; ut16 flags;
// ut16 pc;
} GBInterrupts; } GBInterrupts;
enum { enum {
GB_INTERRUPTS_VBLANK = 0, GB_INTERRUPT_VBLANK = 0,
GB_INTERRUPTS_STAT, GB_INTERRUPT_STAT,
GB_INTERRUPTS_TIMER, GB_INTERRUPT_TIMER,
GB_INTERRUPTS_SERIAL, GB_INTERRUPT_SERIAL,
GB_INTERRUPTS_JOYPAD, GB_INTERRUPT_JOYPAD,
GB_INTERRUPTS_N, //number of interrupts GB_INTERRUPT_N, //number of interrupts
GB_INTERRUPTS_VBLANK_ENABLED = GB_INTERRUPTS_N, GB_INTERRUPT_VBLANK_ENABLED = GB_INTERRUPT_N,
GB_INTERRUPTS_STAT_ENABLED, GB_INTERRUPT_STAT_ENABLED,
GB_INTERRUPTS_TIMER_ENABLED, GB_INTERRUPT_TIMER_ENABLED,
GB_INTERRUPTS_SERIAL_ENABLED, GB_INTERRUPT_SERIAL_ENABLED,
GB_INTERRUPTS_JOYPAD_ENABLED, GB_INTERRUPT_JOYPAD_ENABLED,
GB_INTERRUPTS_ENABLED, //general enable of all interrupts GB_INTERRUPT_ENABLED, //general enable of all interrupts
GB_INTERRUPTS_ENABLE_WAIT, //wait 1 instruction befor enabling interrupts GB_INTERRUPT_ENABLE_WAIT, //wait 1 instruction befor enabling interrupts
GB_INTERRUPTS_SEEK, //2 bits for seek GB_INTERRUPT_WAIT_TRIGGER, //gb was put in wait state on previous call
GB_INTERRUPT_SEEK, //2 bits for seek
}; };
bool gb_interrupts_init(GBInterrupts *ints, RIO *io); bool gb_interrupts_init(GBInterrupts *ints, RIO *io, RReg *reg);
void gb_interrupts_fini(GBInterrupts *ints, RIO *io); void gb_interrupts_fini(GBInterrupts *ints, RIO *io);
typedef struct gb_dma_t { typedef struct gb_dma_t {
@ -134,6 +137,11 @@ enum {
#define GB_PPU_STAT_MODE_MASK 0x3 #define GB_PPU_STAT_MODE_MASK 0x3
#define GB_PPU_STAT_INTR_HBLANK 0x8
#define GB_PPU_STAT_INTR_VBLANK 0x10
#define GB_PPU_STAT_INTR_OAM 0x20
#define GB_PPU_STAT_INTR_LYC 0x40
typedef struct oam_scan_table { typedef struct oam_scan_table {
ut32 data[10]; ut32 data[10];
//low byte is x coordinate of sprite; //low byte is x coordinate of sprite;
@ -212,8 +220,9 @@ typedef struct gb_dmg_ppu_t {
typedef struct gameboy_t { typedef struct gameboy_t {
RIO *io; RIO *io;
REsil *esil;
RArch *arch; RArch *arch;
RAnal *anal;
REsil *esil;
GBTimers timers; GBTimers timers;
GBJoypad joypad; GBJoypad joypad;
GBInterrupts interrupts; GBInterrupts interrupts;

View File

@ -9,7 +9,7 @@ RIOPlugin r_io_plugin_gb_interrupts;
static ut64 __lseek(RIO* io, RIODesc *desc, ut64 offset, int whence) { static ut64 __lseek(RIO* io, RIODesc *desc, ut64 offset, int whence) {
GBInterrupts *ints = desc->data; GBInterrupts *ints = desc->data;
ut64 seek = (ints->flags >> GB_INTERRUPTS_SEEK) & 0x3; ut64 seek = (ints->flags >> GB_INTERRUPT_SEEK) & 0x3;
switch (whence) { switch (whence) {
case R_IO_SEEK_SET: case R_IO_SEEK_SET:
seek = R_MIN (2, offset); seek = R_MIN (2, offset);
@ -21,44 +21,44 @@ static ut64 __lseek(RIO* io, RIODesc *desc, ut64 offset, int whence) {
seek = 2; seek = 2;
break; break;
} }
ints->flags &= ~(0x3 << GB_INTERRUPTS_SEEK); ints->flags &= ~(0x3 << GB_INTERRUPT_SEEK);
ints->flags |= seek << GB_INTERRUPTS_SEEK; ints->flags |= seek << GB_INTERRUPT_SEEK;
return seek; return seek;
} }
static int __read(RIO *io, RIODesc *desc, ut8 *buf, int len) { static int __read(RIO *io, RIODesc *desc, ut8 *buf, int len) {
GBInterrupts *ints = desc->data; GBInterrupts *ints = desc->data;
ut64 seek = (ints->flags >> GB_INTERRUPTS_SEEK) & 0x3; ut64 seek = (ints->flags >> GB_INTERRUPT_SEEK) & 0x3;
if (seek > 1 || len < 1) { if (seek > 1 || len < 1) {
return 0; return 0;
} }
len = R_MIN (len, 2 - seek); len = R_MIN (len, 2 - seek);
ut32 i; ut32 i;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
buf[i] = (ints->flags & (0x1f << (GB_INTERRUPTS_N * seek))) >> buf[i] = (ints->flags & (0x1f << (GB_INTERRUPT_N * seek))) >>
(GB_INTERRUPTS_N * seek); (GB_INTERRUPT_N * seek);
seek++; seek++;
} }
ints->flags &= ~(0x3 << GB_INTERRUPTS_SEEK); ints->flags &= ~(0x3 << GB_INTERRUPT_SEEK);
ints->flags |= seek << GB_INTERRUPTS_SEEK; ints->flags |= seek << GB_INTERRUPT_SEEK;
return len; return len;
} }
static int __write(RIO *io, RIODesc *desc, const ut8 *buf, int len) { static int __write(RIO *io, RIODesc *desc, const ut8 *buf, int len) {
GBInterrupts *ints = desc->data; GBInterrupts *ints = desc->data;
ut64 seek = (ints->flags >> GB_INTERRUPTS_SEEK) & 0x3; ut64 seek = (ints->flags >> GB_INTERRUPT_SEEK) & 0x3;
if (seek > 1 || len < 1) { if (seek > 1 || len < 1) {
return 0; return 0;
} }
len = R_MIN (len, 2 - seek); len = R_MIN (len, 2 - seek);
ut32 i; ut32 i;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
ints->flags &= ~(0x1f << (GB_INTERRUPTS_N * seek)); ints->flags &= ~(0x1f << (GB_INTERRUPT_N * seek));
ints->flags |= (buf[i] & 0x1f) << (GB_INTERRUPTS_N * seek); ints->flags |= (buf[i] & 0x1f) << (GB_INTERRUPT_N * seek);
seek++; seek++;
} }
ints->flags &= ~(0x3 << GB_INTERRUPTS_SEEK); ints->flags &= ~(0x3 << GB_INTERRUPT_SEEK);
ints->flags |= seek << GB_INTERRUPTS_SEEK; ints->flags |= seek << GB_INTERRUPT_SEEK;
return len; return len;
} }
@ -96,8 +96,9 @@ RIOPlugin r_io_plugin_gb_interrupts = {
.write = __write, .write = __write,
}; };
bool gb_interrupts_init(GBInterrupts *ints, RIO *io) { bool gb_interrupts_init(GBInterrupts *ints, RIO *io, RReg *reg) {
ints[0] = (const GBInterrupts) {0}; ints[0] = (const GBInterrupts) {0};
ints->ime = r_reg_get (reg, "ime", R_REG_TYPE_GPR);
char uri[64]; char uri[64];
memset (uri, 0x00, sizeof (char) * 64); memset (uri, 0x00, sizeof (char) * 64);
sprintf (uri, "gb_interrupts://%p", ints); sprintf (uri, "gb_interrupts://%p", ints);
@ -111,22 +112,58 @@ bool gb_interrupts_init(GBInterrupts *ints, RIO *io) {
} }
void gb_interrupts_fini(GBInterrupts *ints, RIO *io) { void gb_interrupts_fini(GBInterrupts *ints, RIO *io) {
r_unref (ints->ime);
r_io_fd_close (io, ints->fd); r_io_fd_close (io, ints->fd);
} }
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);
//TODO 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);
} 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 &= ~0xe00;
return;
}
ut32 i;
for (i = 0; i < GB_INTERRUPT_N; i++) {
const ut16 imask = (0x1 << i) | (0x1 << (i + GB_INTERRUPT_N));
if ((gb->interrupts.flags & imask) == imask) {
goto beach;
}
//remove requests for disabled interrupts
gb->interrupts.flags &= ~(0x1 << i);
}
return;
beach:
if (!(gb->interrupts.flags & (0x1 << GB_INTERRUPT_WAIT_TRIGGER))) {
gb->interrupts.flags |= (0x1 << GB_INTERRUPT_WAIT_TRIGGER);
//stop exection of instructions for 20 cycles
//gb_pause_exec (gb, 20);
return;
}
const ut64 interrupt_vector = 0x40 + (i << 3);
//maybe make this a static buffer?
char *interrupt_expr = r_str_newf ("2,sp,-=,pc,sp,=[2],0x%"PFMT64x",pc,:=", interrupt_vector);
r_esil_parse (gb->esil, interrupt_expr);
free (interrupt_expr);
} }
void gb_interrupts_request (GB *gb, int interrupt) { void gb_interrupts_request (GB *gb, int interrupt) {
switch (interrupt) { switch (interrupt) {
case GB_INTERRUPTS_VBLANK: case GB_INTERRUPT_VBLANK:
case GB_INTERRUPTS_STAT: case GB_INTERRUPT_STAT:
case GB_INTERRUPTS_TIMER: case GB_INTERRUPT_TIMER:
case GB_INTERRUPTS_SERIAL: case GB_INTERRUPT_SERIAL:
case GB_INTERRUPTS_JOYPAD: case GB_INTERRUPT_JOYPAD:
gb->interrupts.flags |= interrupt; gb->interrupts.flags |= (0x1 << interrupt);
break; break;
default: default:
break; break;

View File

@ -135,7 +135,7 @@ void gb_joypad_continue(GB *gb) {
} }
joypad->data = (joypad->data & 0x30) | (ndata ^ 0xf); joypad->data = (joypad->data & 0x30) | (ndata ^ 0xf);
if (joypad->odata & ndata) { if (joypad->odata & ndata) {
gb_interrupts_request (gb, GB_INTERRUPTS_JOYPAD); gb_interrupts_request (gb, GB_INTERRUPT_JOYPAD);
} }
} }

View File

@ -174,6 +174,6 @@ void gb_timers_continue (GB *gb, ut32 cycles) {
cycles -= _cycles; cycles -= _cycles;
} while (cycles); } while (cycles);
if (request_interrupt) { if (request_interrupt) {
gb_interrupts_request (gb, GB_INTERRUPTS_TIMER); gb_interrupts_request (gb, GB_INTERRUPT_TIMER);
} }
} }