
; Public Domain

; test install command: install; hint skipself; hint uninstall
; test run command: skipself; .; .

%include "lmacros3.mac"

	numdef OTHER, 0
%if _OTHER
 %define ELDOTHER 1
 %define OTH_STRING "OTH"
 %imacro getotherds 0
reloc	mov ds, word [ss:otherds]
internaldatarelocation
 %endmacro
 %imacro resetds 0
	push ss
	pop ds
 %endmacro
 %imacro getotheres 0
reloc	mov es, word [ss:otherds]
internaldatarelocation
 %endmacro
 %imacro resetes 0
	push ss
	pop es
 %endmacro
%else
 %define OTH_STRING ""
 %idefine getotherds
 %idefine resetds
 %idefine getotheres
 %idefine resetes
%endif

%include "eld.mac"
%include "eldcall.mac"
%include "elddata.mac"
%include "isvariab.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
header_extension_end:
	iend

description:		asciz "Dump TracList hints for all ELDs."


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


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.hint_keyword
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

%if _OTHER
reloc	mov ax, word [otherseg]
internaldatarelocation
	extcallcall ispm
	jnz @F
	xchg dx, ax
	extcallcall setes2dx
reloc	mov ax, word [es:relocateddata]
otherlinkdatarelocation dssel
@@:
reloc	mov word [otherds], ax
internaldatarelocation
%endif

	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


%if _OTHER
get_es_ext_other:
	getotherds
reloc	mov es, word [relocateddata]
otherlinkdatarelocation extdssel
	extcallcall ispm
	jz @F
reloc	mov es, word [relocateddata]
otherlinkdatarelocation extseg
@@:
	resetds
	retn
%else
 get_es_ext_other equ get_es_ext
%endif


run:
	push ss
	pop es
%ifn _OTHER
	xor bx, bx
reloc	mov dx, msg.skipself
internaldatarelocation
	extcallcall isstring?
	jne @F
	dec bx
@@:
reloc	mov byte [skipself], bl
internaldatarelocation
%endif
	lodsb
	extcallcall chkeol

%ifn _OTHER
reloc2	mov word [buffer.next], buffer
internaldatarelocation -4
internaldatarelocation
reloc	mov byte [buffer], 0
internaldatarelocation -3

	mov al, 2Dh
	extcallcall intchk		; ZR if offset = -1 or segment = 0
					; CHG: ax, dx, bx
	jz .no_hint

reloc	mov ah, byte [relocateddata]
linkdatarelocation try_debugger_amis_multiplex_number
	call later_plex_check
	jnc @F

	mov ah, 0FFh		; start with multiplex number 0FFh
.loop_hint:
	call later_plex_check
@@:
	jnc .end_hint		; if found --> (NC)
	sub ah, 1		; search is backward (to find latest installed first), from 0FFh to 00h including
	jnc .loop_hint		; try next if we didn't check all yet -->
.no_hint:
	push ss
	pop es
reloc	mov dx, msg.no_outer
internaldatarelocation
	extcallcall putsz
	mov ax, 0E61h
	extcallcall setrc
	retn

.end_hint:
	push ss
	pop es
	mov al, 40h		; AMIS message display service
reloc	mov word [later_hint_service], ax
internaldatarelocation
%endif

display_eld_memory:
	xor ax, ax
.loop:
reloc	mov di, relocateddata
linkdatarelocation line_out
	getotherds
reloc	cmp word [relocateddata], ax
otherlinkdatarelocation extseg_used
	ja @F
.end:
	resetds
	push ss
	pop es
%ifn _OTHER
reloc	mov ax, word [later_hint_service]
internaldatarelocation
reloc	mov bx, buffer
internaldatarelocation
reloc	mov dx, [relocateddata]
linkdatarelocation pspdbg	; must be 86 Mode segment even in PM
	extcallcall call_int2D
	cmp al, 0FFh
	je .retn
reloc	mov dx, msg.error_amismsg
internaldatarelocation
	extcallcall putsz
reloc	mov dx, msg.error_no
internaldatarelocation
	cmp al, 0
	je .goterror
reloc	mov dx, msg.error_truncated
internaldatarelocation
	cmp al, 0FEh
	je .goterror
reloc	mov dx, msg.error_unknown
internaldatarelocation
.goterror:
	extcallcall putsz
	mov ax, 0E64h
	extcallcall setrc
.retn:
%endif
	retn

@@:
reloc	mov bx, relocateddata
otherlinkdatarelocation extdssel, -2, optional
	extcallcall ispm
	jz @F
reloc	mov bx, relocateddata
otherlinkdatarelocation extseg, -2, optional
@@:
	test bx, bx
	jnz @F
.error:
	resetds
	push ss
	pop es
reloc	mov dx, msg.eld.error
internaldatarelocation
	extcallcall putsz
	mov ax, 0E62h
	extcallcall setrc
	jmp .end

@@:
	mov es, word [bx]
	mov bx, ax
	cmp word [es:bx + eldiStartCode], ax
	jne .error
	mov dx, ax
	add dx, ELD_INSTANCE_size
	jc .error
	cmp word [es:bx + eldiEndCode], dx
	jb .error
