all repos — mgba @ e5245b6cbb2e582dc07769e65a8cfb409169db3b

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