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