home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
ENTERPRS
/
CPM
/
UTILS
/
S
/
USQFST20.ARK
/
UF20.MAC
< prev
Wrap
Text File
|
1987-01-20
|
38KB
|
1,090 lines
; ***********************************************************************
; * *
; * Fast Unsqueezer, v2.0 *
; * 28 June 1986 *
; * Steven Greenberg *
; ***********************************************************************
.Z80
TITLE 'Fast Unsqueezer, v2.0'
ASEG ; (see note concerning aseg / cseg controversy
ORG 100H ; at end of program where "codtbl" is defined.)
; *** CSEG [out for now]
; v2.0 - 06/28/86
;
; Supports optional destination as well as source drive spec-
; ifications. At Bob Freed's hint, the maximum size of "codtbl"
; has been reduced to 256 (see clarification at "SQUEEZED FILE
; FORMAT"; Bytes 5+n, 6+n). This reduces the required TPA size by
; 4k. Version also gracefully terminates (with an "Unexpected
; EOF" message) should the squeezed input file be truncated or
; otherwise badly damaged. Previous versions would continuously
; decode the final record if faced with this condition. All output
; filenames are automatically converted from lower to upper case
; (Where do these squeezed files with embedded lower-case filenames
; come from, anyway?). Includes additional filename processing to
; limit filename to a maximum of 8 characters plus three extension
; characters (ie, in no case will bytes be written beyond the
; filename area of the output FCB. v2.0 also gives usage (command
; line format) if called with no arguments. Thanks to H. Goldstein
; for some of these suggestions.
; - SGG
;
; v1.9 - 04/02/86
;
; Program now zeroes the MS bits of the filename chars before
; putting them into the output FCB (as it turns out, these bits may
; or may not be guaranteed clear, depending on the program which
; performed the squeeze). This eliminates a possible problem if the
; squeezed file was Read-Only. Fixed a glitch which would cause
; unnecessary additional sectors to be appended to the output file
; in the unlikely event its length was an exact multiple of the
; output buffer. No longer prints the program name to the console
; so you can name your .COM file as you please. Restored some doc-
; umentation that inadvertently got removed in v1.8. Put 35/36ths
; of the output FCB after the end of the program since it is now
; fully initialized by "ZERFCB". Added code to type source file-
; name(s) as well as output filename(s) to console (particularly
; useful in wildcard mode).
; - SGG
;
; v1.8 - 03/04/86
;
; The search-and-optional-erase of the destination file was re-
; moved in favor of a "blind" delete file call. The open-file call
; immediately after the make-file has been eliminated. The TPA size
; check has been changed to take into account that versions 1.1+
; require the CCP to remain resident. Slightly increased the input
; buffer size for this assembly prog now req's memory up to to an
; even 9000H. If anyone has memory problems, change the buffer size
; equ's at the end ("IBUFSZ","OBUFSZ",or "MAXFLS"). I made Sigi's
; wildcard expansion buffer size settable at assembly time & is
; checked for overflow at run time.
; - SGG
;
; v1.7 - 03/01/86
; Found a pre-stack-save Z80 test that I THINK everyone can agree
; on (it also works for HD64180s & NSC800s), went back to Z80 code
; for all stack pointer moves, converted Sigi's wildcard sorter to
; Zilog mnemonics so non-M80 folks can assemble it and threw in
; some JRs here and there. Restored the disk write error message
; routine from another(!) version 1.3 (not documented here) done
; done by Bill Duerr.
; - Bruce Morgen [bm]
;
; v1.6 - 02/18/86
; Added wildcard support and suppress non-*?Q?-files completely.
; Moved code where it belongs (CSEG)!
; - Sigi Kluger
;
; v1.5 - 02/18/86
; One of the changes made in v1.1 has been reversed- Apparently
; the BIOS for some machines (Kaypro & Osborne?) clobber the alter-
; nate registers. Thanks to Keith Peterson for info on this.
;
; No more tricks at "FATAL" or OS stack size assumptions Built
; CR/LF into the message routine; removed extraneous OR A's after
; INC A's from the modified file open success tests.
;
; Most of the changes are in the form of improved documentation,
; & some code segments have been shifted in position.
;
; - Steven Greenberg
;
; v1.4 - 02/17/86 [intermediate unreleased version]
;
; v1.3 - 02/17/86
; Changed stack save and load routines to the old 8080 method
; since they must run even if you try to use an 8080 processor and
; get the error message.
; - D. Jewett, III [dj]
;
; v1.2 - 02/15/86
; Fixed source code so it could used with the M80 assembler which
; is owned by more CP/M users than all other Z80 assemblers com-
; bined (and by a wide margin at that). It can still be assembled
; with the SLR Z80ASM for which it was originally written.
; - Irv Hoff
;
; v1.1 - 02/09/86
; Various small changes to the CP/M interface aspect of the code.
; Fixed the test for file-open success (BDOS won't always return a
; zero). Moved local stack to Copyright message and saved OS stack
; to beginning of same, eliminating warm boot-on-exit (the algor-
; ithm is so fast that the CCP reload often took as much time as
; the unsqueezing itself). Took out now unnecessary stack trick at
; FATAL: and slimmed down on PUSH/POPs at BDOSAV: (BDOS does not
; use Z80 specific registers).
; - Bruce Morgen
; _________________________________________________________________
; (FOR CP/M 2.0+, Z-80 only)
;
; v1.0 - 01/10/86
;
; This program unsqueezes standard (Greenlaw style) squeezed
; programs. Simply specify the filename to be unsqueezed on the
; command line; the result filename will be generated automatically
; and written to the default drive. The difference between this un-
; squeezer and others lies in its unique architecture. This results
; in speed increases ranging from a factor of about 2 (compared to
; fastest assembly coded programs previously available) on up to
; factors greater than 5 (compared to standard C coded versions).
;
; The architecture, very briefly, is as follows. First the "dic-
; tionary" info contained in the squeezed file is "compiled" into a
; decoding program, which sits in memory just above the .COM file.
; Since each squeezed file generates its own unique program, the
; program is a highly efficient subroutine for performing the ac-
; tual unsqueeze operation.
;
; Thanks to Jeff D. Wilson for many worthwhile suggestions.
;
; - Steven Greenberg
; ________________________________________________________________________
; SQUEEZED FILE FORMAT: Here is the format of the mysterious
; squeezed file, originally defined by Richard Greenlaw around '81.
;
; BYTES 0,1: 76,FF always. Identifies the file as "squeezed".
;
; BYTES 2,3: 16-bit checksum, lo-byte first. It is the sum of
; all bytes as they appear in the GENERATED file, modulo 2^16.
;
; BYTES 4,... 4+n: Variable length filename field, containing the
; name of the original file before it was squeezed. The end of
; this field is defined by a zero byte. The "." char separating
; the filename from its .EXT is included as as a char, and its
; position may float.
;
; BYTE 5+n: Zero, as mentioned above
;
; BYTE 6+n,7+n: The length of the "dictionary", which follows.
; The value is the number "word pairs" (described below), and has a
; maximum value of 0100H. The length of the dictionary in bytes is
; four times this value (2 bytes/word x 2 words/pair).
;
; The dictionary is a binary tree based structure. It con-
; tains "internal" and "terminal" (or leaf) nodes. The maximum #of
; codes to be represented is 257 (256 possible bytes plus a special
; EOF code). This may require as many as 257 leaf nodes plus 256
; "internal" nodes (513 total). The standard squeezed file dic-
; tionary format represents the tree by using up to 256 pairs of 2-
; byte words; each word within the pair could be a pointer to an-
; other node or a could be a "leaf" word which is terminal and
; posseses a value. In most of the following discussion, the whole
; pair is called a "node". A worst case situation takes 256 word
; pair nodes, corresponding to 256 "table entries". These will be
; described in more detail later. Pre- v2.0 versions of this
; program allocated twice this amount, with the extra serving no
; particular purpose.
;
; BYTES 8+n,... (8+n)+(4*l)-1: Dictionary, as described above.
;
; REMAINING BYTES: Squeezed code to be thought of as a cont-
; inuous bit stream where byte boundaries are insignificant. Chars
; are defined by codes of varying lengths of bits (The more freq-
; uent the character the shorter the code). The bit stream is LS
; bit thru MS bit, then move on to next byte. The end of the file
; cannot be detected until the special end-of-file code "SPEOF" is
; decoded.
;
; ADDITIONAL NOTE: Multiple contiguous occurrences of the iden-
; tical character are encoded by the 3-byte sequence:
;
; <char> 90H <count>
;
; When "CHAR" is first decoded, it is outputted normally. When
; the 90H is decoded, a flag is set, the next char is decoded for
; a count val, and the first char is repeated an additional "count-
; 1" times. Encoding the byte 90H itself is achieved by the seq-
; uence 90H 00H i.e., when the count is 0 send a 90H, the byte
; before the 90H was irrelevant.
;______________________________________________________________________________
; Opcode equates
JPOP EQU 0C3H ; Opcode for "JP" instruction
; ASCII equates
CR EQU 0DH
LF EQU 0AH
; CP/M address equates
DFCB EQU 5CH ; Default file control block
DFCB2 EQU 6CH ; Default file control block #2
DDMA EQU 80H ; Default dma address
BDOS EQU 0005H ; Bdos entrypoint
; BDOS function equates
CONOUT EQU 2 ; Print char to console
PRTSTR EQU 9 ; Print string to console
OPEN EQU 15 ; Open file
CLOSE EQU 16 ; Close file
SFIRST EQU 17 ; Search for first
SNEXT EQU 18 ; Search for next
ERASE EQU 19 ; Erase file
READ EQU 20 ; Read file (sequential)
WRITE EQU 21 ; Write file (sequential)
MAKE EQU 22 ; Make file
SETDMA EQU 26 ; Set dma address
;______________________________________________________________________________
ENTRY: JP START
;..............................................................................
;
OLDSTK: DEFB 'Copyright (c) Steven Greenberg 6/28/86 201-670-8724; '
DEFB 'may be copied for non-profit use only'
;..............................................................................
START:
LD A,7FH ; Find out if z80 with flag test
ADD A,A ; Add 7fh to 7fh
JP PE,Z80 ; Parity (overflow)=z80
LD DE,WRNGUP ; "program requires z-80 processor"
JP MESS80 ; Non-z80s: print up and go home
Z80: LD (OLDSTK),SP ; Save os's stack
LD SP,START ; Set local stack
LD A,(BDOS+2) ; Size up the tpa
SUB EOBFHI+10 ; (includes 2k for the ccp)
JR NC,ENOUGH ;
LD DE,LAKMEM ; "not enough memory..."
JP FATAL ; (fatal error)
ENOUGH: LD DE,LOGO ; Version#, etc
CALL MESAGE ;
LD A,(DFCB+1) ; See if any input file specified
CP ' ' ;
JR NZ,NOHELP ;
LD DE,USAGE ; User needs help (so to speak)
JP FATAL ;
NOHELP: LD A,'Q' ; Force .?q? !!!
LD (DFCB+10),A ; (middle char of filename extension)
LD DE,DFCB ; Point to specified file
LD HL,FNBUFF ; And to filename buffer
CALL WILDEX ; Do wildcard expansion
JR Z,WERR ; (if no matching file found)
LD DE,MAXFLS ; Check if too many matching files
AND A ; Clear carry
SBC HL,DE ;
JR NC,TOOMNY ;
LD HL,FNBUFF ; Get name buffer
LD (BUFPTR),HL ; Set up buffer pointer
LD A,(DFCB2+0) ; Get destination drive spec for all output
LD (OFCB+0),A ; Put it in the drive byte of the output FCB
;______________________________________________________________________________
;
; *** Come here for each new file ***
NXTFIL:
XOR A ; Zero the "EOF flag"
LD (EOFLAG),A ;
LD DE,DFCB+1 ; Clear input fcb
PUSH DE
CALL ZERFCB
LD HL,(BUFPTR)
POP DE
PUSH HL ; Save filepointer
LD BC,11 ; 11 characters
INC HL
LDIR ; Move next filename in place
LD DE,OFCB+1
CALL ZERFCB ; Clean output fcb
POP HL
LD DE,16 ; Offset to next filename
ADD HL,DE
LD (BUFPTR),HL
LD DE,DFCB
LD C,OPEN
CALL BDOSAV
INC A
JR NZ,PRIN ; Br if successful
WERR: LD DE,ERR1 ; Else, "Input file not found"
JP FATAL
TOOMNY: LD DE,ERR3 ; "too many matching files"
JP FATAL
PRIN: LD DE,CRLF ; Print a CR/LF
CALL PRINT ;
LD HL,DFCB ; Print name of input file
CALL PRNFIL ;
;______________________________________________________________________________
;
; Before going too much further, take this opportunity to "clone" a
; 16 byte template of code into memory 256 times. This forms the
; skeleton for the compiled block of code "CODTBL". Various specific
; instructions and data will overwrite sections of this template after
; the dictionary info is read.
CLONE: LD HL,TMPLAT ; Xfer one copy to the beg of "CODTBL"
LD DE,CODTBL
LD BC,16
LDIR
LD HL,CODTBL ; Now copy it 255 more times.
LD BC,255*16 ; De already points to "CODTBL+16"
LDIR ; That does it
; Now load up the input buffer. The input buffer, the output buffer,
; and "CODTBL" are all page aligned and of page multiple lengths. There
; are no other criteria for the lengths of the buffers, except that the
; input buffer should have a minimum length of 1K plus 1 more page. This
; guarantees that the entire dictionary (plus miscellaneous header info)
; will be read in on the 1st pass, simplifying the program.
CALL RELOAD ; (leaves HL pointing to beg of "ibuf")
XOR A ; Init the "EOF flag" to zero (again)
LD (EOFLAG),A ; (detects multiple reads of last sector)
LD A,(HL) ; Get first byte of squeezed file
CP 76H ; Check for "Squeezed File Header" 76H,FFH
JR NZ,NTSQZD ; Br if not a squeezed file
INC L ; Note buffer starts on a page boundary
INC (HL) ; Chk for ff (clobber it along the way)
NTSQZD: LD DE,NSQMSG ; Meanwhile , prep for poss err msg
JP NZ,FATAL ; Fatal "not squeezed" condition
LD DE,ARROW ; " --->"
CALL PRINT ; Ok, print an arrow
LD HL,(IBUF+2) ; Get the 16 bit checksum and save
LD (CHKSUM),HL ; Goes there
LD HL,IBUF+3 ; Init past "76FF" and 2 byte checksum (-1)
LD DE,OFCB+1 ; Init to point to filename of output fcb
LD B,11 ; Set loop cntr for max #of chars in filename
EATLP: INC L ;
LD A,(HL) ; Eat up the file name
OR A ; A zero byte indicates end of filename
JR Z,ATEIT ; Br when that is encountered
AND 7FH ; Strip off any "attribits"
CALL UCASE ; Upcase char if necessary
CP '.' ; Check for name / ext division char
JR Z,ISDOT ; Br when encountered
LD (DE),A ; Else copy filename char to output fcb
INC DE ; And incr that pointer
DJNZ EATLP ; Continue, but not past filename area of FCB
INC HL ; Once more (position should contain "0")
JR ATEIT ; We are done, by definition.
; 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,OFCB+9 ; Skip to filename .EXT area
LD B,3 ; Adjust counter appropriately
JR EATLP ; And continue
ATEIT: PUSH HL ; Save current pointer value
LD HL,OFCB ; Print the output filename
CALL PRNFIL ; Print the filename to the console
POP HL ; Restore value
LD DE,OFCB ; Output fcb
LD C,ERASE ; "blind erase" the dest file if it exists
CALL BDOSAV ; (*** implement a prompt here? ***)
LD C,MAKE ; In any case, make the new file
CALL BDOSAV
INC A
JR NZ,MAKTBL ; Err cond check
LD DE,ERR2 ; "file open error"
JP FATAL ; Exit
;______________________________________________________________________________
;
; Now create "CODTBL" by overwriting certain sections of the
; template created above. The dictionary contains 4 byte nodes-
; these are converted into 16 byte code segments. The maximum
; length of the original dictionary is (256*4) bytes (2K) corres-
; ponding to a maximum "CODTBL" length of 8K.
;
; NODE DEFINITION: As mentioned above, a "node" consists of two
; pairs of bytes. The first pair corresponds to a zero bit, the
; latter to a "1". To decode a character, we start at node #0. A
; bit is pulled off the bit stream. We then use the 1st or 2nd byte
; pair depending on the bit value. The byte pair takes on 1 of 2
; forms "nn FF" or "xx 0x". The "nn FF" type is a terminal node,
; it means we have our next output value - that value specifically
; being the 1's complement (makes it more mysterious) of "nn". If
; the node is of the second type, it is a pointer to another node
; (an absolute offset from the beg of the dictionary in terms of
; node#, must be multiplied by 4 for a byte offset. It has a max-
; imum value of 256, and is expressed as a 16-bit #, lo-byte
; first). In this case we go to that node, pull another bit off
; the input stream, and continue the process.
;
; There is actually a 3rd node type, which just comes up once.
; Its form is "FF FE". It's a special end-of-file marker called
; "SPEOF".
;
; HOW THE PROGRAM WORKS: Each node is converted into a 16-byte
; (actually 13 plus 3 nop's) series of instructions. These in-
; structions later perform the unsqueezing operation. The whole
; block of code starts at "CODTBL" (which is also the entrypoint).
; Each 13 byte "node code" consists of a 7 byte header. The header
; shifts out the next bit from reg "b", then conditionally branches
; to the first or second half of the remaining code (3 bytes per
; half; 7+3+3 = 13). The 3 bytes in each half are either the 2
; instruc-tions "LD A,<byte>" followed by "RET" (terminal node) or
; the single instruction "JP <nxtnode>". All calls to "CODTBL"
; even-tually hit a terminal node and perform a normal return. The
; only exception is the special end-of-file node which compiles to
; "JP SPEOF"; on this particular return the stack is manually ad-
; justed to compensate for the lack of a "RET" instruction.
;
; THE TEMPLATE: This is the "template" which was "cloned" earlier
;
; The following 3 instructions form the header code for every
; node. They are identical for every node (since the jump is rel-
; ative).
;..............................................................................
TMPLAT: SRL B ; Shift out next bit
CALL Z,REFILL ; Refill reg when empty
JR C,BITIS1 ; If bit is "1"
;..............................................................................
;
; After the header code gets executed, one of 2 halves of the
; remainder of the node gets executed. Which half depends on the
; bit shifted out above ("0" for the first half, "1" for the 2nd).
; Each half-node has two possible forms. The terminal form loads
; an appropriate value and returns. The non-terminal form jumps to
; the header of another node. This 16-byte "node-code template"
; assumes the former case by default, since 2 of 3 bytes in that
; case are fixed (only the value need be in-serted). If it turns
; out to be the latter case, all 3 bytes will be overwritten with a
; "jmp" opcode plus an appropriate address.
;..............................................................................
BITIS0: LD A,00H ; (00h gets replaced with actual value
RET ; To be returned.)
BITIS1: LD A,00H ; 2nd half of the node, likewise
RET ;
NOP ; }
NOP ; } so the template is exactly 16 bytes
NOP ; }
;______________________________________________________________________________
;
; --- Create the "node code" table ---
MAKTBL: INC L ; Now points one past the filename eof
LD E,(HL) ; Get #of nodes (lo byte)
INC L
LD D,(HL) ; Hi byte of same
INC L
LD A,D
; An additional file validity check: though the #of nodes could in theory
; theory as high as 0100H, the high byte shoul never be 2 or more
SUB 2 ; If this happens, assume it is not a
JP NC,NTSQZD ; Squeezed file.
; HL indexes through source dictionary (already initialized), & HL' is
; current dest pointer (indexes thru "codtbl"). DE is initialized to the
; # of nodes and is decreased to 0.
EXX ; Init some constants
LD DE,10 ; Used for incrementing HL'
LD HL,CODTBL+7 ; Init HL' itself
EXX ;
; Remember, the whole "header" code and some other instructions are already
; there (from when "template" was duplicated). Only specific details need
; now be filled in.
NODELP: CALL MAKHAF ; Make the first ("0") half-node
CALL MAKHAF ; 2nd ("1") half-node
; Source pointer has already been incremented 4 times, as desired. Dest
; pointer has only been incremented 6 times, however.
EXX ; }
ADD HL,DE ; } so take care of that
EXX ; }
DEC DE ; Loop counter
LD A,D
OR E
JR NZ,NODELP ; Continue till done
JR RUN ; Go run, hl is is ready, pointing to
; the first byte of squeezed code
;______________________________________________________________________________
;
; Create a "half-node"
MAKHAF: LD C,(HL)
INC HL
LD A,(HL) ; Get a byte pair from the dictionary
INC HL
OR A ; If it is negative, it is "terminal"
JP M,TERMOD ; Branch if that is the case.
;..............................................................................
;
; Create code for one half-node of the non-terminal variety.
; Byte pair is in A,C. Multiply it by 16 (bytes/node in "codtbl")
SLA C
RLA
SLA C
RLA
SLA C
RLA
SLA C
RLA
ADD A,CDTBLH ; Add offset to beginning of "CODTBL",
LD B,A ; Now bc has the jump address
ALTENT: PUSH BC ; Save it
EXX ; Switch to dest pointers
LD (HL),JPOP ; Insert the "jp" opcode
INC L ; Remember "codtbl" is page aligned
POP BC ; Get addr back
LD (HL),C ; Jump addr, lo
INC L ;
LD (HL),B ; Jump addr, hi
INC L ;
EXX ; Back to source pointers
RET ; Thats all
;..............................................................................
;
; Create a half-node of the terminal variety
TERMOD: CP 0FEH ; Check for special eof terminal node
JR Z,SPEOF ; Br for that unique case
LD A,C ; Else this byte is the complement of
; The returned value
CPL
EXX ; Switch to dest pointers
INC L ; Just 2nd of 3 bytes need be inserted
LD (HL),A ; Put it in
INC L ;
INC L ; But make sure hl gets incr'd 3 times
EXX ;
RET ; That's all
;..............................................................................
;
; Special EOF returns to a special address in mainline code, rather
; than using "RET". Stack is adjusted accordingly there.
SPEOF: LD BC,DONE ; The special address
JR ALTENT ; Use convenient code subsection above
;______________________________________________________________________________
;
; Code to refill register 'B' with the next byte
REFILL: INC L
JR Z,POSRLD ; If l is zero, may be at end of buffer
CONT: LD B,(HL) ; Else get next byte
; Now 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 reg is a necesssary and sufficient condition for
; determining that all the bits in the reg have been used up (see
; header code for "TMPLATE"). The only things to be careful of is that
; the the last bit is NOT used, and that the bit now in the carry flag
; IS used upon return from this subroutine.
SCF ; To shift in the flag bit
RR B ; Shift out real bit as described
RET ; That's it
POSRLD: INC H ; Check if time to reload the input
LD A,EIBFHI ; Buffer with additional data.
CP H ;
CALL Z,RELOAD ; Reload if necessary (resets hl)
JR CONT
;______________________________________________________________________________
;
; Main code to perform the unsqueeze. In general, the alternate regs
; are used as output pointers, flags, etc. while the primary registers
; are used for input pointing and general purpose use.
RUN: EXX ; First initialize the alternate regs
LD HL,OBUF ; HL', output pntr, to beg of output bfr
LD BC,0 ; C' always has a copy of the previous
EXX ; char output; B' is a "repeat flag".
; Primary register initialization: initialize HL, the input pntr, to point to
; the first byte of squeezed code -1. Initialize DE, the checksum accumulator,
; to zero. Initialize 'B', which contains bits currently being decoded, to
; to 0 so first "decode" call will immediately load it with the first byte.
DEC HL ; First byte of squeeze code -1
LD DE,0 ; Checksum accumulator
LD B,D ; (zero)
;..............................................................................
;
; *** Main operational loop ***
MAINLP: CALL CODTBL ; Unsqueeze a character
CALL SEND ; Output it to the output buffer
JR MAINLP ; And repeat "forever" (see "SPEOF"
;______________________________________________________________________________
;
; Reload the input buffer, & reset HL to point to the beginning of it.
; Assumes input bfr starts page boundry and is of page multiple length.
RELOAD: PUSH AF
PUSH BC
PUSH DE
LD B,IBUFSZ ; Loop counter, buffer length in pages
LD D,IBUFHI ; Beg of buffer (hi)
RLDLP: LD E,0 ; Lo byte of current dma
CALL RDSEC ; Read in 128 bytes (1/2 page)
JR NZ,RLDRTN ; (return if eof enecountered)
LD E,80H ; To read in the next half page
CALL RDSEC ; Do that
JR NZ,RLDRTN ; As above
INC D ; Next page
DJNZ RLDLP ; Loop till done
RLDRTN: POP DE ; Restore regs
POP BC
POP AF
LD HL,IBUF ; Reset input pointer
RET ; And return
;..............................................................................
;
; Subr for abover, reads 128 bytes to memory starting at HL
RDSEC: PUSH DE ; Save dma before clobbering it with fcb
LD C,SETDMA ; Set dma function
CALL BDOSAV ;
LD DE,DFCB ; Input fcb
LD C,READ ;
CALL BDOSAV ; Read a record
POP DE ; Restore dma to original dma address
OR A ; Set non-zero status
RET Z ; Normal return, barring EOF condition
LD A,(EOFLAG) ; Check if EOF is hit twice
OR A ;
JR NZ,HTCHED ; This is an abnormal condition
CPL ; Else (first EOF) put "FF" in "eoflag"
LD (EOFLAG),A ;
OR A ; Set non-zero status and return
RET ;
HTCHED: LD DE,CHOPPD ; "Unexpected EOF enountered"
JP FATAL ; (file is truncated or damaged)
;______________________________________________________________________________
;
; When "SPEOF" is encountered, a jump to here is made to exit "CODTBL"
; rather than the normal RET instruction.
DONE: INC SP ; So adjust the stack immediately!
INC SP ;
LD A,(CHKSUM+0) ; Make sure the checksum checks out
CP E ; Lo-byte
JR NZ,NFG ; Br if nfg
LD A,(CHKSUM+1) ; Likewise
CP D ;
JR Z,CKSMOK ; Ok
; If a checksum error is detected, report the warning. Let the guy
; have his file anyway, for whats its worth.
NFG: LD DE,CHKERR ; "checksum error detected"
CALL MESAGE ;
; Switch to alternate regs for output. The total #of bytes generated
; should always be a multiple of 128. This assumption is not made,
; however, as it may not be true if the file was squeezed on non-CP/M
; systems. Compute # of sectors to write- specifically subtract the
; buffer start addr from the current pointer value, add 7FH and divide
; by 128. If the byte count was in fact a multiple of 128, this has
; no effect; otherwise it makes sure the final sector gets written.
CKSMOK: EXX ; Switch to alt regs
AND A ; Clear carry
LD DE,OBUF-7FH ; Take care of adding 7fh in advance
SBC HL,DE ; Subtract
SLA L ; Divide by 128
RL H ; Result now in h
LD B,H ; Use 'b' as the counter
CALL WRTOUT ; Writes 'b' sectors to the output file
EXX ; Back to primary regs
LD DE,OFCB ; Close the output file
LD C,CLOSE
CALL BDOSAV
LD DE,DFCB ; Likewise the input file
LD C,CLOSE
CALL BDOSAV
; Fall through
EXIT: LD HL,(NMBFLS)
DEC HL
LD (NMBFLS),HL
LD A,H
OR L
JP NZ,NXTFIL ; Next file
LD SP,(OLDSTK) ; Restore os stack
RET ; To ccp
;______________________________________________________________________________
;
; Write 'B' 128 byte sectors to the output file
;
WRTOUT:
LD A,B ; If b=0, don't write any sectors
OR A ;
RET Z ;
LD DE,OBUF ; Init dma addr to beg of output bfr
WRTLP: LD C,SETDMA ; Set dma to there
CALL BDOSAV
PUSH DE ; Save that address
LD DE,OFCB ; Specify the output file
LD C,WRITE ; Write a record
CALL BDOSAV ;
OR A
JR NZ,WRTERR ; Br on error
POP DE ; Address as saved above
DJNZ NEXSEC ; Decrement counter & continue if not done
RET ;
NEXSEC: LD E,80H ; Else incr by 1/2 page
LD C,SETDMA ;
CALL BDOSAV ;
PUSH DE ; Save dma pntr
LD DE,OFCB ; Output fcb
LD C,WRITE ; Write another sec
CALL BDOSAV ;
OR A ;
JR NZ,WRTERR ; Br if error
POP DE ; Get back orig pointer
INC D ; Inc hi-byte, 0 the lo to effect
LD E,0 ; Another 80h incr
DJNZ WRTLP ; Loop till done
RET
;______________________________________________________________________________
;
BDOSAV: ; Bdos call w/ all regs and alts saved
EXX ; except for AF, AF', IX, & IY
PUSH BC
PUSH DE
PUSH HL
EXX
PUSH BC
PUSH DE
PUSH HL
CALL BDOS
POP HL
POP DE
POP BC
EXX
POP HL
POP DE
POP BC
EXX
RET
WRTERR: LD DE,WRTMSG ; Write error, falls through to "fatal"
;______________________________________________________________________________
;
; For fatal errors- print the message, restore the os stack & return
; This rountine is "jumped to", not called
;
FATAL: CALL MESAGE ;
LD DE,CRLF ;
CALL PRINT ;
LD SP,(OLDSTK) ; Restore stack pointer Z80-style
RET ;
MESAGE: EX DE,HL ; Save pntr to message (supplied in de)
LD DE,CRLF ; First print a cr/lf sequence
LD C,PRTSTR
CALL BDOSAV
EX DE,HL ; Then the message in question
PRINT: LD C,PRTSTR ; Entry here if no cr/lf desired
JP BDOSAV
MESS80: LD C,PRTSTR ; For non-Z80 mesage, don't use "BDOSAV"
JP BDOS ;
;______________________________________________________________________________
;
; 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
CALL OUT ; Else just output the char;
EXX ; Back to normal regs
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
LD A,C ;
AGAIN: CALL OUT ; Repeat b occurrences of byte in 'a'
DJNZ AGAIN ; Leaves b, the rpt flag, 0 as desired
EXX ; Restore regs & rtn
RET
SND90H: LD A,90H ; Special case code to send the byte 90h
CALL OUT ; Itself
EXX ;
RET ; (90h "squeezes" into 2 bytes!)
;..............................................................................
;
; Output character in 'A' directly to the output buffer
;
OUT: EXX ; Back to primary regs briefly
LD C,A ; Save a in c
ADD A,E ; De is the running checksum
LD E,A
JR NC,NOCARY
INC D
NOCARY: LD A,C ; Put the char back into a
EXX ; Back to output (alternate) regs
LD (HL),A ; Put byte into the next avail position
INC L ; Increment pointer
RET NZ ; Return if not passing a page boundry
INC H ; Incr pointer high byte, check limit
LD L,A ; Use l, which is 0, for temp storage
LD A,EOBFHI ; Limit
CP H ; Check
LD A,L ; But first restore regs
LD L,0 ;
RET NZ ; Ret if limit not reached
PUSH AF
PUSH BC
LD B,OBUFSZ*2 ; Number of 128 byte records to write
CALL WRTOUT
POP BC
POP AF
LD HL,OBUF
RET
;______________________________________________________________________________
;
; Initialize an FCB. DE points to the FCB +1.
;
ZERFCB: LD B,11 ; Fill filename with blanks
LD A,' '
CALL ZL
LD B,24 ; Then zero remainder
XOR A
ZL: LD (DE),A
INC DE
DJNZ ZL
RET
;______________________________________________________________________________
;
; Print name of output file. HL should point to the FCB.
;
PRNFIL: LD A,(HL) ; Get drive spec
INC HL ; Move to 1st char of filename
OR A ; Drive = default?
JR Z,DEFDRV ; Br if so
ADD A,'A'-1 ; Else convert to a letter
CALL TYPE ;
LD A,':' ; Follow drive spec with a ":"
CALL TYPE ;
DEFDRV: LD B,12 ; Loop cntr (max #of chars plus ".")
CHARLP: LD A,(HL) ; Get a char
CP " " ; Blank?
JR Z,SKPTYP ; Supress them
TYPEIT: CALL TYPE ; Type the char
SKPTYP: DEC B ; Loop counter
RET Z ; Rtn when done
LD A,B ; Check loop counter
CP 4 ; At this point, type a "."
JR NZ,NOT4 ;
LD A,"." ;
JR TYPEIT ; Type it. do not incr hl or reload a.
NOT4: INC HL ; Advance pointer
JR CHARLP ; Repeat till done
;______________________________________________________________________________
;
TYPE: LD C,CONOUT ; Type the char in "a" to the console
LD E,A ; (clobbers c & e)
CALL BDOSAV ;
RET ;
;______________________________________________________________________________
;
;WILDEX - wildcard expansion module
; S. Kluger 04/15/84
;
; This module, for use with SYSLIB, can be used to expand a wildcard
; filename into a table of file names as found in current DU:
; ENTRY:
; HL = .buffer
; DE = .afn fcb
; EXIT:
; HL = number of files
; ACC= zero flag set if error
; the buffer contains HL file names of 16 char each
; Char 0 contains the user number!
WILDEX: LD (BUFPTR),HL
LD HL,0
LD (NMBFLS),HL
LD C,SFIRST
CALL BDOSAV
CP 0FFH
RET Z ; Nothing found -- error
CALL MOVEN ; Move name
WLOOP: LD C,SNEXT ; Search for next
CALL BDOSAV
CP 0FFH
JR Z,DONEW ; Finished
CALL MOVEN
JR WLOOP
DONEW: OR A
LD HL,(NMBFLS)
RET
MOVEN: PUSH DE
LD HL,(BUFPTR)
ADD A,A
ADD A,A
ADD A,A
ADD A,A
ADD A,A
ADD A,80H
LD C,A
LD B,0
LD D,16 ; Move 16 chars
MOVLP: LD A,(BC)
LD (HL),A
INC HL
INC BC
DEC D
JR NZ,MOVLP
LD (BUFPTR),HL
POP DE
LD HL,(NMBFLS)
INC HL
LD (NMBFLS),HL
RET
;______________________________________________________________________________
;
UCASE: ; "Upcase" the letter in "A", if necessary
CP 'a' ;
RET C ; If < "a", forget it
CP 'z'+1 ;
RET NC ; Likewise if > "z"
SUB 20H ; Else convert
RET ;
;______________________________________________________________________________
LOGO: DEFB 'Fast Unsqueezer v2.0',CR,LF,'$'
ERR1: DEFB 'Input file not found.$'
ERR2: DEFB 'File open error.$'
ERR3: DEFB 'Too many matching files.$'
ARROW: DEFB ' ---> $'
LAKMEM: DEFB 'Out of memory.$'
NSQMSG: DEFB 'Not a squeezed file.$'
CHKERR: DEFB 'Checksum error detected.$'
WRNGUP: DEFB 'Program needs Z-80.$'
WRTMSG: DEFB 'Output error.$'
CHOPPD: DEFB 'Unexpected EOF encountered.$'
USAGE: DEFB 'Usage: UF [d:] <afn> [d:]',CR,LF
DEFB ' optional drive specs are source & dest. respectively.$'
CRLF: DEFB CR,LF,'$'
;______________________________________________________________________________
;
BUFPTR: DEFW 0 ; Used for indexing thru "fnbuff"
NMBFLS: DEFW 0 ; #of wild card matches found
;..............................................................................
CHKSUM: DS 2 ; Checksum kept here
EOFLAG: DS 1 ; "EOF Flag"; set from 0 to FF when EOF is hit
OFCB: DS 36 ; Output fcb; gets initialized by "zerfcb"
MAXFLS EQU 400 ; Max #of filenames in a wild card expansion
; (should be sufficient!)
FNBUFF: DS (16*MAXFLS) ; Wildcard expansion buffer
ENDFNB EQU $ ; End of above buffer
;______________________________________________________________________________
;
; Compute next page boundary following the end of the above buffer. Do
; this by adding 0FFH and "anding" with 0FF00H. This will become the
; beginning of "CODTBL".
; *** NOTE *** [sgg]
; If this code is CSEG'd, M80 will reject this. You can fake
; out the assembler using a "HIGH" , then multiplying by 256,
; but then it will link incorrectly. If you just EQU "codtbl",
; the resulting code isn't really relocatable anyway...
;
; I think there are ways around this, but they involve the
; assumption that this whole file is "page relocatable" only.
PGBND EQU (ENDFNB+0FFH) AND 0FF00H ; (works with aseg only)
ORG PGBND
;..............................................................................
;
; Minimum input buffer size is 5 pages to guarantee that the max possible
; dictionary size (256 x 4) bytes plus overhead) loads the first pass.
;
; Output buffer can be any page multiple length (note it too is also page
; aligned). Increases beyond 64 pages (16k) may do little to improve perf.
IBUFSZ EQU 16 ; Input buffer size (pages)
OBUFSZ EQU 64 ; Output buffer size (pages)
PAGE EQU 256 ; (for clarity)
CODTBL: DS 256*16 ; "codtbl"; 256 entries x 16 bytes/entry
IBUF: DS IBUFSZ*PAGE ; Input buffer
OBUF: DS OBUFSZ*PAGE ; Output buffer
EOBUF EQU $ ; End of output buffer
CDTBLH EQU HIGH CODTBL ; }
IBUFHI EQU HIGH IBUF ; } high bytes of the beginning addresses
EIBFHI EQU HIGH OBUF ; } of the buffers just defined
EOBFHI EQU HIGH EOBUF ; }
END
et i CP 'z'+1