|
@@ -28,8 +28,22 @@ enum drive_flags {
|
|
|
};
|
|
|
|
|
|
enum pending {
|
|
|
- PEND_IO = 1,
|
|
|
- PEND_RESET = 2
|
|
|
+ PEND_IO = 1, /* I/O transfer complete */
|
|
|
+ PEND_STARTCMD = 2, /* C1# = go to command state */
|
|
|
+ PEND_RESET = 4 /* C3# or RST# = local/global reset */
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * Status bits in INP(1); bits 5 & 2 are unknown, bit 4 is apparently
|
|
|
+ * always 0 on at least card 66 81046-02 (55 21046-x1)...
|
|
|
+ * "fast DMA controller".
|
|
|
+ */
|
|
|
+enum abc_status {
|
|
|
+ AS_CMD = 0x80, /* Start of command */
|
|
|
+ AS_WRITE = 0x40, /* Data direction OUT(0) (to controller) */
|
|
|
+ AS_ERROR = 0x08, /* INP(0) contains error info */
|
|
|
+ AS_READING = 0x04, /* Waiting for sector load */
|
|
|
+ AS_READY = 0x01 /* Ready for input */
|
|
|
};
|
|
|
|
|
|
/* Number of fragments (extents) we can memoize */
|
|
@@ -71,6 +85,7 @@ struct ctl_state {
|
|
|
const struct ctl_params *params;
|
|
|
uint8_t k[4]; /* Command bytes */
|
|
|
uint8_t drives; /* Total drives present */
|
|
|
+ uint8_t error; /* Error status */
|
|
|
bool initialized; /* Controller initialized */
|
|
|
volatile enum pending pending; /* Need to do I/O */
|
|
|
struct drive_state drv[8]; /* Per-drive state */
|
|
@@ -78,12 +93,13 @@ struct ctl_state {
|
|
|
};
|
|
|
|
|
|
/*
|
|
|
- * DOSGEN depends on this value... and different DOSGEN
|
|
|
- * expect different values. If this value is wrong, DOSGEN
|
|
|
- * will spin forever on "testing sector..."
|
|
|
+ * Error codes for INP(0), derived from FD1791 status codes
|
|
|
+ *
|
|
|
+ * DOSGEN depends on OUT_OF_RANGE having this value... and different
|
|
|
+ * DOSGEN expect different values. If this value is wrong, DOSGEN will
|
|
|
+ * spin forever on "testing sector..."
|
|
|
*/
|
|
|
-#define OUT_OF_RANGE 0x21 /* Status code for an invalid sector */
|
|
|
-
|
|
|
+#define OUT_OF_RANGE 0x21 /* Status code for an invalid sector */
|
|
|
#define DISK_NOT_READY 0x80
|
|
|
#define WRITE_PROTECT 0x40
|
|
|
#define WRITE_FAULT 0x20
|
|
@@ -226,9 +242,12 @@ static void disk_start_command(struct ctl_state *state)
|
|
|
#if 0
|
|
|
con_printf("%-2s: start command\n", state->params->name);
|
|
|
#endif
|
|
|
-
|
|
|
+
|
|
|
memset(&state->k, 0xee, sizeof state->k);
|
|
|
- abc_setup_out_queue(&state->iodev, state->k, 4, 0x81);
|
|
|
+ state->iodev.callback_mask |= 1 << 0;
|
|
|
+ abc_setup_out_queue(&state->iodev, state->k, 4,
|
|
|
+ AS_CMD|AS_READY|AS_WRITE|
|
|
|
+ (state->error ? AS_ERROR : 0));
|
|
|
}
|
|
|
|
|
|
static void sync_drives(struct ctl_state *state)
|
|
@@ -244,16 +263,14 @@ static void sync_drives(struct ctl_state *state)
|
|
|
|
|
|
static void disk_set_error(struct ctl_state *state, unsigned int error)
|
|
|
{
|
|
|
- abc_set_inp_default(&state->iodev, error);
|
|
|
+ abc_set_inp_default(&state->iodev, state->error = error);
|
|
|
+ state->k[0] = 0; /* Abort remaining command sequence */
|
|
|
}
|
|
|
|
|
|
static void disk_reset_state(struct ctl_state *state)
|
|
|
{
|
|
|
abc_set_inp_status(&state->iodev, 0);
|
|
|
disk_set_error(state, 0);
|
|
|
-
|
|
|
- sync_drives(state);
|
|
|
-
|
|
|
disk_start_command(state);
|
|
|
}
|
|
|
|
|
@@ -372,31 +389,39 @@ static void unmount_drives(struct ctl_state *state)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-#define IDLE_CALLBACK_MASK (1 << 4) /* C3 = reset */
|
|
|
+/* RST# = global reset, C3# = local reset, C1# = goto command start */
|
|
|
+#define IDLE_CALLBACK_MASK ((1 << 7)|(1 << 4)|(1 << 2))
|
|
|
|
|
|
-static void abcdisk_callback_out(struct abc_dev *dev, uint8_t data)
|
|
|
+static ABC_CALLBACK(abcdisk_callback_out)
|
|
|
{
|
|
|
struct ctl_state *state = container_of(dev, struct ctl_state, iodev);
|
|
|
|
|
|
dev->callback_mask = IDLE_CALLBACK_MASK;
|
|
|
ABC_INP1_DATA = dev->inp_data[1] = 0;
|
|
|
+ state->error = 0;
|
|
|
state->pending |= PEND_IO;
|
|
|
}
|
|
|
|
|
|
-static void abcdisk_callback_inp(struct abc_dev *dev)
|
|
|
+static ABC_CALLBACK(abcdisk_callback_inp)
|
|
|
{
|
|
|
struct ctl_state *state = container_of(dev, struct ctl_state, iodev);
|
|
|
|
|
|
dev->callback_mask = IDLE_CALLBACK_MASK;
|
|
|
ABC_INP1_DATA = dev->inp_data[1] = 0;
|
|
|
+ state->error = 0;
|
|
|
state->pending |= PEND_IO;
|
|
|
}
|
|
|
|
|
|
-static void abcdisk_callback_cmd(struct abc_dev *dev, uint8_t data, uint8_t addr)
|
|
|
+static ABC_CALLBACK(abcdisk_callback_restart)
|
|
|
{
|
|
|
struct ctl_state *state = container_of(dev, struct ctl_state, iodev);
|
|
|
- if (addr == 4)
|
|
|
- state->pending |= PEND_RESET;
|
|
|
+ state->pending |= PEND_STARTCMD;
|
|
|
+}
|
|
|
+
|
|
|
+static ABC_CALLBACK(abcdisk_callback_rst)
|
|
|
+{
|
|
|
+ struct ctl_state *state = container_of(dev, struct ctl_state, iodev);
|
|
|
+ state->pending |= PEND_RESET;
|
|
|
}
|
|
|
|
|
|
static char abcdisk_80_800[] = "/abcdisk.800/";
|
|
@@ -448,21 +473,20 @@ static void do_next_command(struct ctl_state *state)
|
|
|
drv->name, state->k[0], state->k[1],
|
|
|
state->k[2], state->k[3]);
|
|
|
#endif
|
|
|
-
|
|
|
if (state->k[0] & 0x01) {
|
|
|
/* READ SECTOR */
|
|
|
if (!(drv->flags & DF_MOUNTED)) {
|
|
|
disk_set_error(state, DISK_NOT_READY);
|
|
|
} else if (!cur_sector_valid(state)) {
|
|
|
disk_set_error(state, OUT_OF_RANGE);
|
|
|
- } else if (!file_pos_valid(state)) {
|
|
|
- disk_set_error(state, CRC_ERROR);
|
|
|
} else {
|
|
|
UINT rlen = 0;
|
|
|
FRESULT rv;
|
|
|
|
|
|
set_led(LED_DISKIO, true);
|
|
|
|
|
|
+ abc_set_inp_status(&state->iodev, AS_READING|AS_READY);
|
|
|
+
|
|
|
rv = f_lseek(&drv->file, file_pos(state));
|
|
|
if (rv == FR_OK)
|
|
|
rv = f_read(&drv->file, buf, 256, &rlen);
|
|
@@ -475,13 +499,15 @@ static void do_next_command(struct ctl_state *state)
|
|
|
if (state->k[0] & 0x02) {
|
|
|
/* SECTOR TO HOST */
|
|
|
state->k[0] &= ~0x02; /* Command done */
|
|
|
- abc_setup_inp_queue(&state->iodev, buf, 256, 0x01);
|
|
|
+ state->iodev.callback_mask |= 1 << 8;
|
|
|
+ abc_setup_inp_queue(&state->iodev, buf, 256, AS_READY);
|
|
|
return;
|
|
|
}
|
|
|
if (state->k[0] & 0x04) {
|
|
|
/* SECTOR FROM HOST */
|
|
|
state->k[0] &= ~0x04; /* Command done */
|
|
|
- abc_setup_out_queue(&state->iodev, buf, 256, 0x01);
|
|
|
+ state->iodev.callback_mask |= 1 << 0;
|
|
|
+ abc_setup_out_queue(&state->iodev, buf, 256, AS_WRITE|AS_READY);
|
|
|
return;
|
|
|
}
|
|
|
if (state->k[0] & 0x08) {
|
|
@@ -492,8 +518,6 @@ static void do_next_command(struct ctl_state *state)
|
|
|
disk_set_error(state, WRITE_PROTECT);
|
|
|
} else if (!cur_sector_valid(state)) {
|
|
|
disk_set_error(state, OUT_OF_RANGE);
|
|
|
- } else if (!file_pos_valid(state)) {
|
|
|
- disk_set_error(state, CRC_ERROR);
|
|
|
} else {
|
|
|
UINT wlen = 0;
|
|
|
FRESULT rv;
|
|
@@ -708,7 +732,7 @@ void abcdisk_io_poll(void)
|
|
|
|
|
|
for (int i = 0; i < CONTROLLER_TYPES; i++) {
|
|
|
struct ctl_state *state = &controllers[i];
|
|
|
- enum pending pending;
|
|
|
+ enum pending pending = 0;
|
|
|
|
|
|
if (unmount_all && state->initialized) {
|
|
|
unmount_drives(state);
|
|
@@ -726,19 +750,18 @@ void abcdisk_io_poll(void)
|
|
|
state->pending = 0;
|
|
|
unmask_irq(ABC_IRQ);
|
|
|
|
|
|
- if (pending & PEND_RESET)
|
|
|
+ if (pending & (PEND_RESET|PEND_STARTCMD))
|
|
|
disk_reset_state(state);
|
|
|
-
|
|
|
- if (pending & PEND_IO)
|
|
|
+ else if (pending & PEND_IO)
|
|
|
do_next_command(state);
|
|
|
}
|
|
|
|
|
|
- if (state->initialized && need_sync)
|
|
|
+ if (state->initialized && (need_sync || (pending & PEND_RESET)))
|
|
|
sync_drives(state);
|
|
|
}
|
|
|
|
|
|
if (need_sync) {
|
|
|
- /* Did we sync drives? */
|
|
|
+ /* Did we sync drives? Clear LED. */
|
|
|
set_led(LED_DISKIO, false);
|
|
|
}
|
|
|
}
|
|
@@ -751,11 +774,14 @@ void abcdisk_init(void)
|
|
|
{
|
|
|
static const struct abc_dev iodev_template = {
|
|
|
.callback_mask = IDLE_CALLBACK_MASK,
|
|
|
+ .inp_en = 3,
|
|
|
.status_first_out_mask = (uint8_t)~0x80,
|
|
|
.status_first_inp_mask = (uint8_t)~0x80,
|
|
|
- .callback_out = abcdisk_callback_out,
|
|
|
- .callback_inp = abcdisk_callback_inp,
|
|
|
- .callback_cmd = abcdisk_callback_cmd
|
|
|
+ .callback_out[0] = abcdisk_callback_out,
|
|
|
+ .callback_inp[0] = abcdisk_callback_inp,
|
|
|
+ .callback_out[2] = abcdisk_callback_restart,
|
|
|
+ .callback_out[4] = abcdisk_callback_rst, /* C3# = local reset */
|
|
|
+ .callback_rst = abcdisk_callback_rst
|
|
|
};
|
|
|
|
|
|
for (int i = 0; i < CONTROLLER_TYPES; i++) {
|
|
@@ -764,7 +790,7 @@ void abcdisk_init(void)
|
|
|
state->params = ¶meters[i];
|
|
|
state->iodev = iodev_template;
|
|
|
|
|
|
- abc_register(&state->iodev, state->params->devsel);
|
|
|
disk_reset_state(state);
|
|
|
+ abc_register(&state->iodev, state->params->devsel);
|
|
|
}
|
|
|
}
|