; IFSResUtila.asm -- IFS resident utility stuff
; Copyright Xerox Corporation 1979, 1980, 1981

; Last modified November 14, 1981  12:19 PM by Taft

.ent TFSSwatProc
.ent MulDiv
.ent DoubleUsc
.ent IfsPupChecksum
.ent DoMove
.ent Mul
.ent Div
.ent Div32x16
.ent LoadXM
.ent StoreXM
.ent DoubleBlt
.ent CallWithArgVec
.ent SysAllocate
.ent SysFree
.ent GetBit
.ent SetBit

.bext sysZone

	.srel

TFSSwatProc: .TFSSwatProc
MulDiv:	.MulDiv
DoubleUsc: .DoubleUsc
IfsPupChecksum: .IfsPupChecksum
DoMove:	.DoMove
Mul:	.Mul
Div:	.Div
Div32x16: .Div32x16
LoadXM:	.LoadXM
StoreXM: .StoreXM
DoubleBlt: .DoubleBlt
CallWithArgVec: .CallWithArgVec
SysAllocate: .SysAllocate
SysFree: .SysFree
GetBit:	.GetBit
SetBit:	.SetBit

	.nrel

; Procedure called by Swat before swapping us out (code = 0)
; and after swapping us back in (code = -1)

.TFSSwatProc:
	snz 0 0		; entering Swat?
kwait:	inc 0 0 snr
	 jmp 1 3
	lda 1 @.KBLK	; yes, wait for no Trident commands pending
	sz 1 1
	 jmp kwait
	inc 1 1 szr	; wait a while longer for things to settle
	 jmp .-1
	jmp 1 3

.KBLK:	640


; MulDiv(a, b, c)
; returns the unsigned value (a*b)/c

.MulDiv:
	sta 3 1 2
	mov 2 3
	mov 0 2
	mkzero 0 0
	mul
	lda 2 3 3
	div
	 #77400
	mov 1 0
	mov 3 2
	jmp done

; DoubleUsc(lvA, lvB, nWords; numargs na) = -1|0|1
; Returns:	-1 if A < B
;		 0 if A = B
;		 1 if A > B
; lvA and lvB are the addresses of the nWords-word operands
; nWords defaults to 2.

.DoubleUsc:
	sta 0 1 2		; lvA
	sta 1 2 2		; lvB
	lda 0 0 3		; number of arguments
	lda 1 c2
	sne 0 1
	 sta 1 3 2		; default nWords to 2
uscLp:	lda 0 @1 2		; A word
	lda 1 @2 2		; B word
	sleu 0 1
	 jmp gr			; A > B
	sub 1 0 szr
	 jmp ls			; A < B
	isz 1 2			; lvA
	isz 2 2			; lvB
	dsz 3 2			; decrement and test count
	 jmp uscLp
	jmp 1 3			; all words equal, return zero

gr:	mkone 0 0 skp
ls:	 mkminusone 0 0
	jmp 1 3

c2:	2


; IfsPupChecksum(pup)
; Microcoded replacement for PupChecksum procedure in PupAl1a.asm

