diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs index f35b89ae37..e8f52c27b2 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs @@ -385,10 +385,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES //at least it should be, but something is off with that (start up time?) so it is 3,3,3,4,3 for now //NTSC: //sequence of ppu clocks per cpu clock: 3 - ByteBuffer cpu_sequence; + public ByteBuffer cpu_sequence; static ByteBuffer cpu_sequence_NTSC = new ByteBuffer(new byte[] { 3, 3, 3, 3, 3 }); static ByteBuffer cpu_sequence_PAL = new ByteBuffer(new byte[] { 3, 3, 3, 4, 3 }); - public int cpu_step, cpu_stepcounter, cpu_deadcounter; + public int cpu_deadcounter; public int oam_dma_index; public bool oam_dma_exec = false; @@ -401,140 +401,122 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public bool do_the_reread; public byte DB; //old data bus values from previous reads - -#if VS2012 - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif internal void RunCpuOne() { - cpu_stepcounter++; - if (cpu_stepcounter == cpu_sequence[cpu_step]) + /////////////////////////// + // OAM DMA start + /////////////////////////// + + if (sprdma_countdown > 0) { - cpu_step++; - if (cpu_step == 5) cpu_step = 0; - cpu_stepcounter = 0; - - /////////////////////////// - // OAM DMA start - /////////////////////////// - - if (sprdma_countdown > 0) + sprdma_countdown--; + if (sprdma_countdown == 0) { - sprdma_countdown--; - if (sprdma_countdown == 0) + if (cpu.TotalExecutedCycles % 2 == 0) { - if (cpu.TotalExecutedCycles % 2 == 0) - { - cpu_deadcounter = 2; - } - else - { - cpu_deadcounter = 1; - } - oam_dma_exec = true; - cpu.RDY = false; - oam_dma_index = 0; - special_case_delay = true; - } - } - - if (oam_dma_exec && apu.dmc_dma_countdown != 1 && !dmc_realign) - { - if (cpu_deadcounter == 0) - { - - if (oam_dma_index % 2 == 0) - { - oam_dma_byte = ReadMemory(oam_dma_addr); - oam_dma_addr++; - } - else - { - WriteMemory(0x2004, oam_dma_byte); - } - oam_dma_index++; - if (oam_dma_index == 512) oam_dma_exec = false; - + cpu_deadcounter = 2; } else { - cpu_deadcounter--; + cpu_deadcounter = 1; } - } - else if (apu.dmc_dma_countdown == 1) - { - dmc_realign = true; - } - else if (dmc_realign) - { - dmc_realign = false; - } - ///////////////////////////// - // OAM DMA end - ///////////////////////////// - - - ///////////////////////////// - // dmc dma start - ///////////////////////////// - - if (apu.dmc_dma_countdown > 0) - { + oam_dma_exec = true; cpu.RDY = false; - dmc_dma_exec = true; - apu.dmc_dma_countdown--; - if (apu.dmc_dma_countdown == 0) - { - apu.RunDMCFetch(); - dmc_dma_exec = false; - apu.dmc_dma_countdown = -1; - do_the_reread = true; - } + oam_dma_index = 0; + special_case_delay = true; } - - ///////////////////////////// - // dmc dma end - ///////////////////////////// - apu.RunOne(true); - - if (cpu.RDY && !IRQ_delay) - { - cpu.IRQ = _irq_apu || Board.IRQSignal; - } - else if (special_case_delay || apu.dmc_dma_countdown == 3) - { - cpu.IRQ = _irq_apu || Board.IRQSignal; - special_case_delay = false; - } - - cpu.ExecuteOne(); - apu.RunOne(false); - - if (ppu.double_2007_read > 0) - ppu.double_2007_read--; - - if (do_the_reread && cpu.RDY) - do_the_reread = false; - - if (IRQ_delay) - IRQ_delay = false; - - if (!dmc_dma_exec && !oam_dma_exec && !cpu.RDY) - { - cpu.RDY = true; - IRQ_delay = true; - } - - ppu.ppu_open_bus_decay(0); - - Board.ClockCPU(); - ppu.PostCpuInstructionOne(); } + + if (oam_dma_exec && apu.dmc_dma_countdown != 1 && !dmc_realign) + { + if (cpu_deadcounter == 0) + { + + if (oam_dma_index % 2 == 0) + { + oam_dma_byte = ReadMemory(oam_dma_addr); + oam_dma_addr++; + } + else + { + WriteMemory(0x2004, oam_dma_byte); + } + oam_dma_index++; + if (oam_dma_index == 512) oam_dma_exec = false; + + } + else + { + cpu_deadcounter--; + } + } + else if (apu.dmc_dma_countdown == 1) + { + dmc_realign = true; + } + else if (dmc_realign) + { + dmc_realign = false; + } + ///////////////////////////// + // OAM DMA end + ///////////////////////////// + + + ///////////////////////////// + // dmc dma start + ///////////////////////////// + + if (apu.dmc_dma_countdown > 0) + { + cpu.RDY = false; + dmc_dma_exec = true; + apu.dmc_dma_countdown--; + if (apu.dmc_dma_countdown == 0) + { + apu.RunDMCFetch(); + dmc_dma_exec = false; + apu.dmc_dma_countdown = -1; + do_the_reread = true; + } + } + + ///////////////////////////// + // dmc dma end + ///////////////////////////// + apu.RunOne(true); + + if (cpu.RDY && !IRQ_delay) + { + cpu.IRQ = _irq_apu || Board.IRQSignal; + } + else if (special_case_delay || apu.dmc_dma_countdown == 3) + { + cpu.IRQ = _irq_apu || Board.IRQSignal; + special_case_delay = false; + } + + cpu.ExecuteOne(); + apu.RunOne(false); + + if (ppu.double_2007_read > 0) + ppu.double_2007_read--; + + if (do_the_reread && cpu.RDY) + do_the_reread = false; + + if (IRQ_delay) + IRQ_delay = false; + + if (!dmc_dma_exec && !oam_dma_exec && !cpu.RDY) + { + cpu.RDY = true; + IRQ_delay = true; + } + + Board.ClockCPU(); } -#if VS2012 - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif public byte ReadReg(int addr) { byte ret_spec; @@ -681,7 +663,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES case 0x4013: apu.WriteReg(addr, val); break; - case 0x4014: Exec_OAMDma(val); break; + case 0x4014: + //schedule a sprite dma event for beginning 1 cycle in the future. + //this receives 2 because thats just the way it works out. + oam_dma_addr = (ushort)(val << 8); + sprdma_countdown = 1; + break; case 0x4015: apu.WriteReg(addr, val); break; case 0x4016: if (_isVS) @@ -740,15 +727,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES return 0; } - void Exec_OAMDma(byte val) - { - //schedule a sprite dma event for beginning 1 cycle in the future. - //this receives 2 because thats just the way it works out. - oam_dma_addr = (ushort)(val << 8); - - sprdma_countdown = 1; - } - /// /// Sets the provided palette as current. /// Applies the current deemph settings if needed to expand a 64-entry palette to 512 diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IStatable.cs index 8cc0a7276c..e5ac4f54b9 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IStatable.cs @@ -60,8 +60,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES ser.Sync("cpu_accumulate", ref cpu_accumulate); ser.Sync("_irq_apu", ref _irq_apu); ser.Sync("sprdma_countdown", ref sprdma_countdown); - ser.Sync("cpu_step", ref cpu_step); - ser.Sync("cpu_stepcounter", ref cpu_stepcounter); ser.Sync("cpu_deadcounter", ref cpu_deadcounter); //oam related diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.cs index ac1ad692f0..ebf93f8361 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.cs @@ -1,6 +1,4 @@ -//http://nesdev.parodius.com/bbs/viewtopic.php?p=4571&sid=db4c7e35316cc5d734606dd02f11dccb - -using System; +using System; using System.Runtime.CompilerServices; using BizHawk.Common; @@ -8,6 +6,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES { public sealed partial class PPU { + public int cpu_step, cpu_stepcounter; + // this only handles region differences within the PPU int preNMIlines; int postNMIlines; @@ -203,6 +203,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public void SyncState(Serializer ser) { + ser.Sync("cpu_step", ref cpu_step); + ser.Sync("cpu_stepcounter", ref cpu_stepcounter); ser.Sync("ppudead", ref ppudead); ser.Sync("idleSynch", ref idleSynch); ser.Sync("NMI_PendingInstructions", ref NMI_PendingInstructions); @@ -255,32 +257,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES ppu_open_bus_decay_timer = new int[8]; } -#if VS2012 - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - void TriggerNMI() - { - nes.cpu.NMI = true; - } - - //this gets called once after each cpu instruction executes. - //anything that needs to happen at instruction granularity can get checked here - //to save having to check it at ppu cycle granularity - public void PostCpuInstructionOne() - { - if (NMI_PendingInstructions > 0) - { - NMI_PendingInstructions--; - if (NMI_PendingInstructions <= 0) - { - TriggerNMI(); - } - } - } - -#if VS2012 - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif void runppu(int x) { //run one ppu cycle at a time so we can interact with the ppu and clockPPU at high granularity @@ -318,13 +294,36 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES ppur.status.cycle++; is_even_cycle = !is_even_cycle; - //might not actually run a cpu cycle if there are none to be run right now - nes.RunCpuOne(); + + // Here we execute a CPU instruction if enough PPU cycles have passed + // also do other things that happen at instruction level granularity + cpu_stepcounter++; + if (cpu_stepcounter == nes.cpu_sequence[cpu_step]) + { + cpu_step++; + if (cpu_step == 5) cpu_step = 0; + cpu_stepcounter = 0; + + // this is where the CPU instruction is called + nes.RunCpuOne(); + + // decay the ppu bus, approximating real behaviour + ppu_open_bus_decay(0); + + // Check for NMIs + if (NMI_PendingInstructions > 0) + { + NMI_PendingInstructions--; + if (NMI_PendingInstructions <= 0) + { + nes.cpu.NMI = true; + } + } + } if (Reg2002_vblank_active_pending) { - //if (Reg2002_vblank_active_pending) - Reg2002_vblank_active = 1; + Reg2002_vblank_active = 1; Reg2002_vblank_active_pending = false; } @@ -337,10 +336,5 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES nes.Board.ClockPPU(); } } - - //hack - //public bool PAL = false; - //bool SPRITELIMIT = true; - } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.regs.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.regs.cs index 8b9a487054..5bfcf77aff 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.regs.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.regs.cs @@ -309,6 +309,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public Reg_2000 reg_2000; public Reg_2001 reg_2001; byte reg_2003; + void regs_reset() { //TODO - would like to reconstitute the entire PPU instead of all this.. @@ -396,12 +397,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES return (byte)((Reg2002_vblank_active << 7) | (Reg2002_objhit << 6) | (Reg2002_objoverflow << 5) | (ppu_open_bus & 0x1F)); } - void clear_2002() - { - Reg2002_objhit = Reg2002_objoverflow = 0; - Reg2002_vblank_clear_pending = true; - } - //OAM ADDRESS (write) void write_2003(int addr, byte value) { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.run.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.run.cs index db120d9c03..e98bdfe145 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.run.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.run.cs @@ -1,6 +1,4 @@ -//http://nesdev.parodius.com/bbs/viewtopic.php?p=4571&sid=db4c7e35316cc5d734606dd02f11dccb - -//TODO - correctly emulate PPU OFF state +//TODO - correctly emulate PPU OFF state using BizHawk.Common; using System; @@ -10,6 +8,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES sealed partial class PPU { const int kFetchTime = 2; + const int kLineTime = 341; struct BGDataRecord { @@ -78,7 +77,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES if (reg_2001.color_disable) pixelcolor_latch_2 &= 0x30; - xbuf[(target - 2)] = PaletteAdjustPixel(pixelcolor_latch_2); + //TODO - check flashing sirens in werewolf + //tack on the deemph bits. THESE MAY BE ORDERED WRONG. PLEASE CHECK IN THE PALETTE CODE + xbuf[(target - 2)] = (short)(pixelcolor_latch_2 | reg_2001.intensity_lsl_6); } if (row_check >= 1) @@ -158,14 +159,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES } //switch(cycle) } - //TODO - check flashing sirens in werewolf - short PaletteAdjustPixel(int pixel) - { - //tack on the deemph bits. THESE MAY BE ORDERED WRONG. PLEASE CHECK IN THE PALETTE CODE - return (short)(pixel | reg_2001.intensity_lsl_6); - } - - const int kLineTime = 341; public unsafe void FrameAdvance() { BGDataRecord* bgdata = stackalloc BGDataRecord[34]; //one at the end is junk, it can never be rendered @@ -191,12 +184,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES runppu(3); bool nmi_destiny = reg_2000.vblank_nmi_gen && Reg2002_vblank_active; runppu(3); - if (nmi_destiny) TriggerNMI(); + if (nmi_destiny) nes.cpu.NMI = true; nes.Board.AtVsyncNMI(); runppu(postNMIlines * kLineTime - delay); - //this seems to run just before the dummy scanline begins - clear_2002(); + //this seems to happen just before the dummy scanline begins + Reg2002_objhit = Reg2002_objoverflow = 0; + Reg2002_vblank_clear_pending = true; idleSynch ^= true; @@ -224,8 +218,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES yp = sl - 1; ppuphase = PPUPHASE.BG; - // "If PPUADDR is not less then 8 when rendering starts, the first 8 fights in OAM and written to from - // the current location off PPUADDR" + // "If PPUADDR is not less then 8 when rendering starts, the first 8 bytes in OAM and written to from + // the current location off PPUADDR" if (sl == 0 && PPUON && reg_2003 >= 8 && region==Region.NTSC) { for (int i = 0; i < 8; i++) @@ -233,7 +227,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES OAM[i] = OAM[reg_2003 & 0xF8 + i]; } } - + if (NTViewCallback != null && yp == NTViewCallback.Scanline) NTViewCallback.Callback(); if (PPUViewCallback != null && yp == PPUViewCallback.Scanline) PPUViewCallback.Callback(); @@ -255,7 +249,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES //check all the conditions that can cause things to render in these 8px bool renderspritenow = show_obj_new && (xt > 0 || reg_2001.show_obj_leftmost); - bool renderbgnow; + bool renderbgnow; + bool hit_pending = false; for (int xp = 0; xp < 8; xp++, rasterpos++) { @@ -350,7 +345,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES { if (yp >= read_value && yp < read_value + spriteHeight && PPUON) { - Reg2002_objoverflow = true; + hit_pending = true; + //Reg2002_objoverflow = true; } if (yp >= read_value && yp < read_value + spriteHeight && spr_true_count == 0) @@ -400,6 +396,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES else runppu(1); + if (hit_pending) + { + hit_pending = false; + Reg2002_objoverflow = true; + } + renderbgnow = show_bg_new && (xt > 0 || reg_2001.show_bg_leftmost); //bg pos is different from raster pos due to its offsetability. //so adjust for that here