
; Public Domain

; test run command: ; .; .

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


	numdef RESIDENT, 1

%if 0
Format of DOS Drive Parameter Block:
Offset	Size	Description	(Table 01395)
 00h	BYTE	drive number (00h = A:, 01h = B:, etc)
 01h	BYTE	unit number within device driver
 02h	WORD	bytes per sector
 04h	BYTE	highest sector number within a cluster
 05h	BYTE	shift count to convert clusters into sectors
 06h	WORD	number of reserved sectors at beginning of drive
 08h	BYTE	number of FATs
 09h	WORD	number of root directory entries
 0Bh	WORD	number of first sector containing user data
 0Dh	WORD	highest cluster number (number of data clusters + 1)
		16-bit FAT if greater than 0FF6h, else 12-bit FAT
---DOS 4.0-6.0---
 0Fh	WORD	number of sectors per FAT
 11h	WORD	sector number of first directory sector
 13h	DWORD	address of device driver header (see #01646)
 17h	BYTE	media ID byte (see #01356)
 18h	BYTE	00h if disk accessed, FFh if not
 19h	DWORD	pointer to next DPB
 1Dh	WORD	cluster at which to start search for free space when writing,
		usually the last cluster allocated
 1Fh	WORD	number of free clusters on drive, FFFFh = unknown

Format of Extended Drive Parameter Block:
Offset	Size	Description	(Table 01787)
 00h 24 BYTEs	standard DOS 4+ DPB
 18h	BYTE	"dpb_flags" (undocumented)
		FFh force media check
 19h	DWORD	pointer to next DPB (see note)
 1Dh	WORD	cluster at which to start search for free space when writing,
		usually the last cluster allocated
 1Fh	WORD	number of free clusters on drive, FFFFh = unknown
 21h	WORD	high word of free cluster count
 23h	WORD	active FAT/mirroring
		bit 7: do not mirror active FAT to inactive FATs
		bits 6-4: reserved (0)
		bits 3-0: the 0-based FAT number of the active FAT
		    (only meaningful if mirroring disabled)
 25h	WORD	sector number of file system information sector, or
		  FFFFh for none (see also #01788)
 27h	WORD	sector number of backup boot sector, or FFFFh for none
 29h	DWORD	first sector number of the first cluster
 2Dh	DWORD	maximum cluster number
 31h	DWORD	number of sectors occupied by FAT
 35h	DWORD	cluster number of start of root directory
 39h	DWORD	cluster number at which to start searching for free space
Notes:	except for offset 18h, all of the first 33 bytes are identical to
	  the standard DOS 4-6 DPB
	unless the proper value is given in SI on entry to "Get_ExtDBP", the
	  next-DPB pointer and device driver address are set to 0000h:0000h
SeeAlso: #01786,#01395 at AH=32h,#01664

%endif

	struc DPB
dpbDrive:			resb 1
dpbDeviceUnit:			resb 1
dpbBytesPerSector:		resw 1
dpbHighestSectorInCluster:	resb 1
dpbSectorsPerClusterShift:	resb 1
dpbReservedSectors:		resw 1
dpbAmountFATs:			resb 1
dpbAmountRootEntries:		resw 1
dpbDataStart:			resw 1
dpbMaximumCluster:		resw 1
dpbSectorsPerFAT:		resw 1
dpbRootStart:			resw 1
dpbDeviceHeader:		resd 1
dpbMediaID:			resb 1
dpbAccessed:			resb 1
dpbNext:			resd 1
dpbFreeClusterNext:		resw 1
dpbAmountFreeClusters:		resw 1
	endstruc

	struc EFDPB
efdpbDPB:			resb DPB_size
efdpbAmountFreeClusters:	equ $ - 2
				resw 1
efdpbFSFlags:			resw 1
efdpbFSInfoSector:		resw 1
efdpbBackupSector:		resw 1
efdpbDataStart:			resd 1
efdpbMaximumCluster:		resd 1
efdpbSectorsPerFAT:		resd 1
efdpbRootCluster:		resd 1
efdpbFreeClusterNext:		resd 1
	endstruc

	struc EDDPB
eddpbDPB:			resb DPB_size
eddpbAmountFreeClusters:	resd 1		; differs from EFDPB
eddpbFSFlags:			resw 1
eddpbFSInfoSector:		resw 1
eddpbBackupSector:		resw 1
eddpbDataStart:			resd 1
eddpbMaximumCluster:		resd 1
eddpbSectorsPerFAT:		resd 1
eddpbRootCluster:		resd 1
eddpbFreeClusterNext:		resd 1
eddpbFSVersion:			resw 1		; differs from EFDPB
	endstruc


	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
%if _RESIDENT
at eldhxHelpOffset,		dd DATAOFFSET + msg.help - datastart
%endif
PUT_ELDHX_DATETIME_OFFSET
header_extension_end:
	iend

description:		asciz "Display DOS DPB."


	align 16, db 0

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
at eldiIdentifier,	fill 8, 32, db "DPB"
at eldiListing,		asciz _ELD_LISTING
	iend


%if _RESIDENT
command:
	jmp strict short .entry
.chain:
	extcall cmd3_not_ext, required	; must NOT be extcallcall
	times 10 - ($ - command) nop
.entry:
	push si
	cmp al, '-'
	jne @F
	extcallcall skipcomma
@@:
	dec si

reloc	mov dx, msg.dpb
internaldatarelocation
	extcallcall isstring?
	je .ours
	pop si
	dec si
	lodsb
	jmp .chain

.ours:
	pop ax
	extcallcall skipcomma
	dec si
reloc2	mov word [relocateddata], relocateddata
linkdatarelocation lastcmd, -4
linkdatarelocation dmycmd
reloc	mov dx, relocateddata
linkdatarelocation msg.uninstall
	extcallcall isstring?
	je uninstall

	call run
	extcallcall cmd3

uninstall:
	lodsb
	extcallcall chkeol

	call get_es_ext

	push es
	pop ds
	xor bx, bx		; = 0 (no prior, modify ext_command_handler)
reloc	mov di, command		; di -> us
internalcoderelocation
reloc	mov si, word [ss:relocateddata]
linkdatarelocation ext_command_handler
				; si -> first
	test si, si		; none installed ?
	jz .error		; error -->

.loop:
	cmp di, si		; found ?
	je .bx			; yes, use bx -->
	mov bx, si		; bx -> prior handler
	lodsw			; skip entrypoint jmp strict short
	lodsb			; get first byte of chainer
	cmp al, 0E9h		; expecting jmp near ?
	jne .error		; no, error -->
	lodsw			; get rel16 displacement
	add si, ax		; -> next handler
	jmp .loop

.bx:
	test bx, bx		; any prior ?
	jnz .bxnz		; yes -->
	scasw			; skip entrypoint jmp strict short
	cmp byte [di], 0E8h	; is it a call to cmd3_not_ext ?
	jne @F			; no -->
				; yes, reset ext_command_handler to zero
.setbx:
reloc	mov word [ss:relocateddata], bx
linkdatarelocation ext_command_handler
	jmp .done

@@:
	cmp byte [di], 0E9h	; validate
	jne .error		; failure -->
	inc di			; -> rel16 displacement
	mov bx, word [di]	; get displacement
	scasw			; -> after jmp near
	add bx, di		; -> next handler
	jmp .setbx		; set ext_command_handler to next

.bxnz:
	mov si, bx		; -> prior handler with us as downlink
	xchg di, si		; si -> ours, di -> prior
	cmpsw			; skip entrypoint jmp strict short
	movsb			; copy 0E8h/0E9h
	lodsw			; ax = near rel16 displacement
	add ax, si		; add in our base (= absolute offset)
	sub ax, di
	dec ax
	dec ax			; subtract new base (= relative displacement)
	stosw			; store new rel16 displacement
	movsw			; jmp strict short
	movsw			; linkcall target
	movsb			; trailer
.done:
reloc	clropt [code + eldiFlags], eldifResident
internalcoderelocation -3	; mark block as free
reloc	mov dx, msg.uninstall_done
internaldatarelocation
@@:
	push ss
	pop ds
	extcallcall putsz
	extcallcall cmd3	; return

.error:
	mov ax, 0E01h
	extcallcall setrc
reloc	mov dx, msg.uninstall_error
internaldatarelocation
	jmp @B


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


run:
	mov word [tablelist.base], table_base
internaldatarelocation -4
internaldatarelocation
	xor ax, ax
	mov word [tablelist.edpb], ax
internaldatarelocation -2
	mov word [listat], ax
internaldatarelocation -2
	mov word [listat + 2], ax
internaldatarelocation -2
%else
start:
	mov bx, es
%endif
	 push ss
	 pop es
	extcallcall skipcomma
	dec si
	mov dx, msg.only
internaldatarelocation
	extcallcall isstring?
	jne @F
	extcallcall skipcomma
	dec si
	mov word [tablelist.base], table_empty
internaldatarelocation -4
internaldatarelocation
@@:
	mov dx, msg.extended
internaldatarelocation
	extcallcall isstring?
	jne .old
@@:
	extcallcall skipcomma
	cmp al, '='
	je @B
	dec si
	mov bx, extendedkeywords
internaldatarelocation
@@:
	mov dx, [bx]
	test dx, dx
	jz .error
	extcallcall isstring?
	je @F
	add bx, 6
	jmp @B

@@:
	mov ax, [bx + 2]
	mov word [tablelist.edpb], ax
internaldatarelocation
	mov ax, [bx + 4]
	mov word [size], ax
internaldatarelocation
	extcallcall skipcomma
	dec si
	jmp @F

.old:
	mov word [size], DPB_size
internaldatarelocation -4
@@:

	mov dx, msg.list
internaldatarelocation
	extcallcall isstring?
	jne .check_address

.list:
	extcallcall skipcomma
	dec si
	mov dx, msg.at
internaldatarelocation
	extcallcall isstring?
	jne .not_list_at
	extcallcall skipcomma
	xor dx, dx
	extcallcall setes2dx
	mov bx, word [es:31h * 4 + 2]		; default segment
	push ss
	pop es

	testopt [relocateddata], 8000h
linkdatarelocation internalflags, -3
	jz @F
subcpu 386
	xor edx, edx
	extcallcall getaddrX
	test edx, 0FFFF_0000h
	jnz .error
	jmp @FF

@@:
		; evil: we're using getaddrX to get a 86 Mode segmented
		;  address, even if we are in Protected Mode. this happens
		;  to work fine however; test_high_limit cannot fault.
	extcallcall getaddrX
@@:
	mov word [listat], dx
internaldatarelocation
	mov word [listat + 2], bx
internaldatarelocation
	dec si
	jmp .list

.not_list_at:
	mov dx, msg.skip
internaldatarelocation
	extcallcall isstring?
	je .listskip
	mov dx, msg.drive
internaldatarelocation
	extcallcall isstring?
	je .listdrive
	jmp .error

.listdrive:
	extcallcall skipcomma
	call getdrive
	test dl, dl
	jnz @F
	call checkdos
	mov ah, 19h
	extcallcall _doscall
	push ax
	db __TEST_IMM16		; skip dec, push
@@:
	dec dx
	push dx
	call getlist
	je .notfound
	pop cx
	xor si, si
@@:
	mov dx, word [es:bx + 2]
	mov bx, word [es:bx]
	extcallcall setes2dx
	cmp bx, -1
	je .notfound
	cmp byte [es:bx + dpbDrive], cl
	je .found
	dec si
	jz .notfound
	add bx, dpbNext
	jmp @B


.listskip:
	extcallcall skipcomma
	xor dx, dx
	extcallcall iseol?
	je @F
	extcallcall getbyte
	extcallcall chkeol
@@:
	mov si, dx
	call getlist
	je .notfound
@@:
	mov dx, word [es:bx + 2]
	mov bx, word [es:bx]
	extcallcall setes2dx
	cmp bx, -1
	je .notfound
	test si, si
	jz @F
	dec si
	add bx, dpbNext
	jmp @B

.notfound:
	mov ax, 0E60h
	extcallcall setrc
	mov dx, msg.notfound
internaldatarelocation
	extcallcall putsz
	extcallcall cmd3

@@:
.found:
	 push ds
	 push es
	pop ds
	pop es

	mov di, buffer
internaldatarelocation
	mov si, bx
	mov cx, [ss:size]
internaldatarelocation
	rep movsb

	push ss
	pop ds

	mov ax, dx
	mov di, msg.foundaddress.address
internaldatarelocation
	extcallcall hexword
	inc di
	mov ax, bx
	extcallcall hexword

	mov dx, msg.foundaddress
internaldatarelocation
	extcallcall putsz

	jmp .have


.check_address:
	mov dx, msg.address
internaldatarelocation
	extcallcall isstring?
	lodsb
	jne .calldos

	extcallcall skipcomm0
	testopt [relocateddata], 8000h
linkdatarelocation internalflags, -3
	jz @F
subcpu 386
	xor edx, edx			; edxh = 0 on 386
subcpureset
@@:
	mov bx, word [relocateddata]
linkdatarelocation reg_ds		; undocumented: default segment
	extcallcall getaddrX
	extcallcall chkeol
	extcallcall ispm
	clc				; NC if no 386 or not PM
	jnz @F
	testopt [relocateddata], 8000h
linkdatarelocation internalflags, -3
	jz @F
subcpu 386
	xor edi, edi			; edih = 0 on PM 386
	xor ecx, ecx			; ecxh = 0 on PM 386
	stc				; CY if 386 and PM
	o32	; mov esi, edx
subcpureset
@@:
	mov si, dx
	mov ds, bx			; ds:(e)si -> source

	mov di, buffer			; es:(e)di -> destination
internaldatarelocation
	mov cx, word [ss:size]		; (e)cx = length
internaldatarelocation
	jnc @F
	a32	; a32 rep movsb
@@:
	rep movsb			; get data

	push ss
	pop ds
	jmp .have


checkdos: equ $
	extcallcall InDOS
	jz @F
.error:
	extcallcall error

@@:
	retn

.calldos:
	call checkdos
	call getdrive

	xor bx, bx
	mov ax, 32FFh
	extcallcall ispm
	jnz .86mode
.pm:
subcpu 286
	push word [relocateddata]
linkdatarelocation pspdbg		; es
	push word [relocateddata]
linkdatarelocation pspdbg		; ds
	push word 21h
	push bp
	call intcall_return_parameter_es_parameter_ds
	pop dx				; get ds
	pop cx				; (discard es)
	mov word [address + 2], dx
internaldatarelocation
	extcallcall setes2dx		; es => DPB
	push ds
	push es
	pop ds
	pop es				; swap ds, es
subcpureset
	jmp @F
.86mode:
	int 21h
	mov word [ss:address + 2], ds
internaldatarelocation
@@:
	mov word [ss:address], bx
internaldatarelocation


	mov dx, msg.return
internaldatarelocation
	mov di, msg.return.code
internaldatarelocation
	extcallcall hexbyte

	mov di, buffer
internaldatarelocation
	mov si, bx
	mov cx, [ss:size]
internaldatarelocation
	rep movsb

	push ss
	pop ds
	extcallcall putsz

	cmp al, 0
	jne .end

	mov ax, word [address + 2]
internaldatarelocation
	mov di, msg.returnaddress.address
internaldatarelocation
	extcallcall hexword
	inc di
	mov ax, word [address]
internaldatarelocation
	extcallcall hexword

	mov dx, msg.returnaddress
internaldatarelocation
	extcallcall putsz

.have:
	mov si, tablelist
internaldatarelocation
.loop:
	lodsw
	test ax, ax
	jz .end
	push si
	xchg si, ax
	call processtable
	pop si
	jmp .loop

.end:
%if _RESIDENT
	retn
%else
	call uninstall_oneshot
	xor ax, ax
	retf
%endif


processtable:
.loop:
	lodsw
	test ax, ax
	jz .end
	xchg bx, ax

	lodsw
	xchg cx, ax

	mov di, relocateddata
linkdatarelocation line_out

	mov ax, bx
	sub ax, strict word buffer
internaldatarelocation
	extcallcall hexbyte
	mov ax, ": "
	stosw

	mov ax, word [bx]
	mov dx, word [bx + 2]

	cmp cl, 2
	jb .byte
	je .word
.dword:
	xchg ax, dx
	extcallcall hexword
	mov byte [di], '_'
	inc di
	xchg ax, dx
	extcallcall hexword

	mov word [di], 2020h
	scasw
	extcallcall decdword
	jmp @FF

.word:
	extcallcall hexword
	jmp @F

.byte:
	extcallcall hexbyte

	mov ah, 0
@@:
	mov word [di], 2020h
	scasw
	extcallcall decword
@@:

	lodsw
	xchg dx, ax
	extcallcall putsz
	extcallcall putsline_crlf
	jmp .loop

.end:
	retn


subcpu 286
intcall_return_parameter_es_parameter_ds:
	lframe near
	lpar word, es_value
	lpar word, ds_value
	lpar_return
	lpar word, int_number
	lpar word, bp_value
	lvar 32h, 86m_call_struc
	lenter
	push es
	mov word [bp + ?86m_call_struc +00h], di	; edi
	mov word [bp + ?86m_call_struc +04h], si	; esi
	mov word [bp + ?86m_call_struc +10h], bx	; ebx
	mov word [bp + ?86m_call_struc +14h], dx	; edx
	mov word [bp + ?86m_call_struc +18h], cx	; ecx
	mov word [bp + ?86m_call_struc +1Ch], ax	; eax
	mov ax, word [bp + ?bp_value]
	mov word [bp + ?86m_call_struc +08h], ax	; bp
	mov al, 0					; (preserve flags!)
	lahf
	xchg al, ah
	mov word [bp + ?86m_call_struc +20h], ax	; flags
	xor ax, ax
	mov word [bp + ?86m_call_struc +0Ch + 2], ax
	mov word [bp + ?86m_call_struc +0Ch], ax
	mov word [bp + ?86m_call_struc +2Eh], ax	; sp
	mov word [bp + ?86m_call_struc +30h], ax	; ss
	mov ax, word [bp + ?es_value]			; usually [pspdbg]
	mov word [bp + ?86m_call_struc +22h], ax	; es
	mov ax, word [bp + ?ds_value]			; usually [pspdbg]
	mov word [bp + ?86m_call_struc +24h], ax	; ds
	push ss
	pop es				; => stack
	lea di, [bp + ?86m_call_struc]	; -> 86-Mode call structure
	testopt [relocateddata], 8000h
linkdatarelocation internalflags, -3
	jz @F
subcpu 386
	movzx edi, di			; (previously checked b[dpmi32] here)
subcpureset
@@:

	mov bx, word [bp + ?int_number]			; int#
	xor cx, cx
	mov ax, 0300h
	int 31h
	mov ah, byte [bp + ?86m_call_struc +20h]	; flags
	sahf
	mov di, word [bp + ?86m_call_struc +00h]	; edi
	mov si, word [bp + ?86m_call_struc +04h]	; esi
	mov bx, word [bp + ?86m_call_struc +10h]	; ebx
	mov dx, word [bp + ?86m_call_struc +14h]	; edx
	mov cx, word [bp + ?86m_call_struc +18h]	; ecx
	mov ax, word [bp + ?86m_call_struc +1Ch]	; eax
	push word [bp + ?86m_call_struc +22h]		; return es value
	pop word [bp + ?es_value]			;  in the parameter
	push word [bp + ?86m_call_struc +24h]		; return ds value
	pop word [bp + ?ds_value]			;  in the parameter
	pop es
	lleave
	lret
subcpureset


getdrive:
	xor dl, dl
	extcallcall iseol?
	je .ret
	cmp byte [si], ':'
	jne .number
	extcallcall uppercase
	mov dl, al
	sub dl, 'A'
	cmp dl, 32
	jae .number
	inc dx
	inc si
	lodsb
	jmp @F

.number:
	extcallcall getbyte
@@:
	extcallcall chkeol
.ret:
	retn


getlist:
	mov bx, word [listat]
internaldatarelocation
	mov dx, word [listat + 2]
internaldatarelocation
	mov ax, dx
	or ax, bx
	jnz .dxbx

.dos:
	call checkdos
	mov bx, -1
	mov ah, 52h
	extcallcall ispm
	jnz .86mode
.pm:
subcpu 286
	push word [relocateddata]
linkdatarelocation pspdbg		; es
	push word [relocateddata]
linkdatarelocation pspdbg		; ds
	push word 21h
	push bp
	call intcall_return_parameter_es_parameter_ds
	pop cx				; discard ds
	pop dx				; get es
.dxbx:
	extcallcall setes2dx		; es => list of lists
subcpureset
	jmp @F
.86mode:
	int 21h
	mov dx, es
@@:
	push es
	push dx
	 push ss
	 pop es
	mov di, msg.listaddress.address
internaldatarelocation
	mov ax, dx
	extcallcall hexword
	inc di
	mov ax, bx
	extcallcall hexword
	mov dx, msg.listaddress
internaldatarelocation
	extcallcall putsz
	pop dx
	pop es
	cmp bx, -1
	retn


%if _RESIDENT
	eldcall_dump_callcall ELDCALL_CALLCALL_LIST

endinstalled equ ($ + CODEFIXUP + 15) & ~15


start:
	mov bx, es
	 push ss
	 pop es
	call skipcomma
	dec si
reloc	mov dx, relocateddata
linkdatarelocation msg.install
	call isstring?
	je install
reloc	mov dx, msg.keyword_help
internaldatarelocation
	call isstring?
	je help
	call run
@@:
	call uninstall_oneshot
	xor ax, ax
	retf

help:
	lodsb
	call chkeol
reloc	mov dx, msg.help
internaldatarelocation
	call putsz
	jmp @B
%endif


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

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

	mov ax, word [cs:code + eldiEndData]
internalcoderelocation
	sub ax, word [cs:code + eldiStartData]
internalcoderelocation
	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

%define NAMES db ""
%define DUPLICATES empty, empty

%imacro duplicatecheck 2-*.nolist
 %define DUPLICATE
 %rep (%0 / 2) - 1
  %rotate 2
  %ifidn %1, CHECKNAME
   %xdefine DUPLICATE %2
   %exitrep
  %endif
 %endrep
%endmacro

%imacro item 2-3.nolist
	dw buffer + %3dpb%1
internaldatarelocation
	dw %2
	dw %%name
internaldatarelocation
 %defstr %%string %1
 %xdefine CHECKNAME %1
 duplicatecheck DUPLICATES
 %ifnempty DUPLICATE
%%name equ DUPLICATE
 %else
  %xdefine DUPLICATES DUPLICATES, %1, %%name
  %xdefine NAMES NAMES, %%name:, {fill 28,32,db %%string}, db 0
 %endif
%endmacro

%imacro dumpnames 1-*.nolist
 %rep %0
	%1
  %rotate 1
 %endrep
%endmacro

	align 2, db 0
tablelist:
.base:
		dw table_base
internaldatarelocation
.edpb:		dw 0
table_empty:
		dw 0

table_base:
item Drive,			1
item DeviceUnit,		1
item BytesPerSector,		2
item HighestSectorInCluster,	1
item SectorsPerClusterShift,	1
item ReservedSectors,		2
item AmountFATs,		1
item AmountRootEntries,		2
item DataStart,			2
item MaximumCluster,		2
item SectorsPerFAT,		2
item RootStart,			2
item DeviceHeader,		4
item MediaID,			1
item Accessed,			1
item Next,			4
item FreeClusterNext,		2
item AmountFreeClusters,	2
	dw 0

table_efdpb:
item AmountFreeClusters,	4, ef
item FSFlags,			2, ef
item FSInfoSector,		2, ef
item BackupSector,		2, ef
item DataStart,			4, ef
item MaximumCluster,		4, ef
item SectorsPerFAT,		4, ef
item RootCluster,		4, ef
item FreeClusterNext,		4, ef
	dw 0

table_eddpb:
item AmountFreeClusters,	4, ed
item FSFlags,			2, ed
item FSInfoSector,		2, ed
item BackupSector,		2, ed
item DataStart,			4, ed
item MaximumCluster,		4, ed
item SectorsPerFAT,		4, ed
item RootCluster,		4, ed
item FreeClusterNext,		4, ed
item FSVersion,			2, ed
	dw 0

extendedkeywords:
internaldatarelocation 0
internaldatarelocation 2
	dw msg.freedos, table_efdpb, EFDPB_size
internaldatarelocation 0
internaldatarelocation 2
	dw msg.msdos, table_efdpb, EFDPB_size
internaldatarelocation 0
internaldatarelocation 2
	dw msg.edrdos, table_eddpb, EDDPB_size
	dw 0

listat:
	dd 0

msg:
.return:	db "DOS function 32h call returned al="
.return.code:	asciz "--h.",13,10
.returnaddress:		db "DOS function returned 86 Mode address="
.returnaddress.address:	asciz "----:----",13,10
.listaddress:		db "Using DOS List of Lists at 86 Mode address="
.listaddress.address:	asciz "----:----",13,10
.foundaddress:		db "Found DPB at 86 Mode address="
.foundaddress.address:	asciz "----:----",13,10
.notfound:		asciz "No DPB found!",13,10

.list:		asciz "LIST"
.at:		asciz "AT"
.skip:		asciz "SKIP"
.drive:		asciz "DRIVE"
.address:	asciz "ADDRESS"
.only:		asciz "ONLY"
.extended:	asciz "EXTENDED"
.freedos:	asciz "FREEDOS"
.msdos:		asciz "MSDOS"
.edrdos:	asciz "EDRDOS"
%if _RESIDENT
.dpb:			asciz "DPB"
.uninstall_done:	db "DPB command uninstalled."
%if _ELD_RECLAIM_HINT
			db " (Don't forget to use reclaim.eld)"
%endif
			asciz 13,10
.uninstall_error:	asciz "DPB command unable to uninstall!",13,10
%endif

dumpnames NAMES

uinit_data: equ $

%if _RESIDENT
.installed:		asciz "DPB command installed.",13,10
.keyword_help:		asciz "HELP"
.help:		db "This ELD can be installed residently or used transiently.",13,10
		db 13,10
		db "DPB command may be used with a drive specified like so:",13,10
		db 9,"DPB A:",9,9,9,9,"Access drive A: using DOS call 21.32",13,10
		db 9,"DPB ADDRESS address",9,9,"Access DPB at address without DOS",13,10
		db 9,"DPB LIST DRIVE A:",9,9,"Access drive A: using DOS call 21.52",13,10
		db 9,"DPB LIST SKIP 0",9,9,9,"Access first DPB using DOS call 21.52",13,10
		db 9,"DPB LIST AT address DRIVE A:",9,"Access drive A: using List of Lists",13,10
		db 9,"DPB LIST AT address SKIP 0",9,"Access first DPB using List of Lists",13,10
		db 9,"(LoL address is always a 86 Mode address, default segment is RI31S)",13,10
		db 13,10
		db "EDPB may be displayed using the following keywords:",13,10
		db 9,"DPB EXTENDED=format drive",9,"Display extended DPB",13,10
		db 9,"DPB ONLY EXTENDED=format drive",9,"Do not display base DPB",13,10
		db 9,"(format may be MSDOS, FREEDOS, or EDRDOS)",13,10
		asciz
%endif

	align 16, db 0
init_data_end:
data_size equ $ - datastart
transient_data_size equ data_size

	absolute uinit_data

	alignb 2
address:	resd 1
size:		resw 1
buffer:		resb DPB_size

	alignb 16
uinit_data_end:
resident_data_end:
resident_data_size equ resident_data_end - datastart

%if uinit_data_end >= init_data_end
 total_data_size equ $ - datastart
%else
 total_data_size equ init_data_end - datastart
%endif
%assign _DATA_SIZE total_data_size

	usesection CODE

%if _RESIDENT
install:
	lodsb
	extcall chkeol

	houdini
	mov es, bx		; => ext seg (writable)

	mov ax, endresident - endinstalled
reloc	sub word [es:code + eldiEndCode], ax
internalcoderelocation		; adjust size
reloc	sub word [relocateddata], ax
linkdatarelocation extseg_used	; adjust size

%if (transient_data_size - resident_data_size) > 0
	mov ax, transient_data_size - resident_data_size
reloc	sub word [es:code + eldiEndData], ax
internalcoderelocation		; adjust size
reloc	sub word [relocateddata], ax
linkdatarelocation extdata_used	; adjust size
%endif

reloc	mov bx, word [relocateddata]
linkdatarelocation ext_command_handler
				; -> prior
reloc	mov di, command		; -> our handler
internalcoderelocation
	test bx, bx		; installing as first ?
	jz .only_first		; yes, simple --> (leave as extcall cmd3_not_ext)
	scasw			; skip entrypoint jmp strict short
	mov al, 0E9h		; = jmp near opcode
	stosb			; store
	xchg ax, bx		; ax -> next handler
	sub ax, di
	dec ax
	dec ax			; ax = ax - (di + 2)
	stosw			; store our downlink as rel16 displacement

.only_first:
reloc	setopt [es:code + eldiFlags], eldifResident
internalcoderelocation -3	; mark block as resident
reloc2	mov word [relocateddata], command
linkdatarelocation ext_command_handler, -4
internalcoderelocation		; -> our entrypoint

reloc	testopt [relocateddata], 4
linkdatarelocation options7, -3
	jnz @F
reloc	mov dx, msg.installed
internaldatarelocation
	call putsz
@@:
	xor ax, ax
	retf
%endif


%include "eldlink.asm"

	align 16
code_size equ $ - code
