all repos — mgba @ 9da3e5e1f91bc7fbb429e6877153ffa9c22727ab

mGBA Game Boy Advance Emulator

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}