title	LT31 Types/Extracts/Uncrunch/USQ/UNLZH LBR/Files (15 Dec 1991)
;
ver	equ	31;		Current version number
;
; LT types normal, lzh-encoded, crunched or squeezed files, either directly
; or from .LBR members.  Wild cards access a series of library members.
;
; Recommended Assembler/Linker - SLRMAC and SLRNKP.  Others will work,
; but normally require a two pass link operation and file truncation
; to avoid generating useless uninitialized data space in the code file.
;
;	SLRMAC LTxx/R
;	SLRNKP LTxx/N,/A:100,/J,LTxx,UNLZH,UNC,/E
;
; For other linkers see the documentation in BUFFERS.  To check your
; methods first assemble and link this release and compare the object
; files.  If they are different you do not have the full technique.
;
;---------------------------------------------------------------------
;			     updates
;
; 15 Dec 91 Modified as follows to enable assembly with ancient (about 1979)
;   v31     version of M80 by changing the names of the following symbols which
;	    exceeded 6 characters.  (This will not effect assembly with any
;	    other assembler.)
;	    OLD LABEL  NEW LABEL   OLD LABEL  NEW LABEL   OLD LABEL  NEW LABEL
;	    setnam2    setnm2	   setfld1    setfd1	  setfld2    setfd2
;	    setfld3    setfd3	   setfld4    setfd4	  setfld5    setfd5
;	    Also modified routine labelled start: to fix a bug dating from v25
;	    that prevented LT from accepting USER specifications as a SOURCE
;	    of data.  (Many thanks to Roger Warren, Sysop, Elephant's Graveyard
;	    San Diego, California for the suggestions for fixing this problem.)
;				     Modified by Brian Murphy (BCM)
;				     Vancouver Kaypro Users Group
;				     Richmond, British Columbia, Canada
;				     BBS phone:  (604) 271-5934
;
; 17 Jul 91 Incorporated version 2.0 of LZH encoding.  This program is
;   v30     necessary to decode files encoded with version 2.0 of LZH
;	    compression, but will AUTOMATICALLY handle files encoded with
;	    version 1.x LZH encoding. Added appropriate documentation changes.
;	    Added instruction to reset MSB in output FCBs.
;	    Corrected error message pointer for UNC & UNL errors
;	    (sent garbage to screen).
;					-R Warren
;					 Sysop, Elephant's Graveyard
;					 San Diego, CA (619)270-3148
;
; 02 Sep 89 Added ability to handle LZH-encoded files (extensions
;   v29     of the form .?Y?).	Added appropriate docs. No functional
;	    changes to the program, otherwise.
;					-R Warren
;					 Sysop, Elephant's Graveyard
;					 San Diego, CA (619)270-3148
;
;
;		****	 DIATRIBE	****
; 18 May 88 Reformatted again, and repaired.  If Mr Hoff wishes to
;   v28     to reformat this source I wish he would retain such for
;	    his own benefit, and not foul the distributed version.
;	    He ingeniously makes it virtually impossible to detect the
;	    changes made using a text comparator.  If this request is
;	    a problem to him he is welcome to call me at the number 
;	    below.  V27 had totally lost any indication of what file
;	    was being typed, and without a convenient comparison I
;	    had to restore from V25.  Thus V26 changes have been lost.
;	    and I do not know whether V26 or V27 was the problem.
;	    I also request that Mr. Hoff refrain from removing my
;	    comments in the future (I have restored my comments to
;	    version 25 below).	Will all modifiers kindly refrain from
;	    altering the file dates of modules that have not changed.
;	    For example, UNC.REL and UNC.SLR are still the '86 versions.
;	    LBR distributions can use LSETDATE, and ARCs are automatic.
;	    The gain from a large value of REC is miniscule and prevents
;	    use on systems with limited memory.  V28 does not produce
;	    extra lines between extractions to file now, and this was
;	    done with less, not more, code.
;	
;	    THINGS to be done: Incorporate checksum testing of results;
;		slave ZCPR linkages to Z3ENV if on ZCPR system (signal
;		is non-zero environment pointer).  I hope to have a
;		DOS+/CCP+ version that respects the ENV for wheel/user
;		levels some time.
;
;	    If I am mistaken as to the culprit, I apologize for the
;	    vented spleen.		- C.B. Falconer
;
; 05/11/88  Rewrote display section when  extracting files to disk.  Was
;   v27     triple spacing with no tabulation. Was unsightly if library
;	    had more than 1-2 files.  (Still under 5k.)  If TPA is under
;	    48k, might need to change  "REC"  from 128 to 96 or even 64.
;					- Irv Hoff, Sysop
;					  PRACSA Sysop RCPM
;
; 04/15/88  When typing a crunched file  with a comment attached, it was
;   v26     running off the end of the screen, to display the uncrunched
;	    file name with comment.	- Ed Minton
;					  Columbus, OH
;
; 10 Apr 88 Reformatted to my original system, which shows the action
;  v25	    of conditionals clearly.  Someone had reformatted the
;	    entire source, and I suspect the promulgator of a certain
;	    reformatting program.  There is no need to keep conditionals
;	    un-nested, since linking requires a reasonable assembler
;	    (not ASM) anyhow.  Added .OVR type prohibition.
;	    Wheel control is now unified, with a single patch point.
;	    Now organized so that all options are patchable - Set maxusr
;	    to 0ffh to use the ZCPR mxusr value.  Pausecheck on every
;	    character is now an option (useful for Braille).  The 
;	    conditionals should be used only where a smaller object
;	    file is required for some reason, not for configuration.
;	    Space has been reserved for ZCPR addicts (environment).
;	**  PATCH POINTS HAVE CHANGED (for the last time??!!).
;	    I have also restored some of the "regs affected" comments
;	    which had been removed.  Please update and maintain them,
;	    they greatly ease future modifications.
;	    I have attached a slightly later version of UNC, functionally
;	    identical, but 5 bytes shorter.
;	    I am glad to see many people actively enhancing this.  When
;	    originally released I did not expect it to attain such
;	    popularity.  Too bad it grew over 4k.  Now should add the
;	    checksum tests.
;				- C.B. Falconer (203) 281-1438
;
; 02/18/88  Fixed bug in PARSE4 routine. If no USER AREA specified for
;   v24     disk output then it defaulted to input file user area rather
;	    than the CURRENT user area. Modified to allow abort during
;	    disk output. Added $U command line option to allow disk
;	    output of squeezed/crunched files WITHOUT unsqueeze/uncrunch.
;	    (see the NOUQZ byte added to the patch area at 103H).
;	    Added REC equate to allow more than one sector in the file
;	    output buffer to reduce wear and tear on floppy drives.
;					- Tom Head
;
; 08/12/87  Fixed to properly handle 0-length files.  Modified to
;   v23     check for pause and abort from console after every typed
;	    character rather than at the end of each line.  This was
;	    necessary to prevent loss of data on some very slow
;	    terminals such as the one I use, and to enable the typing
;	    of a file with no linefeeds to be aborted.	Also fixed the
;	    tab expansion routine to work correctly with files con-
;	    taining unusual control characters such as backspace, and
;	    made the "turn up one line" feature more foolproof.
;					- Howard Goldstein
;
; 07/30/87  Added the long needed ZCPR/ZCMD support of maximum user
;   v22     area.  When the UZCPR option is set, the page 0 value
;	    will be examined to determine if the requested user area
;	    is within the allowable range.  This is primarily useful
;	    on RCP/M's (but I suspect that is where LT is primarily
;	    used).  Added code to allow overriding the line counter
;	    and user area restrictions when the wheel is active.
;					- Gary Inman, Sysop
;					  West Los Angeles RCP/M
;
; 07/21/87  When at a [more] pause, the space bar now turns up one
;   v21     line at a time.  LTxx is often used on RCPM systems as
;	    their general purpose TYPE.COM program.  This makes it
;	    compatible with UNARC16.  These two  programs are used
;	    in the popular LUX program to type ASCII files in ARC,
;	    ARK or LBR files.  I have always liked this feature in
;	    UNARC16 and decided to add it to LT20.  I believe most
;	    users will find this quite useful and hope CB Falconer
;	    does not mind my adding this feature.  This version is
;	    a little over 4k.  If this makes any problem, just use
;	    an earlier version. 	- Irv Hoff, Sysop
;					  PRACSA RCPM
;
; 07/17/87  Able to use wheel byte in conjunction with OUTFLG flag.
;   v20     One byte added in ddt modifiable area at program start.
;	    WHLFLG and/or WHEEL now tested in conjunction with the
;	    OUTFLG flag eliminating need for two copies of program
;	    when used on a remote system.  Found that it would not
;	    assemble properly using M80/L80 because of YES/NO, now
;	    corrected.	Restored program name, version number, and
;	    author credit. Other minor mods to keep code size <4k.
;					- G.F. Reding [72436,45]
;
; 06/10/87  Change to only display characters between "[" and "]" in
;   v19     the header of crunched files as other characters in this
;	    area are reserved.	Removed the redundant "IF NOT UNCR"
;	    (marked ";;;;") following an "ELSE" which prevented LT18
;	    from being assembled in its distributed form. Slight text
;	    changes to keep <4k.	- Steven Greenberg
;
; 12/28/86  Allows access to .LBR files > 512k.  Was using CPM 1.4
;   v18     variety of direct access.  Mods to keep under 4k total.
;	    Should CP/M v1.4 check and refusal.
;					- C.B. Falconer
;
; 12/13/86  Prevent junk file names on "LT fn.t d:" where fn.t is not
;   v17     squeezed or crunched. Thanks to Bill Duerr for calling my
;	    attention to this.	 Set default "drvsup" to all drives.
;	    Fixed OUTFLF usage when extract false, per Frank Whitman.
;					- C.B. Falconer
;
; 11/24/86  Using UNC module, executable on 8080.   Can extract all
;   v16     files to disk. Needed because NULU 1.51 cannot uncrunch
;	    during extractions.  Disk extraction ignores "BADTBL".
;	    For RCPM use, assemble with the "extract" equate set to
;	    NO.  Clean-ups.		- C.B. Falconer
;
; 11/17/86  Added ability to extract to a file with "LT lbr d:fn.ft",
;   v15     where the "d:" signifies extraction.  01Ah is EOF, so not
;	    suitable for binary files.	The code has become a mess to
;	    preserve the ability to generate minimum version.
;					- C.B. Falconer
;
; 86/11/15  Added hooks to include Steve Greenbergs UNCR system. These
;   v14     are guarded by 'UNCR' equate to setup options.  Use requires
;	    linking and creation of relocatable object.  Any uncrunching
;	    requires a Z80 cpu. 	- C.B. Falconer
;
; 02/12/86  Added expansion of UCSD style "dle/code" indentation codes.
;   v13     These also appear in the PASCALP system.
;					- C.B. Falconer
;
; 12/05/84  Stole some features from TYPELxx '?' for forbidden file
;   v12     type table, list output enable on du version, location of
;	    BADTBL for patching.  Checked "LIMITT" and "LMITL" options
;	    functioning with wild cards.  Version display shows options
;	    enabled.  Added bit vector for drives available.  Added du
;	    option under "DUSPEC" conditional.	The minimum version
;	    remains under 0500h bytes, with options disabled.  If an
;	    operator is already logged into a drive/user area then that
;	    drive/user area remains available even though the configured
;	    restrictions should prevent it.  This allows for privileged
;	    users.  John Doe can't get there at all.
;					- C.B. Falconer
;
; 12/04/84  Deleted unused variables and labels, also variables that
;   v11     are better kept on the stack.  Arranged for LBRFCB to be
;	    fully initializable via parameter, for straight type.
;	    Thus:
;			   A>LT fname.typ
;
;	    will type/unsqueeze FNAME, while "LT LBRFILE component"
;	    extracts as before. 	- C.B. Falconer
;
; 10/02/84  Adapted from Steven R. Holtzclaw's "LUXTYP (06/10/83)"
;   v10     for independent use without the complete LUX system.
;	    Eliminated Z80-only opcodes.  Added file searches.	Added
;	    simple usage message, assembly time constants to eliminate
;	    limits on lines and file types, ^Z stops pauses. CRT lines
;	    measured acrossfiles.
;				- C.B. Falconer
;
; ---------------------------------------------------------------------
;
	aseg ;	Needed for M80, ignored by SLRMAC, else ignore error
