diff --git a/Core/display.c b/Core/display.c index 3023357..ae7d757 100644 --- a/Core/display.c +++ b/Core/display.c @@ -1779,7 +1779,7 @@ skip_slow_mode_3: GB_SLEEP(gb, display, 25, 8); if (gb->hdma_on_hblank) { - gb->hdma_starting = true; + gb->hdma_on = true; } GB_SLEEP(gb, display, 11, LINE_LENGTH - gb->cycles_for_line - 2); /* diff --git a/Core/gb.c b/Core/gb.c index fb0965f..adeed1e 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -1128,7 +1128,7 @@ exit: return; } -uint8_t GB_run(GB_gameboy_t *gb) +unsigned GB_run(GB_gameboy_t *gb) { gb->vblank_just_occured = false; diff --git a/Core/gb.h b/Core/gb.h index 76f6192..d1add0b 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -421,7 +421,6 @@ struct GB_gameboy_internal_s { bool hdma_on; bool hdma_on_hblank; uint8_t hdma_steps_left; - int16_t hdma_cycles; // in 8MHz units uint16_t hdma_current_src, hdma_current_dest; uint8_t dma_current_dest; @@ -431,8 +430,7 @@ struct GB_gameboy_internal_s { 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; + uint8_t hdma_open_bus; /* Required to emulate HDMA reads from Exxx */ ) /* MBC */ @@ -767,7 +765,7 @@ struct GB_gameboy_internal_s { bool disable_rendering; uint8_t boot_rom[0x900]; bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank - uint8_t cycles_since_run; // How many cycles have passed since the last call to GB_run(), in 8MHz units + unsigned cycles_since_run; // How many cycles have passed since the last call to GB_run(), in 8MHz units double clock_multiplier; GB_rumble_mode_t rumble_mode; uint32_t rumble_on_cycles; @@ -808,7 +806,7 @@ void GB_reset(GB_gameboy_t *gb); void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model); /* Returns the time passed, in 8MHz ticks. */ -uint8_t GB_run(GB_gameboy_t *gb); +unsigned GB_run(GB_gameboy_t *gb); /* Returns the time passed since the last frame, in nanoseconds */ uint64_t GB_run_frame(GB_gameboy_t *gb); diff --git a/Core/memory.c b/Core/memory.c index da72ebe..805c313 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -475,11 +475,6 @@ static inline void sync_ppu_if_needed(GB_gameboy_t *gb, uint8_t register_accesse static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) { - - if (gb->hdma_on) { - return gb->last_opcode_read; - } - if (addr < 0xFE00) { return read_banked_ram(gb, addr); } @@ -1534,6 +1529,10 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) gb->hdma_current_src &= 0xF0; gb->hdma_current_src |= value << 8; } + /* Range 0xE*** like 0xF*** and can't overflow (with 0x800 bytes) to anything meaningful */ + if (gb->hdma_current_src >= 0xE000) { + gb->hdma_current_src |= 0xF000; + } return; case GB_IO_HDMA2: if (gb->cgb_mode) { @@ -1545,6 +1544,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) if (gb->cgb_mode) { gb->hdma_current_dest &= 0xF0; gb->hdma_current_dest |= value << 8; + gb->hdma_current_dest &= 0x1FF0; } return; case GB_IO_HDMA4: @@ -1570,7 +1570,6 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) if (gb->hdma_current_dest + (gb->hdma_steps_left << 4) > 0xFFFF) { gb->hdma_steps_left = (0x10000 - gb->hdma_current_dest) >> 4; } - gb->hdma_cycles = -12; return; /* Todo: what happens when starting a transfer during a transfer? @@ -1731,24 +1730,32 @@ void GB_dma_run(GB_gameboy_t *gb) void GB_hdma_run(GB_gameboy_t *gb) { - if (likely(!gb->hdma_on)) return; - - while (gb->hdma_cycles >= 0x4) { - gb->hdma_cycles -= 0x4; - - GB_write_memory(gb, 0x8000 | (gb->hdma_current_dest++ & 0x1FFF), GB_read_memory(gb, (gb->hdma_current_src++))); + unsigned cycles = gb->cgb_double_speed? 4 : 2; + /* This is a bit cart, revision and unit specific. TODO: what if PC is in cart RAM? */ + if (gb->model < GB_MODEL_CGB_D || gb->pc > 0x8000) { + gb->hdma_open_bus = 0xFF; + } + GB_advance_cycles(gb, 4); + while (gb->hdma_on) { + uint8_t byte = gb->hdma_open_bus; + 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); + gb->hdma_open_bus = 0xFF; if ((gb->hdma_current_dest & 0xf) == 0) { if (--gb->hdma_steps_left == 0) { gb->hdma_on = false; gb->hdma_on_hblank = false; - gb->hdma_starting = false; gb->io_registers[GB_IO_HDMA5] &= 0x7F; - break; } - if (gb->hdma_on_hblank) { + else if (gb->hdma_on_hblank) { gb->hdma_on = false; - break; } } } diff --git a/Core/sm83_cpu.c b/Core/sm83_cpu.c index e3668d0..1bf8094 100644 --- a/Core/sm83_cpu.c +++ b/Core/sm83_cpu.c @@ -1580,9 +1580,8 @@ static opcode_t *opcodes[256] = { }; void GB_cpu_run(GB_gameboy_t *gb) { - if (gb->hdma_on) { - GB_advance_cycles(gb, 4); - return; + if (unlikely(gb->hdma_on && (gb->stopped || gb->halted))) { + GB_hdma_run(gb); } if (gb->stopped) { GB_timing_sync(gb); @@ -1635,7 +1634,7 @@ void GB_cpu_run(GB_gameboy_t *gb) gb->speed_switch_halt_countdown = 0; uint16_t call_addr = gb->pc; - gb->last_opcode_read = cycle_read(gb, gb->pc++); + cycle_read(gb, gb->pc++); cycle_oam_bug_pc(gb); gb->pc--; GB_trigger_oam_bug(gb, gb->sp); /* Todo: test T-cycle timing */ @@ -1674,22 +1673,22 @@ void GB_cpu_run(GB_gameboy_t *gb) } /* Run mode */ else if (!gb->halted) { - gb->last_opcode_read = cycle_read(gb, gb->pc++); + uint8_t opcode = gb->hdma_open_bus = cycle_read(gb, gb->pc++); + if (unlikely(gb->hdma_on)) { + GB_hdma_run(gb); + } if (unlikely(gb->execution_callback)) { - gb->execution_callback(gb, gb->pc - 1, gb->last_opcode_read); + gb->execution_callback(gb, gb->pc - 1, opcode); } if (unlikely(gb->halt_bug)) { gb->pc--; gb->halt_bug = false; } - opcodes[gb->last_opcode_read](gb, gb->last_opcode_read); + opcodes[opcode](gb, opcode); + } + else if (gb->hdma_on) { + GB_hdma_run(gb); } flush_pending_cycles(gb); - - if (gb->hdma_starting) { - gb->hdma_starting = false; - gb->hdma_on = true; - gb->hdma_cycles = -8; - } } diff --git a/Core/timing.c b/Core/timing.c index 7525eeb..ec10526 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -420,7 +420,6 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) if (likely(gb->io_registers[GB_IO_LCDC] & 0x80)) { gb->double_speed_alignment += cycles; } - gb->hdma_cycles += cycles; gb->apu_output.sample_cycles += cycles * gb->apu_output.sample_rate; gb->cycles_since_last_sync += cycles; gb->cycles_since_run += cycles; @@ -432,7 +431,6 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) GB_display_run(gb, cycles, false); if (unlikely(!gb->stopped)) { // TODO: Verify what happens in STOP mode GB_dma_run(gb); - GB_hdma_run(gb); } ir_run(gb, cycles); rtc_run(gb, cycles);