kernel: very initial Z280 sketch of a port
authorAlan Cox <alan@linux.intel.com>
Wed, 29 Aug 2018 21:27:25 +0000 (22:27 +0100)
committerAlan Cox <alan@linux.intel.com>
Wed, 29 Aug 2018 21:27:25 +0000 (22:27 +0100)
Doesn't really use Z280 features properly yet. Basically a Z80 port running
entirely supervisor mode and faking banking with the MMU.

17 files changed:
Kernel/platform-z280rc/Makefile [new file with mode: 0644]
Kernel/platform-z280rc/README [new file with mode: 0644]
Kernel/platform-z280rc/commonmem.s [new file with mode: 0644]
Kernel/platform-z280rc/config.h [new file with mode: 0644]
Kernel/platform-z280rc/crt0.s [new file with mode: 0644]
Kernel/platform-z280rc/devices.c [new file with mode: 0644]
Kernel/platform-z280rc/devtty.c [new file with mode: 0644]
Kernel/platform-z280rc/devtty.h [new file with mode: 0644]
Kernel/platform-z280rc/discard.c [new file with mode: 0644]
Kernel/platform-z280rc/fuzix.lnk [new file with mode: 0644]
Kernel/platform-z280rc/kernel.def [new file with mode: 0644]
Kernel/platform-z280rc/main.c [new file with mode: 0644]
Kernel/platform-z280rc/platform_ide.h [new file with mode: 0644]
Kernel/platform-z280rc/target.mk [new file with mode: 0644]
Kernel/platform-z280rc/tricks.s [new file with mode: 0644]
Kernel/platform-z280rc/z280.h [new file with mode: 0644]
Kernel/platform-z280rc/z280rc.s [new file with mode: 0644]

