From 47e3300b669742576b6dfd7edb3e2fc3f030cf96 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Wed, 3 Aug 2016 23:31:10 +0300 Subject: [PATCH] Improved DMA accuracy, mooneyegb test ROMs no longer crash miserably. (but still fail) --- Core/gb.c | 4 ++-- Core/gb.h | 11 ++++++--- Core/memory.c | 65 +++++++++++++++++++++++++++++++++++++++++---------- Core/memory.h | 1 + Core/timing.c | 9 ++----- 5 files changed, 66 insertions(+), 24 deletions(-) diff --git a/Core/gb.c b/Core/gb.c index 79848fb..0a40c46 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -230,7 +230,7 @@ int GB_save_state(GB_gameboy_t *gb, const char *path) if (fwrite(GB_GET_SECTION(gb, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error; if (!DUMP_SECTION(gb, f, core_state)) goto error; - if (!DUMP_SECTION(gb, f, hdma )) goto error; + if (!DUMP_SECTION(gb, f, dma )) goto error; if (!DUMP_SECTION(gb, f, mbc )) goto error; if (!DUMP_SECTION(gb, f, hram )) goto error; if (!DUMP_SECTION(gb, f, timing )) goto error; @@ -297,7 +297,7 @@ int GB_load_state(GB_gameboy_t *gb, const char *path) if (fread(GB_GET_SECTION(&save, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error; if (!READ_SECTION(&save, f, core_state)) goto error; - if (!READ_SECTION(&save, f, hdma )) goto error; + if (!READ_SECTION(&save, f, dma )) goto error; if (!READ_SECTION(&save, f, mbc )) goto error; if (!READ_SECTION(&save, f, hram )) goto error; if (!READ_SECTION(&save, f, timing )) goto error; diff --git a/Core/gb.h b/Core/gb.h index 9de63cd..3ad0cf0 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -224,13 +224,18 @@ typedef struct GB_gameboy_s { bool infrared_input; ); - /* HDMA */ - GB_SECTION(hdma, + /* DMA and HDMA */ + GB_SECTION(dma, bool hdma_on; bool hdma_on_hblank; uint8_t hdma_steps_left; uint16_t hdma_cycles; uint16_t hdma_current_src, hdma_current_dest; + + uint8_t dma_steps_left; + uint8_t dma_current_dest; + uint16_t dma_current_src; + uint16_t dma_cycles; ); /* MBC */ @@ -278,7 +283,7 @@ typedef struct GB_gameboy_s { uint32_t display_cycles; uint32_t div_cycles; uint32_t tima_cycles; - uint32_t dma_cycles; + GB_PADDING(uint32_t, dma_cycles); GB_aligned_double apu_cycles; ); diff --git a/Core/memory.c b/Core/memory.c index 89f67b5..d81c386 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -10,6 +10,36 @@ typedef uint8_t GB_read_function_t(GB_gameboy_t *gb, uint16_t addr); typedef void GB_write_function_t(GB_gameboy_t *gb, uint16_t addr, uint8_t value); +typedef enum { + GB_BUS_MAIN, /* In DMG: Cart and RAM. In CGB: Cart only */ + GB_BUS_RAM, /* In CGB only. */ + GB_BUS_VRAM, + GB_BUS_INTERNAL, /* Anything in highram. Might not be the most correct name. */ +} GB_bus_t; + +static GB_bus_t bus_for_addr(GB_gameboy_t *gb, uint16_t addr) +{ + if (addr < 0x8000) { + return GB_BUS_MAIN; + } + if (addr < 0xA000) { + return GB_BUS_VRAM; + } + if (addr < 0xC000) { + return GB_BUS_MAIN; + } + if (addr < 0xFE00) { + return gb->is_cgb? GB_BUS_RAM : GB_BUS_MAIN; + } + return GB_BUS_INTERNAL; +} + +static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr) +{ + if (!gb->dma_steps_left) return false; + return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src); +} + static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr) { if (addr < 0x100 && !gb->boot_rom_finished) { @@ -76,7 +106,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) } if (addr < 0xFEA0) { - if ((gb->io_registers[GB_IO_STAT] & 0x3) >= 2) { + if ((gb->io_registers[GB_IO_STAT] & 0x3) >= 2 || gb->dma_steps_left) { return 0xFF; } return gb->oam[addr & 0xFF]; @@ -202,9 +232,8 @@ static GB_read_function_t * const read_map[] = uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr) { GB_debugger_test_read_watchpoint(gb, addr); - if (addr < 0xFF00 && gb->dma_cycles) { - /* Todo: can we access IO registers during DMA? */ - return 0xFF; + if (is_addr_in_dma_use(gb, addr)) { + addr = gb->dma_current_src; } return read_map[addr >> 12](gb, addr); } @@ -297,7 +326,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) } if (addr < 0xFEA0) { - if ((gb->io_registers[GB_IO_STAT] & 0x3) >= 2) { + if ((gb->io_registers[GB_IO_STAT] & 0x3) >= 2 || gb->dma_steps_left) { return; } gb->oam[addr & 0xFF] = value; @@ -371,13 +400,13 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) case GB_IO_DMA: if (value <= 0xF1) { /* According to Pan Docs */ - for (uint8_t i = 0xA0; i--;) { - gb->oam[i] = GB_read_memory(gb, (value << 8) + i); - } + gb->dma_cycles = 0; + gb->dma_current_dest = 0; + gb->dma_current_src = value << 8; + gb->dma_steps_left = 0xa0; } /* else { what? } */ - /* Todo: measure this value */ - gb->dma_cycles = 640; + return; case GB_IO_SVBK: if (!gb->cgb_mode) { @@ -501,13 +530,25 @@ static GB_write_function_t * const write_map[] = void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) { GB_debugger_test_write_watchpoint(gb, addr, value); - if (addr < 0xFF00 && gb->dma_cycles) { - /* Todo: can we access IO registers during DMA? */ + if (is_addr_in_dma_use(gb, addr)) { + /* Todo: What should happen? Will this affect DMA? Will data be written? What and where? */ return; } write_map[addr >> 12](gb, addr, value); } +void GB_dma_run(GB_gameboy_t *gb) +{ + while (gb->dma_cycles >= 4 && gb->dma_steps_left) { + /* Todo: measure this value */ + gb->dma_cycles -= 4; + gb->dma_steps_left--; + gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src); + /* dma_current_src must be the correct value during GB_read_memory */ + gb->dma_current_src++; + } +} + void GB_hdma_run(GB_gameboy_t *gb) { if (!gb->hdma_on) return; diff --git a/Core/memory.h b/Core/memory.h index f904f8c..b106742 100644 --- a/Core/memory.h +++ b/Core/memory.h @@ -4,6 +4,7 @@ uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr); void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value); +void GB_dma_run(GB_gameboy_t *gb); void GB_hdma_run(GB_gameboy_t *gb); #endif /* memory_h */ diff --git a/Core/timing.c b/Core/timing.c index beffa71..babafb5 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -17,13 +17,7 @@ static void GB_ir_run(GB_gameboy_t *gb) void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) { // Affected by speed boost - if (gb->dma_cycles > cycles){ - gb->dma_cycles -= cycles; - } - else { - gb->dma_cycles = 0; - } - + gb->dma_cycles += cycles; gb->div_cycles += cycles; gb->tima_cycles += cycles; @@ -37,6 +31,7 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) gb->apu_cycles += cycles; gb->cycles_since_ir_change += cycles; gb->cycles_since_input_ir_change += cycles; + GB_dma_run(gb); GB_hdma_run(gb); GB_timers_run(gb); GB_apu_run(gb);