
; Public Domain

; test run command: *.com; .; .

%include "lmacros3.mac"
%include "eld.mac"
%include "eldcall.mac"
%include "elddata.mac"

	numdef DEBUG, 1
	numdef BOOT, 0

%assign _CATCHINT0C 0
%assign _CATCHINT0D 0
%assign _CATCHINTFAULTCOND 0
%assign _LOADER 0
%assign _DEBUG_COND 1
%assign _DELAY_BEFORE_BP 0
%assign _DHIGHLIGHT 0
%assign _GETLINEHIGHLIGHT 0
%assign _IMMASM 0
%assign _INPUT_FILE_BOOT 0
%assign _INPUT_FILE_HANDLES 0
%assign _MS_0RANGE_COMPAT 0
%assign _PM 0
%assign _RH 0
%assign _SYMBOLIC 0
%assign _VXCHG 0
%include "options.mac"

%include "iniload.mac"

	cpu 8086

	addsection RELOCATEDDATA, nobits vstart=_ELD_RELOC_VSTART
relocateddata:

	addsection HEADER, start=0

	istruc ELD_HEADERX
at eldhxHeader
		; ELD executable header
	istruc ELD_HEADER
at eldhSignature,	db "ELD1"
			db 0,0,0
			db 26
at eldhCodeOffset,	dd CODEOFFSET
at eldhCodeImageLength,	dw code_size
at eldhCodeAllocLength,	dw 0
at eldhDataOffset,	dd DATAOFFSET
at eldhDataImageLength,	dw data_size
at eldhDataAllocLength,	dw total_data_size - data_size
at eldhCodeEntrypoint,	dw linker - code
at eldhReserved
at eldhExtensionSize,	dw header_extension_end - $$
	iend
at eldhxDescriptionOffset,	dd description
at eldhxHelpOffset,		dd DATAOFFSET + msg.help - datastart
PUT_ELDHX_DATETIME_OFFSET
header_extension_end:
	iend

%if _BOOT
description:		asciz "List files from directories, boot loaded."
%else
description:		asciz "List DOS files from directories."
%endif

	align 16, db 0

	eldstrict on

CODEOFFSET equ $ - $$
	addsection CODE, follows=HEADER vstart=_ELD_CODE_VSTART
%define CODEFIXUP - code + 0
code:
		; ELD instance header
	istruc ELD_INSTANCE
at eldiStartCode
at eldiEndCode
at eldiStartData
at eldiEndData
%if _BOOT
at eldiIdentifier,	fill 8, 32, db "BOOT DIR"
at eldiListing,		asciz _ELD_LISTING
%else
at eldiIdentifier,	fill 8, 32, db "DOS DIR"
at eldiListing,		asciz _ELD_LISTING
%endif
	iend


run:
	push ss
	pop es

%if _BOOT
run_boot:
reloc	testopt [relocateddata], nodosloaded
linkdatarelocation internalflags, -3
	jz error
	extcallcall yy_boot_parse_name2
	call parse_verbose
reloc	mov word [pathname_start], bx
internaldatarelocation

	push bx
	mov di, bx
	call find_dir
	pop bx
	mov dx, di
 %if 0
	db __TEST_IMM8			; (skip inc)
@@:
	inc di
	cmp byte [di], 0
	je boot_no_wild
	cmp byte [di], '?'
	je boot_wild
	cmp byte [di], '*'
	jne @B

boot_no_wild:
boot_wild:
 %endif
reloc	mov word [wild_pattern], dx
internaldatarelocation

	extcallcall yy_boot_init_dir2

	mov di, word [bp + bsBPB + bpbBytesPerSector]
					; di = sector size
					; = start of available auxbuff
	cmp byte [bp + ldFATType], 12	; FAT12 ?
	jne @F				; no -->
	cmp di, 8192			; already enough for a full FAT ?
	jae @F				; yes -->
	add di, di			; reserve straddling sector
@@:
%else
run_dos:
	extcallcall InDOS
	extcallcall yy_dos_parse_name
	call parse_verbose

	houdini
reloc	mov word [pathname_start], bx
internaldatarelocation
	push bx

	mov ah, 2Fh			; get DTA
	extcallcall ispm
	jnz .rm

subcpu 286
	push word 0
	push word 21h
	extcall intcall_ext_return_es, PM required	; must NOT be extcallcall
	pop dx				; discard interrupt number
	pop dx				; get es
subcpureset
	jmp @F
.rm:
	int 21h
	mov dx, es
@@:

reloc	mov word [originaldta], bx
internaldatarelocation
reloc	mov word [originaldta + 2], dx
internaldatarelocation

reloc	mov ax, word [relocateddata]
linkdatarelocation ext_inject_handler
reloc	mov word [originalinject], ax
internaldatarelocation
reloc2	mov word [relocateddata], inject
linkdatarelocation ext_inject_handler, -4
internalcoderelocation
	call get_es_ext
reloc	setopt [es:code + eldiFlags], eldifResident
internalcoderelocation -3	; mark block as resident
	 push ss
	 pop es

	houdini
reloc	mov dx, dta
internaldatarelocation
	mov ah, 1Ah		; set DTA
	extcallcall _doscall

	pop dx
%endif

reloc	rol byte [sort], 1
internaldatarelocation
	jnc .no_sort_buffer

reloc	mov ax, word [relocateddata]
linkdatarelocation extdata_size
reloc	sub ax, word [relocateddata]
linkdatarelocation extdata_used
reloc	mov bx, word [relocateddata]
linkdatarelocation auxbuff_current_size
	add ax, (uinit_data_end_2 - buffer_end)
%if _BOOT
	sub bx, di
	jbe .use_sort_buffer_eld_data_block
%endif
	cmp ax, bx
	jae .use_sort_buffer_eld_data_block

%ifn _BOOT
reloc	testopt [relocateddata], dif3_auxbuff_guarded_1 \
		| dif3_auxbuff_guarded_2 \
		| dif3_auxbuff_guarded_3
linkdatarelocation internalflags3, -3
	jz .use_sort_buffer_auxbuff
%else
	jmp .use_sort_buffer_auxbuff
%endif

.use_sort_buffer_eld_data_block:
	sub ax, (uinit_data_end_2 - buffer_end)
	call get_es_ext
