 lib ../environment
 sttl Core Shuffling Routines
 pag
 name corshufl
 global cpybtu,cpybug,cpybu,cpyub
 global cpybts,cpystb,cpybsg,dupum
 global indata,updtio,otdata,sumem
 global gtuwrd,gtubyt,ptuwrd,ptubyt
 global segcop,clrpag

*
* All routines in this file deal with moving bytes
* of memory around in one fashion or another.  All
* of the routines are 'semi' machine dependent in
* that they assume a page segment size of 4K.  If
* a different scheme is used, most code here will
* need to be rewritten!
*

*
* cpybtu & cpyutb
*
* Copy bytes from system buffer to user space or
* vice versa.  These routines do basically two things.
* first of all, the users segment containing the
* correct address space needs mapped into the system
* XBUF segment space.  Second, it is determined if
* the data to be transfered will cross a segment
* boundary.  If so, the appropriate splitting is
* performed.  Ultimately the routines 'cpybts' or
* 'cpystb' will be called to do the actual transfer
* since the user data space will be mapped into the
* system address space.  These routines are entered
* with: x=buffer header, u=buffer offset, y=user address,
* and d=total byte count.  The count must not exceed
* 512 bytes total (max size of buffer).
*

* copy buffer to user

cpybtu pshs d,x,y,u save all passed params
cpybt2 clra set xfr direction flag
 pshs a save flag
 tfr y,d check for access into user block
 addd 1,s
 bcs memfault -- can't do it
 cmpd #$FE00
 bhi memfault
 bra cpybug go do transfer

* copy user to buffer

cpyutb pshs d,x,y,u save all passed params
cpyut2 lda #$ff set xfr direction flag
 pshs a save flag

* fall through to general copy routine

 pag

*
* General routine for transfer setup.
*

cpybug tfr y,d get user address
 lsra get into low end of byte
 lsra
 lsra
 lsra
 ldx #umem point to users mem map
 lda a,x get segment number
 cmpa BLKHOL
 bne 2f
memfault ldb #FALTS send Memory Fault interrupt
 ldx utask
 jsr xmtint
 leas 1,s clean up stack
 puls d,x,y,u,pc return to higher level caller
2 lbsr mapxbf map user to XBUF area
 tfr y,d get user address
 anda #$0f mask off seg number
 ora #XBUF<<4 make xbuffer address
 tfr d,y put back in y
 ora #$f0 make a negative number
 coma do 2's comp
 comb
 addd #1 to find bytes left in segment
 pshs y save y
 ldy 7,s get initial user address
 leay d,y set to new value (after xfr)
 sty 7,s put back on stack
 puls y
 tst 0,s+ test direction
 bne cpyub if !=0 then do user->buf

*
* Finish up buffer to user transfer
*

cpybu ldx 2,s reset x ptr
 cmpd 0,s cross segment boundary?
 bhi cpybu4 if higher, it does not
 pshs d save count
 ldd 8,s get buffer offset
 addd 0,s add in byte count
 std 8,s save as new offset
 ldd 0,s reset count
 lbsr cpybts do buffer to sys xfr
 ldd 2,s get total count
 subd 0,s++ calc remaining bytes
 std 0,s
 beq cpybu5 exit if all done
 ldy 4,s reset user address
 ldu 6,s reset buffer offset
 lbra cpybt2 go repeat
cpybu4 ldd 0,s reset actual count
 lbsr cpybts do buffer to system xfr
cpybu5 puls d,x,y,u,pc return
 spc 1,16
*
* Finish up user to buffer transfer
*

cpyub ldx 2,s reset buffer header
 cmpd 0,s cross segment boundary?
 bhi cpyub4 if higher, it does not
 pshs d save count
 ldd 8,s get buffer offset
 addd 0,s add in count
 std 8,s save as new offset
 ldd 0,s reset count
 lbsr cpystb copy system to buffer
 ldd 2,s calc remaining bytes
 subd 0,s++
 std 0,s save result
 beq cpyub5 exit if all done
 ldy 4,s get new user address
 ldu 6,s reset buffer offset
 lbra cpyut2 repeat
cpyub4 ldd 0,s reset actual count
 lbsr cpystb copy system to buffer
cpyub5 puls d,x,y,u,pc return

