Add bare-bones work-in-progress floppy disk driver: no spindown, and everything
authorDavid Given <dg@cowlark.com>
Tue, 17 Apr 2018 21:07:27 +0000 (23:07 +0200)
committerDavid Given <dg@cowlark.com>
Tue, 17 Apr 2018 21:07:27 +0000 (23:07 +0200)
is a sector at a time, but it'll boot and load init.

Kernel/platform-nc100/config.h
Kernel/platform-nc100/device.h
Kernel/platform-nc100/devices.c
Kernel/platform-nc100/devtty.c
Kernel/platform-nc200/devfd.c [new file with mode: 0644]
Kernel/platform-nc200/devfd.h [new file with mode: 0644]
Kernel/platform-nc200/fdc765.s [new file with mode: 0644]

index 971f9d1..45d347a 100644 (file)
@@ -58,6 +58,8 @@
 #define NBUFS    10       /* Number of block buffers */
 #ifdef CONFIG_NC200
 #define NMOUNTS        2         /* Floppy can also be mounted */
+#define BOOTDEVICENAMES "fd#,hd#"
+#define MAX_BLKDEV 1  /* Single floppy */
 #else
 #define NMOUNTS         1        /* Number of mounts at a time - nothing mountable! */
 #define BOOTDEVICE 0x0100      /* Only one possible option */
index 6f4c1e2..42d8282 100644 (file)
@@ -2,5 +2,6 @@
 #define __DEVICE_DOT_H__
 
 extern void mod_control(uint8_t set, uint8_t clr);
+extern void mod_irqen(uint8_t set, uint8_t clr);
 
 #endif /* __DEVICE_DOT_H__ */
index 887b350..f28eb81 100644 (file)
@@ -8,11 +8,21 @@
 #include <vt.h>
 #include <devtty.h>
 #include <devgfx.h>
+#include <printf.h>
+
+#if defined CONFIG_NC200
+#include <devfd.h>
+#include <blkdev.h>
+#endif
 
 struct devsw dev_tab[] =  /* The device driver switch table */
 {
   /* 0: /dev/fd                Floppy disc block devices (NC200 only) */
+#if defined CONFIG_NC200
+  {  blkdev_open, no_close, blkdev_read, blkdev_write, blkdev_ioctl },
+#else
   {  nxio_open,     no_close,    no_rdwr,   no_rdwr,   no_ioctl },
+#endif
   /* 1: /dev/hd                Hard disc block devices (Really PCMCIA) */
   {  rd_open,     no_close,    rd_read,   rd_write,   no_ioctl },
   /* 2: /dev/tty       TTY devices */
@@ -38,10 +48,11 @@ void device_init(void)
 {
   inittod();
   nc100_tty_init();
+  devfd_init();
 }
 
 __sfr __at 0x30 control;
-static uint8_t control_shadow = 0x10;
+static uint8_t control_shadow = 0xb8; // card common, floppy motor off, uPD4711 off, UART reset
 
 /* We need to track the state of the control port */
 void mod_control(uint8_t set, uint8_t clr)
@@ -50,3 +61,13 @@ void mod_control(uint8_t set, uint8_t clr)
   control_shadow |= set;
   control = control_shadow;
 }
+
+__sfr __at 0x60 irqen;
+static uint8_t irqen_shadow = 0x08; // keyboard IRQ only
+
+void mod_irqen(uint8_t set, uint8_t clr)
+{
+  irqen_shadow &= ~clr;
+  irqen_shadow |= set;
+  irqen = irqen_shadow;
+}
\ No newline at end of file
index 9973be2..3082757 100644 (file)
@@ -7,12 +7,15 @@
 #include <vt.h>
 #include <tty.h>
 
+#ifdef CONFIG_NC200
+#include <devfd.h>
+#endif
+
 #undef  DEBUG                  /* UNdefine to delete debug code sequences */
 
 __sfr __at 0xC0 uarta;
 __sfr __at 0xC1 uartb;
 
-__sfr __at 0x60 irqen;
 __sfr __at 0x90 irqmap;
 
 __sfr __at 0xB0 kmap0;
@@ -59,9 +62,9 @@ int nc100_tty_open(uint8_t minor, uint16_t flag)
                nap();
                mod_control(0x06, 0x01);        /* 9600 baud */
 #ifdef CONFIG_NC200
-               irqen = 0x1C;
+               mod_irqen(0x0C, 0x00);    /* single Rx/Tx interrupt on NC200 */
 #else
