Add jitter before track seam (under special conditions) (#1125, PR #1129)

Use FindTrackSeamWOZ() to find longest run of sync FF/10 nibbles.
Jitter added:
. only for tracks 33.0 and above
. only for tracks with long runs of sync FF/10 of more than 110

Changes:
. DumpTrackWOZ() outputs nibble then zeros (instead of zeros first)
. Debugger: 'disk info' outputs bitOffset (instead of byteOffset+mask)
This commit is contained in:
TomCh 2022-09-19 11:00:34 +01:00 committed by GitHub
parent f300edb5de
commit 4c83186545
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 216 additions and 47 deletions

View File

@ -3436,13 +3436,12 @@ Update_t CmdDisk ( int nArgs)
if (nArgs > 2)
return HelpLastCommand();
ConsoleBufferPushFormat("FW%2d: D%d at T$%s, phase $%s, offset $%X, mask $%02X, extraCycles %.2f, %s",
ConsoleBufferPushFormat("FW%2d: D%d at T$%s, phase $%s, bitOffset $%04X, extraCycles %.2f, %s",
diskCard.GetCurrentFirmware(),
diskCard.GetCurrentDrive() + 1,
diskCard.GetCurrentTrackString().c_str(),
diskCard.GetCurrentPhaseString().c_str(),
diskCard.GetCurrentOffset(),
diskCard.GetCurrentLSSBitMask(),
diskCard.GetCurrentBitOffset(),
diskCard.GetCurrentExtraCycles(),
diskCard.GetCurrentState()
);

View File

@ -104,8 +104,7 @@ void Disk2InterfaceCard::SetEnhanceDisk(bool bEnhanceDisk) { m_enhanceDisk = bEn
int Disk2InterfaceCard::GetCurrentDrive(void) { return m_currDrive; }
int Disk2InterfaceCard::GetCurrentTrack(void) { return ImagePhaseToTrack(m_floppyDrive[m_currDrive].m_disk.m_imagehandle, m_floppyDrive[m_currDrive].m_phasePrecise, false); }
float Disk2InterfaceCard::GetCurrentPhase(void) { return m_floppyDrive[m_currDrive].m_phasePrecise; }
int Disk2InterfaceCard::GetCurrentOffset(void) { return m_floppyDrive[m_currDrive].m_disk.m_byte; }
BYTE Disk2InterfaceCard::GetCurrentLSSBitMask(void) { return m_floppyDrive[m_currDrive].m_disk.m_bitMask; }
UINT Disk2InterfaceCard::GetCurrentBitOffset(void) { return m_floppyDrive[m_currDrive].m_disk.m_bitOffset; }
double Disk2InterfaceCard::GetCurrentExtraCycles(void) { return m_floppyDrive[m_currDrive].m_disk.m_extraCycles; }
int Disk2InterfaceCard::GetTrack(const int drive) { return ImagePhaseToTrack(m_floppyDrive[drive].m_disk.m_imagehandle, m_floppyDrive[drive].m_phasePrecise, false); }
@ -325,6 +324,12 @@ void Disk2InterfaceCard::ReadTrack(const int drive, ULONG uExecutedCycles)
}
else
{
// NB. This function is only called for a new track when there's a latch read, ie. only for *even* DEVICE SELECT I/O accesses.
// . So when seeking across tracks (ie. sequencing through the magnet phases), then not all (quarter) tracks will need reading.
// . eg. for 'Balance of Power'(GH#1022), for seek T00->T35: this only reads: 00.00, 00.25, 00.75, 01.25, 01.75, ... 34.25, 34.75, 35.00 (skipping the NN.00, NN.50 tracks).
// . And so the bitOffset "round-up" below isn't called for every track.
// TODO: consider forcing this function be be called for every track (and appropriately adjust the "round-up" amount - ie. halve it)
_ASSERT(pFloppy->m_nibbles && pFloppy->m_bitCount);
if (pFloppy->m_nibbles == 0 || pFloppy->m_bitCount == 0)
{
@ -337,15 +342,23 @@ void Disk2InterfaceCard::ReadTrack(const int drive, ULONG uExecutedCycles)
if (pFloppy->m_bitOffset >= pFloppy->m_bitCount)
pFloppy->m_bitOffset = 0;
#if LOG_DISK_WOZ_READTRACK
LOG_DISK("T%05.2f: %04X->%04X, Len=%04X\n", pDrive->m_phasePrecise / 2, currentBitPosition, pFloppy->m_bitOffset, pFloppy->m_bitCount);
#endif
pFloppy->m_byte = pFloppy->m_bitOffset / 8;
pFloppy->m_bitMask = 1 << (7 - (pFloppy->m_bitOffset % 8));
pFloppy->m_extraCycles = 0.0;
pDrive->m_headWindow = 0;
FindTrackSeamWOZ(*pFloppy, pDrive->m_phasePrecise/2);
}
pFloppy->m_trackimagedata = (pFloppy->m_nibbles != 0);
pFloppy->m_initialBitOffset = pFloppy->m_bitOffset;
pFloppy->m_revs = 0;
}
}
@ -1196,6 +1209,9 @@ __forceinline void Disk2InterfaceCard::IncBitStream(FloppyDisk& floppy)
floppy.m_bitOffset = 0;
floppy.m_byte = 0;
}
if (floppy.m_bitOffset == floppy.m_initialBitOffset)
floppy.m_revs++;
}
void Disk2InterfaceCard::PreJitterCheck(int phase, BYTE latch)
@ -1240,6 +1256,29 @@ void Disk2InterfaceCard::AddJitter(int phase, FloppyDisk& floppy)
m_foundT00S00Pattern = false;
}
// GH#1125: For T$21 (track 33.0) or above (and sufficiently long sync FF/10 run-length), then randomly skip 1 bit-cell at the start of the FF/2 track seam.
// Example of high sync FF/10 run-lengths for tracks 33.0+:
// . Accolade Comics:114, Silent Service:117, Wings of Fury:140, Wizardry I:127, Wizardry III:283
// NB. Restrict to higher FF/10 run-lengths to limit the titles affected by this jitter.
void Disk2InterfaceCard::AddTrackSeamJitter(float phasePrecise, FloppyDisk& floppy)
{
if (phasePrecise >= (33.0 * 2) && floppy.m_longestSyncFFRunLength > 110)
{
if (floppy.m_bitOffset == floppy.m_longestSyncFFBitOffsetStart)
{
if (rand() < RAND_THRESHOLD(5, 10))
{
LogOutput("Disk: T%05.2f jitter - slip 1 bitcell (revs=%d) (PC=%04X)\n", phasePrecise / 2, floppy.m_revs, regs.pc);
IncBitStream(floppy);
}
else
{
LogOutput("Disk: T%05.2f jitter - *** SKIP *** (revs=%d) (PC=%04X)\n", phasePrecise / 2, floppy.m_revs, regs.pc);
}
}
}
}
void __stdcall Disk2InterfaceCard::DataLatchReadWriteWOZ(WORD pc, WORD addr, BYTE bWrite, ULONG uExecutedCycles)
{
_ASSERT(m_seqFunc.function != dataShiftWrite);
@ -1346,6 +1385,8 @@ void Disk2InterfaceCard::DataLatchReadWOZ(WORD pc, WORD addr, UINT bitCellRemain
IncBitStream(floppy);
AddTrackSeamJitter(drive.m_phasePrecise, floppy);
m_shiftReg <<= 1;
m_shiftReg |= outputBit;
@ -1432,6 +1473,8 @@ void Disk2InterfaceCard::DataLoadWriteWOZ(WORD pc, WORD addr, UINT bitCellRemain
LOG_DISK("load shiftReg with %02X (was: %02X)\n", m_floppyLatch, m_shiftReg);
#endif
m_shiftReg = m_floppyLatch;
floppy.m_longestSyncFFBitOffsetStart = -1; // invalidate the track seam location after a write
}
void Disk2InterfaceCard::DataShiftWriteWOZ(WORD pc, WORD addr, ULONG uExecutedCycles)
@ -1478,6 +1521,107 @@ void Disk2InterfaceCard::DataShiftWriteWOZ(WORD pc, WORD addr, ULONG uExecutedCy
//===========================================================================
// For now all that's needed is this basic case:
// . find [start,end] of longest run of FF/10 sync nibbles
void Disk2InterfaceCard::FindTrackSeamWOZ(FloppyDisk& floppy, float track)
{
const UINT oldBitOffset = floppy.m_bitOffset; // Save current state
BYTE shiftReg = 0;
UINT zeroCount = 0;
int startBitOffset = -1; // NB. change this to start of first FF/10
floppy.m_bitOffset = 0;
UpdateBitStreamOffsets(floppy);
int nibbleStartBitOffset = -1;
int syncFFStartBitOffset = -1;
int syncFFRunLength = 0;
int longestSyncFFStartBitOffset = -1;
int longestSyncFFRunLength = 0;
floppy.m_longestSyncFFBitOffsetStart = -1;
while (1)
{
BYTE n = floppy.m_trackimage[floppy.m_byte];
BYTE outputBit = (n & floppy.m_bitMask) ? 1 : 0;
IncBitStream(floppy);
if ((startBitOffset < 0 && floppy.m_bitOffset == 0) || (startBitOffset == floppy.m_bitOffset)) // done complete track?
break;
if (shiftReg & 0x80)
{
if (outputBit == 0) // zero, so LSS holds nibble in latch
{
zeroCount++;
continue;
}
// else: start of next nibble
if (shiftReg == 0xff && zeroCount == 2)
{
if (syncFFStartBitOffset < 0)
syncFFStartBitOffset = nibbleStartBitOffset;
syncFFRunLength++;
}
if ((shiftReg != 0xff || zeroCount != 2) && syncFFStartBitOffset >= 0)
{
// Longest FF/2 run could straddle end/start of track's bit buffer
if (startBitOffset < 0)
startBitOffset = nibbleStartBitOffset;
if (longestSyncFFRunLength < syncFFRunLength)
{
longestSyncFFStartBitOffset = syncFFStartBitOffset;
longestSyncFFRunLength = syncFFRunLength;
}
syncFFStartBitOffset = -1;
syncFFRunLength = 0;
}
shiftReg = 0;
zeroCount = 0;
}
shiftReg <<= 1;
shiftReg |= outputBit;
if (shiftReg == 0x01)
{
nibbleStartBitOffset = floppy.m_bitOffset - 1;
if (nibbleStartBitOffset < 0) nibbleStartBitOffset += floppy.m_bitCount;
}
}
if (longestSyncFFRunLength)
{
const int longestSyncFFBitOffsetEnd = (longestSyncFFStartBitOffset + longestSyncFFRunLength * 10 - 1) % floppy.m_bitCount;
#if LOG_DISK_WOZ_TRACK_SEAM
LOG_DISK("Track seam: T%05.2f: FF/10 (run=%d), start=%04X, end=%04X\n", track, longestSyncFFRunLength, longestSyncFFStartBitOffset, longestSyncFFBitOffsetEnd);
#endif
floppy.m_longestSyncFFBitOffsetStart = longestSyncFFStartBitOffset;
floppy.m_longestSyncFFRunLength = longestSyncFFRunLength;
}
else
{
#if LOG_DISK_WOZ_TRACK_SEAM
LOG_DISK("Track seam: T%05.2f: FF/10 (none)\n", track);
#endif
}
// Restore state
floppy.m_bitOffset = oldBitOffset;
UpdateBitStreamOffsets(floppy);
}
//===========================================================================
#ifdef _DEBUG
// Dump nibbles from current position bitstream wraps to same position
// NB. Need to define LOG_DISK_NIBBLES_READ so that GetReadD5AAxxDetectedString() works.
@ -1489,21 +1633,22 @@ void Disk2InterfaceCard::DumpTrackWOZ(FloppyDisk floppy) // pass a copy of m_flo
UINT zeroCount = 0;
UINT nibbleCount = 0;
const UINT startBitOffset = 0; // NB. may need to tweak this offset, since the bistream is a circular buffer
const UINT startBitOffset = 0; // NB. may need to tweak this offset, since the bitstream is a circular buffer
floppy.m_bitOffset = startBitOffset;
UpdateBitStreamOffsets(floppy);
floppy.m_byte = floppy.m_bitOffset / 8;
const UINT remainder = 7 - (floppy.m_bitOffset & 7);
floppy.m_bitMask = 1 << remainder;
int nibbleStartBitOffset = -1;
bool newLine = true;
bool doneLastBit = false;
while (1)
{
if (newLine)
if (newLine && nibbleStartBitOffset >= 0)
{
newLine = false;
LogOutput("%04X:", floppy.m_bitOffset & 0xffff);
LogOutput("%04X:", nibbleStartBitOffset);
nibbleStartBitOffset = -1;
}
BYTE n = floppy.m_trackimage[floppy.m_byte];
@ -1512,53 +1657,67 @@ void Disk2InterfaceCard::DumpTrackWOZ(FloppyDisk floppy) // pass a copy of m_flo
IncBitStream(floppy);
if (startBitOffset == floppy.m_bitOffset) // done complete track?
doneLastBit = true;
else if (doneLastBit)
break;
if (shiftReg == 0 && outputBit == 0)
if (shiftReg & 0x80)
{
zeroCount++;
continue;
if (outputBit == 0) // zero, so LSS holds nibble in latch
{
zeroCount++;
continue;
}
// else: start of next nibble
nibbleCount++;
char syncBits = zeroCount <= 9 ? '0' + zeroCount : '+';
if (zeroCount == 0) LogOutput("%02X ", shiftReg);
else LogOutput("%02X(%c)", shiftReg, syncBits);
formatTrack.DecodeLatchNibbleRead(shiftReg);
if ((nibbleCount % 32) == 0)
{
std::string strReadDetected = formatTrack.GetReadD5AAxxDetectedString();
if (!strReadDetected.empty())
{
OutputDebugString("\t; ");
OutputDebugString(strReadDetected.c_str());
}
OutputDebugString("\n");
newLine = true;
}
shiftReg = 0;
zeroCount = 0;
}
shiftReg <<= 1;
shiftReg |= outputBit;
if ((shiftReg & 0x80) == 0)
continue;
nibbleCount++;
char syncBits = zeroCount <= 9 ? '0'+zeroCount : '+';
if (zeroCount == 0) LogOutput(" %02X", shiftReg);
else LogOutput("(%c)%02X", syncBits, shiftReg);
formatTrack.DecodeLatchNibbleRead(shiftReg);
if ((nibbleCount % 32) == 0)
if (shiftReg == 0x01)
{
std::string strReadDetected = formatTrack.GetReadD5AAxxDetectedString();
if (!strReadDetected.empty())
{
OutputDebugString("\t; ");
OutputDebugString(strReadDetected.c_str());
}
OutputDebugString("\n");
newLine = true;
nibbleStartBitOffset = floppy.m_bitOffset - 1;
if (nibbleStartBitOffset < 0) nibbleStartBitOffset += floppy.m_bitCount;
}
shiftReg = 0;
zeroCount = 0;
}
// Output any remaining zeroCount
if (zeroCount)
{
char syncBits = zeroCount <= 9 ? '0'+zeroCount : '+';
LogOutput("(%c)", syncBits);
}
// Output any partial nibble
if (shiftReg)
if (shiftReg & 0x80)
{
LogOutput("%02X", shiftReg);
// Output any remaining zeroCount
if (zeroCount)
{
char syncBits = zeroCount <= 9 ? '0' + zeroCount : '+';
LogOutput("(%c)", syncBits);
}
}
else if (shiftReg)
{
LogOutput("%02X/Partial Nibble", shiftReg);
}

View File

@ -68,6 +68,10 @@ public:
m_trackimage = NULL;
m_trackimagedata = false;
m_trackimagedirty = false;
m_longestSyncFFRunLength = 0;
m_longestSyncFFBitOffsetStart = -1;
m_initialBitOffset = 0;
m_revs = 0;
}
public:
@ -85,6 +89,10 @@ public:
LPBYTE m_trackimage;
bool m_trackimagedata;
bool m_trackimagedirty;
UINT m_longestSyncFFRunLength;
int m_longestSyncFFBitOffsetStart;
UINT m_initialBitOffset; // debug
UINT m_revs; // debug
};
class FloppyDrive
@ -155,8 +163,7 @@ public:
int GetCurrentDrive(void);
int GetCurrentTrack(void);
float GetCurrentPhase(void);
int GetCurrentOffset(void);
BYTE GetCurrentLSSBitMask(void);
UINT GetCurrentBitOffset(void);
double GetCurrentExtraCycles(void);
int GetTrack(const int drive);
static std::string FormatPhaseString(float phase);
@ -203,6 +210,7 @@ private:
void DataLoadWriteWOZ(WORD pc, WORD addr, UINT bitCellRemainder);
void DataShiftWriteWOZ(WORD pc, WORD addr, ULONG uExecutedCycles);
void SetSequencerFunction(WORD addr, ULONG executedCycles);
void FindTrackSeamWOZ(FloppyDisk& floppy, float track);
void DumpTrackWOZ(FloppyDisk floppy);
bool GetFirmware(WORD lpNameId, BYTE* pDst);
void InitFirmware(LPBYTE pCxRomPeripheral);
@ -214,6 +222,7 @@ private:
void PreJitterCheck(int phase, BYTE latch);
void AddJitter(int phase, FloppyDisk& floppy);
void AddTrackSeamJitter(float phasePrecise, FloppyDisk& floppy);
void SaveSnapshotFloppy(YamlSaveHelper& yamlSaveHelper, UINT unit);
void SaveSnapshotDriveUnit(YamlSaveHelper& yamlSaveHelper, UINT unit);

View File

@ -14,6 +14,8 @@
#define LOG_DISK_NIBBLES_USE_RUNTIME_VAR 1
#define LOG_DISK_WOZ_LOADWRITE 1
#define LOG_DISK_WOZ_SHIFTWRITE 1
#define LOG_DISK_WOZ_READTRACK 1
#define LOG_DISK_WOZ_TRACK_SEAM 1
// __VA_ARGS__ not supported on MSVC++ .NET 7.x
#if (LOG_DISK_ENABLED)