;------------------------------------------
;              LISTDIR
;
; this is an example of usage of the
; get_files, sort_list, print_list rutines
;
; by: sedlakj@dec59.ruk.cuni.cz
;------------------------------------------


	mov	si,080			;pointer to DTA
	mov 	di,file_list		;pointer to data area
	mov	dx,offset file_name	;name of file (file mask)
	mov	cl,00			;file attributes
					;00h -files;  10h -files+dirs
		
	mov	ch,0fe			;254 files max to work with

	call 	get_files		;read the files from disk
	jc	end_it			;if disk error -> exit

	mov 	si,file_list		;pointer to data list
	mov	cx,20			;size of record
	mov	dl,al			;total of records
	mov	al,1			;sort directories first

	call	sort_list		;sorts files
	call	print_list		;prints files (on strdout device)

end_it:
	mov   	ax,04c00
	int  	021			;termination


file_name  db '*.*',0			;file mask

;--end of the main code ---------------------


;*********
;GET_FILES
;loads file_list table with info about the files in directory
;the structure of the info is following:
;	xxxxxxxx.xxxZ	13 bytes file name ASCIIZ string
;	00		1  byte reserved
;	00		1  byte reserved
;	?		1  byte file attribs 		
;	xxxx		4  bytes file size
; one file has 20 bytes record
; the rutine works within one segment CS=ES=DS 
; (I hope someone will adapt this routine for all segments range)
;
;INPUT:		CL	- file attributes
;		CH	- maximum of files to load into the table (1-254)
;		DX	- pointer to file spec. ASCIIZ
;		SI	- pointer to DTA (usually 080 hexa)
;		DI	- pointer to file_list table
;	
;OUTPUT:	CY	- set - error	
;			  AX  - error code
;		CY	- cleared - OK
;			  AX  - number of files in the file_list table
;
;DESTROYS:	flags
;		AX
;----------------------------------

GET_FILES:

	PUSH BX,CX,DX,SI,DI

	CLD
	PUSH	CX			;saves CX
	MOV 	CH,0
	MOV 	AH,04E
	INT 	021			;the first file
	POP	CX			;restores CX

 	JC	GF_ERR0			;error?

	PUSH	CX			;saves CX	
	CALL 	GF_SAVE			;process the info about the file
	POP	CX			;restores CX
 	MOV 	DL,1			;first file loaded

	XCHG	CH,CL
	MOV 	CH,0			;CX - max of files (for looping)

GF1:
	MOV 	AH,04F
	INT 	33			;get another file

 	JC 	GF_ERR
	ADD 	DI,20	;20 bytes per 1 file

	PUSH 	CX,DX
	CALL 	GF_SAVE
	POP 	DX,CX
	INC 	DL
	LOOP 	GF1

	;THIS MEANS THERE ARE MORE THAN 'CH' (on imput) FILES FOUND

GF2:
	CLC				;CLC no problem
	MOV AL,DL
	MOV AH,0			;loads AX with total of got files

GF3:
	POP DI,SI,DX,CX,BX
	RET				;exit the rutine

GF_SAVE:
	MOV 	BX,SI	;stores SI	
	MOV 	DX,DI	;stores DI

	mov	al,0
	mov	cx,14
	rep	stosb	;deletes previsous data from file_list

	mov	di,dx	;restores di

	ADD 	SI,01e	;si - pointer to file name
GF_S1:
	LODSB
	STOSB
	CMP 	AL,0	;end of file name?
	JNE 	GF_S1

	MOV 	SI,BX	;restores SI
	MOV 	DI,DX	;restores DI

	ADD 	SI,015	;si - pointer to file attribs
	ADD 	DI,0D
	MOV 	B[DI],0	;this byte will be used later
	INC 	DI
	MOV 	b[di],0	;this byte will be used later
	INC	di

	MOVSB

	MOV 	SI,BX
	ADD 	SI,01A	;si - pointer to file size
	MOV 	CX,4
	REP 	MOVSB

	MOV 	SI,BX
	MOV 	DI,DX
	RET 

GF_ERR0:
	MOV 	DL,0		;sets number of found files to 0
GF_ERR:
	
;	push 	ax		;this makes a blank record after
;	MOV	al,0		;the last record (not always needed)
;	add	di,20		;similar to zero terminated blocks
;	mov     cx,20
;       cld
;       rep stosb
;	pop	ax		

	CMP 	AX,012		;"last file" error
	JE 	GF2	
	STC			;STC to indicate the failture of the rutine
	JMP 	NEAR GF3

