dragon: first cut at a floppy driver
authorAlan Cox <alan@linux.intel.com>
Fri, 5 Dec 2014 17:55:03 +0000 (17:55 +0000)
committerAlan Cox <alan@linux.intel.com>
Fri, 5 Dec 2014 17:55:03 +0000 (17:55 +0000)
Completely untested at this point

Kernel/platform-dragon/Makefile
Kernel/platform-dragon/devfd.c [new file with mode: 0644]
Kernel/platform-dragon/devfd.h [new file with mode: 0644]
Kernel/platform-dragon/devices.c
Kernel/platform-dragon/devrd.c [deleted file]
Kernel/platform-dragon/devrd.h [deleted file]
Kernel/platform-dragon/floppy.s [new file with mode: 0644]
Kernel/platform-dragon/p6809.s

index 17aa8ce..531df8f 100644 (file)
@@ -1,9 +1,9 @@
 
-CSRCS = devlpr.c devtty.c devrd.c
+CSRCS = devlpr.c devtty.c devfd.c
 CSRCS += devices.c main.c libc.c
 
 ASRCS = p6809.s crt0.s
-ASRCS += tricks.s commonmem.s usermem_sam.s
+ASRCS += tricks.s commonmem.s usermem_sam.s floppy.s
 
 COBJS = $(CSRCS:.c=$(BINEXT))
 AOBJS = $(ASRCS:.s=$(BINEXT))
@@ -30,7 +30,7 @@ image: d64_1.rom
        setup.o \
        crt0.o commonmem.o usermem_sam.o \
        p6809.o ../start.o ../version.o ../lowlevel-6809.o \
-       tricks.o main.o ../timer.o ../kdata.o devrd.o devices.o \
+       tricks.o main.o ../timer.o ../kdata.o devfd.o floppy.o devices.o \
        ../devio.o ../filesys.o ../process.o ../inode.o ../syscall_fs.o \
        ../syscall_proc.o ../syscall_other.o ../mm.o ../swap.o ../single.o \
        ../tty.o ../devsys.o ../usermem.o ../syscall_fs2.o ../syscall_exec.o \
diff --git a/Kernel/platform-dragon/devfd.c b/Kernel/platform-dragon/devfd.c
new file mode 100644 (file)
index 0000000..1d3443a
--- /dev/null
@@ -0,0 +1,123 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devfd.h>
+
+#define MAX_FD 4
+
+#define OPDIR_READ     0
+#define OPDIR_NONE     1
+#define OPDIR_WRITE    2
+
+#define FD_READ                0x88    /* 2797 needs 0x88, 1797 needs 0x80 */
+#define FD_WRITE       0xA8    /* Likewise A8 v A0 */
+
+static uint8_t motorct;
+static uint8_t fd_selected = 0xFF;
+static uint8_t fd_tab[MAX_FD];
+
+static void fd_motor_busy(void)
+{
+    motorct++;
+}
+
+static void fd_motor_idle(void)
+{
+    motorct--;
+    // if (motorct == 0) ... start timer */
+}
+
+static void fd_motor_timeout(void)
+{
+    fd_selected = 0xff;
+    fd_motor_off();
+}
+
+/*
+ *     We only support normal block I/O because otherwise we'd need
+ *     bounce buffers - which would make it just as pointless!
+ *
+ *     The Dragon and COCO have 18 x 256 byte sectors per track. We
+ *     use them in pairs. We assume an even sectors per track. This is fine
+ *     for our usage but would break for single density media.
+ */
+
+static int fd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+    blkno_t block;
+    uint16_t dptr;
+    int ct = 0;
+    int tries;
+    uint8_t err;
+    uint8_t *driveptr = fd_tab + minor;
+    uint16_t cmd[5];
+
+    /* FIXME: raw is broken unless nicely aligned */
+
+    if(rawflag)
+        goto bad2;
+
+    fd_motor_busy();           /* Touch the motor timer first so we don't
+                                   go and turn it off as we are doing this */
+    if (fd_selected != minor) {
+        uint8_t err = fd_motor_on(driveptr);
+        if (err)
+            goto bad;
+    }
+
+    dptr = (uint16_t)udata.u_buf->bf_data;
+    block = udata.u_buf->bf_blk;
+
+    cmd[0] = is_read ? FD_READ : FD_WRITE;
+    cmd[1] = block / 18;
+    cmd[2] = (block % 18) + 1; /*eww.. */
+    cmd[3] = minor;    /* FIXME: other bits ? */
+    cmd[4] = is_read ? OPDIR_READ: OPDIR_WRITE;
+    cmd[5] = block << 8;
+    cmd[6] = block & 0xFF;
+        
+    while (ct < 2) {
+        for (tries = 0; tries < 3 ; tries++) {
+            err = fd_operation(cmd, driveptr);
+            if (err == 0)
+                break;
+            if (tries > 1)
+                fd_reset(driveptr);
+        }
+        /* FIXME: should we try the other half and then bale out ? */
+        if (tries == 3)
+            goto bad;
+        cmd[5]++;      /* Move on 256 bytes in the buffer */
+        cmd[2]++;      /* Next sector for 2nd block */
+        ct++;
+    }
+    fd_motor_idle();
+    return 1;
+bad:
+    kprintf("fd%d: error %x\n", minor, err);
+bad2:
+    fd_motor_idle();
+    udata.u_error = EIO;
+    return -1;
+
+}
+
+int fd_open(uint8_t minor, uint16_t flag)
+{
+    if(minor > MAX_FD) {
+        udata.u_error = ENODEV;
+        return -1;
+    }
+    return 0;
+}
+
+int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    return fd_transfer(minor, true, rawflag);
+}
+
+int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    return fd_transfer(minor, false, rawflag);
+}
+
diff --git a/Kernel/platform-dragon/devfd.h b/Kernel/platform-dragon/devfd.h
new file mode 100644 (file)
index 0000000..2354e36
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __DEVFD_DOT_H__
+#define __DEVFD_DOT_H__
+
+/* public interface */
+int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int fd_open(uint8_t minor, uint16_t flag);
+
+/* low level interface */
+uint8_t fd_reset(uint8_t *drive);
+uint8_t fd_operation(uint16_t *cmd, uint8_t *drive);
+uint8_t fd_motor_on(uint8_t *drive);
+uint8_t fd_motor_off(void);
+
+#endif /* __DEVFD_DOT_H__ */
+
index 0cfe40b..60723b4 100644 (file)
@@ -1,7 +1,7 @@
 #include <kernel.h>
 #include <version.h>
 #include <kdata.h>
