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