From fe0a183ae5d8cea5eb4b97555b0f9bcd9ec6c5eb Mon Sep 17 00:00:00 2001 From: condret Date: Sat, 26 Oct 2024 05:01:28 +0200 Subject: [PATCH] Implement oam dma --- include/gb.h | 17 ++-- io/dma.c | 250 +++++++++++++++++++++++++++++++++++++++++++++++++++ io/joypad.c | 3 + 3 files changed, 265 insertions(+), 5 deletions(-) create mode 100644 io/dma.c diff --git a/include/gb.h b/include/gb.h index ca1f0b3..031dc50 100644 --- a/include/gb.h +++ b/include/gb.h @@ -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; diff --git a/io/dma.c b/io/dma.c new file mode 100644 index 0000000..cf8b42f --- /dev/null +++ b/io/dma.c @@ -0,0 +1,250 @@ +#include +#include +#include +#include + +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); + } +} diff --git a/io/joypad.c b/io/joypad.c index 308b23d..518efda 100644 --- a/io/joypad.c +++ b/io/joypad.c @@ -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; }