%if 0

Extensions for lDebug linker

Copyright (C) 2008-2025 E. C. Masloch

Usage of the works is permitted provided that this
instrument is retained with the works, so that any entity
that uses the works is notified of this instrument.

DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.

%endif

%include "lmacros3.mac"
%include "eld.mac"

	cpu 8086

%if ELDSTRICT
 %if ELDSTRICTCOUNTER
  %error Missing relocation
 %endif
%endif
	eldstrict off		; linker does some relocations

%ifn ELDCALLCALL_DUMPED
	eldcall_dump_callcall ELDCALL_CALLCALL_LIST
%endif

	numdef ELD_IDENTICALISE, 0
	numdef ELD_DEBUG, 0
	numdef ELD_LINKER_PASSES, 1
	strdef LISTING, _ELD_LISTING
	numdef ELDCALL_OPTIONAL, ELDCALL_OPTIONAL_STATUS
	numdef ELDDATA_OPTIONAL, ELDDATA_OPTIONAL_STATUS
	numdef ELDDATA_NEXT, ELDDATA_NEXT_STATUS
	numdef ELDCALL_NEXT, ELDCALL_NEXT_STATUS
%if _ELD_IDENTICALISE
 ;%assign _ELDCALL_OPTIONAL 1
%endif
%ifn _ELDCALL_OPTIONAL
%assign ELDCALL_OPTIONAL_STATUS 2		; not allowed
%endif
;%ifn _ELDDATA_OPTIONAL
;%assign ELDDATA_OPTIONAL_STATUS 2		; not allowed
;%endif
	gendef MAP, ""

%ifnidni _MAP, ""
 [map all _MAP]
%endif


endresident: equ ($ + _CURRENT_SECTION %+ FIXUP + 15) & ~15

%if _ELD_LINKER_PASSES
%assign ELDLINKER 1

	struc LINKTABLE
ltCodeToCode:	resw 2
ltCodeToData:	resw 2
ltDataToCode:	resw 2
ltDataToData:	resw 2
ltLinkData:	resw 2
ltLinkCode:	resw 2
	endstruc
%endif


		; INP:	es:dx -> loaded initial ELD image
		;	 ELD instance structure filled
		;	es => ELD code area
		;	ds:di -> link info
		;	ss:bx -> loaded initial data
		;	ss:si -> command line tail
		;	cs:ip -> entrypoint
		;	ss:sp -> far return address for current mode
		; STT:	UP, EI
linker:
		; The first 15 Bytes of the linker should be
		;  the same regardless of the linker options
		;  used (debug, passes, vstart). This allows
		;  the eldcomp ELD to match even if up to 15
		;  Bytes of the linker are trailing in the
		;  resident part of the ELD.
	lframe
 %if ELDOTHER
	lvar dword, linkinfo_other
	lvar word, use_hash_other
 %endif
	lenter
	lvar dword, instance
	 push es
	 push dx		; -> start of where we're loaded in ELD cs
	lvar word, targetsection_low_optional_high
	 push ax		; placeholder
	lequ ?targetsection_low_optional_high, targetsection
	lequ ?targetsection_low_optional_high + 1, optional
	lvar dword, linkinfo
	 push ds
	 push di		; -> link info (in debugger ss or message seg)
	lvar dword, nextreloc
	 push es		; => ELD cs
	 push dx		; placeholder
	lvar dword, entry
	 push es		; => ELD cs
	 push ax		; placeholder
	xor ax, ax
	cmp word [di + eldlSignature], 0E1D1h
%if ($ - linker) < 16
 %error Expected invariant first paragraph of linker
%endif
	je @F
.error_j1:
	jmp .error		; signature mismatch -->
@@:
	cmp word [di + eldlUseLinkHash], 1
	ja .error_j1		; unknown use link hash number -->
	jb @F
%if _ELDDATA_NEXT || _ELDCALL_NEXT
	dec al			; = -1 if hash used
%else
	dec ax
%endif
@@:
	lvar word, use_hash_low_skip_last_high
	 push ax		; = -1 if hash used, else 0
	lequ ?use_hash_low_skip_last_high, use_hash
	lequ ?use_hash_low_skip_last_high + 1, skip_last
%if _ELD_IDENTICALISE && _ELD_LINKER_PASSES
	mov ax, linktable - code
	lvar word, lt
	 push ax		; -> link tables (a single reference)
%endif
%if _ELD_LINKER_PASSES || ELDOTHER || _ELDDATA_OPTIONAL || _ELDCALL_OPTIONAL
	xor ax, ax
 %if _ELD_LINKER_PASSES
	lvar word, pass
	 push ax		; = 0 for linker itself, = 2 for application
	lvar word, amounterrors
	 push ax		; how many distinct errors encountered in pass 2
 %endif
 %if _ELD_LINKER_PASSES || _ELDDATA_OPTIONAL || _ELDCALL_OPTIONAL
	lvar word, name
	 push ax		; -> name currently being sought (in our table)
 %endif
 %if ELDOTHER
	lvar word, other
	 push ax		; byte: 0 = own data links, -1 = other data links
 %endif
 %if _ELDDATA_OPTIONAL || _ELDCALL_OPTIONAL
	lvar word, optionalmissing
	 push ax
 %endif
%endif
	push si			; -> command line tail
	push bx			; -> data block in ELD data area (segment ss)

			; simple: add instance base (dx) to code
	push es
	pop ds			; ds = es => ELD cs
%if _ELD_LINKER_PASSES
.pass:
	mov bx, word [bp + ?pass]	; 0 or 2
	add bx, dx		; -> start of the ELD instance in cs, +0 or +2
 %if _ELD_IDENTICALISE
	add bx, word [bp + ?lt]	; add in single reference to linktable
	mov si, word [ltCodeToCode + bx]
 %else
	mov si, word [linktable - code + ltCodeToCode + bx]
 %endif
%else
	mov si, internalcodetocoderelocations - code
%endif		 		; si -> table for code to code (linker or app)
	add si, dx		; relocate the table pointer itself
@@:
	lodsw
	test ax, ax		; was last ?
	jz @F			; yes -->
	xchg bx, ax		; bx = entry
	add bx, dx		; relocate the entry itself
%if _ELD_CODE_VSTART
	sub word [bx], code	; undo adjustment
%endif
	add word [bx], dx	; relocate word referenced by entry
	jmp @B

@@:
			; next, add heap data base (on stack) to code
%if _ELD_LINKER_PASSES
	mov bx, word [bp + ?pass]	; 0 or 2
	add bx, dx		; -> start of the ELD instance in cs, +0 or +2
 %if _ELD_IDENTICALISE
	add bx, word [bp + ?lt]	; add in single reference to linktable
	mov si, word [ltCodeToData + bx]
 %else
	mov si, word [linktable - code + ltCodeToData + bx]
 %endif
%else
	mov si, internalcodetodatarelocations - code
%endif				; si -> table for code to data (linker or app)
	add si, dx		; relocate the table pointer itself
@@:
	lodsw
	test ax, ax		; was last ?
	jz @F			; yes -->
	xchg bx, ax		; bx = entry
	add bx, dx		; relocate the entry itself
		; ! access the offset of the data area block, topmost on stack
	pop ax			; ax -> heap data
	push ax
%if _ELD_DATA_VSTART
	sub ax, datastart	; undo adjustment
%endif
	add word [bx], ax	; relocate word referenced by entry
	jmp @B

@@:

			; add instance base (dx) to data
%if _ELD_LINKER_PASSES
	mov bx, word [bp + ?pass]
	add bx, dx
 %if _ELD_IDENTICALISE
	add bx, word [bp + ?lt]
	mov si, word [ltDataToCode + bx]
 %else
	mov si, word [linktable - code + ltDataToCode + bx]
 %endif
%else
	mov si, internaldatatocoderelocations - code
%endif
	add si, dx
@@:
	lodsw
	cmp ax, -1		; was last ?
	je @F			; yes -->
	xchg bx, ax		; bx = entry
		; ! access the offset of the data area block, topmost on stack
	pop ax
	push ax
	add bx, ax		; relocate the entry itself
%if _ELD_CODE_VSTART
	sub word [ss:bx], code	; undo adjustment
%endif
	add word [ss:bx], dx	; relocate word referenced by entry
	jmp @B

@@:
			; next, add heap data base (on stack) to data
%if _ELD_LINKER_PASSES
	mov bx, word [bp + ?pass]
	add bx, dx
 %if _ELD_IDENTICALISE
	add bx, word [bp + ?lt]
	mov si, word [ltDataToData + bx]
 %else
	mov si, word [linktable - code + ltDataToData + bx]
 %endif
%else
	mov si, internaldatatodatarelocations - code
%endif
	add si, dx
@@:
	lodsw
	cmp ax, -1		; was last ?
	je @F			; yes -->
	xchg bx, ax		; bx = entry
		; ! access the offset of the data area block, topmost on stack
	pop ax			; ax -> heap data
	push ax
	add bx, ax		; relocate the entry itself
%if _ELD_DATA_VSTART
	sub ax, datastart	; undo adjustment
%endif
	add word [ss:bx], ax	; relocate word referenced by entry
	jmp @B

@@:
	mov dx, word [bp + ?instance]
				; reset (why??) -> ELD instance in ELD cs
%if _ELD_LINKER_PASSES
	mov bx, word [bp + ?pass]
	add bx, dx
 %if _ELD_IDENTICALISE
	add bx, word [bp + ?lt]
	add dx, word [ltLinkData + bx]
 %else
	add dx, word [linktable - code + ltLinkData + bx]
 %endif
%else
	add dx, linkerdatarelocations - code
%endif
.otherloop:
	mov word [bp + ?nextreloc], dx
				; -> data link relocation table

