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