Emulating DMA delay correctly

This commit is contained in:
Lior Halphon 2016-08-06 14:24:43 +03:00
parent 4a50000e83
commit 85a33ed8ef
2 changed files with 14 additions and 7 deletions

View File

@ -244,6 +244,7 @@ typedef struct GB_gameboy_s {
uint8_t dma_current_dest;
uint16_t dma_current_src;
int16_t dma_cycles;
bool is_dma_restarting;
);
/* MBC */

View File

@ -37,7 +37,7 @@ static GB_bus_t bus_for_addr(GB_gameboy_t *gb, uint16_t addr)
static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr)
{
if (!gb->dma_steps_left) return false;
if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting)) return false;
return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src);
}
@ -107,7 +107,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 || gb->dma_steps_left) {
if ((gb->io_registers[GB_IO_STAT] & 0x3) >= 2 || (gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) {
return 0xFF;
}
return gb->oam[addr & 0xFF];
@ -342,7 +342,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 || gb->dma_steps_left) {
if ((gb->io_registers[GB_IO_STAT] & 0x3) >= 2 || (gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) {
return;
}
gb->oam[addr & 0xFF] = value;
@ -433,6 +433,13 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
case GB_IO_DMA:
if (value <= 0xE0) {
if (gb->dma_steps_left) {
/* 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_cycles = -7;
gb->dma_current_dest = 0;
gb->dma_current_src = value << 8;
@ -572,8 +579,6 @@ void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
void GB_dma_run(GB_gameboy_t *gb)
{
/* + 1 as a compensation over the fact that DMA is never started in the first internal cycle of an opcode,
and SameBoy isn't sub-cycle accurate (yet?) . */
while (gb->dma_cycles >= 4 && gb->dma_steps_left) {
/* Todo: measure this value */
gb->dma_cycles -= 4;
@ -581,14 +586,15 @@ void GB_dma_run(GB_gameboy_t *gb)
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++;
if (!gb->dma_steps_left) {
gb->is_dma_restarting = false;
}
}
}
void GB_hdma_run(GB_gameboy_t *gb)
{
if (!gb->hdma_on) return;
/* + 1 as a compensation over the fact that HDMA is never started in the first internal cycle of an opcode,
and SameBoy isn't sub-cycle accurate (yet?) . */
while (gb->hdma_cycles >= 8) {
gb->hdma_cycles -= 8;
// The CGB boot rom uses the dest in "absolute" space, while some games use it relative to VRAM.