;
;	FILEFIND.ASM
» Originally written bù R® Rodmaî 
; Version 12.0
;
;
; This program will search all preset user areas of all drives for
; any files matching the command tail, then display those files in
; a DIR-like format.  NOTE THAT A DRIVE NEED NOT BE ACTIVE (logged-in)
; TO BE SEARCHED.  By setting the MINUSR and MAXUSR equates to 0 and
; 32, respectively, this program can be made to list ALL files in
; ALL user areas of ALL drives.
;
; Example:  FILEFIND *.COM  lists all .COM files on all drives
;
;
; This program makes a direct call to the SELDSK routine of the CBIOS
;  to determine a drive's existence without causing a BDOS error.
;
; NOTE:	  THIS PROGRAM ASSUMES THAT ANY CBIOS 'ILLEGAL DRIVE SELECT'
;	  TRAPS WILL CAUSE THE SYSTEM TO EXECUTE A WARM-BOOT BY JUMPING
;	  TO ADDRESS 0.  If a select error bypasses the warm-boot jump
;	  at address 0, then the trap in this program will not work.
;
; PLEASE RETURN ANY UPDATES, REPAIRS, ETC. TO TECHNICAL CBBS, Dearborn, MI
;  at (313) 846-6127 (110, 300, 450, 600 Baud, 24 hours) so that we can
;  keep the file properly updated and available to all.
;
; Modification history (in reverse order to minimize reading time):
;
;12/15/82 Modified to run in the 'secure' mode of NZCPR.  If secure is set
;	  ture, then maxuser and maxdrive are taken from preset memory 
;	  locations, except that it can never exceed the maxuser equate set
;	  in this source.  This means, the program only gives information about
;	  those areas to which the user has access.  Joe Dietz   WD5BUV
;
;12/10/82 Modified to make DE register be zero when Drive Select is called.
;	  This makes sure the CBIOS thinks the new drive needs to be logged in.
;	  May be necessary on some CBIOS's  (like Godbout)   Joe Dietz
;
;08/02/81 Cleaned up file and removed unnecessary code.  Thanks to
;	  Tom and Shawn for adding some more goodies.
;	  By Dave Hardy
;
;07/31/81 Modified AVAIL routine to abort only on control-C
;         from the console.  Cleaned up the file, restored
;         original author's name to the heading.
;         By Thomas V. Churbuck
;
;07/31/81 Added tutorial to be printed if filename (FCB)
;         field (fn.ft tail of the command line) is blank
;         (omitted by the inexperienced user). 
;         By Thomas V. Churbuck
;
;07/31/81 Substituted IN-LINE PRINT ROUTINE where appro-
;	  priate for all printed messages.  Prints message
;	  on exit if no files were found (matched).
;	  By Thomas V. Churbuck
;
;07/30/81 Modified AVAIL routine to abort program if any
;	  character is typed from the console.
;	  By Shawn Everson
;
;07/29/81 Modifed user number routine to include leading
;	  0 when printing user #'s 0-9 to line up printout.
;	  By Shawn Everson
;
;07/17/81 Removed DRVFIRST routines and replaced with DRV1ST
;	  routines to make code shorter and more efficient. 
;	  Note that although these changes will make FILEFIND
;	  look MP/M compatible, it actually is NOT.
;	  By Dave Hardy
;
;07/17/81 Modified SHOFIL routines to permit user #-drive OR
;         drive-user # printout to match either MPM or USERLST
;         by providing DRVFIRST conditional
;         By Earl Bockenfeld
;
;07/12/81 Modified SHOFIL routines to always print leading zero
;	  when MULTUSR is TRUE so that multiple user number displays
;	  will align properly.
;	  By Dave Hardy
;
;07/08/81 Added MULTUSR routines to allow program to be conditionally
;	  assembled to check a preset range of user areas.  With MINUSR
;	  and MAXUSR (below) set to 0 and 32, respectively, this program
;	  will produce a master directory of EVERY file on EVERY drive
;	  on its host system.  If MULTUSR is set TRUE, then when program
;	  is executed, if current user number is within MINUSR and MAXUSR,
;	  program will check the set range of user areas from MINUSR to MAXUSR.
;	  But if current user number is not within set range, program will
;	  check only current user area.  I did this so that remote callers
;	  could use program to check all remote user areas, but a local
;	  SYSOP could use program for any user area, assuming that
;	  program was located in a public (common) user area.
;	  By Dave Hardy
;
;07/02/81 Fixed user number print routine to properly display
;	  user numbers 10-15.  By Keith Petersen, W8SDZ
;
;07/01/81 Changed file name print routine so spaces are not printed
;	  after last filename on a line.  By Keith Petersen, W8SDZ
;
;05/30/81 Removed stack save routines, so that now the program will
;	  warm-boot back, instead of 'RET'.  This was necessary because
;	  the stack may be garbaged by the CBIOS when the warm-boot trap
;	  is encountered.  Also added check in SHOFIL routine to check for
;	  proper user number before displaying a filename, in case of
;	  public user area (e.g. if BDOS-PAT.ASM is installed).
;	  By Dave Hardy
;
;05/12/81 Added automatic log-in feature, so that all drives will
;	  be selected, regardless of which were previously logged in.
;	  In systems with illegal drive select traps in their CBIOS,
;	  this would cause a warm-boot as soon as the first illegal drive
;	  is selected, so a trap is used that replaces the warm-boot
;	  pointer at address 1 during execution of this program.  After
;	  execution, the pointer is restored, and the program does a 
;	  'ret' to CP/M.  In systems with no traps in the CBIOS (most 
;	  systems), this program will finish and return to CP/M without
;	  encountering the warm-boot trap.  In either case, the program
;	  should work properly.  Thanks to Ron Fowler for his suggestions.
;	  NOTE: This program will probably no longer work with CP/M
;	  1.4, since most 1.4's don't check for illegal drive selects.
;	  By Dave Hardy
;
;05/06/81 Fixed BASE equates, added comments, added conditionals
;	  for SYS file and USER number display, added MAXCOL equate
;	  to set number of display columns, eliminated display of
;	  USER number when in USER 0 to make display easier to read,
;	  made miscellaneous changes to make file more readable.
;	  By Dave Hardy
;
;04/20/81 Renamed from FILE.ASM to FILEFIND.ASM to avoid
;	  confusion with CPMUG FIND.ASM
;	  By Dick Mead
;
;  Define some miscellaneous values:
BASE	EQU	0	;Set to base address of your CP/M (normally 0)
BDOS	EQU	BASE+5	;CP/M BDOS entry point
FCB	EQU	BASE+5CH;CP/M file control block address
DMABUF	EQU	BASE+80H;DMA buffer address
CR	EQU	0DH	;ASCII return
LF	EQU	0AH	;ASCII linefeed
;
;  Define some BDOS functions:
SETDMA	EQU	26	;Set DMA Address function
SRCHFST	EQU	17	;Search For First function
SRCHNXT	EQU	18	;Search For Next function
CONOUT	EQU	2	;Console Output function
RTNVER	EQU	12	;Return Version Number function
SETUSR	EQU	32	;Set/Get User Code function
CONSTAT	EQU	11	;Get Console Status function
CONIN	EQU	1	;Console Input function
PRINT	EQU	9	;Print String At Console function
;
;  Define true and false
FALSE	EQU	0
TRUE	EQU	NOT FALSE
;
;  Set the following as desired:
SHOWSYS	EQU	FALSE	;TRUE if want to show SYS files
SHOWUSR	EQU	TRUE	;TRUE if want to show user number
DRV1ST	EQU	TRUE	;TRUE if want to print drive before user number
			;FALSE if want to print user number before drive
MAXCOL	EQU	4	;Set to number of columns desired in display
MULTUSR	EQU	TRUE	;TRUE if want to search multiple user numbers
			;FALSE if want to search only current user number
SECURE	EQU	TRUE	;TRUE if MAXUSR and MAXDRV are stored in memory and are
			;to be dynamically set at run time
;  The following need to be set only if MULTUSR above is set TRUE:
MINUSR	EQU	0	;Set to minimum user number to be checked
MAXUSR	EQU	13d	;Set to maximum user number to be checked
			;Even in secure, maximum user cannot exceed this number
	IF	SECURE
MAXUSRL	EQU	03Fh	;Memory location which holds maxuser+1
MAXDRVL EQU	03Dh	;Memory location which holds MAXDRV (0=A, 1=B, etc.)
	ENDIF  ;NOT SECURE
;
;
	ORG	BASE+100H
;
	LXI	SP,STACK	;Set up local stack
;
	CALL	ILPRT		;Print sign-on message
;
	DB	CR,LF,'FILEFIND ver 12.0',CR,LF
	DB	'Type CTRL-C to abort',CR,LF,CR,LF,0
;
	LDA	FCB+1		;Check for filename on command line
	CPI	' '
	JNZ	SETUP		;If it's there, then continue
	CALL	ILPRT		;Else, print help message and exit
	DB	CR,LF,'Usage: FILEFIND <filename.type>'
	DB	CR,LF,CR,LF
	DB	'       You must specify the file(s) you',CR,LF
	DB	'       want to find.  Ambiguous file names',CR,LF
	DB	'       may be used.',CR,LF,CR,LF
	DB	'       Example:  FILEFIND MODEM.DOC',CR,LF
	DB	'                 FILEFIND *.ASM',CR,LF,0
	JMP	0		;Warm-boot back to CP/M
