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