Adafruit Library
Adafruit_Floppy.h
1 #ifndef ADAFRUIT_FLOPPY_H
2 #define ADAFRUIT_FLOPPY_H
3 
18 #include "Arduino.h"
19 #include <Adafruit_SPIDevice.h>
20 // to implement SdFat Block Driver
21 //
22 #define DISABLE_FS_H_WARNING
23 #include "SdFat.h"
24 #include "SdFatConfig.h"
25 
26 #define FLOPPY_IBMPC_HD_TRACKS 80
27 #define FLOPPY_IBMPC_DD_TRACKS 40
28 #define FLOPPY_HEADS 2
29 
30 #define MFM_IBMPC1200K_SECTORS_PER_TRACK 15
31 #define MFM_IBMPC1440K_SECTORS_PER_TRACK 18
32 #define MFM_IBMPC360K_SECTORS_PER_TRACK 9
33 #define MFM_IBMPC720K_SECTORS_PER_TRACK 9
34 #define MFM_BYTES_PER_SECTOR 512UL
35 
36 #define STEP_OUT HIGH
37 #define STEP_IN LOW
38 #define MAX_FLUX_PULSE_PER_TRACK \
39  (uint32_t)(500000UL / 5 * \
40  1.5) // 500khz / 5 hz per track rotation, 1.5 rotations
41 
42 #define BUSTYPE_IBMPC 1
43 #define BUSTYPE_SHUGART 2
44 
45 typedef enum {
46  IBMPC360K,
47  IBMPC720K,
48  IBMPC720K_360RPM,
49  IBMPC1200K,
50  IBMPC1440K,
51  IBMPC1440K_360RPM,
52  AUTODETECT,
53 } adafruit_floppy_disk_t;
54 
55 /**************************************************************************/
59 /**************************************************************************/
61 protected:
62  Adafruit_FloppyBase(int indexpin, int wrdatapin, int wrgatepin, int rddatapin,
63  bool is_apple2 = false);
64 
65 public:
66  bool begin(void);
67  virtual void end();
68 
69  virtual void soft_reset(void);
70 
71  /**************************************************************************/
76  /**************************************************************************/
77  virtual void select(bool selected) = 0;
78  /**************************************************************************/
83  /**************************************************************************/
84  bool drive_is_selected(void) { return is_drive_selected; }
85 
86  /**************************************************************************/
94  /**************************************************************************/
95  virtual bool spin_motor(bool motor_on) = 0;
96  /**************************************************************************/
101  /**************************************************************************/
102  bool motor_is_spinning(void) { return is_motor_spinning; }
103  /**************************************************************************/
108  /**************************************************************************/
109  bool index_pulses_seen(void) { return is_index_seen; }
110  bool index_pulses_missing(void) const;
111  bool has_index(void) const;
112  /**************************************************************************/
118  /**************************************************************************/
119  virtual bool goto_track(int track_num) = 0;
120  /**************************************************************************/
126  /**************************************************************************/
127  virtual bool side(int head) = 0;
128  /**************************************************************************/
133  /**************************************************************************/
134  virtual int get_side() = 0;
135  /**************************************************************************/
141  /**************************************************************************/
142  virtual int track(void) = 0;
143  /**************************************************************************/
148  /**************************************************************************/
149  virtual bool get_write_protect() = 0;
150 
151  /**************************************************************************/
158  /**************************************************************************/
159  virtual bool get_track0_sense() = 0;
160 
161  /**************************************************************************/
167  /**************************************************************************/
168  virtual bool get_ready_sense() = 0;
169 
170  /**************************************************************************/
176  /**************************************************************************/
177  virtual bool set_density(bool high_density) = 0;
178 
179  size_t decode_track_mfm(uint8_t *sectors, size_t n_sectors,
180  uint8_t *sector_validity, const uint8_t *pulses,
181  size_t n_pulses, float nominal_bit_time_us,
182  bool clear_validity = false,
183  uint8_t *logical_track = nullptr,
184  int expected_track = -1);
185 
186  size_t encode_track_mfm(const uint8_t *sectors, size_t n_sectors,
187  uint8_t *pulses, size_t max_pulses,
188  float nominal_bit_time_us, uint8_t logical_track);
189 
190  size_t capture_track(volatile uint8_t *pulses, size_t max_pulses,
191  int32_t *falling_index_offset,
192  bool store_greaseweazle = false, uint32_t capture_ms = 0,
193  uint32_t index_wait_ms = 250)
194  __attribute__((optimize("O3")));
195 
196  bool write_track(uint8_t *pulses, size_t n_pulses,
197  bool store_greaseweazle = false, bool use_index = true)
198  __attribute__((optimize("O3")));
199  static const uint32_t MAX_ERASE_US = 1000000;
200  bool erase_track(uint32_t duration_us);
201  void print_pulse_bins(uint8_t *pulses, size_t n_pulses, uint8_t max_bins = 64,
202  bool is_gw_format = false, uint32_t min_bin_size = 100);
203  void print_pulses(uint8_t *pulses, size_t n_pulses,
204  bool is_gw_format = false);
205  uint32_t getSampleFrequency(void);
206 
207 #if defined(LED_BUILTIN)
208  int8_t led_pin = LED_BUILTIN;
209 #else
210  int8_t led_pin = -1;
211 #endif
212 
213  uint16_t select_delay_us = 10;
214  uint16_t step_delay_us = 10000;
215  uint16_t settle_delay_ms = 15;
216  uint16_t motor_delay_ms = 1000;
217  uint16_t watchdog_delay_ms =
218  1000;
219  uint8_t bus_type = BUSTYPE_IBMPC;
220 
221  Stream *debug_serial = nullptr;
222 
223 protected:
224  bool read_index();
229 
230 private:
231 #if defined(__SAMD51__)
232  void deinit_capture(void);
233  void enable_capture(void);
234 
235  bool init_generate(void);
236  void deinit_generate(void);
237  void enable_generate(void);
238  void disable_generate(void);
239 #endif
240 
241  bool start_polled_capture(void);
242  void disable_capture(void);
243 
244  bool init_capture(void);
245  void enable_background_capture(void);
246  void wait_for_index_pulse_low(void);
247 
248  int8_t _indexpin, _wrdatapin, _wrgatepin, _rddatapin;
249  bool _is_apple2;
250 
251 #ifdef BUSIO_USE_FAST_PINIO
252  BusIO_PortReg *indexPort;
253  BusIO_PortMask indexMask;
254  uint32_t dummyPort = 1;
255 #endif
256 };
257 
258 /**************************************************************************/
262 /**************************************************************************/
264 public:
265  Adafruit_Floppy(int8_t densitypin, int8_t indexpin, int8_t selectpin,
266  int8_t motorpin, int8_t directionpin, int8_t steppin,
267  int8_t wrdatapin, int8_t wrgatepin, int8_t track0pin,
268  int8_t protectpin, int8_t rddatapin, int8_t sidepin,
269  int8_t readypin);
270  void end() override;
271  void soft_reset(void) override;
272 
273  void select(bool selected) override;
274  bool spin_motor(bool motor_on) override;
275  bool goto_track(int track) override;
276  bool side(int head) override;
277  int track(void) override;
278  int get_side(void) override;
279  void step(bool dir, uint8_t times);
280  bool set_density(bool high_density) override;
281  bool get_write_protect() override;
282  bool get_track0_sense() override;
283  bool get_ready_sense() override;
284 
285 private:
286  // theres a lot of GPIO!
287  int8_t _densitypin, _selectpin, _motorpin, _directionpin, _steppin,
288  _track0pin, _protectpin, _sidepin, _readypin;
289 
290  int _track = -1, _side = -1;
291 };
292 
293 /**************************************************************************/
297 /**************************************************************************/
299 public:
300  /**************************************************************************/
304  /**************************************************************************/
305  enum StepMode {
306  STEP_MODE_WHOLE, //< One step moves by one data track
307  STEP_MODE_HALF, //< Two steps move by one data track
308  STEP_MODE_QUARTER, //< Four steps move by one data track
309  };
310 
311  Adafruit_Apple2Floppy(int8_t indexpin, int8_t selectpin, int8_t phase1pin,
312  int8_t phase2pin, int8_t phase3pin, int8_t phase4pin,
313  int8_t wrdatapin, int8_t wrgatepin, int8_t protectpin,
314  int8_t rddatapin);
315  void end() override;
316  void soft_reset(void) override;
317 
318  void select(bool selected) override;
319  bool spin_motor(bool motor_on) override;
320  bool goto_track(int track) override;
321  bool side(int head) override;
322  int track(void) override;
323  bool set_density(bool high_density) override;
324  bool get_write_protect() override;
325  bool get_track0_sense() override;
326  bool get_ready_sense() override { return true; }
327 
328  int quartertrack();
329  bool goto_quartertrack(int);
330  void step_mode(StepMode mode);
331 
332  int get_side() override { return 0; }
333 
334 private:
335  int _step_multiplier() const;
336  // theres not much GPIO!
337  int8_t _selectpin, _phase1pin, _phase2pin, _phase3pin, _phase4pin,
338  _protectpin;
339  int _quartertrack = -1;
340  StepMode _step_mode = STEP_MODE_HALF;
341  void _step(int dir, int times);
342 };
343 
344 /**************************************************************************/
350 /**************************************************************************/
351 class Adafruit_MFM_Floppy : public FsBlockDeviceInterface {
352 public:
354  adafruit_floppy_disk_t format = AUTODETECT);
355 
356  bool begin(void);
357  void end(void);
358 
359  uint32_t size(void) const;
360  int32_t readTrack(int track, bool head);
361 
364  uint8_t sectors_per_track(void) const { return _sectors_per_track; }
367  uint8_t tracks_per_side(void) const { return _tracks_per_side; }
368 
371  bool dirty() const { return _dirty; }
372 
374  void removed();
380  bool inserted(adafruit_floppy_disk_t format);
381 
382  //------------- SdFat v2 FsBlockDeviceInterface API -------------//
383  virtual bool isBusy();
384  virtual uint32_t sectorCount();
385  virtual bool syncDevice();
386 
387  virtual bool readSector(uint32_t block, uint8_t *dst);
388  virtual bool readSectors(uint32_t block, uint8_t *dst, size_t ns);
389  virtual bool writeSector(uint32_t block, const uint8_t *src);
390  virtual bool writeSectors(uint32_t block, const uint8_t *src, size_t ns);
391 
393  uint8_t track_data[MFM_IBMPC1440K_SECTORS_PER_TRACK * MFM_BYTES_PER_SECTOR];
394 
396  uint8_t track_validity[MFM_IBMPC1440K_SECTORS_PER_TRACK];
397 
398 private:
399  bool autodetect();
400 #if defined(PICO_BOARD) || defined(__RP2040__) || defined(ARDUINO_ARCH_RP2040)
401  uint16_t _last;
402 #endif
403  static constexpr uint8_t NO_TRACK = UINT8_MAX;
404  uint8_t _sectors_per_track = 0;
405  uint8_t _tracks_per_side = 0;
406  uint8_t _last_track_read = NO_TRACK; // last cached track
407  uint16_t _bit_time_ns;
408  bool _high_density = true;
409  bool _dirty = false, _track_has_errors = false;
410  bool _double_step = false;
411  Adafruit_Floppy *_floppy = nullptr;
412  adafruit_floppy_disk_t _format = AUTODETECT;
413 
415  uint8_t _flux[125000];
416  size_t _n_flux;
417 };
418 
419 #endif
static const uint32_t MAX_ERASE_US
Maximum erase time, in us.
Definition: Adafruit_Floppy.h:199
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, uint32_t index_wait_ms=250) __attribute__((optimize("O3")))
Capture flux transitions for one revolution or a bounded duration.
Definition: Adafruit_Floppy.cpp:657
bool drive_is_selected(void)
Is the drive selected based on interal caching.
Definition: Adafruit_Floppy.h:84
bool write_track(uint8_t *pulses, size_t n_pulses, bool store_greaseweazle=false, bool use_index=true) __attribute__((optimize("O3")))
Write one track of flux pulse data, starting at the index pulse.
Definition: Adafruit_Floppy.cpp:829
bool motor_is_spinning(void)
Is the drive motor spinning based on interal caching.
Definition: Adafruit_Floppy.h:102
bool erase_track(uint32_t duration_us)
Erase the selected track without waiting for INDEX.
Definition: Adafruit_Floppy.cpp:947
uint8_t sectors_per_track(void) const
The expected number of sectors per track in this format.
Definition: Adafruit_Floppy.h:364
void print_pulses(uint8_t *pulses, size_t n_pulses, bool is_gw_format=false)
Pretty print the counts in a list of flux transitions.
Definition: Adafruit_Floppy.cpp:1003
bool is_index_seen
cached index pulses seen state
Definition: Adafruit_Floppy.h:227
StepMode
Constants for use with the step_mode method.
Definition: Adafruit_Floppy.h:305
bool is_index_missing
configured INDEX timed out during motor startup
Definition: Adafruit_Floppy.h:228
uint8_t tracks_per_side(void) const
The expected number of tracks per side in this format.
Definition: Adafruit_Floppy.h:367
bool get_ready_sense() override
Check whether the ready output is active.
Definition: Adafruit_Floppy.h:326
bool read_index()
Poll the status of the index pulse.
Definition: Adafruit_Floppy.cpp:229
virtual bool goto_track(int track_num)=0
Seek to the desired track, requires the motor to be spun up!
int get_side() override
Current head in use, based on internal caching.
Definition: Adafruit_Floppy.h:332
size_t decode_track_mfm(uint8_t *sectors, size_t n_sectors, uint8_t *sector_validity, const uint8_t *pulses, size_t n_pulses, float nominal_bit_time_us, bool clear_validity=false, uint8_t *logical_track=nullptr, int expected_track=-1)
Decode one track of previously captured MFM data.
Definition: Adafruit_Floppy.cpp:554
virtual bool spin_motor(bool motor_on)=0
Turn the motor on or off. Starting waits for settling and INDEX, or just settling when indexpin=-1...
uint16_t watchdog_delay_ms
quiescent time until drives reset (msecs)
Definition: Adafruit_Floppy.h:217
virtual int track(void)=0
The current track location, based on internal caching.
An abstract base class for chattin with floppy drives.
Definition: Adafruit_Floppy.h:60
bool dirty() const
Check if there is data to be written to the current track.
Definition: Adafruit_Floppy.h:371
virtual bool set_density(bool high_density)=0
Set the density for flux reading and writing.
virtual void soft_reset(void)
Initializes the GPIO pins but do not start the motor or anything.
Definition: Adafruit_Floppy.cpp:118
uint8_t bus_type
what kind of floppy drive we&#39;re using
Definition: Adafruit_Floppy.h:219
virtual bool get_write_protect()=0
Check whether the floppy in the drive is write protected.
size_t encode_track_mfm(const uint8_t *sectors, size_t n_sectors, uint8_t *pulses, size_t max_pulses, float nominal_bit_time_us, uint8_t logical_track)
Encode one track of previously captured MFM data.
Definition: Adafruit_Floppy.cpp:594
int8_t led_pin
Debug LED output for tracing.
Definition: Adafruit_Floppy.h:210
uint16_t select_delay_us
delay after drive select (usecs)
Definition: Adafruit_Floppy.h:213
bool is_motor_spinning
cached motor spinning state
Definition: Adafruit_Floppy.h:226
virtual bool get_track0_sense()=0
Check whether the track0 sensor is active.
virtual void select(bool selected)=0
Whether to select this drive.
uint16_t step_delay_us
delay between head steps (usecs)
Definition: Adafruit_Floppy.h:214
bool index_pulses_missing(void) const
Whether startup timed out waiting for a configured INDEX input.
Definition: Adafruit_Floppy.cpp:263
Adafruit_FloppyBase(int indexpin, int wrdatapin, int wrgatepin, int rddatapin, bool is_apple2=false)
Create a hardware interface to a floppy drive.
Definition: Adafruit_Floppy.cpp:47
void print_pulse_bins(uint8_t *pulses, size_t n_pulses, uint8_t max_bins=64, bool is_gw_format=false, uint32_t min_bin_size=100)
Pretty print a simple histogram of flux transitions.
Definition: Adafruit_Floppy.cpp:1037
virtual bool get_ready_sense()=0
Check whether the ready output is active.
Definition: Adafruit_Floppy.h:351
A helper class for chattin with PC & Shugart floppy drives.
Definition: Adafruit_Floppy.h:263
virtual bool side(int head)=0
Which head/side to read from.
bool is_drive_selected
cached drive select state
Definition: Adafruit_Floppy.h:225
uint32_t getSampleFrequency(void)
Get the sample rate that we read and emit pulses at, platform and implementation-dependant.
Definition: Adafruit_Floppy.cpp:624
uint16_t motor_delay_ms
delay after motor on (msecs)
Definition: Adafruit_Floppy.h:216
Stream * debug_serial
optional debug stream for serial output
Definition: Adafruit_Floppy.h:221
bool index_pulses_seen(void)
Are index pulses being seen?
Definition: Adafruit_Floppy.h:109
virtual int get_side()=0
Current head in use, based on internal caching.
virtual void end()
Disables floppy communication, allowing pins to be used for general input and output.
Definition: Adafruit_Floppy.cpp:168
A helper class for chattin with Apple 2 floppy drives.
Definition: Adafruit_Floppy.h:298
uint16_t settle_delay_ms
settle delay after seek (msecs)
Definition: Adafruit_Floppy.h:215
bool begin(void)
Initializes the GPIO pins but do not start the motor or anything.
Definition: Adafruit_Floppy.cpp:98
bool has_index(void) const
Whether a physical INDEX input was configured.
Definition: Adafruit_Floppy.cpp:249