
%if 0

lDOS DOSCODE entrypoint handling
 by E. C. Masloch, 2018--2025

Usage of the works is permitted provided that this
instrument is retained with the works, so that any entity
that uses the works is notified of this instrument.

DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.

%endif

usesection DOSCODETABLE
 %if _RELOCATEDOSCODE
global doscode_start
doscode_start:					; must be start of segment

	times DOSCODE_HMA_OFFSET - 16 db 38	; prefix that is unused,
						;  and not relocated
 extern afterdoscodelabel
 global doscode_mcb
doscode_mcb:
	istruc MCB
at mcbSignature,db 'M'
at mcbOwner,	dw 8
at mcbSize
 wlcalc word_shr_4?doscodemcb
		dw afterdoscodelabel - DOSCODE_HMA_OFFSET wrt DOSCODEGROUP
at mcbName,	db "S"
at smcbType,	db S_DOSCODE
	iend
 %endif

		; The following table and function is used by devicerelocate
		;  even if DOSCODE is merged into DOSENTRY (!_RELOCATEDOSCODE).

	write_entrypoint_list ENTRYPOINT_LIST

relocated devprnentry, device
 assume ds:nothing
	push ds
	call biocode_get_ds_dosbiodata
 assume ds:DOSGROUP
 extern PRINTDEV
	MOV	[PRINTDEV],AH		;SAVE INDEX INTO ARRAY OF RETRY COUNTS
	pop ds
 assume ds:nothing

relocated deventry, device
 assume ds:nothing
	jmp common_dev_entrypoint

		; ! all dev entries must be before this point,
		;  and there must be no non-dev entries.
		; This is required by the dev entry check in
		;  relocated_relocatedentry in this file.

%if _RELOCATEDOSCODE && _DOSCODEHMA
	_fill 0C0h + DOSCODE_HMA_start_at, 90h, doscode_start
hma_call5:
 assume ds:nothing, es:nothing, ss:nothing
	jmp DOSENTRY:call5
%endif


	numdef DOSCODE_ENTRY_TRAMPOLINE, 1

		; INP:	byte [cs:ip] = displacement in doscode_entrypoint_list
		;	word [DOSCODE:(displaced)]
		;	 = relocated entry address
		;	word [DOSCODE:(displaced + 2)]
		;	 = optional pointer to device function table
		;	byte [DOSCODE:(displaced + 4)]
		;	 = optional device unit
		;	ss:(sp + 6) -> arbitrary stack of entrypoint
		;	word [ss:sp + 4] = entrypoint's ip,
		;	word [ss:sp + 2] = entrypoint's cs,
		;	word [ss:sp + 0] = original ax value
		;	ax = original bp value
		; OUT:	For non-device entrypoint,
		;	 ss:sp -> arbitrary stack
		;	For device entrypoint,
		;	 ss:sp -> ax, si, arbitrary stack
		;	 al = unit if any
		;	 si -> device table
relocated_relocatedentry:
 assume ds:nothing, es:nothing, ss:nothing
%if _DOSCODE_ENTRY_TRAMPOLINE
	call .handle
	retn

.handle:
%endif
	xchg ax, bp			; restore bp, clobber ax
	 pushf
	lframe 0
	lpar word, entrypoint_ip
	lpar word, entrypoint_cs
	lpar word, orig_ax
%if _DOSCODE_ENTRY_TRAMPOLINE
	lpar word, trampoline_ip
%endif
	lpar word, orig_fl
	lenter
	 lequ ?entrypoint_ip, out_nondev_ip
	 lequ ?entrypoint_ip, out_dev_si
	 lequ ?entrypoint_cs, out_dev_ax
	 lequ ?orig_ax, out_dev_ip
	 lequ ?orig_ax, out_nontrampoline_ip
	push ds
	push si
	push word [bp + ?orig_ax]

	mov ah, 0
	mov ds, word [bp + ?entrypoint_cs]
 assume ds:nothing
	mov si, word [bp + ?entrypoint_ip]
%if 0
	cld
	inc si
	lodsb
	cmp al, 0CCh	; overwritten by debugger ?
	jne @F		; no -->
	int3		; break to make it remove the breakpoint
	dec si
	lodsb		; reload the byte
	cmp al, 0CCh
	je .error
%else
	mov al, [si + 1]
%endif

@@:
	push cs
	pop ds
 assume ds:DOSCODEGROUP

	add ax, doscode_entrypoint_list
	cmp ax, doscode_entrypoint_list.end
	jae .error
	pop word [bp + ?out_dev_ax]
	pop word [bp + ?out_dev_si]
	xchg ax, si

	lodsw		; get relocated entry address
	cmp ax, relocateddeventry
	jbe .dev	; ! depends on deventry and devprnentry early
.nondev:
%if _DOSCODE_ENTRY_TRAMPOLINE
	mov si, word [bp + ?out_dev_ax]
	xchg si, ax			; ax from ?out_dev_ax, si -> destination
	xchg si, word [bp + ?out_nondev_ip]
					; ?out_nondev_ip -> ip, si = original si
%else
	mov si, word [bp + ?out_nondev_ip]
	xchg ax, word [bp + ?out_nontrampoline_ip]
%endif
	pop ds
 assume ds:nothing
	lleave code
	popf
	retn 4

.dev:
%if _DOSCODE_ENTRY_TRAMPOLINE
	mov word [bp + ?out_dev_ip], ax
%else
	mov word [bp + ?out_nontrampoline_ip], ax
%endif
	lodsw
	 push ax			; -> table
	lodsw				; ax = unit things if any
	 pop si				; si -> table
	pop ds
 assume ds:nothing
	lleave code
	popf
	retn

	lleave ctx

.error:
	cld
	mov si, doscode_msg.error_common	; access with cs
	call doscode_disp_msg_cs
	mov si, doscode_msg.error_table		; access with cs
	xor ax, ax				; always halt
@@:
	call doscode_halt_query
	mov si, doscode_msg.crlf
	jmp @B

doscode_halt_query:
	push ds
	push ax
	call doscode_getdosdata
	mov ds, ax				; => DOSDATA
 assume ds:DOSGROUP
 extern haltflags
	pop ax
	test word [haltflags], ax		; check halt flags
	pop ds
	jz .halt
	call doscode_disp_msg_cs
	mov si, doscode_msg.crlf
	call doscode_disp_msg_cs
	int3
	xchg bx, bx				; debugging hint
	retn

.return:
	mov si, doscode_msg.crlf
	call doscode_disp_msg_cs
	retn

.halt:
	call doscode_disp_msg_cs
	mov si, doscode_msg.error_after		; access with cs
	call doscode_disp_msg_cs
.loop:
	clc
	int3					; invoke debugger
	jc .return
	xor ax, ax
	int 16h
	jmp .loop



doscode_disp_msg_cs:
 assume ds:nothing, es:nothing, ss:nothing
	push ax
@@:
	cs lodsb
	test al, al
	jz @F
	call doscode_disp_al
	jmp short @B


doscode_disp_blank:
	mov al, 32
doscode_disp_al:
 assume ds:nothing, es:nothing, ss:nothing
	push ax
	push bx
	push bp
	mov ah, 0Eh
	mov bx, 7
	int 10h
	pop bp
	pop bx
@@:
	pop ax
	retn


doscode_disp_dxax_hex:	; dx:ax
 assume ds:nothing, es:nothing, ss:nothing
		xchg ax, dx
		call doscode_disp_ax_hex
		xchg ax, dx
doscode_disp_ax_hex:	; ax
 assume ds:nothing, es:nothing, ss:nothing
		xchg al, ah
		call doscode_disp_al_hex
				; display former ah
		xchg al, ah	;  and fall trough for al
doscode_disp_al_hex:	; al
 assume ds:nothing, es:nothing, ss:nothing
		push cx
		mov cl, 4
		ror al, cl
		call doscode_disp_al_lownibble_hex
				; display former high-nibble
		rol al, cl
		pop cx
				;  and fall trough for low-nibble
doscode_disp_al_lownibble_hex:
 assume ds:nothing, es:nothing, ss:nothing
		push ax		; save ax for call return
		and al, 00001111b
				; high nibble must be zero
		add al, '0'	; if number is 0-9, now it's the correct character
		cmp al, '9'
		jna .decimalnum	; if we get decimal number with this, ok -->
		add al, 7	;  otherwise, add 7 and we are inside our alphabet
 .decimalnum:
		call doscode_disp_al
		pop ax
		retn