;
no	equ	0;	For conditional assembly
yes	equ	not no; (Some assemblers don't like 0FFh)
;
;	-------- Configuration -------
; Assembly time configurable areas.  Each increases COM file size.
extract equ	yes;	*File extraction ability, wheel controllable
rec	equ	32;	Sectors in file output buffer.	DO NOT make
;			  too large, else uncruncher wont work
;
limitl	equ	yes;	Yes allows output line limits
limitt	equ	yes;	Yes allows file type restriction
duspec	equ	yes;	Use DU style drive/user specifications
xpnd	equ	yes;	Expand UCSD style "DLE/code" indentation codes
paws	equ	no;	Check pause/abort on each output char (braille)
;
; Next is used with EXTRACT so that 1 copy only needed for RCPM systems
; Note: the wheel also over-rides the line count restriction, if any
whlat	equ	0;	Location of wheel byte, 0 disables wheel check
;			(usually 03eh, patch into option area)
;			Value only used to set this word (1 place)
;
; For added security on RCP/M's to prevent access to files outside
; the callers permitted drive user areas.  See whlat above also
mxdrv	equ	03dh;	Location of max drive byte
mxusr	equ	03fh;	Location of max user byte
;
; Configurable values.	Also see "DRVSUP" configurable vector
pagsz	equ	20;	Default lines per CRT page
lnmax	equ	0;	0 for no limit, else max file size (to 255)
noctrl	equ	yes;	Yes to prevent control char. output
usrmax	equ	15;	Maximum user area accessible
;			 0-15 for CP/M 3, 0-31 for CP/M 2.2 or DOSPLUS
;	ZCPR	. .	SPECIAL VALUE - 0ffh causes ZCPR mxusr to be
;			loaded into maxusr.
;	------- END configuration equates --------
;
; Ascii control chars
bell	equ	07h
bs	equ	08h
tab	equ	09h
lf	equ	0ah
cr	equ	0dh
dle	equ	010h
eof	equ	01ah
sqzdle	equ	090h;	special in squeezed/crunched files
;
; CP/M-DOS+ system values
bdos	equ	0005h
fcb1	equ	05ch
fcb2	equ	06ch
cmdln	equ	080h
;
; BDOS calls
cio	equ	6;	Direct console I/O
setdrv	equ	14
open	equ	15
close	equ	16
srchf	equ	17
delete	equ	19
fread	equ	20
fwrt	equ	21
make	equ	22
getdrv	equ	25
setdma	equ	26
gsuser	equ	32;	Get/set user
frdran	equ	33;	Read random record
;
	cseg
;
; Linkages to unc module
	extrn	unc, endu
	entry	getbyt, out
;
; Linkages to UNLZH module
	extrn	unl
	entry	glzhun,plzhun
;
;	--------------
;	PROGRAM BEGINS
;	--------------
begin:	jmp	start;		past configuration area
;
; Unused, but space reserved to fix patch locations
	db	'Z3ENV';	Special signature
	db	1;		External environment
@env:	dw	0;		And ZCPR can patch this location
;
; Configuration values, even if unused via "limit" options
; These may be patched to configure the COM file at installation
whladr: dw	whlat;		Non-zero if wheel controlled
pagsiz: db	pagsz;		Lines per page, 0 = no pauses
maxlin: db	lnmax;		Max lines to type, 0 = unlimited
typflg: db	limitt; 	0 for all file types, else selective
pawsfg: db	paws;		To check pause on each output char.
ctlflg: db	noctrl; 	Non zero allows control char print
altdrv: db	0;		Alternate drive to search, 0 = none
maxusr: db	usrmax; 	Max accessible user, for du entries
;				Set 0ffh to use ZCPR mxusr value
drvsup: dw	1111111111111111b; Vector of available drives
;		ABCDEFGHIJKLMNOP-; Drive ids (No N..P under PascalP)
; (example)	1100000000001000B; (shown set for A, B and M drives)
nouqz:	db	0;		0 to allow unsqueezing/uncrunching
;				of files during disk output. The $U
;				command line option will toggle this.
xpdle:	db	NOT noctrl;	Set NO = 0 to suppress DLE expansion.
;				Useful if noctrl is set NO.
	db	0;		spare for future use
;
; '?' matches any character. Alpha order for convenience only,
; a complete sequential scan is done.  An existing name can be
; made to disappear by setting its high order bit somewhere.
	if	limitt; 	Table of invalid file types
badtbl:  db	'ABS';		Intended to disable
	 db	'ARC';		===================
	 db	'ARK'
	 db	'BAD'
	 db	'CRL'
	 db	'C?M';		COM, CQM, CZM, CPM (v20 executes on PCs)
	 db	'E?E';		EXE, EQE, EZE	(MSDOS executable)
	 db	'IRL'
	 db	'I?T';		INT, IQT, IZT
	 db	'LBR'
	 db	'O??';		OBJ, OQJ, OZJ, OVL, OVR etc
	 db	'P?D';		PCD, PQD, PZD	(executable by RUNPCD)
	 db	'TX#'
	 db	'RBM'
	 db	'R?L';		REL, RQL, RZL
	 db	'S?R';		SLR, SQR, SZR	(SLR format rel files)
	 db	'SYS'
	 db	0,0,0
	 db	0,0,0;		Spares, for user configuration
	 db	0,0,0
	 db	0;		Table end marker
	endif;		limitt

start:	lxi	h,0;		Set up HL for add to stack
	shld	memcnt; 	Zero the memory count
	dad	sp;		Add stack pointer
	shld	stack;		Keep stack contents
	lxi	sp,stack;	Set up local stack
	lda	maxusr
	inr	a;		Special 0ffh max causes
	lda	mxusr;		revision to mxusr
	jnz	start1; 	(Changed from jz to fix bug BCM 15/12/91)
	sta	maxusr; 	Revise if ZCPR user system
start1: lxi	d,lbrbuf
	mvi	a,setdma;	Do all I/O thru this buffer
	call	sys
;	"       "
	if	extract
	 mvi	a,' '
	 sta	outfcb+1;	Default none
	endif;		extract
;	"       "
	if	duspec; 	Parse the command line
	 call	parse
	 lda	savusr
	 call	sguser; 	Set the user access
	endif;		duspec
;	"       "
	call	lbropn; 	Set up the library name buffer
	push	psw;		Save extract/type flag
	jnz	start2; 	Type only
	lhld	memcnt; 	Get member count
	mov	a,l;		Get member count lsb
	ora	h;		Any members?
	lxi	d,nfound
	jz	exeunt; 	No - exit
;	"       "
start2: lxi	d,sign1;	Give name, version, credit
	call	tstrc
	pop	psw
	jnz	dump;		Type only
;	"       "
; Per component loop
next:	call	initlp; 	Initialize the "next" loop
	call	getmem; 	Get next member FCB
	jc	exit;		All done...
	call	lbrset; 	Set up to read the library file
;	"       "
; Input setup, do the extraction and/or unsqueezing
dump:	if	limitt; 	Test the type of file
	 call	tsttyp
	endif;		limitt
;	"       "
	call	get2hl; 	Get the first 2 bytes from the file
	jnz	zerprt; 	Special processing for 0-length file
;	"       "
; This allows disk output WITHOUT any unsqueezing or uncrunching.
	push	h
	lxi	h,outflg;	File output flag
	lda	nouqz;		No unsq/uncr flag
	ana	m;		If (no unsq/uncr) AND (disk output)
	pop	h
	jnz	asprt;		Then skip uncompression routines.
;	"       "
	mvi	a,076h
	cmp	l
	jnz	asprt;		Not squeezed - print an ascii file
	mvi	a,0ffh
	cmp	h
	jz	dumpsq; 	Squeezed, dump it
	dcr	a;		To 0feh
	cmp	h
	jz	dumpcr; 	Crunched, dump it
	dcr	a;		To 0fdh
	cmp	h
	jnz	asprt;		Not compressed - print an ascii file
;	"       "
; Output from a LZH compressed file
	call	nmshow; 	Show actual name etc, absorb header
	lhld	namptr; 	Free memory area, above names
	call	unl;		UNLZH.REL.  Uncompress
	lxi	d,badfile
	jc	donem;		something went wrong
	jmp	done
;	"       "
; Output from a crunched file
dumpcr: call	nmshow; 	Show actual name etc, absorb header
	lhld	namptr; 	Free memory area, above names
	call	unc;		UNC.REL.  Uncrunch
	lxi	d,badfile
	jc	donem;		something went wrong
	jmp	done
;
; Output from a squeezed component
dumpsq: call	get2hl; 	Get and discard the next 2 bytes
	call	nmshow; 	Actual name, and absorb header
	call	sqsetu; 	Setup the squeezed file
;	"       "
; List a squeezed component
sqloop: call	getsqb; 	Get a byte from the file
	jc	done;		Eof - get next file name in queue
	call	crtype; 	Else print the char
	jmp	sqloop; 	And loop for more
;
; Show UNSQ/UNCR member name, etc.  Optionally revise output name.
; This has the side function of absorbing the sqz/crn header.
; a,f,b,d,e,h,l
nmshow: lxi	d,ffsep
	call	tstr;		Make the file-file seperator
;	"       "
	if	extract
	 lxi	h,outfcb+1
	 mvi	b,11
nmshw1:  mvi	m,' ';		Pre-blank the output name
	 inx	h
	 dcr	b
	 jnz	nmshw1
	 mov	m,b
	 lxi	d,outfcb+1
	 mvi	b,8;		Size of name field
	endif;		extract
;	"       "
; This section types the filename to the console and puts it into
; "OUTFCB".  A null character terminates all header processing.  Other
; characters after filename are ignored, unless they follow a "["
; character.  These will be echoed to the console, until either a null
; character is detected (terminate processing) or a "]" character is
; detected (start scanning for null again).
nmshw2: push	d;		Filename area of OUTFCB
	call	lbrget; 	Get character from the file
	pop	d;		Restore pointer
	ora	a;		Check for null
	jz	nmshw9; 	If found, terminate header processing
	cpi	'['
	jz	nmshw7; 	"[", go display "stamp" text
	cpi	'.';		Was the character a "."?
	jnz	nmshw3; 	If not update cnt, display char, put in FCB
	call	ctype;		"." is displayed, but not put into FCB
	mvi	b,3;		set filename char counter to "3"
;	"       "
	if	extract;		
	 lxi	d,outfcb+9;	adv filename dest pointer accordingly
	endif;		extract
;	"       "
	jmp	nmshw2; 	Continue processing filename ext chars

nmshw3: inr	b;		Check filename character limit
	dcr	b;		
	jz	nmshw6; 	If down to zero, exit this section
	ani	07fh;		Strip MS bit before placing in FCB
	call	ctype;		Else display char
;	"       "
	if	extract;		
	 stax	d;		put char in the FCB if appropriate
	endif;		extract
;	"       "
	inx	d;		Bump filename dest pntr
	dcr	b;		Count it
	jmp	nmshw2; 	Continue filename processing
;
; Filename has been fully processed. Continue header analysis.
nmshw4: call	ctype;		(entry here to display extra char)
;	"       "
nmshw5: call	lbrget
	ora	a;		Loop swallows characters until either
	jz	nmshw9; 	  a null or a "[" char is detected
;	"       "
nmshw6: cpi	'['
	jnz	nmshw5
;	"       "
nmshw7: mvi	a,' ';		"[" found. Insert blank for aesthetics
	call	ctype
	mvi	a,'[';		But display the "[" char as well
nmshw8: call	ctype
	call	lbrget
	cpi	']';		Loop to display characters to the con-
	jz	nmshw4; 	  sole until a null or a "]" is found
	ora	a
	jnz	nmshw8
;	"       "
; All paths to here have a=0 and z flag set
nmshw9: if	extract
	 call	outtst; 	Test if output to disk
	 cnz	opnout; 	If to file, create it (outfcb), set nz
	endif;		extract
;	"       "
	cz	crlflf; 	only when typing
	ret
;
; Output an unsqueezed file/component.
;
zerprt: sta	zerlen; 	Save zero length file flag
;	"       "
asprt:	if	extract
	 push	h
	 lda	outfcb+1;	Kludge to prevent file copying
	 sui	' '
	 jnz	asprt0
	 lxi	h,nouqz
	 ora	m;		If we override unsq/uncr
	 jnz	abort;		we would trash the screen.
	 sta	outflg; 	i.e. a zero, following fails
asprt0:  call	outtst; 	Test if output to disk
	 cnz	opnout; 	If to file, create it (outfcb), sets nz
	 pop	h
	 cz	crlflf; 	if typing. Opened output has set nz
	else;		not extract
	 call	crlflf; 	we are typeing
	endif;		not extract
;	"       "
	lda	zerlen
	ora	a
	jnz	done;		Don't type anything for 0-length file
	mov	a,l;		Print
	push	h
	call	crtype; 	First
	pop	h;		(file out clobbers hl)
	mov	a,h;		Two
;	"       "
asprt1: call	crtype; 	Bytes
	call	lbrget; 	Get a byte from the file
	jz	asprt1; 	Not eof, print and get more
	jmp	done
;
; Done, send message
donem:	call	tstr
;	"       '
; Done, no message
done:	lxi	sp,stack;	SP uncertain here - reset the stack
;	"       "
	if	extract
	 call	outtst; 	Test if output to disk
	 cnz	fclose; 	close file if so
	endif;		extract
;	"       "
	jmp	next
;
; Initialize the "next" loop
; a,f,b,h,l
initlp: lda	paglns
	ora	a
	jnz	initl1; 	Paging was not stopped
	sta	lincn1; 	Else clear for fresh start
	lda	pagsiz
	sta	paglns; 	Restart any page pauses
initl1: lxi	h,zeros;	Fill flag area with zeros
	mvi	b,lastz-zeros;	Count of zeroes to load
	xra	a;		Fill (HL) up for (B) with zeroes
initl2: mov	m,a;		Put a byte
	inx	h;		Next location
	dcr	b
	jnz	initl2; 	Fill all 11 bytes
	ret
;
; Open the FILENAME.LBR file and the MEMBER.EXT files, returns Z-flag
; for library extraction, NZ for pure type
;
lbropn: mvi	b,12;		Field size for .LBR file
	lxi	h,fcb1; 	Move first file FCB
	lxi	d,lbrfcb;	To LBRFCB
	call	move
	xra	a;		Set ext & rec # to 0 for proper open
	sta	lbrext
	sta	lbrsno
	lxi	h,fcb2+1;	Source is member FCB name, no drive
	mov	a,m;		First member character
	cpi	' ';		Is it a space or control ?
	jc	helper; 	Control, exit with help
	jz	tfile;		Space, type one file only
	lxi	d,memnam;	Move FCB2 to MEMNAM
	mvi	b,11;		Bytes to move
	call	move;		Member name to local area
;	"       "
; Open the .LBR file
	lxi	h,'BL'
	shld	lbrtyp; 	Force .LBR type
	mvi	a,'R'
	sta	lbrtyp+2
	call	fopnlb; 	Open .LBR file
	inr	a;		Open ok?
	jz	nofile; 	Failure, abort with help
;	"       "
; Read the first record of the library directory
	call	lbread; 	Read a sector
	lhld	lbrbuf+14;	Get directory size
;	"       "
; Test for a valid library file
	lda	lbrbuf
	ora	a;		Test first byte
	lxi	d,corrpt
	jnz	exeunt; 	Non-zero, bad .LBR file
;	"       "
; Read the next library directory record
lbrop5: push	h;		Save DIRSIZE
	cnz	lbread; 	Read a sector, except 1st pass
;	"       "
; Search for the member name in the library directory
	lxi	h,lbrbuf;	Process first entry
	call	addmem; 	To memory buffer
	lxi	h,lbrbuf+20h;	Process second entry
	call	addmem; 	To memory buffer
	lxi	h,lbrbuf+40h;	Process third entry
	call	addmem; 	To memory buffer
	lxi	h,lbrbuf+60h;	Process fourth entry
	call	addmem; 	To memory buffer
	pop	h;		Count of dir entries
	dcx	h;		-1
	mov	a,h;		Zero directory entries left ?
	ora	l
	jnz	lbrop5; 	Now read another directory sector
	ret
;
; The second parameter is missing, just type the main file,
; returns NZ flag to signal no library extraction
;
tfile:	lxi	d,lbrfcb+1
	ldax	d
	dcx	d
	cpi	' '
	jz	helper
	call	fopenf
	inr	a
	jz	nofile
;	"       "
	if	limitt
	 inx	d
	 xchg
	 lxi	d,memfcb
	 mvi	b,11
	 call	move;		Name to memnam for checking
	endif;		limitt
;	"       "
	call	initlp; 	Other one pass initializers
	ori	0ffh;		Set NZ flag
	jmp	initpt; 	Set up pointers, leave NZ flag
;
; Setup access to the library component
; a,f,b,d,e,h,l
lbrset: lxi	d,mbrmsg
	call	tstrc
	lxi	h,memfcb;	Index member FCB
;	"       "
	if	extract
	 push	h
	 lxi	d,outfcb+1
	 mvi	b,11;		Set default output file name
	 call	move;		(will revise if squeezed/crunched)
	 xra	a
	 stax	d;		And clear extent field
	 pop	h
	endif;		extract
;	"       "
	call	fname
	mov	e,m;		Get member starting record LSB
	inx	h
	mov	d,m;		And MSB
	push	d;		Save
	inx	h
	mov	e,m;		Get member size LSB
	inx	h
	mov	d,m;		And MSB
	xchg;			Into 'HL'
	inx	h;		+1
	shld	rcnt;		Save it in record count
	pop	h;		Restore starting record number
	shld	lbrrno
	xra	a
	sta	lbrrno+2;	Set random rcd no
	sta	lbrext
	call	fopnlb; 	Open the LBR file again
	inr	a
	jz	preeof; 	Should not happen
	mvi	a,frdran
	call	sys;		Do a random read to put in sequential
	ora	a
	jnz	preeof; 	No such record
;	"       '
; Initialize pointers to read from LBRFCB
; a,h,l (flags preserved)
initpt: mvi	a,080h
	sta	chrcnt; 	Set char count to force read
	lxi	h,lbrbuf-1
	shld	bufptr
	ret
;
; Get a byte from the .LBR member. GETBYT for UNCREL. GLZHUN for UNLZH use
; a,f,d,e,h,l
glzhun:
getbyt:
lbrget: lda	chrcnt; 	Get pointer
	inr	a;		Point to next position
	sta	chrcnt; 	Put pointer back
	jp	lbrge1; 	Buffer not empty
	call	zbuff;		Empty, reset pointers, read sector
	lhld	rcnt;		Get record count
	dcx	h;		-1
	shld	rcnt;		Set new record count
	mov	a,l
	ora	h
	jz	lbrge2; 	If all records read
	call	lbread; 	Read a sector
	ora	a
	jnz	lbrge2; 	If read was unsuccessful
;	"       "
lbrge1: lhld	bufptr
	inx	h
	shld	bufptr
	mov	a,m;		No  - get the next byte
	cmp	a;		Set zero - no error
	ret
;
lbrge2: mvi	a,0ffh
	ora	a
	ret;			Return non-zero for error
;
; Zero the buffer pointers (for reaccess from start)
; a,f,h,l
zbuff:	xra	a;		Empty, read another record
	sta	chrcnt; 	Clear the character count
	lxi	h,lbrbuf-1
	shld	bufptr
	ret
;
; Read a sector from library file
; a,f,d,e
lbread: mvi	a,fread
	lxi	d,lbrfcb;	.LBR FCB
	jmp	sys;		Read a block, and exit
;
; Get 2 bytes from input file into HL
; a,f,d,e,h,l
get2hl: call	lbrget; 	Get a byte from the input file
	rnz;			May be an empty component
	push	psw
	call	lbrget; 	Get a byte from the input file
	mov	h,a
	pop	psw;		first byte of the pair
	mov	l,a
	ret
;
nofile: lxi	d,nofmsg
	jmp	exeunt
;
helper: lxi	d,signon;	Give name, version, credit
	call	tstr
	lxi	d,usage;	Give help menu
	call	tstrc
	call	qwhl
	lxi	d,whlusg
	cnz	tstrc;		Added help when wheel enabled
	jmp	exit;		without wasting CRT space.
;
preeof: lxi	d,eofmsg
;	"       "
; Error exit, crlf and message (DE)^
exeunt: call	tstrc;		Print message
;	"       "
exit:	if	duspec; 	Restore entry conditions
	 call	restor
	endif;		duspec
;	"       "
	lhld	stack
	sphl;			Restore original stack
	ret;			--exit-- to cp/m
;
	if	extract
; Close any output file
; a,f,d,e,h,l 
fclose:  lda	bytes;		Get bytes in current sector
	 ora	a
	 jz	fcls2;		None
	 lhld	outptr; 	Get buffer pointer
fcls1:	 mvi	m,eof;		Put in EOF
	 inx	h;		Bump pointer
	 inr	a;		And byte count
	 jp	fcls1;		Fill out sector with EOF
	 lda	sector
	 inr	a
	 sta	sector; 	Bump sector count
fcls2:	 call	fput;		Write buffer, maybe
	 lda	outusr; 	Shift to output user
	 call	sguser
	 lxi	d,outfcb
	 mvi	a,close
	 call	sys
	 lda	inuser
	 jmp	sguser; 	Restore and exit
;
; Put character (TOS) to file.	Unclean hook for CRTYPE.
; a,f,h,l
fputch:  pop	psw;		Get char. to ACC.
	 lhld	outptr; 	Pointer
	 mov	m,a;		Put char in buffer
	 inx	h
	 shld	outptr; 	Update pointer
	 lda	bytes
	 inr	a
	 sta	bytes;		Update bytes in this sector
	 rp;			Exit if not full sector
	 xra	a
	 sta	bytes;		Init. byte count
	 lda	sector
	 inr	a
	 sta	sector; 	Update sectors in buffer
	 cpi	rec;		Buffer full?
	 rnz;			Exit if not
;	 "      "
; Write output buffer to file
; a,f,h,l
fput:	 push	d
	 lda	outusr; 	User for output file
	 call	sguser
	 call	pbuf;		Write the sectors
	 lxi	d,lbrbuf
	 mvi	a,setdma;	Back to input buffer
	 call	sys
	 lda	inuser; 	Restore input user
	 call	sguser
;	 "      "
; This code allows aborting a disk file output
	 call	cistat; 	Check for console input
	 jz	fput2;		Continue if none
	 cpi	'C'-40h
	 jz	abort;		Abort on CTL-C
fput2:	 pop	d;		Else continue
	 ret
;
; Unload the stored buffer (up to rec sectors long)
; a,f,d,e,h,l
pbuf:	 lda	sector; 	Get sector count
	 inr	a;		Prepare for upcomming DCR A
	 lxi	h,outbuff;	Start of output buffer
	 shld	outptr; 	Init. byte pointer
pbuf1:	 shld	dmaadd; 	 and write address
	 dcr	a
	 sta	sector; 	Update sector count
	 rz;			Exit if none left to write
	 push	psw;		Save sector count
	 xchg;			Setup DE
	 mvi	a,setdma;	Write address
	 call	sys
	 lxi	d,outfcb
	 mvi	a,fwrt; 	Now write it
	 call	sys
	 ora	a
	 jnz	fullup; 	Write error, abort everything
	 lhld	dmaadd
	 lxi	d,128;		Get next write address
	 dad	d
	 pop	psw;		Get sector count
	 jmp	pbuf1;		And loop
	endif;		extract
;
; Output to CRT or file.  Entry name "out" for UNC or UNCREL, 'plzhun' for
; UNLZH or UNL use
; CRT output tracks column and line.
; a,f,e,h,l
plzhun:
out:
crtype: if	extract
	 push	psw
	 call	outtst; 	Test if output to disk
	 jnz	fputch
	 pop	psw
	endif;		extract
;	"       "
; Not creating a file, this char goes to the console.
	cpi	eof
	jz	done;		EOF on 01Ah for ASCII output
;	"       "
	if	xpnd
	 push	psw
	 lda	dleflg
	 ora	a
	 jnz	crtyp6
	 pop	psw
	 cpi	dle
	 jnz	crtyp1
	 lda	xpdle
	 ora	a
	 jnz	crtyp5
	 mvi	a,dle
	endif;		xpnd
;	"       "
crtyp1: ani	7fh;		Make sure its ASCII
	push	psw;		Save the character
	call	cout
	lda	pawsfg
	ora	a
	cnz	pauser; 	For slow terminals (Braille, etc)
	pop	psw;		Restore character
	cpi	lf;		Was it a line feed
	rnz;			No - continue
	call	qwhl;		Load wheel byte if enabled
	jnz	crtyp2; 	Bypass line count tests on wheel
;	"       "
	if	limitl; 	Check for too many lines typed
	 lda	lincnt; 	Advance line counter
	 inr	a
	 sta	lincnt
	 mov	b,a;		Line number in 'B'
	 lda	maxlin; 	Max number of lines to type
	 ora	a;		Test flag
	 jz	crtyp2; 	If null function
	 cmp	b;		Else compare to max lines
	 lxi	d,excess
	 jz	donem;		Announce too much
	endif;		limitl
;	"       "
crtyp2: lda	lincn1; 	Get line counter
	mov	b,a;		Keep in 'B'
	lda	paglns; 	Number of lines per page
	dcr	a;		Decrement and test flag
	jm	pauser; 	Function is null
	cmp	b;		Compare to lines per page
	jnc	pauser; 	If not at maximum count
	xra	a;		Clear lines counter
	sta	lincn1
	lxi	d,more
	call	tstr;		Announce the pause
	call	pause;		Get input from console
	cpi	' '-eof;	Space for line at a time?
	jnz	crtyp3
	mov	a,b;		Get original line count back
	dcr	a;		And set to "one line left"
	sta	lincn1
crtyp3: lxi	d,clean
	call	tstr;		Clear out the "[more]"
;	"       "
; Check for user pause or abort
; a,f,e (unless aborted)
pauser: call	cistat; 	Check the keyboard
	call	pschk
	cpi	'S'-40h;	CTL-S to pause?
	jz	pause
	ani	5fh
	cpi	'S'
	rnz;			Not CTL-S, return
;	"       "
; Returns input-01Ah.	Aborts on c,C,^C or k,K,^K - next on CTL-X, etc.
; a,f,e
pause:	call	cistat; 	Check the keyboard
	jz	pause;		Nothing yet so wait
	call	pschk
	sui	eof
	rnz;			Not ^Z
	sta	paglns; 	End pauses permanently on ^Z
	ret
;
	if	xpnd
crtyp6:  xra	a;		Indentation expansion
	 sta	dleflg
	 pop	psw
	 sui	' '
	 rc
	 rz;			Else 'A' is count of spaces to send
crtyp7:  sta	dlecnt
	 mvi	e,' '
	 call	cout3
	 lda	dlecnt
	 dcr	a
	 jnz	crtyp7
crtyp5:  sta	dleflg
	 ret
	endif;		xpnd
;
; Console status
; a,f,e
cistat: mvi	a,cio;		BDOS function
	mvi	e,0ffh
	call	sys;		Direct console in call
	ora	a;		Was a key entered ?
	ret
;
; Pause check for special characters
; a,f (unless aborted)
pschk:	cpi	'C'-40h;	Want to abort?
	jz	abort;		If yes, quit
	cpi	'K'-40h
	jz	abort
	cpi	'X'-40h;	Jumping to next file?
	jz	pschk1
	cpi	' ';		Space for "line at a time"?
	rz
	ani	5fh;		Insure in upper case
	cpi	'C'
	jz	abort
	cpi	'K'
	jz	abort
	cpi	'X'
	rnz;			If not, keep going
pschk1: call	crlf
	jmp	done;		Next file on CTL-X
;
; Abort the run with message
abort:	lxi	d,abrmsg
	jmp	exeunt
;
; Output char a to console, expanding tabs, tracking column
; a,f,e
cout:	mov	e,a;		Save output character
	cpi	tab
	jz	tabber; 	Expand a tab
	cpi	cr;		Carriage return
	jnz	cout1
	xra	a;		CR sets COLUMN to 0
	jmp	cout4

cout1:	cpi	bs;		Is char a backspace?
	jnz	cout2
	lda	column; 	Backspace sets COLUMN back one
	dcr	a
	rm;			Ignore if at column 0 already
	jmp	cout4

cout2:	cpi	' ';		
	jc	coute;		Other controls don't affect COLUMN
;	"       "
; Callable entry point here to display e
cout3:	lda	column; 	Advance column counter
	inr	a
cout4:	sta	column
;	"       "
; Output e to console if printable
coute:	call	qctl;		Test control
	mov	a,e;		Get char back
	jz	ctype;		no suppression, print the char
	ret;			absorb it, Return to caller
;
; Expand a tab, at least one space emitted
tabber: mvi	e,' '
	call	cout3;		Print a space
	lda	column
	ani	7;		At next tab stop ?
	jnz	tabber; 	Yes, continue
	ret
;
; Test for control char output (from e). Z flag for no suppression.
; a,f
qctl:	lda	ctlflg; 	Get controls active
	ora	a;		Test flag
	rz;			Return if not
	mov	a,e;		Get output char
	cpi	' '
	jnc	qctl1;		Not control, clear flags
	cpi	cr
	rz
	cpi	bs
	rz
	cpi	bell
	rz
	cpi	lf
	rnz;			Non-typable control char
qctl1:	cmp	a;		Set Z-flag, typable
	ret
;
	if	limitt; 	Test for typable file
; Test for typeable file, abort with message to "DONE" if not
; a,f,b,d,e,h,l
tsttyp:  lda	typflg; 	Get test flag
	 ora	a;		Test it
	 rz;			Return if ok to type all types
;	 "      "
	 if	extract; AND limitt
	  call	outtst; 	Test if output to disk
	  rnz;			Return if outputting to disk
	 endif; 	extract AND limitt
;	 "      "
	 mvi	b,3
	 lxi	h,badtbl-3;	Index bad file type table
tstty1:  inx	h;		Next table address pointer
	 dcr	b;		Bump loop counter
	 jnz	tstty1; 	Do until at next table entry
	 mov	a,m;		Get a byte
	 ora	a
	 rz;			end of table - ok to type this one
	 mvi	b,3;		3 char extension
	 lxi	d,memfcb+8;	Index file name extension
tstty2:  ldax	d;		Get a byte from extension
	 ani	7fh;		Strip any file attribute bits
	 cmp	m
	 jz	tstty3; 	Match, continue scan
	 mov	a,m
	 cpi	'?';		'?' in table matches all
	 jnz	tstty1; 	No match, next entry
tstty3:  inx	h;		Bump table address pointer
	 inx	d;		Bump extent pointer
	 dcr	b;		Bump counter
	 jnz	tstty2; 	Continue for 3 chars
	 lxi	h,memfcb+8;	User name
	 lxi	d,cant
	 call	tstr;		"CAN't type a '"
	 mvi	b,3;		3 byte file type
tstty5:  mov	a,m;		Get byte
	 call	ctype;		Give a chance to abort here
	 inx	h;		Next byte
	 dcr	b
	 jnz	tstty5; 	Type all 3 bytes
	 lxi	d,cant2;	"' FILE ",CR,LF
	 jmp	donem;		And do next file
	endif;		limitt
;
; This part is adapted from TYPE109 by David Rand
; a,f,b,c,d,e,h,l
getsqb: lda	rptcnt; 	Get repeat flag
	ora	a;		Any chars to repeat ?
	jnz	getsq1; 	Yes - get and count
	call	nxtch;		Get a character
	rc;			Eof
	cpi	sqzdle; 	Repeat byte flag
	jnz	getsq3; 	No -
	call	nxtch;		Yes - get another character
	rc;			EOF
	ora	a;		If null
	jnz	getsq2
	mvi	a,sqzdle;	Dle is encoded as dle,0
	ret;			Return with it, carry clear
;
getsq2: dcr	a;		Bump counter twice
	jz	getsqb; 	1 repeat is a null event
getsq1: dcr	a
	sta	rptcnt; 	Set repeat count
	lda	rptchr; 	Return repeat character
getsq3: sta	rptchr; 	Set repeat char
	ora	a;		Clear any carry, not EOF
	ret
;
; Next decoded byte from file, ignoring repeat characters
; a,f,b,c,d,e,h,l
nxtch:	lxi	d,0;		Pointer @ star of text
	lda	char
	mov	c,a
nxtch1: lda	numflt
	ora	a
	jnz	nxtch2
	push	d;		Save 'DE'
	call	lbrget; 	Get a byte from the input file
	jnz	preeof; 	Not expecting an eof here
	pop	d;		Restore 'DE'
	mov	c,a
	mvi	a,8;		'A' is counter
nxtch2: dcr	a;		Bump count
	sta	numflt; 	Save it
	mov	a,c;		Get character
	rrc;			Shift right
	mov	c,a;		Save character
	push	psw;		Save character
	lxi	h,xlatbl;	Index ram area
	dad	d;		HL=HL+(4*DE)
	dad	d
	dad	d
	dad	d
	pop	psw;		Restore char
	jnc	nxtch3; 	If no carry
	inx	h
	inx	h
nxtch3: mov	e,m
	inx	h
	mov	d,m
	mov	a,d
	ani	80h
	jz	nxtch1
	mov	a,c
	sta	char
	mov	a,d
	cpi	0feh;		Special end of file ?
	mvi	a,eof;		Yes - return with EOF character
	stc
	rz;			And carry for EOF
	mov	a,e
	cmc
	cma
	ret;			With carry clear, not EOF
;
; Set up the translation table for the squeezed file
; a,f,d,e,h,l
sqsetu: call	get2hl; 	Get 2 bytes from input file into HL
	lxi	d,xlatbl;	Index ram area
sqset1: mov	a,h;		Get MSB
	ora	l;		Test LSB
	rz
	push	h;		Save table size counter
	push	d;		Save ram area index
	call	get2hl; 	Get 2 bytes from input file into HL
	pop	d;		Restore ram area index
	xchg;			Into 'HL'
	mov	m,e;		Save the LSB byte
	inx	h
	mov	m,d;		And MSB byte
	inx	h
	push	h;		Bump & save pointer
	call	get2hl; 	Get 2 bytes from input file into HL
	xchg;			Into DE
	pop	h;		Restore pointer
	mov	m,e;		Save the LSB byte
	inx	h
	mov	m,d;		And the MSB byte
	inx	h;		Bump pointer
	xchg;			Restore pointer to 'DE'
	pop	h;		Restore table size counter
	dcx	h;		Decrement it the byte count
	jmp	sqset1; 	And loop for more
;
; Add a library member to the name queue buffer if a match to MEMNAM
; a,f,b,d,e,h,l
addmem: mov	a,m;		Get first byte of member entry
	ora	a
	rnz;			Non zero - must be deleted or null entry
	inx	h;		Go to the second byte
	push	h;		Save source address for coming 'LDIR'
	push	h;		Save it again
	mvi	b,11;		11 byte filename
addme0: mov	a,m;		Get byte
	cpi	' '
	jnz	addme1; 	Not space - continue
	inx	h;		Next char
	dcr	b
	jnz	addme0; 	Continue searching for spaces
	pop	h;		Must be the directory
	jmp	addme4; 	So abort this one
;
addme1: pop	h
	lxi	d,memnam;	Index member FCB name
	mvi	b,11;		11 byte compare
addme2: ldax	d;		Get byte from member name FCB
	cpi	'?';		'?' matches all entries
	jz	addme3; 	Match
	cmp	m;		Same as member entry?
	jnz	addme4; 	No - abort this process
addme3: inx	h
	inx	d
	dcr	b
	jnz	addme2; 	Compare all 11 bytes
	lhld	namptr; 	Get destination address
	xchg
	pop	h;		Get source address back again
	mvi	b,15
	call	move;		Move 15 byte block into memory
	xchg
	shld	namptr; 	Save name pointer
	lhld	memcnt; 	Get member number count
	inx	h;		Bump it up one
	shld	memcnt; 	Set next member memory address
	ret

addme4: pop	h;		Balance stack
	ret
;
; Get the next member name from the memory name queue buffer, 
; return carry set if no more members left
; a,f,b,d,e,h,l
getmem: lhld	memcnt; 	Get member count
	mov	a,l
	ora	h
	stc
	rz;			Zero count - set error condition
	dcx	h;		Bump count down
	shld	memcnt; 	And reset member count
	lhld	nampt1; 	Get source address for move
	lxi	d,memfcb;	Get destination for move
	mvi	b,15;		11 byte filename + 4 byte file info
	call	move;		The block
	shld	nampt1; 	Reset the next source address
	ora	a;		Clear any cy
	ret
;
; Double CRLF to console
; a
crlflf: call	crlf
;	"       "
; CR and LF to console
; a
crlf:	mvi	a,cr
	call	ctype
	mvi	a,lf
;	"       "
; Character to console, preserve all registers, track lines
ctype:	push	psw
	push	d
	mov	e,a
	cpi	lf
	jnz	ctype1
	lda	lincn1
	inr	a
	sta	lincn1
ctype1: mvi	a,cio;		Direct console output
	call	sys
	pop	d
	pop	psw
	ret
;
; CRLF, then fall through to TSTR
; a,f
tstrc:	call	crlf
;	"       "
; Output string (DE)^
; a,f
tstr:	push	d
tstr1:	ldax	d
	ora	a
	jz	tstrx
	call	ctype
	inx	d
	jmp	tstr1
;
tstrx:	pop	d
	ret
;
	if	extract
; Query for file purge
; a,f
query:	 push	d
	 lxi	d,exists
	 call	tstr
	 call	pause
	 pop	d
	 adi	eof;		Correct output
	 ani	05fh;		Upshift
	 call	ctype;		And echo
;	 push	psw
;	 call	crlf
;	 pop	psw
	 cpi	'Y'
	 ret;			Purge wanted if z flag
;
; Open (create) file OUTFCB^. Returns NZ when all well
; a,f,d,e
opnout:  lda	outusr
	 call	sguser
	 lxi	d,outfcb
	 mvi	a,open
	 call	sys
	 inr	a
	 ora	a;		Ensure carry reset
	 cnz	query;		When file already exists
	 jnz	done;		Abort if not to be purged
	 xra	a
	 sta	bytes;		Reset byte count
	 sta	sector; 	and sector count
	 sta	outfcb+32;	Reset output record number
	 mvi	a,delete
	 call	sys;		Remove any old file
	 mvi	a,make
	 call	sys;		And create a new one
	 inr	a
	 push	psw
	 lda	inuser
	 call	sguser; 	Go back to input access
	 pop	psw
	 rnz;			All well
;	 "      "
; Fullup message and abort
fullup:  lxi	d,noroom
	 jmp	exeunt; 	Abort everything
	endif;		extract
;
; Open LBRFCB file
; a,f
fopnlb: lxi	d,lbrfcb
;	"       "
; Open file (DE)^, doing any searches, return BDOS response
; a,f
fopenf: call	fopen
	cpi	0ffh
	rnz;			Success
	ldax	d
	ora	a
	mvi	a,0ffh; 	Failure flag
	rnz;			Because a drive was specified
	lda	altdrv; 	Set system drive and retry
	stax	d;		(a zero value causes useless retry)
;	"       "
; Open file (DE)^, return BDOS response in (A),
; assumes DMA is preset to LBRBUF.
; a,f
fopen:	if	limitt; 	Ensure non-ambiguous name
	 mvi	a,srchf
	 call	sys;		Search for openee
	 cpi	0ffh
	 rz;			Failure, not found
	 push	h
	 push	d
	 push	b
	 lxi	h,lbrbuf+1;	Copy the non-ambiguous name
	 add	a
	 add	a;		4*
	 add	a
	 add	a;		16*
	 add	a;		32*
	 add	l
	 mov	l,a;		Index into the DMA area
	 adc	h
	 sub	l
	 mov	h,a;		Point to the "found" file
	 mvi	b,11
	 inx	d
	 call	move;		Exact name into FCB
	 pop	b
	 pop	d
	 pop	h
	endif;		limitt
;	"       "
	mvi	a,open
;	"       '
; Execute BDOS function (A), preserve registers
; a,f
sys:	push	h
	push	d
	push	b
	mov	c,a
	call	bdos
	pop	b
	pop	d
	pop	h
	ret
;
; Move (B) bytes from (HL)^ to (DE)^
; a,f,b,d,e,h,l
move:	mov	a,m
	stax	d
	inx	h
	inx	d
	dcr	b
	jnz	move
	ret
;
; List filename from FCB (HL)^
; a,f,h,l
fname:	push	b
	mvi	b,8;		Size of name field
fname1: mov	a,m;		Get a byte
	ani	07fh
	cpi	' '
	cnz	ctype;		print non-space
	inx	h;		Next char
	dcr	b
	jnz	fname1; 	Continue name
	mvi	a,'.'
	call	ctype;		Print seperator
	mvi	b,3;		3 character extent
fname2: mov	a,m;		Get byte
	ani	07fh
	cpi	' '
	cnz	ctype;		print non-space
	inx	h;		Next char
	dcr	b
	jnz	fname2; 	Continue ext
	pop	b
	ret
;
; This must parse the command line into FCB1 and FCB2, setting the
; appropriate drives and user values, and saving old values.  Note that
; this routine should NOT assume that the command line is upshifted, to
; allow use with CCPLUS when the noupshift option is selected, and thus
; enabling use of Software Tools etc.  This routine does not deny
; access to the currently logged in drive or user, so that a privileged
; operator can always use the program on his own area, even though
; normally restricted.	Note that the fact of logging in establishes
; the existance of the drive unit.
	if	duspec; 	Parse command line
parse:	 mvi	a,getdrv;	Save entry drive
	 call	sys
	 sta	olddrv
	 call	getusr; 	and user values
	 sta	oldusr; 	first so any error restores
	 lxi	h,cmdln
	 mov	a,m;		Line length
	 push	h
	 inr	a
	 add	l
	 mov	l,a
	 mvi	m,0;		Mark line end
;	 "      "
; Check for command line option $U to toggle unsq/uncr flag
	 mvi	a,' '
parse1:  dcx	h
	 cmp	m
	 jz	parse1; 	ignore trailing blanks
	 mov	a,m
	 ani	05fh
	 cpi	'U'
	 jnz	parse4; 	not $U option
	 dcx	h;		Backup 3 chars.
	 mov	a,m
	 cpi	'$'
	 jnz	parse4; 	not $U option
	 dcx	h
	 mov	a,m
	 cpi	' '
	 jnz	parse4; 	not delimited, may be part of fname
	 mvi	m,0;		Found " $U" at end, remove from line
	 lda	nouqz;		Get flag
	 cma;			Toggle it
	 sta	nouqz;		And put it back
;	 "      "
parse4:  pop	h;		line beginning ptr
	 lxi	d,fcb1
	 call	skipbl; 	Skip any leading blanks
	 call	setdu
	 jc	helper; 	Bad specification
	 mov	b,a
	 lda	oldusr
	 cmp	b
	 jz	parse6; 	User ok if already logged on to it
	 lda	maxusr; 	Load maximum user value
parse5:  cmp	b
	 jc	noacc;		Illegal on system
parse6:  push	d
	 mov	a,b
;	 "      "
	 if	extract
	  sta	inuser; 	Preserve for output flipping
	 endif; 	extract
;	 "      "
	 sta	savusr; 	Save for later
	 pop	d
	 ldax	d
	 call	chkdv
	 jc	noacc;		No access or no such drive
	 inx	d;		Drive is already set
	 call	setnam; 	Setup first file name
	 cnz	skipbl; 	Dont skip past eol marker
	 lxi	d,fcb2
	 xra	a
	 stax	d;		Kill the 2nd drive spec.
;	 "      "
	 if	extract
	  sta	outflg; 	Default no output file created
	 endif; 	extract
;	 "      "
	 call	setdu;		To skip over any du spec
	 jc	helper
;	 "      '
	 if	extract
	  jz	parse7; 	No DU specified
	  mov	a,b
	  sta	outusr
	  ldax	d
	  sta	outfcb
	  xra	a
	  stax	d;		Reset drive field.
	  dcr	a;		To 0ffh
	  sta	outflg; 	Flag doing output
	 else	;	NOT extract
	  jnz	helper
	 endif; 	NOT extract
;	 "      "
parse7:  inx	d
;	 "      "
; Parse filename from (HL)^ into FCB at (DE)^
; Z-flag if end of line reached
; a,f,b,d,e,h,l
setnam:  mvi	b,8
	 call	setfld
	 cpi	'.'
	 jnz	setnm2;        No extension specified
	 inx	h
setnm2: mvi    b,3
;	 "      "
; Set field length (B) in (DE)^ from (HL)^ up, blank padding, Z- flag
; for end of line.  Leave HL pointing to delimiter.  (a) := delimiter.
; Truncate any fields longer than expected.
; a,f,b,d,e,h,l
setfld:  call	qdelim; 	Load and upshift char
	 jz	setfd4;        Delimiter, go blank pad
	 cpi	'*'
	 jnz	setfd1
	 mvi	a,'?';		Expand '*' to '?'s
	 jmp	setfd2;        Without advancing input pointr

setfd1: inx    h
setfd2: stax   d
	 inx	d
	 dcr	b
	 jnz	setfld
setfd3: call   qdelim;	       Skip to first delimiter
	 jz	setfd5;        At a delimiter
	 inx	h;		Else truncate
	 jmp	setfd3

setfd4: mvi    a,' ';	       Blank pad
	 stax	d
	 inx	d
	 dcr	b
	 jnz	setfd4
setfd5: mov    a,m
	 ora	a;		Z flag for eol only
	 ret
;
; (A) := (HL)^ upshifted.  Z flag if a delimiter for file names
; a,f
qdelim:  mov	a,m
	 ani	07fh;		Just in case
	 call	upshft
	 cpi	07fh
	 rz;			A rubout is a delimiter
	 cpi	'='
	 rz
	 cpi	','
	 rz
	 cpi	'_'
	 rz
	 cpi	'.'
	 rz
	 cpi	':'
	 rz
	 cpi	';'
	 rz
	 cpi	'<'
	 rz
	 cpi	'>'
	 rz
	 cpi	'['
	 rz
	 cpi	']'
	 rz
	 cpi	' '
	 rz
	 rnc;			
	 cmp	a;		All controls are delimiters
	 ret;			Including EOL
;
; Skip to non-blank in (hl)^.  Z-flag if EOL reached.
; At entry (HL) points to byte previous to first testee.
; a,f,h,l
skipbl:  inx	h
	 mov	a,m
	 ora	a
	 rz;			Empty line, Z-flag
	 cpi	' '
	 jz	skipbl; 	Skip leading blanks
	 ret;			Nz, found something
;
; No access exit
noacc:	 lxi	d,illegl
	 jmp	exeunt
;
; Parse drive/user from line (HL)^  return user in (A), set drive in (DE)^
; At exit (HL) points past any drive/user specification. Drive/user
; validity is not checked here.  Carry for any error, z-flag for no entry
; a,f,b,c,h,l
setdu:	 mvi	a,':';		Scan for ':'
	 call	scan;		Returns (A) = position
	 jc	setdu2; 	No drive/user specified
	 cpi	4
	 jnc	setdu2; 	Not in 1st 4 characters, not du spec
	 mov	c,a;		Save position
	 mov	a,m
	 call	upshft
	 sui	'@'
	 rc;			Illegal, catches initial ':'
	 stax	d;		Preventing any searches unless '@'
	 inx	h
	 call	getusr; 	Default, if none specified
	 mov	b,a
	 dcr	c;		Range 0...2
	 jz	setdu1; 	No user spec
	 mvi	b,0
	 call	decin
	 rc;			Illegal character
	 dcr	c;		Range 0..1
	 jz	setdu1; 	One char in user spec only
	 call	decin
	 rc;			Illegal character
setdu1:  ori	1;		Reset Z-flag, have values
	 mov	a,b
	 inx	h;		Now point past the ':'
	 ret

setdu2:  call	getusr; 	No spec, set z flag & default usr
	 cmp	a;		Set z flag
	 ret
;
; Return current user in (A)
; a,f
getusr:  mvi	a,0ffh
;	 "      "
; Set current user to (A)
; a,f
sguser:  push	d
	 mov	e,a
	 mvi	a,gsuser
	 call	sys
	 pop	d
	 ret
;
; Check for valid drive specification (A).  Carry for invalid value.
; Uses global "DRVSUP" value and "OLDDRV" values
; a,f
chkdv:	 ora	a
	 rz;			Default is always ok
	 push	h
	 lhld	olddrv
	 cmp	l
	 pop	h;		Specific reference to
	 rz;			Logged in drive is ok
	 push	h
	 lhld	drvsup
chkdv2:  dad	h
	 dcr	a
	 jnz	chkdv2; 	Shift access bit into carry
	 pop	h;		Carry for access at this point
	 cmc
	 ret
;
; Incorporate character (HL)^ into the value (E) (decimal).
; Carry for llegal character, else advance (HL).
; a,f,b
decin:	 mov	a,m
	 sui	'0'
	 rc;			Illegal
	 cpi	9+1
	 cmc
	 rc;			Illegal
	 inx	h
	 push	psw
	 mov	a,b
	 add	a;		2*
	 add	a;		4*
	 add	b;		5*
	 add	a;		10*
	 mov	b,a
	 pop	psw
	 add	b
	 mov	b,a
	 ret
;
; Scan (HL) up for (A) or to EOL (marked by null).
; Return (A) = relative position (0 based), carry if not found.
; a,f
scan:	 push	b
	 push	h
	 mvi	c,-1
	 dcx	h
scan1:	 inx	h
	 inr	c
	 cmp	m
	 jz	scan2;		Found, carry clear
	 push	b;		Check for end of string
	 mov	c,m;		This test occurs after the char test
	 inr	c;		So that a null will be found. Else
	 dcr	c;		A null signifies end of string.
	 pop	b;		Don't play with memory, may be ROM.
	 jnz	scan1;		Not EOS, continue
	 stc;			End of line, not found
scan2:	 mov	a,c
	 pop	h
	 pop	b
	 ret
;
; This restores the entry drive/user value
; a,f,e
restor:  lda	oldusr
	 call	sguser
	 lda	olddrv
	 mov	e,a
	 mvi	a,setdrv
	 call	sys
	 ret
;
; Upshift (A)
; a,f
upshft:  cpi	'a'
	 rc
	 cpi	'z'+1
	 rnc
	 ani	05fh
	 ret
	endif;		duspec
;
	if	extract
; See if we use wheel test.  If so, test wheel byte.
; Returns Z set if wheel not set, or no file output,
; else returns with Z not set for output to file.
; a,f
outtst:  call	qwhl
	 rz;			wheel not set
	 lda	outflg; 	Get output flag
	 ora	a;		True for output to file
	 ret
	endif;		extract
;
; Cheek wheel status.  WHLADR = 0 acts as if wheel on, else checks
; NZ flag for priviledged operation
; a,f
qwhl:	push	h
	lhld	whladr
	mov	a,m;		Gets 0c3 or 0cd at lcn 0 if whladr=0
	pop	h
	ora	a;		Non-zero means priviledged
	ret
;
nfound: db	'Member '
nofmsg: db	'Not found',cr,lf,lf,0

signon: db	cr,lf,'LT',ver/10+'0',ver mod 10+'0'
	db	' [d[u]:]lbr/filename [d[u]:][component] [$u]'
	db	'     by C.B. Falconer',cr,lf
sign1:	db	'^S pause, ^C abort, ^X next'
	db		' file, ^Z stops paging',0

usage:	db	cr,lf,'   Type/uncrunch/unsqueeze/unLZH '
	db		'files or LBR members',cr,lf
	db	'   Drive/user after lbr/filename '
	db		'causes file output.',cr,lf
	db	'   $U at end disables unsq/uncr/unlzh '
	db		'of compressed files',cr,lf
	db	'   (wildcards permitted)'
	db	cr,lf,lf
	db	'Examples:',cr,lf
	db	'   B>LT A3:HELLO SOURCE.AZM     '
	db		'   [console]     (defaults to HELLO.LBR)',cr,lf
	db	'   A>LT LZHENCOD.DYC            '
	db		'   [console]     (handles LZH encoding)',cr,lf
	db	'   A>LT SQUEEZE.DQC             '
	db		'   [console]',cr,lf
	db	'   A>LT HELLO *.*               '
	db		'   [console, all typable]',0
;
; 2nd part when wheel set or disabled
whlusg: db	'   B>LT B:HELLO A4:SOURCE.AQM   '
	db		'   [file]',cr,lf
	db	'   B>LT B:HELLO A4:SOURCE.AQM $U'
	db		'   [file with no unsqueeze]',cr,lf
	db	'   A>LT B:CRUNCH.AZM A:         '
	db		'   [file]',0

abrmsg: db	cr,lf,'++ ABORTED ++',cr,lf,0
clean:	db	cr,'        ',cr,0;		Erase the "more"
eofmsg: db	'Early EOF, aborted',0
exists: db	' exists, purge (y/n)? ',0
ffsep:	db	' =>> ',0;			File-file separator
corrpt: db	'LBR file corrupt',0
mbrmsg: db	'Member: ',0
more:	db	'[more] ',0
badfile:db	cr,lf,lf,bell,'Corrupt or unknown format file',cr,lf,0

	if	limitt
cant:	 db	bell," ...Can't type a '",0
cant2:	 db	"' file",cr,lf,0
	endif;		limitt

	if	extract
noroom:  db	cr,lf,lf,bell,'No space, aborting',0
	endif;		extract

	if	limitl
excess:  db	bell,'Too long, transfer with KMD',cr,lf,0
	endif;		limitl

	if	duspec
illegl:  db	bell,'Access denied',cr,lf,0
	endif;		duspec
;
; -------------------------------------------------------------------
;
	dseg
; Link the data segment after the code.  Use 2 passes if necessary.
;
; Temporary storage area
bufptr: dw	0
chrcnt: db	0
	db	0
lincn1: db	0;		Lines printed since [more]
paglns: db	0;		Ln/page before pause, 0 causes setup

nampt1: dw	endu;		NAMBUF
namptr: dw	endu;		NAMBUF
rcnt:	dw	65535;		Maximum. record count for type

outptr: dw	outbuff;	File output buffer
; It is helpful to ensure that the LAST initialized data item
; is NON-ZERO.	Eases any final truncation, ensures CCITCRC can run.
;;
; This (uninitialized) portion takes up no space in the code file
; (with reasonable linkers - l80 zero fills)
bytes:	ds	1;		in current outbuff sector
sector: ds	1;		no. in outbuff filling
memcnt: ds	2;		members in current library
lbrfcb: ds	9
lbrtyp: ds	3
lbrext: ds	20;		Lbrfcb+12; file extent
lbrsno: ds	1;		Lbrfcb+32; sector #
lbrrno: ds	3;		Lbrfcb+33; random rcd no.
memfcb: ds	16
memnam: ds	16

oldusr: ds	1
olddrv: ds	1
savusr: ds	1

	if	extract
inuser:  ds	1;		To restore after file output
outflg:  ds	1;		Do file output if true
outusr:  ds	1;		Output FCB usr
outfcb:  ds	33;		Output file FCB
dmaadd:  ds	2;		Write address
outbuff: ds	128*rec;	Output buffer
	endif;		extract
;
; Mark start of zeroed area, per component loop
zeros:

numflt: ds	1
char:	ds	1
rptchr: ds	1;		Char to repeat
rptcnt: ds	1;		Count of repeat characters
lincnt: ds	1;		Number of lines printed total
column: ds	1;		Crt column position
zerlen: ds	1
	if	xpnd
dleflg:  ds	1;		Flag a dle just received
dlecnt:  ds	1;		Count of blanks to emit
	endif;		xpnd
lastz:	ds	64;		Mark end of zeroed area, stack space

stack:	ds	2;		Store entry stack pointer
lbrbuf: ds	128;		Member read buffer
xlatbl: ds	258*4

	end	begin
, stack space

