303 lines
7.2 KiB
C
303 lines
7.2 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <gb.h>
|
|
#include <r_io.h>
|
|
#include <r_util.h>
|
|
|
|
RIOPlugin r_io_plugin_gb_dma;
|
|
RIOPlugin r_io_plugin_gb_dma_bus;
|
|
|
|
static ut64 __bus_lseek(RIO* io, RIODesc *desc, ut64 offset, int whence) {
|
|
GBDMA *dma = desc->data;
|
|
ut64 seek = dma->seek & 0xffff;
|
|
switch (whence) {
|
|
case R_IO_SEEK_SET:
|
|
seek = R_MIN (0xff80, offset);
|
|
break;
|
|
case R_IO_SEEK_CUR:
|
|
seek = R_MIN (0xff80, seek + offset);
|
|
break;
|
|
case R_IO_SEEK_END:
|
|
seek = 0xff80;
|
|
break;
|
|
}
|
|
dma->seek = (dma->seek & (~0xffff)) | seek;
|
|
return seek;
|
|
}
|
|
|
|
static bool __bus_check(RIO *io, const char *pathname, bool many) {
|
|
return r_str_startswith (pathname, "gb_dma_bus://");
|
|
}
|
|
|
|
static int __bus_read(RIO *io, RIODesc *desc, ut8 *buf, int len) {
|
|
GBDMA *dma = desc->data;
|
|
ut64 seek = dma->seek & 0xffff;
|
|
if (dma->seek > 0xff7f || len < 1) {
|
|
return 0;
|
|
}
|
|
len = R_MIN (len, 0xff80 - seek);
|
|
int _len = R_MIN (len, 0xa0 - ((ut32)dma->dst + (st32)dma->frontrun));
|
|
memcpy (buf, &dma->buf[dma->dst + dma->frontrun], _len);
|
|
dma->frontrun += _len;
|
|
#if 1
|
|
if (len != _len) {
|
|
ut64 vseek = r_io_p2v (io, seek + _len);
|
|
r_io_bank_read_at (io, dma->default_bank_id, vseek, &buf[_len], len - _len);
|
|
}
|
|
#endif
|
|
seek += len;
|
|
dma->seek = (dma->seek & (~0xffff)) | seek;
|
|
return len;
|
|
}
|
|
|
|
static int __bus_write(RIO *io, RIODesc *desc, const ut8 *buf, int len) {
|
|
GBDMA *dma = desc->data;
|
|
ut64 seek = dma->seek & 0xffff;
|
|
if (dma->seek > 0xff7f || len < 1) {
|
|
return 0;
|
|
}
|
|
len = R_MIN (len, 0xff80 - seek);
|
|
int _len = R_MIN (len, 0xa0 - ((ut32)dma->dst + (st32)dma->frontrun));
|
|
// memcpy (buf, &dma->buf[dma->dst + dma->frontrun], _len);
|
|
dma->frontrun += _len;
|
|
#if 1
|
|
if (len != _len) {
|
|
ut64 vseek = r_io_p2v (io, seek + _len);
|
|
r_io_bank_write_at (io, dma->default_bank_id, vseek, &buf[_len], len - _len);
|
|
}
|
|
#endif
|
|
seek += len;
|
|
dma->seek = (dma->seek & (~0xffff)) | seek;
|
|
return len;
|
|
}
|
|
|
|
static bool __close(RIODesc *desc) {
|
|
return true;
|
|
}
|
|
|
|
static RIODesc *__bus_open(RIO *io, const char *pathname, int rw, int mode) {
|
|
if (!r_str_startswith (pathname, "gb_dma_bus://")) {
|
|
return NULL;
|
|
}
|
|
GBDMA *dma = NULL;
|
|
sscanf (pathname, "gb_dma_bus://%p", &dma);
|
|
RIODesc *desc = r_io_desc_new (io, &r_io_plugin_gb_dma_bus, pathname,
|
|
R_PERM_RWX, mode, dma);
|
|
return desc;
|
|
}
|
|
|
|
RIOPlugin r_io_plugin_gb_dma_bus = {
|
|
.meta = {
|
|
.name = "gb_dma_bus",
|
|
.desc = "gb_dma_bus",
|
|
.license = "LGPL",
|
|
},
|
|
.uris = "gb_dma_bus://",
|
|
.open = __bus_open,
|
|
.close = __close,
|
|
.read = __bus_read,
|
|
.check = __bus_check,
|
|
.seek = __bus_lseek,
|
|
.write = __bus_write,
|
|
};
|
|
|
|
static bool __check(RIO *io, const char *pathname, bool many) {
|
|
return r_str_startswith (pathname, "gb_dma://");
|
|
}
|
|
|
|
static RIODesc *__open(RIO *io, const char *pathname, int rw, int mode) {
|
|
if (!r_str_startswith (pathname, "gb_dma://")) {
|
|
return NULL;
|
|
}
|
|
GBDMA *dma = NULL;
|
|
sscanf (pathname, "gb_dma://%p", &dma);
|
|
RIODesc *desc = r_io_desc_new (io, &r_io_plugin_gb_dma_bus, pathname,
|
|
R_PERM_RWX, mode, dma);
|
|
return desc;
|
|
}
|
|
|
|
static ut64 __lseek(RIO* io, RIODesc *desc, ut64 offset, int whence) {
|
|
GBDMA *dma = desc->data;
|
|
ut64 seek = (dma->seek & 0x10000) >> 16;
|
|
switch (whence) {
|
|
case R_IO_SEEK_SET:
|
|
seek = R_MIN (1, offset);
|
|
break;
|
|
case R_IO_SEEK_CUR:
|
|
seek = R_MIN (1, seek + offset);
|
|
break;
|
|
case R_IO_SEEK_END:
|
|
seek = 1;
|
|
break;
|
|
}
|
|
dma->seek = (dma->seek & (~0x10000)) | (seek << 16);
|
|
return seek;
|
|
}
|
|
|
|
static int __read(RIO *io, RIODesc *desc, ut8 *buf, int len) {
|
|
GBDMA *dma = desc->data;
|
|
if (!len || (dma->seek & 0x10000)) {
|
|
return 0;
|
|
}
|
|
buf[0] = dma->val;
|
|
dma->seek |= 0x10000;
|
|
return 1;
|
|
}
|
|
|
|
static int __write(RIO *io, RIODesc *desc, const ut8 *buf, int len) {
|
|
GBDMA *dma = desc->data;
|
|
if (!len || (dma->seek & 0x10000)) {
|
|
return 0;
|
|
}
|
|
dma->val = buf[0];
|
|
dma->seek |= 0x10000;
|
|
if (R_LIKELY (!(dma->seek & GB_DMA_RUNNING) && dma->val < 0xe0)) {
|
|
dma->seek |= GB_DMA_LAUNCH;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
RIOPlugin r_io_plugin_gb_dma = {
|
|
.meta = {
|
|
.name = "gb_dma",
|
|
.desc = "gb_dma",
|
|
.license = "LGPL",
|
|
},
|
|
.uris = "gb_dma://",
|
|
.open = __open,
|
|
.close = __close,
|
|
.read = __read,
|
|
.check = __check,
|
|
.seek = __lseek,
|
|
.write = __write,
|
|
};
|
|
|
|
|
|
bool gb_dma_init (GBDMA *dma, RIO *io) {
|
|
if (!dma || !io) {
|
|
return false;
|
|
}
|
|
dma[0] = (const GBDMA) {0};
|
|
dma->default_bank_id = io->bank;
|
|
RIOBank *bank = r_io_bank_new ("dma bus");
|
|
if (!bank) {
|
|
return false;
|
|
}
|
|
if (!r_io_bank_add (io, bank)) {
|
|
r_io_bank_free (bank);
|
|
return false;
|
|
}
|
|
dma->dma_bank_id = bank->id;
|
|
dma->oam_fd = r_io_fd_open (io, "malloc://0xa0", R_PERM_RWX, 0);
|
|
if (dma->oam_fd < 0) {
|
|
r_io_bank_del (io, bank->id);
|
|
r_io_bank_free (bank);
|
|
r_io_fd_close (io, dma->oam_fd);
|
|
return false;
|
|
}
|
|
RIOMap *oam_map = r_io_map_add (io, dma->oam_fd, R_PERM_RWX, 0, 0xffe0, 0xa0);
|
|
if (!oam_map) {
|
|
r_io_bank_del (io, bank->id);
|
|
r_io_bank_free (bank);
|
|
r_io_fd_close (io, dma->oam_fd);
|
|
return false;
|
|
}
|
|
dma->oam_mapid = oam_map->id;
|
|
char uri[64];
|
|
memset (uri, 0x00, sizeof (char) * 64);
|
|
sprintf (uri, "gb_dma://%p", dma);
|
|
RIODesc *desc = r_io_desc_open_plugin (io, &r_io_plugin_gb_dma,
|
|
uri, R_PERM_RWX, 0);
|
|
if (!desc) {
|
|
r_io_bank_del (io, bank->id);
|
|
r_io_bank_free (bank);
|
|
r_io_fd_close (io, dma->oam_fd);
|
|
return false;
|
|
}
|
|
if (!r_io_map_add (io, dma->dma_fd, R_PERM_RWX, 0, 0xff46, 1)) {
|
|
r_io_bank_del (io, bank->id);
|
|
r_io_bank_free (bank);
|
|
r_io_fd_close (io, dma->oam_fd);
|
|
r_io_desc_close (desc);
|
|
}
|
|
dma->dma_fd = desc->fd;
|
|
sprintf (uri, "gb_dma_bus://%p", dma);
|
|
desc = r_io_desc_open_plugin (io, &r_io_plugin_gb_dma_bus, uri, R_PERM_RWX, 0);
|
|
if (!desc) {
|
|
r_io_bank_del (io, dma->dma_bank_id);
|
|
r_io_bank_free (bank);
|
|
r_io_fd_close (io, dma->oam_fd);
|
|
r_io_fd_close (io, dma->dma_fd);
|
|
return false;
|
|
}
|
|
r_io_bank_use (io, dma->dma_bank_id);
|
|
if (!r_io_map_add (io, desc->fd, R_PERM_RWX, 0, 0, 0xff80)) {
|
|
r_io_bank_use (io, dma->default_bank_id);
|
|
r_io_bank_del (io, dma->dma_bank_id);
|
|
r_io_bank_free (bank);
|
|
r_io_fd_close (io, dma->oam_fd);
|
|
r_io_fd_close (io, dma->dma_fd);
|
|
r_io_desc_close (desc);
|
|
return false;
|
|
}
|
|
r_io_bank_use (io, dma->default_bank_id);
|
|
dma->dma_bus_fd = desc->fd;
|
|
return true;
|
|
}
|
|
|
|
void gb_dma_continue (GBDMA *dma, RIO *io, ut32 cycles, bool pre_exec) {
|
|
if (!(dma->seek & GB_DMA_ACTIVE)) {
|
|
return;
|
|
}
|
|
cycles = cycles >> 2;
|
|
if (!cycles) {
|
|
return;
|
|
}
|
|
if (dma->seek & GB_DMA_LAUNCH) {
|
|
r_io_read_at (io, dma->val << 8, dma->buf, 0xa0);
|
|
dma->seek &= (~GB_DMA_LAUNCH);
|
|
dma->dst = 0;
|
|
r_io_bank_use (io, dma->dma_bank_id);
|
|
cycles--;
|
|
dma->seek |= GB_DMA_RUNNING;
|
|
if (!cycles) {
|
|
dma->frontrun = 0;
|
|
return;
|
|
}
|
|
}
|
|
cycles = R_MIN (0xa0 - dma->dst, cycles);
|
|
if (pre_exec) {
|
|
dma->frontrun = ((st32)cycles) * (-1);
|
|
}
|
|
if (cycles) {
|
|
#if 1
|
|
r_io_fd_write_at (io, dma->oam_fd, (ut64)dma->dst,
|
|
&dma->buf[dma->dst], cycles);
|
|
#else
|
|
r_io_bank_write_at (io, dma->default_bank_id, 0xfe00 | dma->dst,
|
|
&dma->buf[dma->dst], cycles);
|
|
#endif
|
|
dma->dst += cycles;
|
|
}
|
|
if (pre_exec) {
|
|
return;
|
|
}
|
|
if (dma->dst >= 0xa0) {
|
|
r_io_bank_use (io, dma->default_bank_id);
|
|
dma->seek &= (~GB_DMA_ACTIVE);
|
|
}
|
|
}
|
|
|
|
void gb_dma_fini (GBDMA *dma, RIO *io) {
|
|
if (!dma || !io) {
|
|
return;
|
|
}
|
|
r_io_bank_use (io, dma->default_bank_id);
|
|
r_io_fd_close (io, dma->dma_bus_fd);
|
|
r_io_fd_close (io, dma->dma_fd);
|
|
r_io_fd_close (io, dma->oam_fd);
|
|
RIOBank *bank = r_io_bank_get (io, dma->dma_bank_id);
|
|
r_io_bank_del (io, dma->dma_bank_id);
|
|
r_io_bank_free (bank);
|
|
}
|