[NES] general ppu timing, mmc3, and nt view fixups.

This commit is contained in:
zeromus 2011-06-07 07:14:34 +00:00
parent d0ea2f7106
commit d05b81238e
12 changed files with 116 additions and 74 deletions

View File

@ -3,13 +3,13 @@ look for tests here: http://wiki.nesdev.com/w/index.php/Emulator_tests
==blargg's tests==
MMC3:
FAIL for 4 and 6. i think 4 might not fail until some invisible PPU operations are correct. neither nintendulator or fceux pass these.
FAIL for 6. neither nintendulator or fceux pass this. we should be able to pass it once mmc3 variant support is finalized.
ppu tests:
PASS for all but power_up_palette.nes which is lame
ppu_vbl_nmi:
FAIL for 9. But hardly anyone passes this.....
FAIL for 10. But hardly anyone passes this.....
sprite hit tests:
FAIL for 9 and 10. fceux passes 10 (oldppu) and nintendulator passes both.
@ -21,9 +21,6 @@ cpu_timing_test.nes
official_only.nes
PASS
all_instrs.nes
FAIL (freezes emu??). I think this requires undocumented instructions.
instr_timing.nes
PASS
@ -33,6 +30,9 @@ cpu_interrupts.nes
instr_misc.nes
PASS
all_instrs.nes
FAIL (undocumented opcodes)
=============
nestest.nes

View File

@ -13,6 +13,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo
{
void Create(NES nes);
bool Configure(NES.EDetectionOrigin origin);
//gets called once per PPU clock, for boards with complex behaviour which must be monitoring clock (i.e. mmc3 irq counter)
void ClockPPU();
byte ReadPRG(int addr);
byte ReadPPU(int addr); byte PeekPPU(int addr);
void AddressPPU(int addr);
@ -49,6 +53,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
this.NES = nes;
}
public abstract bool Configure(NES.EDetectionOrigin origin);
public virtual void ClockPPU() { }
public CartInfo Cart { get { return NES.cart; } }
public NES NES { get; set; }

View File

@ -109,7 +109,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
//state
public NES.NESBoardBase.EMirrorType mirror;
int ppubus_state, ppubus_statecounter;
int a12_old;
byte irq_reload, irq_counter;
bool irq_pending, irq_enable;
@ -147,6 +147,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
SyncIRQ();
break;
case 0x6001: //$E001 - IRQ Enable
//board.NES.LogLine("irq en");
irq_enable = true;
SyncIRQ();
break;
@ -156,7 +157,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo
void IRQ_EQ_Pass()
{
if (irq_enable)
{
//board.NES.LogLine("mmc3 IRQ");
irq_pending = true;
}
SyncIRQ();
}
@ -174,7 +178,6 @@ namespace BizHawk.Emulation.Consoles.Nintendo
else
{
irq_counter--;
//Console.WriteLine(irq_counter);
if (irq_counter == 0)
{
IRQ_EQ_Pass();
@ -182,20 +185,48 @@ namespace BizHawk.Emulation.Consoles.Nintendo
}
}
//TODO - this should be determined from NES timestamps to correctly emulate ppu writes interfering
public void Tick_PPU(int addr)
//it really seems like these should be the same but i cant seem to unify them.
//theres no sense in delaying the IRQ, so its logic must be tied to the separator.
//the hint, of course, is that the countdown value is the same.
//will someone else try to unify them?
int separator_counter;
int irq_countdown;
public void ClockPPU()
{
ppubus_statecounter++;
int state = (addr >> 12) & 1;
if (ppubus_state == 0 && ppubus_statecounter > 1 && state == 1)
if (separator_counter > 0)
separator_counter--;
if (irq_countdown > 0)
{
ppubus_statecounter = 0;
ClockIRQ();
irq_countdown--;
if (irq_countdown == 0)
{
//board.NES.LogLine("ClockIRQ");
ClockIRQ();
}
}
if (ppubus_state != state)
}
public void AddressPPU(int addr)
{
int a12 = (addr >> 12) & 1;
bool rising_edge = (a12 == 1 && a12_old == 0);
if (rising_edge)
{
ppubus_state = state;
if (separator_counter > 0)
{
separator_counter = 15;
}
else
{
separator_counter = 15;
irq_countdown = 15;
}
}
a12_old = a12;
}
}
@ -276,9 +307,14 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public override void AddressPPU(int addr)
{
mmc3.Tick_PPU(addr);
mmc3.AddressPPU(addr);
}
public override void ClockPPU()
{
mmc3.ClockPPU();
}
protected override void BaseSetup()
{
wram_mask = (Cart.wram_size * 1024) - 1;

View File

@ -33,7 +33,6 @@ namespace BizHawk.Emulation.Consoles.Nintendo
int use_ram = (bank_1k >> 6) & 1;
if (use_ram == 1)
{
mmc3.Tick_PPU(addr);
addr = ((bank_1k&0x3f) << 10) | (addr & 0x3FF);
addr &= 0x1FFF;
return VRAM[addr];
@ -52,7 +51,6 @@ namespace BizHawk.Emulation.Consoles.Nintendo
int use_ram = (bank_1k >> 6) & 1;
if (use_ram == 1)
{
mmc3.Tick_PPU(addr);
addr = ((bank_1k & 0x3f) << 10) | (addr & 0x3FF);
addr &= 0x1FFF;
VRAM[addr] = value;

View File

@ -30,7 +30,6 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public override byte ReadPPU(int addr)
{
mmc3.Tick_PPU(addr);
if (addr < 0x2000)
{
//read patterns from mapper controlled area
@ -44,7 +43,6 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public override void WritePPU(int addr, byte value)
{
mmc3.Tick_PPU(addr);
if (addr < 0x2000)
{
//nothing wired here

View File

@ -90,6 +90,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
protected void RunCpu(int ppu_cycles)
{
//not being used right now. maybe needed later.
//Timestamp += ppu_cycles;
int cycles = ppu_cycles;
if (ppu.PAL)
cycles *= 15;
@ -111,7 +114,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
cpu_accumulate -= 48*todo;
cpu.Execute(todo);
apu.Run(todo);
ppu.TickCpu();
ppu.PostCpuInstruction(todo);
}
}

View File

@ -231,6 +231,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
bool lagged = true;
bool islag = false;
public int Frame { get { return _frame; } set { _frame = value; } }
public long Timestamp { get; private set; }
public int LagCount { get { return _lagcount; } set { _lagcount = value; } }
public bool IsLagFrame { get { return islag; } }

View File

@ -23,14 +23,19 @@ namespace BizHawk.Emulation.Consoles.Nintendo
}
//when the ppu issues a read it goes through here and into the game board
public byte ppubus_read(int addr)
public byte ppubus_read(int addr, bool ppu)
{
//speculative -- hardware doesnt touch the bus when the PPU is disabled
//(without this, smb3 title screen creates garbage when skipped)
if (!reg_2001.PPUON && ppu)
return 0xFF;
nes.board.AddressPPU(addr);
//apply freeze
if (ppubus_freeze[addr].IsFrozen)
return ppubus_freeze[addr].value;
else
return nes.board.ReadPPU(addr);
if (ppubus_freeze[addr].IsFrozen)
return ppubus_freeze[addr].value;
else
return nes.board.ReadPPU(addr);
}
//debug tools peek into the ppu through this
@ -91,12 +96,15 @@ namespace BizHawk.Emulation.Consoles.Nintendo
nes.cpu.NMI = true;
}
public void TickCpu()
//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 PostCpuInstruction(int todo)
{
if (NMI_PendingCycles > 0)
if (NMI_PendingInstructions > 0)
{
NMI_PendingCycles--;
if (NMI_PendingCycles == 0)
NMI_PendingInstructions--;
if (NMI_PendingInstructions <= 0)
{
TriggerNMI();
}
@ -105,34 +113,31 @@ namespace BizHawk.Emulation.Consoles.Nintendo
void runppu(int x)
{
//pputime+=x;
//DON'T LIKE THIS....
ppur.status.cycle += x;
while(ppur.status.cycle >= ppur.status.end_cycle)
ppur.status.cycle -= ppur.status.end_cycle;
if(x == 0) return;
nes.RunCpu(1);
x--;
if (Reg2002_vblank_active_pending)
//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++)
{
//might not actually run a cpu cycle if there are none to be run right now
nes.RunCpu(1);
if (Reg2002_vblank_active_pending)
Reg2002_vblank_active = 1;
Reg2002_vblank_active_pending = false;
}
{
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 (Reg2002_vblank_clear_pending)
{
Reg2002_vblank_active = 0;
Reg2002_vblank_clear_pending = false;
}
if (x == 0) return;
nes.RunCpu(x);
nes.board.ClockPPU();
}
}
//hack

View File

@ -296,7 +296,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
Bit Reg2002_vblank_active; //Vertical blank start (0: has not started; 1: has started)
bool Reg2002_vblank_active_pending; //set of Reg2002_vblank_active is pending
bool Reg2002_vblank_clear_pending; //ppu's clear of vblank flag is pending
int NMI_PendingCycles;
int NMI_PendingInstructions;
byte PPUGenLatch;
public PPUREGS ppur;
public Reg_2000 reg_2000;
@ -330,7 +330,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
if (!reg_2000.vblank_nmi_gen & ((value & 0x80) != 0) && (Reg2002_vblank_active) && !Reg2002_vblank_clear_pending)
{
//if we just unleashed the vblank interrupt then activate it now
NMI_PendingCycles = 2;
NMI_PendingInstructions = 2;
}
reg_2000.Value = value;
}
@ -465,7 +465,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
byte ret = VRAMBuffer;
//in any case, we read from the ppu bus
VRAMBuffer = ppubus_read(addr);
VRAMBuffer = ppubus_read(addr,false);
//but reads from the palette are implemented in the PPU and return immediately
if ((addr & 0x3F00) == 0x3F00)

View File

@ -27,11 +27,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo
void Read_bgdata(ref BGDataRecord bgdata) {
int addr = ppur.get_ntread();
bgdata.nt = ppubus_read(addr);
bgdata.nt = ppubus_read(addr, true);
runppu(kFetchTime);
addr = ppur.get_atread();
byte at = ppubus_read(addr);
byte at = ppubus_read(addr, true);
//modify at to get appropriate palette shift
if((ppur.vt&2)!=0) at >>= 4;
@ -53,10 +53,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo
runppu(1);
addr = ppur.get_ptread(bgdata.nt);
bgdata.pt_0 = ppubus_read(addr);
bgdata.pt_0 = ppubus_read(addr, true);
runppu(kFetchTime);
addr |= 8;
bgdata.pt_1 = ppubus_read(addr);
bgdata.pt_1 = ppubus_read(addr, true);
runppu(kFetchTime);
}
@ -380,11 +380,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo
//pattern table fetches
int addr = patternAddress;
oam->patterns[0] = ppubus_read(addr);
oam->patterns[0] = ppubus_read(addr, true);
if (realSprite) runppu(kFetchTime);
addr += 8;
oam->patterns[1] = ppubus_read(addr);
oam->patterns[1] = ppubus_read(addr, true);
if (realSprite) runppu(kFetchTime);
//hflip
@ -405,7 +405,6 @@ namespace BizHawk.Emulation.Consoles.Nintendo
//screen (or basically, the first nametable address that will be accessed when
//the PPU is fetching background data on the next scanline).
//(not implemented yet)
runppu(kFetchTime * 2);
if (sl == 0)
{
if (idleSynch && reg_2001.show_bg && !PAL)
@ -421,6 +420,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
for (int xt = 0; xt < 2; xt++)
Read_bgdata(ref bgdata[xt]);
runppu(kFetchTime * 2);
//After memory access 170, the PPU simply rests for 4 cycles (or the
//equivelant of half a memory access cycle) before repeating the whole
//pixel/scanline rendering process. If the scanline being rendered is the very

View File

@ -63,8 +63,8 @@ namespace BizHawk.MultiClient
int nt = ppu.ppubus_peek(ntbyte_ptr + 0x2000);
int at = ppu.ppubus_peek(atbyte_ptr + 0x2000);
if((ty&1)!=0) at >>= 4;
if((tx&1)!=0) at >>= 2;
if((ty&2)!=0) at >>= 4;
if((tx&2)!=0) at >>= 2;
at &= 0x03;
at <<= 2;
@ -74,7 +74,11 @@ namespace BizHawk.MultiClient
int pt_0 = ppu.ppubus_peek(pt_addr);
int pt_1 = ppu.ppubus_peek(pt_addr + 8);
int pixel = ((pt_0 >> (7 - bgpx)) & 1) | (((pt_1 >> (7 - bgpx)) & 1) << 1);
pixel |= at;
//if the pixel is transparent, draw the backdrop color
//TODO - consider making this optional? nintendulator does it and fceux doesnt need to do it due to buggy palette logic which creates the same effect
if(pixel!=0)
pixel |= at;
pixel = ppu.PALRAM[pixel];
int cvalue = Nes.LookupColor(pixel);

View File

@ -115,15 +115,6 @@ namespace BizHawk.MultiClient
}
PatternView.pattern.UnlockBits(bmpdata);
PatternView.Refresh();
//Nametable viewer
for (int i = 0; i < 30; i++)
{
for (int j = 0; j < 32; j++)
{
int v = Nes.ppu.ppubus_read(0x2000 + i + j);
}
}
}
private void NESPPU_Load(object sender, EventArgs e)