mirror of https://github.com/mgba-emu/mgba.git
GBA Memory: Implement adjustable EWRAM waitstates (closes #1276)
This commit is contained in:
parent
8c83570191
commit
f9e03e7182
1
CHANGES
1
CHANGES
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
35
src/gba/io.c
35
src/gba/io.c
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue