diff --git a/include/mgba/internal/ds/gx.h b/include/mgba/internal/ds/gx.h index eb05677f2..5b37848ac 100644 --- a/include/mgba/internal/ds/gx.h +++ b/include/mgba/internal/ds/gx.h @@ -13,6 +13,7 @@ CXX_GUARD_START #include #include #include +#include #include mLOG_DECLARE_CATEGORY(DS_GX); @@ -119,10 +120,24 @@ struct DSGX { int outstandingParams[4]; uint8_t outstandingCommand[4]; + int activeParams; + int activeEntries[32]; + bool swapBuffers; int bufferIndex; struct DSGXVertex* vertexBuffer[2]; struct DSGXPolygon* polygonBuffer[2]; + + int mtxMode; + struct DSGXMatrix projMatrixStack; + struct DSGXMatrix texMatrixStack; + struct DSGXMatrix posMatrixStack[32]; + struct DSGXMatrix vecMatrixStack[32]; + + struct DSGXMatrix projMatrix; + struct DSGXMatrix texMatrix; + struct DSGXMatrix posMatrix; + struct DSGXMatrix vecMatrix; }; void DSGXInit(struct DSGX*); diff --git a/include/mgba/internal/ds/matrix.h b/include/mgba/internal/ds/matrix.h new file mode 100644 index 000000000..fdf4d357d --- /dev/null +++ b/include/mgba/internal/ds/matrix.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ +#ifndef DS_MATRIX_H +#define DS_MATRIX_H + +#include + +CXX_GUARD_START + +struct DSGXMatrix { + int32_t m[16]; // 20.12 +}; + +void DSGXMtxIdentity(struct DSGXMatrix*); + +CXX_GUARD_END + +#endif diff --git a/src/ds/gx.c b/src/ds/gx.c index f2f9715af..38275260a 100644 --- a/src/ds/gx.c +++ b/src/ds/gx.c @@ -145,6 +145,10 @@ static void _fifoRun(struct mTiming* timing, void* context, uint32_t cyclesLate) break; } + DSRegGXSTAT gxstat = gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1]; + int pvMatrixPointer = DSRegGXSTATGetPVMatrixStackLevel(gxstat); + int projMatrixPointer = DSRegGXSTATGetProjMatrixStackLevel(gxstat); + if (CircleBufferSize(&gx->pipe) <= 2 * sizeof(struct DSGXEntry)) { _pullPipe(gx); } @@ -165,6 +169,71 @@ static void _fifoRun(struct mTiming* timing, void* context, uint32_t cyclesLate) CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[3]); switch (entry.command) { + case DS_GX_CMD_MTX_MODE: + if (entry.params[0] < 4) { + gx->mtxMode = entry.params[0]; + } else { + mLOG(DS_GX, GAME_ERROR, "Invalid GX MTX_MODE %02X", entry.params[0]); + } + break; + case DS_GX_CMD_MTX_IDENTITY: + switch (gx->mtxMode) { + case 0: + DSGXMtxIdentity(&gx->projMatrix); + break; + case 2: + DSGXMtxIdentity(&gx->vecMatrix); + // Fall through + case 1: + DSGXMtxIdentity(&gx->posMatrix); + break; + case 3: + DSGXMtxIdentity(&gx->texMatrix); + break; + } + break; + case DS_GX_CMD_MTX_PUSH: + switch (gx->mtxMode) { + case 0: + memcpy(&gx->projMatrixStack, &gx->projMatrix, sizeof(gx->projMatrix)); + ++projMatrixPointer; + break; + case 2: + memcpy(&gx->vecMatrixStack[pvMatrixPointer & 0x1F], &gx->vecMatrix, sizeof(gx->vecMatrix)); + // Fall through + case 1: + memcpy(&gx->posMatrixStack[pvMatrixPointer & 0x1F], &gx->posMatrix, sizeof(gx->posMatrix)); + ++pvMatrixPointer; + break; + case 3: + mLOG(DS_GX, STUB, "Unimplemented GX MTX_PUSH mode"); + break; + } + break; + case DS_GX_CMD_MTX_POP: { + int8_t offset = entry.params[0]; + offset <<= 2; + offset >>= 2; + switch (gx->mtxMode) { + case 0: + projMatrixPointer -= offset; + memcpy(&gx->projMatrix, &gx->projMatrixStack, sizeof(gx->projMatrix)); + break; + case 1: + pvMatrixPointer -= offset; + memcpy(&gx->posMatrix, &gx->posMatrixStack[pvMatrixPointer & 0x1F], sizeof(gx->posMatrix)); + break; + case 2: + pvMatrixPointer -= offset; + memcpy(&gx->vecMatrix, &gx->vecMatrixStack[pvMatrixPointer & 0x1F], sizeof(gx->vecMatrix)); + memcpy(&gx->posMatrix, &gx->posMatrixStack[pvMatrixPointer & 0x1F], sizeof(gx->posMatrix)); + break; + case 3: + mLOG(DS_GX, STUB, "Unimplemented GX MTX_POP mode"); + break; + } + break; + } case DS_GX_CMD_SWAP_BUFFERS: gx->swapBuffers = true; break; @@ -172,6 +241,12 @@ static void _fifoRun(struct mTiming* timing, void* context, uint32_t cyclesLate) 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; } + + gxstat = DSRegGXSTATSetPVMatrixStackLevel(gxstat, pvMatrixPointer); + gxstat = DSRegGXSTATSetProjMatrixStackLevel(gxstat, projMatrixPointer); + gxstat = DSRegGXSTATTestFillMatrixStackError(gxstat, projMatrixPointer || pvMatrixPointer >= 0x1F); + gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1] = gxstat; + if (cyclesLate >= cycles) { cyclesLate -= cycles; } @@ -205,7 +280,22 @@ void DSGXDeinit(struct DSGX* gx) { void DSGXReset(struct DSGX* gx) { CircleBufferClear(&gx->fifo); CircleBufferClear(&gx->pipe); + DSGXMtxIdentity(&gx->projMatrix); + DSGXMtxIdentity(&gx->texMatrix); + DSGXMtxIdentity(&gx->posMatrix); + DSGXMtxIdentity(&gx->vecMatrix); + + DSGXMtxIdentity(&gx->projMatrixStack); + DSGXMtxIdentity(&gx->texMatrixStack); + int i; + for (i = 0; i < 32; ++i) { + DSGXMtxIdentity(&gx->posMatrixStack[i]); + DSGXMtxIdentity(&gx->vecMatrixStack[i]); + } gx->swapBuffers = false; + gx->bufferIndex = 0; + gx->mtxMode = 0; + memset(gx->outstandingParams, 0, sizeof(gx->outstandingParams)); memset(gx->outstandingCommand, 0, sizeof(gx->outstandingCommand)); } diff --git a/src/ds/matrix.c b/src/ds/matrix.c new file mode 100644 index 000000000..1b7aaba3e --- /dev/null +++ b/src/ds/matrix.c @@ -0,0 +1,16 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * 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 + +#define MTX_ONE 0x00001000 + +void DSGXMtxIdentity(struct DSGXMatrix* mtx) { + memset(mtx, 0, sizeof(*mtx)); + mtx->m[0] = MTX_ONE; + mtx->m[5] = MTX_ONE; + mtx->m[10] = MTX_ONE; + mtx->m[15] = MTX_ONE; +}