From b033b8fb7c7a494bc2f597bc27580faa54e976fe Mon Sep 17 00:00:00 2001 From: condret Date: Sun, 6 Oct 2024 02:09:42 +0200 Subject: [PATCH] Initial implementation of gb timers --- include/gb.h | 27 ++++++++ io/timers.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 include/gb.h create mode 100644 io/timers.c diff --git a/include/gb.h b/include/gb.h new file mode 100644 index 0000000..4c70d00 --- /dev/null +++ b/include/gb.h @@ -0,0 +1,27 @@ +#ifndef GB_H +#define GB_H +#include + +enum { + GB_TIMERS_DIV = 0, + GB_TIMERS_TIMA, + GB_TIMERS_TMA, + GB_TIMERS_TAC, +}; + +typedef struct gb_timers_t { + ut64 seek; + ut32 fd; + ut16 odiv; + ut16 div; + ut8 buf[4]; + ut8 otma; + st8 tima_wait; + bool check_fedge; +} GBTimers; + +GBTimers *gb_timers_open(RIO *io); +void gb_timers_open(RIO *io, GBTimers); +void gb_timers_update(GBTimers *timers, ut32 cycles); + +#endif diff --git a/io/timers.c b/io/timers.c new file mode 100644 index 0000000..c68503a --- /dev/null +++ b/io/timers.c @@ -0,0 +1,186 @@ +#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); + if (!desc) { + return NULL; + } + 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 NULL; + } + 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, +}; + +GBTimers *gb_timers_open (RIO *io) { + if (!io) { + return NULL; + } + GBTimers *timers = R_NEW0 (GBTimers); + if (!timers) { + return NULL; + } + 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) { + free (timers); + return NULL; + } + timers->fd = desc->fd; + return timers; +} + +void gb_timers_close (RIO *io, GBTimers *timers) { + if (!io || !timers) { + return; + } + r_io_fd_close (io, timers->fd); + free (timers); +} + +void gb_timers_update (GBTimers *timers, ut32 cycles) { + 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) { + //TODO + } +}