Skip to content

Commit

Permalink
Merge pull request RobSmithDev#14 from RobSmithDev/1.6.0.4
Browse files Browse the repository at this point in the history
1.6.0.5
  • Loading branch information
RobSmithDev authored Aug 7, 2024
2 parents 6a1d9fa + 51e3417 commit 710fa15
Show file tree
Hide file tree
Showing 25 changed files with 744 additions and 107 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ Which are separately licenced as UnLicence - see http://unlicense.org
Those are also the files to use if you want to use this plugin in something you're making!

# Updates:
* July 2024 (1.6.4) Faster direct read/write access to floppy disks
Updated Windows update check code
Fixed some compile issues
Fixed a strange crash bug relating to data not being present when expected
* May 2024 (1.6) Sped up the main thread by refactoring and changing how a lock worked that was fixed in 1.5. It now loads disks *slightly* faster
Added support for "Direct Mode" which allows direct MFM buffer reading and writing so you can use this plugin outside of WinUAE
Added some extra commands to the library to make it easier to use outside of WinUAE
Expand Down
19 changes: 16 additions & 3 deletions floppybridge/ArduinoFloppyBridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ bool ArduinoFloppyDiskBridge::performNoClickSeek() {
return false;
case ArduinoFloppyReader::DiagnosticResponse::drReadResponseFailed:
case ArduinoFloppyReader::DiagnosticResponse::drSendFailed:
case ArduinoFloppyReader::DiagnosticResponse::drSendParameterFailed:
case ArduinoFloppyReader::DiagnosticResponse::drSendParameterFailed:
m_wasIOError = true;
return false;
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions floppybridge/ArduinoFloppyBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ class ArduinoFloppyDiskBridge : public CommonBridgeTemplate {
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
Expand Down
199 changes: 198 additions & 1 deletion floppybridge/ArduinoInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ ArduinoInterface::ArduinoInterface() {

// Free me
ArduinoInterface::~ArduinoInterface() {
if (m_tempBuffer) free(m_tempBuffer);
abortReadStreaming();
closePort();
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
6 changes: 5 additions & 1 deletion floppybridge/ArduinoInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 710fa15

Please sign in to comment.