diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Abstraction/IBeeperDevice.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Abstraction/IBeeperDevice.cs deleted file mode 100644 index de19bfe9a2..0000000000 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Abstraction/IBeeperDevice.cs +++ /dev/null @@ -1,25 +0,0 @@ -using BizHawk.Common; - -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC -{ - /// - /// Represents a beeper/buzzer device - /// - public interface IBeeperDevice - { - /// - /// Initialization - /// - void Init(int sampleRate, int tStatesPerFrame); - - /// - /// Processes an incoming pulse value and adds it to the blipbuffer - /// - void ProcessPulseValue(bool pulse); - - /// - /// State serialization - /// - void SyncState(Serializer ser); - } -} diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Datacorder/DatacorderDevice.cs index 450fa39c46..1fde14b52c 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Datacorder/DatacorderDevice.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { /// diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/CHRN.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/CHRN.cs deleted file mode 100644 index 76d7ae23be..0000000000 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/CHRN.cs +++ /dev/null @@ -1,180 +0,0 @@ - -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC -{ - /// - /// Used for the sector CHRN structure - /// - public class CHRN - { - /// - /// Track - /// - public byte C { get; set; } - - /// - /// Side - /// - public byte H { get; set; } - - /// - /// Sector ID - /// - public byte R { get; set; } - - /// - /// Sector Size - /// - public byte N { get; set; } - - /// - /// Status register 1 - /// - private byte _flag1; - public byte Flag1 - { - get => _flag1; - set => _flag1 = value; - } - - /// - /// Status register 2 - /// - private byte _flag2; - public byte Flag2 - { - get => _flag2; - set => _flag2 = value; - } - - /// - /// Used to store the last transmitted/received data bytes - /// - public byte[] DataBytes { get; set; } - - /// - /// ID for the read/write data command - /// - public int DataID { get; set; } - - #region Helper Methods - - /// - /// Missing Address Mark (Sector_ID or DAM not found) - /// - public bool ST1MA - { - get => NECUPD765.GetBit(0, _flag1); - set - { - if (value) { NECUPD765.SetBit(0, ref _flag1); } - else { NECUPD765.UnSetBit(0, ref _flag1); } - } - } - - /// - /// No Data (Sector_ID not found, CRC fail in ID_field) - /// - public bool ST1ND - { - get => NECUPD765.GetBit(2, _flag1); - set - { - if (value) { NECUPD765.SetBit(2, ref _flag1); } - else { NECUPD765.UnSetBit(2, ref _flag1); } - } - } - - /// - /// Data Error (CRC-fail in ID- or Data-Field) - /// - public bool ST1DE - { - get => NECUPD765.GetBit(5, _flag1); - set - { - if (value) { NECUPD765.SetBit(5, ref _flag1); } - else { NECUPD765.UnSetBit(5, ref _flag1); } - } - } - - /// - /// End of Track (set past most read/write commands) (see IC) - /// - public bool ST1EN - { - get => NECUPD765.GetBit(7, _flag1); - set - { - if (value) { NECUPD765.SetBit(7, ref _flag1); } - else { NECUPD765.UnSetBit(7, ref _flag1); } - } - } - - /// - /// Missing Address Mark in Data Field (DAM not found) - /// - public bool ST2MD - { - get => NECUPD765.GetBit(0, _flag2); - set - { - if (value) { NECUPD765.SetBit(0, ref _flag2); } - else { NECUPD765.UnSetBit(0, ref _flag2); } - } - } - - /// - /// Bad Cylinder (read/programmed track-ID different and read-ID = FF) - /// - public bool ST2BC - { - get => NECUPD765.GetBit(1, _flag2); - set - { - if (value) { NECUPD765.SetBit(1, ref _flag2); } - else { NECUPD765.UnSetBit(1, ref _flag2); } - } - } - - /// - /// Wrong Cylinder (read/programmed track-ID different) (see b1) - /// - public bool ST2WC - { - get => NECUPD765.GetBit(4, _flag2); - set - { - if (value) { NECUPD765.SetBit(4, ref _flag2); } - else { NECUPD765.UnSetBit(4, ref _flag2); } - } - } - - /// - /// Data Error in Data Field (CRC-fail in data-field) - /// - public bool ST2DD - { - get => NECUPD765.GetBit(5, _flag2); - set - { - if (value) { NECUPD765.SetBit(5, ref _flag2); } - else { NECUPD765.UnSetBit(5, ref _flag2); } - } - } - - /// - /// Control Mark (read/scan command found sector with deleted DAM) - /// - public bool ST2CM - { - get => NECUPD765.GetBit(6, _flag2); - set - { - if (value) { NECUPD765.SetBit(6, ref _flag2); } - else { NECUPD765.UnSetBit(6, ref _flag2); } - } - } - - #endregion - } -} diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.FDD.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.FDD.cs deleted file mode 100644 index a1ec95fdba..0000000000 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.FDD.cs +++ /dev/null @@ -1,875 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; -using System.Linq; - -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC -{ - /// - /// Floppy drive related stuff - /// - #region Attribution - /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ - #endregion - public partial class NECUPD765 : IFDDHost - { - #region Drive State - - /// - /// FDD Flag - motor on/off - /// - public bool FDD_FLAG_MOTOR; - - /// - /// The index of the currently active disk drive - /// - public int DiskDriveIndex - { - get => _diskDriveIndex; - set - { - // when index is changed update the ActiveDrive - _diskDriveIndex = value; - ActiveDrive = DriveStates[_diskDriveIndex]; - } - } - private int _diskDriveIndex = 0; - - /// - /// The currently active drive - /// - private DriveState ActiveDrive; - - /// - /// Array that holds state information for each possible drive - /// - private DriveState[] DriveStates = new DriveState[4]; - - #endregion - - #region FDD Methods - - /// - /// Initialization / reset of the floppy drive subsystem - /// - private void FDD_Init() - { - for (int i = 0; i < 4; i++) - { - DriveState ds = new DriveState(i, this); - DriveStates[i] = ds; - } - } - - /// - /// Searches for the requested sector - /// - private FloppyDisk.Sector GetSector() - { - FloppyDisk.Sector sector = null; - - // get the current track - var trk = ActiveDrive.Disk.DiskTracks[ActiveDrive.TrackIndex]; - - // get the current sector index - int index = ActiveDrive.SectorIndex; - - // make sure this index exists - if (index > trk.Sectors.Length) - { - index = 0; - } - - // index hole count - int iHole = 0; - - // loop through the sectors in a track - // the loop ends with either the sector being found - // or the index hole being passed twice - while (iHole <= 2) - { - // does the requested sector match the current sector - if (trk.Sectors[index].SectorIDInfo.C == ActiveCommandParams.Cylinder && - trk.Sectors[index].SectorIDInfo.H == ActiveCommandParams.Head && - trk.Sectors[index].SectorIDInfo.R == ActiveCommandParams.Sector && - trk.Sectors[index].SectorIDInfo.N == ActiveCommandParams.SectorSize) - { - // sector has been found - sector = trk.Sectors[index]; - - UnSetBit(SR2_BC, ref Status2); - UnSetBit(SR2_WC, ref Status2); - break; - } - - // check for bad cylinder - if (trk.Sectors[index].SectorIDInfo.C == 255) - { - SetBit(SR2_BC, ref Status2); - } - // check for no cylinder - else if (trk.Sectors[index].SectorIDInfo.C != ActiveCommandParams.Cylinder) - { - SetBit(SR2_WC, ref Status2); - } - - // incrememnt sector index - index++; - - // have we reached the index hole? - if (trk.Sectors.Length <= index) - { - // wrap around - index = 0; - iHole++; - } - } - - // search loop has completed and the sector may or may not have been found - - // bad cylinder detected? - if (Status2.Bit(SR2_BC)) - { - // remove WC - UnSetBit(SR2_WC, ref Status2); - } - - // update sectorindex on drive - ActiveDrive.SectorIndex = index; - - return sector; - } - - #endregion - - #region IFDDHost - - // IFDDHost methods that fall through to the currently active drive - - /// - /// Parses a new disk image and loads it into this floppy drive - /// - public void FDD_LoadDisk(byte[] diskData) - { - // we are only going to load into the first drive - DriveStates[0].FDD_LoadDisk(diskData); - } - - /// - /// Ejects the current disk - /// - public void FDD_EjectDisk() - { - DriveStates[0].FDD_EjectDisk(); - } - - /// - /// Signs whether the current active drive has a disk inserted - /// - public bool FDD_IsDiskLoaded => DriveStates[DiskDriveIndex].FDD_IsDiskLoaded; - - /// - /// Returns the disk object from drive 0 - /// - public FloppyDisk DiskPointer => DriveStates[0].Disk; - - public FloppyDisk Disk { get; set; } - - #endregion - - #region Drive Status Class - - /// - /// Holds specfic state information about a drive - /// - private class DriveState : IFDDHost - { - #region State - - /// - /// The drive ID from an FDC perspective - /// - public int ID; - - /// - /// Signs whether this drive ready - /// TRUE if both drive exists and has a disk inserted - /// - public bool FLAG_READY - { - get - { - if (!FDD_IsDiskLoaded || Disk.GetTrackCount() == 0 || !FDC.FDD_FLAG_MOTOR) - return false; - else - return true; - } - } - - /// - /// Disk is write protected (TRUE BY DEFAULT) - /// - public bool FLAG_WRITEPROTECT = false; - - /// - /// Storage for seek steps - /// One step for each indexpulse (track index) until seeked track - /// - public int SeekCounter; - - /// - /// Seek status - /// - public int SeekStatus; - - /// - /// Age counter - /// - public int SeekAge; - - /// - /// The current side - /// - public byte CurrentSide; - - /// - /// The current track index in the DiskTracks array - /// - public byte TrackIndex; - - /// - /// The track ID of the current cylinder - /// - public byte CurrentTrackID - { - get - { - // default invalid track - int id = 0xff; - - if (Disk == null) - return (byte)id; - - if (Disk.DiskTracks.Count() == 0) - return (byte)id; - - if (TrackIndex >= Disk.GetTrackCount()) - TrackIndex = 0; - else if (TrackIndex < 0) - TrackIndex = 0; - - var track = Disk.DiskTracks[TrackIndex]; - - id = track.TrackNumber; - - return (byte)id; - } - set - { - for (int i = 0; i < Disk.GetTrackCount(); i++) - { - if (Disk.DiskTracks[i].TrackNumber == value) - { - TrackIndex = (byte)i; - break; - } - } - } - } - - - /// - /// The new track that the drive is seeking to - /// (used in seek operations) - /// - public int SeekingTrack; - - /// - /// The current sector index in the Sectors array - /// - public int SectorIndex; - - /// - /// The currently loaded floppy disk - /// - public FloppyDisk Disk { get; set; } - - /// - /// The parent controller - /// - private NECUPD765 FDC; - - #endregion - - #region Lookups - - /// - /// TRUE if we are on track 0 - /// - public bool FLAG_TRACK0 - { - get - { - if (TrackIndex == 0) { return true; } - else { return false; } - } - } - - #endregion - - #region Public Methods - /* - /// - /// Moves the head across the disk cylinders - /// - public void MoveHead(SkipDirection direction, int cylinderCount) - { - // get total tracks - int trackCount = Disk.DiskTracks.Count(); - - int trk = 0; - - switch (direction) - { - case SkipDirection.Increment: - trk = (int)CurrentTrack + cylinderCount; - if (trk >= trackCount) - { - // past the last track - trk = trackCount - 1; - } - else if (trk < 0) - trk = 0; - break; - case SkipDirection.Decrement: - trk = (int)CurrentTrack - cylinderCount; - if (trk < 0) - { - // before the first track - trk = 0; - } - else if (trk >= trackCount) - trk = trackCount - 1; - break; - } - - // move the head - CurrentTrack = (byte)trk; - } - */ - - /* - - /// - /// Finds a supplied sector - /// - public FloppyDisk.Sector FindSector(ref byte[] resBuffer, CommandParameters prms) - { - int index =CurrentSector; - int lc = 0; - FloppyDisk.Sector sector = null; - - bool found = false; - - do - { - sector = Disk.DiskTracks[CurrentTrack].Sectors[index]; - if (sector != null && sector.SectorID == prms.Sector) - { - // sector found - // check for data errors - if ((sector.Status1 & 0x20) != 0 || (sector.Status2 & 0x20) != 0) - { - // data errors found - } - found = true; - break; - } - - // sector doesnt match - var c = Disk.DiskTracks[CurrentTrack].Sectors[index].TrackNumber; - if (c == 255) - { - // bad cylinder - resBuffer[RS_ST2] |= 0x02; - } - else if (prms.Cylinder != c) - { - // cylinder mismatch - resBuffer[RS_ST2] |= 0x10; - } - - // increment index - index++; - - if (index >= Disk.DiskTracks[CurrentTrack].NumberOfSectors) - { - // out of bounds - index = 0; - lc++; - } - - } while (lc < 2); - - if ((resBuffer[RS_ST2] & 0x02) != 0) - { - // bad cylinder set - remove no cylinder - UnSetBit(SR2_WC, ref resBuffer[RS_ST2]); - } - - // update current sector - CurrentSector = index; - - if (found) - return sector; - else - return null; - } - - - /// - /// Populates a result buffer - /// - public void FillResult(ref byte[] resBuffer, CHRN chrn) - { - // clear results - resBuffer[RS_ST0] = 0; - resBuffer[RS_ST1] = 0; - resBuffer[RS_ST2] = 0; - resBuffer[RS_C] = 0; - resBuffer[RS_H] = 0; - resBuffer[RS_R] = 0; - resBuffer[RS_N] = 0; - - if (chrn == null) - { - // no chrn supplied - resBuffer[RS_ST0] = ST0; - resBuffer[RS_ST1] = 0; - resBuffer[RS_ST2] = 0; - resBuffer[RS_C] = 0; - resBuffer[RS_H] = 0; - resBuffer[RS_R] = 0; - resBuffer[RS_N] = 0; - } - } - - - - /// - /// Populates the result buffer with ReadID data - /// - public void ReadID(ref byte[] resBuffer) - { - if (CheckDriveStatus() == false) - { - // drive not ready - resBuffer[RS_ST0] = ST0; - return; - } - - var track = Disk.DiskTracks.Where(a => a.TrackNumber == CurrentTrack).FirstOrDefault(); - - if (track != null && track.NumberOfSectors > 0) - { - // formatted track - - // get the current sector - int index = CurrentSector; - - // is the index out of bounds? - if (index >= track.NumberOfSectors) - { - // reset the index - index = 0; - } - - // read the sector data - var data = track.Sectors[index]; - resBuffer[RS_C] = data.TrackNumber; - resBuffer[RS_H] = data.SideNumber; - resBuffer[RS_R] = data.SectorID; - resBuffer[RS_N] = data.SectorSize; - - resBuffer[RS_ST0] = ST0; - - // increment the current sector - CurrentSector = index + 1; - return; - } - else - { - // unformatted track? - resBuffer[RS_C] = FDC.CommBuffer[CM_C]; - resBuffer[RS_H] = FDC.CommBuffer[CM_H]; - resBuffer[RS_R] = FDC.CommBuffer[CM_R]; - resBuffer[RS_N] = FDC.CommBuffer[CM_N]; - - SetBit(SR0_IC0, ref ST0); - resBuffer[RS_ST0] = ST0; - resBuffer[RS_ST1] = 0x01; - return; - } - } - */ - - /* - - /// - /// The drive performs a seek operation if necessary - /// Return value TRUE indicates seek complete - /// - public void DoSeek() - { - if (CurrentState != DriveMainState.Recalibrate && - CurrentState != DriveMainState.Seek) - { - // no seek/recalibrate has been asked for - return; - } - - if (GetBit(ID, FDC.StatusMain)) - { - // drive is already seeking - return; - } - - RunSeekCycle(); - } - - /// - /// Runs a seek cycle - /// - public void RunSeekCycle() - { - for (;;) - { - switch (SeekState) - { - // seek or recalibrate has been requested - case SeekSubState.Idle: - - if (CurrentState == DriveMainState.Recalibrate) - { - // recalibrate always seeks to track 0 - SeekingTrack = 0; - } - SeekState = SeekSubState.MoveInit; - - // mark drive as busy - // this should be cleared by SIS command - SetBit(ID, ref FDC.StatusMain); - - break; - - // setup for the head move - case SeekSubState.MoveInit: - - if (CurrentTrack == SeekingTrack) - { - // we are already at the required track - if (CurrentState == DriveMainState.Recalibrate && - !FLAG_TRACK0) - { - // recalibration fail - SeekIntState = SeekIntStatus.Abnormal; - - // raise seek interrupt - FDC.ActiveInterrupt = InterruptState.Seek; - - // unset DB bit - UnSetBit(ID, ref FDC.StatusMain); - - // equipment check - SetBit(SR0_EC, ref FDC.Status0); - - SeekState = SeekSubState.PerformCompletion; - break; - } - - if (CurrentState == DriveMainState.Recalibrate && - FLAG_TRACK0) - { - // recalibration success - SeekIntState = SeekIntStatus.Normal; - - // raise seek interrupt - FDC.ActiveInterrupt = InterruptState.Seek; - - // unset DB bit - UnSetBit(ID, ref FDC.StatusMain); - - SeekState = SeekSubState.PerformCompletion; - break; - } - } - - // check for error - if (IntStatus >= IC_ABORTED_DISCREMOVED || Disk == null) - { - // drive not ready - FLAG_READY = false; - - // drive not ready - SeekIntState = SeekIntStatus.DriveNotReady; - - // cancel any interrupt - FDC.ActiveInterrupt = InterruptState.None; - - // unset DB bit - UnSetBit(ID, ref FDC.StatusMain); - - SeekState = SeekSubState.PerformCompletion; - break; - } - - if (SeekCounter > 1) - { - // not ready to seek yet - SeekCounter--; - return; - } - - if (FDC.SRT < 1 && CurrentTrack != SeekingTrack) - { - SeekState = SeekSubState.MoveImmediate; - break; - } - - // head move - SeekState = SeekSubState.HeadMove; - - break; - - case SeekSubState.HeadMove: - - // do the seek - SeekCounter = FDC.SRT; - - if (CurrentTrack < SeekingTrack) - { - // we are seeking forward - var delta = SeekingTrack - CurrentTrack; - MoveHead(SkipDirection.Increment, 1); - } - else if (CurrentTrack > SeekingTrack) - { - // we are seeking backward - var delta = CurrentTrack - SeekingTrack; - MoveHead(SkipDirection.Decrement, 1); - } - - // should the seek be completed now? - if (CurrentTrack == SeekingTrack) - { - SeekState = SeekSubState.PerformCompletion; - break; - } - - // seek not finished yet - return; - - // seek emulation processed immediately - case SeekSubState.MoveImmediate: - - if (CurrentTrack < SeekingTrack) - { - // we are seeking forward - var delta = SeekingTrack - CurrentTrack; - MoveHead(SkipDirection.Increment, delta); - - } - else if (CurrentTrack > SeekingTrack) - { - // we are seeking backward - var delta = CurrentTrack - SeekingTrack; - MoveHead(SkipDirection.Decrement, delta); - } - - SeekState = SeekSubState.PerformCompletion; - break; - - case SeekSubState.PerformCompletion: - SeekDone(); - SeekState = SeekSubState.SeekCompleted; - break; - - case SeekSubState.SeekCompleted: - // seek has already completed - return; - } - } - } - - /// - /// Called when a seek operation has completed - /// - public void SeekDone() - { - SeekCounter = 0; - SeekingTrack = CurrentTrack; - - // generate ST0 register data - - // get only the IC bits - IntStatus &= IC_ABORTED_DISCREMOVED; - - // drive ready? - if (!FLAG_READY) - { - SetBit(SR0_NR, ref IntStatus); - SetBit(SR0_EC, ref IntStatus); - - // are we recalibrating? - if (CurrentState == DriveMainState.Recalibrate) - { - SetBit(SR0_EC, ref IntStatus); - } - } - - // set seek end - SetBit(SR0_SE, ref IntStatus); - /* - // head address - if (CurrentSide > 0) - { - SetBit(SR0_HD, ref IntStatus); - - // drive only supports 1 head - // set the EC bit - SetBit(SR0_EC, ref IntStatus); - } - */ - /* - // UnitSelect - SetUnitSelect(ID, ref IntStatus); - - // move to none state - //CurrentState = DriveMainState.None; - - //SeekState = SeekSubState.SeekCompleted; - - // set the seek interrupt flag for this drive - // this will be cleared at the next successful senseint - FLAG_SEEK_INTERRUPT = true; - - //CurrentState = DriveMainState.None; - - } - */ - - #endregion - - #region Construction - - public DriveState(int driveID, NECUPD765 fdc) - { - ID = driveID; - FDC = fdc; - } - - #endregion - - #region IFDDHost - - /// - /// Parses a new disk image and loads it into this floppy drive - /// - public void FDD_LoadDisk(byte[] diskData) - { - // try dsk first - FloppyDisk fdd = null; - bool found = false; - - foreach (DiskType type in Enum.GetValues(typeof(DiskType))) - { - switch (type) - { - case DiskType.CPCExtended: - fdd = new CPCExtendedFloppyDisk(); - found = fdd.ParseDisk(diskData); - break; - case DiskType.CPC: - fdd = new CPCFloppyDisk(); - found = fdd.ParseDisk(diskData); - break; - } - - if (found) - { - Disk = fdd; - break; - } - } - - if (!found) - { - throw new Exception(this.GetType().ToString() + - "\n\nDisk image file could not be parsed. Potentially an unknown format."); - } - } - - /// - /// Ejects the current disk - /// - public void FDD_EjectDisk() - { - Disk = null; - //FLAG_READY = false; - } - - /// - /// Signs whether the current active drive has a disk inserted - /// - public bool FDD_IsDiskLoaded - { - get - { - if (Disk != null) - return true; - else - return false; - } - } - - #endregion - - #region StateSerialization - - public void SyncState(Serializer ser) - { - ser.Sync(nameof(ID), ref ID); - ser.Sync(nameof(FLAG_WRITEPROTECT), ref FLAG_WRITEPROTECT); - //ser.Sync(nameof(FLAG_DISKCHANGED), ref FLAG_DISKCHANGED); - //ser.Sync(nameof(FLAG_RECALIBRATING), ref FLAG_RECALIBRATING); - //ser.Sync(nameof(FLAG_SEEK_INTERRUPT), ref FLAG_SEEK_INTERRUPT); - //ser.Sync(nameof(IntStatus), ref IntStatus); - //ser.Sync(nameof(ST0), ref ST0); - //ser.Sync(nameof(RecalibrationCounter), ref RecalibrationCounter); - ser.Sync(nameof(SeekCounter), ref SeekCounter); - ser.Sync(nameof(SeekStatus), ref SeekStatus); - ser.Sync(nameof(SeekAge), ref SeekAge); - ser.Sync(nameof(CurrentSide), ref CurrentSide); - //ser.Sync(nameof(CurrentTrack), ref CurrentTrack); - ser.Sync(nameof(TrackIndex), ref TrackIndex); - ser.Sync(nameof(SeekingTrack), ref SeekingTrack); - //ser.Sync(nameof(CurrentSector), ref CurrentSector); - ser.Sync(nameof(SectorIndex), ref SectorIndex); - //ser.Sync(nameof(RAngles), ref RAngles); - //ser.Sync(nameof(DataPointer), ref DataPointer); - //ser.SyncEnum(nameof(CurrentState), ref CurrentState); - //ser.SyncEnum(nameof(SeekState), ref SeekState); - //ser.SyncEnum(nameof(SeekIntState), ref SeekIntState); - } - - #endregion - } - - #endregion - } -} diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765CPC.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765CPC.cs new file mode 100644 index 0000000000..012ecf99ef --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765CPC.cs @@ -0,0 +1,68 @@ +using System; + +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + +namespace BizHawk.Emulation.Cores.Computers.AmstradCPC +{ + public class NECUPD765CPC : NECUPD765 + { + protected override CPCDriveState ConstructDriveState(int driveID, NECUPD765 fdc) => new CPCDriveState(driveID, fdc); + + protected override void TimingInit() + { + // z80 timing + double frameSize = _machine.GateArray.FrameLength; + double rRate = _machine.GateArray.Z80ClockSpeed / frameSize; + long tPerSecond = (long)(frameSize * rRate); + CPUCyclesPerMs = tPerSecond / 1000; + + // drive timing + double dRate = DriveClock / frameSize; + long dPerSecond = (long)(frameSize * dRate); + DriveCyclesPerMs = dPerSecond / 1000; + + long TStatesPerDriveCycle = (long)((double)_machine.GateArray.Z80ClockSpeed / DriveClock); + StatesPerDriveTick = TStatesPerDriveCycle; + + } + + public class CPCDriveState : NECUPD765DriveState + { + public CPCDriveState(int driveID, NECUPD765 fdc) : base(driveID, fdc) {} + + public override void FDD_LoadDisk(byte[] diskData) + { + // try dsk first + FloppyDisk fdd = null; + bool found = false; + + foreach (DiskType type in Enum.GetValues(typeof(DiskType))) + { + switch (type) + { + case DiskType.CPCExtended: + fdd = new CPCExtendedFloppyDisk(); + found = fdd.ParseDisk(diskData); + break; + case DiskType.CPC: + fdd = new CPCFloppyDisk(); + found = fdd.ParseDisk(diskData); + break; + } + + if (found) + { + Disk = fdd; + break; + } + } + + if (!found) + { + throw new Exception(this.GetType().ToString() + + "\n\nDisk image file could not be parsed. Potentially an unknown format."); + } + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/AmstradGateArray.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/AmstradGateArray.cs index f12028334b..05bb4be727 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/AmstradGateArray.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/AmstradGateArray.cs @@ -7,6 +7,8 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { /// @@ -23,7 +25,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC private CRCT_6845 CRCT => _machine.CRCT; //private CRTDevice CRT => _machine.CRT; private IPSG PSG => _machine.AYDevice; - private NECUPD765 FDC => _machine.UPDDiskDevice; + private NECUPD765CPC FDC => _machine.UPDDiskDevice; private DatacorderDevice DATACORDER => _machine.TapeDevice; private ushort BUSRQ => CPU.MEMRQ[CPU.bus_pntr]; public const ushort PCh = 1; diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRCT_6845.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRCT_6845.cs index 3b6b17370e..bd71f9ab77 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRCT_6845.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRCT_6845.cs @@ -1,5 +1,6 @@ using BizHawk.Common; using BizHawk.Common.NumberExtensions; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/PPI/PPI_8255.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/PPI/PPI_8255.cs index 7570ce10c8..e0dc66dbe2 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/PPI/PPI_8255.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/PPI/PPI_8255.cs @@ -1,6 +1,7 @@ using System.Collections; using BizHawk.Common; using BizHawk.Common.NumberExtensions; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/SoundOutput/Beeper.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/SoundOutput/Beeper.cs index db6dbda9f0..5db18834e1 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/SoundOutput/Beeper.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/SoundOutput/Beeper.cs @@ -2,6 +2,8 @@ using BizHawk.Emulation.Common; using System; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { /// diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPC6128/CPC6128.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPC6128/CPC6128.cs index 2128e34ca6..de6990be21 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPC6128/CPC6128.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPC6128/CPC6128.cs @@ -37,7 +37,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC TapeDevice = new DatacorderDevice(autoTape); TapeDevice.Init(this); - UPDDiskDevice = new NECUPD765(); + UPDDiskDevice = new NECUPD765CPC(); UPDDiskDevice.Init(this); InitializeMedia(files); diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.Media.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.Media.cs index 60520e64f6..1cb34dc16b 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.Media.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.Media.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { /// diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.cs index 0e9d9427d9..c412969b49 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.cs @@ -1,5 +1,6 @@ using BizHawk.Common; using BizHawk.Emulation.Cores.Components.Z80A; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { @@ -7,7 +8,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC /// The abstract class that all emulated models will inherit from /// * Main properties / fields / contruction* /// - public abstract partial class CPCBase + public abstract partial class CPCBase : CPCSpectrumBase.CPCSpectrumBase { #region Devices @@ -50,7 +51,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC /// /// The Amstrad disk drive /// - public virtual NECUPD765 UPDDiskDevice { get; set; } + public virtual NECUPD765CPC UPDDiskDevice { get; set; } /// /// The Cathode Ray Tube Controller chip diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/CPCExtendedFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/CPCExtendedFloppyDisk.cs index f045be5df5..f93e17a7b6 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/CPCExtendedFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/CPCExtendedFloppyDisk.cs @@ -3,6 +3,8 @@ using BizHawk.Common; using System; using System.Collections.Generic; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { /// diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/CPCFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/CPCFloppyDisk.cs index 214c0fced0..07eff55776 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/CPCFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/CPCFloppyDisk.cs @@ -3,6 +3,8 @@ using BizHawk.Common; using System.Collections.Generic; using System; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { /// diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/DiskType.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/DiskType.cs deleted file mode 100644 index df211b4e55..0000000000 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/DiskType.cs +++ /dev/null @@ -1,19 +0,0 @@ - -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC -{ - /// - /// The different disk formats ZXHawk currently supports - /// - public enum DiskType - { - /// - /// Standard CPCEMU disk format (used in the built-in +3 disk drive) - /// - CPC, - - /// - /// Extended CPCEMU disk format (used in the built-in +3 disk drive) - /// - CPCExtended - } -} diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Tape/CDT/CdtConverter.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Tape/CDT/CdtConverter.cs index 401e97ff56..7192292d44 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Tape/CDT/CdtConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Tape/CDT/CdtConverter.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { /// diff --git a/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/CPCSpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/CPCSpectrumBase.cs new file mode 100644 index 0000000000..3fce366f34 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/CPCSpectrumBase.cs @@ -0,0 +1,9 @@ +using BizHawk.Emulation.Cores.Components.Z80A; + +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase +{ + public interface CPCSpectrumBase + { + Z80A CPU { get; set; } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Abstraction/IBeeperDevice.cs similarity index 88% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Abstraction/IBeeperDevice.cs index 8d40ce166c..02f9c3c622 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Abstraction/IBeeperDevice.cs @@ -1,6 +1,6 @@ using BizHawk.Common; -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// Represents a beeper/buzzer device diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Abstraction/IFDDHost.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Abstraction/IFDDHost.cs similarity index 90% rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Abstraction/IFDDHost.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Abstraction/IFDDHost.cs index 7ae645b750..eb7cddeaac 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Abstraction/IFDDHost.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Abstraction/IFDDHost.cs @@ -1,5 +1,5 @@  -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// Defines an object that can load a floppy disk image diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Abstraction/IJoystick.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Abstraction/IJoystick.cs similarity index 82% rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Abstraction/IJoystick.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Abstraction/IJoystick.cs index 50088cceae..41e5546f82 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Abstraction/IJoystick.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Abstraction/IJoystick.cs @@ -1,15 +1,17 @@  -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC +using System; + +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// Represents a spectrum joystick /// - public interface IJoystick + public interface IJoystick where T : Enum { /// /// The type of joystick /// - JoystickType JoyType { get; } + T JoyType { get; } /// /// Array of all the possibly button press names diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Abstraction/IPortIODevice.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Abstraction/IPortIODevice.cs similarity index 86% rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Abstraction/IPortIODevice.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Abstraction/IPortIODevice.cs index b71e08ee08..6515a55e27 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Abstraction/IPortIODevice.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Abstraction/IPortIODevice.cs @@ -1,5 +1,5 @@  -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// Represents a device that utilizes port IN & OUT diff --git a/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/CHRN.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/CHRN.cs new file mode 100644 index 0000000000..b1970aa4a3 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/CHRN.cs @@ -0,0 +1,179 @@ +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase +{ + /// + /// Used for the sector CHRN structure + /// + public class CHRN + { + /// + /// Track + /// + public byte C { get; set; } + + /// + /// Side + /// + public byte H { get; set; } + + /// + /// Sector ID + /// + public byte R { get; set; } + + /// + /// Sector Size + /// + public byte N { get; set; } + + /// + /// Status register 1 + /// + private byte _flag1; + public byte Flag1 + { + get => _flag1; + set => _flag1 = value; + } + + /// + /// Status register 2 + /// + private byte _flag2; + public byte Flag2 + { + get => _flag2; + set => _flag2 = value; + } + + /// + /// Used to store the last transmitted/received data bytes + /// + public byte[] DataBytes { get; set; } + + /// + /// ID for the read/write data command + /// + public int DataID { get; set; } + + #region Helper Methods + + /// + /// Missing Address Mark (Sector_ID or DAM not found) + /// + public bool ST1MA + { + get => NECUPD765.GetBit(0, _flag1); + set + { + if (value) { NECUPD765.SetBit(0, ref _flag1); } + else { NECUPD765.UnSetBit(0, ref _flag1); } + } + } + + /// + /// No Data (Sector_ID not found, CRC fail in ID_field) + /// + public bool ST1ND + { + get => NECUPD765.GetBit(2, _flag1); + set + { + if (value) { NECUPD765.SetBit(2, ref _flag1); } + else { NECUPD765.UnSetBit(2, ref _flag1); } + } + } + + /// + /// Data Error (CRC-fail in ID- or Data-Field) + /// + public bool ST1DE + { + get => NECUPD765.GetBit(5, _flag1); + set + { + if (value) { NECUPD765.SetBit(5, ref _flag1); } + else { NECUPD765.UnSetBit(5, ref _flag1); } + } + } + + /// + /// End of Track (set past most read/write commands) (see IC) + /// + public bool ST1EN + { + get => NECUPD765.GetBit(7, _flag1); + set + { + if (value) { NECUPD765.SetBit(7, ref _flag1); } + else { NECUPD765.UnSetBit(7, ref _flag1); } + } + } + + /// + /// Missing Address Mark in Data Field (DAM not found) + /// + public bool ST2MD + { + get => NECUPD765.GetBit(0, _flag2); + set + { + if (value) { NECUPD765.SetBit(0, ref _flag2); } + else { NECUPD765.UnSetBit(0, ref _flag2); } + } + } + + /// + /// Bad Cylinder (read/programmed track-ID different and read-ID = FF) + /// + public bool ST2BC + { + get => NECUPD765.GetBit(1, _flag2); + set + { + if (value) { NECUPD765.SetBit(1, ref _flag2); } + else { NECUPD765.UnSetBit(1, ref _flag2); } + } + } + + /// + /// Wrong Cylinder (read/programmed track-ID different) (see b1) + /// + public bool ST2WC + { + get => NECUPD765.GetBit(4, _flag2); + set + { + if (value) { NECUPD765.SetBit(4, ref _flag2); } + else { NECUPD765.UnSetBit(4, ref _flag2); } + } + } + + /// + /// Data Error in Data Field (CRC-fail in data-field) + /// + public bool ST2DD + { + get => NECUPD765.GetBit(5, _flag2); + set + { + if (value) { NECUPD765.SetBit(5, ref _flag2); } + else { NECUPD765.UnSetBit(5, ref _flag2); } + } + } + + /// + /// Control Mark (read/scan command found sector with deleted DAM) + /// + public bool ST2CM + { + get => NECUPD765.GetBit(6, _flag2); + set + { + if (value) { NECUPD765.SetBit(6, ref _flag2); } + else { NECUPD765.UnSetBit(6, ref _flag2); } + } + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/INECUPD765.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/INECUPD765.cs new file mode 100644 index 0000000000..444aa99cfc --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/INECUPD765.cs @@ -0,0 +1,7 @@ +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase +{ + public interface INECUPD765 + { + bool FDD_FLAG_MOTOR { get; } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.Definitions.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.Definitions.cs similarity index 97% rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.Definitions.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.Definitions.cs index a560164411..85825ab2e5 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.Definitions.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.Definitions.cs @@ -1,20 +1,20 @@ using BizHawk.Common; using System; -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// Definitions /// #region Attribution /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ #endregion - public partial class NECUPD765 + public abstract partial class NECUPD765 { #region Enums @@ -692,10 +692,10 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC /// private class Command { - // /// - // /// Mask to remove potential parameter bits (5,6, and or 7) in order to identify the command - // /// - // public int BitMask { get; set; } +// /// +// /// Mask to remove potential parameter bits (5,6, and or 7) in order to identify the command +// /// +// public int BitMask { get; set; } /// /// The command code after bitmask has been applied /// diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.FDC.cs similarity index 94% rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.FDC.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.FDC.cs index a68d84dec6..c88ff82bef 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.FDC.cs @@ -3,20 +3,20 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// FDC State and Methods /// #region Attribution /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ #endregion - public partial class NECUPD765 + public abstract partial class NECUPD765 { #region Controller State @@ -210,69 +210,69 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC /// Main status register (accessed via reads to port 0x2ffd) /// /* - b0..3 DB FDD0..3 Busy (seek/recalib active, until succesful sense intstat) - b4 CB FDC Busy (still in command-, execution- or result-phase) - b5 EXM Execution Mode (still in execution-phase, non_DMA_only) - b6 DIO Data Input/Output (0=CPU->FDC, 1=FDC->CPU) (see b7) - b7 RQM Request For Master (1=ready for next byte) (see b6 for direction) - */ + b0..3 DB FDD0..3 Busy (seek/recalib active, until succesful sense intstat) + b4 CB FDC Busy (still in command-, execution- or result-phase) + b5 EXM Execution Mode (still in execution-phase, non_DMA_only) + b6 DIO Data Input/Output (0=CPU->FDC, 1=FDC->CPU) (see b7) + b7 RQM Request For Master (1=ready for next byte) (see b6 for direction) + */ private byte StatusMain; /// /// Status Register 0 /// /* - b0,1 US Unit Select (driveno during interrupt) - b2 HD Head Address (head during interrupt) - b3 NR Not Ready (drive not ready or non-existing 2nd head selected) - b4 EC Equipment Check (drive failure or recalibrate failed (retry)) - b5 SE Seek End (Set if seek-command completed) - b6,7 IC Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd - or senseint with no int occured, 3=aborted:disc removed etc.) - */ + b0,1 US Unit Select (driveno during interrupt) + b2 HD Head Address (head during interrupt) + b3 NR Not Ready (drive not ready or non-existing 2nd head selected) + b4 EC Equipment Check (drive failure or recalibrate failed (retry)) + b5 SE Seek End (Set if seek-command completed) + b6,7 IC Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd + or senseint with no int occured, 3=aborted:disc removed etc.) + */ private byte Status0; /// /// Status Register 1 /// /* - b0 MA Missing Address Mark (Sector_ID or DAM not found) - b1 NW Not Writeable (tried to write/format disc with wprot_tab=on) - b2 ND No Data (Sector_ID not found, CRC fail in ID_field) - b3,6 0 Not used - b4 OR Over Run (CPU too slow in execution-phase (ca. 26us/Byte)) - b5 DE Data Error (CRC-fail in ID- or Data-Field) - b7 EN End of Track (set past most read/write commands) (see IC) - */ + b0 MA Missing Address Mark (Sector_ID or DAM not found) + b1 NW Not Writeable (tried to write/format disc with wprot_tab=on) + b2 ND No Data (Sector_ID not found, CRC fail in ID_field) + b3,6 0 Not used + b4 OR Over Run (CPU too slow in execution-phase (ca. 26us/Byte)) + b5 DE Data Error (CRC-fail in ID- or Data-Field) + b7 EN End of Track (set past most read/write commands) (see IC) + */ private byte Status1; /// /// Status Register 2 /// /* - b0 MD Missing Address Mark in Data Field (DAM not found) - b1 BC Bad Cylinder (read/programmed track-ID different and read-ID = FF) - b2 SN Scan Not Satisfied (no fitting sector found) - b3 SH Scan Equal Hit (equal) - b4 WC Wrong Cylinder (read/programmed track-ID different) (see b1) - b5 DD Data Error in Data Field (CRC-fail in data-field) - b6 CM Control Mark (read/scan command found sector with deleted DAM) - b7 0 Not Used - */ + b0 MD Missing Address Mark in Data Field (DAM not found) + b1 BC Bad Cylinder (read/programmed track-ID different and read-ID = FF) + b2 SN Scan Not Satisfied (no fitting sector found) + b3 SH Scan Equal Hit (equal) + b4 WC Wrong Cylinder (read/programmed track-ID different) (see b1) + b5 DD Data Error in Data Field (CRC-fail in data-field) + b6 CM Control Mark (read/scan command found sector with deleted DAM) + b7 0 Not Used + */ private byte Status2; /// /// Status Register 3 /// /* - b0,1 US Unit Select (pin 28,29 of FDC) - b2 HD Head Address (pin 27 of FDC) - b3 TS Two Side (0=yes, 1=no (!)) - b4 T0 Track 0 (on track 0 we are) - b5 RY Ready (drive ready signal) - b6 WP Write Protected (write protected) - b7 FT Fault (if supported: 1=Drive failure) - */ + b0,1 US Unit Select (pin 28,29 of FDC) + b2 HD Head Address (pin 27 of FDC) + b3 TS Two Side (0=yes, 1=no (!)) + b4 T0 Track 0 (on track 0 we are) + b5 RY Ready (drive ready signal) + b6 WP Write Protected (write protected) + b7 FT Fault (if supported: 1=Drive failure) + */ private byte Status3; #endregion @@ -385,8 +385,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC sectorSize = 0x80 << ActiveCommandParams.SectorSize; } - var mtc = maxTransferCap; - // get the current track var track = ActiveDrive.Disk.DiskTracks.FirstOrDefault(a => a.TrackNumber == ActiveDrive.CurrentTrackID); @@ -672,22 +670,13 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC switch (ActiveCommandParams.SectorSize) { case 1: - if (CMD_FLAG_MF) - maxTransferCap = 6656; - else - maxTransferCap = 3840; + maxTransferCap = CMD_FLAG_MF ? 6656 : 3840; break; case 2: - if (CMD_FLAG_MF) - maxTransferCap = 7680; - else - maxTransferCap = 4096; + maxTransferCap = CMD_FLAG_MF ? 7680 : 4096; break; case 3: - if (CMD_FLAG_MF) - maxTransferCap = 8192; - else - maxTransferCap = 4096; + maxTransferCap = CMD_FLAG_MF ? 8192 : 4096; break; } @@ -2148,17 +2137,17 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC // second byte is the current track id ResBuffer[1] = ActiveDrive.CurrentTrackID; } - /* - else if (ActiveDrive.SeekStatus == SEEK_INTACKNOWLEDGED) - { - // DriveA interrupt has already been acknowledged - ActiveDrive.SeekStatus = SEEK_IDLE; +#if false + else if (ActiveDrive.SeekStatus == SEEK_INTACKNOWLEDGED) + { + // DriveA interrupt has already been acknowledged + ActiveDrive.SeekStatus = SEEK_IDLE; - ResLength = 1; - Status0 = 192; - ResBuffer[0] = Status0; - } - */ + ResLength = 1; + Status0 = 192; + ResBuffer[0] = Status0; + } +#endif else if (ActiveDrive.SeekStatus == SEEK_IDLE) { // SIS with no interrupt @@ -2363,7 +2352,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC SetBit(MSR_EXM, ref StatusMain); SetBit(MSR_CB, ref StatusMain); - // overrun detection + // overrun detection OverrunCounter++; if (OverrunCounter >= 64) { @@ -2383,6 +2372,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC break; } +// if (!CheckTiming()) UnSetBit(MSR_EXM, ref StatusMain); + return StatusMain; } @@ -2474,7 +2465,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC //// we are in command phase case Phase.Command: // attempt to process this parameter byte - //ProcessCommand(data); + //ProcessCommand(data); ActiveCommand.CommandDelegate(); break; //// we are in execution phase @@ -2553,15 +2544,15 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC CMDIndex = CommandList.Count() - 1; } - /* - if ((CMD_FLAG_MF && !ActiveCommand.MF) || - (CMD_FLAG_MT && !ActiveCommand.MT) || - (CMD_FLAG_SK && !ActiveCommand.SK)) - { - // command byte included spurious bit 5,6 or 7 flags - CMDIndex = CommandList.Count() - 1; - } - */ +#if false + if ((CMD_FLAG_MF && !ActiveCommand.MF) || + (CMD_FLAG_MT && !ActiveCommand.MT) || + (CMD_FLAG_SK && !ActiveCommand.SK)) + { + // command byte included spurious bit 5,6 or 7 flags + CMDIndex = CommandList.Count() - 1; + } +#endif } CommCounter = 0; @@ -2571,14 +2562,14 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC // move to command phase ActivePhase = Phase.Command; - /* - // check for invalid SIS - if (ActiveInterrupt == InterruptState.None && CMDIndex == CC_SENSE_INTSTATUS) - { - CMDIndex = CC_INVALID; - //ActiveCommand.CommandDelegate(InstructionState.StartResult); - } - */ +#if false + // check for invalid SIS + if (ActiveInterrupt == InterruptState.None && CMDIndex == CC_SENSE_INTSTATUS) + { + CMDIndex = CC_INVALID; + //ActiveCommand.CommandDelegate(InstructionState.StartResult); + } +#endif // set reslength ResLength = ActiveCommand.ResultByteCount; diff --git a/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.FDD.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.FDD.cs new file mode 100644 index 0000000000..5c2cd776a5 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.FDD.cs @@ -0,0 +1,189 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase +{ + /// + /// Floppy drive related stuff + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public abstract partial class NECUPD765 : IFDDHost, INECUPD765 + { + #region Drive State + + /// + /// FDD Flag - motor on/off + /// + public bool FDD_FLAG_MOTOR; + + bool INECUPD765.FDD_FLAG_MOTOR => FDD_FLAG_MOTOR; + + /// + /// The index of the currently active disk drive + /// + public int DiskDriveIndex + { + get => _diskDriveIndex; + set + { + // when index is changed update the ActiveDrive + _diskDriveIndex = value; + ActiveDrive = DriveStates[_diskDriveIndex]; + } + } + private int _diskDriveIndex = 0; + + /// + /// The currently active drive + /// + private NECUPD765DriveState ActiveDrive; + + /// + /// Array that holds state information for each possible drive + /// + private NECUPD765DriveState[] DriveStates = new NECUPD765DriveState[4]; + + #endregion + + #region FDD Methods + + protected abstract TDriveState ConstructDriveState(int driveID, NECUPD765 fdc); + + /// + /// Initialization / reset of the floppy drive subsystem + /// + private void FDD_Init() + { + for (int i = 0; i < 4; i++) + { + NECUPD765DriveState ds = ConstructDriveState(i, this); + DriveStates[i] = ds; + } + } + + /// + /// Searches for the requested sector + /// + private FloppyDisk.Sector GetSector() + { + FloppyDisk.Sector sector = null; + + // get the current track + var trk = ActiveDrive.Disk.DiskTracks[ActiveDrive.TrackIndex]; + + // get the current sector index + int index = ActiveDrive.SectorIndex; + + // make sure this index exists + if (index > trk.Sectors.Length) + { + index = 0; + } + + // index hole count + int iHole = 0; + + // loop through the sectors in a track + // the loop ends with either the sector being found + // or the index hole being passed twice + while (iHole <= 2) + { + // does the requested sector match the current sector + if (trk.Sectors[index].SectorIDInfo.C == ActiveCommandParams.Cylinder && + trk.Sectors[index].SectorIDInfo.H == ActiveCommandParams.Head && + trk.Sectors[index].SectorIDInfo.R == ActiveCommandParams.Sector && + trk.Sectors[index].SectorIDInfo.N == ActiveCommandParams.SectorSize) + { + // sector has been found + sector = trk.Sectors[index]; + + UnSetBit(SR2_BC, ref Status2); + UnSetBit(SR2_WC, ref Status2); + break; + } + + // check for bad cylinder + if (trk.Sectors[index].SectorIDInfo.C == 255) + { + SetBit(SR2_BC, ref Status2); + } + // check for no cylinder + else if (trk.Sectors[index].SectorIDInfo.C != ActiveCommandParams.Cylinder) + { + SetBit(SR2_WC, ref Status2); + } + + // incrememnt sector index + index++; + + // have we reached the index hole? + if (trk.Sectors.Length <= index) + { + // wrap around + index = 0; + iHole++; + } + } + + // search loop has completed and the sector may or may not have been found + + // bad cylinder detected? + if (Status2.Bit(SR2_BC)) + { + // remove WC + UnSetBit(SR2_WC, ref Status2); + } + + // update sectorindex on drive + ActiveDrive.SectorIndex = index; + + return sector; + } + + #endregion + + #region IFDDHost + + // IFDDHost methods that fall through to the currently active drive + + /// + /// Parses a new disk image and loads it into this floppy drive + /// + public void FDD_LoadDisk(byte[] diskData) + { + // we are only going to load into the first drive + DriveStates[0].FDD_LoadDisk(diskData); + } + + /// + /// Ejects the current disk + /// + public void FDD_EjectDisk() + { + DriveStates[0].FDD_EjectDisk(); + } + + /// + /// Signs whether the current active drive has a disk inserted + /// + public bool FDD_IsDiskLoaded => DriveStates[DiskDriveIndex].FDD_IsDiskLoaded; + + /// + /// Returns the disk object from drive 0 + /// + public FloppyDisk DiskPointer => DriveStates[0].Disk; + + public FloppyDisk Disk { get; set; } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.IPortIODevice.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.IPortIODevice.cs similarity index 89% rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.IPortIODevice.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.IPortIODevice.cs index 5f6e87f99a..ed50adff38 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.IPortIODevice.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.IPortIODevice.cs @@ -3,20 +3,20 @@ using System.Collections; using System.Collections.Generic; using System.Text; -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// IPortIODevice /// #region Attribution /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ #endregion - public partial class NECUPD765 : IPortIODevice + public abstract partial class NECUPD765 : IPortIODevice { #region Dev Logging @@ -31,15 +31,15 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC /* - * Status read - * Data write - * Data read - * CMD code - * CMD string - * MT flag - * MK flag - * SK flag - * */ + Status read + Data write + Data read + CMD code + CMD string + MT flag + MK flag + SK flag + */ private string[] workingArr = new string[3]; private void BuildCSVLine() diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPS765.Static.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.Static.cs similarity index 86% rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPS765.Static.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.Static.cs index 9abd27e6a5..1dff759e22 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPS765.Static.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.Static.cs @@ -1,19 +1,19 @@ using System.Collections; -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// Static helper methods /// #region Attribution /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ #endregion - public partial class NECUPD765 + public abstract partial class NECUPD765 { /// /// Returns the specified bit value from supplied byte diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.Timing.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.Timing.cs similarity index 69% rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.Timing.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.Timing.cs index c6fb76300b..f80e422288 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.Timing.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.Timing.cs @@ -1,18 +1,18 @@  -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// Timimng /// #region Attribution /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ #endregion - public partial class NECUPD765 + public abstract partial class NECUPD765 { /// /// The current Z80 cycle @@ -43,7 +43,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC /// /// Defines the numbers of Z80 cycles per MS /// - private long CPUCyclesPerMs; + protected long CPUCyclesPerMs; /// /// The floppy drive emulated clock speed @@ -73,23 +73,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC /// /// Initializes the timing routines /// - private void TimingInit() - { - // z80 timing - double frameSize = _machine.GateArray.FrameLength; - double rRate = _machine.GateArray.Z80ClockSpeed / frameSize; - long tPerSecond = (long)(frameSize * rRate); - CPUCyclesPerMs = tPerSecond / 1000; - - // drive timing - double dRate = DriveClock / frameSize; - long dPerSecond = (long)(frameSize * dRate); - DriveCyclesPerMs = dPerSecond / 1000; - - long TStatesPerDriveCycle = (long)((double)_machine.GateArray.Z80ClockSpeed / DriveClock); - StatesPerDriveTick = TStatesPerDriveCycle; - - } + protected abstract void TimingInit(); /// /// Called by reads to the main status register diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.cs similarity index 68% rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.cs index b876db870c..d4fcbfd24c 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Disk/NECUPD765.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765.cs @@ -1,27 +1,29 @@ using BizHawk.Common; using System.Collections.Generic; -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// The NEC floppy disk controller (and floppy drive) found in the +3 /// #region Attribution /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ #endregion - public partial class NECUPD765 + public abstract partial class NECUPD765 + where TMachine : CPCSpectrumBase + where TDriveState : NECUPD765DriveState { #region Devices /// - /// The emulated spectrum machine + /// The emulated CPC or Spectrum machine /// - private CPCBase _machine; + protected TMachine _machine; #endregion @@ -38,7 +40,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC /// /// Initialization routine /// - public void Init(CPCBase machine) + public void Init(TMachine machine) { _machine = machine; FDD_Init(); @@ -94,56 +96,56 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { CommandList = new List { - // read data - new Command { CommandDelegate = UPD_ReadData, CommandCode = 0x06, MT = true, MF = true, SK = true, IsRead = true, + // read data + new Command { CommandDelegate = UPD_ReadData, CommandCode = 0x06, MT = true, MF = true, SK = true, IsRead = true, Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, - // read id - new Command { CommandDelegate = UPD_ReadID, CommandCode = 0x0a, MF = true, IsRead = true, + // read id + new Command { CommandDelegate = UPD_ReadID, CommandCode = 0x0a, MF = true, IsRead = true, Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 7 }, - // specify - new Command { CommandDelegate = UPD_Specify, CommandCode = 0x03, + // specify + new Command { CommandDelegate = UPD_Specify, CommandCode = 0x03, Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, - // read diagnostic - new Command { CommandDelegate = UPD_ReadDiagnostic, CommandCode = 0x02, MF = true, SK = true, IsRead = true, + // read diagnostic + new Command { CommandDelegate = UPD_ReadDiagnostic, CommandCode = 0x02, MF = true, SK = true, IsRead = true, Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, - // scan equal - new Command { CommandDelegate = UPD_ScanEqual, CommandCode = 0x11, MT = true, MF = true, SK = true, IsRead = true, + // scan equal + new Command { CommandDelegate = UPD_ScanEqual, CommandCode = 0x11, MT = true, MF = true, SK = true, IsRead = true, Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, - // scan high or equal - new Command { CommandDelegate = UPD_ScanHighOrEqual, CommandCode = 0x1d, MT = true, MF = true, SK = true, IsRead = true, + // scan high or equal + new Command { CommandDelegate = UPD_ScanHighOrEqual, CommandCode = 0x1d, MT = true, MF = true, SK = true, IsRead = true, Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, - // scan low or equal - new Command { CommandDelegate = UPD_ScanLowOrEqual, CommandCode = 0x19, MT = true, MF = true, SK = true, IsRead = true, + // scan low or equal + new Command { CommandDelegate = UPD_ScanLowOrEqual, CommandCode = 0x19, MT = true, MF = true, SK = true, IsRead = true, Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, - // read deleted data - new Command { CommandDelegate = UPD_ReadDeletedData, CommandCode = 0x0c, MT = true, MF = true, SK = true, IsRead = true, + // read deleted data + new Command { CommandDelegate = UPD_ReadDeletedData, CommandCode = 0x0c, MT = true, MF = true, SK = true, IsRead = true, Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, - // write data - new Command { CommandDelegate = UPD_WriteData, CommandCode = 0x05, MT = true, MF = true, IsWrite = true, + // write data + new Command { CommandDelegate = UPD_WriteData, CommandCode = 0x05, MT = true, MF = true, IsWrite = true, Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, - // write id - new Command { CommandDelegate = UPD_WriteID, CommandCode = 0x0d, MF = true, IsWrite = true, + // write id + new Command { CommandDelegate = UPD_WriteID, CommandCode = 0x0d, MF = true, IsWrite = true, Direction = CommandDirection.IN, ParameterByteCount = 5, ResultByteCount = 7 }, - // write deleted data - new Command { CommandDelegate = UPD_WriteDeletedData, CommandCode = 0x09, MT = true, MF = true, IsWrite = true, + // write deleted data + new Command { CommandDelegate = UPD_WriteDeletedData, CommandCode = 0x09, MT = true, MF = true, IsWrite = true, Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, - // seek - new Command { CommandDelegate = UPD_Seek, CommandCode = 0x0f, + // seek + new Command { CommandDelegate = UPD_Seek, CommandCode = 0x0f, Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, - // recalibrate (seek track00) - new Command { CommandDelegate = UPD_Recalibrate, CommandCode = 0x07, + // recalibrate (seek track00) + new Command { CommandDelegate = UPD_Recalibrate, CommandCode = 0x07, Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 }, - // sense interrupt status - new Command { CommandDelegate = UPD_SenseInterruptStatus, CommandCode = 0x08, + // sense interrupt status + new Command { CommandDelegate = UPD_SenseInterruptStatus, CommandCode = 0x08, Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 }, - // sense drive status - new Command { CommandDelegate = UPD_SenseDriveStatus, CommandCode = 0x04, + // sense drive status + new Command { CommandDelegate = UPD_SenseDriveStatus, CommandCode = 0x04, Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 }, - // version - new Command { CommandDelegate = UPD_Version, CommandCode = 0x10, + // version + new Command { CommandDelegate = UPD_Version, CommandCode = 0x10, Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 }, - // invalid - new Command { CommandDelegate = UPD_Invalid, CommandCode = 0x00, + // invalid + new Command { CommandDelegate = UPD_Invalid, CommandCode = 0x00, Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 }, }; } diff --git a/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765DriveState.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765DriveState.cs new file mode 100644 index 0000000000..9d51d6da1b --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Hardware/Disk/NECUPD765DriveState.cs @@ -0,0 +1,651 @@ +using System.Linq; + +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase +{ + /// + /// Holds specfic state information about a drive + /// + public abstract class NECUPD765DriveState : IFDDHost + { + #region State + + /// + /// The drive ID from an FDC perspective + /// + public int ID; + + /// + /// Signs whether this drive ready + /// TRUE if both drive exists and has a disk inserted + /// + public bool FLAG_READY + { + get + { + if (!FDD_IsDiskLoaded || Disk.GetTrackCount() == 0 || !FDC.FDD_FLAG_MOTOR) + return false; + else + return true; + } + } + + /// + /// Disk is write protected (TRUE BY DEFAULT) + /// + public bool FLAG_WRITEPROTECT = false; + + /// + /// Storage for seek steps + /// One step for each indexpulse (track index) until seeked track + /// + public int SeekCounter; + + /// + /// Seek status + /// + public int SeekStatus; + + /// + /// Age counter + /// + public int SeekAge; + + /// + /// The current side + /// + public byte CurrentSide; + + /// + /// The current track index in the DiskTracks array + /// + public byte TrackIndex; + + /// + /// The track ID of the current cylinder + /// + public byte CurrentTrackID + { + get + { + // default invalid track + int id = 0xff; + + if (Disk == null) + return (byte)id; + + if (Disk.DiskTracks.Count() == 0) + return (byte)id; + + if (TrackIndex >= Disk.GetTrackCount()) + TrackIndex = 0; + else if (TrackIndex < 0) + TrackIndex = 0; + + var track = Disk.DiskTracks[TrackIndex]; + + id = track.TrackNumber; + + return (byte)id; + } + set + { + for (int i = 0; i < Disk.GetTrackCount(); i++) + { + if (Disk.DiskTracks[i].TrackNumber == value) + { + TrackIndex = (byte)i; + break; + } + } + } + } + + + /// + /// The new track that the drive is seeking to + /// (used in seek operations) + /// + public int SeekingTrack; + + /// + /// The current sector index in the Sectors array + /// + public int SectorIndex; + + /// + /// The currently loaded floppy disk + /// + public FloppyDisk Disk { get; set; } + + /// + /// The parent controller + /// + private INECUPD765 FDC; + + #endregion + + #region Lookups + + /// + /// TRUE if we are on track 0 + /// + public bool FLAG_TRACK0 => TrackIndex == 0; + + #endregion + + #region Public Methods + +#if false + /// + /// Moves the head across the disk cylinders + /// + public void MoveHead(SkipDirection direction, int cylinderCount) + { + // get total tracks + int trackCount = Disk.DiskTracks.Count(); + + int trk = 0; + + switch (direction) + { + case SkipDirection.Increment: + trk = (int)CurrentTrack + cylinderCount; + if (trk >= trackCount) + { + // past the last track + trk = trackCount - 1; + } + else if (trk < 0) + trk = 0; + break; + case SkipDirection.Decrement: + trk = (int)CurrentTrack - cylinderCount; + if (trk < 0) + { + // before the first track + trk = 0; + } + else if (trk >= trackCount) + trk = trackCount - 1; + break; + } + + // move the head + CurrentTrack = (byte)trk; + } +#endif + +#if false + /// + /// Finds a supplied sector + /// + public FloppyDisk.Sector FindSector(ref byte[] resBuffer, CommandParameters prms) + { + int index =CurrentSector; + int lc = 0; + FloppyDisk.Sector sector = null; + + bool found = false; + + do + { + sector = Disk.DiskTracks[CurrentTrack].Sectors[index]; + if (sector != null && sector.SectorID == prms.Sector) + { + // sector found + // check for data errors + if ((sector.Status1 & 0x20) != 0 || (sector.Status2 & 0x20) != 0) + { + // data errors found + } + found = true; + break; + } + + // sector doesnt match + var c = Disk.DiskTracks[CurrentTrack].Sectors[index].TrackNumber; + if (c == 255) + { + // bad cylinder + resBuffer[RS_ST2] |= 0x02; + } + else if (prms.Cylinder != c) + { + // cylinder mismatch + resBuffer[RS_ST2] |= 0x10; + } + + // increment index + index++; + + if (index >= Disk.DiskTracks[CurrentTrack].NumberOfSectors) + { + // out of bounds + index = 0; + lc++; + } + + } while (lc < 2); + + if ((resBuffer[RS_ST2] & 0x02) != 0) + { + // bad cylinder set - remove no cylinder + UnSetBit(SR2_WC, ref resBuffer[RS_ST2]); + } + + // update current sector + CurrentSector = index; + + if (found) + return sector; + else + return null; + } + + + /// + /// Populates a result buffer + /// + public void FillResult(ref byte[] resBuffer, CHRN chrn) + { + // clear results + resBuffer[RS_ST0] = 0; + resBuffer[RS_ST1] = 0; + resBuffer[RS_ST2] = 0; + resBuffer[RS_C] = 0; + resBuffer[RS_H] = 0; + resBuffer[RS_R] = 0; + resBuffer[RS_N] = 0; + + if (chrn == null) + { + // no chrn supplied + resBuffer[RS_ST0] = ST0; + resBuffer[RS_ST1] = 0; + resBuffer[RS_ST2] = 0; + resBuffer[RS_C] = 0; + resBuffer[RS_H] = 0; + resBuffer[RS_R] = 0; + resBuffer[RS_N] = 0; + } + } + + + + /// + /// Populates the result buffer with ReadID data + /// + public void ReadID(ref byte[] resBuffer) + { + if (CheckDriveStatus() == false) + { + // drive not ready + resBuffer[RS_ST0] = ST0; + return; + } + + var track = Disk.DiskTracks.Where(a => a.TrackNumber == CurrentTrack).FirstOrDefault(); + + if (track != null && track.NumberOfSectors > 0) + { + // formatted track + + // get the current sector + int index = CurrentSector; + + // is the index out of bounds? + if (index >= track.NumberOfSectors) + { + // reset the index + index = 0; + } + + // read the sector data + var data = track.Sectors[index]; + resBuffer[RS_C] = data.TrackNumber; + resBuffer[RS_H] = data.SideNumber; + resBuffer[RS_R] = data.SectorID; + resBuffer[RS_N] = data.SectorSize; + + resBuffer[RS_ST0] = ST0; + + // increment the current sector + CurrentSector = index + 1; + return; + } + else + { + // unformatted track? + resBuffer[RS_C] = FDC.CommBuffer[CM_C]; + resBuffer[RS_H] = FDC.CommBuffer[CM_H]; + resBuffer[RS_R] = FDC.CommBuffer[CM_R]; + resBuffer[RS_N] = FDC.CommBuffer[CM_N]; + + SetBit(SR0_IC0, ref ST0); + resBuffer[RS_ST0] = ST0; + resBuffer[RS_ST1] = 0x01; + return; + } + } +#endif + +#if false + /// + /// The drive performs a seek operation if necessary + /// Return value TRUE indicates seek complete + /// + public void DoSeek() + { + if (CurrentState != DriveMainState.Recalibrate && + CurrentState != DriveMainState.Seek) + { + // no seek/recalibrate has been asked for + return; + } + + if (GetBit(ID, FDC.StatusMain)) + { + // drive is already seeking + return; + } + + RunSeekCycle(); + } + + /// + /// Runs a seek cycle + /// + public void RunSeekCycle() + { + for (;;) + { + switch (SeekState) + { + // seek or recalibrate has been requested + case SeekSubState.Idle: + + if (CurrentState == DriveMainState.Recalibrate) + { + // recalibrate always seeks to track 0 + SeekingTrack = 0; + } + SeekState = SeekSubState.MoveInit; + + // mark drive as busy + // this should be cleared by SIS command + SetBit(ID, ref FDC.StatusMain); + + break; + + // setup for the head move + case SeekSubState.MoveInit: + + if (CurrentTrack == SeekingTrack) + { + // we are already at the required track + if (CurrentState == DriveMainState.Recalibrate && + !FLAG_TRACK0) + { + // recalibration fail + SeekIntState = SeekIntStatus.Abnormal; + + // raise seek interrupt + FDC.ActiveInterrupt = InterruptState.Seek; + + // unset DB bit + UnSetBit(ID, ref FDC.StatusMain); + + // equipment check + SetBit(SR0_EC, ref FDC.Status0); + + SeekState = SeekSubState.PerformCompletion; + break; + } + + if (CurrentState == DriveMainState.Recalibrate && + FLAG_TRACK0) + { + // recalibration success + SeekIntState = SeekIntStatus.Normal; + + // raise seek interrupt + FDC.ActiveInterrupt = InterruptState.Seek; + + // unset DB bit + UnSetBit(ID, ref FDC.StatusMain); + + SeekState = SeekSubState.PerformCompletion; + break; + } + } + + // check for error + if (IntStatus >= IC_ABORTED_DISCREMOVED || Disk == null) + { + // drive not ready + FLAG_READY = false; + + // drive not ready + SeekIntState = SeekIntStatus.DriveNotReady; + + // cancel any interrupt + FDC.ActiveInterrupt = InterruptState.None; + + // unset DB bit + UnSetBit(ID, ref FDC.StatusMain); + + SeekState = SeekSubState.PerformCompletion; + break; + } + + if (SeekCounter > 1) + { + // not ready to seek yet + SeekCounter--; + return; + } + + if (FDC.SRT < 1 && CurrentTrack != SeekingTrack) + { + SeekState = SeekSubState.MoveImmediate; + break; + } + + // head move + SeekState = SeekSubState.HeadMove; + + break; + + case SeekSubState.HeadMove: + + // do the seek + SeekCounter = FDC.SRT; + + if (CurrentTrack < SeekingTrack) + { + // we are seeking forward + var delta = SeekingTrack - CurrentTrack; + MoveHead(SkipDirection.Increment, 1); + } + else if (CurrentTrack > SeekingTrack) + { + // we are seeking backward + var delta = CurrentTrack - SeekingTrack; + MoveHead(SkipDirection.Decrement, 1); + } + + // should the seek be completed now? + if (CurrentTrack == SeekingTrack) + { + SeekState = SeekSubState.PerformCompletion; + break; + } + + // seek not finished yet + return; + + // seek emulation processed immediately + case SeekSubState.MoveImmediate: + + if (CurrentTrack < SeekingTrack) + { + // we are seeking forward + var delta = SeekingTrack - CurrentTrack; + MoveHead(SkipDirection.Increment, delta); + + } + else if (CurrentTrack > SeekingTrack) + { + // we are seeking backward + var delta = CurrentTrack - SeekingTrack; + MoveHead(SkipDirection.Decrement, delta); + } + + SeekState = SeekSubState.PerformCompletion; + break; + + case SeekSubState.PerformCompletion: + SeekDone(); + SeekState = SeekSubState.SeekCompleted; + break; + + case SeekSubState.SeekCompleted: + // seek has already completed + return; + } + } + } + + /// + /// Called when a seek operation has completed + /// + public void SeekDone() + { + SeekCounter = 0; + SeekingTrack = CurrentTrack; + + // generate ST0 register data + + // get only the IC bits + IntStatus &= IC_ABORTED_DISCREMOVED; + + // drive ready? + if (!FLAG_READY) + { + SetBit(SR0_NR, ref IntStatus); + SetBit(SR0_EC, ref IntStatus); + + // are we recalibrating? + if (CurrentState == DriveMainState.Recalibrate) + { + SetBit(SR0_EC, ref IntStatus); + } + } + + // set seek end + SetBit(SR0_SE, ref IntStatus); + /* + // head address + if (CurrentSide > 0) + { + SetBit(SR0_HD, ref IntStatus); + + // drive only supports 1 head + // set the EC bit + SetBit(SR0_EC, ref IntStatus); + } + */ + // UnitSelect + SetUnitSelect(ID, ref IntStatus); + + // move to none state + //CurrentState = DriveMainState.None; + + //SeekState = SeekSubState.SeekCompleted; + + // set the seek interrupt flag for this drive + // this will be cleared at the next successful senseint + FLAG_SEEK_INTERRUPT = true; + + //CurrentState = DriveMainState.None; + + } +#endif + + #endregion + + #region Construction + + public NECUPD765DriveState(int driveID, INECUPD765 fdc) + { + ID = driveID; + FDC = fdc; + } + + #endregion + + #region IFDDHost + + /// + /// Parses a new disk image and loads it into this floppy drive + /// + public abstract void FDD_LoadDisk(byte[] diskData); + + /// + /// Ejects the current disk + /// + public void FDD_EjectDisk() + { + Disk = null; + //FLAG_READY = false; + } + + /// + /// Signs whether the current active drive has a disk inserted + /// + public bool FDD_IsDiskLoaded + { + get + { + if (Disk != null) + return true; + else + return false; + } + } + + #endregion + + #region StateSerialization + + public void SyncState(Serializer ser) + { + ser.Sync(nameof(ID), ref ID); + ser.Sync(nameof(FLAG_WRITEPROTECT), ref FLAG_WRITEPROTECT); + //ser.Sync(nameof(FLAG_DISKCHANGED), ref FLAG_DISKCHANGED); + //ser.Sync(nameof(FLAG_RECALIBRATING), ref FLAG_RECALIBRATING); + //ser.Sync(nameof(FLAG_SEEK_INTERRUPT), ref FLAG_SEEK_INTERRUPT); + //ser.Sync(nameof(IntStatus), ref IntStatus); + //ser.Sync(nameof(ST0), ref ST0); + //ser.Sync(nameof(RecalibrationCounter), ref RecalibrationCounter); + ser.Sync(nameof(SeekCounter), ref SeekCounter); + ser.Sync(nameof(SeekStatus), ref SeekStatus); + ser.Sync(nameof(SeekAge), ref SeekAge); + ser.Sync(nameof(CurrentSide), ref CurrentSide); + //ser.Sync(nameof(CurrentTrack), ref CurrentTrack); + ser.Sync(nameof(TrackIndex), ref TrackIndex); + ser.Sync(nameof(SeekingTrack), ref SeekingTrack); + //ser.Sync(nameof(CurrentSector), ref CurrentSector); + ser.Sync(nameof(SectorIndex), ref SectorIndex); + //ser.Sync(nameof(RAngles), ref RAngles); + //ser.Sync(nameof(DataPointer), ref DataPointer); + //ser.SyncEnum(nameof(CurrentState), ref CurrentState); + //ser.SyncEnum(nameof(SeekState), ref SeekState); + //ser.SyncEnum(nameof(SeekIntState), ref SeekIntState); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Media/Disk/DiskType.cs similarity index 90% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Media/Disk/DiskType.cs index d403ec6ffa..76691f17a9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Media/Disk/DiskType.cs @@ -1,5 +1,5 @@  -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// The different disk formats ZXHawk currently supports diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Media/Disk/FloppyDisk.cs similarity index 80% rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/FloppyDisk.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Media/Disk/FloppyDisk.cs index bbafc189bc..295d80ed6b 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Disk/FloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Media/Disk/FloppyDisk.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// This abstract class defines a logical floppy disk @@ -307,7 +307,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC // TrackID is consistent between the sectors although is usually high (233, 237 etc) // SideID is fairly random looking but with all IDs being even // SectorID is also fairly random looking but contains both odd and even numbers - // + // // There doesnt appear to be any CRC errors in this track, but the sector size is always 1 (256 bytes) // Each sector contains different filler byte // Once track 0 is loaded the CPU completely reads all the sectors in this track one-by-one. @@ -402,104 +402,104 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC return false; } - /* - /// - /// Should be run at the end of the ParseDisk process - /// If speedlock is detected the flag is set in the disk image - /// - protected virtual void SpeedlockDetection() - { +#if false + /// + /// Should be run at the end of the ParseDisk process + /// If speedlock is detected the flag is set in the disk image + /// + protected virtual void SpeedlockDetection() + { - if (DiskTracks.Length == 0) - return; + if (DiskTracks.Length == 0) + return; - // check for speedlock copyright notice - string ident = Encoding.ASCII.GetString(DiskData, 0x100, 0x1400); - if (!ident.ToUpper().Contains("SPEEDLOCK")) - { - // speedlock not found - return; - } + // check for speedlock copyright notice + string ident = Encoding.ASCII.GetString(DiskData, 0x100, 0x1400); + if (!ident.ToUpper().Contains("SPEEDLOCK")) + { + // speedlock not found + return; + } - // get cylinder 0 - var cyl = DiskTracks[0]; + // get cylinder 0 + var cyl = DiskTracks[0]; - // get sector with ID=2 - var sec = cyl.Sectors.Where(a => a.SectorID == 2).FirstOrDefault(); + // get sector with ID=2 + var sec = cyl.Sectors.Where(a => a.SectorID == 2).FirstOrDefault(); - if (sec == null) - return; + if (sec == null) + return; - // check for already multiple weak copies - if (sec.ContainsMultipleWeakSectors || sec.SectorData.Length != 0x80 << sec.SectorSize) - return; + // check for already multiple weak copies + if (sec.ContainsMultipleWeakSectors || sec.SectorData.Length != 0x80 << sec.SectorSize) + return; - // check for invalid crcs in sector 2 - if (sec.Status1.Bit(5) || sec.Status2.Bit(5)) - { - Protection = ProtectionType.Speedlock; - } - else - { - return; - } + // check for invalid crcs in sector 2 + if (sec.Status1.Bit(5) || sec.Status2.Bit(5)) + { + Protection = ProtectionType.Speedlock; + } + else + { + return; + } - // we are going to create a total of 5 weak sector copies - // keeping the original copy - byte[] origData = sec.SectorData.ToArray(); - List data = new List(); - //Random rnd = new Random(); + // we are going to create a total of 5 weak sector copies + // keeping the original copy + byte[] origData = sec.SectorData.ToArray(); + List data = new List(); + //Random rnd = new Random(); - for (int i = 0; i < 6; i++) - { - for (int s = 0; s < origData.Length; s++) - { - if (i == 0) - { - data.Add(origData[s]); - continue; - } + for (int i = 0; i < 6; i++) + { + for (int s = 0; s < origData.Length; s++) + { + if (i == 0) + { + data.Add(origData[s]); + continue; + } - // deterministic 'random' implementation - int n = origData[s] + i + 1; - if (n > 0xff) - n = n - 0xff; - else if (n < 0) - n = 0xff + n; + // deterministic 'random' implementation + int n = origData[s] + i + 1; + if (n > 0xff) + n = n - 0xff; + else if (n < 0) + n = 0xff + n; - byte nByte = (byte)n; + byte nByte = (byte)n; - if (s < 336) - { - // non weak data - data.Add(origData[s]); - } - else if (s < 511) - { - // weak data - data.Add(nByte); - } - else if (s == 511) - { - // final sector byte - data.Add(nByte); - } - else - { - // speedlock sector should not be more than 512 bytes - // but in case it is just do non weak - data.Add(origData[i]); - } - } - } + if (s < 336) + { + // non weak data + data.Add(origData[s]); + } + else if (s < 511) + { + // weak data + data.Add(nByte); + } + else if (s == 511) + { + // final sector byte + data.Add(nByte); + } + else + { + // speedlock sector should not be more than 512 bytes + // but in case it is just do non weak + data.Add(origData[i]); + } + } + } - // commit the sector data - sec.SectorData = data.ToArray(); - sec.ContainsMultipleWeakSectors = true; - sec.ActualDataByteLength = data.Count(); + // commit the sector data + sec.SectorData = data.ToArray(); + sec.ContainsMultipleWeakSectors = true; + sec.ActualDataByteLength = data.Count(); - } - */ + } +#endif /// /// Returns the track count for the disk @@ -587,8 +587,18 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC public byte NumberOfSectors { get; set; } public byte GAP3Length { get; set; } public byte FillerByte { get; set; } - public Sector[] Sectors { get; set; } + public virtual Sector[] Sectors { get; set; } + #region UDI + + public byte TrackType { get; set; } + public int TLEN { get; set; } + public int CLEN => TLEN / 8 + (TLEN % 8 / 7) / 8; + public byte[] TrackData { get; set; } + + #endregion + +#if false /// /// Presents a contiguous byte array of all sector data for this track /// (including any multiple weak/random data) @@ -607,6 +617,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC return list.ToArray(); } } +#endif } public class Sector @@ -637,10 +648,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC { return ActualDataByteLength; } - else - { - return ActualDataByteLength / (ActualDataByteLength / (0x80 << SectorSize)); - } + + return ActualDataByteLength / (ActualDataByteLength / (0x80 << SectorSize)); } } @@ -667,10 +676,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC return l.ToArray(); } - else - { - return SectorData; - } + + return SectorData; } else { @@ -688,14 +695,14 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC return res; /* - int copies = ActualDataByteLength / (0x80 << SectorSize); - Random rnd = new Random(); - int r = rnd.Next(0, copies - 1); - int step = r * (0x80 << SectorSize); - byte[] res = new byte[(0x80 << SectorSize)]; - Array.Copy(SectorData, step, res, 0, 0x80 << SectorSize); - return res; - */ + int copies = ActualDataByteLength / (0x80 << SectorSize); + Random rnd = new Random(); + int r = rnd.Next(0, copies - 1); + int step = r * (0x80 << SectorSize); + byte[] res = new byte[(0x80 << SectorSize)]; + Array.Copy(SectorData, step, res, 0, 0x80 << SectorSize); + return res; + */ } } } diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Tape/TapeCommand.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Media/Tape/TapeCommand.cs similarity index 79% rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Tape/TapeCommand.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Media/Tape/TapeCommand.cs index 5af5c32109..d9891206cd 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Tape/TapeCommand.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Media/Tape/TapeCommand.cs @@ -1,5 +1,5 @@  -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// Represents the possible commands that can be raised from each tape block diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Tape/TapeDataBlock.cs b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Media/Tape/TapeDataBlock.cs similarity index 86% rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Tape/TapeDataBlock.cs rename to BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Media/Tape/TapeDataBlock.cs index 75dcc3995c..aea7c494a3 100644 --- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Media/Tape/TapeDataBlock.cs +++ b/BizHawk.Emulation.Cores/Computers/CPCSpectrumBase/Media/Tape/TapeDataBlock.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace BizHawk.Emulation.Cores.Computers.AmstradCPC +namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase { /// /// Represents a tape block @@ -52,31 +52,31 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC set => _blockData = value; } - /* - /// - /// An array of bytearray encoded strings (stored in this format for easy Bizhawk serialization) - /// Its basically tape information - /// - private byte[][] _tapeDescriptionData; +#if false + /// + /// An array of bytearray encoded strings (stored in this format for easy Bizhawk serialization) + /// Its basically tape information + /// + private byte[][] _tapeDescriptionData; - /// - /// Returns the Tape Description Data in a human readable format - /// - public List TapeDescriptionData - { - get - { - List data = new List(); + /// + /// Returns the Tape Description Data in a human readable format + /// + public List TapeDescriptionData + { + get + { + List data = new List(); - foreach (byte[] b in _tapeDescriptionData) - { - data.Add(Encoding.ASCII.GetString(b)); - } + foreach (byte[] b in _tapeDescriptionData) + { + data.Add(Encoding.ASCII.GetString(b)); + } - return data; - } - } - */ + return data; + } + } +#endif #region Block Meta Data diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs deleted file mode 100644 index 2586d05d61..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs +++ /dev/null @@ -1,29 +0,0 @@ - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Defines an object that can load a floppy disk image - /// - public interface IFDDHost - { - /// - /// The currently inserted diskimage - /// - FloppyDisk Disk { get; set; } - - /// - /// Parses a new disk image and loads it into this floppy drive - /// - void FDD_LoadDisk(byte[] diskData); - - /// - /// Ejects the current disk - /// - void FDD_EjectDisk(); - - /// - /// Signs whether the current active drive has a disk inserted - /// - bool FDD_IsDiskLoaded { get; } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IJoystick.cs deleted file mode 100644 index 6ea7acd897..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IJoystick.cs +++ /dev/null @@ -1,34 +0,0 @@ - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Represents a spectrum joystick - /// - public interface IJoystick - { - /// - /// The type of joystick - /// - JoystickType JoyType { get; } - - /// - /// Array of all the possibly button press names - /// - string[] ButtonCollection { get; set; } - - /// - /// The player number that this controller is currently assigned to - /// - int PlayerNumber { get; set; } - - /// - /// Sets the joystick line based on key pressed - /// - void SetJoyInput(string key, bool isPressed); - - /// - /// Gets the state of a particular joystick binding - /// - bool GetJoyInput(string key); - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs index ad1cc4acc2..df93be37a4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs @@ -1,4 +1,5 @@ using BizHawk.Common; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs index cd50120130..bb27454976 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs @@ -1,5 +1,6 @@ using BizHawk.Common; using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs deleted file mode 100644 index 7a24c2d1ba..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs +++ /dev/null @@ -1,19 +0,0 @@ - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Represents a device that utilizes port IN & OUT - /// - public interface IPortIODevice - { - /// - /// Device responds to an IN instruction - /// - bool ReadPort(ushort port, ref int result); - - /// - /// Device responds to an OUT instruction - /// - bool WritePort(ushort port, int result); - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 909df491aa..a85c6c6a43 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; + +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; using BizHawk.Emulation.Cores.Sound; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs deleted file mode 100644 index a3f7bf3fd4..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs +++ /dev/null @@ -1,180 +0,0 @@ - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Used for the sector CHRN structure - /// - public class CHRN - { - /// - /// Track - /// - public byte C { get; set; } - - /// - /// Side - /// - public byte H { get; set; } - - /// - /// Sector ID - /// - public byte R { get; set; } - - /// - /// Sector Size - /// - public byte N { get; set; } - - /// - /// Status register 1 - /// - private byte _flag1; - public byte Flag1 - { - get => _flag1; - set => _flag1 = value; - } - - /// - /// Status register 2 - /// - private byte _flag2; - public byte Flag2 - { - get => _flag2; - set => _flag2 = value; - } - - /// - /// Used to store the last transmitted/received data bytes - /// - public byte[] DataBytes { get; set; } - - /// - /// ID for the read/write data command - /// - public int DataID { get; set; } - - #region Helper Methods - - /// - /// Missing Address Mark (Sector_ID or DAM not found) - /// - public bool ST1MA - { - get => NECUPD765.GetBit(0, _flag1); - set - { - if (value) { NECUPD765.SetBit(0, ref _flag1); } - else { NECUPD765.UnSetBit(0, ref _flag1); } - } - } - - /// - /// No Data (Sector_ID not found, CRC fail in ID_field) - /// - public bool ST1ND - { - get => NECUPD765.GetBit(2, _flag1); - set - { - if (value) { NECUPD765.SetBit(2, ref _flag1); } - else { NECUPD765.UnSetBit(2, ref _flag1); } - } - } - - /// - /// Data Error (CRC-fail in ID- or Data-Field) - /// - public bool ST1DE - { - get => NECUPD765.GetBit(5, _flag1); - set - { - if (value) { NECUPD765.SetBit(5, ref _flag1); } - else { NECUPD765.UnSetBit(5, ref _flag1); } - } - } - - /// - /// End of Track (set past most read/write commands) (see IC) - /// - public bool ST1EN - { - get => NECUPD765.GetBit(7, _flag1); - set - { - if (value) { NECUPD765.SetBit(7, ref _flag1); } - else { NECUPD765.UnSetBit(7, ref _flag1); } - } - } - - /// - /// Missing Address Mark in Data Field (DAM not found) - /// - public bool ST2MD - { - get => NECUPD765.GetBit(0, _flag2); - set - { - if (value) { NECUPD765.SetBit(0, ref _flag2); } - else { NECUPD765.UnSetBit(0, ref _flag2); } - } - } - - /// - /// Bad Cylinder (read/programmed track-ID different and read-ID = FF) - /// - public bool ST2BC - { - get => NECUPD765.GetBit(1, _flag2); - set - { - if (value) { NECUPD765.SetBit(1, ref _flag2); } - else { NECUPD765.UnSetBit(1, ref _flag2); } - } - } - - /// - /// Wrong Cylinder (read/programmed track-ID different) (see b1) - /// - public bool ST2WC - { - get => NECUPD765.GetBit(4, _flag2); - set - { - if (value) { NECUPD765.SetBit(4, ref _flag2); } - else { NECUPD765.UnSetBit(4, ref _flag2); } - } - } - - /// - /// Data Error in Data Field (CRC-fail in data-field) - /// - public bool ST2DD - { - get => NECUPD765.GetBit(5, _flag2); - set - { - if (value) { NECUPD765.SetBit(5, ref _flag2); } - else { NECUPD765.UnSetBit(5, ref _flag2); } - } - } - - /// - /// Control Mark (read/scan command found sector with deleted DAM) - /// - public bool ST2CM - { - get => NECUPD765.GetBit(6, _flag2); - set - { - if (value) { NECUPD765.SetBit(6, ref _flag2); } - else { NECUPD765.UnSetBit(6, ref _flag2); } - } - } - - #endregion - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs deleted file mode 100644 index 351e993631..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs +++ /dev/null @@ -1,826 +0,0 @@ -using BizHawk.Common; -using System; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Definitions - /// - #region Attribution - /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ - #endregion - public partial class NECUPD765 - { - #region Enums - - /// - /// Defines the current phase of the controller - /// - private enum Phase - { - /// - /// FDC is in an idle state, awaiting the next initial command byte - /// - Idle, - - /// - /// FDC is in a state waiting for the next command instruction - /// A command consists of a command byte (eventually including the MF, MK, SK bits), and up to eight parameter bytes - /// - Command, - - /// - /// During this phase, the actual data is transferred (if any). Usually that are the data bytes for the read/written sector(s), except for the Format Track Command, - /// in that case four bytes for each sector are transferred - /// - Execution, - - /// - /// Returns up to seven result bytes (depending on the command) that are containing status information. The Recalibrate and Seek Track commands do not return result bytes directly, - /// instead the program must wait until the Main Status Register signalizes that the command has been completed, and then it must (!) send a - /// Sense Interrupt State command to 'terminate' the Seek/Recalibrate command. - /// - Result - } - - /// - /// The lifecycle of an instruction - /// Similar to phase, this describes the current 'sub-phase' we are in when dealing with an instruction - /// - private enum InstructionState - { - /// - /// FDC has received a command byte and is currently reading parameter bytes from the data bus - /// - ReceivingParameters, - - /// - /// All parameter bytes have been received. This phase allows any neccessary setup before instruction execution starts - /// - PreExecution, - - /// - /// The start of instruction execution. This may end up with the FDC moving into result phase, - /// but also may also prepare the way for further processing to occur later in execution phase - /// - StartExecute, - - /// - /// Data is read or written in execution phase - /// - ExecutionReadWrite, - - /// - /// Execution phase is well under way. This state primarily deals with data transfer between CPU and FDC - /// - ExecutionWrite, - - /// - /// Execution phase is well under way. This state primarily deals with data transfer between FDC and CPU - /// - ExecutionRead, - - /// - /// Execution has finished and results bytes are ready to be read by the CPU - /// Initial result setup - /// - StartResult, - - /// - /// Result processing - /// - ProcessResult, - - /// - /// Results are being sent - /// - SendingResults, - - /// - /// Final cleanup tasks when the instruction has fully completed - /// - Completed - - } - - /// - /// Represents internal interrupt state of the FDC - /// - public enum InterruptState - { - /// - /// There is no interrupt - /// - None, - /// - /// Execution interrupt - /// - Execution, - /// - /// Result interrupt - /// - Result, - /// - /// Ready interrupt - /// - Ready, - /// - /// Seek interrupt - /// - Seek - } - - /// - /// Possible main states that each drive can be in - /// - public enum DriveMainState - { - /// - /// Drive is not doing anything - /// - None, - /// - /// Seek operation is in progress - /// - Seek, - /// - /// Recalibrate operation is in progress - /// - Recalibrate, - /// - /// A scan data operation is in progress - /// - Scan, - /// - /// A read ID operation is in progress - /// - ReadID, - /// - /// A read data operation is in progress - /// - ReadData, - /// - /// A read diagnostic (read track) operation is in progress - /// - ReadDiagnostic, - /// - /// A write id (format track) operation is in progress - /// - WriteID, - /// - /// A write data operation is in progress - /// - WriteData, - } - - /// - /// State information during a seek/recalibration operation - /// - public enum SeekSubState - { - /// - /// Seek hasnt started yet - /// - Idle, - /// - /// Delayed - /// - Wait, - /// - /// Setup for head move - /// - MoveInit, - /// - /// Seek is currently happening - /// - HeadMove, - /// - /// Head move with no delay - /// - MoveImmediate, - /// - /// Ready to complete - /// - PerformCompletion, - /// - /// Seek operation has completed - /// - SeekCompleted - } - - /// - /// Seek int code - /// - public enum SeekIntStatus - { - Normal, - Abnormal, - DriveNotReady, - } - - /// - /// The direction of a specific command - /// - private enum CommandDirection - { - /// - /// Data flows from UPD765A to Z80 - /// - OUT, - /// - /// Data flows from Z80 to UPD765A - /// - IN - } - - /// - /// Enum defining the different types of result that can be returned - /// - private enum ResultType - { - /// - /// Standard 7 result bytes are returned - /// - Standard, - /// - /// 1 byte returned - ST3 - /// (used for SenseDriveStatus) - /// - ST3, - /// - /// 1 byte returned - ST0 - /// (used for version & invalid) - /// - ST0, - /// - /// 2 bytes returned for sense interrupt status command - /// ST0 - /// CurrentCylinder - /// - Interrupt - } - - /// - /// Possible list of encountered drive status errors - /// - public enum Status - { - /// - /// No error detected - /// - None, - /// - /// An undefined error has been detected - /// - Undefined, - /// - /// Drive is not ready - /// - DriveNotReady, - /// - /// Invalid command received - /// - Invalid, - /// - /// The disk has its write protection tab enabled - /// - WriteProtected, - /// - /// The requested sector has not been found - /// - SectorNotFound - } - - /// - /// Represents the direction that the head is moving over the cylinders - /// Increment: Track number increasing (head moving from outside of disk inwards) - /// Decrement: Track number decreasing (head moving from inside of disk outwards) - /// - public enum SkipDirection - { - Increment, - Decrement - } - - #endregion - - #region Constants - - // Command Instruction Constants - // Designates the default postitions within the cmdbuffer array - - public const int CM_HEAD = 0; - /// - /// C - Track - /// - public const int CM_C = 1; - /// - /// H - Side - /// - public const int CM_H = 2; - /// - /// R - Sector ID - /// - public const int CM_R = 3; - /// - /// N - Sector size - /// - public const int CM_N = 4; - /// - /// EOT - End of track - /// - public const int CM_EOT = 5; - /// - /// GPL - Gap length - /// - public const int CM_GPL = 6; - /// - /// DTL - Data length - /// - public const int CM_DTL = 7; - /// - /// STP - Step - /// - public const int CM_STP = 7; - - // Result Instruction Constants - // Designates the default postitions within the cmdbuffer array - - /// - /// Status register 0 - /// - public const int RS_ST0 = 0; - /// - /// Status register 1 - /// - public const int RS_ST1 = 1; - /// - /// Status register 2 - /// - public const int RS_ST2 = 2; - /// - /// C - Track - /// - public const int RS_C = 3; - /// - /// H - Side - /// - public const int RS_H = 4; - /// - /// R - Sector ID - /// - public const int RS_R = 5; - /// - /// N - Sector size - /// - public const int RS_N = 6; - - // Main Status Register Constants - // Designates the bit positions within the Main status register - - /// - /// FDD0 Busy (seek/recalib active, until succesful sense intstat) - /// FDD number 0 is in the seek mode. If any of the DnB bits IS set FDC will not accept read or write command. - /// - public const int MSR_D0B = 0; - /// - /// FDD1 Busy (seek/recalib active, until succesful sense intstat) - /// FDD number 1 is in the seek mode. If any of the DnB bits IS set FDC will not accept read or write command. - /// - public const int MSR_D1B = 1; - /// - /// FDD2 Busy (seek/recalib active, until succesful sense intstat) - /// FDD number 2 is in the seek mode. If any of the DnB bits IS set FDC will not accept read or write command. - /// - public const int MSR_D2B = 2; - /// - /// FDD3 Busy (seek/recalib active, until succesful sense intstat) - /// FDD number 3 is in the seek mode. If any of the DnB bits IS set FDC will not accept read or write command. - /// - public const int MSR_D3B = 3; - /// - /// FDC Busy (still in command-, execution- or result-phase) - /// A Read or Write command is in orocess. (FDC Busy) FDC will not accept any other command - /// - public const int MSR_CB = 4; - /// - /// Execution Mode (still in execution-phase, non_DMA_only) - /// This bit is set only during execution ohase (Execution Mode) in non-DMA mode When DB5 goes low, execution phase has ended and result phase has started.It operates only during - /// non-DMA mode of operation - /// - public const int MSR_EXM = 5; - /// - /// Data Input/Output (0=CPU->FDC, 1=FDC->CPU) (see b7) - /// Indicates direction of data transfer between FDC and data regrster If DIO = 1, then transfer is from data register to the - /// processor.If DIO = 0, then transfer is from the processor to data register - /// - public const int MSR_DIO = 6; - /// - /// Request For Master (1=ready for next byte) (see b6 for direction) - /// ndicates data register IS ready to send or receive data to or from the processor Both bits DIO and RQM should be - /// used to perform the hand-shaking functions of “ready” and “directron” to the processor - /// - public const int MSR_RQM = 7; - - // Status Register 0 Constants - // Designates the bit positions within the status register 0 - - /// - /// Unit Select (driveno during interrupt) - /// This flag IS used to indicate a drive unit number at interrupt - /// - public const int SR0_US0 = 0; - - /// - /// Unit Select (driveno during interrupt) - /// This flag IS used to indicate a drive unit number at interrupt - /// - public const int SR0_US1 = 1; - - /// - /// Head Address (head during interrupt) - /// State of the head at interrupt - /// - public const int SR0_HD = 2; - - /// - /// Not Ready (drive not ready or non-existing 2nd head selected) - /// Not Ready - When the FDD IS in the not-ready state and a Read or Write command IS Issued, this - /// flag IS set If a Read or Write command is issued to side 1 of a single-sided drive, then this flag IS set - /// - public const int SR0_NR = 3; - - /// - /// Equipment Check (drive failure or recalibrate failed (retry)) - /// Equipment check - If a fault srgnal IS received from the FDD, or if the track 0 srgnal fails to occur after 77 - /// step pulses(Recalibrate Command) then this flag is set - /// - public const int SR0_EC = 4; - - /// - /// Seek End (Set if seek-command completed) - /// Seek end - When the FDC completes the Seek command, this flag IS set lo 1 (high) - /// - public const int SR0_SE = 5; - - /// - /// Interrupt Code (low byte) - /// Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd - /// or senseint with no int occured, 3=aborted:disc removed etc.) - /// - public const int SR0_IC0 = 6; - - /// - /// Interrupt Code (high byte) - /// Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd - /// or senseint with no int occured, 3=aborted:disc removed etc.) - /// - public const int SR0_IC1 = 7; - - // Status Register 1 Constants - // Designates the bit positions within the status register 1 - - /// - /// Missing Address Mark (Sector_ID or DAM not found) - /// Missing address mark - This bit is set i f the FDC does not detect the IDAM before 2 index pulses It is also set if - /// the FDC cannot find the DAM or DDAM after the IDAM i s found.MD bit of ST2 is also set at this time - /// - public const int SR1_MA = 0; - - /// - /// Not Writeable (tried to write/format disc with wprot_tab=on) - /// Not writable (write protect) - During execution of Write Data, Write Deleted Data or Write ID command. if the FDC - /// detect: a write protect srgnal from the FDD.then this flag is Set - /// - public const int SR1_NW = 1; - - /// - /// No Data - /// No Data (Sector_ID not found, CRC fail in ID_field) - /// - /// During execution of Read Data. Read Deleted Data Write Data.Write Deleted Data or Scan command, if the FDC cannot find - /// the sector specified in the IDR(2)Register, this flag i s set. - /// - /// During execution of the Read ID command. if the FDC cannot read the ID field without an error, then this flag IS set - /// - /// During execution of the Read Diagnostic command. if the starting sector cannot be found, then this flag is set - /// - public const int SR1_ND = 2; - - /// - /// Over Run (CPU too slow in execution-phase (ca. 26us/Byte)) - /// Overrun - If the FDC i s not serviced by the host system during data transfers within a certain time interval.this flaa i s set - /// - public const int SR1_OR = 4; - - /// - /// Data Error (CRC-fail in ID- or Data-Field) - /// Data error - When the FDC detects a CRC(1) error in either the ID field or the data field, this flag is set - /// - public const int SR1_DE = 5; - - /// - /// End of Track (set past most read/write commands) (see IC) - /// End of cylinder - When the FDC tries to access a sector beyond the final sector of a cylinder, this flag I S set - /// - public const int SR1_EN = 7; - - // Status Register 2 Constants - // Designates the bit positions within the status register 2 - - /// - /// Missing Address Mark in Data Field (DAM not found) - /// Missing address mark - When data IS read from the medium, i f the FDC cannot find a data address mark or deleted - /// data address mark, then this flag is set - /// - public const int SR2_MD = 0; - - /// - /// Bad Cylinder (read/programmed track-ID different and read-ID = FF) - /// Bad cylinder - This bit i s related to the ND bit. and when the contents of C on the medium is different - /// from that stored i n the IDR and the contents of C IS FFH.then this flag IS set - /// - public const int SR2_BC = 1; - - /// - /// Scan Not Satisfied (no fitting sector found) - /// Scan not satisfied - During execution of the Scan command, i f the F D cannot find a sector on the cylinder - /// which meets the condition.then this flag i s set - /// - public const int SR2_SN = 2; - - /// - /// Scan Equal Hit (equal) - /// Scan equal hit - During execution of the Scan command. i f the condition of “equal” is satisfied, this flag i s set - /// - public const int SR2_SH = 3; - - /// - /// Wrong Cylinder (read/programmed track-ID different) (see b1) - /// Wrong cylinder - This bit IS related to the ND bit, and when the contents of C(3) on the medium is different - /// from that stored i n the IDR.this flag is set - /// - public const int SR2_WC = 4; - - /// - /// Data Error in Data Field (CRC-fail in data-field) - /// Data error in data field - If the FDC detects a CRC error i n the data field then this flag is set - /// - public const int SR2_DD = 5; - - /// - /// Control Mark (read/scan command found sector with deleted DAM) - /// Control mark - During execution of the Read Data or Scan command, if the FDC encounters a sector - /// which contains a deleted data address mark, this flag is set Also set if DAM is - /// found during Read Deleted Data - /// - public const int SR2_CM = 6; - - // Status Register 3 Constants - // Designates the bit positions within the status register 3 - - /// - /// Unit select 0 - /// Unit Select (pin 28,29 of FDC) - /// - public const int SR3_US0 = 0; - - /// - /// Unit select 1 - /// Unit Select (pin 28,29 of FDC) - /// - public const int SR3_US1 = 1; - - /// - /// Head address (side select) - /// Head Address (pin 27 of FDC) - /// - public const int SR3_HD = 2; - - /// - /// Two Side (0=yes, 1=no (!)) - /// Two-side - This bit IS used to indicate the status of the two-side signal from the FDD - /// - public const int SR3_TS = 3; - - /// - /// Track 0 (on track 0 we are) - /// Track 0 - This bit IS used to indicate the status of the track 0 signal from the FDD - /// - public const int SR3_T0 = 4; - - /// - /// Ready - status of the ready signal from the fdd - /// Ready (drive ready signal) - /// - public const int SR3_RY = 5; - - /// - /// Write Protected (write protected) - /// Write protect - status of the wp signal from the fdd - /// - public const int SR3_WP = 6; - - /// - /// Fault - This bit is used to indicate the status of the fault signal from the FDD - /// Fault (if supported: 1=Drive failure) - /// - public const int SR3_FT = 7; - - // Interrupt Code Masks - - /// - /// 1 = aborted:readfail / OK if EN (end of track) - /// - public const byte IC_OK = 0x00; - - /// - /// 1 = aborted:readfail / OK if EN (end of track) - /// - public const byte IC_ABORTED_RF_OKEN = 0x40; - - /// - /// 2 = unknown cmd or senseint with no int occured - /// - public const byte IC_NO_INT_OCCURED = 0x80; - - /// - /// 3 = aborted:disc removed etc - /// - public const byte IC_ABORTED_DISCREMOVED = 0xC0; - - // command code constants - public const int CC_READ_DATA = 0x06; - public const int CC_READ_ID = 0x0a; - public const int CC_SPECIFY = 0x03; - public const int CC_READ_DIAGNOSTIC = 0x02; - public const int CC_SCAN_EQUAL = 0x11; - public const int CC_SCAN_HIGHOREQUAL = 0x1d; - public const int CC_SCAN_LOWOREQUAL = 0x19; - public const int CC_READ_DELETEDDATA = 0x0c; - public const int CC_WRITE_DATA = 0x05; - public const int CC_WRITE_ID = 0x0d; - public const int CC_WRITE_DELETEDDATA = 0x09; - public const int CC_SEEK = 0x0f; - public const int CC_RECALIBRATE = 0x07; - public const int CC_SENSE_INTSTATUS = 0x08; - public const int CC_SENSE_DRIVESTATUS = 0x04; - public const int CC_VERSION = 0x10; - public const int CC_INVALID = 0x00; - - // drive seek state constants - public const int SEEK_IDLE = 0; - public const int SEEK_SEEK = 1; - public const int SEEK_RECALIBRATE = 2; - // seek interrupt - public const int SEEK_INTACKNOWLEDGED = 3; - public const int SEEK_NORMALTERM = 4; - public const int SEEK_ABNORMALTERM = 5; - public const int SEEK_DRIVENOTREADY = 6; - - #endregion - - #region Classes & Structs - - /// - /// Class that holds information about a specific command - /// - private class Command - { -// /// -// /// Mask to remove potential parameter bits (5,6, and or 7) in order to identify the command -// /// -// public int BitMask { get; set; } - /// - /// The command code after bitmask has been applied - /// - public int CommandCode { get; set; } - /// - /// The number of bytes that make up the full command - /// - public int ParameterByteCount { get; set; } - /// - /// The number of result bytes that will be generated from the command - /// - public int ResultByteCount { get; set; } - /// - /// The command direction - /// IN - Z80 to UPD765A - /// OUT - UPD765A to Z80 - /// - public CommandDirection Direction { get; set; } - /// - /// Command makes use of the MT bit - /// - public bool MT; - /// - /// Command makes use of the MF bit - /// - public bool MF; - /// - /// Command makes use of the SK bit - /// - public bool SK; - /// - /// Read/Write command that is READ - /// - public bool IsRead; - /// - /// Read/Write command that is WRITE - /// - public bool IsWrite; - - /// - /// Delegate function that is called by this command - /// bool 1: EXECUTE - if TRUE the command will be executed. if FALSE the method will instead parse commmand parameter bytes - /// bool 2: RESULT - if TRUE - /// - public Action CommandDelegate { get; set; } - } - - /// - /// Storage for command parameters - /// - public class CommandParameters - { - /// - /// The requested drive - /// - public byte UnitSelect; - /// - /// The requested physical side - /// - public byte Side; - /// - /// The requested track (C) - /// - public byte Cylinder; - /// - /// The requested head (H) - /// - public byte Head; - /// - /// The requested sector (R) - /// - public byte Sector; - /// - /// The specified sector size (N) - /// - public byte SectorSize; - /// - /// The end of track or last sector value (EOT) - /// - public byte EOT; - /// - /// Gap3 length (GPL) - /// - public byte Gap3Length; - /// - /// Data length (DTL) - When N is defined as 00, DTL stands for the data length - /// which users are going to read out or write into the sector - /// - public byte DTL; - - /// - /// Clear down - /// - public void Reset() - { - UnitSelect = 0; - Side = 0; - Cylinder = 0; - Head = 0; - Sector = 0; - SectorSize = 0; - EOT = 0; - Gap3Length = 0; - DTL = 0; - } - - public void SyncState(Serializer ser) - { - ser.BeginSection("ActiveCmdParams"); - - ser.Sync(nameof(UnitSelect), ref UnitSelect); - ser.Sync(nameof(Side), ref Side); - ser.Sync(nameof(Cylinder), ref Cylinder); - ser.Sync(nameof(Head), ref Head); - ser.Sync(nameof(Sector), ref Sector); - ser.Sync(nameof(SectorSize), ref SectorSize); - ser.Sync(nameof(EOT), ref EOT); - ser.Sync(nameof(Gap3Length), ref Gap3Length); - ser.Sync(nameof(DTL), ref DTL); - - ser.EndSection(); - } - } - - - #endregion - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs deleted file mode 100644 index 094e3eab19..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ /dev/null @@ -1,2840 +0,0 @@ -using BizHawk.Common.NumberExtensions; -using System.Collections; -using System.Collections.Generic; -using System.Linq; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// FDC State and Methods - /// - #region Attribution - /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ - #endregion - public partial class NECUPD765 - { - #region Controller State - - /// - /// Signs whether the drive is active - /// - public bool DriveLight; - - /// - /// Collection of possible commands - /// - private List CommandList; - - /// - /// State parameters relating to the Active command - /// - public CommandParameters ActiveCommandParams = new CommandParameters(); - - /// - /// The current active phase of the controller - /// - private Phase ActivePhase = Phase.Command; - - /// - /// The currently active interrupt - /// - private InterruptState ActiveInterrupt = InterruptState.None; - /// - /// Command buffer - /// This does not contain the initial command byte (only parameter bytes) - /// - private byte[] CommBuffer = new byte[9]; - - /// - /// Current index within the command buffer - /// - private int CommCounter = 0; - - /// - /// Initial command byte flag - /// Bit7 Multi Track (continue multi-sector-function on other head) - /// - private bool CMD_FLAG_MT; - - /// - /// Initial command byte flag - /// Bit6 MFM-Mode-Bit (Default 1=Double Density) - /// - private bool CMD_FLAG_MF; - - /// - /// Initial command byte flag - /// Bit5 Skip-Bit (set if secs with deleted DAM shall be skipped) - /// - private bool CMD_FLAG_SK; - - /// - /// Step Rate Time (supplied via the specify command) - /// SRT stands for the steooino rate for the FDD ( 1 to 16 ms in 1 ms increments). - /// Stepping rate applies to all drives(FH= 1ms, EH= 2ms, etc.). - /// - private int SRT; - - /// - /// Keeps track of the current SRT state - /// - private int SRT_Counter; - - /// - /// Head Unload Time (supplied via the specify command) - /// HUT stands for the head unload time after a Read or Write operation has occurred - /// (16 to 240 ms in 16 ms Increments) - /// - private int HUT; - - /// - /// Keeps track of the current HUT state - /// - private int HUT_Counter; - - /// - /// Head load Time (supplied via the specify command) - /// HLT stands for the head load time in the FDD (2 to 254 ms in 2 ms Increments) - /// - private int HLT; - - /// - /// Keeps track of the current HLT state - /// - private int HLT_Counter; - - /// - /// Non-DMA Mode (supplied via the specify command) - /// ND stands for operation in the non-DMA mode - /// - private bool ND; - - /// - /// In lieu of actual timing, this will count status reads in execution phase - /// where the CPU hasnt actually read any bytes - /// - private int OverrunCounter; - - /// - /// Contains result bytes in result phase - /// - private byte[] ResBuffer = new byte[7]; - - /// - /// Contains sector data to be written/read in execution phase - /// - private byte[] ExecBuffer = new byte[0x8000]; - - /// - /// Interrupt result buffer - /// Persists (and returns when needed) the last result data when a sense interrupt status command happens - /// - private byte[] InterruptResultBuffer = new byte[2]; - - /// - /// Current index within the result buffer - /// - private int ResCounter = 0; - - /// - /// The byte length of the currently active command - /// This may or may not be the same as the actual command resultbytes value - /// - private int ResLength = 0; - - /// - /// Index for sector data within the result buffer - /// - private int ExecCounter = 0; - - /// - /// The length of the current exec command - /// - private int ExecLength = 0; - - /// - /// The last write byte that was received during execution phase - /// - private byte LastSectorDataWriteByte = 0; - - /// - /// The last read byte to be sent during execution phase - /// - private byte LastSectorDataReadByte = 0; - - /// - /// The last parameter byte that was written to the FDC - /// - private byte LastByteReceived = 0; - - /// - /// Delay for reading sector - /// - private int SectorDelayCounter = 0; - - /// - /// The phyical sector ID - /// - private int SectorID = 0; - - /// - /// Counter for index pulses - /// - private int IndexPulseCounter; - - /// - /// Specifies the index of the currently selected command (in the CommandList) - /// - public int CMDIndex - { - get => _cmdIndex; - set - { - _cmdIndex = value; - ActiveCommand = CommandList[_cmdIndex]; - } - } - private int _cmdIndex; - - /// - /// The currently active command - /// - private Command ActiveCommand; - - /// - /// Main status register (accessed via reads to port 0x2ffd) - /// - /* - b0..3 DB FDD0..3 Busy (seek/recalib active, until succesful sense intstat) - b4 CB FDC Busy (still in command-, execution- or result-phase) - b5 EXM Execution Mode (still in execution-phase, non_DMA_only) - b6 DIO Data Input/Output (0=CPU->FDC, 1=FDC->CPU) (see b7) - b7 RQM Request For Master (1=ready for next byte) (see b6 for direction) - */ - private byte StatusMain; - - /// - /// Status Register 0 - /// - /* - b0,1 US Unit Select (driveno during interrupt) - b2 HD Head Address (head during interrupt) - b3 NR Not Ready (drive not ready or non-existing 2nd head selected) - b4 EC Equipment Check (drive failure or recalibrate failed (retry)) - b5 SE Seek End (Set if seek-command completed) - b6,7 IC Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd - or senseint with no int occured, 3=aborted:disc removed etc.) - */ - private byte Status0; - - /// - /// Status Register 1 - /// - /* - b0 MA Missing Address Mark (Sector_ID or DAM not found) - b1 NW Not Writeable (tried to write/format disc with wprot_tab=on) - b2 ND No Data (Sector_ID not found, CRC fail in ID_field) - b3,6 0 Not used - b4 OR Over Run (CPU too slow in execution-phase (ca. 26us/Byte)) - b5 DE Data Error (CRC-fail in ID- or Data-Field) - b7 EN End of Track (set past most read/write commands) (see IC) - */ - private byte Status1; - - /// - /// Status Register 2 - /// - /* - b0 MD Missing Address Mark in Data Field (DAM not found) - b1 BC Bad Cylinder (read/programmed track-ID different and read-ID = FF) - b2 SN Scan Not Satisfied (no fitting sector found) - b3 SH Scan Equal Hit (equal) - b4 WC Wrong Cylinder (read/programmed track-ID different) (see b1) - b5 DD Data Error in Data Field (CRC-fail in data-field) - b6 CM Control Mark (read/scan command found sector with deleted DAM) - b7 0 Not Used - */ - private byte Status2; - - /// - /// Status Register 3 - /// - /* - b0,1 US Unit Select (pin 28,29 of FDC) - b2 HD Head Address (pin 27 of FDC) - b3 TS Two Side (0=yes, 1=no (!)) - b4 T0 Track 0 (on track 0 we are) - b5 RY Ready (drive ready signal) - b6 WP Write Protected (write protected) - b7 FT Fault (if supported: 1=Drive failure) - */ - private byte Status3; - - #endregion - - #region UPD Internal Functions - - #region READ Commands - - /// - /// Read Data - /// COMMAND: 8 parameter bytes - /// EXECUTION: Data transfer between FDD and FDC - /// RESULT: 7 result bytes - /// - private void UPD_ReadData() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - setup for execution phase - - // clear exec buffer and status registers - ClearExecBuffer(); - Status0 = 0; - Status1 = 0; - Status2 = 0; - Status3 = 0; - - // temp sector index - byte secIdx = ActiveCommandParams.Sector; - - // hack for when another drive (non-existent) is being called - if (ActiveDrive.ID != 0) - DiskDriveIndex = 0; - - // do we have a valid disk inserted? - if (!ActiveDrive.FLAG_READY) - { - // no disk, no tracks or motor is not on - SetBit(SR0_IC0, ref Status0); - SetBit(SR0_NR, ref Status0); - - CommitResultCHRN(); - CommitResultStatus(); - //ResBuffer[RS_ST0] = Status0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - - int buffPos = 0; - int sectorSize = 0; - int maxTransferCap = 0; - - // calculate requested size of data required - if (ActiveCommandParams.SectorSize == 0) - { - // When N=0, then DTL defines the data length which the FDC must treat as a sector. If DTL is smaller than the actual - // data length in a sector, the data beyond DTL in the sector is not sent to the Data Bus. The FDC reads (internally) - // the complete sector performing the CRC check and, depending upon the manner of command termination, may perform - // a Multi-Sector Read Operation. - sectorSize = ActiveCommandParams.DTL; - - // calculate maximum transfer capacity - if (!CMD_FLAG_MF) - maxTransferCap = 3328; - - if (maxTransferCap == 0) { } - } - else - { - // When N is non - zero, then DTL has no meaning and should be set to ffh - ActiveCommandParams.DTL = 0xFF; - - // calculate maximum transfer capacity - switch (ActiveCommandParams.SectorSize) - { - case 1: - if (CMD_FLAG_MF) - maxTransferCap = 6656; - else - maxTransferCap = 3840; - break; - case 2: - if (CMD_FLAG_MF) - maxTransferCap = 7680; - else - maxTransferCap = 4096; - break; - case 3: - if (CMD_FLAG_MF) - maxTransferCap = 8192; - else - maxTransferCap = 4096; - break; - } - - sectorSize = 0x80 << ActiveCommandParams.SectorSize; - } - - // get the current track - var track = ActiveDrive.Disk.DiskTracks.FirstOrDefault(a => a.TrackNumber == ActiveDrive.CurrentTrackID); - - if (track == null || track.NumberOfSectors <= 0) - { - // track could not be found - SetBit(SR0_IC0, ref Status0); - SetBit(SR0_NR, ref Status0); - - CommitResultCHRN(); - CommitResultStatus(); - - //ResBuffer[RS_ST0] = Status0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - - FloppyDisk.Sector sector = null; - - // sector read loop - for (; ; ) - { - bool terminate = false; - - // lookup the sector - sector = GetSector(); - - if (sector == null) - { - // sector was not found after two passes of the disk index hole - SetBit(SR1_ND, ref Status1); - SetBit(SR0_IC0, ref Status0); - UnSetBit(SR0_IC1, ref Status0); - - // result requires the actual track id, rather than the sector track id - ActiveCommandParams.Cylinder = track.TrackNumber; - - CommitResultCHRN(); - CommitResultStatus(); - ActivePhase = Phase.Result; - break; - } - - // sector ID was found on this track - - // get status regs from sector - Status1 = sector.Status1; - Status2 = sector.Status2; - - // we don't need EN - UnSetBit(SR1_EN, ref Status1); - - // If SK=1, the FDC skips the sector with the Deleted Data Address Mark and reads the next sector. - // The CRC bits in the deleted data field are not checked when SK=1 - if (CMD_FLAG_SK && Status2.Bit(SR2_CM)) - { - if (ActiveCommandParams.Sector != ActiveCommandParams.EOT) - { - // increment the sector ID and search again - ActiveCommandParams.Sector++; - continue; - } - else - { - // no execution phase - SetBit(SR0_IC0, ref Status0); - UnSetBit(SR0_IC1, ref Status0); - - // result requires the actual track id, rather than the sector track id - ActiveCommandParams.Cylinder = track.TrackNumber; - - CommitResultCHRN(); - CommitResultStatus(); - ActivePhase = Phase.Result; - break; - } - } - - // read the sector - for (int i = 0; i < sector.DataLen; i++) - { - ExecBuffer[buffPos++] = sector.ActualData[i]; - } - - // mark the sector read - sector.SectorReadCompleted(); - - // any CRC errors? - if (Status1.Bit(SR1_DE) || Status2.Bit(SR2_DD)) - { - SetBit(SR0_IC0, ref Status0); - UnSetBit(SR0_IC1, ref Status0); - terminate = true; - } - - if (!CMD_FLAG_SK && Status2.Bit(SR2_CM)) - { - // deleted address mark was detected with NO skip flag set - ActiveCommandParams.EOT = ActiveCommandParams.Sector; - SetBit(SR2_CM, ref Status2); - SetBit(SR0_IC0, ref Status0); - UnSetBit(SR0_IC1, ref Status0); - terminate = true; - } - - if (sector.SectorID == ActiveCommandParams.EOT || terminate) - { - // this was the last sector to read - // or termination requested - - SetBit(SR1_EN, ref Status1); - - int keyIndex = 0; - for (int i = 0; i < track.Sectors.Length; i++) - { - if (track.Sectors[i].SectorID == sector.SectorID) - { - keyIndex = i; - break; - } - } - - if (keyIndex == track.Sectors.Length - 1) - { - // last sector on the cylinder, set EN - SetBit(SR1_EN, ref Status1); - - // increment cylinder - ActiveCommandParams.Cylinder++; - - // reset sector - ActiveCommandParams.Sector = sector.SectorID; // 1; - ActiveDrive.SectorIndex = 0; - } - else - { - ActiveDrive.SectorIndex++; - } - - UnSetBit(SR0_IC1, ref Status0); - if (terminate) - SetBit(SR0_IC0, ref Status0); - else - UnSetBit(SR0_IC0, ref Status0); - - SetBit(SR0_IC0, ref Status0); - - // result requires the actual track id, rather than the sector track id - ActiveCommandParams.Cylinder = track.TrackNumber; - - CommitResultCHRN(); - CommitResultStatus(); - ActivePhase = Phase.Execution; - break; - } - else - { - // continue with multi-sector read operation - ActiveCommandParams.Sector++; - //ActiveDrive.SectorIndex++; - } - } - - if (ActivePhase == Phase.Execution) - { - ExecLength = buffPos; - ExecCounter = buffPos; - - DriveLight = true; - } - } - - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - - var index = ExecLength - ExecCounter; - - LastSectorDataReadByte = ExecBuffer[index]; - - OverrunCounter--; - ExecCounter--; - - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - /// - /// Read Deleted Data - /// COMMAND: 8 parameter bytes - /// EXECUTION: Data transfer between the FDD and FDC - /// RESULT: 7 result bytes - /// - private void UPD_ReadDeletedData() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - setup for execution phase - - // clear exec buffer and status registers - ClearExecBuffer(); - Status0 = 0; - Status1 = 0; - Status2 = 0; - Status3 = 0; - - // temp sector index - byte secIdx = ActiveCommandParams.Sector; - - // do we have a valid disk inserted? - if (!ActiveDrive.FLAG_READY) - { - // no disk, no tracks or motor is not on - SetBit(SR0_IC0, ref Status0); - SetBit(SR0_NR, ref Status0); - - CommitResultCHRN(); - CommitResultStatus(); - //ResBuffer[RS_ST0] = Status0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - - int buffPos = 0; - int sectorSize = 0; - int maxTransferCap = 0; - if (maxTransferCap > 0) { } - - // calculate requested size of data required - if (ActiveCommandParams.SectorSize == 0) - { - // When N=0, then DTL defines the data length which the FDC must treat as a sector. If DTL is smaller than the actual - // data length in a sector, the data beyond DTL in the sector is not sent to the Data Bus. The FDC reads (internally) - // the complete sector performing the CRC check and, depending upon the manner of command termination, may perform - // a Multi-Sector Read Operation. - sectorSize = ActiveCommandParams.DTL; - - // calculate maximum transfer capacity - if (!CMD_FLAG_MF) - maxTransferCap = 3328; - } - else - { - // When N is non - zero, then DTL has no meaning and should be set to ffh - ActiveCommandParams.DTL = 0xFF; - - // calculate maximum transfer capacity - switch (ActiveCommandParams.SectorSize) - { - case 1: - if (CMD_FLAG_MF) - maxTransferCap = 6656; - else - maxTransferCap = 3840; - break; - case 2: - if (CMD_FLAG_MF) - maxTransferCap = 7680; - else - maxTransferCap = 4096; - break; - case 3: - if (CMD_FLAG_MF) - maxTransferCap = 8192; - else - maxTransferCap = 4096; - break; - } - - sectorSize = 0x80 << ActiveCommandParams.SectorSize; - } - - // get the current track - var track = ActiveDrive.Disk.DiskTracks.FirstOrDefault(a => a.TrackNumber == ActiveDrive.CurrentTrackID); - - if (track == null || track.NumberOfSectors <= 0) - { - // track could not be found - SetBit(SR0_IC0, ref Status0); - SetBit(SR0_NR, ref Status0); - - CommitResultCHRN(); - CommitResultStatus(); - - //ResBuffer[RS_ST0] = Status0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - - FloppyDisk.Sector sector = null; - - // sector read loop - for (; ; ) - { - bool terminate = false; - - // lookup the sector - sector = GetSector(); - - if (sector == null) - { - // sector was not found after two passes of the disk index hole - SetBit(SR1_ND, ref Status1); - SetBit(SR0_IC0, ref Status0); - UnSetBit(SR0_IC1, ref Status0); - - // result requires the actual track id, rather than the sector track id - ActiveCommandParams.Cylinder = track.TrackNumber; - - CommitResultCHRN(); - CommitResultStatus(); - ActivePhase = Phase.Result; - break; - } - - // sector ID was found on this track - - // get status regs from sector - Status1 = sector.Status1; - Status2 = sector.Status2; - - // we don't need EN - UnSetBit(SR1_EN, ref Status1); - - // invert CM for read deleted data command - if (Status2.Bit(SR2_CM)) - UnSetBit(SR2_CM, ref Status2); - else - SetBit(SR2_CM, ref Status2); - - // skip flag is set and no DAM found - if (CMD_FLAG_SK && Status2.Bit(SR2_CM)) - { - if (ActiveCommandParams.Sector != ActiveCommandParams.EOT) - { - // increment the sector ID and search again - ActiveCommandParams.Sector++; - continue; - } - else - { - // no execution phase - SetBit(SR0_IC0, ref Status0); - UnSetBit(SR0_IC1, ref Status0); - - // result requires the actual track id, rather than the sector track id - ActiveCommandParams.Cylinder = track.TrackNumber; - - CommitResultCHRN(); - CommitResultStatus(); - ActivePhase = Phase.Result; - break; - } - } - // we can read this sector - else - { - // if DAM is not set this will be the last sector to read - if (Status2.Bit(SR2_CM)) - { - ActiveCommandParams.EOT = ActiveCommandParams.Sector; - } - - if (!CMD_FLAG_SK && !Status2.Bit(SR2_CM) && - ActiveDrive.Disk.Protection == ProtectionType.PaulOwens) - { - ActiveCommandParams.EOT = ActiveCommandParams.Sector; - SetBit(SR2_CM, ref Status2); - SetBit(SR0_IC0, ref Status0); - UnSetBit(SR0_IC1, ref Status0); - terminate = true; - } - - // read the sector - for (int i = 0; i < sectorSize; i++) - { - ExecBuffer[buffPos++] = sector.ActualData[i]; - } - - // mark the sector read - sector.SectorReadCompleted(); - - if (sector.SectorID == ActiveCommandParams.EOT) - { - // this was the last sector to read - - SetBit(SR1_EN, ref Status1); - - int keyIndex = 0; - for (int i = 0; i < track.Sectors.Length; i++) - { - if (track.Sectors[i].SectorID == sector.SectorID) - { - keyIndex = i; - break; - } - } - - if (keyIndex == track.Sectors.Length - 1) - { - // last sector on the cylinder, set EN - SetBit(SR1_EN, ref Status1); - - // increment cylinder - ActiveCommandParams.Cylinder++; - - // reset sector - ActiveCommandParams.Sector = 1; - ActiveDrive.SectorIndex = 0; - } - else - { - ActiveDrive.SectorIndex++; - } - - UnSetBit(SR0_IC1, ref Status0); - if (terminate) - SetBit(SR0_IC0, ref Status0); - else - UnSetBit(SR0_IC0, ref Status0); - - SetBit(SR0_IC0, ref Status0); - - // result requires the actual track id, rather than the sector track id - ActiveCommandParams.Cylinder = track.TrackNumber; - - // remove CM (appears to be required to defeat Alkatraz copy protection) - UnSetBit(SR2_CM, ref Status2); - - CommitResultCHRN(); - CommitResultStatus(); - ActivePhase = Phase.Execution; - break; - } - else - { - // continue with multi-sector read operation - ActiveCommandParams.Sector++; - //ActiveDrive.SectorIndex++; - } - } - } - - if (ActivePhase == Phase.Execution) - { - ExecLength = buffPos; - ExecCounter = buffPos; - DriveLight = true; - } - } - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - var index = ExecLength - ExecCounter; - - LastSectorDataReadByte = ExecBuffer[index]; - - OverrunCounter--; - ExecCounter--; - - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - /// - /// Read Diagnostic (read track) - /// COMMAND: 8 parameter bytes - /// EXECUTION: Data transfer between FDD and FDC. FDC reads all data fields from index hole to EDT - /// RESULT: 7 result bytes - /// - private void UPD_ReadDiagnostic() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - setup for execution phase - - // clear exec buffer and status registers - ClearExecBuffer(); - Status0 = 0; - Status1 = 0; - Status2 = 0; - Status3 = 0; - - // temp sector index - byte secIdx = ActiveCommandParams.Sector; - - // do we have a valid disk inserted? - if (!ActiveDrive.FLAG_READY) - { - // no disk, no tracks or motor is not on - SetBit(SR0_IC0, ref Status0); - SetBit(SR0_NR, ref Status0); - - CommitResultCHRN(); - CommitResultStatus(); - //ResBuffer[RS_ST0] = Status0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - - int buffPos = 0; - int sectorSize = 0; - int maxTransferCap = 0; - if (maxTransferCap > 0) { } - - // calculate requested size of data required - if (ActiveCommandParams.SectorSize == 0) - { - // When N=0, then DTL defines the data length which the FDC must treat as a sector. If DTL is smaller than the actual - // data length in a sector, the data beyond DTL in the sector is not sent to the Data Bus. The FDC reads (internally) - // the complete sector performing the CRC check and, depending upon the manner of command termination, may perform - // a Multi-Sector Read Operation. - sectorSize = ActiveCommandParams.DTL; - - // calculate maximum transfer capacity - if (!CMD_FLAG_MF) - maxTransferCap = 3328; - } - else - { - // When N is non - zero, then DTL has no meaning and should be set to ffh - ActiveCommandParams.DTL = 0xFF; - - // calculate maximum transfer capacity - switch (ActiveCommandParams.SectorSize) - { - case 1: - if (CMD_FLAG_MF) - maxTransferCap = 6656; - else - maxTransferCap = 3840; - break; - case 2: - if (CMD_FLAG_MF) - maxTransferCap = 7680; - else - maxTransferCap = 4096; - break; - case 3: - if (CMD_FLAG_MF) - maxTransferCap = 8192; - else - maxTransferCap = 4096; - break; - } - - sectorSize = 0x80 << ActiveCommandParams.SectorSize; - } - - // get the current track - var track = ActiveDrive.Disk.DiskTracks.FirstOrDefault(a => a.TrackNumber == ActiveDrive.CurrentTrackID); - - if (track == null || track.NumberOfSectors <= 0) - { - // track could not be found - SetBit(SR0_IC0, ref Status0); - SetBit(SR0_NR, ref Status0); - - CommitResultCHRN(); - CommitResultStatus(); - - //ResBuffer[RS_ST0] = Status0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - - //FloppyDisk.Sector sector = null; - ActiveDrive.SectorIndex = 0; - - int secCount = 0; - - // read the whole track - for (int i = 0; i < track.Sectors.Length; i++) - { - if (secCount >= ActiveCommandParams.EOT) - { - break; - } - - var sec = track.Sectors[i]; - for (int b = 0; b < sec.ActualData.Length; b++) - { - ExecBuffer[buffPos++] = sec.ActualData[b]; - } - - // mark the sector read - sec.SectorReadCompleted(); - - // end of sector - compare IDs - if (sec.TrackNumber != ActiveCommandParams.Cylinder || - sec.SideNumber != ActiveCommandParams.Head || - sec.SectorID != ActiveCommandParams.Sector || - sec.SectorSize != ActiveCommandParams.SectorSize) - { - SetBit(SR1_ND, ref Status1); - } - - secCount++; - ActiveDrive.SectorIndex = i; - } - - if (secCount == ActiveCommandParams.EOT) - { - // this was the last sector to read - // or termination requested - - int keyIndex = 0; - for (int i = 0; i < track.Sectors.Length; i++) - { - if (track.Sectors[i].SectorID == track.Sectors[ActiveDrive.SectorIndex].SectorID) - { - keyIndex = i; - break; - } - } - - if (keyIndex == track.Sectors.Length - 1) - { - // last sector on the cylinder, set EN - SetBit(SR1_EN, ref Status1); - - // increment cylinder - ActiveCommandParams.Cylinder++; - - // reset sector - ActiveCommandParams.Sector = 1; - ActiveDrive.SectorIndex = 0; - } - else - { - ActiveDrive.SectorIndex++; - } - - UnSetBit(SR0_IC1, ref Status0); - UnSetBit(SR0_IC0, ref Status0); - - CommitResultCHRN(); - CommitResultStatus(); - ActivePhase = Phase.Execution; - } - - if (ActivePhase == Phase.Execution) - { - ExecLength = buffPos; - ExecCounter = buffPos; - - DriveLight = true; - } - } - - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - - var index = ExecLength - ExecCounter; - - LastSectorDataReadByte = ExecBuffer[index]; - - OverrunCounter--; - ExecCounter--; - - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - /// - /// Read ID - /// COMMAND: 1 parameter byte - /// EXECUTION: The first correct ID information on the cylinder is stored in the data register - /// RESULT: 7 result bytes - /// - private void UPD_ReadID() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - DriveLight = true; - - // all parameter bytes received - ClearResultBuffer(); - Status0 = 0; - Status1 = 0; - Status2 = 0; - Status3 = 0; - - // set unit select - //SetUnitSelect(ActiveDrive.ID, ref Status0); - - // HD should always be 0 - UnSetBit(SR0_HD, ref Status0); - - if (!ActiveDrive.FLAG_READY) - { - // no disk, no tracks or motor is not on - // it is at this point the +3 detects whether a disk is present - // if not (and after another readid and SIS) it will eventually proceed to loading from tape - SetBit(SR0_IC0, ref Status0); - SetBit(SR0_NR, ref Status0); - - // setup the result buffer - ResBuffer[RS_ST0] = Status0; - for (int i = 1; i < 7; i++) - ResBuffer[i] = 0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - - var track = ActiveDrive.Disk.DiskTracks.FirstOrDefault(a => a.TrackNumber == ActiveDrive.CurrentTrackID); - - if (track != null && track.NumberOfSectors > 0 && track.TrackNumber != 0xff) - { - // formatted track - - // is the index out of bounds? - if (ActiveDrive.SectorIndex >= track.NumberOfSectors) - { - // reset the index - ActiveDrive.SectorIndex = 0; - } - - if (ActiveDrive.SectorIndex == 0 && ActiveDrive.Disk.DiskTracks[ActiveDrive.CurrentTrackID].Sectors.Length > 1) - { - // looks like readid always skips the first sector on a track - ActiveDrive.SectorIndex++; - } - - // read the sector data - var data = track.Sectors[ActiveDrive.SectorIndex]; //.GetCHRN(); - ResBuffer[RS_C] = data.TrackNumber; - ResBuffer[RS_H] = data.SideNumber; - ResBuffer[RS_R] = data.SectorID; - ResBuffer[RS_N] = data.SectorSize; - - ResBuffer[RS_ST0] = Status0; - - // check for DAM & CRC - //if (data.Status2.Bit(SR2_CM)) - //SetBit(SR2_CM, ref ResBuffer[RS_ST2]); - - - // increment the current sector - ActiveDrive.SectorIndex++; - - // is the index out of bounds? - if (ActiveDrive.SectorIndex >= track.NumberOfSectors) - { - // reset the index - ActiveDrive.SectorIndex = 0; - } - } - else - { - // unformatted track? - CommitResultCHRN(); - - SetBit(SR0_IC0, ref Status0); - ResBuffer[RS_ST0] = Status0; - ResBuffer[RS_ST1] = 0x01; - } - - ActivePhase = Phase.Result; - } - - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - #endregion - - #region WRITE Commands - - /// - /// Write Data - /// COMMAND: 8 parameter bytes - /// EXECUTION: Data transfer between FDC and FDD - /// RESULT: 7 result bytes - /// - private void UPD_WriteData() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - setup for execution phase - - // clear exec buffer and status registers - ClearExecBuffer(); - Status0 = 0; - Status1 = 0; - Status2 = 0; - Status3 = 0; - - // temp sector index - byte secIdx = ActiveCommandParams.Sector; - - // hack for when another drive (non-existent) is being called - if (ActiveDrive.ID != 0) - DiskDriveIndex = 0; - - // do we have a valid disk inserted? - if (!ActiveDrive.FLAG_READY) - { - // no disk, no tracks or motor is not on - SetBit(SR0_IC0, ref Status0); - SetBit(SR0_NR, ref Status0); - - CommitResultCHRN(); - CommitResultStatus(); - //ResBuffer[RS_ST0] = Status0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - - // check write protect tab - if (ActiveDrive.FLAG_WRITEPROTECT) - { - SetBit(SR0_IC0, ref Status0); - SetBit(SR1_NW, ref Status1); - - CommitResultCHRN(); - CommitResultStatus(); - //ResBuffer[RS_ST0] = Status0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - else - { - - // calculate the number of bytes to write - int byteCounter = 0; - byte startSecID = ActiveCommandParams.Sector; - byte endSecID = ActiveCommandParams.EOT; - bool lastSec = false; - - // get the first sector - var track = ActiveDrive.Disk.DiskTracks[ActiveCommandParams.Cylinder]; - //int secIndex = 0; - for (int s = 0; s < track.Sectors.Length; s++) - { - if (track.Sectors[s].SectorID == endSecID) - lastSec = true; - - for (int i = 0; i < 0x80 << ActiveCommandParams.SectorSize; i++) - { - byteCounter++; - - if (i == (0x80 << ActiveCommandParams.SectorSize) - 1 && lastSec) - { - break; - } - } - - if (lastSec) - break; - } - - ExecCounter = byteCounter; - ExecLength = byteCounter; - ActivePhase = Phase.Execution; - DriveLight = true; - break; - } - } - - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - - var index = ExecLength - ExecCounter; - - ExecBuffer[index] = LastSectorDataWriteByte; - - OverrunCounter--; - ExecCounter--; - - if (ExecCounter <= 0) - { - int cnt = 0; - - // all data received - byte startSecID = ActiveCommandParams.Sector; - byte endSecID = ActiveCommandParams.EOT; - bool lastSec = false; - var track = ActiveDrive.Disk.DiskTracks[ActiveCommandParams.Cylinder]; - //int secIndex = 0; - - for (int s = 0; s < track.Sectors.Length; s++) - { - if (cnt == ExecLength) - break; - - ActiveCommandParams.Sector = track.Sectors[s].SectorID; - - if (track.Sectors[s].SectorID == endSecID) - lastSec = true; - - int size = 0x80 << track.Sectors[s].SectorSize; - - for (int d = 0; d < size; d++) - { - track.Sectors[s].SectorData[d] = ExecBuffer[cnt++]; - } - - if (lastSec) - break; - } - - SetBit(SR0_IC0, ref Status0); - SetBit(SR1_EN, ref Status1); - - CommitResultCHRN(); - CommitResultStatus(); - } - - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - /// - /// Write ID (format write) - /// COMMAND: 5 parameter bytes - /// EXECUTION: Entire track is formatted - /// RESULT: 7 result bytes - /// - private void UPD_WriteID() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - setup for execution phase - DriveLight = true; - - // clear exec buffer and status registers - ClearExecBuffer(); - Status0 = 0; - Status1 = 0; - Status2 = 0; - Status3 = 0; - - // temp sector index - byte secIdx = ActiveCommandParams.Sector; - - // hack for when another drive (non-existent) is being called - if (ActiveDrive.ID != 0) - DiskDriveIndex = 0; - - // do we have a valid disk inserted? - if (!ActiveDrive.FLAG_READY) - { - // no disk, no tracks or motor is not on - SetBit(SR0_IC0, ref Status0); - SetBit(SR0_NR, ref Status0); - - CommitResultCHRN(); - CommitResultStatus(); - //ResBuffer[RS_ST0] = Status0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - - // check write protect tab - if (ActiveDrive.FLAG_WRITEPROTECT) - { - SetBit(SR0_IC0, ref Status0); - SetBit(SR1_NW, ref Status1); - - CommitResultCHRN(); - CommitResultStatus(); - //ResBuffer[RS_ST0] = Status0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - else - { - // not implemented yet - SetBit(SR0_IC0, ref Status0); - SetBit(SR1_NW, ref Status1); - - CommitResultCHRN(); - CommitResultStatus(); - //ResBuffer[RS_ST0] = Status0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - } - - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - /// - /// Write Deleted Data - /// COMMAND: 8 parameter bytes - /// EXECUTION: Data transfer between FDC and FDD - /// RESULT: 7 result bytes - /// - private void UPD_WriteDeletedData() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - setup for execution phase - - // clear exec buffer and status registers - ClearExecBuffer(); - Status0 = 0; - Status1 = 0; - Status2 = 0; - Status3 = 0; - - // temp sector index - byte secIdx = ActiveCommandParams.Sector; - - // hack for when another drive (non-existent) is being called - if (ActiveDrive.ID != 0) - DiskDriveIndex = 0; - - // do we have a valid disk inserted? - if (!ActiveDrive.FLAG_READY) - { - // no disk, no tracks or motor is not on - SetBit(SR0_IC0, ref Status0); - SetBit(SR0_NR, ref Status0); - - CommitResultCHRN(); - CommitResultStatus(); - //ResBuffer[RS_ST0] = Status0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - - // check write protect tab - if (ActiveDrive.FLAG_WRITEPROTECT) - { - SetBit(SR0_IC0, ref Status0); - SetBit(SR1_NW, ref Status1); - - CommitResultCHRN(); - CommitResultStatus(); - //ResBuffer[RS_ST0] = Status0; - - // move to result phase - ActivePhase = Phase.Result; - break; - } - else - { - - // calculate the number of bytes to write - int byteCounter = 0; - byte startSecID = ActiveCommandParams.Sector; - byte endSecID = ActiveCommandParams.EOT; - bool lastSec = false; - - // get the first sector - var track = ActiveDrive.Disk.DiskTracks[ActiveCommandParams.Cylinder]; - //int secIndex = 0; - for (int s = 0; s < track.Sectors.Length; s++) - { - if (track.Sectors[s].SectorID == endSecID) - lastSec = true; - - for (int i = 0; i < 0x80 << ActiveCommandParams.SectorSize; i++) - { - byteCounter++; - - if (i == (0x80 << ActiveCommandParams.SectorSize) - 1 && lastSec) - { - break; - } - } - - if (lastSec) - break; - } - - ExecCounter = byteCounter; - ExecLength = byteCounter; - ActivePhase = Phase.Execution; - DriveLight = true; - break; - } - } - - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - - var index = ExecLength - ExecCounter; - - ExecBuffer[index] = LastSectorDataWriteByte; - - OverrunCounter--; - ExecCounter--; - - if (ExecCounter <= 0) - { - int cnt = 0; - - // all data received - byte startSecID = ActiveCommandParams.Sector; - byte endSecID = ActiveCommandParams.EOT; - bool lastSec = false; - var track = ActiveDrive.Disk.DiskTracks[ActiveCommandParams.Cylinder]; - //int secIndex = 0; - - for (int s = 0; s < track.Sectors.Length; s++) - { - if (cnt == ExecLength) - break; - - ActiveCommandParams.Sector = track.Sectors[s].SectorID; - - if (track.Sectors[s].SectorID == endSecID) - lastSec = true; - - int size = 0x80 << track.Sectors[s].SectorSize; - - for (int d = 0; d < size; d++) - { - track.Sectors[s].SectorData[d] = ExecBuffer[cnt++]; - } - - if (lastSec) - break; - } - - SetBit(SR0_IC0, ref Status0); - SetBit(SR1_EN, ref Status1); - - CommitResultCHRN(); - CommitResultStatus(); - } - - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - #endregion - - #region SCAN Commands - - /// - /// Scan Equal - /// COMMAND: 8 parameter bytes - /// EXECUTION: Data compared between the FDD and FDC - /// RESULT: 7 result bytes - /// - private void UPD_ScanEqual() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - /// - /// Scan Low or Equal - /// COMMAND: 8 parameter bytes - /// EXECUTION: Data compared between the FDD and FDC - /// RESULT: 7 result bytes - /// - private void UPD_ScanLowOrEqual() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - /// - /// Scan High or Equal - /// COMMAND: 8 parameter bytes - /// EXECUTION: Data compared between the FDD and FDC - /// RESULT: 7 result bytes - /// - private void UPD_ScanHighOrEqual() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - #endregion - - #region OTHER Commands - - /// - /// Specify - /// COMMAND: 2 parameter bytes - /// EXECUTION: NO execution phase - /// RESULT: NO result phase - /// - /// Looks like specify command returns status 0x80 throughout its lifecycle - /// so CB is NOT set - /// - private void UPD_Specify() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - byte currByte = CommBuffer[CommCounter]; - BitArray bi = new BitArray(new byte[] { currByte }); - - switch (CommCounter) - { - // SRT & HUT - case 0: - SRT = 16 - (currByte >> 4) & 0x0f; - HUT = (currByte & 0x0f) << 4; - if (HUT == 0) - { - HUT = 255; - } - break; - // HLT & ND - case 1: - if (bi[0]) - ND = true; - else - ND = false; - - HLT = currByte & 0xfe; - if (HLT == 0) - { - HLT = 255; - } - break; - } - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - ActivePhase = Phase.Idle; - } - - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - /// - /// Seek - /// COMMAND: 2 parameter bytes - /// EXECUTION: Head is positioned over proper cylinder on disk - /// RESULT: NO result phase - /// - private void UPD_Seek() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - byte currByte = CommBuffer[CommCounter]; - switch (CommCounter) - { - case 0: - ParseParamByteStandard(CommCounter); - break; - case 1: - ActiveDrive.SeekingTrack = currByte; - break; - } - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - DriveLight = true; - ActivePhase = Phase.Execution; - ActiveCommand.CommandDelegate(); - } - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - // set seek flag - ActiveDrive.SeekStatus = SEEK_SEEK; - - if (ActiveDrive.CurrentTrackID == CommBuffer[CM_C]) - { - // we are already on the correct track - ActiveDrive.SectorIndex = 0; - } - else - { - // immediate seek - ActiveDrive.CurrentTrackID = CommBuffer[CM_C]; - - ActiveDrive.SectorIndex = 0; - - if (ActiveDrive.Disk.DiskTracks[ActiveDrive.CurrentTrackID].Sectors.Length > 1) - { - // always read the first sector - //ActiveDrive.SectorIndex++; - } - } - - // skip execution mode and go directly to idle - // result is determined by SIS command - ActivePhase = Phase.Idle; - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - /// - /// Recalibrate (seek track 0) - /// COMMAND: 1 parameter byte - /// EXECUTION: Head retracted to track 0 - /// RESULT: NO result phase - /// - private void UPD_Recalibrate() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - DriveLight = true; - ActivePhase = Phase.Execution; - ActiveCommand.CommandDelegate(); - } - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - - // immediate recalibration - ActiveDrive.TrackIndex = 0; - ActiveDrive.SectorIndex = 0; - - // recalibrate appears to always skip the first sector - //if (ActiveDrive.Disk.DiskTracks[ActiveDrive.TrackIndex].Sectors.Length > 1) - //ActiveDrive.SectorIndex++; - - // set seek flag - ActiveDrive.SeekStatus = SEEK_RECALIBRATE; - - // skip execution mode and go directly to idle - // result is determined by SIS command - ActivePhase = Phase.Idle; - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - /// - /// Sense Interrupt Status - /// COMMAND: NO parameter bytes - /// EXECUTION: NO execution phase - /// RESULT: 2 result bytes - /// - private void UPD_SenseInterruptStatus() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - // SIS should return 2 bytes if sucessfully sensed an interrupt - // 1 byte otherwise - - // it seems like the +3 ROM makes 3 SIS calls for each seek/recalibrate call for some reason - // possibly one for each drive??? - // 1 - the interrupt is acknowleged with ST0 = 32 and track number - // 2 - second sis returns 1 ST0 byte with 192 - // 3 - third SIS call returns standard 1 byte 0x80 (unknown cmd or SIS with no interrupt occured) - // for now I will assume that the first call is aimed at DriveA, the second at DriveB (which we are NOT implementing) - - // check active drive first - if (ActiveDrive.SeekStatus == SEEK_RECALIBRATE || - ActiveDrive.SeekStatus == SEEK_SEEK) - { - // interrupt has been raised for this drive - // acknowledge - ActiveDrive.SeekStatus = SEEK_IDLE;// SEEK_INTACKNOWLEDGED; - - // result length 2 - ResLength = 2; - - // first byte ST0 0x20 - Status0 = 0x20; - ResBuffer[0] = Status0; - // second byte is the current track id - ResBuffer[1] = ActiveDrive.CurrentTrackID; - } - /* - else if (ActiveDrive.SeekStatus == SEEK_INTACKNOWLEDGED) - { - // DriveA interrupt has already been acknowledged - ActiveDrive.SeekStatus = SEEK_IDLE; - - ResLength = 1; - Status0 = 192; - ResBuffer[0] = Status0; - } - */ - else if (ActiveDrive.SeekStatus == SEEK_IDLE) - { - // SIS with no interrupt - ResLength = 1; - Status0 = 0x80; - ResBuffer[0] = Status0; - } - - ActivePhase = Phase.Result; - - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - /// - /// Sense Drive Status - /// COMMAND: 1 parameter byte - /// EXECUTION: NO execution phase - /// RESULT: 1 result byte - /// - /// The ZX spectrum appears to only specify drive 1 as the parameter byte, NOT drive 0 - /// After the final param byte is received main status changes to 0xd0 - /// Data register (ST3) result is 0x51 if drive/disk not available - /// 0x71 if disk is present in 2nd drive - /// - private void UPD_SenseDriveStatus() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - ActivePhase = Phase.Execution; - UPD_SenseDriveStatus(); - } - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - // one ST3 byte required - - // set US - Status3 = (byte)ActiveDrive.ID; - - if (Status3 != 0) - { - // we only support 1 drive - SetBit(SR3_FT, ref Status3); - } - else - { - // HD - only one side - UnSetBit(SR3_HD, ref Status3); - - // write protect - if (ActiveDrive.FLAG_WRITEPROTECT) - SetBit(SR3_WP, ref Status3); - - // track 0 - if (ActiveDrive.FLAG_TRACK0) - SetBit(SR3_T0, ref Status3); - - // rdy - if (ActiveDrive.Disk != null) - SetBit(SR3_RY, ref Status3); - } - - ResBuffer[0] = Status3; - ActivePhase = Phase.Result; - - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - break; - } - } - - /// - /// Version - /// COMMAND: NO parameter bytes - /// EXECUTION: NO execution phase - /// RESULT: 1 result byte - /// - private void UPD_Version() - { - switch (ActivePhase) - { - case Phase.Idle: - case Phase.Command: - case Phase.Execution: - case Phase.Result: - UPD_Invalid(); - break; - } - } - - /// - /// Invalid - /// COMMAND: NO parameter bytes - /// EXECUTION: NO execution phase - /// RESULT: 1 result byte - /// - private void UPD_Invalid() - { - switch (ActivePhase) - { - //---------------------------------------- - // FDC is waiting for a command byte - //---------------------------------------- - case Phase.Idle: - break; - - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case Phase.Command: - break; - - //---------------------------------------- - // FDC in execution phase reading/writing bytes - //---------------------------------------- - case Phase.Execution: - // no execution phase - ActivePhase = Phase.Result; - UPD_Invalid(); - break; - - //---------------------------------------- - // Result bytes being sent to CPU - //---------------------------------------- - case Phase.Result: - ResBuffer[0] = 0x80; - break; - } - } - - #endregion - - #endregion - - #region Controller Methods - - /// - /// Called when a status register read is required - /// This can be called at any time - /// The main status register appears to be queried nearly all the time - /// so needs to be kept updated. It keeps the CPU informed of the current state - /// - private byte ReadMainStatus() - { - SetBit(MSR_RQM, ref StatusMain); - - switch (ActivePhase) - { - case Phase.Idle: - UnSetBit(MSR_DIO, ref StatusMain); - UnSetBit(MSR_CB, ref StatusMain); - UnSetBit(MSR_EXM, ref StatusMain); - break; - case Phase.Command: - UnSetBit(MSR_DIO, ref StatusMain); - SetBit(MSR_CB, ref StatusMain); - UnSetBit(MSR_EXM, ref StatusMain); - break; - case Phase.Execution: - if (ActiveCommand.Direction == CommandDirection.OUT) - SetBit(MSR_DIO, ref StatusMain); - else - UnSetBit(MSR_DIO, ref StatusMain); - - SetBit(MSR_EXM, ref StatusMain); - SetBit(MSR_CB, ref StatusMain); - - // overrun detection - OverrunCounter++; - if (OverrunCounter >= 64) - { - // CPU has read the status register 64 times without reading the data register - // switch the current command into result phase - ActivePhase = Phase.Result; - - // reset the overun counter - OverrunCounter = 0; - } - - break; - case Phase.Result: - SetBit(MSR_DIO, ref StatusMain); - SetBit(MSR_CB, ref StatusMain); - UnSetBit(MSR_EXM, ref StatusMain); - break; - } - - //if (!CheckTiming()) - //{ - // UnSetBit(MSR_EXM, ref StatusMain); - //} - - return StatusMain; - } - - //private int testCount = 0; - /// - /// Handles CPU reading from the data register - /// - private byte ReadDataRegister() - { - // default return value - byte res = 0xff; - - // check RQM flag status - if (!GetBit(MSR_RQM, StatusMain)) - { - // FDC is not ready to return data - return res; - } - - // check active direction - if (!GetBit(MSR_DIO, StatusMain)) - { - // FDC is expecting to receive, not send data - return res; - } - - switch (ActivePhase) - { - case Phase.Execution: - // reset overrun counter - OverrunCounter = 0; - - // execute read - ActiveCommand.CommandDelegate(); - - res = LastSectorDataReadByte; - - if (ExecCounter <= 0) - { - // end of execution phase - ActivePhase = Phase.Result; - } - - return res; - - case Phase.Result: - - DriveLight = false; - - ActiveCommand.CommandDelegate(); - - // result byte reading - res = ResBuffer[ResCounter]; - - // increment result counter - ResCounter++; - - if (ResCounter >= ResLength) - { - ActivePhase = Phase.Idle; - } - - break; - } - - return res; - } - - /// - /// Handles CPU writing to the data register - /// - private void WriteDataRegister(byte data) - { - if (!GetBit(MSR_RQM, StatusMain) || GetBit(MSR_DIO, StatusMain)) - { - // FDC will not receive and process any bytes - return; - } - - // store the incoming byte - LastByteReceived = data; - - // process incoming bytes - switch (ActivePhase) - { - //// controller is idle awaiting the first command byte of a new instruction - case Phase.Idle: - ParseCommandByte(data); - break; - //// we are in command phase - case Phase.Command: - // attempt to process this parameter byte - //ProcessCommand(data); - ActiveCommand.CommandDelegate(); - break; - //// we are in execution phase - case Phase.Execution: - // CPU is going to be sending data bytes to the FDC to be written to disk - - // store the byte - LastSectorDataWriteByte = data; - ActiveCommand.CommandDelegate(); - - if (ExecCounter <= 0) - { - // end of execution phase - ActivePhase = Phase.Result; - } - - break; - //// result phase - case Phase.Result: - // data register will not receive bytes during result phase - break; - } - } - - /// - /// Processes the first command byte (within a command instruction) - /// Returns TRUE if successful. FALSE if otherwise - /// Called only in idle phase - /// - private bool ParseCommandByte(byte cmdByte) - { - // clear counters - CommCounter = 0; - ResCounter = 0; - - // get the first 4 bytes - byte cByte = (byte)(cmdByte & 0x0f); - - // get MT, MD and SK states - CMD_FLAG_MT = cmdByte.Bit(7); - CMD_FLAG_MF = cmdByte.Bit(6); - CMD_FLAG_SK = cmdByte.Bit(5); - - cmdByte = cByte; - - // lookup the command - var cmd = CommandList.FirstOrDefault(a => a.CommandCode == cmdByte); - - if (cmd == null) - { - // no command found - use invalid - CMDIndex = CommandList.Count() - 1; - } - else - { - // valid command found - CMDIndex = CommandList.FindIndex(a => a.CommandCode == cmdByte); - - // check validity of command byte flags - // if a flag is set but not valid for this command then it is invalid - bool invalid = false; - - if (!ActiveCommand.MT) - if (CMD_FLAG_MT) - invalid = true; - if (!ActiveCommand.MF) - if (CMD_FLAG_MF) - invalid = true; - if (!ActiveCommand.SK) - if (CMD_FLAG_SK) - invalid = true; - - if (invalid) - { - // command byte included spurious bit 5,6 or 7 flags - CMDIndex = CommandList.Count() - 1; - } - - /* - if ((CMD_FLAG_MF && !ActiveCommand.MF) || - (CMD_FLAG_MT && !ActiveCommand.MT) || - (CMD_FLAG_SK && !ActiveCommand.SK)) - { - // command byte included spurious bit 5,6 or 7 flags - CMDIndex = CommandList.Count() - 1; - } - */ - } - - CommCounter = 0; - ResCounter = 0; - - // there will now be an active command set - // move to command phase - ActivePhase = Phase.Command; - - /* - // check for invalid SIS - if (ActiveInterrupt == InterruptState.None && CMDIndex == CC_SENSE_INTSTATUS) - { - CMDIndex = CC_INVALID; - //ActiveCommand.CommandDelegate(InstructionState.StartResult); - } - */ - - // set reslength - ResLength = ActiveCommand.ResultByteCount; - - // if there are no expected param bytes to receive - go ahead and run the command - if (ActiveCommand.ParameterByteCount == 0) - { - ActivePhase = Phase.Execution; - ActiveCommand.CommandDelegate(); - } - - return true; - } - - /// - /// Parses the first 5 command argument bytes that are of the standard format - /// - private void ParseParamByteStandard(int index) - { - byte currByte = CommBuffer[index]; - BitArray bi = new BitArray(new byte[] { currByte }); - - switch (index) - { - // HD & US - case CM_HEAD: - if (bi[2]) - ActiveCommandParams.Side = 1; - else - ActiveCommandParams.Side = 0; - - ActiveCommandParams.UnitSelect = (byte)(GetUnitSelect(currByte)); - DiskDriveIndex = ActiveCommandParams.UnitSelect; - break; - - // C - case CM_C: - ActiveCommandParams.Cylinder = currByte; - break; - - // H - case CM_H: - ActiveCommandParams.Head = currByte; - break; - - // R - case CM_R: - ActiveCommandParams.Sector = currByte; - break; - - // N - case CM_N: - ActiveCommandParams.SectorSize = currByte; - break; - - // EOT - case CM_EOT: - ActiveCommandParams.EOT = currByte; - break; - - // GPL - case CM_GPL: - ActiveCommandParams.Gap3Length = currByte; - break; - - // DTL - case CM_DTL: - ActiveCommandParams.DTL = currByte; - break; - - default: - break; - } - } - - /// - /// Clears the result buffer - /// - public void ClearResultBuffer() - { - for (int i = 0; i < ResBuffer.Length; i++) - { - ResBuffer[i] = 0; - } - } - - /// - /// Clears the result buffer - /// - public void ClearExecBuffer() - { - for (int i = 0; i < ExecBuffer.Length; i++) - { - ExecBuffer[i] = 0; - } - } - - /// - /// Populates the result status registers - /// - private void CommitResultStatus() - { - // check for read diag - if (ActiveCommand.CommandCode == 0x02) - { - // commit to result buffer - ResBuffer[RS_ST0] = Status0; - ResBuffer[RS_ST1] = Status1; - return; - } - - // check for error bits - if (GetBit(SR1_DE, Status1) || - GetBit(SR1_MA, Status1) || - GetBit(SR1_ND, Status1) || - GetBit(SR1_NW, Status1) || - GetBit(SR1_OR, Status1) || - GetBit(SR2_BC, Status2) || - GetBit(SR2_CM, Status2) || - GetBit(SR2_DD, Status2) || - GetBit(SR2_MD, Status2) || - GetBit(SR2_SN, Status2) || - GetBit(SR2_WC, Status2)) - { - // error bits set - unset end of track - UnSetBit(SR1_EN, ref Status1); - } - - // check for data errors - if (GetBit(SR1_DE, Status1) || - GetBit(SR2_DD, Status2)) - { - // unset control mark - UnSetBit(SR2_CM, ref Status2); - } - else if (GetBit(SR2_CM, Status2)) - { - // DAM found - unset IC and US0 - UnSetBit(SR0_IC0, ref Status0); - UnSetBit(SR0_US0, ref Status0); - } - - // commit to result buffer - ResBuffer[RS_ST0] = Status0; - ResBuffer[RS_ST1] = Status1; - ResBuffer[RS_ST2] = Status2; - - } - - /// - /// Populates the result CHRN values - /// - private void CommitResultCHRN() - { - ResBuffer[RS_C] = ActiveCommandParams.Cylinder; - ResBuffer[RS_H] = ActiveCommandParams.Head; - ResBuffer[RS_R] = ActiveCommandParams.Sector; - ResBuffer[RS_N] = ActiveCommandParams.SectorSize; - } - - /// - /// Moves active phase into idle - /// - public void SetPhase_Idle() - { - ActivePhase = Phase.Idle; - - // active direction - UnSetBit(MSR_DIO, ref StatusMain); - // CB - UnSetBit(MSR_CB, ref StatusMain); - // RQM - SetBit(MSR_RQM, ref StatusMain); - - CommCounter = 0; - ResCounter = 0; - } - - /// - /// Moves to result phase - /// - public void SetPhase_Result() - { - ActivePhase = Phase.Result; - - // active direction - SetBit(MSR_DIO, ref StatusMain); - // CB - SetBit(MSR_CB, ref StatusMain); - // RQM - SetBit(MSR_RQM, ref StatusMain); - // EXM - UnSetBit(MSR_EXM, ref StatusMain); - - CommCounter = 0; - ResCounter = 0; - } - - /// - /// Moves to command phase - /// - public void SetPhase_Command() - { - ActivePhase = Phase.Command; - - // default 0x80 - just RQM - SetBit(MSR_RQM, ref StatusMain); - UnSetBit(MSR_DIO, ref StatusMain); - UnSetBit(MSR_CB, ref StatusMain); - UnSetBit(MSR_EXM, ref StatusMain); - CommCounter = 0; - ResCounter = 0; - } - - /// - /// Moves to execution phase - /// - public void SetPhase_Execution() - { - ActivePhase = Phase.Execution; - - // EXM - SetBit(MSR_EXM, ref StatusMain); - // CB - SetBit(MSR_CB, ref StatusMain); - // RQM - UnSetBit(MSR_RQM, ref StatusMain); - - CommCounter = 0; - ResCounter = 0; - } - - #endregion - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs deleted file mode 100644 index 5ea4b182a4..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs +++ /dev/null @@ -1,876 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; -using System.Linq; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Floppy drive related stuff - /// - #region Attribution - /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ - #endregion - public partial class NECUPD765 : IFDDHost - { - #region Drive State - - /// - /// FDD Flag - motor on/off - /// - public bool FDD_FLAG_MOTOR; - - /// - /// The index of the currently active disk drive - /// - public int DiskDriveIndex - { - get => _diskDriveIndex; - set - { - // when index is changed update the ActiveDrive - _diskDriveIndex = value; - ActiveDrive = DriveStates[_diskDriveIndex]; - } - } - private int _diskDriveIndex = 0; - - /// - /// The currently active drive - /// - private DriveState ActiveDrive; - - /// - /// Array that holds state information for each possible drive - /// - private DriveState[] DriveStates = new DriveState[4]; - - #endregion - - #region FDD Methods - - /// - /// Initialization / reset of the floppy drive subsystem - /// - private void FDD_Init() - { - for (int i = 0; i < 4; i++) - { - DriveState ds = new DriveState(i, this); - DriveStates[i] = ds; - } - } - - /// - /// Searches for the requested sector - /// - private FloppyDisk.Sector GetSector() - { - FloppyDisk.Sector sector = null; - - // get the current track - var trk = ActiveDrive.Disk.DiskTracks[ActiveDrive.TrackIndex]; - - // get the current sector index - int index = ActiveDrive.SectorIndex; - - // make sure this index exists - if (index > trk.Sectors.Length) - { - index = 0; - } - - // index hole count - int iHole = 0; - - // loop through the sectors in a track - // the loop ends with either the sector being found - // or the index hole being passed twice - while (iHole <= 2) - { - // does the requested sector match the current sector - if (trk.Sectors[index].SectorIDInfo.C == ActiveCommandParams.Cylinder && - trk.Sectors[index].SectorIDInfo.H == ActiveCommandParams.Head && - trk.Sectors[index].SectorIDInfo.R == ActiveCommandParams.Sector && - trk.Sectors[index].SectorIDInfo.N == ActiveCommandParams.SectorSize) - { - // sector has been found - sector = trk.Sectors[index]; - - UnSetBit(SR2_BC, ref Status2); - UnSetBit(SR2_WC, ref Status2); - break; - } - - // check for bad cylinder - if (trk.Sectors[index].SectorIDInfo.C == 255) - { - SetBit(SR2_BC, ref Status2); - } - // check for no cylinder - else if (trk.Sectors[index].SectorIDInfo.C != ActiveCommandParams.Cylinder) - { - SetBit(SR2_WC, ref Status2); - } - - // incrememnt sector index - index++; - - // have we reached the index hole? - if (trk.Sectors.Length <= index) - { - // wrap around - index = 0; - iHole++; - } - } - - // search loop has completed and the sector may or may not have been found - - // bad cylinder detected? - if (Status2.Bit(SR2_BC)) - { - // remove WC - UnSetBit(SR2_WC, ref Status2); - } - - // update sectorindex on drive - ActiveDrive.SectorIndex = index; - - return sector; - } - - #endregion - - #region IFDDHost - - // IFDDHost methods that fall through to the currently active drive - - /// - /// Parses a new disk image and loads it into this floppy drive - /// - public void FDD_LoadDisk(byte[] diskData) - { - // we are only going to load into the first drive - DriveStates[0].FDD_LoadDisk(diskData); - } - - /// - /// Ejects the current disk - /// - public void FDD_EjectDisk() - { - DriveStates[0].FDD_EjectDisk(); - } - - /// - /// Signs whether the current active drive has a disk inserted - /// - public bool FDD_IsDiskLoaded => DriveStates[DiskDriveIndex].FDD_IsDiskLoaded; - - /// - /// Returns the disk object from drive 0 - /// - public FloppyDisk DiskPointer => DriveStates[0].Disk; - - public FloppyDisk Disk { get; set; } - - #endregion - - #region Drive Status Class - - /// - /// Holds specfic state information about a drive - /// - private class DriveState : IFDDHost - { - #region State - - /// - /// The drive ID from an FDC perspective - /// - public int ID; - - /// - /// Signs whether this drive ready - /// TRUE if both drive exists and has a disk inserted - /// - public bool FLAG_READY - { - get - { - if (!FDD_IsDiskLoaded || Disk.GetTrackCount() == 0 || !FDC.FDD_FLAG_MOTOR) - return false; - else - return true; - } - } - - /// - /// Disk is write protected (TRUE BY DEFAULT) - /// - public bool FLAG_WRITEPROTECT = false; - - /// - /// Storage for seek steps - /// One step for each indexpulse (track index) until seeked track - /// - public int SeekCounter; - - /// - /// Seek status - /// - public int SeekStatus; - - /// - /// Age counter - /// - public int SeekAge; - - /// - /// The current side - /// - public byte CurrentSide; - - /// - /// The current track index in the DiskTracks array - /// - public byte TrackIndex; - - /// - /// The track ID of the current cylinder - /// - public byte CurrentTrackID - { - get - { - // default invalid track - int id = 0xff; - - if (Disk == null) - return (byte)id; - - if (Disk.DiskTracks.Count() == 0) - return (byte)id; - - if (TrackIndex >= Disk.GetTrackCount()) - TrackIndex = 0; - else if (TrackIndex < 0) - TrackIndex = 0; - - var track = Disk.DiskTracks[TrackIndex]; - - id = track.TrackNumber; - - return (byte)id; - } - set - { - for (int i = 0; i < Disk.GetTrackCount(); i++) - { - if (Disk.DiskTracks[i].TrackNumber == value) - { - TrackIndex = (byte)i; - break; - } - } - } - } - - - /// - /// The new track that the drive is seeking to - /// (used in seek operations) - /// - public int SeekingTrack; - - /// - /// The current sector index in the Sectors array - /// - public int SectorIndex; - - /// - /// The currently loaded floppy disk - /// - public FloppyDisk Disk { get; set; } - - /// - /// The parent controller - /// - private NECUPD765 FDC; - - #endregion - - #region Lookups - - /// - /// TRUE if we are on track 0 - /// - public bool FLAG_TRACK0 => TrackIndex == 0; - - #endregion - - #region Public Methods - /* - /// - /// Moves the head across the disk cylinders - /// - public void MoveHead(SkipDirection direction, int cylinderCount) - { - // get total tracks - int trackCount = Disk.DiskTracks.Count(); - - int trk = 0; - - switch (direction) - { - case SkipDirection.Increment: - trk = (int)CurrentTrack + cylinderCount; - if (trk >= trackCount) - { - // past the last track - trk = trackCount - 1; - } - else if (trk < 0) - trk = 0; - break; - case SkipDirection.Decrement: - trk = (int)CurrentTrack - cylinderCount; - if (trk < 0) - { - // before the first track - trk = 0; - } - else if (trk >= trackCount) - trk = trackCount - 1; - break; - } - - // move the head - CurrentTrack = (byte)trk; - } - */ - - /* - - /// - /// Finds a supplied sector - /// - public FloppyDisk.Sector FindSector(ref byte[] resBuffer, CommandParameters prms) - { - int index =CurrentSector; - int lc = 0; - FloppyDisk.Sector sector = null; - - bool found = false; - - do - { - sector = Disk.DiskTracks[CurrentTrack].Sectors[index]; - if (sector != null && sector.SectorID == prms.Sector) - { - // sector found - // check for data errors - if ((sector.Status1 & 0x20) != 0 || (sector.Status2 & 0x20) != 0) - { - // data errors found - } - found = true; - break; - } - - // sector doesnt match - var c = Disk.DiskTracks[CurrentTrack].Sectors[index].TrackNumber; - if (c == 255) - { - // bad cylinder - resBuffer[RS_ST2] |= 0x02; - } - else if (prms.Cylinder != c) - { - // cylinder mismatch - resBuffer[RS_ST2] |= 0x10; - } - - // increment index - index++; - - if (index >= Disk.DiskTracks[CurrentTrack].NumberOfSectors) - { - // out of bounds - index = 0; - lc++; - } - - } while (lc < 2); - - if ((resBuffer[RS_ST2] & 0x02) != 0) - { - // bad cylinder set - remove no cylinder - UnSetBit(SR2_WC, ref resBuffer[RS_ST2]); - } - - // update current sector - CurrentSector = index; - - if (found) - return sector; - else - return null; - } - - - /// - /// Populates a result buffer - /// - public void FillResult(ref byte[] resBuffer, CHRN chrn) - { - // clear results - resBuffer[RS_ST0] = 0; - resBuffer[RS_ST1] = 0; - resBuffer[RS_ST2] = 0; - resBuffer[RS_C] = 0; - resBuffer[RS_H] = 0; - resBuffer[RS_R] = 0; - resBuffer[RS_N] = 0; - - if (chrn == null) - { - // no chrn supplied - resBuffer[RS_ST0] = ST0; - resBuffer[RS_ST1] = 0; - resBuffer[RS_ST2] = 0; - resBuffer[RS_C] = 0; - resBuffer[RS_H] = 0; - resBuffer[RS_R] = 0; - resBuffer[RS_N] = 0; - } - } - - - - /// - /// Populates the result buffer with ReadID data - /// - public void ReadID(ref byte[] resBuffer) - { - if (CheckDriveStatus() == false) - { - // drive not ready - resBuffer[RS_ST0] = ST0; - return; - } - - var track = Disk.DiskTracks.Where(a => a.TrackNumber == CurrentTrack).FirstOrDefault(); - - if (track != null && track.NumberOfSectors > 0) - { - // formatted track - - // get the current sector - int index = CurrentSector; - - // is the index out of bounds? - if (index >= track.NumberOfSectors) - { - // reset the index - index = 0; - } - - // read the sector data - var data = track.Sectors[index]; - resBuffer[RS_C] = data.TrackNumber; - resBuffer[RS_H] = data.SideNumber; - resBuffer[RS_R] = data.SectorID; - resBuffer[RS_N] = data.SectorSize; - - resBuffer[RS_ST0] = ST0; - - // increment the current sector - CurrentSector = index + 1; - return; - } - else - { - // unformatted track? - resBuffer[RS_C] = FDC.CommBuffer[CM_C]; - resBuffer[RS_H] = FDC.CommBuffer[CM_H]; - resBuffer[RS_R] = FDC.CommBuffer[CM_R]; - resBuffer[RS_N] = FDC.CommBuffer[CM_N]; - - SetBit(SR0_IC0, ref ST0); - resBuffer[RS_ST0] = ST0; - resBuffer[RS_ST1] = 0x01; - return; - } - } - */ - - /* - - /// - /// The drive performs a seek operation if necessary - /// Return value TRUE indicates seek complete - /// - public void DoSeek() - { - if (CurrentState != DriveMainState.Recalibrate && - CurrentState != DriveMainState.Seek) - { - // no seek/recalibrate has been asked for - return; - } - - if (GetBit(ID, FDC.StatusMain)) - { - // drive is already seeking - return; - } - - RunSeekCycle(); - } - - /// - /// Runs a seek cycle - /// - public void RunSeekCycle() - { - for (;;) - { - switch (SeekState) - { - // seek or recalibrate has been requested - case SeekSubState.Idle: - - if (CurrentState == DriveMainState.Recalibrate) - { - // recalibrate always seeks to track 0 - SeekingTrack = 0; - } - SeekState = SeekSubState.MoveInit; - - // mark drive as busy - // this should be cleared by SIS command - SetBit(ID, ref FDC.StatusMain); - - break; - - // setup for the head move - case SeekSubState.MoveInit: - - if (CurrentTrack == SeekingTrack) - { - // we are already at the required track - if (CurrentState == DriveMainState.Recalibrate && - !FLAG_TRACK0) - { - // recalibration fail - SeekIntState = SeekIntStatus.Abnormal; - - // raise seek interrupt - FDC.ActiveInterrupt = InterruptState.Seek; - - // unset DB bit - UnSetBit(ID, ref FDC.StatusMain); - - // equipment check - SetBit(SR0_EC, ref FDC.Status0); - - SeekState = SeekSubState.PerformCompletion; - break; - } - - if (CurrentState == DriveMainState.Recalibrate && - FLAG_TRACK0) - { - // recalibration success - SeekIntState = SeekIntStatus.Normal; - - // raise seek interrupt - FDC.ActiveInterrupt = InterruptState.Seek; - - // unset DB bit - UnSetBit(ID, ref FDC.StatusMain); - - SeekState = SeekSubState.PerformCompletion; - break; - } - } - - // check for error - if (IntStatus >= IC_ABORTED_DISCREMOVED || Disk == null) - { - // drive not ready - FLAG_READY = false; - - // drive not ready - SeekIntState = SeekIntStatus.DriveNotReady; - - // cancel any interrupt - FDC.ActiveInterrupt = InterruptState.None; - - // unset DB bit - UnSetBit(ID, ref FDC.StatusMain); - - SeekState = SeekSubState.PerformCompletion; - break; - } - - if (SeekCounter > 1) - { - // not ready to seek yet - SeekCounter--; - return; - } - - if (FDC.SRT < 1 && CurrentTrack != SeekingTrack) - { - SeekState = SeekSubState.MoveImmediate; - break; - } - - // head move - SeekState = SeekSubState.HeadMove; - - break; - - case SeekSubState.HeadMove: - - // do the seek - SeekCounter = FDC.SRT; - - if (CurrentTrack < SeekingTrack) - { - // we are seeking forward - var delta = SeekingTrack - CurrentTrack; - MoveHead(SkipDirection.Increment, 1); - } - else if (CurrentTrack > SeekingTrack) - { - // we are seeking backward - var delta = CurrentTrack - SeekingTrack; - MoveHead(SkipDirection.Decrement, 1); - } - - // should the seek be completed now? - if (CurrentTrack == SeekingTrack) - { - SeekState = SeekSubState.PerformCompletion; - break; - } - - // seek not finished yet - return; - - // seek emulation processed immediately - case SeekSubState.MoveImmediate: - - if (CurrentTrack < SeekingTrack) - { - // we are seeking forward - var delta = SeekingTrack - CurrentTrack; - MoveHead(SkipDirection.Increment, delta); - - } - else if (CurrentTrack > SeekingTrack) - { - // we are seeking backward - var delta = CurrentTrack - SeekingTrack; - MoveHead(SkipDirection.Decrement, delta); - } - - SeekState = SeekSubState.PerformCompletion; - break; - - case SeekSubState.PerformCompletion: - SeekDone(); - SeekState = SeekSubState.SeekCompleted; - break; - - case SeekSubState.SeekCompleted: - // seek has already completed - return; - } - } - } - - /// - /// Called when a seek operation has completed - /// - public void SeekDone() - { - SeekCounter = 0; - SeekingTrack = CurrentTrack; - - // generate ST0 register data - - // get only the IC bits - IntStatus &= IC_ABORTED_DISCREMOVED; - - // drive ready? - if (!FLAG_READY) - { - SetBit(SR0_NR, ref IntStatus); - SetBit(SR0_EC, ref IntStatus); - - // are we recalibrating? - if (CurrentState == DriveMainState.Recalibrate) - { - SetBit(SR0_EC, ref IntStatus); - } - } - - // set seek end - SetBit(SR0_SE, ref IntStatus); - /* - // head address - if (CurrentSide > 0) - { - SetBit(SR0_HD, ref IntStatus); - - // drive only supports 1 head - // set the EC bit - SetBit(SR0_EC, ref IntStatus); - } - */ - /* - // UnitSelect - SetUnitSelect(ID, ref IntStatus); - - // move to none state - //CurrentState = DriveMainState.None; - - //SeekState = SeekSubState.SeekCompleted; - - // set the seek interrupt flag for this drive - // this will be cleared at the next successful senseint - FLAG_SEEK_INTERRUPT = true; - - //CurrentState = DriveMainState.None; - - } - */ - - #endregion - - #region Construction - - public DriveState(int driveID, NECUPD765 fdc) - { - ID = driveID; - FDC = fdc; - } - - #endregion - - #region IFDDHost - - /// - /// Parses a new disk image and loads it into this floppy drive - /// - public void FDD_LoadDisk(byte[] diskData) - { - // try dsk first - FloppyDisk fdd = null; - bool found = false; - - foreach (DiskType type in Enum.GetValues(typeof(DiskType))) - { - switch (type) - { - case DiskType.CPCExtended: - fdd = new CPCExtendedFloppyDisk(); - found = fdd.ParseDisk(diskData); - break; - case DiskType.CPC: - fdd = new CPCFloppyDisk(); - found = fdd.ParseDisk(diskData); - break; - case DiskType.IPF: - fdd = new IPFFloppyDisk(); - found = fdd.ParseDisk(diskData); - break; - case DiskType.UDI: - fdd = new UDI1_0FloppyDisk(); - found = fdd.ParseDisk(diskData); - break; - } - - if (found) - { - Disk = fdd; - break; - } - } - - if (!found) - { - throw new Exception(this.GetType().ToString() + - "\n\nDisk image file could not be parsed. Potentially an unknown format."); - } - } - - /// - /// Ejects the current disk - /// - public void FDD_EjectDisk() - { - Disk = null; - //FLAG_READY = false; - } - - /// - /// Signs whether the current active drive has a disk inserted - /// - public bool FDD_IsDiskLoaded - { - get - { - if (Disk != null) - return true; - else - return false; - } - } - - #endregion - - #region StateSerialization - - public void SyncState(Serializer ser) - { - ser.Sync(nameof(ID), ref ID); - ser.Sync(nameof(FLAG_WRITEPROTECT), ref FLAG_WRITEPROTECT); - //ser.Sync(nameof(FLAG_DISKCHANGED), ref FLAG_DISKCHANGED); - //ser.Sync(nameof(FLAG_RECALIBRATING), ref FLAG_RECALIBRATING); - //ser.Sync(nameof(FLAG_SEEK_INTERRUPT), ref FLAG_SEEK_INTERRUPT); - //ser.Sync(nameof(IntStatus), ref IntStatus); - //ser.Sync(nameof(ST0), ref ST0); - //ser.Sync(nameof(RecalibrationCounter), ref RecalibrationCounter); - ser.Sync(nameof(SeekCounter), ref SeekCounter); - ser.Sync(nameof(SeekStatus), ref SeekStatus); - ser.Sync(nameof(SeekAge), ref SeekAge); - ser.Sync(nameof(CurrentSide), ref CurrentSide); - //ser.Sync(nameof(CurrentTrack), ref CurrentTrack); - ser.Sync(nameof(TrackIndex), ref TrackIndex); - ser.Sync(nameof(SeekingTrack), ref SeekingTrack); - //ser.Sync(nameof(CurrentSector), ref CurrentSector); - ser.Sync(nameof(SectorIndex), ref SectorIndex); - //ser.Sync(nameof(RAngles), ref RAngles); - //ser.Sync(nameof(DataPointer), ref DataPointer); - //ser.SyncEnum(nameof(CurrentState), ref CurrentState); - //ser.SyncEnum(nameof(SeekState), ref SeekState); - //ser.SyncEnum(nameof(SeekIntState), ref SeekIntState); - } - - #endregion - } - - #endregion - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs deleted file mode 100644 index 7964653bab..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// IPortIODevice - /// - #region Attribution - /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ - #endregion - public partial class NECUPD765 : IPortIODevice - { - #region Dev Logging - - public string outputfile = @"D:\Dropbox\Dropbox\_Programming\TASVideos\BizHawk\output\zxhawkio-" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".csv"; - public string outputString = "STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN\r\n"; - public bool writeDebug = false; - - public List dLog = new List - { - "STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN" - }; - - - /* - * Status read - * Data write - * Data read - * CMD code - * CMD string - * MT flag - * MK flag - * SK flag - * */ - private string[] workingArr = new string[3]; - - private void BuildCSVLine() - { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 3; i++) - { - sb.Append(workingArr[i]); - sb.Append(","); - workingArr[i] = ""; - } - - sb.Append(ActiveCommand.CommandCode).Append(","); - - sb.Append(CMD_FLAG_MT).Append(","); - sb.Append(CMD_FLAG_MF).Append(","); - sb.Append(CMD_FLAG_SK).Append(","); - - sb.Append(CommCounter).Append(","); - sb.Append(ResCounter).Append(","); - sb.Append(ExecCounter).Append(","); - sb.Append(ExecLength); - - //sb.Append("\r\n"); - - //outputString += sb.ToString(); - dLog.Add(sb.ToString()); - } - - #endregion - - /// - /// Device responds to an IN instruction - /// - public bool ReadPort(ushort port, ref int data) - { - BitArray bits = new BitArray(new byte[] { (byte)data }); - - if (port == 0x3ffd) - { - // Z80 is trying to read from the data register - data = ReadDataRegister(); - if (writeDebug) - { - workingArr[2] = data.ToString(); - //outputString += ",," + data + "," + ActiveCommand.CommandCode + "\r\n"; - BuildCSVLine(); - } - - return true; - } - - if (port == 0x2ffd) - { - // read main status register - // this can happen at any time - data = ReadMainStatus(); - if (writeDebug) - { - //outputString += data + ",,," + ActiveCommand.CommandCode + "\r\n"; - workingArr[0] = data.ToString(); - BuildCSVLine(); - //System.IO.File.WriteAllText(outputfile, outputString); - } - - return true; - } - - return false; - } - - /// - /// Device responds to an OUT instruction - /// - public bool WritePort(ushort port, int data) - { - BitArray bits = new BitArray(new byte[] { (byte)data }); - - if (port == 0x3ffd) - { - // Z80 is attempting to write to the data register - WriteDataRegister((byte)data); - if (writeDebug) - { - //outputString += "," + data + ",," + ActiveCommand.CommandCode + "\r\n"; - workingArr[1] = data.ToString(); - BuildCSVLine(); - //System.IO.File.WriteAllText(outputfile, outputString); - } - - return true; - } - - if (port == 0x1ffd) - { - // set disk motor on/off - FDD_FLAG_MOTOR = bits[3]; - return true; - } - return false; - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs deleted file mode 100644 index 2ef6f4080d..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs +++ /dev/null @@ -1,120 +0,0 @@ - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Timimng - /// - #region Attribution - /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ - #endregion - public partial class NECUPD765 - { - /// - /// The current Z80 cycle - /// - private long CurrentCPUCycle - { - get - { - if (_machine == null) - return 0; - else - return _machine.CPU.TotalExecutedCycles; - } - } - - /// - /// The last CPU cycle when the FDC accepted an IO read/write - /// - private long LastCPUCycle; - - /// - /// The current delay figure (in Z80 t-states) - /// This implementation only introduces delay upon main status register reads - /// All timing calculations should be done during the other read/write operations - /// - private long StatusDelay; - - /// - /// Defines the numbers of Z80 cycles per MS - /// - private long CPUCyclesPerMs; - - /// - /// The floppy drive emulated clock speed - /// - public const double DriveClock = 31250; - - /// - /// The number of floppy drive cycles per MS - /// - public long DriveCyclesPerMs; - - /// - /// The number of T-States in one floppy drive clock tick - /// - public long StatesPerDriveTick; - - /// - /// Responsible for measuring when the floppy drive is ready to run a cycle - /// - private long TickCounter; - - /// - /// Internal drive cycle counter - /// - private int DriveCycleCounter = 1; - - /// - /// Initializes the timing routines - /// - private void TimingInit() - { - // z80 timing - double frameSize = _machine.ULADevice.FrameLength; - double rRate = _machine.ULADevice.ClockSpeed / frameSize; - long tPerSecond = (long)(frameSize * rRate); - CPUCyclesPerMs = tPerSecond / 1000; - - // drive timing - double dRate = DriveClock / frameSize; - long dPerSecond = (long)(frameSize * dRate); - DriveCyclesPerMs = dPerSecond / 1000; - - long TStatesPerDriveCycle = (long)((double)_machine.ULADevice.ClockSpeed / DriveClock); - StatesPerDriveTick = TStatesPerDriveCycle; - - } - - /// - /// Called by reads to the main status register - /// Returns true if there is no delay - /// Returns false if read is to be deferred - /// - private bool CheckTiming() - { - // get delta - long delta = CurrentCPUCycle - LastCPUCycle; - - if (StatusDelay >= delta) - { - // there is still delay remaining - StatusDelay -= delta; - LastCPUCycle = CurrentCPUCycle; - return false; - } - else - { - // no delay remaining - StatusDelay = 0; - LastCPUCycle = CurrentCPUCycle; - return true; - } - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs deleted file mode 100644 index a248f6fd76..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs +++ /dev/null @@ -1,248 +0,0 @@ -using BizHawk.Common; -using System.Collections.Generic; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// The NEC floppy disk controller (and floppy drive) found in the +3 - /// - #region Attribution - /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ - #endregion - public partial class NECUPD765 - { - #region Devices - - /// - /// The emulated spectrum machine - /// - private SpectrumBase _machine; - - #endregion - - #region Construction & Initialization - - /// - /// Main constructor - /// - public NECUPD765() - { - InitCommandList(); - } - - /// - /// Initialization routine - /// - public void Init(SpectrumBase machine) - { - _machine = machine; - FDD_Init(); - TimingInit(); - Reset(); - } - - /// - /// Resets the FDC - /// - public void Reset() - { - // setup main status - StatusMain = 0; - - Status0 = 0; - Status1 = 0; - Status2 = 0; - Status3 = 0; - - SetBit(MSR_RQM, ref StatusMain); - - SetPhase_Idle(); - - //FDC_FLAG_RQM = true; - //ActiveDirection = CommandDirection.IN; - SRT = 6; - HUT = 16; - HLT = 2; - HLT_Counter = 0; - HUT_Counter = 0; - IndexPulseCounter = 0; - CMD_FLAG_MF = false; - - foreach (var d in DriveStates) - { - //d.SeekingTrack = d.CurrentTrack; - ////d.SeekCounter = 0; - //d.FLAG_SEEK_INTERRUPT = false; - //d.IntStatus = 0; - //d.SeekState = SeekSubState.Idle; - //d.SeekIntState = SeekIntStatus.Normal; - - } - - } - - /// - /// Setup the command structure - /// Each command represents one of the internal UPD765 commands - /// - private void InitCommandList() - { - CommandList = new List - { - // read data - new Command { CommandDelegate = UPD_ReadData, CommandCode = 0x06, MT = true, MF = true, SK = true, IsRead = true, - Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, - // read id - new Command { CommandDelegate = UPD_ReadID, CommandCode = 0x0a, MF = true, IsRead = true, - Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 7 }, - // specify - new Command { CommandDelegate = UPD_Specify, CommandCode = 0x03, - Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, - // read diagnostic - new Command { CommandDelegate = UPD_ReadDiagnostic, CommandCode = 0x02, MF = true, SK = true, IsRead = true, - Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, - // scan equal - new Command { CommandDelegate = UPD_ScanEqual, CommandCode = 0x11, MT = true, MF = true, SK = true, IsRead = true, - Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, - // scan high or equal - new Command { CommandDelegate = UPD_ScanHighOrEqual, CommandCode = 0x1d, MT = true, MF = true, SK = true, IsRead = true, - Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, - // scan low or equal - new Command { CommandDelegate = UPD_ScanLowOrEqual, CommandCode = 0x19, MT = true, MF = true, SK = true, IsRead = true, - Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, - // read deleted data - new Command { CommandDelegate = UPD_ReadDeletedData, CommandCode = 0x0c, MT = true, MF = true, SK = true, IsRead = true, - Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, - // write data - new Command { CommandDelegate = UPD_WriteData, CommandCode = 0x05, MT = true, MF = true, IsWrite = true, - Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, - // write id - new Command { CommandDelegate = UPD_WriteID, CommandCode = 0x0d, MF = true, IsWrite = true, - Direction = CommandDirection.IN, ParameterByteCount = 5, ResultByteCount = 7 }, - // write deleted data - new Command { CommandDelegate = UPD_WriteDeletedData, CommandCode = 0x09, MT = true, MF = true, IsWrite = true, - Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, - // seek - new Command { CommandDelegate = UPD_Seek, CommandCode = 0x0f, - Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, - // recalibrate (seek track00) - new Command { CommandDelegate = UPD_Recalibrate, CommandCode = 0x07, - Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 }, - // sense interrupt status - new Command { CommandDelegate = UPD_SenseInterruptStatus, CommandCode = 0x08, - Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 }, - // sense drive status - new Command { CommandDelegate = UPD_SenseDriveStatus, CommandCode = 0x04, - Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 }, - // version - new Command { CommandDelegate = UPD_Version, CommandCode = 0x10, - Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 }, - // invalid - new Command { CommandDelegate = UPD_Invalid, CommandCode = 0x00, - Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 }, - }; - } - - #endregion - - #region State Serialization - - public void SyncState(Serializer ser) - { - void SyncFDDState(Serializer ser1) - { - ser1.Sync(nameof(FDD_FLAG_MOTOR), ref FDD_FLAG_MOTOR); - - for (int i = 0; i < 4; i++) - { - ser1.BeginSection("HITDrive_" + i); - DriveStates[i].SyncState(ser1); - ser1.EndSection(); - } - - ser1.Sync(nameof(DiskDriveIndex), ref _diskDriveIndex); - // set active drive - DiskDriveIndex = _diskDriveIndex; - } - - void SyncRegisterState(Serializer ser1) - { - ser1.Sync("_RegMain", ref StatusMain); - ser1.Sync("_Reg0", ref Status0); - ser1.Sync("_Reg1", ref Status1); - ser1.Sync("_Reg2", ref Status2); - ser1.Sync("_Reg3", ref Status3); - } - - void SyncControllerState(Serializer ser1) - { - ser1.Sync(nameof(DriveLight), ref DriveLight); - ser1.SyncEnum(nameof(ActivePhase), ref ActivePhase); -#if false - ser1.SyncEnum(nameof(ActiveDirection), ref ActiveDirection); -#endif - ser1.SyncEnum(nameof(ActiveInterrupt), ref ActiveInterrupt); - ser1.Sync(nameof(CommBuffer), ref CommBuffer, false); - ser1.Sync(nameof(CommCounter), ref CommCounter); - ser1.Sync(nameof(ResBuffer), ref ResBuffer, false); - ser1.Sync(nameof(ExecBuffer), ref ExecBuffer, false); - ser1.Sync(nameof(ExecCounter), ref ExecCounter); - ser1.Sync(nameof(ExecLength), ref ExecLength); - ser1.Sync(nameof(InterruptResultBuffer), ref InterruptResultBuffer, false); - ser1.Sync(nameof(ResCounter), ref ResCounter); - ser1.Sync(nameof(ResLength), ref ResLength); - ser1.Sync(nameof(LastSectorDataWriteByte), ref LastSectorDataWriteByte); - ser1.Sync(nameof(LastSectorDataReadByte), ref LastSectorDataReadByte); - ser1.Sync(nameof(LastByteReceived), ref LastByteReceived); - - ser1.Sync(nameof(_cmdIndex), ref _cmdIndex); - // resync the ActiveCommand - CMDIndex = _cmdIndex; - - ActiveCommandParams.SyncState(ser1); - - ser1.Sync(nameof(IndexPulseCounter), ref IndexPulseCounter); -#if false - ser1.SyncEnum(nameof(_activeStatus), ref _activeStatus); - ser1.SyncEnum(nameof(_statusRaised), ref _statusRaised); -#endif - - ser1.Sync(nameof(CMD_FLAG_MT), ref CMD_FLAG_MT); - ser1.Sync(nameof(CMD_FLAG_MF), ref CMD_FLAG_MF); - ser1.Sync(nameof(CMD_FLAG_SK), ref CMD_FLAG_SK); - ser1.Sync(nameof(SRT), ref SRT); - ser1.Sync(nameof(HUT), ref HUT); - ser1.Sync(nameof(HLT), ref HLT); - ser1.Sync(nameof(ND), ref ND); - ser1.Sync(nameof(SRT_Counter), ref SRT_Counter); - ser1.Sync(nameof(HUT_Counter), ref HUT_Counter); - ser1.Sync(nameof(HLT_Counter), ref HLT_Counter); - - ser1.Sync(nameof(SectorDelayCounter), ref SectorDelayCounter); - ser1.Sync(nameof(SectorID), ref SectorID); - } - - void SyncTimingState(Serializer ser1) - { - ser1.Sync(nameof(LastCPUCycle), ref LastCPUCycle); - ser1.Sync(nameof(StatusDelay), ref StatusDelay); - ser1.Sync(nameof(TickCounter), ref TickCounter); - ser1.Sync(nameof(DriveCycleCounter), ref DriveCycleCounter); - } - - ser.BeginSection("NEC-UPD765"); - SyncFDDState(ser); - SyncRegisterState(ser); - SyncControllerState(ser); - SyncTimingState(ser); - ser.EndSection(); - } - - #endregion - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765Spectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765Spectrum.cs new file mode 100644 index 0000000000..ef4f76538c --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765Spectrum.cs @@ -0,0 +1,75 @@ +using System; + +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public class NECUPD765Spectrum : NECUPD765 + { + protected override ZXDriveState ConstructDriveState(int driveID, NECUPD765 fdc) => new ZXDriveState(driveID, fdc); + + protected override void TimingInit() + { + // z80 timing + double frameSize = _machine.ULADevice.FrameLength; + double rRate = _machine.ULADevice.ClockSpeed / frameSize; + long tPerSecond = (long)(frameSize * rRate); + CPUCyclesPerMs = tPerSecond / 1000; + + // drive timing + double dRate = DriveClock / frameSize; + long dPerSecond = (long)(frameSize * dRate); + DriveCyclesPerMs = dPerSecond / 1000; + + long TStatesPerDriveCycle = (long)((double)_machine.ULADevice.ClockSpeed / DriveClock); + StatesPerDriveTick = TStatesPerDriveCycle; + } + + public class ZXDriveState : NECUPD765DriveState + { + public ZXDriveState(int driveID, NECUPD765 fdc) : base(driveID, fdc) {} + + public override void FDD_LoadDisk(byte[] diskData) + { + // try dsk first + FloppyDisk fdd = null; + bool found = false; + + foreach (DiskType type in Enum.GetValues(typeof(DiskType))) + { + switch (type) + { + case DiskType.CPCExtended: + fdd = new CPCExtendedFloppyDisk(); + found = fdd.ParseDisk(diskData); + break; + case DiskType.CPC: + fdd = new CPCFloppyDisk(); + found = fdd.ParseDisk(diskData); + break; + case DiskType.IPF: + fdd = new IPFFloppyDisk(); + found = fdd.ParseDisk(diskData); + break; + case DiskType.UDI: + fdd = new UDI1_0FloppyDisk(); + found = fdd.ParseDisk(diskData); + break; + } + + if (found) + { + Disk = fdd; + break; + } + } + + if (!found) + { + throw new Exception(this.GetType().ToString() + + "\n\nDisk image file could not be parsed. Potentially an unknown format."); + } + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs deleted file mode 100644 index 276bd41ccd..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.Collections; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Static helper methods - /// - #region Attribution - /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ - #endregion - public partial class NECUPD765 - { - /// - /// Returns the specified bit value from supplied byte - /// - public static bool GetBit(int bitNumber, byte dataByte) - { - if (bitNumber < 0 || bitNumber > 7) - return false; - - BitArray bi = new BitArray(new byte[] { dataByte }); - - return bi[bitNumber]; - } - - /// - /// Sets the specified bit of the supplied byte to 1 - /// - public static void SetBit(int bitNumber, ref byte dataByte) - { - if (bitNumber < 0 || bitNumber > 7) - return; - - int db = (int)dataByte; - - db |= 1 << bitNumber; - - dataByte = (byte)db; - } - - /// - /// Sets the specified bit of the supplied byte to 0 - /// - public static void UnSetBit(int bitNumber, ref byte dataByte) - { - if (bitNumber < 0 || bitNumber > 7) - return; - - int db = (int)dataByte; - - db &= ~(1 << bitNumber); - - dataByte = (byte)db; - } - - /// - /// Returns a drive number (0-3) based on the first two bits of the supplied byte - /// - public static int GetUnitSelect(byte dataByte) - { - int driveNumber = dataByte & 0x03; - return driveNumber; - } - - /// - /// Sets the first two bits of a byte based on the supplied drive number (0-3) - /// - public static void SetUnitSelect(int driveNumber, ref byte dataByte) - { - switch (driveNumber) - { - case 0: - UnSetBit(SR0_US0, ref dataByte); - UnSetBit(SR0_US1, ref dataByte); - break; - case 1: - SetBit(SR0_US0, ref dataByte); - UnSetBit(SR0_US1, ref dataByte); - break; - case 2: - SetBit(SR0_US1, ref dataByte); - UnSetBit(SR0_US0, ref dataByte); - break; - case 3: - SetBit(SR0_US0, ref dataByte); - SetBit(SR0_US1, ref dataByte); - break; - } - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs index 57fc3c867b..a32427aa19 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs @@ -1,13 +1,15 @@ using System; using System.Collections.Generic; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// /// Cursor joystick /// Maps to a combination of 0xf7fe and 0xeffe /// - public class CursorJoystick : IJoystick + public class CursorJoystick : IJoystick { //private int _joyLine; private SpectrumBase _machine; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs index 3a1040d045..9c8813e0c9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { - public class KempstonJoystick : IJoystick + public class KempstonJoystick : IJoystick { private int _joyLine; private SpectrumBase _machine; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs index 95bbb879a9..2185bca85d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// /// A null joystick object /// - public class NullJoystick : IJoystick + public class NullJoystick : IJoystick { private int _joyLine; private SpectrumBase _machine; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs index 9908f344de..8004a2a4dc 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs @@ -1,13 +1,15 @@ using System; using System.Collections.Generic; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// /// Sinclair Joystick LEFT /// Just maps to the standard keyboard and is read the same (from port 0xf7fe) /// - public class SinclairJoystick1 : IJoystick + public class SinclairJoystick1 : IJoystick { //private int _joyLine; private SpectrumBase _machine; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs index a01c4435d9..9c28d65ff1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs @@ -1,13 +1,15 @@ using System; using System.Collections.Generic; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// /// Sinclair Joystick RIGHT /// Just maps to the standard keyboard and is read the same (from port 0xeffe) /// - public class SinclairJoystick2 : IJoystick + public class SinclairJoystick2 : IJoystick { //private int _joyLine; private SpectrumBase _machine; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index bac2ae50c5..466bd0dcc7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Linq; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// @@ -280,7 +282,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// protected void InitJoysticks(List joys) { - var jCollection = new List(); + var jCollection = new List>(); for (int i = 0; i < joys.Count(); i++) { @@ -298,7 +300,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Instantiates a new IJoystick object /// - public IJoystick InstantiateJoystick(JoystickType type, int playerNumber) + public IJoystick InstantiateJoystick(JoystickType type, int playerNumber) { switch (type) { @@ -320,7 +322,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Returns a IJoystick object depending on the type (or null if not found) /// - protected IJoystick LocateUniqueJoystick(JoystickType type) + protected IJoystick LocateUniqueJoystick(JoystickType type) { return JoystickCollection.FirstOrDefault(a => a.JoyType == type); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs index 2f4fc56688..5090eda0dd 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index c218adbca6..ed7db3469e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -1,5 +1,6 @@ using BizHawk.Common; using BizHawk.Emulation.Cores.Components.Z80A; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; using BizHawk.Emulation.Cores.Sound; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum @@ -8,7 +9,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// The abstract class that all emulated models will inherit from /// * Main properties / fields / contruction* /// - public abstract partial class SpectrumBase + public abstract partial class SpectrumBase : CPCSpectrumBase.CPCSpectrumBase { #region Devices @@ -66,12 +67,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The +3 built-in disk drive /// - public virtual NECUPD765 UPDDiskDevice { get; set; } + public virtual NECUPD765Spectrum UPDDiskDevice { get; set; } /// /// Holds the currently selected joysticks /// - public virtual IJoystick[] JoystickCollection { get; set; } + public virtual IJoystick[] JoystickCollection { get; set; } /// /// +3/2a printer port strobe diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index cb3bb54eab..cc8bbc565e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -42,7 +42,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape); TapeDevice.Init(this); - UPDDiskDevice = new NECUPD765(); + UPDDiskDevice = new NECUPD765Spectrum(); UPDDiskDevice.Init(this); InitializeMedia(files); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFormat/CPCExtendedFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFormat/CPCExtendedFloppyDisk.cs index 3407d88e16..caa61f03c9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFormat/CPCExtendedFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFormat/CPCExtendedFloppyDisk.cs @@ -3,6 +3,8 @@ using BizHawk.Common; using System; using System.Collections.Generic; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFormat/CPCFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFormat/CPCFloppyDisk.cs index 351229d774..8e3e09c02b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFormat/CPCFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFormat/CPCFloppyDisk.cs @@ -3,6 +3,8 @@ using BizHawk.Common; using System; using System.Collections.Generic; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs deleted file mode 100644 index 1058f98b4a..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs +++ /dev/null @@ -1,735 +0,0 @@ -using BizHawk.Common; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This abstract class defines a logical floppy disk - /// - public abstract class FloppyDisk - { - /// - /// The disk format type - /// - public abstract DiskType DiskFormatType { get; } - - /// - /// Disk information header - /// - public Header DiskHeader = new Header(); - - /// - /// Track array - /// - public Track[] DiskTracks = null; - - /// - /// No. of tracks per side - /// - public int CylinderCount; - - /// - /// The number of physical sides - /// - public int SideCount; - - /// - /// The number of bytes per track - /// - public int BytesPerTrack; - - /// - /// The write-protect tab on the disk - /// - public bool WriteProtected; - - /// - /// The detected protection scheme (if any) - /// - public ProtectionType Protection; - - /// - /// The actual disk image data - /// - public byte[] DiskData; - - /// - /// If TRUE then data on the disk has changed (been written to) - /// This will be used to determine whether the disk data needs to be included - /// in any SyncState operations - /// - protected bool DirtyData = false; - - /// - /// Used to deterministically choose a 'random' sector when dealing with weak reads - /// - public int RandomCounter - { - get => _randomCounter; - set - { - _randomCounter = value; - - foreach (var trk in DiskTracks) - { - foreach (var sec in trk.Sectors) - { - sec.RandSecCounter = _randomCounter; - } - } - } - } - protected int _randomCounter; - - - /// - /// Attempts to parse incoming disk data - /// - /// - /// TRUE: disk parsed - /// FALSE: unable to parse disk - /// - public virtual bool ParseDisk(byte[] diskData) - { - // default result - // override in inheriting class - return false; - } - - /// - /// Examines the floppydisk data to work out what protection (if any) is present - /// If possible it will also fix the disk data for this protection - /// This should be run at the end of the ParseDisk() method - /// - public virtual void ParseProtection() - { - int[] weakArr = new int[2]; - - // speedlock - if (DetectSpeedlock(ref weakArr)) - { - Protection = ProtectionType.Speedlock; - - Sector sec = DiskTracks[0].Sectors[1]; - if (!sec.ContainsMultipleWeakSectors) - { - byte[] origData = sec.SectorData.ToArray(); - List data = new List(); - for (int m = 0; m < 3; m++) - { - for (int i = 0; i < 512; i++) - { - // deterministic 'random' implementation - int n = origData[i] + m + 1; - if (n > 0xff) - n = n - 0xff; - else if (n < 0) - n = 0xff + n; - - byte nByte = (byte)n; - - if (m == 0) - { - data.Add(origData[i]); - continue; - } - - if (i < weakArr[0]) - { - data.Add(origData[i]); - } - - else if (weakArr[1] > 0) - { - data.Add(nByte); - weakArr[1]--; - } - - else - { - data.Add(origData[i]); - } - } - } - - sec.SectorData = data.ToArray(); - sec.ActualDataByteLength = data.Count(); - sec.ContainsMultipleWeakSectors = true; - } - } - else if (DetectAlkatraz(ref weakArr)) - { - Protection = ProtectionType.Alkatraz; - } - else if (DetectPaulOwens(ref weakArr)) - { - Protection = ProtectionType.PaulOwens; - } - else if (DetectHexagon(ref weakArr)) - { - Protection = ProtectionType.Hexagon; - } - else if (DetectShadowOfTheBeast()) - { - Protection = ProtectionType.ShadowOfTheBeast; - } - } - - /// - /// Detection routine for shadow of the beast game - /// Still cannot get this to work, but at least the game is detected - /// - public bool DetectShadowOfTheBeast() - { - if (DiskTracks[0].Sectors.Length != 9) - return false; - - var zeroSecs = DiskTracks[0].Sectors; - if (zeroSecs[0].SectorID != 65 || - zeroSecs[1].SectorID != 66 || - zeroSecs[2].SectorID != 67 || - zeroSecs[3].SectorID != 68 || - zeroSecs[4].SectorID != 69 || - zeroSecs[5].SectorID != 70 || - zeroSecs[6].SectorID != 71 || - zeroSecs[7].SectorID != 72 || - zeroSecs[8].SectorID != 73) - return false; - - var oneSecs = DiskTracks[1].Sectors; - - if (oneSecs.Length != 8) - return false; - - if (oneSecs[0].SectorID != 17 || - oneSecs[1].SectorID != 18 || - oneSecs[2].SectorID != 19 || - oneSecs[3].SectorID != 20 || - oneSecs[4].SectorID != 21 || - oneSecs[5].SectorID != 22 || - oneSecs[6].SectorID != 23 || - oneSecs[7].SectorID != 24) - return false; - - return true; - } - - /// - /// Detect speedlock weak sector - /// - public bool DetectSpeedlock(ref int[] weak) - { - // SPEEDLOCK NOTES (-asni 2018-05-01) - // --------------------------------- - // Speedlock is one of the more common +3 disk protections and there are a few different versions - // Usually, track 0 sector 1 (ID 2) has data CRC errors that result in certain bytes returning a different value every time they are read - // Speedlock will generally read this track a number of times during the load process - // and if the correct bytes are not different between reads, the load fails - - // always must have track 0 containing 9 sectors - if (DiskTracks[0].Sectors.Length != 9) - return false; - - // check for SPEEDLOCK ident in sector 0 - string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[0].SectorData, 0, DiskTracks[0].Sectors[0].SectorData.Length); - if (!ident.ToUpper().Contains("SPEEDLOCK")) - return false; - - // check for correct sector 0 lengths - if (DiskTracks[0].Sectors[0].SectorSize != 2 || - DiskTracks[0].Sectors[0].SectorData.Length < 0x200) - return false; - - // sector[1] (SectorID 2) contains the weak sectors - Sector sec = DiskTracks[0].Sectors[1]; - - // check for correct sector 1 lengths - if (sec.SectorSize != 2 || - sec.SectorData.Length < 0x200) - return false; - - // secID 2 needs a CRC error - //if (!(sec.Status1.Bit(5) || sec.Status2.Bit(5))) - //return false; - - // check for filler - bool startFillerFound = true; - for (int i = 0; i < 250; i++) - { - if (sec.SectorData[i] != sec.SectorData[i + 1]) - { - startFillerFound = false; - break; - } - } - - if (!startFillerFound) - { - weak[0] = 0; - weak[1] = 0x200; - } - else - { - weak[0] = 0x150; - weak[1] = 0x20; - } - - return true; - } - - /// - /// Detect Alkatraz - /// - public bool DetectAlkatraz(ref int[] weak) - { - try - { - var data1 = DiskTracks[0].Sectors[0].SectorData; - var data2 = DiskTracks[0].Sectors[0].SectorData.Length; - } - catch (Exception) - { - return false; - } - - // check for ALKATRAZ ident in sector 0 - string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[0].SectorData, 0, DiskTracks[0].Sectors[0].SectorData.Length); - if (!ident.ToUpper().Contains("ALKATRAZ PROTECTION SYSTEM")) - return false; - - // ALKATRAZ NOTES (-asni 2018-05-01) - // --------------------------------- - // Alkatraz protection appears to revolve around a track on the disk with 18 sectors, - // (track position is not consistent) with the sector ID info being incorrect: - // TrackID is consistent between the sectors although is usually high (233, 237 etc) - // SideID is fairly random looking but with all IDs being even - // SectorID is also fairly random looking but contains both odd and even numbers - // - // There doesnt appear to be any CRC errors in this track, but the sector size is always 1 (256 bytes) - // Each sector contains different filler byte - // Once track 0 is loaded the CPU completely reads all the sectors in this track one-by-one. - // Data transferred during execution must be correct, also result ST0, ST1 and ST2 must be 64, 128 and 0 respectively - - // Immediately following this track are a number of tracks and sectors with a DAM set. - // These are all read in sector by sector - // Again, Alkatraz appears to require that ST0, ST1, and ST2 result bytes are set to 64, 128 and 0 respectively - // (so the CM in ST2 needs to be reset) - - return true; - } - - /// - /// Detect Paul Owens - /// - public bool DetectPaulOwens(ref int[] weak) - { - try - { - var data1 = DiskTracks[0].Sectors[2].SectorData; - var data2 = DiskTracks[0].Sectors[2].SectorData.Length; - } - catch (Exception) - { - return false; - } - - // check for PAUL OWENS ident in sector 2 - string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[2].SectorData, 0, DiskTracks[0].Sectors[2].SectorData.Length); - if (!ident.ToUpper().Contains("PAUL OWENS")) - return false; - - // Paul Owens Disk Protection Notes (-asni 2018-05-01) - // --------------------------------------------------- - // - // This scheme looks a little similar to Alkatraz with incorrect sector ID info in many places - // and deleted address marks (although these do not seem to show the strict relience on removing the CM mark from ST2 result that Alkatraz does) - // There are also data CRC errors but these don't look to be read more than once/checked for changes during load - // Main identifiers: - // - // * There are more than 10 cylinders - // * Cylinder 1 has no sector data - // * The sector ID infomation in most cases contains incorrect track IDs - // * Tracks 0 (boot) and 5 appear to be pretty much the only tracks that do not have incorrect sector ID marks - - return true; - } - - /// - /// Detect Hexagon copy protection - /// - public bool DetectHexagon(ref int[] weak) - { - try - { - var data1 = DiskTracks[0].Sectors.Length; - var data2 = DiskTracks[0].Sectors[8].ActualDataByteLength; - var data3 = DiskTracks[0].Sectors[8].SectorData; - var data4 = DiskTracks[0].Sectors[8].SectorData.Length; - var data5 = DiskTracks[1].Sectors[0]; - } - catch (Exception) - { - return false; - } - - if (DiskTracks[0].Sectors.Length != 10 || DiskTracks[0].Sectors[8].ActualDataByteLength != 512) - return false; - - // check for Hexagon ident in sector 8 - string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[8].SectorData, 0, DiskTracks[0].Sectors[8].SectorData.Length); - if (ident.ToUpper().Contains("GON DISK PROT")) - return true; - - // hexagon protection may not be labelled as such - var track = DiskTracks[1]; - var sector = track.Sectors[0]; - - if (sector.SectorSize == 6 && sector.Status1 == 0x20 && sector.Status2 == 0x60) - { - if (track.Sectors.Length == 1) - return true; - } - - - // Hexagon Copy Protection Notes (-asni 2018-05-01) - // --------------------------------------------------- - // - // - - return false; - } - - /* - /// - /// Should be run at the end of the ParseDisk process - /// If speedlock is detected the flag is set in the disk image - /// - protected virtual void SpeedlockDetection() - { - - if (DiskTracks.Length == 0) - return; - - // check for speedlock copyright notice - string ident = Encoding.ASCII.GetString(DiskData, 0x100, 0x1400); - if (!ident.ToUpper().Contains("SPEEDLOCK")) - { - // speedlock not found - return; - } - - // get cylinder 0 - var cyl = DiskTracks[0]; - - // get sector with ID=2 - var sec = cyl.Sectors.Where(a => a.SectorID == 2).FirstOrDefault(); - - if (sec == null) - return; - - // check for already multiple weak copies - if (sec.ContainsMultipleWeakSectors || sec.SectorData.Length != 0x80 << sec.SectorSize) - return; - - // check for invalid crcs in sector 2 - if (sec.Status1.Bit(5) || sec.Status2.Bit(5)) - { - Protection = ProtectionType.Speedlock; - } - else - { - return; - } - - // we are going to create a total of 5 weak sector copies - // keeping the original copy - byte[] origData = sec.SectorData.ToArray(); - List data = new List(); - //Random rnd = new Random(); - - for (int i = 0; i < 6; i++) - { - for (int s = 0; s < origData.Length; s++) - { - if (i == 0) - { - data.Add(origData[s]); - continue; - } - - // deterministic 'random' implementation - int n = origData[s] + i + 1; - if (n > 0xff) - n = n - 0xff; - else if (n < 0) - n = 0xff + n; - - byte nByte = (byte)n; - - if (s < 336) - { - // non weak data - data.Add(origData[s]); - } - else if (s < 511) - { - // weak data - data.Add(nByte); - } - else if (s == 511) - { - // final sector byte - data.Add(nByte); - } - else - { - // speedlock sector should not be more than 512 bytes - // but in case it is just do non weak - data.Add(origData[i]); - } - } - } - - // commit the sector data - sec.SectorData = data.ToArray(); - sec.ContainsMultipleWeakSectors = true; - sec.ActualDataByteLength = data.Count(); - - } - */ - - /// - /// Returns the track count for the disk - /// - public virtual int GetTrackCount() - { - return DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; - } - - /// - /// Reads the current sector ID info - /// - public virtual CHRN ReadID(byte trackIndex, byte side, int sectorIndex) - { - if (side != 0) - return null; - - if (DiskTracks.Length <= trackIndex || trackIndex < 0) - { - // invalid track - wrap around - trackIndex = 0; - } - - var track = DiskTracks[trackIndex]; - - if (track.NumberOfSectors <= sectorIndex) - { - // invalid sector - wrap around - sectorIndex = 0; - } - - var sector = track.Sectors[sectorIndex]; - - CHRN chrn = new CHRN(); - - chrn.C = sector.TrackNumber; - chrn.H = sector.SideNumber; - chrn.R = sector.SectorID; - - // wrap around for N > 7 - if (sector.SectorSize > 7) - { - chrn.N = (byte)(sector.SectorSize - 7); - } - else if (sector.SectorSize < 0) - { - chrn.N = 0; - } - else - { - chrn.N = sector.SectorSize; - } - - chrn.Flag1 = (byte)(sector.Status1 & 0x25); - chrn.Flag2 = (byte)(sector.Status2 & 0x61); - - chrn.DataBytes = sector.ActualData; - - return chrn; - } - - /// - /// State serialization routines - /// - public abstract void SyncState(Serializer ser); - - - public class Header - { - public string DiskIdent { get; set; } - public string DiskCreatorString { get; set; } - public byte NumberOfTracks { get; set; } - public byte NumberOfSides { get; set; } - public int[] TrackSizes { get; set; } - } - - public class Track - { - public string TrackIdent { get; set; } - public byte TrackNumber { get; set; } - public byte SideNumber { get; set; } - public byte DataRate { get; set; } - public byte RecordingMode { get; set; } - public byte SectorSize { get; set; } - public byte NumberOfSectors { get; set; } - public byte GAP3Length { get; set; } - public byte FillerByte { get; set; } - public virtual Sector[] Sectors { get; set; } - - #region UDI - - public virtual byte TrackType { get; set; } - public virtual int TLEN { get; set; } - public virtual int CLEN => TLEN / 8 + (TLEN % 8 / 7) / 8; - public virtual byte[] TrackData { get; set; } - - #endregion - - /// - /// Presents a contiguous byte array of all sector data for this track - /// (including any multiple weak/random data) - /// - public virtual byte[] TrackSectorData - { - get - { - List list = new List(); - - foreach (var sec in Sectors) - { - list.AddRange(sec.ActualData); - } - - return list.ToArray(); - } - } - } - - public class Sector - { - public virtual byte TrackNumber { get; set; } - public virtual byte SideNumber { get; set; } - public virtual byte SectorID { get; set; } - public virtual byte SectorSize { get; set; } - public virtual byte Status1 { get; set; } - public virtual byte Status2 { get; set; } - public virtual int ActualDataByteLength { get; set; } - public virtual byte[] SectorData { get; set; } - public virtual bool ContainsMultipleWeakSectors { get; set; } - - public int WeakReadIndex = 0; - - public void SectorReadCompleted() - { - if (ContainsMultipleWeakSectors) - WeakReadIndex++; - } - - public int DataLen - { - get - { - if (!ContainsMultipleWeakSectors) - { - return ActualDataByteLength; - } - - return ActualDataByteLength / (ActualDataByteLength / (0x80 << SectorSize)); - } - } - - - public int RandSecCounter = 0; - - public byte[] ActualData - { - get - { - if (!ContainsMultipleWeakSectors) - { - // check whether filler bytes are needed - int size = 0x80 << SectorSize; - if (size > ActualDataByteLength) - { - List l = new List(); - l.AddRange(SectorData); - for (int i = 0; i < size - ActualDataByteLength; i++) - { - //l.Add(SectorData[i]); - l.Add(SectorData.Last()); - } - - return l.ToArray(); - } - - return SectorData; - } - else - { - // weak read neccessary - int copies = ActualDataByteLength / (0x80 << SectorSize); - - // handle index wrap-around - if (WeakReadIndex > copies - 1) - WeakReadIndex = copies - 1; - - // get the sector data based on the current weakreadindex - int step = WeakReadIndex * (0x80 << SectorSize); - byte[] res = new byte[(0x80 << SectorSize)]; - Array.Copy(SectorData, step, res, 0, 0x80 << SectorSize); - return res; - - /* - int copies = ActualDataByteLength / (0x80 << SectorSize); - Random rnd = new Random(); - int r = rnd.Next(0, copies - 1); - int step = r * (0x80 << SectorSize); - byte[] res = new byte[(0x80 << SectorSize)]; - Array.Copy(SectorData, step, res, 0, 0x80 << SectorSize); - return res; - */ - } - } - } - - public CHRN SectorIDInfo => - new CHRN - { - C = TrackNumber, - H = SideNumber, - R = SectorID, - N = SectorSize, - Flag1 = Status1, - Flag2 = Status2, - }; - } - } - - /// - /// Defines the type of speedlock detection found - /// - public enum ProtectionType - { - None, - Speedlock, - Alkatraz, - Hexagon, - Frontier, - PaulOwens, - ShadowOfTheBeast - } - -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/IPFFormat/IPFFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/IPFFormat/IPFFloppyDisk.cs index 96e446c2c2..85489454bf 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/IPFFormat/IPFFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/IPFFormat/IPFFloppyDisk.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public class IPFFloppyDisk : FloppyDisk diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/UDIFormat/UDI1_0FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/UDIFormat/UDI1_0FloppyDisk.cs index 95cd1ea1d0..5b565f289f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/UDIFormat/UDI1_0FloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/UDIFormat/UDI1_0FloppyDisk.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public class UDI1_0FloppyDisk : FloppyDisk diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/CSW/CswConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/CSW/CswConverter.cs index 479571ebc4..2be656af72 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/CSW/CswConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/CSW/CswConverter.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; using System.Text; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs index 7f1b1b29dc..5d10ee4c1f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; using System.Text; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapConverter.cs index 3016b7aba0..7e072ffc44 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapConverter.cs @@ -4,6 +4,8 @@ using System.IO; using System.Linq; using System.Text; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs index 94a5f21f97..862ec85708 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs deleted file mode 100644 index 28383dd838..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs +++ /dev/null @@ -1,16 +0,0 @@ - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Represents the possible commands that can be raised from each tape block - /// - public enum TapeCommand - { - NONE, - STOP_THE_TAPE, - STOP_THE_TAPE_48K, - BEGIN_GROUP, - END_GROUP, - SHOW_MESSAGE, - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs deleted file mode 100644 index b05036d7ef..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs +++ /dev/null @@ -1,282 +0,0 @@ -using BizHawk.Common; -using System.Collections.Generic; -using System.Linq; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Represents a tape block - /// - public class TapeDataBlock - { - /// - /// Either the TZX block ID, or -1 in the case of non-tzx blocks - /// - private int _blockID = -1; - public int BlockID - { - get => _blockID; - set - { - _blockID = value; - - if (MetaData == null) - MetaData = new Dictionary(); - - AddMetaData(BlockDescriptorTitle.Block_ID, value.ToString()); - } - } - - /// - /// The block type - /// - private BlockType _blockType; - public BlockType BlockDescription - { - get => _blockType; - set - { - _blockType = value; - if (MetaData == null) - MetaData = new Dictionary(); - } - } - - /// - /// Byte array containing the raw block data - /// - private byte[] _blockData; - public byte[] BlockData - { - get => _blockData; - set => _blockData = value; - } - - /* - - /// - /// An array of bytearray encoded strings (stored in this format for easy Bizhawk serialization) - /// Its basically tape information - /// - private byte[][] _tapeDescriptionData; - - /// - /// Returns the Tape Description Data in a human readable format - /// - public List TapeDescriptionData - { - get - { - List data = new List(); - - foreach (byte[] b in _tapeDescriptionData) - { - data.Add(Encoding.ASCII.GetString(b)); - } - - return data; - } - } - */ - - - #region Block Meta Data - - /// - /// Dictionary of block related data - /// - public Dictionary MetaData { get; set; } - - /// - /// Adds a single metadata item to the Dictionary - /// - public void AddMetaData(BlockDescriptorTitle descriptor, string data) - { - // check whether entry already exists - bool check = MetaData.ContainsKey(descriptor); - if (check) - { - // already exists - update - MetaData[descriptor] = data; - } - else - { - // create new - MetaData.Add(descriptor, data); - } - } - - #endregion - - - - /// - /// List containing the pulse timing values - /// - public List DataPeriods = new List(); - - public bool InitialPulseLevel; - - /// - /// Command that is raised by this data block - /// (that may or may not need to be acted on) - /// - private TapeCommand _command = TapeCommand.NONE; - public TapeCommand Command - { - get => _command; - set => _command = value; - } - - /// - /// The defined post-block pause - /// - private int _pauseInMS; - public int PauseInMS - { - get => _pauseInMS; - set => _pauseInMS = value; - } - - - /// - /// Returns the data periods as an array - /// (primarily to aid in bizhawk state serialization) - /// - public int[] GetDataPeriodsArray() - { - return DataPeriods.ToArray(); - } - - /// - /// Accepts an array of data periods and updates the DataPeriods list accordingly - /// (primarily to aid in bizhawk state serialization) - /// - public void SetDataPeriodsArray(int[] periodArray) - { - DataPeriods = periodArray?.ToList() ?? new List(); - } - - /// - /// Bizhawk state serialization - /// - public void SyncState(Serializer ser, int blockPosition) - { - ser.BeginSection("DataBlock" + blockPosition); - - ser.Sync(nameof(_blockID), ref _blockID); - //ser.SyncFixedString(nameof(_blockDescription), ref _blockDescription, 200); - ser.SyncEnum(nameof(_blockType), ref _blockType); - ser.Sync(nameof(_blockData), ref _blockData, true); - ser.SyncEnum(nameof(_command), ref _command); - - int[] tempArray = null; - - if (ser.IsWriter) - { - tempArray = GetDataPeriodsArray(); - ser.Sync("_periods", ref tempArray, true); - } - else - { - ser.Sync("_periods", ref tempArray, true); - SetDataPeriodsArray(tempArray); - } - - ser.EndSection(); - } - } - - /// - /// The types of TZX blocks - /// - public enum BlockType - { - Standard_Speed_Data_Block = 0x10, - Turbo_Speed_Data_Block = 0x11, - Pure_Tone = 0x12, - Pulse_Sequence = 0x13, - Pure_Data_Block = 0x14, - Direct_Recording = 0x15, - CSW_Recording = 0x18, - Generalized_Data_Block = 0x19, - Pause_or_Stop_the_Tape = 0x20, - Group_Start = 0x21, - Group_End = 0x22, - Jump_to_Block = 0x23, - Loop_Start = 0x24, - Loop_End = 0x25, - Call_Sequence = 0x26, - Return_From_Sequence = 0x27, - Select_Block = 0x28, - Stop_the_Tape_48K = 0x2A, - Set_Signal_Level = 0x2B, - Text_Description = 0x30, - Message_Block = 0x31, - Archive_Info = 0x32, - Hardware_Type = 0x33, - Custom_Info_Block = 0x35, - Glue_Block = 0x5A, - - // depreciated blocks - C64_ROM_Type_Data_Block = 0x16, - C64_Turbo_Tape_Data_Block = 0x17, - Emulation_Info = 0x34, - Snapshot_Block = 0x40, - - // unsupported / undetected - Unsupported, - - // PZX blocks - PZXT, - PULS, - DATA, - BRWS, - PAUS, - - // zxhawk proprietry - PAUSE_BLOCK, - - WAV_Recording - } - - - /// - /// Different title possibilities - /// - public enum BlockDescriptorTitle - { - Undefined, - Block_ID, - Program, - Data_Bytes, - Bytes, - - Pilot_Pulse_Length, - Pilot_Pulse_Count, - First_Sync_Length, - Second_Sync_Length, - Zero_Bit_Length, - One_Bit_Length, - Data_Length, - Bits_In_Last_Byte, - Pause_After_Data, - - Pulse_Length, - Pulse_Count, - - Text_Description, - Title, - Publisher, - Author, - Year, - Language, - Type, - Price, - Protection, - Origin, - Comments, - - Needs_Parsing - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavConverter.cs index 8d1780c193..c0a3a156b8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavConverter.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.IO; using System.Text; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs index c5be96f6b6..9bd459a7c4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs @@ -2,6 +2,8 @@ using System.Linq; using System.Text; +using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { ///