all repos — mgba @ 738b3dc44e1d8f7eb3179b0b07c85bae7dc4123b

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
 10static void _unLz77(struct GBAMemory* memory, uint32_t source, uint8_t* dest);
 11
 12static void _RegisterRamReset(struct GBA* gba) {
 13	uint32_t registers = gba->cpu.gprs[0];
 14	(void)(registers);
 15	GBALog(GBA_LOG_STUB, "RegisterRamReset unimplemented");
 16}
 17
 18static void _CpuSet(struct GBA* gba) {
 19	uint32_t source = gba->cpu.gprs[0];
 20	uint32_t dest = gba->cpu.gprs[1];
 21	uint32_t mode = gba->cpu.gprs[2];
 22	int count = mode & 0x000FFFFF;
 23	int fill = mode & 0x01000000;
 24	int wordsize = (mode & 0x04000000) ? 4 : 2;
 25	int i;
 26	if (fill) {
 27		if (wordsize == 4) {
 28			source &= 0xFFFFFFFC;
 29			dest &= 0xFFFFFFFC;
 30			int32_t word = GBALoad32(&gba->memory.d, source);
 31			for (i = 0; i < count; ++i) {
 32				GBAStore32(&gba->memory.d, dest + (i << 2), word);
 33			}
 34		} else {
 35			source &= 0xFFFFFFFE;
 36			dest &= 0xFFFFFFFE;
 37			uint16_t word = GBALoad16(&gba->memory.d, source);
 38			for (i = 0; i < count; ++i) {
 39				GBAStore16(&gba->memory.d, dest + (i << 1), word);
 40			}
 41		}
 42	} else {
 43		if (wordsize == 4) {
 44			source &= 0xFFFFFFFC;
 45			dest &= 0xFFFFFFFC;
 46			for (i = 0; i < count; ++i) {
 47				int32_t word = GBALoad32(&gba->memory.d, source + (i << 2));
 48				GBAStore32(&gba->memory.d, dest + (i << 2), word);
 49			}
 50		} else {
 51			source &= 0xFFFFFFFE;
 52			dest &= 0xFFFFFFFE;
 53			for (i = 0; i < count; ++i) {
 54				uint16_t word = GBALoad16(&gba->memory.d, source + (i << 1));
 55				GBAStore16(&gba->memory.d, dest + (i << 1), word);
 56			}
 57		}
 58	}
 59}
 60
 61static void _FastCpuSet(struct GBA* gba) {
 62	uint32_t source = gba->cpu.gprs[0] & 0xFFFFFFFC;
 63	uint32_t dest = gba->cpu.gprs[1] & 0xFFFFFFFC;
 64	uint32_t mode = gba->cpu.gprs[2];
 65	int count = mode & 0x000FFFFF;
 66	count = ((count + 7) >> 3) << 3;
 67	int i;
 68	if (mode & 0x01000000) {
 69		int32_t word = GBALoad32(&gba->memory.d, source);
 70		for (i = 0; i < count; ++i) {
 71			GBAStore32(&gba->memory.d, dest + (i << 2), word);
 72		}
 73	} else {
 74		for (i = 0; i < count; ++i) {
 75			int32_t word = GBALoad32(&gba->memory.d, source + (i << 2));
 76			GBAStore32(&gba->memory.d, dest + (i << 2), word);
 77		}
 78	}
 79}
 80
 81static void _BgAffineSet(struct GBA* gba) {
 82	int i = gba->cpu.gprs[2];
 83	float ox, oy;
 84	float cx, cy;
 85	float sx, sy;
 86	float theta;
 87	int offset = gba->cpu.gprs[0];
 88	int destination = gba->cpu.gprs[1];
 89	int diff = gba->cpu.gprs[3];
 90	(void)(diff); // Are we supposed to use this?
 91	float a, b, c, d;
 92	float rx, ry;
 93	while (i--) {
 94		// [ sx   0  0 ]   [ cos(theta)  -sin(theta)  0 ]   [ 1  0  cx - ox ]   [ A B rx ]
 95		// [  0  sy  0 ] * [ sin(theta)   cos(theta)  0 ] * [ 0  1  cy - oy ] = [ C D ry ]
 96		// [  0   0  1 ]   [     0            0       1 ]   [ 0  0     1    ]   [ 0 0  1 ]
 97		ox = GBALoad32(&gba->memory.d, offset) / 256.f;
 98		oy = GBALoad32(&gba->memory.d, offset + 4) / 256.f;
 99		cx = GBALoad16(&gba->memory.d, offset + 8);
100		cy = GBALoad16(&gba->memory.d, offset + 10);
101		sx = GBALoad16(&gba->memory.d, offset + 12) / 256.f;
102		sy = GBALoad16(&gba->memory.d, offset + 14) / 256.f;
103		theta = (GBALoadU16(&gba->memory.d, offset + 16) >> 8) / 128.f * M_PI;
104		offset += 20;
105		// Rotation
106		a = d = cosf(theta);
107		b = c = sinf(theta);
108		// Scale
109		a *= sx;
110		b *= -sx;
111		c *= sy;
112		d *= sy;
113		// Translate
114		rx = ox - (a * cx + b * cy);
115		ry = oy - (c * cx + d * cy);
116		GBAStore16(&gba->memory.d, destination, a * 256);
117		GBAStore16(&gba->memory.d, destination + 2, b * 256);
118		GBAStore16(&gba->memory.d, destination + 4, c * 256);
119		GBAStore16(&gba->memory.d, destination + 6, d * 256);
120		GBAStore32(&gba->memory.d, destination + 8, rx * 256);
121		GBAStore32(&gba->memory.d, destination + 12, ry * 256);
122		destination += 16;
123	}
124}
125
126static void _ObjAffineSet(struct GBA* gba) {
127	int i = gba->cpu.gprs[2];
128	float sx, sy;
129	float theta;
130	int offset = gba->cpu.gprs[0];
131	int destination = gba->cpu.gprs[1];
132	int diff = gba->cpu.gprs[3];
133	float a, b, c, d;
134	while (i--) {
135		// [ sx   0 ]   [ cos(theta)  -sin(theta) ]   [ A B ]
136		// [  0  sy ] * [ sin(theta)   cos(theta) ] = [ C D ]
137		sx = GBALoad16(&gba->memory.d, offset) / 256.f;
138		sy = GBALoad16(&gba->memory.d, offset + 2) / 256.f;
139		theta = (GBALoadU16(&gba->memory.d, offset + 4) >> 8) / 128.f * M_PI;
140		offset += 6;
141		// Rotation
142		a = d = cosf(theta);
143		b = c = sinf(theta);
144		// Scale
145		a *= sx;
146		b *= -sx;
147		c *= sy;
148		d *= sy;
149		GBAStore16(&gba->memory.d, destination, a * 256);
150		GBAStore16(&gba->memory.d, destination + diff, b * 256);
151		GBAStore16(&gba->memory.d, destination + diff * 2, c * 256);
152		GBAStore16(&gba->memory.d, destination + diff * 3, d * 256);
153		destination += diff * 4;
154	}
155}
156
157static void _MidiKey2Freq(struct GBA* gba) {
158	uint32_t key = GBALoad32(&gba->memory.d, gba->cpu.gprs[0] + 4);
159	gba->cpu.gprs[0] = key / powf(2, (180.f - gba->cpu.gprs[1] - gba->cpu.gprs[2] / 256.f) / 12.f);
160}
161
162void GBASwi16(struct ARMBoard* board, int immediate) {
163	struct GBA* gba = ((struct GBABoard*) board)->p;
164	switch (immediate) {
165	case 0x1:
166		_RegisterRamReset(gba);
167		break;
168	case 0x2:
169		GBAHalt(gba);
170		break;
171	case 0x05:
172		// VBlankIntrWait
173		gba->cpu.gprs[0] = 1;
174		gba->cpu.gprs[1] = 1;
175		// Fall through:
176	case 0x04:
177		// IntrWait
178		gba->memory.io[REG_IME >> 1] = 1;
179		if (!gba->cpu.gprs[0] && gba->memory.io[REG_IF >> 1] & gba->cpu.gprs[1]) {
180			break;
181		}
182		gba->memory.io[REG_IF >> 1] = 0;
183		ARMRaiseSWI(&gba->cpu);
184		break;
185	case 0x6:
186		{
187			div_t result = div(gba->cpu.gprs[0], gba->cpu.gprs[1]);
188			gba->cpu.gprs[0] = result.quot;
189			gba->cpu.gprs[1] = result.rem;
190			gba->cpu.gprs[3] = abs(result.quot);
191		}
192		break;
193	case 0x7:
194		{
195			div_t result = div(gba->cpu.gprs[1], gba->cpu.gprs[0]);
196			gba->cpu.gprs[0] = result.quot;
197			gba->cpu.gprs[1] = result.rem;
198			gba->cpu.gprs[3] = abs(result.quot);
199		}
200		break;
201	case 0x8:
202		gba->cpu.gprs[0] = sqrt(gba->cpu.gprs[0]);
203		break;
204	case 0xA:
205		gba->cpu.gprs[0] = (atan2f(gba->cpu.gprs[0] / 16384.f, gba->cpu.gprs[1] / 16384.f) / 2 * M_PI) * 0x10000;
206		break;
207	case 0xB:
208		_CpuSet(gba);
209		break;
210	case 0xC:
211		_FastCpuSet(gba);
212		break;
213	case 0xE:
214		_BgAffineSet(gba);
215		break;
216	case 0xF:
217		_ObjAffineSet(gba);
218		break;
219	case 0x11:
220	case 0x12:
221		switch (gba->cpu.gprs[1] >> BASE_OFFSET) {
222			case REGION_WORKING_RAM:
223				_unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.wram)[(gba->cpu.gprs[1] & (SIZE_WORKING_RAM - 1))]);
224				break;
225			case REGION_WORKING_IRAM:
226				_unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.iwram)[(gba->cpu.gprs[1] & (SIZE_WORKING_IRAM - 1))]);
227				break;
228			case REGION_VRAM:
229				_unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->video.vram)[(gba->cpu.gprs[1] & 0x0001FFFF)]);
230				break;
231			default:
232				GBALog(GBA_LOG_WARN, "Bad LZ77 destination");
233				break;
234		}
235		break;
236	case 0x1F:
237		_MidiKey2Freq(gba);
238		break;
239	default:
240		GBALog(GBA_LOG_STUB, "Stub software interrupt: %02x", immediate);
241	}
242}
243
244void GBASwi32(struct ARMBoard* board, int immediate) {
245	GBASwi16(board, immediate >> 16);
246}
247
248static void _unLz77(struct GBAMemory* memory, uint32_t source, uint8_t* dest) {
249	int remaining = (GBALoad32(&memory->d, source) & 0xFFFFFF00) >> 8;
250	// We assume the signature byte (0x10) is correct
251	int blockheader;
252	uint32_t sPointer = source + 4;
253	uint8_t* dPointer = dest;
254	int blocksRemaining = 0;
255	int block;
256	uint8_t* disp;
257	int bytes;
258	while (remaining > 0) {
259		if (blocksRemaining) {
260			if (blockheader & 0x80) {
261				// Compressed
262				block = GBALoadU8(&memory->d, sPointer) | (GBALoadU8(&memory->d, sPointer + 1) << 8);
263				sPointer += 2;
264				disp = dPointer - (((block & 0x000F) << 8) | ((block & 0xFF00) >> 8)) - 1;
265				bytes = ((block & 0x00F0) >> 4) + 3;
266				while (bytes-- && remaining) {
267					--remaining;
268					*dPointer = *disp;
269					++disp;
270					++dPointer;
271				}
272			} else {
273				// Uncompressed
274				*dPointer = GBALoadU8(&memory->d, sPointer++);
275				++dPointer;
276				--remaining;
277			}
278			blockheader <<= 1;
279			--blocksRemaining;
280		} else {
281			blockheader = GBALoadU8(&memory->d, sPointer++);
282			blocksRemaining = 8;
283		}
284	}
285}