188 lines
4.3 KiB
C
188 lines
4.3 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <r_io.h>
|
|
#include <r_util.h>
|
|
#include <gb.h>
|
|
#include <ragb_sdl.h>
|
|
|
|
RIOPlugin r_io_plugin_gb_ppu;
|
|
|
|
static ut64 __lseek(RIO* io, RIODesc *desc, ut64 offset, int whence) {
|
|
GBPPU *ppu = desc->data;
|
|
ut64 seek = ppu->seek & 0xffff;
|
|
switch (whence) {
|
|
case R_IO_SEEK_SET:
|
|
seek = R_MIN (GB_PPU_N_REGS, offset);
|
|
break;
|
|
case R_IO_SEEK_CUR:
|
|
seek = R_MIN (GB_PPU_N_REGS, seek + offset);
|
|
break;
|
|
case R_IO_SEEK_END:
|
|
seek = GB_PPU_N_REGS;
|
|
break;
|
|
}
|
|
ppu->seek = (ppu->seek & (~0xffff)) | seek;
|
|
return seek;
|
|
}
|
|
|
|
static bool __check(RIO *io, const char *pathname, bool many) {
|
|
return r_str_startswith (pathname, "gb_ppu://");
|
|
}
|
|
|
|
static int __read(RIO *io, RIODesc *desc, ut8 *buf, int len) {
|
|
GBPPU *ppu = desc->data;
|
|
ut64 seek = ppu->seek & 0xffff;
|
|
if (ppu->seek >= GB_PPU_N_REGS || len < 1) {
|
|
return 0;
|
|
}
|
|
len = R_MIN (len, GB_PPU_N_REGS - seek);
|
|
memcpy (buf, &ppu->buf[ppu->seek], len);
|
|
seek += len;
|
|
ppu->seek = (ppu->seek & (~0xffff)) | seek;
|
|
return len;
|
|
}
|
|
|
|
static int __write(RIO *io, RIODesc *desc, const ut8 *buf, int len) {
|
|
GBPPU *ppu = desc->data;
|
|
ut64 seek = ppu->seek & 0xffff;
|
|
if (ppu->seek >= GB_PPU_N_REGS || len < 1) {
|
|
return 0;
|
|
}
|
|
len = R_MIN (len, GB_PPU_N_REGS - seek);
|
|
ut32 i;
|
|
for (i = 0; i < len; i++) {
|
|
switch (seek) {
|
|
case GB_PPU_STAT:
|
|
ppu->buf[GB_PPU_STAT] = (buf[i] & 0xf8) | (ppu->buf[GB_PPU_STAT] & 0x7);
|
|
case GB_PPU_LY:
|
|
break;
|
|
default:
|
|
ppu->buf[seek] = buf[i];
|
|
break;
|
|
}
|
|
seek++;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
static bool __close(RIODesc *desc) {
|
|
return true;
|
|
}
|
|
|
|
static RIODesc *__open(RIO *io, const char *pathname, int rw, int mode) {
|
|
if (!r_str_startswith (pathname, "gb_ppu://")) {
|
|
return NULL;
|
|
}
|
|
GBPPU *ppu = NULL;
|
|
sscanf (pathname, "gb_ppu://%p", &ppu);
|
|
RIODesc *desc = r_io_desc_new (io, &r_io_plugin_gb_ppu, pathname,
|
|
R_PERM_RWX, mode, ppu);
|
|
return desc;
|
|
}
|
|
|
|
RIOPlugin r_io_plugin_gb_ppu = {
|
|
.meta = {
|
|
.name = "gb_ppu",
|
|
.desc = "gb_ppu",
|
|
.license = "LGPL",
|
|
},
|
|
.uris = "gb_ppu://",
|
|
.open = __open,
|
|
.close = __close,
|
|
.read = __read,
|
|
.check = __check,
|
|
.seek = __lseek,
|
|
.write = __write,
|
|
};
|
|
|
|
GBPPU *gb_ppu_open (RIO *io, SDL_Renderer *renderer) {
|
|
GBPPU *ppu = R_NEW0 (GBPPU);
|
|
if (!ppu) {
|
|
return NULL;
|
|
}
|
|
ppu->vram_fd = r_io_fd_open (io, "malloc//:0x2000", R_PERM_RWX, 0);
|
|
if (ppu->vram_fd < 0) {
|
|
free (ppu);
|
|
return NULL;
|
|
}
|
|
char uri[64];
|
|
sprintf (uri, "gb_ppu://%p", ppu);
|
|
RIODesc *desc = r_io_desc_open_plugin (io, &r_io_plugin_gb_ppu, uri, R_PERM_RWX, 0);
|
|
if (!desc) {
|
|
r_io_fd_close (io, ppu->vram_fd);
|
|
free (ppu);
|
|
return NULL;
|
|
}
|
|
ppu->reg_fd = desc->fd;
|
|
ppu->pixbuf = gb_pix_buf_new (renderer, 160, 144);
|
|
if (!ppu->pixbuf) {
|
|
r_io_desc_close (desc);
|
|
r_io_fd_close (io, ppu->vram_fd);
|
|
free (ppu);
|
|
return NULL;
|
|
}
|
|
return ppu;
|
|
}
|
|
|
|
static ut32 gb_ppu_oam_scan_update (GB *gb, ut32 cycles) {
|
|
if (gb->ppu->ost.addr >= 0xa0) {
|
|
gb->ppu->ost.addr = 0;
|
|
gb->ppu->ost.n_entries = 0;
|
|
}
|
|
if (cycles & 0x1) {
|
|
R_LOG_WARN ("Odd amount of cycles");
|
|
}
|
|
if (gb->dma->seek & GB_DMA_ACTIVE) {
|
|
const ut8 running_cycles = R_MIN (cycles, (0xa0 - gb->ppu->ost.addr) >> 1);
|
|
//every oam entry costs 2 cycles
|
|
gb->ppu->ost.addr += running_cycles << 1;
|
|
cycles -= running_cycles;
|
|
goto beach;
|
|
}
|
|
const ut8 height = gb->ppu->buf[GB_PPU_LCDC] & 0x3? 16: 8;
|
|
const ut8 ly = gb->ppu->buf[GB_PPU_LY] + 16;
|
|
while (cycles && gb->ppu->ost.addr <= 0xa0) {
|
|
if (gb->ppu->ost.n_entries < 10) {
|
|
ut8 yx[2];
|
|
r_io_fd_read_at (gb->io, gb->dma->oam_fd, (ut64)gb->ppu->ost.addr, yx, 2);
|
|
if ((yx[0] <= ly) && (ly < (yx[0] + height))) {
|
|
gb->ppu->ost.data[gb->ppu->ost.n_entries] =
|
|
(gb->ppu->ost.addr << 8) | yx[1];
|
|
gb->ppu->ost.n_entries++;
|
|
}
|
|
}
|
|
gb->ppu->ost.addr += 4;
|
|
cycles -= 2;
|
|
}
|
|
beach:
|
|
if (gb->ppu->ost.addr == 0xa0) {
|
|
//indicate next mode
|
|
gb->ppu->buf[GB_PPU_STAT] |= GB_PPU_STAT_MODE_RENDER;
|
|
}
|
|
return cycles;
|
|
}
|
|
|
|
void gb_ppu_update (GB *gb, ut32 cycles) {
|
|
while (cycles) {
|
|
switch (gb->ppu->buf[GB_PPU_STAT] & GB_PPU_STAT_MODE_MASK) {
|
|
case GB_PPU_STAT_MODE_OAM_SCAN:
|
|
cycles = gb_ppu_oam_scan_update (gb, cycles);
|
|
break;
|
|
case GB_PPU_STAT_MODE_RENDER:
|
|
break;
|
|
case GB_PPU_STAT_MODE_HBLANK:
|
|
break;
|
|
case GB_PPU_STAT_MODE_VBLANK:
|
|
break;
|
|
}
|
|
}
|
|
//TODO
|
|
}
|
|
|
|
void gb_ppu_close (GBPPU *ppu, RIO *io) {
|
|
r_io_fd_close (io, ppu->vram_fd);
|
|
r_io_fd_close (io, ppu->reg_fd);
|
|
gb_pix_buf_free (ppu->pixbuf);
|
|
free (ppu);
|
|
}
|