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