From 4e1decfa213abba312cd8933eb112dc4d92305ec Mon Sep 17 00:00:00 2001 From: zeromus Date: Thu, 15 Jun 2017 15:01:50 -0500 Subject: [PATCH 1/9] add LuaCanvas DrawArc missing from last luacanvas PR merge --- BizHawk.Client.EmuHawk/tools/Lua/LuaCanvas.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/BizHawk.Client.EmuHawk/tools/Lua/LuaCanvas.cs b/BizHawk.Client.EmuHawk/tools/Lua/LuaCanvas.cs index 8319f7b25b..6336b17a9d 100644 --- a/BizHawk.Client.EmuHawk/tools/Lua/LuaCanvas.cs +++ b/BizHawk.Client.EmuHawk/tools/Lua/LuaCanvas.cs @@ -283,6 +283,16 @@ namespace BizHawk.Client.EmuHawk DrawLine(x, y + size, x, y - size, color); } + [LuaMethodAttributes( + "drawArc", + "draws a Arc shape at the given coordinates and the given width and height" + )] + public void DrawArc(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null) + { + var pen = new Pen(line.HasValue ? line.Value : Color.Black); + _graphics.DrawArc(pen, x, y, width, height, startangle, sweepangle); + } + [LuaMethodAttributes("drawPie", "draws a Pie shape at the given coordinates and the given width and height")] public void DrawPie( int x, From 5b731cf8b8ad9597152ac58c083072339790056e Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 16 Jun 2017 08:50:28 -0400 Subject: [PATCH 2/9] SNES saveram: check for existance checks if SGB saveram exists before attempting to assign it to the buffer pointer. Also return null if no saveram is found, which conforms to what other cores are doing and what EMUHawk expects --- .../Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs index 0cf3e94476..cb1ffdfb68 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs @@ -15,11 +15,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES { byte* buf = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM); var size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM); - if (buf == null) + + if (buf == null && Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM)>0) { buf = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.SGB_CARTRAM); size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM); } + if (buf==null) + { + return null; + } var ret = new byte[size]; Marshal.Copy((IntPtr)buf, ret, 0, size); From 5a44c10a79c49cf17e486c25113a3f448685843d Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 16 Jun 2017 10:34:35 -0400 Subject: [PATCH 3/9] Update MainForm.cs --- BizHawk.Client.EmuHawk/MainForm.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 21dff09bae..fbe6691143 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -3634,10 +3634,10 @@ namespace BizHawk.Client.EmuHawk ((OpenAdvanced_OpenRom)ioa).Path = loader.CanonicalFullPath; } - string loaderName = "*" + OpenAdvancedSerializer.Serialize(ioa); - if (result) { + + string loaderName = "*" + OpenAdvancedSerializer.Serialize(ioa); Emulator = loader.LoadedEmulator; Global.Game = loader.Game; CoreFileProvider.SyncCoreCommInputSignals(nextComm); From 93254b4b6ee35ed64efd1e13977b77ca6623ade8 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 16 Jun 2017 10:35:41 -0400 Subject: [PATCH 4/9] Update RomLoader.cs Fixes #865 --- BizHawk.Client.Common/RomLoader.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 6f0f250422..3f89408972 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -317,6 +317,15 @@ namespace BizHawk.Client.Common } else { + // at this point, file is either assigned to the ROM path, if it exists, + // or is empty and corecomm is not a libretro core + // so, we still need to check path here before continuing + if (string.IsNullOrEmpty(path)) + { + Console.WriteLine("No ROM to Load"); + return false; + } + // if not libretro: // do extension checknig ext = file.Extension.ToLowerInvariant(); From 7bb76f9f03c4b1e88cc3d43780a6380d106397db Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 16 Jun 2017 16:44:57 -0400 Subject: [PATCH 5/9] A7800Hawk commit #4 -Start Maria DMA and frame execution -Refactor CPU execution -Expose more tools (Hex Editor, RAM Search, etc) -Add Ready and Halt behaviours trigggered by Maria --- .../Atari/A7800Hawk/A7800Hawk.IDebuggable.cs | 2 +- .../Atari/A7800Hawk/A7800Hawk.IEmulator.cs | 96 +++++++----- .../A7800Hawk/A7800Hawk.IMemoryDomains.cs | 1 + .../Consoles/Atari/A7800Hawk/A7800Hawk.cs | 9 +- .../Consoles/Atari/A7800Hawk/Maria.cs | 144 +++++++++++++++++- .../Consoles/Atari/A7800Hawk/MemoryMap.cs | 18 ++- 6 files changed, 216 insertions(+), 54 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs index 056eb62a0a..0a83661f8a 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs @@ -54,7 +54,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk } } - public IMemoryCallbackSystem MemoryCallbacks { get; private set; } + public IMemoryCallbackSystem MemoryCallbacks { get; } = new MemoryCallbackSystem(); public bool CanStep(StepType type) { diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs index 09ad452125..d66887478e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs @@ -12,7 +12,10 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk //Maria related variables public int cycle; public int cpu_cycle; - public int scanline; + public bool cpu_is_haltable; + public bool cpu_is_halted; + public bool cpu_halt_pending; + public bool cpu_resume_pending; // there are 4 maria cycles in a CPU cycle (fast access, both NTSC and PAL) // if the 6532 or TIA are accessed (PC goes to one of those addresses) the next access will be slower by 1/2 a CPU cycle @@ -47,58 +50,67 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk // read the controller state here for now GetControllerState(controller); - scanline = 0; + maria.RunFrame(); + } - // actually execute the frame - while (scanline < 263) + public void RunCPUCycle() + { + cpu_cycle++; + tia._hsyncCnt++; + + if (cpu_cycle <= (2 + (slow_access ? 1 : 0))) { - maria.Execute(cycle, scanline); - cycle++; - cpu_cycle++; - tia._hsyncCnt++; + cpu_is_haltable = true; + } else + { + cpu_is_haltable = false; + } - // the time a cpu cycle takes depends on the status of the address bus - // any address in range of the TIA or m6532 takes an extra cycle to complete - if (cpu_cycle==(4 + (slow_access ? 2 : 0))) - { + // the time a cpu cycle takes depends on the status of the address bus + // any address in range of the TIA or m6532 takes an extra cycle to complete + if (cpu_cycle == (4 + (slow_access ? 2 : 0))) + { + if (!cpu_is_halted) cpu.ExecuteOne(); - cpu_cycle = 0; - } - - // determine if the next access will be fast or slow - if (cpu.PC < 0x0400) + + cpu_cycle = 0; + + if (cpu_halt_pending) { - if ((cpu.PC & 0xFF) < 0x20) - { - if ((A7800_control_register & 0x1) == 0 && (cpu.PC < 0x20)) - { - slow_access = false; - } - else - { - slow_access = true; - } - } - else if (cpu.PC < 0x300) - { - slow_access = true; - } - else - { - slow_access = false; - } + cpu_halt_pending = false; + cpu_is_halted = true; } - - if (cycle == 454) + if (cpu_resume_pending) { - scanline++; - cycle = 0; - tia._hsyncCnt = 0; + cpu_resume_pending = false; + cpu_is_halted = false; } } - + // determine if the next access will be fast or slow + if (cpu.PC < 0x0400) + { + if ((cpu.PC & 0xFF) < 0x20) + { + if ((A7800_control_register & 0x1) == 0 && (cpu.PC < 0x20)) + { + slow_access = false; + } + else + { + slow_access = true; + } + } + else if (cpu.PC < 0x300) + { + slow_access = true; + } + else + { + slow_access = false; + } + } } private void GetControllerState(IController controller) diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IMemoryDomains.cs index 3d7d86a4f4..5090e1f139 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IMemoryDomains.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IMemoryDomains.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using BizHawk.Emulation.Common; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs index 32c4872461..a34a2151fd 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs @@ -127,18 +127,20 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk if (_isPAL) { maria._frameHz = 50; - maria._screen_width = 454; + maria._screen_width = 320; maria._screen_height = 313; maria._palette = PALPalette; } else { maria._frameHz = 60; - maria._screen_width = 454; + maria._screen_width = 320; maria._screen_height = 263; maria._palette = NTSCPalette; } + maria.Core = this; + ser.Register(maria); ser.Register(tia); ServiceProvider = ser; @@ -146,6 +148,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk _tracer = new TraceBuffer { Header = cpu.TraceHeader }; ser.Register(_tracer); + SetupMemoryDomains(); HardReset(); } @@ -174,7 +177,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk private void ExecFetch(ushort addr) { - //MemoryCallbacks.CallExecutes(addr); + MemoryCallbacks.CallExecutes(addr); } private void Reset_Mapper(string m) diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs index 95f136866b..25d717cb47 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs @@ -7,8 +7,10 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk // Emulates the Atari 7800 Maria graphics chip public class Maria : IVideoProvider { + public A7800Hawk Core { get; set; } + public int _frameHz = 60; - public int _screen_width = 454; + public int _screen_width = 320; public int _screen_height = 263; public int[] _vidbuffer; @@ -19,9 +21,9 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk return _vidbuffer; } - public int VirtualWidth => 454; + public int VirtualWidth => 320; public int VirtualHeight => _screen_height; - public int BufferWidth => 454; + public int BufferWidth => 320; public int BufferHeight => _screen_height; public int BackgroundColor => unchecked((int)0xff000000); public int VsyncNumerator => _frameHz; @@ -30,11 +32,145 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk // the Maria chip can directly access memory public Func ReadMemory; + public int cycle; + public int scanline; + public bool sl_DMA_complete; + + public int DMA_phase = 0; + public int DMA_phase_counter; + + public static int DMA_START_UP = 0; + public static int DMA_HEADER = 1; + public static int DMA_GRAPHICS = 2; + public static int DMA_CHAR_MAP = 3; + public static int DMA_SHUTDOWN_OTHER = 4; + public static int DMA_SHUTDOWN_LAST = 5; + + public byte list_low_byte; + public byte list_high_byte; + + + // each frame contains 263 scanlines // each scanline consists of 113.5 CPU cycles (fast access) which equates to 454 Maria cycles // In total there are 29850.5 CPU cycles (fast access) in a frame - public void Execute(int cycle, int scanline) + public void RunFrame() { + scanline = 0; + + Core.Maria_regs[8] = 0x80; // indicates VBlank state + + // we start off in VBlank for 20 scanlines + // at the end of vblank is a DMA to set up the display for the start of drawing + // this is free time for the CPU to set up display lists + while (scanline < 19) + { + Core.RunCPUCycle(); + cycle++; + + if (cycle == 454) + { + scanline++; + cycle = 0; + Core.tia._hsyncCnt = 0; + Core.cpu.RDY = true; + } + + } + + // "The end of vblank is made up of a DMA startup plus a long shut down" + sl_DMA_complete = false; + DMA_phase = DMA_START_UP; + DMA_phase_counter = 0; + + for (int i=0; i<454;i++) + { + if (i<28) + { + // DMA doesn't start until 7 CPU cycles into a scanline + } + else if (i==28) + { + Core.cpu_halt_pending = true; + DMA_phase_counter = 0; + } + else if (!sl_DMA_complete) + { + RunDMA(i - 28, true); + } + + Core.RunCPUCycle(); + } + + scanline++; + cycle = 0; + + Core.Maria_regs[8] = 0; // we have now left VBLank + + // Now proceed with the remaining scanlines + // the first one is a pre-render line, since we didn't actually put any data into the buffer yet + while (scanline < 263) + { + + + Core.RunCPUCycle(); + + cycle++; + + if (cycle == 454) + { + scanline++; + cycle = 0; + Core.tia._hsyncCnt = 0; + Core.cpu.RDY = true; + } + } + } + + public void RunDMA(int c, bool short_dma) + { + // During DMA the CPU is HALTED, This appears to happen on the falling edge of Phi2 + // Current implementation is that a HALT request must be acknowledged in phi1 + // if the CPU is now in halted state, start DMA + if (Core.cpu_is_halted) + { + DMA_phase_counter++; + + if (DMA_phase_counter==2 && DMA_phase==DMA_START_UP) + { + DMA_phase_counter = 0; + if (short_dma) + DMA_phase = DMA_SHUTDOWN_LAST; + else + { + DMA_phase = DMA_HEADER; + } + + return; + } + + if (DMA_phase == DMA_HEADER) + { + // get all the data from the display list header + if (DMA_phase_counter==1) + { + + } + } + + if (DMA_phase == DMA_SHUTDOWN_LAST) + { + if (DMA_phase_counter==4) + { + Core.cpu_resume_pending = true; + sl_DMA_complete = true; + } + return; + } + + + } + } diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/MemoryMap.cs index a7541855c6..d6ec152ba9 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/MemoryMap.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/MemoryMap.cs @@ -2,7 +2,6 @@ using BizHawk.Common.BufferExtensions; using BizHawk.Emulation.Common; -using BizHawk.Emulation.Cores.Components.M6502; namespace BizHawk.Emulation.Cores.Atari.A7800Hawk { @@ -10,6 +9,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk { public byte ReadMemory(ushort addr) { + MemoryCallbacks.CallReads(addr); + if (addr < 0x0400) { if ((addr & 0xFF) < 0x20) { @@ -85,6 +86,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk public void WriteMemory(ushort addr, byte value) { + MemoryCallbacks.CallWrites(addr); + if (addr < 0x0400) { if ((addr & 0xFF) < 0x20) @@ -92,18 +95,25 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk // return TIA registers or control register if it is still unlocked if ((A7800_control_register & 0x1) == 0 && (addr < 0x20)) { - A7800_control_register = value; // TODO: what to return here? + A7800_control_register = value; } else { - TIA_regs[addr] = value; // TODO: what to return here? + TIA_regs[addr] = value; } } else if ((addr & 0xFF) < 0x40) { if ((A7800_control_register & 0x2) > 0) { - Maria_regs[(addr & 0x3F) - 0x20] = value; + var temp = (addr & 0x3F) - 0x20; + + // register 8 is read only and controlled by Maria + if (temp != 8) + Maria_regs[temp] = value; + + if (temp==4) // WSYNC + cpu.RDY = false; } } else if (addr < 0x100) From 13b99bafd0ed7fb13d380e4deaa57087ac518d7f Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 16 Jun 2017 21:41:13 -0400 Subject: [PATCH 6/9] NESHawk: Clean up and minor refactor Simplify the code base a bit and gets a performance boost as well. --- .../Consoles/Nintendo/NES/NES.Core.cs | 242 ++++++++---------- .../Consoles/Nintendo/NES/NES.IStatable.cs | 2 - .../Consoles/Nintendo/NES/PPU.cs | 70 +++-- .../Consoles/Nintendo/NES/PPU.regs.cs | 7 +- .../Consoles/Nintendo/NES/PPU.run.cs | 42 +-- 5 files changed, 165 insertions(+), 198 deletions(-) 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 From 0ddd7392c7bed9841930eef7b37815f486e17a46 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 17 Jun 2017 13:55:21 -0400 Subject: [PATCH 7/9] A7800Hawk Commit 5 - Almost done with DMA --- .../Atari/A7800Hawk/A7800Hawk.IEmulator.cs | 8 + .../Consoles/Atari/A7800Hawk/Maria.cs | 184 ++++++++++++++++-- 2 files changed, 180 insertions(+), 12 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs index d66887478e..e0c6169241 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs @@ -71,7 +71,15 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk if (cpu_cycle == (4 + (slow_access ? 2 : 0))) { if (!cpu_is_halted) + { cpu.ExecuteOne(); + } + else + { + // we still want to keep track of CPU time even if it is halted, so increment the counter here + // The basic 6502 has no halt state, this feature is specific to SALLY + cpu.TotalExecutedCycles++; + } cpu_cycle = 0; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs index 25d717cb47..fd9f314194 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs @@ -1,6 +1,7 @@ using System; using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; namespace BizHawk.Emulation.Cores.Atari.A7800Hawk { @@ -9,6 +10,23 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk { public A7800Hawk Core { get; set; } + struct GFX_Object + { + public byte palette; + public byte width; + public ushort addr; + public byte h_pos; + + // additional entries used only in 5-byte header mode + public bool write_mode; + public bool ind_mode; + public bool exp_mode; + } + + // technically there is no limit on he number of graphics objects, but since dma is automatically killed + // at the end of a scanline, we have an effective limit + GFX_Object[] GFX_Objects = new GFX_Object[64]; + public int _frameHz = 60; public int _screen_width = 320; public int _screen_height = 263; @@ -35,6 +53,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk public int cycle; public int scanline; public bool sl_DMA_complete; + public bool do_dma; public int DMA_phase = 0; public int DMA_phase_counter; @@ -46,10 +65,21 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk public static int DMA_SHUTDOWN_OTHER = 4; public static int DMA_SHUTDOWN_LAST = 5; - public byte list_low_byte; - public byte list_high_byte; + public int header_read_time = 8; // default for 4 byte headers (10 for 5 bytes ones) + public int DMA_phase_next; + public int base_scanline; + public ushort display_zone_pointer; + public int display_zone_counter; + public byte current_DLL_offset; + public ushort current_DLL_addr; + public bool current_DLL_DLI; + public bool current_DLL_H16; + public bool current_DLL_H8; + + public int header_counter; + public int header_pointer; // since headers could be 4 or 5 bytes, we need a seperate pointer // each frame contains 263 scanlines // each scanline consists of 113.5 CPU cycles (fast access) which equates to 454 Maria cycles @@ -80,8 +110,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk // "The end of vblank is made up of a DMA startup plus a long shut down" sl_DMA_complete = false; - DMA_phase = DMA_START_UP; - DMA_phase_counter = 0; + do_dma = false; for (int i=0; i<454;i++) { @@ -89,14 +118,16 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk { // DMA doesn't start until 7 CPU cycles into a scanline } - else if (i==28) + else if (i==28 && Core.Maria_regs[0x1C].Bit(6) && !Core.Maria_regs[0x1C].Bit(6)) { Core.cpu_halt_pending = true; + DMA_phase = DMA_START_UP; DMA_phase_counter = 0; + do_dma = true; } - else if (!sl_DMA_complete) + else if (!sl_DMA_complete && do_dma) { - RunDMA(i - 28, true); + RunDMA(true); } Core.RunCPUCycle(); @@ -104,14 +135,31 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk scanline++; cycle = 0; - - Core.Maria_regs[8] = 0; // we have now left VBLank + do_dma = false; + Core.Maria_regs[8] = 0; // we have now left VBLank' + base_scanline = 0; + sl_DMA_complete = false; // Now proceed with the remaining scanlines // the first one is a pre-render line, since we didn't actually put any data into the buffer yet while (scanline < 263) { - + + if (cycle < 28) + { + // DMA doesn't start until 7 CPU cycles into a scanline + } + else if (cycle == 28 && Core.Maria_regs[0x1C].Bit(6) && !Core.Maria_regs[0x1C].Bit(6)) + { + Core.cpu_halt_pending = true; + DMA_phase = DMA_START_UP; + DMA_phase_counter = 0; + do_dma = true; + } + else if (!sl_DMA_complete && do_dma) + { + RunDMA(false); + } Core.RunCPUCycle(); @@ -123,11 +171,13 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk cycle = 0; Core.tia._hsyncCnt = 0; Core.cpu.RDY = true; + do_dma = false; + sl_DMA_complete = false; } } } - public void RunDMA(int c, bool short_dma) + public void RunDMA(bool short_dma) { // During DMA the CPU is HALTED, This appears to happen on the falling edge of Phi2 // Current implementation is that a HALT request must be acknowledged in phi1 @@ -140,7 +190,14 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk { DMA_phase_counter = 0; if (short_dma) + { DMA_phase = DMA_SHUTDOWN_LAST; + + // also here we load up the display list list + // is the timing correct? + display_zone_pointer = (ushort)((Core.Maria_regs[0xC] << 8) | Core.Maria_regs[0x10]); + display_zone_counter = -1; + } else { DMA_phase = DMA_HEADER; @@ -154,16 +211,119 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk // get all the data from the display list header if (DMA_phase_counter==1) { + header_counter++; + GFX_Objects[header_counter].addr = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + header_pointer++; + byte temp = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + // if there is no width, then we must have an extended header + // or at the end of this list + if ((temp & 0x1F) == 0) + { + if ((temp & 0xE0) == 0) + { + // at the end of the list, time to end the DMA + // check if we are at the end of the zone + if (scanline == base_scanline + current_DLL_offset) + { + DMA_phase_next = DMA_SHUTDOWN_LAST; + } + else + { + DMA_phase_next = DMA_SHUTDOWN_OTHER; + } + header_read_time = 8; + } + else + { + // we are in 5 Byte header mode + GFX_Objects[header_counter].write_mode = temp.Bit(7); + GFX_Objects[header_counter].ind_mode = temp.Bit(5); + header_pointer++; + temp = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + GFX_Objects[header_counter].addr |= (ushort)(temp << 8); + header_pointer++; + temp = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + GFX_Objects[header_counter].width = (byte)(temp & 0x1F); + GFX_Objects[header_counter].palette = (byte)((temp & 0xE0) >> 5); + header_pointer++; + GFX_Objects[header_pointer].h_pos = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + header_pointer++; + + GFX_Objects[header_pointer].exp_mode = true; + DMA_phase_next = DMA_GRAPHICS; + + header_read_time = 10; + } + } + else + { + GFX_Objects[header_counter].width = (byte)(temp & 0x1F); + GFX_Objects[header_counter].palette = (byte)((temp & 0xE0) >> 5); + header_pointer++; + temp = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + GFX_Objects[header_counter].addr |= (ushort)(temp << 8); + header_pointer++; + GFX_Objects[header_pointer].h_pos = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + header_pointer++; + + GFX_Objects[header_pointer].exp_mode = false; + DMA_phase_next = DMA_GRAPHICS; + + header_read_time = 8; + } } + else if (DMA_phase_counter == header_read_time) + { + DMA_phase_counter = 0; + DMA_phase = DMA_phase_next; + } + return; + } + + if (DMA_phase == DMA_GRAPHICS) + { + if (DMA_phase_counter == 1) + { + // get all the graphics data + } + + if (DMA_phase_counter == 3) + { + DMA_phase = DMA_SHUTDOWN_OTHER; + DMA_phase_counter = 0; + } + return; + } + + if (DMA_phase == DMA_SHUTDOWN_OTHER) + { + Core.cpu_resume_pending = true; + sl_DMA_complete = true; + return; } if (DMA_phase == DMA_SHUTDOWN_LAST) { - if (DMA_phase_counter==4) + if (DMA_phase_counter==6) { Core.cpu_resume_pending = true; sl_DMA_complete = true; + + // on the last line of a zone, we load up the disply list list for the next zone. + display_zone_counter++; + ushort temp_addr = (ushort)(display_zone_pointer + 3 * display_zone_counter); + byte temp = ReadMemory(temp_addr); + current_DLL_addr = (ushort)(ReadMemory((ushort)(temp_addr + 1)) << 8); + current_DLL_addr |= ReadMemory((ushort)(temp_addr + 2)); + + current_DLL_offset = (byte)(temp & 0xF + 1); + current_DLL_DLI = temp.Bit(7); + current_DLL_H16 = temp.Bit(6); + current_DLL_H8 = temp.Bit(5); + + header_counter = -1; + header_pointer = 0; } return; } From 0e0d3e4143d026ebc7f6edfef91fcc6a15bc3359 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 17 Jun 2017 19:01:04 -0400 Subject: [PATCH 8/9] Update Execute.cs --- BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs index 5187e5f028..7e4ad61e6d 100644 --- a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs @@ -314,7 +314,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 //the interrupt would then take place if necessary, using a cached PC. but im not so sure about that. /*VOP_NMI*/ new Uop[] { Uop.FetchDummy, Uop.FetchDummy, Uop.PushPCH, Uop.PushPCL, Uop.PushP_NMI, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End_SuppressInterrupt }, /*VOP_IRQ*/ new Uop[] { Uop.FetchDummy, Uop.FetchDummy, Uop.PushPCH, Uop.PushPCL, Uop.PushP_IRQ, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End_SuppressInterrupt }, - /*VOP_RESET*/ new Uop[] { Uop.FetchDummy, Uop.FetchDummy, Uop.FetchDummy, Uop.PushDummy, Uop.PushDummy, Uop.PushP_Reset, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End_SuppressInterrupt }, + /*VOP_RESET*/ new Uop[] { Uop.FetchDummy, /*Uop.FetchDummy,*/ Uop.FetchDummy, Uop.PushDummy, Uop.PushDummy, Uop.PushP_Reset, Uop.FetchPCLVector, Uop.FetchPCHVector, Uop.End_SuppressInterrupt }, /*VOP_Fetch1_NoInterrupt*/ new Uop[] { Uop.Fetch1_Real }, }; From bd29ec087e68b637ad07da00d3847ffefab91166 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 17 Jun 2017 19:02:21 -0400 Subject: [PATCH 9/9] NES: small PPU refactor Will make future testing easier --- .../Consoles/Nintendo/NES/APU.cs | 4 +- .../Consoles/Nintendo/NES/PPU.run.cs | 84 ++++++++++--------- 2 files changed, 47 insertions(+), 41 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/APU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/APU.cs index 1d2f1ca0e9..f45b8719a1 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/APU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/APU.cs @@ -1160,8 +1160,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public void NESHardReset() { // "at power on it is as if $00 was written to $4017 9-12 cycles before the reset vector" - // that translates to a starting value for the counter of -2 - sequencer_counter = -2; + // that translates to a starting value for the counter of -3 + sequencer_counter = -1; } public void WriteReg(int addr, byte val) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.run.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.run.cs index e98bdfe145..841374cf6f 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.run.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/PPU.run.cs @@ -63,7 +63,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES void Read_bgdata(ref BGDataRecord bgdata) { for (int i = 0; i < 8; i++) + { Read_bgdata(i, ref bgdata); + runppu(1); + + if (PPUON && i==6) + { + ppu_was_on = true; + } + + if (PPUON && i==7) + { + if (!race_2006) + ppur.increment_hsc(); + + if (ppur.status.cycle == 256 && !race_2006) + ppur.increment_vs(); + + ppu_was_on = false; + } + } } // attempt to emulate graphics pipeline behaviour @@ -97,10 +116,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES case 0: ppu_addr_temp = ppur.get_ntread(); bgdata.nt = ppubus_read(ppu_addr_temp, true, true); - runppu(1); break; case 1: - runppu(1); break; case 2: { @@ -112,49 +129,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES if ((ppur.ht & 2) != 0) at >>= 2; at &= 0x03; at <<= 2; - bgdata.at = at; - runppu(1); - + bgdata.at = at; break; } case 3: - runppu(1); break; case 4: ppu_addr_temp = ppur.get_ptread(bgdata.nt); bgdata.pt_0 = ppubus_read(ppu_addr_temp, true, true); - runppu(1); break; case 5: - runppu(1); break; case 6: ppu_addr_temp |= 8; bgdata.pt_1 = ppubus_read(ppu_addr_temp, true, true); - - - runppu(1); - if (PPUON) - { - ppu_was_on = true; - } - break; case 7: - - runppu(1); - //horizontal scroll clocked at cycle 3 and then - //vertical scroll at 256 - if (PPUON) - { - if (!race_2006) - ppur.increment_hsc(); - - if (ppur.status.cycle == 256 && !race_2006) - ppur.increment_vs(); - } - - ppu_was_on = false; break; } //switch(cycle) } @@ -185,6 +175,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES bool nmi_destiny = reg_2000.vblank_nmi_gen && Reg2002_vblank_active; runppu(3); if (nmi_destiny) nes.cpu.NMI = true; + nes.Board.AtVsyncNMI(); runppu(postNMIlines * kLineTime - delay); @@ -391,10 +382,28 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES //process the current clock's worth of bg data fetching //this needs to be split into 8 pieces or else exact sprite 0 hitting wont work due to the cpu not running while the sprite renders below + + if (PPUON) Read_bgdata(xp, ref bgdata[xt + 2]); - else - runppu(1); + + runppu(1); + + if (PPUON && xp == 6) + { + ppu_was_on = true; + } + + if (PPUON && xp == 7) + { + if (!race_2006) + ppur.increment_hsc(); + + if (ppur.status.cycle == 256 && !race_2006) + ppur.increment_vs(); + + ppu_was_on = false; + } if (hit_pending) { @@ -484,8 +493,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES } //oamcount loop pipeline(pixelcolor, target, xt*32+xp); - target++; - + target++; } //loop across 8 pixels } //loop across 32 tiles } @@ -751,14 +759,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES Read_bgdata(ref bgdata[xt]); } - // this sequence is tuned to pass 10-even_odd_timing.nes - + // this sequence is tuned to pass 10-even_odd_timing.nes runppu(1); runppu(1); runppu(1); - + runppu(1); bool evenOddDestiny = PPUON; @@ -777,7 +784,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES //idle for pre NMI lines runppu(preNMIlines * kLineTime); - } //FrameAdvance void FrameAdvance_ppudead() @@ -788,7 +794,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES //should write to those regs during that time, it needs //to wait for vblank - runppu(241 * kLineTime+3);// -8*3); + runppu(241 * kLineTime-3);// -8*3); ppudead--; } }