proper Peek support in NES and 6502X

This commit is contained in:
zeromus 2012-11-02 19:28:00 +00:00
parent 5bf33b54a6
commit aa161d8910
8 changed files with 182 additions and 17 deletions

View File

@ -41,7 +41,7 @@ namespace BizHawk.Emulation.CPUs.M6502
public string State()
{
int notused;
string a = string.Format("{0:X4} {1:X2} {2} ", PC, ReadMemory(PC), Disassemble(PC, out notused)).PadRight(30);
string a = string.Format("{0:X4} {1:X2} {2} ", PC, PeekMemory(PC), Disassemble(PC, out notused)).PadRight(30);
string b = string.Format("A:{0:X2} X:{1:X2} Y:{2:X2} P:{3:X2} SP:{4:X2} Cy:{5}", A, X, Y, P, S, TotalExecutedCycles);
string val = a + b + " ";
if (FlagN) val = val + "N";
@ -167,18 +167,21 @@ namespace BizHawk.Emulation.CPUs.M6502
public Func<ushort, byte> ReadMemory;
public Func<ushort, byte> DummyReadMemory;
public Func<ushort, byte> PeekMemory;
public Action<ushort, byte> WriteMemory;
public void SetCallbacks
(
Func<ushort, byte> ReadMemory,
Func<ushort, byte> DummyReadMemory,
Func<ushort, byte> PeekMemory,
Action<ushort, byte> WriteMemory,
Action<System.Runtime.InteropServices.GCHandle> DisposeBuilder
)
{
this.ReadMemory = ReadMemory;
this.DummyReadMemory = DummyReadMemory;
this.PeekMemory = PeekMemory;
this.WriteMemory = WriteMemory;
}

View File

@ -34,6 +34,12 @@ namespace BizHawk.Emulation.CPUs.M6502
reads.Enqueue(ret);
return ret;
},
delegate(ushort addr)
{
byte ret = PeekMemory(addr);
reads.Enqueue(ret);
return ret;
},
delegate(ushort addr, byte value)
{
writes.Enqueue(value);
@ -108,18 +114,21 @@ namespace BizHawk.Emulation.CPUs.M6502
public Func<ushort, byte> ReadMemory; //{ set { m.ReadMemory = value; n.ReadMemory = value; } }
public Func<ushort, byte> DummyReadMemory; //{ set { m.DummyReadMemory = value; n.DummyReadMemory = value; } }
public Func<ushort, byte> PeekMemory; //{ set { m.ReadMemory = value; n.ReadMemory = value; } }
public Action<ushort, byte> WriteMemory; //{ set { m.WriteMemory = value; n.WriteMemory = value; } }
public void SetCallbacks
(
Func<ushort, byte> ReadMemory,
Func<ushort, byte> DummyReadMemory,
Func<ushort, byte> PeekMemory,
Action<ushort, byte> WriteMemory,
Action<System.Runtime.InteropServices.GCHandle> DisposeBuilder
)
{
this.ReadMemory = ReadMemory;
this.DummyReadMemory = DummyReadMemory;
this.PeekMemory = PeekMemory;
this.WriteMemory = WriteMemory;
}

View File

@ -104,6 +104,15 @@ namespace BizHawk
return temp;
}
public byte PeekMemory(ushort addr)
{
//TODO - this is dangerous, because at least, the lag flag can get set by a read
byte temp = mapper.ReadMemory((ushort)(addr & 0x1FFF));
return temp;
}
public void WriteMemory(ushort addr, byte value)
{
mapper.WriteMemory((ushort)(addr & 0x1FFF), value);
@ -154,6 +163,7 @@ namespace BizHawk
//cpu.debug = true;
cpu.ReadMemory = ReadMemory;
cpu.WriteMemory = WriteMemory;
cpu.PeekMemory = PeekMemory;
cpu.DummyReadMemory = ReadMemory;
// Setup TIA

View File

@ -926,13 +926,14 @@ namespace BizHawk.Emulation.Consoles.Nintendo
}
}
public byte ReadReg(int addr)
public byte PeekReg(int addr)
{
switch (addr)
{
case 0x4015:
{
//notice a missing bit here. should properly emulate with empty bus
//notice a missing bit here. should properly emulate with empty / Data bus
//if an interrupt flag was set at the same moment of the read, it will read back as 1 but it will not be cleared.
int dmc_nonzero = dmc.IsLenCntNonZero() ? 1 : 0;
int noise_nonzero = noise.IsLenCntNonZero() ? 1 : 0;
@ -940,10 +941,25 @@ namespace BizHawk.Emulation.Consoles.Nintendo
int pulse1_nonzero = pulse[1].IsLenCntNonZero() ? 1 : 0;
int pulse0_nonzero = pulse[0].IsLenCntNonZero() ? 1 : 0;
int ret = ((dmc_irq ? 1 : 0) << 7) | ((sequencer_irq ? 1 : 0) << 6) | (dmc_nonzero << 4) | (noise_nonzero << 3) | (tri_nonzero << 2) | (pulse1_nonzero << 1) | (pulse0_nonzero);
return (byte)ret;
}
default:
//don't return 0xFF here or SMB will break
return 0x00;
}
}
public byte ReadReg(int addr)
{
switch (addr)
{
case 0x4015:
{
byte ret = PeekReg(0x4015);
//Console.WriteLine("{0} {1,5} $4015 clear irq, was at {2}", nes.Frame, sequencer_counter, sequencer_irq);
sequencer_irq = false;
SyncIRQ();
return (byte)ret;
return ret;
}
default:
//don't return 0xFF here or SMB will break

View File

@ -26,6 +26,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
//gets called once per CPU clock; typically for boards with M2 counters
void ClockCPU();
byte PeekCart(int addr);
byte ReadPRG(int addr);
byte ReadPPU(int addr); byte PeekPPU(int addr);
void AddressPPU(int addr);
@ -236,6 +238,28 @@ namespace BizHawk.Emulation.Consoles.Nintendo
}
}
/// <summary>
/// derived classes should override this if they have peek-unsafe logic
/// </summary>
public virtual byte PeekCart(int addr)
{
byte ret;
if (addr >= 0x8000)
{
ret = ReadPRG(addr - 0x8000); //easy optimization, since rom reads are so common, move this up (reordering the rest of these elseifs is not easy)
}
else if (addr < 0x6000)
{
ret = ReadEXP(addr - 0x4000);
}
else
{
ret = ReadWRAM(addr - 0x6000);
}
return ret;
}
public virtual byte[] SaveRam
{
get

View File

@ -121,7 +121,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
cpu = new MOS6502X((h) => DisposeList.Add(h));
//cpu = new MOS6502X_CPP((h) => DisposeList.Add(h));
//cpu = new MOS6502XDouble((h) => DisposeList.Add(h));
cpu.SetCallbacks(ReadMemory, ReadMemory, WriteMemory, (h) => DisposeList.Add(h));
cpu.SetCallbacks(ReadMemory, ReadMemory, PeekMemory, WriteMemory, (h) => DisposeList.Add(h));
cpu.BCD_Enabled = false;
ppu = new PPU(this);
ram = new byte[0x800];
@ -303,6 +303,29 @@ namespace BizHawk.Emulation.Consoles.Nintendo
return 0xFF;
}
public byte PeekReg(int addr)
{
switch (addr)
{
case 0x4000: case 0x4001: case 0x4002: case 0x4003:
case 0x4004: case 0x4005: case 0x4006: case 0x4007:
case 0x4008: case 0x4009: case 0x400A: case 0x400B:
case 0x400C: case 0x400D: case 0x400E: case 0x400F:
case 0x4010: case 0x4011: case 0x4012: case 0x4013:
return apu.PeekReg(addr);
case 0x4014: /*OAM DMA*/ break;
case 0x4015: return apu.PeekReg(addr);
case 0x4016:
case 0x4017:
return peek_joyport(addr);
default:
//Console.WriteLine("read register: {0:x4}", addr);
break;
}
return 0xFF;
}
void WriteReg(int addr, byte val)
{
switch (addr)
@ -336,14 +359,24 @@ namespace BizHawk.Emulation.Consoles.Nintendo
byte read_joyport(int addr)
{
if (CoreInputComm.InputCallback != null) CoreInputComm.InputCallback();
return handle_read_joyport(addr, false);
}
byte peek_joyport(int addr)
{
return handle_read_joyport(addr, true);
}
byte handle_read_joyport(int addr, bool peek)
{
//read joystick port
//many todos here
lagged = false;
byte ret;
if (addr == 0x4016)
ret = ports[vs_io ? 1 : 0].Read();
ret = ports[vs_io ? 1 : 0].Read(peek);
else
ret = ports[vs_io ? 0 : 1].Read();
ret = ports[vs_io ? 0 : 1].Read(peek);
if (vs_io)
{
if (addr == 0x4016)
@ -444,6 +477,39 @@ namespace BizHawk.Emulation.Consoles.Nintendo
}
}
public byte PeekMemory(ushort addr)
{
byte ret;
if (addr >= 0x4020)
{
ret = board.PeekCart(addr); //easy optimization, since rom reads are so common, move this up (reordering the rest of these elseifs is not easy)
}
else if (addr < 0x0800)
{
ret = ram[addr];
}
else if (addr < 0x1000)
{
ret = ram[addr & 0x7FF];
}
else if (addr < 0x4000)
{
ret = ppu.ReadReg(addr & 7);
}
else if (addr < 0x4020)
{
ret = ReadReg(addr); //we're not rebasing the register just to keep register names canonical
}
else
{
throw new Exception("Woopsie-doodle!");
ret = 0xFF;
}
return ret;
}
//old data bus values from previous reads
public byte DB;

View File

@ -247,7 +247,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
interface IPortDevice
{
void Write(int value);
byte Read();
byte Read(bool peek);
void Update();
}
@ -284,10 +284,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo
Strobe();
state = value;
}
public override byte Read()
public override byte Read(bool peek)
{
int ret = value & 1;
value >>= 1;
if(!peek) value >>= 1;
return (byte)(ret | nes.DB);
}
public override void Update()
@ -302,7 +302,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public virtual void Write(int value)
{
}
public virtual byte Read()
public virtual byte Read(bool peek)
{
return 0xFF;
}

