
%if 0

lDebug M commands (move, machine type)

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


	usesection lDEBUG_CODE

%if _LOADER
		; INP:	bx = how many paragraphs wanted (including image ident)
		; OUT:	CY if error,
		;	 ax = error code
		;	 ss:dx -> error message (ASCIZ)
		;	NC if success,
		;	 ax = 0 if no EBDA
		;	 ax = 1 if EBDA present
		;	 dx => image ident address
		;	 bx = rounded up amount paragraphs
		;	 cx = EBDA size in paragraphs
		;	 dx - cx => EBDA destination if ax = 1
		; CHG:	ax, bx, cx, dx
loader_get_resident_address: section_of_function
	push ds
	push si
	push di

	cmp byte [ss:currentposition], 0
	jne .error_must_be_bottom

	test bx, bx
	jz .error_empty
	mov ax, paras(1024) - 1
	add bx, ax
	jc .error_oom
	not ax
	and bx, ax		; = size in KiB
	call bootgetmemorysize
	int 12h
	mov cl, 6
	shl ax, cl
	cmp ax, dx
	jne .error_rpl

	xor cx, cx
	mov ds, cx
	cmp word [40Eh], 400h	; have an EBDA ?
	jb @F
	cmp word [40Eh], dx
	jne .error_ebdanottop
	mov ds, dx
	xor ax, ax		; ah = 0
	mov al, byte [0]	; EBDA size in KiB
	mov cl, 6
	shl ax, cl
	mov cx, ax		; cx = EBDA size in paras
	add dx, ax		; => behind EBDA
	jc .error_toolargeebda
.sub:
	sub dx, bx
	jc .error_oom		; if not branching: NC
	mov ax, 1
	jmp .done_j1

@@:
	sub dx, bx
	jc .error_oom
	xor ax, ax		; NC
.done_j1:
	jmp .done

.error_ebdanottop:
	mov dx, msg.move_ebdanottop
	mov ax, 0284h
	jmp .specificerror

.error_toolargeebda:
	mov dx, msg.move_toolargeebda
	mov ax, 0285h
	jmp .specificerror

.error_empty:
	mov dx, msg.move_empty
	mov ax, 0291h
	jmp .specificerror

.error_must_be_bottom:
	mov dx, msg.move_must_be_bottom
	mov ax, 0292h
	jmp .specificerror

.error_rpl:
	mov dx, msg.move_rpl
	mov ax, 0281h
	jmp .specificerror

.error_oom:
	mov dx, msg.move_oom
	mov ax, 0283h
.specificerror:
	stc
.done:
	pop di
	pop si
	pop ds
	retn



		; INP:	bx = how many paragraphs wanted (including image ident)
		; OUT:	CY if error,
		;	 ax = error code
		;	 ss:dx -> error message (ASCIZ)
		;	NC if success,
		;	 ax = 0 if no EBDA
		;	 ax = 1 if EBDA present
		;	 dx => image ident address,
		;	  image ident initialised (NEXT00), remainder zeroed
		;	 bx = rounded up amount paragraphs
		;	 cx = EBDA size in paragraphs, 0 if ax = 0
		;	 dx - cx => EBDA destination if ax = 1
		; CHG:	ax, bx, cx, dx
loader_prepare_resident: section_of_function
	push ds
	push es
	push si
	push di

	call loader_get_resident_address
	jc .done
	xor si, si
	mov ds, si		; ds => IVT, BDA
	push ax
	test ax, ax
	jz .no_ebda
	push dx
	mov ax, word [40Eh]	; => EBDA source
	sub dx, cx		; => EBDA destination
	call movp		; move EBDA
	mov word [40Eh], dx	; relocate
	call .set_memsize	; set mem size to point to EBDA
	pop dx
	jmp @F

.no_ebda:
	call .set_memsize	; set mem size to point to NEXT image ident
@@:
	push cx
	push dx

	mov si, bx		; = how many paragraphs to init
.loop_init:
	mov cx, paras(64 * 1024)
	cmp si, cx		; > 64 KiB left ?
	jae @F			; yes -->
	mov cx, si		; no, do remainder only
@@:
	sub si, cx		; si = remainder after this round
	mov es, dx		; => current chunk
	add dx, cx		; => next chunk
	xchg ax, cx		; ax = paragraphs this round
	mov cl, 3
	shl ax, cl		; ax = words this round
	xchg ax, cx		; cx = words this round
	xor di, di		; es:di -> current chunk
	xor ax, ax		; ax = 0
	rep stosw		; init to all-zeroes
	test si, si		; another round needed ?
	jnz .loop_init		; yes -->

	pop dx

	mov ds, dx		; => image ident
	xor si, si
	mov word [si + liiSignature + 0], "NE"
	mov word [si + liiSignature + 2], "XT"
	mov word [si + liiVersion], "00"
				; write "NEXT00"
	mov word [si + liiAmountParagraphs], bx
				; write size
	mov cx, words(16)
	xor di, di
@@:
	lodsw
	add di, ax		; add all words (liiChecksum == 0)
	loop @B
	neg di			; get checksum
	mov word [liiChecksum], di
				; finish the header
	pop cx
	pop ax
	clc
.done:
	pop di
	pop si
	pop es
	pop ds
	retn

.set_memsize:
	push dx
	push cx
	mov cl, 6
	shr dx, cl
	mov word [413h], dx
	pop cx
	pop dx
	retn


	usesection lDEBUG_DATA_ENTRY

		; INP:	cs => NLDR image ident
		;	ip = this word field content
		;	ax = 60h
		; OUT:	ax => behind memory requested (should be >= 60h)
		; CHG:	es, ds, di, si, bx, cx, dx
		; STT:	far called, UP, EI
loader_query:
	cmp ax, 60h
	jne loader_error
	add ax, word [cs:liiAmountParagraphs]
	retf


		; INP:	cs => NLDR image ident
		;	ip = this word field content
		;	ax = 60h
		; OUT:	ax => relocated NLDR segment (usually 60h)
		; CHG:	es, ds, di, si, bx, cx, dx
		; STT:	far called, UP, EI
loader_transfer_relocate:
	cmp ax, 60h
	jne loader_error

	push ax
	push bp
	mov dx, ss
	mov bx, sp
	cli
	 push cs
	 pop ss
	mov sp, stack_end		; have to run on our stack
	sti
	push dx
	push bx
	times 1 - (($ - $$) & 1) nop	; align in-code parameter
	call entry_to_code_seg
	dw .code

	usesection lDEBUG_CODE
.code:
	push ss
	pop ds
	push ss
	pop es
	mov bx, movetable.bottom
	call moveloader.change_no_line

	pop bx
	pop dx				; -> caller's stack
	cli
	mov ss, dx
	mov sp, bx			; restore caller's stack
	sti
	pop bp
	pop ax				; ax, bp preserved
	retf


	usesection lDEBUG_DATA_ENTRY

loader_error:
	mov bx, cs
	cli
	mov ss, bx
	mov sp, stack_end
	sti
	mov ds, bx
	mov es, bx
	times 1 - (($ - $$) & 1) nop	; align in-code parameter
	call entry_to_code_seg
	dw .code

	usesection lDEBUG_CODE
.code:
	mov dx, msg.loader_error
loader_error_msg:
	call putsz
	jmp cmd3


	usesection lDEBUG_DATA_ENTRY

		; INP:	es = ds = cs = ss => relocated NLDR
		;	ip = this word field content
		;	sp = next word field content
		; STT:	running as debuggee, UP, EI
loader_transfer_cmd3:
	times 1 - (($ - $$) & 1) nop	; align in-code parameter
	call entry_to_code_seg
	dw cmd3


iispentry intr_loader_18, 80h, mm_sharedentry
	mov dx, int18msg
	jmp @F
mm_sharedentry.hwreset:
	retf

iispentry intr_loader_19, 80h, mm_sharedentry
	mov dx, int19msg
@@:
	mov ax, cs
	cli
	mov ss, ax
	mov sp, stack_end
	sti
	mov ds, ax
	mov es, ax
	times 1 - (($ - $$) & 1) nop	; align in-code parameter
	call entry_to_code_seg
	dw loader_error_msg


	usesection lDEBUG_CODE

moveloader_hook_ints:
	cmp byte [currentposition], 2
	jne .ret

	push bx

	mov al, 18h		; interrupt number
	mov si, intr_loader_18	; -> IISP entry header
	mov dx, opt4_int_18_force >> 16
 %if (opt4_int_18_force >> 16) == dif4_int_18_hooked
	call cmd3_int_enable.set_bx_to_dx
 %else
	mov bx, dif4_int_18_hooked
	call cmd3_int_enable
 %endif

	mov al, 19h		; interrupt number
	mov si, intr_loader_19	; -> IISP entry header
	mov dx, opt4_int_19_force >> 16
 %if (opt4_int_19_force >> 16) == dif4_int_19_hooked
	call cmd3_int_enable.set_bx_to_dx
 %else
	mov bx, dif4_int_19_hooked
	call cmd3_int_enable
 %endif

	pop bx

.ret:
	retn


		; INP:	bx => destination for auxbuff
		; OUT:	ZR if this destination for auxbuff doesn't cross
		; 	 a 64 KiB boundary
		;	NZ else
		; CHG:	si, di
moveloader_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


		; INP:	dx => destination
		;	ax => old source
		;	cs => old code segment
		;	cx = delta
		; CHG:	ax, cx, dx, si, di
		; STT:	ds = es = ss => source
moveloader_auxup:
	mov ds, dx
	lframe near
	lenter
	lvar word, source
	 push ax
	lvar word, delta
	 push cx
	lvar word, destination
	 push dx
	mov dx, word [auxbuff_current_size]
	mov cl, 4
	shr dx, cl
	lvar word, auxsize
	 push dx
	mov cx, paras(ldebug_code_size + ldebug_code2_size)	; untruncated
	mov dx, word [auxbuff_segorsel]	; => destination
	mov ax, word [code_seg]		; => source
	call movp
	mov word [code_seg], dx		; update
	mov es, word [bp + ?source]
	add dx, cx			; => behind code1+2
	mov ax, word [es:auxbuff_segorsel]
					; => unrelocated auxbuff
	mov cx, word [bp + ?auxsize]
	call movp
	mov word [auxbuff_segorsel], dx
	push ss
	pop ds
	push ss
	pop es
	lleave
	mov dx, msg.move_auxup
	jmp putsz


moveloader_auxdown:
	mov ds, dx
	lframe near
	lenter
	lvar word, source
	 push ax
	lvar word, delta
	 push cx
	lvar word, destination
	 push dx
	mov dx, word [auxbuff_current_size]
	mov cl, 4
	shr dx, cl
	lvar word, auxsize
	 push dx
	mov cx, dx
	mov dx, word [code_seg]		; => destination
	mov ax, word [auxbuff_segorsel]	; => source
	call movp
	mov word [auxbuff_segorsel], dx	; update
	mov es, word [bp + ?source]
	add dx, cx			; => behind aux
	mov ax, word [es:code_seg]
					; => unrelocated code1+2
	mov cx, paras(ldebug_code_size + ldebug_code2_size)	; untruncated
	call movp
	mov word [code_seg], dx
	push ss
	pop ds
	push ss
	pop es
	lleave
	mov dx, msg.move_auxdown
	jmp putsz


moveloader_auxnop:
	retn


		; INP:	ss = ax => old data/entry segment
		;	dx => new segment (stack and code is usable)
		; CHG:	si, di, es
		; OUT:	ds => old segment
		;	doesn't return if error
moveloader_rehook_serial:
	push bx
	push cx
	push ax
	push dx

	mov ds, ax		; ! => old segment
	testopt [internalflags4], dif4_int_serial_hooked
	jz .done_serial
	call serial_uninstall_interrupt_handler	; CHG si, ax, dx, di, es
		; ! es = ss
	jnc .rehook_serial	; if it succeeded -->

	mov di, msg.serial_cannot_unhook.int
	mov al, byte [serial_installed_intnum]
	call hexbyte
	mov dx, msg.serial_cannot_unhook.nowarn
	call putsz_error
	mov dx, msg.move_int_panic
	call putsz_error
	call near [moveloader_undo]
	jmp cmd3

.rehook_serial:
		; old segment will be discarded soon, so do not reset these
	; setopt [internalflags4], dif4_int_serial_hooked
	; call update_inttab_optional

	 pop dx			; dx => new segment
	 push dx
	mov ss, dx		; ! need to run on new stack
	nop
	push ds			; => old segment
	mov ds, dx		; ss = ds => new segment
	mov es, dx		; harden
	mov al, byte [serial_installed_intnum]
	mov si, serial_interrupt_handler
	call install_86m_interrupt_handler	; CHG: ax, bx, cx, dx
	pop dx			; => old segment
	mov ss, dx		; ! careful, return stack frame is on old stack
	nop
	mov ds, dx
	mov es, dx

.done_serial:

%if _CATCHINT2D
				; ! ds => old segment
	testopt [internalflags4], dif4_int_2D_hooked
	jz .done_2D
	; call serial_uninstall_interrupt_handler ; CHG si, ax, dx, di, es
	mov si, int2D
	mov al, 2Dh
	mov dx, opt4_int_2D_force >> 16
	call UnhookInterruptForce
	jc @F
	clropt [internalflags4], dif4_int_2D_hooked
	call update_inttab_optional
				; (NC)
@@:
		; ! es = ss
	jnc .rehook_2D	; if it succeeded -->

	pop dx
	push dx			; => new segment
	mov ds, dx
	mov es, dx
	mov ss, dx
	nop

	testopt [internalflags4], dif4_int_serial_hooked
	jz .2D_panic_done_serial
	call serial_uninstall_interrupt_handler	; CHG si, ax, dx, di, es
		; ! es = ss
	jnc .2D_panic_rehook_serial	; if it succeeded -->
	jmp moveloader.haltmsg	; panic if this happens

.2D_panic_rehook_serial:
	 pop dx
	 pop ax
	 push ax
	 push dx
	mov ds, ax
	mov es, ax
	mov ss, ax		; => old segment
	nop

	mov al, byte [serial_installed_intnum]
	mov si, serial_interrupt_handler
	call install_86m_interrupt_handler	; CHG: ax, bx, cx, dx
		; new segment will be discarded soon, so do not reset these
		;  there. but do reset them in old segment now !
	setopt [internalflags4], dif4_int_serial_hooked
	call update_inttab_optional
.2D_panic_done_serial:

	mov di, msg.serial_cannot_unhook.int
	mov al, 2Dh
	call hexbyte
	mov dx, msg.serial_cannot_unhook.nowarn
	call putsz_error
	mov dx, msg.move_int_panic
	call putsz_error
	call near [moveloader_undo]
	jmp cmd3

.rehook_2D:
		; old segment will be discarded soon, so do not reset these
	; setopt [internalflags4], dif4_int_2D_hooked
	; call update_inttab_optional

	 pop dx			; dx => new segment
	 push dx
	mov ss, dx		; ! need to run on new stack
	nop
	push ds			; => old segment
	mov ds, dx		; ss = ds => new segment
	mov es, dx		; harden
	mov al, 2Dh
	mov si, int2D
	call install_86m_interrupt_handler	; CHG: ax, bx, cx, dx
	pop dx			; => old segment
	mov ss, dx		; ! careful, return stack frame is on old stack
	nop
	mov ds, dx
	mov es, dx

.done_2D:
%endif

	pop dx
	pop ax
	pop cx
	pop bx
	retn


moveloader_check_serial:
	mov al, byte [serial_installed_intnum]
	mov si, serial_interrupt_handler
	push cx			; (make space)
	mov di, sp		; es:di -> word on stack
	call hexbyte		; write byte value as text
	pop cx			; cx = what to write into error message
	mov dx, opt4_int_serial_force >> 16
 %if (opt4_int_serial_force >> 16) == dif4_int_serial_hooked
	call qq_int_unhook_sim.set_bx_to_dx
 %else
	mov bx, dif4_int_serial_hooked
	call qq_int_unhook_sim
 %endif

%if _CATCHINT2D
	mov al, 2Dh
	mov si, int2D
	mov cx, "2D"
	mov dx, opt4_int_2D_force >> 16
 %if (opt4_int_2D_force >> 16) == dif4_int_2D_hooked
	call qq_int_unhook_sim.set_bx_to_dx
 %else
	mov bx, dif4_int_2D_hooked
	call qq_int_unhook_sim
 %endif
%endif
	retn


		; INP:	ss => source
		;	dx => destination
		; CHG:	ax, cx, si, di, ds, es
		; OUT:	doesn't return if error
		;	cx -> code to handle auxbuffer (after entire movp)
moveloader_check_dx_destination:
	push bp
	push bx
	push dx

	push ss
	pop ds
	push ss
	pop es
	xor bp, bp
.serial:
	call moveloader_check_serial

	mov dx, msg.move_int_error
	mov ax, 0287h
	test bp, bp
	jnz .specificerror

	pop dx
	push dx

	mov ax, ss
	mov cx, dx
	sub cx, ax		; cx = dx - ax
				; cx + ax = dx
	mov bx, word [auxbuff_segorsel]	; bx => auxbuff
	add bx, cx
	call moveloader_check_auxbuff
	jz .ret_nop
	mov dx, word [code_seg]
	add dx, cx

	cmp dx, bx
	je .error
	jb .auxdown		; if cs < aux, put aux to cs
.auxup:				; if cs > aux, put aux to aux + cs size
	add bx, paras(ldebug_code_size + ldebug_code2_size)	; untruncated
	call moveloader_check_auxbuff
	mov cx, moveloader_auxup
	jz .ret
	jmp .error

.auxdown:
	mov bx, dx
	call moveloader_check_auxbuff
	mov cx, moveloader_auxdown
	jz .ret

.error:
	mov dx, msg.move_auxbuff_error
	mov ax, 0286h
.specificerror:
	jmp moveloader.specificerror

.ret_nop:
	mov cx, moveloader_auxnop
.ret:
	pop dx
	pop bx
	pop bp
	retn

	usesection lDEBUG_DATA_ENTRY
	align 2, db 0
moveloader_undo:	dw moveloader_undo_noop
	usesection lDEBUG_CODE


moveloader_undo_ebda_move:
	xor ax, ax
	mov ds, ax
	mov dx, word [40Eh]
	mov es, dx
	mov al, byte [es:0]	; EBDA size in KiB
	mov cl, 6
	shl ax, cl
	mov cx, ax		; cx = EBDA size in paras
	mov ax, dx		; => EBDA source (moved here previously)
	add dx, [ss:liiAmountParagraphs]
				; => EBDA destination
	call movp		; move EBDA
	xor ax, ax
	mov ds, ax
	mov word [40Eh], dx	; relocate EBDA segment pointer
	mov cl, 6
	shr dx, cl
	mov word [413h], dx	; relocate memory size in KiB
	 push ss
	 pop es
	 push ss
	 pop ds
	mov word [moveloader_undo], moveloader_undo_noop
moveloader_undo_noop:
	retn


moveloader:
	call skipcomma
	call iseol?
	je .query
	dec si
	mov bx, movetable
.loop:
	mov dx, word [bx + mteIdentifier]
	test dx, dx
	jz .error
	call isstring?
	je .change
	add bx, MOVETABLEENTRY_size
	jmp .loop

.error:
	jmp error

.query:
	mov al, [currentposition]
	cmp al, 2
	ja .error
	mov ah, MOVETABLEENTRY_size
	mul ah
	xchg bx, ax
	mov dx, word [movetable + bx + mteStatusMessage]
	jmp putsz

.change:
	call skipwhite
	call chkeol
.change_no_line:
	xor ax, ax
	mov al, [currentposition]
	cmp al, 2
	ja .error
	add ax, ax
	xchg si, ax
	mov word [moveloader_undo], moveloader_undo_noop
	jmp word [bx + mteFromArray + si]

.already:
	mov dx, msg.isalready
.statusplus:
	 push ss
	 pop ds
	call putsz
	mov dx, [bx + mteStatusMessage]
	inc dx
	jmp putsz


		; INP:	dx => destination
		;	ss => source
		;	ss => DATA ENTRY STACK segment
		; OUT:	es = ds = ss => destination
		;	cs => destination code segment
		; CHG:	ax, cx, dx, si, di, es
.mov:
	call moveloader_check_dx_destination
	push cx
	mov ax, ss
	push ax
	mov cx, [ss:liiAmountParagraphs]
	call movp		; move entire program (including stack !)
	mov cx, dx
	sub cx, ax		; cx = dx - ax
				; cx + ax = dx
	mov ds, dx		; => destination segment
	mov si, movesegrefstable
@@:
	lodsw
	test ax, ax		; end of table ?
	jz @F
	xchg di, ax		; ds:di -> segment reference
	add word [di], cx	; fixup
	jmp @B

@@:
	pop ax			; => old position
	pop di			; -> function to swap auxbuff/code if needed
	push dx
	push ax
	mov ds, ax
	mov es, ax
	call di
		; INP:	dx => destination
		;	ax = ds = ss = es => old source
		;	cs => old code segment
		;	cx = delta
		; CHG:	ax, cx, dx, si, di
	pop ax
	pop dx
	call moveloader_rehook_serial

	cmp byte [currentposition], 2
	jne @F
	push bx
	push dx

	mov al, 18h		; interrupt number
	mov si, intr_loader_18	; -> IISP entry header
	mov dx, opt4_int_18_force >> 16
 %if (opt4_int_18_force >> 16) == dif4_int_18_hooked
	call cmd3_int_disable.set_bx_to_dx
 %else
	mov bx, dif4_int_18_hooked
	call cmd3_int_disable
 %endif
 	jnc .18done
	jz .18done

.haltmsg:
	mov dx, msg.move_int_panic
	call putsz
.halt:
	int3
	sti
	hlt
	jmp .halt

.18done:
	mov al, 19h		; interrupt number
	mov si, intr_loader_19	; -> IISP entry header
	mov dx, opt4_int_19_force >> 16
 %if (opt4_int_19_force >> 16) == dif4_int_19_hooked
	call cmd3_int_disable.set_bx_to_dx
 %else
	mov bx, dif4_int_19_hooked
	call cmd3_int_disable
 %endif
 	jnc .19done
	jz .19done
	jmp .haltmsg

.19done:
	pop dx
	pop bx

@@:
	mov ds, dx
	mov es, dx
	mov ss, dx		; => new data entry stack segment
	nop			; ! near return address must be on new stack
	clropt [internalflags4], dif4_int_18_hooked | dif4_int_19_hooked
				; fix the flags in new segment
	call update_inttab_optional

	push word [code_seg]
	call .retf
	mov al, [bx + mteArrayIndex]
	mov byte [currentposition], al
	call moveloader_hook_ints
	retn

.retf:
	retf

.top_to_bottom:
	mov dx, 60h
	call .mov
	mov dx, msg.isnow
	call .statusplus
	retn

.hidden_to_bottom:
	xor bp, bp

	push bx
		; redundant call to find inability early
	call moveloader_check_serial

	mov al, 18h
	mov si, intr_loader_18
	mov cx, "18"
	mov dx, opt4_int_18_force >> 16
 %if (opt4_int_18_force >> 16) == dif4_int_18_hooked
	call qq_int_unhook_sim.set_bx_to_dx
 %else
	mov bx, dif4_int_18_hooked
	call qq_int_unhook_sim
 %endif

	mov al, 19h
	mov si, intr_loader_19
	mov cx, "19"
	mov dx, opt4_int_19_force >> 16
 %if (opt4_int_19_force >> 16) == dif4_int_19_hooked
	call qq_int_unhook_sim.set_bx_to_dx
 %else
	mov bx, dif4_int_19_hooked
	call qq_int_unhook_sim
 %endif

	mov dx, msg.move_int_error
	mov ax, 0290h
	test bp, bp
	jnz .specificerror
	pop bx

	call bootgetmemorysize
	int 12h
	mov cl, 6
	shl ax, cl
	cmp ax, dx
	jne .error_rpl
	mov cx, ss
	cmp cx, dx
	je @F
	xor ax, ax
	mov ds, ax		; ! ah = 0
	cmp word [40Eh], dx
	jne .error_nottop
	mov ds, dx
	mov al, byte [0]	; EBDA size in KiB
	mov cl, 6
	shl ax, cl
	mov cx, ax		; cx = EBDA size in paras
	add ax, dx
	jc .error_oom
	push cx
	push dx
	mov cx, ss
	cmp ax, cx
	jne .error_nottop
	mov dx, 60h
	call .mov
	pop dx
	pop cx
	mov ax, dx		; => EBDA
	add dx, [liiAmountParagraphs]
	call movp
	xor ax, ax
	mov ds, ax
	mov word [40Eh], dx
	mov cl, 6
	shr dx, cl
	mov word [413h], dx
	mov dx, msg.isnow
	call .statusplus
	retn

@@:
	mov dx, 60h
	call .mov
	mov dx, word [liiAmountParagraphs]
	mov cl, 6
	shr dx, cl
	xor ax, ax
	mov ds, ax
	add word [413h], dx
	mov dx, msg.isnow
	call .statusplus
	retn

.bottom_to_top:
	call bootgetmemorysize
	sub dx, word [liiAmountParagraphs]
	call .mov
	mov dx, msg.isnow
	call .statusplus
	retn

.error_rpl:
	mov dx, msg.move_rpl
	mov ax, 0281h
	jmp .specificerror

.error_nottop:
	mov dx, msg.move_nottop
	mov ax, 0282h
	jmp .specificerror

.error_oom:
	mov dx, msg.move_oom
	mov ax, 0283h
	jmp .specificerror

.error_ebdanottop:
	mov dx, msg.move_ebdanottop
	mov ax, 0284h
	jmp .specificerror

.error_toolargeebda:
	mov dx, msg.move_toolargeebda
	mov ax, 0285h
	jmp .specificerror

.hidden_to_top:
.top_to_hidden:
	mov dx, msg.unsupportedmove
	mov ax, 0280h
.specificerror:
	 push ss
	 pop ds
	call setrc
	call putsz_error
	jmp cmd3