diff --git a/Kernel/platform-z280rc/Makefile b/Kernel/platform-z280rc/Makefile
new file mode 100644 (file)
index 0000000..fc6123c
--- /dev/null
@@ -0,0 +1,55 @@
+CROSS_CCOPTS += -I../dev/
+
+
+CSRCS = devtty.c
+CSRCS += devices.c main.c
+
+DISCSRCS = discard.c
+
+ASRCS = z280rc.s crt0.s
+ASRCS += tricks.s commonmem.s
+
+DISCARD_DSRCS = ../dev/devide_discard.c
+DSRCS = ../dev/blkdev.c ../dev/devide.c ../dev/mbr.c
+
+NSRCS = 
+
+COBJS = $(CSRCS:.c=.rel)
+AOBJS = $(ASRCS:.s=.rel)
+NOBJS = $(patsubst ../dev/net/%.c,%.rel, $(NSRCS))
+DISCOBJS = $(DISCSRCS:.c=.rel)
+DISCARD_DOBJS = $(patsubst ../dev/%.c,%.rel, $(DISCARD_DSRCS))
+DOBJS = $(patsubst ../dev/%.c,%.rel, $(DSRCS))
+DAOBJS = $(patsubst ../dev/%.s,%.rel, $(DASRCS))
+
+OBJS  = $(COBJS) $(AOBJS) $(NOBJS) $(DISCOBJS) $(DOBJS) $(DISCARD_DOBJS) $(DAOBJS)
+
+JUNK =  *.lst *.asm *.sym *.rst *.lst
+
+all:   $(OBJS)
+
+$(COBJS): %.rel: %.c
+       $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(DISCOBJS): %.rel: %.c
+       $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $<
+
+$(DOBJS): %.rel: ../dev/%.c
+       $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(DISCARD_DOBJS): %.rel: ../dev/%.c
+       $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $<
+
+$(NOBJS): %.rel: ../dev/net/%.c
+       $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(AOBJS): %.rel: %.s
+       $(CROSS_AS) $(ASOPTS) $<
+
+$(DAOBJS): %.rel: ../dev/%.s
+       $(CROSS_AS) $(ASOPTS) $@ $<
+
+clean:
+       rm -f $(OBJS) $(JUNK)  core *~
+
+image:
diff --git a/Kernel/platform-z280rc/README b/Kernel/platform-z280rc/README
new file mode 100644 (file)
index 0000000..7677bec
--- /dev/null
@@ -0,0 +1,50 @@
+Minimal Initial Support for Bill Shen's Z280RC
+
+This is a port in progress. It doesn't work yet.
+
+
+Z280RC is a Z280 based SBC with limited support for RC2014 peripherals. Only
+8bit I/O port access to the bus is supported and IM2 cannto be used. In
+practice this isn't much of a limitation as the Z280RC is self contained for
+all things that matter.
+
+Requirements
+
+Z280RC SBC
+
+Supported
+
+Z280 on board UART as console
+Z280 on board timers for clock tick and baud rate (NEITHER DONE YET)
+16bit IDE/CF adapter on the board
+
+Not Supported (initially)
+
+Z280 processor modes
+Virtual memory
+Proper paging
+Split I/D
+
+
+Memory Map
+
+We fake up a Z80 with banked memory and use the MMU to create 8 (for now)
+user banks with the top 8K common and the rest per bank, plus a kernel bank
+that is the full 64K identity mapped.
+
+User Mode
+
+0000-00FF      Z80 vectors and RST
+0100-DDFF      User code
+DE00-DFFF      udata stash
+E000-EFFF      Unused
+F000-FFFF      Common space (lots of room left)
+
+Kernel Mode
+
+0000-00FF      Z80 vectors and RST
+0100-????      Kernel
+????-EFFF      Buffers
+F000-FFFF      Common space (lots of room left)
+
+No external peripherals are currently supported.
diff --git a/Kernel/platform-z280rc/commonmem.s b/Kernel/platform-z280rc/commonmem.s
new file mode 100644 (file)
index 0000000..ed19918
--- /dev/null
@@ -0,0 +1,11 @@
+;
+;      The common memory area traditionally starts with the udata and the
+;      interrupt stacks. As this is standard in almost all cases you can
+;      just include the standard implementation.
+;
+        .module commonmem
+
+        .area _COMMONMEM
+
+       .include "../cpu-z80/std-commonmem.s"
+
diff --git a/Kernel/platform-z280rc/config.h b/Kernel/platform-z280rc/config.h
new file mode 100644 (file)
index 0000000..b9e20ad
--- /dev/null
@@ -0,0 +1,122 @@
+/* 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
+
+/* This is a quick hack before we worry about the real MMU and all the rest
+   of it */
+
+/* Select a banked memory set up */
+#define CONFIG_BANK_FIXED
+/* This is the number of banks of user memory available (maximum) */
+#define MAX_MAPS       15              /* 512 KByte... minus the high one */
+/* How big is each bank - in our case 32K, 48K is actually more common. This
+   is hardware dependant */
+#define MAP_SIZE       0xE000
+/* How many banks do we have in our address space */
+#define CONFIG_BANKS   1
+
+/*
+ *     Define the program loading area (needs to match kernel.def)
+ */
+#define PROGBASE    0x0000  /* Base of user  */
+#define PROGLOAD    0x0100  /* Load and run here */
+#define PROGTOP     0xDE00  /* Top of program, base of U_DATA stash */
+#define PROC_SIZE   48     /* Memory needed per process including stash */
+/*
+ *     Definitions for swapping.
+ */
+#define SWAPDEV     (swap_dev) /* A variable for dynamic, or a device major/minor */
+extern unsigned int swap_dev;
+#define SWAP_SIZE   0x70       /* 56K in 512 byte blocks */
+#define SWAPBASE    0x0000     /* We swap the lot in one, include the */
+#define SWAPTOP            0xE000      /* vectors so its a round number of sectors */
+
+#define MAX_SWAPS      16      /* Maximum number of swapped out processes.
+                                   As we use the default 15 process max this
+                                   is definitely sufficient (14 would do) */
+/*
+ *     When the kernel swaps something it needs to map the right page into
+ *     memory using map_for_swap and then turn the user address into a
+ *     physical address. For a simple banked setup there is no conversion
+ *     needed so identity map it.
+ */
+#define swap_map(x)    ((uint8_t *)(x))
+
+/* Set these two for networking - no point right now */
+//#define CONFIG_NET
+//#define CONFIG_NET_NATIVE
+
+
+/* What is the maximum number of /dev/hd devices we have. In theory right now
+   it's actually 3 - two in the IDE and one on the SD interface */
+#define MAX_BLKDEV     4
+/* Select IDE disk support, and PPIDE (parallel port IDE) as the interface */
+#define CONFIG_IDE
+#define CONFIG_PPIDE   /* PPIDE is present */
+
+/* We will resize the buffers available after boot. This is the normal setting */
+#define CONFIG_DYNAMIC_BUFPOOL
+/* Swap will be set up when a suitably labelled partition is seen */
+#define CONFIG_DYNAMIC_SWAP
+/* Larger transfers (including process execution) should go directly not via
+   the buffer cache. For all small (eg bit) systems this is the right setting
+   as it avoids polluting the small cache with data when it needs to be full
+   of directory and inode information */
+#define CONFIG_LARGE_IO_DIRECT
+
+/* Specify this if there is a real time clock capable of reporting seconds. It
+   will be used to lock the kernel time better to reality. Other details like
+   Y2K support, or even supporting dates as well don't matter */
+#undef CONFIG_RTC
+/* Specify that there is a full real time clock that can supply the date and
+   time to the system. */
+#undef CONFIG_RTC_FULL
+/* Set this if the system has no proper real time clock (or has configurations
+   where it lacks one). This is not usually needed but for platforms it is also
+   see platform-sbcv2/main.c on what is needed */
+#undef CONFIG_NO_CLOCK
+/*
+ * How fast does the clock tick (if present), or how many times a second do
+ * we simulate if not. For a machine without video 10 is a good number. If
+ * you have video you probably want whatever vertical sync/blank interrupt
+ * rate the machine has. For many systems it's whatever the hardware gives
+ * you.
+ *
+ * Note that this needs to be divisible by 10 and at least 10. If your clock
+ * is a bit slower you may need to fudge things somewhat so that the kernel
+ * gets 10 timer interrupt calls per second. 
+ */
+#define TICKSPERSEC 10     /* Ticks per second */
+
+/*
+ *     The device (major/minor) for the console and boot up tty attached to
+ *     init at start up. 512 is the major 2, so all the tty devices are
+ *     512 + n where n is the tty.
+ */
+#define BOOT_TTY (512 + 1)      /* Set this to default device for stdio, stderr */
+                          /* In this case, the default is the first TTY device */
+/*
+ *     If you have a mechanism to pass in a root device configuration then
+ *     this holds the address of the buffer (eg a CP/M command line or similar).
+ *     If the configuration is fixed then this can be a string holding the
+ *     configuration. NULL means 'prompt the user'.
+ */
+#define CMDLINE        NULL      /* Location of root dev name */
+
+/* Device parameters */
+#define NUM_DEV_TTY 1    /* How many tty devices does the platform support */
+#define TTYDEV   BOOT_TTY /* Device used by kernel for messages, panics */
+#define NBUFS    5        /* Number of block buffers. Must be 4+ and must match
+                             kernel.def */
+#define NMOUNTS         4        /* Number of mounts at a time */
+
+/* This can optionally be set to force a default baud rate, eg if the system
+   console should match a firmware set rate */
+#define TTY_INIT_BAUD B38400   /* To match ROMWBW */
diff --git a/Kernel/platform-z280rc/crt0.s b/Kernel/platform-z280rc/crt0.s
new file mode 100644 (file)
index 0000000..d93868a
--- /dev/null
@@ -0,0 +1,74 @@
+               ; Ordering of segments for the linker.
+               .area _CODE
+               .area _CODE2
+               .area _HOME
+               .area _CONST
+               .area _INITIALIZED
+               .area _DATA
+               .area _BSEG
+               .area _BSS
+               .area _HEAP
+               .area _GSINIT
+               .area _GSFINAL
+               .area _BUFFERS
+               .area _DISCARD
+               .area _VECTORS
+               .area _COMMONMEM
+               ; note that areas below here may be overwritten by the heap at runtime, so
+               ; put initialisation stuff in here
+               .area _INITIALIZER
+
+               ; imported symbols
+               .globl _fuzix_main
+               .globl init_early
+               .globl init_hardware
+               .globl s__DATA
+               .globl l__DATA
+               .globl s__DISCARD
+               .globl l__DISCARD
+               .globl s__BUFFERS
+               .globl l__BUFFERS
+               .globl s__COMMONMEM
+               .globl l__COMMONMEM
+               .globl s__INITIALIZER
+               .globl kstack_top
+               .globl map_kernel
+
+               ; startup code
+               .area _CODE
+
+               ; Load at 0x0100
+               ; We are executed as a CP/M task identity mapped into the
+               ; low 64K. Nice and simple. We just expand ourselves all
+               ; over CP/M and take over.
+start:
+               di
+               ld sp, #kstack_top
+               ; move the common memory where it belongs    
+               ld hl, #s__DATA
+               ld de, #s__COMMONMEM
+               ld bc, #l__COMMONMEM
+               ldir
+               ; then the discard
+               ; Discard can just be linked in but is next to the buffers
+               ld de, #s__DISCARD
+               ld bc, #l__DISCARD
+               ldir
+               ; 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
+               call init_early
+               call init_hardware
+               call _fuzix_main
+               di
+stop:          halt
+               jr stop
diff --git a/Kernel/platform-z280rc/devices.c b/Kernel/platform-z280rc/devices.c
new file mode 100644 (file)
index 0000000..d51eca9
--- /dev/null
@@ -0,0 +1,45 @@
+#include <kernel.h>
+#include <tty.h>
+#include <version.h>
+#include <kdata.h>
+#include <devsys.h>
+#include <devtty.h>
+#include <blkdev.h>
+#include <devide.h>
+#include <printf.h>
+#include <devfd.h>
+
+/*
+ *     This table is the glue that holds all the kernel device driver
+ *     logic together. Each device driver provides methods for
+ *     open, close, read, write and ioctl, although it can opt to use
+ *     defaults as well.
+ *
+ *     The validdev function is the same for all platforms but has to live
+ *     in the same file as the table. Just paste it into each.
+ */
+
+struct devsw dev_tab[] =  /* The device driver switch table */
+{
+  /* 0: /dev/hd                Hard disc block devices */
+  {  blkdev_open, no_close,     blkdev_read,   blkdev_write,   blkdev_ioctl  },
+  /* 1: /dev/fd                Floppy disc block devices */
+  {  no_open,     no_close,     no_rdwr,       no_rdwr ,       no_ioctl  },
+  /* 2: /dev/tty       TTY devices */
+  {  tty_open,    tty_close,    tty_read,      tty_write,      tty_ioctl },
+  /* 3: /dev/lpr       Printer devices */
+  {  nxio_open,   no_close,     no_rdwr,       no_rdwr,        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) - 1)
+       return false;
+    else
+        return true;
+}
diff --git a/Kernel/platform-z280rc/devtty.c b/Kernel/platform-z280rc/devtty.c
new file mode 100644 (file)
index 0000000..e9295b1
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ *     This file implements the serial ports for the platform. Fuzix implements
+ *     a reasonable subset of the System 5 termios. Certain things that are
+ *     rarely relevant like XCASE, delay fills and parity are left to the
+ *     driver if desired.
+ *
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <tty.h>
+#include <devtty.h>
+#include <z280.h>
+
+/* Remember these are in I/O FExxxx range */
+__sfr __at 0x12 uart_tsr;
+__sfr __at 0x14 uart_rsr;
+__sfr __at 0x16 uart_rdr;
+__sfr __at 0x18 uart_tdr;
+
+/*
+ *     One buffer for each tty. For now just the console
+ */
+static char tbuf1[TTYSIZ];
+
+/*
+ *     One entry per tty. The 0th entry is never used as tty minor 0 is
+ *     special (/dev/tty) and it's cheaper to waste a few bytes that keep
+ *     doing subtractions.
+ */
+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},
+};
+
+/* Write to system console. This is the backend to all the kernel messages,
+   kprintf(), panic() etc. */
+
+void kputchar(char c)
+{
+       if (c == '\n')
+               tty_putc(1, '\r');
+       tty_putc(1, c);
+}
+
+/*
+ *     See if the given tty is able to transmit data without blocking. This
+ *     may be done by checking the hardware, or if there is a software
+ *     transmit queue by checking the queue is full.
+ *
+ *     There are three possible returns
+ *     TTY_READY_NOW means fire away
+ *     TTY_READY_SOON means we will spin trying until pre-empted. As the
+ *             8bit processors are slow relative to baud rates it's often
+ *             more efficient to do this
+ *     TTY_READY_LATER means we will give up the CPU. This is best if the
+ *             baud rate is low, the link is blocked by flow control signals
+ *             or the CPU is fast.
+ *
+ *     If TTY_READY_LATER is returned then the kernel will also call
+ *     tty_sleeping(minor) before sleeping on the tty so that the driver
+ *     can turn on or off tx complete interrupts.
+ *
+ *     A video display that never blocks will just return TTY_READY_NOW
+ */
+uint8_t tty_writeready(uint8_t minor)
+{
+       uint8_t r, iob;
+       used(minor);
+       iob = io_bank_set(0xFE);
+       r = uart_tsr & 0x01 ? TTY_READY_NOW : TTY_READY_SOON;
+       io_bank_set(iob);
+       return r;
+}
+
+/*
+ *     Write a character to a tty. This is the normal user space path for
+ *     each outbound byte. It gets called in the normal tty flow, but may
+ *     also be called from an interrupt to echo characters even if the
+ *     tty is busy. This one reason to implement a small transmit queue.
+ *
+ *     If the character echo doesn't fit just drop it. It should pretty much
+ *     never occur and there is nothing else to do.
+ */
+void tty_putc(uint8_t minor, unsigned char c)
+{
+       uint8_t iob;
+       used(minor);
+       iob = io_bank_set(0xFE);
+       uart_tdr = c;
+       io_bank_set(iob);
+}
+
+/*
+ *     16x50 conversion betwen a Bxxxx speed rate (see tty.h) and the values
+ *     to stuff into the chip.
+ */
+static uint16_t clocks[] = {
+       12,             /* Not a real rate */
+       2304,
+       1536,
+       1047,
+       857,
+       768,
+       384,
+       192,
+       96,
+       48,
+       24,
+       12,
+       6,
+       3,
+       2,
+       1
+};
+
+/*
+ *     This function is called whenever the terminal interface is opened
+ *     or the settings changed. It is responsible for making the requested
+ *     changes to the port if possible. Strictly speaking it should write
+ *     back anything that cannot be implemented to the state it selected.
+ *
+ *     That needs tidying up in many platforms and we also need a proper way
+ *     to say 'this port is fixed config' before making it so.
+ */
+void tty_setup(uint8_t minor)
+{
+       used(minor);
+}
+
+/*
+ *     This function is called when the kernel is about to sleep on a tty.
+ *     We don't care about this.
+ */
+void tty_sleeping(uint8_t minor)
+{
+       used(minor);
+}
+
+/*
+ *     Return 1 if the carrier on the terminal is raised. If the port has
+ *     no carrier signal always return 1. It is used to block a port on open
+ *     until carrier.
+ */
+int tty_carrier(uint8_t minor)
+{
+       used(minor);
+       return 1;
+}
+
+/*
+ *     When the input queue is part drained this method is called from the
+ *     kernel so that hardware flow control signals can be updated.
+ */
+void tty_data_consumed(uint8_t minor)
+{
+       used(minor);
+}
+
+/*
+ *     Our platform specific code so we have a function to call to poll the
+ *     serial ports for activity.
+ */
+void tty_poll(void)
+{      
+       uint8_t iob = io_bank_set(0xFE);
+       /* Should be IRQ driven but we might not be so poll anyway if
+          pending. IRQs are off here so this is safe */
+       if (uart_rsr & 0x10)
+               tty_inproc(1, uart_rdr);
+       io_bank_set(iob);
+}
diff --git a/Kernel/platform-z280rc/devtty.h b/Kernel/platform-z280rc/devtty.h
new file mode 100644 (file)
index 0000000..f0188e8
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _DEVTTY_H
+#define _DEVTTY_H
+
+extern void tty_poll(void);
+extern uint8_t timermsr;
+
+
+#endif
diff --git a/Kernel/platform-z280rc/discard.c b/Kernel/platform-z280rc/discard.c
new file mode 100644 (file)
index 0000000..0b11a13
--- /dev/null
@@ -0,0 +1,88 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include <blkdev.h>
+#include <devide.h>
+#include <propio2.h>
+
+extern int strcmp(const char *, const char *);
+
+/*
+ *     Everything in this file ends up in discard which means the moment
+ *     we try and execute init it gets blown away. That includes any
+ *     variables declared here so beware!
+ */
+
+/*
+ *     We get passed each kernel command line argument. if we return 1 then
+ *     we claim it, if not it gets passed to init. It's perfectly acceptable
+ *     to act on a match and return to also pass it to init if you need to.
+ */
+uint8_t platform_param(unsigned char *p)
+{
+       used(p);
+       return 0;
+}
+
+/*
+ *     Set up our memory mappings. This is not needed for simple banked memory
+ *     only more complex setups such as 16K paging.
+ */
+void map_init(void)
+{
+}
+
+/*
+ *     Add all the available pages to the list of pages we an use. If this
+ *     is runtime dynamic check to make sure you don't add more than MAX_MAPS
+ *     of them. On some machines with a lot of RAM the implementation turns
+ *     the excess into a RAM disc
+ *
+ *     The mapping can be logical numbers 1-n, or can be physical values to
+ *     write into registers. Whatever works best. The 0 value is however
+ *     reserved throughout to indicate kernel mode in calls, and also
+ *     to mean swapped out for processes. If your bank 0 is user space you
+ *     might need to just dec/inc before using it in an I/O port or similar
+ *     to avoid confusion.
+ *
+ *     Kernel in bank 0, user in banks 1-8. That's enough to get us going
+ *     until we do real Z280 management
+ */
+void pagemap_init(void)
+{
+       uint8_t i;
+       for (i = 1; i <= 8; i++)
+               pagemap_add(i);
+}
+
+/*
+ *     This function is called for partitioned devices if a partition is found
+ *     and marked as swap type. The first one found will be used as swap. We
+ *     only support one swap device.
+ */
+void platform_swap_found(uint8_t letter, uint8_t m)
+{
+  blkdev_t *blk = blk_op.blkdev;
+  uint16_t n;
+  if (swap_dev != 0xFFFF)
+    return;
+  letter -= 'a';
+  kputs("(swap) ");
+  swap_dev = letter << 4 | m;
+  n = blk->lba_count[m - 1] / SWAP_SIZE;
+  if (n > MAX_SWAPS)
+    n = MAX_SWAPS;
+  while(n)
+    swapmap_add(n--);
+}
+
+/*
+ *     Called after interrupts are enabled in order to enumerate and set up
+ *     any devices. In our case we simply need to probe the IDE and SD card.
+ */
+void device_init(void)
+{
+       devide_init();
+}
diff --git a/Kernel/platform-z280rc/fuzix.lnk b/Kernel/platform-z280rc/fuzix.lnk
new file mode 100644 (file)
index 0000000..f0a1d4c
--- /dev/null
@@ -0,0 +1,41 @@
+-mwxuy
+-i fuzix.ihx
+-b _CODE=0x0100
+-b _VECTORS=0xF000
+-b _INITIALIZER=0xFD00
+-l z80
+platform-z280rc/crt0.rel
+platform-z280rc/commonmem.rel
+platform-z280rc/z280rc.rel
+start.rel
+version.rel
+lowlevel-z80.rel
+usermem.rel
+usermem_std-z80.rel
+platform-z280rc/tricks.rel
+platform-z280rc/main.rel
+platform-z280rc/discard.rel
+timer.rel
+kdata.rel
+platform-z280rc/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
+devsys.rel
+platform-z280rc/devtty.rel
+platform-z280rc/blkdev.rel
+platform-z280rc/mbr.rel
+platform-z280rc/devide.rel
+platform-z280rc/devide_discard.rel
+-e
diff --git a/Kernel/platform-z280rc/kernel.def b/Kernel/platform-z280rc/kernel.def
new file mode 100644 (file)
index 0000000..5765fb5
--- /dev/null
@@ -0,0 +1,44 @@
+;      FUZIX mnemonics for memory addresses etc
+;
+;
+;      The U_DATA address. If we are doing a normal build this is the start
+;      of common memory. We do actually have a symbol for udata so
+;      eventually this needs to go away
+;
+U_DATA                      .equ 0xE000       ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE           .equ 0x200        ; 256+256 bytes)
+;
+;      Space for the udata of a switched out process within the bank of
+;      memory that it uses. Normally placed at the very top
+;
+U_DATA_STASH               .equ 0xDE00       ; DE00-DFFF
+;
+;      Z80 systems start program space at 0, and load at 0x100 so that the
+;      low 256 bytes are free for syscall vectors and the like, with some
+;      also used as a special case by the CP/M emulator.
+;
+PROGBASE                   .equ 0x0000
+PROGLOAD                   .equ 0x0100
+;
+;      CPU type
+;      0 = CMOS Z80 (set this for Z280 for now)
+;      1 = NMOS Z80 (also works with CMOS)
+;      2 = Z180
+;
+;      If either NMOS or CMOS may be present pick NMOS as the NMOS build
+;      contains extra code to work around an erratum n the NUMS Z80
+;
+Z80_TYPE                   .equ 0
+;
+;      For special platforms that have external memory protectuon hardware
+;      Just say 0.
+;
+Z80_MMU_HOOKS              .equ 0
+;
+;      Set this if the platform has swap enabled in config.h
+;
+CONFIG_SWAP                .equ 1
+;
+;      The number of disk buffers. Must match config.h
+;
+NBUFS                      .equ 5
diff --git a/Kernel/platform-z280rc/main.c b/Kernel/platform-z280rc/main.c
new file mode 100644 (file)
index 0000000..5b625fb
--- /dev/null
@@ -0,0 +1,129 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include <blkdev.h>
+#include <devide.h>
+#include <z280.h>
+
+uint16_t ramtop = PROGTOP;
+uint16_t swap_dev = 0xFFFF;
+
+/*
+ *     This routine is called continually when the machine has nothing else
+ *     it needs to execute. On a machine with entirely interrupt driven
+ *     hardware this could just halt for interrupt.
+ */
+void platform_idle(void)
+{
+       /* Disable interrupts so we don't accidentally process a polled tty
+          and interrupt call at once and make a mess */
+       irqflags_t irq = di();
+       tty_poll();
+       /* Restore prior state. */
+       irqrestore(irq);
+}
+
+/*
+ *     This routine is called from the interrupt handler code to process
+ *     interrupts. All of the nasty stuff (register saving, bank switching,
+ *     reti instructions) is dealt with for you.
+ */
+void platform_interrupt(void)
+{
+       tty_poll();
+       timer_interrupt();
+}
+
+/* This points to the last buffer in the disk buffers. There must be at least
+   four buffers to avoid deadlocks. */
+struct blkbuf *bufpool_end = bufpool + NBUFS;
+
+/*
+ *     We pack discard into the memory image is if it were just normal
+ *     code but place it at the end after the buffers. When we finish up
+ *     booting we turn everything from the buffer pool to common into
+ *     buffers. This blows away the _DISCARD segment.
+ */
+void platform_discard(void)
+{
+        /* We start our common block with the vectors as they must be 4K
+           aligned */
+       uint16_t discard_size = (uint16_t)vectors - (uint16_t)bufpool_end;
+       bufptr bp = bufpool_end;
+
+       discard_size /= sizeof(struct blkbuf);
+
+       kprintf("%d buffers added\n", discard_size);
+
+       bufpool_end += discard_size;
+
+       memset( bp, 0, discard_size * sizeof(struct blkbuf) );
+
+       for( bp = bufpool + NBUFS; bp < bufpool_end; ++bp ){
+               bp->bf_dev = NO_DEVICE;
+               bp->bf_busy = BF_FREE;
+       }
+}
+
+/****************************************************************************/
+/* The innermost part of the transfer routines has to live in common memory */
+/* since it must be able to bank switch to the user memory bank.            */
+/****************************************************************************/
+
+/* IDE Port I/O: Z280 edition. We know the bank is the usual default 0 here */
+
+COMMON_MEMORY
+
+void devide_read_data(void) __naked
+{
+    __asm
+            ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user
+            ld hl, (_blk_op+BLKPARAM_ADDR_OFFSET)   ; blkparam.addr
+            ld bc, #IDE_REG_DATA                    ; setup port number
+                                                    ; and count
+#ifdef SWAPDEV
+           cp #2
+            jr nz, not_swapin
+            ld a, (_blk_op+BLKPARAM_SWAP_PAGE)     ; blkparam.swap_page
+            call map_for_swap
+            jr swapin
+not_swapin:
+#endif
+            or a                                    ; test is_user
+            call nz, map_process_always             ; map user memory first if required
+swapin:
+;;            inirw                                   ; transfer 256 words
+            .db 0xED, 0x92
+            or a                                    ; test is_user
+            ret z                                   ; done if kernel memory transfer
+            jp map_kernel                           ; else map kernel then return
+    __endasm;
+}
+
+void devide_write_data(void) __naked
+{
+    __asm
+            ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user
+            ld hl, (_blk_op+BLKPARAM_ADDR_OFFSET)   ; blkparam.addr
+            ld bc, #IDE_REG_DATA                    ; setup port number
+                                                    ; and count
+#ifdef SWAPDEV
+           cp #2
+            jr nz, not_swapout
+            ld a, (_blk_op+BLKPARAM_SWAP_PAGE)     ; blkparam.swap_page
+            call map_for_swap
+            jr swapout
+not_swapout:
+#endif
+            or a                                    ; test is_user
+            call nz, map_process_always             ; else map user memory first if required
+swapout:
+;;            otirw                                   ; transfer 256 words
+            .db 0xED, 0x93
+            or a                                    ; test is_user
+            ret z                                   ; done if kernel memory transfer
+            jp map_kernel                           ; else map kernel then return
+    __endasm;
+}
diff --git a/Kernel/platform-z280rc/platform_ide.h b/Kernel/platform-z280rc/platform_ide.h
new file mode 100644 (file)
index 0000000..0f8e13b
--- /dev/null
@@ -0,0 +1,18 @@
+#define ide_select(x)
+#define ide_deselect()
+
+/*16bit, no altstatus/control */
+#define IDE_REG_DATA           0xC0
+#define IDE_REG_ERROR          0xC2
+#define IDE_REG_FEATURES       0xC2
+#define IDE_REG_SEC_COUNT      0xC5
+#define IDE_REG_LBA_0          0xC7
+#define IDE_REG_LBA_1          0xC9
+#define IDE_REG_LBA_2          0xCB
+#define IDE_REG_LBA_3          0xCD
+#define IDE_REG_DEVHEAD                0xCD
+#define IDE_REG_STATUS         0xCF
+#define IDE_REG_COMMAND                0xCF
+
+/* The data register should be accessed using inirw/otirw and similar */
+#define IDE_NONSTANDARD_XFER
diff --git a/Kernel/platform-z280rc/target.mk b/Kernel/platform-z280rc/target.mk
new file mode 100644 (file)
index 0000000..b97b60c
--- /dev/null
@@ -0,0 +1,6 @@
+#
+#      Tell the build system what processor type we are using
+#      For now pretend to be a Z80
+#
+export CPU = z80
+export USERCPU = z80
diff --git a/Kernel/platform-z280rc/tricks.s b/Kernel/platform-z280rc/tricks.s
new file mode 100644 (file)
index 0000000..1eaafca
--- /dev/null
@@ -0,0 +1,13 @@
+;
+;      For simple banked systems there is a standard implementation. The
+;      only reason to do otherwise is for speed. A custom bank logic aware
+;      bank to bank copier will give vastly better fork() performance.
+;
+;      As this is meant to be a simple reference port we use the standard
+;      approach. The morbidly curious can read the TRS80 model 1 bank to
+;      bank copier.
+;
+       .include "../kernel.def"
+       .include "kernel.def"
+
+       .include "../lib/z80fixedbank.s"
diff --git a/Kernel/platform-z280rc/z280.h b/Kernel/platform-z280rc/z280.h
new file mode 100644 (file)
index 0000000..6fb9e21
--- /dev/null
@@ -0,0 +1,2 @@
+extern uint8_t io_bank_set(uint8_t bank) __z88dk_fastcall;
+extern uint8_t vectors[];
diff --git a/Kernel/platform-z280rc/z280rc.s b/Kernel/platform-z280rc/z280rc.s
new file mode 100644 (file)
index 0000000..27baf47
--- /dev/null
@@ -0,0 +1,717 @@
+;
+;          Z280 RC support.
+;
+;          Minimal for now to get us up and running
+;
+
+            .module sbcv2
+
+            ; exported symbols
+            .globl init_early
+            .globl init_hardware
+            .globl interrupt_handler
+            .globl _program_vectors
+           .globl map_kernel
+           .globl map_process
+           .globl map_process_a
+           .globl map_process_always
+           .globl map_save
+           .globl map_restore
+           .globl map_for_swap
+           .globl platform_interrupt_all
+           .globl _kernel_flag
+
+            ; 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 trap_illegal
+            .globl outcharhex
+           .globl outstring
+           .globl outhl
+           .globl null_handler
+           .globl nmi_handler
+           .globl _inint
+           .globl kstack_top
+
+           .globl s__COMMONMEM
+           .globl l__COMMONMEM
+
+            .include "kernel.def"
+            .include "../kernel.def"
+
+;
+; Buffers (we use asm to set this up as we need them in a special segment
+; so we can recover the discard memory into the buffer pool
+;
+
+           .globl _bufpool
+           .area _BUFFERS
+
+_bufpool:
+           .ds BUFSIZE * NBUFS
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (kept even when we task switch)
+; -----------------------------------------------------------------------------
+            .area _COMMONMEM
+
+;
+;      This method is invoked early in interrupt handling before any
+;      complex handling is done. It's useful on a few platforms but
+;      generally a ret is all that is needed
+;
+platform_interrupt_all:
+           ret
+
+;
+;      If you have a ROM monitor you can get back to then do so, if not
+;      fall into reboot.
+;
+_platform_monitor:
+;
+;      Reboot the system if possible, halt if not. On a system where the
+;      ROM promptly wipes the display you may want to delay or wait for
+;      a keypress here (just remember you may be interrupts off, no kernel
+;      mapped so hit the hardware).
+;
+_platform_reboot:
+       jr _platform_reboot
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (may be below 0x8000, only accessible when the kernel is
+; mapped)
+; -----------------------------------------------------------------------------
+           .area _CODE
+
+;
+;      This routine is called very early, before the boot code shuffles
+;      things into place. We assume the bootstrap already set up
+;
+;      - bus timing and initialization
+;      - bus timing and control
+;      - cache
+;
+init_early:
+           ld c,#0x16
+           ld hl,#0x0000
+;          ldctl (c),hl                ; set interrupt status
+           .byte 0xED, 0x6E
+           ld c,#0x06
+           ld hl,#_vectors             ; Vectors on 4K boundary
+           ld l,h                      ; Work around crappy linker
+           ld h,#0
+;          ldctl (c),hl                ; into int trap/vector ptr
+           .byte 0xED, 0x6E
+           ld c,#0x08
+           ld l,#0
+;          ldctl (c),hl                ; clear I/O page
+           .byte 0xED, 0x6E
+           ld c,#0x10
+           ld l,#0x04                  ; no user I/O no EPU
+;          ldctl (c),hl                ; set trap control (can't set
+                                       ; stack trap until have user mode
+                                       ; done)
+           .byte 0xED, 0x6E
+           ret
+
+; -----------------------------------------------------------------------------
+; DISCARD is memory that will be recycled when we exec init
+; -----------------------------------------------------------------------------
+           .area _DISCARD
+;
+;      After the kernel has shuffled things into place this code is run.
+;      It's the best place to breakpoint or trace if you are not sure your
+;      kernel is loading and putting itself into place properly.
+;
+;      It's required jobs are to set up the vectors, ramsize (total RAM),
+;      and procmem (total memory free to processs), as well as setting the
+;      interrupt mode but *not* enabling interrupts. Many platforms also
+;      program up support hardware like PIO and CTC devices here.
+;
+init_hardware:
+           call map_kernel
+           ld l,#0xFF
+           call _io_bank_set
+           push hl
+           ld hl,#0xBBE0       ; MMU on S and U, no split I/D
+                               ; We may want to consider MMU off in
+                               ; supervisor later on - is there a perf
+                               ; gain versus MMU on ??
+           ld c,#0xF0
+;          outw (c),hl
+           .byte 0xED,0xBF
+           pop hl
+           call _io_bank_set       
+
+           ld hl,#2048
+            ld (_ramsize), hl
+           ld hl,#1984
+            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
+
+           ; Our CTC is fed from the system clock / 4 which gives us
+           ; 7.372800Mhz input. We want a semsible multiple of 10Hz and
+           ; for a fast CPU 50 or 100 isn't a bad value. On a single CTC
+           ; counter our best shot is 120Mhz (61440 divider exactly)
+
+           ld l,#0xFE
+           call _io_bank_set
+           push hl
+           ld a,#0xA0          ; Timer, interrupting
+           out (0xE0),a
+           ld hl,#61440        ; 120Hz
+           ld c,#0xE2
+;          outw (c),hl
+           .byte 0xED,0xBF
+           ld a,#0xC0          ; Enable, gate
+           out (0xE1),a
+           pop hl      
+           call _io_bank_set    
+
+
+;            im 3 ; set CPU interrupt mode
+           .byte 0xED, 0x4E
+
+            ret
+
+;
+;      Bank switching unsurprisingly must be in common memory space so it's
+;      always available. This is a simple hack to get us going. The Z280
+;      has a real MMU and virtual memory. We just treat it like a bunch of
+;      banks.
+;
+            .area _COMMONMEM
+
+mapreg:    .db 0       ; Our map register is write only so keep a copy
+mapsave:   .db 0       ; Saved copy of the previous map (see map_save)
+
+_kernel_flag:
+           .db 1       ; We start in kernel mode
+
+;
+;      This is invoked with a NULL argument at boot to set the kernel
+;      vectors and then elsewhere in the kernel when the kernel knows
+;      a bank may need vectors writing to it.
+;
+;      Will need rewriting when we do things properly
+;
+_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
+
+            ; set restart vector for FUZIX system calls
+           ; once we do real Z280 mode we'll need this to syscall
+            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
+           ; Fall into map_kernel
+
+;
+;      Mapping set up for the Z280. This is hack to get us going
+;
+;      We map the low 64K 1:1 in kernel mode, keep the top 8K fixed and
+;      map the others by 'bank' for user mode. We don't actually use the
+;      hardware user mode yet. It's all just a bodge to get us up and
+;      running
+;
+map_kernel:
+           push af
+           xor a
+           call map_process_a  ; do all the logic in one place with
+           pop af              ; kernel as entry 0 in the table
+           ret
+           ; map_process is called with HL either NULL or pointing to the
+           ; page mapping. Unlike the other calls it's allowed to trash AF
+map_process:
+           ld a, h
+           or l
+           jr z, map_kernel
+map_process_hl:
+           ld a, (hl)                  ; and fall through
+           ;
+           ; With a simple bank switching system you need to provide a
+           ; method to switch to the bank in A without corrupting any
+           ; other registers. The stack is safe in common memory.
+           ; For swap you need to provide what for simple banking is an
+           ; identical routine.
+map_for_swap:
+map_process_a:                 ; used by bankfork
+           push bc
+           push de
+           push hl
+           ld (mapreg), a      ; bank
+           ld c,#0x08
+;;         ldctl hl,(c)
+           .byte 0xED, 0x66
+           push hl
+           ld l,#0xff          ; MMU is I/O bank FF
+;;         ldctl (c),hl
+           .byte 0xED, 0x6E
+           ld l,a
+           ld h,#0
+           add hl,hl           ; 16 words per bank
+           add hl,hl
+           add hl,hl
+           add hl,hl
+           add hl,hl
+           ld de,#frames       ; Lazy - look it up it's only a hack for now
+           ld a,#0x10
+           out (0x44),a        ; pointer to 0x10 (system pages)
+           ld bc,#0x0FF4
+;;         otirw
+            .db 0xED, 0x93
+           pop hl
+           ld c,#0x08
+;;         ldctl (c),hl        ; previous bank register
+           .byte 0xED, 0x6E
+           pop hl
+           pop de
+           pop bc
+            ret
+
+           ;
+           ; Map the current process into memory. We do this by extracting
+           ; the bank value from u_page.
+           ;
+map_process_always:
+           push af
+           push hl
+           ld hl, #U_DATA__U_PAGE
+           call map_process_hl
+           pop hl
+           pop af
+           ret
+
+           ;
+           ; Save the existing mapping. The place you save it to needs to
+           ; be in common memory as you have no idea what bank is live
+            ;
+map_save:   push af
+           ld a, (mapreg)
+           ld (mapsave), a
+           pop af
+           ret
+           ;
+           ; Restore the saved bank. Note that you don't need to deal with
+           ; stacking of banks (we never recursively use save/restore), and
+           ; that we may well call save and decide not to call restore.
+           ;
+map_restore:
+           push af
+           ld a, (mapsave)
+           call map_process_a
+           pop af
+           ret
+           ;
+           ; Used for low level debug. Output the character in A without
+           ; corrupting other registers. May block. Interrupts and memory
+           ; state are undefined
+           ;
+outchar:
+           push af
+           push bc
+           push de
+           push hl
+           ld l,#0xFE
+           call _io_bank_set
+twait:     in a,(0x12)
+           bit 0,a
+           jr z, twait
+           pop af
+           out (0x18),a
+           call _io_bank_set
+           pop hl
+           pop de
+           pop bc
+           pop af
+            ret
+
+;
+;      These belong in core Z280 code eventually
+;
+           .globl _io_bank_set
+           .globl _flush_cpu_cache
+
+
+_io_bank_set:
+           ld c,#8
+           ex de,hl            ; save the new value in DE
+;;         ldctl hl,(c)        ; read old into HL
+           .byte 0xED, 0x66
+           ex de,hl            ; switch bank
+;;         ldctl (c),hl        ; new into HL
+           .byte 0xED, 0x6E
+           ex de,hl            ; and back again to return old
+           ret
+
+_flush_cpu_cache:
+           .byte 0xED, 0x65
+           ret
+           
+
+;
+;      Mapping tables for speed
+;
+frames:
+       .word 0x0000            ; Identity map kernel
+       .word 0x0010
+       .word 0x0020
+       .word 0x0030
+       .word 0x0040
+       .word 0x0050
+       .word 0x0060
+       .word 0x0070
+       .word 0x0080
+       .word 0x0090
+       .word 0x00A0
+       .word 0x00B0
+       .word 0x00C0
+       .word 0x00D0
+       .word 0x00E0            ; Common
+       .word 0x00F0            ;
+
+       .word 0x0100
+       .word 0x0110
+       .word 0x0120
+       .word 0x0130
+       .word 0x0140
+       .word 0x0150
+       .word 0x0160
+       .word 0x0170
+       .word 0x0180
+       .word 0x0190
+       .word 0x01A0
+       .word 0x01B0
+       .word 0x01C0
+       .word 0x01D0
+       .word 0x00E0            ; Common
+       .word 0x00F0            ;
+
+       .word 0x0200
+       .word 0x0210
+       .word 0x0220
+       .word 0x0230
+       .word 0x0240
+       .word 0x0250
+       .word 0x0260
+       .word 0x0270
+       .word 0x0280
+       .word 0x0290
+       .word 0x02A0
+       .word 0x02B0
+       .word 0x02C0
+       .word 0x02D0
+       .word 0x00E0            ; Common
+       .word 0x00F0            ;
+
+       .word 0x0300
+       .word 0x0310
+       .word 0x0320
+       .word 0x0330
+       .word 0x0340
+       .word 0x0350
+       .word 0x0360
+       .word 0x0370
+       .word 0x0380
+       .word 0x0390
+       .word 0x03A0
+       .word 0x03B0
+       .word 0x03C0
+       .word 0x03D0
+       .word 0x00E0            ; Common
+       .word 0x00F0            ;
+
+       .word 0x0400
+       .word 0x0410
+       .word 0x0420
+       .word 0x0430
+       .word 0x0440
+       .word 0x0450
+       .word 0x0460
+       .word 0x0470
+       .word 0x0480
+       .word 0x0490
+       .word 0x04A0
+       .word 0x04B0
+       .word 0x04C0
+       .word 0x04D0
+       .word 0x00E0            ; Common
+       .word 0x00F0            ;
+
+       .word 0x0500
+       .word 0x0510
+       .word 0x0520
+       .word 0x0530
+       .word 0x0540
+       .word 0x0550
+       .word 0x0560
+       .word 0x0570
+       .word 0x0580
+       .word 0x0590
+       .word 0x05A0
+       .word 0x05B0
+       .word 0x05C0
+       .word 0x05D0
+       .word 0x00E0            ; Common
+       .word 0x00F0            ;
+
+       .word 0x0600
+       .word 0x0610
+       .word 0x0620
+       .word 0x0630
+       .word 0x0640
+       .word 0x0650
+       .word 0x0660
+       .word 0x0670
+       .word 0x0680
+       .word 0x0690
+       .word 0x06A0
+       .word 0x06B0
+       .word 0x06C0
+       .word 0x06D0
+       .word 0x00E0            ; Common
+       .word 0x00F0            ;
+
+       .word 0x0700
+       .word 0x0710
+       .word 0x0720
+       .word 0x0730
+       .word 0x0740
+       .word 0x0750
+       .word 0x0760
+       .word 0x0770
+       .word 0x0780
+       .word 0x0790
+       .word 0x07A0
+       .word 0x07B0
+       .word 0x07C0
+       .word 0x07D0
+       .word 0x00E0            ; Common
+       .word 0x00F0            ;
+
+       .word 0x0700
+       .word 0x0710
+       .word 0x0720
+       .word 0x0730
+       .word 0x0740
+       .word 0x0750
+       .word 0x0760
+       .word 0x0770
+       .word 0x0780
+       .word 0x0790
+       .word 0x07A0
+       .word 0x07B0
+       .word 0x07C0
+       .word 0x07D0
+       .word 0x00E0            ; Common
+       .word 0x00F0            ;
+
+
+;
+;      Interrupt vectors. Kernel and identity mapped
+;
+       .area _VECTORS
+
+SUPER  .equ    0x0000          ; Supervisor, all ints masked
+
+       .globl _vectors
+
+_vectors:
+       .word   #SUPER          ; 00    Reserved
+       .word   invalid
+       .word   #SUPER          ; 04    NMI
+       .word   nmi_handler
+       .word   #SUPER          ; 08    External Line A
+       .word   external
+       .word   #SUPER          ; 0C    External Line B
+       .word   external
+       .word   #SUPER          ; 10    External Line C
+       .word   external
+       .word   #SUPER          ; 14    CTC 0
+       .word   ctc0
+       .word   #SUPER          ; 18    CTC 1
+       .word   ctc1
+       .word   #SUPER          ; 1C    Reserved
+       .word   invalid         ; The broken undocumented CTC
+       .word   #SUPER          ; 20    CTC 2
+       .word   invalid
+       .word   #SUPER          ; 24    DMA 0
+       .word   invalid
+       .word   #SUPER          ; 28    DMA 1
+       .word   invalid
+       .word   #SUPER          ; 2C    DMA 2
+       .word   invalid
+       .word   #SUPER          ; 30    DMA 3
+       .word   invalid
+       .word   #SUPER          ; 34    UART rx
+       .word   ttyint
+       .word   #SUPER          ; 38    UART tx
+       .word   invalid
+       .word   #SUPER          ; 3C    Single-step
+       .word   sstep
+       .word   #SUPER          ; 40    Breakpoint
+       .word   sigtrap
+       .word   #SUPER          ; 44    Divison by zero
+       .word   sigfpe
+       .word   #SUPER          ; 48    Stack overflow
+       .word   stackover
+       .word   #SUPER          ; 4C    Access violation
+       .word   sigsegv
+       .word   #SUPER          ; 50    System call
+       .word   invalid
+       .word   #SUPER          ; 54    Privileged instruction
+       .word   sigill
+       .word   #SUPER          ; 58    EPU
+       .word   sigill
+       .word   #SUPER          ; 5C    EPU
+       .word   sigill
+       .word   #SUPER          ; 60    EPU
+       .word   sigill
+       .word   #SUPER          ; 64    EPU
+       .word   invalid
+       .word   #SUPER          ; 68    Reserved
+       .word   invalid
+       .word   #SUPER          ; 6C    Reserved
+       .word   invalid
+
+       ; What can follow then is 384 word size vectors for INT A B C
+       ; for IM2 style external I/O which we don't use
+
+       .area _COMMONMEM
+
+SIGILL .equ    4
+SIGTRAP        .equ    5
+SIGFPE .equ    8
+SIGSEGV        .equ    11
+
+       .globl _irq_source
+
+_irq_source:
+       .byte 0
+
+invalid:
+       ld hl,#invalidint
+       call outstring
+       jr trapexit
+
+external:
+       ; Interrupt off the RC2014 bus
+       push af
+       xor a
+irqcall:
+       ld (_irq_source),a
+       ; This bit belongs in  a future Z280 lowlevel not here
+       push de
+       push hl
+       xor a
+       call _io_bank_set
+       push hl
+       call interrupt_handler
+       pop hl
+       call _io_bank_set
+       pop hl
+       pop de
+       pop af
+       jr trapexit
+ctc0:
+       push af
+       ld a,#1
+       jr irqcall
+ctc1:
+       push af
+       ld a,#2
+       jr irqcall
+
+ttyint:
+       push af
+       ld a,#3
+       jr irqcall
+
+sstep:
+sigtrap:
+       push af
+       ld a,#SIGTRAP
+       jr sig_or_die
+sigfpe:
+       push af
+       ld a,#SIGFPE
+       jr sig_or_die
+sigsegv:
+       push af
+       ld a,#SIGSEGV
+       jr sig_or_die
+sigill:
+       push af
+       ld a,#SIGILL
+sig_or_die:
+       ; Once we have proper supervisor/user we can just check the pushed
+       ; status to see what to do. For now fudge it roughly.
+       push af
+       ld a,(_inint)
+       or a
+       jr nz, diediedie
+       ld a,(U_DATA__U_INSYS)
+       or a
+       jr nz, diediedie
+       ; TODO - we need to go via the syscall signal path as we need this
+       ;  to be synchronous. Will need to be in the lowlevel-z280 code when
+       ;   we get there
+       jr trapexit
+
+stackover:
+       ld sp,#kstack_top
+       ld hl,#stackfault
+       call outstring
+       jp _platform_monitor
+
+trapexit:
+       ; Discard the data
+       inc sp
+       inc sp
+       ; retil
+       .byte 0xed, 0x55
+
+invalidint:
+       .asciz 'INVIRQ'
+stackfault:
+       .asciz 'STKFLT'
+
+diediedie:
+       call outcharhex
+       pop af          ; discard
+       pop hl          ; cause
+       call outhlcolon
+       pop hl          ; status
+       call outhlcolon
+       pop hl          ; pc
+       call outhlcolon
+       jp _platform_monitor
+
+outhlcolon:
+       call outcharhex
+       ld a,#':'
+       jp outhl