;
SETUP	LDA	4		;Save USER/DRIVE number
	STA	UDNUM
	RRC
	RRC
	RRC
	RRC
	ANI	0FH
	STA	CUN		;Save current user number
	STA	CTU		;Save as current try number, too
;
	IF	MULTUSR		;then set up some boundaries
	MVI	B,MINUSR
	CMP	B		;Current user number within range?
	JC	OUTSIDE		;No? then search only current user number
	MOV	B,A

	IF	NOT SECURE	;then MAXUSR is set by an equate
	MVI	A,MAXUSR
	ELSE			;value of MAXUSR+1 is found at MAXUSRL
	LDA	MAXUSRL
	DCR	A
	CPI	MAXUSR
	JC	SETUP1		;jmp if (MAXUSRL)<=MAXUSR  ie, within range
	MVI	A,MAXUSR
SETUP1
	ENDIF  ;not secure

	CMP	B
	JC	OUTSIDE
	MVI	A,MINUSR
	STA	CTU		;Set Current Try User to MINUSR
	STA	ORIGCTU		;Set original CTU to same

	IF	NOT SECURE	;then MAXUSR is set by an equate
	MVI	A,MAXUSR
	ELSE			;value of MAXUSR+1 is found at MAXUSRL
	LDA	MAXUSRL		
	DCR	A
	CPI	MAXUSR
	JC	SETUP2		;jmp if (MAXUSRL)<=MAXUSR  ie, within range
	MVI	A,MAXUSR
SETUP2
	ENDIF  ;NOT SECURE

	STA	MAXTEMP		;Set maximum user number
	ENDIF
;
SBOOT	LHLD	1
	SHLD	WBOOT		;Save warm-boot address
	LXI	D,18H
	DAD	D
	SHLD	DSKSEL+1	;Get jump to CBIOS DSKSEL routine
;
;  The following code sets a trap at the warm-boot jump, so that
;  any 'illegal drive select' traps in the CBIOS will be defeated.
	LXI	H,DONE		;Replace warm-boot pointer with trap
	SHLD	1
;
	LXI	D,DMABUF
	MVI	C,SETDMA
	CALL	BDOS		;set DMA address
;
	XRA	A
	STA	TRYDRV		;Set up to try drive '0' first
TRY	MOV	C,A		;Try to select drive
	
	IF MULTUSR AND SECURE	;then see if TRYDRV exceeds value in MAXDRVL
	LDA	MAXDRVL
	CMP	C
	JC	DONE		;jmp if TRYDRV > (MAXDRL).  We're done.
	ENDIF  ;multusr and secure

	PUSH	D		;savå thå ä register
	LXI	D,0		;make de=0 to cause cbios to log in new disk
	CALL	DSKSEL
	POP	D		;restore d register
	MOV	A,L
	ORA	H
	JZ	DONE		;If HL=0 then no more drives
	LDA	TRYDRV
	INR	A
	STA	TRYDRV		;Get ready for next drive, too
	STA	FCB		;Store 1+drive# in FCB (1=A, 2=B, etc.)
;
	IF	MULTUSR		;Then set user before each pass
NXTUSR	LDA	CTU		;Set user to CTU via a BDOS call
	MOV	E,A
	MVI	C,SETUSR
	CALL	BDOS
	ENDIF
