tbblue: first cut at tbblue support
authorAlan Cox <alan@linux.intel.com>
Sat, 8 Dec 2018 17:29:44 +0000 (17:29 +0000)
committerAlan Cox <alan@linux.intel.com>
Sat, 8 Dec 2018 17:29:44 +0000 (17:29 +0000)
Really more of a way to try and debug the bank8k code than anything else.
Right now this is very very basic - an outline to get booting and work from.

18 files changed:
Kernel/platform-tbblue/Makefile [new file with mode: 0644]
Kernel/platform-tbblue/README [new file with mode: 0644]
Kernel/platform-tbblue/commonmem.s [new file with mode: 0644]
Kernel/platform-tbblue/config.h [new file with mode: 0644]
Kernel/platform-tbblue/crt0.s [new file with mode: 0644]
Kernel/platform-tbblue/devices.c [new file with mode: 0644]
Kernel/platform-tbblue/devtty.c [new file with mode: 0644]
Kernel/platform-tbblue/devtty.h [new file with mode: 0644]
Kernel/platform-tbblue/discard.c [new file with mode: 0644]
Kernel/platform-tbblue/fuzix.lnk [new file with mode: 0644]
Kernel/platform-tbblue/kernel.def [new file with mode: 0644]
Kernel/platform-tbblue/load-esx.s [new file with mode: 0644]
Kernel/platform-tbblue/main.c [new file with mode: 0644]
Kernel/platform-tbblue/rules.mk [new file with mode: 0644]
Kernel/platform-tbblue/target.mk [new file with mode: 0644]
Kernel/platform-tbblue/tbblue.s [new file with mode: 0644]
Kernel/platform-tbblue/tmxvideo.s [new file with mode: 0644]
Kernel/platform-tbblue/tricks.s [new file with mode: 0644]

diff --git a/Kernel/platform-tbblue/Makefile b/Kernel/platform-tbblue/Makefile
new file mode 100644 (file)
index 0000000..50d0a83
--- /dev/null
@@ -0,0 +1,63 @@
+ASRCS = crt0.s tbblue.s tmxvideo.s
+ASRCS += tricks.s commonmem.s
+CSRCS = devtty.c devices.c main.c
+CDSRCS = discard.c
+DSRCS = ../dev/devsd.c ../dev/blkdev.c
+DDSRCS = ../dev/devsd_discard.c ../dev/mbr.c
+DZSRCS = ../dev/zx/divmmc.c ../dev/zx/zxkeyboard.c ../dev/zx/devinput.c
+DDZSRCS =
+NSRCS = ../dev/net/net_native.c
+
+COBJS = $(CSRCS:.c=.rel)
+CDOBJS = $(CDSRCS:.c=.rel)
+AOBJS = $(ASRCS:.s=.rel)
+DOBJS = $(patsubst ../dev/%.c,%.rel, $(DSRCS))
+DDOBJS = $(patsubst ../dev/%.c,%.rel, $(DDSRCS))
+DZOBJS = $(patsubst ../dev/zx/%.c,%.rel, $(DZSRCS))
+DDZOBJS = $(patsubst ../dev/zx/%.c,%.rel, $(DDZSRCS))
+NOBJS = $(patsubst ../dev/net/%.c,%.rel, $(NSRCS))
+
+OBJS  = $(COBJS) $(CDOBJS) $(AOBJS) $(DOBJS) $(DDOBJS) $(DZOBJS) $(DDZOBJS) $(NOBJS)
+
+CROSS_CCOPTS += -I../dev/ -I../dev/zx/ -I../dev/net/
+
+all:   $(OBJS)
+
+$(COBJS): %.rel: %.c
+       $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(CDOBJS): %.rel: %.c
+       $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $<
+
+$(DOBJS): %.rel: ../dev/%.c
+       $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(DDOBJS): %.rel: ../dev/%.c
+       $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $<
+
+$(DZOBJS): %.rel: ../dev/zx/%.c
+       $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(DDZOBJS): %.rel: ../dev/zx/%.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) $<
+
+clean:
+       rm -f $(OBJS) *.lst *.asm *.sym *.rst *.rel core *~ 
+       rm -f FUZIX FUZIX.BIN load-esx.ihx load-esx.tmp
+
+image:
+       # Build an esxdos friendly setup
+       sdasz80 -o load-esx.s
+       sdldz80 -i load-esx.rel
+       makebin -s 8704 load-esx.ihx load-esx.tmp
+       # Generate the image file we need
+       dd if=load-esx.tmp of=FUZIX bs=8192 skip=1
+
+       dd if=/dev/zero bs=256 count=1 >FUZIX.BIN
+       cat ../fuzix.bin >>FUZIX.BIN
diff --git a/Kernel/platform-tbblue/README b/Kernel/platform-tbblue/README
new file mode 100644 (file)
index 0000000..9c922af
--- /dev/null
@@ -0,0 +1,28 @@
+First wild guesses at TBBlue support
+
+8K paging also needs debug
+
+Currently goal is
+
+load kernel into pages 16-23
+switch 16-22 into memory
+jump to kernel 0x0100
+switch 23 into memory
+set up and go
+
+TODO:
+DONE   program_vectors
+DONE   review where font and video code needs to land
+       Use nextreg
+       memory sizing
+DONEish        set up page map properly
+
+       See if we can make it boot
+
+
+Bigger issues
+- We actually want a banked kernel so that we can put networking in its
+  own bank and maybe external buffers. We have tons of RAM and fast DMA.
+- Need to look at optimal inter bank copies for user copying and buffers.
+
+
diff --git a/Kernel/platform-tbblue/commonmem.s b/Kernel/platform-tbblue/commonmem.s
new file mode 100644 (file)
index 0000000..dc31c24
--- /dev/null
@@ -0,0 +1,8 @@
+;
+; Multiple app sizes and the fact the kernel and apps share the same banks
+; means we need to put this somewhere low
+;
+        .module commonmem
+        .area _COMMONDATA
+
+       .include "../cpu-z80/std-commonmem.s"
diff --git a/Kernel/platform-tbblue/config.h b/Kernel/platform-tbblue/config.h
new file mode 100644 (file)
index 0000000..797fcda
--- /dev/null
@@ -0,0 +1,119 @@
+/* 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
+
+/* Select a banked memory set up */
+#define CONFIG_BANK8   /* 8K pages */
+#define MAX_MAPS       96      /* Assume 512K for now  FIXME */
+/* How big is each bank - in our case 8K */
+#define MAP_SIZE       0x2000
+#define TOP_SIZE       0x200   /* Udata to swap */
+/* How many banks do we have in our address space */
+#define CONFIG_BANKS   8       /* 8 x 8K will fix when do paging */
+#define PAGE_INVALID 0xFF      /* A never used value */
+
+/* Input layer support */
+#define CONFIG_INPUT
+#define CONFIG_INPUT_GRABMAX   3
+/* Video terminal, not a serial tty */
+#define CONFIG_VT
+/* Keyboard contains non-ascii symbols */
+#define CONFIG_UNIKEY
+#define CONFIG_FONT8X8
+#define CONFIG_FONT8X8SMALL
+/* Vt definitions */
+#define VT_WIDTH       64
+#define VT_HEIGHT      24
+#define VT_RIGHT       63
+#define VT_BOTTOM      23
+
+
+
+/*
+ *     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     0xE000  /* Top of program, base of U_DATA stash */
+#define PROC_SIZE   56             /* Memory needed per process including stash */
+
+/*
+ *     Definitions for swapping - we don't need swap!
+ */
+
+/* What is the maximum number of /dev/hd devices we have. In theory right now
+   it's actually 1 - the SD interface */
+#define MAX_BLKDEV     2
+/* Select IDE disk support, and PPIDE (parallel port IDE) as the interface */
+#define CONFIG_SD
+#define SD_DRIVE_COUNT 1
+
+#define BOOTDEVICENAMES "hd#"
+
+/* We will resize the buffers available after boot. This is the normal setting */
+#define CONFIG_DYNAMIC_BUFPOOL
+/* 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
+
+/*
+ * 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 */
+
+/* Core networking support */
+#define CONFIG_NET
+/* Or native (eg SLIP) */
+#define CONFIG_NET_NATIVE
+
+/*
+ *     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 2    /* 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-tbblue/crt0.s b/Kernel/platform-tbblue/crt0.s
new file mode 100644 (file)
index 0000000..a58fa8f
--- /dev/null
@@ -0,0 +1,114 @@
+        .module crt0
+
+        .area _CODE
+       .area _CODE2
+       .area _CODE3
+        .area _VIDEO
+        .area _INITIALIZED
+        .area _HOME
+       .area _CONST
+
+       ;
+       ;       Beyond this point we just zero.
+       ;
+
+        .area _DATA
+        .area _BSEG
+        .area _BSS
+        .area _HEAP
+        .area _GSINIT
+        .area _GSFINAL
+       ;
+       ;       Finally the buffers so they can expand
+       ;
+       .area _BUFFERS
+        .area _INITIALIZER
+
+        .area _DISCARD
+       ; Somewhere to throw it out of the way
+
+
+       ; Our common is mapped high
+       .area _COMMONDATA
+        .area _COMMONMEM
+       .area _FONT
+
+
+
+        ; imported symbols
+        .globl _fuzix_main
+        .globl init_early
+        .globl init_hardware
+       .globl l__BUFFERS
+       .globl s__BUFFERS
+       .globl l__DATA
+       .globl s__DATA
+        .globl kstack_top
+
+        .globl unix_syscall_entry
+        .globl nmi_handler
+        .globl interrupt_handler
+
+       .include "../kernel.def"
+       .include "kernel.def"
+
+       ;
+        ; startup code. Runs from 0x100. The kernel is mapped into pages
+       ; 16-23 and 16-22 are currently mapped. Stack is not valid on entry
+       ; and interrupts are off.
+       ;
+
+        .area _CODE
+
+       .globl _start
+
+_start:
+       ; Map in the top page (it couldn't be mapped by the loader as the
+       ; loader is living in it)
+       ld a,#0x57              ; MMU E000-FFFF
+       ld bc,#0x243B
+       out (c),a
+       ld a,#23                ; Top kernel page
+       inc b
+       out (c),a
+
+       ;  We need to wipe the BSS but the rest of the job is done.
+
+       ld hl, #s__DATA
+       ld de, #s__DATA+1
+       ld bc, #l__DATA-1
+       ld (hl), #0
+       ldir
+       ld hl, #s__BUFFERS
+       ld de, #s__BUFFERS+1
+       ld bc, #l__BUFFERS-1
+       ld (hl), #0
+       ldir
+
+        ld sp, #kstack_top
+
+        ; Configure memory map
+        call init_early
+
+        ; Hardware setup
+        call init_hardware
+
+        ; Call the C main routine
+        call _fuzix_main
+    
+        ; main shouldn't return, but if it does...
+        di
+stop:   halt
+        jr stop
+
+       .area _BUFFERS
+;
+; 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
diff --git a/Kernel/platform-tbblue/devices.c b/Kernel/platform-tbblue/devices.c
new file mode 100644 (file)
index 0000000..dac8912
--- /dev/null
@@ -0,0 +1,41 @@
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <tty.h>
+#include <devsys.h>
+#include <vt.h>
+#include <devsd.h>
+#include <blkdev.h>
+#include <devtty.h>
+
+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: nope */
+  {  no_open,      no_close,     no_rdwr,  no_rdwr,   no_ioctl },
+  /* 2: /dev/tty       TTY devices */
+  {  tty_open,    tty_close,    tty_read,      tty_write,     gfx_ioctl },
+  /* 3: /dev/lpr       Printer devices */
+  {  no_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  },
+  /* 5: 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;
+}
+
+void device_init(void)
+{
+#ifdef CONFIG_SD
+  devsd_init();
+#endif
+}
diff --git a/Kernel/platform-tbblue/devtty.c b/Kernel/platform-tbblue/devtty.c
new file mode 100644 (file)
index 0000000..5e7dc53
--- /dev/null
@@ -0,0 +1,150 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <devtty.h>
+#include <keycode.h>
+#include <vt.h>
+#include <tty.h>
+#include <graphics.h>
+#include <input.h>
+#include <devinput.h>
+
+char tbuf1[TTYSIZ];
+
+uint8_t vtattr_cap = VTA_INVERSE|VTA_FLASH|VTA_UNDERLINE;
+extern uint8_t curattr;
+
+static tcflag_t console_mask[4] = {
+       _ISYS,
+       _OSYS,
+       _CSYS,
+       _LSYS
+};
+
+tcflag_t *termios_mask[NUM_DEV_TTY + 1] = {
+       NULL,
+       console_mask
+};
+
+
+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},
+};
+
+/* tty1 is the screen */
+
+/* Output for the system console (kprintf etc) */
+void kputchar(char c)
+{
+       if (c == '\n')
+               tty_putc(0, '\r');
+       tty_putc(0, c);
+}
+
+/* Both console and debug port are always ready */
+ttyready_t tty_writeready(uint8_t minor)
+{
+       minor;
+       return TTY_READY_NOW;
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+       minor;
+       vtoutput(&c, 1);
+}
+
+int tty_carrier(uint8_t minor)
+{
+       minor;
+       return 1;
+}
+
+void tty_setup(uint8_t minor, uint8_t flags)
+{
+       minor;
+}
+
+void tty_sleeping(uint8_t minor)
+{
+       minor;
+}
+
+void tty_data_consumed(uint8_t minor)
+{
+}
+
+
+/* This is used by the vt asm code, but needs to live in the kernel */
+uint16_t cursorpos;
+
+/* For now we only support 64 char mode - we should add the mode setting
+   logic an dother modes FIXME */
+static struct display specdisplay = {
+       0,
+       512, 192,
+       512, 192,
+       0xFF, 0xFF,
+       FMT_TIMEX64,
+       HW_UNACCEL,
+       GFX_VBLANK|GFX_MAPPABLE|GFX_TEXT,
+       0
+};
+
+static struct videomap specmap = {
+       0,
+       0,
+       0x4000,
+       14336,
+       0,
+       0,
+       0,
+       MAP_FBMEM|MAP_FBMEM_SIMPLE
+};
+
+/*
+ *     Graphics ioctls. Very minimal for this platform. It's a single fixed
+ *     mode with direct memory mapping.
+ */
+int gfx_ioctl(uint8_t minor, uarg_t arg, char *ptr)
+{
+       if (minor != 1 || arg >> 8 != 0x03)
+               return vt_ioctl(minor, arg, ptr);
+       switch(arg) {
+       case GFXIOC_GETINFO:
+               return uput(&specdisplay, ptr, sizeof(struct display));
+       case GFXIOC_MAP:
+               return uput(&specmap, ptr, sizeof(struct videomap));
+       case GFXIOC_UNMAP:
+               return 0;
+       case GFXIOC_WAITVB:
+               /* Our system clock is vblank */
+               timer_wait++;
+               psleep(&timer_interrupt);
+               timer_wait--;
+               chksigs();
+               if (udata.u_cursig) {
+                       udata.u_error = EINTR;
+                       return -1;
+               }
+               return 0;
+       }
+       return -1;
+}
+
+void vtattr_notify(void)
+{
+       /* Attribute byte fixups: not hard as the colours map directly
+          to the spectrum ones */
+       if (vtattr & VTA_INVERSE)
+               curattr =  ((vtink & 7) << 3) | (vtpaper & 7);
+       else
+               curattr = (vtink & 7) | ((vtpaper & 7) << 3);
+       if (vtattr & VTA_FLASH)
+               curattr |= 0x80;
+       /* How to map the bright bit - we go by either */
+       if ((vtink | vtpaper) & 0x10)
+               curattr |= 0x40;
+}
diff --git a/Kernel/platform-tbblue/devtty.h b/Kernel/platform-tbblue/devtty.h
new file mode 100644 (file)
index 0000000..40a67f5
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+
+void tty_pollirq(void);
+static void keydecode(void);
+
+#define KEY_ROWS       8
+#define KEY_COLS       5
+extern uint8_t keymap[8];
+extern uint8_t keyboard[8][5];
+extern uint8_t shiftkeyboard[8][5];
+
+extern uint8_t timer_wait;
+
+extern int gfx_ioctl(uint8_t minor, uarg_t arg, char *ptr);
+
+extern uint8_t vtborder;
+
+#endif
diff --git a/Kernel/platform-tbblue/discard.c b/Kernel/platform-tbblue/discard.c
new file mode 100644 (file)
index 0000000..b5a0486
--- /dev/null
@@ -0,0 +1,52 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include <blkdev.h>
+
+extern uint8_t kempston, kmouse;
+
+uint8_t platform_param(char *p)
+{
+       return 0;
+}
+
+/* Nothing to do for the map of init */
+void map_init(void)
+{
+}
+
+void pagemap_init(void)
+{
+       uint8_t i;
+       /* 8K pages that are above the kernel */
+       /* FIXME: will depend upon size (<160 or < 224 for expanded)  */
+       for (i = 24; i < 96; i++)
+               pagemap_add(i);
+       /* We can arguably also steal the memory wasted on the DivMMC
+          that we don't use FIXME */
+
+       /* Low pages we can scavenge */
+       /*
+           Reserved
+               10-11: system screen (console 1)
+               14-15: shadow screen (console 2)
+       
+           We should probably reserve Layer 2 buffers as well at some
+           point in time
+       */              
+       for (i = 0; i < 10; i++)
+               pagemap_add(i);
+       pagemap_add(12);
+       pagemap_add(13);
+       /* Common for init */
+       pagemap_add(23);
+}
+
+void platform_copyright(void)
+{
+       kempston = 1;
+       kmouse = 1;
+}
+
diff --git a/Kernel/platform-tbblue/fuzix.lnk b/Kernel/platform-tbblue/fuzix.lnk
new file mode 100644 (file)
index 0000000..f67939b
--- /dev/null
@@ -0,0 +1,50 @@
+-mwxuy
+-i fuzix.ihx
+-l z80
+-b _CODE=0x0100
+-b _COMMONDATA=0xF200
+-b _DISCARD=0xE200
+platform-tbblue/crt0.rel
+platform-tbblue/commonmem.rel
+platform-tbblue/tbblue.rel
+platform-tbblue/tmxvideo.rel
+platform-tbblue/main.rel
+platform-tbblue/discard.rel
+start.rel
+version.rel
+lowlevel-z80.rel
+usermem_std-z80.rel
+platform-tbblue/tricks.rel
+timer.rel
+kdata.rel
+usermem.rel
+platform-tbblue/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
+syscall_net.rel
+tty.rel
+vt.rel
+font8x8.rel
+mm.rel
+bank8k.rel
+swap.rel
+devsys.rel
+devinput.rel
+platform-tbblue/devtty.rel
+platform-tbblue/devsd.rel
+platform-tbblue/devsd_discard.rel
+platform-tbblue/divmmc.rel
+platform-tbblue/mbr.rel
+platform-tbblue/blkdev.rel
+platform-tbblue/devinput.rel
+platform-tbblue/zxkeyboard.rel
+platform-tbblue/net_native.rel
+-e
diff --git a/Kernel/platform-tbblue/kernel.def b/Kernel/platform-tbblue/kernel.def
new file mode 100644 (file)
index 0000000..849a768
--- /dev/null
@@ -0,0 +1,11 @@
+U_DATA                      .equ 0xF000       ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE           .equ 0x200        ; 256+256 bytes.
+
+Z80_TYPE                   .equ 1
+
+PROGBASE                   .equ 0x0000
+PROGLOAD                   .equ 0x0100
+
+NBUFS                      .equ 5
+
+Z80_MMU_HOOKS              .equ 0
diff --git a/Kernel/platform-tbblue/load-esx.s b/Kernel/platform-tbblue/load-esx.s
new file mode 100644 (file)
index 0000000..48d1138
--- /dev/null
@@ -0,0 +1,143 @@
+;
+;      TBBlue bootstrap via ESXDOS API
+;
+;      We are loaded at 0x2000. We load the kernel into banks 16-23 and
+;      then map most of it in and jump to 0x0100 in the loaded image
+;
+;      As we use higher pages we know that we can return to BASIC and
+;      stuff like the RAMdisc will remain intact.
+;
+
+       .area _BOOT(ABS)
+       .org 0x2000
+
+       ; We get run with a valid SP but it's lurking in high space so
+       ; move sp for banking.
+start:
+       ld (escape),sp
+       ld sp,#0x8000
+       xor a
+       rst 8
+       .db 0x89
+       ld (drive),a
+
+       ld hl, #filename
+       ld b, #0x01
+       ld a,(drive)
+
+       rst 8
+       .db 0x9A
+       
+       jr c, failure
+
+       ld (handle),a
+
+       ;
+       ;       We load the image into high pages so that if we barf
+       ;       everything is good even the ramdisc.
+       ;
+       ld a,#16                ; Stuff the first 16K in bank 16/17
+       ld hl,#0xC000
+       call load16k
+
+       ld a,#18                ; Second bank into 18/19
+       call load16k
+
+       ld a,#20                ; Third bank
+       call load16k
+
+       ld a,#22
+       call load16k            ; Fourth bank
+
+       ld a,(handle)
+       rst 8
+       .db 0x9B                ; Close
+
+       ld hl,#strap
+       ld de,#0xFF00
+       ld bc,#0x0100
+       ldir
+
+       ; Now the delicate bit of the operation - taking over the DivMMC/IDE
+       ; ROM image
+
+       ld hl,#0xFF00
+       rst 0x20                ; Terminate esxdos command and return to
+                               ; our bootstrap code we just moved
+
+load16k:
+       ld bc,#0x243B
+       ld e,#0x56              ; 0xC000 mapping register
+       out (c),e
+       inc b
+       out (c),a               ; Page requested
+       inc b
+       out (c),b               ; MMU register 7
+       dec b                   ; back to control port
+       inc e                   ; next register
+       inc a                   ; next page
+       out (c),e
+       inc b
+       out (c),a
+
+       ld a,(handle)           ; Load 16K into that page
+       ld bc,#0x4000
+       ld hl,#0xC000           ; We mapped it at 48K up
+       rst 8
+       .byte 0x9D
+       ret nc
+failure:
+       ld hl,#ohpoo
+fail:
+       ld a,(hl)
+       inc hl
+       or a
+       jr z, faildone
+       rst 0x10
+       jr fail
+       ; throw ourselves under the bus
+faildone:
+       ld a,(handle)
+       or a
+       jr z, noclose
+       rst 8
+       .byte 0x9B
+noclose:
+       ld sp,(escape)
+       ret
+
+ohpoo:
+       .ascii 'Unable to load Fuzix image'
+       .db 13,0
+filename:
+       .asciz 'FUZIX.BIN'
+handle:
+       .db 0
+drive:
+       .db 0
+escape:
+       .dw 0
+
+;
+;      Code run from 0xFF00
+;
+strap:
+       ; We are moving interrupt vectors and stuff
+       di
+       ; Page in 16/17/18/19/20/21/22
+       ; not 23 as that will go over us. Instead the kernel entry will
+       ; fix that one up
+       ld a,#16
+       ld bc,#0x243B
+       ld de,#0x5007
+mmuset:
+       out (c),d               ; register
+       inc b
+       out (c),a               ; data
+       dec b
+       inc a                   ; next page
+       inc d                   ; next register
+       dec e                   ; are we there yet ?
+       jr nz, mmuset
+
+       jp 0x0100       
diff --git a/Kernel/platform-tbblue/main.c b/Kernel/platform-tbblue/main.c
new file mode 100644 (file)
index 0000000..cd8de9e
--- /dev/null
@@ -0,0 +1,66 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include <devinput.h>
+
+uint16_t ramtop = PROGTOP;
+
+/* On idle we spin checking for the terminals. Gives us more responsiveness
+   for the polled ports */
+void platform_idle(void)
+{
+  /* We don't want an idle poll and IRQ driven tty poll at the same moment */
+  __asm
+   halt
+  __endasm;
+}
+
+uint8_t timer_wait;
+
+void platform_interrupt(void)
+{
+ tty_pollirq();
+ timer_interrupt();
+ poll_input();
+ if (timer_wait)
+   wakeup(&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 the start of
+ *     user space into buffers.
+ *
+ *     Discard gets turned into buffers or user space
+ */
+void platform_discard(void)
+{
+       uint16_t discard_size = 0xE000 - (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;
+       }
+}
+
+/* Adding dummy swapper since it is referenced by tricks.s */
+void swapper(ptptr p)
+{
+  p;
+}
diff --git a/Kernel/platform-tbblue/rules.mk b/Kernel/platform-tbblue/rules.mk
new file mode 100644 (file)
index 0000000..b24847a
--- /dev/null
@@ -0,0 +1 @@
+export CROSS_CC_SYS5=--codeseg CODE3
diff --git a/Kernel/platform-tbblue/target.mk b/Kernel/platform-tbblue/target.mk
new file mode 100644 (file)
index 0000000..3bffcde
--- /dev/null
@@ -0,0 +1 @@
+export CPU = z80
diff --git a/Kernel/platform-tbblue/tbblue.s b/Kernel/platform-tbblue/tbblue.s
new file mode 100644 (file)
index 0000000..6ba3087
--- /dev/null
@@ -0,0 +1,285 @@
+;
+;    TBBlue hardware support
+;
+
+        .module tbblue
+
+        ; exported symbols
+        .globl init_early
+        .globl init_hardware
+        .globl _program_vectors
+        .globl platform_interrupt_all
+       .globl interrupt_handler
+       .globl unix_syscall_entry
+       .globl null_handler
+       .globl nmi_handler
+
+        .globl map_kernel
+        .globl map_process_always
+        .globl map_process
+        .globl map_kernel_di
+        .globl map_process_always_di
+        .globl map_save_kernel
+        .globl map_restore
+       .globl map_kernel_restore
+       .globl map_video
+       .globl current_map
+
+       .globl _int_disabled
+       .globl _vtborder
+
+        ; exported debugging tools
+        .globl _platform_monitor
+       .globl _platform_reboot
+        .globl outchar
+
+        ; imported symbols
+        .globl _ramsize
+        .globl _procmem
+
+       .globl _vtoutput
+       .globl _vtinit
+
+        .globl outcharhex
+        .globl outhl, outde, outbc
+        .globl outnewline
+        .globl outstring
+        .globl outstringhex
+
+        .include "kernel.def"
+        .include "../kernel.def"
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (above 0xE000)
+; -----------------------------------------------------------------------------
+        .area _COMMONMEM
+
+_platform_monitor:
+       ;
+       ;       Not so much a monitor as wait for space
+       ;
+       ld a, #0x7F
+       in a, (0xFE)
+       rra
+       jr c, _platform_monitor
+
+_platform_reboot:
+       di
+       ; FIXME: put back 48K ROM
+        rst 0          ; back into our booter
+
+platform_interrupt_all:
+        ret
+
+       .area _COMMONDATA
+
+_int_disabled:
+       .db 1
+
+_vtborder:             ; needs to be common
+       .db 0
+
+
+; -----------------------------------------------------------------------------
+; KERNEL CODE BANK (below 0xE000, only accessible when the kernel is mapped)
+; -----------------------------------------------------------------------------
+        .area _CODE
+
+init_early:
+        ret
+
+       .area _DISCARD
+
+init_hardware:
+        ; set system RAM size
+        ld hl, #512            ; FIXME: probe not hardcode
+        ld (_ramsize), hl
+        ld hl, #448            ; FIXME: TBD
+        ld (_procmem), hl
+
+       ; Reprogram the TBBlue registers
+
+       ld a,#0x06
+       ld de,#0x9B80   ; Turbo on, DivMMC paging off, Multiface off, 
+                       ; Sound mode AY
+       call tbblue_config
+
+       ld a,#0x07
+       ld de,#0x0302   ; 14Mhz
+       call tbblue_config
+
+       ld a,#0x08
+       ld de,#0xEECE   ; 128K paging(?), no RAM contention, ABC stereo
+                       ; Covox, Timex, Turbosound on
+       call tbblue_config
+
+       ld a,#0x09
+       ld de,#0xFC00   ; Kempston on, divMMC on
+       call tbblue_config
+
+       ld a,#0x15
+       ld de,#0xFF00   ; Lores off, sprites off, border off, normal order
+       call tbblue_config
+
+       ; Activate Timex screen
+       ld a,#0x3E
+       out (0xFF),a
+
+        ; screen initialization
+       call _vtinit
+
+        ret
+
+tbblue_config:
+       ld bc,#0x243B
+       out (c),a
+       inc b
+       ld a,d          ; mask
+       cpl             ; bits to keep
+       in d,(c)        ; read register
+       and d           ; mask out bits to change
+       or e            ; mask in new bits
+       out (c),a       ; write register
+       ret
+
+
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+        .area _COMMONMEM
+
+_program_vectors:
+       pop de
+       pop hl
+       push hl
+       push de
+       call map_process
+
+        ; write zeroes across all vectors
+        ld hl, #0
+        ld de, #1
+        ld bc, #0x007f ; program first 0x80 bytes only
+        ld (hl), #0x00
+        ldir
+
+        ; now install the interrupt vector at 0x0038
+        ld a, #0xC3 ; JP instruction
+        ld (0x0038), a
+        ld hl, #interrupt_handler
+        ld (0x0039), hl
+
+        ; set restart vector for FUZIX system calls
+        ld (0x0030), a   ;  (rst 30h is unix function call vector)
+        ld hl, #unix_syscall_entry
+        ld (0x0031), hl
+
+        ld (0x0000), a   
+        ld hl, #null_handler   ;   to Our Trap Handler
+        ld (0x0001), hl
+
+        ld (0x0066), a  ; Set vector for NMI
+        ld hl, #nmi_handler
+        ld (0x0067), hl
+
+       jr map_kernel
+
+map_process:
+        ld a, h
+        or l
+        jr z, map_kernel
+map_process_always:
+map_process_always_di:
+       push af
+       push bc
+       push de
+       push hl
+       ld hl,(U_DATA__U_PAGE)
+map_write_hl:
+       ; Switch this to nextreg at some point FIXME
+       ld bc,#0x243b
+       ld (current_map),hl
+       ld e,#0x0750
+map_write_loop:
+       ld a,(hl)
+       out (c),e
+       inc b
+       out (c),a
+       dec b
+       dec d
+       jr nz, map_write_loop
+       pop hl
+       pop de
+       pop bc
+       pop af
+       ret
+
+;
+;      Save and switch to kernel
+;
+map_save_kernel:
+       push hl
+        ld hl, (current_map)
+        ld (map_store), hl
+       pop hl
+map_kernel_di:
+map_kernel:
+map_kernel_restore:
+       push af
+       push bc
+       push de
+       push hl
+       ld hl, #kernel_map
+       jr map_write_hl
+
+map_video:
+       push af
+       push bc
+       push de
+       push hl
+       ld hl, #video_map
+       jr map_write_hl
+
+map_restore:
+       push af
+       push bc
+       push de
+       push hl
+        ld hl, (map_store)
+       jr map_write_hl
+
+;
+;      We have no easy serial debug output instead just breakpoint this
+;      address when debugging.
+;
+outchar:
+       ld (_tmpout), a
+       push bc
+       push de
+       push hl
+       push ix
+       ld hl, #1
+       push hl
+       ld hl, #_tmpout
+       push hl
+       call _vtoutput
+       pop af
+       pop af
+       pop ix
+       pop hl
+       pop de
+       pop bc
+        ret
+
+       .area _COMMONDATA
+_tmpout:
+       .db 1
+
+current_map:                ; place to store current page pointer
+        .dw 0
+map_store:                 ; and the saved one
+        .dw 0
+
+kernel_map:
+       .db 16, 17, 18, 19, 20, 21, 22
+video_map:
+       .db 16, 17, 10, 11, 20, 21, 22
diff --git a/Kernel/platform-tbblue/tmxvideo.s b/Kernel/platform-tbblue/tmxvideo.s
new file mode 100644 (file)
index 0000000..7acae6a
--- /dev/null
@@ -0,0 +1,414 @@
+;
+;        Timex VT primitives
+;
+;      512 pixel mode
+;
+;      Need to move into one spot and share between tc2068/chloe/tbblue
+;
+
+        .module tmxvideo
+
+        ; exported symbols
+        .globl _plot_char
+        .globl _scroll_down
+        .globl _scroll_up
+        .globl _cursor_on
+        .globl _cursor_off
+       .globl _cursor_disable
+        .globl _clear_lines
+        .globl _clear_across
+        .globl _do_beep
+       .globl _fontdata_8x8
+       .globl _curattr
+       .globl _vtattr
+       .globl map_kernel
+       .globl map_video
+
+       ; Build the video library as the only driver
+       ; We will make this includable at some point
+
+TMXVID_ONLY    .equ    1
+
+        .area _VIDEO
+
+;
+;       zx128 vt primitives hacked a bit
+;
+;      Will be replaced by Timex video shortly
+;
+        ; exported symbols
+        .globl tmx_plot_char
+        .globl tmx_scroll_down
+        .globl tmx_scroll_up
+        .globl tmx_cursor_on
+        .globl tmx_cursor_off
+       .globl tmx_cursor_disable
+        .globl tmx_clear_lines
+        .globl tmx_clear_across
+        .globl tmx_do_beep
+       .globl _fontdata_8x8
+       .globl _curattr
+       .globl _vtattr
+
+videopos:
+       srl d               ; we alternate pixels between two screens
+       push af
+        ld a,e
+        and #7
+        rrca
+        rrca
+        rrca 
+        add a,d
+        ld d,e
+        ld e,a
+       pop af
+       jr c, rightside
+        ld a,d
+        and #0x18
+        or #0x40           ; Left screen
+        ld d,a
+       jp map_video
+rightside:
+        ld a,d
+        and #0x18
+        or #0x60           ; Left screen
+        ld d,a
+       jp map_video
+
+       .if TMXVID_ONLY
+_plot_char:
+       .endif
+tmx_plot_char:
+        pop hl
+        pop de              ; D = x E = y
+        pop bc
+        push bc
+        push de
+        push hl
+
+       ld hl,(_vtattr)     ; l is vt attributes
+       push hl             ; save attributes as inaccessible once vid mapped
+
+        call videopos
+
+        ld b, #0            ; calculating offset in font table
+        ld a, c
+
+       or a                ; clear carry
+        rla
+        rl b
+        rla
+        rl b
+        rla
+        rl b
+        ld c, a
+
+        ld hl, #_fontdata_8x8-32*8    ; font
+        add hl, bc          ; hl points to first byte of char data
+
+       pop bc
+       ; We do underline for now - not clear italic or bold are useful
+       ; with the font we have.
+
+        ; printing
+plot_char_loop:
+        ld a, (hl)
+        ld (de), a
+        inc hl              ; next byte of char data
+        inc d               ; next screen line
+
+        ld a, (hl)
+        ld (de), a
+        inc hl              ; next byte of char data
+        inc d               ; next screen line
+
+        ld a, (hl)
+        ld (de), a
+        inc hl              ; next byte of char data
+        inc d               ; next screen line
+
+        ld a, (hl)
+        ld (de), a
+        inc hl              ; next byte of char data
+        inc d               ; next screen line
+
+        ld a, (hl)
+        ld (de), a
+        inc hl              ; next byte of char data
+        inc d               ; next screen line
+
+        ld a, (hl)
+        ld (de), a
+        inc hl              ; next byte of char data
+        inc d               ; next screen line
+
+        ld a, (hl)
+        ld (de), a
+        inc hl              ; next byte of char data
+        inc d               ; next screen line
+
+        ld a, (hl)
+       bit 1,c             ; underline ?
+       jr nz, last_ul
+plot_attr:
+        ld (de), a
+       jp map_kernel
+
+last_ul:
+       ld a,#0xff
+       jr plot_attr
+
+       .if TMXVID_ONLY
+_clear_lines:
+       .endif
+tmx_clear_lines:
+        pop hl
+        pop de              ; E = line, D = count
+        push de
+        push hl
+       ; This way we handle 0 correctly
+       inc d
+       jr nextline
+
+clear_next_line:
+        push de
+        ld d, #0            ; from the column #0
+        ld b, d             ; b = 0
+        ld c, #64           ; clear 64 cols
+        push bc
+        push de
+        call _clear_across
+        pop hl              ; clear stack
+        pop hl
+
+        pop de
+        inc e
+nextline:
+        dec d
+        jr nz, clear_next_line
+
+        ret
+
+
+       .if TMXVID_ONLY
+_clear_across:
+       .endif
+tmx_clear_across:
+        pop hl
+        pop de              ; DE = coords 
+        pop bc              ; C = count
+        push bc
+        push de
+        push hl
+       ld a,c
+       or a
+       ret z               ; No work to do - bail out
+        call videopos       ; first pixel line of first character in DE
+
+       ; Save it in HL
+       ld h,d
+       ld l,e
+
+        xor a
+
+       ; TODO Figure out if starting L or R
+       bit 5,d
+       jr nz, clear_start_r
+
+
+        ; no boundary checks. Assuming that D + C < SCREEN_WIDTH
+clear_line:
+
+       ; Do the left character
+        ld b, #8            ; 8 pixel lines to clear for this char
+clear_char_l:
+        ld (de), a
+        inc d
+        djnz clear_char_l
+
+       ; Left was final char
+       dec c
+       jr z, lines_done
+
+       ; Recover position from HL and move 8K up
+       ld d,h
+       ld e,l
+       set 5,d
+
+clear_start_r:
+        ld b, #8            ; 8 pixel lines to clear for this char
+clear_char_r:
+        ld (de), a
+        inc d
+        djnz clear_char_r
+
+        inc hl
+       ld d,h
+       ld e,l
+
+       ; Next character ?
+        dec c
+        jr nz, clear_line
+lines_done:
+       jp map_kernel
+
+copy_line:
+        ; HL - source, DE - destination
+
+        ; convert line coordinates to screen coordinates both for DE and HL
+        push de
+        ex de, hl
+        call videopos
+        ex de, hl
+        pop de
+        call videopos
+
+        ld c, #8
+
+copy_line_nextchar:
+        push hl
+        push de
+
+        ld b, #32
+
+copy_pixel_line:
+        ld a, (hl)
+        ld (de), a
+        inc e
+        inc l
+        djnz copy_pixel_line
+
+        pop de
+        pop hl
+       push hl
+       push de
+       set 5,d         ; Even half
+       set 5,h
+       ld b,#32
+copy_pixel_line_r:
+       ld a,(hl)
+       ld (de),a
+       inc e
+       inc l
+       djnz copy_pixel_line_r
+       pop de
+       pop hl
+        inc d
+        inc h
+        dec c
+        jr nz, copy_line_nextchar
+        ret
+
+        ; TODO: the LDIR way should be much faster
+
+       .if TMXVID_ONLY
+_scroll_down:
+       .endif
+tmx_scroll_down:
+        ; set HL = (0,22), DE = (0, 23)
+        xor a
+        ld d, a
+        ld h, a
+        ld l, #22
+        ld e, #23
+        ld c, #23           ; 23 lines to move
+
+       call map_video
+
+loop_scroll_down:
+        push hl
+        push de
+        push bc
+
+        call copy_line
+
+        pop bc
+        pop de
+        pop hl
+
+        dec l
+        dec e
+        dec c
+        jr nz, loop_scroll_down
+        jp map_kernel
+
+
+       .if TMXVID_ONLY
+_scroll_up:
+       .endif
+tmx_scroll_up:
+        ; set HL = (0,1), DE = (0, 0)
+        xor a
+        ld d, a
+        ld e, a
+        ld h, a
+        ld l, #1
+        ld c, #23           ; 23 lines to move
+
+       call map_video
+
+loop_scroll_up:
+        push hl
+        push de
+        push bc
+
+        call copy_line
+
+        pop bc
+        pop de
+        pop hl
+
+        inc l
+        inc e
+        dec c
+        jr nz, loop_scroll_up
+       jp map_kernel
+
+       .if TMXVID_ONLY
+_cursor_on:
+       .endif
+tmx_cursor_on:
+        pop hl
+        pop de
+        push de
+        push hl
+        ld (cursorpos), de
+
+        call videopos
+        ld a, #7
+        add a, d
+        ld d, a
+        ld a, #0xFF
+        ld (de), a
+        jp map_kernel
+       .if TMXVID_ONLY
+_cursor_disable:
+_cursor_off:
+       .endif
+tmx_cursor_disable:
+tmx_cursor_off:
+        ld de, (cursorpos)
+        call videopos
+        ld a, #7
+        add a, d
+        ld d, a
+        xor a
+        ld (de), a
+       jp map_kernel
+
+       .if TMXVID_ONLY
+_do_beep:
+       .endif
+tmx_do_beep:
+        ret
+
+        .area _DATA
+
+cursorpos:
+        .dw 0
+
+       .area _COMMONDATA
+
+_curattr:
+       .db 7
+
diff --git a/Kernel/platform-tbblue/tricks.s b/Kernel/platform-tbblue/tricks.s
new file mode 100644 (file)
index 0000000..62cfb04
--- /dev/null
@@ -0,0 +1,279 @@
+; 2013-12-21 William R Sowerbutts
+
+        .module tricks
+
+        .globl _ptab_alloc
+        .globl _makeproc
+        .globl _chksigs
+        .globl _getproc
+        .globl _platform_monitor
+        .globl trap_illegal
+        .globl _platform_switchout
+        .globl _switchin
+        .globl _doexec
+        .globl _dofork
+        .globl _runticks
+        .globl unix_syscall_entry
+        .globl interrupt_handler
+       .globl map_kernel
+       .globl _need_resched
+       .globl _int_disabled
+       .globl _udata
+
+        ; imported debug symbols
+        .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex
+
+        .include "kernel.def"
+        .include "../kernel.def"
+
+        .area _COMMONMEM
+
+_need_resched:
+       .db 0
+
+; Switchout switches out the current process, finds another that is READY,
+; possibly the same process, and switches it in.  When a process is
+; restarted after calling switchout, it thinks it has just returned
+; from switchout().
+;
+; This function can have no arguments or auto variables.
+_platform_switchout:
+        ; save machine state
+
+        ld hl, #0 ; return code set here is ignored, but _switchin can 
+        ; return from either _switchout OR _dofork, so they must both write 
+        ; U_DATA__U_SP with the following on the stack:
+        push hl ; return code
+        push ix
+        push iy
+        ld (U_DATA__U_SP), sp ; this is where the SP is restored in _switchin
+
+        ; find another process to run (may select this one again)
+        call _getproc
+
+        push hl
+        call _switchin
+
+        ; we should never get here
+        call _platform_monitor
+
+badswitchmsg: .ascii "_switchin: FAIL"
+            .db 13, 10, 0
+
+_switchin:
+        di
+        pop bc  ; return address
+        pop de  ; new process pointer
+;
+;      FIXME: do we actually *need* to restore the stack !
+;
+        push de ; restore stack
+        push bc ; restore stack
+
+        ld hl, #P_TAB__P_PAGE_OFFSET
+       add hl, de              ; process ptr
+       ld a,(hl)
+       inc hl
+       ld h,(hl)               ; page pointer
+       ld l,a
+
+       ld bc,#7
+       add hl,bc               ; common
+
+       ld bc,#0x243B
+       ld a,#0x57              ; common bank MMU pointer
+       out (c),a
+
+       ld a, (hl)
+       inc b
+       out (c), a      ; *CAUTION* our stack just left the building
+
+       ; ------- No stack -------
+        ; check u_data->u_ptab matches what we wanted
+        ld hl, (U_DATA__U_PTAB) ; u_data->u_ptab
+        or a                    ; clear carry flag
+        sbc hl, de              ; subtract, result will be zero if DE==IX
+        jr nz, switchinfail
+
+       ; wants optimising up a bit
+       ld hl, #P_TAB__P_STATUS_OFFSET
+       add hl, de
+       ld (hl), #P_RUNNING
+
+        ; runticks = 0
+        ld hl, #0
+        ld (_runticks), hl
+
+        ; restore machine state -- note we may be returning from either
+        ; _switchout or _dofork
+        ld sp, (U_DATA__U_SP)
+
+       ; ---- New task stack ----
+
+        pop iy
+        pop ix
+        pop hl ; return code
+
+        ; enable interrupts, if the ISR isn't already running
+        ld a, (U_DATA__U_ININTERRUPT)
+       ld (_int_disabled),a
+        or a
+        ret nz ; in ISR, leave interrupts off
+        ei
+        ret ; return with interrupts on
+
+switchinfail:
+       call outhl
+        ld hl, #badswitchmsg
+        call outstring
+       ; something went wrong and we didn't switch in what we asked for
+        jp _platform_monitor
+
+fork_proc_ptr: .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry
+
+;
+;      Called from _fork. We are in a syscall, the uarea is live as the
+;      parent uarea. The kernel is the mapped object.
+;
+_dofork:
+        ; always disconnect the vehicle battery before performing maintenance
+        di ; should already be the case ... belt and braces.
+
+        pop de  ; return address
+        pop hl  ; new process p_tab*
+        push hl
+        push de
+
+        ld (fork_proc_ptr), hl
+
+        ; prepare return value in parent process -- HL = p->p_pid;
+        ld de, #P_TAB__P_PID_OFFSET
+        add hl, de
+        ld a, (hl)
+        inc hl
+        ld h, (hl)
+        ld l, a
+
+        ; Save the stack pointer and critical registers.
+        ; When this process (the parent) is switched back in, it will be as if
+        ; it returns with the value of the child's pid.
+        push hl ; HL still has p->p_pid from above, the return value in the parent
+        push ix
+        push iy
+
+        ; save kernel stack pointer -- when it comes back in the parent we'll be in
+        ; _switchin which will immediately return (appearing to be _dofork()
+       ; returning) and with HL (ie return code) containing the child PID.
+        ; Hurray.
+        ld (U_DATA__U_SP), sp
+
+        ; now we're in a safe state for _switchin to return in the parent
+       ; process.
+
+       ; --------- we switch stack copies in this call -----------
+       call fork_copy                  ; copy 0x000 to udata.u_top and the
+                                       ; uarea and return on the childs
+                                       ; common
+       ; We are now in the kernel child context
+
+        ; now the copy operation is complete we can get rid of the stuff
+        ; _switchin will be expecting from our copy of the stack.
+        pop bc
+        pop bc
+        pop bc
+
+        ; The child makes its own new process table entry, etc.
+       ld hl, #_udata
+       push hl
+        ld hl, (fork_proc_ptr)
+        push hl
+        call _makeproc
+        pop bc 
+       pop bc
+
+       ; any calls to map process will now map the childs memory
+
+        ; runticks = 0;
+        ld hl, #0
+        ld (_runticks), hl
+        ; in the child process, fork() returns zero.
+       ;
+       ; And we exit, with the kernel mapped, the child now being deemed
+       ; to be the live uarea. The parent is frozen in time and space as
+       ; if it had done a switchout().
+        ret
+
+;
+;      FIXME: DMA for this ?
+;
+fork_copy:
+       ld hl, (fork_proc_ptr)
+       ld de, #P_TAB__P_PAGE_OFFSET
+       add hl,de
+       ld a,(hl)
+       inc hl
+       ld h,(hl)
+       ld l,a
+       ex de,hl
+
+       ; DE is now the 8 page pointers for the child
+
+       ld hl, (U_DATA__U_PAGE) ; pointer to banks of parent
+
+       ; HL for the parent
+
+       ld b,#7                 ; 7 full banks to copy
+
+       ld a,#0xff              ; never used page value
+fork_copy_loop:
+       cp (hl)                 ; same as previous - don't copy
+       jr z, nocopy
+       ld a,(hl)
+       push bc
+       call do_copy
+       pop bc
+nocopy:
+       inc hl
+       inc de
+       djnz fork_copy_loop
+
+       exx
+       ld bc,#0x0200           ; 512 bytes at 0xF000
+                               ; will need changes if we move it!
+       ld hl,#0x3000
+       ld de,#0x5000
+       call do_partial_copy
+       ;
+       ; Put the MMU mappings back to sanity
+       ;
+       jp map_kernel
+
+do_copy:
+       ; On entry (HL) is the source bank (DE) is the target bank
+       ; BC is the size
+       ;
+       ; We preserve AF, DE, HL
+       exx
+       ld hl,#0x2000
+       ld de,#0x4000
+       ld bc,#0x2000
+do_partial_copy:
+       exx
+       push af
+       ld bc,#0x243B
+       ld a,#0x51
+       out (c),a               ; 2000-4000
+       inc b
+       ld a,(hl)
+       out (c),a
+       dec b
+       ld a,#0x52              ; 4000-6000
+       out (c),a
+       inc b
+       ld a,(de)
+       out (c),a
+       pop af
+       exx
+       ldir
+       exx
+       ret