src/gba/gba-bios.c (view raw)
1/* Copyright (c) 2013-2014 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 "gba-bios.h"
7
8#include "gba.h"
9#include "gba-io.h"
10#include "gba-memory.h"
11
12const uint32_t GBA_BIOS_CHECKSUM = 0xBAAE187F;
13const uint32_t GBA_DS_BIOS_CHECKSUM = 0xBAAE1880;
14
15static void _unLz77(struct GBA* gba, uint32_t source, uint32_t dest, int width);
16static void _unHuffman(struct GBA* gba, uint32_t source, uint32_t dest);
17static void _unRl(struct GBA* gba, uint32_t source, uint32_t dest, int width);
18static void _unFilter(struct GBA* gba, uint32_t source, uint32_t dest, int inwidth, int outwidth);
19
20static void _RegisterRamReset(struct GBA* gba) {
21 uint32_t registers = gba->cpu->gprs[0];
22 UNUSED(registers);
23 GBALog(gba, GBA_LOG_STUB, "RegisterRamReset unimplemented");
24}
25
26static void _BgAffineSet(struct GBA* gba) {
27 struct ARMCore* cpu = gba->cpu;
28 int i = cpu->gprs[2];
29 float ox, oy;
30 float cx, cy;
31 float sx, sy;
32 float theta;
33 int offset = cpu->gprs[0];
34 int destination = cpu->gprs[1];
35 float a, b, c, d;
36 float rx, ry;
37 while (i--) {
38 // [ sx 0 0 ] [ cos(theta) -sin(theta) 0 ] [ 1 0 cx - ox ] [ A B rx ]
39 // [ 0 sy 0 ] * [ sin(theta) cos(theta) 0 ] * [ 0 1 cy - oy ] = [ C D ry ]
40 // [ 0 0 1 ] [ 0 0 1 ] [ 0 0 1 ] [ 0 0 1 ]
41 ox = cpu->memory.load32(cpu, offset, 0) / 256.f;
42 oy = cpu->memory.load32(cpu, offset + 4, 0) / 256.f;
43 cx = cpu->memory.load16(cpu, offset + 8, 0);
44 cy = cpu->memory.load16(cpu, offset + 10, 0);
45 sx = cpu->memory.load16(cpu, offset + 12, 0) / 256.f;
46 sy = cpu->memory.load16(cpu, offset + 14, 0) / 256.f;
47 theta = (cpu->memory.loadU16(cpu, offset + 16, 0) >> 8) / 128.f * M_PI;
48 offset += 20;
49 // Rotation
50 a = d = cosf(theta);
51 b = c = sinf(theta);
52 // Scale
53 a *= sx;
54 b *= -sx;
55 c *= sy;
56 d *= sy;
57 // Translate
58 rx = ox - (a * cx + b * cy);
59 ry = oy - (c * cx + d * cy);
60 cpu->memory.store16(cpu, destination, a * 256, 0);
61 cpu->memory.store16(cpu, destination + 2, b * 256, 0);
62 cpu->memory.store16(cpu, destination + 4, c * 256, 0);
63 cpu->memory.store16(cpu, destination + 6, d * 256, 0);
64 cpu->memory.store32(cpu, destination + 8, rx * 256, 0);
65 cpu->memory.store32(cpu, destination + 12, ry * 256, 0);
66 destination += 16;
67 }
68}
69
70static void _ObjAffineSet(struct GBA* gba) {
71 struct ARMCore* cpu = gba->cpu;
72 int i = cpu->gprs[2];
73 float sx, sy;
74 float theta;
75 int offset = cpu->gprs[0];
76 int destination = cpu->gprs[1];
77 int diff = cpu->gprs[3];
78 float a, b, c, d;
79 while (i--) {
80 // [ sx 0 ] [ cos(theta) -sin(theta) ] [ A B ]
81 // [ 0 sy ] * [ sin(theta) cos(theta) ] = [ C D ]
82 sx = cpu->memory.load16(cpu, offset, 0) / 256.f;
83 sy = cpu->memory.load16(cpu, offset + 2, 0) / 256.f;
84 theta = (cpu->memory.loadU16(cpu, offset + 4, 0) >> 8) / 128.f * M_PI;
85 offset += 8;
86 // Rotation
87 a = d = cosf(theta);
88 b = c = sinf(theta);
89 // Scale
90 a *= sx;
91 b *= -sx;
92 c *= sy;
93 d *= sy;
94 cpu->memory.store16(cpu, destination, a * 256, 0);
95 cpu->memory.store16(cpu, destination + diff, b * 256, 0);
96 cpu->memory.store16(cpu, destination + diff * 2, c * 256, 0);
97 cpu->memory.store16(cpu, destination + diff * 3, d * 256, 0);
98 destination += diff * 4;
99 }
100}
101
102static void _MidiKey2Freq(struct GBA* gba) {
103 struct ARMCore* cpu = gba->cpu;
104 uint32_t key = cpu->memory.load32(cpu, cpu->gprs[0] + 4, 0);
105 cpu->gprs[0] = key / powf(2, (180.f - cpu->gprs[1] - cpu->gprs[2] / 256.f) / 12.f);
106}
107
108static void _Div(struct GBA* gba, int32_t num, int32_t denom) {
109 struct ARMCore* cpu = gba->cpu;
110 if (denom != 0) {
111 div_t result = div(num, denom);
112 cpu->gprs[0] = result.quot;
113 cpu->gprs[1] = result.rem;
114 cpu->gprs[3] = abs(result.quot);
115 } else {
116 GBALog(gba, GBA_LOG_GAME_ERROR, "Attempting to divide %i by zero!", num);
117 // If abs(num) > 1, this should hang, but that would be painful to
118 // emulate in HLE, and no game will get into a state where it hangs...
119 cpu->gprs[0] = (num < 0) ? -1 : 1;
120 cpu->gprs[1] = num;
121 cpu->gprs[3] = 1;
122 }
123}
124
125void GBASwi16(struct ARMCore* cpu, int immediate) {
126 struct GBA* gba = (struct GBA*) cpu->master;
127 GBALog(gba, GBA_LOG_SWI, "SWI: %02X r0: %08X r1: %08X r2: %08X r3: %08X",
128 immediate, cpu->gprs[0], cpu->gprs[1], cpu->gprs[2], cpu->gprs[3]);
129
130 if (gba->memory.fullBios) {
131 ARMRaiseSWI(cpu);
132 return;
133 }
134 switch (immediate) {
135 case 0x1:
136 _RegisterRamReset(gba);
137 break;
138 case 0x2:
139 GBAHalt(gba);
140 break;
141 case 0x05:
142 // VBlankIntrWait
143 // Fall through:
144 case 0x04:
145 // IntrWait
146 ARMRaiseSWI(cpu);
147 break;
148 case 0x6:
149 _Div(gba, cpu->gprs[0], cpu->gprs[1]);
150 break;
151 case 0x7:
152 _Div(gba, cpu->gprs[1], cpu->gprs[0]);
153 break;
154 case 0x8:
155 cpu->gprs[0] = sqrt(cpu->gprs[0]);
156 break;
157 case 0xA:
158 cpu->gprs[0] = atan2f(cpu->gprs[1] / 16384.f, cpu->gprs[0] / 16384.f) / (2 * M_PI) * 0x10000;
159 break;
160 case 0xB:
161 case 0xC:
162 ARMRaiseSWI(cpu);
163 break;
164 case 0xD:
165 cpu->gprs[0] = GBAChecksum(gba->memory.bios, SIZE_BIOS);
166 case 0xE:
167 _BgAffineSet(gba);
168 break;
169 case 0xF:
170 _ObjAffineSet(gba);
171 break;
172 case 0x11:
173 case 0x12:
174 if (cpu->gprs[0] < BASE_WORKING_RAM) {
175 GBALog(gba, GBA_LOG_GAME_ERROR, "Bad LZ77 source");
176 }
177 switch (cpu->gprs[1] >> BASE_OFFSET) {
178 default:
179 GBALog(gba, GBA_LOG_GAME_ERROR, "Bad LZ77 destination");
180 case REGION_WORKING_RAM:
181 case REGION_WORKING_IRAM:
182 case REGION_VRAM:
183 _unLz77(gba, cpu->gprs[0], cpu->gprs[1], immediate == 0x11 ? 1 : 2);
184 break;
185 }
186 break;
187 case 0x13:
188 if (cpu->gprs[0] < BASE_WORKING_RAM) {
189 GBALog(gba, GBA_LOG_GAME_ERROR, "Bad Huffman source");
190 }
191 switch (cpu->gprs[1] >> BASE_OFFSET) {
192 default:
193 GBALog(gba, GBA_LOG_GAME_ERROR, "Bad Huffman destination");
194 case REGION_WORKING_RAM:
195 case REGION_WORKING_IRAM:
196 case REGION_VRAM:
197 _unHuffman(gba, cpu->gprs[0], cpu->gprs[1]);
198 break;
199 }
200 break;
201 case 0x14:
202 case 0x15:
203 if (cpu->gprs[0] < BASE_WORKING_RAM) {
204 GBALog(gba, GBA_LOG_GAME_ERROR, "Bad RL source");
205 }
206 switch (cpu->gprs[1] >> BASE_OFFSET) {
207 default:
208 GBALog(gba, GBA_LOG_GAME_ERROR, "Bad RL destination");
209 case REGION_WORKING_RAM:
210 case REGION_WORKING_IRAM:
211 case REGION_VRAM:
212 _unRl(gba, cpu->gprs[0], cpu->gprs[1], immediate == 0x14 ? 1 : 2);
213 break;
214 }
215 break;
216 case 0x16:
217 case 0x17:
218 case 0x18:
219 if (cpu->gprs[0] < BASE_WORKING_RAM) {
220 GBALog(gba, GBA_LOG_GAME_ERROR, "Bad UnFilter source");
221 }
222 switch (cpu->gprs[1] >> BASE_OFFSET) {
223 default:
224 GBALog(gba, GBA_LOG_GAME_ERROR, "Bad UnFilter destination");
225 case REGION_WORKING_RAM:
226 case REGION_WORKING_IRAM:
227 case REGION_VRAM:
228 _unFilter(gba, cpu->gprs[0], cpu->gprs[1], immediate == 0x18 ? 2 : 1, immediate == 0x16 ? 1 : 2);
229 break;
230 }
231 break;
232 case 0x1F:
233 _MidiKey2Freq(gba);
234 break;
235 default:
236 GBALog(gba, GBA_LOG_STUB, "Stub software interrupt: %02X", immediate);
237 }
238}
239
240void GBASwi32(struct ARMCore* cpu, int immediate) {
241 GBASwi16(cpu, immediate >> 16);
242}
243
244uint32_t GBAChecksum(uint32_t* memory, size_t size) {
245 size_t i;
246 uint32_t sum = 0;
247 for (i = 0; i < size; i += 4) {
248 sum += memory[i >> 2];
249 }
250 return sum;
251}
252
253static void _unLz77(struct GBA* gba, uint32_t source, uint32_t dest, int width) {
254 struct ARMCore* cpu = gba->cpu;
255 int remaining = (cpu->memory.load32(cpu, source, 0) & 0xFFFFFF00) >> 8;
256 // We assume the signature byte (0x10) is correct
257 int blockheader = 0; // Some compilers warn if this isn't set, even though it's trivially provably always set
258 uint32_t sPointer = source + 4;
259 uint32_t dPointer = dest;
260 int blocksRemaining = 0;
261 int block;
262 uint32_t disp;
263 int bytes;
264 int byte;
265 int halfword;
266 while (remaining > 0) {
267 if (blocksRemaining) {
268 if (blockheader & 0x80) {
269 // Compressed
270 block = cpu->memory.loadU8(cpu, sPointer, 0) | (cpu->memory.loadU8(cpu, sPointer + 1, 0) << 8);
271 sPointer += 2;
272 disp = dPointer - (((block & 0x000F) << 8) | ((block & 0xFF00) >> 8)) - 1;
273 bytes = ((block & 0x00F0) >> 4) + 3;
274 while (bytes-- && remaining) {
275 --remaining;
276 byte = cpu->memory.loadU8(cpu, disp, 0);
277 ++disp;
278 if (width == 2) {
279 if (dPointer & 1) {
280 halfword |= byte << 8;
281 cpu->memory.store16(cpu, dPointer ^ 1, halfword, 0);
282 } else {
283 halfword = byte;
284 }
285 } else {
286 cpu->memory.store8(cpu, dPointer, byte, 0);
287 }
288 ++dPointer;
289 }
290 } else {
291 // Uncompressed
292 byte = cpu->memory.loadU8(cpu, sPointer, 0);
293 ++sPointer;
294 if (width == 2) {
295 if (dPointer & 1) {
296 halfword |= byte << 8;
297 cpu->memory.store16(cpu, dPointer ^ 1, halfword, 0);
298 } else {
299 halfword = byte;
300 }
301 } else {
302 cpu->memory.store8(cpu, dPointer, byte, 0);
303 }
304 ++dPointer;
305 --remaining;
306 }
307 blockheader <<= 1;
308 --blocksRemaining;
309 } else {
310 blockheader = cpu->memory.loadU8(cpu, sPointer, 0);
311 ++sPointer;
312 blocksRemaining = 8;
313 }
314 }
315}
316
317DECL_BITFIELD(HuffmanNode, uint8_t);
318DECL_BITS(HuffmanNode, Offset, 0, 6);
319DECL_BIT(HuffmanNode, RTerm, 6);
320DECL_BIT(HuffmanNode, LTerm, 7);
321
322static void _unHuffman(struct GBA* gba, uint32_t source, uint32_t dest) {
323 struct ARMCore* cpu = gba->cpu;
324 source = source & 0xFFFFFFFC;
325 uint32_t header = cpu->memory.load32(cpu, source, 0);
326 int remaining = header >> 8;
327 int bits = header & 0xF;
328 if (32 % bits) {
329 GBALog(gba, GBA_LOG_STUB, "Unimplemented unaligned Huffman");
330 return;
331 }
332 int padding = (4 - remaining) & 0x3;
333 remaining &= 0xFFFFFFFC;
334 // We assume the signature byte (0x20) is correct
335 int treesize = (cpu->memory.loadU8(cpu, source + 4, 0) << 1) + 1;
336 int block = 0;
337 uint32_t treeBase = source + 5;
338 uint32_t sPointer = source + 5 + treesize;
339 uint32_t dPointer = dest;
340 uint32_t nPointer = treeBase;
341 HuffmanNode node;
342 int bitsRemaining;
343 int readBits;
344 int bitsSeen = 0;
345 node = cpu->memory.load8(cpu, nPointer, 0);
346 while (remaining > 0) {
347 uint32_t bitstream = cpu->memory.load32(cpu, sPointer, 0);
348 sPointer += 4;
349 for (bitsRemaining = 32; bitsRemaining > 0 && remaining > 0; --bitsRemaining, bitstream <<= 1) {
350 uint32_t next = (nPointer & ~1) + HuffmanNodeGetOffset(node) * 2 + 2;
351 if (bitstream & 0x80000000) {
352 // Go right
353 if (HuffmanNodeIsRTerm(node)) {
354 readBits = cpu->memory.load8(cpu, next + 1, 0);
355 } else {
356 nPointer = next + 1;
357 node = cpu->memory.load8(cpu, nPointer, 0);
358 continue;
359 }
360 } else {
361 // Go left
362 if (HuffmanNodeIsLTerm(node)) {
363 readBits = cpu->memory.load8(cpu, next, 0);
364 } else {
365 nPointer = next;
366 node = cpu->memory.load8(cpu, nPointer, 0);
367 continue;
368 }
369 }
370
371 block |= (readBits & ((1 << bits) - 1)) << bitsSeen;
372 bitsSeen += bits;
373 nPointer = treeBase;
374 node = cpu->memory.load8(cpu, nPointer, 0);
375 if (bitsSeen == 32) {
376 bitsSeen = 0;
377 cpu->memory.store32(cpu, dPointer, block, 0);
378 dPointer += 4;
379 remaining -= 4;
380 block = 0;
381 }
382 }
383
384 }
385 if (padding) {
386 cpu->memory.store32(cpu, dPointer, block, 0);
387 }
388}
389
390static void _unRl(struct GBA* gba, uint32_t source, uint32_t dest, int width) {
391 struct ARMCore* cpu = gba->cpu;
392 source = source & 0xFFFFFFFC;
393 int remaining = (cpu->memory.load32(cpu, source, 0) & 0xFFFFFF00) >> 8;
394 int padding = (4 - remaining) & 0x3;
395 // We assume the signature byte (0x30) is correct
396 int blockheader;
397 int block;
398 uint32_t sPointer = source + 4;
399 uint32_t dPointer = dest;
400 int halfword = 0;
401 while (remaining > 0) {
402 blockheader = cpu->memory.loadU8(cpu, sPointer++, 0);
403 if (blockheader & 0x80) {
404 // Compressed
405 blockheader &= 0x7F;
406 blockheader += 3;
407 block = cpu->memory.loadU8(cpu, sPointer++, 0);
408 while (blockheader-- && remaining) {
409 --remaining;
410 if (width == 2) {
411 if (dPointer & 1) {
412 halfword |= block << 8;
413 cpu->memory.store16(cpu, dPointer ^ 1, halfword, 0);
414 } else {
415 halfword = block;
416 }
417 } else {
418 cpu->memory.store8(cpu, dPointer, block, 0);
419 }
420 ++dPointer;
421 }
422 } else {
423 // Uncompressed
424 blockheader++;
425 while (blockheader-- && remaining) {
426 --remaining;
427 int byte = cpu->memory.loadU8(cpu, sPointer, 0);
428 ++sPointer;
429 if (width == 2) {
430 if (dPointer & 1) {
431 halfword |= byte << 8;
432 cpu->memory.store16(cpu, dPointer ^ 1, halfword, 0);
433 } else {
434 halfword = byte;
435 }
436 } else {
437 cpu->memory.store8(cpu, dPointer, byte, 0);
438 }
439 ++dPointer;
440 }
441 }
442 }
443 if (width == 2) {
444 if (dPointer & 1) {
445 --padding;
446 ++dPointer;
447 }
448 for (; padding > 0; padding -= 2, dPointer += 2) {
449 cpu->memory.store16(cpu, dPointer, 0, 0);
450 }
451 } else {
452 while (padding--) {
453 cpu->memory.store8(cpu, dPointer, 0, 0);
454 ++dPointer;
455 }
456 }
457}
458
459static void _unFilter(struct GBA* gba, uint32_t source, uint32_t dest, int inwidth, int outwidth) {
460 struct ARMCore* cpu = gba->cpu;
461 source = source & 0xFFFFFFFC;
462 uint32_t header = cpu->memory.load32(cpu, source, 0);
463 int remaining = header >> 8;
464 // We assume the signature nybble (0x8) is correct
465 uint16_t halfword = 0;
466 uint16_t old = 0;
467 source += 4;
468 while (remaining > 0) {
469 if (inwidth == 1) {
470 halfword = cpu->memory.loadU8(cpu, source, 0);
471 } else {
472 halfword = cpu->memory.loadU16(cpu, source, 0);
473 }
474 halfword += old;
475 if (outwidth > inwidth) {
476 GBALog(gba, GBA_LOG_STUB, "Unimplemented Diff8bitUnFilterVram");
477 } else {
478 if (outwidth == 1) {
479 halfword &= 0xFF;
480 cpu->memory.store8(cpu, dest, halfword, 0);
481 } else {
482 cpu->memory.store16(cpu, dest, halfword, 0);
483 }
484 old = halfword;
485 dest += outwidth;
486 }
487 source += inwidth;
488 --remaining;
489 }
490}