
; Public Domain

%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

		; 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 0
at eldhCodeEntrypoint,	dw linker - code
at eldhReserved
	iend


	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 "ELDTEST"
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

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

@@:
.ours:
	pop ax
	extcallcall skipcomma
reloc2	mov word [relocateddata], relocateddata
linkdatarelocation lastcmd, -4
linkdatarelocation dmycmd

	extcallcall chkeol
	jmp uninstall


install_handler:
houdini

	call get_es_ext
	mov bx, word [relocateddata]
linkdatarelocation ext_puts_handler
				; -> prior
	mov di, puts_handler	; -> our handler
internalcoderelocation
	test bx, bx		; installing as first ?
	jnz .chain		; no, go and chain -->

.first:
	houdini
	mov word [relocateddata], puts_handler
linkdatarelocation ext_puts_handler, -4
internalcoderelocation		; -> our entrypoint
	jmp .hooked		; yes, simple --> (leave as extcall puts_ext_done)

.chain:
	xchg bx, di
@@:
	mov ax, 08EBh
	scasw			; check entrypoint jmp strict short
	jne .error
	mov al, 0E9h		; = jmp near opcode
	scasb			; check for jmp
	jne @F
	add di, word [es:di]
	inc di
	inc di
	jmp @B

@@:
	dec di
	dec ax			; check for call
	scasb
	jne .error

	inc byte [es:di - 1]	; patch to 0E9h
	sub bx, di
	dec bx
	dec bx
	xchg ax, bx		; -> rel16 to our handler
	stosw			; make a downlink to us

.hooked:
	not byte [putstable + htInstalled]
internaldatarelocation
	retn

.error:
	extcallcall error


puts_handler:
.:
	jmp strict short .entry
.chain:
	extcall puts_ext_done, required	; must NOT be extcallcall
	times 10 - ($ - .) nop
.entry:

	clc
	jmp .chain


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


uninstall_table:
	lframe
	lenter
	lvar word, table
	 push si

	rol byte [ss:si + htInstalled], 1
	jnc .next_table		; --> (NC)
	xor bx, bx		; = 0 (no prior, modify handler)
	mov di, word [ss:si + htEntry]	; di -> us
	mov si, word [ss:si + htHandler]; -> handler
	mov si, word [ss:si]	; 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 si, word [bp + ?table]
	mov si, word [ss:si + htHandler]; -> handler
	mov word [ss:si], bx
	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:
	mov si, word [bp + ?table]
	not byte [ss:si + htInstalled]
	clc
.next_table:
@@:
	lleave
	retn

.error:
	stc
	jmp @B


uninstall:
	call get_es_ext
	push es
	pop ds

	mov si, putstable
internaldatarelocation
	call uninstall_table
	jc .error

	mov si, commandtable
internaldatarelocation
	call uninstall_table
	jc .error

	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


	eldcall_dump_callcall ELDCALL_CALLCALL_LIST

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


start:
	mov bx, es
	push ss
	pop es
	lodsb
	extcall chkeol
	jmp install


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

	struc HOOKTABLE
htEntry:		resw 1
htHandler:		resw 1
htInstalled:		resw 1
	endstruc

	align 2, db 0
putstable:
	istruc HOOKTABLE
at htEntry,		dw puts_handler
internalcoderelocation
at htHandler,		dw relocateddata
linkdatarelocation ext_puts_handler
at htInstalled,		dw 0
	iend

commandtable:
	istruc HOOKTABLE
at htEntry,		dw command
internalcoderelocation
at htHandler,		dw relocateddata
linkdatarelocation ext_command_handler
at htInstalled,		dw 0
	iend

msg:
.eldtestuninstall:	asciz "ELDTESTUNINSTALL"
.installed:		asciz "Installed",13,10
.uninstall_done:	asciz "Uninstalled",13,10
.uninstall_error:	asciz "Uninstall error",13,10

	align 16, db 0
data_size equ $ - datastart
%assign _DATA_SIZE data_size

	usesection CODE

install:
	call .internal

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


.internal:
	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

	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
	not byte [commandtable + htInstalled]
internaldatarelocation
	call install_handler
	retn


%include "eldlink.asm"

	align 16
code_size equ $ - code