reloc	add word [es:code + eldiEndData], ax
internalcoderelocation		; adjust size
reloc	add word [relocateddata], ax
linkdatarelocation extdata_used	; adjust size
reloc	mov ax, word [es:code + eldiEndData]
internalcoderelocation		; -> end of sort buffer
	push ss
	pop es

reloc	mov di, buffer_end	; -> start of sort buffer
internaldatarelocation
	mov si, ss		; => sort buffer
	jmp .sort_buffer_common

.use_sort_buffer_auxbuff:
	xchg ax, bx		; ax -> end of sort buffer
%ifn _BOOT
	xor di, di		; di -> start of sort buffer
%endif
reloc	mov si, word [relocateddata]
linkdatarelocation auxbuff_segorsel	; => sort buffer
%ifn _BOOT
	extcallcall guard_auxbuff
%endif

		; INP:	si:di -> buffer
		;	si:ax -> behind buffer
.sort_buffer_common:
reloc	mov word [sortsegsel], si
internaldatarelocation
reloc	mov word [sortstartoffset], di
internaldatarelocation
reloc	mov word [sort_last], di
internaldatarelocation
reloc	mov word [sort_next], di
internaldatarelocation
reloc	mov word [sortendoffset], ax
internaldatarelocation

.no_sort_buffer:

%ifn _BOOT
	call findfirst
	jc .notfound
reloc	mov di, relocateddata
linkdatarelocation line_out

.loop:
%else
boot_scan:
reloc	mov byte [relocateddata], 0
linkdatarelocation load_check_dir_attr, -3
reloc	mov si, word [relocateddata]
linkdatarelocation load_yyname_input
	cmp byte [si], '/'
	jne @F
	inc si
@@:
	cmp byte [si], 0
	jne @F
	extcall ..@yy_filename_empty
@@:
	 push ss
	 pop es
	call boot_parse_name
	cmp al, '/'
	jne @F
reloc	mov byte [relocateddata], ATTR_DIRECTORY
linkdatarelocation load_check_dir_attr, -3
reloc	mov word [relocateddata], si
linkdatarelocation load_yyname_next
@@:

	mov di, -1
	mov si, di
	mov [bp + lsvFATSector], di
	mov [bp + lsvFATSector + 2], si

	xor ax, ax
	xor dx, dx

scan_dir_yyname_loop:
	mov word [bp + ldDirCluster], ax
	mov word [bp + ldDirCluster + 2], dx

	push ss
	pop es
reloc	mov bx, relocateddata
linkdatarelocation load_yy_direntry

reloc2	mov word [relocateddata], relocateddata
linkdatarelocation handle_scan_dir_not_found, -4
;linkdatarelocation near_transfer_ext_entry
linkdatarelocation dmycmd
	extcall scan_dir_aux
	jc end_of_directory

reloc	cmp byte [relocateddata], ATTR_DIRECTORY
linkdatarelocation load_check_dir_attr, -3
	jne got_single_yyentry

	push si
	push di
reloc	mov byte [relocateddata], 0
linkdatarelocation load_check_dir_attr, -3
reloc	mov si, word [relocateddata]
linkdatarelocation load_yyname_next
	cmp byte [si], 0
	jne @F
	extcall ..@yy_filename_empty
@@:
	push es
	 push ss
	 pop es
	call boot_parse_name
	pop es
	cmp al, '/'
	jne @F
reloc	mov byte [relocateddata], ATTR_DIRECTORY
linkdatarelocation load_check_dir_attr, -3
reloc	mov word [relocateddata], si
linkdatarelocation load_yyname_next
@@:
	pop di
	pop si

	xor dx, dx
	mov ax, [es:bx + deClusterLow]
				; = first cluster (not FAT32)
	cmp byte [bp + ldFATType], 32
	jne @F
	mov dx, [es:bx + deClusterHigh]
				; dx:ax = first cluster (FAT32)
@@:

	jmp scan_dir_yyname_loop

boot_parse_name:
reloc	cmp word [wild_pattern], si
internaldatarelocation
	je @F
	extcallcall boot_parse_fn	; get next pathname

reloc2	mov word [relocateddata], relocateddata
linkdatarelocation handle_scan_dir_entry, -4
linkdatarelocation scan_dir_entry

	retn

@@:
	extcallcall boot_parse_fn	; get next pathname

reloc2	mov word [relocateddata], match_wildcard
linkdatarelocation near_transfer_ext_address, -4
internalcoderelocation
reloc2	mov word [relocateddata], relocateddata
linkdatarelocation handle_scan_dir_entry, -4
linkdatarelocation near_transfer_ext_entry

	push ax
reloc	mov di, relocateddata
linkdatarelocation load_kernel_name
	mov cx, 8
	call .handle_asterisk		; handle in basename
	mov cl, 3
	call .handle_asterisk		; handle in extension
	pop ax
	retn

.handle_asterisk:
	mov al, '*'
	repne scasb			; scan for asterisk
	jne @F				; not found -->
	mov al, '?'			; fill with question marks
	dec di				; -> at the asterisk
	inc cx				; = how many bytes remain
	rep stosb			; fill this field
@@:
	retn


		; INP:	es:di -> DIRENTRY
		;	dx:ax - 1 = sector
		;	cx, si in use
		; OUT:	CY if end of directory
		;	NC if to continue,
		;	 ZR if match
		;	 NZ if mismatch
		; CHG:	bx
match_wildcard:
	cmp byte [es:di], 0
	stc
	je .ret
	mov bl, byte [es:di + deAttrib]
	test bl, ATTR_VOLLABEL
	jnz .ret_NC		; skip volume labels (and LFNs) --> (NZ)
	push si
	push di
	push cx
reloc	mov si, relocateddata	; ds:si-> name to match
linkdatarelocation load_kernel_name
	mov cx, 11		; length of padded 8.3 FAT filename
@@:
	repe cmpsb		; check entry
	je @F			; match -->
	cmp byte [si - 1], '?'	; wildcard ?
	je @B			; yes --> (ZR)
				; no (NZ)
@@:
	pop cx
	pop di
	pop si
	jne .ret_NC

	push si
	push es
	push di
	push dx
	push ax
	push cx

	mov cx, DIRENTRY_size
	push ds
	 push es
	 pop ds
	mov si, di		; ds:si -> entry in directory buffer
	push ss
	pop es
reloc	mov di, relocateddata	; es:di -> destination for entry
linkdatarelocation load_yy_direntry
	push di
	rep movsb
	pop si
	pop ds

reloc	mov di, pathnamebuffer - 1
internaldatarelocation		; di -> buffer
	mov al, 0
	stosb
	push di
	mov cl, 8
	cmp byte [si], 05h
	jne @F
	mov al, 0E5h
	stosb
	dec cx
	inc si
@@:
	rep movsb
	mov cl, 9
	db __TEST_IMM8
@@:
	dec di
	cmp byte [di - 1], 32
	loope @B

	cmp byte [di - 1], '.'
	jne @F
	cmp word [si], 2020h
	jne @F
	cmp byte [si + 2], 32
	je .dotname
@@:

	mov al, '.'
	stosb
	mov cl, 3
	rep movsb
	mov cl, 4
	db __TEST_IMM8
@@:
	dec di
	cmp byte [di - 1], 32
	loope @B
	cmp byte [di - 1], '.'
	jne @F
	dec di
@@:
.dotname:
	mov al, 0
	stosb

	 push ss
	 pop es
	pop dx

		; get attrib byte
reloc	mov al, byte [relocateddata + deAttrib]
linkdatarelocation load_yy_direntry
reloc	mov byte [finddata.attrib], al
internaldatarelocation

		; get time and date
reloc	mov ax, word [relocateddata + deTime]
linkdatarelocation load_yy_direntry
reloc	mov word [finddata.time], ax
internaldatarelocation
reloc	mov ax, word [relocateddata + deDate]
linkdatarelocation load_yy_direntry
reloc	mov word [finddata.date], ax
internaldatarelocation

		; get size low
reloc	mov ax, word [relocateddata + deSize]
linkdatarelocation load_yy_direntry
reloc	mov word [finddata.sizelow], ax
internaldatarelocation
reloc	mov ax, word [relocateddata + deSize + 2]
linkdatarelocation load_yy_direntry
reloc	mov word [finddata.sizelow + 2], ax
internaldatarelocation

		; zero unused fields
	xor ax, ax
reloc	mov word [finddata.sizehigh + 2], ax
internaldatarelocation
reloc	mov byte [finddata.shortname], al
internaldatarelocation

		; get FAT+ high 6 size bits
reloc	mov al, byte [relocateddata + dePlusSize]
linkdatarelocation load_yy_direntry
	mov ah, al
	and ax, 0E007h
	shr ah, 1
	shr ah, 1
	or al, ah
	mov ah, 0
reloc	mov word [finddata.sizehigh], ax
internaldatarelocation

reloc	call near [matchfunction]
internaldatarelocation		; NZ to continue search, ZR to end

	pop cx
	pop ax
	pop dx
	pop di
	pop es
	pop si			; restore registers

%if 0
	pushf
	sub ax, 1
	sbb dx, 0
reloc	mov bx, [relocateddata]
linkdatarelocation load_adr_dirbuf_segment
	extcall read_sector	; restore sector
	popf
%endif

.ret_NC:
	clc
.ret:
	extcall near_transfer_ext_return, required ; must NOT be extcallcall


got_single_yyentry:
end_of_directory:
reloc2	cmp word [matchfunction], display_ZR
internaldatarelocation -4
internalcoderelocation
	jne ..@boot_scan_end
	retn

sort_or_display:
%endif
reloc	rol byte [sort], 1
internaldatarelocation
	jnc .display

	call populate_finddata
	je .next

%ifn _BOOT
reloc	rol byte [sortlfn], 1
internaldatarelocation
	jnc @F

reloc	mov si, finddata.fullname
internaldatarelocation
@@:
%endif

	mov di, si
	mov al, 0
	mov cx, 512
	repne scasb
	jne .error

	mov cx, di
	sub cx, si		; = length including NUL

reloc	mov di, word [sort_last]
internaldatarelocation
	mov ax, di
	add ax, cx
	jc .toofull
reloc	add ax, word [meta]
internaldatarelocation
	jc .toofull
reloc	cmp ax, word [sortendoffset]
internaldatarelocation
	ja .toofull

reloc	mov es, word [sortsegsel]
internaldatarelocation
reloc	mov di, word [sortstartoffset]
internaldatarelocation
	mov dx, si

.sort:
reloc	cmp di, word [sort_last]
internaldatarelocation
	jae .sort_insert_last

	mov bx, di

reloc	rol byte [dirfirst], 1
internaldatarelocation
	jnc .notdirfirst
reloc	mov al, byte [finddata.attrib]
internaldatarelocation
	mov ah, byte [es:di]
	and ax, (ATTR_DIRECTORY << 8) | ATTR_DIRECTORY
	cmp al, ah
	jne ..@compare_normal
	inc di
.notdirfirst:

reloc	rol byte [date], 1
internaldatarelocation
	jnc .notdate
reloc	mov ax, word [finddata.date]
internaldatarelocation
	cmp ax, word [es:di + 2]
	jne .sort_compare
reloc	mov ax, word [finddata.time]
internaldatarelocation
	cmp ax, word [es:di]
	jne .sort_compare
	; scasw
	; scasw
.notdate:

reloc	rol byte [size], 1
internaldatarelocation
	jnc .notsize
reloc	mov al, byte [size40bit + 4]
internaldatarelocation
	cmp al, byte [es:di + 4]
	jne .sort_compare
reloc	mov ax, word [size40bit + 2]
internaldatarelocation
	cmp ax, word [es:di + 2]
	jne .sort_compare
reloc	mov ax, word [size40bit]
internaldatarelocation
	cmp ax, word [es:di]
	jne .sort_compare
	; add di, 5
.notsize:

	mov di, bx
reloc	add di, word [meta]
internaldatarelocation
	mov si, dx
	mov al, 1
@@:
	cmp al, 0
	je .sort_insert_skip_bx
	lodsb
	mov ah, byte [es:di]
	inc di
	extcallcall uppercase
	xchg al, ah
	extcallcall uppercase
	cmp al, ah
	je @B
.sort_compare:
reloc	jmp near [compare]
internaldatarelocation

..@compare_reverse:
	jb .sort_insert_before_bx
	jmp .sort_insert_skip_bx

..@compare_normal:
	ja .sort_insert_before_bx
.sort_insert_skip_bx:
	push cx
	mov di, bx
reloc	add di, word [meta]
internaldatarelocation
	mov al, 0
	mov cx, 512
	repne scasb
	pop cx
	jmp .sort

.sort_insert_before_bx:
	houdini
reloc	mov di, word [sort_last]	; -> after last
internaldatarelocation
	mov si, di			; -> behind source
	add di, cx			; -> behind target
reloc	add di, word [meta]
internaldatarelocation
	push cx
	mov cx, si			; -> behind source
	sub cx, bx			; behind source minus start source
					;  = length of source
	std
	cmpsb				; -> last bytes
@@:
	es movsb
	loop @B				; move up trailer
	cld
	pop cx
reloc	add word [sort_last], cx
internaldatarelocation
reloc	mov di, word [meta]
internaldatarelocation
reloc	add word [sort_last], di
internaldatarelocation
	mov di, bx
	call .store_with_meta
	jmp .sort_inserted

.store_with_meta:
reloc	rol byte [dirfirst], 1
internaldatarelocation
	jnc @F
reloc	mov al, byte [finddata.attrib]
internaldatarelocation
	stosb
@@:

reloc	rol byte [size], 1
internaldatarelocation
	jnc @F
reloc	mov si, size40bit
internaldatarelocation
	movsw
	movsw
	movsb
@@:

reloc	rol byte [date], 1
internaldatarelocation
	jnc @F
reloc	mov ax, word [finddata.time]
internaldatarelocation
	stosw
reloc	mov ax, word [finddata.date]
internaldatarelocation
	stosw
@@:

	mov si, dx			; -> to insert, cx = length
	rep movsb			; store new entry
	retn

.sort_insert_last:
reloc	mov di, word [sort_last]	; -> after last
internaldatarelocation
	call .store_with_meta
reloc	mov word [sort_last], di	; update -> after last
internaldatarelocation

.sort_inserted:
	push ss
	pop es

	jmp @F

.display:
	call display_single
@@:

.next:
%if _BOOT
	test sp, sp		; NZ (continue search)
	retn
%else
reloc	mov ax, word [findnext_function_number]
internaldatarelocation
reloc	mov bx, word [findhandle]
internaldatarelocation
	push di
	mov si, 1
reloc	mov di, finddata
internaldatarelocation
	stc
	extcallcall _doscall
	pop di
	jnc .loop
%endif

%if _BOOT
..@boot_scan_end:
%endif

reloc	rol byte [found], 1
internaldatarelocation
	jnc .notfound
reloc	rol byte [sort], 1
internaldatarelocation
	jc sorted
reloc	rol byte [wide], 1
internaldatarelocation
	jnc @F

%if _BOOT
reloc	mov di, [widecurrent]
internaldatarelocation
%endif

	extcallcall trimputs
@@:
	extcallcall cmd3


.notfound:
reloc	mov dx, msg.notfound
internaldatarelocation
	mov ax, 0E22h
@@:
	extcallcall setrc
	extcallcall putsz
	extcallcall cmd3

.error:
reloc	mov dx, msg.error
internaldatarelocation
	mov ax, 0E23h
	jmp @B

.error2:
reloc	mov dx, msg.error2
internaldatarelocation
	mov ax, 0E24h
	jmp @B

.toofull:
reloc	mov dx, msg.toofull
internaldatarelocation
	mov ax, 0E25h
	jmp @B


sorted:
	houdini
%ifn _BOOT
	call findclose
%endif

reloc	mov di, word [pathname_start]
internaldatarelocation

.find_dir:
	mov bx, di		; bx -> start of path
	mov cx, -1
	mov al, 0
	repne scasb
	dec di
@@:
	cmp di, bx
	jbe .di
	dec di
	cmp byte [di], '/'
	je .di_plus_one
	cmp byte [di], '\'
	je .di_plus_one
	cmp byte [di], ':'
	jne @B
.di_plus_one:
	inc di
.di:				; di -> end of path
	mov cx, di
	sub cx, bx		; = length of path

reloc	mov word [pathname_length], cx
internaldatarelocation

reloc	mov di, relocateddata
linkdatarelocation line_out

.loop:
	push di
reloc	mov bx, word [pathname_length]
internaldatarelocation
reloc	mov si, word [pathname_start]
internaldatarelocation
	mov cx, bx
	cmp cx, buffer_end - buffer
	jae .error

reloc	mov di, buffer
internaldatarelocation
	rep movsb
	mov dx, di

reloc	mov di, word [sort_next]
internaldatarelocation
reloc	cmp di, word [sort_last]
internaldatarelocation
	jae .end
reloc	add di, word [meta]
internaldatarelocation
	mov si, di

reloc	mov es, word [sortsegsel]
internaldatarelocation
	mov al, 0
	mov cx, 512
	repne scasb
	jne .error
	mov cx, di
	sub cx, si

	add bx, cx
	cmp bx, buffer_end - !!_BOOT - buffer
	ja .error

	mov di, dx
%if _BOOT
reloc	cmp di, strict word buffer
internaldatarelocation
	je .slash
	cmp byte [di - 1], '/'
	je .notslash
	cmp byte [di - 1], '\'
	je .notslash
.slash:
	mov byte [di], '/'
	inc di
.notslash:
reloc	mov word [wild_pattern], di
internaldatarelocation
%endif
	 push es
	 push ds
	 pop es
	 pop ds				; swap
	rep movsb
	 push ss
	 pop ds				; reset
reloc	mov word [sort_next], si
internaldatarelocation

reloc	mov dx, buffer
internaldatarelocation
%if _BOOT
	pop di

	mov bx, dx
reloc	clropt [relocateddata], dif3_auxbuff_guarded_1
linkdatarelocation internalflags3, -3
	extcallcall yy_boot_init_dir2

reloc2	mov word [matchfunction], display_ZR
internaldatarelocation -4
internalcoderelocation

	call boot_scan

	jmp .loop

