NESHawk: Clean up and minor refactor

Simplify the code base a bit and gets a performance boost as well.
This commit is contained in:
alyosha-tas 2017-06-16 21:41:13 -04:00 committed by GitHub
parent 7bb76f9f03
commit 13b99bafd0
5 changed files with 165 additions and 198 deletions

View File

@ -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;
}
/// <summary>
/// Sets the provided palette as current.
/// Applies the current deemph settings if needed to expand a 64-entry palette to 512

View File

@ -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

View File

@ -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;
}
}

View File

@ -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)
{

View File

@ -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