Not yet tested. In theory the changes done are small...
--- /dev/null
+ASRCS = crt0.s tricks.s commonmem.s rc2014.s monitor.s vfdterm.s
+ASRCS += devrd_zeta2_hw.s
+CSRCS = devices.c main.c devtty.c devrd_zeta2.c vfd-debug.c vfd-term.c
+DISCARD_CSRCS = discard.c devtty_discard.c
+DISCARD_DSRCS =
+DSRCS = ../dev/devfd.c ../dev/devsd.c ../dev/mbr.c ../dev/blkdev.c
+DSRCS += ../dev/devrd.c
+DASRCS = ../dev/devfd_hw.s ../dev/devrd_hw.s
+
+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))
+DAOBJS = $(patsubst ../dev/%.s,%.rel, $(DASRCS))
+
+OBJS = $(AOBJS) $(COBJS) $(DOBJS) $(DAOBJS) $(DISCARD_DOBJS) $(DISCARD_COBJS)
+
+CROSS_CCOPTS += -I../dev/
+
+JUNK = *.rel *.lst *.asm *.sym *.rst *.map *.ihx *.bin
+
+all: $(OBJS) diskboot.bin
+
+$(AOBJS): %.rel: %.s
+ $(CROSS_AS) $(ASOPTS) $<
+
+$(COBJS): %.rel: %.c
+ $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(DOBJS): %.rel: ../dev/%.c
+ $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(DAOBJS): %.rel: ../dev/%.s
+ $(CROSS_AS) $(ASOPTS) $@ $<
+
+$(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 diskboot.bin fuzix.com fuzix.rom
+
+diskboot.bin: diskboot.s
+ $(CROSS_AS) $(ASOPTS) diskboot.s
+ sdldz80 -nmi diskboot.rel
+ makebin -s 65536 diskboot.ihx | dd bs=512 skip=125 count=1 of=diskboot.bin
+
+image:
+ sdasz80 -o bootrom.s
+ sdldz80 -m -i bootrom.rel
+ makebin -s 136 bootrom.ihx > bootrom.bin
+# cat bootrom.bin ../fuzix.bin | dd conv=sync bs=65536 count=1 of=fuzix.rom
+ cat bootrom.bin ../fuzix.bin > tmp.rom
+ cat tmp.rom | dd conv=sync bs=65536 count=1 of=fuzix.rom
+ cat fuzix.rom ../rc2014_root_fs > fuzix_rc2014_sio_bootfs.rom
+ ../cpm-loader/makecpmloader ../cpm-loader/cpmload.bin ../fuzix.bin 0x88 fuzix.com
+ ../cpm-loader/makecpmloader ../cpm-loader/fuzixload.bin ../fuzix.bin 0x88 fuzix
+
+cleanup_sio_acia:
+ rm -f devtty.asm devtty.rel main.asm main.rel rc2014.rel
+ rm -f devtty_discard.asm devtty_discard.rel discard.asm discard.rel
--- /dev/null
+--- README for RC2014 ---
+This is Fuzix for the RC2014
+
+Imported into the current tree from Scott Baker's git tree and adjusted to
+build against a current kernel. Don't blame Scott for any bugs!
+
+Modified for RC2014 with FlashROM/Ram board by Scott Baker <smbaker@gmail.com>.
+
+Heavily based on prior work by Will Sowerbutts <will@sowerbutts.com>,
+Sergey Kiselev <skiselev@gmail.com>, and others.
+
+Supported Hardware
+
+ * The Flash ROM / RAM board is required. This board is basically a clone of
+ the memory management subsystem of the Zeta V2. It replaces the RC2014's
+ default ROM and RAM boards.
+
+ * A serial IO board is required. This can either be an SIO/2 board or a
+ 68B50 ACIA board. The 68B50 ACIA is the one that comes standard with the
+ RC2014 kit.
+
+ * VFD Display. If config.h:CONFIG_VFD_TERM is defined, then the VFD Terminal
+ will be supported. This will display all output to the serial port on the
+ VFD. If it's defined and you don't have a VFD, then it probably won't hurt
+ anything, other than some useless io to ports 0-3.
+
+Notes
+
+ * This platform is based heavily on the Zeta V2 platform, so consult the
+ Zeta V2 readme for more instructions. This instructions are intentionally
+ minimal, focusing on items specific to the RC2014 configuration.
+
+Configuration
+
+ * Make sure to edit config.h and kernel.def to specify whether you are
+ using SIO/2 or ACIA.
+
+ * The Makefile assumes a file system image exists in ../rc2014_root_fs.
+ See the Zeta V2 instructions for building a filesystem.
+
+Things that don't work
+
+ * The RC2014 doesn't come with a clock. I added fakeclock.c, which is a
+ clock that always returns 0.
+
+ * I couldn't get the WD37C65 floppy to work.
+
+ * Flow control isn't yet enabled for the serial port.
--- /dev/null
+;
+; ROM boot for FUZIX on the RC2014
+;
+ .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
+ ; 4 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 #4 ; are we there yet (RAM page == 4?)
+ jr nz,kernel_copy
+
+;; ; copy data to RAM disk
+;; ; 16 pages, starting from ROM page 4, RAM page 48
+;; ld a,#4
+;; ramdisk_copy:
+;; out (MPGSEL_1),a ; map ROM page to bank #1
+;; add #44 ; RAM page = ROM page + 44
+;; 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 #43 ; next ROM page = RAM page - 44 + 1
+;; cp #20 ; are we there yet (RAM page == 4 + 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 0x8B ; jump to init_from_rom in crt0
+; pad
+ .ds (0x88-(.-start))
--- /dev/null
+;
+; Common is placed at 0xF000 by fuzix.lnk
+;
+
+ .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, whatever the RAM disk uses */
+#define MAX_MAPS (32 - 3 - DEV_RD_RAM_PAGES)
+
+/* Banks as reported to user space */
+#define CONFIG_BANKS 4
+
+#define TICKSPERSEC 20 /* 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 KERNTOP 0xC000 /* Top of kernel (first 3 banks), base of shared bank */
+#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 "hd#,fd,,rd"
+
+//#define SWAPDEV (256 + 1) /* Device for swapping */
+#define CONFIG_DYNAMIC_BUFPOOL /* we expand bufpool to overwrite the _DISCARD segment at boot */
+#define NBUFS 4 /* Number of block buffers, keep in line with space reserved in zeta-v2.s */
+#define NMOUNTS 4 /* Number of mounts at a time */
+
+#define MAX_BLKDEV 4 /* 1 ROM disk, 1 RAM disk, 1 floppy, 1 PPIDE */
+
+#if 0 /* for now */
+/* 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 */
+#endif
+
+/* Floppy support */
+#define CONFIG_FLOPPY /* #define CONFIG_FLOPPY to enable floppy */
+
+/* Optional ParPortProp board connected to PPI */
+//#define CONFIG_PPP /* #define CONFIG_PPP to enable as tty3 */
+
+#define CONFIG_SIO /* #define CONFIG_SIO to enable SIO support */
+//#define CONFIG_ACIA /* #define CONFIG_SIO to enable ACIA support */
+
+#define CONFIG_VFD_TERM /* #define CONFIG_VFD_TERM to show console output on VFD display */
+
+// sanity check
+#ifdef CONFIG_SIO
+#ifdef CONFIG_ACIA
+#error "please define either CONFIG_SIO or CONFIG_ACIA but not both"
+#endif
+#endif
+
+/* Device parameters */
+#define CONFIG_DEV_MEM /* enable /dev/mem driver */
+
+#define CONFIG_RAMDISK /* enable memory-backed disk driver */
+#define DEV_RD_ROM_PAGES 28 /* size of the ROM disk in 16KB pages (max 32, any unused pages are at the start of the ROM) */
+#define DEV_RD_RAM_PAGES 0 /* size of the RAM disk in 16KB pages */
+
+#define DEV_RD_ROM_START ((uint32_t)(32-DEV_RD_ROM_PAGES) << 14) /* first byte used by the ROM disk */
+#define DEV_RD_RAM_START ((uint32_t)(64-DEV_RD_RAM_PAGES) << 14) /* first byte used by the RAM disk */
+#define DEV_RD_ROM_SIZE ((uint32_t)DEV_RD_ROM_PAGES << 14) /* size of the ROM disk */
+#define DEV_RD_RAM_SIZE ((uint32_t)DEV_RD_RAM_PAGES << 14) /* size of the RAM disk */
+
+#ifdef CONFIG_PPP
+ /* SD card in ParPortProp */
+ #define CONFIG_SD
+ #define SD_DRIVE_COUNT 1
+ #define NUM_DEV_TTY 3
+
+ /* ParPortProp as the console */
+ #define BOOT_TTY (512 + 3)
+#else
+ #define NUM_DEV_TTY 2
+
+ /* UART0 as the console */
+ #define BOOT_TTY (512 + 1)
+ #define TTY_INIT_BAUD B38400
+#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 _INITIALIZED
+ .area _DATA
+ .area _BSEG
+ .area _BSS
+ .area _HEAP
+ ; note that areas below here may be overwritten by the heap at runtime, so
+ ; put initialisation stuff in here
+ .area _BUFFERS ; _BUFFERS grows to consume all before it (up to KERNTOP)
+ .area _INITIALIZER ; binman copies this to the right place for us
+ .area _GSINIT ; unused
+ .area _GSFINAL ; unused
+ .area _DISCARD
+ .area _COMMONMEM
+
+ ; exported symbols
+ .globl init
+ .globl init_from_rom
+ .globl _boot_from_rom
+
+ ; imported symbols
+ .globl _fuzix_main
+ .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: ; must be at 0x88 -- warm boot methods enter here
+ xor a
+ jr init_common
+init_from_rom: ; must be at 0x8B -- bootrom.s enters here
+ ld a, #1
+ ; fall through
+init_common:
+ di
+ ld (_boot_from_rom), a
+ or a
+ jr nz, mappedok ; bootrom.s loads us in the correct pages
+
+ ; move kernel to the correct location in RAM
+ ; note that this cannot cope with kernel images larger than 48KB
+ ld hl, #0x0000
+ ld a, #32 ; first page of RAM is page 32
+movenextbank:
+ out (MPGSEL_3), a ; map page at 0xC000 upwards
+ ld de, #0xC000
+ ld bc, #0x4000 ; copy 16KB
+ ldir
+ inc a
+ cp #35 ; done three pages?
+ jr nz, movenextbank
+
+ ; setup the memory paging for kernel
+ out (MPGSEL_3), a ; map page 35 at 0xC000
+ ld a, #32
+ out (MPGSEL_0), a ; map page 32 at 0x0000
+ inc a
+ out (MPGSEL_1), a ; map page 33 at 0x4000
+ inc a
+ out (MPGSEL_2), a ; map page 34 at 0x8000
+
+mappedok:
+ ; switch to stack in high memory
+ ld sp, #kstack_top
+
+ ; move the common memory where it belongs
+ ld hl, #s__DATA
+ 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
+
+ ; 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
+
+_boot_from_rom: .db 0
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <tty.h>
+#include <devsys.h>
+#include <devfd.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/hd - block device interface */
+#ifdef CONFIG_PPIDE
+ { blkdev_open, no_close, blkdev_read, blkdev_write, blkdev_ioctl},
+#else
+ { no_open, no_close, no_rdwr, no_rdwr, no_ioctl},
+#endif
+ /* 1: /dev/fd - Floppy disk block devices */
+#ifdef CONFIG_FLOPPY
+ { fd_open, fd_close, fd_read, fd_write, no_ioctl},
+#else
+ { no_open, no_close, no_rdwr, no_rdwr, no_ioctl},
+#endif
+ /* 2: /dev/tty -- serial ports */
+ { tty_open, tty_close, tty_read, tty_write, tty_ioctl},
+ /* 3: RAM disk */
+#ifdef CONFIG_RAMDISK
+ { rd_open, no_close, rd_read, rd_write, no_ioctl},
+#else
+ { no_open, no_close, no_rdwr, no_rdwr, no_ioctl},
+#endif
+ /* 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;
+}
--- /dev/null
+/* Zeta SBC V2 memory driver
+ *
+ * 2017-01-03 William R Sowerbutts, based on RAM disk code by Sergey Kiselev
+ */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#define DEVRD_PRIVATE
+#include "devrd.h"
+
+void rd_page_copy(void); // devrd_zeta2_hw.s
+
+void rd_platform_copy(void)
+{
+ uint16_t ocount, count, maxcpy;
+
+ ocount = count = rd_cpy_count;
+
+ while(true){
+ /* ensure transfer will not span a 16KB bank boundary */
+ maxcpy = ((uint16_t)rd_src_address) & 0x3FFF;
+ if((rd_dst_address & 0x3FFF) > maxcpy)
+ maxcpy = rd_dst_address & 0x3FFF;
+ maxcpy = 0x4000 - maxcpy;
+ if(rd_cpy_count > maxcpy)
+ rd_cpy_count = maxcpy;
+#ifdef DEBUG
+ kprintf("rd_transfer: src=0x%lx, dst=0x%x(%s) reverse=%d count=%d\n",
+ rd_src_address, rd_dst_address, rd_dst_userspace?"user":"kern",
+ rd_reverse, rd_cpy_count);
+#endif
+ rd_page_copy();
+
+ count -= rd_cpy_count;
+ if(!count)
+ break;
+
+ rd_dst_address += rd_cpy_count;
+ rd_src_address += rd_cpy_count;
+ rd_cpy_count = count;
+ }
+
+ rd_cpy_count = ocount;
+}
--- /dev/null
+ .module devrd_hw
+
+ ; imported symbols
+ .globl map_kernel, mpgsel_cache, _kernel_pages
+ .globl _rd_platform_copy
+
+ ; exported symbols
+ .globl _rd_page_copy
+ .globl _rd_cpy_count
+ .globl _rd_reverse
+ .globl _rd_dst_userspace
+ .globl _rd_dst_address
+ .globl _rd_src_address
+ .globl _devmem_read
+ .globl _devmem_write
+
+ .include "../kernel.def"
+ .include "kernel.def"
+
+ .area _CODE
+_devmem_write:
+ ld a, #1
+ ld (_rd_reverse), a ; 1 = write
+ jr _devmem_go
+
+_devmem_read:
+ xor a
+ ld (_rd_reverse), a ; 0 = read
+ inc a
+_devmem_go:
+ ld (_rd_dst_userspace), a ; 1 = userspace
+ ; load the other parameters
+ ld hl, (U_DATA__U_BASE)
+ ld (_rd_dst_address), hl
+ ld hl, (U_DATA__U_OFFSET)
+ ld (_rd_src_address), hl
+ ld hl, (U_DATA__U_OFFSET+2)
+ ld (_rd_src_address+2), hl
+ ld hl, (U_DATA__U_COUNT)
+ ld (_rd_cpy_count), hl
+ ; for single byte transfers we can optimise away the outer loop
+ dec l ; test for HL=1
+ ld a, h
+ or l
+ jp nz, _rd_platform_copy ; > 1 byte, do it the hard way
+ call _rd_page_copy ; transfer single byte
+ ld hl, #1 ; return with HL set appropriately
+ ret
+
+ .area _COMMONMEM
+;=========================================================================
+; _rd_page_copy - Copy data from one physical page to another
+; See notes in devrd.h for input parameters
+;=========================================================================
+_rd_page_copy:
+ ; split rd_src_address into page and offset -- it's limited to 20 bits (max 0xFFFFF)
+ ; example address 0x000ABCDE
+ ; in memory it is stored: DE BC 0A 00
+ ; offset would be 0x0ABCDE & 0x3FFF = 0x3CDE
+ ; page would be 0x0ABCDE >> 14 = 0x2A
+
+ ; compute source page number
+ ld a,(_rd_src_address+1) ; load 0xBC -> B
+ ld b, a
+ ld a,(_rd_src_address+2) ; load 0x0A -> A
+ rl b ; grab the top bit into carry
+ rla ; shift accumulator left, load carry bit at the bottom
+ rl b ; and again
+ rla ; now A is the page number (0x2A)
+
+ ; map source page
+ ld (mpgsel_cache+1),a ; save the mapping
+ out (MPGSEL_1),a ; map source page to bank #1
+
+ ; compute source page offset, store in DE
+ ld a,(_rd_src_address+1)
+ and #0x3F ; mask to 16KB
+ or #0x40 ; add offset for bank 1
+ ld d, a
+ ld a,(_rd_src_address+0)
+ ld e, a ; now offset is in DE
+
+ ; compute destination page index (addr 0xABCD >> 14 = 0x02)
+ ld a,(_rd_dst_address+1) ; load top 8 bits
+ and #0xc0 ; mask off top 2 bits
+ rlca ; rotate into lower 2 bits
+ rlca
+ ld b, #0
+ ld c, a ; store in l
+
+ ; look up page number
+ ld a,(_rd_dst_userspace) ; are we loading into userspace memory?
+ or a
+ jr nz, rd_translate_userspace
+ ld hl, #_kernel_pages ; get kernel page table
+ jr rd_do_translate
+rd_translate_userspace:
+ ld hl, #U_DATA__U_PAGE ; get user process page table
+rd_do_translate:
+ add hl, bc ; add index to base ptr (uint8_t *)
+ ld a, (hl) ; load the page number from the page table
+
+ ; map destination page
+ ld (mpgsel_cache+2),a ; save the mapping
+ out (MPGSEL_2),a ; map destination page to bank #2
+
+ ; compute destination page offset, store in HL
+ ld a,(_rd_dst_address+1)
+ and #0x3F ; mask to 16KB
+ or #0x80 ; add offset for bank #2
+ ld h, a
+ ld a, (_rd_dst_address+0)
+ ld l, a ; now offset is in HL
+
+ ; load byte count
+ ld bc,(_rd_cpy_count) ; bytes to copy
+
+ ; check if reversed
+ ld a, (_rd_reverse)
+ or a
+ jr nz, go
+ ex de,hl ; reverse if necessary
+go:
+ ldir ; do the copy
+ jp map_kernel ; map back the kernel
+
+; variables
+_rd_cpy_count:
+ .dw 0 ; uint16_t
+_rd_reverse:
+ .db 0 ; bool
+_rd_dst_userspace:
+ .db 0 ; bool
+_rd_dst_address:
+ .dw 0 ; uint16_t
+_rd_src_address:
+ .db 0 ; uint32_t
+ .db 0
+ .db 0
+ .db 0
+;=========================================================================
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <tty.h>
+#include <devtty.h>
+#include <rc2014.h>
+#include "vfd-term.h"
+#include "vfd-debug.h"
+
+char tbuf1[TTYSIZ];
+char tbuf2[TTYSIZ];
+
+#ifdef CONFIG_PPP
+char tbufp[TTYSIZ];
+#endif
+
+unsigned char sio_type;
+
+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},
+#ifdef CONFIG_PPP
+ {tbufp, tbufp, tbufp, TTYSIZ, 0, TTYSIZ/2},
+#endif
+};
+
+void tty_setup(uint8_t minor)
+{
+ if (minor == 1) {
+ }
+}
+
+int tty_carrier(uint8_t minor)
+{
+// uint8_t c;
+ if (minor == 1) {
+// c = UART0_MSR;
+// return (c & 0x80) ? 1 : 0; /* test DCD */
+ }
+ return 1;
+}
+
+void tty_pollirq_sio(void)
+{
+ uint8_t ca, cb;
+
+ SIOA_C = 0; // read register 0
+ ca = SIOA_C;
+ if (ca & 1) {
+ tty_inproc(1, SIOA_D);
+ }
+ if (ca & 4) {
+ tty_outproc(1);
+ SIOA_C = 5 << 3; // reg 0 CMD 5 - reset transmit interrupt pending
+ }
+
+ SIOB_C = 0; // read register 0
+ cb = SIOB_C;
+ if (cb & 1) {
+ tty_inproc(2, SIOB_D);
+ }
+ if (cb & 4) {
+ tty_outproc(2);
+ SIOB_C = 5 << 3; // reg 0 CMD 5 - reset transmit interrupt pending
+ }
+}
+
+void tty_pollirq_acia(void)
+{
+ uint8_t ca;
+
+ ca = ACIA_C;
+ if (ca & 1) {
+ tty_inproc(1, ACIA_D);
+ }
+ if (ca & 2) {
+ tty_outproc(1);
+ }
+}
+
+#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)
+{
+// while (tty_writeready(minor) != TTY_READY_NOW) ;
+ if (minor == 1) {
+#ifdef CONFIG_SIO
+ SIOA_D = c;
+#endif
+#ifdef CONFIG_ACIA
+ ACIA_D = c;
+#endif
+#ifdef CONFIG_VFD_TERM
+ vfd_term_write(c);
+#endif
+ } else if (minor == 2) {
+ SIOB_D = c;
+#ifdef CONFIG_PPP
+ } else if (minor = 3) {
+ /* FIXME: implement */
+#endif
+ }
+}
+
+void tty_sleeping(uint8_t minor)
+{
+ if (minor == 1) {
+// UART0_IER = 0x0B; /* enable all but LSR interrupt */
+ }
+}
+
+ttyready_t tty_writeready(uint8_t minor)
+{
+ uint8_t c;
+ if (minor == 1) {
+#ifdef CONFIG_SIO
+ SIOA_C = 0; // read register 0
+ c = SIOA_C;
+ if (c & 0x04) /* THRE? */
+ return TTY_READY_NOW;
+ return TTY_READY_SOON;
+#endif
+#ifdef CONFIG_ACIA
+ c = ACIA_C;
+ if (c & 0x02) /* THRE? */
+ return TTY_READY_NOW;
+ return TTY_READY_SOON;
+#endif
+ } else if (minor == 2) {
+#ifdef CONFIG_SIO
+ SIOB_C = 0; // read register 0
+ c = SIOB_C;
+ if (c & 0x04) /* THRE? */
+ return TTY_READY_NOW;
+ return TTY_READY_SOON;
+#endif
+ }
+ 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__
+
+#define SIO_SIO 1
+
+void tty_putc(uint8_t minor, unsigned char c);
+void tty_pollirq_sio(void);
+void tty_pollirq_acia(void);
+
+void sio_init(void);
+void acia_init(void);
+
+#ifdef CONFIG_PPP
+void tty_poll_ppp(void);
+#endif
+#endif
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <tty.h>
+#include <devtty.h>
+#include <rc2014.h>
+
+extern unsigned char sio_type;
+
+/* uart0_init - detect UART type, print it, enable FIFO if present
+ */
+void sio_init(void) {
+ kprintf("UART0 type: SIO/2.\n");
+}
+
+void acia_init(void) {
+ kprintf("UART0 type: ACIA.\n");
+}
+
+
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include <ds1302.h>
+#include "config.h"
+#include "devrd.h"
+#include "vfd-term.h"
+
+/* Everything in here is discarded after init starts */
+
+#ifdef CONFIG_PPIDE
+#include <devide.h>
+void ppide_init(void);
+#endif
+
+void init_hardware_c(void)
+{
+// vfd_debug_init();
+#ifdef CONFIG_VFD_TERM
+ vfd_term_init();
+#endif
+ ramsize = 512;
+ procmem = 512 - 64 - (DEV_RD_RAM_PAGES<<4);
+ /* zero out the initial bufpool */
+ memset(bufpool, 0, (char*)bufpool_end - (char*)bufpool);
+}
+
+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_RAM_START >> 14); i++)
+ pagemap_add(i);
+
+ /* finally add the common area */
+ pagemap_add(32 + 3);
+}
+
+void map_init(void)
+{
+}
+
+void device_init(void)
+{
+#ifdef CONFIG_SIO
+ sio_init();
+#endif
+#ifdef CONFIG_ACIA
+ acia_init();
+#endif
+#ifdef CONFIG_PPIDE
+ ppide_init();
+ devide_init();
+#endif
+}
--- /dev/null
+; 2015-01-21 Will Sowerbutts <will@sowerbutts.com>
+;
+; Boot sector for N8VEM Mark IV SBC with UNA BIOS, based on
+; my UNA CP/M boot sector (2014-07-11)
+
+ .module diskboot
+
+
+; we are loaded at 0x8000
+himem = 0xFA00 ; our location in memory
+buffer = 0xF800 ; disk buffer (512 bytes)
+stacktop = 0xFE00 ; top of stack (512 bytes)
+
+; UNA BIOS constants
+UNABIOS_STUB_ENTRY = 0xFFFD ; main UNA entry vector
+UNABIOS_BOOTHISTORY = 0xFC ; C register (subfunction in B)
+UNABIOS_BOOT_GET = 0x00 ; B register (BOOTHISTORY subfunction)
+UNABIOS_GETINFO = 0xFA ; C regsister (subfunction in B)
+UNABIOS_GET_USER_PAGES = 0x05 ; B register (GETINFO subfunction)
+UNABIOS_BANKEDMEM = 0xFB ; C register (subfunction in B)
+UNABIOS_BANK_GET = 0x00 ; B register (BANKEDMEM subfunction)
+UNABIOS_BANK_SET = 0x01 ; B register (BANKEDMEM subfunction)
+UNABIOS_GET_HMA = 0xF1 ; C register (subfunction in B)
+UNABIOS_BLOCK_SETLBA = 0x41 ; C register (unit number in B, 28-bit LBA in DEHL)
+UNABIOS_BLOCK_READ = 0x42 ; C register (unit number in B, buffer address in DE, sector count in L)
+UNABIOS_OUTPUT_WRITE = 0x12 ; C register (unit number in B)
+
+ .area _LOADER (ABS)
+ .org himem
+start:
+ ; UNA BIOS loads us from disk sector 0 at 0x8000
+ jr gocopy ; we must start with a JP or JR instruction.
+ .ds 0x40 - (.-start) ; must leave room for floppy or partition superblock information
+
+ ; Copy us up into high memory
+gocopy: ld hl, #0x8000
+ ld de, #himem
+ ld bc, #512
+ ldir
+ jp go
+
+ ; Executes in high memory
+go: ld sp, #stacktop ; set inital stack
+ ; write a character
+ ld e, #0x5B ; '['
+ call printchar
+
+ ; determine the boot unit
+ ld bc, #(UNABIOS_BOOT_GET << 8 | UNABIOS_BOOTHISTORY)
+ call #UNABIOS_STUB_ENTRY
+ ld a, l
+ ld (unit), a ; save boot unit
+
+ ; get the page number for the user memory bank
+ ld bc, #(UNABIOS_GET_USER_PAGES << 8 | UNABIOS_GETINFO)
+ call #UNABIOS_STUB_ENTRY
+ ; returns EXEC_PAGE value in DE
+
+ ; map in user memory bank
+ ld bc, #(UNABIOS_BANK_SET << 8 | UNABIOS_BANKEDMEM)
+ call #UNABIOS_STUB_ENTRY
+
+ ; write unabios vector in user memory
+ ld hl, #UNABIOS_STUB_ENTRY
+ ld de, #0x0008
+ ld bc, #3
+ ldir
+
+ ; wipe BDOS entry vector
+ ld a, #0x76 ; halt instruction
+ ld (0x0005), a ; this is used as a marker to detect cold boot versus warm reload
+
+ ; wipe persistent memory pointer (persist_ptr, immediately below UNA UBIOS stub / HMA)
+ ld c, #UNABIOS_GET_HMA ; get pointer to lowest byte used by UNA BIOS stub
+ call #UNABIOS_STUB_ENTRY ; returns lowest used byte in HL.
+ xor a ; zero out the two bytes below that.
+ dec hl
+ ld (hl), a
+ dec hl
+ ld (hl), a
+
+ ld a, (firstblock)
+ ld (block), a
+
+nextblock:
+ ; print a = character only every other block
+ ld a, (block)
+ and #1
+ jr z, setlba
+ ld e, #0x3D ; '='
+ call printchar
+
+setlba: ; set LBA
+ xor a
+ ld d, a
+ ld e, a
+ ld h, a
+ ld a, (block)
+ ld l, a
+ inc a ; setup for next block now
+ ld (block), a
+ ld c, #UNABIOS_BLOCK_SETLBA
+ ld a, (unit)
+ ld b, a
+ call #UNABIOS_STUB_ENTRY
+ jr nz, error
+
+ ; read block into buffer
+ ld c, #UNABIOS_BLOCK_READ
+ ld a, (unit)
+ ld b, a
+ ld l, #1
+ ld de, #buffer
+ call #UNABIOS_STUB_ENTRY
+ jr nz, error
+
+ ; copy block into low memory
+ ld hl, #buffer
+ ld de, (copyaddr)
+ ld bc, #512
+ ldir
+
+ ld (copyaddr), de
+ ld a, (firstblock)
+ ld d, a
+ ld a, (count)
+ ld e, a
+ ld a, (block)
+ sub d
+ cp e
+ jr nz, nextblock
+
+ ld e, #0x5D ; ']'
+ call printchar
+ ld e, #0x0D
+ call printchar
+ ld e, #0x0A
+ call printchar
+
+ ; we're loaded. let's go.
+ ld hl, (entryaddr)
+ jp (hl)
+
+
+; print a hex byte in A
+byt_out:
+ push af ; save low nibble
+ rrca ; move high nibble into position
+ rrca ; **
+ rrca
+ rrca
+ call nib_out ; put out the high nibble
+ pop af ; fall into nib_out to put out low nibble
+; print a hex-nibble in A
+nib_out:
+ and #0x0F ; mask the nibble
+ add #0 ; clear the AUX carry bit
+ daa ; decimal adjust the A
+ add #0xF0 ; move hi-nib into carry, hi-nib is 0 or F
+ adc #0x40 ; form ascii character
+ ld e, a
+ ; fall through into printchar
+printchar:
+ ld bc, #UNABIOS_OUTPUT_WRITE
+ jp UNABIOS_STUB_ENTRY
+
+error:
+ ; print the error
+ ld a, c
+ call byt_out
+ ld e, a
+ call printchar
+
+ ; sad face :(
+ ld e, #0x20 ; space
+ call printchar
+ ld e, #0x3A ; ':'
+ call printchar
+ ld e, #0x28 ; '('
+ call printchar
+
+ ; park the vehicle
+ halt
+
+block: .ds 1
+unit: .ds 1
+firstblock: .db 2 ; start loading at sector 2
+count: .db 124 ; max sectors we can load before we overwrite ourselves (62KB)
+copyaddr: .dw 0x0088 ; load address
+entryaddr: .dw 0x0088 ; entry address
+
+ .ds 0x1BE - ( . -start) ; pad to start of partition tables
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; partition 1
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; partition 2
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; partition 3
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; partition 4
+ .dw 0xAA55 ; DOS boot signature
--- /dev/null
+-mwxuy
+-i fuzix.ihx
+-b _CODE=0x0088
+-b _COMMONMEM=0xF000
+-b _DISCARD=0xE000
+-l z80
+platform-rc2014/crt0.rel
+platform-rc2014/commonmem.rel
+platform-rc2014/rc2014.rel
+platform-rc2014/vfd-debug.rel
+platform-rc2014/vfdterm.rel
+platform-rc2014/vfd-term.rel
+start.rel
+version.rel
+lowlevel-z80.rel
+platform-rc2014/tricks.rel
+platform-rc2014/main.rel
+timer.rel
+kdata.rel
+platform-rc2014/devfd.rel
+platform-rc2014/devfd_hw.rel
+platform-rc2014/devrd.rel
+platform-rc2014/devrd_hw.rel
+platform-rc2014/devrd_zeta2.rel
+platform-rc2014/devrd_zeta2_hw.rel
+platform-rc2014/devices.rel
+devio.rel
+filesys.rel
+process.rel
+inode.rel
+syscall_exec16.rel
+syscall_fs.rel
+syscall_proc.rel
+syscall_fs2.rel
+syscall_fs3.rel
+syscall_other.rel
+mm.rel
+swap.rel
+bank16k.rel
+tty.rel
+devsys.rel
+usermem.rel
+usermem_std-z80.rel
+platform-rc2014/discard.rel
+platform-rc2014/devtty.rel
+platform-rc2014/devtty_discard.rel
+platform-rc2014/mbr.rel
+platform-rc2014/blkdev.rel
+platform-rc2014/monitor.rel
+-e
--- /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 a old good Z80
+USE_FANCY_MONITOR .equ 1 ; disabling this saves around approx 0.5KB
+
+Z80_MMU_HOOKS .equ 0
+
+PROGBASE .equ 0x0000
+PROGLOAD .equ 0x0100
+
+; Zeta SBC V2 mnemonics for I/O ports etc
+
+CONSOLE_RATE .equ 38400
+
+CPU_CLOCK_KHZ .equ 7372
+
+; VFD Debugging
+VFD_C .EQU 0 ; control register
+VFD_D .EQU 1 ; data register
+
+; Z80 CTC ports
+CTC_CH0 .equ 0x90 ; CTC channel 0 and interrupt vector
+CTC_CH1 .equ 0x91 ; CTC channel 1 (periodic interrupts)
+CTC_CH2 .equ 0x92 ; CTC channel 2 (UART interrupt)
+CTC_CH3 .equ 0x93 ; CTC channel 3 (PPI interrupt)
+
+; 37C65 FDC ports
+FDC_CCR .equ 0x48 ; Configuration Control Register (W/O)
+FDC_MSR .equ 0x50 ; 8272 Main Status Register (R/O)
+FDC_DATA .equ 0x51 ; 8272 Data Port (R/W)
+FDC_DOR .equ 0x58 ; Digital Output Register (W/O)
+FDC_TC .equ 0x58 ; Pulse terminal count (R/O)
+
+; 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)
+
+; Define which serial device to use
+; NOTE: Make sure this agrees with CONFIG_SIO and CONFIG_ACIA
+; in config.h
+CONFIG_SIO .equ 1
+CONFIG_ACIA .equ 0
+
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include "config.h"
+#ifdef CONFIG_FLOPPY
+#include <devfd.h>
+#endif
+
+extern unsigned char irqvector;
+struct blkbuf *bufpool_end = bufpool + NBUFS; /* minimal for boot -- expanded after we're done with _DISCARD */
+
+void platform_discard(void)
+{
+ while(bufpool_end < (struct blkbuf*)(KERNTOP - sizeof(struct blkbuf))){
+ memset(bufpool_end, 0, sizeof(struct blkbuf));
+#if BF_FREE != 0
+ bufpool_end->bf_busy = BF_FREE; /* redundant when BF_FREE == 0 */
+#endif
+ bufpool_end->bf_dev = NO_DEVICE;
+ bufpool_end++;
+ }
+}
+
+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;
+}
+
+uint8_t platform_param(unsigned char *p)
+{
+ used(p);
+ return 0;
+}
+
+void platform_interrupt(void)
+{
+ switch(irqvector) {
+ case 1:
+#ifdef CONFIG_PPP
+ tty_poll_ppp()
+#endif
+#ifdef CONFIG_FLOPPY
+ fd_tick();
+#endif
+ timer_interrupt();
+ return;
+
+ case 4:
+#ifdef CONFIG_SIO
+ tty_pollirq_sio();
+#endif
+ return;
+ case 0x38:
+#ifdef CONFIG_ACIA
+ tty_pollirq_acia();
+#endif
+ return;
+ default:
+ return;
+ }
+}
--- /dev/null
+; 2015-01-17 William R Sowerbutts
+
+ .module monitor
+ .include "kernel.def"
+ .globl _platform_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
+_platform_monitor:
+ di
+ call map_kernel
+ jp monitor_entry
+
+
+; -----------------------------------------------------------------------------
+.else ; MICRO MONITOR ---------------------------------------------------------
+ .globl outchar
+ .globl outnewline
+ .globl outhl
+
+ .area _COMMONMEM
+_platform_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
+#ifdef CONFIG_PPIDE
+#define PPIDE_BASE 0x60 /* Base address of 8255A */
+#define IDE_REG_INDIRECT /* IDE registers are not directly connected to the CPU bus */
+
+/* IDE control signal to 8255 port C mapping */
+#define PPIDE_A0_LINE 0x01 // Direct from 8255 to IDE interface
+#define PPIDE_A1_LINE 0x02 // Direct from 8255 to IDE interface
+#define PPIDE_A2_LINE 0x04 // Direct from 8255 to IDE interface
+#define PPIDE_CS0_LINE 0x08 // Inverter between 8255 and IDE interface
+#define PPIDE_CS1_LINE 0x10 // Inverter between 8255 and IDE interface
+#define PPIDE_WR_LINE 0x20 // Inverter between 8255 and IDE interface
+#define PPIDE_WR_BIT 5 // (1 << PPIDE_WR_BIT) = PPIDE_WR_LINE
+#define PPIDE_RD_LINE 0x40 // Inverter between 8255 and IDE interface
+#define PPIDE_RD_BIT 6 // (1 << PPIDE_RD_BIT) = PPIDE_RD_LINE
+#define PPIDE_RST_LINE 0x80 // Inverter between 8255 and IDE interface
+
+/* 8255 configuration */
+#define PPIDE_PPI_BUS_READ 0x92
+#define PPIDE_PPI_BUS_WRITE 0x80
+
+/* IDE register addresses */
+#define ide_reg_data (PPIDE_CS0_LINE)
+#define ide_reg_error (PPIDE_CS0_LINE | PPIDE_A0_LINE)
+#define ide_reg_features (PPIDE_CS0_LINE | PPIDE_A0_LINE)
+#define ide_reg_sec_count (PPIDE_CS0_LINE | PPIDE_A1_LINE)
+#define ide_reg_lba_0 (PPIDE_CS0_LINE | PPIDE_A1_LINE | PPIDE_A0_LINE)
+#define ide_reg_lba_1 (PPIDE_CS0_LINE | PPIDE_A2_LINE)
+#define ide_reg_lba_2 (PPIDE_CS0_LINE | PPIDE_A2_LINE | PPIDE_A0_LINE)
+#define ide_reg_lba_3 (PPIDE_CS0_LINE | PPIDE_A2_LINE | PPIDE_A1_LINE)
+#define ide_reg_devhead (PPIDE_CS0_LINE | PPIDE_A2_LINE | PPIDE_A1_LINE)
+#define ide_reg_command (PPIDE_CS0_LINE | PPIDE_A2_LINE | PPIDE_A1_LINE | PPIDE_A0_LINE)
+#define ide_reg_status (PPIDE_CS0_LINE | PPIDE_A2_LINE | PPIDE_A1_LINE | PPIDE_A0_LINE)
+#define ide_reg_altstatus (PPIDE_CS1_LINE | PPIDE_A2_LINE | PPIDE_A1_LINE)
+#define ide_reg_control (PPIDE_CS1_LINE | PPIDE_A2_LINE | PPIDE_A1_LINE | PPIDE_A0_LINE)
+#endif /* CONFIG_PPIDE */
+
+#define ide_select(x)
+#define ide_deselect()
--- /dev/null
+/* 2015-04-24 WRS: devide glue functions for PPIDE */
+
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <timer.h>
+#include <devide.h>
+#include <blkdev.h>
+
+__sfr __at (PPIDE_BASE + 0x00) ppi_port_a; /* IDE bus LSB */
+__sfr __at (PPIDE_BASE + 0x01) ppi_port_b; /* IDE bus MSB */
+__sfr __at (PPIDE_BASE + 0x02) ppi_port_c; /* IDE bus control signals */
+__sfr __at (PPIDE_BASE + 0x03) ppi_control; /* 8255 command register */
+
+void ppide_init(void)
+{
+ ppi_control = PPIDE_PPI_BUS_READ;
+ ppi_port_c = ide_reg_status;
+}
+
+uint8_t devide_readb(uint8_t regaddr)
+{
+ uint8_t r;
+
+ /* note: ppi_control should contain PPIDE_PPI_BUS_READ already */
+ ppi_port_c = regaddr;
+ ppi_control = 1 | (PPIDE_RD_BIT << 1); /* begin /RD pulse */
+ r = ppi_port_a;
+ ppi_control = 0 | (PPIDE_RD_BIT << 1); /* end /RD pulse */
+ return r;
+}
+
+void devide_writeb(uint8_t regaddr, uint8_t value)
+{
+ ppi_control = PPIDE_PPI_BUS_WRITE;
+ ppi_port_c = regaddr;
+ ppi_port_a = value;
+ ppi_port_b = 0;
+ ppi_control = 1 | (PPIDE_WR_BIT << 1); /* begin /WR pulse */
+ ppi_control = 0 | (PPIDE_WR_BIT << 1); /* end /WR pulse */
+ ppi_control = PPIDE_PPI_BUS_READ;
+}
+
+/****************************************************************************/
+/* The innermost part of the transfer routines has to live in common memory */
+/* since it must be able to bank switch to the user memory bank. */
+/****************************************************************************/
+COMMON_MEMORY
+
+void devide_read_data(void) __naked
+{
+ __asm
+ ld a, #ide_reg_data
+ ld c, #PPIDE_BASE+2 ; select control lines
+ out (c), a ; select IDE data register
+ ld hl, (_blk_op+BLKPARAM_ADDR_OFFSET) ; blkparam.addr
+ ld d, #ide_reg_data ; register address
+ ld e, #ide_reg_data | PPIDE_RD_LINE ; register address with /RD asserted
+ ld b, #0 ; setup count
+ ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user
+ or a ; test is_user
+ push af ; save flags
+ ld a, #PPIDE_BASE+0 ; I will be needing this later
+ call nz, map_process_always ; map user memory first if required
+goread: ; now we do the transfer
+ out (c), e ; assert /RD
+ ld c, a ; PPIDE_BASE
+ ini ; read byte from LSB
+ inc c ; up to MSB
+ ini ; read byte from MSB
+ inc c ; control lines
+ out (c), d ; de-assert /RD
+ inc b ; (delay) counteract second ini instruction
+ jr nz, goread ; (delay) next word
+ ; read completed
+ pop af ; recover is_user test result
+ ret z ; done if kernel memory transfer
+ jp map_kernel ; else map kernel then return
+ __endasm;
+}
+
+void devide_write_data(void) __naked
+{
+ __asm
+ ld c, #PPIDE_BASE+2 ; select control lines
+ ld a, #ide_reg_data
+ out (c), a ; select data register
+ ld a, #PPIDE_PPI_BUS_WRITE
+ inc c ; up to 8255A command register
+ out (c), a ; 8255A ports A, B to output mode
+ dec c ; back down to the control lines
+ ld hl, (_blk_op+BLKPARAM_ADDR_OFFSET) ; blkparam.addr
+ ld d, #ide_reg_data ; register address
+ ld e, #ide_reg_data | PPIDE_WR_LINE ; register address with /WR asserted
+ ld b, #0 ; setup count
+ ld a, (_blk_op+BLKPARAM_IS_USER_OFFSET) ; blkparam.is_user
+ or a ; test is_user
+ push af ; save flags
+ ld a, #PPIDE_BASE+0 ; I will be needing this later
+ call nz, map_process_always ; map user memory first if required
+gowrite: ; now we do the transfer
+ out (c), d ; de-assert /WR
+ ld c, a ; PPIDE_BASE
+ outi ; write byte to LSB
+ inc c ; up to MSB
+ outi ; write byte to MSB
+ inc c ; up to control lines
+ out (c), e ; assert /WR
+ inc b ; (delay) offset to counteract second outi instruction
+ jr nz, gowrite ; (delay) next word
+ ; write completed
+ out (c), d ; de-assert /WR
+ ld a, #PPIDE_PPI_BUS_READ
+ inc c ; up to 8255A command register
+ out (c), a ; 8255A ports A, B to read mode
+ pop af ; recover is_user test result
+ ret z ; done if kernel memory transfer
+ jp map_kernel ; else map kernel then return
+ __endasm;
+}
--- /dev/null
+#ifndef __RC2014_SIO_DOT_H__
+#define __RC2014_SIO_DOT_H__
+
+#include "config.h"
+
+#define SIO0_IVT 8
+
+#define SIO0_BASE 0x80
+__sfr __at (SIO0_BASE + 0) SIOA_D;
+__sfr __at (SIO0_BASE + 2) SIOA_C;
+__sfr __at (SIO0_BASE + 1) SIOB_D;
+__sfr __at (SIO0_BASE + 3) SIOB_C;
+
+/* ACIA is at same address as SIO. Assume CONFIG_ACIA or CONFIG_SIO has been
+ defined in config.h, but not both.
+*/
+
+#define ACIA_BASE 0x80
+__sfr __at (ACIA_BASE + 0) ACIA_C;
+__sfr __at (ACIA_BASE + 1) ACIA_D;
+
+extern bool boot_from_rom;
+
+#endif
--- /dev/null
+; 2014-02-19 Sergey Kiselev
+; RC2014 hardware specific code
+
+ .module zeta_v2
+
+ ; exported symbols
+ .globl init_hardware
+ .globl _program_vectors
+ .globl map_kernel
+ .globl map_process
+ .globl map_process_always
+ .globl map_save
+ .globl map_restore
+ .globl _irqvector
+ .globl platform_interrupt_all
+ .globl mpgsel_cache
+ .globl _kernel_pages
+ .globl _platform_reboot
+ .globl _bufpool
+
+ ; imported symbols
+ .globl _ramsize
+ .globl _procmem
+ .globl _init_hardware_c
+ .globl outhl
+ .globl outnewline
+ .globl interrupt_handler
+ .globl unix_syscall_entry
+ .globl nmi_handler
+ .globl null_handler
+ .globl _boot_from_rom
+
+ ; 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)
+
+RTS_HIGH .EQU 0xE8
+RTS_LOW .EQU 0xEA
+
+SIOA_D .EQU 0x80 ; Base address of SIO/2 chip
+SIOA_C .EQU SIOA_D+2
+SIOB_D .EQU SIOA_D+1
+SIOB_C .EQU SIOA_D+3
+
+SIO_IV .EQU 8 ; Interrupt vector table entry to use
+
+ACIA_C .EQU 0x80
+ACIA_D .EQU 0x81
+ACIA_RESET .EQU 0x03
+ACIA_RTS_HIGH_A .EQU 0xD6 ; rts high, xmit interrupt disabled
+ACIA_RTS_LOW_A .EQU 0x96 ; rts low, xmit interrupt disabled
+;ACIA_RTS_LOW_A .EQU 0xB6 ; rts low, xmit interrupt enabled
+
+;=========================================================================
+; Buffers
+;=========================================================================
+ .area _BUFFERS
+_bufpool:
+ .ds (BUFSIZE * 4) ; adjust NBUFS in config.h in line with this
+
+;=========================================================================
+; Initialization code
+;=========================================================================
+ .area _DISCARD
+init_hardware:
+ ; program vectors for the kernel
+ ld hl, #0
+ push hl
+ call _program_vectors
+ pop hl
+
+ ; Stop floppy drive motors
+ ld a, #0x0C
+ out (FDC_DOR), a
+
+ ; initialize UART0
+ ld a, (_boot_from_rom) ; do not set the baud rate and other
+ or a ; serial line parameters if the BIOS
+ jr z, init_partial_uart ; already set them for us.
+ ; nothing to do here yet
+init_partial_uart:
+
+.if CONFIG_SIO
+ LD A,#0x00
+ OUT (SIOA_C),A
+ LD A,#0x18
+ OUT (SIOA_C),A
+
+ LD A,#0x04
+ OUT (SIOA_C),A
+ LD A,#0xC4
+ OUT (SIOA_C),A
+
+ LD A,#0x01
+ OUT (SIOA_C),A
+ LD A,#0x1A ; Receive int mode 11, tx int enable (was $18)
+ OUT (SIOA_C),A
+
+ LD A,#0x03
+ OUT (SIOA_C),A
+ LD A,#0xE1
+ OUT (SIOA_C),A
+
+ LD A,#0x05
+ OUT (SIOA_C),A
+ LD A,#RTS_LOW
+ OUT (SIOA_C),A
+
+ LD A,#0x00
+ OUT (SIOB_C),A
+ LD A,#0x18
+ OUT (SIOB_C),A
+
+ LD A,#0x04
+ OUT (SIOB_C),A
+ LD A,#0xC4
+ OUT (SIOB_C),A
+
+ LD A,#0x01
+ OUT (SIOB_C),A
+ LD A, #0x1A ; Receive int mode 11, tx int enable (was $18)
+ OUT (SIOB_C),A
+
+ LD A,#0x02
+ OUT (SIOB_C),A
+ LD A,#SIO_IV ; INTERRUPT VECTOR ADDRESS
+ OUT (SIOB_C),A
+
+ LD A,#0x03
+ OUT (SIOB_C),A
+ LD A,#0xE1
+ OUT (SIOB_C),A
+
+ LD A,#0x05
+ OUT (SIOB_C),A
+ LD A,#RTS_LOW
+ OUT (SIOB_C),A
+
+ ; ---------------------------------------------------------------------
+ ; Initialize CTC
+ ; Only supported for SIO, since CTC must operate in IM2.
+ ; If you don't have a CTC probably nothing bad will happen, other than
+ ; your floppy not working.
+
+ ld a,#0x57 ; 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
+
+ ; Done CTC Stuff
+ ; ---------------------------------------------------------------------
+
+ ld hl,#intvectors
+ 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
+.endif
+
+.if CONFIG_ACIA
+ LD A, #ACIA_RESET
+ OUT (ACIA_C),A\r
+ LD A, #ACIA_RTS_LOW_A\r
+ OUT (ACIA_C),A ; Initialise ACIA
+
+ im 1
+.endif
+
+ jp _init_hardware_c ; pass control to C, which returns for us
+
+;=========================================================================
+; Kernel code
+;=========================================================================
+ .area _CODE
+
+_platform_reboot:
+ ; We need to map the ROM back in -- ideally into every page.
+ ; This little trick based on a clever suggestion from John Coffman.
+ di
+ ld hl, #(MPGENA << 8) | 0xD3 ; OUT (MPGENA), A
+ ld (0xFFFE), hl ; put it at the very top of RAM
+ xor a ; A=0
+ jp 0xFFFE ; execute it; PC then wraps to 0
+
+
+;=========================================================================
+; 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
+ .dw sio_int ; SIO 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
+
+; SIO interrupt
+sio_int:
+ push af
+ ld a,#4 ; IRQ vector = 4
+ ld (_irqvector),a ; store it
+ pop af
+ jp interrupt_handler
+
+; int38h handler
+; Calls interrupt_handler with irqvector of 0x38
+; For SIO/2, nothing will happen, since it uses IM2
+; For ACIA, serial interrupt handler will execute
+int38h_int:
+ push af
+ LD A, #'B'
+ OUT (VFD_D),A
+ ld a,#0x38 ; not a real vector, just a signal that the 0x38h occurred
+ 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
+ ld a,#0xC3 ; JP instruction
+ ld (0x0038),a
+ ld hl,#int38h_int
+ 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
+ jr map_process_2_pophl_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
+ jr map_process_2_pophl_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,#mpgsel_cache ; 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
+map_process_2_pophl_ret:
+ call map_process_2
+ pop hl
+ ret
+
+;=========================================================================
+; map_save - save the current page mapping to map_savearea
+; Inputs: none
+; Outputs: none
+;=========================================================================
+map_save:
+ push hl
+ ld hl,(mpgsel_cache)
+ ld (map_savearea),hl
+ ld hl,(mpgsel_cache+2)
+ ld (map_savearea+2),hl
+ pop hl
+ ret
+
+; MPGSEL registers are read only, so their content is cached here
+mpgsel_cache:
+ .db 0,0,0,0
+
+; kernel page mapping
+_kernel_pages:
+ .db 32,33,34,35
+
+; memory page mapping save area for map_save/map_restore
+map_savearea:
+ .db 0,0,0,0
+
+;=========================================================================
+; Basic console I/O
+;=========================================================================
+
+;=========================================================================
+; outchar - Wait for UART TX idle, then print the char in A
+; Inputs: A - character to print
+; Outputs: none
+;=========================================================================
+outchar:
+
+.if CONFIG_SIO
+ push af
+ ; wait for transmitter to be idle
+ocloop_sio:
+ xor a ; read register 0
+ out (SIOA_C), a
+ in a,(SIOA_C) ; read Line Status Register
+ and #0x04 ; get THRE bit
+ jr z,ocloop_sio
+ ; now output the char to serial port
+ pop af
+ out (SIOA_D),a
+.endif
+
+.if CONFIG_ACIA
+ push af
+ ; wait for transmitter to be idle
+ocloop_acia:
+ in a,(ACIA_C) ; read Line Status Register
+ and #0x02 ; get THRE bit
+ jr z,ocloop_acia
+ ; now output the char to serial port
+ pop af
+ out (ACIA_D),a
+.endif
+
+ out (VFD_D),a
+ ret
+
+;=========================================================================
+; inchar - Wait for character on UART, return in A
+; Inputs: none
+; Outputs: A - received character, F destroyed
+;=========================================================================
+inchar:
+.if CONFIG_SIO
+ xor a ; read register 0
+ out (SIOA_C), a
+ in a,(SIOA_C) ; read Line Status Register
+ and #0x01 ; test if data is in receive buffer
+ jp z,inchar ; no data, wait
+ in a,(SIOA_D) ; read the character from the UART
+.endif
+
+.if CONFIG_ACIA
+ in a,(ACIA_C) ; read Line Status Register
+ and #0x01 ; test if data is in receive buffer
+ jp z,inchar ; no data, wait
+ in a,(ACIA_D) ; read the character from the UART
+.endif
+ ret
--- /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 _platform_monitor
+ .globl trap_illegal
+ .globl _platform_switchout
+ .globl _switchin
+ .globl _doexec
+ .globl _dofork
+ .globl _runticks
+ .globl unix_syscall_entry
+ .globl interrupt_handler
+ .globl map_kernel
+ .globl _ramtop
+ .globl _need_resched
+ .globl mpgsel_cache
+
+ ; 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
+_need_resched:
+ .db 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().
+;
+; This function can have no arguments or auto variables.
+_platform_switchout:
+ ; 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
+
+ ; find another process to run (may select this one again)
+ call _getproc
+
+ push hl
+ call _switchin
+
+ ; we should never get here
+ call _platform_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, (U_DATA__U_ININTERRUPT)
+ or a
+ ret nz ; 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 _platform_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
+ ld (mpgsel_cache+3),a ; cache the page number
+ 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
+#include "vfd-debug.h"
+
+void vfd_debug_init(void) {
+ VFD_C = 0x30;
+ VFD_D = 0;
+ VFD_C = 1;
+ VFD_C = 0x0F;
+ VFD_D = '>';
+}
+
--- /dev/null
+#ifndef __VFD_DEBUG_DOT_H__
+#define __VFD_DEBUG_DOT_H__
+
+#include "config.h"
+
+__sfr __at (0) VFD_C;
+__sfr __at (1) VFD_D;
+
+void vfd_debug_init(void);
+
+#define vprtch(c) VFD_D=(c);
+
+#endif
--- /dev/null
+#include "vfd-term.h"
+
+void vfd_term_init(void) {
+ __asm
+ call VFDTERM_PREINIT
+ __endasm;
+}
+
+void vfd_term_write(char c) {
+ __asm
+ ld hl, #2+0
+ add hl, sp
+ ld e, (hl)
+ call VFDTERM_PUTC
+ __endasm;
+ (void)c; // suppress error
+}
+
--- /dev/null
+#ifndef __VFD_TERM_DOT_H__
+#define __VFD_TERM_DOT_H__
+
+#include "config.h"
+
+void vfd_term_init(void);
+void vfd_term_write(char c);
+
+#endif
--- /dev/null
+ .module vfdterm
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+ .globl VFDTERM_PREINIT
+ .globl VFDTERM_PUTC
+
+;------------------------
+; VFD dumb terminal
+;------------------------
+
+VFDTERM_C0 .EQU 0
+VFDTERM_D0 .EQU 1
+VFDTERM_C1 .EQU 2
+VFDTERM_D1 .EQU 3
+
+ .area _DATA
+vfdPtr: .DW 0
+vfdLen: .DB 0
+vfdLine1: .DW 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+vfdLine2: .DW 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+vfdLine3: .DW 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+vfdLine4: .DW 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+
+vfdLineLen .EQU 40
+
+
+ .area _CODE
+VFDTERM_PREINIT:
+ ; initialize first two lines
+ LD A, #0x30
+ OUT (VFDTERM_C0), A
+ LD A, #0
+ OUT (VFDTERM_D0), A
+ LD A, #1
+ OUT (VFDTERM_C0), A
+ LD A, #0x0C ; display on, cursor off
+ OUT (VFDTERM_C0), A
+
+ ; initialize second two lines
+ LD A, #0x30
+ OUT (VFDTERM_C1), A
+ LD A, #0
+ OUT (VFDTERM_D1), A
+ LD A, #1
+ OUT (VFDTERM_C1), A
+ LD A, #0x0F ; display on, cursor on
+ OUT (VFDTERM_C1), A
+
+ ; position to start of last line
+ CALL VFDTERM_SOL
+ RET
+
+ ; assume character in E
+VFDTERM_PUTC: PUSH AF
+ PUSH BC
+ PUSH DE
+ PUSH HL
+
+ LD A,E
+ CP #0x08
+ JR Z, VFDTERM_BSPC
+ CP #0x0A ; line feed
+ JR Z, VFDTERM_NEWLINE
+ CP #0x0D ; CR
+ JR Z, VFDTERM_CR
+
+VFDTERM_STOREC: LD A, E ; write the character to the vfd
+ OUT (VFDTERM_D1), A
+
+ LD HL, (vfdPtr) ; update pointer
+ LD (HL), E
+ INC HL
+ LD (vfdPtr), HL
+
+ LD A, (vfdLen) ; update position index
+ INC A
+ LD (vfdLen), A
+
+ LD A, (vfdLen) ; check if we just wrapped
+ CP #vfdLineLen
+ JR NZ, VFDTERM_PUTC_OUT
+
+VFDTERM_NEWLINE:
+ CALL VFDTERM_SCROLL
+ CALL VFDTERM_REDRAW
+ CALL VFDTERM_SOL
+ JR VFDTERM_PUTC_OUT
+
+VFDTERM_BSPC: LD A, (vfdLen)
+ CP #0x00
+ JR Z, VFDTERM_PUTC_OUT ; at beginning of buffer, no-op
+
+ DEC A ; decrement line len
+ LD (vfdLen), A
+ LD HL, (vfdPtr) ; decrement ptr
+ DEC HL
+ LD (vfdPtr), HL
+
+ LD A, #0x10 ; shift cursor left one place
+ OUT (VFDTERM_C1), A
+
+ JR VFDTERM_PUTC_OUT
+
+VFDTERM_CR: CALL VFDTERM_SOL
+ JR VFDTERM_PUTC_OUT
+
+VFDTERM_PUTC_OUT:
+ POP HL
+ POP DE
+ POP BC
+ POP AF
+ RET
+
+ ; scroll the buffer up one line
+VFDTERM_SCROLL:
+ LD HL, #vfdLine2
+ LD DE, #vfdLine1
+ LD BC, #120
+ LDIR ; do the move
+ LD HL, #vfdLine4 ; clear the last line of the buffer
+ LD BC, #vfdLineLen
+ LD A, #' '
+ CALL UTIL_FILL
+ RET
+
+UTIL_FILL:
+ LD D,H ; SET DE TO HL\r
+ LD E,L ; SO DESTINATION EQUALS SOURCE\r
+ LD (HL),A ; FILL THE FIRST BYTE WITH DESIRED VALUE\r
+ INC DE ; INCREMENT DESTINATION\r
+ DEC BC ; DECREMENT THE COUNT\r
+ LDIR ; DO THE REST\r
+ RET ; RETURN
+
+ ; move pointer to start of 4th line
+VFDTERM_SOL:
+ LD HL, #vfdLine4
+ LD (vfdPtr), HL
+ LD A, #0
+ LD (vfdLen), A
+ LD A, #0xC0 ; command to move to start of line 2
+ OUT (VFDTERM_C1), A ; send to VFD
+ RET
+
+VFDTERM_REDRAW:
+ LD A, #0x80 ; command to move to start of line 1
+ OUT (VFDTERM_C0), A
+ LD DE, #vfdLine1
+ LD C, #vfdLineLen
+VFDTERM_REDRAW0: LD A, (DE)
+ INC DE
+ OUT (VFDTERM_D0), A
+ DEC C
+ JR NZ, VFDTERM_REDRAW0
+
+ LD A, #0xC0 ; command to move to start of line 2
+ OUT (VFDTERM_C0), A
+ LD DE, #vfdLine2
+ LD C, #vfdLineLen
+VFDTERM_REDRAW1: LD A, (DE)
+ INC DE
+ OUT (VFDTERM_D0), A
+ DEC C
+ JR NZ, VFDTERM_REDRAW1
+
+ LD A, #0x80 ; command to move to start of line 3
+ OUT (VFDTERM_C1), A
+ LD DE, #vfdLine3
+ LD C, #vfdLineLen
+VFDTERM_REDRAW2: LD A, (DE)
+ INC DE
+ OUT (VFDTERM_D1), A
+ DEC C
+ JR NZ, VFDTERM_REDRAW2
+
+ LD A, #0xC0 ; command to move to start of line 4
+ OUT (VFDTERM_C1), A
+ LD DE, #vfdLine4
+ LD C, #vfdLineLen
+VFDTERM_REDRAW3: LD A, (DE)
+ INC DE
+ OUT (VFDTERM_D1), A
+ DEC C
+ JR NZ, VFDTERM_REDRAW3
+
+ RET
+
+
+