65c816: this mornings fiddling
authorAlan Cox <alan@linux.intel.com>
Thu, 21 Sep 2017 21:17:50 +0000 (22:17 +0100)
committerAlan Cox <alan@linux.intel.com>
Thu, 21 Sep 2017 21:17:50 +0000 (22:17 +0100)
Kernel/lib/65c816.s [new file with mode: 0644]
Kernel/lowlevel-65c816.s
Kernel/usermem_std-65c816.s

diff --git a/Kernel/lib/65c816.s b/Kernel/lib/65c816.s
new file mode 100644 (file)
index 0000000..dd734c6
--- /dev/null
@@ -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
+       ldx     #>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
+
index 54a8c27..044d316 100644 (file)
@@ -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
 ;      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                ; FIXME
        pha
-       lda #>sigret            ; needs to be a fixed address in user
-       pha
-       lda #<sigret            ; FIXME
-       pha
-       phy
-       asl a
+       phx                             ; signal number
+       phy                             ; temporarily save C sp value
+       txa
+       asl a                           ; vector offset
        tay
-       rep #$10
+       rep     #$10
        .i16
-       ldx U_DATA__U_SIGVEC,y
-       clz U_DATA__U_SIGVEC,y
+       ldx     U_DATA__U_SIGVEC,y      ; get signal vector
+       clz     U_DATA__U_SIGVEC,y      ; and reset it
+       ply                             ; get the temporary sp save back
        phx
-       lda U_DATA__U_PAGE
+       ; Needs to change for split I/D
+       lda     U_DATA__U_PAGE          ; target code page
        pha
-       ldx #PROGLOAD+20
-       phx
-       sep #$10
+       pea     #PROGLOAD+20            ; trap handler in user app
+       pha
+       plb                             ; get the right user data bank
+       xba                             ; get into ff00 format
+       lda     #0
+       tcd                             ; set user DP correctly
+       sty     sp                      ; and finally restore the user C sp
+       sep     #$10                    ; i8a8 on entry to handler
        .i8
-       rtl     ;       return into user app
-
-;
-;      On 6502 the platform code is responsible for invoking the
-;      signal dispatch (as it may have to be in the stub in the
-;      process space if we have no common)
-;
-
-
+       rtl                     ;       return into user app handler
 ;
-;      doexec is a special case syscall exit path. As we may have no
-;      common we have to hand the last bits off to the platform code
-;      x,a holds the target address. This routine is in common and is the
-;      one case we can and do want to have fastcall.
+;      doexec is a special case syscall exit path. Set up the bank
+;      registers and return directly to the start of the user process
 ;
 _doexec:
-       ; FIXME set up U_DATA__U_PAGE+2 here by lookup
+       ; FIXME how do we find the correct cpu stack for this process
+       ;
+       ; Would (bank + offset) << 8 work ?
+       sta     _tmp1
+       stx     _tmp1+1
        sei
-       stz _kernel_flag
-       ; where to save address ... ? (or is it always bank:0 anyway ?)
-       rep #$30
+       stz     _kernel_flag
+       rep     #$30
        .i16
        .a16
-       lda U_DATA__U_PAGE+2    ;       CPU stack
+       ldx     _tmp1           ;       target address
+       sep     #$20
+       .a8
+       lda     U_DATA__U_PAGE  ;       get our bank
+       clc
+       adc     #STACK_BANKOFF
+       xba                     ;       swap to xx00
+       lda     #FF             ;       stack top is xxff
        tcs                     ;       Set CPU stack at xxFF
-       ina                     ;       Zp follows
-       xba                     ;       now in the form 00xx as we need
-       pha
-       pld
+       ; Split I/D will need to change this logic
+       lda     U_DATA__U_PAGE  ;       our bank
+       xba                     ;       now in the form xx00 as we need
+       tcd                     ;       set the user DP
        ; We are now on the correct DP and CPU stack
-       ldy U_DATA__U_ISP
-       sty sp                  ;       sp is in DP so we write user version
-       sep #$10
+       ldy     U_DATA__U_ISP
+       sty     sp              ;       sp is in DP so we write user version
+       sep     #$30
        .i8
        .a8
