
; Public Domain

; test install command: install; r eldamount .; amount uninstall
; test run command: ; .; .

%include "lmacros3.mac"
%include "eld.mac"
%include "eldcall.mac"
%include "elddata.mac"
%include "isvariab.mac"

	cpu 8086

	addsection RELOCATEDDATA, nobits vstart=_ELD_RELOC_VSTART
%xdefine SECTION_OF_relocateddata _CURRENT_SECTION
relocateddata:
%xdefine _EXPR_SECTION _CURRENT_SECTION

	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 transient_data_size
at eldhDataAllocLength,	dw total_data_size - transient_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 "Provide ELDAMOUNT variable."


	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 "AMOUNT"
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.amount
internaldatarelocation
	extcallcall isstring?
	je .ours
@@:
	pop si
	dec si
	lodsb
	jmp .chain

.ours:
	extcallcall skipcomma
	dec si
reloc2	mov word [relocateddata], relocateddata
linkdatarelocation lastcmd, -4
linkdatarelocation dmycmd
	mov dx, relocateddata
linkdatarelocation msg.uninstall
	extcallcall isstring?
	jne @B

uninstall:
	pop ax
	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
	mov di, [ss:nameheader_offset]
internaldatarelocation
	push ss
	pop es
	xor ax, ax
	stosw			; name header
	mov di, word [ss:structure_offset]
internaldatarelocation
	stosw			; ivName
	stosw			; ivFlags
	stosw			; ivAddress
@@:
	push ss
	pop ds
	extcallcall putsz
	extcallcall cmd3	; return

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


		; INP:	ax = array index (0-based)
		;	cx = offset of this handler (ip)
		;	dil = default size of variable (1..4)
		;	dih = length of variable name
		; CHG:	si, ax
		; OUT:	NC if valid,
		;	 bx -> var, di = 0 or di -> mask
		;	 cl = size of variable (1..4)
		;	 ch = length of variable name
varsetup:
	xor si, si
	xor ax, ax
.loop:
	cmp si, word [relocateddata]
linkdatarelocation extseg_used
	jae .end		; NC -->
	testopt [cs:si + eldiFlags], eldifResident
	jz .next
	inc ax
.next:
	mov bx, word [cs:si + eldiEndCode]
	add si, 32
	jc .end			; CY -->
	cmp bx, si
	mov si, bx
	jae .loop
	; jb .end		; CY -->

.end:
	mov bx, variable
internaldatarelocation
	mov word [bx], ax
	mov cx, 0		; ! preserve CF
	xchg cx, di
	extcall var_ext_setup_done, required	; must NOT be extcallcall


	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


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

transientdata_displacement_equate equ transientdata_displacement
%assign transientdata_displacement_assign transientdata_displacement_equate
transientdata_vstart equ _ELD_DATA_VSTART + transientdata_displacement_equate
	addsection TRANSIENTDATA, align=1 valign=1 \
		 follows=DATA vstart=transientdata_vstart, DATA
%define TRANSIENTDATAFIXUP - transientdatastart + transientdata_displacement_assign
transientdatastart:

%define IV_NAME_RELOCATION internaldatarelocation
%define IV_ADDRESS_RELOCATION internalcoderelocation
%define IV_SETUP_RELOCATION linkdatarelocation var_ext_setup
%assign IVS_ONEBYTE 0

; align 1-2+.nolist nop
    times (((2) - (($-$$ + transientdata_displacement_assign) % (2))) % (2)) db 0
structure:
isvariablestruc "ELDAMOUNT", 2, ivfReadOnly, varsetup, 0, 0, relocateddata

; align 1-2+.nolist nop
    times (((16) - (($-$$ + transientdata_displacement_assign) % (16))) % (16)) db 0
transient_data_size equ $ - $$ + transientdata_displacement_assign


	usesection DATA

isvariablestrings ISVARIABLESTRINGS


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


uinit_data: equ $

.help:		db "Install this ELD using the INSTALL keyword parameter.",13,10
		db 13,10
		db "Use ELDAMOUNT variable to get number of installed ELDs.",13,10
		db "Run as AMOUNT UNINSTALL to uninstall this ELD.",13,10
		asciz
.installed:		asciz "AMOUNT installed.",13,10
.nofree:		asciz "AMOUNT cannot install, no ext variable free!",13,10
.unknown:		asciz "AMOUNT cannot install, unknown ext variable format!",13,10


	align 2, db 0
header:
	dw IVS_MOREBYTE_NAMEHEADERS

transientdata_displacement: equ $ - datastart


	absolute uinit_data

	alignb 2
structure_offset:	resw 1
nameheader_offset:	resw 1
variable:		resw 1

%if $ > msg.installed
 %error Variables overlap message
%endif

	alignb 16
uinit_data_end:
resident_data_end:
resident_data_size equ resident_data_end - datastart

uinit_data_size equ uinit_data_end - datastart


%if uinit_data_size >= transient_data_size
 total_data_size equ uinit_data_size
%else
 total_data_size equ transient_data_size
%endif
%assign _DATA_SIZE total_data_size


	usesection CODE

install:
	lodsb
	extcall chkeol

	houdini

	mov ax, relocateddata
linkdatarelocation ext_var_format
	cmp ax, 1
	jne .unknown
	mov ax, relocateddata
linkdatarelocation ext_var_size
	cmp ax, ISVARIABLESTRUC_size
	jne .unknown
	mov cx, relocateddata
linkdatarelocation ext_var_amount
	jcxz .unknown
	jmp @F

.unknown:
	mov ax, 0E18h
	extcall setrc
	mov dx, msg.unknown
internaldatarelocation
	extcall putsz
	xor ax, ax
	retf

@@:

	mov di, relocateddata
linkdatarelocation ext_var
	mov si, relocateddata
linkdatarelocation isvariable_morebyte_nameheaders.ext
.loop:
	cmp word [di], 0
	je .found
	add di, ISVARIABLESTRUC_size
	lodsw
	loop .loop

.nofree:
	mov ax, 0E19h
	extcall setrc
	mov dx, msg.nofree
internaldatarelocation
	extcall putsz
	xor ax, ax
	retf

.found:
	mov word [nameheader_offset], si
internaldatarelocation
	mov ax, word [header]
internaldatarelocation
	mov word [si], ax
	mov word [structure_offset], di
internaldatarelocation
	mov si, structure
internaldatarelocation
	mov cx, words(ISVARIABLESTRUC_size)
	rep movsw

	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
