Skip to content

Commit

Permalink
GBA DMA: Fix invalid DMA reads (fixes #142)
Browse files Browse the repository at this point in the history
  • Loading branch information
endrift committed Nov 1, 2017
1 parent 20754b7 commit 6796307
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGES
Expand Up @@ -23,6 +23,7 @@ Bugfixes:
- GB Video: Only trigger STAT write IRQs when screen is on (fixes mgba.io/i/912)
- GBA Cheats: Fix PARv3 slide codes (fixes mgba.io/i/919)
- GBA Video: OBJWIN can change blend params after OBJ is drawn (fixes mgba.io/i/921)
- GBA DMA: Fix invalid DMA reads (fixes mgba.io/i/142)
Misc:
- GBA Timer: Use global cycles for timers
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
Expand Down
1 change: 1 addition & 0 deletions include/mgba/internal/gba/memory.h
Expand Up @@ -106,6 +106,7 @@ struct GBAMemory {
struct GBADMA dma[4];
struct mTimingEvent dmaEvent;
int activeDMA;
uint32_t dmaTransferRegister;

bool mirroring;
};
Expand Down
7 changes: 5 additions & 2 deletions include/mgba/internal/gba/serialize.h
Expand Up @@ -169,7 +169,8 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | bits 4 - 8: GB Player transmit position
* | bits 9 - 23: Reserved
* 0x002C4 - 0x002C7: Game Boy Player next event
* 0x002C8 - 0x002DF: Reserved (leave zero)
* 0x002C8 - 0x002CB: Current DMA transfer word
* 0x002CC - 0x002DF: Reserved (leave zero)
* 0x002E0 - 0x002EF: Savedata state
* | 0x002E0 - 0x002E0: Savedata type
* | 0x002E1 - 0x002E1: Savedata command (see savedata.h)
Expand Down Expand Up @@ -293,7 +294,9 @@ struct GBASerializedState {
uint32_t gbpNextEvent;
} hw;

uint32_t reservedHardware[6];
uint32_t dmaTransferRegister;

uint32_t reservedHardware[5];

struct {
uint8_t type;
Expand Down
41 changes: 28 additions & 13 deletions src/gba/dma.c
Expand Up @@ -46,6 +46,8 @@ uint32_t GBADMAWriteSAD(struct GBA* gba, int dma, uint32_t address) {
address &= 0x0FFFFFFE;
if (_isValidDMASAD(dma, address)) {
memory->dma[dma].source = address;
} else {
memory->dma[dma].source = 0;
}
return memory->dma[dma].source;
}
Expand Down Expand Up @@ -242,31 +244,44 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
info->when += cycles;

gba->performingDMA = 1 | (number << 1);
uint32_t word;
if (width == 4) {
word = cpu->memory.load32(cpu, source, 0);
gba->bus = word;
cpu->memory.store32(cpu, dest, word, 0);
if (source) {
memory->dmaTransferRegister = cpu->memory.load32(cpu, source, 0);
}
gba->bus = memory->dmaTransferRegister;
cpu->memory.store32(cpu, dest, memory->dmaTransferRegister, 0);
memory->dmaTransferRegister &= 0xFFFF0000;
memory->dmaTransferRegister |= memory->dmaTransferRegister >> 16;
} else {
if (sourceRegion == REGION_CART2_EX && memory->savedata.type == SAVEDATA_EEPROM) {
word = GBASavedataReadEEPROM(&memory->savedata);
cpu->memory.store16(cpu, dest, word, 0);
} else if (destRegion == REGION_CART2_EX) {
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
mLOG(GBA_MEM, INFO, "Detected EEPROM savegame");
GBASavedataInitEEPROM(&memory->savedata, gba->realisticTiming);
}
word = cpu->memory.load16(cpu, source, 0);
GBASavedataWriteEEPROM(&memory->savedata, word, wordsRemaining);
memory->dmaTransferRegister = GBASavedataReadEEPROM(&memory->savedata);
} else {
word = cpu->memory.load16(cpu, source, 0);
cpu->memory.store16(cpu, dest, word, 0);
if (source) {
memory->dmaTransferRegister = cpu->memory.load16(cpu, source, 0);
}
}
gba->bus = word | (word << 16);
if (destRegion == REGION_CART2_EX) {
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
mLOG(GBA_MEM, INFO, "Detected EEPROM savegame");
GBASavedataInitEEPROM(&memory->savedata, gba->realisticTiming);
}
GBASavedataWriteEEPROM(&memory->savedata, memory->dmaTransferRegister, wordsRemaining);
} else {
cpu->memory.store16(cpu, dest, memory->dmaTransferRegister, 0);

}
memory->dmaTransferRegister |= memory->dmaTransferRegister << 16;
gba->bus = memory->dmaTransferRegister;
}
int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width;
source += sourceOffset;
if (source) {
source += sourceOffset;
}
dest += destOffset;
--wordsRemaining;
gba->performingDMA = 0;
Expand Down
3 changes: 3 additions & 0 deletions src/gba/io.c
Expand Up @@ -939,6 +939,8 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
STORE_32(gba->memory.dma[i].when, 0, &state->dma[i].when);
}

state->dmaTransferRegister = gba->memory.dmaTransferRegister;

GBAHardwareSerialize(&gba->memory.hw, state);
}

Expand Down Expand Up @@ -984,6 +986,7 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
}
}
GBAAudioWriteSOUNDCNT_X(&gba->audio, gba->memory.io[REG_SOUNDCNT_X >> 1]);
gba->memory.dmaTransferRegister = state->dmaTransferRegister;
GBADMAUpdate(gba);
GBAHardwareDeserialize(&gba->memory.hw, state);
}

0 comments on commit 6796307

Please sign in to comment.