relocated shellreturned
 assume ds:nothing, es:nothing, ss:nothing
	sti
	cld
	jnc .returned
	mov si, doscode_msg.error_shell_failed.1	; access with cs
	call doscode_disp_msg_cs
	call doscode_disp_ax_hex
	mov si, doscode_msg.error_shell_failed.2	; access with cs
	jmp @F
.returned:
	xor ax, ax
	mov si, doscode_msg.error_shell_returned	; access with cs
@@:
 extern doscode_getdosdata
 extern shell_return_has_been_freed
 extern shell_return_address
	push ax
	call doscode_getdosdata
	mov ds, ax
 assume ds:DOSGROUP
	pop ax
	rol byte [shell_return_has_been_freed], 1
	jc .halt
	call doscode_disp_msg_cs
		; INP:	ax = return error value (0 = no error)
		;	ds => DOSDATA
		; STT:	ss => init stack block
	jmp far [shell_return_address]

.halt:
	push ax
	xor ax, ax				; always halt
@@:
	call doscode_halt_query
	mov si, doscode_msg.crlf
	jmp @B


TAGCRASHDOS:
 assume ds:nothing, es:nothing, ss:nothing
 global TAGCRASHDOS
 extern tagcrash_query_flag
	push ax
	push si
	mov si, doscode_msg.error_common	; access with cs
	call doscode_disp_msg_cs
	mov si, doscode_msg.error_tag		; access with cs
 extern flag_no_halt_tagcrash
	mov ax, flag_no_halt_tagcrash
	call doscode_halt_query
	pop si
	pop ax
 extern FETCHI_TAG
	mov word [ss:FETCHI_TAG], 22642
	retn


DEVCRASHDOS:
 assume ds:nothing, es:nothing, ss:nothing
 global DEVCRASHDOS
	push ax
	push si
	mov si, doscode_msg.error_common	; access with cs
	call doscode_disp_msg_cs
	mov si, doscode_msg.error_dev		; access with cs
 extern flag_no_halt_devcrash
	mov ax, flag_no_halt_devcrash
	call doscode_halt_query
	pop si
	pop ax
	retn


doscode_msg:
.error_common:	asciz 7,"DOSCODE error: "
.error_table:	asciz "Invalid doscode_entrypoint_list reference."
.error_shell_failed.1:	asciz 7,"Initial shell process execution failed! Code="
.error_shell_failed.2:	asciz "h."
.error_shell_returned:	asciz 7,"Initial shell process returned!"
.error_tag:	asciz "Internal error, tag mismatch!"
.error_dev:	asciz "Internal error, no device!"
.error_after:	asciz 13,10,"System halted. "
.mcb.1:		asciz "Corrupted MCB chain, err="
.mcb.2:		asciz "h",13,10,"Prior: "
.mcb.3:		asciz 13,10,"Next:  "
.mcb.first:	asciz " First/no MCB"
.hmcb.4:
.mcb.4:		db '"'
.empty:		asciz
%if _RELOCATEDOSCODE && _DOSCODEHMA
.hmcb.1:	asciz "Corrupted HMCB chain",13,10,"cx: "
.hmcb.7:
.hmcb.3:	asciz 13,10,"di: "
.hmcb.5:	asciz "Corrupted HMCB chain",13,10,"bx: "
.hmcb.first:	asciz " First/no HMCB"
%endif
.crlf:		asciz 13,10

;segm sn ownr size 11 22 33 11 22 33 44 55 66 77 88 "name"

 global doscode_halt_query
 global doscode_disp_msg_cs
 global doscode_disp_al
 global doscode_disp_al_hex
 global doscode_disp_ax_hex
 global doscode_disp_blank
 global doscode_msg.mcb.1
 global doscode_msg.mcb.2
 global doscode_msg.mcb.3
 global doscode_msg.mcb.4
 global doscode_msg.mcb.first
 global doscode_msg.empty
 global doscode_msg.hmcb.1
 global doscode_msg.hmcb.3
 global doscode_msg.hmcb.4
 global doscode_msg.hmcb.5
 global doscode_msg.hmcb.7
 global doscode_msg.hmcb.first
 global doscode_msg.error_common
 global doscode_msg.crlf
