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
}
}