diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/APU.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/APU.cs index d03264872f..3e00eec2cb 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/APU.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/APU.cs @@ -660,7 +660,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public DMCUnit(APU apu, bool pal) { this.apu = apu; - this.nes = apu.nes; + nes = apu.nes; out_silence = true; DMC_RATE = pal ? DMC_RATE_PAL : DMC_RATE_NTSC; timer_reload = DMC_RATE[0]; @@ -668,26 +668,28 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES sample_buffer_filled = false; out_deltacounter = 64; 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_address = 0xC000; // even though this can't be accessed by writing, it is indeed the power up address + sample_address = 0xC000; user_length = 1; } private bool irq_enabled; private bool loop_flag; - private int timer_reload; + public int timer_reload; // dmc delay per visual 2a03 - private int delay; + public int delay; // this timer never stops, ever, so it is convenient to use for even/odd timing used elsewhere public int timer; private int user_address; public uint user_length, sample_length; - private int sample_address, sample_buffer; + public int sample_address, sample_buffer; private bool sample_buffer_filled; - private int out_shift, out_bits_remaining, out_deltacounter; + public int out_shift, out_bits_remaining, out_deltacounter; private bool out_silence; + public bool fill_glitch; public int sample => out_deltacounter /* - 64*/; @@ -711,6 +713,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES ser.Sync(nameof(out_bits_remaining), ref out_bits_remaining); ser.Sync(nameof(out_deltacounter), ref out_deltacounter); ser.Sync(nameof(out_silence), ref out_silence); + ser.Sync(nameof(fill_glitch), ref fill_glitch); ser.Sync(nameof(delay), ref delay); @@ -729,15 +732,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES // Any time the sample buffer is in an empty state and bytes remaining is not zero, the following occur: // 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) + if (!sample_buffer_filled && sample_length > 0 && apu.dmc_dma_countdown == -1 && delay==0) { if (!apu.call_from_write) { // when called due to empty bueffer while DMC running, there is no delay - delay = 0; + //delay = 1; nes.cpu.RDY = false; nes.dmc_dma_exec = true; - apu.dmc_dma_countdown = 3; // 3 here but this actually stops 4 cpu cycles because it starts before the cpu is run + apu.dmc_dma_countdown = 3; apu.DMC_RDY_check = 2; } else @@ -750,7 +753,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES else { delay = 3; - } + } } } @@ -762,7 +765,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES delay--; if (delay == 0) { - if (!apu.call_from_write) + if (fill_glitch) + { + //fill_glitch = false; + apu.dmc_dma_countdown = 1; + apu.DMC_RDY_check = -1; + } + else if (!apu.call_from_write) { apu.dmc_dma_countdown = 4; apu.DMC_RDY_check = 2; @@ -825,11 +834,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public void set_lenctr_en(bool en) { - if (!en) + // should this be delayed by two/three cycles? + + if(!en) { // If the DMC bit is clear, the DMC bytes remaining will be set to 0 // and the DMC will silence when it empties. - sample_length = 0; + sample_length = 0; } else { @@ -892,6 +903,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES sample_buffer = apu.nes.ReadMemory((ushort)sample_address); sample_buffer_filled = true; sample_address = (ushort)(sample_address + 1); + + //sample address wraps to 0xC000 + if (sample_address == 0) { sample_address = 0xC000;} // Console.WriteLine(sample_length); // Console.WriteLine(user_length); sample_length--; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs index c657f0e41d..769503c9d7 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs @@ -442,7 +442,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public bool dmc_realign; public bool IRQ_delay; public bool special_case_delay; // very ugly but the only option - public bool do_the_reread; + public bool reread_trigger; + public int do_the_reread_2002, do_the_reread_2007, do_the_reread_cont_1, do_the_reread_cont_2; public byte DB; //old data bus values from previous reads internal void RunCpuOne() @@ -489,7 +490,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES if (apu.dmc_dma_countdown > 0) { - if (apu.dmc_dma_countdown == 1) + if (apu.dmc_dma_countdown == 1 && !apu.dmc.fill_glitch) { dmc_realign = true; } @@ -507,14 +508,47 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES apu.dmc_dma_countdown--; if (apu.dmc_dma_countdown == 0) { - apu.RunDMCFetch(); + if (!apu.dmc.fill_glitch) + { + reread_trigger = true; + // if the DMA address has the same bits set as the re-read address, they don't occur + if ((apu.dmc.sample_address & 0x2007) != 0x2002) + { + do_the_reread_2002++; + } + + if ((apu.dmc.sample_address & 0x2007) != 0x2007) + { + do_the_reread_2007++; + } + + if ((apu.dmc.sample_address & 0x401F) != 0x4016) + { + do_the_reread_cont_1++; + } + + if ((apu.dmc.sample_address & 0x401F) != 0x4017) + { + do_the_reread_cont_2++; + } + + apu.RunDMCFetch(); + } + dmc_dma_exec = false; apu.dmc_dma_countdown = -1; - do_the_reread = true; - - //Console.WriteLine("dmc RDY false " + cpu.TotalExecutedCycles + " " + (apu.dmc.timer & 1)); - } - //Console.WriteLine("dmc RDY false " + cpu.TotalExecutedCycles + " " + cpu.opcode + " " + cpu.mi + " " + apu.dmc_dma_countdown); + apu.dmc.fill_glitch = false; + /* + //if (apu.dmc.timer == (apu.dmc.timer_reload-0) && apu.dmc.out_bits_remaining == 7) + //if (apu.dmc.timer == 2 && apu.dmc.out_bits_remaining == 0) + { + Console.WriteLine("close " + cpu.TotalExecutedCycles + " " + apu.dmc.timer + " " + apu.dmc.sample_length); + apu.dmc.fill_glitch = true; + apu.dmc.delay = 3; + } + */ + //Console.WriteLine("dmc RDY false " + cpu.TotalExecutedCycles + " " + cpu.opcode + " " + cpu.mi + " " + apu.dmc.timer); + } } ///////////////////////////// @@ -528,7 +562,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES } else if (special_case_delay || apu.dmc_dma_countdown == 3) { + cpu.IRQ = _irq_apu || Board.IrqSignal; + //if (cpu.IRQ) { Console.WriteLine("something IRQ"); } special_case_delay = false; } @@ -546,8 +582,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES apu.RunOneLast(); - if (do_the_reread && cpu.RDY) - do_the_reread = false; + if (reread_trigger && cpu.RDY) + { + do_the_reread_2002 = 0; + do_the_reread_2007 = 0; + do_the_reread_cont_1 = 0; + do_the_reread_cont_2 = 0; + reread_trigger = false; + } + IRQ_delay = false; @@ -602,11 +645,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES // special hardware glitch case ret_spec = read_joyport(addr); - if (do_the_reread && ppu.region==PPU.Region.NTSC) + //if (reread_trigger && (do_the_reread_cont_1 == 0)) { Console.WriteLine("same 1 " + (apu.dmc.sample_address - 1)); } + + if ((do_the_reread_cont_1 > 0) && ppu.region==PPU.Region.NTSC) { ret_spec = read_joyport(addr); - do_the_reread = false; - Console.WriteLine("DMC glitch player 1 "); + do_the_reread_cont_1--; + if (do_the_reread_cont_1 > 0) { ret_spec = read_joyport(addr); } + //Console.WriteLine("DMC glitch player 1 " + cpu.TotalExecutedCycles + " addr " + (apu.dmc.sample_address - 1)); } return ret_spec; @@ -626,12 +672,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES { // special hardware glitch case ret_spec = read_joyport(addr); - if (do_the_reread && ppu.region == PPU.Region.NTSC) + + //if (reread_trigger && (do_the_reread_cont_2 == 0)) { Console.WriteLine("same 2 " + (apu.dmc.sample_address - 1)); } + + if ((do_the_reread_cont_2 > 0) && ppu.region == PPU.Region.NTSC) { ret_spec = read_joyport(addr); - do_the_reread = false; - Console.WriteLine("DMC glitch player 2"); + do_the_reread_cont_2--; + if (do_the_reread_cont_2 > 0) { ret_spec = read_joyport(addr); } + //Console.WriteLine("DMC glitch player 2 " + cpu.TotalExecutedCycles + " addr " + (apu.dmc.sample_address - 1)); } + return ret_spec; } default: diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IStatable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IStatable.cs index f3c0d2f973..ff198a874c 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IStatable.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IStatable.cs @@ -31,7 +31,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES ser.Sync(nameof(dmc_realign), ref dmc_realign); ser.Sync(nameof(IRQ_delay), ref IRQ_delay); ser.Sync(nameof(special_case_delay), ref special_case_delay); - ser.Sync(nameof(do_the_reread), ref do_the_reread); + ser.Sync(nameof(reread_trigger), ref reread_trigger); + ser.Sync(nameof(do_the_reread_2002), ref do_the_reread_2002); + ser.Sync(nameof(do_the_reread_2007), ref do_the_reread_2007); + ser.Sync(nameof(do_the_reread_cont_1), ref do_the_reread_cont_1); + ser.Sync(nameof(do_the_reread_cont_2), ref do_the_reread_cont_2); // VS related ser.Sync("VS", ref _isVS); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.regs.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.regs.cs index b0489acbff..27be45d8ce 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.regs.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.regs.cs @@ -345,12 +345,24 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES private byte read_2002() { byte ret = peek_2002(); - + + if (nes.do_the_reread_2002 > 0) + { + if (Reg2002_vblank_active || Reg2002_vblank_active_pending) + Console.WriteLine("reread 2002"); + } + // reading from $2002 resets the destination for $2005 and $2006 writes vtoggle = false; Reg2002_vblank_active = 0; Reg2002_vblank_active_pending = false; + if (nes.do_the_reread_2002 > 0) + { + ret = peek_2002(); + // could be another reread, but no other side effects, so don't bother + } + // update the open bus here ppu_open_bus = ret; PpuOpenBusDecay(DecayType.High); @@ -679,11 +691,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES double_2007_read = nes.cpu.TotalExecutedCycles + 1; } - if (nes.do_the_reread) + if (nes.do_the_reread_2007 > 0) { ret_spec = read_2007(); ret_spec = read_2007(); - nes.do_the_reread = false; + // always 2? } return ret_spec; }