;			-  XDIR -
;					BY S J SINGER
;
;	XDIR IS A CPM UTILITY THAT DISPLAYS A DISK DIRECTORY IN A MORE
;READABLE FORM. THE DIRECTORY IS READ FROM THE SPECIFIED DISK, SORTED
;THEN DISPLAYED IN A 3 COLUMN FORMAT. BOTH THE FILE NAMES AND FILE SIZES 
;IN 1 K GROUPS ARE DISPLAYED.
;	THE PRESENT VERSION OF XDIR HAS CERTAIN MINOR LIMITATIONS WHICH
;MAY BE EASILY REMOVED IF THEY CAUSE PROBLEMS.
;
;	1.  ONLY 48 FILES ARE DISPLAYED. THIS LIMIT IS SUFFICIENT FOR
;	    ALMOST ALL DISKS AND LEAVES ROOM ON THE SCREEN FOR EDITING.
;	    TO CHANGE THE NO OF LINES DISPLAYED, CHANGE THE LINES VARIABLE
;	    IN THE DATA ALLOCATION SECTION OF THE PROGRAM. FOR EXAMPLE
;	    TO DISPLAY A MAXIMUM OF 54 FILES CHANGE LINES TO 18.
;	2.  THE PRESENT VERSION READS GROUPS 0 AND 1 DIRECTLY FOR SPEED.
;	    IF A DISK IS SET UP FOR MORE THAN 64 FILES OR THE BLOCK FORMAT
;	    IS CHANGED THE READ ROUTINE WILL HAVE TO BE MODIFIED.
;
;	OCCASIONALLY THE FILE NAMES WILL BE DISPLAYED WITH THE FORMAT
;SCRAMBLED OR THE SPACE REMAINING ON THE DISK WILL NOT AGREE WITH THAT REPORTED
;BY THE STAT UTILITY. THIS ALMOST ALWAYS MEANS THE DISK DIRECTORY HAS BEEN
;MESSED UP SOMEHOW. USUALLY THE PROBLEM CAN BE CORRECTED BY COPYING ALL THE
;FILES TO ANOTHER DISK USING PIP.
;	XDIR WAS ASSEMBLED USING THE NEW CP/M MACRO ASSEMBLER AND USES A LARGE
;NUMBER OF MACROS. THESE ARE CONTAINED IN A LIBRARY CALLED MACRO.LIB WHICH
;IS REQUIRED IF THE PROGRAM IS TO BE REASSEMBLED.
;
;
;		COMMAND FORMAT
;
;	XDIR			DISPLAY DIRECTORY OF LOGGED DISK
;	XDIR A:			DISPLAY DIRECTORY OF DISK A
;	XDIR B:			DISPLAY DIRECTORY OF DISK B
;
;
	MACLIB	MACRO		;INCLUDE MACRO LIBRARY
	ORG	100H		;SET PROG START
	LXI	H,0
	DAD	SP		;GET OLD STACK POINTER
	SHLD	OLDSTK		;SAVE IT
	LXI	SP,NEWSTK	;LOAD NEW STACK POINTER
	JMP	DIR
;
;   GRPTS  CONVERT CPM GROUP AND SECTOR NUMBER TO TRK AND SEC
;
GRPTS:	MVI	H,0		;ZERO H
	LDA	G		;GROUP NO
	MOV	L,A		;TO L
	MOV	D,H		;ZERO	D
	DAD	H
	DAD	H
	DAD	H		;SHIFT LEFT 3
	LDA	S		;GET SECTOR NO
	MOV	E,A		;TO DE
	DAD	D		;HL HAS G*8+S
	LXI	D,-26		;DIVISOR
	MVI	A,1		;CONTAINS DIVIDEND
DIV:	DAD	D		;SUB 26
	INR	A
	JC	DIV		;LOOP TILL MINUS
	LXI	D,TABLE+26	;INDEX INTO TABLE
	DAD	D
	STA	TRACK		;STORE TRACK NO
	MOV	A,M		;GET SECTOR NO
	STA	BSEC		;SAVE IN BEGINNING SECTOR
	STA	ESEC		;SAVE IN END SECTOR TOO
	RET
;
;    START OF DIRECTORY ROUTINE READ IN GROUPS 0 AND 1 AND STORE
;    DIRECTORY FILE NAMES NOT FLAGGED E5.  IF EXTENT NOT ZERO STORE
;    OVER PREVIOUS FILE NAME. TABLE OF POINTERS WILL BE BUILT IN PDIR
;
DIR:	DISKIO	?DRIVE
	STA	NEWDRV
	LDA	81H
	ORA	A		;CHECK IF INPUT BUFFER EMPTY
	JZ	DDD
	LDA	82H		;CHECK NEW DRIVE
	CPI	'A'
	JNZ	DX1
	XRA	A
	STA	NEWDRV		;SELECT DRIVE A
	JMP	DDD
DX1:	CPI	'B'
	JNZ	DSKERR
	MVI	A,1
	STA	NEWDRV
DDD:	PRINT	<CR,LF,LF,'                        DIRECTORY DRIVE - '>
	LDA	NEWDRV		;LOGGED DISK
	ORA	A
	JNZ	BDIR
	PRINT	<'A',CR,LF,LF>
	JMP	DIR2
BDIR:	PRINT	<'B',CR,LF,LF>
	CALL	FIXB		;RESTORE DRIVE B
DIR2:	XRA	A
	STA	S		;SECTOR COUNT
	STA	G		;GROUP 0 = DIRECTORY
	STA	COUNT		;COUNT OF DIRECTORY ENTRIES
	FILL	PDIR,PDIR+130	;ZERO DIRECTORY POINTER TABLE
	LXI	H,DIRBUF	;POINTS TO DIRECTORY BUFFER
	SHLD	OUTB
	LXI	H,PDIR		;POINTER TABLE
	SHLD	IPOINT
	LDA	NEWDRV
	MOV	E,A
	DISKIO	LOGIN		;LOG IN NEW DRIVE NO
DIR4:	LXI	H,80H		;POINTS TO INPUT BUFFER
	SHLD	INB
	CALL	GRPTS		;COMPUTE TRACK AND SECTOR NO FROM G AND S
	SETSEC	BSEC		;SET SECTOR
	SETTRK	TRACK		;SET TRACK
	CALLBIOS DREAD		;READ DIRECT
DIR6:	LHLD	OUTB		;LOAD DESTINATION POINTER
	XCHG			;PUT IT IN DE
	LHLD	INB		;LOAD SOURCE POINTER
	MVI	A,0E5H		;FLAG BYTE
	CMP	M		;TEST FIRST BYTE
	JNZ	DIR8
	INX	H
	CMP	M		;TEST SECOND BYTE
	JZ	SORT		;SORT DIRECTORY
	JMP	DIR12
DIR8:	INX	H
	SAVE	H,D
	LXI	D,11		;EXTENSION OFFSET
	DAD	D
	MOV	A,M
	ORA	A
	JZ	DIR10		;IF EXTENT ZERO CONTINUE
	LXI	H,0		;ELSE, SEARCH FOR SAME NAME AND SWITCH
	SHLD	J		;INITIALIZE INDEX
DIR9:	DLOAD	PDIR,J
	MOV	A,H
	ORA	L
	JZ	DIR10		;ERROR TABLE EMPTY
	XCHG
	LHLD	INB		;POINTER TO NEW DIR ENTRY
	SAVE	D,H
	INX	H
	MATCH	,,11		;COMPARE 11 CHARAACTERS
	RESTORE	H,D
	JZ	SWITCH		;STORE NEW ENTRY OVER OLD
	INDEX	J,2		;INCR INDEX BY 2
	JMP	DIR9
SWITCH:	INX	H
	MOVE	,,15		;OVERWRITE OLD ENTRY
	RESTORE	H
	JMP	DIR12
DIR10:	RESTORE	D,H
	MOVE	,,15		;MOVE THE DIRECTORY ENTRY
	LDA	COUNT
	INR	A
	STA	COUNT		;INCR COUNT OF DIRECTORY ENTRIES
	LHLD	OUTB
	DSTORE	0,IPOINT	;INDEXED STORE HL
	INDEX	OUTB,16
	INDEX	IPOINT,2
DIR12:	INDEX	INB,32		;INCR POINTERS
	LXI	D,100H
	CPHL			;LIMIT OF 4 ENTRIES
	JNZ	DIR6
	LDA	S
	INR	A
	STA	S		;INCR DIRECTORY SECTOR COUNT
	JMP	DIR4		;READ ANOTHER BLOCK FROM DIRECTORY
;
;    THIS ROUTINE PRINTS THE DIRECTORY IN 3 COLUMNS. NO OF LINES
;    PRINTED IS CONTROLED BY VARIABLE LINES. ALL DIRECTORY NAMES
;    ARE PRESENT IN TABLE BUT ONLY A MAXIMUM OF 3*LINES WILL BE
;    PRINTED.
;
DIR14:	LXI	H,0
	SHLD	W		;INITIALIZE ALLOCATION
	SHLD	I		;INITIALIZE INDEX
DIR16:	DLOAD	PDIR,I		;INDEX LOAD POINTER
	DJZ	ENDFIL		;EXIT IF POINTER ZERO
	CALL	DIR20		;CALL PRINT ROUTINE
	PRINT	'         '
	DLOAD	PDIR+LINES*2,I	;POINTER COL 2
	DJZ	DIR18		;NO PRINT IF ZERO
	CALL	DIR20		;PRINT IT
	PRINT	'         '
	DLOAD	PDIR+LINES*4,I	;POINTER COL 3
	DJZ	DIR18
	CALL	DIR20		;CALL PRINT ROUTINE
DIR18:	PRINT	CRLF,$
	INDEX	I,2		;INCR INDEX BY 2
	LXI	D,LINES*2	;CHECK INDEX LIMIT
	CPHL
	JZ	ENDFIL		;EXIT WHEN INDEX 32
	JMP	DIR16		;PRINT SOME MORE
;
;    SUBROUTINE TO PRINT A SINGLE DIRECTORY ENTRY
;
DIR20:	MVI	C,11		;NAME LENGTH
DIR22:	SAVE	B,H		;SAVE REGISTERS
	MVI	C,2
	MOV	E,M		;CHAR TO BE PRINTER
	CALL	5		;CALL BDOS
	RESTORE	H,B		;RESTORE THE REGISTERS
	INX	H		;INCR NAME POINTER
	DCR	C		;DECR CHAR COUNT
	JZ	DIR24		;PRINT SIZE
	JMP	DIR22		;LOOP TILL COUNT 0
DIR24:	MOV	A,M		;EXTENSION TO A
	ADD	A
	ADD	A
	ADD	A
	ADD	A		;MULTIPLY BY 16
	MOV	B,A		;SAVE IN B
	INX	H
	INX	H
	INX	H		;ADD 3
	MOV	A,M		;GET RECORD COUNT
	RRC
	RRC
	RRC			;SHIFT RIGHT 3
	PUSH	PSW
	ANI	1FH		;EXTRACT
	LXI	H,0
	MOV	L,A		;NO TO HL TO PRINT
	POP	PSW
	ANI	0E0H		;EXTRACT
	JZ	DIR26
	INX	H
DIR26:	MOV	A,L
	ADD	B
	MOV	L,A
	SAVE	H
	LXI	D,10
	CPHL
	JM	DIR28
	PRINT	'  '
	JMP	DIR30
DIR28:	PRINT	'   '
DIR30:	POP	H
	PUSH	H
	XCHG
	LHLD	W
	DAD	D
	SHLD	W
	POP	H
	DECOUT
	PRINT	'K'
	RET
