mirror of https://github.com/mgba-emu/mgba.git
DS GX: Start implementing FIFO
This commit is contained in:
parent
d157fd6037
commit
164712fdd4
|
@ -16,6 +16,20 @@ CXX_GUARD_START
|
||||||
|
|
||||||
mLOG_DECLARE_CATEGORY(DS_GX);
|
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 {
|
enum DSGXCommand {
|
||||||
DS_GX_CMD_NOP = 0,
|
DS_GX_CMD_NOP = 0,
|
||||||
DS_GX_CMD_MTX_MODE = 0x10,
|
DS_GX_CMD_MTX_MODE = 0x10,
|
||||||
|
@ -73,6 +87,8 @@ struct DSGX {
|
||||||
struct CircleBuffer fifo;
|
struct CircleBuffer fifo;
|
||||||
|
|
||||||
struct mTimingEvent fifoEvent;
|
struct mTimingEvent fifoEvent;
|
||||||
|
|
||||||
|
bool swapBuffers;
|
||||||
};
|
};
|
||||||
|
|
||||||
void DSGXInit(struct DSGX*);
|
void DSGXInit(struct DSGX*);
|
||||||
|
@ -82,6 +98,9 @@ void DSGXReset(struct DSGX*);
|
||||||
uint16_t DSGXWriteRegister(struct DSGX*, uint32_t address, uint16_t value);
|
uint16_t DSGXWriteRegister(struct DSGX*, uint32_t address, uint16_t value);
|
||||||
uint32_t DSGXWriteRegister32(struct DSGX*, uint32_t address, uint32_t value);
|
uint32_t DSGXWriteRegister32(struct DSGX*, uint32_t address, uint32_t value);
|
||||||
|
|
||||||
|
void DSGXSwapBuffers(struct DSGX*);
|
||||||
|
void DSGXUpdateGXSTAT(struct DSGX*);
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
141
src/ds/gx.c
141
src/ds/gx.c
|
@ -5,6 +5,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include <mgba/internal/ds/gx.h>
|
#include <mgba/internal/ds/gx.h>
|
||||||
|
|
||||||
|
#include <mgba/internal/ds/ds.h>
|
||||||
#include <mgba/internal/ds/io.h>
|
#include <mgba/internal/ds/io.h>
|
||||||
|
|
||||||
mLOG_DEFINE_CATEGORY(DS_GX, "DS GX");
|
mLOG_DEFINE_CATEGORY(DS_GX, "DS GX");
|
||||||
|
@ -52,8 +53,49 @@ static const int32_t _gxCommandCycleBase[DS_GX_CMD_MAX] = {
|
||||||
[DS_GX_CMD_VEC_TEST] = 10,
|
[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) {
|
void DSGXInit(struct DSGX* gx) {
|
||||||
CircleBufferInit(&gx->fifo, sizeof(struct DSGXEntry) * DS_GX_FIFO_SIZE);
|
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) {
|
void DSGXDeinit(struct DSGX* gx) {
|
||||||
|
@ -62,20 +104,86 @@ void DSGXDeinit(struct DSGX* gx) {
|
||||||
|
|
||||||
void DSGXReset(struct DSGX* gx) {
|
void DSGXReset(struct DSGX* gx) {
|
||||||
CircleBufferClear(&gx->fifo);
|
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 DSGXWriteRegister(struct DSGX* gx, uint32_t address, uint16_t value) {
|
||||||
|
uint16_t oldValue = gx->p->memory.io9[address >> 1];
|
||||||
switch (address) {
|
switch (address) {
|
||||||
case DS9_REG_DISP3DCNT:
|
case DS9_REG_DISP3DCNT:
|
||||||
case DS9_REG_GXSTAT_LO:
|
|
||||||
case DS9_REG_GXSTAT_HI:
|
|
||||||
mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
|
mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
|
||||||
break;
|
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:
|
||||||
|
value = DSRegGXSTATIsDoIRQ(value << 16) >> 16;
|
||||||
|
gx->p->memory.io9[address >> 1] = value;
|
||||||
|
DSGXUpdateGXSTAT(gx);
|
||||||
|
value = gx->p->memory.io9[address >> 1];
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (address < DS9_REG_GXFIFO_00) {
|
if (address < DS9_REG_GXFIFO_00) {
|
||||||
mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
|
mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
|
||||||
} else if (address < DS9_REG_GXSTAT_LO) {
|
} 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 {
|
} else {
|
||||||
mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
|
mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
|
||||||
}
|
}
|
||||||
|
@ -87,14 +195,26 @@ uint16_t DSGXWriteRegister(struct DSGX* gx, uint32_t address, uint16_t value) {
|
||||||
uint32_t DSGXWriteRegister32(struct DSGX* gx, uint32_t address, uint32_t value) {
|
uint32_t DSGXWriteRegister32(struct DSGX* gx, uint32_t address, uint32_t value) {
|
||||||
switch (address) {
|
switch (address) {
|
||||||
case DS9_REG_DISP3DCNT:
|
case DS9_REG_DISP3DCNT:
|
||||||
case DS9_REG_GXSTAT_LO:
|
|
||||||
mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
|
mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
|
||||||
break;
|
break;
|
||||||
|
case DS9_REG_GXSTAT_LO:
|
||||||
|
value = (value & 0xFFFF0000) | DSGXWriteRegister(gx, DS9_REG_GXSTAT_LO, value);
|
||||||
|
value = (value & 0x0000FFFF) | (DSGXWriteRegister(gx, DS9_REG_GXSTAT_HI, value >> 16) << 16);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (address < DS9_REG_GXFIFO_00) {
|
if (address < DS9_REG_GXFIFO_00) {
|
||||||
mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
|
mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
|
||||||
} else if (address < DS9_REG_GXSTAT_LO) {
|
} 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 {
|
} else {
|
||||||
mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
|
mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
|
||||||
}
|
}
|
||||||
|
@ -102,3 +222,14 @@ uint32_t DSGXWriteRegister32(struct DSGX* gx, uint32_t address, uint32_t value)
|
||||||
}
|
}
|
||||||
return value;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -157,6 +157,7 @@ static uint32_t DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t va
|
||||||
case DS_REG_IF_LO:
|
case DS_REG_IF_LO:
|
||||||
case DS_REG_IF_HI:
|
case DS_REG_IF_HI:
|
||||||
value = dscore->memory.io[address >> 1] & ~value;
|
value = dscore->memory.io[address >> 1] & ~value;
|
||||||
|
DSGXUpdateGXSTAT(&dscore->p->gx);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -289,6 +289,9 @@ void _startHdraw9(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
|
video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
|
||||||
if (video->frameskipCounter <= 0) {
|
if (video->frameskipCounter <= 0) {
|
||||||
video->renderer->finishFrame(video->renderer);
|
video->renderer->finishFrame(video->renderer);
|
||||||
|
if (video->p->gx.swapBuffers) {
|
||||||
|
DSGXSwapBuffers(&video->p->gx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
|
if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
|
||||||
DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VBLANK);
|
DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VBLANK);
|
||||||
|
|
Loading…
Reference in New Issue