#include #include #include #include #include #include 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; } }