reloc	mov dx, word [relocateddata]
otherlinkdatarelocation extseg_used
	cmp word [es:bx + eldiEndCode], dx
	ja .error
	je @F
	sub dx, ELD_INSTANCE_size
	jc .error
	cmp word [es:bx + eldiEndCode], dx
	ja .error
@@:
	mov dx, word [es:bx + eldiEndCode]
	mov bp, dx
	sub dx, ax
	jc .error
%ifn _OTHER
reloc	rol byte [ss:skipself], 1
internaldatarelocation
	jnc @F
reloc	cmp bx, strict word code
internalcoderelocation
	je .next
@@:
%endif
	lea si, [bx + eldiListing]
	cmp byte [es:si], 0
	je .next
	push es
	 push ss
	 pop es
	push si
	resetds
reloc	mov si, msg.hint
internaldatarelocation
	extcallcall copy_single_counted_string
	pop si
	pop ds

	mov cx, eldiListing_size
	db __TEST_IMM8			; skip stosb
@@:
	stosb				; store
	lodsb				; load
	test al, al			; is it NUL ?
	loopne @B			; store if non-NUL -->

	push ss
	pop ds
	mov ax, "::"
	stosw
	xchg ax, bx
	extcallcall hexword
	mov al, 'h'
	stosb

%if _OTHER
	extcallcall putsline_crlf
%else
	mov ax, 13 | (10 << 8)
	stosw
	mov cx, di
reloc	mov si, relocateddata
linkdatarelocation line_out
	sub cx, si
reloc	mov di, word [buffer.next]
internaldatarelocation
	mov ax, di
	add ax, cx
reloc	cmp ax, strict word buffer.end - 1
internaldatarelocation
	ja .full
	rep movsb
reloc	mov word [buffer.next], di
internaldatarelocation
	mov al, 0
	stosb
%endif

.next:
	mov ax, bp
	jmp .loop


%ifn _OTHER
.full:
reloc	mov dx, msg.buffer_full
internaldatarelocation
	extcallcall putsz
	mov ax, 0E63h
	extcallcall setrc
	jmp .end


		; INP:	ah = multiplex number to check
		; OUT:	CY if multiplex number unused or no signature match,
		;	 bp, ah, ds unmodified
		;	NC if match found,
		;	 ah = multiplex number (unmodified)
		; CHG:	si, di, es, cx, dx
later_plex_check:
	push ss
	pop ds
reloc	testopt [relocateddata], 8
linkdatarelocation internalflags4, -3
	jz @F
reloc	mov si, relocateddata
linkdatarelocation amis_multiplex_number, -2, optional
	test si, si
	jz @F
	cmp ah, byte [si]
	je .notfound		; do not use our own multiplexer -->
@@:
	mov al, 00h		; AMIS installation check
	extcallcall call_int2D	; AMIS (or "DOS reserved" = iret if no AMIS present)
	cmp al, 0FFh
	jne .notfound
	push ss
	pop ds
reloc	mov si, msg.debuggeramissig
internaldatarelocation		; ds:si -> our AMIS name strings
	extcallcall setes2dx	; es:di -> name strings of AMIS multiplexer that just answered
	mov cx, 8		; Ignore description, only compare vendor and program name
	repe cmpsw
	je .checkret		; ZR, NC = match -->
.notfound:
	stc			; CY no match
.checkret:
	retn
%endif


	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


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:

%if _OTHER
	align 2, db 0
otherds:	dw 0
otherseg:	dw 0
%endif

msg:
%ifn _OTHER
	align 2, db 0
.debuggeramissig:
		fill 8, 32, db "ecm"
		fill 8, 32, db "lDebug"
%endif
.hint_keyword:		asciz "HINT",OTH_STRING
.uninstall_done:	db "HINT",OTH_STRING," uninstalled."
%if _ELD_RECLAIM_HINT
			db " (Don't forget to use reclaim.eld)"
%endif
			asciz 13,10
.uninstall_error:	asciz "HINT",OTH_STRING," unable to uninstall!",13,10
.eld.error:		asciz "Error during ELD enumeration.",13,10
.hint:			counted "TracList-add-offset="
%ifn _OTHER
.skipself:		asciz "SKIPSELF"
.no_outer:		asciz "Error: No outer debugger found.",13,10
.error_amismsg:		asciz "Error: Outer debugger AMIS message service "
.error_no:		asciz "not installed.",13,10
.error_truncated:	asciz "truncated.",13,10
.error_unknown:		asciz "unknown error.",13,10
.buffer_full:		asciz "Error: Buffer full.",13,10
%endif

uinit_data: equ $
.installed:		asciz "HINT",OTH_STRING," installed.",13,10
.keyword_help:		asciz "HELP"
.help:		db "This ELD can be installed residently or used transiently.",13,10
		db 13,10
		db "Running HINT",OTH_STRING," displays TracList hints for all currently",13,10
		db "installed ELD instances"
%if _OTHER
		db " of the other link debugger"
%else
		db " to the outer debugger"
%endif
		db ".",13,10
		asciz

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

	absolute uinit_data
	alignb 2
%ifn _OTHER
later_hint_service:	resw 1
buffer:			resb 384
.end:
.next:			resw 1
skipself:		resb 1
%endif

	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

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


%include "eldlink.asm"

	align 16
code_size equ $ - code
