home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
mbug
/
mbug078.arc
/
UNCR.ZZ0
/
UNCR.Z80
Wrap
Text File
|
1979-12-31
|
38KB
|
1,083 lines
;************************************************************************
;* *
;* UNCRunch v2.3 *
;* 15 Nov 1986 *
;* - Steven Greenberg *
;************************************************************************
.Z80
.SALL
TITLE 'UNCRrunch v2.3'
EXTRN UNCR1,PARSEU
PUBLIC GETBYT,OUT
; +---------------------------------------------------------------------+
; | This source code, as well as any object code created from it, are |
; | Copywrite (C) Steven Greenberg, 15 November 1986. May be reproduced |
; | for non-profit use only. Public release of modifications strictly |
; | prohibited without expressed consent of the author. |
; +---------------------------------------------------------------------+
; This core of this source code is pretty much unchanged since the orig-
; conception and refinement of CRUNCH v2.0. Though attention was made in
; selecting an algorithm which could be implemented in a relatively ef-
; fecient manner, and some care was taken to keep the 'innermost' loops
; fairly streamlined, there is no doubt room for improvement in terms of
; the optimally efficient implementation. This simply means that future
; releases may run even faster than the current one.
; This code is reasonably fully documented, though no attempt has been
; made to explain every concept in full detail. Anyway, here it is.
CSEG
;==============================================================================
;
MEMPAG EQU 1700H ; <=== SET! [See comment near end of program]
;
;==============================================================================
REV EQU 23H ; Program revision level
SIGREV EQU 20H ; "significant" revision level (compatibility)
NOPRED EQU 0FFFFH ; "no predecessor"
IMPRED EQU 07FFFH ; Pred that can't be matched or bumped
; --- reserved codes ---
EOFCOD EQU 100H ; EOF code
RSTCOD EQU 101H ; Adaptive reset code
NULCOD EQU 102H ; Null code
SPRCOD EQU 103H ; Spare code
;--- Ascii equates ---
;
CTRLC EQU 03H ; ^C
BELL EQU 07H ; Beep
BS EQU 08H ; Backspace
LF EQU 0AH ; Linefeed
CR EQU 0DH ; Carriage return
;--- CP/M address equates ---
;
DFCB EQU 5CH ; Default FCB #1
DFCB2 EQU 6CH ; Default FCB #2
DDMA EQU 80H ; Default dma address
BDOS EQU 0005H ; Bdos entrypoint
;--- BDOS function equates ---
;
CONIN EQU 1 ; Input a character from the console
CONOUT EQU 2 ; Output single char to console
DIRCON EQU 6 ; Direct console i/o
PRTSTR EQU 9 ; Print string to console
GETVER EQU 12 ; Get cp/m version#
OPEN EQU 15 ; Open file
CLOSE EQU 16 ; Close file
SFIRST EQU 17 ; Search for first file
SNEXT EQU 18 ; Search for next file
ERASE EQU 19 ; Erase file
READ EQU 20 ; Read file (sequential)
WRITE EQU 21 ; Write file (sequential)
MAKE EQU 22 ; Make file
GETDSK EQU 25 ; Get currently logged drive
SETDMA EQU 26 ; Set dma address
GSUSER EQU 32 ; Get/set user code
SETMS EQU 44 ; Set multi-sector count (cp/m+ only)
;______________________________________________________________________________
;
; Macros to facilitate "horizontal" movement through the table.
; See "Table structure" comment near "initbl" for more information.
;
RIGHT1 MACRO
LD A,H ; }
ADD A,10H ; } move "right" one column (same row)
LD H,A ; }
ENDM
;------------------------------------------------------------------------------
START: JP STRT ; <--- entry
;------------------------------------------------------------------------------
DB 'Z3ENV',01H ; ZCPR3 environment descriptor
Z3ED: DB 00H,00H
;----------------------------------------------
SPFLG: DB 0 ; Spare flag
Z3FLG: DB 0 ; ZCPR flag
INSREV: DB 23H ; Program rev for install program reference
QUIFL: DB 0 ; Quiet mode flag
NPROFL: DB 0 ; No prompt before overwrite flag
TRBOFL: DB 0 ; Defeat multi-sector i/o flag
CNFRFL: DB 0 ; Confirm every file flag
WRMFLG: DB 0 ; Warm boot flag
BIGFLG: DB 0 ; Override larger file question flag
MAXDRV: DB 0FFH ; Max drive
MAXUSR: DB 0FFH ; Max user
UFLAG: DB 0 ; Flags this as the uncruncher, for com subrs
UFLAG2: DB 01H ; Controls screen update rate
;-=*=-=*=-=*==*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-
CPYWRT: DEFB 'Copyright (c) Steven Greenberg 11/15/86 201-670-8724. '
DEFB 'May be reproduced for non-profit use only.'
;-=*=-=*=-=*==*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-=*=-
STRT: SUB A ; B. Freeds Z-80 test
JP PO,Z80 ;
LD DE,WRNGUP ; "Program requires Z-80 processor"
JP MESS80 ; No frills exit w/ message
Z80: LD (OLDSTK),SP ; Save os's stack
LD SP,TOPSTK ; Set local stack
CALL STRTUP ; Does a lot of stuff
;______________________________________________________________________________
;
; ***** Re-enter here for each matching file *****
;
NXTFIL: LD SP,TOPSTK ; This shouldn't be necessary, but...
LD DE,INFCB ; Input file fcb
CALL CLRFCB ; Init it to blanks and zeroes
INC DE ; Leave "DE" pointing at infcb+1 for below
LD HL,(BUFPTR) ; Pntr to name of next file from expansion bfr
PUSH HL ; Save a copy
INC HL ; Skip past user# or whatever
LD BC,11 ; Filename character count
LDIR ; Put next file name into input fcb
POP HL ; Get back filename buffer pointer
LD DE,16 ; Filenames are 16 bytes apart in buffer
ADD HL,DE ; So pre-incr to next name
LD (BUFPTR),HL ; Save new pointer for next file, if any
CALL INTRAM ; Init all necessary ram locs
LD A,80H ; (init'd by "INTRAM", but must be init'd
LD (CSAVE),A ; - differently for crunch & uncrunch)
CALL OPNIN ; Open the input file
JR NC,INOK ; If ok
LD DE,ERR1 ; Else, "Input file not found"
JP SKIP1 ; (couldn't find it- this is unusual)
;==============================================================================
;
; If we got here, the input file is open. The output file is not
;
INOK: LD DE,OUTFCB ; Now for the output fcb
CALL CLRFCB ; Clear it
INC DE ; Leave "DE" pointing at filename area
LD A,' ' ; For proper alignment
CALL TYPE ;
LD HL,INFCB ; Print input filename to console
CALL PRNFIL ;
XOR A ; Init 1st char of date stamp bfr to zero
LD (STAMP),A ; (nulls the buffer; zero is the eof char)
CALL GETCHR ; Get a char from the input stream
JR NC,NTMT ; If carry set on first byte, file is empty
LD DE,ERR0 ; "File Empty"
JP SKIP2 ;
NTMT: CP 76H ; Check for crunched file header "76FE"
JR NZ,NCRNCH ; Br if not
CALL GETCHR ;
CP 0FEH ;
JR Z,YCRNCH ;
NCRNCH: LD DE,ERR43 ; "not a crunched file."
JP SKIP2 ; Skip file and continue
YCRNCH: LD DE,OUTFCB+1 ; Where output filename will be copied to
LD B,12 ; Loop limiter (11 char filename + ".")
EATLP: CALL GETCHR ; Get next char
OR A ; A zero byte indicates end of filename
JR Z,ATEIT ; Br when that is encountered
AND 7FH ; Force valid ascii (should be already)
CP '.' ; Check for name / ext division char
JR Z,ISDOT ; Br when encountered
LD (DE),A ; Else copy char to output fcb
INC DE ; And incr that pointer
DJNZ EATLP ; Continue, but not past end of filename
JR IGNORE ; If no 0 detected, ignore following info
; When "." is encountered, skip to the file extension bytes of the
; output FCB. Any remaining non-extension bytes were init'd to blank).
; Do not copy the "." to the output FCB.
ISDOT: LD DE,OUTFCB+9 ; Skip...
LD B,3 ; Update loop limiter counter
JR EATLP ; And continue
;................................
;
IGNORE: CALL GETCHR ; Loop absorbs extraneous header info
JR C,NCRNCH ; Circumvent possible hangup (eof before 0)
OR A ; Wait for the terminating zero
JR Z,ATEIT ; If terminating zero is reached
CP '[' ; Else check for date stamp bof char
JR NZ,IGNORE ; Other chars are extraneous at this point
;................................
;
LD DE,STAMP ; Start copying file stamp info to this buffer
JR ENTSLP ;
;
STMPLP: CALL GETCHR ; Get a char
JR C,NCRNCH ; Circumvent hangup
ENTSLP: LD (DE),A ; Put char in dest
INC DE ; Incr dest pntr
OR A ;
JR NZ,STMPLP ; Loop till zero is reached
;................................
;
ATEIT: CALL GETCHR ; Get revision level, do nothing with it
CALL GETCHR ; Get significant revision level
CP SIGREV ; Compare to this prog
JP C,OLDTYP ; Br if old type1x crunched file
JR Z,SIGOK ; If equal, ok, else...
LD DE,ERR5 ; "Can't uncrunch that file. newer revision of
JP SKIP2 ; - this program needed" or some such remark
SIGOK: CALL GETCHR ; Get checksum flag
LD (CKSMFL),A ; Put it there
CALL GETCHR ; Get spare byte, do nothing with it
CALL OPNOUT ; Open output file & type "---> <filename>"
PUSH AF ; Save stat from above
CALL PRNID ; Type any embedded date stamp info also
POP AF ;
JP C,SKIP2A ; If user wants to skip it
;==============================================================================
;
; Now both files are open. Eventually either both will be closed, or the
; input file will be closed and the output deleted.
LD A,(QUIFM) ; Skip column headings if in quiet mode
OR A ;
JR NZ,QUIET1 ;
LD DE,HEADNG ; Type all the "in / out ca cr" stuff
CALL MESAGE ;
QUIET1: CALL INITB2 ; Initialize the lzw table
LD DE,NOPRED ; Init to "NOPRED" (null value)
PAGE
;______________________________________________________________________________
;
; *** 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"
JP 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
JP 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)
JP NZ,FATBAD ; File is invalid
;..............................................................................
; --- perf an adaptive reset ---
LD HL,0000 ; Reset entry# prior to table re-initialization
LD (ENTRY),HL ;
LD (TTOTAL),HL ; Reset "codes reassigned"
XOR A ;
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"
LD A,1 ; 1st entry is always a special case
LD (ENTFLG),A ; (trick it to make no table entry)
JP MAINLP ; And continue where we left off
;______________________________________________________________________________
;
DUNDUN EQU $ ; --- actually done, close things up ---
CALL GETCHR ; Get the checksum, always next
LD E,A ;
CALL GETCHR ; Get checksum (hi-byte)
LD D,A ; Checksum (from input file) now in "DE"
LD HL,(CHKSUM) ; Checksum (as computed)
LD A,(CKSMFL) ; Checksum override flag (not currently used)
AND A ; Check flag, also clear carry for below
JR NZ,CHKSOK ; If flag > 0, don't check checksum
SBC HL,DE ; Else check by subtraction
JR Z,CHKSOK ; Br if match
LD DE,BADCHK ; Bad checksum, issue warning
CALL MESAGE ;
;................................
;
CHKSOK: CALL DONE ; Write out remaining output buffer
CLOSE2: CALL CLSOUT ; Close output file
CALL CLSIN ; Close input file
NEXT: CALL SKIPIT ; Skip to next file
JP NZ,NXTFIL ; If multiple files were specified
JP RETCCP ; Else return to ccp (or warm boot)
;______________________________________________________________________________
;................................
; Entry if neither in nor output files open yet
SKIP1: CALL MESAGE ;
SKIP1A: JR NEXT ; (entry here if no error text desired)
;................................
; Entry here if input file open only
SKIP2: CALL MESAGE ;
SKIP2A: CALL CLSIN ; (entry here for no message)
JR NEXT ;
;................................
; Entry here if in & output files to be closed
SKIP3: CALL MESAGE ;
SKIP3A: JR CLOSE2 ; (rest is same as above)
;................................
; Entry here to erase output & close input file
SKIP4: CALL MESAGE ;
SKIP4A: CALL CLSOUT ; (entry here for no message)
LD DE,OUTFCB ; Erase ouptut file, already started
LD C,ERASE ;
CALL BDOSAV ;
CALL CLSIN ; Close input file as well
JR NEXT ;
;...............................;
;______________________________________________________________________________
;
; 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,H ; (add offset to beg of table, of course)
ADD A,TABLHI ;
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,D ; Convert index in "DE" to address in "HL"
ADD A,TABLHI ;
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 ;
JR 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)
;______________________________________________________________________________
;
FATBAD: LD DE,ERR4 ; "Invalid crunched file"
JP CHKSOK ; Write out whatever we have, then next file
; (stack gets reloaded before next file)
;______________________________________________________________________________
;
; 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,H ; Convert that to an address
ADD A,TABLHI ;
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
SLA 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: SLA 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: LD SP,TOPSTK ; "emergency exit"- reset stack
LD DE,UNXEOF ; "unexpected eof."
CALL MESAGE ;
JP CHKSOK ; Write out what we have, then continue
;______________________________________________________________________________
;
; 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
CALL OUTC ; Else just output the char;
RET ;
;..............................................................................
;
; 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 OUTC ; 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 ;
CALL OUTC ;
RET ;
;______________________________________________________________________________
;
; Send the char in "A" to the output buffer, & add it to the running checksum
;
OUT EQU $ ;
OUTC: CALL OUTB ; Output it
CALL CKSUM ; Add to the checksum
RET ;
;______________________________________________________________________________
;
; Convert the middle letter of the filename extension to a "Z" iff it is "?"
;
FIXFCB: LD HL,INFCB+10 ; Point to middle letter of extension
LD A,'?' ;
CP (HL) ; See if it is ambiguous
RET NZ ; If not, we'll allow any letter (rev v1.2)
LD (HL),'Z' ; Else force it to "Z". this is mainly so
RET ; The command line uncr *.* will work well
;______________________________________________________________________________
;
; Type the stuff in the "stamp" buffer to the console.
;
PRNID: LD DE,SPACE3 ; Precede w/ 3 spaces
CALL MESAG2 ;
LD HL,STAMP ; Point to that buffer
LD B,40 ; Practical limit of 40 char "stamp"
PRFILP: LD A,(HL) ; Get a char
OR A ; Zero terminates the field
RET Z ; Return when encountered
CALL TYPE ; Else type the char
CP ']' ; This terminates stamp also (after typing it)
RET Z ; Return if that happened
INC HL ; Else incr pointer
DJNZ PRFILP ; And loop
RET ;
;______________________________________________________________________________
;
STKOVF: LD DE,ERR6 ; "*** stack overflow ***"
LD SP,TOPSTK ; Shouldn't happen, but we're still in control
JP FATBAD ; Reset & continue w/ next file, if any
;______________________________________________________________________________
;
; 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 DE,TABLE+1 ;
LD BC,1000H ;
LD (HL),80H ;
LDIR ; Put in 1000H "80H"'s
LD (HL),0 ;
LD BC,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 DE,XLATBL+1 ;
LD BC,2800H ; Total entries = 1400H x 2
LD (HL),80H ;
LDIR ;
LD A,7FH ; <
LD (XLATBL+0),A ; <
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 EQU $ ; No match yet... find next "link" in chain
LD DE,(DISP) ; Secondary probe- add DISP computed by "HASH"
ADD HL,DE ;
LD A,H ;
CP XLATBH ; Check for loop around
JR NC,NC9 ; Br if not
LD DE,5003 ; Else loop
ADD HL,DE ;
NC9: RET ;
;______________________________________________________________________________
;
ENTFIL EQU $ ; 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?
CP 0FFH ;
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,D
ADD A,TABLHI ; Convert to an addr in "HL"
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 ;
CP 0FFH ; "FF" means no candidate entry was found
JR Z,ENTRTS ; 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"
PUSH HL ; Increase the "codes reassigned" total
LD HL,(TTOTAL) ;
INC HL ;
LD (TTOTAL),HL ;
POP HL ;
LD (HL),D ; Actually make the entry
RIGHT1 ;
LD (HL),E ; [pred(lo)]
RIGHT1 ;
LD (HL),B ; [suffix]
ENTRTS: RET ; Done
;------------------------------------------------------------------------------
;
; For additional details about the hashing algorithm, see CRUNCH.
;
HASH EQU $ ;
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
ADD A,XLATBH ; Convenient time to add in table offset
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,-5003-XLATBL ; 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
;==============================================================================
;
; For old style (v1.x) type crunched files, simply call the "UNCR1" module.
; All I/O will be done by this program- UNCR1 will get and feed data thru
; calls to entrypoints "GETBYT" and "OUT".
;
OLDTYP: LD A,0FFH ; Flag this as an old style uncrunch
LD (OLDFLG),A ;
CALL GETCHR ; Get checksum flag
LD (CKSMFL),A ; Put it there
CALL GETCHR ; Get spare byte, do nothing with it
CALL OPNOUT ; Open output file & type "---> <filename>"
PUSH AF ; Save stat from abv call
CALL PRNID ; Type any embedded date stamp info also
POP AF ; Get back carry stat
JP C,SKIP2A ; If user wants to skip it
CALL CRLF ; More aesthetics
EXX ; Go back to start of file (already read some)
LD HL,IBUF ;
EXX ;
FIX21: LD HL,SECNT ; Bugger up the sector count since we just
INC (HL) ; - reset the input pointer
LD HL,(INCTR) ; [rev 2.1 fix]
DEC HL ; Decr "inctr" for same reason
LD (INCTR),HL ;
LD A,'0' ; [rev 2.1 fix]
LD (PROGBF+5),A ; Reset ascii display to zero
LD HL,TABLE ; Point to large data area for UNCR1
CALL UNCR1 ; Uncrunch the whole thing
JP NC,DUNDUN ; A "normal" return
JP FATBAD ; Any other error falls under "invalid.."
;______________________________________________________________________________
;
PRCSTM: LD DE,PRSER1 ; "invalid argument" (no stamps allowed)
JP FATALU ;
;______________________________________________________________________________
VUNITS EQU (REV/16)+'0' ; Version, units dig, in ascii
VTNTHS EQU (REV AND 0FH)+'0' ; Version, tenths dig, in ascii
INTRO: DEFB 'GEL Uncruncher v',VUNITS,'.',VTNTHS,CR,LF,CR,LF,'$'
BADCHK: DEFB 'Checksum error detected.',CR,LF,'$'
ERR43: DEFB 'Not a Crunched File.',CR,LF,'$'
ERR4: DEFB 'Invalid Crunched File.',CR,LF,'$'
ERR5: DEFB 'File requires newer program rev.',CR,LF,'$'
ERR6: DEFB 'Stack Overflow.',CR,LF,'$'
ERR0: DEFB 'File Empty.$'
ERR1: DEFB 'Input file not found.',CR,LF,'$'
ERR2A: DEFB 'File open error.$'
ERR2B: DEFB 'Disk Full.$'
ERR2C: DEFB 'Output error.$'
ERR3: DEFB 'Too many files.$'
LAKMEM: DEFB 'Not enough memory.$'
WRNGUP: DEFB 'Prog req''s Z-80.$'
ARROW: DEFB ' ---> $'
ARROW2: DEFB ' --->$'
ARROW3: DEFB ' ===> $'
PERCNT: DEFB '% $'
SPACE3: DEFB ' $'
SPCPAR: DEFB ' ($'
UNXEOF: DEFB 'Unexpected EOF.',CR,LF,'$'
USAGE:
DEFB ' filename Quiet Confirm',CR,LF
DEFB ' / / /',CR,LF
DEFB 'Usage: UNCR {du:}<afn> {du:} { /Q | /V | /C}',CR,LF
DEFB ' \ \ \',CR,LF
DEFB ' source destination Verbose'
DEFB CR,LF,CR,LF,CR,LF
DEFB ' Both "du:" are of form DU:, UD:, D:, or U:',CR,LF
DEFB ' Letter options are preceded by a space/slash.'
DEFB CR,LF,CR,LF
DEFB ' Everything is optional except filename.',CR,LF,'$'
;______________________________________________________________________________
INCLUDE COMMON.LIB
;______________________________________________________________________________
;
;Additional misc ram locs which need not be initialized, or are init-
;ialized by the routines which use them.
;
CKSMFL: DS 1 ; Skip checksum if flag non-zero
BUFPTR: DS 2 ; }
COUNT: DS 2 ; } for wildcard expansion
NMBFLS: DS 2 ; }
OLDSTK: DS 2 ; Ccp's stack val saved here on entry
CHAR: DS 1 ; Last char of the previously decoded string
AVAIL: DS 2 ; *
FFFLAG: DS 1 ; *
DISP: DS 2 ;
DIVISR: DS 2 ;
BDOSHL: DS 2 ;
CPM3FL: DS 1 ;
CSAVE: DS 1 ;
CURUSR: DS 1 ;
USERNO: DS 1 ;
DEFDRV: DS 1 ;
;................................
STKSZ EQU 8 ; Minimum stack size (pages)
IBUFSZ EQU 8 ; Input buffer size (pages)
OBUFSZ EQU 8 ; Output buffer size (pages)
; "MAXFLS" is buffer size (in files) for wildcard expansions. Room for
; this many files will be allocated.
MAXFLS EQU 128
;==============================================================================
;
; ===> All tables will begin at "MEMPAG", defined at the top of the pro-
; gram. This should be set to a page aligned value (ie address that ends in
; "00") which is ABOVE the end all program and data segments. You may have
; to do one test link to determine the proper value (changing "MEMPAG" will
; not change the length of the segments on the subsequent link).
;
; "MEMPAG" is defined at the beginning of this program to remind you to set
; it properly. If you set it higher than necessary, there will be no negative
; effect other than an increase in the TPA req'd to run the program. If you
; set it too low, you will be in big trouble. The value must be set manually
; because most linkers cannot resolve an "and", "shift", or "hi" byte
; extraction at link time to determine the page boundary.
;
;==============================================================================
FNBUFF EQU MEMPAG ; (= beg of wildcard expansion buffer)
ENDFNB EQU FNBUFF+(16*MAXFLS) ; End of expansion buffer
IBUF EQU ENDFNB ; (= beg of input buffer)
EIBUF EQU IBUF+(IBUFSZ*256) ; End of input buffer
OBUF EQU EIBUF ; (= beg of output buffer)
EOBUF EQU OBUF+(OBUFSZ*256) ; End of output buffer
TABLE EQU EOBUF ; (= beg of table)
EOTBL EQU TABLE+(3*1000H) ; End of table
XLATBL EQU EOTBL ;
EXLATB EQU XLATBL+(2*1400H) ;
STAMP EQU EXLATB ; 80h bytes for "stamp" buffer
ESTAMP EQU STAMP+80H ;
SAFETY EQU ESTAMP ; Safety region- beyond legal bottom of stack
BOTSTK EQU SAFETY+80H ; "legal" stack limit
TOPSTK EQU EXLATB+(STKSZ*256) ; Top of stack
ENDALL EQU TOPSTK+1 ; End of everything
;..............................................................................
STKLIM EQU 0-BOTSTK ; Negation of "botstk", used as safety check
IBUFHI EQU HIGH IBUF ; Input bfr addr, hi byte (lo byte = 0)
EIBFHI EQU HIGH EIBUF ; End of input bfr addr, hi byte, likewise
OBUFHI EQU HIGH OBUF ; Output bfr addr, hi byte likewise
EOBFHI EQU HIGH EOBUF ; End of output buffer, hi byte, likewise
TABLHI EQU HIGH TABLE ; Beg of table, hi byte, likewise
ETBLHI EQU HIGH EOTBL ; End of table, hi byte, likewise
XLATBH EQU HIGH XLATBL ; *
EFNBHI EQU HIGH ENDFNB ; End of expansion buffer, likewise
ENDHI EQU HIGH ENDALL ; End of everything, likewise
END