2012-11-27 05:11:40 +00:00
|
|
|
|
using BizHawk.Emulation.CPUs.M6502;
|
|
|
|
|
using System;
|
2013-08-09 04:15:33 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Runtime.InteropServices;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
|
|
|
|
{
|
|
|
|
|
// an extension of the 6502 processor
|
|
|
|
|
|
2012-12-05 21:07:51 +00:00
|
|
|
|
public class MOS6510
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
2013-08-09 04:15:33 +00:00
|
|
|
|
private MOS6502X cpu;
|
|
|
|
|
private List<GCHandle> disposeList = new List<GCHandle>();
|
2012-11-27 05:11:40 +00:00
|
|
|
|
private bool freezeCpu;
|
2012-12-01 08:40:08 +00:00
|
|
|
|
private bool pinNMILast;
|
2013-08-13 12:23:32 +00:00
|
|
|
|
private LatchedPort port;
|
2012-11-27 20:23:27 +00:00
|
|
|
|
private bool unusedPin0;
|
|
|
|
|
private bool unusedPin1;
|
|
|
|
|
private uint unusedPinTTL0;
|
|
|
|
|
private uint unusedPinTTL1;
|
|
|
|
|
private uint unusedPinTTLCycles;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
|
2012-12-05 21:07:51 +00:00
|
|
|
|
public Func<int, byte> PeekMemory;
|
|
|
|
|
public Action<int, byte> PokeMemory;
|
|
|
|
|
public Func<bool> ReadAEC;
|
|
|
|
|
public Func<bool> ReadIRQ;
|
|
|
|
|
public Func<bool> ReadNMI;
|
|
|
|
|
public Func<bool> ReadRDY;
|
2013-08-14 05:05:17 +00:00
|
|
|
|
public Func<int, byte> ReadMemory;
|
2013-08-13 12:23:32 +00:00
|
|
|
|
public Func<byte> ReadPort;
|
2013-08-14 05:05:17 +00:00
|
|
|
|
public Action<int, byte> WriteMemory;
|
2012-12-05 21:07:51 +00:00
|
|
|
|
|
2012-11-27 05:11:40 +00:00
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
2012-12-05 21:07:51 +00:00
|
|
|
|
public MOS6510()
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
2013-08-09 04:15:33 +00:00
|
|
|
|
cpu = new MOS6502X();
|
2012-11-27 05:11:40 +00:00
|
|
|
|
|
|
|
|
|
// configure cpu r/w
|
|
|
|
|
cpu.DummyReadMemory = Read;
|
|
|
|
|
cpu.ReadMemory = Read;
|
|
|
|
|
cpu.WriteMemory = Write;
|
|
|
|
|
|
2012-11-27 20:23:27 +00:00
|
|
|
|
// todo: verify this value (I only know that unconnected bits fade after a number of cycles)
|
|
|
|
|
unusedPinTTLCycles = 40;
|
2012-12-01 08:40:08 +00:00
|
|
|
|
|
2013-08-12 07:00:31 +00:00
|
|
|
|
// perform hard reset
|
|
|
|
|
HardReset();
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-09 04:15:33 +00:00
|
|
|
|
~MOS6510()
|
|
|
|
|
{
|
|
|
|
|
foreach (GCHandle handle in disposeList)
|
|
|
|
|
{
|
|
|
|
|
handle.Free();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-27 05:11:40 +00:00
|
|
|
|
public void HardReset()
|
|
|
|
|
{
|
2013-08-12 07:00:31 +00:00
|
|
|
|
// configure CPU defaults
|
2012-11-27 05:11:40 +00:00
|
|
|
|
cpu.Reset();
|
|
|
|
|
cpu.FlagI = true;
|
|
|
|
|
cpu.BCD_Enabled = true;
|
2013-08-12 07:00:31 +00:00
|
|
|
|
if (ReadMemory != null)
|
2013-08-14 05:05:17 +00:00
|
|
|
|
cpu.PC = (ushort)(ReadMemory(0x0FFFC) | (ReadMemory(0x0FFFD) << 8));
|
2013-08-12 07:00:31 +00:00
|
|
|
|
|
|
|
|
|
// configure data port defaults
|
2013-08-13 12:23:32 +00:00
|
|
|
|
port = new LatchedPort();
|
|
|
|
|
port.Direction = 0x00;
|
2013-08-13 19:52:03 +00:00
|
|
|
|
port.Latch = 0xFF;
|
2013-08-12 07:00:31 +00:00
|
|
|
|
|
|
|
|
|
// NMI is high on startup (todo: verify)
|
|
|
|
|
pinNMILast = true;
|
|
|
|
|
|
|
|
|
|
// reset unused IO pin TTLs
|
|
|
|
|
unusedPinTTL0 = 0;
|
|
|
|
|
unusedPinTTL1 = 0;
|
|
|
|
|
}
|
2012-11-27 05:11:40 +00:00
|
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
|
|
|
|
public void ExecutePhase1()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ExecutePhase2()
|
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
if (ReadAEC() && !freezeCpu)
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
|
|
|
|
// the 6502 core expects active high
|
|
|
|
|
// so we reverse the polarity here
|
2012-12-05 21:07:51 +00:00
|
|
|
|
bool thisNMI = ReadNMI();
|
2012-12-01 08:40:08 +00:00
|
|
|
|
if (!thisNMI && pinNMILast)
|
|
|
|
|
cpu.NMI = true;
|
|
|
|
|
else
|
|
|
|
|
cpu.NMI = false;
|
|
|
|
|
pinNMILast = thisNMI;
|
|
|
|
|
|
2012-12-05 21:07:51 +00:00
|
|
|
|
cpu.IRQ = !ReadIRQ();
|
2012-11-27 05:11:40 +00:00
|
|
|
|
cpu.ExecuteOne();
|
|
|
|
|
}
|
2012-11-27 20:23:27 +00:00
|
|
|
|
|
2012-12-02 22:15:08 +00:00
|
|
|
|
// unfreeze cpu if BA is high
|
2012-12-05 21:07:51 +00:00
|
|
|
|
if (ReadRDY()) freezeCpu = false;
|
2012-12-02 22:15:08 +00:00
|
|
|
|
|
2012-11-27 20:23:27 +00:00
|
|
|
|
// process unused pin TTL
|
|
|
|
|
if (unusedPinTTL0 == 0)
|
|
|
|
|
unusedPin0 = false;
|
|
|
|
|
else
|
|
|
|
|
unusedPinTTL0--;
|
|
|
|
|
|
|
|
|
|
if (unusedPinTTL1 == 0)
|
|
|
|
|
unusedPin1 = false;
|
|
|
|
|
else
|
|
|
|
|
unusedPinTTL1--;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
2012-12-07 21:01:22 +00:00
|
|
|
|
public ushort PC
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return cpu.PC;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-27 05:11:40 +00:00
|
|
|
|
public byte Peek(int addr)
|
|
|
|
|
{
|
|
|
|
|
if (addr == 0x0000)
|
2013-08-13 12:23:32 +00:00
|
|
|
|
return port.Direction;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
else if (addr == 0x0001)
|
|
|
|
|
return PortData;
|
|
|
|
|
else
|
2012-12-05 21:07:51 +00:00
|
|
|
|
return PeekMemory(addr);
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Poke(int addr, byte val)
|
|
|
|
|
{
|
2013-08-14 05:05:17 +00:00
|
|
|
|
if (addr == 0x0000)
|
2013-08-13 12:23:32 +00:00
|
|
|
|
port.Direction = val;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
else if (addr == 0x0001)
|
2013-08-13 12:23:32 +00:00
|
|
|
|
port.Latch = val;
|
2012-11-30 21:12:23 +00:00
|
|
|
|
else
|
2012-12-05 21:07:51 +00:00
|
|
|
|
PokeMemory(addr, val);
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-13 12:23:32 +00:00
|
|
|
|
public byte PortData
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2013-08-13 19:52:03 +00:00
|
|
|
|
return port.ReadInput(ReadPort());
|
2013-08-13 12:23:32 +00:00
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
port.Latch = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte Read(ushort addr)
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
|
|
|
|
// cpu freezes after first read when RDY is low
|
2012-12-05 21:07:51 +00:00
|
|
|
|
if (!ReadRDY())
|
2012-11-27 05:11:40 +00:00
|
|
|
|
freezeCpu = true;
|
|
|
|
|
|
|
|
|
|
if (addr == 0x0000)
|
2013-08-13 12:23:32 +00:00
|
|
|
|
return port.Direction;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
else if (addr == 0x0001)
|
|
|
|
|
return PortData;
|
|
|
|
|
else
|
2012-12-05 21:07:51 +00:00
|
|
|
|
return ReadMemory(addr);
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-13 12:23:32 +00:00
|
|
|
|
public void SyncState(Serializer ser)
|
|
|
|
|
{
|
|
|
|
|
cpu.SyncState(ser);
|
|
|
|
|
ser.Sync("freezeCpu", ref freezeCpu);
|
|
|
|
|
ser.Sync("pinNMILast", ref pinNMILast);
|
|
|
|
|
ser.Sync("unusedPin0", ref unusedPin0);
|
|
|
|
|
ser.Sync("unusedPin1", ref unusedPin1);
|
|
|
|
|
ser.Sync("unusedPinTTL0", ref unusedPinTTL0);
|
|
|
|
|
ser.Sync("unusedPinTTL1", ref unusedPinTTL1);
|
|
|
|
|
ser.Sync("unusedPinTTLCycles", ref unusedPinTTLCycles);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Write(ushort addr, byte val)
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
|
|
|
|
if (addr == 0x0000)
|
2013-08-13 12:23:32 +00:00
|
|
|
|
port.Direction = val;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
else if (addr == 0x0001)
|
2013-08-13 12:23:32 +00:00
|
|
|
|
port.Latch = val;
|
2012-12-05 21:07:51 +00:00
|
|
|
|
WriteMemory(addr, val);
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|