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-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;
|
|
|
|
int32_t word = GBALoad32(&gba->memory.d, source);
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
|
|
GBAStore32(&gba->memory.d, dest + (i << 2), word);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
source &= 0xFFFFFFFE;
|
|
|
|
dest &= 0xFFFFFFFE;
|
|
|
|
uint16_t word = GBALoad16(&gba->memory.d, source);
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
|
|
GBAStore16(&gba->memory.d, dest + (i << 1), word);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (wordsize == 4) {
|
|
|
|
source &= 0xFFFFFFFC;
|
|
|
|
dest &= 0xFFFFFFFC;
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
|
|
int32_t word = GBALoad32(&gba->memory.d, source + (i << 2));
|
|
|
|
GBAStore32(&gba->memory.d, dest + (i << 2), word);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
source &= 0xFFFFFFFE;
|
|
|
|
dest &= 0xFFFFFFFE;
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
|
|
uint16_t word = GBALoad16(&gba->memory.d, source + (i << 1));
|
|
|
|
GBAStore16(&gba->memory.d, dest + (i << 1), word);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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) {
|
|
|
|
int32_t word = GBALoad32(&gba->memory.d, source);
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
|
|
GBAStore32(&gba->memory.d, dest + (i << 2), word);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
|
|
int32_t word = GBALoad32(&gba->memory.d, source + (i << 2));
|
|
|
|
GBAStore32(&gba->memory.d, dest + (i << 2), word);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-20 10:01:50 +00:00
|
|
|
static void _MidiKey2Freq(struct GBA* gba) {
|
|
|
|
uint32_t key = GBALoad32(&gba->memory.d, gba->cpu.gprs[0] + 4);
|
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;
|
|
|
|
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-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-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;
|
|
|
|
case REGION_VRAM:
|
|
|
|
_unLz77(&gba->memory, gba->cpu.gprs[0], &((uint8_t*) gba->video.vram)[(gba->cpu.gprs[1] & 0x0001FFFF)]);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
GBALog(GBA_LOG_WARN, "Bad LZ77 destination");
|
|
|
|
break;
|
|
|
|
}
|
2013-04-20 09:52:10 +00:00
|
|
|
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) {
|
|
|
|
int remaining = (GBALoad32(&memory->d, source) & 0xFFFFFF00) >> 8;
|
|
|
|
// 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
|
|
|
|
block = GBALoadU8(&memory->d, sPointer) | (GBALoadU8(&memory->d, sPointer + 1) << 8);
|
|
|
|
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
|
|
|
|
*dPointer = GBALoadU8(&memory->d, sPointer++);
|
|
|
|
++dPointer;
|
|
|
|
--remaining;
|
|
|
|
}
|
|
|
|
blockheader <<= 1;
|
|
|
|
--blocksRemaining;
|
|
|
|
} else {
|
|
|
|
blockheader = GBALoadU8(&memory->d, sPointer++);
|
|
|
|
blocksRemaining = 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|