[nes] timing and accuracy work.
This commit is contained in:
parent
89ff0c8927
commit
e6afb2359b
|
@ -224,6 +224,7 @@
|
||||||
<Compile Include="Sound\Utilities\Waves.cs" />
|
<Compile Include="Sound\Utilities\Waves.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Content Include="Consoles\Nintendo\Docs\test_status.txt" />
|
||||||
<Content Include="Consoles\PC Engine\Compat.txt" />
|
<Content Include="Consoles\PC Engine\Compat.txt" />
|
||||||
<Content Include="Consoles\Sega\SMS\Compat.txt" />
|
<Content Include="Consoles\Sega\SMS\Compat.txt" />
|
||||||
<Content Include="Notes.txt" />
|
<Content Include="Notes.txt" />
|
||||||
|
|
|
@ -24,9 +24,20 @@ namespace BizHawk.Emulation.CPUs.M6502
|
||||||
}
|
}
|
||||||
if (IRQ && !FlagI)
|
if (IRQ && !FlagI)
|
||||||
{
|
{
|
||||||
|
if (SEI_Pending)
|
||||||
|
FlagI = true;
|
||||||
TriggerException(ExceptionType.IRQ);
|
TriggerException(ExceptionType.IRQ);
|
||||||
}
|
}
|
||||||
|
if (CLI_Pending)
|
||||||
|
{
|
||||||
|
FlagI = false;
|
||||||
|
CLI_Pending = false;
|
||||||
|
}
|
||||||
|
if (SEI_Pending)
|
||||||
|
{
|
||||||
|
FlagI = true;
|
||||||
|
SEI_Pending = false;
|
||||||
|
}
|
||||||
if(debug) Console.WriteLine(State());
|
if(debug) Console.WriteLine(State());
|
||||||
|
|
||||||
ushort this_pc = PC;
|
ushort this_pc = PC;
|
||||||
|
@ -209,7 +220,15 @@ TriggerException(ExceptionType.BRK);
|
||||||
PendingCycles -= 5; TotalExecutedCycles += 5;
|
PendingCycles -= 5; TotalExecutedCycles += 5;
|
||||||
break;
|
break;
|
||||||
case 0x28: // PLP
|
case 0x28: // PLP
|
||||||
P = ReadMemory((ushort)(++S + 0x100));
|
//handle I flag differently. sort of a sloppy way to do the job, but it does finish it off.
|
||||||
|
value8 = ReadMemory((ushort)(++S + 0x100));
|
||||||
|
if ((value8 & 0x04) != 0 && !FlagI)
|
||||||
|
SEI_Pending = true;
|
||||||
|
if ((value8 & 0x04) == 0 && FlagI)
|
||||||
|
CLI_Pending = true;
|
||||||
|
value8 &= unchecked((byte)~0x04);
|
||||||
|
P &= 0x04;
|
||||||
|
P |= value8;
|
||||||
FlagT = true;//this seems wrong
|
FlagT = true;//this seems wrong
|
||||||
PendingCycles -= 4; TotalExecutedCycles += 4;
|
PendingCycles -= 4; TotalExecutedCycles += 4;
|
||||||
break;
|
break;
|
||||||
|
@ -431,7 +450,8 @@ FlagT = true;// this seems wrong
|
||||||
PendingCycles -= 6; TotalExecutedCycles += 6;
|
PendingCycles -= 6; TotalExecutedCycles += 6;
|
||||||
break;
|
break;
|
||||||
case 0x58: // CLI
|
case 0x58: // CLI
|
||||||
FlagI = false;
|
//FlagI = false;
|
||||||
|
CLI_Pending = true;
|
||||||
PendingCycles -= 2; TotalExecutedCycles += 2;
|
PendingCycles -= 2; TotalExecutedCycles += 2;
|
||||||
break;
|
break;
|
||||||
case 0x59: // EOR addr,Y
|
case 0x59: // EOR addr,Y
|
||||||
|
@ -594,7 +614,8 @@ FlagT = true;// this seems wrong
|
||||||
PendingCycles -= 6; TotalExecutedCycles += 6;
|
PendingCycles -= 6; TotalExecutedCycles += 6;
|
||||||
break;
|
break;
|
||||||
case 0x78: // SEI
|
case 0x78: // SEI
|
||||||
FlagI = true;
|
//FlagI = true;
|
||||||
|
SEI_Pending = true;
|
||||||
PendingCycles -= 2; TotalExecutedCycles += 2;
|
PendingCycles -= 2; TotalExecutedCycles += 2;
|
||||||
break;
|
break;
|
||||||
case 0x79: // ADC addr,Y
|
case 0x79: // ADC addr,Y
|
||||||
|
|
|
@ -103,6 +103,9 @@ namespace BizHawk.Emulation.CPUs.M6502
|
||||||
|
|
||||||
public bool IRQ;
|
public bool IRQ;
|
||||||
public bool NMI;
|
public bool NMI;
|
||||||
|
public bool CLI_Pending;
|
||||||
|
public bool SEI_Pending;
|
||||||
|
public bool EscapeRequest;
|
||||||
|
|
||||||
public void SyncState(Serializer ser)
|
public void SyncState(Serializer ser)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
look for tests here: http://wiki.nesdev.com/w/index.php/Emulator_tests
|
||||||
|
|
||||||
|
blargg's 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.
|
||||||
|
|
||||||
|
blargg's ppu tests:
|
||||||
|
PASS for all but power_up_palette.nes which is lame
|
||||||
|
|
||||||
|
blargg's ppu_vbl_nmi:
|
||||||
|
FAIL for 9. But hardly anyone passes this.....
|
||||||
|
|
||||||
|
blargg's sprite hit tests:
|
||||||
|
FAIL for 9 and 10. fceux passes 10 (oldppu) and nintendulator passes both.
|
||||||
|
boath are not going to work any time soon due to sprite pattern decoding being separate from BG pattern fetching instead of interleaved.
|
||||||
|
|
||||||
|
all_instrs.nes
|
||||||
|
FAIL (freezes emu??). I think this requires undocumented instructions.
|
||||||
|
|
||||||
|
cpu_interrupts.nes
|
||||||
|
PASS
|
||||||
|
|
||||||
|
instr_misc.nes
|
||||||
|
PASS
|
||||||
|
|
||||||
|
instr_timing.nes
|
||||||
|
PASS
|
||||||
|
|
||||||
|
nestest.nes
|
||||||
|
PASS
|
||||||
|
|
||||||
|
official_only.nes
|
||||||
|
PASS
|
|
@ -15,6 +15,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
bool Configure(NES.EDetectionOrigin origin);
|
bool Configure(NES.EDetectionOrigin origin);
|
||||||
byte ReadPRG(int addr);
|
byte ReadPRG(int addr);
|
||||||
byte ReadPPU(int addr); byte PeekPPU(int addr);
|
byte ReadPPU(int addr); byte PeekPPU(int addr);
|
||||||
|
void AddressPPU(int addr);
|
||||||
byte ReadWRAM(int addr);
|
byte ReadWRAM(int addr);
|
||||||
byte ReadEXP(int addr);
|
byte ReadEXP(int addr);
|
||||||
void WritePRG(int addr, byte value);
|
void WritePRG(int addr, byte value);
|
||||||
|
@ -140,6 +141,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void AddressPPU(int addr) { }
|
||||||
public virtual byte PeekPPU(int addr) { return ReadPPU(addr); }
|
public virtual byte PeekPPU(int addr) { return ReadPPU(addr); }
|
||||||
|
|
||||||
public virtual byte ReadPPU(int addr)
|
public virtual byte ReadPPU(int addr)
|
||||||
|
|
|
@ -63,7 +63,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
public override void WritePRG(int addr, byte value)
|
public override void WritePRG(int addr, byte value)
|
||||||
{
|
{
|
||||||
if (ROM != null && bus_conflict) value = HandleNormalPRGConflict(addr,value);
|
if (ROM != null && bus_conflict) value = HandleNormalPRGConflict(addr,value);
|
||||||
prg = (value*2) & prg_mask;
|
int prg_bank = value & 7;
|
||||||
|
prg = (prg_bank * 2) & prg_mask;
|
||||||
if ((value & 0x10) == 0)
|
if ((value & 0x10) == 0)
|
||||||
SetMirrorType(NES.NESBoardBase.EMirrorType.OneScreenA);
|
SetMirrorType(NES.NESBoardBase.EMirrorType.OneScreenA);
|
||||||
else
|
else
|
||||||
|
|
|
@ -22,6 +22,13 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
{
|
{
|
||||||
bank_regs[8] = (byte)(num_prg_banks - 1);
|
bank_regs[8] = (byte)(num_prg_banks - 1);
|
||||||
bank_regs[9] = (byte)(num_prg_banks - 2);
|
bank_regs[9] = (byte)(num_prg_banks - 2);
|
||||||
|
bank_regs[0] = 0;
|
||||||
|
bank_regs[1] = 1;
|
||||||
|
bank_regs[2] = 2;
|
||||||
|
bank_regs[3] = 3;
|
||||||
|
bank_regs[4] = 4;
|
||||||
|
bank_regs[5] = 5;
|
||||||
|
sync_2k_chr();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -34,10 +41,28 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
public int chr_mode, prg_mode, reg_addr;
|
public int chr_mode, prg_mode, reg_addr;
|
||||||
|
|
||||||
//this contains the 8 programmable regs and 2 more at the end to represent PRG banks -2 and -1; and 4 more at the end to break down chr regs 0 and 1
|
//this contains the 8 programmable regs and 2 more at the end to represent PRG banks -2 and -1; and 4 more at the end to break down chr regs 0 and 1
|
||||||
|
//in other words:
|
||||||
|
//0: chr reg 0 (not used directly)
|
||||||
|
//1: chr reg 1 (not used directly)
|
||||||
|
//2,3,4,5: chr reg 2,3,4,5
|
||||||
|
//6,7: prg reg 6,7
|
||||||
|
//8,9: prg reg -2,-1
|
||||||
|
//10: chr reg 0A
|
||||||
|
//11: chr reg 0B
|
||||||
|
//12: chr reg 1A
|
||||||
|
//13: chr reg 1B
|
||||||
ByteBuffer bank_regs = new ByteBuffer(14);
|
ByteBuffer bank_regs = new ByteBuffer(14);
|
||||||
ByteBuffer prg_lookup = new ByteBuffer(new byte[] { 6, 7, 9, 8, 9, 7, 6, 8 });
|
ByteBuffer prg_lookup = new ByteBuffer(new byte[] { 6, 7, 9, 8, 9, 7, 6, 8 });
|
||||||
ByteBuffer chr_lookup = new ByteBuffer(new byte[] { 10, 11, 12, 13, 2, 3, 4, 5 });
|
ByteBuffer chr_lookup = new ByteBuffer(new byte[] { 10, 11, 12, 13, 2, 3, 4, 5 });
|
||||||
|
|
||||||
|
void sync_2k_chr()
|
||||||
|
{
|
||||||
|
bank_regs[10] = (byte)((bank_regs[0] & ~1) + 0);
|
||||||
|
bank_regs[11] = (byte)((bank_regs[0] & ~1) + 1);
|
||||||
|
bank_regs[12] = (byte)((bank_regs[1] & ~1) + 0);
|
||||||
|
bank_regs[13] = (byte)((bank_regs[1] & ~1) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void WritePRG(int addr, byte value)
|
public virtual void WritePRG(int addr, byte value)
|
||||||
{
|
{
|
||||||
switch (addr & 0x6001)
|
switch (addr & 0x6001)
|
||||||
|
@ -49,11 +74,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
break;
|
break;
|
||||||
case 0x0001: //$8001
|
case 0x0001: //$8001
|
||||||
bank_regs[reg_addr] = value;
|
bank_regs[reg_addr] = value;
|
||||||
//setup the 2K chr regs
|
sync_2k_chr();
|
||||||
bank_regs[10] = (byte)((bank_regs[0] & ~1) + 0);
|
|
||||||
bank_regs[11] = (byte)((bank_regs[0] & ~1) + 1);
|
|
||||||
bank_regs[12] = (byte)((bank_regs[1] & ~1) + 0);
|
|
||||||
bank_regs[13] = (byte)((bank_regs[1] & ~1) + 1);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,37 +135,49 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
case 0x2001: //$A001
|
case 0x2001: //$A001
|
||||||
//wram enable/protect
|
//wram enable/protect
|
||||||
break;
|
break;
|
||||||
case 0x4000: //$C000
|
case 0x4000: //$C000 - IRQ Reload value
|
||||||
irq_reload = value;
|
irq_reload = value;
|
||||||
break;
|
break;
|
||||||
case 0x4001: //$C001
|
case 0x4001: //$C001 - IRQ Clear
|
||||||
irq_counter = 0;
|
irq_counter = 0;
|
||||||
break;
|
break;
|
||||||
case 0x6000: //$E000
|
case 0x6000: //$E000 - IRQ Acknowledge / Disable
|
||||||
irq_enable = false;
|
irq_enable = false;
|
||||||
irq_pending = false;
|
irq_pending = false;
|
||||||
SyncIRQ();
|
SyncIRQ();
|
||||||
break;
|
break;
|
||||||
case 0x6001: //$E001
|
case 0x6001: //$E001 - IRQ Enable
|
||||||
irq_enable = true;
|
irq_enable = true;
|
||||||
SyncIRQ();
|
SyncIRQ();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IRQ_EQ_Pass()
|
||||||
|
{
|
||||||
|
if (irq_enable)
|
||||||
|
irq_pending = true;
|
||||||
|
SyncIRQ();
|
||||||
|
}
|
||||||
|
|
||||||
void ClockIRQ()
|
void ClockIRQ()
|
||||||
{
|
{
|
||||||
if (irq_counter == 0)
|
if (irq_counter == 0)
|
||||||
|
{
|
||||||
irq_counter = irq_reload;
|
irq_counter = irq_reload;
|
||||||
|
|
||||||
|
//TODO - MMC3 variant behaviour??? not sure
|
||||||
|
//was needed to pass 2-details.nes
|
||||||
|
if (irq_counter == 0)
|
||||||
|
IRQ_EQ_Pass();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
irq_counter--;
|
irq_counter--;
|
||||||
//Console.WriteLine(irq_counter);
|
//Console.WriteLine(irq_counter);
|
||||||
if (irq_counter == 0)
|
if (irq_counter == 0)
|
||||||
{
|
{
|
||||||
if (irq_enable)
|
IRQ_EQ_Pass();
|
||||||
irq_pending = true;
|
|
||||||
SyncIRQ();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,21 +274,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
//state
|
//state
|
||||||
protected MMC3 mmc3;
|
protected MMC3 mmc3;
|
||||||
|
|
||||||
public override byte ReadPPU(int addr)
|
public override void AddressPPU(int addr)
|
||||||
{
|
{
|
||||||
mmc3.Tick_PPU(addr);
|
mmc3.Tick_PPU(addr);
|
||||||
return base.ReadPPU(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void WritePPU(int addr, byte value)
|
|
||||||
{
|
|
||||||
mmc3.Tick_PPU(addr);
|
|
||||||
base.WritePPU(addr, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void WritePRG(int addr, byte value)
|
|
||||||
{
|
|
||||||
base.WritePRG(addr, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void BaseSetup()
|
protected override void BaseSetup()
|
||||||
|
|
|
@ -50,6 +50,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
//analyze board type
|
//analyze board type
|
||||||
switch (Cart.board_type)
|
switch (Cart.board_type)
|
||||||
{
|
{
|
||||||
|
case "TXROM-HOMEBREW":
|
||||||
|
break;
|
||||||
case "NES-TBROM": //tecmo world cup soccer (DE) [untested]
|
case "NES-TBROM": //tecmo world cup soccer (DE) [untested]
|
||||||
AssertPrg(64); AssertChr(64); AssertVram(0); AssertWram(0);
|
AssertPrg(64); AssertChr(64); AssertVram(0); AssertWram(0);
|
||||||
AssertBattery(false);
|
AssertBattery(false);
|
||||||
|
|
|
@ -96,13 +96,22 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
else
|
else
|
||||||
cycles <<= 4;
|
cycles <<= 4;
|
||||||
|
|
||||||
|
|
||||||
|
//tricky logic to try to run one instruction at a time
|
||||||
cpu_accumulate += cycles;
|
cpu_accumulate += cycles;
|
||||||
if (cpu_accumulate >= 48)
|
int cpu_cycles = cpu_accumulate / 48;
|
||||||
|
for (; ; )
|
||||||
{
|
{
|
||||||
int todo = cpu_accumulate / 48;
|
if (cpu_cycles == 0) break;
|
||||||
cpu_accumulate -= todo * 48;
|
int need_cpu = -cpu.PendingCycles + 1;
|
||||||
|
if (cpu_cycles < need_cpu) break;
|
||||||
|
if (need_cpu == 0) need_cpu = 1;
|
||||||
|
int todo = need_cpu;
|
||||||
|
cpu_cycles -= todo;
|
||||||
|
cpu_accumulate -= 48*todo;
|
||||||
cpu.Execute(todo);
|
cpu.Execute(todo);
|
||||||
apu.Run(todo);
|
apu.Run(todo);
|
||||||
|
ppu.TickCpu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,14 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
//when the ppu issues a write it goes through here and into the game board
|
//when the ppu issues a write it goes through here and into the game board
|
||||||
public void ppubus_write(int addr, byte value)
|
public void ppubus_write(int addr, byte value)
|
||||||
{
|
{
|
||||||
|
nes.board.AddressPPU(addr);
|
||||||
nes.board.WritePPU(addr, value);
|
nes.board.WritePPU(addr, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
//when the ppu issues a read it goes through here and into the game board
|
//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)
|
||||||
{
|
{
|
||||||
|
nes.board.AddressPPU(addr);
|
||||||
//apply freeze
|
//apply freeze
|
||||||
if (ppubus_freeze[addr].IsFrozen)
|
if (ppubus_freeze[addr].IsFrozen)
|
||||||
return ppubus_freeze[addr].value;
|
return ppubus_freeze[addr].value;
|
||||||
|
@ -89,17 +91,48 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
nes.cpu.NMI = true;
|
nes.cpu.NMI = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void TickCpu()
|
||||||
|
{
|
||||||
|
if (NMI_PendingCycles > 0)
|
||||||
|
{
|
||||||
|
NMI_PendingCycles--;
|
||||||
|
if (NMI_PendingCycles == 0)
|
||||||
|
{
|
||||||
|
TriggerNMI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void runppu(int x)
|
void runppu(int x)
|
||||||
{
|
{
|
||||||
//pputime+=x;
|
//pputime+=x;
|
||||||
|
|
||||||
//DON'T LIKE THIS....
|
//DON'T LIKE THIS....
|
||||||
ppur.status.cycle += x;
|
ppur.status.cycle += x;
|
||||||
if (ppur.status.cycle > ppur.status.end_cycle)
|
while(ppur.status.cycle >= ppur.status.end_cycle)
|
||||||
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)
|
||||||
|
{
|
||||||
|
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 (x == 0) return;
|
||||||
nes.RunCpu(x);
|
nes.RunCpu(x);
|
||||||
//pputime -= cputodo<<2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//hack
|
//hack
|
||||||
|
|
|
@ -294,6 +294,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
Bit Reg2002_objoverflow; //Sprite overflow. The PPU can handle only eight sprites on one scanline and sets this bit if it starts drawing sprites.
|
Bit Reg2002_objoverflow; //Sprite overflow. The PPU can handle only eight sprites on one scanline and sets this bit if it starts drawing sprites.
|
||||||
Bit Reg2002_objhit; //Sprite 0 overlap. Set when a nonzero pixel of sprite 0 is drawn overlapping a nonzero background pixel. Used for raster timing.
|
Bit Reg2002_objhit; //Sprite 0 overlap. Set when a nonzero pixel of sprite 0 is drawn overlapping a nonzero background pixel. Used for raster timing.
|
||||||
Bit Reg2002_vblank_active; //Vertical blank start (0: has not started; 1: has started)
|
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;
|
||||||
byte PPUGenLatch;
|
byte PPUGenLatch;
|
||||||
public PPUREGS ppur;
|
public PPUREGS ppur;
|
||||||
Reg_2000 reg_2000;
|
Reg_2000 reg_2000;
|
||||||
|
@ -324,11 +327,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
//PPU CONTROL (write)
|
//PPU CONTROL (write)
|
||||||
void write_2000(byte value)
|
void write_2000(byte value)
|
||||||
{
|
{
|
||||||
if (!reg_2000.vblank_nmi_gen & ((value & 0x80) != 0) && (Reg2002_vblank_active))
|
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
|
//if we just unleashed the vblank interrupt then activate it now
|
||||||
//FCEUX would use a "trigger NMI2" here in order to result in some delay effect
|
NMI_PendingCycles = 2;
|
||||||
TriggerNMI();
|
|
||||||
}
|
}
|
||||||
reg_2000.Value = value;
|
reg_2000.Value = value;
|
||||||
}
|
}
|
||||||
|
@ -354,12 +356,14 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
int ret = (Reg2002_vblank_active << 7) | (Reg2002_objhit << 6) | (Reg2002_objoverflow << 5) | (PPUGenLatch & 0x1F);
|
int ret = (Reg2002_vblank_active << 7) | (Reg2002_objhit << 6) | (Reg2002_objoverflow << 5) | (PPUGenLatch & 0x1F);
|
||||||
|
|
||||||
Reg2002_vblank_active = 0;
|
Reg2002_vblank_active = 0;
|
||||||
|
Reg2002_vblank_active_pending = false;
|
||||||
|
|
||||||
return (byte)ret;
|
return (byte)ret;
|
||||||
}
|
}
|
||||||
void clear_2002()
|
void clear_2002()
|
||||||
{
|
{
|
||||||
Reg2002_vblank_active = Reg2002_objhit = Reg2002_objoverflow = 0;
|
Reg2002_objhit = Reg2002_objoverflow = 0;
|
||||||
|
Reg2002_vblank_clear_pending = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//OAM ADDRESS (write)
|
//OAM ADDRESS (write)
|
||||||
|
@ -378,7 +382,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
OAM[reg_2003] = value;
|
OAM[reg_2003] = value;
|
||||||
reg_2003++;
|
reg_2003++;
|
||||||
}
|
}
|
||||||
byte read_2004() { return 0xFF; /* TODO !!!!!! THIS IS UGLY. WE SHOULD PASTE IT IN OR REWRITE IT BUT WE NEED TO ASK QEED FOR TEST CASES*/ }
|
byte read_2004() {
|
||||||
|
return OAM[reg_2003];
|
||||||
|
}
|
||||||
|
|
||||||
//SCROLL (write)
|
//SCROLL (write)
|
||||||
void write_2005(byte value)
|
void write_2005(byte value)
|
||||||
|
@ -414,6 +420,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
ppur._vt |= (value >> 5);
|
ppur._vt |= (value >> 5);
|
||||||
ppur._ht = value & 31;
|
ppur._ht = value & 31;
|
||||||
ppur.install_latches();
|
ppur.install_latches();
|
||||||
|
|
||||||
|
nes.board.AddressPPU(ppur.get_2007access());
|
||||||
}
|
}
|
||||||
vtoggle ^= true;
|
vtoggle ^= true;
|
||||||
}
|
}
|
||||||
|
@ -443,6 +451,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
}
|
}
|
||||||
|
|
||||||
ppur.increment2007(reg_2000.vram_incr32 != 0);
|
ppur.increment2007(reg_2000.vram_incr32 != 0);
|
||||||
|
int newaddr = ppur.get_2007access() & 0x3FFF;
|
||||||
|
nes.board.AddressPPU(newaddr);
|
||||||
}
|
}
|
||||||
byte read_2007()
|
byte read_2007()
|
||||||
{
|
{
|
||||||
|
@ -463,6 +473,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
|
|
||||||
ppur.increment2007(reg_2000.vram_incr32 != 0);
|
ppur.increment2007(reg_2000.vram_incr32 != 0);
|
||||||
|
|
||||||
|
int newaddr = ppur.get_2007access() & 0x3FFF;
|
||||||
|
nes.board.AddressPPU(newaddr);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
//--------
|
//--------
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//http://nesdev.parodius.com/bbs/viewtopic.php?p=4571&sid=db4c7e35316cc5d734606dd02f11dccb
|
//http://nesdev.parodius.com/bbs/viewtopic.php?p=4571&sid=db4c7e35316cc5d734606dd02f11dccb
|
||||||
|
|
||||||
|
//todo - read http://wiki.nesdev.com/w/index.php/PPU_sprite_priority
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
@ -85,17 +87,19 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Reg2002_vblank_active = 1;
|
Reg2002_vblank_active_pending = true;
|
||||||
ppuphase = PPUPHASE.VBL;
|
ppuphase = PPUPHASE.VBL;
|
||||||
|
|
||||||
//Not sure if this is correct. According to Matt Conte and my own tests, it is.
|
//Not sure if this is correct. According to Matt Conte and my own tests, it is. Timing is probably off, though.
|
||||||
//Timing is probably off, though.
|
|
||||||
//NOTE: Not having this here breaks a Super Donkey Kong game.
|
//NOTE: Not having this here breaks a Super Donkey Kong game.
|
||||||
reg_2003 = 0;
|
reg_2003 = 0;
|
||||||
const int delay = 20; //fceu used 12 here but I couldnt get it to work in marble madness and pirates.
|
|
||||||
|
|
||||||
runppu(delay); //X6502_Run(12);
|
//fceu/fceux had 12 here, but 15 was required to pass blargg's 05-nmi_timing.nes
|
||||||
if (reg_2000.vblank_nmi_gen) TriggerNMI();
|
const int delay = 15;
|
||||||
|
runppu(3);
|
||||||
|
bool nmi_destiny = reg_2000.vblank_nmi_gen && Reg2002_vblank_active;
|
||||||
|
runppu(delay - 3);
|
||||||
|
if (nmi_destiny) TriggerNMI();
|
||||||
if (PAL)
|
if (PAL)
|
||||||
runppu(70 * (kLineTime) - delay);
|
runppu(70 * (kLineTime) - delay);
|
||||||
else
|
else
|
||||||
|
@ -103,17 +107,6 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
|
|
||||||
//this seems to run just before the dummy scanline begins
|
//this seems to run just before the dummy scanline begins
|
||||||
clear_2002();
|
clear_2002();
|
||||||
//this early out caused metroid to fail to boot. I am leaving it here as a reminder of what not to do
|
|
||||||
//if(!PPUON) { runppu(kLineTime*242); goto finish; }
|
|
||||||
|
|
||||||
//There are 2 conditions that update all 5 PPU scroll counters with the
|
|
||||||
//contents of the latches adjacent to them. The first is after a write to
|
|
||||||
//2006/2. The second, is at the beginning of scanline 20, when the PPU starts
|
|
||||||
//rendering data for the first time in a frame (this update won't happen if
|
|
||||||
//all rendering is disabled via 2001.3 and 2001.4).
|
|
||||||
|
|
||||||
//if(PPUON)
|
|
||||||
// ppur.install_latches();
|
|
||||||
|
|
||||||
TempOAM* oams = stackalloc TempOAM[128];
|
TempOAM* oams = stackalloc TempOAM[128];
|
||||||
int* oamcounts = stackalloc int[2];
|
int* oamcounts = stackalloc int[2];
|
||||||
|
@ -142,6 +135,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
//two of those tiles were read in the last scanline.
|
//two of those tiles were read in the last scanline.
|
||||||
for (int xt = 0; xt < 32; xt++)
|
for (int xt = 0; xt < 32; xt++)
|
||||||
{
|
{
|
||||||
|
if (sl == 31 && xt == 31)
|
||||||
|
{
|
||||||
|
int zzz = 9;
|
||||||
|
}
|
||||||
Read_bgdata(ref bgdata[xt + 2]);
|
Read_bgdata(ref bgdata[xt + 2]);
|
||||||
|
|
||||||
//ok, we're also going to draw here.
|
//ok, we're also going to draw here.
|
||||||
|
@ -205,11 +202,16 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
//transparent pixel bailout
|
//transparent pixel bailout
|
||||||
if (spixel == 0) continue;
|
if (spixel == 0) continue;
|
||||||
|
|
||||||
|
//TODO - make sure we dont trigger spritehit if the edges are masked for either BG or OBJ
|
||||||
//spritehit:
|
//spritehit:
|
||||||
//1. is it sprite#0?
|
//1. is it sprite#0?
|
||||||
//2. is the bg pixel nonzero?
|
//2. is the bg pixel nonzero?
|
||||||
//then, it is spritehit.
|
//then, it is spritehit.
|
||||||
if (oam->index == 0 && (pixel & 3) != 0 && rasterpos < 255)
|
if (oam->index == 0)
|
||||||
|
{
|
||||||
|
int zzz = 9;
|
||||||
|
}
|
||||||
|
if (oam->index == 0 && pixel != 0 && rasterpos < 255)
|
||||||
{
|
{
|
||||||
Reg2002_objhit = true;
|
Reg2002_objhit = true;
|
||||||
}
|
}
|
||||||
|
@ -397,20 +399,16 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
|
|
||||||
ppuphase = PPUPHASE.BG;
|
ppuphase = PPUPHASE.BG;
|
||||||
|
|
||||||
//fetch BG: two tiles for next line
|
|
||||||
for (int xt = 0; xt < 2; xt++)
|
|
||||||
Read_bgdata(ref bgdata[xt]);
|
|
||||||
|
|
||||||
//I'm unclear of the reason why this particular access to memory is made.
|
//I'm unclear of the reason why this particular access to memory is made.
|
||||||
//The nametable address that is accessed 2 times in a row here, is also the
|
//The nametable address that is accessed 2 times in a row here, is also the
|
||||||
//same nametable address that points to the 3rd tile to be rendered on the
|
//same nametable address that points to the 3rd tile to be rendered on the
|
||||||
//screen (or basically, the first nametable address that will be accessed when
|
//screen (or basically, the first nametable address that will be accessed when
|
||||||
//the PPU is fetching background data on the next scanline).
|
//the PPU is fetching background data on the next scanline).
|
||||||
//(not implemented yet)
|
//(not implemented yet)
|
||||||
runppu(kFetchTime*2);
|
runppu(kFetchTime * 2);
|
||||||
if (sl == 0)
|
if (sl == 0)
|
||||||
{
|
{
|
||||||
if (idleSynch && reg_2001.PPUON && !PAL)
|
if (idleSynch && reg_2001.show_bg && !PAL)
|
||||||
ppur.status.end_cycle = 340;
|
ppur.status.end_cycle = 340;
|
||||||
else
|
else
|
||||||
ppur.status.end_cycle = 341;
|
ppur.status.end_cycle = 341;
|
||||||
|
@ -418,7 +416,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ppur.status.end_cycle = 341;
|
ppur.status.end_cycle = 341;
|
||||||
//runppu(kFetchTime);
|
|
||||||
|
//fetch BG: two tiles for next line
|
||||||
|
for (int xt = 0; xt < 2; xt++)
|
||||||
|
Read_bgdata(ref bgdata[xt]);
|
||||||
|
|
||||||
//After memory access 170, the PPU simply rests for 4 cycles (or the
|
//After memory access 170, the PPU simply rests for 4 cycles (or the
|
||||||
//equivelant of half a memory access cycle) before repeating the whole
|
//equivelant of half a memory access cycle) before repeating the whole
|
||||||
|
|
|
@ -55,12 +55,16 @@ static string ClassifyTable = @"
|
||||||
1 32 32 8 0 NES-SEROM; lolo
|
1 32 32 8 0 NES-SEROM; lolo
|
||||||
1 128 0 8 0 NES-SNROM; zelda
|
1 128 0 8 0 NES-SNROM; zelda
|
||||||
1 128 128 8 0 NES-SKROM; zelda 2
|
1 128 128 8 0 NES-SKROM; zelda 2
|
||||||
1 128 0 8 8 NES-SNROM; some of blargg's tests (apu)
|
1 32 0 8 8 NROM-HOMEBREW; instr_timing.nes
|
||||||
1 256 0 8 8 NES-SNROM; some of blargg's test (cpu tests)
|
1 64 0 8 8 NROM-HOMEBREW; instr_misc.nes
|
||||||
|
1 80 0 8 8 NROM-HOMEBREW; blargg's cpu_interrupts.nes
|
||||||
|
1 128 0 8 8 NES-SNROM; some of blargg's tests (apu) [TODO recheck as NROM-HOMEBREW]
|
||||||
|
1 256 0 8 8 NES-SNROM; some of blargg's test (cpu tests) [TODO recheck as NROM-HOMEBREW]
|
||||||
2 128 0 8 0 NES-UNROM; mega man
|
2 128 0 8 0 NES-UNROM; mega man
|
||||||
2 256 0 8 0 NES-UOROM; paperboy 2
|
2 256 0 8 0 NES-UOROM; paperboy 2
|
||||||
3 32 32 8 0 NES-CNROM; adventure island
|
3 32 32 8 0 NES-CNROM; adventure island
|
||||||
4 128 128 8 0 NES-TSROM; double dragon 2 (should be TL1ROM but maybe this will work)
|
4 128 128 8 0 NES-TSROM; double dragon 2 (should be TL1ROM but maybe this will work)
|
||||||
|
4 32 8 8 0 TXROM-HOMEBREW; blargg's mmc3 tests
|
||||||
7 128 0 8 0 NES-ANROM; marble madness
|
7 128 0 8 0 NES-ANROM; marble madness
|
||||||
7 256 0 8 8 NES-AOROM; battletoads
|
7 256 0 8 8 NES-AOROM; battletoads
|
||||||
11 32 16 8 0 Discrete_74x377
|
11 32 16 8 0 Discrete_74x377
|
||||||
|
|
|
@ -362,7 +362,6 @@ namespace M6502
|
||||||
w.WriteLine(" PendingCycles += cycles;");
|
w.WriteLine(" PendingCycles += cycles;");
|
||||||
w.WriteLine(" while (PendingCycles > 0)");
|
w.WriteLine(" while (PendingCycles > 0)");
|
||||||
w.WriteLine(" {");
|
w.WriteLine(" {");
|
||||||
|
|
||||||
w.WriteLine(" if (NMI)");
|
w.WriteLine(" if (NMI)");
|
||||||
w.WriteLine(" {");
|
w.WriteLine(" {");
|
||||||
w.WriteLine(" TriggerException(ExceptionType.NMI);");
|
w.WriteLine(" TriggerException(ExceptionType.NMI);");
|
||||||
|
@ -370,9 +369,20 @@ namespace M6502
|
||||||
w.WriteLine(" }");
|
w.WriteLine(" }");
|
||||||
w.WriteLine(" if (IRQ && !FlagI)");
|
w.WriteLine(" if (IRQ && !FlagI)");
|
||||||
w.WriteLine(" {");
|
w.WriteLine(" {");
|
||||||
|
w.WriteLine(" if (SEI_Pending)");
|
||||||
|
w.WriteLine(" FlagI = true;");
|
||||||
w.WriteLine(" TriggerException(ExceptionType.IRQ);");
|
w.WriteLine(" TriggerException(ExceptionType.IRQ);");
|
||||||
w.WriteLine(" }");
|
w.WriteLine(" }");
|
||||||
w.WriteLine("");
|
w.WriteLine(" if (CLI_Pending)");
|
||||||
|
w.WriteLine(" {");
|
||||||
|
w.WriteLine(" FlagI = false;");
|
||||||
|
w.WriteLine(" CLI_Pending = false;");
|
||||||
|
w.WriteLine(" }");
|
||||||
|
w.WriteLine(" if (SEI_Pending)");
|
||||||
|
w.WriteLine(" {");
|
||||||
|
w.WriteLine(" FlagI = true;");
|
||||||
|
w.WriteLine(" SEI_Pending = false;");
|
||||||
|
w.WriteLine(" }");
|
||||||
|
|
||||||
w.WriteLine(" if(debug) Console.WriteLine(State());");
|
w.WriteLine(" if(debug) Console.WriteLine(State());");
|
||||||
w.WriteLine("");
|
w.WriteLine("");
|
||||||
|
|
|
@ -82,7 +82,8 @@ namespace M6502
|
||||||
|
|
||||||
private void CLI(OpcodeInfo op, TextWriter w)
|
private void CLI(OpcodeInfo op, TextWriter w)
|
||||||
{
|
{
|
||||||
w.WriteLine(Spaces + "FlagI = false;");
|
w.WriteLine(Spaces + "//FlagI = false;");
|
||||||
|
w.WriteLine(Spaces + "CLI_Pending = true;");
|
||||||
w.WriteLine(Spaces + "PendingCycles -= {0}; TotalExecutedCycles += {0};", op.Cycles);
|
w.WriteLine(Spaces + "PendingCycles -= {0}; TotalExecutedCycles += {0};", op.Cycles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +245,15 @@ namespace M6502
|
||||||
|
|
||||||
private void PLP(OpcodeInfo op, TextWriter w)
|
private void PLP(OpcodeInfo op, TextWriter w)
|
||||||
{
|
{
|
||||||
w.WriteLine(Spaces + "P = ReadMemory((ushort)(++S + 0x100));");
|
w.WriteLine(Spaces + "//handle I flag differently. sort of a sloppy way to do the job, but it does finish it off.");
|
||||||
|
w.WriteLine(Spaces + "value8 = ReadMemory((ushort)(++S + 0x100));");
|
||||||
|
w.WriteLine(Spaces + "if ((value8 & 0x04) != 0 && !FlagI)");
|
||||||
|
w.WriteLine(Spaces + "\tSEI_Pending = true;");
|
||||||
|
w.WriteLine(Spaces + "if ((value8 & 0x04) == 0 && FlagI)");
|
||||||
|
w.WriteLine(Spaces + "\tCLI_Pending = true;");
|
||||||
|
w.WriteLine(Spaces + "value8 &= unchecked((byte)~0x04);");
|
||||||
|
w.WriteLine(Spaces + "P &= 0x04;");
|
||||||
|
w.WriteLine(Spaces + "P |= value8;");
|
||||||
w.WriteLine("FlagT = true;//this seems wrong");//this seems wrong
|
w.WriteLine("FlagT = true;//this seems wrong");//this seems wrong
|
||||||
w.WriteLine(Spaces + "PendingCycles -= {0}; TotalExecutedCycles += {0};", op.Cycles);
|
w.WriteLine(Spaces + "PendingCycles -= {0}; TotalExecutedCycles += {0};", op.Cycles);
|
||||||
}
|
}
|
||||||
|
@ -333,7 +342,8 @@ w.WriteLine("FlagT = true;// this seems wrong");//this seems wrong
|
||||||
|
|
||||||
private void SEI(OpcodeInfo op, TextWriter w)
|
private void SEI(OpcodeInfo op, TextWriter w)
|
||||||
{
|
{
|
||||||
w.WriteLine(Spaces + "FlagI = true;");
|
w.WriteLine(Spaces + "//FlagI = true;");
|
||||||
|
w.WriteLine(Spaces + "SEI_Pending = true;");
|
||||||
w.WriteLine(Spaces + "PendingCycles -= {0}; TotalExecutedCycles += {0};", op.Cycles);
|
w.WriteLine(Spaces + "PendingCycles -= {0}; TotalExecutedCycles += {0};", op.Cycles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue