home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
enterprs
/
cpm
/
utils
/
f
/
lbrhlp22.lbr
/
LH-UNCR.LZB
/
LH-UNCR.LIB
Wrap
Text File
|
1992-05-09
|
21KB
|
646 lines
;========================================================================
;
; LH-UNCR.LIB (formerly LBRHLP5.LIB)
; Uncrunch routines for LBRHLP
;
uncrel:
call intram ; init all necessary ram locs
ld (stksav),sp
ld sp,(topstk)
call getchr ; get a char from the input stream
cp 76h ; check for crunched file header "76fe"
jr nz,ncrnch ; br if not
call getchr ;
cp 0feh ;
jr z,ycrnch ;
ncrnch:
ld a,2 ; invalid version
jr mainrtn ; skip file and continue
stkovf:
ld a,4
jr mainrtn
oldtyp:
ld a,1 ; newer revision of this program needed
jr mainrtn
uneof:
ld a,5
jr mainrtn
fatbad:
ld a,3
mainrtn:
scf
mrtn2:
ld sp,(stksav)
ret
retccp:
xor a
jr mrtn2
ycrnch:
call getchr ; get next char
or a ; a zero byte indicates end of filename
jr nz,ycrnch
;
call getchr ; loop absorbs extraneous header info
call getchr ; get a char
push af
call getchr ; get revision level, do nothing with it
call getchr ; get significant revision level
pop af
cp sigrev ; compare to this prog
jr c,oldtyp ; br if old type1x crunched file
jr nz,ncrnch ; if equal, ok, else...
sigok:
call initb2 ; initialize the lzw table
exx ; switch to alt regs
ld bc,0 ; init repeat flag register
exx
ld de,nopred ; init to "nopred" (null value)
;______________________________________________________________________________
;
; *** main decoding loop(s). ***
;
mainlp: ld (lastpr),de ; always keep a copy of the last "pred" here
call getcod ; get bits to form a a new code in "de"
jr c,dun ; br if eof node or physical end-of-file
push de ; push a copy of the new pred
call decode ; decode new pred
ld hl,entflg ; flag is "01" if "decode" made the entry
srl (hl) ; check (and zero) the flag
jr c,noentr ; don"t make the same entry twice!
ld hl,(lastpr) ; get old pred
ld a,(char) ; and suffix char generated from the new pred
call enterx ; make new table entry from those two
noentr: pop de ; get newest pred again (not that new anymore)
ld a,(fulflg) ; monitor the table full flag
or a ;
jr z,mainlp ; continue decoding & entering "till full
;................................
;
cp 0feh ; when this becomes "ff", we are done
jr nz,fastlp ; first it will become "fe", though. in that
inc a ; - case perf 1 more loop & change it to "ff"
ld (fulflg),a ;
jr mainlp ; one more!
;..............................................................................
;
fastlp: ld (lastpr),de ; table full loop similar to above ,except
call getcod ; - don"t bother checking table full flag
jr c,dun ; - call "entfil", not "enterx" (for possible
push de ; - code reassignment
call decode ; call to actually decode chars
ld hl,(lastpr) ; get old pred
ld a,(char) ; and suffix char generated from the new pred
call entfil ; possibly make new table entry from those two
pop de ;
jr fastlp ; continue in code reassignment mode
;
; *** end of main processing loop(s)
;______________________________________________________________________________
; come here when one of the special codes is encountered (we may not
; really be "dun"). actually, a null code should have been intercepted
; by the get12 routine, leaving only eof (actually done) or adaptive
; reset.
dun: ld a,e ; some kind of special code encountered
cp low(eofcod) ; actually done?
jr z,dundun ; br if do
cp low(rstcod) ; else better be reset (null was intercepted)
jr nz,fatbad ; file is invalid
;..............................................................................
; --- perf an adaptive reset ---
xor a ;
ld h,a ; reset entry# prior to table re-initialization
ld l,a
ld (entry),hl ;
ld (fulflg),a ; reset "table full" flag
call initb2 ; reset the entire table
ld a,9 ; reset the code length to "9"
ld (codlen),a ;
ld a,02h ; reset the target mask value accordingly
ld (trgmsk),a ;
ld de,nopred ; set pred to "nopred"
dec a ; 1st entry is always a special case (A=1)
ld (entflg),a ; (trick it to make no table entry)
jr mainlp ; and continue where we left off
;______________________________________________________________________________
;
dundun equ $ ; --- actually done, close things up ---
xor a
jp retccp ; else return to ccp (or warm boot)
;______________________________________________________________________________
; the following routine actually performs the decoding. the top sec-
; tion, "decode", flags the entry as "referenced". it then calls the
; recursive section below it, "decodr", to do the actual work.
decode: push de ; save code. the code provides us an immediate
ex de,hl ; - index into the main logical table
ld a,(tablhi) ; (add offset to beg of table, of course)
add a,h
ld h,a ;
set 5,(hl) ; set bit 5 of pred (hi) to flag entry as
pop de ; - "referenced" (ie not bumpable)
;..............................................................................
;
decodr equ $ ; decode and output the index supplied in "de"
ld iy,(stklim) ; stack overflow check as a safety precaution
add iy,sp ; (limit allows extra for this invocation lvl)
jp nc,stkovf ; br on overflow (shouldn"t happen)
push hl ; only "hl" need be saved
ld a,(tablhi) ; convert index in "de" to address in "hl"
add a,d
ld h,a ;
ld l,e ; address now in "hl"
ld a,(hl) ; make sure the entry exists
and 0dfh ; <
cp 80h ; (value for a vacant entry)
jr nz,ok1 ; br if so (normal case)
;................................
;
ld a,01h ; the "ugly" exception, wswsw
ld (entflg),a ; set flag so entry isn"t made twice
push hl ; save current stuff.
ld hl,(lastpr) ; get the last pred..
ld a,20h ; (setting this flag will flag the entry as
ld (ffflag),a ; - referenced,)
ld a,(char) ; get the last char
call enterx ; make an on the fly entry...
xor a ;
ld (ffflag),a ; put this back to normal
pop hl ; and presto...
;
ld a,(hl) ; it had better exist now!
cp 80h ;
jp z,fatbad ; *** else file is fatally invalid ***
;................................
ok1: ld d,(hl) ; normal code- get "pred" (hi)
right1 ; move to "pred" (lo)
ld e,(hl) ; get that. if msb of hi byte is set, val must
bit 7,d ; - be "ff" (nopred) because it isn"t "80h"
jr nz,term ; if so, branch. this terminates recursion.
res 5,d ; else clear flag bit & decode pred we found
call decodr ; decode and output the "pred" (recursive call)
right1 ; move pointer ahead to the "suffix" byte
ld a,(hl) ; get it
samabv: call send ; output the "suffix" byte
pop hl ; restore reg and return
ret
;
term: right1 ; move pointer ahead to the suffix byte
ld a,(hl) ; get it & save it. it is the 1st char of the
ld (char),a ; - decoded string, and will be used later to
jr samabv ; - attempt to make a new table entry.
; (rest is same as above)
;______________________________________________________________________________
;
; enter { <pred>, <suffix> } into the table, as defined in { hl, a }
;
enterx: push af ; save the suffix till we"re ready to enter it
push hl ; save pred, xferred to "de" just below
call figure ; puts result in "phyloc" only, affects nothing
pop de ; put pred in "de" (pushed as "hl" above)
ld hl,(entry) ; get next avail entry#
ld a,(tablhi) ; convert that to an address
add a,h
ld h,a ;
; entries are made here, but not normally flagged as "referenced" until
; the are received by "decode". until they are flagged as referenced,
; they are "bumpable", that is available for code reassignment. if
; "ffflag" is set to 20h, however, they will be flagged now. this only
; occurs during initialization (bumping an atomic entry would be most
; unfortunate) and when a wswsw string encounter initiates an emergency
; entry, despite the code never having been received by "decode".
ld a,(ffflag) ; normally zero, as described above
or d ;
ld (hl),a ; make the entry- pred (hi) first
right1 ; move to pred (lo) position
ld (hl),e ; put that in
right1 ; move to suffix position
pop af ; retrieve the suffix, saved on entry
ld (hl),a ; stick it in
ld hl,(entry) ; increment the entry# counter
inc hl ;
ld (entry),hl ;
inc hl ; see if a new code length is indicated. the
ld a,(trgmsk) ; - extra inc "hl" above is to account for
cp h ; - skew delays of uncruncher vs. cruncher
ret nz ; normally just return
add a,a ; change to a new code length
ld (trgmsk),a ; this will be the next target mask
ld a,(codlen) ; get the old code length, as a #of bits
inc a ; increment it, too
cp 13 ; check for overflow (12 bits is the max)
jr z,flgful ; if so, flag table as full
ld (codlen),a ; else this is the new code length
ret ;
;................................
;
flgful: ld a,0feh ; flag table as full
ld (fulflg),a ;
ret ;
;______________________________________________________________________________
;
; get the next code by stripping the appropriate #of bits off the input
; stream, based on the current code length "codlen". if the code is
; "null", don"t even return; just get another one. if the code is one
; of the other special codes, return with the carry flag set. "spare" is
; actually treated like a "null" for the time being, since it"s use has
; yet to be defined.
;
getcod: ld de,0000 ; init "shift register" to zero
ld a,(codlen) ; get current code length
ld b,a ; will be used as a loop counter
ld a,(csave) ; "leftover" bits
getlp:
add a,a ; shift out a bit
call z,ref ; refill when necessary
rl e ; shift in the bit shifted out
rl d ; likewise
djnz getlp ; loop for #of bits needed
ld (csave),a ; save "leftover" bits for next time
ld a,d ; if hi-byte = "01", we may have a special code
dec a ; set z if it was "1"
and a ; clr carry
ret nz ; rtn w/ clr carry if byte wasn"t "01"
;................................
;
ld a,e ; else further analysis necessary
cp 4 ; set carry on 100, 101, 102, 103
ret nc ; else code is normal, rtn with clr carry
cp low(nulcod) ; is it the "null" code?
jr z,getcod ; if so, just go get another code
cp low(sprcod) ; (treat the unimplemented "spare" like a null)
jr z,getcod ; as above
scf ; < rtn w/ carry set indicating special code
ret ; (presumably "eof" or "reset")
;______________________________________________________________________________
;
; routine to reload "a" with more bits from the input stream. note
; we pre-shift out the next bit, shifting in a "1" from the left.
; since the leftmost bit in the reg is a guaranteed "1", testing
; the zero stat of the accumulator later is a necessary and suf-
; ficient condition for determining that all the bits in the accum-
; ulator have been used up.
;
; the only things to be careful of is that the last bit is not used
; later, and that the bit now in the carry flag is used upon return
; from this subroutine. (this is the identical scheme used in
; usqfst. a exact complement to it is incorporated for shifting
; bits out in the crunch program).
;
ref: call getchr ; get the char
jr c,phyeof ; br if unexpected physical eof encountered
scf ; to shift in the "1" from the right
rla ; do that, shifting out a "real" bit
ret ; rtn (w/ that real bit in the carry flag)
;______________________________________________________________________________
;
phyeof:
jp uneof ; unexpected eof
;______________________________________________________________________________
;
; send character to the output buffer, plus related processing
send:
exx ; alt regs used for output processing
srl b ; if reg is "1", repeat flag is set
; (note, clears itself automatically)
jr c,repeat ; go perf the repeat
cp 90h ; else see if char is the repeat spec
jr z,setrpt ; br if so
ld c,a ; else nothing special- but always keep
exx ; back to normal regs
jp out ; else just output the char;
;..............................................................................
;
; set repeat flag; count value will come as the next byte. (note: don"t
; clobber c with the "90h"- it still has the prev character, the one to
; be repeated)
;
setrpt: inc b ; set flag
exx ; switch to primary regs & return.
ret
;..............................................................................
;
; repeat flag was previously set; current byte in a is a count value.
; a zero count is a special case which means send 90h itself. otherwise
; use b (was the flag) as a counter. the byte itself goes in a.
;
repeat: or a ; check for special case
jr z,snd90h ; jump if so
dec a ; compute "count-1"
ld b,a ; juggle registers
push bc ; the count and the char
ld b,0 ; zero the count in advance
exx ;
pop bc ;
again:
ld a,c ;
push bc ;
call out ; repeat b occurrences of byte in "c"
pop bc ;
djnz again ; leaves b, the rpt flag, 0 as desired
ret
;................................
;
snd90h: ld a,90h ; special case code to send the byte 90h
exx ;
jp out ;
;______________________________________________________________________________
;
; initialize the table to contain the 256 "atomic" entries-
; { "nopred", <char> }, for all values of <char> from 0 thru 255
initb2: call prese2 ; "pre-initializes" the table (mostly zeroes)
ld a,20h ;
ld (ffflag),a ; <
xor a ; start with a suffix of zero
ld hl,nopred ; pred for all 256 atomic entries
inilp: push hl ; <
push af ; <
call enterx ;
pop af ; <
pop hl ; <
inc a ; next suffix
jr nz,inilp ; loop 256 times
;..............................................................................
;
; now reserve the four reserved codes 100h - 103h (eof, reset, null, and
; spare. this is easily achieved by inserting values in the table which
; cannot possibly be matched, and insuring that they cannot be reas-
; signed. an occurrence of any of these codes is possible only when the
; cruncher explicitely outputs them for the special cases for which they
; are designated.
ld b,4 ; loop counter for the 4 reserved entries
rsrvlp: push bc ; <
ld hl,impred ; an "impossible" pred
xor a ; any old suffix will do
call enterx ; make the entry
pop bc ; <
djnz rsrvlp ; loop 4 times
xor a ; now restore this flag to its normal value
ld (ffflag),a ;
ret ;
;..............................................................................
;
; low level table preset called before initialization above. this routine
; presets the main table as follows: (see description of table elsewhere):
; column 1: 4096 x 80h, columns 2 and 3: 4096 x 00h
;
prese2:
ld hl,(table) ; beg of main table, 4096 rows x 3 columns
ld d,h ;
ld e,l
inc de
ld bc,1000h ;
ld (hl),80h ;
ldir ; put in 1000h "80h""s
ld (hl),c ; C = 0
ld b,high(2*1000h) ; " " "
ldir ; and 2000h more "00h""s
;..............................................................................
;
; the auxiliary physical translation table is 5003 rows, 2 columns
; (logically speaking). actually 5120 rows, 2 columns are allocated. all
; entries are initialized to 80h.
ld hl,(xlatbl) ; physical <--> logical xlation table
ld d,h ;
ld e,l
inc de
ld bc,2800h ; total entries = 1400h x 2
ld (hl),80h ;
push hl
ldir ;
pop hl
dec (hl)
ret ;
;______________________________________________________________________________
;
; figure out what physical location the cruncher put it"s entry by
; reproducing the hashing process. insert the entry# into the correspon-
; ding physical location in xlatbl.
figure: ld b,a ; < suffix supplied goes into b
call hash ; get initial hash value into "hl"
phylp: ld c,h ; c <-- extra copy of h
ld a,(hl) ; check if any entry exists at that location
cp 80h ; value for a vacant spot
jr z,ismt ; br if vacant
call nm ; else find next in chain
jr phylp ; and continue
;................................
;
ismt: ld de,(entry) ; get the logical entry#
ld (hl),d ; stick in hi-byte
ld a,h ; move "right1" for this table
add a,14h ;
ld h,a ;
ld (hl),e ; lo-byte goes there
ret ;
;................................
;
nm: ; no match yet... find next "link" in chain
ld de,(disp) ; secondary probe- add disp computed by "hash"
add hl,de ;
ld a,(xlatbh)
cp h
ret c
ret z
ld de,5003 ; else loop
add hl,de ;
ret ;
;______________________________________________________________________________
;
entfil: ; try to enter the pred/suffix in hl|a
ld b,a ;
ld a,0ffh ;
ld (avail+1),a ;
ld a,b ;
call hash ; get initial hash value into "hl"
;..............................................................................
;
phylp2: ld c,h ; c <-- extra copy of h
ld a,(hl) ; check if any entry exists at that location
cp 80h ;
jr z,makit ; end-of chain- make entry (elsewhere) if poss
ld a,(avail+1) ; got an entry yet?
inc a
jr nz,nxt1 ; if so, don"t bother with the below
;................................
push hl ; save physical table pointer
ld d,(hl) ; get entry#, hi
ld a,h ; }
add a,14h ; } right 1 for this table
ld h,a ; }
ld l,(hl) ; entry#, lo byte
ld a,(tablhi) ; convert to an addr in "hl"
add a,d
ld h,a
bit 5,(hl) ; see if entry is bumpable
jr nz,nxtone ; if not, try the next one
ld (avail),hl ; and save resulting entry# here for later use
nxtone: pop hl ; restore physical table pointer
nxt1: ; come here if "hl" wasn"t pushed yet
call nm ; find next "link" in chain
jr phylp2 ; and continue
;______________________________________________________________________________
;
; reassign the entry pointed to by "avail", if any. re-define the "last
; pred entered" and "last suffix" variables.
;
makit: ld hl,(avail) ; get "avail"
ld a,h ;
inc a ; "ff" means no candidate entry was found
ret z ; so forget it
ld de,(lastpr) ; else redefine the "last pred entered" var
ld a,(char) ; as well as the "last suffix entered"
ld b,a ; put suffix here, we need to use "a"
ld (hl),d ; actually make the entry
right1 ;
ld (hl),e ; [pred(lo)]
right1 ;
ld (hl),b ; [suffix]
ret ; done
;------------------------------------------------------------------------------
;
; for additional details about the hashing algorithm, see crunch.
;
hash:
ld e,l ; save so low nybble of pred can be used below
add hl,hl ;
add hl,hl ;
add hl,hl ;
add hl,hl ; shift whole pred value left 4 bits
xor h ; xor hi-byte of that with suffix
ld l,a ; goes there as lo-byte of result
ld a,e ; get pred(lo) saved above
and 0fh ; want only low nybble of that
ld h,a
ld a,(xlatbh) ; convenient time to add in table offset
add a,h
ld h,a ; goes here as hi-byte of result
inc hl ; except add one. this eliminates poss. of 0.
push hl ; save hash val for return
ld de,(hasher) ; compute displacement value, - (5003-hash)
add hl,de ; (displacement has table offset removed again)
ld (disp),hl ; secondary hashing value, a negative number.
pop hl ; get back orig hash address
ret ; and return it
;______________________________________________________________________________
;
; (re-)initialize all necessary ram locs. called once for each file to be
; processed. this routine gets its info from an initialization block called
; "shadow", which is copied into the working memory.
;
intram: ld hl,shadow ; contains a copy of all relevant init values
ld de,ram ; target
ld bc,eoshad-shadow
ldir ; do it
ret
membuf: call codend
ld (table),hl
ld c,l
ld b,high(3*1000h) ; C = 0
add hl,bc
ld d,h ; Copy of eotbl pointer to DE
ld e,l
ld (eotbl),hl
ld b,high(2*1400h) ; C = 0
add hl,bc
ld (exlatb),hl
ld b,high(800h) ; C = 0
push hl ; Stack exlatb value
add hl,bc
ld (topstk),hl
ld hl,-5003 ; Compute "-5003-eotbl"
sbc hl,de ; Carry had better be clear....
pop de ; Get back exlatb
ld (hasher),hl ; Store "-5003-eotbl" for "hash:"
ld h,c ; C = 0, carry still clear
ld l,c
sbc hl,de ; Negate exlatb
ld (stklim),hl ; Stow as stack limit
ret ;
;______________________________________________________________________________
;
shadow equ $ ; (for description, see immediately below)
;
db 00 ; "fulflg"
dw nopred ; "lastpr"
db 01h ; "entflg"
dw 0000h ; "entry"
db 80h ; "csave"
db 09 ; "codlen"
db 02h ; "trgmsk"
eoshad equ $
;______________________________________________________________________________
;
; End LH-UNCR.LIB
;
;========================================================================