From: Alan Cox Date: Sun, 9 Dec 2018 00:49:33 +0000 (+0000) Subject: cromemco: further pass over the Cromemco code X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=20dd462459002810345669bcc50fddd957dff2e9;p=FUZIX.git cromemco: further pass over the Cromemco code Hopefully now we have thunked mappings this becomes a bit more viable --- diff --git a/Kernel/platform-cromemco/Makefile b/Kernel/platform-cromemco/Makefile index 2e8ab09c..6a6a87bd 100644 --- a/Kernel/platform-cromemco/Makefile +++ b/Kernel/platform-cromemco/Makefile @@ -2,7 +2,7 @@ CSRCS = devtty.c devfd.c CSRCS += devices.c main.c -ASRCS = crt0.s cromemco.s +ASRCS = crt0.s cromemco.s usermem.s ASRCS += tricks.s commonmem.s AOBJS = $(ASRCS:.s=.rel) @@ -10,7 +10,7 @@ COBJS = $(CSRCS:.c=.rel) OBJS = $(AOBJS) $(COBJS) -JUNK = *.rel *.lst *.asm *.sym *.rst + all: $(OBJS) @@ -21,7 +21,10 @@ $(COBJS): %.rel: %.c $(CROSS_CC) $(CROSS_CCOPTS) -c $< clean: - rm -f $(OBJS) $(JUNK) core *~ + rm -f *.rel *.lst *.asm *.sym *.rst core *~ + rm -f cloader-16fdc.ihx cloader-16fdc.tmp + rm -f cloader-16fdc.lk cloader-16fdc.bin + rm -f cloader-16fdc.map image: sdasz80 -o cloader-16fdc.s diff --git a/Kernel/platform-cromemco/README b/Kernel/platform-cromemco/README index 7be076f8..efad738d 100644 --- a/Kernel/platform-cromemco/README +++ b/Kernel/platform-cromemco/README @@ -1,6 +1,31 @@ FUZIX on a Cromenco (no hard disk support right now - need docs for that and suitable emulation!) +The mapping model is intended to be + +Bank 0 Kernel + 0000-00FF, F200-FFFF are propogated into the other banks + +Bank 1-6 User + 0000-00FF start with shared vectors + FIXME: we need to clean this up in program_vectors + from the kernel copy ???? + 0100-EFFF Application + F000-F111 Udata copy for this application + F200-FFFF common copy + +The kstack and istack exist in each bank. We'll normally only use the kernel +one but there are cases we switch and borrow a bit of the other bank copy. +It's easier to arrange this way anyhow. + + +TODO +- Debug a loader +- An awful lot of early kernel boot debugging +- Interrupts properly (and IM2) + + + We expect 0 tuart / 4 FDC diff --git a/Kernel/platform-cromemco/cloader-16fdc.s b/Kernel/platform-cromemco/cloader-16fdc.s index 285e5d57..06cf742f 100644 --- a/Kernel/platform-cromemco/cloader-16fdc.s +++ b/Kernel/platform-cromemco/cloader-16fdc.s @@ -1,116 +1,113 @@ - .area ASEG(ABS) - .org 0x80 + .area ASEG(ABS) + .org 0x80 ; -; Loader for Fuzix on a Cromenco 16FDC +; We have 128 bytes and are loaded by the ROM at 0x0080. The rest is +; bascially our problem ; -; The kernel is written in blocks 1+ SS/SD with no magic -; involved. We have 128 bytes, but fortunately a ROM helper +; Load sectors 2-26 ( a whopping 3.2K ) ; +; Assumes an 8" disk in SD mode ; -; Memory map -; 0x0000-0x002E Stack -; 0x0060-0x007F Used by the boot ROM -; 0x0080-0x00FF This boot block -; 0x0100-0xBFFF Free RAM -; 0xC000-0xCFFF Boot ROM (until we out to 0x40) -; 0xD000-0xFFFF May be RAM but may also be ROM shadows -; + ld a,#1 + out (0x40),a ; ROM off + + ld hl,#go + call strout + +restart: + ld bc,#0x020F ; sector 2 , restore + ld de,#0x3100 ; D 8", A drive motor on E track + + ld hl,#0x0100 + ld a,#0x7f + out (0x04),a ; Side 0 + + ld a,d ; set up drive + out (0x34),a + + ld a,c + +movecmd: + out (0x30),a ; now doing a restore at lowest speed -set_side .equ 0xC003 -set_track .equ 0xC006 -set_sector .equ 0xC009 -set_buffer .equ 0xC00C -disk_restore .equ 0xC00F -disk_seek .equ 0xC012 -disk_read .equ 0xC015 -disk_write .equ 0xC018 -setup_uart .equ 0xC01B -char_input .equ 0xC01E -char_ready .equ 0xC021 -line_input .equ 0xC024 -char_output .equ 0xC027 -newline_output .equ 0xC02A - - .globl start - .globl endc - .globl end - .globl msg -; Carry set, drive in A -start: - push af - ld hl,#boot - call tout - ld hl,#0xFC - pop af - ld (hl),a ; boot drive - inc hl - ld a,#16 - ld (hl),a ; controller type (16FDC) - inc hl - ld bc,(0x7E) ; features and feature save for boot disk - ld (hl),c - inc hl - ld (hl),b - inc hl ; now 0x100 xor a - ld b,a - call set_side - ld a,(0x6E) ; sectors per track - inc a - ld c,a - ld a,#1 ; sector 2 - ld de,#128 -trackloop: - push af +wait: dec a + jr nz,wait + +wait2: in a,(0x34) ; when restore is done we are good + rra + jr nc, wait2 + + in a,(0x30) + and #0x98 + jr nz, restart + + ; Ok drive is up and running + ld a,b - call set_track - pop af -secloop: - inc a - call set_sector - call set_buffer - ex af,af' ; shorter than pushing them - exx - call disk_seek - call disk_read - jr c, crap - exx - add hl,de - ld a,h - cp #0xC0 - jr z,go - ex af,af' - cp c - jr nz, secloop - inc b - xor a - jr trackloop -go: - ld a,(0x100) - cp #0xC3 - jr z, 0x100 -crap: - ld hl, #fail - call tout - jp 0xC000 - -tout: + out (0x32),a ; sector we want + ld a,d + or #0x80 ; autowait + out (0x34),a + + ld c,#0x33 + ld a,#0x9c ; read multiple 128byte, head load delay + out (0x30),a ; load heads and read + + ; autowait means we don't poll the ready bit +eoj: + in a,(0x34) + rra + jr c, trackend + ini + jp eoj +trackend: + ; + ; FIXME: now step in until we hit track 19 (63000 bytes) + ; + in a,(0x30) + bit 4,a + jr z,restart + + ld a,d + out (0x34),a ; turn off autowait + + inc e + ld a,#19 + cp e + jr z, run + + ld a,#'.' ; We know that one byte per track will not + out (0),a ; overrun! + + ld b,#0x01 ; run from sector 1 + + ; + ; Step in a track + ; + ld a,#0x5F ; step in with verify and update, slowest + + + jr movecmd + +strout: + in a,(0x0) + rla + jr nc,strout ld a,(hl) - inc hl or a ret z - push hl - call char_output - pop hl - jr tout + out (0),a + inc hl + jr strout -msg: -boot: .asciz 'Boot' -fail: .ascii ' failed' - .byte 13,10,0 +go: .ascii 'FUZIX LOADER' +nl: .db 13,10,0 -endc: - .org 0xF8 + .ds 1 - .ascii 'FZSSSD' +run: + ld hl,#nl + call strout end: + ; and runs on into 0x100 diff --git a/Kernel/platform-cromemco/config.h b/Kernel/platform-cromemco/config.h index 842cd87e..15be6e17 100644 --- a/Kernel/platform-cromemco/config.h +++ b/Kernel/platform-cromemco/config.h @@ -20,8 +20,8 @@ #define TICKSPERSEC 10 /* Ticks per second */ #define PROGBASE 0x0000 /* also data base */ #define PROGLOAD 0x0100 /* also data base */ -#define PROGTOP 0xED00 /* Top of program, base of U_DATA copy */ -#define PROC_SIZE 60 /* Memory needed per process */ +#define PROGTOP 0xF000 /* Top of program, base of U_DATA copy */ +#define PROC_SIZE 60 /* Memory needed per process */ #define BOOT_TTY (512 + 1)/* Set this to default device for stdio, stderr */ /* In this case, the default is the first TTY device */ diff --git a/Kernel/platform-cromemco/cromemco.s b/Kernel/platform-cromemco/cromemco.s index 633cbd99..f598ed4c 100644 --- a/Kernel/platform-cromemco/cromemco.s +++ b/Kernel/platform-cromemco/cromemco.s @@ -2,228 +2,151 @@ ; Cromemco hardware support ; - .module cromemco - - ; exported symbols - .globl init_early - .globl init_hardware - .globl _program_vectors - .globl platform_interrupt_all - - .globl map_kernel - .globl map_process - .globl map_process_always - .globl map_kernel_di - .globl map_process_di - .globl map_process_always_di - .globl map_process_a - .globl map_save_kernel - .globl map_restore - - .globl _platform_reboot - - .globl _int_disabled - - ; exported debugging tools - .globl _platform_monitor - .globl outchar - - ; imported symbols - .globl _ramsize - .globl _procmem - - .globl unix_syscall_entry - .globl null_handler - .globl nmi_handler - .globl interrupt_handler - .globl _doexit - .globl _inint - .globl kstack_top - .globl _panic - .globl _need_resched - .globl _ssig - - .globl outcharhex - .globl outhl, outde, outbc - .globl outnewline - .globl outstring - .globl outstringhex - - .include "kernel.def" - .include "../kernel.def" + .module cromemco + + ; exported symbols + .globl init_early + .globl init_hardware + .globl _program_vectors + .globl platform_interrupt_all + + .globl map_kernel_low + .globl map_save_low + .globl map_restore_low + .globl map_user_low + .globl map_page_low + + .globl _platform_reboot + + .globl _platform_doexec + + .globl _int_disabled + + ; exported debugging tools + .globl _platform_monitor + .globl outchar + + ; imported symbols + .globl _ramsize + .globl _procmem + + .globl s__COMMONMEM + .globl l__COMMONMEM + + .globl unix_syscall_entry + .globl my_nmi_handler + .globl interrupt_handler + .globl _doexit + .globl _inint + .globl kstack_top + .globl istack_top + .globl istack_switched_sp + .globl _panic + .globl _need_resched + .globl _ssig + + .globl outcharhex + .globl outhl, outde, outbc + .globl outnewline + .globl outstring + .globl outstringhex + + .include "kernel.def" + .include "../kernel.def" ; ----------------------------------------------------------------------------- -; COMMON MEMORY BANK (0xF000 upwards) +; COMMON MEMORY BANK (0xF200 upwards) ; ----------------------------------------------------------------------------- - .area _COMMONMEM + .area _COMMONMEM _platform_reboot: _platform_monitor: - jr _platform_monitor + jr _platform_monitor platform_interrupt_all: - ret + ret _int_disabled: - .db 1 + .db 1 ; ----------------------------------------------------------------------------- -; KERNEL MEMORY BANK (below 0xF000, only accessible when the kernel is mapped) +; KERNEL MEMORY BANK (only accessible when the kernel is mapped) ; ----------------------------------------------------------------------------- - .area _CODE + .area _CODE init_early: - ld a,#0x81 ; Every memory is in bank 7 - out (0x40),a ; MMU set - ld hl,#0xF000 ; Copy 4K to itself loading - ld d,h ; into into the other banks - ld e,l - ld b,#0x10 - ld c,l - ldir - ld a,#0x01 ; bank to the kernel bank - out (0x40),a - ret + ld a,#0x81 ; Every memory writeable, read kernel + out (0x40),a ; MMU set + + ; Write the stubs everywhere + ld hl,#stubs_low + ld de,#0x0000 + ld bc,#0x67 + ldir + + ; And the common across all banks + ld hl,#s__COMMONMEM + ld d,h + ld e,l + ld bc,#l__COMMONMEM + ldir + + ld a,#0x01 ; bank to the kernel bank + out (0x40),a + ret init_hardware: - ; set system RAM size - ld hl, #448 - ld (_ramsize), hl - ld hl, #(448-64) ; 64K for kernel - ld (_procmem), hl - - ld a, #156 ; ticks for 10Hz (9984uS per tick) - out (8), a ; 10Hz timer on + ; set system RAM size + ld hl, #448 ; Assuming fully loaded for now + ld (_ramsize), hl + ld hl, #(448-64) ; 64K for kernel + ld (_procmem), hl - ; 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 + ld a, #156 ; ticks for 10Hz (9984uS per tick) + out (8), a ; 10Hz timer on -; ld a, #0xfe ; Use FEFF (currently free) -; ld i, a -; im 2 ; set CPU interrupt mode - im 1 ; really should use a page and im2? - ret + im 1 ; really should use a page and im2? + ret ;------------------------------------------------------------------------------ ; COMMON MEMORY PROCEDURES FOLLOW - .area _COMMONMEM + .area _COMMONMEM +; +; We switch in one go so we don't have these helpers. This means +; we need custom I/O wrappers and custom usercopy functions. +; +map_save_low: +map_kernel_low: +map_restore_low: +map_user_low: +map_page_low: + ret _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 0x38 - ld hl, #interrupt_handler - ld (0x39), hl - - ld a,#0xC3 ; JP - ; set restart vector for UZI system calls - ld (0x0030), a ; (rst 30h is unix function call vector) - ld (0x0038), a ; (rst 38h) - ld hl, #unix_syscall_entry - ld (0x0031), hl - - ; now install the interrupt vector at 0x38 - ld hl, #interrupt_handler - ld (0x39), 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 - - ; falls through - - ; put the paging back as it was -- we're in kernel mode so this is predictable -map_kernel: -map_kernel_di: - push af - ld a,#1 - out (0x40), a - ld (map_page), a ; map_page lives in kernel so be careful - pop af ; our common is r/o common so writes won't - ret ; cross a bank -map_process: -map_process_di: - ld a, h - or l - jr z, map_kernel - ld a, (hl) -map_process_a: - ld (map_page),a ; save before we map out kernel - out (0x40), a - ret -map_process_always: -map_process_always_di: - push af - ld a, (U_DATA__U_PAGE) - ld (map_page),a ; save before we map out kernel - out (0x40), a - pop af - ret -map_save_kernel: - push af ; map_save will always do a map_kernel - ld a, #1 ; map_kernel so we do the map_kernel - out (0x40), a ; first so we can get the variables - ld a, (map_page) ; - ld (map_store), a - pop af - ret -map_restore: ; called in kernel map - push af - ld a, (map_store) - ld (map_page),a - out (0x40), a - pop af - ret -map_store: - .db 0 -map_page: - .db 0 + ret ; outchar: Wait for UART TX idle, then print the char in A ; destroys: AF outchar: - push af + push af outcharl: - in a, (0x00) - bit 7,a - jr z, outcharl - pop af - out (0x01), a - ret + in a, (0x00) + bit 7,a + jr z, outcharl + pop af + out (0x01), a + ret ; ; Low level pieces for floppy driver ; - .globl _fd_reset - .globl _fd_seek - .globl _fd_operation - .globl _fd_map - .globl _fd_cmd + .globl _fd_reset + .globl _fd_seek + .globl _fd_operation + .globl _fd_map + .globl _fd_cmd ; ; On entry c holds the bits from 0x34, bde must be preserved @@ -285,13 +208,6 @@ nodisk: ; ; _fd_operation: - ; - ; We run in common. Map the kernel or user according to the - ; destination of the transfer - ; - ld a, (_fd_map) - or a - call nz, map_process_always ; ; Set up the registers in order. The aux register is already ; done by our caller @@ -347,9 +263,22 @@ nomod: issue_command: ld hl,(_fd_cmd+3) ; buffer ld c,#0x33 ; data port - ld a,b + ld e,b ; save command code + di + + ; If need be flip bank. Remember this also switches stack copy + ld a,(_fd_map) + or a + jr nz, cmdout + + out (0x40),a ; switch bank and stack copy +cmdout: + ld a,e out (0x30),a ; issue the command + ld a,(_fd_map) + + ; FIXME: mappings ; ; For now we only do double density (256 words per sector) ; @@ -375,17 +304,24 @@ patch2: outi ; second byte out ; The transfer is done - wait for EOJ ; fd_waiteoj: + ; + ; Memory and stack back + ; + ld a,#1 + out (0x40),a ; kernel map back +fd_waiteoj_l: ei in a,(0x34) rra - jr nc, fd_waiteoj + jr nc, fd_waiteoj_l ; ; Job complete. Turn off autowait and recover ; the status byte ; fd_done: + ld a,#1 + out (0x40),a ; kernel map back ei - call map_kernel ld a,d ; 0x34 bits and #0x7F out (0x34),a ; autowait off @@ -477,3 +413,280 @@ delayhl3: pop bc ; 10 ret ; 10 +; +; Don't be tempted to put the symbol in the code below ..it's relocated +; to zero. Instead define where it ends up. +; + +_platform_doexec .equ 0x18 + + .area _DISCARD + + .globl rst38 + .globl stubs_low +; +; This exists at the bottom of each bank. We move these into place +; from discard. +; +stubs_low: + .byte 0xC3 + .word 0 ; cp/m emu changes this + .byte 0 ; cp/m emu I/O byte + .byte 0 ; cp/m emu drive and user + jp 0 ; cp/m emu bdos entry point +rst8: + ret + nop + nop + nop + nop + nop + nop + nop +rst10: + ret + nop + nop + nop + nop + nop + nop + nop +rst18: + ; Activate the user bank (which also holds these bytes) + ld a,(U_DATA__U_PAGE) + out (0x40),a + ei + ; and leap into user space + jp (hl) + nop + nop +rst20: ret + nop + nop + nop + nop + nop + nop + nop +rst28: ret + nop + nop + nop + nop + nop + nop + nop +rst30: jp syscall_high + nop + nop + nop + nop + nop +; +; We only have 38-4F available for this in low space +; +rst38: jp interrupt_high ; Interrupt handling stub + nop + nop + nop + nop + nop + .ds 0x26 +my_nmi_handler: ; Should be at 0x66 + retn + +; +; This stuff needs to live somewhere, anywhere out of the way (so we +; use common). We need to copy it to the same address on both banks +; so place it in common as we will put common into both banks +; + + .area _COMMONMEM + + .globl ldir_to_user + .globl ldir_from_user + .globl ldir_far +; +; This needs some properly optimized versions! +; +ldir_to_user: + ld de,(U_DATA__U_PAGE) ; will load with 0 e with page + inc d ; Kernel is in #1 +ldir_far: + push bc + ld c,#0x40 + exx + pop bc ; get BC into alt bank +far_ldir_1: + exx + out (c),d ; Select source + ld a,(hl) + inc hl + out (c),e ; Select target + ld (ix),a + inc ix + exx + dec bc + ld a,b + or c + jr nz, far_ldir_1 + ld a,#1 + out (0x40),a ; Select kernel + ret +ldir_from_user: + ld a,(U_DATA__U_PAGE) + ld e,#1 + ld d,a + jr ldir_far +; +; High stubs. Present in each bank in the top 256 bytes +; of the available space (remembering F000-FFFF is not available +; for general usage but is ok for reading) +; +interrupt_high: + push af + push de + push hl + ex af,af' + push af + push bc + exx + push bc + push de + push hl + push ix + push iy + in a,(0x40) ; bank register is thankfully R/W + ld c,a + ld a,#0x01 + out (0x40),a ; Kernel map + ld (istack_switched_sp),sp ; istack is positioned to be valid + ld sp,#istack_top ; in both banks. We just have to + ; + ; interrupt_handler may come back on a different stack in + ; which case bc is junk. Fortuntely we never pre-empt in + ; kernel so the case we care about bc is always safe. This is + ; not a good way to write code and should be fixed! FIXME + ; + push bc + call interrupt_handler ; switch on the right SP + pop bc + ; Restore stack pointer to user. This leaves us with an invalid + ; stack pointer if called from user but interrupts are off anyway + ld sp,(istack_switched_sp) + ; On return HL = signal vector E= signal (if any) A = page for + ; high + or a + jr z, kernout + ; Returning to user space + out (0x40),a ; page passed back + ; User stack is now valid + ; back on user stack + xor a + cp e + call nz, sigpath +pops: + ex af,af' + exx + pop iy + pop ix + pop hl + pop de + pop bc + exx + pop bc + pop af + ex af,af' + pop hl + pop de + pop af + ei + ret +kernout: + ; restore bank - if we interrupt mid user copy or similar we + ; have to put the right bank back + ld a,c + out (0x40),a + jr pops + +sigpath: + push de ; signal number + ld de,#irqsigret + push de ; clean up + jp (hl) +irqsigret: + inc sp ; drop signal number + inc sp + ret + +syscall_high: + push ix + ld ix,#0 + add ix,sp + push de ; the syscall if must preserve de for now + ; needs fixing when we change the syscall + ; API for Z80 to something less sucky + ld a,4(ix) + ld c,6(ix) + ld b,7(ix) + ld e,8(ix) + ld d,9(ix) + ld l,10(ix) + ld h,11(ix) + push hl + ld l,12(ix) + ld h,13(ix) + pop ix + di + ; FIXME: corrupts af' - probably will define af' and some other ' + ; registers as non-save in API change anyway + ex af, af' ; Ick - find a better way to do this bit ! + ld a,#1 ; Kernel + out (0x40),a + ex af,af' + ; Stack now invalid + ld (U_DATA__U_SYSCALL_SP),sp + ld sp,#kstack_top + call unix_syscall_entry + ; FIXME check di rules + ; stack now invalid. Grab the new sp before we unbank the + ; memory holding it + ld sp,(U_DATA__U_SYSCALL_SP) + ld a, (U_DATA__U_PAGE) ; back to the user page + out (0x40),a + xor a + cp h + call nz, syscall_sigret + ; FIXME for now do the grungy C flag HL DE stuff from + ; lowlevel-z80 until we fix the ABI + ld bc,#0 + ld a,h + or l + jr nz, error + ex de,hl + pop de + pop ix + ei + ret +error: scf + pop de + pop ix + ei + ret +syscall_sigret: + ld a,l ; DEBUG + push hl ; save errno + push de ; save retval + ld l,h + ld h,#0 + push hl ; signal + ld hl,#syscall_sighelp + push hl ; vector + ret +syscall_sighelp: + pop de ; discard signal + pop de ; recover error info + pop hl + ld h,#0 ; clear signal bit + ret diff --git a/Kernel/platform-cromemco/crt0.s b/Kernel/platform-cromemco/crt0.s index 027a3034..1df55fbc 100644 --- a/Kernel/platform-cromemco/crt0.s +++ b/Kernel/platform-cromemco/crt0.s @@ -44,15 +44,24 @@ init: ; Configure memory map call init_early - ; move the common memory where it belongs + ld a,#0x81 ; Every memory writeable, read kernel + out (0x40),a ; MMU set + + ; move the common memory where it belongs in all banks + ld hl, #s__DATA ld de, #s__COMMONMEM ld bc, #l__COMMONMEM ldir - ; and the discard + + ld a,#0x01 ; Kernel mapping only + out (0x40),a ; MMU set + + ; and the discard to the kernel ld de, #s__DISCARD ld bc, #l__DISCARD ldir + ; then zero the data area ld hl, #s__DATA ld de, #s__DATA + 1 diff --git a/Kernel/platform-cromemco/fuzix.lnk b/Kernel/platform-cromemco/fuzix.lnk index 886a5e12..c1e5e7c2 100644 --- a/Kernel/platform-cromemco/fuzix.lnk +++ b/Kernel/platform-cromemco/fuzix.lnk @@ -1,7 +1,7 @@ -mwxuy -i fuzix.ihx -b _CODE=0x0100 --b _COMMONMEM=0xF000 +-b _COMMONMEM=0xF200 -b _DISCARD=0xE000 -l z80 platform-cromemco/crt0.rel @@ -10,12 +10,12 @@ platform-cromemco/cromemco.rel platform-cromemco/main.rel start.rel version.rel -lowlevel-z80.rel -usermem_std-z80.rel +lowlevel-z80-thunked.rel platform-cromemco/tricks.rel timer.rel kdata.rel usermem.rel +platform-cromemco/usermem.rel platform-cromemco/devfd.rel platform-cromemco/devices.rel devio.rel diff --git a/Kernel/platform-cromemco/kernel.def b/Kernel/platform-cromemco/kernel.def index f3a5687e..def2cec1 100644 --- a/Kernel/platform-cromemco/kernel.def +++ b/Kernel/platform-cromemco/kernel.def @@ -1,9 +1,9 @@ ; 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_DATA .equ 0xF200 ; (this is struct u_data from kernel.h) +U_DATA__TOTALSIZE .equ 0x200 ; 256+256 bytes -U_DATA_STASH .equ 0xED00 ; ED00-EFFF +U_DATA_STASH .equ 0xF000 ; F000-F200 PROGBASE .equ 0x0000 PROGLOAD .equ 0x0100 @@ -12,5 +12,12 @@ Z80_TYPE .equ 1 Z80_MMU_HOOKS .equ 0 - CONFIG_SWAP .equ 0 + +; +; Select where to put the high code - in our case we need this +; in _COMMONMEM +; +.macro HIGH + .area _COMMONMEM +.endm diff --git a/Kernel/platform-cromemco/main.c b/Kernel/platform-cromemco/main.c index 8f83fac5..5952c42f 100644 --- a/Kernel/platform-cromemco/main.c +++ b/Kernel/platform-cromemco/main.c @@ -6,6 +6,7 @@ uaddr_t ramtop = PROGTOP; void pagemap_init(void) { int i; + /* 1 << 0 is kernel */ for (i = 1; i < 7; i++) pagemap_add(1 << i); } diff --git a/Kernel/platform-cromemco/tricks.s b/Kernel/platform-cromemco/tricks.s index ee50c077..47bc2daf 100644 --- a/Kernel/platform-cromemco/tricks.s +++ b/Kernel/platform-cromemco/tricks.s @@ -2,4 +2,307 @@ .include "../kernel.def" .include "kernel.def" - .include "../lib/z80fixedbank.s" + ; + ; We can't quite use the standard helpers as we have rather weird + ; memory mapping logic + ; + .module cromemcotricks + + .globl _ptab_alloc + .globl _makeproc + .globl _chksigs + .globl _getproc + .globl _platform_monitor + .globl _platform_switchout + .globl _switchin + .globl _doexec + .globl _dofork + .globl _runticks + .globl unix_syscall_entry + .globl interrupt_handler + .globl _need_resched + .globl _nready + .globl _platform_idle + .globl _int_disabled + .globl _udata + + .globl ldir_to_user + .globl ldir_from_user + .globl ldir_far + + ; imported debug symbols + .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex + + .area _COMMONMEM + +; __switchout switches out the current process, finds another that is READY, +; possibly the same process, and switches it in. When a process is +; restarted after calling switchout, it thinks it has just returned +; from switchout(). +; +; This function can have no arguments or auto variables. +; +_platform_switchout: + 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 + + ; Stash the uarea back into process memory + ld hl, #U_DATA + ld ix, #U_DATA_STASH + ld bc, #U_DATA__TOTALSIZE + ; Slow - must be a more elegant way to tackle this ! FIXME + call ldir_to_user + + ; 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 +swapped: .ascii "_switchin: SWAPPED" + .db 13, 10, 0 + +_switchin: + di + pop bc ; return address + pop de ; new process pointer + + ld hl, #P_TAB__P_PAGE_OFFSET + add hl, de ; process ptr + +.ifne CONFIG_SWAP + .globl _swapper + ; + ; Always use the swapstack, otherwise when we call map_kernel + ; having copied the udata stash back to udata we will crap + ; somewhere up the stackframe and it's then down to luck + ; if those bytes are discarded or not. + ; + ; Yes - this was a bitch to debug, please don't break it ! + ; + ld sp, #_swapstack + + ld a, (hl) + + or a + jr nz, not_swapped + + ; + ; Re-enable interrupts while we swap. This is ok because + ; we are not on the IRQ stack when switchin is invoked. + ; + ; There are two basic cases + ; #1: pre-emption. Not in a system call, must avoid + ; re-entering pre-emption logic, Z80 lowlevel code sets U_INSYS + ; #2: kernel syscall. Also protected by U_DATA__U_INSYS + ; + ei + xor a + ld (_int_disabled),a + push hl + push de + call _swapper + pop de + pop hl + ld a,#1 + ld (_int_disabled),a + di +.endif + ld a, (hl) +not_swapped: + ; We need the swap stack anyway or we run out of registers + ld sp, #_swapstack + + ld hl, (U_DATA__U_PTAB) + or a + sbc hl, de + jr z, skip_copyback ; Tormod's optimisation: don't copy the + ; the stash back if we are the task who + ; last owned the real udata + + ; bear in mind that the stack will be switched now, so we can't use it + ; to carry values over this point + + push de + + ld hl, #U_DATA_STASH + ld de, #U_DATA + ld bc, #U_DATA__TOTALSIZE + call ldir_to_user + + pop de + + ; In the non swap case we must set so before we use the stack + ; otherwise we risk corrupting the restored stack frame + ld sp, (U_DATA__U_SP) + + ; check u_data->u_ptab matches what we wanted + ld hl, (U_DATA__U_PTAB) ; u_data->u_ptab + or a ; clear carry flag + sbc hl, de ; subtract, result will be zero if DE==HL + jr nz, switchinfail + +skip_copyback: + ; wants optimising up a bit + ld ix, (U_DATA__U_PTAB) + ; next_process->p_status = P_RUNNING + ld P_TAB__P_STATUS_OFFSET(ix), #P_RUNNING + + ; Fix the moved page pointers + ; Just do one byte as that is all we use on this platform + ld a, P_TAB__P_PAGE_OFFSET(ix) + ld (U_DATA__U_PAGE), a + ; runticks = 0 + ld hl, #0 + ld (_runticks), hl + + ; restore machine state -- note we may be returning from either + ; _switchout or _dofork + ld sp, (U_DATA__U_SP) + + pop iy + pop ix + pop hl ; return code + + ; enable interrupts, if we didn't pre-empt in an ISR + ld a, (U_DATA__U_ININTERRUPT) + ld (_int_disabled),a + or a + ret nz ; Not an 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. + + ; + ; FIXME: we should no longer need interrupts off for most of a + ; fork() call. + ; + 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) + ; load existing page ptr + ld a, (U_DATA__U_PAGE) + + push bc + call bankfork ; do the bank to bank copy + pop bc + + ; Copy done + + ; 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 hl, #U_DATA ; copy the udata from common into the + ld de, #U_DATA_STASH ; target process + ld bc, #U_DATA__TOTALSIZE + + call ldir_to_user + + ; 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,#_udata + push hl + ld hl, (fork_proc_ptr) + push hl + call _makeproc + pop bc + 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 + + .area _COMMONMEM +; +; We can keep a stack because we don't have enough registers +; to do all the bank trickery we need +; + .ds 256 +_swapstack: +_need_resched: .db 0 + + ; + ; FIXME: optimize ldir_far. or maybe add an ldir_far_aligned + ; This one is really hard to do at a sensible speed + ; +bankfork: + ld d,c ; source + ld e,a ; dest + ld hl,#0x0000 + ld ix,#0x0000 + ld bc,#0xF000 + call ldir_far + ret