From 9665fd03f0cca1facd26bec6613e33a330b15413 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 31 Oct 2014 16:26:42 +0000 Subject: [PATCH] socz80: Add the target 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. --- Kernel/platform-socz80/Makefile | 25 ++ Kernel/platform-socz80/commonmem.s | 134 +++++++ Kernel/platform-socz80/config.h | 35 ++ Kernel/platform-socz80/crt0.s | 101 ++++++ Kernel/platform-socz80/devices.c | 41 +++ Kernel/platform-socz80/devlpr.c | 32 ++ Kernel/platform-socz80/devlpr.h | 8 + Kernel/platform-socz80/devrd.c | 98 +++++ Kernel/platform-socz80/devrd.h | 10 + Kernel/platform-socz80/devrd_hw.s | 42 +++ Kernel/platform-socz80/devsd.c | 395 +++++++++++++++++++++ Kernel/platform-socz80/devsd.h | 67 ++++ Kernel/platform-socz80/devsd_hw.s | 103 ++++++ Kernel/platform-socz80/devtty.c | 38 ++ Kernel/platform-socz80/devtty.h | 4 + Kernel/platform-socz80/kernel.def | 6 + Kernel/platform-socz80/main.c | 65 ++++ Kernel/platform-socz80/socz80.def | 54 +++ Kernel/platform-socz80/socz80.s | 550 +++++++++++++++++++++++++++++ Kernel/platform-socz80/tricks.s | 296 ++++++++++++++++ Kernel/platform-socz80/usermem.s | 251 +++++++++++++ 21 files changed, 2355 insertions(+) create mode 100644 Kernel/platform-socz80/Makefile create mode 100644 Kernel/platform-socz80/commonmem.s create mode 100644 Kernel/platform-socz80/config.h create mode 100644 Kernel/platform-socz80/crt0.s create mode 100644 Kernel/platform-socz80/devices.c create mode 100644 Kernel/platform-socz80/devlpr.c create mode 100644 Kernel/platform-socz80/devlpr.h create mode 100644 Kernel/platform-socz80/devrd.c create mode 100644 Kernel/platform-socz80/devrd.h create mode 100644 Kernel/platform-socz80/devrd_hw.s create mode 100644 Kernel/platform-socz80/devsd.c create mode 100644 Kernel/platform-socz80/devsd.h create mode 100644 Kernel/platform-socz80/devsd_hw.s create mode 100644 Kernel/platform-socz80/devtty.c create mode 100644 Kernel/platform-socz80/devtty.h create mode 100644 Kernel/platform-socz80/kernel.def create mode 100644 Kernel/platform-socz80/main.c create mode 100644 Kernel/platform-socz80/socz80.def create mode 100644 Kernel/platform-socz80/socz80.s create mode 100644 Kernel/platform-socz80/tricks.s create mode 100644 Kernel/platform-socz80/usermem.s diff --git a/Kernel/platform-socz80/Makefile b/Kernel/platform-socz80/Makefile new file mode 100644 index 00000000..b5047a5f --- /dev/null +++ b/Kernel/platform-socz80/Makefile @@ -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 index 00000000..42606b3c --- /dev/null +++ b/Kernel/platform-socz80/commonmem.s @@ -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 index 00000000..2cd4d040 --- /dev/null +++ b/Kernel/platform-socz80/config.h @@ -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 index 00000000..fbbf7a65 --- /dev/null +++ b/Kernel/platform-socz80/crt0.s @@ -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 index 00000000..76d715e1 --- /dev/null +++ b/Kernel/platform-socz80/devices.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 index 00000000..0e125a7a --- /dev/null +++ b/Kernel/platform-socz80/devlpr.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include + +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 index 00000000..7765c187 --- /dev/null +++ b/Kernel/platform-socz80/devlpr.h @@ -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 index 00000000..a0fe3031 --- /dev/null +++ b/Kernel/platform-socz80/devrd.c @@ -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 +#include +#include + +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 index 00000000..e54a1546 --- /dev/null +++ b/Kernel/platform-socz80/devrd.h @@ -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 index 00000000..b3494f02 --- /dev/null +++ b/Kernel/platform-socz80/devrd_hw.s @@ -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 index 00000000..cec8f912 --- /dev/null +++ b/Kernel/platform-socz80/devsd.c @@ -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 +#include +#include +#include +#include +#include + +/* 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 is the command sequense of CMD55-CMD */ + 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 index 00000000..bcf90384 --- /dev/null +++ b/Kernel/platform-socz80/devsd.h @@ -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 index 00000000..c21a9bec --- /dev/null +++ b/Kernel/platform-socz80/devsd_hw.s @@ -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 index 00000000..791b8d33 --- /dev/null +++ b/Kernel/platform-socz80/devtty.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include +#include + + +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 index 00000000..c387e904 --- /dev/null +++ b/Kernel/platform-socz80/devtty.h @@ -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 index 00000000..895914cb --- /dev/null +++ b/Kernel/platform-socz80/kernel.def @@ -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 index 00000000..a6c7e421 --- /dev/null +++ b/Kernel/platform-socz80/main.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include + +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 index 00000000..bfc693ab --- /dev/null +++ b/Kernel/platform-socz80/socz80.def @@ -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 index 00000000..00278cf4 --- /dev/null +++ b/Kernel/platform-socz80/socz80.s @@ -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 index 00000000..5b2587e2 --- /dev/null +++ b/Kernel/platform-socz80/tricks.s @@ -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 index 00000000..cb0f8bee --- /dev/null +++ b/Kernel/platform-socz80/usermem.s @@ -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 -- 2.34.1