socz80: Add the target
authorAlan Cox <alan@etchedpixels.co.uk>
Fri, 31 Oct 2014 16:26:42 +0000 (16:26 +0000)
committerAlan Cox <alan@etchedpixels.co.uk>
Fri, 31 Oct 2014 16:26:42 +0000 (16:26 +0000)
This code isn't yet complete. It's had a first pass conversion to the revised
kernel interfaces but the MMU logic has not yet been wired up to bank16 or
converted to the new format API.

21 files changed:
Kernel/platform-socz80/Makefile [new file with mode: 0644]
Kernel/platform-socz80/commonmem.s [new file with mode: 0644]
Kernel/platform-socz80/config.h [new file with mode: 0644]
Kernel/platform-socz80/crt0.s [new file with mode: 0644]
Kernel/platform-socz80/devices.c [new file with mode: 0644]
Kernel/platform-socz80/devlpr.c [new file with mode: 0644]
Kernel/platform-socz80/devlpr.h [new file with mode: 0644]
Kernel/platform-socz80/devrd.c [new file with mode: 0644]
Kernel/platform-socz80/devrd.h [new file with mode: 0644]
Kernel/platform-socz80/devrd_hw.s [new file with mode: 0644]
Kernel/platform-socz80/devsd.c [new file with mode: 0644]
Kernel/platform-socz80/devsd.h [new file with mode: 0644]
Kernel/platform-socz80/devsd_hw.s [new file with mode: 0644]
Kernel/platform-socz80/devtty.c [new file with mode: 0644]
Kernel/platform-socz80/devtty.h [new file with mode: 0644]
Kernel/platform-socz80/kernel.def [new file with mode: 0644]
Kernel/platform-socz80/main.c [new file with mode: 0644]
Kernel/platform-socz80/socz80.def [new file with mode: 0644]
Kernel/platform-socz80/socz80.s [new file with mode: 0644]
Kernel/platform-socz80/tricks.s [new file with mode: 0644]
Kernel/platform-socz80/usermem.s [new file with mode: 0644]

