[NES] general ppu timing, mmc3, and nt view fixups.
This commit is contained in:
parent
d0ea2f7106
commit
d05b81238e
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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; } }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue