Implement interrupt handling
This commit is contained in:
parent
27bc6bcc39
commit
e94520f6cd
41
include/gb.h
41
include/gb.h
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user