;		DIRFILES: Version 3.7 (Kaypro video only)
;
; Note: this code is riddled with Kaypro-specific video equates, if
;       you don't have a Kaypro then get DIRF36.AQM if you need source.
;
;                      Steve Sanders - Tampa Bay Kaypro UG
;                      (813) 791-1454 or 791-1455  300/1200/2400
;
; Modification History
;
; 01/28/86  Made a Kaypro-only version to show all files, no restrictions,
;    v3.7   for local use only - not for remote systems. - S. Sanders
;
; 02/23/85  Corrected bug in MATCH routine that caused failure to
;    v3.6   compare if any flag is set.  Removed  embedded tabs in
;           space strings.  Thanks to Steve Sanders for secure mode
;	    improvements.  Named Version 3.6 because of existence of
;	    spurious versions that were created without coordination
;	    between authors. - C. Horn
;
; 10/24/84  Modified to use C/R to continue instead of "any key"
;    v3.1   which caused a lot of problems on-line with 1200 baud
;           noise present (infinite loop).  Added a CTRL-C option
;           to abort listing if desired, entry of CTRL-C while
;           printing will abort to options menu.  -  Steve Sanders
;
; 09/04/84  Modified to write the data file back to disk only if it
;    v3.0   was edited while we were here.  -  C. Horn.
;
; A utility that maintains a data file on any default disk or user area
; containing directory file names for all current files.  The data file
; is called in and updated each time DIRFILES is run.  The data file
; contains a description of each listed file that is entered by the user
; via the EDIT function within DIRFILES.  When the program exits it writes
; the updated data file back to disk.  No disk space is needed in this
; process other than the space occupied by the data file when first created.
; When first called, if the data file DIRFILES.DAT does not exist, the
; program provides by menu the option to create it.  The maximum number of
; directory entries at the EQUate NUMFILES determines the size of the data
; file.  128 is the correct entry for a standard SSSD CP/M disk, and is
; probably appropriate for SSDD and DSSD disks.  This setting creates a
; 10K DIRFILES.DAT file.
;
; This program is a product of Horn Engineering Associates.  It is placed
; into the public domain for free use by anybody except that it may not
; be sold, either in itself or as any part of a collection of software,
; without the express permission of Horn Engineering Associates.  It is
; requested that any user retain our SIGNON logo.
;
; Copyright (c) 1985 by Charles E. Horn,PE
; for Horn Engineering Associates
;
TRUE		EQU	0FFH
FALSE		EQU	NOT TRUE
;
SECURE		EQU	FALSE		;TRUE, for RCP/M, uses WHEEL status
NOSYS		EQU	FALSE		;set TRUE to exclude SYS files
WHEEL		EQU	003EH		;address of the WHEEL byte
;
;	CP/M Equates
;
BOOT		EQU	0		;org zero CP/M
BDOS		EQU	BOOT+5		;bdos entry
DEFDMA		EQU	80H		;normal default DMA address
TPA		EQU	BOOT+100H	;ORG address
CONIN		EQU	1		;BDOS console input function
CONOUT		EQU	2		;console output function
PBUF		EQU	9		;print buffer function
RDCON		EQU	10		;read console buffer function
CONSTAT 	EQU	11		;console status function
RESDISK 	EQU	13		;reset disk system function
SELDISK 	EQU	14		;select default disk function
OPENF		EQU	15		;open file function
CLOSEF		EQU	16		;close file function
SRCHF		EQU	17		;search for first dir file function
SRCHN		EQU	18		;search for next file function
DELF		EQU	19		;delete file function
READF		EQU	20		;read file function
WRITEF		EQU	21		;write file function
MAKEF		EQU	22		;make file function
GETDISK 	EQU	25		;get default disk function
SETDMA		EQU	26		;set DMA address function
SETATTR 	EQU	30		;set file attributes
;
;	Misc Equates
;
BELL		EQU	07H		;bell character
BS		EQU	08H		;backspace character
LF		EQU	0AH		;line feed
CR		EQU	0DH		;carriage return
BLANK		EQU	20H		;space character
CLRSCR		EQU	1AH		;clear screen (Kaypro, Wyse, ADM3)
;
;	Program Equates
;
NUMFILES	EQU	128		;maximum number of directory entries
DATLEN		EQU	NUMFILES * 80	;size of DIRFILES.DAT data file
					;(80 columns per directory entry)
NUMRECORDS	EQU	DATLEN / 128	;number of records in data file
SCREEN		EQU	23		;number of lines per screen
COLUMNS 	EQU	80		;80 column screen
;
	ORG	TPA			;program runs here
