Skip to content

Commit

Permalink
msd: quality of life improvements (WIP)
Browse files Browse the repository at this point in the history
* Use the TFT on the Floppsy to show some I/O states, track & side #,
a "dirty" indicator, and a throbber
* add a timeout to flux capturing waiting for the index pulse
* handle disk removal/insertion about as well as I can
* make a step towards autodetection (just defining an enum value for it)
* allow re-writing a full track even if it had errors when read
  * this still has problems related to rewriting "sector zero", because
    it is flushed immediately right now
  * this is basically a low level format
  • Loading branch information
jepler committed Mar 7, 2024
1 parent b68c5a2 commit ef76615
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 42 deletions.
131 changes: 111 additions & 20 deletions examples/04_msd_test/04_msd_test.ino
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@
#define USE_GFX (1)
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
Adafruit_ST7789 tft = Adafruit_ST7789(&SPI1, TFT_CS, TFT_DC, TFT_RESET);
#define TFT_INIT() do { \
tft.init(240, 240); \
pinMode(TFT_BACKLIGHT, OUTPUT); \
digitalWrite(TFT_BACKLIGHT, 1); \
tft.fillScreen(0); \
tft.setTextSize(3); \
tft.setTextColor(~0, 0); \
tft.setCursor(3, 3); \
tft.println("TFTinit!"); \
Serial.println("TFTinit!"); \
} while(0)
#else
#error "Please set up pin definitions!"
#endif
Expand All @@ -59,10 +70,6 @@ Adafruit_ST7789 tft = Adafruit_ST7789(&SPI1, TFT_CS, TFT_DC, TFT_RESET);
#error "Please set Adafruit TinyUSB under Tools > USB Stack"
#endif

#if !defined(USE_GFX)
#define USE_GFX (0)
#endif

#if USE_GFX
#include "Adafruit_GFX.h"
#define IF_GFX(...) do { __VA_ARGS__ } while(0)
Expand All @@ -82,10 +89,9 @@ 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);
TFT_INIT();
Serial.begin(115200);

#if defined(FLOPPY_DIRECTION_PIN)
Expand All @@ -103,38 +109,106 @@ void setup() {
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.setCapacity(0, SECTOR_SIZE);
// Set callbacks
usb_msc.setReadyCallback(0, msc_ready_callback);
usb_msc.setReadWriteCallback(msc_read_callback, msc_write_callback, msc_flush_callback);

floppy.debug_serial = &Serial;
floppy.begin();
// floppy.debug_serial = &Serial;
// Set Lun ready
usb_msc.setUnitReady(true);
usb_msc.setUnitReady(false);
Serial.println("Ready!");

tft.init(240, 240);
digitalWrite(TFT_BACKLIGHT, 1);
usb_msc.begin();

Serial.println("serial Ready!");

floppy.begin();
if (! mfm_floppy.begin()) {
Serial.println("Failed to spin up motor & find index pulse");
while (1) yield();
}

IF_GFX(tft.fillScreen(0););
}

uint32_t index_time;
bool index_ui_state;

#if USE_GFX
const char spinner[4] = {' ', '.', 'o', 'O'};
size_t i_spin;

void update_display() {
noInterrupts();
auto trk0 = floppy.get_track0_sense();
auto wp = floppy.get_write_protect();
auto ind = digitalRead(INDEX_PIN);
auto rdy = digitalRead(READY_PIN);
auto dirty = mfm_floppy.dirty();
int x = 3;
int y = 3;
tft.setCursor(x, y);
tft.printf("%s %s %s",
trk0 ? "TRK0" : " ",
wp ? "R/O" : " ",
rdy ? "RDY" : index_ui_state ? "IDX" : " "
);

y += 24;
tft.setCursor(x, y);
if (floppy.track() == -1) {
tft.printf("T:?? S:?");
} else {
tft.printf("T:%02d S:%d",
floppy.track(),
floppy.get_side()
);
}
y += 24;
tft.setCursor(x, y);
tft.printf("%s %c",
dirty ? "dirty" : " ", spinner[i_spin++ % std::size(spinner)]);
interrupts();
}
#else
void update_display() {
}
#endif

bool index_delayed, ready_delayed;
void loop() {
delay(1000);
uint32_t now = millis();
bool index = !digitalRead(INDEX_PIN);
bool ready = digitalRead(READY_PIN);

// ready pin fell: media ejected
if (!ready && ready_delayed) {
Serial.println("removed");
mfm_floppy.removed();
}
if (index && !index_delayed) {
index_time = now;
if (mfm_floppy.sectorCount() == 0) {
Serial.println("inserted");
mfm_floppy.inserted();
}
}

index_ui_state = (now - index_time) < 400;
index_delayed = index;
ready_delayed = ready;

update_display();
}

// 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);
//Serial.printf("read call back block %d size %d\r\n", lba, bufsize);
auto result = mfm_floppy.readSectors(lba, reinterpret_cast<uint8_t*>(buffer), bufsize / MFM_BYTES_PER_SECTOR);
update_display();
return result ? bufsize : -1;
}

Expand All @@ -143,16 +217,33 @@ int32_t msc_read_callback (uint32_t lba, void* buffer, uint32_t bufsize)
// 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);
auto result = mfm_floppy.writeSectors(lba, buffer, bufsize / MFM_BYTES_PER_SECTOR);
//Serial.printf("write call back block %d size %d\r\n", lba, bufsize);
auto sectors = bufsize / MFM_BYTES_PER_SECTOR;
auto result = mfm_floppy.writeSectors(lba, buffer, sectors);
if (result) {
if (lba == 0 || (lba + sectors) == mfm_floppy.sectorCount()) {
// If writing the first or last sector,
mfm_floppy.syncDevice();
}
}
update_display();
return result ? bufsize : -1;
}