-#include <devrd.h>
+#include <devfd.h>
 #include <devsys.h>
 #include <devlpr.h>
 #include <tty.h>
@@ -12,7 +12,7 @@ struct devsw dev_tab[] =  /* The device driver switch table */
 // minor    open         close        read      write       ioctl
 // -----------------------------------------------------------------
   /* 0: /dev/fd                Floppy disc block devices  */
-  {  rd_open,     no_close,    rd_read,   rd_write,   no_ioctl },
+  {  fd_open,     no_close,    fd_read,   fd_write,   no_ioctl },
   /* 1: /dev/hd                Hard disc block devices (absent) */
   {  nxio_open,     no_close,    no_rdwr,   no_rdwr,   no_ioctl },
   /* 2: /dev/tty       TTY devices */
diff --git a/Kernel/platform-dragon/devrd.c b/Kernel/platform-dragon/devrd.c
deleted file mode 100644 (file)
index ddbf8b3..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/* 
- * NC100 RD PCMCIA driver
- *
- */
-
-#include <kernel.h>
-#include <kdata.h>
-#include <printf.h>
-#include <devrd.h>
-
-static int rd_transfer(bool is_read, uint8_t rawflag)
-{
-    blkno_t block;
-    int block_xfer;
-    uint16_t dptr;
-    int dlen;
-    int ct = 0;
-    int map;
-
-    /* FIXME: raw is broken unless nicely aligned */
-    if(rawflag) {
-        dlen = udata.u_count;
-        dptr = (uint16_t)udata.u_base;
-        if (dptr & 0x1FF) {
-            udata.u_error = EIO;
-            return -1;
-        }
-        block = udata.u_offset >> 9;
-        block_xfer = dlen >> 9;
-        map = 1;
-    } else { /* rawflag == 0 */
-        dlen = 512;
-        dptr = (uint16_t)udata.u_buf->bf_data;
-        block = udata.u_buf->bf_blk;
-        block_xfer = 1;
-        map = 0;
-    }
-    block += 2*320;    /* ramdisc starts at 320K in */
-        
-    while (ct < block_xfer) {
-/*        rd_memcpy(is_read, map, dptr, block); */
-        block++;
-        ct++;
-    }
-    return ct;
-}
-
-int rd_open(uint8_t minor, uint16_t flag)
-{
-    flag;
-    if(minor != 0) {
-        udata.u_error = ENODEV;
-        return -1;
-    }
-    return 0;
-}
-
-int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
-{
-    flag;minor;
-    return rd_transfer(true, rawflag);
-}
-
-int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
-{
-    flag;minor;
-    return rd_transfer(false, rawflag);
-}
-
diff --git a/Kernel/platform-dragon/devrd.h b/Kernel/platform-dragon/devrd.h
deleted file mode 100644 (file)
index 6320b26..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef __DEVRD_DOT_H__
-#define __DEVRD_DOT_H__
-
-/* public interface */
-int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
-int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
-int rd_open(uint8_t minor, uint16_t flag);
-
-/* asm banking helper */
-void rd_memcpy(uint8_t isread, uint8_t map, uint16_t dptr, uint16_t block);
-
-#endif /* __DEVRD_DOT_H__ */
-
diff --git a/Kernel/platform-dragon/floppy.s b/Kernel/platform-dragon/floppy.s
new file mode 100644 (file)
index 0000000..789ea06
--- /dev/null
@@ -0,0 +1,307 @@
+;
+;      Core floppy routines for the Dragon
+;
+
+       .globl  fd_nmi_handler
+       .globl  nmi_handler
+
+       .globl _fd_reset
+       .globl _fd_operation
+       .globl _fd_motor_on
+       .globl _fd_motor_off
+;
+;      MMIO for the floppy controller
+;
+;
+;      These four are a normal WD2797 under DragonDOS
+;
+FDCREG EQU     0xFF40
+FDCTRK EQU     0xFF41
+FDCSEC EQU     0xFF42
+FDCDATA        EQU     0xFF43
+;
+;      This is the control logic
+;
+FDCCTRL        EQU     0xFF48          ; drive select and motors
+;      bit5: NMI enable, 4: Precomp ?, 3: Density, 2: Motor, 
+;      bits 0-1 are drive nymber 0-3
+
+
+;
+;      Structures we use
+;
+;
+;      Per disk structure to hold device state
+;
+TRKCOPY        EQU     0
+
+;
+;      Command issue
+;
+CMD    EQU     0
+TRACK  EQU     1
+SECTOR EQU     2
+DRIVESEL EQU   3
+DIRECT EQU     4               ; 0 = read 2 = write 1 = status
+DATA   EQU     5
+
+       .area   .text
+;
+;      NMI handling for the floppy drive
+;
+fd_nmi_handler:
+       lda     FDCREG
+       ldy     nmivector
+       sty     10,s            ; overwrite the return PC
+       ldy     #nmi_handler
+       sty     nmivector       ; unexpected NMI trap
+       rti
+
+;
+;      Snooze to give the drive controller time to think
+;
+disknap:
+       ldx     #8750           ; assuming 0.9MHz
+disknapw:
+       leax    -1,x
+       bne     disknapw
+       rts
+
+;
+;      Wait for the drive controller to become ready
+;
+waitdisk:
+       ldx     #0              ; wait timer
+waitdisk_l:
+       leax    -1,x
+       beq     forceint        ; try forcing an interrupt
+       lda     FDCREG
+       bita    #0x01
+       bne     waitdisk_l
+       rts                     ; done, idle EQ true
+forceint:                      ; no response, bigger stick
+       lda     #0xD0           ; reset
+       sta     FDCREG
+       nop
+       exg     a,a
+       exg     a,a
+       lda     FDCREG          ; read to reset int status
+       ; ?? what to do next ??
+       lda     #0xff           ; force NEQ
+       rts
+
+;
+;      Set up the disk. On entry y points to our per drive data and
+;      x points to the command block
+;
+fdsetup:
+       lda     TRKCOPY,y
+       sta     FDCTRK          ; reset track register
+       pshs    x,y
+       cmpa    TRACK,x         ; target track
+       beq     fdiosetup
+       ;
+       ;       Need to seek the disk
+       ;
+       lda     #0x14
+       sta     FDCREG          ; seek
+       nop
+       exg     a,a
+       exg     a,a
+       jsr     waitdisk
+       bne     setuptimeout
+       jsr     disknap
+       anda    #0x18           ; error bits
+       beq     fdiosetup
+       ; seek failed, not good
+setuptimeout:                  ; NE = bad
+       puls    x,y
+       lda     FDCTRK          ; we have no idea where we are
+       sta     TRKCOPY,y       ; so remember what the drive reported
+       rts
+;
+;      Head in the right place
+;
+fdiosetup:
+       puls    x,y
+       lda     TRACK,x
+       sta     TRKCOPY,y       ; remember we arrived
+       ldb     FDCCTRL
+       andb    #0xEF
+       cmpa    #22
+       blo     noprecomp
+       orb     #0x10
+noprecomp:
+       stb     FDCCTRL         ; precomp configured
+       lda     SECTOR,x
+       sta     FDCSEC
+       lda     FDCREG          ; clear any pending int
+       lda     CMD,x           ; command to issue
+       ldy     #fdxferdone
+       sty     nmivector       ; so our NMI handler will clean up
+       ldy     #0              ; timeout handling
+       orcc    #0x10           ; irqs off or we'll miss bytes
+       sta     FDCREG          ; issue the command
+       nop                     ; give the FDC a moment to think
+       exg     a,a
+       exg     a,a
+       ldb     DIRECT,x
+       cmpb    #0x01           ; read ?
+       beq     fdio_in
+       cmpb    #0x02
+       beq     wait_drq        ; write
+;
+;      Status registers
+;
+fdxferdone:
+       andcc   #0xef
+       lda     FDCREG
+       anda    #0x7C           ; Returns with A holding the status bits
+       rts
+;
+;      Relies on B being 2...
+;
+wait_drq:
+       bitb    FDCREG
+       bne     drq_on
+       leay    -1,y
+       bne     wait_drq
+       ;
+       ;       Timed out - reset etc to clean up ??
+       ;
+       andcc   #0xef
+       lda     #0xff           ; our error code
+       rts
+
+;
+;      Once the controller decides it is finished it will flag an NMI
+;      and the NMI will switch the PC on the return to fdxferdone.
+;
+;      Begin the actual copy
+;
+drq_on:
+       ldy     DATA,x
+drq_loop:
+       ldb     ,y+
+       stb     FDCDATA         ; hardware will stall this for us
+       sta     FDCREG
+       bra     drq_loop
+
+;
+;      Write to the disk
+;
+fdio_in:
+       ldy     DATA,x
+fdio_loop:
+       ldb     FDCDATA         ; Sync is done by hardware
+       stb     ,y+
+       bra     fdio_loop       ; exit is via NMI to fdxferdone
+
+
+;
+;      C glue interface.
+;
+;      Because of the brain dead memory paging we dump the bits into
+;      kernel space always. The thought of taking an NMI while in the
+;      user memory and bank flipping to recover is just too odious !
+;
+
+;
+;      Reset to track 0, wait for the command then idle
+;
+;      fd_reset(uint16_t *drive)
+;
+_fd_reset:
+       pshs    x,y
+       lda     #0x00           ; seek
+       sta     FDCREG
+       nop
+       exg     a,a
+       exg     a,a
+       jsr     waitdisk
+       jsr     disknap
+       ; FIXME: longer delay goes here ???
+       cmpa    #0xff
+       beq     rstff           ; Total fail
+       anda    #0x10           ; Error bit from the reset
+rstff:
+       tfr a,b
+       puls    x, y, pc
+;
+;      fd_operation(uint16_t *cmd, uint16_t *drive)
+;
+;      The caller must ensure the drive has been selected and the motor is
+;      running.
+;
+_fd_operation:
+       pshs y
+       ldy 4,s         ; Drive struct
+       jsr fdsetup     ; Set up for a command
+       puls y
+       tfr a,b         ; Status code or 0xFF for total failure
+       rts
+;
+;      C interface fd_motor_on(uint8 drive)
+;
+;      Selects this drive and turns on the motors
+;
+_fd_motor_on:
+       pshs y
+       ;
+       ;       Select drive B, turn on motor if needed
+       ;
+       lda     motor_running   ; nothing selected
+       beq     notsel
+       cmpb    curdrive        ; check if we are good
+       bne     notsel
+;
+;      All is actually good
+;
+motor_was_on:
+       ldb     #0
+       puls    y,pc
+;
+;      Select our drive
+;
+notsel:
+       orb     #0xA8           ; NMI, motor on, density + our drive id
+       lda     FDCCTRL
+       stb     FDCCTRL
+       bita    #0x08
+       bne     motor_was_on
+       jsr     disknap
+       ; FIXME: longer motor spin up delay goes here
+       jsr     waitdisk
+       tfr a,b         ; return in the right place
+       puls y,pc
+
+;
+;      C interface fd_motor_off(void)
+;
+;      Turns off the drive motors, deselects all drives
+;
+_fd_motor_off:
+       pshs y
+;
+;      Deselect drives and turn off motor
+;
+       ldb     motor_running
+       beq     no_work_motor
+       ; Should we seek to track 0 ?
+       ldb     FDCCTRL
+       andb    #0xF0
+       stb     FDCCTRL
+       clr     motor_running
+no_work_motor:
+       puls y,pc
+
+       .area .data
+
+nmivector:
+       .word   nmi_handler
+curdrive:
+       .byte   0xff
+
+       .area .bss
+motor_running:
+       .byte   0
index 90f39f2..5f285fa 100644 (file)
@@ -27,7 +27,7 @@
             .globl _ramsize
             .globl _procmem
             .globl unix_syscall_entry
-           .globl nmi_handler
+           .globl fd_nmi_handler
 
             include "kernel.def"
             include "../kernel09.def"
@@ -86,7 +86,7 @@ init_hardware:
            jmp badswi_handler                  ; 0x100
            jmp badswi_handler                  ; 0x103
            jmp unix_syscall_entry              ; 0x106
-           jmp nmi_handler                     ; 0x109
+           jmp fd_nmi_handler                  ; 0x109
            jmp interrupt_handler               ; 0x10C
            jmp firq_handler                    ; 0x10F