;
;	Main program
;
START:	JMP	START1			;jump over fixed data
DATFNAME:
	DB	'DIRFILESDAT'		;name of data file (here for easy patch
;	   ---- >-----------< ----	;filenames must be exactly this long
					;..space fill - type must be last 3
DIRFNAME:
	DB	'???????????'		;wild card directory name
START1: LXI	H,0			;find CP/M stack pointer
	DAD	SP
	LXI	SP,STACK		;set up our local stack
	PUSH	H			;store CP/Ms SP on ours
;
	CALL	CLR			;clear screen
;
	LXI	D,SIGNON		;point to signon message
	MVI	C,PBUF			;and show it
	CALL	BDOS
;
;	We will only work on the current default drive which by now we know
;	has a mounted disk which might not be logged on.  We reset the disk
;	system here.  If the disk is write protected we find out later.
;
	MVI	C,GETDISK		;get the default drive
	CALL	BDOS
	PUSH	PSW			;save it
	MVI	C,RESDISK		;reset the disk system
	CALL	BDOS
	POP	PSW			;recover the default drive
	MOV	E,A			;move to E
	MVI	C,SELDISK		;set default to original
	CALL	BDOS
;
;	Now try to open the directory data file.  First we need to move
;	in the data file name - case it has been changed or patched.
;
	LXI	B,11			;11 char to move in
	LXI	H,DATFNAME		;point to data file name
	LXI	D,FCB+1 		;point past the drive number
	CALL	MOVE			;move it in
;
	LXI	D,FCB			;point to file control block
	MVI	C,OPENF 		;open the file
	CALL	BDOS
	INR	A			;was 0FFH if error
	JNZ	READIN			;if no error, go read it in
;
;	If no data file - create it per menu
;
    IF SECURE
	LDA	WHEEL			;get wheel byte
	ORA	A			;secure?
	JZ	EXIT0			;exit with no support message
    ENDIF	;SECURE
;
	LXI	D,NFDMSG		;file not found and query message
	MVI	C,PBUF
	CALL	BDOS			;print it
	MVI	C,CONIN 		;get keyboard response
	CALL	BDOS
	ANI	5FH			;make response UC
	CPI	'Y'			;if not yes
	JNZ	EXITA			;abort program
;
;	We must create the data file.  Fill data buffer with blanks.
;
	LXI	H,FILEBUF		;point to data file buffer
	LXI	B,DATLEN		;length of data file
FILLOOP:
	MVI	M,BLANK 		;insert a space
	INX	H			;bump pointer
	DCX	B			;bump count down
	MOV	A,C			;get count LS
	ORA	B			;'z' set if done
	JNZ	FILLOOP 		;loop til done
;
;	Create, write, and close the new data file
;
	LXI	D,FCB			;point to FCB
	MVI	C,MAKEF 		;create the file in directory
	CALL	BDOS
	INR	A			;was 0FFH if no directory space
	JZ	EXITB
;
	CALL	WRITEFILE
	JC	EXITC			;cy set if disk full
	LXI	D,FCB			;point to FCB
	MVI	C,CLOSEF		;close the file
	CALL	BDOS
;
;	Clean up the FCB here to get ready to read in the data file
;
READIN: CALL	ZFCB			;zero the FCB
;
;	Move in the data file name
;
	LXI	B,11			;11 char to move
	LXI	H,DATFNAME		;point to the data file name
	LXI	D,FCB+1 		;point past drive in FCB
	CALL	MOVE			;move it in
;
;	If not to show SYS files we set the file attribute
;
    IF NOSYS
	LXI	H,FCB+10		;point to second type byte
	MOV	A,M			;get it
	ORI	80H			;set the high byte
	MOV	M,A			;put it back
    ENDIF	;NOSYS
;
;	At least - set the R/W attribute - case it has been set R/O
;
	LXI	D,FCB			;point to FCB
	MVI	C,SETATTR		;set attributes
	CALL	BDOS
;
;	Open the data file
;
	LXI	D,FCB
	MVI	C,OPENF
	CALL	BDOS			;no error expected - we know it's there
;
;	Read it into memory and close it
;
	CALL	READFILE		;read it
	LXI	D,FCB
	MVI	C,CLOSEF		;if it is ever to work with MP/M
	CALL	BDOS
;
;	Here we set up the FCB to read the directory, read it, and one by
;	one either match an existing entry in the data file or enter the
;	new file if not present.
;
	CALL	ZFCB			;zero the FCB
	LXI	B,11			;11 char in file name
	LXI	H,DIRFNAME		;the wild card file name
	LXI	D,FCB+1 		;point to FCB file name space
	CALL	MOVE			;move the file name in
;
	LXI	D,DEFDMA		;set the CP/M DMA address
	MVI	C,SETDMA
	CALL	BDOS
;
;	Now start the directory search.  We set the maximum number of files
;	we will enter into the data file buffer to prevent any attempt to
;	write past the buffer if there are more files in the directory than
;	we have designated as NUMFILES.
;
	MVI	B,NUMFILES-1		;the maximum number to the buffer
	PUSH	B			;save it
	LXI	D,FCB			;find first match
	MVI	C,SRCHF
	CALL	BDOS
	LXI	H,DEFDMA		;point to DMA buffer
	CALL	FINDFN			;find the file name in the DMA space
    IF NOSYS
	PUSH	H			;save DMA filename pointer
	LXI	D,10			;index to SYS attribute byte
	DAD	D			;point to it
	MOV	A,M			;get it
	ANI	80H			;will be zero if not SYS
	POP	H			;restore pointer
	JNZ	MOREFILES		;skip it if SYS
    ENDIF	;NOSYS
;
	INX	H			;point past drive number
	CALL	ADDOT			;put name into FNBUF with dot
;
;	SEARCH looks for the file name in the data file.  Returns with HL
;	pointing to data file entry or CY set if not found.  If not found,
;	HL points to first available blank line.
;
	CALL	SEARCH
	CC	ADDNAME 		;add name to file if not found
	MVI	M,0FFH			;flag the entry good
	POP	B			;recover maximum files number
	DCR	B			;bump down - we wrote one
	PUSH	B			;resave it
MOREFILES:
	LXI	D,FCB			;look for matches for all files
	MVI	C,SRCHN 		;look for next
	CALL	BDOS
	INR	A			;was 0FFH if no more files
	JZ	PURGE			;if done - go purge erased files
	DCR	A			;set directory code back to what is was
	LXI	H,DEFDMA		;point to DMA buffer
	CALL	FINDFN			;find the name in the DMA space
    IF NOSYS
	PUSH	H			;save pointer
	LXI	D,10			;index to SYS byte
	DAD	D			;point to it
	MOV	A,M			;get it
	ANI	80H			;zero if not set
	POP	H			;restore pointer
	JNZ	MOREFILES		;skip it
    ENDIF	;NOSYS
;
	INX	H			;point past drive number in DMA
	CALL	ADDOT			;file name to FNBUF with dot
	CALL	SEARCH
	CC	ADDNAME 		;add name to file if not found there
	MVI	M,0FFH			;flag entry good
	POP	B			;recover maximum files count
	DCR	B			;bump down - we wrote one
	PUSH	B			;save max file count
	JNZ	MOREFILES		;find all directory entries
;
;	At this point all directory entries are matched.  Found files are
;	flagged 0FFH.  Erased files are still flagged 0.  Blank lines are
;	flagged blank.	We go through and blank all lines containing erased
;	files, then go through again and flag all current files 0 for next time
;
PURGE:	POP	B			;clear stack
	LXI	H,FILEBUF		;point to data file buffer
	MVI	C,NUMFILES		;maximum entry counter
PURGE1: MOV	A,M			;get an entry flag
	ORA	A			;flag 0 (erased)?
	JNZ	PURGE2			;if not
	PUSH	H			;save buffer pointer
	MVI	B,COLUMNS		;number of bytes per entry
PURLOOP:
	MVI	M,BLANK 		;enter a space
	INX	H			;bump pointer
	DCR	B			;bump column counter down
	JNZ	PURLOOP 		;fill line with blanks
	POP	H			;recover buffer pointer
PURGE2: LXI	D,COLUMNS		;index to next entry
	DAD	D			;set pointer to next
	DCR	C			;bump down max files counter
	JNZ	PURGE1			;continue for all possible entries
;
;	Flag all current files zero
;
	LXI	H,FILEBUF		;point to data buffer
	MVI	C,NUMFILES		;max number of entries
FLAGLOOP:
	MOV	A,M			;get entry flag
	CPI	0FFH			;good entry?
	JNZ	NXTFLAG 		;if blank line space
	XRA	A			;else - get a zero
	MOV	M,A			;and flag entry good
NXTFLAG:
	LXI	D,COLUMNS		;index to next entry
	DAD	D
	DCR	C			;bump down remaining entries to do
	JNZ	FLAGLOOP		;fix all entries
;
;	We now have a clean updated buffer that is suitable for display.
;	First we will sort all entries aphabetically.  This sort
;	moves all entries to the beginning of the buffer, with all
;	blanked entries following.
;
	CALL	SORT			;sort it
;
;	The data file is now sorted and ready for display and edit.
;	We enter here with a clean stack and no passed parameters.  We
;	return here after editing.  The only exit from here is via the
;	display menu.
;
DISPLAY:
	CALL	CLR			;clear screen
	LXI	H,FILEBUF		;point to data buffer
	MVI	B,NUMFILES		;max entry counter
	MVI	C,SCREEN-1		;no of display lines less one for menu
;
;	The main display loop
;
DISPLAY1:
	MOV	A,M			;get status flag
	ORA	A			;zero if valid entry
	JNZ	DISPLAY3		;if not valid
	XRA	A			;entry is valid - flag at least 1 found
	STA	NONEFLAG
	PUSH	H			;save registers
	PUSH	B
	MVI	A,CR			;get cursor to left margin
	CALL	PCHAR			;print CR
	POP	B			;restore registers
	POP	H
	PUSH	H			;resave registers
	PUSH	B
	MVI	C,15			;length of file name entry
	CALL	PSTRING 		;print the entry
	MVI	C,COLUMNS-15		;remainder of string space
	CALL	MINSTRG 		;go print minimum text (skip blanks)
	CALL	CRLF			;set up for next
;
; added following routine to check console for CTRL-C abort request
;              - Steve Sanders 10/24/84
;
	MVI	C,CONSTAT		;any chr waiting at console?
	CALL	BDOS			;let's see
	CPI	0			;if none waiting
	JZ	DISPL1B			;then continue displaying
;
	MVI	C,CONIN			;else check console for
	CALL	BDOS			;char waiting at keyboard
	CPI	'C'-40H			;CTRL-C ?
	JZ	DISPL1A			;if yes, then abort to menu
	JMP	DISPL1B			;or jump over DISPL1A routine
;
DISPL1A:
	POP	B			;restore registers
	POP	H			;      ditto
	JMP	DISPLAY2		;before going back to menu
;
DISPL1B:
	POP	B			;restore registers
	POP	H
	DCR	C			;screen full?
	JNZ	DISPLAY3		;continue if not
DISPLAY2:
	PUSH	H			;save registers
	PUSH	B
	LXI	D,DISMENU		;point to normal display menu
    IF SECURE
	LDA	WHEEL			;get wheel byte
	ORA	A			;zero if secure
	JNZ	DISPLAY6		;if not secure
	LXI	D,SECMENU		;else change pointer to secure menu
    ENDIF	;SECURE
;
DISPLAY6:
	MVI	C,PBUF			;print it
	CALL	BDOS
	MVI	C,CONIN 		;get the console reply
	CALL	BDOS
	POP	B			;restore registers
	POP	H
	ANI	5FH			;make assumed ascii UC
	CPI	'Q'			;quit?
	JZ	DISPLAY4		;check for secure?
	CPI	'E'			;edit?
	JZ	DISPLAY5		;check for secure?
;
; changed the following routine to wait for CR to continue instead
; of "any key".  Remote callers would be hung in an endless loop if
; extreme line noise was present.  - Steve Sanders 10/24/84
;
	CPI	CR			;C/R to continue
	JZ	DISP6A			;yes,then continue
	JMP	DISPLAY2		;not 'Q', 'E', or C/R, ask again...
;
DISP6A:
	MVI	C,SCREEN-1		;reset screen line counter
	PUSH	H			;save pointer
	PUSH	B			;save counters
	MVI	A,CR			;to left of current line
	CALL	PCHAR
	MVI	A,BLANK 		;blank the menu
	MVI	C,COLUMNS		;number of blanks per line
	CALL	PCHARS
	POP	B			;restore counters
	POP	H			;restore pointer
	JMP	DISPLAY3		;continue display
;
DISPLAY4:				;process Quit
    IF SECURE
	LDA	WHEEL			;get wheel byte
	ORA	A			;zero is secure
	JZ	EXIT			;without rewriting the file
    ENDIF	;SECURE
	JMP	WRITEBACK		;not secure - write the file back
;
DISPLAY5:				;process Edit
    IF SECURE
	LDA	WHEEL			;wheel byte
	ORA	A			;zero is secure
	JZ	DISPLAY3		;ignore edit - continue display
    ENDIF	;SECURE
	JMP	EDIT			;else not secure - go edit
;
NONEFLAG:
	DB	0FFH			;default is files present
;
;	Pointer and end of buffer processor
;
DISPLAY3:
	LDA	CRLFLAG 		;are we at end of buffer?
	ORA	A			;not zero if so
	JNZ	DISPLAY7		;reset CRLFLAG and go to top of buffer
	LXI	D,COLUMNS		;index to next data entry
	DAD	D			;set the pointer in HL
	DCR	B			;end of buffer?
	JNZ	DISPLAY1		;display more if not
	LDA	NONEFLAG		;get files present flag
	ORA	A			;zero if present
	JNZ	EXITD			;if none to display
	LXI	H,FILEBUF		;else - reset pointer to top
	MVI	B,NUMFILES		;and reset entry counter
	PUSH	H			;save buffer pointer
	PUSH	B			;save counters
	CALL	CRLF			;put in a blank line
	MVI	A,1			;set the CRLFLAG
	STA	CRLFLAG
	POP	B			;restore registers
	POP	H
	JMP	DISPLAY2		;show the menu
;
DISPLAY7:
	XRA	A			;reset the CRLFLAG
	STA	CRLFLAG
	JMP	DISPLAY1
;
CRLFLAG:
	DB	0			;default not flagged

;
DISMENU:	;full access menu
       DB	CR,'     ',1bh,'B0',1bh,'B1'
       DB	' [ Options: ',1bh,'C1','<Q>',1bh,'B1','uit, '
       DB	1bh,'C1','<E>',1bh,'B1','dit, or ',1bh,'C1'
       DB	'<RETURN>',1bh,'B1',' shows more ] '
       DB	1bh,'C1',1bh,'C0',' ','$'
;
SECMENU:	;secure menu
       DB	CR,'     [ Options:  <Q>=Quit, <C/R>=Continue,'
	DB	' or ^C aborts while printing ] $'
;
;	The file EDIT code
;
;	We first set a flag here to note that we have been here.  Flag
;	is checked at exit.  If there has been no edit, we do not write
;	the data file back to disk.
;
EDIT:	MVI	A,1			;set flag to 1
	STA	EDITFLG
	MVI	A,CR			;move to left margin
	CALL	PCHAR			;..of current console line
	MVI	A,BLANK 		;fill line with spaces
	MVI	C,COLUMNS		;number of spaces to print
	CALL	PCHARS			;print them
;	LXI	D,EDTMENU1		;show the primary editor menu
;	MVI	C,PBUF
;	CALL	BDOS
EDIT1:	LXI	D,EDTMENU2		;show the secondary editor menu
	MVI	C,PBUF
	CALL	BDOS
;
;	Call for the file name to edit
;
	CALL	SCIN$NE 		;read console buffer - no CRLF echo
	LDA	CLEFT			;get number of characters entered
	ORA	A			;zero if none entered
	JZ	DISPLAY 		;so go back to main menu
;
;	This code gets the entered file name into FNBUF in a directory
;	format that can be matched to the data file name entry.  This code
;	is a kludge but it allows for entry of the file name in lower case
;	in any reasonable way.
;
	CALL	MAKEUC			;make the buffer alphas upper case
	LXI	H,CLIN			;point to console buffer text
	LXI	D,FNBUF 		;point to the processed file name buf
	MVI	B,8			;8 char spaces for main filename
GETENTRY:
	MOV	A,M			;get char from buf
	ORA	A			;end of entry?
	JZ	FNBLANK 		;fill out name field with blanks
	CPI	'.'			;dot?
	JZ	FNBLANK
	STAX	D			;else put char in FNBUF
	INX	H			;bump pointers
	INX	D
	DCR	B			;more?
	JZ	PUTDOT			;if not
	JMP	GETENTRY		;else - loop
;
FNBLANK:
	MVI	A,BLANK 		;get a space
	STAX	D			;put in FNBUF
	INX	D			;bump pointer
	DCR	B			;downcount
	JZ	PUTDOT			;install the dot
	JMP	FNBLANK 		;fill out til 8
;
PUTDOT: MVI	A,'.'			;get a dot
	STAX	D			;install it
	INX	H			;bump pointers
	INX	D
	MVI	B,3			;3 char in file type field
PUTDOT1:
	MOV	A,M			;get a type char
	ORA	A			;zero if past entry
	JZ	TYPBLANK		;fill field with blanks
	CPI	'.'			;dot?
	JZ	TYPBLANK
	STAX	D			;good char - install it
	INX	H			;bump pointers
	INX	D
	DCR	B			;downcount
	JZ	FINDMAT 		;done - go find a match in data buffer
	JMP	PUTDOT1 		;else - finish it
;
TYPBLANK:
	MVI	A,BLANK 		;fill unused type field with blanks
	STAX	D
	INX	D
	DCR	B
	JNZ	TYPBLANK
;
;	FNBUF now filled.  This code finds the file entry in the data buffer
;	and returns with HL point to the head of the entry line.  If CY
;	returned the search failed.
;
FINDMAT:
	CALL	SEARCH
	JC	FNF			;if file not found
	PUSH	H			;save line pointer
	MVI	C,COLUMNS
	CALL	PSTRING 		;show the line on the console
	MVI	A,BS			;backspace to head of text
	MVI	C,COLUMNS-15		;number to do
	CALL	PCHARS			;do it
	POP	H			;restore line pointer
	LXI	D,15			;index to start of text
	DAD	D			;set text pointer in HL
	PUSH	H			;save it
;
;	We are now ready for editing the text.	Existing text will be
;	overwritten.  Trailing garbage will not be retained.
;
	CALL	SCIN$NE 		;read the console buffer
	POP	D			;the text pointer
	LDA	CLEFT			;get number of char entered
	ORA	A			;zero if none entered
	JZ	DISPLAY 		;back to main menu
	LXI	H,CLIN			;point to buffer text
	MVI	B,COLUMNS-15		;we will accept only this many char
BUFLOOP:
	MOV	A,M			;get char from con buffer
	ORA	A			;zero if at end
	JZ	FILLOUT 		;go fill out the line with blanks
	STAX	D			;else enter the character
	INX	H			;bump pointers
	INX	D
	DCR	B			;countdown
	JNZ	BUFLOOP 		;if not done
	JMP	EDIT1			;max characters in text line
;
FILLOUT:
	MVI	A,BLANK 		;get a space
	STAX	D			;put in text line
	INX	D			;move to next
	DCR	B			;countdown
	JNZ	FILLOUT
	JMP	EDIT1			;back to top of EDIT
;
FNF:	LXI	D,FNFMSG		;file not found prompt
	MVI	C,PBUF
	CALL	BDOS
	JMP	EDIT			;back to top of editor
;
FNFMSG: DB	CR,LF,'File ',1bh,'B2','not',1bh,'C2'
       DB	'found, check your spelling.',7,CR,LF,'$'
;
EDTMENU1:
	DB	CR,LF,'Just a RETURN while in EDIT will go to the main '
	DB	'menu with no changes...',CR,LF,'$'
EDTMENU2:
	DB	CR,1bh,'B0',1bh,'B1',' File to ',1bh,'C1',' edit ',1bh,'B1'
	DB	' (RETURN alone exits): ',1bh,'C1',1bh,'C0',' ',17h,'$'
;
;	This is where we write the data file back to disk before we exit.
;	Here we will set the existing data file to R/W just be be safe,
;	erase it, recreate it, open it, write it, and close it.  This way,
;	we do not need more disk space than we already use.
;
WRITEBACK:
	LDA	EDITFLG 		;did we edit while here?
	ORA	A			;zero if we did not
	JZ	RESDMA			;..so don't write a new data file
	CALL	ZFCB			;clean up the FCB
	LXI	B,11			;11 characters in file name
	LXI	H,DATFNAME		;point to data file name
	LXI	D,FCB+1 		;place to put it
	CALL	MOVE			;move it in
;
;	Erase the old data file
;
	LXI	D,FCB
	MVI	C,DELF			;delete file
	CALL	BDOS
;
;	Create a new one
;
	LXI	D,FCB
	MVI	C,MAKEF
	CALL	BDOS
;
;	Open it
;
	LXI	D,FCB
	MVI	C,OPENF
	CALL	BDOS
;
;	Write it
;
	CALL	WRITEFILE
;
;	Close it
;
	LXI	D,FCB
	MVI	C,CLOSEF
	CALL	BDOS
;
    IF NOSYS		;set it to SYS
	LXI	H,FCB+10		;point to SYS attribute byte
	MOV	A,M			;get it
	ORI	80H			;set the high bit
	MOV	M,A			;put it back
	LXI	D,FCB
	MVI	C,SETATTR		;set it to SYS
	CALL	BDOS
    ENDIF	;NOSYS
;
;	Reset CP/M DMA
;
RESDMA: LXI	D,DEFDMA
	MVI	C,SETDMA
	CALL	BDOS
;
;	All done
;
	JMP	EXIT
;
;***********************
;*** EXIT PROCESSORS ***
;***********************
;
EXIT0:	LXI	D,NSPMSG		;the no support message
	MVI	C,PBUF			;print it
	CALL	BDOS
	JMP	EXIT
;
EXITA:	LXI	D,ABTMSG		;abort message
	MVI	C,PBUF			;print it
	CALL	BDOS
	JMP	EXIT			;and quit
;
EXITB:	LXI	D,NDSMSG		;no disk space message
	MVI	C,PBUF			;print it
	CALL	BDOS
	JMP	EXITA			;and do abort report
;
EXITC:	LXI	D,FULMSG		;disk full message
	MVI	C,PBUF			;print it
	CALL	BDOS
	JMP	EXITA			;and report abort
;
EXITD:	LXI	D,NONMSG		;no files message
	MVI	C,PBUF			;print it
	CALL	BDOS			;fall through to EXIT
;
EXIT:	POP	H			;recover CP/M SP from stack
	SPHL				;set it
	RET				;and exit quietly to CP/M
;
;************************
;*** CONSOLE MESSAGES ***
;************************
;
SIGNON: DB	CR,LF,1bh,'B0',1bh,'B1'
	DB	' DIRFiles v3.7kp  (c)1985 Horn Engineering Associates '
	DB	1bh,'C0',CR,LF,LF,1bh,'B3'
	DB	' Kaypro screen and video by Steve Sanders  01/28/86) '
	DB	1bh,'C3',CR,LF,LF,LF,1bh,'B2','>> Loading data file...'
	DB	1bh,'C2',1bh,'C1','$'
;
NFDMSG: DB	CR,'DIRFILES.DAT not found... Create new file? '
	DB	1bh,'B2','<Y/N> ',1bh,'C2',7,'$'
;
ABTMSG: DB	CR,LF,LF,'DIRFILES Aborted...',7,'$'
;
NDSMSG: DB	CR,LF,LF,'No Disk Space...',7,'$'
;
FULMSG: DB	CR,LF,LF,'Disk Full...',7,'$'
;
NONMSG: DB	CR,LF,LF,'++ NO FILES ++',7,'$'
;
NSPMSG: DB	CR,LF,LF,'DIRFILES not supported in this area...',7,'$'
;
;**************************************
;*** THE PRIVATE FILE CONTROL BLOCK ***
;**************************************
;
FCB:
FCB$DISK:	DB	0		;preset default drive
FCB$NAME:	DB	'DIRFILES'	;preset default data file name
FCB$TYP 	DB	'DAT'		;file type
FCB$EXTENT:	DB	0		;preset extent
FCB$RESV:	DB	0,0		;reserved by CP/M
FCB$RECUSED:	DB	0		;records used
FCB$ABUSED:	DB	0,0,0,0,0,0,0,0 ;assigned blocks
		DB	0,0,0,0,0,0,0,0
FCB$SEQREC:	DB	0		;sequential record number
FCB$RANREC:	DW	0		;random record number
FCB$RANRECO:	DB	0		;record overflow
;
;*******************
;*** SUBROUTINES ***
;*******************
;
;	WRITEFILE - Writes the records at FILEBUF to default disk
;
WRITEFILE:
	MVI	C,NUMRECORDS		;number of records to write
	LXI	D,FILEBUF		;set DMA to data file buffer
WRLOOP: PUSH	B			;save number of records
	PUSH	D			;save DMA pointer
	MVI	C,SETDMA		;set the DMA address
	CALL	BDOS
	LXI	D,FCB			;point to the FCB
	MVI	C,WRITEF		;write a record
	CALL	BDOS
	POP	D			;clear stack for possible error
	POP	B
	ORA	A			;zero if no error
	JNZ	RETERR			;if error set cy and return
	LXI	H,128			;add 128 to DMA pointer
	DAD	D			;original pointer in DE
	XCHG				;new pointer to DE
	DCR	C			;bump down record count
	JNZ	WRLOOP			;do more if not done
	XRA	A			;clear possible cy flag
	RET				;with no error
RETERR: STC				;set cy for error
	RET				;with error flag set
;
;	READFILE - Read the file specified by the FCB into FILEBUF
;
READFILE:
	LXI	D,FILEBUF		;point to file buffer
RDLOOP: PUSH	D			;save DMA pointer
	MVI	C,SETDMA		;set the DMA
	CALL	BDOS
	LXI	D,FCB			;point to FCB
	MVI	C,READF 		;read a record
	CALL	BDOS
	POP	D			;recover DMA pointer
	ORA	A			;non-zero is EOF
	RNZ				;if done
	LXI	H,128			;index DMA to next record space
	DAD	D
	XCHG				;pointer to DE
	JMP	RDLOOP			;loop til done
;
;	ZFCB - Zero the 36 bytes in the designated FCB
;
ZFCB:	MVI	C,36
	LXI	H,FCB			;point to FCB
ZLOOP:	MVI	M,0			;enter a zero
	INX	H			;bump pointer
	DCR	C			;bump down counter
	JNZ	ZLOOP			;loop for more
	RET
;
;	ADDOT - Moves the file name in the DMA area to FNBUF.  Adds the
;	dot before file type on the way.
;
ADDOT:	MVI	C,8			;8 char in filename
	LXI	D,FNBUF 		;point to name buffer
ADDOT1: MOV	A,M			;get a character
	ani	7fh			;strip hi bits
	STAX	D			;put into buffer
	INX	H			;bump pointer
	INX	D			;bump pointer
	DCR	C			;bump down counter
	JNZ	ADDOT1			;move all 8
	MVI	A,'.'			;get a dot
	STAX	D			;put into buffer
	INX	D			;bump buffer pointer
	MVI	C,3			;3 char in file type
ADDOT2: MOV	A,M			;get type char
	ani	7fh
	STAX	D			;to buffer
	INX	H
	INX	D
	DCR	C
	JNZ	ADDOT2
	RET
;
FNBUF:	DS	12			;the 12 char name buffer
;
;	SEARCH - Searches for a match between directory file name in FNBUF
;	and entries in the data file.
;	If found:	Returns with HL pointing to data file entry line.
;	If not found:	Returns with HL pointing to the first available
;			blank line. CY flag is set.
;
SEARCH: LXI	H,FILEBUF		;point to data file buffer
	MVI	C,NUMFILES		;max number of tries
SRCHLOOP:
	PUSH	H			;save buffer pointer
	PUSH	B			;save try counter
	INX	H			;point past status flag
	LXI	D,FNBUF 		;point to file name buffer
	CALL	MATCH			;look for match - CY set if none
	POP	B			;recover count
	POP	H			;recover pointer
	RNC				;if no CY we found match
	LXI	D,COLUMNS		;index to next data line + 1
	DAD	D			;set it
	DCR	C			;bump down try counter
	JNZ	SRCHLOOP		;if not done
;
;	No match found - find first available blank line - RET to caller
;
	LXI	H,FILEBUF		;buffer pointer
BLNKLOOP:
	MOV	A,M			;get flag byte
	CPI	BLANK			;is it a space?
	JZ	NOTFND			;return with CY set
	LXI	D,COLUMNS		;index to next flag
	DAD	D			;set it
	JMP	BLNKLOOP		;we'll find one somewhere
NOTFND: STC
	RET
;
;	MATCH - Checks for 12 char match between (HL) and (DE).  Returns
;		with CY set if no match.
;
MATCH:
	MVI	C,12			;12 char to check
MATLOOP:
	LDAX	D			;get char from (DE)
	ANI	7FH			;strip possible flag (v3.2)
	MOV	B,A			;char to B
	MOV	A,M			;(HL) to A
	ANI	7FH			;strip possible flag (v3.2)
	CMP	B			;compare chars
	JNZ	NOMATCH 		;any failure will cause exit with CY
	INX	H
	INX	D
	DCR	C
	JNZ	MATLOOP 		;go for all 12
	XRA	A			;reset CY flag (if set)
	RET				;got a match
NOMATCH:
	STC				;set no match flag
	RET
;
;	ADDNAME - Entry with HL pointing to head of a blank line.  Adds
;		  the file name in FNBUF to the data buffer, adds a trailing
;		  colon, and returns with HL pointing to head of line.
;
ADDNAME:
	PUSH	H			;save line pointer
	INX	H			;point past status flag
	XCHG				;put pointer in DE
	LXI	B,12			;12 char to move
	LXI	H,FNBUF 		;point to new file name
	CALL	MOVE			;move it in
	MVI	A,':'			;get a colon
	STAX	D			;add at end of filename
	POP	H			;restore line pointer
	RET
;
;	FINDFN - Locates directory file name in DMA after directory
;	search.  Returns pointer in HL.  Entry with directory code in A.
;
FINDFN: ADD	A		;*2
	ADD	A		;*4
	ADD	A		;*8
	ADD	A		;*16
	ADD	A		;*32
	MOV	E,A		;offset to DE
	MVI	D,0
	DAD	D		;add to HL
	RET
;
;	MOVE - Moves the number of characters in BC from (HL) to (DE)
;
MOVE:	MOV	A,M		;get char from (HL)
	STAX	D		;put in (DE)
	INX	H
	INX	D
	DCX	B
	MOV	A,C		;get LS count byte
	ORA	B		;both B & C zero?
	JNZ	MOVE		;loop til done
	RET
;
;	PSTRING - Prints string pointed to by HL.
;	Number of characters to print in C
;
PSTRING:
	PUSH	H		;save string pointer
	PUSH	B		;save count
	MOV	E,M		;get a character
	MVI	C,CONOUT	;print it
	CALL	BDOS
	POP	B		;restore count
	POP	H		;restore pointer
	INX	H		;bump it
	DCR	C		;done?
	JNZ	PSTRING 	;if not
	RET
;
;	MINSTRG - Types a string until either 3 blanks in a row or
;	the maximum number of characters is reached.
;	HL = string pointer
;	C = max number of characters in string
;
MINSTRG:
	MVI	E,3		;set blank counter
MINSTRG1:
	MOV	A,M		;get a character
	CPI	BLANK		;space?
	JNZ	MINSTRG2	;no space
	DCR	E		;bump down counter
	RZ			;exit if 3 spaces in row
;
MINSTRG2:
	PUSH	H		;save registers
	PUSH	D
	PUSH	B
	PUSH	PSW
	MOV	E,A		;char to E
	ani	7fh
	MVI	C,CONOUT	;out to console
	CALL	BDOS
	POP	PSW
	POP	B		;restore registers
	POP	D
	POP	H
	INX	H		;bump pointer
	DCR	C		;downcount characters
	RZ			;if maximum
	CPI	BLANK		;was it a blank?
	JZ	MINSTRG1	;yes - don't reset blank counter
	JMP	MINSTRG 	;else - loop back to top
;
;
;	CLR - Clear local screen
;
CLR:	MVI	E,CLRSCR
	MVI	C,CONOUT
	CALL	BDOS
	RET
;
;	CRLF - Puts a CR and LF out to console
;
CRLF:	MVI	E,CR
	MVI	C,CONOUT
	CALL	BDOS
	MVI	E,LF
	MVI	C,CONOUT
	CALL	BDOS
	RET
;
;	PCHAR - Prints the character in A
;
PCHAR:	MOV	E,A
	ani	7fh		;strip hi bits
	MVI	C,CONOUT
	CALL	BDOS
	RET
;
;	PCHARS - Prints the character in A the number of times in C
;
PCHARS: PUSH	PSW			;save character
	PUSH	B			;save count
	MOV	E,A			;char to E
	ani	7fh			;strip hi bits
	MVI	C,CONOUT		;print one
	CALL	BDOS
	POP	B			;restore count
	POP	PSW			;restore character
	DCR	C			;bump down count
	JNZ	PCHARS			;do some more
	RET
;
;	MAKEUC - Makes the alpha characters in the console buffer upper case
;
MAKEUC: LXI	H,CLIN			;point to the buffer
MAKEUC1:
	MOV	A,M			;get a char
	ORA	A			;zero if past end
	RZ
	CPI	'A'			;< 'A'?
	JC	MAKEUC2 		;if not alpha
	CPI	'z'+1			;> 'z'?
	JNC	MAKEUC2 		;if not alpha
	ANI	5FH			;is alpha - make UC
	MOV	M,A			;and put it back
	INX	H			;point to next
	JMP	MAKEUC1 		;and loop
;
MAKEUC2:
	INX	H			;not alpha - skip it
	JMP	MAKEUC1
;
;	SCIN$NE - Reads the console buffer without a CRLF echo to console
;
SCIN$NE:
	LXI	H,CLIN			;point to buffer string space
	LDA	CBUF			;get buffer length
	MOV	B,A			;put into B
	XRA	A			;get a zero
SCIN$NE1:
	MOV	M,A			;move in a zero
	DCR	B			;countdown
	INX	H			;bump pointer
	JNZ	SCIN$NE1		;fill whole buffer with zeros
;
	LXI	D,CBUF			;point to buffer
	MVI	C,RDCON 		;read console function
	CALL	BDOS
	RET
;
;	The console buffer
;
CBUF:	DB	CLEN			;buffer length
CLEFT:	DB	0			;number of char in buffer
CLIN:	DS	128			;number of buffer spaces
CLEN:	EQU	$-CLIN			;buffer length
;
;	SORT - Main sort routine - sorts directory entries alphabetically
;	       Records handled in pairs.
;	       String1 = first position pointer
;	       String2 = second position pointer
;	       Entry: none
;	       Exit: entries sorted
;
SORT:	LXI	H,FILEBUF		;point to data buffer first entry
	LXI	B,NUMFILES-1		;max number of entry pairs
SORT1:	PUSH	B			;save counter
	PUSH	H			;save string1
	LXI	D,COLUMNS		;offset to string2
	DAD	D			;string2 to HL
	XCHG				;string2 to DE
	POP	H			;string1 to HL
	PUSH	H			;save string1
	PUSH	D			;save string2
	MVI	C,13			;length of search string
	CALL	COMP			;CY set if (string2) < (string1)
	POP	D			;restore string2
	POP	H			;restore string1
	PUSH	D			;resave string2
	CC	SWAP			;swap string if CY
	POP	H			;new string1 pointer
	POP	B			;get counter
	DCX	B			;bump it down
	MOV	A,C
	ORA	B			;zero if at bottom
	JZ	BOTTOM
	JMP	SORT1			;go do next pair
;
BOTTOM: LDA	SWAPFLG 		;is 1 if anything was swapped
	DCR	A
	STA	SWAPFLG 		;assume was 1 - reset to zero
	JZ	SORT			;was 1? - sort entire buffer again
	RET				;was 0 - sort done
;
;	SWAP - Swaps record string pair.
;	       Entry: HL points to string1
;		      DE points to string2
;	       Process:
;	       (1) buffer <--- string2
;	       (2) string2 <--- string1
;	       (3) string1 <--- buffer
;
SWAP:	PUSH	H			;save string1 ptr
	PUSH	D			;save string2 ptr
	XCHG				;string2 to HL
	LXI	D,SWAPBUF		;point to buffer
	LXI	B,COLUMNS		;size of record
	CALL	MOVE			;move string2 to buffer
	POP	D			;restore string2
	POP	H			;restore string1
	PUSH	H			;save string1
	LXI	B,COLUMNS		;size of record
	CALL	MOVE			;move string1 to string2 position
	POP	D			;string1 to DE
	LXI	H,SWAPBUF		;point HL to buffer
	LXI	B,COLUMNS		;size of record
	CALL	MOVE			;buffer to string1 position
	MVI	A,1			;flag that a swap was done
	STA	SWAPFLG
	RET
;
SWAPFLG:
	DB	0			;default no swap
;
;	COMP - Compares two strings.
;	       Entry: HL points to string1
;		      DE points to string2
;		      C = maximum length of search
;	       Exit: Z set if strings match
;		     CY set if string2 < string1 (flags swap)
;		     No CY if (1) string1 < string2
;			   or (2) string1 = string2 (all blanks - no swap)
;
COMP:	LDAX	D			;get byte from string2
	CMP	M			;CY if string2 byte < string1 byte
	RNZ				;if no match
	INX	H			;bump pointers
	INX	D
	DCR	C			;downcount
	JNZ	COMP			;check next
	RET				;strings match - no CY
;
EDITFLG:
	DB	0			;default edit never used
;
;************************
;*** UNITIALIZED AREA ***
;************************
;
SWAPBUF:
	DS	COLUMNS 		;string swap buffer
	DS	64			;our private stack area
STACK:	EQU	$
;
FILEBUF:
	DS	DATLEN			;the data file buffer
;
	END
