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