
; Public Domain

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

%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 "Display or write environment variables."


	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 "SET"
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 word [active], strict byte 0
internaldatarelocation -3
	je @F
	rol byte [putstable + htInstalled], 1
internaldatarelocation
	jnc @F

	call get_es_ext
	push es
	pop ds
	mov si, putstable
internaldatarelocation
	call uninstall_table
	push ss
	pop ds
	push ss
	pop es
	pop si
	push si
	dec si
	lodsb
@@:

	cmp al, '-'
	jne @F
	extcallcall skipcomma
@@:
	dec si

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

@@:
.ours:
	pop ax
	extcallcall skipcomma
	dec si
reloc2	mov word [relocateddata], relocateddata
linkdatarelocation lastcmd, -4
linkdatarelocation dmycmd
	push si
	mov dx, relocateddata
linkdatarelocation msg.uninstall
	extcallcall isstring?
	jne @F
	extcallcall skipwhite
	extcallcall iseol?
	jne @F
	pop ax
	jmp uninstall
@@:
	pop si
	lodsb
run:
	mov byte [exec], 0
internaldatarelocation -3

	extcallcall iseol?
	je showall
	dec si
	extcallcall skipwhite
houdini
	cmp al, '/'
	jne @F
	lodsb
	extcallcall uppercase
	cmp al, 'E'
	jne error
	extcallcall skipwhite
	push si
	xor bx, bx
	cmp al, '/'
	jne .gotexec
	lodsb
	cmp al, '0'
	jb .gotexec
	cmp al, '9'
	ja .gotexec
	sub al, '0'
	mov ah, 0
.loop:
	mov cx, bx
	add bx, bx		; times 2
	add bx, bx		; times 4
	add bx, cx		; times 5
	add bx, bx
	add bx, ax
	lodsb
	cmp al, '0'
	jb .gotnum
	cmp al, '9'
	ja .gotnum
	sub al, '0'
	jmp .loop

.gotnum:
	extcallcall skipwh0
	db __TEST_IMM8		; (skip pop)

.gotexec:
	pop si
	mov word [execnum], bx
internaldatarelocation
	mov byte [exec], 0FFh
internaldatarelocation -3

@@:
	dec si
	mov bx, si
@@:
	lodsb
	cmp al, '='
	je set_or_clear
	extcallcall iseol?.notsemicolon
	jne @B
showone:
	rol byte [exec], 1
internaldatarelocation
	jc error
	dec si
	mov cx, si
	sub cx, bx
	jz error
	mov si, bx
	push cx
	mov di, relocateddata
linkdatarelocation line_out
	push di
@@:
	lodsb
	extcallcall uppercase
	stosb
	loop @B
	mov al, '='
	stosb
	pop si
	pop cx
	inc cx

	call get_env
	extcallcall setes2dx
	xor di, di
	houdini
.loop:
	mov al, 0
	scasb
	je .end
	dec di
	push di
	push si
	push cx
@@:
	repe cmpsb
	je @F
	mov al, [es:di - 1]
	extcallcall uppercase
	cmp al, [si - 1]
	je @B
@@:
	mov dx, di
	pop cx
	pop si
	pop di
	je .found
	push cx
	mov al, 0
	mov cx, 7FFFh
	repne scasb
	pop cx
	je .loop
.end:
	push ss
	pop es
	mov dx, msg.notfound.1
internaldatarelocation
	extcallcall putsz
	mov dx, bx
	dec cx
	extcallcall puts
	mov dx, msg.notfound.2
internaldatarelocation
	extcallcall putsz
	mov ax, 0E55h
	extcallcall setrc
	extcallcall cmd3


.found:
	mov al, 0
	mov cx, 7FFFh
	repne scasb
	mov cx, di
	dec cx
	sub cx, dx
	extcallcall puts
	mov dx, msg.crlf
internaldatarelocation
	extcallcall putsz
	extcallcall cmd3


set_or_clear:
	push si
	lodsb
	extcallcall iseol?.notsemicolon
	je clear
@@:
	lodsb
	extcallcall iseol?.notsemicolon
	jne @B
set:
	dec si
	mov dx, si			; -> EOL
	pop si
	dec si
	mov cx, si
	sub cx, bx
	jz error
	mov si, bx
	push cx
	mov di, relocateddata
linkdatarelocation line_out
	push di
@@:
	lodsb
	extcallcall uppercase
	stosb
	loop @B
	mov al, '='
	stosb
	pop si
	pop cx
	inc cx

	add bx, cx

	lframe none
	lvar word, offsettoinsert
	lenter
	lvar word, pointercontent
	 push bx
	lvar word, pointereol
	 push dx
	lvar word, pointername
	 push si
	lvar word, lengthname
	 push cx
	lvar word, execnum
	 push word [execnum]
internaldatarelocation
	xor ax, ax
	lvar word, lastcr
	 push ax
	lvar word, state
	 push ax

	rol byte [exec], 1
internaldatarelocation
	jnc set_not_exec

set_exec:
	inc cx
	and cl, ~1
	sub sp, cx
	mov di, sp
	rep movsb
	mov word [bp + ?pointername], sp

	mov di, execbuffer
internaldatarelocation
	mov word [contentstart], di
internaldatarelocation
	mov word [contentend], di
internaldatarelocation
	mov si, bx
	mov cx, dx
	sub cx, si
	mov word [execlen], cx
internaldatarelocation
	rep movsb

	houdini
	rol byte [putstable + htInstalled], 1
internaldatarelocation
	jc .error

	push word [relocateddata]
linkdatarelocation ext_inject_handler
	push word [relocateddata]
linkdatarelocation savesp
	push word [relocateddata]
linkdatarelocation throwsp
	push word [eld_sp]
internaldatarelocation
	push bp

houdini

	call get_es_ext
	mov bx, word [relocateddata]
linkdatarelocation ext_puts_handler
				; -> prior
	mov di, puts_handler	; -> our handler
internalcoderelocation
	houdini
	mov ax, word [puts_ext_done_rel16]
internaldatarelocation
	mov byte [es:di + 2], 0E8h
				; re-init the handler
	mov word [es:di + 3], ax
				; re-init the handler

	test bx, bx		; installing as first ?
	jnz .chain		; no, go and chain -->

.first:
	mov word [relocateddata], di
linkdatarelocation ext_puts_handler
				; -> 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

	inc word [active]
internaldatarelocation
	jz @F
	dec word [active]
internaldatarelocation
.error:
	jmp error

@@:
	mov word [eld_sp], sp
internaldatarelocation
	mov word [relocateddata], sp
linkdatarelocation savesp
	mov word [relocateddata], sp
linkdatarelocation throwsp

	mov word [relocateddata], .inject
linkdatarelocation ext_inject_handler, -4
internalcoderelocation

	rol byte [installed], 1
internaldatarelocation
	jc @F

	setopt [es:code + eldiFlags], eldifResident
internalcoderelocation -3	; mark block as resident

@@:
	extcallcall cmd3

.inject:
	extcallcall yy_reset_buf
	mov di, relocateddata + 1
linkdatarelocation line_in
	mov si, execbuffer
internaldatarelocation
	mov cx, word [execlen]
internaldatarelocation
	mov al, cl
	stosb
	push di
	rep movsb
	mov al, 13
	stosb
	pop si
	extcall skipwhite

	mov word [relocateddata], .return
linkdatarelocation ext_inject_handler, -4
internalcoderelocation
	extcall cmd3_injected

.return:
	mov sp, word [eld_sp]
internaldatarelocation

houdini

	pop bp
	pop word [eld_sp]
internaldatarelocation
	pop word [relocateddata]
linkdatarelocation throwsp
	pop word [relocateddata]
linkdatarelocation savesp
	pop word [relocateddata]
linkdatarelocation ext_inject_handler

	dec word [active]
