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