NESHawk: Improve DMC emulation

This commit is contained in:
alyosha-tas 2021-06-30 16:53:15 -04:00
parent 858faaeb56
commit e0498c06ad
2 changed files with 23 additions and 17 deletions

View File

@ -22,6 +22,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
public int m_vol = 1;
public int dmc_dma_countdown = -1;
public int DMC_RDY_check;
public bool call_from_write;
public bool recalculate = false;
@ -661,10 +662,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
out_silence = true;
DMC_RATE = pal ? DMC_RATE_PAL : DMC_RATE_NTSC;
timer_reload = DMC_RATE[0];
timer = timer_reload;
timer = 1024-1; // confirmed in VisualNES although aligning controller read glitches still doesn't work
sample_buffer_filled = false;
out_deltacounter = 64;
out_bits_remaining = 0;
out_bits_remaining = 7; //confirmed in VisualNES
user_address = 0x8000; // even though this can't be accessed by writing, it is indeed the power up address
user_length = 1;
}
@ -709,7 +710,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
ser.Sync(nameof(out_deltacounter), ref out_deltacounter);
ser.Sync(nameof(out_silence), ref out_silence);
ser.Sync("dmc_call_delay", ref delay);
ser.Sync(nameof(delay), ref delay);
ser.EndSection();
}
@ -728,19 +729,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
// also note that the halt for DMC DMA occurs on APU cycles only (hence the timer check)
if (!sample_buffer_filled && sample_length > 0 && apu.dmc_dma_countdown == -1 && delay==0)
{
// calls from write take one less cycle, but start on a write instead of a read
if (!apu.call_from_write)
{
if (timer % 2 == 1)
{
delay = 3;
} else
{
delay = 2;
}
// when called due to empty bueffer while DMC running, there is no delay
delay = 1;
}
else
{
// when called from write, either a 2 or 3 cycle delay in activation.
if (timer % 2 == 1)
{
delay = 2;
@ -752,8 +748,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
}
}
// I did some tests in Visual 2A03 and there seems to be some delay betwen when a DMC is first needed and when the
// process to execute the DMA starts. The details are not currently known, but it seems to be a 2 cycle delay
// VisualNES and test roms verify that DMC DMA is 1 cycle shorter for calls from writes
// however, if the third cycle lands on a write, there is a 2 cycle delay even if the instruction only has a single write
// therefore, the RDY_check is 2 in both cases.
if (delay != 0)
{
delay--;
@ -762,11 +759,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
if (!apu.call_from_write)
{
apu.dmc_dma_countdown = 4;
apu.DMC_RDY_check = 2;
}
else
{
apu.dmc_dma_countdown = 3;
apu.DMC_RDY_check = 2;
apu.call_from_write = false;
}
}
@ -923,6 +922,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
ser.Sync(nameof(sequencer_irq_assert), ref sequencer_irq_assert);
ser.Sync(nameof(dmc_dma_countdown), ref dmc_dma_countdown);
ser.Sync(nameof(DMC_RDY_check), ref DMC_RDY_check);
ser.Sync("sample_length_delay", ref pending_length_change);
ser.Sync("dmc_called_from_write", ref call_from_write);
ser.Sync("sequencer_tick_delay", ref seq_tick);
@ -1164,7 +1164,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
}
else if (addr == 0x4017)
{
if (dmc.timer%2==0)
if (dmc.timer % 2 == 1)
{
seq_tick = 3;
@ -1273,7 +1273,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
_WriteReg(pending_reg, pending_val);
pending_reg = -1;
}
else if (dmc.timer % 2 == 0)
else if (dmc.timer % 2 == 1)
{
_WriteReg(pending_reg, pending_val);
pending_reg = -1;

View File

@ -493,9 +493,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
}
// By this point the cpu should be frozen, if it is not, then we are in a multi-write opcode, add another cycle delay
if (!cpu.RDY && !cpu.rdy_freeze && ((cpu.TotalExecutedCycles & 1) == 1))
if (!cpu.RDY && !cpu.rdy_freeze && (apu.dmc_dma_countdown == apu.DMC_RDY_check))
{
apu.dmc_dma_countdown+=2;
//Console.WriteLine("dmc RDY false " + cpu.TotalExecutedCycles + " " + apu.call_from_write + " " + cpu.opcode + " " + oam_dma_exec);
apu.dmc_dma_countdown += 2;
apu.DMC_RDY_check = -1;
}
cpu.RDY = false;
@ -508,6 +510,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
apu.dmc_dma_countdown = -1;
do_the_reread = true;
}
//Console.WriteLine("dmc RDY false " + cpu.TotalExecutedCycles + " " + apu.call_from_write + " " + cpu.opcode);
}
/////////////////////////////
@ -598,6 +602,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
{
ret_spec = read_joyport(addr);
do_the_reread = false;
Console.WriteLine("DMC glitch player 1");
}
return ret_spec;
@ -622,6 +627,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
{
ret_spec = read_joyport(addr);
do_the_reread = false;
Console.WriteLine("DMC glitch player 2");
}
return ret_spec;
}