-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))
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 \
--- /dev/null
+#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);
+}
+
--- /dev/null
+#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__ */
+
#include <kernel.h>
#include <version.h>
#include <kdata.h>
-#include <devrd.h>
+#include <devfd.h>
#include <devsys.h>
#include <devlpr.h>
#include <tty.h>
// 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 */
+++ /dev/null
-/*
- * 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);
-}
-
+++ /dev/null
-#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__ */
-
--- /dev/null
+;
+; 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
.globl _ramsize
.globl _procmem
.globl unix_syscall_entry
- .globl nmi_handler
+ .globl fd_nmi_handler
include "kernel.def"
include "../kernel09.def"
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