trs80m1: first pass towards decent floppy disk support
authorAlan Cox <alan@linux.intel.com>
Fri, 12 Oct 2018 21:03:05 +0000 (22:03 +0100)
committerAlan Cox <alan@linux.intel.com>
Fri, 12 Oct 2018 21:03:05 +0000 (22:03 +0100)
Kernel/platform-trs80m1/Makefile
Kernel/platform-trs80m1/devfd.c
Kernel/platform-trs80m1/devfd.h
Kernel/platform-trs80m1/devfd3.c [deleted file]
Kernel/platform-trs80m1/devices.c
Kernel/platform-trs80m1/floppy.s
Kernel/platform-trs80m1/floppy3.s
Kernel/platform-trs80m1/fuzix.lnk

index 5ce4fec..e1d415d 100644 (file)
@@ -8,7 +8,7 @@ ASRCS += tricks.s commonmem.s floppy.s floppy3.s stringy.s ide.s
 
 # buffers.c must be in CODE2, direct users are more convenient there as
 # the asm helpers then can avoid another switch
-C2SRCS = buffers.c devfd.c devfd3.c devhd.c
+C2SRCS = buffers.c devfd.c devhd.c
 # And these so CODE1 is under 32K
 C2SRCS += devices.c main.c devstringy.c devinput.c
 C2SRCS += devlpr.c devtty.c devgfx.c
index 7a15485..f25eb4e 100644 (file)
@@ -1,7 +1,30 @@
+/*
+ *     Higher level logic for TRS80 floppy disk interfaces and clones
+ *     on both Model 1 and Model 3
+ *
+ *     Things To Do
+ *     - Set sector size and shift values in the config calls
+ *     - Use those in the I/O loop so we can do varying sector sizes
+ *     - Handle double sided media (need to consider heads in the loop)
+ *     - Turn the step rate value into a step mask and use it in the asm code
+ *     - Teach the asm code about density
+ *     - Teach the asm code about double sided and maybe 128 byte sectors
+ *     - Rework density handling
+ *     - Write compensation
+ *     - Formatting
+ *     - Motor delay improvements
+ *     - Try slowing step rate on repeated errors ?
+ *     - 4 retries may not be sufficient ?
+ *     - Head jiggling before we try restore ?
+ *     - Tandy style doubler - how to pass that info ?
+ *     - Autodetect density/sides/geometry
+ */
 #include <kernel.h>
 #include <kdata.h>
 #include <printf.h>
+#include <fdc.h>
 #include <devfd.h>
+#include <trs80.h>
 
 #define MAX_FD 4
 
@@ -17,12 +40,117 @@ static uint8_t motorct;
 /* Extern as they live in common */
 extern uint8_t fd_map, fd_tab[MAX_FD];
 extern uint8_t fd_selected;
-extern uint8_t fd_cmd[6];
+extern uint8_t fd_cmd[7];
 
-/*
- *     We only support normal block I/O for the moment. We do need to
- *     add swapping!
- */
+static struct fd_ops *fops;
+
+static struct fd_ops fd1_ops = {
+    fd_restore,
+    fd_operation,
+    fd_motor_on
+};
+
+static struct fd_ops fd3_ops = {
+    fd3_restore,
+    fd3_operation,
+    fd3_motor_on
+};
+
+static struct fdcinfo fdcap[MAX_FD] = {
+    {
+        FDF_SD|FDF_DD|FDF_DS|FDF_SEC256|FDF_SEC512,
+        0,
+        80,
+        2,
+        12,
+        FDC_DSTEP|FDC_AUTO|FDC_SEC0,
+        FDC_FMT_17XX,
+        0, /* To calc worst case */
+        0,0,0
+    },
+    {
+        FDF_SD|FDF_DD|FDF_DS|FDF_SEC256|FDF_SEC512,
+        0,
+        80,
+        2,
+        12,
+        FDC_DSTEP|FDC_AUTO|FDC_SEC0,
+        FDC_FMT_17XX,
+        0, /* To calc worst case */
+        0,0,0
+    },
+    {
+        FDF_SD|FDF_DD|FDF_DS|FDF_SEC256|FDF_SEC512,
+        0,
+        80,
+        2,
+        12,
+        FDC_DSTEP|FDC_AUTO|FDC_SEC0,
+        FDC_FMT_17XX,
+        0, /* To calc worst case */
+        0,0,0
+    },
+    {
+        FDF_SD|FDF_DD|FDF_DS|FDF_SEC256|FDF_SEC512,
+        0,
+        80,
+        2,
+        12,
+        FDC_DSTEP|FDC_AUTO|FDC_SEC0,
+        FDC_FMT_17XX,
+        0, /* To calc worst case */
+        0,0,0
+    },
+};
+
+static struct fdcinfo fdc[MAX_FD] = {
+    {
+        FDF_DD|FDF_SEC512,
+        0,
+        40,
+        1,
+        9,
+        0,
+        FDC_FMT_17XX,
+        0, /* To calc worst case */
+        0,0,0
+    },
+    {
+        FDF_DD|FDF_SEC512,
+        0,
+        40,
+        1,
+        9,
+        0,
+        FDC_FMT_17XX,
+        0, /* To calc worst case */
+        0,0,0
+    },
+    {
+        FDF_DD|FDF_SEC512,
+        0,
+        40,
+        1,
+        9,
+        0,
+        FDC_FMT_17XX,
+        0, /* To calc worst case */
+        0,0,0
+    },
+    {
+        FDF_DD|FDF_SEC512,
+        0,
+        40,
+        1,
+        9,
+        0,
+        FDC_FMT_17XX,
+        0, /* To calc worst case */
+        0,0,0
+    },
+};
+
+/* Translate the drive into a selection. Assumes single sided on the M1 */
 static uint8_t selmap[4] = { 0x01, 0x02, 0x04, 0x08 };
 
 static int fd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
@@ -31,47 +159,69 @@ static int fd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
     int tries;
     uint8_t err = 0;
     uint8_t *driveptr = fd_tab + minor;
+    struct fdcinfo *f = fdc + minor;
 
+    /* You can't swap to floppy */
     if(rawflag == 2)
         goto bad2;
 
-    /* FIXME: We force DD for now */
-    err = fd_motor_on(selmap[minor]|0x80);
-    if (err)
-        goto bad;
+    /* Do we need to select the drive ? */
+    if (trs80_model != TRS80_MODEL3 || fd_selected != minor) {
+        uint8_t err;
+        uint8_t tmp = 0;
+        /* Decide if we need double density */
+        if (f->features & FDF_DD)
+            tmp |= 0x80;
+        err = fops->fd_motor_on(selmap[minor]|tmp);
+        if (err)
+            goto bad;
+    }
 
+    /* If we don't know where the head on this drive is then force
+       a seek */
     if (*driveptr == 0xFF)
-        fd_reset(driveptr);
+        if (err = fops->fd_restore(driveptr))
+            goto bad;
 
+    /* Adjust for the block size if raw I/O. For now hard code 512 byte
+       sectors but we need to sort this */
     fd_map = rawflag;
     if (rawflag && d_blkoff(BLKSHIFT))
             return -1;
 
-    udata.u_nblock *= 2;
-
+    /* We only deal with single sided 512 byte/sector media for the moment */
     fd_cmd[0] = is_read ? FD_READ : FD_WRITE;
-    fd_cmd[1] = udata.u_block / 9;             /* 2 sectors per block */
-    fd_cmd[2] = ((udata.u_block % 9) << 1) + 1;        /*eww.. */
     fd_cmd[3] = is_read ? OPDIR_READ: OPDIR_WRITE;
     fd_cmd[4] = ((uint16_t)udata.u_dptr) & 0xFF;
     fd_cmd[5] = ((uint16_t)udata.u_dptr) >> 8;
+    fd_cmd[6] = 2; /* 0 128 1 256 2 512 : fixed for now */
 
     while (ct < udata.u_nblock) {
+        /* For each block we need to load we work out where to find it */
+        fd_cmd[1] = udata.u_block / f->sectors;
+        fd_cmd[2] = udata.u_block % f->sectors;
+        /* Some single density media has sectors numbered from zero */
+        if (!(f->config & FDC_SEC0))
+            fd_cmd[2]++;
+        /* Reading 40 track media on an 80 track drive */
+        if (f->config & FDC_DSTEP)
+            fd_cmd[1] <<= 1;
+        /* Now try the I/O */
         for (tries = 0; tries < 4 ; tries++) {
-            err = fd_operation(driveptr);
+            err = fops->fd_op(driveptr);
             if (err == 0)
                 break;
+            /* Reposition the head */
             if (tries > 1)
-                fd_reset(driveptr);
+                fops->fd_restore(driveptr);
         }
-        /* FIXME: should we try the other half and then bale out ? */
         if (tries == 4)
             goto bad;
         fd_cmd[5]++;   /* Move on 256 bytes in the buffer */
-        fd_cmd[2]++;   /* Next sector for 2nd block */
+        fd_cmd[5]++;   /* Move on 256 bytes in the buffer */
         ct++;
     }
