; PupAl1a.asm -- Pup level 1 assembly code (Alto-specific)
; Assembly routines to facilitate computing Pup checksums
; Plus level 1 utility routines
; Copyright Xerox Corporation 1979, 1980

;	Last modified March 7, 1980  2:23 PM by Taft


.ent PupChecksum
.ent OnesComplementAdd
.ent OnesComplementSubtract
.ent LeftCycle
.ent MultEq
.ent DoubleIncrement
.ent DoubleSubtract
.ent DoubleDifference
.ent HLookup
.ent HInsert
.ent HDelete
.ent HEnumerate

	.srel

PupChecksum:		.PupChecksum
OnesComplementAdd:	.OnesComplementAdd
OnesComplementSubtract:	.OnesComplementSubtract
LeftCycle:		.LeftCycle
MultEq:			.MultEq
DoubleIncrement:	.DoubleIncrement
DoubleSubtract:		.DoubleSubtract
DoubleDifference:	.DoubleDifference
HLookup:		0		;Call0
HInsert:		0		;Call1
HDelete:		0		;Call2
HEnumerate:		0		;Call3

	.nrel


; PupChecksum(pup)
; Computes and returns the ones-complement add-and-cycle checksum
; over the block starting at pup and containing pup!0 bytes.
; Let count = (pup!0-1)/2.  Then
; Timing:  24 + 8.5*(count mod 8) + 42*(count/8) memory cycles
;	  = 1486 cycles for a maximum-length Pup (276 words)

