diff --git a/source/Debugger/Debug.cpp b/source/Debugger/Debug.cpp index a8a63e38..020e2e6b 100644 --- a/source/Debugger/Debug.cpp +++ b/source/Debugger/Debug.cpp @@ -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() ); diff --git a/source/Disk.cpp b/source/Disk.cpp index ae88846f..d8260065 100644 --- a/source/Disk.cpp +++ b/source/Disk.cpp @@ -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); } diff --git a/source/Disk.h b/source/Disk.h index c7b079bb..3c4a3839 100644 --- a/source/Disk.h +++ b/source/Disk.h @@ -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); diff --git a/source/DiskLog.h b/source/DiskLog.h index c098226a..d72fa8dd 100644 --- a/source/DiskLog.h +++ b/source/DiskLog.h @@ -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)