.error equ sort_or_display.error
.error2 equ sort_or_display.error2
%else
	call findfirst
	jc .error2

	call findclose
	pop di
	call display_single
	jmp .loop

.error equ run_dos.error
.error2 equ run_dos.error2
%endif

.end:
	pop di
reloc	rol byte [wide], 1
internaldatarelocation
	jnc @F

%if _BOOT
reloc	mov di, [widecurrent]
internaldatarelocation
%endif

	extcallcall trimputs
@@:
	extcallcall cmd3


populate_finddata:
%ifn _BOOT
reloc	cmp byte [findnext_function_number + 1], 71h
internaldatarelocation -3
	je .have_lfn

reloc	mov al, byte [dta.attrib]
internaldatarelocation
reloc	mov byte [finddata.attrib], al
internaldatarelocation

reloc	mov ax, word [dta.time]
internaldatarelocation
reloc	mov word [finddata.time], ax
internaldatarelocation

reloc	mov ax, word [dta.date]
internaldatarelocation
reloc	mov word [finddata.date], ax
internaldatarelocation

reloc	mov ax, word [dta.size]
internaldatarelocation
reloc	mov word [finddata.sizelow], ax
internaldatarelocation

reloc	mov ax, word [dta.size + 2]
internaldatarelocation
reloc	mov word [finddata.sizelow + 2], ax
internaldatarelocation

	xor ax, ax
reloc	mov word [finddata.sizehigh], ax
internaldatarelocation
reloc	mov word [finddata.sizehigh + 2], ax
internaldatarelocation

	push di
reloc	mov si, dta.name
internaldatarelocation
reloc	mov di, finddata.fullname
internaldatarelocation
	mov cx, words(14)
	rep movsw
	pop di

reloc	mov byte [finddata.shortname], al
internaldatarelocation

.have_lfn:
%endif

	push di
reloc	mov di, size40bit
internaldatarelocation
reloc	test byte [finddata.attrib], ATTR_DIRECTORY
internaldatarelocation -3
	jz @F
	xor ax, ax
reloc	mov word [finddata.sizelow], ax
internaldatarelocation
reloc	mov word [finddata.sizelow + 2], ax
internaldatarelocation
reloc	mov word [finddata.sizehigh], ax
internaldatarelocation
reloc	mov word [finddata.sizehigh + 2], ax
internaldatarelocation
@@:
reloc	cmp word [finddata.sizehigh + 2], strict byte 0
internaldatarelocation -3
	jne .sizelarge
reloc	cmp word [finddata.sizehigh], 256
internaldatarelocation -4
	jae .sizelarge
reloc	mov ax, word [finddata.sizelow]
internaldatarelocation
	stosw
reloc	mov ax, word [finddata.sizelow + 2]
internaldatarelocation
	stosw
reloc	mov al, byte [finddata.sizehigh]
internaldatarelocation
	stosb
	jmp @F

.sizelarge:
	mov ax, 0FFFFh
	stosw
	stosw
	stosb
@@:
	pop di

reloc	mov si, finddata.shortname
internaldatarelocation
	cmp byte [si], 0
	jne @F
reloc	mov si, finddata.fullname
internaldatarelocation
@@:

	cmp byte [si], '.'	; dot ?
	jne @F			; no --> NZ
	cmp word [si + 1], "."	; dot and NUL ?
	je .next		; dotdot, skip --> ZR
	cmp byte [si + 1], 0	; NUL ?
	; je .next		; dot, skip --> ZR
.next:
@@:
	je @F
reloc	or byte [found], -1
internaldatarelocation -3	; NZ
@@:
	retn


%if _BOOT
display_ZR:
%endif
display_single:
	call populate_finddata
	je .next

reloc	rol byte [wide], 1
internaldatarelocation
	jnc .notwide

.wide:
	houdini
%if _BOOT
reloc	mov di, [widecurrent]
internaldatarelocation
%endif

reloc	inc byte [widecounter]
internaldatarelocation
reloc	cmp byte [widecounter], 6
internaldatarelocation -3
	jb @F
	extcallcall trimputs
reloc	mov di, relocateddata
linkdatarelocation line_out
reloc	mov byte [widecounter], 1
internaldatarelocation -3
@@:

	mov ah, 32
	mov cx, 15		; width of column: 8 + 1 + 3 + 2 + 1
reloc	test byte [finddata.attrib], ATTR_DIRECTORY
internaldatarelocation -3
	jz @FF			; skip brackets and first iter stosb -->
	mov ax, "[]"		; use brackets
	dec cx			; decrement remaining width
@@:
	stosb			; dir first iter: store opening square bracket
				; file first iter: skipped
				; subsequent iter: store text byte
@@:
	lodsb			; load from name
	test al, al		; got a NUL ? do not loop if so
	loopnz @BB		; decrements width once per text byte
				;  and finally once more for the NUL
	mov al, ah		; get blank or closing square bracket
	stosb			; store blank or closing square bracket
				; (this store does not decrement the width)
	mov al, 32		; pad with blanks
	jcxz @F			; (error?) only once -->
	rep
@@:
	stosb			; store separator blanks

%if _BOOT
reloc	mov [widecurrent], di
internaldatarelocation
%endif
	jmp .next


.notwide:
reloc	mov di, relocateddata
linkdatarelocation line_out
	mov cx, 8

@@:
	lodsb
	cmp al, '.'
	je @F
	cmp al, 0
	je .no_ext
	stosb
	loop @B
	lodsb
	cmp al, '.'
	je @F
	dec si			; doesn't fit, nothing sensible here

@@:
	inc cx
	mov al, 32
	rep stosb

	mov cl, 3
@@:
	lodsb
	cmp al, 0
	je @F
	stosb
	loop @B
	jmp @F

.no_ext:
	add cx, 1 + 3

@@:
	inc cx
	inc cx
	mov al, 32
	rep stosb


reloc	mov bl, byte [finddata.attrib]
internaldatarelocation

	mov al, '-'
	test bl, ATTR_ARCHIVE
	jz @F
	mov al, 'A'
@@:
	stosb
	mov al, '-'
	test bl, ATTR_HIDDEN
	jz @F
	mov al, 'H'
@@:
	stosb
	mov al, '-'
	test bl, ATTR_READONLY
	jz @F
	mov al, 'R'
