
; Public Domain

; test run command: list; .; .

%include "lmacros3.mac"

%include "eld.mac"
%include "eldcall.mac"
%include "elddata.mac"
%include "iniload.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 "Work with lDOS style kernel command line."


	align 16, db 0

	eldstrict on

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

reloc	mov dx, msg.kcmdline
internaldatarelocation
	extcallcall isstring?
	je .ours
	pop si
	dec si
	lodsb
	jmp .chain

.ours:
	pop ax
	extcallcall skipcomma
	dec si
reloc2	mov word [relocateddata], relocateddata
linkdatarelocation lastcmd, -4
linkdatarelocation dmycmd
reloc	mov dx, relocateddata
linkdatarelocation msg.uninstall
	extcallcall isstring?
	je uninstall

	call run
	extcallcall cmd3

uninstall:
	lodsb
	extcallcall chkeol

	call get_es_ext

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

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


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


check:
reloc	mov es, word [relocateddata]
linkdatarelocation reg_ss
reloc	mov di, word [relocateddata]
linkdatarelocation reg_ebp		; es:di = original ss:bp
reloc	mov ax, word [relocateddata]
linkdatarelocation reg_esp		; ax = original sp

	cmp di, - lsvCommandLine.start	; buffer fits below ss:bp ?
	jb .none			; no -->
	cmp word [es:di + lsvCommandLine.signature], lsvclSignature
					; signature passed to us ?
	jne .none			; no -->
	lea di, [di + lsvCommandLine.start]
					; -> command line buffer
	cmp ax, di			; stack top starts below-or-equal buffer ?
	ja .none			; no -->
	xor ax, ax
	push cx
	push di
	mov cx, lsvclBufferLength
	repne scasb
	pop di
	pop cx
	jne .overflow
	retn

.overflow:
	mov ax, 0E6Dh
reloc	mov dx, msg.is_overflow
internaldatarelocation
	jmp @F

.none:
	mov ax, 0E6Bh
reloc	mov dx, msg.is_invalid
internaldatarelocation
@@:
	extcallcall setrc
	extcallcall putsz
	extcallcall cmd3


run:
	push ss
	pop es
reloc	mov dx, msg.list
internaldatarelocation
	extcallcall isstring?
	je list
reloc	mov dx, msg.append
internaldatarelocation
	extcallcall isstring?
	je append
reloc	mov dx, msg.replace
internaldatarelocation
	extcallcall isstring?
	je replace
reloc	mov dx, msg.invalid
internaldatarelocation
	extcallcall isstring?
	je invalid
	extcallcall error


invalid:
	extcallcall chkeol
	call check
	and word [es:di - lsvCommandLine.start + lsvCommandLine.signature], 0
	retn


list:
	extcallcall chkeol
	call check
	push es
	pop ds
	mov dx, di
	extcallcall putsz
	push ss
	pop ds
	push ss
	pop es
reloc	mov dx, msg.linebreak
internaldatarelocation
	extcallcall putsz
	retn


append:
houdini
	call get
	call check
	lea bx, [di + 255]	; -> terminator last byte allowed
	xor al, al
	push cx
	mov cx, -1
	repne scasb		; di -> after zero byte
	pop cx
	dec di			; -> at zero byte
	sub bx, cx		; -> last destinstion allowed
	cmp bx, di
	jae replace.common

	mov ax, 0E6Ch
	extcallcall setrc
reloc	mov dx, msg.overflow
internaldatarelocation
	extcallcall putsz
	extcallcall cmd3


get:
	extcallcall skipcomma
	dec si
	mov dx, si
@@:
	lodsb
	test al, al
	jz @F
	cmp al, 13
	jne @B
@@:
	mov cx, si
	dec cx
	sub cx, dx
	mov si, dx
	retn


replace:
	call get
	call check
.common:
	rep movsb
	mov al, 0
	stosb
	retn


	eldcall_dump_callcall ELDCALL_CALLCALL_LIST

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


start:
	mov bx, es
	 push ss
	 pop es
	call skipcomma
	dec si
reloc	mov dx, relocateddata
linkdatarelocation msg.install
	call isstring?
	je install
reloc	mov dx, msg.keyword_help
internaldatarelocation
	call isstring?
	je help
	call run
@@:
	call uninstall_oneshot
	xor ax, ax
	retf

help:
	lodsb
	call chkeol
reloc	mov dx, msg.help
internaldatarelocation
	call putsz
	jmp @B


uninstall_oneshot:
reloc	testopt [ss:relocateddata], 1
linkdatarelocation options7, -3
	jnz @F

reloc	mov ax, word [cs:code + eldiEndCode]
internalcoderelocation
reloc	sub ax, word [cs:code + eldiStartCode]
internalcoderelocation
reloc	sub word [relocateddata], ax
linkdatarelocation extseg_used

reloc	mov ax, word [cs:code + eldiEndData]
internalcoderelocation
reloc	sub ax, word [cs:code + eldiStartData]
internalcoderelocation
reloc	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

msg:
.kcmdline:		asciz "KCMDLINE"
.list:			asciz "LIST"
.replace:		asciz "REPLACE"
.append:		asciz "APPEND"
.invalid:		asciz "INVALID"
.is_invalid:		asciz "Error: No valid kernel command line found!",13,10
.is_overflow:		asciz "Error: Kernel command line terminator not found!",13,10
.overflow:		asciz "Error: Appending would cause an overflow!",13,10
.uninstall_done:	db "KCMDLINE uninstalled."
%if _ELD_RECLAIM_HINT
			db " (Don't forget to use reclaim.eld)"
%endif
.linebreak:		asciz 13,10
.uninstall_error:	asciz "KCMDLINE unable to uninstall!",13,10

uinit_data: equ $
.installed:		asciz "KCMDLINE installed.",13,10
.keyword_help:		asciz "HELP"
.help:		db "This ELD can be installed residently or used transiently.",13,10
		db 13,10
		db "The following commands are recognised:",13,10
		db 9,"LIST",9,9,"List kernel command line contents",13,10
		db 9,"APPEND",9,9,"Append following to kernel command line contents",13,10
		db 9,"REPLACE",9,9,"Replace kernel command line contents",13,10
		db 9,"INVALID",9,9,"Invalidate kernel command line",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

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

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

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


%include "eldlink.asm"

	align 16
code_size equ $ - code
