C64: Allow writing to disk.
This commit is contained in:
parent
0cdb28fc8f
commit
4e1892d094
|
@ -129,15 +129,15 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
||||||
_acrSrControl = 0;
|
_acrSrControl = 0;
|
||||||
_acrT1Control = 0;
|
_acrT1Control = 0;
|
||||||
_acrT2Control = 0;
|
_acrT2Control = 0;
|
||||||
_ca1L = false;
|
_ca1L = true;
|
||||||
_cb1L = false;
|
_cb1L = true;
|
||||||
Ca1 = false;
|
Ca1 = true;
|
||||||
Ca2 = false;
|
Ca2 = true;
|
||||||
Cb1 = false;
|
Cb1 = true;
|
||||||
Cb2 = false;
|
Cb2 = true;
|
||||||
|
|
||||||
_pb6L = false;
|
_pb6L = true;
|
||||||
_pb6 = false;
|
_pb6 = true;
|
||||||
_resetCa2NextClock = false;
|
_resetCa2NextClock = false;
|
||||||
_resetCb2NextClock = false;
|
_resetCb2NextClock = false;
|
||||||
_handshakeCa2NextClock = false;
|
_handshakeCa2NextClock = false;
|
||||||
|
@ -222,9 +222,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
||||||
switch (_acrT1Control)
|
switch (_acrT1Control)
|
||||||
{
|
{
|
||||||
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS:
|
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS:
|
||||||
_t1C = _t1L;
|
|
||||||
_t1CLoaded = true;
|
|
||||||
break;
|
|
||||||
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7:
|
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7:
|
||||||
_t1C = _t1L;
|
_t1C = _t1L;
|
||||||
_t1CLoaded = true;
|
_t1CLoaded = true;
|
||||||
|
@ -261,11 +258,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
||||||
if (!_pb6 && _pb6L)
|
if (!_pb6 && _pb6L)
|
||||||
{
|
{
|
||||||
_t2C--;
|
_t2C--;
|
||||||
if (_t2C < 0)
|
if (_t2C == 0)
|
||||||
{
|
{
|
||||||
_ifr |= 0x20;
|
_ifr |= 0x20;
|
||||||
_t2C = 0xFFFF;
|
|
||||||
}
|
}
|
||||||
|
_t2C &= 0xFFFF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -331,26 +328,45 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// interrupt generation
|
// 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((_pcrCa1IntControl == PCR_INT_CONTROL_POSITIVE_EDGE && Ca1 && !_ca1L) ||
|
/*
|
||||||
(_pcrCa1IntControl == PCR_INT_CONTROL_NEGATIVE_EDGE && !Ca1 && _ca1L))
|
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
|
||||||
_ifr |= 0x02;
|
input or output modes.
|
||||||
if (_acrPaLatchEnable)
|
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
|
||||||
_paLatch = _port.ReadExternalPra();
|
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))
|
||||||
|
{
|
||||||
|
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)
|
switch (_acrSrControl)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,22 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
|
||||||
{
|
{
|
||||||
const int D64_DISK_ID_OFFSET = 0x165A2; // track 18, sector 0, 0xA2
|
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 =
|
private static readonly int[] DensityTable =
|
||||||
{
|
{
|
||||||
3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3,
|
||||||
|
@ -84,25 +100,34 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
|
||||||
return result;
|
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())
|
using (var mem = new MemoryStream())
|
||||||
{
|
{
|
||||||
var writer = new BinaryWriter(mem);
|
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
|
// assemble written data for GCR encoding
|
||||||
var writtenData = new byte[260];
|
var writtenData = new byte[260];
|
||||||
|
var syncBytes40 = Enumerable.Repeat((byte) (errorType == ErrorType.NoSyncSequence ? 0x00 : 0xFF), 5).ToArray();
|
||||||
|
|
||||||
Array.Copy(source, 0, writtenData, 1, 256);
|
Array.Copy(source, 0, writtenData, 1, 256);
|
||||||
writtenData[0] = 0x07;
|
writtenData[0] = (byte)(errorType == ErrorType.HeaderNotFound ? 0x00 : 0x07);
|
||||||
writtenData[0x101] = Checksum(source);
|
writtenData[0x101] = (byte)(Checksum(source) ^ (errorType == ErrorType.DataChecksumError ? 0xFF : 0x00));
|
||||||
writtenData[0x102] = 0x00;
|
writtenData[0x102] = 0x00;
|
||||||
writtenData[0x103] = 0x00;
|
writtenData[0x103] = 0x00;
|
||||||
|
|
||||||
writer.Write(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }); // sync
|
writer.Write(syncBytes40); // sync
|
||||||
writer.Write(EncodeGcr(new byte[] { 0x08, headerChecksum, sectorNo, trackNo, formatA, formatB, 0x0F, 0x0F })); // header
|
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[] { 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(EncodeGcr(writtenData)); // data
|
||||||
writer.Write(Enumerable.Repeat((byte)0x55, gapLength).ToArray()); // gap
|
writer.Write(Enumerable.Repeat((byte)0x55, gapLength).ToArray()); // gap
|
||||||
|
|
||||||
|
@ -156,67 +181,76 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
|
||||||
|
|
||||||
public static Disk Read(byte[] source)
|
public static Disk Read(byte[] source)
|
||||||
{
|
{
|
||||||
var formatB = source[D64_DISK_ID_OFFSET + 0x00];
|
var formatB = source[D64_DISK_ID_OFFSET + 0x00];
|
||||||
var formatA = source[D64_DISK_ID_OFFSET + 0x01];
|
var formatA = source[D64_DISK_ID_OFFSET + 0x01];
|
||||||
|
|
||||||
using (var mem = new MemoryStream(source))
|
using (var mem = new MemoryStream(source))
|
||||||
{
|
{
|
||||||
var reader = new BinaryReader(mem);
|
var reader = new BinaryReader(mem);
|
||||||
var trackDatas = new List<byte[]>();
|
var trackDatas = new List<byte[]>();
|
||||||
var trackLengths = new List<int>();
|
var trackLengths = new List<int>();
|
||||||
var trackNumbers = new List<int>();
|
var trackNumbers = new List<int>();
|
||||||
var trackDensities = new List<int>();
|
var trackDensities = new List<int>();
|
||||||
int trackCount;
|
var errorType = ErrorType.NoError;
|
||||||
|
int trackCount;
|
||||||
|
int errorOffset = -1;
|
||||||
|
|
||||||
switch (source.Length)
|
switch (source.Length)
|
||||||
{
|
{
|
||||||
case 174848: // 35 tracks no errors
|
case 174848: // 35 tracks no errors
|
||||||
trackCount = 35;
|
trackCount = 35;
|
||||||
break;
|
break;
|
||||||
case 175531: // 35 tracks with errors
|
case 175531: // 35 tracks with errors
|
||||||
trackCount = 35;
|
trackCount = 35;
|
||||||
break;
|
errorOffset = 174848;
|
||||||
case 196608: // 40 tracks no errors
|
break;
|
||||||
trackCount = 40;
|
case 196608: // 40 tracks no errors
|
||||||
break;
|
trackCount = 40;
|
||||||
case 197376: // 40 tracks with errors
|
break;
|
||||||
trackCount = 40;
|
case 197376: // 40 tracks with errors
|
||||||
break;
|
trackCount = 40;
|
||||||
default:
|
errorOffset = 196608;
|
||||||
throw new Exception("Not able to identify capacity of the D64 file.");
|
break;
|
||||||
}
|
default:
|
||||||
|
throw new Exception("Not able to identify capacity of the D64 file.");
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < trackCount; i++)
|
for (var i = 0; i < trackCount; i++)
|
||||||
{
|
{
|
||||||
var sectors = SectorsPerTrack[i];
|
if (errorOffset >= 0)
|
||||||
var trackLengthBits = 0;
|
{
|
||||||
using (var trackMem = new MemoryStream())
|
errorType = (ErrorType) source[errorOffset];
|
||||||
{
|
errorOffset++;
|
||||||
for (var j = 0; j < sectors; j++)
|
}
|
||||||
{
|
var sectors = SectorsPerTrack[i];
|
||||||
int bitsWritten;
|
var trackLengthBits = 0;
|
||||||
var sectorData = reader.ReadBytes(256);
|
using (var trackMem = new MemoryStream())
|
||||||
var diskData = ConvertSectorToGcr(sectorData, (byte)j, (byte)(i + 1), formatA, formatB, StandardSectorGapLength[DensityTable[i]], out bitsWritten);
|
{
|
||||||
trackMem.Write(diskData, 0, diskData.Length);
|
for (var j = 0; j < sectors; j++)
|
||||||
trackLengthBits += bitsWritten;
|
{
|
||||||
}
|
int bitsWritten;
|
||||||
var density = DensityTable[i];
|
var sectorData = reader.ReadBytes(256);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
var density = DensityTable[i];
|
||||||
|
|
||||||
// we pad the tracks with extra gap bytes to meet MNIB standards
|
// we pad the tracks with extra gap bytes to meet MNIB standards
|
||||||
while (trackMem.Length < StandardTrackLengthBytes[density])
|
while (trackMem.Length < StandardTrackLengthBytes[density])
|
||||||
{
|
{
|
||||||
trackMem.WriteByte(0x55);
|
trackMem.WriteByte(0x55);
|
||||||
}
|
}
|
||||||
|
|
||||||
trackDatas.Add(trackMem.ToArray());
|
trackDatas.Add(trackMem.ToArray());
|
||||||
trackLengths.Add(trackLengthBits);
|
trackLengths.Add(trackLengthBits);
|
||||||
trackNumbers.Add(i * 2);
|
trackNumbers.Add(i * 2);
|
||||||
trackDensities.Add(DensityTable[i]);
|
trackDensities.Add(DensityTable[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Disk(trackDatas, trackNumbers, trackDensities, 84);
|
return new Disk(trackDatas, trackNumbers, trackDensities, 84);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
|
||||||
private int _rngCurrent;
|
private int _rngCurrent;
|
||||||
private int _clocks;
|
private int _clocks;
|
||||||
private int _cpuClocks;
|
private int _cpuClocks;
|
||||||
|
private int _diskWriteBitsRemaining;
|
||||||
|
private bool _diskWriteEnabled;
|
||||||
|
private int _diskWriteLatch;
|
||||||
|
private int _diskOutputBits;
|
||||||
|
private bool _diskWriteProtected;
|
||||||
|
|
||||||
// Lehmer RNG
|
// Lehmer RNG
|
||||||
private void AdvanceRng()
|
private void AdvanceRng()
|
||||||
|
@ -31,127 +36,167 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
|
||||||
_rngCurrent = (int)(_rngCurrent * LEHMER_RNG_PRIME % int.MaxValue);
|
_rngCurrent = (int)(_rngCurrent * LEHMER_RNG_PRIME % int.MaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteFlux()
|
private void ExecuteFlux()
|
||||||
{
|
{
|
||||||
// This actually executes the main 16mhz clock
|
// This actually executes the main 16mhz clock
|
||||||
while (_clocks > 0)
|
while (_clocks > 0)
|
||||||
{
|
{
|
||||||
_clocks--;
|
_clocks--;
|
||||||
|
|
||||||
// rotate disk
|
// rotate disk
|
||||||
if (_motorEnabled)
|
if (_motorEnabled)
|
||||||
{
|
{
|
||||||
if (_disk == null)
|
if (_disk == null)
|
||||||
{
|
{
|
||||||
_diskBitsLeft = 1;
|
_diskBitsLeft = 1;
|
||||||
_diskBits = 0;
|
_diskBits = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_diskBitsLeft <= 0)
|
if (_diskBitsLeft <= 0)
|
||||||
{
|
{
|
||||||
_diskByteOffset++;
|
if (_diskWriteEnabled)
|
||||||
if (_diskByteOffset == Disk.FluxEntriesPerTrack)
|
_trackImageData[_diskByteOffset] = _diskOutputBits;
|
||||||
{
|
|
||||||
_diskByteOffset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_diskBits = _trackImageData[_diskByteOffset];
|
_diskByteOffset++;
|
||||||
_diskBitsLeft = Disk.FluxBitsPerEntry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((_diskBits & 1) != 0)
|
if (_diskByteOffset == Disk.FluxEntriesPerTrack)
|
||||||
{
|
_diskByteOffset = 0;
|
||||||
_countsBeforeRandomTransition = 0;
|
|
||||||
_diskFluxReversalDetected = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_diskBits >>= 1;
|
if (!_diskWriteEnabled)
|
||||||
_diskBitsLeft--;
|
_diskBits = _trackImageData[_diskByteOffset];
|
||||||
}
|
|
||||||
|
|
||||||
// random flux transition readings for unformatted data
|
_diskOutputBits = 0;
|
||||||
if (_countsBeforeRandomTransition > 0)
|
_diskBitsLeft = Disk.FluxBitsPerEntry;
|
||||||
{
|
}
|
||||||
_countsBeforeRandomTransition--;
|
}
|
||||||
if (_countsBeforeRandomTransition == 0)
|
_diskOutputBits >>= 1;
|
||||||
{
|
|
||||||
_diskFluxReversalDetected = true;
|
|
||||||
AdvanceRng();
|
|
||||||
|
|
||||||
// This constant is what VICE uses. TODO: Determine accuracy.
|
if (_diskWriteEnabled)
|
||||||
_countsBeforeRandomTransition = (_rngCurrent % 367) + 33;
|
_countsBeforeRandomTransition = 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// flux transition circuitry
|
if ((_diskBits & 1) != 0)
|
||||||
if (_diskFluxReversalDetected)
|
{
|
||||||
{
|
_countsBeforeRandomTransition = 0;
|
||||||
_diskDensityCounter = _diskDensity;
|
_diskFluxReversalDetected = true;
|
||||||
_diskSupplementaryCounter = 0;
|
_diskOutputBits |= int.MinValue; // set bit 31
|
||||||
_diskFluxReversalDetected = false;
|
}
|
||||||
if (_countsBeforeRandomTransition == 0)
|
else
|
||||||
{
|
{
|
||||||
AdvanceRng();
|
_diskOutputBits &= int.MaxValue; // clear bit 31
|
||||||
|
}
|
||||||
|
|
||||||
// This constant is what VICE uses. TODO: Determine accuracy.
|
_diskBits >>= 1;
|
||||||
_countsBeforeRandomTransition = (_rngCurrent & 0x1F) + 289;
|
_diskBitsLeft--;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// counter circuitry
|
// random flux transition readings for unformatted data
|
||||||
if (_diskDensityCounter >= 16)
|
if (_countsBeforeRandomTransition > 0)
|
||||||
{
|
{
|
||||||
_diskDensityCounter = _diskDensity;
|
_countsBeforeRandomTransition--;
|
||||||
_diskSupplementaryCounter++;
|
if (_countsBeforeRandomTransition == 0)
|
||||||
if ((_diskSupplementaryCounter & 0x3) == 0x2)
|
{
|
||||||
{
|
_diskFluxReversalDetected = true;
|
||||||
_bitsRemainingInLatchedByte--;
|
AdvanceRng();
|
||||||
_byteReady = false;
|
// This constant is what VICE uses. TODO: Determine accuracy.
|
||||||
_bitHistory = (_bitHistory << 1) | ((_diskSupplementaryCounter & 0xC) == 0x0 ? 1 : 0);
|
_countsBeforeRandomTransition = (_rngCurrent % 367) + 33;
|
||||||
_sync = false;
|
}
|
||||||
if (Via1.Cb2 && (_bitHistory & 0x3FF) == 0x3FF)
|
}
|
||||||
{
|
|
||||||
_sync = true;
|
|
||||||
_bitsRemainingInLatchedByte = 8;
|
|
||||||
_byteReady = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_bitsRemainingInLatchedByte <= 0)
|
// flux transition circuitry
|
||||||
{
|
if (_diskFluxReversalDetected)
|
||||||
_bitsRemainingInLatchedByte = 8;
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SOE (sync output enabled)
|
// counter circuitry
|
||||||
_byteReady = Via1.Ca2;
|
if (_diskDensityCounter >= 16)
|
||||||
}
|
{
|
||||||
|
_diskDensityCounter = _diskDensity;
|
||||||
|
_diskSupplementaryCounter++;
|
||||||
|
|
||||||
// negative transition activates SO pin on CPU
|
if ((_diskSupplementaryCounter & 0x3) == 0x2)
|
||||||
_previousCa1 = Via1.Ca1;
|
{
|
||||||
Via1.Ca1 = !_byteReady;
|
if (!_diskWriteEnabled)
|
||||||
if (_previousCa1 && !Via1.Ca1)
|
_diskWriteBitsRemaining = 0;
|
||||||
{
|
_diskWriteEnabled = !Via1.Cb2;
|
||||||
// cycle 6 is roughly 400ns
|
|
||||||
_overflowFlagDelaySr |= _diskCycle > 6 ? 4 : 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_diskSupplementaryCounter >= 16)
|
_diskWriteBitsRemaining--;
|
||||||
{
|
if (_diskWriteEnabled)
|
||||||
_diskSupplementaryCounter = 0;
|
{
|
||||||
}
|
_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 (!_diskWriteEnabled && (_bitHistory & 0x3FF) == 0x3FF)
|
||||||
|
{
|
||||||
|
_sync = true;
|
||||||
|
_bitsRemainingInLatchedByte = 8;
|
||||||
|
_byteReady = false;
|
||||||
|
}
|
||||||
|
|
||||||
_cpuClocks--;
|
if (_bitsRemainingInLatchedByte <= 0)
|
||||||
if (_cpuClocks <= 0)
|
{
|
||||||
{
|
_bitsRemainingInLatchedByte = 8;
|
||||||
ExecuteSystem();
|
|
||||||
_cpuClocks = 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
_diskDensityCounter++;
|
// SOE (SO/Byte Ready enabled)
|
||||||
_diskCycle = (_diskCycle + 1) & 0xF;
|
_byteReady = Via1.Ca2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// negative transition activates SO pin on CPU
|
||||||
|
_previousCa1 = Via1.Ca1;
|
||||||
|
Via1.Ca1 = !_byteReady;
|
||||||
|
if (_previousCa1 && !Via1.Ca1)
|
||||||
|
{
|
||||||
|
// cycle 6 is roughly 400ns
|
||||||
|
_overflowFlagDelaySr |= _diskCycle > 6 ? 4 : 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_diskSupplementaryCounter >= 16)
|
||||||
|
{
|
||||||
|
_diskSupplementaryCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cpuClocks--;
|
||||||
|
if (_cpuClocks <= 0)
|
||||||
|
{
|
||||||
|
ExecuteSystem();
|
||||||
|
_cpuClocks = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
_diskDensityCounter++;
|
||||||
|
_diskCycle = (_diskCycle + 1) & 0xF;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
|
|
||||||
private int ReadVia1PrB()
|
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)
|
public int Peek(int addr)
|
||||||
|
|
|
@ -117,6 +117,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
|
||||||
ser.Sync("Clocks", ref _clocks);
|
ser.Sync("Clocks", ref _clocks);
|
||||||
ser.Sync("CpuClocks", ref _cpuClocks);
|
ser.Sync("CpuClocks", ref _cpuClocks);
|
||||||
ser.Sync("OverflowFlagDelayShiftRegister", ref _overflowFlagDelaySr);
|
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()
|
public override void ExecutePhase()
|
||||||
|
|
Loading…
Reference in New Issue