From ebb10b9888936f5618acae84459ca00a2eb7803c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 21 Sep 2017 22:17:50 +0100 Subject: [PATCH] 65c816: this mornings fiddling --- Kernel/lib/65c816.s | 223 +++++++++++++++ Kernel/lowlevel-65c816.s | 533 +++++++++++++++++++++++------------- Kernel/usermem_std-65c816.s | 432 +++++++++++++++-------------- 3 files changed, 797 insertions(+), 391 deletions(-) create mode 100644 Kernel/lib/65c816.s diff --git a/Kernel/lib/65c816.s b/Kernel/lib/65c816.s new file mode 100644 index 00000000..dd734c6e --- /dev/null +++ b/Kernel/lib/65c816.s @@ -0,0 +1,223 @@ +; +; The 65C816 memory management and switching logic is common +; to all systems so keep it all here +; + .include "kernel.def" + .include "../kernel816.def" + .include "zeropage.inc" + + .export _switchout + .export _switchin + .export _dofork + + .importzp ptr1 + .import jump_monitor + .import _chksigs + .import _platform_idle + .import _nready + .import _inint + .import _getproc + .import _runticks + .import outcharhex + .import outstring + + .segment "COMMONMEM" + .p816 + .i8 + .a8 + +_switchout: + sei + jsr _chksigs + rep #$10 ; Index to 16bit + .i16 + ldx #0 + phx + ldx sp + phx + tsx + stx U_DATA__U_SP + sep #$10 ; Back to 8 for C code + + .i8 + lda _nready + bne slow_path +idling: + cli + jsr _platform_idle + sei + lda _nready + beq idling + cmp #1 + bne slow_path + + rep #$10 + .i16 + ldx U_DATA__U_PTAB + lda 0,x + cmp #P_READY + bne slow_path + lda #P_RUNNING + sta 0,x + plx + stx sp + plx ; discard 0 + sep #$30 + ; Get back into the way C expects us + .i8 + .a8 + cli + rts +slow_path: + ; + ; Switch of task - save our udata and stack. Note we are + ; saving the stack we are executing upon ! + ; + sep #$30 + .i8 + .a8 + lda U_DATA__U_PAGE + sta switch_patch_1+2 ; target bank of save + rep #$30 + .i16 + .a16 + ldx #U_DATA + ldy #U_DATA_STASH + lda #U_DATA__TOTALSIZE-1 ; including our live stack + phb +switch_patch_1: + mv KERNEL_BANK:0,0 ; save stack and udata + plb + sep #$30 + clz _inint + jsr _getproc ; x,a holds process + jsr _switchin ; switch to process + jsr _trap_monitor ; bug out if it fails + +; +; FIXME: add swap support +; +_switchin: + sei + sta ptr1 + stx ptr1+1 + + sep #$30 + .i8 + .a8 + ldy #P_TAB__P_PAGE_OFFSET + lda (ptr1),y ; bank of target + + ; If this is zero we need swapping so the swapper checks go here + ; FIXME + + sta switch_patch_2+1 ; source bank of retrieve + rep #$30 + .i16 + .a16 + + ; Set our stack pointer. We must not use it until the mvn + ; is completed + ldx U_DATA__U_SP ; correct stack pointer + txs + ldx #U_DATA_STASH + ldx #U_DATA + lda #U_DATA__TOTALSIZE-1 +switch_patch_2: + ; FIXME check syntax required for bank value ?? + mvn 0,KERNEL_BANK:0 + ; after the MVN our data bank is KERNEL_DATA + ; Our stack is now valid and we may use it again, our UDATA + ; is for the new process + ldx U_DATA__U_PTAB + cpx ptr1 + bne switchinfail ; wrong process !! + clz _runticks + sep #$20 + .a8 + lda #P_RUNNING + sta P_TAB__P_STATUS_OFFSET,x + ; This will only be needed once we swap, and we will need to + ; do a few other fixups too + lda P_TAB__P_PAGE_OFFSET,x + sta U_DATA__U_PAGE,x + plx ; stacked kernel space C sp + stx sp + sep #$10 + .i8 + lda U_DATA__U_INTERRUPT + beq notisr + cli ; interrupts back on +notisr: + plx ; return code + pla + rts +switchinfail: + lda ptr1+1 + jsr outcharhex + lda ptr1 + jsr outcharhex + lda #badswitchmsg + jsr outstring + ; something went wrong and we didn't switch in what we asked for + jmp _ trap_monitor +badswitchmsg: .byte "_switchin: FAIL" + .byte 13, 10, 0 + +_dofork: + sta ptr1 ; new process ptr. U_DATA gives parent + stx ptr1+1 + lda U_DATA__U_PAGE + sta fork_patch+2 ; source bank (parent) + ldy #P_TAB__P_PAGE + lda (ptr1),y + sta fork_patch+1 ; destination bank (child) + sta fork_patch_2+1 + + + rep #$20 + .a16 + + ldy #P_TAB__P_PID_OFFSET ; Stack pid and sp + lda (ptr1),y + pha + rep #$10 + .i16 + ldx sp + phx + tsx + stx U_DATA__U_SP + + ; Our context is now a valid child stack frame so we can save stuff + ldx #0 + txy + lda #MAP_SIZE ; 64K - udata shadow + phb +fork_patch: + mvn 0,0 ; copy the entire bank + ldx #U_DATA + ldy #U_DATA_STASH + lda #U_DATA_TOTALSIZE-1 +fork_patch_2: + mvn KERNEL_BANK:0,0 + plb ; back to kernel bank + + ; At this point we have copied the parent into the child bank + ; and copied the current uarea into the child uarea + plx ; discard frame we build for child + plx + + .sep #$30 ; back to 8bit mode for C + .a8 + .i8 + lda ptr1 + ldx ptr1+1 + jsr _newproc + ; We are now being the child properly + lda #0 + sta _runticks + sta _runticks+1 + tax ; return 0 + rts + diff --git a/Kernel/lowlevel-65c816.s b/Kernel/lowlevel-65c816.s index 54a8c27e..044d316c 100644 --- a/Kernel/lowlevel-65c816.s +++ b/Kernel/lowlevel-65c816.s @@ -14,7 +14,8 @@ .export outnewline .export outcharhex .export outxa - .export stash_zp + + .export _userpage .export _need_resched @@ -28,7 +29,6 @@ .import _platform_interrupt_i .import platform_doexec .import _inint - .import CTemp .import _trap_monitor .include "platform/zeropage.inc" @@ -39,9 +39,7 @@ ; ; Unlike Z80 we need to deal with systems that have no overlapping ; memory banks. We pass the arguments is a single pointer therefore -; we expect the platform code to have copied the syscall arguments into -; udata then called us it also saves any registers etc for us (as it will -; need them too) +; we have to copy them interbank ; ; Called with interrupts off, on the kernel stack ; On completion U_DATA__U_ERROR an U_DATA__U_RETVAL hold the returns @@ -51,141 +49,248 @@ ; FIXME: do we want 6502 binaries syscall or to implement a cleaner ; 65c816 brk based syscall ? ; +; +; Assumptions: +; No split I/D : code = data = direct page +; This could be fixed by making page track two +; values and keep them in ->page. +; +; +; TODO: +; - add vectors and stubs to bank 0 setup, and to other banks for +; stubs (syscall and signal return) +; - audit and begin testing +; +; +; This is called from a stub we put into each user process bank that +; does a jsl to the kernel entry point then an rts +; syscall_entry: - php - sei - cld - sep #$30 + php ; save cpu mode of caller on ustack + sei ; interrupts off + cld ; get into sane mode - no decimal, known state + sep $30 + .a8 + .i8 + stx U_DATA__U_CALLNO + cpy #0 + beq noargs + + lda sp + sta ptr1 + ldx sp+1 + stx ptr1+1 + jsr incaxy + sta sp + stx sp+1 + + ; + ; We copy the arguments but need to deal with the compiler + ; stacking in the reverse order. At this point ptr1 points + ; to the last byte of the arguments (first argument). We go + ; down the stack copying words up the argument list. + ; + ; Would it be saner to invert the struct fields on LTR + ; stacking platforms and use mvn ???? FIXME + ; + ldx #0 +copy_args: + rep #$20 + .a16 + dey + dey + lda (ptr1),y ; copy the arguments over + ld + sta KERNEL_BANK:U_DATA__U_ARGN,x + inx + inx + cpy #0 + bne copy_args + +noargs: + rep #$30 + .a16 .i16 - lda #KERNEL_BANK + lda #KERNEL_BANK ; our direct and base bank need setting pha plb - rep #$30 - stx U_DATA__U_CALLNO - cpy #0 - beq noargs - - FIXME: inter bank move the arguments - -noargs: - rep #$10 + lda #KERNEL_DP + tcd + rep #$30 + .a8 + .i8 + rep #$10 .i16 - ldx sp - stx U_DATA__U_SYSCALL_SP + ldx sp ; save the C stack + pshx ; on the main stack + ; FIXME: how to check stack space in brk when this is not visible ? tsx - stx U_DATA__U_PAGE+2 ; ewww.. FIXME - ; FIXME: kstack actually depends on process number so needs to be - ; a look up - ldx #kstack_top - stx sp - cli - - sep #$10 + stx U_DATA__U_SYSCALL_SP + ldx #kstack_top + txs ; set our CPU stack + ldx #kstack_c ; set our C stack + stx sp ; + cli ; sane state achieved + + sep #$10 .i8 - lda #1 - sta _kernel_flag ; In kernel mode - cli ; Interrupts now ok - jsr _unix_syscall_i ; Enter C space via the __interrupt wrapper - sei ; Interrupts back off - stz _kernel_flag - rep #$10 + lda #1 + sta _kernel_flag ; In kernel mode + cli ; Interrupts now ok + jsr _unix_syscall_i ; Enter C space via the __interrupt wrapper + sei ; Interrupts back off + stz _kernel_flag + rep #$10 .i16 - ldx U_DATA__U_PAGE+2 - txs - ldx U_DATA__U_SYSCALL_SP - stx sp + ldx U_DATA__U_SYSCALL_SP + txs ; Back to the old stack + ; We can't restore sp yet as we are + ; in the wrong bank .i8 - sep #$10 - lda U_DATA__U_CURSIG - bne signal_out - plp + sep #$10 + lda U_DATA__U_CURSIG + bne signal_out + sep #$20 + lda U_DATA__U_PAGE + .a16 + ; + ; We use this trick several times. U_PAGE holds the bank + ; register. As we want our DP to be bank:0000 we need to + ; shift left 8 and add 00 to get our DP value + ; + pha + plb ; User mapping for data bits + xba ; from xx to xx00 for DP + lda #0 + tcd + + pla ; pull the saved sp off the ustack + sta sp ; and store it 16bit + + rep #$20 + .a8 + plp ; off ustack ; We may now be in decimal ! - ldy U_DATA__U_RETVAL - ldx U_DATA__U_RETVAL+1 + ldy U_DATA__U_RETVAL + ldx U_DATA__U_RETVAL+1 ; also sets z for us - lda U_DATA__U_ERROR - rts + lda U_DATA__U_ERROR + rtl + + ; + ; The goal of the signal handler is to push a frame onto + ; the stack that causes us to return from the kernel to user + ; space PROGLOAD+0x20 (0x220) with a stack frame holding + ; the desired vector and a return path to a cleanup routine + ; to recover stuff + ; + ; The tricky bit here is keeping the user C sp somewhere safe + ; we pop it into y and carefully juggle it around until we can + ; write into the correct DP + ; + ; The sigret code is stuffed into each user bank, and it + ; does the following + ; + ; sep #$30 + ; plx recover return and error codes + ; ply + ; pla + ; plp recover cpu state + ; rts in bank return to the interruption point + ; signal_out: - tay - clz U_DATA__U_CURSIG - rep #$10 + clz U_DATA__U_CURSIG + + rep #$10 .i16 + ply ; user stack pointer to go to sp tsx - dex ; move past existing frame - dex - dex - dex ; FIXME check is 4 bytes + dex ; move past existing frame P + dex ; return PC + dex ; return PC + ; lose the bank txs - setp #$10 + sep #$10 .i8 + tax ; Save signal code in X ; Stack the signal return (the signal itself can cause syscalls) - lda U_DATA__U_ERROR - pha - lda U_DATA__U_RETVAL + ; Be careful here stack ops are currently going to user stack but + ; data ops to kernel. Don't mix them up! + lda #>sigret ; needs to be a fixed address in user pha - lda U_DATA__U_RETVAL+1 + lda #sigret ; needs to be a fixed address in user - pha - lda #nmi_trap lda #