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 @@
+
+
+
+
+
+
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