diff --git a/Core/display.c b/Core/display.c index db893d8..729d61a 100644 --- a/Core/display.c +++ b/Core/display.c @@ -470,7 +470,7 @@ static void add_object_from_index(GB_gameboy_t *gb, unsigned index) if (gb->n_visible_objs == 10) return; /* TODO: It appears that DMA blocks PPU access to OAM, but it needs verification. */ - if (gb->dma_steps_left && (gb->dma_cycles >= 0 || gb->is_dma_restarting)) { + if (GB_is_dma_active(gb)) { return; } @@ -1215,7 +1215,7 @@ static inline uint16_t mode3_batching_length(GB_gameboy_t *gb) { if (gb->model & GB_MODEL_NO_SFC_BIT) return 0; if (gb->hdma_on) return 0; - if (gb->dma_steps_left) return 0; + if (GB_is_dma_active(gb)) return 0; if (gb->wy_triggered && (gb->io_registers[GB_IO_LCDC] & 0x20) && (gb->io_registers[GB_IO_WX] < 8 || gb->io_registers[GB_IO_WX] == 166)) { return 0; } @@ -1406,7 +1406,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) GB_STAT_update(gb); gb->n_visible_objs = 0; - if (!gb->dma_steps_left && !gb->oam_ppu_blocked) { + if (!GB_is_dma_active(gb) && !gb->oam_ppu_blocked) { GB_BATCHPOINT(gb, display, 5, 80); } for (gb->oam_search_index = 0; gb->oam_search_index < 40; gb->oam_search_index++) { diff --git a/Core/gb.c b/Core/gb.c index 632c5a8..fb0965f 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -1639,8 +1639,8 @@ void GB_reset(GB_gameboy_t *gb) gb->io_registers[GB_IO_DMA] = gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = GB_is_cgb(gb)? 0x00 : 0xFF; gb->accessed_oam_row = -1; - - + gb->dma_current_dest = 0xa1; + if (GB_is_hle_sgb(gb)) { if (!gb->sgb) { gb->sgb = malloc(sizeof(*gb->sgb)); diff --git a/Core/gb.h b/Core/gb.h index c116cbc..af9af73 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -431,13 +431,11 @@ struct GB_gameboy_internal_s { int16_t hdma_cycles; // in 8MHz units uint16_t hdma_current_src, hdma_current_dest; - uint8_t dma_steps_left; uint8_t dma_current_dest; + uint8_t last_dma_read; uint16_t dma_current_src; int16_t dma_cycles; bool is_dma_restarting; - uint8_t dma_and_pattern; - bool dma_skip_write; uint8_t last_opcode_read; /* Required to emulte HDMA reads from Exxx */ bool hdma_starting; ); diff --git a/Core/memory.c b/Core/memory.c index 913fb68..9c26ec8 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -251,7 +251,7 @@ void GB_trigger_oam_bug_read(GB_gameboy_t *gb, uint16_t address) static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr) { - if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting) || addr >= 0xfe00) return false; + if (!GB_is_dma_active(gb) || addr >= 0xfe00) return false; if (addr >= 0xfe00) return false; if (gb->dma_current_src == addr) return false; // Shortcut for DMA access flow if (gb->dma_current_src > 0xe000 && (gb->dma_current_src & ~0x2000) == addr) return false; @@ -482,7 +482,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) return 0xff; } - if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { + if (GB_is_dma_active(gb)) { /* Todo: Does reading from OAM during DMA causes the OAM bug? */ return 0xff; } @@ -746,14 +746,14 @@ uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr) if (GB_is_cgb(gb) && bus_for_addr(gb, gb->dma_current_src) != GB_BUS_RAM && addr >= 0xc000) { // TODO: this should probably affect the DMA dest as well - addr = (gb->dma_current_src & 0x1000) | (addr & 0xFFF) | 0xC000; + addr = ((gb->dma_current_src - 1) & 0x1000) | (addr & 0xFFF) | 0xC000; } else if (GB_is_cgb(gb) && gb->dma_current_src >= 0xe000 && addr > 0xc000) { // TODO: this should probably affect the DMA dest as well - addr = (gb->dma_current_src & 0x1000) | (addr & 0xFFF) | 0xC000; + addr = ((gb->dma_current_src - 1) & 0x1000) | (addr & 0xFFF) | 0xC000; } else { - addr = gb->dma_current_src; + addr = (gb->dma_current_src - 1); } } uint8_t data = read_map[addr >> 12](gb, addr); @@ -1209,7 +1209,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) return; } - if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { + if (GB_is_dma_active(gb)) { /* Todo: Does writing to OAM during DMA causes the OAM bug? */ return; } @@ -1459,18 +1459,16 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) return; case GB_IO_DMA: - if (gb->dma_steps_left) { + if (GB_is_dma_active(gb)) { /* This is not correct emulation, since we're not really delaying the second DMA. One write that should have happened in the first DMA will not happen. However, since that byte will be overwritten by the second DMA before it can actually be read, it doesn't actually matter. */ gb->is_dma_restarting = true; } - gb->dma_and_pattern = 0xFF; - gb->dma_cycles = -7; + gb->dma_cycles = -3; gb->dma_current_dest = 0; gb->dma_current_src = value << 8; - gb->dma_steps_left = 0xa0; gb->io_registers[GB_IO_DMA] = value; return; case GB_IO_SVBK: @@ -1663,23 +1661,24 @@ void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) if (GB_is_cgb(gb) && bus_for_addr(gb, gb->dma_current_src) != GB_BUS_RAM && addr >= 0xc000) { // TODO: this should probably affect the DMA dest as well - addr = (gb->dma_current_src & 0x1000) | (addr & 0xFFF) | 0xC000; + addr = ((gb->dma_current_src - 1) & 0x1000) | (addr & 0xFFF) | 0xC000; } else if (GB_is_cgb(gb) && gb->dma_current_src >= 0xe000 && addr > 0xc000) { // TODO: this should probably affect the DMA dest as well - addr = (gb->dma_current_src & 0x1000) | (addr & 0xFFF) | 0xC000; + addr = ((gb->dma_current_src - 1) & 0x1000) | (addr & 0xFFF) | 0xC000; } else { - addr = gb->dma_current_src; + addr = (gb->dma_current_src - 1); } if (GB_is_cgb(gb) || addr > 0xc000) { - gb->dma_and_pattern = addr < 0xc000? 0x00 : 0xFF; - if ((gb->model < GB_MODEL_CGB_0 || gb->model == GB_MODEL_CGB_B) && addr > 0xc000) { - gb->dma_and_pattern = value; + if (addr < 0xc000) { + gb->oam[gb->dma_current_dest - 1] = 0; } - else if ((gb->model < GB_MODEL_CGB_C || gb->model > GB_MODEL_CGB_E) && addr > 0xc000 && !oam_write) { - gb->dma_skip_write = true; - gb->oam[gb->dma_current_dest] = value; + else if ((gb->model < GB_MODEL_CGB_0 || gb->model == GB_MODEL_CGB_B)) { + gb->oam[gb->dma_current_dest - 1] &= value; + } + else if ((gb->model < GB_MODEL_CGB_C || gb->model > GB_MODEL_CGB_E) && !oam_write) { + gb->oam[gb->dma_current_dest - 1] = value; } if (gb->model < GB_MODEL_CGB_E || addr >= 0xc000) return; } @@ -1687,34 +1686,38 @@ void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) write_map[addr >> 12](gb, addr, value); } +bool GB_is_dma_active(GB_gameboy_t *gb) +{ + return gb->dma_current_dest < 0xa1 || gb->is_dma_restarting; +} + void GB_dma_run(GB_gameboy_t *gb) { - while (unlikely(gb->dma_cycles >= 4 && gb->dma_steps_left)) { - /* Todo: measure this value */ + if (gb->dma_current_dest >= 0xa1) return; + while (unlikely(gb->dma_cycles >= 4)) { gb->dma_cycles -= 4; - gb->dma_steps_left--; - if (gb->dma_skip_write) { - gb->dma_skip_write = false; - gb->dma_current_dest++; + if (gb->dma_current_dest >= 0xa1) { + gb->is_dma_restarting = false; + break; } - else if (gb->dma_current_src < 0xe000) { - gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src) & gb->dma_and_pattern; + if (gb->dma_current_dest >= 0xa0) { + gb->dma_current_dest++; + continue; + } + if (gb->dma_current_src < 0xe000) { + gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src); } else { if (GB_is_cgb(gb)) { - gb->oam[gb->dma_current_dest++] = gb->dma_and_pattern; + gb->oam[gb->dma_current_dest++] = 0; } else { - gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src & ~0x2000) & gb->dma_and_pattern; + gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src & ~0x2000); } } - gb->dma_and_pattern = 0xFF; /* dma_current_src must be the correct value during GB_read_memory */ gb->dma_current_src++; - if (!gb->dma_steps_left) { - gb->is_dma_restarting = false; - } } } diff --git a/Core/memory.h b/Core/memory.h index adfdcaa..7a78283 100644 --- a/Core/memory.h +++ b/Core/memory.h @@ -13,6 +13,7 @@ uint8_t GB_safe_read_memory(GB_gameboy_t *gb, uint16_t addr); // Without side ef void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value); #ifdef GB_INTERNAL internal void GB_dma_run(GB_gameboy_t *gb); +internal bool GB_is_dma_active(GB_gameboy_t *gb); internal void GB_hdma_run(GB_gameboy_t *gb); internal void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address); #endif diff --git a/Core/save_state.c b/Core/save_state.c index e090036..3695edb 100644 --- a/Core/save_state.c +++ b/Core/save_state.c @@ -372,11 +372,6 @@ static void sanitize_state(GB_gameboy_t *gb) if (!GB_is_cgb(gb)) { gb->current_tile_attributes = 0; } - - if ((unsigned)gb->dma_current_dest + (unsigned)gb->dma_steps_left >= 0xa0) { - gb->dma_current_dest = 0; - gb->dma_steps_left = 0; - } gb->object_low_line_address &= gb->vram_size & ~1; if (gb->lcd_x > gb->position_in_line) {