--- /dev/null
+
+CSRCS = devlpr.c devtty.c devfd.c devhd.c
+CSRCS += devices.c main.c
+
+ASRCS = ubee.s crt0.s
+ASRCS += tricks.s commonmem.s floppy.s
+
+COBJS = $(CSRCS:.c=.rel)
+AOBJS = $(ASRCS:.s=.rel)
+OBJS = $(COBJS) $(AOBJS)
+
+JUNK = $(CSRCS:.c=.lst) $(CSRCS:.c=.asm) $(CSRCS:.c=.sym) $(ASRCS:.s=.lst) $(ASRCS:.s=.sym) $(CSRCS:.c=.rst) $(ASRCS:.s=.rst)
+
+all: $(OBJS)
+
+$(COBJS): %.rel: %.c
+ $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(AOBJS): %.rel: %.s
+ $(CROSS_AS) $(ASOPTS) $<
+
+clean:
+ rm -f $(OBJS) $(JUNK) core *~
+
+image:
--- /dev/null
+uBee draft code
--- /dev/null
+/* Enable to make ^Z dump the inode table for debug */
+#undef CONFIG_IDUMP
+/* Enable to make ^A drop back into the monitor */
+#undef CONFIG_MONITOR
+/* Profil syscall support (not yet complete) */
+#define CONFIG_PROFIL
+/* Multiple processes in memory at once */
+#define CONFIG_MULTI
+/* Single tasking */
+#undef CONFIG_SINGLETASK
+/* Video terminal, not a serial tty */
+#define CONFIG_VT
+/* Simple character addressed device */
+#define CONFIG_VT_SIMPLE
+/* Banked memory set up */
+#define CONFIG_BANK_FIXED
+#define MAX_MAPS 32 /* 1MByte... */
+#define MAP_SIZE 0x8000
+
+#define CONFIG_BANKS 2 /* 2 x 32K */
+
+/* Vt definitions */
+#define VT_BASE ((uint8_t *)0xF000)
+#define VT_WIDTH 80
+#define VT_HEIGHT 24
+#define VT_RIGHT 79
+#define VT_BOTTOM 23
+
+#define TICKSPERSEC 60 /* Ticks per second */
+#define PROGBASE 0x0000 /* Base of user */
+#define PROGLOAD 0x0100 /* Load and run here */
+#define PROGTOP 0x7D00 /* Top of program, base of U_DATA stash */
+#define PROC_SIZE 32 /* Memory needed per process */
+
+#define SWAP_SIZE 0x40 /* 32K in blocks */
+#define SWAPBASE 0x0000 /* We swap the lot in one, include the */
+#define SWAPTOP 0x8000 /* vectors so its a round number of sectors */
+
+#define MAX_SWAPS 16 /* Should be plenty */
+
+#define BOOT_TTY (512 + 1) /* Set this to default device for stdio, stderr */
+ /* In this case, the default is the first TTY device */
+
+/* We need a tidier way to do this from the loader */
+#define CMDLINE NULL /* Location of root dev name */
+
+/* Device parameters */
+#define NUM_DEV_TTY 2
+#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
+#define SWAPDEV (256) /* Device for swapping (1st hd). */
+#define NBUFS 10 /* Number of block buffers */
+#define NMOUNTS 4 /* Number of mounts at a time */
+
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devfd.h>
+
+#define MAX_FD 4
+
+#define OPDIR_NONE 0
+#define OPDIR_READ 1
+#define OPDIR_WRITE 2
+
+#define FD_READ 0x80 /* 2797 needs 0x88, 1797 needs 0x80 */
+#define FD_WRITE 0xA0 /* Likewise A8 v A0 */
+
+static uint8_t motorct;
+static uint8_t fd_selected = 0xFF;
+static uint8_t fd_tab[MAX_FD] = { 0xFF, 0xFF, 0xFF, 0xFF };
+
+/*
+ * We only support normal block I/O for the moment. We do need to
+ * add swapping!
+ */
+
+static uint8_t selmap[4] = { 0x01, 0x02, 0x04, 0x08 };
+
+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 = 0;
+ uint8_t *driveptr = fd_tab + minor;
+ uint8_t cmd[6];
+
+ if(rawflag)
+ goto bad2;
+
+ if (fd_selected != minor) {
+ uint8_t err;
+ /* FIXME: We force DD for now */
+ err = fd_motor_on(selmap[minor]|0x80);
+ if (err)
+ goto bad;
+ }
+
+ if (*driveptr == 0xFF)
+ fd_reset(driveptr);
+
+ 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 / 9; /* 2 sectors per block */
+ cmd[2] = ((block % 9) << 1) + 1; /*eww.. */
+ cmd[3] = is_read ? OPDIR_READ: OPDIR_WRITE;
+ cmd[4] = dptr & 0xFF;
+ cmd[5] = dptr >> 8;
+
+ while (ct < 2) {
+ for (tries = 0; tries < 4 ; 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 == 4)
+ goto bad;
+ cmd[5]++; /* Move on 256 bytes in the buffer */
+ cmd[2]++; /* Next sector for 2nd block */
+ ct++;
+ }
+ return 1;
+bad:
+ kprintf("fd%d: error %x\n", minor, err);
+bad2:
+ udata.u_error = EIO;
+ return -1;
+}
+
+int fd_open(uint8_t minor, uint16_t flag)
+{
+ 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)
+{
+ flag;
+ return fd_transfer(minor, true, rawflag);
+}
+
+int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;rawflag;minor;
+// return 0;
+ 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 */
+uint16_t fd_reset(uint8_t *driveptr);
+uint16_t fd_operation(uint8_t *cmd, uint8_t *driveptr);
+uint16_t fd_motor_on(uint16_t drivesel);
+uint16_t fd_motor_off(uint16_t driveptr);
+
+#endif /* __DEVFD_DOT_H__ */
--- /dev/null
+/*
+ * WD1010 hard disk driver
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devhd.h>
+
+__sfr __at 0x40 hd_data;
+__sfr __at 0x41 hd_precomp; /* W/O */
+__sfr __at 0x41 hd_err; /* R/O */
+__sfr __at 0x42 hd_seccnt;
+__sfr __at 0x43 hd_secnum;
+__sfr __at 0x44 hd_cyllo;
+__sfr __at 0x45 hd_cylhi;
+__sfr __at 0x46 hd_sdh;
+__sfr __at 0x47 hd_status; /* R/O */
+__sfr __at 0x47 hd_cmd;
+__sfr __at 0x48 hd_fdcside; /* Side select for FDC */
+
+#define HDCMD_RESTORE 0x10
+#define HDCMD_READ 0x20
+#define HDCMD_WRITE 0x30
+#define HDCMD_VERIFY 0x40 /* Not on the 1010 later only */
+#define HDCMD_FORMAT 0x50
+#define HDCMD_INIT 0x60 /* Ditto */
+#define HDCMD_SEEK 0x70
+
+#define HDSDH_ECC256 0x80
+
+/* Used by the asm helpers */
+uint8_t hd_page;
+
+/* Seek and restore low 4 bits are the step rate, read/write support
+ multi-sector mode but not all emulators do .. */
+
+#define MAX_HD 4
+
+/* Wait for the drive to show ready */
+static uint8_t hd_waitready(void)
+{
+ uint8_t st;
+ do {
+ st = hd_status;
+ } while (!(st & 0x40));
+ return st;
+}
+
+/* Wait for DRQ or an error */
+static uint8_t hd_waitdrq(void)
+{
+ uint8_t st;
+ do {
+ st = hd_status;
+ } while (!(st & 0x09));
+ return st;
+}
+
+/* FIXME: move this to asm in _COMMONMEM and support banks and swap */
+static uint8_t hd_xfer(bool is_read, uint16_t addr)
+{
+ /* Error ? */
+ if (hd_status & 0x01)
+ return hd_status;
+ if (is_read)
+ hd_xfer_in(addr);
+ else
+ hd_xfer_out(addr);
+ /* Should be returning READY, and maybe SEEKDONE */
+ return hd_status;
+}
+
+/*
+ * We only support normal block I/O for the moment. We do need to
+ * add swapping!
+ */
+
+static int hd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+ blkno_t block;
+ uint16_t dptr;
+ uint16_t ct = 0;
+ int tries;
+ uint8_t err = 0;
+ uint8_t cmd = HDCMD_READ;
+ uint8_t head;
+ uint8_t sector;
+ uint16_t nblock;
+
+ if (rawflag == 0) {
+ dptr = (uint16_t)udata.u_buf->bf_data;
+ block = udata.u_buf->bf_blk;
+ nblock = 2;
+ hd_page = 0; /* Kernel */
+ } else if (rawflag == 2) {
+ nblock = swapcnt >> 8; /* in 256 byte chunks */
+ dptr = (uint16_t)swapbase;
+ hd_page = swapproc->p_page;
+ block = swapblk;
+ } else
+ goto bad2;
+
+ if (!is_read)
+ cmd = HDCMD_WRITE;
+
+
+ /* We assume 32 sectors per track for now. From our 512 byte
+ PoV that's 16 */
+
+ /* For our test purposes we use a disk with 32 sectors, 4 heads so
+ our blocks map out as 00cccccc ccCCCCCC CCHHSSSS
+
+ This matches a real ST506 which is 153 cyls, 4 heads, 32 sector */
+
+ hd_precomp = 0x20; /* For now, matches an ST506 */
+ hd_seccnt = 1;
+ block <<= 1; /* Into 256 byte blocks, limits us to 32MB
+ so ought to FIXME */
+
+ while (ct < nblock) {
+ /* 32 sectors per track assumed for now */
+ sector = (block & 31) + 1;
+ head = (block >> 5) & 3;
+ /* Head next bits, plus drive */
+ hd_sdh = 0x80 | head | (minor << 3);
+ hd_secnum = sector;
+ /* cylinder bits */
+ hd_cyllo = (block >> 7) & 0xFF;
+ hd_cylhi = (block >> 15) & 0xFF;
+
+ for (tries = 0; tries < 4; tries++) {
+ /* issue the command */
+ hd_cmd = cmd;
+ /* DRQ will go high once the controller is ready
+ for us */
+ err = hd_waitdrq();
+ if (!(err & 1)) {
+ err = hd_xfer(is_read, dptr);
+ /* Ready, no error ? */
+ if ((err & 0x41) == 0x40)
+ break;
+ } else
+ kprintf("hd%d: err %x\n", minor, err);
+
+ if (tries > 1) {
+ hd_cmd = HDCMD_RESTORE;
+ if (hd_waitready() & 1)
+ kprintf("hd%d: restore error %z\n", minor, err);
+ }
+ }
+ /* FIXME: should we try the other half and then bale out ? */
+ if (tries == 3)
+ goto bad;
+ ct++;
+ dptr += 256;
+ block ++;
+ }
+ return 1;
+bad:
+ if (err & 1)
+ kprintf("hd%d: error %x\n", minor, hd_err);
+ else
+ kprintf("hd%d: status %x\n", minor, err);
+bad2:
+ udata.u_error = EIO;
+ return -1;
+}
+
+int hd_open(uint8_t minor, uint16_t flag)
+{
+ flag;
+ if (minor >= MAX_HD) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return hd_transfer(minor, true, rawflag);
+}
+
+int hd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return hd_transfer(minor, false, rawflag);
+}
--- /dev/null
+#ifndef __DEVHD_DOT_H__
+#define __DEVHD_DOT_H__
+
+/* public interface */
+int hd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int hd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int hd_open(uint8_t minor, uint16_t flag);
+
+/* helpers in common memory for the block transfers */
+int hd_xfer_in(uint16_t addr);
+int hd_xfer_out(uint16_t addr);
+
+#endif /* __DEVHD_DOT_H__ */
--- /dev/null
+#include <kernel.h>
+#include <tty.h>
+#include <version.h>
+#include <kdata.h>
+#include <devfd.h>
+#include <devhd.h>
+#include <devsys.h>
+#include <devlpr.h>
+#include <devtty.h>
+
+struct devsw dev_tab[] = /* The device driver switch table */
+{
+ /* 0: /dev/fd Floppy disc block devices */
+ { fd_open, no_close, fd_read, fd_write, no_ioctl },
+ /* 1: /dev/hd Hard disc block devices */
+ { hd_open, no_close, hd_read, hd_write, no_ioctl },
+ /* 2: /dev/tty TTY devices */
+ { tty_open, tty_close, tty_read, tty_write, tty_ioctl },
+ /* 3: /dev/lpr Printer devices */
+ { lpr_open, lpr_close, no_rdwr, lpr_write, no_ioctl },
+ /* 4: /dev/mem etc System devices (one offs) */
+ { no_open, no_close, sys_read, sys_write, sys_ioctl },
+ /* Pack to 7 with nxio if adding private devices and start at 8 */
+};
+
+bool validdev(uint16_t dev)
+{
+ /* This is a bit uglier than needed but the right hand side is
+ a constant this way */
+ if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) + 255)
+ return false;
+ else
+ return true;
+}
+
+void device_init(void)
+{
+ int i;
+ /* Add 64 swaps (2MB) */
+ for (i = MAX_SWAPS - 1 ; i >= 0; i--)
+ swapmap_add(i);
+}
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <devlpr.h>
+
+__sfr __at 0x02 lpstat; /* I/O 2 and 3 */
+__sfr __at 0x03 lpdata;
+
+int lpr_open(uint8_t minor, uint16_t flag)
+{
+ minor; flag; // shut up compiler
+ return 0;
+}
+
+int lpr_close(uint8_t minor)
+{
+ minor; // shut up compiler
+ return 0;
+}
+
+int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ int c = udata.u_count;
+ char *p = udata.u_base;
+ minor; rawflag; flag; // shut up compiler
+
+ while(c) {
+ /* Note; on real hardware it might well be necessary to
+ busy wait a bit just to get acceptable performance */
+ while (lpstat != 0xFF) {
+// if (psleep_flags(&clocktick, flag))
+// return -1;
+ }
+ /* FIXME: tidy up ugetc and sysio checks globally */
+ lpdata = ugetc(p++);
+ }
+ return (-1);
+}
--- /dev/null
+#ifndef __DEVLPR_DOT_H__
+#define __DEVLPR_DOT_H__
+
+int lpr_open(uint8_t minor, uint16_t flag);
+int lpr_close(uint8_t minor);
+int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+
+#endif
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <tty.h>
+#include <vt.h>
+#include <devtty.h>
+#include <stdarg.h>
+
+char tbuf1[TTYSIZ];
+char tbuf2[TTYSIZ];
+
+__sfr __at 0xE8 tr1865_ctrl;
+__sfr __at 0xE9 tr1865_baud;
+__sfr __at 0xEA tr1865_status;
+__sfr __at 0xEB tr1865_rxtx;
+
+struct s_queue ttyinq[NUM_DEV_TTY + 1] = { /* ttyinq[0] is never used */
+ {NULL, NULL, NULL, 0, 0, 0},
+ {tbuf1, tbuf1, tbuf1, TTYSIZ, 0, TTYSIZ / 2},
+ {tbuf2, tbuf2, tbuf2, TTYSIZ, 0, TTYSIZ / 2}
+};
+
+/* Write to system console */
+void kputchar(char c)
+{
+ if (c == '\n')
+ tty_putc(1, '\r');
+ tty_putc(1, c);
+}
+
+static bool tty_writeready(uint8_t minor)
+{
+ minor;
+ return 1;
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+ minor;
+ vtoutput(&c, 1);
+}
+
+void tty_setup(uint8_t minor)
+{
+ minor;
+}
+
+int tty_carrier(uint8_t minor)
+{
+ minor;
+ return 1;
+}
+
+#if 0
+static uint8_t keymap[8];
+static uint8_t keyin[8];
+static uint8_t keybyte, keybit;
+static uint8_t newkey;
+static int keysdown = 0;
+static uint8_t shiftmask[8] = {
+ 0, 0, 0, 0, 0, 0, 0, 7
+};
+
+static void keyproc(void)
+{
+ int i;
+ uint8_t key;
+
+ for (i = 0; i < 8; i++) {
+ /* Set one of A0 to A7, and read the byte we get back.
+ Invert that to get a mask of pressed buttons */
+ keyin[i] = *(uint8_t *) (0xF400 | (1 << i));
+ key = keyin[i] ^ keymap[i];
+ if (key) {
+ int n;
+ int m = 1;
+ for (n = 0; n < 8; n++) {
+ if ((key & m) && (keymap[i] & m)) {
+ if (!(shiftmask[i] & m))
+ keysdown--;
+ }
+ if ((key & m) && !(keymap[i] & m)) {
+ if (!(shiftmask[i] & m))
+ keysdown++;
+ keybyte = i;
+ keybit = n;
+ newkey = 1;
+ }
+ m += m;
+
+ }
+ }
+ keymap[i] = keyin[i];
+ }
+}
+
+static uint8_t keyboard[8][8] = {
+ {'@', 'a', 'b', 'c', 'd', 'e', 'f', 'g'},
+ {'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'},
+ {'p', 'q', 'r', 's', 't', 'u', 'v', 'w'},
+ {'x', 'y', 'z', '[', '\\', ']', '^', '_'},
+ {'0', '1', '2', '3', '4', '5', '6', '7'},
+ {'8', '9', ':', ';', ',', '-', '.', '/'},
+ {13, 12, 3, 0 /*up */ , 0 /*down */ , 8 /* left */ , 0 /*right */ ,
+ ' '},
+ {0, 0, 0, 0, 0xF1, 0xF2, 0xF3, 0}
+};
+
+static uint8_t shiftkeyboard[8][10] = {
+ {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G'},
+ {'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'},
+ {'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W'},
+ {'X', 'Y', 'Z', '{', '|', '}', '^', '_'},
+ {'0', '!', '"', '#', '$', '%', '&', '\''},
+ {'(', ')', '*', '+', '<', '=', '>', '?'},
+ {13, 12, 3, 0 /*up */ , 0 /*down */ , 8 /* left */ , 0 /*right */ ,
+ ' '},
+ {0, 0, 0, 0, 0xF1, 0xF2, 0xF3, 0}
+};
+
+static uint8_t capslock = 0;
+
+static void keydecode(void)
+{
+ uint8_t c;
+
+ if (keybyte == 7 && keybit == 3) {
+ capslock = 1 - capslock;
+ return;
+ }
+
+ if (keymap[7] & 3) /* shift */
+ c = shiftkeyboard[keybyte][keybit];
+ else
+ c = keyboard[keybyte][keybit];
+
+ /* The keyboard lacks some rather important symbols so remap them
+ with control */
+ if (keymap[7] & 4) { /* control */
+ if (c > 31 && c < 96)
+ c &= 31;
+ if (keymap[7] & 3) {
+ if (c == '(')
+ c = '{';
+ if (c == ')')
+ c = '}';
+ if (c == '-')
+ c = '_';
+ if (c == '/')
+ c = '``';
+ if (c == '<')
+ c = '^';
+ } else {
+ if (c == '(')
+ c = '[';
+ if (c == ')')
+ c = ']';
+ if (c == '-')
+ c = '|';
+ }
+ }
+ if (capslock && c >= 'a' && c <= 'z')
+ c -= 'a' - 'A';
+ if (c)
+ tty_inproc(1, c);
+}
+
+void kbd_interrupt(void)
+{
+ newkey = 0;
+ keyproc();
+ if (keysdown < 3 && newkey)
+ keydecode();
+}
+
+#endif
--- /dev/null
+#ifndef _DEVTTY_H
+#define _DEVTTY_H
+
+extern void tty_interrupt(void);
+extern void kbd_interrupt(void);
+extern int trstty_close(uint8_t minor);
+
+#endif
--- /dev/null
+;
+; Core floppy routines for the TRS80 1791 FDC
+; Based on the 6809 code
+;
+; FIXME: better drive spin up wait
+; FIXME: double sided media
+; FIXME: correct step rates (per drive ?)
+; FIXME: precompensation
+; - not on single density
+; - track dependant for double density based on trsdos dir pos
+;
+;
+ .globl _fd_reset
+ .globl _fd_operation
+ .globl _fd_motor_on
+ .globl _fd_motor_off
+ .globl fd_nmi_handler
+
+FDCREG .equ 0xF0
+FDCTRK .equ 0xF1
+FDCSEC .equ 0xF2
+FDCDATA .equ 0xF3
+FDCCTRL .equ 0xF4
+FDCINT .equ 0xE4
+;
+; interrupt register reports 0x80 for interrut, 0x40 for drq
+; (0x20 is the unrelated reset button)
+;
+
+;
+; Structures we use
+;
+;
+; Per disk structure to hold device state
+;
+TRKCOPY .equ 0
+
+;
+; Command issue
+;
+CMD .equ 0
+TRACK .equ 1
+SECTOR .equ 2
+DIRECT .equ 3 ; 0 = read 2 = write 1 = status
+DATA .equ 4
+
+ .area _COMMONMEM
+;
+; Simple routine for pauses
+;
+nap: dec bc
+ ld a, b
+ or c
+ jr nz, nap
+ ret
+;
+; The motor off logic is driven from hardware
+;
+fd_nmi_handler:
+ push af
+ push bc
+ ld a, (fdc_active)
+ or a
+ jr z, boring_nmi
+ xor a
+ out (FDCINT), a
+ ld bc, #100
+ call nap
+ pop bc
+ pop af
+ pop af ; discard return address
+ jp fdio_nmiout ; and jump
+
+;
+; FIXME: check for motor off here
+;
+boring_nmi:
+ pop bc
+ pop af
+ retn
+;
+; Wait for the drive controller to become ready
+; Preserve HL, DE
+;
+waitdisk:
+ ld bc, #0
+waitdisk_l:
+ in a, (FDCREG)
+ bit 0, a
+ ret z
+ ;
+ ; Keep poking fdcctrl to avoid a hardware motor timeout
+ ;
+ ld a, (fdcctrl)
+ out (FDCCTRL), a
+ djnz waitdisk_l
+ dec c
+ jr nz, waitdisk_l
+ ld a, #0xD0 ; reset
+ out (FDCREG), a
+ ex (sp),hl
+ ex (sp),hl
+ ex (sp),hl
+ ex (sp),hl
+ in a, (FDCREG) ; read to reset int status
+ bit 0, a
+ ret
+;
+; Set up and perform a disk operation
+;
+; IX points to the command block
+; HL points to the buffer
+; DE points to the track reg copy
+;
+fdsetup:
+ ld a, (de)
+ out (FDCTRK), a
+ cp TRACK(ix)
+; jr z, fdiosetup
+
+ ;
+ ; So we can verify
+ ;
+ ld a, TRACK(ix)
+ out (FDCDATA), a
+ ld a, SECTOR(ix)
+ out (FDCSEC), a
+ ;
+ ; Need to seek the disk
+ ;
+ ld a, #0x18 ; seek
+ out (FDCREG), a
+ ld b, #100
+seekwt: djnz seekwt
+ call waitdisk
+ jr nz, setuptimeout
+ and #0x18 ; error bits
+ jr z, fdiosetup
+ ; seek failed, not good
+setuptimeout: ; NE = bad
+ ld a, #0xff ; we have no idea where we are, force a seek
+ ld (de), a ; zap track info
+ ret
+;
+; Head in the right place
+;
+fdiosetup:
+ ld a, TRACK(ix)
+ ld (de), a ; save track
+; cmp #22 ; FIXME
+; jr nc, noprecomp
+; ld a, (fdcctrl)
+; or #0x10 ; Precomp on
+; jr precomp1
+;noprecomp:
+ ld a, (fdcctrl)
+;precomp1:
+ out (FDCCTRL), a
+ ld a, SECTOR(ix)
+ out (FDCSEC), a
+ in a, (FDCREG) ; Clear any pending status
+
+ ld a, CMD(ix)
+
+ ld de, #0 ; timeout handling
+
+ out (FDCREG), a ; issue the command
+ ld b, #0
+rwiowt: djnz rwiowt
+ ld a, DIRECT(ix)
+ dec a
+ ld a, (fdcctrl)
+ ld d, a ; we need this in a register
+ ; to meet timing
+ ld a, #1
+ ld (fdc_active), a ; NMI pop and jump
+ jr z, fdio_in
+ jr nc, fdio_out
+;
+; Status registers
+;
+fdxferdone:
+ ei
+fdxferdone2:
+ xor a
+ ld (fdc_active), a
+ in a, (FDCREG)
+ and #0x19 ; Error bits + busy
+ bit 0, a ; Wait for busy to drop, return in a
+ ret z
+ ld a, (fdcctrl)
+ out (FDCCTRL), a
+ jr fdxferdone2
+;
+; Read from the disk - HL points to the target buffer
+;
+fdio_in:
+ ld e, #0x16 ; bits to check
+ ld bc, #FDCDATA ; 256 bytes/sector, c is our port
+fdio_inl:
+ in a, (FDCREG)
+ and e
+ jr z, fdio_inl
+ ini
+ di
+ ld a, d
+fdio_inbyte:
+ out (FDCCTRL), a ; stalls
+ ini
+ jr nz, fdio_inbyte
+ jr fdxferdone
+
+;
+; Read from the disk - HL points to the target buffer
+;
+fdio_out:
+ set 6,d ; halt mode bit
+ ld c, #FDCDATA ; C is our port
+ ld e, #0x76
+fdio_outl:
+ in a, (FDCREG) ; Wait for DRQ (or error)
+ and e
+ jr z, fdio_outl
+ outi ; Stuff byte into FDC while we think
+ di
+ in a, (FDCREG) ; No longer busy ??
+ rra
+ jr nc, fdxferbad ; Bugger...
+ ld a, #0xC0 ; Turn on magic floppy NMI interface
+ out (FDCINT), a
+ ld b, #50 ; Spin for it
+spin1: djnz spin1
+ ld b, (hl) ; Next byte
+ inc hl
+fdio_waitlock:
+ ld a, d
+ out (FDCCTRL), a ; wait states on
+ in a, (FDCREG)
+ and e
+ jr z, fdio_waitlock
+ out (c), b
+ ld a, d
+fdio_outbyte:
+ out (FDCCTRL), a ; stalls
+ outi
+ jr fdio_outbyte
+fdio_nmiout:
+;
+; Now tidy up
+;
+ jr fdxferdone
+
+fdxferbad:
+ ld a, #0xff
+ ret
+
+;
+; 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(uint8_t *drvptr)
+;
+_fd_reset:
+ pop de
+ pop hl
+ push hl
+ push de
+ ld a, (fdcctrl)
+ out (FDCCTRL), a
+ ld a, #1
+ out (FDCSEC), a
+ xor a
+ out (FDCTRK), a
+ ld a, #0x0C
+ out (FDCREG), a ; restore
+ ld a, #0xFF
+ ld (hl), a ; Zap track pointer
+ ld b, #0
+_fdr_wait:
+ djnz _fdr_wait
+
+ call waitdisk
+ cp #0xff
+ ret z
+ and #0x99 ; Error bit from the reset
+ ret nz
+ ld (hl), a ; Track 0 correctly hit (so 0)
+ ret
+;
+; fd_operation(uint16_t *cmd, uint16_t *drive)
+;
+; The caller must ensure the drive has been selected and the motor is
+; running.
+;
+_fd_operation:
+ pop bc ; return address
+ pop hl ; command
+ pop de ; drive track ptr
+ push de
+ push hl
+ push bc
+ push ix
+ push hl
+ pop ix
+ ld l, DATA(ix)
+ ld h, DATA+1(ix)
+ call fdsetup ; Set up for a command
+ ld l, a
+ ld h, #0
+ pop ix
+ ret
+;
+; C interface fd_motor_on(uint16_t drivesel)
+;
+; Selects this drive and turns on the motors. Also pass in the
+; choice of density
+;
+; bits 0-3: select that drive
+; bit 4: side (must rewrite each drive change)
+; bit 5: precompensation (not set here but in the I/O ops)
+; bit 6: synchronize I/O by stalling the CPU (don't set this)
+; bit 7: set for double density (MFM)
+;
+;
+_fd_motor_on:
+ pop de
+ pop hl
+ push hl
+ push de
+ ;
+ ; Select drive B, turn on motor if needed
+ ;
+ ld a,(motor_running) ; nothing selected
+ or a
+ jr z, notsel
+
+ cp l
+ jr z, motor_was_on
+;
+; Select our drive
+;
+notsel:
+ ld a, l
+ out (FDCCTRL), a
+ out (FDCCTRL), a ; TRS80 erratum apparently needs this
+ ld (fdcctrl), a
+ ld bc, #0x7F00 ; Long delay (may need FE or FF for some disks)
+ call nap
+ ; FIXME: longer motor spin up delay goes here (0.5 or 1 second)
+
+ call waitdisk
+;
+; All is actually good
+;
+motor_was_on:
+ ld hl, #0
+ ret
+
+;
+; C interface fd_motor_off(void)
+;
+; Turns off the drive motors, deselects all drives
+;
+_fd_motor_off:
+ ld a, (motor_running)
+ or a
+ ret z
+ ; Should we seek to track 0 ?
+ in a, (FDCCTRL)
+ and #0xF0 ; clear drive bits
+ out (FDCCTRL), a
+ xor a
+ ld (motor_running), a
+ ret
+
+curdrive:
+ .db 0xff
+motor_running:
+ .db 0
+fdcctrl:
+ .db 0
+fdc_active:
+ .db 0
--- /dev/null
+; UZI mnemonics for memory addresses etc
+
+U_DATA .equ 0xE000 ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE .equ 0x300 ; 256+256+256 bytes.
+
+U_DATA_STASH .equ 0x7D00 ; BD00-BFFF
+
+PROG_BASE .equ 0x0000
+
+NMOS_Z80 .equ 1
--- /dev/null
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+
+uint16_t ramtop = PROGTOP;
+
+/* On idle we spin checking for the terminals. Gives us more responsiveness
+ for the polled ports */
+void platform_idle(void)
+{
+ __asm halt __endasm;
+}
+
+void do_beep(void)
+{
+}
+
+void platform_interrupt(void)
+{
+ timer_interrupt();
+}
+
+void map_init(void)
+{
+}
+
+/* Toggle bit 1, set the no ROM bits */
+#define PAGE_MAP(page) (((page) | 0x04)^0x02)
+
+/* Kernel in 0/1. We don't pull video tricks with 0xF000 yet to save memory
+ but we should eventually */
+
+void pagemap_init(void)
+{
+ /* This is model dependent */
+ pagemap_add(PAGE_MAP(2));
+ pagemap_add(PAGE_MAP(3));
+#if defined(CONFIG_UB256) || defined(CONFIG_UB512)
+ pagemap_add(PAGE_MAP(0) | 0x40);
+ pagemap_add(PAGE_MAP(1) | 0x40);
+ pagemap_add(PAGE_MAP(2) | 0x40);
+ pagemap_add(PAGE_MAP(3) | 0x40);
+#endif
+#if defined(CONFIG_UB256TC) || defined(CONFIG_UB512)
+ pagemap_add(PAGE_MAP(0) | 0x80);
+ pagemap_add(PAGE_MAP(1) | 0x80);
+ pagemap_add(PAGE_MAP(2) | 0x80);
+ pagemap_add(PAGE_MAP(3) | 0x80);
+#endif
+#if defined(CONFIG_UB512)
+ pagemap_add(PAGE_MAP(0) | 0xC0);
+ pagemap_add(PAGE_MAP(1) | 0xC0);
+ pagemap_add(PAGE_MAP(2) | 0xC0);
+ pagemap_add(PAGE_MAP(3) | 0xC0);
+#endif
+/* and if we ever poke at the 1024 stuff its bit 7-5/1-0 */
+}
--- /dev/null
+;
+; TRS 80 hardware support
+;
+
+ .module ubee
+
+ ; exported symbols
+ .globl init_early
+ .globl init_hardware
+ .globl interrupt_handler
+ .globl _program_vectors
+ .globl _system_tick_counter
+ .globl map_kernel
+ .globl map_process
+ .globl map_process_a
+ .globl map_process_always
+ .globl map_save
+ .globl map_restore
+ .globl platform_interrupt_all
+ .globl _kernel_flag
+
+ ; hard disk helpers
+ .globl _hd_xfer_in
+ .globl _hd_xfer_out
+ ; and the page from the C code
+ .globl _hd_page
+
+ ; exported debugging tools
+ .globl _trap_monitor
+ .globl outchar
+
+ ; imported symbols
+ .globl _ramsize
+ .globl _procmem
+ .globl istack_top
+ .globl istack_switched_sp
+ .globl unix_syscall_entry
+ .globl trap_illegal
+ .globl outcharhex
+ .globl fd_nmi_handler
+ .globl null_handler
+
+ .globl s__COMMONMEM
+ .globl l__COMMONMEM
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (0xE800 upwards)
+; -----------------------------------------------------------------------------
+ .area _COMMONMEM
+
+_trap_monitor:
+ di
+ call map_kernel
+ jp to_monitor
+
+platform_interrupt_all:
+ in a,(0xef)
+ ret
+
+_trap_reboot:
+ di
+ call map_kernel
+ jp to_reboot
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (below 0xE800, only accessible when the kernel is mapped)
+; -----------------------------------------------------------------------------
+ .area _CODE
+
+; These two must be below 32K and not use the stack until they hit ROM space
+;
+to_monitor:
+ xor a ; 0 or 1 to keep low 32K right ? */
+ out (0x50), a ; ROMS please
+ jp 0xE003 ; Monitor
+
+to_reboot:
+ xor a
+ out (0x50), a
+ jp 0xE000
+
+;_ctc6845: ; registers in reverse order
+; .db 99, 80, 85, 10, 25, 4, 24, 24, 0, 9, 101, 9, 0, 0, 0, 0
+init_early:
+;
+; ; load the 6845 parameters
+; ld hl, #_ctc6845
+; ld bc, #1588
+;ctcloop: out (c), b ; register
+; ld a, (hl)
+; out (0x89), a ; data
+; inc hl
+; djnz ctcloop
+
+ ; clear screen
+ ld hl, #0xF000
+ ld (hl), #'*' ; debugging aid in top left
+ inc hl
+ ld de, #0xF002
+ ld bc, #1998
+ ld (hl), #' '
+ ldir
+ ret
+
+init_hardware:
+ ; set system RAM size
+ ld hl, #128 ; FIXME according to platform
+ ld (_ramsize), hl
+ ld hl, #(128-64) ; 64K for kernel
+ ld (_procmem), hl
+
+ ; set up interrupt vectors for the kernel (also sets up common memory in page 0x000F which is unused)
+ ld hl, #0
+ push hl
+ call _program_vectors
+ pop hl
+
+ im 1 ; set CPU interrupt mode
+
+ ret
+
+
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+ .area _COMMONMEM
+
+mapreg: .db 0
+mapsave: .db 0
+
+_kernel_flag:
+ .db 1 ; We start in kernel mode
+
+_program_vectors:
+ ; we are called, with interrupts disabled, by both newproc() and crt0
+ ; will exit with interrupts off
+ di ; just to be sure
+ pop de ; temporarily store return address
+ pop hl ; function argument -- base page number
+ push hl ; put stack back as it was
+ push de
+
+ call map_process
+
+ ; write zeroes across all vectors
+ ld hl, #0
+ ld de, #1
+ ld bc, #0x007f ; program first 0x80 bytes only
+ ld (hl), #0x00
+ ldir
+
+ ; now install the interrupt vector at 0x0038
+ ld a, #0xC3 ; JP instruction
+ ld (0x0038), a
+ ld hl, #interrupt_handler
+ ld (0x0039), hl
+
+ ; set restart vector for UZI system calls
+ ld (0x0030), a ; (rst 30h is unix function call vector)
+ ld hl, #unix_syscall_entry
+ ld (0x0031), hl
+
+ ld (0x0000), a
+ ld hl, #null_handler ; to Our Trap Handler
+ ld (0x0001), hl
+
+ ld (0x0066), a ; Set vector for NMI
+ ld hl, #fd_nmi_handler
+ ld (0x0067), hl
+
+;
+; Mapping set up for the TRS80 4/4P
+;
+; The top 32K bank holds kernel code and pieces of common memory
+; The lower 32K is switched between the various user banks. On a
+; 4 or 4P without add in magic thats 0x62 and 0x63 mappings.
+;
+map_kernel:
+ push af
+ ld a, #0x04 ; bank 0, 1 no ROM - FIXME: map video over kernel
+ ld (mapreg), a
+ out (0x50), a
+ pop af
+ ret
+;
+; Userspace mapping is mode 3, U64K/L32 mapped at L64K/L32
+;
+map_process:
+ ld a, h
+ or l
+ jr z, map_kernel
+map_process_hl:
+ ld a, (hl)
+map_process_a: ; used by bankfork
+ ld (mapreg), a
+ out (0x50), a
+ ret
+
+map_process_always:
+ push af
+ push hl
+ ld hl, #U_DATA__U_PAGE
+ call map_process_hl
+ pop hl
+ pop af
+ ret
+
+map_save: push af
+ ld a, (mapreg)
+ ld (mapsave), a
+ pop af
+ ret
+
+map_restore:
+ push af
+ ld a, (mapsave)
+ ld (mapreg), a
+ out (0x50), a
+ pop af
+ ret
+
+; No UART (could use printer port ?)
+outchar:
+ ret
+
+;
+; Swap helpers
+;
+_hd_xfer_in:
+ pop de
+ pop hl
+ push hl
+ push de
+ ld a, (_hd_page)
+ or a
+ call nz, map_process_a
+ ld bc, #0x48 ; 512 bytes from 0x48
+ inir
+ inir
+ call map_kernel
+ ret
+
+_hd_xfer_out:
+ pop de
+ pop hl
+ push hl
+ push de
+ ld a, (_hd_page)
+ or a
+ call nz, map_process_a
+ ld bc, #0x48 ; 512 bytes to 0x48
+ otir
+ otir
+ call map_kernel
+ ret
--- /dev/null
+-mwxuy
+-i uzi.ihx
+-b _CODE=0x0100
+-b _DISCARD=0xD000
+-b _COMMONMEM=0xE800
+-l z80
+platform-ubee/crt0.rel
+platform-ubee/commonmem.rel
+platform-ubee/ubee.rel
+start.rel
+version.rel
+lowlevel-z80.rel
+usermem.rel
+usermem_std-z80.rel
+platform-ubee/tricks.rel
+platform-ubee/main.rel
+timer.rel
+kdata.rel
+platform-ubee/devfd.rel
+platform-ubee/floppy.rel
+platform-ubee/devhd.rel
+platform-ubee/devices.rel
+devio.rel
+filesys.rel
+process.rel
+inode.rel
+syscall_exec.rel
+syscall_fs.rel
+syscall_fs2.rel
+syscall_proc.rel
+syscall_other.rel
+tty.rel
+mm.rel
+swap.rel
+bankfixed.rel
+vt.rel
+devsys.rel
+platform-ubee/devlpr.rel
+platform-ubee/devtty.rel
+-e