GBA Memory: Implement adjustable EWRAM waitstates (closes #1276)

This commit is contained in:
Vicki Pfau 2022-06-07 00:05:27 -07:00
parent 8c83570191
commit f9e03e7182
7 changed files with 66 additions and 8 deletions

View File

@ -67,6 +67,7 @@ Misc:
- GBA: Automatically skip BIOS if ROM has invalid logo
- GBA: Refine multiboot detection (fixes mgba.io/i/2192)
- GBA Cheats: Implement "never" type codes (closes mgba.io/i/915)
- GBA Memory: Implement adjustable EWRAM waitstates (closes mgba.io/i/1276)
- GBA DMA: Enhanced logging (closes mgba.io/i/2454)
- GBA Video: Implement layer placement for OpenGL renderer (fixes mgba.io/i/1962)
- GBA Video: Fix highlighting for sprites with mid-frame palette changes

View File

@ -154,6 +154,13 @@ enum GBAIORegisters {
REG_POSTFLG = 0x300,
REG_HALTCNT = 0x301,
REG_EXWAITCNT_LO = 0x800,
REG_EXWAITCNT_HI = 0x802,
REG_INTERNAL_EXWAITCNT_LO = 0x210,
REG_INTERNAL_EXWAITCNT_HI = 0x212,
REG_INTERNAL_MAX = 0x214,
REG_DEBUG_STRING = 0xFFF600,
REG_DEBUG_FLAGS = 0xFFF700,
REG_DEBUG_ENABLE = 0xFFF780,

View File

@ -172,6 +172,7 @@ uint32_t GBAStoreMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum
int* cycleCounter);
void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters);
void GBAAdjustEWRAMWaitstates(struct GBA* gba, uint16_t parameters);
struct GBASerializedState;
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state);

View File