.datarelocloop:		; must be done before further relocs
	lds si, [bp + ?nextreloc]
	lodsw			; ax = section indicator
	test ax, ax		; end of table ?
	jz .donedatareloc
	dec ax			; al = 1 if data, 0 if code (NB ah may != 0)
		; ! access the offset of the data area block, topmost on stack
	pop bx
	push bx			; bx -> ELD data block
	mov word [bp + ?targetsection_low_optional_high], ax
	test al, al		; data ?
	lodsw
	jnz @F			; yes -->
	add ax, word [bp + ?instance]
				; relocate entry into code
	db __TEST_IMM16		; (skip add)
@@:
	add ax, bx		; relocate entry into data
	mov word [bp + ?entry], ax
				; save away -> where to patch
	lodsw			; get unrelocated -> name
	mov word [bp + ?nextreloc], si
				; update -> behind relocation table entry
%if _ELDDATA_NEXT
	rol byte [bp + ?skip_last], 1	; skipping ?
	jnc @F				; no -->
	test byte [bp + ?optional], 8	; is it a last relocation ?
	jz .datarelocloop		; no, leave skip_last set -->
	not byte [bp + ?skip_last]	; disable skip_last
	jmp .datarelocloop
@@:
%endif
	call .build_match	; get ax = scan value, si -> counted string,
				;  dl = original value at byte [si]
				; NC if symbol already not found before
				; CY if to search for the symbol
%if _ELD_LINKER_PASSES
	jnc .datarelocloop	; if duplicate of a not found symbol -->
%endif
	les bx, [bp + ?linkinfo]
%if ELDOTHER
	rol byte [bp + ?other], 1
	jnc @F
	les bx, [bp + ?linkinfo_other]
@@:
%endif				; es:bx -> link info table of the ELD host
	mov cx, word [es:bx + eldlDataAmount]	; amount data link info
	mov di, word [es:bx + eldlDataPrefixes]	; -> prefixes or hashes
.datarelocfind:
	cmp cx, 1			; NZ if cx == 0
	repne scasw			; scan initial
%if _ELDDATA_OPTIONAL
	jne .datanotfound_may_be_optional
%else
	jne .datanotfound
%endif
	push di
	push cx
	push si
	sub di, word [es:bx + eldlDataPrefixes]	; = double index, 1-based
	dec di
	dec di				; 0-based
	add di, word [es:bx + eldlDataEntries]	; -> entry
	mov di, [es:di]			; -> counted suffix/string
	mov ch, 0
	mov cl, byte [si]		; cx = length
	inc cx
	; cmp al, al			; ZR if length is zero
	repe cmpsb			; match ?
	pop si
	pop cx
	pop di
	jne .datarelocfind		; if not found this time,
					;  scan for next initial -->
	mov byte [si], dl		; restore the original byte at counter
	sub di, word [es:bx + eldlDataPrefixes]	; = double index, 1-based
	dec di
	dec di				; 0-based
	add di, word [es:bx + eldlDataAddresses]	; -> address (offset)
	mov ax, word [es:di]		; get link data from the table
	call .address_data_reloc
%if _ELD_RELOC_VSTART
	sub ax, relocateddata		; *not* a relocation
%endif
	add word [si], ax		; relocate
%if _ELDDATA_NEXT
	test byte [bp + ?optional], 4	; is it a next relocation ?
	jz .datarelocloop
	mov byte [bp + ?skip_last], -1	; skip subsequent next/last relocs
%endif
	jmp .datarelocloop

.address_data_reloc:
	lds si, [bp + ?entry]		; -> entry if in ELD code instance
	cmp byte [bp + ?targetsection], 0
					; relocation in code ?
	je @F				; yes -->
	push ss
	pop ds				; => our ELD data block
@@:
	retn

%if _ELDDATA_OPTIONAL
.datanotfound_may_be_optional:
	mov byte [si], dl		; restore the original byte at counter
%if _ELDDATA_NEXT
	test byte [bp + ?optional], 3	; is it an optional relocation ?
%else
	cmp byte [bp + ?optional], 0	; is it an optional relocation ?
%endif
%if _ELD_DEBUG
	jnz @F
 %ifn _ELD_LINKER_PASSES
	mov si, 0			; (invalid link)
linkdatarelocation internalflags6
	test si, si
	jz .datanotfound
	mov si, 0			; (invalid link)
linkdatarelocation options7
	test si, si
	jz .datanotfound
 %else
	cmp byte [bp + ?pass], 0
	je .datanotfound
 %endif
	houdini				; (the above conditions are for this)
	jmp .datanotfound
@@:
%else
	jz .datanotfound		; no, handle as an error -->
%endif

%if ELDDATA_PMREQUIRED_STATUS
	mov si, relocateddata
