#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); } } void gb_dma_close (GBDMA *dma, RIO *io) { if (!dma || !io) { return; } r_io_bank_use (io, dma->default_bank_id); r_io_bank_del (io, dma->dma_bank_id); r_io_fd_close (io, dma->dma_bus_fd); r_io_fd_close (io, dma->dma_fd); free (dma); }