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 adelikat
parent 000d30025a
commit 7814a9ed30
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 //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: //NTSC:
//sequence of ppu clocks per cpu clock: 3 //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_NTSC = new ByteBuffer(new byte[] { 3, 3, 3, 3, 3 });
static ByteBuffer cpu_sequence_PAL = new ByteBuffer(new byte[] { 3, 3, 3, 4, 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 int oam_dma_index;
public bool oam_dma_exec = false; public bool oam_dma_exec = false;
@ -401,140 +401,122 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
public bool do_the_reread; public bool do_the_reread;
public byte DB; //old data bus values from previous reads public byte DB; //old data bus values from previous reads
#if VS2012
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
internal void RunCpuOne() internal void RunCpuOne()
{ {
cpu_stepcounter++; ///////////////////////////
if (cpu_stepcounter == cpu_sequence[cpu_step]) // OAM DMA start
///////////////////////////
if (sprdma_countdown > 0)
{ {
cpu_step++; sprdma_countdown--;
if (cpu_step == 5) cpu_step = 0; if (sprdma_countdown == 0)
cpu_stepcounter = 0;
///////////////////////////
// OAM DMA start
///////////////////////////
if (sprdma_countdown > 0)
{ {
sprdma_countdown--; if (cpu.TotalExecutedCycles % 2 == 0)
if (sprdma_countdown == 0)
{ {
if (cpu.TotalExecutedCycles % 2 == 0) cpu_deadcounter = 2;
{
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;
} }
else else
{ {
cpu_deadcounter--; cpu_deadcounter = 1;
} }
} oam_dma_exec = true;
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; cpu.RDY = false;
dmc_dma_exec = true; oam_dma_index = 0;
apu.dmc_dma_countdown--; special_case_delay = true;
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;
}
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) public byte ReadReg(int addr)
{ {
byte ret_spec; byte ret_spec;
@ -681,7 +663,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
case 0x4013: case 0x4013:
apu.WriteReg(addr, val); apu.WriteReg(addr, val);
break; 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 0x4015: apu.WriteReg(addr, val); break;
case 0x4016: case 0x4016:
if (_isVS) if (_isVS)
@ -740,15 +727,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
return 0; 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> /// <summary>
/// Sets the provided palette as current. /// Sets the provided palette as current.
/// Applies the current deemph settings if needed to expand a 64-entry palette to 512 /// 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("cpu_accumulate", ref cpu_accumulate);
ser.Sync("_irq_apu", ref _irq_apu); ser.Sync("_irq_apu", ref _irq_apu);
ser.Sync("sprdma_countdown", ref sprdma_countdown); 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); ser.Sync("cpu_deadcounter", ref cpu_deadcounter);
//oam related //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 System.Runtime.CompilerServices;
using BizHawk.Common; using BizHawk.Common;
@ -8,6 +6,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
{ {
public sealed partial class PPU public sealed partial class PPU
{ {
public int cpu_step, cpu_stepcounter;
// this only handles region differences within the PPU // this only handles region differences within the PPU
int preNMIlines; int preNMIlines;
int postNMIlines; int postNMIlines;
@ -203,6 +203,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
public void SyncState(Serializer ser) 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("ppudead", ref ppudead);
ser.Sync("idleSynch", ref idleSynch); ser.Sync("idleSynch", ref idleSynch);
ser.Sync("NMI_PendingInstructions", ref NMI_PendingInstructions); 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]; 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) void runppu(int x)
{ {
//run one ppu cycle at a time so we can interact with the ppu and clockPPU at high granularity //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++; ppur.status.cycle++;
is_even_cycle = !is_even_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)
{ {
//if (Reg2002_vblank_active_pending) Reg2002_vblank_active = 1;
Reg2002_vblank_active = 1;
Reg2002_vblank_active_pending = false; Reg2002_vblank_active_pending = false;
} }
@ -337,10 +336,5 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
nes.Board.ClockPPU(); 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_2000 reg_2000;
public Reg_2001 reg_2001; public Reg_2001 reg_2001;
byte reg_2003; byte reg_2003;
void regs_reset() void regs_reset()
{ {
//TODO - would like to reconstitute the entire PPU instead of all this.. //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)); 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) //OAM ADDRESS (write)
void write_2003(int addr, byte value) 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 BizHawk.Common;
using System; using System;
@ -10,6 +8,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
sealed partial class PPU sealed partial class PPU
{ {
const int kFetchTime = 2; const int kFetchTime = 2;
const int kLineTime = 341;
struct BGDataRecord struct BGDataRecord
{ {
@ -78,7 +77,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
if (reg_2001.color_disable) if (reg_2001.color_disable)
pixelcolor_latch_2 &= 0x30; 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) if (row_check >= 1)
@ -158,14 +159,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
} //switch(cycle) } //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() public unsafe void FrameAdvance()
{ {
BGDataRecord* bgdata = stackalloc BGDataRecord[34]; //one at the end is junk, it can never be rendered 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); runppu(3);
bool nmi_destiny = reg_2000.vblank_nmi_gen && Reg2002_vblank_active; bool nmi_destiny = reg_2000.vblank_nmi_gen && Reg2002_vblank_active;
runppu(3); runppu(3);
if (nmi_destiny) TriggerNMI(); if (nmi_destiny) nes.cpu.NMI = true;
nes.Board.AtVsyncNMI(); nes.Board.AtVsyncNMI();
runppu(postNMIlines * kLineTime - delay); runppu(postNMIlines * kLineTime - delay);
//this seems to run just before the dummy scanline begins //this seems to happen just before the dummy scanline begins
clear_2002(); Reg2002_objhit = Reg2002_objoverflow = 0;
Reg2002_vblank_clear_pending = true;
idleSynch ^= true; idleSynch ^= true;
@ -224,8 +218,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
yp = sl - 1; yp = sl - 1;
ppuphase = PPUPHASE.BG; ppuphase = PPUPHASE.BG;
// "If PPUADDR is not less then 8 when rendering starts, the first 8 fights in OAM and written to from // "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" // the current location off PPUADDR"
if (sl == 0 && PPUON && reg_2003 >= 8 && region==Region.NTSC) if (sl == 0 && PPUON && reg_2003 >= 8 && region==Region.NTSC)
{ {
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
@ -233,7 +227,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
OAM[i] = OAM[reg_2003 & 0xF8 + i]; OAM[i] = OAM[reg_2003 & 0xF8 + i];
} }
} }
if (NTViewCallback != null && yp == NTViewCallback.Scanline) NTViewCallback.Callback(); if (NTViewCallback != null && yp == NTViewCallback.Scanline) NTViewCallback.Callback();
if (PPUViewCallback != null && yp == PPUViewCallback.Scanline) PPUViewCallback.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 //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 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++) 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) 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) if (yp >= read_value && yp < read_value + spriteHeight && spr_true_count == 0)
@ -400,6 +396,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
else else
runppu(1); runppu(1);
if (hit_pending)
{
hit_pending = false;
Reg2002_objoverflow = true;
}
renderbgnow = show_bg_new && (xt > 0 || reg_2001.show_bg_leftmost); renderbgnow = show_bg_new && (xt > 0 || reg_2001.show_bg_leftmost);
//bg pos is different from raster pos due to its offsetability. //bg pos is different from raster pos due to its offsetability.
//so adjust for that here //so adjust for that here