internaldatarelocation

	call get_es_ext
	push es
	pop ds
	mov si, putstable
internaldatarelocation
	call uninstall_table
	push ss
	pop ds
	jnc @FF
	rol byte [installed], 1
internaldatarelocation
	jc @F

	houdini
	mov ax, 0E56h
	extcallcall setrc
	mov dx, msg.putscriticalerror
internaldatarelocation
	extcallcall putsz
	mov bx, es		; bx => ext seg
	call install.internal
	extcallcall cmd3

@@:
	mov ax, 0E57h
	extcallcall setrc
	mov dx, msg.putserror
internaldatarelocation
	extcallcall putsz
	extcallcall cmd3

@@:
	rol byte [installed], 1
internaldatarelocation
	jc @F

	clropt [es:code + eldiFlags], eldifResident
internalcoderelocation -3	; mark block as no longer resident

@@:
	push ss
	pop es

houdini
	mov ax, word [contentstart]
internaldatarelocation
	mov word [bp + ?pointercontent], ax
	mov ax, word [contentend]
internaldatarelocation
	mov word [bp + ?pointereol], ax

	mov si, word [bp + ?pointername]
	mov cx, word [bp + ?lengthname]

	mov di, relocateddata
linkdatarelocation line_out
	push di
	push cx
	rep movsb
	pop cx
	pop si
	mov word [bp + ?pointername], si
	lea sp, [bp + ?state]

	sub ax, word [bp + ?pointercontent]
	jnz set_not_exec
	lleave code
	jmp clear_exec


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

	houdini
	cmp word [active], strict byte 0
internaldatarelocation -3
	je @F
	clc
	jmp .chain

@@:
	push si
	push di
	push ax
	push es
	push bp
	mov bp, word [eld_sp]
internaldatarelocation
	mov bp, [bp]

	mov si, dx
	mov cx, ax
	jcxz .end
	rol byte [bp + ?state + 1], 1
	jc .end
	push es
	pop ds
	push ss
	pop es
.loop:
	lodsb
	rol byte [bp + ?state], 1
	jc .read
	cmp al, 13
	je .next
	cmp al, 10
	je .next
	cmp al, 0
	je .next
	not byte [bp + ?state]
.read:

	cmp word [bp + ?execnum], 0
	je .reallyread
	cmp al, 13
	je .dec
	cmp al, 10
	jne @F
	cmp byte [bp + ?lastcr], 13
	jne .dec
@@:
	cmp al, 0
	jne .next
.dec:
	dec word [bp + ?execnum]
	jmp .next

.reallyread:
	mov di, word [ss:contentend]
internaldatarelocation
	cmp al, 13
	je .finish
	cmp al, 10
	jne @F
	cmp byte [bp + ?lastcr], 13
	jne .finish
	jmp .next
@@:
	cmp al, 0
	je .finish
	cmp di, strict word execbuffer.end
internaldatarelocation
	jae .finish
	stosb
	mov word [ss:contentend], di
internaldatarelocation
	jmp .next

.finish:
	not byte [bp + ?state + 1]
	jmp .end

.next:
	mov byte [bp + ?lastcr], al
	loop .loop
.end:
	stc
	 push ss
	 pop ds
	pop bp
	pop es
	pop ax
	pop di
	pop si
	extcall puts_ext_done, required


set_not_exec:
	xor dx, dx
	lvar word, lengthtofree
	 push dx
	lvar word, offsettofree
	 push dx
	call get_env
	dec dx
	extcallcall setes2dx
	lvar word, envsizepara
	 push word [es:3]
	inc dx
	extcallcall setes2dx
	xor di, di
	houdini
.loop:
	mov al, 0
	scasb
	je .end
	dec di
	push di
	push si
	push cx
@@:
	repe cmpsb
	je @F
	mov al, [es:di - 1]
	extcallcall uppercase
	cmp al, [si - 1]
	je @B
@@:
	pop cx
	pop si
	pop di
	je .found
	push cx
	mov al, 0
	mov cx, 7FFFh
	repne scasb
	pop cx
	je .loop
