;**************** BYPASS CUSTOMIZING SECTION ************************
	ORG	100H
	JMP	START
;********************************************************************
;
; TITLE: DLABEL.ASM		Author: R.Bascom
;
P$LOGON	DB	'DLABEL 1.0   24/11/85',0Dh,0Ah,0Ah,'$'
;
; PURPOSE: To print an alphabetically sorted disk directory on a 
;	   label in small (condensed) font.
;
; FEATURE: (1)	The condensed font is used for the main directory.
;	   (2)	The program can be repeated, using the same drive
;		and optional filenames.  Allowing for labeling 
;		numerous disks at one time.
;
; USAGE:   DLABELER [D][FILENAME.TYP]
;
;		   	   D = disk (i.e. A:, B:, ect)
;		filename.typ = filename using conventional wildcards
;
TRUE	EQU	0FFh
FALSE	EQU	00h
;
;*********************** PROGRAM EQUATES ****************************
;
MAXFILS		EQU	255	;maximum number of directory entries
BDOS		EQU	5
FCB		EQU	5Ch     ;first file control block
;
CR		EQU	0Dh	;carriage return
LF		EQU	0Ah	;line feed
ESC		EQU	1Bh	;escape
BELL		EQU	7	;bell
CTL$C		EQU	3	;end of text
;
;
;*********************** BDOS EQUATES *******************************
;
B$CONIN		EQU	1	;read CON byte
B$CONOUT	EQU	2	;write CON byte
B$LISTOUT	EQU	5	;write PRN byte
B$DIRCONIO	EQU	6	;direct CON input and output
B$PRINTS	EQU	9	;write a $ terminated string to CON
B$CONST		EQU	11	;read CON status
B$RESETDSK	EQU	13	;reset disk system
B$SEARCHF	EQU	17	;search for first name match
B$SEARCHN	EQU	18	;search for next name match
;
;******************** CUSTOMIZING SECTION ***************************
;
V$COLUMNS	DB	60	;number of label columns (maximum of 
				;250 columns) based on char per inch
				;of condensed fontŠ;
V$ROWS		DB	8 	;number of label rows based on the
				;line spacing
