From: Alan Cox Date: Sat, 1 Sep 2018 21:09:22 +0000 (+0100) Subject: z80fixedbank: Split into two X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=9012115701fd4e9ca442112739523b3c153f088c;p=FUZIX.git z80fixedbank: Split into two This allows someone to opt out of the default fork bank copier if they have say a DMA engine or want to write their own fast platform specific code. --- diff --git a/Kernel/lib/z80fixedbank-core.s b/Kernel/lib/z80fixedbank-core.s new file mode 100644 index 00000000..72cdbc9b --- /dev/null +++ b/Kernel/lib/z80fixedbank-core.s @@ -0,0 +1,302 @@ +; +; For a purely bank based architecture this code is common and can be +; used by most platforms +; +; The caller needs to provide the standard map routines along with +; map_kernel_a which maps in the kernel bank in a. This code assumes +; that the bank can be encoded in 8bits. +; + .module z80fixedbank + + .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 _need_resched + .globl _nready + .globl _platform_idle + + .globl map_kernel + .globl map_process + .globl map_process_a + .globl map_process_always + + ; 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(). +; +; 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 + call map_process_always + ld hl, #U_DATA + ld de, #U_DATA_STASH + ld bc, #U_DATA__TOTALSIZE + ldir + call map_kernel + + ; 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 +; +; FIXME: do we actually *need* to restore the stack ! +; + push de ; restore stack + push bc ; restore stack + + call map_kernel + + 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 + push hl + push de + call _swapper + pop de + pop hl + di +.endif + ld a, (hl) +not_swapped: + 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 + ; Pages please ! + call map_process_a + + ; bear in mind that the stack will be switched now, so we can't use it + ; to carry values over this point + + exx ; thank goodness for exx 8) + ld hl, #U_DATA_STASH + ld de, #U_DATA + ld bc, #U_DATA__TOTALSIZE + ldir + exx + + ; 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) + call map_kernel + + ; 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) + 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) + + call bankfork ; do the bank to bank copy + + ; Copy done + + call map_process_always + + ; 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 + ldir + ; Return to the kernel mapping + call map_kernel + + ; 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 +; +; 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: +_need_resched: .db 0 diff --git a/Kernel/lib/z80fixedbank.s b/Kernel/lib/z80fixedbank.s index 6a669efd..7f46c4fa 100644 --- a/Kernel/lib/z80fixedbank.s +++ b/Kernel/lib/z80fixedbank.s @@ -1,292 +1,5 @@ -; -; For a purely bank based architecture this code is common and can be -; used by most platforms -; -; The caller needs to provide the standard map routines along with -; map_kernel_a which maps in the kernel bank in a. This code assumes -; that the bank can be encoded in 8bits. -; - .module z80fixedbank - - .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 _need_resched - .globl _nready - .globl _platform_idle - - .globl map_kernel - .globl map_process - .globl map_process_a - .globl map_process_always - - ; 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(). -; -; 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 - call map_process_always - ld hl, #U_DATA - ld de, #U_DATA_STASH - ld bc, #U_DATA__TOTALSIZE - ldir - call map_kernel - - ; 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 -; -; FIXME: do we actually *need* to restore the stack ! -; - push de ; restore stack - push bc ; restore stack - - call map_kernel - - 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 - push hl - push de - call _swapper - pop de - pop hl - di -.endif - ld a, (hl) -not_swapped: - 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 - ; Pages please ! - call map_process_a - - ; bear in mind that the stack will be switched now, so we can't use it - ; to carry values over this point - - exx ; thank goodness for exx 8) - ld hl, #U_DATA_STASH - ld de, #U_DATA - ld bc, #U_DATA__TOTALSIZE - ldir - exx - - ; 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) - call map_kernel - - ; 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) - 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) - - call bankfork ; do the bank to bank copy - - ; Copy done - - call map_process_always - - ; 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 - ldir - ; Return to the kernel mapping - call map_kernel - - ; 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 + .include "../lib/z80fixedbank-core.s" ; ; 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