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