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 _fifoRun(struct mTiming* timing, void* context, uint32_t cyclesLate) {
140 struct DSGX* gx = context;
141 uint32_t cycles;
142 bool first = true;
143 while (!gx->swapBuffers) {
144 if (CircleBufferSize(&gx->pipe) <= 2 * sizeof(struct DSGXEntry)) {
145 _pullPipe(gx);
146 }
147
148 if (!CircleBufferSize(&gx->pipe)) {
149 cycles = 0;
150 break;
151 }
152
153 DSRegGXSTAT gxstat = gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1];
154 int projMatrixPointer = DSRegGXSTATGetProjMatrixStackLevel(gxstat);
155
156 struct DSGXEntry entry = { 0 };
157 CircleBufferDump(&gx->pipe, (int8_t*) &entry.command, 1);
158 cycles = _gxCommandCycleBase[entry.command];
159
160 if (first) {
161 first = false;
162 } else if (cycles > cyclesLate) {
163 break;
164 }
165 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.command);
166 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[0]);
167 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[1]);
168 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[2]);
169 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[3]);
170
171 if (gx->activeParams) {
172 int index = _gxCommandParams[entry.command] - gx->activeParams;
173 gx->activeEntries[index] = entry;
174 --gx->activeParams;
175 } else {
176 gx->activeParams = _gxCommandParams[entry.command];
177 if (gx->activeParams) {
178 --gx->activeParams;
179 }
180 if (gx->activeParams) {
181 gx->activeEntries[0] = entry;
182 }
183 }
184
185 if (gx->activeParams) {
186 continue;
187 }
188
189 switch (entry.command) {
190 case DS_GX_CMD_MTX_MODE:
191 if (entry.params[0] < 4) {
192 gx->mtxMode = entry.params[0];
193 } else {
194 mLOG(DS_GX, GAME_ERROR, "Invalid GX MTX_MODE %02X", entry.params[0]);
195 }
196 break;
197 case DS_GX_CMD_MTX_PUSH:
198 switch (gx->mtxMode) {
199 case 0:
200 memcpy(&gx->projMatrixStack, &gx->projMatrix, sizeof(gx->projMatrix));
201 ++projMatrixPointer;
202 break;
203 case 2:
204 memcpy(&gx->vecMatrixStack[gx->pvMatrixPointer & 0x1F], &gx->vecMatrix, sizeof(gx->vecMatrix));
205 // Fall through
206 case 1:
207 memcpy(&gx->posMatrixStack[gx->pvMatrixPointer & 0x1F], &gx->posMatrix, sizeof(gx->posMatrix));
208 ++gx->pvMatrixPointer;
209 break;
210 case 3:
211 mLOG(DS_GX, STUB, "Unimplemented GX MTX_PUSH mode");
212 break;
213 }
214 break;
215 case DS_GX_CMD_MTX_POP: {
216 int8_t offset = entry.params[0];
217 offset <<= 2;
218 offset >>= 2;
219 switch (gx->mtxMode) {
220 case 0:
221 projMatrixPointer -= offset;
222 memcpy(&gx->projMatrix, &gx->projMatrixStack, sizeof(gx->projMatrix));
223 break;
224 case 1:
225 gx->pvMatrixPointer -= offset;
226 memcpy(&gx->posMatrix, &gx->posMatrixStack[gx->pvMatrixPointer & 0x1F], sizeof(gx->posMatrix));
227 break;
228 case 2:
229 gx->pvMatrixPointer -= offset;
230 memcpy(&gx->vecMatrix, &gx->vecMatrixStack[gx->pvMatrixPointer & 0x1F], sizeof(gx->vecMatrix));
231 memcpy(&gx->posMatrix, &gx->posMatrixStack[gx->pvMatrixPointer & 0x1F], sizeof(gx->posMatrix));
232 break;
233 case 3:
234 mLOG(DS_GX, STUB, "Unimplemented GX MTX_POP mode");
235 break;
236 }
237 break;
238 }
239 case DS_GX_CMD_MTX_IDENTITY:
240 switch (gx->mtxMode) {
241 case 0:
242 DSGXMtxIdentity(&gx->projMatrix);
243 break;
244 case 2:
245 DSGXMtxIdentity(&gx->vecMatrix);
246 // Fall through
247 case 1:
248 DSGXMtxIdentity(&gx->posMatrix);
249 break;
250 case 3:
251 DSGXMtxIdentity(&gx->texMatrix);
252 break;
253 }
254 break;
255 case DS_GX_CMD_MTX_LOAD_4x4: {
256 struct DSGXMatrix m;
257 int i;
258 for (i = 0; i < 16; ++i) {
259 m.m[i] = gx->activeEntries[i].params[0];
260 m.m[i] |= gx->activeEntries[i].params[1] << 8;
261 m.m[i] |= gx->activeEntries[i].params[2] << 16;
262 m.m[i] |= gx->activeEntries[i].params[3] << 24;
263 }
264 switch (gx->mtxMode) {
265 case 0:
266 memcpy(&gx->projMatrix, &m, sizeof(gx->projMatrix));
267 break;
268 case 2:
269 memcpy(&gx->vecMatrix, &m, sizeof(gx->vecMatrix));
270 // Fall through
271 case 1:
272 memcpy(&gx->posMatrix, &m, sizeof(gx->posMatrix));
273 break;
274 case 3:
275 memcpy(&gx->texMatrix, &m, sizeof(gx->texMatrix));
276 break;
277 }
278 break;
279 }
280 case DS_GX_CMD_MTX_LOAD_4x3: {
281 struct DSGXMatrix m;
282 int i, j;
283 for (j = 0; j < 4; ++j) {
284 for (i = 0; i < 3; ++i) {
285 m.m[i + j * 4] = gx->activeEntries[i + j * 3].params[0];
286 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[1] << 8;
287 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[2] << 16;
288 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[3] << 24;
289 }
290 m.m[j * 4 + 3] = 0;
291 }
292 m.m[15] = MTX_ONE;
293 switch (gx->mtxMode) {
294 case 0:
295 memcpy(&gx->projMatrix, &m, sizeof(gx->projMatrix));
296 break;
297 case 2:
298 memcpy(&gx->vecMatrix, &m, sizeof(gx->vecMatrix));
299 // Fall through
300 case 1:
301 memcpy(&gx->posMatrix, &m, sizeof(gx->posMatrix));
302 break;
303 case 3:
304 memcpy(&gx->texMatrix, &m, sizeof(gx->texMatrix));
305 break;
306 }
307 break;
308 }
309 case DS_GX_CMD_MTX_MULT_4x4: {
310 struct DSGXMatrix m;
311 int i;
312 for (i = 0; i < 16; ++i) {
313 m.m[i] = gx->activeEntries[i].params[0];
314 m.m[i] |= gx->activeEntries[i].params[1] << 8;
315 m.m[i] |= gx->activeEntries[i].params[2] << 16;
316 m.m[i] |= gx->activeEntries[i].params[3] << 24;
317 }
318 switch (gx->mtxMode) {
319 case 0:
320 DSGXMtxMultiply(&gx->projMatrix, &m);
321 break;
322 case 2:
323 DSGXMtxMultiply(&gx->vecMatrix, &m);
324 // Fall through
325 case 1:
326 DSGXMtxMultiply(&gx->posMatrix, &m);
327 break;
328 case 3:
329 DSGXMtxMultiply(&gx->texMatrix, &m);
330 break;
331 }
332 break;
333 }
334 case DS_GX_CMD_MTX_MULT_4x3: {
335 struct DSGXMatrix m;
336 int i, j;
337 for (j = 0; j < 4; ++j) {
338 for (i = 0; i < 3; ++i) {
339 m.m[i + j * 4] = gx->activeEntries[i + j * 3].params[0];
340 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[1] << 8;
341 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[2] << 16;
342 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[3] << 24;
343 }
344 m.m[j * 4 + 3] = 0;
345 }
346 m.m[15] = MTX_ONE;
347 switch (gx->mtxMode) {
348 case 0:
349 DSGXMtxMultiply(&gx->projMatrix, &m);
350 break;
351 case 2:
352 DSGXMtxMultiply(&gx->vecMatrix, &m);
353 // Fall through
354 case 1:
355 DSGXMtxMultiply(&gx->posMatrix, &m);
356 break;
357 case 3:
358 DSGXMtxMultiply(&gx->texMatrix, &m);
359 break;
360 }
361 break;
362 }
363 case DS_GX_CMD_MTX_MULT_3x3: {
364 struct DSGXMatrix m;
365 int i, j;
366 for (j = 0; j < 3; ++j) {
367 for (i = 0; i < 3; ++i) {
368 m.m[i + j * 4] = gx->activeEntries[i + j * 3].params[0];
369 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[1] << 8;
370 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[2] << 16;
371 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[3] << 24;
372 }
373 m.m[j * 4 + 3] = 0;
374 }
375 m.m[12] = 0;
376 m.m[13] = 0;
377 m.m[14] = 0;
378 m.m[15] = MTX_ONE;
379 switch (gx->mtxMode) {
380 case 0:
381 memcpy(&gx->projMatrix, &m, sizeof(gx->projMatrix));
382 break;
383 case 2:
384 memcpy(&gx->vecMatrix, &m, sizeof(gx->vecMatrix));
385 // Fall through
386 case 1:
387 memcpy(&gx->posMatrix, &m, sizeof(gx->posMatrix));
388 break;
389 case 3:
390 memcpy(&gx->texMatrix, &m, sizeof(gx->projMatrix));
391 break;
392 }
393 break;
394 }
395 case DS_GX_CMD_MTX_TRANS: {
396 int32_t m[3];
397 m[0] = gx->activeEntries[0].params[0];
398 m[0] |= gx->activeEntries[0].params[1] << 8;
399 m[0] |= gx->activeEntries[0].params[2] << 16;
400 m[0] |= gx->activeEntries[0].params[3] << 24;
401 m[1] = gx->activeEntries[1].params[0];
402 m[1] |= gx->activeEntries[1].params[1] << 8;
403 m[1] |= gx->activeEntries[1].params[2] << 16;
404 m[1] |= gx->activeEntries[1].params[3] << 24;
405 m[2] = gx->activeEntries[2].params[0];
406 m[2] |= gx->activeEntries[2].params[1] << 8;
407 m[2] |= gx->activeEntries[2].params[2] << 16;
408 m[2] |= gx->activeEntries[2].params[3] << 24;
409 switch (gx->mtxMode) {
410 case 0:
411 DSGXMtxTranslate(&gx->projMatrix, m);
412 break;
413 case 2:
414 DSGXMtxTranslate(&gx->vecMatrix, m);
415 // Fall through
416 case 1:
417 DSGXMtxTranslate(&gx->posMatrix, m);
418 break;
419 case 3:
420 DSGXMtxTranslate(&gx->texMatrix, m);
421 break;
422 }
423 break;
424 }
425 case DS_GX_CMD_MTX_SCALE: {
426 int32_t m[3];
427 m[0] = gx->activeEntries[0].params[0];
428 m[0] |= gx->activeEntries[0].params[1] << 8;
429 m[0] |= gx->activeEntries[0].params[2] << 16;
430 m[0] |= gx->activeEntries[0].params[3] << 24;
431 m[1] = gx->activeEntries[1].params[0];
432 m[1] |= gx->activeEntries[1].params[1] << 8;
433 m[1] |= gx->activeEntries[1].params[2] << 16;
434 m[1] |= gx->activeEntries[1].params[3] << 24;
435 m[2] = gx->activeEntries[2].params[0];
436 m[2] |= gx->activeEntries[2].params[1] << 8;
437 m[2] |= gx->activeEntries[2].params[2] << 16;
438 m[2] |= gx->activeEntries[2].params[3] << 24;
439 switch (gx->mtxMode) {
440 case 0:
441 DSGXMtxScale(&gx->projMatrix, m);
442 break;
443 case 2:
444 DSGXMtxScale(&gx->vecMatrix, m);
445 // Fall through
446 case 1:
447 DSGXMtxScale(&gx->posMatrix, m);
448 break;
449 case 3:
450 DSGXMtxScale(&gx->texMatrix, m);
451 break;
452 }
453 break;
454 }
455 case DS_GX_CMD_SWAP_BUFFERS:
456 gx->swapBuffers = true;
457 break;
458 default:
459 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]);
460 break;
461 }
462
463 gxstat = DSRegGXSTATSetPVMatrixStackLevel(gxstat, gx->pvMatrixPointer);
464 gxstat = DSRegGXSTATSetProjMatrixStackLevel(gxstat, projMatrixPointer);
465 gxstat = DSRegGXSTATTestFillMatrixStackError(gxstat, projMatrixPointer || gx->pvMatrixPointer >= 0x1F);
466 gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1] = gxstat;
467
468 if (cyclesLate >= cycles) {
469 cyclesLate -= cycles;
470 } else {
471 break;
472 }
473 }
474 DSGXUpdateGXSTAT(gx);
475 if (cycles && !gx->swapBuffers) {
476 mTimingSchedule(timing, &gx->fifoEvent, cycles - cyclesLate);
477 }
478}
479
480void DSGXInit(struct DSGX* gx) {
481 gx->renderer = &dummyRenderer;
482 CircleBufferInit(&gx->fifo, sizeof(struct DSGXEntry) * DS_GX_FIFO_SIZE);
483 CircleBufferInit(&gx->pipe, sizeof(struct DSGXEntry) * DS_GX_PIPE_SIZE);
484 gx->fifoEvent.name = "DS GX FIFO";
485 gx->fifoEvent.priority = 0xC;
486 gx->fifoEvent.context = gx;
487 gx->fifoEvent.callback = _fifoRun;
488}
489
490void DSGXDeinit(struct DSGX* gx) {
491 DSGXAssociateRenderer(gx, &dummyRenderer);
492 CircleBufferDeinit(&gx->fifo);
493 CircleBufferDeinit(&gx->pipe);
494}
495
496void DSGXReset(struct DSGX* gx) {
497 CircleBufferClear(&gx->fifo);
498 CircleBufferClear(&gx->pipe);
499 DSGXMtxIdentity(&gx->projMatrix);
500 DSGXMtxIdentity(&gx->texMatrix);
501 DSGXMtxIdentity(&gx->posMatrix);
502 DSGXMtxIdentity(&gx->vecMatrix);
503
504 DSGXMtxIdentity(&gx->projMatrixStack);
505 DSGXMtxIdentity(&gx->texMatrixStack);
506 int i;
507 for (i = 0; i < 32; ++i) {
508 DSGXMtxIdentity(&gx->posMatrixStack[i]);
509 DSGXMtxIdentity(&gx->vecMatrixStack[i]);
510 }
511 gx->swapBuffers = false;
512 gx->bufferIndex = 0;
513 gx->mtxMode = 0;
514 gx->pvMatrixPointer = 0;
515
516 memset(gx->outstandingParams, 0, sizeof(gx->outstandingParams));
517 memset(gx->outstandingCommand, 0, sizeof(gx->outstandingCommand));
518 gx->activeParams = 0;
519}
520
521void DSGXAssociateRenderer(struct DSGX* gx, struct DSGXRenderer* renderer) {
522 gx->renderer->deinit(gx->renderer);
523 gx->renderer = renderer;
524 gx->renderer->init(gx->renderer);
525}
526
527void DSGXUpdateGXSTAT(struct DSGX* gx) {
528 uint32_t value = gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] << 16;
529 value = DSRegGXSTATIsDoIRQ(value);
530
531 size_t entries = CircleBufferSize(&gx->fifo) / sizeof(struct DSGXEntry);
532 // XXX
533 if (gx->swapBuffers) {
534 entries++;
535 }
536 value = DSRegGXSTATSetFIFOEntries(value, entries);
537 value = DSRegGXSTATSetFIFOLtHalf(value, entries < (DS_GX_FIFO_SIZE / 2));
538 value = DSRegGXSTATSetFIFOEmpty(value, entries == 0);
539
540 if ((DSRegGXSTATGetDoIRQ(value) == 1 && entries < (DS_GX_FIFO_SIZE / 2)) ||
541 (DSRegGXSTATGetDoIRQ(value) == 2 && entries == 0)) {
542 DSRaiseIRQ(gx->p->ds9.cpu, gx->p->ds9.memory.io, DS_IRQ_GEOM_FIFO);
543 }
544
545 value = DSRegGXSTATSetBusy(value, mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent) || gx->swapBuffers);
546
547 gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] = value >> 16;
548}
549
550static void DSGXUnpackCommand(struct DSGX* gx, uint32_t command) {
551 gx->outstandingCommand[0] = command;
552 gx->outstandingCommand[1] = command >> 8;
553 gx->outstandingCommand[2] = command >> 16;
554 gx->outstandingCommand[3] = command >> 24;
555 if (gx->outstandingCommand[0] >= DS_GX_CMD_MAX) {
556 gx->outstandingCommand[0] = 0;
557 }
558 if (gx->outstandingCommand[1] >= DS_GX_CMD_MAX) {
559 gx->outstandingCommand[1] = 0;
560 }
561 if (gx->outstandingCommand[2] >= DS_GX_CMD_MAX) {
562 gx->outstandingCommand[2] = 0;
563 }
564 if (gx->outstandingCommand[3] >= DS_GX_CMD_MAX) {
565 gx->outstandingCommand[3] = 0;
566 }
567 gx->outstandingParams[0] = _gxCommandParams[gx->outstandingCommand[0]];
568 gx->outstandingParams[1] = _gxCommandParams[gx->outstandingCommand[1]];
569 gx->outstandingParams[2] = _gxCommandParams[gx->outstandingCommand[2]];
570 gx->outstandingParams[3] = _gxCommandParams[gx->outstandingCommand[3]];
571}
572
573static void DSGXWriteFIFO(struct DSGX* gx, struct DSGXEntry entry) {
574 if (gx->outstandingParams[0]) {
575 entry.command = gx->outstandingCommand[0];
576 --gx->outstandingParams[0];
577 if (!gx->outstandingParams[0]) {
578 // TODO: improve this
579 memmove(&gx->outstandingParams[0], &gx->outstandingParams[1], sizeof(gx->outstandingParams[0]) * 3);
580 memmove(&gx->outstandingCommand[0], &gx->outstandingCommand[1], sizeof(gx->outstandingCommand[0]) * 3);
581 gx->outstandingParams[3] = 0;
582 }
583 } else {
584 gx->outstandingCommand[0] = entry.command;
585 gx->outstandingParams[0] = _gxCommandParams[entry.command];
586 if (gx->outstandingParams[0]) {
587 --gx->outstandingParams[0];
588 }
589 }
590 uint32_t cycles = _gxCommandCycleBase[entry.command];
591 if (!cycles) {
592 return;
593 }
594 if (CircleBufferSize(&gx->fifo) == 0 && CircleBufferSize(&gx->pipe) < (DS_GX_PIPE_SIZE * sizeof(entry))) {
595 CircleBufferWrite8(&gx->pipe, entry.command);
596 CircleBufferWrite8(&gx->pipe, entry.params[0]);
597 CircleBufferWrite8(&gx->pipe, entry.params[1]);
598 CircleBufferWrite8(&gx->pipe, entry.params[2]);
599 CircleBufferWrite8(&gx->pipe, entry.params[3]);
600 } else if (CircleBufferSize(&gx->fifo) < (DS_GX_FIFO_SIZE * sizeof(entry))) {
601 CircleBufferWrite8(&gx->fifo, entry.command);
602 CircleBufferWrite8(&gx->fifo, entry.params[0]);
603 CircleBufferWrite8(&gx->fifo, entry.params[1]);
604 CircleBufferWrite8(&gx->fifo, entry.params[2]);
605 CircleBufferWrite8(&gx->fifo, entry.params[3]);
606 } else {
607 mLOG(DS_GX, STUB, "Unimplemented GX full");
608 }
609 if (!gx->swapBuffers && !mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent)) {
610 mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, cycles);
611 }
612}
613
614uint16_t DSGXWriteRegister(struct DSGX* gx, uint32_t address, uint16_t value) {
615 uint16_t oldValue = gx->p->memory.io9[address >> 1];
616 switch (address) {
617 case DS9_REG_DISP3DCNT:
618 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
619 break;
620 case DS9_REG_GXSTAT_LO:
621 value = DSRegGXSTATIsMatrixStackError(value);
622 if (value) {
623 oldValue = DSRegGXSTATClearMatrixStackError(oldValue);
624 oldValue = DSRegGXSTATClearProjMatrixStackLevel(oldValue);
625 }
626 value = oldValue;
627 break;
628 case DS9_REG_GXSTAT_HI:
629 value = DSRegGXSTATIsDoIRQ(value << 16) >> 16;
630 gx->p->memory.io9[address >> 1] = value;
631 DSGXUpdateGXSTAT(gx);
632 value = gx->p->memory.io9[address >> 1];
633 break;
634 default:
635 if (address < DS9_REG_GXFIFO_00) {
636 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
637 } else if (address <= DS9_REG_GXFIFO_1F) {
638 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
639 } else if (address < DS9_REG_GXSTAT_LO) {
640 struct DSGXEntry entry = {
641 .command = (address & 0x1FC) >> 2,
642 .params = {
643 value,
644 value >> 8,
645 }
646 };
647 if (entry.command < DS_GX_CMD_MAX) {
648 DSGXWriteFIFO(gx, entry);
649 }
650 } else {
651 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
652 }
653 break;
654 }
655 return value;
656}
657
658uint32_t DSGXWriteRegister32(struct DSGX* gx, uint32_t address, uint32_t value) {
659 switch (address) {
660 case DS9_REG_DISP3DCNT:
661 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
662 break;
663 case DS9_REG_GXSTAT_LO:
664 value = (value & 0xFFFF0000) | DSGXWriteRegister(gx, DS9_REG_GXSTAT_LO, value);
665 value = (value & 0x0000FFFF) | (DSGXWriteRegister(gx, DS9_REG_GXSTAT_HI, value >> 16) << 16);
666 break;
667 default:
668 if (address < DS9_REG_GXFIFO_00) {
669 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
670 } else if (address <= DS9_REG_GXFIFO_1F) {
671 if (gx->outstandingParams[0]) {
672 struct DSGXEntry entry = {
673 .command = gx->outstandingCommand[0],
674 .params = {
675 value,
676 value >> 8,
677 value >> 16,
678 value >> 24
679 }
680 };
681 DSGXWriteFIFO(gx, entry);
682 } else {
683 DSGXUnpackCommand(gx, value);
684 }
685 } else if (address < DS9_REG_GXSTAT_LO) {
686 struct DSGXEntry entry = {
687 .command = (address & 0x1FC) >> 2,
688 .params = {
689 value,
690 value >> 8,
691 value >> 16,
692 value >> 24
693 }
694 };
695 DSGXWriteFIFO(gx, entry);
696 } else {
697 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
698 }
699 break;
700 }
701 return value;
702}
703
704void DSGXSwapBuffers(struct DSGX* gx) {
705 mLOG(DS_GX, STUB, "Unimplemented GX swap buffers");
706 gx->swapBuffers = false;
707
708 // TODO
709 DSGXUpdateGXSTAT(gx);
710 if (CircleBufferSize(&gx->fifo)) {
711 mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, 0);
712 }
713}
714
715static void DSGXDummyRendererInit(struct DSGXRenderer* renderer) {
716 UNUSED(renderer);
717 // Nothing to do
718}
719
720static void DSGXDummyRendererReset(struct DSGXRenderer* renderer) {
721 UNUSED(renderer);
722 // Nothing to do
723}
724
725static void DSGXDummyRendererDeinit(struct DSGXRenderer* renderer) {
726 UNUSED(renderer);
727 // Nothing to do
728}
729
730static void DSGXDummyRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount) {
731 UNUSED(renderer);
732 UNUSED(verts);
733 UNUSED(polys);
734 UNUSED(polyCount);
735 // Nothing to do
736}
737
738static void DSGXDummyRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
739 UNUSED(renderer);
740 UNUSED(y);
741 // Nothing to do
742}
743
744static void DSGXDummyRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) {
745 UNUSED(renderer);
746 UNUSED(y);
747 *output = NULL;
748 // Nothing to do
749}