
%if 0

lDebug initialisation

Copyright (C) 1995-2003 Paul Vojta
Copyright (C) 2008-2026 E. C. Masloch

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

%define SOURCEFILENAME "init.asm"
%ifidni __OUTPUT_FORMAT__, obj
 %include VERSIONMAC
 %include "debug.mac"
%endif

%if _LINK_COMPAT
 %define FORCEWORD word
 %define FORCESTRICTWORD strict word
%else
 %define FORCEWORD
 %define FORCESTRICTWORD
%endif


%if _LINK

%if !(_ONLY386 || _ONLYNON386) || _DUALCODE && _PM
 wllist G_GINITGROUP_SINIT
%endif

%ifn _ONLY386 || _ONLYNON386
 wllist N_SINIT_Lpatch_386_table
 wllist N_SINIT_Lpatch_no386_table
%if _DUALCODE
 wllist N_SINIT_Lpatch_386_table2
 wllist N_SINIT_Lpatch_no386_table2
%endif
 wllist N_SINIT_Lpatch_386_tableentry
 wllist N_SINIT_Lpatch_no386_tableentry
%endif

%if _DUALCODE && ! _PM
 wllist N_SINIT_Lrelocate_from_code
 wllist N_SINIT_Lrelocate_from_code2

 extern relocate_from_code
 extern relocate_from_code.end
 extern relocate_from_code2
 extern relocate_from_code2.end
%endif

%endif
	usesection INIT

%ifn _LINK
CODETARGET1_equate equ CODETARGET1
CODETARGET2_equate equ CODETARGET2
AUXTARGET1_equate equ AUXTARGET1
AUXTARGET2_equate equ AUXTARGET2
BOOTCODETARGET1_equate equ BOOTCODETARGET1
BOOTCODETARGET2_equate equ BOOTCODETARGET2
BOOTAUXTARGET1_equate equ BOOTAUXTARGET1
BOOTAUXTARGET2_equate equ BOOTAUXTARGET2
ldebug_codes_size_equate equ ldebug_code_size + ldebug_code2_size
ldebug_codes_truncated_size_equate equ ldebug_code_bootldr_truncated_size + ldebug_code2_size
auxbuff_size_equate equ auxbuff_size
initsectionoffset_p_equate equ paras(INITSECTIONOFFSET)
%endif

initcode:
%if ($ - $$) != 0
 %fatal initcode expected at start of section
%endif
%ifn _APPLICATION
	push cs
	pop ds
	mov dx, imsg.not_an_application
	mov ah, 09h
	int 21h
	mov ax, 4CFFh
	int 21h

		; magic sequence for tellsize
%if _LINK
	mov bx, 1
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
 wlcalc word_segrel_INIT, equ $ - 2
%else
	mov bx, paras(INITSECTIONOFFSET + init_size + deviceshim_size + 16)
%endif
	mov ah, 4Ah
	int 21h
		; end of magic sequence for tellsize

		; The +16 is to avoid entering a zero value
		;  for exeMinAlloc and exeMaxAlloc, which
		;  seems to be handled in a special way by
		;  DOS. (This special handling occurs for
		;  the uncompressed bootable build, eg
		;  ldebugu.com, and the nonbootable MZ shim
		;  build, eg debug.com.)
%endif

%if _APPLICATION
	mov ax, ss
	mov dx, ds
	sub ax, dx

	push ax			; (if sp was zero)

	mov dx, sp
	mov cl, 3
	add dx, 15
	rcr dx, 1
	shr dx, cl
	add ax, dx

%ifn _LINK
	cmp ax, strict word paras(APPINITSTACK_END)
%else

%if 0
%define APPINITTARGET (INITSECTIONOFFSET \
			+ datastack_size \
			+ ext_data_nonboot_add_size \
			+ auxbuff_size + auxbuff_init_size \
			+ historysegment_size \
			+ app_env_size \
			+ extsegment_size)
		; The early stack goes behind the init target.
%define APPINITSTACK_START (APPINITTARGET+init_size)
APPINITSTACK_SIZE equ 512	; must be even
		; This is the size of the application mode
		;  process memory block required while still
		;  using the init stack behind init target.
%define APPINITSTACK_END (APPINITSTACK_START+APPINITSTACK_SIZE)
%endif

	cmp ax, strict word relocatedzero + paras( \
		+ auxbuff_size + auxbuff_init_size \
		+ historysegment_size \
		+ app_env_size \
		+ extsegment_size \
		+ APPINITSTACK_SIZE)
 wlcalc word_segrel_INIT, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
%endif
	jae .stackdownfirst
.memupfirst:
		; magic sequence for tellsize
%ifn _LINK
	mov bx, paras(APPINITSTACK_END)
%else
	mov bx, relocatedzero + paras( \
		+ auxbuff_size + auxbuff_init_size \
		+ historysegment_size \
		+ app_env_size \
		+ extsegment_size \
		+ APPINITSTACK_SIZE)
 wlcalc word_segrel_INIT, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
%endif
	mov ah, 4Ah
	int 21h
		; end of magic sequence for tellsize
	jnc @F
.memfail:
	mov dx, imsg.early_mem_fail
.earlyfail:
	call init_putsz_cs
	mov ax, 4CFFh
	int 21h

@@:
.stackdownfirst:
	mov ax, ds
%ifn _LINK
	add ax, paras(APPINITSTACK_START)
%else
	add ax, strict word relocatedzero + paras( \
		+ auxbuff_size + auxbuff_init_size \
		+ historysegment_size \
		+ app_env_size \
		+ extsegment_size)
 wlcalc word_segrel_INIT, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
%endif
	cli
	mov ss, ax
	mov sp, APPINITSTACK_SIZE
	sti

		; if jumped to .stackdownfirst: now, shrink our memory block
		; else: no-op (already grew or shrunk block)
%ifn _LINK
	mov bx, paras(APPINITSTACK_END)
%else
	mov bx, relocatedzero + paras( \
		+ auxbuff_size + auxbuff_init_size \
		+ historysegment_size \
		+ app_env_size \
		+ extsegment_size \
		+ APPINITSTACK_SIZE)
 wlcalc word_segrel_INIT, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
%endif
	mov ah, 4Ah
	int 21h
	jc .memfail


	mov ax, ds
%ifn _LINK
	add ax, paras(INITSECTIONOFFSET)
%else
	add ax, strict word relocatedzero + 0
 wlcalc word_segrel_INIT, equ $ - 2
%endif
	mov dx, ds
%ifn _LINK
	add dx, paras(APPINITTARGET)
	mov cx, init_size_p
	call init_movp
init_size_p_equate equ init_size_p
appinittarget_p_equate equ paras(APPINITTARGET)
%if initsectionoffset_p_equate + init_size_p_equate > appinittarget_p_equate
 %error Overlap detected
%endif
%else
	add dx, strict word relocatedzero + paras( \
		+ auxbuff_size + auxbuff_init_size \
		+ historysegment_size \
		+ app_env_size \
		+ extsegment_size)
 wlcalc word_segrel_INIT, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
	mov cx, relocatedzero + 0
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
	call init_movp
%endif

	push dx
	call init_retf

%ifn _LINK
%if _APP_ENV_SIZE
	mov word [cs:memsize], paras(CODETARGET2 \
			+ ldebug_code_bootldr_truncated_size \
			+ ldebug_code2_size \
			+ historysegment_size \
			+ app_env_size \
			+ extsegment_size)
%endif

	mov bx, ds
	mov dx, bx
	add bx, paras(AUXTARGET1)
	add dx, paras(CODETARGET1)
 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _APP_ENV_SIZE
	mov ax, bx
	add ax, paras(auxbuff_size)
  %if AUXTARGET1_equate <= CODETARGET1_equate
%assign nn AUXTARGET1_equate
%assign mm CODETARGET1_equate
   %error Unexpected layout aux = nn code = mm
  %endif
 %endif
	mov cx, dx
	mov word [cs:init_layout], init_app_layout_1
	call init_check_auxbuff
	jz @F

	mov bx, ds
	mov dx, bx
	add bx, paras(AUXTARGET2)
	add dx, paras(CODETARGET2)
 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _APP_ENV_SIZE
  %if (paras(AUXTARGET1_equate) + paras(auxbuff_size_equate)) \
	!= (paras(CODETARGET2_equate) + paras(ldebug_codes_truncated_size_equate))
ldebug_code2_size_equate equ ldebug_code2_size
ldebug_code_size_equate equ ldebug_code_size
ldebug_code_bootldr_truncated_size_equate equ ldebug_code_bootldr_truncated_size
%assign vv (paras(ldebug_code2_size_equate))
%assign uu (paras(ldebug_code_size_equate))
%assign tt (paras(ldebug_code_bootldr_truncated_size_equate))
%assign sss (paras(CODETARGET1_equate))
%assign rr (paras(AUXTARGET1_equate))
%assign qq (paras(CODETARGET2_equate))
%assign pp (paras(auxbuff_size_equate))
%assign oo (paras(ldebug_codes_truncated_size_equate))
%assign nn (paras(AUXTARGET1_equate) + paras(auxbuff_size_equate))
%assign mm (paras(CODETARGET2_equate) + paras(ldebug_codes_truncated_size_equate))
   %error Unexpected layout aux1+auxb != code2+trunc
   %error code2+trunc = mm aux1+auxb = nn trunc = oo auxb = pp code2 = qq aux1 = rr
   %error code1 = sss codeseg1trunc = tt codeseg1 = uu codeseg2 = vv
  %endif
 %endif

%else	; _LINK
%if _APP_ENV_SIZE
 %if 0
%define CODETARGET1 (MESSAGESECTIONOFFSET + messagesegment_nobits_truncated_size + datastack_size)
%define CODETARGET2 (CODETARGET1+auxbuff_size)
%define AUXTARGET1 (CODETARGET1+ldebug_code_bootldr_truncated_size+ldebug_code2_size)
%define AUXTARGET2 CODETARGET1
%define AUXTARGET3 (AUXTARGET1+auxbuff_size)
 %endif
	mov word [cs:memsize], relocatedzero + paras(auxbuff_size \
			+ historysegment_size \
			+ app_env_size \
			+ extsegment_size)
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code_truncated, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE2@_extpara_behind_code2, equ $ - 2
%endif

	mov bx, ds
	mov dx, bx
	; add bx, paras(AUXTARGET1)
	add bx, strict word relocatedzero + 0
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code_truncated, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE2@_extpara_behind_code2, equ $ - 2
	; add dx, paras(CODETARGET1)
	add dx, strict word relocatedzero + 0
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _APP_ENV_SIZE
	mov ax, bx
	add ax, paras(auxbuff_size)
 %endif
	mov cx, dx
	mov word [cs:init_layout], init_app_layout_1
	call init_check_auxbuff
	jz @F

	mov bx, ds
	mov dx, bx
	; add bx, paras(AUXTARGET2)
	add bx, strict word relocatedzero + 0
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
	; add dx, paras(CODETARGET2)
	add dx, strict word relocatedzero + paras(auxbuff_size)
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
%endif
	mov word [cs:init_layout], init_app_layout_2
	call init_check_auxbuff
	jz @F

		; If both prior attempts failed, we allocate
		;  an additional 8 KiB and move the buffer to
		;  that. This should always succeed.
%ifn _LINK
	mov word [cs:memsize], paras(AUXTARGET3 \
			+ auxbuff_size \
			+ historysegment_size \
			+ app_env_size \
			+ extsegment_size)
				; enlarge the final memory block size

	mov bx, ds
	add bx, paras(AUXTARGET3)
%else	; _LINK
 %if 0
%define CODETARGET1 (MESSAGESECTIONOFFSET + messagesegment_nobits_truncated_size + datastack_size)
%define CODETARGET2 (CODETARGET1+auxbuff_size)
%define AUXTARGET1 (CODETARGET1+ldebug_code_bootldr_truncated_size+ldebug_code2_size)
%define AUXTARGET2 CODETARGET1
%define AUXTARGET3 (AUXTARGET1+auxbuff_size)
 %endif
	mov word [cs:memsize], relocatedzero + paras(auxbuff_size \
			+ auxbuff_size \
			+ historysegment_size \
			+ app_env_size \
			+ extsegment_size)
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code_truncated, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE2@_extpara_behind_code2, equ $ - 2
				; enlarge the final memory block size

	mov bx, ds
	add bx, strict word relocatedzero + paras(auxbuff_size)
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code_truncated, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE2@_extpara_behind_code2, equ $ - 2
%endif	; _LINK
	mov dx, cx
 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _APP_ENV_SIZE
	mov ax, bx
	add ax, paras(auxbuff_size)
 %endif
	mov word [cs:init_layout], init_app_layout_3
	call init_check_auxbuff
	jz @F

		; Because this shouldn't happen, this is
		;  considered an internal error.
	mov dx, imsg.early_reloc_fail
	jmp .earlyfail

@@:
 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _APP_ENV_SIZE
	push ax
 %endif
	call place_code_segments

%if _MESSAGESEGMENT
	mov ax, ds
 %ifn _LINK
	add ax, paras(MESSAGESECTIONOFFSET)
	mov dx, ds
	add dx, paras(100h + DATAENTRYTABLESIZE + datastack_size)
	mov cx, paras(messagesegment_truncated_size)
 %else		; _LINK
	add ax, strict word relocatedzero + 0
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
	mov dx, ds
	add dx, strict word relocatedzero + 0
 wlcalc word_wrt_@@lDEBUG_DATA_ENTRY@_extpara_end_of_datastack, equ $ - 2
	mov cx, relocatedzero + 0
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_truncated, equ $ - 2
 %endif		; _LINK
	call init_movp
	mov word [messageseg], dx
 %if _HELP_COMPRESSED && ! _PM
	mov [hshrink_memory_source.segment], dx
 %endif
 %if _BOOTLDR_DISCARD_HELP
   %ifn _LINK
	mov word [messageseg_size], messagesegment_nobits_truncated_size
   %else
	mov word [messageseg_size], behind_messagesegment_nobits_truncated wrt MESSAGESEGMENT
   %endif
  %if _HELP_COMPRESSED
	mov word [indirect_hshrink_message_buffer], truncated_hshrink_message_buffer
   %if _LINK || _LINK_COMPAT
	mov es, [code_seg]
	setopt [es:patch_boothelp_uncompressed], NONCOMPRESSEDFLAG
   %endif
  %endif
	push ds
	 push cs
	 pop ds
	mov si, imsg.boothelp_replacement
	mov es, dx
	mov di, msg.boothelp
	mov cx, imsg.boothelp_replacement_size_w
	rep movsw
	xor ax, ax
   %ifn _LINK
	mov cx, words(messagesegment_nobits_truncated_size \
		- (msg.boothelp - messagesegment_start \
			+ fromwords(imsg.boothelp_replacement_size_w)))
   %else
	mov cx, relocatedzero + fromwords(imsg.boothelp_replacement_size_w)
 wlcalc word_ext_msg.boothelp, equ $ - 2	; length + start
 wlcalc word_subr_0, equ $ - 2			; - (length + start)
 wlcalc word_wrt_MESSAGESEGMENT_ext_behind_messagesegment_nobits_truncated, equ $ - 2
 						; behind - (length + start)
 wlcalc word_shr_1, equ $ - 2			; above >> 1
   %endif
	rep stosw
	pop ds
 %endif
%endif

 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _APP_ENV_SIZE
	pop ax
  %if _HISTORY_SEPARATE_FIXED && _HISTORY
	mov word [history.segorsel + soaSegSel], ax
%if _PM
	mov word [history.segorsel + soaSegment], ax
%endif
	mov es, ax
	xor di, di
	mov cx, historysegment_size >> 1
	xor ax, ax
	rep stosw
  %endif

  %if _APP_ENV_SIZE
   %if _HISTORY_SEPARATE_FIXED && _HISTORY
	mov ax, es
	add ax, paras(historysegment_size)
   %endif
	mov word [envseg], ax
	mov es, ax
	xor di, di
	mov cx, app_env_size >> 1
	mov word [env_size], app_env_size
	xor ax, ax
	rep stosw
	mov word [es:3], paras(app_env_size - 16)
  %endif

  %if _EXTENSIONS
   %if _HISTORY_SEPARATE_FIXED && _HISTORY || _APP_ENV_SIZE
	mov ax, es
    %if _APP_ENV_SIZE
	add ax, paras(app_env_size)
    %else
	add ax, paras(historysegment_size)
    %endif
   %endif
	mov word [extseg], ax
	mov es, ax
	xor di, di
	mov cx, extsegment_size >> 1
	xor ax, ax
	rep stosw
  %endif
 %endif

	mov ax, bx

	mov word [auxbuff_segorsel + soaSegSel], ax
%if _PM
	mov word [auxbuff_segorsel + soaSegment], ax
					; initialise auxbuff references
%endif
%if _IMMASM  && _IMMASM_AUXBUFF
	mov word [immseg], ax
%endif

	mov es, ax
	xor di, di
	mov cx, _AUXBUFFSIZE >> 1
	xor ax, ax
	rep stosw			; initialise auxbuff

	cli
	mov ax, ds
	mov es, ax
	mov ss, ax
	mov sp, stack_end		; application mode stack switch
	sti

	mov ah, 4Ah
%ifn _LINK
	mov bx, paras(APPINITSTACK_START)
%else
	mov bx, strict word relocatedzero + paras( \
		+ auxbuff_size + auxbuff_init_size \
		+ historysegment_size \
		+ app_env_size \
		+ extsegment_size)
 wlcalc word_segrel_INIT, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
%endif
	mov word [cs:init_target_size], bx
	mov word [cs:init_base_size], bx
	int 21h				; shrink to drop init stack

%if _CONFIG
find_config_application:
	mov byte [configpath], 0
	mov ax, [2Ch]
	mov di, imsg.varconfig
	mov cx, imsg.varconfig.length
	mov dx, configpath
	call init_copyvar
	jnc .done
	call find_executable_application
.done:


find_scripts_application:
	mov byte [ss:scriptspath], 0
	mov ax, [ss:2Ch]
	mov di, imsg.varscripts
	mov cx, imsg.varscripts.length
	mov dx, scriptspath
	call init_copyvar
	jnc .done
	call find_executable_application
.done:

	push ss
	pop ds
	push ss
	pop es
%endif

	jmp old_initcode


%if _CONFIG
		; INP:	dx -> buffer
		;	ds => PSP
find_executable_application:
	mov ax, [2Ch]
	test ax, ax
	jz .noexec
	mov ds, ax
	xor si, si
@@:
	call init_nextvar
	jne @B
	inc si
	lodsw
	cmp ax, 1
	jne .noexec
	push ds
	pop es
	mov di, si
	mov cx, 127
	mov al, 0
	repne scasb
	jne .noexec
	dec di

	mov cx, 0
@@:
	cmp di, si
	jbe @F
	dec di
	cmp byte [di], '/'
	je .slash
	cmp byte [di], '\'
	jne @B
.slash:
	mov cx, di
	inc cx
	sub cx, si
@@:

	push ss
	pop es
	mov di, dx
	rep movsb
	mov al, 0
	stosb

.noexec:
.done:
	retn
%endif

%endif


%if _APPLICATION || _DEVICE
		; INP:	ds => (pseudo) PSP, data/entry segment
		; OUT:	word [code_seg] set
		;	code segments placed
		; CHG:	ax, cx, dx
		; STT:	UP
place_code_segments:
	mov ax, ds
%ifn _LINK
	add ax, paras(CODESECTIONOFFSET)
%else
	add ax, strict word relocatedzero + 0
 wlcalc word_segrel_lDEBUG_CODE, equ $ - 2
%endif
				; => code1 image, then code2 image
 %if _BOOTLDR_DISCARD
%ifn _LINK
	mov word [code_size], ldebug_code_bootldr_truncated_size
	mov cx, ldebug_code_bootldr_truncated_size_p
%else
	mov word [code_size], behind_code_truncated + 15 wrt lDEBUG_CODE
 wlcalc word_clr_15, equ $ - 2
	mov cx, relocatedzero + 0
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code_truncated, equ $ - 2
%endif
				; prepare for code1 segment move
  %if _DUALCODE
	cmp ax, dx		; source above destination ?
	jae @F			; yes -->
	call .place_code2	; source is below, move "backward" (high first)
  %endif
	call init_movp		; low last
  %if _DUALCODE
	jmp @FF

@@:
	call init_movp		; source is above, move "forward" (low first)
	call .place_code2	; high last
@@:
  %endif
 %else
%ifn _LINK
	mov cx, ldebug_code_size_p + ldebug_code2_size_p	; untruncated
%else
	mov cx, 0
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code2, equ $ - 2
%endif
	call init_movp
 %endif
	mov word [code_seg], dx	; initialise code segment reference
	retn

 %if _BOOTLDR_DISCARD && _DUALCODE
.place_code2:
	push ax
	push cx
	push dx
	add dx, cx		; => behind truncated code1 segment
%ifn _LINK
	add ax, ldebug_code_size_p	; untruncated
				; => at code2 image
	mov cx, ldebug_code2_size_p
				; = size of code2 segment
%else
	add ax, strict word relocatedzero + 0
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code, equ $ - 2
	mov cx, relocatedzero + 0
 wlcalc word_wrt_@@lDEBUG_CODE2@_extpara_behind_code2, equ $ - 2
%endif
	call init_movp
	pop dx
	pop cx
	pop ax
	retn
 %endif
%endif


init_retf:
	retf


		; INP:	bx => destination for auxbuff
		;	(The following are not actually used by this function,
		;	 they're just what is passed in and preserved to
		;	 be used by the caller after returning.)
		;	dx => destination for code image
		;	(if boot-loaded:) cx => destination for pseudo-PSP
		;		(implies cx+10h => destination for data_entry)
		;	ax => segment for history buffer
		; OUT:	ZR if this destination for auxbuff doesn't cross
		; 	 a 64 KiB boundary
		;	NZ else
		; CHG:	si, di
init_check_auxbuff:
	mov si, bx		; => auxbuff
%if _AUXBUFFSIZE < 8192
 %error Expected full sector length auxbuff
%endif
	lea di, [si + (8192 >> 4)]; => behind auxbuff (at additional paragraph)
	and si, 0F000h		; => 64 KiB chunk of first paragraph of auxbuff
	and di, 0F000h		; => 64 KiB chunk of additional paragraph
	cmp di, si		; same ?
				; ZR if they are the same
	retn


%if _BOOTLDR
		; Our loader transfers control to us with these registers:
		; INP:	ss:bp -> BPB
		;	ss:bp - 16 -> loadstackvars
		;	ss:bp - 32 -> loaddata
		;	(loader enters at) cs:0 -> loaded payload
		;	(loader enters at) cs:32 -> entry point
		;	(entrypoint sets up) ds:100h -> loaded payload
		; STT:	EI, UP
		;	all interrupts left from BIOS
boot_initcode:
	cld

d4	call init_d4message
d4	asciz "In boot_initcode",13,10

	mov dx, word [bp + ldMemoryTop]

		; initialise sdp
	mov ax, word [bp + bsBPB + bpbHiddenSectors + 2]
	mov word [load_data - LOADDATA2 + bsBPB + bpbHiddenSectors + 2], ax
	mov ax, word [bp + bsBPB + bpbHiddenSectors]
	mov word [load_data - LOADDATA2 + bsBPB + bpbHiddenSectors], ax

	xor bx, bx
	mov al, byte [bp + bsBPB + ebpbNew + bpbnBootUnit]
	mov byte [load_data - LOADDATA2 + bsBPB + ebpbNew + bpbnBootUnit], al
	mov bl, al			; bx = LD unit
	 test al, al			; hdd or diskette ?
	mov ax, word [bp + ldQueryPatchValue]
	 jns @F				; diskette -->
	xchg al, ah			; get high word of query patch value
@@:
	test al, al			; use for our access to this unit ?
	jns @F				; no -->
	and al, luf_mask_writable	; clear unused bits
	mov byte [load_unit_flags + bx], al
					; save here
@@:


	mov bx, ds
	mov es, bx			; => data entry image
	mov di, loaddata_loadedfrom	; -> loaded from data (ldp)

		; initialise LOADDATA, LOADSTACKVARS, and BPB
	push ss
	pop ds
	lea si, [bp + LOADDATA]		; -> LOADDATA on stack
	mov cx, (-LOADDATA + bsBPB + ebpbNew + BPBN_size)
	rep movsb

		; initialise cmdline_buffer from below LOADDATA
	lea si, [bp + ldCommandLine.start]
	mov di, cmdline_buffer		; -> our buffer in data entry

	cmp word [si], 0FF00h
	jne @F

	 push cs
	 pop ds
	mov si, imsg.default_cmdline.boot

@@:
	lodsb
	test al, al
	jz @FF

	setopt [es:internalflags3], dif3_input_cmdline
	db __TEST_IMM16
.switch_c_loop:
	stosb
	lodsb
.switch_c_loop_after_semicolon:
	cmp al, 0
	je @F
	cmp al, ';'
	jne .switch_c_not_semicolon
	mov al, 13
	stosb
	call init_skipwhite
	jmp .switch_c_loop_after_semicolon

.switch_c_not_semicolon:
	cmp al, '\'
	jne .switch_c_loop
	lodsb
	cmp al, 0
	jne .switch_c_loop

@@:
	stosb
@@:

		; es => data entry image
	push ss
	pop ds
	lea si, [bp + bsBPB + ebpbNew + BPBN_size]	; -> tail (FAT32 style)
		; ds:si -> tail. this is the same for FAT12/FAT16 as FAT32
	mov cx, 512 - (bsBPB + bpbNew + BPBN_size)	; tail size FAT12/FAT16
	cmp word [bp + bsBPB + bpbSectorsPerFAT], 0
	jne @F
	mov cx, 512 - (bsBPB + ebpbNew + BPBN_size)	; tail size FAT32
@@:
	call findname

%if _EXECUTABLE
	push bx
	push es
	pop ds
	mov si, boot_init_foundname
	mov di, init_executable_pathname
	cmp byte [si], 0
	je .unable

	xor cx, cx
	mov bx, si
@@:
	lodsb
	inc cx
	test al, al
	jnz @B

	mov si, imsg.ldpslash

	cs movsw
	cs movsw

	mov si, bx
	rep movsb

.unable:
	mov di, init_executable_pathname
	xor ax, ax
	mov cx, 128
	repne scasb
	dec di
	mov word [init_executable_pathname.end], di
	pop bx
%endif

	mov ax, dx
%ifn _LINK
	sub ax, paras(BOOTDELTA)
%else
 %if 0
%define BOOTDELTA	(fromkib(kib(auxbuff_size * 2 \
				+ historysegment_boot_size \
				+ boot_env_size \
				+ extsegment_boot_size \
				+ datastack_size \
				+ INITSECTIONOFFSET + 16)))
 %endif
	sub ax, strict word relocatedzero + paras( \
		+ auxbuff_size * 2 \
		+ historysegment_boot_size \
		+ boot_env_size \
		+ extsegment_boot_size \
		+ 16)
 wlcalc word_segrel_INIT, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_add_63, equ $ - 2		; 1023 >> 4
 wlcalc word_clr_63, equ $ - 2		; 1023 >> 4
%endif
	jc .error_out_of_memory
		; We exaggerate the target size (BOOTDELTA) for the
		;  worst case, thus we do not need to check for narrower
		;  fits later on. BOOTDELTA includes the pseudo-PSP size,
		;  data_entry size, asmtable1_size, asmtable2_size,
		;  datastack_size, code_size, 2 times auxbuff_size,
		;  historysegment_boot_size, boot_env_size,
		;  extsegment_boot_size,
		;  plus 16 bytes for the image ident prefix paragraph,
		;  and all of that rounded to a kibibyte boundary.

	mov cx, cs
%ifn _LINK
	add cx, paras(init_size + BOOTINITSTACK_SIZE)
%else
	add cx, strict word relocatedzero + paras(BOOTINITSTACK_SIZE)
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
%endif
	jc .error_out_of_memory
	cmp cx, dx
	ja .error_out_of_memory
		; This requires that above the image (including init)
		;  there is some 512 bytes free. That could be a problem
		;  except we've already exhausted the iniload-internally
		;  used buffers + stack, and do not need to preserve any
		;  of those. Recall that dx holds MemoryTop, *not* the
		;  lower LoadTop which could be right behind the image.
		; The sector buffer alone is documented as being 8 KiB
		;  sized, not to mention the FAT buffer and stack and
		;  LOADDATA/LOADSTACKVARS/BPB/boot sector.

	testopt [bp + ldHasLBA], ldhlfPreserveLoader
		; set NZ if loader resident

	mov di, cs
	cli
	mov ss, di
%ifn _LINK
	mov sp, init_size + BOOTINITSTACK_SIZE
%else
	mov sp, BOOTINITSTACK_SIZE + behind_init wrt INIT
%endif
	sti

d4	call init_d4message
d4	asciz "Switched to init stack",13,10

	lframe none
	lvar word,	target
	lenter
	lvar word,	targetstart
	 push ax
	lvar word,	memtop
	 push dx
	lea di, [bx + 10h]
	lvar word,	data
	 push di
	lvar word,	lowestused
	 push di
%ifn _LINK
	lea di, [bx + paras(CODESECTIONOFFSET)]
%else
	lea di, [word relocatedzero + bx + 0]
 wlcalc word_segrel_lDEBUG_CODE, equ $ - 2
%endif
	lvar word,	code
	 push di
	lvar dword,	loaderentry
	 push ax
	 push ax
	mov di, 60h
	lvar word,	lowlimit
	 push di

%if !_LOADER
	push cx
	push ax

		; had set NZ if loader resident
	jz .no_loader
	call get_size_and_ebda
	mov bx, word [bp + ?memtop]
	add bx, [cs:init_boot_ebdasize]
	mov es, bx
	xor di, di
	 push cs
	 pop ds
	mov si, loader_imageident
	mov cx, words(loader_imageident.size)
	mov dx, imsg.boot_warn_no_loader
	repe cmpsw
	jne .loaderwarning
	xor si, si
	xor dx, dx
	mov cx, 8
@@:
	es lodsw
	add dx, ax
	loop @B
	mov dx, imsg.boot_warn_invalid_loader
	jnz .loaderwarning

	mov word [bp + ?loaderentry + 2], bx
	not byte [cs:init_preserveloader]
	mov dx, imsg.boot_warn_lazy_loader
	mov cx, word [es:liiEntryQuerySize]
	jcxz .loaderwarning
	mov word [bp + ?loaderentry], cx
	mov ax, 60h
	call far [bp + ?loaderentry]
	mov word [bp + ?lowlimit], ax
	jmp @F

.loaderwarning:
	push dx
	mov dx, imsg.bootwarn
	call init_putsz_cs_bootldr
	pop dx
	call init_putsz_cs_bootldr
@@:

.no_loader:
	pop ax
	pop cx
%endif

	cmp cx, ax			; does init end below-or-equal target ?
	jbe .no_relocation		; yes, no relocation needed -->

d4	call init_d4message
d4	asciz "Needs relocation of init segment",13,10

	mov ax, word [bp + ?data]
%ifn _LINK
	sub ax, strict word paras(init_size + BOOTINITSTACK_SIZE)
%else
	sub ax, strict word relocatedzero + paras(BOOTINITSTACK_SIZE)
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
%endif
	jc .error_out_of_memory		; already at start of memory -->
	cmp ax, [bp + ?lowlimit]
	jb .error_out_of_memory		; already at start of memory -->

		; The relocation never overlaps, as we move init
		;  and its stack to the space before the image.
		;  Therefore we can move UP. And only a single
		;  rep movsw instruction is needed as init and
		;  its stack always fit in a single segment.
	push cs
	pop ds
	xor si, si			; -> init source
	mov es, ax
	mov word [bp + ?lowestused], ax
	xor di, di			; -> init destination
%ifn _LINK
	mov cx, words(init_size + BOOTINITSTACK_SIZE)
%else
	mov cx, BOOTINITSTACK_SIZE + 1 + behind_init wrt INIT
 wlcalc word_shr_1, equ $ - 2
%endif
	rep movsw			; relocate only init
		; Must not modify the data already on the stack here,
		;  until after either .done_relocation or
		;  .entire_relocation_done (which both relocate ss).

	push ax
	call init_retf			; jump to new init

	; mov ss, ax
		; Logic error: The entire load image relocation would
		;  make it so that the stack would corrupt part of the
		;  load image that had been relocated the first time
		;  already here. We want to keep using the high stack
		;  here until both relocations are done, at which point
		;  we *must* relocate ss so as to get the stack out of
		;  the way of installing the final image components.
		; Avoid modifying the stack frame variables until after
		;  we have relocated the stack. However, temporary use
		;  of the stack is okay before relocating it. (It must
		;  be because IRQs and debugger tracing will use it.)
	mov cx, word [bp + ?code]
%ifn _LINK
	add cx, paras(ldebug_code_size + ldebug_code2_size)	; untruncated
%else
	add cx, strict word relocatedzero + 0
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code2, equ $ - 2
%endif
	cmp cx, word [bp + ?targetstart]
					; does code end below-or-equal target ?
	jbe .done_relocation		; yes, relocated enough -->

.entire_relocation_needed:
d4	call init_d4message
d4	asciz "Needs relocation of entire load image",13,10

	mov dx, [bp + ?lowlimit]
	mov es, dx
	mov ax, cs
	cmp dx, ax			; already at start of memory ?
	jae .error_out_of_memory	; then error -->
		; This move is always downwards, that is, we can
		;  move UP. Multiple instructions operating on
		;  different segments may be needed as the image
		;  can be larger than 64 KiB.
		; However, init was already moved to before the
		;  remainder of the image, so the first 64 KiB
		;  move will always leave init finished and
		;  ready to use. So only move the first chunk
		;  in the special relocator, then jump into the
		;  final relocated init to do the remainder.

	inc dx
	; cmp dx, ax
	; ja .error_out_of_memory
	mov cx, .relocated
	 push dx
	 push cx			; on stack: far address of .relocated

	mov cx, ax			; source
	sub cx, dx			; source - target = how far to relocate

	xor di, di			; es:di -> where to put relocator
	push es
	push di				; on stack: relocator destination
	push cx				; on stack: how far to relocate
	 push cs
	 pop ds
	mov si, .relocator		; -> relocator source
	mov cx, 8
	rep movsw			; put relocator stub

	mov es, dx
	pop dx				; dx = how far to relocate

	xor di, di			; -> where to relocate to
	xor si, si			; -> relocate start

%ifn _LINK
BOOTRELOC1 equ	paras( init_size + BOOTINITSTACK_SIZE \
			+ DATAENTRYTABLESIZE \
			+ messagesegment_size \
			+ ldebug_code_size + ldebug_code2_size)	; untruncated
%endif

%if _LINK_COMPAT || _LINK
 %ifn _LINK
	mov cx, BOOTRELOC1		; how much to relocate
 %else
	mov cx, relocatedzero + paras(BOOTINITSTACK_SIZE - 256)
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_DATA_ENTRY@_extpara_behind_progbits_dataentry, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code2, equ $ - 2
 %endif
	mov bx, 1000h
	mov ax, cx
	cmp ax, bx			; > 64 KiB?
	jbe @F
	mov cx, bx			; first relocate the first 64 KiB
@@:
	sub ax, cx			; how much to relocate later
	shl cx, 1
	shl cx, 1
	shl cx, 1			; how much to relocate first,
					;  << 3 == convert paragraphs to words
%else
	mov bx, 1000h
 %if BOOTRELOC1 > 1000h
	mov cx, 8000h
	mov ax, BOOTRELOC1 - 1000h
 %else
	mov cx, BOOTRELOC1 << 3
	xor ax, ax
 %endif
%endif
	retf				; jump to relocator

		; ds:si -> first chunk of to be relocated data
		; es:di -> first chunk of relocation destination
		;  (si = di = 0, and es always <= ds)
		; cx = number of words in first chunk
		; ax = how many paragraphs remain after first chunk is done
		; dx = how far to relocate in paragraphs
		; bx = 1000h
		; ss:sp -> far return address into relocated init section
		;  (this always points into the first chunk)
.relocator:
	rep movsw
	retf				; jump to relocated cs : .relocated
.relocator_size equ $ - .relocator
%if .relocator_size > 16
 %error Unexpected large relocator
%endif

.relocated:
		; ds => prior chunk of relocation source (may be corrupted)
		; es => prior chunk of relocation destination
		; cx = 0
		; ax = how many paragraphs remain
		; dx = how far to relocate in paragraphs
		; bx = 1000h
@@:
	mov cx, es
	add cx, bx
	mov es, cx	; => next segment

	mov cx, ds
	add cx, bx
	mov ds, cx	; => next segment

	sub ax, bx	; = how much to relocate after this round
	mov cx, 1000h << 3	; in case another full 64 KiB to relocate
	jae @F		; another full 64 KiB to relocate this round -->
	add ax, bx	; restore (possibly zero)
	shl ax, 1
	shl ax, 1
	shl ax, 1	; convert paragraphs to words
	xchg cx, ax	; cx = that many words (possibly zero)
	xor ax, ax	; no more to relocate after this round

@@:
		; ds:0 -> next chunk of source
		; es:0 -> next chunk of destination
		; cx = how many words in this chunk, may be zero
		; ax = how many paragraphs remain for next round
		; (if ax is nonzero then cx is 8000h for a full 64 KiB)
		; dx = how far to relocate in paragraphs
		; bx = 1000h
	xor si, si	; -> source
	xor di, di	; -> destination
	rep movsw	; relocate next chunk
	test ax, ax	; another round needed?
	jnz @BB		; yes -->

.entire_relocation_done:
	mov ax, cs
	mov ss, ax			; relocate the stack
	nop
		; The stack frame variables have been relocated here
		;  along with the INIT segment data.

		; Now okay to modify relocated stack frame variables.
	mov word [bp + ?lowestused], ax
	sub word [bp + ?data], dx
	jc .error_internal
	sub word [bp + ?code], dx
	jc .error_internal

	mov cx, word [bp + ?code]
 %ifn _LINK
	add cx, paras(ldebug_code_size + ldebug_code2_size)	; untruncated
 %else
	add cx, strict word relocatedzero + 0
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code2, equ $ - 2
 %endif
	cmp cx, word [bp + ?targetstart]
					; does code end below-or-equal target ?
	jbe .done_relocation		; yes -->

.error_out_of_memory:
	mov dx, imsg.boot_error_out_of_memory
.putsz_error:
	call init_putsz_cs_bootldr
	jmp init_booterror.soft

.error_internal:
	mov dx, imsg.boot_error_internal
	jmp .putsz_error


.done_relocation:
	mov ax, cs
	mov ss, ax			; relocate the stack
	nop
		; Not needed if we got here after having executed
		;  .entire_relocation_done or by branching to this
		;  place through the .no_relocation label, but
		;  doesn't hurt in those cases either.
		; The stack frame variables have been relocated here
		;  along with the INIT segment data.
	mov ax, word [bp + ?lowestused]
	cmp ax, word [bp + ?lowlimit]
	jb .error_out_of_memory

.no_relocation:
	mov byte [cs:init_booterror.patch_switch_stack], __TEST_IMM8
					; SMC in section INIT

		; Due to the prior check in .done_relocation,
		;  we only go to .relocatehigh if the init is
		;  still at its initial position (*above* the
		;  data and code images). We depend on this
		;  in order to relocate init first, then do
		;  the remaining (image) relocation from init.
	mov ax, word [bp + ?lowestused]
	sub ax, word [bp + ?lowlimit]
	jae .highenough
.relocatehigh:
	neg ax				; how much to relocate higher
	mov cx, cs
%ifn _LINK
	add cx, paras(init_size + BOOTINITSTACK_SIZE + 256)
%else
	add cx, strict word relocatedzero + paras(BOOTINITSTACK_SIZE + 256)
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
%endif
	jc .error_out_of_memory
	add cx, ax			; => past wanted init
	jc .error_out_of_memory
	cmp cx, word [bp + ?targetstart]
	jae .error_out_of_memory	; if too little left -->

	mov dx, cs
	add dx, ax			; => destination init
	mov bx, sp			; preserve sp
	cli
	mov ss, dx
%ifn _LINK
	mov sp, fromwords(words(init_size + BOOTINITSTACK_SIZE + 256 - 1))
%else
	mov sp, behind_init + BOOTINITSTACK_SIZE + 256 wrt INIT
 wlcalc word_clr_1, equ $ - 2
%endif
					; go to temporary stack
	sti
	mov di, .relocatedhigh
	  push dx
	  push di			; -> relocated .relocatedhigh (temp stack)
	inc cx
	mov es, cx			; => relocator destination
	xor di, di
	  push es
	  push di			; -> relocator destination (temp stack)
	 push cs
	 pop ds
	mov si, .relocator		; -> relocator source
	mov cx, 8
	rep movsw			; put relocator stub
	mov es, dx
%ifn _LINK
	mov cx, words(init_size + BOOTINITSTACK_SIZE)
	mov si, fromwords(words(init_size + BOOTINITSTACK_SIZE)) - 2
%else
	mov cx, behind_init + BOOTINITSTACK_SIZE + 1 wrt INIT
 wlcalc word_shr_1, equ $ - 2
	mov si, behind_init + BOOTINITSTACK_SIZE + 1 - 2 wrt INIT
 wlcalc word_clr_1, equ $ - 2
%endif
	mov di, si			; -> last word
	std
	retf				; (uses temp stack)

.relocatedhigh:
	cld
%if 0		; ss already == dx
	cli
	mov ss, dx
	mov sp, bx
	sti
%else
	mov sp, bx			; restore original stack
%endif
	mov dx, word [bp + ?data]	; => data source
	add word [bp + ?data], ax
	jc .error_internal
	add word [bp + ?code], ax
	jc .error_internal

	push dx
	add dx, ax			; => data destination
	jc .error_internal
	pop ax				; => data source
%ifn _LINK
	mov cx, paras(CODESECTIONOFFSET \
		+ ldebug_code_size + ldebug_code2_size)	; untruncated
%else
	mov cx, relocatedzero + 0
 wlcalc word_wrt_@@lDEBUG_DATA_ENTRY@_extpara_behind_code2, equ $ - 2
%endif
					; = data + code size
	call init_movp

.highenough:


d4	call init_d4message
d4	asciz "Relocated enough",13,10


%if !_LOADER
	rol byte [cs:init_preserveloader], 1
	jnc @F

	mov dx, imsg.boot_warn_loader_no_reloc
	mov es, word [bp + ?loaderentry + 2]
	mov cx, word [es:liiEntryRelocate]
	jcxz .reloc_loaderwarning
	mov word [bp + ?loaderentry], cx
	mov ax, 60h
	call far [bp + ?loaderentry]
	mov word [bp + ?loaderentry + 2], ax
					; update our reference
	jmp @F

.reloc_loaderwarning:
	push dx
	mov dx, imsg.bootwarn
	call init_putsz_cs_bootldr
	pop dx
	call init_putsz_cs_bootldr
@@:
%endif

	call get_size_and_ebda

	mov cx, word [bp + ?memtop]
	add cx, [cs:init_boot_ebdasize]
%ifn _LINK
	sub cx, paras(INITSECTIONOFFSET - messagesegment_size + messagesegment_nobits_size + datastack_size + auxbuff_size + historysegment_boot_size + boot_env_size + extsegment_boot_size)
%else
	sub cx, relocatedzero + paras( \
		+ auxbuff_size \
		+ historysegment_boot_size \
		+ boot_env_size \
		+ extsegment_boot_size)
 wlcalc word_segrel_INIT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_minusextpara_behind_messagesegment, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
%endif

					; cx = paragraph of pseudo-PSP if here
%if !_LOADER
	dec cx				; => paragraph of image ident
	and cx, ~ (paras(1024) - 1)	; round down to kibibyte boundary
	inc cx				; => paragraph of pseudo-PSP if here
%else
	and cx, ~ (paras(1024) - 1)	; round down to kibibyte boundary
%endif

	mov bx, cx
	mov dx, bx
%ifn _LINK
	add bx, paras(BOOTAUXTARGET1)	; => auxbuff target if here
	add dx, paras(BOOTCODETARGET1)	; => code target if here
 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _BOOT_ENV_SIZE
	mov ax, bx
	add ax, paras(auxbuff_size)
  %if BOOTAUXTARGET1_equate <= BOOTCODETARGET1_equate
   %error Unexpected layout
  %endif
 %endif
%else	; _LINK
%if 0
%define BOOTCODETARGET1 (MESSAGESECTIONOFFSET + messagesegment_nobits_size + datastack_size)
%define BOOTCODETARGET2 (BOOTCODETARGET1+auxbuff_size)
%define BOOTAUXTARGET1 (BOOTCODETARGET1+ldebug_code_size+ldebug_code2_size)
%define BOOTAUXTARGET2 BOOTCODETARGET1
%define BOOTAUXTARGET3 (BOOTAUXTARGET1+auxbuff_size)
%endif
	; add bx, paras(BOOTAUXTARGET1)
	add bx, strict word relocatedzero + 0
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE2@_extpara_behind_code2, equ $ - 2
	; add dx, paras(BOOTCODETARGET1)
	add dx, strict word relocatedzero + 0
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _BOOT_ENV_SIZE
	mov ax, bx
	add ax, paras(auxbuff_size)
 %endif
%endif	; _LINK
	call init_check_auxbuff
	jz @F

d4	call init_d4message
d4	asciz "First layout rejected",13,10

	mov bx, cx			; attempt same target again
	mov dx, bx
%ifn _LINK
	add bx, paras(BOOTAUXTARGET2)	; => auxbuff target if here
	add dx, paras(BOOTCODETARGET2)	; => code target if here
 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _BOOT_ENV_SIZE
  %if (paras(BOOTAUXTARGET1_equate) + paras(auxbuff_size_equate)) \
	!= (paras(BOOTCODETARGET2_equate) + paras(ldebug_codes_size_equate))
   %error Unexpected layout
  %endif
 %endif
%else	; _LINK
%if 0
%define BOOTCODETARGET1 (MESSAGESECTIONOFFSET + messagesegment_nobits_size + datastack_size)
%define BOOTCODETARGET2 (BOOTCODETARGET1+auxbuff_size)
%define BOOTAUXTARGET1 (BOOTCODETARGET1+ldebug_code_size+ldebug_code2_size)
%define BOOTAUXTARGET2 BOOTCODETARGET1
%define BOOTAUXTARGET3 (BOOTAUXTARGET1+auxbuff_size)
%endif
	; add bx, paras(BOOTAUXTARGET2)
	add bx, strict word relocatedzero + 0
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
	; add dx, paras(BOOTCODETARGET2)
	add dx, strict word relocatedzero + paras(auxbuff_size)
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
%endif	; _LINK
	call init_check_auxbuff
	jz @F

d4	call init_d4message
d4	asciz "Second layout rejected",13,10

		; If both prior attempts failed, we allocate
		;  an additional 8 KiB and move the buffer to
		;  that. This should always succeed.
	mov cx, word [bp + ?memtop]
	add cx, [cs:init_boot_ebdasize]
%ifn _LINK
	sub cx, paras(INITSECTIONOFFSET - messagesegment_size + messagesegment_nobits_size + datastack_size + auxbuff_size*2 + historysegment_boot_size + boot_env_size + extsegment_boot_size)
%else	; _LINK
	sub cx, relocatedzero + paras( \
		+ auxbuff_size * 2 \
		+ historysegment_boot_size \
		+ boot_env_size \
		+ extsegment_boot_size)
 wlcalc word_segrel_INIT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_minusextpara_behind_messagesegment, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
%endif	; _LINK
					; cx = paragraph of pseudo-PSP if here
%if !_LOADER
	dec cx				; => paragraph of image ident
	and cx, ~ (paras(1024) - 1)	; round down to kibibyte boundary
	inc cx				; => paragraph of pseudo-PSP if here
%else
	and cx, ~ (paras(1024) - 1)	; round down to kibibyte boundary
%endif
	mov bx, cx
	mov dx, bx
%ifn _LINK
	add bx, paras(BOOTAUXTARGET1)	; => auxbuff target if here
; Note that we use BOOTAUXTARGET1 here, not BOOTAUXTARGET3, because
;  we move where the debugger starts rather than where it ends.
	add dx, paras(BOOTCODETARGET1)	; => code target if here
%else	; _LINK
%if 0
%define BOOTCODETARGET1 (MESSAGESECTIONOFFSET + messagesegment_nobits_size + datastack_size)
%define BOOTCODETARGET2 (BOOTCODETARGET1+auxbuff_size)
%define BOOTAUXTARGET1 (BOOTCODETARGET1+ldebug_code_size+ldebug_code2_size)
%define BOOTAUXTARGET2 BOOTCODETARGET1
%define BOOTAUXTARGET3 (BOOTAUXTARGET1+auxbuff_size)
%endif
	; add bx, paras(BOOTAUXTARGET1)
	add bx, strict word relocatedzero + 0
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE2@_extpara_behind_code2, equ $ - 2
; Note that we use BOOTAUXTARGET1 here, not BOOTAUXTARGET3, because
;  we move where the debugger starts rather than where it ends.
	; add dx, paras(BOOTCODETARGET1)
	add dx, strict word relocatedzero + 0
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
%endif	; _LINK
 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _BOOT_ENV_SIZE
	mov ax, bx
	add ax, paras(auxbuff_size)
 %endif
	call init_check_auxbuff
	jz @F

		; Because this shouldn't happen, this is
		;  considered an internal error.
	mov dx, imsg.early_reloc_fail
	jmp .putsz_error


		; cx => data_entry target
		; dx => code target
		; bx => auxbuff target
		; ax => history segment
@@:
d4	call init_d4message
d4	asciz "Layout found"
d4	call init_d4dumpregs
d4	call init_d4message
d4	asciz 13,10

 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _BOOT_ENV_SIZE
	push ax
 %endif
	mov word [bp + ?target], cx
	push dx
	cmp byte [cs:init_boot_ebdaflag], 0
	jz .reloc_memtop_no_ebda
%if !_LOADER
	dec cx
%endif
	sub cx, word [cs:init_boot_ebdasize]
	mov ax, word [cs:init_boot_ebdasource]
	mov dx, cx
	mov word [cs:init_boot_ebdadest], cx
	mov cx, word [cs:init_boot_ebdasize]
	cli
	call init_movp
	add word [bp + ?memtop], cx
	or byte [cs:init_boot_ebdaflag], 2
	mov ax, 40h
	mov es, ax
	mov word [es:0Eh], dx	; relocate EBDA

d4	call init_d4message
d4	asciz "EBDA relocated",13,10

	jmp @F

.reloc_memtop_no_ebda:
	mov dx, cx
@@:
	mov cl, 6
	shr dx, cl
	mov ax, 40h
	mov es, ax
	mov word [ cs:init_boot_new_memsizekib ], dx
	xchg word [es:13h], dx
	mov word [ cs:init_boot_old_memsizekib ], dx
	pop dx
	sti
d4	call init_d4message
d4	asciz "Memory top relocated",13,10

	mov cx, word [bp + ?target]
	mov ds, cx
	mov di, word [bp + ?memtop]	; => memory top
	sub di, paras(1024+8192)
	mov es, di
	cmp di, cx			; max padding starts below target PSP ?
	jb @F				; yes, do not initialise padding
	xor di, di			; -> padding
	mov cx, words(1024+8192)
	xor ax, ax
	rep stosw			; initialise padding
@@:

	mov ax, word [bp + ?code]	; => code source
					; dx => code target
%ifn _LINK
	mov cx, ldebug_code_size_p + ldebug_code2_size_p	; untruncated
					; = size
%else
	mov cx, relocatedzero + 0
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code2, equ $ - 2
%endif
	call init_movp			; relocate code to target
d4	call init_d4message
d4	asciz "Code segment relocated",13,10

		push dx			; (code segment)
	mov ax, word [bp + ?data]	; => data_entry source
	mov dx, ds
	add dx, paras(100h)		; => data_entry target
%ifn _LINK
	mov cx, paras(DATAENTRYTABLESIZE)
%else
	mov cx, relocatedzero + - paras(256)
 wlcalc word_wrt_@@lDEBUG_DATA_ENTRY@_extpara_behind_progbits_dataentry, equ $ - 2
%endif
	call init_movp			; relocate data_entry to target
		pop word [code_seg]	; initialise code reference
d4	call init_d4message
d4	asciz "Data segment relocated",13,10

%if _MESSAGESEGMENT
	mov ax, word [bp + ?data]	; => data_entry source
%ifn _LINK
	add ax, paras(MESSAGESECTIONOFFSET - 100h)
	mov dx, ds
	add dx, paras(100h + DATAENTRYTABLESIZE + datastack_size)
	mov cx, paras(messagesegment_size)
%else
	add ax, strict word relocatedzero + - paras(100h)
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
	mov dx, ds
	add dx, strict word relocatedzero + 0
 wlcalc word_wrt_@@lDEBUG_DATA_ENTRY@_extpara_end_of_datastack, equ $ - 2
	mov cx, relocatedzero + 0
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment, equ $ - 2
%endif
	call init_movp
	mov word [messageseg], dx
 %if _HELP_COMPRESSED && ! _PM
	mov [hshrink_memory_source.segment], dx
 %endif
 	mov es, dx
%ifn _LINK
	mov di, messagesegment_size
	xor ax, ax
	mov cx, words(messagesegment_nobits_size - messagesegment_size)
%else
	mov di, relocatedzero + 0
 wlcalc word_wrt_MESSAGESEGMENT_ext_behind_messagesegment, equ $ - 2
	xor ax, ax
	mov cx, behind_messagesegment_nobits + 1 wrt MESSAGESEGMENT
 wlcalc word_wrt_MESSAGESEGMENT_minusext_behind_messagesegment, equ $ - 2
 wlcalc word_shr_1, equ $ - 2
%endif
	rep stosw
%endif

 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _BOOT_ENV_SIZE
	pop ax
  %if _HISTORY_SEPARATE_FIXED && _HISTORY
	mov word [history.segorsel + soaSegSel], ax
%if _PM
	mov word [history.segorsel + soaSegment], ax
%endif
	mov es, ax
	xor di, di
	mov cx, historysegment_boot_size >> 1
	xor ax, ax
	rep stosw
  %endif

  %if _BOOT_ENV_SIZE
   %if _HISTORY_SEPARATE_FIXED && _HISTORY
	mov ax, es
	add ax, paras(historysegment_boot_size)
   %endif
	mov word [envseg], ax
	mov es, ax
	mov dx, ax
	inc dx
	xor di, di
	mov cx, boot_env_size >> 1
	mov word [env_size], boot_env_size
	xor ax, ax
	rep stosw
	mov word [es:3], paras(boot_env_size - 16)
  %endif

  %if _EXTENSIONS
   %if _HISTORY_SEPARATE_FIXED && _HISTORY || _BOOT_ENV_SIZE
	mov ax, es
    %if _BOOT_ENV_SIZE
	add ax, paras(boot_env_size)
    %else
	add ax, paras(historysegment_boot_size)
    %endif
   %endif
	mov word [extseg], ax
	mov es, ax
	xor di, di
	mov cx, extsegment_boot_size >> 1
	mov word [extseg_size], extsegment_boot_size
	xor ax, ax
	rep stosw
  %endif
 %endif

	mov ax, bx
	mov word [auxbuff_segorsel + soaSegSel], ax
%if _PM
	mov word [auxbuff_segorsel + soaSegment], ax
					; initialise auxbuff references
%endif
%if _IMMASM  && _IMMASM_AUXBUFF
	mov word [immseg], ax
%endif

	mov es, ax
	xor di, di
	mov cx, _AUXBUFFSIZE >> 1
	xor ax, ax
	rep stosw			; initialise auxbuff
d4	call init_d4message
d4	asciz "auxbuff initialised",13,10

	push ds
	pop es
	xor di, di
	mov cx, words(100h)
	rep stosw			; initialise pseudo-PSP
%if _BOOT_ENV_SIZE
	mov word [2Ch], dx
%endif
%if _EXTENSIONS
	mov cx, word [extdata_size]
	mov di, ext_data_area
	mov al, 0
	rep stosb			; init ext data area
%endif

init_boot_imageident:
	mov ax, ds
%if !_LOADER
	dec ax
%endif
	mov es, ax			; => paragraph for imageident
	xor di, di			; -> imageident target
	mov bx, word [bp + ?memtop]
	sub bx, ax			; = how many paragraphs do we use ?

	mov word [alloc_size], bx
	mov word [alloc_seg], ax

	 push cs
	 pop ds
	mov word [imageident.size], bx	; set image ident size

	mov si, imageident
	push si
	mov cx, 8
	xor dx, dx
.loop:
	lodsw
	add dx, ax
	loop .loop
	pop si

	neg dx
	mov word [imageident.check], dx	; set image ident checksum

%if !_LOADER
	mov cl, 8
%else
	mov cl, words(2Ch)
%endif
	rep movsw			; write image ident paragraph
		; cx = 0

%if !_LOADER
	rol byte [cs:init_preserveloader], 1
	jnc @F
	mov dx, word [bp + ?loaderentry + 2]
	mov es, dx
	mov cx, word [es:liiEntryDebug]	; cx nonzero if it has an entry
	mov bx, word [es:liiStackPointer]
					; fallback as zero if not supported
@@:
%endif

	mov ax, word [bp + ?target]

	; lleave ctx			; dropping this frame for stack switch
		; Frame context still used by get_size_and_ebda

	cli
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, stack_end		; boot mode stack switch
	sti

%if !_LOADER
	jcxz init_boot_noloader
	mov word [reg_eip], cx
	mov word [reg_esp], bx

	xor ax, ax		; initialise ax = 0 and FL = ZR NC etc
	pushf
	pop word [reg_efl]	; set to FL

	xchg ax, dx
	mov di, reg_ds
	stosw
	scasw			; (skip dummy high word)
	stosw
	scasw
	stosw
	scasw
	stosw

		; Set up initial addresses for 'a', 'd', and 'u' commands.
init_loader_adusetup:
	mov ax, word [reg_eip]
	mov cx, word [reg_eip+2]
	mov bx, word [reg_cs]
	mov dx, var_addr_entries.amount
	mov di, var_addr_entries

.loop:
	stosw			; IP
%if saSegSel == 4
	mov word [di], cx
	scasw			; skip this word
%endif
	xchg ax, bx
	stosw			; CS
%if _PM
 %if SEGADR_size != 10
  %error Unexpected SEGADR size
 %endif
.86m:
	stosw			; store saSegment
	scasw			; skip saSelector
%else
 %if SEGADR_size == 10
  %error Unexpected SEGADR size
 %endif
%endif
	xchg ax, bx		; d_addr

	dec dx
	jnz .loop
	jmp @F
%endif

init_boot_noloader:
	setopt [internalflags3], dif3_gotint19
@@:

	push word [cs:init_boot_old_memsizekib]
	pop word [boot_old_memsizekib]
	push word [cs:init_boot_new_memsizekib]
	pop word [boot_new_memsizekib]
	mov al, byte [cs:init_boot_ebdaflag]
	and al, 1
	mov byte [boot_ebdaflag], al

	setopt [internalflags], nodosloaded
	clropt [internalflags], notstdinput|inputfile|notstdoutput|outputfile
	mov byte [notatty], 0	; it _is_ a tty

	mov dx, imsg.crlf
	call init_putsz_cs

d4	call init_d4message
d4	asciz "New boot_initcode done",13,10

%if _CONFIG
write_config_boot:
	mov di, configpath
	mov ax, "ld"
	stosw
	mov ax, "p/"
	stosw
	mov word [configpath.dir_end], di
	mov di, scriptspath
	mov ax, "ld"
	stosw
	mov ax, "p/"
	stosw
	mov word [scriptspath.dir_end], di
%endif

	jmp boot_old_initcode


		; INP:	ds:si -> first byte to check for name
		;	cx = number of bytes left
		; OUT:	(8+1+3+1)bytes[es:boot_init_foundname] = found name,
		;	 converted to 8.3 ASCIZ format,
		;	 not written if none
		;	CY if no filename found,
		;	 si = INP:si + INP:cx
		;	 cx = 0
		;	NC if filename found,
		;	 ds:si -> byte behind the name, thus ds:(si-11)-> name
		;	 cx = number of bytes left
		; CHG:	di, ax
findname:
.:
	cmp cx, 11		; enough for another name ?
	jb .none		; no -->
				; (cx == 0 jumps here too)
.check:
	push cx
	push si
	mov cx, 11
	lodsb
	mov ah, al		; check for same char in all 11 places
	cmp al, 32		; first character must not be blank
	je .check_fail		; if it is -->
;	cmp al, 5		; first character may be 05h to indicate 0E5h
;	je .check_pass
	db __TEST_IMM8		; (skip lodsb)
.check_loop_same:
	lodsb
	cmp ah, al
	jne .check_loop_differs
	call .check_character
	jc .check_fail
	loop .check_loop_same
		; if we arrive here, all characters (while valid) are the
		;  same character repeated 11 times. we disallow this in case
		;  that the padding character is an allowed one (eg '&' 26h).
.check_fail:
	pop si
	pop cx
	dec cx			; lessen the counter
	inc si			; -> next position to check
	jmp .

.check_character:
	cmp al, 32
	jb .check_character_fail
	cmp al, 127
;	je .check_character_fail
	jae .check_character_fail
		; note: with all characters >= 128 allowed,
		;  we get false positives in our sectors.
	cmp al, '.'
	je .check_character_fail
	cmp al, '/'
	je .check_character_fail
	cmp al, '\'
	je .check_character_fail
	cmp al, 'a'
	jb .check_character_pass
	cmp al, 'z'
	ja .check_character_pass
.check_character_fail:
	stc
	retn

.check_character_pass:
	clc
	retn

.check_loop:
	lodsb
.check_loop_differs:
	call .check_character
	jc .check_fail
	cmp al, 32
	je .check_loop_is_blank
.check_pass:
	loop .check_loop

	pop ax			; (discard si)
	sub si, 11		; -> at name

	call convert_name_to_asciz
				; si -> behind name
	pop cx
	sub cx, 11		; lessen the counter
	clc
	retn

.check_loop_is_blank:
	mov ah, cl		; = how many remaining in ext (1 to 3)
				;  or name+ext (1 to 10, cannot be 11)
	cmp ah, 3		; in ext ?
	jbe @F			; yes -->
	sub ah, 3		; 1 to 7 name field remaining
	db __TEST_IMM8		; (skip lodsb)
.check_loop_blank:
	lodsb
@@:
	call .check_character	; general check
	jc .check_fail
	cmp al, 32		; non-blank ?
	jne .check_fail		; yes, fails -->
	dec ah			; at end of field ?
	jz .check_pass		; yes -->
				; no, still in field after blank.
				;  check that next trail is all blanks too.
	loop .check_loop_blank
	jmp .check_fail		; (never reached)

.none:
	 add si, cx
	 xor cx, cx
	stc
	retn


		; INP:	ds:si -> 11-byte blank-padded name
		;	es:boot_init_foundname -> (8+1+3+1)-byte buffer
		; OUT:	ds:si -> behind 11-byte blank-padded name
		;	es:boot_init_foundname filled
		; CHG:	cx, di, ax
convert_name_to_asciz:
	mov di, boot_init_foundname
	mov cx, 8
	rep movsb		; copy over base name, si -> extension
	cmp byte [es:di - 8], 05h	; is it 05h ?
	jne @F			; no -->
	mov byte [es:di - 8], 0E5h	; yes, convert to 0E5h
@@:

	db __TEST_IMM8		; (skip dec)
@@:
	dec di			; decrement -> at previous trailing blank
	cmp byte [es:di - 1], 32	; trailing blank ?
	je @B			; yes -->

	mov al, '.'
	stosb			; store dot (if needed)
	mov cl, 3
	rep movsb		; copy over extension, si -> behind name

	db __TEST_IMM8		; (skip dec)
@@:
	dec di			; decrement -> at previous trailing blank
	cmp byte [es:di - 1], 32	; trailing blank ?
	je @B			; yes -->

	cmp byte [es:di - 1], '.'	; trailing dot ? (only occurs if all-blank ext)
	jne @F			; no -->
	dec di			; -> at the dot
@@:
	mov al, 0
	stosb			; store filename terminator
	retn


get_size_and_ebda:
	int 12h
	mov cl, 6
	shl ax, cl

	push ax
	push ds
	xor si, si
	xchg dx, ax
	mov ds, si
	lds si, [4 * 2Fh]
	add si, 3
	lodsb
	cmp al, 'R'
	jne .no_rpl
	lodsb
	cmp al, 'P'
	jne .no_rpl
	lodsb
	cmp al, 'L'
	jne .no_rpl
	mov ax, 4A06h
	int 2Fh
.no_rpl:
	xchg ax, dx
	pop ds
	pop dx

	cmp ax, dx
	je .no_error_rpl
		; in case RPL is present, error out (for now)

		; notes for +RPL installation:
		; 1. Allocate enough memory for our MCB + an PSP + our image + the last and the RPL MCB
		; 2. Create the RPL's MCB + a last MCB
		; 3. Relocate, initialise PSP
		; 4. Hook Int2F as RPLOADER to report DOS our new size

	mov dx, imsg.rpl_detected
	jmp .putsz_error

.no_error_rpl:
d4	call init_d4message
d4	asciz "Loader past RPL detection",13,10

	mov bx, word [bp + ?memtop]
	cmp bx, ax
	je @F

		; Special debugging support: If memtop is below
		;  what we detected using int 12h then update
		;  memtop and continue with the changed memtop.
		; The relocations done will suffice in any case.
	mov word [bp + ?memtop], ax
	mov bx, ax
	jb @F

	mov dx, imsg.mismatch_detected
	jmp .putsz_error

@@:					; bx => behind usable memory
%if 0
	mov ah, 0C1h
	stc
	int 15h				; BIOS, do you have an EBDA?
	mov ax, es
	jnc .ebda			; segment in ax -->
					; I don't believe you, let's check
%endif	; Enabling this would enable the BIOS to return an EBDA even if it isn't
	; noted at 40h:0Eh, which would be useless because we have to relocate it.

	xor dx, dx			; initialise dx to zero if no EBDA
	mov ax, 40h
	mov es, ax
	mov ax, word [ es:0Eh ]		; EBDA segment (unless zero) or LPT4 base I/O address (200h..3FCh)
	cmp ax, 400h
	jb .noebda			; -->
.ebda:
d4	call init_d4message
d4	asciz "EBDA detected",13,10

	or byte [cs:init_boot_ebdaflag], 1
	cmp ax, bx
	;jb init_booterror.soft		; uhh, the EBDA is inside our memory?
	;ja init_booterror.soft		; EBDA higher than top of memory. This is just as unexpected.
	je @F
	mov dx, imsg.boot_ebda_unexpected
	jmp .putsz_error

@@:
	mov ds, ax
	xor dx, dx
	mov dl, byte [ 0 ]		; EBDA size in KiB
	mov cl, 6
	shl dx, cl			; *64, to paragraphs
	mov word [cs:init_boot_ebdasize], dx
	mov word [cs:init_boot_ebdasource], ax
d4	jmp @F
.noebda:
d4	call init_d4message
d4	asciz "No EBDA detected",13,10
@@:
	retn

.putsz_error: equ boot_initcode.putsz_error

	lleave ctx			; dropping early boot_initcode frame


init_booterror:
.soft:
	xor ax, ax
	db __TEST_IMM16			; (skip mov)
.hard:
	mov al, 1

;d4	call init_d4pocketdosmemdump
d4	call init_d4dumpregs

.patch_switch_stack:
	jmp strict short .no_switch_stack

	mov bx, cs
	cli
	mov ss, bx
%ifn _LINK
	mov sp, init_size + BOOTINITSTACK_SIZE
%else
	mov sp, BOOTINITSTACK_SIZE + behind_init wrt INIT
%endif
	sti

.no_switch_stack:
	push ax

	mov ax, 40h
	mov es, ax

	test byte [cs:init_boot_ebdaflag], 2
	jz @F

	cli
	mov dx, [cs:init_boot_ebdasource]
	mov ax, [cs:init_boot_ebdadest]
	mov cx, [cs:init_boot_ebdasize]
	call init_movp

	mov word [es:0Eh], dx
@@:

	mov dx, [cs:init_boot_old_memsizekib]
	test dx, dx
	jz @F
	mov word [es:13h], dx
@@:
	sti

	mov dx, imsg.booterror
	call init_putsz_cs_bootldr
	call init_getc_bootldr
	pop ax
	test ax, ax
	jnz @F
	int 19h
@@:
	jmp 0FFFFh:0
%endif	; _BOOTLDR

%if _DEVICE
		; Our entrypoint transfers control to us with these registers:
		; INP:	ss:sp -> bx, fl, ds, ax, far return address to DOS
		;	ds:100h -> loaded payload
device_initcode:
	cld

	or word [device_header.next], -1
		; ! this uses offset 100h in the adjusted ds

	pop bx
	push es
	push bx
	push cx
	push dx
	push si
	push di

	mov ax, word [es:bx + 0Eh]	; ax = amount bytes in last segment
	mov cl, 4
	shr ax, cl			; = paragraphs, rounding down
	add ax, word [es:bx + 0Eh + 2]	; => behind available memory
	jc .memorybad
	mov word [cs:dev_memory_end], ax
	mov dx, ds
	add dx, 10h			; => our memory
	sub ax, dx			; ax = amount available paragraphs
	jc .memorybad

%ifn _LINK
	cmp ax, (DEVICEINITSIZE + 15) >> 4
%else
 %if 0
%define DEVICEADJUST	(deviceshim_size + 110h)
%define DEVICEINITTARGET (INITSECTIONOFFSET \
			+ DEVICEADJUST \
			+ datastack_size \
			+ ext_data_nonboot_add_size \
			+ auxbuff_size + auxbuff_init_size \
			+ historysegment_size \
			+ dev_env_size \
			+ extsegment_size \
			+ 10h)
%define DEVICEINITSIZE (DEVICEINITTARGET - 100h \
			+ init_size \
			+ deviceshim_size)
 %endif
	cmp ax, strict word relocatedzero + paras( \
		+ auxbuff_size \
		+ auxbuff_init_size \
		+ historysegment_size \
		+ dev_env_size \
		+ extsegment_size \
		+ 20h)
 wlcalc word_segrel_INIT, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
; wlcalc word_wrt_@@lDEBUG_DATA_ENTRY@_minusextpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
%endif
	jae .memorygood

.memorybad:
	mov dx, imsg.early_mem_fail
	call init_putsz_cs

	mov ax, 3000h
	int 21h
	cmp al, 5
	jae @F
	mov dx, imsg.dos_below_5
.earlyfail:
	call init_putsz_cs
@@:

	pop di
	pop si
	pop dx
	pop cx
	pop bx
	pop es

	mov ax, ds
	add ax, paras(100h)
	mov word [es:bx + 3], 8105h	; error, done, code: bad structure length
	and word [es:bx + 0Eh], 0
	mov word [es:bx + 0Eh + 2], ax	; -> behind memory in use

	popf
	pop ds
	pop ax
	retf

.memorygood:
	mov ax, ds
%ifn _LINK
	add ax, paras(INITSECTIONOFFSET)
%else
	add ax, strict word relocatedzero + 0
 wlcalc word_segrel_INIT, equ $ - 2
%endif
	mov dx, ds
%ifn _LINK
	add dx, paras(DEVICEINITTARGET)
	mov cx, init_size_p + deviceshim_size_p
%else
 %if 0
%define DEVICEADJUST	(deviceshim_size + 110h)
%define DEVICEINITTARGET (INITSECTIONOFFSET \
			+ DEVICEADJUST \
			+ datastack_size \
			+ ext_data_nonboot_add_size \
			+ auxbuff_size + auxbuff_init_size \
			+ historysegment_size \
			+ dev_env_size \
			+ extsegment_size \
			+ 10h)
%define DEVICEINITSIZE (DEVICEINITTARGET - 100h \
			+ init_size \
			+ deviceshim_size)
 %endif
	add dx, strict word relocatedzero + paras( \
		+ auxbuff_size \
		+ auxbuff_init_size \
		+ historysegment_size \
		+ dev_env_size \
		+ extsegment_size \
		+ 120h)
 wlcalc word_segrel_INIT, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
; wlcalc word_wrt_@@lDEBUG_DATA_ENTRY@_minusextpara_end_of_datastack, equ $ - 2
	mov cx, relocatedzero + 0
 wlcalc word_wrt_INIT_extpara_behind_deviceshim, equ $ - 2
%endif
	call init_movp
%ifn _LINK
init_plus_deviceshim_size_p_equate equ init_size_p + deviceshim_size_p
deviceinittarget_p_equate equ paras(DEVICEINITTARGET)
%if initsectionoffset_p_equate + init_plus_deviceshim_size_p_equate \
	> deviceinittarget_p_equate
 %error Overlap detected
%endif
%endif

	push dx
	call init_retf

%if _DEV_ENV_SIZE
 %ifn _LINK
	mov word [cs:memsize], paras(CODETARGET2 \
			+ ldebug_code_bootldr_truncated_size \
			+ ldebug_code2_size \
			+ historysegment_size \
			+ dev_env_size \
			+ extsegment_size)
 %else
 %if 0
%define CODETARGET1 (MESSAGESECTIONOFFSET + messagesegment_nobits_truncated_size + datastack_size)
%define CODETARGET2 (CODETARGET1+auxbuff_size)
%define AUXTARGET1 (CODETARGET1+ldebug_code_bootldr_truncated_size+ldebug_code2_size)
%define AUXTARGET2 CODETARGET1
%define AUXTARGET3 (AUXTARGET1+auxbuff_size)
 %endif
	mov word [cs:memsize], relocatedzero + paras(auxbuff_size \
			+ historysegment_size \
			+ dev_env_size \
			+ extsegment_size)
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code_truncated, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE2@_extpara_behind_code2, equ $ - 2
 %endif
	mov word [cs:mem_envsize], paras(dev_env_size)
%endif

	mov bx, ds
 %ifn _LINK
	add bx, FORCESTRICTWORD paras(DEVICEADJUST)
 %else
;%define DEVICEADJUST	(deviceshim_size + 110h)
	add bx, strict word relocatedzero + paras(110h)
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
 %endif
	mov dx, bx
 %ifn _LINK
	add bx, paras(AUXTARGET1)
	add dx, paras(CODETARGET1)
 %else
	; add bx, paras(AUXTARGET1)
	add bx, strict word relocatedzero + 0
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code_truncated, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE2@_extpara_behind_code2, equ $ - 2
	; add dx, paras(CODETARGET1)
	add dx, strict word relocatedzero + 0
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 %endif
 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _DEV_ENV_SIZE
	mov ax, bx
	add ax, paras(auxbuff_size)
 %endif
	mov cx, dx
	mov word [cs:init_layout], init_dev_layout_1
	call init_check_auxbuff
	jz @F

	mov bx, ds
 %ifn _LINK
	add bx, FORCESTRICTWORD paras(DEVICEADJUST)
 %else
;%define DEVICEADJUST	(deviceshim_size + 110h)
	add bx, strict word relocatedzero + paras(110h)
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
 %endif
	mov dx, bx
 %ifn _LINK
	add bx, paras(AUXTARGET2)
	add dx, paras(CODETARGET2)
 %else
	; add bx, paras(AUXTARGET2)
	add bx, strict word relocatedzero + 0
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
	; add dx, paras(CODETARGET2)
	add dx, strict word relocatedzero + paras(auxbuff_size)
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 %endif
	mov word [cs:init_layout], init_dev_layout_2
	call init_check_auxbuff
	jz @F

		; If both prior attempts failed, we allocate
		;  an additional 8 KiB and move the buffer to
		;  that. This should always succeed.
%ifn _LINK
	mov word [cs:memsize], paras(AUXTARGET3 \
			+ auxbuff_size \
			+ historysegment_size \
			+ dev_env_size \
			+ extsegment_size)
				; enlarge the final memory block size
%else
	mov word [cs:memsize], relocatedzero + paras(auxbuff_size \
			+ auxbuff_size \
			+ historysegment_size \
			+ dev_env_size \
			+ extsegment_size)
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code_truncated, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE2@_extpara_behind_code2, equ $ - 2
%endif

	mov bx, ds
 %ifn _LINK
	add bx, FORCESTRICTWORD paras(DEVICEADJUST)
 %else
;%define DEVICEADJUST	(deviceshim_size + 110h)
	add bx, strict word relocatedzero + paras(110h)
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
 %endif
%ifn _LINK
	add bx, paras(AUXTARGET3)
%else
	add bx, strict word relocatedzero + paras(auxbuff_size)
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code_truncated, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE2@_extpara_behind_code2, equ $ - 2
%endif	; _LINK
	mov dx, cx
 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _DEV_ENV_SIZE
	mov ax, bx
	add ax, paras(auxbuff_size)
 %endif
	mov word [cs:init_layout], init_dev_layout_3
	call init_check_auxbuff
	jz @F

		; Because this shouldn't happen, this is
		;  considered an internal error.
	mov dx, imsg.early_reloc_fail
	jmp .earlyfail

@@:
 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _DEV_ENV_SIZE
	push ax
 %endif
	call place_code_segments

%if _MESSAGESEGMENT
	mov ax, ds
%ifn _LINK
	add ax, paras(MESSAGESECTIONOFFSET)
%else
	add ax, strict word relocatedzero + 0
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
%endif
	mov dx, ds
%ifn _LINK
	add dx, 10h \
		+ paras(deviceshim_size + 110h \
		+ DATAENTRYTABLESIZE + datastack_size)
	mov cx, paras(messagesegment_truncated_size)
%else
	add dx, strict word relocatedzero + 10h + paras(110h - 100h)
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_DATA_ENTRY@_extpara_end_of_datastack, equ $ - 2
	mov cx, relocatedzero + 0
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_truncated, equ $ - 2
%endif
	call init_movp
 %if _BOOTLDR_DISCARD_HELP
%ifn _LINK
	mov word [messageseg_size], messagesegment_nobits_truncated_size
%else
	mov word [messageseg_size], behind_messagesegment_nobits_truncated wrt MESSAGESEGMENT
%endif
  %if _HELP_COMPRESSED
	mov word [indirect_hshrink_message_buffer], truncated_hshrink_message_buffer
   %if _LINK || _LINK_COMPAT
	mov es, [code_seg]
	setopt [es:patch_boothelp_uncompressed], NONCOMPRESSEDFLAG
   %endif
  %endif
	push ds
	 push cs
	 pop ds
	mov si, imsg.boothelp_replacement
	mov es, dx
	mov di, msg.boothelp
	mov cx, imsg.boothelp_replacement_size_w
	rep movsw
	xor ax, ax
   %ifn _LINK
	mov cx, words(messagesegment_nobits_truncated_size \
		- (msg.boothelp - messagesegment_start \
			+ fromwords(imsg.boothelp_replacement_size_w)))
   %else
	mov cx, relocatedzero + fromwords(imsg.boothelp_replacement_size_w)
 wlcalc word_ext_msg.boothelp, equ $ - 2	; length + start
 wlcalc word_subr_0, equ $ - 2			; - (length + start)
 wlcalc word_wrt_MESSAGESEGMENT_ext_behind_messagesegment_nobits_truncated, equ $ - 2
 						; behind - (length + start)
 wlcalc word_shr_1, equ $ - 2			; above >> 1
   %endif
	rep stosw
	pop ds
 %endif
	push dx
%endif

	mov ax, ds
	add ax, 10h
	mov dx, ax
%ifn _LINK
	add dx, FORCESTRICTWORD paras(deviceshim_size + 110h)
%else
	add dx, strict word relocatedzero + paras(110h)
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
%endif
%ifn _LINK
	mov cx, paras(DATAENTRYTABLESIZE)
%else
	mov cx, relocatedzero + - paras(256)
 wlcalc word_wrt_@@lDEBUG_DATA_ENTRY@_extpara_behind_progbits_dataentry, equ $ - 2
%endif
	call init_movp

	mov ax, cs
%ifn _LINK
	add ax, init_size_p
%else
	add ax, strict word relocatedzero + 0
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
%endif
	mov dx, ds
	add dx, paras(100h)
%ifn _LINK
	mov cx, deviceshim_size_p
%else
	mov cx, relocatedzero + 0
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
%endif
	call init_movp

	mov dx, ds
%ifn _LINK
	add dx, FORCESTRICTWORD paras(100h) + deviceshim_size_p
%else
	add dx, strict word relocatedzero + paras(100h)
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
%endif
	mov es, dx
	push ds
%ifn _LINK
	sub dx, FORCESTRICTWORD deviceshim_size_p + 1
%else
	sub dx, strict word relocatedzero + 1
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
%endif
	mov ds, dx
	xor ax, ax
	xor di, di
	mov cx, 4
	rep stosw
	mov si, 8
	mov cl, 4
	rep movsw
	pop ds

	mov dx, ds
 %ifn _LINK
	add dx, FORCESTRICTWORD paras(DEVICEADJUST)
 %else
;%define DEVICEADJUST	(deviceshim_size + 110h)
	add dx, strict word relocatedzero + paras(110h)
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
 %endif
	mov ds, dx

%if _MESSAGESEGMENT
 %if _HELP_COMPRESSED && ! _PM
	pop ax
	mov [hshrink_memory_source.segment], ax
	mov [messageseg], ax
 %else
	pop word [messageseg]
 %endif
%endif

	push bx
	mov ah, 51h
	int 21h
	mov word [pspdbe], bx
	pop bx

	mov es, dx
	mov cx, words(256)
	xor di, di
	xor ax, ax
	rep stosw		; clear buffer for PSP + command line tail

		; PSP creation moved to later, after command line parsing

 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || _DEV_ENV_SIZE
	pop ax
  %if _HISTORY_SEPARATE_FIXED && _HISTORY
	mov word [history.segorsel + soaSegSel], ax
%if _PM
	mov word [history.segorsel + soaSegment], ax
%endif
	mov es, ax
	xor di, di
	mov cx, historysegment_size >> 1
	xor ax, ax
	rep stosw
  %endif

  %if _DEV_ENV_SIZE
   %if _HISTORY_SEPARATE_FIXED && _HISTORY
	mov ax, es
	add ax, paras(historysegment_size)
   %endif
	mov word [envseg], ax
	mov es, ax
	mov dx, ax
	inc dx
	xor di, di
	mov cx, dev_env_size >> 1
	mov word [env_size], dev_env_size
	xor ax, ax
	rep stosw
	mov word [es:3], paras(dev_env_size - 16)
  %endif

  %if _EXTENSIONS
   %if _HISTORY_SEPARATE_FIXED && _HISTORY || _DEV_ENV_SIZE
	mov ax, es
    %if _DEV_ENV_SIZE
	add ax, paras(dev_env_size)
    %else
	add ax, paras(historysegment_size)
    %endif
   %endif
	mov word [extseg], ax
	mov es, ax
	xor di, di
	mov cx, extsegment_size >> 1
	xor ax, ax
	rep stosw
  %endif
 %endif

	mov ax, bx

	mov word [auxbuff_segorsel + soaSegSel], ax
%if _PM
	mov word [auxbuff_segorsel + soaSegment], ax
					; initialise auxbuff references
%endif
%if _IMMASM  && _IMMASM_AUXBUFF
	mov word [immseg], ax
%endif

	mov es, ax
	xor di, di
	mov cx, _AUXBUFFSIZE >> 1
	xor ax, ax
	rep stosw			; initialise auxbuff

%if _DEV_ENV_SIZE
	mov word [2Ch], dx
	push ds
	call init_device_get_environment
	test ax, ax
	jz @F
	mov ds, ax
	mov es, dx
	xor di, di
	xor si, si
.loop_env:
	call init_nextvar
	jne .loop_env
	inc si
	cmp si, dev_env_size - 16 - 2
	jbe .copy_env
	mov dx, imsg.dev_env_too_large
	call init_putsz_cs
	jmp @F

.copy_env:
	mov cx, si
	xor si, si
	rep movsb
	xor ax, ax
	stosw
@@:
	pop ds
%endif

	mov ax, ds			; => PSP
%ifn _LINK
	sub ax, strict word deviceshim_size_p + paras(10h)
%else
	sub ax, strict word relocatedzero + paras(10h)
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
%endif
	mov word [device_header_address + 2], ax
	mov word [alloc_seg], ax

	mov ax, ds			; => PSP
	mov bx, word [cs:memsize]
		; = amount paragraphs for PSP + DATA ENTRY + TABLE
		;  + DATA STACK + CODE + AUXBUFF + HISTORY + dev ENV + EXT
	add ax, bx			; => placeholder for trailing container
%ifn _LINK
	add bx, FORCESTRICTWORD deviceshim_size_p + paras(10h) + paras(10h)
%else
	add bx, strict word relocatedzero + paras(10h + 10h)
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
%endif
		; (layout is deviceshim, MCB placeholder, debugger segments,
		;  placeholder for trailing container MCB)
					; = amount paragraphs expected in MCB
	mov word [device_mcb_paragraphs], bx
	mov word [alloc_size], bx

					; ax => where to place container sig
	call init_dev_place_container_signature
					; ax => behind memory used for device
	jmp @F

		; INP:	ax => where to place container signature
		; OUT:	ax => behind memory used for device
		;	ax = INP:ax + 1
		; CHG:	es, di, cx, si
init_dev_place_container_signature:
	mov es, ax
	xor di, di			; -> buffer for trailing container MCB
	mov cx, words(10h)		; = amount words
	push ds
	 push cs
	 pop ds
	 mov si, init_container_signature	; -> init string
	rep movsw
	pop ds
	inc ax				; => behind memory used for device
	retn


@@:
	pop word [reg_edi]
	pop word [reg_esi]
	pop word [reg_edx]
	pop word [reg_ecx]
	mov word [reg_ebp], bp
	pop bx
	pop es
	mov word [reg_ebx], bx
	mov word [reg_es], es

	mov word [es:bx + 3], 100h	; no error, done
	and word [es:bx + 0Eh], 0
	mov word [es:bx + 0Eh + 2], ax	; -> behind memory in use

	pop word [reg_efl]
	pop word [reg_ds]
	pop word [reg_eax]
	mov word [reg_ss], ss
	mov word [reg_esp], sp

	mov word [reg_cs], ds
	mov word [reg_eip], entry_retf

.cmdline:
	push ds
	lds si, [es:bx + 12h]		; ds:si -> device command line
	pop es
%if _CONFIG
	push ds
	push si
%endif
	mov di, 81h			; es:di -> PSP command line tail

		; Writing MS-DOS Device Drivers, second edition, page 349
		;  specifies the following as to the command line termination:
		; "Note that the DEVICE= command string is terminated by an
		;  Ah when there are no arguments. When there are arguments,
		;  the string is terminated with the following sequence:
		;  0h, Dh, Ah."

		; First skip past name.
@@:
	lodsb
	cmp al, 32			; blank ?
	je @F
	cmp al, 9
	je @F				; yes, got past executable filename -->
	cmp al, 0
	je .cmdline_end
	cmp al, 13
	je .cmdline_end
	cmp al, 10
	je .cmdline_end			; if empty tail -->
	jmp @B
@@:
	cmp di, 0FFh			; can store and still have space for CR ?
	je .cmdline_end_truncate	; no -->
	stosb				; store it
	cmp al, 0			; EOL ?
	je .cmdline_end_dec
	cmp al, 13
	je .cmdline_end_dec
	cmp al, 10
	je .cmdline_end_dec		; yes -->
	lodsb
	cmp al, '!'			; escape for small letters ?
	jne @B				; no -->
	lodsb
	cmp al, 0
	je .cmdline_end_escaped
	cmp al, 13
	je .cmdline_end_escaped
	cmp al, 10
	je .cmdline_end_escaped
	; cmp al, '!'			; (automatically supported)
	cmp al, 'A'			; is it a capital letter ?
	jb @B
	cmp al, 'Z'
	ja @B
	xor al, 'a' ^ 'A'		; get the small letter
	jmp @B

.cmdline_end_escaped:
	mov dx, imsg.device_end_escaped
	jmp @F

.cmdline_end_truncate:
	mov dx, imsg.device_end_truncate
 @@:
	call init_putsz_cs
	db __TEST_IMM8			; skip dec
.cmdline_end_dec:
	dec di
.cmdline_end:
	mov al, 13
	stosb				; store CR
	xchg ax, di
	 mov bx, es
	 mov ds, bx
	sub al, 82h			; if -> 82h (CR at 81h). get 0
	mov byte [80h], al		; store length

%if _CONFIG
	pop si
	pop dx
%endif

	cli
	 mov ss, bx
	 mov sp, stack_end		; device mode stack switch
	sti

	setopt [internalflags], tsrmode
	clropt [internalflags], attachedterm
	setopt [internalflags6], dif6_device_mode

%if _CONFIG
find_config_device:
	call init_device_get_environment

	mov byte [ss:configpath], 0
	push ax
	push dx
	push si
	mov di, imsg.varconfig
	mov cx, imsg.varconfig.length
	mov dx, configpath
	call init_copyvar
	pop si
	pop ds			; -> command line
	push ds
	push si
	jnc .done
	call find_executable_device
.done:
	pop si
	pop ds			; -> command line
	pop ax

find_scripts_device:
	mov byte [ss:scriptspath], 0
	push ds
	push si
	mov di, imsg.varscripts
	mov cx, imsg.varscripts.length
	mov dx, scriptspath
	call init_copyvar
	pop si
	pop ds			; -> command line
	jnc .done
	call find_executable_device
.done:

	push ss
	pop ds
	push ss
	pop es
%endif

	jmp old_initcode


init_device_get_environment:
	mov ah, 51h
	int 21h
	mov ds, bx
	mov ax, [2Ch]
	test ax, ax
	jnz @F
	mov ax, [16h]
	test ax, ax
	jz .zero
	cmp ax, -1
	je .zero
	cmp ax, bx
	mov ds, ax
	mov ax, [2Ch]
	jne @F
.zero:
	xor ax, ax
@@:
	retn


%if _CONFIG
		; INP:	ss:dx -> buffer
		;	ds:si -> device driver command line
find_executable_device:
@@:
	lodsb
	cmp al, 32
	je @B
	cmp al, 9
	je @B
	dec si
	mov di, si
@@:
	lodsb
	cmp al, 9
	je @F
	cmp al, 32
	je @F
	cmp al, 13
	je @F
	cmp al, 10
	je @F
	test al, al
	jnz @B
@@:
	dec si
	push ds
	pop es
	mov cx, si
	sub cx, di
	cmp cx, 127
	ja .nodevice
	xchg di, si

	mov cx, 0
@@:
	cmp di, si
	jbe @F
	dec di
	cmp byte [di], '/'
	je .slash
	cmp byte [di], '\'
	jne @B
.slash:
	mov cx, di
	inc cx
	sub cx, si
@@:

	push ss
	pop es
	mov di, dx
	rep movsb
	mov al, 0
	stosb

.nodevice:
.done:
	retn
%endif


init_device_error_late:
	testopt [internalflags], has386
	jz .16

subcpu 386
	mov eax, [reg_eax]
	mov ebx, [reg_ebx]
	mov ecx, [reg_ecx]
	mov edx, [reg_edx]
	mov esi, [reg_esi]
	mov edi, [reg_edi]
	mov ebp, [reg_ebp]
	push dword [reg_efl]
	popfd
	mov fs, [reg_fs]
	mov gs, [reg_gs]
subcpureset

.16:
		; ax done last
	mov bx, [reg_ebx]
	mov cx, [reg_ecx]
	mov dx, [reg_edx]
	mov si, [reg_esi]
	mov di, [reg_edi]
	mov bp, [reg_ebp]
	push word [reg_efl]
	popf
	mov es, [reg_es]
	mov ss, [reg_ss]
	mov sp, [reg_esp]
	push word [reg_eax]
	mov ax, ds
	mov ds, [reg_ds]

%ifn _LINK
	sub ax, strict word paras(deviceshim_size + 10h)
%else
	sub ax, strict word relocatedzero + paras(10h)
 wlcalc word_wrt_DEVICESHIM_extpara_behind_deviceshim, equ $ - 2
%endif
	mov word [es:bx + 3], 8103h	; error, done, code: unknown command
	and word [es:bx + 0Eh], 0
	mov word [es:bx + 0Eh + 2], ax	; -> behind memory in use
	pop ax
	retf
%endif


%if _DEBUG4 || _DEBUG5
%define _DEB_ASM_PREFIX init_
%include "deb.asm"
%endif


%if ! _LINK && ! _LINK_COMPAT
	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

%if _DUALCODE
	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_table2, PATCH_NO386_TABLE2
%undef PATCH_NO386_TABLE2

	align 2, db 0
		; Table of patches that are to be set NOP if running on a 386.
writepatchtable patch_386_table2, PATCH_386_TABLE2
%undef PATCH_386_TABLE2
%else
 %assign __patch_no386_table2_method 0
 %assign __patch_386_table2_method 0
%endif

	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_tableentry, PATCH_NO386_TABLEENTRY
%undef PATCH_NO386_TABLEENTRY

	align 2, db 0
		; Table of patches that are to be set NOP if running on a 386.
writepatchtable patch_386_tableentry, PATCH_386_TABLEENTRY
%undef PATCH_386_TABLEENTRY
%elif _LINK
%ifn _ONLY386 || _ONLYNON386
 extern patch_no386_table
 extern patch_no386_table.amount
%assign __patch_no386_table_method 1
 extern patch_386_table
 extern patch_386_table.amount
%assign __patch_386_table_method 1
%if _DUALCODE
 extern patch_no386_table2
 extern patch_no386_table2.amount
%assign __patch_no386_table2_method 1
 extern patch_386_table2
 extern patch_386_table2.amount
%assign __patch_386_table2_method 1
%else
%assign __patch_no386_table2_method 0
%assign __patch_386_table2_method 0
%endif
 extern patch_no386_tableentry
 extern patch_no386_tableentry.amount
%assign __patch_no386_tableentry_method 1
 extern patch_386_tableentry
 extern patch_386_tableentry.amount
%assign __patch_386_tableentry_method 1
%assign patch_386_tableentry_size 1	; placeholder
%assign patch_no386_tableentry_size 1	; placeholder
%endif
%endif

%if !_LINK && _LINK_COMPAT
%assign _patch_no386_table_FORCE_METHOD 1
%assign _patch_386_table_FORCE_METHOD 1
%assign _patch_no386_table2_FORCE_METHOD 1
%assign _patch_386_table2_FORCE_METHOD 1
%assign _patch_no386_tableentry_FORCE_METHOD 1
%assign _patch_386_tableentry_FORCE_METHOD 1

 %ifn _ONLY386 || _ONLYNON386
%assign __patch_no386_table_method 1
%assign __patch_386_table_method 1
  %if _DUALCODE
%assign __patch_no386_table2_method 1
%assign __patch_386_table2_method 1
  %else
%assign __patch_no386_table2_method 0
%assign __patch_386_table2_method 0
  %endif
%assign __patch_no386_tableentry_method 1
%assign __patch_386_tableentry_method 1
 %endif
%endif


%ifn _LINK || _LINK_COMPAT
%if _DUALCODE && ! _PM
	align 2, db 0
relocate_from_code:
	dw  PATCH_RELOCATE_FROM_lDEBUG_CODE
.end:
	align 2, db 0
relocate_from_code2:
	dw  PATCH_RELOCATE_FROM_lDEBUG_CODE2
.end:

%unimacro dualcall 1.nolist
	; make sure we do not allow later uses
%endif
%endif


%if _BOOTLDR
	align 16, db 0
	; Image identification
	; First dword: signature
	; Next word: version, two ASCII digits
	; Next word: checksum. adding up all words of the paragraph gives zero
	; Next word: size of image (including this paragraph)
	; Three words reserved, zero.
imageident:
%if !_LOADER
	db "NDEB00"
.check:	dw 0
.size:	dw 0
	times 3 dw 0
%else
	istruc LOADERIMAGEIDENT
at liiSignature
	db "NLDR00"
at liiChecksum
.check:	dw 0
at liiAmountParagraphs
.size:	dw 0
at liiReserved
	times 3 dw 0
at liiEntryQuerySize
	dw loader_query
at liiEntryRelocate
	dw loader_transfer_relocate
at liiEntryDebug
	dw loader_transfer_cmd3
at liiStackPointer
	dw stack_end
	iend
	times (2Ch - ($ - imageident)) / 2 dw 0
%endif
%endif
%if _DEVICE
	align 16, db 0
init_container_signature:
	fill 16, 0, db "FOR_SD_CONTAINER"
%endif
%if _EXTENSIONS
	align 16, db 0
eld_linkcall:
.:
	istruc ELD_INSTANCE
at eldiStartCode,	dw 0
at eldiEndCode,		dw .end - .
at eldiStartData,	dw 0
at eldiEndData,		dw 0
at eldiIdentifier,	fill 8, 32, db "LINKCALL"
at eldiFlags,		dw eldifResident
	iend
.to_entry:
	push bx
	mov bl, 0 * (1 + !!_PM)
	jmp @F
	align 8
.to_code1:
	push bx
	mov bl, 2 * (1 + !!_PM)
%if _DUALCODE
	jmp @F
	align 8
.to_code2:
	push bx
	mov bl, 4 * (1 + !!_PM)
%endif
@@:
	lframe 0
	lpar word, origret_farret_seg
	lpar word, origbx_farret_ofs
	lpar word, restore_back_nearret
	 push ax
	lpar dword, transfer
	 push ax
	 push ax
	lenter
	lvar word, restorebx
	 push word [bp + ?origbx_farret_ofs]
 %if _PM
	lvar word, restoredi
	 push di
 %endif
	mov bh, 0
	push word [ss:extcall_table.ret + bx]
	pop word [bp + ?restore_back_nearret]
 %if _PM
	mov di, [ss:pm_2_86m_0]
	mov bx, word [ss:extcall_table.segsel + bx + di]
 %else
	mov bx, word [ss:extcall_table.segsel + bx]
 %endif
	mov bx, word [ss:bx]
	mov word [bp + ?transfer + 2], bx

	mov bx, word [bp + ?origret_farret_seg]
	push word [cs:bx + 2]
	pop word [bp + ?transfer]
	mov word [bp + ?origbx_farret_ofs], bx
 %ifn _PM
	mov word [bp + ?origret_farret_seg], cs
 %endif
 %if _PM
	pop di		; ?restoredi
 %endif
	pop bx		; ?restorebx
	lleave ctx
	pop bp		; ?frame_bp
	retf		; ?transfer
			; still on stack: near return address, far return address

	times 79h - ($ - .) nop
.call_cmd3:
	call .to_code1
	jmp strict short $
	dw cmd3

	align 16
	times 128 - ($ - .) nop
.end:
	endarea eld_linkcall
%endif

%if _BOOTLDR
	align 2, db 0
loader_imageident:
.:
	db "NLDR00"
.size equ $ - .
%endif

	align 2, db 0
memsize:
%ifn _LINK
		dw paras(CODETARGET2 \
			+ ldebug_code_bootldr_truncated_size \
			+ ldebug_code2_size \
			+ historysegment_size \
			+ extsegment_size)
	; same as paras(AUXTARGET1
	;	+ auxbuff_size
	;	+ historysegment_size
	;	+ extsegment_size)
%else
 %if 0
%define CODETARGET1 (MESSAGESECTIONOFFSET + messagesegment_nobits_truncated_size + datastack_size)
%define CODETARGET2 (CODETARGET1+auxbuff_size)
%define AUXTARGET1 (CODETARGET1+ldebug_code_bootldr_truncated_size+ldebug_code2_size)
%define AUXTARGET2 CODETARGET1
%define AUXTARGET3 (AUXTARGET1+auxbuff_size)
 %endif
		dw  relocatedzero + paras(auxbuff_size \
			+ historysegment_size \
			+ extsegment_size)
 wlcalc word_segrel_MESSAGESEGMENT, equ $ - 2
 wlcalc word_wrt_MESSAGESEGMENT_extpara_behind_messagesegment_nobits_truncated, equ $ - 2
 wlcalc word_wrt_DATASTACKBASE_extpara_end_of_datastack, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code_truncated, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE2@_extpara_behind_code2, equ $ - 2
%endif

%if _DEV_ENV_SIZE || _APP_ENV_SIZE
mem_envsize:	dw 0
%endif
cmdline_buffer_start:
		dw cmdline_buffer
init_layout:
		dw init_layout_none
%if _DEVICE
dev_memory_end:	dw 0
%endif
init_auxbuff_want:
		dw _AUXBUFFSIZE
%if _EXTENSIONS
init_ext_want:	dw _EXT_CODE_DEFAULT_SIZE
init_extdata_want:	dw _EXT_DATA_DEFAULT_SIZE
%endif
%if _HISTORY_SEPARATE_FIXED && _HISTORY
init_history_want:	dw _HISTORY_WANT_SIZE
%endif

init_switch_p_low_pathsearch_high_guessextension:
init_switch_p_pathsearch:	db 0
init_switch_p_guessextension:	db 0
relocate_source:	dw 0
relocate_down:		dw 0
init_switch_r:		dw 0
init_base_size:		dw 0
init_target_size:	dw 0
init_switch_t_umblink:	dw 1		; Add UMBs to memory chain
			dw -1
			dw -1
init_switch_t_strategy:	dw 82h		; Last fit, high then low
			dw 82h
			dw 82h
%if _BOOTLDR
init_boot_new_memsizekib:	dw 0
init_boot_old_memsizekib:	dw 0

init_boot_ebdasize:	dw 0
init_boot_ebdasource:	dw 0
init_boot_ebdadest:	dw 0
init_boot_ebdaflag:	db 0
init_preserveloader:	db 0
%endif
init_switch_pw:		db 0FFh
init_switch_t:		db 0
init_switch_t_ignore:	db 0
init_switch_t_break:	db 0
init_switch_t_variable:	db 0		; /TV: do not relocate environment
%if _APP_ENV_SIZE || _DEV_ENV_SIZE
app_env_allocation:	db 0		; set to 0FFh if app's environment
					;  *is* in its own separate memory block
					;  (like it used to be always)
%endif


imsg:

%if _APPLICATION || _DEVICE
%assign ELD 0
%assign INTERC3 0
%assign PART1 0
%assign PART2 0
%assign PART3 1

%include "pathshar.asm"
%endif

.max:	asciz "MAX"
.min:	asciz "MIN"
.default:
	asciz "DEFAULT"
%if _CONFIG
.varconfig:	db "LDEBUGCONFIG="
.varconfig.length equ $ - .varconfig
.varscripts:	db "LDEBUGSCRIPTS="
.varscripts.length equ $ - .varscripts
%endif
%if _DEVICE && _DEV_ENV_SIZE || _APPLICATION && _APP_ENV_SIZE
.app_env_too_large:
.dev_env_too_large:
	asciz _PROGNAME,": Warning, too long environment! Not copying.",13,10
%endif
%if _APPLICATION || _DEVICE
.early_mem_fail:
	db _PROGNAME,": Failed to allocate memory!"
%endif
.crlf:
	asciz 13,10
.relocate_failure:
	asciz _PROGNAME,": Relocation failed.",13,10
.init_relocate_failure:
	asciz _PROGNAME,": Second init relocation failed, out of memory.",13,10
.early_reloc_fail:
	asciz _PROGNAME,": Failed to relocate, internal error!",13,10
%if _DEVICE
.dos_below_5:
	asciz " Note: DOS must be at least version 5.",13,10
.device_end_escaped:
	asciz _PROGNAME,": Error, got escaped command line end!",13,10
.device_end_truncate:
	asciz _PROGNAME,": Error, truncating too long command line!",13,10
%endif
%ifn _APPLICATION
.not_an_application:
	ascic _PROGNAME,": Error, no application mode included!",13,10
%endif
%if _APPLICATION || _DEVICE
%if _ALTVID
.noaltvid:
	asciz _PROGNAME,": Error, no alternative video adapter detected!",13,10
%endif
	align 2, db 0
.help.defaultfilename:
	db _FILENAME
.help.defaultfilename.length equ $ - .help.defaultfilename

	align 2, db 0
.help.1:
	fill_at_least 80, 0, asciz _PROGNAME,_VERSION,", debugger.",13,10

.help.1a:
	db 13,10
	db "Usage: "
	asciz
.help.2:
	db "[.COM] [/C=commands] [[drive:][path]progname.ext [parameters]]",13,10
	db 13,10
	db "  /C=commands",9,9,	"semicolon-separated list of commands (quote spaces)",13,10
%if _CONFIG
	db "  /IN",9,9,9,	"discard command line buffer, do not run config",13,10
%endif
%if _AUXBUFFSIZE != _AUXBUFFMAXSIZE
	db "  /A=MAX",9,9,	"expand auxiliary buffer to maximum, #"
		_autodigits _AUXBUFFMAXSIZE, 1, 1
		db " Bytes",13,10
	db "  /A=MIN",9,9,	"restrict auxiliary buffer to minimum, #"
		_autodigits _AUXBUFFSIZE, 1, 1
		db " Bytes",13,10
	db "  /A=number",9,9,	"set auxiliary buffer size to hex number of bytes",13,10
	db "  /A=#number",9,9,	"set auxiliary buffer size to decimal number of bytes",13,10
	db "  /A",9,9,9,	"alias for /A=MAX",13,10
%endif
%if _EXTENSIONS
	db "  /X=[MAX|MIN|number]",9,	"change ELD code buffer size, 0 to #65_520 Bytes",13,10
	db "  /Y=[MAX|MIN|number]",9,	"change ELD data buffer size, 0 to #"
 %ifn _LINK
%assign NUMBER ext_data_max_size_equate_early
		_autodigits NUMBER, 5, 1
	db " Bytes",13,10
 %else
 wlcalc word_minusext_ext_data_area, equ $
 wlcalc word_itoa_31_06_0Ah, equ $
	dw  relocatedzero + 0FFF0h
	times 4 db 0
	db " Bytes",13,10
 %endif
%endif
%if _HISTORY_SEPARATE_FIXED && _HISTORY
	db "  /H=[MAX|MIN|number]",9,	"change history buffer size, #260 to #65_520 Bytes",13,10
%endif
	db "  /B",9,9,9,	"run a breakpoint within initialisation",13,10
	db "  /P[+|-]",9,9,	"append ext to initial filename and search path",13,10
	db "  /F[+|-]",9,9,	"always treat executable file as a flat binary",13,10
	db "  /E[+|-]",9,9,	"for flat binaries set up Stack Segment != PSP",13,10
%if _VXCHG
	db "  /V[+|-]",9,9,	"enable/disable video screen swapping",13,10
%endif
%if _DEBUG && _DEBUG_COND
	db "  /D[+|-]",9,9,	"enable/disable debuggable mode",13,10
%endif
%if _ALTVID
	db "  /2[+|-]",9,9,	"enable/disable use alternate video adapter for output",13,10
%endif
	db "  progname.ext",9,9,"(executable) file to debug or examine",13,10
	db "  parameters",9,9,	"parameters given to program",13,10
	db 13,10
	db "For a list of debugging commands, run "
	asciz
.help.3:
	db " and type ? at the prompt.",13,10
	asciz
%endif
%if _ONLY386
.no386:	ascizline "Error: This ",_PROGNAME," build requires a 386 CPU or higher."
%elif _ONLYNON386
.386:	asciiline "Warning: This ",_PROGNAME," build is ignorant of 386 CPU specifics."
	ascizline 9," It does not allow access to the available 386-specific registers!"
%endif

%if _BOOTLDR_DISCARD_HELP
	align 2, db 0
 %ifn _LINK || _LINK_COMPAT
.boothelp_replacement: helppage bootdisc
 %else
.boothelp_replacement:
%define MESSAGE_INLINE
	%include "help/bootdisc.asm"
%undef MESSAGE_INLINE
	asciz
 %endif
	endarea .boothelp_replacement
%endif

%if _APPLICATION || _DEVICE
%if _SYMBOLIC
.switch_s_garbage:
	asciz "Ignoring garbage at end of /S switch!",13,10
.switch_s_error:
	asciz "Switch /S invalid content",13,10
%endif
.invalidswitch:
	db "Invalid switch - "
.invalidswitch_a:
	asciz "x",13,10
.switch_x_error:
	db "Switch /"
.switch_x_error_a:
	asciz "x invalid content",13,10
%endif
%if _CONFIG
 %if _APPLICATION
	align 2, db 0
.default_cmdline.app:
	db _APPSCRIPTPREFIX
	db "@if exists y ::CONFIG::",_APPSCRIPTNAME," :",_APPSCRIPTLABEL
	db " then y ::CONFIG::",_APPSCRIPTNAME," :",_APPSCRIPTLABEL
	db 13
.default_cmdline.app.length equ $ - .default_cmdline.app
	db 0
  %if .default_cmdline.app.length + 128 + 8 > _RC_BUFFER_SIZE
   %error Too large default app cmdline
  %endif
 %endif
 %if _DEVICE
	align 2, db 0
.default_cmdline.dev:
	db _DEVSCRIPTPREFIX
	db "@if exists y ::CONFIG::",_DEVSCRIPTNAME," :",_DEVSCRIPTLABEL
	db " then y ::CONFIG::",_DEVSCRIPTNAME," :",_DEVSCRIPTLABEL
	db 13
.default_cmdline.dev.length equ $ - .default_cmdline.dev
	db 0
  %if .default_cmdline.dev.length + 128 + 8 > _RC_BUFFER_SIZE
   %error Too large default dev cmdline
  %endif
 %endif
%endif
%if _BOOTLDR
	align 2, db 0
.default_cmdline.boot:
	db _BOOTSCRIPTPREFIX
	db "@if exists y ldp/",_BOOTSCRIPTNAME," :",_BOOTSCRIPTLABEL
	db " then y ldp/",_BOOTSCRIPTNAME," :",_BOOTSCRIPTLABEL
	asciz
.default_cmdline.boot.length equ $ - .default_cmdline.boot
  %if .default_cmdline.boot.length + 8 > _RC_BUFFER_SIZE
   %error Too large default boot cmdline
  %endif

%if _EXECUTABLE
.ldpslash:
	db "ldp/"
%endif
.rpl_detected:
	asciz "RPL detected! Currently unsupported.",13,10
.mismatch_detected:
	asciz "Mismatch in memory size detected! Internal error!",13,10
.boot_ebda_unexpected:
	asciz "EBDA at unexpected position.",13,10
.boot_error_out_of_memory:
	asciz "Out of memory!",13,10
.boot_error_internal:
	asciz "Internal error while relocating load image!",13,10
.booterror:
	asciz 13,10,_PROGNAME," boot error. Press any key to reboot.",13,10
.bootwarn:
	asciz 13,10,_PROGNAME," warning: "
.boot_warn_no_loader:
	asciz "Expected loader not found!",13,10
.boot_warn_invalid_loader:
	asciz "Expected loader is invalid!",13,10
.boot_warn_lazy_loader:
	asciz "Expected loader doesn't have a query function!",13,10
.boot_warn_loader_no_reloc:
	asciz "Loader doesn't provide a relocation function!",13,10
%endif
%if _DOSEMU
.dosemudate:	db "02/25/93"
%endif
%if _VDD && (_APPLICATION || _DEVICE)
.vdd:		asciz "DEBXXVDD.DLL"
.dispatch:	asciz "Dispatch"
.init:		asciz "Init"
.mouse:		db "MOUSE",32,32,32		; Looks like a device name
.andy:		db "Andy Watson"		; I don't know him and why he's inside the NTVDM mouse driver
	endarea .andy
.ntdos:		db "Windows NT MS-DOS subsystem Mouse Driver"	; Int33.004D mouse driver copyright string (not ASCIZ)
	endarea .ntdos

		; INP:	-
		; OUT:	CY if not NTVDM
		;	NC if NTVDM
		;	ds = es = cs
		; CHG:	ax, bx, cx, dx, di, si, bp, es, ds
isnt:
		mov ax, 5802h			; Get UMB link state
		int 21h
		xor ah, ah
		push ax				; Save UMB link state
		mov ax, 5803h			; Set UMB link state:
		mov bx, 1			;  Add UMBs to memory chain
		int 21h
		mov ah, 52h
		mov bx, -1
		int 21h				; Get list of lists
		inc bx				; 0FFFFh ?
		jz .notnt			; invalid -->
		cmp bx, 2			; would access word at 0FFFFh ?
		je .notnt			; yes, invalid -->
		mov ax, word [es:bx-3]		; First MCB
		push cs
		pop es				; reset es
.loop:
		mov ds, ax			; ds = MCB
		inc ax				; Now segment of memory block itself
		xor dx, dx
		xor bx, bx
		cmp byte [bx], 'Z'		; End of MCB chain?
		jne .notlast
		inc dx
		jmp short .notchain
 .notlast:
		cmp byte [bx], 'M'		; Valid MCB chain?
		jne .error
 .notchain:
		mov cx, [bx+3]			; MCB size in paragraphs
				; ax = current memory block
				; cx = size of current memory block in paragraphs
				; dx = flag whether this is the last MCB
				; ds = current MCB (before memory block)
		cmp word [bx+1], 8		; MCB owner DOS?
		jne .notfound_1
		cmp word [bx+8], "SD"		; MCB name "SD"?
		jne .notfound_1
.loopsub:
		mov ds, ax			; SD sub-segment inside memory block
		inc ax
		dec cx
		mov bp, word [bx+3]		; Paragraphs 'til end of SD sub-segment
				; ax = current SD sub-segment
				; cx = paragraphs from SD sub-segment start (ax) to current memory block end
				; ds = current SD sub-MCB (like MCB, but for SD sub-segment)
				; bp = current SD sub-segment size in paragraphs
		cmp cx, bp
		jb .notfound_1			; Goes beyond memory block, invalid -->
		cmp byte [bx], 'Q'		; NTVDM type 51h sub-segment ?
		jne .notfound_2			; no -->
		mov si, 8			; Offset of device name (if SD device driver sub-segment)
		mov di, imsg.mouse
		push cx
		mov cx, si			; length of name
		repe cmpsb			; blank-padded device name "MOUSE" ?
		pop cx
		jne .notfound_2			;  Device name doesn't match, try next SD sub-segment
		mov ax, ds
		inc ax
		mov ds, ax			; Segment of SD sub-segment
				; ds = current SD sub-segment
		mov ax, bp			; Leave paragraph value in bp
		test ax, 0F000h			; Would *16 cause an overflow?
		jnz .notfound_3			;  Then too large -->
		push cx
		mov cl, 4
		shl ax, cl			; *16
		pop cx
				; ax = current SD sub-segment size in byte
.andy:
		mov di, imsg.andy
		push cx
		mov cx, imsg.andy_size
		call findstring			; String "Andy Watson"?
		pop cx
		jc .notfound_3
.ntdos:
		mov di, imsg.ntdos
		push cx
		mov cx, imsg.ntdos_size
		call findstring			; String "Windows NT MS-DOS subsystem Mouse Driver"?
		pop cx
		jnc .found			; (NC)
.notfound_3:
		mov ax, ds
.notfound_2:
		cmp cx, bp
		je .notfound_1			; End of SD memory block, get next MCB
		add ax, bp			; Address next SD sub-MCB
		sub cx, bp
		jmp short .loopsub		; Try next SD sub-segment
.notfound_1:
		add ax, cx			; Address next MCB
		test dx, dx			; Non-zero if 'Z' MCB
		jz .loop			; If not at end of MCB chain, try next
		; jmp short .notnt		;  Otherwise, not found
 .error:
 .notnt:
		stc
.found:
		push cs
		pop ds				; restore ds

		pop bx				; saved UMB link state
		mov ax, 5803h
		pushf
		int 21h				; Set UMB link state
		popf
		retn

findstring:
		xor si, si
.loop:
		push si
		add si, cx
		jc .notfound_c
		dec si				; The largest offset we need for this compare
		cmp ax, si
 .notfound_c:
		pop si
		jb .return			; Not found if at top of memory block -->
		push di
		push si
		push cx
		repe cmpsb			; String somewhere inside program?
		pop cx
		pop si
		pop di
		je .return			;  Yes, proceed --> (if ZR, NC)
		inc si				; Increase pointer by one
		jmp short .loop			;  Try next address
.return:
		retn
%endif


		; Move paragraphs
		;
		; INP:	ax:0-> source
		;	dx:0-> destination
		;	cx = number of paragraphs
		; CHG:	-
		; Note:	Doesn't work correctly on HMA; doesn't always wrap to LMA either.
		;	Do not provide a wrapped/HMA source or destination!
init_movp:
	push cx
	push ds
	push si
	push es
	push di

	cmp ax, dx		; source above destination ?
	ja .up			; yes, move up (forwards) -->
	je .return		; same, no need to move -->
	push ax
	add ax, cx		; (expected not to carry)
	cmp ax, dx		; end of source is above destination ?
	pop ax
	ja .down		; yes, move from top down -->
	; Here, the end of source is below-or-equal the destination,
	;  so they do not overlap. In this case we prefer moving up.

.up:
	push ax
	push dx
.uploop:
	mov ds, ax
	mov es, dx
	xor di, di
	xor si, si		; -> start of segment
	sub cx, 1000h		; 64 KiB left ?
	jbe .uplast		; no -->
	push cx
	mov cx, 10000h /2
	rep movsw		; move 64 KiB
	pop cx
	add ax, 1000h
	add dx, 1000h		; -> next segment
	jmp short .uploop	; proceed for more -->
.uplast:
	add cx, 1000h		; restore counter
	shl cx, 1
	shl cx, 1
	shl cx, 1		; *8, paragraphs to words
	rep movsw		; move last part
	pop dx
	pop ax
	jmp short .return

.down:
	std			; _AMD_ERRATUM_109_WORKAROUND as below
.dnloop:
	sub cx, 1000h		; 64 KiB left ?
	jbe .dnlast		; no -->
	push ax
	push dx
	add ax, cx
	add dx, cx
	mov ds, ax		; -> 64 KiB not yet moved
	mov es, dx
	pop dx
	pop ax
	mov di, -2
	mov si, di		; moved from last word down
	push cx
	mov cx, 10000h /2
	rep movsw		; move 64 KiB
	pop cx
	jmp short .dnloop	; proceed for more -->
.dnlast:
	add cx, 1000h		; restore counter
	shl cx, 1
	shl cx, 1
	shl cx, 1		; *8, paragraphs to words
	mov di, cx
	dec di
	shl di, 1		; words to offset, -> last word
	mov si, di
	mov ds, ax
	mov es, dx		; first segment correct


	numdef AMD_ERRATUM_109_WORKAROUND, 1
%if 0

Jack R. Ellis pointed out this erratum:

Quoting from https://www.amd.com/system/files/TechDocs/25759.pdf page 69:

109   Certain Reverse REP MOVS May Produce Unpredictable Behavior

Description

In certain situations a REP MOVS instruction may lead to
incorrect results. An incorrect address size, data size
or source operand segment may be used or a succeeding
instruction may be skipped. This may occur under the
following conditions:

* EFLAGS.DF=1 (the string is being moved in the reverse direction).

* The number of items being moved (RCX) is between 1 and 20.

* The REP MOVS instruction is preceded by some microcoded instruction
  that has not completely retired by the time the REP MOVS begins
  execution. The set of such instructions includes BOUND, CLI, LDS,
  LES, LFS, LGS, LSS, IDIV, and most microcoded x87 instructions.

Potential Effect on System

Incorrect results may be produced or the system may hang.

Suggested Workaround

Contact your AMD representative for information on a BIOS update.

%endif

%if _AMD_ERRATUM_109_WORKAROUND
	jcxz @FF
	cmp cx, 20
	ja @FF
@@:
	movsw
	loop @B
@@:
%endif
	rep movsw		; move first part
	cld
.return:
	pop di
	pop es
	pop si
	pop ds
	pop cx
	retn


%if _BOOTLDR
		; only called for boot-loaded mode
init_getc_bootldr:
	xor ax, ax
	int 16h
	retn
%endif

init_putsz_cs:
	push ax
	push bx
	push cx
	push dx
	push ds
	push es
	push di
	 push cs
	 pop es
	 push cs
	 pop ds
	mov di, dx			; es:di-> string
	xor al, al
	mov cx, -1
	repne scasb			; search zero
	not cx
	dec cx				; cx = length of message
	pop di
	call init_puts_ds
	pop es
	pop ds
	pop dx
	pop cx
	pop bx
	pop ax
	retn

%if _BOOTLDR
init_putsz_cs_bootldr:
	push ax
	push bx
	push cx
	push dx
	push ds
	push es
	push di
	 push cs
	 pop es
	 push cs
	 pop ds
	mov di, dx			; es:di-> string
	xor al, al
	mov cx, -1
	repne scasb			; search zero
	not cx
	dec cx				; cx = length of message
	pop di
	call init_puts_ds_bootldr
	pop es
	pop ds
	pop dx
	pop cx
	pop bx
	pop ax
	retn
%endif

init_puts_ds:
%if _BOOTLDR
 %if _APPLICATION || _DEVICE
	testopt [ss:internalflags], nodosloaded
	jz @F
 %endif

init_puts_ds_bootldr:
	push si
	push bp
	mov si, dx
	jcxz .return
.loop:
	lodsb
	mov bx, 0007
	mov ah, 0Eh
	int 10h
	loop .loop
.return:
	pop bp
	pop si
	retn

@@:
%endif
%if _APPLICATION || _DEVICE
	mov bx, 1			; standard output
	mov ah, 40h			; write to file
	jcxz @F
	int 21h
@@:
	retn
%endif


%if _BOOTLDR
		; ds = ss = debugger data segment
		; (ds - 1) = image ident prefix paragraph
boot_old_initcode:
	cld

d4	call init_d4message
d4	asciz "In boot loader; press any key",13,10
d4	call init_d4pauseforkey

%if !_LOADER
	mov word [execblk.cmdline], 80h
	mov byte [81h], 0Dh
	mov byte [fileext], EXT_OTHER	; empty file name and command line as per N
%endif
%endif	; _BOOTLDR

old_initcode:
	cld
	d0bp
	mov ax, ds
%if !_LOADER
	mov word [execblk.cmdline + 2], ax
	mov word [execblk.fcb1 + 2], ax
	mov word [execblk.fcb2 + 2], ax	; set up parameter block for exec command
%endif
	mov word [pspdbg], ax

%if _IMMASM  && !_IMMASM_AUXBUFF
 %ifn _LINK
	add ax, (immasm_buffer + DATASECTIONFIXUP) >> 4
 %else
	add ax, strict word immasm_buffer
 wlcalc word_shr_4, equ $ - 2
 %endif
	mov word [immseg], ax
%endif

	push ds
	mov ax, 40h
	mov ds, ax
	mov ax, word [82h]	; end of circular keypress buffer
	mov dx, word [80h]	; start of circular buffer
	test ax, ax
	jz .forcekeybuffer
	test dx, dx
	jz .forcekeybuffer
	mov bx, ax
	sub bx, dx		; cmp end, start
	jbe .forcekeybuffer	; below or equal is invalid -->
	test bl, 1		; even amount of bytes ?
	jnz .forcekeybuffer	; no, invalid -->
	mov bx, word [1Ah]	; current head of circular buffer
	cmp bx, ax
	jae .forcekeybuffer
	sub bx, dx
	jb .forcekeybuffer
	test bl, 1
	jnz .forcekeybuffer	; invalid -->
	mov bx, word [1Ch]	; current tail of circular buffer
	cmp bx, ax
	jae .forcekeybuffer
	sub bx, dx
	jb .forcekeybuffer
	test bl, 1
	jz @F			; valid -->
.forcekeybuffer:
	pop ds
	mov word [io_end_buffer], 3Eh
	mov word [io_start_buffer], 1Eh
	db __TEST_IMM8		; (skip pop)
@@:
	pop ds

%if _BOOTLDR
 %if _APPLICATION || _DEVICE
	testopt [internalflags], nodosloaded
	jz .checkio
 %endif
d4	call init_d4message
d4	asciz "Common initialisation, determining processor type now",13,10
	jmp init_determineprocessor
.checkio:
%endif

%if _APPLICATION || _DEVICE
		; Check for console input vs. input from a file or other device.
		; This has to be done early because MS-DOS seems to switch CON
		; to cooked I/O mode only then.
	mov ax, 4400h		; IOCTL get device information
	xor bx, bx		; StdIn
	mov dl, 83h		; default if 21.4400 fails
	int 21h
	test dl, 80h
	jz .inputfile
	clropt [internalflags], inputfile
	test dl, 3
	jz .inputdevice		; if not the console input
	clropt [internalflags], notstdinput
	mov byte [notatty], 0	; it _is_ a tty
.inputdevice:
.inputfile:
	mov ax, 4400h		; IOCTL get device information
	inc bx			; StdOut
	mov dl, 83h		; default if 21.4400 fails
	int 21h
	test dl, 80h
	jz .outputfile
	clropt [internalflags], outputfile
	test dl, 3
	jz .outputdevice	; if not the console output
	clropt [internalflags], notstdoutput
.outputdevice:
.outputfile:

		; Check DOS version
%if _VDD
	push ds
	 push cs
	 pop ds
	 push cs
	 pop es
	call isnt		; NTVDM ?
	pop ds
	jc .isnotnt		; no -->
	setopt [internalflags], runningnt
.isnotnt:
%endif

	mov ax, 3000h		; check DOS version
	int 21h
	xchg al, ah
	cmp ax, ver(3,31)	; MS-DOS version > 3.30 ?
	jb .notoldpacket	; no -->
	setopt [internalflags], oldpacket	; assume Int25/Int26 packet method available
.notoldpacket:
	push ax
	xor bx, bx		; preset to invalid value
	mov ax, 3306h
	int 21h
	test al, al		; invalid, DOS 1.x error -->
	jz .213306invalid
	cmp al, -1		; invalid
.213306invalid:
	pop ax
	je .useoldver
	test bx, bx		; 0.0 ?
	jz .useoldver		; assume invalid -->
	xchg ax, bx		; get version to ax
	xchg al, ah		; strange Microsoft version format
.useoldver:
	cmp ax, ver(7,01)	; MS-DOS version > 7.00 ?
	jb .notnewpacket	; no -->
	setopt [internalflags], newpacket| oldpacket	; assume both packet methods available
.notnewpacket:
%if _VDD
	testopt [internalflags], runningnt
	jz .novdd
	push ds
	 push cs
	 pop ds
	 push cs
	 pop es
	mov si, imsg.vdd	; ds:si-> ASCIZ VDD filename
	mov bx, imsg.dispatch	; ds:bx-> ASCIZ dispatching entry
	mov di, imsg.init	; es:di-> ASCIZ init entry
	clc			; !
	RegisterModule		; register VDD
	pop ds
	jc .novdd		; error ? -->
	mov word [hVdd], ax
	setopt [internalflags], ntpacket| oldpacket	; assume old packet method also available
.novdd:
%endif
%endif


%if _CONFIG
do_truename_config:
	mov dx, configpath
	call do_truename
	mov word [configpath.dir_end], di
do_truename_scripts:
	mov dx, scriptspath
	call do_truename
	mov word [scriptspath.dir_end], di
%endif


init_determineprocessor:
d4	call init_d4message
d4	asciz "Determining processor type",13,10

	mov cx, 0121h
	shl ch, cl
	jnz .found_186_plus	; normal 186 masks shift count with 31 -->

		; To make it easier to trace past the long-form pop cx
		;  instruction, we now run it in a subfunction.
	call .detect_nec
	jcxz .found_186_plus	; if it was a nop -->
	jmp .cpudone		; is an actual 8088/8086 -->


		; INP:	-
		; OUR:	cx = 0 if NEC V20 or NEC V30
		;	cx = 1 else
		; CHG:	ax, cx
.detect_nec:
		; The NEC V20/V30 processors do support the 186 extensions
		;  to the instruction set but do not mask the shift count.
		;  Therefore, specifically detect them here. Based on the
		;  text in http://www.textfiles.com/hamradio/v20_bug.txt
	mov ax, sp
	mov cx, 1		; = 1 if on actual 8088/8086
	push cx
	dec cx			; = 0 if on NEC V20/V30

		; NB: Do *NOT* trace this instruction with Trace Flag = 1 and
		;  do *NOT* write a breakpoint at the mov sp instruction,
		;  that is the very next instruction after the pop cx.
		; Doing either leads to locking up the HP 95LX, requiring to
		;  reset the system using Ctrl-Shift-On (which zeroes the
		;  system date and time).
	db 8Fh, 0C1h		; pop r/m16 with cx as operand
				;  (reportedly a nop on the NECs)
	mov sp, ax		; reset stack to known state
	retn

.found_186_plus:
d4	call init_d4message
d4	asciz "Found 186+ processor",13,10
	inc byte [ machine ]	; 1
	push sp
	pop ax
	cmp ax, sp
	jne .cpudone		; 80186 pushes the adjusted value of sp -->

d4	call init_d4message
d4	asciz "Found 286+ processor",13,10
		; Determine the processor type.  This is adapted from code in the
		; Pentium<tm> Family User's Manual, Volume 3:  Architecture and
		; Programming Manual, Intel Corp., 1994, Chapter 5.  That code contains
		; the following comment:
		;
		; This program has been developed by Intel Corporation.
		; Software developers have Intel's permission to incorporate
		; this source code into your software royalty free.
		;
		; Intel 286 CPU check.
		; Bits 12-15 of the flags register are always clear on the
		; 286 processor in real-address mode.
		; Bits 12-15 of the FLAGS register are always set on the
		; 8086 and 186 processor.
	inc byte [ machine ]	; 2
	 pushf			; save IF
	pushf			; get original flags into ax
	pop ax
	or ax, 0F000h		; try to set bits 12-15
	and ax, ~0200h		; clear IF
	push ax			; save new flags value on stack
	popf			; replace current flags value; DI
	pushf			; get new flags
	pop ax			; store new flags in ax
	 popf			; restore IF (in 86 Mode)
	test ax, 0F000h		; if bits 12-15 clear, CPU = 80286
	jz .cpudone		; if 80286 -->

d4	call init_d4message
d4	asciz "Found 386+ processor",13,10
		; Intel 386 CPU check.
		; The AC bit, bit #18, is a new bit introduced in the EFLAGS
		; register on the Intel486 DX cpu to generate alignment faults.
		; This bit cannot be set on the Intel386 CPU.
		;
		; It is now safe to use 32-bit opcode/operands.
subcpu 386
	setopt [internalflags], has386
	inc byte [ machine ]	; 3

%if _DEVICE
 %if _APPLICATION || _BOOTLDR
	testopt [internalflags6], dif6_device_mode
	jz @F
 %endif

	mov word [reg_fs], fs
	mov word [reg_gs], gs
%macro set_gpr_h 1
	push e %+ %1
	pop %1
	pop word [reg_e %+ %1 + 2]
%endmacro
	set_gpr_h ax
	set_gpr_h bx
	set_gpr_h cx
	set_gpr_h dx
	set_gpr_h bp
	set_gpr_h si
	set_gpr_h di
		; esph and eiph remain zero
	pushfd
	popfw
	pop word [reg_efl + 2]
%endif
@@:

	mov bx, sp		; save current stack pointer to align
	and sp, ~3		; align stack to avoid AC fault
	pushfd			; push original EFLAGS
	pop eax			; get original EFLAGS
	mov ecx, eax		; save original EFLAGS in ECX (including IF)

	xor eax, 40000h		; flip AC bit in EFLAGS
	and ax, ~0200h		; clear IF
	push eax		; put new EFLAGS value on stack
	popfd			; replace EFLAGS value; DI
	pushfd			; get new EFLAGS
	pop eax			; store new EFLAGS value in EAX
	mov ax, cx		; ignore low bits (including IF)
	cmp eax, ecx
	je .cpudone_stack_eax_equals_ecx	; if 80386 -->

d4	call init_d4message
d4	asciz "Found 486+ processor",13,10
		; Intel486 DX CPU, Intel487 SX NDP, and Intel486 SX CPU check.
		; Checking for ability to set/clear ID flag (bit 21) in EFLAGS
		; which indicates the presence of a processor with the ability
		; to use the CPUID instruction.
	inc byte [ machine ]	; 4
	mov eax, ecx		; get original EFLAGS
	xor eax, 200000h	; flip ID bit in EFLAGS
	and ax, ~0200h		; clear IF
	push eax		; save new EFLAGS value on stack
	popfd			; replace current EFLAGS value; DI
	pushfd			; get new EFLAGS
	pop eax			; store new EFLAGS in EAX
	mov ax, cx		; ignore low bits (including IF)

.cpudone_stack_eax_equals_ecx:
	push ecx
	popfd			; restore AC,ID bits and IF in EFLAGS (86 Mode)
	mov sp, bx		; restore sp

	cmp eax, ecx		; check if it's changed
	je .cpudone		; if it's a 486 (can't toggle ID bit) -->

d4	call init_d4message
d4	asciz "Found processor with CPUID support",13,10
		; Execute CPUID instruction.
subcpu 486		; NASM (at least 2.10rc1) handles cpuid itself as a
			;  586+ instruction, but we know better. So this
			;  part is declared for 486 compatibility, and only
			;  the cpuid instructions are emitted with 586
			;  compatibility to appease NASM.
%if 0
d4	call init_d4message
d4	asciz "CPUID will NOT be executed, to work around official DOSBox releases",13,10
d4	jmp .cpudone
%endif
	xor eax, eax		; set up input for CPUID instruction
d4	call init_d4message
d4	asciz "Executing CPUID 0",13,10
	  [cpu 586]
	 cpuid
	  __CPU__
d4	call init_d4message
d4	asciz "CPUID 0 executed",13,10
	cmp eax, byte 1
	jb .cpudone		; if 1 is not a valid input value for CPUID
	xor eax, eax		; otherwise, run CPUID with eax = 1
	inc eax
d4	call init_d4message
d4	asciz "Executing CPUID 1",13,10
	  [cpu 586]
	 cpuid
	  __CPU__
d4	call init_d4message
d4	asciz "CPUID 1 executed",13,10
%if _MMXSUPP
	test edx, 80_0000h
	setnz byte [has_mmx]
%endif

	mov al, ah
	and al, 0Fh		; bits 8..11 are the model number
	cmp al, 6
	jb .below686		; if < 6
	mov al, 6		; if >= 6, set it to 6
.below686:
	mov byte [ machine ], al; save machine type (486, 586, 686+)

.cpudone:
subcpureset			; subcpu 486
subcpureset			; subcpu 386
d4	call init_d4message
d4	asciz "Determining floating-point unit",13,10

		; Next determine the type of FPU in a system and set the mach_87
		; variable with the appropriate value.  All registers are used by
		; this code; none are preserved.
		;
		; Coprocessor check.
		; The algorithm is to determine whether the floating-point
		; status and control words can be written to.  If not, no
		; coprocessor exists.  If the status and control words can be
		; written to, the correct coprocessor is then determined
		; depending on the processor ID.  The Intel 386 CPU can
		; work with either an Intel 287 NDP or an Intel 387 NDP.
		; The infinity of the coprocessor must be checked
		; to determine the correct coprocessor ID.
	mov al, byte [ machine ]
	mov byte [ mach_87 ], al	; by default, set mach_87 to machine
	inc byte [ has_87 ]
	mov byte [encodedmach87], 0Ch
	cmp al, 5			; a Pentium or above always will have a FPU
	jae .fpudone
	dec byte [ has_87 ]		; assume no FPU
	mov byte [encodedmach87], 0C0h

	fninit				; reset FPU
	mov al, -1			; initialise with a non-zero value
	push ax
	mov bx, sp
	fnstsw word [ss:bx]		; save FP status word
	pop ax				; retrieve it
	test al, al
	jnz .fpudone			; if no FPU present

		; al = 0 here
	push ax
	fnstcw word [ss:bx]		; save FP control word
	pop ax				; retrieve it
	and ax, 103Fh			; see if selected parts look OK
	cmp ax, byte 3Fh
	jne .fpudone			; if no FPU present
	inc byte [ has_87 ]		; there's an FPU
	mov byte [encodedmach87], 0Ch

		; If we're using a 386, check for 287 vs. 387 by checking whether
		; +infinity = -infinity.
	cmp byte [ machine ], 3
	jne .fpudone			; if not a 386
[cpu 386]
	fld1				; must use default control from FNINIT
	fldz				; form infinity
	fdivp ST1			; 1 / 0 = infinity
	fld ST0
	fchs				; form negative infinity
	fcompp				; see if they are the same and remove them
	fstsw ax
	sahf				; look at status from FCOMPP
	jne .fpudone			; if they are different, then it's a 387
	dec byte [ mach_87 ]		; otherwise, it's a 287
	mov byte [encodedmach87], 0C2h
__CPU__
.fpudone:

apply_patches:
%if _ONLY386
	testopt [internalflags], has386
	jnz @F				; okay -->

 %if _BOOTLDR && (_APPLICATION || _DEVICE)
	testopt [internalflags], nodosloaded
	lahf				; remember status
 %endif
	mov dx, imsg.no386
	call init_putsz_cs		; display the error
 %if _BOOTLDR && (_APPLICATION || _DEVICE)
	sahf
	jnz init_booterror.soft		; abort for loader -->
 %elif _BOOTLDR
	jmp init_booterror.soft
 %endif
%if _DEVICE && _APPLICATION
	testopt [internalflags6], dif6_device_mode
	jnz init_device_error_late
%elif _DEVICE
	jmp init_device_error_late
%endif
%if _APPLICATION
	mov ax, 4C01h
	int 21h				; abort our process
%endif

@@:
%elif _ONLYNON386
	testopt [internalflags], has386
	jz @F				; okay -->
	mov dx, imsg.386
	call init_putsz_cs		; display the warning
@@:
%endif

		; Determine which patch table to use, then patch
		; out either the 386+ or non-386 code as appropriate.
	mov es, [code_seg]
	testopt [internalflags], has386
	jz @F
	mov si, patch_386_table		; table of patches to set for 386+
%if __patch_386_table_method == 1
	mov cx, patch_386_table.amount
	call .patch1
%else
	call .patch2
%endif
	jmp .patch_code1_end

@@:
%ifn _ONLYNON386

%if _RUN2_ENTRY_SECTION
	mov byte [..@patch_no386_ds_code_or_entry], 3Eh
					; write a ds prefix
	mov byte [..@patch_no386_iret_code_or_entry], 0CFh
					; write an iret instruction
%else
	mov byte [es:..@patch_no386_ds_code_or_entry], 3Eh
					; write a ds prefix
	mov byte [es:..@patch_no386_iret_code_or_entry], 0CFh
					; write an iret instruction
%endif
 %if _PM && _CATCHPMINT214C
	mov byte [..@patch_no386_ds_entry_pmint21_1], 3Eh
	mov byte [..@patch_no386_ds_entry_pmint21_2], 3Eh
					; write ds prefixes (note the segment!)
 %endif
 %if _PM
	mov byte [es:..@patch_no386_ds_4], 3Eh
	mov byte [es:..@patch_no386_ds_5], 3Eh	; write some more ds prefixes
 %endif
 %if _CATCHINT07 || _CATCHINT0C || _CATCHINT0D
	mov byte [..@patch_no386_ds_6_DATA_ENTRY], 3Eh
					; write a ds prefix (note the segment!)
 %endif
%endif
	mov si, patch_no386_table	; table of patches to set for 16-bit CPU
%if __patch_no386_table_method == 1
	mov cx, patch_no386_table.amount
	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 \
	|| __patch_no386_table2_method == 2 || __patch_386_table2_method == 2 \
	|| __patch_no386_tableentry_method == 2 || __patch_386_tableentry_method == 2
.patch2:
	mov di, code_start		; initialise offset
	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
	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 \
	|| __patch_no386_table2_method == 1 || __patch_386_table2_method == 1 \
	|| __patch_no386_tableentry_method == 1 || __patch_386_tableentry_method == 1
.patch1:
	jcxz .patchesdone
.looppatch1:
	cs lodsw			; load address of patch
	xchg bx, ax			; (set bx = ax, CHG ax)
	mov byte [es:bx], 90h		; patch
	loop .looppatch1
%endif
.patchesdone:
	retn

.patch_code1_end:

%if _DUALCODE
	mov dx, es			; dx => code1 seg
%endif
 %if _BOOTLDR_DISCARD
	testopt [internalflags], nodosloaded
	jnz @F

  %if _AREAS && _AREAS_HOOK_CLIENT
   %ifn _LINK
	mov ax, ldebug_code_bootldr_truncated_size
   %else
	mov ax, behind_code_truncated wrt lDEBUG_CODE
 wlcalc word_add_15, equ $ - 2
 wlcalc word_clr_15, equ $ - 2
   %endif
	mov word [areas_sub + areastrucfunLinearEnd], ax
	mov word [areas_fun + areastrucfunLinearEnd], ax
  %endif
 %endif
%ifn _DUALCODE
@@:					; if bootloaded
%else
 %if _BOOTLDR_DISCARD
  %ifn _LINK
	add dx, ldebug_code_bootldr_truncated_size_p
  %else
	add dx, strict word relocatedzero + 0
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code_truncated, equ $ - 2
  %endif
					; if not bootloaded, use truncated
	jmp @FF
@@:					; if bootloaded
 %endif
  %ifn _LINK
	add dx, ldebug_code_size_p	; use untruncated
  %else
	add dx, strict word relocatedzero + 0
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code, equ $ - 2
  %endif
@@:
	mov word [code2_seg], dx
patch_relocate:

 %if ! _PM
	mov si, relocate_from_code
	mov di, relocate_from_code.end
	call .patch
	mov si, relocate_from_code2
	mov di, relocate_from_code2.end
 %endif
	push es
	mov es, dx
	pop dx
 %if ! _PM
	call .patch
	jmp .done

.loop:
	cs lodsw
	xchg bx, ax
	mov word [es:bx], dx
.patch:
	cmp si, di
	jb .loop
	retn

.done:
 %endif

	testopt [internalflags], has386
	jz @F
	mov si, patch_386_table2	; table of patches to set for 386+
%if __patch_386_table2_method == 1
	mov cx, patch_386_table2.amount
	call apply_patches.patch1
%else
	call apply_patches.patch2
%endif
	jmp .patch_code2_end

@@:
	mov si, patch_no386_table2	; table of patches to set for 16-bit CPU
%if __patch_no386_table2_method == 1
	mov cx, patch_no386_table2.amount
	call apply_patches.patch1
%else
	call apply_patches.patch2
%endif
.patch_code2_end:
%endif


patch_entry:
	numdef EMIT_ENTRY_PATCH, _LINK || _LINK_COMPAT
%ifn _EMIT_ENTRY_PATCH
 %if patch_386_tableentry_size != 0 || patch_no386_tableentry_size != 0
  %assign _EMIT_ENTRY_PATCH 1
 %endif
%endif
%if _EMIT_ENTRY_PATCH
	push ds
	pop es
	testopt [internalflags], has386
	jz @F
	mov si, patch_386_tableentry	; table of patches to set for 386+
%if __patch_386_tableentry_method == 1
	mov cx, patch_386_tableentry.amount
	call apply_patches.patch1
%else
	call apply_patches.patch2
%endif
	jmp .patch_entry_end

@@:
	mov si, patch_no386_tableentry	; table of patches to set for 16-bit CPU
%if __patch_no386_tableentry_method == 1
	mov cx, patch_no386_tableentry.amount
	call apply_patches.patch1
%else
	call apply_patches.patch2
%endif
.patch_entry_end:
%endif


%if _DEVICE
		; This must be done after CPU detection
		;  because we want to get the high parts
		;  of the registers only initialised here.
	push ds
	pop es
	mov si, regs
	mov di, device_quittable_regs
 %ifn _LINK
	mov cx, words(regs.size)
 %else
	mov cx, regs.size + 1
 wlcalc word_shr_1, equ $ - 2
 %endif
	rep movsw
%endif


		; Check for dosemu. This is done for the boot loaded instance
		; too, as we might be running as DOS inside dosemu.
%if _DOSEMU
	mov ax, 0F000h
	mov es, ax
	push ds
	 push cs
	 pop ds			; avoid "repe cs cmpsw" (8086 bug)
	mov di, 0FFF5h
	mov si, imsg.dosemudate
	mov cx, 4
	repe cmpsw		; running in DosEmu?
	pop ds
	jne .dosemuchecked
	setopt [internalflags], runningdosemu
 %if _DOSEMU_PRESTROKES_HELP
	mov ax, 0009h
	int 0E6h		; run dosemu2 start_pre_strokes function
; dosemu2 recently gained a feature that tries to avoid
;  a kernel from eating the first text byte of an -input
;  string. However, this interferes with our use because
;  the strokes are only enabled in dos_post_boot (int.c).
; There is a new int 0E6h helper function now to enable
;  the strokes right away, which is made for lDebug to work
;  when the debugger is booted and wants the input. This
;  is called DOS_HELPER_PRESTROKES_START (also int.c).
; Reference: https://github.com/dosemu2/dosemu2/issues/2071
 %endif
.dosemuchecked:
%endif

	push ds
	pop es			; => lDEBUG_DATA_ENTRY

	mov di, line_in
	mov al, 255
	stosb
	mov al, 0
	stosb
	mov al, 13
	stosb				; overwrite line_in beginning

	mov sp, stack_end		; stack pointer (paragraph aligned)
	mov di, ..@init_first
%ifn _LINK
	mov cx, ..@init_behind - ..@init_first
%else
	mov cx, ..@init_behind
 wlcalc word_minusext_..@init_first, equ $ - 2
%endif
	xor ax, ax
	rep stosb			; initialise breakpoint lists, line_out
%if 1
..@init_behind_equate equ ..@init_behind
stack_equate equ stack
%if ..@init_behind_equate != stack_equate
	mov di, stack
%endif
%ifn _LINK
	mov cx, stack_end - stack
%else
	mov cx, stack_end
 wlcalc word_minusext_stack, equ $ - 2
%endif
	mov al, 5Eh
	rep stosb			; initialise the stack
%endif

	mov byte [ trim_overflow ], '0'	; initialise line_out so the trimputs loop doesn't overflow
	mov word [line_out_overflow], 2642h


%if _AREAS_HOOK_SERVER
	mov ax, ds
	mov word [ddebugareas.next + 2], ax
	mov word [ddebugareas.prev + 2], ax
	mov word [..@patch_entry_seg], ax
%endif

%if _AREAS && _AREAS_HOOK_CLIENT
	mov ax, word [code_seg]
	call add_to_areas_linear_code1

 %if _DUALCODE && _EXPRDUALCODE
	mov ax, word [code2_seg]
	call add_to_areas_linear_code2
 %endif
%endif


%if _DEBUG && _DEBUG_COND && _DEBUG_COND_DEFAULT_ON
	setopt [internalflags6], dif6_debug_mode
	setopt [options6], opt6_debug_mode
	setopt [startoptions6], opt6_debug_mode
%endif

%if _CATCHINT06 && _DETECT95LX
	mov ax, 4DD4h
	xor bx, bx
	int 15h				; HP 95LX/100LX/200LX detect
	cmp bx, 4850h			; "HP" reversed
	jne @F
	cmp cx, 0101h			; 95LX ?
	jne @F				; no -->

 %if _CATCHINT2D
	mov ax, word [inttab.i2D + 1]
	mov word [inttab.i06 + 1], ax	; overwrite i06 entry with i2D
	mov al, byte [inttab.i2D]
	mov byte [inttab.i06], al	; interrupt number too
 %endif
	dec word [inttab_number_variable]	; remember one less in use
@@:
%endif

	mov ah, 0Fh
	int 10h
	mov byte [vpage], bh


%if _BOOTLDR
 %if _APPLICATION || _DEVICE
	testopt [internalflags], nodosloaded
	jz initdos
 %endif
d4	call init_d4message
d4	asciz "386-related patches applied, boot initialisation proceeding",13,10


%if CATCHINTAMOUNT
		; Set up interrupt vectors.

		; ds still => lDEBUG_DATA_ENTRY
%if _CATCHINT06 && _DETECT95LX
	mov cx, word [inttab_number_variable]
%else
	mov cx, inttab_number
%endif
	jcxz .bootintend
	mov si, inttab
.bootintloop:

		; assumes ss = lDEBUG_DATA_ENTRY
	ss lodsb
	xor bx, bx
	mov ds, bx
	mov bl, al
	add bx, bx
	add bx, bx
	xchg ax, di
	ss lodsw		; get address of IISP header
	xchg ax, di
%if _DEBUG && !_DEBUG_COND
				; vectors are set only when debuggee runs
%else
 %if _DEBUG
	testopt [ss:internalflags6], dif6_debug_mode
	jnz @F
 %endif
	push word [ bx+2 ]
	push word [ bx ]	; get vector
	pop word [ ss:di + ieNext ]
	pop word [ ss:di + ieNext + 2 ]
				; store it
	mov word [ bx+2 ], ss
	mov word [ bx ], di	; set interrupt vector
@@:
%endif
	loop .bootintloop

.bootintend:
%endif

%if _EXTENSIONS
init_eld_boot:
	call init_eld
%else
	push ss
	pop ds
	push ss
	pop es
%endif

d4	call init_d4message
d4	asciz "Jumping to final boot initialisation code",13,10
	mov si, initcont.boot_entry
	push word [code_seg]
	push si
	retf
%endif

%if _APPLICATION || _DEVICE
initdos:
%if _MCB || _INT || _EXTENSIONS
	mov ax, 5802h
	int 21h
	xor ah, ah		; some "DOS" only return al
	push ax			; save UMB link

getfirstmcb:
	mov ah, 52h		; get list of lists
	mov bx, -1
	int 21h
	cmp bx, -1
	je mcb_not_found
	cmp bx, 1
	je mcb_not_found
	mov ax, word [ es:bx-2 ]; start of MCBs
	mov word [firstmcb], ax

getfirstumcb:
	push ds
			; We try to get the first UMCB for gateout
			;  for now. To harden our code it should
			;  not be assumed that the address is of
			;  a valid MCB. However, it is fine to
			;  compare an actual MCB address with it.
  %if _GUARD_86M_INT2F
	push es
	xor ax, ax
	mov es, ax		; (only used in 86 Mode)
	mov ax, [es:2Fh * 4]
	cmp ax, -1
	je @F			; --> (ZR)
	or ax, [es:2Fh * 4 + 2]
@@:
	pop es
	jz .determine
  %endif
	mov ax, 1261h		; PTS-DOS: Get first UMCB
	stc
	int 2Fh
	jc .determine		; not supported -->
	inc ax
	cmp ax, byte 2		; -1, 0, 1 ?
	jbe .determine		; not supported (or none) -->
	dec ax
	mov word [ firstumcb ], ax	; set UMB
	jmp short .got		; got it -->

.determine:
	mov ax, 5803h
	xor bx, bx
	int 21h			; disable UMB link, leave only LMA chain
	jc .none		; that isn't supported either -->

	mov ax, word [firstmcb]
	mov dx, ax		; first MCB
	xor bx, bx		; use offsets from bx, not addresses
.looplmb:
	mov ds, ax
	inc ax
	add ax, word [ bx + 3 ]	; next MCB's address
	cmp byte [ bx ], 'M'
	je .looplmb		; not last -->
	cmp byte [ bx ], 'Z'
	jne .none		; corrupted -->
	xchg ax, dx		; dx = what we assume to be the first UMA chain MCB
				; ax = first MCB

	push ax
	inc bx			; = 1
	mov ax, 5803h
	int 21h			; enable UMB link, include UMA chain
	pop ax
	jc .none		; so we can disable it but not enable? -->

	dec bx			; = 0
	xor cx, cx		; flag if assumed first UMCB found
.loopumb:
	cmp ax, dx
	jne .notlastlmb
	inc cx			; there it is
.notlastlmb:
	mov ds, ax
	cmp byte [ bx ], 'M'
	jne .islastumb?		; last or corrupted -->
	inc ax
	add ax, word [ bx + 3 ]
	jmp short .loopumb	; process next -->
.islastumb?:
	cmp byte [ bx ], 'Z'
	jne .none		; corrupted -->
	jcxz .none		; didn't find that UMCB -->
			; The MCB at dx which was behind the one that contained the 'Z'
			; signature when we disabled the UMB link is now a valid MCB in
			; the MCB chain after we enabled the UMB link. All previous MCBs
			; are now 'M'.
	pop ds
	mov word [ firstumcb ], dx
	db __TEST_IMM8		; (skip pop)
.none:
.got:
	pop ds
mcb_not_found:
	pop bx
	mov ax, 5803h
	int 21h			; restore UMB link
%endif

getindosflag:
	mov ah, 34h
	int 21h
	mov word [pInDOS + so16aOffset], bx
	mov word [pInDOS + so16aSegSel], es
%if _PM
	mov word [pInDOS + so16aSegment], es
%endif

		; get address of DOS swappable DATA area
		; to be used to get/set PSP and thus avoid DOS calls
		; will not work for DOS < 3
%if _USESDA
getsda:
	push ds
	mov ax, 5D06h
	stc				; initialise to CY
	int 21h
	mov ax, ds
	pop ds
	jc .noSDA			; if CY returned, not supported -->
	mov es, ax			; es:si -> SDA
 %if _DEVICE
	push ax
	mov ah, 51h
	int 21h				; bx = current PSP
	pop ax
 %else
	mov bx, ds			; bx = our PSP (= current PSP in app mode)
 %endif
	cmp word [es:si + 10h], bx	; does this seem like the current PSP field ?
	jne .noSDA			; no -->
	mov word [pSDA + so16aOffset], si
	mov word [pSDA + so16aSegSel], ax
%if _PM
	mov word [pSDA + so16aSegment], ax
%endif
.noSDA:
%endif

	mov ax, 3531h
	int 21h
	mov bx, es
%if _USESDA
	cmp bx, word [pSDA + so16aSegSel]
	jne @F
%endif
	cmp bx, word [pInDOS + so16aSegSel]
	jne @F

	setopt [internalflags2], dif2_int31_segment
@@:


commandline:
	push ss
	pop es

%if _CONFIG
%if _APPLICATION
	mov si, imsg.default_cmdline.app
	mov cx, words(imsg.default_cmdline.app.length)
%endif
%if _DEVICE
 %if _APPLICATION
	testopt [internalflags6], dif6_device_mode
	jz @F
 %endif
	mov si, imsg.default_cmdline.dev
	mov cx, words(imsg.default_cmdline.dev.length)
%endif
@@:

	push cs
	pop ds
	mov di, cmdline_buffer
	rep movsw
	cmp byte [es:di - 1], 0
	jne @F
	dec di
@@:
	mov [cmdline_buffer_start], di
	mov al, 0
	stosb
	push ss
	pop ds
	setopt [internalflags3], dif3_input_cmdline
%endif

		; Interpret switches and erase them from the command line.
	mov ax, 3700h			; get switch character
	mov dl, '/'			; preset with default value
	int 21h
	mov byte [ switchar ], dl
	cmp dl, '/'
	jne .notslash
	mov byte [ swch1 ], dl
.notslash:
	mov si, DTA+1
%if _MS_N_COMPAT
	mov byte [si - 1], '0'	; avoid kk underflow before start of tail
%endif
.blankloop:
	lodsb
	cmp al, 32
	je .blankloop
	cmp al, 9
	je .blankloop

		; Process the /? switch (or the [switchar]? switch).
		; If switchar != / and /? occurs, make sure nothing follows.
	cmp al, byte [switchar]
	je .switch		; if switch character -->
	cmp al, '/'
	jne .noswitches		; if not the help switch -->
	mov al, byte [ si ]
	cmp al, '?'
	jne .noswitches		; if not /?
	mov al, byte [ si+1 ]
	cmp al, 32
	je .help		; if nothing after /?
	cmp al, 9
	je .help		; ditto
	cmp al, 13
	jne .noswitches		; if something after /? -->

		; Print a help message
.help:
	push ds
%if _DEVICE
 %if _APPLICATION
	testopt [internalflags6], dif6_device_mode
	jz .help_not_device
 %endif

		; We modify the device command line here.
		;  Is that wise? Seems to work though.
	mov si, word [reg_ebx]
	mov ds, word [reg_es]
	lds si, [si + 12h]
	push si
@@:
	lodsb
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 0
	je @F
	cmp al, 13
	je @F
	cmp al, 10
	je @F
	jmp @B

@@:
	mov byte [si - 1], 0
	pop si
	jmp .help_common

.help_not_device:
%endif
%if _APPLICATION
	mov ax, word [2Ch]	; => environment
	test ax, ax
	jz .help.no_name
	mov ds, ax
	xor si, si
@@:
	lodsb
	test al, al
	jnz @B
	lodsb
	test al, al
	jnz @B
	lodsw
	cmp ax, 1
	jne .help.no_name
%endif
.help_common:
@@:
	mov bx, si
@@:
	lodsb
	cmp al, 'a'
	jb @F
	cmp al, 'z'
	ja @F
	sub byte [si - 1], 'a' - 'A'
@@:
	cmp al, '\'
	je @BBB
	cmp al, '/'
	je @BBB
	test al, al
	jnz @BB

	mov cx, si
	dec cx
	sub cx, bx

@@:
	dec si
	cmp si, bx
	jb @F
	cmp byte [si], '.'
	jne @B

	mov cx, si
	sub cx, bx
@@:
	jcxz .help.no_name
@@:
	mov dx, imsg.help.1	; command-line help message
	call init_putsz_cs
	mov dx, imsg.help.1a
	call init_putsz_cs
	push bx
	mov dx, bx
	call init_puts_ds
	mov dx, imsg.help.2
	call init_putsz_cs
	pop dx
	call init_puts_ds
	mov dx, imsg.help.3
	call init_putsz_cs
	pop ds
.helpterminate:
%if _DEVICE && _APPLICATION
	testopt [internalflags6], dif6_device_mode
	jnz init_device_error_late
%elif _DEVICE
	jmp init_device_error_late
%endif
%if _APPLICATION
	mov ax, 4C00h
	int 21h			; done
%endif

.help.no_name:
	push cs
	pop ds
	mov bx, imsg.help.defaultfilename
	mov cx, imsg.help.defaultfilename.length
	jmp @B


		; Do the (proper) switches.
.switch:lodsb
	cmp al,'?'
	je .help		; if -?
	call init_capitalise
%if _EXTHELP
	cmp al, 'U'
	je switch_u
%endif
%if _CONFIG
	cmp al, 'I'
	je .switch_i
%endif
	cmp al, 'C'
	je .switch_c
	cmp al, 'P'
	je .switch_p
	cmp al, 'F'
	je .switch_f
	cmp al, 'E'
	je .switch_e
	cmp al, 'B'
	je .switch_b
	cmp al, 'A'
	je .switch_a
	cmp al, 'T'
	je .switch_t
	cmp al, 'R'
	je .switch_r
%if _VXCHG
	cmp al, 'V'
	je .switch_v
%endif
%if _ALTVID
	cmp al, '2'
	je .switch_2
%endif
%if _MCLOPT
	cmp al, 'M'
	je .switch_m
%endif

%if _SYMBOLIC
	cmp al, 'S'
	je .switch_s
%endif

%if 1 || (_DEBUG && _DEBUG_COND)
	cmp al, 'D'
	je .switch_d
%endif

%if _EXTENSIONS
	cmp al, 'X'
	je .switch_x
	cmp al, 'Y'
	je .switch_y
%endif
%if _HISTORY_SEPARATE_FIXED && _HISTORY
	cmp al, 'H'
	je .switch_h
%endif

		; Other switches may go here.
	mov [ cs:imsg.invalidswitch_a ], al
	mov dx, imsg.invalidswitch	; Invalid switch
..@init_cmdline_error:
	call init_putsz_cs	; print string
%if _DEVICE && _APPLICATION
	testopt [internalflags6], dif6_device_mode
	jnz init_device_error_late
%elif _DEVICE
	jmp init_device_error_late
%endif
%if _APPLICATION
	mov ax, 4C01h		; Quit and return error status
	int 21h
%endif

..@init_cmdline_switch_error:
	mov byte [cs:imsg.switch_x_error_a], al
	mov dx, imsg.switch_x_error
	jmp ..@init_cmdline_error


%if _CONFIG
.switch_i:
	lodsb
	cmp al, 'N'
	je @F
	cmp al, 'n'
	je @F
	mov al, 'I'
	jmp ..@init_cmdline_switch_error

@@:
	clropt [internalflags3], dif3_input_cmdline
	mov byte [cmdline_buffer], 0
	mov word [cs:cmdline_buffer_start], cmdline_buffer
	jmp .blankloop
%endif

.switch_c:
@@:
	lodsb
	cmp al, '='
	je @B
	cmp al, ':'
	je @B

	mov di, [cs:cmdline_buffer_start]
	mov ah, 0		; initialise to 0 = unquoted
	db __TEST_IMM16
.switch_c_loop:
	stosb
.switch_c_quoted:
	lodsb
.switch_c_loop_after_semicolon:

	cmp al, 13
	je .switch_c_eol
	cmp al, ah		; close quote mark ?
	jne @F			; no -->
	cmp al, 0
	je .switch_c_eol
	mov ah, 0		; continue unquoted
	jmp .switch_c_quoted	; and load next character -->

@@:
	test ah, ah		; currently quoted ?
	jnz .switch_c_not_blank	; yes -->

	cmp al, '"'		; open quote mark ?
	je @F
	cmp al, "'"
	jne @FF			; no -->
@@:
	mov ah, al		; remember quoted state
	jmp .switch_c_quoted	; and load next character -->

@@:
	cmp al, 32		; blank while unquoted ?
	je .unquoted_blank
	cmp al, 9
	je .unquoted_blank	; yes -->
.switch_c_not_blank:
	cmp al, ';'		; unescaped semicolon ?
	jne .switch_c_not_semicolon
	mov al, 13		; yes, replace by CR
	stosb
	test ah, ah
	jz .switch_c_quoted
	call init_skipwhite
	jmp .switch_c_loop_after_semicolon

.switch_c_not_semicolon:
	cmp al, '\'		; escape ?
	jne .switch_c_loop	; no, store literal -->
	lodsb			; load escaped character
				;  (may be backslash, semicolon, quote)
	cmp al, 13		; guard against EOL
	jne .switch_c_loop
.switch_c_error:
	mov al, 'C'
	jmp ..@init_cmdline_switch_error

.switch_c_eol:
	test ah, ah		; in quoted state ?
	jnz .switch_c_error	; yes, error -->
.unquoted_blank:
	mov al, 0
	stosb			; terminate command line buffer
	setopt [internalflags3], dif3_input_cmdline
	dec si
	jmp .blankloop


.switch_b:
	mov byte [cs:.breakpoint], 0CCh	; SMC in section init, set point
	jmp .blankloop


.switch_p:
	lodsb
	call init_capitalise
	mov bx, 0FFFFh		; or 0FFFFh
	mov dx, bx		; and 0FFFFh
	dec si
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 13
	je @F
	inc si
	cmp al, '+'
	je @F
	inc dx			; and 0000h
	cmp al, '-'
	je @F

.switch_p_not_plusminus:
	cmp al, 'S'
	jne .switch_p_not_s
	mov bh, 00h		; or 0FFh
	dec dx			; and 0FFFFh
	lodsb
	dec si
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 13
	je @F
	inc si
	cmp al, '+'
	je @F
	mov dl, 00h		; and 0FF00h
	cmp al, '-'
	je @F
	jmp .switch_p_error

.switch_p_not_s:
	cmp al, 'E'
	jne .switch_p_not_e
	mov bl, 00h		; or 0FF00h
	dec dx			; and 0FFFFh
	lodsb
	dec si
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 13
	je @F
	inc si
	cmp al, '+'
	je @F
	mov dh, 00h		; and 00FFh
	cmp al, '-'
	je @F
	jmp .switch_p_error

.switch_p_not_e:
	cmp al, 'W'
	jne .switch_p_not_w
		; bl = 0FFh
	lodsb
	dec si
	cmp al, 32
	je @FF
	cmp al, 9
	je @FF
	cmp al, 13
	je @FF
	inc si
	cmp al, '+'
	je @FF
	mov bl, 00h
	cmp al, '-'
	je @FF
	; jmp .switch_p_error

.switch_p_not_w:
.switch_p_error:
	mov al, 'P'
	jmp ..@init_cmdline_switch_error
@@:
	or [cs:init_switch_p_low_pathsearch_high_guessextension], bx
	and [cs:init_switch_p_low_pathsearch_high_guessextension], dx
	jmp .blankloop

@@:
	mov [cs:init_switch_pw], bl
	jmp .blankloop


.switch_f:
	lodsb
	mov bx, opt6_flat_binary + opt6_big_stack
	dec si
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 13
	je @F
	mov bx, opt6_flat_binary
	inc si
	cmp al, '+'
	je @F
	xor bx, bx
	cmp al, '-'
	je @F
.switch_f_error:
	mov al, 'F'
	jmp ..@init_cmdline_switch_error
@@:
	clropt [options6], opt6_flat_binary
	or word [options6], bx
	jmp .blankloop

.switch_e:
	lodsb
	mov bx, opt6_big_stack
	dec si
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 13
	je @F
	inc si
	cmp al, '+'
	je @F
	xor bx, bx
	cmp al, '-'
	je @F
.switch_e_error:
	mov al, 'E'
	jmp ..@init_cmdline_switch_error
@@:
	clropt [options6], opt6_big_stack
	or word [options6], bx
	jmp .blankloop

%if _MCLOPT
.switch_m:
	lodsb
	mov bl, 20h
	dec si
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 13
	je @F
	inc si
	cmp al, '+'
	je @F
	mov bl, 8
	cmp al, '-'
	je @F
.switch_m_error:
	mov al, 'M'
	jmp ..@init_cmdline_switch_error
@@:
	mov byte [master_pic_base], bl
	jmp .blankloop
%endif

%if _VXCHG
.switch_v:
	lodsb
	mov bx, opt6_vv_mode
	dec si
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 13
	je @F
	inc si
	cmp al, '+'
	je @F
	xor bx, bx
	cmp al, '-'
	je @F
.switch_v_error:
	mov al, 'V'
	jmp ..@init_cmdline_switch_error
@@:
	clropt [options6], opt6_vv_mode
	or word [options6], bx
	jmp .blankloop
%endif

%if _DEBUG && _DEBUG_COND
.switch_d:
	lodsb
	mov bx, dif6_debug_mode
	dec si
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 13
	je @F
	inc si
	cmp al, '+'
	je @F
	xor bx, bx
	cmp al, '-'
	je @F
.switch_d_error:
	mov al, 'D'
	jmp ..@init_cmdline_switch_error
@@:
	clropt [internalflags6], dif6_debug_mode
	clropt [options6], opt6_debug_mode
	or word [internalflags6], bx
	or word [options6], bx
 %if dif6_debug_mode != opt6_debug_mode
  %error Mismatch of flag and option
 %endif
	jmp .blankloop
%else
.switch_d:
	lodsb
 %if _DEBUG
	dec si
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 13
	je @F
	inc si
	cmp al, '+'
@@:
 %else
	cmp al, '-'
 %endif
	je .blankloop
	mov al, 'D'
	jmp ..@init_cmdline_switch_error
%endif


%if _ALTVID
.switch_2:
	lodsb
	mov bl, 1Eh			; "push ds"
	dec si
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 13
	je @F
	inc si
	cmp al, '+'
	je @F
	mov bl, 0C3h			; "retn"
	cmp al, '-'
	je @F
.switch_2_error:
	mov al, '2'
	jmp ..@init_cmdline_switch_error
@@:
	mov es, [code_seg]
	mov byte [es:setscreen], bl
	push ss
	pop es
	cmp bl, 0C3h
	je .noaltvid
	mov ax, 1A00h
	int 10h
	cmp al, 1Ah
	jnz .noaltvid
	cmp bh, 0
	jz .noaltvid

	push ds
	mov ax, 40h
	mov ds, ax
	mov dx, [63h]
	pop ds
	xor dl, 60h
	mov [oldcrtp], dx
	mov al, 7
	cmp dl, 0B4h
	jz @F
	mov al, 3
@@:
	mov [oldmode], al
	mov al, 0Eh
	out dx, al
	inc dx
	in al, dx
	mov ah, al
	dec dx
	mov al, 0Fh
	out dx, al
	inc dx
	in al, dx
	mov bl, 80
	div bl
	xchg al, ah
	mov [oldcsrpos], ax
	jmp .blankloop

.noaltvid:
	mov dx, imsg.noaltvid
	call init_putsz_cs
	mov es, [code_seg]
	mov byte [es:setscreen], 0C3h
	push ss
	pop es
	jmp .blankloop
%endif


%if _SYMBOLIC
.switch_s:
	mov dx, si
	lodsb
	mov ah, 0		; flag for not quoted
	cmp al, '"'
	je .s_quoted
	cmp al, "'"
	jne .s_unquoted
.s_quoted:
	mov ah, al		; save away our quote mark
	inc dx			; -> behind the quote mark
@@:
	lodsb
	cmp al, 13
	je .switch_s_error
	cmp al, 0
	je .switch_s_error
	cmp al, ah		; closing quote mark ?
	jne @B			; not yet -->
	jmp .s_end

	db __TEST_IMM8		; (skip lodsb)
@@:
	lodsb
.s_unquoted:
	cmp al, 32
	ja @B

.s_end:
	dec si			; -> blank or terminator or closing quote
	push ax
	push si
	mov byte [si], 13	; put in a CR for good measure
	mov si, dx

	push word [errret]
	push word [throwret]
	push word [throwsp]

	push cs
	call .jump

	pop word [throwsp]
	pop word [throwret]	; restore throw destination
	pop word [errret]
	pop si
	pop ax
	mov byte [si], al	; restore if it wasn't CR
		; si -> next character to process
	test dx, dx
	jz @F

	cmp dx, si
	je @F

	mov dx, imsg.switch_s_garbage
	call init_putsz_cs

@@:
	test ah, ah		; was quoted ?
	jz @F			; no -->
	inc si			; skip closing quote mark
@@:
	jmp .blankloop

.switch_s_error:
	mov al, 'S'
	jmp ..@init_cmdline_switch_error

.jump:
	mov word [errret], ..@switch_s_catch
	mov word [throwret], ..@switch_s_catch
	mov word [throwsp], sp

	mov ax, ..@switch_s_cont
	push word [code_seg]
	push ax
	retf
%endif


	usesection INIT

.switch_t:
	mov bl, 0FFh
	xor di, di
.switch_t_loop:
@@:
	lodsb
	cmp al, '='
	je @B
	cmp al, ':'
	je @B
	cmp al, ','
	je @B
	dec si
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 13
	je @F
	inc si
	cmp al, '+'
	je .switch_t_set
	cmp al, '-'
	je .switch_t_clear

	mov bl, byte [cs:init_switch_t]
	call init_capitalise
	cmp al, 'I'		; ignore ?
	jne .t_notignore
	mov byte [cs:init_switch_t_ignore], 0FFh
	jmp @B

.t_notignore:
	cmp al, 'O'		; only ?
	jne .t_notonly
	mov byte [cs:init_switch_t_ignore], 0
	jmp @B

.t_notonly:
	cmp al, 'P'		; point ?
	jne .t_notpoint
	mov byte [cs:init_switch_t_break], 0FFh
	jmp @B

.t_notpoint:
	cmp al, 'V'		; variable ?
	jne .t_notvar
	mov byte [cs:init_switch_t_variable], 0FFh
	jmp @B

.t_notvar:
	cmp al, 'U'		; enable UMA ?
	jne .t_notupper
	mov word [cs:init_switch_t_umblink + di], 1
	jmp @B

.t_notupper:
	cmp al, 'L'		; restrict to LMA ?
	jne .t_notlower
	mov word [cs:init_switch_t_umblink + di], 0
	jmp @B

.t_notlower:
	cmp al, 'X'
	jne .t_not_x
	xor di, di
	jmp @B

.t_not_x:
	cmp al, 'Y'
	jne .t_not_y
	mov di, 2
	jmp @B

.t_not_y:
	cmp al, 'Z'
	jne .t_not_z
	mov di, 4
	jmp @B

.t_not_z:
	cmp al, '0'
	jb .switch_t_error
	cmp al, '9'
	jbe .switch_t_numeric
	cmp al, 'A'
	jb .switch_t_error
	cmp al, 'F'
	jbe .switch_t_numeric
.switch_t_error:
	mov al, 'T'
	jmp ..@init_cmdline_switch_error

.switch_t_set:
	mov bl, 0FFh
	jmp @F

.switch_t_clear:
	xor bx, bx

@@:
%if _DEVICE
	test bl, bl
	jz @F
	testopt [internalflags6], dif6_device_mode
	jnz .switch_t_error
@@:
%endif
	mov byte [cs:init_switch_t], bl
	jmp .blankloop

.switch_t_numeric:
	xor dx, dx
	db __TEST_IMM8		; (skip lodsb)
@@:
	lodsb
	cmp al, ','
	je @F
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 13
	je @F
	call init_capitalise
	mov ah, 0
	cmp al, '0'			; decimal digit ?
	jb .switch_t_error
	cmp al, '9'
	ja .switch_t_notdec
	sub al, '0'			; ax = digit
.switch_t_gotdigit:
	cmp dx, 1000h
	jae .switch_t_error
	add dx, dx
	add dx, dx
	add dx, dx
	add dx, dx			; * 16
	add dx, ax
	jmp @B

.switch_t_notdec:
	cmp al, 'A'			; alphabetic ?
	jb .switch_t_error
	cmp al, 'F'
	ja .switch_t_error
	add al, 10 - 'A'		; ax = digit
	jmp .switch_t_gotdigit

@@:
	mov word [cs:init_switch_t_strategy + di], dx
	dec si
	jmp .switch_t_loop

%if _EXTENSIONS
.switch_x:
	mov bx, _EXT_CODE_SIZE
	lodsb
	cmp al, 9
	je .switch_x_got
	cmp al, 32
	je .switch_x_got
	cmp al, 13
	je .switch_x_got
	cmp al, ':'
	je @F
	cmp al, '='
	jne @FF
@@:
	lodsb
@@:
	dec si
	push cs
	pop es
	mov dx, imsg.max
	call init_isstring?
	je .switch_x_got.lodsb
	mov bx, 0
	mov dx, imsg.min
	call init_isstring?
	je .switch_x_got.lodsb
	mov bx, _EXT_CODE_DEFAULT_SIZE
	mov dx, imsg.default
	call init_isstring?
	je .switch_x_got.lodsb

	mov dx, 16
	cmp byte [si], '#'
	jne .switch_x_zero
	inc si
	mov dl, 10
.switch_x_zero:
	xor bx, bx
.switch_x_num:
	lodsb
	cmp al, 32			; end of number ?
	je .switch_x_check
	cmp al, 9
	je .switch_x_check
	cmp al, 13
	je .switch_x_check		; yes -->
	cmp al, '_'			; separator ?
	je .switch_x_num
	cmp al, '#'			; base change ?
	je .switch_x_base
	call init_capitalise
	mov ah, 0
	cmp al, '0'			; decimal digit ?
	jb .switch_x_error
	cmp al, '9'
	ja .switch_x_notdec
	sub al, '0'			; ax = digit
.switch_x_gotdigit:
	xchg ax, bx			; ax = prior
	push dx
	mul dx				; dx:ax = prior * base
	test dx, dx			; >= 64 KiB ?
	jnz .switch_x_error		; error -->
	pop dx
	xchg bx, ax			; return bx = prior
	add bx, ax			; prior += next
	jc .switch_x_error
	jmp .switch_x_num

.switch_x_notdec:
	cmp al, 'A'			; alphabetic ?
	jb .switch_x_error
	cmp al, 'Z'
	ja .switch_x_error
	add al, 10 - 'A'		; ax = digit
	cmp ax, dx
	jb .switch_x_gotdigit
	; jae .switch_x_error
.switch_x_error:
..@init_cmdline_switch_x_error:
	mov al, 'X'
	jmp ..@init_cmdline_switch_error

.switch_x_base:
	cmp bx, 2
	jb .switch_x_error
	cmp bx, 36
	ja .switch_x_error
	mov dx, bx
	jmp .switch_x_zero

.switch_x_check:
	cmp bx, _EXT_CODE_SIZE
	ja .switch_x_error

	db __TEST_IMM8			; (skip lodsb)
.switch_x_got.lodsb:
	lodsb
.switch_x_got:
	push ds
	pop es
	mov word [cs:init_ext_want], bx
	dec si
	jmp .blankloop


.switch_y:
%ifn _LINK
	mov bx, ext_data_max_size_equate
%else
	mov bx, relocatedzero + 0FFF0h
 wlcalc word_minusext_ext_data_area, equ $ - 2
%endif
	lodsb
	cmp al, 9
	je .switch_y_got
	cmp al, 32
	je .switch_y_got
	cmp al, 13
	je .switch_y_got
	cmp al, ':'
	je @F
	cmp al, '='
	jne @FF
@@:
	lodsb
@@:
	dec si
	push cs
	pop es
	mov dx, imsg.max
	call init_isstring?
	je .switch_y_got.lodsb
	mov bx, 0
	mov dx, imsg.min
	call init_isstring?
	je .switch_y_got.lodsb
	mov bx, _EXT_DATA_DEFAULT_SIZE
	mov dx, imsg.default
	call init_isstring?
	je .switch_y_got.lodsb

	mov dx, 16
	cmp byte [si], '#'
	jne .switch_y_zero
	inc si
	mov dl, 10
.switch_y_zero:
	xor bx, bx
.switch_y_num:
	lodsb
	cmp al, 32			; end of number ?
	je .switch_y_check
	cmp al, 9
	je .switch_y_check
	cmp al, 13
	je .switch_y_check		; yes -->
	cmp al, '_'			; separator ?
	je .switch_y_num
	cmp al, '#'			; base change ?
	je .switch_y_base
	call init_capitalise
	mov ah, 0
	cmp al, '0'			; decimal digit ?
	jb .switch_y_error
	cmp al, '9'
	ja .switch_y_notdec
	sub al, '0'			; ax = digit
.switch_y_gotdigit:
	xchg ax, bx			; ax = prior
	push dx
	mul dx				; dx:ax = prior * base
	test dx, dx			; >= 64 KiB ?
	jnz .switch_y_error		; error -->
	pop dx
	xchg bx, ax			; return bx = prior
	add bx, ax			; prior += next
	jc .switch_y_error
	jmp .switch_y_num

.switch_y_notdec:
	cmp al, 'A'			; alphabetic ?
	jb .switch_y_error
	cmp al, 'Z'
	ja .switch_y_error
	add al, 10 - 'A'		; ax = digit
	cmp ax, dx
	jb .switch_y_gotdigit
	; jae .switch_y_error
.switch_y_error:
..@init_cmdline_switch_y_error:
	mov al, 'Y'
	jmp ..@init_cmdline_switch_error

.switch_y_base:
	cmp bx, 2
	jb .switch_y_error
	cmp bx, 36
	ja .switch_y_error
	mov dx, bx
	jmp .switch_y_zero

.switch_y_check:
%ifn _LINK
	cmp bx, ext_data_max_size_equate
%else
	cmp bx, strict word relocatedzero + 0FFF0h
 wlcalc word_minusext_ext_data_area, equ $ - 2
