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");
12
13#define DS_GX_FIFO_SIZE 256
14
15static const int32_t _gxCommandCycleBase[DS_GX_CMD_MAX] = {
16 [DS_GX_CMD_NOP] = 0,
17 [DS_GX_CMD_MTX_MODE] = 2,
18 [DS_GX_CMD_MTX_PUSH] = 34,
19 [DS_GX_CMD_MTX_POP] = 72,
20 [DS_GX_CMD_MTX_STORE] = 34,
21 [DS_GX_CMD_MTX_RESTORE] = 72,
22 [DS_GX_CMD_MTX_IDENTITY] = 38,
23 [DS_GX_CMD_MTX_LOAD_4x4] = 68,
24 [DS_GX_CMD_MTX_LOAD_4x3] = 60,
25 [DS_GX_CMD_MTX_MULT_4x4] = 70,
26 [DS_GX_CMD_MTX_MULT_4x3] = 62,
27 [DS_GX_CMD_MTX_MULT_3x3] = 56,
28 [DS_GX_CMD_MTX_SCALE] = 44,
29 [DS_GX_CMD_MTX_TRANS] = 44,
30 [DS_GX_CMD_COLOR] = 2,
31 [DS_GX_CMD_NORMAL] = 18,
32 [DS_GX_CMD_TEXCOORD] = 2,
33 [DS_GX_CMD_VTX_16] = 18,
34 [DS_GX_CMD_VTX_10] = 16,
35 [DS_GX_CMD_VTX_XY] = 16,
36 [DS_GX_CMD_VTX_XZ] = 16,
37 [DS_GX_CMD_VTX_YZ] = 16,
38 [DS_GX_CMD_VTX_DIFF] = 16,
39 [DS_GX_CMD_POLYGON_ATTR] = 2,
40 [DS_GX_CMD_TEXIMAGE_PARAM] = 2,
41 [DS_GX_CMD_PLTT_BASE] = 2,
42 [DS_GX_CMD_DIF_AMB] = 8,
43 [DS_GX_CMD_SPE_EMI] = 8,
44 [DS_GX_CMD_LIGHT_VECTOR] = 12,
45 [DS_GX_CMD_LIGHT_COLOR] = 2,
46 [DS_GX_CMD_SHININESS] = 64,
47 [DS_GX_CMD_BEGIN_VTXS] = 2,
48 [DS_GX_CMD_END_VTXS] = 2,
49 [DS_GX_CMD_SWAP_BUFFERS] = 784,
50 [DS_GX_CMD_VIEWPORT] = 2,
51 [DS_GX_CMD_BOX_TEST] = 206,
52 [DS_GX_CMD_POS_TEST] = 18,
53 [DS_GX_CMD_VEC_TEST] = 10,
54};
55
56static void _fifoRun(struct mTiming* timing, void* context, uint32_t cyclesLate) {
57 struct DSGX* gx = context;
58 uint32_t cycles;
59 while (true) {
60 struct DSGXEntry entry = { 0 };
61 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.command);
62 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[0]);
63 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[1]);
64 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[2]);
65 CircleBufferRead8(&gx->fifo, (int8_t*) &entry.params[3]);
66 cycles = _gxCommandCycleBase[entry.command];
67
68 switch (entry.command) {
69 case DS_GX_CMD_SWAP_BUFFERS:
70 gx->swapBuffers = true;
71 break;
72 default:
73 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]);
74 break;
75 }
76 if (CircleBufferSize(&gx->fifo)) {
77 if (cycles <= cyclesLate) {
78 cyclesLate -= cycles;
79 } else {
80 break;
81 }
82 } else {
83 cycles = 0;
84 break;
85 }
86 }
87 DSGXUpdateGXSTAT(gx);
88 if (cycles) {
89 mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, cycles);
90 }
91}
92
93void DSGXInit(struct DSGX* gx) {
94 CircleBufferInit(&gx->fifo, sizeof(struct DSGXEntry) * DS_GX_FIFO_SIZE);
95 gx->fifoEvent.name = "DS GX FIFO";
96 gx->fifoEvent.priority = 0xC;
97 gx->fifoEvent.context = gx;
98 gx->fifoEvent.callback = _fifoRun;
99}
100
101void DSGXDeinit(struct DSGX* gx) {
102 CircleBufferDeinit(&gx->fifo);
103}
104
105void DSGXReset(struct DSGX* gx) {
106 CircleBufferClear(&gx->fifo);
107 gx->swapBuffers = false;
108}
109
110void DSGXUpdateGXSTAT(struct DSGX* gx) {
111 uint32_t value = gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] << 16;
112 value = DSRegGXSTATIsDoIRQ(value);
113
114 size_t entries = CircleBufferSize(&gx->fifo) / sizeof(struct DSGXEntry);
115 // XXX
116 if (gx->swapBuffers) {
117 entries++;
118 }
119 value = DSRegGXSTATSetFIFOEntries(value, entries);
120 value = DSRegGXSTATSetFIFOLtHalf(value, entries < (DS_GX_FIFO_SIZE / 2));
121 value = DSRegGXSTATSetFIFOEmpty(value, entries == 0);
122
123 if ((DSRegGXSTATGetDoIRQ(value) == 1 && entries < (DS_GX_FIFO_SIZE / 2)) ||
124 (DSRegGXSTATGetDoIRQ(value) == 2 && entries == 0)) {
125 DSRaiseIRQ(gx->p->ds9.cpu, gx->p->ds9.memory.io, DS_IRQ_GEOM_FIFO);
126 }
127
128 value = DSRegGXSTATSetBusy(value, mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent) || gx->swapBuffers);
129
130 gx->p->memory.io9[DS9_REG_GXSTAT_HI >> 1] = value >> 16;
131}
132
133static void DSGXWriteFIFO(struct DSGX* gx, struct DSGXEntry entry) {
134 uint32_t cycles = _gxCommandCycleBase[entry.command];
135 if (!cycles) {
136 return;
137 }
138 // TODO: Outstanding parameters
139 if (CircleBufferSize(&gx->fifo) < (DS_GX_FIFO_SIZE * sizeof(entry))) {
140 CircleBufferWrite8(&gx->fifo, entry.command);
141 CircleBufferWrite8(&gx->fifo, entry.params[0]);
142 CircleBufferWrite8(&gx->fifo, entry.params[1]);
143 CircleBufferWrite8(&gx->fifo, entry.params[2]);
144 CircleBufferWrite8(&gx->fifo, entry.params[3]);
145 if (!mTimingIsScheduled(&gx->p->ds9.timing, &gx->fifoEvent)) {
146 mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, 0);
147 }
148 } else {
149 mLOG(DS_GX, STUB, "Unimplemented GX full");
150 }
151}
152
153uint16_t DSGXWriteRegister(struct DSGX* gx, uint32_t address, uint16_t value) {
154 uint16_t oldValue = gx->p->memory.io9[address >> 1];
155 switch (address) {
156 case DS9_REG_DISP3DCNT:
157 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
158 break;
159 case DS9_REG_GXSTAT_LO:
160 value = DSRegGXSTATIsMatrixStackError(value);
161 if (value) {
162 oldValue = DSRegGXSTATClearMatrixStackError(oldValue);
163 oldValue = DSRegGXSTATClearProjMatrixStackLevel(oldValue);
164 }
165 value = oldValue;
166 break;
167 case DS9_REG_GXSTAT_HI:
168 value = DSRegGXSTATIsDoIRQ(value << 16) >> 16;
169 gx->p->memory.io9[address >> 1] = value;
170 DSGXUpdateGXSTAT(gx);
171 value = gx->p->memory.io9[address >> 1];
172 break;
173 default:
174 if (address < DS9_REG_GXFIFO_00) {
175 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
176 } else if (address < DS9_REG_GXSTAT_LO) {
177 struct DSGXEntry entry = {
178 .command = (address & 0x1FC) >> 2,
179 .params = {
180 value,
181 value >> 8,
182 }
183 };
184 if (entry.command < 0x80) {
185 DSGXWriteFIFO(gx, entry);
186 }
187 } else {
188 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
189 }
190 break;
191 }
192 return value;
193}
194
195uint32_t DSGXWriteRegister32(struct DSGX* gx, uint32_t address, uint32_t value) {
196 switch (address) {
197 case DS9_REG_DISP3DCNT:
198 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
199 break;
200 case DS9_REG_GXSTAT_LO:
201 value = (value & 0xFFFF0000) | DSGXWriteRegister(gx, DS9_REG_GXSTAT_LO, value);
202 value = (value & 0x0000FFFF) | (DSGXWriteRegister(gx, DS9_REG_GXSTAT_HI, value >> 16) << 16);
203 break;
204 default:
205 if (address < DS9_REG_GXFIFO_00) {
206 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
207 } else if (address < DS9_REG_GXSTAT_LO) {
208 struct DSGXEntry entry = {
209 .command = (address & 0x1FC) >> 2l,
210 .params = {
211 value,
212 value >> 8,
213 value >> 16,
214 value >> 24
215 }
216 };
217 DSGXWriteFIFO(gx, entry);
218 } else {
219 mLOG(DS_GX, STUB, "Unimplemented GX write %03X:%04X", address, value);
220 }
221 break;
222 }
223 return value;
224}
225
226void DSGXSwapBuffers(struct DSGX* gx) {
227 mLOG(DS_GX, STUB, "Unimplemented GX swap buffers");
228 gx->swapBuffers = false;
229
230 // TODO
231 DSGXUpdateGXSTAT(gx);
232 if (CircleBufferSize(&gx->fifo)) {
233 mTimingSchedule(&gx->p->ds9.timing, &gx->fifoEvent, 0);
234 }
235}