C64: Allow writing to disk.

This commit is contained in:
SaxxonPike 2019-07-04 00:11:03 -05:00
parent 0cdb28fc8f
commit 4e1892d094
5 changed files with 304 additions and 204 deletions

View File

@ -129,15 +129,15 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_acrSrControl = 0;
_acrT1Control = 0;
_acrT2Control = 0;
_ca1L = false;
_cb1L = false;
Ca1 = false;
Ca2 = false;
Cb1 = false;
Cb2 = false;
_ca1L = true;
_cb1L = true;
Ca1 = true;
Ca2 = true;
Cb1 = true;
Cb2 = true;
_pb6L = false;
_pb6 = false;
_pb6L = true;
_pb6 = true;
_resetCa2NextClock = false;
_resetCb2NextClock = false;
_handshakeCa2NextClock = false;
@ -222,9 +222,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
switch (_acrT1Control)
{
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS:
_t1C = _t1L;
_t1CLoaded = true;
break;
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7:
_t1C = _t1L;
_t1CLoaded = true;
@ -261,11 +258,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
if (!_pb6 && _pb6L)
{
_t2C--;
if (_t2C < 0)
if (_t2C == 0)
{
_ifr |= 0x20;
_t2C = 0xFFFF;
}
_t2C &= 0xFFFF;
}
break;
}
@ -332,24 +329,43 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
}
// interrupt generation
if ((_pcrCb1IntControl == PCR_INT_CONTROL_POSITIVE_EDGE && Cb1 && !_cb1L) ||
(_pcrCb1IntControl == PCR_INT_CONTROL_NEGATIVE_EDGE && !Cb1 && _cb1L))
{
_ifr |= 0x10;
if (_acrPbLatchEnable)
{
_pbLatch = _port.ReadExternalPrb();
}
}
/*
As long as the CA1 interrupt flag is set, the data on the peripheral pins can change
without affecting the data in the latches. This input latching can be used with any of the CA2
input or output modes.
It is important to note that on the PA port, the processor always reads the data on the
peripheral pins (as reflected in the latches). For output pins, the processor still reads the
latches. This may or may not reflect the data currently in the ORA. Proper system operation
requires careful planning on the part of the system designer if input latching is combined
with output pins on the peripheral ports.
*/
if ((_pcrCa1IntControl == PCR_INT_CONTROL_POSITIVE_EDGE && Ca1 && !_ca1L) ||
(_pcrCa1IntControl == PCR_INT_CONTROL_NEGATIVE_EDGE && !Ca1 && _ca1L))
{
_ifr |= 0x02;
if (_acrPaLatchEnable)
if (_acrPaLatchEnable && (_ifr & 0x02) == 0)
{
_paLatch = _port.ReadExternalPra();
}
_ifr |= 0x02;
}
/*
Input latching on the PB port is controlled in the same manner as that described for the PA port.
However, with the peripheral B port the input latch will store either the voltage on the pin or the contents
of the Output Register (ORB) depending on whether the pin is programmed to act as an input or an
output. As with the PA port, the processor always reads the input latches.
*/
if ((_pcrCb1IntControl == PCR_INT_CONTROL_POSITIVE_EDGE && Cb1 && !_cb1L) ||
(_pcrCb1IntControl == PCR_INT_CONTROL_NEGATIVE_EDGE && !Cb1 && _cb1L))
{
if (_acrPbLatchEnable && (_ifr & 0x10) == 0)
{
_pbLatch = _port.ReadPrb(_prb, _ddrb);
}
_ifr |= 0x10;
}
switch (_acrSrControl)

View File

