src/gba/gba-bios.c (view raw)
1#include "gba-bios.h"
2
3#include "gba.h"
4#include "gba-io.h"
5#include "gba-memory.h"
6
7#include <math.h>
8#include <stdlib.h>
9
10static void _unLz77(struct GBAMemory* memory, uint32_t source, uint8_t* dest);
11static void _unRl(struct GBAMemory* memory, uint32_t source, uint8_t* dest);
12
13static void _RegisterRamReset(struct GBA* gba) {
14 uint32_t registers = gba->cpu.gprs[0];
15 (void)(registers);
16 GBALog(gba, GBA_LOG_STUB, "RegisterRamReset unimplemented");
17}
18
19static void _CpuSet(struct GBA* gba) {
20 uint32_t source = gba->cpu.gprs[0];
21 uint32_t dest = gba->cpu.gprs[1];
22 uint32_t mode = gba->cpu.gprs[2];
23 int count = mode & 0x000FFFFF;
24 int fill = mode & 0x01000000;
25 int wordsize = (mode & 0x04000000) ? 4 : 2;
26 int i;
27 if (fill) {
28 if (wordsize == 4) {
29 source &= 0xFFFFFFFC;
30 dest &= 0xFFFFFFFC;
31 int32_t word = gba->memory.d.load32(&gba->memory.d, source, 0);
32 for (i = 0; i < count; ++i) {
33 gba->memory.d.store32(&gba->memory.d, dest + (i << 2), word, 0);
34 }
35 } else {
36 source &= 0xFFFFFFFE;
37 dest &= 0xFFFFFFFE;
38 uint16_t word = gba->memory.d.load16(&gba->memory.d, source, 0);
39 for (i = 0; i < count; ++i) {
40 gba->memory.d.store16(&gba->memory.d, dest + (i << 1), word, 0);
41 }
42 }
43 } else {
44 if (wordsize == 4) {
45 source &= 0xFFFFFFFC;
46 dest &= 0xFFFFFFFC;
47 for (i = 0; i < count; ++i) {
48 int32_t word = gba->memory.d.load32(&gba->memory.d, source + (i << 2), 0);
49 gba->memory.d.store32(&gba->memory.d, dest + (i << 2), word, 0);
50 }
51 } else {
52 source &= 0xFFFFFFFE;
53 dest &= 0xFFFFFFFE;
54 for (i = 0; i < count; ++i) {
55 uint16_t word = gba->memory.d.load16(&gba->memory.d, source + (i << 1), 0);
56 gba->memory.d.store16(&gba->memory.d, dest + (i << 1), word, 0);
57 }
58 }
59 }
60}
61
62static void _FastCpuSet(struct GBA* gba) {
63 uint32_t source = gba->cpu.gprs[0] & 0xFFFFFFFC;
64 uint32_t dest = gba->cpu.gprs[1] & 0xFFFFFFFC;
65 uint32_t mode = gba->cpu.gprs[2];
66 int count = mode & 0x000FFFFF;
67 count = ((count + 7) >> 3) << 3;
68 int i;
69 if (mode & 0x01000000) {
70 int32_t word = gba->memory.d.load32(&gba->memory.d, source, 0);
71 for (i = 0; i < count; ++i) {
72 gba->memory.d.store32(&gba->memory.d, dest + (i << 2), word, 0);
73 }
74 } else {
75 for (i = 0; i < count; ++i) {
76 int32_t word = gba->memory.d.load32(&gba->memory.d, source + (i << 2), 0);
77 gba->memory.d.store32(&gba->memory.d, dest + (i << 2), word, 0);
78 }
79 }
80}
81
82static void _BgAffineSet(struct GBA* gba) {
83 int i = gba->cpu.gprs[2];
84 float ox, oy;
85 float cx, cy;
86 float sx, sy;
87 float theta;
88 int offset = gba->cpu.gprs[0];
89 int destination = gba->cpu.gprs[1];
90 int diff = gba->cpu.gprs[3];
91 (void)(diff); // Are we supposed to use this?
92 float a, b, c, d;
93 float rx, ry;
94 while (i--) {
95 // [ sx 0 0 ] [ cos(theta) -sin(theta) 0 ] [ 1 0 cx - ox ] [ A B rx ]
96 // [ 0 sy 0 ] * [ sin(theta) cos(theta) 0 ] * [ 0 1 cy - oy ] = [ C D ry ]
97 // [ 0 0 1 ] [ 0 0 1 ] [ 0 0 1 ] [ 0 0 1 ]
98 ox = gba->memory.d.load32(&gba->memory.d, offset, 0) / 256.f;
99 oy = gba->memory.d.load32(&gba->memory.d, offset + 4, 0) / 256.f;
100 cx = gba->memory.d.load16(&gba->memory.d, offset + 8, 0);
101 cy = gba->memory.d.load16(&gba->memory.d, offset + 10, 0);
102 sx = gba->memory.d.load16(&gba->memory.d, offset + 12, 0) / 256.f;
103 sy = gba->memory.d.load16(&gba->memory.d, offset + 14, 0) / 256.f;
104 theta = (gba->memory.d.loadU16(&gba->memory.d, offset + 16, 0) >> 8) / 128.f * M_PI;
105 offset += 20;
106 // Rotation
107 a = d = cosf(theta);
108 b = c = sinf(theta);
109 // Scale
110 a *= sx;
111 b *= -sx;
112 c *= sy;
113 d *= sy;
114 // Translate
115 rx = ox - (a * cx + b * cy);
116 ry = oy - (c * cx + d * cy);
117 gba->memory.d.store16(&gba->memory.d, destination, a * 256, 0);
118 gba->memory.d.store16(&gba->memory.d, destination + 2, b * 256, 0);
119 gba->memory.d.store16(&gba->memory.d, destination + 4, c * 256, 0);
120 gba->memory.d.store16(&gba->memory.d, destination + 6, d * 256, 0);
121 gba->memory.d.store32(&gba->memory.d, destination + 8, rx * 256, 0);
122 gba->memory.d.store32(&gba->memory.d, destination + 12, ry * 256, 0);
123 destination += 16;
124 }
125}
126
127static void _ObjAffineSet(struct GBA* gba) {
128 int i = gba->cpu.gprs[2];
129 float sx, sy;
130 float theta;
131 int offset = gba->cpu.gprs[0];
132 int destination = gba->cpu.gprs[1];
133 int diff = gba->cpu.gprs[3];
134 float a, b, c, d;
135 while (i--) {
136 // [ sx 0 ] [ cos(theta) -sin(theta) ] [ A B ]
137 // [ 0 sy ] * [ sin(theta) cos(theta) ] = [ C D ]
138 sx = gba->memory.d.load16(&gba->memory.d, offset, 0) / 256.f;
139 sy = gba->memory.d.load16(&gba->memory.d, offset + 2, 0) / 256.f;
140 theta = (gba->memory.d.loadU16(&gba->memory.d, offset + 4, 0) >> 8) / 128.f * M_PI;
141 offset += 6;
142 // Rotation
143 a = d = cosf(theta);
144 b = c = sinf(theta);
145 // Scale
146 a *= sx;
147 b *= -sx;
148 c *= sy;
149 d *= sy;
150 gba->memory.d.store16(&gba->memory.d, destination, a * 256, 0);
151 gba->memory.d.store16(&gba->memory.d, destination + diff, b * 256, 0);
152 gba->memory.d.store16(&gba->memory.d, destination + diff * 2, c * 256, 0);
153 gba->memory.d.store16(&gba->memory.d, destination + diff * 3, d * 256, 0);
154 destination += diff * 4;
155 }
156}
157
158static void _MidiKey2Freq(struct GBA* gba) {
159 uint32_t key = gba->memory.d.load32(&gba->memory.d, gba->cpu.gprs[0] + 4, 0);
160 gba->cpu.gprs[0] = key / powf(2, (180.f - gba->cpu.gprs[1] - gba->cpu.gprs[2] / 256.f) / 12.f);
161}
162
163void GBASwi16(struct ARMBoard* board, int immediate) {
164 struct GBA* gba = ((struct GBABoard*) board)->p;
165 switch (immediate) {
166 case 0x1:
167 _RegisterRamReset(gba);
168 break;
169 case 0x2:
170 GBAHalt(gba);
171 break;
172 case 0x05:
173 // VBlankIntrWait
174 gba->cpu.gprs[0] = 1;
175 gba->cpu.gprs[1] = 1;
176 // Fall through:
177 case 0x04:
178 // IntrWait
179 gba->memory.io[REG_IME >> 1] = 1;
180 if (!gba->cpu.gprs[0] && gba->memory.io[REG_IF >> 1] & gba->cpu.gprs[1]) {
181 break;
182 }
183 gba->memory.io[REG_IF >> 1] = 0;
184 ARMRaiseSWI(&gba->cpu);
185 break;
186 case 0x6:
187 {
188 div_t result = div(gba->cpu.gprs[0], gba->cpu.gprs[1]);
189 gba->cpu.gprs[0] = result.quot;
190 gba->cpu.gprs[1] = result.rem;
191 gba->cpu.gprs[3] = abs(result.quot);
192 }
193 break;
194 case 0x7:
195 {
196 div_t result = div(gba->cpu.gprs[1], gba->cpu.gprs[0]);
197 gba->cpu.gprs[0] = result.quot;
198 gba->cpu.gprs[1] = result.rem;
199 gba->cpu.gprs[3] = abs(result.quot);
200 }
201 break;
202 case 0x8:
203 gba->cpu.gprs[0] = sqrt(gba->cpu.gprs[0]);
204 break;
205 case 0xA:
206 gba->cpu.gprs[0] = atan2f(gba->cpu.gprs[1] / 16384.f, gba->cpu.gprs[0] / 16384.f) / (2 * M_PI) * 0x10000;
207 break;
208 case 0xB:
209 _CpuSet(gba);
210 break;
211 case 0xC:
212 _FastCpuSet(gba);
213 break;
214 case 0xE:
215 _BgAffineSet(gba);
216 break;
217 case 0xF:
218 _ObjAffineSet(gba);
219 break;
220 case 0x11:
221 case 0x12:
222 switch (gba->cpu.gprs[1] >> BASE_OFFSET) {
223 case REGION_WORKING_RAM:
224 _unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.wram)[(gba->cpu.gprs[1] & (SIZE_WORKING_RAM - 1))]);
225 break;
226 case REGION_WORKING_IRAM:
227 _unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.iwram)[(gba->cpu.gprs[1] & (SIZE_WORKING_IRAM - 1))]);
228 break;
229 case REGION_VRAM:
230 _unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->video.renderer->vram)[(gba->cpu.gprs[1] & 0x0001FFFF)]);
231 break;
232 default:
233 GBALog(gba, GBA_LOG_WARN, "Bad LZ77 destination");
234 break;
235 }
236 break;
237 case 0x14:
238 case 0x15:
239 switch (gba->cpu.gprs[1] >> BASE_OFFSET) {
240 case REGION_WORKING_RAM:
241 _unRl(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.wram)[(gba->cpu.gprs[1] & (SIZE_WORKING_RAM - 1))]);
242 break;
243 case REGION_WORKING_IRAM:
244 _unRl(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.iwram)[(gba->cpu.gprs[1] & (SIZE_WORKING_IRAM - 1))]);
245 break;
246 case REGION_VRAM:
247 _unRl(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->video.renderer->vram)[(gba->cpu.gprs[1] & 0x0001FFFF)]);
248 break;
249 default:
250 GBALog(gba, GBA_LOG_WARN, "Bad RL destination");
251 break;
252 }
253 break;
254 case 0x1F:
255 _MidiKey2Freq(gba);
256 break;
257 default:
258 GBALog(gba, GBA_LOG_STUB, "Stub software interrupt: %02x", immediate);
259 }
260}
261
262void GBASwi32(struct ARMBoard* board, int immediate) {
263 GBASwi16(board, immediate >> 16);
264}
265
266static void _unLz77(struct GBAMemory* memory, uint32_t source, uint8_t* dest) {
267 int remaining = (memory->d.load32(&memory->d, source, 0) & 0xFFFFFF00) >> 8;
268 // We assume the signature byte (0x10) is correct
269 int blockheader;
270 uint32_t sPointer = source + 4;
271 uint8_t* dPointer = dest;
272 int blocksRemaining = 0;
273 int block;
274 uint8_t* disp;
275 int bytes;
276 while (remaining > 0) {
277 if (blocksRemaining) {
278 if (blockheader & 0x80) {
279 // Compressed
280 block = memory->d.loadU8(&memory->d, sPointer, 0) | (memory->d.loadU8(&memory->d, sPointer + 1, 0) << 8);
281 sPointer += 2;
282 disp = dPointer - (((block & 0x000F) << 8) | ((block & 0xFF00) >> 8)) - 1;
283 bytes = ((block & 0x00F0) >> 4) + 3;
284 while (bytes-- && remaining) {
285 --remaining;
286 *dPointer = *disp;
287 ++disp;
288 ++dPointer;
289 }
290 } else {
291 // Uncompressed
292 *dPointer = memory->d.loadU8(&memory->d, sPointer++, 0);
293 ++dPointer;
294 --remaining;
295 }
296 blockheader <<= 1;
297 --blocksRemaining;
298 } else {
299 blockheader = memory->d.loadU8(&memory->d, sPointer++, 0);
300 blocksRemaining = 8;
301 }
302 }
303}
304
305static void _unRl(struct GBAMemory* memory, uint32_t source, uint8_t* dest) {
306 source = source & 0xFFFFFFFC;
307 int remaining = (memory->d.load32(&memory->d, source, 0) & 0xFFFFFF00) >> 8;
308 int padding = (4 - remaining) & 0x3;
309 // We assume the signature byte (0x30) is correct
310 int blockheader;
311 int block;
312 uint32_t sPointer = source + 4;
313 uint8_t* dPointer = dest;
314 while (remaining > 0) {
315 blockheader = memory->d.loadU8(&memory->d, sPointer++, 0);
316 if (blockheader & 0x80) {
317 // Compressed
318 blockheader &= 0x7F;
319 blockheader += 3;
320 block = memory->d.loadU8(&memory->d, sPointer++, 0);
321 while (blockheader-- && remaining) {
322 --remaining;
323 *dPointer = block;
324 ++dPointer;
325 }
326 } else {
327 // Uncompressed
328 blockheader++;
329 while (blockheader-- && remaining) {
330 --remaining;
331 *dPointer = memory->d.loadU8(&memory->d, sPointer++, 0);
332 ++dPointer;
333 }
334 }
335 }
336 while (padding--) {
337 *dPointer = 0;
338 ++dPointer;
339 }
340}