-TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-zx128 platform-trs80 platform-z80pack platform-z80pack-lite platform-z80pack32 platform-dragon platform-tgl6502 platform-plus3
+TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-zx128 platform-trs80 platform-z80pack platform-z80pack-lite platform-z80pack32 platform-dragon platform-tgl6502 platform-plus3 platform-zeta-v2
#export TARGET = 8086test
#export TARGET = atarist
#export TARGET = nc100
#export TARGET = p112
#export TARGET = pcw8256
-export TARGET = plus3
+#export TARGET = plus3
#export TARGET = px4plus
#export TARGET = tgl6502
#export TARGET = trs80
#export TARGET = ubee
#export TARGET = z80pack
#export TARGET = z80pack-lite
-#export TARGET= zx128
+#export TARGET = zeta-v2
+#export TARGET = zx128
export VERSION = "0.1"
export SUBVERSION = "ac1"
--- /dev/null
+ASRCS = crt0.s tricks.s commonmem.s zeta-v2.s monitor.s
+ASRCS += ds1302-n8vem.s devrd_hw.s
+CSRCS = devices.c main.c devtty.c devrd.c
+DISCARD_CSRCS = discard.c
+DISCARD_DSRCS = ../dev/ds1302_discard.c
+DSRCS = ../dev/devsd.c ../dev/mbr.c ../dev/blkdev.c ../dev/ds1302.c
+
+AOBJS = $(ASRCS:.s=.rel)
+COBJS = $(CSRCS:.c=.rel)
+DISCARD_COBJS = $(DISCARD_CSRCS:.c=.rel)
+DISCARD_DOBJS = $(patsubst ../dev/%.c,%.rel, $(DISCARD_DSRCS))
+DOBJS = $(patsubst ../dev/%.c,%.rel, $(DSRCS))
+
+OBJS = $(AOBJS) $(COBJS) $(DOBJS) $(DISCARD_DOBJS) $(DISCARD_COBJS)
+
+CROSS_CCOPTS += -I../dev/
+
+JUNK = *.rel *.lst *.asm *.sym *.rst *.map *.ihx *.bin
+
+all: $(OBJS)
+
+$(AOBJS): %.rel: %.s
+ $(CROSS_AS) $(ASOPTS) $<
+
+$(COBJS): %.rel: %.c
+ $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(DOBJS): %.rel: ../dev/%.c
+ $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(DISCARD_COBJS): %.rel: %.c
+ $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $<
+
+$(DISCARD_DOBJS): %.rel: ../dev/%.c
+ $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $<
+
+clean:
+ rm -f $(OBJS) $(JUNK) core *~ bootrom.ihx bootrom.bin
+
+image:
+ sdasz80 -o bootrom.s
+ sdldz80 -m -i bootrom.rel
+ makebin -s 256 bootrom.ihx > bootrom.bin
+ cat bootrom.bin ../fuzix.bin > ../fuzix.rom
--- /dev/null
+--- README for Zeta SBC V2 ---
+This is Fuzix for the Zeta SBV V2
+Written by Sergey Kiselev <skiselev@gmail.com>
+Heavily based on prior work by Will Sowerbutts <will@sowerbutts.com> and others
+
+Supported hardware:
+ - RAM disk
+ - RS232 serial port (16550 UART, tty1)
+ - Real time clock (should be, not tested)
+
+Memory allocation (16 KiB pages):
+- ROM pages: 0 - 31
+ - Kernel image: 0 - 2 - copied by bootrom.s to pages 32 - 34
+ - RAM disk image: 3 - 19 - copied by bootrom.s to pages 48 - 63
+- RAM pages: 32 - 63
+ - Kernel pages: 32 - 34
+ - Common page: 35
+ - User space pages: 36 - 47
+ - RAM disk: 48 - 63
+
+To build the kernel, edit the TARGET line in Kernel/Makefile to read:
+ export TARGET = zeta-v2
+Then run "make clean; make all" in the "Kernel" directory.
+
+Currently the system can be booted from the Flash ROM. Use the following steps
+to build the Flash ROM image:
+
+1. Build the kernel (see above)
+2. Build the userspace utils (needs to be documented)
+3. [In FUZIX/Kernel dir] Create a 256 KiB filesystem image:
+ ../Standalone/mkfs zeta_v2_rootfs.img 16 512
+4. Populate the filesystem image
+ ../Standalone/ucp zeta_v2_rootfs.img
+
+run the following ucp commands:
+
+mkdir bin
+chmod 0755 bin
+mkdir etc
+chmod 0755 etc
+mkdir dev
+chmod 0755 dev
+mkdir tmp
+chmod 01777 tmp
+mkdir usr
+chmod 0755 usr
+mkdir usr/lib
+chmod 0755 usr/lib
+mkdir root
+chmod 0700 root
+cd /dev
+mknod tty1 20660 513
+mknod tty2 20660 514
+mknod tty3 20660 515
+mknod tty4 20660 516
+mknod hda 60660 0
+mknod hda1 60660 1
+mknod fd0 60660 256
+mknod fd1 60660 257
+mknod null 20666 1024
+mknod mem 20660 1025
+mknod zero 20444 1026
+mknod proc 20660 1027
+cd /
+bget ../Applications/util/init
+chmod 0755 init
+cd /etc
+bget passwd
+chmod 0644 passwd
+cd /bin
+bget ../Applications/util/cat
+chmod 0755 cat
+bget ../Applications/util/cp
+chmod 0755 cp
+bget ../Applications/util/date
+chmod 0755 date
+bget ../Applications/util/df
+chmod 0755 df
+bget ../Applications/util/echo
+chmod 0755 echo
+bget ../Applications/util/fdisk
+chmod 0755 fdisk
+bget ../Applications/util/fsck
+chmod 0755 fsck
+bget ../Applications/util/ln
+chmod 0755 ln
+bget ../Applications/util/ls
+chmod 0755 ls
+bget ../Applications/util/mkdir
+chmod 0755 mkdir
+bget ../Applications/util/mkfs
+chmod 0755 mkfs
+bget ../Applications/util/mount
+chmod 0755 mount
+bget ../Applications/util/mv
+chmod 0755 mv
+bget ../Applications/util/passwd
+chmod 0755 passwd
+bget ../Applications/util/rm
+chmod 0755 rm
+bget ../Applications/util/rmdir
+chmod 0755 rmdir
+bget ../Applications/util/ssh
+chmod 0755 ssh
+bget ../Applications/util/umount
+chmod 0755 umount
+exit
+
+5. Create ROM image:
+dd if=fuzix.rom of=fuzix_48k.rom bs=48k count=1 conv=syn
+cat fuzix_48k.rom zeta_v2_rootfs.img > fuzix_zeta_v2_bootfs.rom
+
+6. Program the image (fuzix_zeta_v2_bootfs.rom) to the Flash ROM using an
+EPROM programmer
+
+7. Power on the the system. Enter device number 256 to boot from the RAM disk.
+
--- /dev/null
+;
+; ROM boot for FUZIX on the ZETA SBC V2
+;
+ .module bootrom
+ .include "kernel.def"
+
+ .area _LOADER (ABS)
+ .org 0x0000
+start:
+ ; map ROM page 0 to bank #0 and enable paging
+ di ; better be safe than sorry
+ xor a
+ out (MPGSEL_0),a ; map page 0 (ROM) to bank #0
+ ld a,#1
+ out (MPGENA),a ; enable paging
+
+ ; copy FUZIX kernel to RAM
+ ; 3 pages, starting from ROM page 0, RAM page 32
+ xor a
+kernel_copy:
+ out (MPGSEL_1),a ; map ROM page to bank #1
+ add #32 ; RAM page = ROM page + 32
+ out (MPGSEL_2),a ; map RAM page to bank #2
+ ld hl,#0x4000 ; source - bank #1 offset
+ ld de,#0x8000 ; destination - bank #2 offset
+ ld bc,#0x4000 ; count - 16 KiB
+ ldir ; copy it
+ sub a,#31 ; next ROM page = RAM page - 32 + 1
+ cp #3 ; are we there yet (RAM page == 3?)
+ jr nz,kernel_copy
+
+ ; copy data to RAM disk
+ ; 16 pages, starting from ROM page 3, RAM page 48
+ ld a,#3
+ramdisk_copy:
+ out (MPGSEL_1),a ; map ROM page to bank #1
+ add #45 ; RAM page = ROM page + 45
+ out (MPGSEL_2),a ; map RAM page to bank #2
+ ld hl,#0x4000 ; source - bank #1 offset
+ ld de,#0x8000 ; destination - bank #2 offset
+ ld bc,#0x4000 ; count - 16 KiB
+ ldir ; copy it
+ sub #44 ; next ROM page = RAM page - 45 + 1
+ cp #19 ; are we there yet (RAM page == 3 + 16?)
+ jr nz,ramdisk_copy
+
+ ; scary... switching memory bank under our feet
+ ld a,#32 ; map page 32 (RAM) to bank 0
+ out (MPGSEL_0),a
+ inc a ; map page 33 (RAM+16k) to bank 1
+ out (MPGSEL_1),a
+ inc a ; map page 34 (RAM+32K) to bank 2
+ out (MPGSEL_2),a
+ inc a ; map page 35 (RAM+48K) to bank 3
+ out (MPGSEL_3),a
+ jp 0x100 ; jump to crt0.s
+; pad
+ .ds (0x100-(.-start))
--- /dev/null
+;
+; Common on z80pack is at 0xF000 as defined by hardware.
+;
+
+ .module commonmem
+
+ .area _COMMONMEM
+
+ .include "../cpu-z80/std-commonmem.s"
--- /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
+/* Flexible 4x16K banking */
+#define CONFIG_BANK16
+/* Permit large I/O requests to bypass cache and go direct to userspace */
+#define CONFIG_LARGE_IO_DIRECT
+/* 32 x 16K pages, 3 pages for kernel, 16 pages for RAM disk */
+#define MAX_MAPS 13
+
+/* Banks as reported to user space */
+#define CONFIG_BANKS 4
+
+#define TICKSPERSEC 15 /* Ticks per second */
+#define PROGBASE 0x0000 /* also data base */
+#define PROGLOAD 0x0100 /* also data base */
+#define PROGTOP 0xF000 /* Top of program, base of U_DATA copy */
+#define PROC_SIZE 64 /* Memory needed per process */
+
+/* WRS: this is probably wrong -- we want to swap the full 64K minus the common code */
+/* For now let's just use something and fix this up later when we have a swap device */
+#define SWAP_SIZE 0x7F /* 63.5K in blocks (which is the wrong number) */
+#define SWAPBASE 0x0000 /* start at the base of user mem */
+#define SWAPTOP 0xFF00 /* can we stop at the top? not sure how. let's stop short. */
+#define MAX_SWAPS 10 /* Well, that depends really, hmmmmmm. Pick a number, any number. */
+
+/* We need a tidier way to do this from the loader */
+#define CMDLINE NULL /* Location of root dev name */
+#define BOOTDEVICENAMES "fd,hd#"
+
+//#define SWAPDEV (256 + 1) /* Device for swapping */
+#define NBUFS 10 /* Number of block buffers */
+#define NMOUNTS 4 /* Number of mounts at a time */
+
+#define MAX_BLKDEV 4 /* 1 ROM disk, 1 RAM disk, 1 floppy, 1 SD card */
+
+/* On-board DS1302, we can read the time of day from it */
+#define CONFIG_RTC
+#define CONFIG_RTC_INTERVAL 30 /* deciseconds between reading RTC seconds counter */
+
+/* Optional ParPortProp board connected to PPI */
+//#define CONFIG_PPP /* #define CONFIG_PPP to enable as tty3 */
+
+/* Device parameters */
+#define NUM_DEV_RD 1
+#define DEV_RD_PAGES 16 /* size of the RAM disk in pages */
+#define DEV_RD_START 48 /* first page used by the RAM disk */
+
+#ifdef CONFIG_PPP
+ /* SD card in ParPortProp */
+ #define DEVICE_SD
+ #define SD_DRIVE_COUNT 1
+ #define NUM_DEV_TTY 2
+
+ /* ParPortProp as the console */
+ #define BOOT_TTY (512 + 2)
+#else
+ #define NUM_DEV_TTY 1
+
+ /* UART0 as the console */
+ #define BOOT_TTY (512 + 1)
+#endif
+
+#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
--- /dev/null
+; 2015-02-20 Sergey Kiselev
+; 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 _HOME ; compiler stores __mullong etc in here if you use them
+ .area _CODE2
+ .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 _DISCARD
+ .area _COMMONMEM
+
+ ; imported symbols
+ .globl _fuzix_main
+ .globl init_early
+ .globl init_hardware
+ .globl s__INITIALIZER
+ .globl s__COMMONMEM
+ .globl l__COMMONMEM
+ .globl s__DISCARD
+ .globl l__DISCARD
+ .globl s__DATA
+ .globl l__DATA
+ .globl kstack_top
+
+ .include "kernel.def"
+
+ ; startup code
+ .area _CODE
+init:
+ di
+ ld sp, #kstack_top
+ ; move the common memory where it belongs
+ ld hl, #s__INITIALIZER
+ ld de, #s__COMMONMEM
+ ld bc, #l__COMMONMEM
+ ldir
+ ; and the discard
+ ld de, #s__DISCARD
+ ld bc, #l__DISCARD
+ ldir
+ ; then zero the data area
+ ld hl, #s__DATA
+ ld de, #s__DATA + 1
+ ld bc, #l__DATA - 1
+ ld (hl), #0
+ ldir
+
+ ; setup the rest of the memory paging
+ call init_early
+
+ ; Hardware setup
+ call init_hardware
+
+ ; Call the C main routine
+ call _fuzix_main
+
+ ; fuzix_main() shouldn't return, but if it does...
+ di
+stop: halt
+ jr stop
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <tty.h>
+#include <devsys.h>
+#include <devrd.h>
+#include <devtty.h>
+#include <blkdev.h>
+#include <ds1302.h>
+
+struct devsw dev_tab[] = /* The device driver switch table */
+{
+/* open close read write ioctl */
+ /* 0: /dev/fd - Floppy disk block devices */
+ { no_open, no_close, no_rdwr, no_rdwr, no_ioctl},
+ /* 1: /dev/hd - RAM disk interface */
+ { rd_open, no_close, rd_read, rd_write, no_ioctl},
+ /* 2: /dev/tty -- serial ports */
+ { tty_open, tty_close, tty_read, tty_write, tty_ioctl},
+ /* 3: unused slot */
+ { no_open, no_close, no_rdwr, no_rdwr, no_ioctl},
+ /* 4: /dev/mem etc System devices (one offs) */
+ { no_open, no_close, sys_read, sys_write, sys_ioctl},
+};
+
+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)
+{
+ ds1302_init();
+}
--- /dev/null
+/* Zeta SBC V2 RAM disk driver
+ *
+ * Implements a single RAM disk DEV_RD_PAGES size and
+ * starting from DEV_RD_START page
+ *
+ * */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+
+extern uint8_t src_page; /* source page number */
+extern uint8_t dst_page; /* destination page number */
+extern uint16_t src_offset; /* offset of the data in the source page */
+extern uint16_t dst_offset; /* offset of the data in the destination page */
+extern uint16_t cpy_count; /* data transfer length */
+extern uint8_t kernel_pages[]; /* kernel's page table */
+
+int ramdisk_transfer(bool is_read, uint8_t minor, uint8_t rawflag);
+int page_copy(void); /* assembler code */
+
+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) */
+ uint32_t rd_addr;
+ uint16_t buffer_addr;
+ usize_t xfer_count;
+
+ if (minor >= NUM_DEV_RD) {
+ udata.u_error = ENXIO;
+ return -1;
+ }
+
+ if (rawflag) { /* rawflag == 1, read to or write from user space */
+ xfer_count = udata.u_count;
+ buffer_addr = (uint16_t) udata.u_base;
+ block = udata.u_offset >> 9;
+ block_xfer = xfer_count >> 9;
+ } else { /* rawflag == 0, read to or write from kernel space */
+ xfer_count = 512;
+ buffer_addr = (uint16_t) udata.u_buf->bf_data;
+ block = udata.u_buf->bf_blk;
+ block_xfer = 1;
+ }
+
+ if (block > (DEV_RD_PAGES * 16 * 2)) { /* block beyond RAM disk end? */
+ udata.u_error = EIO;
+ return -1;
+ }
+
+ /* calculate physical address of the RAM drive data */
+ /* rd_addr = block * 512 + DEV_RD_START * 16K; */
+ rd_addr = ((unsigned long) block << 9) + DEV_RD_START * 16384UL;
+ while (xfer_count > 0) {
+ if (is_read) {
+ /* RAM disk page number = rd_addr / 16K */
+ src_page = rd_addr >> 14;
+ /* offset within RAM disk page */
+ src_offset = rd_addr & 0x3FFF;
+ /* destination page number */
+ if (rawflag)
+ dst_page = ((uint8_t *) &udata.u_page)[buffer_addr >> 14];
+ else
+ dst_page = kernel_pages[buffer_addr >> 14];
+ /* offset in the destination page */
+ dst_offset = buffer_addr & 0x3FFF;
+ } else {
+ /* source page number */
+ if (rawflag)
+ src_page = ((uint8_t *) &udata.u_page)[buffer_addr >> 14];
+ else
+ src_page = kernel_pages[buffer_addr >> 14];
+ /* offset in the source page */
+ src_offset = buffer_addr & 0x3FFF;
+ /* RAM disk page number = rd_addr / 16K */
+ dst_page = rd_addr >> 14;
+ /* offset within RAM disk page */
+ dst_offset = rd_addr & 0x3FFF;
+ }
+ cpy_count = xfer_count;
+ if (cpy_count > 16384 - src_offset)
+ cpy_count = 16384 - src_offset;
+ if (cpy_count > 16384 - dst_offset)
+ cpy_count = 16384 - dst_offset;
+#ifdef DEBUG
+ kprintf("page_cpy(src_page=%x, src_offset=%x, dst_page=%x, dst_offset=%x, cpy_count=%x)\n", src_page, src_offset, dst_page, dst_offset, cpy_count);
+#endif
+ __critical {
+ page_copy();
+ }
+ xfer_count -= cpy_count;
+ buffer_addr += cpy_count;
+ rd_addr += cpy_count;
+ }
+
+ return block_xfer;
+}
+
+
+int rd_open(uint8_t minor)
+{
+ if(minor < NUM_DEV_RD){
+ return 0;
+ } else {
+ udata.u_error = EIO;
+ return -1;
+ }
+}
--- /dev/null
+#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__ */
--- /dev/null
+ .module devrd_hw
+
+ ; imported symbols - from zeta-v2.s
+ .globl map_table
+
+ ; exported symbols (used by devrd.c)
+ .globl _page_copy
+ .globl _src_page, _src_offset, _dst_page, _dst_offset, _cpy_count
+
+ .include "kernel.def"
+
+ .area _COMMONMEM
+
+;=========================================================================
+; _page_copy - Copy data from one physical page to another
+; Inputs:
+; _src_page - page number of the source page (uint8_t)
+; _src_offset - offset in the source page (uint16_t)
+; _dst_page - page number of the destination page (uint8_t)
+; _dst_offset - offset in the destination page (uint16_t)
+; _cpy_count - number of bytes to copy (uint16_t)
+; map_table - current page register values, used to restore paging registers
+; Outputs:
+; Data copied
+; Destroys AF, BC, DE, HL
+;=========================================================================
+_page_copy:
+ ld a,(_src_page)
+ out (MPGSEL_1),a ; map source page to bank #1
+ ld a,(_dst_page)
+ out (MPGSEL_2),a ; map destination page to bank #2
+ ld hl,(_src_offset) ; load offset in source page
+ ld a,#0x40 ; add bank #1 offset - 0x4000
+ add h ; to the source offset
+ ld h,a
+ ld de,(_dst_offset)
+ ld a,#0x80 ; add bank #2 offset - 0x8000
+ add d ; to the destination offset
+ ld d,a
+ ld bc,(_cpy_count) ; bytes to copy
+ ldir ; do the copy
+ ld a,(map_table+1)
+ out (MPGSEL_1),a ; restore the mapping of bank #1
+ ld a,(map_table+2)
+ out (MPGSEL_2),a ; restore the mapping of bank #2
+ ret
+
+; variables
+_src_page:
+ .db 0
+_dst_page:
+ .db 0
+_src_offset:
+ .dw 0
+_dst_offset:
+ .dw 0
+_cpy_count:
+ .dw 0
+;=========================================================================
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <tty.h>
+#include <devtty.h>
+#include <zeta-v2.h>
+
+char tbuf1[TTYSIZ];
+
+#ifdef CONFIG_PPP
+char tbufp[TTYSIZ];
+#endif
+
+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 },
+#ifdef CONFIG_PPP
+ { tbufp, tbufp, tbufp, TTYSIZ, 0, TTYSIZ/2 },
+#endif
+};
+
+/* FIXME: implement */
+void tty_setup(uint8_t minor)
+{
+ minor;
+}
+
+/* FIXME: implement (although /DCD is hardwired to GND) */
+int tty_carrier(uint8_t minor)
+{
+ minor;
+ return 1;
+}
+
+void tty_interrupt(void)
+{
+ uint8_t reg = UART0_LSR;
+ if (reg & 0x01) {
+ /* data available */
+ reg = UART0_RBR;
+ tty_inproc(1, reg);
+ }
+}
+
+#ifdef CONFIG_PPP
+void tty_poll_ppp(void)
+{
+ while(PROPIO2_STAT & 0x20)
+ tty_inproc(3, PROPIO2_TERM);
+}
+#endif
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+ if (minor == 1) {
+ while(!(UART0_LSR & 0x20));
+ UART0_THR = c;
+#ifdef CONFIG_PPP
+ } else if (minor = 2) {
+ /* FIXME: implement */
+#endif
+ }
+}
+
+void tty_sleeping(uint8_t minor)
+{
+ minor;
+}
+
+ttyready_t tty_writeready(uint8_t minor)
+{
+ uint8_t reg;
+ if (minor == 1) {
+ reg = UART0_LSR;
+ return (reg & 0x20) ? TTY_READY_NOW : TTY_READY_SOON;
+ }
+ return TTY_READY_NOW;
+}
+
+/* kernel writes to system console -- never sleep! */
+void kputchar(char c)
+{
+ tty_putc(TTYDEV - 512, c);
+ if(c == '\n')
+ tty_putc(TTYDEV - 512, '\r');
+}
--- /dev/null
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+void tty_putc(uint8_t minor, unsigned char c);
+void tty_interrupt(void);
+
+#ifdef CONFIG_PPP
+void tty_poll_ppp(void);
+#endif
+#endif
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include "config.h"
+
+void pagemap_init(void)
+{
+ int i;
+
+ /* ZETA SBC V2 has RAM in the top 512 KiB of physical memory
+ * corresponding pages are 32-63 (page size is 16 KiB)
+ * Pages 32-34 are used by the kernel
+ * Page 35 is the common area
+ * Pages starting from DEV_RD_START are used by RAM disk
+ */
+ for (i = 32 + 4; i < DEV_RD_START; i++)
+ pagemap_add(i);
+
+ /* finally add the common area */
+ pagemap_add(32 + 3);
+}
+
+void map_init(void)
+{
+}
--- /dev/null
+; 2015-02-19 Sergey Kiselev
+; 2014-12-31 William R Sowerbutts
+; N8VEM SBC / Zeta SBC DS1302 real time clock interface code
+
+ .module ds1302-n8vem
+
+ ; exported symbols
+ .globl _ds1302_set_pin_ce
+ .globl _ds1302_set_pin_clk
+ .globl _ds1302_set_pin_data
+ .globl _ds1302_set_pin_data_driven
+ .globl _ds1302_get_pin_data
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+; -----------------------------------------------------------------------------
+; DS1302 interface
+; -----------------------------------------------------------------------------
+
+N8VEM_RTC = 0x70
+PIN_CE = 0x10
+PIN_DATA_HIZ = 0x20
+PIN_CLK = 0x40
+PIN_DATA_OUT = 0x80
+PIN_DATA_IN = 0x01
+
+.area _DATA
+
+rtc_shadow: .db 0 ; we can't read back the latch contents, so we must keep a copy
+
+.area _CODE
+
+_ds1302_get_pin_data:
+ in a, (N8VEM_RTC) ; read input register
+ and #PIN_DATA_IN ; mask off data pin
+ ld l, a ; return result in L
+ ret
+
+_ds1302_set_pin_data_driven:
+ ld hl, #2 ; get address of function argument
+ add hl, sp
+ ld b, (hl) ; load argument from stack
+ ld a, (rtc_shadow)
+ and #~PIN_DATA_HIZ ; 0 - output pin
+ bit 0, b ; test bit
+ jr nz, writereg
+ or #PIN_DATA_HIZ
+ jr writereg
+
+_ds1302_set_pin_data:
+ ld bc, #(((~PIN_DATA_OUT) << 8) | PIN_DATA_OUT)
+ jr setpin
+
+_ds1302_set_pin_ce:
+ ld bc, #(((~PIN_CE) << 8) | PIN_CE)
+ jr setpin
+
+_ds1302_set_pin_clk:
+ ld bc, #(((~PIN_CLK) << 8) | PIN_CLK)
+ jr setpin
+
+setpin:
+ ld a, (rtc_shadow) ; load current register contents
+ and b ; unset the pin
+ ld hl, #2 ; get address of function argument
+ add hl, sp
+ ld b, (hl) ; load argument from stack
+ bit 0, b ; test bit
+ jr z, writereg ; arg is false
+ or c ; arg is true
+writereg:
+ out (N8VEM_RTC), a ; write out new register contents
+ ld (rtc_shadow), a ; update our shadow copy
+ ret
+
--- /dev/null
+; UZI mnemonics for memory addresses etc
+
+U_DATA .equ 0xF000 ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE .equ 0x300 ; 256+256+256 bytes.
+Z80_TYPE .equ 0 ; just an old good Z80
+;RAM_KB .equ 512
+RAM_KB .equ 256
+USE_FANCY_MONITOR .equ 1 ; disabling this saves around approx 0.5KB
+
+PROGBASE .equ 0x0000
+PROGLOAD .equ 0x0100
+
+; Zeta SBC V2 mnemonics for I/O ports etc
+
+CONSOLE_RATE .equ 38400
+
+; Z80 CTC ports
+CTC_CH0 .equ 0x20 ; CTC channel 0 and interrupt vector
+CTC_CH1 .equ 0x21 ; CTC channel 1 (periodic interrupts)
+CTC_CH2 .equ 0x22 ; CTC channel 2 (UART interrupt)
+CTC_CH3 .equ 0x23 ; CTC channel 3 (PPI interrupt)
+
+; 37C65 FDC ports
+FDC_CCR .equ 0x28 ; Configuration Control Register (W/O)
+FDC_MSR .equ 0x30 ; 8272 Main Status Register (R/O?)
+FDC_DATA .equ 0x31 ; 8272 Data Port (R/W)
+FDC_DOR .equ 0x38 ; Digital Output Register (W/O)
+FDC_TC .equ 0x38 ; Pulse terminal count (R/O)
+
+; 8255 PPI ports
+PPI_BASE .equ 0x60
+PPI_PORTA .equ PPI_BASE + 0 ; Port A
+PPI_PORTB .equ PPI_BASE + 1 ; Port B
+PPI_PORTC .equ PPI_BASE + 2 ; Port C
+PPI_CONTROL .equ PPI_BASE + 3 ; PPI Control Port
+
+; 16550 UART
+UART0_BASE .equ 0x68
+UART0_RBR .equ UART0_BASE + 0 ; DLAB=0: Receiver buffer register (R/O)
+UART0_THR .equ UART0_BASE + 0 ; DLAB=0: Transmitter holding reg (W/O)
+UART0_IER .equ UART0_BASE + 1 ; DLAB=0: Interrupt enable register
+UART0_IIR .equ UART0_BASE + 2 ; Interrupt identification reg (R/0)
+UART0_FCR .equ UART0_BASE + 2 ; FIFO control register (W/O)
+UART0_LCR .equ UART0_BASE + 3 ; Line control register
+UART0_MCR .equ UART0_BASE + 4 ; Modem control register
+UART0_LSR .equ UART0_BASE + 5 ; Line status register
+UART0_MSR .equ UART0_BASE + 6 ; Modem status register
+UART0_SCR .equ UART0_BASE + 7 ; Scratch register
+UART0_DLL .equ UART0_BASE + 0 ; DLAB=1: Divisor latch - low byte
+UART0_DLH .equ UART0_BASE + 1 ; DLAB=1: Divisor latch - high byte
+
+; DS1302 RTC
+N8VEM_RTC .equ 0x70 ; RTC / bit banging (R/W)
+
+; MMU Ports
+MPGSEL_0 .equ 0x78 ; Bank_0 page select register (W/O)
+MPGSEL_1 .equ 0x79 ; Bank_1 page select register (W/O)
+MPGSEL_2 .equ 0x7A ; Bank_2 page select register (W/O)
+MPGSEL_3 .equ 0x7B ; Bank_3 page select register (W/O)
+MPGENA .equ 0x7C ; memory paging enable register, bit 0 (W/O)
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include "config.h"
+
+extern unsigned char irqvector;
+
+void platform_idle(void)
+{
+ /* Let's go to sleep while we wait for something to interrupt us;
+ * Makes the HALT LED go yellow, which amuses me greatly. */
+ __asm
+ halt
+ __endasm;
+}
+
+void platform_interrupt(void)
+{
+ switch(irqvector) {
+ case 1:
+#ifdef CONFIG_PPP
+ tty_poll_ppp()
+#endif
+ timer_interrupt();
+ return;
+ case 2:
+ tty_interrupt();
+ return;
+ default:
+ return;
+ }
+}
--- /dev/null
+; 2015-01-17 William R Sowerbutts
+
+ .module monitor
+ .include "kernel.def"
+ .globl _trap_monitor
+ .globl map_kernel
+
+; -----------------------------------------------------------------------------
+.ifne USE_FANCY_MONITOR ; -----------------------------------------------------
+ .area _CODE ; actual monitor lives in kernel bank
+ .include "../lib/monitor-z80.s"
+
+ .area _COMMONMEM ; just a stub goes in common memory
+_trap_monitor:
+ di
+ call map_kernel
+ jp monitor_entry
+
+
+; -----------------------------------------------------------------------------
+.else ; MICRO MONITOR ---------------------------------------------------------
+ .globl outchar
+ .globl outnewline
+ .globl outhl
+
+ .area _COMMONMEM
+_trap_monitor: di
+ call outnewline
+ ; just dump a few words from the stack
+ ld b, #50
+stacknext: pop hl
+ call outhl
+ ld a, #' '
+ call outchar
+ djnz stacknext
+ halt
+.endif
--- /dev/null
+export CPU = z80
--- /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 map_kernel
+ .globl _ramtop
+
+ ; imported debug symbols
+ .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+ .area _COMMONMEM
+
+; ramtop must be in common for single process swapping cases
+; and its a constant for the others from before init forks so it'll be fine
+; here
+_ramtop:
+ .dw 0
+
+; Switchout switches out the current process, finds another that is READY,
+; possibly the same process, and switches it in. When a process is
+; restarted after calling switchout, it thinks it has just returned
+; from switchout().
+;
+; 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
+
+_switchin:
+ di
+ pop bc ; return address
+ pop de ; new process pointer
+;
+; FIXME: do we actually *need* to restore the stack !
+;
+ push de ; restore stack
+ push bc ; restore stack
+
+ ld hl, #P_TAB__P_PAGE_OFFSET+3 ; Common
+ add hl, de ; process ptr
+ ld a, (hl)
+ out (MPGSEL_3), a ; *CAUTION* our stack just left the building
+
+ ; ------- No stack -------
+ ; check u_data->u_ptab matches what we wanted
+ ld hl, (U_DATA__U_PTAB) ; u_data->u_ptab
+ or a ; clear carry flag
+ sbc hl, de ; subtract, result will be zero if DE==IX
+ jr nz, switchinfail
+
+ ; wants optimising up a bit
+ ld hl, #P_TAB__P_STATUS_OFFSET
+ add hl, de
+ ld (hl), #P_RUNNING
+
+ ; runticks = 0
+ ld hl, #0
+ ld (_runticks), hl
+
+ ; restore machine state -- note we may be returning from either
+ ; _switchout or _dofork
+ ld sp, (U_DATA__U_SP)
+
+ ; ---- New task stack ----
+
+ pop iy
+ pop ix
+ pop hl ; return code
+
+ ; enable interrupts, if the ISR isn't already running
+ ld a, (_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.
+
+ ; --------- we switch stack copies in this call -----------
+ call fork_copy ; copy 0x000 to udata.u_top and the
+ ; uarea and return on the childs
+ ; common
+ ; We are now in the kernel child context
+
+ ; now the copy operation is complete we can get rid of the stuff
+ ; _switchin will be expecting from our copy of the stack.
+ pop bc
+ pop bc
+ pop bc
+
+ ; The child makes its own new process table entry, etc.
+ ld hl, (fork_proc_ptr)
+ push hl
+ call _newproc
+ pop bc
+
+ ; any calls to map process will now map the childs memory
+
+ ; runticks = 0;
+ ld hl, #0
+ ld (_runticks), hl
+ ; in the child process, fork() returns zero.
+ ;
+ ; And we exit, with the kernel mapped, the child now being deemed
+ ; to be the live uarea. The parent is frozen in time and space as
+ ; if it had done a switchout().
+ ret
+
+fork_copy:
+ ld hl, (U_DATA__U_TOP)
+ ld de, #0x0fff
+ add hl, de ; + 0x1000 (-1 for the rounding to follow)
+ ld a, h
+ rlca
+ rlca ; get just the number of banks in the bottom
+ ; bits
+ and #3
+ inc a ; and round up to the next bank
+ ld b, a
+ ; we need to copy the relevant chunks
+ ld hl, (fork_proc_ptr)
+ ld de, #P_TAB__P_PAGE_OFFSET
+ add hl, de
+ ; hl now points into the child pages
+ ld de, #U_DATA__U_PAGE
+ ; and de is the parent
+fork_next:
+ ld a,(hl)
+ out (MPGSEL_1), a ; 0x4000 map the child
+ ld c, a
+ inc hl
+ ld a, (de)
+ out (MPGSEL_2), a ; 0x8000 maps the parent
+ inc de
+ exx
+ ld hl, #0x8000 ; copy the bank
+ ld de, #0x4000
+ ld bc, #0x4000 ; we copy the whole bank, we could optimise
+ ; further
+ ldir
+ exx
+ call map_kernel ; put the maps back so we can look in p_tab
+ djnz fork_next
+ ld a, c
+ out (MPGSEL_3), a ; our last bank repeats up to common
+ ; --- we are now on the stack copy, parent stack is locked away ---
+ ret ; this stack is copied so safe to return on
+
+
--- /dev/null
+#ifndef __ZETA_V2_DOT_H__
+#define __ZETA_V2_DOT_H__
+
+#include "config.h"
+
+#define UART0_BASE 0x68
+__sfr __at (UART0_BASE + 0) UART0_RBR; /* receive buffer register, R/O */
+__sfr __at (UART0_BASE + 0) UART0_THR; /* xmit holding register, W/O */
+__sfr __at (UART0_BASE + 1) UART0_IER; /* interrupt enable register */
+__sfr __at (UART0_BASE + 2) UART0_IIR; /* interrupt ident. register, R/O */
+__sfr __at (UART0_BASE + 2) UART0_FCR; /* FIFO control register, W/O */
+__sfr __at (UART0_BASE + 3) UART0_LCR; /* Line control register */
+__sfr __at (UART0_BASE + 4) UART0_MCR; /* Modem control register */
+__sfr __at (UART0_BASE + 5) UART0_LSR; /* Line status register */
+__sfr __at (UART0_BASE + 6) UART0_MSR; /* Modem status register */
+__sfr __at (UART0_BASE + 7) UART0_SCR; /* Scratch register */
+__sfr __at (UART0_BASE + 0) UART0_DLL; /* Divisor latch - low byte */
+__sfr __at (UART0_BASE + 1) UART0_DLH; /* Divisor latch - high byte */
+
+#endif
--- /dev/null
+; 2014-02-19 Sergey Kiselev
+; Zeta SBC V2 hardware specific code
+
+ .module zeta-v2
+
+ ; exported symbols
+ .globl init_early
+ .globl init_hardware
+ .globl _program_vectors
+ .globl map_kernel
+ .globl map_process
+ .globl map_process_always
+ .globl map_save
+ .globl map_restore
+ .globl _kernel_flag
+ .globl _irqvector
+ .globl platform_interrupt_all
+ .globl map_table
+ .globl _kernel_pages
+
+ ; imported symbols
+ .globl _ramsize
+ .globl _procmem
+ .globl outhl
+ .globl outnewline
+ .globl interrupt_handler
+ .globl unix_syscall_entry
+ .globl nmi_handler
+ .globl null_handler
+
+ ; exported debugging tools
+ .globl inchar
+ .globl outchar
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+;=========================================================================
+; Constants
+;=========================================================================
+CONSOLE_DIVISOR .equ (1843200 / (16 * CONSOLE_RATE))
+CONSOLE_DIVISOR_HIGH .equ (CONSOLE_DIVISOR >> 8)
+CONSOLE_DIVISOR_LOW .equ (CONSOLE_DIVISOR & 0xFF)
+
+;=========================================================================
+; Initialization code
+;=========================================================================
+ .area _DISCARD
+
+init_early:
+ ret
+
+init_hardware:
+ ld hl, #RAM_KB ; set system RAM size
+ ld (_ramsize), hl
+ ld hl,#(RAM_KB-64) ; 64K for kernel
+ ld (_procmem), hl
+
+ ; initialize UART0
+ ld a,#0x80 ; LCR = DLAB ON
+ out (UART0_LCR),a ; set LCR
+ ld a,#CONSOLE_DIVISOR_LOW ; baud rate divisor - low byte
+ out (UART0_DLL),a ; set low byte of divisor
+ ld a,#CONSOLE_DIVISOR_HIGH ; baud rate divisor - high byte
+ out (UART0_DLH),a ; set high byte of divisor
+ ld a,#3 ; value for LCR and MCR
+ out (UART0_LCR),a ; 8 bit data, 1 stop, no parity
+ out (UART0_MCR),a ; DTR ON, RTS ON
+ ld a,#6 ; disable and reset FIFOs
+ out (UART0_FCR),a
+ ld a,#1 ; enable receive data available
+ out (UART0_IER),a ; interrupt
+ ; initialize CTC
+ ld a,#0x47 ; counter mode, disable interrupts
+ out (CTC_CH0),a ; set CH0 mode
+ ld a,#0 ; time constant = 256
+ out (CTC_CH0),a ; set CH0 time constant
+ ld a,#0xC7 ; counter mode, enable interrupts
+ out (CTC_CH1),a ; set CH1 mode
+ ld a,#180 ; time constant = 180
+ out (CTC_CH1),a ; set CH1 time constant
+ ld a,#0xD7 ; counter mode, rising edge
+ ; enable interrupts
+ out (CTC_CH2),a ; set CH2 mode
+ ld a,#1 ; time constant = 1
+ out (CTC_CH2),a ; set CH2 time constant
+ ; FIXME: should use interrupts when PPP firmware allows it
+ ld a,#0x37 ; timer mode for now, disable interrupts
+ out (CTC_CH3),a
+ ld a,#0 ; time constant = 256
+ out (CTC_CH3),a ; set CH3 time constant
+ ld hl,#intvectors
+ ld a,l
+ and #0xF8 ; get bits 7-3 of int. vectors table
+ out (CTC_CH0),a ; send it to CTC
+ ld a,h ; get bits 15-8 of int. vectors table
+ ld i,a ; load to I register
+ im 2 ; set Z80 CPU interrupt mode 2
+
+ ret
+
+;=========================================================================
+; Common Memory (0xF000 upwards)
+;=========================================================================
+ .area _COMMONMEM
+
+;=========================================================================
+; Interrupt stuff
+;=========================================================================
+; IM2 interrupt verctors table
+; Note: this is linked after the udata block, so it is aligned on 256 byte
+; boundary
+intvectors:
+ .dw ctc0_int ; CTC CH0 used as prescaler for CH1
+ .dw ctc1_int ; timer interrupt handler
+ .dw serial_int ; UART interrupt handler
+ .dw ppi_int ; PPI interrupt handler
+
+_irqvector:
+ .db 0 ; used to identify interrupt vector
+
+; CTC CH0 shouldn't be used to generate interrupts
+; but we'll implement it just in case
+ctc0_int:
+ push af
+ xor a ; IRQ vector = 0
+ ld (_irqvector),a ; store it
+ pop af
+ jp interrupt_handler
+
+; periodic timer interrupt
+ctc1_int:
+ push af
+ ld a,#1 ; IRQ vector = 1
+ ld (_irqvector),a ; store it
+ pop af
+ jp interrupt_handler
+
+; UART interrupt
+serial_int:
+ push af
+ ld a,#2 ; IRQ vector = 2
+ ld (_irqvector),a ; store it
+ pop af
+ jp interrupt_handler
+
+; PPI interrupt - not used for now
+ppi_int:
+ push af
+ ld a,#3 ; IRQ vector = 3
+ ld (_irqvector),a ; store it
+ pop af
+ jp interrupt_handler
+
+platform_interrupt_all:
+ ret
+
+; install interrupt vectors
+_program_vectors:
+ di
+ pop de ; temporarily store return address
+ pop hl ; function argument -- base page number
+ push hl ; put stack back as it was
+ push de
+
+ ; At this point the common block has already been copied
+ call map_process
+
+ ; write zeroes across all vectors
+ ld hl,#0
+ ld de,#1
+ ld bc,#0x007f ; program first 0x80 bytes only
+ ld (hl),#0x00
+ ldir
+
+ ; now install the interrupt vector at 0x0038
+ ; Zeta SBC V2 uses IM 2 interrupts, so nothing should hit this vector
+ ; use null_handler just in case something causes it anyway
+ ld a,#0xC3 ; JP instruction
+ ld (0x0038),a
+ ld hl,#null_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
+
+ ld (0x0000),a
+ ld hl,#null_handler ; to Our Trap Handler
+ ld (0x0001),hl
+
+ ld (0x0066),a ; Set vector for NMI
+ ld hl,#nmi_handler
+ ld (0x0067),hl
+
+ jr map_kernel
+
+;=========================================================================
+; Memory management
+; - kernel pages: 32 - 34
+; - common page: 35
+; - user space pages: 36 - 63
+;=========================================================================
+
+;=========================================================================
+; map_process_always - map process pages
+; Inputs: page table address in #U_DATA__U_PAGE
+; Outputs: none; all registers preserved
+;=========================================================================
+map_process_always:
+ push hl
+ ld hl,#U_DATA__U_PAGE
+ call map_process_2
+ pop hl
+ ret
+
+;=========================================================================
+; map_process - map process or kernel pages
+; Inputs: page table address in HL, map kernel if HL == 0
+; Outputs: none; A and HL destroyed
+;=========================================================================
+map_process:
+ ld a,h
+ or l ; HL == 0?
+ jr nz,map_process_2 ; HL == 0 - map the kernel
+
+;=========================================================================
+; map_kernel - map kernel pages
+; Inputs: none
+; Outputs: none; all registers preserved
+;=========================================================================
+map_kernel:
+ push hl
+ ld hl,#_kernel_pages
+ call map_process_2
+ pop hl
+ ret
+
+;=========================================================================
+; map_process_2 - map process or kernel pages
+; Inputs: page table address in HL
+; Outputs: none, HL destroyed
+;=========================================================================
+map_process_2:
+ push de
+ push af
+ ld de,#map_table ; paging registers are write only
+ ; so cache their content in RAM
+ ld a,(hl) ; memory page number for bank #0
+ ld (de),a
+ out (MPGSEL_0),a ; set bank #0
+ inc hl
+ inc de
+ ld a,(hl) ; memory page number for bank #1
+ ld (de),a
+ out (MPGSEL_1),a ; set bank #1
+ inc hl
+ inc de
+ ld a,(hl) ; memory page number for bank #2
+ ld (de),a
+ out (MPGSEL_2),a ; set bank #2
+ pop af
+ pop de
+ ret
+
+;=========================================================================
+; map_restore - restore a saved page mapping
+; Inputs: none
+; Outputs: none, all registers preserved
+;=========================================================================
+map_restore:
+ push hl
+ ld hl,#map_savearea
+ call map_process_2
+ pop hl
+ ret
+
+;=========================================================================
+; map_save - save the current page mapping
+; Inputs: none
+; Outputs: none
+;=========================================================================
+map_save:
+ push hl
+ ld hl,(map_table)
+ ld (map_savearea),hl
+ ld hl,(map_table+2)
+ ld (map_savearea+2),hl
+ pop hl
+ ret
+
+map_table:
+ .db 32,33,34,35
+_kernel_pages:
+ .db 32,33,34,35
+map_savearea:
+ .db 32,33,34,35
+
+; has to live in common
+_kernel_flag:
+ .db 1
+
+;=========================================================================
+; Basic console I/O
+;=========================================================================
+
+;=========================================================================
+; outchar - Wait for UART TX idle, then print the char in A
+; Inputs: A - character to print
+; Outputs: none
+;=========================================================================
+outchar:
+ push af
+ ; wait for transmitter to be idle
+ocloop:
+ in a,(UART0_LSR) ; read Line Status Register
+ and #0x20 ; get THRE bit
+ jr z,ocloop
+ ; now output the char to serial port
+ pop af
+ out (UART0_THR),a
+ ret
+
+;=========================================================================
+; inchar - Wait for character on UART, return in A
+; Inputs: none
+; Outputs: A - received character, F destroyed
+;=========================================================================
+inchar:
+ in a,(UART0_LSR) ; read Line Status Register
+ and #0x01 ; test if data is in receive buffer
+ jp z,inchar ; no data, wait
+ in a,(UART0_RBR) ; read the character from the UART
+ ret