From e94520f6cd67fc9b74ae5bfcc6d434aab5039ca0 Mon Sep 17 00:00:00 2001 From: condret Date: Sat, 9 Nov 2024 18:10:11 +0100 Subject: [PATCH] Implement interrupt handling --- include/gb.h | 41 +++++++++++++++---------- io/interrupts.c | 81 +++++++++++++++++++++++++++++++++++-------------- io/joypad.c | 2 +- io/timers.c | 2 +- 4 files changed, 86 insertions(+), 40 deletions(-) diff --git a/include/gb.h b/include/gb.h index ac768aa..9a981cd 100644 --- a/include/gb.h +++ b/include/gb.h @@ -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; diff --git a/io/interrupts.c b/io/interrupts.c index d769822..d9ff29d 100644 --- a/io/interrupts.c +++ b/io/interrupts.c @@ -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; diff --git a/io/joypad.c b/io/joypad.c index 3f547fc..1e6d772 100644 --- a/io/joypad.c +++ b/io/joypad.c @@ -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); } } diff --git a/io/timers.c b/io/timers.c index d158ebf..dd8f89e 100644 --- a/io/timers.c +++ b/io/timers.c @@ -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); } }