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: Automatically skip BIOS if ROM has invalid logo
- GBA: Refine multiboot detection (fixes mgba.io/i/2192) - GBA: Refine multiboot detection (fixes mgba.io/i/2192)
- GBA Cheats: Implement "never" type codes (closes mgba.io/i/915) - 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 DMA: Enhanced logging (closes mgba.io/i/2454)
- GBA Video: Implement layer placement for OpenGL renderer (fixes mgba.io/i/1962) - GBA Video: Implement layer placement for OpenGL renderer (fixes mgba.io/i/1962)
- GBA Video: Fix highlighting for sprites with mid-frame palette changes - GBA Video: Fix highlighting for sprites with mid-frame palette changes

View File

@ -154,6 +154,13 @@ enum GBAIORegisters {
REG_POSTFLG = 0x300, REG_POSTFLG = 0x300,
REG_HALTCNT = 0x301, 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_STRING = 0xFFF600,
REG_DEBUG_FLAGS = 0xFFF700, REG_DEBUG_FLAGS = 0xFFF700,
REG_DEBUG_ENABLE = 0xFFF780, REG_DEBUG_ENABLE = 0xFFF780,

View File

@ -172,6 +172,7 @@ uint32_t GBAStoreMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum
int* cycleCounter); int* cycleCounter);
void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters); void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters);
void GBAAdjustEWRAMWaitstates(struct GBA* gba, uint16_t parameters);
struct GBASerializedState; struct GBASerializedState;
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state); 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); mLOG_DECLARE_CATEGORY(GBA_STATE);
/* Savestate format: /* Savestate format:
* 0x00000 - 0x00003: Version Magic (0x01000005) * 0x00000 - 0x00003: Version Magic (0x01000006)
* 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS) * 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS)
* 0x00008 - 0x0000B: ROM CRC32 * 0x00008 - 0x0000B: ROM CRC32
* 0x0000C - 0x0000F: Master cycles * 0x0000C - 0x0000F: Master cycles

View File

@ -199,7 +199,7 @@ const char* const GBAIORegisterNames[] = {
"IME" "IME"
}; };
static const int _isValidRegister[REG_MAX >> 1] = { static const int _isValidRegister[REG_INTERNAL_MAX >> 1] = {
// Video // Video
1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
1, 1, 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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// Interrupts // 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 // Video
0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// Interrupts // 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 // Video
0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// Interrupts // Interrupts
1, 1, 0, 0, 1 1, 1, 0, 0, 1, 0, 0, 0,
// Internal registers
1, 1
}; };
void GBAIOInit(struct GBA* gba) { 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_BG2PD >> 1] = 0x100;
gba->memory.io[REG_BG3PA >> 1] = 0x100; gba->memory.io[REG_BG3PA >> 1] = 0x100;
gba->memory.io[REG_BG3PD >> 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) { if (!gba->biosVf) {
gba->memory.io[REG_VCOUNT >> 1] = 0x7E; 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: case REG_MAX:
// Some bad interrupt libraries will write to this // Some bad interrupt libraries will write to this
break; 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: case REG_DEBUG_ENABLE:
gba->debug = value == 0xC0DE; gba->debug = value == 0xC0DE;
return; return;
@ -936,6 +951,11 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
case 0x206: case 0x206:
mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address); mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address);
return 0; 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: case REG_DEBUG_ENABLE:
if (gba->debug) { if (gba->debug) {
return 0x1DEA; return 0x1DEA;
@ -950,7 +970,7 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) { void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
int i; int i;
for (i = 0; i < REG_MAX; i += 2) { for (i = 0; i < REG_INTERNAL_MAX; i += 2) {
if (_isRSpecialRegister[i >> 1]) { if (_isRSpecialRegister[i >> 1]) {
STORE_16(gba->memory.io[i >> 1], i, state->io); STORE_16(gba->memory.io[i >> 1], i, state->io);
} else if (_isValidRegister[i >> 1]) { } else if (_isValidRegister[i >> 1]) {
@ -991,6 +1011,9 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
GBAIOWrite(gba, i, reg); GBAIOWrite(gba, i, reg);
} }
} }
if (state->versionMagic >= 0x01000006) {
GBAIOWrite(gba, REG_EXWAITCNT_HI, gba->memory.io[REG_INTERNAL_EXWAITCNT_HI >> 1]);
}
uint32_t when; uint32_t when;
for (i = 0; i < 4; ++i) { 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)); memset(gba->memory.io, 0, sizeof(gba->memory.io));
GBAAdjustWaitstates(gba, 0); GBAAdjustWaitstates(gba, 0);
GBAAdjustEWRAMWaitstates(gba, 0x0D00);
gba->memory.activeRegion = -1; gba->memory.activeRegion = -1;
gba->memory.agbPrintProtect = 0; 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) { int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
struct GBA* gba = (struct GBA*) cpu->master; struct GBA* gba = (struct GBA*) cpu->master;
struct GBAMemory* memory = &gba->memory; struct GBAMemory* memory = &gba->memory;

View File

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