From: David Given Date: Tue, 17 Apr 2018 21:07:27 +0000 (+0200) Subject: Add bare-bones work-in-progress floppy disk driver: no spindown, and everything X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=537649ceb264a0c13d9c244dafbb7fa3af6aaff0;p=FUZIX.git Add bare-bones work-in-progress floppy disk driver: no spindown, and everything is a sector at a time, but it'll boot and load init. --- diff --git a/Kernel/platform-nc100/config.h b/Kernel/platform-nc100/config.h index 971f9d18..45d347a2 100644 --- a/Kernel/platform-nc100/config.h +++ b/Kernel/platform-nc100/config.h @@ -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 */ diff --git a/Kernel/platform-nc100/device.h b/Kernel/platform-nc100/device.h index 6f4c1e26..42d8282b 100644 --- a/Kernel/platform-nc100/device.h +++ b/Kernel/platform-nc100/device.h @@ -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__ */ diff --git a/Kernel/platform-nc100/devices.c b/Kernel/platform-nc100/devices.c index 887b350d..f28eb817 100644 --- a/Kernel/platform-nc100/devices.c +++ b/Kernel/platform-nc100/devices.c @@ -8,11 +8,21 @@ #include #include #include +#include + +#if defined CONFIG_NC200 +#include +#include +#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 diff --git a/Kernel/platform-nc100/devtty.c b/Kernel/platform-nc100/devtty.c index 9973be23..30827573 100644 --- a/Kernel/platform-nc100/devtty.c +++ b/Kernel/platform-nc100/devtty.c @@ -7,12 +7,15 @@ #include #include +#ifdef CONFIG_NC200 +#include +#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 index 00000000..b5e12752 --- /dev/null +++ b/Kernel/platform-nc200/devfd.c @@ -0,0 +1,139 @@ +/* + * Amstrad PCW8256 Floppy Driver + */ + +#include +#include +#include +#include +#include +#include + +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 index 00000000..f41426b5 --- /dev/null +++ b/Kernel/platform-nc200/devfd.h @@ -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 index 00000000..85056ae7 --- /dev/null +++ b/Kernel/platform-nc200/fdc765.s @@ -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