-    return udata.u_nblock << 8;
+    return udata.u_nblock << 9;
 bad:
     kprintf("fd%d: error %x\n", minor, err);
 bad2:
@@ -86,6 +236,11 @@ int fd_open(uint8_t minor, uint16_t flag)
         udata.u_error = ENODEV;
         return -1;
     }
+    /* No media ? */
+    if (fops->fd_restore(fd_tab + minor) && !(flag & O_NDELAY)) {
+        udata.u_error = EIO;
+        return -1;
+    }
     return 0;
 }
 
@@ -100,3 +255,38 @@ int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
     flag;rawflag;minor;
     return fd_transfer(minor, false, rawflag);
 }
+
+int fd_ioctl(uint8_t minor, uarg_t request, char *buffer)
+{
+    switch(request) {
+        case FDIO_GETCAP:
+            return uput(fdcap + minor, buffer, sizeof(struct fdcinfo));
+        case FDIO_GETINFO:
+            return uput(fdc + minor, buffer, sizeof(struct fdcinfo));
+        case FDIO_SETINFO:
+            /* Ick.. but we are not portable code so we know how it packs */
+            if (uget(fdc + minor, buffer, 7))
+                return -1;
+            /*FIXME when we sort DS meida */
+            fdc[minor].heads = 1;
+            /* TODO : steprate to masks */
+            fdc[minor].features &= fdcap[minor].features;
+            /* Force reconfiguration */
+            fd_tab[minor] = 0xFF;
+            fd_selected = 255;
+            return 0;
+        case FDIO_FMTTRK:
+            return -1;
+        case FDIO_RESTORE:
+            return fops->fd_restore(fd_tab + minor);
+    }
+    return -1;
+}
+
+void floppy_setup(void)
+{
+    if (trs80_model == TRS80_MODEL3)
+        fops = &fd3_ops;
+    else
+        fops = &fd1_ops;
+}
index 672cf90..0bc76af 100644 (file)
@@ -7,9 +7,19 @@ int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
 int fd_open(uint8_t minor, uint16_t flag);
 
 /* low level interface */
-uint16_t fd_reset(uint8_t *driveptr);
-uint16_t fd_operation(uint8_t *driveptr);
-uint16_t fd_motor_on(uint16_t drivesel);
-uint16_t fd_motor_off(uint16_t driveptr);
+uint8_t fd_restore(uint8_t *driveptr) __z88dk_fastcall;
+uint16_t fd_operation(uint8_t *driveptr) __z88dk_fastcall;
+uint16_t fd_motor_on(uint16_t drivesel) __z88dk_fastcall;
 
+/* low level interface */
+uint8_t fd3_restore(uint8_t *driveptr) __z88dk_fastcall;
+uint16_t fd3_operation(uint8_t *driveptr) __z88dk_fastcall;
+uint16_t fd3_motor_on(uint16_t drivesel) __z88dk_fastcall;
+
+struct fd_ops {
+    uint8_t (*fd_restore)(uint8_t *driveptr) __z88dk_fastcall;
+    uint16_t (*fd_op)(uint8_t *driveptr) __z88dk_fastcall;
+    uint16_t (*fd_motor_on)(uint16_t drivesel) __z88dk_fastcall;
+};
+    
 #endif /* __DEVFD_DOT_H__ */
