all repos — mgba @ c5d243fca282cf11d7f2c38010fce18361babdc2

mGBA Game Boy Advance Emulator

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