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:
parent
98c4b2d098
commit
1508b08997
3 changed files with 364 additions and 94 deletions
|
|
@ -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!");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue