src/ds/gx.c (view raw)
1/* Copyright (c) 2013-2017 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/ds/gx.h>
7
8#include <mgba/internal/ds/ds.h>
9#include <mgba/internal/ds/io.h>
10
11mLOG_DEFINE_CATEGORY(DS_GX, "DS GX");
12
13#define DS_GX_FIFO_SIZE 256
14#define DS_GX_PIPE_SIZE 4
15
16static void DSGXDummyRendererInit(struct DSGXRenderer* renderer);
17static void DSGXDummyRendererReset(struct DSGXRenderer* renderer);
18static void DSGXDummyRendererDeinit(struct DSGXRenderer* renderer);
19static void DSGXDummyRendererInvalidateTex(struct DSGXRenderer* renderer, int slot);
20static void DSGXDummyRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount);
21static void DSGXDummyRendererDrawScanline(struct DSGXRenderer* renderer, int y);
22static void DSGXDummyRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output);
23
24static void DSGXWriteFIFO(struct DSGX* gx, struct DSGXEntry entry);
25
26static const int32_t _gxCommandCycleBase[DS_GX_CMD_MAX] = {
27 [DS_GX_CMD_NOP] = 0,
28 [DS_GX_CMD_MTX_MODE] = 2,
29 [DS_GX_CMD_MTX_PUSH] = 34,
30 [DS_GX_CMD_MTX_POP] = 72,
31 [DS_GX_CMD_MTX_STORE] = 34,
32 [DS_GX_CMD_MTX_RESTORE] = 72,
33 [DS_GX_CMD_MTX_IDENTITY] = 38,
34 [DS_GX_CMD_MTX_LOAD_4x4] = 68,
35 [DS_GX_CMD_MTX_LOAD_4x3] = 60,
36 [DS_GX_CMD_MTX_MULT_4x4] = 70,
37 [DS_GX_CMD_MTX_MULT_4x3] = 62,
38 [DS_GX_CMD_MTX_MULT_3x3] = 56,
39 [DS_GX_CMD_MTX_SCALE] = 44,
40 [DS_GX_CMD_MTX_TRANS] = 44,
41 [DS_GX_CMD_COLOR] = 2,
42 [DS_GX_CMD_NORMAL] = 18,
43 [DS_GX_CMD_TEXCOORD] = 2,
44 [DS_GX_CMD_VTX_16] = 18,
45 [DS_GX_CMD_VTX_10] = 16,
46 [DS_GX_CMD_VTX_XY] = 16,
47 [DS_GX_CMD_VTX_XZ] = 16,
48 [DS_GX_CMD_VTX_YZ] = 16,
49 [DS_GX_CMD_VTX_DIFF] = 16,
50 [DS_GX_CMD_POLYGON_ATTR] = 2,
51 [DS_GX_CMD_TEXIMAGE_PARAM] = 2,
52 [DS_GX_CMD_PLTT_BASE] = 2,
53 [DS_GX_CMD_DIF_AMB] = 8,
54 [DS_GX_CMD_SPE_EMI] = 8,
55 [DS_GX_CMD_LIGHT_VECTOR] = 12,
56 [DS_GX_CMD_LIGHT_COLOR] = 2,
57 [DS_GX_CMD_SHININESS] = 64,
58 [DS_GX_CMD_BEGIN_VTXS] = 2,
59 [DS_GX_CMD_END_VTXS] = 2,
60 [DS_GX_CMD_SWAP_BUFFERS] = 784,
61 [DS_GX_CMD_VIEWPORT] = 2,
62 [DS_GX_CMD_BOX_TEST] = 206,
63 [DS_GX_CMD_POS_TEST] = 18,
64 [DS_GX_CMD_VEC_TEST] = 10,
65};
66
67static const int32_t _gxCommandParams[DS_GX_CMD_MAX] = {
68 [DS_GX_CMD_MTX_MODE] = 1,
69 [DS_GX_CMD_MTX_POP] = 1,
70 [DS_GX_CMD_MTX_STORE] = 1,
71 [DS_GX_CMD_MTX_RESTORE] = 1,
72 [DS_GX_CMD_MTX_LOAD_4x4] = 16,
73 [DS_GX_CMD_MTX_LOAD_4x3] = 12,
74 [DS_GX_CMD_MTX_MULT_4x4] = 16,
75 [DS_GX_CMD_MTX_MULT_4x3] = 12,
76 [DS_GX_CMD_MTX_MULT_3x3] = 9,
77 [DS_GX_CMD_MTX_SCALE] = 3,
78 [DS_GX_CMD_MTX_TRANS] = 3,
79 [DS_GX_CMD_COLOR] = 1,
80 [DS_GX_CMD_NORMAL] = 1,
81 [DS_GX_CMD_TEXCOORD] = 1,
82 [DS_GX_CMD_VTX_16] = 2,
83 [DS_GX_CMD_VTX_10] = 1,
84 [DS_GX_CMD_VTX_XY] = 1,
85 [DS_GX_CMD_VTX_XZ] = 1,
86 [DS_GX_CMD_VTX_YZ] = 1,
87 [DS_GX_CMD_VTX_DIFF] = 1,
88 [DS_GX_CMD_POLYGON_ATTR] = 1,
89 [DS_GX_CMD_TEXIMAGE_PARAM] = 1,
90 [DS_GX_CMD_PLTT_BASE] = 1,
91 [DS_GX_CMD_DIF_AMB] = 1,
92 [DS_GX_CMD_SPE_EMI] = 1,
93 [DS_GX_CMD_LIGHT_VECTOR] = 1,
94 [DS_GX_CMD_LIGHT_COLOR] = 1,
95 [DS_GX_CMD_SHININESS] = 32,
96 [DS_GX_CMD_BEGIN_VTXS] = 1,
97 [DS_GX_CMD_SWAP_BUFFERS] = 1,
98 [DS_GX_CMD_VIEWPORT] = 1,
99 [DS_GX_CMD_BOX_TEST] = 3,
100 [DS_GX_CMD_POS_TEST] = 2,
101 [DS_GX_CMD_VEC_TEST] = 1,
102};
103
104static struct DSGXRenderer dummyRenderer = {
105 .init = DSGXDummyRendererInit,
106 .reset = DSGXDummyRendererReset,
107 .deinit = DSGXDummyRendererDeinit,
108 .invalidateTex = DSGXDummyRendererInvalidateTex,
109 .setRAM = DSGXDummyRendererSetRAM,
110 .drawScanline = DSGXDummyRendererDrawScanline,
111 .getScanline = DSGXDummyRendererGetScanline,
112};
113
114static void _pullPipe(struct DSGX* gx) {
115 if (CircleBufferSize(&gx->fifo) >= sizeof(struct DSGXEntry)) {
116 struct DSGXEntry entry = { 0 };
117 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.command);
118 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[0]);
119 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[1]);
120 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[2]);
121 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[3]);
122 CircleBufferWrite8(&gx->pipe, entry.command);
123 CircleBufferWrite8(&gx->pipe, entry.params[0]);
124 CircleBufferWrite8(&gx->pipe, entry.params[1]);
125 CircleBufferWrite8(&gx->pipe, entry.params[2]);
126 CircleBufferWrite8(&gx->pipe, entry.params[3]);
127 }
128 if (CircleBufferSize(&gx->fifo) >= sizeof(struct DSGXEntry)) {
129 struct DSGXEntry entry = { 0 };
130 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.command);
131 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[0]);
132 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[1]);
133 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[2]);
134 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[3]);
135 CircleBufferWrite8(&gx->pipe, entry.command);
136 CircleBufferWrite8(&gx->pipe, entry.params[0]);
137 CircleBufferWrite8(&gx->pipe, entry.params[1]);
138 CircleBufferWrite8(&gx->pipe, entry.params[2]);
139 CircleBufferWrite8(&gx->pipe, entry.params[3]);
140 }
141}
142
143static void _updateClipMatrix(struct DSGX* gx) {
144 DSGXMtxMultiply(&gx->clipMatrix, &gx->posMatrix, &gx->projMatrix);
145}
146
147static int32_t _dotViewport(struct DSGXVertex* vertex, int32_t* col) {
148 int64_t a;
149 int64_t b;
150 int64_t sum;
151 a = col[0];
152 b = vertex->x;
153 sum = a * b;
154 a = col[4];
155 b = vertex->y;
156 sum += a * b;
157 a = col[8];
158 b = vertex->z;
159 sum += a * b;
160 a = col[12];
161 b = MTX_ONE;
162 sum += a * b;
163 return sum >> 8LL;
164}
165
166static int16_t _dotTexture(struct DSGXVertex* vertex, int mode, int32_t* col) {
167 int64_t a;
168 int64_t b;
169 int64_t sum;
170 switch (mode) {
171 case 1:
172 a = col[0];
173 b = vertex->s;
174 sum = a * b;
175 a = col[4];
176 b = vertex->t;
177 sum += a * b;
178 a = col[8];
179 b = MTX_ONE >> 4;
180 sum += a * b;
181 a = col[12];
182 b = MTX_ONE >> 4;
183 sum += a * b;
184 break;
185 case 2:
186 return 0;
187 case 3:
188 a = col[0];
189 b = vertex->vx;
190 sum = a * b;
191 a = col[4];
192 b = vertex->vy;
193 sum += a * b;
194 a = col[8];
195 b = vertex->vz;
196 sum += a * b;
197 a = col[12];
198 b = MTX_ONE;
199 sum += a * b;
200 }
201 return sum >> 12;
202}
203
204static void _emitVertex(struct DSGX* gx, uint16_t x, uint16_t y, uint16_t z) {
205 if (gx->vertexMode < 0 || gx->vertexIndex == DS_GX_VERTEX_BUFFER_SIZE || gx->polygonIndex == DS_GX_POLYGON_BUFFER_SIZE) {
206 return;
207 }
208 gx->currentVertex.x = x;
209 gx->currentVertex.y = y;
210 gx->currentVertex.z = z;
211 gx->currentVertex.vx = _dotViewport(&gx->currentVertex, &gx->clipMatrix.m[0]);
212 gx->currentVertex.vy = _dotViewport(&gx->currentVertex, &gx->clipMatrix.m[1]);
213 gx->currentVertex.vz = _dotViewport(&gx->currentVertex, &gx->clipMatrix.m[2]);
214 gx->currentVertex.vw = _dotViewport(&gx->currentVertex, &gx->clipMatrix.m[3]);
215
216 // TODO: What to do if w is 0?
217
218 gx->currentVertex.vx = (gx->currentVertex.vx + gx->currentVertex.vw) * (int64_t) (gx->viewportWidth << 12) / (gx->currentVertex.vw * 2) + (gx->viewportX1 << 12);
219 gx->currentVertex.vy = (gx->currentVertex.vy + gx->currentVertex.vw) * (int64_t) (gx->viewportHeight << 12) / (gx->currentVertex.vw * 2) + (gx->viewportY1 << 12);
220 gx->currentVertex.vw = 0x40000000 / gx->currentVertex.vw;
221
222 if (DSGXTexParamsGetCoordTfMode(gx->currentPoly.texParams) > 0) {
223 int32_t m12 = gx->texMatrix.m[12];
224 int32_t m13 = gx->texMatrix.m[13];
225 if (DSGXTexParamsGetCoordTfMode(gx->currentPoly.texParams) > 1) {
226 gx->texMatrix.m[12] = gx->currentVertex.vs;
227 gx->texMatrix.m[13] = gx->currentVertex.vt;
228 }
229 gx->currentVertex.vs = _dotTexture(&gx->currentVertex, DSGXTexParamsGetCoordTfMode(gx->currentPoly.texParams), &gx->texMatrix.m[0]);
230 gx->currentVertex.vt = _dotTexture(&gx->currentVertex, DSGXTexParamsGetCoordTfMode(gx->currentPoly.texParams), &gx->texMatrix.m[1]);
231 gx->texMatrix.m[12] = m12;
232 gx->texMatrix.m[13] = m13;
233 } else {
234 gx->currentVertex.vs = gx->currentVertex.s;
235 gx->currentVertex.vt = gx->currentVertex.t;
236 }
237
238 struct DSGXVertex* vbuf = gx->vertexBuffer[gx->bufferIndex];
239 vbuf[gx->vertexIndex] = gx->currentVertex;
240
241 gx->currentPoly.vertIds[gx->currentPoly.verts] = gx->vertexIndex;
242
243 ++gx->vertexIndex;
244 ++gx->currentPoly.verts;
245 int totalVertices;
246 switch (gx->vertexMode) {
247 case 0:
248 case 2:
249 totalVertices = 3;
250 break;
251 case 1:
252 case 3:
253 totalVertices = 4;
254 break;
255 }
256 if (gx->currentPoly.verts == totalVertices) {
257 struct DSGXPolygon* pbuf = gx->polygonBuffer[gx->bufferIndex];
258 pbuf[gx->polygonIndex] = gx->currentPoly;
259
260 switch (gx->vertexMode) {
261 case 0:
262 case 1:
263 gx->currentPoly.verts = 0;
264 break;
265 case 2:
266 gx->currentPoly.vertIds[0] = gx->currentPoly.vertIds[1];
267 gx->currentPoly.vertIds[1] = gx->currentPoly.vertIds[2];
268 gx->currentPoly.verts = 2;
269 break;
270 case 3:
271 gx->currentPoly.vertIds[0] = gx->currentPoly.vertIds[2];
272 gx->currentPoly.vertIds[1] = gx->currentPoly.vertIds[3];
273 // Ensure quads don't cross over
274 pbuf[gx->polygonIndex].vertIds[2] = gx->currentPoly.vertIds[3];
275 pbuf[gx->polygonIndex].vertIds[3] = gx->currentPoly.vertIds[2];
276 gx->currentPoly.verts = 2;
277 break;
278 }
279 ++gx->polygonIndex;
280 }
281}
282
283static void _flushOutstanding(struct DSGX* gx) {
284 if (CircleBufferSize(&gx->fifo) == (DS_GX_FIFO_SIZE * sizeof(struct DSGXEntry))) {
285 return;
286 }
287 if (gx->p->cpuBlocked & DS_CPU_BLOCK_GX) {
288 gx->p->cpuBlocked &= ~DS_CPU_BLOCK_GX;
289 DSGXWriteFIFO(gx, gx->outstandingEntry);
290 gx->outstandingEntry.command = 0;
291 }
292 while (gx->outstandingCommand[0] && !gx->outstandingParams[0]) {
293 if (CircleBufferSize(&gx->fifo) == (DS_GX_FIFO_SIZE * sizeof(struct DSGXEntry))) {
294 return;
295 }
296 DSGXWriteFIFO(gx, (struct DSGXEntry) { 0 });
297 }
298}
299
300static void _fifoRun(struct mTiming* timing, void* context, uint32_t cyclesLate) {
301 struct DSGX* gx = context;
302 uint32_t cycles;
303 bool first = true;
304 while (!gx->swapBuffers) {
305 if (CircleBufferSize(&gx->pipe) <= 2 * sizeof(struct DSGXEntry)) {
306 _pullPipe(gx);
307 }
308
309 if (!CircleBufferSize(&gx->pipe)) {
310 cycles = 0;
311 break;
312 }
313
314 DSRegGXSTAT gxstat = gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1];
315 int projMatrixPointer = DSRegGXSTATGetProjMatrixStackLevel(gxstat);
316
317 struct DSGXEntry entry = { 0 };
318 CircleBufferDump(&gx->pipe, (int8_t*) &entry.command, 1);
319 cycles = _gxCommandCycleBase[entry.command];
320
321 if (first) {
322 first = false;
323 } else if (!gx->activeParams && cycles > cyclesLate) {
324 break;
325 }
326 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.command);
327 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[0]);
328 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[1]);
329 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[2]);
330 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[3]);
331
332 if (gx->activeParams) {
333 int index = _gxCommandParams[entry.command] - gx->activeParams;
334 gx->activeEntries[index] = entry;
335 --gx->activeParams;
336 } else {
337 gx->activeParams = _gxCommandParams[entry.command];
338 if (gx->activeParams) {
339 --gx->activeParams;
340 }
341 if (gx->activeParams) {
342 gx->activeEntries[0] = entry;
343 }
344 }
345
346 if (gx->activeParams) {
347 continue;
348 }
349
350 switch (entry.command) {
351 case DS_GX_CMD_MTX_MODE:
352 if (entry.params[0] < 4) {
353 gx->mtxMode = entry.params[0];
354 } else {
355 mLOG(DS_GX, GAME_ERROR, "Invalid GX MTX_MODE %02X", entry.params[0]);
356 }
357 break;
358 case DS_GX_CMD_MTX_PUSH:
359 switch (gx->mtxMode) {
360 case 0:
361 memcpy(&gx->projMatrixStack, &gx->projMatrix, sizeof(gx->projMatrix));
362 ++projMatrixPointer;
363 break;
364 case 2:
365 memcpy(&gx->vecMatrixStack[gx->pvMatrixPointer & 0x1F], &gx->vecMatrix, sizeof(gx->vecMatrix));
366 // Fall through
367 case 1:
368 memcpy(&gx->posMatrixStack[gx->pvMatrixPointer & 0x1F], &gx->posMatrix, sizeof(gx->posMatrix));
369 ++gx->pvMatrixPointer;
370 break;
371 case 3:
372 mLOG(DS_GX, STUB, "Unimplemented GX MTX_PUSH mode");
373 break;
374 }
375 break;
376 case DS_GX_CMD_MTX_POP: {
377 int8_t offset = entry.params[0];
378 offset <<= 2;
379 offset >>= 2;
380 switch (gx->mtxMode) {
381 case 0:
382 projMatrixPointer -= offset;
383 memcpy(&gx->projMatrix, &gx->projMatrixStack, sizeof(gx->projMatrix));
384 break;
385 case 1:
386 gx->pvMatrixPointer -= offset;
387 memcpy(&gx->posMatrix, &gx->posMatrixStack[gx->pvMatrixPointer & 0x1F], sizeof(gx->posMatrix));
388 break;
389 case 2:
390 gx->pvMatrixPointer -= offset;
391 memcpy(&gx->vecMatrix, &gx->vecMatrixStack[gx->pvMatrixPointer & 0x1F], sizeof(gx->vecMatrix));
392 memcpy(&gx->posMatrix, &gx->posMatrixStack[gx->pvMatrixPointer & 0x1F], sizeof(gx->posMatrix));
393 break;
394 case 3:
395 mLOG(DS_GX, STUB, "Unimplemented GX MTX_POP mode");
396 break;
397 }
398 _updateClipMatrix(gx);
399 break;
400 }
401 case DS_GX_CMD_MTX_STORE: {
402 int8_t offset = entry.params[0] & 0x1F;
403 // TODO: overflow
404 switch (gx->mtxMode) {
405 case 0:
406 memcpy(&gx->projMatrixStack, &gx->projMatrix, sizeof(gx->projMatrixStack));
407 break;
408 case 2:
409 memcpy(&gx->vecMatrixStack[offset], &gx->vecMatrix, sizeof(gx->vecMatrix));
410 // Fall through
411 case 1:
412 memcpy(&gx->posMatrixStack[offset], &gx->posMatrix, sizeof(gx->posMatrix));
413 break;
414 case 3:
415 mLOG(DS_GX, STUB, "Unimplemented GX MTX_STORE mode");
416 break;
417 }
418 break;
419 }
420 case DS_GX_CMD_MTX_RESTORE: {
421 int8_t offset = entry.params[0] & 0x1F;
422 // TODO: overflow
423 switch (gx->mtxMode) {
424 case 0:
425 memcpy(&gx->projMatrix, &gx->projMatrixStack, sizeof(gx->projMatrix));
426 break;
427 case 2:
428 memcpy(&gx->vecMatrix, &gx->vecMatrixStack[offset], sizeof(gx->vecMatrix));
429 // Fall through
430 case 1:
431 memcpy(&gx->posMatrix, &gx->posMatrixStack[offset], sizeof(gx->posMatrix));
432 break;
433 case 3:
434 mLOG(DS_GX, STUB, "Unimplemented GX MTX_RESTORE mode");
435 break;
436 }
437 _updateClipMatrix(gx);
438 break;
439 }
440 case DS_GX_CMD_MTX_IDENTITY:
441 switch (gx->mtxMode) {
442 case 0:
443 DSGXMtxIdentity(&gx->projMatrix);
444 break;
445 case 2:
446 DSGXMtxIdentity(&gx->vecMatrix);
447 // Fall through
448 case 1:
449 DSGXMtxIdentity(&gx->posMatrix);
450 break;
451 case 3:
452 DSGXMtxIdentity(&gx->texMatrix);
453 break;
454 }
455 _updateClipMatrix(gx);
456 break;
457 case DS_GX_CMD_MTX_LOAD_4x4: {
458 struct DSGXMatrix m;
459 int i;
460 for (i = 0; i < 16; ++i) {
461 m.m[i] = gx->activeEntries[i].params[0];
462 m.m[i] |= gx->activeEntries[i].params[1] << 8;
463 m.m[i] |= gx->activeEntries[i].params[2] << 16;
464 m.m[i] |= gx->activeEntries[i].params[3] << 24;
465 }
466 switch (gx->mtxMode) {
467 case 0:
468 memcpy(&gx->projMatrix, &m, sizeof(gx->projMatrix));
469 break;
470 case 2:
471 memcpy(&gx->vecMatrix, &m, sizeof(gx->vecMatrix));
472 // Fall through
473 case 1:
474 memcpy(&gx->posMatrix, &m, sizeof(gx->posMatrix));
475 break;
476 case 3:
477 memcpy(&gx->texMatrix, &m, sizeof(gx->texMatrix));
478 break;
479 }
480 _updateClipMatrix(gx);
481 break;
482 }
483 case DS_GX_CMD_MTX_LOAD_4x3: {
484 struct DSGXMatrix m;
485 int i, j;
486 for (j = 0; j < 4; ++j) {
487 for (i = 0; i < 3; ++i) {
488 m.m[i + j * 4] = gx->activeEntries[i + j * 3].params[0];
489 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[1] << 8;
490 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[2] << 16;
491 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[3] << 24;
492 }
493 m.m[j * 4 + 3] = 0;
494 }
495 m.m[15] = MTX_ONE;
496 switch (gx->mtxMode) {
497 case 0:
498 memcpy(&gx->projMatrix, &m, sizeof(gx->projMatrix));
499 break;
500 case 2:
501 memcpy(&gx->vecMatrix, &m, sizeof(gx->vecMatrix));
502 // Fall through
503 case 1:
504 memcpy(&gx->posMatrix, &m, sizeof(gx->posMatrix));
505 break;
506 case 3:
507 memcpy(&gx->texMatrix, &m, sizeof(gx->texMatrix));
508 break;
509 }
510 _updateClipMatrix(gx);
511 break;
512 }
513 case DS_GX_CMD_MTX_MULT_4x4: {
514 struct DSGXMatrix m;
515 int i;
516 for (i = 0; i < 16; ++i) {
517 m.m[i] = gx->activeEntries[i].params[0];
518 m.m[i] |= gx->activeEntries[i].params[1] << 8;
519 m.m[i] |= gx->activeEntries[i].params[2] << 16;
520 m.m[i] |= gx->activeEntries[i].params[3] << 24;
521 }
522 switch (gx->mtxMode) {
523 case 0:
524 DSGXMtxMultiply(&gx->projMatrix, &m, &gx->projMatrix);
525 break;
526 case 2:
527 DSGXMtxMultiply(&gx->vecMatrix, &m, &gx->vecMatrix);
528 // Fall through
529 case 1:
530 DSGXMtxMultiply(&gx->posMatrix, &m, &gx->posMatrix);
531 break;
532 case 3:
533 DSGXMtxMultiply(&gx->texMatrix, &m, &gx->texMatrix);
534 break;
535 }
536 _updateClipMatrix(gx);
537 break;
538 }
539 case DS_GX_CMD_MTX_MULT_4x3: {
540 struct DSGXMatrix m;
541 int i, j;
542 for (j = 0; j < 4; ++j) {
543 for (i = 0; i < 3; ++i) {
544 m.m[i + j * 4] = gx->activeEntries[i + j * 3].params[0];
545 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[1] << 8;
546 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[2] << 16;
547 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[3] << 24;
548 }
549 m.m[j * 4 + 3] = 0;
550 }
551 m.m[15] = MTX_ONE;
552 switch (gx->mtxMode) {
553 case 0:
554 DSGXMtxMultiply(&gx->projMatrix, &m, &gx->projMatrix);
555 break;
556 case 2:
557 DSGXMtxMultiply(&gx->vecMatrix, &m, &gx->vecMatrix);
558 // Fall through
559 case 1:
560 DSGXMtxMultiply(&gx->posMatrix, &m, &gx->posMatrix);
561 break;
562 case 3:
563 DSGXMtxMultiply(&gx->texMatrix, &m, &gx->texMatrix);
564 break;
565 }
566 _updateClipMatrix(gx);
567 break;
568 }
569 case DS_GX_CMD_MTX_MULT_3x3: {
570 struct DSGXMatrix m;
571 int i, j;
572 for (j = 0; j < 3; ++j) {
573 for (i = 0; i < 3; ++i) {
574 m.m[i + j * 4] = gx->activeEntries[i + j * 3].params[0];
575 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[1] << 8;
576 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[2] << 16;
577 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[3] << 24;
578 }
579 m.m[j * 4 + 3] = 0;
580 }
581 m.m[12] = 0;
582 m.m[13] = 0;
583 m.m[14] = 0;
584 m.m[15] = MTX_ONE;
585 switch (gx->mtxMode) {
586 case 0:
587 DSGXMtxMultiply(&gx->projMatrix, &m, &gx->projMatrix);
588 break;
589 case 2:
590 DSGXMtxMultiply(&gx->vecMatrix, &m, &gx->vecMatrix);
591 // Fall through
592 case 1:
593 DSGXMtxMultiply(&gx->posMatrix, &m, &gx->posMatrix);
594 break;
595 case 3:
596 DSGXMtxMultiply(&gx->texMatrix, &m, &gx->texMatrix);
597 break;
598 }
599 _updateClipMatrix(gx);
600 break;
601 }
602 case DS_GX_CMD_MTX_TRANS: {
603 int32_t m[3];
604 m[0] = gx->activeEntries[0].params[0];
605 m[0] |= gx->activeEntries[0].params[1] << 8;
606 m[0] |= gx->activeEntries[0].params[2] << 16;
607 m[0] |= gx->activeEntries[0].params[3] << 24;
608 m[1] = gx->activeEntries[1].params[0];
609 m[1] |= gx->activeEntries[1].params[1] << 8;
610 m[1] |= gx->activeEntries[1].params[2] << 16;
611 m[1] |= gx->activeEntries[1].params[3] << 24;
612 m[2] = gx->activeEntries[2].params[0];
613 m[2] |= gx->activeEntries[2].params[1] << 8;
614 m[2] |= gx->activeEntries[2].params[2] << 16;
615 m[2] |= gx->activeEntries[2].params[3] << 24;
616 switch (gx->mtxMode) {
617 case 0:
618 DSGXMtxTranslate(&gx->projMatrix, m);
619 break;
620 case 2:
621 DSGXMtxTranslate(&gx->vecMatrix, m);
622 // Fall through
623 case 1:
624 DSGXMtxTranslate(&gx->posMatrix, m);
625 break;
626 case 3:
627 DSGXMtxTranslate(&gx->texMatrix, m);
628 break;
629 }
630 _updateClipMatrix(gx);
631 break;
632 }
633 case DS_GX_CMD_MTX_SCALE: {
634 int32_t m[3];
635 m[0] = gx->activeEntries[0].params[0];
636 m[0] |= gx->activeEntries[0].params[1] << 8;
637 m[0] |= gx->activeEntries[0].params[2] << 16;
638 m[0] |= gx->activeEntries[0].params[3] << 24;
639 m[1] = gx->activeEntries[1].params[0];
640 m[1] |= gx->activeEntries[1].params[1] << 8;
641 m[1] |= gx->activeEntries[1].params[2] << 16;
642 m[1] |= gx->activeEntries[1].params[3] << 24;
643 m[2] = gx->activeEntries[2].params[0];
644 m[2] |= gx->activeEntries[2].params[1] << 8;
645 m[2] |= gx->activeEntries[2].params[2] << 16;
646 m[2] |= gx->activeEntries[2].params[3] << 24;
647 switch (gx->mtxMode) {
648 case 0:
649 DSGXMtxScale(&gx->projMatrix, m);
650 break;
651 case 2:
652 DSGXMtxScale(&gx->vecMatrix, m);
653 // Fall through
654 case 1:
655 DSGXMtxScale(&gx->posMatrix, m);
656 break;
657 case 3:
658 DSGXMtxScale(&gx->texMatrix, m);
659 break;
660 }
661 _updateClipMatrix(gx);
662 break;
663 }
664 case DS_GX_CMD_COLOR:
665 gx->currentVertex.color = entry.params[0];
666 gx->currentVertex.color |= entry.params[1] << 8;
667 break;
668 case DS_GX_CMD_TEXCOORD:
669 gx->currentVertex.s = entry.params[0];
670 gx->currentVertex.s |= entry.params[1] << 8;
671 gx->currentVertex.t = entry.params[2];
672 gx->currentVertex.t |= entry.params[3] << 8;
673 break;
674 case DS_GX_CMD_VTX_16: {
675 int16_t x = gx->activeEntries[0].params[0];
676 x |= gx->activeEntries[0].params[1] << 8;
677 int16_t y = gx->activeEntries[0].params[2];
678 y |= gx->activeEntries[0].params[3] << 8;
679 int16_t z = gx->activeEntries[1].params[0];
680 z |= gx->activeEntries[1].params[1] << 8;
681 _emitVertex(gx, x, y, z);
682 break;
683 }
684 case DS_GX_CMD_VTX_10: {
685 int32_t xyz = entry.params[0];
686 xyz |= entry.params[1] << 8;
687 xyz |= entry.params[2] << 16;
688 xyz |= entry.params[3] << 24;
689 int16_t x = (xyz << 6) & 0xFFC0;
690 int16_t y = (xyz >> 4) & 0xFFC0;
691 int16_t z = (xyz >> 14) & 0xFFC0;
692 _emitVertex(gx, x, y, z);
693 break;
694 }
695 case DS_GX_CMD_VTX_XY: {
696 int16_t x = entry.params[0];
697 x |= entry.params[1] << 8;
698 int16_t y = entry.params[2];
699 y |= entry.params[3] << 8;
700 _emitVertex(gx, x, y, gx->currentVertex.z);
701 break;
702 }
703 case DS_GX_CMD_VTX_XZ: {
704 int16_t x = entry.params[0];
705 x |= entry.params[1] << 8;
706 int16_t z = entry.params[2];
707 z |= entry.params[3] << 8;
708 _emitVertex(gx, x, gx->currentVertex.y, z);
709 break;
710 }
711 case DS_GX_CMD_VTX_YZ: {
712 int16_t y = entry.params[0];
713 y |= entry.params[1] << 8;
714 int16_t z = entry.params[2];
715 z |= entry.params[3] << 8;
716 _emitVertex(gx, gx->currentVertex.x, y, z);
717 break;
718 }
719 case DS_GX_CMD_VTX_DIFF: {
720 int32_t xyz = entry.params[0];
721 xyz |= entry.params[1] << 8;
722 xyz |= entry.params[2] << 16;
723 xyz |= entry.params[3] << 24;
724 int16_t x = (xyz << 6) & 0xFFC0;
725 int16_t y = (xyz >> 4) & 0xFFC0;
726 int16_t z = (xyz >> 14) & 0xFFC0;
727 _emitVertex(gx, gx->currentVertex.x + (x >> 6), gx->currentVertex.y + (y >> 6), gx->currentVertex.z + (z >> 6));
728 }
729 case DS_GX_CMD_POLYGON_ATTR:
730 gx->nextPoly.polyParams = entry.params[0];
731 gx->nextPoly.polyParams |= entry.params[1] << 8;
732 gx->nextPoly.polyParams |= entry.params[2] << 16;
733 gx->nextPoly.polyParams |= entry.params[3] << 24;
734 break;
735 case DS_GX_CMD_TEXIMAGE_PARAM:
736 gx->nextPoly.texParams = entry.params[0];
737 gx->nextPoly.texParams |= entry.params[1] << 8;
738 gx->nextPoly.texParams |= entry.params[2] << 16;
739 gx->nextPoly.texParams |= entry.params[3] << 24;
740 break;
741 case DS_GX_CMD_PLTT_BASE:
742 gx->nextPoly.palBase = entry.params[0];
743 gx->nextPoly.palBase |= entry.params[1] << 8;
744 gx->nextPoly.palBase |= entry.params[2] << 16;
745 gx->nextPoly.palBase |= entry.params[3] << 24;
746 break;
747 case DS_GX_CMD_BEGIN_VTXS:
748 gx->vertexMode = entry.params[0] & 3;
749 gx->currentPoly = gx->nextPoly;
750 break;
751 case DS_GX_CMD_END_VTXS:
752 gx->vertexMode = -1;
753 break;
754 case DS_GX_CMD_SWAP_BUFFERS:
755 gx->swapBuffers = true;
756 break;
757 case DS_GX_CMD_VIEWPORT:
758 gx->viewportX1 = (uint8_t) entry.params[0];
759 gx->viewportY1 = (uint8_t) entry.params[1];
760 gx->viewportX2 = (uint8_t) entry.params[2];
761 gx->viewportY2 = (uint8_t) entry.params[3];
762 gx->viewportWidth = gx->viewportX2 - gx->viewportX1;
763 gx->viewportHeight = gx->viewportY2 - gx->viewportY1;
764 break;
765 default:
766 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]);
767 break;
768 }
769
770 gxstat = DSRegGXSTATSetPVMatrixStackLevel(gxstat, gx->pvMatrixPointer);
771 gxstat = DSRegGXSTATSetProjMatrixStackLevel(gxstat, projMatrixPointer);
772 gxstat = DSRegGXSTATTestFillMatrixStackError(gxstat, projMatrixPointer || gx->pvMatrixPointer >= 0x1F);
773 gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1] = gxstat;
774
775 if (cyclesLate >= cycles) {
776 cyclesLate -= cycles;
777 } else {
778 break;
779 }
780 }
781 if (cycles && !gx->swapBuffers) {
782 mTimingSchedule(timing, &gx->fifoEvent, cycles - cyclesLate);
783 }
784 _flushOutstanding(gx);
785 DSGXUpdateGXSTAT(gx);
786}
787
788void DSGXInit(struct DSGX* gx) {
789 gx->renderer = &dummyRenderer;
790 CircleBufferInit(&gx->fifo, sizeof(struct DSGXEntry) * DS_GX_FIFO_SIZE);
791 CircleBufferInit(&gx->pipe, sizeof(struct DSGXEntry) * DS_GX_PIPE_SIZE);
792 gx->vertexBuffer[0] = malloc(sizeof(struct DSGXVertex) * DS_GX_VERTEX_BUFFER_SIZE);
793 gx->vertexBuffer[1] = malloc(sizeof(struct DSGXVertex) * DS_GX_VERTEX_BUFFER_SIZE);
794 gx->polygonBuffer[0] = malloc(sizeof(struct DSGXPolygon) * DS_GX_POLYGON_BUFFER_SIZE);
795 gx->polygonBuffer[1] = malloc(sizeof(struct DSGXPolygon) * DS_GX_POLYGON_BUFFER_SIZE);
796 gx->fifoEvent.name = "DS GX FIFO";
797 gx->fifoEvent.priority = 0xC;
798 gx->fifoEvent.context = gx;
799 gx->fifoEvent.callback = _fifoRun;
800}
801
802void DSGXDeinit(struct DSGX* gx) {
803 DSGXAssociateRenderer(gx, &dummyRenderer);
804 CircleBufferDeinit(&gx->fifo);
805 CircleBufferDeinit(&gx->pipe);
806 free(gx->vertexBuffer[0]);
807 free(gx->vertexBuffer[1]);
808 free(gx->polygonBuffer[0]);
809 free(gx->polygonBuffer[1]);
810}
811
812void DSGXReset(struct DSGX* gx) {
813 CircleBufferClear(&gx->fifo);
814 CircleBufferClear(&gx->pipe);
815 DSGXMtxIdentity(&gx->projMatrix);
816 DSGXMtxIdentity(&gx->texMatrix);
817 DSGXMtxIdentity(&gx->posMatrix);
818 DSGXMtxIdentity(&gx->vecMatrix);
819
820 DSGXMtxIdentity(&gx->clipMatrix);
821 DSGXMtxIdentity(&gx->projMatrixStack);
822 DSGXMtxIdentity(&gx->texMatrixStack);
823 int i;
824 for (i = 0; i < 32; ++i) {
825 DSGXMtxIdentity(&gx->posMatrixStack[i]);
826 DSGXMtxIdentity(&gx->vecMatrixStack[i]);
827 }
828 gx->swapBuffers = false;
829 gx->bufferIndex = 0;
830 gx->vertexIndex = 0;
831 gx->polygonIndex = 0;
832 gx->mtxMode = 0;
833 gx->pvMatrixPointer = 0;
834 gx->vertexMode = -1;
835
836 gx->viewportX1 = 0;
837 gx->viewportY1 = 0;
838 gx->viewportX2 = DS_VIDEO_HORIZONTAL_PIXELS - 1;
839 gx->viewportY2 = DS_VIDEO_VERTICAL_PIXELS - 1;
840 gx->viewportWidth = gx->viewportX2 - gx->viewportX1;
841 gx->viewportHeight = gx->viewportY2 - gx->viewportY1;
842
843 memset(gx->outstandingParams, 0, sizeof(gx->outstandingParams));
844 memset(gx->outstandingCommand, 0, sizeof(gx->outstandingCommand));
845 memset(&gx->outstandingEntry, 0, sizeof(gx->outstandingEntry));
846 gx->activeParams = 0;
847 memset(&gx->currentVertex, 0, sizeof(gx->currentVertex));
848 memset(&gx->nextPoly, 0, sizeof(gx-> nextPoly));
849}
850
851void DSGXAssociateRenderer(struct DSGX* gx, struct DSGXRenderer* renderer) {
852 gx->renderer->deinit(gx->renderer);
853 gx->renderer = renderer;
854 memcpy(gx->renderer->tex, gx->tex, sizeof(gx->renderer->tex));
855 memcpy(gx->renderer->texPal, gx->texPal, sizeof(gx->renderer->texPal));
856 gx->renderer->init(gx->renderer);
857}
858
859void DSGXUpdateGXSTAT(struct DSGX* gx) {
860 uint32_t value = gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] << 16;
861 value = DSRegGXSTATIsDoIRQ(value);
862
863 size_t entries = CircleBufferSize(&gx->fifo) / sizeof(struct DSGXEntry);
864 // XXX
865 if (gx->swapBuffers) {
866 entries++;
867 }
868 value = DSRegGXSTATSetFIFOEntries(value, entries);
869 value = DSRegGXSTATSetFIFOLtHalf(value, entries < (DS_GX_FIFO_SIZE / 2));
870 value = DSRegGXSTATSetFIFOEmpty(value, entries == 0);
871
872 if ((DSRegGXSTATGetDoIRQ(value) == 1 && entries < (DS_GX_FIFO_SIZE / 2)) ||
873 (DSRegGXSTATGetDoIRQ(value) == 2 && entries == 0)) {
874 DSRaiseIRQ(gx->p->ds9.cpu, gx->p->ds9.memory.io, DS_IRQ_GEOM_FIFO);
875 }
876
877 value = DSRegGXSTATSetBusy(value, mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent) || gx->swapBuffers);
878
879 gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] = value >> 16;
880}
881
882static void DSGXUnpackCommand(struct DSGX* gx, uint32_t command) {
883 gx->outstandingCommand[0] = command;
884 gx->outstandingCommand[1] = command >> 8;
885 gx->outstandingCommand[2] = command >> 16;
886 gx->outstandingCommand[3] = command >> 24;
887 if (gx->outstandingCommand[0] >= DS_GX_CMD_MAX) {
888 gx->outstandingCommand[0] = 0;
889 }
890 if (gx->outstandingCommand[1] >= DS_GX_CMD_MAX) {
891 gx->outstandingCommand[1] = 0;
892 }
893 if (gx->outstandingCommand[2] >= DS_GX_CMD_MAX) {
894 gx->outstandingCommand[2] = 0;
895 }
896 if (gx->outstandingCommand[3] >= DS_GX_CMD_MAX) {
897 gx->outstandingCommand[3] = 0;
898 }
899 gx->outstandingParams[0] = _gxCommandParams[gx->outstandingCommand[0]];
900 gx->outstandingParams[1] = _gxCommandParams[gx->outstandingCommand[1]];
901 gx->outstandingParams[2] = _gxCommandParams[gx->outstandingCommand[2]];
902 gx->outstandingParams[3] = _gxCommandParams[gx->outstandingCommand[3]];
903 _flushOutstanding(gx);
904 DSGXUpdateGXSTAT(gx);
905}
906
907static void DSGXWriteFIFO(struct DSGX* gx, struct DSGXEntry entry) {
908 if (CircleBufferSize(&gx->fifo) == (DS_GX_FIFO_SIZE * sizeof(entry))) {
909 mLOG(DS_GX, INFO, "FIFO full");
910 if (gx->p->cpuBlocked & DS_CPU_BLOCK_GX) {
911 abort();
912 }
913 gx->p->cpuBlocked |= DS_CPU_BLOCK_GX;
914 gx->outstandingEntry = entry;
915 gx->p->ds9.cpu->nextEvent = 0;
916 return;
917 }
918 if (gx->outstandingCommand[0]) {
919 entry.command = gx->outstandingCommand[0];
920 if (gx->outstandingParams[0]) {
921 --gx->outstandingParams[0];
922 }
923 if (!gx->outstandingParams[0]) {
924 // TODO: improve this
925 memmove(&gx->outstandingParams[0], &gx->outstandingParams[1], sizeof(gx->outstandingParams[0]) * 3);
926 memmove(&gx->outstandingCommand[0], &gx->outstandingCommand[1], sizeof(gx->outstandingCommand[0]) * 3);
927 gx->outstandingParams[3] = 0;
928 gx->outstandingCommand[3] = 0;
929 }
930 } else {
931 gx->outstandingParams[0] = _gxCommandParams[entry.command];
932 if (gx->outstandingParams[0]) {
933 --gx->outstandingParams[0];
934 }
935 if (gx->outstandingParams[0]) {
936 gx->outstandingCommand[0] = entry.command;
937 }
938 }
939 uint32_t cycles = _gxCommandCycleBase[entry.command];
940 if (!cycles) {
941 return;
942 }
943 if (CircleBufferSize(&gx->fifo) == 0 && CircleBufferSize(&gx->pipe) < (DS_GX_PIPE_SIZE * sizeof(entry))) {
944 CircleBufferWrite8(&gx->pipe, entry.command);
945 CircleBufferWrite8(&gx->pipe, entry.params[0]);
946 CircleBufferWrite8(&gx->pipe, entry.params[1]);
947 CircleBufferWrite8(&gx->pipe, entry.params[2]);
948 CircleBufferWrite8(&gx->pipe, entry.params[3]);
949 } else if (CircleBufferSize(&gx->fifo) < (DS_GX_FIFO_SIZE * sizeof(entry))) {
950 CircleBufferWrite8(&gx->fifo, entry.command);
951 CircleBufferWrite8(&gx->fifo, entry.params[0]);
952 CircleBufferWrite8(&gx->fifo, entry.params[1]);
953 CircleBufferWrite8(&gx->fifo, entry.params[2]);
954 CircleBufferWrite8(&gx->fifo, entry.params[3]);
955 }
956 if (!gx->swapBuffers && !mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent)) {
957 mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, cycles);
958 }
959
960 _flushOutstanding(gx);
961}
962
963uint16_t DSGXWriteRegister(struct DSGX* gx, uint32_t address, uint16_t value) {
964 uint16_t oldValue = gx->p->memory.io9[address >> 1];
965 switch (address) {
966 case DS9_REG_DISP3DCNT:
967 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
968 break;
969 case DS9_REG_GXSTAT_LO:
970 value = DSRegGXSTATIsMatrixStackError(value);
971 if (value) {
972 oldValue = DSRegGXSTATClearMatrixStackError(oldValue);
973 oldValue = DSRegGXSTATClearProjMatrixStackLevel(oldValue);
974 }
975 value = oldValue;
976 break;
977 case DS9_REG_GXSTAT_HI:
978 value = DSRegGXSTATIsDoIRQ(value << 16) >> 16;
979 gx->p->memory.io9[address >> 1] = value;
980 DSGXUpdateGXSTAT(gx);
981 value = gx->p->memory.io9[address >> 1];
982 break;
983 default:
984 if (address < DS9_REG_GXFIFO_00) {
985 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
986 } else if (address <= DS9_REG_GXFIFO_1F) {
987 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
988 } else if (address < DS9_REG_GXSTAT_LO) {
989 struct DSGXEntry entry = {
990 .command = (address & 0x1FC) >> 2,
991 .params = {
992 value,
993 value >> 8,
994 }
995 };
996 if (entry.command < DS_GX_CMD_MAX) {
997 DSGXWriteFIFO(gx, entry);
998 }
999 } else {
1000 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
1001 }
1002 break;
1003 }
1004 return value;
1005}
1006
1007uint32_t DSGXWriteRegister32(struct DSGX* gx, uint32_t address, uint32_t value) {
1008 switch (address) {
1009 case DS9_REG_DISP3DCNT:
1010 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
1011 break;
1012 case DS9_REG_GXSTAT_LO:
1013 value = (value & 0xFFFF0000) | DSGXWriteRegister(gx, DS9_REG_GXSTAT_LO, value);
1014 value = (value & 0x0000FFFF) | (DSGXWriteRegister(gx, DS9_REG_GXSTAT_HI, value >> 16) << 16);
1015 break;
1016 default:
1017 if (address < DS9_REG_GXFIFO_00) {
1018 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
1019 } else if (address <= DS9_REG_GXFIFO_1F) {
1020 if (gx->outstandingParams[0]) {
1021 struct DSGXEntry entry = {
1022 .command = gx->outstandingCommand[0],
1023 .params = {
1024 value,
1025 value >> 8,
1026 value >> 16,
1027 value >> 24
1028 }
1029 };
1030 DSGXWriteFIFO(gx, entry);
1031 } else {
1032 DSGXUnpackCommand(gx, value);
1033 }
1034 } else if (address < DS9_REG_GXSTAT_LO) {
1035 struct DSGXEntry entry = {
1036 .command = (address & 0x1FC) >> 2,
1037 .params = {
1038 value,
1039 value >> 8,
1040 value >> 16,
1041 value >> 24
1042 }
1043 };
1044 DSGXWriteFIFO(gx, entry);
1045 } else {
1046 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
1047 }
1048 break;
1049 }
1050 return value;
1051}
1052
1053void DSGXFlush(struct DSGX* gx) {
1054 if (gx->swapBuffers) {
1055 gx->renderer->setRAM(gx->renderer, gx->vertexBuffer[gx->bufferIndex], gx->polygonBuffer[gx->bufferIndex], gx->polygonIndex);
1056 gx->swapBuffers = false;
1057 gx->bufferIndex ^= 1;
1058 gx->vertexIndex = 0;
1059 gx->polygonIndex = 0;
1060 }
1061
1062 if (CircleBufferSize(&gx->fifo)) {
1063 mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, 0);
1064 }
1065 DSGXUpdateGXSTAT(gx);
1066}
1067
1068static void DSGXDummyRendererInit(struct DSGXRenderer* renderer) {
1069 UNUSED(renderer);
1070 // Nothing to do
1071}
1072
1073static void DSGXDummyRendererReset(struct DSGXRenderer* renderer) {
1074 UNUSED(renderer);
1075 // Nothing to do
1076}
1077
1078static void DSGXDummyRendererDeinit(struct DSGXRenderer* renderer) {
1079 UNUSED(renderer);
1080 // Nothing to do
1081}
1082
1083static void DSGXDummyRendererInvalidateTex(struct DSGXRenderer* renderer, int slot) {
1084 UNUSED(renderer);
1085 UNUSED(slot);
1086 // Nothing to do
1087}
1088
1089static void DSGXDummyRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount) {
1090 UNUSED(renderer);
1091 UNUSED(verts);
1092 UNUSED(polys);
1093 UNUSED(polyCount);
1094 // Nothing to do
1095}
1096
1097static void DSGXDummyRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
1098 UNUSED(renderer);
1099 UNUSED(y);
1100 // Nothing to do
1101}
1102
1103static void DSGXDummyRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) {
1104 UNUSED(renderer);
1105 UNUSED(y);
1106 *output = NULL;
1107 // Nothing to do
1108}