*
* cpybts
*
* Copy data from a system buffer into the system space.
* A maximum of 512 bytes may be transfered (buffer size).
* On entry: x=buffer header, u=buffer offset,
* y=address of system space, and d=transfer byte count.
* The routine is optimized for speed so 2 bytes at
* a time are moved during the transfer.
*

cpybts bsr cpybsg do initial setup
 beq cpybt6 if 0, no move
 leau d,x add in buffer offset
 pshs u save it
 bitb #1 is count odd?
 beq cpybt4
 lda 0,x+ get first byte
 sta 0,y+ move it
 cmpx 0,s finished?
 beq cpybt5
cpybt4 ldd 0,x++ from here on do 2 bytes
 std 0,y++ at a time for speed
 cmpx 0,s finished?
 bne cpybt4 loop til done
cpybt5 puls u,pc return
cpybt6 rts return

 pag

*
* cpystb
*
* This routine is exactly like above except date is
* transfered from the system space to a buffer.  The
* same entry conditions apply.
*

cpystb bsr cpybsg do init stuff
 beq cpyst6 if 0, no move
 leau d,x calc final address
 pshs u save on stack
 bitb #1 is count odd?
 beq cpyst4
 lda 0,y+ get first byte
 sta 0,x+ move it
 cmpx 0,s finished?
 beq cpyst5
cpyst4 ldd 0,y++ xfr data 2 bytes
 std 0,x++ at a time!
 cmpx 0,s finished?
 bne cpyst4
cpyst5 puls u,pc return
cpyst6 rts return

*
* cpybsg
*
* Do initial setup for buffer data transfers.
*

cpybsg pshs d save params
 lbsr mapbuf map in buffer segment
 ldd bfadr,x get buffer address
 anda #$0f mask off top bits
 leau d,u point to buffer in seg
 ldx #SBUFFR point to sbuffr space
 tfr u,d get offset in d
 leax d,x adjust buffer ptr
 ldd 0,s++ reset d (count)
 rts return

 pag

*
* dupum
*
* Duplicate a user space.  New memory is allocated
* and all old user memory data is copied to new space.
* Entered with b containing size in segments of
* the user space (stack and data only).  On entry,
* U points to the new task table entry.
*

dupum pshs b save size count
 jsr getpag get a memory segment
 pshs b save page number
 ldx #umem+USRHIP point to high page
 lda 0,x get high page number
 jsr segcop copy the segment
 pshs a,x compute (XBUF<<12)+((umem+USRHIP)&FFF)
 ldd #umem+USRHIP
 anda #$0F
 tfr d,x
 ldb 3,s get new page number
 stb (XBUF<<12),x
 puls a,x
 stb tsutop,u set new user top
 stu (XBUF<<12)+(utask&$FFF) save task pointer
 tst MAXMAP multiple memory maps?
 beq dupum2
 pshs x
 tfr u,x
 jsr getmap get new map
 stb (XBUF<<12)+(umapno&$FFF) set map register
 stb (XBUF<<12)+(urelod&$FFF)
 puls x
dupum2 dec 1,s dec the mem count
 beq dupum4
dupum3 lda 0,-x get old page number
 cmpa BLKHOL existent memory?
 beq dupum3
 jsr getpag get a segment
 jsr segcop copy segment
 pshs x,b
 lda 3,s get new user page
 lbsr mapxbf map into XBUFFER
 ldd 1,s get address
 anda #$0F mask top four bits (page)
 ora #XBUF<<4 pont to buffer page
 tfr d,x get ptr back
 puls b
 stb 0,x put in new mem map
 puls x
 bra dupum2
dupum4 puls d,pc return


 pag

*
* indata
*
* Move data from the buffer in X (buffer offset
* in U) to the address space pointed to by 'uistrt'.
* Move D bytes.  If 'uiosp' is non-zero, move the
* data to the user's space.
*

indata pshs d save count
 ldy uistrt get address
 tst uiosp user's space?
 beq indat2
 lbsr cpybtu copy data to user
 bra indat3
indat2 lbsr cpybts copy data to system
indat3 puls d reset count


*
* updtio
*
* Update the user's io data.  This includes
* updating 'uistrt', 'uicnt', and 'uipos'.
*

updtio pshs d save count
 addd uistrt updata start address
 std uistrt save new
 ldd uicnt get initial count
 subd 0,s remove what just xfrd
 std uicnt save new value
 ldd uipos2 get position
 addd 0,s++ add in bytes xfrd
 std uipos2
 bcc updti4 overflow?
 ldd uipos get top half
 addd #1 add in overflow bit
 std uipos save result
