NesHawk: Single Tick PPU

Should allow for breaking out into a debugger.
This commit is contained in:
alyosha-tas 2017-09-01 22:11:41 -04:00 committed by GitHub
parent 6f021653aa
commit 40ec613982
3 changed files with 909 additions and 669 deletions

View File

@ -366,7 +366,43 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
VS_coin_inserted &= 1;
}
ppu.FrameAdvance();
FrameGo = true;
ppu.ppu_tick_counter = 0;
if (ppu.ppudead > 0)
{
while (ppu.ppudead > 0)
{
ppu.NewDeadPPU();
}
}
else
{
ppu.ppu_init_frame();
ppu.do_vbl = true;
ppu.do_active_sl = true;
ppu.do_pre_vbl = true;
// do the vbl ticks seperate, that will save us a few checks that don't happen in active region
while (ppu.do_vbl)
{
ppu.TickPPU_VBL();
}
// now do the rest of the frame
while (ppu.do_active_sl)
{
ppu.TickPPU_active();
}
// now do the pre-NMI lines
while (ppu.do_pre_vbl)
{
ppu.TickPPU_preVBL();
}
}
if (lagged)
{
_lagcount++;
@ -385,6 +421,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
num_cheats = 0;
}
public bool FrameGo;
//PAL:
//0 15 30 45 60 -> 12 27 42 57 -> 9 24 39 54 -> 6 21 36 51 -> 3 18 33 48 -> 0
//sequence of ppu clocks per cpu clock: 3,3,3,3,4

View File

