all repos — mgba @ 4320669d216f5b5fb77f53f359ad207cf2046ecb

mGBA Game Boy Advance Emulator

DS GX: First pass at textures
Vicki Pfau vi@endrift.com
Wed, 01 Mar 2017 17:55:25 -0800
commit

4320669d216f5b5fb77f53f359ad207cf2046ecb

parent

4061d4d39cefc5747dab5cdd1f77627ec047b2ad

M include/mgba/internal/ds/gx.hinclude/mgba/internal/ds/gx.h

@@ -35,6 +35,18 @@ DECL_BIT(DSRegGXSTAT, FIFOEmpty, 26);

DECL_BIT(DSRegGXSTAT, Busy, 27); DECL_BITS(DSRegGXSTAT, DoIRQ, 30, 2); +DECL_BITFIELD(DSGXTexParams, uint32_t); +DECL_BITS(DSGXTexParams, VRAMBase, 0, 16); +DECL_BIT(DSGXTexParams, SRepeat, 16); +DECL_BIT(DSGXTexParams, TRepeat, 17); +DECL_BIT(DSGXTexParams, SMirror, 18); +DECL_BIT(DSGXTexParams, TMirror, 19); +DECL_BITS(DSGXTexParams, SSize, 20, 3); +DECL_BITS(DSGXTexParams, TSize, 23, 3); +DECL_BITS(DSGXTexParams, Format, 26, 3); +DECL_BIT(DSGXTexParams, 0Transparent, 29); +DECL_BITS(DSGXTexParams, CoordTfMode, 30, 2); + enum DSGXCommand { DS_GX_CMD_NOP = 0, DS_GX_CMD_MTX_MODE = 0x10,

@@ -90,21 +102,25 @@ // World coords

int16_t x; // 4.12 int16_t y; // 4.12 int16_t z; // 4.12 - - // Viewport coords - int32_t vx; // 16.16 - int32_t vy; // 16.16 - int32_t vz; // 16.16 - int32_t vw; // 16.16 // Color/Texcoords uint16_t color; // 5.5.5 int16_t s; // 12.4 int16_t t; // 12.4 + + // Viewport coords + int32_t vx; + int32_t vy; + int32_t vz; + int32_t vw; + int16_t vs; // 12.4 + int16_t vt; // 12.4 }; struct DSGXPolygon { uint32_t polyParams; + DSGXTexParams texParams; + uint32_t palBase; int verts; unsigned vertIds[4]; };

@@ -114,9 +130,13 @@ void (*init)(struct DSGXRenderer* renderer);

void (*reset)(struct DSGXRenderer* renderer); void (*deinit)(struct DSGXRenderer* renderer); + void (*invalidateTex)(struct DSGXRenderer* renderer, int slot); void (*setRAM)(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount); void (*drawScanline)(struct DSGXRenderer* renderer, int y); void (*getScanline)(struct DSGXRenderer* renderer, int y, color_t** output); + + uint16_t* tex[4]; + uint16_t* texPal[6]; }; struct DS;

@@ -142,6 +162,9 @@ int polygonIndex;

struct DSGXVertex* vertexBuffer[2]; struct DSGXPolygon* polygonBuffer[2]; + uint16_t* tex[4]; + uint16_t* texPal[6]; + int mtxMode; int pvMatrixPointer; struct DSGXMatrix projMatrixStack;

@@ -165,6 +188,7 @@ int viewportHeight;

int vertexMode; struct DSGXVertex currentVertex; + struct DSGXPolygon nextPoly; struct DSGXPolygon currentPoly; };
M include/mgba/internal/ds/gx/software.hinclude/mgba/internal/ds/gx/software.h

@@ -17,6 +17,11 @@ #include <mgba-util/vector.h>

struct DSGXSoftwarePolygon { struct DSGXPolygon* poly; + uint16_t* texBase; + uint16_t* palBase; + int texFormat; + unsigned texW; + unsigned texH; int32_t topY; int32_t bottomY; int32_t topZ;

@@ -54,6 +59,7 @@ int16_t t;

}; struct DSGXSoftwareSpan { + struct DSGXSoftwarePolygon* poly; struct DSGXSoftwareEndpoint ep[2]; };
M src/ds/gx.csrc/ds/gx.c

@@ -16,6 +16,7 @@