;
AVAIL	MVI	C,CONSTAT	;Check to see if key pressed
	CALL	BDOS
	ORA	A
	JZ	NOPRESS		;If no key pressed, then continue
	MVI	C,CONIN		;If key pressed, then check for abort
	CALL	BDOS
	CPI	'C'-40H		;Is it control-C?
	JNZ	NOPRESS		;If no, then continue
	CALL	ILPRT		;If yes, then print abort message
	DB	CR,LF,LF,'+++ ABORTED',CR,LF,0
	JMP	DONE1		;Then restore warm-boot jmp and UDNUM then exit
;
NOPRESS LXI	D,FCB
	MVI	C,SRCHFST
	CALL	BDOS		;Check for directory match with FCB
	CPI	0FFH		;"A" register has 0-3 if file found else 0FFH
	JZ	NXT		;Do next drive if no more matches found
	CALL	SHOFIL		;If match found, then display the filename
;
SNEXT	MVI	C,SRCHNXT
	CALL	BDOS		;Check for next match with FCB
	CPI	0FFH	
	JZ	NXT		;No more matches? Then do next drive
	CALL	SHOFIL		;If match found, then display the filename
	JMP	SNEXT		;Continue until no more matches found
;
NXT	EQU	$
;
	IF	MULTUSR		;then see if any more user numbers to check
	LDA	CTU		;Increment current try user number
	INR	A
	STA	CTU
	MOV	B,A
	LDA	MAXTEMP
	CMP	B
	JNC	NXTUSR		;Do next user number if all not done
	LDA	ORIGCTU		;Else reset user number and do next drive
	STA	CTU
	ENDIF