@ -20,7 +20,7 @@ extern MGBA_EXPORT const uint32_t GBASavestateVersion;
mLOG_DECLARE_CATEGORY(GBA_STATE);
/* Savestate format:
* 0x00000 - 0x00003: Version Magic (0x01000005)
* 0x00000 - 0x00003: Version Magic (0x01000006)
* 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS)
* 0x00008 - 0x0000B: ROM CRC32
* 0x0000C - 0x0000F: Master cycles

View File

@ -199,7 +199,7 @@ const char* const GBAIORegisterNames[] = {
"IME"
};
static const int _isValidRegister[REG_MAX >> 1] = {
static const int _isValidRegister[REG_INTERNAL_MAX >> 1] = {
// Video
1, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
@ -238,10 +238,12 @@ static const int _isValidRegister[REG_MAX >> 1] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
// Interrupts
1, 1, 1, 0, 1
1, 1, 1, 0, 1, 0, 0, 0,
// Internal registers
1, 1
};
static const int _isRSpecialRegister[REG_MAX >> 1] = {
static const int _isRSpecialRegister[REG_INTERNAL_MAX >> 1] = {
// Video
0, 0, 1, 1, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1,
@ -280,9 +282,12 @@ static const int _isRSpecialRegister[REG_MAX >> 1] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
// Interrupts
0, 0, 0, 0, 0, 0, 0, 0,
// Internal registers
1, 1
};
static const int _isWSpecialRegister[REG_MAX >> 1] = {
static const int _isWSpecialRegister[REG_INTERNAL_MAX >> 1] = {
// Video
0, 0, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@ -321,7 +326,9 @@ static const int _isWSpecialRegister[REG_MAX >> 1] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
// Interrupts
1, 1, 0, 0, 1
1, 1, 0, 0, 1, 0, 0, 0,
// Internal registers
1, 1
};
void GBAIOInit(struct GBA* gba) {
@ -333,6 +340,8 @@ void GBAIOInit(struct GBA* gba) {
gba->memory.io[REG_BG2PD >> 1] = 0x100;
gba->memory.io[REG_BG3PA >> 1] = 0x100;
gba->memory.io[REG_BG3PD >> 1] = 0x100;
gba->memory.io[REG_INTERNAL_EXWAITCNT_LO >> 1] = 0x20;
gba->memory.io[REG_INTERNAL_EXWAITCNT_HI >> 1] = 0xD00;
if (!gba->biosVf) {
gba->memory.io[REG_VCOUNT >> 1] = 0x7E;
@ -574,6 +583,12 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
case REG_MAX:
// Some bad interrupt libraries will write to this
break;
case REG_EXWAITCNT_HI:
// This register sits outside of the normal I/O block, so we need to stash it somewhere unused
address = REG_INTERNAL_EXWAITCNT_HI;
value &= 0xFF00;
GBAAdjustEWRAMWaitstates(gba, value);
break;
case REG_DEBUG_ENABLE:
gba->debug = value == 0xC0DE;
return;
@ -936,6 +951,11 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
case 0x206:
mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address);
return 0;
// These registers sit outside of the normal I/O block, so we need to stash them somewhere unused
case REG_EXWAITCNT_LO:
case REG_EXWAITCNT_HI:
address += REG_INTERNAL_EXWAITCNT_LO - REG_EXWAITCNT_LO;
break;
case REG_DEBUG_ENABLE:
if (gba->debug) {
return 0x1DEA;
@ -950,7 +970,7 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
int i;
for (i = 0; i < REG_MAX; i += 2) {
for (i = 0; i < REG_INTERNAL_MAX; i += 2) {
if (_isRSpecialRegister[i >> 1]) {
STORE_16(gba->memory.io[i >> 1], i, state->io);
} else if (_isValidRegister[i >> 1]) {
@ -991,6 +1011,9 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
GBAIOWrite(gba, i, reg);
}
}
if (state->versionMagic >= 0x01000006) {
GBAIOWrite(gba, REG_EXWAITCNT_HI, gba->memory.io[REG_INTERNAL_EXWAITCNT_HI >> 1]);
}
uint32_t when;
for (i = 0; i < 4; ++i) {

View File

@ -127,6 +127,7 @@ void GBAMemoryReset(struct GBA* gba) {
memset(gba->memory.io, 0, sizeof(gba->memory.io));
GBAAdjustWaitstates(gba, 0);
GBAAdjustEWRAMWaitstates(gba, 0x0D00);
gba->memory.activeRegion = -1;
gba->memory.agbPrintProtect = 0;
@ -1714,6 +1715,31 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) {
}
}
void GBAAdjustEWRAMWaitstates(struct GBA* gba, uint16_t parameters) {
struct GBAMemory* memory = &gba->memory;
struct ARMCore* cpu = gba->cpu;
int wait = 15 - ((parameters >> 8) & 0xF);
if (wait) {
memory->waitstatesNonseq16[REGION_WORKING_RAM] = wait;
memory->waitstatesSeq16[REGION_WORKING_RAM] = wait;
memory->waitstatesNonseq32[REGION_WORKING_RAM] = 2 * wait + 1;
memory->waitstatesSeq32[REGION_WORKING_RAM] = 2 * wait + 1;
cpu->memory.activeSeqCycles32 = memory->waitstatesSeq32[memory->activeRegion];
cpu->memory.activeSeqCycles16 = memory->waitstatesSeq16[memory->activeRegion];
cpu->memory.activeNonseqCycles32 = memory->waitstatesNonseq32[memory->activeRegion];
cpu->memory.activeNonseqCycles16 = memory->waitstatesNonseq16[memory->activeRegion];
} else {
if (!gba->hardCrash) {
mLOG(GBA_MEM, GAME_ERROR, "Cannot set EWRAM to 0 waitstates");
} else {
mLOG(GBA_MEM, FATAL, "Cannot set EWRAM to 0 waitstates");
}
}
}
int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
struct GBA* gba = (struct GBA*) cpu->master;
struct GBAMemory* memory = &gba->memory;

View File

@ -15,7 +15,7 @@
#include <fcntl.h>
MGBA_EXPORT const uint32_t GBASavestateMagic = 0x01000000;
MGBA_EXPORT const uint32_t GBASavestateVersion = 0x00000005;
MGBA_EXPORT const uint32_t GBASavestateVersion = 0x00000006;
mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize");