TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-spec3 platform-trs80 platform-z80pack platform-z80pack-lite
-export TARGET= pcw8256
+export TARGET= px4plus
export CPU = z80
#export TARGET = 6809test
#export CPU = 6809
--- /dev/null
+
+CSRCS = devlpr.c devtty.c devfd.c
+CSRCS += devices.c main.c
+
+ASRCS = crt0.s px4plus.s
+ASRCS += tricks.s commonmem.s
+
+COBJS = $(CSRCS:.c=.rel)
+AOBJS = $(ASRCS:.s=.rel)
+OBJS = $(COBJS) $(AOBJS)
+
+JUNK = $(CSRCS:.c=.lst) $(CSRCS:.c=.asm) $(CSRCS:.c=.sym) $(ASRCS:.s=.lst) $(ASRCS:.s=.sym) $(CSRCS:.c=.rst) $(ASRCS:.s=.rst)
+
+all: $(OBJS)
+
+$(COBJS): %.rel: %.c
+ $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(AOBJS): %.rel: %.s
+ $(CROSS_AS) $(ASOPTS) $<
+
+clean:
+ rm -f $(OBJS) $(JUNK) core *~
+
+image:
--- /dev/null
+A FUZIX target for the PX4plus
+
+Just playing to see if we can make it fit.
+
+The PX4Plus has 64K of RAM, and can overlay 6000-DFFF with one of two
+different ROMpacks (usually 'basic' and 'utility'). We build the kernel
+at 0x6000 but for now plonk it into RAM so we can debug it. The small 8K
+top window is awkward as we need to get udata and our kernel data/common in
+that space if possible. We can hide the font/video below that under the top of
+the ROM however.
+
+
+Intended Memory Map
+
+
+0x0000-0x00FF Vectors
+0x0100-0x???? (RAM) User program (and discard area)
+0xD000-0xDFFF Framebuffer, font, video code
+0xE000-0xF7FFish Data for kernel
+0xF800-0xFCFF Common (currently ends FBCC)
+0xFD00-0xFFFF Udata
+
+In kernel execution modes we then are either
+
+0x0000-0x00FF Vectors
+0x0100-0x5FFF Lower part of user
+0x6000-0xDFFF Kernel code in ROM
+0xE000-0xF7FFish Data for kernel
+0xF800-0xFCFF Common (currently ends FBCC)
+0xFD00-0xFFFF Udata
+
+and we will need to tuck the video code/font/framebuffer at 0xD000 or
+similar in RAM under the ROM area. May need to hide the floppy driver
+there too as its all a little bit tight
--- /dev/null
+;
+; Uarea is not in common on pure swap
+;
+ .module commonmem
+
+ ; exported symbols
+ .globl _ub
+ .globl _udata
+ .globl kstack_top
+ .globl istack_top
+ .globl istack_switched_sp
+
+ .area _UDATA
+
+_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
--- /dev/null
+/* Enable to make ^Z dump the inode table for debug */
+#undef CONFIG_IDUMP
+/* Enable to make ^A drop back into the monitor */
+#undef CONFIG_MONITOR
+/* Profil syscall support (not yet complete) */
+#undef CONFIG_PROFIL
+/* Multiple processes in memory at once */
+#define CONFIG_MULTI
+/* Single tasking */
+#undef CONFIG_SINGLETASK
+/* CP/M emulation */
+#undef CONFIG_CPM_EMU
+/* Fixed banking */
+#undef CONFIG_BANK_FIXED
+/* Swap only */
+#define CONFIG_SWAP_ONLY
+/* Simple user copies for now (change when ROM the kernel) */
+#define CONFIG_USERMEM_C
+#define BANK_KERNEL
+#define BANK_PROCESS
+
+/* Banks as reported to user space */
+#define CONFIG_BANKS 1
+
+/* FIXME: the OVL timer isn't quite 100/sec and we have an accurate 1Hz
+ timer available, so needs some tweaking */
+#define TICKSPERSEC 100 /* Ticks per second */
+#define PROGBASE ((char *)(0x0100)) /* also data base */
+#define PROGTOP ((char *)(0x7D00)) /* Top of program, base of U_DATA */
+
+#define SWAP_SIZE 0x40 /* 32K in blocks (we actually don't need the low 256) */
+#define SWAPBASE 0x0000 /* We swap the lot in one, include the */
+#define SWAPTOP 0x8000 /* vectors so its a round number of sectors */
+#define MAX_SWAPS 4 /* We have a whopping 128K of RAMDISC! */
+
+#define BOOT_TTY (512 + 1)/* Set this to default device for stdio, stderr */
+ /* In this case, the default is the first TTY device */
+
+/* We need a tidier way to do this from the loader */
+#define CMDLINE NULL /* Location of root dev name */
+
+/* Device parameters */
+#define NUM_DEV_TTY 2
+
+#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
+#define SWAPDEV (256 + 0) /* Device for swapping. (ram drive) */
+#define NBUFS 6 /* Number of block buffers */
+#define NMOUNTS 2 /* Number of mounts at a time */
+
--- /dev/null
+; 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 _CONST
+ .area _DATA
+ .area _INITIALIZED
+ .area _COMMONMEM
+ .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 _DISCARD
+ .area _UDATA
+
+ ; imported symbols
+ .globl _fuzix_main
+ .globl init_early
+ .globl init_hardware
+ .globl s__INITIALIZER
+ .globl s__COMMONMEM
+ .globl l__COMMONMEM
+ .globl s__DATA
+ .globl l__DATA
+ .globl kstack_top
+
+ ; startup code
+ .area _CODE
+init:
+ di
+ ld sp, #kstack_top
+
+ ; Configure memory map
+ call init_early
+
+ ; zero the data area
+ ld hl, #s__DATA
+ ld de, #s__DATA + 1
+ ld bc, #l__DATA - 1
+ ld (hl), #0
+ ldir
+
+ ; 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
+
--- /dev/null
+/*
+ * Disk driver
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devfd.h>
+
+static int fd_transfer(bool is_read, uint8_t minor, uint8_t rawflag);
+static int hd_transfer(bool is_read, uint8_t minor, uint8_t rawflag);
+
+int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return fd_transfer(true, minor, rawflag);
+}
+
+int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return fd_transfer(false, minor, rawflag);
+}
+
+/* hd is only used for swapping */
+int hd_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return hd_transfer(true, minor, rawflag);
+}
+
+int hd_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ flag;
+ return hd_transfer(false, minor, rawflag);
+}
+
+static int fd_transfer(bool is_read, uint8_t minor, uint8_t rawflag)
+{
+ /* Serial floppy driver - to write */
+ is_read;
+ minor;
+ rawflag;
+ return -EIO;
+}
+
+/* Easier to use statics as is this is single threaded and talking to asm
+ code in a critical path */
+
+uint16_t ramd_off;
+uint16_t ramd_size;
+uint16_t ramd_uaddr;
+
+/* Really only ever used for swap */
+static int hd_transfer(bool is_read, uint8_t minor, uint8_t rawflag)
+{
+ minor;
+
+ if(rawflag == 1) {
+ ramd_size = udata.u_count;
+ ramd_uaddr = (uint16_t)udata.u_base;
+ ramd_off = udata.u_offset << 1;
+ /* Should check this higher up ? */
+ if ((ramd_size & 0x1FF) || (ramd_off & 0x1FF))
+ return -EIO;
+ } else if (rawflag == 2) { /* Swap device special */
+ ramd_size = swapcnt << 9;
+ ramd_uaddr = (uint16_t)swapbase;
+ ramd_off = swapblk << 1;
+ } else { /* rawflag == 0 */
+ ramd_uaddr = (uint16_t)udata.u_buf->bf_data;
+ ramd_off = udata.u_buf->bf_blk << 8;
+ ramd_size = 512;
+ }
+ /* Block copy to/from the RAM disc I/O */
+ if (is_read)
+ hd_xferin();
+ else
+ hd_xferout();
+ return ramd_size;
+}
+
+int fd_open(uint8_t minor, uint16_t flag)
+{
+ flag;
+ if(minor >= 3) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ return 0;
+}
+
+int hd_open(uint8_t minor, uint16_t flag)
+{
+ flag;
+ if(minor) {
+ udata.u_error = ENODEV;
+ return -1;
+ }
+ return 0;
+}
--- /dev/null
+#ifndef __DEVFD_DOT_H__
+#define __DEVFD_DOT_H__
+
+/* public interface */
+int fd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int fd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int fd_open(uint8_t minor, uint16_t flag);
+
+int hd_read(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int hd_write(uint8_t minor, uint8_t rawflag, uint8_t flag);
+int hd_open(uint8_t minor, uint16_t flag);
+
+/* asm code */
+void hd_xferin(void);
+void hd_xferout(void);
+
+#endif /* __DEVRD_DOT_H__ */
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <tty.h>
+#include <devices.h>
+#include <devfd.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/fd Floppy disc block devices */
+ { fd_open, no_close, fd_read, fd_write, no_ioctl },
+ /* 1: /dev/hd Hard disc block devices (absent) */
+ { hd_open, no_close, hd_read, hd_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)
+{
+ int i;
+ /* Add 4 swaps (128K) to use the entire RAM drive */
+ for (i = 0; i < MAX_SWAPS; i++)
+ swapmap_add(i);
+}
+
+__sfr __at 0x19 ioctrlr;
+
+/* FIXME: find correct initial value */
+static uint8_t ioctrlr_shadow = 0x00;
+
+/* We need to track the state of the ioctrlr port as it is a shared
+ resource (7 = speaker, 6-4 LEDs, 3 - !cartridge reset, 2 serial out ctrl
+ 1 !printer initial output, 0 lpt strobe */
+
+void mod_ioctrlr(uint8_t set, uint8_t clr)
+{
+ ioctrlr_shadow &= ~clr;
+ ioctrlr_shadow |= set;
+ ioctrlr = ioctrlr_shadow;
+}
--- /dev/null
+
+extern void mod_ioctrlr(uint8_t set, uint8_t clr);
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <devlpr.h>
+#include <devices.h>
+
+__sfr __at 0x16 lpstat;
+__sfr __at 0x17 lpdata;
+
+int lpr_open(uint8_t minor, uint16_t flag)
+{
+ minor; flag; // shut up compiler
+ return 0;
+}
+
+int lpr_close(uint8_t minor)
+{
+ minor; // shut up compiler
+ return 0;
+}
+
+int lpr_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
+{
+ int c = udata.u_count;
+ char *p = udata.u_base;
+ uint16_t ct;
+ minor; rawflag; flag; // shut up compiler
+
+ while(c-- > 0) {
+ ct = 0;
+ while ((lpstat & 0x3) == 0x1) {
+ ct++;
+ /* Try and be polite */
+ if (ct == 10000) {
+ udata.u_ptab->p_timeout = 3;
+ if (psleep_flags(NULL, flag)) {
+ if (udata.u_count)
+ udata.u_error = 0;
+ return udata.u_count;
+ }
+ ct = 0;
+ }
+ }
+ /* Printer on fire ? */
+ if (lpstat & 0x2)
+ return -EIO;
+ lpdata = ugetc(p++);
+ /* Strobe */
+ mod_ioctrlr(1,1);
+ mod_ioctrlr(0,1);
+ }
+ return (-1);
+}
--- /dev/null
+#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
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <tty.h>
+#include <devtty.h>
+
+/* Console is only port we provide, port 2 there but used for floppies */
+char tbuf1[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 },
+};
+
+/* Write to system console */
+void kputchar(char c)
+{
+ /* handle CRLF */
+ if(c=='\n')
+ tty_putc(1, '\r');
+ tty_putc(1, c);
+}
+
+/* It's the console display, always ready */
+static bool tty_writeready(uint8_t minor)
+{
+ minor;
+ return 1;
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+ minor;c;
+ /* Fixme: wire in VT code */
+}
+
+void tty_setup(uint8_t minor)
+{
+ minor;
+}
+
+int tty_carrier(uint8_t minor)
+{
+ minor;
+ return 1;
+}
--- /dev/null
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+void tty_putc(uint8_t minor, unsigned char c);
+bool tty_writeready(uint8_t minor);
+void tty_pollirq(void);
+void tty_setup(uint8_t minor);
+#endif
--- /dev/null
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+
+uint8_t *ramtop = PROGTOP;
+
+/* On idle we spin checking for the terminals. Gives us more responsiveness
+ for the polled ports */
+void platform_idle(void)
+{
+ __asm
+ halt
+ __endasm;
+}
+
+__sfr __at 0x04 isr;
+
+void platform_interrupt(void)
+{
+ uint8_t irq = isr;
+ /* We need to handle 1 for the 7508, and 2 for GAPNIO (serial in) at least */
+ /* Overflow on timer */
+ if (irq & 4)
+ timer_interrupt();
+}
+
+/* Nothing to do for the map of init */
+void map_init(void)
+{
+}
--- /dev/null
+;
+; Z80Pack hardware support
+;
+;
+; This goes straight after udata for common. Because of that the first
+; 256 bytes get swapped to and from disk with the uarea (512 byte disk
+; blocks). This isn't a problem but don't put any variables in here.
+;
+; If you make this module any shorter, check what follows next
+;
+
+
+ .module px4plus
+
+ ; exported symbols
+ .globl init_early
+ .globl init_hardware
+ .globl _program_vectors
+ .globl _system_tick_counter
+
+ .globl map_kernel
+ .globl map_process
+ .globl map_process_always
+ .globl map_save
+ .globl map_restore
+
+ ; exported debugging tools
+ .globl _trap_monitor
+ .globl outchar
+
+ ; imported symbols
+ .globl _ramsize
+ .globl _procmem
+
+ .globl unix_syscall_entry
+ .globl null_handler
+ .globl nmi_handler
+ .globl interrupt_handler
+
+ ; RAM drive
+ .globl _hd_xferin
+ .globl _hd_xferout
+ .globl _ramd_off
+ .globl _ramd_size
+ .globl _ramd_uaddr
+
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (not unmapped when we flip to ROM)
+; -----------------------------------------------------------------------------
+ .area _COMMONMEM
+
+; FIXME: figure out how to reboot into CP/M
+_trap_monitor:
+_trap_reboot:
+ di
+ halt
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (only accessible when the kernel is mapped)
+; -----------------------------------------------------------------------------
+ .area _CODE
+
+init_early:
+ ret
+
+init_hardware:
+ ; set system RAM size
+ ; Not strictly correct FIXME: set right when ROM the image
+ ld hl, #64
+ ld (_ramsize), hl
+ ld hl, #32 ; 32K for kernel
+ ld (_procmem), hl
+
+ ; set up interrupt vectors for the kernel
+ ld hl, #0
+ push hl
+ call _program_vectors
+ pop hl
+
+ ; IRQ enables
+ ld a, #0xB ; OVF (timer), RXRDY (gapnio), 7508
+ out (0x04), a
+
+ im 1 ; set CPU interrupt mode
+ ret
+
+
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+ .area _COMMONMEM
+
+
+_program_vectors:
+ ; we are called, with interrupts disabled, by both newproc() and crt0
+ ; will exit with interrupts off
+
+ ; write zeroes across all vectors
+ ld hl, #0
+ ld de, #1
+ ld bc, #0x007f ; program first 0x80 bytes only
+ ld (hl), #0x00
+ ldir
+
+ ; now install the interrupt vector at 0x0038
+ ld a, #0xC3 ; JP instruction
+ ld (0x0038), a
+ ld hl, #interrupt_handler
+ ld (0x0039), hl
+
+ ; set restart vector for UZI system calls
+ ld (0x0030), a ; (rst 30h is unix function call vector)
+ ld hl, #unix_syscall_entry
+ ld (0x0031), hl
+
+ ; Set vector for jump to NULL
+ ld (0x0000), a
+ ld hl, #null_handler ; to Our Trap Handler
+ ld (0x0001), hl
+
+ ld (0x0066), a ; Set vector for NMI
+ ld hl, #nmi_handler
+ ld (0x0067), hl
+
+ ; our platform has a "true" common area, if it did not we would
+ ; need to copy the "common" code into the common area of the new
+ ; process.
+
+ ; falls through
+
+ ; Map functions are trivial for now
+ ; FIXME: will need to play with BANKR once we ROM the image
+map_kernel:
+map_process:
+map_process_always:
+map_save:
+map_restore:
+ ret
+
+; outchar: Wait for UART TX idle, then print the char in A
+outchar:
+ push af
+outcharw:
+ in a, (0x15)
+ bit 0, a
+ jr z, outcharw
+ pop af
+ out (0x14), a
+ ret
+
+;
+; RAM disc
+;
+_hd_xferin:
+;
+; Load the full 24 bit address to start
+;
+ xor a ; always aligned
+ out (0x90), a
+ ld hl, #_ramd_off + 1
+ ld de, (_ramd_size)
+hd_xiloop:
+ ld a, (hl)
+ out (0x91), a
+ inc hl
+ ld a, (hl)
+ out (0x92), a
+ ld bc, #0x93
+;
+; With a 256 byte block it autoincrements
+;
+ ld hl, (_ramd_uaddr)
+ inir
+ dec d
+ ret z
+;
+; Move on a block
+;
+ ld (_ramd_uaddr), hl
+ ld hl, #_ramd_off + 1
+ inc (hl)
+ jr nz, hd_xiloop
+ inc hl
+ inc (hl)
+ dec hl
+ jr hd_xiloop
+
+_hd_xferout:
+;
+; Load the full 24 bit address to start
+;
+ xor a ; always aligned
+ out (0x90), a
+ ld hl, #_ramd_off + 1
+ ld de, (_ramd_size)
+hd_xoloop:
+ ld a, (hl)
+ out (0x91), a
+ inc hl
+ ld a, (hl)
+ out (0x92), a
+ ld bc, #0x93
+;
+; With a 256 byte block it autoincrements
+;
+ ld hl, (_ramd_uaddr)
+ otir
+ dec d
+ ret z
+;
+; Move on a block
+;
+ ld (_ramd_uaddr), hl
+ ld hl, #_ramd_off + 1
+ inc (hl)
+ jr nz, hd_xoloop
+ inc hl
+ inc (hl)
+ dec hl
+ jr hd_xiloop
--- /dev/null
+; 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 _doexec
+ .globl _dofork
+ .globl _runticks
+ .globl unix_syscall_entry
+ .globl interrupt_handler
+ .globl dispatch_process_signal
+ .globl _swapper
+ .globl _swapout
+
+ ; imported debug symbols
+ .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex
+
+ .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().
+;
+; FIXME: make sure we optimise the switch to self case higher up the stack!
+;
+; 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
+swapped: .ascii "_switchin: SWAPPED"
+ .db 13, 10, 0
+
+_switchin:
+ di
+ pop bc ; return address
+ pop de ; new process pointer
+;
+; FIXME: do we actually *need* to restore the stack !
+;
+ push de ; restore stack
+ push bc ; restore stack
+
+ push de
+ ld hl, #P_TAB__P_PAGE_OFFSET
+ add hl, de ; process ptr
+ pop de
+
+ ld a, (hl)
+
+ or a
+ jr nz, not_swapped
+
+ ;
+ ; We are still on the departing processes stack, which is
+ ; fine for now.
+ ;
+ ld sp, #_swapstack
+ push hl
+ ; We will always swap out the current process
+ ld hl, (U_DATA__U_PTAB)
+ push hl
+ call _swapout
+ pop hl
+ pop hl
+ push de
+ call _swapper
+ pop de
+ pop hl
+ ld a, (hl)
+
+not_swapped:
+ ; check u_data->u_ptab matches what we wanted
+ ld hl, (U_DATA__U_PTAB) ; u_data->u_ptab
+ or a ; clear carry flag
+ sbc hl, de ; subtract, result will be zero if DE==IX
+ jr nz, switchinfail
+
+ ; wants optimising up a bit
+ ld ix, (U_DATA__U_PTAB)
+ ; next_process->p_status = P_RUNNING
+ ld P_TAB__P_STATUS_OFFSET(ix), #P_RUNNING
+
+ ; Fix the moved page pointers
+ ; Just do one byte as that is all we use on this platform
+ ld a, P_TAB__P_PAGE_OFFSET(ix)
+ ld (U_DATA__U_PAGE), a
+ ; 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:
+ call outhl
+ ld hl, #badswitchmsg
+ call outstring
+ ; something went wrong and we didn't switch in what we asked for
+ jp _trap_monitor
+
+fork_proc_ptr: .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry
+
+;
+; Called from _fork. We are in a syscall, the uarea is live as the
+; parent uarea. The kernel is the mapped object.
+;
+_dofork:
+ ; always disconnect the vehicle battery before performing maintenance
+ di ; should already be the case ... belt and braces.
+
+ pop de ; return address
+ pop hl ; new process p_tab*
+ push hl
+ push de
+
+ ld (fork_proc_ptr), hl
+
+ ; prepare return value in parent process -- HL = p->p_pid;
+ ld de, #P_TAB__P_PID_OFFSET
+ add hl, de
+ ld a, (hl)
+ inc hl
+ ld h, (hl)
+ ld l, a
+
+ ; Save the stack pointer and critical registers.
+ ; When this process (the parent) is switched back in, it will be as if
+ ; it returns with the value of the child's pid.
+ push hl ; HL still has p->p_pid from above, the return value in the parent
+ push ix
+ push iy
+
+ ; save kernel stack pointer -- when it comes back in the parent we'll be in
+ ; _switchin which will immediately return (appearing to be _dofork()
+ ; returning) and with HL (ie return code) containing the child PID.
+ ; Hurray.
+ ld (U_DATA__U_SP), sp
+
+ ; now we're in a safe state for _switchin to return in the parent
+ ; process.
+
+ ld hl, (U_DATA__U_PTAB)
+ push hl
+ call _swapout
+ pop hl
+
+ ; 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.
+ ;
+ ; And we exit, with the kernel mapped, the child now being deemed
+ ; to be the live uarea. The parent is frozen in time and space as
+ ; if it had done a switchout().
+ ret
+;
+; We can keep a stack in common because we will complete our
+; use of it before we switch common block. In this case we have
+; a true common so it's even easier.
+;
+ .ds 128
+_swapstack:
--- /dev/null
+-mwxuy
+-i uzi.ihx
+-b _DISCARD=0x5000
+-b _UDATA=0xFD00
+-b _CODE=0x6000
+-b _DATA=0xE000
+-k /usr/share/sdcc/lib/z80
+-l z80
+platform-px4plus/crt0.rel
+platform-px4plus/commonmem.rel
+platform-px4plus/px4plus.rel
+platform-px4plus/main.rel
+start.rel
+version.rel
+lowlevel-z80.rel
+platform-px4plus/tricks.rel
+timer.rel
+kdata.rel
+usermem.rel
+platform-px4plus/devfd.rel
+platform-px4plus/devices.rel
+devio.rel
+filesys.rel
+process.rel
+inode.rel
+syscall_fs.rel
+syscall_fs2.rel
+syscall_proc.rel
+syscall_other.rel
+tty.rel
+mm.rel
+simple.rel
+swap.rel
+devsys.rel
+platform-px4plus/devlpr.rel
+platform-px4plus/devtty.rel
+-e