%endif
	ja .switch_y_error

	db __TEST_IMM8			; (skip lodsb)
.switch_y_got.lodsb:
	lodsb
.switch_y_got:
	push ds
	pop es
	mov word [cs:init_extdata_want], bx
	dec si
	jmp .blankloop
%endif


%if _HISTORY_SEPARATE_FIXED && _HISTORY
.switch_h:
	mov bx, 0FFF0h
	lodsb
	cmp al, 9
	je .switch_h_got
	cmp al, 32
	je .switch_h_got
	cmp al, 13
	je .switch_h_got
	cmp al, ':'
	je @F
	cmp al, '='
	jne @FF
@@:
	lodsb
@@:
	dec si
	push cs
	pop es
	mov dx, imsg.max
	call init_isstring?
	je .switch_h_got.lodsb
	mov bx, 260
	mov dx, imsg.min
	call init_isstring?
	je .switch_h_got.lodsb
	mov bx, _HISTORY_WANT_SIZE
	mov dx, imsg.default
	call init_isstring?
	je .switch_h_got.lodsb

	mov dx, 16
	cmp byte [si], '#'
	jne .switch_h_zero
	inc si
	mov dl, 10
.switch_h_zero:
	xor bx, bx
.switch_h_num:
	lodsb
	cmp al, 32			; end of number ?
	je .switch_h_check
	cmp al, 9
	je .switch_h_check
	cmp al, 13
	je .switch_h_check		; yes -->
	cmp al, '_'			; separator ?
	je .switch_h_num
	cmp al, '#'			; base change ?
	je .switch_h_base
	call init_capitalise
	mov ah, 0
	cmp al, '0'			; decimal digit ?
	jb .switch_h_error
	cmp al, '9'
	ja .switch_h_notdec
	sub al, '0'			; ax = digit
.switch_h_gotdigit:
	xchg ax, bx			; ax = prior
	push dx
	mul dx				; dx:ax = prior * base
	test dx, dx			; >= 64 KiB ?
	jnz .switch_h_error		; error -->
	pop dx
	xchg bx, ax			; return bx = prior
	add bx, ax			; prior += next
	jc .switch_h_error
	jmp .switch_h_num

.switch_h_notdec:
	cmp al, 'A'			; alphabetic ?
	jb .switch_h_error
	cmp al, 'Z'
	ja .switch_h_error
	add al, 10 - 'A'		; ax = digit
	cmp ax, dx
	jb .switch_h_gotdigit
	; jae .switch_h_error
.switch_h_error:
..@init_cmdline_switch_h_error:
	mov al, 'H'
	jmp ..@init_cmdline_switch_error

.switch_h_base:
	cmp bx, 2
	jb .switch_h_error
	cmp bx, 36
	ja .switch_h_error
	mov dx, bx
	jmp .switch_h_zero

.switch_h_check:
	cmp bx, 0FFF0h
	ja .switch_h_error
	cmp bx, 260
	jb .switch_h_error

	db __TEST_IMM8			; (skip lodsb)
.switch_h_got.lodsb:
	lodsb
.switch_h_got:
	push ds
	pop es
	mov word [cs:init_history_want], bx
	dec si
	jmp .blankloop
%endif


.switch_a:
	mov bx, _AUXBUFFMAXSIZE
	lodsb
	cmp al, 9
	je .switch_a_got
	cmp al, 32
	je .switch_a_got
	cmp al, 13
	je .switch_a_got
	cmp al, ':'
	je @F
	cmp al, '='
	jne @FF
@@:
	lodsb
@@:
	dec si
	push cs
	pop es
	mov dx, imsg.max
	call init_isstring?
	je .switch_a_got.lodsb
	mov bx, _AUXBUFFSIZE
	mov dx, imsg.min
	call init_isstring?
	je .switch_a_got.lodsb
	; default == minimum
	mov dx, imsg.default
	call init_isstring?
	je .switch_a_got.lodsb

	mov dx, 16
	cmp byte [si], '#'
	jne .switch_a_zero
	inc si
	mov dl, 10
.switch_a_zero:
	xor bx, bx
.switch_a_num:
	lodsb
	cmp al, 32			; end of number ?
	je .switch_a_check
	cmp al, 9
	je .switch_a_check
	cmp al, 13
	je .switch_a_check		; yes -->
	cmp al, '_'			; separator ?
	je .switch_a_num
	cmp al, '#'			; base change ?
	je .switch_a_base
	call init_capitalise
	mov ah, 0
	cmp al, '0'			; decimal digit ?
	jb .switch_a_error
	cmp al, '9'
	ja .switch_a_notdec
	sub al, '0'			; ax = digit
.switch_a_gotdigit:
	xchg ax, bx			; ax = prior
	push dx
	mul dx				; dx:ax = prior * base
	test dx, dx			; >= 64 KiB ?
	jnz .switch_a_error		; error -->
	pop dx
	xchg bx, ax			; return bx = prior
	add bx, ax			; prior += next
	jc .switch_a_error
	jmp .switch_a_num

.switch_a_notdec:
	cmp al, 'A'			; alphabetic ?
	jb .switch_a_error
	cmp al, 'Z'
	ja .switch_a_error
	add al, 10 - 'A'		; ax = digit
	cmp ax, dx
	jb .switch_a_gotdigit
	; jae .switch_a_error
.switch_a_error:
..@init_cmdline_switch_a_error:
	mov al, 'A'
	jmp ..@init_cmdline_switch_error

.switch_a_base:
	cmp bx, 2
	jb .switch_a_error
	cmp bx, 36
	ja .switch_a_error
	mov dx, bx
	jmp .switch_a_zero

.switch_a_check:
	cmp bx, _AUXBUFFSIZE
	jb .switch_a_error
	cmp bx, _AUXBUFFMAXSIZE
	ja .switch_a_error

	db __TEST_IMM8			; (skip lodsb)
.switch_a_got.lodsb:
	lodsb
.switch_a_got:
	push ds
	pop es
	mov word [cs:init_auxbuff_want], bx
	dec si
	jmp .blankloop


.switch_r:
	lodsb
	cmp al, ':'
	je @F
	cmp al, '='
	jne @FF
@@:
	db __TEST_IMM8			; skip dec
@@:
	dec si
	mov dx, 16
	xor bx, bx
.switch_r_num:
	lodsb
	cmp al, 32			; end of number ?
	je .switch_r_check
	cmp al, 9
	je .switch_r_check
	cmp al, 13
	je .switch_r_check		; yes -->
	cmp al, '_'			; separator ?
	je .switch_r_num
	call init_capitalise
	mov ah, 0
	cmp al, '0'			; decimal digit ?
	jb .switch_r_error
	cmp al, '9'
	ja .switch_r_notdec
	sub al, '0'			; ax = digit
.switch_r_gotdigit:
	xchg ax, bx			; ax = prior
	push dx
	mul dx				; dx:ax = prior * base
	test dx, dx			; >= 64 KiB ?
	jnz .switch_r_error		; error -->
	pop dx
	xchg bx, ax			; return bx = prior
	add bx, ax			; prior += next
	jc .switch_r_error
	jmp .switch_r_num

.switch_r_notdec:
	cmp al, 'A'			; alphabetic ?
	jb .switch_r_error
	cmp al, 'Z'
	ja .switch_r_error
	add al, 10 - 'A'		; ax = digit
	cmp ax, dx
	jb .switch_r_gotdigit
	; jae .switch_r_error
.switch_r_error:
..@init_cmdline_switch_r_error:
	mov al, 'R'
	jmp ..@init_cmdline_switch_error

.switch_r_check:
.switch_r_got:
	push ds
	pop es
	mov word [cs:init_switch_r], bx
	dec si
	jmp .blankloop


.noswitches:
.breakpoint:
	nop			; SMC in section init
		; Feed the remaining command line to the 'n' command.
	dec si
	call init_skipcomma
	dec si
	push si


%assign ELD 0
%assign INTERC3 0
%assign PART1 1
%assign PART2 0
%assign PART3 0
%define init_data_segment cs

 %imacro internalcoderelocation 0-*.nolist
 %endmacro
 %imacro internaldatarelocation 0-*.nolist
 %endmacro
 %imacro linkdatarelocation 0-*.nolist
 %endmacro
 %define relocated(address) address

%include "pathshar.asm"

 %undef extcall
 %undef extcallcall
 %unimacro internalcoderelocation 0-*.nolist
 %unimacro internaldatarelocation 0-*.nolist
 %unimacro linkdatarelocation 0-*.nolist
 %undef relocated


%if _APP_ENV_SIZE
init_copy_app_env:
	push ss
	pop ds
 %if _DEVICE
	testopt [internalflags6], dif6_device_mode
	jnz .skip
 %endif
	mov word [cs:mem_envsize], paras(app_env_size)
	mov dx, word [envseg]		; => pseudo MCB
	inc dx				; => space for environment
	mov ax, word [2Ch]
	push ds
	test ax, ax
	jz @F
	mov ds, ax
	mov es, dx
	xor di, di
	xor si, si
.loop_env:
	call init_nextvar
	jne .loop_env
	inc si
	lodsw
	cmp ax, 1
	jne .notrail
	call init_nextvar
	db __TEST_IMM16
.notrail:
	dec si
	dec si
	cmp si, app_env_size - 16 - 2
	jbe .copy_env
	mov dx, imsg.app_env_too_large
	call init_putsz_cs
.no_reloc:
	mov byte [cs:app_env_allocation], 0FFh
	mov dx, ds
	jmp @F

.copy_env:
	rol byte [cs:init_switch_t_variable], 1
	jc .no_reloc
	mov cx, si
	xor si, si
	rep movsb
	xor ax, ax
	stosw
	push ds
	pop es
	mov ah, 49h
	int 21h
@@:
	pop ds
	mov word [2Ch], dx

.skip:
%endif


init_relocate_init:
	push ss
	pop ds
	push ss
	pop es
%if _AUXBUFFSIZE != _AUXBUFFMAXSIZE
	mov ax, word [cs:init_auxbuff_want]
	add ax, 15			; cannot carry, ax <= _AUXBUFFMAXSIZE
	and al, 0F0h
	mov cl, 4
	mov bx, ax
	shr bx, cl
	mov si, - paras(_AUXBUFFSIZE)
	add si, bx			; want size - init size = how much to add
	xor dx, dx			; always positive
%else
	xor si, si			; = 0
	xor dx, dx
%endif

%if _EXTENSIONS
	mov ax, word [cs:init_extdata_want]
	add ax, 15
	and al, 0F0h

	mov cl, 4
	mov bx, ax
	shr bx, cl
	mov di, - paras(_EXT_DATA_BOOT_SIZE)
	add di, bx			; want size - init size = how much to add
					;  negative if to subtract
	sbb bx, bx			; -1 if >= 0 to add
	not bx				; -1 if negative
	add si, di
	adc dx, bx			; add positive or negative dword


	mov ax, word [cs:init_ext_want]
	add ax, 15
	and al, 0F0h

	mov cl, 4
	mov bx, ax
	shr bx, cl
	mov di, - paras(_EXT_CODE_INIT_SIZE)
	add di, bx			; want size - init size = how much to add
					;  negative if to subtract
	sbb bx, bx			; -1 if >= 0 to add
	not bx
	add si, di
	adc dx, bx			; add positive or negative dword
%endif

%if _HISTORY_SEPARATE_FIXED && _HISTORY
	mov ax, word [cs:init_history_want]
	add ax, 15
	and al, 0F0h

	mov cl, 4
	mov bx, ax
	shr bx, cl
	mov di, - paras(historysegment_size)
	add di, bx			; want size - init size = how much to add
					;  negative if to subtract
	sbb bx, bx			; -1 if >= 0 to add
	not bx				; -1 if negative
	add si, di
	adc dx, bx			; add positive or negative dword
%endif

	test dx, dx			; add paras is negative ?
	jnz .done			; yes -->
	test si, si
	jz .done			; if to add == 0 -->

.reloc:
	add si, word [cs:memsize]	; = new memsize
	jc .error
	mov ax, ds
	add si, ax			; => past want alloc
	jc .error
%if _DEVICE
	testopt [internalflags6], dif6_device_mode
	jz @F
	inc si				; account for container paragraph
	jz .error
@@:
%endif
	mov bx, cs
	cmp si, bx			; below-or-equal cs ?
	jbe .done			; yes, no need to relocate -->

%ifn _LINK
	mov cx, init_size_p		; size of init
%else
	mov cx, relocatedzero + 0
 wlcalc word_wrt_INIT_extpara_behind_init, equ $ - 2
%endif
	add bx, cx			; => minimum init reloc target
	cmp si, bx
	jbe .havetarget
	mov bx, si			; => needed init reloc target

.havetarget:
	mov dx, bx			; dx => target
	add bx, cx			; bx => behind target
	jc .error
%if _DEVICE
	testopt [internalflags6], dif6_device_mode
	jz @F
	cmp bx, word [cs:dev_memory_end]; enough space ?
	ja .error			; no -->
	jmp .movp

@@:
%endif
	mov ax, ds
	sub bx, ax			; = amount paragraphs needed
	mov word [cs:init_target_size], bx
	mov word [cs:init_base_size], bx
	mov ah, 4Ah
	int 21h				; resize memory block = es
	jc .error			; if error -->

.movp:
	mov ax, cs
	call init_movp			; move init
	push dx				; => relocated init
	call init_retf			; ttansfer control
	jmp .done			; done

.error:
	mov dx, imsg.init_relocate_failure
	jmp ..@init_cmdline_error

.done:


init_change_auxbuff_size:
	push ss
	pop ds
	push ss
	pop es
%if _AUXBUFFSIZE != _AUXBUFFMAXSIZE
	mov ax, word [cs:init_auxbuff_want]
	add ax, 15			; cannot carry, ax <= _AUXBUFFMAXSIZE
	and al, 0F0h

	mov di, ax
	sub di, 8 * 3
	mov word [auxbuff_current_size], ax
	mov word [auxbuff_current_size_minus_24], di

	mov cl, 4
	mov bx, ax
	shr bx, cl
	mov si, - paras(_AUXBUFFSIZE)
	add si, bx
	jz @F				; if to keep minimum size

		; INP:	ax = wanted size (rounded to paragraph boundary)
		;	bx = wanted size paragraphs
		;	si = bx - paragraphs of minimum size = how much to enlarge
		;	ip -> function depending on layout
		; STT:	ss = ds = es
		; OUT:	NC if success
		;	CY if error, branch to command line error
	call near [cs:init_layout]
	push ss
	pop ds
	push ss
	pop es
	jc ..@init_cmdline_switch_a_error
@@:
%endif


%if _EXTENSIONS
init_change_extdata_size_and_init:
	mov ax, word [cs:init_extdata_want]
	add ax, 15
	and al, 0F0h
	mov word [extdata_size], ax

	mov cx, ax
	sub cx, fromparas(paras(_EXT_DATA_BOOT_SIZE))
	add word [entryseg_size], cx

	mov cl, 4
	mov bx, ax
	shr bx, cl
	mov si, paras(_EXT_DATA_BOOT_SIZE)
	sub si, bx			; init size - want
		; negative if larger want than init
	jz .keep			; if to keep init size
	sbb di, di			; -1 if negative
	not di				; -1 if positive (smaller want than init)
	mov word [cs:relocate_down], di	; relocating down if smaller want

	mov cx, word [cs:memsize]	; how many paragraphs do we have
 %if _DEVICE
	testopt [internalflags6], dif6_device_mode
	jz @F
	inc cx				; account for container paragraph
@@:
 %endif
	sub word [cs:memsize], si

	mov ax, ds
%ifn _LINK
	add ax, paras(100h + DATAENTRYTABLESIZE + datastack_size)
	mov dx, ax
	sub dx, si
	sub cx, paras(100h + DATAENTRYTABLESIZE + datastack_size)
%else
	add ax, strict word relocatedzero + 0
 wlcalc word_wrt_@@lDEBUG_DATA_ENTRY@_extpara_end_of_datastack, equ $ - 2
	mov dx, ax
	sub dx, si
	sub cx, strict word relocatedzero + 0
 wlcalc word_wrt_@@lDEBUG_DATA_ENTRY@_extpara_end_of_datastack, equ $ - 2
%endif
	call init_movp

 %if _DEVICE
	testopt [internalflags6], dif6_device_mode
	jz @F

	sub word [device_mcb_paragraphs], si
					; fix our variable
	sub word [alloc_size], si	; this one too
	mov es, word [reg_es]
	mov bx, word [reg_ebx]		; -> device request header
	sub word [es:bx + 0Eh + 2], si	; -> behind memory in use
	push ss
	pop es
 %endif
@@:

	neg si

%if _HISTORY_SEPARATE_FIXED && _HISTORY
.relocate_history:
	add word [history.segorsel + soaSegSel], si
%if _PM
	add word [history.segorsel + soaSegment], si
%endif
%endif

%if _APP_ENV_SIZE || _DEV_ENV_SIZE
.relocate_env:
	rol byte [cs:app_env_allocation], 1
	jc @F
	add word [2Ch], si
@@:
	cmp word [envseg], 0
	je @F
	add word [envseg], si
@@:
%endif

%if _EXTENSIONS
.relocate_ext:
	add word [extseg], si
%endif

.relocate_code:
	mov ax, word [code_seg]
	mov dx, ax
	add dx, si

	mov es, dx
	add word [code_seg], si
%if _DUALCODE
	add word [code2_seg], si
	mov dx, word [code2_seg]
	push si
 %if ! _PM
	mov si, relocate_from_code
	mov di, relocate_from_code.end
	call .relocate_code_patch
	mov si, relocate_from_code2
	mov di, relocate_from_code2.end
 %endif
	push es
	mov es, dx
	pop dx
 %if ! _PM
	call .relocate_code_patch
	jmp .relocate_code_done

.relocate_code_loop:
	cs lodsw
	xchg bx, ax
	mov word [es:bx], dx
.relocate_code_patch:
	cmp si, di
	jb .relocate_code_loop
	retn

.relocate_code_done:
 %endif
	pop si
%endif

%if _AREAS && _AREAS_HOOK_CLIENT
	mov ax, si
	mov dx, word [cs:relocate_down]
	call add_to_areas_linear_code1.dxax
 %if _DUALCODE && _EXPRDUALCODE
	mov ax, si
	mov dx, word [cs:relocate_down]
	call add_to_areas_linear_code2.dxax
 %endif
%endif


.relocate_auxbuff:
	add word [auxbuff_segorsel + soaSegSel], si
%if _PM
	add word [auxbuff_segorsel + soaSegment], si
					; initialise auxbuff references
%endif
%if _IMMASM  && _IMMASM_AUXBUFF
	add word [immseg], si
%endif


%if _MESSAGESEGMENT
.relocate_messagesegment:
	add word [messageseg], si
 %if _HELP_COMPRESSED && ! _PM
	add word [hshrink_memory_source.segment], si
 %endif
%endif

.keep:
	mov cx, word [extdata_size]
	push ss
	pop es
	mov di, ext_data_area
	mov al, 0
	rep stosb			; init ext data area
%endif


%if _EXTENSIONS
init_change_ext_size:
	mov ax, word [cs:init_ext_want]
	add ax, 15
	and al, 0F0h
	mov word [extseg_size], ax

	mov cl, 4
	mov bx, ax
	shr bx, cl
	mov si, paras(_EXT_CODE_INIT_SIZE)
	sub si, bx			; init size - want
		; negative if larger want than init
	jz @F				; if to keep init size

	sub word [cs:memsize], si

	test si, si
	jns .smaller
	mov di, _EXT_CODE_INIT_SIZE
	mov es, word [extseg]
	mov cx, word [extseg_size]
	sub cx, di
	mov al, 0
	rep stosb			; init trailer
.smaller:

 %if _DEVICE
	testopt [internalflags6], dif6_device_mode
	jz @F

	sub word [device_mcb_paragraphs], si
					; fix our variable
	sub word [alloc_size], si	; this one too
	mov ax, ds			; => PSP
	add ax, word [cs:memsize]	; => where to place container sig
	call init_dev_place_container_signature
					; CHG: es, di, cx, si
					; ax => behind memory used for device
	mov es, word [reg_es]
	mov bx, word [reg_ebx]		; -> device request header
	mov word [es:bx + 0Eh + 2], ax	; -> behind memory in use
	push ss
	pop es
 %endif
@@:
%endif


%if _HISTORY_SEPARATE_FIXED && _HISTORY
init_change_history_size:
	mov ax, word [cs:init_history_want]
	add ax, 15
	and al, 0F0h
	mov word [historyseg_size], ax
	mov bx, ax

	dec ax
	dec ax
	mov word [history.first], ax
	mov word [history.last], ax

	mov cl, 4
	shr bx, cl
	mov si, paras(historysegment_size)
	sub si, bx			; init size - want
		; negative if larger want than init
	jz .keep			; if to keep init size
	sbb di, di			; -1 if negative
	not di				; -1 if positive (smaller want than init)
	mov word [cs:relocate_down], di	; relocating down if smaller want

	mov cx, word [cs:memsize]	; how many paragraphs do we have
 %if _DEVICE
	testopt [internalflags6], dif6_device_mode
	jz @F
	inc cx				; account for container paragraph
@@:
 %endif
	sub word [cs:memsize], si

	mov ax, ds
	add cx, ax
	mov ax, word [history.segorsel + soaSegSel]
	mov dx, ax
	add ax, paras(historysegment_size)
					; => source
	add dx, bx			; => destination
	sub cx, ax
	call init_movp

 %if _DEVICE
	testopt [internalflags6], dif6_device_mode
	jz @F

	sub word [device_mcb_paragraphs], si
					; fix our variable
	sub word [alloc_size], si	; this one too
	mov es, word [reg_es]
	mov bx, word [reg_ebx]		; -> device request header
	sub word [es:bx + 0Eh + 2], si	; -> behind memory in use
	push ss
	pop es
 %endif
@@:

	neg si

%if _APP_ENV_SIZE || _DEV_ENV_SIZE
.relocate_env:
	rol byte [cs:app_env_allocation], 1
	jc @F
	add word [2Ch], si
@@:
	cmp word [envseg], 0
	je @F
	add word [envseg], si
@@:
%endif

%if _EXTENSIONS
.relocate_ext:
	add word [extseg], si
%endif

.keep:
	mov cx, word [historyseg_size]
	mov es, word [history.segorsel + soaSegSel]
	xor di, di
	mov al, 0
	rep stosb			; init history buffer
%endif


init_relocate:
	rol byte [cs:init_switch_t], 1
	jnc .done_outer
%if _DEVICE
	testopt [internalflags6], dif6_device_mode
	jnz .error_outer
%endif

	rol byte [cs:init_switch_t_break], 1
	jnc @F
	int3
@@:

		mov ax, 5802h			; Get UMB link state
		int 21h
		xor ah, ah
		push ax				; Save UMB link state
		mov ax, 5800h			; Get allocation strategy
		int 21h
		push ax				; Save allocation strategy

	xor di, di				; first set
.retry_alloc:
		mov ax, 5803h			; Set UMB link state:
		mov bx, [cs:init_switch_t_umblink + di]
		cmp bx, -1			; not set ?
		je .error_no_alloc_retry	; then error out -->
		int 21h
		mov ax, 5801h			; Set allocation strategy:
		mov bx, [cs:init_switch_t_strategy + di]
		int 21h

		; allocate new memory block for process
	mov bx, word [cs:memsize]
	mov ah, 48h
	int 21h
	jc .error_no_alloc_retry

	xchg dx, ax			; dx => new process block

		; allocate new memory block for environment
%if _APP_ENV_SIZE
	rol byte [cs:app_env_allocation], 1
	jnc @F
%endif
	mov ax, word [2Ch]
	test ax, ax			; zero ?
	jz .allocated			; yes, nothing to relocate -->
	dec ax
	mov es, ax			; es => MCB
	mov bx, word [es:3]		; = MCB size
	mov ah, 48h
	int 21h				; allocate
	jc .error_no_alloc_retry_free_dx

		; relocate environment
	push dx
	xchg dx, ax			; dx => target
	mov ax, word [2Ch]		; ax => source
	mov cx, bx			; cx = amount paragraphs
	call init_movp
	mov word [2Ch], dx		; relocate environment
	pop dx				; => process memory block target
	mov es, ax
	mov ah, 49h
	int 21h				; explicitly free old environment
					;  (not needed depending on method)
@@:

.allocated:	; dx => new process block
	mov ax, ss			; => source process block
	mov cx, word [cs:memsize]	; cx = amount paragraphs
	mov word [cs:init_target_size], cx
	call init_movp

	mov word [cs:relocate_source], ax
	dec ax
	push ds
	mov ds, ax			; ds => original block MCB
	mov word [1], dx		; set original block owner to new

	mov ax, dx
	dec ax
	mov es, ax			; es => new block MCB
	mov word [es:1], dx		; set new block owner to new

	mov di, 8		; es:di-> MCB name field
	mov si, di
	movsw
	movsw
	movsw
	movsw			; Force MCB string
	pop ds			; restore => old block

	mov ax, word [2Ch]		; => environment, or zero
	test ax, ax
	jz @F
	dec ax
	mov es, ax			; => environment MCB
	mov word [es:1], dx		; set environment block owner to new
@@:

	mov ax, ss			; ax => old PSP
	mov ss, dx			; relocate stack
	nop
					; ds still => old PSP
	xchg ax, dx			; ax => new PSP, dx => old PSP

		; code from ecm TSR
	mov es, ax		; es = new PSP
	mov di, 18h
	mov cx, 20
	mov word [ es:34h+2 ], ax
	mov word [ es:34h ], di	; fix the new PSP's PHT pointer
	mov word [ es:32h ], cx	;  and the count of PHT entries field
	 push ax
	mov al, -1
	 push di
	mov bx, cx		; = 20
	rep stosb		; initialise new PHT with empty entries
	 pop di
	mov cx, word [ 32h ]	; cx = count of PHT entries
	cmp cx, bx		; >= 20 ?
	jb .shortertable	; no -->
	mov cx, bx		; limit to 20
.shortertable:
	lds si, [ 34h ]		; ds:si-> old PHT
	push si
	push cx
	rep movsb		; get all entries
	pop cx
	pop di
	 push ds
	 pop es			; es:di-> old PHT
	rep stosb		; fill moved entries with -1 (closed)
	 pop ax
	mov ds, dx		; ds = old PSP
	mov word [ 0Ah ], .terminated
	mov word [ 0Ah+2 ], cs	; Parent Return Address -> us
%if 0		; (not used)
	mov word [ 0Eh ], i23
	mov word [ 0Eh+2 ], ax
	mov word [ 12h ], i24
	mov word [ 12h+2 ], ax	; set interrupt vectors to ours
%endif
	mov word [ 16h ], ax	; set parent PSP to the relocated one
	mov word [ ss:2Eh+2 ], ax
				; set SS used by process termination

	xchg ax, bx		; bx = new location, dx = old location
	mov ax, 335Dh
	int 21h			; PSP relocated call-out

		; In order to set the correct stack address here,
		; the last Int21 call to a usual function (such as
		; Int21.48, .49) must've been made with the same stack
		; pointer as the Int21.4C call below gets.
		;
		; Update: dosemu2 does weird things to the stack.
		;  In particular, it inserts an additional iret
		;  frame depending on some conditions.
		; Only the interrupt 21h subfunctions 00h, 26h,
		;  31h, 4Bh, and 4Ch are handled differently.
		;  As a workaround we can call service 4Bh as the
		;  last interrupt 21h function before terminating.
		; Refer to https://github.com/dosemu2/dosemu2/blob/d7402eec84478c051d25e7b26dd8515514c186e2/src/base/core/int.c#L1633-L1639

	; rol byte [j_flags.invalidexec], 1
	; jnc @F

	push cs
	pop ds
	mov dx, .nullbyte	; just in case, ds:dx -> zero value byte
	mov ax, 4B7Fh		; 21.4B with invalid subfunction in al
				;  (note that FreeDOS masks off 80h)
	int 21h
@@:

	mov es, word [cs:relocate_source]
	push word [ es:2Eh ]
	pop word [ ss:2Eh ]	; set SP used by process termination
.nullbyte: equ $ - 1

	mov ax, 4C00h
	int 21h			; terminate, and make the new PSP active
				; also handles freeing all memory allocated to the old PSP
				; also closes any handles >20 if PHT larger
			; (not used) also relocates Int23, Int24
				; also notifies resident software old PSP is no longer valid
.terminated:			; (ax, bx, es might be changed)
	push ss
	pop ds
	push ss
	pop es
		; end code from ecm TSR


.relocate:
	and word [cs:relocate_down], 0	; = 0
	mov si, ss
	sub si, word [cs:relocate_source]
	jnc @F
	not word [cs:relocate_down]	; = 0FFFFh
@@:
		; target - source = delta
		; delta + source = target
		;  because target - source + source = target

%if _HISTORY_SEPARATE_FIXED && _HISTORY
.relocate_history:
	add word [history.segorsel + soaSegSel], si
%if _PM
	add word [history.segorsel + soaSegment], si
%endif
%endif

%if _APP_ENV_SIZE
.relocate_env:
	rol byte [cs:app_env_allocation], 1
	jc @F
	add word [2Ch], si
@@:
	cmp word [envseg], 0
	je @F
	add word [envseg], si
@@:
%endif

%if _EXTENSIONS
.relocate_ext:
	add word [extseg], si
%endif

.relocate_code:
	mov ax, word [code_seg]
	mov dx, ax
	add dx, si

	mov es, dx
	add word [code_seg], si
%if _DUALCODE
	add word [code2_seg], si
	mov dx, word [code2_seg]
	push si
 %if ! _PM
	mov si, relocate_from_code
	mov di, relocate_from_code.end
	call .relocate_code_patch
	mov si, relocate_from_code2
	mov di, relocate_from_code2.end
 %endif
	push es
	mov es, dx
	pop dx
 %if ! _PM
	call .relocate_code_patch
	jmp .relocate_code_done

.relocate_code_loop:
	cs lodsw
	xchg bx, ax
	mov word [es:bx], dx
