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