;-- end of rutine ------------------------------------------------------


;*********************************
;SORT_LIST
;sorts records in a list - designed for file_list with info about
;directory files but can be used universally
;
;INPUT:		SI	pointer to list of records
;		CX	size of record in bytes
;		DL	total of records in list
;		AL	- 0 sort all records
;			- 1 sort directory records (#-note) first
;OUTPUT:	none
;DESTOYS:	flags	
;
; # note:	directory record is bit 4 (starting from 0) in
;		file attributes that are save on offset 0F in a record  
;		see GET_FILES to learn more
;------------------------------------
sort_list:

	cmp	dl,1
	if be	ret		;if only one (or 0) record than return

	push	ax,bx,cx,dx,si,di
	
	dec	dl		;total-1 records are compared
	mov	dh,0		;will be used for testing

	mov	bl,al

sl1:
	push	si		
	push 	dx

sl2:
	mov	di,si
	add	di,cx		;di pointer to record+1
sl3:

	cmp	bl,0		;0 - sort all
	jz	sl34		;1 - sort directories first


	mov	al,[di+0f]	
	test	al,010
	mov	al,0
	if nz	mov al,1	;is record+1 a directory? yes->al=1

	mov	ah,[si+0f]
	test	ah,010
	mov	ah,0
	if nz	mov ah,1	;is record a directory?  yes->ah=1

	cmp	ah,al
	ja	sl35		;rec is dir, rec+1 is not -> no change
	jb	sl4		;rec+1 is dir, rec is not -> swap
				;else: compare
sl34:

	push	di		;stores di for next indexing
	push	cx		;stores cx - size of record
	cld
	repe	cmpsb		;compare string
	pop	cx
	pop	di

	ja	sl4		;jump if the first string is above

sl35:
	dec	dl		;another recored compared
	jz	sl8		;0? than it is the last record
	mov 	si,di		;si pointer to record+1
	jmp 	short sl2	;again: compare next record


sl4:	call	swap_record	;swaps record and record+1
	mov	dh,1		;maker: we have swaped
	mov	si,di
	sub	si,cx		;si pointer record
	
sl8:
	or	dh,dh		;test if swapped
	jz	sl_end		;no - then everything sorted --> end

				;we have swaped so we must resort
	pop	dx		;restore dx
	pop	si		;restore si - pointer to beginning
	jmp	sl1

sl_end:
	pop	dx		;restore dx
	pop	si		;restore si

	pop	di,si,dx,cx,bx,ax

	ret


swap_record:		;swaps two consecutive records

;	di	pointer to record+1
;	cx	record size

	push	di
	push	cx	

	mov	si,di
	sub	si,cx	;si now pointer to record

	cld		;direction forward
swr1:
	mov	al,[di]
	xchg	al,[si]
	stosb			;swaps [si]<->[di]
	inc 	si
	loop	short swr1

	pop 	cx
	pop	di

	ret

;-- end of rutine -------------------------------------------- 


;*********************************
;PRINT_LIST
;prints records in the list onto the standard output device (strdout)
;every record must be zero terminated
;
;INPUT:		SI	pointer to the list
;		DL	total od records
;		CX	record size 
;OUTPUT		registers: none
;		data on strdout
;DESTROYS	flags
;---------------------------------
print_list: 
	cmp	dl,0		;returns if no records in list
	if e	ret


	push	ax,bx,cx,dx,si
prl0:
	mov 	bx,si		;stores si
prl1:
	cld
	lodsb			;read 1 byte from record
	cmp 	al,0
	jz	prl2		;end of record
	push 	dx		;stores total of records
	mov 	dl,al		;move byte to dl for INT 021 fn02
	call 	writel		;write onto the strdout
	pop dx
	jmp short prl1		;again: next byte


prl2:	call	cr		;CR onto the strdout
	dec	dl		;one record less
	jz	prl3		;0? -> than end
	add	bx,cx		;
	mov	si,bx		;pointer to the next record
	jmp	short prl0

prl3:
	pop	si,dx,cx,bx,ax
	ret


writel:
	mov	ah,02
	int 	021
	ret
cr:
	push	dx
	mov 	ah,09
	mov 	dx,offset cr1
	int 	021 
	pop	dx
	ret

cr1:	db 0d,0a,'$'

;--- end of rutine --------------------------------------------------


db 'FILE LIST:'			;data area

file_list equ $ 