static void DSGXDummyRendererInit(struct DSGXRenderer* renderer); static void DSGXDummyRendererReset(struct DSGXRenderer* renderer); static void DSGXDummyRendererDeinit(struct DSGXRenderer* renderer); +static void DSGXDummyRendererInvalidateTex(struct DSGXRenderer* renderer, int slot); static void DSGXDummyRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount); static void DSGXDummyRendererDrawScanline(struct DSGXRenderer* renderer, int y); static void DSGXDummyRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output);

@@ -104,6 +105,7 @@ static struct DSGXRenderer dummyRenderer = {

.init = DSGXDummyRendererInit, .reset = DSGXDummyRendererReset, .deinit = DSGXDummyRendererDeinit, + .invalidateTex = DSGXDummyRendererInvalidateTex, .setRAM = DSGXDummyRendererSetRAM, .drawScanline = DSGXDummyRendererDrawScanline, .getScanline = DSGXDummyRendererGetScanline,

@@ -161,6 +163,44 @@ sum += a * b;

return sum >> 8LL; } +static int16_t _dotTexture(struct DSGXVertex* vertex, int mode, int32_t* col) { + int64_t a; + int64_t b; + int64_t sum; + switch (mode) { + case 1: + a = col[0]; + b = vertex->s; + sum = a * b; + a = col[4]; + b = vertex->t; + sum += a * b; + a = col[8]; + b = MTX_ONE >> 4; + sum += a * b; + a = col[12]; + b = MTX_ONE >> 4; + sum += a * b; + break; + case 2: + return 0; + case 3: + a = col[0]; + b = vertex->vx; + sum = a * b; + a = col[4]; + b = vertex->vy; + sum += a * b; + a = col[8]; + b = vertex->vz; + sum += a * b; + a = col[12]; + b = MTX_ONE; + sum += a * b; + } + return sum >> 12; +} + static void _emitVertex(struct DSGX* gx, uint16_t x, uint16_t y, uint16_t z) { if (gx->vertexMode < 0 || gx->vertexIndex == DS_GX_VERTEX_BUFFER_SIZE || gx->polygonIndex == DS_GX_POLYGON_BUFFER_SIZE) { return;

@@ -179,6 +219,22 @@ gx->currentVertex.vx = (gx->currentVertex.vx + gx->currentVertex.vw) * (int64_t) (gx->viewportWidth << 12) / (gx->currentVertex.vw * 2) + (gx->viewportX1 << 12);

gx->currentVertex.vy = (gx->currentVertex.vy + gx->currentVertex.vw) * (int64_t) (gx->viewportHeight << 12) / (gx->currentVertex.vw * 2) + (gx->viewportY1 << 12); gx->currentVertex.vw = 0x40000000 / gx->currentVertex.vw; + if (DSGXTexParamsGetCoordTfMode(gx->currentPoly.texParams) > 0) { + int32_t m12 = gx->texMatrix.m[12]; + int32_t m13 = gx->texMatrix.m[13]; + if (DSGXTexParamsGetCoordTfMode(gx->currentPoly.texParams) > 1) { + gx->texMatrix.m[12] = gx->currentVertex.vs; + gx->texMatrix.m[13] = gx->currentVertex.vt; + } + gx->currentVertex.vs = _dotTexture(&gx->currentVertex, DSGXTexParamsGetCoordTfMode(gx->currentPoly.texParams), &gx->texMatrix.m[0]); + gx->currentVertex.vt = _dotTexture(&gx->currentVertex, DSGXTexParamsGetCoordTfMode(gx->currentPoly.texParams), &gx->texMatrix.m[1]); + gx->texMatrix.m[12] = m12; + gx->texMatrix.m[13] = m13; + } else { + gx->currentVertex.vs = gx->currentVertex.s; + gx->currentVertex.vt = gx->currentVertex.t; + } + struct DSGXVertex* vbuf = gx->vertexBuffer[gx->bufferIndex]; vbuf[gx->vertexIndex] = gx->currentVertex;

@@ -671,14 +727,26 @@ int16_t z = (xyz >> 14) & 0xFFC0;

_emitVertex(gx, gx->currentVertex.x + (x >> 6), gx->currentVertex.y + (y >> 6), gx->currentVertex.z + (z >> 6)); } case DS_GX_CMD_POLYGON_ATTR: - gx->currentPoly.polyParams = entry.params[0]; - gx->currentPoly.polyParams |= entry.params[1] << 8; - gx->currentPoly.polyParams |= entry.params[2] << 16; - gx->currentPoly.polyParams |= entry.params[3] << 24; + gx->nextPoly.polyParams = entry.params[0]; + gx->nextPoly.polyParams |= entry.params[1] << 8; + gx->nextPoly.polyParams |= entry.params[2] << 16; + gx->nextPoly.polyParams |= entry.params[3] << 24; + break; + case DS_GX_CMD_TEXIMAGE_PARAM: + gx->nextPoly.texParams = entry.params[0]; + gx->nextPoly.texParams |= entry.params[1] << 8; + gx->nextPoly.texParams |= entry.params[2] << 16; + gx->nextPoly.texParams |= entry.params[3] << 24; + break; + case DS_GX_CMD_PLTT_BASE: + gx->nextPoly.palBase = entry.params[0]; + gx->nextPoly.palBase |= entry.params[1] << 8; + gx->nextPoly.palBase |= entry.params[2] << 16; + gx->nextPoly.palBase |= entry.params[3] << 24; break; case DS_GX_CMD_BEGIN_VTXS: gx->vertexMode = entry.params[0] & 3; - gx->currentPoly.verts = 0; + gx->currentPoly = gx->nextPoly; break; case DS_GX_CMD_END_VTXS: gx->vertexMode = -1;

@@ -777,11 +845,14 @@ memset(gx->outstandingCommand, 0, sizeof(gx->outstandingCommand));

memset(&gx->outstandingEntry, 0, sizeof(gx->outstandingEntry)); gx->activeParams = 0; memset(&gx->currentVertex, 0, sizeof(gx->currentVertex)); + memset(&gx->nextPoly, 0, sizeof(gx-> nextPoly)); } void DSGXAssociateRenderer(struct DSGX* gx, struct DSGXRenderer* renderer) { gx->renderer->deinit(gx->renderer); gx->renderer = renderer; + memcpy(gx->renderer->tex, gx->tex, sizeof(gx->renderer->tex)); + memcpy(gx->renderer->texPal, gx->texPal, sizeof(gx->renderer->texPal)); gx->renderer->init(gx->renderer); }

