
; Public Domain

; 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
header_extension_end:
	iend

description:		asciz "Compare ELDs with differing linker options."


	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 "ELDCOMP"
	iend

start:
	push ss
	pop es
	extcallcall skipcomma
	dec si
	mov bx, commands
internaldatarelocation
	mov al, ';'
	mov di, word [bx]
.loop:
	inc di
	cmp al, ';'
	jne @FF
@@:
	lodsb
	extcallcall iseol?
	je @F
	stosb
	jmp @B

@@:
	mov al, 13
	stosb
	mov cx, di
	sub cx, word [bx]
	dec cx
	dec cx
	mov di, word [bx]
	mov byte [di], cl
	dec si
	lodsb
	inc bx
	inc bx
	mov di, word [bx]
	test di, di
	jnz .loop


run:
	call clear_eld_space
	mov word [data_original], bx	; data
internaldatarelocation
	mov word [code_original], dx	; code
internaldatarelocation

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

	push word [relocateddata]
linkdatarelocation ext_inject_handler
	push word [relocateddata]
linkdatarelocation savesp
	push word [relocateddata]
linkdatarelocation throwsp
	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
	extcallcall cmd3

.inject:
	mov bx, [step]
internaldatarelocation
	cmp bx, 4
	je .checksum
	cmp bx, 8 + 4
	jne @F
.checksum:
	mov si, word [data_original]
internaldatarelocation
	mov cx, word [relocateddata]
linkdatarelocation extdata_used
	add cx, word [relocateddata]
linkdatarelocation extdata
	sub cx, si
	mov word [word results + 0 + bx - 4], cx
internaldatarelocation
	xchg bx, dx
	push si
	push cx
	call zz_hash
	pop ax
	pop si
	xchg bx, dx
	mov word [word results + 2 + bx - 4], dx
internaldatarelocation
	mov cx, 1
	call [savedata_dispatch]
internaldatarelocation

	mov si, word [code_original]
internaldatarelocation
	mov cx, word [relocateddata]
linkdatarelocation extseg_used
	sub cx, si
	mov word [word results + 4 + bx - 4], cx
internaldatarelocation
	call get_es_ext
	 push es
	 pop ds
	xchg bx, dx
	push si
	push cx
	call zz_hash
	pop ax
	pop si
	xchg bx, dx
	mov word [ss:word results + 6 + bx - 4], dx
internaldatarelocation
	 push ss
	 pop es
	mov cx, 1
	call [ss:savecode_dispatch]
internaldatarelocation
	 push ss
	 pop ds

@@:
	cmp bx, 8
	jne @F

	push bx
	call clear_eld_space
	 push ss
	 pop es
	pop bx
@@:

	add word [step], strict byte 2
internaldatarelocation -3
	mov di, relocateddata + 1
linkdatarelocation line_in
	mov si, [word bx + commands]
internaldatarelocation
	test si, si
	jz .finish
	mov word [relocateddata], .inject
linkdatarelocation ext_inject_handler, -4
internalcoderelocation

.common:
	push di
	mov cx, words(256)
	rep movsw
	pop si
	inc si
	extcall skipwhite

	extcall cmd3_injected

.finish:
%if 0
	mov si, reclaimcommand
	mov word [relocateddata], .return
linkdatarelocation ext_inject_handler, -4
internalcoderelocation
	jmp .common
%elif 0
	mov word [relocateddata], .return
linkdatarelocation ext_inject_handler, -4
internalcoderelocation
	extcall cmd3
%endif

.return:
	mov sp, word [eld_sp]
internaldatarelocation
	pop word [relocateddata]
linkdatarelocation throwsp
	pop word [relocateddata]
linkdatarelocation savesp
	pop word [relocateddata]
linkdatarelocation ext_inject_handler
	call get_es_ext
	clropt [es:code + eldiFlags], eldifResident
internalcoderelocation -3		; mark block as free
	 push ss
	 pop es

	mov si, results
internaldatarelocation
	mov bx, 2
@@:
	mov cx, 4
	mov di, relocateddata
linkdatarelocation line_out
@@:
	lodsw
	extcallcall hexword
	mov al, 32
	stosb
	loop @B
	push bx
	extcallcall trimputs
	pop bx
	dec bx
	jnz @BB

	mov si, results
internaldatarelocation
	mov di, results + 8
