
; Public Domain

; test install command: install; .; inject uninstall
; test run command: ; .; .

%include "lmacros3.mac"
%include "eld.mac"
%include "eldcall.mac"
%include "elddata.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

description:		asciz "Inject commands to other's AMIS function 43h."


	align 16, db 0

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


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


	usesection CODE

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

	mov dx, msg.inject
internaldatarelocation
	extcallcall isstring?
	je @F
.transfer_to_chain:
	pop si
	dec si
	lodsb
	jmp .chain

@@:
	extcallcall skipcomma
	dec si
reloc2	mov word [relocateddata], relocateddata
linkdatarelocation lastcmd, -4
linkdatarelocation dmycmd
	mov dx, relocateddata
linkdatarelocation msg.uninstall
	extcallcall isstring?
	je uninstall
	mov dx, msg.run
internaldatarelocation
	call isstring?
	extcallcall skipcomma
	call run
	extcallcall cmd3


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

	mov si, msg.debuggeramissig
internaldatarelocation

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

	mov ah, 0FFh		; start with multiplex number 0FFh
.loop_hint_debug:
	call inject_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_debug	; try next if we didn't check all yet -->

	mov si, msg.loaderamissig
internaldatarelocation

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

	mov ah, 0FFh		; start with multiplex number 0FFh
.loop_hint_loader:
	call inject_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_loader	; try next if we didn't check all yet -->

	jmp .no_hint

.end_hint:
	pop si
	 push ss
	 pop es

	mov al, ah
	mov di, msg.return.mpx
internaldatarelocation
	extcallcall hexbyte

	mov di, relocateddata
linkdatarelocation line_out
	mov bx, di			; bx -> buffer
	xor cx, cx
	inc di
.loop:
	lodsb
	extcallcall iseol?.notsemicolon
	je .end
	inc cx
	stosb
	jmp .loop

.end:
	mov byte [bx], cl		; insert the count
	mov al, 13
	stosb				; for good measure add a CR
	mov dx, word [relocateddata]
linkdatarelocation pspdbg		; => buffer
	xor cx, cx			; flags
	mov al, 43h
	extcallcall call_int2D
	mov di, msg.return.code
internaldatarelocation
	extcallcall hexbyte
	mov dx, msg.return
internaldatarelocation
@@:
	extcallcall putsz
	retn

.no_hint:
	pop si
	 push ss
	 pop es
	mov dx, msg.none
internaldatarelocation
	jmp @B


		; INP:	ah = multiplex number to check
		;	ss: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, bx, cx, dx
inject_plex_check:
	push ss
	pop ds
	testopt [relocateddata], 8
linkdatarelocation internalflags4, -3
	jz @F
	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 -->
@@:
	mov al, 00h		; AMIS installation check
	extcallcall call_int2D	; AMIS (or "DOS reserved" = iret if no AMIS present)
	cmp al, 0FFh
	jne .notfound
	; push cs
	; pop ds
	push si
	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
	pop si
	je .checkret		; ZR, NC = match -->
.notfound:
	stc			; CY no match
.checkret:
	retn


uninstall:
	lodsb
	extcallcall chkeol

	mov es, word [relocateddata]
linkdatarelocation extdssel
	extcallcall ispm
	jz @F
	mov es, word [relocateddata]
linkdatarelocation extseg
@@:
	push es
	pop ds

	xor bx, bx		; = 0 (no prior, modify ext_command_handler)
	mov di, command		; di -> us
internalcoderelocation
	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:
	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:
	clropt [code + eldiFlags], eldifResident
internalcoderelocation -3	; mark block as free
	mov dx, msg.uninstall_done
internaldatarelocation
@@:
	push ss
	pop ds
	extcallcall putsz
	extcallcall cmd3	; return

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


error:
	extcall error

	eldcall_dump_callcall ELDCALL_CALLCALL_LIST

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


start:
	mov bx, es
	 push ss
	 pop es
	call skipcomma
	dec si
	mov dx, relocateddata
linkdatarelocation msg.install
	call isstring?
	je install

	mov dx, msg.run
internaldatarelocation
	call isstring?
	je .run

	mov dx, msg.help
internaldatarelocation
	extcall putsz
@@:
	call uninstall_oneshot
	xor ax, ax
	retf

.run:
	call skipcomma
	call run
	jmp @B


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


	usesection DATA
	align 2, db 0

msg:
.debuggeramissig:
		fill 8, 32, db "ecm"
		fill 8, 32, db "lDebug"
.loaderamissig:
		fill 8, 32, db "ecm"
		fill 8, 32, db "Loader"
.inject:		asciz "INJECT"
.run:			asciz "RUN"
.return:		db "Injecting to multiplex number "
.return.mpx:		db "--h, return value is "
.return.code:		asciz "--h.",13,10
.none:			asciz "No multiplexer detected!",13,10
.uninstall_done:	db "INJECT uninstalled."
%if _ELD_RECLAIM_HINT
			db " (Don't forget to use reclaim.eld)"
%endif
			asciz 13,10
.uninstall_error:	db "INJECT unable to uninstall!"
.linebreak:		asciz 13,10

uinit_data: equ $

.installed:	asciz "INJECT installed.",13,10
.help:		db "Install this ELD using an INSTALL keyword or run with RUN.",13,10
		db 13,10
		db "Sends a command to other's AMIS function 43h.",13,10
		db "Injection occurs before the next command in cmd3.",13,10
		db 13,10
		db "Run with INJECT [RUN] <command> to inject command.",13,10
		db "Run with INJECT UNINSTALL to uninstall.",13,10
		asciz


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

	absolute uinit_data

	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
	sub word [es:code + eldiEndCode], ax
internalcoderelocation		; adjust size
	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
	sub word [es:code + eldiEndData], ax
internalcoderelocation		; adjust size
	sub word [relocateddata], ax
linkdatarelocation extdata_used	; adjust size
%endif

	mov bx, word [relocateddata]
linkdatarelocation ext_command_handler
				; -> prior
	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:
	setopt [es:code + eldiFlags], eldifResident
internalcoderelocation -3	; mark block as resident
	mov word [relocateddata], command
linkdatarelocation ext_command_handler, -4
internalcoderelocation		; -> our entrypoint

	testopt [relocateddata], 4
linkdatarelocation options7, -3
	jnz @F
	mov dx, msg.installed
internaldatarelocation
	call putsz
@@:

	xor ax, ax
	retf


%include "eldlink.asm"

	align 16
code_size equ $ - code