@@:
	stosb
	mov al, '-'
	test bl, ATTR_SYSTEM
	jz @F
	mov al, 'S'
@@:
	stosb
	mov ax, 2020h
	stosw

	test bl, ATTR_DIRECTORY
	jz @F
reloc	mov si, msg.dirinsteadsize
internaldatarelocation
.nosize:
	extcallcall copy_single_counted_string
	jmp @FF

@@:
reloc	cmp word [finddata.sizehigh + 2], strict byte 0
internaldatarelocation -3
	je .sizefits

reloc	mov si, msg.sizetoolarge
internaldatarelocation
	jmp .nosize

.sizefits:
reloc	mov si, word [finddata.sizehigh] 	; from FAT+ upper size bits
internaldatarelocation
reloc	mov dx, word [finddata.sizelow + 2]
internaldatarelocation
reloc	mov ax, word [finddata.sizelow]
internaldatarelocation
	xor cx, cx
	mov bx, 4+4
	extcallcall disp_dxax_times_cx_width_bx_size.store

@@:
	mov ax, 2020h
	stosw
reloc	mov bx, word [finddata.date]
internaldatarelocation
reloc	mov si, word [finddata.time]
internaldatarelocation

	xor dx, dx
	test bx, bx
	jnz @F
	mov ax, 2020h
	mov cx, 5
	rep stosw
	jmp .nodate

@@:
	mov ax, bx
	mov cx, 9
	shr ax, cl
	add ax, 1980
	mov cl, 4
	extcallcall dec_dword_minwidth
	mov al, '-'
	stosb
	mov ax, bx
	mov cl, 5
	shr ax, cl
	and ax, 15
	mov cl, 2
	extcallcall dec_dword_minwidth
	mov al, '-'
	stosb
	mov ax, bx
	and ax, 31
	extcallcall dec_dword_minwidth
.nodate:
	mov al, 32
	stosb
	mov ax, si
	mov cl, 11
	shr ax, cl
	mov cl, 2
	extcallcall dec_dword_minwidth
	mov al, ':'
	stosb
	mov ax, si
	mov cl, 5
	shr ax, cl
	and ax, 63
	mov cl, 2
	extcallcall dec_dword_minwidth
	mov al, ':'
	stosb
	mov ax, si
	and ax, 31
	shl ax, 1
	extcallcall dec_dword_minwidth

reloc	cmp byte [finddata.shortname], 0
internaldatarelocation -3
	je @F
	mov ax, 2020h
	stosw
	extcallcall putsline
reloc	mov dx, finddata.fullname
internaldatarelocation
	extcallcall putsz
reloc	mov dx, msg.linebreak
internaldatarelocation
	extcallcall putsz
	jmp .next

@@:
	extcallcall putsline_crlf

.next:
%if _BOOT
	cmp al, al		; ZR
%endif
	retn


%ifn _BOOT
findfirst:
reloc	mov word [findnext_function_number], 714Fh
internaldatarelocation -4
	mov ax, 714Eh
	mov cx, 16h		; find all files and directories
	mov si, 1
reloc	mov di, finddata
internaldatarelocation
reloc	rol byte [prefersfn], 1
internaldatarelocation
	jc .sfn_try
	stc
	extcallcall _doscall
	jnc .lfn_found
	cmp ax, 1
	je .sfn_try
	cmp ax, 7100h
	je .sfn_try
	stc	; CY
	retn

.lfn_found:
reloc	mov word [findhandle], ax
internaldatarelocation
reloc	mov byte [findhandle_set], 0FFh
internaldatarelocation -3
		; NC
	retn

.sfn_try:
reloc	mov word [findnext_function_number], 4F00h
internaldatarelocation -4
	mov ax, 4E00h
	mov cx, 16h		; find all files and directories
	extcallcall _doscall
		; CY if not found
	retn
%endif


%if _BOOT
find_dir:
	mov bx, di		; bx -> start of path
	mov cx, -1
	mov al, 0
	repne scasb
	dec di
@@:
	cmp di, bx
	jbe .di
	dec di
	cmp byte [di], '/'
	je .di_plus_one
	cmp byte [di], '\'
	je .di_plus_one
	cmp byte [di], ':'
	jne @B
.di_plus_one:
	inc di
.di:				; di -> end of path
	retn
%endif


parse_verbose.loop:
	lodsb
parse_verbose:
	extcallcall skipcomm0
	extcallcall iseol?
	jne .parse

.meta:
reloc	rol byte [date], 1
internaldatarelocation
	jnc @F
reloc	mov byte [size], 0
internaldatarelocation -3
reloc	add byte [meta], 4
internaldatarelocation -3
@@:

reloc	rol byte [size], 1
internaldatarelocation
	jnc @F
reloc	add byte [meta], 5
internaldatarelocation -3
@@:

reloc	rol byte [dirfirst], 1
internaldatarelocation
	jnc @F
reloc	inc byte [meta]
internaldatarelocation
@@:
	retn

.parse:
	dec si
%ifn _BOOT
reloc	mov dx, msg.string_sfn
internaldatarelocation
	extcallcall isstring?
	jne @F
reloc	mov byte [prefersfn], 0FFh
internaldatarelocation -3
	jmp .loop

@@:
%endif
reloc	mov dx, msg.string_wide
internaldatarelocation
	extcallcall isstring?
	jne @F
reloc	mov byte [wide], 0FFh
internaldatarelocation -3
	jmp .loop

@@:
reloc	mov dx, msg.string_sort
internaldatarelocation
	extcallcall isstring?
	jne @F
.loop_sort:
reloc	mov byte [sort], 0FFh
internaldatarelocation -3
	jmp .loop
@@:
reloc	mov dx, msg.string_dirfirst
internaldatarelocation
	extcallcall isstring?
	jne @F
reloc	mov byte [dirfirst], 0FFh
internaldatarelocation -3
	jmp .loop

@@:
reloc	mov dx, msg.string_date
internaldatarelocation
	extcallcall isstring?
	jne @F
reloc	mov byte [date], 0FFh
internaldatarelocation -3
	jmp .loop
@@:
reloc	mov dx, msg.string_size
internaldatarelocation
	extcallcall isstring?
	jne @F