internaldatarelocation
	mov ax, [si]
	cmpsw
	je @FF
	ja @F
	mov ax, [di - 2]
@@:
	mov word [savedata_dispatch], savedata
internaldatarelocation -4
internalcoderelocation
	mov dx, msg.mismatch.datalen
internaldatarelocation
	extcallcall putsz
@@:
	cmpsw
	je @F
	mov word [savedata_dispatch], savedata
internaldatarelocation -4
internalcoderelocation
	mov dx, msg.mismatch.datahash
internaldatarelocation
	extcallcall putsz
@@:
	mov word [savedata_length], ax
internaldatarelocation

	mov ax, [si]
	cmpsw
	je @FF
	ja @F
	mov ax, [di - 2]
@@:
	mov word [savecode_dispatch], savecode
internaldatarelocation -4
internalcoderelocation
	mov dx, msg.mismatch.codelen
internaldatarelocation
	extcallcall putsz
@@:
	cmpsw
	je @F
	mov word [savecode_dispatch], savecode
internaldatarelocation -4
internalcoderelocation
	mov dx, msg.mismatch.codehash
internaldatarelocation
	extcallcall putsz
@@:
	mov word [savecode_length], ax
internaldatarelocation


rerun:
	rol byte [rerunning], 1
internaldatarelocation
	jc compare

	xor cx, cx			; cx = 0, get sizes
	xor ax, ax			; (NC)
	call near [savedata_dispatch]
internaldatarelocation
	call near [savecode_dispatch]
internaldatarelocation
	jnc end

	not byte [rerunning]
internaldatarelocation

	mov bx, ax
	add bx, word [savedata_length]
internaldatarelocation
	mov dx, word [cs:code + eldiEndData]
internalcoderelocation
	sub dx, word [relocateddata]
linkdatarelocation extdata
	cmp dx, word [relocateddata]
linkdatarelocation extdata_used
	je @F
	mov dx, msg.notcontiguous
internaldatarelocation
	extcallcall putsz
	jmp end

@@:
	neg dx
	add dx, word [relocateddata]
linkdatarelocation extdata_size
	cmp bx, dx
	jbe @F
	mov dx, msg.notenough
internaldatarelocation
	extcallcall putsz
	jmp end

@@:
	call get_es_ext
	mov di, word [es:code + eldiEndData]
internalcoderelocation
	add word [es:code + eldiEndData], ax
internalcoderelocation
	add word [relocateddata], ax
linkdatarelocation extdata_used
	 push ss
	 pop es
	push di
	xchg cx, ax
	mov al, 0
	rep stosb		; clear our buffers
	pop ax			; ax -> buffers

		; cx = 0
	call near [savedata_dispatch]
internaldatarelocation
	call near [savecode_dispatch]
internaldatarelocation		; find offsets for each used buffer

	and word [step], strict byte 0
internaldatarelocation -3
	jmp run


compare:
	mov cx, 2
	call near [savedata_dispatch]
internaldatarelocation
	mov cx, 2
	call near [savecode_dispatch]
internaldatarelocation		; compare buffers
	jmp end


empty:
	retn


		; INP:	ax -> buffer 1
		;	bx -> buffer 2
		;	cx = length
		;	dx -> message
commoncompare:
	lframe none
	lenter
	lvar word, buffer1
	 push ax
	lvar word, buffer2
	 push bx
	lvar word, length
	 push cx
	extcallcall putsz
	mov di, relocateddata
linkdatarelocation line_out
	call hexword
	mov si, msg.compare.1
internaldatarelocation
	extcallcall copy_single_counted_string
	mov ax, word [bp + ?buffer2]
	call hexword
	mov si, msg.compare.2
internaldatarelocation
	extcallcall copy_single_counted_string
	mov ax, word [bp + ?length]
	call hexword
	extcallcall putsline_crlf

	mov di, comparebuffer + 1
internaldatarelocation
	mov si, msg.compare.3
internaldatarelocation
	extcallcall copy_single_counted_string
	mov ax, ss
	call hexword
	mov al, ':'
	stosb
	mov ax, word [bp + ?buffer1]
	call hexword
	mov si, msg.compare.4
internaldatarelocation
	extcallcall copy_single_counted_string
	mov ax, word [bp + ?length]
	call hexword
	mov al, 32
	stosb
	mov ax, ss
	call hexword
	mov al, ':'
	stosb
	mov ax, word [bp + ?buffer2]
	call hexword
	mov al, 13
	stosb
	xchg ax, di
	sub ax, comparebuffer + 2
internaldatarelocation
	mov byte [comparebuffer], al
internaldatarelocation

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

	lvar word, injecthandler
	push word [relocateddata]
linkdatarelocation ext_inject_handler
	lvar word, putshandler
	push word [relocateddata]
linkdatarelocation ext_puts_handler
	lvar word, savesp
	push word [relocateddata]
linkdatarelocation savesp
	lvar word, throwsp
	push word [relocateddata]
linkdatarelocation throwsp
	lvar word, bp
	push bp
	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
	extcallcall cmd3

.inject:
	mov si, comparebuffer
internaldatarelocation
	mov di, relocateddata + 1
linkdatarelocation line_in

	push di
	mov cx, words(256)
	rep movsw
	pop si
	inc si
	extcall skipwhite

	mov word [relocateddata], puts_handler
linkdatarelocation ext_puts_handler, -4
internalcoderelocation

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

.finish:
	mov sp, word [eld_sp]
internaldatarelocation
	pop bp
	pop word [relocateddata]
linkdatarelocation throwsp
	pop word [relocateddata]
linkdatarelocation savesp
	pop word [relocateddata]
linkdatarelocation ext_puts_handler
	pop word [relocateddata]
linkdatarelocation ext_inject_handler
	call get_es_ext
	clropt [es:code + eldiFlags], eldifResident
internalcoderelocation -3		; mark block as free
	 push ss
	 pop es
	lleave code
	retn


puts_handler:
	push si
	push di
	push ax
	push es
	push dx
	push bp
	mov bp, word [eld_sp]
internaldatarelocation
	mov bp, [bp]
	push word [bp + ?buffer1]
	push word [bp + ?buffer2]
	lframe 0, nested
	lpar word, messagelength
	lpar word, messagesegment
	lpar word, messageoffset
	lpar word, bp
	lpar word, buffer1
	lpar word, buffer2
	lenter

	cmp ax, 4 + 1 + 4 + 2 + 2 + 2 + 2 + 2 + 4 + 1 + 4 + 2
	je @F	;seg col ofs bl dig bl dig bl seg col ofs eol
	cmp ax, 4 + 1 + 8 + 2 + 2 + 2 + 2 + 2 + 4 + 1 + 8 + 2
	jne .not;seg col ofs bl dig bl dig bl seg col ofs eol
@@:

	mov di, putsbuffer
internaldatarelocation
	push di
	mov ax, ss
	 push ss
	 pop es
	extcallcall hexword
	mov al, ':'
	stosb
	pop si			; ds:si -> buffer
	mov cx, 5
	mov di, dx		; di -> puts input
	mov es, word [bp + ?messagesegment]
				; es:di -> input
	repe cmpsb
	jne .not		; mismatch
	cmp word [es:di], "00"	; 32-bit offset prefix ?
	jne @F
	cmp word [es:di + 2], "00"
	jne @F			; no -->
	add di, 4		; yes, skip it
@@:
	xchg si, di
	push es
	push ds
	pop es
	pop ds			; ds:si -> input offset left, es => ss
	mov cx, 4
	xor bx, bx
	mov di, putsbuffer	; es:di -> buffer
internaldatarelocation
@@:
	lodsb
	extcallcall getnyb
	jc .not
	add bx, bx
	add bx, bx
	add bx, bx
	add bx, bx		; times 16
	mov ah, 0
	add bx, ax		; add in next hexit
	loop @B			; process 4 hexits -->
	sub bx, word [bp + ?buffer1]
	xchg ax, bx
	extcallcall hexword	; write offset adjusted
	mov ax, 2020h
	stosw			; blanks

	mov si, word [bp + ?messageoffset]
				; ds:si -> message
	mov cx, word [bp + ?messagelength]
				; cx = length (harden against overflow)
	db __TEST_IMM8		; (skip stosb)
@@:
	stosb
	lodsb			; load next byte
	cmp al, 13		; end of line ?
	loopne @B		; not yet, store it -->
	sub si, 5		; 1 for CR, 4 for offset (low) word as hex
	mov ax, 2020h
	stosw			; more blanks
	mov cx, 4
	xor bx, bx