.error:
	mov ax, 0E58h
	extcallcall setrc
	mov dx, msg.error_invalid
internaldatarelocation
	extcallcall putsz
	extcallcall cmd3

.full:
	mov ax, 0E59h
	extcallcall setrc
	mov dx, msg.error_full
internaldatarelocation
	extcallcall putsz
	extcallcall cmd3


.found:
	mov si, di		; si -> at the variable
	mov al, 0
	mov cx, 7FFFh
	repne scasb
	jne .error
				; di -> after NUL
	mov dx, di
	sub dx, si
	mov word [bp + ?lengthtofree], dx
	mov word [bp + ?offsettofree], si

.loop_find_length:
	mov al, 0
	scasb
	je .end
	dec di
	mov al, 0
	mov cx, 7FFFh
	repne scasb
	je .loop_find_length
	jmp .error


.end:
	lea ax, [di - 1]
	mov word [bp + ?offsettoinsert], ax
	mov ax, 1
	scasw			; cmp ax, [es:di]
	jb .error		; 1 < word, unknown -->
	ja .havesize		; 1 > word, no additional string -->
	mov al, 0
	mov cx, 7FFFh
	repne scasb
	jne .error
.havesize:
	mov cx, di		; length total
	sub cx, word [bp + ?lengthtofree]
				; length when freeing prior
	mov ax, word [bp + ?pointereol]
	sub ax, word [bp + ?pointercontent]
				; length content
	lvar word, lengthcontent
	 push ax
	add ax, word [bp + ?lengthname]
				; length name
	inc ax			; length insert
	lvar word, lengthinsert
	 push ax
	add cx, ax
	jc .full
	mov dx, word [bp + ?envsizepara]
	add dx, dx
	jc @F
	add dx, dx
	jc @F
	add dx, dx
	jc @F
	add dx, dx
	jnc @FF
@@:
	mov dx, -1
@@:
	cmp dx, cx
	jb .full

	lvar word, offsetend
	 push di
	mov cx, di			; -> end
	mov di, word [bp + ?offsettofree]
					; -> to free (destination)
	mov si, di
	add si, word [bp + ?lengthtofree]
					; -> source
	sub cx, si			; = end minus source = how long
	 push es
	 pop ds
	rep movsb			; di -> new end
	dec di				; -> new last byte
	mov si, di			; si -> new last byte
	add di, word [bp + ?lengthinsert]
					; di -> destination last byte
	mov cx, word [bp + ?offsettoinsert]
	sub cx, word [bp + ?lengthtofree]
					; cx -> where to insert
	push cx
	neg cx
	add cx, si			; last byte minus where to insert
	inc cx

	std
	numdef AMD_ERRATUM_109_WORKAROUND, 1
%if _AMD_ERRATUM_109_WORKAROUND
	jcxz @FF
	cmp cx, 20
	ja @FF
@@:
	movsb
	loop @B
@@:
%endif
	rep movsb
	cld

	pop di
	 push ss
	 pop ds
	mov si, word [bp + ?pointername]
	mov cx, word [bp + ?lengthname]
	rep movsb
	mov si, word [bp + ?pointercontent]
	mov cx, word [bp + ?lengthcontent]
	rep movsb
	mov al, 0
	stosb
	extcallcall cmd3
	lleave ctx


clear:
	pop si
	dec si
	rol byte [exec], 1
internaldatarelocation
	jc error
	mov cx, si
	sub cx, bx
	jz error
	mov si, bx
	push cx
	mov di, relocateddata
linkdatarelocation line_out
	push di
@@:
	lodsb
	extcallcall uppercase
	stosb
	loop @B
	mov al, '='
	stosb
	pop si
	pop cx
	inc cx

clear_exec:
	call get_env
	dec dx
	extcallcall setes2dx
	mov bp, word [es:3]
	inc dx
	extcallcall setes2dx
	xor di, di
	houdini