@@ -1006,6 +1077,12 @@ }

static void DSGXDummyRendererDeinit(struct DSGXRenderer* renderer) { UNUSED(renderer); + // Nothing to do +} + +static void DSGXDummyRendererInvalidateTex(struct DSGXRenderer* renderer, int slot) { + UNUSED(renderer); + UNUSED(slot); // Nothing to do }
M src/ds/gx/software.csrc/ds/gx/software.c

@@ -17,6 +17,7 @@

static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer); static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer); static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer); +static void DSGXSoftwareRendererInvalidateTex(struct DSGXRenderer* renderer, int slot); static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount); static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y); static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output);

@@ -38,6 +39,73 @@ #error Unsupported color depth

#endif } +static color_t _lookupColor(struct DSGXSoftwareEndpoint* ep, struct DSGXSoftwarePolygon* poly) { + // TODO: Optimize + uint16_t texel; + + uint16_t s = ep->s >> 4; + uint16_t t = ep->t >> 4; + if (!DSGXTexParamsIsSRepeat(poly->poly->texParams)) { + if (s < 0) { + s = 0; + } else if (s >= poly->texW) { + s = poly->texW - 1; + } + } else if (DSGXTexParamsIsSMirror(poly->poly->texParams)) { + if (s & poly->texW) { + s = poly->texW - s; + } + s &= poly->texW - 1; + } else { + s &= poly->texW - 1; + } + if (!DSGXTexParamsIsTRepeat(poly->poly->texParams)) { + if (t < 0) { + t = 0; + } else if (s >= poly->texH) { + t = poly->texW - 1; + } + } else if (DSGXTexParamsIsTMirror(poly->poly->texParams)) { + if (t & poly->texH) { + t = poly->texH - t; + } + t &= poly->texH - 1; + } else { + t &= poly->texH - 1; + } + + uint16_t texelCoord = s + t * poly->texW; + switch (poly->texFormat) { + case 0: + default: + return _finishColor(ep->cr, ep->cg, ep->cb); + case 1: + return _finishColor(0, 0, 0x3F); + case 2: + return _finishColor(0, 0x3F, 0); + case 3: + texel = ((uint8_t*) poly->texBase)[texelCoord >> 1]; + if ((ep->s >> 4) & 0x1) { + texel >>= 4; + } + texel &= 0xF; + break; + case 4: + texel = ((uint8_t*) poly->texBase)[texelCoord]; + break; + case 5: + return _finishColor(0x3F, 0, 0x3F); + case 6: + return _finishColor(0x3F, 0x3F, 0); + case 7: + return _finishColor(0x3F, 0x3F, 0x3F); + } + uint8_t r, g, b; + texel = poly->palBase[texel]; + _expandColor(texel, &r, &g, &b); + return _finishColor(r, g, b); +} + static int _edgeSort(const void* a, const void* b) { const struct DSGXSoftwareEdge* ea = a; const struct DSGXSoftwareEdge* eb = b;

