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, 0);
31 for (i = 0; i < count; ++i) {
32 GBAStore32(&gba->memory.d, dest + (i << 2), word, 0);
33 }
34 } else {
35 source &= 0xFFFFFFFE;
36 dest &= 0xFFFFFFFE;
37 uint16_t word = GBALoad16(&gba->memory.d, source, 0);
38 for (i = 0; i < count; ++i) {
39 GBAStore16(&gba->memory.d, dest + (i << 1), word, 0);
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), 0);
48 GBAStore32(&gba->memory.d, dest + (i << 2), word, 0);
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), 0);
55 GBAStore16(&gba->memory.d, dest + (i << 1), word, 0);
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, 0);
70 for (i = 0; i < count; ++i) {
71 GBAStore32(&gba->memory.d, dest + (i << 2), word, 0);
72 }
73 } else {
74 for (i = 0; i < count; ++i) {
75 int32_t word = GBALoad32(&gba->memory.d, source + (i << 2), 0);
76 GBAStore32(&gba->memory.d, dest + (i << 2), word, 0);
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, 0) / 256.f;
98 oy = GBALoad32(&gba->memory.d, offset + 4, 0) / 256.f;
99 cx = GBALoad16(&gba->memory.d, offset + 8, 0);
100 cy = GBALoad16(&gba->memory.d, offset + 10, 0);
101 sx = GBALoad16(&gba->memory.d, offset + 12, 0) / 256.f;
102 sy = GBALoad16(&gba->memory.d, offset + 14, 0) / 256.f;
103 theta = (GBALoadU16(&gba->memory.d, offset + 16, 0) >> 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, 0);
117 GBAStore16(&gba->memory.d, destination + 2, b * 256, 0);
118 GBAStore16(&gba->memory.d, destination + 4, c * 256, 0);
119 GBAStore16(&gba->memory.d, destination + 6, d * 256, 0);
120 GBAStore32(&gba->memory.d, destination + 8, rx * 256, 0);
121 GBAStore32(&gba->memory.d, destination + 12, ry * 256, 0);
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, 0) / 256.f;
138 sy = GBALoad16(&gba->memory.d, offset + 2, 0) / 256.f;
139 theta = (GBALoadU16(&gba->memory.d, offset + 4, 0) >> 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, 0);
150 GBAStore16(&gba->memory.d, destination + diff, b * 256, 0);
151 GBAStore16(&gba->memory.d, destination + diff * 2, c * 256, 0);
152 GBAStore16(&gba->memory.d, destination + diff * 3, d * 256, 0);
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, 0);
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.renderer->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, 0) & 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, 0) | (GBALoadU8(&memory->d, sPointer + 1, 0) << 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++, 0);
275 ++dPointer;
276 --remaining;
277 }
278 blockheader <<= 1;
279 --blocksRemaining;
280 } else {
281 blockheader = GBALoadU8(&memory->d, sPointer++, 0);
282 blocksRemaining = 8;
283 }
284 }
285}