Files

269 lines
10 KiB
C
Raw Normal View History

2023-03-17 11:40:49 +00:00
#ifndef ARDUINO_FLOPPY_READER_WRITER_INTERFACE
#define ARDUINO_FLOPPY_READER_WRITER_INTERFACE
/* ArduinoFloppyReader (and writer)
*
* Copyright (C) 2017-2021 Robert Smith (@RobSmithDev)
* https://amiga.robsmithdev.co.uk
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, see http://www.gnu.org/licenses/
*/
////////////////////////////////////////////////////////////////////////////////////////
// Class to manage the communication between the computer and the Arduino V2.5 //
////////////////////////////////////////////////////////////////////////////////////////
//
// Purpose:
// The class handles the command interface to the arduino. It doesn't do any decoding
// Just open ports, switch motors on and off, seek to tracks etc.
//
//
//
#include <string>
#include <functional>
#include <vector>
#include "RotationExtractor.h"
#include "SerialIO.h"
// Paula on the Amiga used to find the SYNC then read 1900 WORDS. (12868 bytes)
// As the PC is doing the SYNC we need to read more than this to allow a further overlap
// This number must match what the sketch in the Arduino is set to.
#define RAW_TRACKDATA_LENGTH (0x1900*2+0x440)
// With the disk spinning at 300rpm, and data rate of 500kbps, for a full revolution we should receive 12500 bytes of data (12.5k)
// The above buffer assumes a full Paula data capture plsu the size of a sector.
namespace ArduinoFloppyReader {
// Array to hold data from a floppy disk read
typedef unsigned char RawTrackData[RAW_TRACKDATA_LENGTH];
// Sketch firmware version
struct FirmwareVersion {
unsigned char major, minor;
bool fullControlMod;
};
// Represent which side of the disk we're looking at
enum class DiskSurface {
dsUpper, // The upper side of the disk
dsLower // The lower side of the disk
};
// Speed at which the head will seek to a track
enum class TrackSearchSpeed {
tssSlow,
tssNormal,
tssFast,
tssVeryFast
};
// Diagnostic responses from the interface
enum class DiagnosticResponse {
drOK,
// Responses from openPort
drPortInUse,
drPortNotFound,
drPortError,
drAccessDenied,
drComportConfigError,
drBaudRateNotSupported,
drErrorReadingVersion,
drErrorMalformedVersion,
drOldFirmware,
// Responses from commands
drSendFailed,
drSendParameterFailed,
drReadResponseFailed,
drWriteTimeout,
drSerialOverrun,
drFramingError,
drError,
// Response from selectTrack
drTrackRangeError,
drSelectTrackError,
drWriteProtected,
drStatusError,
drSendDataFailed,
drTrackWriteResponseError,
// Returned if there is no disk in the drive
drNoDiskInDrive,
drDiagnosticNotAvailable,
drUSBSerialBad,
drCTSFailure,
drRewindFailure
};
enum class LastCommand {
lcOpenPort,
lcGetVersion,
lcEnableWrite,
lcRewind,
lcDisableMotor,
lcEnableMotor,
lcGotoTrack,
lcSelectSurface,
lcReadTrack,
lcWriteTrack,
lcRunDiagnostics,
lcSwitchDiskMode,
lcReadTrackStream,
lcCheckDiskInDrive,
lcCheckDiskWriteProtected,
lcEraseTrack
};
class ArduinoInterface {
private:
// Windows handle to the serial port device
SerialIO m_comPort;
FirmwareVersion m_version;
bool m_inWriteMode;
LastCommand m_lastCommand;
DiagnosticResponse m_lastError;
bool m_abortStreaming;
bool m_isWriteProtected;
bool m_diskInDrive;
bool m_abortSignalled;
bool m_isStreaming;
// Read a desired number of bytes into the target pointer
bool deviceRead(void* target, const unsigned int numBytes, const bool failIfNotAllRead = false);
// Writes a desired number of bytes from the the pointer
bool deviceWrite(const void* source, const unsigned int numBytes);
// Version of the above where the command has a parameter on the end (as long as its not char 0)
DiagnosticResponse runCommand(const char command, const char parameter = '\0', char* actualResponse = nullptr);
// Apply and change the timeouts on the com port
void applyCommTimeouts(bool shortTimeouts);
// Attempts to write a sector back to the disk. This must be pre-formatted and MFM encoded correctly depending on usePrecomp
DiagnosticResponse internalWriteTrack(const unsigned char* data, const unsigned short numBytes, const bool writeFromIndexPulse, bool usePrecomp);
// Attempts to verify if the reader/writer is running on this port
static DiagnosticResponse internalOpenPort(const std::wstring& portName, bool enableCTSflowcontrol, bool triggerReset, std::string& versionString, SerialIO& port);
// Attempt to sync and get version
static DiagnosticResponse attemptToSync(std::string& versionString, SerialIO& port);
public:
// Constructor for this class
ArduinoInterface();
// Free me
~ArduinoInterface();
const LastCommand getLastFailedCommand() const { return m_lastCommand; };
const DiagnosticResponse getLastError() const { return m_lastError; };
// Uses the above fields to constructr a suitable error message, hopefully useful in diagnosing the issue
const std::string getLastErrorStr() const;
const bool isOpen() const { return m_comPort.isPortOpen(); };
const bool isInWriteMode() const { return m_inWriteMode; };
// Returns a list of ports this coudl be available on
static void enumeratePorts(std::vector<std::wstring>& portList);
// Returns TRUE if there is a disk in the drive. This is ONLY updated by checkForDisk or checkIfDiskIsWriteProtected
inline bool isDiskInDrive() const { return m_diskInDrive; };
// Get the current firmware version. Only valid if openPort is successful
const FirmwareVersion getFirwareVersion() const { return m_version; };
// Turns on and off the reading interface. For the new modded firmware this also allows writing as such the function below is no longer needed
DiagnosticResponse enableReading(const bool enable, const bool reset = true, const bool dontWait = false);
// Turns on and off the reading interface. If irError is returned the disk is write protected. This is no longer needed if you are using the new modded firmware
DiagnosticResponse enableWriting(const bool enable, const bool reset = true);
// Attempts to open the reader running on the COM port number provided. Port MUST support 2M baud
DiagnosticResponse openPort(const std::wstring& portName, bool enableCTSflowcontrol = true);
// Attempts to verify if the reader/writer is running on this port
static bool isPortCorrect(const std::wstring& portName);
// Checks if the disk is write protected. If forceCheck=false then the last cached version is returned. This is also updated by checkForDisk() as well as this function
DiagnosticResponse checkIfDiskIsWriteProtected(bool forceCheck);
// Seek to track 0
DiagnosticResponse findTrack0();
// Check to see if a disk is inserted in the drive. If forceCheck then the last cached verson is returned, which is also updated by diskStepOnce.
DiagnosticResponse checkForDisk(bool forceCheck);
// Reads a complete rotation of the disk, and returns it using the callback function whcih 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 readRotation(RotationExtractor& extractor, const unsigned int maxOutputSize, RotationExtractor::MFMSample* firstOutputBuffer, RotationExtractor::IndexSequenceMarker& startBitPatterns, std::function<bool(RotationExtractor::MFMSample** mfmData, const unsigned int dataLengthInBits)> onRotation);
// Stops the read streamming immediately and any data in the buffer will be discarded. The above function will exit when the Arduino has also stopped streaming data
bool abortReadStreaming();
// Returns TURE if the disk si currently streaming data
inline bool isStreaming() { return m_isStreaming; };
// Check if an index pulse can be detected from the drive
DiagnosticResponse testIndexPulse();
// Check if data can be detected from the drive
DiagnosticResponse testDataPulse();
// Check and switch to HD disk
DiagnosticResponse setDiskCapacity(bool switchToHD_Disk);
// Select the track, this makes the motor seek to this position
DiagnosticResponse selectTrack(const unsigned char trackIndex, const TrackSearchSpeed searchSpeed = TrackSearchSpeed::tssNormal, bool ignoreDiskInsertCheck = false);
// If the drive is on track 0, this does a test seek to -1 if supported
DiagnosticResponse performNoClickSeek();
// Choose which surface of the disk to read from
DiagnosticResponse selectSurface(const DiskSurface side);
// Read RAW data from the current track and surface selected
DiagnosticResponse readCurrentTrack(RawTrackData& trackData, const bool readFromIndexPulse);
// Attempts to write a sector back to the disk. This must be pre-formatted and MFM encoded correctly
DiagnosticResponse writeCurrentTrack(const unsigned char* data, const unsigned short numBytes, const bool writeFromIndexPulse);
// The precomp version of the above.
DiagnosticResponse writeCurrentTrackPrecomp(const unsigned char* mfmData, const unsigned short numBytes, const bool writeFromIndexPulse, bool usePrecomp);
// Erases the current track by writing 0xAA to it
DiagnosticResponse eraseCurrentTrack();
// Check CTS status - you must open with CTS disabled for this to work
DiagnosticResponse testCTS();
// Check if the USB to Serial device can keep up properly
DiagnosticResponse testTransferSpeed();
// Returns true if the track actually contains some data, else its considered blank or unformatted
bool trackContainsData(const RawTrackData& trackData) const;
// Closes the port down
void closePort();
};
};
#endif