all repos — mgba @ 9c92a29b28d1f81224ba28d5fc83a9481eccd5eb

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