; FTPUtila.asm -- assembly routines for Pup FTP
; Copyright Xerox Corporation 1979, 1980
; Last modified December 3, 1980 3:08 PM by Boggs

.ent DataIsBinary
.ent MulPlus32x16
.ent Divide32x16
.ent DblUsc

.srel

DataIsBinary:
.DataIsBinary
MulPlus32x16:
.MulPlus32x16
Divide32x16:
.Divide32x16
DblUsc:
.DblUsc

.nrel

; DataIsBinary(address, count) = true or false
; Returns true if the "count" bytes of data starting at "address"
; have any high-order bits on.

.DataIsBinary:
sta 3 1,2
mov 0 3; Address to ac3
lda 0 c17; Compute count mod 16
and 1 0
sta 0 2,2; Save for later
lda 0 c100200; Load mask
movzr 1 1; Compute count/16
movzr 1 1
movzr 1 1
movzr 1 1 snr
jmp datib2; Less than 16 bytes
sta 1 3,2; Store loop count

; Fast loop -- scans 16 bytes per iteration
datib1:
lda 1 0,3
and# 0 1 snr
lda 1 1,3
and# 0 1 snr
lda 1 2,3
and# 0 1 snr
lda 1 3,3
and# 0 1 snr
lda 1 4,3
and# 0 1 snr
lda 1 5,3
and# 0 1 snr
lda 1 6,3
and# 0 1 snr
lda 1 7,3
and# 0 1 szr
jmp truret; Return true if saw a high bit on
lda 1 c10; Advance address
add 1 3
dsz 3,2; Decrement and test count
jmp datib1

; Slow loop to handle leftover words
datib2:
lda 1 2,2; Get leftover byte count
movzr 1 1 snr; Compute word count (remainder to carry)
jmp datib4; No full words left
sta 1 2,2; Store loop count
datib3:
lda 1 0,3; Look at a word
and# 0 1 szr
jmp truret; Return true if saw a high bit on
inc 3 3; Advance address
dsz 2,2; Decrement and test count
jmp datib3

; Handle leftover byte if any
datib4:
lda 1 0,3; Get the word
mov# 0 0 szc; Is there a leftover byte?
movl# 1 1 snc; Yes, skip if it has high bit set
mkzero 0 0 skp; Return false -- data is not binary
truret:
mkminusone 0 0; Return true -- data is binary
lda 3 1,2
jmp 1,3

c10:
10
c17:
17
c100200: 100200

; MulPlus32x16(addend, multiplier, lvMultiplicand)
; lvMultiplicand = lvMultiplicand*multiplier + addend
; 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

; 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 dividend
div; ac0 ← remainder, ac1 ← quotient
nop
sta 1 @2,3; store low quotient
mov 3 2; restore stack pointer
lda 3 1,2
jmp 1,3

; DblUsc(lvA, lvB, nWords) = -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.

.DblUsc:
sta 0 1,2; lvA
sta 1 2,2; lvB
lda 0 0,3; numArgs
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; nWords
jmp uscLp
jmp 1,3; all words equal; return zero.


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

c2:
2

.end