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