DS GX: First pass at textures

This commit is contained in:
Vicki Pfau 2017-03-01 17:55:25 -08:00
parent 4061d4d39c
commit 4320669d21
5 changed files with 255 additions and 33 deletions

View File

@ -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,
@ -91,20 +103,24 @@ struct DSGXVertex {
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 @@ struct DSGXRenderer {
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 @@ struct DSGX {
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 @@ struct DSGX {
int vertexMode;
struct DSGXVertex currentVertex;
struct DSGXPolygon nextPoly;
struct DSGXPolygon currentPoly;
};

View File

@ -17,6 +17,11 @@ CXX_GUARD_START
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 @@ struct DSGXSoftwareEndpoint {
};
struct DSGXSoftwareSpan {
struct DSGXSoftwarePolygon* poly;
struct DSGXSoftwareEndpoint ep[2];
};

View File

@ -16,6 +16,7 @@ mLOG_DEFINE_CATEGORY(DS_GX, "DS GX");
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 @@ static int32_t _dotViewport(struct DSGXVertex* vertex, int32_t* col) {
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 @@ static void _emitVertex(struct DSGX* gx, uint16_t x, uint16_t y, uint16_t z) {
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 @@ static void _fifoRun(struct mTiming* timing, void* context, uint32_t cyclesLate)
_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 @@ void DSGXReset(struct DSGX* gx) {
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);
}
@ -1009,6 +1080,12 @@ static void DSGXDummyRendererDeinit(struct DSGXRenderer* renderer) {
// Nothing to do
}
static void DSGXDummyRendererInvalidateTex(struct DSGXRenderer* renderer, int slot) {
UNUSED(renderer);
UNUSED(slot);
// Nothing to do
}
static void DSGXDummyRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount) {
UNUSED(renderer);
UNUSED(verts);

View File

@ -17,6 +17,7 @@ DEFINE_VECTOR(DSGXSoftwareSpanList, struct DSGXSoftwareSpan);
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 @@ static color_t _finishColor(uint8_t r, uint8_t g, uint8_t b) {
#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 @@ static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
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 @@ static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSG
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 @@ static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSG
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 @@ static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSG
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 @@ static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int
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 @@ static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int
}
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
}

View File

@ -68,26 +68,26 @@ const struct DSVRAMBankInfo {
{ 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 @@ void DSVideoConfigureVRAM(struct DS* ds, int index, uint8_t value, uint8_t oldVa
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;
@ -491,6 +506,17 @@ void DSVideoConfigureVRAM(struct DS* ds, int index, uint8_t value, uint8_t oldVa
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) {
ds->memory.vram7[(offset + i) >> 4] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 5)];