; CopyDiskUtilA.asm
; Copyright Xerox Corporation 1979, 1980
; Last modified April 16, 1980  3:13 AM by Boggs

.ent UBlockEq
.ent BlockEq
.ent UPupChecksum

.ent MulDiv
.ent DoubleUsc
.ent MulPlus32x16
.ent Divide32x16

	.srel

UBlockEq:	.UBlockEq
BlockEq:	.BlockEq
UPupChecksum:	.UPupChecksum

MulDiv:		.MulDiv
DoubleUsc:	.DoubleUsc
MulPlus32x16:	.MulPlus32x16
Divide32x16:	.Divide32x16

	.nrel

; UBlockEq(adr1,adr2,count) - Microcode version
; Compare the count words starting at adr1 with the corresponding
; words starting at adr2, and return true iff all the corresponding
; words are equal.

.UBlockEq:
	sta 3 1,2
	lda 3 3,2		; get count
	63000			; call microcode
	mov 3 3 szr		; ac3 = 0 if all is well
	sub 0 0 skp
	 adc 0 0
	lda 3 1,2
	jmp 1,3

; BlockEq(adr1,adr2,count) - Assembly language version
; Compare the count words starting at adr1 with the corresponding
; words starting at adr2, and return true iff all the corresponding
; words are equal.

.BlockEq:
	sta 3 1,2
	sta 0 2,2		; Store adr1
	mov 1 3			; ac3 ← adr2

; Compare the first (count mod 8) words one at a time
bleq1:	lda 0 3,2		; See if count mod 8 = 0
	lda 1 c7
	and# 0 1 snr
	 jmp bleq2		; Yes, go to fast loop
	lda 0 @2,2		; No, do slow compare of one word
	lda 1 0,3
	se 0 1
	 jmp bleq5		; Not equal, return false
	isz 2 2			; Equal, increment addresses
	inc 3 3
	dsz 3,2			; Decrement and test count
	 jmp bleq1

; Set up for fast loop
bleq2:	lda 0 3,2		; Compute count/8
	movzr 0 0
	movzr 0 0
	movzr 0 0 snr
	 jmp bleq4		; Less than 8 words in block
	sta 0 3,2		; Store count/8
	lda 0 2,2		; Get current adr1

; Fast loop.  ac0/ adr1, ac3/ adr2, count in frame temp 3.
; The portion inside dir...eir is non-reentrant due to the use
; of save2.
bleq3:	dir			; Interlock non-reentrant portion
	sta 2 save2		; Save frame pointer
	mov 0 2			; ac2 ← adr1

	lda 0 0,2		; Compare 8 words, to bleq6 if fail
	lda 1 0,3
	se 0 1
	 jmp bleq6

	lda 0 1,2
	lda 1 1,3
	se 0 1
	 jmp bleq6

	lda 0 2,2
	lda 1 2,3
	se 0 1
	 jmp bleq6

	lda 0 3,2
	lda 1 3,3
	se 0 1
	 jmp bleq6

	lda 0 4,2
	lda 1 4,3
	se 0 1
	 jmp bleq6

	lda 0 5,2
	lda 1 5,3
	se 0 1
	 jmp bleq6

	lda 0 6,2
	lda 1 6,3
	se 0 1
	 jmp bleq6

	lda 0 7,2
	lda 1 7,3
	se 0 1
	 jmp bleq6

	lda 0 c10
	add 0 3			; Increment adr2 by 8
	add 2 0			; Increment adr1 by 8, move to ac0
	lda 2 save2		; Recover frame pointer
	eir			; Now reentrant
	dsz 3,2			; Decrement and test count
	 jmp bleq3		; More to do

bleq4:	mkminusone 0 0 skp	; Here to return true
bleq5:	 mkzero 0 0		; Here to return false
	lda 3 1,2
	jmp 1,3

bleq6:	lda 2 save2		; Here to return false when
	eir			;  inside fast loop
	jmp bleq5

c7:	7
c10:	10
save2:	0

; PupChecksum microcode interface
; microcoded replacement for PupChecksum procedure in PupAl1a.asm

.UPupChecksum:
	sta 3 1,2
	mov 0 1			; address in AC1 for microcode
	mov 0 3
	lda 3 0,3		; get pup length in bytes
	neg 3 3			; compute # words exclusive of checksum
	comzr 3 3		;  = (# bytes -1)/2
	mkzero 0 0		; init checksum
	63400			; call microcode
	lda 3 1,2
	jmp 1,3
	

; 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
	lda 3 1,2
	jmp 1,3

; MulPlus32x16(addend,multiplier,lvMultiplicand)
; lvMultiplicand = lvMultiplicand*multiplier + addend (all unsigned)
; lvMultiplicand is 32 bits, multiplier and addend are 16 bits.
; returns the overflow

.MulPlus32x16:
	sta 3 1,2		; save return in frame
	mov 2 3			; vacate AC2.  AC3← frame
	sta 1 2,3		; save multiplier in frame
	lda 2 3,3		; AC2← lvMultiplicand
	lda 2 1,2		; AC2← lvMultiplicand!low
	mul
	lda 2 3,3		; AC2← lvMultiplicand
	sta 1 1,2		; store low result
	lda 2 0,2		; AC2← lvMultiplicand!high
	lda 1 2,3		; AC1← multiplier
	mul
	sta 1 @3,3		; store high result
	mov 3 2			; restore frame
	lda 3 1,2		; return
	jmp 1,3			; overflow in AC0

; DoubleUsc(lvA, lvB) = -1|0|1
; Returns:	-1 if A < B
;		 0 if A = B
;		 1 if A > B
; lvA and lvB are the addresses of the 32 bit operands

.DoubleUsc:
	sta 0 3,2		; lvA
	sta 1 2,2		; lvB
	lda 0 @3,2		; A high part
	lda 1 @2,2		; B high part
	se 0 1			; A, B
	 jmp dusc1
	isz 3 2			; lvA
	isz 2 2			; lvB
	lda 0 @3,2		; A low part
	lda 1 @2,2		; B low part

dusc1:	sleu 0 1		; A, B
	 jmp gr			; A > B
	se 0 1			; A, B
	 mkminusone 0 0 skp	; A < B
	 mkzero 0 0		; A = B
	jmp 1,3

gr:	mkone 0 0
	jmp 1,3


; Divide32x16(lvNumber, divisor) =
; unsigned divide the 32-bit number at @lvNumber,
; put the quotient back in @lvNumber, and return the remainder.

.Divide32x16:
	sta 3 1,2		; return
	mov 2 3			; save stack pointer
	sta 0 2,3		; lvNumber
	sta 1 3,3		; divisor
	mkzero 0 0
	lda 1 @2,3		; get high dividend
	lda 2 3,3		; get divisor
	div			; ac0 ← remainder, ac1 ← quotient
	 nop
	sta 1 @2,3		; store high quotient
	isz 2,3
	lda 1 @2,3		; get low divident
	div			; ac0 ← remainder, ac1 ← quotient
	 nop
	sta 1 @2,3		; store low result
	mov 3 2			; restore stack pointer
	lda 3 1,2
	jmp 1,3

	.end