
; Public Domain

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

%assign _CATCHINT0C 0
%assign _CATCHINT0D 0
%assign _CATCHINTFAULTCOND 0
%assign _LOADER 0
%assign _DEBUG 0
%assign _DELAY_BEFORE_BP 0
%assign _DHIGHLIGHT 0
%assign _GETLINEHIGHLIGHT 0
%assign _IMMASM 0
%assign _INPUT_FILE_BOOT 0
%assign _INPUT_FILE_HANDLES 0
%assign _MS_0RANGE_COMPAT 0
%assign _PM 0
%assign _RH 0
%assign _SYMBOLIC 0
%assign _VXCHG 0
%include "options.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
PUT_ELDHX_DATETIME_OFFSET
header_extension_end:
	iend

description:		asciz "Provide SBRANCH (search memory for branches) command."


	align 16, db 0

	eldstrict on

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

reloc	mov dx, msg.sbranch
internaldatarelocation
	extcallcall isstring?
	je .ours
.transfer_to_chain:
	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
	lodsb
	call sbranch
	extcallcall cmd3

uninstall:
	lodsb
	extcallcall chkeol

reloc	mov es, word [relocateddata]
linkdatarelocation extdssel
	extcallcall ispm
	jz @F
reloc	mov es, word [relocateddata]
linkdatarelocation extseg
@@:
	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


%define relocated(address) relocateddata
%assign ELD 1
%assign _PM 1
%assign _ONLY386 0
%assign _ONLYNON386 0
%assign _SWHILEBUFFER 0
%assign _SCOUNT 1
%assign _SDUMP 1
%assign _SDUMPDISPLACEMENT 1
%assign _SREVERSE 1
%assign _AUXBUFFSIZE 8192 + 16
%define lDEBUG_CODE CODE
%define lDEBUG_DATA_ENTRY DATA
%define dualcall call
	cpu 8086
%assign _WPT_LABELS 0

%include "386.mac"

%if 0
%include "ssshared.asm"
%endif

sbranch:
reloc	mov bx, word [relocateddata]
linkdatarelocation reg_cs
	xor cx, cx
	extcallcall getrangeX
reloc	mov word [startaddress], dx
internaldatarelocation
reloc	mov word [startaddress + 2], bx
internaldatarelocation
	sub cx, dx
	inc cx
reloc	mov word [length], cx
internaldatarelocation
	extcallcall skipcomm0
	extcallcall getword
reloc	mov word [target], dx
internaldatarelocation
	extcallcall chkeol

reloc	mov di, relocated(search_results)
linkdatarelocation search_results
	push cx
reloc	mov cx, relocated(search_results_amount)
linkdatarelocation search_results_amount
reloc	mov ax, relocated(0)
linkdatarelocation build_option_PM
	test ax, ax
	jz @F
	mov ax, cx
@@:
	add cx, cx		; times 2
	add cx, ax		; PM: times 3, else: times 2
	xor ax, ax
reloc	mov word [relocated(sscounter)], ax
linkdatarelocation sscounter
reloc	mov word [relocated(sscounter) + 2], ax
linkdatarelocation sscounter
	rep stosw
	pop cx

reloc	mov di, [startaddress]
internaldatarelocation
.loop:
reloc	mov es, [startaddress + 2]
internaldatarelocation
	dec cx
	cmp byte [es:di], 0EBh
	je .check1
	cmp byte [es:di], 070h
	jb @F
	cmp byte [es:di], 07Fh
	jbe .check1
@@:
	cmp byte [es:di], 0E0h
	jb @F
	cmp byte [es:di], 0E3h
	jbe .check1
@@:
	cmp byte [es:di], 0E9h
	je .check2
	cmp byte [es:di], 0E8h
	je .check2
.next:
	inc di
	jcxz .done
	jmp .loop

.done:
	push ss
	pop es
reloc	mov di, relocated(line_out)
linkdatarelocation line_out

reloc	mov ax, word [relocated(sscounter) + 2]
linkdatarelocation sscounter
	test ax, ax
	jz .nohighcounter
	extcallcall hexword
.nohighcounter:

reloc	mov ax, word [relocated(sscounter)]
linkdatarelocation sscounter
	extcallcall hexword
	extcallcall putsline
reloc	mov dx, msg.matches
internaldatarelocation
	extcallcall putsz
	retn


.check1:
	mov al, [es:di + 1]
	cbw
	xchg bx, ax
	lea ax, [di + 2 + bx]
	jmp .common

.check2:
	mov bl, [es:di + 1]
	mov bh, [es:di + 2]
	lea ax, [di + 3 + bx]
.common:
reloc	cmp word [target], ax
internaldatarelocation
	jne .next
	push cx
	push es
	push di
	 push ss
	 pop es
reloc	cmp word [relocated(sscounter) + 2], strict byte 0
linkdatarelocation sscounter, -3
	jne @F
reloc	mov di, word [relocated(sscounter)]
linkdatarelocation sscounter
reloc	cmp di, strict word relocated(search_results_amount)
linkdatarelocation search_results_amount
	jae @F
reloc	mov ax, relocated(0)
linkdatarelocation build_option_PM
	add di, di		; times 2
	test ax, ax		; PM build ?
	jz .idxnotpm		; no --> (ax == zero)
	mov ax, di		; = times 2
.idxnotpm:
	add di, di		; times 4
	add di, ax		; _PM: times 4 + times 2 = times 6, else: times 4