diff --git a/Kernel/platform-socz80/Makefile b/Kernel/platform-socz80/Makefile
new file mode 100644 (file)
index 0000000..b5047a5
--- /dev/null
@@ -0,0 +1,25 @@
+
+CSRCS = devlpr.c devtty.c devsd.c devrd.c
+CSRCS += devices.c main.c
+
+ASRCS = crt0.s devsd_hw.s devrd_hw.s socz80.s
+ASRCS += tricks.s usermem.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-socz80/commonmem.s b/Kernel/platform-socz80/commonmem.s
new file mode 100644 (file)
index 0000000..42606b3
--- /dev/null
@@ -0,0 +1,134 @@
+; 2013-12-18 William R Sowerbutts
+
+        .module commonmem
+
+        ; exported symbols
+        .globl _ub
+        .globl _udata
+        .globl kstack_top
+        .globl istack_top
+        .globl istack_switched_sp
+
+        .include "../kernel.def"
+        .include "kernel.def"
+
+        .area _COMMONMEM
+
+        ; common memory is 4KB starting at 0xF000 to top of CPU address space 
+        ; first 2KB is actually process memory and is not used by the kernel, but
+        ; it's convenient to have it zeroed out. The MMU switches in 4KB units so
+        ; this is still shared between userspace and kernel.
+
+        ; 2KB of zeroes
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+
+        ; another 256 bytes of zeroes
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+
+        ; ------------------------------------------------------------------------------------------------
+        ; 0xF900: start of common memory
+        ; ------------------------------------------------------------------------------------------------
+
+_ub:    ; first 512 bytes: starts with struct u_block, with the kernel stack working down from above
+_udata:
+kstack_base:
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+kstack_top:
+
+        ; next 256 bytes: 254 byte interrupt stack, then 2 byte saved stack pointer
+istack_base:
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+        .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+istack_top:
+istack_switched_sp: .dw 0
diff --git a/Kernel/platform-socz80/config.h b/Kernel/platform-socz80/config.h
new file mode 100644 (file)
index 0000000..2cd4d04
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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
+/* CP/M emulation */
+#define CONFIG_CPM_EMU
+/* We use flexible 16K banks so use the helper */
+#define CONFIG_BANK16
+#define MAX_MAPS 128
+
+#define CONFIG_BANKS   4       /* For now lets use 16K banking */
+#define TICKSPERSEC 100   /* Ticks per second */
+#define PROGBASE    ((char *)(0x0100))  /* also data base */
+#define PROGTOP     ((char *)(0xF900))  /* Top of program, base of U_DATA */
+
+#define BOOT_TTY 37       /* Set this to default device for stdio, stderr */
+                          /* In this case, the default is the first TTY device */
+
+/* We borrow the CP/M command line */
+#define CMDLINE        0x81
+
+/* Device parameters */
+#define NUM_DEV_TTY (512 + 2)
+#define NUM_DEV_SD 28
+#define NUM_DEV_RD 4
+
+#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-socz80/crt0.s b/Kernel/platform-socz80/crt0.s
new file mode 100644 (file)
index 0000000..fbbf7a6
--- /dev/null
@@ -0,0 +1,101 @@
+; 2013-12-18 William R Sowerbutts
+
+        .module crt0
+
+        ; Ordering of segments for the linker.
+        ; WRS: Note we list all our segments here, even though
+        ; we don't use them all, because their ordering is set
+        ; when they are first seen.
+        .area _CODE
+        .area _CODE2
+       .area _DISCARD
+        .area _CONST
+        .area _DATA
+        .area _INITIALIZED
+        .area _BSEG
+        .area _BSS
+        .area _HEAP
+        ; note that areas below here may be overwritten by the heap at runtime, so
+        ; put initialisation stuff in here
+        .area _INITIALIZER
+        .area _GSINIT
+        .area _GSFINAL
+        .area _COMMONMEM
+
+        ; imported symbols
+        .globl _fuzix_main
+        .globl init_early
+        .globl init_hardware
+        .globl l__INITIALIZER
+        .globl s__INITIALIZED
+        .globl s__INITIALIZER
+        .globl s__BSS
+        .globl l__BSS
+        .globl s__DATA
+        .globl l__DATA
+        .globl kstack_top
+        .globl _trap_monitor
+
+        ; startup code
+        .area _CODE
+init:
+        di
+        ld sp, #kstack_top
+
+        ; Configure memory map
+        call init_early
+    
+        ; Initialise global variables, heap, etc
+        call gsinit
+
+        ; 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
+
+        ; define function to copy _INITIALIZER to _INITIALIZED
+        .area _GSINIT
+gsinit::
+        ld bc, #l__INITIALIZER
+        ld a, b
+        or a, c
+        jr Z, gsinit_next
+        ld de, #s__INITIALIZED
+        ld hl, #s__INITIALIZER
+        ldir
+gsinit_next:
+        ; other module's code is appended here
+
+        .area _GSFINAL
+        ; we clear _DATA and _BSS
+        ld bc, #l__BSS
+        ld de, #s__BSS
+        call zeroarea
+        ld bc, #l__DATA
+        ld de, #s__DATA
+        call zeroarea
+        ret
+
+zeroarea:
+        ; zero the memory at (DE) for BC bytes
+        ; check BC!=0
+        ld a, b
+        or c
+        ret z
+        push de
+        pop hl
+        ld (hl), #0 ; place initial 0
+        inc de
+        dec bc
+        ; check BC!=0
+        ld a, b
+        or c
+        ret z
+        ldir
+        ret
diff --git a/Kernel/platform-socz80/devices.c b/Kernel/platform-socz80/devices.c
new file mode 100644 (file)
index 0000000..76d715e
--- /dev/null
@@ -0,0 +1,41 @@
+#include <kernel.h>
+#include <tty.h>
+#include <version.h>
+#include <kdata.h>
+#include <devsd.h>
+#include <devrd.h>
+#include <devsys.h>
+#include <devlpr.h>
+#include <devtty.h>
+
+struct devsw dev_tab[] =  /* The device driver switch table */
+{
+// minor    open         close        read      write       ioctl
+// -----------------------------------------------------------------
+  /* 0: /dev/sd                SD disc block devices  */
+  {  sd_open,     no_close,    sd_read,   sd_write,   no_ioctl },
+  /* 1: /dev/hd                Hard disc block devices (RAMdisc) */
+  {  rd_open,     no_close,    rd_read,   rd_write,   no_ioctl },
+  /* 2: /dev/tty       TTY devices */
+  {  tty_open,     tty_close,   tty_read,  tty_write,  tty_ioctl },
+  /* 3: /dev/lpr       Printer devices */
+  {  lpr_open,     lpr_close,   no_rdwr,   lpr_write,  no_ioctl  },
+  /* 4: /dev/mem etc   System devices (one offs) */
+  {  no_open,      no_close,    sys_read, sys_write, sys_ioctl  },
+  /* Pack to 7 with nxio if adding private devices and start at 8 */
+};
+
+bool validdev(uint16_t dev)
+{
+    /* This is a bit uglier than needed but the right hand side is
+       a constant this way */
+    if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) + 255)
+       return false;
+    else
+        return true;
+}
+
+void device_init(void)
+{
+  sd_init();
+}
diff --git a/Kernel/platform-socz80/devlpr.c b/Kernel/platform-socz80/devlpr.c
new file mode 100644 (file)
index 0000000..0e125a7
--- /dev/null
@@ -0,0 +1,32 @@
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <devlpr.h>
+
+int lpr_open(uint8_t minor, uint16_t flag)
+{
+    minor; flag; // shut up compiler
+    udata.u_error = ENODEV;
+    return (-1);
+}
+
+int lpr_close(uint8_t minor)
+{
+    minor; // shut up compiler
+    udata.u_error = ENODEV;
+    return (-1);
+}
+
+int lpr_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    minor; rawflag; flag; // shut up compiler
+    udata.u_error = EINVAL;
+    return (-1);
+}
+
+int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    minor; rawflag; flag; // shut up compiler
+    udata.u_error = ENODEV;
+    return (-1);
+}
diff --git a/Kernel/platform-socz80/devlpr.h b/Kernel/platform-socz80/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-socz80/devrd.c b/Kernel/platform-socz80/devrd.c
new file mode 100644 (file)
index 0000000..a0fe303
--- /dev/null
@@ -0,0 +1,98 @@
+/* socz80 RAM disk driver 
+ *
+ * Implements two RAM disks in the top 4MB of RAM, starting at 4MB and 6MB.
+ * Each disk is 2MB in size.
+ *
+ * */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+
+volatile char   *rd_dptr;          /* pointer to data buffer */
+volatile int     rd_dlen;          /* data transfer length */
+volatile uint16_t  rd_address;     /* disk address (in 256-byte chunks) */
+
+int ramdisk_transfer(bool is_read, uint8_t minor, uint8_t rawflag);
+int ramdisk_read(void); // assembler
+int ramdisk_write(void); // assembler
+
+int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    flag;
+    return ramdisk_transfer(true, minor, rawflag);
+}
+
+int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    flag;
+    return ramdisk_transfer(false, minor, rawflag);
+}
+
+int ramdisk_transfer(bool is_read, uint8_t minor, uint8_t rawflag)
+{
+    blkno_t block;
+    int block_xfer;     /* r/w return value (number of 512 byte blocks transferred) */
+    char *dptr;
+    int dlen;
+    uint16_t addr;
+
+    if(rawflag){
+        dlen = udata.u_count;
+        dptr = udata.u_base;
+        block = udata.u_offset >> 9;
+        block_xfer = dlen >> 9;
+    }else{ /* rawflag == 0 */
+        dlen = 512;
+        dptr = udata.u_buf->bf_data;
+        block = udata.u_buf->bf_blk;
+        block_xfer = 1;
+    }
+
+    if(block > (2 * 1024 * 2)){ /* > 2MB? */
+        udata.u_error = EIO;
+        return -1;
+    }
+
+    switch(minor){
+        case 0: 
+            block += (4 * 1024 * 2); /* rd0 at 4MB */ 
+            break;
+        case 1: 
+            block += (6 * 1024 * 2); /* rd1 at 6MB */
+            break;
+        default:
+            udata.u_error = ENXIO;
+            return -1;
+    }
+
+    /* compute address in 256-byte chunks */
+    addr = block << 1;
+
+    // kprintf("ramdisk_transfer(%s, %d, block=0x%04x, dptr=0x%04x, dlen=0x%04x)",
+    //         is_read?"read":"write", minor, block, dptr, dlen);
+
+    /* FIXME Should be able to avoid the __critical once bank switching is fixed */
+    __critical {
+        rd_dlen = dlen;
+        rd_dptr = dptr;
+        rd_address = addr;
+        if(is_read)
+            ramdisk_read();
+        else
+            ramdisk_write();
+    }
+
+    return block_xfer;
+}
+
+
+int rd_open(uint8_t minor)
+{
+    if(minor < NUM_DEV_RD){
+        return 0;
+    } else {
+        udata.u_error = EIO;
+        return -1;
+    }
+}
diff --git a/Kernel/platform-socz80/devrd.h b/Kernel/platform-socz80/devrd.h
new file mode 100644 (file)
index 0000000..e54a154
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __DEVRD_DOT_H__
+#define __DEVRD_DOT_H__
+
+/* public interface */
+int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int rd_init(void);
+int rd_open(uint8_t minor, uint16_t flag);
+
+#endif /* __DEVRD_DOT_H__ */
diff --git a/Kernel/platform-socz80/devrd_hw.s b/Kernel/platform-socz80/devrd_hw.s
new file mode 100644 (file)
index 0000000..b3494f0
--- /dev/null
@@ -0,0 +1,42 @@
+            .module devrd_hw
+
+            ; imported symbols
+            .globl _rd_dptr, _rd_dlen, _rd_address             ; from devrd.c
+            .globl page17in, page17out                         ; from socz80.s
+
+            ; exported symbols (used by devrd.c)
+            .globl _ramdisk_read
+            .globl _ramdisk_write
+
+            .include "socz80.def"
+
+            .area _CODE
+
+_ramdisk_read:
+            call ramdisk_setup
+            call page17in
+            ret
+
+_ramdisk_write:
+            call ramdisk_setup
+            call page17out
+            ret
+
+
+; load MMU page17 to point to start of sector on RAM disk
+; load DE with byte count
+; load HL with destination pointer
+ramdisk_setup:
+            ld hl, (_rd_address)
+            ld a, #MMU_SELECT_PAGE17
+            out (MMU_SELECT), a
+            xor a
+            out (MMU_PTR_VAL0), a
+            out (MMU_PTR_VAL3), a
+            ld a, h
+            out (MMU_PTR_VAL1), a
+            ld a, l
+            out (MMU_PTR_VAL2), a
+            ld de, (_rd_dlen)
+            ld hl, (_rd_dptr)
+            ret
diff --git a/Kernel/platform-socz80/devsd.c b/Kernel/platform-socz80/devsd.c
new file mode 100644 (file)
index 0000000..cec8f91
--- /dev/null
@@ -0,0 +1,395 @@
+/*-----------------------------------------------------------------------*/
+/* socz80 SD card driver                                                 */
+/* Based on:                                                             */
+/*   MMCv3/SDv1/SDv2 (in SPI mode) control module  (C)ChaN, 2007         */
+/*  (from http://www.cl.cam.ac.uk/teaching/1011/P31/lib/diskio.c)        */
+/* and http://elm-chan.org/docs/mmc/mmc_e.html                           */
+/*-----------------------------------------------------------------------*/
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <timer.h>
+#include <devsd.h>
+#include <stdbool.h>
+
+/* keep track of current card type, UZI partition offset */
+static int sd_card_type;
+static unsigned long sd_first_uzi_sector;
+
+/* used to track current transfer in progress */
+static int sd_blockdev_count;
+static char *sd_dptr;
+static int sd_dlen;
+static uint16_t sd_blocks;
+static unsigned long sd_next_block;
+
+void sd_setup(uint8_t minor, uint8_t rawflag)
+{
+    blkno_t block;
+
+    if(rawflag){
+        sd_dlen = udata.u_count;
+        sd_dptr = udata.u_base;
+        block = udata.u_offset >> 9;
+        sd_blocks = sd_dlen >> 9;
+    }else{
+        sd_dlen = 512;
+        sd_dptr = udata.u_buf->bf_data;
+        block = udata.u_buf->bf_blk;
+        sd_blocks = 1;
+    }
+
+    if(sd_blocks != 1)
+        panic("sd: unexpected block count");
+
+    sd_next_block = sd_first_uzi_sector + /* start of our partition */
+        (((unsigned long)minor) << UZI_BLOCKDEV_SIZE_LOG2_SECTORS) + /* start of this minor device */
+        ((unsigned long)block); /* requested sector */
+}
+
+int sd_open(uint8_t minor, uint16_t flag)
+{
+    flag;
+    if(minor < sd_blockdev_count){
+        return 0;
+    } else {
+        udata.u_error = EIO;
+        return -1;
+    }
+}
+
+int sd_readwrite(uint8_t minor, uint8_t rawflag, bool do_write)
+{
+    int attempt;
+    for(attempt=0; attempt<5; attempt++){
+        sd_setup(minor, rawflag);
+        if(do_write){
+            if(sd_write_sector(sd_dptr, sd_next_block))
+                return sd_blocks;
+        }else{
+            if(sd_read_sector(sd_dptr, sd_next_block))
+                return sd_blocks;
+        }
+        kputs("sd: failed, resetting.\n");
+        if(sd_init())
+            break;
+    }
+    return -1;
+}
+
+int sd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    flag;
+    return sd_readwrite(minor, rawflag, false);
+}
+
+int sd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+    flag;
+    return sd_readwrite(minor, rawflag, true);
+}
+
+static const char cardname[] = "     MMC\0\0SDv1\0?>???SDv2\0";
+
+/* Stop sdcc inlining a ton of crap each time */
+static unsigned long shift11(unsigned long p)
+{
+    return (p >> 11);
+}
+
+void sd_print_partition(unsigned char *p)
+{
+    long size;
+    if(!*p)
+        return;
+        
+    size = *(unsigned long *)(p + 4);
+    
+    kprintf("sd: partition type 0x%x, offset %uMB, length %uMB (%s)\n",
+             *p,
+             (unsigned int)shift11(*(unsigned long *)(p + 4)),
+             (unsigned int)shift11(size),
+             *p != UZI_PARTITION_TYPE ? "ignored" : 
+             (sd_blockdev_count == 0 ? "UZI" : "unused"));
+
+    /* We will use the first partition with the appropriate type */
+    if(*p == UZI_PARTITION_TYPE && sd_blockdev_count == 0){
+        sd_first_uzi_sector = *((unsigned long*)&p[4]);
+        /* 16 bit shift */
+        sd_blockdev_count = size >> UZI_BLOCKDEV_SIZE_LOG2_SECTORS;
+        /* Allow a smaller than 32MB final 'device' */
+        if (size & 0xFFFF)
+            sd_blockdev_count++;
+    }
+}
+
+int sd_init(void)
+{
+    int n;
+    unsigned long sector_count;
+    unsigned char *sector;
+    unsigned char *p; /* into sector */
+
+    sd_first_uzi_sector = 0;
+    sd_blockdev_count = 0;
+
+    kputs("sd: Probing ... ");
+
+    if (!(sd_card_type = sd_spi_init())) {
+        kputs("No card found\n");
+        return -1;
+    }
+    
+    /* read and compute card size */
+    sector_count = sd_get_size_sectors();
+    if(!sector_count){
+        kputs("Weird card\n");
+        return -1;
+    }
+    n = shift11(sector_count);
+
+    kprintf("Found %s card (%dMB, b%s addressed)\n", 
+                cardname + 5 * (sd_card_type & ~CT_BLOCK),
+                n,
+                (sd_card_type & CT_BLOCK) ? "lock" : "yte");
+
+    /* read partition table, locate the UZI partition */
+    sector = (unsigned char*)tmpbuf();
+    if(!sd_read_sector(sector, 0)){
+        kputs("sd: Failed to read partition table\n");
+    }else{
+        p = sector + 510;
+        if(*p != 0x55 || p[1] != 0xAA){ /* check for presence of MBR boot signature */
+            kputs("sd: Cannot find MBR partition table\n");
+        }else{
+            p = sector + 0x1BE + 4;
+            for(n = 4; n > 0; n--) {
+                sd_print_partition(p);
+                p+=16;
+            }
+            if(sd_blockdev_count == 0){
+                kprintf("sd: No UZI partition (type 0x%x) found\n", UZI_PARTITION_TYPE);
+            }
+        }
+    }
+
+    if(sd_blockdev_count > NUM_DEV_SD)
+        sd_blockdev_count = NUM_DEV_SD;
+
+    if(sd_blockdev_count)
+        kprintf("sd: %d block devices, max 32MB each\n", sd_blockdev_count);
+
+    brelse((bufptr)sector);
+
+    return 0; /* success */
+}
+
+int sd_spi_init(void)
+{
+    unsigned char n, cmd, card_type, ocr[4];
+    timer_t timer;
+
+    sd_spi_mode0();
+    sd_spi_raise_cs();
+
+    sd_spi_clock(255); /* 250kHz */
+    for (n = 20; n; n--)
+        sd_spi_receive_byte(); /* 160 dummy clocks */
+
+    card_type = 0;
+    /* Enter Idle state */
+    if (sd_send_command(CMD0, 0) == 1) {
+        /* initialisation timeout 1 second */
+        timer = set_timer_duration(TICKSPERSEC);
+        if (sd_send_command(CMD8, (uint32_t)0x1AA) == 1) {    /* SDHC */
+            /* Get trailing return value of R7 resp */
+            for (n = 0; n < 4; n++) ocr[n] = sd_spi_receive_byte();
+            /* The card can work at vdd range of 2.7-3.6V */
+            if (ocr[2] == 0x01 && ocr[3] == 0xAA) {
+                /* Wait for leaving idle state (ACMD41 with HCS bit) */
+                while(!timer_expired(timer) && sd_send_command(ACMD41, (uint32_t)1 << 30));
+                /* Check CCS bit in the OCR */
+                if (!timer_expired(timer) && sd_send_command(CMD58, 0) == 0) {
+                    for (n = 0; n < 4; n++) ocr[n] = sd_spi_receive_byte();
+                    card_type = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;   /* SDv2 */
+                }
+            }
+        } else { /* SDSC or MMC */
+            if (sd_send_command(ACMD41, 0) <= 1)    {
+                /* SDv1 */
+                card_type = CT_SD1;
+                cmd = ACMD41;
+            } else {
+                /* MMCv3 */
+                card_type = CT_MMC;
+                cmd = CMD1;
+            }
+            /* Wait for leaving idle state */
+            while(!timer_expired(timer) && sd_send_command(cmd, 0));
+            /* Set R/W block length to 512 */
+            if(timer_expired(timer) || sd_send_command(CMD16, (uint32_t)512) != 0)
+                card_type = 0;
+        }
+    }
+    sd_spi_release();
+
+    if (card_type) {
+        if(card_type == CT_MMC)
+            sd_spi_clock(3); /* 16MHz (can do up to 20MHz with MMC) */
+        else
+            sd_spi_clock(2); /* 21MHz (can do up to 25MHz with SD) */
+        return card_type;
+    }
+
+    return 0; /* failed */
+}
+
+void sd_spi_release(void)
+{
+    sd_spi_raise_cs();
+    sd_spi_receive_byte();
+}
+
+int sd_spi_wait_ready(void)
+{
+    unsigned char res;
+    timer_t timer;
+
+    timer = set_timer_duration(TICKSPERSEC/10); /* 100ms */
+    sd_spi_receive_byte();
+    do{
+        res = sd_spi_receive_byte();
+    }while ((res != 0xFF) && !timer_expired(timer));
+
+    return res;
+}
+
+int sd_spi_transmit_block(void *ptr, int length)
+{
+    unsigned char reply;
+
+    if(sd_spi_wait_ready() != 0xFF)
+        return 0; /* failed */
+
+    sd_spi_transmit_byte(0xFE);
+    sd_spi_transmit_from_memory(ptr, length);
+    sd_spi_transmit_byte(0xFF); /* dummy CRC */
+    sd_spi_transmit_byte(0xFF);
+    reply = sd_spi_receive_byte();
+    if((reply & 0x1f) != 0x05)
+        return 0; /* failed */
+    return 1; /* hooray! */
+}
+
+int sd_spi_receive_block(void *ptr, int length)
+{
+    unsigned int timer;
+    unsigned char b;
+
+    timer = set_timer_duration(TICKSPERSEC/5); /* 200ms */
+
+    do{
+        b = sd_spi_receive_byte();
+    }while(b == 0xFF && !timer_expired(timer));
+    if(b != 0xFE)
+        return 0; /* failed */
+
+    return sd_spi_receive_to_memory(ptr, length); /* returns nonzero */
+}
+
+int sd_send_command(unsigned char cmd, uint32_t arg)
+{
+    unsigned char n, res;
+
+    if (cmd & 0x80) {   /* ACMD<n> is the command sequense of CMD55-CMD<n> */
+        cmd &= 0x7F;
+        res = sd_send_command(CMD55, 0);
+        if (res > 1) 
+            return res;
+    }
+
+    /* Select the card and wait for ready */
+    sd_spi_raise_cs();
+    sd_spi_lower_cs();
+    if (sd_spi_wait_ready() != 0xFF) 
+        return 0xFF;
+
+    /* Send command packet */
+    sd_spi_transmit_byte(cmd);                               /* Start + Command index */
+    sd_spi_transmit_byte((unsigned char)(arg >> 24));        /* Argument[31..24] */
+    sd_spi_transmit_byte((unsigned char)(arg >> 16));        /* Argument[23..16] */
+    sd_spi_transmit_byte((unsigned char)(arg >> 8));         /* Argument[15..8] */
+    sd_spi_transmit_byte((unsigned char)arg);                /* Argument[7..0] */
+    /* there's only a few commands (in native mode) that need correct CRCs */
+    n = 0x01;                                                /* Dummy CRC + Stop */
+    if (cmd == CMD0) n = 0x95;                               /* Valid CRC for CMD0(0) */
+    if (cmd == CMD8) n = 0x87;                               /* Valid CRC for CMD8(0x1AA) */
+    sd_spi_transmit_byte(n);
+
+    /* Receive command response */
+    if (cmd == CMD12) 
+        sd_spi_receive_byte();          /* Skip a stuff byte when stop reading */
+    n = 10;                             /* Wait for a valid response in timeout of 10 attempts */
+    do{
+        res = sd_spi_receive_byte();
+    }while ((res & 0x80) && --n);
+
+    return res;         /* Return with the response value */
+}
+
+unsigned long sd_get_size_sectors(void)
+{
+    unsigned char csd[16], n;
+    unsigned long sectors = 0;
+
+    if(sd_send_command(CMD9, 0) == 0 && sd_spi_receive_block(csd, 16)){
+        if ((csd[0] >> 6) == 1) {      /* SDC ver 2.00 */
+            sectors = (csd[9] + ((unsigned int)csd[8] << 8) + 1) << 10;
+        } else {                                       /* SDC ver 1.XX or MMC*/
+            n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
+            sectors = (csd[8] >> 6) + ((unsigned int)csd[7] << 2) + ((unsigned int)(csd[6] & 3) << 10) + 1;
+            sectors = sectors << (n - 9);
+        }
+    }
+    sd_spi_release();
+    return sectors;
+}
+
+int sd_read_sector(void *ptr, unsigned long lba)
+{
+    int r = 0;
+
+    if(sd_card_type == CT_NONE)
+        return r;
+
+    if(!(sd_card_type & CT_BLOCK))
+        lba = lba << 9; /* multiply by 512 to get byte address */
+
+    if(sd_send_command(CMD17, lba) == 0 && sd_spi_receive_block(ptr, 512)){
+        r = -1;
+    }
+
+    sd_spi_release();
+
+    return r;
+}
+
+int sd_write_sector(void *ptr, unsigned long lba)
+{
+    int r = 0;
+
+    if(sd_card_type == CT_NONE)
+        return r;
+
+    if(!(sd_card_type & CT_BLOCK))
+        lba = lba << 9; /* multiply by 512 to get byte address */
+
+    if(sd_send_command(CMD24, lba) == 0 && sd_spi_transmit_block(ptr, 512)){
+        r = -1;
+    }
+
+    sd_spi_release();
+
+    return r;
+}
diff --git a/Kernel/platform-socz80/devsd.h b/Kernel/platform-socz80/devsd.h
new file mode 100644 (file)
index 0000000..bcf9038
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef __DEVSD_DOT_H__
+#define __DEVSD_DOT_H__
+
+#define UZI_PARTITION_TYPE 0x5A    /* ASCII "Z", Wikipedia suggests this partition type is not widely used */
+#define UZI_BLOCKDEV_SIZE_LOG2_SECTORS 16 /* Each device is 2^16 sectors ie 2^16 * 2^9 = 2^25 = 32MB */
+
+/* public interface */
+int sd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int sd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int sd_init(void);
+int sd_open(uint8_t minor, uint16_t flag);
+
+/* private interface */
+int sd_spi_init(void);
+int sd_read_sector(void *ptr, unsigned long lba);
+int sd_write_sector(void *ptr, unsigned long lba);
+unsigned long sd_get_size_sectors(void);
+
+/* internal functions */
+void sd_spi_clock(int divisor); 
+/* 
+   sd_clock sets SPI bus to 64.0 / (1+divisor) MHz 
+     0=64MHz, 1=32MHz, 2=21.33MHz, 3=16MHz, 4=12.8MHz, 
+     5=10.66MHz, 6=9.14MHz, 7=8MHz, 15=4MHz, etc 
+     255 = 0.25MHz, about right for MMC/SPI initialisation
+*/
+
+void sd_spi_mode0(void);
+void sd_spi_raise_cs(void);
+void sd_spi_lower_cs(void);
+void sd_spi_release(void);
+int sd_spi_wait_ready(void);
+void sd_spi_transmit_byte(unsigned char byte);
+unsigned char sd_spi_receive_byte(void);
+int sd_spi_receive_block(void *ptr, int length); /* waits for card ready then calls sd_spi_receive_to_memory */
+int sd_spi_transmit_block(void *ptr, int length); /* waits for card ready then calls sd_spi_receive_to_memory */
+int sd_spi_receive_to_memory(void *ptr, int length);
+int sd_spi_transmit_from_memory(void *ptr, int length);
+int sd_send_command(unsigned char cmd, uint32_t arg);
+
+/* Definitions for MMC/SDC command */
+#define CMD0    (0x40+0)    /* GO_IDLE_STATE */
+#define CMD1    (0x40+1)    /* SEND_OP_COND (MMC) */
+#define ACMD41  (0xC0+41)   /* SEND_OP_COND (SDC) */
+#define CMD8    (0x40+8)    /* SEND_IF_COND */
+#define CMD9    (0x40+9)    /* SEND_CSD */
+#define CMD10   (0x40+10)   /* SEND_CID */
+#define CMD12   (0x40+12)   /* STOP_TRANSMISSION */
+#define ACMD13  (0xC0+13)   /* SD_STATUS (SDC) */
+#define CMD16   (0x40+16)   /* SET_BLOCKLEN */
+#define CMD17   (0x40+17)   /* READ_SINGLE_BLOCK */
+#define CMD18   (0x40+18)   /* READ_MULTIPLE_BLOCK */
+#define CMD23   (0x40+23)   /* SET_BLOCK_COUNT (MMC) */
+#define ACMD23  (0xC0+23)   /* SET_WR_BLK_ERASE_COUNT (SDC) */
+#define CMD24   (0x40+24)   /* WRITE_BLOCK */
+#define CMD25   (0x40+25)   /* WRITE_MULTIPLE_BLOCK */
+#define CMD55   (0x40+55)   /* APP_CMD */
+#define CMD58   (0x40+58)   /* READ_OCR */
+
+#define CT_NONE 0x00
+#define CT_MMC 0x01
+#define CT_SD1 0x02
+#define CT_SD2 0x04
+#define CT_SDC (CT_SD1|CT_SD2)
+#define CT_BLOCK 0x08
+
+#endif /* __DEVSD_DOT_H__ */
diff --git a/Kernel/platform-socz80/devsd_hw.s b/Kernel/platform-socz80/devsd_hw.s
new file mode 100644 (file)
index 0000000..c21a9be
--- /dev/null
@@ -0,0 +1,103 @@
+; 2013-12-19 William R Sowerbutts
+
+            .module devsd_hw
+
+            ; exported symbols
+            .globl _sd_spi_clock
+            .globl _sd_spi_raise_cs
+            .globl _sd_spi_lower_cs
+            .globl _sd_spi_receive_byte
+            .globl _sd_spi_transmit_byte
+            .globl _sd_spi_receive_to_memory
+            .globl _sd_spi_transmit_from_memory
+           .globl _sd_spi_mode0
+
+            .include "socz80.def"
+
+            .area _CODE
+
+_sd_spi_clock:
+            pop de
+            pop hl
+            push hl
+            push de
+            ld a, l
+            out (SD_SPI_DIVISOR), a
+            ret
+
+_sd_spi_raise_cs:
+            ld a, #0xFF
+            out (SD_SPI_CHIPSELECT), a
+            ret
+
+_sd_spi_lower_cs:
+            ld a, #0xFE
+            out (SD_SPI_CHIPSELECT), a
+            ret
+
+_sd_spi_mode0:
+          xor a
+           out (SD_SPI_MODE), a
+           ret
+
+_sd_spi_receive_byte:
+            ; read a byte
+            ld a, #0xFF
+            out (SD_SPI_TX), a
+            in a, (SD_SPI_RX)
+            ld h, #0
+            ld l, a
+            ret
+
+_sd_spi_transmit_from_memory:
+            pop de  ; return address
+            pop hl  ; memory pointer
+            pop bc  ; byte count
+            push bc ; now put the stack back ...
+            push hl
+            push de
+tnextbyte:
+            ld a, (hl)
+            out (SD_SPI_TX), a
+            inc hl
+            dec bc
+            ld a, b
+            or c
+            jr nz, tnextbyte
+            ; return success in HL
+            ld hl, #1
+            ret
+
+
+_sd_spi_receive_to_memory:
+            pop de  ; return address
+            pop hl  ; memory pointer
+            pop bc  ; byte count
+            push bc ; now put the stack back ...
+            push hl
+            push de
+rnextbyte:
+            ld a, #0xFF
+            out (SD_SPI_TX), a
+            in a, (SD_SPI_RX)
+            ld (hl), a
+            inc hl
+            dec bc
+            ld a, b
+            or c
+            jr nz, rnextbyte
+            ; there's also a 16-bit CRC that we discard
+            call _sd_spi_receive_byte
+            call _sd_spi_receive_byte
+            ; return success in HL
+            ld hl, #1
+            ret
+
+_sd_spi_transmit_byte:
+            pop de
+            pop hl
+            push hl
+            push de
+            ld a, l
+            out (SD_SPI_TX), a
+            ret
diff --git a/Kernel/platform-socz80/devtty.c b/Kernel/platform-socz80/devtty.c
new file mode 100644 (file)
index 0000000..791b8d3
--- /dev/null
@@ -0,0 +1,38 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <tty.h>
+#include <devtty.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 }
+};
+
+/* console helper */
+void kputchar(char c)
+{
+    /* handle CRLF */
+    if(c=='\n')
+        tty_putc(1, '\r');
+    tty_putc(1, c);
+}
+
+/* Called to set baud rate etc */
+void tty_setup(uint8_t minor)
+{
+    minor;
+}
+
+/* For the moment */
+int tty_carrier(uint8_t minor)
+{
+    minor;
+    return 1;
+}
diff --git a/Kernel/platform-socz80/devtty.h b/Kernel/platform-socz80/devtty.h
new file mode 100644 (file)
index 0000000..c387e90
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+
+#endif
diff --git a/Kernel/platform-socz80/kernel.def b/Kernel/platform-socz80/kernel.def
new file mode 100644 (file)
index 0000000..895914c
--- /dev/null
@@ -0,0 +1,6 @@
+; UZI mnemonics for memory addresses etc
+
+U_DATA                      .equ 0xf900       ; (this is struct u_data from kernel.h)
+U_DATA__PAGEOFFSET          .equ 0x0900       ; U_DATA % 0x1000
+U_DATA__TOTALSIZE           .equ 0x300        ; 256+256+256 bytes.
+
diff --git a/Kernel/platform-socz80/main.c b/Kernel/platform-socz80/main.c
new file mode 100644 (file)
index 0000000..a6c7e42
--- /dev/null
@@ -0,0 +1,65 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <tty.h>
+
+uint8_t *ramtop = PROGTOP;
+
+void platform_idle(void)
+{
+    __asm
+    halt
+    __endasm;
+}
+
+__sfr __at 0x00 uart0_status;
+__sfr __at 0x00 uart0_data;
+__sfr __at 0x10 timer_status;
+__sfr __at 0x11 timer_command;
+__sfr __at 0x28 uart1_status;
+__sfr __at 0x29 uart1_data;
+
+void platform_interrupt(void)
+{
+    uint8_t st0 = uart0_status;
+    uint8_t st1 = uart1_status;
+    uint8_t ts = timer_status;
+    uint8_t d;
+    
+    if (ts & 0x80)
+        timer_command = 0;     /* Ack the timer */
+    if (st0 & 0x81) {
+        uart0_status = st0 & 0xFC;
+        if (st0 & 0x80) {      /* RX data */
+            d = uart0_data;
+            tty_inproc(1, d);
+        }
+        if (st0 & 0x01)                /* TX idle */
+            tty_outproc(1);
+    }
+    if (st1 & 0x81) {
+        uart1_status = st1 & 0xFC;
+        if (st0 & 0x80) {      /* RX data */
+            d = uart1_data;
+            tty_inproc(2, d);
+        }
+        if (st1 & 0x01)                /* TX idle */
+            tty_outproc(2);
+    }
+    if (ts & 0x80)
+        timer_interrupt();
+}
+
+void pagemap_init(void)
+{
+    int i;
+    /* 0/1/2 kernel, 3 initial common, 4+ to use */
+    for (i = 4; i < 128 ; i++)
+        pagemap_add(i);
+    /*
+     * The kernel boots with 3 as the common, list it last here so it also
+     * gets given to init as the kernel kicks off the init stub. init will then
+     * exec preserving this common and all forks will be copies from it.
+     */
+    pagemap_add(3);
+}
diff --git a/Kernel/platform-socz80/socz80.def b/Kernel/platform-socz80/socz80.def
new file mode 100644 (file)
index 0000000..bfc693a
--- /dev/null
@@ -0,0 +1,54 @@
+; socZ80 mnemonics for I/O ports etc
+
+UART0_STATUS                .equ 0x00
+UART0_DATA                  .equ 0x01
+
+TIMER_STATUS                .equ 0x10
+TIMER_COMMAND               .equ 0x11
+TIMER_VAL0                  .equ 0x14
+TIMER_VAL1                  .equ 0x15
+TIMER_VAL2                  .equ 0x16
+TIMER_VAL3                  .equ 0x17
+
+PPRO_FLASH_SPI_CHIPSELECT   .equ 0x18
+PPRO_FLASH_SPI_STATUS       .equ 0x19
+PPRO_FLASH_SPI_TX           .equ 0x1A
+PPRO_FLASH_SPI_RX           .equ 0x1B
+PPRO_FLASH_SPI_DIVISOR      .equ 0x1C
+
+SD_SPI_CHIPSELECT           .equ 0x30
+SD_SPI_STATUS               .equ 0x31
+SD_SPI_TX                   .equ 0x32
+SD_SPI_RX                   .equ 0x33
+SD_SPI_DIVISOR              .equ 0x34
+SD_SPI_GPIO                .equ 0x35
+SD_SPI_MODE                .equ 0x36
+
+GPIO_INPUT                  .equ 0x20
+GPIO_OUTPUT                 .equ 0x21
+
+UART1_STATUS                .equ 0x28
+UART1_DATA                  .equ 0x29
+
+CPU_CLKMOD                  .equ 0xF0
+
+MMU_SELECT                  .equ 0xF8
+MMU_PAGE17                  .equ 0xFA
+MMU_PERM                    .equ 0xFB
+MMU_FRAMEHI                 .equ 0xFC
+MMU_FRAMELO                 .equ 0xFD
+MMU_SELECT_PAGE17           .equ 0xFF
+MMU_PTR_VAL0                .equ 0xFC
+MMU_PTR_VAL1                .equ 0xFD
+MMU_PTR_VAL2                .equ 0xFE
+MMU_PTR_VAL3                .equ 0xFF
+
+; timer commands (write to TIMER_COMMAND register)
+TIMCMD_INTACK               .equ 0x00
+TIMCMD_UPRESET              .equ 0x01
+TIMCMD_UPLATCH              .equ 0x02
+TIMCMD_DOWNRESET            .equ 0x03
+TIMCMD_SEL_UPVAL            .equ 0x10
+TIMCMD_SEL_UPLATCH          .equ 0x11
+TIMCMD_SEL_DOWNVAL          .equ 0x12
+TIMCMD_SEL_DOWNRESET        .equ 0x13
diff --git a/Kernel/platform-socz80/socz80.s b/Kernel/platform-socz80/socz80.s
new file mode 100644 (file)
index 0000000..00278cf
--- /dev/null
@@ -0,0 +1,550 @@
+; 2013-12-18 William R Sowerbutts
+; socz80 hardware specific code (no doubt there's lots more of it elsewhere!)
+
+            .module socz80
+
+            ; exported symbols
+            .globl init_early
+            .globl init_hardware
+            .globl page17in
+            .globl page17out
+            .globl interrupt_handler
+            .globl _program_vectors
+            .globl _system_tick_counter
+            .globl _tty_putc
+            .globl _tty_writeready
+            .globl _tty_outproc
+           .globl map_kernel
+           .globl map_process
+
+            ; exported debugging tools
+            .globl _trap_monitor
+            .globl mmu_map_page
+            .globl mmu_map_page_fast
+            .globl map_process
+            .globl mmu_state_dump
+            .globl outchar
+
+            ; imported symbols
+            .globl _ramsize
+            .globl _procmem
+            .globl _inint
+            .globl _tty_inproc
+            .globl istack_top
+            .globl istack_switched_sp
+            .globl dispatch_process_signal
+            .globl unix_syscall_entry
+            .globl trap_illegal
+            .globl _timer_interrupt
+           .globl nmi_handler
+            .globl outnibble
+            .globl outcharhex
+            .globl outhl, outde, outbc
+            .globl outnewline
+            .globl outstring
+            .globl outstringhex
+
+            .include "socz80.def"
+            .include "kernel.def"
+            .include "../kernel.def"
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (0xF000 upwards)
+; -----------------------------------------------------------------------------
+            .area _COMMONMEM
+
+trapmsg:    .ascii "Trapdoor: SP="
+            .db 0
+trapmsg2:   .ascii ", PC="
+            .db 0
+tm_user_sp: .dw 0
+
+tm_stack:
+            .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+tm_stack_top:
+
+_trap_monitor:
+            ; stash SP
+            ld (tm_user_sp), sp
+            ; switch to temporary stack
+            ld sp, #tm_stack_top
+            call outnewline
+            ld hl, #trapmsg
+            call outstring
+            ld hl, (tm_user_sp)
+            call outhl
+            ld hl, #trapmsg2
+            call outstring
+            ld hl, (tm_user_sp)
+            ld e, (hl)
+            inc hl
+            ld d, (hl)
+            call outde
+            call outnewline
+            ; dump MMU
+            call mmu_state_dump
+            ; map ROM
+            xor a
+            out (MMU_SELECT), a
+            ld a, #0x20
+            out (MMU_FRAMEHI), a
+            xor a
+            out (MMU_FRAMELO), a
+            ; jump into to ROM, lovely ROM. come and give us a cuddle, ROM.
+            jp 0x0000
+            ; it's never a dull day with ROM around!
+
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (below 0xF000, only accessible when the kernel is mapped)
+; -----------------------------------------------------------------------------
+            .area _CODE
+
+init_early:
+            ; use MMU to map fast SRAM (memory page 0x2001) into frame at 0xf000
+            ld a, #0x0F
+            out (MMU_SELECT), a
+            ld a, #0x20
+            out (MMU_FRAMEHI), a
+            ld a, #0x01
+            out (MMU_FRAMELO), a
+            ; otherwise we assume the MMU is set up with DRAM from 0x0000 to 0xEFFF.
+
+            ; we need to recover the contents of 0xF000, especially as it holds the stack 
+            ; (with our return address on it!) the interrupt handler code, etc.
+
+            ; map RAM that was at 0xF000 into 0xE000
+            ld a, #0x0E
+            out (MMU_SELECT), a
+            xor a
+            out (MMU_FRAMEHI), a
+            ld a, #0x0F
+            out (MMU_FRAMELO), a
+
+            ; copy 0xE000 -> 0xF000 for 0x1000 bytes
+            ld hl, #0xE000
+            ld de, #0xF000
+            ld bc, #0x1000
+            ldir
+
+            ; map page 001F (which will become the top page for our first process, init) into frame at 0xf000
+            ld a, #0x0F
+            out (MMU_SELECT), a
+            xor a
+            out (MMU_FRAMEHI), a
+            ld a, #0x1f
+            out (MMU_FRAMELO), a
+
+            ; copy 0xE000 -> 0xF000 for 0x1000 bytes
+            ld hl, #0xE000
+            ld de, #0xF000
+            ld bc, #0x1000
+            ldir
+            
+            ; put 0xE000 back as it was
+            ld a, #0x0E
+            out (MMU_SELECT), a
+            out (MMU_FRAMELO), a
+
+            ret
+
+init_hardware:
+            ; set system RAM size
+            ld hl, #8192
+            ld (_ramsize), hl
+            ; 6MB is reserved for the RAM disks, and 64K for the kernel, so ...
+            ld hl, #1984               ; (8192-4096-2048-64)
+            ld (_procmem), hl
+
+            ; set up timer hardware to interrupt us at 100Hz
+            ld a, #TIMCMD_SEL_DOWNRESET
+            out (TIMER_COMMAND), a
+
+            ; (1000000 / 50) - 1 = 0x00004e1f
+            ; (1000000 / 100) - 1 = 0x0000270f
+            ; (1000000 / 250) - 1 = 0x0000049f
+            ld a, #0x0F
+            out (TIMER_VAL0), a
+            ld a, #0x27
+            out (TIMER_VAL1), a
+            xor a
+            out (TIMER_VAL2), a
+            out (TIMER_VAL3), a
+
+            ; reset downcounter
+            ld a, #TIMCMD_DOWNRESET
+            out (TIMER_COMMAND), a
+
+            ; program timer control register
+            in a, (TIMER_STATUS)
+            set 6, a ; enable interrupt generation
+            res 7, a ; clear any outstanding interrupt
+            out (TIMER_STATUS), a
+
+            ; program UART0 for interrupts on RX (not TX ... yet)
+            in a, (UART0_STATUS)
+            and #0xF0 ; clear bottom four bits only
+            or  #0x0c ; enable TX & RX ints
+            out (UART0_STATUS), a
+
+            ; program UART1 similarly
+            in a, (UART1_STATUS)
+            and #0xF0 ; clear bottom four bits only
+            or  #0x0c ; enable TX & RX ints
+            out (UART1_STATUS), a
+
+            ; set up interrupt vectors for the kernel (also sets up common memory in page 0x000F which is unused)
+            ld hl, #OS_BANK
+            push hl
+            call _program_vectors
+            pop hl
+
+            im 1 ; set CPU interrupt mode
+            ret
+
+
+; tty_writeready(uint8_t minor, uint8_t char)
+_tty_writeready:
+            ; stack has: return address, minor
+            ;                   0 1      2    
+            ; set HL to point to character on stack
+            ld hl, #2
+            add hl,sp
+            ld a, (hl) ; load tty device minor number
+            cp #1
+            jr z, uart0wr
+            cp #2
+            jr z, uart1wr
+                            call _trap_monitor
+            ret ; not a console we recognise
+uart1wr:    in a, (UART1_STATUS)
+            jr testready
+uart0wr:    in a, (UART0_STATUS)
+            ; fall through
+testready:  bit 6, a ; transmitter busy?
+            jr nz, notready ; 0=idle, 1=busy
+            ld l, #1
+            ret
+notready:   ld l, #0
+            ret
+
+
+; tty_putc(uint8_t minor, uint8_t char)
+_tty_putc:
+            ; stack has: return address, minor, character
+            ;                   0 1      2      3
+            ; set HL to point to minor on stack
+            ld hl, #2
+            add hl,sp
+            ld a, (hl)
+            inc hl ; advance HL to point to character
+            cp #1
+            jr z, uart0putc
+            cp #2
+            jr z, uart1putc
+            ret ; not a console we recognise
+uart0putc:
+tpc0loop:   ; wait for transmitter to be idle
+            in a, (UART0_STATUS)
+            bit 6, a
+            jr nz, tpc0loop
+            ; output character
+            ld a, (hl)
+            out (UART0_DATA), a
+            ret
+uart1putc:
+tpc1loop:   ; wait for transmitter to be idle
+            in a, (UART1_STATUS)
+            bit 6, a
+            jr nz, tpc1loop
+            ; output character
+            ld a, (hl)
+            out (UART1_DATA), a
+            ret
+
+uart0_input: ; on arrival we know UART0_STATUS bit 7 is set (RX ready); we must preserve BC
+            push bc
+            in a, (UART0_DATA)
+            ld b, a
+            ld c, #1
+indocall:   push bc
+            call _tty_inproc
+            pop bc
+            pop bc
+            ret
+
+uart1_input: ; on arrival we know UART1_STATUS bit 7 is set (RX ready); we must preserve BC
+            push bc
+            in a, (UART1_DATA)
+            ld b, a
+            ld c, #2
+            jr indocall
+
+uart0_output: ; on arrival we know UART0_STATUS bit 6 is clear (TX idle); we must preserve BC
+            push bc
+            ld c, #1
+outdocall:  push bc
+            call _tty_outproc
+            pop bc
+            pop bc
+            ret
+
+uart1_output: ; on arrival we know UART1_STATUS bit 6 is clear (TX idle); we must preserve BC
+            push bc
+            ld c, #2
+            jr outdocall
+
+; write out DE bytes to MMU page17 register from (HL)
+page17out:
+            ld bc, #MMU_PAGE17 ; also loads B=0
+            jr next256write
+dowrite:
+            ; register B contains number of bytes to write
+            otir    ; writes B bytes (B=0 means 256)
+            ; setup for next write
+next256write:
+            ld a, d
+            or a
+            jr z, writelastbytes
+            ; D>0 so we do another 256 bytes
+            dec d
+            jr dowrite
+writelastbytes:
+            ; now D=0, just write E bytes
+            ld a, e
+            or a
+            ret z
+            ld b, e
+            otir
+            ret
+
+; read in DE bytes to MMU page17 register to (HL)
+page17in:
+            ld bc, #MMU_PAGE17 ; also loads B=0
+            ; read in DE bytes from MMU page17 register (in C) to (HL); B contains 0
+            jr next256read
+doread:
+            ; register B contains number of bytes to read
+            inir    ; reads B bytes (B=0 means 256)
+            ; setup for next read
+next256read:
+            ld a, d
+            or a
+            jr z, readlastbytes
+            ; D>0 so we do another 256 bytes
+            dec d
+            jr doread
+readlastbytes:
+            ; now D=0, just read E bytes
+            ld a, e
+            or a
+            ret z
+            ld b, e
+            inir
+            ret
+
+_program_vectors:
+            ; we are called, with interrupts disabled, by both newproc() and crt0
+            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, #0x0E ; use 0xe000 for mapping
+            out (MMU_SELECT), a
+            ld a, l
+            out (MMU_FRAMELO), a
+            ld a, h
+            out (MMU_FRAMEHI), a
+
+            ; write zeroes across all vectors
+            ld hl, #0xE000
+            ld de, #0xE001
+            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 (0xE038), a
+            ld hl, #interrupt_handler
+            ld (0xE039), hl
+
+            ; set restart vector for UZI system calls
+            ld (0xE030), a   ;  (rst 30h is unix function call vector)
+            ld hl, #unix_syscall_entry
+            ld (0xE031), hl
+
+            ; Set vector for Illegal Instructions (this is presuambly a Z180 CPU feature? Z80 doesn't do this)
+            ld (0xE000), a   
+            ld hl, #trap_illegal   ;   to Our Trap Handler
+            ld (0xE001), hl
+
+            ld (0xE066), a  ; Set vector for NMI
+            ld hl, #nmi_handler
+            ld (0xE067), hl
+
+            ; now prepare the top page of memory for this process; it will need
+            ; a copy of the code located there (we take this from our SRAM page)
+            pop de  ; return address
+            pop hl  ; base page number
+            push hl
+            push de
+
+            ld de, #0x000f  ; advance HL to top page of process memory
+            add hl, de
+
+            ; program MMU (frame E is still selected)
+            ld a, l
+            out (MMU_FRAMELO), a
+            ld a, h
+            out (MMU_FRAMEHI), a
+
+            ; load SRAM in frame D
+            ld a, #0x0d
+            out (MMU_SELECT), a
+            ld a, #0x20
+            out (MMU_FRAMEHI), a
+            ld a, #0x01
+            out (MMU_FRAMELO), a
+
+            ; copy the code only, not the udata or stacks.
+            ld de, #(0xE000 + U_DATA__PAGEOFFSET + U_DATA__TOTALSIZE)   ; to process RAM
+            ld hl, #(0xD000 + U_DATA__PAGEOFFSET + U_DATA__TOTALSIZE)   ; from SRAM
+            ld bc, #(0x1000 - U_DATA__PAGEOFFSET - U_DATA__TOTALSIZE)   ; count
+            ldir                                    ; copy copy copy
+
+            ; put the MMU back as it was -- we're in kernel mode so this is predictable
+            xor a
+            out (MMU_FRAMEHI), a
+            ld a, #0x0d
+            out (MMU_FRAMELO), a
+
+            ld a, #0x0e
+            out (MMU_SELECT), a
+            out (MMU_FRAMELO), a
+            xor a
+            out (MMU_FRAMEHI), a
+
+            ret
+
+mmumsg:     .ascii "MMU page "
+            .db 0
+
+mmu_state_dump:
+            push bc
+            push hl
+            ld c, #0
+            ld b, #16
+dumpnextframe:
+            ld hl, #mmumsg
+            call outstring
+            ld a, c
+            out (MMU_SELECT), a
+            call outnibble
+            ld a, #':'
+            call outchar
+            ld a, #' '
+            call outchar
+            in a, (MMU_FRAMEHI)
+            ld h, a
+            in a, (MMU_FRAMELO)
+            ld l, a
+            call outhl
+            call outnewline
+            inc c
+            djnz dumpnextframe
+            pop hl
+            pop bc
+            ret
+
+
+
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+            .area _COMMONMEM
+
+; map process into address space
+; first page address is in HL
+; destroys HL, BC, AF, does not use DE
+map_process:
+           ld a, h
+           or l
+           jr nz, map_process_user
+           ld hl, #OS_BANK
+           jr map_restore
+
+map_process_user:
+           ld a, (hl)
+           inc hl
+           ld h, (hl)
+           ld l, a
+map_restore:
+            ; examine 0 page
+            xor a
+            out (MMU_SELECT), a
+            ; if HL is the mapping already loaded, abort early
+            in a, (MMU_FRAMEHI)
+            cp h
+            jr nz, map_process_go
+            in a, (MMU_FRAMELO)
+            cp l
+            ret z
+map_process_go:
+            ; now setup the MMU
+            ld b, #15
+            ld c, #1 ; this is the next frame to be remapped
+map_process_next:
+            ld a, h
+            out (MMU_FRAMEHI), a
+            ld a, l
+            out (MMU_FRAMELO), a
+            ld a, c
+            out (MMU_SELECT), a
+            inc c
+            inc hl
+            djnz map_process_next
+            ret
+
+            ; Load page HL into frame A
+            ; Return old mapping in DE
+mmu_map_page:
+            out (MMU_SELECT), a
+            ld c, #MMU_FRAMEHI
+            in d, (c)
+            out (c), h
+            inc c ; c is now MMU_FRAMELO
+            in e, (c)
+            out (c), l
+            ret
+
+            ; remap page (used after mmu_map_page)
+mmu_map_page_fast:
+            ld a, h
+            out (MMU_FRAMEHI), a
+            ld a, l
+            out (MMU_FRAMELO), a
+            ret
+
+map_kernel:
+           ld hl, #0
+            jp map_process
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+; outchar: Wait for UART TX idle, then print the char in A
+; destroys: AF
+outchar:
+            push bc
+            ld b, a
+            ; wait for transmitter to be idle
+ocloop:     in a, (UART0_STATUS)
+            bit 6, a
+            jr nz, ocloop   ; loop while busy
+            ; now output the char to serial port
+            ld a, b
+            out (UART0_DATA), a
+            pop bc
+            ret
diff --git a/Kernel/platform-socz80/tricks.s b/Kernel/platform-socz80/tricks.s
new file mode 100644 (file)
index 0000000..5b2587e
--- /dev/null
@@ -0,0 +1,296 @@
+; 2013-12-21 William R Sowerbutts
+
+        .module tricks
+
+        .globl _ptab_alloc
+        .globl _newproc
+        .globl _chksigs
+        .globl _getproc
+        .globl _trap_monitor
+        .globl trap_illegal
+        .globl _inint
+        .globl _switchout
+        .globl _switchin
+        .globl _dofork
+        .globl _runticks
+        .globl unix_syscall_entry
+        .globl mmu_map_page
+        .globl map_process
+        .globl mmu_state_dump
+        .globl mmu_map_page_fast
+        .globl interrupt_handler
+
+        ; imported debug symbols
+        .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex
+
+        .include "socz80.def"
+        .include "../kernel.def"
+        .include "kernel.def"
+
+        .area _COMMONMEM
+
+; 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.
+_switchout:
+        di
+        call _chksigs
+        ; 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
+
+        ; set inint to false
+        xor a
+        ld (_inint), a
+
+        ; find another process to run (may select this one again)
+        call _getproc
+
+        push hl
+        call _switchin
+
+        ; we should never get here
+        call _trap_monitor
+
+badswitchmsg: .ascii "_switchin: FAIL"
+            .db 13, 10, 0
+_switchin:
+        di
+        pop bc  ; return address
+        pop de  ; new process pointer
+        push de ; restore stack
+        push bc ; restore stack
+
+        ld iy, #0
+        add iy, de
+
+        ld h, P_TAB__P_PAGE_OFFSET+1(iy)
+        ld l, P_TAB__P_PAGE_OFFSET(iy)
+        push de
+        ld de, #0x0f
+        add hl, de
+        pop de ; keep new process pointer in DE so we can compare with
+              ; it after the switch
+
+        ; bear in mind that the stack will be switched now, so we can't use it
+       ; to carry values over this point
+        
+        ; get next_process->p_page value and load it into the MMU for the
+       ; top page
+        ld a, #0x0f
+        out (MMU_SELECT), a
+        ld a, h
+        out (MMU_FRAMEHI), a
+        ld a, l
+        out (MMU_FRAMELO), a
+
+        ; 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==HL
+        jr nz, switchinfail
+
+        ; next_process->p_status = P_RUNNING
+        ld P_TAB__P_STATUS_OFFSET(iy), #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)
+        pop iy
+        pop ix
+        pop hl ; return code
+
+        ; enable interrupts, if the ISR isn't already running
+        ld a, (_inint)
+        or a
+        ret z ; in ISR, leave interrupts off
+        ei
+        ret ; return with interrupts on
+
+switchinfail:
+        ld hl, #badswitchmsg
+        call outstring
+       ; something went wrong and we didn't switch in what we asked for
+        call nz, _trap_monitor
+
+fork_proc_ptr: .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry
+
+_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.
+
+        ; --------- copy process ---------
+        ; store the old MMU mapping
+        ld a, #0x0E
+        out (MMU_SELECT), a
+        in a, (MMU_FRAMEHI)
+        ld b, a
+        in a, (MMU_FRAMELO)
+        ld c, a
+        push bc
+        ld a, #0x0D
+        out (MMU_SELECT), a
+        in a, (MMU_FRAMEHI)
+        ld b, a
+        in a, (MMU_FRAMELO)
+        ld c, a
+        push bc
+
+        ; Copy entire 64k process into Child's address space
+        ; note source base page is in DE
+        ; dest base page is in (_ub+OPAGE) which is in 0xF000, which will not
+       ; be available after we start remapping
+        ; we can do the copy in frame 0xE000 and 0xF000
+
+        ld hl, (fork_proc_ptr)
+        ld de, #P_TAB__P_PAGE_OFFSET
+        add hl, de
+        ; load child base page
+        ld e, (hl)
+        inc hl
+        ld d, (hl)
+        ld hl, #0x1000 ; +16MB, this puts the destination pointer into uncached DRAM.
+        add hl, de     ; this is so we don't obliterate the read cache (the cache is
+                       ; direct mapped and so the parent and child processes are cache
+                       ; aliases of each other)
+
+        ld de, (U_DATA__U_PAGE) ; load parent page base page
+
+        ld b, #16 ; we're going to copy 0x0000 ... 0xFFFF
+copynextpage:
+        ; map source page at E000
+        LD A, #0x0E
+        out (MMU_SELECT), a
+        ld a, d
+        out (MMU_FRAMEHI), a
+        ld a, e
+        out (MMU_FRAMELO), a
+        ; map destination page at D000
+        ld a, #0x0D
+        out (MMU_SELECT), a
+        ld a, h
+        out (MMU_FRAMEHI), a
+        ld a, l
+        out (MMU_FRAMELO), a
+
+        push hl
+        push de
+        push bc
+
+        ld hl, #0xe000
+        ld de, #0xd000
+        ld bc, #0x1000
+        ldir
+
+        pop bc
+        pop de
+        pop hl
+        
+        ; next loop, do the next 4k
+        inc hl
+        inc de
+
+        ; loop around
+        djnz copynextpage
+
+        ; done copying, restore MMU
+        ld a, #0x0D
+        out (MMU_SELECT), a
+        pop bc
+        ld a, b
+        out (MMU_FRAMEHI), a
+        ld a, c
+        out (MMU_FRAMELO), a
+        ld a, #0x0E
+        out (MMU_SELECT), a
+        pop bc
+        ld a, b
+        out (MMU_FRAMEHI), a
+        ld a, c
+        out (MMU_FRAMELO), a
+        ; --------- copy completed ---------
+
+        ; switch into the child process context
+        ; get address of top page: fork_proc_ptr->p_page + 0x0f
+        ld hl, (fork_proc_ptr)
+        ld de, #P_TAB__P_PAGE_OFFSET
+        add hl, de
+        ; load p_page
+        ld e, (hl)
+        inc hl
+        ld d, (hl)
+        ; add 0x000F
+        ld hl, #0x000F
+        add hl, de
+        ; load into MMU
+        ld a, #0x0F
+        out (MMU_SELECT), a
+        ld a, h
+        out (MMU_FRAMEHI), a
+        ld a, l
+        out (MMU_FRAMELO), a
+
+        ; 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
+
+        ; Make a new process table entry, etc.
+        ld  hl, (fork_proc_ptr)
+        push hl
+        call _newproc
+        pop bc 
+
+        ; runticks = 0;
+        ld hl, #0
+        ld (_runticks), hl
+
+        ; in the child process, fork() returns zero.
+        ld  hl, #0
+        ret
+
diff --git a/Kernel/platform-socz80/usermem.s b/Kernel/platform-socz80/usermem.s
new file mode 100644 (file)
index 0000000..cb0f8be
--- /dev/null
@@ -0,0 +1,251 @@
+        .module usermem
+
+        .include "socz80.def"
+        .include "../kernel.def"
+        .include "kernel.def"
+
+        ; imported symbols
+        .globl _ub
+        .globl page17out
+        .globl page17in
+
+        ; exported symbols
+        .globl __uget
+        .globl __ugetc
+        .globl __ugets
+        .globl __ugetw
+
+        .globl __uput
+        .globl __uputc
+        .globl __uputw
+        .globl __uzero
+
+        .area _CODE
+
+__uput:
+        push ix
+        ; stack has: ix, return address, source, dest, count. 
+        ;                                ix+4    ix+6  ix+8
+        ld ix, #0   ; load ix with stack pointer
+        add ix, sp
+        ; store interrupt state, disable interrupts
+        ld a, i
+        di
+        push af
+        ; load DE with destination address (in userspace)
+        ld e, 6(ix)
+        ld d, 7(ix)
+        call ugetputsetup
+        ; load HL with the source address
+        ld l, 4(ix) ; src address
+        ld h, 5(ix)
+        ; load DE with the byte count
+        ld e, 8(ix) ; byte count
+        ld d, 9(ix)
+        call page17out
+        jr ugetputret
+
+__uputc:
+        push ix
+        ; stack has: ix, return address, char, dest
+        ;                                ix+4  ix+6,7
+        ld ix, #0   ; load ix with stack pointer
+        add ix, sp
+        ; store interrupt state, disable interrupts
+        ld a, i
+        di
+        push af
+        ; load DE with destination address (in userspace)
+        ld e, 6(ix)
+        ld d, 7(ix)
+        call ugetputsetup
+        ld a, 4(ix)
+        out (MMU_PAGE17), a
+        jr ugetputret
+
+__uputw:
+        push ix
+        ; stack has: ix, return address, word, dest
+        ;                                ix+4  ix+6
+        ld ix, #0   ; load ix with stack pointer
+        add ix, sp
+        ; store interrupt state, disable interrupts
+        ld a, i
+        di
+        push af
+        ; load DE with destination address (in userspace)
+        ld e, 6(ix)
+        ld d, 7(ix)
+        call ugetputsetup
+        ld a, 4(ix)
+        out (MMU_PAGE17), a
+        ld a, 5(ix)
+        out (MMU_PAGE17), a
+        jr ugetputret
+
+__ugetc:
+        push ix
+        ; stack has: ix, return address, source
+        ;                                ix+4 
+        ld ix, #0   ; load ix with stack pointer
+        add ix, sp
+        ; store interrupt state, disable interrupts
+        ld a, i
+        di
+        push af
+        ; load DE with source address (in userspace)
+        ld e, 4(ix)
+        ld d, 5(ix)
+        call ugetputsetup
+        in a, (MMU_PAGE17)
+        ld l, a
+        ld h, #0
+        jr ugetputret
+
+__ugetw:
+        push ix
+        ; stack has: ix, return address, source
+        ;                                ix+4 
+        ld ix, #0   ; load ix with stack pointer
+        add ix, sp
+        ; store interrupt state, disable interrupts
+        ld a, i
+        di
+        push af
+        ; load DE with source address (in userspace)
+        ld e, 4(ix)
+        ld d, 5(ix)
+        call ugetputsetup
+        in a, (MMU_PAGE17)
+        ld l, a
+        in a, (MMU_PAGE17)
+        ld h, a
+ugetputret: ; this is shared with the other routines, above and below
+        pop af
+        pop ix
+        ret po
+        ei
+        ret
+
+__uget:
+        push ix
+        ; stack has: ix, return address, source, dest, count. 
+        ;                                ix+4    ix+6  ix+8
+        ld ix, #0   ; load ix with stack pointer
+        add ix, sp
+        ; store interrupt state, disable interrupts
+        ld a, i
+        di
+        push af
+        ; load DE with source address (in userspace)
+        ld e, 4(ix)
+        ld d, 5(ix)
+        call ugetputsetup
+        ; load HL with destination address
+        ld l, 6(ix)
+        ld h, 7(ix)
+        ; load DE with the byte count
+        ld e, 8(ix) ; byte count
+        ld d, 9(ix)
+        call page17in
+        jr ugetputret
+
+ugetputsetup:
+        ; compute 32-bit dest address based on process MMU page and userspace address in DE
+        ld hl, (U_DATA__U_PAGE) ; load 4K page address
+
+        ; we shift HL 4 bits to the left (would need to handle overflow for future hardware with >16MB RAM)
+        add hl, hl
+        add hl, hl
+        add hl, hl
+        add hl, hl
+
+        ld a, d         ; high byte of userspace address
+        add a, l        ; add with L
+        jr nc, ugetputsetupnoadd
+        inc h        ; handle overflow into H
+ugetputsetupnoadd:
+        ld l, a      ; update L
+
+        ; now configure the MMU page17 pointer
+        ld a, #MMU_SELECT_PAGE17
+        out (MMU_SELECT), a
+        xor a
+        out (MMU_PTR_VAL0), a
+        ld a, h
+        out (MMU_PTR_VAL1), a
+        ld a, l
+        out (MMU_PTR_VAL2), a
+        ld a, e        ; low byte of userspace address
+        out (MMU_PTR_VAL3), a
+        ret
+
+__uzero:
+        push ix
+        ; stack has: ix, return address, dest, count. 
+        ;                                ix+4  ix+6
+        ld ix, #0
+        add ix, sp
+        ; store interrupt state, disable interrupts
+        ld a, i
+        di
+        push af
+        ; load DE with dest address (in userspace)
+        ld e, 4(ix)
+        ld d, 5(ix)
+        call ugetputsetup
+        ; load byte count
+        ld e, 6(ix)
+        ld d, 7(ix)
+        ; write 0s to page17 for DE bytes
+        ld bc, #MMU_PAGE17 ; also loads B=0
+        jr checkzero ; just to be safe against DE=0
+nextbyte:
+        out (c), b ; write zero
+        dec de
+checkzero:
+        ld a, d
+        or e
+        jr nz, nextbyte
+        jr ugetputret
+
+__ugets:
+        push ix
+        ld ix, #0   ; load ix with stack pointer
+        add ix, sp
+        ; store interrupt state, disable interrupts
+        ld a, i
+        di
+        push af
+        ; load DE with source address (in userspace)
+        ld e, 4(ix)
+        ld d, 5(ix)
+        call ugetputsetup
+        ; load HL with destination address
+        ld l, 6(ix)
+        ld h, 7(ix)
+        ; load DE with the byte count
+        ld e, 8(ix) ; byte count
+        ld d, 9(ix)
+        ; read from page17, write to HL for AT MOST DE bytes or until 0 byte found
+        ld bc, #MMU_PAGE17 ; also loads B=0
+        jr checkzeros
+nextbytes:
+        in a, (c) ; read from page17
+        ld (hl), a ; write to string
+        or a
+        jr z, getsdone
+        inc hl ; advance pointer
+        dec de ; decrement remaining byte counter
+checkzeros:
+        ld a, d
+        or e
+        jr nz, nextbytes
+        ; ah - we've run out of space
+        dec hl ; back it up
+        ld (hl), #0 ; terminate string
+        ; leave HL as nonzero
+        jp ugetputret ; too far for a jr
+getsdone:
+        ld hl, #0 ; indicate success
+        jp ugetputret ; too far for a jr