189 lines
4.4 KiB
C
189 lines
4.4 KiB
C
#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);
|
|
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;
|
|
}
|