tricks.o ../syscall_fs3.o \
../usermem_std-pdp11.o devtty.o libc.o > ../fuzix.map
pdp11-aout-objcopy fuzix.aout -O binary ../fuzix.bin
+
+ pdp11-aout-as boot-rx0.s -o boot-rx0.o
+ pdp11-aout-ld boot-rx0.o -o boot-rx0.aout
+ pdp11-aout-objcopy boot-rx0.aout -O binary boot-rx0
Once it all builds and the syscall and some minimal driver code is filled in
it ought to come in at about 20KW for a pure swap based system so probably
not useful on an MMUless /11 except for testing.
+
+Boot notes:
+ start block with nop apparently sometimes needed
+ Generally first block, but on rx appears to be first sector of track 1
+ (not 0 as expected)
+
+Boot logic:
+ Load inode 1 (hardcoded offset)
+ Walk direct link blocks looking for /bootstrap
+ Load only direct blocks of the referenced inode
+
+ Run /bootstrap
--- /dev/null
+/*
+ * Boot off an RX0.
+ */
+
+start:
+ nop
+ mov $hello,r0
+loop:
+ movb 0177564,r2
+ bpl loop
+ movb (r0)+,r1
+ beq done
+ movb r1,0177566
+ br loop
+done:
+ jsr pc, donereq
+ mov $0x200,r0
+ movb $111, r1 // 111 blocks is our 56K
+ mov $0177170,r2 // controller base
+ movb $1,r3 // sector
+ movb $2,r4 // track
+next:
+ movb $0x07,(r2)
+ jsr pc, txwait
+ movb r3,2(r2)
+ jsr pc, txwait
+ movb r4,2(r2)
+ jsr pc, donereq
+
+ movb $0x80,r5
+ movb $0x03,(r2)
+load:
+ tstb (r2)
+ bpl load
+ movb 2(r2),(r0)+
+ dec r5
+ bne load
+
+ dec r1
+ beq _finished
+ inc r3
+ cmp $27,r3
+ bne next
+ movb $1,r3
+ inc r4
+ br done
+
+donereq:
+ bis $0x20,(r2)
+ beq donereq
+ rts pc
+
+txwait:
+ tstb (r2)
+ bpl txwait
+ rts pc
+
+_finished:
+ jmp 0x200
+
+hello: .asciz "Loading Fuzix..."
/* Device parameters */
#define NUM_DEV_TTY 1
#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
-#define NBUFS 6 /* Number of block buffers */
+#define NBUFS 5 /* Number of block buffers */
#define NMOUNTS 2 /* Number of mounts at a time */
--- /dev/null
+/*
+ * PDP/11 RK05 cartridge driver
+ * We assume RK05 packs for PDP/11 the moment (2/202/12 geometry)
+ *
+ * Also assumed is one unit. But that's easily fixed
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <dev_rk.h>
+
+static uint16_t *rkds = (uint16_t *)0177400;
+static uint16_t *rker = (uint16_t *)0177402;
+static uint16_t *rkcs = (uint16_t *)0177404;
+static uint16_t *rkwc = (uint16_t *)0177406;
+static uint16_t *rkba = (uint16_t *)0177410;
+static uint16_t *rkda = (uint16_t *)0177412;
+
+static int rk_probe(uint8_t minor)
+{
+ /* FIXME: timeout ?? */
+ while(!(*rkcs & 0x80));
+ *rkda = minor << 12;
+ *rkcs = 0x0B; /* Drive reset, go */
+ while(!(*rkcs & 0x80));
+ /* Now check the drive status */
+ if (!(*rkds & 0x80))
+ return 0;
+ if (*rkds & 0x20)
+ return 3;
+ return 1;
+}
+
+static int rk_transfer(bool is_read, uint8_t minor, uint8_t rawflag)
+{
+ uint16_t ct = 0;
+ uint16_t st;
+ uint16_t *page = NULL;
+ uint8_t cmd = is_read ? 4 : 2;
+
+ if (rk_offline & (1 << minor)) {
+ udata.u_error = EIO;
+ return -1;
+ }
+ if(rawflag == 1) {
+ if (d_blkoff(9))
+ return -1;
+ page = &udata.u_page;
+#ifdef SWAPDEV
+ } else if (rawflag == 2) { /* Swap device special */
+ page = &swappage; /* Acting on this page */
+#endif
+ } else { /* rawflag == 0 */
+ }
+
+
+ /* We don't try and merge linearly sequential writes but these are so rare
+ (mkfs basically) even without vm.. We may want to reconsider for swap
+ however */
+ /* Overlapped seek is also supported but we can't really use it */
+
+ while (ct < udata.u_nblock) {
+ /* It's not quite as simple as it seems because while there is a single
+ disk address register it's up to use to set the bits properly for
+ sectors, suface, cylinder, drive */
+ uint16_t darv = minor << 13;
+ uint8_t sector;
+ uint8_t surface = 0x00;
+ uint16_t cylinder;
+
+ /* Assume a PDP/11 catridge not a PDP/8 one */
+ sector = udata.u_block % 12;
+ cylinder = udata.u_block /12;
+ /* Check this logic: there are spare tracks but are they hard/soft
+ spared ? */
+ if (cylinder > 202) {
+ cylinder -= 202;
+ surface = 0x10;
+ }
+ darv |= sector | surface
+ darv |= (cylinder << 5);
+
+ /* Wait until ready */
+ while(!(*rkcd & 0x80));
+
+ /* Load the target, addresses and command */
+ *rkda = darv;
+ *rkwc = -256; /* 512 bytes for now */
+ *rkba = udata.u_dptr;
+ *rkcs = cmd | 1; /* Needs top bits of VA when we do virtual */
+
+ while(!(*rkcd & 0x80));
+
+ st = *rker;
+ if (st) {
+ kprintf("fd%d: block %d, error %x/%x\n", minor, udata.u_block, st,
+ *rkds);
+ if (st & 0xFFF0)
+ rk_offline |= (1 << minor);
+ break;
+ }
+ udata.u_block++;
+ ct++;
+ udata.u_dptr += 512;
+ }
+ return ct << 9;
+}
+
+/* For now assume a single controller, max 8 packs, no partitioning support */
+
+int rk_open(uint8_t minor, uint16_t flag)
+{
+ flag;
+ uint8_t r;
+ /* No such drive or drive not loaded */
+ if(minor >= 8 || !(r = rk_probe(minor))) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ /* Read only media */
+ if ((r & 2) && O_ACCMODE(flag)) {
+ udata.u_error = EROFS;
+ return -1;
+ }
+ return 0;
+}
+
+int rk_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return rk_transfer(true, minor + 8, rawflag);
+}
+
+int rk_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return rk_transfer(false, minor + 8, rawflag);
+}
--- /dev/null
+/*
+ * Driver for an RX floppy disk controller
+ *
+ * 77 track, 26 sector per track, 128 byte per sector floppy
+ *
+ * Usually lives at 17170/2 (command/status at 0 data at 2)
+ *
+ * See "RX8/RX11 Floppy Disk System User's Manual EK-RX01-OP-001"
+ */
+
+#include <kernel.h>
+#include <printf.h>
+#include <dev_rx.h>
+
+#define GO 0x01
+
+#define CMD_FILL 0x00
+#define CMD_EMPTY 0x02
+#define CMD_WRITE 0x04
+#define CMD_READ 0x06
+#define CMD_READSTAT 0x08
+#define CMD_FORMAT 0x0A /* Set density (later models only) */
+#define CMD_WRITEDEL 0x0C
+#define CMD_READERROR 0x0E
+
+#define DRIVE_B 0x10
+#define DONE 0x20
+#define INTEN 0x40
+#define XFERRQ 0x80
+#define INTIALIZE 0x4000
+#define ERROR 0x8000
+
+static uint16_t *dev = (uint16_t *)0177170;
+static uint8_t drive;
+static uint8_t track;
+static uint8_t sector;
+
+static void issue_read(void)
+{
+ uint8_t x;
+ uint8_t ct = 0;
+ /* Might want timeouts here */
+
+ /* Wait for it to stop being busy */
+ while(!(*dev & DONE));
+ *dev = CMD_READ|GO|drive; /* READ|GO|DRIVE_B etc */
+ while(!(*dev & XFERRQ));
+ dev[1] = sector; /* When ready send the sector */
+ while(!(*dev & XFERRQ));
+ dev[1] = track; /* And the track */
+
+ /* The controller will no go off and get the sector */
+ while(!(*dev & DONE));
+
+ /* Now read the buffer */
+ *dev = CMD_EMPTY|GO; /* Ready for xfer */
+ while(ct < 128) {
+ x = *dev;
+ if (x & XFERRQ) {
+ *udata.u_dptr++ = dev[1]; /* Store byte */
+ ct++:
+ }
+ /* An early done means an error */
+ if (x & DONE)
+ break; /* Error */
+ }
+ return dev[1]; /* Error bits */
+}
+
+static void issue_write(void)
+{
+ uint8_t x;
+ uint8_t ct = 0;
+ while(!(*dev & DONE)); /* Make sure we are not busy */
+
+ /* We need to fill the buffer */
+ *dev = CMD_FILL|GO; /* Ready for xfer */
+ while(ct < 128) {
+ x = *dev;
+ if (x & 0x80) { /* Failure */
+ dev[1] = *udata.u_dptr+; /* Store byte */
+ ct++:
+ }
+ if (x & DONE)
+ break; /* Error */
+ }
+ /* Error in xfer ? */
+ if (dev[0] & (1 << 15)))
+ return dev[1];
+ /* Now we have copied the buffer into the controller we issue an I/O */
+ *dev = CMD_WRITE|GO|drive; /* READ|GO|DRIVE_B etc */
+ while(!(*dev & XFERRQ));
+ dev[1] = sector;
+ while(!(*dev & XFERRQ));
+ dev[1] = track;
+
+ /* Wait for the write to complete or error */
+ while(!(*dev & DONE));
+ return dev[1]; /* Error bits */
+}
+
+static int probe_drive(void)
+{
+ while(!(*dev & DONE));
+ *dev = CMD_READSTAT|GO|drive;
+ while(!(*dev & DONE));
+ return (*dev & 0x80);
+}
+
+static int rx_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+ int ct = 0;
+ int tries;
+ uint8_t err = 0;
+ uint16_t ptr;
+
+ if(rawflag == 2)
+ goto bad2;
+
+ rx_map = rawflag;
+ if (rawflag && d_blkoff(BLKSHIFT))
+ return -1;
+
+ /* In 128's */
+ udata.u_nblock *= 4;
+ udata.u_block *= 4;
+
+ drive = (minor & 1) ? DRIVE_B : 0;
+
+ while (ct < udata.u_nblock) {
+ track = udata.u_block /26;
+ sector = (udata.u_block % 26) + 1;
+ for (tries = 0; tries < 4 ; tries++) {
+ ptr = udata.u_dptr;
+ err = (is_read ? issue_read : issue_write)(dev))
+ if (err == 0)
+ break;
+ udata.u_dptr = ptr;
+ if (tries > 1)
+ rx_reset(driveptr);
+ }
+ /* FIXME: should we try the other half and then bale out ? */
+ if (tries == 4)
+ goto bad;
+ ct++;
+ udata.u_block++;
+ }
+ return ct << 7;
+bad:
+ kprintf("rx%d: error %x\n", minor, err);
+bad2:
+ udata.u_error = EIO;
+ return -1;
+}
+
+int rx_open(uint8_t minor, uint16_t flag)
+{
+ flag;
+ drive = (minor & 1) ? DRIVE_B : 0;
+ if(minor >= MAX_FD || !probe_drive()) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ return 0;
+}
+
+int rx_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return rx_transfer(minor, true, rawflag);
+}
+
+int rx_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;rawflag;minor;
+ return rx_transfer(minor, false, rawflag);
+}
+
\ No newline at end of file
#include <device.h>
#include <tty.h>
-volatile uint8_t *uart_data = (volatile uint8_t *)0xF03000; /* UART data */
-volatile uint8_t *uart_status = (volatile uint8_t *)0xF03010; /* UART status */
+volatile uint16_t *uart_rxstatus = (volatile uint16_t *)0177560;
+volatile uint8_t *uart_rxdata = (volatile uint8_t *)0177562;
+volatile uint8_t *uart_txstatus = (volatile uint8_t *)0177564;
+volatile uint8_t *uart_txdata = (volatile uint8_t *)0177566;
unsigned char tbuf1[TTYSIZ];
ttyready_t tty_writeready(uint8_t minor)
{
- uint8_t c = *uart_status;
- return (c & 2) ? TTY_READY_NOW : TTY_READY_SOON; /* TX DATA empty */
+ uint8_t c = *uart_txstatus;
+ return (c & 0x80) ? TTY_READY_NOW : TTY_READY_SOON;
}
void tty_putc(uint8_t minor, unsigned char c)
{
- *uart_data = c; /* Data */
+ /* Just the console for now */
+ *uart_txdata = c;
}
void tty_setup(uint8_t minor)
/* Currently run off the timer */
void tty_interrupt(void)
{
- uint8_t r = *uart_status;
- if (r & 1) {
- r = *uart_data;
+ uint8_t r = *uart_rxstatus;
+ if (r & 0x80) {
+ r = *uart_rxdata;
tty_inproc(1,r);
}
}