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);
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 void _fifoRun(struct mTiming* timing, void* context, uint32_t cyclesLate) {
333 struct DSGX* gx = context;
334 uint32_t cycles;
335 bool first = true;
336 while (!gx->swapBuffers) {
337 if (CircleBufferSize(&gx->pipe) <= 2 * sizeof(struct DSGXEntry)) {
338 _pullPipe(gx);
339 }
340
341 if (!CircleBufferSize(&gx->pipe)) {
342 cycles = 0;
343 break;
344 }
345
346 DSRegGXSTAT gxstat = gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1];
347 int projMatrixPointer = DSRegGXSTATGetProjMatrixStackLevel(gxstat);
348
349 struct DSGXEntry entry = { 0 };
350 CircleBufferDump(&gx->pipe, (int8_t*) &entry.command, 1);
351 cycles = _gxCommandCycleBase[entry.command];
352
353 if (first) {
354 first = false;
355 } else if (!gx->activeParams && cycles > cyclesLate) {
356 break;
357 }
358 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.command);
359 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[0]);
360 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[1]);
361 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[2]);
362 CircleBufferRead8(&gx->pipe, (int8_t*) &entry.params[3]);
363
364 if (gx->activeParams) {
365 int index = _gxCommandParams[entry.command] - gx->activeParams;
366 gx->activeEntries[index] = entry;
367 --gx->activeParams;
368 } else {
369 gx->activeParams = _gxCommandParams[entry.command];
370 if (gx->activeParams) {
371 --gx->activeParams;
372 }
373 if (gx->activeParams) {
374 gx->activeEntries[0] = entry;
375 }
376 }
377
378 if (gx->activeParams) {
379 continue;
380 }
381
382 switch (entry.command) {
383 case DS_GX_CMD_MTX_MODE:
384 if (entry.params[0] < 4) {
385 gx->mtxMode = entry.params[0];
386 } else {
387 mLOG(DS_GX, GAME_ERROR, "Invalid GX MTX_MODE %02X", entry.params[0]);
388 }
389 break;
390 case DS_GX_CMD_MTX_PUSH:
391 switch (gx->mtxMode) {
392 case 0:
393 memcpy(&gx->projMatrixStack, &gx->projMatrix, sizeof(gx->projMatrix));
394 ++projMatrixPointer;
395 break;
396 case 2:
397 memcpy(&gx->vecMatrixStack[gx->pvMatrixPointer & 0x1F], &gx->vecMatrix, sizeof(gx->vecMatrix));
398 // Fall through
399 case 1:
400 memcpy(&gx->posMatrixStack[gx->pvMatrixPointer & 0x1F], &gx->posMatrix, sizeof(gx->posMatrix));
401 ++gx->pvMatrixPointer;
402 break;
403 case 3:
404 mLOG(DS_GX, STUB, "Unimplemented GX MTX_PUSH mode");
405 break;
406 }
407 break;
408 case DS_GX_CMD_MTX_POP: {
409 int8_t offset = entry.params[0];
410 offset <<= 2;
411 offset >>= 2;
412 switch (gx->mtxMode) {
413 case 0:
414 projMatrixPointer -= offset;
415 memcpy(&gx->projMatrix, &gx->projMatrixStack, sizeof(gx->projMatrix));
416 break;
417 case 1:
418 gx->pvMatrixPointer -= offset;
419 memcpy(&gx->posMatrix, &gx->posMatrixStack[gx->pvMatrixPointer & 0x1F], sizeof(gx->posMatrix));
420 break;
421 case 2:
422 gx->pvMatrixPointer -= offset;
423 memcpy(&gx->vecMatrix, &gx->vecMatrixStack[gx->pvMatrixPointer & 0x1F], sizeof(gx->vecMatrix));
424 memcpy(&gx->posMatrix, &gx->posMatrixStack[gx->pvMatrixPointer & 0x1F], sizeof(gx->posMatrix));
425 break;
426 case 3:
427 mLOG(DS_GX, STUB, "Unimplemented GX MTX_POP mode");
428 break;
429 }
430 _updateClipMatrix(gx);
431 break;
432 }
433 case DS_GX_CMD_MTX_STORE: {
434 int8_t offset = entry.params[0] & 0x1F;
435 // TODO: overflow
436 switch (gx->mtxMode) {
437 case 0:
438 memcpy(&gx->projMatrixStack, &gx->projMatrix, sizeof(gx->projMatrixStack));
439 break;
440 case 2:
441 memcpy(&gx->vecMatrixStack[offset], &gx->vecMatrix, sizeof(gx->vecMatrix));
442 // Fall through
443 case 1:
444 memcpy(&gx->posMatrixStack[offset], &gx->posMatrix, sizeof(gx->posMatrix));
445 break;
446 case 3:
447 mLOG(DS_GX, STUB, "Unimplemented GX MTX_STORE mode");
448 break;
449 }
450 break;
451 }
452 case DS_GX_CMD_MTX_RESTORE: {
453 int8_t offset = entry.params[0] & 0x1F;
454 // TODO: overflow
455 switch (gx->mtxMode) {
456 case 0:
457 memcpy(&gx->projMatrix, &gx->projMatrixStack, sizeof(gx->projMatrix));
458 break;
459 case 2:
460 memcpy(&gx->vecMatrix, &gx->vecMatrixStack[offset], sizeof(gx->vecMatrix));
461 // Fall through
462 case 1:
463 memcpy(&gx->posMatrix, &gx->posMatrixStack[offset], sizeof(gx->posMatrix));
464 break;
465 case 3:
466 mLOG(DS_GX, STUB, "Unimplemented GX MTX_RESTORE mode");
467 break;
468 }
469 _updateClipMatrix(gx);
470 break;
471 }
472 case DS_GX_CMD_MTX_IDENTITY:
473 switch (gx->mtxMode) {
474 case 0:
475 DSGXMtxIdentity(&gx->projMatrix);
476 break;
477 case 2:
478 DSGXMtxIdentity(&gx->vecMatrix);
479 // Fall through
480 case 1:
481 DSGXMtxIdentity(&gx->posMatrix);
482 break;
483 case 3:
484 DSGXMtxIdentity(&gx->texMatrix);
485 break;
486 }
487 _updateClipMatrix(gx);
488 break;
489 case DS_GX_CMD_MTX_LOAD_4x4: {
490 struct DSGXMatrix m;
491 int i;
492 for (i = 0; i < 16; ++i) {
493 m.m[i] = gx->activeEntries[i].params[0];
494 m.m[i] |= gx->activeEntries[i].params[1] << 8;
495 m.m[i] |= gx->activeEntries[i].params[2] << 16;
496 m.m[i] |= gx->activeEntries[i].params[3] << 24;
497 }
498 switch (gx->mtxMode) {
499 case 0:
500 memcpy(&gx->projMatrix, &m, sizeof(gx->projMatrix));
501 break;
502 case 2:
503 memcpy(&gx->vecMatrix, &m, sizeof(gx->vecMatrix));
504 // Fall through
505 case 1:
506 memcpy(&gx->posMatrix, &m, sizeof(gx->posMatrix));
507 break;
508 case 3:
509 memcpy(&gx->texMatrix, &m, sizeof(gx->texMatrix));
510 break;
511 }
512 _updateClipMatrix(gx);
513 break;
514 }
515 case DS_GX_CMD_MTX_LOAD_4x3: {
516 struct DSGXMatrix m;
517 int i, j;
518 for (j = 0; j < 4; ++j) {
519 for (i = 0; i < 3; ++i) {
520 m.m[i + j * 4] = gx->activeEntries[i + j * 3].params[0];
521 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[1] << 8;
522 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[2] << 16;
523 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[3] << 24;
524 }
525 m.m[j * 4 + 3] = 0;
526 }
527 m.m[15] = MTX_ONE;
528 switch (gx->mtxMode) {
529 case 0:
530 memcpy(&gx->projMatrix, &m, sizeof(gx->projMatrix));
531 break;
532 case 2:
533 memcpy(&gx->vecMatrix, &m, sizeof(gx->vecMatrix));
534 // Fall through
535 case 1:
536 memcpy(&gx->posMatrix, &m, sizeof(gx->posMatrix));
537 break;
538 case 3:
539 memcpy(&gx->texMatrix, &m, sizeof(gx->texMatrix));
540 break;
541 }
542 _updateClipMatrix(gx);
543 break;
544 }
545 case DS_GX_CMD_MTX_MULT_4x4: {
546 struct DSGXMatrix m;
547 int i;
548 for (i = 0; i < 16; ++i) {
549 m.m[i] = gx->activeEntries[i].params[0];
550 m.m[i] |= gx->activeEntries[i].params[1] << 8;
551 m.m[i] |= gx->activeEntries[i].params[2] << 16;
552 m.m[i] |= gx->activeEntries[i].params[3] << 24;
553 }
554 switch (gx->mtxMode) {
555 case 0:
556 DSGXMtxMultiply(&gx->projMatrix, &m, &gx->projMatrix);
557 break;
558 case 2:
559 DSGXMtxMultiply(&gx->vecMatrix, &m, &gx->vecMatrix);
560 // Fall through
561 case 1:
562 DSGXMtxMultiply(&gx->posMatrix, &m, &gx->posMatrix);
563 break;
564 case 3:
565 DSGXMtxMultiply(&gx->texMatrix, &m, &gx->texMatrix);
566 break;
567 }
568 _updateClipMatrix(gx);
569 break;
570 }
571 case DS_GX_CMD_MTX_MULT_4x3: {
572 struct DSGXMatrix m;
573 int i, j;
574 for (j = 0; j < 4; ++j) {
575 for (i = 0; i < 3; ++i) {
576 m.m[i + j * 4] = gx->activeEntries[i + j * 3].params[0];
577 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[1] << 8;
578 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[2] << 16;
579 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[3] << 24;
580 }
581 m.m[j * 4 + 3] = 0;
582 }
583 m.m[15] = MTX_ONE;
584 switch (gx->mtxMode) {
585 case 0:
586 DSGXMtxMultiply(&gx->projMatrix, &m, &gx->projMatrix);
587 break;
588 case 2:
589 DSGXMtxMultiply(&gx->vecMatrix, &m, &gx->vecMatrix);
590 // Fall through
591 case 1:
592 DSGXMtxMultiply(&gx->posMatrix, &m, &gx->posMatrix);
593 break;
594 case 3:
595 DSGXMtxMultiply(&gx->texMatrix, &m, &gx->texMatrix);
596 break;
597 }
598 _updateClipMatrix(gx);
599 break;
600 }
601 case DS_GX_CMD_MTX_MULT_3x3: {
602 struct DSGXMatrix m;
603 int i, j;
604 for (j = 0; j < 3; ++j) {
605 for (i = 0; i < 3; ++i) {
606 m.m[i + j * 4] = gx->activeEntries[i + j * 3].params[0];
607 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[1] << 8;
608 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[2] << 16;
609 m.m[i + j * 4] |= gx->activeEntries[i + j * 3].params[3] << 24;
610 }
611 m.m[j * 4 + 3] = 0;
612 }
613 m.m[12] = 0;
614 m.m[13] = 0;
615 m.m[14] = 0;
616 m.m[15] = MTX_ONE;
617 switch (gx->mtxMode) {
618 case 0:
619 DSGXMtxMultiply(&gx->projMatrix, &m, &gx->projMatrix);
620 break;
621 case 2:
622 DSGXMtxMultiply(&gx->vecMatrix, &m, &gx->vecMatrix);
623 // Fall through
624 case 1:
625 DSGXMtxMultiply(&gx->posMatrix, &m, &gx->posMatrix);
626 break;
627 case 3:
628 DSGXMtxMultiply(&gx->texMatrix, &m, &gx->texMatrix);
629 break;
630 }
631 _updateClipMatrix(gx);
632 break;
633 }
634 case DS_GX_CMD_MTX_TRANS: {
635 int32_t m[3];
636 m[0] = gx->activeEntries[0].params[0];
637 m[0] |= gx->activeEntries[0].params[1] << 8;
638 m[0] |= gx->activeEntries[0].params[2] << 16;
639 m[0] |= gx->activeEntries[0].params[3] << 24;
640 m[1] = gx->activeEntries[1].params[0];
641 m[1] |= gx->activeEntries[1].params[1] << 8;
642 m[1] |= gx->activeEntries[1].params[2] << 16;
643 m[1] |= gx->activeEntries[1].params[3] << 24;
644 m[2] = gx->activeEntries[2].params[0];
645 m[2] |= gx->activeEntries[2].params[1] << 8;
646 m[2] |= gx->activeEntries[2].params[2] << 16;
647 m[2] |= gx->activeEntries[2].params[3] << 24;
648 switch (gx->mtxMode) {
649 case 0:
650 DSGXMtxTranslate(&gx->projMatrix, m);
651 break;
652 case 2:
653 DSGXMtxTranslate(&gx->vecMatrix, m);
654 // Fall through
655 case 1:
656 DSGXMtxTranslate(&gx->posMatrix, m);
657 break;
658 case 3:
659 DSGXMtxTranslate(&gx->texMatrix, m);
660 break;
661 }
662 _updateClipMatrix(gx);
663 break;
664 }
665 case DS_GX_CMD_MTX_SCALE: {
666 int32_t m[3];
667 m[0] = gx->activeEntries[0].params[0];
668 m[0] |= gx->activeEntries[0].params[1] << 8;
669 m[0] |= gx->activeEntries[0].params[2] << 16;
670 m[0] |= gx->activeEntries[0].params[3] << 24;
671 m[1] = gx->activeEntries[1].params[0];
672 m[1] |= gx->activeEntries[1].params[1] << 8;
673 m[1] |= gx->activeEntries[1].params[2] << 16;
674 m[1] |= gx->activeEntries[1].params[3] << 24;
675 m[2] = gx->activeEntries[2].params[0];
676 m[2] |= gx->activeEntries[2].params[1] << 8;
677 m[2] |= gx->activeEntries[2].params[2] << 16;
678 m[2] |= gx->activeEntries[2].params[3] << 24;
679 switch (gx->mtxMode) {
680 case 0:
681 DSGXMtxScale(&gx->projMatrix, m);
682 break;
683 case 2:
684 DSGXMtxScale(&gx->vecMatrix, m);
685 // Fall through
686 case 1:
687 DSGXMtxScale(&gx->posMatrix, m);
688 break;
689 case 3:
690 DSGXMtxScale(&gx->texMatrix, m);
691 break;
692 }
693 _updateClipMatrix(gx);
694 break;
695 }
696 case DS_GX_CMD_COLOR:
697 gx->currentVertex.color = entry.params[0];
698 gx->currentVertex.color |= entry.params[1] << 8;
699 break;
700 case DS_GX_CMD_TEXCOORD:
701 gx->currentVertex.s = entry.params[0];
702 gx->currentVertex.s |= entry.params[1] << 8;
703 gx->currentVertex.t = entry.params[2];
704 gx->currentVertex.t |= entry.params[3] << 8;
705 break;
706 case DS_GX_CMD_VTX_16: {
707 int16_t x = gx->activeEntries[0].params[0];
708 x |= gx->activeEntries[0].params[1] << 8;
709 int16_t y = gx->activeEntries[0].params[2];
710 y |= gx->activeEntries[0].params[3] << 8;
711 int16_t z = gx->activeEntries[1].params[0];
712 z |= gx->activeEntries[1].params[1] << 8;
713 _emitVertex(gx, x, y, z);
714 break;
715 }
716 case DS_GX_CMD_VTX_10: {
717 int32_t xyz = entry.params[0];
718 xyz |= entry.params[1] << 8;
719 xyz |= entry.params[2] << 16;
720 xyz |= entry.params[3] << 24;
721 int16_t x = (xyz << 6) & 0xFFC0;
722 int16_t y = (xyz >> 4) & 0xFFC0;
723 int16_t z = (xyz >> 14) & 0xFFC0;
724 _emitVertex(gx, x, y, z);
725 break;
726 }
727 case DS_GX_CMD_VTX_XY: {
728 int16_t x = entry.params[0];
729 x |= entry.params[1] << 8;
730 int16_t y = entry.params[2];
731 y |= entry.params[3] << 8;
732 _emitVertex(gx, x, y, gx->currentVertex.z);
733 break;
734 }
735 case DS_GX_CMD_VTX_XZ: {
736 int16_t x = entry.params[0];
737 x |= entry.params[1] << 8;
738 int16_t z = entry.params[2];
739 z |= entry.params[3] << 8;
740 _emitVertex(gx, x, gx->currentVertex.y, z);
741 break;
742 }
743 case DS_GX_CMD_VTX_YZ: {
744 int16_t y = entry.params[0];
745 y |= entry.params[1] << 8;
746 int16_t z = entry.params[2];
747 z |= entry.params[3] << 8;
748 _emitVertex(gx, gx->currentVertex.x, y, z);
749 break;
750 }
751 case DS_GX_CMD_VTX_DIFF: {
752 int32_t xyz = entry.params[0];
753 xyz |= entry.params[1] << 8;
754 xyz |= entry.params[2] << 16;
755 xyz |= entry.params[3] << 24;
756 int16_t x = (xyz << 6) & 0xFFC0;
757 int16_t y = (xyz >> 4) & 0xFFC0;
758 int16_t z = (xyz >> 14) & 0xFFC0;
759 _emitVertex(gx, gx->currentVertex.x + (x >> 6), gx->currentVertex.y + (y >> 6), gx->currentVertex.z + (z >> 6));
760 }
761 case DS_GX_CMD_POLYGON_ATTR:
762 gx->nextPoly.polyParams = entry.params[0];
763 gx->nextPoly.polyParams |= entry.params[1] << 8;
764 gx->nextPoly.polyParams |= entry.params[2] << 16;
765 gx->nextPoly.polyParams |= entry.params[3] << 24;
766 break;
767 case DS_GX_CMD_TEXIMAGE_PARAM:
768 gx->nextPoly.texParams = entry.params[0];
769 gx->nextPoly.texParams |= entry.params[1] << 8;
770 gx->nextPoly.texParams |= entry.params[2] << 16;
771 gx->nextPoly.texParams |= entry.params[3] << 24;
772 break;
773 case DS_GX_CMD_PLTT_BASE:
774 gx->nextPoly.palBase = entry.params[0];
775 gx->nextPoly.palBase |= entry.params[1] << 8;
776 gx->nextPoly.palBase |= entry.params[2] << 16;
777 gx->nextPoly.palBase |= entry.params[3] << 24;
778 break;
779 case DS_GX_CMD_BEGIN_VTXS:
780 gx->vertexMode = entry.params[0] & 3;
781 gx->currentPoly = gx->nextPoly;
782 break;
783 case DS_GX_CMD_END_VTXS:
784 gx->vertexMode = -1;
785 break;
786 case DS_GX_CMD_SWAP_BUFFERS:
787 gx->swapBuffers = true;
788 break;
789 case DS_GX_CMD_VIEWPORT:
790 gx->viewportX1 = (uint8_t) entry.params[0];
791 gx->viewportY1 = (uint8_t) entry.params[1];
792 gx->viewportX2 = (uint8_t) entry.params[2];
793 gx->viewportY2 = (uint8_t) entry.params[3];
794 gx->viewportWidth = gx->viewportX2 - gx->viewportX1;
795 gx->viewportHeight = gx->viewportY2 - gx->viewportY1;
796 break;
797 default:
798 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]);
799 break;
800 }
801
802 gxstat = DSRegGXSTATSetPVMatrixStackLevel(gxstat, gx->pvMatrixPointer);
803 gxstat = DSRegGXSTATSetProjMatrixStackLevel(gxstat, projMatrixPointer);
804 gxstat = DSRegGXSTATTestFillMatrixStackError(gxstat, projMatrixPointer || gx->pvMatrixPointer >= 0x1F);
805 gx->p->memory.io9[DS9_REG_GXSTAT_LO >> 1] = gxstat;
806
807 if (cyclesLate >= cycles) {
808 cyclesLate -= cycles;
809 } else {
810 break;
811 }
812 }
813 if (cycles && !gx->swapBuffers) {
814 mTimingSchedule(timing, &gx->fifoEvent, cycles - cyclesLate);
815 }
816 _flushOutstanding(gx);
817 DSGXUpdateGXSTAT(gx);
818}
819
820void DSGXInit(struct DSGX* gx) {
821 gx->renderer = &dummyRenderer;
822 CircleBufferInit(&gx->fifo, sizeof(struct DSGXEntry) * DS_GX_FIFO_SIZE);
823 CircleBufferInit(&gx->pipe, sizeof(struct DSGXEntry) * DS_GX_PIPE_SIZE);
824 gx->vertexBuffer[0] = malloc(sizeof(struct DSGXVertex) * DS_GX_VERTEX_BUFFER_SIZE);
825 gx->vertexBuffer[1] = malloc(sizeof(struct DSGXVertex) * DS_GX_VERTEX_BUFFER_SIZE);
826 gx->polygonBuffer[0] = malloc(sizeof(struct DSGXPolygon) * DS_GX_POLYGON_BUFFER_SIZE);
827 gx->polygonBuffer[1] = malloc(sizeof(struct DSGXPolygon) * DS_GX_POLYGON_BUFFER_SIZE);
828 gx->fifoEvent.name = "DS GX FIFO";
829 gx->fifoEvent.priority = 0xC;
830 gx->fifoEvent.context = gx;
831 gx->fifoEvent.callback = _fifoRun;
832}
833
834void DSGXDeinit(struct DSGX* gx) {
835 DSGXAssociateRenderer(gx, &dummyRenderer);
836 CircleBufferDeinit(&gx->fifo);
837 CircleBufferDeinit(&gx->pipe);
838 free(gx->vertexBuffer[0]);
839 free(gx->vertexBuffer[1]);
840 free(gx->polygonBuffer[0]);
841 free(gx->polygonBuffer[1]);
842}
843
844void DSGXReset(struct DSGX* gx) {
845 CircleBufferClear(&gx->fifo);
846 CircleBufferClear(&gx->pipe);
847 DSGXMtxIdentity(&gx->projMatrix);
848 DSGXMtxIdentity(&gx->texMatrix);
849 DSGXMtxIdentity(&gx->posMatrix);
850 DSGXMtxIdentity(&gx->vecMatrix);
851
852 DSGXMtxIdentity(&gx->clipMatrix);
853 DSGXMtxIdentity(&gx->projMatrixStack);
854 DSGXMtxIdentity(&gx->texMatrixStack);
855 int i;
856 for (i = 0; i < 32; ++i) {
857 DSGXMtxIdentity(&gx->posMatrixStack[i]);
858 DSGXMtxIdentity(&gx->vecMatrixStack[i]);
859 }
860 gx->swapBuffers = false;
861 gx->bufferIndex = 0;
862 gx->vertexIndex = 0;
863 gx->polygonIndex = 0;
864 gx->mtxMode = 0;
865 gx->pvMatrixPointer = 0;
866 gx->vertexMode = -1;
867
868 gx->viewportX1 = 0;
869 gx->viewportY1 = 0;
870 gx->viewportX2 = DS_VIDEO_HORIZONTAL_PIXELS - 1;
871 gx->viewportY2 = DS_VIDEO_VERTICAL_PIXELS - 1;
872 gx->viewportWidth = gx->viewportX2 - gx->viewportX1;
873 gx->viewportHeight = gx->viewportY2 - gx->viewportY1;
874
875 memset(gx->outstandingParams, 0, sizeof(gx->outstandingParams));
876 memset(gx->outstandingCommand, 0, sizeof(gx->outstandingCommand));
877 memset(&gx->outstandingEntry, 0, sizeof(gx->outstandingEntry));
878 gx->activeParams = 0;
879 memset(&gx->currentVertex, 0, sizeof(gx->currentVertex));
880 memset(&gx->nextPoly, 0, sizeof(gx-> nextPoly));
881}
882
883void DSGXAssociateRenderer(struct DSGX* gx, struct DSGXRenderer* renderer) {
884 gx->renderer->deinit(gx->renderer);
885 gx->renderer = renderer;
886 memcpy(gx->renderer->tex, gx->tex, sizeof(gx->renderer->tex));
887 memcpy(gx->renderer->texPal, gx->texPal, sizeof(gx->renderer->texPal));
888 gx->renderer->init(gx->renderer);
889}
890
891void DSGXUpdateGXSTAT(struct DSGX* gx) {
892 uint32_t value = gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] << 16;
893 value = DSRegGXSTATIsDoIRQ(value);
894
895 size_t entries = CircleBufferSize(&gx->fifo) / sizeof(struct DSGXEntry);
896 // XXX
897 if (gx->swapBuffers) {
898 entries++;
899 }
900 value = DSRegGXSTATSetFIFOEntries(value, entries);
901 value = DSRegGXSTATSetFIFOLtHalf(value, entries < (DS_GX_FIFO_SIZE / 2));
902 value = DSRegGXSTATSetFIFOEmpty(value, entries == 0);
903
904 if ((DSRegGXSTATGetDoIRQ(value) == 1 && entries < (DS_GX_FIFO_SIZE / 2)) ||
905 (DSRegGXSTATGetDoIRQ(value) == 2 && entries == 0)) {
906 DSRaiseIRQ(gx->p->ds9.cpu, gx->p->ds9.memory.io, DS_IRQ_GEOM_FIFO);
907 }
908
909 value = DSRegGXSTATSetBusy(value, mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent) || gx->swapBuffers);
910
911 gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] = value >> 16;
912}
913
914static void DSGXUnpackCommand(struct DSGX* gx, uint32_t command) {
915 gx->outstandingCommand[0] = command;
916 gx->outstandingCommand[1] = command >> 8;
917 gx->outstandingCommand[2] = command >> 16;
918 gx->outstandingCommand[3] = command >> 24;
919 if (gx->outstandingCommand[0] >= DS_GX_CMD_MAX) {
920 gx->outstandingCommand[0] = 0;
921 }
922 if (gx->outstandingCommand[1] >= DS_GX_CMD_MAX) {
923 gx->outstandingCommand[1] = 0;
924 }
925 if (gx->outstandingCommand[2] >= DS_GX_CMD_MAX) {
926 gx->outstandingCommand[2] = 0;
927 }
928 if (gx->outstandingCommand[3] >= DS_GX_CMD_MAX) {
929 gx->outstandingCommand[3] = 0;
930 }
931 gx->outstandingParams[0] = _gxCommandParams[gx->outstandingCommand[0]];
932 gx->outstandingParams[1] = _gxCommandParams[gx->outstandingCommand[1]];
933 gx->outstandingParams[2] = _gxCommandParams[gx->outstandingCommand[2]];
934 gx->outstandingParams[3] = _gxCommandParams[gx->outstandingCommand[3]];
935 _flushOutstanding(gx);
936 DSGXUpdateGXSTAT(gx);
937}
938
939static void DSGXWriteFIFO(struct DSGX* gx, struct DSGXEntry entry) {
940 if (CircleBufferSize(&gx->fifo) == (DS_GX_FIFO_SIZE * sizeof(entry))) {
941 mLOG(DS_GX, INFO, "FIFO full");
942 if (gx->p->cpuBlocked & DS_CPU_BLOCK_GX) {
943 abort();
944 }
945 gx->p->cpuBlocked |= DS_CPU_BLOCK_GX;
946 gx->outstandingEntry = entry;
947 gx->p->ds9.cpu->nextEvent = 0;
948 return;
949 }
950 if (gx->outstandingCommand[0]) {
951 entry.command = gx->outstandingCommand[0];
952 if (gx->outstandingParams[0]) {
953 --gx->outstandingParams[0];
954 }
955 if (!gx->outstandingParams[0]) {
956 // TODO: improve this
957 memmove(&gx->outstandingParams[0], &gx->outstandingParams[1], sizeof(gx->outstandingParams[0]) * 3);
958 memmove(&gx->outstandingCommand[0], &gx->outstandingCommand[1], sizeof(gx->outstandingCommand[0]) * 3);
959 gx->outstandingParams[3] = 0;
960 gx->outstandingCommand[3] = 0;
961 }
962 } else {
963 gx->outstandingParams[0] = _gxCommandParams[entry.command];
964 if (gx->outstandingParams[0]) {
965 --gx->outstandingParams[0];
966 }
967 if (gx->outstandingParams[0]) {
968 gx->outstandingCommand[0] = entry.command;
969 }
970 }
971 uint32_t cycles = _gxCommandCycleBase[entry.command];
972 if (!cycles) {
973 return;
974 }
975 if (CircleBufferSize(&gx->fifo) == 0 && CircleBufferSize(&gx->pipe) < (DS_GX_PIPE_SIZE * sizeof(entry))) {
976 CircleBufferWrite8(&gx->pipe, entry.command);
977 CircleBufferWrite8(&gx->pipe, entry.params[0]);
978 CircleBufferWrite8(&gx->pipe, entry.params[1]);
979 CircleBufferWrite8(&gx->pipe, entry.params[2]);
980 CircleBufferWrite8(&gx->pipe, entry.params[3]);
981 } else if (CircleBufferSize(&gx->fifo) < (DS_GX_FIFO_SIZE * sizeof(entry))) {
982 CircleBufferWrite8(&gx->fifo, entry.command);
983 CircleBufferWrite8(&gx->fifo, entry.params[0]);
984 CircleBufferWrite8(&gx->fifo, entry.params[1]);
985 CircleBufferWrite8(&gx->fifo, entry.params[2]);
986 CircleBufferWrite8(&gx->fifo, entry.params[3]);
987 }
988 if (!gx->swapBuffers && !mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent)) {
989 mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, cycles);
990 }
991
992 _flushOutstanding(gx);
993}
994
995uint16_t DSGXWriteRegister(struct DSGX* gx, uint32_t address, uint16_t value) {
996 uint16_t oldValue = gx->p->memory.io9[address >> 1];
997 switch (address) {
998 case DS9_REG_DISP3DCNT:
999 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
1000 break;
1001 case DS9_REG_GXSTAT_LO:
1002 value = DSRegGXSTATIsMatrixStackError(value);
1003 if (value) {
1004 oldValue = DSRegGXSTATClearMatrixStackError(oldValue);
1005 oldValue = DSRegGXSTATClearProjMatrixStackLevel(oldValue);
1006 }
1007 value = oldValue;
1008 break;
1009 case DS9_REG_GXSTAT_HI:
1010 value = DSRegGXSTATIsDoIRQ(value << 16) >> 16;
1011 gx->p->memory.io9[address >> 1] = value;
1012 DSGXUpdateGXSTAT(gx);
1013 value = gx->p->memory.io9[address >> 1];
1014 break;
1015 default:
1016 if (address < DS9_REG_GXFIFO_00) {
1017 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
1018 } else if (address <= DS9_REG_GXFIFO_1F) {
1019 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
1020 } else if (address < DS9_REG_GXSTAT_LO) {
1021 struct DSGXEntry entry = {
1022 .command = (address & 0x1FC) >> 2,
1023 .params = {
1024 value,
1025 value >> 8,
1026 }
1027 };
1028 if (entry.command < DS_GX_CMD_MAX) {
1029 DSGXWriteFIFO(gx, entry);
1030 }
1031 } else {
1032 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
1033 }
1034 break;
1035 }
1036 return value;
1037}
1038
1039uint32_t DSGXWriteRegister32(struct DSGX* gx, uint32_t address, uint32_t value) {
1040 switch (address) {
1041 case DS9_REG_DISP3DCNT:
1042 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
1043 break;
1044 case DS9_REG_GXSTAT_LO:
1045 value = (value & 0xFFFF0000) | DSGXWriteRegister(gx, DS9_REG_GXSTAT_LO, value);
1046 value = (value & 0x0000FFFF) | (DSGXWriteRegister(gx, DS9_REG_GXSTAT_HI, value >> 16) << 16);
1047 break;
1048 default:
1049 if (address < DS9_REG_GXFIFO_00) {
1050 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
1051 } else if (address <= DS9_REG_GXFIFO_1F) {
1052 if (gx->outstandingParams[0]) {
1053 struct DSGXEntry entry = {
1054 .command = gx->outstandingCommand[0],
1055 .params = {
1056 value,
1057 value >> 8,
1058 value >> 16,
1059 value >> 24
1060 }
1061 };
1062 DSGXWriteFIFO(gx, entry);
1063 } else {
1064 DSGXUnpackCommand(gx, value);
1065 }
1066 } else if (address < DS9_REG_GXSTAT_LO) {
1067 struct DSGXEntry entry = {
1068 .command = (address & 0x1FC) >> 2,
1069 .params = {
1070 value,
1071 value >> 8,
1072 value >> 16,
1073 value >> 24
1074 }
1075 };
1076 DSGXWriteFIFO(gx, entry);
1077 } else {
1078 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%08X", address, value);
1079 }
1080 break;
1081 }
1082 return value;
1083}
1084
1085void DSGXFlush(struct DSGX* gx) {
1086 if (gx->swapBuffers) {
1087 gx->renderer->setRAM(gx->renderer, gx->vertexBuffer[gx->bufferIndex], gx->polygonBuffer[gx->bufferIndex], gx->polygonIndex);
1088 gx->swapBuffers = false;
1089 gx->bufferIndex ^= 1;
1090 gx->vertexIndex = 0;
1091 gx->polygonIndex = 0;
1092 if (CircleBufferSize(&gx->fifo)) {
1093 mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, 0);
1094 }
1095 }
1096
1097 DSGXUpdateGXSTAT(gx);
1098}
1099
1100static void DSGXDummyRendererInit(struct DSGXRenderer* renderer) {
1101 UNUSED(renderer);
1102 // Nothing to do
1103}
1104
1105static void DSGXDummyRendererReset(struct DSGXRenderer* renderer) {
1106 UNUSED(renderer);
1107 // Nothing to do
1108}
1109
1110static void DSGXDummyRendererDeinit(struct DSGXRenderer* renderer) {
1111 UNUSED(renderer);
1112 // Nothing to do
1113}
1114
1115static void DSGXDummyRendererInvalidateTex(struct DSGXRenderer* renderer, int slot) {
1116 UNUSED(renderer);
1117 UNUSED(slot);
1118 // Nothing to do
1119}
1120
1121static void DSGXDummyRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount) {
1122 UNUSED(renderer);
1123 UNUSED(verts);
1124 UNUSED(polys);
1125 UNUSED(polyCount);
1126 // Nothing to do
1127}
1128
1129static void DSGXDummyRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
1130 UNUSED(renderer);
1131 UNUSED(y);
1132 // Nothing to do
1133}
1134
1135static void DSGXDummyRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) {
1136 UNUSED(renderer);
1137 UNUSED(y);
1138 *output = NULL;
1139 // Nothing to do
1140}