enhancement: updated FloppyBridge to v1.6.4

This commit is contained in:
Dimitris Panokostas 2024-08-24 09:06:53 +02:00
parent 2401021c5b
commit 5d3915ea9b
No known key found for this signature in database
GPG Key ID: 330156A68E9E0929
20 changed files with 725 additions and 90 deletions

View File

@ -270,8 +270,8 @@ bool ArduinoFloppyDiskBridge::setCurrentCylinder(const unsigned int cylinder) {
if (!m_io.getFirwareVersion().fullControlMod) ignoreDiskCheck |= !isReadyForManualDiskCheck();
// Go! - and don't ask
ArduinoFloppyReader::DiagnosticResponse dr = m_io.selectTrack(cylinder, ArduinoFloppyReader::TrackSearchSpeed::tssFast, ignoreDiskCheck);
if (dr != ArduinoFloppyReader::DiagnosticResponse::drOK) dr = m_io.selectTrack(cylinder, ArduinoFloppyReader::TrackSearchSpeed::tssNormal, ignoreDiskCheck);
ArduinoFloppyReader::DiagnosticResponse dr = m_io.selectTrack(cylinder, ArduinoFloppyReader::TrackSearchSpeed::tssVeryFast, ignoreDiskCheck);
if (dr != ArduinoFloppyReader::DiagnosticResponse::drOK) dr = m_io.selectTrack(cylinder, ArduinoFloppyReader::TrackSearchSpeed::tssFast, ignoreDiskCheck);
if (dr != ArduinoFloppyReader::DiagnosticResponse::drOK) dr = m_io.selectTrack(cylinder, ArduinoFloppyReader::TrackSearchSpeed::tssNormal, ignoreDiskCheck);
if (dr != ArduinoFloppyReader::DiagnosticResponse::drOK) dr = m_io.selectTrack(cylinder, ArduinoFloppyReader::TrackSearchSpeed::tssNormal, ignoreDiskCheck);
if (dr == ArduinoFloppyReader::DiagnosticResponse::drOK) {
@ -306,6 +306,19 @@ CommonBridgeTemplate::ReadResponse ArduinoFloppyDiskBridge::readData(PLL::Bridge
}
}
// Called for a direct read. This does not match up a rotation and should be used with the pll initialized with the LinearExtractor
// pll: required
// Returns: ReadResponse, explains its self
CommonBridgeTemplate::ReadResponse ArduinoFloppyDiskBridge::readLinearData(PLL::BridgePLL& pll) {
ArduinoFloppyReader::DiagnosticResponse result = m_io.readData(pll);
switch (result) {
case ArduinoFloppyReader::DiagnosticResponse::drOK: return ReadResponse::rrOK;
case ArduinoFloppyReader::DiagnosticResponse::drNoDiskInDrive: return ReadResponse::rrNoDiskInDrive;
default: return ReadResponse::rrError;
}
}
// Called when a cylinder revolution should be written to the disk.
// Parameters are: rawMFMData The raw data to be written. This is an actual MFM stream, going from MSB to LSB for each byte
// numBytes Number of bits in the buffer to write

View File

@ -111,6 +111,11 @@ protected:
virtual ReadResponse readData(PLL::BridgePLL& pll, const unsigned int maxBufferSize, RotationExtractor::MFMSample* buffer, RotationExtractor::IndexSequenceMarker& indexMarker,
std::function<bool(RotationExtractor::MFMSample* mfmData, const unsigned int dataLengthInBits)> onRotation) override;
// Called for a direct read. This does not match up a rotation and should be used with the pll initialized with the LinearExtractor
// pll: required
// Returns: ReadResponse, explains its self
virtual ReadResponse readLinearData(PLL::BridgePLL& pll) override;
// Called when a cylinder revolution should be written to the disk.
// Parameters are: rawMFMData The raw data to be written. This is an actual MFM stream, going from MSB to LSB for each byte
// numBytes Number of bits in the buffer to write
@ -137,4 +142,4 @@ public:
#endif
#endif
#endif

View File

@ -186,6 +186,7 @@ ArduinoInterface::ArduinoInterface() {
// Free me
ArduinoInterface::~ArduinoInterface() {
if (m_tempBuffer) free(m_tempBuffer);
abortReadStreaming();
closePort();
}
@ -991,6 +992,202 @@ DiagnosticResponse ArduinoInterface::readCurrentTrack(RawTrackDataDD& trackData,
return readCurrentTrack(&trackData, RAW_TRACKDATA_LENGTH_DD, readFromIndexPulse);
}
// Reads just enough data to fulfill most extractions needed, but doesnt care about rotation position or index - pll should have the LinearExtractor configured
DiagnosticResponse ArduinoInterface::readData(PLL::BridgePLL& pll) {
pll.rotationExtractor()->reset(m_isHDMode);
LinearExtractor* linearExtractor = dynamic_cast<LinearExtractor*>(pll.rotationExtractor());
if (!linearExtractor) return DiagnosticResponse::drError;
if (!m_tempBuffer) {
m_tempBuffer = malloc(RAW_TRACKDATA_LENGTH_HD);
if (!m_tempBuffer) return DiagnosticResponse::drError;
}
const uint32_t sizeRequired = m_isHDMode ? RAW_TRACKDATA_LENGTH_HD : RAW_TRACKDATA_LENGTH_DD;
DiagnosticResponse response = readCurrentTrack(m_tempBuffer, sizeRequired, false);
if (response != DiagnosticResponse::drOK) return response;
linearExtractor->copyToBuffer(m_tempBuffer, sizeRequired);
return response;
m_lastCommand = LastCommand::lcReadTrackStream;
if (m_version.major == 1 && m_version.minor < 8) {
m_lastError = DiagnosticResponse::drOldFirmware;
return m_lastError;
}
const bool highPrecisionMode = !m_isHDMode && m_version.deviceFlags1 & FLAGS_HIGH_PRECISION_SUPPORT;
char mode = highPrecisionMode ? COMMAND_READTRACKSTREAM_HIGHPRECISION : COMMAND_READTRACKSTREAM;
if (mode == COMMAND_READTRACKSTREAM_HIGHPRECISION && m_version.deviceFlags1 & FLAGS_FLUX_READ) mode = COMMAND_READTRACKSTREAM_HALFPLL;
m_lastError = runCommand(mode);
if (m_lastError != DiagnosticResponse::drOK) return m_lastError;
m_isStreaming = true;
applyCommTimeouts(true);
unsigned char tempReadBuffer[4096] = { 0 };
// Let the class know we're doing some streaming stuff
m_abortStreaming = false;
m_abortSignalled = false;
// Number of times we failed to read anything
int32_t readFail = 0;
// Sliding window for abort
char slidingWindow[5] = { 0,0,0,0,0 };
bool timeout = false;
bool dataState = false;
bool isFirstByte = true;
unsigned char mfmSequences = 0;
MFMExtractionTarget* extractor = pll.rotationExtractor();
for (;;) {
// More efficient to read several bytes in one go
unsigned int bytesAvailable = m_comPort.getBytesWaiting();
if (bytesAvailable < 1) bytesAvailable = 1;
if (bytesAvailable > sizeof tempReadBuffer) bytesAvailable = sizeof tempReadBuffer;
unsigned int bytesRead = m_comPort.read(tempReadBuffer, bytesAvailable);
for (size_t a = 0; a < bytesRead; a++) {
const unsigned char byteRead = tempReadBuffer[a];
if (m_abortSignalled) {
// Make space
for (int s = 0; s < 4; s++) slidingWindow[s] = slidingWindow[s + 1];
// Append the new byte
slidingWindow[4] = byteRead;
// Watch the sliding window for the pattern we need
if (slidingWindow[0] == 'X' && slidingWindow[1] == 'Y' && slidingWindow[2] == 'Z' && slidingWindow[3] == SPECIAL_ABORT_CHAR && slidingWindow[4] == '1') {
m_isStreaming = false;
m_comPort.purgeBuffers();
m_lastError = timeout ? DiagnosticResponse::drError : DiagnosticResponse::drOK;
applyCommTimeouts(false);
return m_lastError;
}
}
else {
unsigned char tmp;
RotationExtractor::MFMSequenceInfo sequence{};
if (m_isHDMode) {
for (int i = 6; i >= 0; i -= 2) {
tmp = byteRead >> i & 0x03;
sequence.mfm = (RotationExtractor::MFMSequence)((tmp == 0x03 ? 0 : tmp) + 1);
sequence.timeNS = 2000 + (unsigned int)sequence.mfm * 2000;
extractor->submitSequence(sequence, tmp == 0x03);
}
}
else {
if (highPrecisionMode) {
if (isFirstByte) {
// Throw away the first byte
isFirstByte = false;
if (byteRead != 0xC3) {
// This should never happen.
abortReadStreaming();
}
}
else {
if (dataState) {
if (mode == COMMAND_READTRACKSTREAM_HALFPLL) {
const int32_t sequences[4] = { (mfmSequences >> 6) & 0x03, (mfmSequences >> 4) & 0x03, (mfmSequences >> 2) & 0x03, mfmSequences & 0x03 };
// First remove any flux time that us 000 as thats always fixed
int32_t readSpeed = (unsigned int)(byteRead & 0x7F) * 2000 / 128;
// Output the fluxes
for (int a = 0; a < 4; a++) {
if (sequences[a] == 0) {
sequence.mfm = RotationExtractor::MFMSequence::mfm000;
sequence.timeNS = 5000 + readSpeed;
}
else {
sequence.mfm = (RotationExtractor::MFMSequence)sequences[a];
sequence.timeNS = 1000 + readSpeed + (((int)sequence.mfm) * 2000);
}
sequence.pllTimeNS = sequence.timeNS;
extractor->submitSequence(sequence, ((byteRead & 0x80) != 0) && (a == 0));
}
}
else {
int32_t readSpeed = (unsigned int)(byteRead & 0x7F) * 2000 / 128;
tmp = mfmSequences >> 6 & 0x03;
sequence.mfm = tmp == 0 ? RotationExtractor::MFMSequence::mfm000 : (RotationExtractor::MFMSequence)(tmp);
sequence.timeNS = 1000 + (unsigned int)sequence.mfm * 2000 + (sequence.mfm == RotationExtractor::MFMSequence::mfm000 ? -2000 : readSpeed);
extractor->submitSequence(sequence, (byteRead & 0x80) != 0);
tmp = mfmSequences >> 4 & 0x03;
sequence.mfm = tmp == 0 ? RotationExtractor::MFMSequence::mfm000 : (RotationExtractor::MFMSequence)(tmp);
sequence.timeNS = 1000 + (unsigned int)sequence.mfm * 2000 + (sequence.mfm == RotationExtractor::MFMSequence::mfm000 ? -2000 : readSpeed);
extractor->submitSequence(sequence, false);
tmp = mfmSequences >> 2 & 0x03;
sequence.mfm = tmp == 0 ? RotationExtractor::MFMSequence::mfm000 : (RotationExtractor::MFMSequence)(tmp);
sequence.timeNS = 1000 + (unsigned int)sequence.mfm * 2000 + (sequence.mfm == RotationExtractor::MFMSequence::mfm000 ? -2000 : readSpeed);
extractor->submitSequence(sequence, false);
tmp = mfmSequences & 0x03;
sequence.mfm = tmp == 0 ? RotationExtractor::MFMSequence::mfm000 : (RotationExtractor::MFMSequence)(tmp);
sequence.timeNS = 1000 + (unsigned int)sequence.mfm * 2000 + (sequence.mfm == RotationExtractor::MFMSequence::mfm000 ? -2000 : readSpeed);
extractor->submitSequence(sequence, false);
}
dataState = false;
}
else {
// in 'false' mode we get the flux data
dataState = true;
mfmSequences = byteRead;
}
}
}
else {
unsigned short readSpeed = (unsigned long long)((unsigned int)((byteRead & 0x07) * 16) * 2000) / 128;
// Now packet up the data in the format the rotation extractor expects it to be in
tmp = byteRead >> 5 & 0x03;
sequence.mfm = tmp == 0 ? RotationExtractor::MFMSequence::mfm000 : (RotationExtractor::MFMSequence)(tmp);
sequence.timeNS = 1000 + (unsigned int)sequence.mfm * 2000 + readSpeed;
extractor->submitSequence(sequence, (byteRead & 0x80) != 0);
tmp = byteRead >> 3 & 0x03;
sequence.mfm = tmp == 0 ? RotationExtractor::MFMSequence::mfm000 : (RotationExtractor::MFMSequence)(tmp);
sequence.timeNS = 1000 + (unsigned int)sequence.mfm * 2000 + readSpeed;
extractor->submitSequence(sequence, false);
}
}
// Is it ready to extract? - takes 15-16ms to abort so take that into account
if ((extractor->canExtract() || extractor->totalTimeReceived()>(220000000 - 14000000))) {
if (!m_abortSignalled) abortReadStreaming();
}
}
}
if (bytesRead < 1) {
readFail++;
if (readFail > 30) {
m_abortStreaming = false; // force the 'abort' command to be sent
abortReadStreaming();
m_lastError = DiagnosticResponse::drReadResponseFailed;
m_isStreaming = false;
applyCommTimeouts(false);
return m_lastError;
}
else {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
else {
readFail = 0;
}
}
}
// Read RAW data from the current track and surface HD mode
DiagnosticResponse ArduinoInterface::readCurrentTrack(void* trackData, const int dataLength, const bool readFromIndexPulse) {
m_lastCommand = LastCommand::lcReadTrack;
@ -1161,7 +1358,7 @@ DiagnosticResponse ArduinoInterface::readCurrentTrack(void* trackData, const int
// Reads a complete rotation of the disk, and returns it using the callback function which can return FALSE to stop
// An instance of RotationExtractor is required. This is purely to save on re-allocations. It is internally reset each time
DiagnosticResponse ArduinoInterface::readRotation(RotationExtractor& extractor, const unsigned int maxOutputSize, RotationExtractor::MFMSample* firstOutputBuffer, RotationExtractor::IndexSequenceMarker& startBitPatterns, std::function<bool(RotationExtractor::MFMSample** mfmData, const unsigned int dataLengthInBits)> onRotation, bool useHalfPLL) {
DiagnosticResponse ArduinoInterface::readRotation(MFMExtractionTarget& extractor, const unsigned int maxOutputSize, RotationExtractor::MFMSample* firstOutputBuffer, RotationExtractor::IndexSequenceMarker& startBitPatterns, std::function<bool(RotationExtractor::MFMSample** mfmData, const unsigned int dataLengthInBits)> onRotation, bool useHalfPLL) {
m_lastCommand = LastCommand::lcReadTrackStream;
if (m_version.major == 1 && m_version.minor < 8) {

View File

@ -171,6 +171,7 @@ namespace ArduinoFloppyReader {
bool m_isStreaming;
bool m_isHDMode;
std::mutex m_protectAbort;
void* m_tempBuffer = nullptr;
// Read a desired number of bytes into the target pointer
bool deviceRead(void* target, const unsigned int numBytes, const bool failIfNotAllRead = false);
@ -248,7 +249,7 @@ namespace ArduinoFloppyReader {
// Reads a complete rotation of the disk, and returns it using the callback function which can return FALSE to stop
// An instance of BridgePLL is required.
DiagnosticResponse readRotation(RotationExtractor& extractor, const unsigned int maxOutputSize, RotationExtractor::MFMSample* firstOutputBuffer, RotationExtractor::IndexSequenceMarker& startBitPatterns, std::function<bool(RotationExtractor::MFMSample** mfmData, const unsigned int dataLengthInBits)> onRotation, bool useHalfPLL);
DiagnosticResponse readRotation(MFMExtractionTarget& extractor, const unsigned int maxOutputSize, RotationExtractor::MFMSample* firstOutputBuffer, RotationExtractor::IndexSequenceMarker& startBitPatterns, std::function<bool(RotationExtractor::MFMSample** mfmData, const unsigned int dataLengthInBits)> onRotation, bool useHalfPLL);
// Same as the above, but this uses the newer much more accurate flux read
DiagnosticResponse readFlux(PLL::BridgePLL& pll, const unsigned int maxOutputSize, RotationExtractor::MFMSample* firstOutputBuffer, RotationExtractor::IndexSequenceMarker& startBitPatterns, std::function<bool(RotationExtractor::MFMSample** mfmData, const unsigned int dataLengthInBits)> onRotation);
@ -289,6 +290,9 @@ namespace ArduinoFloppyReader {
// Choose which surface of the disk to read from
DiagnosticResponse selectSurface(const DiskSurface side);
// Reads just enough data to fulfill most extractions needed, but doesnt care about rotation position or index - pll should have the LinearExtractor configured
DiagnosticResponse readData(PLL::BridgePLL& pll);
// Read RAW data from the current track and surface selected - this works properly with the HD and DD options
// dataLength must be either RAW_TRACKDATA_LENGTH or RAW_TRACKDATA_LENGTH_HD. If you mismatch the type then the function will return an error
DiagnosticResponse readCurrentTrack(void* trackData, const int dataLength, const bool readFromIndexPulse);

View File

@ -807,7 +807,8 @@ bool CommonBridgeTemplate::isMFMDataAvailable() {
}
// Requests an entire track of data. Returns 0 if the track is not available
// The return value is the wrap point in bits (last byte is shifted to MSB)
// The return value is the wrap point in bits (last byte is shifted to MSB) or in Direct mode, just the number of bits received
// resyncRotation is ignored in direct mode
int CommonBridgeTemplate::getMFMTrack(bool side, unsigned int track, bool resyncRotation, const int bufferSizeInBytes, void* output) {
if (m_directMode) {
threadLockControl(true);
@ -831,37 +832,22 @@ int CommonBridgeTemplate::getMFMTrack(bool side, unsigned int track, bool resync
setActiveSurface(_side);
}
MFMCache& trackData = m_mfmRead[m_actualCurrentCylinder][(int)m_actualFloppySide].next;
trackData.amountReadInBits = 0;
trackData.ready = false;
m_linearExtractor.setOutputBuffer(output, bufferSizeInBytes);
// Switch to linear extractor
m_pll.setRotationExtractor(&m_linearExtractor);
m_extractor.setAlwaysUseIndex(resyncRotation);
ReadResponse r = readLinearData(m_pll);
// put it back!
m_pll.setRotationExtractor(&m_extractor);
// Grab full revolutions if possible.
ReadResponse r = readData(m_pll, bufferSizeInBytes, trackData.mfmBuffer,
m_mfmRead[m_actualCurrentCylinder][(int)m_actualFloppySide].startBitPatterns,
[this, &trackData](RotationExtractor::MFMSample* mfmData, const unsigned int dataLengthInBits) -> bool {
trackData.amountReadInBits = dataLengthInBits;
return false; // stop reading
});
// Prevent disk check while we're doing this
m_lastDiskCheckTime = std::chrono::steady_clock::now();
// Release thread
threadLockControl(false);
RotationExtractor::MFMSample* sample = trackData.mfmBuffer;
const int bitsRemaining = trackData.amountReadInBits;
const int bytesToCopy = std::min((bitsRemaining + 7) / 8, bufferSizeInBytes);
unsigned char* outByte = (unsigned char*)output;
for (int byte = 0; byte < bytesToCopy; byte++) {
*outByte++ = sample->mfmData;
sample++;
}
// Clear the data read
trackData.amountReadInBits = 0;
return bitsRemaining;
// Finalise the output and get the mumber of bits
return m_linearExtractor.finaliseAndGetNumBits();
}
// Be in the right place
@ -1388,6 +1374,7 @@ bool CommonBridgeTemplate::initialise() {
internalSetMotorStatus(false);
m_autoCacheMotorStatus = false;
setActiveSurface(m_actualFloppySide);
setCurrentCylinder(0);
// Get some real disk status values
if (supportsDiskChange()) {
@ -1595,6 +1582,9 @@ bool CommonBridgeTemplate::writeMFMTrackToBuffer(bool side, unsigned int track,
m_writePending = false;
m_writeComplete = true;
// Prevent disk check while we're doing this
m_lastDiskCheckTime = std::chrono::steady_clock::now();
// Release thread
threadLockControl(false);

View File

@ -126,6 +126,9 @@ private:
// For extracting rotations from disks
RotationExtractor m_extractor;
// For doing the same as above, but no searching for rotations, just get the data
LinearExtractor m_linearExtractor;
// In direct mode FloppyBridge doesnt do some functions in the background
// Its useful if you're using FloppyBridge just for direct access to the interfaces
bool m_directMode;
@ -409,6 +412,11 @@ protected:
virtual ReadResponse readData(PLL::BridgePLL& pll, const unsigned int maxBufferSize, RotationExtractor::MFMSample* buffer, RotationExtractor::IndexSequenceMarker& indexMarker,
std::function<bool(RotationExtractor::MFMSample* mfmData, const unsigned int dataLengthInBits)> onRotation) = 0;
// Called for a direct read. This does not match up a rotation and should be used with the pll initialized with the LinearExtractor
// pll: required
// Returns: ReadResponse, explains its self
virtual ReadResponse readLinearData(PLL::BridgePLL& pll) = 0;
// Called when a cylinder revolution should be written to the disk.
// Parameters are: rawMFMData The raw data to be written. This is an actual MFM stream, going from MSB to LSB for each byte
// numBytes Number of bits in the buffer to write

View File

@ -41,7 +41,8 @@
#include <WinSock2.h>
#include "bridgeProfileListEditor.h"
#include "bridgeProfileEditor.h"
#include <WinDNS.h>
#pragma comment(lib, "Dnsapi.lib")
HINSTANCE hInstance;
#else
@ -154,31 +155,56 @@ void handleAbout(bool checkForUpdates, FloppyBridge::BridgeAbout** output) {
// Start winsock
WSADATA data;
WSAStartup(MAKEWORD(2, 0), &data);
#endif
// Fetch version from 'A' record in the DNS record
DNS_RECORD* dnsRecord;
std::wstring versionString;
// Look up TXT record
DNS_STATUS error = DnsQuery(L"floppybridge-amiga.robsmithdev.co.uk", DNS_TYPE_TEXT, DNS_QUERY_BYPASS_CACHE, NULL, &dnsRecord, NULL);
if (!error) {
const DNS_RECORD* record = dnsRecord;
while (record) {
if (record->wType == DNS_TYPE_TEXT) {
const DNS_TXT_DATAW* pData = &record->Data.TXT;
for (DWORD string = 0; string < pData->dwStringCount; string++) {
const std::wstring text = pData->pStringArray[string];
if ((text.length() >= 9) && (text.substr(0, 2) == L"v=")) versionString = text.substr(2);
}
}
record = record->pNext;
}
DnsRecordListFree(dnsRecord, DnsFreeRecordList); //DnsFreeRecordListDeep
}
if (!versionString.empty()) {
// A little hacky to convert a.b.c.d into an array
sockaddr_in tmp;
INT len = sizeof(tmp);
if (WSAStringToAddress((wchar_t*)versionString.c_str(), AF_INET, NULL, (sockaddr*)&tmp, &len) == 0) {
const in_addr add = tmp.sin_addr;
BridgeInformationUpdate.updateMajorVersion = add.S_un.S_un_b.s_b1;
BridgeInformationUpdate.updateMinorVersion = add.S_un.S_un_b.s_b2;
BridgeInformation.isUpdateAvailable = ((BridgeInformation.majorVersion < BridgeInformation.updateMajorVersion) ||
((BridgeInformation.majorVersion == BridgeInformation.updateMajorVersion) && (BridgeInformation.minorVersion < BridgeInformation.updateMinorVersion))) ? 1 : 0;
BridgeInformationUpdate.isUpdateAvailable = BridgeInformation.isUpdateAvailable;;
}
}
#else
// Fetch version from 'A' record in the DNS record - this is probably not used by anything anyway
hostent* address = gethostbyname("floppybridge-amiga.robsmithdev.co.uk");
if ((address) && (address->h_addrtype == AF_INET)) {
if (address->h_addr_list[0] != 0) {
in_addr add = *((in_addr*)address->h_addr_list[0]);
#ifdef _WIN32
// BridgeInformation.updateMajorVersion = add.S_un.S_un_b.s_b1;
// BridgeInformation.updateMinorVersion = add.S_un.S_un_b.s_b2;
BridgeInformationUpdate.updateMajorVersion = add.S_un.S_un_b.s_b1;
BridgeInformationUpdate.updateMinorVersion = add.S_un.S_un_b.s_b2;
#else
uint32_t bytes = htonl(add.s_addr);
/// BridgeInformation.updateMajorVersion = bytes >> 24;
// BridgeInformation.updateMinorVersion = (bytes >> 16) & 0xFF;
BridgeInformationUpdate.updateMajorVersion = bytes >> 24;
BridgeInformationUpdate.updateMinorVersion = (bytes >> 16) & 0xFF;
#endif
BridgeInformation.isUpdateAvailable = ((BridgeInformation.majorVersion < BridgeInformation.updateMajorVersion) ||
((BridgeInformation.majorVersion == BridgeInformation.updateMajorVersion) && (BridgeInformation.minorVersion < BridgeInformation.updateMinorVersion))) ? 1 : 0;
BridgeInformationUpdate.isUpdateAvailable = BridgeInformation.isUpdateAvailable;;
}
}
#endif
}
#ifdef _WIN32
if (output)
@ -422,7 +448,7 @@ extern "C" {
// Returns a pointer to a string containing the details for a profile
FLOPPYBRIDGE_API bool CALLING_CONVENSION BRIDGE_GetProfileConfigFromString(unsigned int profileID, char** profilesConfigString) {
if (!profilesConfigString) return false;
auto f = profileList.find(profileID);
const auto f = profileList.find(profileID);
if (f == profileList.end()) return false;
f->second->toString(profilesConfigString);
@ -550,7 +576,7 @@ extern "C" {
// Creates an instance of a driver from a config string. This will automatically choose the correct driver index
FLOPPYBRIDGE_API bool CALLING_CONVENSION BRIDGE_CreateDriverFromProfileID(unsigned int profileID, BridgeOpened** bridgeDriverHandle) {
if (!bridgeDriverHandle) return false;
const auto f = profileList.find(profileID);
auto f = profileList.find(profileID);
if (f == profileList.end()) return false;
if (!BRIDGE_CreateDriver(f->second->bridgeIndex, bridgeDriverHandle)) return false;

View File

@ -225,14 +225,14 @@ bool GreaseWeazleDiskBridge::performNoClickSeek() {
if (!m_io.supportsDiskChange()) return true;
switch (m_io.performNoClickSeek()) {
case GWResponse::drOK:
updateLastManualCheckTime();
return true;
case GWResponse::drOldFirmware:
return false;
case GWResponse::drError:
m_wasIOError = true;
return false;
case GWResponse::drOK:
updateLastManualCheckTime();
return true;
case GWResponse::drOldFirmware:
return false;
case GWResponse::drError:
m_wasIOError = true;
return false;
}
return false;
@ -277,10 +277,25 @@ CommonBridgeTemplate::ReadResponse GreaseWeazleDiskBridge::readData(PLL::BridgeP
case GWResponse::drOK: return ReadResponse::rrOK;
case GWResponse::drNoDiskInDrive: return ReadResponse::rrNoDiskInDrive;
default: return ReadResponse::rrError;
}
}
}
// Called for a direct read. This does not match up a rotation and should be used with the pll initialized with the LinearExtractor
// pll: required
// Returns: ReadResponse, explains its self
CommonBridgeTemplate::ReadResponse GreaseWeazleDiskBridge::readLinearData(PLL::BridgePLL& pll) {
GWResponse result = m_io.readData(pll);
m_motorTurnOnTime = std::chrono::steady_clock::now();
switch (result) {
case GWResponse::drOK: return ReadResponse::rrOK;
case GWResponse::drNoDiskInDrive: return ReadResponse::rrNoDiskInDrive;
default: return ReadResponse::rrError;
}
}
// Called when a cylinder revolution should be written to the disk.
// Parameters are: rawMFMData The raw data to be written. This is an actual MFM stream, going from MSB to LSB for each byte
// numBytes Number of bits in the buffer to write
@ -316,4 +331,4 @@ void GreaseWeazleDiskBridge::poll() {
}
#endif
#endif

View File

@ -116,6 +116,11 @@ protected:
virtual ReadResponse readData(PLL::BridgePLL& pll, const unsigned int maxBufferSize, RotationExtractor::MFMSample* buffer, RotationExtractor::IndexSequenceMarker& indexMarker,
std::function<bool(RotationExtractor::MFMSample* mfmData, const unsigned int dataLengthInBits)> onRotation) override;
// Called for a direct read. This does not match up a rotation and should be used with the pll initialized with the LinearExtractor
// pll: required
// Returns: ReadResponse, explains its self
virtual ReadResponse readLinearData(PLL::BridgePLL& pll) override;
// Called when a cylinder revolution should be written to the disk.
// Parameters are: rawMFMData The raw data to be written. This is an actual MFM stream, going from MSB to LSB for each byte
// numBytes Number of bits in the buffer to write
@ -128,7 +133,6 @@ protected:
// It's virtual as the default method issues a read and looks for data. If you have a better implementation then override this
virtual bool attemptToDetectDiskChange() override;
public:
GreaseWeazleDiskBridge(FloppyBridge::BridgeMode bridgeMode, FloppyBridge::BridgeDensityMode bridgeDensity, bool enableAutoCache, bool useSmartSpeed, bool autoDetectComPort, char* comPort, FloppyBridge::DriveSelection drive);
@ -143,4 +147,4 @@ public:
#endif
#endif
#endif

View File

@ -915,20 +915,103 @@ inline void unpackStreamQueue(std::queue<unsigned char>& queue, PLLData& pllData
}
// Reads "enough" data to extract data from the disk. This doesnt care about creating a perfect revolution - pll should have the LinearExtractor configured
GWResponse GreaseWeazleInterface::readData(PLL::BridgePLL& pll) {
GWReadFlux header;
// 220ms is long enough for even the worst drives. If a revolution takes longer than this then theres a problem anyway
header.ticks = nSecToTicks(220 * 1000 * 1000, m_gwVersionInformation.sample_freq);
header.max_index = 0;
header.max_index_linger = 0;
m_shouldAbortReading = false;
// Sample storage
std::queue<unsigned char> queue;
// Reset ready for extraction
pll.rotationExtractor()->reset(m_inHDMode);
selectDrive(true);
// Write request
Ack response = Ack::Okay;
if (!sendCommand(Cmd::ReadFlux, (void*)&header, sizeof(header), response)) {
if (!m_motorIsEnabled) selectDrive(false);
return GWResponse::drReadResponseFailed;
}
int32_t failCount = 0;
// Buffer to read into
unsigned char tempReadBuffer[64] = { 0 };
bool zeroDetected = false;
applyCommTimeouts(true);
PLLData pllData;
pllData.freq = m_gwVersionInformation.sample_freq;
do {
// More efficient to read several bytes in one go
uint32_t bytesAvailable = m_comPort.getBytesWaiting();
if (bytesAvailable < 1) bytesAvailable = 1;
if (bytesAvailable > sizeof(tempReadBuffer)) bytesAvailable = sizeof(tempReadBuffer);
uint32_t bytesRead = m_comPort.read(tempReadBuffer, m_shouldAbortReading ? 1 : bytesAvailable);
if (bytesRead < 1) {
failCount++;
if (failCount > 10) break;
}
else failCount = 0;
// If theres this many we can process as this is the maximum we need to process
if ((!m_shouldAbortReading) && (bytesRead)) {
for (uint32_t a = 0; a < bytesRead; a++) {
queue.push(tempReadBuffer[a]);
zeroDetected |= tempReadBuffer[a] == 0;
}
unpackStreamQueue(queue, pllData, pll, m_inHDMode);
}
else {
for (uint32_t a = 0; a < bytesRead; a++) zeroDetected |= tempReadBuffer[a] == 0;
}
} while (!zeroDetected);
applyCommTimeouts(false);
// Check for errors
response = Ack::Okay;
sendCommand(Cmd::GetFluxStatus, nullptr, 0, response);
if (!m_motorIsEnabled) selectDrive(false);
// Update this flag
m_diskInDrive = response != Ack::NoIndex;
switch (response) {
case Ack::FluxOverflow: return GWResponse::drSerialOverrun;
case Ack::NoIndex: return GWResponse::drNoDiskInDrive;
case Ack::Okay: return GWResponse::drOK;
default: return GWResponse::drReadResponseFailed;
}
}
// Reads a complete rotation of the disk, and returns it using the callback function which can return FALSE to stop
// An instance of RotationExtractor is required. This is purely to save on re-allocations. It is internally reset each time
// This is slower than the above because this one focuses on an accurate rotation image of the data rather than just a stream
GWResponse GreaseWeazleInterface::readRotation(PLL::BridgePLL& pll, const unsigned int maxOutputSize, RotationExtractor::MFMSample* firstOutputBuffer, RotationExtractor::IndexSequenceMarker& startBitPatterns,
std::function<bool(RotationExtractor::MFMSample** mfmData, const unsigned int dataLengthInBits)> onRotation) {
GWReadFlux header;
const uint32_t extraTime = OVERLAP_SEQUENCE_MATCHES * (OVERLAP_EXTRA_BUFFER) * 8000;
const uint32_t extraTime = OVERLAP_SEQUENCE_MATCHES * (OVERLAP_EXTRA_BUFFER) * 8000; // this is approx 49mS
if ((pll.rotationExtractor()->isInIndexMode()) || (!pll.rotationExtractor()->hasLearntRotationSpeed())) {
header.ticks = 0;
header.max_index = 1;
header.max_index_linger = nSecToTicks((212 * 1000 * 1000) + extraTime, m_gwVersionInformation.sample_freq);
header.max_index_linger = nSecToTicks((210 * 1000 * 1000) + extraTime, m_gwVersionInformation.sample_freq);
}
else {
header.ticks = nSecToTicks((212 * 1000 * 1000) + extraTime, m_gwVersionInformation.sample_freq);
header.ticks = nSecToTicks((210 * 1000 * 1000) + extraTime, m_gwVersionInformation.sample_freq);
header.max_index = 0;
header.max_index_linger = 0;
}

View File

@ -211,6 +211,9 @@ namespace GreaseWeazle {
GWResponse readRotation(PLL::BridgePLL& pll, const unsigned int maxOutputSize, RotationExtractor::MFMSample* firstOutputBuffer, RotationExtractor::IndexSequenceMarker& startBitPatterns,
std::function<bool(RotationExtractor::MFMSample** mfmData, const unsigned int dataLengthInBits)> onRotation);
// Reads "enough" data to extract data from the disk. This doesn't care about creating a perfect revolution - pll should have the LinearExtractor configured
GWResponse readData(PLL::BridgePLL& pll);
// Turns on and off the reading interface. dontWait disables the GW timeout waiting, so you must instead. Returns drOK, drError,
GWResponse enableMotor(const bool enable, const bool dontWait = false);

View File

@ -30,6 +30,7 @@
// This isn't 100% perfect but does seem to work.
#include "RotationExtractor.h"
#include <cstring>
RotationExtractor::RotationExtractor() : m_sequences(new MFMSequenceInfo[MAX_REVOLUTION_SEQUENCES]),
m_initialSequences(
@ -506,3 +507,74 @@ bool RotationExtractor::extractRotation(MFMSample* output, uint32_t& outputBits,
// and success!
return true;
}
// Reset this back to "empty"
void LinearExtractor::reset(bool isHD) {
m_totalTime = 0;
m_currentPosition = m_outputBuffer;
m_outputStreamPos = 0;
m_outputStreamBit = 0;
}
// Set where the data should be saved to
void LinearExtractor::setOutputBuffer(void* outputBuffer, const uint32_t bufferSizeInBytes) {
m_outputBuffer = (uint8_t*)outputBuffer;
reset(false);
m_totalSize = bufferSizeInBytes;
}
// Write a 0 or 1 to the bit stream
inline void LinearExtractor::writeLinearBit(const bool value) {
if (!m_currentPosition) return;
(*m_currentPosition) <<= 1;
if (value) *m_currentPosition |= 1;
m_outputStreamBit++;
if (m_outputStreamBit >= 8) {
m_outputStreamBit = 0;
m_outputStreamPos++;
if (m_outputStreamPos >= m_totalSize)
m_currentPosition = nullptr;
else m_currentPosition++;
}
}
// Copies the supplied buffer directly in
void LinearExtractor::copyToBuffer(void* data, const uint32_t dataSize) {
uint32_t amountToCopy = dataSize;
if (amountToCopy > m_totalSize) amountToCopy = m_totalSize;
if (!m_outputBuffer) return;
if (!data) return;
#ifdef _WIN32
memcpy_s(m_outputBuffer, m_totalSize, data, amountToCopy);
#else
memcpy(m_outputBuffer, data, amountToCopy);
#endif
m_outputStreamPos = dataSize;
m_outputStreamBit = 0;
m_currentPosition = nullptr;
}
// Submit a single sequence to the list - abstract function
void LinearExtractor::submitSequence(const MFMSequenceInfo& sequence, bool isIndex, bool discardEarlySamples) {
if (!m_currentPosition) return;
m_totalTime += sequence.timeNS;
// And write the output stream
uint32_t bitsToWrite = (uint32_t)sequence.mfm;
if (bitsToWrite > 3) bitsToWrite = 3;
for (uint32_t s = 0; s < bitsToWrite; s++) writeLinearBit(false);
if (sequence.mfm != MFMSequence::mfm000) writeLinearBit(true);
}
// Finalise the buffer (shifting the bits for the current byte into place) and returns the total number of bits received
uint32_t LinearExtractor::finaliseAndGetNumBits() {
// Shift the remaining bits into place
if (m_outputStreamBit && m_currentPosition) {
(*m_currentPosition) <<= 8 - m_outputStreamBit;
m_currentPosition = nullptr;
}
return (m_outputStreamPos * 8) + m_outputStreamBit;
}

View File

@ -61,11 +61,9 @@
#include <stdint.h>
// Class to extract a single rotation from an incoming mfm data sequence.
class RotationExtractor {
// A class that can receive data
class MFMExtractionTarget {
public:
// Enum for the possible sequences we support - mfm1 is not normally allowed.
enum class MFMSequence : unsigned char { mfm1 = 0, mfm01 = 1, mfm001 = 2, mfm0001 = 3, mfm000 = 4 };
@ -109,6 +107,38 @@ public:
bool valid = false;
};
// Return the total amount of time data received so far
[[nodiscard]] virtual uint32_t totalTimeReceived() const = 0;
// Get and set the sequence identified as data round the INDEX pulse so that next time we get consistent revolution starting points
virtual void setIndexSequence(const IndexSequenceMarker& sequence) = 0;
virtual void getIndexSequence(IndexSequenceMarker& sequence) const = 0;
// Reset this back to "empty"
virtual void reset(bool isHD) = 0;
// Submit a single sequence to the list - abstract function
virtual void submitSequence(const MFMSequenceInfo& sequence, bool isIndex, bool discardEarlySamples = true) = 0;
// Returns TRUE if we are readt to extract (eg: full revolution or buffer full)
[[nodiscard]] virtual bool canExtract() const = 0;
// Returns TRUE if this has learnt the time of a disk revolution
[[nodiscard]] virtual bool hasLearntRotationSpeed() const = 0;
// Returns TRUE if we're in INDEX mode
[[nodiscard]] virtual bool isInIndexMode() const = 0;
// Extracts the data we have so far. Might need canExtract to be true depending on the implementation
[[nodiscard]] virtual bool extractRotation(MFMSample* output, uint32_t& outputBits, uint32_t maxBufferSizeBytes, bool usePLLTime = false) = 0;
// I want the destructor virtual
virtual ~MFMExtractionTarget() {};
};
// Class to extract a single rotation from an incoming mfm data sequence and ensure it's a perfect MFM rotation
class RotationExtractor : public MFMExtractionTarget {
private:
// How long a revolution is
uint32_t m_revolutionTime = 0;
@ -158,11 +188,11 @@ public:
virtual ~RotationExtractor();
// Get and set the sequence identified as data round the INDEX pulse so that next time we get consistent revolution starting points
void setIndexSequence(const IndexSequenceMarker& sequence) { m_indexSequence = sequence; }
void getIndexSequence(IndexSequenceMarker& sequence) const { sequence = m_indexSequence; }
virtual void setIndexSequence(const IndexSequenceMarker& sequence) override { m_indexSequence = sequence; }
virtual void getIndexSequence(IndexSequenceMarker& sequence) const override { sequence = m_indexSequence; }
// Reset this back to "empty"
void reset(bool isHD);
virtual void reset(bool isHD) override;
// Return TRUE if we're in HD mode
[[nodiscard]] bool isHD() const { return m_isHD; }
@ -182,13 +212,13 @@ public:
void setRevolutionTime(const uint32_t time) { m_revolutionTime = time; m_revolutionTimeNearlyComplete = (uint32_t)(time * 0.9f); }
// Return the total amount of time data received so far
[[nodiscard]] uint32_t totalTimeReceived() const { return m_timeReceived; }
[[nodiscard]] virtual uint32_t totalTimeReceived() const override { return m_timeReceived; };
// Returns TRUE if this has learnt the time of a disk revolution
[[nodiscard]] bool hasLearntRotationSpeed() const { return m_revolutionTime > (m_isHD ? 300000000U : 150000000U); }
[[nodiscard]] bool hasLearntRotationSpeed() const override { return m_revolutionTime > (m_isHD ? 300000000U : 150000000U); }
// Returns TRUE if we're in INDEX mode
[[nodiscard]] bool isInIndexMode() const { return m_useIndex; }
[[nodiscard]] bool isInIndexMode() const override { return m_useIndex; }
// Sets the code so it always uses the index marker when finding revolutions
void setAlwaysUseIndex(bool useIndex) { m_useIndex = useIndex; }
@ -200,15 +230,59 @@ public:
[[nodiscard]] bool isNearlyReady() const { return (m_revolutionTimeNearlyComplete) && (m_currentTime >= m_revolutionTimeNearlyComplete) && (!m_useIndex); }
// Submit a single sequence to the list
void submitSequence(const MFMSequenceInfo& sequence, bool isIndex, bool discardEarlySamples = true);
virtual void submitSequence(const MFMSequenceInfo& sequence, bool isIndex, bool discardEarlySamples = true) override;
// Returns TRUE if we should be able to extract a revolution
[[nodiscard]] bool canExtract() const { return (m_revolutionReadyAt != INDEX_NOT_FOUND) && (m_revolutionReady) && (m_sequencePos>100); }
[[nodiscard]] virtual bool canExtract() const override { return (m_revolutionReadyAt != INDEX_NOT_FOUND) && (m_revolutionReady) && (m_sequencePos>100); }
// Extracts a single rotation and updates the buffer to remove it. Returns FALSE if no rotation is available
// If calculateSpeedFactor is true, we're in INDEX mode, and HIGH_RESOLUTION_MODE is defined then this will output time in NS rather than the speed factor value
[[nodiscard]] bool extractRotation(MFMSample* output, uint32_t& outputBits, uint32_t maxBufferSizeBytes, bool usePLLTime = false);
[[nodiscard]] virtual bool extractRotation(MFMSample* output, uint32_t& outputBits, uint32_t maxBufferSizeBytes, bool usePLLTime = false) override;
};
// Simple class to just receive that data being sent from the PLL into a linear MFM buffer. It doesn't try to find rotations
class LinearExtractor : public MFMExtractionTarget {
private:
uint8_t* m_outputBuffer = nullptr;
uint8_t* m_currentPosition = nullptr;
uint32_t m_outputStreamPos = 0;
uint32_t m_outputStreamBit = 0;
uint32_t m_totalSize = 0;
uint32_t m_totalTime = 0;
// Write a 0 or 1 to the bit stream
inline void writeLinearBit(const bool value);
public:
// Dont care about this
virtual void setIndexSequence(const IndexSequenceMarker& sequence) override {};
virtual void getIndexSequence(IndexSequenceMarker& sequence) const override {};
virtual bool hasLearntRotationSpeed() const override { return true; };
virtual bool isInIndexMode() const override { return false; };
virtual bool extractRotation(MFMSample* output, uint32_t& outputBits, uint32_t maxBufferSizeBytes, bool usePLLTime = false) override { return false; };
// Returns TRUE if we are readt to extract (eg: full revolution or buffer full)
virtual bool canExtract() const override { return m_outputStreamPos >= m_totalSize; };
// Set where the data should be saved to
void setOutputBuffer(void* outputBuffer, const uint32_t bufferSizeInBytes);
// Finalise the buffer (shifting the bits for the current byte into place) and returns the total number of bits received
uint32_t finaliseAndGetNumBits();
// Return the total amount of time data received so far
virtual uint32_t totalTimeReceived() const override { return m_totalTime; };
// Reset this back to "empty"
virtual void reset(bool isHD) override;
// Copies the supplied buffer directly in
void copyToBuffer(void* data, const uint32_t dataSize);
// Submit a single sequence to the list - abstract function
virtual void submitSequence(const MFMSequenceInfo& sequence, bool isIndex, bool discardEarlySamples = true) override;
};
#endif

View File

@ -230,10 +230,25 @@ CommonBridgeTemplate::ReadResponse SupercardProDiskBridge::readData(PLL::BridgeP
case SCPErr::scpOK: return ReadResponse::rrOK;
case SCPErr::scpNoDiskInDrive: return ReadResponse::rrNoDiskInDrive;
default: return ReadResponse::rrError;
}
}
}
// Called for a direct read. This does not match up a rotation and should be used with the pll initialized with the LinearExtractor
// pll: required
// Returns: ReadResponse, explains its self
CommonBridgeTemplate::ReadResponse SupercardProDiskBridge::readLinearData(PLL::BridgePLL& pll) {
SCPErr result = m_io.readData(pll);
m_motorTurnOnTime = std::chrono::steady_clock::now();
switch (result) {
case SCPErr::scpOK: return ReadResponse::rrOK;
case SCPErr::scpNoDiskInDrive: return ReadResponse::rrNoDiskInDrive;
default: return ReadResponse::rrError;
}
}
// Called when a cylinder revolution should be written to the disk.
// Parameters are: rawMFMData The raw data to be written. This is an actual MFM stream, going from MSB to LSB for each byte
// numBytes Number of bits in the buffer to write

View File

@ -116,6 +116,11 @@ protected:
virtual ReadResponse readData(PLL::BridgePLL& pll, const unsigned int maxBufferSize, RotationExtractor::MFMSample* buffer, RotationExtractor::IndexSequenceMarker& indexMarker,
std::function<bool(RotationExtractor::MFMSample* mfmData, const unsigned int dataLengthInBits)> onRotation) override;
// Called for a direct read. This does not match up a rotation and should be used with the pll initialized with the LinearExtractor
// pll: required
// Returns: ReadResponse, explains its self
virtual ReadResponse readLinearData(PLL::BridgePLL& pll) override;
// Called when a cylinder revolution should be written to the disk.
// Parameters are: rawMFMData The raw data to be written. This is an actual MFM stream, going from MSB to LSB for each byte
// numBytes Number of bits in the buffer to write
@ -141,6 +146,5 @@ public:
};
#endif
#endif
#endif

View File

@ -265,8 +265,8 @@ bool SCPInterface::enableMotor(const bool enable, const bool dontWait) {
if (dontWait) {
payload[0] = htons(1000); // Drive select delay (microseconds)
payload[1] = htons(5000); // Step command delay (microseconds)
payload[2] = htons(1); // Motor on delay (milliseconds)
payload[3] = htons(1); // Track Seek Delay (Milliseconds)
payload[2] = htons(150); // Motor on delay (milliseconds) - for some reason if less than 100 then seeking doesnt work properly
payload[3] = htons(5); // Track Seek Delay (Milliseconds)
payload[4] = htons(MOTOR_AUTOOFF_DELAY); // Before turning off the motor automatically (milliseconds)
}
else {
@ -315,9 +315,10 @@ bool SCPInterface::performNoClickSeek() {
// Select the track, this makes the motor seek to this position
bool SCPInterface::selectTrack(const unsigned char trackIndex, bool ignoreDiskInsertCheck) {
if (m_currentTrack < 0) return false;
//if (m_currentTrack < 0) return false;
if (trackIndex < 0) return false;
if (trackIndex > 81) return false;
if (trackIndex > 83) return false;
selectDrive(true);
SCPResponse response;
@ -918,6 +919,124 @@ SCPErr SCPInterface::readRotation(PLL::BridgePLL& pll, const unsigned int maxOut
}
}
// Reads just enough data to fulfill most extractions needed, but doesnt care about rotation position or index - pll should have the LinearExtractor configured
SCPErr SCPInterface::readData(PLL::BridgePLL& pll) {
SCPResponse response;
pll.rotationExtractor()->reset(m_isHDMode);
// Issue a read-request
selectDrive(true);
// Request real-time flux stream, 8-bit resolution, and 50ns timings.
const unsigned char payload[1] = { STREAMFLAGS_RESOLUTION_50NS | FLAGS_BITSIZE_8BIT | STREAMFLAGS_IMMEDIATEREAD | STREAMFLAGS_RAWFLUX };
bool ret = sendCommand(SCPCommand::DoCMD_STARTSTREAM, payload, 1, response);
if (!ret) {
if (!m_motorIsEnabled) selectDrive(false);
if ((response == SCPResponse::pr_NotReady) || (response == SCPResponse::pr_NoDisk)) {
m_diskInDrive = false;
return SCPErr::scpNoDiskInDrive;
}
return SCPErr::scpUnknownError;
}
applyCommTimeouts(true);
unsigned char tempReadBuffer[4096] = { 0 };
m_isStreaming = true;
m_abortStreaming = false;
m_abortSignalled = false;
// Number of times we failed to read anything
int readFail = 0;
// Sliding window for abort
unsigned char slidingWindow[4] = { 0,0,0,0 };
bool timeout = false;
bool dataState = false;
bool isFirstByte = true;
unsigned char mfmSequences = 0;
int hitIndex = 0;
uint32_t tickInNS = 0;
for (;;) {
// More efficient to read several bytes in one go
unsigned int bytesAvailable = m_comPort.getBytesWaiting();
if (bytesAvailable < 1) bytesAvailable = 1;
if (bytesAvailable > sizeof(tempReadBuffer)) bytesAvailable = sizeof(tempReadBuffer);
unsigned int bytesRead = m_comPort.read(tempReadBuffer, m_abortSignalled ? 1 : bytesAvailable);
for (size_t a = 0; a < bytesRead; a++) {
const unsigned char byteRead = tempReadBuffer[a];
if (m_abortSignalled) {
for (int s = 0; s < 3; s++) slidingWindow[s] = slidingWindow[s + 1];
slidingWindow[3] = byteRead;
// Watch the sliding window for the pattern we need
if ((slidingWindow[0] == 0xDE) && (slidingWindow[1] == 0xAD) && (slidingWindow[2] == (unsigned char)SCPCommand::DoCMD_STOPSTREAM)) {
m_isStreaming = false;
m_comPort.purgeBuffers();
applyCommTimeouts(false);
if (!m_diskInDrive) return SCPErr::scpNoDiskInDrive;
if (timeout) return SCPErr::scpUnknownError;
if (slidingWindow[3] == (unsigned char)SCPResponse::pr_Overrun) return SCPErr::scpOverrun;
return SCPErr::scpOK;
}
}
else {
switch (byteRead) {
case 0xFF:
hitIndex = 1;
break;
case 0x00:
if (hitIndex == 1) hitIndex = 2; else {
tickInNS += m_isHDMode ? (256 * 100) : (256 * 50);
hitIndex = 0;
}
break;
default:
pll.submitFlux(tickInNS + (m_isHDMode ? (byteRead * 100UL) : (byteRead * 50UL)), hitIndex == 2);
tickInNS = 0;
hitIndex = 0;
break;
}
// Is it ready to extract?
if ((pll.canExtract()) || (pll.totalTimeReceived() > 220000000)) {
if (!m_abortSignalled) abortReadStreaming();
}
else
if (pll.totalTimeReceived() > (m_isHDMode ? 1200000000U : 600000000U)) {
// No data, stop
abortReadStreaming();
timeout = true;
}
}
}
if (bytesRead < 1) {
readFail++;
if (readFail > 30) {
if (!m_abortStreaming) {
abortReadStreaming();
readFail = 0;
m_diskInDrive = false;
}
else {
m_isStreaming = false;
applyCommTimeouts(false);
return SCPErr::scpUnknownError;
}
}
else {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
else {
readFail = 0;
}
}
}
// Stops the read streaming immediately and any data in the buffer will be discarded.
bool SCPInterface::abortReadStreaming() {

View File

@ -177,6 +177,9 @@ namespace SuperCardPro {
SCPErr readRotation(PLL::BridgePLL& pll, const unsigned int maxOutputSize, RotationExtractor::MFMSample* firstOutputBuffer, RotationExtractor::IndexSequenceMarker& startBitPatterns,
std::function<bool(RotationExtractor::MFMSample** mfmData, const unsigned int dataLengthInBits)> onRotation);
// Reads just enough data to fulfill most extractions needed, but doesnt care about rotation position or index - pll should have the LinearExtractor configured
SCPErr readData(PLL::BridgePLL& pll);
// Turns on and off the reading interface. dontWait disables the GW timeout waiting, so you must instead.
bool enableMotor(const bool enable, const bool dontWait = false);
@ -216,4 +219,4 @@ namespace SuperCardPro {
};
#endif
#endif

View File

@ -13,6 +13,7 @@
*/
#pragma once
typedef void* BridgeDriverHandle;
namespace FloppyBridge {
@ -77,4 +78,4 @@ namespace FloppyBridge {
};
};

View File

@ -20,7 +20,6 @@
#include <locale>
#include <algorithm>
#include <cstring>
#ifndef _WIN32
#include <dlfcn.h>
#endif

View File

@ -55,7 +55,7 @@ namespace PLL {
const bool m_enabled;
// Rotation extractor
RotationExtractor* m_extractor = nullptr;
MFMExtractionTarget* m_extractor = nullptr;
// Clock
int32_t m_clock = 0;
@ -93,14 +93,14 @@ namespace PLL {
void prepareExtractor(bool isHD, const RotationExtractor::IndexSequenceMarker& indexSequence);
// Change the rotation extractor
void setRotationExtractor(RotationExtractor* extractor) { m_extractor = extractor; }
void setRotationExtractor(MFMExtractionTarget* extractor) { m_extractor = extractor; }
// Re-plays the data back into the rotation extractor but with (random) +/- 64ns of jitter
void rePlayData(const unsigned int maxBufferSize, RotationExtractor::MFMSample* buffer, RotationExtractor::IndexSequenceMarker& indexMarker,
std::function<bool(RotationExtractor::MFMSample* mfmData, const unsigned int dataLengthInBits)> onRotation);
// Return the active rotation extractor
RotationExtractor* rotationExtractor() { return m_extractor; }
MFMExtractionTarget* rotationExtractor() { return m_extractor; }
// Pass on some functions from the extractor
bool canExtract() { return m_extractor->canExtract(); }