;
;
;    THIS ROUTINE RESTORES DRIVE B
;
FIXB:	LDA	NEWDRV		;CHECK DRIVE NO
	ORA	A
	RZ			;RETURN IF DRIVE A
	LDA	NEWDRV		;SELECT DRIVE B
	MOV	E,A
	DISKIO	LOGIN
	XRA	A
	STA	TNUM		;SELECT TRACK ZERO
	INR	A		;SELECT SECTOR 1
	STA	SNUM
	SETSEC	SNUM
	SETTRK	TNUM
	CALLBIOS DHOME		;HOME DRIVES
	CALLBIOS DREAD		;READ TRACK ZERO DIRECT
	RET
;
;    THIS IS THE EXIT POINT FROM THE PROGRAM. PRINT NO OF FILES AND
;    SPACE REMAINING, RELOAD OLD STACK POINTER AND RETURN BACK TO CCP.
;
ENDFIL:	PRINT	<CR,LF,'                '>
	LXI	H,0
	LDA	COUNT
	MOV	L,A
	DECOUT
	PRINT	' FILES    '
	LHLD	W
	MOV	A,L
	CMA
	INR	A		;NEGATE
	ADI	240
	MOV	L,A
	DECOUT
	PRINT	<'K BYTES REMAINING ON DISK',CR,LF>
EF1:	LHLD	OLDSTK
	SPHL			;RELOAD OLD STACK POINTER
	RET			;RETURN TO CCP WITHOUT REBOOT
;
DSKERR:	PRINT	<CR,LF,'ERROR - SELECT DRIVE A OR B'>
	JMP	EF1		;EXIT
;
;    THIS SECTION DOES THE ACTUAL SORTING OF THE DIRECTORY. DURING THE
;    INPUT OF THE DIRECTORY NAMES, A TABLE OF ADDRESS POINTERS PDIR
;    WAS CONSTRUCTED. THE SORT ROUTINE SORTS THE ADDRESS POINTERS
;    RATHER THAN THE ACTUAL DIRECTORY. 
;    THIS IS AN IMPLEMENTATION OF C. A. R. HOARE'S QUICKSORT ALGORITHM.
;    THE ALGORITHM IS VERY FAST AND GENERALLY USEFUL, HOWEVER CAUTION
;    SHOULD BE USED WITH LARGE FILES. THE ALGORITHM IS RECURSIVE AND
;    THE STACK SPACE REQUIRED IS PROPORTIONAL TO THE NO OF ITEMS TO BE
;    SORTED.
;
SORT:	LDA	COUNT		;NO OF ENTRIES IN DIR
	ORA	A
	JZ	ENDFIL		;EXIT IF DIRECTORY EMPTY
	DCR	A
	LXI	H,0		;ZERO HL
	MOV	L,A
	DAD	H
	SHLD	LAST		;END OF ARRAY
	LXI	H,0
	SHLD	FIRST		;START OF ARRAY
	LXI	H,0FFFFH
	PUSH	H		;FLAG FOR STACK EMPTY
	LHLD	FIRST
	PUSH	H
	LHLD	LAST
	PUSH	H		;STACK CONTAINS FIRST AND LAST INDICES
;
;    NOW POP STACK AND KEEP CALLING SPLIT RECURSIVELY TILL STACK EMPTY
;
SORT2:	POP	H
	MOV	A,H
	CPI	0FFH
	JZ	DIR14		;GO TO PRINT ROUTINE
	SHLD	J
	SHLD	LAST
	POP	H
	SHLD	I
	SHLD	FIRST
	CALL	SPLIT
	LHLD	I
	XCHG
	LHLD	FIRST
	CPHL
	JZ	SORT4
	PUSH	H		;I ON STACK
	DCX	D
	DCX	D
	PUSH	D		;J ON STACK
SORT4:	LHLD	J
	XCHG
	LHLD	LAST
	CPHL
	JZ	SORT8
	INX	D
	INX	D
	PUSH	D		;NEW I ON STACK
	PUSH	H		;NEW J ON STACK
