From 9042f75a71f2f87b8381a66bad59f38454b736dc Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 25 Nov 2014 22:39:54 +0000 Subject: [PATCH] z80pack32: Prototyping work to support 32K + 32K fixed better Initial test work to see what is needed to handle machines that have a 32K chunk you can page and a fixed 32K chunk (MTX512, N8VEM v2, MicroBee etc) Buildable prototype but needs work doing on the crt0.s/binman setup to cope with binaries containing a big hole in the middle. --- Kernel/Makefile | 4 +- Kernel/bank32k.c | 17 +- Kernel/include/kernel.h | 2 + Kernel/platform-z80pack32/Makefile | 36 +++ Kernel/platform-z80pack32/README | 37 +++ Kernel/platform-z80pack32/bootblock.s | 81 +++++ Kernel/platform-z80pack32/commonmem.s | 9 + Kernel/platform-z80pack32/config.h | 46 +++ Kernel/platform-z80pack32/crt0.s | 74 +++++ Kernel/platform-z80pack32/devices.c | 43 +++ Kernel/platform-z80pack32/kernel.def | 7 + Kernel/platform-z80pack32/main.c | 36 +++ Kernel/platform-z80pack32/tricks.s | 442 ++++++++++++++++++++++++++ Kernel/platform-z80pack32/uzi.lnk | 38 +++ Kernel/platform-z80pack32/z80pack.s | 214 +++++++++++++ Kernel/swap.c | 6 +- 16 files changed, 1081 insertions(+), 11 deletions(-) create mode 100644 Kernel/platform-z80pack32/Makefile create mode 100644 Kernel/platform-z80pack32/README create mode 100644 Kernel/platform-z80pack32/bootblock.s create mode 100644 Kernel/platform-z80pack32/commonmem.s create mode 100644 Kernel/platform-z80pack32/config.h create mode 100644 Kernel/platform-z80pack32/crt0.s create mode 100644 Kernel/platform-z80pack32/devices.c create mode 100644 Kernel/platform-z80pack32/kernel.def create mode 100644 Kernel/platform-z80pack32/main.c create mode 100644 Kernel/platform-z80pack32/tricks.s create mode 100644 Kernel/platform-z80pack32/uzi.lnk create mode 100644 Kernel/platform-z80pack32/z80pack.s diff --git a/Kernel/Makefile b/Kernel/Makefile index 60fe9d7e..3fab9dee 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -1,6 +1,6 @@ -TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-zx128 platform-trs80 platform-z80pack platform-z80pack-lite platform-dragon +TARGET_LIST = platform-nc100 platform-micropack platform-pcw8256 platform-socz80 platform-zx128 platform-trs80 platform-z80pack platform-z80pack-lite platform-z80pack32 platform-dragon -export TARGET= z80pack +export TARGET= z80pack32 export CPU = z80 #export TARGET = dragon #export CPU = 6809 diff --git a/Kernel/bank32k.c b/Kernel/bank32k.c index e9b388de..b5aaa653 100644 --- a/Kernel/bank32k.c +++ b/Kernel/bank32k.c @@ -4,8 +4,9 @@ #ifdef CONFIG_BANK32 -#ifndef bank32_invalidate_cache -#define bank32_invalidate_cache(x) do {} while(0) +/* 32K common, ldir copying to use for 48K apps */ +#ifndef CONFIG_COMMON_COPY +#define invalidate_cache(x) do {} while(0) #endif /* @@ -61,7 +62,7 @@ void pagemap_free(ptptr p) pfree[pfptr--] = *ptr; if (*ptr != ptr[1]) { pfree[pfptr--] = ptr[1]; - bank32_invalidate_cache(ptr[1]); + invalidate_cache((uint16_t)ptr[1]); } } @@ -73,7 +74,7 @@ static int maps_needed(uint16_t top) /* Usually we have 0x1000 common - 1 for shift and inc */ if (needed & 0x8000) return 2; - return return 1; + return 1; } /* @@ -85,7 +86,6 @@ int pagemap_alloc(ptptr p) { uint8_t *ptr = (uint8_t *) & p->p_page; int needed = maps_needed(udata.u_top); - int i; #ifdef SWAPDEV /* Throw our toys out of our pram until we have enough room */ @@ -107,12 +107,17 @@ int pagemap_alloc(ptptr p) /* * Reallocate the maps for a process + * + * Subtlety: On a 32K box we have the udata area being copied to/from + * the stash. As we always realloc for the live process we don't have + * to worry about this in the 32K + common case because we'll switchin + * at one size, and switchout at the other and the udata will just get + * saved/restored to the right places. */ int pagemap_realloc(uint16_t size) { int have = maps_needed(udata.u_top); int want = maps_needed(size); uint8_t *ptr = (uint8_t *) & udata.u_page; - int i; /* If we are shrinking then free pages and propogate the common page into the freed spaces */ diff --git a/Kernel/include/kernel.h b/Kernel/include/kernel.h index 018923c6..cf3e2d69 100644 --- a/Kernel/include/kernel.h +++ b/Kernel/include/kernel.h @@ -703,6 +703,8 @@ CODE2 void platform_idle(void); /* Will need a uptr_t eventually */ extern uint16_t ramtop; /* Note: ramtop must be in common in some cases */ CODE2 extern void platform_interrupt(void); +COMMON void invalidate_cache(uint16_t page); +COMMON void flush_cache(ptptr p); CODE2 int16_t __exit(void); /* FUZIX system call 0 */ CODE2 int16_t _open(void); /* FUZIX system call 1 */ diff --git a/Kernel/platform-z80pack32/Makefile b/Kernel/platform-z80pack32/Makefile new file mode 100644 index 00000000..1bcbe006 --- /dev/null +++ b/Kernel/platform-z80pack32/Makefile @@ -0,0 +1,36 @@ + +DSRCS = ../dev/z80pack/devlpr.c ../dev/z80pack/devtty.c ../dev/z80pack/devfd.c +CSRCS += devices.c main.c + +ASRCS = crt0.s z80pack.s +ASRCS += tricks.s commonmem.s + +AOBJS = $(ASRCS:.s=.rel) +COBJS = $(CSRCS:.c=.rel) +DOBJS = $(patsubst ../dev/z80pack/%.c,%.rel, $(DSRCS)) + +OBJS = $(AOBJS) $(COBJS) $(DOBJS) + +CROSS_CCOPTS += -I../dev/z80pack/ --codeseg CODE3 + +JUNK = *.rel *.lst *.asm *.sym *.rst + +all: $(OBJS) + +$(AOBJS): %.rel: %.s + $(CROSS_AS) $(ASOPTS) $< + +$(COBJS): %.rel: %.c + $(CROSS_CC) $(CROSS_CCOPTS) -c $< + +$(DOBJS): %.rel: ../dev/z80pack/%.c + $(CROSS_CC) $(CROSS_CCOPTS) -c $< + + +clean: + rm -f $(OBJS) $(JUNK) core *~ + +image: + sdasz80 -o bootblock.s + sdldz80 -m -i bootblock.rel + makebin -s 128 bootblock.ihx > bootblock.bin diff --git a/Kernel/platform-z80pack32/README b/Kernel/platform-z80pack32/README new file mode 100644 index 00000000..61a59220 --- /dev/null +++ b/Kernel/platform-z80pack32/README @@ -0,0 +1,37 @@ +Z80 Pack Test For 32K + cached high for bigger binaries + +Kernel +0000-07FFF CODE1, CODE2 +0x8000-0xBFFF User cache area (0xB000 discard) +0xC000 Udata +0xC300 CONST, CODE3, DATA ... +0xF000 Common + +TODO + +We waste 16K per high process but its not clear how much of a win it would +be to pack them, and if we can squash stuff better we might go over 48K and +break it. + +binman has no idea how to pack such an image, probably better to have a new +tool as binman is getting too unwieldy. Perhaps it's time to have a tool and +crt0.s that simply reads a table of block/offsets to ldir then runs ? + +Also need to pack carefully so we don't overwrite source data. Probably +means we need to start at the end of the image and lddr back down it. + +Need to fix the packing/crt0.s before we can test any of this + + +Disk swap device recommended + +Put the kernel at the end of a floppy image from cyl 60 +Add the fs in the first 60 cyls (390 blocks) + +Put the bootblock in sector 0 + +dd the kernel image to offset 199680 + +ie + +dd if=fuzix.bin of=drivea.cpm bs=1 seek=199680 conv=notrunc diff --git a/Kernel/platform-z80pack32/bootblock.s b/Kernel/platform-z80pack32/bootblock.s new file mode 100644 index 00000000..887293fe --- /dev/null +++ b/Kernel/platform-z80pack32/bootblock.s @@ -0,0 +1,81 @@ +; +; Z80pack cpmsim loads the first (128 byte) sector from the disk +; into memory at 0 then executes it +; We are a bit tight on space here +; +; Floppy loader: +; Our boot disc is 77 tracks of 26 x 128 byte sectors, and we put +; the OS on tracks 60+, which means we can put a file system in the +; usual place providing its a bit smaller than a whole disc. +; +; +; assemble with sdasz80 +; + .area ASEG(ABS) + .org 0 + +start: jr diskload + +rootdev: .dw 0 ; patched by hand +swapdev: .dw 0 ; ditto + .dw 0 ; spare + +progress: .db '/', '-', '\', '|' + +diskload: di + ld sp, #stack + ld hl, #0x88 + exx + xor a + ld h, a + ld b, a + out (17), a ; sector high always 0 + out (10), a ; drive always 0 + ld a, #59 ; start on track 60 + out (11), a + exx + ld c, #17 ; number of tracks to load (56Kish) +load_tracks: in a, (11) + inc a ; track + out (11), a + xor a + out (12), a + ld b, #26 ; sectors +load_sectors: exx + ld a, b + and #3 + add #progress + ld l, a + ld a, (hl) + out (01), a + ld a, #8 + out (01), a + inc b + exx + + in a, (12) + inc a + out (12), a ; sector + ld a, l + out (15), a ; dma low + ld a, h + out (16), a ; dma high + xor a ; read + out (13), a ; go + in a, (14) ; status + ld de, #128 + add hl, de + djnz load_sectors ; 26 sectors = 3328 bytes + dec c + jr nz, load_tracks + ld a, #0xc9 ; to help debug + ld (start), a + ld a, #13 + out (1), a + ld a, #10 + out (1), a + jp 0x88 + + .ds 26 +stack: + .db 0xff diff --git a/Kernel/platform-z80pack32/commonmem.s b/Kernel/platform-z80pack32/commonmem.s new file mode 100644 index 00000000..522503a8 --- /dev/null +++ b/Kernel/platform-z80pack32/commonmem.s @@ -0,0 +1,9 @@ +; +; Common on z80pack is at 0xF000 as defined by hardware. +; + + .module commonmem + + .area _COMMONMEM + + .include "../cpu-z80/std-commonmem.s" diff --git a/Kernel/platform-z80pack32/config.h b/Kernel/platform-z80pack32/config.h new file mode 100644 index 00000000..e6f391ee --- /dev/null +++ b/Kernel/platform-z80pack32/config.h @@ -0,0 +1,46 @@ +/* Enable to make ^Z dump the inode table for debug */ +#undef CONFIG_IDUMP +/* Enable to make ^A drop back into the monitor */ +#undef CONFIG_MONITOR +/* Profil syscall support (not yet complete) */ +#define CONFIG_PROFIL +/* Multiple processes in memory at once */ +#define CONFIG_MULTI +/* Single tasking */ +#undef CONFIG_SINGLETASK +/* CP/M emulation */ +#undef CONFIG_CPM_EMU +/* 32K banking */ +#define CONFIG_BANK32 +/* but with the high block copied on switch as needed */ +#define CONFIG_COMMON_COPY +/* 8 32K banks, 1 is kernel */ +#define MAX_MAPS 7 +#define MAP_SIZE 0x8000U + +/* Banks as reported to user space */ +#define CONFIG_BANKS 2 + +#define TICKSPERSEC 100 /* Ticks per second */ +#define PROGBASE 0x0000 /* also data base */ +#define PROGLOAD 0x0100 /* also data base */ +#define PROGTOP 0xBC00 /* Top of program, base of U_DATA copy */ + +#define SWAP_SIZE 0x60 /* 48K in blocks */ +#define SWAPBASE 0x0000 /* We swap the lot in one, include the */ +#define SWAPTOP 0xC000 /* vectors so its a round number of sectors */ +#define MAX_SWAPS 64 /* The full drive would actually be 85! */ + +#define BOOT_TTY (512 + 1)/* Set this to default device for stdio, stderr */ + /* In this case, the default is the first TTY device */ + +/* We need a tidier way to do this from the loader */ +#define CMDLINE NULL /* Location of root dev name */ + +/* Device parameters */ +#define NUM_DEV_TTY 3 + +#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */ +#define SWAPDEV (256 + 1) /* Device for swapping. (z80pack drive J) */ +#define NBUFS 10 /* Number of block buffers */ +#define NMOUNTS 4 /* Number of mounts at a time */ diff --git a/Kernel/platform-z80pack32/crt0.s b/Kernel/platform-z80pack32/crt0.s new file mode 100644 index 00000000..bce4e13f --- /dev/null +++ b/Kernel/platform-z80pack32/crt0.s @@ -0,0 +1,74 @@ +; 2013-12-18 William R Sowerbutts + + .module crt0 + + ; Ordering of segments for the linker. + ; WRS: Note we list all our segments here, even though + ; we don't use them all, because their ordering is set + ; when they are first seen. + .area _CODE + .area _CODE2 + .area _CODE3 + .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 _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 + + ; startup code + .area _CODE +init: + di + ld sp, #kstack_top + + ; Configure memory map + call init_early + + ; 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 + + ; Hardware setup + call init_hardware + + ; Call the C main routine + call _fuzix_main + + ; main shouldn't return, but if it does... + di +stop: halt + jr stop + diff --git a/Kernel/platform-z80pack32/devices.c b/Kernel/platform-z80pack32/devices.c new file mode 100644 index 00000000..c60640f7 --- /dev/null +++ b/Kernel/platform-z80pack32/devices.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +struct devsw dev_tab[] = /* The device driver switch table */ +{ +// minor open close read write ioctl +// ----------------------------------------------------------------- + /* 0: /dev/fd Floppy disc block devices */ + { fd_open, no_close, fd_read, fd_write, no_ioctl }, + /* 1: /dev/hd Hard disc block devices (absent) */ + { hd_open, no_close, hd_read, hd_write, no_ioctl }, + /* 2: /dev/tty TTY devices */ + { tty_open, tty_close, tty_read, tty_write, tty_ioctl }, + /* 3: /dev/lpr Printer devices */ + { lpr_open, lpr_close, no_rdwr, lpr_write, no_ioctl }, + /* 4: /dev/mem etc System devices (one offs) */ + { no_open, no_close, sys_read, sys_write, sys_ioctl }, + /* Pack to 7 with nxio if adding private devices and start at 8 */ +}; + +bool validdev(uint16_t dev) +{ + /* This is a bit uglier than needed but the right hand side is + a constant this way */ + if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) + 255) + return false; + else + return true; +} + +void device_init(void) +{ + int i; + /* Add 64 swaps (4MB) to use the entire J drive */ + for (i = 0; i < MAX_SWAPS; i++) + swapmap_add(i); +} diff --git a/Kernel/platform-z80pack32/kernel.def b/Kernel/platform-z80pack32/kernel.def new file mode 100644 index 00000000..d5211a93 --- /dev/null +++ b/Kernel/platform-z80pack32/kernel.def @@ -0,0 +1,7 @@ +; 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. + +U_STASH_HIGH .equ 0xBD00 ; BD00-BFFF +U_STASH_LOW .equ 0x7D00 ; 7D00-BFFF diff --git a/Kernel/platform-z80pack32/main.c b/Kernel/platform-z80pack32/main.c new file mode 100644 index 00000000..eb6b778c --- /dev/null +++ b/Kernel/platform-z80pack32/main.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include + +uint16_t ramtop = PROGTOP; + + +void pagemap_init(void) +{ + int i; + for (i = 1; i < 8; i++) + pagemap_add(i); +} + +/* On idle we spin checking for the terminals. Gives us more responsiveness + for the polled ports */ +void platform_idle(void) +{ + /* We don't want an idle poll and IRQ driven tty poll at the same moment */ + irqflags_t irq = di(); + tty_pollirq(); + irqrestore(irq); +} + +void platform_interrupt(void) +{ + tty_pollirq(); + timer_interrupt(); +} + +/* Nothing to do for the map of init */ +void map_init(void) +{ +} diff --git a/Kernel/platform-z80pack32/tricks.s b/Kernel/platform-z80pack32/tricks.s new file mode 100644 index 00000000..3f70cf3c --- /dev/null +++ b/Kernel/platform-z80pack32/tricks.s @@ -0,0 +1,442 @@ +; 2013-12-21 William R Sowerbutts + + .module tricks + + .globl _ptab_alloc + .globl _newproc + .globl _chksigs + .globl _getproc + .globl _trap_monitor + .globl trap_illegal + .globl _inint + .globl _switchout + .globl _switchin + .globl _doexec + .globl _dofork + .globl _runticks + .globl unix_syscall_entry + .globl interrupt_handler + .globl dispatch_process_signal + .globl _swapper + .globl _cached_page + .globl _flush_cache + .globl _invalidate_cache + + ; imported debug symbols + .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex + + .include "kernel.def" + .include "../kernel.def" + + .area _COMMONMEM + +; Switchout switches out the current process, finds another that is READY, +; possibly the same process, and switches it in. When a process is +; restarted after calling switchout, it thinks it has just returned +; from switchout(). +; +; FIXME: make sure we optimise the switch to self case higher up the stack! +; +; This function can have no arguments or auto variables. +_switchout: + di + call _chksigs + ; save machine state + + ld hl, #0 ; return code set here is ignored, but _switchin can + ; return from either _switchout OR _dofork, so they must both write + ; U_DATA__U_SP with the following on the stack: + push hl ; return code + push ix + push iy + ld (U_DATA__U_SP), sp ; this is where the SP is restored in _switchin + + ; set inint to false + xor a + ld (_inint), a + + ; Stash the uarea back into process memory + ld hl, (U_DATA__U_PAGE) + ld a, l + out (21), a + cp h ; small or large process ? + jr z, switchoutlow + ld de, #U_STASH_HIGH +switchoutlow: + ld hl, #U_DATA + ld de, #U_STASH_LOW + ld bc, #U_DATA__TOTALSIZE + ldir + xor a + out (21), a + + ; find another process to run (may select this one again) + call _getproc + + push hl + call _switchin + + ; we should never get here + call _trap_monitor + +badswitchmsg: .ascii "_switchin: FAIL" + .db 13, 10, 0 +swapped: .ascii "_switchin: SWAPPED" + .db 13, 10, 0 + +_switchin: + di + pop bc ; return address + pop de ; new process pointer +; +; FIXME: do we actually *need* to restore the stack ! +; + push de ; restore stack + push bc ; restore stack + + xor a + out (21), a + + push de + ld hl, #P_TAB__P_PAGE_OFFSET + add hl, de ; process ptr + pop de + + ld a, (hl) + + or a + jr nz, not_swapped + + ; + ; We are still on the departing processes stack, which is + ; fine for now. + ; + ld sp, #_swapstack + push hl + push de + call _swapper + pop de + pop hl + ld a, (hl) + +not_swapped: + ; Decide where the uarea stash lives right now and check the cache + inc hl + cp (hl) ; must cp first as (hl) vanishes on the out + jr z, switchinlow + ld hl, #_cached_page + cp (hl) + push af + call z, update_cache + pop af + out (21), a + exx ; thank goodness for exx 8) + ld hl, #U_STASH_HIGH + jr switchin_page +switchinlow: + ; Pages please ! + out (21), a + exx ; thank goodness for exx 8) + ld hl, #U_STASH_LOW +switchin_page: + ; bear in mind that the stack will be switched now, so we can't use it + ; to carry values over this point + ld de, #U_DATA + ld bc, #U_DATA__TOTALSIZE + ldir + exx + + ; Return to kernel mappings + xor a + out (21), a + +switchlow2: + + ; check u_data->u_ptab matches what we wanted + ld hl, (U_DATA__U_PTAB) ; u_data->u_ptab + or a ; clear carry flag + sbc hl, de ; subtract, result will be zero if DE==IX + jr nz, switchinfail + + ; wants optimising up a bit + ld ix, (U_DATA__U_PTAB) + ; next_process->p_status = P_RUNNING + ld P_TAB__P_STATUS_OFFSET(ix), #P_RUNNING + + ; Fix the moved page pointers + ; Just do two bytes as that is all we use on this platform + ld l, P_TAB__P_PAGE_OFFSET(ix) + ld h, P_TAB__P_PAGE_OFFSET+1(ix) + ld (U_DATA__U_PAGE), hl + ; runticks = 0 + ld hl, #0 + ld (_runticks), hl + + ; restore machine state -- note we may be returning from either + ; _switchout or _dofork + ld sp, (U_DATA__U_SP) + + pop iy + pop ix + pop hl ; return code + + ; enable interrupts, if the ISR isn't already running + ld a, (_inint) + or a + ret z ; in ISR, leave interrupts off + ei + ret ; return with interrupts on + +switchinfail: + call outhl + ld hl, #badswitchmsg + call outstring + ; something went wrong and we didn't switch in what we asked for + jp _trap_monitor + +; (hl) points to cached page ptr, a is desired page +update_cache: + ld (hl), a + ld hl, #0 + ld de, #0x8000 + ld bc, #0x7D00 + ; map that page low (interrupts *must* be off) + out (21), a + ldir + ; put the kernel back + xor a + out (21), a + ret + +; +; Invalidate a freed page - cache becomes void +; +_invalidate_cache: + pop de + pop hl + push hl + push de + ld a, (_cached_page) + cp l + ret nz + ld a, #0xff + ld (_cached_page), a + ret + + +_flush_cache: ; argument is the process it may apply to + pop de + pop hl + push hl + push de + ld a, i + push af + ld de, #P_TAB__P_PAGE_OFFSET + 1 + add hl, de + ld a, (_cached_page) + cp (hl) + jr nz, flush_none + di + call flush_cache_self + pop af + ret po + ei + ret +flush_none: + pop af + ret + +; interrupts must be disabled +flush_cache_self: + push af + ld a, (_cached_page) + cp #0xff + jr z, no_cache + push bc + push de + push hl + ld hl, #0x8000 ; copy into the page + ld de, #0 + ld bc, #0x7D00 + ; map that page low (interrupts *must* be off) + out (21), a + ldir + ; put the kernel back + xor a + out (21), a + pop hl + pop de + pop bc +no_cache: + pop af + ret + +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. + ; --------- copy process --------- + + ld hl, (fork_proc_ptr) + ld de, #P_TAB__P_PAGE_OFFSET + add hl, de + ; load p_page + ld c, (hl) + inc hl + ; load p_page + 1 (high page) + ld b, (hl) + push af + ld a, c + call outcharhex + pop af + ; load existing page ptr + ld a, (U_DATA__U_PAGE) + + call bankfork ; do the bank to bank copy + + ; Copy done + + ld a, (U_DATA__U_PAGE) ; parent memory + out (21), a ; Switch context to parent + + ; We are going to copy the uarea into the parents uarea stash + ; we must not touch the parent uarea after this point, any + ; changes only affect the child + ld de, #U_STASH_LOW ; parent location + ld hl, #U_DATA__U_PAGE + ld a, (hl) + inc hl + cp (hl) + jr z, stash_low + ld de, #U_STASH_HIGH ; high stash +stash_low: + ld hl, #U_DATA ; copy the udata from common + ld bc, #U_DATA__TOTALSIZE + ldir + ; Return to the kernel mapping + xor a + out (21), a + ; now the copy operation is complete we can get rid of the stuff + ; _switchin will be expecting from our copy of the stack. + pop bc + pop bc + pop bc + + ; Make a new process table entry, etc. + ld hl, (fork_proc_ptr) + push hl + call _newproc + pop bc + + ; runticks = 0; + ld hl, #0 + ld (_runticks), hl + ; in the child process, fork() returns zero. + ; + ; 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 + +; +; This is related so we will keep it here. Copy the process memory +; for a fork. a is the page base of the parent, c of the child +; (this API will be insufficient once we have chmem and proper use of +; banks - as well as needing to support fork to disk) +; +; Assumption - fits into a fixed number of whole 256 byte blocks +; +; The parent must be running here, therefore the parent must be +; mapped, therefore the cached page is loaded with the parent +; +bankfork: + push af + ld hl, #U_DATA__U_PAGE + 1 + cp (hl) ; Are we a two pager ? + jr z, bankfork_low ; If not skip the high space + call flush_cache_self ; Flush the parent cache + ld a, b + ld (_cached_page), a ; High cache is now child (which runs first) + ld b, #0x80 + jr bankfork_go ; Now copy the low 32K +bankfork_low: + ld b, #0x7D ; 32K minus UAREA stash +bankfork_go: + pop af ; A is now the parent bank + ld hl, #0 ; base of memory to fork (vectors included) +bankfork_1: + push bc ; Save our counter and also child offset + push hl + out (21), a ; switch to parent bank + ld de, #bouncebuffer + ld bc, #256 + ldir ; copy into the bounce buffer + pop de ; recover source of copy to bounce + ; as destination in new bank + pop bc ; recover child page number + push bc + ld b, a ; save the parent bank id + ld a, c ; switch to the child + out (21), a + push bc ; save the bank pointers + ld hl, #bouncebuffer + ld bc, #256 + ldir ; copy into the child + pop bc ; recover the bank pointers + ex de, hl ; destination is now source for next bank + ld a, b ; parent bank is wanted in a + pop bc + djnz bankfork_1 ; rinse, repeat + ret + +; +; For the moment +; +bouncebuffer: + .ds 256 +; +; We can keep a stack in common because we will complete our +; use of it before we switch common block. In this case we have +; a true common so it's even easier. This can share with the bounce +; buffer used by bankfork as we won't switchin mid way through the +; banked fork() call. +; +_swapstack: +_cached_page: + .db 0xff diff --git a/Kernel/platform-z80pack32/uzi.lnk b/Kernel/platform-z80pack32/uzi.lnk new file mode 100644 index 00000000..9f6e9968 --- /dev/null +++ b/Kernel/platform-z80pack32/uzi.lnk @@ -0,0 +1,38 @@ +-mwxuy +-i uzi.ihx +-b _CODE=0x0088 +-b _CODE3=0xC000 +-b _COMMONMEM=0xF000 +-b _DISCARD=0xB000 +-l z80 +platform-z80pack32/crt0.rel +platform-z80pack32/commonmem.rel +platform-z80pack32/z80pack.rel +platform-z80pack32/main.rel +start.rel +version.rel +lowlevel-z80.rel +usermem_std-z80.rel +platform-z80pack32/tricks.rel +timer.rel +kdata.rel +usermem.rel +platform-z80pack32/devfd.rel +platform-z80pack32/devices.rel +devio.rel +filesys.rel +process.rel +inode.rel +syscall_exec.rel +syscall_fs.rel +syscall_fs2.rel +syscall_proc.rel +syscall_other.rel +tty.rel +mm.rel +bank32k.rel +swap.rel +devsys.rel +platform-z80pack32/devlpr.rel +platform-z80pack32/devtty.rel +-e diff --git a/Kernel/platform-z80pack32/z80pack.s b/Kernel/platform-z80pack32/z80pack.s new file mode 100644 index 00000000..13900249 --- /dev/null +++ b/Kernel/platform-z80pack32/z80pack.s @@ -0,0 +1,214 @@ +; +; Z80Pack hardware support +; +; +; This goes straight after udata for common. Because of that the first +; 256 bytes get swapped to and from disk with the uarea (512 byte disk +; blocks). This isn't a problem but don't put any variables in here. +; +; If you make this module any shorter, check what follows next +; + + + .module z80pack + + ; exported symbols + .globl init_early + .globl init_hardware + .globl _program_vectors + .globl _system_tick_counter + .globl platform_interrupt_all + + .globl map_kernel + .globl map_process + .globl map_process_always + .globl map_save + .globl map_restore + + .globl _fd_bankcmd + + .globl _kernel_flag + + ; exported debugging tools + .globl _trap_monitor + .globl outchar + + ; imported symbols + .globl _ramsize + .globl _procmem + + .globl unix_syscall_entry + .globl null_handler + .globl nmi_handler + .globl interrupt_handler + + .globl outcharhex + .globl outhl, outde, outbc + .globl outnewline + .globl outstring + .globl outstringhex + + .include "kernel.def" + .include "../kernel.def" + +; ----------------------------------------------------------------------------- +; COMMON MEMORY BANK (0xF000 upwards) +; ----------------------------------------------------------------------------- + .area _COMMONMEM + +_trap_monitor: + ld a, #128 + out (29), a +platform_interrupt_all: + ret + +_trap_reboot: + ld a, #1 + out (29), a + +; +; We need the right bank present when we cause the transfer +; +_fd_bankcmd:pop de ; return + pop bc ; command + pop hl ; bank + push hl + push bc + push de ; fix stack + ld a, i + di + push af ; save DI state + call map_process ; HL alread holds our bank + ld a, c ; issue the command + out (13), a ; + call map_kernel ; return to kernel mapping + pop af + ret po + ei + ret + +; ----------------------------------------------------------------------------- +; KERNEL MEMORY BANK (below 0xC000, only accessible when the kernel is mapped) +; ----------------------------------------------------------------------------- + .area _CODE + +init_early: + ld a, #240 ; 240 * 256 bytes (60K) + out (22), a ; set up memory banking + ld a, #8 + out (20), a ; 8 segments + ret + +init_hardware: + ; set system RAM size + ld hl, #480 + ld (_ramsize), hl + ld hl, #(480-64) ; 64K for kernel + ld (_procmem), hl + + ld a, #1 + out (27), a ; 100Hz timer on + + ; set up interrupt vectors for the kernel (also sets up common memory in page 0x000F which is unused) + ld hl, #0 + push hl + call _program_vectors + pop hl + + im 1 ; set CPU interrupt mode + ret + + +;------------------------------------------------------------------------------ +; COMMON MEMORY PROCEDURES FOLLOW + + .area _COMMONMEM + + +_program_vectors: + ; we are called, with interrupts disabled, by both newproc() and crt0 + ; will exit with interrupts off + di ; just to be sure + pop de ; temporarily store return address + pop hl ; function argument -- base page number + push hl ; put stack back as it was + push de + + 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, #interrupt_handler + ld (0x0039), hl + + ; set restart vector for UZI system calls + ld (0x0030), a ; (rst 30h is unix function call vector) + ld hl, #unix_syscall_entry + ld (0x0031), hl + + ; Set vector for jump to NULL + ld (0x0000), a + ld hl, #null_handler ; to Our Trap Handler + ld (0x0001), hl + + ld (0x0066), a ; Set vector for NMI + ld hl, #nmi_handler + ld (0x0067), hl + + ; our platform has a "true" common area, if it did not we would + ; need to copy the "common" code into the common area of the new + ; process. + + ; falls through + + ; put the paging back as it was -- we're in kernel mode so this is predictable +map_kernel: + push af + xor a + out (21), a + pop af + ret +map_process: + ld a, h + or l + jr z, map_kernel + ld a, (hl) + out (21), a + ret +map_process_always: + push af + ld a, (U_DATA__U_PAGE) + out (21), a + pop af + ret +map_save: + push af + in a, (21) + ld (map_store), a + pop af + ret +map_restore: + push af + ld a, (map_store) + out (21), a + pop af + ret +map_store: + .db 0 + +_kernel_flag: + .db 1 + +; outchar: Wait for UART TX idle, then print the char in A +; destroys: AF +outchar: + out (0x01), a + ret diff --git a/Kernel/swap.c b/Kernel/swap.c index f9f64545..ea395969 100644 --- a/Kernel/swap.c +++ b/Kernel/swap.c @@ -16,8 +16,8 @@ #define UDATA_BLOCKS 0 #endif -#ifndef swap_flush_cache -#define swap_flush_cache(p) do {} while(0) +#ifndef CONFIG_COMMON_COPY +#define flush_cache(p) do {} while(0) #endif uint8_t *swapbase; @@ -77,7 +77,7 @@ int swapout(ptptr p) /* Are we out of swap ? */ if (swapptr == 0) return ENOMEM; - swap_flush_cache(p); + flush_cache(p); map = swapmap[--swapptr]; blk = map * SWAP_SIZE; #ifdef UDATA_SWAPSIZE -- 2.34.1