201 lines
5.6 KiB
C
201 lines
5.6 KiB
C
#include <gb.h>
|
|
#include <r_io.h>
|
|
#include <r_util.h>
|
|
#include <r_esil.h>
|
|
#include <r_anal.h>
|
|
#include <r_reg.h>
|
|
|
|
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_INTERRUPT_SEEK) & 0x3;
|
|
switch (whence) {
|
|
case R_IO_SEEK_SET:
|
|
seek = R_MIN (2, offset);
|
|
break;
|
|
case R_IO_SEEK_CUR:
|
|
seek = R_MIN (2, seek + offset);
|
|
break;
|
|
case R_IO_SEEK_END:
|
|
seek = 2;
|
|
break;
|
|
}
|
|
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_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_INTERRUPT_N * seek))) >>
|
|
(GB_INTERRUPT_N * seek);
|
|
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_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_INTERRUPT_N * seek));
|
|
ints->flags |= (buf[i] & 0x1f) << (GB_INTERRUPT_N * seek);
|
|
seek++;
|
|
}
|
|
ints->flags &= ~(0x3 << GB_INTERRUPT_SEEK);
|
|
ints->flags |= seek << GB_INTERRUPT_SEEK;
|
|
return len;
|
|
}
|
|
|
|
static bool __check(RIO *io, const char *pathname, bool many) {
|
|
return r_str_startswith (pathname, "gb_interrupts://");
|
|
}
|
|
|
|
static RIODesc *__open(RIO *io, const char *pathname, int rw, int mode) {
|
|
if (!r_str_startswith (pathname, "gb_interrupts://")) {
|
|
return NULL;
|
|
}
|
|
GBInterrupts *ints = NULL;
|
|
sscanf (pathname, "gb_interrupts://%p", &ints);
|
|
RIODesc *desc = r_io_desc_new (io, &r_io_plugin_gb_interrupts, pathname,
|
|
R_PERM_RWX, mode, ints);
|
|
return desc;
|
|
}
|
|
|
|
static bool __close(RIODesc *desc) {
|
|
return true;
|
|
}
|
|
|
|
RIOPlugin r_io_plugin_gb_interrupts = {
|
|
.meta = {
|
|
.name = "gb_interrupts",
|
|
.desc = "gb_interrupts",
|
|
.license = "LGPL",
|
|
},
|
|
.uris = "gb_interrupts://",
|
|
.open = __open,
|
|
.close = __close,
|
|
.read = __read,
|
|
.check = __check,
|
|
.seek = __lseek,
|
|
.write = __write,
|
|
};
|
|
|
|
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);
|
|
RIODesc *desc = r_io_desc_open_plugin (io, &r_io_plugin_gb_interrupts,
|
|
uri, R_PERM_RWX, 0);
|
|
if (!desc) {
|
|
return false;
|
|
}
|
|
//map IF register
|
|
if (!r_io_map_add (io, desc->fd, R_PERM_RWX, 0, 0xff0f, 1)) {
|
|
r_io_desc_close (desc);
|
|
return false;
|
|
}
|
|
//map IE register
|
|
if (!r_io_map_add (io, desc->fd, R_PERM_RWX, 1, 0xffff, 1)) {
|
|
r_io_desc_close (desc);
|
|
//r_io will automatically remove the map for IF register
|
|
return false;
|
|
}
|
|
ints->fd = desc->fd;
|
|
return true;
|
|
}
|
|
|
|
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) {
|
|
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)) {
|
|
gb->interrupts.flags ^= (0x1 << GB_INTERRUPT_ENABLE_WAIT);
|
|
if (!ime) {
|
|
return;
|
|
}
|
|
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))) {
|
|
//wait 1 instruction
|
|
gb->interrupts.flags |= (0x1 << GB_INTERRUPT_ENABLE_WAIT);
|
|
return;
|
|
} else if (!ime) {
|
|
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;
|
|
}
|
|
if (!(gb->interrupts.flags & (0x1 << GB_INTERRUPT_WAIT_TRIGGER))) {
|
|
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) {
|
|
gb->interrupts.flags |= (0x1 << GB_INTERRUPT_WAIT_TRIGGER);
|
|
//stop exection of instructions for 20 cycles
|
|
//gb_pause_exec (gb, 20);
|
|
gb->pause_cycles += 20;
|
|
//save pending interrupt index
|
|
gb->interrupts.flags &= ~(0x7 << 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;
|
|
}
|
|
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;
|
|
const ut32 interrupt = (gb->interrupts.flags & (0x7 << GB_INTERRUPT_PENDING)) >> GB_INTERRUPT_PENDING;
|
|
gb->interrupts.flags &= ~(0x1 << interrupt);
|
|
const ut64 interrupt_vector = 0x40 + (interrupt << 3);
|
|
//maybe make this a static buffer?
|
|
char *interrupt_expr = r_str_newf (dec_pc ?
|
|
"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);
|
|
free (interrupt_expr);
|
|
}
|
|
|
|
void gb_interrupts_request (GB *gb, int interrupt) {
|
|
switch (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;
|
|
}
|
|
}
|