DMA/PPU VRAM conflicts on the CGB/AGB

This commit is contained in:
Lior Halphon 2022-01-17 22:07:24 +02:00
parent b45761146f
commit ab75858c86
3 changed files with 26 additions and 4 deletions

View File

@ -677,9 +677,25 @@ static inline uint8_t vram_read(GB_gameboy_t *gb, uint16_t addr)
}
if (unlikely(gb->dma_current_dest <= 0xa0 && gb->dma_current_dest > 0 && (gb->dma_current_src & 0xE000) == 0x8000)) { // TODO: what happens in the last and first M cycles?
// DMAing from VRAM!
/* TODO: This is only correct on a DMG/MGB, CGBs and AGBs use some other pattern; AGS has a completely different one */
/* TODO: AGS has its own, very different pattern, but AGS is not currently a supported model */
if (GB_is_cgb(gb)) {
if (gb->dma_ppu_vram_conflict) {
addr = (gb->dma_ppu_vram_conflict_addr & 0x1FFF) | (addr & 0x2000);
}
else if (gb->dma_cycles_modulo) {
addr &= 0x2000;
addr |= ((gb->dma_current_src - 1) & 0x1FFF);
gb->oam[gb->dma_current_dest - 1] = gb->vram[addr];
}
else {
addr &= 0x2000 | ((gb->dma_current_src - 1) & 0x1FFF);
gb->dma_ppu_vram_conflict_addr = addr;
gb->dma_ppu_vram_conflict = true;
}
}
else {
addr |= ((gb->dma_current_src - 1) & 0x1FFF);
}
gb->oam[gb->dma_current_dest - 1] = gb->vram[(addr & 0x1FFF) | (gb->cgb_vram_bank? 0x2000 : 0)];
}
return gb->vram[addr];
}

View File

@ -429,6 +429,8 @@ struct GB_gameboy_internal_s {
uint16_t dma_current_src;
uint16_t dma_cycles;
int8_t dma_cycles_modulo;
bool dma_ppu_vram_conflict;
uint16_t dma_ppu_vram_conflict_addr;
uint8_t last_opcode_read; /* Required to emulate HDMA reads from Exxx */
bool hdma_starting;
)
@ -775,6 +777,7 @@ struct GB_gameboy_internal_s {
bool wx_just_changed;
bool tile_sel_glitch;
bool disable_oam_corruption; // For safe memory reads
bool in_dma_read;
GB_gbs_header_t gbs_header;
)

View File

@ -303,7 +303,7 @@ static uint8_t read_vram(GB_gameboy_t *gb, uint16_t addr)
}
}
if (unlikely(gb->vram_read_blocked)) {
if (unlikely(gb->vram_read_blocked && !gb->in_dma_read)) {
return 0xFF;
}
if (unlikely(gb->display_state == 22 && GB_is_cgb(gb) && !gb->cgb_double_speed)) {
@ -1700,6 +1700,7 @@ void GB_dma_run(GB_gameboy_t *gb)
{
if (gb->dma_current_dest == 0xa1) return;
signed cycles = gb->dma_cycles + gb->dma_cycles_modulo;
gb->in_dma_read = true;
while (unlikely(cycles >= 4)) {
cycles -= 4;
if (gb->dma_current_dest >= 0xa0) {
@ -1720,7 +1721,9 @@ void GB_dma_run(GB_gameboy_t *gb)
/* dma_current_src must be the correct value during GB_read_memory */
gb->dma_current_src++;
gb->dma_ppu_vram_conflict = false;
}
gb->in_dma_read = false;
gb->dma_cycles_modulo = cycles;
gb->dma_cycles = 0;
}