
; Public Domain

; test install command: install; amiscmd; amiscmd 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 received on 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 "AMISCMD"
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.amiscmd
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
	lodsb
	extcallcall chkeol
@@:
	extcallcall cmd3


amis:
	jmp strict short .entry
.chain:
	retf
	times 5 - ($ - amis) nop
.entry:
	lframe far
	lpar word, intfl
	lpar word, intcs
	lpar word, intip
	lpar word, ax
	lpar word, bx
	lpar word, cx
	lpar word, dx
	lpar word, ds
	lpar_return
	lenter
	push es
	push si
	push di
	push ds
	cmp al, 43h
	jne .transfer
	mov al, 2
	cmp word [bp + ?cx], 0
	jne .return_al
	push ds
	pop es
	mov ds, dx
	mov si, bx
	mov ah, 0
	lodsb
	mov cx, ax		; = length without length byte and CR
	cmp al, 255		; valid length ?
	je .toomuch		; no -->
	mov bx, ax
	add bx, 4		; = length in buffer
	mov di, word [es:buffer.last]
internaldatarelocation		; -> free space
	add bx, di
	jc .toomuch
	cmp bx, strict word buffer.end
internaldatarelocation		; fits ?
	ja .toomuch		; no -->

	inc word [es:injecting]
internaldatarelocation

; -> 0
; inject: store 0 as first, -> cmd
; inject: store first (0) as second, store cmd as first
; inject: store second (0) as third, store cmd as second

; -> comp
; inject: store comp as first, -> cmd
; inject: store first (comp) as second, store cmd as first
; inject: store second (comp) as third, store cmd as second

; -> 0
; inject: store 0 as first, -> cmd
; other inject: stores cmd, -> other
; inject: store first (0) as second, store cmd as first

	mov bx, buffer
internaldatarelocation
	cmp bx, di
	jae .store_first
.find_last:
	push bx
	mov dl, byte [es:bx + 2]
	mov dh, 0
	add dx, 4
	add bx, dx
	cmp bx, di
	jae .found_last
	pop dx
	jmp .find_last

.found_last:
	pop bx
	mov dx, inject
internalcoderelocation
	xchg dx, [es:bx]	; get prior last, store cmd
	mov bx, dx		; bx = prior inject
	jmp @F

.store_first:
	mov bx, inject
internalcoderelocation
	xchg bx, word [es:relocateddata]
linkdatarelocation ext_inject_handler
@@:
	xchg ax, bx
	stosw			; store prior inject
	xchg ax, bx
	stosb			; store length
	rep movsb		; store text
	mov al, 13
	stosb			; store CR

	mov word [es:buffer.last], di
internaldatarelocation		; -> free space

	mov al, 0FFh
.return_al:
	db __TEST_IMM16		; skip mov al
.toomuch:
	mov al, 1
	mov byte [bp + ?ax], al
	jmp .return

.transfer:
	db __TEST_IMM8		; (NC)
.return:
	stc
	pop ds
	pop di
	pop si
	pop es
	lleave , optimiserestoresp
	jnc .chain
	retf


inject:
	cmp word [buffer.last], strict word buffer + 4
internaldatarelocation -4		; are we ok ?
internaldatarelocation
	jae @F				; ok -->
	houdini
	nop
	extcall cmd3_not_inject

@@:
	extcallcall yy_reset_buf

	extcallcall putsline

	mov si, buffer			; si -> entry
internaldatarelocation
	lodsw				; ax = prior inject

	mov di, relocateddata + 1
linkdatarelocation line_in		; -> buffer
	mov cl, byte [si]
	mov ch, 0			; = length excluding length byte and CR
	push cx
	inc cx
	inc cx				; include length byte and CR
	rep movsb			; store it
					; si -> next entry (if any)
	mov di, buffer			; di -> start of buffer
internaldatarelocation
	mov cx, word [buffer.last]	; -> end of buffer
internaldatarelocation
	sub cx, si			; = size remaining entries

		; critical block
	rep movsb			; move them down
	mov word [buffer.last], di	; -> new end of buffer
internaldatarelocation
	mov word [relocateddata], ax
linkdatarelocation ext_inject_handler	; store for next injection
	dec word [injecting]
internaldatarelocation
		; end critical block

	mov si, relocateddata + 2
linkdatarelocation line_in

	mov dx, si
	pop cx
	extcallcall puts
	mov dx, msg.linebreak
internaldatarelocation
	extcallcall putsz

	extcallcall skipwhite

	extcall cmd3_injected


uninstall:
	lodsb
	extcallcall chkeol

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

	cmp word [ss:injecting], strict byte 0
internaldatarelocation -3
	jne uninstall_command.error

uninstall_amis:
	xor bx, bx		; = 0 (no prior, modify ext_command_handler)
	mov di, amis		; di -> us
internalcoderelocation
	cmp word [di], 9090h
	je .amis_done
	mov si, word [ss:relocateddata]
linkdatarelocation ext_amis_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], 0CBh	; is it a retf ?
	jne @F			; no -->
				; yes, reset ext_amis_handler to zero
.setbx:
	mov word [ss:relocateddata], bx
linkdatarelocation ext_amis_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_amis_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 0CBh/0E9h
	lodsw			; ax = near rel16 displacement (if 0E9h)
	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
.done:
	mov word [amis], 9090h
internalcoderelocation -4
.amis_done:

.error equ uninstall_command.error

uninstall_command:
	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.help
internaldatarelocation
	extcall putsz
@@:
	call uninstall_oneshot
	xor ax, ax
	retf


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

buffer.last:	dw buffer
internaldatarelocation
injecting:	dw 0


msg:
.amiscmd:		asciz "AMISCMD"
.uninstall_done:	db "AMIS command uninstalled."
%if _ELD_RECLAIM_HINT
			db " (Don't forget to use reclaim.eld)"
%endif
			asciz 13,10
.uninstall_error:	db "AMIS command unable to uninstall!"
.linebreak:		asciz 13,10

uinit_data: equ $

.installed:	asciz "AMIS command installed.",13,10
.help:		db "Install this ELD using an INSTALL keyword.",13,10
		db 13,10
		db "Runs command sent to AMIS function 43h.",13,10
		db "Call as AH = multiplex number, AL = 43h, DX:BX -> command.",13,10
		db "Injection occurs before the next command in cmd3.",13,10
		db 13,10
		db "Run with AMISCMD 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 2
buffer:		resb 1024
.end:

	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


	mov bx, word [relocateddata]
linkdatarelocation ext_amis_handler
				; -> prior
	mov di, amis		; -> our handler
internalcoderelocation
	test bx, bx		; installing as first ?
	jz .amis_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

.amis_only_first:
	mov word [relocateddata], amis
linkdatarelocation ext_amis_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