SORT8:	JMP	SORT2
;
;    SPLIT SUBROUTINE DOES A SINGLE PARTITION ON AN ARRAY OF POINTERS
;
SPLIT:	HALF	I
	XCHG
	HALF	J
	DAD	D
	MOV	A,L
	ANI	0FEH
	MOV	L,A
	SHLD	K		;K=I+J/2
	DLOAD	PDIR,K
	SHLD	W		;W IS POINTER TO PARTITION ELEMENT OF PDIR
SPLIT2:	DLOAD	PDIR,I		;GET ITEM FROM LEFT
	XCHG
	LHLD	W		;PARTITION ELEMENT
	MATCH	,,11		;CONPARE KEYS
	JP	SPLIT4
	INDEX	I,2		;INCR I
	JMP	SPLIT2
SPLIT4:	DLOAD	PDIR,J		;GET ITEM FROM RIGHT
	XCHG
	LHLD	W		;PARTITION ELEMENT
	XCHG
	MATCH	,,11		;COMPARE KEYS
	JP	SPLIT6
	INDEX	J,-2
	JMP	SPLIT4		;LOOP BACK
SPLIT6:	LHLD	I
	XCHG
	LHLD	J
	CPHL			;COMPARE I AND J
	RZ			;RET IF I = J
	DLOAD	PDIR,I		;SWITCH POINTERS
	SAVE	H
	DLOAD	PDIR,J
	DSTORE	PDIR,I
	RESTORE	H
	DSTORE	PDIR,J
	JMP	SPLIT2
;
;   DATA ALLOCATIONS
;
LINES	EQU	16		;LINES PER PAGE ON DISPLAY
SPACE:	DB	' $'		;ASCII SPACE
CRLF:	DB	0DH,0AH,24H	;ASCII CR LF
I:	DW	0		;PSEUDO INDEX REGISTER
J:	DW	0		;PSEUDO INDEX REGISTER
K:	DW	0		;PSEUDO INDEX REGISTER
FIRST:	DW	0		;START OF ARRAY
LAST:	DW	0		;END OF ARRAY
W:	DW	0		;STORAGE FOR PARTITION INDEX
LINE:	DW	0		;LINE NUMBER FOR LISTING
IPOINT:	DW	00		;VARIABLE BUFFER POINTER
DRVNO:	DB	0		;STORAGE FOR ORIGINALLY LOGGED DRIVE
NEWDRV:	DB	0		;STORAGE FOR NEW DRIVE NO
TRACK:	DB	0		;SELECTED TRACK
BSEC:	DB	0		;SELECTED BEGINNING SECTOR
ESEC:	DB	0		;SELECTED ENDING SECTOR
TNUM:	DB	0		;TRACK NUMBER
SNUM:	DB	0		;SECTOR NUMBER
G:	DB	0		;CPM GROUP NO
S:	DB	0		;SECTOR NO WITHIN GROUP G
COUNT:	DB	0		;COUNT OF DIRECTORY ENTRIES
OLDSTK:	DW	0		;STORAGE FOR OLD STACK POINTER
ENDSTK:	DS	60		;STORAGE FOR NEW STACK
NEWSTK:	DW	0		;NEW STACK
INB:	DW	0		;STORES POINTER TO INPUT BUFFER AREA
OUTB:	DW	0		;STORES POINTER TO DIRECTORY BUFFER AREA
TABLE:	DB	01H		;SECTOR LOOK UP TABLE
	DB	07H
	DB	0DH
	DB	13H
	DB	19H
	DB	05H
	DB	0BH
	DB	11H
	DB	17H
	DB	03H
	DB	09H
	DB	0FH
	DB	15H
	DB	02H
	DB	08H
	DB	0EH
	DB	14H
	DB	1AH
	DB	06H
	DB	0CH
	DB	12H
	DB	18H
	DB	04H
	DB	0AH
	DB	10H
	DB	16H
PDIR	DW	0		;POINTER TABLE TO DIRECTORY (64 ENTRIES MAX)
DIRBUF:	EQU	PDIR+130	;START OF AREA USED TO STORE AND SORT DIRECTORY
	END