reloc	add di, strict word relocated(search_results)
linkdatarelocation search_results

	pop ax
	push ax
	stosw
reloc	mov ax, relocated(0)
linkdatarelocation build_option_PM
	test ax, ax
	jz .storenotpm
	xor ax, ax
	stosw
.storenotpm:
reloc	mov ax, word [startaddress + 2]
internaldatarelocation
	stosw

@@:
reloc	add word [relocated(sscounter)], strict byte 1
linkdatarelocation sscounter, -3
reloc	adc word [relocated(sscounter) + 2], strict byte 0
linkdatarelocation sscounter, -3

reloc	mov ax, word [startaddress + 2]
internaldatarelocation
reloc	mov di, relocated(line_out)
linkdatarelocation line_out
	extcallcall hexword	; 4 (segment)
	mov al, ':'
	stosb			; +1=5
	pop ax
	 pop ds
	 push ds		; restore search's segment
	push ax
	extcallcall hexword

reloc	testopt [ss:relocated(options)], ss_no_dump
linkdatarelocation options, -3
	jnz .no_dump
	mov si, ax
	mov ax, 32<<8|32
	stosw
	lea bx, [di+3*16]

	mov cx, si
	neg cx
	jcxz .16
	cmp cx, byte 16
	jbe .cxdone
.16:
	mov cx, 16
.cxdone:			; cx cannot be zero
	push cx
.disploop:
	lodsb
	extcallcall dd_store
	mov al, 32
	stosb
	loop .disploop
	pop cx
	 push ss
	 pop ds
	neg cx
	add cx, byte 16
	jz .noblanks
.loopblanks:
	mov ax, 32<<8|32
	stosw
	stosb
	loop .loopblanks
.noblanks:
	mov byte [di-(1+(8*3))], '-'
	mov di, bx
.noresult:
.no_dump:
	extcallcall putsline_crlf
	pop di
	pop es
	pop cx

	jmp .next


error:
	extcall error

	eldcall_dump_callcall ELDCALL_CALLCALL_LIST

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

	align 2, db 0				; align on word boundary
		; Table of patches that are to be set NOP if not running on a 386.
writepatchtable patch_no386_table, PATCH_NO386_TABLE
%undef PATCH_NO386_TABLE

	align 2, db 0
		; Table of patches that are to be set NOP if running on a 386.
writepatchtable patch_386_table, PATCH_386_TABLE
%undef PATCH_386_TABLE


start:
	houdini
	push si
reloc	mov dx, code
internalcoderelocation
reloc	testopt [relocateddata], has386
linkdatarelocation internalflags, -3
	jz @F
reloc	mov si, patch_386_table		; table of patches to set for 386+
internalcoderelocation
%if __patch_386_table_method == 1
	mov cx, patch_386_table_size_w
	call .patch1
%else
	call .patch2
%endif
	jmp .patch_code1_end

@@:
reloc	mov si, patch_no386_table	; table of patches to set for 16-bit CPU
internalcoderelocation
%if __patch_no386_table_method == 1
	mov cx, patch_no386_table_size_w
	call .patch1
%else
	call .patch2
%endif
	jmp .patch_code1_end

		; Complicated table patch code.
%if __patch_no386_table_method == 2 || __patch_386_table_method == 2
.patch2:
reloc	mov di, code_start		; initialise offset
internalcoderelocation
	xor ax, ax			; initialise ah
.looppatch2:
	cs lodsb
	add di, ax			; skip number of bytes to skip
	cmp al, 255			; really repositioning?
	jne .l2patch			; no -->
	xchg ax, di			; (to preserve ah)
	cs lodsw			; ax = new address
	add ax, dx
	xchg ax, di			; di = new address
.l2patch:
	cs lodsb
	mov cx, ax			; cx = number of bytes to patch
	jcxz .patchesdone		; end of table -->
	mov al, 90h			; patch to NOP
	rep stosb			; patch as many bytes as specified
	jmp short .looppatch2
%endif

		; Simple table patch code.
%if __patch_no386_table_method == 1 || __patch_386_table_method == 1
.patch1:
	jcxz .patchesdone
.looppatch1:
	cs lodsw			; load address of patch
	add ax, dx
	xchg bx, ax			; (set bx = ax, CHG ax)
	mov byte [es:bx], 90h		; patch
	loop .looppatch1
%endif
.patchesdone:
	retn

.patch_code1_end:
	pop si

	mov bx, es
	 push ss
	 pop es

	call skipcomma
	dec si
reloc	mov dx, relocateddata
linkdatarelocation msg.install
	call isstring?
	je install
	lodsb
	call sbranch
@@:
	call uninstall_oneshot
	xor ax, ax
	retf


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


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


	usesection DATA
msg:
.sbranch:		asciz "SBRANCH"
.matches:		asciz " matches",13,10
.uninstall_done:	db "SBRANCH command uninstalled."
%if _ELD_RECLAIM_HINT
			db " (Don't forget to use reclaim.eld)"
%endif
			asciz 13,10
.uninstall_error:	asciz "SBRANCH command unable to uninstall!",13,10

uinit_data: equ $

.installed:	asciz "SBRANCH command installed.",13,10

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

	absolute uinit_data

	alignb 2
startaddress:		resd 1
length:			resw 1
target:			resw 1

	alignb 16
uinit_data_end:
resident_data_end:

%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

%include "eldlink.asm"

	align 16
code_size equ $ - code
