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