From: Alan Cox Date: Sun, 2 Sep 2018 20:51:29 +0000 (+0100) Subject: r2000/r3000/r3000a: initial guess at tricks X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=4d9b2213c4ef3e7fef6603b61aec2bdce0a95898;p=FUZIX.git r2000/r3000/r3000a: initial guess at tricks --- diff --git a/Kernel/lib/rabbit2000.s b/Kernel/lib/rabbit2000.s new file mode 100644 index 00000000..0e39a35b --- /dev/null +++ b/Kernel/lib/rabbit2000.s @@ -0,0 +1,300 @@ +; +; The Rabbit 2000 memory management is sort of common.. at least +; providing your board follows the design rules. +; + .export _platform_switchout + .export _switchin + .export _dofork + + .import _platform_monitor + .import _chksigs + .import _platform_idle + .import _newproc + .import _nready + .import _inint + .import _getproc + .import _runticks + .import outcharhex + .import outstring + .import kstack_base + + .include "../kernel.def" + .include "kernel.def" + + .area _COMMONMEM + +; _platform__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. +; +; We do not currently save the alt regs. We need to review this if sdcc ever +; uses them +; +_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 + + ; 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 + + push de + ld hl, #P_TAB__P_PAGE_OFFSET + add hl, de ; process ptr + pop de + +.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: + ; Pages please ! + call map_process_a + +;; FIXME: switch common page here + + ; bear in mind that the stack will be switched now, so we can't use it + ; to carry values over this point + + 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 + + ; 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 + + ; + ; 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 + + ; Clone the udata into the new common + call clone_udata + + ; 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 + +; +; 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 +; +; Assumption - fits into a fixed number of whole 256 byte blocks +; +; FIXME: rewrite efficiently for R2000 and don't use ldir! +; +bankfork: + ld b, #(U_DATA_STASH - PROGBASE)/256 + ld hl, #PROGBASE ; base of memory to fork (vectors included) +bankfork_1: + push bc ; Save our counter and also child offset + push hl + call map_process_a + 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 + call map_process_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: +_need_resched: .db 0