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