From 1480f4a87549c4000ee954a1c6d37b1dd54a90e6 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 5 Dec 2014 17:55:03 +0000 Subject: [PATCH] dragon: first cut at a floppy driver Completely untested at this point --- Kernel/platform-dragon/Makefile | 6 +- Kernel/platform-dragon/devfd.c | 123 +++++++++++++ Kernel/platform-dragon/devfd.h | 16 ++ Kernel/platform-dragon/devices.c | 4 +- Kernel/platform-dragon/devrd.c | 69 ------- Kernel/platform-dragon/devrd.h | 13 -- Kernel/platform-dragon/floppy.s | 307 +++++++++++++++++++++++++++++++ Kernel/platform-dragon/p6809.s | 4 +- 8 files changed, 453 insertions(+), 89 deletions(-) create mode 100644 Kernel/platform-dragon/devfd.c create mode 100644 Kernel/platform-dragon/devfd.h delete mode 100644 Kernel/platform-dragon/devrd.c delete mode 100644 Kernel/platform-dragon/devrd.h create mode 100644 Kernel/platform-dragon/floppy.s diff --git a/Kernel/platform-dragon/Makefile b/Kernel/platform-dragon/Makefile index 17aa8ce3..531df8f3 100644 --- a/Kernel/platform-dragon/Makefile +++ b/Kernel/platform-dragon/Makefile @@ -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 index 00000000..1d3443a9 --- /dev/null +++ b/Kernel/platform-dragon/devfd.c @@ -0,0 +1,123 @@ +#include +#include +#include +#include + +#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 index 00000000..2354e361 --- /dev/null +++ b/Kernel/platform-dragon/devfd.h @@ -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__ */ + diff --git a/Kernel/platform-dragon/devices.c b/Kernel/platform-dragon/devices.c index 0cfe40bf..60723b45 100644 --- a/Kernel/platform-dragon/devices.c +++ b/Kernel/platform-dragon/devices.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include @@ -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 index ddbf8b3c..00000000 --- a/Kernel/platform-dragon/devrd.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * NC100 RD PCMCIA driver - * - */ - -#include -#include -#include -#include - -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 index 6320b269..00000000 --- a/Kernel/platform-dragon/devrd.h +++ /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 index 00000000..789ea06b --- /dev/null +++ b/Kernel/platform-dragon/floppy.s @@ -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 diff --git a/Kernel/platform-dragon/p6809.s b/Kernel/platform-dragon/p6809.s index 90f39f27..5f285fae 100644 --- a/Kernel/platform-dragon/p6809.s +++ b/Kernel/platform-dragon/p6809.s @@ -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 -- 2.34.1