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);
typedef struct gb_interrupts_t {
RRegItem *ime;
int fd;
ut16 flags;
// ut16 pc;
} GBInterrupts;
enum {
GB_INTERRUPTS_VBLANK = 0,
GB_INTERRUPTS_STAT,
GB_INTERRUPTS_TIMER,
GB_INTERRUPTS_SERIAL,
GB_INTERRUPTS_JOYPAD,
GB_INTERRUPTS_N, //number of interrupts
GB_INTERRUPTS_VBLANK_ENABLED = GB_INTERRUPTS_N,
GB_INTERRUPTS_STAT_ENABLED,
GB_INTERRUPTS_TIMER_ENABLED,
GB_INTERRUPTS_SERIAL_ENABLED,
GB_INTERRUPTS_JOYPAD_ENABLED,
GB_INTERRUPTS_ENABLED, //general enable of all interrupts
GB_INTERRUPTS_ENABLE_WAIT, //wait 1 instruction befor enabling interrupts
GB_INTERRUPTS_SEEK, //2 bits for seek
GB_INTERRUPT_VBLANK = 0,
GB_INTERRUPT_STAT,
GB_INTERRUPT_TIMER,
GB_INTERRUPT_SERIAL,
GB_INTERRUPT_JOYPAD,
GB_INTERRUPT_N, //number of interrupts
GB_INTERRUPT_VBLANK_ENABLED = GB_INTERRUPT_N,
GB_INTERRUPT_STAT_ENABLED,
GB_INTERRUPT_TIMER_ENABLED,
GB_INTERRUPT_SERIAL_ENABLED,
GB_INTERRUPT_JOYPAD_ENABLED,
GB_INTERRUPT_ENABLED, //general enable of all interrupts
GB_INTERRUPT_ENABLE_WAIT, //wait 1 instruction befor enabling interrupts
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);
typedef struct gb_dma_t {
@ -134,6 +137,11 @@ enum {
#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 {
ut32 data[10];
//low byte is x coordinate of sprite;
@ -212,8 +220,9 @@ typedef struct gb_dmg_ppu_t {
typedef struct gameboy_t {
RIO *io;
REsil *esil;
RArch *arch;
RAnal *anal;
REsil *esil;
GBTimers timers;
GBJoypad joypad;
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) {
GBInterrupts *ints = desc->data;
ut64 seek = (ints->flags >> GB_INTERRUPTS_SEEK) & 0x3;
ut64 seek = (ints->flags >> GB_INTERRUPT_SEEK) & 0x3;
switch (whence) {
case R_IO_SEEK_SET:
seek = R_MIN (2, offset);
@ -21,44 +21,44 @@ static ut64 __lseek(RIO* io, RIODesc *desc, ut64 offset, int whence) {
seek = 2;
break;
}
ints->flags &= ~(0x3 << GB_INTERRUPTS_SEEK);
ints->flags |= seek << GB_INTERRUPTS_SEEK;
ints->flags &= ~(0x3 << GB_INTERRUPT_SEEK);
ints->flags |= seek << GB_INTERRUPT_SEEK;
return seek;
}
static int __read(RIO *io, RIODesc *desc, ut8 *buf, int len) {
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) {
return 0;
}
len = R_MIN (len, 2 - seek);
ut32 i;
for (i = 0; i < len; i++) {
buf[i] = (ints->flags & (0x1f << (GB_INTERRUPTS_N * seek))) >>
(GB_INTERRUPTS_N * seek);
buf[i] = (ints->flags & (0x1f << (GB_INTERRUPT_N * seek))) >>
(GB_INTERRUPT_N * seek);
seek++;
}
ints->flags &= ~(0x3 << GB_INTERRUPTS_SEEK);
ints->flags |= seek << GB_INTERRUPTS_SEEK;
ints->flags &= ~(0x3 << GB_INTERRUPT_SEEK);
ints->flags |= seek << GB_INTERRUPT_SEEK;
return len;
}
static int __write(RIO *io, RIODesc *desc, const ut8 *buf, int len) {
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) {
return 0;
}
len = R_MIN (len, 2 - seek);
ut32 i;
for (i = 0; i < len; i++) {
ints->flags &= ~(0x1f << (GB_INTERRUPTS_N * seek));
ints->flags |= (buf[i] & 0x1f) << (GB_INTERRUPTS_N * seek);
ints->flags &= ~(0x1f << (GB_INTERRUPT_N * seek));
ints->flags |= (buf[i] & 0x1f) << (GB_INTERRUPT_N * seek);
seek++;
}
ints->flags &= ~(0x3 << GB_INTERRUPTS_SEEK);
ints->flags |= seek << GB_INTERRUPTS_SEEK;
ints->flags &= ~(0x3 << GB_INTERRUPT_SEEK);
ints->flags |= seek << GB_INTERRUPT_SEEK;
return len;
}
@ -96,8 +96,9 @@ RIOPlugin r_io_plugin_gb_interrupts = {
.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->ime = r_reg_get (reg, "ime", R_REG_TYPE_GPR);
char uri[64];
memset (uri, 0x00, sizeof (char) * 64);
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) {
r_unref (ints->ime);
r_io_fd_close (io, ints->fd);
}
void gb_interrupts_continue(GB *gb) {
//TODO
const bool ime = !!r_reg_get_value (gb->anal->reg, gb->interrupts.ime);
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) {
switch (interrupt) {
case GB_INTERRUPTS_VBLANK:
case GB_INTERRUPTS_STAT:
case GB_INTERRUPTS_TIMER:
case GB_INTERRUPTS_SERIAL:
case GB_INTERRUPTS_JOYPAD:
gb->interrupts.flags |= interrupt;
case GB_INTERRUPT_VBLANK:
case GB_INTERRUPT_STAT:
case GB_INTERRUPT_TIMER:
case GB_INTERRUPT_SERIAL:
case GB_INTERRUPT_JOYPAD:
gb->interrupts.flags |= (0x1 << interrupt);
break;
default:
break;

View File

@ -135,7 +135,7 @@ void gb_joypad_continue(GB *gb) {
}
joypad->data = (joypad->data & 0x30) | (ndata ^ 0xf);
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;
} while (cycles);
if (request_interrupt) {
gb_interrupts_request (gb, GB_INTERRUPTS_TIMER);
gb_interrupts_request (gb, GB_INTERRUPT_TIMER);
}
}