; IfsScavCompare.asm
; Copyright Xerox Corporation 1981
; Last modified July 13, 1981 5:51 PM by Boggs
.ent CompareRecords, CompareLPTEs, CompareStrings, DirEntryLength
.bext IFSError
; The following must agree with the DR structure in IfsDirs.decl.
DR.pathName = 6.
drHeaderMask = 171600
drLengthMask = 177
; The following must agree with the LPTE structure in IfsScavLPT.bcpl.
LPTE.ifsName = 9.
.dmr GetNextChar = 66400
.srel
CompareRecords: .CompareRecords
CompareLPTEs: .CompareLPTEs
CompareStrings: .CompareStrings
DirEntryLength: .DirEntryLength
.nrel
; CompareRecords(r1, r2)
; Compares two B-Tree records in the same manner as DirCompareKey.
; This differs from DirCompareKey in that it compares two records
; rather than a key and a record.
; Note: this procedure is not reentrant.
.CompareRecords:
sta 3 1 2
lda 3 cDR.pathName
add 3 0
add 3 1
jmp Compare
; CompareLPTEs(lpte1, lpte2)
; Same as CompareRecords except compares two LPTEs.
; Note: this procedure is not reentrant.
.CompareLPTEs:
sta 3 1 2
mov 0 3
lda 0 LPTE.ifsName 3
add 3 0
mov 1 3
lda 1 LPTE.ifsName 3
add 3 1
jmp Compare
; CompareStrings(string1, string2)
; Same as CompareRecords except compares two Strings.
; Note: this procedure is not reentrant.
.CompareStrings:
sta 3 1 2
Compare:
sta 0 2 2 ; string1
sta 1 3 2 ; string2
; Find position of last "!" in string1.
lda 0 @2 2 ; first word of string1
lda 1 c177400
ands 1 0
sta 0 lenVerString ; save temporarily
lda 1 2 2 ; address of string1
lda 3 csdR1
snz 3 3 ; CSD pointers already set up?
jsr SetupCSDs ; no, do so first
sta 1 1 3 ; save string address
FindLastExcl:
jsr @363 ; load ac0'th byte from array pointed to
; by ac1, and return byte in ac1.
; does not clobber ac0!
lda 3 cExcl
sne 1 3
jmp FoundExcl
neg 0 0 ; decrement string index
com 0 0
lda 3 csdR1
lda 1 1 3
jmp FindLastExcl
; CompareRecords (cont'd)
; Set up CSD for string1
FoundExcl:
lda 1 lenVerString ; compute length of version string
sub 0 1 ; = entire string length - position of "!"
sta 1 lenVerString
subzr 1 1 ; 100000B
add 1 0 ; begin at right-hand byte
sta 0 @csdR1
; Set up CSD for r2
lda 0 @3 2 ; first word of string2
lda 1 c177400
ands 1 0
subzr 1 1 ; 100000B
add 1 0 ; begin at right-hand byte
lda 1 3 2 ; address of string2
lda 3 csdR2
sta 0 0 3
sta 1 1 3
; Compare characters in the "
name!" portion
CompareBodyLoop:
lda 0 csdR1
GetNextChar 140 ; get char from r1, convert to upper case
jmp R1Exhausted
mov 1 3
lda 0 csdR2
GetNextChar 140 ; get char from r2, convert to upper case
jmp RetGr ; exhausted: r1 is greater than r2
sne 1 3
jmp CompareBodyLoop ; equal, continue comparing
; First mismatch. If all remaining characters of r2 are digits
; then r2's body is an initial substring of r1 and we declare
; r1 to be greater. Otherwise we return the result of comparing
; the mismatching character codes. Note that the only possible effect
; of looking at the rest of r2 is to return "greater" where we
; otherwise would return "less". But if r1's char > r2's char anyway, then
; just say so now without bothering to check.
slt 3 1
jmp RetGr
CheckDigitsLoop:
lda 0 cDigit0 ; r2 char a digit?
sub 0 1
lda 0 d10
sltu 1 0
jmp RetLs ; no, r1 is less than r2
lda 0 csdR2
GetNextChar 0 ; get next char from r2
jmp RetGr ; exhausted: r1 is greater than r2
jmp CheckDigitsLoop
; Still inside CompareRecords; this stuff is here for addressing reasons.
; Internal procedure to set up csdR1 and csdR2.
; Returns with AC3 = csdR1.
; Does not disturb other ACs.
SetupCSDs:
sta 3 csdBlk
jsr AfterCSDs
csdBlk: .blk 5
AfterCSDs:
skeven 3 3 ; force CSDs to be even
inc 3 3
sta 3 csdR2
inc 3 3
inc 3 3
sta 3 csdR1
jmp @csdBlk
csdR1: .blk 1
csdR2: .blk 1
lenVerString: .blk 1
c177400: 177400
cDigit0: 60
d10: 10.
cDR.pathName: DR.pathName
cExcl: 41 ; "!"
; CompareRecords (cont'd)
; r1 is exhausted before r2: bodies seem to match.
; Now parse the version strings and compare them numerically.
; It must be possible to parse r1's version; if not, the record is malformed.
; If a non-digit is encountered in r2 then r1's body is an initial substring
; of r2 and is therefore "less".
R1Exhausted:
lda 0 @csdR1 ; get r1 byte flag (count is zero)
lda 1 lenVerString ; merge with length of version string
add 1 0
sta 0 @csdR1
mkzero 3 3 ; init parsed version
ParseV1Loop:
lda 0 csdR1
GetNextChar 0 ; get next char from r1
jmp V1Exhausted ; exhausted: go begin work on v2
lda 0 cDigit0 ; char a digit?
sub 0 1
lda 0 d10
sltu 1 0
jmp BadDR ; no, r1 is malformed
mov 3 0 ; yes, multiply parsed version by 10
addzl 3 3
addzl 0 3
add 1 3 ; add in new digit
jmp ParseV1Loop
V1Exhausted:
sta 3 lenVerString ; store r1's version here temporarily
mkzero 3 3 ; init parsed version
ParseV2Loop:
lda 0 csdR2
GetNextChar 0 ; get next char from r2
jmp V2Exhausted ; exhausted: go compare version numbers
lda 0 cDigit0 ; char a digit?
sub 0 1
lda 0 d10
sltu 1 0
jmp RetLs ; no, r1 is less than r2
mov 3 0 ; yes, multiply parsed version by 10
addzl 3 3
addzl 0 3
add 1 3 ; add in new digit
jmp ParseV2Loop
; Reached end of version number. Compare versions.
V2Exhausted:
lda 0 lenVerString ; r1's version
sne 0 3
jmp RetEq
sltu 0 3
RetGr: mkone 0 0 skp ; r1 > r2, return 1
RetLs: mkminusone 0 0 ; r1 < r2, return -1
lda 3 1 2
jmp 1 3
RetEq: mkzero 0 0 ; r1 = r2, return 0
lda 3 1 2
jmp 1 3
; DirEntryLength(record)
; returns the record's length.
.DirEntryLength:
sta 0 2 2
lda 0 @2 2
lda 1 cdrHeaderMask ; Check for valid DR
and# 1 0 szr ; Unused bits must be zero
jmp BadDRx
lda 1 cdrLengthMask ; Extract length -- must be nonzero
and 1 0 snr
jmp BadDRx
jmp 1 3
BadDRx: sta 3 1 2
BadDR: lda 0 2 2
lda 1 3 2
jsr @370
6
77400
lda 0 .ecBadDR
jsrii lvIFSError ; IFSError(ecBadDR)
1
cdrHeaderMask: drHeaderMask
cdrLengthMask: drLengthMask
.ecBadDR: 120.
lvIFSError: IFSError
.end
// The following procedure is equivalent to the one in this module:
//---------------------------------------------------------------------------
let CompareRecords(r1, r2) = valof
//---------------------------------------------------------------------------
// Compares two B-Tree records in the same manner as DirCompareKey.
// This differs from DirCompareKey in that it compares two records
// rather than a key and a record.
[
if (r1>>DR.header & drHeaderMask) ne 0 then IFSError(ecMalformedDR, r1)
if (r2>>DR.header & drHeaderMask) ne 0 then IFSError(ecMalformedDR, r2)
// Find position of last "!" in first record
let lenBodyString = nil
for i = r1>>DR.pathName.length to 1 by -1 do
if r1>>DR.pathName.char^i eq $! then [ lenBodyString = i; break ]
// Compare chars in the "name!" (string) portion
let lenR2 = r2>>DR.pathName.length
for i = 1 to lenBodyString do
[
// If we run off the end of r2 then r1 is greater.
if i gr lenR2 resultis 1
let c1 = r1>>DR.pathName.char^i
let c2 = r2>>DR.pathName.char^i
if c1 ne c2 then
[
// Lower-case alphabetics collate with upper-case
if c1 ge $a & c1 le $z then c1 = c1-($a-$A)
if c2 ge $a & c2 le $z then c2 = c2-($a-$A)
if c1 ne c2 then
[
// Definitely a mismatch. If all remaining characters of r2
// are digits then r2's body is an initial substring
// of r1 and we declare r1 to be greater. Otherwise we
// return the result of comparing the mismatching character codes.
// Note that the only possible effect of looking at the rest of r2
// is to return "greater" where we otherwise would return
// "less". But if r1's char > r2's char anyway, then just say so now
// without bothering to check.
if c1 ls c2 then
for j = i to lenR2 do
[
let digit = r2>>DR.pathName.char^j - $0
if digit ls 0 % digit gr 9 resultis -1
]
resultis 1
]
]
]
// Bodies equal, now parse the version strings and compare them numerically.
// It must be possible to parse r1's version; if not, the record is malformed.
// If a non-digit is encountered in r2 then r1's body is an initial substring
// of r2 and we return "less than"
let v1 = 0
for i = lenBodyString+1 to r1>>DR.pathName.length do
[
let digit = r1>>DR.pathName.char^i - $0
if digit ls 0 % digit gr 9 then IFSError(ecMalformedDR, r1)
v1 = 10*v1+digit
]
let v2 = 0
for i = lenBodyString+1 to lenR2 do
[
let digit = r2>>DR.pathName.char^i - $0
if digit ls 0 % digit gr 9 resultis -1 //non-digit encountered
v2 = 10*v2+digit
]
resultis Usc(v1, v2)
]
//----------------------------------------------------------------------------
and DirEntryLength(record) = valof
//----------------------------------------------------------------------------
[
let length = record>>DR.length
if (record>>DR.header & drHeaderMask) ne 0 % length eq 0 then
IFSError(ecBadDR)
resultis length
]