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

247 lines
9.7 KiB
C#

using BizHawk.Common;
using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// The NEC floppy disk controller (and floppy drive) found in the +3
/// </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 Devices
/// <summary>
/// The emulated spectrum machine
/// </summary>
private SpectrumBase _machine;
#endregion
#region Construction & Initialization
/// <summary>
/// Main constructor
/// </summary>
/// <param name="machine"></param>
public NECUPD765()
{
InitCommandList();
}
/// <summary>
/// Initialization routine
/// </summary>
public void Init(SpectrumBase machine)
{
_machine = machine;
FDD_Init();
TimingInit();
Reset();
}
/// <summary>
/// Resets the FDC
/// </summary>
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;
}
}
/// <summary>
/// Setup the command structure
/// Each command represents one of the internal UPD765 commands
/// </summary>
private void InitCommandList()
{
CommandList = new List<Command>
{
// 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)
{
ser.BeginSection("NEC-UPD765");
#region FDD
ser.Sync("FDD_FLAG_MOTOR", ref FDD_FLAG_MOTOR);
for (int i = 0; i < 4; i++)
{
ser.BeginSection("HITDrive_" + i);
DriveStates[i].SyncState(ser);
ser.EndSection();
}
ser.Sync("DiskDriveIndex", ref _diskDriveIndex);
// set active drive
DiskDriveIndex = _diskDriveIndex;
#endregion
#region Registers
ser.Sync("_RegMain", ref StatusMain);
ser.Sync("_Reg0", ref Status0);
ser.Sync("_Reg1", ref Status1);
ser.Sync("_Reg2", ref Status2);
ser.Sync("_Reg3", ref Status3);
#endregion
#region Controller state
ser.Sync("DriveLight", ref DriveLight);
ser.SyncEnum("ActivePhase", ref ActivePhase);
//ser.SyncEnum("ActiveDirection", ref ActiveDirection);
ser.SyncEnum("ActiveInterrupt", ref ActiveInterrupt);
ser.Sync("CommBuffer", ref CommBuffer, false);
ser.Sync("CommCounter", ref CommCounter);
ser.Sync("ResBuffer", ref ResBuffer, false);
ser.Sync("ExecBuffer", ref ExecBuffer, false);
ser.Sync("ExecCounter", ref ExecCounter);
ser.Sync("ExecLength", ref ExecLength);
ser.Sync("InterruptResultBuffer", ref InterruptResultBuffer, false);
ser.Sync("ResCounter", ref ResCounter);
ser.Sync("ResLength", ref ResLength);
ser.Sync("LastSectorDataWriteByte", ref LastSectorDataWriteByte);
ser.Sync("LastSectorDataReadByte", ref LastSectorDataReadByte);
ser.Sync("LastByteReceived", ref LastByteReceived);
ser.Sync("_cmdIndex", ref _cmdIndex);
// resync the ActiveCommand
CMDIndex = _cmdIndex;
ActiveCommandParams.SyncState(ser);
ser.Sync("IndexPulseCounter", ref IndexPulseCounter);
//ser.SyncEnum("_activeStatus", ref _activeStatus);
//ser.SyncEnum("_statusRaised", ref _statusRaised);
ser.Sync("CMD_FLAG_MT", ref CMD_FLAG_MT);
ser.Sync("CMD_FLAG_MF", ref CMD_FLAG_MF);
ser.Sync("CMD_FLAG_SK", ref CMD_FLAG_SK);
ser.Sync("SRT", ref SRT);
ser.Sync("HUT", ref HUT);
ser.Sync("HLT", ref HLT);
ser.Sync("ND", ref ND);
ser.Sync("SRT_Counter", ref SRT_Counter);
ser.Sync("HUT_Counter", ref HUT_Counter);
ser.Sync("HLT_Counter", ref HLT_Counter);
ser.Sync("SectorDelayCounter", ref SectorDelayCounter);
ser.Sync("SectorID", ref SectorID);
#endregion
#region Timing
ser.Sync("LastCPUCycle", ref LastCPUCycle);
ser.Sync("StatusDelay", ref StatusDelay);
ser.Sync("TickCounter", ref TickCounter);
ser.Sync("DriveCycleCounter", ref DriveCycleCounter);
#endregion
ser.EndSection();
}
#endregion
}
}