.PupChecksum:
	sta 3 1 2	; Store BCPL return
	mov 0 3		; Put pup address in 3
	lda 1 0 3	; Get Pup length (bytes)
	neg 1 1		; Compute # words -1
	comzr 1 1	;  = (# bytes -1)/2
	movzr 1 0	; Compute count/8
	movzr 0 0
	movzr 0 0
	inc 0 0		; Add 1 since end test is done first
	sta 0 2 2	; Store loop count in frame temp
	lda 0 C7	; Compute (count rem 8)
	and 1 0 snr
	 jmp ELoop8	; Start 8-at-a-time loop if no remainder
	sta 0 3 2  	; Remainder in another frame temp
	sub 0 0		; Init checksum to zero
CLoop1:	lda 1 0 3	; Fetch a word
	addz 1 0 szc	; Ones-complement add
	 inc 0 0	; Carry
	cycle 1		; Left cycle 1
	inc 3 3		; Increment pointer
	dsz 3 2  	; Decrement and test count
	 jmp CLoop1	; Repeat slow loop
	jmp ELoop8	; Done, begin fast loop

; This is the main loop of the add-and-cycle checksum computation.
; It checksums 8 words before incrementing and testing the pointer
; and count.

CLoop8:	lda 1 0 3	; Fetch a word
	addz 1 0 szc	; Ones-complement add
	 inc 0 0	; Carry
	cycle 1		; Left cycle 1

	lda 1 1 3
	addz 1 0 szc
	 inc 0 0
	cycle 1

	lda 1 2 3
	addz 1 0 szc
	 inc 0 0
	cycle 1

	lda 1 3 3
	addz 1 0 szc
	 inc 0 0
	cycle 1

	lda 1 4 3
	addz 1 0 szc
	 inc 0 0
	cycle 1

	lda 1 5 3
	addz 1 0 szc
	 inc 0 0
	cycle 1

	lda 1 6 3
	addz 1 0 szc
	 inc 0 0
	cycle 1

	lda 1 7 3
	addz 1 0 szc
	 inc 0 0
	cycle 1

	lda 1 C10	; Advance pointer to next group of 8
	add 1 3
ELoop8:	dsz 2 2		; Test loop count
	 jmp CLoop8	; Loop

	lda 3 1 2	; Done, prepare to return
	jmp TestM0	; Test for -0 and return

C7:	7
C10:	10

; OnesComplementSubtract(a, b)

.OnesComplementSubtract:
	com 1 1		; Negate b (ones-complement)
			; Fall into OnesComplementAdd

; OnesComplementAdd(a, b)

.OnesComplementAdd:
	addz 1 0 snc	; Add, skip if carry
TestM0:	 inc# 0 0 snr	; No carry, test for -0
	 inc 0 0	; Carry or turn -0 into +0
	jmp 1 3

; LeftCycle(word, count)

.LeftCycle:
	cycle 0		; Left-cycle ac0 by number in ac1
	jmp 1 3


; MultEq(adr1, adr2, nWords) - Multiple word equal predicate.
; Compares the nWords words starting at address adr1 with the ones
; starting at adr2, returns true if equal, false if unequal.
; If nWords is omitted, it is defaulted to 2.

.MultEq:
	sta 0 1 2		; Save adr1 in frame
	sta 1 2 2		; Save adr2 in frame
	lda 0 0 3		; Get number of args in call
	lda 1 c2
	sgt 0 1			; More than 2?
	 sta 1 3 2		; No, default nWords to 2
eqloop:	lda 0 @1 2		; Get word from adr1
	lda 1 @2 2		; Get word from adr2
	se 0 1			; Compare
	 jmp eqfail		; Not equal, return false
	isz 1 2			; Equal, increment adr1
	isz 2 2			; Increment adr2
	dsz 3 2			; Decrement and test word count
	 jmp eqloop		; More words to check
	mkminusone 0 0 skp	; That's all, return true
eqfail:	 mkzero 0 0		; Here to return false
	jmp 1 3			; Return

c2:	2

; DoubleIncrement(adr, offset)
; Add offset to the 32-bit number pointed to by adr.
; If offset is omitted, it is defaulted to 1.
; Offset is treated as a SIGNED 16-bit integer.

.DoubleIncrement:
	sta 3 1 2		; Save return
	mov 0 3			; Pointer to 32-bit number
	lda 0 @1 2		; Get number of args in call
	movzr# 0 0 snr		; Less than 2?
	 mkone 1 1		; Yes, default offset to 1
	movl# 1 1 szc		; Skip if offset positive
	 dsz 0 3		; Negative, effectively extend sign
	  nop
	lda 0 1 3		; Get low 16 bits of number
	addz 1 0 szc		; Add offset
	 isz 0 3		; Carry out, increment high part
	  nop
	sta 0 1 3		; Update low 16 bits
	lda 3 1 2		; Return
	jmp 1 3

; DoubleDifference(adrA, adrB) = 16-bit number
; Returns A-B as a signed 16-bit number, where adrA and adrB point
; to 32-bit numbers A and B.  If A and B differ by more than 2↑15,
; the result is 2↑15-1 or -2↑15 as appropriate.

.DoubleDifference:
	sta 3 1 2		; Save return
	sta 1 2 2		; Save adrB
	mov 1 3			; adrB
	lda 1 1 3		; Get low 16 bits of B
	mov 0 3			; adrA
	lda 0 1 3		; Get low 16 bits of A
	subz 1 0		; Compute low part of A-B
	lda 1 @2 2		; Get high 16 bits of B
	lda 3 0 3		; Get high 16 bits of A
	mov# 0 0 szc		; Test carry from low difference
	 sub 1 3 skp		; No borrow, just compute high A-B
	 adc 1 3		; Borrow first before subtracting
	movl 0 1		; Get sign of low result
	subcl 1 1		; 0 if positive, 1 if negative
	add 3 1 snr		; Test for high = sign extension
	 jmp diffok		; Yes, return the low difference
	movl# 3 3 snc		; Overflow, test sign of result
	 adczr 0 0 skp		; Positive, return +2↑35-1
	 subzr 0 0		; Negative, return -2↑35
diffok:	lda 3 1 2		; Return
	jmp 1 3


; DoubleSubtract(adrA, adrB)
; Does A ← A-B, where adrA and adrB point to 32-bit numbers A and B

.DoubleSubtract:
	sta 3 1 2		; Save return
	sta 1 2 2		; Save adrB
	mov 1 3			; adrB
	lda 1 1 3		; Get low 16 bits of B
	mov 0 3			; adrA
	lda 0 1 3		; Get low 16 bits of A
	subz 1 0		; Compute low part of A-B
	sta 0 1 3		; Store result back into low A
	lda 0 0 3		; Get high 16 bits of A
	lda 1 @2 2		; Get high 16 bits of B
	mov# 0 0 szc		; Test carry from low difference
	 sub 1 0 skp		; No borrow, just compute high A-B
	 adc 1 0		; Borrow first before subtracting
	sta 0 0 3		; Store result back into high A
	lda 3 1 2		; Return
	jmp 1 3

	.end