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