From aa161d8910753e7c84df14b763321562a700b21e Mon Sep 17 00:00:00 2001 From: zeromus Date: Fri, 2 Nov 2012 19:28:00 +0000 Subject: [PATCH] proper Peek support in NES and 6502X --- BizHawk.Emulation/CPUs/MOS 6502X/MOS6502X.cs | 5 +- .../CPUs/MOS 6502X/MOS6502XDouble.cs | 9 +++ .../Consoles/Atari/2600/Atari2600.Core.cs | 10 +++ .../Consoles/Nintendo/NES/APU.cs | 22 +++++- .../Consoles/Nintendo/NES/BoardSystem.cs | 24 +++++++ .../Consoles/Nintendo/NES/Core.cs | 72 ++++++++++++++++++- .../Consoles/Nintendo/NES/NES.cs | 8 +-- .../Consoles/Nintendo/NES/PPU.regs.cs | 49 +++++++++++-- 8 files changed, 182 insertions(+), 17 deletions(-) diff --git a/BizHawk.Emulation/CPUs/MOS 6502X/MOS6502X.cs b/BizHawk.Emulation/CPUs/MOS 6502X/MOS6502X.cs index 1885690480..512ecb01e1 100644 --- a/BizHawk.Emulation/CPUs/MOS 6502X/MOS6502X.cs +++ b/BizHawk.Emulation/CPUs/MOS 6502X/MOS6502X.cs @@ -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 ReadMemory; public Func DummyReadMemory; + public Func PeekMemory; public Action WriteMemory; public void SetCallbacks ( Func ReadMemory, Func DummyReadMemory, + Func PeekMemory, Action WriteMemory, Action DisposeBuilder ) { this.ReadMemory = ReadMemory; this.DummyReadMemory = DummyReadMemory; + this.PeekMemory = PeekMemory; this.WriteMemory = WriteMemory; } diff --git a/BizHawk.Emulation/CPUs/MOS 6502X/MOS6502XDouble.cs b/BizHawk.Emulation/CPUs/MOS 6502X/MOS6502XDouble.cs index 8c04ad52e0..131f49695c 100644 --- a/BizHawk.Emulation/CPUs/MOS 6502X/MOS6502XDouble.cs +++ b/BizHawk.Emulation/CPUs/MOS 6502X/MOS6502XDouble.cs @@ -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 ReadMemory; //{ set { m.ReadMemory = value; n.ReadMemory = value; } } public Func DummyReadMemory; //{ set { m.DummyReadMemory = value; n.DummyReadMemory = value; } } + public Func PeekMemory; //{ set { m.ReadMemory = value; n.ReadMemory = value; } } public Action WriteMemory; //{ set { m.WriteMemory = value; n.WriteMemory = value; } } public void SetCallbacks ( Func ReadMemory, Func DummyReadMemory, + Func PeekMemory, Action WriteMemory, Action DisposeBuilder ) { this.ReadMemory = ReadMemory; this.DummyReadMemory = DummyReadMemory; + this.PeekMemory = PeekMemory; this.WriteMemory = WriteMemory; } diff --git a/BizHawk.Emulation/Consoles/Atari/2600/Atari2600.Core.cs b/BizHawk.Emulation/Consoles/Atari/2600/Atari2600.Core.cs index 31750eabd5..4327e4dbcf 100644 --- a/BizHawk.Emulation/Consoles/Atari/2600/Atari2600.Core.cs +++ b/BizHawk.Emulation/Consoles/Atari/2600/Atari2600.Core.cs @@ -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 diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/APU.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/APU.cs index c5bd1bbf67..ee76a580b0 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/APU.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/APU.cs @@ -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 diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs index ec2cae317b..51711d3fb0 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs @@ -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 } } + /// + /// derived classes should override this if they have peek-unsafe logic + /// + 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 diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Core.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Core.cs index cb7787ff54..c74806bde9 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/Core.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Core.cs @@ -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; diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs index 663dc62b39..21fdca3760 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs @@ -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; } diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/PPU.regs.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/PPU.regs.cs index 996be7b887..6b2bc0bee9 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/PPU.regs.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/PPU.regs.cs @@ -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;