2016-02-22 23:50:11 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using BizHawk.Emulation.Common;
|
|
|
|
|
using BizHawk.Emulation.Cores.Components.M6502;
|
|
|
|
|
using BizHawk.Emulation.Cores.Computers.Commodore64.Media;
|
|
|
|
|
using BizHawk.Emulation.Cores.Computers.Commodore64.MOS;
|
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
|
|
|
|
|
{
|
|
|
|
|
public sealed partial class Drive1541 : SerialPortDevice
|
|
|
|
|
{
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("Disk")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private Disk _disk;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("BitHistory")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private int _bitHistory;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("BitsRemainingInLatchedByte")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private int _bitsRemainingInLatchedByte;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("Sync")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private bool _sync;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("ByteReady")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private bool _byteReady;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("DriveCpuClockNumerator")]
|
|
|
|
|
private readonly int _driveCpuClockNum;
|
|
|
|
|
[SaveState.SaveWithName("TrackNumber")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private int _trackNumber;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("MotorEnabled")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private bool _motorEnabled;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("LedEnabled")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private bool _ledEnabled;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("MotorStep")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private int _motorStep;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.DoNotSave]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private int _via0PortBtemp;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("CPU")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private readonly MOS6502X _cpu;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("RAM")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private readonly int[] _ram;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("VIA0")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
public readonly Via Via0;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("VIA1")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
public readonly Via Via1;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("SystemCpuClockNumerator")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private readonly int _cpuClockNum;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("SystemDriveCpuRatioDifference")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private int _ratioDifference;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.SaveWithName("DriveLightOffTime")]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
private int _driveLightOffTime;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.DoNotSave]
|
|
|
|
|
private int[] _trackImageData = new int[1];
|
|
|
|
|
[SaveState.DoNotSave]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
public Func<int> ReadIec = () => 0xFF;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.DoNotSave]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
public Action DebuggerStep;
|
2016-03-01 20:01:56 +00:00
|
|
|
|
[SaveState.DoNotSave]
|
2016-02-22 23:50:11 +00:00
|
|
|
|
public readonly Chip23128 DriveRom;
|
|
|
|
|
|
|
|
|
|
public Drive1541(int clockNum, int clockDen)
|
|
|
|
|
{
|
|
|
|
|
DriveRom = new Chip23128();
|
|
|
|
|
_cpu = new MOS6502X
|
|
|
|
|
{
|
|
|
|
|
ReadMemory = CpuRead,
|
|
|
|
|
WriteMemory = CpuWrite,
|
|
|
|
|
DummyReadMemory = CpuRead,
|
|
|
|
|
PeekMemory = CpuPeek,
|
|
|
|
|
NMI = false
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_ram = new int[0x800];
|
2016-03-04 00:03:48 +00:00
|
|
|
|
Via0 = Chip6522.Create(ViaReadClock, ViaReadData, ViaReadAtn, 8);
|
|
|
|
|
Via1 = Chip6522.Create(ReadVia1PrA, ReadVia1PrB);
|
2016-02-22 23:50:11 +00:00
|
|
|
|
|
|
|
|
|
_cpuClockNum = clockNum;
|
|
|
|
|
_driveCpuClockNum = clockDen*1000000; // 1mhz
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private byte CpuPeek(ushort addr)
|
|
|
|
|
{
|
|
|
|
|
return unchecked((byte)Peek(addr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private byte CpuRead(ushort addr)
|
|
|
|
|
{
|
|
|
|
|
return unchecked((byte) Read(addr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CpuWrite(ushort addr, byte val)
|
|
|
|
|
{
|
|
|
|
|
Write(addr, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ViaReadClock()
|
|
|
|
|
{
|
|
|
|
|
var inputClock = ReadMasterClk();
|
|
|
|
|
var outputClock = ReadDeviceClk();
|
|
|
|
|
return !(inputClock && outputClock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ViaReadData()
|
|
|
|
|
{
|
|
|
|
|
var inputData = ReadMasterData();
|
|
|
|
|
var outputData = ReadDeviceData();
|
|
|
|
|
return !(inputData && outputData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ViaReadAtn()
|
|
|
|
|
{
|
|
|
|
|
var inputAtn = ReadMasterAtn();
|
|
|
|
|
return !inputAtn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void ExecutePhase()
|
|
|
|
|
{
|
|
|
|
|
if (_cpuClockNum > _driveCpuClockNum)
|
|
|
|
|
{
|
|
|
|
|
_ratioDifference += _cpuClockNum - _driveCpuClockNum;
|
|
|
|
|
if (_ratioDifference > _cpuClockNum)
|
|
|
|
|
{
|
|
|
|
|
_ratioDifference -= _cpuClockNum;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (_cpuClockNum <= _driveCpuClockNum)
|
|
|
|
|
{
|
|
|
|
|
_ratioDifference += _driveCpuClockNum - _cpuClockNum;
|
|
|
|
|
while (_ratioDifference > _driveCpuClockNum)
|
|
|
|
|
{
|
|
|
|
|
_ratioDifference -= _driveCpuClockNum;
|
|
|
|
|
ExecutePhaseInternal();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ExecutePhaseInternal();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ExecutePhaseInternal()
|
|
|
|
|
{
|
|
|
|
|
Via0.Ca1 = ViaReadAtn();
|
|
|
|
|
|
|
|
|
|
// clock output from 325572-01 drives CPU clock (phi0)
|
|
|
|
|
ExecuteMotor();
|
|
|
|
|
ExecuteFlux();
|
|
|
|
|
Via0.ExecutePhase();
|
|
|
|
|
Via1.ExecutePhase();
|
|
|
|
|
|
|
|
|
|
// SO pin pipeline
|
|
|
|
|
if ((_overflowFlagDelaySr & 0x01) != 0)
|
|
|
|
|
{
|
|
|
|
|
_cpu.SetOverflow();
|
|
|
|
|
}
|
|
|
|
|
_overflowFlagDelaySr >>= 1;
|
|
|
|
|
|
|
|
|
|
_cpu.IRQ = !(Via0.Irq && Via1.Irq); // active low IRQ line
|
|
|
|
|
_cpu.ExecuteOne();
|
|
|
|
|
|
|
|
|
|
_via0PortBtemp = Via0.EffectivePrB;
|
|
|
|
|
_ledEnabled = (_via0PortBtemp & 0x08) != 0;
|
|
|
|
|
|
|
|
|
|
if (_ledEnabled)
|
|
|
|
|
{
|
|
|
|
|
_driveLightOffTime = 1000000;
|
|
|
|
|
}
|
|
|
|
|
else if (_driveLightOffTime > 0)
|
|
|
|
|
{
|
|
|
|
|
_driveLightOffTime--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void HardReset()
|
|
|
|
|
{
|
|
|
|
|
Via0.HardReset();
|
|
|
|
|
Via1.HardReset();
|
|
|
|
|
_trackNumber = 34;
|
|
|
|
|
for (var i = 0; i < _ram.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
_ram[i] = 0x00;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_diskDensity = 0;
|
|
|
|
|
_diskFluxReversalDetected = false;
|
|
|
|
|
_diskByteOffset = 0;
|
|
|
|
|
_diskBitsLeft = 0;
|
|
|
|
|
_diskBits = 0;
|
|
|
|
|
_driveLightOffTime = 0;
|
|
|
|
|
_diskDensityCounter = 0;
|
|
|
|
|
_diskSupplementaryCounter = 0;
|
|
|
|
|
_diskCycle = 0;
|
|
|
|
|
_previousCa1 = false;
|
|
|
|
|
_countsBeforeRandomTransition = 0;
|
|
|
|
|
|
|
|
|
|
SoftReset();
|
|
|
|
|
UpdateMediaData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SoftReset()
|
|
|
|
|
{
|
|
|
|
|
_cpu.NESSoftReset();
|
|
|
|
|
_overflowFlagDelaySr = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void InsertMedia(Disk disk)
|
|
|
|
|
{
|
|
|
|
|
_disk = disk;
|
|
|
|
|
UpdateMediaData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateMediaData()
|
|
|
|
|
{
|
|
|
|
|
if (_disk != null)
|
|
|
|
|
{
|
|
|
|
|
_trackImageData = _disk.GetDataForTrack(_trackNumber);
|
2016-03-01 19:15:27 +00:00
|
|
|
|
_diskBits = _trackImageData[_diskByteOffset] >> (Disk.FluxBitsPerEntry - _diskBitsLeft);
|
2016-02-22 23:50:11 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RemoveMedia()
|
|
|
|
|
{
|
2016-03-04 03:14:19 +00:00
|
|
|
|
_disk = null;
|
2016-02-22 23:50:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Peek(int addr)
|
|
|
|
|
{
|
|
|
|
|
switch (addr & 0xFC00)
|
|
|
|
|
{
|
|
|
|
|
case 0x1800:
|
|
|
|
|
return Via0.Peek(addr);
|
|
|
|
|
case 0x1C00:
|
|
|
|
|
return Via1.Peek(addr);
|
|
|
|
|
}
|
|
|
|
|
if ((addr & 0x8000) != 0)
|
|
|
|
|
return DriveRom.Peek(addr & 0x3FFF);
|
|
|
|
|
if ((addr & 0x1F00) < 0x800)
|
|
|
|
|
return _ram[addr & 0x7FF];
|
|
|
|
|
return (addr >> 8) & 0xFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int PeekVia0(int addr)
|
|
|
|
|
{
|
|
|
|
|
return Via0.Peek(addr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int PeekVia1(int addr)
|
|
|
|
|
{
|
|
|
|
|
return Via1.Peek(addr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Poke(int addr, int val)
|
|
|
|
|
{
|
|
|
|
|
switch (addr & 0xFC00)
|
|
|
|
|
{
|
|
|
|
|
case 0x1800:
|
|
|
|
|
Via0.Poke(addr, val);
|
|
|
|
|
break;
|
|
|
|
|
case 0x1C00:
|
|
|
|
|
Via1.Poke(addr, val);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
if ((addr & 0x8000) == 0 && (addr & 0x1F00) < 0x800)
|
|
|
|
|
_ram[addr & 0x7FF] = val & 0xFF;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void PokeVia0(int addr, int val)
|
|
|
|
|
{
|
|
|
|
|
Via0.Poke(addr, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void PokeVia1(int addr, int val)
|
|
|
|
|
{
|
|
|
|
|
Via1.Poke(addr, val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Read(int addr)
|
|
|
|
|
{
|
|
|
|
|
switch (addr & 0xFC00)
|
|
|
|
|
{
|
|
|
|
|
case 0x1800:
|
|
|
|
|
return Via0.Read(addr);
|
|
|
|
|
case 0x1C00:
|
|
|
|
|
return Via1.Read(addr);
|
|
|
|
|
}
|
|
|
|
|
if ((addr & 0x8000) != 0)
|
|
|
|
|
return DriveRom.Read(addr & 0x3FFF);
|
|
|
|
|
if ((addr & 0x1F00) < 0x800)
|
|
|
|
|
return _ram[addr & 0x7FF];
|
|
|
|
|
return (addr >> 8) & 0xFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Write(int addr, int val)
|
|
|
|
|
{
|
|
|
|
|
switch (addr & 0xFC00)
|
|
|
|
|
{
|
|
|
|
|
case 0x1800:
|
|
|
|
|
Via0.Write(addr, val);
|
|
|
|
|
break;
|
|
|
|
|
case 0x1C00:
|
|
|
|
|
Via1.Write(addr, val);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
if ((addr & 0x8000) == 0 && (addr & 0x1F00) < 0x800)
|
|
|
|
|
_ram[addr & 0x7FF] = val & 0xFF;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool ReadDeviceClk()
|
|
|
|
|
{
|
|
|
|
|
var viaOutputClock = (Via0.DdrB & 0x08) != 0 && (Via0.PrB & 0x08) != 0;
|
|
|
|
|
return !viaOutputClock;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool ReadDeviceData()
|
|
|
|
|
{
|
|
|
|
|
var viaOutputData = (Via0.DdrB & 0x02) != 0 && (Via0.PrB & 0x02) != 0;
|
|
|
|
|
var viaInputAtn = ViaReadAtn();
|
|
|
|
|
var viaOutputAtna = (Via0.DdrB & 0x10) != 0 && (Via0.PrB & 0x10) != 0;
|
|
|
|
|
|
|
|
|
|
return !(viaOutputAtna ^ viaInputAtn) && !viaOutputData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool ReadDeviceLight()
|
|
|
|
|
{
|
|
|
|
|
return _driveLightOffTime > 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|