;
	MVI	C,RTNVER
	CALL	BDOS		;See if CP/M version 1.4 or 2.x
	MVI	C,5		; (if CP/M 1.4, then CBIOS must return an
	ORA	A		; error if bad drive number, else won't work)
	JZ	FOUR		;If 1.4 then four drives maximum
	MVI	C,17		;Otherwise 16 drives maximum
;
FOUR	LDA	TRYDRV
	CMP	C
	JC	TRY		;Continue until all drives checked...
;
;  all done now, return to CP/M
;
DONE	LDA	MFLAG		;See if any files found
	CPI	0
	JNZ	DONE1		;If yes, then done, so exit
	CALL	ILPRT		;If no, then say none found first
	DB	CR,LF,'+++ FILE NOT FOUND',CR,LF,0
DONE1	LHLD	WBOOT		;Restore warm-boot jump
	SHLD	1
	LDA	UDNUM		;Restore USER/DRIVE number
	STA	4
	ANI	0FH
	MOV	C,A		;Select original drive before return so that
	CALL	DSKSEL		;  CP/M won't get confused...
	JMP	0		;Then return to CP/M via a warm-boot...
;
	IF	MULTUSR		;and current user number is outside MIN/MAX
OUTSIDE	
	IF	SECURE
	JMP	DONE1		;quit without any messages
	ENDIF  ;secure

	LDA	CUN
	STA	CTU		;Set current try user to current user number
	STA	ORIGCTU		;Set original CTU to same
	STA	MAXTEMP		;Set maximum user number to current
	JMP	SBOOT		;and continue...
	ENDIF  ;multusr
;
SHOFIL	MOV	L,A		;Get filename to display from directory record
	MVI	H,0		;  which CP/M puts at DMA address
	DAD	H		;("A" register has relative position of name
	DAD	H		; within directory record)
	DAD	H
	DAD	H
	DAD	H	;Multiply "A" by 32 to point to start of filename
	LXI	D,DMABUF
	DAD	D	;Now point to filename
	XCHG		;save filename pointer in de
;
	IF	NOT SHOWSYS	;then ignore SYS files
	LXI	H,10
	DAD	D	;Point to SYS file attribute
	MOV	A,M
	ANI	80H	;Check for SYS type file
	RNZ		;Return if SYS attribute set (i.e. don't display)
	ENDIF
;
;  The following code is needed only if BDOS-PAT.ASM is installed in
;  your CP/M.  It makes sure that this program only lists those files
;  that are in the current user area (i.e. if you are in a non-zero
;  user area, it won't list the files in user 0, too).
	LDA	CTU	;Get current try USER number
	XCHG		;Point HL to user number byte of directory entry
	CMP	M 
	RNZ		;Return if wrong user number (i.e. don't show file)
	XCHG		;Get directory pointer back into DE 
;
	IF	DRV1ST	;then print drive number before user number
	LDA	TRYDRV
	ADI	'A'-1
	CALL	AOUT	;Display drive (A-P)
	ENDIF
;
	IF	SHOWUSR	OR MULTUSR	;then display USER number (except 0)
	LDAX	D	;get USER number
	INX	D
	ENDIF
;
	IF	NOT MULTUSR	;then don't print leading 0 for user 0
	ORA	A	;USER 0 ?
	JZ	NOUSR	;if USER 0 don't report
	ENDIF
;
	IF	SHOWUSR OR MULTUSR	;continue displaying user numbers
	CPI	10	;is USER number = 0 thru 9?
	JNC	NEAT	;If no, then print a leading '1'
	PUSH	PSW	;Save USER number
	MVI	A,'0'	;If yes, then print a leading '0'
	CALL	AOUT
	POP	PSW	;Restore USER number
	JMP	USRL	;Then print second digit of user number
;
NEAT	SUI	10	;USER number = 10 thru 15
	PUSH	PSW
	MVI	A,'1'
	CALL	AOUT	;print a leading '1'
	POP	PSW
;
USRL	ADI	'0'	;make into ASCII number
	CALL	AOUT	;print digit
	ENDIF
;
NOUSR	EQU	$
;
	IF	NOT DRV1ST	;Then print drive number after user number
	LDA	TRYDRV
	ADI	'A'-1
	CALL	AOUT	;Display drive (A-P)
	ENDIF
;
	MVI	A,':'	;Fence character
	CALL	AOUT	;Display a ':' to look nice
;
	MVI	B,8	;(8 characters or less in filename)
PFN	LDAX	D	;Now print the filename...
	INX	D
	CALL	AOUT	;  ... one character at a time
	DCR	B
	JNZ	PFN	;Continue until all 8 characters displayed
	MVI	A,'.'
	CALL	AOUT	;Display the '.' before the filetype
;
	MVI	B,3	;Now do the same for the filetype
PXT	LDAX	D
	INX	D
	CALL	AOUT
	DCR	B
	JNZ	PXT
;
	LDA	COLUMN	;Print MAXCOL columns across the screen
	DCR	A
	JZ	DOCRLF
	STA	COLUMN
	CALL	TWOSPC	;Print two spaces to make it neat
	MVI	A,0FFH	;Set MFLAG to remember that a file was found
	STA	MFLAG	; (so that 'NOT FOUND' message will be suppressed)
	RET
;
DOCRLF	MVI	A,MAXCOL ;After last column:
	STA	COLUMN	;  Reset column number
	MVI	A,CR	;   Then print CRLF
	CALL	AOUT
	MVI	A,LF
	CALL	AOUT
	RET
;
TWOSPC	MVI	A,' '
	CALL	AOUT
	MVI	A,' '	;Fall into AOUT
;
AOUT	PUSH	H	;Send a character to the console
	PUSH	D
	PUSH	B	;Save the registers in case BDOS eats them
	ANI	7FH	;Strip parity bit
	MOV	E,A
	MVI	C,2
	CALL	BDOS	;Print the character in 'A' on the console
	POP	B
	POP	D
	POP	H	;Restore the registers
	RET
;
;INLINE PRINT ROUTINE
;
ILPRT	XTHL		;Set HL to point to message
;
ILPLP	MOV	A,M	;Get a character from message
	CALL	AOUT	;Output it
	INX	H	;Point to next character
	MOV	A,M	;Check for end of message
	ORA	A	; (00H marks end of message)
	JNZ	ILPLP
	XTHL		;Get proper return address onto stack
	RET		;Then return to program
;
COLUMN	DB	MAXCOL	;Column counter
TRYDRV	DB	0	;Number of drive being tried
UDNUM	DB	0	;USER/DRIVE number is saved here
CUN	DB	0	;Currently logged-in user number
MFLAG	DB	0	;Flag set to non-zero if file is found
WBOOT	DW	0000H	;CP/M warm-boot address is saved here
;
	IF	MULTUSR
CTU	DB	0	;Current try user number
ORIGCTU	DB	0	;Original current try user number
MAXTEMP	DB	0	;Maximum try user number
	ENDIF
;
DSKSEL	DB	0C3H	;Jump to CBIOS's disk select routine
	DW	0000H	; (CBIOS's DSKSEL address is stored here)
;
	DS	64	;32 level stack should be enough room
STACK	DS	2
;
	END
