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