// 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");
Serial.print("flush\r\n");
mfm_floppy.syncDevice();
update_display();
// nothing to do
}

bool msc_ready_callback (void)
{
//Serial.printf("ready callback -> %d\r\n", mfm_floppy.sectorCount());
auto sectors = mfm_floppy.sectorCount();
usb_msc.setCapacity(sectors, SECTOR_SIZE);
return sectors != 0;
}
7 changes: 5 additions & 2 deletions src/Adafruit_Floppy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ bool Adafruit_Floppy::spin_motor(bool motor_on) {
timedout = true; // its been a second
break;
}
yield();
}

if (timedout) {
Expand Down Expand Up @@ -567,13 +568,15 @@ size_t Adafruit_FloppyBase::capture_track(volatile uint8_t *pulses,
size_t max_pulses,
int32_t *falling_index_offset,
bool store_greaseweazle,
uint32_t capture_ms) {
uint32_t capture_ms,
uint32_t index_wait_ms) {
memset((void *)pulses, 0, max_pulses); // zero zem out

#if defined(ARDUINO_ARCH_RP2040)
return rp2040_flux_capture(_indexpin, _rddatapin, pulses, pulses + max_pulses,
falling_index_offset, store_greaseweazle,
capture_ms * (getSampleFrequency() / 1000));
capture_ms * (getSampleFrequency() / 1000),
index_wait_ms);
#elif defined(__SAMD51__)
noInterrupts();
wait_for_index_pulse_low();
Expand Down
38 changes: 31 additions & 7 deletions src/Adafruit_Floppy.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
#define FLOPPY_IBMPC_DD_TRACKS 40
#define FLOPPY_HEADS 2

#define MFM_IBMPC1200K_SECTORS_PER_TRACK 15
#define MFM_IBMPC1440K_SECTORS_PER_TRACK 18
#define MFM_IBMPC360K_SECTORS_PER_TRACK 9
#define MFM_IBMPC720K_SECTORS_PER_TRACK 9
#define MFM_BYTES_PER_SECTOR 512UL

#define STEP_OUT HIGH
Expand All @@ -39,8 +41,14 @@
#define BUSTYPE_SHUGART 2

typedef enum {
IBMPC1440K,
IBMPC360K,
IBMPC720K,
IBMPC720K_360RPM,
IBMPC1200K,
IBMPC1440K,
IBMPC1440K_360RPM,
FAILED_AUTODETECT,
AUTODETECT,
} adafruit_floppy_disk_t;

/**************************************************************************/
Expand Down Expand Up @@ -145,7 +153,8 @@ class Adafruit_FloppyBase {

size_t capture_track(volatile uint8_t *pulses, size_t max_pulses,
int32_t *falling_index_offset,
bool store_greaseweazle = false, uint32_t capture_ms = 0)
bool store_greaseweazle = false, uint32_t capture_ms = 0,
uint32_t index_wait_ms = 250)
__attribute__((optimize("O3")));

bool write_track(uint8_t *pulses, size_t n_pulses,
Expand Down Expand Up @@ -288,6 +297,12 @@ class Adafruit_Apple2Floppy : public Adafruit_FloppyBase {
void _step(int dir, int times);
};

struct adafruit_floppy_format_info_t {
uint8_t cylinders, heads;
uint16_t bit_time_ns;
uint16_t track_time_ms;
};

/**************************************************************************/
/*!
This class adds support for the BaseBlockDriver interface to an MFM
Expand All @@ -298,20 +313,29 @@ class Adafruit_Apple2Floppy : public Adafruit_FloppyBase {
class Adafruit_MFM_Floppy : public FsBlockDeviceInterface {
public:
Adafruit_MFM_Floppy(Adafruit_Floppy *floppy,
adafruit_floppy_disk_t format = IBMPC1440K);
adafruit_floppy_disk_t format = AUTODETECT);

bool begin(void);
void end(void);

uint32_t size(void);
adafruit_floppy_disk_t format() const { return _format; }
const adafruit_floppy_format_info_t *format_info() const;

uint32_t size(void) const;
int32_t readTrack(uint8_t track, bool head);


/**! @brief The expected number of sectors per track in this format
@returns The number of sectors per track */
uint8_t sectors_per_track(void) { return _sectors_per_track; }
uint8_t sectors_per_track(void) const { return _sectors_per_track; }
/**! @brief The expected number of tracks per side in this format
@returns The number of tracks per side */
uint8_t tracks_per_side(void) { return _tracks_per_side; }
uint8_t tracks_per_side(void) const { return _tracks_per_side; }

bool dirty() const { return _dirty; }

void removed();
void inserted();

//------------- SdFat v2 FsBlockDeviceInterface API -------------//
virtual bool isBusy();
Expand Down Expand Up @@ -340,7 +364,7 @@ class Adafruit_MFM_Floppy : public FsBlockDeviceInterface {
bool _high_density = true;
bool _dirty = false, _track_has_errors = false;
Adafruit_Floppy *_floppy = NULL;
adafruit_floppy_disk_t _format = IBMPC1440K;
adafruit_floppy_disk_t _format = AUTODETECT;

/**! The raw flux data from the last track read */
uint8_t _flux[125000];
Expand Down
Loading

0 comments on commit ef76615

Please sign in to comment.