reloc	mov byte [size], 0FFh
internaldatarelocation -3
	jmp .loop

@@:
reloc	mov dx, msg.string_reverse
internaldatarelocation
	extcallcall isstring?
	jne @F
reloc2	mov word [compare], ..@compare_reverse
internaldatarelocation -4
internalcoderelocation
	jmp .loop

@@:
%ifn _BOOT
reloc	mov dx, msg.string_sortlfn
internaldatarelocation
	extcallcall isstring?
	jne @F
reloc	mov byte [sortlfn], 0FFh
internaldatarelocation -3
	jmp .loop_sort

@@:
reloc	mov dx, msg.string_sortsfn
internaldatarelocation
	extcallcall isstring?
	jne @F
reloc	mov byte [sortlfn], 0
internaldatarelocation -3
	jmp .loop_sort

@@:
%endif
%if _DEBUG
reloc	mov dx, msg.string_sortdebug
internaldatarelocation
	extcallcall isstring?
	je @F
%endif
	extcallcall error

%if _DEBUG
@@:
	lodsb
	extcallcall chkeol

	call .meta

%if _BOOT
	extcallcall yy_boot_init_dir2

	mov dx, word [bp + bsBPB + bpbBytesPerSector]
					; di = sector size
					; = start of available auxbuff
	cmp byte [bp + ldFATType], 12	; FAT12 ?
	jne @F				; no -->
	cmp dx, 8192			; already enough for a full FAT ?
	jae @F				; yes -->
	add dx, dx			; reserve straddling sector
@@:
%endif

reloc	mov di, relocateddata
linkdatarelocation line_out

reloc	mov si, msg.debug.1
internaldatarelocation
	extcallcall copy_single_counted_string

	mov ax, ss
	extcallcall hexword
	mov al, ':'
	stosb
reloc	mov ax, buffer_end
internaldatarelocation
	extcallcall hexword

reloc	mov ax, word [relocateddata]
linkdatarelocation extdata_size
reloc	sub ax, word [relocateddata]
linkdatarelocation extdata_used
reloc	mov bx, word [relocateddata]
linkdatarelocation auxbuff_current_size
%if _BOOT
	sub bx, dx
	push dx
%endif

reloc	mov si, msg.debug.2
internaldatarelocation
	extcallcall copy_single_counted_string

	add ax, (uinit_data_end_2 - buffer_end)
	call display_buffer_size

reloc	mov si, msg.debug.3
internaldatarelocation
	extcallcall copy_single_counted_string

reloc	mov dx, msg.debug.larger_data_block
internaldatarelocation

	cmp ax, bx
	jae .use_sort_buffer_eld_data_block

reloc	mov dx, msg.debug.smaller_data_block
internaldatarelocation

%ifn _BOOT
reloc	testopt [relocateddata], dif3_auxbuff_guarded_1 \
		| dif3_auxbuff_guarded_2 \
		| dif3_auxbuff_guarded_3
linkdatarelocation internalflags3, -3
	jz .use_sort_buffer_auxbuff

reloc	mov dx, msg.debug.smaller_data_block_forced
internaldatarelocation
%endif

.use_sort_buffer_eld_data_block:
.use_sort_buffer_auxbuff:

reloc	mov ax, word [relocateddata]
linkdatarelocation auxbuff_segorsel	; => sort buffer
	extcallcall hexword

%if _BOOT
	mov al, ':'
	stosb
	pop ax
	extcallcall hexword
%endif

	xchg ax, bx
reloc	mov si, msg.debug.5
internaldatarelocation
	extcallcall copy_single_counted_string
	call display_buffer_size

reloc	mov si, msg.debug.6.available
internaldatarelocation

reloc	testopt [relocateddata], dif3_auxbuff_guarded_1 \
		| dif3_auxbuff_guarded_2 \
		| dif3_auxbuff_guarded_3
linkdatarelocation internalflags3, -3
	jz @F

reloc	mov si, msg.debug.6.inuse
internaldatarelocation
@@:

	extcallcall copy_single_counted_string
	push dx
	extcallcall putsline_crlf
	pop dx

	extcallcall putsz
	extcallcall cmd3


display_buffer_size:
	extcallcall decword
reloc	mov si, msg.debug.bytes
internaldatarelocation
	extcallcall copy_single_counted_string
	push ax
	push dx
	xor dx, dx
	mov si, 8 + 1 + 3 + 1		; ASCIZ SFN maximum length
reloc	add si, word [meta]
internaldatarelocation
	div si
	extcallcall decword
	pop dx
	pop ax
	retn
%endif


%ifn _BOOT
inject:
reloc	mov dx, word [originaldta]	; dx -> original DTA
internaldatarelocation
	mov ah, 1Ah			; set DTA (restoring)
	extcallcall ispm
	jnz .rm

subcpu 286
reloc	push word [originaldta + 2]
internaldatarelocation			; ds => original DTA (86M segment)
	push word 21h
	extcall intcall_ext_return_es, PM required	; must NOT be extcallcall
	pop dx				; discard interrupt number
	pop dx				; discard es
subcpureset
	jmp @F
.rm:
reloc	mov ds, word [originaldta + 2]	; ds => original DTA
internaldatarelocation
	int 21h
	 push ss
	 pop ds
@@:

	call findclose

reloc	mov cx, word [originalinject]
internaldatarelocation
	call get_es_ext
reloc	clropt [es:code + eldiFlags], eldifResident
internalcoderelocation -3		; mark block as no longer resident
	 push ss
	 pop es
	jcxz @F
	jmp cx

@@:
	extcall cmd3_not_inject


findclose:
reloc	mov bx, word [findhandle]	; bx = find handle, if any
internaldatarelocation
reloc	rol byte [findhandle_set], 1	; find handle set ?
internaldatarelocation
	jnc @F				; no -->
reloc	not byte [findhandle_set]
internaldatarelocation
	mov ax, 71A1h
	extcallcall _doscall		; LFN find close
@@:
	retn
%endif


get_es_ext:
reloc	mov es, word [relocateddata]
linkdatarelocation extdssel
	extcallcall ispm
	jz @F
reloc	mov es, word [relocateddata]
linkdatarelocation extseg
@@:
	retn


