trs80 model 1: initial commit tree
authorAlan Cox <alan@linux.intel.com>
Tue, 22 May 2018 00:21:03 +0000 (01:21 +0100)
committerAlan Cox <alan@linux.intel.com>
Tue, 22 May 2018 00:21:03 +0000 (01:21 +0100)
Work in progress.

28 files changed:
Kernel/platform-trs80m1/Makefile [new file with mode: 0644]
Kernel/platform-trs80m1/README [new file with mode: 0644]
Kernel/platform-trs80m1/commonmem.s [new file with mode: 0644]
Kernel/platform-trs80m1/config.h [new file with mode: 0644]
Kernel/platform-trs80m1/crt0.s [new file with mode: 0644]
Kernel/platform-trs80m1/devfd.c [new file with mode: 0644]
Kernel/platform-trs80m1/devfd.h [new file with mode: 0644]
Kernel/platform-trs80m1/devgfx.c [new file with mode: 0644]
Kernel/platform-trs80m1/devgfx.h [new file with mode: 0644]
Kernel/platform-trs80m1/devhd.c [new file with mode: 0644]
Kernel/platform-trs80m1/devhd.h [new file with mode: 0644]
Kernel/platform-trs80m1/devhd_discard.c [new file with mode: 0644]
Kernel/platform-trs80m1/devices.c [new file with mode: 0644]
Kernel/platform-trs80m1/devlpr.c [new file with mode: 0644]
Kernel/platform-trs80m1/devlpr.h [new file with mode: 0644]
Kernel/platform-trs80m1/devtty.c [new file with mode: 0644]
Kernel/platform-trs80m1/devtty.h [new file with mode: 0644]
Kernel/platform-trs80m1/discard.c [new file with mode: 0644]
Kernel/platform-trs80m1/floppy.s [new file with mode: 0644]
Kernel/platform-trs80m1/fuzix.lnk [new file with mode: 0644]
Kernel/platform-trs80m1/kernel.def [new file with mode: 0644]
Kernel/platform-trs80m1/main.c [new file with mode: 0644]
Kernel/platform-trs80m1/rules.mk [new file with mode: 0644]
Kernel/platform-trs80m1/target.mk [new file with mode: 0644]
Kernel/platform-trs80m1/tricks.s [new file with mode: 0644]
Kernel/platform-trs80m1/trs80-bank.s [new file with mode: 0644]
Kernel/platform-trs80m1/trs80.s [new file with mode: 0644]
Kernel/platform-trs80m1/trs80load.s [new file with mode: 0644]

