kernel: initial development tree for the Sam Coupe
authorAlan Cox <alan@linux.intel.com>
Tue, 7 Aug 2018 23:13:43 +0000 (00:13 +0100)
committerAlan Cox <alan@linux.intel.com>
Tue, 7 Aug 2018 23:13:43 +0000 (00:13 +0100)
This is so that the 32K/32K split code can be developed. This memory model is
actually a lot more complex and different to the existing Z80 models we use.

Don't expect anything runnable for quite some time.

22 files changed:
Kernel/platform-sam/Makefile [new file with mode: 0644]
Kernel/platform-sam/README [new file with mode: 0644]
Kernel/platform-sam/boot.s [new file with mode: 0644]
Kernel/platform-sam/commonmem.s [new file with mode: 0644]
Kernel/platform-sam/config.h [new file with mode: 0644]
Kernel/platform-sam/crt0.s [new file with mode: 0644]
Kernel/platform-sam/devfd.c [new file with mode: 0644]
Kernel/platform-sam/devfd.h [new file with mode: 0644]
Kernel/platform-sam/devices.c [new file with mode: 0644]
Kernel/platform-sam/devlpr.c [new file with mode: 0644]
Kernel/platform-sam/devlpr.h [new file with mode: 0644]
Kernel/platform-sam/devtty.c [new file with mode: 0644]
Kernel/platform-sam/devtty.h [new file with mode: 0644]
Kernel/platform-sam/floppy.s [new file with mode: 0644]
Kernel/platform-sam/fuzix.lnk [new file with mode: 0644]
Kernel/platform-sam/kernel.def [new file with mode: 0644]
Kernel/platform-sam/main.c [new file with mode: 0644]
Kernel/platform-sam/rules.mk [new file with mode: 0644]
Kernel/platform-sam/sam.s [new file with mode: 0644]
Kernel/platform-sam/sam_vt.s [new file with mode: 0644]
Kernel/platform-sam/target.mk [new file with mode: 0644]
Kernel/platform-sam/tricks.s [new file with mode: 0644]

