ZXHawk: Overhaul datacorder and cassette loading code. This should now be more accurate and fixes a number of loading isues with particular games and loading schemes. There be desync dragons here!! Fixes #1446

This commit is contained in:
Matt Burgess 2021-06-11 23:41:14 +01:00
parent 160217ef74
commit 51a67a947a
9 changed files with 1525 additions and 1123 deletions

View File

@ -122,14 +122,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
MonitorFrame();
}
/// <summary>
/// No longer in use
/// </summary>
public void StartFrame()
{
//_buzzer.ProcessPulseValue(currentState);
}
/// <summary>
/// Starts the tape playing from the beginning of the current block
/// </summary>
@ -466,27 +458,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
cycles -= _waitEdge;
if (_position == 0 && _tapeIsPlaying)
{
// start of block - take care of initial pulse level for PZX
switch (_dataBlocks[_currentDataBlockIndex].BlockDescription)
{
case BlockType.PULS:
// initial pulse level is always low
if (currentState)
FlipTapeState();
break;
case BlockType.DATA:
// initial pulse level is stored in block
if (currentState != _dataBlocks[_currentDataBlockIndex].InitialPulseLevel)
FlipTapeState();
break;
case BlockType.PAUS:
// initial pulse level is stored in block
if (currentState != _dataBlocks[_currentDataBlockIndex].InitialPulseLevel)
FlipTapeState();
break;
}
{
// notify about the current block
var bl = _dataBlocks[_currentDataBlockIndex];
@ -595,7 +567,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
_waitEdge = _dataBlocks[_currentDataBlockIndex].DataPeriods.Count > 0 ? _dataBlocks[_currentDataBlockIndex].DataPeriods[_position] : 0;
// flip the current state
FlipTapeState();
//FlipTapeState();
currentState = _dataBlocks[_currentDataBlockIndex].DataLevels[_position];
}
// update lastCycle and return currentstate

View File

@ -208,17 +208,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
CPU.RegPC = 0;
Spectrum.SetCpuRegister("SP", 0xFFFF);
Spectrum.SetCpuRegister("IY", 0xFFFF);
Spectrum.SetCpuRegister("IX", 0xFFFF);
Spectrum.SetCpuRegister("IY", 0);
Spectrum.SetCpuRegister("IX", 0);
Spectrum.SetCpuRegister("AF", 0xFFFF);
Spectrum.SetCpuRegister("BC", 0xFFFF);
Spectrum.SetCpuRegister("DE", 0xFFFF);
Spectrum.SetCpuRegister("HL", 0xFFFF);
Spectrum.SetCpuRegister("BC", 0);
Spectrum.SetCpuRegister("DE", 0);
Spectrum.SetCpuRegister("HL", 0);
Spectrum.SetCpuRegister("SP", 0xFFFF);
Spectrum.SetCpuRegister("Shadow AF", 0xFFFF);
Spectrum.SetCpuRegister("Shadow BC", 0xFFFF);
Spectrum.SetCpuRegister("Shadow DE", 0xFFFF);
Spectrum.SetCpuRegister("Shadow HL", 0xFFFF);
Spectrum.SetCpuRegister("Shadow BC", 0);
Spectrum.SetCpuRegister("Shadow DE", 0);
Spectrum.SetCpuRegister("Shadow HL", 0);
CPU.Regs[CPU.I] = 0;
CPU.Regs[CPU.R] = 0;
@ -260,17 +260,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
CPU.RegPC = 0;
Spectrum.SetCpuRegister("SP", 0xFFFF);
Spectrum.SetCpuRegister("IY", 0xFFFF);
Spectrum.SetCpuRegister("IX", 0xFFFF);
//Spectrum.SetCpuRegister("IY", 0xFFFF);
//Spectrum.SetCpuRegister("IX", 0xFFFF);
Spectrum.SetCpuRegister("AF", 0xFFFF);
Spectrum.SetCpuRegister("BC", 0xFFFF);
Spectrum.SetCpuRegister("DE", 0xFFFF);
Spectrum.SetCpuRegister("HL", 0xFFFF);
Spectrum.SetCpuRegister("SP", 0xFFFF);
//Spectrum.SetCpuRegister("BC", 0xFFFF);
//Spectrum.SetCpuRegister("DE", 0xFFFF);
//Spectrum.SetCpuRegister("HL", 0xFFFF);
Spectrum.SetCpuRegister("Shadow AF", 0xFFFF);
Spectrum.SetCpuRegister("Shadow BC", 0xFFFF);
Spectrum.SetCpuRegister("Shadow DE", 0xFFFF);
Spectrum.SetCpuRegister("Shadow HL", 0xFFFF);
//Spectrum.SetCpuRegister("Shadow BC", 0xFFFF);
//Spectrum.SetCpuRegister("Shadow DE", 0xFFFF);
//Spectrum.SetCpuRegister("Shadow HL", 0xFFFF);
CPU.Regs[CPU.I] = 0;
CPU.Regs[CPU.R] = 0;

View File

@ -145,15 +145,35 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// Takes a PauseInMilliseconds value and returns the value in T-States
/// </summary>
public static int TranslatePause(int pauseInMS)
{
// t-states per millisecond
var tspms = (69888 * 50) / 1000;
// get value
int res = pauseInMS * tspms;
{
var tspms = (double)(69888 * 50) / (double)1000;
int res = (int)(pauseInMS * tspms);
return res;
}
/// <summary>
/// Caluclate a data block XOR checksum
/// </summary>
/// <param name="buf"></param>
/// <param name="len"></param>
/// <returns></returns>
public static bool CheckChecksum(byte[] buf, int len)
{
byte c = 0;
for (int n = 0; n < len - 1; n++)
{
c ^= buf[n];
}
if (c == buf[len - 1])
{
return true;
}
return false;
}
/// <summary>
/// Decompresses a byte array that is Z-RLE compressed
/// </summary>

View File

@ -197,12 +197,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
t.BlockDescription = BlockType.CSW_Recording;
t.BlockID = 0x18;
t.DataPeriods = new List<int>();
t.DataLevels = new List<bool>();
if (flags.Bit(0))
t.InitialPulseLevel = true;
else
t.InitialPulseLevel = false;
bool currLevel = !t.InitialPulseLevel;
var rate = (69888 * 50) / sampleRate;
for (int i = 0; i < cswDataUncompressed.Length;)
@ -215,10 +218,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
t.DataPeriods.Add(length);
currLevel = !currLevel;
t.DataLevels.Add(currLevel);
}
// add closing period
t.DataPeriods.Add((69888 * 50) / 10);
currLevel = !currLevel;
t.DataLevels.Add(currLevel);
// add to datacorder
_datacorder.DataBlocks.Add(t);

View File

@ -158,8 +158,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
t.BlockID = GetInt32(b, 0);
t.DataPeriods = new List<int>();
t.DataLevels = new List<bool>();
t.InitialPulseLevel = false;
bool pLevel = !t.InitialPulseLevel;
List<ushort[]> pulses = new List<ushort[]>();
@ -197,6 +199,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
for (int i = 0; i < x[0]; i++)
{
t.DataPeriods.Add(x[1]);
pLevel = !pLevel;
t.DataLevels.Add(pLevel);
}
}
@ -220,6 +224,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
t.BlockID = GetInt32(b, 0);
t.DataPeriods = new List<int>();
t.DataLevels = new List<bool>();
List<ushort> s0 = new List<ushort>();
List<ushort> s1 = new List<ushort>();
@ -235,6 +240,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
initPulseLevel = (uint)((dCount & 0x80000000) == 0 ? 0 : 1);
t.InitialPulseLevel = initPulseLevel == 1;
bool bLevel = !t.InitialPulseLevel;
dCount = (int)(dCount & 0x7FFFFFFF);
pos += 4;
@ -274,6 +280,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
foreach (var pu in s1)
{
t.DataPeriods.Add((int)pu);
bLevel = !bLevel;
t.DataLevels.Add(bLevel);
}
}
@ -282,13 +290,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
foreach (var pu in s0)
{
t.DataPeriods.Add((int)pu);
bLevel = !bLevel;
t.DataLevels.Add(bLevel);
}
}
}
}
if (tail > 0)
{
t.DataPeriods.Add(tail);
bLevel = !bLevel;
t.DataLevels.Add(bLevel);
}
dData.Clear();
}
@ -320,12 +335,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
var d = GetInt32(b, pos);
iniPulseLevel = ((d & 0x80000000) == 0 ? 0 : 1);
t.InitialPulseLevel = iniPulseLevel == 1;
bool paLevel = !t.InitialPulseLevel;
pCount = (d & 0x7FFFFFFF);
// convert to tape block
t.BlockDescription = BlockType.PAUS;
paLevel = !paLevel;
t.DataLevels.Add(paLevel);
t.DataPeriods.Add(0);
paLevel = !paLevel;
t.DataLevels.Add(paLevel);
t.DataPeriods.Add(pCount);
paLevel = !paLevel;
t.DataLevels.Add(paLevel);
t.DataPeriods.Add(0);
_datacorder.DataBlocks.Add(t);

View File

@ -277,16 +277,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// create a list to hold the data periods
List<int> dataPeriods = new List<int>();
List<bool> dataLevels = new List<bool>();
bool currLevel = false;
// generate pilot pulses
for (int i = 0; i < pilotLength; i++)
{
dataPeriods.Add(PILOT_PL);
currLevel = !currLevel;
dataLevels.Add(currLevel);
}
// add syncro pulses
dataPeriods.Add(SYNC_1_PL);
currLevel = !currLevel;
dataLevels.Add(currLevel);
dataPeriods.Add(SYNC_2_PL);
currLevel = !currLevel;
dataLevels.Add(currLevel);
int pos = 0;
@ -296,13 +305,33 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
for (byte b = 0x80; b != 0; b >>= 1)
{
if ((blockdata[i] & b) != 0)
{
dataPeriods.Add(BIT_1_PL);
currLevel = !currLevel;
dataLevels.Add(currLevel);
}
else
{
dataPeriods.Add(BIT_0_PL);
currLevel = !currLevel;
dataLevels.Add(currLevel);
}
if ((blockdata[i] & b) != 0)
{
dataPeriods.Add(BIT_1_PL);
currLevel = !currLevel;
dataLevels.Add(currLevel);
}
else
{
dataPeriods.Add(BIT_0_PL);
currLevel = !currLevel;
dataLevels.Add(currLevel);
}
}
}
@ -310,13 +339,29 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
for (byte c = 0x80; c != (byte)(0x80 >> BIT_COUNT_IN_LAST); c >>= 1)
{
if ((blockdata[pos] & c) != 0)
{
dataPeriods.Add(BIT_1_PL);
currLevel = !currLevel;
dataLevels.Add(currLevel);
}
else
{
dataPeriods.Add(BIT_0_PL);
currLevel = !currLevel;
dataLevels.Add(currLevel);
}
if ((blockdata[pos] & c) != 0)
{
dataPeriods.Add(BIT_1_PL);
currLevel = !currLevel;
dataLevels.Add(currLevel);
}
else
{
dataPeriods.Add(BIT_0_PL);
currLevel = !currLevel;
dataLevels.Add(currLevel);
}
}
// add block pause
@ -325,14 +370,27 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// default pause for tap files
tdb.PauseInMS = 1000;
tdb.PauseInTStates = TranslatePause(tdb.PauseInMS);
// small inversion
dataPeriods.Add(3476);
currLevel = !currLevel;
dataLevels.Add(currLevel);
// actual pause
dataPeriods.Add(tdb.PauseInTStates - 3476);
currLevel = !currLevel;
dataLevels.Add(currLevel);
// add to the tapedatablock object
tdb.DataPeriods = dataPeriods;
tdb.DataLevels = dataLevels;
// add the raw data
tdb.BlockData = blockdata;
// generate separate PAUS block
/*
TapeDataBlock tdbPause = new TapeDataBlock();
tdbPause.DataPeriods = new List<int>();
tdbPause.BlockDescription = BlockType.PAUSE_BLOCK;
@ -341,10 +399,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
//if (pauseInTStates > 0)
//tdbPause.DataPeriods.Add(pauseInTStates);
tdb.PauseInMS = 0;
tdb.PauseInTStates = pauseInTStates;
*/
// add block to the tape
_datacorder.DataBlocks.Add(tdb);
/*
// PAUS block if neccessary
if (pauseInTStates > 0)
{
@ -366,6 +427,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
_datacorder.DataBlocks.Add(tdbPause);
}
*/
}
}
}