diff --git a/Kernel/platform-trs80m1/Makefile b/Kernel/platform-trs80m1/Makefile
new file mode 100644 (file)
index 0000000..f3c811e
--- /dev/null
@@ -0,0 +1,37 @@
+
+CSRCS = devlpr.c devtty.c devfd.c devhd.c devgfx.c
+CSRCS += devices.c main.c
+DISCARD_CSRCS = discard.c devhd_discard.c
+
+ASRCS = trs80.s trs80-bank.s crt0.s
+ASRCS += tricks.s commonmem.s floppy.s
+
+COBJS = $(CSRCS:.c=.rel)
+AOBJS = $(ASRCS:.s=.rel)
+DISCARD_COBJS = $(DISCARD_CSRCS:.c=.rel)
+OBJS  = $(COBJS) $(AOBJS) $(DISCARD_COBJS) $(DOBJS)
+
+CROSS_CCOPTS += -I../dev/
+
+JUNK = $(CSRCS:.c=.lst) $(CSRCS:.c=.asm) $(CSRCS:.c=.sym) $(ASRCS:.s=.lst) $(ASRCS:.s=.sym) $(CSRCS:.c=.rst) $(ASRCS:.s=.rst)
+
+all:   $(OBJS) trs80load.bin
+
+$(COBJS): %.rel: %.c
+       $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG1) -c $<
+
+$(AOBJS): %.rel: %.s
+       $(CROSS_AS) $(ASOPTS) $<
+
+$(DISCARD_COBJS): %.rel: %.c
+       $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $<
+
+clean:
+       rm -f $(OBJS) $(JUNK)  core *~ 
+
+image: trs80load.bin
+
+trs80load.bin: trs80load.s
+       sdasz80 -o trs80load.s
+       sdldz80 -i trs80load.rel
+       makebin -s 256 trs80load.ihx trs80load.bin
diff --git a/Kernel/platform-trs80m1/README b/Kernel/platform-trs80m1/README
new file mode 100644 (file)
index 0000000..e54bcba
--- /dev/null
@@ -0,0 +1,104 @@
+TRS80 Model 1
+
+       This is just a development tree. It doesn't yet have a loader nor
+       has it been tested!
+
+Emulator Bugs:
+       The emulator is horribly insecure, it's default is to allow all
+       sorts of direct access to things. Even if you turn this off I've
+       had it segfault with FUZIX bugs which suggests its not too secure.
+
+       Repeating instructions like LDIR appear to be misemulated. LDIR
+       is performed an emulated block copy, not as an iterated LDI. The
+       real processor actually implements LDIR as  "LDI, if not done
+       PC -= 2". FUZIX doesn't do any overlapped LDIR tricks so shouldn't
+       be affected.
+
+       The interrupt flags are misemulated. A patch for this is in the
+       Fuzix tree and is needed to run Fuzix on xtrs/sdltrs
+
+Requirements: 
+       TRS80 Model 1
+       Preferably the lower case mod
+       Supermem or compatible expansion
+       Floppy drive or Hard drive (strongly recommend the latter)
+
+Memory Map:
+       0000-3FFF       Various fixed model 1 functionality
+       4000-41FF       Kept clear for ROM bits
+       4200-7FFF       Kernel data, common etc
+       8000-FFFF       Bank0: kernel code
+       8000-FFFF       Bank1: kernel code
+
+       Remaining banks of 32K are user apps.
+
+       The kernel doesn't actually need 80K but it also doesn't fit in
+       the available 48K unbanked either (although a minimalish setup might)
+
+       Need to move buffers out of line using the external buffers code so we
+       can better balance memory
+
+Drivers:
+       Display 64x16                   In progress (mod from existing)
+       Keyboard                        Just an address change I think
+       Hard drive                      Straight model 4 port
+       Floppy                          In progress (initial code design)
+       Floppy drivers do not yet deal with double sided disks or sd/dd
+       media detection
+       Hard disk reads block 0, and handle partitions of some form
+       including finding where 'swap' lives
+
+Setting It Up (current xtrs: https://github.com/TimothyPMann/xtrs)
+
+       This needs patches currently in progress
+
+       # Tool to make disk images
+       make tools/makejv3
+       # Double density single sided 40 track boot disk
+       tools/makejv3 dd40s /dev/zero disk1-0
+       # Build kernel and boot block (edit Makefile, set target then)
+       make
+       # Add pieces to the disk (boot block in sector 0, kernel at end)
+       dd if=zout/trs80load.cim of=disk1-0 bs=1 seek=8960 conv=notrunc
+       dd if=fuzix.bin of=disk1-0 bs=1 seek=142336 conv=notrunc
+       # Once we get that far you can also put a filesystem in the lower
+       # blocks
+       #
+       xtrs -model 1 -emtsafe --supermem
+
+To set up for hard disk, create a hard disk with the xtrs tools and
+
+       mkdisk -h hard1-0
+       # This assumes the default disk size
+       cd Standalone/filesystem-src
+       ./build-filesystem trs80.hd 256 21760
+       dd if=trs80.hd of=hard1-0 bs=256 seek=1 conv=notrunc
+       tools/trslabel hard1-0
+       #
+       xtrs -model 1 -emtsafe -supermem
+
+
+You will still need a boot floppy at this point but just boot with device
+0 (or hit return). Swap is configured to be on the end of the hard disk
+
+
+
+Banking Models
+
+Currently Supported:
+       Alpha Technology Supermem upper 32K banking only.
+
+Not Yet Started:
+       "Selector" for Model 1. Port 31 allows memory reshuffling away from
+the model 1 default. Either the upper or lower 32K is switchable but not
+both at once. bits 4/5 control the selection between a further 4 32K banks.
+
+
+
+Useful rom addresses
+
+04C3 -> 64 column
+04F6 -> 32 column
+
+0060 -> 14.5*BC uS delay
+
diff --git a/Kernel/platform-trs80m1/commonmem.s b/Kernel/platform-trs80m1/commonmem.s
new file mode 100644 (file)
index 0000000..bafd590
--- /dev/null
@@ -0,0 +1,10 @@
+;
+;      We have no real common on the TRS80so just tuck it up at the top of
+;      memory leaving room for the keyboard and video (3K)
+;
+        .module commonmem
+
+        .area _COMMONMEM
+
+       .include "../cpu-z80/std-commonmem.s"
+
diff --git a/Kernel/platform-trs80m1/config.h b/Kernel/platform-trs80m1/config.h
new file mode 100644 (file)
index 0000000..ef657a4
--- /dev/null
@@ -0,0 +1,62 @@
+/* Set if you want RTC support and have an RTC on ports 0xB0-0xBC */
+#define CONFIG_RTC
+/* 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) */
+#undef 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       16
+
+#define MAP_SIZE       0x8000
+
+#define CONFIG_BANKS   2       /* 2 x 32K */
+
+/* Vt definitions */
+#define VT_BASE                ((uint16_t)0x3C00
+#define VT_WIDTH       64
+#define VT_HEIGHT      16
+#define VT_RIGHT       63
+#define VT_BOTTOM      15
+
+#define TICKSPERSEC 40     /* Ticks per second */
+#define PROGBASE    0x8000  /* Base of user  */
+#define PROGLOAD    0x8000  /* Load and run here */
+#define PROGTOP     0xFE00  /* 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    0x8000     /* 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 (512K!) */
+
+#define swap_map(x)    ((uint8_t *)(x))
+
+#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  (swap_dev)  /* Device for swapping (dynamic). */
+#define NBUFS    5        /* Number of block buffers - keep in sync with asm! */
+#define NMOUNTS         2        /* Number of mounts at a time */
+/* Reclaim the discard space for buffers */
+#define CONFIG_DYNAMIC_BUFPOOL
+
+extern void platform_discard(void);
diff --git a/Kernel/platform-trs80m1/crt0.s b/Kernel/platform-trs80m1/crt0.s
new file mode 100644 (file)
index 0000000..a31b6c9
--- /dev/null
@@ -0,0 +1,84 @@
+               ; Ordering of segments for the linker.
+               ; WRS: Note we list all our segments here, even though
+               ; we don't use them all, because their ordering is set
+               ; when they are first seen.     
+               .area _CODE
+               .area _CODE1
+               .area _CODE2
+               .area _DISCARD2
+               .area _VIDEO
+               .area _COMMONMEM
+               .area _STUBS
+               .area _CONST
+               .area _INITIALIZED
+               .area _BSEG
+               .area _BSS
+               .area _HEAP
+               ; note that areas below here may be overwritten by the heap at runtime, so
+               ; put initialisation stuff in here
+               .area _GSINIT
+               .area _GSFINAL
+               .area _DATA
+               .area _BUFFERS
+               .area _INITIALIZER
+               ; Buffers must be directly before discard as they will
+               ; expand over it
+
+               ; imported symbols
+               .globl _fuzix_main
+               .globl init_early
+               .globl init_hardware
+               .globl s__DATA
+               .globl l__DATA
+               .globl s__BUFFERS
+               .globl l__BUFFERS
+               .globl s__COMMONMEM
+               .globl l__COMMONMEM
+               .globl s__INITIALIZER
+               .globl kstack_top
+               .globl bufend
+
+               ; exports
+               .globl _discard_size
+
+               ; startup code
+               .area _CODE
+
+;
+;      Once the loader completes it jumps here
+;
+start:
+               ld sp, #kstack_top
+               ; then zero the data area
+               ld hl, #s__DATA
+               ld de, #s__DATA + 1
+               ld bc, #l__DATA - 1
+               ld (hl), #0
+               ldir
+;              Zero buffers area
+               ld hl, #s__BUFFERS
+               ld de, #s__BUFFERS + 1
+               ld bc, #l__BUFFERS - 1
+               ld (hl), #0
+               ldir
+               ld hl,#0x8000
+               ld de,#bufend
+               or a
+               sbc hl,de
+               ld (_discard_size),hl
+               call init_early
+               call init_hardware
+               push af
+               call _fuzix_main
+               pop af
+               di
+stop:          halt
+               jr stop
+
+               .area _DATA
+_discard_size:
+               .dw 0
+
+       .area _STUBS
+stubs:
+       .ds 768
diff --git a/Kernel/platform-trs80m1/devfd.c b/Kernel/platform-trs80m1/devfd.c
new file mode 100644 (file)
index 0000000..7a15485
--- /dev/null
@@ -0,0 +1,102 @@
+#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;
+
+/* Extern as they live in common */
+extern uint8_t fd_map, fd_tab[MAX_FD];
+extern uint8_t fd_selected;
+extern uint8_t fd_cmd[6];
+
+/*
+ *     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)
+{
+    int ct = 0;
+    int tries;
+    uint8_t err = 0;
+    uint8_t *driveptr = fd_tab + minor;
+
+    if(rawflag == 2)
+        goto bad2;
+
+    /* FIXME: We force DD for now */
+    err = fd_motor_on(selmap[minor]|0x80);
+    if (err)
+        goto bad;
+
+    if (*driveptr == 0xFF)
+        fd_reset(driveptr);
+
+    fd_map = rawflag;
+    if (rawflag && d_blkoff(BLKSHIFT))
+            return -1;
+
+    udata.u_nblock *= 2;
+
+    fd_cmd[0] = is_read ? FD_READ : FD_WRITE;
+    fd_cmd[1] = udata.u_block / 9;             /* 2 sectors per block */
+    fd_cmd[2] = ((udata.u_block % 9) << 1) + 1;        /*eww.. */
+    fd_cmd[3] = is_read ? OPDIR_READ: OPDIR_WRITE;
+    fd_cmd[4] = ((uint16_t)udata.u_dptr) & 0xFF;
+    fd_cmd[5] = ((uint16_t)udata.u_dptr) >> 8;
+
+    while (ct < udata.u_nblock) {
+        for (tries = 0; tries < 4 ; tries++) {
+            err = fd_operation(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;
+        fd_cmd[5]++;   /* Move on 256 bytes in the buffer */
+        fd_cmd[2]++;   /* Next sector for 2nd block */
+        ct++;
+    }
+    return udata.u_nblock << 8;
+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 fd_transfer(minor, false, rawflag);
+}
diff --git a/Kernel/platform-trs80m1/devfd.h b/Kernel/platform-trs80m1/devfd.h
new file mode 100644 (file)
index 0000000..672cf90
--- /dev/null
@@ -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 *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-trs80m1/devgfx.c b/Kernel/platform-trs80m1/devgfx.c
new file mode 100644 (file)
index 0000000..6ea6cba
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *     Graphics logic for the TRS80 graphics add on board
+ *
+ *     FIXME: - turn the gfx on/off when we switch tty
+ *            - tie gfx to one tty and report EBUSY for the other on enable
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <vt.h>
+#include <graphics.h>
+#include <devgfx.h>
+
+static const struct display trsdisplay[1] = {
+  {
+    /* Once we get around to it this is probably best described as
+       128 x 96 sixel */
+    0,
+    64, 16,
+    64, 16,
+    255, 255,
+    FMT_TEXT,
+    HW_UNACCEL,
+    GFX_MULTIMODE|GFX_TEXT,
+    2,
+    0
+  }
+};
+
+/* Assumes a Tandy board */
+static const struct videomap trsmap = {
+  0,
+  0,
+  0x3C00,
+  0x0400,              /* Directly mapped */
+  0, 0,        /* No segmentation */
+  1,           /* Standard spacing */
+  MAP_FBMEM_SIMPLE|MAP_FBMEM
+};
+
+static uint8_t vmode;
+
+int gfx_ioctl(uint8_t minor, uarg_t arg, char *ptr)
+{
+  uint8_t m;
+  int err;
+
+  if (arg >> 8 != 0x03)
+    return vt_ioctl(minor, arg, ptr);
+
+  switch(arg) {
+  case GFXIOC_GETINFO:
+    return uput(&trsdisplay[vmode], ptr, sizeof(struct display));
+  case GFXIOC_GETMODE:
+  case GFXIOC_SETMODE:
+    m = ugetc(ptr);
+    if (m != 0)
+      break;
+    if (arg == GFXIOC_GETMODE)
+      return uput(&trsdisplay[m], ptr, sizeof(struct display));
+    vmode = m;
+    return 0;
+  case GFXIOC_UNMAP:
+    return 0;
+  /* Users can "map" 8) the MMIO into their process and use the
+     card directly */
+  case GFXIOC_MAP:
+    if (vmode == 0)
+      break;
+    return uput(&trsmap, ptr, sizeof(trsmap));
+  }
+  return -1;
+}
diff --git a/Kernel/platform-trs80m1/devgfx.h b/Kernel/platform-trs80m1/devgfx.h
new file mode 100644 (file)
index 0000000..c29616d
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _DEV_GFX_H
+#define _DEV_GFX_H
+
+#include <graphics.h>
+
+extern int gfx_ioctl(uint8_t minor, uarg_t arg, char *ptr);
+extern void video_cmd(uint8_t *ptr);
+extern void video_read(uint8_t *ptr);
+extern void video_write(uint8_t *ptr);
+extern void video_exg(uint8_t *ptr);
+
+#endif
diff --git a/Kernel/platform-trs80m1/devhd.c b/Kernel/platform-trs80m1/devhd.c
new file mode 100644 (file)
index 0000000..81381c7
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ *     WD1010 hard disk driver
+ */
+
+#define _HD_PRIVATE
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devhd.h>
+#include <diskgeom.h>
+
+/* Used by the asm helpers */
+uint8_t hd_page;
+
+/* Swap is scanned for so not constant */
+uint16_t swap_dev;
+
+/* Seek and restore low 4 bits are the step rate, read/write support
+   multi-sector mode but not all emulators do .. */
+
+struct minipart parts[MAX_HD];
+
+/* Wait for the drive to show ready */
+uint8_t hd_waitready(void)
+{
+       uint8_t st;
+       do {
+               st = hd_status;
+       } while (!(st & 0x40));
+       return st;
+}
+
+/* Wait for DRQ or an error */
+uint8_t hd_waitdrq(void)
+{
+       uint8_t st;
+       do {
+               st = hd_status;
+       } while (!(st & 0x09));
+       return st;
+}
+
+uint8_t hd_xfer(bool is_read)
+{
+       /* Error ? */
+       if (hd_status & 0x01)
+               return hd_status;
+       if (is_read)
+               hd_xfer_in(udata.u_dptr);
+       else
+               hd_xfer_out(udata.u_dptr);
+       /* Should be returning READY, and maybe SEEKDONE */
+       return hd_status;
+}
+
+int hd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+       uint16_t ct = 0;
+       staticfast uint8_t tries;
+       uint8_t err = 0;
+       uint8_t cmd = HDCMD_READ;
+       uint8_t head;
+       uint8_t sector;
+       staticfast uint16_t cyl;
+       uint8_t dev = minor >> 4;
+       staticfast struct minipart *p;
+
+       p = &parts[dev];
+
+       /* FIXME: We only support 512 byte access chunks even for raw I/O */
+       hd_page = 0;
+       if (rawflag == 1) {
+               if (d_blkoff(BLKSHIFT))
+                       return -1;
+               hd_page = udata.u_page;         /* Kernel */
+       } else if (rawflag == 2)
+               hd_page = swappage;
+
+       udata.u_nblock *= 2;
+
+       if (!is_read)
+               cmd = HDCMD_WRITE;
+
+       /* TRS80 hard disk are 32 sectors/track, 256 byte sectors */
+
+       hd_precomp = p->g.precomp;
+       hd_seccnt = 1;
+
+       sector = udata.u_block;
+       sector = (sector << 1) & 0x1E;
+
+       cyl = udata.u_block >> 4;
+
+       /* Do the maths once and on 16 bit numbers */
+       head = cyl % p->g.head;
+       cyl /= p->g.head;
+       if (minor)
+               cyl += p->cyl[(minor-1)&0x0F];
+
+       while (ct < udata.u_nblock) {
+               /* Head next bits, plus drive */
+               hd_sdh = 0x80 | head | (dev << 3);
+               hd_secnum = sector;
+               /* cylinder bits */
+               hd_cyllo = cyl & 0xFF;
+               hd_cylhi = cyl >> 8;
+
+               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);
+                               /* Ready, no error ? */
+                               if ((err & 0x41) == 0x40)
+                                       break;
+                       } else
+                               kprintf("hd%d: err %x\n", minor, err);
+
+                       if (tries > 1) {
+                               hd_cmd = HDCMD_RESTORE | p->g.seek;
+                               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++;
+               udata.u_dptr += 256;
+               sector++;
+               /* Cheaper than division! */
+               if (sector == 32) {
+                       sector = 0;
+                       head++;
+                       if (head == p->g.head) {
+                               head = 0;
+                               cyl++;
+                       }
+               }
+       }
+       return ct << 8;
+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)
+{
+       uint8_t dev = minor >> 4;
+       flag;
+       if (dev > MAX_HD || parts[dev].g.head == 0 ||
+               (minor && parts[dev].cyl[(minor-1)&0x0F] == 0xFFFF)) {
+               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-trs80m1/devhd.h b/Kernel/platform-trs80m1/devhd.h
new file mode 100644 (file)
index 0000000..2f06e0b
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef __DEVHD_DOT_H__
+#define __DEVHD_DOT_H__
+
+/* public interface */
+extern int hd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+extern int hd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+extern int hd_open(uint8_t minor, uint16_t flag);
+
+extern void hd_probe(void);
+
+#ifdef _HD_PRIVATE
+
+__sfr __at 0xC0 hd_wpbits;     /* Write protect and IRQ (not used) */
+__sfr __at 0xC1 hd_ctrl;       /* Reset and enable bits */
+__sfr __at 0xC8 hd_data;
+__sfr __at 0xC9 hd_precomp;    /* W/O */
+__sfr __at 0xC9 hd_err;                /* R/O */
+__sfr __at 0xCA hd_seccnt;
+__sfr __at 0xCB hd_secnum;
+__sfr __at 0xCC hd_cyllo;
+__sfr __at 0xCD hd_cylhi;
+__sfr __at 0xCE hd_sdh;
+__sfr __at 0xCF hd_status;     /* R/O */
+__sfr __at 0xCF hd_cmd;
+
+#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 RATE_4MS       0x08    /* 4ms step rate for hd (conservative) */
+
+#define HDCTRL_SOFTRESET       0x10
+#define HDCTRL_ENABLE          0x08
+#define HDCTRL_WAITENABLE      0x04
+
+#define HDSDH_ECC256           0x80
+
+/* 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
+
+extern struct minipart parts[MAX_HD];
+
+extern uint8_t hd_waitready(void);
+extern uint8_t hd_waitdrq(void);
+extern uint8_t hd_xfer(bool is_read);
+
+/* helpers in common memory for the block transfers */
+extern int hd_xfer_in(uint8_t *addr);
+extern int hd_xfer_out(uint8_t *addr);
+
+#endif
+#endif /* __DEVHD_DOT_H__ */
diff --git a/Kernel/platform-trs80m1/devhd_discard.c b/Kernel/platform-trs80m1/devhd_discard.c
new file mode 100644 (file)
index 0000000..313b968
--- /dev/null
@@ -0,0 +1,96 @@
+#define _HD_PRIVATE
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devhd.h>
+#include <diskgeom.h>
+
+/*
+ *     Mini part processing. This and mbr and other things all one day
+ *     need to become a bit more unified.
+ */
+static void hd_swapon(struct minipart *p, unsigned int d, unsigned int i)
+{
+       uint16_t cyls;
+       if (i == 14 || p->cyl[i+1] == 0xFFFF)
+               cyls = p->g.cyl;
+       else
+               cyls = p->cyl[i+1];
+       cyls -= p->cyl[i];
+       cyls *= p->g.head;
+       /* This is now the number of sets of 32 sectors (16K) in swap.
+          We need 32K per process: hardwire it here - FIX if you change
+          the mapping model */
+
+       swap_dev = (d << 4) + i + 1;
+
+       if (cyls >= MAX_SWAPS)
+               cyls = MAX_SWAPS - 1;
+       for (i = 0; i < cyls; i++) {
+               swapmap_init(i);
+       }
+       kputs("swap-");
+}
+
+void hd_probe(void)
+{
+       unsigned int dev = 0;
+       unsigned int i;
+       /* Second half of second block */
+       struct minipart *p;
+
+       udata.u_dptr = tmpbuf();
+       p = (struct minipart *)(udata.u_dptr + 128);
+
+       for (dev = 0; dev < 4; dev++) {
+               hd_sdh = 0x80 | (dev << 3);
+               hd_cmd = HDCMD_RESTORE | RATE_4MS;
+               if (hd_waitready() & 1) {
+                       if ((hd_err & 0x12) == 0x12)
+                               continue;
+               }
+               hd_seccnt = 1;
+               hd_sdh = 0x80 | (dev << 3);
+               hd_secnum = 1;
+               hd_cyllo = 0;
+               hd_cylhi = 0;
+               hd_cmd = HDCMD_READ;
+               if (hd_waitdrq() & 1)
+                       continue;
+               if((hd_xfer(1) & 0x41) != 0x40)
+                       continue;
+               kprintf("hd%c: ", dev + 'a');
+               if (p->g.magic != MP_SIG_0) {
+                       p->g.cyl = 1;
+                       p->g.head = 1;
+                       p->g.sec = 32;
+                       p->g.precomp = 0;
+                       p->g.seek = 10;
+                       p->g.secsize = 8;
+                       for (i = 0; i < 15; i++)
+                               p->cyl[i] = 0xFFFFU;
+                       kputs("(unlabelled)\n");
+               } else {
+                       for (i = 0; i < 15; i++) {
+                               if (p->cyl[i] != 0xFFFFU) {
+                                       if (p->type[i] == 0x56) {
+                                               /* Configure swap */
+                                               hd_swapon(p, dev, i);
+                                       }
+                                       kprintf("hd%c%d ", dev + 'a', i + 1);
+                               }
+                       }
+                       kputs("\n");
+               }
+               if (p->g.seek) {
+                       p->g.seek /= 2;
+                       if (p->g.seek == 0)
+                               p->g.seek = 1;
+               }
+               /* Set the step rate */
+               hd_cmd = HDCMD_SEEK | p->g.seek;
+               hd_waitready();
+               memcpy(&parts[dev], p, sizeof(parts[dev]));
+       }
+       tmpfree(udata.u_dptr);
+}
diff --git a/Kernel/platform-trs80m1/devices.c b/Kernel/platform-trs80m1/devices.c
new file mode 100644 (file)
index 0000000..0f13813
--- /dev/null
@@ -0,0 +1,37 @@
+#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 <devdw.h>
+#include <vt.h>
+#include <devtty.h>
+#include <devgfx.h>
+
+struct devsw dev_tab[] =  /* The device driver switch table */
+{
+  /* 0: /dev/hd                Hard disc block devices */
+  {  hd_open,     no_close,     hd_read,   hd_write,   no_ioctl  },
+  /* 1: /dev/fd                Floppy disc block devices */
+  {  fd_open,     no_close,     fd_read,   fd_write,   no_ioctl  },
+  /* 2: /dev/tty       TTY devices */
+  {  tty_open,    trstty_close, tty_read,  tty_write,  gfx_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 },
+};
+
+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) - 1)
+       return false;
+    else
+        return true;
+}
+
diff --git a/Kernel/platform-trs80m1/devlpr.c b/Kernel/platform-trs80m1/devlpr.c
new file mode 100644 (file)
index 0000000..a55ae29
--- /dev/null
@@ -0,0 +1,52 @@
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <devlpr.h>
+
+#define lp     *((volatile uint8_t *)0x37E8)
+
+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;
+}
+
+static uint8_t iopoll(void)
+{
+       /* Ought to be a core helper for this lot ? */
+       if (need_reschedule())
+               _sched_yield();
+       if (chksigs()) {
+               if (!udata.u_done) {
+                       udata.u_error = EINTR;
+                       udata.u_done = (usize_t)-1;
+                }
+                return 1;
+       }
+       return 0;
+}
+
+int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    char *p = udata.u_base;
+    minor; rawflag; flag; // shut up compiler
+
+    while(udata.u_done < udata.u_count) {
+        /* Note; on real hardware it might well be necessary to
+           busy wait a bit just to get acceptable performance */
+        while (lp & 0x80) {
+            if (iopoll())
+                return udata.u_done;
+        }
+        /* FIXME: tidy up ugetc and sysio checks globally */
+        lp = ugetc(p++);
+        udata.u_done++;
+    }
+    return udata.u_done;
+}
diff --git a/Kernel/platform-trs80m1/devlpr.h b/Kernel/platform-trs80m1/devlpr.h
new file mode 100644 (file)
index 0000000..7765c18
--- /dev/null
@@ -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-trs80m1/devtty.c b/Kernel/platform-trs80m1/devtty.c
new file mode 100644 (file)
index 0000000..eed87db
--- /dev/null
@@ -0,0 +1,252 @@
+#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];
+
+uint8_t curtty;                /* output side */
+uint8_t inputtty;      /* input side */
+static struct vt_switch ttysave[2];
+static uint8_t vtbackbuf[VT_WIDTH * VT_HEIGHT];
+struct vt_repeat keyrepeat;
+
+uint8_t *vtbase[2] = { 0xF800, vtbackbuf };
+
+__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);
+}
+
+ttyready_t tty_writeready(uint8_t minor)
+{
+    uint8_t reg;
+    if (minor != 3)
+        return TTY_READY_NOW;
+    reg = tr1865_status;
+    return (reg & 0x40) ? TTY_READY_NOW : TTY_READY_SOON;
+}
+
+void vtbuf_init(void)
+{
+    memset(vtbackbuf, ' ', VT_WIDTH * VT_HEIGHT);
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+    if (minor == 2)
+        tr1865_rxtx = c;
+    else
+       vtoutput(&c, 1);
+}
+
+void tty_interrupt(void)
+{
+    uint8_t reg = tr1865_status;
+    if (reg & 0x80) {
+        reg = tr1865_rxtx;
+        tty_inproc(3, reg);
+    }
+}
+
+/* Called to set baud rate etc */
+static const uint8_t trsbaud[] = {
+    0,0,1,2, 3,4,5,6, 7,10,14, 15
+};
+
+static const uint8_t trssize[4] = {
+    0x00, 0x40, 0x20, 0x60
+};
+
+void tty_setup(uint8_t minor)
+{
+    uint8_t baud;
+    uint8_t ctrl;
+    if (minor != 2)
+        return;
+    baud = ttydata[2].termios.c_cflag & CBAUD;
+    if (baud > B19200) {
+        ttydata[2].termios.c_cflag &= ~CBAUD;
+        ttydata[2].termios.c_cflag |= B19200;
+        baud = B19200;
+    }
+    baud = trsbaud[baud];
+    tr1865_baud = baud | (baud << 4);
+
+    ctrl = 3;
+    if (ttydata[2].termios.c_cflag & PARENB) {
+        if (ttydata[2].termios.c_cflag & PARODD)
+            ctrl |= 0x80;
+    } else
+        ctrl |= 0x8;           /* No parity */
+    ctrl |= trssize[(ttydata[2].termios.c_cflag & CSIZE) >> 4];
+    tr1865_ctrl = ctrl;
+}
+
+int trstty_close(uint8_t minor)
+{
+    if (minor == 2 && ttydata[2].users == 0)
+        tr1865_ctrl = 0;       /* Drop carrier */
+    return tty_close(minor);
+}
+
+int tty_carrier(uint8_t minor)
+{
+    if (minor != 2)
+        return 1;
+    if (tr1865_ctrl & 0x80)
+        return 1;
+    return 0;
+}
+
+void tty_sleeping(uint8_t minor)
+{
+        used(minor);
+}
+
+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++;
+                                               newkey = 1;
+                                               keybyte = i;
+                                               keybit = n;
+                                       }
+                               }
+                               m += m;
+
+                       }
+               }
+               keymap[i] = keyin[i];
+       }
+}
+
+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', ':', ';', ',', '-', '.', '/' },
+       { KEY_ENTER, KEY_CLEAR, KEY_STOP, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ' '},
+       { 0, 0, 0, 0, KEY_F1, KEY_F2, KEY_F3, 0 }
+};
+
+uint8_t shiftkeyboard[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', '!', '"', '#', '$', '%', '&', '\'' },
+       {'(', ')', '*', '+', '<', '=', '>', '?' },
+       { KEY_ENTER, KEY_CLEAR, KEY_STOP, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ' '},
+       { 0, 0, 0, 0, KEY_F1, KEY_F2, KEY_F3, 0 }
+};
+
+static uint8_t capslock = 0;
+static uint8_t kbd_timer;
+
+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 (keymap[7] & 3) {   /* shift */
+                    if (c == '(')
+                        c = '{';
+                    if (c == ')')
+                        c = '}';
+                    if (c == '-')
+                        c = '_';
+                    if (c == '/')
+                        c = '``';
+                    if (c == '<')
+                        c = '^';
+                } else {
+                    if (c == '(')
+                        c = '[';
+                    else if (c == ')')
+                        c = ']';
+                    else if (c == '-')
+                        c = '|';
+                    else if (c > 31 && c < 127)
+                       c &= 31;
+                }
+       }
+       else if (capslock && c >= 'a' && c <= 'z')
+               c -= 'a' - 'A';
+       if (c)
+               vt_inproc(inputtty+1, c);
+}
+
+void kbd_interrupt(void)
+{
+       newkey = 0;
+       keyproc();
+       if (keysdown && keysdown < 3) {
+               if (newkey) {
+                       keydecode();
+                       kbd_timer = keyrepeat.first;
+               } else if (! --kbd_timer) {
+                       keydecode();
+                       kbd_timer = keyrepeat.continual;
+               }
+       }
+}
diff --git a/Kernel/platform-trs80m1/devtty.h b/Kernel/platform-trs80m1/devtty.h
new file mode 100644 (file)
index 0000000..12714e4
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _DEVTTY_H
+#define _DEVTTY_H
+
+extern void tty_interrupt(void);
+extern void kbd_interrupt(void);
+extern int trstty_close(uint8_t minor);
+extern void vtbuf_init(void);
+
+#define KEY_ROWS       8
+#define KEY_COLS       8
+extern uint8_t keymap[8];
+extern uint8_t keyboard[8][8];
+extern uint8_t shiftkeyboard[8][8];
+
+extern uint8_t *vtbase[2];
+extern uint8_t curtty;
+#endif
diff --git a/Kernel/platform-trs80m1/discard.c b/Kernel/platform-trs80m1/discard.c
new file mode 100644 (file)
index 0000000..9dcac11
--- /dev/null
@@ -0,0 +1,36 @@
+#include <kernel.h>
+#include <devhd.h>
+#include <devtty.h>
+#include <tty.h>
+
+void device_init(void)
+{
+  vtbuf_init();
+#ifdef CONFIG_RTC
+  /* Time of day clock */
+  inittod();
+#endif
+  hd_probe();
+  tty_setup(3);
+}
+
+void map_init(void)
+{
+}
+
+uint8_t nbanks = 2;    /* Default 2 banks, unless port 94 probe updates */
+
+void pagemap_init(void)
+{
+#ifdef CONFIG_MAP94
+ int i = nbanks - 1;
+ while (i) {
+  pagemap_add(i);      /* Mode 3, U64K low 32K mapped as low 32K */
+  pagemap_add(i|0x80); /* Mode 3, U64K high 32K mapped as low 32K */
+  i--;
+ }
+#else
+ pagemap_add(0x63);    /* Mode 3, U64K low 32K mapped as low 32K */
+ pagemap_add(0x73);    /* Mode 3, U64K high 32K mapped as low 32K */
+#endif
+}
diff --git a/Kernel/platform-trs80m1/floppy.s b/Kernel/platform-trs80m1/floppy.s
new file mode 100644 (file)
index 0000000..a40b6a9
--- /dev/null
@@ -0,0 +1,443 @@
+;
+;      Core floppy routines for the TRS80 1791 FDC
+;      Based on the 6809 code
+;
+;      FIXME: better drive spin up wait
+;      FIXME: tandy doubler
+;      FIXME: correct step rates (per drive ?)
+;      FIXME: precompensation ??
+;      FIXME: support speed setting (turbo cards)
+;
+       .globl _fd_reset
+       .globl _fd_operation
+       .globl _fd_motor_on
+       .globl _fd_motor_off
+       .globl _fd_map
+       .globl _fd_selected
+       .globl _fd_tab
+       .globl _fd_cmd
+       .globl map_kernel, map_process_always
+
+;
+;      The 1791 is memory mapped
+;
+FDCREG .equ    0x37EC
+FDCTRK .equ    0x37ED
+FDCSEC .equ    0x37EE
+FDCDATA        .equ    0x37EF
+;
+;      Drive select is also more complicated than other models
+;
+LATCHD0        .equ    0x37E1          ; also drive select
+LATCHD1        .equ    0x37E3
+LATCHD2        .equ    0x37E5
+LATCHD3        .equ    0x37E7
+
+;
+;      And the select is arranged as
+;      bit 0-3 select drives, bit 3 also selects side (so you can't have
+;      a disk 3 with any double sided drive)
+;
+
+;
+;      The final bit of weirdness is magic writes control the doubler. This
+;      actually *changes* the chip currently connected to those addresses
+;      so things like track must be written in the right order!
+;
+; Percom (to cmd port)
+P_SET_FM       .equ    0xFE
+P_SET_MFM      .equ    0xFF
+; Tandy (to track register)
+T_SET_FM       .equ    0xA0
+T_SET_MFM      .equ    0x80
+T_UNSET_PRECOMP        .equ    0xC0
+T_SET_PRECOMP  .equ    0xE0
+
+;
+;      It's our responsibility to wait 1 second for motor on and to allow
+;      80ms for head load. (and we can call 0060 for 14.65 * BC us delay)
+;
+
+;
+;      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
+;
+;      Set up and perform a disk operation
+;
+;      IX points to the command block
+;      BC points to the buffer
+;      DE points to the track reg copy
+;
+;      Drive must already be selected and density set up
+;
+fdsetup:
+       ld      hl,#FDCTRK
+       ld      a, (de)
+       ld      (hl), a                 ; Load track register
+       cp      TRACK(ix)
+       jr      z, fdiosetup            ; Is it the one we wanted
+       ;
+       ;       So we can verify
+       ;
+       inc     hl                      ; now FDCTRK
+       ld      a, TRACK(ix)
+       ld      (hl), a
+       inc     hl                      ; now FDCSEC
+       ld      a, SECTOR(ix)
+       ld      (hl), a
+       ;
+       ;       Need to seek the disk
+       ;
+       ld      hl,#FDCREG
+       ld      a, #0x18        ; seek  FIXME: need to set step rate
+       ld      (hl), a
+       call    waitcmd
+       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
+       ld      l,#0xFF         ; report failure
+       ret
+;
+;      Try and kick the controller back into sanity
+;
+bad_cmd:
+       ld      a,(hl)          ; clear status
+       ld      (hl),#0xD0      ; force interrupt
+       pop     af
+       ld      l,#255
+       ret
+       
+waitcmd:
+       ex      (sp),hl
+       ex      (sp),hl
+       ex      (sp),hl
+       ex      (sp),hl         ; 87 clocks + the call (17) = 104
+waitcmdl:
+       bit     0,(hl)
+       jr      z, waitfail
+       rlca
+       ld      a,(last_drive)  ; Keep the motor spinning
+       ld      (LATCHD0),a
+       jr      c, waitcmdl
+       ld      a,(hl)          ; Status
+       ret
+waitfail:
+       ld      a,#255
+       ret
+;
+;      Head in the right place
+;
+fdiosetup:
+       ld      a, TRACK(ix)
+       ld      (de), a         ; save track
+
+       ; FIXME: select which controller first etc
+
+       ld      de, #FDCSEC     ; sector register
+       ld      a, SECTOR(ix)
+       ld      (FDCSEC), a
+
+       inc     de              ; now points at FDCDATA
+
+       ld      hl, #FDCREG     ; status/cmd
+       ld      a,(hl)          ; clear status
+       
+       ld      a, DIRECT(ix)
+       dec     a
+       ld      a, CMD(ix)      ; 0 - none, 1 - in, 2 = out
+       ld      (hl),a
+       push    af              ; 11
+       ex      (sp),hl         ; 30            Delay 55us (98 clocks)
+       ex      (sp),hl         ; 49
+       ex      (sp),hl         ; 68
+       ex      (sp),hl         ; 87
+       add     a,#0            ; 94
+       di                      ; 98
+       bit     0,(hl)          ; controller busy ?
+       jr      z, bad_cmd
+       pop     af
+       jr      z, fdio_in
+       jr      nc, fdio_out
+       jr      fd_xferdone
+;
+;      Read from the disk - HL points to the target buffer. This is very
+;      tight timing on a 2MHz Z80 with a doubler.
+;
+;      We point HL at control/status, and DE at the data port. We can't do
+;      this with IX offsets as we don't have enough clocks for it.
+;
+fdio_in:
+fdio_do_in:
+       ld      a,#0x83                 ; Wait for the controller to go
+       and     (hl)                    ; ready
+       jp      po, fdio_do_in
+fdio_xfer_in:
+       ld      a,(de)                  ; 7     data from the port ASAP
+       ld      (bc),a                  ; 7     to memory
+       inc     bc                      ; 6     aligned buffer would be 4
+       ;
+       ;       Old trick. What we are effectively doing is synchronizing
+       ;       the controller and CPU knowing the worst case misalignment
+       ;
+       ;       We simply don't have time for this to be a loop
+       ;
+       ;       Our best case is 44 clocks per loop so 25us / loop. We have
+       ;       31us when running double density so will alternate between
+       ;       44 clock loops and 63 clock (35us) loops.
+       ;
+       bit     1,(hl)                  ; 12 clocks
+       jr      nz, fdio_xfer_in                ; 7 clocks if not taken
+       bit     1,(hl)
+       jr      nz, fdio_xfer_in
+       bit     1,(hl)
+       jr      nz, fdio_xfer_in
+       bit     1,(hl)                  ; we may have a CPU speed upgrade
+       jr      nz, fdio_xfer_in                ; fitted...
+       bit     1,(hl)
+       jr      nz, fdio_xfer_in
+       bit     1,(hl)
+       jr      nz, fdio_xfer_in
+       ;
+       ;       If another byte hasn't turned up in 114 clocks then either
+       ;       it failed or it finished. Until that happens we can't afford
+       ;       to check anything else
+       ;
+fd_xferdone:
+       ld      l,(hl)                  ; read the status
+       ld      (hl),#0xD0              ; force interrupt
+       ei
+       ret                             ; pass C code the status byte
+
+;
+;      Very similar to the above but going the other way
+;
+fdio_out:
+       ld      (hl),a                  ; issue command
+fdio_do_out:
+       ld      a,#0x83                 ; Wait for the controller to go
+       and     (hl)                    ; ready
+       jp      po, fdio_out
+fdio_xfer_out:
+       ld      a,(bc)                  ; 7     data from the user
+       ld      (de),a                  ; 7     to the port
+       inc     bc                      ; 6     aligned buffer would be 4
+       ;
+       ;       Old trick. What we are effectively doing is synchronizing
+       ;       the controller and CPU knowing the worst case misalignment
+       ;
+       ;       We simply don't have time for this to be a loop
+       ;
+       ;       Our best case is 44 clocks per loop so 25us / loop. We have
+       ;       31us when running double density so will alternate between
+       ;       44 clock loops and 63 clock (35us) loops.
+       ;
+       bit     1,(hl)                  ; 12 clocks
+       jr      nz, fdio_xfer_out               ; 7 clocks if not taken
+       bit     1,(hl)
+       jr      nz, fdio_xfer_out
+       bit     1,(hl)
+       jr      nz, fdio_xfer_out
+       bit     1,(hl)                  ; we may have a CPU speed upgrade
+       jr      nz, fdio_xfer_out               ; fitted...
+       bit     1,(hl)
+       jr      nz, fdio_xfer_out
+       bit     1,(hl)
+       jr      nz, fdio_xfer_out
+;
+;      Now tidy up
+;
+       jr      fd_xferdone
+
+
+;
+;      C glue interface.
+;
+;
+;      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, #1
+       ld      (FDCSEC), a
+       xor     a
+       ld      (FDCTRK), a
+       ld      a, #0x0C
+       ld      (FDCREG), a     ; restore
+       ld      a, #0xFF
+       ld      (hl), a         ; Zap track pointer
+       call    waitcmd
+       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:
+       ld      a, (_fd_map)
+       or      a
+       call    nz, map_process_always
+       pop     bc              ; return address
+       pop     de              ; drive track ptr
+       push    de
+       push    bc
+       push    ix
+       ld      ix, #_fd_cmd
+       ld      c, DATA(ix)
+       ld      b, DATA+1(ix)
+       call    fdsetup         ; Set up for a command
+       ld      h, #0
+       pop     ix
+       ld      a, (_fd_map)
+       or      a
+       ret     z
+       jp      map_kernel
+;
+;      Delay loops
+;
+wait45:                                ; wait 45ms for head load. HL points to 
+       ld      bc,#2662        ; latch - D is latch value, preserve HL/DE
+wait45l:
+       dec     bc              ; 6     30 cycles per loop (16.9us)
+       ld      a,b             ; 4
+       or      c               ; 4
+       jr      nz, wait45l     ; 12/7
+       ret
+
+;
+;      Wait about 500ms while poking the motor to keep it spinning.
+;
+waitdisk:
+       ld      b,#11
+waitdiskl:
+       push    bc
+       call    wait45l
+       ld      (hl),d          ; tickle the motor
+       pop     bc
+       djnz    waitdiskl
+       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 (yes side and drive 3 clash)
+;      bit 3:          side (must rewrite each drive change)
+;      bit 7:          set for double density (MFM)
+;
+;
+_fd_motor_on:
+       pop     de
+       pop     bc
+       push    bc
+       push    de
+
+       ;
+       ;       Is the motor running ?
+       ;
+       ld      a,(LATCHD0)
+       ld      e, a            ; save the latch status (motor on bit)
+       rlca
+       ld      a,(last_drive)
+       jr      nc, must_config ; if the motor is off always do set up
+       ;
+       ;       Are we changing our selection ?
+       ;
+       cp      c
+       jr      nz, must_config
+       ;
+       ;       Motor running, same configuration. Poke the selection
+       ;       so the motor stays running.
+       ;
+       ld      (LATCHD0),a
+       ret
+
+must_config:
+       ld      a,c             ; Save the new configuration
+       ld      (last_drive),a
+
+       ld      hl,#FDCREG
+
+       and     #0x7F           ; We borrowed bit 7 for our own use
+       ld      (LATCHD0), a    ; Selects the actual disk we want
+       ld      d,a             ; Save latch value
+       rl      c               ; Bit 7 into C
+       ld      a,#0xFE         ; Figure out the density
+       adc     a,#0            ; FE or FF according to density
+       di
+       ld      (hl),a          ; if a doubler is present this switches FDC
+       ; Do we need a delay here (eg if there is no doubler present)
+       ; and do we need to avoid the FE/FF scribbles on doubler-less hw
+       ; as we are writing twice to the FDC a few clocks apart otherwise
+       ld      (hl),#0xD0      ; Hit it over the head with a hammer
+       ei
+
+       ld      hl,#LATCHD0     ; used by waitdisk too
+
+       bit     7,e             ; was the motor running
+       jr      z, motor_running
+
+       call    waitdisk        ; wait 500ms or so for spin up
+motor_running:
+       ld      (hl),d
+       call    wait45          ; Wait 45ms for the head to load
+       ld      (hl),d          ; Reset timer
+       ret
+
+;
+;      C interface fd_motor_off(void)
+;
+;      Turns off the drive motors, deselects all drives
+;
+;      Not sure we need this.
+;
+_fd_motor_off:
+       xor     a
+       ld      (LATCHD0),a
+       ret
+
+last_drive:
+       .db     0xff
+_fd_map:
+       .db     0
+_fd_selected:
+       .db     0xFF
+_fd_tab:
+       .db     0xFF, 0xFF, 0xFF, 0xFF
+_fd_cmd:
+       .ds     6
diff --git a/Kernel/platform-trs80m1/fuzix.lnk b/Kernel/platform-trs80m1/fuzix.lnk
new file mode 100644 (file)
index 0000000..213593f
--- /dev/null
@@ -0,0 +1,48 @@
+-mwxuy
+-r
+-i fuzix.ihx
+-b _INITIALIZER=0x0001
+-b _CODE=0x4200
+-b _CODE1=0x8000
+-b _CODE2=0x8000
+-b _COMMONMEM=0x4300
+-l z80
+platform-trs80m1/crt0.rel
+platform-trs80m1/commonmem.rel
+platform-trs80m1/trs80.rel
+platform-trs80m1/trs80-bank.rel
+start.rel
+version.rel
+lowlevel-z80-banked.rel
+usermem.rel
+usermem_std-z80-banked.rel
+platform-trs80m1/tricks.rel
+platform-trs80m1/main.rel
+timer.rel
+kdata.rel
+platform-trs80m1/devfd.rel
+platform-trs80m1/floppy.rel
+platform-trs80m1/devhd.rel
+platform-trs80m1/devhd_discard.rel
+platform-trs80m1/devgfx.rel
+platform-trs80m1/devices.rel
+devio.rel
+filesys.rel
+process.rel
+inode.rel
+syscall_exec16.rel
+syscall_fs.rel
+syscall_fs2.rel
+syscall_fs3.rel
+syscall_proc.rel
+syscall_other.rel
+tty.rel
+mm.rel
+swap.rel
+bankfixed.rel
+vt.rel
+devsys.rel
+platform-trs80m1/devlpr.rel
+platform-trs80m1/devtty.rel
+platform-trs80m1/discard.rel
+-e
diff --git a/Kernel/platform-trs80m1/kernel.def b/Kernel/platform-trs80m1/kernel.def
new file mode 100644 (file)
index 0000000..ac7c025
--- /dev/null
@@ -0,0 +1,17 @@
+; UZI mnemonics for memory addresses etc
+
+U_DATA                      .equ 0x4300       ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE           .equ 0x200        ; 256+256 (we don't save istack)
+
+U_DATA_STASH               .equ 0xFE00       ; FE00-FFFF
+
+PROGBASE                   .equ 0x8000
+PROGLOAD                   .equ 0x8000
+
+Z80_TYPE                   .equ 1
+
+NBUFS                      .equ 5
+
+Z80_MMU_HOOKS              .equ 0
+
+CONFIG_SWAP                .equ 1
diff --git a/Kernel/platform-trs80m1/main.c b/Kernel/platform-trs80m1/main.c
new file mode 100644 (file)
index 0000000..3d03046
--- /dev/null
@@ -0,0 +1,76 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+
+uint16_t ramtop = PROGTOP;
+
+uint8_t vtattr_cap;
+
+struct blkbuf *bufpool_end = bufpool + NBUFS;
+
+/* We need to spin here because we don't have interrupts for the UART on the
+   model I */
+void platform_idle(void)
+{
+  irqflags_t irq = di();
+  platform_interrupt();
+  irqrestore(irq);
+}
+
+void do_beep(void)
+{
+}
+
+uint8_t platform_param(char *p)
+{
+    used(p);
+    return 0;
+}
+
+void platform_interrupt(void)
+{
+  uint8_t irq = *((volatile uint8_t *)0x37E0);
+  uint8_t dummy;
+  /* FIXME: do we care about floppy interrupts */
+  if (irq & 0x80)
+    *((volatile uint8_t *)0x37EC);
+  else {
+    tty_interrupt();
+    kbd_interrupt();
+    if (irq & 0x80) {  /* FIXME??? */
+      timer_interrupt();
+      *((volatile uint8_t *)0x37E0);   /* Ack the timer */
+    }
+  }
+}
+
+/*
+ *     We can't recover discard space usefully... yet. I have a cunning plan
+ *     involving external buffers in the spare bank space 8)
+ */
+
+void platform_discard(void)
+{
+}
+
+#ifdef CONFIG_RTC
+
+__sfr __at 0xB0 rtc_secl;
+__sfr __at 0xB1 rtc_sech;
+
+/* FIXME: the RTC is optional so we should test for it first */
+uint8_t platform_rtc_secs(void)
+{
+    uint8_t sl, rv;
+    /* BCD encoded */
+    do {
+        sl = rtc_secl;
+        rv = sl + rtc_sech * 10;
+    } while (sl != rtc_secl);
+    return rv;
+}
+
+#endif
+
diff --git a/Kernel/platform-trs80m1/rules.mk b/Kernel/platform-trs80m1/rules.mk
new file mode 100644 (file)
index 0000000..64073e5
--- /dev/null
@@ -0,0 +1,20 @@
+#
+#      TRS80 model 1 uses banked kernel images
+#
+CROSS_CCOPTS += --external-banker
+#
+# Tell the core code we are using the banked helpers
+#
+export BANKED=-banked
+export CROSS_CC_SEG1=--codeseg CODE1
+export CROSS_CC_SEG2=--codeseg CODE2
+export CROSS_CC_SEG3=--codeseg CODE1
+export CROSS_CC_VIDEO=--codeseg CODE2
+#
+export CROSS_CC_SYS1=--codeseg CODE1
+export CROSS_CC_SYS2=--codeseg CODE1
+export CROSS_CC_SYS3=--codeseg CODE1
+export CROSS_CC_SYS4=--codeseg CODE2
+export CROSS_CC_SYS5=--codeseg CODE2
+export CROSS_CC_SEGDISC=--codeseg DISCARD2 --constseg DISCARD2
+
diff --git a/Kernel/platform-trs80m1/target.mk b/Kernel/platform-trs80m1/target.mk
new file mode 100644 (file)
index 0000000..3bffcde
--- /dev/null
@@ -0,0 +1 @@
+export CPU = z80
diff --git a/Kernel/platform-trs80m1/tricks.s b/Kernel/platform-trs80m1/tricks.s
new file mode 100644 (file)
index 0000000..08a3be5
--- /dev/null
@@ -0,0 +1,5 @@
+
+       .include "../kernel.def"
+       .include "kernel.def"
+
+       .include "../lib/z80fixedbank-banked.s"
diff --git a/Kernel/platform-trs80m1/trs80-bank.s b/Kernel/platform-trs80m1/trs80-bank.s
new file mode 100644 (file)
index 0000000..f89ac75
--- /dev/null
@@ -0,0 +1,261 @@
+;
+;          TRS 80 banking logic for the base Model 4 and 4P
+;
+
+            .module trs80bank
+
+            ; exported symbols
+            .globl init_hardware
+           .globl map_kernel
+           .globl map_process
+           .globl map_process_a
+           .globl map_process_always
+           .globl map_process_save
+           .globl map_kernel_restore
+           .globl map_save
+           .globl map_restore
+
+            ; imported symbols
+           .globl _program_vectors     
+            .globl _ramsize
+            .globl _procmem
+
+           .globl s__COMMONMEM
+           .globl l__COMMONMEM
+
+            .include "kernel.def"
+            .include "../kernel.def"
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (below 0xE800, only accessible when the kernel is mapped)
+; -----------------------------------------------------------------------------
+            .area _CODE
+
+init_hardware:
+            ; set system RAM size
+           ; TODO - sizing
+            ld hl, #144
+            ld (_ramsize), hl
+            ld hl, #(144-80)           ; 80K 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
+
+ksave_map:  .db 0x00   ; saved kernel map version
+map_port:   .db 0x43   ; cheap hack so we can ld bc and out (c),b
+map_reg:    .db 0x00   ; current value written to map register
+map_store:  .db 0x00   ; save value from map_save
+;
+;      Map in the kernel. We are in common and all kernel entry points are
+;      in common. Thus someone will always have banked in the right page
+;      before jumping out of common. Thus we don't need to do anything but
+;      restore the last saved kernel map!
+;
+map_kernel:
+map_kernel_restore:
+           push af
+           ld a, (ksave_map)
+           ld (map_reg), a
+           out (0x43), a
+           pop af
+           ret
+;
+;      Select the bank for the relevant process. Update the ksave_map so we
+;      can restore the correct kernel mapping when banked.
+;
+map_process:
+           ld a, h
+           or l
+           jr z, map_kernel
+map_process_hl:
+            ld a, (map_reg)
+           ld (ksave_map),a    ; for map_kernel_restore
+           ld a,(hl)           ; udata page
+           ld (map_reg),a
+           out (0x43), a
+            ret
+
+map_process_a:                 ; used by bankfork
+           push af
+           ld a, (map_reg)
+           ld (ksave_map), a
+           pop af
+           ld (map_reg), a
+           out (0x43), a
+           ret
+
+map_process_save:
+map_process_always:
+           push af
+           ld a, (map_reg);
+           ld (map_store), a
+           pop af
+           ret
+
+map_save:   push af
+           ld a, (map_reg)
+           ld (map_store), a
+           pop af
+           ret
+
+map_restore:
+           push af
+           ld a, (map_store)
+           ld (map_reg), a
+           out (0x43), a
+           pop af
+           ret
+
+;
+;      This lot is tricky.
+;
+       .globl __bank_0_1
+       .globl __bank_0_2
+       .globl __bank_1_2
+       .globl __bank_2_1
+       .globl __stub_0_1
+       .globl __stub_0_2
+
+;
+;      Start with the easy ones - we are going from common
+;      to a bank. We can use registers here providing the compiler expects
+;      them to be destroyed by the called function, and on the return
+;      providing they are also not a return value. In practice that means
+;      we need to avoid IX going in and IX HL DE coming out.
+;
+;      The linker rewrote
+;                      push af call foo pop af
+;      into
+;                      call __bank_0_1 defw foo
+;
+;      We expand these into separate functions as they are executed a fair
+;      bit
+;
+;      The only hard case to understand logically here is calls and stubs
+;      from common to a bank. In those cases we must save the previous bank
+;      and restore it. We do this because we optimise the performance by
+;      making calls into COMMON or CODE free of bank work so we can put
+;      performance sensitive widely used routines (like compiler helpers)
+;      there. The cost of that is that if we do call back out of common
+;      space we have to do the restore as we return.
+;
+__bank_0_2:                    ; Call from common to bank 2
+       ld a,#1
+       jr bankina
+__bank_0_1:                    ; Call from common to bank 1
+       xor a
+bankina:
+       pop hl                  ; Get the return address
+       ld e,(hl)               ; The two bytes following it are the true
+       inc hl                  ; function to call
+       ld d,(hl)
+       inc hl
+       push hl
+       ld bc,(map_port)
+       ld (map_reg),a
+       out (c),a
+       ex de,hl
+       ld a,b
+       or a
+       jr z, retbank1          ; Arrange that we put the banks back as they
+       call callhl             ; were before the call. Needed because calls
+       ld a,#1                 ; to common routines don't do banking so
+       ld (map_reg),a          ; the return won't either.
+       out (0x43),a
+       ret
+retbank1:
+       call callhl
+       xor a
+       ld (map_reg),a
+       out (0x43),a
+       ret
+
+__bank_1_2:
+       ld a,#1
+       pop hl                  ; Get the return address
+       ld e,(hl)               ; The two bytes following it are the true
+       inc hl                  ; function to call
+       ld d,(hl)
+       inc hl
+       push hl
+       ld bc,(map_port)
+       ld (map_reg),a
+       out (c),a
+       ex de,hl
+       call callhl
+       xor a
+       ld (map_reg),a
+       out (0x43),a
+       ret
+
+__bank_2_1:
+       xor a
+       pop hl                  ; Get the return address
+       ld e,(hl)               ; The two bytes following it are the true
+       inc hl                  ; function to call
+       ld d,(hl)
+       inc hl
+       push hl
+       ld bc,(map_port)
+       ld (map_reg),a
+       out (c),a
+       ex de,hl
+       call callhl
+       ld a,#1
+       ld (map_reg),a
+       out (0x43),a
+       ret
+
+;
+;      Stubs are similar. Where there is a function pointer the linker adds
+;      a call in common and resolves the function pointer to the code in
+;      common. The function stub it adds is simply
+;
+;              ld de,#realaddr
+;              call __stub_0_%d   where %d is the target bank
+;
+;      For the same reason as bank calls we need to restore the previous
+;      banking setup.
+;
+__stub_0_1:
+       xor a
+       jr stub_call
+__stub_0_2:
+       ld a,#1
+stub_call:
+       pop hl                  ; return address
+       ex (sp),hl              ; swap the junk word for the return
+       ld bc,(map_port)
+       ld (map_reg),a
+       out (c),a
+       ex de,hl
+       ld a,b
+       or a
+       jr z, stub_ret_1
+       call callhl
+       ld a,#1
+       jr stub_ret
+stub_ret_1:
+       call callhl             ; invoke code in other bank
+       xor a                   ; and back to bank 1
+stub_ret:
+       ld (map_reg),a
+       out (0x43),a
+       pop bc                  ; bc is now the return
+       push bc                 ; stack it twice
+       push bc
+       ret                     ; and ret - can't use jp (ix) or jp (hl) here
+callhl:        jp (hl)
diff --git a/Kernel/platform-trs80m1/trs80.s b/Kernel/platform-trs80m1/trs80.s
new file mode 100644 (file)
index 0000000..0307f22
--- /dev/null
@@ -0,0 +1,136 @@
+;
+;          TRS 80  hardware support
+;
+
+            .module trs80
+
+            ; exported symbols
+            .globl init_early
+            .globl init_hardware
+            .globl interrupt_handler
+            .globl _program_vectors
+           .globl platform_interrupt_all
+
+           ; 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 _platform_monitor
+            .globl _platform_reboot
+            .globl outchar
+
+            ; imported symbols
+            .globl _ramsize
+            .globl _procmem
+            .globl istack_top
+            .globl istack_switched_sp
+            .globl unix_syscall_entry
+            .globl outcharhex
+           .globl null_handler
+           .globl map_kernel
+           .globl map_process
+           .globl map_process_a
+           .globl map_process_always
+           .globl map_save
+           .globl map_restore
+
+           .globl s__COMMONMEM
+           .globl l__COMMONMEM
+
+           .globl _bufpool
+           .globl bufend
+
+            .include "kernel.def"
+            .include "../kernel.def"
+
+           .area _BUFFERS
+
+_bufpool:
+           .ds BUFSIZE * NBUFS
+bufend:
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (0xE800 upwards)
+; -----------------------------------------------------------------------------
+            .area _COMMONMEM
+
+_platform_monitor:
+           di
+           halt
+
+platform_interrupt_all:
+           ret
+
+_platform_reboot:
+          di
+          ld sp,#0xffff
+          xor a
+          out (0x43),a
+          rst 0
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (above 0x8000)
+; -----------------------------------------------------------------------------
+            .area _CODE
+
+init_early:
+            ret
+
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+            .area _COMMONMEM
+
+_program_vectors:
+           ret
+
+_rom_vectors:
+           ld a,#0xC3
+           ld (0x4012), a
+            ld hl, #interrupt_handler
+            ld (0x4013), hl
+
+            ; set restart vector for UZI system calls
+            ld (0x400F), a   ;  (rst 30h is unix function call vector)
+            ld hl, #unix_syscall_entry
+            ld (0x4010), hl
+
+           jp map_kernel
+           
+; outchar: Wait for UART TX idle, then print the char in A
+; destroys: AF
+outchar:
+            out (0xEB), a
+            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, #0xC8                 ; 256 bytes from 0xC8
+          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, #0xC8                 ; 256 bytes to 0xC8
+          otir
+          call map_kernel
+          ret
+
diff --git a/Kernel/platform-trs80m1/trs80load.s b/Kernel/platform-trs80m1/trs80load.s
new file mode 100644 (file)
index 0000000..f4a22f9
--- /dev/null
@@ -0,0 +1,158 @@
+.z80
+;
+;      TRS80 bootblock (256 bytes)
+;
+;      FIXME rewrite for model 1
+;
+.area      BOOT        (ABS)
+.org       0x000
+start:
+           ld a, #0x86                 ; kernel map, 80 column, no remap
+           out (0x84), a               ; video page 1
+           ld a, #0x50                 ; 80 column, sound, altchars off,
+                                       ; ext I/O on , 4MHz
+           out (0xEC), a
+           ld hl, #0x4300
+           ld de, #0x0
+           ld bc, #256
+           ldir
+           jp go
+;
+;      Assume the boot ROM left us on track 0 and motor on
+;
+floppy_read:
+           ld bc, #0x81F4              ; select drive
+           out (c), b
+           out (0xF2), a               ; sector please
+           ld a, #0x80                 ; READ
+           out (0xF0), a
+           ld b, #100
+l1:        djnz l1
+           ld de, #0x8116
+           ld bc, #0xF3
+flopin:            in a, (0xF0)
+           and e
+           jr z, flopin
+           ini
+           ld a, d
+flopind:
+           out (0xF4), a
+           ini
+           jr nz, flopind
+flopstat:
+           in a, (0xF0)
+           and #0x19
+           bit 0, a
+           jr nz, flopstat
+           or a
+           ret z
+           ld de, #0xF850
+           call prints
+           .ascii 'Read\0'
+fail:      jr fail
+;
+go:   
+           ld sp, #0xEE00              ; temp stackpointer
+
+            ; load the 6845 parameters
+           ld hl, #_ctc6845            ; reverse order
+           ld bc, #0x0F88
+ctcloop:    out (c), b                 ; select register
+           ld a, (hl)
+           out (0x89), a               ; output data
+           dec hl
+           djnz ctcloop
+
+           ; clear screen
+           ld hl, #0xF800
+           ld de, #0xF801
+           ld bc, #0x07FF
+           ld (hl), #' '
+           ldir
+           ld de, #0xF800
+           call prints
+           .ascii 'TRS80Load 0.2\0'
+
+           ld hl, #0x0100
+;
+
+
+lastsec:    ld a, (tracknum)
+           inc a
+           ld (tracknum), a
+           cp #40
+           jr z, booted
+           out (0xF3), a
+           ld a, #0x18                 ; seek
+           out (0xF0), a
+           ld b, #100
+cmd1:      djnz cmd1
+cmdwait:    in a, (0xF0)
+           bit 0, a
+           jr z, seekstat
+           bit 7, a
+           jr z, cmdwait
+seekstat:
+           in a, (0xF0)
+           and #0x18
+           bit 0, a
+           jr nz, seekstat
+           or a
+           jr z, secmove
+           ld de, #0xF850
+           call prints
+           .ascii 'seek\0'
+bad:       jr bad
+secmove:    xor a
+           dec a       
+           ld (secnum), a
+nextsec:
+           ld a, (secnum)
+           inc a
+           ld (secnum), a
+           cp #18
+           jr z, lastsec
+           push hl
+           call floppy_read
+           pop hl
+           inc h
+           jr nextsec
+
+tracknum:   .db 28                     ; tracks 29-39 (50688 bytes)
+
+                               ; ctc6845 registers in reverse order
+           .db 99
+           .db 80
+           .db 85
+           .db 10
+           .db 25
+           .db 4
+           .db 24
+           .db 24
+           .db 0
+           .db 9
+           .db 101
+           .db 9
+           .db 0
+           .db 0
+secnum:            .db 0               ; recycle last crc value as secnum so we fit 256 bytes
+_ctc6845:   .db 0
+
+
+prints:
+          pop hl
+printsl:
+          ld a, (hl)
+          inc hl
+          or a
+          jr z, out
+          ld (de), a
+          inc de
+          jr printsl
+out:      jp (hl)
+
+booted:            ld de, #0xF850
+           call prints
+           .ascii 'Booting..\0'
+
+; OS starts on the following byte