diff --git a/Kernel/platform-trs80m1/devfd3.c b/Kernel/platform-trs80m1/devfd3.c
deleted file mode 100644 (file)
index 6dd7520..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-#include <kernel.h>
-#include <kdata.h>
-#include <printf.h>
-#include <devfd3.h>
-
-#define MAX_FD 4
-
-#define OPDIR_NONE     0
-#define OPDIR_READ     1
-#define OPDIR_WRITE    2
-
-#define FD_READ                0x80    /* 2797 needs 0x88, 1797 needs 0x80 */
-#define FD_WRITE       0xA0    /* Likewise A8 v A0 */
-
-static uint8_t motorct;
-
-/* Extern as they live in common */
-extern uint8_t fd_map, fd_tab[MAX_FD];
-extern uint8_t fd_selected;
-extern uint8_t fd_cmd[7];
-
-/*
- *     We only support normal block I/O for the moment. We do need to
- *     add swapping!
- */
-static uint8_t selmap[4] = { 0x01, 0x02, 0x04, 0x08 };
-
-static int fd3_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
-{
-    int ct = 0;
-    int tries;
-    uint8_t err = 0;
-    uint8_t *driveptr = fd_tab + minor;
-
-    if(rawflag == 2)
-        goto bad2;
-
-    if (fd_selected != minor) {
-        uint8_t err;
-        /* FIXME: We force DD for now */
-        err = fd3_motor_on(selmap[minor]|0x80);
-        if (err)
-            goto bad;
-    }
-
-    if (*driveptr == 0xFF)
-        fd3_reset(driveptr);
-
-    fd_map = rawflag;
-    if (rawflag && d_blkoff(BLKSHIFT))
-            return -1;
-
-    udata.u_nblock *= 2;
-
-    fd_cmd[0] = is_read ? FD_READ : FD_WRITE;
-    fd_cmd[1] = udata.u_block / 9;             /* 2 sectors per block */
-    fd_cmd[2] = ((udata.u_block % 9) << 1) + 1;        /*eww.. */
-    fd_cmd[3] = is_read ? OPDIR_READ: OPDIR_WRITE;
-    fd_cmd[4] = ((uint16_t)udata.u_dptr) & 0xFF;
-    fd_cmd[5] = ((uint16_t)udata.u_dptr) >> 8;
-    fd_cmd[6] = 1;                             /* For now . FIXME */
-
-    while (ct < udata.u_nblock) {
-        for (tries = 0; tries < 4 ; tries++) {
-            err = fd3_operation(driveptr);
-            if (err == 0)
-                break;
-            if (tries > 1)
-                fd3_reset(driveptr);
-        }
-        /* FIXME: should we try the other half and then bale out ? */
-        if (tries == 4)
-            goto bad;
-        fd_cmd[5]++;   /* Move on 256 bytes in the buffer */
-        fd_cmd[2]++;   /* Next sector for 2nd block */
-        ct++;
-    }
-    return udata.u_nblock << 8;
-bad:
-    kprintf("fd%d: error %x\n", minor, err);
-bad2:
-    udata.u_error = EIO;
-    return -1;
-}
-
-int fd3_open(uint8_t minor, uint16_t flag)
-{
-    flag;
-    if(minor >= MAX_FD) {
-        udata.u_error = ENODEV;
-        return -1;
-    }
-    return 0;
-}
-
-int fd3_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
-{
-    flag;
-    return fd3_transfer(minor, true, rawflag);
-}
-
-int fd3_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
-{
-    flag;rawflag;minor;
-//    return 0;
-    return fd3_transfer(minor, false, rawflag);
-}
index 90159b4..6bca6b4 100644 (file)
@@ -38,10 +38,6 @@ struct devsw dev_tab[] =  /* The device driver switch table */
   {  blkdev_open, no_close,     blkdev_read,   blkdev_write,   blkdev_ioctl  },
 };
 
