diff --git a/Core/display.c b/Core/display.c index ae7d757..cd17b31 100644 --- a/Core/display.c +++ b/Core/display.c @@ -696,7 +696,12 @@ static inline uint8_t vram_read(GB_gameboy_t *gb, uint16_t addr) if (unlikely(gb->vram_ppu_blocked)) { return 0xFF; } - 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? + if (unlikely(gb->hdma_on)) { + gb->addr_for_hdma_conflict = addr; + return 0; + } + // TODO: what if both? + else 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: AGS has its own, very different pattern, but AGS is not currently a supported model */ /* TODO: Research this when researching odd modes */ diff --git a/Core/gb.h b/Core/gb.h index d1add0b..09ebb77 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -776,6 +776,7 @@ struct GB_gameboy_internal_s { bool tile_sel_glitch; bool disable_oam_corruption; // For safe memory reads bool in_dma_read; + uint16_t addr_for_hdma_conflict; GB_gbs_header_t gbs_header; ) diff --git a/Core/memory.c b/Core/memory.c index 805c313..a41191a 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -1735,17 +1735,37 @@ void GB_hdma_run(GB_gameboy_t *gb) if (gb->model < GB_MODEL_CGB_D || gb->pc > 0x8000) { gb->hdma_open_bus = 0xFF; } - GB_advance_cycles(gb, 4); + gb->addr_for_hdma_conflict = 0xFFFF; + gb->hdma_current_dest &= 0x1FFF; + uint16_t vram_base = gb->cgb_vram_bank? 0x2000 : 0; + GB_advance_cycles(gb, cycles); while (gb->hdma_on) { uint8_t byte = gb->hdma_open_bus; + gb->addr_for_hdma_conflict = 0xFFFF; + GB_advance_cycles(gb, cycles); + if (gb->hdma_current_src < 0x8000 || (gb->hdma_current_src & 0xE000) == 0xC000 || (gb->hdma_current_src & 0xE000) == 0xA000) { byte = GB_read_memory(gb, gb->hdma_current_src); } gb->hdma_current_src++; - GB_write_memory(gb, 0x8000 | (gb->hdma_current_dest++ & 0x1FFF), byte); - GB_advance_cycles(gb, cycles); + if (gb->addr_for_hdma_conflict == 0xFFFF /* || gb->model == GB_MODEL_AGS */) { + gb->vram[vram_base + gb->hdma_current_dest++] = byte; + } + else { + if (gb->model == GB_MODEL_CGB_E || gb->cgb_double_speed) { + /* + These corruptions revision (unit?) specific in single speed. They happen only on my CGB-E. + */ + gb->addr_for_hdma_conflict &= 0x1FFF; + // Can't write to even bitmap bytes in single speed mode + if (gb->cgb_double_speed || gb->addr_for_hdma_conflict >= 0x1900 || (gb->addr_for_hdma_conflict & 1)) { + gb->vram[vram_base + (gb->hdma_current_dest & gb->addr_for_hdma_conflict)] = byte; + } + } + gb->hdma_current_dest++; + } gb->hdma_open_bus = 0xFF; if ((gb->hdma_current_dest & 0xf) == 0) { @@ -1759,4 +1779,7 @@ void GB_hdma_run(GB_gameboy_t *gb) } } } + if (!gb->cgb_double_speed) { + GB_advance_cycles(gb, 2); + } }