Deduplicate code common to CPCHawk and ZXHawk

Also fixed indentation and replaced some block comments with #if false
This commit is contained in:
YoshiRulz 2020-03-07 10:06:20 +10:00
parent 7456d81a9c
commit c7c26cd946
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
72 changed files with 1562 additions and 7887 deletions

View File

@ -1,25 +0,0 @@
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Represents a beeper/buzzer device
/// </summary>
public interface IBeeperDevice
{
/// <summary>
/// Initialization
/// </summary>
void Init(int sampleRate, int tStatesPerFrame);
/// <summary>
/// Processes an incoming pulse value and adds it to the blipbuffer
/// </summary>
void ProcessPulseValue(bool pulse);
/// <summary>
/// State serialization
/// </summary>
void SyncState(Serializer ser);
}
}

View File

@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>

View File

@ -1,180 +0,0 @@

namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Used for the sector CHRN structure
/// </summary>
public class CHRN
{
/// <summary>
/// Track
/// </summary>
public byte C { get; set; }
/// <summary>
/// Side
/// </summary>
public byte H { get; set; }
/// <summary>
/// Sector ID
/// </summary>
public byte R { get; set; }
/// <summary>
/// Sector Size
/// </summary>
public byte N { get; set; }
/// <summary>
/// Status register 1
/// </summary>
private byte _flag1;
public byte Flag1
{
get => _flag1;
set => _flag1 = value;
}
/// <summary>
/// Status register 2
/// </summary>
private byte _flag2;
public byte Flag2
{
get => _flag2;
set => _flag2 = value;
}
/// <summary>
/// Used to store the last transmitted/received data bytes
/// </summary>
public byte[] DataBytes { get; set; }
/// <summary>
/// ID for the read/write data command
/// </summary>
public int DataID { get; set; }
#region Helper Methods
/// <summary>
/// Missing Address Mark (Sector_ID or DAM not found)
/// </summary>
public bool ST1MA
{
get => NECUPD765.GetBit(0, _flag1);
set
{
if (value) { NECUPD765.SetBit(0, ref _flag1); }
else { NECUPD765.UnSetBit(0, ref _flag1); }
}
}
/// <summary>
/// No Data (Sector_ID not found, CRC fail in ID_field)
/// </summary>
public bool ST1ND
{
get => NECUPD765.GetBit(2, _flag1);
set
{
if (value) { NECUPD765.SetBit(2, ref _flag1); }
else { NECUPD765.UnSetBit(2, ref _flag1); }
}
}
/// <summary>
/// Data Error (CRC-fail in ID- or Data-Field)
/// </summary>
public bool ST1DE
{
get => NECUPD765.GetBit(5, _flag1);
set
{
if (value) { NECUPD765.SetBit(5, ref _flag1); }
else { NECUPD765.UnSetBit(5, ref _flag1); }
}
}
/// <summary>
/// End of Track (set past most read/write commands) (see IC)
/// </summary>
public bool ST1EN
{
get => NECUPD765.GetBit(7, _flag1);
set
{
if (value) { NECUPD765.SetBit(7, ref _flag1); }
else { NECUPD765.UnSetBit(7, ref _flag1); }
}
}
/// <summary>
/// Missing Address Mark in Data Field (DAM not found)
/// </summary>
public bool ST2MD
{
get => NECUPD765.GetBit(0, _flag2);
set
{
if (value) { NECUPD765.SetBit(0, ref _flag2); }
else { NECUPD765.UnSetBit(0, ref _flag2); }
}
}
/// <summary>
/// Bad Cylinder (read/programmed track-ID different and read-ID = FF)
/// </summary>
public bool ST2BC
{
get => NECUPD765.GetBit(1, _flag2);
set
{
if (value) { NECUPD765.SetBit(1, ref _flag2); }
else { NECUPD765.UnSetBit(1, ref _flag2); }
}
}
/// <summary>
/// Wrong Cylinder (read/programmed track-ID different) (see b1)
/// </summary>
public bool ST2WC
{
get => NECUPD765.GetBit(4, _flag2);
set
{
if (value) { NECUPD765.SetBit(4, ref _flag2); }
else { NECUPD765.UnSetBit(4, ref _flag2); }
}
}
/// <summary>
/// Data Error in Data Field (CRC-fail in data-field)
/// </summary>
public bool ST2DD
{
get => NECUPD765.GetBit(5, _flag2);
set
{
if (value) { NECUPD765.SetBit(5, ref _flag2); }
else { NECUPD765.UnSetBit(5, ref _flag2); }
}
}
/// <summary>
/// Control Mark (read/scan command found sector with deleted DAM)
/// </summary>
public bool ST2CM
{
get => NECUPD765.GetBit(6, _flag2);
set
{
if (value) { NECUPD765.SetBit(6, ref _flag2); }
else { NECUPD765.UnSetBit(6, ref _flag2); }
}
}
#endregion
}
}

View File

@ -1,875 +0,0 @@
using BizHawk.Common;
using BizHawk.Common.NumberExtensions;
using System;
using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Floppy drive related stuff
/// </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 : IFDDHost
{
#region Drive State
/// <summary>
/// FDD Flag - motor on/off
/// </summary>
public bool FDD_FLAG_MOTOR;
/// <summary>
/// The index of the currently active disk drive
/// </summary>
public int DiskDriveIndex
{
get => _diskDriveIndex;
set
{
// when index is changed update the ActiveDrive
_diskDriveIndex = value;
ActiveDrive = DriveStates[_diskDriveIndex];
}
}
private int _diskDriveIndex = 0;
/// <summary>
/// The currently active drive
/// </summary>
private DriveState ActiveDrive;
/// <summary>
/// Array that holds state information for each possible drive
/// </summary>
private DriveState[] DriveStates = new DriveState[4];
#endregion
#region FDD Methods
/// <summary>
/// Initialization / reset of the floppy drive subsystem
/// </summary>
private void FDD_Init()
{
for (int i = 0; i < 4; i++)
{
DriveState ds = new DriveState(i, this);
DriveStates[i] = ds;
}
}
/// <summary>
/// Searches for the requested sector
/// </summary>
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
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
public void FDD_LoadDisk(byte[] diskData)
{
// we are only going to load into the first drive
DriveStates[0].FDD_LoadDisk(diskData);
}
/// <summary>
/// Ejects the current disk
/// </summary>
public void FDD_EjectDisk()
{
DriveStates[0].FDD_EjectDisk();
}
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
public bool FDD_IsDiskLoaded => DriveStates[DiskDriveIndex].FDD_IsDiskLoaded;
/// <summary>
/// Returns the disk object from drive 0
/// </summary>
public FloppyDisk DiskPointer => DriveStates[0].Disk;
public FloppyDisk Disk { get; set; }
#endregion
#region Drive Status Class
/// <summary>
/// Holds specfic state information about a drive
/// </summary>
private class DriveState : IFDDHost
{
#region State
/// <summary>
/// The drive ID from an FDC perspective
/// </summary>
public int ID;
/// <summary>
/// Signs whether this drive ready
/// TRUE if both drive exists and has a disk inserted
/// </summary>
public bool FLAG_READY
{
get
{
if (!FDD_IsDiskLoaded || Disk.GetTrackCount() == 0 || !FDC.FDD_FLAG_MOTOR)
return false;
else
return true;
}
}
/// <summary>
/// Disk is write protected (TRUE BY DEFAULT)
/// </summary>
public bool FLAG_WRITEPROTECT = false;
/// <summary>
/// Storage for seek steps
/// One step for each indexpulse (track index) until seeked track
/// </summary>
public int SeekCounter;
/// <summary>
/// Seek status
/// </summary>
public int SeekStatus;
/// <summary>
/// Age counter
/// </summary>
public int SeekAge;
/// <summary>
/// The current side
/// </summary>
public byte CurrentSide;
/// <summary>
/// The current track index in the DiskTracks array
/// </summary>
public byte TrackIndex;
/// <summary>
/// The track ID of the current cylinder
/// </summary>
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;
}
}
}
}
/// <summary>
/// The new track that the drive is seeking to
/// (used in seek operations)
/// </summary>
public int SeekingTrack;
/// <summary>
/// The current sector index in the Sectors array
/// </summary>
public int SectorIndex;
/// <summary>
/// The currently loaded floppy disk
/// </summary>
public FloppyDisk Disk { get; set; }
/// <summary>
/// The parent controller
/// </summary>
private NECUPD765 FDC;
#endregion
#region Lookups
/// <summary>
/// TRUE if we are on track 0
/// </summary>
public bool FLAG_TRACK0
{
get
{
if (TrackIndex == 0) { return true; }
else { return false; }
}
}
#endregion
#region Public Methods
/*
/// <summary>
/// Moves the head across the disk cylinders
/// </summary>
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;
}
*/
/*
/// <summary>
/// Finds a supplied sector
/// </summary>
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;
}
/// <summary>
/// Populates a result buffer
/// </summary>
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;
}
}
/// <summary>
/// Populates the result buffer with ReadID data
/// </summary>
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;
}
}
*/
/*
/// <summary>
/// The drive performs a seek operation if necessary
/// Return value TRUE indicates seek complete
/// </summary>
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();
}
/// <summary>
/// Runs a seek cycle
/// </summary>
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;
}
}
}
/// <summary>
/// Called when a seek operation has completed
/// </summary>
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
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
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.");
}
}
/// <summary>
/// Ejects the current disk
/// </summary>
public void FDD_EjectDisk()
{
Disk = null;
//FLAG_READY = false;
}
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
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
}
}

View File

@ -0,0 +1,68 @@
using System;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
public class NECUPD765CPC : NECUPD765<CPCBase, NECUPD765CPC.CPCDriveState>
{
protected override CPCDriveState ConstructDriveState(int driveID, NECUPD765<CPCBase, CPCDriveState> 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<CPCBase, CPCDriveState> 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.");
}
}
}
}
}

View File

@ -7,6 +7,8 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
@ -23,7 +25,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
private CRCT_6845 CRCT => _machine.CRCT; private CRCT_6845 CRCT => _machine.CRCT;
//private CRTDevice CRT => _machine.CRT; //private CRTDevice CRT => _machine.CRT;
private IPSG PSG => _machine.AYDevice; private IPSG PSG => _machine.AYDevice;
private NECUPD765 FDC => _machine.UPDDiskDevice; private NECUPD765CPC FDC => _machine.UPDDiskDevice;
private DatacorderDevice DATACORDER => _machine.TapeDevice; private DatacorderDevice DATACORDER => _machine.TapeDevice;
private ushort BUSRQ => CPU.MEMRQ[CPU.bus_pntr]; private ushort BUSRQ => CPU.MEMRQ[CPU.bus_pntr];
public const ushort PCh = 1; public const ushort PCh = 1;

View File

@ -1,5 +1,6 @@
using BizHawk.Common; using BizHawk.Common;
using BizHawk.Common.NumberExtensions; using BizHawk.Common.NumberExtensions;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {

View File

@ -1,6 +1,7 @@
using System.Collections; using System.Collections;
using BizHawk.Common; using BizHawk.Common;
using BizHawk.Common.NumberExtensions; using BizHawk.Common.NumberExtensions;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {

View File

@ -2,6 +2,8 @@
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
using System; using System;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>

View File

@ -37,7 +37,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
TapeDevice = new DatacorderDevice(autoTape); TapeDevice = new DatacorderDevice(autoTape);
TapeDevice.Init(this); TapeDevice.Init(this);
UPDDiskDevice = new NECUPD765(); UPDDiskDevice = new NECUPD765CPC();
UPDDiskDevice.Init(this); UPDDiskDevice.Init(this);
InitializeMedia(files); InitializeMedia(files);

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>

View File

@ -1,5 +1,6 @@
using BizHawk.Common; using BizHawk.Common;
using BizHawk.Emulation.Cores.Components.Z80A; using BizHawk.Emulation.Cores.Components.Z80A;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC 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 /// The abstract class that all emulated models will inherit from
/// * Main properties / fields / contruction* /// * Main properties / fields / contruction*
/// </summary> /// </summary>
public abstract partial class CPCBase public abstract partial class CPCBase : CPCSpectrumBase.CPCSpectrumBase
{ {
#region Devices #region Devices
@ -50,7 +51,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <summary> /// <summary>
/// The Amstrad disk drive /// The Amstrad disk drive
/// </summary> /// </summary>
public virtual NECUPD765 UPDDiskDevice { get; set; } public virtual NECUPD765CPC UPDDiskDevice { get; set; }
/// <summary> /// <summary>
/// The Cathode Ray Tube Controller chip /// The Cathode Ray Tube Controller chip

View File

@ -3,6 +3,8 @@ using BizHawk.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>

View File

@ -3,6 +3,8 @@ using BizHawk.Common;
using System.Collections.Generic; using System.Collections.Generic;
using System; using System;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>

View File

@ -1,19 +0,0 @@

namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// The different disk formats ZXHawk currently supports
/// </summary>
public enum DiskType
{
/// <summary>
/// Standard CPCEMU disk format (used in the built-in +3 disk drive)
/// </summary>
CPC,
/// <summary>
/// Extended CPCEMU disk format (used in the built-in +3 disk drive)
/// </summary>
CPCExtended
}
}

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>

View File

@ -0,0 +1,9 @@
using BizHawk.Emulation.Cores.Components.Z80A;
namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{
public interface CPCSpectrumBase
{
Z80A CPU { get; set; }
}
}

View File

@ -1,6 +1,6 @@
using BizHawk.Common; using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// Represents a beeper/buzzer device /// Represents a beeper/buzzer device

View File

@ -1,5 +1,5 @@
 
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// Defines an object that can load a floppy disk image /// Defines an object that can load a floppy disk image

View File

@ -1,15 +1,17 @@
 
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC using System;
namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// Represents a spectrum joystick /// Represents a spectrum joystick
/// </summary> /// </summary>
public interface IJoystick public interface IJoystick<T> where T : Enum
{ {
/// <summary> /// <summary>
/// The type of joystick /// The type of joystick
/// </summary> /// </summary>
JoystickType JoyType { get; } T JoyType { get; }
/// <summary> /// <summary>
/// Array of all the possibly button press names /// Array of all the possibly button press names

View File

@ -1,5 +1,5 @@
 
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// Represents a device that utilizes port IN & OUT /// Represents a device that utilizes port IN & OUT

View File

@ -0,0 +1,179 @@
namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{
/// <summary>
/// Used for the sector CHRN structure
/// </summary>
public class CHRN
{
/// <summary>
/// Track
/// </summary>
public byte C { get; set; }
/// <summary>
/// Side
/// </summary>
public byte H { get; set; }
/// <summary>
/// Sector ID
/// </summary>
public byte R { get; set; }
/// <summary>
/// Sector Size
/// </summary>
public byte N { get; set; }
/// <summary>
/// Status register 1
/// </summary>
private byte _flag1;
public byte Flag1
{
get => _flag1;
set => _flag1 = value;
}
/// <summary>
/// Status register 2
/// </summary>
private byte _flag2;
public byte Flag2
{
get => _flag2;
set => _flag2 = value;
}
/// <summary>
/// Used to store the last transmitted/received data bytes
/// </summary>
public byte[] DataBytes { get; set; }
/// <summary>
/// ID for the read/write data command
/// </summary>
public int DataID { get; set; }
#region Helper Methods
/// <summary>
/// Missing Address Mark (Sector_ID or DAM not found)
/// </summary>
public bool ST1MA
{
get => NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.GetBit(0, _flag1);
set
{
if (value) { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.SetBit(0, ref _flag1); }
else { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.UnSetBit(0, ref _flag1); }
}
}
/// <summary>
/// No Data (Sector_ID not found, CRC fail in ID_field)
/// </summary>
public bool ST1ND
{
get => NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.GetBit(2, _flag1);
set
{
if (value) { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.SetBit(2, ref _flag1); }
else { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.UnSetBit(2, ref _flag1); }
}
}
/// <summary>
/// Data Error (CRC-fail in ID- or Data-Field)
/// </summary>
public bool ST1DE
{
get => NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.GetBit(5, _flag1);
set
{
if (value) { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.SetBit(5, ref _flag1); }
else { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.UnSetBit(5, ref _flag1); }
}
}
/// <summary>
/// End of Track (set past most read/write commands) (see IC)
/// </summary>
public bool ST1EN
{
get => NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.GetBit(7, _flag1);
set
{
if (value) { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.SetBit(7, ref _flag1); }
else { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.UnSetBit(7, ref _flag1); }
}
}
/// <summary>
/// Missing Address Mark in Data Field (DAM not found)
/// </summary>
public bool ST2MD
{
get => NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.GetBit(0, _flag2);
set
{
if (value) { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.SetBit(0, ref _flag2); }
else { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.UnSetBit(0, ref _flag2); }
}
}
/// <summary>
/// Bad Cylinder (read/programmed track-ID different and read-ID = FF)
/// </summary>
public bool ST2BC
{
get => NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.GetBit(1, _flag2);
set
{
if (value) { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.SetBit(1, ref _flag2); }
else { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.UnSetBit(1, ref _flag2); }
}
}
/// <summary>
/// Wrong Cylinder (read/programmed track-ID different) (see b1)
/// </summary>
public bool ST2WC
{
get => NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.GetBit(4, _flag2);
set
{
if (value) { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.SetBit(4, ref _flag2); }
else { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.UnSetBit(4, ref _flag2); }
}
}
/// <summary>
/// Data Error in Data Field (CRC-fail in data-field)
/// </summary>
public bool ST2DD
{
get => NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.GetBit(5, _flag2);
set
{
if (value) { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.SetBit(5, ref _flag2); }
else { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.UnSetBit(5, ref _flag2); }
}
}
/// <summary>
/// Control Mark (read/scan command found sector with deleted DAM)
/// </summary>
public bool ST2CM
{
get => NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.GetBit(6, _flag2);
set
{
if (value) { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.SetBit(6, ref _flag2); }
else { NECUPD765<CPCSpectrumBase, NECUPD765DriveState>.UnSetBit(6, ref _flag2); }
}
}
#endregion
}
}

View File

@ -0,0 +1,7 @@
namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{
public interface INECUPD765
{
bool FDD_FLAG_MOTOR { get; }
}
}

View File

@ -1,20 +1,20 @@
using BizHawk.Common; using BizHawk.Common;
using System; using System;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// Definitions /// Definitions
/// </summary> /// </summary>
#region Attribution #region Attribution
/* /*
Implementation based on the information contained here: Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC http://www.cpcwiki.eu/index.php/765_FDC
and here: and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/ */
#endregion #endregion
public partial class NECUPD765 public abstract partial class NECUPD765<TMachine, TDriveState>
{ {
#region Enums #region Enums
@ -692,10 +692,10 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// </summary> /// </summary>
private class Command private class Command
{ {
// /// <summary> // /// <summary>
// /// Mask to remove potential parameter bits (5,6, and or 7) in order to identify the command // /// Mask to remove potential parameter bits (5,6, and or 7) in order to identify the command
// /// </summary> // /// </summary>
// public int BitMask { get; set; } // public int BitMask { get; set; }
/// <summary> /// <summary>
/// The command code after bitmask has been applied /// The command code after bitmask has been applied
/// </summary> /// </summary>

View File

@ -3,20 +3,20 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// FDC State and Methods /// FDC State and Methods
/// </summary> /// </summary>
#region Attribution #region Attribution
/* /*
Implementation based on the information contained here: Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC http://www.cpcwiki.eu/index.php/765_FDC
and here: and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/ */
#endregion #endregion
public partial class NECUPD765 public abstract partial class NECUPD765<TMachine, TDriveState>
{ {
#region Controller State #region Controller State
@ -210,69 +210,69 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// Main status register (accessed via reads to port 0x2ffd) /// Main status register (accessed via reads to port 0x2ffd)
/// </summary> /// </summary>
/* /*
b0..3 DB FDD0..3 Busy (seek/recalib active, until succesful sense intstat) b0..3 DB FDD0..3 Busy (seek/recalib active, until succesful sense intstat)
b4 CB FDC Busy (still in command-, execution- or result-phase) b4 CB FDC Busy (still in command-, execution- or result-phase)
b5 EXM Execution Mode (still in execution-phase, non_DMA_only) b5 EXM Execution Mode (still in execution-phase, non_DMA_only)
b6 DIO Data Input/Output (0=CPU->FDC, 1=FDC->CPU) (see b7) 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) b7 RQM Request For Master (1=ready for next byte) (see b6 for direction)
*/ */
private byte StatusMain; private byte StatusMain;
/// <summary> /// <summary>
/// Status Register 0 /// Status Register 0
/// </summary> /// </summary>
/* /*
b0,1 US Unit Select (driveno during interrupt) b0,1 US Unit Select (driveno during interrupt)
b2 HD Head Address (head during interrupt) b2 HD Head Address (head during interrupt)
b3 NR Not Ready (drive not ready or non-existing 2nd head selected) b3 NR Not Ready (drive not ready or non-existing 2nd head selected)
b4 EC Equipment Check (drive failure or recalibrate failed (retry)) b4 EC Equipment Check (drive failure or recalibrate failed (retry))
b5 SE Seek End (Set if seek-command completed) 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 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.) or senseint with no int occured, 3=aborted:disc removed etc.)
*/ */
private byte Status0; private byte Status0;
/// <summary> /// <summary>
/// Status Register 1 /// Status Register 1
/// </summary> /// </summary>
/* /*
b0 MA Missing Address Mark (Sector_ID or DAM not found) b0 MA Missing Address Mark (Sector_ID or DAM not found)
b1 NW Not Writeable (tried to write/format disc with wprot_tab=on) 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) b2 ND No Data (Sector_ID not found, CRC fail in ID_field)
b3,6 0 Not used b3,6 0 Not used
b4 OR Over Run (CPU too slow in execution-phase (ca. 26us/Byte)) b4 OR Over Run (CPU too slow in execution-phase (ca. 26us/Byte))
b5 DE Data Error (CRC-fail in ID- or Data-Field) b5 DE Data Error (CRC-fail in ID- or Data-Field)
b7 EN End of Track (set past most read/write commands) (see IC) b7 EN End of Track (set past most read/write commands) (see IC)
*/ */
private byte Status1; private byte Status1;
/// <summary> /// <summary>
/// Status Register 2 /// Status Register 2
/// </summary> /// </summary>
/* /*
b0 MD Missing Address Mark in Data Field (DAM not found) b0 MD Missing Address Mark in Data Field (DAM not found)
b1 BC Bad Cylinder (read/programmed track-ID different and read-ID = FF) b1 BC Bad Cylinder (read/programmed track-ID different and read-ID = FF)
b2 SN Scan Not Satisfied (no fitting sector found) b2 SN Scan Not Satisfied (no fitting sector found)
b3 SH Scan Equal Hit (equal) b3 SH Scan Equal Hit (equal)
b4 WC Wrong Cylinder (read/programmed track-ID different) (see b1) b4 WC Wrong Cylinder (read/programmed track-ID different) (see b1)
b5 DD Data Error in Data Field (CRC-fail in data-field) b5 DD Data Error in Data Field (CRC-fail in data-field)
b6 CM Control Mark (read/scan command found sector with deleted DAM) b6 CM Control Mark (read/scan command found sector with deleted DAM)
b7 0 Not Used b7 0 Not Used
*/ */
private byte Status2; private byte Status2;
/// <summary> /// <summary>
/// Status Register 3 /// Status Register 3
/// </summary> /// </summary>
/* /*
b0,1 US Unit Select (pin 28,29 of FDC) b0,1 US Unit Select (pin 28,29 of FDC)
b2 HD Head Address (pin 27 of FDC) b2 HD Head Address (pin 27 of FDC)
b3 TS Two Side (0=yes, 1=no (!)) b3 TS Two Side (0=yes, 1=no (!))
b4 T0 Track 0 (on track 0 we are) b4 T0 Track 0 (on track 0 we are)
b5 RY Ready (drive ready signal) b5 RY Ready (drive ready signal)
b6 WP Write Protected (write protected) b6 WP Write Protected (write protected)
b7 FT Fault (if supported: 1=Drive failure) b7 FT Fault (if supported: 1=Drive failure)
*/ */
private byte Status3; private byte Status3;
#endregion #endregion
@ -385,8 +385,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
sectorSize = 0x80 << ActiveCommandParams.SectorSize; sectorSize = 0x80 << ActiveCommandParams.SectorSize;
} }
var mtc = maxTransferCap;
// get the current track // get the current track
var track = ActiveDrive.Disk.DiskTracks.FirstOrDefault(a => a.TrackNumber == ActiveDrive.CurrentTrackID); var track = ActiveDrive.Disk.DiskTracks.FirstOrDefault(a => a.TrackNumber == ActiveDrive.CurrentTrackID);
@ -672,22 +670,13 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
switch (ActiveCommandParams.SectorSize) switch (ActiveCommandParams.SectorSize)
{ {
case 1: case 1:
if (CMD_FLAG_MF) maxTransferCap = CMD_FLAG_MF ? 6656 : 3840;
maxTransferCap = 6656;
else
maxTransferCap = 3840;
break; break;
case 2: case 2:
if (CMD_FLAG_MF) maxTransferCap = CMD_FLAG_MF ? 7680 : 4096;
maxTransferCap = 7680;
else
maxTransferCap = 4096;
break; break;
case 3: case 3:
if (CMD_FLAG_MF) maxTransferCap = CMD_FLAG_MF ? 8192 : 4096;
maxTransferCap = 8192;
else
maxTransferCap = 4096;
break; break;
} }
@ -2148,17 +2137,17 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
// second byte is the current track id // second byte is the current track id
ResBuffer[1] = ActiveDrive.CurrentTrackID; ResBuffer[1] = ActiveDrive.CurrentTrackID;
} }
/* #if false
else if (ActiveDrive.SeekStatus == SEEK_INTACKNOWLEDGED) else if (ActiveDrive.SeekStatus == SEEK_INTACKNOWLEDGED)
{ {
// DriveA interrupt has already been acknowledged // DriveA interrupt has already been acknowledged
ActiveDrive.SeekStatus = SEEK_IDLE; ActiveDrive.SeekStatus = SEEK_IDLE;
ResLength = 1; ResLength = 1;
Status0 = 192; Status0 = 192;
ResBuffer[0] = Status0; ResBuffer[0] = Status0;
} }
*/ #endif
else if (ActiveDrive.SeekStatus == SEEK_IDLE) else if (ActiveDrive.SeekStatus == SEEK_IDLE)
{ {
// SIS with no interrupt // SIS with no interrupt
@ -2363,7 +2352,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
SetBit(MSR_EXM, ref StatusMain); SetBit(MSR_EXM, ref StatusMain);
SetBit(MSR_CB, ref StatusMain); SetBit(MSR_CB, ref StatusMain);
// overrun detection // overrun detection
OverrunCounter++; OverrunCounter++;
if (OverrunCounter >= 64) if (OverrunCounter >= 64)
{ {
@ -2383,6 +2372,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
break; break;
} }
// if (!CheckTiming()) UnSetBit(MSR_EXM, ref StatusMain);
return StatusMain; return StatusMain;
} }
@ -2474,7 +2465,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
//// we are in command phase //// we are in command phase
case Phase.Command: case Phase.Command:
// attempt to process this parameter byte // attempt to process this parameter byte
//ProcessCommand(data); //ProcessCommand(data);
ActiveCommand.CommandDelegate(); ActiveCommand.CommandDelegate();
break; break;
//// we are in execution phase //// we are in execution phase
@ -2553,15 +2544,15 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
CMDIndex = CommandList.Count() - 1; CMDIndex = CommandList.Count() - 1;
} }
/* #if false
if ((CMD_FLAG_MF && !ActiveCommand.MF) || if ((CMD_FLAG_MF && !ActiveCommand.MF) ||
(CMD_FLAG_MT && !ActiveCommand.MT) || (CMD_FLAG_MT && !ActiveCommand.MT) ||
(CMD_FLAG_SK && !ActiveCommand.SK)) (CMD_FLAG_SK && !ActiveCommand.SK))
{ {
// command byte included spurious bit 5,6 or 7 flags // command byte included spurious bit 5,6 or 7 flags
CMDIndex = CommandList.Count() - 1; CMDIndex = CommandList.Count() - 1;
} }
*/ #endif
} }
CommCounter = 0; CommCounter = 0;
@ -2571,14 +2562,14 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
// move to command phase // move to command phase
ActivePhase = Phase.Command; ActivePhase = Phase.Command;
/* #if false
// check for invalid SIS // check for invalid SIS
if (ActiveInterrupt == InterruptState.None && CMDIndex == CC_SENSE_INTSTATUS) if (ActiveInterrupt == InterruptState.None && CMDIndex == CC_SENSE_INTSTATUS)
{ {
CMDIndex = CC_INVALID; CMDIndex = CC_INVALID;
//ActiveCommand.CommandDelegate(InstructionState.StartResult); //ActiveCommand.CommandDelegate(InstructionState.StartResult);
} }
*/ #endif
// set reslength // set reslength
ResLength = ActiveCommand.ResultByteCount; ResLength = ActiveCommand.ResultByteCount;

View File

@ -0,0 +1,189 @@
using BizHawk.Common;
using BizHawk.Common.NumberExtensions;
using System;
using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{
/// <summary>
/// Floppy drive related stuff
/// </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 abstract partial class NECUPD765<TMachine, TDriveState> : IFDDHost, INECUPD765
{
#region Drive State
/// <summary>
/// FDD Flag - motor on/off
/// </summary>
public bool FDD_FLAG_MOTOR;
bool INECUPD765.FDD_FLAG_MOTOR => FDD_FLAG_MOTOR;
/// <summary>
/// The index of the currently active disk drive
/// </summary>
public int DiskDriveIndex
{
get => _diskDriveIndex;
set
{
// when index is changed update the ActiveDrive
_diskDriveIndex = value;
ActiveDrive = DriveStates[_diskDriveIndex];
}
}
private int _diskDriveIndex = 0;
/// <summary>
/// The currently active drive
/// </summary>
private NECUPD765DriveState ActiveDrive;
/// <summary>
/// Array that holds state information for each possible drive
/// </summary>
private NECUPD765DriveState[] DriveStates = new NECUPD765DriveState[4];
#endregion
#region FDD Methods
protected abstract TDriveState ConstructDriveState(int driveID, NECUPD765<TMachine, TDriveState> fdc);
/// <summary>
/// Initialization / reset of the floppy drive subsystem
/// </summary>
private void FDD_Init()
{
for (int i = 0; i < 4; i++)
{
NECUPD765DriveState ds = ConstructDriveState(i, this);
DriveStates[i] = ds;
}
}
/// <summary>
/// Searches for the requested sector
/// </summary>
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
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
public void FDD_LoadDisk(byte[] diskData)
{
// we are only going to load into the first drive
DriveStates[0].FDD_LoadDisk(diskData);
}
/// <summary>
/// Ejects the current disk
/// </summary>
public void FDD_EjectDisk()
{
DriveStates[0].FDD_EjectDisk();
}
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
public bool FDD_IsDiskLoaded => DriveStates[DiskDriveIndex].FDD_IsDiskLoaded;
/// <summary>
/// Returns the disk object from drive 0
/// </summary>
public FloppyDisk DiskPointer => DriveStates[0].Disk;
public FloppyDisk Disk { get; set; }
#endregion
}
}

View File

@ -3,20 +3,20 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// IPortIODevice /// IPortIODevice
/// </summary> /// </summary>
#region Attribution #region Attribution
/* /*
Implementation based on the information contained here: Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC http://www.cpcwiki.eu/index.php/765_FDC
and here: and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/ */
#endregion #endregion
public partial class NECUPD765 : IPortIODevice public abstract partial class NECUPD765<TMachine, TDriveState> : IPortIODevice
{ {
#region Dev Logging #region Dev Logging
@ -31,15 +31,15 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/* /*
* Status read Status read
* Data write Data write
* Data read Data read
* CMD code CMD code
* CMD string CMD string
* MT flag MT flag
* MK flag MK flag
* SK flag SK flag
* */ */
private string[] workingArr = new string[3]; private string[] workingArr = new string[3];
private void BuildCSVLine() private void BuildCSVLine()

View File

@ -1,19 +1,19 @@
using System.Collections; using System.Collections;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// Static helper methods /// Static helper methods
/// </summary> /// </summary>
#region Attribution #region Attribution
/* /*
Implementation based on the information contained here: Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC http://www.cpcwiki.eu/index.php/765_FDC
and here: and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/ */
#endregion #endregion
public partial class NECUPD765 public abstract partial class NECUPD765<TMachine, TDriveState>
{ {
/// <summary> /// <summary>
/// Returns the specified bit value from supplied byte /// Returns the specified bit value from supplied byte

View File

@ -1,18 +1,18 @@
 
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// Timimng /// Timimng
/// </summary> /// </summary>
#region Attribution #region Attribution
/* /*
Implementation based on the information contained here: Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC http://www.cpcwiki.eu/index.php/765_FDC
and here: and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/ */
#endregion #endregion
public partial class NECUPD765 public abstract partial class NECUPD765<TMachine, TDriveState>
{ {
/// <summary> /// <summary>
/// The current Z80 cycle /// The current Z80 cycle
@ -43,7 +43,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <summary> /// <summary>
/// Defines the numbers of Z80 cycles per MS /// Defines the numbers of Z80 cycles per MS
/// </summary> /// </summary>
private long CPUCyclesPerMs; protected long CPUCyclesPerMs;
/// <summary> /// <summary>
/// The floppy drive emulated clock speed /// The floppy drive emulated clock speed
@ -73,23 +73,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <summary> /// <summary>
/// Initializes the timing routines /// Initializes the timing routines
/// </summary> /// </summary>
private void TimingInit() protected abstract 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;
}
/// <summary> /// <summary>
/// Called by reads to the main status register /// Called by reads to the main status register

View File

@ -1,27 +1,29 @@
using BizHawk.Common; using BizHawk.Common;
using System.Collections.Generic; using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// The NEC floppy disk controller (and floppy drive) found in the +3 /// The NEC floppy disk controller (and floppy drive) found in the +3
/// </summary> /// </summary>
#region Attribution #region Attribution
/* /*
Implementation based on the information contained here: Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC http://www.cpcwiki.eu/index.php/765_FDC
and here: and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/ */
#endregion #endregion
public partial class NECUPD765 public abstract partial class NECUPD765<TMachine, TDriveState>
where TMachine : CPCSpectrumBase
where TDriveState : NECUPD765DriveState
{ {
#region Devices #region Devices
/// <summary> /// <summary>
/// The emulated spectrum machine /// The emulated CPC or Spectrum machine
/// </summary> /// </summary>
private CPCBase _machine; protected TMachine _machine;
#endregion #endregion
@ -38,7 +40,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <summary> /// <summary>
/// Initialization routine /// Initialization routine
/// </summary> /// </summary>
public void Init(CPCBase machine) public void Init(TMachine machine)
{ {
_machine = machine; _machine = machine;
FDD_Init(); FDD_Init();
@ -94,56 +96,56 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
CommandList = new List<Command> CommandList = new List<Command>
{ {
// read data // read data
new Command { CommandDelegate = UPD_ReadData, CommandCode = 0x06, MT = true, MF = true, SK = true, IsRead = true, new Command { CommandDelegate = UPD_ReadData, CommandCode = 0x06, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
// read id // read id
new Command { CommandDelegate = UPD_ReadID, CommandCode = 0x0a, MF = true, IsRead = true, new Command { CommandDelegate = UPD_ReadID, CommandCode = 0x0a, MF = true, IsRead = true,
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 7 }, Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 7 },
// specify // specify
new Command { CommandDelegate = UPD_Specify, CommandCode = 0x03, new Command { CommandDelegate = UPD_Specify, CommandCode = 0x03,
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
// read diagnostic // read diagnostic
new Command { CommandDelegate = UPD_ReadDiagnostic, CommandCode = 0x02, MF = true, SK = true, IsRead = true, new Command { CommandDelegate = UPD_ReadDiagnostic, CommandCode = 0x02, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
// scan equal // scan equal
new Command { CommandDelegate = UPD_ScanEqual, CommandCode = 0x11, MT = true, MF = true, SK = true, IsRead = true, new Command { CommandDelegate = UPD_ScanEqual, CommandCode = 0x11, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// scan high or equal // scan high or equal
new Command { CommandDelegate = UPD_ScanHighOrEqual, CommandCode = 0x1d, MT = true, MF = true, SK = true, IsRead = true, new Command { CommandDelegate = UPD_ScanHighOrEqual, CommandCode = 0x1d, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// scan low or equal // scan low or equal
new Command { CommandDelegate = UPD_ScanLowOrEqual, CommandCode = 0x19, MT = true, MF = true, SK = true, IsRead = true, new Command { CommandDelegate = UPD_ScanLowOrEqual, CommandCode = 0x19, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// read deleted data // read deleted data
new Command { CommandDelegate = UPD_ReadDeletedData, CommandCode = 0x0c, MT = true, MF = true, SK = true, IsRead = true, new Command { CommandDelegate = UPD_ReadDeletedData, CommandCode = 0x0c, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
// write data // write data
new Command { CommandDelegate = UPD_WriteData, CommandCode = 0x05, MT = true, MF = true, IsWrite = true, new Command { CommandDelegate = UPD_WriteData, CommandCode = 0x05, MT = true, MF = true, IsWrite = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// write id // write id
new Command { CommandDelegate = UPD_WriteID, CommandCode = 0x0d, MF = true, IsWrite = true, new Command { CommandDelegate = UPD_WriteID, CommandCode = 0x0d, MF = true, IsWrite = true,
Direction = CommandDirection.IN, ParameterByteCount = 5, ResultByteCount = 7 }, Direction = CommandDirection.IN, ParameterByteCount = 5, ResultByteCount = 7 },
// write deleted data // write deleted data
new Command { CommandDelegate = UPD_WriteDeletedData, CommandCode = 0x09, MT = true, MF = true, IsWrite = true, new Command { CommandDelegate = UPD_WriteDeletedData, CommandCode = 0x09, MT = true, MF = true, IsWrite = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// seek // seek
new Command { CommandDelegate = UPD_Seek, CommandCode = 0x0f, new Command { CommandDelegate = UPD_Seek, CommandCode = 0x0f,
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
// recalibrate (seek track00) // recalibrate (seek track00)
new Command { CommandDelegate = UPD_Recalibrate, CommandCode = 0x07, new Command { CommandDelegate = UPD_Recalibrate, CommandCode = 0x07,
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 }, Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 },
// sense interrupt status // sense interrupt status
new Command { CommandDelegate = UPD_SenseInterruptStatus, CommandCode = 0x08, new Command { CommandDelegate = UPD_SenseInterruptStatus, CommandCode = 0x08,
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 }, Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 },
// sense drive status // sense drive status
new Command { CommandDelegate = UPD_SenseDriveStatus, CommandCode = 0x04, new Command { CommandDelegate = UPD_SenseDriveStatus, CommandCode = 0x04,
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 }, Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 },
// version // version
new Command { CommandDelegate = UPD_Version, CommandCode = 0x10, new Command { CommandDelegate = UPD_Version, CommandCode = 0x10,
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 }, Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 },
// invalid // invalid
new Command { CommandDelegate = UPD_Invalid, CommandCode = 0x00, new Command { CommandDelegate = UPD_Invalid, CommandCode = 0x00,
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 }, Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 },
}; };
} }

View File

@ -0,0 +1,651 @@
using System.Linq;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{
/// <summary>
/// Holds specfic state information about a drive
/// </summary>
public abstract class NECUPD765DriveState : IFDDHost
{
#region State
/// <summary>
/// The drive ID from an FDC perspective
/// </summary>
public int ID;
/// <summary>
/// Signs whether this drive ready
/// TRUE if both drive exists and has a disk inserted
/// </summary>
public bool FLAG_READY
{
get
{
if (!FDD_IsDiskLoaded || Disk.GetTrackCount() == 0 || !FDC.FDD_FLAG_MOTOR)
return false;
else
return true;
}
}
/// <summary>
/// Disk is write protected (TRUE BY DEFAULT)
/// </summary>
public bool FLAG_WRITEPROTECT = false;
/// <summary>
/// Storage for seek steps
/// One step for each indexpulse (track index) until seeked track
/// </summary>
public int SeekCounter;
/// <summary>
/// Seek status
/// </summary>
public int SeekStatus;
/// <summary>
/// Age counter
/// </summary>
public int SeekAge;
/// <summary>
/// The current side
/// </summary>
public byte CurrentSide;
/// <summary>
/// The current track index in the DiskTracks array
/// </summary>
public byte TrackIndex;
/// <summary>
/// The track ID of the current cylinder
/// </summary>
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;
}
}
}
}
/// <summary>
/// The new track that the drive is seeking to
/// (used in seek operations)
/// </summary>
public int SeekingTrack;
/// <summary>
/// The current sector index in the Sectors array
/// </summary>
public int SectorIndex;
/// <summary>
/// The currently loaded floppy disk
/// </summary>
public FloppyDisk Disk { get; set; }
/// <summary>
/// The parent controller
/// </summary>
private INECUPD765 FDC;
#endregion
#region Lookups
/// <summary>
/// TRUE if we are on track 0
/// </summary>
public bool FLAG_TRACK0 => TrackIndex == 0;
#endregion
#region Public Methods
#if false
/// <summary>
/// Moves the head across the disk cylinders
/// </summary>
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
/// <summary>
/// Finds a supplied sector
/// </summary>
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;
}
/// <summary>
/// Populates a result buffer
/// </summary>
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;
}
}
/// <summary>
/// Populates the result buffer with ReadID data
/// </summary>
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
/// <summary>
/// The drive performs a seek operation if necessary
/// Return value TRUE indicates seek complete
/// </summary>
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();
}
/// <summary>
/// Runs a seek cycle
/// </summary>
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;
}
}
}
/// <summary>
/// Called when a seek operation has completed
/// </summary>
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
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
public abstract void FDD_LoadDisk(byte[] diskData);
/// <summary>
/// Ejects the current disk
/// </summary>
public void FDD_EjectDisk()
{
Disk = null;
//FLAG_READY = false;
}
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
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
}
}

View File

@ -1,5 +1,5 @@
 
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// The different disk formats ZXHawk currently supports /// The different disk formats ZXHawk currently supports

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// This abstract class defines a logical floppy disk /// 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) // TrackID is consistent between the sectors although is usually high (233, 237 etc)
// SideID is fairly random looking but with all IDs being even // SideID is fairly random looking but with all IDs being even
// SectorID is also fairly random looking but contains both odd and even numbers // 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) // 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 // Each sector contains different filler byte
// Once track 0 is loaded the CPU completely reads all the sectors in this track one-by-one. // 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; return false;
} }
/* #if false
/// <summary> /// <summary>
/// Should be run at the end of the ParseDisk process /// Should be run at the end of the ParseDisk process
/// If speedlock is detected the flag is set in the disk image /// If speedlock is detected the flag is set in the disk image
/// </summary> /// </summary>
protected virtual void SpeedlockDetection() protected virtual void SpeedlockDetection()
{ {
if (DiskTracks.Length == 0) if (DiskTracks.Length == 0)
return; return;
// check for speedlock copyright notice // check for speedlock copyright notice
string ident = Encoding.ASCII.GetString(DiskData, 0x100, 0x1400); string ident = Encoding.ASCII.GetString(DiskData, 0x100, 0x1400);
if (!ident.ToUpper().Contains("SPEEDLOCK")) if (!ident.ToUpper().Contains("SPEEDLOCK"))
{ {
// speedlock not found // speedlock not found
return; return;
} }
// get cylinder 0 // get cylinder 0
var cyl = DiskTracks[0]; var cyl = DiskTracks[0];
// get sector with ID=2 // get sector with ID=2
var sec = cyl.Sectors.Where(a => a.SectorID == 2).FirstOrDefault(); var sec = cyl.Sectors.Where(a => a.SectorID == 2).FirstOrDefault();
if (sec == null) if (sec == null)
return; return;
// check for already multiple weak copies // check for already multiple weak copies
if (sec.ContainsMultipleWeakSectors || sec.SectorData.Length != 0x80 << sec.SectorSize) if (sec.ContainsMultipleWeakSectors || sec.SectorData.Length != 0x80 << sec.SectorSize)
return; return;
// check for invalid crcs in sector 2 // check for invalid crcs in sector 2
if (sec.Status1.Bit(5) || sec.Status2.Bit(5)) if (sec.Status1.Bit(5) || sec.Status2.Bit(5))
{ {
Protection = ProtectionType.Speedlock; Protection = ProtectionType.Speedlock;
} }
else else
{ {
return; return;
} }
// we are going to create a total of 5 weak sector copies // we are going to create a total of 5 weak sector copies
// keeping the original copy // keeping the original copy
byte[] origData = sec.SectorData.ToArray(); byte[] origData = sec.SectorData.ToArray();
List<byte> data = new List<byte>(); List<byte> data = new List<byte>();
//Random rnd = new Random(); //Random rnd = new Random();
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
for (int s = 0; s < origData.Length; s++) for (int s = 0; s < origData.Length; s++)
{ {
if (i == 0) if (i == 0)
{ {
data.Add(origData[s]); data.Add(origData[s]);
continue; continue;
} }
// deterministic 'random' implementation // deterministic 'random' implementation
int n = origData[s] + i + 1; int n = origData[s] + i + 1;
if (n > 0xff) if (n > 0xff)
n = n - 0xff; n = n - 0xff;
else if (n < 0) else if (n < 0)
n = 0xff + n; n = 0xff + n;
byte nByte = (byte)n; byte nByte = (byte)n;
if (s < 336) if (s < 336)
{ {
// non weak data // non weak data
data.Add(origData[s]); data.Add(origData[s]);
} }
else if (s < 511) else if (s < 511)
{ {
// weak data // weak data
data.Add(nByte); data.Add(nByte);
} }
else if (s == 511) else if (s == 511)
{ {
// final sector byte // final sector byte
data.Add(nByte); data.Add(nByte);
} }
else else
{ {
// speedlock sector should not be more than 512 bytes // speedlock sector should not be more than 512 bytes
// but in case it is just do non weak // but in case it is just do non weak
data.Add(origData[i]); data.Add(origData[i]);
} }
} }
} }
// commit the sector data // commit the sector data
sec.SectorData = data.ToArray(); sec.SectorData = data.ToArray();
sec.ContainsMultipleWeakSectors = true; sec.ContainsMultipleWeakSectors = true;
sec.ActualDataByteLength = data.Count(); sec.ActualDataByteLength = data.Count();
} }
*/ #endif
/// <summary> /// <summary>
/// Returns the track count for the disk /// Returns the track count for the disk
@ -587,8 +587,18 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
public byte NumberOfSectors { get; set; } public byte NumberOfSectors { get; set; }
public byte GAP3Length { get; set; } public byte GAP3Length { get; set; }
public byte FillerByte { 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
/// <summary> /// <summary>
/// Presents a contiguous byte array of all sector data for this track /// Presents a contiguous byte array of all sector data for this track
/// (including any multiple weak/random data) /// (including any multiple weak/random data)
@ -607,6 +617,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
return list.ToArray(); return list.ToArray();
} }
} }
#endif
} }
public class Sector public class Sector
@ -637,10 +648,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
return ActualDataByteLength; 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(); return l.ToArray();
} }
else
{ return SectorData;
return SectorData;
}
} }
else else
{ {
@ -688,14 +695,14 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
return res; return res;
/* /*
int copies = ActualDataByteLength / (0x80 << SectorSize); int copies = ActualDataByteLength / (0x80 << SectorSize);
Random rnd = new Random(); Random rnd = new Random();
int r = rnd.Next(0, copies - 1); int r = rnd.Next(0, copies - 1);
int step = r * (0x80 << SectorSize); int step = r * (0x80 << SectorSize);
byte[] res = new byte[(0x80 << SectorSize)]; byte[] res = new byte[(0x80 << SectorSize)];
Array.Copy(SectorData, step, res, 0, 0x80 << SectorSize); Array.Copy(SectorData, step, res, 0, 0x80 << SectorSize);
return res; return res;
*/ */
} }
} }
} }

View File

@ -1,5 +1,5 @@
 
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// Represents the possible commands that can be raised from each tape block /// Represents the possible commands that can be raised from each tape block

View File

@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.CPCSpectrumBase
{ {
/// <summary> /// <summary>
/// Represents a tape block /// Represents a tape block
@ -52,31 +52,31 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
set => _blockData = value; set => _blockData = value;
} }
/* #if false
/// <summary> /// <summary>
/// An array of bytearray encoded strings (stored in this format for easy Bizhawk serialization) /// An array of bytearray encoded strings (stored in this format for easy Bizhawk serialization)
/// Its basically tape information /// Its basically tape information
/// </summary> /// </summary>
private byte[][] _tapeDescriptionData; private byte[][] _tapeDescriptionData;
/// <summary> /// <summary>
/// Returns the Tape Description Data in a human readable format /// Returns the Tape Description Data in a human readable format
/// </summary> /// </summary>
public List<string> TapeDescriptionData public List<string> TapeDescriptionData
{ {
get get
{ {
List<string> data = new List<string>(); List<string> data = new List<string>();
foreach (byte[] b in _tapeDescriptionData) foreach (byte[] b in _tapeDescriptionData)
{ {
data.Add(Encoding.ASCII.GetString(b)); data.Add(Encoding.ASCII.GetString(b));
} }
return data; return data;
} }
} }
*/ #endif
#region Block Meta Data #region Block Meta Data

View File

@ -1,29 +0,0 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Defines an object that can load a floppy disk image
/// </summary>
public interface IFDDHost
{
/// <summary>
/// The currently inserted diskimage
/// </summary>
FloppyDisk Disk { get; set; }
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
void FDD_LoadDisk(byte[] diskData);
/// <summary>
/// Ejects the current disk
/// </summary>
void FDD_EjectDisk();
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
bool FDD_IsDiskLoaded { get; }
}
}

View File

@ -1,34 +0,0 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Represents a spectrum joystick
/// </summary>
public interface IJoystick
{
/// <summary>
/// The type of joystick
/// </summary>
JoystickType JoyType { get; }
/// <summary>
/// Array of all the possibly button press names
/// </summary>
string[] ButtonCollection { get; set; }
/// <summary>
/// The player number that this controller is currently assigned to
/// </summary>
int PlayerNumber { get; set; }
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
void SetJoyInput(string key, bool isPressed);
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
bool GetJoyInput(string key);
}
}

View File

@ -1,4 +1,5 @@
using BizHawk.Common; using BizHawk.Common;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {

View File

@ -1,5 +1,6 @@
using BizHawk.Common; using BizHawk.Common;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {

View File

@ -1,19 +0,0 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Represents a device that utilizes port IN & OUT
/// </summary>
public interface IPortIODevice
{
/// <summary>
/// Device responds to an IN instruction
/// </summary>
bool ReadPort(ushort port, ref int result);
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
bool WritePort(ushort port, int result);
}
}

View File

@ -4,6 +4,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
using BizHawk.Emulation.Cores.Sound; using BizHawk.Emulation.Cores.Sound;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum

View File

@ -1,180 +0,0 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Used for the sector CHRN structure
/// </summary>
public class CHRN
{
/// <summary>
/// Track
/// </summary>
public byte C { get; set; }
/// <summary>
/// Side
/// </summary>
public byte H { get; set; }
/// <summary>
/// Sector ID
/// </summary>
public byte R { get; set; }
/// <summary>
/// Sector Size
/// </summary>
public byte N { get; set; }
/// <summary>
/// Status register 1
/// </summary>
private byte _flag1;
public byte Flag1
{
get => _flag1;
set => _flag1 = value;
}
/// <summary>
/// Status register 2
/// </summary>
private byte _flag2;
public byte Flag2
{
get => _flag2;
set => _flag2 = value;
}
/// <summary>
/// Used to store the last transmitted/received data bytes
/// </summary>
public byte[] DataBytes { get; set; }
/// <summary>
/// ID for the read/write data command
/// </summary>
public int DataID { get; set; }
#region Helper Methods
/// <summary>
/// Missing Address Mark (Sector_ID or DAM not found)
/// </summary>
public bool ST1MA
{
get => NECUPD765.GetBit(0, _flag1);
set
{
if (value) { NECUPD765.SetBit(0, ref _flag1); }
else { NECUPD765.UnSetBit(0, ref _flag1); }
}
}
/// <summary>
/// No Data (Sector_ID not found, CRC fail in ID_field)
/// </summary>
public bool ST1ND
{
get => NECUPD765.GetBit(2, _flag1);
set
{
if (value) { NECUPD765.SetBit(2, ref _flag1); }
else { NECUPD765.UnSetBit(2, ref _flag1); }
}
}
/// <summary>
/// Data Error (CRC-fail in ID- or Data-Field)
/// </summary>
public bool ST1DE
{
get => NECUPD765.GetBit(5, _flag1);
set
{
if (value) { NECUPD765.SetBit(5, ref _flag1); }
else { NECUPD765.UnSetBit(5, ref _flag1); }
}
}
/// <summary>
/// End of Track (set past most read/write commands) (see IC)
/// </summary>
public bool ST1EN
{
get => NECUPD765.GetBit(7, _flag1);
set
{
if (value) { NECUPD765.SetBit(7, ref _flag1); }
else { NECUPD765.UnSetBit(7, ref _flag1); }
}
}
/// <summary>
/// Missing Address Mark in Data Field (DAM not found)
/// </summary>
public bool ST2MD
{
get => NECUPD765.GetBit(0, _flag2);
set
{
if (value) { NECUPD765.SetBit(0, ref _flag2); }
else { NECUPD765.UnSetBit(0, ref _flag2); }
}
}
/// <summary>
/// Bad Cylinder (read/programmed track-ID different and read-ID = FF)
/// </summary>
public bool ST2BC
{
get => NECUPD765.GetBit(1, _flag2);
set
{
if (value) { NECUPD765.SetBit(1, ref _flag2); }
else { NECUPD765.UnSetBit(1, ref _flag2); }
}
}
/// <summary>
/// Wrong Cylinder (read/programmed track-ID different) (see b1)
/// </summary>
public bool ST2WC
{
get => NECUPD765.GetBit(4, _flag2);
set
{
if (value) { NECUPD765.SetBit(4, ref _flag2); }
else { NECUPD765.UnSetBit(4, ref _flag2); }
}
}
/// <summary>
/// Data Error in Data Field (CRC-fail in data-field)
/// </summary>
public bool ST2DD
{
get => NECUPD765.GetBit(5, _flag2);
set
{
if (value) { NECUPD765.SetBit(5, ref _flag2); }
else { NECUPD765.UnSetBit(5, ref _flag2); }
}
}
/// <summary>
/// Control Mark (read/scan command found sector with deleted DAM)
/// </summary>
public bool ST2CM
{
get => NECUPD765.GetBit(6, _flag2);
set
{
if (value) { NECUPD765.SetBit(6, ref _flag2); }
else { NECUPD765.UnSetBit(6, ref _flag2); }
}
}
#endregion
}
}

View File

@ -1,826 +0,0 @@
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(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
}
}

View File

@ -1,876 +0,0 @@
using BizHawk.Common;
using BizHawk.Common.NumberExtensions;
using System;
using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Floppy drive related stuff
/// </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 : IFDDHost
{
#region Drive State
/// <summary>
/// FDD Flag - motor on/off
/// </summary>
public bool FDD_FLAG_MOTOR;
/// <summary>
/// The index of the currently active disk drive
/// </summary>
public int DiskDriveIndex
{
get => _diskDriveIndex;
set
{
// when index is changed update the ActiveDrive
_diskDriveIndex = value;
ActiveDrive = DriveStates[_diskDriveIndex];
}
}
private int _diskDriveIndex = 0;
/// <summary>
/// The currently active drive
/// </summary>
private DriveState ActiveDrive;
/// <summary>
/// Array that holds state information for each possible drive
/// </summary>
private DriveState[] DriveStates = new DriveState[4];
#endregion
#region FDD Methods
/// <summary>
/// Initialization / reset of the floppy drive subsystem
/// </summary>
private void FDD_Init()
{
for (int i = 0; i < 4; i++)
{
DriveState ds = new DriveState(i, this);
DriveStates[i] = ds;
}
}
/// <summary>
/// Searches for the requested sector
/// </summary>
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
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
public void FDD_LoadDisk(byte[] diskData)
{
// we are only going to load into the first drive
DriveStates[0].FDD_LoadDisk(diskData);
}
/// <summary>
/// Ejects the current disk
/// </summary>
public void FDD_EjectDisk()
{
DriveStates[0].FDD_EjectDisk();
}
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
public bool FDD_IsDiskLoaded => DriveStates[DiskDriveIndex].FDD_IsDiskLoaded;
/// <summary>
/// Returns the disk object from drive 0
/// </summary>
public FloppyDisk DiskPointer => DriveStates[0].Disk;
public FloppyDisk Disk { get; set; }
#endregion
#region Drive Status Class
/// <summary>
/// Holds specfic state information about a drive
/// </summary>
private class DriveState : IFDDHost
{
#region State
/// <summary>
/// The drive ID from an FDC perspective
/// </summary>
public int ID;
/// <summary>
/// Signs whether this drive ready
/// TRUE if both drive exists and has a disk inserted
/// </summary>
public bool FLAG_READY
{
get
{
if (!FDD_IsDiskLoaded || Disk.GetTrackCount() == 0 || !FDC.FDD_FLAG_MOTOR)
return false;
else
return true;
}
}
/// <summary>
/// Disk is write protected (TRUE BY DEFAULT)
/// </summary>
public bool FLAG_WRITEPROTECT = false;
/// <summary>
/// Storage for seek steps
/// One step for each indexpulse (track index) until seeked track
/// </summary>
public int SeekCounter;
/// <summary>
/// Seek status
/// </summary>
public int SeekStatus;
/// <summary>
/// Age counter
/// </summary>
public int SeekAge;
/// <summary>
/// The current side
/// </summary>
public byte CurrentSide;
/// <summary>
/// The current track index in the DiskTracks array
/// </summary>
public byte TrackIndex;
/// <summary>
/// The track ID of the current cylinder
/// </summary>
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;
}
}
}
}
/// <summary>
/// The new track that the drive is seeking to
/// (used in seek operations)
/// </summary>
public int SeekingTrack;
/// <summary>
/// The current sector index in the Sectors array
/// </summary>
public int SectorIndex;
/// <summary>
/// The currently loaded floppy disk
/// </summary>
public FloppyDisk Disk { get; set; }
/// <summary>
/// The parent controller
/// </summary>
private NECUPD765 FDC;
#endregion
#region Lookups
/// <summary>
/// TRUE if we are on track 0
/// </summary>
public bool FLAG_TRACK0 => TrackIndex == 0;
#endregion
#region Public Methods
/*
/// <summary>
/// Moves the head across the disk cylinders
/// </summary>
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;
}
*/
/*
/// <summary>
/// Finds a supplied sector
/// </summary>
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;
}
/// <summary>
/// Populates a result buffer
/// </summary>
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;
}
}
/// <summary>
/// Populates the result buffer with ReadID data
/// </summary>
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;
}
}
*/
/*
/// <summary>
/// The drive performs a seek operation if necessary
/// Return value TRUE indicates seek complete
/// </summary>
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();
}
/// <summary>
/// Runs a seek cycle
/// </summary>
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;
}
}
}
/// <summary>
/// Called when a seek operation has completed
/// </summary>
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
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
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.");
}
}
/// <summary>
/// Ejects the current disk
/// </summary>
public void FDD_EjectDisk()
{
Disk = null;
//FLAG_READY = false;
}
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
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
}
}

View File

@ -1,145 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// IPortIODevice
/// </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 : 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<string> dLog = new List<string>
{
"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
/// <summary>
/// Device responds to an IN instruction
/// </summary>
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;
}
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
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;
}
}
}

View File

@ -1,120 +0,0 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Timimng
/// </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
{
/// <summary>
/// The current Z80 cycle
/// </summary>
private long CurrentCPUCycle
{
get
{
if (_machine == null)
return 0;
else
return _machine.CPU.TotalExecutedCycles;
}
}
/// <summary>
/// The last CPU cycle when the FDC accepted an IO read/write
/// </summary>
private long LastCPUCycle;
/// <summary>
/// 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
/// </summary>
private long StatusDelay;
/// <summary>
/// Defines the numbers of Z80 cycles per MS
/// </summary>
private long CPUCyclesPerMs;
/// <summary>
/// The floppy drive emulated clock speed
/// </summary>
public const double DriveClock = 31250;
/// <summary>
/// The number of floppy drive cycles per MS
/// </summary>
public long DriveCyclesPerMs;
/// <summary>
/// The number of T-States in one floppy drive clock tick
/// </summary>
public long StatesPerDriveTick;
/// <summary>
/// Responsible for measuring when the floppy drive is ready to run a cycle
/// </summary>
private long TickCounter;
/// <summary>
/// Internal drive cycle counter
/// </summary>
private int DriveCycleCounter = 1;
/// <summary>
/// Initializes the timing routines
/// </summary>
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;
}
/// <summary>
/// Called by reads to the main status register
/// Returns true if there is no delay
/// Returns false if read is to be deferred
/// </summary>
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;
}
}
}
}

View File

@ -1,248 +0,0 @@
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>
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)
{
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
}
}

View File

@ -0,0 +1,75 @@
using System;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
public class NECUPD765Spectrum : NECUPD765<SpectrumBase, NECUPD765Spectrum.ZXDriveState>
{
protected override ZXDriveState ConstructDriveState(int driveID, NECUPD765<SpectrumBase, ZXDriveState> 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<SpectrumBase, ZXDriveState> 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.");
}
}
}
}
}

View File

@ -1,96 +0,0 @@
using System.Collections;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Static helper methods
/// </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
{
/// <summary>
/// Returns the specified bit value from supplied byte
/// </summary>
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];
}
/// <summary>
/// Sets the specified bit of the supplied byte to 1
/// </summary>
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;
}
/// <summary>
/// Sets the specified bit of the supplied byte to 0
/// </summary>
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;
}
/// <summary>
/// Returns a drive number (0-3) based on the first two bits of the supplied byte
/// </summary>
public static int GetUnitSelect(byte dataByte)
{
int driveNumber = dataByte & 0x03;
return driveNumber;
}
/// <summary>
/// Sets the first two bits of a byte based on the supplied drive number (0-3)
/// </summary>
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;
}
}
}
}

View File

@ -1,13 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// Cursor joystick /// Cursor joystick
/// Maps to a combination of 0xf7fe and 0xeffe /// Maps to a combination of 0xf7fe and 0xeffe
/// </summary> /// </summary>
public class CursorJoystick : IJoystick public class CursorJoystick : IJoystick<JoystickType>
{ {
//private int _joyLine; //private int _joyLine;
private SpectrumBase _machine; private SpectrumBase _machine;

View File

@ -1,9 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
public class KempstonJoystick : IJoystick public class KempstonJoystick : IJoystick<JoystickType>
{ {
private int _joyLine; private int _joyLine;
private SpectrumBase _machine; private SpectrumBase _machine;

View File

@ -1,12 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// A null joystick object /// A null joystick object
/// </summary> /// </summary>
public class NullJoystick : IJoystick public class NullJoystick : IJoystick<JoystickType>
{ {
private int _joyLine; private int _joyLine;
private SpectrumBase _machine; private SpectrumBase _machine;

View File

@ -1,13 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// Sinclair Joystick LEFT /// Sinclair Joystick LEFT
/// Just maps to the standard keyboard and is read the same (from port 0xf7fe) /// Just maps to the standard keyboard and is read the same (from port 0xf7fe)
/// </summary> /// </summary>
public class SinclairJoystick1 : IJoystick public class SinclairJoystick1 : IJoystick<JoystickType>
{ {
//private int _joyLine; //private int _joyLine;
private SpectrumBase _machine; private SpectrumBase _machine;

View File

@ -1,13 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// Sinclair Joystick RIGHT /// Sinclair Joystick RIGHT
/// Just maps to the standard keyboard and is read the same (from port 0xeffe) /// Just maps to the standard keyboard and is read the same (from port 0xeffe)
/// </summary> /// </summary>
public class SinclairJoystick2 : IJoystick public class SinclairJoystick2 : IJoystick<JoystickType>
{ {
//private int _joyLine; //private int _joyLine;
private SpectrumBase _machine; private SpectrumBase _machine;

View File

@ -1,6 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
@ -280,7 +282,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary> /// </summary>
protected void InitJoysticks(List<JoystickType> joys) protected void InitJoysticks(List<JoystickType> joys)
{ {
var jCollection = new List<IJoystick>(); var jCollection = new List<IJoystick<JoystickType>>();
for (int i = 0; i < joys.Count(); i++) for (int i = 0; i < joys.Count(); i++)
{ {
@ -298,7 +300,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <summary> /// <summary>
/// Instantiates a new IJoystick object /// Instantiates a new IJoystick object
/// </summary> /// </summary>
public IJoystick InstantiateJoystick(JoystickType type, int playerNumber) public IJoystick<JoystickType> InstantiateJoystick(JoystickType type, int playerNumber)
{ {
switch (type) switch (type)
{ {
@ -320,7 +322,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <summary> /// <summary>
/// Returns a IJoystick object depending on the type (or null if not found) /// Returns a IJoystick object depending on the type (or null if not found)
/// </summary> /// </summary>
protected IJoystick LocateUniqueJoystick(JoystickType type) protected IJoystick<JoystickType> LocateUniqueJoystick(JoystickType type)
{ {
return JoystickCollection.FirstOrDefault(a => a.JoyType == type); return JoystickCollection.FirstOrDefault(a => a.JoyType == type);
} }

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>

View File

@ -1,5 +1,6 @@
using BizHawk.Common; using BizHawk.Common;
using BizHawk.Emulation.Cores.Components.Z80A; using BizHawk.Emulation.Cores.Components.Z80A;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
using BizHawk.Emulation.Cores.Sound; using BizHawk.Emulation.Cores.Sound;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum 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 /// The abstract class that all emulated models will inherit from
/// * Main properties / fields / contruction* /// * Main properties / fields / contruction*
/// </summary> /// </summary>
public abstract partial class SpectrumBase public abstract partial class SpectrumBase : CPCSpectrumBase.CPCSpectrumBase
{ {
#region Devices #region Devices
@ -66,12 +67,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <summary> /// <summary>
/// The +3 built-in disk drive /// The +3 built-in disk drive
/// </summary> /// </summary>
public virtual NECUPD765 UPDDiskDevice { get; set; } public virtual NECUPD765Spectrum UPDDiskDevice { get; set; }
/// <summary> /// <summary>
/// Holds the currently selected joysticks /// Holds the currently selected joysticks
/// </summary> /// </summary>
public virtual IJoystick[] JoystickCollection { get; set; } public virtual IJoystick<JoystickType>[] JoystickCollection { get; set; }
/// <summary> /// <summary>
/// +3/2a printer port strobe /// +3/2a printer port strobe

View File

@ -42,7 +42,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape); TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this); TapeDevice.Init(this);
UPDDiskDevice = new NECUPD765(); UPDDiskDevice = new NECUPD765Spectrum();
UPDDiskDevice.Init(this); UPDDiskDevice.Init(this);
InitializeMedia(files); InitializeMedia(files);

View File

@ -3,6 +3,8 @@ using BizHawk.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>

View File

@ -3,6 +3,8 @@ using BizHawk.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>

View File

@ -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
{
/// <summary>
/// This abstract class defines a logical floppy disk
/// </summary>
public abstract class FloppyDisk
{
/// <summary>
/// The disk format type
/// </summary>
public abstract DiskType DiskFormatType { get; }
/// <summary>
/// Disk information header
/// </summary>
public Header DiskHeader = new Header();
/// <summary>
/// Track array
/// </summary>
public Track[] DiskTracks = null;
/// <summary>
/// No. of tracks per side
/// </summary>
public int CylinderCount;
/// <summary>
/// The number of physical sides
/// </summary>
public int SideCount;
/// <summary>
/// The number of bytes per track
/// </summary>
public int BytesPerTrack;
/// <summary>
/// The write-protect tab on the disk
/// </summary>
public bool WriteProtected;
/// <summary>
/// The detected protection scheme (if any)
/// </summary>
public ProtectionType Protection;
/// <summary>
/// The actual disk image data
/// </summary>
public byte[] DiskData;
/// <summary>
/// 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
/// </summary>
protected bool DirtyData = false;
/// <summary>
/// Used to deterministically choose a 'random' sector when dealing with weak reads
/// </summary>
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;
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public virtual bool ParseDisk(byte[] diskData)
{
// default result
// override in inheriting class
return false;
}
/// <summary>
/// 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
/// </summary>
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<byte> data = new List<byte>();
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;
}
}
/// <summary>
/// Detection routine for shadow of the beast game
/// Still cannot get this to work, but at least the game is detected
/// </summary>
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;
}
/// <summary>
/// Detect speedlock weak sector
/// </summary>
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;
}
/// <summary>
/// Detect Alkatraz
/// </summary>
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;
}
/// <summary>
/// Detect Paul Owens
/// </summary>
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;
}
/// <summary>
/// Detect Hexagon copy protection
/// </summary>
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;
}
/*
/// <summary>
/// Should be run at the end of the ParseDisk process
/// If speedlock is detected the flag is set in the disk image
/// </summary>
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<byte> data = new List<byte>();
//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();
}
*/
/// <summary>
/// Returns the track count for the disk
/// </summary>
public virtual int GetTrackCount()
{
return DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides;
}
/// <summary>
/// Reads the current sector ID info
/// </summary>
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;
}
/// <summary>
/// State serialization routines
/// </summary>
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
/// <summary>
/// Presents a contiguous byte array of all sector data for this track
/// (including any multiple weak/random data)
/// </summary>
public virtual byte[] TrackSectorData
{
get
{
List<byte> list = new List<byte>();
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<byte> l = new List<byte>();
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,
};
}
}
/// <summary>
/// Defines the type of speedlock detection found
/// </summary>
public enum ProtectionType
{
None,
Speedlock,
Alkatraz,
Hexagon,
Frontier,
PaulOwens,
ShadowOfTheBeast
}
}

