Implement oam dma

This commit is contained in:
condret 2024-10-26 05:01:28 +02:00
parent e6c5e00908
commit fe0a183ae5
3 changed files with 265 additions and 5 deletions

View File

@ -44,18 +44,25 @@ void gb_joypad_update(GBJoypad *joypad);
void gb_joypad_close(GBJoypad *joypad, RIO *io);
typedef struct gb_dma_t {
ut64 seek; //17 bit seek and some flags
int dma_fd; //fd representing the dma register
int dma_bus_fd; //fd representing the memory bus while dma occupies it
ut16 bus_occupancy_size;
ut32 dma_bank_id;
ut32 default_bank_id;
// ut16 bus_occupancy_size;
ut8 buf[0xa0];
ut8 dst; //current dst addr low byte
ut8 todo; //cycles todo
ut8 frontrun; //cycles that read/write ops had frontrun the dma copy
// ut8 todo; //cycles todo
st8 frontrun; //cycles that read/write ops had frontrun the dma copy
ut8 val;
} GBDMA;
GBDMA *gb_dma_open(RIO *io);
void gb_dma_update(GBDMA *dma, RIO *io, ut32 cycles);
#define GB_DMA_LAUNCH 0x20000
#define GB_DMA_RUNNING 0x40000
#define GB_DMA_ACTIVE 0x60000
GBDMA *gb_dma_open(RIO *io, bool cgb);
void gb_dma_update(GBDMA *dma, RIO *io, ut32 cycles, bool pre_exec);
void gb_dma_close(GBDMA *dma, RIO *io);
extern RIOPlugin r_io_plugin_gb_timers;

250
io/dma.c Normal file
View File

@ -0,0 +1,250 @@
#include <stdio.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 (timers->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, &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 (timers->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 0
if (len != _len) {
ut64 vseek = r_io_p2v (io, seek + _len);
r_io_bank_write_at (io, dma->default_bank_id, &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,
};
GBDMA *gb_dma_open (RIO *io, bool cgb) {
GBDMA *dma = R_NEW0 (GBDMA);
if (!dma) {
return NULL;
}
dma->default_bank_id = io->bank;
RIOBank *bank = r_io_bank_new (io, "dma bus");
if (!bank) {
free (dma);
return NULL;
}
if (!r_io_bank_add (io, bank)) {
r_io_bank_free (bank);
free (dma);
return NULL;
}
dma->dma_bank_id = bank->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_free (bank);
free (dma);
return NULL;
}
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_fd_close (dma->dma_fd);
r_io_bank_free (bank);
free (dma);
return NULL;
}
dma->dma_bus_fd = desc->fd;
return dma;
}
void gb_dma_update (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) {
r_io_bank_write_at (io, dma->default_bank_id, 0xfe00 | dma->dst,
&dma->buf[dma->dst], cycles);
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);
}
}

View File

@ -42,7 +42,10 @@ static int __read(RIO *io, RIODesc *desc, ut8 *buf, int len) {
if (!len || (joypad->odata & 0x40)) {
return 0;
}
#if 0
joypad->data = buf[0] & 0xf;
#else
buf[0] = joypad->data & 0x3f
joypad->odata |= 0x40;
return 1;
}