.IfsPupChecksum:
	sta 3 1 2
	mov 0 1			; address to ac1 for microcode
	mov 0 3
	lda 3 0 3		; get pup length (bytes)
	neg 3 3			; compute # words exclusive of checksum
	comzr 3 3		;  = (# bytes -1)/2
	mkzero 0 0		; init checksum
	#63000			; call microcode
	jmp done


; DoMove -- tail of Dvec operation (see IfsDvec.bcpl for explanation)

.DoMove:
	lda 3 3 2
	add 2 3
	lda 2 3 3
	lda 3 4 3
	blt
	inc 1 0
	jmp done

; Mul(addend, multiplicand, multiplier, lvResult)
; Gives direct access to the "mul" instruction.
; Computes addend + (multiplicand*multiplier) and puts the
; 32-bit unsigned result in @lvResult.

.Mul:	sta 3 1 2
	mov 2 3			; put stack pointer here
	lda 2 3 3		; get extra args word
	add 3 2			; compute absolute pointer
	sta 2 3 3		; put it back
	lda 2 3 2		; get 3rd arg (multiplier)
	mul			; ac0 + (ac1*ac2) -> (ac0, ac1)
	mov 3 2			; restore stack pointer
	lda 3 3 2		; get extra args pointer
	lda 3 4 3		; get lvResult
	sta 0 0 3		; store result
	sta 1 1 3
	jmp done


; Div(lvDividend, divisor, lvRemainder) = quotient
; Gives direct access to the "div" instruction.
; @lvDividend is the 32-bit unsigned dividend.
; Computes (@lvDividend)/divisor, puts the remainder in @lvRemainder,
; and returns the quotient.

.Div:	sta 3 1 2
	sta 1 2 2		; save divisor
	mov 0 3			; lvDividend
	lda 0 0 3		; Get dividend into ac0 and ac1
	lda 1 1 3
	mov 2 3			; preserve stack pointer
	lda 2 2 3		; get divisor
	div			; (ac0, ac1)/ac2, ac0←remainder, ac1←quotient
	 #77400
	mov 3 2			; restore stack pointer
	sta 0 @3 2		; @lvRemainder ← remainder
	mov 1 0			; return quotient
	jmp done


; Div32x16(lvDividend, divisor)
; Does an unsigned division of the 32-bit number at @lvDividend by the
; 16-bit divisor, stores the quotient back in @lvDividend, and returns
; the remainder.

.Div32x16:
	sta 3 1 2
	mov 2 3			; preserve stack pointer
	sta 0 2 3		; save lvDividend
	mov 1 2			; divisor
	mkzero 0 0
	lda 1 @2 3		; get high dividend
	div			; ac0←remainder, ac1←quotient
	 #77400
	sta 1 @2 3		; store high quotient
	isz 2 3
	lda 1 @2 3		; get low dividend
	div			; ac0←remainder, ac1←quotient
	 #77400
	sta 1 @2 3		; store low quotient
	mov 3 2			; restore stack pointer
done:	lda 3 1 2
	jmp 1 3

; Extended memory support.

; LoadXM(bank, adr)
; Returns the value at adr in bank

.LoadXM:
	#63420			; XMLoad: AC0 ← (@AC1 in bank AC0)
	jmp 1 3

; StoreXM(bank, adr, value)
; Stores value into adr in bank

.StoreXM:
	sta 3 1 2
	lda 3 3 2
	#63421			; XMStore: (@AC1 in bank AC0) ← AC3
	jmp done


; DoubleBlt(dest, source, count, banks)
; Block-transfers double-words, possibly from one bank to another.
; dest = start of destination block.  *** Must be even ***
; source = start of source block.  *** Must be even ***
; count = number of words to transfer.  *** Must be even ***
; banks = (destBank lshift 2) + sourceBank, where destBank and sourceBank
;	are the destination and source bank numbers.
; The previous contents of the emulator bank register are saved and restored.
; *** Not reentrant ***

.DoubleBlt:
	sta 3 1 2
	lda 3 3 2		; Frame offset of extra args
	add 2 3			; Make absolute pointer
	sta 0 2 2		; Save dest and source
	sta 1 3 2
	lda 0 4 3		; ac0← banks
	lda 1 .dblblt		; Construct DoubleBlt instruction
	add 0 1
	sta 1 inst		; Put it down in front of us
	lda 0 2 2		; Recover dest and source
	lda 1 3 2
	lda 3 3 3		; ac3← count
	movzr 3 3		; Microcode wants count/2
inst:	0			; The instruction
	jmp done

.dblblt: #63400			; DoubleBlt trap opcode

; CallWithArgVec(proc, lvArgs, numArgs) = result
; Calls proc with arguments lvArgs!0 through lvArgs!(numArgs-1),
; and returns the result returned by proc.

proc = 4
lvArgs = 5
numArgs = 6
retInst = 7

.CallWithArgVec:
	sta 3 1 2
	jsr @370
	 retInst+1
	 jsr @367

	lda 3 lvArgs 2
	lda 0 numArgs 2
	lda 1 c3
	se 0 1			; exactly 3 args?
	 adc 2 3 skp		; no, temp3 ← lvArgs-frame-1
	 lda 3 2 3		; yes, temp3 ← 3rd arg
	sta 3 3 2

	lda 3 lvArgs 2		; build calling sequence in frame
	lda 0 cJsrProc
	sta 0 lvArgs 2
	lda 0 cReturn
	sta 0 retInst 2
	lda 0 0 3		; first 2 args
	lda 1 1 3
	jmp lvArgs 2		; jump to calling sequence

; calling sequence in frame:
;	jsr @proc 2
;	 numArgs
;	jsr @366		; bcpl return

cJsrProc: jsr @proc 2
cReturn: jsr @366
c3:	3

; SysAllocate(nWords) = Allocate(sysZone, nWords)
; SysFree(block) = Free(sysZone, block)
; Only the 2-argument forms of Allocate and Free are supported.
; Note: this code depends on the fact that GetFrame always stores
; its first two arguments, regardless of the number of arguments
; specified in the call.

.dmr ObjCall = 65400

.SysAllocate:
	mov 0 1
	lda 0 @lvSysZone
	ObjCall 0		; jmp ac0!0 = ZN.Allocate

.SysFree:
	mov 0 1
	lda 0 @lvSysZone
	ObjCall 1		; jmp ac0!1 = ZN.Free

lvSysZone: sysZone


; GetBit(lvBitArray, bitNumber) = value
; Reads a bit from a bit array.  The base of the array is lvBitArray
; and the index is bitNumber.  Returns the result as a BCPL boolean;
; i.e., a one is returned as -1 (BCPL true).

.GetBit:
	sta 3 1 2
	mov 0 3			; lvBitArray
	movzr 1 0		; bitNumber
	movzr 0 0
	movzr 0 0
	movzr 0 0
	add 0 3			; lvBitArray + bitNumber/16
	lda 0 0 3		; word from array
	cycle 0			; left-cycle bitNumber rem 16
	movl# 0 0 snc		; test result now in sign
	 mkzero 0 0 skp		; false
	 mkminusone 0 0		; true
	jmp done


; SetBit(lvBitArray, bitNumber, value)
; Writes a bit into a bit array.  The base of the array is lvBitArray
; and the index is bitNumber.  The bit written is zero if value is
; zero, one if value is nonzero.

.SetBit:
	sta 3 1 2
	movzr 1 3		; bitNumber
	movzr 3 3
	movzr 3 3
	movzr 3 3
	add 3 0			; lvBitArray + bitNumber/16
	sta 0 2 2
	neg 1 1			; negate bitNumber for right shift
	adczr 0 0		; 77777B
	cycle 0			; left-cycle bitNumber rem 16
	lda 1 @2 2		; word from array
	and 0 1			; turn off the bit
	lda 3 3 2		; get value
	mov# 3 3 szr
	 adc 0 1		; nonzero, turn on the bit
	sta 1 @2 2		; store back into array
	jmp done


	.end