-       lda U_DATA__U_PAGE      ;       bank
-       pha
-       lda #0                  ;       should be passed address but will
-       pha                     ;       I think always be zero !
-       pha
+       ; Will need to change for split I/D
+       lda     U_DATA__U_PAGE  ;       bank
+       pha                     ;       switch to userdata bank
+       plb
+       pha                     ;       stack bank for rtl
+       rep     #$10
+       .i16
+       phx                     ;       Execution address within bank
        cli
-       tax                     ;       bank is 0
-       tay                     ;       ZP base is 0
+       lda     #0              ;       
+       tay                     ;       DP base is 0
+       sep     #$10
+       .i8
        rtl
        
 ;
@@ -193,105 +298,178 @@ _doexec:
 ;      zero page magic because its not re-entrant.
 ;
 interrupt_handler:
-       rep #$30
+       ; Make sure we save all of the register bits
+       rep     #$30
+       .a16
+       .i16
        pha
        phx
        phy
-       sep #$30                        ; 8i8a
-       cld
+       cld                     ; no funnies with decimal
 
-       ; FIXME: rewrite this in 16i mode
-       ; Save our ZP in case we are in kernel mode and using it
-       ; (we could saacrifice 256 bytes to IRQ handling instead which
-       ; might be smarter FIXME)
-       jsr stash_zp                    ; side effect saves sp
+       ; Now switch our data and DP to the kernel ones
+       lda     #KERNEL_BANK
+       pha
+       plb
+       lda     #IRQ_DP         ; interrupt has a private direct page
+       tcd
 
-       .i16
-       rep #$10
+       ; Switch stacks
        tsx
-       stx istack_switched_sp
-       ldx istack
+       stx     istack_switched_sp
+       ldx     #istack
        txs
-       ldx #istack
-       stx sp                          ; C stack is now right
+       ldx     #istack_c       ; set up C istack (may not need eventually)
+       stx     sp              
 
-       sep #$10
+       sep     #$30
 
+       .a8
        .i8
 
-       lda #1
-       sta _inint
-       jsr _platform_interrupt_i       ; call via C int wrapper
-       stz _inint
-       jsr map_process_always          ; may have switched task
-       jmp int_switch
-int_switch:
-       stz _inint
+       lda     #1
+       sta     _inint
+       jsr     _platform_interrupt_i   ; call via C int wrapper
+       stz     _inint
 
        ; Restore the stack we arrived on
 
+       rep     #$10
        .i16
-       rep #$10
-       ldx istack_sitched_sp
+       ldx     istack_switched_sp
        txs
-       sep #$10
-       .i8
-       jsr_stash_zp
        ; TODO .. pre-emption
 
+
        ; Signal return path
        ; The basic idea here is that if a signal is pending we
        ; build a new stack frame under the real one and rti to that. The
        ; hook code in low user memory will then clean up the real frame
-       lda U_DATA__U_CURSIG
-       clz U_DATA__U_CURSIG
-       bne signal_exit
-       rep #$30
+       lda     U_DATA__U_CURSIG
+       bne     signal_exit
+
+       ;
+       ;       Switch banks correctly and return to user. We can't just
+       ;       save the bank as a task switch or swap might move the
+       ;       process
+       ;
+       lda     U_DATA_U_PAGE
+       sep     #$30
+
+       .a8
+       .i8
+
+       pha
+       plb     ; Now data is the user bank
+       xba
+       lda     #0
+       tcd     ; and DP is right
+
+       rep     #$30
+       .a16
+       .i16
+
+       ldx     istack_switched_sp
+       txs
        ply
        plx
        pla
        rti
 
+;
+;      If we hit this we are on the user stack with all kernel mappings
+;      and in a8/i16
+;
+       .a8
+       .i16
 signal_exit:
-       tay             ; save signal 8bits
+       clz     U_DATA__U_CURSIG
+       sta     _tmp1           ; save signal 8bits (irq tmp1)
 
 
        ; Move down the stack frame
-       ; FIXME: we need to mangle the frame to take out the page
-       ; so we can match syscall (or modify syscall!)
+       ;
+       ; Right now it looks like this
+       ;
+       ;               Bank
+       ;               PC high
+       ;               PC low
+       ;               P
+       ;               A16
+       ;               X16
+       ;               Y16
+       ;       SP
+       ;
+       ; We want it to look like
+       ;               PC high
+       ;               PC low
+       ;               P
+       ;               A16
+       ;               X16
+       ;               Y16
+       ;               error
+       ;               retval
+       ;               sigret_irq
+       ;               signal number
+       ;
+       rep #$30
        .i16
-       rep #$10
-       tsx
-       dex                     ; 7 bites FIXME check
-       dex
-       dex
-       dex
-       dex
-       dex
-       dex
-       txs
-       ldx #irqout
-       phx                     ; return vector
-       sep #$10
+       .a16
+
+       tsc                     ; stack to accumulator
+       clc
+       adc     #10             ; top of block to change
+       tax
+       tay
+       dex                     ; source one below dest
+       lda     #8              ; copy 9 bytes
+       mvp     0,0
+       ;
+       ;       At this point y points to the byte below the last
+       ;       destination copied (aka s)
+       ;
+       tya
+       tcs     ; stick y in s
+
+       sep     #$20
+       .a8
+       ;
+       ;       Stack the signal bits
+       ;
+       lda     U_DATA__U_ERROR
+       pha
+       lda     U_DATA__U_RETVAL
+       pha
+       lda     U_DATA__U_RETVAL+1
+       pha
+       pea     #sigret_irq
+                               ; sig   nal return
+       sep     #$10
        .i8
        phy                     ; signal code
        tya
        asl a
        tay
-       rep #$30
+       rep     #$30
        .i16
        .a16
-       lda U_DATA__U_SIGVEC,y
-       clz U_DATA__U_SIGVEC,y
+       lda     U_DATA__U_SIGVEC,y
+       clz     U_DATA__U_SIGVEC,y
        pha
-       lda #PROGLOAD+20
+       sep     #$20
+       .a8
+       lda     U_DATA__U_PAGE
        pha
-       sep #$30
+       rep     #$30
+       .a16 
+       .i16
+       ldx     #0
+       txy
+       pea     #PROGLOAD+20
+       sep     #$30
        .i8
        .a8
-       lda U_DATA__U_PAGE
-       pha 
-       lda #$30                ; i8a8
+       lda     #$30            ; i8a8
        pha
        rti
 ;
@@ -299,9 +477,16 @@ signal_exit:
 ;      mapping model. We don't change anything but instead track the page
 ;      we need to use for the'far' operations.
 ;
+;      We don't actually make any use of this code in the base 65c816
+;      image but it is there and needed for drivers that do swapping.
+;
+;      As we can't map the bank into kernel memory we'll actually need
+;      some custom swap logic in the disk drivers which is sucky but I
+;      don't currently have any better idea!
+;
 map_process_always:
        lda U_DATA__U_PAGE
-       sta userpage
+       sta _userpage
        rts
 map_process:
        cmp #0
@@ -314,31 +499,13 @@ map_process_2:
        sta ptr1
        stx ptr1+1
        lda (ptr1)      ; 4 bytes if needed
-       sta userpage
+       sta _userpage
        rts
-userpage:
+_userpage:
        .byte 0
 
-;
-;      The following is taken from the debugger example as referenced in
-;      the compiler documentation. We swap a stashed ZP in our commondata
-;      with an IRQ handler one. The commondata is per process and we depend
-;      upon this to make it all work
-;
-; Swap the C temporaries
-;
-stash_zp:
-        ldy     #zpsavespace-1
-Swap1:  ldx     CTemp,y
-        lda     <sp,y
-        sta     CTemp,y
-        txa
-        sta     sp,y
-        dey
-        bpl     Swap1
-        rts
-
 nmi_handler:
+       sep #$30
        ldx #>nmi_trap
        lda #<nmi_trap
        jsr outstring
index cb1fb96..00914e1 100644 (file)
        .include "platform/kernel.def"
-        .include "kernel02.def"
+        .include "kernel816.def"
        .include "platform/zeropage.inc"
 
-               .export __uget, __ugetc, __ugetw, __ugets
-               .export __uput, __uputc, __uputw, __uzero
+       .export __uget, __ugetc, __ugetw, __ugets
+       .export __uput, __uputc, __uputw, __uzero
 
