is a sector at a time, but it'll boot and load init.
#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 */
#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__ */
#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 */
{
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)
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
#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;
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);
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 */
}
}
}
timer_interrupt();
+ devfd_spindown();
}
if (!(a & 16)) {
/* FIXME: Power button */
;
}
if (!(a & 32)) {
- /* FIXME: FDC interrupt */
+ /* FIXME: FDC */
;
}
/* clear the mask */
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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__ */
--- /dev/null
+;
+; 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