@@ -142,6 +210,7 @@ void DSGXSoftwareRendererCreate(struct DSGXSoftwareRenderer* renderer) {

renderer->d.init = DSGXSoftwareRendererInit; renderer->d.reset = DSGXSoftwareRendererReset; renderer->d.deinit = DSGXSoftwareRendererDeinit; + renderer->d.invalidateTex = DSGXSoftwareRendererInvalidateTex; renderer->d.setRAM = DSGXSoftwareRendererSetRAM; renderer->d.drawScanline = DSGXSoftwareRendererDrawScanline; renderer->d.getScanline = DSGXSoftwareRendererGetScanline;

@@ -170,6 +239,11 @@ mappedMemoryFree(softwareRenderer->bucket, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);

mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48); } +static void DSGXSoftwareRendererInvalidateTex(struct DSGXRenderer* renderer, int slot) { + struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer; + // TODO +} + static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount) { struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;

@@ -181,6 +255,20 @@ for (i = 0; i < polyCount; ++i) {

struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys); struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges); poly->poly = &polys[i]; + poly->texFormat = DSGXTexParamsGetFormat(poly->poly->texParams); + poly->texW = 8 << DSGXTexParamsGetSSize(poly->poly->texParams); + poly->texH = 8 << DSGXTexParamsGetTSize(poly->poly->texParams); + switch (poly->texFormat) { + case 0: + case 7: + poly->texBase = NULL; + poly->palBase = NULL; + break; + default: + poly->texBase = &renderer->tex[DSGXTexParamsGetVRAMBase(poly->poly->texParams) >> VRAM_BLOCK_OFFSET][(DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 2) & 0xFFFF]; + poly->palBase = &renderer->texPal[poly->poly->palBase >> 11][(poly->poly->palBase << 3) & 0x1FFF]; + break; + } edge->polyId = i; struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];

@@ -194,29 +282,29 @@ edge->y0 = SCREEN_SIZE - v0->vy;

edge->x0 = v0->vx; edge->w0 = v0->vw; _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0); - edge->s0 = v0->s; - edge->t0 = v0->t; + edge->s0 = v0->vs; + edge->t0 = v0->vt; edge->y1 = SCREEN_SIZE - v1->vy; edge->x1 = v1->vx; edge->w1 = v1->vw; _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1); - edge->s1 = v1->s; - edge->t1 = v1->t; + edge->s1 = v1->vs; + edge->t1 = v1->vt; } else { edge->y0 = SCREEN_SIZE - v1->vy; edge->x0 = v1->vx; edge->w0 = v1->vw; _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0); - edge->s0 = v1->s; - edge->t0 = v1->t; + edge->s0 = v1->vs; + edge->t0 = v1->vt; edge->y1 = SCREEN_SIZE - v0->vy; edge->x1 = v0->vx; edge->w1 = v0->vw; _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1); - edge->s1 = v0->s; - edge->t1 = v0->t; + edge->s1 = v0->vs; + edge->t1 = v0->vt; } edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);

@@ -230,29 +318,29 @@ edge->y0 = SCREEN_SIZE - v0->vy;

edge->x0 = v0->vx; edge->w0 = v0->vw; _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0); - edge->s0 = v0->s; - edge->t0 = v0->t; + edge->s0 = v0->vs; + edge->t0 = v0->vt; edge->y1 = SCREEN_SIZE - v1->vy; edge->x1 = v1->vx; edge->w1 = v1->vw; _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1); - edge->s1 = v1->s; - edge->t1 = v1->t; + edge->s1 = v1->vs; + edge->t1 = v1->vt; } else { edge->y0 = SCREEN_SIZE - v1->vy; edge->x0 = v1->vx; edge->w0 = v1->vw; _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0); - edge->s0 = v1->s; - edge->t0 = v1->t; + edge->s0 = v1->vs; + edge->t0 = v1->vt; edge->y1 = SCREEN_SIZE - v0->vy; edge->x1 = v0->vx; edge->w1 = v0->vw; _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1); - edge->s1 = v0->s; - edge->t1 = v0->t; + edge->s1 = v0->vs; + edge->t1 = v0->vt; } } qsort(DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, 0), DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges), sizeof(struct DSGXSoftwareEdge), _edgeSort);