start:
	mov bx, es
	 push ss
	 pop es
	extcallcall skipcomma
	extcallcall iseol?
	je .help
	call run
@@:
	call uninstall_oneshot
	xor ax, ax
	retf

.help:
reloc	mov dx, msg.help
internaldatarelocation
	extcallcall putsz
	jmp @B


uninstall_oneshot:
reloc	testopt [ss:relocateddata], 1
linkdatarelocation options7, -3
	jnz @F

reloc	mov ax, word [cs:code + eldiEndCode]
internalcoderelocation
reloc	sub ax, word [cs:code + eldiStartCode]
internalcoderelocation
reloc	sub word [relocateddata], ax
linkdatarelocation extseg_used

reloc	mov ax, word [cs:code + eldiEndData]
internalcoderelocation
reloc	sub ax, word [cs:code + eldiStartData]
internalcoderelocation
reloc	sub word [relocateddata], ax
linkdatarelocation extdata_used
@@:
	retn


DATAOFFSET equ CODEOFFSET + code_size
	addsection DATA, follows=CODE vstart=_ELD_DATA_VSTART
%define DATAFIXUP - datastart + 0
datastart:
PUT_ELD_DATETIME

%if _BOOT
reloc matchfunction:	dw sort_or_display
internalcoderelocation
reloc widecurrent:	dw relocateddata
linkdatarelocation line_out
%endif
meta:			dw 0
reloc compare:		dw ..@compare_normal
internalcoderelocation
found:			db 0
%ifn _BOOT
findhandle_set:		db 0
prefersfn:		db 0
%endif
wide:			db 0
widecounter:		db 0
sort:			db 0
%ifn _BOOT
sortlfn:		db 0
%endif
dirfirst:		db 0
date:			db 0
size:			db 0

msg:
%ifn _BOOT
.string_sfn:		asciz "SFN"
%endif
.string_wide:		asciz "WIDE"
.string_sort:		asciz "SORT"
.string_dirfirst:	asciz "DIRFIRST"
.string_date:		asciz "DATE"
.string_size:		asciz "SIZE"
.string_reverse:	asciz "REVERSE"
%ifn _BOOT
.string_sortlfn:	asciz "SORTLFN"
.string_sortsfn:	asciz "SORTSFN"
%endif
%if _DEBUG
.string_sortdebug:	asciz "SORTDEBUG"
.debug.1:			counted "ELD data block buffer at "
.debug.2:			counted ", size "
.debug.bytes:			counted " bytes, >="
.debug.3:			counted " SFN entries",13,10,"Auxiliary buffer at "
%if _BOOT
.debug.5 equ .debug.2
%else
.debug.5:			counted ":0000, size "
%endif
.debug.6.available:		counted " SFN entries (available)"
.debug.6.inuse:			counted " SFN entries (in use)"
.debug.larger_data_block:	asciz "Data block chosen because it is larger (above-or-equal size).",13,10
.debug.smaller_data_block:	asciz "Auxiliary buffer chosen because it is larger and available.",13,10
 %ifn _BOOT
.debug.smaller_data_block_forced:asciz "Smaller data block chosen because auxiliary buffer not available.",13,10
 %endif
%endif
.error:			asciz "Too long short name, internal error!",13,10
.error2:		asciz "File not found, internal error!",13,10
.toofull:		asciz "Filenames list does not fit in buffer!",13,10
.dirinsteadsize:	countedb "   [DIR]"
.sizetoolarge:		countedb ">256 TiB"
.notfound:		db "File not found."
.linebreak:		asciz 13,10
.help:		db "Run with a pathname as parameter.",13,10
		db "Last component may include wildcards.",13,10
		db 13,10
		db "After the pathname, keywords may follow:",13,10
%ifn _BOOT
		db 9,"SFN",9,"Use DOS SFN search even if LFN available",13,10
		db 9,"WIDE",9,"Display in wide mode, up to 5 SFNs per line",13,10
		db 9,"SORT",9,"Sort filenames, default is SFNs. Must fit in buffer!",13,10
		db 9,"SORTSFN",9,"Same as SORT, but explicitly sort SFNs",13,10
		db 9,"SORTLFN",9,"Sort filenames using their LFNs",13,10
%else
		db 9,"WIDE",9,"Display in wide mode, up to 5 SFNs per line",13,10
		db 9,"SORT",9,"Sort filenames. Must fit in buffer!",13,10
%endif
		db 9,"DATE",9,"When sorting, sort by date first",13,10
		db 9,"SIZE",9,"When sorting, sort by size first",13,10
		db 9,"REVERSE",9,"When sorting, reverse name/DATE/SIZE sort order",13,10
		db 9,"DIRFIRST",9,"When sorting, sort directories first",13,10
		asciz

uinit_data: equ $

	align 16, db 0
data_size equ $ - datastart

	absolute uinit_data

	alignb 2
%if _BOOT
wild_pattern:	resw 1
%else
originaldta:	resd 1
originalinject:	resw 1
%endif

sortsegsel:	resw 1
sortstartoffset:resw 1
sortendoffset:	resw 1
sort_last:	resw 1
sort_next:	resw 1

pathname_start:	resw 1
pathname_length:resw 1
%ifn _BOOT
findnext_function_number:
		resw 1
findhandle:	resw 1
%endif

buffer:
%ifn _BOOT
dta:		resb 15h
.attrib:	resb 1
.time:		resw 1
.date:		resw 1
.size:		resd 1
.name:		resb 13
%endif

	alignb 2
finddata:
.attrib:	resd 1
		resq 2
.time:		resw 1
.date:		resw 1
		resd 1
.sizehigh:	resd 1
.sizelow:	resd 1
		resb 8
pathnamebuffer equ $
.fullname:	resb 260
.shortname:	resb 14
size40bit:	resb 5

	alignb 16
uinit_data_end_1:

	absolute buffer
		resb 512
buffer_end:

	alignb 16
uinit_data_end_2:

%if uinit_data_end_2 > uinit_data_end_1
 uinit_data_end equ uinit_data_end_2
%else
 uinit_data_end equ uinit_data_end_1
%endif


total_data_size equ uinit_data_end - datastart
%assign _DATA_SIZE total_data_size

	usesection CODE

%include "eldlink.asm"

	align 16
code_size equ $ - code