linkdatarelocation build_option_PM
	test si, si			; is it a PM build ?
	jz @F				; no --> (treat PM required as optional)
	test byte [bp + ?optional], 2	; optional status is PM required ?
	jnz .datanotfound		; yes, handle as an error -->
@@:
%endif

%if _ELD_LINKER_PASSES
%if _ELDDATA_OPTIONAL || _ELDCALL_OPTIONAL
	rol byte [bp + ?optionalmissing], 1
	jnc .optionaldatamissingmsg
	mov si, [bp + ?name]
	or byte [si], 80h
	not byte [bp + ?optionalmissing]
	jmp @F
.optionaldatamissingmsg:
%endif
	testopt [ss:relocateddata], 2	; debug optional link ?
linkdatarelocation options7, -3
	jz @F				; no -->
%if _ELDDATA_OPTIONAL || _ELDCALL_OPTIONAL
	mov byte [bp + ?optionalmissing + 1], -1
%endif
	mov si, lmsg.warning_data_optional
internalcoderelocation
	call .notfound			; display a warning
%if _ELDDATA_OPTIONAL || _ELDCALL_OPTIONAL
	not byte [bp + ?optionalmissing + 1]
%endif
@@:
%endif
%if _ELDDATA_NEXT
	test byte [bp + ?optional], 4	; is it a next relocation ?
	jnz .datarelocloop
%endif
	call .address_data_reloc
	and word [si], 0		; indicate optional data link failed
	jmp .datarelocloop
%endif


%if _ELDCALL_OPTIONAL
.codenotfound_may_be_optional:
	mov byte [si], dl		; restore the original byte at counter
%if _ELDCALL_NEXT
	test byte [bp + ?optional], 3	; is it an optional relocation ?
%else
	cmp byte [bp + ?optional], 0	; is it an optional relocation ?
%endif
%if _ELD_DEBUG
	jnz @F
 %ifn _ELD_LINKER_PASSES
	mov si, 0			; (invalid link)
linkdatarelocation internalflags6
	test si, si
	jz .codenotfound
	mov si, 0			; (invalid link)
linkdatarelocation options7
	test si, si
	jz .codenotfound
 %else
	cmp byte [bp + ?pass], 0
	je .codenotfound
 %endif
	houdini				; (the above conditions are for this)
	jmp .codenotfound
@@:
%else
	jz .codenotfound		; no, handle as an error -->
%endif

%if ELDCALL_PMREQUIRED_STATUS
 %if _ELD_LINKER_PASSES
  %assign ELDLINKER 0
 %endif
	mov si, relocateddata
linkdatarelocation build_option_PM
 %if _ELD_LINKER_PASSES
  %assign ELDLINKER 1
 %endif
	test si, si			; is it a PM build ?
	jz @F				; no --> (treat PM required as optional)
	test byte [bp + ?optional], 2	; optional status is PM required ?
	jnz .codenotfound		; yes, handle as an error -->
@@:
%endif

%if _ELD_LINKER_PASSES
%if _ELDDATA_OPTIONAL || _ELDCALL_OPTIONAL
	rol byte [bp + ?optionalmissing], 1
	jnc .optionalcodemissingmsg
	mov si, [bp + ?name]
	or byte [si], 80h
	not byte [bp + ?optionalmissing]
	jmp @F
.optionalcodemissingmsg:
%endif
	testopt [ss:relocateddata], 2	; debug optional link ?
linkdatarelocation options7, -3
	jz @F				; no -->
%if _ELDDATA_OPTIONAL || _ELDCALL_OPTIONAL
	mov byte [bp + ?optionalmissing + 1], -1
%endif
	mov si, lmsg.warning_code_optional
internalcoderelocation
	call .notfound			; display a warning
%if _ELDDATA_OPTIONAL || _ELDCALL_OPTIONAL
	not byte [bp + ?optionalmissing + 1]
%endif
@@:
%endif
%if _ELDCALL_NEXT
	test byte [bp + ?optional], 4	; is it a next relocation ?
	jnz .coderelocloop
%endif
	lds si, [bp + ?entry]		; -> code site (must be in ELD cs)
	mov word [si], 9090h
	mov byte [si + 2], 90h		; indicate optional code link failed
					;  (NOP out the 3-byte call near imm)
	jmp .coderelocloop
%endif


%if _ELD_LINKER_PASSES
.datanotfound:
	mov si, lmsg.error_data
internalcoderelocation
	call .notfound			; display error
	jnc .error_j3			; NC if in pass 0, error out now -->
	inc word [bp + ?amounterrors]	; count the amount of errors
	jmp .datarelocloop		; go to next relocation entry -->

.error_j3:
	jmp .error

.codenotfound:
	mov si, lmsg.error_code
internalcoderelocation
	call .notfound			; display error
	jnc .error_j3			; NC if in pass 0 -->
	inc word [bp + ?amounterrors]	; count the amount of errors
	jmp .coderelocloop		; go to next -->

.notfound:
	cmp byte [bp + ?pass], 0	; pass 0 ?
	je .notfound.retn		; yes, do not try displaying --> (NC)
	 push ss
	 pop es
	mov di, relocateddata
linkdatarelocation line_out		; es:di -> line_out buffer
	call .copy			; copy prefix message
	mov si, word [bp + ?name]
	lodsw				; = hash
	extcall hexword			; display the hash in hex
	push si
	mov si, lmsg.error.2
internalcoderelocation
	call .copy			; display message fragment
	pop si
%if _ELD_LINKER_PASSES
	push si
	call .copy			; copy over the full name
	pop si
 %if _ELDDATA_OPTIONAL || _ELDCALL_OPTIONAL
	or byte [si], 80h		; mark as failed optional link
	rol byte [bp + ?optionalmissing + 1], 1
					; was it optional ?
	jc @F				; yes -->
 %endif
	mov byte [si], 0		; mark as failed (counter byte = 0)
@@:
%else
	call .copy
%endif
	mov al, '"'
	stosb				; end message with a closing quote mark
	 push ss
	 pop ds
	extcall putsline_crlf		; display diagnostic line
	mov ax, 0E35h
	extcall setrc
	stc				; CY, diagnostic displayed
.notfound.retn:
	retn

.copy:		; (cannot use extcallcall here)
	extcall copy_single_counted_string
	retn
%else
.datanotfound: equ .error
.codenotfound: equ .error
%endif

.build_match:
%if _ELDDATA_OPTIONAL || _ELDCALL_OPTIONAL
	mov byte [bp + ?optionalmissing], 0
		; This is needed for eldtestj.asm with debugoptlink.
		;  The optional data link sets flag 80h, then the
		;  required data link to the same symbol sets this
		;  flag but doesn't reset it. So the first optional
		;  code link still had this set. Reset here to fix.
%endif
%if _ELD_CODE_VSTART
	sub ax, code			; revert adjustment (= displacement)
%endif
	add ax, word [bp + ?instance]	; make displacement into -> name string
	xchg si, ax			; si -> hash word, counted name string
%if _ELD_LINKER_PASSES || _ELDDATA_OPTIONAL || _ELDCALL_OPTIONAL
	mov word [bp + ?name], si	; save away for diagnostics
%endif
	lodsw			; = hash, si -> counted string
	mov dl, byte [si]	; dl = original start byte of match
%if _ELD_LINKER_PASSES
	test dl, dl		; marked as already failed ?
				;  (80h = optional, 0 = required)
	jnz @F			; no -->
	cmp word [bp + ?amounterrors], 0
				; validate error status
	je .error_j		; no error known ?! cancel quickly -->
	retn			; (NC)

@@:
%else
	test dl, dl
%endif
%if _ELDDATA_OPTIONAL || _ELDCALL_OPTIONAL
	jns @F
	and dl, 7Fh
	mov byte [si], dl
	mov byte [bp + ?optionalmissing], -1
@@:
%endif
%if ELDOTHER
	rol byte [bp + ?other], 1
	jnc @F
	rol byte [bp + ?use_hash_other], 1
	jmp @FF

@@:
%endif
	rol byte [bp + ?use_hash], 1
@@:
		; If to use the hash, then ax = hash and si ->
		;  the entire symbol name as a counted string.
	jc @F			; if to use hash, return now -->
	lodsb			; al = length
	xchg cx, ax		; cl = length
	lodsw			; ax = prefix
	dec cx
	dec cx			; cl = adjusted length
	dec si			; -> second byte of prefix
	mov dl, byte [si]	; dl = original start byte of match
	mov byte [si], cl	; si -> counted string suffix
		; (this name entry has been edited to provide the
		;  name suffix as a counted string, as required by
		;  the old-style non-hash link info tables.
		; it will be reset later by writing dl to si -> the
		;  first byte of the match string.)
@@:
%if _ELD_LINKER_PASSES
	stc			; CY
%endif
	retn


.donedatareloc:
%if ELDOTHER
	rol byte [bp + ?other], 1
	jc .otherdone		; other link only has data links,
				;  so at this point we're done -->
%endif

	mov dx, word [bp + ?instance]
%if _ELD_LINKER_PASSES
	mov bx, word [bp + ?pass]
	add bx, dx
 %if _ELD_IDENTICALISE
	add bx, word [bp + ?lt]
	add dx, word [ltLinkCode + bx]
 %else
	add dx, word [linktable - code + ltLinkCode + bx]
 %endif
%else
	add dx, linkercoderelocations - code
%endif
	mov word [bp + ?nextreloc], dx

.coderelocloop:
	lds si, [bp + ?nextreloc]
	lodsw
%if _ELDCALL_OPTIONAL || _ELDCALL_NEXT
	dec ax				; optional word is 1-based, get 0-based
	js .donecodereloc
	mov word [bp + ?targetsection_low_optional_high], ax
	lodsw