@@:
	lodsb
	extcallcall getnyb
	jc .not
	add bx, bx
	add bx, bx
	add bx, bx
	add bx, bx		; times 16
	mov ah, 0
	add bx, ax		; add in next hexit
	loop @B			; process 4 hexits -->
	sub bx, word [bp + ?buffer2]
	xchg ax, bx
	extcallcall hexword	; write offset adjusted

	mov ax, 13 | 10 << 8
	stosw
	mov cx, di		; cx -> behind new line
	mov dx, putsbuffer	; es:dx -> buffer
internaldatarelocation
	sub cx, dx		; cx = length
	push ss
	pop ds			; ds = ss
	extcallcall puts	; (will recurse, length check will fail)
	stc			; do not display
	jmp .leave

.not:
	clc
.leave:
	lleave
	pop ax
	pop ax			; (discard)
	 push ss
	 pop ds			; reset ds
	pop bp
	pop dx
	pop es
	pop ax
	pop di
	pop si
	extcall puts_ext_done, required

	lleave ctx


savedata:
	jcxz .size_and_offsets
	loop .compare

.write:
	mov di, [savedata_offset_1]
internaldatarelocation
	cmp bx, 4
	je @F
	mov di, [savedata_offset_2]
internaldatarelocation
@@:
	xchg cx, ax
	cmp cx, word [savedata_length]
internaldatarelocation
	jbe @F
	mov cx, word [savedata_length]
internaldatarelocation
@@:
	rep movsb
	retn

.size_and_offsets:
	mov word [savedata_offset_1], ax
internaldatarelocation
	add ax, word [savedata_length]
internaldatarelocation
	jc overflow
	mov word [savedata_offset_2], ax
internaldatarelocation
	add ax, word [savedata_length]
internaldatarelocation
	jc overflow
	stc
	retn

.compare:
	mov ax, word [savedata_offset_1]
internaldatarelocation
	mov bx, word [savedata_offset_2]
internaldatarelocation
	mov cx, word [savedata_length]
internaldatarelocation
	mov dx, msg.data
internaldatarelocation
	jmp commoncompare


savecode:
	jcxz .size_and_offsets
	loop .compare

		; NB ds != ss
.write:
	mov di, [ss:savecode_offset_1]
internaldatarelocation
	cmp bx, 4
	je @F
	mov di, [ss:savecode_offset_2]
internaldatarelocation
@@:
	xchg cx, ax
	cmp cx, word [ss:savecode_length]
internaldatarelocation
	jbe @F
	mov cx, word [ss:savecode_length]
internaldatarelocation
@@:
	rep movsb
	retn

.size_and_offsets:
	mov word [savecode_offset_1], ax
internaldatarelocation
	add ax, word [savecode_length]
internaldatarelocation
	jc overflow
	mov word [savecode_offset_2], ax
internaldatarelocation
	add ax, word [savecode_length]
internaldatarelocation
	jc overflow
	stc
	retn

.compare:
	mov ax, word [savecode_offset_1]
internaldatarelocation
	mov bx, word [savecode_offset_2]
internaldatarelocation
	mov cx, word [savecode_length]
internaldatarelocation
	mov dx, msg.code
internaldatarelocation
	jmp commoncompare


overflow:
	mov dx, msg.overflow
internaldatarelocation
	extcallcall putsz

end:
	testopt [ss:relocateddata], 1
linkdatarelocation options7, -3
	jnz .skip

	call get_es_ext
	mov ax, word [cs:code + eldiEndData]
internalcoderelocation			; ax -> our data end
	mov bx, 32			; prepare to keep 32 Bytes
	push ax
	sub ax, [relocateddata]		; ax = index into data behind our end
linkdatarelocation extdata
	cmp word [relocateddata], ax	; are we last ?
linkdatarelocation extdata_used
	pop ax
	jne @F				; no, keep >= 32 Bytes of instance -->
	xor bx, bx			; remember to free instance completely
	sub ax, word [cs:code + eldiStartData]
internalcoderelocation			; get our length
	sub word [relocateddata], ax	; subtract from use counter
linkdatarelocation extdata_used
	xor ax, ax			; zero out our data references
					;  (in case we keep the instance)
	mov word [es:code + eldiStartData], ax
internalcoderelocation
	mov word [es:code + eldiEndData], ax
internalcoderelocation
@@:

	mov dx, word [cs:code + eldiStartCode]
