using BizHawk.Common; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; 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("UnitSelect", ref UnitSelect); ser.Sync("Side", ref Side); ser.Sync("Cylinder", ref Cylinder); ser.Sync("Head", ref Head); ser.Sync("Sector", ref Sector); ser.Sync("SectorSize", ref SectorSize); ser.Sync("EOT", ref EOT); ser.Sync("Gap3Length", ref Gap3Length); ser.Sync("DTL", ref DTL); ser.EndSection(); } } #endregion } }