View File

@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
public class IPFFloppyDisk : FloppyDisk public class IPFFloppyDisk : FloppyDisk

View File

@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
public class UDI1_0FloppyDisk : FloppyDisk public class UDI1_0FloppyDisk : FloppyDisk

View File

@ -3,6 +3,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>

View File

@ -3,6 +3,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>

View File

@ -4,6 +4,8 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>

View File

@ -1,16 +0,0 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Represents the possible commands that can be raised from each tape block
/// </summary>
public enum TapeCommand
{
NONE,
STOP_THE_TAPE,
STOP_THE_TAPE_48K,
BEGIN_GROUP,
END_GROUP,
SHOW_MESSAGE,
}
}

View File

@ -1,282 +0,0 @@
using BizHawk.Common;
using System.Collections.Generic;
using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Represents a tape block
/// </summary>
public class TapeDataBlock
{
/// <summary>
/// Either the TZX block ID, or -1 in the case of non-tzx blocks
/// </summary>
private int _blockID = -1;
public int BlockID
{
get => _blockID;
set
{
_blockID = value;
if (MetaData == null)
MetaData = new Dictionary<BlockDescriptorTitle, string>();
AddMetaData(BlockDescriptorTitle.Block_ID, value.ToString());
}
}
/// <summary>
/// The block type
/// </summary>
private BlockType _blockType;
public BlockType BlockDescription
{
get => _blockType;
set
{
_blockType = value;
if (MetaData == null)
MetaData = new Dictionary<BlockDescriptorTitle, string>();
}
}
/// <summary>
/// Byte array containing the raw block data
/// </summary>
private byte[] _blockData;
public byte[] BlockData
{
get => _blockData;
set => _blockData = value;
}
/*
/// <summary>
/// An array of bytearray encoded strings (stored in this format for easy Bizhawk serialization)
/// Its basically tape information
/// </summary>
private byte[][] _tapeDescriptionData;
/// <summary>
/// Returns the Tape Description Data in a human readable format
/// </summary>
public List<string> TapeDescriptionData
{
get
{
List<string> data = new List<string>();
foreach (byte[] b in _tapeDescriptionData)
{
data.Add(Encoding.ASCII.GetString(b));
}
return data;
}
}
*/
#region Block Meta Data
/// <summary>
/// Dictionary of block related data
/// </summary>
public Dictionary<BlockDescriptorTitle, string> MetaData { get; set; }
/// <summary>
/// Adds a single metadata item to the Dictionary
/// </summary>
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
/// <summary>
/// List containing the pulse timing values
/// </summary>
public List<int> DataPeriods = new List<int>();
public bool InitialPulseLevel;
/// <summary>
/// Command that is raised by this data block
/// (that may or may not need to be acted on)
/// </summary>
private TapeCommand _command = TapeCommand.NONE;
public TapeCommand Command
{
get => _command;
set => _command = value;
}
/// <summary>
/// The defined post-block pause
/// </summary>
private int _pauseInMS;
public int PauseInMS
{
get => _pauseInMS;
set => _pauseInMS = value;
}
/// <summary>
/// Returns the data periods as an array
/// (primarily to aid in bizhawk state serialization)
/// </summary>
public int[] GetDataPeriodsArray()
{
return DataPeriods.ToArray();
}
/// <summary>
/// Accepts an array of data periods and updates the DataPeriods list accordingly
/// (primarily to aid in bizhawk state serialization)
/// </summary>
public void SetDataPeriodsArray(int[] periodArray)
{
DataPeriods = periodArray?.ToList() ?? new List<int>();
}
/// <summary>
/// Bizhawk state serialization
/// </summary>
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();
}
}
/// <summary>
/// The types of TZX blocks
/// </summary>
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
}
/// <summary>
/// Different title possibilities
/// </summary>
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
}
}

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>

View File

@ -2,6 +2,8 @@
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using BizHawk.Emulation.Cores.Computers.CPCSpectrumBase;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>