DS GX: Start implementing FIFO
Vicki Pfau vi@endrift.com
Sun, 26 Feb 2017 13:04:18 -0800
4 files changed,
158 insertions(+),
4 deletions(-)
M
include/mgba/internal/ds/gx.h
→
include/mgba/internal/ds/gx.h
@@ -16,6 +16,20 @@ #include <mgba-util/circle-buffer.h>
mLOG_DECLARE_CATEGORY(DS_GX); +DECL_BITFIELD(DSRegGXSTAT, uint32_t); +DECL_BIT(DSRegGXSTAT, TestBusy, 0); +DECL_BIT(DSRegGXSTAT, BoxTestResult, 1); +DECL_BITS(DSRegGXSTAT, PVMatrixStackLevel, 8, 5); +DECL_BIT(DSRegGXSTAT, ProjMatrixStackLevel, 13); +DECL_BIT(DSRegGXSTAT, MatrixStackBusy, 14); +DECL_BIT(DSRegGXSTAT, MatrixStackError, 15); +DECL_BITS(DSRegGXSTAT, FIFOEntries, 16, 9); +DECL_BIT(DSRegGXSTAT, FIFOFull, 24); +DECL_BIT(DSRegGXSTAT, FIFOLtHalf, 25); +DECL_BIT(DSRegGXSTAT, FIFOEmpty, 26); +DECL_BIT(DSRegGXSTAT, Busy, 27); +DECL_BITS(DSRegGXSTAT, DoIRQ, 30, 2); + enum DSGXCommand { DS_GX_CMD_NOP = 0, DS_GX_CMD_MTX_MODE = 0x10,@@ -73,6 +87,8 @@ struct DSGXEntry pipe[4];
struct CircleBuffer fifo; struct mTimingEvent fifoEvent; + + bool swapBuffers; }; void DSGXInit(struct DSGX*);@@ -81,6 +97,9 @@ void DSGXReset(struct DSGX*);
uint16_t DSGXWriteRegister(struct DSGX*, uint32_t address, uint16_t value); uint32_t DSGXWriteRegister32(struct DSGX*, uint32_t address, uint32_t value); + +void DSGXSwapBuffers(struct DSGX*); +void DSGXUpdateGXSTAT(struct DSGX*); CXX_GUARD_END
M
src/ds/gx.c
→
src/ds/gx.c
@@ -5,6 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <mgba/internal/ds/gx.h> +#include <mgba/internal/ds/ds.h> #include <mgba/internal/ds/io.h> mLOG_DEFINE_CATEGORY(DS_GX, "DS GX");@@ -52,8 +53,49 @@ [DS_GX_CMD_POS_TEST] = 18,
[DS_GX_CMD_VEC_TEST] = 10, }; +static void _fifoRun(struct mTiming* timing, void* context, uint32_t cyclesLate) { + struct DSGX* gx = context; + uint32_t cycles; + while (true) { + struct DSGXEntry entry = { 0 }; + CircleBufferRead8(&gx->fifo, (int8_t*) &entry.command); + CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[0]); + CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[1]); + CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[2]); + CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[3]); + cycles = _gxCommandCycleBase[entry.command]; + + switch (entry.command) { + case DS_GX_CMD_SWAP_BUFFERS: + gx->swapBuffers = true; + break; + default: + mLOG(DS_GX, STUB, "Unimplemented GX command %02X:%02X %02X %02X %02X", entry.command, entry.params[0], entry.params[1], entry.params[2], entry.params[3]); + break; + } + if (CircleBufferSize(&gx->fifo)) { + if (cycles <= cyclesLate) { + cyclesLate -= cycles; + } else { + break; + } + } else { + cycles = 0; + break; + } + } + DSGXUpdateGXSTAT(gx); + if (cycles) { + mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, cycles); + } +} + void DSGXInit(struct DSGX* gx) { CircleBufferInit(&gx->fifo, sizeof(struct DSGXEntry) * DS_GX_FIFO_SIZE); + gx->fifoEvent.name = "DS GX FIFO"; + gx->fifoEvent.priority = 0xC; + gx->fifoEvent.context = gx; + gx->fifoEvent.callback = _fifoRun; } void DSGXDeinit(struct DSGX* gx) {@@ -62,20 +104,86 @@ }
void DSGXReset(struct DSGX* gx) { CircleBufferClear(&gx->fifo); + gx->swapBuffers = false; +} + +void DSGXUpdateGXSTAT(struct DSGX* gx) { + uint32_t value = gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] << 16; + value = DSRegGXSTATIsDoIRQ(value); + + size_t entries = CircleBufferSize(&gx->fifo) / sizeof(struct DSGXEntry); + // XXX + if (gx->swapBuffers) { + entries++; + } + value = DSRegGXSTATSetFIFOEntries(value, entries); + value = DSRegGXSTATSetFIFOLtHalf(value, entries < (DS_GX_FIFO_SIZE / 2)); + value = DSRegGXSTATSetFIFOEmpty(value, entries == 0); + + if ((DSRegGXSTATGetDoIRQ(value) == 1 && entries < (DS_GX_FIFO_SIZE / 2)) || + (DSRegGXSTATGetDoIRQ(value) == 2 && entries == 0)) { + DSRaiseIRQ(gx->p->ds9.cpu, gx->p->ds9.memory.io, DS_IRQ_GEOM_FIFO); + } + + value = DSRegGXSTATSetBusy(value, mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent) || gx->swapBuffers); + + gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] = value >> 16; +} + +static void DSGXWriteFIFO(struct DSGX* gx, struct DSGXEntry entry) { + uint32_t cycles = _gxCommandCycleBase[entry.command]; + if (!cycles) { + return; + } + // TODO: Outstanding parameters + if (CircleBufferSize(&gx->fifo) < (DS_GX_FIFO_SIZE * sizeof(entry))) { + CircleBufferWrite8(&gx->fifo, entry.command); + CircleBufferWrite8(&gx->fifo, entry.params[0]); + CircleBufferWrite8(&gx->fifo, entry.params[1]); + CircleBufferWrite8(&gx->fifo, entry.params[2]); + CircleBufferWrite8(&gx->fifo, entry.params[3]); + if (!mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent)) { + mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, 0); + } + } else { + mLOG(DS_GX, STUB, "Unimplemented GX full"); + } } uint16_t DSGXWriteRegister(struct DSGX* gx, uint32_t address, uint16_t value) { + uint16_t oldValue = gx->p->memory.io9[address >> 1]; switch (address) { case DS9_REG_DISP3DCNT: + mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value); + break; case DS9_REG_GXSTAT_LO: + value = DSRegGXSTATIsMatrixStackError(value); + if (value) { + oldValue = DSRegGXSTATClearMatrixStackError(oldValue); + oldValue = DSRegGXSTATClearProjMatrixStackLevel(oldValue); + } + value = oldValue; + break; case DS9_REG_GXSTAT_HI: - mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value); + value = DSRegGXSTATIsDoIRQ(value << 16) >> 16; + gx->p->memory.io9[address >> 1] = value; + DSGXUpdateGXSTAT(gx); + value = gx->p->memory.io9[address >> 1]; break; default: if (address < DS9_REG_GXFIFO_00) { mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value); } else if (address < DS9_REG_GXSTAT_LO) { - mLOG(DS_GX, STUB, "Unimplemented FIFO write %03X:%04X", address, value); + struct DSGXEntry entry = { + .command = (address & 0x1FC) >> 2, + .params = { + value, + value >> 8, + } + }; + if (entry.command < 0x80) { + DSGXWriteFIFO(gx, entry); + } } else { mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value); }@@ -87,14 +195,26 @@
uint32_t DSGXWriteRegister32(struct DSGX* gx, uint32_t address, uint32_t value) { switch (address) { case DS9_REG_DISP3DCNT: + mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value); + break; case DS9_REG_GXSTAT_LO: - mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value); + value = (value & 0xFFFF0000) | DSGXWriteRegister(gx, DS9_REG_GXSTAT_LO, value); + value = (value & 0x0000FFFF) | (DSGXWriteRegister(gx, DS9_REG_GXSTAT_HI, value >> 16) << 16); break; default: if (address < DS9_REG_GXFIFO_00) { mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value); } else if (address < DS9_REG_GXSTAT_LO) { - mLOG(DS_GX, STUB, "Unimplemented FIFO write %03X:%04X", address, value); + struct DSGXEntry entry = { + .command = (address & 0x1FC) >> 2l, + .params = { + value, + value >> 8, + value >> 16, + value >> 24 + } + }; + DSGXWriteFIFO(gx, entry); } else { mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value); }@@ -102,3 +222,14 @@ break;
} return value; } + +void DSGXSwapBuffers(struct DSGX* gx) { + mLOG(DS_GX, STUB, "Unimplemented GX swap buffers"); + gx->swapBuffers = false; + + // TODO + DSGXUpdateGXSTAT(gx); + if (CircleBufferSize(&gx->fifo)) { + mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, 0); + } +}
M
src/ds/io.c
→
src/ds/io.c
@@ -157,6 +157,7 @@ break;
case DS_REG_IF_LO: case DS_REG_IF_HI: value = dscore->memory.io[address >> 1] & ~value; + DSGXUpdateGXSTAT(&dscore->p->gx); break; default: return 0;
M
src/ds/video.c
→
src/ds/video.c
@@ -289,6 +289,9 @@ case DS_VIDEO_VERTICAL_PIXELS:
video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat); if (video->frameskipCounter <= 0) { video->renderer->finishFrame(video->renderer); + if (video->p->gx.swapBuffers) { + DSGXSwapBuffers(&video->p->gx); + } } if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) { DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VBLANK);