mirror of https://github.com/mgba-emu/mgba.git
GBA Memory: AGBPrint support
This commit is contained in:
parent
459d133855
commit
e2f4fdbdac
1
CHANGES
1
CHANGES
|
@ -10,6 +10,7 @@ Features:
|
||||||
- Map viewer
|
- Map viewer
|
||||||
- Automatic cheat loading and saving
|
- Automatic cheat loading and saving
|
||||||
- GameShark and Action Replay button support
|
- GameShark and Action Replay button support
|
||||||
|
- AGBPrint support
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
|
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
|
||||||
- GB Serialize: Fix audio state loading
|
- GB Serialize: Fix audio state loading
|
||||||
|
|
|
@ -69,7 +69,9 @@ enum {
|
||||||
SIZE_CART_FLASH512 = 0x00010000,
|
SIZE_CART_FLASH512 = 0x00010000,
|
||||||
SIZE_CART_FLASH1M = 0x00020000,
|
SIZE_CART_FLASH1M = 0x00020000,
|
||||||
SIZE_CART_EEPROM = 0x00002000,
|
SIZE_CART_EEPROM = 0x00002000,
|
||||||
SIZE_CART_EEPROM512 = 0x00000200
|
SIZE_CART_EEPROM512 = 0x00000200,
|
||||||
|
|
||||||
|
SIZE_AGB_PRINT = 0x10000
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -77,8 +79,23 @@ enum {
|
||||||
BASE_OFFSET = 24
|
BASE_OFFSET = 24
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
AGB_PRINT_BASE = 0x00FD0000,
|
||||||
|
AGB_PRINT_TOP = 0x00FE0000,
|
||||||
|
AGB_PRINT_PROTECT = 0x00FE2FFE,
|
||||||
|
AGB_PRINT_STRUCT = 0x01FE20F8,
|
||||||
|
AGB_PRINT_FLUSH_ADDR = 0x01FE209C,
|
||||||
|
};
|
||||||
|
|
||||||
mLOG_DECLARE_CATEGORY(GBA_MEM);
|
mLOG_DECLARE_CATEGORY(GBA_MEM);
|
||||||
|
|
||||||
|
struct GBAPrintContext {
|
||||||
|
uint16_t request;
|
||||||
|
uint16_t bank;
|
||||||
|
uint16_t get;
|
||||||
|
uint16_t put;
|
||||||
|
};
|
||||||
|
|
||||||
struct GBAMemory {
|
struct GBAMemory {
|
||||||
uint32_t* bios;
|
uint32_t* bios;
|
||||||
uint32_t* wram;
|
uint32_t* wram;
|
||||||
|
@ -108,6 +125,10 @@ struct GBAMemory {
|
||||||
int activeDMA;
|
int activeDMA;
|
||||||
uint32_t dmaTransferRegister;
|
uint32_t dmaTransferRegister;
|
||||||
|
|
||||||
|
uint16_t agbPrint;
|
||||||
|
struct GBAPrintContext agbPrintCtx;
|
||||||
|
uint16_t* agbPrintBuffer;
|
||||||
|
|
||||||
bool mirroring;
|
bool mirroring;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -146,6 +167,8 @@ struct GBASerializedState;
|
||||||
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state);
|
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state);
|
||||||
void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state);
|
void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state);
|
||||||
|
|
||||||
|
void GBAPrintFlush(struct GBA* gba);
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -333,6 +333,12 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
|
||||||
mLOG(GBA_BIOS, DEBUG, "SWI: %02X r0: %08X r1: %08X r2: %08X r3: %08X",
|
mLOG(GBA_BIOS, DEBUG, "SWI: %02X r0: %08X r1: %08X r2: %08X r3: %08X",
|
||||||
immediate, cpu->gprs[0], cpu->gprs[1], cpu->gprs[2], cpu->gprs[3]);
|
immediate, cpu->gprs[0], cpu->gprs[1], cpu->gprs[2], cpu->gprs[3]);
|
||||||
|
|
||||||
|
switch (immediate) {
|
||||||
|
case 0xFA:
|
||||||
|
GBAPrintFlush(gba);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (gba->memory.fullBios) {
|
if (gba->memory.fullBios) {
|
||||||
ARMRaiseSWI(cpu);
|
ARMRaiseSWI(cpu);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -22,7 +22,10 @@
|
||||||
mLOG_DEFINE_CATEGORY(GBA_MEM, "GBA Memory", "gba.memory");
|
mLOG_DEFINE_CATEGORY(GBA_MEM, "GBA Memory", "gba.memory");
|
||||||
|
|
||||||
static void _pristineCow(struct GBA* gba);
|
static void _pristineCow(struct GBA* gba);
|
||||||
static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb
|
static void _agbPrintStore(struct GBA* gba, uint32_t address, int16_t value);
|
||||||
|
static int16_t _agbPrintLoad(struct GBA* gba, uint32_t address);
|
||||||
|
static uint8_t _deadbeef[4] = { 0x10, 0xB7, 0x10, 0xE7 }; // Illegal instruction on both ARM and Thumb
|
||||||
|
static uint8_t _agbPrintFunc[4] = { 0xFA, 0xDF /* swi 0xFF */, 0x70, 0x47 /* bx lr */ };
|
||||||
|
|
||||||
static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t region);
|
static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t region);
|
||||||
static int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait);
|
static int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait);
|
||||||
|
@ -80,6 +83,10 @@ void GBAMemoryInit(struct GBA* gba) {
|
||||||
gba->memory.biosPrefetch = 0;
|
gba->memory.biosPrefetch = 0;
|
||||||
gba->memory.mirroring = false;
|
gba->memory.mirroring = false;
|
||||||
|
|
||||||
|
gba->memory.agbPrint = 0;
|
||||||
|
memset(&gba->memory.agbPrintCtx, 0, sizeof(gba->memory.agbPrintCtx));
|
||||||
|
gba->memory.agbPrintBuffer = NULL;
|
||||||
|
|
||||||
gba->memory.iwram = anonymousMemoryMap(SIZE_WORKING_IRAM);
|
gba->memory.iwram = anonymousMemoryMap(SIZE_WORKING_IRAM);
|
||||||
gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM);
|
gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM);
|
||||||
|
|
||||||
|
@ -116,6 +123,12 @@ void GBAMemoryReset(struct GBA* gba) {
|
||||||
|
|
||||||
memset(gba->memory.io, 0, sizeof(gba->memory.io));
|
memset(gba->memory.io, 0, sizeof(gba->memory.io));
|
||||||
|
|
||||||
|
gba->memory.agbPrint = 0;
|
||||||
|
memset(&gba->memory.agbPrintCtx, 0, sizeof(gba->memory.agbPrintCtx));
|
||||||
|
if (gba->memory.agbPrintBuffer) {
|
||||||
|
gba->memory.agbPrintBuffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
gba->memory.prefetch = false;
|
gba->memory.prefetch = false;
|
||||||
gba->memory.lastPrefetchedPc = 0;
|
gba->memory.lastPrefetchedPc = 0;
|
||||||
|
|
||||||
|
@ -295,10 +308,15 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
||||||
if ((address & (SIZE_CART0 - 1)) < memory->romSize) {
|
if ((address & (SIZE_CART0 - 1)) < memory->romSize) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if ((address & (SIZE_CART0 - 1)) == AGB_PRINT_FLUSH_ADDR && memory->agbPrint == 0x20) {
|
||||||
|
cpu->memory.activeRegion = (uint32_t*) _agbPrintFunc;
|
||||||
|
cpu->memory.activeMask = sizeof(_agbPrintFunc) - 1;
|
||||||
|
|
||||||
|
}
|
||||||
// Fall through
|
// Fall through
|
||||||
default:
|
default:
|
||||||
memory->activeRegion = -1;
|
memory->activeRegion = -1;
|
||||||
cpu->memory.activeRegion = _deadbeef;
|
cpu->memory.activeRegion = (uint32_t*) _deadbeef;
|
||||||
cpu->memory.activeMask = 0;
|
cpu->memory.activeMask = 0;
|
||||||
|
|
||||||
if (!gba->yankedRomSize && mCoreCallbacksListSize(&gba->coreCallbacks)) {
|
if (!gba->yankedRomSize && mCoreCallbacksListSize(&gba->coreCallbacks)) {
|
||||||
|
@ -528,6 +546,16 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
LOAD_16(value, address & memory->romMask, memory->rom);
|
LOAD_16(value, address & memory->romMask, memory->rom);
|
||||||
} else if (memory->vfame.cartType) {
|
} else if (memory->vfame.cartType) {
|
||||||
value = GBAVFameGetPatternValue(address, 16);
|
value = GBAVFameGetPatternValue(address, 16);
|
||||||
|
} else if ((address & (SIZE_CART0 - 1)) >= AGB_PRINT_BASE) {
|
||||||
|
uint32_t agbPrintAddr = address & 0x00FFFFFF;
|
||||||
|
if (agbPrintAddr == AGB_PRINT_PROTECT) {
|
||||||
|
value = memory->agbPrint;
|
||||||
|
} else if (agbPrintAddr < AGB_PRINT_TOP || (agbPrintAddr & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8)) {
|
||||||
|
value = _agbPrintLoad(gba, address);
|
||||||
|
} else {
|
||||||
|
mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load16: 0x%08X", address);
|
||||||
|
value = (address >> 1) & 0xFFFF;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load16: 0x%08X", address);
|
mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load16: 0x%08X", address);
|
||||||
value = (address >> 1) & 0xFFFF;
|
value = (address >> 1) & 0xFFFF;
|
||||||
|
@ -838,9 +866,23 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
||||||
if (memory->hw.devices != HW_NONE && IS_GPIO_REGISTER(address & 0xFFFFFE)) {
|
if (memory->hw.devices != HW_NONE && IS_GPIO_REGISTER(address & 0xFFFFFE)) {
|
||||||
uint32_t reg = address & 0xFFFFFE;
|
uint32_t reg = address & 0xFFFFFE;
|
||||||
GBAHardwareGPIOWrite(&memory->hw, reg, value);
|
GBAHardwareGPIOWrite(&memory->hw, reg, value);
|
||||||
} else {
|
break;
|
||||||
mLOG(GBA_MEM, GAME_ERROR, "Bad cartridge Store16: 0x%08X", address);
|
|
||||||
}
|
}
|
||||||
|
// Fall through
|
||||||
|
case REGION_CART0_EX:
|
||||||
|
if ((address & 0x00FFFFFF) >= AGB_PRINT_BASE) {
|
||||||
|
uint32_t agbPrintAddr = address & 0x00FFFFFF;
|
||||||
|
if (agbPrintAddr == AGB_PRINT_PROTECT) {
|
||||||
|
memory->agbPrint = value;
|
||||||
|
_agbPrintStore(gba, address, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (memory->agbPrint == 0x20 && (agbPrintAddr < AGB_PRINT_TOP || (agbPrintAddr & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8))) {
|
||||||
|
_agbPrintStore(gba, address, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mLOG(GBA_MEM, GAME_ERROR, "Bad cartridge Store16: 0x%08X", address);
|
||||||
break;
|
break;
|
||||||
case REGION_CART2_EX:
|
case REGION_CART2_EX:
|
||||||
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
|
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
|
||||||
|
@ -1595,3 +1637,50 @@ void _pristineCow(struct GBA* gba) {
|
||||||
gba->memory.rom = newRom;
|
gba->memory.rom = newRom;
|
||||||
gba->memory.hw.gpioBase = &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1];
|
gba->memory.hw.gpioBase = &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GBAPrintFlush(struct GBA* gba) {
|
||||||
|
char oolBuf[0x101];
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; gba->memory.agbPrintCtx.get != gba->memory.agbPrintCtx.put && i < 0x100; ++i) {
|
||||||
|
int16_t value;
|
||||||
|
LOAD_16(value, gba->memory.agbPrintCtx.get & -2, gba->memory.agbPrintBuffer);
|
||||||
|
if (gba->memory.agbPrintCtx.get & 1) {
|
||||||
|
value >>= 8;
|
||||||
|
} else {
|
||||||
|
value &= 0xFF;
|
||||||
|
}
|
||||||
|
oolBuf[i] = value;
|
||||||
|
++gba->memory.agbPrintCtx.get;
|
||||||
|
}
|
||||||
|
_agbPrintStore(gba, AGB_PRINT_STRUCT + 4, gba->memory.agbPrintCtx.get);
|
||||||
|
|
||||||
|
mLOG(GBA_DEBUG, INFO, "%s", oolBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _agbPrintStore(struct GBA* gba, uint32_t address, int16_t value) {
|
||||||
|
struct GBAMemory* memory = &gba->memory;
|
||||||
|
if ((address & 0x00FFFFFF) < AGB_PRINT_TOP) {
|
||||||
|
if (!memory->agbPrintBuffer) {
|
||||||
|
memory->agbPrintBuffer = anonymousMemoryMap(SIZE_AGB_PRINT);
|
||||||
|
}
|
||||||
|
STORE_16(value, address & (SIZE_AGB_PRINT - 2), memory->agbPrintBuffer);
|
||||||
|
} else if ((address & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8)) {
|
||||||
|
(&memory->agbPrintCtx.request)[(address & 7) >> 1] = value;
|
||||||
|
}
|
||||||
|
if (memory->romSize == SIZE_CART0) {
|
||||||
|
_pristineCow(gba);
|
||||||
|
memcpy(&memory->rom[AGB_PRINT_FLUSH_ADDR >> 2], _agbPrintFunc, sizeof(_agbPrintFunc));
|
||||||
|
STORE_16(value, address & (SIZE_CART0 - 2), memory->rom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int16_t _agbPrintLoad(struct GBA* gba, uint32_t address) {
|
||||||
|
struct GBAMemory* memory = &gba->memory;
|
||||||
|
int16_t value = 0xFFFF;
|
||||||
|
if (address < AGB_PRINT_TOP) {
|
||||||
|
LOAD_16(value, address & (SIZE_AGB_PRINT - 1), memory->agbPrintBuffer);
|
||||||
|
} else if ((address & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8)) {
|
||||||
|
value = (&memory->agbPrintCtx.request)[(address & 7) >> 1];
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue