
%if 0

lDebug initialisation

Copyright (C) 1995-2003 Paul Vojta
Copyright (C) 2008-2012 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


	usesection INIT

initcode:
%if ($ - $$) != 0
 %fatal initcode expected at start of section
%endif

	mov ax, ss
	mov dx, ds
	sub ax, dx
	xor dx, dx
	mov cx, 4
@@:
	shl ax, 1
	rcl dx, 1
	loop @B

	push ax			; (if sp was zero)

	add ax, sp
	adc dx, 0
	add ax, 15
	adc dx, 0

	and al, ~15

	cmp dx, NONBOOTINITSTACK_END >> 16
	ja .stackdownfirst
	jb .memupfirst
	cmp ax, NONBOOTINITSTACK_END & 0FFFFh
	jae .stackdownfirst
.memupfirst:
	mov bx, paras(NONBOOTINITSTACK_END)
	mov ah, 4Ah
	int 21h
	jnc @F
.memfail:
	mov dx, imsg.early_mem_fail
.earlyfail:
	call init_putsz_cs
	mov ax, 4CFFh
	int 21h

@@:
.stackdownfirst:
	mov ax, ds
	add ax, paras(NONBOOTINITSTACK_START)
	cli
	mov ss, ax
	mov sp, NONBOOTINITSTACK_SIZE
	sti

		; if jumped to .stackdownfirst: now, shrink our memory block
		; else: no-op (already grew or shrunk block)
	mov bx, paras(NONBOOTINITSTACK_END)
	mov ah, 4Ah
	int 21h
	jc .memfail


	mov ax, ds
	add ax, paras(INITSECTIONOFFSET)
	mov dx, ds
	add dx, paras(NONBOOTINITTARGET)
	mov cx, init_size_p
	call init_movp

	push dx
	call init_retf

	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)
  CODETARGET1_equ equ CODETARGET1
  CODETARGET2_equ equ CODETARGET2
  AUXTARGET1_equ equ AUXTARGET1
  AUXTARGET2_equ equ AUXTARGET2
  %if AUXTARGET1_equ <= CODETARGET1_equ
%assign nn AUXTARGET1_equ
%assign mm CODETARGET1_equ
   %error Unexpected layout aux = nn code = mm
  %endif
 %endif
	mov cx, dx
	call init_check_auxbuff
	jz @F

	mov bx, ds
	mov dx, bx
	add bx, paras(AUXTARGET2)
	add dx, paras(CODETARGET2)
 %if _HISTORY_SEPARATE_FIXED && _HISTORY
  ldebug_code_size_equ equ ldebug_code_size
  auxbuff_size_equ equ auxbuff_size
  %if (paras(AUXTARGET1_equ) + paras(auxbuff_size_equ)) \
	!= (paras(CODETARGET2_equ) + paras(ldebug_code_size_equ))
   %error Unexpected layout
  %endif
 %endif
	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.
	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
	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 _HISTORY_SEPARATE_FIXED && _HISTORY
	push ax
 %endif
	mov ax, ds
	add ax, paras(CODESECTIONOFFSET)
	mov cx, ldebug_code_size_p
	call init_movp

	mov word [code_seg], dx		; initialise code segment reference
 %if _HISTORY_SEPARATE_FIXED && _HISTORY
	pop ax
	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

	mov ax, bx

	mov word [auxbuff_segorsel + soaSegSel], ax
%if _PM
	mov word [auxbuff_segorsel + soaSegment], ax
					; initialise auxbuff references
%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
	sti

	mov ah, 4Ah
	mov bx, paras(NONBOOTINITSTACK_START)
	int 21h				; shrink to drop init stack

	jmp old_initcode


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
		;	cs:0 -> loaded payload
		;	cs:32 -> entry point
		; 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
	mov al, byte [bp + bsBPB + ebpbNew + bpbnBootUnit]
	mov byte [load_data - LOADDATA2 + bsBPB + ebpbNew + bpbnBootUnit], al

	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

@@:
	lodsb
	test al, al
	jz @FF

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

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

@@:
	stosb
@@:

	mov ax, dx
	sub ax, paras(BOOTDELTA)
	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_size,
		;  plus 16 bytes for the image ident prefix paragraph,
		;  and all of that rounded to a kibibyte boundary.

	mov cx, cs
	add cx, paras(init_size + BOOTINITSTACK_SIZE)
	jc .error_out_of_memory
	cmp cx, dx
	ja .error_out_of_memory

	mov di, cs
	cli
	mov ss, di
	mov sp, init_size + BOOTINITSTACK_SIZE
	sti

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

	lframe none
	lvar word,	relocatedparas
	lvar word,	target
	lenter
	lvar word,	targetstart
	 push ax
	lvar word,	memtop
	 push dx
	lea di, [bx + 10h]
	lvar word,	data
	 push di
	lea di, [bx + paras(CODESECTIONOFFSET)]
	lvar word,	code
	 push di

	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]
	sub ax, paras(init_size + BOOTINITSTACK_SIZE)
	jc .error_out_of_memory		; already at start of memory -->
	cmp ax, 60h
	jb .error_out_of_memory		; already at start of memory -->

	push cs
	pop ds
	xor si, si			; -> init source
	mov es, ax
	xor di, di			; -> init destination
	mov cx, words(init_size + BOOTINITSTACK_SIZE)
	rep movsw			; relocate only init
		; Must not modify the data already on the stack here,
		;  until after .done_relocation (which relocates ss).

	push ax
	call init_retf			; jump to new init

	mov ss, ax
	mov cx, word [bp + ?code]
	add cx, paras(ldebug_code_size)
	cmp cx, word [bp + ?targetstart]
					; does code end below-or-equal target ?
	jbe .done_relocation		; yes, relocated enough -->

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

	mov dx, 60h
	mov es, dx
	mov ax, cs
	cmp dx, ax			; already at start of memory ?
	jae .error_out_of_memory	; then error -->

	inc dx
	; cmp dx, ax
	; ja .error_out_of_memory
	 push dx
	 push word [cs:.word_relocated]	; on stack: far address of .relocated

	mov cx, ax			; source
	sub cx, dx			; source - target = how far to relocate
	mov word [bp + ?relocatedparas], cx
					; save away this value

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

	mov es, dx
	xor di, di			; -> where to relocate to
	xor si, si			; -> relocate start

BOOTRELOC1 equ	paras( init_size + BOOTINITSTACK_SIZE + ldebug_data_entry_size \
			+ asmtable1_size + asmtable2_size \
			+ ldebug_code_size)

%if 0
	mov cx, BOOTRELOC1		; how much to relocate
	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

	align 2, db 0
.word_relocated:
	dw .relocated

		; ds:si -> first chunk of to be relocated data
		; es:di -> first chunk of relocation destination
		; cx = number of words in first chunk
.relocator:
	rep movsw
	retf				; jump to relocated cs : .relocated

.relocated:
@@:
	mov dx, es
	add dx, bx
	mov es, dx	; next segment

	mov dx, ds
	add dx, bx
	mov ds, dx	; 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 -->
	add ax, bx	; restore
	shl ax, 1
	shl ax, 1
	shl ax, 1	; convert paragraphs to words
	xchg cx, ax	; cx = that many words
	xor ax, ax	; no more to relocate after this round

@@:
	xor si, si
	xor di, di
	rep movsw	; relocate next chunk
	test ax, ax	; another round needed?
	jnz @BB		; yes -->

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

	mov ax, word [bp + ?relocatedparas]
	sub word [bp + ?data], ax
	jc .error_internal
	sub word [bp + ?code], ax
	jc .error_internal

	mov cx, word [bp + ?code]
	add cx, paras(ldebug_code_size)
	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:
.no_relocation:
	mov ax, cs
	mov ss, ax			; relocate the stack
		; The stack frame variables have been relocated here
		;  along with the INIT segment data.

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

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


	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

	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

	inc byte [cs:init_boot_ebdaflag]
	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
@@:


	mov cx, word [bp + ?memtop]
	add cx, [cs:init_boot_ebdasize]
	sub cx, paras(INITSECTIONOFFSET + datastack_size + auxbuff_size + historysegment_size)
					; cx = paragraph of pseudo-PSP if here
	dec cx				; => paragraph of image ident
	and cx, ~ (paras(1024) - 1)	; round down to kibibyte boundary
	inc cx				; => paragraph of pseudo-PSP if here

	mov bx, cx
	mov dx, bx
	add bx, paras(AUXTARGET1)	; => auxbuff target if here
	add dx, paras(CODETARGET1)	; => code target if here
 %if _HISTORY_SEPARATE_FIXED && _HISTORY
	mov ax, bx
	add ax, paras(auxbuff_size)
  %if AUXTARGET1_equ <= CODETARGET1_equ
   %error Unexpected layout
  %endif
 %endif
	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
	add bx, paras(AUXTARGET2)	; => auxbuff target if here
	add dx, paras(CODETARGET2)	; => code target if here
 %if _HISTORY_SEPARATE_FIXED && _HISTORY
  %if (paras(AUXTARGET1_equ) + paras(auxbuff_size_equ)) \
	!= (paras(CODETARGET2_equ) + paras(ldebug_code_size_equ))
   %error Unexpected layout
  %endif
 %endif
	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]
	sub cx, paras(INITSECTIONOFFSET + datastack_size + auxbuff_size*2 + historysegment_size)
					; cx = paragraph of pseudo-PSP if here
	dec cx				; => paragraph of image ident
	and cx, ~ (paras(1024) - 1)	; round down to kibibyte boundary
	inc cx				; => paragraph of pseudo-PSP if here

	mov bx, cx
	mov dx, bx
	add bx, paras(AUXTARGET1)	; => auxbuff target if here
		; Note that we use AUXTARGET1 here, not AUXTARGET3, because
		;  we move where the debugger starts rather than where it ends.
	add dx, paras(CODETARGET1)	; => code target if here
 %if _HISTORY_SEPARATE_FIXED && _HISTORY
	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 _HISTORY_SEPARATE_FIXED && _HISTORY
	push ax
 %endif
	mov word [bp + ?target], cx
	push dx
	cmp byte [cs:init_boot_ebdaflag], 0
	jz .reloc_memtop_no_ebda
	dec cx
	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]
	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
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
	mov cx, ldebug_code_size_p	; = size
	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
	mov cx, paras(ldebug_data_entry_size + asmtable1_size + asmtable2_size)
	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 _HISTORY_SEPARATE_FIXED && _HISTORY
	pop ax
	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

	mov ax, bx
	mov word [auxbuff_segorsel + soaSegSel], ax
%if _PM
	mov word [auxbuff_segorsel + soaSegment], ax
					; initialise auxbuff references
%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

init_boot_imageident:
	mov ax, ds
	dec ax
	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 ?

	 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

	mov cl, 8
	rep movsw			; write image ident paragraph

	mov ax, word [bp + ?target]

	lleave ctx			; dropping this frame for stack switch

	cli
	mov ds, ax
	mov ss, ax
	mov sp, stack_end		; switch stacks
	sti

	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

	jmp boot_old_initcode


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
	mov sp, init_size + BOOTINITSTACK_SIZE
	sti

.no_switch_stack:
	push ax

	mov ax, 40h
	mov es, ax

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

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

	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 _DEBUG4 || _DEBUG5
%define _DEB_ASM_PREFIX init_
%include "deb.asm"
%endif


%macro __writepatchtable2 0-*.nolist
%if %0 & 1
 %fatal Expected even number of arguments
%endif
%rep %0 >> 1
	%1 %2
%rotate 2
%endrep
%endmacro

%macro __patchtable2_entry 0.nolist
  ; only if this isn't the first (pseudo-)entry
  %if %$lastcount != 0
    %assign %$runscount %[%$runscount]+1
   ; if the offset from %$previous is less than 255
   %if (%$last-%$previous) < 255
    %assign %$$method2tablesize %$$method2tablesize+1
    ; then write a single byte (number of bytes not to patch between)
    %xdefine %$$method2list %$$method2list,db,%$last-%$previous
   %else
    ; otherwise write a 255 ("reposition") and write the 16-bit address afterwards
    %assign %$$method2tablesize %$$method2tablesize+3
    %xdefine %$$method2list %$$method2list,db,255,dw,%$last
    %assign %$reposcount %[%$reposcount]+1
   %endif
   %if %$lastcount == 1
    %assign %$onecount %[%$onecount]+1
   %endif
   %assign %$$method2tablesize %$$method2tablesize+1
   ; and write the number of bytes to be patched
   %xdefine %$$method2list %$$method2list,db,%$lastcount
   ; define %$previous for the next entry: it points to the next non-patched byte
   %define %$previous (%[%$last]+%[%$lastcount])
  %endif
%endmacro

%macro writepatchtable 2-*.nolist

	numdef %{1}_FORCE_METHOD, 0
%push
		; Determine length of simple table:
%assign %$method1tablesize (%0 - 2)*2

%if !_%{1}_FORCE_METHOD || _%{1}_FORCE_METHOD == 2
		; Determine length of complicated table:
%assign %$method2tablesize 0
%define %$method2list db,""
%push
%if _WPT_LABELS
 %define %$previous code_start	; if list contains labels
%else
 %define %$previous 0
%endif
%define %$last %[%$previous]
%assign %$lastcount 0
%assign %$onecount 0
%assign %$reposcount 0
%assign %$bytescount %0 - 2
%assign %$runscount 0
%rotate 1
%rep %0 - 2
 %rotate 1
 ;if  it continues the previous patch   and not too long   and this isn't the first
 %if ((%$last+%$lastcount) == %1) && (%$lastcount < 255) && (%$lastcount != 0)
  ; then do not write an entry, just increase the patch's size
  %assign %$lastcount %[%$lastcount]+1
 %else
  ; otherwise write the last entry
  __patchtable2_entry
  ; define new %$last to this parameter, %$lastcount to one
  %define %$last %1
  %assign %$lastcount 1
 %endif
%endrep
__patchtable2_entry

; at the end, there's a patch with offset 0, size 0
%assign %$$method2tablesize %$$method2tablesize+2
%xdefine %$$method2list %$$method2list,db,0,db,0

%assign %$$onecount %$onecount
%assign %$$reposcount %$reposcount
%assign %$$bytescount %$bytescount
%assign %$$runscount %$runscount
%pop
%rotate 1
%endif

%if _%{1}_FORCE_METHOD == 2
 %define __%{1}_method 2
%elif _%{1}_FORCE_METHOD == 1
 %define __%{1}_method 1
%else
 %if _%{1}_FORCE_METHOD
  %fatal Invalid forced method selected: _%{1}_FORCE_METHOD
 %endif
 %if %$method1tablesize > (%$method2tablesize+20)
  %define __%{1}_method 2
 %else
  %define __%{1}_method 1
 %endif
%endif


%1:
%if __%{1}_method == 2
	__writepatchtable2 %$method2list
	endarea %1
%assign %$size %1_size
%warning %1: %$size bytes (Method 2)
%warning 1B=%$onecount repo=%$reposcount run=%$runscount byte=%$bytescount
%else
%rotate 1
%rep %0 - 2
%rotate 1
	dw %1
%endrep
%rotate 1
	endarea %1
%assign %$size %1_size
%warning %1: %$size bytes (Method 1)
%endif
%pop
%endmacro

	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

%unmacro __writepatchtable2 0-*.nolist
%unmacro __patchtable2_entry 0.nolist
%unmacro writepatchtable 2-*.nolist


%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:
	db "NDEB00"
.check:	dw 0
.size:	dw 0
	times 3 dw 0
%endif
	align 2, db 0
memsize:	dw paras(CODETARGET2 \
			+ ldebug_code_size \
			+ historysegment_size)
			; same as paras(AUXTARGET1 + auxbuff_size + historysegment_size)

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


imsg:
.early_mem_fail:
	db _PROGNAME,": Failed to allocate memory!"
.crlf:
	asciz 13,10
.early_reloc_fail:
	asciz _PROGNAME,": Failed to relocate, internal error!",13,10
.help.defaultfilename:
	db _FILENAME
.help.defaultfilename.length equ $ - .help.defaultfilename
.help.1:
	db _PROGNAME,_VERSION,", debugger.",13,10
	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
	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
%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

.invalidswitch:
	db "Invalid switch - "
.invalidswitch_a:
	asciz "x",13,10
.switch_c_error:
	asciz "Switch /C invalid content",13,10
%if _BOOTLDR
.default_cmdline:
	db _BOOTSCRIPTPREFIX
	db "@if exists y ldp/",_BOOTSCRIPTNAME," :",_BOOTSCRIPTLABEL
	db " then y ldp/",_BOOTSCRIPTNAME," :",_BOOTSCRIPTLABEL
	asciz
.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
%endif
%if _DOSEMU
.dosemudate:	db "02/25/93"
%endif
%if _VDD
.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 -->
		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
	neg cx
	dec 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
	neg cx
	dec 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
	testopt [ss:internalflags], nodosloaded
	jz @F

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
	mov bx, 1			; standard output
	mov ah, 40h			; write to file
	int 21h
	retn



%if _BOOTLDR
		; Initial entry when boot loading.

		; 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

	mov word [execblk+2], 80h
	mov byte [81h], 0Dh
	mov byte [fileext], EXT_OTHER	; empty file name and command line as per N
%endif	; _BOOTLDR

old_initcode:
	cld
	d0bp
	mov ax, ds
	mov word [ execblk+4 ], ax
	mov word [ execblk+8 ], ax
	mov word [ execblk+12 ], ax	; set up parameter block for exec command
	mov word [ pspdbg ], ax

	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
	testopt [internalflags], nodosloaded
	jz .checkio
d4	call init_d4message
d4	asciz "Common initialisation, determining processor type now",13,10
	jmp .determineprocessor
.checkio:
%endif
		; 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
	or 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
.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 -->

		; 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
	db 8Fh, 0C1h		; pop r/m16 with cx as operand
				;  (reportedly a nop on the NECs)
	mov sp, ax		; reset stack to known state
	jcxz .found_186_plus	; if it was a nop -->
	jmp .cpudone		; is an actual 8088/8086 -->

.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

	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.
cpu 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
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 ]
	cmp al, 5			; a Pentium or above always will have a FPU
	jae .fpudone
	dec byte [ has_87 ]		; assume no FPU

	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

		; 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
__CPU__
.fpudone:

%if _ONLY386
	testopt [internalflags], has386
	jnz @F				; okay -->
 %if _BOOTLDR
	testopt [internalflags], nodosloaded
	lahf				; remember status
 %endif
	mov dx, imsg.no386
	call init_putsz_cs		; display the error
 %if _BOOTLDR
	sahf
	jnz init_booterror.soft		; abort for loader -->
 %endif
	mov ax, 4C01h
	int 21h				; abort our process

@@:
%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]
	mov si, patch_386_table		; table of patches to set for 386+
%if __patch_386_table_method == 1
	mov cx, patch_386_table_size_w
%endif
	testopt [internalflags], has386
%if __patch_386_table_method == 1
	jnz .patch1			; set these patches (CPU is 386+) -->
%else
	jnz .patch2			; set these patches (CPU is 386+) -->
%endif
%ifn _ONLYNON386
	mov byte [es:..@patch_no386_ds], 3Eh	; write a ds prefix
	mov byte [es:..@patch_no386_iret], 0CFh	; write an iret instruction
 %if _PM && _CATCHPMINT214C
	mov byte [es:..@patch_no386_ds_2], 3Eh
	mov byte [es:..@patch_no386_ds_3], 3Eh
	mov byte [ss:..@patch_no386_nop_DATA_ENTRY], 90h
					; write a nop (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_size_w
 %if __patch_386_table_method == 2
	jmp short .patch1		; skip .patch2 code -->
 %endif
%endif

		; Complicated table patch code.
%if __patch_no386_table_method == 2 || __patch_386_table_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_386_table_method == 1 || __patch_no386_table_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:

		; 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
.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 word [ savesp ], stack_end-2; save new SP minus two (for the word we'll push)
	mov di, ..@init_first
	mov cx, ..@init_behind - ..@init_first
	xor ax, ax
	rep stosb			; initialise breakpoint lists, line_out
%if 1
%if ..@init_behind != stack
	mov di, stack
%endif
	mov cx, stack_end - stack
	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 _BOOTLDR
	testopt [internalflags], nodosloaded
	jz initdos
d4	call init_d4message
d4	asciz "386-related patches applied, boot initialisation proceeding",13,10


%if CATCHINTAMOUNT
		; Set up interrupt vectors.
	mov cx, inttab_number
	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
	push word [ bx+2 ]
	push word [ bx ]	; get vector
	xchg ax, di
	ss lodsw		; get address of IISP header
	xchg ax, di
	pop word [ ss:di + ieNext ]
	pop word [ ss:di + ieNext + 2 ]
				; store it
%ifn _DEBUG			; vectors are set only when debuggee runs
	mov word [ bx+2 ], ss
	mov word [ bx ], di	; set interrupt vector
%endif
	loop .bootintloop
%endif


	push ss
	pop ds
	push ss
	pop es

d4	call init_d4message
d4	asciz "Jumping to final boot initialisation code",13,10
	push word [code_seg]
	push word [cs:.word_initcont.boot_entry]
	retf

	align 2, db 0
.word_initcont.boot_entry:
	dw initcont.boot_entry
%endif

initdos:
%if _MCB || _INT
	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
	int 21h
	mov ax, word [ es:bx-2 ]; start of MCBs
	mov word [firstmcb], ax

getfirstumcb:
			; 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.
	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]
	push ds
	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
.none:
.got:
	pop bx
	mov ax, 5803h
	int 21h			; restore UMB link
%endif

	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
	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
	mov bx, ds			; bx = our PSP
	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

		; 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
.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, dl
	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:
	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
@@:
	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
	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
	mov ax, 4C00h
	int 21h			; done

.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 -?
	cmp al, 'c'
	je .switch_c
	cmp al, 'C'
	je .switch_c

		; 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
	mov ax, 4C01h		; Quit and return error status
	int 21h

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

	mov di, cmdline_buffer
	mov ah, 0		; initialise to 0 = unquoted
	db __TEST_IMM16
.switch_c_loop:
	stosb
.switch_c_quoted:
	lodsb

	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
	jmp .switch_c_loop

.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 dx, imsg.switch_c_error
	jmp ..@init_cmdline_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


.noswitches:
		; Feed the remaining command line to the 'n' command.
	dec si
	push si


%if CATCHINTAMOUNT
		; Set up interrupt vectors.
	mov cx, inttab_number
	mov si, inttab
.intloop:
	lodsb
	mov ah, 35h
	int 21h			; get vector
	xchg ax, di
	lodsw
	xchg ax, di
	mov word [ di + ieNext ], bx
	mov word [ di + ieNext + 2 ], es
				; store it
%ifn _DEBUG			; vectors are set only when debuggee runs
	mov dx, di
	mov ah, 25h		; set interrupt vector
	int 21h			; ds => lDEBUG_DATA_ENTRY
%endif
	loop .intloop
%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:
	setopt [internalflags], nohook2F
.dpmihostchecked:
 %endif
%endif
	push ds
	pop es

		; Save, then modify termination address and parent PSP.
	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)

		; shrink to required resident size
	push ds
	pop es
	mov ah, 4Ah
	mov bx, word [cs:memsize]

	push word [code_seg]
	push word [cs:.word_initcont]
	retf

	align 2, db 0
.word_initcont:
	dw initcont


	usesection lDEBUG_CODE
initcont:
	int 21h				; resize to required
;	jc ...				; (expected to work since it had to be larger. also we hooked ints)

	push ds
	pop es
	call getint2324			; init run2324 to avoid using or displaying NUL vectors

	push ds
	pop es
	pop si
	lodsb
	call nn				; process the rest of the command line

.boot_entry:
	push ds
	pop es				; => lDEBUG_DATA_ENTRY

	mov si, cmd3
%if _BOOTLDR
	testopt [internalflags], nodosloaded
	jz @F
	jmp si				; directly jump to cmd3 of the installed image
@@:
%endif
	push si
	jmp ll3				; load a program if one has been given at the command line
