aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/lib/adafruit_floppy/examples
diff options
context:
space:
mode:
Diffstat (limited to 'circuitpython/lib/adafruit_floppy/examples')
-rw-r--r--circuitpython/lib/adafruit_floppy/examples/fat_test/fat_test.ino157
-rw-r--r--circuitpython/lib/adafruit_floppy/examples/floppy_capture_track_test/floppy_capture_track_test.ino112
-rw-r--r--circuitpython/lib/adafruit_floppy/examples/greaseweazle/greaseweazle.ino525
-rw-r--r--circuitpython/lib/adafruit_floppy/examples/mfm_test/mfm_test.ino138
-rw-r--r--circuitpython/lib/adafruit_floppy/examples/msd_test/msd_test.ino163
5 files changed, 1095 insertions, 0 deletions
diff --git a/circuitpython/lib/adafruit_floppy/examples/fat_test/fat_test.ino b/circuitpython/lib/adafruit_floppy/examples/fat_test/fat_test.ino
new file mode 100644
index 0000000..f9ff9ff
--- /dev/null
+++ b/circuitpython/lib/adafruit_floppy/examples/fat_test/fat_test.ino
@@ -0,0 +1,157 @@
+/*
+ * Print size, modify date/time, and name for all files in root.
+ */
+
+/*********************************************************************
+ Adafruit invests time and resources providing this open source code,
+ please support Adafruit and open-source hardware by purchasing
+ products from Adafruit!
+*********************************************************************/
+
+#include <SPI.h>
+#include "SdFat.h"
+#include <Adafruit_Floppy.h>
+
+// If using SAMD51, turn on TINYUSB USB stack
+#if defined(ADAFRUIT_FEATHER_M4_EXPRESS)
+ #define DENSITY_PIN A0 // IDC 2
+ #define INDEX_PIN A1 // IDC 8
+ #define SELECT_PIN A2 // IDC 12
+ #define MOTOR_PIN A3 // IDC 16
+ #define DIR_PIN A4 // IDC 18
+ #define STEP_PIN A5 // IDC 20
+ #define WRDATA_PIN 13 // IDC 22 (not used during read)
+ #define WRGATE_PIN 12 // IDC 24 (not used during read)
+ #define TRK0_PIN 11 // IDC 26
+ #define PROT_PIN 10 // IDC 28
+ #define READ_PIN 9 // IDC 30
+ #define SIDE_PIN 6 // IDC 32
+ #define READY_PIN 5 // IDC 34
+#if F_CPU != 180000000L
+ #warning "please set CPU speed to 180MHz overclock"
+#endif
+#elif defined (ARDUINO_ADAFRUIT_FEATHER_RP2040)
+ #define DENSITY_PIN A0 // IDC 2
+ #define INDEX_PIN A1 // IDC 8
+ #define SELECT_PIN A2 // IDC 12
+ #define MOTOR_PIN A3 // IDC 16
+ #define DIR_PIN 24 // IDC 18
+ #define STEP_PIN 25 // IDC 20
+ #define WRDATA_PIN 13 // IDC 22 (not used during read)
+ #define WRGATE_PIN 12 // IDC 24 (not used during read)
+ #define TRK0_PIN 11 // IDC 26
+ #define PROT_PIN 10 // IDC 28
+ #define READ_PIN 9 // IDC 30
+ #define SIDE_PIN 8 // IDC 32
+ #define READY_PIN 7 // IDC 34
+#if F_CPU != 200000000L
+ #warning "please set CPU speed to 200MHz overclock"
+#endif
+#elif defined (ARDUINO_RASPBERRY_PI_PICO)
+ #define DENSITY_PIN 2 // IDC 2
+ #define INDEX_PIN 3 // IDC 8
+ #define SELECT_PIN 4 // IDC 12
+ #define MOTOR_PIN 5 // IDC 16
+ #define DIR_PIN 6 // IDC 18
+ #define STEP_PIN 7 // IDC 20
+ #define WRDATA_PIN 8 // IDC 22 (not used during read)
+ #define WRGATE_PIN 9 // IDC 24 (not used during read)
+ #define TRK0_PIN 10 // IDC 26
+ #define PROT_PIN 11 // IDC 28
+ #define READ_PIN 12 // IDC 30
+ #define SIDE_PIN 13 // IDC 32
+ #define READY_PIN 14 // IDC 34
+#if F_CPU != 200000000L
+ #warning "please set CPU speed to 200MHz overclock"
+#endif
+#else
+#error "Please set up pin definitions!"
+#endif
+
+Adafruit_Floppy floppy(DENSITY_PIN, INDEX_PIN, SELECT_PIN,
+ MOTOR_PIN, DIR_PIN, STEP_PIN,
+ WRDATA_PIN, WRGATE_PIN, TRK0_PIN,
+ PROT_PIN, READ_PIN, SIDE_PIN, READY_PIN);
+Adafruit_MFM_Floppy mfm_floppy(&floppy);
+
+// file system object from SdFat
+FatFileSystem fatfs;
+
+FatFile root;
+FatFile file;
+
+//------------------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Wait for USB Serial
+ while (!Serial) {
+ SysCall::yield();
+ }
+
+ Serial.println("Floppy FAT directory listing demo");
+
+ // Init floppy drive - must spin up and find index
+ if (! mfm_floppy.begin()) {
+ Serial.println("Floppy didn't initialize - check wiring and diskette!");
+ }
+
+ // Init file system on the flash
+ fatfs.begin(&mfm_floppy);
+
+ if (!root.open("/")) {
+ Serial.println("open root failed");
+ }
+ // Open next file in root.
+ // Warning, openNext starts at the current directory position
+ // so a rewind of the directory may be required.
+ while (file.openNext(&root, O_RDONLY)) {
+ file.printFileSize(&Serial);
+ Serial.write(' ');
+ file.printModifyDateTime(&Serial);
+ Serial.write(' ');
+ file.printName(&Serial);
+ if (file.isDir()) {
+ // Indicate a directory.
+ Serial.write('/');
+ }
+ Serial.println();
+ file.close();
+ }
+
+ if (root.getError()) {
+ Serial.println("openNext failed");
+ } else {
+ Serial.println("Done!");
+ }
+}
+//------------------------------------------------------------------------------
+void loop() {
+ Serial.print("Read a file? >");
+ String filename;
+ do {
+ filename = Serial.readStringUntil('\n');
+ filename.trim();
+ } while (filename.length() == 0);
+
+ Serial.print("Reading file name: ");
+ Serial.println(filename);
+
+ // Open the file for reading and check that it was successfully opened.
+ // The FILE_READ mode will open the file for reading.
+ File dataFile = fatfs.open(filename, FILE_READ);
+ if (!dataFile) {
+ Serial.println("Failed to open data file! Does it exist?");
+ return;
+ }
+ // File was opened, now print out data character by character until at the
+ // end of the file.
+ Serial.println("Opened file, printing contents below:");
+ while (dataFile.available()) {
+ // Use the read function to read the next character.
+ // You can alternatively use other functions like readUntil, readString, etc.
+ // See the fatfs_full_usage example for more details.
+ char c = dataFile.read();
+ Serial.print(c);
+ }
+}
diff --git a/circuitpython/lib/adafruit_floppy/examples/floppy_capture_track_test/floppy_capture_track_test.ino b/circuitpython/lib/adafruit_floppy/examples/floppy_capture_track_test/floppy_capture_track_test.ino
new file mode 100644
index 0000000..2861b0c
--- /dev/null
+++ b/circuitpython/lib/adafruit_floppy/examples/floppy_capture_track_test/floppy_capture_track_test.ino
@@ -0,0 +1,112 @@
+#include <Adafruit_Floppy.h>
+
+// If using SAMD51, turn on TINYUSB USB stack
+#if defined(ADAFRUIT_FEATHER_M4_EXPRESS)
+ #define DENSITY_PIN A0 // IDC 2
+ #define INDEX_PIN A1 // IDC 8
+ #define SELECT_PIN A2 // IDC 12
+ #define MOTOR_PIN A3 // IDC 16
+ #define DIR_PIN A4 // IDC 18
+ #define STEP_PIN A5 // IDC 20
+ #define WRDATA_PIN 13 // IDC 22 (not used during read)
+ #define WRGATE_PIN 12 // IDC 24 (not used during read)
+ #define TRK0_PIN 11 // IDC 26
+ #define PROT_PIN 10 // IDC 28
+ #define READ_PIN 9 // IDC 30
+ #define SIDE_PIN 6 // IDC 32
+ #define READY_PIN 5 // IDC 34
+#if F_CPU != 180000000L
+ #warning "please set CPU speed to 180MHz overclock"
+#endif
+#elif defined (ARDUINO_ADAFRUIT_FEATHER_RP2040)
+ #define DENSITY_PIN A0 // IDC 2
+ #define INDEX_PIN A1 // IDC 8
+ #define SELECT_PIN A2 // IDC 12
+ #define MOTOR_PIN A3 // IDC 16
+ #define DIR_PIN 24 // IDC 18
+ #define STEP_PIN 25 // IDC 20
+ #define WRDATA_PIN 13 // IDC 22 (not used during read)
+ #define WRGATE_PIN 12 // IDC 24 (not used during read)
+ #define TRK0_PIN 11 // IDC 26
+ #define PROT_PIN 10 // IDC 28
+ #define READ_PIN 9 // IDC 30
+ #define SIDE_PIN 6 // IDC 32
+ #define READY_PIN 5 // IDC 34
+#if F_CPU != 200000000L
+ #warning "please set CPU speed to 200MHz overclock"
+#endif
+#elif defined (ARDUINO_RASPBERRY_PI_PICO)
+ #define DENSITY_PIN 2 // IDC 2
+ #define INDEX_PIN 3 // IDC 8
+ #define SELECT_PIN 4 // IDC 12
+ #define MOTOR_PIN 5 // IDC 16
+ #define DIR_PIN 6 // IDC 18
+ #define STEP_PIN 7 // IDC 20
+ #define WRDATA_PIN 8 // IDC 22 (not used during read)
+ #define WRGATE_PIN 9 // IDC 24 (not used during read)
+ #define TRK0_PIN 10 // IDC 26
+ #define PROT_PIN 11 // IDC 28
+ #define READ_PIN 12 // IDC 30
+ #define SIDE_PIN 13 // IDC 32
+ #define READY_PIN 14 // IDC 34
+#if F_CPU != 200000000L
+ #warning "please set CPU speed to 200MHz overclock"
+#endif
+#else
+#error "Please set up pin definitions!"
+#endif
+
+Adafruit_Floppy floppy(DENSITY_PIN, INDEX_PIN, SELECT_PIN,
+ MOTOR_PIN, DIR_PIN, STEP_PIN,
+ WRDATA_PIN, WRGATE_PIN, TRK0_PIN,
+ PROT_PIN, READ_PIN, SIDE_PIN, READY_PIN);
+
+// WARNING! there are 150K max flux pulses per track!
+uint8_t flux_transitions[MAX_FLUX_PULSE_PER_TRACK];
+
+uint32_t time_stamp = 0;
+
+
+void setup() {
+ Serial.begin(115200);
+ while (!Serial) delay(100);
+
+ Serial.println("its time for a nice floppy transfer!");
+ floppy.debug_serial = &Serial;
+ floppy.begin();
+
+ floppy.select(true);
+ if (! floppy.spin_motor(true)) {
+ Serial.println("Failed to spin up motor & find index pulse");
+ while (1) yield();
+ }
+
+ Serial.print("Seeking track...");
+ if (! floppy.goto_track(0)) {
+ Serial.println("Failed to seek to track");
+ while (1) yield();
+ }
+ Serial.println("done!");
+}
+
+void loop() {
+ uint32_t captured_flux = floppy.capture_track(flux_transitions, sizeof(flux_transitions));
+
+ Serial.print("Captured ");
+ Serial.print(captured_flux);
+ Serial.println(" flux transitions");
+
+ //floppy.print_pulses(flux_transitions, captured_flux);
+ floppy.print_pulse_bins(flux_transitions, captured_flux, 255);
+
+ if ((millis() - time_stamp) > 1000) {
+ Serial.print("Ready? ");
+ Serial.println(digitalRead(READY_PIN) ? "No" : "Yes");
+ Serial.print("Write Protected? ");
+ Serial.println(digitalRead(PROT_PIN) ? "No" : "Yes");
+ Serial.print("Track 0? ");
+ Serial.println(digitalRead(TRK0_PIN) ? "No" : "Yes");
+ time_stamp = millis();
+ }
+ yield();
+}
diff --git a/circuitpython/lib/adafruit_floppy/examples/greaseweazle/greaseweazle.ino b/circuitpython/lib/adafruit_floppy/examples/greaseweazle/greaseweazle.ino
new file mode 100644
index 0000000..5ed7a4b
--- /dev/null
+++ b/circuitpython/lib/adafruit_floppy/examples/greaseweazle/greaseweazle.ino
@@ -0,0 +1,525 @@
+#include <Adafruit_Floppy.h>
+
+#if defined(ADAFRUIT_FEATHER_M4_EXPRESS)
+ #define DENSITY_PIN A0 // IDC 2
+ #define INDEX_PIN A1 // IDC 8
+ #define SELECT_PIN A2 // IDC 12
+ #define MOTOR_PIN A3 // IDC 16
+ #define DIR_PIN A4 // IDC 18
+ #define STEP_PIN A5 // IDC 20
+ #define WRDATA_PIN 13 // IDC 22
+ #define WRGATE_PIN 12 // IDC 24
+ #define TRK0_PIN 11 // IDC 26
+ #define PROT_PIN 10 // IDC 28
+ #define READ_PIN 9 // IDC 30
+ #define SIDE_PIN 6 // IDC 32
+ #define READY_PIN 5 // IDC 34
+#if F_CPU != 180000000L
+ #warning "please set CPU speed to 180MHz overclock"
+#endif
+ #define GW_SAMPLEFREQ (F_CPU * 11/90) // samd51 is sample rate of 22MHz at 180MHz OC
+#elif defined (ARDUINO_ADAFRUIT_FEATHER_RP2040)
+ #define DENSITY_PIN A0 // IDC 2
+ #define INDEX_PIN A1 // IDC 8
+ #define SELECT_PIN A2 // IDC 12
+ #define MOTOR_PIN A3 // IDC 16
+ #define DIR_PIN 24 // IDC 18
+ #define STEP_PIN 25 // IDC 20
+ #define WRDATA_PIN 13 // IDC 22
+ #define WRGATE_PIN 12 // IDC 24
+ #define TRK0_PIN 11 // IDC 26
+ #define PROT_PIN 10 // IDC 28
+ #define READ_PIN 9 // IDC 30
+ #define SIDE_PIN 8 // IDC 32
+ #define READY_PIN 7 // IDC 34
+#if F_CPU != 200000000L
+ #warning "please set CPU speed to 200MHz overclock"
+#endif
+ #define GW_SAMPLEFREQ 26000000UL // 26mhz for rp2040
+#elif defined (ARDUINO_RASPBERRY_PI_PICO)
+ #define DENSITY_PIN 2 // IDC 2
+ #define INDEX_PIN 3 // IDC 8
+ #define SELECT_PIN 4 // IDC 12
+ #define MOTOR_PIN 5 // IDC 16
+ #define DIR_PIN 6 // IDC 18
+ #define STEP_PIN 7 // IDC 20
+ #define WRDATA_PIN 8 // IDC 22 (not used during read)
+ #define WRGATE_PIN 9 // IDC 24 (not used during read)
+ #define TRK0_PIN 10 // IDC 26
+ #define PROT_PIN 11 // IDC 28
+ #define READ_PIN 12 // IDC 30
+ #define SIDE_PIN 13 // IDC 32
+ #define READY_PIN 14 // IDC 34
+#if F_CPU != 200000000L
+ #warning "please set CPU speed to 200MHz overclock"
+#endif
+ #define GW_SAMPLEFREQ 26000000UL // 26mhz for rp2040
+#else
+#error "Please set up pin definitions!"
+#endif
+
+Adafruit_Floppy floppy(DENSITY_PIN, INDEX_PIN, SELECT_PIN,
+ MOTOR_PIN, DIR_PIN, STEP_PIN,
+ WRDATA_PIN, WRGATE_PIN, TRK0_PIN,
+ PROT_PIN, READ_PIN, SIDE_PIN, READY_PIN);
+
+uint32_t time_stamp = 0;
+
+uint8_t cmd_buffer[32], reply_buffer[128];
+uint8_t cmd_buff_idx = 0;
+
+#define GW_FIRMVER_MAJOR 1
+#define GW_FIRMVER_MINOR 0
+#define GW_MAXCMD 21
+#define GW_HW_MODEL 8 // Adafruity
+#define GW_HW_SUBMODEL 0 // Adafruit Floppy Generic
+#define GW_USB_SPEED 0 // Full Speed
+
+#define GW_CMD_GETINFO 0
+#define GW_CMD_GETINFO_FIRMWARE 0
+#define GW_CMD_GETINFO_BANDWIDTH 1
+#define GW_CMD_SEEK 2
+#define GW_CMD_HEAD 3
+#define GW_CMD_SETPARAMS 4
+#define GW_CMD_GETPARAMS 5
+#define GW_CMD_GETPARAMS_DELAYS 0
+#define GW_CMD_MOTOR 6
+#define GW_CMD_READFLUX 7
+#define GW_CMD_GETFLUXSTATUS 9
+#define GW_CMD_SELECT 12
+#define GW_CMD_DESELECT 13
+#define GW_CMD_SETBUSTYPE 14
+#define GW_CMD_SETBUSTYPE_IBM 1
+#define GW_CMD_SETBUSTYPE_SHUGART 2
+#define GW_CMD_SETPIN 15
+#define GW_CMD_RESET 16
+#define GW_CMD_SOURCEBYTES 18
+#define GW_CMD_SINKBYTES 19
+#define GW_CMD_GETPIN 20
+
+#define GW_ACK_OK (byte)0
+#define GW_ACK_BADCMD 1
+#define GW_ACK_NOINDEX 2
+#define GW_ACK_NOTRACK0 3
+#define GW_ACK_NOUNIT 7
+
+uint32_t timestamp = 0;
+
+void setup() {
+ Serial.begin(115200);
+ Serial1.begin(115200);
+ //while (!Serial) delay(100);
+ Serial1.println("GrizzlyWizzly");
+
+ floppy.debug_serial = &Serial1;
+ floppy.begin();
+ timestamp = millis();
+}
+
+uint8_t get_cmd(uint8_t *buff, uint8_t maxbuff) {
+ int i=0;
+
+ if (Serial.available() < 2) return 0;
+ buff[i++] = Serial.read();
+ buff[i++] = Serial.read();
+ // wait for remaining data
+ while (Serial.available() < (buff[1] - 2)) {
+ delay(1);
+ yield();
+ }
+ for (; i<buff[1]; i++) {
+ buff[i] = Serial.read();
+ }
+ return i;
+}
+
+uint32_t bandwidth_timer;
+float bytes_per_sec;
+uint32_t transfered_bytes;
+uint32_t captured_pulses;
+// WARNING! there are 100K max flux pulses per track!
+uint8_t flux_transitions[MAX_FLUX_PULSE_PER_TRACK];
+bool motor_state = false; // we can cache whether the motor is spinning
+
+
+void loop() {
+ uint8_t cmd_len = get_cmd(cmd_buffer, sizeof(cmd_buffer));
+ if (!cmd_len) {
+ if ((millis() > timestamp) && ((millis()-timestamp) > 3000)) {
+ Serial1.println("Timed out waiting for command, resetting motor");
+ floppy.goto_track(0);
+ floppy.spin_motor(false);
+ motor_state = false;
+ floppy.select(false);
+ timestamp = millis();
+ }
+ return;
+ }
+ timestamp = millis();
+
+ int i = 0;
+ uint8_t cmd = cmd_buffer[0];
+ memset(reply_buffer, 0, sizeof(reply_buffer));
+ reply_buffer[i++] = cmd; // echo back the cmd itself
+
+ Serial1.printf("Got command 0x%02x\n\r", cmd);
+
+ if (cmd == GW_CMD_GETINFO) {
+ Serial1.println("Get info");
+ uint8_t sub_cmd = cmd_buffer[2];
+ if (sub_cmd == GW_CMD_GETINFO_FIRMWARE) {
+ reply_buffer[i++] = GW_ACK_OK;
+ reply_buffer[i++] = GW_FIRMVER_MAJOR; // 1 byte
+ reply_buffer[i++] = GW_FIRMVER_MINOR; // 1 byte
+ reply_buffer[i++] = 1; // is main firm
+ reply_buffer[i++] = GW_MAXCMD;
+ reply_buffer[i++] = GW_SAMPLEFREQ & 0xFF;
+ reply_buffer[i++] = (GW_SAMPLEFREQ >> 8) & 0xFF;
+ reply_buffer[i++] = (GW_SAMPLEFREQ >> 16) & 0xFF;
+ reply_buffer[i++] = (GW_SAMPLEFREQ >> 24) & 0xFF;
+ reply_buffer[i++] = GW_HW_MODEL;
+ reply_buffer[i++] = GW_HW_SUBMODEL;
+ reply_buffer[i++] = GW_USB_SPEED;
+ Serial.write(reply_buffer, 34);
+ }
+ else if (sub_cmd == GW_CMD_GETINFO_BANDWIDTH) {
+ reply_buffer[i++] = GW_ACK_OK;
+ uint32_t min_bytes = transfered_bytes;
+ uint32_t max_bytes = transfered_bytes;
+ uint32_t min_usec = bandwidth_timer * 1000;
+ uint32_t max_usec = bandwidth_timer * 1000;
+ // TODO What is this math supposed to be??
+
+ reply_buffer[i++] = min_bytes & 0xFF;
+ reply_buffer[i++] = min_bytes >> 8;
+ reply_buffer[i++] = min_bytes >> 16;
+ reply_buffer[i++] = min_bytes >> 24;
+ reply_buffer[i++] = min_usec & 0xFF;
+ reply_buffer[i++] = min_usec >> 8;
+ reply_buffer[i++] = min_usec >> 16;
+ reply_buffer[i++] = min_usec >> 24;
+ reply_buffer[i++] = max_bytes & 0xFF;
+ reply_buffer[i++] = max_bytes >> 8;
+ reply_buffer[i++] = max_bytes >> 16;
+ reply_buffer[i++] = max_bytes >> 24;
+ reply_buffer[i++] = max_usec & 0xFF;
+ reply_buffer[i++] = max_usec >> 8;
+ reply_buffer[i++] = max_usec >> 16;
+ reply_buffer[i++] = max_usec >> 24;
+
+ // TODO more?
+ Serial.write(reply_buffer, 34);
+ }
+ }
+
+ else if (cmd == GW_CMD_GETPARAMS) {
+ Serial1.println("Get params");
+ uint8_t sub_cmd = cmd_buffer[2];
+ if (sub_cmd == GW_CMD_GETPARAMS_DELAYS) {
+ reply_buffer[i++] = GW_ACK_OK;
+ reply_buffer[i++] = floppy.select_delay_us & 0xFF;
+ reply_buffer[i++] = floppy.select_delay_us >> 8;
+ reply_buffer[i++] = floppy.step_delay_us & 0xFF;
+ reply_buffer[i++] = floppy.step_delay_us >> 8;
+ reply_buffer[i++] = floppy.settle_delay_ms & 0xFF;
+ reply_buffer[i++] = floppy.settle_delay_ms >> 8;
+ reply_buffer[i++] = floppy.motor_delay_ms & 0xFF;
+ reply_buffer[i++] = floppy.motor_delay_ms >> 8;
+ reply_buffer[i++] = floppy.watchdog_delay_ms & 0xFF;
+ reply_buffer[i++] = floppy.watchdog_delay_ms >> 8;
+ Serial.write(reply_buffer, 12);
+ }
+ }
+
+ else if (cmd == GW_CMD_RESET) {
+ Serial1.println("Soft reset");
+ floppy.soft_reset();
+ reply_buffer[i++] = GW_ACK_OK;
+ Serial.write(reply_buffer, 2);
+ }
+
+ else if (cmd == GW_CMD_SETBUSTYPE) {
+ uint8_t bustype = cmd_buffer[2];
+ Serial1.printf("Set bus type %d\n\r", bustype);
+ // TODO: whats the diff???
+ if (bustype == GW_CMD_SETBUSTYPE_IBM) {
+ reply_buffer[i++] = GW_ACK_OK;
+ }
+ else if (bustype == GW_CMD_SETBUSTYPE_SHUGART) {
+ floppy.bus_type = BUSTYPE_SHUGART;
+ reply_buffer[i++] = GW_ACK_OK;
+ } else {
+ reply_buffer[i++] = GW_ACK_BADCMD;
+ }
+ Serial.write(reply_buffer, 2);
+ }
+
+ else if (cmd == GW_CMD_SEEK) {
+ uint8_t track = cmd_buffer[2];
+ Serial1.printf("Seek track %d\n\r", track);
+ bool r = floppy.goto_track(track);
+ if (r) {
+ reply_buffer[i++] = GW_ACK_OK;
+ } else {
+ reply_buffer[i++] = GW_ACK_NOTRACK0;
+ }
+ Serial.write(reply_buffer, 2);
+ }
+
+ else if (cmd == GW_CMD_HEAD) {
+ uint8_t head = cmd_buffer[2];
+ Serial1.printf("Seek head %d\n\r", head);
+ floppy.side(head);
+ reply_buffer[i++] = GW_ACK_OK;
+ Serial.write(reply_buffer, 2);
+ }
+
+ else if (cmd == GW_CMD_MOTOR) {
+ uint8_t unit = cmd_buffer[2];
+ uint8_t state = cmd_buffer[3];
+ Serial1.printf("Turn motor %d %s\n\r", unit, state ? "on" : "off");
+ if (motor_state != state) { // we're in the opposite state
+ if (! floppy.spin_motor(state)) {
+ reply_buffer[i++] = GW_ACK_NOINDEX;
+ } else {
+ reply_buffer[i++] = GW_ACK_OK;
+ }
+ motor_state = state;
+ } else {
+ // our cached state is correct!
+ reply_buffer[i++] = GW_ACK_OK;
+ }
+ Serial.write(reply_buffer, 2);
+ }
+
+ else if (cmd == GW_CMD_SELECT) {
+ uint8_t sub_cmd = cmd_buffer[2];
+ Serial1.printf("Select drive %d\n\r", sub_cmd);
+ if (sub_cmd == 0) {
+ floppy.select(true);
+ reply_buffer[i++] = GW_ACK_OK;
+ } else {
+ reply_buffer[i++] = GW_ACK_NOUNIT;
+ }
+ Serial.write(reply_buffer, 2);
+ }
+
+ else if (cmd == GW_CMD_DESELECT) {
+ Serial1.printf("Deselect drive\n\r");
+ floppy.select(false);
+ reply_buffer[i++] = GW_ACK_OK;
+ Serial.write(reply_buffer, 2);
+ }
+
+ else if (cmd == GW_CMD_READFLUX) {
+ uint32_t flux_ticks;
+ uint16_t revs;
+ flux_ticks = cmd_buffer[5];
+ flux_ticks <<= 8;
+ flux_ticks |= cmd_buffer[4];
+ flux_ticks <<= 8;
+ flux_ticks |= cmd_buffer[3];
+ flux_ticks <<= 8;
+ flux_ticks |= cmd_buffer[2];
+ revs = cmd_buffer[7];
+ revs <<= 8;
+ revs |= cmd_buffer[6];
+ revs -= 1;
+
+ if (floppy.track() == -1) {
+ floppy.goto_track(0);
+ }
+
+ Serial1.printf("Reading flux0rs on track %d: %u ticks and %d revs\n\r", floppy.track(), flux_ticks, revs);
+ reply_buffer[i++] = GW_ACK_OK;
+ Serial.write(reply_buffer, 2);
+ while (revs--) {
+ captured_pulses = floppy.capture_track(flux_transitions, sizeof(flux_transitions));
+ Serial1.printf("Rev #%d captured %u pulses\n\r", revs, captured_pulses);
+ //floppy.print_pulse_bins(flux_transitions, captured_pulses, 64, Serial1);
+ // trim down extra long pulses
+ for (uint32_t f=0; f<captured_pulses; f++) {
+ if (flux_transitions[f] > 250) {
+ flux_transitions[f] = 250;
+ }
+ }
+ // Send the index opcode, which is right at the start of this data xfer
+ reply_buffer[0] = 0xFF;
+ reply_buffer[1] = 1; // index opcode
+ reply_buffer[2] = 0x1;
+ reply_buffer[3] = 0x1;
+ reply_buffer[4] = 0x1;
+ reply_buffer[5] = 0x1; // 0 are special, so we send 1 to == 0
+ Serial.write(reply_buffer, 6);
+
+ uint8_t *flux_ptr = flux_transitions;
+ while (captured_pulses) {
+ uint32_t to_send = min(captured_pulses, (uint32_t)256);
+ Serial.write(flux_ptr, to_send);
+ //Serial1.println(to_send);
+ flux_ptr += to_send;
+ captured_pulses -= to_send;
+ }
+ }
+
+ // send a final indexop
+ reply_buffer[0] = 0xFF;
+ reply_buffer[1] = 1; // index opcode
+ reply_buffer[2] = 0x1;
+ reply_buffer[3] = 0x1;
+ reply_buffer[4] = 0x1;
+ reply_buffer[5] = 0x1; // 0 are special, so we send 1 to == 0
+ Serial.write(reply_buffer, 6);
+
+ // flush input, to account for fluxengine bug
+ while (Serial.available()) Serial.read();
+ Serial.write((byte)0);
+ }
+
+ else if (cmd == GW_CMD_GETFLUXSTATUS) {
+ Serial1.println("get flux status");
+ reply_buffer[i++] = GW_ACK_OK;
+ Serial.write(reply_buffer, 2);
+ }
+
+
+ else if (cmd == GW_CMD_SINKBYTES) {
+ uint32_t numbytes = 0;
+ uint32_t seed = 0;
+ numbytes |= cmd_buffer[5];
+ numbytes <<= 8;
+ numbytes |= cmd_buffer[4];
+ numbytes <<= 8;
+ numbytes |= cmd_buffer[3];
+ numbytes <<= 8;
+ numbytes |= cmd_buffer[2];
+ Serial1.printf("sink numbytes %d\n\r", numbytes);
+
+ seed |= cmd_buffer[9];
+ seed <<= 8;
+ seed |= cmd_buffer[8];
+ seed <<= 8;
+ seed |= cmd_buffer[7];
+ seed <<= 8;
+ seed |= cmd_buffer[6];
+ reply_buffer[i++] = GW_ACK_OK;
+ Serial.write(reply_buffer, 2);
+ yield();
+ bandwidth_timer = millis();
+ transfered_bytes = numbytes;
+ bytes_per_sec = numbytes;
+
+ while (numbytes != 0) {
+ uint32_t avail = Serial.available();
+ if (avail == 0) {
+ //Serial1.print("-");
+ yield();
+ continue;
+ }
+ //Serial1.printf("%lu avail, ", avail);
+ uint32_t to_read = min(numbytes, min((uint32_t)sizeof(reply_buffer), avail));
+ //Serial1.printf("%lu to read, ", to_read);
+ numbytes -= Serial.readBytes((char *)reply_buffer, to_read);
+ //Serial1.printf("%lu remain\n\r", numbytes);
+ }
+ bandwidth_timer = millis() - bandwidth_timer;
+ bytes_per_sec /= bandwidth_timer;
+ bytes_per_sec *= 1000;
+ Serial1.print("Done in ");
+ Serial1.print(bandwidth_timer);
+ Serial1.print(" ms, ");
+ Serial1.print(bytes_per_sec);
+ Serial1.println(" bytes per sec");
+ Serial.write(GW_ACK_OK);
+ yield();
+ }
+ else if (cmd == GW_CMD_SOURCEBYTES) {
+ uint32_t numbytes = 0;
+ uint32_t seed = 0;
+ numbytes |= cmd_buffer[5];
+ numbytes <<= 8;
+ numbytes |= cmd_buffer[4];
+ numbytes <<= 8;
+ numbytes |= cmd_buffer[3];
+ numbytes <<= 8;
+ numbytes |= cmd_buffer[2];
+ Serial1.printf("source numbytes %d\n\r", numbytes);
+
+ seed |= cmd_buffer[9];
+ seed <<= 8;
+ seed |= cmd_buffer[8];
+ seed <<= 8;
+ seed |= cmd_buffer[7];
+ seed <<= 8;
+ seed |= cmd_buffer[6];
+ reply_buffer[i++] = GW_ACK_OK;
+ Serial.write(reply_buffer, 2);
+ yield();
+ bandwidth_timer = millis();
+ bytes_per_sec = numbytes;
+ transfered_bytes = numbytes;
+
+ uint32_t randnum = seed;
+ while (numbytes != 0) {
+ uint32_t to_write = min(numbytes, sizeof(reply_buffer));
+ // we dont write 'just anything'!
+ for (uint32_t i=0; i<to_write; i++) {
+ reply_buffer[i] = randnum;
+ if (randnum & 0x01) {
+ randnum = (randnum >> 1) ^ 0x80000062;
+ } else {
+ randnum >>= 1;
+ }
+ }
+ numbytes -= Serial.write(reply_buffer, to_write);
+ }
+ bandwidth_timer = millis() - bandwidth_timer;
+ bytes_per_sec /= bandwidth_timer;
+ bytes_per_sec *= 1000;
+ Serial1.print("Done in ");
+ Serial1.print(bandwidth_timer);
+ Serial1.print(" ms, ");
+ Serial1.print(bytes_per_sec);
+ Serial1.println(" bytes per sec");
+ } else if (cmd == GW_CMD_GETPIN) {
+ uint32_t pin = cmd_buffer[2];
+ Serial1.printf("getpin %d\n\r", pin);
+
+ switch(pin) {
+ case 26:
+ reply_buffer[i++] = GW_ACK_OK;
+ reply_buffer[i++] = digitalRead(TRK0_PIN);
+ break;
+
+ default:
+ // unknown pin, don't pretend we did it right
+ reply_buffer[i++] = GW_ACK_BADCMD;
+ reply_buffer[i++] = 0;
+ }
+ Serial.write(reply_buffer, i);
+ } else if (cmd == GW_CMD_SETPIN) {
+ uint32_t pin = cmd_buffer[2];
+ bool value = cmd_buffer[3];
+ Serial1.printf("setpin %d to \n\r", pin, value);
+
+ switch(pin) {
+ case 2:
+ pinMode(DENSITY_PIN, OUTPUT);
+ digitalWrite(DENSITY_PIN, value);
+ reply_buffer[i++] = GW_ACK_OK;
+ break;
+
+ default:
+ // unknown pin, don't pretend we did it right
+ reply_buffer[i++] = GW_ACK_BADCMD;
+ }
+
+ Serial.write(reply_buffer, i);
+
+ /********** unknown ! ********/
+ } else {
+ reply_buffer[i++] = GW_ACK_BADCMD;
+ Serial.write(reply_buffer, 2);
+ }
+ //Serial1.println("cmd complete!");
+} \ No newline at end of file
diff --git a/circuitpython/lib/adafruit_floppy/examples/mfm_test/mfm_test.ino b/circuitpython/lib/adafruit_floppy/examples/mfm_test/mfm_test.ino
new file mode 100644
index 0000000..07206f1
--- /dev/null
+++ b/circuitpython/lib/adafruit_floppy/examples/mfm_test/mfm_test.ino
@@ -0,0 +1,138 @@
+#include <Adafruit_Floppy.h>
+
+
+
+// If using SAMD51, turn on TINYUSB USB stack
+#if defined(ADAFRUIT_FEATHER_M4_EXPRESS)
+ #define DENSITY_PIN A0 // IDC 2
+ #define INDEX_PIN A1 // IDC 8
+ #define SELECT_PIN A2 // IDC 12
+ #define MOTOR_PIN A3 // IDC 16
+ #define DIR_PIN A4 // IDC 18
+ #define STEP_PIN A5 // IDC 20
+ #define WRDATA_PIN 13 // IDC 22 (not used during read)
+ #define WRGATE_PIN 12 // IDC 24 (not used during read)
+ #define TRK0_PIN 11 // IDC 26
+ #define PROT_PIN 10 // IDC 28
+ #define READ_PIN 9 // IDC 30
+ #define SIDE_PIN 6 // IDC 32
+ #define READY_PIN 5 // IDC 34
+#if F_CPU != 180000000L
+ #warning "please set CPU speed to 180MHz overclock"
+#endif
+#elif defined (ARDUINO_ADAFRUIT_FEATHER_RP2040)
+ #define DENSITY_PIN A0 // IDC 2
+ #define INDEX_PIN A1 // IDC 8
+ #define SELECT_PIN A2 // IDC 12
+ #define MOTOR_PIN A3 // IDC 16
+ #define DIR_PIN 24 // IDC 18
+ #define STEP_PIN 25 // IDC 20
+ #define WRDATA_PIN 13 // IDC 22 (not used during read)
+ #define WRGATE_PIN 12 // IDC 24 (not used during read)
+ #define TRK0_PIN 11 // IDC 26
+ #define PROT_PIN 10 // IDC 28
+ #define READ_PIN 9 // IDC 30
+ #define SIDE_PIN 8 // IDC 32
+ #define READY_PIN 7 // IDC 34
+#if F_CPU != 200000000L
+ #warning "please set CPU speed to 200MHz overclock"
+#endif
+#elif defined (ARDUINO_RASPBERRY_PI_PICO)
+ #define DENSITY_PIN 2 // IDC 2
+ #define INDEX_PIN 3 // IDC 8
+ #define SELECT_PIN 4 // IDC 12
+ #define MOTOR_PIN 5 // IDC 16
+ #define DIR_PIN 6 // IDC 18
+ #define STEP_PIN 7 // IDC 20
+ #define WRDATA_PIN 8 // IDC 22 (not used during read)
+ #define WRGATE_PIN 9 // IDC 24 (not used during read)
+ #define TRK0_PIN 10 // IDC 26
+ #define PROT_PIN 11 // IDC 28
+ #define READ_PIN 12 // IDC 30
+ #define SIDE_PIN 13 // IDC 32
+ #define READY_PIN 14 // IDC 34
+#if F_CPU != 200000000L
+ #warning "please set CPU speed to 200MHz overclock"
+#endif
+#else
+#error "Please set up pin definitions!"
+#endif
+
+Adafruit_Floppy floppy(DENSITY_PIN, INDEX_PIN, SELECT_PIN,
+ MOTOR_PIN, DIR_PIN, STEP_PIN,
+ WRDATA_PIN, WRGATE_PIN, TRK0_PIN,
+ PROT_PIN, READ_PIN, SIDE_PIN, READY_PIN);
+
+// You can select IBMPC1440K or IBMPC360K (check adafruit_floppy_disk_t options!)
+Adafruit_MFM_Floppy mfm_floppy(&floppy, IBMPC360K);
+
+
+uint32_t time_stamp = 0;
+
+void setup() {
+ pinMode(LED_BUILTIN, OUTPUT);
+ Serial.begin(115200);
+ while (!Serial) delay(100);
+
+ delay(500); // wait for serial to open
+ Serial.println("its time for a nice floppy transfer!");
+
+ floppy.debug_serial = &Serial;
+
+ if (! mfm_floppy.begin()) {
+ Serial.println("Failed to spin up motor & find index pulse");
+ while (1) yield();
+ }
+}
+
+uint8_t track = 0;
+bool head = 0;
+void loop() {
+ int32_t captured_sectors;
+
+ Serial.printf("Seeking track %d head %d\n", track, head);
+ captured_sectors = mfm_floppy.readTrack(track, head);
+ if (captured_sectors < 0) {
+ Serial.println("Failed to seek to track");
+ while (1) yield();
+ }
+
+ Serial.printf("Captured %d sectors\n", captured_sectors);
+
+ Serial.print("Validity: ");
+ for(size_t i=0; i < mfm_floppy.sectors_per_track(); i++) {
+ Serial.print(mfm_floppy.track_validity[i] ? "V" : "?");
+ }
+ Serial.print("\n");
+ for(size_t sector=0; sector < mfm_floppy.sectors_per_track(); sector++) {
+ if (!mfm_floppy.track_validity[sector]) {
+ continue; // skip it, not valid
+ }
+ for(size_t i=0; i<512; i+=16) {
+ size_t addr = sector * 512 + i;
+ Serial.printf("%08x", addr);
+ for(size_t j=0; j<16; j++) {
+ Serial.printf(" %02x", mfm_floppy.track_data[addr+j]);
+ }
+ Serial.print(" | ");
+ for(size_t j=0; j<16; j++) {
+ uint8_t d = mfm_floppy.track_data[addr+j];
+ if (! isprint(d)) {
+ d = ' ';
+ }
+ Serial.write(d);
+ }
+ Serial.print("\n");
+ }
+ }
+
+ // advance to next track
+ if (!head) { // we were on side 0
+ head = 1; // go to side 1
+ } else { // we were on side 1?
+ track = (track + 1) % mfm_floppy.tracks_per_side(); // next track!
+ head = 0; // and side 0
+ }
+
+ delay(1000);
+} \ No newline at end of file
diff --git a/circuitpython/lib/adafruit_floppy/examples/msd_test/msd_test.ino b/circuitpython/lib/adafruit_floppy/examples/msd_test/msd_test.ino
new file mode 100644
index 0000000..ab407cd
--- /dev/null
+++ b/circuitpython/lib/adafruit_floppy/examples/msd_test/msd_test.ino
@@ -0,0 +1,163 @@
+// this example makes a lot of assumptions: MFM floppy which is already inserted
+// and only reading is supported - no write yet!
+
+#include <Adafruit_Floppy.h>
+#include "Adafruit_TinyUSB.h"
+
+Adafruit_USBD_MSC usb_msc;
+
+// If using SAMD51, turn on TINYUSB USB stack
+#if defined(ADAFRUIT_FEATHER_M4_EXPRESS)
+ #define DENSITY_PIN A0 // IDC 2
+ #define INDEX_PIN A1 // IDC 8
+ #define SELECT_PIN A2 // IDC 12
+ #define MOTOR_PIN A3 // IDC 16
+ #define DIR_PIN A4 // IDC 18
+ #define STEP_PIN A5 // IDC 20
+ #define WRDATA_PIN 13 // IDC 22 (not used during read)
+ #define WRGATE_PIN 12 // IDC 24 (not used during read)
+ #define TRK0_PIN 11 // IDC 26
+ #define PROT_PIN 10 // IDC 28
+ #define READ_PIN 9 // IDC 30
+ #define SIDE_PIN 6 // IDC 32
+ #define READY_PIN 5 // IDC 34
+#if F_CPU != 180000000L
+ #warning "please set CPU speed to 180MHz overclock"
+#endif
+#elif defined (ARDUINO_ADAFRUIT_FEATHER_RP2040)
+ #define DENSITY_PIN A0 // IDC 2
+ #define INDEX_PIN A1 // IDC 8
+ #define SELECT_PIN A2 // IDC 12
+ #define MOTOR_PIN A3 // IDC 16
+ #define DIR_PIN 24 // IDC 18
+ #define STEP_PIN 25 // IDC 20
+ #define WRDATA_PIN 13 // IDC 22 (not used during read)
+ #define WRGATE_PIN 12 // IDC 24 (not used during read)
+ #define TRK0_PIN 11 // IDC 26
+ #define PROT_PIN 10 // IDC 28
+ #define READ_PIN 9 // IDC 30
+ #define SIDE_PIN 8 // IDC 32
+ #define READY_PIN 7 // IDC 34
+#if F_CPU != 200000000L
+ #warning "please set CPU speed to 200MHz overclock"
+#endif
+#elif defined (ARDUINO_RASPBERRY_PI_PICO)
+ #define DENSITY_PIN 2 // IDC 2
+ #define INDEX_PIN 3 // IDC 8
+ #define SELECT_PIN 4 // IDC 12
+ #define MOTOR_PIN 5 // IDC 16
+ #define DIR_PIN 6 // IDC 18
+ #define STEP_PIN 7 // IDC 20
+ #define WRDATA_PIN 8 // IDC 22 (not used during read)
+ #define WRGATE_PIN 9 // IDC 24 (not used during read)
+ #define TRK0_PIN 10 // IDC 26
+ #define PROT_PIN 11 // IDC 28
+ #define READ_PIN 12 // IDC 30
+ #define SIDE_PIN 13 // IDC 32
+ #define READY_PIN 14 // IDC 34
+#if F_CPU != 200000000L
+ #warning "please set CPU speed to 200MHz overclock"
+#endif
+#else
+#error "Please set up pin definitions!"
+#endif
+
+Adafruit_Floppy floppy(DENSITY_PIN, INDEX_PIN, SELECT_PIN,
+ MOTOR_PIN, DIR_PIN, STEP_PIN,
+ WRDATA_PIN, WRGATE_PIN, TRK0_PIN,
+ PROT_PIN, READ_PIN, SIDE_PIN, READY_PIN);
+
+// You can select IBMPC1440K or IBMPC360K (check adafruit_floppy_disk_t options!)
+Adafruit_MFM_Floppy mfm_floppy(&floppy, IBMPC1440K);
+
+
+constexpr size_t SECTOR_SIZE = 512UL;
+int8_t last_track_read = -1; // last cached track
+
+void setup() {
+ pinMode(LED_BUILTIN, OUTPUT);
+ Serial.begin(115200);
+
+#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
+ // Manual begin() is required on core without built-in support for TinyUSB such as
+ // - mbed rp2040
+ TinyUSB_Device_Init(0);
+#endif
+
+ // Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively
+ usb_msc.setID("Adafruit", "Floppy Mass Storage", "1.0");
+
+ // Set disk size
+ usb_msc.setCapacity(mfm_floppy.sectors_per_track() * mfm_floppy.tracks_per_side() * FLOPPY_HEADS, SECTOR_SIZE);
+
+ // Set callback
+ usb_msc.setReadWriteCallback(msc_read_callback, msc_write_callback, msc_flush_callback);
+
+ floppy.debug_serial = &Serial;
+ floppy.begin();
+ // Set Lun ready
+ usb_msc.setUnitReady(true);
+ Serial.println("Ready!");
+
+ usb_msc.begin();
+
+ if (! mfm_floppy.begin()) {
+ Serial.println("Failed to spin up motor & find index pulse");
+ while (1) yield();
+ }
+}
+
+void loop() {
+ delay(1000);
+}
+
+// Callback invoked when received READ10 command.
+// Copy disk's data to buffer (up to bufsize) and
+// return number of copied bytes (must be multiple of block size)
+int32_t msc_read_callback (uint32_t lba, void* buffer, uint32_t bufsize)
+{
+ Serial.printf("read call back block %d size %d\n", lba, bufsize);
+
+ uint8_t track = lba / (2 * mfm_floppy.sectors_per_track());
+ uint8_t head = (lba / mfm_floppy.sectors_per_track()) % 2;
+ uint8_t subsector = lba % mfm_floppy.sectors_per_track();
+
+ uint8_t retries = 5;
+
+ for (int retry=0; retry<retries; retry++) {
+ if (((track * 2 + head) == last_track_read) && mfm_floppy.track_validity[subsector]) {
+ // aah we've got it and its valid!
+ Serial.println("OK!");
+ memcpy(buffer, mfm_floppy.track_data+(subsector * SECTOR_SIZE), SECTOR_SIZE);
+ return SECTOR_SIZE;
+ }
+ // ok so either its not valid, or we didn't read this track yet...
+ int32_t tracks_read = mfm_floppy.readTrack(track, head);
+ if (tracks_read < 0) {
+ Serial.println("Failed to seek to track");
+ return 0;
+ }
+ last_track_read = track * 2 + head;
+ // we'll go again on the next round
+ }
+ Serial.println("subsector invalid CRC :(");
+ return 0;
+}
+
+// Callback invoked when received WRITE10 command.
+// Process data in buffer to disk's storage and
+// return number of written bytes (must be multiple of block size)
+int32_t msc_write_callback (uint32_t lba, uint8_t* buffer, uint32_t bufsize)
+{
+ Serial.printf("write call back block %d size %d\n", lba, bufsize);
+ // we dont actually write yet
+ return bufsize;
+}
+
+// Callback invoked when WRITE10 command is completed (status received and accepted by host).
+// used to flush any pending cache.
+void msc_flush_callback (void)
+{
+ Serial.println("flush\n");
+ // nothing to do
+} \ No newline at end of file