Add support for apple2 floppy drives to the greaseweazle sketch

.. this involves making some refinements to the floppy base class
as well.
This commit is contained in:
Jeff Epler 2022-03-10 13:33:37 -06:00
parent 98c4b2d098
commit 1508b08997
No known key found for this signature in database
GPG key ID: D5BF15AB975AB4DE
3 changed files with 364 additions and 94 deletions

View file

@ -28,6 +28,19 @@
#define READ_PIN 9 // IDC 30
#define SIDE_PIN 8 // IDC 32
#define READY_PIN 7 // IDC 34
// jepler's prototype board, subject to change
#define APPLE2_ENABLE_PIN (8) // D6
#define APPLE2_PHASE1_PIN (A2)
#define APPLE2_PHASE2_PIN (13)
#define APPLE2_PHASE3_PIN (12)
#define APPLE2_PHASE4_PIN (11)
#define APPLE2_RDDATA_PIN (7) // D5
#define APPLE2_WRDATA_PIN (-1)
#define APPLE2_WRGATE_PIN (-1)
#define APPLE2_PROTECT_PIN (-1)
#define APPLE2_INDEX_PIN (A3)
#ifndef USE_TINYUSB
#error "Please set Adafruit TinyUSB under Tools > USB Stack"
#endif
@ -52,10 +65,23 @@
#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);
#ifdef READ_PIN
Adafruit_Floppy pcfloppy(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);
#else
#warning "This firmware will not support PC/Shugart drives"
#endif
#ifdef APPLE2_RDDATA_PIN
Adafruit_Apple2Floppy apple2floppy(APPLE2_INDEX_PIN, APPLE2_ENABLE_PIN,
APPLE2_PHASE1_PIN, APPLE2_PHASE2_PIN, APPLE2_PHASE3_PIN, APPLE2_PHASE4_PIN,
APPLE2_WRDATA_PIN, APPLE2_WRGATE_PIN, APPLE2_PROTECT_PIN, APPLE2_RDDATA_PIN);
#else
#warning "This firmware will not support Apple ][ drives"
#endif
Adafruit_FloppyBase *floppy;
uint32_t time_stamp = 0;
@ -86,32 +112,67 @@ uint8_t cmd_buff_idx = 0;
#define GW_CMD_SETBUSTYPE 14
#define GW_CMD_SETBUSTYPE_IBM 1
#define GW_CMD_SETBUSTYPE_SHUGART 2
#define GW_CMD_SETBUSTYPE_APPLE2 3
#define GW_CMD_SETBUSTYPE_APPLE2_QUARTERTRACK 4
#define GW_CMD_SETPIN 15
#define GW_CMD_SETPIN_DENSITY 2
#define GW_CMD_RESET 16
#define GW_CMD_SOURCEBYTES 18
#define GW_CMD_SINKBYTES 19
#define GW_CMD_GETPIN 20
#define GW_CMD_GETPIN_TRACK0 26
#define GW_ACK_OK (byte)0
#define GW_ACK_BADCMD 1
#define GW_ACK_NOINDEX 2
#define GW_ACK_NOTRACK0 3
#define GW_ACK_WRPROT 6
#define GW_ACK_NOUNIT 7
#define GW_ACK_BADPIN 10
uint32_t timestamp = 0;
bool setbustype(int bustype) {
if (floppy) {
floppy->end();
}
floppy = nullptr;
switch (bustype) {
case -1:
#ifdef READ_PIN
case GW_CMD_SETBUSTYPE_IBM:
case GW_CMD_SETBUSTYPE_SHUGART:
// TODO: whats the diff???
floppy = &pcfloppy;
break;
#endif
#ifdef APPLE2_RDDATA_PIN
case GW_CMD_SETBUSTYPE_APPLE2:
floppy = &apple2floppy;
apple2floppy.step_mode(Adafruit_Apple2Floppy::STEP_MODE_HALF);
break;
case GW_CMD_SETBUSTYPE_APPLE2_QUARTERTRACK:
apple2floppy.step_mode(Adafruit_Apple2Floppy::STEP_MODE_QUARTER);
floppy = &apple2floppy;
break;
#endif
default:
Serial1.printf("Unsupported bus type %d\n", bustype);
return false;
}
floppy->debug_serial = &Serial1;
auto result = floppy->begin();
Serial1.printf("setbustype() floppy = %p result=%d\n", floppy, result);
return result;
}
void setup() {
Serial.begin(115200);
Serial1.begin(115200);
//while (!Serial) delay(100);
Serial1.println("GrizzlyWizzly");
floppy.debug_serial = &Serial1;
if (!floppy.begin()) {
Serial1.println("Failed to initialize floppy interface");
while (1) yield();
}
timestamp = millis();
}
uint8_t get_cmd(uint8_t *buff, uint8_t maxbuff) {
@ -145,16 +206,23 @@ void loop() {
if (!cmd_len) {
if ((millis() > timestamp) && ((millis() - timestamp) > 10000)) {
Serial1.println("Timed out waiting for command, resetting motor");
floppy.goto_track(0);
floppy.spin_motor(false);
if (floppy) {
Serial1.println("goto track 0");
floppy->goto_track(0);
Serial1.println("stop motor");
floppy->spin_motor(false);
Serial1.println("deselect");
floppy->select(false);
}
Serial1.println("motor reset");
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));
@ -162,6 +230,7 @@ void loop() {
Serial1.printf("Got command 0x%02x of length %d\n\r", cmd, cmd_buffer[1]);
if (cmd == GW_CMD_GETINFO) {
Serial1.println("Get info");
uint8_t sub_cmd = cmd_buffer[2];
@ -171,7 +240,7 @@ void loop() {
reply_buffer[i++] = GW_FIRMVER_MINOR; // 1 byte
reply_buffer[i++] = 1; // is main firm
reply_buffer[i++] = GW_MAXCMD;
uint32_t samplefreq = floppy.getSampleFrequency();
uint32_t samplefreq = floppy->getSampleFrequency();
reply_buffer[i++] = samplefreq & 0xFF;
reply_buffer[i++] = (samplefreq >> 8) & 0xFF;
reply_buffer[i++] = (samplefreq >> 16) & 0xFF;
@ -216,47 +285,47 @@ void loop() {
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;
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();
if (floppy)
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;
auto result = setbustype(bustype);
Serial1.printf("Set bus type %d -> %d\n\r", bustype, result);
if (result) {
reply_buffer[i++] = GW_ACK_OK;
} else {
reply_buffer[i++] = GW_ACK_BADCMD;
}
motor_state = false;
Serial.write(reply_buffer, 2);
}
else if (cmd == GW_CMD_SEEK) {
if (!floppy) goto needfloppy;
uint8_t track = cmd_buffer[2];
Serial1.printf("Seek track %d\n\r", track);
bool r = floppy.goto_track(track);
bool r = floppy->goto_track(track);
if (r) {
reply_buffer[i++] = GW_ACK_OK;
} else {
@ -266,19 +335,23 @@ void loop() {
}
else if (cmd == GW_CMD_HEAD) {
if (!floppy) goto needfloppy;
uint8_t head = cmd_buffer[2];
Serial1.printf("Seek head %d\n\r", head);
floppy.side(head);
floppy->side(head);
reply_buffer[i++] = GW_ACK_OK;
Serial.write(reply_buffer, 2);
}
else if (cmd == GW_CMD_MOTOR) {
if (!floppy) goto needfloppy;
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)) {
if (! floppy->spin_motor(state)) {
reply_buffer[i++] = GW_ACK_NOINDEX;
} else {
reply_buffer[i++] = GW_ACK_OK;
@ -292,25 +365,32 @@ void loop() {
}
else if (cmd == GW_CMD_SELECT) {
if (!floppy) goto needfloppy;
uint8_t sub_cmd = cmd_buffer[2];
Serial1.printf("Select drive %d\n\r", sub_cmd);
if (sub_cmd == 0) {
floppy.select(true);
floppy->select(true);
reply_buffer[i++] = GW_ACK_OK;
} else {
reply_buffer[i++] = GW_ACK_NOUNIT;
}
Serial1.printf("Reply buffer = %d %d\n", reply_buffer[0], reply_buffer[1]);
Serial.write(reply_buffer, 2);
}
else if (cmd == GW_CMD_DESELECT) {
if (!floppy) goto needfloppy;
Serial1.printf("Deselect drive\n\r");
floppy.select(false);
floppy->select(false);
reply_buffer[i++] = GW_ACK_OK;
Serial.write(reply_buffer, 2);
}
else if (cmd == GW_CMD_READFLUX) {
if (!floppy) goto needfloppy;
uint32_t flux_ticks;
uint16_t revs;
flux_ticks = cmd_buffer[5];
@ -327,16 +407,16 @@ void loop() {
revs -= 1;
}
if (floppy.track() == -1) {
floppy.goto_track(0);
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);
Serial1.printf("Reading flux0rs on track %d: %u ticks and %d revs\n\r", floppy->track(), flux_ticks, revs);
uint16_t capture_ms = 0;
uint16_t capture_revs = 0;
if (flux_ticks) {
// we'll back calculate the revolutions
capture_ms = 1000.0 * (float)flux_ticks / (float)floppy.getSampleFrequency();
capture_ms = 1000.0 * (float)flux_ticks / (float)floppy->getSampleFrequency();
revs = 1;
} else {
capture_revs = revs;
@ -348,11 +428,11 @@ void loop() {
while (revs--) {
uint32_t index_offset;
// read in greaseweazle mode (long pulses encoded with 250's)
captured_pulses = floppy.capture_track(flux_transitions, sizeof(flux_transitions),
&index_offset, true, capture_ms);
captured_pulses = floppy->capture_track(flux_transitions, sizeof(flux_transitions),
&index_offset, true, capture_ms);
Serial1.printf("Rev #%d captured %u pulses, second index fall @ %d\n\r",
revs, captured_pulses, index_offset);
//floppy.print_pulse_bins(flux_transitions, captured_pulses, 64, Serial1);
//floppy->print_pulse_bins(flux_transitions, captured_pulses, 64, Serial1);
// Send the index falling signal opcode, which was right
// at the start of this data xfer (we wait for index to fall
// before we start reading
@ -405,27 +485,35 @@ void loop() {
Serial.write((byte)0);
}
else if (cmd == GW_CMD_WRITEFLUX) {
if (!floppy) goto needfloppy;
Serial1.println("write flux");
//uint8_t cue_at_index = cmd_buffer[2];
//uint8_t terminate_at_index = cmd_buffer[3];
reply_buffer[i++] = GW_ACK_OK;
Serial.write(reply_buffer, 2);
if (floppy->get_write_protect()) {
reply_buffer[i++] = GW_ACK_WRPROT;
Serial.write(reply_buffer, 2);
uint32_t fluxors = 0;
uint8_t flux = 0xFF;
while (flux && (fluxors < MAX_FLUX_PULSE_PER_TRACK)) {
while (!Serial.available()) yield();
flux = Serial.read();
flux_transitions[fluxors++] = flux;
} else {
//uint8_t cue_at_index = cmd_buffer[2];
//uint8_t terminate_at_index = cmd_buffer[3];
reply_buffer[i++] = GW_ACK_OK;
Serial.write(reply_buffer, 2);
uint32_t fluxors = 0;
uint8_t flux = 0xFF;
while (flux && (fluxors < MAX_FLUX_PULSE_PER_TRACK)) {
while (!Serial.available()) yield();
flux = Serial.read();
flux_transitions[fluxors++] = flux;
}
if (fluxors == MAX_FLUX_PULSE_PER_TRACK) {
Serial1.println("*** FLUX OVERRUN ***");
while (1) yield();
}
floppy->write_track(flux_transitions, fluxors - 7, true);
Serial1.println("wrote fluxors");
Serial.write((byte)0);
}
if (fluxors == MAX_FLUX_PULSE_PER_TRACK) {
Serial1.println("*** FLUX OVERRUN ***");
while (1) yield();
}
floppy.write_track(flux_transitions, fluxors - 7, true);
Serial1.println("wrote fluxors");
Serial.write((byte)0);
}
else if (cmd == GW_CMD_GETFLUXSTATUS) {
Serial1.println("get flux status");
@ -537,32 +625,36 @@ void loop() {
Serial1.printf("getpin %d\n\r", pin);
switch (pin) {
case 26:
case GW_CMD_GETPIN_TRACK0:
reply_buffer[i++] = GW_ACK_OK;
reply_buffer[i++] = digitalRead(TRK0_PIN);
reply_buffer[i++] = floppy->get_track0_sense();
break;
default:
// unknown pin, don't pretend we did it right
reply_buffer[i++] = GW_ACK_BADCMD;
reply_buffer[i++] = GW_ACK_BADPIN;
reply_buffer[i++] = 0;
}
Serial.write(reply_buffer, i);
} else if (cmd == GW_CMD_SETPIN) {
if (!floppy) goto needfloppy;
uint32_t pin = cmd_buffer[2];
bool value = cmd_buffer[3];
Serial1.printf("setpin %d to \n\r", pin, value);
Serial1.printf("setpin %d to %d\n", pin, value);
switch (pin) {
case 2:
pinMode(DENSITY_PIN, OUTPUT);
digitalWrite(DENSITY_PIN, value);
reply_buffer[i++] = GW_ACK_OK;
case GW_CMD_SETPIN_DENSITY:
if (floppy->set_density(value)) {
reply_buffer[i++] = GW_ACK_OK;
} else {
reply_buffer[i++] = GW_ACK_BADCMD;
}
break;
default:
// unknown pin, don't pretend we did it right
reply_buffer[i++] = GW_ACK_BADCMD;
reply_buffer[i++] = GW_ACK_BADPIN;
}
Serial.write(reply_buffer, i);
@ -572,5 +664,13 @@ void loop() {
reply_buffer[i++] = GW_ACK_BADCMD;
Serial.write(reply_buffer, 2);
}
return;
needfloppy:
Serial1.printf("Got command without valid floppy object\n", cmd);
reply_buffer[i++] = GW_ACK_BADCMD;
Serial.write(reply_buffer, 2);
//Serial1.println("cmd complete!");
}

View file

@ -265,10 +265,19 @@ void Adafruit_Floppy::select(bool selected) {
/*!
@brief Which head/side to read from
@param head Head 0 or 1
@return true if the head can be selected, false otherwise
@note If _sidepin is no pin, then only head 0 can be selected
*/
/**************************************************************************/
void Adafruit_Floppy::side(uint8_t head) {
bool Adafruit_Floppy::side(uint8_t head) {
if (head != 0 && head != 1) {
return false;
}
if (_sidepin == -1) {
return head == 0;
}
digitalWrite(_sidepin, !head); // Head 0 is logic level 1, head 1 is logic 0!
return true;
}
/**************************************************************************/
@ -414,6 +423,28 @@ void Adafruit_Floppy::step(bool dir, uint8_t times) {
/**************************************************************************/
int8_t Adafruit_Floppy::track(void) { return _track; }
bool Adafruit_Floppy::get_write_protect(void) {
if (_protectpin == -1) {
return false;
}
return digitalRead(_protectpin);
}
bool Adafruit_Floppy::get_track0_sense(void) {
if (_track0pin == 0) {
return track() == 0;
}
return digitalRead(_track0pin);
}
bool Adafruit_Floppy::set_density(bool high_density) {
if (_densitypin == 0) {
return !high_density;
}
digitalWrite(_densitypin, high_density);
return true;
}
/**************************************************************************/
/*!
@brief Capture and decode one track of MFM data
@ -927,7 +958,8 @@ void Adafruit_Apple2Floppy::soft_reset() {
/**************************************************************************/
void Adafruit_Apple2Floppy::select(bool selected) {
digitalWrite(_selectpin, !selected);
Serial.printf("set selectpin %d to %d\n", _selectpin, !selected);
if (debug_serial)
debug_serial->printf("set selectpin %d to %d\n", _selectpin, !selected);
}
/**************************************************************************/
@ -996,16 +1028,32 @@ enum {
*/
/**************************************************************************/
bool Adafruit_Apple2Floppy::goto_track(uint8_t track_num) {
if (track_num < 0 || track_num > 160) {
return false;
}
if (_quartertrack == -1) {
_quartertrack = 160;
goto_track(0);
goto_quartertrack(0);
}
int quartertrack = track_num * _step_multiplier();
return goto_quartertrack(quartertrack);
}
/**************************************************************************/
/*!
@brief Seek to the desired quarter track, requires the motor to be spun up!
@param quartertrack The position to step to
@return True If we were able to get to the track location
*/
/**************************************************************************/
bool Adafruit_Apple2Floppy::goto_quartertrack(int quartertrack) {
if (quartertrack < 0 || quartertrack >= 160) {
return false;
}
int quartertrack = track_num * 4;
int diff = quartertrack - this->_quartertrack;
int diff = (int)quartertrack - (int)this->_quartertrack;
if (debug_serial) {
debug_serial->printf("Stepping from %d -> %d, diff = %d\n",
this->_quartertrack, quartertrack, diff);
}
if (diff != 0) {
if (diff < 0) {
@ -1024,11 +1072,14 @@ bool Adafruit_Apple2Floppy::goto_track(uint8_t track_num) {
digitalWrite(_phase3pin, 0);
digitalWrite(_phase4pin, 0);
_quartertrack = quartertrack;
return true;
}
void Adafruit_Apple2Floppy::_step(int direction, int count) {
Serial.printf("Step by %d x %d\n", direction, count);
if (debug_serial)
debug_serial->printf("Step by %d x %d\n", direction, count);
for (; count--;) {
_quartertrack += direction;
auto phase = _quartertrack % std::size(phases);
@ -1044,19 +1095,27 @@ void Adafruit_Apple2Floppy::_step(int direction, int count) {
/**************************************************************************/
/*!
@brief Which head/side to read from
@param head Head 0
@param head Head must be 0
@return true if the head is 0, false otherwise
@note Apple II floppy drives only have a single side
*/
/**************************************************************************/
void Adafruit_Apple2Floppy::side(uint8_t head) {}
bool Adafruit_Apple2Floppy::side(uint8_t head) { return head == 0; }
/**************************************************************************/
/*!
@brief The current track location, based on internal caching
@return The cached track location
@note Partial tracks are rounded, with quarter tracks always rounded down.
*/
/**************************************************************************/
int8_t Adafruit_Apple2Floppy::track(void) { return _quartertrack / 4; }
int8_t Adafruit_Apple2Floppy::track(void) {
if (_quartertrack == -1) {
return -1;
}
auto m = _step_multiplier();
return (_quartertrack + m / 2) / m;
}
/**************************************************************************/
/*!
@ -1069,18 +1128,84 @@ int8_t Adafruit_Apple2Floppy::track(void) { return _quartertrack / 4; }
energized; having the "phase 1" winding active also prevents writing, but at
the Disk Interface Card, not at the drive. So, it's necessary for us to check
write_protected in software!
If_protectpin is -1 (not available), then we always report that the disk is
write protected.
*/
/**************************************************************************/
bool Adafruit_Apple2Floppy::write_protected(void) {
auto t = track();
if (t & 1) {
goto_track(t & ~1);
bool Adafruit_Apple2Floppy::get_write_protect(void) {
if (_protectpin == -1) {
return true;
}
auto t = quartertrack();
// we need to be on an even-numbered track, so that activating the "phase 1"
// winding doesn't pull out of position. We'll return to the right spot below.
goto_quartertrack(t & ~3);
// goto_track() has deenergized all windings, we need to enable phase1
// temporarily
digitalWrite(_phase1pin, 1);
// ensure that the output has time to rise, 1ms is plenty
delay(1);
// only now can we read the protect pin status
bool result = !digitalRead(_protectpin);
digitalWrite(_phase1pin, 0);
delay(settle_delay_ms);
goto_track(t);
// Return to where we were before...!
goto_quartertrack(t);
return result;
}
/**************************************************************************/
/*!
@brief Set the density for flux reading and writing
@return true if low density mode is selected, false if high density is
selected
@note The drive hardware is only capable of single density operation
*/
bool Adafruit_Apple2Floppy::set_density(bool high_density) {
return high_density == false;
}
/**************************************************************************/
/*!
@brief Check whether the track0 sensor is active
@returns True if the track0 sensor is active, false otherwise
@note This device has no home sensor so it just returns track() == 0.
*/
/**************************************************************************/
bool Adafruit_Apple2Floppy::get_track0_sense(void) { return track() == 0; }
/**************************************************************************/
/*!
@brief Get the drive position in quarter tracks
@returns True if the track0 sensor is active, false otherwise
@note Returns -1 if the position is unknown
*/
/**************************************************************************/
int8_t Adafruit_Apple2Floppy::quartertrack() { return _quartertrack; }
/**************************************************************************/
/*!
@brief Set the positioning mode
@param step_mode The new positioning mode
@note This does not re-position the drive
*/
/**************************************************************************/
void Adafruit_Apple2Floppy::step_mode(StepMode step_mode) {
_step_mode = step_mode;
}
int Adafruit_Apple2Floppy::_step_multiplier(void) const {
switch (_step_mode) {
case STEP_MODE_WHOLE:
return 4;
case STEP_MODE_HALF:
return 2;
default:
case STEP_MODE_QUARTER:
return 1;
}
}

View file

@ -74,16 +74,44 @@ public:
/*!
@brief Which head/side to read from
@param head Head 0 or 1
@return true if the head exists, false otherwise
*/
/**************************************************************************/
virtual void side(uint8_t head) = 0;
virtual bool side(uint8_t head) = 0;
/**************************************************************************/
/*!
@brief The current track location, based on internal caching
@return The cached track location
@note Returns -1 if the track is not known.
*/
/**************************************************************************/
virtual int8_t track(void) = 0;
/**************************************************************************/
/*!
@brief Check whether the floppy in the drive is write protected
@returns False if the floppy is writable, true otherwise
*/
/**************************************************************************/
virtual bool get_write_protect() = 0;
/**************************************************************************/
/*!
@brief Check whether the track0 sensor is active
@returns True if the track0 sensor is active, false otherwise
@note On devices without a track0 sensor, this returns true when
track()==0
*/
/**************************************************************************/
virtual bool get_track0_sense() = 0;
/**************************************************************************/
/*!
@brief Set the density for flux reading and writing
@param high_density false for low density, true for high density
@returns True if the drive interface supports the given density.
*/
/**************************************************************************/
virtual bool set_density(bool high_density) = 0;
uint32_t read_track_mfm(uint8_t *sectors, size_t n_sectors,
uint8_t *sector_validity, bool high_density = true);
@ -167,9 +195,12 @@ public:
void select(bool selected) override;
bool spin_motor(bool motor_on) override;
bool goto_track(uint8_t track) override;
void side(uint8_t head) override;
bool side(uint8_t head) override;
int8_t track(void) override;
void step(bool dir, uint8_t times);
bool set_density(bool high_density) override;
bool get_write_protect() override;
bool get_track0_sense() override;
private:
// theres a lot of GPIO!
@ -186,6 +217,12 @@ private:
/**************************************************************************/
class Adafruit_Apple2Floppy : public Adafruit_FloppyBase {
public:
enum StepMode {
STEP_MODE_WHOLE,
STEP_MODE_HALF,
STEP_MODE_QUARTER,
};
Adafruit_Apple2Floppy(int8_t indexpin, int8_t selectpin, int8_t phase1pin,
int8_t phase2pin, int8_t phase3pin, int8_t phase4pin,
int8_t wrdatapin, int8_t wrgatepin, int8_t protectpin,
@ -196,15 +233,23 @@ public:
void select(bool selected) override;
bool spin_motor(bool motor_on) override;
bool goto_track(uint8_t track) override;
void side(uint8_t head) override;
bool side(uint8_t head) override;
int8_t track(void) override;
bool write_protected(void);
bool set_density(bool high_density) override;
bool get_write_protect() override;
bool get_track0_sense() override;
int8_t quartertrack();
bool goto_quartertrack(int);
void step_mode(StepMode mode);
private:
int _step_multiplier() const;
// theres not much GPIO!
int8_t _selectpin, _phase1pin, _phase2pin, _phase3pin, _phase4pin,
_protectpin;
int8_t _quartertrack = -1;
int _quartertrack = -1;
StepMode _step_mode = STEP_MODE_HALF;
void _step(int dir, int times);
};