View File

@ -9,6 +9,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public class TapeDataBlock
{
public TapeDataBlock()
{
DataPeriods = new List<int>();
DataLevels = new List<bool>();
}
/// <summary>
/// Either the TZX block ID, or -1 in the case of non-tzx blocks
/// </summary>
@ -77,6 +83,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public List<int> DataPeriods = new List<int>();
/// <summary>
/// List containing the pulse levels (in relation to the pulse timing values)
/// </summary>
public List<bool> DataLevels = new List<bool>();
public List<string> PulseDescription = new List<string>();
public bool InitialPulseLevel;
/// <summary>
@ -91,7 +104,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
/// <summary>
/// The defined post-block pause
/// The defined post-block pause in MS
/// </summary>
private int _pauseInMS;
public int PauseInMS
@ -100,6 +113,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
set => _pauseInMS = value;
}
/// <summary>
/// The defined post-block pause in T-States
/// </summary>
private int _pauseInTStates;
public int PauseInTStates
{
get { return _pauseInTStates; }
set { _pauseInTStates = value; }
}
/// <summary>
/// Returns the data periods as an array
@ -226,6 +248,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
Pulse_Length,
Pulse_Count,
TStatesPerSample,
Archive_Info,
Custom_Info,
Text_Description,
Title,
Publisher,

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
@ -85,7 +86,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
WavStreamReader reader = new WavStreamReader(stream);
int rate = (69888 * 50) / reader.Header.sampleRate;
//int rate = (int)(((double)69888 * (double)50) / (double)reader.Header.sampleRate);
int rate = (int)((double)(3500000) / (double)reader.Header.sampleRate);
int smpCounter = 0;
int state = reader.ReadNext();
@ -94,6 +96,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
t.BlockDescription = BlockType.WAV_Recording;
t.BlockID = 0;
t.DataPeriods = new List<int>();
t.DataLevels = new List<bool>();
bool currLevel = false;
for (int i = 0; i < reader.Count; i++)
{
@ -101,16 +106,33 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
smpCounter++;
if ((state < 0 && sample < 0) || (state >= 0 && sample >= 0))
continue;
t.DataPeriods.Add(smpCounter * rate);
t.DataPeriods.Add((int)(((double)smpCounter * (double)rate) / (double)0.9838560885608856));
currLevel = !currLevel;
t.DataLevels.Add(currLevel);
smpCounter = 0;
state = sample;
}
// add closing period
t.DataPeriods.Add((69888 * 50) / 10);
currLevel = false;
t.DataLevels.Add(currLevel);
// add to datacorder
_datacorder.DataBlocks.Add(t);
StringBuilder export = new StringBuilder();
foreach (var b in _datacorder.DataBlocks)
{
for (int i = 0; i < b.DataPeriods.Count(); i++)
{
export.Append(b.DataPeriods[i].ToString());
export.Append("\t\t");
export.AppendLine(b.DataLevels[i].ToString());
}
}
string o = export.ToString();
}
}
}