all repos — mgba @ e263467f139a17c5313242711ee2a39c8248cd6d

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);
 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		}
176		switch (cpu->gprs[1] >> BASE_OFFSET) {
177			default:
178				GBALog(gba, GBA_LOG_GAME_ERROR, "Bad LZ77 destination");
179			case REGION_WORKING_RAM:
180			case REGION_WORKING_IRAM:
181			case REGION_VRAM:
182				_unLz77(gba, cpu->gprs[0], cpu->gprs[1], immediate == 0x11 ? 1 : 2);
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		}
190		switch (cpu->gprs[1] >> BASE_OFFSET) {
191			default:
192				GBALog(gba, GBA_LOG_GAME_ERROR, "Bad Huffman destination");
193			case REGION_WORKING_RAM:
194			case REGION_WORKING_IRAM:
195			case REGION_VRAM:
196				_unHuffman(gba, cpu->gprs[0], cpu->gprs[1]);
197				break;
198		}
199		break;
200	case 0x14:
201	case 0x15:
202		if (cpu->gprs[0] < BASE_WORKING_RAM) {
203			GBALog(gba, GBA_LOG_GAME_ERROR, "Bad RL source");
204		}
205		switch (cpu->gprs[1] >> BASE_OFFSET) {
206			default:
207				GBALog(gba, GBA_LOG_GAME_ERROR, "Bad RL destination");
208			case REGION_WORKING_RAM:
209			case REGION_WORKING_IRAM:
210			case REGION_VRAM:
211				_unRl(gba, cpu->gprs[0], cpu->gprs[1], immediate == 0x14 ? 1 : 2);
212				break;
213		}
214		break;
215	case 0x1F:
216		_MidiKey2Freq(gba);
217		break;
218	default:
219		GBALog(gba, GBA_LOG_STUB, "Stub software interrupt: %02X", immediate);
220	}
221}
222
223void GBASwi32(struct ARMCore* cpu, int immediate) {
224	GBASwi16(cpu, immediate >> 16);
225}
226
227uint32_t GBAChecksum(uint32_t* memory, size_t size) {
228	size_t i;
229	uint32_t sum = 0;
230	for (i = 0; i < size; i += 4) {
231		sum += memory[i >> 2];
232	}
233	return sum;
234}
235
236static void _unLz77(struct GBA* gba, uint32_t source, uint32_t dest, int width) {
237	struct ARMCore* cpu = gba->cpu;
238	int remaining = (cpu->memory.load32(cpu, source, 0) & 0xFFFFFF00) >> 8;
239	// We assume the signature byte (0x10) is correct
240	int blockheader = 0; // Some compilers warn if this isn't set, even though it's trivially provably always set
241	uint32_t sPointer = source + 4;
242	uint32_t dPointer = dest;
243	int blocksRemaining = 0;
244	int block;
245	uint32_t disp;
246	int bytes;
247	int byte;
248	int halfword;
249	while (remaining > 0) {
250		if (blocksRemaining) {
251			if (blockheader & 0x80) {
252				// Compressed
253				block = cpu->memory.loadU8(cpu, sPointer, 0) | (cpu->memory.loadU8(cpu, sPointer + 1, 0) << 8);
254				sPointer += 2;
255				disp = dPointer - (((block & 0x000F) << 8) | ((block & 0xFF00) >> 8)) - 1;
256				bytes = ((block & 0x00F0) >> 4) + 3;
257				while (bytes-- && remaining) {
258					--remaining;
259					byte = cpu->memory.loadU8(cpu, disp, 0);
260					++disp;
261					if (width == 2) {
262						if (dPointer & 1) {
263							halfword |= byte << 8;
264							cpu->memory.store16(cpu, dPointer ^ 1, halfword, 0);
265						} else {
266							halfword = byte;
267						}
268					} else {
269						cpu->memory.store8(cpu, dPointer, byte, 0);
270					}
271					++dPointer;
272				}
273			} else {
274				// Uncompressed
275				byte = cpu->memory.loadU8(cpu, sPointer, 0);
276				++sPointer;
277				if (width == 2) {
278					if (dPointer & 1) {
279						halfword |= byte << 8;
280						cpu->memory.store16(cpu, dPointer ^ 1, halfword, 0);
281					} else {
282						halfword = byte;
283					}
284				} else {
285					cpu->memory.store8(cpu, dPointer, byte, 0);
286				}
287				++dPointer;
288				--remaining;
289			}
290			blockheader <<= 1;
291			--blocksRemaining;
292		} else {
293			blockheader = cpu->memory.loadU8(cpu, sPointer, 0);
294			++sPointer;
295			blocksRemaining = 8;
296		}
297	}
298}
299
300DECL_BITFIELD(HuffmanNode, uint8_t);
301DECL_BITS(HuffmanNode, Offset, 0, 6);
302DECL_BIT(HuffmanNode, RTerm, 6);
303DECL_BIT(HuffmanNode, LTerm, 7);
304
305static void _unHuffman(struct GBA* gba, uint32_t source, uint32_t dest) {
306	struct ARMCore* cpu = gba->cpu;
307	source = source & 0xFFFFFFFC;
308	uint32_t header = cpu->memory.load32(cpu, source, 0);
309	int remaining = header >> 8;
310	int bits = header & 0xF;
311	if (32 % bits) {
312		GBALog(gba, GBA_LOG_STUB, "Unimplemented unaligned Huffman");
313		return;
314	}
315	int padding = (4 - remaining) & 0x3;
316	remaining &= 0xFFFFFFFC;
317	// We assume the signature byte (0x20) is correct
318	int treesize = (cpu->memory.loadU8(cpu, source + 4, 0) << 1) + 1;
319	int block = 0;
320	uint32_t treeBase = source + 5;
321	uint32_t sPointer = source + 5 + treesize;
322	uint32_t dPointer = dest;
323	uint32_t nPointer = treeBase;
324	HuffmanNode node;
325	int bitsRemaining;
326	int readBits;
327	int bitsSeen = 0;
328	node = cpu->memory.load8(cpu, nPointer, 0);
329	while (remaining > 0) {
330		uint32_t bitstream = cpu->memory.load32(cpu, sPointer, 0);
331		sPointer += 4;
332		for (bitsRemaining = 32; bitsRemaining > 0 && remaining > 0; --bitsRemaining, bitstream <<= 1) {
333			uint32_t next = (nPointer & ~1) + HuffmanNodeGetOffset(node) * 2 + 2;
334			if (bitstream & 0x80000000) {
335				// Go right
336				if (HuffmanNodeIsRTerm(node)) {
337					readBits = cpu->memory.load8(cpu, next + 1, 0);
338				} else {
339					nPointer = next + 1;
340					node = cpu->memory.load8(cpu, nPointer, 0);
341					continue;
342				}
343			} else {
344				// Go left
345				if (HuffmanNodeIsLTerm(node)) {
346					readBits = cpu->memory.load8(cpu, next, 0);
347				} else {
348					nPointer = next;
349					node = cpu->memory.load8(cpu, nPointer, 0);
350					continue;
351				}
352			}
353
354			block |= (readBits & ((1 << bits) - 1)) << bitsSeen;
355			bitsSeen += bits;
356			nPointer = treeBase;
357			node = cpu->memory.load8(cpu, nPointer, 0);
358			if (bitsSeen == 32) {
359				bitsSeen = 0;
360				cpu->memory.store32(cpu, dPointer, block, 0);
361				dPointer += 4;
362				remaining -= 4;
363				block = 0;
364			}
365		}
366
367	}
368	if (padding) {
369		cpu->memory.store32(cpu, dPointer, block, 0);
370	}
371}
372
373static void _unRl(struct GBA* gba, uint32_t source, uint32_t dest, int width) {
374	struct ARMCore* cpu = gba->cpu;
375	source = source & 0xFFFFFFFC;
376	int remaining = (cpu->memory.load32(cpu, source, 0) & 0xFFFFFF00) >> 8;
377	int padding = (4 - remaining) & 0x3;
378	// We assume the signature byte (0x30) is correct
379	int blockheader;
380	int block;
381	uint32_t sPointer = source + 4;
382	uint32_t dPointer = dest;
383	int halfword = 0;
384	while (remaining > 0) {
385		blockheader = cpu->memory.loadU8(cpu, sPointer++, 0);
386		if (blockheader & 0x80) {
387			// Compressed
388			blockheader &= 0x7F;
389			blockheader += 3;
390			block = cpu->memory.loadU8(cpu, sPointer++, 0);
391			while (blockheader-- && remaining) {
392				--remaining;
393				if (width == 2) {
394					if (dPointer & 1) {
395						halfword |= block << 8;
396						cpu->memory.store16(cpu, dPointer ^ 1, halfword, 0);
397					} else {
398						halfword = block;
399					}
400				} else {
401					cpu->memory.store8(cpu, dPointer, block, 0);
402				}
403				++dPointer;
404			}
405		} else {
406			// Uncompressed
407			blockheader++;
408			while (blockheader-- && remaining) {
409				--remaining;
410				int byte = cpu->memory.loadU8(cpu, sPointer, 0);
411				++sPointer;
412				if (width == 2) {
413					if (dPointer & 1) {
414						halfword |= byte << 8;
415						cpu->memory.store16(cpu, dPointer ^ 1, halfword, 0);
416					} else {
417						halfword = byte;
418					}
419				} else {
420					cpu->memory.store8(cpu, dPointer, byte, 0);
421				}
422				++dPointer;
423			}
424		}
425	}
426	if (width == 2) {
427		if (dPointer & 1) {
428			--padding;
429			++dPointer;
430		}
431		for (; padding > 0; padding -= 2, dPointer += 2) {
432			cpu->memory.store16(cpu, dPointer, 0, 0);
433		}
434	} else {
435		while (padding--) {
436			cpu->memory.store8(cpu, dPointer, 0, 0);
437			++dPointer;
438		}
439	}
440}