Initial implementation of gb timers
This commit is contained in:
parent
3ef15b94aa
commit
b033b8fb7c
27
include/gb.h
Normal file
27
include/gb.h
Normal 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
186
io/timers.c
Normal 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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user