;
V$SPACES	DB	1       ;lines btwn labels (based on line spacing)
;
;This string is the divider fence that is printed between filenames and can 
;be changed as desired.  The divider is not printed before the first or after
;the last filename on a line.  String can have a maximum of of 5 chars and
;must end with $.   NOTE the first byte is the char count.
;
V$FENCE	DB	3			;char count (# of divider chars)
	DB	' | '			;the divider chars
	DB	'$'			;end of string
	DB	0,0			;spares
;
;******************** PRINTER CONTROL STRINGS ***********************
;
;This string is used to set line spacing (usually to 9 LPI) and small print
;font (usually condensed).  It may include any printer characteristics you
;desire.  String can have a maximum of 15 chars and must end with $.
;
V$CONDENST:
	DB	ESC,'B',3		;set pitch to condensed (17 CPI)
	DB	ESC,'A',8		;set line spacing to 8/72
	DB	'$'			;end of string 
	DB	0,0,0,0,0,0,0,0,0,0,0	;spares
;
;This string is used to return printer to its normal line spacing (usually
;6 LPI) and normal print font (usually 10 CPI).  It may also include any 
;printer characteristics you desire.  String can have a maximum of 15 chars
;and must end with $.
;
V$RESETST:
	DB	ESC,'@'			;reset the printer
	DB	'$'			;end of string 
	DB	0,0,0,0,0,0,0,0,0,0,0,0	;spares
;
;****************** END OF CUSTOMIZING SECTION *************************

;***********************************************************************
;***********************************************************************
;		        M A I N   P R O G R A M
;***********************************************************************
;
START:
	LXI	SP,STACK	;get new stack
;
;save disk for restarting later
;
	LXI	H,FCB
	MOV	A,M
	STA	SAVEFCB
;
;type logonŠ;
	LXI	D,P$LOGON
	MVI	C,B$PRINTS
	CALL	BDOS
;----------------
;
;calculate label width (in number of filenames/fences across the label)
;
	LDA	V$FENCE		;number of fence chars
	MVI	H,12		;plus number chars for filename
	ADD	H
	MOV	B,A		;B = number of chars per filename/fence
;
	XRA	A		;clear flags
	LDA	V$COLUMNS	;number of columns
	CPI	251		;maximum number of columns is 250 (FAh)
	JNC	COLERR		;err if max num of colums is exceeded
	MOV	H,A	
	LDA	V$FENCE		;plus length of one fence
	ADD	H		;A = line length and one fence length
;
	MVI	C,0		;C is the dividend
	ANA	A		;prepare for divide - clear carry
;	
;mod divide A by B
;
LWID:
	SUB	B		;repeated subtraction
	JC	LWID1
	INR	C		;count the number of subtractions
	JMP	LWID
LWID1:
	MOV	A,C		;
	CPI	0		;check for zero filename width
	JZ	LWIDERR		;  then error
	STA	LBLWID		;  else save the width
;----------------
;
;check number of rows 
;
CKROWS:
	LDA	V$ROWS
	CPI	0
	JZ	ROWERR		;err if zero rows calculated
;----------------
;
;set maximum number of files
;
SETMFIL:
	LDA	V$ROWS		;get the number of filenames wide
	MOV	B,A
	LDA	LBLWID		;get the number of rows
	MOV	C,A
	XRA	A		;zero A and clear carry
SETMFIL1:Š	ADD	C
	JC	MFILERR		;error if count exceeds 255 (FFh)
	DCR	B
	JNZ	SETMFIL1
	STA	MAXFIL		;maximum possible filenames on a label
;----------------
;
;got something - set up the printer
;
	LXI	H,V$CONDENST	;set condensed print
	CALL	WRPRNT		;send initialization string to printer 
;----------------
;
;able to restart the program here using the same command tail
;
REINIT:
	MVI	A,0	
	STA	COUNT		;clear counters
	STA	SCOUNT
	STA	WCOUNT
	STA	LCOUNT
	STA	SWITCH
;----------------
;
;does the command tail exist?
;
	LDA	SAVEFCB		;restore disk
	STA	FCB
	LXI	H,FCB+1		;set pointer first char of FCB
	MOV	A,M
	CPI	' '
	JNZ	GOTFCB		;yes, comand tail exists
;
;command tail does not exist
;
	MVI	B,11		;initial filename and filetype count
QLOOP:
	MVI	M,'?'		;store '?' in FCB
	INX	H
	DCR  	B
	JNZ	QLOOP
;
;look up the FCB in the directory
;
GOTFCB:
	LDA	V$ROWS		;initialize # of lines remaining on
	STA	LCOUNT		;   label to full count
	MVI	C,B$SEARCHF 	;search for first name match
	LXI	D,FCB
	CALL	BDOS		;read first
	INR	A		;were there any?
	STA	TEMP		;save extent
	JZ	FNFERR		;didn't get any
	LDA	TEMP		;reload extent
;----------------Š;
;point to directory entry 
;
SOME:
	DCR	A		;undo previous INR A
	ANI	3		;make modulus 4
	ADD	A		;multiply
	ADD	A		;  by 32 because
	ADD	A		;  each directory
	ADD	A		;  entry is 32
	ADD	A		;  bytes long
	LXI	H,81H		;point to buffer (skip to FN/FT)
	ADD	L		;point to entry
	MOV	L,A		;save (can't carry to H)
	LDA	MAXFIL		;is there room in table for entry ?
	MOV	B,A
	LDA	COUNT
	CMP	B
	JZ	TMFERR		;err - too many files for the label
;
;move entry to table
;
	XCHG			;entry to DE
	LHLD	NEXTT		;next table entry to HL
	MVI	B,31		;entry length
TMOVE:
	LDAX	D		;get entry char
	MOV	M,A		;store in table
	INX	D
	INX	H
	DCR	B		;more?
	JNZ	TMOVE
	SHLD	NEXTT		;save updated table adx
	LDA	COUNT		;get previous count
	INR	A
	STA	COUNT
;
;read more directory entries		
;
	MVI	C,B$SEARCHN 	;search for next name match
	LXI	D,FCB
	CALL	BDOS		;read dir entry
	INR	A		;check for end (0FFh)
	JNZ	SOME		;more 
;----------------
;
;sort filenames
;
	LDA	COUNT		;init the order table
	STA	SCOUNT		;save as # to sort
	LXI	H,ORDER
	LXI	D,TABLE
	LXI	B,31		;entry length
BLDORD:	
	MOV	M,E		;save LO order adxŠ	INX	H
	MOV	M,D		;save HI order adx
	INX	H
	XCHG			;table adx to HL
	DAD	B		;point to next entry
	XCHG
	DCR	A		;more?
	JNZ	BLDORD		;  yes
SORT	XRA	A		;get a zero
	STA	SWITCH		;show none switched
	LDA	SCOUNT		;get count
	DCR	A		;use 1 less
	STA	TEMP		;save # to compare
	STA	SCOUNT		;save highest entry
	JZ	DONE		;exit if no more
	LXI	H,ORDER 	;point to order table
SORTLP	CALL	COMPR		;compare 2 entries
	CM	SWAP		;swap if not in order
	INX	H		;bump order
	INX	H		;  table pointer
	LDA	TEMP		;get count
	DCR	A
	STA	TEMP
	JNZ	SORTLP		;continue
;
;one pass of sort done
;
	LDA	SWITCH		;any swaps done?
	ORA	A
	JNZ	SORT
;
;sort is all done - print entries
;
DONE	LXI	D,ORDER		;DE point to 1st entry in ORDER
	PUSH	D		;Save DE
	LDA	LBLWID		;get initial count of filenames per line
	STA	WCOUNT		;   and save it for a counter
;----------------
;
;print the label
;
PRINT:
	MVI	C,B$CONST	;check status of keyboard
	CALL	BDOS		;are any keys pressed?
	DCR	A
	JZ	0		;yes, abort
	POP	D		;restore DE
	LDAX	D		;no - get memory byte addressed by DE
	MOV	L,A		;   and put in L
	INX	D		;move to next location in ORDER
	LDAX	D		;get memory byte addressed by DE
	MOV	H,A		;   and put in L
	INX	D		;move to next location in ORDER
	PUSH	D		;   and save DE
	MVI	B,8		;file name lengthŠ	CALL	TYPEIT		;type filename
	MVI	E,'.'
	CALL	TYPE  
	MVI	B,3		;get the filetype
	CALL	TYPEIT
;
;check for last directory entry
;
	LDA     COUNT		;decrement count of file names to print
	DCR	A
	STA	COUNT
	JZ	TOP		;exit if count is zero
;
;check for maximum directory entries on current row
;
WFENCE	LDA	WCOUNT		;decrement # of entries left on this
	DCR	A		;  line for filenames
	STA	WCOUNT
	JZ	NXTLIN		;no more room on line so skip divider fence
;
;divider fence goes to the printer
;
	LXI	H,V$FENCE+1	;since there is room for another filename 
  	CALL	WRPRNT		;   put divider fence btwn filenames
	JMP	PRINT
;
;end of current line
;
NXTLIN:
	CALL	CRLF		;output CR/LF 
	JMP	PRINT		;go to print next line and file name
;----------------
;
;completed printing the directory entries, now set up for next label
;
TOP:
	LDA	LCOUNT		;number of remaining lines in the label
	CPI	0		;done?
	JZ	TOP2		;yes, then space out btwn labels
	CALL	CRLF		;no, send out CR/LF 
	JMP	TOP		;try again
;
TOP2:
	LDA	V$SPACES	;get number of lines btween labels
TOP3:
	CPI	0		;done
	JZ	AGAIN		;yes, then ask if another label desired
	PUSH	A		;no, spaceing line count
	CALL	CRLF		;send out CR/LF
	POP 	A		;restore spacing line count
	DCR	A		;decrement count
	JMP	TOP3		;try again
;----------------
;
;do another label or exit?Š;
AGAIN:	
	LDA	SAVEFCB
	CPI	0
	JNZ	AGAIN1		;zero means use current disk
	LDA	4		;get curent disk and user
	ANI	0Fh		;bits 0-3 are the current disk
	INR	A
AGAIN1:
	ADI	'@'		;change to alfa 1->A, 2->B,...ect.
	STA	CDR1		;set drive in prompt
	CALL	ILPRT
	DB	'Mount another DISK TO BE LABELED in Drive '
CDR1:
	DB	'A:'		;hot-patched to show correct drive
	DB	' Ready? (Y/N): ',0
AGAIN2:
	MVI	C,B$DIRCONIO	;direct input from keyboard
	MVI	E,TRUE		;  true = read keyboard char 
	CALL	BDOS
	CPI	'N'		;N exits
	JZ	EXIT
	CPI	'n'		;and little n too
	JZ	EXIT
	CPI	' '		;accept space as yes
	JZ	AGAIN3
	CPI	CR		;accept CR as yes
	JZ	AGAIN3
	CPI	'Y'		;accept Y
	JZ	AGAIN3
	CPI	'y'		;and little y
	JNZ	AGAIN2		;anything else is not accepted
;
AGAIN3:
	CALL	ILPRT
	DB	CR,'                                          '
	DB	'                    ',CR,0
	MVI	C,B$RESETDSK	;reset the disks
	CALL	BDOS		
	JMP	REINIT	
;----------------
;
;reset the printer and return to CP/M 
;
EXIT:
	MVI	A,CR		;CR
	CALL	CRTOUT
	MVI	A,LF		;LF
	CALL	CRTOUT
	LXI	H,V$RESETST	;reset printer to Normal mode
	CALL	WRPRNT
	JMP	0
;--------------------------------------------------------------------
;
;			S U B R O U T I N E SŠ;
; Inline print subroutine
;
ILPRT:	XTHL			;get starting address of string to 'HL'
;
ILPLP:	MOV	A,M
	PUSH	H
	CALL	CRTOUT		;show the character on the CRT
	POP	H
	INX	H
	MOV	A,M
	ORA	A
	JNZ	ILPLP
	INX	H
	XTHL			;return address to top of stack
	RET
;----------------
;
; Displays one character on the CRT
;
CRTOUT:	PUSH	PSW		;save the character
	MVI	C,B$CONOUT
	MOV	E,A		;get the character into 'E' reg.
	CALL	BDOS		;show the character on the crt
	POP	PSW		;get the character back
	RET
;----------------
;
;char in E goes to the printer
;
TYPE	PUSH	B
	PUSH	D
	PUSH	H
	MVI	C,B$LISTOUT
	CALL	BDOS
	POP	H
	POP 	D
	POP	B
	RET
;----------------
;
;string specified by HL goes to the printer
;
WRPRNT	MVI	A,'$'
	CMP	M
	RZ 
	MOV	E,M
	CALL	TYPE
	INX	H
	JMP	WRPRNT
;----------------
;
;string specified by HL and length in B goes to the printer
;with any tag bits stripped off
;ŠTYPEIT:
	MOV	A,M		;get rid of any tag bits in name
	ANI	07Fh		; for print out
	MOV	E,A
	CALL	TYPE
	INX	H
	DCR	B
	JNZ	TYPEIT
	RET
;----------------
;
;CR/LF go to the printer and
;reset label width counter and decrement line counter
;
CRLF:
	MVI	E,CR		;get CR character in E
	CALL	TYPE		; and output it
	MVI	E,LF		;get LF character in E
	CALL	TYPE		; and output it
;
	LDA	LBLWID		;reset number of filenames across label
	STA	WCOUNT
;
	LDA	LCOUNT		;decrement line count
	DCR	A 
	STA	LCOUNT 
;
	RET
;---------------- 
;
;compare routine for sort
;
COMPR:
	PUSH	H		;save table adx
	MOV	E,M		;load LO
	INX	H
	MOV	D,M		;load HI
	INX	H
	MOV	C,M
	INX	H
	MOV	B,M
;
;BC and DE now point to entries to be compared
;
	XCHG
CMPLP:
	MOV	A,M		;get rid of any tag bit in name
	ANI	07FH		; before comparing names for sort
	MOV	M,A
	LDAX	B
	ANI	07FH
	CMP	M
	INX	H
	INX	B
	JZ	CMPLPŠ	POP	H
	RET			;cond code tells all
;----------------
;
;swap entries in the order table
;
SWAP:
	MVI	A,1
	STA	SWITCH		;show a swap was made
	MOV	C,M
	INX	H
	PUSH	H		;save table adx+1
	MOV	B,M
	INX	H
	MOV	E,M
	MOV	M,C
	INX	H
	MOV	D,M
	MOV	M,B
	POP	H
	MOV	M,D
	DCX	H		;back pointer to correct location
	MOV	M,E
	RET
;----------------
;
;		E R R O R   R O U T I N E S
;
COLERR:
	CALL	ILPRT
	DB	CR,LF,'++ YOUR SETTINGS ALLOW MORE THAN 250 COLUMNS ++'
	DB	BELL,0
	JMP	0
;
LWIDERR:
	CALL	ILPRT
	DB	CR,LF,'++ YOUR SETTINGS DO NOT ALLOW AT LEAST 12 COLUMNS ++'
	DB	BELL,0
	JMP	0
;
ROWERR:
	CALL	ILPRT
	DB	CR,LF,'++ YOUR SETTINGS DO NOT ALLOW ANY ROWS ON A LABEL ++'
	DB	BELL,0
	JMP	0
;
MFILERR:
	CALL	ILPRT
	DB	CR,LF,'++ YOUR SETTINGS ALLOW MORE THAN 255 FILENAMES ++'
	DB	BELL,0
	JMP	0
;
FNFERR:
	CALL	ILPRT	
        DB	CR,LF,'++ FILE(S) NOT FOUND ++',CR,LF,0Š	JMP	AGAIN		;try another disk or exit
;
TMFERR:
	CALL	ILPRT
	DB	CR,LF,'++ TOO MANY FILENAMES ++',CR,LF,0
	JMP	AGAIN		;try another disk or exit 
;
;
;
;	
;
NEXTT	DW	TABLE		;next table entry
LBLWID	DB	0		;label width = (V$COLUMNS+V$FENCE)/(12+V$FENCE)
MAXFIL	DB	0		;max number of disk filenames on one label
COUNT	DB	0		;entry count
SCOUNT	DB	0		;# to sort
WCOUNT	DB	0		;# of filename sapces left across line
LCOUNT	DB	0		;# of lines remaining on this Label
SWITCH	DB	0		;swap switch for sort
SAVEFCB DB	0		;save disk FCB
TEMP	DS	1
ORDER	DS	2*MAXFILS	;order table
	DS	50		;stack area
STACK
TABLE	DB	0		;read entries from here to top of BDOS
	END	100H
	