@ -194,7 +194,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
}
//state
int ppudead; //measured in frames
public int ppudead; //measured in frames
bool idleSynch;
int NMI_PendingInstructions;
byte PPUGenLatch;
@ -253,6 +253,51 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
ser.Sync("xbuf", ref xbuf, false);
ser.Sync("_totalCycles", ref _totalCycles);
ser.Sync("do_vbl", ref do_vbl);
ser.Sync("do_active_sl", ref do_active_sl);
ser.Sync("do_pre_vbl", ref do_pre_vbl);
ser.Sync("ppu_tick_counter", ref ppu_tick_counter);
ser.Sync("scanline_counter", ref scanline_counter);
ser.Sync("nmi_destiny", ref nmi_destiny);
ser.Sync("yp_shift", ref yp_shift);
ser.Sync("sprite_eval_cycle", ref sprite_eval_cycle);
ser.Sync("xt", ref xt);
ser.Sync("xp", ref xp);
ser.Sync("xstart", ref xstart);
ser.Sync("rasterpos", ref rasterpos);
ser.Sync("renderspritenow", ref renderspritenow);
ser.Sync("renderbgnow", ref renderbgnow);
ser.Sync("hit_pending", ref hit_pending);
ser.Sync("s", ref s);
ser.Sync("ppu_aux_index", ref ppu_aux_index);
ser.Sync("junksprite", ref junksprite);
ser.Sync("line", ref line);
ser.Sync("patternNumber", ref patternNumber);
ser.Sync("patternAddress", ref patternAddress);
ser.Sync("temp_addr", ref temp_addr);
ser.Sync("sl_sprites", ref sl_sprites, false);
byte bg_byte;
for (int i = 0; i < 34; i++)
{
bg_byte = bgdata[i].at; ser.Sync("bgdata", ref bg_byte); bgdata[i].at = bg_byte;
bg_byte = bgdata[i].nt; ser.Sync("bgdata", ref bg_byte); bgdata[i].nt = bg_byte;
bg_byte = bgdata[i].pt_0; ser.Sync("bgdata", ref bg_byte); bgdata[i].pt_0 = bg_byte;
bg_byte = bgdata[i].pt_1; ser.Sync("bgdata", ref bg_byte); bgdata[i].pt_1 = bg_byte;
}
byte oam_byte;
for (int i = 0; i < 64; i++)
{
oam_byte = t_oam[i].oam_y; ser.Sync("bgdata", ref oam_byte); t_oam[i].oam_y = oam_byte;
oam_byte = t_oam[i].oam_ind; ; ser.Sync("bgdata", ref oam_byte); t_oam[i].oam_ind = oam_byte;
oam_byte = t_oam[i].oam_attr; ser.Sync("bgdata", ref oam_byte); t_oam[i].oam_attr = oam_byte;
oam_byte = t_oam[i].oam_x; ser.Sync("bgdata", ref oam_byte); t_oam[i].oam_x = oam_byte;
oam_byte = t_oam[i].patterns_0; ser.Sync("bgdata", ref oam_byte); t_oam[i].patterns_0 = oam_byte;
oam_byte = t_oam[i].patterns_1; ser.Sync("bgdata", ref oam_byte); t_oam[i].patterns_1 = oam_byte;
}
}
public void Reset()
@ -264,93 +309,91 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
ppu_open_bus_decay_timer = new int[8];
}
void runppu(int x)
void runppu()
{
//run one ppu cycle at a time so we can interact with the ppu and clockPPU at high granularity
for (int i = 0; i < x; i++)
race_2006 = false;
if (install_2006>0)
{
race_2006 = false;
if (install_2006>0)
install_2006--;
if (install_2006==0)
{
install_2006--;
if (install_2006==0)
{
ppur.install_latches();
ppur.install_latches();
//nes.LogLine("addr wrote vt = {0}, ht = {1}", ppur._vt, ppur._ht);
//normally the address isnt observed by the board till it gets clocked by a read or write.
//but maybe thats just because a ppu read/write shoves it on the address bus
//apparently this shoves it on the address bus, too, or else blargg's mmc3 tests dont pass
//ONLY if the ppu is not rendering
if (ppur.status.sl == 241 || !PPUON)
nes.Board.AddressPPU(ppur.get_2007access());
//nes.LogLine("addr wrote vt = {0}, ht = {1}", ppur._vt, ppur._ht);
//normally the address isnt observed by the board till it gets clocked by a read or write.
//but maybe thats just because a ppu read/write shoves it on the address bus
//apparently this shoves it on the address bus, too, or else blargg's mmc3 tests dont pass
//ONLY if the ppu is not rendering
if (ppur.status.sl == 241 || !PPUON)
nes.Board.AddressPPU(ppur.get_2007access());
race_2006 = true;
}
}
if (install_2001 > 0)
{
install_2001--;
if (install_2001 == 0)
{
show_bg_new = reg_2001.show_bg;
show_obj_new = reg_2001.show_obj;
}
}
ppur.status.cycle++;
is_even_cycle = !is_even_cycle;
if (PPUON && ppur.status.cycle >= 257 && ppur.status.cycle <= 320 && 0 <= ppur.status.sl && ppur.status.sl <= 240)
{
reg_2003 = 0;
}
// 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
PpuOpenBusDecay(DecayType.None);
// Check for NMIs
if (NMI_PendingInstructions > 0)
{
NMI_PendingInstructions--;
if (NMI_PendingInstructions <= 0)
{
nes.cpu.NMI = true;
}
}
}
if (Reg2002_vblank_active_pending)
{
Reg2002_vblank_active = 1;
Reg2002_vblank_active_pending = false;
}
if (Reg2002_vblank_clear_pending)
{
Reg2002_vblank_active = 0;
Reg2002_vblank_clear_pending = false;
}
if (HasClockPPU)
{
nes.Board.ClockPPU();
race_2006 = true;
}
}
_totalCycles += x;
}
if (install_2001 > 0)
{
install_2001--;
if (install_2001 == 0)
{
show_bg_new = reg_2001.show_bg;
show_obj_new = reg_2001.show_obj;
}
}
ppur.status.cycle++;
is_even_cycle = !is_even_cycle;
if (PPUON && ppur.status.cycle >= 257 && ppur.status.cycle <= 320 && 0 <= ppur.status.sl && ppur.status.sl <= 240)
{
reg_2003 = 0;
}
// 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
PpuOpenBusDecay(DecayType.None);
// Check for NMIs
if (NMI_PendingInstructions > 0)
{
NMI_PendingInstructions--;
if (NMI_PendingInstructions <= 0)
{
nes.cpu.NMI = true;
}
}
}
if (Reg2002_vblank_active_pending)
{
Reg2002_vblank_active = 1;
Reg2002_vblank_active_pending = false;
}
if (Reg2002_vblank_clear_pending)
{
Reg2002_vblank_active = 0;
Reg2002_vblank_clear_pending = false;
}
if (HasClockPPU)
{
nes.Board.ClockPPU();
}
_totalCycles += 1;
}
}
}

File diff suppressed because it is too large Load Diff