--- /dev/null
+;
+; 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
+
.export outnewline
.export outcharhex
.export outxa
- .export stash_zp
+
+ .export _userpage
.export _need_resched
.import _platform_interrupt_i
.import platform_doexec
.import _inint
- .import CTemp
.import _trap_monitor
.include "platform/zeropage.inc"
;
; 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
;
; 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
;
; 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
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
.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