updti4 rts return


 pag

*
* otdata
*
* Move data from memory to the buffer pointed at
* by X.  The buffer offset is in U and the byte
* count is in D.
*

otdata pshs d save count
 ldy uistrt point to start address
 tst uiosp system space?
 beq otdat2 if zero, system
 lbsr cpyutb copy user's to buffer
 bra otdat3
otdat2 lbsr cpystb copy system to buffer
otdat3 puls d reset count
 bra updtio update io info


*
* sumem
*
* General setup for get and put user byte routines.
* Work done is to get the appropriate use segment
* mapped into XBUFFER.
*

sumem pshs x save data
 ldd 0,s get address
 lsra get hi part to lo
 lsra
 lsra
 lsra
 ldx #umem point to user mem map
 lda a,x get user segment
 lbsr mapxbf map it in
 puls d get address
 pshs cc save error state from mapping
 anda #$0f mask page bits
 ora #XBUF<<4 point to XBUFFER page
 tfr d,x put address in x
 puls cc,pc return - indicate mapping error or not


 pag

*
* gtuwrd
*
* Get a word (in D) from the user address space.
* The address is in X.
*

gtuwrd pshs x save address
 bsr sumem map in user segment
 bcc 00f jump if mapped in OK
 ldd #0 mapping error - return 0
 bra 01f
00 anda #$0f check if only one
 cmpd #$fff byte left in segment
 beq gtuwr2
 ldd 0,x get the word
01 puls x,pc return
gtuwr2 lda 0,x get one byte
 ldx 0,s restore address
 leax 1,x bump to next byte
 bsr gtubyt
 puls x,pc


*
* gtubyt
*
* Get a byte from the user address specified in X.
* Return byte in B.
*

gtubyt pshs x,a save data
 bsr sumem setup user memory
 bcc 00f jump if mapped OK
 ldb #0 map error - return 0
 bra 01f
00 ldb 0,x get the byte
01 puls a,x,pc return


 pag

*
* ptuwrd
*
* Put word in D into user address space specified
* by the address in X.
*

ptuwrd pshs x,d save data
 bsr sumem set up user memory
 bcs 00f jump if mapping error
 anda #$0f sheck if only one
 cmpd #$fff byte left in segment
 beq ptuwr2
 ldd 0,s get the word
 std 0,x put the word
00 puls d clean up stack
01 puls x,pc return
ptuwr2 puls d get the word
 sta 0,x put one byte
 ldx 0,s reset the address
 leax 1,x bump to next byte
 bsr ptubyt
 puls x,pc


*
* ptubyt
*
* Put byte in B into user space at X.
*

ptubyt pshs d,x save data
 lbsr sumem set up user memory
 puls d reset data
 bcs 00f jump if mapping error
 stb 0,x put the byte
00 puls x,pc return

 pag

*
* segcop
*
* Copy one 4k segment to another.  Copy from segment
* in a to segment in b.  X and y are preserved.
*

segcop pshs d,x,y save regs
 jsr mapsbf map (A) into SBUFFER
 lda 1,s
 jsr mapxbf map (B) into XBUFFER
 ldx #SBUFFR point to system buffer
 ldy #XBUFFR point to cross buffer
segco2 ldd 0,x transfer data 2 bytes a crack
 std 0,y
 ldd 2,x
 std 2,y this way for speed
 ldd 4,x
 std 4,y
 ldd 6,x
 std 6,y
 ldd 8,x
 std 8,y
 ldd 10,x
 std 10,y
 ldd 12,x
 std 12,y
 ldd 14,x
 std 14,y finally finished!
 leax 16,x jump ahead
 leay 16,y
 cmpx #XBUFFR end of buffer?
 bne segco2
 puls d,x,y,pc return


 pag

*
* clrpag
*
* Allocate a new memory segment and clear it out.
* (fill it with null bytes)
*

clrpag pshs x,y,u save x
 jsr getpag get a new segment
 beq clrpa4 no memory?
 tfr b,a
 jsr mapxbf map in segment
 ldu #XBUFFR+PAGSIZ point to new segment
 ldy #0 set null words
 ldx #0
clrpa2 pshu x,y zero out 4 bytes
 cmpu #XBUFFR end of segment?
 bne clrpa2
 clz set true
clrpa4 puls x,y,u,pc return