-               irqen = 0x0B;                   /* Allow serial interrupts */
+               mod_irqen(0x03, 0x00);    /* separate Rx/Tx interrupts on NC100 */
 #endif
        }
        return (0);
@@ -75,9 +78,9 @@ int nc100_tty_close(uint8_t minor)
                return 0;
        if (minor == 2) {
 #ifdef CONFIG_NC200
-               irqen = 0x18;
+               mod_irqen(0x00, 0x0C);    /* single Rx/Tx interrupt on NC200 */
 #else
-               irqen = 0x08;           /* mask serial interrupts */
+               mod_irqen(0x00, 0x03);    /* separate Rx/Tx interrupts on NC100 */
 #endif
                mod_control(0x10, 0);   /* turn off the line driver */
        }
@@ -325,13 +328,14 @@ void platform_interrupt(void)
                        }
                }
                timer_interrupt();
+               devfd_spindown();
        }
        if (!(a & 16)) {
                /* FIXME: Power button */
                ;
        }
        if (!(a & 32)) {
-               /* FIXME: FDC interrupt */
+               /* FIXME: FDC */
                ;
        }
        /* clear the mask */
diff --git a/Kernel/platform-nc200/devfd.c b/Kernel/platform-nc200/devfd.c
new file mode 100644 (file)
index 0000000..b5e1275
--- /dev/null
@@ -0,0 +1,139 @@
+/* 
+ *     Amstrad PCW8256 Floppy Driver
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devfd.h>
+#include <device.h>
+#include <blkdev.h>
+
+bool fd765_ready;
+
+static uint8_t motorct;
+static int8_t devsel = -1;
+
+__sfr __at 0xe0 fd_st;
+
+void devfd_init(void)
+{
+    blkdev_t* blk = blkdev_alloc();
+    if (!blk)
+        return;
+
+    fd765_track = 0xff; /* not on a known track */
+    blk->transfer = devfd_transfer;
+    blk->drive_lba_count = 1440; /* 512-byte sectors */
+    blkdev_scan(blk, 0);
+}
+
+void devfd_spindown(void)
+{
+}
+
+static void motor_on(int minor)
+{
+    minor;
+    mod_control(0x00, 0x20); /* motor on (active low) */
+    fd765_ready = false;
+    mod_irqen(0x20, 0x00); /* enable FDC IRQ */
+}
+
+static void motor_off(void)
+{
+    mod_control(0x20, 0x00); /* motor off (active low( */
+    mod_irqen(0x00, 0x20); /* disable FDC IRQ */
+    devsel = -1;
+}
+
+/* Seek to track 0. */
+static void fd_recalibrate(void)
+{
+    /* Keep trying to recalibrate until the command succeeds. We use this
+     * as a really shoddy way to wait for spinup. There should be a timeout here.
+     */
+
+    for (;;)
+    {
+        fd765_do_recalibrate();
+        if (((fd765_status[0] & 0xf8) == 0x20) && !fd765_status[1])
+            break;
+    }
+
+    fd765_track = 0;
+}
+
+/* Set up the controller for a given block, seek, and wait for spinup. */
+static void fd_seek(void)
+{
+    uint16_t block = blk_op.lba;
+    uint8_t track2 = block / 9;
+    uint8_t newtrack = track2 >> 1;
+
+    fd765_sector = (block % 9) + 1;
+    fd765_head = track2 & 1;
+
+    if (newtrack != fd765_track)
+    {
+        fd765_track = newtrack;
+
+        for (;;)
+        {
+            fd765_do_nudge_tc();
+            fd765_do_seek();
+            if ((fd765_status[0] & 0xf8) == 0x20)
+                break;
+
+            fd_recalibrate();
+        }
+    }
+}
+
+/* Select a drive and ensure the motor is on. */
+static void fd_select(int minor)
+{
+    if (devsel == minor)
+        return;
+    motor_on(minor);
+}
+
+/*
+ *     Block transfer
+ */
+uint8_t devfd_transfer(void)
+{
+    int ct = 0;
+    int tries;
+
+    fd_select(0);              /* Select, motor on */
+    fd765_is_user = blk_op.is_user;
+    while (ct < blk_op.nblock) {       /* For each block */
+        for (tries = 0; tries < 3; tries ++) { /* Try 3 times */
+            if (tries != 0)
+                fd_recalibrate();
+            fd_seek();
+
+            fd765_buffer = blk_op.addr;
+            if (blk_op.is_read) {
+                fd765_do_read();
+            } else {
+                fd765_do_write();
+            }
+
+            /* Did it work ? */
+            if ((fd765_status[0] & 0xc0) == 0)
+                break;
+        }
+        if (tries == 3)
+        {
+            kprintf("fd%d: I/O error %d:%d\n", blk_op.is_read, blk_op.lba);
+            udata.u_error = EIO;
+            break;
+        }
+        udata.u_block++;
+        ct++;
+        blk_op.addr += 512;
+    }
+    return ct;
+}
\ No newline at end of file
diff --git a/Kernel/platform-nc200/devfd.h b/Kernel/platform-nc200/devfd.h
new file mode 100644 (file)
index 0000000..f41426b
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef __DEVFD_DOT_H__
+#define __DEVFD_DOT_H__
+
+/* public interface */
+extern void devfd_init(void);
+extern void devfd_spindown(void);
+extern uint8_t devfd_transfer(void);
+
+extern void fd765_do_nudge_tc(void);
+extern void fd765_do_recalibrate(void);
+extern void fd765_do_seek(void);
+extern void fd765_do_read(void);
+extern void fd765_do_write(void);
+extern void fd765_do_read_id(void);
+
+extern uint8_t fd765_track;
+extern uint8_t fd765_head;
+extern uint8_t fd765_sector;
+extern uint8_t fd765_status[8];
+extern uint8_t* fd765_buffer;
+extern bool fd765_is_user;
+
+#endif /* __DEVRD_DOT_H__ */
diff --git a/Kernel/platform-nc200/fdc765.s b/Kernel/platform-nc200/fdc765.s
new file mode 100644 (file)
index 0000000..85056ae
--- /dev/null
@@ -0,0 +1,224 @@
+;
+;              765 Floppy Controller Support
+;
+               .module fdc765
+
+               .include "kernel.def"
+               .include "../kernel.def"
+
+       .globl  map_process_always
+       .globl  map_kernel
+
+       .globl _fd765_do_nudge_tc
+       .globl _fd765_do_recalibrate
+       .globl _fd765_do_seek
+       .globl _fd765_do_read
+       .globl _fd765_do_write
+       .globl _fd765_do_read_id
+
+       .globl _fd765_track
+       .globl _fd765_head
+       .globl _fd765_sector
+       .globl _fd765_status
+       .globl _fd765_buffer
+       .globl _fd765_is_user
+
+       .area _COMMONMEM
+
+       FD_ST   .equ    0xe0
+       FD_DT   .equ    0xe1
+
+; The Ranger ROM in the NC200 does mysterious things with port 0x20.
+;   bit 0: TC (documenteD)
+;   bit 1: always set
+;   bit 2: set on motor power on
+
+; Twiddle the Terminal Count line to the FDC.
+
+_fd765_do_nudge_tc:
+       ld a, #0x83
+       out (0x20), a
+       dec a
+       out (0x20), a
+       ret
+
+; Writes A to the FDC data register.
+
+fd765_tx:
+       ex af, af'
+fd765_tx_loop:
+       in a, (FD_ST)
+       rla                                                     ; RQM...
+       jr nc, fd765_tx_loop            ; ...low, keep waiting
+
+       ex af, af'
+       out (FD_DT), a
+       ret
+
+; Reads bytes from the FDC data register until the FDC tells us to stop (by
+; lowering DIO in the status register).
+
+fd765_read_status:
+       ld hl, #_fd765_status
+       ld c, #FD_DT
+read_status_loop:
+       in a, (FD_ST)
+       rla                                             ; RQM...
+       jr nc, read_status_loop         ; ...low, keep waiting 
+       rla                                                     ; DIO...
+       ret nc                                          ; ...low, no more data
+       ini                                                     ; (hl)++ = port(c); b--
+       jr read_status_loop
+_fd765_status:
+       .ds 8                                           ; 8 bytes of status data
+
+; Sends the head/drive byte of a command.
+; (Drive #0 is always used.)
+
+send_head:
+       ld a, (_fd765_head)
+       add a
+       add a
+       jr fd765_tx
+
+; Performs a RECALIBRATE command.
+
+_fd765_do_recalibrate:
+       ld a, #0x07                                     ; RECALIBRATE
+       call fd765_tx
+       ld a, #0x00                             ; drive #0
+       call fd765_tx
+       jr wait_for_seek_ending
+
+; Performs a SEEK command.
+
+_fd765_do_seek:
+       ld a, #0x0f                                     ; SEEK
+       call fd765_tx
+       call send_head                          ; specified head, drive #0
+       ld a, (_fd765_track)            ; specified track
+       call fd765_tx
+       jr wait_for_seek_ending
+_fd765_track:
+       .db 0
+_fd765_head:
+       .db 0
+_fd765_sector:
+       .db 0
+
+; Waits for a SEEK or RECALIBRATE command to finish by polling SENSE INTERRUPT STATUS.
+wait_for_seek_ending:
+       ld a, #0x08                                     ; SENSE INTERRUPT STATUS
+       call fd765_tx
+       call fd765_read_status
+
+       ld a, (_fd765_status)
+       bit 5, a                                        ; SE, seek end
+       jr z, wait_for_seek_ending
+       ret
+
+; Reads a 512-byte sector, after having previously saught to the right track.
+
+_fd765_do_read:
+       di                                                      ; performance critical, run with interrupts off
+       ld a, #0x46                                     ; READ SECTOR MFM
+       call setup_read_or_write
+
+       ld a, (_fd765_is_user)
+       or a
+       call nz, map_process_always
+       
+       ld hl, (_fd765_buffer)
+       ld c, #FD_DT
+       ld b, #0
+       ld e, #2
+read_loop:
+       in a, (FD_ST)
+       rla                                                     ; RQM...
+       jr nc, read_loop                ; ...low, keep waiting
+       rla                                                     ; DIO (ignore)
+       rla                                                     ; EXM...
+       jr nc, read_finished            ; ...low, transfer complete
+       ini                                                     ; (HL)++ = port(C), B--
+       jr nz, read_loop                        ; inner loop: 256 iterations
+       dec e
+       jr nz, read_loop                        ; outer loop: 2 iterations
+read_finished:
+       call _fd765_do_nudge_tc         ; Tell FDC we've finished
+       call fd765_read_status
+       ei
+
+       ld a, (_fd765_is_user)
+       or a
+       call nz, map_kernel
+       ret
+
+_fd765_do_write:
+       di                                                      ; performance critical, run with interrupts off
+       ld a, #0x45                                     ; WRITE SECTOR MFM
+       call setup_read_or_write
+
+       ld a, (_fd765_is_user)
+       or a
+       call nz, map_process_always
+       
+       ld hl, (_fd765_buffer)
+       ld c, #FD_DT
+       ld b, #0
+       ld e, #2
+write_loop:
+       in a, (FD_ST)
+       rla                                                     ; RQM...
+       jr nc, write_loop               ; ...low, keep waiting
+       rla                                                     ; DIO (ignore)
+       rla                                                     ; EXM...
+       jr nc, write_finished           ; ...low, transfer complete
+       outi                                            ; port(C) = (HL)++, B--
+       jr nz, write_loop                       ; inner loop: 256 iterations
+       dec e
+       jr nz, write_loop                       ; outer loop: 2 iterations
+write_finished:
+       call _fd765_do_nudge_tc         ; Tell FDC we've finished
+       call fd765_read_status
+       ei
+
+       ld a, (_fd765_is_user)
+       or a
+       call nz, map_kernel
+       ret
+
+; Given an FDC opcode in A, sets up a read or write.
+
+setup_read_or_write:
+       call fd765_tx                           ; 0: send opcode (in A)
+       call send_head                          ; 1: specified head, drive #0
+       ld a, (_fd765_track)            ; 2: specified track
+       call fd765_tx
+       ld a, (_fd765_head)                     ; 3: specified head
+       call fd765_tx
+       ld a, (_fd765_sector)           ; 4: specified sector
+       ld b, a
+       call fd765_tx
+       ld a, #2                                        ; 5: bytes per sector: 512
+       call fd765_tx
+       ld a, b                     ; 6: last sector (same as start sector)
+       call fd765_tx
+       ld a, #27                                       ; 7: Gap 3 length (27 is standard for 3.5" drives)
+       call fd765_tx
+       xor a                                           ; 8: Data length (unused)
+       call fd765_tx
+       ret
+
+_fd765_buffer:
+       .dw 0
+_fd765_is_user:
+       .db 0
+
+; Read the next sector ID off the disk.
+; (Only used for debugging.)
+
+_fd765_do_read_id:
+       ld a, #0x4a                             ; READ MFM ID
+       call fd765_tx
+       call send_head                          ; specified head, drive 0
+       jp fd765_read_status