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 DSGXDummyRendererInvalidateTex(struct DSGXRenderer* renderer, int slot);
20static void DSGXDummyRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount, bool wSort);
21static void DSGXDummyRendererDrawScanline(struct DSGXRenderer* renderer, int y);
22static void DSGXDummyRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output);
23
24static void DSGXWriteFIFO(struct DSGX* gx, struct DSGXEntry entry);
25
26static const int32_t _gxCommandCycleBase[DS_GX_CMD_MAX] = {
27 [DS_GX_CMD_NOP] = 0,
28 [DS_GX_CMD_MTX_MODE] = 2,
29 [DS_GX_CMD_MTX_PUSH] = 34,
30 [DS_GX_CMD_MTX_POP] = 72,
31 [DS_GX_CMD_MTX_STORE] = 34,
32 [DS_GX_CMD_MTX_RESTORE] = 72,
33 [DS_GX_CMD_MTX_IDENTITY] = 38,
34 [DS_GX_CMD_MTX_LOAD_4x4] = 68,
35 [DS_GX_CMD_MTX_LOAD_4x3] = 60,
36 [DS_GX_CMD_MTX_MULT_4x4] = 70,
37 [DS_GX_CMD_MTX_MULT_4x3] = 62,
38 [DS_GX_CMD_MTX_MULT_3x3] = 56,
39 [DS_GX_CMD_MTX_SCALE] = 44,
40 [DS_GX_CMD_MTX_TRANS] = 44,
41 [DS_GX_CMD_COLOR] = 2,
42 [DS_GX_CMD_NORMAL] = 18,
43 [DS_GX_CMD_TEXCOORD] = 2,
44 [DS_GX_CMD_VTX_16] = 18,
45 [DS_GX_CMD_VTX_10] = 16,
46 [DS_GX_CMD_VTX_XY] = 16,
47 [DS_GX_CMD_VTX_XZ] = 16,
48 [DS_GX_CMD_VTX_YZ] = 16,
49 [DS_GX_CMD_VTX_DIFF] = 16,
50 [DS_GX_CMD_POLYGON_ATTR] = 2,
51 [DS_GX_CMD_TEXIMAGE_PARAM] = 2,
52 [DS_GX_CMD_PLTT_BASE] = 2,
53 [DS_GX_CMD_DIF_AMB] = 8,
54 [DS_GX_CMD_SPE_EMI] = 8,
55 [DS_GX_CMD_LIGHT_VECTOR] = 12,
56 [DS_GX_CMD_LIGHT_COLOR] = 2,
57 [DS_GX_CMD_SHININESS] = 64,
58 [DS_GX_CMD_BEGIN_VTXS] = 2,
59 [DS_GX_CMD_END_VTXS] = 2,
60 [DS_GX_CMD_SWAP_BUFFERS] = 784,
61 [DS_GX_CMD_VIEWPORT] = 2,
62 [DS_GX_CMD_BOX_TEST] = 206,
63 [DS_GX_CMD_POS_TEST] = 18,
64 [DS_GX_CMD_VEC_TEST] = 10,
65};
66
67static const int32_t _gxCommandParams[DS_GX_CMD_MAX] = {
68 [DS_GX_CMD_MTX_MODE] = 1,
69 [DS_GX_CMD_MTX_POP] = 1,
70 [DS_GX_CMD_MTX_STORE] = 1,
71 [DS_GX_CMD_MTX_RESTORE] = 1,
72 [DS_GX_CMD_MTX_LOAD_4x4] = 16,
73 [DS_GX_CMD_MTX_LOAD_4x3] = 12,
74 [DS_GX_CMD_MTX_MULT_4x4] = 16,
75 [DS_GX_CMD_MTX_MULT_4x3] = 12,
76 [DS_GX_CMD_MTX_MULT_3x3] = 9,
77 [DS_GX_CMD_MTX_SCALE] = 3,
78 [DS_GX_CMD_MTX_TRANS] = 3,
79 [DS_GX_CMD_COLOR] = 1,
80 [DS_GX_CMD_NORMAL] = 1,
81 [DS_GX_CMD_TEXCOORD] = 1,
82 [DS_GX_CMD_VTX_16] = 2,
83 [DS_GX_CMD_VTX_10] = 1,
84 [DS_GX_CMD_VTX_XY] = 1,
85 [DS_GX_CMD_VTX_XZ] = 1,
86 [DS_GX_CMD_VTX_YZ] = 1,
87 [DS_GX_CMD_VTX_DIFF] = 1,
88 [DS_GX_CMD_POLYGON_ATTR] = 1,
89 [DS_GX_CMD_TEXIMAGE_PARAM] = 1,
90 [DS_GX_CMD_PLTT_BASE] = 1,
91 [DS_GX_CMD_DIF_AMB] = 1,
92 [DS_GX_CMD_SPE_EMI] = 1,
93 [DS_GX_CMD_LIGHT_VECTOR] = 1,
94 [DS_GX_CMD_LIGHT_COLOR] = 1,
95 [DS_GX_CMD_SHININESS] = 32,
96 [DS_GX_CMD_BEGIN_VTXS] = 1,
97 [DS_GX_CMD_SWAP_BUFFERS] = 1,
98 [DS_GX_CMD_VIEWPORT] = 1,
99 [DS_GX_CMD_BOX_TEST] = 3,
100 [DS_GX_CMD_POS_TEST] = 2,
101 [DS_GX_CMD_VEC_TEST] = 1,
102};
103
104static struct DSGXRenderer dummyRenderer = {
105 .init = DSGXDummyRendererInit,
106 .reset = DSGXDummyRendererReset,
107 .deinit = DSGXDummyRendererDeinit,
108 .invalidateTex = DSGXDummyRendererInvalidateTex,
109 .setRAM = DSGXDummyRendererSetRAM,
110 .drawScanline = DSGXDummyRendererDrawScanline,
111 .getScanline = DSGXDummyRendererGetScanline,
112};
113
114static void _pullPipe(struct DSGX* gx) {
115 if (CircleBufferSize(&gx->fifo) >= sizeof(struct DSGXEntry)) {
116 struct DSGXEntry entry = { 0 };
117 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.command);
118 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[0]);
119 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[1]);
120 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[2]);
121 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[3]);
122 CircleBufferWrite8(&gx->pipe, entry.command);
123 CircleBufferWrite8(&gx->pipe, entry.params[0]);
124 CircleBufferWrite8(&gx->pipe, entry.params[1]);
125 CircleBufferWrite8(&gx->pipe, entry.params[2]);
126 CircleBufferWrite8(&gx->pipe, entry.params[3]);
127 }
128 if (CircleBufferSize(&gx->fifo) >= sizeof(struct DSGXEntry)) {
129 struct DSGXEntry entry = { 0 };
130 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.command);
131 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[0]);
132 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[1]);
133 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[2]);
134 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[3]);
135 CircleBufferWrite8(&gx->pipe, entry.command);
136 CircleBufferWrite8(&gx->pipe, entry.params[0]);
137 CircleBufferWrite8(&gx->pipe, entry.params[1]);
138 CircleBufferWrite8(&gx->pipe, entry.params[2]);
139 CircleBufferWrite8(&gx->pipe, entry.params[3]);
140 }
141}
142
143static void _updateClipMatrix(struct DSGX* gx) {
144 DSGXMtxMultiply(&gx->clipMatrix, &gx->posMatrix, &gx->projMatrix);
145 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_00 >> 1] = gx->clipMatrix.m[0];
146 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_01 >> 1] = gx->clipMatrix.m[0] >> 16;
147 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_02 >> 1] = gx->clipMatrix.m[1];
148 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_03 >> 1] = gx->clipMatrix.m[1] >> 16;
149 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_04 >> 1] = gx->clipMatrix.m[2];
150 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_05 >> 1] = gx->clipMatrix.m[2] >> 16;
151 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_06 >> 1] = gx->clipMatrix.m[3];
152 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_07 >> 1] = gx->clipMatrix.m[3] >> 16;
153 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_08 >> 1] = gx->clipMatrix.m[4];
154 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_09 >> 1] = gx->clipMatrix.m[4] >> 16;
155 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_0A >> 1] = gx->clipMatrix.m[5];
156 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_0B >> 1] = gx->clipMatrix.m[5] >> 16;
157 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_0C >> 1] = gx->clipMatrix.m[6];
158 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_0D >> 1] = gx->clipMatrix.m[6] >> 16;
159 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_0E >> 1] = gx->clipMatrix.m[7];
160 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_0F >> 1] = gx->clipMatrix.m[7] >> 16;
161 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_10 >> 1] = gx->clipMatrix.m[8];
162 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_11 >> 1] = gx->clipMatrix.m[8] >> 16;
163 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_12 >> 1] = gx->clipMatrix.m[9];
164 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_13 >> 1] = gx->clipMatrix.m[9] >> 16;
165 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_14 >> 1] = gx->clipMatrix.m[10];
166 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_15 >> 1] = gx->clipMatrix.m[10] >> 16;
167 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_16 >> 1] = gx->clipMatrix.m[11];
168 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_17 >> 1] = gx->clipMatrix.m[11] >> 16;
169 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_18 >> 1] = gx->clipMatrix.m[12];
170 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_19 >> 1] = gx->clipMatrix.m[12] >> 16;
171 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_1A >> 1] = gx->clipMatrix.m[13];
172 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_1B >> 1] = gx->clipMatrix.m[13] >> 16;
173 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_1C >> 1] = gx->clipMatrix.m[14];
174 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_1D >> 1] = gx->clipMatrix.m[14] >> 16;
175 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_1E >> 1] = gx->clipMatrix.m[15];
176 gx->p->memory.io9[DS9_REG_CLIPMTX_RESULT_1F >> 1] = gx->clipMatrix.m[15] >> 16;
177}
178
179static int32_t _dotViewport(struct DSGXVertex* vertex, int32_t* col) {
180 int64_t a;
181 int64_t b;
182 int64_t sum;
183 a = col[0];
184 b = vertex->x;
185 sum = a * b;
186 a = col[4];
187 b = vertex->y;
188 sum += a * b;
189 a = col[8];
190 b = vertex->z;
191 sum += a * b;
192 a = col[12];
193 b = MTX_ONE;
194 sum += a * b;
195 return sum >> 8LL;
196}
197
198static int16_t _dotTexture(struct DSGXVertex* vertex, int mode, int32_t* col) {
199 int64_t a;
200 int64_t b;
201 int64_t sum;
202 switch (mode) {
203 case 1:
204 a = col[0];
205 b = vertex->s;
206 sum = a * b;
207 a = col[4];
208 b = vertex->t;
209 sum += a * b;
210 a = col[8];
211 b = MTX_ONE >> 4;
212 sum += a * b;
213 a = col[12];
214 b = MTX_ONE >> 4;
215 sum += a * b;
216 break;
217 case 2:
218 return 0;
219 case 3:
220 a = col[0];
221 b = vertex->vx;
222 sum = a * b;
223 a = col[4];
224 b = vertex->vy;
225 sum += a * b;
226 a = col[8];
227 b = vertex->vz;
228 sum += a * b;
229 a = col[12];
230 b = MTX_ONE;
231 sum += a * b;
232 }
233 return sum >> 12;
234}
235
236static void _emitVertex(struct DSGX* gx, uint16_t x, uint16_t y, uint16_t z) {
237 if (gx->vertexMode < 0 || gx->vertexIndex == DS_GX_VERTEX_BUFFER_SIZE || gx->polygonIndex == DS_GX_POLYGON_BUFFER_SIZE) {
238 return;
239 }
240 gx->currentVertex.x = x;
241 gx->currentVertex.y = y;
242 gx->currentVertex.z = z;
243 gx->currentVertex.vx = _dotViewport(&gx->currentVertex, &gx->clipMatrix.m[0]);
244 gx->currentVertex.vy = _dotViewport(&gx->currentVertex, &gx->clipMatrix.m[1]);
245 gx->currentVertex.vz = _dotViewport(&gx->currentVertex, &gx->clipMatrix.m[2]);
246 gx->currentVertex.vw = _dotViewport(&gx->currentVertex, &gx->clipMatrix.m[3]);
247
248 // TODO: What to do if w is 0?
249
250 gx->currentVertex.vx = (gx->currentVertex.vx + gx->currentVertex.vw) * (int64_t) (gx->viewportWidth << 12) / (gx->currentVertex.vw * 2) + (gx->viewportX1 << 12);
251 gx->currentVertex.vy = (gx->currentVertex.vy + gx->currentVertex.vw) * (int64_t) (gx->viewportHeight << 12) / (gx->currentVertex.vw * 2) + (gx->viewportY1 << 12);
252 //gx->currentVertex.vw = 0x40000000 / gx->currentVertex.vw;
253
254 if (DSGXTexParamsGetCoordTfMode(gx->currentPoly.texParams) > 0) {
255 int32_t m12 = gx->texMatrix.m[12];
256 int32_t m13 = gx->texMatrix.m[13];
257 if (DSGXTexParamsGetCoordTfMode(gx->currentPoly.texParams) > 1) {
258 gx->texMatrix.m[12] = gx->currentVertex.vs;
259 gx->texMatrix.m[13] = gx->currentVertex.vt;
260 }
261 gx->currentVertex.vs = _dotTexture(&gx->currentVertex, DSGXTexParamsGetCoordTfMode(gx->currentPoly.texParams), &gx->texMatrix.m[0]);
262 gx->currentVertex.vt = _dotTexture(&gx->currentVertex, DSGXTexParamsGetCoordTfMode(gx->currentPoly.texParams), &gx->texMatrix.m[1]);
263 gx->texMatrix.m[12] = m12;
264 gx->texMatrix.m[13] = m13;
265 } else {
266 gx->currentVertex.vs = gx->currentVertex.s;
267 gx->currentVertex.vt = gx->currentVertex.t;
268 }
269
270 struct DSGXVertex* vbuf = gx->vertexBuffer[gx->bufferIndex];
271 vbuf[gx->vertexIndex] = gx->currentVertex;
272
273 gx->currentPoly.vertIds[gx->currentPoly.verts] = gx->vertexIndex;
274
275 ++gx->vertexIndex;
276 ++gx->currentPoly.verts;
277 int totalVertices;
278 switch (gx->vertexMode) {
279 case 0:
280 case 2:
281 totalVertices = 3;
282 break;
283 case 1:
284 case 3:
285 totalVertices = 4;
286 break;
287 }
288 if (gx->currentPoly.verts == totalVertices) {
289 struct DSGXPolygon* pbuf = gx->polygonBuffer[gx->bufferIndex];
290 pbuf[gx->polygonIndex] = gx->currentPoly;
291
292 switch (gx->vertexMode) {
293 case 0:
294 case 1:
295 gx->currentPoly.verts = 0;
296 break;
297 case 2:
298 gx->currentPoly.vertIds[0] = gx->currentPoly.vertIds[1];
299 gx->currentPoly.vertIds[1] = gx->currentPoly.vertIds[2];
300 gx->currentPoly.verts = 2;
301 break;
302 case 3:
303 gx->currentPoly.vertIds[0] = gx->currentPoly.vertIds[2];
304 gx->currentPoly.vertIds[1] = gx->currentPoly.vertIds[3];
305 // Ensure quads don't cross over
306 pbuf[gx->polygonIndex].vertIds[2] = gx->currentPoly.vertIds[3];
307 pbuf[gx->polygonIndex].vertIds[3] = gx->currentPoly.vertIds[2];
308 gx->currentPoly.verts = 2;
309 break;
310 }
311 ++gx->polygonIndex;
312 }
313}
314
315static void _flushOutstanding(struct DSGX* gx) {
316 if (CircleBufferSize(&gx->fifo) == (DS_GX_FIFO_SIZE * sizeof(struct DSGXEntry))) {
317 return;
318 }
319 if (gx->p->cpuBlocked & DS_CPU_BLOCK_GX) {
320 gx->p->cpuBlocked &= ~DS_CPU_BLOCK_GX;
321 DSGXWriteFIFO(gx, gx->outstandingEntry);
322 gx->outstandingEntry.command = 0;
323 }
324 while (gx->outstandingCommand[0] && !gx->outstandingParams[0]) {
325 if (CircleBufferSize(&gx->fifo) == (DS_GX_FIFO_SIZE * sizeof(struct DSGXEntry))) {
326 return;
327 }
328 DSGXWriteFIFO(gx, (struct DSGXEntry) { 0 });
329 }
330}
331
332static bool _boxTestVertex(struct DSGX* gx, struct DSGXVertex* vertex) {
333 int32_t vx = _dotViewport(vertex, &gx->clipMatrix.m[0]);
334 int32_t vy = _dotViewport(vertex, &gx->clipMatrix.m[1]);
335 int32_t vz = _dotViewport(vertex, &gx->clipMatrix.m[2]);
336 int32_t vw = _dotViewport(vertex, &gx->clipMatrix.m[3]);
337
338 vx = (vx + vw) * (int64_t) (gx->viewportWidth << 12) / (vw * 2) + (gx->viewportX1 << 12);
339 vy = (vy + vw) * (int64_t) (gx->viewportHeight << 12) / (vw * 2) + (gx->viewportY1 << 12);
340 vx >>= 12;
341 vy >>= 12;
342
343 if (vx < gx->viewportX1) {
344 return false;
345 }
346 if (vx >= gx->viewportX2) {
347 return false;
348 }
349 if (vy < gx->viewportY1) {
350 return false;
351 }
352 if (vy >= gx->viewportY2) {
353 return false;
354 }
355 // TODO: depth clipping
356 return true;
357}
358
359static bool _boxTest(struct DSGX* gx) {
360 int16_t x = gx->activeEntries[0].params[0];
361 x |= gx->activeEntries[0].params[1] << 8;
362 int16_t y = gx->activeEntries[0].params[2];
363 y |= gx->activeEntries[0].params[3] << 8;
364 int16_t z = gx->activeEntries[1].params[0];
365 z |= gx->activeEntries[1].params[1] << 8;
366 int16_t w = gx->activeEntries[1].params[2];
367 w |= gx->activeEntries[1].params[3] << 8;
368 int16_t h = gx->activeEntries[2].params[0];
369 h |= gx->activeEntries[2].params[1] << 8;
370 int16_t d = gx->activeEntries[2].params[2];
371 d |= gx->activeEntries[2].params[3] << 8;
372
373 struct DSGXVertex vertex = {
374 .x = x,
375 .y = y,
376 .z = z
377 };
378 if (_boxTestVertex(gx, &vertex)) {
379 return true;
380 }
381
382 vertex.x += w;
383 if (_boxTestVertex(gx, &vertex)) {
384 return true;
385 }
386
387 vertex.x = x;
388 vertex.y += h;
389 if (_boxTestVertex(gx, &vertex)) {
390 return true;
391 }
392
393 vertex.x += w;
394 if (_boxTestVertex(gx, &vertex)) {
395 return true;
396 }
397
398 vertex.x = x;
399 vertex.y = y;
400 vertex.z += d;
401 if (_boxTestVertex(gx, &vertex)) {
402 return true;
403 }
404
405 vertex.x += w;
406 if (_boxTestVertex(gx, &vertex)) {
407 return true;
408 }
409
410 vertex.x = x;
411 vertex.y += h;
412 if (_boxTestVertex(gx, &vertex)) {
413 return true;
414 }
415
416 vertex.x += w;
417 if (_boxTestVertex(gx, &vertex)) {
418 return true;
419 }
420
421 return false;
422}
423
424static void _fifoRun(struct mTiming* timing, void* context, uint32_t cyclesLate) {
425 struct DSGX* gx = context;
426 uint32_t cycles;
427 bool first = true;
428 while (!gx->swapBuffers) {
429 if (CircleBufferSize(&gx->pipe) <= 2 * sizeof(struct DSGXEntry)) {
430 _pullPipe(gx);
431 }
432
433 if (!CircleBufferSize(&gx->pipe)) {
434 cycles = 0;
435 break;
436 }
437
438 DSRegGXSTAT gxstat = gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1];
439 int projMatrixPointer = DSRegGXSTATGetProjMatrixStackLevel(gxstat);
440
441 struct DSGXEntry entry = { 0 };
442 CircleBufferDump(&gx->pipe, (int8_t*) &entry.command, 1);
443 cycles = _gxCommandCycleBase[entry.command];
444
445 if (first) {
446 first = false;
447 } else if (!gx->activeParams && cycles > cyclesLate) {
448 break;
449 }
450 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.command);
451 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[0]);
452 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[1]);
453 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[2]);
454 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[3]);
455
456 if (gx->activeParams) {
457 int index = _gxCommandParams[entry.command] - gx->activeParams;
458 gx->activeEntries[index] = entry;
459 --gx->activeParams;
460 } else {
461 gx->activeParams = _gxCommandParams[entry.command];
462 if (gx->activeParams) {
463 --gx->activeParams;
464 }
465 if (gx->activeParams) {
466 gx->activeEntries[0] = entry;
467 }
468 }
469
470 if (gx->activeParams) {
471 continue;
472 }
473
474 switch (entry.command) {
475 case DS_GX_CMD_MTX_MODE:
476 if (entry.params[0] < 4) {
477 gx->mtxMode = entry.params[0];
478 } else {
479 mLOG(DS_GX, GAME_ERROR, "Invalid GX MTX_MODE %02X", entry.params[0]);
480 }
481 break;
482 case DS_GX_CMD_MTX_PUSH:
483 switch (gx->mtxMode) {
484 case 0:
485 memcpy(&gx->projMatrixStack, &gx->projMatrix, sizeof(gx->projMatrix));
486 ++projMatrixPointer;
487 break;
488 case 2:
489 memcpy(&gx->vecMatrixStack[gx->pvMatrixPointer & 0x1F], &gx->vecMatrix, sizeof(gx->vecMatrix));
490 // Fall through
491 case 1:
492 memcpy(&gx->posMatrixStack[gx->pvMatrixPointer & 0x1F], &gx->posMatrix, sizeof(gx->posMatrix));
493 ++gx->pvMatrixPointer;
494 break;
495 case 3:
496 mLOG(DS_GX, STUB, "Unimplemented GX MTX_PUSH mode");
497 break;
498 }
499 break;
500 case DS_GX_CMD_MTX_POP: {
501 int8_t offset = entry.params[0];
502 offset <<= 2;
503 offset >>= 2;
504 switch (gx->mtxMode) {
505 case 0:
506 projMatrixPointer -= offset;
507 memcpy(&gx->projMatrix, &gx->projMatrixStack, sizeof(gx->projMatrix));
508 break;
509 case 1:
510 gx->pvMatrixPointer -= offset;
511 memcpy(&gx->posMatrix, &gx->posMatrixStack[gx->pvMatrixPointer & 0x1F], sizeof(gx->posMatrix));
512 break;
513 case 2:
514 gx->pvMatrixPointer -= offset;
515 memcpy(&gx->vecMatrix, &gx->vecMatrixStack[gx->pvMatrixPointer & 0x1F], sizeof(gx->vecMatrix));
516 memcpy(&gx->posMatrix, &gx->posMatrixStack[gx->pvMatrixPointer & 0x1F], sizeof(gx->posMatrix));
517 break;
518 case 3:
519 mLOG(DS_GX, STUB, "Unimplemented GX MTX_POP mode");
520 break;
521 }
522 _updateClipMatrix(gx);
523 break;
524 }
525 case DS_GX_CMD_MTX_STORE: {
526 int8_t offset = entry.params[0] & 0x1F;
527 // TODO: overflow
528 switch (gx->mtxMode) {
529 case 0:
530 memcpy(&gx->projMatrixStack, &gx->projMatrix, sizeof(gx->projMatrixStack));
531 break;
532 case 2:
533 memcpy(&gx->vecMatrixStack[offset], &gx->vecMatrix, sizeof(gx->vecMatrix));
534 // Fall through
535 case 1:
536 memcpy(&gx->posMatrixStack[offset], &gx->posMatrix, sizeof(gx->posMatrix));
537 break;
538 case 3:
539 mLOG(DS_GX, STUB, "Unimplemented GX MTX_STORE mode");
540 break;
541 }
542 break;
543 }
544 case DS_GX_CMD_MTX_RESTORE: {
545 int8_t offset = entry.params[0] & 0x1F;
546 // TODO: overflow
547 switch (gx->mtxMode) {
548 case 0:
549 memcpy(&gx->projMatrix, &gx->projMatrixStack, sizeof(gx->projMatrix));
550 break;
551 case 2:
552 memcpy(&gx->vecMatrix, &gx->vecMatrixStack[offset], sizeof(gx->vecMatrix));
553 // Fall through
554 case 1:
555 memcpy(&gx->posMatrix, &gx->posMatrixStack[offset], sizeof(gx->posMatrix));
556 break;
557 case 3:
558 mLOG(DS_GX, STUB, "Unimplemented GX MTX_RESTORE mode");
559 break;
560 }
561 _updateClipMatrix(gx);
562 break;
563 }
564 case DS_GX_CMD_MTX_IDENTITY:
565 switch (gx->mtxMode) {
566 case 0:
567 DSGXMtxIdentity(&gx->projMatrix);
568 break;
569 case 2:
570 DSGXMtxIdentity(&gx->vecMatrix);
571 // Fall through
572 case 1:
573 DSGXMtxIdentity(&gx->posMatrix);
574 break;
575 case 3:
576 DSGXMtxIdentity(&gx->texMatrix);
577 break;
578 }
579 _updateClipMatrix(gx);
580 break;
581 case DS_GX_CMD_MTX_LOAD_4x4: {
582 struct DSGXMatrix m;
583 int i;
584 for (i = 0; i < 16; ++i) {
585 m.m[i] = gx->activeEntries[i].params[0];
586 m.m[i] |= gx->activeEntries[i].params[1] << 8;
587 m.m[i] |= gx->activeEntries[i].params[2] << 16;
588 m.m[i] |= gx->activeEntries[i].params[3] << 24;
589 }
590 switch (gx->mtxMode) {
591 case 0:
592 memcpy(&gx->projMatrix, &m, sizeof(gx->projMatrix));
593 break;
594 case 2:
595 memcpy(&gx->vecMatrix, &m, sizeof(gx->vecMatrix));
596 // Fall through
597 case 1:
598 memcpy(&gx->posMatrix, &m, sizeof(gx->posMatrix));
599 break;
600 case 3:
601 memcpy(&gx->texMatrix, &m, sizeof(gx->texMatrix));
602 break;
603 }
604 _updateClipMatrix(gx);
605 break;
606 }
607 case DS_GX_CMD_MTX_LOAD_4x3: {
608 struct DSGXMatrix m;
609 int i, j;
610 for (j = 0; j < 4; ++j) {
611 for (i = 0; i < 3; ++i) {
612 m.m[i + j * 4] = gx->activeEntries[i + j * 3].params[0];
613 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[1] << 8;
614 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[2] << 16;
615 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[3] << 24;
616 }
617 m.m[j * 4 + 3] = 0;
618 }
619 m.m[15] = MTX_ONE;
620 switch (gx->mtxMode) {
621 case 0:
622 memcpy(&gx->projMatrix, &m, sizeof(gx->projMatrix));
623 break;
624 case 2:
625 memcpy(&gx->vecMatrix, &m, sizeof(gx->vecMatrix));
626 // Fall through
627 case 1:
628 memcpy(&gx->posMatrix, &m, sizeof(gx->posMatrix));
629 break;
630 case 3:
631 memcpy(&gx->texMatrix, &m, sizeof(gx->texMatrix));
632 break;
633 }
634 _updateClipMatrix(gx);
635 break;
636 }
637 case DS_GX_CMD_MTX_MULT_4x4: {
638 struct DSGXMatrix m;
639 int i;
640 for (i = 0; i < 16; ++i) {
641 m.m[i] = gx->activeEntries[i].params[0];
642 m.m[i] |= gx->activeEntries[i].params[1] << 8;
643 m.m[i] |= gx->activeEntries[i].params[2] << 16;
644 m.m[i] |= gx->activeEntries[i].params[3] << 24;
645 }
646 switch (gx->mtxMode) {
647 case 0:
648 DSGXMtxMultiply(&gx->projMatrix, &m, &gx->projMatrix);
649 break;
650 case 2:
651 DSGXMtxMultiply(&gx->vecMatrix, &m, &gx->vecMatrix);
652 // Fall through
653 case 1:
654 DSGXMtxMultiply(&gx->posMatrix, &m, &gx->posMatrix);
655 break;
656 case 3:
657 DSGXMtxMultiply(&gx->texMatrix, &m, &gx->texMatrix);
658 break;
659 }
660 _updateClipMatrix(gx);
661 break;
662 }
663 case DS_GX_CMD_MTX_MULT_4x3: {
664 struct DSGXMatrix m;
665 int i, j;
666 for (j = 0; j < 4; ++j) {
667 for (i = 0; i < 3; ++i) {
668 m.m[i + j * 4] = gx->activeEntries[i + j * 3].params[0];
669 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[1] << 8;
670 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[2] << 16;
671 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[3] << 24;
672 }
673 m.m[j * 4 + 3] = 0;
674 }
675 m.m[15] = MTX_ONE;
676 switch (gx->mtxMode) {
677 case 0:
678 DSGXMtxMultiply(&gx->projMatrix, &m, &gx->projMatrix);
679 break;
680 case 2:
681 DSGXMtxMultiply(&gx->vecMatrix, &m, &gx->vecMatrix);
682 // Fall through
683 case 1:
684 DSGXMtxMultiply(&gx->posMatrix, &m, &gx->posMatrix);
685 break;
686 case 3:
687 DSGXMtxMultiply(&gx->texMatrix, &m, &gx->texMatrix);
688 break;
689 }
690 _updateClipMatrix(gx);
691 break;
692 }
693 case DS_GX_CMD_MTX_MULT_3x3: {
694 struct DSGXMatrix m;
695 int i, j;
696 for (j = 0; j < 3; ++j) {
697 for (i = 0; i < 3; ++i) {
698 m.m[i + j * 4] = gx->activeEntries[i + j * 3].params[0];
699 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[1] << 8;
700 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[2] << 16;
701 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[3] << 24;
702 }
703 m.m[j * 4 + 3] = 0;
704 }
705 m.m[12] = 0;
706 m.m[13] = 0;
707 m.m[14] = 0;
708 m.m[15] = MTX_ONE;
709 switch (gx->mtxMode) {
710 case 0:
711 DSGXMtxMultiply(&gx->projMatrix, &m, &gx->projMatrix);
712 break;
713 case 2:
714 DSGXMtxMultiply(&gx->vecMatrix, &m, &gx->vecMatrix);
715 // Fall through
716 case 1:
717 DSGXMtxMultiply(&gx->posMatrix, &m, &gx->posMatrix);
718 break;
719 case 3:
720 DSGXMtxMultiply(&gx->texMatrix, &m, &gx->texMatrix);
721 break;
722 }
723 _updateClipMatrix(gx);
724 break;
725 }
726 case DS_GX_CMD_MTX_TRANS: {
727 int32_t m[3];
728 m[0] = gx->activeEntries[0].params[0];
729 m[0] |= gx->activeEntries[0].params[1] << 8;
730 m[0] |= gx->activeEntries[0].params[2] << 16;
731 m[0] |= gx->activeEntries[0].params[3] << 24;
732 m[1] = gx->activeEntries[1].params[0];
733 m[1] |= gx->activeEntries[1].params[1] << 8;
734 m[1] |= gx->activeEntries[1].params[2] << 16;
735 m[1] |= gx->activeEntries[1].params[3] << 24;
736 m[2] = gx->activeEntries[2].params[0];
737 m[2] |= gx->activeEntries[2].params[1] << 8;
738 m[2] |= gx->activeEntries[2].params[2] << 16;
739 m[2] |= gx->activeEntries[2].params[3] << 24;
740 switch (gx->mtxMode) {
741 case 0:
742 DSGXMtxTranslate(&gx->projMatrix, m);
743 break;
744 case 2:
745 DSGXMtxTranslate(&gx->vecMatrix, m);
746 // Fall through
747 case 1:
748 DSGXMtxTranslate(&gx->posMatrix, m);
749 break;
750 case 3:
751 DSGXMtxTranslate(&gx->texMatrix, m);
752 break;
753 }
754 _updateClipMatrix(gx);
755 break;
756 }
757 case DS_GX_CMD_MTX_SCALE: {
758 int32_t m[3];
759 m[0] = gx->activeEntries[0].params[0];
760 m[0] |= gx->activeEntries[0].params[1] << 8;
761 m[0] |= gx->activeEntries[0].params[2] << 16;
762 m[0] |= gx->activeEntries[0].params[3] << 24;
763 m[1] = gx->activeEntries[1].params[0];
764 m[1] |= gx->activeEntries[1].params[1] << 8;
765 m[1] |= gx->activeEntries[1].params[2] << 16;
766 m[1] |= gx->activeEntries[1].params[3] << 24;
767 m[2] = gx->activeEntries[2].params[0];
768 m[2] |= gx->activeEntries[2].params[1] << 8;
769 m[2] |= gx->activeEntries[2].params[2] << 16;
770 m[2] |= gx->activeEntries[2].params[3] << 24;
771 switch (gx->mtxMode) {
772 case 0:
773 DSGXMtxScale(&gx->projMatrix, m);
774 break;
775 case 2:
776 DSGXMtxScale(&gx->vecMatrix, m);
777 // Fall through
778 case 1:
779 DSGXMtxScale(&gx->posMatrix, m);
780 break;
781 case 3:
782 DSGXMtxScale(&gx->texMatrix, m);
783 break;
784 }
785 _updateClipMatrix(gx);
786 break;
787 }
788 case DS_GX_CMD_COLOR:
789 gx->currentVertex.color = entry.params[0];
790 gx->currentVertex.color |= entry.params[1] << 8;
791 break;
792 case DS_GX_CMD_TEXCOORD:
793 gx->currentVertex.s = entry.params[0];
794 gx->currentVertex.s |= entry.params[1] << 8;
795 gx->currentVertex.t = entry.params[2];
796 gx->currentVertex.t |= entry.params[3] << 8;
797 break;
798 case DS_GX_CMD_VTX_16: {
799 int16_t x = gx->activeEntries[0].params[0];
800 x |= gx->activeEntries[0].params[1] << 8;
801 int16_t y = gx->activeEntries[0].params[2];
802 y |= gx->activeEntries[0].params[3] << 8;
803 int16_t z = gx->activeEntries[1].params[0];
804 z |= gx->activeEntries[1].params[1] << 8;
805 _emitVertex(gx, x, y, z);
806 break;
807 }
808 case DS_GX_CMD_VTX_10: {
809 int32_t xyz = entry.params[0];
810 xyz |= entry.params[1] << 8;
811 xyz |= entry.params[2] << 16;
812 xyz |= entry.params[3] << 24;
813 int16_t x = (xyz << 6) & 0xFFC0;
814 int16_t y = (xyz >> 4) & 0xFFC0;
815 int16_t z = (xyz >> 14) & 0xFFC0;
816 _emitVertex(gx, x, y, z);
817 break;
818 }
819 case DS_GX_CMD_VTX_XY: {
820 int16_t x = entry.params[0];
821 x |= entry.params[1] << 8;
822 int16_t y = entry.params[2];
823 y |= entry.params[3] << 8;
824 _emitVertex(gx, x, y, gx->currentVertex.z);
825 break;
826 }
827 case DS_GX_CMD_VTX_XZ: {
828 int16_t x = entry.params[0];
829 x |= entry.params[1] << 8;
830 int16_t z = entry.params[2];
831 z |= entry.params[3] << 8;
832 _emitVertex(gx, x, gx->currentVertex.y, z);
833 break;
834 }
835 case DS_GX_CMD_VTX_YZ: {
836 int16_t y = entry.params[0];
837 y |= entry.params[1] << 8;
838 int16_t z = entry.params[2];
839 z |= entry.params[3] << 8;
840 _emitVertex(gx, gx->currentVertex.x, y, z);
841 break;
842 }
843 case DS_GX_CMD_VTX_DIFF: {
844 int32_t xyz = entry.params[0];
845 xyz |= entry.params[1] << 8;
846 xyz |= entry.params[2] << 16;
847 xyz |= entry.params[3] << 24;
848 int16_t x = (xyz << 6) & 0xFFC0;
849 int16_t y = (xyz >> 4) & 0xFFC0;
850 int16_t z = (xyz >> 14) & 0xFFC0;
851 _emitVertex(gx, gx->currentVertex.x + (x >> 6), gx->currentVertex.y + (y >> 6), gx->currentVertex.z + (z >> 6));
852 }
853 case DS_GX_CMD_POLYGON_ATTR:
854 gx->nextPoly.polyParams = entry.params[0];
855 gx->nextPoly.polyParams |= entry.params[1] << 8;
856 gx->nextPoly.polyParams |= entry.params[2] << 16;
857 gx->nextPoly.polyParams |= entry.params[3] << 24;
858 break;
859 case DS_GX_CMD_TEXIMAGE_PARAM:
860 gx->nextPoly.texParams = entry.params[0];
861 gx->nextPoly.texParams |= entry.params[1] << 8;
862 gx->nextPoly.texParams |= entry.params[2] << 16;
863 gx->nextPoly.texParams |= entry.params[3] << 24;
864 break;
865 case DS_GX_CMD_PLTT_BASE:
866 gx->nextPoly.palBase = entry.params[0];
867 gx->nextPoly.palBase |= entry.params[1] << 8;
868 gx->nextPoly.palBase |= entry.params[2] << 16;
869 gx->nextPoly.palBase |= entry.params[3] << 24;
870 break;
871 case DS_GX_CMD_BEGIN_VTXS:
872 gx->vertexMode = entry.params[0] & 3;
873 gx->currentPoly = gx->nextPoly;
874 break;
875 case DS_GX_CMD_END_VTXS:
876 gx->vertexMode = -1;
877 break;
878 case DS_GX_CMD_SWAP_BUFFERS:
879 gx->swapBuffers = true;
880 gx->wSort = entry.params[0] & 2;
881 break;
882 case DS_GX_CMD_VIEWPORT:
883 gx->viewportX1 = (uint8_t) entry.params[0];
884 gx->viewportY1 = (uint8_t) entry.params[1];
885 gx->viewportX2 = (uint8_t) entry.params[2];
886 gx->viewportY2 = (uint8_t) entry.params[3];
887 gx->viewportWidth = gx->viewportX2 - gx->viewportX1;
888 gx->viewportHeight = gx->viewportY2 - gx->viewportY1;
889 break;
890 case DS_GX_CMD_BOX_TEST:
891 gxstat = DSRegGXSTATClearTestBusy(gxstat);
892 gxstat = DSRegGXSTATTestFillBoxTestResult(gxstat, _boxTest(gx));
893 break;
894 default:
895 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]);
896 break;
897 }
898
899 gxstat = DSRegGXSTATSetPVMatrixStackLevel(gxstat, gx->pvMatrixPointer);
900 gxstat = DSRegGXSTATSetProjMatrixStackLevel(gxstat, projMatrixPointer);
901 gxstat = DSRegGXSTATTestFillMatrixStackError(gxstat, projMatrixPointer || gx->pvMatrixPointer >= 0x1F);
902 gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1] = gxstat;
903
904 if (cyclesLate >= cycles) {
905 cyclesLate -= cycles;
906 } else {
907 break;
908 }
909 }
910 if (cycles && !gx->swapBuffers) {
911 mTimingSchedule(timing, &gx->fifoEvent, cycles - cyclesLate);
912 }
913 _flushOutstanding(gx);
914 DSGXUpdateGXSTAT(gx);
915}
916
917void DSGXInit(struct DSGX* gx) {
918 gx->renderer = &dummyRenderer;
919 CircleBufferInit(&gx->fifo, sizeof(struct DSGXEntry) * DS_GX_FIFO_SIZE);
920 CircleBufferInit(&gx->pipe, sizeof(struct DSGXEntry) * DS_GX_PIPE_SIZE);
921 gx->vertexBuffer[0] = malloc(sizeof(struct DSGXVertex) * DS_GX_VERTEX_BUFFER_SIZE);
922 gx->vertexBuffer[1] = malloc(sizeof(struct DSGXVertex) * DS_GX_VERTEX_BUFFER_SIZE);
923 gx->polygonBuffer[0] = malloc(sizeof(struct DSGXPolygon) * DS_GX_POLYGON_BUFFER_SIZE);
924 gx->polygonBuffer[1] = malloc(sizeof(struct DSGXPolygon) * DS_GX_POLYGON_BUFFER_SIZE);
925 gx->fifoEvent.name = "DS GX FIFO";
926 gx->fifoEvent.priority = 0xC;
927 gx->fifoEvent.context = gx;
928 gx->fifoEvent.callback = _fifoRun;
929}
930
931void DSGXDeinit(struct DSGX* gx) {
932 DSGXAssociateRenderer(gx, &dummyRenderer);
933 CircleBufferDeinit(&gx->fifo);
934 CircleBufferDeinit(&gx->pipe);
935 free(gx->vertexBuffer[0]);
936 free(gx->vertexBuffer[1]);
937 free(gx->polygonBuffer[0]);
938 free(gx->polygonBuffer[1]);
939}
940
941void DSGXReset(struct DSGX* gx) {
942 CircleBufferClear(&gx->fifo);
943 CircleBufferClear(&gx->pipe);
944 DSGXMtxIdentity(&gx->projMatrix);
945 DSGXMtxIdentity(&gx->texMatrix);
946 DSGXMtxIdentity(&gx->posMatrix);
947 DSGXMtxIdentity(&gx->vecMatrix);
948
949 DSGXMtxIdentity(&gx->clipMatrix);
950 DSGXMtxIdentity(&gx->projMatrixStack);
951 DSGXMtxIdentity(&gx->texMatrixStack);
952 int i;
953 for (i = 0; i < 32; ++i) {
954 DSGXMtxIdentity(&gx->posMatrixStack[i]);
955 DSGXMtxIdentity(&gx->vecMatrixStack[i]);
956 }
957 gx->swapBuffers = false;
958 gx->bufferIndex = 0;
959 gx->vertexIndex = 0;
960 gx->polygonIndex = 0;
961 gx->mtxMode = 0;
962 gx->pvMatrixPointer = 0;
963 gx->vertexMode = -1;
964
965 gx->viewportX1 = 0;
966 gx->viewportY1 = 0;
967 gx->viewportX2 = DS_VIDEO_HORIZONTAL_PIXELS - 1;
968 gx->viewportY2 = DS_VIDEO_VERTICAL_PIXELS - 1;
969 gx->viewportWidth = gx->viewportX2 - gx->viewportX1;
970 gx->viewportHeight = gx->viewportY2 - gx->viewportY1;
971
972 memset(gx->outstandingParams, 0, sizeof(gx->outstandingParams));
973 memset(gx->outstandingCommand, 0, sizeof(gx->outstandingCommand));
974 memset(&gx->outstandingEntry, 0, sizeof(gx->outstandingEntry));
975 gx->activeParams = 0;
976 memset(&gx->currentVertex, 0, sizeof(gx->currentVertex));
977 memset(&gx->nextPoly, 0, sizeof(gx-> nextPoly));
978}
979
980void DSGXAssociateRenderer(struct DSGX* gx, struct DSGXRenderer* renderer) {
981 gx->renderer->deinit(gx->renderer);
982 gx->renderer = renderer;
983 memcpy(gx->renderer->tex, gx->tex, sizeof(gx->renderer->tex));
984 memcpy(gx->renderer->texPal, gx->texPal, sizeof(gx->renderer->texPal));
985 gx->renderer->init(gx->renderer);
986}
987
988void DSGXUpdateGXSTAT(struct DSGX* gx) {
989 uint32_t value = gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] << 16;
990 value = DSRegGXSTATIsDoIRQ(value);
991
992 size_t entries = CircleBufferSize(&gx->fifo) / sizeof(struct DSGXEntry);
993 // XXX
994 if (gx->swapBuffers) {
995 entries++;
996 }
997 value = DSRegGXSTATSetFIFOEntries(value, entries);
998 value = DSRegGXSTATSetFIFOLtHalf(value, entries < (DS_GX_FIFO_SIZE / 2));
999 value = DSRegGXSTATSetFIFOEmpty(value, entries == 0);
1000
1001 if ((DSRegGXSTATGetDoIRQ(value) == 1 && entries < (DS_GX_FIFO_SIZE / 2)) ||
1002 (DSRegGXSTATGetDoIRQ(value) == 2 && entries == 0)) {
1003 DSRaiseIRQ(gx->p->ds9.cpu, gx->p->ds9.memory.io, DS_IRQ_GEOM_FIFO);
1004 }
1005
1006 value = DSRegGXSTATSetBusy(value, mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent) || gx->swapBuffers);
1007
1008 gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] = value >> 16;
1009}
1010
1011static void DSGXUnpackCommand(struct DSGX* gx, uint32_t command) {
1012 gx->outstandingCommand[0] = command;
1013 gx->outstandingCommand[1] = command >> 8;
1014 gx->outstandingCommand[2] = command >> 16;
1015 gx->outstandingCommand[3] = command >> 24;
1016 if (gx->outstandingCommand[0] >= DS_GX_CMD_MAX) {
1017 gx->outstandingCommand[0] = 0;
1018 }
1019 if (gx->outstandingCommand[1] >= DS_GX_CMD_MAX) {
1020 gx->outstandingCommand[1] = 0;
1021 }
1022 if (gx->outstandingCommand[2] >= DS_GX_CMD_MAX) {
1023 gx->outstandingCommand[2] = 0;
1024 }
1025 if (gx->outstandingCommand[3] >= DS_GX_CMD_MAX) {
1026 gx->outstandingCommand[3] = 0;
1027 }
1028 gx->outstandingParams[0] = _gxCommandParams[gx->outstandingCommand[0]];
1029 gx->outstandingParams[1] = _gxCommandParams[gx->outstandingCommand[1]];
1030 gx->outstandingParams[2] = _gxCommandParams[gx->outstandingCommand[2]];
1031 gx->outstandingParams[3] = _gxCommandParams[gx->outstandingCommand[3]];
1032 _flushOutstanding(gx);
1033 DSGXUpdateGXSTAT(gx);
1034}
1035
1036static void DSGXWriteFIFO(struct DSGX* gx, struct DSGXEntry entry) {
1037 if (CircleBufferSize(&gx->fifo) == (DS_GX_FIFO_SIZE * sizeof(entry))) {
1038 mLOG(DS_GX, INFO, "FIFO full");
1039 if (gx->p->cpuBlocked & DS_CPU_BLOCK_GX) {
1040 abort();
1041 }
1042 gx->p->cpuBlocked |= DS_CPU_BLOCK_GX;
1043 gx->outstandingEntry = entry;
1044 gx->p->ds9.cpu->nextEvent = 0;
1045 return;
1046 }
1047 if (gx->outstandingCommand[0]) {
1048 entry.command = gx->outstandingCommand[0];
1049 if (gx->outstandingParams[0]) {
1050 --gx->outstandingParams[0];
1051 }
1052 if (!gx->outstandingParams[0]) {
1053 // TODO: improve this
1054 memmove(&gx->outstandingParams[0], &gx->outstandingParams[1], sizeof(gx->outstandingParams[0]) * 3);
1055 memmove(&gx->outstandingCommand[0], &gx->outstandingCommand[1], sizeof(gx->outstandingCommand[0]) * 3);
1056 gx->outstandingParams[3] = 0;
1057 gx->outstandingCommand[3] = 0;
1058 }
1059 } else {
1060 gx->outstandingParams[0] = _gxCommandParams[entry.command];
1061 if (gx->outstandingParams[0]) {
1062 --gx->outstandingParams[0];
1063 }
1064 if (gx->outstandingParams[0]) {
1065 gx->outstandingCommand[0] = entry.command;
1066 }
1067 }
1068 uint32_t cycles = _gxCommandCycleBase[entry.command];
1069 if (!cycles) {
1070 return;
1071 }
1072 if (CircleBufferSize(&gx->fifo) == 0 && CircleBufferSize(&gx->pipe) < (DS_GX_PIPE_SIZE * sizeof(entry))) {
1073 CircleBufferWrite8(&gx->pipe, entry.command);
1074 CircleBufferWrite8(&gx->pipe, entry.params[0]);
1075 CircleBufferWrite8(&gx->pipe, entry.params[1]);
1076 CircleBufferWrite8(&gx->pipe, entry.params[2]);
1077 CircleBufferWrite8(&gx->pipe, entry.params[3]);
1078 } else if (CircleBufferSize(&gx->fifo) < (DS_GX_FIFO_SIZE * sizeof(entry))) {
1079 CircleBufferWrite8(&gx->fifo, entry.command);
1080 CircleBufferWrite8(&gx->fifo, entry.params[0]);
1081 CircleBufferWrite8(&gx->fifo, entry.params[1]);
1082 CircleBufferWrite8(&gx->fifo, entry.params[2]);
1083 CircleBufferWrite8(&gx->fifo, entry.params[3]);
1084 }
1085 if (entry.command == DS_GX_CMD_BOX_TEST) {
1086 DSRegGXSTAT gxstat = gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1];
1087 gxstat = DSRegGXSTATFillTestBusy(gxstat);
1088 gxstat = DSRegGXSTATClearBoxTestResult(gxstat);
1089 gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1] = gxstat;
1090 }
1091 if (!gx->swapBuffers && !mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent)) {
1092 mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, cycles);
1093 }
1094
1095 _flushOutstanding(gx);
1096}
1097
1098uint16_t DSGXWriteRegister(struct DSGX* gx, uint32_t address, uint16_t value) {
1099 uint16_t oldValue = gx->p->memory.io9[address >> 1];
1100 switch (address) {
1101 case DS9_REG_DISP3DCNT:
1102 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
1103 break;
1104 case DS9_REG_GXSTAT_LO:
1105 value = DSRegGXSTATIsMatrixStackError(value);
1106 if (value) {
1107 oldValue = DSRegGXSTATClearMatrixStackError(oldValue);
1108 oldValue = DSRegGXSTATClearProjMatrixStackLevel(oldValue);
1109 }
1110 value = oldValue;
1111 break;
1112 case DS9_REG_GXSTAT_HI:
1113 value = DSRegGXSTATIsDoIRQ(value << 16) >> 16;
1114 gx->p->memory.io9[address >> 1] = value;
1115 DSGXUpdateGXSTAT(gx);
1116 value = gx->p->memory.io9[address >> 1];
1117 break;
1118 default:
1119 if (address < DS9_REG_GXFIFO_00) {
1120 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
1121 } else if (address <= DS9_REG_GXFIFO_1F) {
1122 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
1123 } else if (address < DS9_REG_GXSTAT_LO) {
1124 struct DSGXEntry entry = {
1125 .command = (address & 0x1FC) >> 2,
1126 .params = {
1127 value,
1128 value >> 8,
1129 }
1130 };
1131 if (entry.command < DS_GX_CMD_MAX) {
1132 DSGXWriteFIFO(gx, entry);
1133 }
1134 } else {
1135 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
1136 }
1137 break;
1138 }
1139 return value;
1140}
1141
1142uint32_t DSGXWriteRegister32(struct DSGX* gx, uint32_t address, uint32_t value) {
1143 switch (address) {
1144 case DS9_REG_DISP3DCNT:
1145 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
1146 break;
1147 case DS9_REG_GXSTAT_LO:
1148 value = (value & 0xFFFF0000) | DSGXWriteRegister(gx, DS9_REG_GXSTAT_LO, value);
1149 value = (value & 0x0000FFFF) | (DSGXWriteRegister(gx, DS9_REG_GXSTAT_HI, value >> 16) << 16);
1150 break;
1151 default:
1152 if (address < DS9_REG_GXFIFO_00) {
1153 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
1154 } else if (address <= DS9_REG_GXFIFO_1F) {
1155 if (gx->outstandingParams[0]) {
1156 struct DSGXEntry entry = {
1157 .command = gx->outstandingCommand[0],
1158 .params = {
1159 value,
1160 value >> 8,
1161 value >> 16,
1162 value >> 24
1163 }
1164 };
1165 DSGXWriteFIFO(gx, entry);
1166 } else {
1167 DSGXUnpackCommand(gx, value);
1168 }
1169 } else if (address < DS9_REG_GXSTAT_LO) {
1170 struct DSGXEntry entry = {
1171 .command = (address & 0x1FC) >> 2,
1172 .params = {
1173 value,
1174 value >> 8,
1175 value >> 16,
1176 value >> 24
1177 }
1178 };
1179 DSGXWriteFIFO(gx, entry);
1180 } else {
1181 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
1182 }
1183 break;
1184 }
1185 return value;
1186}
1187
1188void DSGXFlush(struct DSGX* gx) {
1189 if (gx->swapBuffers) {
1190 gx->renderer->setRAM(gx->renderer, gx->vertexBuffer[gx->bufferIndex], gx->polygonBuffer[gx->bufferIndex], gx->polygonIndex, gx->wSort);
1191 gx->swapBuffers = false;
1192 gx->bufferIndex ^= 1;
1193 gx->vertexIndex = 0;
1194 gx->polygonIndex = 0;
1195 if (CircleBufferSize(&gx->fifo)) {
1196 mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, 0);
1197 }
1198 }
1199
1200 DSGXUpdateGXSTAT(gx);
1201}
1202
1203static void DSGXDummyRendererInit(struct DSGXRenderer* renderer) {
1204 UNUSED(renderer);
1205 // Nothing to do
1206}
1207
1208static void DSGXDummyRendererReset(struct DSGXRenderer* renderer) {
1209 UNUSED(renderer);
1210 // Nothing to do
1211}
1212
1213static void DSGXDummyRendererDeinit(struct DSGXRenderer* renderer) {
1214 UNUSED(renderer);
1215 // Nothing to do
1216}
1217
1218static void DSGXDummyRendererInvalidateTex(struct DSGXRenderer* renderer, int slot) {
1219 UNUSED(renderer);
1220 UNUSED(slot);
1221 // Nothing to do
1222}
1223
1224static void DSGXDummyRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount, bool wSort) {
1225 UNUSED(renderer);
1226 UNUSED(verts);
1227 UNUSED(polys);
1228 UNUSED(polyCount);
1229 // Nothing to do
1230}
1231
1232static void DSGXDummyRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
1233 UNUSED(renderer);
1234 UNUSED(y);
1235 // Nothing to do
1236}
1237
1238static void DSGXDummyRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) {
1239 UNUSED(renderer);
1240 UNUSED(y);
1241 *output = NULL;
1242 // Nothing to do
1243}