.relocate_code_patch:
	cmp si, di
	jb .relocate_code_loop
	retn

.relocate_code_done:
 %endif
	pop si
%endif

%if _AREAS && _AREAS_HOOK_CLIENT
	mov ax, si
	mov dx, word [cs:relocate_down]
	call add_to_areas_linear_code1.dxax
 %if _DUALCODE && _EXPRDUALCODE
	mov ax, si
	mov dx, word [cs:relocate_down]
	call add_to_areas_linear_code2.dxax
 %endif
%endif


.relocate_auxbuff:
	add word [auxbuff_segorsel + soaSegSel], si
%if _PM
	add word [auxbuff_segorsel + soaSegment], si
					; initialise auxbuff references
%endif
%if _IMMASM  && _IMMASM_AUXBUFF
	add word [immseg], si
%endif


%if _MESSAGESEGMENT
.relocate_messagesegment:
	add word [messageseg], si
 %if _HELP_COMPRESSED && ! _PM
	add word [hshrink_memory_source.segment], si
 %endif
%endif


.relocate_old_initcode_variables:
	mov ax, ds
	mov word [execblk.cmdline + 2], ax
	mov word [execblk.fcb1 + 2], ax
	mov word [execblk.fcb2 + 2], ax	; set up parameter block for exec command
	mov word [pspdbg], ax

%if _IMMASM  && !_IMMASM_AUXBUFF
 %ifn _LINK
	add ax, (immasm_buffer + DATASECTIONFIXUP) >> 4
 %else
	add ax, strict word immasm_buffer
 wlcalc word_shr_4, equ $ - 2
 %endif
	mov word [immseg], ax
%endif


%if _AREAS_HOOK_SERVER
.relocate_areas:
	mov ax, ds
	mov word [ddebugareas.next + 2], ax
	mov word [ddebugareas.prev + 2], ax
	mov word [..@patch_entry_seg], ax
%endif


.relocate_done:
	jmp .done


		; environment could not be allocated
.error_no_alloc_retry_free_dx:
	mov es, dx
	mov ah, 49h
	int 21h				; free allocated block

		; some memory could not be allocated
.error_no_alloc_retry:
	scasw				; di += 2
	cmp di, 6
	jb .retry_alloc

.error_no_retry:
	rol byte [cs:init_switch_t_ignore], 1
	jnc .error

.done:
	db __TEST_IMM8			; (skip stc, NC)
.error:
	stc				; CY
.status:
	sbb dx, dx				; 0 = NC, -1 = CY
		pop bx				; saved allocation strategy
		mov ax, 5801h
		int 21h				; Set allocation strategy
		pop bx				; saved UMB link state
		mov ax, 5803h
		int 21h				; Set UMB link state
	test dx, dx
	jz .done_outer
.error_outer:
	mov dx, imsg.relocate_failure
	jmp ..@init_cmdline_error

.done_outer:


%if _EXECUTABLE
init_executable:
	push ss
	pop ds
	push ss
	pop es
	testopt [internalflags6], dif6_device_mode
	jnz .device

.application:
	mov dx, word [2Ch]
	test dx, dx
	jz .unable
	mov es, dx
	mov cx, 8000h
	xor si, si
@@:
	call .nextvar
	jc .unable
	jne @B
	inc si
	es lodsw
	cmp ax, 1
	jne .unable

	push es
	pop ds				; => env
	push ss
	pop es				; => data entry
	mov di, init_executable_pathname
	mov ah, 60h
	int 21h
	jmp .done

.nextvar:
@@:
	es lodsb
	test al, al
	loopnz @B
	stc
	jnz @F
	cmp byte [es:si], 0
@@:
	retn


.device:
	mov si, word [reg_ebx]
	mov ds, word [reg_es]
	lds si, [si + 12h]

	mov di, si
@@:
	lodsb
	cmp al, 32
	je @F
	cmp al, 9
	je @F
	cmp al, 0
	je @F
	cmp al, 13
	je @F
	cmp al, 10
	je @F
	jmp @B

@@:
	push word [si - 1]
	push si
	mov byte [si - 1], 0
	mov si, di
	mov di, init_executable_pathname
	mov ah, 60h
	int 21h
	pop si
	pop ax
	mov byte [si - 1], al

.done:
.unable:
	push ss
	pop ds
	push ss
	pop es

	mov di, init_executable_pathname
	xor ax, ax
	mov cx, 128
	repne scasb
	dec di
	mov word [init_executable_pathname.end], di
%endif


init_hook_interrupts:
%if CATCHINTAMOUNT
		; Set up interrupt vectors.
%if _CATCHINT06 && _DETECT95LX
	mov cx, word [inttab_number_variable]
%else
	mov cx, inttab_number
%endif
	jcxz .intend
	mov si, inttab
.intloop:
	lodsb
	mov ah, 35h
	int 21h			; get vector
	xchg ax, di
	lodsw
	xchg ax, di
%if _DEBUG && !_DEBUG_COND
				; vectors are set only when debuggee runs
%else
 %if _DEBUG
	testopt [internalflags6], dif6_debug_mode
	jnz @F
 %endif
	mov word [ di + ieNext ], bx
	mov word [ di + ieNext + 2 ], es
				; store it
	mov dx, di
	mov ah, 25h		; set interrupt vector
	int 21h			; ds => lDEBUG_DATA_ENTRY
@@:
%endif
	loop .intloop

.intend:
%endif


		; Disabled this. hook2F (debug.asm) now detects this condition.
%if _PM && 0
		; Windows 9x and DosEmu are among those hosts which handle some
		; V86 Ints internally without first calling the interrupt chain.
		; This causes various sorts of troubles and incompatibilities;
		; in our case, hooking interrupt 2Fh would not intercept calls
		; made to the DPMI interface because the host sees them first.
 %if _WIN9XSUPP
  %if _GUARD_86M_INT2F
	push es
	xor ax, ax
	mov es, ax		; (only used in 86 Mode)
	mov ax, [es:2Fh * 4]
	cmp ax, -1
	je @F			; --> (ZR)
	or ax, [es:2Fh * 4 + 2]
@@:
	pop es
	jz @F
  %endif
	mov ax, 1600h		; running in a Win9x DOS box?
	int 2Fh
	cmp al, 4
	jge .no2Fhook		; this is intentionally a signed comparison!
@@:
 %endif
 %if _DOSEMU
	testopt [internalflags], runningdosemu
	jnz .no2Fhook
 %endif
 %if _WIN9XSUPP || _DOSEMU
	jmp short .dpmihostchecked
.no2Fhook:
	clropt [options4], opt4_int_2F_hook
.dpmihostchecked:
 %endif
%endif

%if _EXTENSIONS
init_eld_app_dev:
	call init_eld
%else
	push ds
	pop es
%endif

set_parent_pra:
		; Save, then modify termination address and parent PSP.
%if _DEVICE && _APPLICATION
	testopt [internalflags6], dif6_device_mode
	jnz final_device
%elif _DEVICE
	jmp final_device
%endif
%if _APPLICATION
	mov si, TPIV
	mov di, psp22
	movsw
	movsw				; save Int22
	mov dx, debug22
	mov word [ si-4 ], dx
	mov word [ si-2 ], ds		; set pspInt22 (required)
	mov si, 16h
	movsw				; save parent
	mov word [ si-2 ], ds		; set pspParent
	mov ax, 2522h			; set Int22
	int 21h				; (not really required)

init_app_allocate_reserved:
		mov ax, 5802h			; Get UMB link state
		int 21h
		xor ah, ah
		push ax				; Save UMB link state
		mov ax, 5800h			; Get allocation strategy
		int 21h
		push ax				; Save allocation strategy

		mov ax, 5803h			; Set UMB link state:
		mov bx, 1			; enabled
		int 21h
		mov ax, 5801h			; Set allocation strategy:
		xor bx, bx			; first fit, LMA then UMA
		int 21h

	mov dx, word [cs:init_switch_r]
.loop:
	xor bx, bx
	mov ah, 48h
	int 21h
	jc .done
	mov es, ax
	cmp ax, dx
	jbe .grow
.free:
	mov ah, 49h
	int 21h
	jmp .done

.grow:
	push ax
	xchg bx, ax
	neg bx
	add bx, dx				; = dx - bx
	mov ah, 4Ah
	int 21h					; grow up to desired segment
	pop ax					;  (if not free grow less)

	test bx, bx
	jnz .nonempty
	dec ax					; => MCB (hack into the name field)
.nonempty:
	mov es, ax
	mov word [es:12], bx			; size (0 if in MCB)
	xchg ax, word [next_reserved_segment]	; :12 -> chain
	mov word [es:14], ax			; => next in chain
	jmp .loop

.done:
		pop bx				; saved allocation strategy
		mov ax, 5801h
		int 21h				; Set allocation strategy
		pop bx				; saved UMB link state
		mov ax, 5803h
		int 21h				; Set UMB link state

init_app_resize:
		; shrink to required resident size
	push ds
	pop es
	mov ax, word [cs:memsize]
	mov word [alloc_size_not_reserved], ax
	mov ax, ds
	mov dx, word [cs:init_switch_r]
	mov word [reserved_address], dx
	cmp ax, dx
	jae @F
	mov bx, word [cs:init_target_size]
	add ax, bx
	cmp ax, dx
	jb .fulltargetsize
	mov ax, ds
	add ax, word [cs:memsize]
	cmp ax, dx
	jae @F					; no action needed
	mov bx, dx
	mov ax, ds
	sub bx, ax				; grow it up to /R (or less)
	mov ah, 4Ah
	int 21h					; get bx = actual size
.fulltargetsize:
	mov word [cs:memsize], bx
@@:
	mov ah, 4Ah
	mov bx, word [cs:memsize]
	mov word [alloc_size], bx
	mov word [alloc_seg], ds


	rol byte [cs:init_switch_t], 1
	jc .application_relocate

.application_no_relocate:
	mov si, initcont
	push word [code_seg]
	push si
	retf

.application_relocate:
	sub sp, .relocate_trampoline_size_w * 2
	mov di, sp			; es:di -> stack space
	push cs
	pop ds
	mov si, .relocate_trampoline	; ds:si -> trampoline code
	mov cx, .relocate_trampoline_size_w
	 push di
	rep movsw			; place trampoline
	 pop di

	mov si, initcont
	push word [ss:code_seg]
	push si				; place our return address

	push es
	push bx
	push ax				; stack for trampoline

	mov ch, 49h			; set up to free original block
	mov ax, word [relocate_source]
	 push ax			; => source block
	mov dx, word [init_switch_r]
	cmp ax, dx
	jae @F
	mov bx, word [cs:init_base_size]
	add ax, bx
	cmp ax, dx
	jb .fullbasesize
	mov bx, dx
	 pop ax
	 push ax
	sub bx, ax
.fullbasesize:
	mov ch, 4Ah			; resize instead

	 pop ax
	 push ax
	test bx, bx
	jnz .nonempty
	dec ax					; => MCB (hack into the name field)
.nonempty:
	mov es, ax
	mov word [es:12], bx			; size (0 if in MCB)
	xchg ax, word [ss:next_reserved_segment]; :12 -> chain
	mov word [es:14], ax			; => next in chain
@@:
	 pop ax
	mov es, ax			; => relocate source
	xchg ax, cx			; ah = 49h or 4Ah

	 push ss
	 pop ds				; reset ds
	push ss
	push di				; -> placed trampoline
	retf				; jump to trampoline


	align 2, nop
.relocate_trampoline:
	int 21h				; free original process block
	pop ax				; ah = 4Ah
	pop bx				; bx = memory size
	pop es				; es => new block
		; (we do not really need to resize the block here
		;  as it was already allocated the wanted size.)
	retf .relocate_trampoline_size_w * 2
	align 2, nop
 endarea .relocate_trampoline

%endif


%if _DEVICE
final_device:
	mov si, 80h			; -> command line tail
	mov cx, si			; = 128
	sub sp, cx			; -> buffer on stack
	mov di, sp
	rep movsb			; preserve it

 %if _DEV_ENV_SIZE
	push word [2Ch]
 %endif

	mov dx, ds
	mov ah, 55h
	clc
	int 21h				; create child PSP

	mov si, TPIV + 4
	mov dx, debug22
	mov word [ si-4 ], dx
	mov word [ si-2 ], ds		; set pspInt22 (required)
 %if _DEVICE_SET_2324
	mov word [ si ], devint23
	mov word [ si + 2 ], ds		; set pspInt23
	mov word [ si + 4 ], devint24
	mov word [ si + 6 ], ds		; set pspInt24
 %endif
	mov si, 16h + 2
	mov word [ si-2 ], ds		; set pspParent
	; mov ax, 2522h			; set Int22
	; int 21h			; (not really required)

 %if _DEV_ENV_SIZE
	pop word [2Ch]
 %else
	xor ax, ax
	mov word [2Ch], ax		; set environment to none
 %endif

	mov bx, 5			; close file handles past std (5)
	mov cx, word [32h]		; = number of file handles
	sub cx, bx			; = number past std handles
	jbe @FF				; if <= 0 handles to close -->
@@:
	mov ah, 3Eh
	int 21h				; close file handle
	inc bx				; next handle
	loop @B				; loop -->
@@:

	mov si, sp			; -> buffer on stack
	mov di, 80h			; -> command line tail buffer in PSP
	mov ax, di			; = 128
	mov cx, di			; = 128
	rep movsb			;
	add sp, ax			; discard buffer

	mov si, initcont.device
	push word [code_seg]
	push si
	retf
%endif
%endif


%assign ELD 0
%assign INTERC3 0
%assign PART1 0
%assign PART2 1
%assign PART3 0
%define init_data_segment cs

 %imacro internalcoderelocation 0-*.nolist
 %endmacro
 %imacro internaldatarelocation 0-*.nolist
 %endmacro
 %imacro linkdatarelocation 0-*.nolist
 %endmacro
 %define relocated(address) address

%include "pathshar.asm"

 %undef extcall
 %undef extcallcall
 %unimacro internalcoderelocation 0-*.nolist
 %unimacro internaldatarelocation 0-*.nolist
 %unimacro linkdatarelocation 0-*.nolist
 %undef relocated


%if _EXTHELP
switch_u:
	setopt [options], nonpagingdevice

	lodsb
	call init_capitalise
	cmp al, 'L'
	je .license
	cmp al, 'R'
	jne .both
.revision:
	call .do_revision
	jmp .done

.do_revision:
	mov bx, msg.build_array
	mov cx, msg.build_short_amount
	call .putsz_spec_multi
	mov dx, imsg.crlf
	jmp init_putsz_cs

.both:
	call .do_revision
.license:
	mov dx, msg.license
	call init_transfer_to_putsz_exthelp
	mov dx, imsg.crlf
	call init_putsz_cs
.done:
	jmp commandline.helpterminate

.putsz_spec_multi:
@@:
	mov dx, word [bx]
	call init_transfer_to_putsz_exthelp
	inc bx
	inc bx
	loop @B
	retn


init_transfer_to_putsz_exthelp:
	lframe 0
	lpar word, in_ip_out_far_cs
	 ; near call ip
	lpar word, in_ax_out_far_ip
	 push ax			; push into ?in_ax_out_far_ip
	lpar word, out_near_ip
	mov ax, code_retf
	 push ax			; push into ?out_near_ip
	lpar dword, destination
	 push word [code_seg]		; push into ?destination + 2
	mov ax, putsz_exthelp
	 push ax			; push into ?destination + 0
	lenter
	mov ax, cs			; = cs
	xchg ax, word [bp + ?in_ip_out_far_cs]
					; ax = ip, var = cs
	xchg ax, word [bp + ?in_ax_out_far_ip]
					; ax = ax, var = ip
	lleave
	retf
%endif


%if _CONFIG
		; INP:	ax => environment, zero if none
		;	cs:di -> variable name, including '=' terminator
		;	cx = variable name length, including '=' terminator
		;	ss:dx -> buffer to write to, 256 bytes
		; OUT:	es = ds = ss
		;	CY if not found
		;	NC if found,
		;	 buffer filled
init_copyvar:
	call init_findvar
	jc .endvar

.foundvar:
	push ds
	pop es
	mov di, si
	mov al, 0
	mov cx, 255
	repne scasb

	sub di, si
	dec di
	mov cx, di

	push ss
	pop es
	mov di, dx
	rep movsb
	mov al, 0
	stosb
	clc

.endvar:
	push ss
	pop ds
	push ss
	pop es
	retn
%endif


%assign ELD 0
%define PREFIX init_
%include "isstring.asm"


		; INP:	bx => destination for auxbuff
		;	(The following are not actually used by this function,
		;	 they're just what is passed in and preserved to
		;	 be used by the caller after returning.)
		;	dx => destination for code image
		;	(if boot-loaded:) cx => destination for pseudo-PSP
		;		(implies cx+10h => destination for data_entry)
		;	ax => segment for history buffer

init_app_layout_1:
%if 0
	mov bx, ds
	mov dx, bx
	add bx, paras(AUXTARGET1)
	add dx, paras(CODETARGET1)
 %if _HISTORY_SEPARATE_FIXED && _HISTORY
	mov ax, bx
	add ax, paras(auxbuff_size)
  %if AUXTARGET1_equate <= CODETARGET1_equate
	...
  %endif
 %endif
	mov cx, dx
%endif

		; REM:	Only history segment and extsegment are behind auxbuff.
 %if _EXTENSIONS || _HISTORY_SEPARATE_FIXED && _HISTORY || \
	_DEV_ENV_SIZE || _APP_ENV_SIZE
  %if _HISTORY_SEPARATE_FIXED && _HISTORY
	mov ax, word [history.segorsel + soaSegSel]
					; => history segment
  %elif (_DEV_ENV_SIZE || _APP_ENV_SIZE) && _EXTENSIONS
	mov ax, word [envseg]
	test ax, ax
	jnz @F
	mov ax, word [extseg]
@@:
  %elif (_DEV_ENV_SIZE || _APP_ENV_SIZE)
	mov ax, word [envseg]
		; if here then no history segment and no ext segment,
		;  so we do not want to copy anything if envseg unused.
		;  in this case hope that mem_envsize is 0 so the movp
		;  call below is a no-op.
		; better fix: just include a check for zero here.
	test ax, ax
	jz .skip_env_reloc
  %elif _EXTENSIONS
	mov ax, word [extseg]
  %else
   %error Unexpected condition
  %endif
	mov dx, ax
	add dx, si
	mov cx, paras(historysegment_size + extsegment_size)
		; This runs before init_change_history_size
		;  and init_change_ext_size.
%if (_DEV_ENV_SIZE || _APP_ENV_SIZE)
	add cx, word [cs:mem_envsize]
%endif
	call init_movp
.skip_env_reloc:
init_app_layout_common:
  %if _HISTORY_SEPARATE_FIXED && _HISTORY
	add word [history.segorsel + soaSegSel], si
%if _PM
	add word [history.segorsel + soaSegment], si
%endif
  %endif
  %if _EXTENSIONS
	add word [extseg], si
  %endif
  %if (_DEV_ENV_SIZE || _APP_ENV_SIZE)
	rol byte [cs:app_env_allocation], 1
	jc @F
	add word [2Ch], si
@@:
	cmp word [envseg], 0
	je @F
	add word [envseg], si
@@:
  %endif
 %else
init_app_layout_common:
 %endif
	add word [cs:memsize], si
	mov es, word [auxbuff_segorsel + soaSegSel]
	mov di, fromwords(words(_AUXBUFFSIZE))
	mov cx, si			; = amount paragraphs added
	shl cx, 1			; = amount qwords
	shl cx, 1			; = amount dwords
	shl cx, 1			; = amount words
	xor ax, ax
	rep stosw			; initialise auxbuff trail
	clc
	retn


init_app_layout_2:
%if 0
	mov bx, ds
	mov dx, bx
	add bx, paras(AUXTARGET2)
	add dx, paras(CODETARGET2)
 %if _HISTORY_SEPARATE_FIXED && _HISTORY
  %if (paras(AUXTARGET1_equate) + paras(auxbuff_size_equate)) \
	!= (paras(CODETARGET2_equate) + paras(ldebug_codes_truncated_size_equate))
	...
  %endif
 %endif
%endif

		; REM:	Code1/code2 and history segment are behind auxbuff.
	mov ax, word [code_seg]
	mov dx, ax
	add dx, si
%ifn _LINK
	mov cx, ldebug_code_bootldr_truncated_size_p \
		+ ldebug_code2_size_p \
		+ paras(historysegment_size) \
		+ paras(extsegment_size)
%else
	mov cx, relocatedzero + 0 \
		+ paras(historysegment_size) \
		+ paras(extsegment_size)
 wlcalc word_wrt_@@lDEBUG_CODE@_extpara_behind_code_truncated, equ $ - 2
 wlcalc word_wrt_@@lDEBUG_CODE2@_extpara_behind_code2, equ $ - 2
%endif

		; This runs before init_change_history_size
		;  and init_change_ext_size.
%if (_DEV_ENV_SIZE || _APP_ENV_SIZE)
	add cx, word [cs:mem_envsize]
%endif
	call init_movp

	mov es, dx
	add word [code_seg], si
%if _DUALCODE
	add word [code2_seg], si
	mov dx, word [code2_seg]
repatch_relocate:
	push si
 %if ! _PM
	mov si, relocate_from_code
	mov di, relocate_from_code.end
	call .patch
	mov si, relocate_from_code2
	mov di, relocate_from_code2.end
 %endif
	push es
	mov es, dx
	pop dx
 %if ! _PM
	call .patch
	jmp .done

.loop:
	cs lodsw
	xchg bx, ax
	mov word [es:bx], dx
.patch:
	cmp si, di
	jb .loop
	retn

.done:
 %endif
	pop si
%endif

%if _AREAS && _AREAS_HOOK_CLIENT
	mov ax, si
	call add_to_areas_linear_code1
 %if _DUALCODE && _EXPRDUALCODE
	mov ax, si
	call add_to_areas_linear_code2
 %endif
%endif

	jmp init_app_layout_common


		; REM:	Only history segment is behind auxbuff.
init_app_layout_3: equ init_app_layout_1
%if 0
		; If both prior attempts failed, we allocate
		;  an additional 8 KiB and move the buffer to
		;  that. This should always succeed.
	mov word [cs:memsize], paras(AUXTARGET3 \
			+ auxbuff_size \
			+ historysegment_size)
				; enlarge the final memory block size

	mov bx, ds
	add bx, paras(AUXTARGET3)
	mov dx, cx
 %if _HISTORY_SEPARATE_FIXED && _HISTORY
	mov ax, bx
	add ax, paras(auxbuff_size)
 %endif
%endif


%if _DEVICE
init_dev_layout_1:
%if 0
	mov bx, ds
	add bx, paras(DEVICEADJUST)
	mov dx, bx
	add bx, paras(AUXTARGET1)
	add dx, paras(CODETARGET1)
 %if _HISTORY_SEPARATE_FIXED && _HISTORY
	mov ax, bx
	add ax, paras(auxbuff_size)
 %endif
	mov cx, dx
%endif
	call init_app_layout_1
	jmp init_dev_layout_common


init_dev_layout_2:
%if 0
	mov bx, ds
	add bx, paras(DEVICEADJUST)
	mov dx, bx
	add bx, paras(AUXTARGET2)
	add dx, paras(CODETARGET2)
%endif
	call init_app_layout_2
	jmp init_dev_layout_common


init_dev_layout_3:
%if 0
	mov word [cs:memsize], paras(AUXTARGET3 \
			+ auxbuff_size \
			+ historysegment_size)
				; enlarge the final memory block size

	mov bx, ds
	add bx, paras(DEVICEADJUST)
	add bx, paras(AUXTARGET3)
	mov dx, cx
 %if _HISTORY_SEPARATE_FIXED && _HISTORY
	mov ax, bx
	add ax, paras(auxbuff_size)
 %endif
%endif

	call init_app_layout_3
init_dev_layout_common:
	add word [device_mcb_paragraphs], si
					; fix our variable
	add word [alloc_size], si	; this one too
	mov ax, ds			; => PSP
	add ax, word [cs:memsize]	; => where to place container sig
	call init_dev_place_container_signature
					; CHG: es, di, cx, si
					; ax => behind memory used for device
	mov es, word [reg_es]
	mov bx, word [reg_ebx]		; -> device request header
	mov word [es:bx + 0Eh + 2], ax	; -> behind memory in use
	clc
	retn
%endif


init_layout_none:
	stc
	retn


%if _AREAS && _AREAS_HOOK_CLIENT
add_to_areas_linear_code1:
	xor dx, dx
.dxax:
	mov cx, 4
@@:
	shl ax, 1
	rcl dx, 1
	loop @B

	add word [areas_sub + areastrucsubLinear], ax
	adc word [areas_sub + areastrucsubLinear + 2], dx
	add word [areas_sub + areastrucsubLinearEnd], ax
	adc word [areas_sub + areastrucsubLinearEnd + 2], dx

	add word [areas_fun + areastrucfunLinear], ax
	adc word [areas_fun + areastrucfunLinear + 2], dx
	add word [areas_fun + areastrucfunLinearEnd], ax
	adc word [areas_fun + areastrucfunLinearEnd + 2], dx
	retn

 %if _DUALCODE && _EXPRDUALCODE
add_to_areas_linear_code2:
	xor dx, dx
.dxax:
	mov cx, 4
@@:
	shl ax, 1
	rcl dx, 1
	loop @B

	add word [areas_sub + AREASTRUCSUB_size + areastrucsubLinear], ax
	adc word [areas_sub + AREASTRUCSUB_size + areastrucsubLinear + 2], dx
	add word [areas_sub + AREASTRUCSUB_size + areastrucsubLinearEnd], ax
	adc word [areas_sub + AREASTRUCSUB_size + areastrucsubLinearEnd + 2], dx
	retn
 %endif
%endif


%if _CONFIG
do_truename:
.:
	push ss
	pop ds
	mov si, dx		; ds:si -> source
	push ss
	pop es
	mov di, dx		; es:di -> destination (same)
	mov ah, 60h
	int 21h
	jnc .done
	cmp byte [si], 0	; empty ? (kernel rejects this)
	mov word [si], "."	; make a dot + NUL (current directory)
	je .			; if expected failure then repeat -->

.done:
	mov al, 0
	mov cx, 128
	repne scasb		; scan for NUL
	dec di			; -> at the NUL

@@:
	cmp di, si
	jbe @F
	cmp byte [di - 1], '/'
	je @FF
	cmp byte [di - 1], '\'
	je @FF
@@:
	mov byte [di], '\'
	inc di
@@:
	retn
%endif


%if _EXTENSIONS
init_eld:
	push ss
	pop ds

	numdef ELD_DEBUG_LINKCALL_NONZERO, 0, 32
%if _ELD_DEBUG_LINKCALL_NONZERO
 %if _ELD_DEBUG_LINKCALL_NONZERO & 15
  %error Invalid size
 %endif
 %if _ELD_DEBUG_LINKCALL_NONZERO < 32
  %error Invalid size
 %endif
 %if _ELD_DEBUG_LINKCALL_NONZERO > 65520
  %error Invalid size
 %endif
	mov ax, word [extseg_size]
	mov di, word [extseg_used]
	sub ax, di			; = amount free space
	jc @F
	mov cx, _ELD_DEBUG_LINKCALL_NONZERO
	cmp ax, cx
	jb @F
	mov es, word [extseg]		; es:di -> where to place ELD instance
	xor ax, ax
	push di
	shr cx, 1			; = amount words
	rep stosw			; zero
	mov word [extseg_used], di	; -> behind ELD
	xchg ax, di			; ax -> behind ELD
	pop di
	mov word [es:di + eldiStartCode], di
	mov word [es:di + eldiEndCode], ax
	mov ax, "--"
	mov cl, 4
	add di, eldiIdentifier
	rep stosw

@@:
%endif

	mov ax, word [extseg_size]
	mov di, word [extseg_used]
	sub ax, di			; = amount free space
	jc .ret
	cmp ax, eld_linkcall_size	; enough ?
	jb .ret
	mov ax, di			; -> where to install this ELD
	mov es, word [extseg]		; es:di -> where to place ELD instance
	push cs
	pop ds				; ! ds no longer equals ss
	mov si, eld_linkcall + 2	; ds:si -> ELD source plus one word
	mov cx, eld_linkcall_size_w - 2	; cx = length less two words
	stosw				; init eldiStartCode
	movsw
	add word [es:di - 2], ax	; init eldiEndCode
	rep movsw			; write our ELD
	push ss
	pop ds
	mov word [extseg_used], di	; -> behind ELD
	add ax, eld_linkcall.to_code1 - eld_linkcall
	mov word [linkcall_table.code1], ax
 %if _DUALCODE
	add ax, eld_linkcall.to_code2 - eld_linkcall.to_code1
	mov word [linkcall_table.code2], ax
	add ax, eld_linkcall.to_entry - eld_linkcall.to_code2
 %else
	add ax, eld_linkcall.to_entry - eld_linkcall.to_code1
 %endif
	mov word [linkcall_table.entry], ax
	mov word [linkcall_table.init], 1
.ret:
	push ss
	pop ds
	push ss
	pop es
	retn
%endif

%if !_LINK && _LINK_COMPAT
%ifn _ONLY386 || _ONLYNON386
	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

	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

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

	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_table2, PATCH_NO386_TABLE2
%undef PATCH_NO386_TABLE2
%else
 %assign __patch_no386_table2_method 0
 %assign __patch_386_table2_method 0
%endif

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

	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_tableentry, PATCH_NO386_TABLEENTRY
%undef PATCH_NO386_TABLEENTRY
%endif

%if _DUALCODE && ! _PM
	align 2, db 0
relocate_from_code:
	dw  PATCH_RELOCATE_FROM_lDEBUG_CODE
.end:
	align 2, db 0
relocate_from_code2:
	dw  PATCH_RELOCATE_FROM_lDEBUG_CODE2
.end:

%unimacro dualcall 1.nolist
	; make sure we do not allow later uses
%endif
%endif
