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