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-06 04:29:08 +00:00
|
|
|
|
private bool pinCassetteButton; // note: these are only
|
|
|
|
|
private bool pinCassetteMotor; // latches!
|
2012-11-27 05:11:40 +00:00
|
|
|
|
private bool pinCassetteOutput;
|
|
|
|
|
private bool pinCharen;
|
|
|
|
|
private bool pinLoram;
|
|
|
|
|
private bool pinHiram;
|
2012-12-01 08:40:08 +00:00
|
|
|
|
private bool pinNMILast;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
private byte portDir;
|
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;
|
2012-12-06 04:29:08 +00:00
|
|
|
|
public Func<bool> ReadCassetteButton;
|
2012-12-05 21:07:51 +00:00
|
|
|
|
public Func<bool> ReadIRQ;
|
|
|
|
|
public Func<bool> ReadNMI;
|
|
|
|
|
public Func<bool> ReadRDY;
|
|
|
|
|
public Func<ushort, byte> ReadMemory;
|
2012-12-06 04:29:08 +00:00
|
|
|
|
public Action<bool> WriteCassetteLevel;
|
|
|
|
|
public Action<bool> WriteCassetteMotor;
|
2012-12-05 21:07:51 +00:00
|
|
|
|
public Action<ushort, byte> WriteMemory;
|
|
|
|
|
|
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)
|
|
|
|
|
cpu.PC = (ushort)(ReadMemory(0xFFFC) | (ReadMemory(0xFFFD) << 8));
|
|
|
|
|
|
|
|
|
|
// configure data port defaults
|
|
|
|
|
portDir = 0x00;
|
|
|
|
|
SetPortData(0x1F);
|
|
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
return PortDirection;
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
if (addr == 0x0000)
|
2012-12-01 08:40:08 +00:00
|
|
|
|
SetPortDir(val);
|
2012-11-27 05:11:40 +00:00
|
|
|
|
else if (addr == 0x0001)
|
|
|
|
|
SetPortData(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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte Read(ushort addr)
|
|
|
|
|
{
|
|
|
|
|
// 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)
|
|
|
|
|
return PortDirection;
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Write(ushort addr, byte val)
|
|
|
|
|
{
|
|
|
|
|
if (addr == 0x0000)
|
|
|
|
|
PortDirection = val;
|
|
|
|
|
else if (addr == 0x0001)
|
|
|
|
|
PortData = val;
|
2012-12-05 21:07:51 +00:00
|
|
|
|
WriteMemory(addr, val);
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
2012-12-06 04:29:08 +00:00
|
|
|
|
public bool CassetteButton
|
|
|
|
|
{
|
|
|
|
|
get { return pinCassetteButton; }
|
|
|
|
|
set { pinCassetteButton = value; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool CassetteMotor
|
|
|
|
|
{
|
|
|
|
|
get { return pinCassetteMotor; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool CassetteOutputLevel
|
|
|
|
|
{
|
|
|
|
|
get { return pinCassetteOutput; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-30 21:12:23 +00:00
|
|
|
|
public bool Charen
|
|
|
|
|
{
|
|
|
|
|
get { return pinCharen; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool HiRam
|
|
|
|
|
{
|
|
|
|
|
get { return pinHiram; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool LoRam
|
|
|
|
|
{
|
|
|
|
|
get { return pinLoram; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-27 05:11:40 +00:00
|
|
|
|
public byte PortData
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2013-08-09 03:14:11 +00:00
|
|
|
|
//byte result = 0x00;
|
|
|
|
|
byte result = (byte)(~portDir & 0xEF);
|
2012-11-27 05:11:40 +00:00
|
|
|
|
|
|
|
|
|
result |= pinLoram ? (byte)0x01 : (byte)0x00;
|
|
|
|
|
result |= pinHiram ? (byte)0x02 : (byte)0x00;
|
|
|
|
|
result |= pinCharen ? (byte)0x04 : (byte)0x00;
|
|
|
|
|
result |= pinCassetteOutput ? (byte)0x08 : (byte)0x00;
|
|
|
|
|
result |= pinCassetteButton ? (byte)0x10 : (byte)0x00;
|
|
|
|
|
result |= pinCassetteMotor ? (byte)0x20 : (byte)0x00;
|
2012-11-27 20:23:27 +00:00
|
|
|
|
result |= unusedPin0 ? (byte)0x40 : (byte)0x00;
|
|
|
|
|
result |= unusedPin1 ? (byte)0x80 : (byte)0x00;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
byte val = Port.CPUWrite(PortData, value, portDir);
|
|
|
|
|
SetPortData(val);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte PortDirection
|
|
|
|
|
{
|
|
|
|
|
get { return portDir; }
|
2012-11-30 21:12:23 +00:00
|
|
|
|
set
|
|
|
|
|
{
|
2012-12-01 08:40:08 +00:00
|
|
|
|
SetPortDir(value);
|
2012-11-30 21:12:23 +00:00
|
|
|
|
}
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-12-05 21:07:51 +00:00
|
|
|
|
public byte ReadPortData()
|
|
|
|
|
{
|
|
|
|
|
return PortData;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-27 05:11:40 +00:00
|
|
|
|
private void SetPortData(byte val)
|
|
|
|
|
{
|
|
|
|
|
pinCassetteOutput = ((val & 0x08) != 0);
|
|
|
|
|
pinCassetteButton = ((val & 0x10) != 0);
|
|
|
|
|
pinCassetteMotor = ((val & 0x20) != 0);
|
|
|
|
|
|
2012-12-02 15:10:37 +00:00
|
|
|
|
pinLoram = ((val & 0x01) != 0) || ((portDir & 0x01) == 0);
|
|
|
|
|
pinHiram = ((val & 0x02) != 0) || ((portDir & 0x02) == 0);
|
|
|
|
|
pinCharen = ((val & 0x04) != 0) || ((portDir & 0x04) == 0);
|
2012-11-27 20:23:27 +00:00
|
|
|
|
|
|
|
|
|
unusedPin0 = ((val & 0x40) != 0);
|
|
|
|
|
unusedPin1 = ((val & 0x80) != 0);
|
|
|
|
|
unusedPinTTL0 = unusedPinTTLCycles;
|
|
|
|
|
unusedPinTTL1 = unusedPinTTLCycles;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-12-01 08:40:08 +00:00
|
|
|
|
private void SetPortDir(byte val)
|
|
|
|
|
{
|
|
|
|
|
portDir = val;
|
2012-12-02 15:10:37 +00:00
|
|
|
|
SetPortData(PortData);
|
2012-12-01 08:40:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-12-03 08:38:12 +00:00
|
|
|
|
public void SyncState(Serializer ser)
|
|
|
|
|
{
|
|
|
|
|
cpu.SyncState(ser);
|
|
|
|
|
ser.Sync("freezeCpu", ref freezeCpu);
|
|
|
|
|
ser.Sync("pinCassetteButton", ref pinCassetteButton);
|
|
|
|
|
ser.Sync("pinCassetteMotor", ref pinCassetteMotor);
|
|
|
|
|
ser.Sync("pinCassetteOutput", ref pinCassetteOutput);
|
|
|
|
|
ser.Sync("pinCharen", ref pinCharen);
|
|
|
|
|
ser.Sync("pinLoram", ref pinLoram);
|
|
|
|
|
ser.Sync("pinHiram", ref pinHiram);
|
|
|
|
|
ser.Sync("pinNMILast", ref pinNMILast);
|
|
|
|
|
ser.Sync("portDir", ref portDir);
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-05 21:07:51 +00:00
|
|
|
|
public void WritePortData(byte data)
|
|
|
|
|
{
|
|
|
|
|
PortData = data;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-27 05:11:40 +00:00
|
|
|
|
// ------------------------------------
|
|
|
|
|
}
|
|
|
|
|
}
|