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