View File

@ -346,6 +346,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
reg_2000.Value = value;
}
byte read_2000() { return PPUGenLatch; }
byte peek_2000() { return PPUGenLatch; }
//PPU MASK (write)
void write_2001(byte value)
@ -354,6 +355,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
reg_2001.Value = value;
}
byte read_2001() { return PPUGenLatch; }
byte peek_2001() { return PPUGenLatch; }
//PPU STATUS (read)
void write_2002(byte value) { }
@ -363,14 +365,19 @@ namespace BizHawk.Emulation.Consoles.Nintendo
//i think we should only reset the state machine for 2005/2006
//ppur.clear_latches();
vtoggle = false;
int ret = (Reg2002_vblank_active << 7) | (Reg2002_objhit << 6) | (Reg2002_objoverflow << 5) | (PPUGenLatch & 0x1F);
byte ret = peek_2002();
vtoggle = false;
Reg2002_vblank_active = 0;
Reg2002_vblank_active_pending = false;
return (byte)ret;
return ret;
}
byte peek_2002()
{
return (byte)((Reg2002_vblank_active << 7) | (Reg2002_objhit << 6) | (Reg2002_objoverflow << 5) | (PPUGenLatch & 0x1F));
}
void clear_2002()
{
Reg2002_objhit = Reg2002_objoverflow = 0;
@ -384,6 +391,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
reg_2003 = value;
}
byte read_2003() { return PPUGenLatch; }
byte peek_2003() { return PPUGenLatch; }
//OAM DATA (write)
void write_2004(byte value)
@ -393,9 +401,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
OAM[reg_2003] = value;
reg_2003++;
}
byte read_2004() {
return OAM[reg_2003];
}
byte read_2004() { return OAM[reg_2003]; }
byte peek_2004() { return OAM[reg_2003]; }
//SCROLL (write)
void write_2005(byte value)
@ -415,6 +422,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
vtoggle ^= true;
}
byte read_2005() { return PPUGenLatch; }
byte peek_2005() { return PPUGenLatch; }
//VRAM address register (write)
void write_2006(byte value)
@ -445,6 +453,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
vtoggle ^= true;
}
byte read_2006() { return PPUGenLatch; }
byte peek_2006() { return PPUGenLatch; }
//VRAM data register (r/w)
void write_2007(byte value)
@ -501,6 +510,25 @@ namespace BizHawk.Emulation.Consoles.Nintendo
return ret;
}
byte peek_2007()
{
int addr = ppur.get_2007access() & 0x3FFF;
//ordinarily we return the buffered values
byte ret = VRAMBuffer;
//in any case, we read from the ppu bus
VRAMBuffer = ppubus_peek(addr);
//but reads from the palette are implemented in the PPU and return immediately
if ((addr & 0x3F00) == 0x3F00)
{
//TODO apply greyscale shit?
ret = PALRAM[addr & 0x1F];
}
return ret;
}
//--------
public byte ReadReg(int addr)
@ -512,6 +540,15 @@ namespace BizHawk.Emulation.Consoles.Nintendo
default: throw new InvalidOperationException();
}
}
public byte PeekReg(int addr)
{
switch (addr)
{
case 0: return peek_2000(); case 1: return peek_2001(); case 2: return peek_2002(); case 3: return peek_2003();
case 4: return peek_2004(); case 5: return peek_2005(); case 6: return peek_2006(); case 7: return peek_2007();
default: throw new InvalidOperationException();
}
}
public void WriteReg(int addr, byte value)
{
PPUGenLatch = value;