|
@@ -4,7 +4,11 @@
|
|
|
* Emulate an ABC80/800 disk controller
|
|
|
*/
|
|
|
|
|
|
+#include <string.h>
|
|
|
+#include <stdio.h>
|
|
|
+
|
|
|
#include "fw.h"
|
|
|
+#include "io.h"
|
|
|
#include "abcio.h"
|
|
|
#include "ff.h"
|
|
|
|
|
@@ -39,7 +43,7 @@ struct drive_state {
|
|
|
|
|
|
/* Per-controller state */
|
|
|
struct ctl_state {
|
|
|
- struct abc_dev *iodev;
|
|
|
+ struct abc_dev iodev;
|
|
|
uint8_t k[4]; /* Command bytes */
|
|
|
uint8_t clustshift; /* log2(clustersize/256) */
|
|
|
uint8_t devsel; /* I/O device select */
|
|
@@ -86,7 +90,8 @@ enum controller_types {
|
|
|
# define IL(mask, fac)
|
|
|
#endif
|
|
|
|
|
|
-static struct ctl_state controllers[CONTROLLER_TYPES] = {
|
|
|
+/* This array is ~24K(!), probably needs to be moved to DRAM */
|
|
|
+static struct ctl_state __dram_data controllers[CONTROLLER_TYPES] = {
|
|
|
/*
|
|
|
* MOx: covers all of these formats:
|
|
|
* SSSD = 40×8×1 (80K, FD2/DD80),
|
|
@@ -127,7 +132,7 @@ static struct ctl_state controllers[CONTROLLER_TYPES] = {
|
|
|
.devsel = 36,
|
|
|
.clustshift = 5,
|
|
|
.newaddr = true, /* Actually irrelevant for clustshift = 5 */
|
|
|
- .maxsectors = (239 * 32 - 1) * 32, /* Maximum supported by UFD-DOS */
|
|
|
+ .maxsectors = (239 * 8 - 1) * 32, /* Maximum supported by UFD-DOS */
|
|
|
.c = 238, .h = 16, .s = 64,
|
|
|
.name = "hd"
|
|
|
}
|
|
@@ -176,11 +181,16 @@ static inline unsigned int phys_sector(const struct ctl_state *state)
|
|
|
return virt2phys(cur_drv(state), cur_sector(state));
|
|
|
}
|
|
|
|
|
|
-static inline off_t file_pos(const struct ctl_state *state)
|
|
|
+static inline unsigned int file_pos(const struct ctl_state *state)
|
|
|
{
|
|
|
return phys_sector(state) << 8;
|
|
|
}
|
|
|
|
|
|
+static inline bool file_pos_valid(const struct ctl_state *state)
|
|
|
+{
|
|
|
+ return phys_sector(state) < cur_drv(state)->sectors;
|
|
|
+}
|
|
|
+
|
|
|
static inline bool cur_sector_valid(const struct ctl_state *state)
|
|
|
{
|
|
|
uint8_t k3 = state->k[3];
|
|
@@ -188,12 +198,7 @@ static inline bool cur_sector_valid(const struct ctl_state *state)
|
|
|
if (!state->newaddr && ((k3 & 31) >> state->clustshift))
|
|
|
return false;
|
|
|
|
|
|
- return cur_sector(state) < cur_drv(state)->sectors;
|
|
|
-}
|
|
|
-
|
|
|
-static inline bool file_pos_valid(const struct ctl_state *state)
|
|
|
-{
|
|
|
- return file_pos(state) < cur_drv(state)->hf->filesize - 255;
|
|
|
+ return phys_sector(state) < state->maxsectors;
|
|
|
}
|
|
|
|
|
|
static inline uint8_t *cur_buf(struct ctl_state *state)
|
|
@@ -212,7 +217,12 @@ static void sync_drives(struct ctl_state *state)
|
|
|
if (state->drv[i].flags & DF_MOUNTED)
|
|
|
f_sync(&state->drv[i].file);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+static void disk_set_error(struct ctl_state *state, unsigned int error)
|
|
|
+{
|
|
|
+ abc_set_inp_default(&state->iodev, error);
|
|
|
+}
|
|
|
+
|
|
|
static void disk_reset_state(struct ctl_state *state)
|
|
|
{
|
|
|
abc_set_inp_status(&state->iodev, 0);
|
|
@@ -223,16 +233,9 @@ static void disk_reset_state(struct ctl_state *state)
|
|
|
disk_start_command(state);
|
|
|
}
|
|
|
|
|
|
-static void disk_set_error(struct ctl_state *state, unsigned int error)
|
|
|
-{
|
|
|
- state->error = error;
|
|
|
- abc_set_inp_default(&state->iodev, error);
|
|
|
-}
|
|
|
-
|
|
|
static struct drive_state *
|
|
|
name_to_drive(const char *drive)
|
|
|
{
|
|
|
- int sel;
|
|
|
struct ctl_state *state;
|
|
|
unsigned int ndrive;
|
|
|
|
|
@@ -246,15 +249,14 @@ name_to_drive(const char *drive)
|
|
|
|
|
|
if (!memcmp("dr", drive, 2)) {
|
|
|
/* DRx alias for MOx (matches "old DOS") */
|
|
|
- return &mo_state.drv[ndrive];
|
|
|
+ return &controllers[MOx].drv[ndrive];
|
|
|
}
|
|
|
|
|
|
- for (sel = 0; sel < 64; sel++) {
|
|
|
- state = sel_to_state[sel];
|
|
|
- if (!state)
|
|
|
- continue;
|
|
|
+ for (int i = 0; i < CONTROLLER_TYPES; i++) {
|
|
|
+ struct ctl_state *state = &controllers[i];
|
|
|
+
|
|
|
if (!memcmp(state->name, drive, 2))
|
|
|
- return &state->drv[ndrive];
|
|
|
+ return &state->drv[ndrive];
|
|
|
}
|
|
|
|
|
|
return NULL; /* No such disk */
|
|
@@ -265,7 +267,8 @@ bool valid_drive_name(const char *drive)
|
|
|
return name_to_drive(drive) != NULL;
|
|
|
}
|
|
|
|
|
|
-static int mount_drive(struct drive_state *drv, const char *filename)
|
|
|
+static int mount_drive(struct drive_state *drv, struct ctl_state *state,
|
|
|
+ const char *filename)
|
|
|
{
|
|
|
if (mounted(drv)) {
|
|
|
f_close(&drv->file);
|
|
@@ -309,9 +312,8 @@ static int mount_drive(struct drive_state *drv, const char *filename)
|
|
|
* Smaller than the standard disk size? Treat the sectors
|
|
|
* beyond the end as bad.
|
|
|
*/
|
|
|
- unsigned int filesec = f_size(drv->file) >> 8;
|
|
|
- drv->sectors = (filesec && filesec < state->maxsectors)
|
|
|
- ? filesec : state->maxsectors;
|
|
|
+ unsigned int filesec = f_size(&drv->file) >> 8;
|
|
|
+ drv->sectors = (filesec < state->maxsectors) ? filesec : state->maxsectors;
|
|
|
|
|
|
/* Interleaving parameters */
|
|
|
#if INTERLEAVE
|
|
@@ -327,7 +329,7 @@ static int mount_drive(struct drive_state *drv, const char *filename)
|
|
|
static void abcdisk_callback_out(struct abc_dev *dev, uint8_t data)
|
|
|
{
|
|
|
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->pending |= PEND_IO;
|
|
@@ -349,8 +351,9 @@ static void abcdisk_callback_cmd(struct abc_dev *dev, uint8_t data, uint8_t addr
|
|
|
state->pending |= PEND_RESET;
|
|
|
}
|
|
|
|
|
|
-static const char * const disk_pfx_abc80[] = { "/abcdisk.80/", "/abcdisk/", "/", NULL };
|
|
|
-static const char * const disk_pfx_abc800[] = { "/abcdisk.800/", "/abcdisk/", "/", NULL };
|
|
|
+static char * const disk_pfx[] = {
|
|
|
+ "/abcdisk.800/", "/abcdisk/", "/abcdisk.", "/", NULL
|
|
|
+};
|
|
|
|
|
|
static void init_drives(struct ctl_state *state)
|
|
|
{
|
|
@@ -364,9 +367,10 @@ static void init_drives(struct ctl_state *state)
|
|
|
disk_init_status = disk_init();
|
|
|
disk_initialized = true;
|
|
|
}
|
|
|
-
|
|
|
- pfx_list = (abc_status & ABC_STATUS_800) ? disk_pfx_abc800 : disk_pfx_abc80;
|
|
|
-
|
|
|
+
|
|
|
+ /* .80/ or .800/ for the first entry */
|
|
|
+ strcpy(disk_pfx[0] + 10 + !!(abc_status & ABC_STATUS_800), "0/");
|
|
|
+
|
|
|
/* If any of these don't exist we simply report device not ready */
|
|
|
state->drives = 0;
|
|
|
|
|
@@ -374,15 +378,15 @@ static void init_drives(struct ctl_state *state)
|
|
|
for (i = 0; i < 8; i++) {
|
|
|
struct drive_state *drv = &state->drv[i];
|
|
|
unsigned int filesec;
|
|
|
- const char **pfx;
|
|
|
-
|
|
|
+ char * const *pfx;
|
|
|
+
|
|
|
snprintf(drv->name, sizeof drv->name, "%-.2s%c", state->name, i + '0');
|
|
|
-
|
|
|
- for (pfx = pfx_list; *pfx; pfx++) {
|
|
|
+
|
|
|
+ for (pfx = disk_pfx; *pfx; pfx++) {
|
|
|
char filename_buf[64];
|
|
|
snprintf(filename_buf, sizeof filename_buf, "%s%s", *pfx, drv->name);
|
|
|
-
|
|
|
- if (!mount_drive(drv, filename_buf)) {
|
|
|
+
|
|
|
+ if (!mount_drive(drv, state, filename_buf)) {
|
|
|
state->drives++;
|
|
|
break;
|
|
|
}
|
|
@@ -399,11 +403,11 @@ static void do_next_command(struct ctl_state *state)
|
|
|
if (state->k[0] & 0x01) {
|
|
|
/* READ SECTOR */
|
|
|
if (!(drv->flags & DF_MOUNTED)) {
|
|
|
- disk_set_error(DISK_NOT_READY);
|
|
|
+ disk_set_error(state, DISK_NOT_READY);
|
|
|
} else if (!cur_sector_valid(state)) {
|
|
|
- disk_set_error(OUT_OF_RANGE);
|
|
|
+ disk_set_error(state, OUT_OF_RANGE);
|
|
|
} else if (!file_pos_valid(state)) {
|
|
|
- disk_set_error(CRC_ERROR);
|
|
|
+ disk_set_error(state, CRC_ERROR);
|
|
|
} else {
|
|
|
UINT rlen = 0;
|
|
|
FRESULT rv;
|
|
@@ -412,7 +416,7 @@ static void do_next_command(struct ctl_state *state)
|
|
|
if (rv == FR_OK)
|
|
|
rv = f_read(&drv->file, buf, 256, &rlen);
|
|
|
if (rv != FR_OK || rlen != 256) {
|
|
|
- disk_set_error(CRC_ERROR);
|
|
|
+ disk_set_error(state, CRC_ERROR);
|
|
|
}
|
|
|
}
|
|
|
state->k[0] &= ~0x01; /* Command done */
|
|
@@ -432,25 +436,24 @@ static void do_next_command(struct ctl_state *state)
|
|
|
if (state->k[0] & 0x08) {
|
|
|
/* WRITE SECTOR */
|
|
|
if (!(drv->flags & DF_MOUNTED)) {
|
|
|
- disk_set_error(DISK_NOT_READY);
|
|
|
- } else if (df->flags & DF_READONLY) {
|
|
|
- disk_set_error(WRITE_PROTECT);
|
|
|
+ disk_set_error(state, DISK_NOT_READY);
|
|
|
+ } else if (drv->flags & DF_READONLY) {
|
|
|
+ disk_set_error(state, WRITE_PROTECT);
|
|
|
} else if (!cur_sector_valid(state)) {
|
|
|
- disk_set_error(OUT_OF_RANGE);
|
|
|
+ disk_set_error(state, OUT_OF_RANGE);
|
|
|
} else if (!file_pos_valid(state)) {
|
|
|
- disk_set_error(CRC_ERROR);
|
|
|
+ disk_set_error(state, CRC_ERROR);
|
|
|
} else {
|
|
|
UINT wlen = 0;
|
|
|
FRESULT rv;
|
|
|
-
|
|
|
- rv = f_lseek(&state->file, file_pos(state));
|
|
|
+
|
|
|
+ rv = f_lseek(&drv->file, file_pos(state));
|
|
|
if (rv == FR_OK)
|
|
|
- rv = f_write(&state->file, buf, 256, &wlen);
|
|
|
+ rv = f_write(&drv->file, buf, 256, &wlen);
|
|
|
if (rv != FR_OK || wlen != 256)
|
|
|
- disk_set_error(WRITE_FAULT);
|
|
|
- }
|
|
|
+ disk_set_error(state, WRITE_FAULT);
|
|
|
}
|
|
|
- state->k[0] &= ~0x08; /* Command done */
|
|
|
+ state->k[0] &= ~0x08; /* Command done */
|
|
|
}
|
|
|
|
|
|
#if FORMAT_SUPPORT
|
|
@@ -568,7 +571,7 @@ void abcdisk_io_poll(void)
|
|
|
uint32_t now = rdtime();
|
|
|
bool need_sync = (now - last_sync) >= SYNC_TIME;
|
|
|
last_sync = now;
|
|
|
-
|
|
|
+
|
|
|
for (int i = 0; i < CONTROLLER_TYPES; i++) {
|
|
|
struct ctl_state *state = &controllers[i];
|
|
|
enum pending pending;
|
|
@@ -605,18 +608,18 @@ void abcdisk_io_poll(void)
|
|
|
void abcdisk_init(void)
|
|
|
{
|
|
|
for (int i = 0; i < CONTROLLER_TYPES; i++) {
|
|
|
- struct state *state = &controllers[i];
|
|
|
+ struct ctl_state *state = &controllers[i];
|
|
|
|
|
|
/* Set up the bus device */
|
|
|
memset(&state->iodev, 0, sizeof state->iodev);
|
|
|
state->iodev.callback_mask = IDLE_CALLBACK_MASK;
|
|
|
- state->iodev.state_first_out_mask = ~0x80;
|
|
|
- state->iodev.state_first_inp_mask = ~0x80;
|
|
|
- state->callback_out = abcdisk_callback_out;
|
|
|
- state->callback_inp = abcdisk_callback_inp;
|
|
|
- state->callback_cmd = abcdisk_callback_cmd;
|
|
|
-
|
|
|
+ state->iodev.status_first_out_mask = (uint8_t)~0x80;
|
|
|
+ state->iodev.status_first_inp_mask = (uint8_t)~0x80;
|
|
|
+ state->iodev.callback_out = abcdisk_callback_out;
|
|
|
+ state->iodev.callback_inp = abcdisk_callback_inp;
|
|
|
+ state->iodev.callback_cmd = abcdisk_callback_cmd;
|
|
|
+
|
|
|
disk_reset_state(state);
|
|
|
- abcio_register(&state->iodev, state->devsel);
|
|
|
+ abc_register(&state->iodev, state->devsel);
|
|
|
}
|
|
|
}
|