diff --git a/Core/display.c b/Core/display.c index 8ab3547..55b466f 100644 --- a/Core/display.c +++ b/Core/display.c @@ -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 */ - addr |= ((gb->dma_current_src - 1) & 0x1FFF); - gb->oam[gb->dma_current_dest - 1] = gb->vram[addr]; + /* 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); + } + 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]; } diff --git a/Core/gb.h b/Core/gb.h index 630281c..76f6192 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -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; ) diff --git a/Core/memory.c b/Core/memory.c index 9cc75a0..2d69fe5 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -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; }