%endif
	add ax, word [bp + ?instance]
	mov word [bp + ?entry], ax
	lodsw
	mov word [bp + ?nextreloc], si
%ifn _ELDCALL_OPTIONAL || _ELDCALL_NEXT
	test ax, ax			; if no optional word, check -> match
	jz .donecodereloc
%endif
%if _ELDCALL_NEXT
	rol byte [bp + ?skip_last], 1	; skipping ?
	jnc @F				; no -->
	test byte [bp + ?optional], 8	; is it a last relocation ?
	jz .coderelocloop		; no, leave skip_last set -->
	not byte [bp + ?skip_last]	; disable skip_last
	jmp .coderelocloop
@@:
%endif
	call .build_match
%if _ELD_LINKER_PASSES
	jnc .coderelocloop
%endif
	les bx, [bp + ?linkinfo]
	mov cx, word [es:bx + eldlCodeAmount]	; amount code link info
	mov di, word [es:bx + eldlCodePrefixes]	; -> prefixes or hashes
.coderelocfind:
	cmp cx, 1			; NZ if cx == 0
	repne scasw			; scan initial
%if _ELDCALL_OPTIONAL
	jne .codenotfound_may_be_optional
%else
 %if _ELD_DEBUG
	je @F
	houdini
	jmp .codenotfound
@@:
 %else
	jne .codenotfound
 %endif
%endif
	push di
	push cx
	push si
	sub di, word [es:bx + eldlCodePrefixes]	; = double index, 1-based
	dec di
	dec di				; 0-based
	add di, word [es:bx + eldlCodeEntries]	; -> entry
	mov di, [es:di]			; -> counted suffix/string
	mov ch, 0
	mov cl, byte [si]		; cx = length
	inc cx
	; cmp al, al			; ZR if length is zero
	repe cmpsb			; match ?
	pop si
	pop cx
	pop di
	jne .coderelocfind
	mov byte [si], dl
	sub di, word [es:bx + eldlCodePrefixes]	; = double index, 1-based
	dec di
	dec di				; 0-based
	add di, di			; quadruple index
	add di, word [es:bx + eldlCodeAddresses]	; -> address (offset, segment)
	push word [es:di]
	lds si, [bp + ?entry]
	pop word [si + 3 + 2]		; call near imm (3), jmp short (2)
					;  patch the destination address
	cmp byte [si], 0E8h		; is it as expected ?
	jne .error_j
	mov di, word [es:di + 2]
	mov bx, relocateddata
linkdatarelocation linkcall_table	; ss:bx -> link call table
	cmp word [ss:bx], 1		; is it initialised to format 1 ?
	jne .error_j			; no -->
	mov cx, word [ss:bx + 2]	; get amount of segments
	jcxz .error_j			; none -->
@@:
	add bx, 4			; -> next segment
	cmp word [ss:bx], di		; value matches ?
	je @F				; yes, found -->
	loop @B				; try remaining segments
.error_j:
	jmp .error

@@:
	mov bx, word [ss:bx + 2]	; -> call destination offset
		; si + 3 + delta = bx
		; delta = bx - si - 3
	sub bx, si
	sub bx, 3
	mov word [si + 1], bx		; patch the call instruction
%if _ELDCALL_NEXT
	test byte [bp + ?optional], 4	; is it a next relocation ?
	jz .coderelocloop
	mov byte [bp + ?skip_last], -1	; skip subsequent next/last relocs
%endif
	jmp .coderelocloop

.donecodereloc:
%if _ELD_LINKER_PASSES
	mov ax, word [bp + ?amounterrors]
	test ax, ax
	jnz .error_j
	mov bx, word [bp + ?pass]
	test bx, bx
	jnz @F
	inc bx
	inc bx
	mov word [bp + ?pass], bx
	mov dx, word [bp + ?instance]
	jmp .pass

@@:
%endif

%ifnidni _LISTING, ""
%assign ELDLINKER 0
.hint:
	push ss
	pop ds
	mov al, 2Dh
	extcall intchk			; ZR if offset = -1 or segment = 0
					; CHG: ax, dx, bx
	jz .no_hint

%if ELDOTHER
	mov si, debuggeramissig	; ds:si -> our AMIS name strings
internalcoderelocation
%endif

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

	mov ah, 0FFh		; start with multiplex number 0FFh
.loop_hint:
	call 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 -->
	jmp .no_hint

.end_hint:
	push ax
	les ax, [bp + ?instance]
	mov di, listmsg.hint.offset
internalcoderelocation
	extcall hexword
	mov bx, listmsg.hint
internalcoderelocation
	mov dx, [ss:relocateddata]
linkdatarelocation extseg	; must be 86 Mode segment even in PM
	pop ax
	mov al, 40h		; AMIS message display service
	extcall call_int2D
.no_hint:
%endif

%if ELDOTHER
%assign ELDLINKER 0
	not byte [bp + ?other]

	push ss
	pop ds
	mov al, 2Dh
	extcall intchk			; ZR if offset = -1 or segment = 0
					; CHG: ax, dx, bx
	jz .no_other

	mov si, debuggeramissig	; ds:si -> our AMIS name strings
internalcoderelocation

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

	mov ah, 0FFh		; start with multiplex number 0FFh
.loop_other_debug:
	call plex_check
@@:
	jnc .end_other		; if found --> (NC)
	sub ah, 1		; search is backward (to find latest installed first), from 0FFh to 00h including
	jnc .loop_other_debug	; try next if we didn't check all yet -->

	mov si, loaderamissig	; ds:si -> our AMIS name strings
internalcoderelocation

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

	mov ah, 0FFh		; start with multiplex number 0FFh
.loop_other_loader:
	call plex_check
@@:
	jnc .end_other		; if found --> (NC)
	sub ah, 1		; search is backward (to find latest installed first), from 0FFh to 00h including
	jnc .loop_other_loader	; try next if we didn't check all yet -->

	jmp .no_other

.end_other:
	mov al, 42h		; AMIS get other link info service
	extcall call_int2D
	cmp al, 0FFh
	jne .no_other

	mov ax, si		; selector => PSP
	extcall ispm
	jz @F
	mov ax, dx		; segment => PSP
@@:
	 push ss
	 pop ds
	mov word [otherds], ax
internaldatarelocation		; current mode sel/seg
	mov word [otherseg], dx
internaldatarelocation		; remember segment

	mov ax, cx		; selector => link info
	extcall ispm
	jz @F
	mov ax, bx		; segment => link info
@@:
	mov word [bp + ?linkinfo_other], di
	mov word [bp + ?linkinfo_other + 2], ax

	mov ds, ax		; => link info
	xor ax, ax
	cmp word [di + eldlSignature], 0E1D1h
	jne .error_other
	cmp word [di + eldlUseLinkHash], 1
	ja .error_other
	jb @F
	dec ax
@@:
	mov word [bp + ?use_hash_other], ax

	mov dx, word [bp + ?instance]
	add dx, linkerotherdatarelocations - code
	jmp .otherloop

.error_other:
	push cs
	pop ds
	mov ax, 0E33h
	extcall setrc
	mov dx, lmsg.error_other
internalcoderelocation
	jmp @F

.no_other:
	push cs
	pop ds
	mov ax, 0E34h
	extcall setrc
	mov dx, lmsg.no_other
internalcoderelocation
@@:
	extcall putsz
.error_j2:
	jmp .error

.otherdone:
 %if _ELD_LINKER_PASSES
	mov ax, word [bp + ?amounterrors]
	test ax, ax
	jnz .error_j2
 %endif
%endif

	pop bx				; -> our heap data
	pop si				; -> our command line tail
	 push ss
	 pop ds
	les di, [bp + ?instance]
	mov ax, word [es:di + eldiEndCode]
houdini
	sub ax, strict word endresident + code
internalcoderelocation
	mov word [es:di + eldiEndCode], endresident + code
internalcoderelocation
	sub word [relocateddata], ax
linkdatarelocation extseg_used
%if _DATA_SIZE == 0
	xor ax, ax
	mov word [es:di + eldiStartData], ax
	mov word [es:di + eldiEndData], ax
%endif
	lleave code
		; INP:	es => ELD code segment
		;	ds:si -> command line tail
		;	ds:bx -> ELD data block
		;	ss:sp -> far return address for current mode
		; STT:	ds = ss, UP, EI
	jmp start

.error:
	lleave
	mov ax, -1
	retf


%ifnidni _LISTING, ""
 %assign ELDHINT 1
%else
 %assign ELDHINT 0
%endif

%if ELDOTHER || ELDHINT
		; INP:	ah = multiplex number to check
		;	(if ELDOTHER:)
		;	 cs:si -> 16-byte AMIS signature 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:	di, es, cx, dx
		;	(if ELDOTHER:) bx
		;	(if not ELDOTHER:) si
plex_check:
	push ss
	pop ds
	testopt [relocateddata], 8
linkdatarelocation internalflags4, -3
	jz @F
%if ELDOTHER
	mov bx, relocateddata
linkdatarelocation amis_multiplex_number, -2, optional
	test bx, bx
	jz @F
	cmp ah, byte [bx]
	je .notfound		; do not use our own multiplexer -->
%else
	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 -->
%endif
@@:
	mov al, 00h		; AMIS installation check
	extcall call_int2D	; AMIS (or "DOS reserved" = iret if no AMIS present)
	cmp al, 0FFh
	jne .notfound
	push cs
	pop ds
%if ELDOTHER
	push si
	extcall setes2dx	; es:di -> name strings of AMIS multiplexer that just answered
	mov cx, 8		; Ignore description, only compare vendor and program name
	repe cmpsw
	pop si
%else
	mov si, debuggeramissig	; ds:si -> our AMIS name strings