.bottom_to_hidden:
	call bootgetmemorysize
	int 12h
	mov cl, 6
	shl ax, cl
	cmp ax, dx
	jne .error_rpl
	xor ax, ax
	mov ds, ax
	cmp word [40Eh], 400h	; have an EBDA ?
	jb @F
	cmp word [40Eh], dx
	jne .error_ebdanottop
	mov ds, dx
	mov al, byte [0]	; EBDA size in KiB
	mov cl, 6
	shl ax, cl
	mov cx, ax		; cx = EBDA size in paras
	add ax, dx		; => behind EBDA
	jc .error_toolargeebda
	sub ax, [ss:liiAmountParagraphs]
				; => NLDR destination
	jc .error_oom
	 push ax
	  push cx
	  push dx
	  xchg dx, ax
	call moveloader_check_dx_destination
	  pop dx
	  pop cx
	mov ax, dx		; => EBDA source
	sub dx, [ss:liiAmountParagraphs]
				; => EBDA destination
	jc .error_oom
	call movp		; move EBDA
	xor ax, ax
	mov ds, ax
	mov word [40Eh], dx	; relocate EBDA segment pointer
	mov cl, 6
	shr dx, cl
	mov word [413h], dx	; relocate memory size in KiB
	 pop dx
	mov word [ss:moveloader_undo], moveloader_undo_ebda_move
	call .mov		; relocate us
	mov dx, msg.isnow
	call .statusplus
	retn

@@:
	sub dx, [ss:liiAmountParagraphs]
				; => NLDR destination
	jc .error_oom
	push dx
	call .mov
	pop dx
	xor ax, ax
	mov ds, ax
	mov cl, 6
	shr dx, cl
	mov word [413h], dx	; relocate memory size in KiB
	mov dx, msg.isnow
	call .statusplus
	retn
%endif


		; M command - move from place to place.
		;
		; First check for machine-related M commands.
		; Those are: M, MNC, M?, MC, MC2, MC3, M [one expression]
		; Move M command has more than one expression.
mm:
%if _LOADER
	dec si
	dec si
	mov dx, msg.move
	call isstring?
	je moveloader
	inc si
	lodsb
%endif
	mov cx, si		; - 1 -> input
	push si
	call iseol?
	je mc			; no argument, CPU-related M command
	mov ah, byte [ si ]
	push ax
	and ax, ~(2020h)
	cmp ax, "NC"
	pop ax
	jne @F
	mov cx, msg.c0 + 1	; - 1 -> C0 string
	inc si			; skip 'N'
	jmp .checkend

@@:
	cmp al, '?'
	jne @F
	mov cx, msg.cr + 1	; - 1 -> empty string
.checkend:
	call skipwhite		; skip '?' or 'C' (in "NC")
	call iseol?
	je mc
	pop si
	push si
	dec si
	call skipwhite
@@:
	push si
	call prephack
	mov bx, word [reg_ds]	; get source range
	nearcall getaddrX	; just parse an address first
		; Note that valid MC commands allow at most C3h
		;  in the expression read here. This must be
		;  allowed by this getaddrX call regardless the
		;  D/B bit and limit of the ds segment.
		; By using getaddrX here instead of getexpression
		;  as previously, we get support for all special
		;  cases of address parameters for free. This now
		;  includes the single or double dollar sign prefix
		;  and also the taken keywords (not previously
		;  allowed here).
	pop cx			; - 1 -> input
	call iseol?
	je mc			; one argument, CPU-related

		; bx:(e)dx, si, al are already initialised here.
.mm:
	pop di			; discard si on stack

			; It is a normal M command (Move)
%if _PM
	mov di, getaddr		; second parameter must be writable
%endif
	call parsecm_have_address
				; parse arguments (DS:ESI, ES:EDI, ECX)
	push cx
%if _PM
	call ispm
	jnz .rm
	mov ax, ds
	mov cx, es
	cmp ax, cx
	je .pmsimple		; same selector, simple -->

	mov ax, 0006h
	mov bx, ds
	int 31h			; get selector's base
	jc error
	push cx
	push dx
	mov ax, 0006h
	mov bx, es
	int 31h			; get selector's base
	jc error		; throw
	cmp byte [ss:bAddr32], 0
	je .pm16
[cpu 386]
	pop eax
	push cx
	push dx
	pop edx			; mov edx, cxdx
	add eax, esi		; add offset to source selector's base
	jc error
	add edx, edi		; add offset to destination selector's base
	jc error		; if overflow (> 4 GiB) -->
	cmp eax, edx		; compare linear source to linear destination
	jmp short m3		; and decide whether to move up or down -->
__CPU__

.rm:
	mov ax, ds
	mov bx, ds
	mov dx, es
	mov cl, 12
	shr bx, cl
	shr dx, cl
	push dx
	mov dx, es
	mov cl, 4
	shl ax, cl
	shl dx, cl
	pop cx
	db __TEST_IMM16		; (skip 2 pop instructions)

