#include #include #include static ut64 __lseek(RIO* io, RIODesc *desc, ut64 offset, int whence) { GBTimers *timers = (GBTimers *)desc->data; switch (whence) { case R_IO_SEEK_SET: return timers->seek = R_MIN (4, offset); case R_IO_SEEK_CUR: return timers->seek = R_MIN (4, timers->seek + offset); case R_IO_SEEK_END: return timers->seek = 4; } return timers->seek; } static int __read(RIO *io, RIODesc *desc, ut8 *buf, int len) { GBTimers *timers = (GBTimers *)desc->data; if (timers->seek > 3 || len < 1) { return 0; } len = R_MIN (len, 4 - timers->seek); memcpy (buf, &timers->buf[timers->seek], len); timers->seek += len; return len; } static bool __check(RIO *io, const char *pathname, bool many) { return r_str_startswith (pathname, "gb_timers://"); } static RIODesc *__open(RIO *io, const char *pathname, int rw, int mode) { if (!r_str_startswith (pathname, "gb_timers://")) { return NULL; } GBTimers *timers = NULL; sscanf (pathname, "gb_timers://%p", &timers); RIODesc *desc = r_io_desc_new (io, &r_io_plugin_gb_timers, pathname, R_PERM_RWX, mode, timers); return desc; } static bool __close(RIODesc *desc) { return true; } static int __write(RIO *io, RIODesc *desc, const ut8 *buf, int len) { GBTimers *timers = (GBTimers *)desc->data; if (timers->seek == 4) { return 0; } int wlen = R_MIN (len, 4 - timers->seek); ut64 endseek = timers->seek + wlen; ut32 i; for (i = 0; timers->seek < endseek; timers->seek++) { switch (timers->seek) { case GB_TIMERS_DIV: timers->buf[GB_TIMERS_DIV] = 0; timers->div = 0; break; case GB_TIMERS_TIMA: timers->buf[GB_TIMERS_TIMA] = buf[i]; #if 0 if (timers->tima_wait > 1) { timers->tima_wait = 0; } #else timers->tima_wait = 0; #endif break; case GB_TIMERS_TMA: timers->otma = timers->buf[GB_TIMERS_TMA]; timers->buf[GB_TIMERS_TMA] = buf[i]; break; case GB_TIMERS_TAC: timers->check_fedge = !!(timers->buf[GB_TIMERS_TAC] & 0x4); timers->buf[GB_TIMERS_TAC] = buf[i]; if (!!(buf[i] & 0x4)) { timers->check_fedge = false;; } break; } i++; } return wlen; } RIOPlugin r_io_plugin_gb_timers = { .meta = { .name = "gb_timers", .desc = "gb_timers", .license = "LGPL", }, .uris = "gb_timers://", .open = __open, .close = __close, .read = __read, .check = __check, .seek = __lseek, .write = __write, }; bool gb_timers_init (GBTimers *timers, RIO *io) { if (!timers || !io) { return false; } timers[0] = (const GBTimers){0}; char uri[64]; memset (uri, 0x00, sizeof (char) * 64); sprintf (uri, "gb_timers://%p", timers); RIODesc *desc = r_io_desc_open_plugin (io, &r_io_plugin_gb_timers, uri, R_PERM_RWX, 0); if (!desc) { return false; } if (!r_io_map_add (io, desc->fd, R_PERM_RWX, 0, 0xff04, 4)) { r_io_desc_close (desc); return false; } timers->fd = desc->fd; return true; } void gb_timers_fini (GBTimers *timers, RIO *io) { if (!timers || !io) { return; } r_io_fd_close (io, timers->fd); } void gb_timers_continue (GB *gb, ut32 cycles) { GBTimers *timers = &gb->timers; bool request_interrupt = false; do { //ensure all falling edges are detected ut32 _cycles = R_MIN (((~timers->div) & 0x7) + 1, cycles); timers->odiv = timers->div; timers->div += _cycles; timers->buf[GB_TIMERS_DIV] = (timers->div >> 8) & 0xff; if (timers->tima_wait) { timers->tima_wait -= _cycles; if (timers->tima_wait <= 0) { timers->tima_wait = 0; timers->buf[GB_TIMERS_TIMA] = timers->otma; request_interrupt = true; } } if ((timers->buf[GB_TIMERS_TAC] & 0x4) || timers->check_fedge) { timers->otma = timers->buf[GB_TIMERS_TMA]; ut32 mask; switch (timers->buf[GB_TIMERS_TAC] & 0x3) { case 0: mask = 0x1 << 9; break; case 1: mask = 0x1 << 3; break; case 2: mask = 0x1 << 5; break; case 3: mask = 0x1 << 7; break; } if (((timers->odiv & mask) && !(timers->div & mask)) || ((timers->odiv & mask) && (timers->div & mask) && timers->check_fedge)) { if (timers->buf[GB_TIMERS_TIMA] == 0xff) { timers->tima_wait = 4; timers->buf[GB_TIMERS_TIMA] = 0x00; } else { timers->buf[GB_TIMERS_TIMA]++; } } timers->check_fedge = false; } cycles -= _cycles; } while (cycles); if (request_interrupt) { gb_interrupts_request (gb, GB_INTERRUPT_TIMER); } } void gb_timers_reset_div (GBTimers *timers) { timers->buf[GB_TIMERS_DIV] = 0; timers->div = 0; }