BizHawk/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs

827 lines
29 KiB
C#
Raw Normal View History

2018-04-26 11:54:10 +00:00
using BizHawk.Common;
using System;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Definitions
/// </summary>
#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
/// <summary>
/// Defines the current phase of the controller
/// </summary>
private enum Phase
{
/// <summary>
/// FDC is in an idle state, awaiting the next initial command byte
/// </summary>
Idle,
/// <summary>
/// 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
/// </summary>
Command,
/// <summary>
/// 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
/// </summary>
Execution,
/// <summary>
/// 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.
/// </summary>
Result
}
/// <summary>
/// The lifecycle of an instruction
/// Similar to phase, this describes the current 'sub-phase' we are in when dealing with an instruction
/// </summary>
private enum InstructionState
{
/// <summary>
/// FDC has received a command byte and is currently reading parameter bytes from the data bus
/// </summary>
ReceivingParameters,
/// <summary>
/// All parameter bytes have been received. This phase allows any neccessary setup before instruction execution starts
/// </summary>
PreExecution,
/// <summary>
/// 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
/// </summary>
StartExecute,
/// <summary>
/// Data is read or written in execution phase
/// </summary>
ExecutionReadWrite,
/// <summary>
/// Execution phase is well under way. This state primarily deals with data transfer between CPU and FDC
/// </summary>
ExecutionWrite,
/// <summary>
/// Execution phase is well under way. This state primarily deals with data transfer between FDC and CPU
/// </summary>
ExecutionRead,
/// <summary>
/// Execution has finished and results bytes are ready to be read by the CPU
/// Initial result setup
/// </summary>
StartResult,
/// <summary>
/// Result processing
/// </summary>
ProcessResult,
/// <summary>
/// Results are being sent
/// </summary>
SendingResults,
/// <summary>
/// Final cleanup tasks when the instruction has fully completed
/// </summary>
Completed
}
/// <summary>
/// Represents internal interrupt state of the FDC
/// </summary>
public enum InterruptState
{
/// <summary>
/// There is no interrupt
/// </summary>
None,
/// <summary>
/// Execution interrupt
/// </summary>
Execution,
/// <summary>
/// Result interrupt
/// </summary>
Result,
/// <summary>
/// Ready interrupt
/// </summary>
Ready,
/// <summary>
/// Seek interrupt
/// </summary>
Seek
}
/// <summary>
/// Possible main states that each drive can be in
/// </summary>
public enum DriveMainState
{
/// <summary>
/// Drive is not doing anything
/// </summary>
None,
/// <summary>
/// Seek operation is in progress
/// </summary>
Seek,
/// <summary>
/// Recalibrate operation is in progress
/// </summary>
Recalibrate,
/// <summary>
/// A scan data operation is in progress
/// </summary>
Scan,
/// <summary>
/// A read ID operation is in progress
/// </summary>
ReadID,
/// <summary>
/// A read data operation is in progress
/// </summary>
ReadData,
/// <summary>
/// A read diagnostic (read track) operation is in progress
/// </summary>
ReadDiagnostic,
/// <summary>
/// A write id (format track) operation is in progress
/// </summary>
WriteID,
/// <summary>
/// A write data operation is in progress
/// </summary>
WriteData,
}
/// <summary>
/// State information during a seek/recalibration operation
/// </summary>
public enum SeekSubState
{
/// <summary>
/// Seek hasnt started yet
/// </summary>
Idle,
/// <summary>
/// Delayed
/// </summary>
Wait,
/// <summary>
/// Setup for head move
/// </summary>
MoveInit,
/// <summary>
/// Seek is currently happening
/// </summary>
HeadMove,
/// <summary>
/// Head move with no delay
/// </summary>
MoveImmediate,
/// <summary>
/// Ready to complete
/// </summary>
PerformCompletion,
/// <summary>
/// Seek operation has completed
/// </summary>
SeekCompleted
}
/// <summary>
/// Seek int code
/// </summary>
public enum SeekIntStatus
{
Normal,
Abnormal,
DriveNotReady,
}
/// <summary>
/// The direction of a specific command
/// </summary>
private enum CommandDirection
{
/// <summary>
/// Data flows from UPD765A to Z80
/// </summary>
OUT,
/// <summary>
/// Data flows from Z80 to UPD765A
/// </summary>
IN
}
/// <summary>
/// Enum defining the different types of result that can be returned
/// </summary>
private enum ResultType
{
/// <summary>
/// Standard 7 result bytes are returned
/// </summary>
Standard,
/// <summary>
/// 1 byte returned - ST3
/// (used for SenseDriveStatus)
/// </summary>
ST3,
/// <summary>
/// 1 byte returned - ST0
/// (used for version & invalid)
/// </summary>
ST0,
/// <summary>
/// 2 bytes returned for sense interrupt status command
/// ST0
/// CurrentCylinder
/// </summary>
Interrupt
}
/// <summary>
/// Possible list of encountered drive status errors
/// </summary>
public enum Status
{
/// <summary>
/// No error detected
/// </summary>
None,
/// <summary>
/// An undefined error has been detected
/// </summary>
Undefined,
/// <summary>
/// Drive is not ready
/// </summary>
DriveNotReady,
/// <summary>
/// Invalid command received
/// </summary>
Invalid,
/// <summary>
/// The disk has its write protection tab enabled
/// </summary>
WriteProtected,
/// <summary>
/// The requested sector has not been found
/// </summary>
SectorNotFound
}
/// <summary>
/// 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)
/// </summary>
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;
/// <summary>
/// C - Track
/// </summary>
public const int CM_C = 1;
/// <summary>
/// H - Side
/// </summary>
public const int CM_H = 2;
/// <summary>
/// R - Sector ID
/// </summary>
public const int CM_R = 3;
/// <summary>
/// N - Sector size
/// </summary>
public const int CM_N = 4;
/// <summary>
/// EOT - End of track
/// </summary>
public const int CM_EOT = 5;
/// <summary>
/// GPL - Gap length
/// </summary>
public const int CM_GPL = 6;
/// <summary>
/// DTL - Data length
/// </summary>
public const int CM_DTL = 7;
/// <summary>
/// STP - Step
/// </summary>
public const int CM_STP = 7;
// Result Instruction Constants
// Designates the default postitions within the cmdbuffer array
/// <summary>
/// Status register 0
/// </summary>
public const int RS_ST0 = 0;
/// <summary>
/// Status register 1
/// </summary>
public const int RS_ST1 = 1;
/// <summary>
/// Status register 2
/// </summary>
public const int RS_ST2 = 2;
/// <summary>
/// C - Track
/// </summary>
public const int RS_C = 3;
/// <summary>
/// H - Side
/// </summary>
public const int RS_H = 4;
/// <summary>
/// R - Sector ID
/// </summary>
public const int RS_R = 5;
/// <summary>
/// N - Sector size
/// </summary>
public const int RS_N = 6;
// Main Status Register Constants
// Designates the bit positions within the Main status register
/// <summary>
/// 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.
/// </summary>
public const int MSR_D0B = 0;
/// <summary>
/// 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.
/// </summary>
public const int MSR_D1B = 1;
/// <summary>
/// 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.
/// </summary>
public const int MSR_D2B = 2;
/// <summary>
/// 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.
/// </summary>
public const int MSR_D3B = 3;
/// <summary>
/// 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
/// </summary>
public const int MSR_CB = 4;
/// <summary>
/// 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
/// </summary>
public const int MSR_EXM = 5;
/// <summary>
/// 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
/// </summary>
public const int MSR_DIO = 6;
/// <summary>
/// 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
/// </summary>
public const int MSR_RQM = 7;
// Status Register 0 Constants
// Designates the bit positions within the status register 0
/// <summary>
/// Unit Select (driveno during interrupt)
/// This flag IS used to indicate a drive unit number at interrupt
/// </summary>
public const int SR0_US0 = 0;
/// <summary>
/// Unit Select (driveno during interrupt)
/// This flag IS used to indicate a drive unit number at interrupt
/// </summary>
public const int SR0_US1 = 1;
/// <summary>
/// Head Address (head during interrupt)
/// State of the head at interrupt
/// </summary>
public const int SR0_HD = 2;
/// <summary>
/// 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
/// </summary>
public const int SR0_NR = 3;
/// <summary>
/// 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
/// </summary>
public const int SR0_EC = 4;
/// <summary>
/// Seek End (Set if seek-command completed)
/// Seek end - When the FDC completes the Seek command, this flag IS set lo 1 (high)
/// </summary>
public const int SR0_SE = 5;
/// <summary>
/// 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.)
/// </summary>
public const int SR0_IC0 = 6;
/// <summary>
/// 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.)
/// </summary>
public const int SR0_IC1 = 7;
// Status Register 1 Constants
// Designates the bit positions within the status register 1
/// <summary>
/// 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
/// </summary>
public const int SR1_MA = 0;
/// <summary>
/// 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
/// </summary>
public const int SR1_NW = 1;
/// <summary>
/// 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
/// </summary>
public const int SR1_ND = 2;
/// <summary>
/// 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
/// </summary>
public const int SR1_OR = 4;
/// <summary>
/// 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
/// </summary>
public const int SR1_DE = 5;
/// <summary>
/// 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
/// </summary>
public const int SR1_EN = 7;
// Status Register 2 Constants
// Designates the bit positions within the status register 2
/// <summary>
/// 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
/// </summary>
public const int SR2_MD = 0;
/// <summary>
/// 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
/// </summary>
public const int SR2_BC = 1;
/// <summary>
/// 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
/// </summary>
public const int SR2_SN = 2;
/// <summary>
/// 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
/// </summary>
public const int SR2_SH = 3;
/// <summary>
/// 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
/// </summary>
public const int SR2_WC = 4;
/// <summary>
/// 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
/// </summary>
public const int SR2_DD = 5;
/// <summary>
/// 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
/// </summary>
public const int SR2_CM = 6;
// Status Register 3 Constants
// Designates the bit positions within the status register 3
/// <summary>
/// Unit select 0
/// Unit Select (pin 28,29 of FDC)
/// </summary>
public const int SR3_US0 = 0;
/// <summary>
/// Unit select 1
/// Unit Select (pin 28,29 of FDC)
/// </summary>
public const int SR3_US1 = 1;
/// <summary>
/// Head address (side select)
/// Head Address (pin 27 of FDC)
/// </summary>
public const int SR3_HD = 2;
/// <summary>
/// Two Side (0=yes, 1=no (!))
/// Two-side - This bit IS used to indicate the status of the two-side signal from the FDD
/// </summary>
public const int SR3_TS = 3;
/// <summary>
/// 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
/// </summary>
public const int SR3_T0 = 4;
/// <summary>
/// Ready - status of the ready signal from the fdd
/// Ready (drive ready signal)
/// </summary>
public const int SR3_RY = 5;
/// <summary>
/// Write Protected (write protected)
/// Write protect - status of the wp signal from the fdd
/// </summary>
public const int SR3_WP = 6;
/// <summary>
/// Fault - This bit is used to indicate the status of the fault signal from the FDD
/// Fault (if supported: 1=Drive failure)
/// </summary>
public const int SR3_FT = 7;
// Interrupt Code Masks
/// <summary>
/// 1 = aborted:readfail / OK if EN (end of track)
/// </summary>
public const byte IC_OK = 0x00;
/// <summary>
/// 1 = aborted:readfail / OK if EN (end of track)
/// </summary>
public const byte IC_ABORTED_RF_OKEN = 0x40;
/// <summary>
/// 2 = unknown cmd or senseint with no int occured
/// </summary>
public const byte IC_NO_INT_OCCURED = 0x80;
/// <summary>
/// 3 = aborted:disc removed etc
/// </summary>
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
/// <summary>
/// Class that holds information about a specific command
/// </summary>
private class Command
{
/// <summary>
/// Mask to remove potential parameter bits (5,6, and or 7) in order to identify the command
/// </summary>
//public int BitMask { get; set; }
/// <summary>
/// The command code after bitmask has been applied
/// </summary>
public int CommandCode { get; set; }
/// <summary>
/// The number of bytes that make up the full command
/// </summary>
public int ParameterByteCount { get; set; }
/// <summary>
/// The number of result bytes that will be generated from the command
/// </summary>
public int ResultByteCount { get; set; }
/// <summary>
/// The command direction
/// IN - Z80 to UPD765A
/// OUT - UPD765A to Z80
/// </summary>
public CommandDirection Direction { get; set; }
/// <summary>
/// Command makes use of the MT bit
/// </summary>
public bool MT;
/// <summary>
/// Command makes use of the MF bit
/// </summary>
public bool MF;
/// <summary>
/// Command makes use of the SK bit
/// </summary>
public bool SK;
/// <summary>
/// Read/Write command that is READ
/// </summary>
public bool IsRead;
/// <summary>
/// Read/Write command that is WRITE
/// </summary>
public bool IsWrite;
/// <summary>
/// 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
/// </summary>
public Action CommandDelegate { get; set; }
}
/// <summary>
/// Storage for command parameters
/// </summary>
public class CommandParameters
{
/// <summary>
/// The requested drive
/// </summary>
public byte UnitSelect;
/// <summary>
/// The requested physical side
/// </summary>
public byte Side;
/// <summary>
/// The requested track (C)
/// </summary>
public byte Cylinder;
/// <summary>
/// The requested head (H)
/// </summary>
public byte Head;
/// <summary>
/// The requested sector (R)
/// </summary>
public byte Sector;
/// <summary>
/// The specified sector size (N)
/// </summary>
public byte SectorSize;
/// <summary>
/// The end of track or last sector value (EOT)
/// </summary>
public byte EOT;
/// <summary>
/// Gap3 length (GPL)
/// </summary>
public byte Gap3Length;
/// <summary>
/// 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
/// </summary>
public byte DTL;
/// <summary>
/// Clear down
/// </summary>
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
}
}