.pm16:
	pop ax
	pop bx
	add ax, si
	adc bx, byte 0		; add offset to source selector's base
	jc error
	add dx, di
	adc cx, byte 0		; add offset to destination selector's base
	jc error		; if overflow (> 4 GiB) -->
	cmp bx, cx		; compare linear source to linear destination
	jne m3
	cmp ax, dx
	jmp short m3		; and decide whether to move up or down -->

.pmsimple:
	_386_o32	; cmp esi, edi
	cmp si, di
%else
	mov dx, di
	mov bx, es
	mov cl, 4
	shr dx, cl
	add dx, bx		; upper 16 bits of destination
	mov ax, si
	shr ax, cl
	mov bx, ds
	add ax, bx
	cmp ax, dx
	jne m3			; if we know which is larger
	mov ax, si
	and al, 0Fh
	mov bx, di
	and bl, 0Fh
	cmp al, bl
%endif
m3:	pop cx
	lahf
	push ds
	push es
	push ss			; ds := cs
	pop ds
	call dohack		; do the interrupt pointer hack
	pop es
	pop ds
	sahf
	jae .forward		; if forward copy is OK
	_386_PM_o32
	add si, cx
	_386_PM_o32
	add di, cx		; point both behind data
	std			; _AMD_ERRATUM_109_WORKAROUND as below


	numdef AMD_ERRATUM_109_WORKAROUND, 1
		; Refer to comment in init.asm init_movp.

%if _AMD_ERRATUM_109_WORKAROUND
	_386_PM_a32
	jcxz @FF
	_386_PM_o32
	cmp cx, strict byte 20
	ja @FF
@@:
	_386_PM_a32
	movsb
	_386_PM_a32
	loop @B
@@:
%endif
.forward:
	_386_PM_a32
	rep movsb		; do the move
	_386_PM_a32
	movsb			; one more byte (length of zero means 64 KiB. or 4 GiB..)
.was32:
	cld			; restore flag
	jmp ee0a		; restore segments and undo the interrupt pointer hack


		; Other M command: set machine type.
		;
		; INP:	cx -> numeric input (expression 0..6, C, C0, C2, C3)
		;	or cx -> EOL
		;	word [ss:sp] = to discard
mc:
	mov si, cx
	pop dx			; discard
	dec si
	call skipwhite		; reload
	call iseol?
	je mquery		; if just an M or M? (query machine type) -->
	nearcall getbyte	; get numeric input
	call chkeol		; insure valid
	xchg ax, dx
	cmp al, 6
	ja mc_fpu

mc_cpu:
	mov byte [machine], al	; set machine type
	mov byte [mach_87], al	; coprocessor type, too

mc_encode:
	cmp byte [has_87], 0
	mov al, 0C0h
	je .done
	cmp byte [machine], 3
	mov al, 0Ch
	jne .done
	cmp byte [mach_87], 2
	jne .done
	mov al, 0C2h
.done:
	mov byte [encodedmach87], al
	retn

mc_fpu:
	mov ah, byte [machine]
	cmp al, 0Ch		; MC command ?
	je mcc_ah
	cmp al, 0C0h		; MC0 command or MNC command ?
	je mnc
	cmp ah, 3		; MC2 or MC3 only valid for machine 386
	jne .error
	cmp al, 0C2h		; MC2 command ?
	je mcc_2
	cmp al, 0C3h		; MC3 command ?
	je mcc_3		; (ah = 3)
.error:				; invalid input
	jmp error

mnc:
	mov byte [has_87], 0	; clear coprocessor flag
	jmp mc_encode		; done

mcc_2:
	mov ah, 2		; set type to 287
mcc_3:				; (if jumping here ah = 3) set type to 387
mcc_ah:
	mov byte [has_87], 1	; set coprocessor flag
	mov byte [mach_87], ah	; set coprocessor type
	jmp mc_encode		; done


		; Display machine type.
mquery:
	mov si, msg8088
	mov al, byte [machine]
	cmp al, 0
	je .88or86		; if 8088
	mov si, msgx86
	add al, '0'
	mov byte [si], al
.88or86:
	call showstring
	mov si, no_copr
	cmp byte [has_87], 0
	je .m12			; if no coprocessor
	mov si, has_copr
	mov al, byte [mach_87]
	cmp al, byte [machine]
	je .m12			; if has coprocessor same as processor
	mov si, has_287
.m12:
	call showstring		; show string
	jmp putsline_crlf	; call puts and quit