.loop:
	mov al, 0
	scasb
	je .end
	dec di
	push di
	push si
	push cx
@@:
	repe cmpsb
	je @F
	mov al, [es:di - 1]
	extcallcall uppercase
	cmp al, [si - 1]
	je @B
@@:
	pop cx
	pop si
	pop di
	je .found
	push cx
	mov al, 0
	mov cx, 7FFFh
	repne scasb
	pop cx
	je .loop
.end:
	extcallcall cmd3

.found:
	mov si, di		; si -> at the variable
	mov al, 0
	mov cx, 7FFFh
	repne scasb
	jne .end
				; di -> after NUL
	xchg si, di		; di -> variable, si -> after NUL
	mov cx, bp
	add cx, cx
	jc @F
	add cx, cx
	jc @F
	add cx, cx
	jc @F
	add cx, cx
	jnc @FF
@@:
	mov cx, -1
@@:
	sub cx, si
	push es
	pop ds
	rep movsb
	jmp .end


showall:
	call get_env
	extcallcall setes2dx
	xor di, di
.loop:
	mov dx, di
	mov al, 0
	scasb
	je .end
	mov cx, 7FFFh
	repne scasb
	jne .end
	mov cx, di
	dec cx
	sub cx, dx
	extcallcall puts
	mov dx, msg.crlf
internaldatarelocation
	extcallcall putsz
	jmp .loop

.end:
	extcallcall cmd3


get_env:
	mov dx, [2Ch]
	test dx, dx
	jz @F
	retn

@@:
	mov ax, 0E5Ah
	extcallcall setrc
	mov dx, msg.error
internaldatarelocation
	extcallcall putsz
	extcallcall cmd3


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


error:
	extcall error

	eldcall_dump_callcall ELDCALL_CALLCALL_LIST

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


start:
	mov ax, word [es:puts_handler + 3]
internalcoderelocation		; save for re-init
	mov word [puts_ext_done_rel16], ax
internaldatarelocation

	mov bx, es
	 push ss
	 pop es

	call skipcomma
	dec si
	mov dx, relocateddata
linkdatarelocation msg.install
	call isstring?
	je install
	mov dx, relocateddata
linkdatarelocation msg.run
	call isstring?
	jne help
	extcall skipcomma
	jmp run

help:
	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

	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


	align 2, db 0
puts_ext_done_rel16:
		dw 0
active:		dw -1
installed:	db 0

msg:
.set:			asciz "SET"
.notfound.1:		asciz "Environment variable '"
.notfound.2:		asciz "' not found.",13,10
.uninstall_done:	db "SET command uninstalled."
%if _ELD_RECLAIM_HINT
			db " (Don't forget to use reclaim.eld)"
%endif
			asciz 13,10
.uninstall_error:	asciz "SET command unable to uninstall!",13,10
.error:			db "SET command error: No environment!"
.crlf:			asciz 13,10
.error_full:		asciz "Environment full.",13,10
.error_invalid:		asciz "Environment invalid.",13,10
.putserror:		asciz "SET: Unable to uninstall puts handler!",13,10
.putscriticalerror:	asciz "SET: Unable to uninstall puts handler! Staying resident!",13,10

uinit_data: equ $

.installed:	asciz "SET command installed.",13,10
.help:		db "Install this ELD using the INSTALL keyword parameter.",13,10
		db 13,10
		db "Run as SET variable=content to set a variable.",13,10
		db "Run as SET variable= to delete a variable.",13,10
		db "Run as SET variable to display a variable.",13,10
		db "Run as SET to display all variables.",13,10
		db "Run as SET UNINSTALL to uninstall this ELD.",13,10
		asciz

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

	absolute uinit_data

exec:		resb 1
	alignb 2
execnum:	resw 1
execbuffer:	resb 384
.end:
contentstart:	resw 1
contentend:	resw 1
execlen:	resw 1
eld_sp:		resw 1

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

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


%include "eldlink.asm"

	align 16
code_size equ $ - code
