mgba/src/gba/gba-bios.c

341 lines
9.5 KiB
C
Raw Normal View History

2013-04-14 20:21:21 +00:00
#include "gba-bios.h"
#include "gba.h"
2013-04-26 09:00:59 +00:00
#include "gba-io.h"
2013-04-14 20:36:32 +00:00
#include "gba-memory.h"
2013-04-20 10:01:50 +00:00
#include <math.h>
2013-04-26 10:08:52 +00:00
#include <stdlib.h>
2013-04-20 10:01:50 +00:00
2013-04-20 09:52:10 +00:00
static void _unLz77(struct GBAMemory* memory, uint32_t source, uint8_t* dest);
2013-05-12 01:22:23 +00:00
static void _unRl(struct GBAMemory* memory, uint32_t source, uint8_t* dest);
2013-04-20 09:52:10 +00:00
2013-04-28 05:54:41 +00:00
static void _RegisterRamReset(struct GBA* gba) {
uint32_t registers = gba->cpu.gprs[0];
(void)(registers);
GBALog(GBA_LOG_STUB, "RegisterRamReset unimplemented");
}
2013-04-14 20:36:32 +00:00
static void _CpuSet(struct GBA* gba) {
uint32_t source = gba->cpu.gprs[0];
uint32_t dest = gba->cpu.gprs[1];
uint32_t mode = gba->cpu.gprs[2];
int count = mode & 0x000FFFFF;
int fill = mode & 0x01000000;
int wordsize = (mode & 0x04000000) ? 4 : 2;
int i;
if (fill) {
if (wordsize == 4) {
source &= 0xFFFFFFFC;
dest &= 0xFFFFFFFC;
2013-05-05 06:57:12 +00:00
int32_t word = GBALoad32(&gba->memory.d, source, 0);
2013-04-14 20:36:32 +00:00
for (i = 0; i < count; ++i) {
2013-05-05 06:57:12 +00:00
GBAStore32(&gba->memory.d, dest + (i << 2), word, 0);
2013-04-14 20:36:32 +00:00
}
} else {
source &= 0xFFFFFFFE;
dest &= 0xFFFFFFFE;
2013-05-05 06:57:12 +00:00
uint16_t word = GBALoad16(&gba->memory.d, source, 0);
2013-04-14 20:36:32 +00:00
for (i = 0; i < count; ++i) {
2013-05-05 06:57:12 +00:00
GBAStore16(&gba->memory.d, dest + (i << 1), word, 0);
2013-04-14 20:36:32 +00:00
}
}
} else {
if (wordsize == 4) {
source &= 0xFFFFFFFC;
dest &= 0xFFFFFFFC;
for (i = 0; i < count; ++i) {
2013-05-05 06:57:12 +00:00
int32_t word = GBALoad32(&gba->memory.d, source + (i << 2), 0);
GBAStore32(&gba->memory.d, dest + (i << 2), word, 0);
2013-04-14 20:36:32 +00:00
}
} else {
source &= 0xFFFFFFFE;
dest &= 0xFFFFFFFE;
for (i = 0; i < count; ++i) {
2013-05-05 06:57:12 +00:00
uint16_t word = GBALoad16(&gba->memory.d, source + (i << 1), 0);
GBAStore16(&gba->memory.d, dest + (i << 1), word, 0);
2013-04-14 20:36:32 +00:00
}
}
}
}
2013-04-14 20:21:21 +00:00
2013-04-17 06:52:53 +00:00
static void _FastCpuSet(struct GBA* gba) {
uint32_t source = gba->cpu.gprs[0] & 0xFFFFFFFC;
uint32_t dest = gba->cpu.gprs[1] & 0xFFFFFFFC;
uint32_t mode = gba->cpu.gprs[2];
int count = mode & 0x000FFFFF;
count = ((count + 7) >> 3) << 3;
int i;
if (mode & 0x01000000) {
2013-05-05 06:57:12 +00:00
int32_t word = GBALoad32(&gba->memory.d, source, 0);
2013-04-17 06:52:53 +00:00
for (i = 0; i < count; ++i) {
2013-05-05 06:57:12 +00:00
GBAStore32(&gba->memory.d, dest + (i << 2), word, 0);
2013-04-17 06:52:53 +00:00
}
} else {
for (i = 0; i < count; ++i) {
2013-05-05 06:57:12 +00:00
int32_t word = GBALoad32(&gba->memory.d, source + (i << 2), 0);
GBAStore32(&gba->memory.d, dest + (i << 2), word, 0);
2013-04-17 06:52:53 +00:00
}
}
}
2013-05-04 08:20:42 +00:00
static void _BgAffineSet(struct GBA* gba) {
int i = gba->cpu.gprs[2];
float ox, oy;
float cx, cy;
float sx, sy;
float theta;
int offset = gba->cpu.gprs[0];
int destination = gba->cpu.gprs[1];
int diff = gba->cpu.gprs[3];
(void)(diff); // Are we supposed to use this?
float a, b, c, d;
float rx, ry;
while (i--) {
// [ sx 0 0 ] [ cos(theta) -sin(theta) 0 ] [ 1 0 cx - ox ] [ A B rx ]
// [ 0 sy 0 ] * [ sin(theta) cos(theta) 0 ] * [ 0 1 cy - oy ] = [ C D ry ]
// [ 0 0 1 ] [ 0 0 1 ] [ 0 0 1 ] [ 0 0 1 ]
2013-05-05 06:57:12 +00:00
ox = GBALoad32(&gba->memory.d, offset, 0) / 256.f;
oy = GBALoad32(&gba->memory.d, offset + 4, 0) / 256.f;
cx = GBALoad16(&gba->memory.d, offset + 8, 0);
cy = GBALoad16(&gba->memory.d, offset + 10, 0);
sx = GBALoad16(&gba->memory.d, offset + 12, 0) / 256.f;
sy = GBALoad16(&gba->memory.d, offset + 14, 0) / 256.f;
theta = (GBALoadU16(&gba->memory.d, offset + 16, 0) >> 8) / 128.f * M_PI;
2013-05-04 08:20:42 +00:00
offset += 20;
// Rotation
a = d = cosf(theta);
b = c = sinf(theta);
// Scale
a *= sx;
b *= -sx;
c *= sy;
d *= sy;
// Translate
rx = ox - (a * cx + b * cy);
ry = oy - (c * cx + d * cy);
2013-05-05 06:57:12 +00:00
GBAStore16(&gba->memory.d, destination, a * 256, 0);
GBAStore16(&gba->memory.d, destination + 2, b * 256, 0);
GBAStore16(&gba->memory.d, destination + 4, c * 256, 0);
GBAStore16(&gba->memory.d, destination + 6, d * 256, 0);
GBAStore32(&gba->memory.d, destination + 8, rx * 256, 0);
GBAStore32(&gba->memory.d, destination + 12, ry * 256, 0);
2013-05-04 08:20:42 +00:00
destination += 16;
}
}
static void _ObjAffineSet(struct GBA* gba) {
int i = gba->cpu.gprs[2];
float sx, sy;
float theta;
int offset = gba->cpu.gprs[0];
int destination = gba->cpu.gprs[1];
int diff = gba->cpu.gprs[3];
float a, b, c, d;
while (i--) {
// [ sx 0 ] [ cos(theta) -sin(theta) ] [ A B ]
// [ 0 sy ] * [ sin(theta) cos(theta) ] = [ C D ]
2013-05-05 06:57:12 +00:00
sx = GBALoad16(&gba->memory.d, offset, 0) / 256.f;
sy = GBALoad16(&gba->memory.d, offset + 2, 0) / 256.f;
theta = (GBALoadU16(&gba->memory.d, offset + 4, 0) >> 8) / 128.f * M_PI;
offset += 6;
// Rotation
a = d = cosf(theta);
b = c = sinf(theta);
// Scale
a *= sx;
b *= -sx;
c *= sy;
d *= sy;
2013-05-05 06:57:12 +00:00
GBAStore16(&gba->memory.d, destination, a * 256, 0);
GBAStore16(&gba->memory.d, destination + diff, b * 256, 0);
GBAStore16(&gba->memory.d, destination + diff * 2, c * 256, 0);
GBAStore16(&gba->memory.d, destination + diff * 3, d * 256, 0);
destination += diff * 4;
}
}
2013-04-20 10:01:50 +00:00
static void _MidiKey2Freq(struct GBA* gba) {
2013-05-05 06:57:12 +00:00
uint32_t key = GBALoad32(&gba->memory.d, gba->cpu.gprs[0] + 4, 0);
2013-04-20 21:46:53 +00:00
gba->cpu.gprs[0] = key / powf(2, (180.f - gba->cpu.gprs[1] - gba->cpu.gprs[2] / 256.f) / 12.f);
2013-04-20 10:01:50 +00:00
}
2013-04-14 20:21:21 +00:00
void GBASwi16(struct ARMBoard* board, int immediate) {
2013-04-14 20:36:32 +00:00
struct GBA* gba = ((struct GBABoard*) board)->p;
2013-04-14 20:21:21 +00:00
switch (immediate) {
2013-04-28 05:54:41 +00:00
case 0x1:
_RegisterRamReset(gba);
break;
2013-04-17 06:14:16 +00:00
case 0x2:
GBAHalt(gba);
break;
2013-04-26 09:00:59 +00:00
case 0x05:
// VBlankIntrWait
gba->cpu.gprs[0] = 1;
gba->cpu.gprs[1] = 1;
// Fall through:
case 0x04:
// IntrWait
gba->memory.io[REG_IME >> 1] = 1;
if (!gba->cpu.gprs[0] && gba->memory.io[REG_IF >> 1] & gba->cpu.gprs[1]) {
break;
}
gba->memory.io[REG_IF >> 1] = 0;
2013-04-26 09:00:59 +00:00
ARMRaiseSWI(&gba->cpu);
break;
2013-04-26 09:05:54 +00:00
case 0x6:
{
div_t result = div(gba->cpu.gprs[0], gba->cpu.gprs[1]);
gba->cpu.gprs[0] = result.quot;
gba->cpu.gprs[1] = result.rem;
2013-05-01 03:54:09 +00:00
gba->cpu.gprs[3] = abs(result.quot);
2013-04-26 09:05:54 +00:00
}
break;
2013-04-28 02:58:21 +00:00
case 0x7:
{
div_t result = div(gba->cpu.gprs[1], gba->cpu.gprs[0]);
gba->cpu.gprs[0] = result.quot;
gba->cpu.gprs[1] = result.rem;
2013-05-01 03:54:09 +00:00
gba->cpu.gprs[3] = abs(result.quot);
2013-04-28 02:58:21 +00:00
}
break;
2013-05-01 07:27:31 +00:00
case 0x8:
gba->cpu.gprs[0] = sqrt(gba->cpu.gprs[0]);
break;
2013-05-04 22:50:42 +00:00
case 0xA:
gba->cpu.gprs[0] = (atan2f(gba->cpu.gprs[0] / 16384.f, gba->cpu.gprs[1] / 16384.f) / 2 * M_PI) * 0x10000;
break;
2013-04-14 20:36:32 +00:00
case 0xB:
_CpuSet(gba);
break;
2013-04-17 06:52:53 +00:00
case 0xC:
_FastCpuSet(gba);
break;
2013-05-04 08:20:42 +00:00
case 0xE:
_BgAffineSet(gba);
break;
case 0xF:
_ObjAffineSet(gba);
break;
2013-04-20 09:52:10 +00:00
case 0x11:
case 0x12:
2013-04-23 05:55:49 +00:00
switch (gba->cpu.gprs[1] >> BASE_OFFSET) {
case REGION_WORKING_RAM:
_unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.wram)[(gba->cpu.gprs[1] & (SIZE_WORKING_RAM - 1))]);
break;
2013-05-01 07:24:28 +00:00
case REGION_WORKING_IRAM:
_unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.iwram)[(gba->cpu.gprs[1] & (SIZE_WORKING_IRAM - 1))]);
break;
2013-04-23 05:55:49 +00:00
case REGION_VRAM:
2013-05-07 10:36:15 +00:00
_unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->video.renderer->vram)[(gba->cpu.gprs[1] & 0x0001FFFF)]);
2013-04-23 05:55:49 +00:00
break;
default:
GBALog(GBA_LOG_WARN, "Bad LZ77 destination");
break;
}
2013-04-20 09:52:10 +00:00
break;
2013-05-12 01:22:23 +00:00
case 0x14:
case 0x15:
switch (gba->cpu.gprs[1] >> BASE_OFFSET) {
case REGION_WORKING_RAM:
_unRl(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.wram)[(gba->cpu.gprs[1] & (SIZE_WORKING_RAM - 1))]);
break;
case REGION_WORKING_IRAM:
_unRl(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->memory.iwram)[(gba->cpu.gprs[1] & (SIZE_WORKING_IRAM - 1))]);
break;
case REGION_VRAM:
_unRl(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->video.renderer->vram)[(gba->cpu.gprs[1] & 0x0001FFFF)]);
break;
default:
GBALog(GBA_LOG_WARN, "Bad RL destination");
break;
}
break;
2013-04-20 10:01:50 +00:00
case 0x1F:
_MidiKey2Freq(gba);
break;
2013-04-14 20:21:21 +00:00
default:
GBALog(GBA_LOG_STUB, "Stub software interrupt: %02x", immediate);
}
}
void GBASwi32(struct ARMBoard* board, int immediate) {
2013-04-27 09:56:34 +00:00
GBASwi16(board, immediate >> 16);
2013-04-14 20:21:21 +00:00
}
2013-04-20 09:52:10 +00:00
static void _unLz77(struct GBAMemory* memory, uint32_t source, uint8_t* dest) {
2013-05-05 06:57:12 +00:00
int remaining = (GBALoad32(&memory->d, source, 0) & 0xFFFFFF00) >> 8;
2013-04-20 09:52:10 +00:00
// We assume the signature byte (0x10) is correct
int blockheader;
uint32_t sPointer = source + 4;
uint8_t* dPointer = dest;
int blocksRemaining = 0;
int block;
uint8_t* disp;
int bytes;
while (remaining > 0) {
if (blocksRemaining) {
if (blockheader & 0x80) {
// Compressed
2013-05-05 06:57:12 +00:00
block = GBALoadU8(&memory->d, sPointer, 0) | (GBALoadU8(&memory->d, sPointer + 1, 0) << 8);
2013-04-20 09:52:10 +00:00
sPointer += 2;
disp = dPointer - (((block & 0x000F) << 8) | ((block & 0xFF00) >> 8)) - 1;
bytes = ((block & 0x00F0) >> 4) + 3;
while (bytes-- && remaining) {
--remaining;
*dPointer = *disp;
++disp;
++dPointer;
}
} else {
// Uncompressed
2013-05-05 06:57:12 +00:00
*dPointer = GBALoadU8(&memory->d, sPointer++, 0);
2013-04-20 09:52:10 +00:00
++dPointer;
--remaining;
}
blockheader <<= 1;
--blocksRemaining;
} else {
2013-05-05 06:57:12 +00:00
blockheader = GBALoadU8(&memory->d, sPointer++, 0);
2013-04-20 09:52:10 +00:00
blocksRemaining = 8;
}
}
}
2013-05-12 01:22:23 +00:00
static void _unRl(struct GBAMemory* memory, uint32_t source, uint8_t* dest) {
source = source & 0xFFFFFFFC;
int remaining = (GBALoad32(&memory->d, source, 0) & 0xFFFFFF00) >> 8;
int padding = (4 - remaining) & 0x3;
// We assume the signature byte (0x30) is correct
int blockheader;
int block;
uint32_t sPointer = source + 4;
uint8_t* dPointer = dest;
while (remaining > 0) {
blockheader = GBALoadU8(&memory->d, sPointer++, 0);
if (blockheader & 0x80) {
// Compressed
blockheader &= 0x7F;
blockheader += 3;
block = GBALoadU8(&memory->d, sPointer++, 0);
while (blockheader-- && remaining) {
--remaining;
*dPointer = block;
++dPointer;
}
} else {
// Uncompressed
blockheader++;
while (blockheader-- && remaining) {
--remaining;
*dPointer = GBALoadU8(&memory->d, sPointer++, 0);
++dPointer;
}
}
}
while (padding--) {
*dPointer = 0;
++dPointer;
}
}