-               .import map_kernel, map_process_always
-               .import outxa, popax
-               .importzp ptr2, tmp2
+       .import outxa, popax
+       .importzp ptr2, tmp2
 ;
-;      65c816 specific usermem access functions. These should use and know we
-;      are using the processor bank register but are not yet optimized for
-;      this.
+;      ptr1 and tmp1 are reserved for map_* functions in 6502 but
+;      are actually free here. We keep the convention however in case
+;      of future changes
 ;
-;      ptr1 and tmp1 are reserved for map_* functions
+       .segment "COMMONMEM"
+
+; user, dst, count(count in ax)
 ;
+;      Compiler glue is not pretty - might be worth having some optimized
+;      16bit aware stack handlers
 ;
-               .segment "COMMONMEM"
+__uget:        sta     tmp2
+       stx     tmp2+1                  ; save the count
+       jsr     popax                   ; pop the destination
+       sta     ptr2                    ; (ptr2) is our target
+       stx     ptr2+1
+       jsr     popax                   ; (ptr2) is our source
+       sta     ptr3
+       stx     ptr3+1
+       lda     U_DATA__U_PAGE
+       sta     ugetpatch+1
+       phb
+       .i16
+       .a16
+       rep     #$30
+       ldx     ptr3                    ; source
+       ldy     ptr2                    ; destination
+       lda     tmp2
+       beq     ug_nomov                ; 0 means 64K!
+       dec                             ; need 1 less than size
+ugetpatch:
+       mvn     0,KERNEL_BANK:0
+ug_nomov:
+       .i8
+       .a8
+       sep     #$30
+       plb
+       lda     #0
+       tax
+       rts
 
-; user, dst, count(count in ax)
 ;
-;      Decidedly unoptimised (even the 6502 could manage a word a switch)
+;      This could be done more nicely using .i16 and wants optimizing
 ;
-__uget:                sta tmp2
-               stx tmp2+1              ; save the count
-               jsr popax               ; pop the destination
-               sta ptr2                ; (ptr2) is our target
-               stx ptr2+1
-               jsr popax               ; (ptr2) is our source
-               sta ptr3
-               stx ptr3+1
-
-               ldy #0                  ; counter
-
-               ldx tmp2+1              ; how many 256 byte blocks
-               beq __uget_tail         ; if none skip to the tail
-
-__uget_blk:
-               jsr map_process_always  ; map the user process in
-               lda (ptr3), y           ; get a byte of user data
-               jsr map_kernel          ; map the kernel back in
-               sta (ptr2), y           ; save it to the kernel buffer
-               iny                     ; move on one
-               bne __uget_blk          ; not finished a block ?
-               inc ptr2+1              ; move src ptr 256 bytes on
-               inc ptr3+1              ; move dst ptr the same
-               dex                     ; one less block to do
-               bne __uget_blk          ; out of blocks ?
-
-__uget_tail:   cpy tmp2                ; finished ?
-               beq __uget_done
-
-               jsr map_process_always  ; map the user process
-               lda (ptr3),y            ; get a byte of user data
-               jsr map_kernel          ; map the kernel back in
-               sta (ptr2),y            ; save it to the kernel buffer
-               iny                     ; move on
-               bne __uget_tail         ; always taken (y will be non zero)
-
-__uget_done:
-               lda #0
-               tax
-               rts
-
-__ugets:       sta tmp2
-               stx tmp2+1              ; save the count
-               jsr popax               ; pop the destination
-               sta ptr2                ; (ptr2) is our target
-               stx ptr2+1
-               jsr popax               ; (ptr2) is our source
-               sta ptr3
-               stx ptr3+1
-
-               ldy #0                  ; counter
-
-               ldx tmp2+1              ; how many 256 byte blocks
-               beq __uget_tail         ; if none skip to the tail
+__ugets:
+       sta     tmp2
+       stx     tmp2+1                  ; save the count
+       jsr     popax                   ; pop the destination
+       sta     ptr2                    ; (ptr2) is our target
+       stx     ptr2+1
+       jsr     popax                   ; (ptr2) is our source
+       sta     ptr3
+       stx     ptr3+1
+
+       ldy     #0                      ; counter
+
+       ldx     tmp2+1                  ; how many 256 byte blocks
+       beq     __uget_tail             ; if none skip to the tail
 
 __ugets_blk:
-               jsr map_process_always  ; map the user process in
-               lda (ptr3), y           ; get a byte of user data
-               beq __ugets_end
-               jsr map_kernel          ; map the kernel back in
-               sta (ptr2), y           ; save it to the kernel buffer
-               iny                     ; move on one
-               bne __ugets_blk         ; not finished a block ?
-               inc ptr3+1              ; move src ptr 256 bytes on
-               inc ptr2+1              ; move dst ptr the same
-               dex                     ; one less block to do
-               bne __ugets_blk         ; out of blocks ?
-
-__ugets_tail:  cpy tmp2                ; finished ?
-               beq __ugets_bad
-
-               jsr map_process_always  ; map the user process
-               lda (ptr3),y            ; get a byte of user data
-               beq __ugets_end
-               jsr map_kernel          ; map the kernel back in
-               sta (ptr2),y            ; save it to the kernel buffer
-               iny                     ; move on
-               bne __ugets_tail        ; always taken (y will be non zero)
+       phb
+       lda     U_DATA__U_PAGE          ; switch to user bank read
+       pha
+       plb
+       lda     (ptr3), y               ; get a byte of user data
+       beq     __ugets_end
+       plb                             ; back to kernel
+       sta     (ptr2), y               ; save it to the kernel buffer
+       iny                             ; move on one
+       bne     __ugets_blk             ; not finished a block ?
+       inc     ptr3+1                  ; move src ptr 256 bytes on
+       inc     ptr2+1                  ; move dst ptr the same
+       dex                             ; one less block to do
+       bne     __ugets_blk             ; out of blocks ?
+
+__ugets_tail:
+       cpy     tmp2                    ; finished ?
+       beq     __ugets_bad
+
+       phb
+       lda     U_DATA__U_PAGE
+       pha
+       plb
+       lda     (ptr3),y                ; get a byte of user data
+       beq     __ugets_end
+       plb
+       sta     (ptr2),y                ; save it to the kernel buffer
+       iny                             ; move on
+       bne     __ugets_tail            ; always taken (y will be non zero)
 
 __ugets_bad:
-               dey
-               lda #0
-               sta (ptr2), y           ; terminate kernel buffer
-               lda #$FF                ; string too large
-               tax                     ; return $FFFF
-               rts
+       dey
+       lda     #0
+       sta     (ptr2), y               ; terminate kernel buffer
+       lda     #$FF                    ; string too large
+       tax                             ; return $FFFF
+       rts
 
 __ugets_end:
-               jsr map_kernel
-               lda #0
-               sta (ptr2), y
-               tax
-               rts
-
-__ugetc:       sta ptr2
-               stx ptr2+1
-__uget_ptr2:
-               jsr map_process_always
-               ldy #0
-               lda (ptr2),y
-               jmp map_kernel
-
-__ugetw:       sta ptr2
-               stx ptr2+1
-               jsr map_process_always
-               ldy #1
-               lda (ptr2),y
-               tax
-               dey
-               lda (ptr2),y
-               jmp map_kernel
-
-
-__uput:                sta tmp2
-               stx tmp2+1
-               jsr popax       ; dest
-               sta ptr2
-               stx ptr2+1
-               jsr popax       ; source
-               sta ptr3
-               stx ptr3+1
-
-               ldy #0
-
-               ldx tmp2+1
-               beq __uput_tail
-__uput_blk:
-               jsr map_kernel
-               lda (ptr3), y
-               jsr map_process_always
-               sta (ptr2), y
-               iny
-               bne __uput_blk
-               inc ptr2+1
-               inc ptr3+1
-               dex
-               bne __uput_blk
-
-__uput_tail:   cpy tmp2
-               beq __uput_done
-               jsr map_kernel
-               lda (ptr3),y
-               jsr map_process_always
-               sta (ptr2),y
-               iny
-               bne __uput_tail
-
-__uput_done:
-               jsr map_kernel
-               lda #0
-               tax
-               rts
-
-__uputc:       sta ptr2
-               stx ptr2+1
-               jsr map_process_always
-               jsr popax
-               ldy #0
-               sta (ptr2),y
-               jmp map_kernel
-
-__uputw:       sta ptr2
-               stx ptr2+1
-               jsr map_process_always
-               jsr popax
-               ldy #0
-               sta (ptr2),y
-               txa
-               iny
-               sta (ptr2),y
-               jmp map_kernel
-
-__uzero:       sta tmp2
-               stx tmp2+1
-               jsr map_process_always
-               jsr popax               ; ax is now the usermode address
-               sta ptr2
-               stx ptr2+1
-
-               ldy #0
-               tya
-
-               ldx tmp2+1              ; more than 256 bytes
-               beq __uzero_tail        ; no - just do dribbles
-__uzero_blk:
-               sta (ptr2),y
-               iny
-               bne __uzero_blk
-               inc ptr2+1              ; next 256 bytes
-               dex                     ; are we done with whole blocks ?
-               bne __uzero_blk
-
-__uzero_tail:
-               cpy tmp2
-               beq __uzero_done
-               sta (ptr2),y
-               iny
-               bne __uzero_tail
-__uzero_done:  jmp map_kernel
-
-
+       plb
+       lda     #0
+       sta     (ptr2), y
+       tax
+       rts
+
+__ugetc:
+       sta     ptr2
+       stx     ptr2+1
+       phb
+       lda     U_DATA__U_PAGE
+       pha
+       plb
+       lda     (ptr2)
+       plb
+       rts
+
+__ugetw:
+       sta     ptr2
+       stx     ptr2+1
+       phb
+       lda     U_DATA__U_PAGE
+       pha
+       plb
+       ldy     #1
+       lda     (ptr2),y
+       tax
+       lda     (ptr2)
+       plb
+       rts
+
+
+__uput:
+       sta     tmp2
+       stx     tmp2+1
+       jsr     popax                   ; dest
+       sta     ptr2
+       stx     ptr2+1
+       jsr     popax                   ; source
+       sta     ptr3
+       stx     ptr3+1
+       lda     U_DATA__U_PAGE
+       sta     uputpatch+2
+       phb
+       .i16
+       .a16
+       rep     #$30
+       ldx     ptr3                    ; source
+       ldy     ptr2                    ; destination
+       lda     tmp2
+       beq     up_nomov                ; 0 means 64K!
+       dec                             ; need 1 less than size
+uputpatch:
+       mvn     KERNEL_BANK:0,0
+up_nomov:
+       .i8
+       .a8
+       sep     #$30
+       plb
+       lda     #0
+       tax
+       rts
+
+__uputc:
+       sta     ptr2
+       stx     ptr2+1
+       jsr     popax
+       phb
+       lda     U_DATA__U_PAGE
+       pha
+       plb
+       sta     (ptr2)
+       plb
+       rts
+
+__uputw:
+       sta     ptr2
+       stx     ptr2+1
+       jsr     popax
+       phb
+       lda     U_DATA__U_PAGE
+       pha
+       plb
+       sta     (ptr2)
+       txa
+       ldy     #1
+       sta     (ptr2),y
+       plb
+       rts
+
+__uzero:
+       sta     tmp2
+       stx     tmp2+1
+       jsr     popax                   ; ax is now the usermode address
+       sta     ptr2
+       stx     ptr2+1
+
+       lda     U_DATA__U_PAGE
+       sta     uzero_patch+1
+       sta     uzero_patch+2
+
+       ; Clear lead byte in user space
+       phb
+       pha
+       plb
+       clz     (ptr2)
+       plb
+
+       phb
+       .i16
+       .a16
+       rep     #$30
+       ldx     ptr2                    ; copy from x to x+1 moving the 0 up
+       ldy     ptr2
+       iny
+       lda     tmp2                    ; no bytes means we are done
+       beq     nozero
+       dec                             ; 1 byte means our clz did it
+       beq     nozero
+       ; Use mvn to wipe the range required
+       ; The set up is worth it as most uzero() calls are big
+       ; ranges
+uzero_patch:
+       mvn     0,0
+nozero:
+       plb
+       sep     #$30
+       .i8
+       .a8
+       lda     #0
+       tax
+       rts