diff --git a/Kernel/platform-sam/Makefile b/Kernel/platform-sam/Makefile
new file mode 100644 (file)
index 0000000..a9856af
--- /dev/null
@@ -0,0 +1,25 @@
+
+CSRCS = devlpr.c devtty.c devfd.c
+CSRCS += devices.c main.c
+
+ASRCS = sam.s crt0.s sam_vt.s floppy.s
+ASRCS += tricks.s commonmem.s
+
+COBJS = $(CSRCS:.c=.rel)
+AOBJS = $(ASRCS:.s=.rel)
+OBJS  = $(COBJS) $(AOBJS)
+
+JUNK = $(CSRCS:.c=.lst) $(CSRCS:.c=.asm) $(CSRCS:.c=.sym) $(ASRCS:.s=.lst) $(ASRCS:.s=.sym) $(CSRCS:.c=.rst) $(ASRCS:.s=.rst)
+
+all:   $(OBJS)
+
+$(COBJS): %.rel: %.c
+       $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(AOBJS): %.rel: %.s
+       $(CROSS_AS) $(ASOPTS) $<
+
+clean:
+       rm -f $(OBJS) $(JUNK)  core *~ 
+
+image:
diff --git a/Kernel/platform-sam/README b/Kernel/platform-sam/README
new file mode 100644 (file)
index 0000000..ab6effb
--- /dev/null
@@ -0,0 +1,90 @@
+Sam Coupe prototyping and 32K/32K split development work
+
+Proposed memory layout
+
+Kernel mode
+
+0-7FFF         Kernel in bank 2/3
+8000-FFFF      Kernel in bank 0/1
+
+Video
+
+0-5FFF         Frame buffer (mode 3)   bank 4/5
+6000-6FFF      Font
+7000-7FFF      Free
+8000-FFFF      Kernel in bank 0/1
+
+User Space
+
+0000-00FF      Thunking code
+0100-7FFF      User page
+8000-FCFFF     User page
+FD00-FEFF      UDATA stash
+FF00-FFFF      Thunking code
+
+Probably will move the thunking code mostly into FFxx somewhere so we
+can do CP/M emulation still
+
+Extmem support would require extra work and separate high/low pools for
+the bank allocator
+
+User Copies
+
+0000-7FFF      User bank for copy
+8000-FFFF      Kernel in bank 0/1
+
+Forking
+
+0000-00FF      Parent low stubs
+0100-7FFF      Parent low bank
+8000-80FF      Child low stubs (not yet set)
+8100-FFFF      Child low bank
+
+then
+
+0000-7EFF      Parent high bank
+7F00-7FFF      Parent high stubs
+8000-FEFF      Child high bank
+FF00-FFFF      Child stubs (not yet set)
+
+Stub code is in the low parts of the low banks and the top of the high banks
+so we have two copier routines one for each half.
+
+
+To Do:
+
+-      Keyboard map
+-      Interrupt logic
+-      Boot loader
+
+Then once we can get to the point of being able to see and type
+
+-      Move fonts to video bank after frame buffer
+-      Floppy driver (at least for read)
+-      Get to init
+-      Debug all the new banking code and stubs
+-      Atom and Atom-lite IDE driver
+-      Look at how to preserve high colour bits
+
+-      RTC driver
+-      Serial driver
+-      Mouse/Joystick/Input
+
+-      Mode setting/graphics mode support
+-      Video ioctls
+-      UDG ioctls
+-      Is there any way to do video mapping into user process sanely
+       (probably not)
+
+-      Sound
+
+-      MegaRAM
+
+-      Maybe look at a 16K boundary aware allocator to get better memory
+       packing - but it's really hairy.
+
+-      Less interrupt masking (but the banking logic makes it really foul
+       especially as we don't have proper IM2 support)
+
+-      Maybe look at the modern add ons (SD card, network etc)
+
diff --git a/Kernel/platform-sam/boot.s b/Kernel/platform-sam/boot.s
new file mode 100644 (file)
index 0000000..9e6a03f
--- /dev/null
@@ -0,0 +1,84 @@
+;
+;      Boot block, loaded at 0x4000
+;
+;      Banks and SP we need to double check are ROM0/1/2/3 and valid
+;
+;      The boot block is trivial but it gets a bit fun once we've loaded
+;      our 14 tracks
+;
+;      We load
+;      32K into bank 2/3
+;      32K into bank 4/5
+;      5.5K into bank 6/7
+;
+;      by loading 14 tracks off side 0 skipping track 0 sector 1 which is
+;      the boot block itself.
+;
+;      We then run from 0x8000 in bank 6, which will untangle everything
+;      for us shuffling the kernel so that 0/1 is the low kernel in RAM
+;      2/3 is the high kernel in RAM and 4/5 is the video
+;      (or maybe it'll be saner to use 4/5 for kernel 2/3 video..)
+;      
+boot:
+       di
+       ld de,#2                ; track 0 sector 2 (we are sector 0)
+       ld a,e                  ; Start in bank 2
+       ld hl,#0x8000           ; Which we map high
+       out (HIMEM),a
+       ex af,af'
+next_sec:
+       bit 7,h                 ; If the last read went to 0000
+       jr nz, still_good
+       set 7,h                 ; Go back to 8000 and move on 32K
+       ex af,af'
+       inc a
+       inc a
+       out (HIMEM),a           ; switch bank
+       ex af,af'
+still_good:
+       ld a,e                  ; update the sector register
+       ld (SECTOR),a
+dread: ld a,#CMD_READ          ; Ask for data from the FDC
+       out (CMD),a
+       ld b,#20                ; Wait for FDC
+nap:   djnz nap
+       ld bc,DATA              ; Begin reading from data port
+       jr waitbyte
+byte:  ini
+wait:  in a,(STATUS)           ; Wait for DRQ
+       bit 1,a
+       jr nz,byte              ; When we get a DRQ read a byte
+       rrca                    ; Check for end/error
+       jr c,wait
+       ; and with 0d to check error FIXME
+       ;
+       ;       Sector done. Move on a sector
+       ;
+       inc e
+       ld a,#11
+       cp e
+       jr nz, next_sec
+       ;
+       ;       Move on a track - do a step in command
+       ;
+       ld e,#1
+       inc d
+       ;
+       ;       We read 14 tracks minus 1 sector which loads us
+       ;
+       ;
+       ld a,#14
+       cp d
+       ; Jump to the start of the last bank loaded. This is actually
+       ; packed up boot stuff and font. We loaded 5.5K here, of which 4K
+       ; was font, leaving a spacious 1.5K to untangle all the banks and
+       ; boot up.
+       jp z, 0x8000
+       ld a,#CMD_STEPIN
+       out (CMD),a
+nap2:  ld b,#20
+       djnz nap2
+wait2: in a,(STATUS)
+       bit 0,a
+       jr nz,wait2
+       jr next_sec
diff --git a/Kernel/platform-sam/commonmem.s b/Kernel/platform-sam/commonmem.s
new file mode 100644 (file)
index 0000000..bafd590
--- /dev/null
@@ -0,0 +1,10 @@
+;
+;      We have no real common on the TRS80so just tuck it up at the top of
+;      memory leaving room for the keyboard and video (3K)
+;
+        .module commonmem
+
+        .area _COMMONMEM
+
+       .include "../cpu-z80/std-commonmem.s"
+
diff --git a/Kernel/platform-sam/config.h b/Kernel/platform-sam/config.h
new file mode 100644 (file)
index 0000000..3377692
--- /dev/null
@@ -0,0 +1,52 @@
+/* Set if you want RTC support and have an RTC on ports 0xB0-0xBC */
+#undef CONFIG_RTC
+
+/* Enable to make ^Z dump the inode table for debug */
+#undef CONFIG_IDUMP
+/* Enable to make ^A drop back into the monitor */
+#undef CONFIG_MONITOR
+/* Profil syscall support (not yet complete) */
+#define CONFIG_PROFIL
+/* Multiple processes in memory at once */
+#define CONFIG_MULTI
+/* Single tasking */
+#undef CONFIG_SINGLETASK
+
+/* Video terminal, not a serial tty */
+#define CONFIG_VT
+/* Fonts are pre-expanded as we have reasonable amounts of memory and
+   need to deal with a 2bit deep bitmap display */
+#define CONFIG_FONT_8X8_EXP2
+
+/* Banked memory set up */
+/* FIXME */
+#define CONFIG_BANK_FIXED
+#define MAX_MAPS       16
+#define MAP_SIZE       0xFD00
+
+#define CONFIG_BANKS   2       /* 2 x 32K */
+
+/* Vt definitions */
+#define VT_WIDTH       80
+#define VT_HEIGHT      24
+#define VT_RIGHT       79
+#define VT_BOTTOM      23
+
+#define TICKSPERSEC 50   /* Ticks per second */
+#define PROGBASE    0x0000  /* Base of user  */
+#define PROGLOAD    0x0100  /* Load and run here */
+#define PROGTOP     0xFD00  /* Top of program, base of U_DATA stash */
+#define PROC_SIZE   64             /* Memory needed per process */
+
+#define BOOT_TTY (512 + 1)      /* Set this to default device for stdio, stderr */
+                          /* In this case, the default is the first TTY device */
+
+/* We need a tidier way to do this from the loader */
+#define CMDLINE        NULL      /* Location of root dev name */
+
+/* Device parameters */
+#define NUM_DEV_TTY 2
+#define TTYDEV   BOOT_TTY /* Device used by kernel for messages, panics */
+
+#define NBUFS    10       /* Number of block buffers */
+#define NMOUNTS         4        /* Number of mounts at a time */
diff --git a/Kernel/platform-sam/crt0.s b/Kernel/platform-sam/crt0.s
new file mode 100644 (file)
index 0000000..d0ef914
--- /dev/null
@@ -0,0 +1,70 @@
+               ; For 32K banking we need to keep anything that is common
+               ; and anything that might be copied to or from user space
+               ; in the top 32K. So fill the bottom with code
+               .area _CODE
+               .area _CODE2
+               .area _HOME
+               ; Must be above the 32K boundary
+               ; FIXME: we really want to bank the fonts in the video area
+               .area _VIDEO
+               .area _HIGH
+               .area _FONT
+               .area _CONST
+               .area _INITIALIZED
+               .area _DATA
+               .area _BSEG
+               .area _BSS
+               .area _HEAP
+               ; note that areas below here may be overwritten by the heap at runtime, so
+               ; put initialisation stuff in here
+               .area _GSINIT
+               .area _GSFINAL
+               .area _DISCARD
+               .area _INITIALIZER
+               .area _COMMONMEM
+
+               .area _PAGE0
+               .area _PAGEH
+
+               ; 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__COMMONMEM
+               .globl l__COMMONMEM
+               .globl s__INITIALIZER
+               .globl kstack_top
+
+               ; startup code
+               .area _CODE
+
+;
+;      Once the loader completes it jumps here
+;
+start:
+               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
+               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
+               call init_early
+               call init_hardware
+               call _fuzix_main
+               di
+stop:          halt
+               jr stop
diff --git a/Kernel/platform-sam/devfd.c b/Kernel/platform-sam/devfd.c
new file mode 100644 (file)
index 0000000..77503e1
--- /dev/null
@@ -0,0 +1,102 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devfd.h>
+
+#define MAX_FD 4
+
+#define OPDIR_NONE     0
+#define OPDIR_READ     1
+#define OPDIR_WRITE    2
+
+#define FD_READ                0x80    /* 2797 needs 0x88, 1797 needs 0x80 */
+#define FD_WRITE       0xA0    /* Likewise A8 v A0 */
+
+static uint8_t motorct;
+static uint8_t fd_selected = 0xFF;
+static uint8_t fd_tab[MAX_FD] = { 0xFF, 0xFF, 0xFF, 0xFF };
+
+/*
+ *     We only support normal block I/O for the moment. We do need to
+ *     add swapping!
+ */
+
+static uint8_t selmap[4] = { 0x01, 0x02, 0x04, 0x08 };
+
+static int fd_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
+{
+    int ct = 0;
+    int tries;
+    uint8_t err = 0;
+    uint8_t *driveptr = fd_tab + minor;
+    uint8_t cmd[6];
+
+    if(rawflag == 2)
+        goto bad2;
+
+    if (fd_selected != minor) {
+        uint8_t err;
+        /* FIXME: We force DD for now */
+        err = fd_motor_on(selmap[minor]|0x80);
+        if (err)
+            goto bad;
+    }
+
+    if (*driveptr == 0xFF)
+        fd_reset(driveptr);
+
+    if (rawflag && d_blkoff(BLKSHIFT))
+            return -1;
+
+    cmd[0] = is_read ? FD_READ : FD_WRITE;
+    cmd[1] = udata.u_block / 9;                /* 2 sectors per block */
+    cmd[2] = ((udata.u_block % 9) << 1) + 1;   /*eww.. */
+    cmd[3] = is_read ? OPDIR_READ: OPDIR_WRITE;
+    cmd[4] = ((uint16_t)udata.u_dptr) & 0xFF;
+    cmd[5] = ((uint16_t)udata.u_dptr) >> 8;
+
+    while (ct < 2) {
+        for (tries = 0; tries < 4 ; tries++) {
+            err = fd_operation(cmd, driveptr);
+            if (err == 0)
+                break;
+            if (tries > 1)
+                fd_reset(driveptr);
+        }
+        /* FIXME: should we try the other half and then bale out ? */
+        if (tries == 4)
+            goto bad;
+        cmd[5]++;      /* Move on 256 bytes in the buffer */
+        cmd[2]++;      /* Next sector for 2nd block */
+        ct++;
+    }
+    return udata.u_nblock << 8;
+bad:
+    kprintf("fd%d: error %x\n", minor, err);
+bad2:
+    udata.u_error = EIO;
+    return -1;
+}
+
+int fd_open(uint8_t minor, uint16_t flag)
+{
+    flag;
+    if(minor >= MAX_FD) {
+        udata.u_error = ENODEV;
+        return -1;
+    }
+    return 0;
+}
+
+int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    flag;
+    return fd_transfer(minor, true, rawflag);
+}
+
+int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    flag;rawflag;minor;
+//    return 0;
+    return fd_transfer(minor, false, rawflag);
+}
diff --git a/Kernel/platform-sam/devfd.h b/Kernel/platform-sam/devfd.h
new file mode 100644 (file)
index 0000000..5f94507
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __DEVFD_DOT_H__
+#define __DEVFD_DOT_H__
+
+/* public interface */
+int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int fd_open(uint8_t minor, uint16_t flag);
+
+/* low level interface */
+uint16_t fd_reset(uint8_t *driveptr);
+uint16_t fd_operation(uint8_t *cmd, uint8_t *driveptr);
+uint16_t fd_motor_on(uint16_t drivesel);
+uint16_t fd_motor_off(uint16_t driveptr);
+
+#endif /* __DEVFD_DOT_H__ */
diff --git a/Kernel/platform-sam/devices.c b/Kernel/platform-sam/devices.c
new file mode 100644 (file)
index 0000000..66c7184
--- /dev/null
@@ -0,0 +1,42 @@
+#include <kernel.h>
+#include <tty.h>
+#include <version.h>
+#include <kdata.h>
+#include <devfd.h>
+#include <devsys.h>
+#include <devlpr.h>
+#include <devtty.h>
+
+struct devsw dev_tab[] =  /* The device driver switch table */
+{
+  /* 0: FIXME IDE */
+  {  fd_open,     no_close,     fd_read,   fd_write,   no_ioctl  },
+  /* 1: /dev/dd                Hard disc block devices */
+  {  fd_open,     no_close,     fd_read,   fd_write,   no_ioctl  },
+  /* 2: /dev/tty       TTY devices */
+  {  tty_open,    tty_close,    tty_read,  tty_write,  tty_ioctl },
+  /* 3: /dev/lpr       Printer devices */
+  {  lpr_open,    lpr_close,    no_rdwr,   lpr_write,  no_ioctl  },
+  /* 4: /dev/mem etc   System devices (one offs) */
+  {  no_open,     no_close,     sys_read,  sys_write,  sys_ioctl },
+  /* Pack to 7 with nxio if adding private devices and start at 8 */
+};
+
+bool validdev(uint16_t dev)
+{
+    /* This is a bit uglier than needed but the right hand side is
+       a constant this way */
+    if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) - 1)
+       return false;
+    else
+        return true;
+}
+
+void device_init(void)
+{
+  int i;
+#ifdef CONFIG_RTC
+  /* Time of day clock */
+  inittod();
+#endif
+}
diff --git a/Kernel/platform-sam/devlpr.c b/Kernel/platform-sam/devlpr.c
new file mode 100644 (file)
index 0000000..65d8c43
--- /dev/null
@@ -0,0 +1,38 @@
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <devlpr.h>
+
+__sfr __at 0x02 lpstat;                /* I/O 2 and 3 */
+__sfr __at 0x03 lpdata;
+
+int lpr_open(uint8_t minor, uint16_t flag)
+{
+    minor; flag; // shut up compiler
+    return 0;
+}
+
+int lpr_close(uint8_t minor)
+{
+    minor; // shut up compiler
+    return 0;
+}
+
+int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    int c = udata.u_count;
+    char *p = udata.u_base;
+    minor; rawflag; flag; // shut up compiler
+
+    while(c) {
+        /* Note; on real hardware it might well be necessary to
+           busy wait a bit just to get acceptable performance */
+        while (lpstat != 0xFF) {
+//            if (psleep_flags(&clocktick, flag))
+//                return -1;
+        }
+        /* FIXME: tidy up ugetc and sysio checks globally */
+        lpdata = ugetc(p++);
+    }
+    return (-1);
+}
diff --git a/Kernel/platform-sam/devlpr.h b/Kernel/platform-sam/devlpr.h
new file mode 100644 (file)
index 0000000..7765c18
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __DEVLPR_DOT_H__
+#define __DEVLPR_DOT_H__
+
+int lpr_open(uint8_t minor, uint16_t flag);
+int lpr_close(uint8_t minor);
+int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+
+#endif
diff --git a/Kernel/platform-sam/devtty.c b/Kernel/platform-sam/devtty.c
new file mode 100644 (file)
index 0000000..78dfb7a
--- /dev/null
@@ -0,0 +1,177 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <tty.h>
+#include <vt.h>
+#include <devtty.h>
+#include <stdarg.h>
+
+char tbuf1[TTYSIZ];
+char tbuf2[TTYSIZ];
+
+struct  s_queue  ttyinq[NUM_DEV_TTY+1] = {       /* ttyinq[0] is never used */
+    {   NULL,    NULL,    NULL,    0,        0,       0    },
+    {   tbuf1,   tbuf1,   tbuf1,   TTYSIZ,   0,   TTYSIZ/2 },
+    {   tbuf2,   tbuf2,   tbuf2,   TTYSIZ,   0,   TTYSIZ/2 }
+};
+
+/* Write to system console */
+void kputchar(char c)
+{
+    if(c=='\n')
+        tty_putc(1, '\r');
+    tty_putc(1, c);
+}
+
+uint8_t tty_writeready(uint8_t minor)
+{
+    return TTY_READY_NOW;
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+    if (minor == 1)
+        vtoutput(&c, 1);
+}
+
+void tty_interrupt(void)
+{
+}
+
+void tty_setup(uint8_t minor)
+{
+}
+
+int tty_carrier(uint8_t minor)
+{
+    return 1;
+}
+
+void tty_sleeping(uint8_t minor)
+{
+}
+
+void tty_data_consumed(uint8_t minor)
+{
+}
+
+static uint8_t keymap[9];
+uint8_t keyin[9];
+static uint8_t keybyte, keybit;
+static uint8_t newkey;
+static int keysdown = 0;
+static uint8_t shiftmask[8] = {
+    0, 0, 0, 0, 0, 0, 0, 7
+};
+
+static void keyproc(void)
+{
+       int i;
+       uint8_t key;
+
+       /* Call asm keyin here FIXME */
+       for (i = 0; i < 8; i++) {
+               /* Set one of A0 to A7, and read the byte we get back.
+                  Invert that to get a mask of pressed buttons */
+               key = keyin[i] ^ keymap[i];
+               if (key) {
+                       int n;
+                       int m = 1;
+                       for (n = 0; n < 8; n++) {
+                               if ((key & m) && (keymap[i] & m)) {
+                                       if (!(shiftmask[i] & m))
+                                               keysdown--;
+                               }
+                               if ((key & m) && !(keymap[i] & m)) {
+                                       if (!(shiftmask[i] & m))
+                                               keysdown++;
+                                       keybyte = i;
+                                       keybit = n;
+                                       newkey = 1;
+                               }
+                               m += m;
+
+                       }
+               }
+               keymap[i] = keyin[i];
+       }
+}
+
+static uint8_t keyboard[8][8] = {
+       {'@', 'a', 'b', 'c', 'd', 'e', 'f', 'g' },
+       {'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o' },
+       {'p', 'q', 'r', 's', 't', 'u', 'v', 'w' },
+       {'x', 'y', 'z', '[', '\\', ']', '^', '_' },
+       {'0', '1', '2', '3', '4', '5', '6', '7' },
+       {'8', '9', ':', ';', ',', '-', '.', '/' },
+       {13, 12, 3, 0/*up*/, 0/*down*/, 8/* left */, 0/*right*/, ' '},
+       { 0, 0, 0, 0, 0xF1, 0xF2, 0xF3, 0 }
+};
+
+static uint8_t shiftkeyboard[8][10] = {
+       {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G' },
+       {'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O' },
+       {'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W' },
+       {'X', 'Y', 'Z', '{', '|', '}', '^', '_' },
+       {'0', '!', '"', '#', '$', '%', '&', '\'' },
+       {'(', ')', '*', '+', '<', '=', '>', '?' },
+       {13, 12, 3, 0/*up*/, 0/*down*/, 8/* left */, 0/*right*/, ' '},
+       { 0, 0, 0, 0, 0xF1, 0xF2, 0xF3, 0 }
+};
+
+static uint8_t capslock = 0;
+
+static void keydecode(void)
+{
+       uint8_t c;
+
+       if (keybyte == 7 && keybit == 3) {
+               capslock = 1 - capslock;
+               return;
+       }
+
+       if (keymap[7] & 3)      /* shift */
+               c = shiftkeyboard[keybyte][keybit];
+       else
+               c = keyboard[keybyte][keybit];
+
+        /* The keyboard lacks some rather important symbols so remap them
+           with control */
+       if (keymap[7] & 4) {    /* control */
+               if (c > 31 && c < 96)
+                       c &= 31;
+                if (keymap[7] & 3) {
+                    if (c == '(')
+                        c = '{';
+                    if (c == ')')
+                        c = '}';
+                    if (c == '-')
+                        c = '_';
+                    if (c == '/')
+                        c = '``';
+                    if (c == '<')
+                        c = '^';
+                } else {
+                    if (c == '(')
+                        c = '[';
+                    if (c == ')')
+                        c = ']';
+                    if (c == '-')
+                        c = '|';
+                }
+       }
+       if (capslock && c >= 'a' && c <= 'z')
+               c -= 'a' - 'A';
+       if (c)
+               tty_inproc(1, c);
+}
+
+void kbd_interrupt(void)
+{
+       newkey = 0;
+       keyproc();
+       if (keysdown < 3 && newkey)
+               keydecode();
+}
+
diff --git a/Kernel/platform-sam/devtty.h b/Kernel/platform-sam/devtty.h
new file mode 100644 (file)
index 0000000..bda124e
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _DEVTTY_H
+#define _DEVTTY_H
+
+extern void tty_interrupt(void);
+extern void kbd_interrupt(void);
+
+#endif
diff --git a/Kernel/platform-sam/floppy.s b/Kernel/platform-sam/floppy.s
new file mode 100644 (file)
index 0000000..6298393
--- /dev/null
@@ -0,0 +1,16 @@
+;
+;      TODO
+;
+
+       .module floppy
+
+       .area _HIGH
+
+       .globl _fd_motor_on
+       .globl _fd_operation
+       .globl _fd_reset
+
+_fd_operation:
+_fd_reset:
+_fd_motor_on:
+       ret
diff --git a/Kernel/platform-sam/fuzix.lnk b/Kernel/platform-sam/fuzix.lnk
new file mode 100644 (file)
index 0000000..62ea0f6
--- /dev/null
@@ -0,0 +1,43 @@
+-mwxuy
+-i fuzix.ihx
+-b _PAGE0=0x0001
+-b _CODE=0x0100
+-b _COMMONMEM=0xF800
+-b _PAGEH=0xFF00
+-l z80
+platform-sam/crt0.rel
+platform-sam/commonmem.rel
+start.rel
+version.rel
+lowlevel-z80-thunked.rel
+usermem.rel
+usermem_std-z80-thunked.rel
+timer.rel
+kdata.rel
+devio.rel
+filesys.rel
+process.rel
+inode.rel
+syscall_exec16.rel
+syscall_fs.rel
+syscall_fs2.rel
+syscall_fs3.rel
+syscall_proc.rel
+syscall_other.rel
+tty.rel
+mm.rel
+swap.rel
+bankfixed.rel
+vt.rel
+font8x8_exp2.rel
+devsys.rel
+platform-sam/devfd.rel
+platform-sam/devices.rel
+platform-sam/devlpr.rel
+platform-sam/devtty.rel
+platform-sam/floppy.rel
+platform-sam/main.rel
+platform-sam/sam.rel
+platform-sam/tricks.rel
+platform-sam/sam_vt.rel
+-e
diff --git a/Kernel/platform-sam/kernel.def b/Kernel/platform-sam/kernel.def
new file mode 100644 (file)
index 0000000..28a47e3
--- /dev/null
@@ -0,0 +1,14 @@
+; UZI mnemonics for memory addresses etc
+
+U_DATA                      .equ 0x010       ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE           .equ 0x200       ; 512 bytes.
+
+U_DATA_STASH               .equ 0xFD00      ; FD00-FEFF
+                                            ; FF00-FFFF is the high stubs
+                                            
+
+PROGBASE                   .equ 0x0000
+PROGLOAD                   .equ 0x0100
+
+Z80_TYPE                   .equ 1
+
diff --git a/Kernel/platform-sam/main.c b/Kernel/platform-sam/main.c
new file mode 100644 (file)
index 0000000..4aaac46
--- /dev/null
@@ -0,0 +1,50 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+
+uint16_t ramtop = PROGTOP;
+uint8_t need_resched;
+
+/* On idle we spin checking for the terminals. Gives us more responsiveness
+   for the polled ports */
+void platform_idle(void)
+{
+    __asm
+    halt
+    __endasm;
+}
+
+void do_beep(void)
+{
+}
+
+void platform_interrupt(void)
+{
+  tty_interrupt();
+  kbd_interrupt();
+  timer_interrupt();
+}
+
+void map_init(void)
+{
+}
+
+void platform_discard(void)
+{
+}
+
+void pagemap_init(void)
+{
+ pagemap_add(0x63);    /* Mode 3, U64K low 32K mapped as low 32K */
+ pagemap_add(0x73);    /* Mode 3, U64K high 32K mapped as low 32K */
+}
+
+
+uint8_t platform_param(char *p)
+{
+    used(p);
+    return 0;
+}
+
diff --git a/Kernel/platform-sam/rules.mk b/Kernel/platform-sam/rules.mk
new file mode 100644 (file)
index 0000000..c3701b0
--- /dev/null
@@ -0,0 +1 @@
+export BANKED=-thunked
diff --git a/Kernel/platform-sam/sam.s b/Kernel/platform-sam/sam.s
new file mode 100644 (file)
index 0000000..a502c7f
--- /dev/null
@@ -0,0 +1,365 @@
+;
+;          SAM Coupe initial hardware support
+;
+
+            .module sam
+
+            ; exported symbols
+            .globl init_early
+            .globl init_hardware
+            .globl interrupt_handler
+            .globl _program_vectors
+           .globl _kernel_flag
+           .globl map_page_low
+           .globl map_kernel_low
+           .globl map_user_low
+           .globl map_save_low
+           .globl map_restore_low
+           .globl map_video
+           .globl unmap_video
+           .globl _platform_doexec
+           .globl _platform_reboot
+           .globl _platform_copier_l
+           .globl _platform_copier_h
+
+            ; exported debugging tools
+            .globl _platform_monitor
+            .globl outchar
+
+            ; imported symbols
+            .globl _ramsize
+            .globl _procmem
+            .globl istack_top
+            .globl istack_switched_sp
+           .globl kstack_top
+            .globl unix_syscall_entry
+            .globl outcharhex
+           .globl _keyin
+
+           .globl s__COMMONMEM
+           .globl l__COMMONMEM
+
+            .include "kernel.def"
+            .include "../kernel.def"
+
+KERNEL_HIGH    .equ    0
+KERNEL_LOW     .equ    2               ; 0/1 high 2/3 low
+VIDEO_LOW      .equ    4
+
+; -----------------------------------------------------------------------------
+;
+;      Because of the 32K split our memory model is a bit different to the
+;      usual Z80 setup.
+;
+; -----------------------------------------------------------------------------
+            .area _COMMONMEM
+
+_platform_monitor:
+           di
+           halt                        ; NMI button will cause a reboot..
+_platform_reboot:
+           xor a
+           out (250), a                ; ROM appears low
+           rst 0                       ; bang
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (below common, only accessible when the kernel is mapped)
+; -----------------------------------------------------------------------------
+            .area _DISCARD
+
+; colours  - FIXME correct these
+           .db 0,32,64,120
+clutmap:
+init_early:
+           ld a, #0x44         ; Kernel is in 0/1 2/3, so video goes above it
+                               ; Video mode 3 (with luck)
+           out (252), a
+           ld a, #0x10         ; black border, mic 1
+           out (254), a
+           in a, (251)
+           and #0x9F           ; low palette colours
+           ld hl, #clutmap
+           ld bc, #4*256 + 248 ; 4 colours, port 248
+           otdr
+            ret
+
+init_hardware:
+            ; set system RAM size (FIXME - check for 512/1M)
+            ld hl, #256
+            ld (_ramsize), hl
+            ld hl, #(256-64)           ; 64K for kernel
+            ld (_procmem), hl
+
+            ; set up interrupt vectors for the kernel (also sets up common memory in page 0x000F which is unused)
+            ld hl, #0
+            push hl
+            call _program_vectors
+            pop hl
+
+            im 1 ; set CPU interrupt mode
+
+           ; interrupt mask
+           ; 60Hz timer on
+
+            ret
+
+           .area _HIGH
+
+_kernel_flag:
+           .db 1       ; We start in kernel mode
+low_map:
+           .db KERNEL_LOW
+low_save:
+           .db 0
+video_save:
+           .db 0
+
+map_save_low:
+           push af
+           ld a,(low_map)
+           ld (low_save),a
+           pop af
+           ; Fall through as we also map the kernel
+map_kernel_low:
+           push af
+           ld a,#KERNEL_LOW
+           ld (low_map),a
+           out (250),a
+           pop af
+           ret
+map_restore_low:
+           ld a, (low_save)
+           jr map_page_low
+map_user_low:
+           ld a,(U_DATA__U_PAGE)
+map_page_low:
+           ld (low_map),a
+           out (250),a
+           ret
+map_video:
+           ld a,(low_map)
+           ld (video_save),a
+           ld a,#VIDEO_LOW
+           jr map_page_low
+unmap_video:
+           ld a,(video_save)
+           jr map_page_low
+
+_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
+
+           ld a,(hl)   ; get the low page
+           call map_page_low
+
+           ; Copy the stub page into place
+            ld hl, #stub_page
+            ld de, #0x0000
+            ld bc, #0x0100
+            ldir
+           jp map_kernel_low
+
+; outchar: Wait for UART TX idle, then print the char in A
+; destroys: AF
+;
+; We use the MIDI port for debug - it's not useful for much else after all.
+;
+outchar:
+           push af
+outcw:     in a,(248)
+           bit 1,a
+           jr nz, outcw
+           pop af
+           out (253),a
+            ret
+;
+;      Keyboard scanner. This is easiest kept in asm space
+;      (reference code from the technical manual)
+;
+_keyscan:
+           ld hl, #_keyin
+           ld b, #0xFE
+keyscl:            ld c, #249                  ; need BC out to get address lines
+           in a, (c)
+           and #0xE0                   ; top 3 bits valid
+           ld d, a
+           ld c, #254
+           in a, (c)
+           and #0x1F                   ; low 5 bits
+           or d
+           ld (hl), a
+           inc hl
+           scf
+           rlc b
+           jr c, keyscl
+           in a, (c)
+           and #0x1F
+           ld (hl), a
+           ret
+
+           .area _PAGE0
+;
+;      Loaded into 00-FF in the bottom of the kernel and then into other
+;      pages. There is an argument for putting most of this at the top of
+;      the high page of everything. That might allow us to run CP/M
+;      emulation
+;
+;      Move what we can into PAGEH and high kernel code
+;
+;      We'd still need some low space but we have 0A-3A/3B-4F/57-5B
+;
+;      We can't deal with NMI in the CP/M case though.
+;
+;      Starts at address 1 to avoid an SDCC flaw
+;
+stub_page:
+
+stub0:     .word 0             ; cp/m emu changes this
+           .byte 0             ; cp/m emu I/O byte
+           .byte 0             ; cp/m emu drive and user
+           jp 0                ; cp/m emu bdos entry point
+rst8:
+_platform_copier_l:
+           ld a,(hl)
+           out (251),a
+           exx
+           ldir
+           exx
+           nop                 ; fall through
+rst10:     ld a,#KERNEL_HIGH
+           out (251),a
+           ret
+           nop
+           nop
+           nop
+rst18:
+_platform_doexec:
+           out (251),a         ; FIXME: trashing of clut high bits
+           ei
+           jp (hl)
+           nop
+rst20:     nop
+           nop
+           nop
+           nop
+           nop
+           nop
+           nop
+           nop
+rst28:     jp syscall_path
+           nop
+           nop
+           nop
+           nop
+           nop
+           nop
+           nop
+rst30:     nop
+           nop
+           nop
+           nop
+           nop
+           nop
+           nop
+           nop
+rst38:     ; Interrupt handling stub
+           push af
+           push de
+           push hl
+           ; FIXME: this trashes any clut overrides
+           ld a,#KERNEL_HIGH
+           out (251),a
+           ; Kernel is now the high map. Our stack may be invalid
+           ; so be careful
+           ld (istack_switched_sp),sp
+           ld sp,#istack_top
+           call interrupt_handler
+           ; On return HL = signal vector E= signal (if any) A = page for
+           ; high
+           ; FIXME  0 = kernel do we need to xlate ?
+           out (251),a
+           ; stack is invalid again now
+           ld sp,(istack_switched_sp)
+           ; back on user stack
+           xor a
+           cp e
+           call nz, sigpath
+           pop hl
+           pop de
+           pop af
+           ei
+           ret
+           nop
+           nop
+           nop
+nmi_handler:           ; FIXME: check ends up at 0066
+           retn
+sigpath:
+           push de             ; signal number
+           ld de,#irqsigret
+           push de             ; clean up
+           jp (hl)
+irqsigret:
+           inc sp              ; drop signal number
+           inc sp
+           ret
+
+syscall_path:
+           push ix
+           ;TODO : BC/DE/HL/IX off stack
+           ; FIXME trashes the clut bits
+           ld a,#KERNEL_HIGH
+           di
+           out (251),a
+           ; Stack now invalid
+           ld (U_DATA__U_SYSCALL_SP),sp
+           ld sp,#kstack_top
+           call unix_syscall_entry
+           ; FIXME check di rules
+           ; On return A is the page (0 means kernel), 
+           ; FIXME: do we need to xlate
+           out (251),a
+           ; stack now invalid
+           ld sp,(U_DATA__U_SYSCALL_SP)
+           xor a
+           cp h
+           call nz, syscall_sigret
+           ; FIXME for now do the grungy C flag HL DE stuff from
+           ; lowlevel-z80 until we fix the ABI
+           ld bc,#0
+           pop ix
+           ret
+syscall_sigret:
+           push hl             ; save errno
+           push de             ; save retval
+           ld l,h
+           ld h,#0
+           push hl             ; signal
+           ld hl,#syscall_sighelp
+           push bc             ; vector
+           ret
+syscall_sighelp:
+           pop de              ; discard signal
+           pop de              ; recover error info
+           pop hl
+           ld h,#0             ; clear signal bit
+           ret
+
+           .area _PAGEH
+;
+;      High stubs. Present in each bank
+;
+_platform_copier_h:
+           ld a,(hl)
+           out (251),a
+           exx
+           ldir
+           exx
+           ld a,#KERNEL_HIGH
+           out (251),a
+           ret
diff --git a/Kernel/platform-sam/sam_vt.s b/Kernel/platform-sam/sam_vt.s
new file mode 100644 (file)
index 0000000..72a509a
--- /dev/null
@@ -0,0 +1,186 @@
+;
+;      Video layer for the SAM. For the moment we use a fairly primitve
+;      set up. We'd really like a mono hi-res mode but you don't get one
+;
+;      Our display consists of 192 lines of 128 bytes (24K) and is page
+;      aligned.
+;
+;      We really want to do 80 column 6bit chars but start simple
+;
+;      Our video is 0 based so we don't add an offset
+;      Must preserve BC
+;
+
+       .module sam_vt
+
+       .area _VIDEO
+
+       .globl _scroll_up
+       .globl _scroll_down
+       .globl _plot_char
+       .globl _clear_lines
+       .globl _clear_across
+
+       .globl _cursor_on
+       .globl _cursor_off
+       .globl _cursor_disable
+
+       .globl _vtattr_notify
+       .globl _vtattr_cap
+
+       .globl map_video
+       .globl unmap_video
+       .globl ___hard_di
+       .globl _fontdata_8x8_exp2
+
+VIDEO_PAGE     .equ    4
+
+_vtattr_cap:
+       .byte   0       ; for now- we can add funky effects later
+
+base_addr:
+       ld a,d          ; save X
+       ld d,e          ; 256 * Y
+       ld e,#0
+       srl d
+       srl e           ; this is DE = E * 128
+       ; A is a char 0-63 - need a byte 0-126
+       ; Aligned so no carry issues
+       add a
+       add e
+       ld e,a
+       ret
+
+;
+;      This is awkward. We can't map the video in 0000-7FFF without
+;      interrupts off, and we can't map it high without confusing the
+;      hell out of everything else. For now just di. 32K/32K splits suck
+;
+_scroll_up:
+       call ___hard_di
+       push af
+       call map_video
+       ld hl,#128
+       ld de,#0
+       ld bc,#24576-128
+ldir_pop:
+       ldir
+pop_unmap:
+       call unmap_video
+       pop af
+       ret c
+       ei
+       ret
+
+_scroll_down:
+       call ___hard_di
+       push af
+       call map_video
+       ld hl,#24576
+       ld de,#24576-128
+       ld b,d
+       ld c,e
+       lddr
+       jr pop_unmap
+
+_plot_char:
+       pop hl                  ; return
+       pop de                  ; H = X L = Y
+       pop bc                  ; C = char
+       push bc
+       push de
+       push hl
+       call ___hard_di
+       push af
+       call base_addr
+       ld h,#0
+       ld l,c
+       add hl,hl               ; char * 16 (expanded 8 x 8)
+       add hl,hl
+       add hl,hl
+       add hl,hl
+       ld bc,#_fontdata_8x8_exp2       ; plus font base
+       add hl,bc
+       call map_video
+       ld b,#20                ; it gets decremented by 4 by the ldi's
+                               ; and once by djnz, and we need to do it
+                               ; four times
+plot_loop:
+       ldi                     ; copy expanded char
+       ldi
+       dec e
+       dec e
+       set 7,e                 ; de += 126
+       ldi                     ; copy second char row
+       ldi
+       dec e                   ; Similar idea
+       dec e                   ; toggle the bit back and gives us
+       res 7,e
+       inc d                   ; de += 126
+       djnz plot_loop
+       jr pop_unmap
+
+_clear_lines:
+       pop hl
+       pop de
+       push de
+       push hl
+       call ___hard_di
+       push af
+       ld a,d                  ; count (0-23)
+       ld d,#0
+       ; Now how much to copy ?
+       ; 128 bytes per scan line, 8 scan lines per char = 1024
+       add a,a                 ; x 2
+       add a,a                 ; x 4
+       ld b,a                  ; x 1024
+       ld c,d                  ; d is 0
+       call base_addr
+       call map_video
+       ld h,d
+       ld e,l
+       inc de
+       dec bc
+       ld (hl),#0
+       jr ldir_pop
+
+_clear_across:
+       pop hl
+       pop de          ; DE = X/Y
+       pop bc          ; C = count
+       push bc
+       push de
+       push hl
+
+       call ___hard_di
+       push af
+
+       call base_addr
+       call map_video
+
+       xor a
+       ld l,#4 ; 4 line pairs
+       ld h,e  ; save offset start
+wipe_line:
+       ld b,c
+wipe_rowpair:
+       ld (de),a
+       set 7,e
+       ld (de),a
+       res 7,e
+       inc de
+       djnz wipe_rowpair
+       ld e,h  ; restore offset. As we are 256 byte aligned we don't need
+       inc d   ; to worry about carries just restore low, inc high
+       dec l
+       jr nz, wipe_line
+       jp pop_unmap
+
+;
+;      TODO
+;
+_cursor_on:
+_cursor_off:
+_cursor_disable:
+_vtattr_notify:
+       ret
diff --git a/Kernel/platform-sam/target.mk b/Kernel/platform-sam/target.mk
new file mode 100644 (file)
index 0000000..3bffcde
--- /dev/null
@@ -0,0 +1 @@
+export CPU = z80
diff --git a/Kernel/platform-sam/tricks.s b/Kernel/platform-sam/tricks.s
new file mode 100644 (file)
index 0000000..dbe2770
--- /dev/null
@@ -0,0 +1,239 @@
+
+       .include "../kernel.def"
+       .include "kernel.def"
+
+;FIXME .include "../lib/z80fixedbank.s"
+
+       .module tricks32
+
+       .globl _ptab_alloc
+       .globl _newproc
+       .globl _chksigs
+       .globl _getproc
+       .globl _runticks
+       .globl _platform_monitor
+
+       .globl _platform_switchout
+       .globl _switchin
+       .globl _dofork
+
+       .globl map_page_low
+       .globl map_kernel_low
+
+       .globl outhl
+       .globl outstring
+
+       .globl _platform_copier_l
+       .globl _platform_copier_h
+
+       .area _HIGH
+
+;
+;      Switch out this process for another task. All the optimisation
+;      tricks have been done by switchout, so when we are called we
+;      really need to switch.
+;
+_platform_switchout:
+       di
+       ld hl,#0
+       push hl         ; stack a retcode of 0 (see fork)
+       push ix         ; save registers
+       push iy
+       ld (U_DATA__U_SP), sp
+
+       ; Map the to
+       ld a,(U_DATA__U_PAGE2)
+       call map_page_low
+       ld hl,#U_DATA
+       ld de,#U_DATA_STASH-0x8000
+       ld bc,#U_DATA__TOTALSIZE
+       ldir
+       call map_kernel_low
+       ; switch the new process in
+       call _getproc
+       push hl
+       call _switchin
+       ; Should never be hit
+       call _platform_monitor
+
+_switchin:
+       di
+       pop bc  ; return address (we never do)
+       pop de  ; new process pointer
+
+       ld hl,#P_TAB__P_PAGE2_OFFSET
+       add hl,de
+       ld a,(hl)       ; our high page
+
+       ; FIXME: add swap support
+
+       ld hl,(U_DATA__U_PTAB)
+       or a
+       sbc hl,de
+       jr z, skip_copyback
+
+       call map_page_low
+
+       ; We are going to write our stack directly under us. No calls for
+       ; a moment
+       exx
+       ld hl,#U_DATA_STASH
+       ld de,#U_DATA
+       ld bc,#U_DATA__TOTALSIZE
+       ldir
+       exx
+       ld sp, (U_DATA__U_SP)
+
+       ; Ok our stack is now valid
+
+       call map_kernel_low
+       ld hl, (U_DATA__U_PTAB)
+       or a
+       sbc hl,de
+       jr nz, switchin_failed  ; invalid u_data
+
+skip_copyback:
+       ld sp, (U_DATA__U_SP)
+       ld ix, (U_DATA__U_PTAB)
+       ld P_TAB__P_STATUS_OFFSET(ix), #P_RUNNING
+       ld a, P_TAB__P_PAGE_OFFSET(ix)
+       ld (U_DATA__U_PAGE),a
+       ld a, P_TAB__P_PAGE2_OFFSET(ix)
+       ld (U_DATA__U_PAGE2),a
+       ld hl,#0
+       ld (_runticks), hl
+       ; Recover IX and IY, return value
+       pop ix
+       pop iy
+       pop hl
+
+       ; if we pre-empted in an ISR IRQ's stay off, if not they get enabled
+       ld a, (U_DATA__U_ININTERRUPT)
+       or a
+       ret nz
+       ei
+       ret
+
+switchin_failed:
+       call outhl
+       ld hl, #bad_switch
+       call outstring
+       jp _platform_monitor
+
+fork_proc_ptr:
+       .dw 0
+
+bad_switch:
+       .asciz '!SWITCH'
+
+_dofork:
+       di
+       pop de
+       pop hl          ; HL is the new process ptab pointer
+       push hl
+       push de
+
+       ld (fork_proc_ptr),hl
+
+       ld de,#P_TAB__P_PID_OFFSET
+       add hl,de
+       ld a,(hl)
+       inc hl
+       ld h,(hl)
+       ld l,a
+
+       ; Build a parent frame for switchin that has the child pid
+       ; on it
+       push hl
+       push ix
+       push iy
+
+       ld (U_DATA__U_SP), sp
+
+       ; Parent state built and in u_data
+
+       call copy_process
+
+       ; Low page is undefined at this point which is ok as we are
+       ; about to change it again
+
+       ; Now put the u_data into the stash for the parent, then we can
+       ; modify it for the child
+
+       ; Map parent
+       ld a,(U_DATA__U_PAGE)
+       call map_page_low
+       ; Copy udata
+       ld hl, #U_DATA
+       ld de, #U_DATA_STASH - 0x8000
+       ld bc, #U_DATA__TOTALSIZE
+       ldir
+       ; Kernel back
+       call map_kernel_low     
+
+       pop bc                  ; discard stack frame we built
+       pop bc
+       pop bc
+
+       ; Back into the kernel to finish the process creation
+
+       ld hl,(fork_proc_ptr)
+       push hl
+       call _newproc
+       pop bc
+
+       ; Clear runticks, and return 0
+       ld hl,#0
+       ld (_runticks),hl
+       ret
+
+;
+;      Process copying is tricky. We want to do a direct copy for speed
+;      but that requires mapping both banks and means
+;      1. We can't put all of this routine in a common helper when we make one
+;      2. We need interrupts off for now
+;      3. We need a magic helper in the low page
+;
+;      HL is the child ptab, udata is the parent
+;
+;      The platform needs to provide a platform_copier method for each
+;      bank that copies low to high of all process, stubs and user data
+;      in that bank (basically a straight 32K copy)
+;
+;      typically that would be
+;
+;      ld a,(hl)
+;      out (whatever),a
+;      exx
+;      ldir
+;      exx
+;      ld a,#kernel
+;      out (whatever),a
+;      ret
+;
+;      There have to be two of these - one in high stubs one in low because
+;      the high banks will both be mapped so the low one isn't available
+;
+copy_process:
+       ld de,#P_TAB__P_PAGE2_OFFSET
+       add hl,de
+       ld a,(U_DATA__U_PAGE)
+       call map_page_low
+       call setup_platform_copier
+       call _platform_copier_l
+       inc hl
+       inc hl
+       ld a,(U_DATA__U_PAGE2)
+       call map_page_low
+       call setup_platform_copier
+       call _platform_copier_h
+       ret
+
+setup_platform_copier:
+       exx
+       ld hl,#0
+       ld de,#0x8000
+       ld b,d
+       ld c,e
+       exx
+       ret
\ No newline at end of file