@@ -280,7 +368,8 @@ _edgeToSpan(span, edge, 1, y);

softwareRenderer->bucket[poly] = NULL; } else if (!span) { span = DSGXSoftwareSpanListAppend(&softwareRenderer->activeSpans); - memset(span, 0, sizeof(*span)); + memset(&span->ep[1], 0, sizeof(span->ep[1])); + span->poly = DSGXSoftwarePolygonListGetPointer(&softwareRenderer->activePolys, poly); if (!_edgeToSpan(span, edge, 0, y)) { // Horizontal line DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1, 1);

@@ -339,7 +428,7 @@ }

} if (span) { _lerpEndpoint(span, &ep, i); - scanline[i] = _finishColor(ep.cr, ep.cg, ep.cb); + scanline[i] = _lookupColor(&ep, span->poly); } else { scanline[i] = FLAG_UNWRITTEN; // TODO }
M src/ds/video.csrc/ds/video.c

@@ -68,26 +68,26 @@ { // A

{ 0x000, 0x40, MODE_LCDC }, { 0x000, 0x20, MODE_A_BG, { 0x00, 0x08, 0x10, 0x18 } }, { 0x000, 0x10, MODE_A_OBJ, { 0x00, 0x08, 0x80, 0x80 } }, - { 0x000, 0x10, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } }, + { 0x000, 0x01, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } }, }, { // B { 0x008, 0x40, MODE_LCDC }, { 0x000, 0x20, MODE_A_BG, { 0x00, 0x08, 0x10, 0x18 } }, { 0x000, 0x10, MODE_A_OBJ, { 0x00, 0x08, 0x80, 0x80 } }, - { 0x000, 0x10, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } }, + { 0x000, 0x01, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } }, }, { // C { 0x010, 0x40, MODE_LCDC }, { 0x000, 0x20, MODE_A_BG, { 0x00, 0x08, 0x10, 0x18 } }, { 0x000, 0x40, MODE_7_VRAM, { 0x00, 0x08, 0x80, 0x80 } }, - { 0x000, 0x10, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } }, + { 0x000, 0x01, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } }, { 0x000, 0x08, MODE_B_BG }, }, { // D { 0x018, 0x40, MODE_LCDC }, { 0x000, 0x20, MODE_A_BG, { 0x00, 0x08, 0x10, 0x18 } }, { 0x000, 0x40, MODE_7_VRAM, { 0x00, 0x08, 0x80, 0x80 } }, - { 0x000, 0x10, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } }, + { 0x000, 0x01, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } }, { 0x000, 0x08, MODE_B_OBJ }, }, { // E

@@ -418,6 +418,21 @@ ds->video.renderer->vramBOBJExtPal = NULL;

ds->video.renderer->invalidateExtPal(ds->video.renderer, true, true, 0); } break; + case MODE_3D_TEX: + if (ds->gx.tex[offset] == memory->vramBank[index]) { + ds->gx.tex[offset] = NULL; + ds->gx.renderer->tex[offset] = NULL; + ds->gx.renderer->invalidateTex(ds->gx.renderer, offset); + } + break; + case MODE_3D_TEX_PAL: + for (i = 0; i < oldInfo.mirrorSize; ++i) { + if (ds->gx.texPal[offset + i] == &memory->vramBank[index][i << 13]) { + ds->gx.texPal[offset + i] = NULL; + ds->gx.renderer->texPal[offset + i] = NULL; + } + } + break; case MODE_7_VRAM: for (i = 0; i < size; i += 16) { ds->memory.vram7[(offset + i) >> 4] = NULL;

@@ -490,6 +505,17 @@ case MODE_B_OBJ_EXT_PAL:

ds->video.vramBOBJExtPal = memory->vramBank[index]; ds->video.renderer->vramBOBJExtPal = ds->video.vramBOBJExtPal; ds->video.renderer->invalidateExtPal(ds->video.renderer, true, true, 0); + break; + case MODE_3D_TEX: + ds->gx.tex[offset] = memory->vramBank[index]; + ds->gx.renderer->tex[offset] = ds->gx.tex[offset]; + ds->gx.renderer->invalidateTex(ds->gx.renderer, offset); + break; + case MODE_3D_TEX_PAL: + for (i = 0; i < info.mirrorSize; ++i) { + ds->gx.texPal[offset + i] = &memory->vramBank[index][i << 13]; + ds->gx.renderer->texPal[offset + i] = ds->gx.texPal[offset + i]; + } break; case MODE_7_VRAM: for (i = 0; i < size; i += 16) {