-static struct devsw floppy3 = {
-  fd3_open,     no_close,     fd3_read,   fd3_write,   no_ioctl
-};
-
 bool validdev(uint16_t dev)
 {
     /* This is a bit uglier than needed but the right hand side is
@@ -51,9 +47,3 @@ bool validdev(uint16_t dev)
     else
         return true;
 }
-
-void floppy_setup(void)
-{
-  if (trs80_model == TRS80_MODEL3)
-    memcpy(dev_tab + 1, &floppy3, sizeof(struct devsw));
-}
index ed393d6..3b5ea25 100644 (file)
@@ -8,7 +8,7 @@
 ;      FIXME: precompensation ??
 ;      FIXME: 512 byte sector support
 ;
-       .globl _fd_reset
+       .globl _fd_restore
        .globl _fd_operation
        .globl _fd_motor_on
        .globl _fd_motor_off
@@ -280,13 +280,9 @@ fdio_xfer_out:
 ;
 ;      Reset to track 0, wait for the command then idle
 ;
-;      fd_reset(uint8_t *drvptr)
+;      fd_restore(uint8_t *drvptr)
 ;
-_fd_reset:
-       pop     de
-       pop     hl
-       push    hl
-       push    de
+_fd_restore:
        call    go_slow
        ld      a, #1
        ld      (FDCSEC), a
@@ -297,7 +293,9 @@ _fd_reset:
        ld      a, #0xFF
        ld      (hl), a         ; Zap track pointer
        call    waitcmd
+       ld      l,a
        call    go_fast
+       ld      a,l
        cp      #0xff
        ret     z
        and     #0x99           ; Error bit from the reset
@@ -305,16 +303,13 @@ _fd_reset:
        ld      (hl), a         ; Track 0 correctly hit (so 0)
        ret
 ;
-;      fd_operation(uint16_t *cmd, uint16_t *drive)
+;      fd_operation(uint8_t *cmd)
 ;
 ;      The caller must ensure the drive has been selected and the motor is
 ;      running.
 ;
 _fd_operation:
-       pop     bc              ; return address
-       pop     de              ; drive track ptr
-       push    de
-       push    bc
+       ex      de,hl
        push    ix
        ld      a, (_fd_map)
        or      a
@@ -368,11 +363,7 @@ waitdiskl:
 ;
 ;
 _fd_motor_on:
-       pop     de
-       pop     bc
-       push    bc
-       push    de
-
+       ld      c,l
        ;
        ;       Is the motor running ?
        ;
index 24a8a67..aa099c2 100644 (file)
@@ -17,7 +17,7 @@
 ;      FIXME: backport fixes to model 4
 ;
 ;
-       .globl _fd3_reset
+       .globl _fd3_restore
        .globl _fd3_operation
        .globl _fd3_motor_on
        .globl _fd3_motor_off
@@ -241,11 +241,11 @@ fdio_inl:
 fdio_inbyte:
        out     (FDCCTRL), a            ; stalls
        ini
-       jr      nz, fdio_inbyte
+       jp      nz, fdio_inbyte
 fdio_inbyte2:                          ; this is patched for I/O size
        out     (FDCCTRL), a            ; stalls
        ini
-       jr      nz, fdio_inbyte2
+       jp      nz, fdio_inbyte2
        jr      fdxferdone
 
 ;
@@ -287,7 +287,7 @@ fdio_waitlock:
 fdio_outbyte:
        out     (FDCCTRL), a            ; stalls
        outi
-       jr      nz,fdio_outbyte
+       jp      nz,fdio_outbyte
        
 fdio_nmiout:
 ;
@@ -306,13 +306,7 @@ fdxferbad:
 ;
 ;      fd_reset3(uint8_t *drvptr)
 ;
-_fd3_reset:
-       pop     bc
-       pop     de
-       pop     hl
-       push    hl
-       push    de
-       push    bc
+_fd3_restore:
        ld      a, (fdcctrl)
        out     (FDCCTRL), a
        ld      a, #1
@@ -328,6 +322,7 @@ _fdr_wait:
        djnz    _fdr_wait
        
        call    waitdisk
+       ld      l,a
        cp      #0xff
        ret     z
        and     #0x99           ; Error bit from the reset
@@ -335,21 +330,16 @@ _fdr_wait:
        ld      (hl), a         ; Track 0 correctly hit (so 0)
        ret
 ;
-;      fd_operation3(uint16_t *drive)
+;      fd_operation3(uint8_t *driveptr)
 ;
 ;      The caller must ensure the drive has been selected and the motor is
 ;      running.
 ;
 _fd3_operation:
+       ex      de,hl           ; arg into de
        ld      a, (_fd_map)
        or      a
        call    nz, map_process_always
-       pop     hl              ; banked
-       pop     bc              ; return address
-       pop     de              ; drive track ptr
-       push    de
-       push    bc
-       push    hl
        push    ix
        ld      ix, #_fd_cmd
        ld      l, DATA(ix)
@@ -376,12 +366,6 @@ _fd3_operation:
 ;
 ;
 _fd3_motor_on:
-       pop     bc
-       pop     de
-       pop     hl
-       push    hl
-       push    de
-       push    bc
        ;
        ;       Select drive B, turn on motor if needed
        ;
@@ -397,9 +381,9 @@ _fd3_motor_on:
 notsel:
        ld      a, l
        out     (FDCCTRL), a
-       out     (FDCCTRL), a    ; TRS80 erratum apparently needs this
+       out     (FDCCTRL), a    ; TRS80 erratum: model 4 gate array apparently needs this
        ld      (fdcctrl), a
-       ld      bc, #0x7F00     ; Long delay (may need FE or FF for some disks)
+       ld      bc, #0x7F00     ; Long delay (may need FE or FF for some disks?)
        call    nap
        ; FIXME: longer motor spin up delay goes here (0.5 or 1 second)
        
index 103543c..d8eb335 100644 (file)
@@ -20,7 +20,6 @@ platform-trs80m1/main.rel
 timer.rel
 kdata.rel
 platform-trs80m1/devfd.rel
-platform-trs80m1/devfd3.rel
 platform-trs80m1/floppy.rel
 platform-trs80m1/floppy3.rel
 platform-trs80m1/devhd.rel