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 #include "SdFat.h"
22 #include "SdFatConfig.h"
23 
24 #define FLOPPY_IBMPC_HD_TRACKS 80
25 #define FLOPPY_IBMPC_DD_TRACKS 40
26 #define FLOPPY_HEADS 2
27 
28 #define MFM_IBMPC1200K_SECTORS_PER_TRACK 15
29 #define MFM_IBMPC1440K_SECTORS_PER_TRACK 18
30 #define MFM_IBMPC360K_SECTORS_PER_TRACK 9
31 #define MFM_IBMPC720K_SECTORS_PER_TRACK 9
32 #define MFM_BYTES_PER_SECTOR 512UL
33 
34 #define STEP_OUT HIGH
35 #define STEP_IN LOW
36 #define MAX_FLUX_PULSE_PER_TRACK \
37  (uint32_t)(500000UL / 5 * \
38  1.5) // 500khz / 5 hz per track rotation, 1.5 rotations
39 
40 #define BUSTYPE_IBMPC 1
41 #define BUSTYPE_SHUGART 2
42 
43 typedef enum {
44  IBMPC360K,
45  IBMPC720K,
46  IBMPC720K_360RPM,
47  IBMPC1200K,
48  IBMPC1440K,
49  IBMPC1440K_360RPM,
50  AUTODETECT,
51 } adafruit_floppy_disk_t;
52 
53 /**************************************************************************/
57 /**************************************************************************/
59 protected:
60  Adafruit_FloppyBase(int indexpin, int wrdatapin, int wrgatepin, int rddatapin,
61  bool is_apple2 = false);
62 
63 public:
64  bool begin(void);
65  virtual void end();
66 
67  virtual void soft_reset(void);
68 
69  /**************************************************************************/
74  /**************************************************************************/
75  virtual void select(bool selected) = 0;
76  /**************************************************************************/
84  /**************************************************************************/
85  virtual bool spin_motor(bool motor_on) = 0;
86  /**************************************************************************/
92  /**************************************************************************/
93  virtual bool goto_track(uint8_t track_num) = 0;
94  /**************************************************************************/
100  /**************************************************************************/
101  virtual bool side(uint8_t head) = 0;
102  /**************************************************************************/
107  /**************************************************************************/
108  virtual int8_t get_side() = 0;
109  /**************************************************************************/
115  /**************************************************************************/
116  virtual int8_t track(void) = 0;
117  /**************************************************************************/
122  /**************************************************************************/
123  virtual bool get_write_protect() = 0;
124 
125  /**************************************************************************/
132  /**************************************************************************/
133  virtual bool get_track0_sense() = 0;
134 
135  /**************************************************************************/
141  /**************************************************************************/
142  virtual bool get_ready_sense() = 0;
143 
144  /**************************************************************************/
150  /**************************************************************************/
151  virtual bool set_density(bool high_density) = 0;
152 
153  size_t decode_track_mfm(uint8_t *sectors, size_t n_sectors,
154  uint8_t *sector_validity, const uint8_t *pulses,
155  size_t n_pulses, float nominal_bit_time_us,
156  bool clear_validity = false,
157  uint8_t *logical_track = nullptr);
158 
159  size_t encode_track_mfm(const uint8_t *sectors, size_t n_sectors,
160  uint8_t *pulses, size_t max_pulses,
161  float nominal_bit_time_us, uint8_t logical_track);
162 
163  size_t capture_track(volatile uint8_t *pulses, size_t max_pulses,
164  int32_t *falling_index_offset,
165  bool store_greaseweazle = false, uint32_t capture_ms = 0,
166  uint32_t index_wait_ms = 250)
167  __attribute__((optimize("O3")));
168 
169  bool write_track(uint8_t *pulses, size_t n_pulses,
170  bool store_greaseweazle = false)
171  __attribute__((optimize("O3")));
172  void print_pulse_bins(uint8_t *pulses, size_t n_pulses, uint8_t max_bins = 64,
173  bool is_gw_format = false, uint32_t min_bin_size = 100);
174  void print_pulses(uint8_t *pulses, size_t n_pulses,
175  bool is_gw_format = false);
176  uint32_t getSampleFrequency(void);
177 
178 #if defined(LED_BUILTIN)
179  int8_t led_pin = LED_BUILTIN;
180 #else
181  int8_t led_pin = -1;
182 #endif
183 
184  uint16_t select_delay_us = 10;
185  uint16_t step_delay_us = 10000;
186  uint16_t settle_delay_ms = 15;
187  uint16_t motor_delay_ms = 1000;
188  uint16_t watchdog_delay_ms =
189  1000;
190  uint8_t bus_type = BUSTYPE_IBMPC;
191 
192  Stream *debug_serial = nullptr;
193 
194 protected:
195  bool read_index();
196 
197 private:
198 #if defined(__SAMD51__)
199  void deinit_capture(void);
200  void enable_capture(void);
201 
202  bool init_generate(void);
203  void deinit_generate(void);
204  void enable_generate(void);
205  void disable_generate(void);
206 #endif
207 
208  bool start_polled_capture(void);
209  void disable_capture(void);
210 
211  bool init_capture(void);
212  void enable_background_capture(void);
213  void wait_for_index_pulse_low(void);
214 
215  int8_t _indexpin, _wrdatapin, _wrgatepin, _rddatapin;
216  bool _is_apple2;
217 
218 #ifdef BUSIO_USE_FAST_PINIO
219  BusIO_PortReg *indexPort;
220  BusIO_PortMask indexMask;
221  uint32_t dummyPort = 0;
222 #endif
223 };
224 
225 /**************************************************************************/
229 /**************************************************************************/
231 public:
232  Adafruit_Floppy(int8_t densitypin, int8_t indexpin, int8_t selectpin,
233  int8_t motorpin, int8_t directionpin, int8_t steppin,
234  int8_t wrdatapin, int8_t wrgatepin, int8_t track0pin,
235  int8_t protectpin, int8_t rddatapin, int8_t sidepin,
236  int8_t readypin);
237  void end() override;
238  void soft_reset(void) override;
239 
240  void select(bool selected) override;
241  bool spin_motor(bool motor_on) override;
242  bool goto_track(uint8_t track) override;
243  bool side(uint8_t head) override;
244  int8_t track(void) override;
245  int8_t get_side(void) override;
246  void step(bool dir, uint8_t times);
247  bool set_density(bool high_density) override;
248  bool get_write_protect() override;
249  bool get_track0_sense() override;
250  bool get_ready_sense() override;
251 
252 private:
253  // theres a lot of GPIO!
254  int8_t _densitypin, _selectpin, _motorpin, _directionpin, _steppin,
255  _track0pin, _protectpin, _sidepin, _readypin;
256 
257  int8_t _track = -1, _side = -1;
258 };
259 
260 /**************************************************************************/
264 /**************************************************************************/
266 public:
267  /**************************************************************************/
271  /**************************************************************************/
272  enum StepMode {
273  STEP_MODE_WHOLE, //< One step moves by one data track
274  STEP_MODE_HALF, //< Two steps move by one data track
275  STEP_MODE_QUARTER, //< Four steps move by one data track
276  };
277 
278  Adafruit_Apple2Floppy(int8_t indexpin, int8_t selectpin, int8_t phase1pin,
279  int8_t phase2pin, int8_t phase3pin, int8_t phase4pin,
280  int8_t wrdatapin, int8_t wrgatepin, int8_t protectpin,
281  int8_t rddatapin);
282  void end() override;
283  void soft_reset(void) override;
284 
285  void select(bool selected) override;
286  bool spin_motor(bool motor_on) override;
287  bool goto_track(uint8_t track) override;
288  bool side(uint8_t head) override;
289  int8_t track(void) override;
290  bool set_density(bool high_density) override;
291  bool get_write_protect() override;
292  bool get_track0_sense() override;
293  bool get_ready_sense() override { return true; }
294 
295  int8_t quartertrack();
296  bool goto_quartertrack(int);
297  void step_mode(StepMode mode);
298 
299  int8_t get_side() override { return 0; }
300 
301 private:
302  int _step_multiplier() const;
303  // theres not much GPIO!
304  int8_t _selectpin, _phase1pin, _phase2pin, _phase3pin, _phase4pin,
305  _protectpin;
306  int _quartertrack = -1;
307  StepMode _step_mode = STEP_MODE_HALF;
308  void _step(int dir, int times);
309 };
310 
311 /**************************************************************************/
317 /**************************************************************************/
318 class Adafruit_MFM_Floppy : public FsBlockDeviceInterface {
319 public:
321  adafruit_floppy_disk_t format = AUTODETECT);
322 
323  bool begin(void);
324  void end(void);
325 
326  uint32_t size(void) const;
327  int32_t readTrack(uint8_t track, bool head);
328 
331  uint8_t sectors_per_track(void) const { return _sectors_per_track; }
334  uint8_t tracks_per_side(void) const { return _tracks_per_side; }
335 
338  bool dirty() const { return _dirty; }
339 
341  void removed();
347  bool inserted(adafruit_floppy_disk_t format);
348 
349  //------------- SdFat v2 FsBlockDeviceInterface API -------------//
350  virtual bool isBusy();
351  virtual uint32_t sectorCount();
352  virtual bool syncDevice();
353 
354  virtual bool readSector(uint32_t block, uint8_t *dst);
355  virtual bool readSectors(uint32_t block, uint8_t *dst, size_t ns);
356  virtual bool writeSector(uint32_t block, const uint8_t *src);
357  virtual bool writeSectors(uint32_t block, const uint8_t *src, size_t ns);
358 
360  uint8_t track_data[MFM_IBMPC1440K_SECTORS_PER_TRACK * MFM_BYTES_PER_SECTOR];
361 
363  uint8_t track_validity[MFM_IBMPC1440K_SECTORS_PER_TRACK];
364 
365 private:
366  bool autodetect();
367 #if defined(PICO_BOARD) || defined(__RP2040__) || defined(ARDUINO_ARCH_RP2040)
368  uint16_t _last;
369 #endif
370  static constexpr uint8_t NO_TRACK = UINT8_MAX;
371  uint8_t _sectors_per_track = 0;
372  uint8_t _tracks_per_side = 0;
373  uint8_t _last_track_read = NO_TRACK; // last cached track
374  uint16_t _bit_time_ns;
375  bool _high_density = true;
376  bool _dirty = false, _track_has_errors = false;
377  bool _double_step = false;
378  Adafruit_Floppy *_floppy = nullptr;
379  adafruit_floppy_disk_t _format = AUTODETECT;
380 
382  uint8_t _flux[125000];
383  size_t _n_flux;
384 };
385 
386 #endif
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 one track&#39;s worth of flux transitions, between two falling index pulses.
Definition: Adafruit_Floppy.cpp:576
uint8_t sectors_per_track(void) const
The expected number of sectors per track in this format.
Definition: Adafruit_Floppy.h:331
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:846
virtual bool goto_track(uint8_t track_num)=0
Seek to the desired track, requires the motor to be spun up!
StepMode
Constants for use with the step_mode method.
Definition: Adafruit_Floppy.h:272
uint8_t tracks_per_side(void) const
The expected number of tracks per side in this format.
Definition: Adafruit_Floppy.h:334
bool get_ready_sense() override
Check whether the ready output is active.
Definition: Adafruit_Floppy.h:293
bool read_index()
Poll the status of the index pulse.
Definition: Adafruit_Floppy.cpp:216
virtual bool side(uint8_t head)=0
Which head/side to read from.
virtual bool spin_motor(bool motor_on)=0
Turn on or off the floppy motor, if on we wait till we get an index pulse!
uint16_t watchdog_delay_ms
quiescent time until drives reset (msecs)
Definition: Adafruit_Floppy.h:188
An abstract base class for chattin with floppy drives.
Definition: Adafruit_Floppy.h:58
virtual int8_t get_side()=0
Current head in use, based on internal caching.
bool dirty() const
Check if there is data to be written to the current track.
Definition: Adafruit_Floppy.h:338
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:190
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:520
int8_t get_side() override
Current head in use, based on internal caching.
Definition: Adafruit_Floppy.h:299
int8_t led_pin
Debug LED output for tracing.
Definition: Adafruit_Floppy.h:181
uint16_t select_delay_us
delay after drive select (usecs)
Definition: Adafruit_Floppy.h:184
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:185
bool write_track(uint8_t *pulses, size_t n_pulses, bool store_greaseweazle=false) __attribute__((optimize("O3")))
Write one track of flux pulse data, starting at the index pulse.
Definition: Adafruit_Floppy.cpp:716
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)
Decode one track of previously captured MFM data.
Definition: Adafruit_Floppy.cpp:484
virtual int8_t track(void)=0
The current track location, based on internal caching.
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:880
virtual bool get_ready_sense()=0
Check whether the ready output is active.
Definition: Adafruit_Floppy.h:318
A helper class for chattin with PC & Shugart floppy drives.
Definition: Adafruit_Floppy.h:230
uint32_t getSampleFrequency(void)
Get the sample rate that we read and emit pulses at, platform and implementation-dependant.
Definition: Adafruit_Floppy.cpp:548
uint16_t motor_delay_ms
delay after motor on (msecs)
Definition: Adafruit_Floppy.h:187
Stream * debug_serial
optional debug stream for serial output
Definition: Adafruit_Floppy.h:192
virtual void end()
Disables floppy communication, allowing pins to be used for general input and output.
Definition: Adafruit_Floppy.cpp:159
A helper class for chattin with Apple 2 floppy drives.
Definition: Adafruit_Floppy.h:265
uint16_t settle_delay_ms
settle delay after seek (msecs)
Definition: Adafruit_Floppy.h:186
bool begin(void)
Initializes the GPIO pins but do not start the motor or anything.
Definition: Adafruit_Floppy.cpp:98