@ -9,6 +9,22 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
const int D64_DISK_ID_OFFSET = 0x165A2; // track 18, sector 0, 0xA2
private enum ErrorType
{
NoError = 0x01,
HeaderNotFound = 0x02,
NoSyncSequence = 0x03,
DataNotFound = 0x04,
DataChecksumError = 0x05,
WriteVerifyFormatError = 0x06,
WriteVerifyError = 0x07,
WriteProtectOn = 0x08,
HeaderChecksumError = 0x09,
WriteError = 0x0A,
IdMismatch = 0x0B,
DriveNotReady = 0x0F
}
private static readonly int[] DensityTable =
{
3, 3, 3, 3, 3,
@ -84,25 +100,34 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
return result;
}
private static byte[] ConvertSectorToGcr(byte[] source, byte sectorNo, byte trackNo, byte formatA, byte formatB, int gapLength, out int bitsWritten)
private static byte[] ConvertSectorToGcr(byte[] source, byte sectorNo, byte trackNo, byte formatA, byte formatB, int gapLength, ErrorType errorType, out int bitsWritten)
{
using (var mem = new MemoryStream())
{
var writer = new BinaryWriter(mem);
var headerChecksum = (byte)(sectorNo ^ trackNo ^ formatA ^ formatB);
if (errorType == ErrorType.IdMismatch)
{
formatA ^= 0xFF;
formatB ^= 0xFF;
}
var headerChecksum = (byte)(sectorNo ^ trackNo ^ formatA ^ formatB ^ (errorType == ErrorType.HeaderChecksumError ? 0xFF : 0x00));
// assemble written data for GCR encoding
var writtenData = new byte[260];
var syncBytes40 = Enumerable.Repeat((byte) (errorType == ErrorType.NoSyncSequence ? 0x00 : 0xFF), 5).ToArray();
Array.Copy(source, 0, writtenData, 1, 256);
writtenData[0] = 0x07;
writtenData[0x101] = Checksum(source);
writtenData[0] = (byte)(errorType == ErrorType.HeaderNotFound ? 0x00 : 0x07);
writtenData[0x101] = (byte)(Checksum(source) ^ (errorType == ErrorType.DataChecksumError ? 0xFF : 0x00));
writtenData[0x102] = 0x00;
writtenData[0x103] = 0x00;
writer.Write(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }); // sync
writer.Write(EncodeGcr(new byte[] { 0x08, headerChecksum, sectorNo, trackNo, formatA, formatB, 0x0F, 0x0F })); // header
writer.Write(syncBytes40); // sync
writer.Write(EncodeGcr(new byte[] { (byte)(errorType == ErrorType.DataNotFound ? 0x00 : 0x08), headerChecksum, sectorNo, trackNo, formatA, formatB, 0x0F, 0x0F })); // header
writer.Write(new byte[] { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }); // gap
writer.Write(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }); // sync
writer.Write(syncBytes40); // sync
writer.Write(EncodeGcr(writtenData)); // data
writer.Write(Enumerable.Repeat((byte)0x55, gapLength).ToArray()); // gap
@ -166,7 +191,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
var trackLengths = new List<int>();
var trackNumbers = new List<int>();
var trackDensities = new List<int>();
var errorType = ErrorType.NoError;
int trackCount;
int errorOffset = -1;
switch (source.Length)
{
@ -175,12 +202,14 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
break;
case 175531: // 35 tracks with errors
trackCount = 35;
errorOffset = 174848;
break;
case 196608: // 40 tracks no errors
trackCount = 40;
break;
case 197376: // 40 tracks with errors
trackCount = 40;
errorOffset = 196608;
break;
default:
throw new Exception("Not able to identify capacity of the D64 file.");
@ -188,6 +217,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
for (var i = 0; i < trackCount; i++)
{
if (errorOffset >= 0)
{
errorType = (ErrorType) source[errorOffset];
errorOffset++;
}
var sectors = SectorsPerTrack[i];
var trackLengthBits = 0;
using (var trackMem = new MemoryStream())
@ -196,7 +230,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
int bitsWritten;
var sectorData = reader.ReadBytes(256);
var diskData = ConvertSectorToGcr(sectorData, (byte)j, (byte)(i + 1), formatA, formatB, StandardSectorGapLength[DensityTable[i]], out bitsWritten);
var diskData = ConvertSectorToGcr(sectorData, (byte)j, (byte)(i + 1), formatA, formatB, StandardSectorGapLength[DensityTable[i]], errorType, out bitsWritten);
trackMem.Write(diskData, 0, diskData.Length);
trackLengthBits += bitsWritten;
}

View File

@ -19,6 +19,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
private int _rngCurrent;
private int _clocks;
private int _cpuClocks;
private int _diskWriteBitsRemaining;
private bool _diskWriteEnabled;
private int _diskWriteLatch;
private int _diskOutputBits;
private bool _diskWriteProtected;
// Lehmer RNG
private void AdvanceRng()
@ -50,21 +55,35 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
if (_diskBitsLeft <= 0)
{
_diskByteOffset++;
if (_diskByteOffset == Disk.FluxEntriesPerTrack)
{
_diskByteOffset = 0;
}
if (_diskWriteEnabled)
_trackImageData[_diskByteOffset] = _diskOutputBits;
_diskByteOffset++;
if (_diskByteOffset == Disk.FluxEntriesPerTrack)
_diskByteOffset = 0;
if (!_diskWriteEnabled)
_diskBits = _trackImageData[_diskByteOffset];
_diskOutputBits = 0;
_diskBitsLeft = Disk.FluxBitsPerEntry;
}
}
_diskOutputBits >>= 1;
if (_diskWriteEnabled)
_countsBeforeRandomTransition = 0;
if ((_diskBits & 1) != 0)
{
_countsBeforeRandomTransition = 0;
_diskFluxReversalDetected = true;
_diskOutputBits |= int.MinValue; // set bit 31
}
else
{
_diskOutputBits &= int.MaxValue; // clear bit 31
}
_diskBits >>= 1;
@ -79,7 +98,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
_diskFluxReversalDetected = true;
AdvanceRng();
// This constant is what VICE uses. TODO: Determine accuracy.
_countsBeforeRandomTransition = (_rngCurrent % 367) + 33;
}
@ -87,14 +105,16 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
// flux transition circuitry
if (_diskFluxReversalDetected)
{
if (!_diskWriteEnabled)
{
_diskDensityCounter = _diskDensity;
_diskSupplementaryCounter = 0;
}
_diskFluxReversalDetected = false;
if (_countsBeforeRandomTransition == 0)
{
AdvanceRng();
// This constant is what VICE uses. TODO: Determine accuracy.
_countsBeforeRandomTransition = (_rngCurrent & 0x1F) + 289;
}
@ -105,13 +125,37 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
_diskDensityCounter = _diskDensity;
_diskSupplementaryCounter++;
if ((_diskSupplementaryCounter & 0x3) == 0x2)
{
if (!_diskWriteEnabled)
_diskWriteBitsRemaining = 0;
_diskWriteEnabled = !Via1.Cb2;
_diskWriteBitsRemaining--;
if (_diskWriteEnabled)
{
_countsBeforeRandomTransition = 0;
_byteReady = false;
if (_diskWriteBitsRemaining <= 0)
{
_diskWriteLatch = Via1.EffectivePrA;
_diskWriteBitsRemaining = 8;
_byteReady = Via1.Ca2;
}
if ((_diskWriteLatch & 0x80) != 0)
{
_diskOutputBits |= int.MinValue; // set bit 31
}
_diskWriteLatch <<= 1;
}
else
{
_bitsRemainingInLatchedByte--;
_byteReady = false;
_bitHistory = (_bitHistory << 1) | ((_diskSupplementaryCounter & 0xC) == 0x0 ? 1 : 0);
_sync = false;
if (Via1.Cb2 && (_bitHistory & 0x3FF) == 0x3FF)
if (!_diskWriteEnabled && (_bitHistory & 0x3FF) == 0x3FF)
{
_sync = true;
_bitsRemainingInLatchedByte = 8;
@ -122,9 +166,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
_bitsRemainingInLatchedByte = 8;
// SOE (sync output enabled)
// SOE (SO/Byte Ready enabled)
_byteReady = Via1.Ca2;
}
}
}
// negative transition activates SO pin on CPU
_previousCa1 = Via1.Ca1;
@ -135,7 +181,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
_overflowFlagDelaySr |= _diskCycle > 6 ? 4 : 2;
}
}
}
if (_diskSupplementaryCounter >= 16)
{

View File

@ -46,7 +46,7 @@
private int ReadVia1PrB()
{
return (_motorStep & 0x03) | (_motorEnabled ? 0x04 : 0x00) | (_sync ? 0x00 : 0x80);
return (_motorStep & 0x03) | (_motorEnabled ? 0x04 : 0x00) | (_sync ? 0x00 : 0x80) | (_diskWriteProtected ? 0x00 : 0x10);
}
public int Peek(int addr)

View File

@ -117,6 +117,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
ser.Sync("Clocks", ref _clocks);
ser.Sync("CpuClocks", ref _cpuClocks);
ser.Sync("OverflowFlagDelayShiftRegister", ref _overflowFlagDelaySr);
ser.Sync("DiskWriteBitsRemaining", ref _diskWriteBitsRemaining);
ser.Sync("DiskWriteEnabled", ref _diskWriteEnabled);
ser.Sync("DiskWriteLatch", ref _diskWriteLatch);
ser.Sync("DiskOutputBits", ref _diskOutputBits);
ser.Sync("DiskWriteProtected", ref _diskWriteProtected);
}
public override void ExecutePhase()