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