internalcoderelocation
	add dx, bx			; -> behind to keep (= start if no data)
	mov ax, word [cs:code + eldiEndCode]
internalcoderelocation
	cmp word [relocateddata], ax	; at end ?
linkdatarelocation extseg_used
	jne @F				; no, keep block as is -->
	mov word [es:code + eldiEndCode], dx
internalcoderelocation			; modify end to -> behind to keep
	sub ax, word [cs:code + eldiStartCode]
internalcoderelocation			; = original length
	sub ax, bx			; = how much to free
	sub word [relocateddata], ax	; subtract from use counter
linkdatarelocation extseg_used
@@:

.skip:
	extcallcall cmd3


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


clear_eld_space:
	 push ss
	 pop es
	mov di, word [relocateddata]
linkdatarelocation extdata_used
	mov cx, word [relocateddata]
linkdatarelocation extdata_size
	sub cx, di
	add di, word [relocateddata]
linkdatarelocation extdata
	mov bx, di
	mov al, 0
	rep stosb

	call get_es_ext
	mov di, word [relocateddata]
linkdatarelocation extseg_used
	mov cx, word [relocateddata]
linkdatarelocation extseg_size
	sub cx, di
	mov dx, di
	mov al, 0
	rep stosb
	retn


		; INP:	ds:si -> string to hash
		;	cx = length of string (may be zero)
		; OUT:	bx = hash value computed
		; CHG:	ax, si, cx
		; STT:	UP
zz_hash:
	mov bx, 1

		; INP:	ds:si -> string to hash
		;	cx = length of string (may be zero)
		;	bx = initial hash value
		; OUT:	bx = hash value computed
		; CHG:	ax, si, cx
		; STT:	UP
.bx_init:
	jcxz .end
.loop:
	xor ax, ax
	lodsb
	sub ax, bx
	push cx
	mov cl, 5
	shl bx, cl
	pop cx
	add bx, ax
	loop .loop
.end:
	retn


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

msg:
.mismatch.datalen:	asciz "Data length mismatch",13,10
.mismatch.datahash:	asciz "Data hash mismatch",13,10
.mismatch.codelen:	asciz "Code length mismatch",13,10
.mismatch.codehash:	asciz "Code hash mismatch",13,10

.notenough:	asciz "Not enough ELD data space left!",13,10
.overflow:	asciz "Overflow!",13,10
.notcontiguous:	asciz "Free data space is not contiguous!",13,10
.code:		asciz "Comparing code, left="
.data:		asciz "Comparing data, left="
.compare.1:	counted " right="
.compare.2:	counted " length="
.compare.3:	counted "c "
.compare.4:	counted " l "

rerunning:		db 0

	align 2, db 0
results:
	times 8 dw 0

savedata_dispatch:	dw empty
internalcoderelocation
savedata_length:	dw 0
savedata_offset_1:	dw 0
savedata_offset_2:	dw 0

savecode_dispatch:	dw empty
internalcoderelocation
savecode_length:	dw 0
savecode_offset_1:	dw 0
savecode_offset_2:	dw 0

	align 2, db 0
commands:
	dw install_command_1
internaldatarelocation
	dw status_command_1
internaldatarelocation
	dw uninstall_command_1
internaldatarelocation
	dw reclaim_command_1
internaldatarelocation

	dw install_command_2
internaldatarelocation
	dw status_command_2
internaldatarelocation
	dw uninstall_command_2
internaldatarelocation
	dw reclaim_command_2
internaldatarelocation
	dw 0

step:	dw 0

%if 0
reclaimcommand:
	counted "ext reclaim.eld"
	db 13
%endif

uinit_data:

	align 16, db 0
data_size equ $ - datastart

	absolute uinit_data
	alignb 2
eld_sp:
	resw 1
code_original:
	resw 1
data_original:
	resw 1

comparebuffer:
	resb 64

putsbuffer:
install_command_1:
	resb 256
status_command_1:
	resb 256
uninstall_command_1:
	resb 256
reclaim_command_1:
	resb 256
install_command_2:
	resb 256
status_command_2:
	resb 256
uninstall_command_2:
	resb 256
reclaim_command_2:
	resb 256

	alignb 16
uinit_data_end:

total_data_size equ $ - datastart
%assign _DATA_SIZE total_data_size

	usesection CODE

%include "eldlink.asm"

	align 16
code_size equ $ - code
