From: Alan Cox Date: Sun, 21 Dec 2014 17:55:15 +0000 (+0000) Subject: ubee: initial bits cloned from the TRS80 to begin a Microbee 128/256/512 port X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=503b7d93d187e911ca2982e2525c3ca5452dbdd9;p=FUZIX.git ubee: initial bits cloned from the TRS80 to begin a Microbee 128/256/512 port --- diff --git a/Kernel/platform-ubee/Makefile b/Kernel/platform-ubee/Makefile new file mode 100644 index 00000000..7f907383 --- /dev/null +++ b/Kernel/platform-ubee/Makefile @@ -0,0 +1,25 @@ + +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: diff --git a/Kernel/platform-ubee/README b/Kernel/platform-ubee/README new file mode 100644 index 00000000..64288816 --- /dev/null +++ b/Kernel/platform-ubee/README @@ -0,0 +1 @@ +uBee draft code diff --git a/Kernel/platform-ubee/config.h b/Kernel/platform-ubee/config.h new file mode 100644 index 00000000..63d39624 --- /dev/null +++ b/Kernel/platform-ubee/config.h @@ -0,0 +1,53 @@ +/* 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 */ + diff --git a/Kernel/platform-ubee/devfd.c b/Kernel/platform-ubee/devfd.c new file mode 100644 index 00000000..9b08ebc3 --- /dev/null +++ b/Kernel/platform-ubee/devfd.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include + +#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); +} diff --git a/Kernel/platform-ubee/devfd.h b/Kernel/platform-ubee/devfd.h new file mode 100644 index 00000000..5f945073 --- /dev/null +++ b/Kernel/platform-ubee/devfd.h @@ -0,0 +1,15 @@ +#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__ */ diff --git a/Kernel/platform-ubee/devhd.c b/Kernel/platform-ubee/devhd.c new file mode 100644 index 00000000..9539704b --- /dev/null +++ b/Kernel/platform-ubee/devhd.c @@ -0,0 +1,191 @@ +/* + * WD1010 hard disk driver + */ + +#include +#include +#include +#include + +__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); +} diff --git a/Kernel/platform-ubee/devhd.h b/Kernel/platform-ubee/devhd.h new file mode 100644 index 00000000..41200a76 --- /dev/null +++ b/Kernel/platform-ubee/devhd.h @@ -0,0 +1,13 @@ +#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__ */ diff --git a/Kernel/platform-ubee/devices.c b/Kernel/platform-ubee/devices.c new file mode 100644 index 00000000..fdb13ac7 --- /dev/null +++ b/Kernel/platform-ubee/devices.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/Kernel/platform-ubee/devlpr.c b/Kernel/platform-ubee/devlpr.c new file mode 100644 index 00000000..65d8c439 --- /dev/null +++ b/Kernel/platform-ubee/devlpr.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include + +__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); +} diff --git a/Kernel/platform-ubee/devlpr.h b/Kernel/platform-ubee/devlpr.h new file mode 100644 index 00000000..7765c187 --- /dev/null +++ b/Kernel/platform-ubee/devlpr.h @@ -0,0 +1,8 @@ +#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 diff --git a/Kernel/platform-ubee/devtty.c b/Kernel/platform-ubee/devtty.c new file mode 100644 index 00000000..9ec6ac5d --- /dev/null +++ b/Kernel/platform-ubee/devtty.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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 diff --git a/Kernel/platform-ubee/devtty.h b/Kernel/platform-ubee/devtty.h new file mode 100644 index 00000000..300ecd62 --- /dev/null +++ b/Kernel/platform-ubee/devtty.h @@ -0,0 +1,8 @@ +#ifndef _DEVTTY_H +#define _DEVTTY_H + +extern void tty_interrupt(void); +extern void kbd_interrupt(void); +extern int trstty_close(uint8_t minor); + +#endif diff --git a/Kernel/platform-ubee/floppy.s b/Kernel/platform-ubee/floppy.s new file mode 100644 index 00000000..9c61c030 --- /dev/null +++ b/Kernel/platform-ubee/floppy.s @@ -0,0 +1,390 @@ +; +; 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 diff --git a/Kernel/platform-ubee/kernel.def b/Kernel/platform-ubee/kernel.def new file mode 100644 index 00000000..c6bc875a --- /dev/null +++ b/Kernel/platform-ubee/kernel.def @@ -0,0 +1,10 @@ +; 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 diff --git a/Kernel/platform-ubee/main.c b/Kernel/platform-ubee/main.c new file mode 100644 index 00000000..08dae68d --- /dev/null +++ b/Kernel/platform-ubee/main.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include + +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 */ +} diff --git a/Kernel/platform-ubee/ubee.s b/Kernel/platform-ubee/ubee.s new file mode 100644 index 00000000..4929dd95 --- /dev/null +++ b/Kernel/platform-ubee/ubee.s @@ -0,0 +1,258 @@ +; +; 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 diff --git a/Kernel/platform-ubee/uzi.lnk b/Kernel/platform-ubee/uzi.lnk new file mode 100644 index 00000000..b678a97e --- /dev/null +++ b/Kernel/platform-ubee/uzi.lnk @@ -0,0 +1,40 @@ +-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