all repos — mgba @ fb2ddd6c32a16d86535989fea8743aea01e30504

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 _ObjAffineSet(struct GBA* gba) {
 82	int i = gba->cpu.gprs[2];
 83	float sx, sy;
 84	float theta;
 85	int offset = gba->cpu.gprs[0];
 86	int destination = gba->cpu.gprs[1];
 87	int diff = gba->cpu.gprs[3];
 88	float a, b, c, d;
 89	while (i--) {
 90		// [ sx   0 ]   [ cos(theta)  -sin(theta) ]   [ A B ]
 91		// [  0  sy ] * [ sin(theta)   cos(theta) ] = [ C D ]
 92		sx = GBALoad16(&gba->memory.d, offset) / 256.f;
 93		sy = GBALoad16(&gba->memory.d, offset + 2) / 256.f;
 94		theta = (GBALoadU16(&gba->memory.d, offset + 4) >> 8) / 128.f * M_PI;
 95		offset += 6;
 96		// Rotation
 97		a = d = cosf(theta);
 98		b = c = sinf(theta);
 99		// Scale
100		a *= sx;
101		b *= -sx;
102		c *= sy;
103		d *= sy;
104		GBAStore16(&gba->memory.d, destination, a * 256);
105		GBAStore16(&gba->memory.d, destination + diff, b * 256);
106		GBAStore16(&gba->memory.d, destination + diff * 2, c * 256);
107		GBAStore16(&gba->memory.d, destination + diff * 3, d * 256);
108		destination += diff * 4;
109	}
110}
111
112static void _MidiKey2Freq(struct GBA* gba) {
113	uint32_t key = GBALoad32(&gba->memory.d, gba->cpu.gprs[0] + 4);
114	gba->cpu.gprs[0] = key / powf(2, (180.f - gba->cpu.gprs[1] - gba->cpu.gprs[2] / 256.f) / 12.f);
115}
116
117void GBASwi16(struct ARMBoard* board, int immediate) {
118	struct GBA* gba = ((struct GBABoard*) board)->p;
119	switch (immediate) {
120	case 0x1:
121		_RegisterRamReset(gba);
122		break;
123	case 0x2:
124		GBAHalt(gba);
125		break;
126	case 0x05:
127		// VBlankIntrWait
128		gba->cpu.gprs[0] = 1;
129		gba->cpu.gprs[1] = 1;
130		// Fall through:
131	case 0x04:
132		// IntrWait
133		gba->memory.io[REG_IME >> 1] = 1;
134		if (!gba->cpu.gprs[0] && gba->memory.io[REG_IF >> 1] & gba->cpu.gprs[1]) {
135			break;
136		}
137		gba->memory.io[REG_IF >> 1] = 0;
138		ARMRaiseSWI(&gba->cpu);
139		break;
140	case 0x6:
141		{
142			div_t result = div(gba->cpu.gprs[0], gba->cpu.gprs[1]);
143			gba->cpu.gprs[0] = result.quot;
144			gba->cpu.gprs[1] = result.rem;
145			gba->cpu.gprs[3] = abs(result.quot);
146		}
147		break;
148	case 0x7:
149		{
150			div_t result = div(gba->cpu.gprs[1], gba->cpu.gprs[0]);
151			gba->cpu.gprs[0] = result.quot;
152			gba->cpu.gprs[1] = result.rem;
153			gba->cpu.gprs[3] = abs(result.quot);
154		}
155		break;
156	case 0x8:
157		gba->cpu.gprs[0] = sqrt(gba->cpu.gprs[0]);
158		break;
159	case 0xB:
160		_CpuSet(gba);
161		break;
162	case 0xC:
163		_FastCpuSet(gba);
164		break;
165	case 0xF:
166		_ObjAffineSet(gba);
167		break;
168	case 0x11:
169	case 0x12:
170		switch (gba->cpu.gprs[1] >> BASE_OFFSET) {
171			case REGION_WORKING_RAM:
172				_unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.wram)[(gba->cpu.gprs[1] & (SIZE_WORKING_RAM - 1))]);
173				break;
174			case REGION_WORKING_IRAM:
175				_unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.iwram)[(gba->cpu.gprs[1] & (SIZE_WORKING_IRAM - 1))]);
176				break;
177			case REGION_VRAM:
178				_unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->video.vram)[(gba->cpu.gprs[1] & 0x0001FFFF)]);
179				break;
180			default:
181				GBALog(GBA_LOG_WARN, "Bad LZ77 destination");
182				break;
183		}
184		break;
185	case 0x1F:
186		_MidiKey2Freq(gba);
187		break;
188	default:
189		GBALog(GBA_LOG_STUB, "Stub software interrupt: %02x", immediate);
190	}
191}
192
193void GBASwi32(struct ARMBoard* board, int immediate) {
194	GBASwi16(board, immediate >> 16);
195}
196
197static void _unLz77(struct GBAMemory* memory, uint32_t source, uint8_t* dest) {
198	int remaining = (GBALoad32(&memory->d, source) & 0xFFFFFF00) >> 8;
199	// We assume the signature byte (0x10) is correct
200	int blockheader;
201	uint32_t sPointer = source + 4;
202	uint8_t* dPointer = dest;
203	int blocksRemaining = 0;
204	int block;
205	uint8_t* disp;
206	int bytes;
207	while (remaining > 0) {
208		if (blocksRemaining) {
209			if (blockheader & 0x80) {
210				// Compressed
211				block = GBALoadU8(&memory->d, sPointer) | (GBALoadU8(&memory->d, sPointer + 1) << 8);
212				sPointer += 2;
213				disp = dPointer - (((block & 0x000F) << 8) | ((block & 0xFF00) >> 8)) - 1;
214				bytes = ((block & 0x00F0) >> 4) + 3;
215				while (bytes-- && remaining) {
216					--remaining;
217					*dPointer = *disp;
218					++disp;
219					++dPointer;
220				}
221			} else {
222				// Uncompressed
223				*dPointer = GBALoadU8(&memory->d, sPointer++);
224				++dPointer;
225				--remaining;
226			}
227			blockheader <<= 1;
228			--blocksRemaining;
229		} else {
230			blockheader = GBALoadU8(&memory->d, sPointer++);
231			blocksRemaining = 8;
232		}
233	}
234}