Initial implementation of gb timers

This commit is contained in:
condret 2024-10-06 02:09:42 +02:00
parent 3ef15b94aa
commit b033b8fb7c
2 changed files with 213 additions and 0 deletions

27
include/gb.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef GB_H
#define GB_H
#include <r_io.h>
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

186
io/timers.c Normal file
View File

@ -0,0 +1,186 @@
#include <stdio.h>
#include <gb.h>
#include <r_io.h>
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
}
}