diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml new file mode 100644 index 0000000..e7fd47e --- /dev/null +++ b/.github/workflows/c-cpp.yml @@ -0,0 +1,27 @@ +name: C/C++ CI + +on: + push: + branches: [ linux ] + pull_request: + branches: [ linux ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Install gtkmm + run: sudo apt-get install libgtkmm-3.0-dev + - name: make + run: | + cd ArduinoFloppyReader/ArduinoFloppyReader + make all + - name: Upload Artifact + uses: actions/upload-artifact@v2 + with: + name: GTK GUI + path: ArduinoFloppyReader/ArduinoFloppyReader/GarduinoReaderWriter + diff --git a/ArduinoFloppyReader/.gitignore b/ArduinoFloppyReader/.gitignore index 4ce6fdd..b490478 100644 --- a/ArduinoFloppyReader/.gitignore +++ b/ArduinoFloppyReader/.gitignore @@ -27,6 +27,11 @@ bld/ [Oo]bj/ [Ll]og/ +ArduinoReaderWriter +GarduinoReaderWriter +*~ +.vscode/ + # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot diff --git a/ArduinoFloppyReader/ArduinoFloppyReader/Garduino.glade b/ArduinoFloppyReader/ArduinoFloppyReader/Garduino.glade new file mode 100644 index 0000000..cfdea86 --- /dev/null +++ b/ArduinoFloppyReader/ArduinoFloppyReader/Garduino.glade @@ -0,0 +1,608 @@ + + + + + + 700 + 600 + False + Arduino Amiga Floppy Disk Reader and Writer + + + True + False + vertical + 5 + + + True + False + + + True + False + 0 + + + True + False + 1 + 1 + 12 + 12 + + + True + False + center + 20 + True + + + True + False + Arduino connected on: + + + False + True + 0 + + + + + True + False + + + False + True + 1 + + + + + Run Diagnostics + True + False + True + True + + + False + True + 2 + + + + + + + + + True + False + 10 + Arduino Configuration + + + + + + False + True + 0 + + + + + True + False + ../ArduinoFloppyReaderWin/res/floppy.bmp + + + True + True + 1 + + + + + False + True + 0 + + + + + True + False + 0 + + + True + False + 10 + 10 + 12 + 12 + + + True + False + vertical + + + True + False + + + True + False + Save to + + + False + True + 0 + + + + + True + False + 1 + + ADF + SCP + + + + False + True + 1 + + + + + True + True + + + True + True + 2 + + + + + True + False + select-folder + + + + False + True + 3 + + + + + Copy Disk + True + False + True + True + + + False + True + 4 + + + + + False + True + 0 + + + + + True + False + + + True + False + SCP will have limited resolution enough for most Amiga disks. + + + True + True + 0 + + + + + True + False + Tracks: + + + False + True + 1 + + + + + 80 + True + True + False + True + True + radio82 + + + False + True + 10 + 2 + + + + + 82 + True + True + False + True + True + radio80 + + + False + True + end + 2 + + + + + False + True + 1 + + + + + True + False + center + + + Always Ignore Read Errors + True + True + False + start + True + True + + + True + True + 0 + + + + + False + True + 2 + + + + + + + + + True + False + 10 + Copy Disk to ADF or SCP File (read) + + + + + False + True + 6 + 1 + + + + + True + False + 0 + + + True + False + 12 + 12 + + + True + False + vertical + + + True + False + + + True + False + ADF File: + + + False + True + 0 + + + + + True + False + + + + True + True + 1 + + + + + Write Disk + True + False + True + True + + + False + True + 2 + + + + + False + True + 0 + + + + + True + False + + + Verify Write + True + True + False + start + True + + + True + True + 0 + + + + + Use Write Compensation (V1.8+ firmware) + True + True + False + start + True + + + True + True + end + 1 + + + + + False + True + 1 + + + + + True + False + With precomp do not use a USB hub and try to use a USB2 port if possible + + + False + True + 2 + + + + + + + + + True + False + 10 + Write ADF File to Disk (write) + + + + + False + True + 6 + 2 + + + + + True + False + 12 + + + True + False + Cylinder: + + + True + True + 0 + + + + + True + False + 0 + + + True + True + 1 + + + + + True + False + Current Side: + + + True + True + 2 + + + + + True + False + Upper + + + True + True + 3 + + + + + True + False + Good Sectors: + + + True + True + 4 + + + + + True + False + 0 + + + True + True + 5 + + + + + True + False + Partial Sectors + + + True + True + 6 + + + + + True + False + 0 + + + True + True + 7 + + + + + False + True + 6 + 3 + + + + + True + False + never + in + 100 + False + False + 300 + True + True + + + True + True + False + False + False + True + + + + + True + True + 5 + + + + + + diff --git a/ArduinoFloppyReader/ArduinoFloppyReader/GarduinoReaderWriter.cpp b/ArduinoFloppyReader/ArduinoFloppyReader/GarduinoReaderWriter.cpp new file mode 100644 index 0000000..d213111 --- /dev/null +++ b/ArduinoFloppyReader/ArduinoFloppyReader/GarduinoReaderWriter.cpp @@ -0,0 +1,386 @@ +#include +#include +#include +#include +#include +#include +#include "../lib/ArduinoInterface.h" +#include "../lib/ADFWriter.h" + +class MainWindow : public Gtk::ApplicationWindow +{ +public: + MainWindow(BaseObjectType *obj, Glib::RefPtr const &builder) + : Gtk::ApplicationWindow(obj), builder{builder} + { + get_widgets(); + std::vector portList; + ArduinoFloppyReader::ArduinoInterface::enumeratePorts(portList); + for (std::wstring port : portList) + { + const std::string portString(port.begin(), port.end()); + Glib::ustring text = Glib::ustring(portString.c_str(), port.size()); + portsCombo->append(text); + } + if (portList.size() > 0) + { + portsCombo->set_active(0); + diagnosticsButton->set_sensitive(true); + } + diagnosticsButton->signal_clicked().connect([this]() + { + diagnosticsButton->set_sensitive(false); + Glib::ustring serial = portsCombo->get_active_text(); + run_diagnostics(serial); + diagnosticsButton->set_sensitive(true); + }); + + auto readerCallback = [this](const int currentTrack, const ArduinoFloppyReader::DiskSurface currentSide, const int retryCounter, const int sectorsFound, const int badSectorsFound) -> ArduinoFloppyReader::WriteResponse + { + std::string side = (currentSide == ArduinoFloppyReader::DiskSurface::dsUpper ? "Upper" : "Lower"); + std::cout << "currentTrack: " << currentTrack << " side: " << side << " retryCount: " << retryCounter << std::endl; + cylinderCount->set_text(Glib::ustring::sprintf("%i", currentTrack)); + goodCount->set_text(Glib::ustring::sprintf("%i", sectorsFound)); + partialCount->set_text(Glib::ustring::sprintf("%i", badSectorsFound)); + sideLabel->set_text(side); + + while (Gtk::Main::events_pending()) + { + Gtk::Main::iteration(); + } + if (retryCounter > 20) + { + if (alwaysIgnore->get_active()) + { + return ArduinoFloppyReader::WriteResponse::wrSkipBadChecksums; + } + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "Verify error writing track.\nDisk Write Error", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_NONE); + dialog.add_button("Abort", 1); + dialog.add_button("Retry", 2); + dialog.add_button("Ignore", 3); + int response = dialog.run(); + dialog.hide(); + + switch (response) + { + case 1: + return ArduinoFloppyReader::WriteResponse::wrAbort; + case 2: + return ArduinoFloppyReader::WriteResponse::wrRetry; + case 3: + return ArduinoFloppyReader::WriteResponse::wrSkipBadChecksums; + } + } + + // Just continue + return ArduinoFloppyReader::WriteResponse::wrContinue; + }; + + fileEntry->signal_changed().connect([this]() + { + if (fileEntry->get_text_length() > 0 && diagnosticsButton->get_sensitive()) + { + copyButton->set_sensitive(true); + } + else + { + copyButton->set_sensitive(false); + } + }); + copyButton->signal_clicked().connect([this, readerCallback]() + { + copyButton->set_sensitive(false); + while (Gtk::Main::events_pending()) + { + Gtk::Main::iteration(); + } + Glib::ustring folder = folderButton->get_filename(); + Glib::ustring filename = folder + "/" + fileEntry->get_buffer()->get_text(); + ArduinoFloppyReader::ADFWriter writer; + Glib::ustring serial = portsCombo->get_active_text(); + writer.openDevice(std::wstring(serial.begin(), serial.end())); + bool tracks80 = trackButton->get_active(); + ArduinoFloppyReader::ADFResult readerResult; + if (typeSelector->get_active_text() == "ADF") + { + readerResult = writer.DiskToADF(std::wstring(filename.begin(), filename.end()), tracks80 ? 80 : 82, readerCallback); + } + else + { + readerResult = writer.DiskToSCP(std::wstring(filename.begin(), filename.end()), tracks80 ? 80 : 82, 3, readerCallback); + } + // Handle the result + handleResult(writer, readerResult); + copyButton->set_sensitive(true); + }); + + writeFolderSelector->signal_file_set().connect([this]() + { + std::cout << "got filename " << writeFolderSelector->get_filename() << std::endl; + if (this->writeFolderSelector->get_filename() != "" && diagnosticsButton->get_sensitive()) + { + writeButton->set_sensitive(true); + } + else + { + writeButton->set_sensitive(false); + } + }); + auto writerCallback = [this](const int currentTrack, const ArduinoFloppyReader::DiskSurface currentSide, const bool isVerifyError) -> ArduinoFloppyReader::WriteResponse + { + std::string side = (currentSide == ArduinoFloppyReader::DiskSurface::dsUpper ? "Upper" : "Lower"); + std::cout << "writing: currentTrack: " << currentTrack << " side: " << side << std::endl; + + cylinderCount->set_text(Glib::ustring::sprintf("%i", currentTrack)); + sideLabel->set_text(side); + if (isVerifyError) + { + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "Verify error writing track.\nDisk Write Error", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_NONE); + dialog.add_button("Abort", 1); + dialog.add_button("Retry", 2); + dialog.add_button("Ignore", 3); + int response = dialog.run(); + dialog.hide(); + + switch (response) + { + case 1: + return ArduinoFloppyReader::WriteResponse::wrAbort; + case 2: + return ArduinoFloppyReader::WriteResponse::wrRetry; + case 3: + return ArduinoFloppyReader::WriteResponse::wrSkipBadChecksums; + } + } + // Just continue + return ArduinoFloppyReader::WriteResponse::wrContinue; + }; + writeButton->signal_clicked().connect([this, writerCallback]() + { + writeButton->set_sensitive(false); + while (Gtk::Main::events_pending()) + { + Gtk::Main::iteration(); + } + Glib::ustring filename = writeFolderSelector->get_filename(); + ArduinoFloppyReader::ADFWriter writer; + Glib::ustring serial = portsCombo->get_active_text(); + writer.openDevice(std::wstring(serial.begin(), serial.end())); + ArduinoFloppyReader::ADFResult readerResult; + readerResult = writer.ADFToDisk(std::wstring(filename.begin(), filename.end()), verify->get_active(), writeCompensation->get_active(), writerCallback); + // Handle the result + handleResult(writer, readerResult); + writeButton->set_sensitive(true); + }); + } + + virtual ~MainWindow() = default; + +private: + Glib::RefPtr builder; + Gtk::ComboBoxText *portsCombo = nullptr; + Gtk::Button *diagnosticsButton = nullptr; + Gtk::Button *copyButton = nullptr; + Gtk::Label *cylinderCount = nullptr; + Gtk::Label *sideLabel = nullptr; + Gtk::Label *goodCount = nullptr; + Gtk::Label *partialCount = nullptr; + Gtk::CheckButton *verify = nullptr; + Gtk::CheckButton *alwaysIgnore = nullptr; + Gtk::CheckButton *writeCompensation = nullptr; + Gtk::FileChooserButton *writeFolderSelector = nullptr; + Gtk::Button *writeButton = nullptr; + Gtk::Entry *fileEntry = nullptr; + Gtk::FileChooserButton *folderButton = nullptr; + Gtk::ComboBoxText *typeSelector = nullptr; + Gtk::RadioButton *trackButton = nullptr; + Gtk::TextView *statusBar = nullptr; + Gtk::Image *diskImage = nullptr; + + void get_widgets() + { + builder->get_widget("serialPortsCombo", portsCombo); + builder->get_widget("diagnosticsButton", diagnosticsButton); + builder->get_widget("copyButton", copyButton); + builder->get_widget("cylinderCount", cylinderCount); + builder->get_widget("sideLabel", sideLabel); + builder->get_widget("goodCount", goodCount); + builder->get_widget("partialCount", partialCount); + builder->get_widget("fileNameEntry", fileEntry); + builder->get_widget("folderSelector", folderButton); + builder->get_widget("typeSelector", typeSelector); + builder->get_widget("radio80", trackButton); + builder->get_widget("alwaysIgnore", alwaysIgnore); + builder->get_widget("verify", verify); + builder->get_widget("writeCompensation", writeCompensation); + builder->get_widget("writeFolderSelector", writeFolderSelector); + builder->get_widget("writeButton", writeButton); + builder->get_widget("statusBar", statusBar); + } + + void reset() + { + cylinderCount->set_text("0"); + goodCount->set_text("0"); + partialCount->set_text("0"); + sideLabel->set_text("Upper"); + } + + void handleResult(ArduinoFloppyReader::ADFWriter writer, ArduinoFloppyReader::ADFResult readerResult) + { + switch (readerResult) + { + case ArduinoFloppyReader::ADFResult::adfrComplete: + { + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "Completed", false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK); + dialog.run(); + dialog.hide(); + break; + } + case ArduinoFloppyReader::ADFResult::adfrAborted: + { + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "Aborted", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); + dialog.run(); + dialog.hide(); + break; + } + case ArduinoFloppyReader::ADFResult::adfrFileError: + { + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "Unable to open the specified file to write to it.\nOutput File Error", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); + dialog.run(); + dialog.hide(); + break; + } + case ArduinoFloppyReader::ADFResult::adfrFileIOError: + { + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "An error occured writing to the specified file.\nOutput File Error", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); + dialog.run(); + dialog.hide(); + break; + } + case ArduinoFloppyReader::ADFResult::adfrFirmwareTooOld: + { + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "This requires firmware V1.8 or newer.\nFirmware out of date", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); + dialog.run(); + dialog.hide(); + break; + } + case ArduinoFloppyReader::ADFResult::adfrDiskWriteProtected: + { + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "The disk is write protected", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); + dialog.run(); + dialog.hide(); + break; + } + case ArduinoFloppyReader::ADFResult::adfrCompletedWithErrors: + { + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, "Completed with some errors", false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK); + dialog.run(); + dialog.hide(); + break; + } + case ArduinoFloppyReader::ADFResult::adfrDriveError: + { + std::string msg = "An error occured communicating with the Arduino interface:\r\n\r\n"; + msg += writer.getLastError(); + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, msg.c_str(), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); + dialog.run(); + dialog.hide(); + break; + } + } + reset(); + } + + bool showQuestion(bool isQuestion, const std::string question) + { + Gtk::ButtonsType buttons = Gtk::BUTTONS_OK_CANCEL; + if (isQuestion) + { + buttons = Gtk::BUTTONS_YES_NO; + } + + Gtk::MessageDialog dialog = Gtk::MessageDialog(*this, question, false, Gtk::MESSAGE_QUESTION, buttons); + int Answer = dialog.run(); + dialog.hide(); + + // Process user choice + bool isYes = false; + switch (Answer) + { + case (Gtk::RESPONSE_OK): + isYes = true; + break; + case (Gtk::RESPONSE_CANCEL): + std::cout << "Cancel clicked." << std::endl; + break; + case (Gtk::RESPONSE_YES): + std::cout << "Yes clicked." << std::endl; + isYes = true; + break; + case (Gtk::RESPONSE_NO): + std::cout << "No clicked." << std::endl; + break; + default: + std::cout << "Unexpected button clicked." << std::endl; + break; + } + while (Gtk::Main::events_pending()) + { + Gtk::Main::iteration(); + } + return isYes; + } + + void showStatus(bool isError, std::string status) + { + std::string strLine; + + while (Gtk::Main::events_pending()) + { + Gtk::Main::iteration(); + } + if (isError) + strLine = "DIAGNOSTICS FAILED: "; + strLine += (status + "\n").c_str(); + std::cerr << strLine; + auto buffer = statusBar->get_buffer(); + buffer->insert(buffer->end(), strLine); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + statusBar->scroll_to(buffer->get_insert(), 0); + while (Gtk::Main::events_pending()) + { + Gtk::Main::iteration(); + } + } + + void run_diagnostics(Glib::ustring serial) + { + ArduinoFloppyReader::ADFWriter writer; + writer.runDiagnostics( + std::wstring(serial.begin(), serial.end()), [this, serial](bool isError, const std::string message) -> void + { this->showStatus(isError, message); }, + [this, serial](bool isQuestion, const std::string question) -> bool + { + return this->showQuestion(isQuestion, question); + }); + while (Gtk::Main::events_pending()) + { + Gtk::Main::iteration(); + } + } +}; + +int main(int argc, char *argv[]) +{ + auto app = Gtk::Application::create(argc, argv, "be.sourcery.Garduino"); + auto builder = Gtk::Builder::create(); + + builder->add_from_file("Garduino.glade"); + + MainWindow *wnd = nullptr; + + builder->get_widget_derived("MainWindow", wnd); + + return app->run(*wnd); +} diff --git a/ArduinoFloppyReader/ArduinoFloppyReader/WarduinoReaderWriter.cpp b/ArduinoFloppyReader/ArduinoFloppyReader/WarduinoReaderWriter.cpp new file mode 100644 index 0000000..feef0b5 --- /dev/null +++ b/ArduinoFloppyReader/ArduinoFloppyReader/WarduinoReaderWriter.cpp @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include +#include "../lib/ArduinoInterface.h" +#include "../lib/ADFWriter.h" + +class WarduinoApp : public wxApp +{ +public: + virtual bool OnInit(); +}; + +class MyFrame : public wxFrame +{ +public: + MyFrame(); + +private: + // wxTextCtrl *textctrl; + wxComboBox *portCombo; + wxButton *diagnosticsButton; + wxButton *readButton; + wxButton *writeButton; + void OnDiagnostics(wxCommandEvent &WXUNUSED(event)); + void run_diagnostics(std::string serial); + void showStatus(bool isError, std::string status); + bool showQuestion(bool isQuestion, const std::string question); +}; + +enum +{ + BUTTON_Diagnostics = wxID_HIGHEST + 1, + COMBO_Ports = BUTTON_Diagnostics + 1, +}; + +bool WarduinoApp::OnInit() +{ + MyFrame *frame = new MyFrame(); + frame->Show(true); + return true; +} +MyFrame::MyFrame() + : wxFrame(NULL, wxID_ANY, "Arduino Amiga Floppy Disk Reader and Writer", wxDefaultPosition, wxSize(600, 650)) +{ + CreateStatusBar(); + SetStatusText("Welcome to wxWidgets!"); + + wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *hbox1 = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *hbox2 = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *hbox3 = new wxBoxSizer(wxHORIZONTAL); + wxStaticBox *frameDiag = new wxStaticBox(this, wxID_ANY, "Arduino Configuration", wxDefaultPosition, wxSize(600, 200)); + wxStaticBox *frameCopy = new wxStaticBox(this, wxID_ANY, "Copy Disk to ADF or SCP File (read)", wxDefaultPosition, wxSize(600, 200)); + wxStaticBox *frameWrite = new wxStaticBox(this, wxID_ANY, "Write ADF File to Disk (write)", wxDefaultPosition, wxSize(600, 200)); + + auto arduinoLabel = new wxStaticText(this, -1, "Arduino connected on"); + frameDiag->SetSizer(hbox1); + frameCopy->SetSizer(hbox2); + frameWrite->SetSizer(hbox3); + hbox1->Add(arduinoLabel); + vbox->Add(frameDiag, 1); + vbox->Add(frameCopy, 1); + vbox->Add(frameWrite, 1); + auto saveLabel = new wxStaticText(this, -1, "Save to"); + hbox2->Add(saveLabel); + auto writeLabel = new wxStaticText(this, -1, "ADF File"); + hbox3->Add(writeLabel); + this->SetSizer(vbox); + + portCombo = new wxComboBox(this, COMBO_Ports); + hbox1->Add(portCombo); + std::vector portList; + ArduinoFloppyReader::ArduinoInterface::enumeratePorts(portList); + for (std::wstring port : portList) + { + const std::string portString(port.begin(), port.end()); + portCombo->Append(portString); + } + if (portList.size() > 0) { + portCombo->SetSelection(0); + } + diagnosticsButton = new wxButton(frameDiag, BUTTON_Diagnostics, "Run Diagnostics"); + Connect(BUTTON_Diagnostics, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MyFrame::OnDiagnostics)); + hbox1->Add(diagnosticsButton); + Centre(); + Show(true); +} +void MyFrame::OnDiagnostics(wxCommandEvent &WXUNUSED(event)) +{ + auto serial = this->portCombo->GetValue(); + run_diagnostics(serial.c_str().AsChar()); +} + +bool MyFrame::showQuestion(bool isQuestion, const std::string question) +{ + long style = (wxOK | wxCANCEL); + if (isQuestion) + { + style = (wxYES | wxNO); + } + + wxMessageDialog dial(NULL, question.c_str(), wxT("Question"), style | wxICON_QUESTION); + auto Answer = dial.ShowModal(); + // Process user choice + bool isYes = false; + switch (Answer) + { + case (wxID_OK): + isYes = true; + break; + case (wxID_CANCEL): + std::cout << "Cancel clicked." << std::endl; + break; + case (wxID_YES): + std::cout << "Yes clicked." << std::endl; + isYes = true; + break; + case (wxID_NO): + std::cout << "No clicked." << std::endl; + break; + default: + std::cout << "Unexpected answer: " << Answer << std::endl; + break; + } + return isYes; +} + +void MyFrame::showStatus(bool isError, std::string status) +{ + std::string strLine; + + if (isError) + strLine = "DIAGNOSTICS FAILED: "; + strLine += (status + "\n").c_str(); + std::cerr << strLine; + auto statusBar = GetStatusBar(); + statusBar->SetStatusText(strLine); +} + +void MyFrame::run_diagnostics(std::string serial) +{ + ArduinoFloppyReader::ADFWriter writer; + writer.runDiagnostics( + std::wstring(serial.begin(), serial.end()), [this, serial](bool isError, const std::string message) -> void + { this->showStatus(isError, message); }, + [this, serial](bool isQuestion, const std::string question) -> bool + { + return this->showQuestion(isQuestion, question); + }); +} + +wxIMPLEMENT_APP(WarduinoApp); diff --git a/ArduinoFloppyReader/ArduinoFloppyReader/makefile b/ArduinoFloppyReader/ArduinoFloppyReader/makefile index 9453425..0208ab2 100644 --- a/ArduinoFloppyReader/ArduinoFloppyReader/makefile +++ b/ArduinoFloppyReader/ArduinoFloppyReader/makefile @@ -9,7 +9,9 @@ CC = g++ # define any compile-time flags -CFLAGS = -Wall -std=c++14 -Wno-psabi -O3 +CFLAGS = -Wall -std=c++14 -Wno-psabi -O0 -g +GCFLAGS = ${CFLAGS} $(shell pkg-config --cflags gtkmm-3.0) +WCFLAGS = ${CFLAGS} $(shell wx-config --cflags) # define any directories containing header files other than /usr/include # @@ -19,14 +21,19 @@ INCLUDES = -I../lib # if I wanted to include libraries not in /usr/lib I'd specify # their path using -Lpath, something like: LFLAGS = -L../lib +GLFLAGS = ${LFLAGS} $(shell pkg-config --libs gtkmm-3.0) +WLFLAGS = ${LFLAGS} $(shell wx-config --libs) # define any libraries to link into executable: # if I want to link in libraries (libx.so or libx.a) I use the -llibname # option, something like (this will link in libmylib.so and libm.so: LIBS = -# define the C source files -SRCS = Main.cpp ../lib/SerialIO.cpp ../lib/RotationExtractor.cpp ../lib/ArduinoInterface.cpp ../lib/ADFWriter.cpp +# define the C surce files +COMMON_SRC = ../lib/SerialIO.cpp ../lib/RotationExtractor.cpp ../lib/ArduinoInterface.cpp ../lib/ADFWriter.cpp +SRCS = Main.cpp ${COMMON_SRC} +GSRCS = GarduinoReaderWriter.cpp ${COMMON_SRC} +WSRCS = WarduinoReaderWriter.cpp ${COMMON_SRC} # define the C object files # @@ -37,10 +44,16 @@ SRCS = Main.cpp ../lib/SerialIO.cpp ../lib/RotationExtractor.cpp ../lib/ArduinoI # with the .o suffix # OBJS = $(SRCS:.c=.o) +GOBJS = $(GSRCS:.c=.o) +WOBJS = $(WSRCS:.c=.o) # define the executable file MAIN = ArduinoReaderWriter +#GUI +GARDUINO = GarduinoReaderWriter +WARDUINO = WarduinoReaderWriter + # # The following part of the makefile is generic; it can be used to # build any executable just by changing the definitions above and by @@ -49,12 +62,18 @@ MAIN = ArduinoReaderWriter .PHONY: depend clean -all: $(MAIN) +all: $(MAIN) $(GARDUINO) $(WARDUINO) @echo ArduinoReaderWriter ready $(MAIN): $(OBJS) $(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LFLAGS) $(LIBS) +$(GARDUINO): $(GOBJS) makefile Garduino.glade + $(CC) $(GCFLAGS) $(INCLUDES) -o $(GARDUINO) $(GOBJS) $(GLFLAGS) $(LIBS) + +$(WARDUINO): $(WOBJS) makefile + $(CC) $(WCFLAGS) $(INCLUDES) -o $(WARDUINO) $(WOBJS) $(WLFLAGS) $(LIBS) + # this is a suffix replacement rule for building .o's from .c's # it uses automatic variables $<: the name of the prerequisite of # the rule(a .c file) and $@: the name of the target of the rule (a .o file) @@ -63,7 +82,7 @@ $(MAIN): $(OBJS) $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ clean: - $(RM) *.o *~ $(MAIN) + $(RM) *.o *~ $(MAIN) ${GARDUINO} depend: $(SRCS) makedepend $(INCLUDES) $^ diff --git a/ArduinoFloppyReader/ArduinoFloppyReader/readme.txt b/ArduinoFloppyReader/ArduinoFloppyReader/readme.txt index 2d2a3bd..608a4f9 100644 --- a/ArduinoFloppyReader/ArduinoFloppyReader/readme.txt +++ b/ArduinoFloppyReader/ArduinoFloppyReader/readme.txt @@ -5,6 +5,9 @@ This is a console application It should build with Visual Studio 2019 -It will also compile on some distros of Linux. +It will also compile on some distros of Linux. +To compile/run the GTK+ GUI (requires gtkmm-3.x): +make all; ./GarduinoReaderWriter + It has been tested on Raspberry Pi OS (Raspbian - Debian-based) and can be compiled using the provided makefile \ No newline at end of file diff --git a/ArduinoFloppyReader/lib/ADFWriter.cpp b/ArduinoFloppyReader/lib/ADFWriter.cpp index 3b39bf7..85f074c 100644 --- a/ArduinoFloppyReader/lib/ADFWriter.cpp +++ b/ArduinoFloppyReader/lib/ADFWriter.cpp @@ -89,13 +89,13 @@ typedef struct alignas(8) { unsigned char sectorNumber; // The sector we just read (0 to 11) unsigned char sectorsRemaining; // How many more sectors remain until the gap (0 to 10) - unsigned long sectorLabel[4]; // OS Recovery Data, we ignore this + uint32_t sectorLabel[4]; // OS Recovery Data, we ignore this - unsigned long headerChecksum; // Read from the header, header checksum - unsigned long dataChecksum; // Read from the header, data checksum + uint32_t headerChecksum; // Read from the header, header checksum + uint32_t dataChecksum; // Read from the header, data checksum - unsigned long headerChecksumCalculated; // The header checksum we calculate - unsigned long dataChecksumCalculated; // The data checksum we calculate + uint32_t headerChecksumCalculated; // The header checksum we calculate + uint32_t dataChecksumCalculated; // The data checksum we calculate RawDecodedSector data; // decoded sector data @@ -116,15 +116,15 @@ struct DecodedTrack { // *input; MFM coded data buffer (size == 2*data_size) // *output; decoded data buffer (size == data_size) // Returns the checksum calculated over the data -unsigned long decodeMFMdata(const unsigned long* input, unsigned long* output, const unsigned int data_size) { - unsigned long odd_bits, even_bits; - unsigned long chksum = 0L; +uint32_t decodeMFMdata(const uint32_t* input, uint32_t* output, const unsigned int data_size) { + uint32_t odd_bits, even_bits; + uint32_t chksum = 0L; unsigned int count; // the decoding is made here long by long : with data_size/4 iterations for (count = 0; count < data_size / 4; count++) { odd_bits = *input; // longs with odd bits - even_bits = *(unsigned long*)(((unsigned char*)input) + data_size); // longs with even bits - located 'data_size' bytes after the odd bits + even_bits = *(uint32_t*)(((unsigned char*)input) + data_size); // longs with even bits - located 'data_size' bytes after the odd bits chksum ^= odd_bits; // XOR Checksum chksum ^= even_bits; @@ -140,12 +140,12 @@ unsigned long decodeMFMdata(const unsigned long* input, unsigned long* output, c // *input; RAW data buffer (size == data_size) // *output; MFM encoded buffer (size == data_size*2) // Returns the checksum calculated over the data -unsigned long encodeMFMdataPart1(const unsigned long* input, unsigned long* output, const unsigned int data_size) { - unsigned long chksum = 0L; +uint32_t encodeMFMdataPart1(const uint32_t* input, uint32_t* output, const unsigned int data_size) { + uint32_t chksum = 0L; unsigned int count; - unsigned long* outputOdd = output; - unsigned long* outputEven = (unsigned long*)(((unsigned char*)output) + data_size); + uint32_t* outputOdd = output; + uint32_t* outputEven = (uint32_t*)(((unsigned char*)output) + data_size); // Encode over two passes. First split out the odd and even data, then encode the MFM values, the /4 is because we're working in longs, not bytes for (count = 0; count < data_size / 4; count++) { @@ -301,11 +301,11 @@ bool decodeSector(const RawEncodedSector& rawSector, const unsigned int trackNum unsigned char* sectorData = (unsigned char*)rawSector; // Read the first 4 bytes (8). This is the track header data - sector.headerChecksumCalculated = decodeMFMdata((unsigned long*)(sectorData + 8), (unsigned long*)§or, 4); + sector.headerChecksumCalculated = decodeMFMdata((uint32_t*)(sectorData + 8), (uint32_t*)§or, 4); // Decode the label data and update the checksum - sector.headerChecksumCalculated ^= decodeMFMdata((unsigned long*)(sectorData + 16), (unsigned long*)§or.sectorLabel[0], 16); + sector.headerChecksumCalculated ^= decodeMFMdata((uint32_t*)(sectorData + 16), (uint32_t*)§or.sectorLabel[0], 16); // Get the checksum for the header - decodeMFMdata((unsigned long*)(sectorData + 48), (unsigned long*)§or.headerChecksum, 4); // (computed on mfm longs, longs between offsets 8 and 44 == 2 * (1 + 4) longs) + decodeMFMdata((uint32_t*)(sectorData + 48), (uint32_t*)§or.headerChecksum, 4); // (computed on mfm longs, longs between offsets 8 and 44 == 2 * (1 + 4) longs) // If the header checksum fails we just cant trust anything we received, so we just drop it if ((sector.headerChecksum != sector.headerChecksumCalculated) && (!ignoreHeaderChecksum)) { return false; @@ -329,7 +329,7 @@ bool decodeSector(const RawEncodedSector& rawSector, const unsigned int trackNum if (sector.trackNumber != targetTrackNumber) return false; // this'd be weird // Get the checksum for the data - decodeMFMdata((unsigned long*)(sectorData + 56), (unsigned long*)§or.dataChecksum, 4); + decodeMFMdata((uint32_t*)(sectorData + 56), (uint32_t*)§or.dataChecksum, 4); // Lets see if we already have this one @@ -342,7 +342,7 @@ bool decodeSector(const RawEncodedSector& rawSector, const unsigned int trackNum if (index != decodedTrack.validSectors.end()) return true; // Decode the data and receive it's checksum - sector.dataChecksumCalculated = decodeMFMdata((unsigned long*)(sectorData + 64), (unsigned long*)§or.data[0], SECTOR_BYTES); // (from 64 to 1088 == 2*512 bytes) + sector.dataChecksumCalculated = decodeMFMdata((uint32_t*)(sectorData + 64), (uint32_t*)§or.data[0], SECTOR_BYTES); // (from 64 to 1088 == 2*512 bytes) // Is the data valid? if (sector.dataChecksum != sector.dataChecksumCalculated) { @@ -384,15 +384,15 @@ void encodeSector(const unsigned int trackNumber, const DiskSurface surface, con header.sectorsRemaining = NUM_SECTORS_PER_TRACK - sectorNumber; //1..11 - header.headerChecksumCalculated = encodeMFMdataPart1((const unsigned long*)&header, (unsigned long*)&encodedSector[8], 4); + header.headerChecksumCalculated = encodeMFMdataPart1((const uint32_t*)&header, (uint32_t*)&encodedSector[8], 4); // Then theres the 16 bytes of the volume label that isnt used anyway - header.headerChecksumCalculated ^= encodeMFMdataPart1((const unsigned long*)&header.sectorLabel, (unsigned long*)&encodedSector[16], 16); + header.headerChecksumCalculated ^= encodeMFMdataPart1((const uint32_t*)&header.sectorLabel, (uint32_t*)&encodedSector[16], 16); // Thats 40 bytes written as everything doubles (8+4+4+16+16). - Encode the header checksum - encodeMFMdataPart1((const unsigned long*)&header.headerChecksumCalculated, (unsigned long*)&encodedSector[48], 4); + encodeMFMdataPart1((const uint32_t*)&header.headerChecksumCalculated, (uint32_t*)&encodedSector[48], 4); // And move on to the data section. Next should be the checksum, but we cant encode that until we actually know its value! - header.dataChecksumCalculated = encodeMFMdataPart1((const unsigned long*)&input, (unsigned long*)&encodedSector[64], SECTOR_BYTES); + header.dataChecksumCalculated = encodeMFMdataPart1((const uint32_t*)&input, (uint32_t*)&encodedSector[64], SECTOR_BYTES); // And add the checksum - encodeMFMdataPart1( (const unsigned long*)&header.dataChecksumCalculated, (unsigned long*)&encodedSector[56], 4); + encodeMFMdataPart1( (const uint32_t*)&header.dataChecksumCalculated, (uint32_t*)&encodedSector[56], 4); // Now fill in the MFM clock bits bool lastBit = encodedSector[7] & (1 << 0); @@ -418,14 +418,14 @@ void encodeSector(const unsigned int trackNumber, const DiskSurface surface, con // Find sectors within raw data read from the drive by searching bit-by-bit for the SYNC bytes void findSectors(const RawTrackData& track, unsigned int trackNumber, DiskSurface side, unsigned short trackSync, DecodedTrack& decodedTrack, bool ignoreHeaderChecksum) { // Work out what we need to search for which is 2AAAAAAAsyncsync - //const unsigned long long search = (trackSync | (((DWORD)trackSync) << 16)) | (((long long)0x2AAAAAAA) << 32); + //const uint32_t long search = (trackSync | (((DWORD)trackSync) << 16)) | (((long long)0x2AAAAAAA) << 32); // For speed and to ignore some data errors, we now just search for syncsync and ignore the 2AAAAAAA part // Work out what we need to search for which is syncsync - const unsigned long search = (trackSync | (((unsigned long)trackSync) << 16)); + const uint32_t search = (trackSync | (((uint32_t)trackSync) << 16)); // Prepare our test buffer - unsigned long decoded = 0; + uint32_t decoded = 0; // Keep runnign until we run out of data unsigned int byteIndex = 0; @@ -666,7 +666,6 @@ bool ADFWriter::runDiagnostics(const std::wstring& portName, std::function start = std::chrono::steady_clock::now(); - messageOutput(false, "Testing Disk Change pin."); messageOutput(false, "*** Please remove disk from drive *** (you have 30 seconds)"); while (std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count() < 30000) { std::this_thread::sleep_for(std::chrono::milliseconds(250)); @@ -1243,7 +1242,7 @@ struct SCPFileHeader { unsigned char bitcellEncoding; unsigned char numHeads; unsigned char timeBase; - unsigned long checksum; + uint32_t checksum; }; struct SCPTrackHeader { @@ -1252,9 +1251,9 @@ struct SCPTrackHeader { }; struct SCPTrackRevolution { - unsigned long indexTime; // Time in NS/25 for this revolution - unsigned long trackLength; // Number of bit-cells in this revolution - unsigned long dataOffset; // From the start of SCPTrackHeader + uint32_t indexTime; // Time in NS/25 for this revolution + uint32_t trackLength; // Number of bit-cells in this revolution + uint32_t dataOffset; // From the start of SCPTrackHeader }; // Track data is 16-bit value in NS/25. If =0 means no flux transition for max time @@ -1324,7 +1323,7 @@ ADFResult ADFWriter::DiskToSCP(const std::wstring& outputFile, const unsigned in } // Pad out the records. Theres 4 bytes for each track - unsigned long notPresent = 0; + uint32_t notPresent = 0; for (unsigned int a = 0; a <168; a++) { try { hADFFile.write((const char*)¬Present, sizeof(notPresent)); @@ -1433,7 +1432,7 @@ ADFResult ADFWriter::DiskToSCP(const std::wstring& outputFile, const unsigned in } // Get the current position - unsigned long currentPosition = (unsigned long)hADFFile.tellp(); + uint32_t currentPosition = (uint32_t)hADFFile.tellp(); // Move to the beginning of the file, and write the offset for where this starts hADFFile.seekp(sizeof(header) + (track.header.trackNumber * 4), std::fstream::beg); @@ -1487,7 +1486,7 @@ ADFResult ADFWriter::DiskToSCP(const std::wstring& outputFile, const unsigned in while (hADFFile.good()) { try { hADFFile.read((char*)buffer, sizeof(buffer)); - const unsigned long read = sizeof(buffer) / 4; + const uint32_t read = sizeof(buffer) / 4; for (size_t pos = 0; pos < read; pos++) header.checksum += buffer[pos]; } diff --git a/ArduinoFloppyReader/lib/ArduinoInterface.cpp b/ArduinoFloppyReader/lib/ArduinoInterface.cpp index 1843172..74f8bfc 100644 --- a/ArduinoFloppyReader/lib/ArduinoInterface.cpp +++ b/ArduinoFloppyReader/lib/ArduinoInterface.cpp @@ -265,7 +265,7 @@ DiagnosticResponse ArduinoInterface::testTransferSpeed() { unsigned char buffer[256]; for (int a = 0; a <= 10; a++) { - unsigned long read; + uint32_t read; read = m_comPort.read(buffer, sizeof(buffer)); @@ -340,7 +340,7 @@ DiagnosticResponse ArduinoInterface::attemptToSync(std::string& versionString, S buffer[0] = SPECIAL_ABORT_CHAR; buffer[1] = COMMAND_VERSION; - unsigned long size = port.write(&buffer[0], 2); + uint32_t size = port.write(&buffer[0], 2); if (size != 2) { // Couldn't write to device port.closePort(); @@ -462,7 +462,7 @@ DiagnosticResponse ArduinoInterface::openPort(const std::wstring& portName, bool char buffer[2]; int counter = 0; for (;;) { - unsigned long size = m_comPort.read(buffer, 1); + uint32_t size = m_comPort.read(buffer, 1); if (size < 1) if (counter++>=10) break; } @@ -931,10 +931,10 @@ DiagnosticResponse ArduinoInterface::readRotation(RotationExtractor& extractor, for (;;) { // More efficient to read several bytes in one go - unsigned long bytesAvailable = m_comPort.getBytesWaiting(); + uint32_t bytesAvailable = m_comPort.getBytesWaiting(); if (bytesAvailable < 1) bytesAvailable = 1; if (bytesAvailable > sizeof(tempReadBuffer)) bytesAvailable = sizeof(tempReadBuffer); - unsigned long bytesRead = m_comPort.read(tempReadBuffer, m_abortSignalled ? 1 : bytesAvailable); + uint32_t bytesRead = m_comPort.read(tempReadBuffer, m_abortSignalled ? 1 : bytesAvailable); for (size_t a = 0; a < bytesRead; a++) { @@ -1327,7 +1327,7 @@ DiagnosticResponse ArduinoInterface::runCommand(const char command, const char p bool ArduinoInterface::deviceRead(void* target, const unsigned int numBytes, const bool failIfNotAllRead) { if (!m_comPort.isPortOpen()) return false; - unsigned long read = m_comPort.read(target, numBytes); + uint32_t read = m_comPort.read(target, numBytes); if (read < numBytes) { if (failIfNotAllRead) return false; diff --git a/readme.md b/readme.md index d18e1b0..09a4f77 100644 --- a/readme.md +++ b/readme.md @@ -4,6 +4,8 @@ Created by Robert Smith @RobSmithDev https://amiga.robsmithdev.co.uk +# This repo adds a GTK+ GUI for linux +To compile (requires gtkmm-3.x): make all; ./GarduinoReaderWriter # What is it? This project uses an Arduino to interface with a floppy disk drive and