internalcoderelocation
	extcall setes2dx	; es:di -> name strings of AMIS multiplexer that just answered
	mov cx, 8		; Ignore description, only compare vendor and program name
	repe cmpsw
%endif
	je .checkret		; ZR, NC = match -->
.notfound:
	stc			; CY no match
.checkret:
	retn
%endif


	align 4, db 0
linkercoderelocations:
%if _ELDCALL_OPTIONAL || _ELDCALL_NEXT
	dw ELDCALL_CODE_RELOCATIONS_OPTIONAL, 0
%else
	dw ELDCALL_CODE_RELOCATIONS, 0, 0
%endif

%if _ELD_LINKER_PASSES
linkerlinkercoderelocations:
%if _ELDCALL_OPTIONAL || _ELDCALL_NEXT
	dw ELDCALL_LINKER_CODE_RELOCATIONS_OPTIONAL, 0
%else
	dw ELDCALL_LINKER_CODE_RELOCATIONS, 0, 0
%endif

	align 2, db 0
linktable:
	istruc LINKTABLE
at ltCodeToCode
	dw internallinkercodetocoderelocations - code
	dw internalcodetocoderelocations - code
at ltCodeToData
	dw internallinkercodetodatarelocations - code
	dw internalcodetodatarelocations - code
at ltDataToCode
	dw internallinkerdatatocoderelocations - code
	dw internaldatatocoderelocations - code
at ltDataToData
	dw internallinkerdatatodatarelocations - code
	dw internaldatatodatarelocations - code
at ltLinkData
	dw linkerlinkerdatarelocations - code
	dw linkerdatarelocations - code
at ltLinkCode
	dw linkerlinkercoderelocations - code
	dw linkercoderelocations - code
	iend
%endif

	align 2, db 0
linkerdatarelocations:
	dw LINK_DATA_RELOCATIONS, 0

	align 2, db 0
internalcodetocoderelocations:
	dw INTERNAL_CODE_TO_CODE_RELOCATIONS, 0

	align 2, db 0
internalcodetodatarelocations:
	dw INTERNAL_CODE_TO_DATA_RELOCATIONS, 0

	align 2, db 0
internaldatatocoderelocations:
	dw INTERNAL_DATA_TO_CODE_RELOCATIONS, -1

	align 2, db 0
internaldatatodatarelocations:
	dw INTERNAL_DATA_TO_DATA_RELOCATIONS, -1

%if _ELD_LINKER_PASSES
	align 2, db 0
linkerlinkerdatarelocations:
	dw LINK_LINKER_DATA_RELOCATIONS, 0

	align 2, db 0
internallinkercodetocoderelocations:
	dw INTERNAL_LINKER_CODE_TO_CODE_RELOCATIONS, 0

	align 2, db 0
internallinkercodetodatarelocations:
	dw INTERNAL_LINKER_CODE_TO_DATA_RELOCATIONS, 0

	align 2, db 0
internallinkerdatatocoderelocations:
	dw INTERNAL_LINKER_DATA_TO_CODE_RELOCATIONS, -1

	align 2, db 0
internallinkerdatatodatarelocations:
	dw INTERNAL_LINKER_DATA_TO_DATA_RELOCATIONS, -1
%endif

%if ELDOTHER
	align 2, db 0
linkerotherdatarelocations:
	dw LINK_OTHER_DATA_RELOCATIONS, 0

 %ifnidn LINK_OTHER_LINKER_DATA_RELOCATIONS, ""
  %error Other linker data links not supported
 %endif
%endif

%if ELDOTHER || ELDHINT
	align 2, db 0
debuggeramissig:
		fill 8, 32, db "ecm"
		fill 8, 32, db "lDebug"
%endif

%if ELDOTHER
	align 2, db 0
loaderamissig:
		fill 8, 32, db "ecm"
		fill 8, 32, db "Loader"
%endif

%ifnidni _LISTING, ""
listmsg:
.hint:		db "TracList-add-offset=",_LISTING,"::"
.hint.offset:	asciz "----h",13,10
%endif

	eldcall_dump_names ELDCALL_CODE_NAMES
	link_data_dump_names LINK_DATA_NAMES
%if ELDOTHER
	link_data_dump_names LINK_OTHER_DATA_NAMES
%endif

lmsg:
%if _ELD_LINKER_PASSES
%if _ELDCALL_OPTIONAL
.warning_code_optional:		counted "Optional code link missing, hash="
%endif
%if _ELDDATA_OPTIONAL
.warning_data_optional:		counted "Optional data link missing, hash="
%endif
.error_data:			counted "Data link missing, hash="
.error_code:			counted "Code link missing, hash="
.error.2:			counted "h, label ",'"'
%endif
%if ELDOTHER
.no_other:		asciz "ELD linker: No other link info found!",13,10
.error_other:		asciz "ELD linker: Other link info unknown format!",13,10
%endif

	align 16, db 0
