
%if 0

lDebug - libre 86-DOS debugger

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

[list -]
%if 0

lDebug build notes (OUTDATED)

This section is superceded by the manual's chapter on building.

Compile MKTABLES:	wcl -ox -3 -d__MSDOS__ mktables.c
or:	gcc -xc MKTABLES.C -o mktables -Wno-write-strings -DOMIT_VOLATILE_VOID
Execute MKTABLES:	mktables
	(This deletes debugtbl.old then creates debugtbl.inc.)
	(A temporary file named debugtbl.tmp is used.)
Assemble DEBUG.COM:	nasm debug.asm -I../macro/ -oDEBUG.COM
Assemble DEBUGX.COM:	nasm debug.asm -I../macro/ -d_PM -oDEBUGX.COM

OpenWatcom 1.9 was used for compiling MKTABLES but it should reasonably well
work with other C compilers. gcc 6.3.0 is now also able to compile MKTABLES.
Assembling lDebug requires NASM (2.12.01 tested).

Note that MKTABLES only needs to be used if either the source files changed or
the MKTABLES program itself has been altered. If the assembler and disassembler
table is not to change, NASM is sufficient to assemble lDebug.


lDebug build options

There are some more options that can be used on the NASM command line to create
alternative lDebug versions. Unless otherwise specified, each option is a flag
and so should be set to either 1 (to enable an option) or 0 (to disable it).
Omitting a numeric value as well as the equals sign enables the option too.

-d_FILENAME=	Set base string for file name. Up to six characters.
-d_PROGNAME=	Set program name string displayed in help screens.
-d_VERSION=	Set version string displayed after _PROGNAME. Include a blank.
-d_REVISIONID=	Set revision ID string. (Disabled if empty or undefined.)
-d_PM=		Set DPMI support.
-d_DEBUG=	Set support for debugging lDebug, for lDebug development. The
		interrupt handlers will be reset to these of the next debugger
		in lDebug code and the BU command breaks to the next debugger.
-d_EMS=		Set EMS allocation commands (XA, XD, XM, XR, XS, X?).
-d_INT=		Set command (DI) to display exception and interrupt handlers.
-d_MCB=		Set command (DM) to display DOS Memory Control Blocks.
-d_RN=		Set command (RN) to display numerical co-processor registers.
-d_DSTRINGS=	Set commands (DZ, D$, D#, DW#) to display strings in memory.
-d_SDUMP=	Set that the search command (S) displays memory after matches.
-d_COND=	Set that the register dump (R, T, P, G) displays jump notices.
-d_USESDA=	Set that switching processes uses the DOS Swappable Data Area.
-d_VDD=		Set NTVDM direct disk access support via loading DEBXXVDD.DLL.
-d_EXPRESSIONS=	Set expression evaluator that accepts calculations.
		(Currently is required enabled.)
-d_VARIABLES=	Set 256 32-bit variables (V0..VFF) that can be set freely.
-d_OPTIONS=	Set 32-bit option variables (DCO, DCS, DIF, DAO, DAS).
-d_BOOTLDR=	Set boot loading support.
-d_BREAKPOINTS=	Set permanent breakpoint support (B commands).
-d_NUM_B_BP=	Set number of permanent breakpoints. Must be numerical.
-d_NUM_G_BP=	Set number of temporary breakpoints. Must be numerical.
-d_MMXSUPP=	Set command (RM) to display MMX registers.
-d_CATCHINT06=	Set that Interrupt 06h (Invalid opcode) is hooked.
-d_CATCHINT08=	Set that Interrupt 08h (IRQ0, timer) is hooked. (Special!)
-d_CATCHINT18=	Set that Interrupt 18h (Diskless boot hook) is hooked.
-d_CATCHINT19=	Set that Interrupt 19h (Boot load) is hooked.
-d_STACKSIZE=	Set size of lDebug's stack (in byte). Must be numerical.
-d_AUXBUFFSIZE=	Set size of the auxiliary buffer (in byte). Must be numerical.
	(The following only apply if DPMI support is enabled.)
-d_NOEXTENDER=	Set support of DPMI hosts without DOS extender.
-d_EXCCSIP=	Set to display where exceptions inside lDebug occured.
-d_CATCHEXC06=	Set that exception 06h (Invalid opcode) is hooked.
-d_CATCHEXC0C=	Set that exception 0Ch (Stack fault) is hooked.
-d_DISPHOOK=	Set to display when the DPMI entry is hooked.

Refer to the list in debug.mac for the default values of these options. You
can also adjust the defaults there. Refer to the source code and commentary
for what each option does if these descriptions are not specific enough.


lDebug contributions

lDebug is based on DEBUG/X 1.13 to 1.18 as released by Japheth, whose work
on DEBUG/X has been released as Public Domain. (Some changes up to version
1.27 were picked up from DEBUG/X since.)

%endif

%include "debug.mac"
[list +]

%ifndef _MAP
%elifempty _MAP
%else	; defined non-empty, str or non-str
	[map all _MAP]
%endif

%if _ONLY386
	cpu 386
%else
	cpu 8086
%endif
	org 100h
	addsection lDEBUG_DATA_ENTRY, align=16 start=100h
data_entry_start:

	addsection ASMTABLE1, align=16 follows=lDEBUG_DATA_ENTRY
	addsection ASMTABLE2, align=16 follows=ASMTABLE1
	addsection lDEBUG_CODE, align=16 follows=ASMTABLE2 vstart=0
code_start:
	addsection DATASTACK, align=16 follows=ASMTABLE2 nobits
	addsection INIT, align=16 follows=lDEBUG_CODE vstart=0


	usesection lDEBUG_DATA_ENTRY

%define DATASECTIONFIXUP -data_entry_start+100h
%define CODESECTIONFIXUP -code_start+0
%define CODESECTIONOFFSET (100h+ldebug_data_entry_size+asmtable1_size+asmtable2_size)
%define INITSECTIONOFFSET (CODESECTIONOFFSET+ldebug_code_size)

%define CODETARGET1 (CODESECTIONOFFSET+datastack_size)
%define CODETARGET2 (CODETARGET1+auxbuff_size)

%define AUXTARGET1 (CODETARGET1+ldebug_code_size)
%define AUXTARGET2 CODETARGET1
%define AUXTARGET3 AUXTARGET1+auxbuff_size

%define NONBOOTINITTARGET (INITSECTIONOFFSET \
			+ datastack_size \
			+ auxbuff_size * 2 \
			+ historysegment_size)
%define NONBOOTINITSTACK_START (NONBOOTINITTARGET+init_size)
NONBOOTINITSTACK_SIZE equ 512	; must be even
%define NONBOOTINITSTACK_END (NONBOOTINITSTACK_START+NONBOOTINITSTACK_SIZE)

BOOTINITSTACK_SIZE equ 512	; must be divisible by 16
%define BOOTDELTA	(fromkib(kib(auxbuff_size * 2 \
				+ historysegment_size \
				+ datastack_size \
				+ INITSECTIONOFFSET + 16)))


%if _DEVICE
		; Note: Once this is implemented, the final
		;  copy of this device header should live
		;  in front of our PSP. Therefore, this space
		;  after the PSP can be re-used for the newly
		;  expanded N buffer. (Refer to N_BUFFER_END.)
	fill 4,0FFh,jmp initcode_j
	dw 8000h
	dw deviceinit -$$
	dw deviceinit.retf -$$
	fill 8,32,db "DEBUG$$"
%else
	jmp initcode_j
%endif
		; Startup codes can be discarded after one of
		;  them is used to enter the initialisation part.
		;  Therefore the N buffer is now extended past
		;  these codes, refer to N_BUFFER_END.
%if _BOOTLDR
	align 32, db 0
	mov ax, cs
	sub ax, 10h
	mov ds, ax
	mov bx, boot_initcode
	jmp @F
%endif
	align 64, db 0
initcode_j:
	mov ax, cs
	xor bx, bx
@@:
	add ax, paras(INITSECTIONOFFSET)
	push ax
	push bx
	retf


	align 2, db 0
N_BUFFER_END equ $		; end of N buffer (starts in PSP at 80h)

cmdlist:	dw aa,bb,cc,ddd,ee,ff,gg,hh,ii,error,error,ll,mm,nn,oo
		dw pp,qq,rr,sss,tt,uu,error,ww,xx,yy

	align 4, db 0
				; options, startoptions and internalflags
				; have to be consecutive
options:	dd DEFAULTOPTIONS ; run-time options
dispregs32	equ	  1	; RX: 32-bit register display (R, T/P/G)
traceints	equ	  2	; TM: trace into interrupts (T)
cpdepchars	equ	  4	; allow dumping of CP-dependant characters (D, DX)
fakeindos	equ	  8	; always assume InDOS flag non-zero (all)
nonpagingdevice	equ	 10h	; disallow paged output with [more] prompt (all exc. P, T)
pagingdevice	equ	 20h	; allow paged output with [more] prompt (all exc. P, T)
				; paged output is by default on if the output device is StdOut, else off
hexrn		equ	 40h	; display raw hexadecimal content of FPU registers (RN)
;novdd		equ	 80h	; don't use a registered NTVDM VDD (L, W)
nondospaging	equ	100h	; paging: don't use DOS for input when waiting for a key
nohlt		equ	200h	; HLT doesn't work, don't use it
biosidles	equ	400h	; don't idle with HLT or Int2F.1680, only call BIOS Int16.00
opt_userawinput	equ	800h	; use rawinput for int 21h interactive input
use_si_units	equ    1000h	; in disp_*_size use SI units (kB = 1000, etc)
use_jedec_units	equ    2000h	; in disp_*_size use JEDEC units (kB = 1024)
enable_serial	equ    4000h	; enable serial I/O (preferred over DOS or BIOS terminal)
int8_disable_serial equ	   8000h	; disable serial I/O when breaking due to intr8
gg_do_not_skip_bp equ	 1_0000h	; gg: do not skip a breakpoint (bb or gg)
gg_no_autorepeat equ	 2_0000h	; gg: do not auto-repeat
tp_do_not_skip_bp equ	 4_0000h	; T/TP/P: do not skip a (bb) breakpoint
gg_bb_hit_no_repeat equ	 8_0000h	; gg: do not auto-repeat after bb hit
tp_bb_hit_no_repeat equ	10_0000h	; T/TP/P: do not auto-repeat after bb hit
gg_unexpected_no_repeat equ 20_0000h	; gg: do not auto-repeat after unexpectedinterrupt
tp_unexpected_no_repeat equ 40_0000h	; T/TP/P
ss_no_dump:		equ 80_0000h
rr_disasm_no_rept:	equ 1000_0000h
rr_disasm_no_show:	equ 2000_0000h
opt_cmdline_quiet_input:equ 4000_0000h
opt_cmdline_quiet_output:equ 8000_0000h
DEFAULTOPTIONS	equ 0

options2:	dd DEFAULTOPTIONS2
opt2_db_header:		equ 1
opt2_db_trailer:	equ 2
opt2_dw_header:		equ 10h
opt2_dw_trailer:	equ 20h
opt2_dd_header:		equ 100h
opt2_dd_trailer:	equ 200h
opt2_rawinput_dpmi:	equ 800h
opt2_hh_compat:		equ 1000h
opt2_getc_idle:		equ 2000h
opt2_getc_idle_dpmi:	equ 4000h
opt2_re_cancel_tpg:	equ 8000h
DEFAULTOPTIONS2	equ opt2_dw_header | opt2_dd_header | opt2_getc_idle_dpmi

options3:	dd DEFAULTOPTIONS3
opt3_tt_no_paging:	equ 1
opt3_tp_no_paging:	equ 2
opt3_pp_no_paging:	equ 4
opt3_gg_no_paging:	equ 8
opt3_silence_paging_set:equ 100h
opt3_silence_paging_on:	equ 200h
opt3_r_highlight_diff:	equ 10000h
opt3_r_highlight_dumb:	equ 20000h
opt3_r_highlight_full:	equ 40000h
opt3_r_highlight_eip:	equ 80000h
opt3_no_idle_2F:	equ  200_0000h
%if _DELAY_BEFORE_BP
opt3_delay_before_bp:	equ  400_0000h
%endif
opt3_no_call_update:	equ  800_0000h
opt3_disable_autorepeat:equ 1000_0000h
opt3_check_ctrlc_keyb:	equ 2000_0000h
opt3_check_ctrlc_0bh:	equ 4000_0000h
opt3_tsr_quit_leave_tf:	equ 8000_0000h
DEFAULTOPTIONS3	equ opt3_tt_no_paging | opt3_tp_no_paging \
		| opt3_pp_no_paging | opt3_gg_no_paging \
		| opt3_check_ctrlc_keyb | opt3_check_ctrlc_0bh

options4:	dd DEFAULTOPTIONS4
opt4_int_2F_hook:	equ 2
opt4_int_08_hook:	equ 4
opt4_int_2D_hook:	equ 8
opt4_int_serial_force:	equ 1_0000h
opt4_int_2F_force:	equ 2_0000h
opt4_int_08_force:	equ 4_0000h
opt4_int_2D_force:	equ 8_0000h
opt4_int_00_force:	equ  100_0000h
opt4_int_01_force:	equ  200_0000h
opt4_int_03_force:	equ  400_0000h
opt4_int_06_force:	equ  800_0000h
opt4_int_18_force:	equ 1000_0000h
opt4_int_19_force:	equ 2000_0000h
DEFAULTOPTIONS4 equ opt4_int_2F_hook
	; options, options2, options3, options4
	;  are each assumed to be dwords
	;  and all consecutive in expr.asm isvariable?

startoptions:	dd DEFAULTOPTIONS ; options as determined during startup; read-only for user
startoptions2:	dd DEFAULTOPTIONS2
startoptions3:	dd DEFAULTOPTIONS3
startoptions4:	dd DEFAULTOPTIONS4
	; startoptions, startoptions2, startoptions3, startoptions4
	;  are each assumed
	;  to be dwords and all consecutive in expr.asm isvariable?

internalflags:	dd attachedterm|pagedcommand|notstdinput|inputfile|notstdoutput|outputfile|(!!_PM*dpminohlt)|debuggeeA20|debuggerA20
				; flags only modified by DEBUG itself
oldpacket	equ	  1	; Int25/Int26 packet method available (L, W)
newpacket	equ	  2	; Int21.7305 packet method available (L, W)
ntpacket	equ	  4	; VDD registered and usable (L, W)
pagedcommand	equ	  8	; allows paging in puts
notstdinput	equ	 10h	; DEBUG's StdIn isn't a device with StdIn bit (is file or other device)
inputfile	equ	 20h	; DEBUG's StdIn is a file, notstdinput also set
notstdoutput	equ	 40h	; DEBUG's StdOut isn't a device with StdOut bit (is file or other device)
outputfile	equ	  80h	; DEBUG's StdOut is a file, notstdoutput also set
hooked2F	equ	 100h	; Int2F hooked
nohook2F	equ	 200h	; don't hook Int2F.1687 (required for Win9x, DosEmu?)
dpminohlt	equ	 400h	; DPMI doesn't like hlt
protectedmode	equ	 800h	; in (DPMI) protected mode
debuggeeA20	equ	1000h	; state of debuggee's A20
debuggerA20	equ	2000h	; state of debugger's A20 (will be on if possible)
nodosloaded	equ	4000h	; No DOS loaded currently (Boot loader mode)
has386		equ	8000h	; CPU is a 386
usecharcounter	equ    1_0000h	; don't reset charcounter between calls to puts
runningnt	equ    2_0000h	; running in NTVDM
canswitchmode	equ    4_0000h	; can switch modes (auxbuff large enough, DPMI mode switch set up)
modeswitched	equ    8_0000h	; switched mode (now in the mode that we weren't entered in)
promptwaiting	equ   10_0000h	; puts: any more output needs to display a prompt first
switchbuffer	equ   20_0000h	; mode switch needs a buffer (auxbuff)
tsrmode		equ   40_0000h	; in TSR mode; DPI and DPP not valid
attachedterm	equ   80_0000h	; the attached process terminated
runningdosemu	equ  100_0000h	; running in dosemu
; load_is_ldp	equ  200_0000h	; boot load: partition specified as "ldp"
tt_while:	equ  400_0000h	; tt: while condition specified
tt_p:		equ  800_0000h	; tt: proceed past repeated string instructions
tt_silent_mode:	equ 1000_0000h	; tt: run should be silent (dump at end)
tt_silence:	equ 2000_0000h	; tt: silent writing (write to auxbuff instead)
tt_no_bb:	equ 4000_0000h	; tt: do not use bb breakpoints
tt_no_bb_first:	equ 8000_0000h	; tt: do not use bb breakpoints at first

internalflags2:	dd 0
dif2_gg_is_first:	equ   1
dif2_gg_skip_non_cseip:	equ   2
dif2_gg_skip_cseip:	equ   4
dif2_gg_is_gg:		equ   8
dif2_gg_first_detected:	equ  10h
dif2_gg_again:		equ  20h
dif2_tpg_proceed_bp_set:equ  40h
dif2_tpg_keep_proceed_bp: equ 80h
dif2_tpg_have_bp:	equ 100h
dif2_tpg_adjusted_cseip:equ 200h
dif2_tpg_do_not_adjust:	equ 400h
dif2_bp_failure:	equ 800h
dif2_is_pp:		equ 1000h
%if _INPUT_FILE_HANDLES
dif2_input_file:	equ 10_0000h
dif2_closed_input_file:	equ 20_0000h
%endif
dif2_did_getline_file:	equ 40_0000h
dif2_boot_loaded_kernel:equ 100_0000h
%if _INPUT_FILE_BOOT
dif2_input_file_boot:	equ 200_0000h
dif2_closed_input_file_boot:	equ 400_0000h
%endif
dif2_in_silence_dump:	equ 1000_0000h
dif2_int31_segment:	equ 2000_0000h

internalflags3:	dd dif3_partition_changed
dif3_load_is_ldp:	equ 1	; boot load: partition specified as "ldp"
dif3_load_is_sdp:	equ 2	; boot load: partition specified as "sdp"
dif3_load_is_ydp:	equ 4	; boot load: partition specified as "ydp"
dif3_load_is_dp:	equ dif3_load_is_ldp \
				| dif3_load_is_sdp \
				| dif3_load_is_ydp
dif3_load_dir_dir:	equ 8
dif3_input_terminal_override:	equ 10h
dif3_input_serial_override:	equ 20h
dif3_if_not:		equ 40h
dif3_partition_changed:	equ 80h
;
dif3_input_cmdline:	equ 100h; input reading from cmdline_buffer
dif3_input_cmdline_closed: equ 200h
dif3_at_line_end:	equ 400h
dif3_quiet_input_single:equ 800h
dif3_quiet_output:	equ 1000h
dif3_unquiet_error:	equ 2000h
dif3_unquiet_prompt:	equ 4000h
dif3_unquiet:		equ dif3_unquiet_error | dif3_unquiet_prompt
dif3_return_eof:	equ 8000h
dif3_highlighting:	equ 1_0000h
dif3_do_not_highlight:	equ 2_0000h
dif3_int10_highlight:	equ 2_0000h
%if _DELAY_BEFORE_BP
dif3_delayed		equ  80_0000h
%endif
dif3_input_re:		equ 100_0000h	; input reading from re_buffer
dif3_input_re_closed:	equ 200_0000h
dif3_accept_getrange_0:	equ 400_0000h
%if _INPUT_FILE_HANDLES || _INPUT_FILE_BOOT
dif3_in_if:		equ 800_0000h
%endif
dif3_auxbuff_guarded_1:	equ 1000_0000h
dif3_auxbuff_guarded_2:	equ 2000_0000h
dif3_prior_pm:		equ 4000_0000h
dif3_sss_is_reverse:	equ 8000_0000h

internalflags4:	dd 0
dif4_int_serial_hooked:	equ 1
dif4_int_2F_hooked:	equ 2
dif4_int_08_hooked:	equ 4
dif4_int_2D_hooked:	equ 8
	; internalflags, internalflags2, internalflags3, internalflags4
	;  are each assumed
	;  to be dwords and all consecutive in expr.asm isvariable?

asm_options:	dd DEFAULTASMOPTIONS
disasm_lowercase	equ 1
disasm_commablank	equ 2
disasm_nasm		equ 4
disasm_lowercase_refmem:equ 8
disasm_show_short:	equ 10h
disasm_show_near:	equ 20h
disasm_show_far:	equ 40h
DEFAULTASMOPTIONS equ disasm_lowercase|disasm_commablank|disasm_nasm

asm_startoptions:
		dd DEFAULTASMOPTIONS

gg_first_cseip_linear:	dd 0
gg_next_cseip_linear:	dd 0
tpg_possible_breakpoint:dd 0
gg_deferred_message:	dw msg.empty_message
bb_deferred_message_in_lineout_behind:
			dw 0
		align 4, db 0
tpg_proceed_bp:		times BPSIZE db 0
%if _DEBUG1
		align 2, db 0
test_records_Readmem:		times 6 * 16 db 0
test_records_Writemem:		times 6 * 16 db 0
test_records_getLinear:		times 6 * 16 db 0
test_records_getSegmented:	times 6 * 16 db 0

test_readmem_value:		db 0
%endif
		align 2, db 0
code_seg:	dw 0
%if _PM
code_sel:	dw 0
%endif

		align 16, db 0
cmdline_buffer:	times 256 db 0
.position:	dw cmdline_buffer

		db 13
		align 2, db 13	; insure the re_buffer is prefixed by CR
re_buffer:
.size:		equ _RE_BUFFER_SIZE
		fill .size,0,db "@R"
.end:
.position:	dw re_buffer

		align 4, db 0
re_count:	dd 0
re_limit:	dd 256

		align 2, db 0
%if _PM
auxbuff_switchbuffer_size:	dw 0
%endif
auxbuff_segorsel:segonlyaddress
auxbuff_behind_while_condition:
		dw 0		; -> behind while condition stored in auxbuff
				;  (this is also the first silent buffer entry)
auxbuff_behind_last_silent:
		dw 0		; -> behind last silent buffer entry
tt_silent_mode_number:
		dw 0		; if non-zero: maximum amount of dumps
				;  displayed after T/TP/P while silent
%if _INPUT_FILE_HANDLES
		align INPUTFILEHANDLE_size
input_file_handles:
		times _INPUT_FILE_HANDLES * INPUTFILEHANDLE_size db -1
.active:	dw 0
.to_close:	dw 0
%endif
	align 2, db 0
indos_remember_seek_function:	dw 4201h
indos_remember_seek_handle:	dw -1
	align 4, db 0
indos_remember_seek_offset:	dd 0
%if _INPUT_FILE_BOOT
	align 4, db 0
boot_remember_seek_offset:	dd 0
	align 2, db 0
boot_remember_seek_handle:	dw -1
%endif

charcounter:	db 0		; used by raw output to handle tab
linecounter:	db 0		; used by paging in puts
	align 4, db 0
savesp:		dw 0		; saved stack pointer
		dw 0		; 0 to set high word of esp
re_sp:		dw 0
errret:		dw cmd3		; return here if error
throwret:	dw errhandler	; return here if error - priority, no display
throwsp:	dw stack_end - 2; stack pointer set before jumping to throwret
run_sp:		dw 0		; stack pointer when running
spadjust:	dw 40h 		; adjust sp by this amount for save
pspdbe:		dw 0		; debuggee's PSP (unless DIF&attachedterm)
pspdbg:		dw 0		; debugger's PSP (RM segment)
	align 4, db 0
run2324:	dd 0,0		; debuggee's interrupt vectors 23h and 24h (both modes)
%if _PM
		dd 0
dbg2324:	dw i23pm, i24pm
%endif
%if _VDD
hVdd:		dw -1		; NTVDM VDD handle
%endif
	align 4, db 0
sav2324:	dd 0,0		; debugger's interrupt vectors 23h and 24h (real-mode only)
hakstat:	db 0		; whether we have hacked the vectors or not
	align 4, db 0
psp22:		dd 0		; original terminate address from our PSP
parent:		dw 0		; original parent process from our PSP (must follow psp22)
%if _MCB || _INT
firstmcb:	dw -1		; start of MCB chain (always segment)
firstumcb:	dw -1
%endif
pInDOS:		segofs16address	; far16 address of InDOS flag (bimodal)
%if _USESDA
pSDA:		segofs16address minusone
				; far16 address of SDA (bimodal)
%endif
machine:	db 0		; type of processor for assembler and disassembler (1..6)
has_87:		db 0		; if there is a math coprocessor present
mach_87:	db 0		; type of coprocessor present
%if _MMXSUPP
has_mmx:	db 0
%endif
bInDbg:		db 1		; 1=debugger is running
notatty:	db 10		; if standard input is from a file
				; this is also used for a linebreak processing hack
switchar:	db 0		; switch character
swch1:		db ' '		; switch character if it's a slash
	align 2, db 0
promptlen:	dw 0		; length of prompt
bufnext:	dw line_in+2	; address of next available character
bufend:		dw line_in+2	; address + 1 of last valid character
rc:		dw 0
priorrc:	dw 0
erc:		dw 0
%if _HISTORY
history:
 %if _HISTORY_SEPARATE_FIXED
.segorsel:	segonlyaddress
.first:		dw _HISTORY_SIZE - 2
.last:		dw _HISTORY_SIZE - 2
 %else
.first:		dw historybuffer.end - 2
.last:		dw historybuffer.end - 2
 %endif
%endif

var_addr_entries:
a_addr:		segmentedaddress; address for next A command
d_addr:		segmentedaddress; address for next D command; must follow a_addr
behind_r_u_addr:segmentedaddress; address behind R's disassembly
u_addr:		segmentedaddress; address for next U command; must follow d_addr
e_addr:		segmentedaddress; address for current/next E command
%if _DSTRINGS
dz_addr:	segmentedaddress; address for next ASCIZ string
dcpm_addr:	segmentedaddress; address for next $-terminated string
dcount_addr:	segmentedaddress; address for next byte-counted string
dwcount_addr:	segmentedaddress; address for next word-counted string
%endif
var_addr_entries.amount equ ($ - var_addr_entries) / SEGADR_size
%if _PM
x_addr:		dd 0		; (phys) address for next DX command
%endif
%if _DSTRINGS
dstringtype:	db 0		; FFh byte-counted, FEh word-counted, else terminator byte
	align 2, db 0
dstringaddr:	dw dz_addr	; -> address of last string
%endif
%if _INT
	align 4, db 0
intaddress:	dd 0
lastint_is_86m_and_mcb:
		dw 0
lastint:	db 0
%endif
	align 4, db 0
search_results:
%if _PM
		times 6 * 16 db 0
%else
		times 4 * 16 db 0
%endif
	align 4, db 0
sscounter:	dd 0

rrmask:		dw 0
rrtype:		db 0
eqflag:		db 0		; flag indicating presence of `=' operand
	align 2, db 0
eqladdr:	dw 0,0,0	; address of `=' operand in G, P and T command
	align 2, db 0
run_int:	dw 0		; interrupt type that stopped the running
lastcmd:	dw dmycmd
bInit:		db 0		; 0=ensure a valid opcode is at debuggee's CS:IP
fileext:	db 0		; file extension (0 if no file name)
EXT_OTHER	equ 1
EXT_COM		equ 2
EXT_EXE		equ 4
EXT_HEX		equ 8

	align 4, db 0
mmxbuff:	dd 0		; buffer with a (read-only) part of MMX register
				; for access from within expressions
%if _CATCHINT08
intr8_counter:	dw 0
%endif
maxmachinetype:	db 0
serial_rows:
		db 24
serial_columns:
		db 80
serial_keep_timeout:
		db 15
%if _USE_TX_FIFO
serial_fifo_size:
		db _BI_TX_FIFO_SIZE
			; size of built-in TX fifo (1 is as if no FIFO)
%endif
serial_flags:
		db 0
sf_init_done:	equ 1
sf_ctrl_c:	equ 2
sf_double_ctrl_c:	equ 4
sf_built_in_fifo:	equ 8

serial_var_intnum:	db _INTNUM
serial_var_params:	db _UART_PARAMS
serial_var_fifo:	db _UART_FIFO
	align 2, db 0
serial_var_baseport:	dw _UART_BASE
serial_var_dl:		dw _UART_RATE
serial_var_irqmask:	dw _OFFMASK

io_rows:	db 1
io_columns:	db 1
	align 2, db 0
io_start_buffer:	dw 0
io_end_buffer:		dw 0
io_levels:		dw 255
io_flags:		dw DEFAULTIOFLAGS
iof_extra_iol_for_tpg_re:	equ 1
DEFAULTIOFLAGS equ iof_extra_iol_for_tpg_re

	align 2, db 0
getline_timer_count:	dw 0
getline_timer_last:	dw 0
getline_timer_func:	dw dmycmd

%if _INPUT_FILE_HANDLES || _INPUT_FILE_BOOT
	align 2, db 0
if_exists_then_address:	dw 0
if_exists_si:		dw 0
if_exists_sp:		dw 0
if_exists_length:	dw 0
%endif

	align 2, db 0
terminator_in_line_in:
.offset:		dw 0
.value:			db 0

termcode:		db 0

	align 2, db 0
inttab_optional:
.:
serial_installed_intnum: equ $
	dw 0
	dw serial_interrupt_handler
	dw dif4_int_serial_hooked
%if _PM
	dw 2Fh
	dw debug2F
	dw dif4_int_2F_hooked
%endif
%if _CATCHINT08
	dw 8
	dw intr8
	dw dif4_int_08_hooked
%endif
.amount: equ ($ - .) / 6
	dw -1


amisintr_offset:
	dw inttab

inttab_pre:
	times 3 * inttab_optional.amount db 0

inttab:
%if _CATCHINT00
	db 0
	dw intr0	; table of interrupt initialization stuff
%endif
%if _CATCHINT01
	db 1
	dw intr1
%endif
%if _CATCHINT03
	db 3
	dw intr3
%endif
%if _CATCHINT06
	db 6
	dw intr6
%endif
%if _CATCHINT18
	db 18h
	dw intr18
%endif
%if _CATCHINT19
	db 19h
	dw intr19
%endif
	endarea inttab
	inttab_number equ inttab_size / 3
%if _CATCHINT2D
.i2D:	db 2Dh
	dw int2D
%endif


intforcetab:
%if _CATCHINT00
	db opt4_int_00_force >> 24
%endif
%if _CATCHINT01
	db opt4_int_01_force >> 24
%endif
%if _CATCHINT03
	db opt4_int_03_force >> 24
%endif
%if _CATCHINT06
	db opt4_int_06_force >> 24
%endif
%if _CATCHINT18
	db opt4_int_18_force >> 24
%endif
%if _CATCHINT19
	db opt4_int_19_force >> 24
%endif


	align 2, db 0
		; Parameter block for EXEC call
execblk:dw 0		;(00) zero: copy the parent's environment
	dw 0,0		;(02) address of command tail to copy
	dw 5Ch,0	;(06) address of first FCB to copy
	dw 6Ch,0	;(10) address of second FCB to copy
	dw 0,0		;(14) initial SS:SP
	dw 0,0		;(18) initial CS:IP


		; Register save area (32 words).
		; must be DWORD aligned, used as stack
	align 4, db 0
regs:
reg_eax:	dd 0	;+00 eax
reg_ebx:	dd 0	;+04 ebx
reg_ecx:	dd 0	;+08 ecx
reg_edx:	dd 0	;+12 edx
reg_esp:	dd 0	;+16 esp
reg_ebp:	dd 0	;+20 ebp
reg_esi:	dd 0	;+24 esi
reg_edi:	dd 0	;+28 edi
reg_ds:		dd 0	;+32  ds (high word unused)
reg_es:		dd 0	;+36  es (high word unused)
reg_ss:		dd 0	;+40  ss (high word unused)
reg_cs:		dd 0	;+44  cs (high word unused)
reg_fs:		dd 0	;+48  fs (high word unused)
reg_gs:		dd 0	;+52  gs (high word unused)
reg_eip:	dd 0	;+56 eip
reg_efl:	dd 0	;+60 efl(ags)
regs.end:
regs.size: equ regs.end - regs

regs_prior:
.:
	times 16 dd 0
.end:
.size: equ .end - .

%if .size != regs.size
 %error regs prior save area size mismatch
%endif

%if _VARIABLES
vregs: times 256 dd 0	; internal v0..vff
%endif

; possible byte encoding of lDebug variables for dynamic computations:
; xxxxyyyy
; 10: register
;   xx: size (0 = 1, 1 = 2, 2 = 4)
;     yyyy: 0..15: register as stored in the register save area
;                  as SIL, DIL, BPL, SPL aren't supported these map to xH
;                  xSL, IPL and FLL are invalid, ExS are invalid
; 1011: variable
;     yyyy: which variable. variables are always dword-sized
; 11000000: 32-bit compound, next byte stores: xxxxyyyy first, second 16-bit reg
; 11000001..11111111: available for encoding other compound regs, vars, indirection,
;                     symbols, types etc
; 0xxxxxxx: operators


; Instruction set information needed for the 'p' command.
; ppbytes and ppinfo needs to be consecutive.
ppbytes:db 66h,67h,26h,2Eh,36h,3Eh,64h,65h,0F2h,0F3h	; prefixes
.string:
	db 0ACh,0ADh,0AAh,0ABh,0A4h,0A5h	; lods,stos,movs
	db 6Ch,6Dh,6Eh,6Fh			; ins,outs
	db 0A6h,0A7h,0AEh,0AFh			; cmps,scas
PPLEN_ONLY_STRING	equ $-ppbytes
.string_amount:		equ $ - .string
	db 0CCh,0CDh				; int instructions
	db 0E0h,0E1h,0E2h			; loop instructions
	db 0E8h					; call rel16/32
	db 09Ah					; call far seg16:16/32
;	(This last one is done explicitly by the code.)
;	db 0FFh					; FF/2 or FF/3: indirect call

PPLEN	equ	$-ppbytes	; size of the above table

;	Info for the above, respectively. This MUST follow
;	 immediately after ppbytes, as we add + PPLEN - 1 to
;	 di after repne scasb to index into this (ppinfo).
;	80h = prefix; 82h = operand size prefix; 81h = address size prefix.
;	If the high bit is not set, the next highest bit (40h) indicates
;	 that the instruction size depends on whether there is an operand
;	 size prefix; if set, under o32 two bytes are added to the size.
;	 (This is only used for direct near and far call.)
;	If both the two highest bits are clear, then PP_STRDEST,
;	 PP_STRSRC, or PP_STRSRC2 may be set. This only happens for
;	 string instructions, which always are neither prefixes nor
;	 use additional bytes.
;	The remaining bits tell the number of additional bytes in the
;	 instruction. This is at most 4. It must be below-or-equal to
;	 7, or if PP_VARSIZ is used, 5 (so the sum stays below 8).

PP_ADRSIZ	equ 01h
PP_OPSIZ	equ 02h
PP_PREFIX	equ 80h
PP_VARSIZ	equ 40h
PP_STRDEST	equ 20h
PP_STRSRC	equ 10h
PP_STRSRC2	equ 08h
PP_SIZ_MASK	equ 07h

ppinfo:	db PP_PREFIX | PP_OPSIZ, PP_PREFIX | PP_ADRSIZ
	times 8 db PP_PREFIX		; prefixes
	db PP_STRSRC, PP_STRSRC		; lods
	db PP_STRDEST, PP_STRDEST	; stos
	db PP_STRDEST | PP_STRSRC, PP_STRDEST | PP_STRSRC
					; movs
	db PP_STRDEST, PP_STRDEST	; ins
	db PP_STRSRC, PP_STRSRC		; outs
	db PP_STRSRC2 | PP_STRSRC, PP_STRSRC2 | PP_STRSRC
					; cmps
	db PP_STRSRC2, PP_STRSRC2	; scas
	db 0,1				; int
	db 1,1,1			; loop
	db PP_VARSIZ | 2		; call rel16/32 with displacement
	db PP_VARSIZ | 4		; call far 16:16 or 16:32 immediate

%if PPLEN != $-ppinfo
 %error "ppinfo table has wrong size"
%endif


;	Equates for instruction operands.
;	First the sizes.

OP_ALL		equ 40h		; byte/word/dword operand (could be 30h but ...)
OP_1632		equ 50h		; word or dword operand
OP_8		equ 60h		; byte operand
OP_16		equ 70h		; word operand
OP_32		equ 80h		; dword operand
OP_64		equ 90h		; qword operand
OP_1632_DEFAULT	equ 0A0h	; word or dword or default opsize

OP_SIZE	equ OP_ALL		; the lowest of these

;	These operand types need to be combined with a size.
;	Bits 0 to 3 give one of these types (maximum 15),
;	 and bits 4 to 7 specify the size. Table entries
;	 for these are identified by detecting that they
;	 are above-or-equal OP_SIZE.
;	The first parameter to the opsizeditem macro is the
;	 name of the item. It has to match the names used in
;	 the instr.key and debugtbl.inc files.
;	The second parameter is the entry for bittab that
;	 is used by aa.asm (the assembler).
;	The third parameter is the suffix used to create the
;	 entry for asmjmp (prefix aop_) and disjmp2 (dop_).

%macro opsizeditem 3.nolist
 %1 equ nextindex
 %xdefine BITTAB_OPSIZEDITEMS BITTAB_OPSIZEDITEMS,%2
 %xdefine ASMJMP_OPSIZEDITEMS ASMJMP_OPSIZEDITEMS,aop_%3
 %xdefine DISJMP2_OPSIZEDITEMS DISJMP2_OPSIZEDITEMS,dop_%3
 %assign nextindex nextindex + 1
%endmacro
%assign nextindex 0
%define BITTAB_OPSIZEDITEMS ""
%define ASMJMP_OPSIZEDITEMS ""
%define DISJMP2_OPSIZEDITEMS ""
opsizeditem OP_IMM,	ARG_IMMED,	imm	; immediate
opsizeditem OP_RM,ARG_DEREF+ARG_JUSTREG,rm	; reg/mem
opsizeditem OP_M,	ARG_DEREF,	m	; mem (but not reg)
opsizeditem OP_R_MOD,	ARG_JUSTREG,	r_mod	; register, determined from MOD R/M part
opsizeditem OP_MOFFS,	ARG_DEREF,	moffs	; memory offset; e.g., [1234]
opsizeditem OP_R,	ARG_JUSTREG,	r	; reg part of reg/mem byte
opsizeditem OP_R_ADD,	ARG_JUSTREG,	r_add	; register, determined from instruction byte
opsizeditem OP_AX,	ARG_JUSTREG,	ax	; al or ax or eax
%if nextindex > 16
 %error Too many op sized items
%endif

;	These don't need a size.
;	Because the size needs to be clear to indicate
;	 that one of these is to be used, the maximum
;	 value for these is 63 (as 64 is OP_SIZE).
;	The minimum value for these is 1 because a 0
;	 without size means the end of an op list (OP_END).
;	The first parameter to the opitem macro is the name
;	 of the item. It has to match the names used in the
;	 instr.key and debugtbl.inc files.
;	The second parameter is the entry for bittab that
;	 is used by aa.asm (the assembler). The third
;	 parameter is the entry for asmjmp.
;	The fourth parameter is the entry for optab as used
;	 by uu.asm (the disassembler).
;
;	asm_siznum contains entries for OP_M64 to OP_MXX.
;	 (The order has to match their opitem order.)
;	asm_regnum contains entries for OP_DX to OP_GS.
;	 (The order has to match their opitem order.)

%macro opitem 4.nolist
 %1 equ nextindex
 %xdefine BITTAB_OPITEMS BITTAB_OPITEMS,%2
 %xdefine ASMJMP_OPITEMS ASMJMP_OPITEMS,%3
 %xdefine OPTAB_OPITEMS OPTAB_OPITEMS,%4
 %assign nextindex nextindex + 1
%endmacro
	OP_END	equ 0
%assign nextindex 1
%define BITTAB_OPITEMS ""
%define ASMJMP_OPITEMS ""
%define OPTAB_OPITEMS ""
	; order of the following (ao17 entries) must match asm_siznum in aa.asm
OP_FIRST_ASM_SIZNUM	equ nextindex	; corresponding to asm_siznum start
opitem OP_M64,		ARG_DEREF,	ao17,dop_m64	; qword memory (obsolete?)
opitem OP_MFLOAT,	ARG_DEREF,	ao17,dop_mfloat	; float memory
opitem OP_MDOUBLE,	ARG_DEREF,	ao17,dop_mdouble; double-precision floating memory
opitem OP_M80,		ARG_DEREF,	ao17,dop_m80	; tbyte memory
opitem OP_MXX,		ARG_DEREF,	ao17,dop_mxx	; memory (size unknown)
opitem OP_FARIMM,	ARG_FARADDR,	ao21,dop_farimm	; far16/far32 immediate
opitem OP_REL8,		ARG_IMMED,	ao23,dop_rel8	; byte address relative to IP
opitem OP_REL1632,	ARG_IMMED,	ao25,dop_rel1632; word or dword address relative to IP
opitem OP_1CHK,		ARG_WEIRDREG,	ao29,dop49	; check for ST(1)
opitem OP_STI,		ARG_WEIRDREG,	aop_sti,dop_sti	; ST(I)
opitem OP_CR,		ARG_WEIRDREG,	aop_cr,dop_cr	; CRx
opitem OP_DR,		ARG_WEIRDREG,	ao34,dop_dr	; DRx
opitem OP_TR,		ARG_WEIRDREG,	ao35,dop_tr	; TRx
opitem OP_SEGREG,	ARG_WEIRDREG,	ao39,dop_segreg	; segment register
opitem OP_IMMS8,	ARG_IMMED,	ao41,dop_imms8 	; sign extended immediate byte
opitem OP_IMMS8_EXTEND,	ARG_IMMED,	ao41_extend,dop_imms8		; add etc word/dword r/m, imms8
opitem OP_IMM8,		ARG_IMMED,	ao42,dop_imm8	; immediate byte (other args may be (d)word)
opitem OP_IMM8_INT,	ARG_IMMED,	ao42,dop_imm8_int		; immediate byte for int
opitem OP_MMX,		ARG_WEIRDREG,	aop_mmx,dop_mmx	; MMx
opitem OP_MMX_MOD,	ARG_WEIRDREG,	aop_mmx_mod,dop_mmx_mod	; MMx, but in ModR/M part
opitem OP_SHOSIZ,	0FFh,	ao44,	dop_shosiz	; set flag to always show the size
opitem OP_SHORT,	0FFh,	ao_short,dop_short	; allow short keyword
opitem OP_NEAR,		0FFh,	ao_near,dop_near	; allow near keyword
opitem OP_FAR,		0FFh,	ao_far,	dop_far		; allow far keyword
opitem OP_FAR_REQUIRED,	0FFh,	ao_far_required,dop_far_required	; require far keyword
opitem OP_FAR_M,	0FFh,	ao_modifier_continue,dop_far_m		; les, lds, lss, lfs, lgs, or jmp/call far mem
opitem OP_DOUBLE_M,	0FFh,	ao_modifier_continue,dop_double_m	; bound
opitem OP_M_SRC,	0FFh,	ao_modifier_continue,dop_m_src
opitem OP_M_DST,	0FFh,	ao_modifier_continue,dop_m_dst
opitem OP_M_SRC_DST,	0FFh,	ao_modifier_continue,dop_m_src_dst
opitem OP_STACK_PUSH,	0FFh,	ac09_internal_error,dop_stack_push
opitem OP_STACK_POP,	0FFh,	ac09_internal_error,dop_stack_pop
opitem OP_STACK_SPECIAL,0FFh,	ac09_internal_error,dop_stack_special
opitem OP_E_CX,	ARG_JUSTREG,	aop_e_cx, da_internal_error		; (E)CX
OP_FIRST_STRING equ nextindex
opitem OP_1,	ARG_IMMED,	ao46, "1"	; 1 (simple "string" ops from here on)
opitem OP_3,	ARG_IMMED,	ao47, "3"	; 3
	; order of the following (ao48 entries) must match asm_regnum in aa.asm
OP_FIRST_ASM_REGNUM	equ nextindex	; corresponding to asm_regnum start
opitem OP_DX,	ARG_JUSTREG,	ao48, "DX"	; DX
opitem OP_CL,	ARG_JUSTREG,	ao48, "CL"	; CL
opitem OP_ST,	ARG_WEIRDREG,	ao48, "ST"	; ST (top of coprocessor stack)
opitem OP_CS,	ARG_WEIRDREG,	ao48, "CS"	; CS
opitem OP_DS,	ARG_WEIRDREG,	ao48, "DS"	; DS
opitem OP_ES,	ARG_WEIRDREG,	ao48, "ES"	; ES
opitem OP_FS,	ARG_WEIRDREG,	ao48, "FS"	; FS
opitem OP_GS,	ARG_WEIRDREG,	ao48, "GS"	; GS
opitem OP_SS,	ARG_WEIRDREG,	ao48, "SS"	; SS
OP_AFTER_LAST equ nextindex
%if nextindex > OP_SIZE
 %error Too many op items
%endif
OP_AMOUNT_TABLE	equ nextindex + 16 - 1
		; nextindex: amount sizeless types
		; 16: OP_SIZE combined types
		; -1: OP_END does not occur in tables

	; Instructions that have an implicit operand subject to a segment prefix.
	; This means a prefixed segment is allowed by the strict assembler, and
	; the disassembler treats a segment prefix as part of the instruction and
	; displays it in front of the instruction's mnemonic.
	; (outs, movs, cmps, lods, xlat).
segprfxtab:
	db 06Eh,06Fh,0A4h,0A5h,0A6h,0A7h,0ACh,0ADh
a32prfxtab:
	db 0D7h				; xlat, last in segprfxtab, first in a32prfxtab
SEGP_LEN equ $-segprfxtab

		; Instructions that can be used with REPE/REPNE.
		; (ins, outs, movs, stos, lods; cmps, scas)
replist:db 06Ch,06Eh,0A4h,0AAh,0ACh	; REP (no difference)
REP_SAME_LEN equ $-replist		; number of indifferent replist entries
	db 0A6h,0AEh			; REPE/REPNE
REP_LEN equ $-replist
REP_DIFF_LEN equ REP_LEN-REP_SAME_LEN	; number of replist entries with difference

A32P_LEN equ $-a32prfxtab

; prfxtab P_LEN REP_LEN REPE_REPNE_LEN

		; All the instructions in replist also have an implicit operand
		; subject to ASIZE (similar to segprfxtab). Additionally, the
		; xlat instruction (0D7h) has such an implicit operand too.
		; maskmovq too.


	%include "asmtabs.asm"


	usesection lDEBUG_DATA_ENTRY

msg_start:
	%include "msg.asm"

msg_end:

	numdef SHOWMSGSIZE, 0
%if _SHOWMSGSIZE
%assign MSGSIZE msg_end - msg_start
%warning msg holds MSGSIZE bytes
%endif


	usesection lDEBUG_DATA_ENTRY
		; INP:	word [cs:ip] = near address to jump to in other segment
..@symhint_trace_caller_entry_to_code_seg:
entry_to_code_seg:
	push ax			; word space for ?jumpaddress_ip, is ax
	mov ax, word [cs:code_seg]
%if _PM
	jmp entry_to_code_common

..@symhint_trace_caller_entry_to_code_sel:
entry_to_code_sel:
	push ax
	mov ax, word [cs:code_sel]
%endif

entry_to_code_common:
	lframe 0
	lpar word, jumpaddress_cs_and_orig_ip
	lpar word, jumpaddress_ip
	lenter

	push si
	pushf
	cld

	xchg word [bp + ?jumpaddress_cs_and_orig_ip], ax	; fill function segment
	mov si, ax
	cs lodsw
%if _DEBUG
	cmp al, 0CCh		; debugger breakpoint ?
	jne @F			; no -->
	int3			; break to make it remove the breakpoint
	dec si
	dec si
	cs lodsw		; reload the word
	cmp al, 0CCh
	jne @F

.l:
	int3
	jmp .l

@@:
%endif
	xchg word [bp + ?jumpaddress_ip], ax		; fill function offset
		; (and restore ax)

	popf
	pop si

	lleave
	retf			; jump to dword [bp + ?jumpaddress]



		; debug22 - Interrupt 22h handler
		;
		; This is for DEBUG itself: it's a catch-all for the various Int23
		; and Int24 calls that may occur unpredictably at any time. What we
		; do is pretend to be a command interpreter (which we are, in a sense,
		; just with different sort of commands) by setting our parent PSP
		; value equal to our own PSP so that DOS does not free our memory when
		; we quit. Therefore control ends up here when DOS detects Control-C
		; or an Abort in the critical error prompt is selected.
debug22:
	cli
.cleartraceflag:
	cld			; reestablish things
	mov ax, cs
	mov ds, ax
	mov ss, ax
	mov sp, word [ savesp ]	; restore stack
%if _PM
	clropt [internalflags], protectedmode	; reset PM flag
%endif
	times 1 - (($ - $$) & 1) nop	; align in-code parameter
	call entry_to_code_seg
	dw cmd2_reset_re_maybe_pm


	usesection lDEBUG_CODE

%if $ - $$
 %error cmd3 is not at offset 0 in lDEBUG_CODE
%endif

	code_insure_low_byte_not_0CCh
		; Begin main command loop.
cmd3:
	push ss
	pop ds
	_386_o32		; mov esp
	mov sp, word [ savesp ]		; restore stack
_386	and sp, ~3			; align stack
	_386_o32
	xor ax, ax
	_386_o32
	push ax
	_386_o32
	popf
_386	mov sp, word [ savesp ]		; restore stack
	cld
	sti
	mov word [ errret ], cmd3
	mov word [ throwret ], errhandler
	mov word [ throwsp ], sp
	push ds
	pop es

	xor di, di
	xchg di, word [terminator_in_line_in.offset]
	test di, di
	jz @F
	cmp byte [di], 0
	jne @F
	mov al, byte [terminator_in_line_in.value]
	stosb
@@:

	clropt [internalflags3], dif3_unquiet_error
	clropt [internalflags3], dif3_do_not_highlight
%if _INPUT_FILE_HANDLES || _INPUT_FILE_BOOT
	clropt [internalflags3], dif3_auxbuff_guarded_1 | dif3_in_if
%else
	clropt [internalflags3], dif3_auxbuff_guarded_1
%endif
	clropt [internalflags3], \
		dif3_input_serial_override | dif3_input_terminal_override
	clropt [internalflags2], dif2_in_silence_dump
%if _PM
	call resetmode
%endif


%if _PM
cmd3_int2F_init:
	mov al, 2Fh		; interrupt number
	mov si, debug2F		; -> IISP entry header

	testopt [options4], opt4_int_2F_hook
	jnz .done
.check_disable:
	testopt [internalflags4], dif4_int_2F_hooked
	jz .done

	mov dx, opt4_int_2F_force >> 16
	call UnhookInterruptForce
	jc .done

	clropt [internalflags], hooked2F
	clropt [internalflags4], dif4_int_2F_hooked
	call update_inttab_optional

.done:
%endif


%if _CATCHINT08
cmd3_int08_init:
	mov al, 08h		; interrupt number
	mov si, intr8		; -> IISP entry header

	testopt [options4], opt4_int_08_hook
	jz .check_disable
.check_enable:
	testopt [internalflags4], dif4_int_08_hooked
	jnz .done

	call install_86m_interrupt_handler
	setopt [internalflags4], dif4_int_08_hooked
	call update_inttab_optional
	jmp .done

.check_disable:
	testopt [internalflags4], dif4_int_08_hooked
	jz .done

	mov dx, opt4_int_08_force >> 16
	call UnhookInterruptForce
	jc .done

	clropt [internalflags4], dif4_int_08_hooked
	call update_inttab_optional

.done:
%endif


%if _CATCHINT2D
cmd3_int2D_init:
	mov al, 2Dh		; interrupt number
	mov si, int2D		; -> IISP entry header

	testopt [options4], opt4_int_2D_hook
	jz .check_disable
.check_enable:
	testopt [internalflags4], dif4_int_2D_hooked
	jnz .done

	call intchk			; ZR if offset = -1 or segment = 0
					; CHG: ax, dx, bx
	jz .fail

	mov ah, byte [try_amis_multiplex_number]
	mov al, 00h
		; function 0 changes dx, di, cx, al
%if _PM
	call call_int2D
%else
	int 2Dh				; enquire whether there's anyone
%endif
	test al, al
	jz .got

	xor ax, ax			; start with multiplex number 0
.loopplex:
	mov al, 00h			; AMIS installation check
		; function 0 changes dx, di, cx, al
%if _PM
	call call_int2D
%else
	int 2Dh				; enquire whether there's anyone
%endif
	test al, al			; free ?
	jz .got				; yes, put it to use -->
	inc ah
	jnz .loopplex			; try next multiplexer -->

	mov dx, msg.cannot_hook_2D.nofree
	jmp .fail_putsz

.got:
	mov byte [amis_multiplex_number], ah

	mov al, 2Dh		; interrupt number
	call install_86m_interrupt_handler
	setopt [internalflags4], dif4_int_2D_hooked
	jmp .done

.fail:
	mov dx, msg.cannot_hook_2D.invalid
.fail_putsz:
	call putsz
	clropt [options4], opt4_int_2D_hook
	jmp .done

.check_disable:
	testopt [internalflags4], dif4_int_2D_hooked
	jz .done

	mov dx, opt4_int_2D_force >> 16
	call UnhookInterruptForce
	jc .done

	clropt [internalflags4], dif4_int_2D_hooked

.done:
%endif


	testopt [internalflags3], dif3_input_re
	jnz cmd3_continue_1_re
	clropt [options2], opt2_re_cancel_tpg
	call silence_dump


cmd3_serial_init:
	testopt [options], enable_serial
	jz .check_disable_serial
.check_enable_serial:
	testopt [serial_flags], sf_init_done
	jnz .done_serial
.enable_serial:

	mov al, byte [serial_var_intnum]
	mov byte [serial_use_intnum], al
	mov al, byte [serial_var_params]
	mov byte [serial_use_params], al
	mov al, byte [serial_var_fifo]
	mov byte [serial_use_fifo], al
	mov ax, word [serial_var_baseport]
	mov word [serial_use_baseport], ax
	mov ax, word [serial_var_dl]
	mov word [serial_use_dl], ax
	mov ax, word [serial_var_irqmask]
	mov word [serial_use_irqmask], ax
  call  serial_clear_fifos
  call  serial_install_interrupt_handler
	jnc @F
	mov di, msg.serial_cannot_hook.old_int
	mov al, byte [serial_installed_intnum]
	call hexbyte
	mov di, msg.serial_cannot_hook.new_int
	mov al, byte [serial_use_intnum]
	call hexbyte
	mov dx, msg.serial_cannot_hook
	jmp .no_keep

@@:
	mov byte [serial_interrupt_handler + ieEOI], 80h
  call  serial_init_UART

	setopt [serial_flags], sf_init_done

	mov dx, msg.serial_request_keep
	call putsz

	mov di, line_out
%if _DEBUG
	mov al, '~'		; indicate instance is to be debugged
	stosb
%endif
	mov al, '='
	stosb
	mov al, 32
	stosb

	xor ax, ax
	mov word [getline_timer_count], ax
	mov word [getline_timer_last], ax
	mov word [getline_timer_func], .timer

		; if we're executing from the command line
		;  buffer or a Y file then we want to
		;  override input to be from serial for the
		;  KEEP confirmation prompt.
		; output is always to serial if we're here.
	setopt [internalflags3], dif3_input_serial_override
	call getline00
	clropt [internalflags3], dif3_input_serial_override

	call skipcomm0
	dec si
	mov dx, msg.keep
	call isstring?
	je .done_serial

	mov dx, msg.serial_no_keep_enter
.no_keep:
	clropt [options], enable_serial
	call putsz
	jmp cmd3


.timer:
	push ax
	push dx
	push cx
	push es

	mov dx, 40h
	mov es, dx

	mov cx, word [getline_timer_count]
	mov dx, word [getline_timer_last]

	cmp dx, word [es:6Ch]
	je .timer_next
	mov dx, word [es:6Ch]
	inc cx
	mov al, 18
	mul byte [serial_keep_timeout]
	test ax, ax
	jz .timer_next
	cmp cx, ax
	jb .timer_next

	pop es
	mov dx, msg.serial_no_keep_timer
	jmp .no_keep

.timer_next:
	mov word [getline_timer_count], cx
	mov word [getline_timer_last], dx
	pop es
	pop cx
	pop dx
	pop ax
	retn


.check_disable_serial:
		; If serial is initialised, uninstall it.
	testopt [serial_flags], sf_init_done
	jnz .disable_serial
		; Not initialised. Is the interrupt still hooked?
	testopt [internalflags4], dif4_int_serial_hooked
	jz .done_serial
		; Try unhooking the interrupt handler.
	call serial_uninstall_interrupt_handler
	jc .done_serial			; if it failed again -->
	mov di, msg.serial_late_unhook.int
	mov al, byte [serial_installed_intnum]
	call hexbyte
	mov dx, msg.serial_late_unhook
	call putsz
	jmp .done_serial

.disable_serial:

  call serial_clean_up
	jnc @F
	mov di, msg.serial_cannot_unhook.int
	mov al, byte [serial_installed_intnum]
	call hexbyte
	mov dx, msg.serial_cannot_unhook
	call putsz
	mov byte [serial_interrupt_handler + ieEOI], 0
					; we do not issue EOI any longer
@@:
	clropt [serial_flags], sf_init_done
.done_serial:

	call ensuredebuggeeloaded	; if no task is active, create a dummy one

cmd3_continue_1_re:
	mov di, line_out	; build prompt
%if _DEBUG
	mov al, '~'		; indicate instance is to be debugged
	stosb
%endif
%if _INDOS_PROMPT
	call InDos
	jz @F
 %if _BOOTLDR
	testopt [internalflags], nodosloaded
				; boot mode ?
  %if _INDOS_PROMPT_NOBOOT
	jnz @F			; yes, do not show special prompt -->
  %elif _INDOS_PROMPT_NOFLAG
	jnz .indos_prompt	; yes, show special prompt -->
				;  (do not call .real_indos check)
  %endif
 %endif
 %if _INDOS_PROMPT_NOFLAG
	call InDos.real_indos	; real InDOS set ?
	jz @F			; no, do not show special prompt -->
 %endif
.indos_prompt:
	mov al, '!'
	stosb
@@:
%endif
	mov al, '-'		; main prompt
%if _PM
	call ispm
	jnz .realmode
	mov al, '#'		; PM main prompt
.realmode:
%endif
	testopt [internalflags3], dif3_input_re
	jz @F
	mov al, '%'
@@:
	stosb

	mov byte [hhflag], 0
	and word [hh_depth], 0
	and word [hh_depth_of_single_term], 0
	mov word [getline_timer_func], dmycmd
	clropt [internalflags], usecharcounter	; reset this automatically

	testopt [internalflags3], dif3_input_re
	jnz cmd3_continue_2_re

	setopt [internalflags], pagedcommand	; 2009-02-21: default to page all commands
	clropt [internalflags], tt_silence | tt_silent_mode
				; reset, in case it's still set
	clropt [internalflags2], dif2_tpg_proceed_bp_set | \
			dif2_bp_failure | dif2_tpg_keep_proceed_bp, 1
%if _INPUT_FILE_HANDLES
	clropt [internalflags2], dif2_closed_input_file
%endif

cmd3_continue_2_re:
	call determine_quiet_output

	xor cx, cx
	xchg cx, word [rc]	; reset rc
	mov word [priorrc], cx	; make prior value available
	jcxz @F
	mov word [erc], cx	; update to last non-zero value
@@:

	testopt [internalflags3], dif3_input_re
	jz cmd3_continue_not_re

	add word [re_count], 1
	adc word [re_count + 2], 0
	mov dx, word [re_limit + 2]
	mov ax, word [re_limit]
	cmp word [re_count + 2], dx
	jne @F
	cmp word [re_count], ax
@@:
	jbe cmd3_continue_not_re

	mov dx, msg.re_limit_reached
	call putsz_error
	mov ax, 0104h
	call setrc
	setopt [internalflags3], dif3_at_line_end
	jmp getline_close_file

cmd3_continue_not_re:

cmd3_check_line_out_overflow:
	cmp word [line_out_overflow], 2642h
	je @F
	mov word [line_out_overflow], 2642h
	mov dx, msg.line_out_overflow
	call putsz_error
@@:

cmd3_getline:
	call getline00		; prompted input, also resets linecounter

	call iseol?.notsemicolon
	jne cmd3_notblank
	testopt [options3], opt3_disable_autorepeat
	jnz @F
	mov dx, word [lastcmd]
	mov byte [si], al
	jmp short cmd4

@@:
	mov word [lastcmd], dmycmd
	jmp cmd3

cmd3_notblank:
	mov word [lastcmd], dmycmd
	cmp al, ';'
	je cmd3_j1		; if comment -->
	cmp al, ':'
	je cmd3_j1		; if jump label -->
	cmp al, '?'
	je help			; if request for help -->
	call uppercase
	sub al, 'A'
	cmp al, 'Y'-'A'
	ja error		; if not recognized
	cbw
	xchg bx, ax
	call skipcomma
	shl bx, 1
	mov dx, word [ cmdlist+bx ]
cmd4:
	mov di, line_out
	call dx
cmd3_j1:
	jmp cmd3		; back to the top


	code_insure_low_byte_not_0CCh
cmd2_reset_re_maybe_pm:

	_386_o32		; mov esp
	mov sp, word [ savesp ]		; restore stack
_386	and sp, ~3			; align stack
	_386_o32
	xor ax, ax
	_386_o32
	push ax
	_386_o32
	popf
_386	mov sp, word [ savesp ]		; restore stack
	cld
	sti

%if _PM
	call handle_mode_changed
%endif

	code_insure_low_byte_not_0CCh
cmd2_reset_re:
	mov bx, word [io_levels]
.entry_bx_levels:
	xor cx, cx
.entry_bx_levels_cx_cmdline:
	xor dx, dx
%if _INPUT_FILE_HANDLES
	testopt [internalflags2], dif2_input_file
	jz @F
	add cx, word [input_file_handles.active]
	inc cx
@@:
%endif
%if _INPUT_FILE_BOOT
	testopt [internalflags2], dif2_input_file_boot
	jz @F
	add cx, word [load_input_file.active]
	inc cx
@@:
%endif
	testopt [internalflags3], dif3_input_re
	jz @F
	inc cx
	inc dx
	testopt [io_flags], iof_extra_iol_for_tpg_re
	jz @F
	inc bx
	jnz @F
	dec bx
@@:
	cmp cx, bx
	jbe @F
	mov cx, bx
@@:
	jcxz cmd3_j1
	push ds
	pop es
@@:
	push cx
	push dx
	call getline_close_file.resetstuff
	pop dx
	pop cx
	loop @B
	test dx, dx
	jz cmd3_j1
	setopt [options2], opt2_re_cancel_tpg
	jmp dumpregs_extended.exit


dmycmd:
	retn

help:
	call skipcomma
	call uppercase
%if _EXTHELP
 %if _COND
	mov dx, msg.condhelp
	cmp al, 'C'
	je .spec
 %endif
 %if _OPTIONS
	mov dx, msg.ophelp
	cmp al, 'O'
	je .spec		; option help -->
 %endif
 %if _EXPRESSIONS
	mov dx, msg.expressionhelp
	cmp al, 'E'
	je .spec
 %endif
%endif
%if _EMS
	mov dx, msg.xhelp
	cmp al, 'X'
	je .spec
%endif
	dec si
 %if _BOOTLDR && _EXTHELP
	mov dx, msg.boot
	call isstring?
	mov dx, msg.boothelp
	je .spec
 %endif
%if _EXTHELP
	mov dx, msg.source
	call isstring?
	mov dx, msg.help_source
	je .spec
%endif
	mov dx, msg.re
	call isstring?
	mov dx, msg.help_re
	je .spec
	mov dx, msg.run
	call isstring?
	mov dx, msg.help_run
	je .spec
	mov dx, msg.string_build
	call isstring?
	mov bx, msg.build_array
	mov cx, msg.build_short_amount
	je .spec_multi
	lodsb
	call uppercase
	mov cx, msg.build_long_amount
	cmp al, 'B'
	je .spec_multi		; build info -->
%if _EXTHELP
	mov dx, msg.license
	cmp al, 'L'
	je .spec		; licence -->
	mov dx, msg.flaghelp
	cmp al, 'F'
	je .spec		; flag help -->
	mov dx, msg.reghelp
	cmp al, 'R'
	je .spec		; register help -->
 %if _VARIABLES || _OPTIONS || _PSPVARIABLES
	mov dx, msg.varhelp
	cmp al, 'V'
	je .spec		; variable help -->
 %endif
%endif
	mov dx, msg.help	; default help
	db __TEST_IMM8		; (skip lodsb)
.spec:
	lodsb
	call chkeol
prnquit:
	call putsz		; print string
cmd3_j1a:
	jmp cmd3_j1		; done

errorj1:jmp error

help.spec_multi:
	lodsb
	call chkeol
.loop:
	mov dx, word [bx]
	call putsz
	inc bx
	inc bx
	loop .loop
	jmp short cmd3_j1a


determine_quiet_output:
	clropt [internalflags3], dif3_quiet_output

	push di
	push ax
	testopt [internalflags3], dif3_input_re
	jnz .notquiet

%if _INPUT_FILE_BOOT
	testopt [internalflags2], dif2_input_file_boot
	jz @F
	mov ax, LOAD_INPUT_FILE_SIZE
	push dx
	mul word [load_input_file.active]
	pop dx
	mov di, ax
	testopt [load_input_file + di - LOADDATA3 + ldFATType], ifhfQuietOutput
	jmp .quiet_if_nz

@@:
%endif
%if _INPUT_FILE_HANDLES
	call InDos
	jnz @F

	testopt [internalflags2], dif2_input_file
	jz @F
	mov di, word [input_file_handles.active]
	shl di, 1
	shl di, 1
	shl di, 1		; to qword array index
 %if INPUTFILEHANDLE_size != 8
  %error Unexpected structure size
 %endif
	testopt [input_file_handles + di + ifhFlags], ifhfQuietOutput
	jmp .quiet_if_nz

@@:
%endif
	testopt [internalflags3], dif3_input_cmdline
	jz @F
	testopt [options], opt_cmdline_quiet_output
	; jmp .quiet_if_nz

.quiet_if_nz:
	jz @F
.quiet:
	setopt [internalflags3], dif3_quiet_output
.notquiet:
@@:
	pop ax
	pop di
	retn


guard_auxbuff:
	testopt [internalflags3], dif3_auxbuff_guarded_1 | dif3_auxbuff_guarded_2
	jnz @F
	setopt [internalflags3], dif3_auxbuff_guarded_1
	retn

@@:
	mov ax, 0101h
	call setrc
	mov dx, msg.guard_auxbuff_error
.putsz_error:
	call putsz
	jmp cmd3


		; This is used to disallow commands
		;  while reading from the RE buffer.
guard_re:
	testopt [internalflags3], dif3_input_re
	jnz @F
	retn

@@:
	mov ax, 0102h
	call setrc
	mov dx, msg.guard_re_error
	jmp guard_auxbuff.putsz_error


	usesection lDEBUG_DATA_ENTRY

	align 2, db 0
debuggerfunction:	dw 0

%if ! _CATCHINT2D
	align 2, db 0
debuggeramissig:
.ven:	fill 8,32,db "ecm"		; vendor
.prod:	fill 8,32,db "lDebug"		; product
	db 0, 0

try_debugger_amis_multiplex_number:
	db -1
%else
%if 0

Supported Int2D functions:

AMIS - Installation check
INP:	al = 00h
OUT:	al = 0FFh
	cx = Private version number (currently 0100h)
	dx:di-> signature: "ecm     ", "lDebug  "

AMIS - Get private entry point - NOP: no private entry point
INP:	al = 01h
OUT:	al = 00h

AMIS - Uninstall - NOP: can't uninstall
INP:	al = 02h
OUT:	al = 00h (not implemented)

AMIS - Request pop-up - NOP: no pop-up
INP:	al = 03h
OUT:	al = 00h

AMIS - Determine chained interrupts
INP:	al = 04h
OUT:	al = 04h
	dx:bx -> interrupt hook list (Int2D always.)

AMIS - Get hotkeys - NOP: no hotkeys
INP:	al = 05h
OUT:	al = 00h

AMIS - Get device driver information - NOP: no device
INP:	al = 06h
OUT:	al = 00h

AMIS - Reserved for AMIS
INP:	al = 07h..0Fh
OUT:	al = 00h

TSR - Reserved for TSR
INP:	al = 10h..2Fh
OUT:	al = 00h

lDebug - Update IISP Header
INP:	al = 30h
	ds:si -> source IISP header (or pseudo header)
	es:di -> destination IISP header
OUT:	al = FFh to indicate suppported,
	 si and di both incremented by 6
	 destination's ieNext field updated from source
	al != FFh if not supported,
	 si and di unchanged
CHG:	-
REM:	This function is intended to aid in debugging
	 handler re-ordering, removal, or insertion.
	 The 32-bit far pointer needs to be updated
	 as atomically as possible to avoid using
	 an incorrect pointer.
	Test case: Run a program such as our TSRs'
	 uninstaller or SHUFHOOK and step through it
	 with "tp fffff" when operating on something
	 crucial such as interrupt 21h. Without this
	 function the machine will crash!
	To enable this function to be called, enter
	 the command "r dco4 or= 8" first (install our
	 AMIS multiplexer handler).
	Other workaround: Use SILENT for TP and disable
	 DCO3 flag 4000_0000 (do not call int 21.0B to
	 check for Ctrl-C status).
	Yet another workaround: Set flag DCO 8 (enable
	 fake InDOS mode, avoid calling int 21h).
REM:	The source may be a pseudo IISP header. In this
	 case the ieEntry field should hold 0FEEBh
	 (jmp short $) and the ieSignature field
	 should indicate the source, eg "VT" for the IVT
	 or "NH" for inserting a New Handler.

TSR - Reserved for TSR
INP:	al = 31h..FFh
OUT:	al = 00h

%endif

	align 2, db 0
debuggeramissig:
amissig:
.ven:	fill 8,32,db "ecm"		; vendor
.prod:	fill 8,32,db "lDebug"		; product
.desc:	asciz _PROGNAME,_VERSION,", debugger."
					; description
%if $ - .desc > 64
 %error AMIS description too long
%endif

try_amis_multiplex_number:
	db 0
try_debugger_amis_multiplex_number:
	db -1


iispentry int2D
	cmp ah, 0
amis_multiplex_number equ $-1		; AMIS multiplex number (data for cmp opcode)
	je .handle			; our multiplex number -->
	jmp far [cs:.next]		; else go to next handler -->

.handle:
	test al, al
	jz .installationcheck		; installation check -->
	; cmp al, 02h
	; je .uninstall			; uninstallation -->
	cmp al, 04h
	je .determineinterrupts		; determine hooked interrupts -->
	cmp al, 30h
	je .updateiispheader
				; all other functions are reserved or not supported by TSR
.uninstall:
.nop:
	mov al, 0			; show not implemented
	iret

.installationcheck:
	dec al				; (= FFh) show we're here
	mov cx, 0100h			; = version
	mov di, amissig			; dx:di -> AMIS signature strings of this program
.iret_dx_cs:
	mov dx, cs
.iret:
	iret

.determineinterrupts:			; al = 04h, always returns list
	mov bx, word [cs:amisintr_offset]
					; dx:bx -> hooked interrupts list
	jmp short .iret_dx_cs

.updateiispheader:
	mov al, 0FFh			; show supported
	cld
	cli				; try to rest while updating chain
	cmpsw				; skip over first word (entrypoint)
					;  (generally xxEBh or 0EA90h)
	movsw
	movsw				; transfer source ieNext to dest ieNext
	iret
%endif


	; doscall is used by symbols.asm and run.asm, so define it prior
%if _PM && _NOEXTENDER
%macro doscall 0
	call _doscall
%endmacro
%else
		; When we don't support non-extended DPMI all Int21 calls
		; are either in Real Mode or extended (all are real Int21
		; instructions).
%macro doscall 0
	int 21h
%endmacro
%endif


	usesection lDEBUG_CODE

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


%include "aa.asm"
%include "dd.asm"
%include "rr.asm"
%if _RN
%include "fptostr.asm"
%endif
%include "run.asm"
%include "uu.asm"


%if _PM
%include "pmdata.asm"
%include "pminit.asm"
%include "pmentry.asm"


	usesection lDEBUG_CODE

resetmode_and_testattrhigh:
%if _PM
	call resetmode
%endif

		; Test if bx is a 32-bit selector (as opposed to a 16-bit selector or a segment)
		;
		; INP:	bx = selector (PM) or segment (RM)
		; OUT:	NZ = 32-bit
		;	ZR = 16-bit
		;	NC
testattrhigh:
_386	call ispm
_386	jz .pm				; 386 and PM, check selector -->
		; not PM or no 386
	cmp al, al			; ZR, NC
	retn
.pm:
[cpu 386]
	push eax
	lar eax, ebx			; access rights
	test eax, 400000h		; test bit (NC)
	pop eax
	retn
__CPU__

subcpureset	; subcpu used in pminit.asm
%endif	; _PM

%if _NOEXTENDER
		; When we support non-extended DPMI, some calls to Int21
		; are (extended) Int21 calls and some are (not extended)
		; calls down to the real mode Int21. doscall is a macro
		; that will always call the non-extended Int21.

		; Execute a non-extended DOS call
_doscall:
	pushf
	call ispm
	jnz .rm
subcpu 286
		; Execute a non-extended DOS call from PM
	popf
.pm:
	push word [ss:pspdbg]
	push 21h
	call intcall
	retn
subcpureset
.rm:
	popf
	jmp _int21
%endif

%if _PM
	; For branches other than symbolic, here's selector_to_segment
	;  (as used by the puts in expr.asm). Picked from symsnip
	;  binsrch.asm at revision 9c232415d568.
		; INP:	word [ss:sp] = selector to access
		; OUT:	word [ss:sp] = segment value to use for access
		; CHG:	-
selector_to_segment:
	lframe near
	lpar word,	in_selector_out_segment
	lpar_return
	lenter

	call ispm		; is it PM ?
	jnz .ret		; no, 86M --> (selector == segment)

subcpu 286
	push ax
	push bx
	push cx
	push dx

	mov bx, word [bp + ?in_selector_out_segment]
	mov ax, 6
	int 31h			; get segment base to cx:dx
	shr dx, 4
	shl cx, 12
	or dx, cx
	mov word [bp + ?in_selector_out_segment], dx

	pop dx
	pop cx
	pop bx
	pop ax
subcpureset

.ret:
	lleave
	lret
%endif


	; support functions for symbols.asm


		; INP:	ds:dx -> message
		;	cx = length
		; CHG:	-
		; STT:	ds, es don't care
disp_message_length_cx:
	push ax
	push bx
	push cx
	push dx
	push es
	push ds
	 push ds
	 pop es			; es:dx -> message, cx = length
	 push ss
	 pop ds			; ds = ss (required for puts)
	call puts
	pop ds
	pop es
	pop dx
	pop cx
	pop bx
	pop ax
	retn

		; INP:	ds:dx -> message, ASCIZ
		; CHG:	-
		; STT:	ds, es don't care
disp_message:
	push es
	 push ds
	 pop es			; es:dx -> message
	call putsz		; (sets up ds = ss internally)
	pop es
	retn

		; INP:	al = character to display
		; CHG:	-
		; STT:	ds, es don't care
disp_al: equ putc


		; Display number in ax hexadecimal, always 4 digits
		;
		; INP:	ax = number
		; OUT:	displayed using disp_al
		; CHG:	none
disp_ax_hex:
	xchg al, ah
	call disp_al_hex
	xchg al, ah
disp_al_hex:
	push cx
	mov cl, 4
	rol al, cl
	call disp_al_nybble_hex
	rol al, cl
	pop cx
disp_al_nybble_hex:
	push ax
	and al, 0Fh
	add al, '0'
	cmp al, '9'
	jbe @F
	add al, -'9' -1 +'A'
@@:
	call disp_al
	pop ax
	retn


		; Display number in ax decimal
		;
		; INP:	ax = number
		; OUT:	displayed using disp_al
		; CHG:	none
disp_ax_dec:			; ax (no leading zeros)
		push bx
		xor bx, bx
.pushax:
		push dx
		push ax
		or bl, bl
		jz .nobl
		sub bl, 5
		neg bl
.nobl:
		push cx
		mov cx, 10000
		call .divide_out
		mov cx, 1000
		call .divide_out
		mov cx, 100
		call .divide_out
		mov cl, 10
		call .divide_out
							; (Divisor 1 is useless)
		add al, '0'
		call disp_al
		pop cx
		pop ax
		pop dx
		pop bx					; Caller's register
		retn


		; INP:	ax = number
		;	cx = divisor
		; OUT:	ax = remainder of operation
		;	result displayed
.divide_out:
		push dx
		xor dx, dx
		div cx				; 0:ax / cx
		push dx				; remainder
		dec bl
		jnz .nobl2
		or bh, 1
.nobl2:
		or bh, al
		jz .leadingzero
		add al, '0'
		call disp_al			; display result
 .leadingzero:
		pop ax				; remainder
		pop dx
		retn


		; C command - compare bytes.
cc:
	call parsecm		; parse arguments

		; To make the 16-bit 64 KiB compare hack below work, the
		; full ecx mustn't be increased here for 16-bit segments.
		; The passed ecx isn't higher than FFFFh for 16-bit segments,
		; and a value of 0001_0000h needs to be passed as zero to
		; the hack anyway.
%if _PM
	cmp byte [ss:bAddr32], 0
	je .16
[cpu 386]
	inc ecx
	jnz cc1
	jmp error
__CPU__
.16:
%endif
	inc cx
cc1:
	push ds
	push es
	push ss
	pop ds			; ds := cs
	call dohack		; do the interrupt pointer hack
	pop es
	pop ds
%if _PM
	cmp byte [ss:bAddr32], 0
	jz .cmp16
	a32 repe cmpsb
	mov dl, byte [esi-1]
	mov dh, byte [es:edi-1]
	jmp short .cmpdone
.cmp16:
%endif
		; The following 3 instructions make a hack to support 64 KiB
		; compare. The only time we get here with cx = 0 is the first
		; iteration for a 64 KiB compare. In that case, dec cx results
		; in FFFFh making repe cmpsb work. The single cmpsb will either
		; jump the repe cmpsb (if it found a mismatch) or not jump it.
		; The repe cmpsb might be executed with cx = 0, but will then
		; not change anything including the flags so it works.
	dec cx
	cmpsb
	jne .skip
	repe cmpsb		; start comparing
.skip:
	mov dl, byte [si-1]	; save the possibly errant characters
	mov dh, byte [es:di-1]
.cmpdone:
	lahf
	push ds
	push es
	push ss
	pop ds
	call unhack		; undo the interrupt pointer hack
	pop es
	pop ds
	sahf
	je cc2			; if we're done
	push es
	push ss
	pop es
	_386_PM_o32	; mov ebx, edi
	mov bx, di
	mov	di, line_out
	mov	ax, ds
	call hexword
	mov	al, ':'
	stosb
	_386_PM_o32	; mov eax, esi
	mov ax, si
	_386_PM_o32	; dec eax
	dec ax
%if _PM
	cmp byte [ss:bAddr32], 0
	jz .16si
	call hexword_high
.16si:
%endif
	call hexword
	mov ax, 32<<8|32
	stosw
	mov al, dl
	call hexbyte
	mov ax, 32<<8|32
	stosw
	mov al, dh
	call hexbyte
	mov ax, 32<<8|32
	stosw
	pop	ax
	push	ax
	call hexword
	mov al, ':'
	stosb
	_386_PM_o32	; mov eax, ebx
	mov ax, bx
	_386_PM_o32	; dec eax
	dec ax
%if _PM
	cmp byte [ss:bAddr32], 0
	jz .16bx
	call hexword_high
.16bx:
%endif
	call hexword
	push ds
	push ss
	pop ds
	push bx
	push cx
	call putsline_crlf
	pop cx
	pop	di
	pop	ds
	pop	es
%if _PM
	cmp byte [ss:bAddr32],0
	jz cc1_6
[cpu 386]
	jecxz cc2
__CPU__
 cc1_j1:
	jmp cc1
cc1_6:
%else
 cc1_j1 equ cc1
%endif
%if 0
	_386_PM_o32	; inc ecx
	inc cx
	_386_PM_a32	; loopd cc1
	loop cc1		; if not done yet
%else
	_386_PM_a32	; jecxz cc2
	jcxz cc2
	jmp cc1			; if not done yet
%endif
cc2:
	push ss			; restore segment registers
	pop ds
	push ss
	pop es
	retn


%if _BOOTLDR
 %include "boot.asm"
%endif


	usesection lDEBUG_CODE

bb:
%if _BOOTLDR
	testopt [internalflags], nodosloaded
	jz @F
	dec si
	dec si			; -> at 'B'
	mov dx, msg.boot
	call isstring?		; check for "BOOT"
	je bootcmd
	inc si			; skip 'B'
	lodsb			; load next
@@:
%endif
	call uppercase
%if _BREAKPOINTS
	cmp al, 'P'
	je point_set
	cmp al, 'N'
	je point_number
	cmp al, 'C'
	je point_clear
	cmp al, 'D'
	je point_disable
	cmp al, 'E'
	je point_enable
	cmp al, 'T'
	je point_toggle
	cmp al, 'L'
	je point_list
	cmp al, 'I'
	je point_id
	cmp al, 'W'
	je point_when
 %if BPSIZE == 6 || BPSIZE == 9
	cmp al, 'O'
	je point_offset
 %endif
%endif
	cmp al, 'U'		; BU command ?
	je bu_breakpoint

	jmp error		; no valid subcommand -->

%if _BREAKPOINTS
point_set:
	call skipwhite

	call getpointat		; "AT" keyword ?
	jc .not_at		; no -->
	mov di, .get_saved	; access saved address later
	call findpointat	; do we find it ?
	jc .find_new		; no, treat as if "NEW" keyword given -->
				; point index is in dx
	push dx
	jmp @FF			; skip check whether used

.not_at:
	mov di, .get_addr	; get address from input command line later
	call getpointindex
	jnc @F			; got an index -->
	jz error		; "ALL" is invalid
				; got "NEW" keyword
.find_new:
	xor cx, cx
	push ax
.new_loop:
	mov ax, cx		; try this index
	call calcpointbit
%if ((_NUM_B_BP+7)>>3) != 1
	test byte [b_bplist.used_mask+bx], ah
%else
	test byte [b_bplist.used_mask], ah
%endif
	jz .new_found		; found unused one -->

	inc cx			; = next index
	cmp cx, _NUM_B_BP	; valid ?
	jb .new_loop		; yes, try next -->

	mov dx, msg.bb_no_new
	jmp prnquit


		; INP:	al=, si-> input line
		; OUT:	al=, si-> after
		;	bx:dx = linear adddress
		;	does not return if error
		; CHG:	edxh
.get_addr:
	mov bx, word [reg_cs]
	call getlinearaddr
	jc error
	retn


		; INP:	al=, si-> input line
		; OUT:	bx:dx = linear address
		; CHG:	-
.get_saved:
	mov dx, word [..@bb_saved_linear]
	mov bx, word [..@bb_saved_linear + 2]
	retn

	usesection lDEBUG_DATA_ENTRY
	align 4, db 0
..@bb_saved_linear:
	dd 0
	usesection lDEBUG_CODE


.new_found:
	pop ax
	push cx
	jmp @FF

@@:
	push dx
	push ax
	mov ax, dx
	call calcpointbit
%if ((_NUM_B_BP+7)>>3) != 1
	test byte [b_bplist.used_mask+bx], ah
%else
	test byte [b_bplist.used_mask], ah
%endif
	pop ax
	jnz error
@@:
	call di			; call either .get_addr or .get_saved
				; in any case, bx:dx = linear address
	mov di, 8000h		; default counter
	push dx
	xor dx, dx

	usesection lDEBUG_DATA_ENTRY
	align 4, db 0
..@bb_id_start:
	dw 0
..@bb_id_length:
	dw 0
..@bb_when_start:
	dw 0
..@bb_when_length:
	dw 0
	usesection lDEBUG_CODE


	mov word [..@bb_id_length], dx
	mov word [..@bb_when_length], dx
				; initialise to empty ID
	push dx
.loop_additional:
	call skipwh0
	dec si
	mov dx, msg.number
	call isstring?
	je .additional_number
	mov dx, msg.counter
	call isstring?
	je .additional_number
	mov dx, msg.id
	call isstring?
	je .additional_id
	mov dx, msg.when
	call isstring?
	je .additional_when
%if BPSIZE == 6 || BPSIZE == 9
	mov dx, msg.offset
	call isstring?
	je .additional_offset
%endif
	lodsb
	call iseol?
	je .no_additional
	pop dx
	test dx, dx		; already got a number without keyword?
	jnz error		; yes -->
	inc dx			; remember for subsequent iterations
	push dx
	call skipwh0
	jmp @F

.additional_number:
	call skipequals
	call iseol?
	je error
@@:
	call getcounter
	jmp .loop_additional

%if BPSIZE == 6 || BPSIZE == 9
.additional_offset:
	pop dx
	or dl, 1		; remember for subsequent iterations
				;  not to accept number without keyword
	push dx
	call skipequals
%if _PM
	push bx
	call getdword
	mov word [bp_offset], dx
	mov word [bp_offset + 2], bx
	pop bx
%else
	call getword
	mov word [bp_offset], dx
%endif
	jmp .loop_additional
%endif

.additional_when:
	pop dx
	or dl, 1		; remember for subsequent iterations
				;  not to accept number without keyword
	push dx
	call skipequals
	dec si
	call get_when
	jmp .loop_additional

.additional_id:
	call skipequals
	dec si
	call get_id

.no_additional:
	pop dx			; discard non-keyword NUMBER indicator
	pop dx			; restore dx = low word of linear

	xchg bx, dx		; dx:bx = linear
	xchg bx, ax		; dx:ax = linear
	pop bx			; = 0-based point index to set
	push dx
	push ax			; on stack: dword linear

	push di
	xchg dx, bx		; dx = point index

		; As for set_id, set_when will free a prior condition
		;  when writing the new one. However, we check for the
		;  appropriate buffer size being still free before
		;  calling set_when because we want to cancel the point
		;  initialisation if either the ID or the condition do
		;  not fit, without having yet written anything.
	call check_when_space	; CHG ax, bx, cx, si, di

		; Note that point_clear and init both leave the
		;  empty word in the ID array. Therefore we can
		;  always handle this by freeing the prior value
		;  first, which is required if we're resetting
		;  an existing point with BP AT.
		; The set_id function takes care of this.
	call set_id		; CHG ax, bx, cx, si, di

		; After check_when_space then set_id both returned,
		;  we have finally checked all error conditions and
		;  are now actually modifying things.
	call set_when		; CHG ax, bx, cx, si, di
	xchg ax, dx		; ax = point index
	pop di			; preserve counter value

	mov bx, ax
	add bx, bx
	add bx, bx
%if BPSIZE == 4
%elif BPSIZE == 5
	add bx, ax
%elif BPSIZE == 6
	add bx, ax		; * 5
	add bx, ax		; * 6
%elif BPSIZE == 9
	add bx, bx		; * 8
	add bx, ax		; * 9
%else
 %error Unexpected breakpoint size
%endif
	pop word [ b_bplist.bp + bx ]
		; These two instructions need to stay in that order.
		; For the non-PM version, the pop overwrites the byte
		; that is then initialized to 0CCh (the breakpoint
		; content byte).
	pop word [ b_bplist.bp + bx + 2 ]
	mov byte [ b_bplist.bp + bx + BPSIZE - 1 ], 0CCh
%if BPSIZE == 6
	push word [bp_offset]
	pop word [ b_bplist.bp + bx + 3 ]
%elif BPSIZE == 9
	push word [bp_offset]
	pop word [ b_bplist.bp + bx + 4 ]
	push word [bp_offset + 2]
	pop word [ b_bplist.bp + bx + 6 ]
%endif
	mov bx, ax
	add bx, bx
	mov word [ b_bplist.counter + bx ], di
	call calcpointbit
%if ((_NUM_B_BP+7)>>3) != 1
	or byte [b_bplist.used_mask+bx], ah
	not ah
	and byte [b_bplist.disabled_mask+bx], ah
 %if _BREAKPOINTS_STICKY
	and byte [b_bplist.sticky_mask+bx], ah
 %endif
%else
	or byte [b_bplist.used_mask], ah
	not ah
	and byte [b_bplist.disabled_mask], ah
 %if _BREAKPOINTS_STICKY
	and byte [b_bplist.sticky_mask], ah
 %endif
%endif
	retn


		; INP:	si -> first non-blank character
		; OUT:	..@bb_id_start and ..@bb_id_length set
		;	does not return if error (too long)
		; CHG:	ax, cx, si
get_id:
	mov word [..@bb_id_start], si
@@:
	lodsb
	call iseol?.notsemicolon
	jne @B
		; si -> after EOL char
		; si - 1 -> EOL char
@@:
	dec si
	cmp si, word [..@bb_id_start]
	je @F
	cmp byte [si - 1], 32
	je @B
	cmp byte [si - 1], 9
	je @B
@@:
	mov cx, si
	sub cx, word [..@bb_id_start]
	mov word [..@bb_id_length], cx
	cmp cx, 63
	ja error

%if 0
	push dx
	mov dx, msg.id
	call putsz
	mov al, 32
	call putc
	mov al, '"'
	call putc
	mov dx, word [..@bb_id_start]
	mov cx, word [..@bb_id_length]
	call disp_message_length_cx
	mov al, '"'
	call putc
	mov dx, crlf
	call putsz
	pop dx
%endif
	retn


		; INP:	si -> first non-blank character
		; OUT:	..@bb_when_start and ..@bb_when_length set
		;	does not return if error
		;	al = character after the condition, si -> next
		; CHG:	ax, cx, si
get_when:
	mov word [..@bb_when_start], si
@@:
	lodsb
	push dx
	push bx
	call getexpression
	pop bx
	pop dx
		; si -> after condition char
		; si - 1 -> last condition char
@@:
	dec si
	cmp si, word [..@bb_when_start]
	je @F
	cmp byte [si - 1], 32
	je @B
	cmp byte [si - 1], 9
	je @B
@@:
	mov cx, si
	sub cx, word [..@bb_when_start]
	mov word [..@bb_when_length], cx

	lodsb
%if 0
	push dx
	mov dx, msg.when
	call putsz
	mov al, 32
	call putc
	mov al, '"'
	call putc
	mov dx, word [..@bb_when_start]
	mov cx, word [..@bb_when_length]
	call disp_message_length_cx
	mov al, '"'
	call putc
	mov dx, crlf
	call putsz
	dec si
	lodsb
	pop dx
%endif
	retn


		; INP:	al = first character, si -> next character
		;	di = default value
		; OUT:	di = counter value (default if EOL)
		;	al = first character after number, si -> next
		; CHG:	-
		;	does not return if error encountered
getcounter:
.:
	call skipwh0
	call iseol?
	je .got_counter
	push dx
	call getword
	mov di, dx
	pop dx
.got_counter:
	retn


		; INP:	al = first character, si -> next character
		; OUT:	di = counter value (defaults to 8000h)
		; CHG:	ax, si (flags not changed)
		;	does not return if error encountered
.pushf_chkeol:
	pushf
	push dx
	dec si
	mov dx, msg.number
	call isstring?
	je @F
	mov dx, msg.counter
	call isstring?
	jne @FF
@@:
	call skipequals
	db __TEST_IMM8		; (skip lodsb)
@@:
	lodsb
	pop dx
	call .
	call chkeol
	popf
	retn


point_number:
	call skipwhite

	call getpointat		; "AT" keyword ?
	jc .not_at		; no -->
	call findpointat	; do we find it ?
	jc error		; not found -->
	mov di, 8000h		; default counter
	call getcounter.pushf_chkeol
	jmp @F			; point index is in dx -->

.not_at:
	call getpointindex
	mov di, 8000h		; default counter
	call getcounter.pushf_chkeol
	jnc @F
	jnz error		; "NEW" is invalid -->

	xor cx, cx
.all_loop:
	mov ax, cx
	mov dx, cx
	call calcpointbit
%if ((_NUM_B_BP+7)>>3) != 1
	test byte [b_bplist.used_mask+bx], ah
%else
	test byte [b_bplist.used_mask], ah
%endif
	jz .all_next
	call .setnumber
.all_next:
	inc cx
	cmp cx, _NUM_B_BP
	jb .all_loop
	retn

@@:
	mov ax, dx
	call calcpointbit
%if ((_NUM_B_BP+7)>>3) != 1
	test byte [b_bplist.used_mask+bx], ah
%else
	test byte [b_bplist.used_mask], ah
%endif
	jz error

.setnumber:
	mov bx, dx
	add bx, bx
	mov word [b_bplist.counter + bx], di
	retn


point_id:
	call skipwhite

	call getpointat		; "AT" keyword ?
	jc .not_at		; no -->
	call findpointat	; do we find it ?
	jc error		; not found -->
	jmp @F			; point index is in dx -->

.not_at:
	call getpointindex
	jc error		; "NEW" and "ALL" keywords are invalid -->

@@:
	push ax
	mov ax, dx
	call calcpointbit
%if ((_NUM_B_BP+7)>>3) != 1
	test byte [b_bplist.used_mask+bx], ah
%else
	test byte [b_bplist.used_mask], ah
%endif
	jz error
	pop ax

	call skipwh0

	dec si
	push dx
	mov dx, msg.id
	call isstring?
	jne .no_id_kw
	call skipequals
	dec si
.no_id_kw:
	pop dx
	call get_id

		; INP:	..@bb_id_start
		;	..@bb_id_length
		;	dx = point index
		; OUT:	jumps to error if failure (too long)
		; CHG:	ax, bx, cx, si, di
set_id:
	mov bx, -1
	call get_set_id_offset_length
	mov cl, 10
	mov ax, bx
	shr bx, cl		; bx = length of ID to free

	mov di, word [b_bplist.idbuffer.free]
	mov si, di		; offset free
	neg si			; - offset free
	add si, b_bplist.idbuffer.length
				; 1024 - offset free = amount free
	add si, bx		; amount free + length of ID to free
	mov cx, word [..@bb_id_length]
	jcxz .empty		; if no ID to set -->
	cmp si, cx		; enough free ?
	mov si, word [..@bb_id_start]
				; -> ID string
	jb error		; no -->

	push cx
	call free_id		; actually free it now
	pop cx
	push cx
	mov bx, cx		; length
	mov cl, 10		; offset part is 10 bits (0..1023)
	shl bx, cl		; length is in top 6 bits (0..63)
	pop cx
	mov di, word [b_bplist.idbuffer.free]
				; = offset of free part
	add word [b_bplist.idbuffer.free], cx
				; mark space as used
	or bx, di		; OR in the offset
	add di, b_bplist.idbuffer
				; -> into buffer space
	rep movsb		; write

				; now remember this
.after_empty:
		; INP:	dx = 0-based point index
		;	bx = word to set (-1 if not to modify)
		; OUT:	bx = word read
get_set_id_offset_length: equ $
	xchg dx, bx
	shl bx, 1
	push word [b_bplist.id + bx]
	cmp dx, -1
	je @F
	mov word [b_bplist.id + bx], dx
@@:
	pop dx
	shr bx, 1
	xchg dx, bx
	retn

.empty:
	call free_id
	xor bx, bx		; offset = 0 and length = 0
	jmp .after_empty


		; INP:	ax = offset/length word of ID to free
		;	b_bplist.id = ID array (ONE of which matches ax)
		; CHG:	ax, bx, cx
		; OUT:	b_bplist.id entries adjusted
		;	 (the one that is being freed is unaffected)
		;	b_bplist.idbuffer adjusted
		; STT:	UP
free_id:
	lframe
	lenter
	lvar word,	freeingword
	 push ax

	mov cl, 10
	mov bx, ax
	and bx, 1023		; bx = offset of ID to free
	shr ax, cl		; ax = length of ID to free
	xchg cx, ax		; cx = length of ID to free

	lvar word,	freeinglength
	 push cx
	; lvar word,	freeingoffset
	;  push bx
	push si
	push di

	jcxz .return

	 push cx
	lea di, [b_bplist.idbuffer + bx]
				; -> ID to be freed
				;  (destination of following data)
	mov si, di
	add si, cx		; -> behind ID to be freed
				;  (source of following data)
	mov cx, si
	neg cx			; minus pointer to first subsequent data
	add cx, b_bplist.idbuffer + b_bplist.idbuffer.length
				; pointer behind buffer - pointer subsequent data
				;  = length of data to move
	rep movsb		; now di -> first uninitialised byte
	 pop cx			; = length of data freed

	sub word [b_bplist.idbuffer.free], cx
				; mark as free

	xor al, al
	rep stosb		; clear the buffer trailer (uninitialised part)

	mov si, b_bplist.id
%if _NUM_B_BP < 256
	mov cl, _NUM_B_BP
%else
	mov cx, _NUM_B_BP
%endif
.loop:
	lodsw
	cmp ax, word [bp + ?freeingword]
				; word we're freeing ?
	je .next
	mov di, ax
	and ax, 1023		; get offset
	cmp ax, bx		; is it above the word we're freeing ?
	jbe .next		; no -->
	sub ax, word [bp + ?freeinglength]
	and di, ~ 1023		; get length only (in high 6 bits)
	or ax, di		; get new combined word
	mov word [si - 2], ax	; adjust offset
.next:
	loop .loop

.return:
	pop di
	pop si
	lleave
	retn


%if BPSIZE == 6 || BPSIZE == 9
point_offset:
	call skipwhite

	call getpointat		; "AT" keyword ?
	jc .not_at		; no -->
	call findpointat	; do we find it ?
	jc error		; not found -->
	jmp @F			; point index is in dx -->

.not_at:
	call getpointindex
	jc error		; "NEW" and "ALL" keywords are invalid -->

@@:
	push ax
	mov ax, dx
	call calcpointbit
%if ((_NUM_B_BP+7)>>3) != 1
	test byte [b_bplist.used_mask+bx], ah
%else
	test byte [b_bplist.used_mask], ah
%endif
	jz error
	pop ax

	call skipwh0
	push dx
	mov dx, -1
%if BPSIZE == 9
	mov bx, dx
%endif
	call iseol?
	je @F
	dec si
	mov dx, msg.offset
	call isstring?
	jne .no_offset_kw
	call skipequals
	dec si
.no_offset_kw:
	lodsb
%if BPSIZE == 9
	call getdword		; bx:dx = offset
%else
	call getword		; dx = offset
%endif
	call chkeol
@@:
	pop ax
	mov di, ax
	add di, di
	add di, di		; * 4
%if BPSIZE == 6
	add di, ax		; * 5
	add di, ax		; * 6
	add di, b_bplist.bp + 3
%elif BPSIZE == 9
	add di, di		; * 8
	add di, ax		; * 9
	add di, b_bplist.bp + 4
%else
 %error Unexpected breakpoint size
%endif
	xchg ax, dx
	stosw			; store low word of offset
%if BPSIZE == 9
	xchg ax, bx
	stosw			; store high word of offset
%endif
	retn
%endif


point_when:
	call skipwhite

	call getpointat		; "AT" keyword ?
	jc .not_at		; no -->
	call findpointat	; do we find it ?
	jc error		; not found -->
	jmp @F			; point index is in dx -->

.not_at:
	call getpointindex
	jc error		; "NEW" and "ALL" keywords are invalid -->

@@:
	push ax
	mov ax, dx
	call calcpointbit
%if ((_NUM_B_BP+7)>>3) != 1
	test byte [b_bplist.used_mask+bx], ah
%else
	test byte [b_bplist.used_mask], ah
%endif
	jz error
	pop ax

	call skipwh0
	and word [..@bb_when_length], 0
	call iseol?
	je @F
	dec si
	push dx
	mov dx, msg.when
	call isstring?
	jne .no_when_kw
	call skipequals
	dec si
.no_when_kw:
	pop dx
	call get_when
	call chkeol
@@:


		; INP:	..@bb_when_start
		;	..@bb_when_length
		;	dx = point index
		; OUT:	jumps to error if failure (too long)
		; CHG:	ax, bx, cx, si, di
set_when:
	call check_when_space
	jcxz .empty

	push cx
	call free_when		; actually free it now
	pop cx
	mov di, word [b_bplist.whenbuffer.free]
				; = offset of free part
	add word [b_bplist.whenbuffer.free], cx
				; mark space as used
	add di, b_bplist.whenbuffer
				; -> into buffer space
	mov bx, di
	rep movsb		; write
	mov byte [di - 1], 0

				; now remember this
.after_empty:
		; INP:	dx = 0-based point index
		;	bx = word to set (-1 if not to modify)
		; OUT:	bx = word read
get_set_when_offset: equ $
	xchg dx, bx
	shl bx, 1
	push word [b_bplist.when + bx]
	cmp dx, -1
	je @F
	mov word [b_bplist.when + bx], dx
@@:
	pop dx
	shr bx, 1
	xchg dx, bx
	retn

.empty:
	call free_when
	xor bx, bx		; offset = 0
	jmp .after_empty


		; INP:	..@bb_when_start
		;	..@bb_when_length
		;	dx = point index
		; OUT:	jumps to error if failure (too long)
		; CHG:	ax, bx, cx, si, di
check_when_space:
	mov bx, -1
	call get_set_when_offset

	mov ax, bx
	test bx, bx
	jz @F
	push ax
	mov di, bx
	mov cx, -1
	mov al, 0
	repne scasb
	not cx			; = length to free (including terminating NUL)
	mov bx, cx
	pop ax
@@:				; bx = length to free

	mov di, word [b_bplist.whenbuffer.free]
	mov si, di		; offset free
	neg si			; - offset free
	add si, b_bplist.whenbuffer.length
				; 1024 - offset free = amount free
	add si, bx		; amount free + length of condition to free
	mov cx, word [..@bb_when_length]
	jcxz .empty		; if no condition to set -->
	inc cx			; count terminating NUL
	cmp si, cx		; enough free ?
	mov si, word [..@bb_when_start]
				; -> condition string
	jb error		; no -->
.empty:
	retn


		; INP:	ax = offset word of condition to free
		;	b_bplist.when = condition array (ONE of which matches ax)
		; CHG:	ax, bx, cx
		; OUT:	b_bplist.when entries adjusted
		;	 (the one that is being freed is unaffected)
		;	b_bplist.whenbuffer adjusted
		; STT:	UP
free_when:
	lframe
	lenter
	push si
	push di
	push dx

	test ax, ax
	jz .return

	mov di, ax
	mov bx, ax
				; -> condition to be freed
				;  (destination of following data)
	push di
	mov cx, -1
	mov al, 0
	repne scasb
	not cx			; = length to free (including terminating NUL)
	pop di
	 push cx
	mov si, di
	add si, cx		; -> behind condition to be freed
				;  (source of following data)
	mov cx, si
	neg cx			; minus pointer to first subsequent data
	add cx, b_bplist.whenbuffer + b_bplist.whenbuffer.length
				; pointer behind buffer - pointer subsequent data
				;  = length of data to move
	rep movsb		; now di -> first uninitialised byte
	 pop cx			; = length of data freed

	mov dx, cx
	sub word [b_bplist.whenbuffer.free], cx
				; mark as free

	xor al, al
	rep stosb		; clear the buffer trailer (uninitialised part)

	mov si, b_bplist.when
%if _NUM_B_BP < 256
	mov cl, _NUM_B_BP
%else
	mov cx, _NUM_B_BP
%endif
.loop:
	lodsw
	cmp ax, bx		; offset we're freeing ?, OR
				;  is it above the offset we're freeing ?
	jbe .next		; no --> (also jumps if ax == 0)
	sub word [si - 2], dx	; adjust offset
.next:
	loop .loop

.return:
	pop dx
	pop di
	pop si
	lleave
	retn


point_clear:
	mov di, .clear
	jmp point_clear_enable_disable_toggle_common

.clear:
	not ax
%if ((_NUM_B_BP+7)>>3) != 1
	and byte [b_bplist.used_mask+bx], ah
%else
	and byte [b_bplist.used_mask], ah
%endif
	and byte [b_bplist.disabled_mask + bx], ah
	push cx

	xor bx, bx		; replace by empty word
	call get_set_id_offset_length
	xchg ax, bx		; ax = word what to free
	call free_id		; actually free it now

	xor bx, bx		; replace by empty word
	call get_set_when_offset
	xchg ax, bx		; ax = word what to free
	call free_when		; actually free it now

	pop cx
	retn

point_clear_enable_disable_toggle_common:
	call skipwhite

	call getpointat		; "AT" keyword ?
	jc .not_at		; no -->
	call findpointat	; do we find it ?
	jc error		; not found -->
	jmp @F			; point index is in dx -->

.not_at:
	call getpointindex
	jnc @F			; point index is in dx, no keyword -->
	jnz error		; "NEW" is invalid -->

	call chkeol
	xor cx, cx
.all_loop:
	mov ax, cx
	call calcpointbit
%if ((_NUM_B_BP+7)>>3) != 1
	test byte [b_bplist.used_mask+bx], ah
%else
	test byte [b_bplist.used_mask], ah
%endif
	jz .all_next
	mov dx, cx
	call di
.all_next:
	inc cx
	cmp cx, _NUM_B_BP
	jb .all_loop
	retn

@@:
	call chkeol
	mov ax, dx
	call calcpointbit
%if ((_NUM_B_BP+7)>>3) != 1
	test byte [b_bplist.used_mask+bx], ah
%else
	test byte [b_bplist.used_mask], ah
%endif
	jz error
	call di
	retn


point_enable:
	mov di, .enable
	jmp point_clear_enable_disable_toggle_common

.enable:
	not ax
%if ((_NUM_B_BP+7)>>3) != 1
	and byte [b_bplist.disabled_mask+bx], ah
%else
	and byte [b_bplist.disabled_mask], ah
%endif
	retn


point_disable:
	mov di, .disable
	jmp point_clear_enable_disable_toggle_common

.disable:
%if ((_NUM_B_BP+7)>>3) != 1
	or byte [b_bplist.disabled_mask+bx], ah
%else
	or byte [b_bplist.disabled_mask], ah
%endif
	retn


point_toggle:
	mov di, .toggle
	jmp point_clear_enable_disable_toggle_common

.toggle:
%if ((_NUM_B_BP+7)>>3) != 1
	xor byte [b_bplist.disabled_mask+bx], ah
%else
	xor byte [b_bplist.disabled_mask], ah
%endif
	retn


point_list:
	call skipwhite
	call iseol?
	je .all

	call getpointat		; "AT" keyword ?
	jc .not_at		; no -->
	call findpointat	; do we find it ?
		; Here we ignore the point index in dx, we just
		;  take note that at least one point matching the
		;  specified address exists. The points are matched
		;  against the linear address in ..@bb_saved_linear.
	jnc .all_matching

	mov dx, msg.bpnone_at
	call putsz
	retn


.all_matching:
	xor bp, bp
	xor bx, bx
	xor dx, dx
	mov di, line_out
.loop_matching:
	mov si, bx
	add si, si
	add si, si
%if BPSIZE == 4
%elif BPSIZE == 5
	add si, bx
%elif BPSIZE == 6
	add si, bx		; * 5
	add si, bx		; * 6
%elif BPSIZE == 9
	add si, si		; * 8
	add si, bx		; * 9
%else
 %error Unexpected breakpoint size
%endif
	add si, b_bplist.bp
	lodsw
	cmp word [..@bb_saved_linear], ax
	jne .next_matching
%if _PM
	lodsw
%else
	xor ax, ax
	lodsb
%endif
	cmp word [..@bb_saved_linear + 2], ax
	jne .next_matching

	push di
	mov al, 32
	mov cx, 40
	rep stosb		; initialize field with blanks
	xor al, al
	stosb			; terminate it
	pop di

	call .single		; fill buffer

	push dx
	push bx
%if 0
	test dl, 1		; an odd point ?
	jnz .odd_matching	; yes -->
	mov di, line_out + 40	; write next point after the field
	jmp .was_even_matching
.odd_matching:
%endif
	call putsline_crlf	; put line with linebreak (and no excess blanks)
	call handle_bl_when
	mov di, line_out	; write next point at start of field
.was_even_matching:
	pop bx
	pop dx
	inc dx			; increment odd/even counter
.next_matching:
	inc bx			; increment breakpoint index
	cmp bx, _NUM_B_BP
	jne .loop_matching
	jmp .end


.not_at:
	call getpointindex
	jnc @F
	jnz error		; "NEW" is invalid -->

	call chkeol
	jmp .all
@@:
	call chkeol
	mov bx, dx
	mov di, line_out
	call .single
	call putsline_crlf
	jmp handle_bl_when


.all:
	xor bp, bp		; high byte: any set points encountered yet,
				; low byte: current line has any set points
	xor bx, bx
	mov di, line_out
.loop:
	push di
	mov al, 32
	mov cx, 40
	rep stosb		; initialize field with blanks
	xor al, al
	stosb			; terminate it
	pop di

	call .single		; fill buffer

	push bx
%if 0
	test bl, 1		; an odd point ?
	jnz .odd		; yes -->
	mov di, line_out + 40	; write next point after the field
	jmp .was_even
.odd:
%endif
	test bp, 00FFh		; any point set in this line ?
	jz .skip_putsline	; no -->
	call putsline_crlf	; put line with linebreak (and no excess blanks)
	call handle_bl_when

	and bp, ~00FFh		; clear flag for next line processing
.skip_putsline:
	mov di, line_out	; write next point at start of field
.was_even:
	pop bx
	inc bx
	cmp bx, _NUM_B_BP
	jne .loop
.end:
	cmp di, line_out
	je @F
	call putsline_crlf
	call handle_bl_when
@@:
	test bp, 0FF00h
	jnz @F
	mov dx, msg.bpnone
	call putsz
@@:
	retn

.single:
	mov si, msg.bp
	call showstring
	push bx
	mov ax, bx
	call hexbyte		; store index of this point
	call calcpointbit
	mov si, msg.bpunused
%if ((_NUM_B_BP+7)>>3) != 1
	test byte [b_bplist.used_mask+bx], ah
%else
	test byte [b_bplist.used_mask], ah
%endif
	jnz @F			; if set -->
	call showstring
	xor si, si
	jmp .unused

@@:
	or bp, 0101h		; flag that there was a point set in this line
	mov si, msg.bpdisabled
%if ((_NUM_B_BP+7)>>3) != 1
	test byte [b_bplist.disabled_mask+bx], ah
%else
	test byte [b_bplist.disabled_mask], ah
%endif
	jnz .disabled		; disabled --> (D)
	mov si, msg.bpenabled
.disabled:
	call showstring
	mov si, msg.bpaddress
	call showstring
	 pop ax
	 push ax
	mov si, ax
	add si, si
	add si, si
%if BPSIZE == 4
%elif BPSIZE == 5
	add si, ax
%elif BPSIZE == 6
	add si, ax		; * 5
	add si, ax		; * 6
%elif BPSIZE == 9
	add si, si		; * 8
	add si, ax		; * 9
%else
 %error Unexpected breakpoint size
%endif
	add si, b_bplist.bp	; -> point
	push dx
	lodsw
	xchg ax, dx
%if _PM
	lodsw
	call hexword
%else
	xor ax, ax
	lodsb
	call hexbyte
%endif
	 push ax
	mov al, '_'
	stosb
	 pop ax
	xchg ax, dx
	call hexword		; display (linear) address
%if BPSIZE == 6 || BPSIZE == 9
		; INP:	dx:ax = linear address
		;	si -> (d)word offset
		;	di -> where to store
		; OUT:	cx = length displayed
		;	si -> after offset
		;	di -> after stored string
		; CHG:	ax, dx
	call bp_display_offset	; BPSIZE implied
%else
	xor cx, cx
%endif
	pop dx
	lodsb
	push ax
	mov si, msg.bpcontent
	call showstring
	pop ax
	call hexbyte		; display content
	mov si, msg.bpcounter
	call showstring
	 pop ax
	 push ax
	mov bx, ax
	push dx
	mov dx, ax
	add bx, bx
	mov ax, word [b_bplist.counter + bx]
	call hexword

	mov bx, -1
	call get_set_id_offset_length
	test bh, 63 << 2	; length nonzero ?
	jz @F			; no -->

		; The maximum length of a short ID is based on
		;  how much space there is assuming 80 columns.
	mov si, msg.bb_hitpass_id.short
	shl cl, 1
	shl cl, 1
	neg cl
	add cl, 35 << 2
	cmp bh, cl		; long ?
	jb .trigger_short_id
		; This jump MUST be a jb, not jbe. The jbe
		;  would not match ZR for words where the
		;  idbuffer offset is a nonzero value.
	mov si, msg.bb_hitpass_id.long
.trigger_short_id:

	call copy_single_counted_string
	mov cl, bh
	shr cl, 1
	shr cl, 1		; cx = length
	and bx, 1023		; bx = offset
	lea si, [b_bplist.idbuffer + bx]
	rep movsb

@@:

	mov bx, -1
	call get_set_when_offset
	mov si, bx
	pop dx

.unused:
	pop bx			; restore counter (if displaying all)
	retn


		; CHG:	si, al
handle_bl_when:
	xchg dx, si
	test dx, dx
	jz @F
	push dx
	mov dx, msg.bb_when
	call putsz
	pop dx
	call putsz
%if 0
	mov al, '$'
	call putc
%endif
	mov dx, crlf
	call putsz
@@:
	xchg dx, si
	retn


		; INP:	ax = 0-based index of point
		; OUT:	(bx-> byte to access. only if at least 9 points)
		;	(bx = 0 always if 8 or fewer points)
		;	ah = value to access
		; CHG:	al
calcpointbit:
%if ((_NUM_B_BP+7)>>3) != 1
	mov bx, ax
%endif
	and al, 7
	mov ah, 1
	xchg ax, cx
	shl ch, cl
%if ((_NUM_B_BP+7)>>3) != 1
	mov cl, 3
	shr bx, cl
%else
	xor bx, bx
%endif
	xchg ax, cx
	retn


		; INP:	bx:dx = linear address
		; OUT:	NC if point found,
		;	 dx = point index
		;	CY if point not found,
		;	 bx:dx unchanged
		; CHG:	di
findpointat:
	lframe near
	lenter
	lvar word,	orig_ax
	 push ax
	lvar word,	orig_si
	 push si
	lvar dword,	orig_bxdx
	 push bx
	 push dx
	xor dx, dx
.loop:
	mov ax, dx
	call calcpointbit
%if ((_NUM_B_BP+7)>>3) != 1
	test byte [b_bplist.used_mask + bx], ah
%else
	test byte [b_bplist.used_mask], ah
%endif
	jz .next

	mov si, dx
	add si, si
	add si, si
%if BPSIZE == 4
%elif BPSIZE == 5
	add si, dx
%elif BPSIZE == 6
	add si, dx		; * 5
	add si, dx		; * 6
%elif BPSIZE == 9
	add si, si		; * 8
	add si, dx		; * 9
%else
 %error Unexpected breakpoint size
%endif
	add si, b_bplist.bp	; -> point
	lodsw
	cmp word [bp + ?orig_bxdx], ax
	jne .next
%if _PM
	lodsw
%else
	xor ax, ax
	lodsb
%endif
	cmp word [bp + ?orig_bxdx + 2], ax
	jne .next
				; (NC)
	mov word [bp + ?orig_bxdx], dx
	jmp .ret

.next:
	inc dx
	cmp dx, _NUM_B_BP
	jb .loop

	stc
.ret:
	pop dx
	pop bx			; pop ?orig_bxdx
	pop si			; pop ?orig_si
	pop ax			; pop ?orig_ax
	lleave
	lret


		; INP:	si->, al=
		; OUT:	CY if no "AT" keyword + address,
		;	 si, al unchanged
		;	NC if "AT" keyword + address,
		;	 si->, al= after
		;	 bx:dx = dword [..@bb_saved_linear] = linear address
		; CHG:	edx, bx
getpointat:
	dec si
	mov dx, msg.at
	call isstring?
	lodsb
	je .at
	stc
	retn

.at:
	mov bx, word [reg_cs]
	call getlinearaddr
	jc error
	mov word [..@bb_saved_linear], dx
	mov word [..@bb_saved_linear + 2], bx
	retn


		; INP:	si->, al=
		; OUT:	NC if a point is specified,
		;	 dx = point index (0-based, below _NUM_B_BP)
		;	CY if a keyword is specified,
		;	 ZR if "ALL" keyword specified
		;	 NZ if "NEW" keyword specified
getpointindex:
	dec si
	mov dx, msg.all
	call isstring?
	je .is_all		; (ZR)
	mov dx, msg.new
	call isstring?
	je .is_new
	lodsb
	call getword
	cmp dx, _NUM_B_BP
	jae error
	clc			; (NC)
	retn

.is_new:
	test si, si		; (NZ)
.is_all:
	stc			; (CY)
	lodsb			; al = separator, si-> after
	retn
%endif


%if BPSIZE == 6 || BPSIZE == 9
		; INP:	dx:ax = linear address
		;	si -> (d)word offset
		;	di -> where to store
		; OUT:	cx = length displayed
		;	si -> after offset
		;	di -> after stored string
		; CHG:	ax, dx
bp_display_offset:
	lframe
	lvar dword,	offset
	lenter
	lvar dword,	linear
	 push dx
	 push ax
	mov ax, "  "
	lvar word,	prefix
	 push ax
	lvar word,	start_write
	 push di
	push bx
	lodsw
%if _PM
	xchg ax, dx
	lodsw
	cmp ax, -1
	xchg ax, dx
	jne @F
%else
	xor dx, dx
%endif
	cmp ax, -1
	je .skip
@@:
	mov word [bp + ?offset + 2], dx
	mov word [bp + ?offset], ax

	mov dx, word [bp + ?linear + 2]
	mov ax, word [bp + ?linear]
	sub ax, word [bp + ?offset]
	sbb dx, word [bp + ?offset + 2]

%if _PM
	call ispm
	jnz .r86m

	push dx
	push ax
	mov ax, 6
	mov bx, word [reg_cs]
	int 31h
	pop ax
	pop bx
	jc .try_r86m

	cmp cx, bx
	jne .try_r86m
	cmp dx, ax
	jne .try_r86m

	mov ax, "  "
	stosw
	mov ax, "CS"
	stosw
	jmp .offset

.try_r86m:
	mov byte [bp + ?prefix + 1], '$'
%endif

.r86m:
	mov cx, 4
	test al, 15
	jnz .questionmarks
	test dx, 0FFF0h
	jnz .questionmarks
	shr ax, cl
	ror dx, cl
	or dx, ax
	mov ax, word [bp + ?prefix]
	stosw
	xchg ax, dx
	call hexword

.offset:
	mov al, ':'
	stosb
%if _PM
	mov ax, word [bp + ?offset + 2]
	test ax, ax
	jz @F
	call hexword
@@:
%endif
	mov ax, word [bp + ?offset]
	call hexword

.skip:
	pop bx
	pop cx			; get ?start_write
	neg cx
	add cx, di
	lleave
	retn

.questionmarks:
	mov ax, "  "
	stosw
	mov ax, "??"
	stosw
	stosw
	jmp .offset
%endif


bu_breakpoint:
	lodsb
	call chkeol
%if _DEBUG
	mov dx, msg.bu
	call putsz
	int3
	retn
%else
	mov dx, msg.notbu
	jmp putsz
%endif


uppercase:
	cmp al, 'a'
	jb .ret
	cmp al, 'z'
	ja .ret
	and al, TOUPPER
.ret:
	retn


errorj4:
	jmp error


%if 0

ee 0:
	push ss
	pop ds
	push ss
	pop es
	mov ax, word [ savesp ]
	inc ax
	inc ax
	mov sp, ax		; restore stack
	mov bx, word [e_addr + saSegSel]
	_386_PM_o32
	mov dx, word [ e_addr ]	; get back address


		; Prompt mode.
ee 1:
	mov word [ errret ], ee 0

		; Begin loop over lines.
ee 2:				; <--- next line
	mov word [e_addr + saSegSel], bx
%if _PM
	call ispm
	jnz .86m
.pm:
	mov word [e_addr + saSelector], bx
	jmp @F
.86m:
	mov word [e_addr + saSegment], bx
@@:
%endif
	_386_PM_o32
	mov word [ e_addr ], dx	; save address
	mov di, line_out
	mov ax, bx		; print out segment and offset
	call hexword

===


	mov al, '.'
	stosb
	call getline00		; read input line
	call iseol?
	je .end
%if _PM
	xor bx, bx
%endif
	mov dx, 1
	call ee_checkplusminus
	jne .notplusminus
	cmp al, '+'
	je ee 3
	jmp short ee 2

.notplusminus:

===


		; INP:	al = character, si-> line
		;	bx:dx = increment to add/subtract if this is an add/sub request
		; OUT:	al, si unchanged
		;	NZ if no add/sub request
		;	ZR if add/sub request,
		;	 [ e_addr ] offset adjusted
ee_checkplusminus:
	cmp al, '-'
	jne .not
	cmp al, '+'
	jne .not
	push si
	push ax
	call skipwhite
	call iseol?
	pop ax
	pop si
	jne .not
	cmp al, '-'
	je .minus
	add word [ e_addr ], dx
_386_PM	adc word [ e_addr+2 ], bx
	jmp short .done

.minus:
	sub word [ e_addr ], dx
_386_PM	sbb word [ e_addr+2 ], bx
.done:
	cmp al, al
.not:
	retn

===


ee 9:
	call getline00

%endif


		; E command - edit memory.
ee:
	call prephack
	mov bx, word [reg_ds]
	call getaddr		; get address into bx:(e)dx
	call skipcomm0
	call iseol?
	je ee1			; if prompt mode

eeparsestr:
	push dx			; save destination offset
	call getstr		; get data bytes
	mov cx, di
	mov dx, line_out
	sub cx, dx		; length of byte string
	pop di
	mov ax, cx
	dec ax
	add ax, di
	jc short errorj4	; if it wraps around
	call dohack
	mov si, dx
	mov es, bx
%if _PM
	cmp byte [bAddr32], 0
	jz ee_2
[cpu 386]
	mov dx, di		; dx was destroyed
	mov edi, edx
	movzx esi, si
	movzx ecx, cx
	a32
__CPU__
ee_2:
%endif
	rep movsb

		; Restore ds + es and undo the interrupt vector hack.
		; This code is also used by the `m' command.
ee0a:
	push ss			; restore ds
	pop ds
	push ss			; restore es
	pop es
	mov di, run2324		; debuggee's int 23/24 values
	call prehak1		; copy things back
	jmp unhack



		; Prompt mode.
ee1:
	call guard_re
		; Begin loop over lines.
ee2:				; <--- next line
	mov ax, bx		; print out segment and offset
	call hexword
	mov al, ':'
	stosb
	_386_PM_o32		; mov eax, edx
	mov ax, dx
%if _PM
	call testattrhigh	; 32-bit segment ?
	jz .16			; no -->
	call hexword_high
.16:
%endif
	call hexword

		; Begin loop over bytes.
ee3:				; <--- next byte
	mov ax, 32<<8|32	; print old value of byte
	stosw
	call dohack		; do the INT pointer hack
	call readmem		; read mem at BX:(E)DX
	call unhack		; undo the INT pointer hack
	call hexbyte
	mov al, '.'
	stosb
	mov byte [ linecounter ], 0	; reset counter
	clropt [internalflags], promptwaiting
	push bx
	push dx
	call putsline
	pop dx
	pop bx
	mov si, line_out+16	; address of buffer for characters
	xor cx, cx		; number of characters so far

ee4_next:
	call getline_is_input_file?
	jc ee9_getc_tty		; if it's a TTY

ee_getc_file:
	setopt [internalflags2], dif2_did_getline_file
				; set this flag so yy_reset_buf knows
				;  that we may have buffered the file

	push si
%if _NEWFULLHANDLING
	mov di, line_in+3       ; read max
%else
	mov di, line_in+2
%endif
	mov si, word [bufnext]
ee5:
	cmp si, word [bufend]
	jb ee6			; if there's a character already
	call fillbuf
	mov al, 13
	jc ee8			; if eof
ee6:
	cmp byte [notatty], 13
	jne ee7			; if no need to compress CR/LF
	cmp byte [si], 10
	jne ee7			; if not a line feed
	inc si			; skip it
	inc byte [notatty]	; avoid repeating this
	jmp ee5			; next character

ee7:
	lodsb			; get the character
	mov byte [notatty], al
ee8:
	mov word [bufnext], si
	pop si
	jmp ee10_got_codepoint

ee9_getc_tty:
	call getc		; character input without echo
ee10_got_codepoint:
	cmp al, 32		; (go to next byte)
	je ee13_write
	cmp al, '-'		; (go to prior byte)
	je ee13_write
	cmp al, '.'		; (exit E interactive mode)
	je ee13_write
	cmp al, 10
	je ee13_write
	call iseol?.notsemicolon; (also exit E interactive mode)
	je ee13_write		; all: done with this byte -->
	cmp al, 8
	je ee11_backspace	; if backspace -->
	cmp al, 7Fh
	je ee11_backspace	; if DEL (handle like backspace) -->
	cmp cx, byte 2		; otherwise, it should be a hex character
	jae ee4_next		; if we have a full byte already
	mov byte [si], al
	call getnyb
	jc ee4_next		; if it's not a hex character
	inc cx
	lodsb			; get the character back
	jmp ee12_put_then_next

ee112_priorbyte:
	call putc		; display the minus
_386_PM	call testattrhigh	; 32-bit segment ?
_386_PM	jz .16			; no -->
	_386_PM_o32
.16:
	dec dx			; decrement offset (16 bit or 32 bit)
	mov di, line_out
	jmp ee15_linebreak_and_ee2

ee11_backspace:
	jcxz ee4_next		; if nothing to backspace over
	dec cx
	dec si
	call fullbsout
	jmp ee4_next

ee12_put_then_next:
	call putc
	jmp ee4_next		; back for more

		; We have a byte (if CX != 0).
		;
		; cx = number of digits we have (0..2)
		; al = codepoint specifying how to proceed after writing
ee13_write:
	jcxz ee14_done_write	; if no change for this byte

	push ax			; preserve proceed control
	xor ax, ax		; ah = 0, al = 0
	mov byte [si], al	; terminate the string
	sub si, cx		; point to beginning
@@:
	add ah, ah
	add ah, ah
	add ah, ah
	add ah, ah		; prior value times 16
	add ah, al		; add next digit (0 in first iteration)
	lodsb			; load next digit (or NUL)
	call getnyb
	jnc @B			; if another digit --> (NC)
				; (CY can only mean we reached the NUL)
	mov al, ah		; get byte value
	call dohack		; do the INT pointer hack
	call writemem		; write AL at BX:(E)DX
	mov di, run2324		; debuggee's int 23/24
	call prehak1		; copy things back
	call unhack		; undo the INT pointer hack
	pop ax			; al = how to proceed

		; End the loop over bytes.
ee14_done_write:
	mov di, line_out	; reset output buffer

	cmp al, 32		; (go to next byte)
	je ee_nextbyte
	cmp al, '-'		; (go to prior byte)
	je ee112_priorbyte
	cmp al, '.'		; (exit E interactive mode)
	je ee16_end
	cmp al, 10
	je ee16_end
	call iseol?.notsemicolon; (also exit E interactive mode)
	je ee16_end
	jmp error		; unexpected value

ee_nextbyte:
_386_PM	call testattrhigh	; 32-bit segment ?
_386_PM	jz .16			; no -->
	_386_PM_o32
.16:
	inc dx			; increment offset (16 bit or 32 bit)
	test dl, 7
	jz ee15_linebreak_and_ee2
				; if new line
	not cx
	add cx, byte 4		; compute 3 - cx
	mov al, 32
	rep stosb		; store that many spaces
	jmp ee3			; back for more

ee15_linebreak_and_ee2:
	mov ax, 10 << 8 | 13	; terminate this line
	stosw
	jmp ee2			; back for a new line

ee16_end:
	jmp putsline_crlf	; call putsline and return


		; F command - fill memory
ff:
	xor cx, cx		; get address range (no default length)
	mov bx, word [reg_ds]
	call getrange		; get address range into bx:(e)dx
	_386_PM_o32	; sub ecx, edx
	sub cx, dx
	_386_PM_o32	; inc ecx
	inc cx			; (e)cx = number of bytes
	push bx
	_386_PM_o32	; push ecx
	push cx			; save it
	_386_PM_o32	; push edx
	push dx			; save start address

	dec si
	mov dx, msg.range
	call isstring?
	lodsb
	jne .notrange

	mov bx, word [reg_ds]	; get search range
	setopt [internalflags3], dif3_accept_getrange_0
	call getrangeX.ecx_and_0_valid
				; try to get second range
	call chkeol		; and insure end-of-line
				; successful if it returned
	_386_PM_o32		; mov esi, edx
	mov si, dx		; bx:esi-> source string
	_386_PM_o32		; sub ecx, edx
	sub cx, dx		; ecx = count - 1
	jmp short @F

.notrange:
	call skipcomm0
	call getstr		; get string of bytes
	mov cx, di
	sub cx, line_out
	jz error
	dec cx
_386_PM	movzx ecx, cx
	mov bx, ds
_386_PM	xor esi, esi
	mov si, line_out

@@:
	_386_PM_o32	; pop edi
	pop di
	_386_PM_o32	; pop eax
	pop ax
	pop es
	mov ds, bx
%if _PM
	cmp byte [ss:bAddr32], 0
	jz ff16
ff32:
[cpu 386]
	inc ecx
	jz error
	cmp ecx, byte 1
	je .onebytesource
	xor edx, edx		; edx:eax = size
	div ecx
	test eax, eax
	jz .partial
.loop:
	push esi
	push ecx
	a32 movsb
	dec ecx
	a32 rep movsb
	pop ecx
	pop esi
	dec eax
	jnz .loop
.partial:
	mov ecx, edx		; get remainder (number of bytes in partial copy)
	jecxz ffret		; if no partial copy -->
	a32 rep movsb
	jmp short ffret		; done -->
.onebytesource:
	mov ecx, eax		; size
	mov al, byte [esi]
	a32 rep stosb
	jmp short ffret
__CPU__
ff16:
%endif
	xor dx, dx		; dx:ax = size
	cmp ax, byte 1
	adc dx, byte 0		; convert 0000:0000 to 0001:0000 (0 = 64 KiB)
	inc cx
	jnz @F
; dx:ax = 1_0000h, remainder = 0, quotient = 1
; dx:ax = 1, remainder = 1, quotient = 0
; dx:ax = 1234h, remainder = 1234h, quotient = 0
	xchg ax, dx
	jmp @FF

@@:
	cmp cx, byte 1
	je .onebytesource	; a common optimization
	div cx			; compute number of whole repetitions
@@:
	test ax, ax
	jz .partial		; if less than one whole rep
.loop:
	push si
	push cx
	movsb
	dec cx
	rep movsb
	pop cx
	pop si
	dec ax
	jnz .loop		; if more to go
.partial:
	mov cx, dx		; get remainder (number of bytes in partial copy)
	jcxz ffret		; if no partial copy -->
	rep movsb
	jmp short ffret		; done -->
.onebytesource:
	mov cx, ax		; size
	mov al, byte [si]
	stosb			; cx=0 -> 64 kB
	dec cx
	rep stosb
ffret:
	push ss			; restore ds
	pop ds
	push ss			; restore es
	pop es
	retn


%if _EXPRESSIONS

		; H command - hex computation
hh:
	call skipcomm0
	dec si
	mov dx, msg.base
	call isstring?
	jne .normal
	call skipequals
	call getexpression
	test bx, bx
@@:
	jnz error
	cmp dx, 36
	ja @B
	cmp dx, 2
	jb @B
	push dx

	call skipcomm0
	dec si
	mov dx, msg.group
	call isstring?
	mov dx, 0
	jne .gotgroup
	call skipequals
	call getexpression
	call skipcomm0
	test bx, bx
	jnz @B
	cmp dx, 32
	ja @B
	dec si
.gotgroup:
	push dx
	mov dx, msg.width
	call isstring?
	mov bx, 0
	jne .gotwidth
	call skipequals
	call getexpression
	call skipcomm0
	test bx, bx
	jnz @B
	cmp dx, 32
	ja @B
	mov bx, dx
	db __TEST_IMM8		; (skip lodsb)
.gotwidth:
	lodsb
	push bx
	call .compute
	pop ax
	pop si
	pop cx
	mov di, line_out + 66

	call .storeresult

	xchg bx, ax		; ax:dx = number, bx = width
	xchg ax, dx		; dx:ax = number
	lframe
	lenter
	lvar dword,	dividend
	 push dx
	 push ax
	dec bx
	lvar word,	minwidth
	 push bx
	lvar word,	group
	 push si
	lvar word,	groupcounter
	 push si

	mov bx, di
	std			; _AMD_ERRATUM_109_WORKAROUND does not apply

		; dword [bp + ?dividend] = number to display
		; cx = base
.loop_write:

	xor dx, dx
	push di
	mov di, 4
.loop_divide:
	mov ax, [bp + ?dividend - 2 + di]
	div cx
	mov word [bp + ?dividend - 2 + di], ax
	dec di
	dec di
	jnz .loop_divide
				; dx = last remainder
	pop di
	xchg ax, dx		; ax = remainder (next digit)
				; dword [bp + ?dividend] = result of div
	add al, '0'
	cmp al, '9'
	jbe @F
	add al, -('9'+1)+'A'
@@:
	stosb

	dec word [bp + ?groupcounter]
	jnz @F
	push word [bp + ?group]
	pop word [bp + ?groupcounter]
	mov al, '_'
	stosb
@@:

	dec word [bp + ?minwidth]
	jns .loop_write

	cmp word [bp + ?dividend + 2], 0
	jnz .loop_write
	cmp word [bp + ?dividend], 0
				; any more ?
	jnz .loop_write		; loop -->

	cld

	sub bx, di
	mov cx, bx
	mov si, di
	inc si

	mov di, line_out

	cmp byte [si], '_'
	jne @F
	inc si
	dec cx
		; never need to loop because next digit is always a digit

@@:
	rep movsb		; overlapping!

	lleave
	jmp short .putsline_crlf


.normal:
	lodsb
	push si
	push ax
	or byte [hhflag], 1	; set flag so no operator means add
	call .compute
	pop ax
	pop si
	call .storeresult
	test byte [hhflag], 4	; any two-fold operation ?
	jz .single		; no -->
	mov ah, byte [options2 + 1]
	and ah, opt2_hh_compat >> 8
	call .store2		; display "FFFFFFFF (-0001)"
	push ax
	mov ax, 32<<8|32
	stosw
	pop ax
	push ax
	or byte [hhflag], 2	; set flag so no operator means sub
	call .compute
	pop ax
	call .store2		; display "FFFFFFFF (-0001)"
	jmp short .putsline_crlf

.single:
	mov ah, 0
	 push bx
	 push dx
	call .store2

	mov ax, (32 << 8) | 32
	stosw
	mov ax, "de"
	stosw
	mov ax, "ci"
	stosw
	mov ax, "ma"
	stosw
	mov ax, "l:"
	stosw
	mov al, 32
	stosb
	 pop ax
	 pop dx
	call decdword
	test dx, dx		; result negative ?
	jns @F			; no -->
	 push ax
	mov ax, " ("
	stosw
	mov al, "-"
	stosb
	 pop ax
	neg dx
	neg ax
	sbb dx, byte 0		; neg bx:dx
	call decdword
	mov al, ")"
	stosb
@@:
.putsline_crlf:
	jmp putsline_crlf


.compute:
	call getdword
	call chkeol		; expect end of line here
.comp_ret:
	retn

		; INP:	bx:dx = result
		;	ah = flag, nonzero if to stay 86-DOS Debug compatible
		; OUT:	displayed
		; CHG:	di, bx, dx
.store:
	push ax
	test ah, ah
	jnz .store_nothigh
	test bx, bx
	jz .store_nothigh	; no need to display 32-bit value
	mov ax, bx
	call hexword
.store_nothigh:
	mov ax, dx
	call hexword
	pop ax
	retn

.store2:
	call .store
	push ax
	test ah, ah
	jnz .store2_ret
	test bx, bx		; result negative ?
	jns .store2_ret		; no -->
	mov ax, " ("
	stosw
	mov al, "-"
	stosb
	neg bx
	neg dx
	sbb bx, byte 0		; neg bx:dx
	pop ax
	push ax
	call .store
	mov al, ")"
	stosb
.store2_ret:
	pop ax
	retn


.storeresult:
	mov word [hhresult], dx
	mov word [hhresult + 2], bx
	retn
%else
		; H command - hex addition and subtraction.
hh:
	call getdword
	push bx
	push dx
	call skipcomm0
	call getdword
	call chkeol		; expect end of line here
	pop cx
	pop ax			; first value in AX:CX, second in BX:DX
	mov si, ax
	mov bp, cx		; first value in SI:BP now
	mov ax, cx
	add ax, dx
	push ax
	mov ax, si
	adc ax, bx
	jz .nothigh1		; no need to display 32-bit value
	call hexword
.nothigh1:
	pop ax
	call hexword
	mov ax, 2020h
	stosw
	mov ax, bp
	sub ax, dx
	push ax
	mov ax, si
	sbb ax, bx
	jz .nothigh2		; no need to display 32-bit value
	or si, bx
	jz .nothigh2		; both were zero, non-zero result only by carry -->
	call hexword
.nothigh2:
	pop ax
	call hexword
	call putsline_crlf
	retn
%endif


	usesection lDEBUG_DATA_ENTRY

	align 4, db 0
hhresult:	dd 0

	usesection lDEBUG_CODE


		; O command - output to I/O port.
oo:
	mov ah, 'O'
	mov bx, .tab
	jmp ii.common


	align 2, db 0
.tab:
	dw .byte, .word, .dword


.byte:
	call getbyte		; read value from command line
	call chkeol		; expect end of line here
	xchg ax, dx		; al = value
	pop dx			; recover port number
	out dx, al		; send
	retn

.word:
	call getword
	call chkeol
	xchg ax, dx		; ax = value
	pop dx
	out dx, ax
	retn

.dword:
[cpu 386]
	call getdword
	call chkeol		; expect end of line here
	push bx
	push dx
	pop eax			; eax = value
	pop dx
	out dx, eax
	retn
__CPU__


%if _INPUT_FILE_HANDLES || _INPUT_FILE_BOOT
if_exists_check:
	push ss
	pop es
	mov di, word [if_exists_si]
	mov cx, word [if_exists_length]
	mov si, word [if_exists_sp]
	rep movsb
	mov cx, word [if_exists_length]
	inc cx
	and cl, ~1

	mov si, word [if_exists_then_address]
	test si, si
	jz .error
	dec si
	mov dx, msg.then
	call isstring?
	jne .error
	retn

.error:
	mov ax, 107h
	call setrc
	jmp error

if_exists_not_found:
	call if_exists_check
	testopt [internalflags3], dif3_if_not
	jnz if_exists_condition_met
if_exists_condition_not_met:
	jmp cmd3

if_exists_found_open:
	push ss
	pop es
	call getline_close_file

if_exists_found_closed:
	call if_exists_check
	testopt [internalflags3], dif3_if_not
	jnz if_exists_condition_not_met
if_exists_condition_met:
	mov sp, word [if_exists_sp]
	add sp, cx
	call skipwhite
	pop dx				; discard near return address
	clropt [internalflags3], dif3_in_if | dif3_auxbuff_guarded_1
	jmp cmd3_notblank
%endif


		; I command - input from I/O port.
		;
		; IF command -- conditional
ii:
	mov dx, si
	push ax
	mov ax, [si - 2]
	and ax, TOUPPER_W
	cmp ax, "IF"
	pop ax
	jne .not_if

	call skipwhite
	call isoperator?
	jne .if
	mov bx, cx
	add bx, bx			; bh = 0 !
	push ax
	call near [operatordispatchers+bx]
	pop ax
	test bx, bx
	jnz .not_if
	call skipwhite
.if:
	clropt [internalflags3], dif3_if_not
	dec si
	mov dx, msg.not
	call isstring?
	lodsb
	jne @F
	call skipwh0
	setopt [internalflags3], dif3_if_not

@@:
%if _INPUT_FILE_HANDLES || _INPUT_FILE_BOOT
	dec si
	mov dx, msg.exists
	call isstring?
	lodsb
	jne .if_numeric
	call skipwh0
	dec si
	mov dx, msg.y
	call isstring?
	jne error
	call skipwhite

	dec si
	mov word [if_exists_si], si
	mov bx, si
@@:
	lodsb
	call iseol?.notsemicolon
	jne @B
	mov cx, si		; -> after EOL byte
	sub cx, bx		; = length including EOL
	mov word [if_exists_length], cx
	inc cx			; round up
	and cl, ~1		; make even
	sub sp, cx
	mov word [if_exists_sp], sp
	mov di, sp
	mov si, bx
	shr cx, 1
	rep movsw

	mov si, bx
	lodsb
	and word [if_exists_then_address], 0
	setopt [internalflags3], dif3_in_if
	call yy
	jmp error
%endif

.if_numeric:
	call getexpression
	call toboolean
	mov bx, dx
	mov dx, msg.then
	dec si
	call isstring?
	jne error
	call skipwhite
	testopt [internalflags3], dif3_if_not
	jz @F
	xor bl, 1
@@:
	test bx, bx
	jz .if_false
	pop bx			; discard near return address to cmd3
	jmp cmd3_notblank	; execute tail

.if_false:
	jmp resetrc

.not_if:
	mov si, dx
	dec si
	lodsb

	mov ah, 'I'
	mov bx, .tab

		; bx = jump table for byte, word, dword handler
		; ah = letter of the command
		; si, al etc.
.common:
	push ax
	call uppercase
	cmp al, 'W'
	jne .notw
.incbx2:
	inc bx
	inc bx			; use word handler
	call skipwhite		; skip the 'W' til next character
	jmp short .sizeset

.notw:
	cmp al, 'D'
	jne .sizeset
%if 1
	xor ah, byte [si-2]
	jz .d			; "Id" or "Od" --> (uppercase command)
	xor ah, 32
	jnz .sizeset		; no space is allowed between the command and 'D' -->
			; "id" or "od" here (lowercase command)
.d:
%endif
_386	inc bx
_386	inc bx			; use dword handler
	_386_jmps .incbx2	; bx += 2 and skip the 'D'
			; no 386 here. try with D as part of port number
.sizeset:
	call getword		; get port
	pop cx			; restore letter if necessary
	cmp ch, 'I'		; check whether I or O
	jne .o			; O -->
	call chkeol		; expect end of line here for I commands
	db __TEST_IMM8		; (skip push)
.o:
	push dx			; save port number for O commands
	jmp near [cs:bx]


	align 2, db 0
.tab:
	dw .byte, .word, .dword

.byte:
	in al, dx
	call hexbyte
	jmp short .done

.word:
	in ax, dx
.doneword:
	call hexword
.done:
	jmp putsline_crlf

.dword:
[cpu 386]
	in eax, dx
	call hexword_high
__CPU__
	jmp short .doneword


errorj5:jmp	error


%if _PM
		; OUT:	NC
		;	ZR if in protected mode
		;	NZ otherwise
		; STT:	-
		;	([internalflags] & nodosloaded, [internalflags] & protectedmode set up)
ispm:
	push ax
%if protectedmode & ~0FF00h
 %error Internal flags re-ordered, adjust code here
%endif
	mov al, byte [ss:internalflags+1]	; get flag byte
	and al, protectedmode>>8		; separate PM flag
	xor al, protectedmode>>8		; ZR if in PM (NC)
	pop ax
	retn
%endif


setpspdbg:
	mov bx, ss
setpsp:
%if _BOOTLDR
	testopt [internalflags], nodosloaded
	jnz .ret		; no PSPs -->
%endif
	mov ah, 50h
%if _PM && (_NOEXTENDER || _USESDA)
	call ispm
 %if _NOEXTENDER
	jnz .rm
subcpu 286
	push cx
	push dx
	push bx
	push ax
	mov ax, 0006h
	int 31h
	pop ax
	shl cx, 12
	shr dx, 4
	or dx, cx
	mov bx, dx
	call _doscall.pm	; insure non-extended
	pop bx
	pop dx
	pop cx
	retn
subcpureset
 %else
	jz _int21		; extended
 %endif
%endif
.rm:
%if _USESDA
	cmp word [pSDA+0], byte -1
	je _int21
	push ds
	push si
	mov si, pSDA + so16aSegSel
	call update_dosdata_segment
	lds si, [si - so16aSegSel]
	mov word [si+10h], bx
	pop si
	pop ds
	retn
%else
	jmp short _int21
%endif
%if _BOOTLDR
.ret:
getpsp.ret:
	retn
%endif

getpsp:
%if _BOOTLDR
	xor bx, bx
	testopt [internalflags], nodosloaded
	jnz .ret		; no PSPs -->
%endif
	mov ah, 51h
%if _PM && (_NOEXTENDER || _USESDA)
	call ispm
 %if _NOEXTENDER
	jnz .rm
	call _doscall.pm	; insure non-extended
	mov ax, 2
	int 31h
	mov bx, ax
	retn
 %else
	jz _int21		; extended
 %endif
%endif
.rm:
%if _USESDA
	cmp word [pSDA+0], byte -1
	je _int21
	push ds
	push si
	mov si, pSDA + so16aSegSel
	call update_dosdata_segment
	lds si, [si - so16aSegSel]
	mov bx, word [si + 10h]
	pop si
	pop ds
	retn
%else
	jmp short _int21
%endif


_doscall_return_es:
_doscall_return_es_parameter_es_ds:
	lframe near
	lpar word, es_ds_value
	lpar_return
%if _PM
	lvar word, int_number
	lenter
	mov word [bp + ?int_number], 21h
	pushf
	call ispm
	jnz .rm
.pm:
	popf
	push word [bp + ?es_ds_value]
	push word [bp + ?es_ds_value]
	push word [bp + ?int_number]
	push word [bp + ?frame_bp]
	call intcall_return_parameter_es_parameter_ds
	pop word [bp + ?es_ds_value]		; discard returned ds
	pop word [bp + ?es_ds_value]		; get es
	jmp .ret
.rm:
	popf
%else
	lenter
%endif
	 push es
	 push ds
	push word [bp + ?es_ds_value]
	pop ds
	push word [bp + ?es_ds_value]
	pop es
	int 21h
	push es
	pop word [bp + ?es_ds_value]
	 pop ds
	 pop es
.ret:
	lleave
	lret


		; Execute real Int21 instruction. If this is in PM it might get extended.
_int21:
%if _BOOTLDR
	pushf
	testopt [internalflags], nodosloaded
	jnz .reterr		; no Int21 --> (throw?)
	popf
%endif
	int 21h
	retn
%if _BOOTLDR
.reterr:
	popf
	mov ax, 1
	stc
	retn
%endif


%if _PM
intcall_return_parameter_es_parameter_ds:
	lframe near
	lpar word, es_value
	lpar word, ds_value
	lpar_return
	lpar word, int_number
	lpar word, bp_value
	lvar 32h, 86m_call_struc
	lenter
	push es
	mov word [bp + ?86m_call_struc +00h], di	; edi
	mov word [bp + ?86m_call_struc +04h], si	; esi
	mov word [bp + ?86m_call_struc +10h], bx	; ebx
	mov word [bp + ?86m_call_struc +14h], dx	; edx
	mov word [bp + ?86m_call_struc +18h], cx	; ecx
	mov word [bp + ?86m_call_struc +1Ch], ax	; eax
	mov ax, word [bp + ?bp_value]
	mov word [bp + ?86m_call_struc +08h], ax	; bp
	mov al, 0					; (preserve flags!)
	lahf
	xchg al, ah
	mov word [bp + ?86m_call_struc +20h], ax	; flags
	xor ax, ax
	mov word [bp + ?86m_call_struc +2Eh], ax	; sp
	mov word [bp + ?86m_call_struc +30h], ax	; ss
	mov ax, word [bp + ?es_value]			; usually [pspdbg]
	mov word [bp + ?86m_call_struc +22h], ax	; es
	mov ax, word [bp + ?ds_value]			; usually [pspdbg]
	mov word [bp + ?86m_call_struc +24h], ax	; ds
	push ss
	pop es				; => stack
	lea di, [bp + ?86m_call_struc]	; -> 86-Mode call structure
_386	movzx edi, di			; (previously checked b[dpmi32] here)
	mov bx, word [bp + ?int_number]			; int#
	xor cx, cx
	mov ax, 0300h
	int 31h
	mov ah, byte [bp + ?86m_call_struc +20h]	; flags
	sahf
	mov di, word [bp + ?86m_call_struc +00h]	; edi
	mov si, word [bp + ?86m_call_struc +04h]	; esi
	mov bx, word [bp + ?86m_call_struc +10h]	; ebx
	mov dx, word [bp + ?86m_call_struc +14h]	; edx
	mov cx, word [bp + ?86m_call_struc +18h]	; ecx
	mov ax, word [bp + ?86m_call_struc +1Ch]	; eax
	push word [bp + ?86m_call_struc +22h]		; return es value
	pop word [bp + ?es_value]			;  in the parameter
	push word [bp + ?86m_call_struc +24h]		; return ds value
	pop word [bp + ?ds_value]			;  in the parameter
	pop es
	lleave
	lret

intcall:
	lframe near
	lpar word, es_ds_value
	lpar word, int_number
	lenter
	push word [bp + ?es_ds_value]			; es
	push word [bp + ?es_ds_value]			; ds
	push word [bp + ?int_number]			; int number
	push word [bp + ?frame_bp]			; bp
	call intcall_return_parameter_es_parameter_ds
		; (discard returned parameters ?es_value, ?ds_value, done by lleave)
	lleave , forcerestoresp
	lret


call_int2D:
	call ispm
	jnz short .rm
subcpu 286
	push word [ss:pspdbg]	; es ds value. generally unused
	push 2Dh		; interrupt 2Dh
	call intcall		; call it
	retn
subcpureset
.rm:
	int 2Dh			; directly call int 2Dh
	retn


		; Called in PM only, ds unknown.
		;
		; INP:	-
		; OUT:	CY if no DOS extender available ("MS-DOS" on Int2F.168A)
		;	NC if DOS extender available
		; CHG:	-
isextenderavailable:
subcpu 286
	push ds
	push es
	pusha
	push ss
	pop ds
	mov si, msg.msdos
_386	movzx esi, si
	mov ax, 168Ah
	int 2Fh
	cmp al, 1			; CY if al is zero
	cmc				; NC if al is zero, CY else
	popa
	pop es
	pop ds
	retn
subcpureset

nodosextinst:
	push ss
	pop ds
	mov dx, nodosext
	jmp putsz
%endif


		; L command - read a program, or disk sectors, from disk.
ll:
%if _BOOTLDR
	testopt [internalflags], nodosloaded
	jz @F
	mov dx, msg.nobootsupp
	jmp putsz
@@:
%endif

	call parselw		; parse L and W argument format
	jz ll1			; if request to read program
%if _PM && _NOEXTENDER
	call ispm
	jnz .rm
	call isextenderavailable
	jc nodosextinst
.rm:
%endif
	testopt [ss:internalflags], newpacket| ntpacket
	jz .oldint
	mov dl, al		; zero-based drive
	mov si, 6000h		; read, assume "file data"
%if _VDD
	testopt [internalflags], ntpacket
	jnz .vdd
%endif
	inc dl			; one-based drive
	mov ax, 7305h		; ds:(e)bx-> packet
	stc
	int 21h			; use int 21h here, not doscall
	jmp short .done
%if _VDD
.vdd:
	mov ax, word [hVdd]
	mov cx, 5
%if _PM
	add cl, byte [dpmi32]
%endif
	DispatchCall
	jmp short .done
%endif
.oldint:
	int 25h
.done:
	mov dx, reading
	jmp ww1

		; For .COM or .EXE files, we can only load at cs:100.  Check that first.
ll1:
	call InDos
	jnz not_while_indos
	call guard_re
	test byte [fileext], EXT_COM| EXT_EXE
	jz ll4			; if not .COM or .EXE file
	cmp bx, word [reg_cs]
	jne ll2			; if segment is wrong
	cmp dx, 100h
	je ll4			; if address is OK (or not given)
ll2:
	jmp error		; can only load .COM or .EXE at cs:100

not_while_indos:
	mov ax, 0105h
	call setrc
	mov dx, msg.not_while_indos
	jmp putsz_error


		; load (any) file (if not .EXE or .COM, load at BX:DX)
ll3:
	cmp byte [fileext], 0
	jne ll4
	retn

		; open file and get length
ll4:
	mov si, bx		; save destination address, segment
	mov di, dx		; and offset
	mov ax, 3D00h		; open file for reading
	mov dx, DTA
	doscall
	jc ll16			; error
	xchg ax, bx		; mov bx, ax
	mov ax, 4202h		; lseek
	xor cx, cx
	xor dx, dx
	int 21h

;	Split off file types
;	At this point:
;		bx	file handle
;		dx:ax	file length
;		si:di	load address (CS:100h for .EXE or .COM)

	test byte [fileext], EXT_COM | EXT_EXE
	jnz ll13		; if .COM or .EXE file

%if _PM
;--- dont load a file in protected mode,
;--- the read loop makes some segment register arithmetic
	call ispm
	jnz .rm
	mov dx, nopmsupp
	call putsz
	jmp ll12
.rm:
%endif

		; Load it ourselves.
		; For non-.com/.exe files, we just do a read, and set BX:CX to the
		; number of bytes read.

	call ensuredebuggeeloaded	; make sure a debuggee is loaded
	mov es, word [pspdbe]

		; Check the size against available space.
	push	si
	push	bx

	cmp si, word [es:ALASAP]
	pushf
	neg si
	popf
	jae ll6				; if loading past end of mem, allow through ffff
	add si, word [es:ALASAP]	; si = number of paragraphs available
ll6:
	mov cx, 4
	xor bx, bx
ll7:
	shl si, 1
	rcl bx, 1
	loop ll7
	sub si, di
	sbb bx, cx			; bx:si = number of words left
	jb ll9				; if already we're out of space
	cmp bx, dx
	jne ll8
	cmp si, ax
ll8:
	jae ll10			; if not out of space
ll9:
	pop bx				; out of space
	pop si
	mov dx, doserr8			; not enough memory
	call putsz			; print string
	jmp short ll12

ll10:
	pop	bx
	pop	si

;	Store length in registers

; seems a bit unwise to modify registers if a debuggee is running
; but MS DEBUG does it as well

%if 0
	mov cx,[reg_cs]
	cmp cx,[pspdbe]
	jnz .noregmodify
	cmp word [reg_eip], 100h
	jnz .noregmodify
%endif
	mov word [reg_ebx], dx
	mov word [reg_ecx], ax
.noregmodify:

		; Rewind the file
	mov ax, 4200h		; lseek
	xor cx, cx
	xor dx, dx
	int 21h

	mov dx, 0Fh
	and dx, di
	mov cl, 4
	shr di, cl
	add si, di		; si:dx is the address to read to

		; Begin loop over chunks to read
ll11:
	mov ah, 3Fh		; read from file into DS:(E)DX
	mov cx, 0FE00h		; read up to this many bytes
	mov ds, si
	int 21h

	add	si,0fe0h	;wont work in protected-mode!
	cmp	ax,cx
	je	ll11		;if end of file reached

;	Close the file and finish up.

ll12:
	mov ah, 3Eh		; close file
	int 21h
	push ss			; restore ds
	pop ds
	retn			; done

ll13:
		; file is .EXE or .COM
		; Close the file
%if 0
	push ax
	mov ah, 3Eh		; close file
	int 21h
	pop bx			; dx:bx is the file length

		; adjust .exe size by 200h (who knows why)
		; cm: this is wrong. It needs to be adjusted by the header size,
		; which is stored (as number of paragraphs) in the .EXE header.
		; The header size is often 200h, but not always.
	test byte [fileext], EXT_EXE
	jz ll14			; if not .EXE
	sub bx, 200h
	sbb dx, 0
%else
	push dx
	push ax

	mov ax, 4200h		; lseek set
	xor cx, cx
	xor dx, dx
	int 21h
	 push ss
	 pop ds

	mov bp, sp
	mov cx, EXEHEADER_size
	sub sp, cx
	mov dx, sp
	mov si, sp
	mov ah, 3Fh
	int 21h

	push ax
	mov ah, 3Eh		; close file
	int 21h
	pop ax

	cmp ax, cx
	jne .no_exe
	cmp word [si + exeSignature], "MZ"
	je @F
	cmp word [si + exeSignature], "ZM"
	jne .no_exe
@@:

		; This possibly should honour the size of the image in pages
		; as indicated by the header, instead of the file size.
		; Oh well, for now we use the file size (on stack).
	mov ax, [si + exeHeaderSize]
	xor si, si
	mov cx, 4
@@:
	shl ax, 1
	rcl si, 1
	loop @B			; si:ax <<= 4

	mov sp, bp
	pop bx
	pop dx

	sub bx, ax
	sbb dx, si		; file size minus header size

	jmp @F

.no_exe:
	mov sp, bp
	pop bx
	pop dx			; full file size
@@:
%endif

		; Clear registers

ll14:
	push bx
	push dx
;	mov word [reg_ebx], dx
;	mov word [reg_ecx], bx

;--- cancel current process (unless there is none)
;--- this will also put cpu back in real-mode!!!

	call terminate_attached_process
	jz ll_attached_unterminated

	mov di, regs
	mov cx, 16*2	;(8 std, 6 seg, ip, fl) * 2
	xor ax, ax
	rep stosw

	pop word [reg_ebx]
	pop word [reg_ecx]

		; Fix up interrupt vectors in PSP
	mov si, CCIV		; address of original INT 23 and 24 (in PSP)
	mov di, run2324
	movsw
	movsw
	movsw
	movsw

		; Actual program loading.  Use the DOS interrupt.
	mov ax, 4B01h		; load program
	mov dx, DTA		; offset of file to load
	mov bx, execblk		; parameter block
	int 21h			; load it
	jc ll16			; if error
	mov ax, sp
	sub ax, [SPSAV]
	cmp ax, 80h
	jb ll15			; if in range
	mov ax, 80h
ll15:
	mov word [spadjust], ax
	les si, [execblk+14]
	es lodsw		; recover ax
	mov word [reg_eax], ax
	mov word [reg_esp], si
	and word [reg_esp + 2], 0
	mov word [reg_ss], es
	les si, [execblk+18]
	mov word [reg_eip], si
	and word [reg_eip + 2], 0
	mov word [reg_cs], es
	mov byte [bInit],0
	push ss
	pop es
	call set_efl_to_fl
	call getpsp
	mov ax, bx
	mov word [pspdbe], ax
	clropt [internalflags], attachedterm
	mov di, reg_ds
	stosw
	scasw
	stosw			; reg_es
	call setpspdbg

		; Finish up. Set termination address.
	mov ax, 2522h		; set interrupt vector 22h
	mov dx, int22		; ds => lDEBUG_DATA_ENTRY
	int 21h
	mov ds, word [pspdbe]
	mov word [TPIV], dx
	mov word [TPIV+2], ss	; => lDEBUG_DATA_ENTRY
	push ss
	pop ds

		; Set up initial addresses for 'a', 'd', and 'u' commands.
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
	call ispm
	jnz .86m
.pm:
	scasw			; skip saSegment
	stosw			; store saSelector
	jmp @F
.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
	retn

		; Error messages.  Print and quit.
ll16:
	jmp ww15		; print error message

ll_attached_unterminated:
	mov dx, msg.ll_unterm
	jmp putsz

		; M command - move from place to place.
		;
		; first check if there is more than 1 argument
		; 0 or 1 arguments are handled by the 'M [cpu]' code
mm:
%if 0
	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
	je mc
	call getdword
	call iseol?
	je mc			; one argument, CPU-related
	cmp al, 32
	je .blank		; end of first argument
	cmp al, 9
	jne .nonblank		; not end of first argument
.blank:
%else
	push si
	call iseol?
	je mc			; no argument, CPU-related M command
.nonblank:
	lodsb			; is a non-space and non-CR ?
	call iseol?
	je mc			; one argument, CPU-related
	cmp al, 32
	je .blank		; end of first argument
	cmp al, 9
	jne .nonblank		; not end of first argument
.blank:
%endif
	call skipwh0		; skip blanks behind argument
	call iseol?
	je mc			; one argument, CPU-related
	pop si
	dec si
	lodsb
			; It is a normal M command (Move)
	call parsecm		; 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.
mc:
	pop si
	dec si
	lodsb
	call iseol?
	je m10			; if just an 'm' (query machine type)
	cmp al, '?'
	je m10			; if '?' (also query)
	cmp al, '0'
	jb mc1			; if not a digit
	cmp al, '6'
	ja mc1			; ditto
	sub al, '0'
	mov byte [machine], al	; set machine type
	mov byte [mach_87], al	; coprocessor type, too
	retn

mc1:	or al, TOLOWER
	cmp al, 'c'
	je mcc			; if coprocessor declaration
	cmp al, 'n'
	jne short errorj3	; if something else
	lodsb
	or al, TOLOWER
	cmp al, 'c'
	jne short errorj3	; if not 'c' after that
	lodsb
	call chkeol
	mov byte [has_87], 0	; clear coprocessor flag
	retn			; done

mcc:
	call skipwhite		; get next nonblank character
	mov ah, byte [machine]
	cmp ah, 3
	jne mcc2		; if not a 386
	cmp al, '3'
	je mcc1			; if declaring a 387
	cmp al, '2'
	jne mcc2		; if not '2'
	mov ah, 2
mcc1:
	call skipwhite
mcc2:
	call chkeol
	mov byte [has_87], 1	; set coprocessor flag
	mov byte [mach_87], ah	; set copr. type
	retn

		; Display machine type.
m10:
	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

errorj3:
	jmp error


		; N command - change the name of the program being debugged.
nn:
	push ss
	pop es
%if _BOOTLDR
	testopt [ss:internalflags], nodosloaded
	jz @F
	mov dx, msg.nobootsupp
	jmp putsz
@@:
%endif
	mov	di, DTA		; destination address

		; Copy and canonicalize file name.
nn1:
	cmp di, N_BUFFER_END
	jae .toolong
	call ifsep		; check for separators CR, blank, tab, comma, ;, =
	je nn3			; if end of file name
	cmp al, byte [ss:swch1]
		; The use of ss here appears to be intended to
		;  allow loading from ds different from the
		;  data entry and PSP segment, However, the
		;  subsequent copy of the command tail around
		;  nn4 does not participate in this scheme.
		; So if this is used make sure to adjust that.
	je nn3			; if '/' (and '/' is the switch character)
	call uppercase
	stosb
	lodsb
	jmp short nn1		; back for more

.toolong:
nn4.toolong:
	push ss
	pop ds
	mov dx, msg.n_toolongname
	call putsz
	mov di, N_BUFFER_END - 3
	mov al, 0		; truncate the name
	stosb
	mov byte [fileext], al	; invalid / none
	mov word [execblk+2], di
	mov ax, 13 << 8		; 0 in low byte (tail length), CR in high byte
	stosw
	retn


nn3:
	push ss
	pop ds
	mov al, 0		; null terminate the file name string
	stosb
	mov word [execblk+2], di; save start of command tail

%if _DEBUG4
	push dx
	mov dx, DTA
	call d4disp_msg
	mov dx, crlf
	call d4disp_msg
	pop dx
%endif
		; Determine file extension
	cmp di, DTA+1
	je nn3d			; if no file name at all
	cmp di, DTA+5
	jb nn3c			; if no extension (name too short)
	mov al, EXT_HEX
	cmp word [di-5], ".H"
	jne nn3a		; if not .HEX
	cmp word [di-3], "EX"
	je nn3d			; if .HEX
nn3a:
	mov al, EXT_EXE
	cmp word [di-5], ".E"
	jne nn3b		; if not .EXE
	cmp word [di-3], "XE"
	je nn3d			; if .EXE
nn3b:
	mov al, EXT_COM
	cmp word [di-5], ".C"
	jne nn3c		; if not .COM
	cmp word [di-3], "OM"
	je nn3d			; if .COM
nn3c:
	mov al, EXT_OTHER
nn3d:
	mov byte [fileext], al

		; Finish the N command
	push di
	mov di, line_out
	dec si
nn4:
	lodsb			; copy the remainder to line_out
	stosb
	call iseol?.notsemicolon
	jne nn4

	call InDos
	jz .fcb_setup
	and word [reg_eax], 0
	jmp .fcb_none

.fcb_setup:
		; Set up FCBs.
	mov si, line_out
	mov di, 5Ch
	call nn6		; do first FCB
	mov byte [reg_eax], al
	mov di, 6Ch
	call nn6		; second FCB
	mov byte [reg_eax+1], al
.fcb_none:

		; Copy command tail.
	mov si, line_out
	pop di
	cmp di, N_BUFFER_END - 2
	jae .toolong
	push di
	inc di
nn5:
	lodsb
	stosb
	cmp di, N_BUFFER_END
	jae .toolong
	call iseol?.notsemicolon
	jne nn5			; if not end of string
	; test al, al
	; jnz @B
	mov byte [di - 1], 13	; (just overwrite this unconditionally)
; @@:			; jump destination from above if al == 13
@@:			; jump destination from .toolong
	push di
	mov cx, di
	sub cx, N_BUFFER_END
	neg cx
	xor ax, ax
	rep stosb
	pop di
	pop ax			; recover old DI
	xchg ax, di
	sub ax, di		; compute length of tail
	dec ax
	dec ax
	stosb
%if _DEBUG4
	mov dx, DTA
	call d4disp_msg
	mov dx, crlf
	call d4disp_msg
%endif
	retn			; done

.toolong:
	mov dx, msg.n_toolongtail
	call putsz
	mov di, N_BUFFER_END
	mov byte [di - 1], 13
	jmp @B


		; Subroutine to process an FCB.
		;
		; INP:	di -> FCB
		;	si -> input
nn6:
	lodsb
	call iseol?.notsemicolon
	je nn7			; if end
	call ifsep
	je nn6			; if separator (other than CR)
	cmp al, byte [switchar]
	je nn10			; if switch character
nn7:
	dec si
	mov ax, 2901h		; parse filename
	doscall
	push ax			; save AL
nn8:
	lodsb			; skip till separator
	call ifsep
	je nn9			; if separator character (including CR)
	cmp al, byte [swch1]
	jne nn8			; if not switchar (sort of)
nn9:
	dec si
	pop ax			; recover AL
	cmp al, 1
	jne nn9a		; if not 1
	dec ax
nn9a:
	retn

		; Handle a switch (differently).
nn10:	lodsb
	call iseol?.notsemicolon
	je nn7			; if end of string
	call ifsep
	je nn10			; if another separator (other than CR)
	mov al, 0
	stosb
	dec si
	lodsb
	cmp al, 'a'
	jb nn11			; if not a lower case letter
	cmp al, 'z'
	ja nn11
	and al, TOUPPER		; convert to upper case
nn11:	stosb
	mov ax, 32<<8|32
	stosw
	stosw
	stosw
	stosw
	stosw
	xor ax, ax
	stosw
	stosw
	stosw
	stosw
	retn			; return with al = 0


		; Compare character with separators
		;
		; INP:	al = character
		; OUT:	ZR if al is CR, NUL, blank, tab, comma, semicolon, or equal sign
		;	NZ else
		; REM:	This is only used for parsing FCBs.
ifsep:
	call iseol?		; semicolon or CR or NUL
	je .return
	cmp al, 32
	je .return
	cmp al, 9
	je .return
	cmp al, ','
	je .return
	cmp al, '='
.return:
	retn


		; Ensure segment in bx is writeable
		;
		; INP:	bx = selector/segment
		; OUT:	NC if in 86M, bx unchanged
		;	NC if in PM and bx not a code segment, bx unchanged
		;	NC if in PM and was a code segment,
		;	 bx = word [scratchsel], set up to mirror INP:bx selector
		;	CY if in PM and a failure occurred, segment not writeable
		; CHG:	bx
		; STT:	(if in PM) es = ss = debugger data selector
%if _PM
verifysegm:
	call ispm
	jnz .rm			; (NC)
	push ax
	_386_o32	; push edi
	push di
	push bp
	mov bp, sp
	sub sp, 8
	mov di, sp
_386	movzx edi, di
	mov ax, 000Bh		; get descriptor
	int 31h
	jc @F
	test byte [di+5], 8	; code segment ?
	jz @F			; (NC) no -->
	and byte [di+5], 0F3h	; reset CODE+conforming attr
	or byte [di+5], 2	; set writable
	mov bx, word [scratchsel]
	mov ax, 000Ch
	int 31h
@@:
	mov sp, bp
	pop bp
	_386_o32	; pop edi
	pop di
	pop ax
.rm:
	retn

subcpu 286
		; INP:	dx = 86 Mode segment to access
		; OUT:	bx = scratch selector, addressing that segment
		; CHG:	-
		; STT:	ss = lDEBUG_DATA_ENTRY selector, in PM
setrmsegm:
	mov bx, word [ss:scratchsel]
setrmaddr:		;<--- set selector in BX to segment address in DX
.:
	push dx
	push ax
	push cx
	mov cx, dx
	shl dx, 4
	shr cx, 12
	mov ax, 7
	int 31h
	pop cx
	pop ax
	pop dx
	retn

subcpureset
%endif

		; Read a byte relative to cs:eip
		;
		; INP:	reg_cs, reg_eip
		;	cx = (signed) eip adjustment
		; OUT:	al = byte at that address
		;	(e)bx = new offset (eip+adjustment)
		; CHG:	-
getcseipbyte:
	push es
%if _PM
	mov bx, word [reg_cs]
	mov es, bx
	call testattrhigh
	jz .16
[cpu 386]
	mov ebx, dword [reg_eip]
	push edx
	movsx edx, cx
	add ebx, edx
	mov al, byte [es:ebx]
	pop edx
	pop es
	retn
__CPU__
.16:
%else
	mov es, word [reg_cs]
%endif
	mov bx, word [reg_eip]
	add bx, cx
	mov al, byte [es:bx]
	pop es
	retn

		; Write to a byte relative to cs:eip
		;
		; INP:	reg_cs, reg_eip
		;	cx = (signed) eip adjustment
		; OUT:	al = byte at that address
		; CHG:	(e)bx
setcseipbyte:
	push es
%if _PM
	mov bx, word [reg_cs]
	call verifysegm
	jc .ret
	mov es, bx
	call testattrhigh
	jz .16
[cpu 386]
	mov ebx, dword [reg_eip]
	push edx
	movsx edx, cx
	mov byte [es:ebx+edx],al
	pop edx
	pop es
	retn
__CPU__
.16:
%else
	mov es, word [reg_cs]
%endif
	mov bx, word [reg_eip]
	add bx, cx
	mov byte [es:bx], al
.ret:
	pop es
	retn

		; Exchange byte with memory
		;
		; INP:	bx:(e)dx-> destination byte
		;	al = source byte
		; OUT:	CY if failed due to segment not writable
		;	NC if successful,
		;	 al = previous value of destination byte
		; CHG:	ah
writemem:
%if _DEBUG1
	push dx
	push ax

	call getlinear.do_not_use_test
	jc @F			; already an error ?  then return --> (CY)
	push bx
	push cx
	mov bx, test_records_Writemem
	call handle_test_case_multiple_16
				; check whether this should testcase the error
				; CY to indicate error from this call
	pop cx
	pop bx
@@:
	pop ax
	pop dx
	jnc .do_not_use_test
	retn			; return CY here

%endif
.do_not_use_test:

	mov ah, al
%if _PM
	call ispm
	jnz .16			; (NC from ispm) -->
	call verifysegm		; make bx a writeable segment
	jc .ret
	call testattrhigh
	jz .16			; (NC from testattrhigh) -->
[cpu 386]
	push ds
	mov ds, bx
	xchg al, byte [edx]
	cmp ah, byte [edx]
	pop ds
__CPU__
	jmp short .cmp
.16:
%endif
	push ds
	mov ds, bx
	push bx
	mov bx, dx
	xchg al, byte [bx]
	cmp ah, byte [bx]
	pop bx
	pop ds
.cmp:
	je .ret			; (NC)
	stc			; Failed to compare (i.e. memory wasn't our byte after writing).
				; This check catches ROM that will silently fail to write.
.ret:
	retn


;--- read byte at BX:EDX into AL

readmem:
%if _DEBUG1
	push dx
	push ax

	call getlinear.do_not_use_test
	jc @F			; already an error ?  then return --> (CY)
	push bx
	push cx
	mov bx, test_records_Readmem
	call handle_test_case_multiple_16
				; check whether this should testcase the error
				; CY to indicate error from this call
	pop cx
	pop bx
@@:
	pop ax
	pop dx
	jnc .do_not_use_test
	mov al, byte [test_readmem_value]
				; return a most likely wrong value
	retn

%endif
.do_not_use_test:

%if _PM
	call testattrhigh
	jz .16
[cpu 386]
	push ds
	mov ds, bx
	mov al, byte [edx]
	pop ds
	retn
__CPU__
.16:
%endif
	push ds
	push bx
	mov ds, bx
	mov bx, dx
	mov al, byte [bx]
	pop bx
	pop ds
	retn


		; Q command - quit.
qq:
	call guard_re
 %if _BOOTLDR
		; Test whether we are in non-DOS mode, and were
		; currently entered in protected mode. Since
		; this will make the entire operation fail,
		; it has to be checked for before modifying
		; or releasing any of the resources.
		; (Does this ever occur? No?)
	testopt [internalflags], nodosloaded
	jz .notpmnodos
%if _PM
	call ispm
  %if _TSR	; same message, reuse code
	jz .cannotpmquit
  %else
	jnz .notpmnodos_nodos
	mov dx, msg.cannotpmquit
	jmp putsz
  %endif
%endif
.notpmnodos_nodos:
	call bootgetmemorysize		; dx => behind usable memory
	mov ax, word [ boot_new_memsizekib ]
	mov cl, 6
	shl ax, cl
	cmp ax, dx			; same?
	je @F
	mov dx, msg.cannotbootquit_memsizes
	jmp .putsz
%if !_TSR || !_PM
	.putsz equ putsz
%endif

@@:
.notpmnodos:
 %endif
%if _PM
 %if _TSR
		; Test whether we are in TSR mode, and were
		; currently entered in protected mode. Since
		; this will make the entire operation fail,
		; it has to be checked for before modifying
		; or releasing any of the resources.
	testopt [internalflags], tsrmode
	jz .notpmtsr
	call ispm
	jnz .notpmtsr

; This isn't yet implemented. Broken down:
; * Uses terminate_attached_process which returns in real mode.
;  * Exception vectors are implicitly restored/discarded by that.
; * (RM) Interrupt vectors are currently restored in real mode. Unnecessary.
; * The VDD is un-registered in real mode. Necessary?
; * Normal 21.4C is used to return to the real parent.
;  * We have to discard our DOS process resources. Any DPMI TSR resources?
;  * We must again gain control in debuggee's mode after discarding them.
;  * We must return to the debuggee and seemlessly discard our memory. The
;    stack trick possibly/probably does not work in protected mode.

.cannotpmquit:
	mov dx, msg.cannotpmquit
.putsz:
	jmp putsz

.notpmtsr:
 %endif

 %if (nohook2F)&~0FF00h
  %fatal Internal flags re-ordered, adjust code here
 %endif
	mov ax, [internalflags]
 	mov al, __TEST_IMM8
	xchg al, [dpmidisable]		 	; disable DPMI hook
						; (SMC in section lDEBUG_DATA_ENTRY)
	push ax
	setopt [internalflags], nohook2F	; avoid a new hook while terminating
%endif


qq_restore_interrupts_simulated:
	xor bp, bp
%if _CATCHINT2D
.2D:
	testopt [internalflags4], dif4_int_2D_hooked
	jz .noint2D

	mov al, 2Dh		; interrupt number
	mov si, int2D		; -> IISP entry header
	mov dx, opt4_int_2D_force >> 16
	call UnhookInterruptForceSim
				; try unhooking it
	push ss
	pop es
	jnc .got2D

.not2D:
	mov word [msg.serial_cannot_unhook.int], "2D"
	mov dx, msg.serial_cannot_unhook.nowarn
	call putsz
	inc bp

.got2D:
.noint2D:
%endif


%if _CATCHINT08
.08:
	testopt [internalflags4], dif4_int_08_hooked
	jz .noint08

	mov al, 08h		; interrupt number
	mov si, intr8		; -> IISP entry header
	mov dx, opt4_int_08_force >> 16
	call UnhookInterruptForceSim
				; try unhooking it
	push ss
	pop es
	jnc .got08

.not08:
	mov word [msg.serial_cannot_unhook.int], "08"
	mov dx, msg.serial_cannot_unhook.nowarn
	call putsz
	inc bp

.got08:
.noint08:
%endif


.serial:
	testopt [internalflags4], dif4_int_serial_hooked
	jz .done_serial
	mov si, serial_interrupt_handler
	mov al, byte [serial_installed_intnum]
	mov dx, opt4_int_serial_force >> 16
	call UnhookInterruptForceSim
	push ss
	pop es
	jnc .done_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
	inc bp

.done_serial:

%if _PM
.2F:
	testopt [internalflags], hooked2F
	jz .noint2F

	mov al, 2Fh		; interrupt number
	mov si, debug2F		; -> IISP entry header
	mov dx, opt4_int_2F_force >> 16
	call UnhookInterruptForceSim
				; try unhooking it
	push ss
	pop es
	jnc .got2F

.not2F:
	mov word [msg.serial_cannot_unhook.int], "2F"
	mov dx, msg.serial_cannot_unhook.nowarn
	call putsz
	inc bp

.got2F:
.noint2F:
%endif


%if CATCHINTAMOUNT && ! _DEBUG
		; Simulate to restore interrupt vectors.
	mov si, inttab
	mov di, intforcetab
	mov cx, inttab_number
	xor dx, dx
.nextintsim:
	lodsb
	xchg ax, bx			; bl = number
	lodsw				; si -> list
	xchg ax, si			; si -> entry, ax -> list
	xchg ax, bx			; al = number, bx -> list
	push di
	mov dh, byte [di]
	call UnhookInterruptForceSim
	pop di
	push ss
	pop es
	jnc @F
	mov di, msg.serial_cannot_unhook.int
	call hexbyte
	mov dx, msg.serial_cannot_unhook.nowarn
	call putsz
	inc bp
@@:
	inc di
	xchg bx, si			; si -> list
	loop .nextintsim
%endif

	mov dx, msg.empty_message
	test bp, bp
	jnz qq_attached_unterminated.common


qq_restore_interrupts:
%if _CATCHINT2D
.2D:
	testopt [internalflags4], dif4_int_2D_hooked
	jz .noint2D

	mov al, 2Dh		; interrupt number
	mov si, int2D		; -> IISP entry header
	mov dx, opt4_int_2D_force >> 16
	call UnhookInterruptForce
				; try unhooking it
	jnc .got2D

.not2D:
	mov word [msg.serial_cannot_unhook.int], "2D"
	mov dx, msg.serial_cannot_unhook.nowarn
	jmp qq_attached_unterminated.common

.got2D:
	clropt [internalflags4], dif4_int_2D_hooked
.noint2D:
%endif


%if _CATCHINT08
.08:
	testopt [internalflags4], dif4_int_08_hooked
	jz .noint08

	mov al, 08h		; interrupt number
	mov si, intr8		; -> IISP entry header
	mov dx, opt4_int_08_force >> 16
	call UnhookInterruptForce
				; try unhooking it
	jnc .got08

.not08:
	mov word [msg.serial_cannot_unhook.int], "08"
	mov dx, msg.serial_cannot_unhook.nowarn
	jmp qq_attached_unterminated.common

.got08:
	clropt [internalflags4], dif4_int_08_hooked
	call update_inttab_optional
.noint08:
%endif


.serial:
	testopt [serial_flags], sf_init_done
	jz @F
	call serial_clean_up			; unhook interrupt
	clropt [serial_flags], sf_init_done	; clear (in case return to cmd3)
	clropt [options], enable_serial		; do not output to serial any longer
@@:
	testopt [internalflags4], dif4_int_serial_hooked
	jz .done_serial
	call serial_uninstall_interrupt_handler
	jnc .done_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
	mov byte [serial_interrupt_handler + ieEOI], 0
						; we do not issue EOI any longer
	jmp qq_attached_unterminated.common


.done_serial:

%if _PM
.2F:
	testopt [internalflags], hooked2F
	jz .noint2F

	mov al, 2Fh		; interrupt number
	mov si, debug2F		; -> IISP entry header
	mov dx, opt4_int_2F_force >> 16
	call UnhookInterruptForce
				; try unhooking it
	jnc .got2F

.not2F:
	mov word [msg.serial_cannot_unhook.int], "2F"
	mov dx, msg.serial_cannot_unhook.nowarn
	jmp qq_attached_unterminated.common

.got2F:
	clropt [internalflags], hooked2F
	clropt [internalflags4], dif4_int_2F_hooked
	call update_inttab_optional
.noint2F:
%endif


%if _BOOTLDR
	testopt [internalflags], nodosloaded
	jnz .restoreints
%endif

		; Cancel child's process if any.
		; This will drop to real mode if debuggee is in protected mode.
%if _TSR
	testopt [internalflags], tsrmode
	jz .terminate_attached

%if _PM
	call ispm
	jz @F			; in PM -->
	testopt [internalflags], canswitchmode
	jz @FF			; in 86 Mode and cannot switch to PM -->

	setopt [internalflags], modeswitched	; set flag for resetmode
	mov al, 0
	call sr_state		; save state
	call switchmode 	; switch to PM
		; ! handle_mode_changed not called here !
		; do not call InDos or other functions using seg/sels
@@:
	call pm_reset_handlers
		; ! this calls resetmode

		; remember that we cannot access Protected Mode any longer
	clropt [internalflags], canswitchmode | switchbuffer
@@:
%endif

	jmp .restoreints

.terminate_attached:
%endif

	call terminate_attached_process
	jz qq_attached_unterminated
%if _PM
	call ispm
	jnz @F

	mov dx, msg.qq_still_pm
	jmp qq_attached_unterminated.common
@@:
%endif

.restoreints:
%if CATCHINTAMOUNT && ! _DEBUG
		; Restore interrupt vectors.
	mov si, inttab
	mov di, intforcetab
	mov cx, inttab_number
	xor dx, dx
.nextint:
	lodsb
	xchg ax, bx			; bl = number
	lodsw				; si -> list
	xchg ax, si			; si -> entry, ax -> list
	xchg ax, bx			; al = number, bx -> list
	push di
	mov dh, byte [di]
	call UnhookInterruptForce
	pop di
	inc di
	xchg bx, si			; si -> list
	loop .nextint
%endif


%if _PM
	pop ax					; (discard)
%endif


qqlate:
		; Release the registered VDD.
%if _VDD
	testopt [internalflags], ntpacket
	jz .novdd
	mov ax, word [hVdd]
	UnRegisterModule
.novdd:
%endif

		; Restore termination address.
%if _BOOTLDR
	testopt [internalflags], nodosloaded
	jnz .bootterminate	; terminate -->
%endif
%if _TSR
	testopt [internalflags], tsrmode
	jz .nontsrterminate
	xor si, si
	call guard_auxbuff
	mov es, word [auxbuff_segorsel]
	xor di, di
	xor ax, ax
	mov cx, 8
	rep stosw		; 10h MCB bytes
	mov cx, 40h
	rep movsw		; 80h PSP bytes
	mov ax, es
	inc ax
	mov word [es:1], ax	; fake MCB
	push ds
	mov ds, ax
	mov word [34h], 18h
	mov word [36h], ax	; insure default PHT and fix segment
	mov word [32h], 1	; only one PHT entry (zero might crash)
	mov byte [18h], -1	; PHT entry is closed
	mov word [2Ch], 0	; PSP clear
	call .setparent		; make it self-owned, just in case
	mov bx, ss
	dec bx
	mov ds, bx		; => our (real) MCB
	mov word [1], ax	; parent = fake PSP
	pop ds
	call .setparent		; make the fake PSP our parent
	jmp short terminate_00	; see ya

.nontsrterminate:
%endif
	mov si, psp22		; restore termination address
	mov di, TPIV
	movsw
	movsw
	mov di, 16h		; restore PSP of parent
	movsw
		; Really done.
	mov ah, 4Ch		; quit
	mov al, byte [termcode]	; return code
	int 21h


terminate_00:	; used by terminate_attached_process
	mov ax, 4C00h		; quit
	int 21h


qq_attached_unterminated:
	mov dx, msg.qq_unterm

.common:
		; Restore state:
%if _PM
 %if (nohook2F)&~0FF00h
  %fatal Internal flags re-ordered, adjust code here
 %endif
 	pop ax
	mov [dpmidisable], al	; (SMC in section lDEBUG_DATA_ENTRY)
	and ah, nohook2F>>8
	clropt [internalflags], nohook2F
	or [internalflags+1], ah
%endif
	jmp putsz


	usesection lDEBUG_DATA_ENTRY

%if _TSR
qq.proceedtsrtermination:
	cli
	cld
	mov ax, cs
	mov ds, ax
	mov ss, ax
	mov sp, stack_end
	sti
	sub word [reg_esp], 2+4+((qq.tsrfreecode_size+1)&~1)
	mov di, word [reg_esp]	; -> stack frame
	mov es, word [reg_ss]
	mov ax, word [reg_ds]
	stosw			; debuggee's ds
	mov ax, word [reg_eip]
	stosw
	mov ax, word [reg_cs]
	stosw			; debuggee's cs:ip
	push es
	push di
	mov si, qq.tsrfreecode
	mov cx, ((qq.tsrfreecode_size+1)>>1)
	rep movsw		; code on stack
	mov ax, cs
	dec ax
	mov word [reg_ds], ax	; = our MCB
	pop word [reg_eip]
	pop word [reg_cs]	; -> code on stack
	testopt [options3], opt3_tsr_quit_leave_tf
	jnz @F
	clropt [reg_efl], 100h	; clear TF
@@:

	call entry_to_code_seg
	dw .proceedtsrcode


	usesection lDEBUG_CODE

	code_insure_low_byte_not_0CCh
.proceedtsrcode:
%if _DEBUG
		; avoid hooking interrupts again:
	mov byte [cs:..@patch_tsr_quit_run], __JMP_REL16
				; (SMC in section lDEBUG_CODE)
%endif
	jmp run			; run this


	usesection lDEBUG_DATA_ENTRY

	align 2, db 0
	; (Update: Explicitly clears TF now, except if the
	; option opt3_tsr_quit_leave_tf is set. See above.)
	;
	; Note that since we are in control of debuggee's TF and
	; reset it every time the debugger is entered, this code
	; will not be entered with TF set. It might be entered
	; with IF set and an interrupt might occur; the only harm
	; done then is that the interrupt handler has less stack
	; available. All flags must be preserved by this code.
qq.tsrfreecode:
	mov word [1], 0		; free the MCB
	pop ds			; restore debuggee's ds
	retf ((qq.tsrfreecode_size+1)&~1)	; jump
qq.tsrfreecode_size: equ $-qq.tsrfreecode


	usesection lDEBUG_CODE

qqlate.setparent:
	mov word [16h], ax
	mov word [0Ah], qq.proceedtsrtermination
	mov word [0Ah+2], ss
	retn
%endif

%if _BOOTLDR
qqlate.bootterminate:
	sub word [reg_esp], 2*8+4+((qq.bootfreecode_size+1)&~1)
	mov di, word [reg_esp]	; -> stack frame
	mov es, word [reg_ss]
	mov ax, word [reg_ds]
	stosw
	mov ax, word [reg_es]
	stosw
	mov ax, word [reg_esi]
	stosw
	mov ax, word [reg_edi]
	stosw
	mov ax, word [reg_eax]
	stosw
	mov ax, word [reg_ecx]
	stosw
	mov ax, word [reg_ebx]
	stosw
	mov ax, word [reg_edx]
	stosw
	mov ax, word [reg_eip]
	stosw
	mov ax, word [reg_cs]
	stosw			; debuggee's cs:ip
	push es
	push di
	 push ds
	  push cs
	  pop ds		; => lDEBUG_CODE
	mov si, qq.bootfreecode
	mov cx, ((qq.bootfreecode_size+1)>>1)
	rep movsw		; code on stack
	 pop ds

	 push ss
	 pop es

	mov ax, word [ boot_new_memsizekib ]
	mov cl, 6
	shl ax, cl		; ax => source of EBDA (new position)
	mov dx, word [ boot_old_memsizekib ]
	shl dx, cl		; dx => destination of EBDA (old position)
	xor cx, cx		; size of EBDA to move (if none)
	push ds
	mov ds, cx
	mov bx, word [40Eh]	; new ref in word [0:40Eh] (if none)
	pop ds
	cmp byte [ boot_ebdaflag ], 0	; any EBDA ?
	jz .noebda

	push ds
	mov ds, ax		; => EBDA
	xor bx, bx
	mov bl, byte [ 0 ]	; EBDA size in KiB
	mov cl, 6
	shl bx, cl		; *64, to paragraphs
	mov cx, bx		; = size of EBDA to move (in paragraphs)
	mov bx, dx		; = new EBDA reference to put in word [0:40Eh]
	pop ds

.noebda:
	mov word [reg_eax], ax	; => relocated (new) EBDA position
				;  (in front of debugger image)
	mov word [reg_ebx], bx	; = what to put in word [0:40Eh],
				;  unchanged content of that word if no EBDA
	mov word [reg_ecx], cx	; = EBDA size, 0 if no EBDA
	mov word [reg_edx], dx	; = original (old) EBDA position
				; = original mem size (in paras)
				;  (behind/in debugger image)
	mov word [reg_ds], 0

	pop word [reg_eip]
	pop word [reg_cs]	; -> code on stack
	testopt [options3], opt3_tsr_quit_leave_tf
	jnz @F
	clropt [reg_efl], 100h	; clear TF
@@:
	; call dumpregs
%if _DEBUG
		; avoid hooking interrupts again:
	mov byte [cs:..@patch_tsr_quit_run], __JMP_REL16
				; (SMC in section lDEBUG_CODE)
%endif
	; jmp cmd3
	jmp run			; run this


	align 2, db 0
qq.bootfreecode:
	pushf
	call movp		; move EBDA back (if any)
	mov word [40Eh], bx	; back relocate EBDA (if any)
	mov cl, 6
	shr dx, cl		; = to KiB
	mov word [413h], dx	; back relocate mem size
	popf
	pop ds
	pop es
	pop si
	pop di
	pop ax
	pop cx
	pop bx
	pop dx
	retf ((qq.bootfreecode_size+1)&~1)


		; 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!
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
		; Refer to comment in init.asm init_movp.

%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
qq.bootfreecode_size: equ $-qq.bootfreecode
%endif


sleepcmd:
	call skipcomma
	call getdword
	push bx
	push dx
	call skipwh0
	call iseol?
	je .seconds
	dec si
	mov dx, msg.seconds
	call isstring?
	je .seconds_check_eol
	mov dx, msg.ticks
	call isstring?
.error_NZ:
	jne error
.ticks_check_eol:
	lodsb
	call chkeol

	mov ax, 1
	jmp .common

.seconds_check_eol:
	lodsb
	call chkeol
.seconds:
	mov ax, 18
.common:
	pop dx
	push ax
	mul dx			; dx:ax = low word times multiplier
	mov bx, dx
	mov cx, ax		; bx:cx = low word times multiplier
	pop ax
	pop dx
	mul dx			; dx:ax = high word times multiplier
	add bx, ax
	adc dx, 0		; dx:bx:cx = entire result
	jnz .error_NZ

	test cx, cx
	jnz @F
	test bx, bx
	jz .end
@@:

	mov ax, 40h		; bimodal segment/selector
	mov es, ax
.loop_reset:
	mov dx, word [es:6Ch]
.loop:
	cmp dx, word [es:6Ch]
	jne .next
	call handle_serial_flags_ctrl_c
	testopt [options3], opt3_check_ctrlc_0bh
	jnz @F			; already called function 0Bh -->
	call InDos
	jnz @F
	mov ah, 0Bh
	doscall			; allow to break with Ctrl-C
@@:
	call idle
	jmp .loop

.next:
	sub cx, 1
	sbb bx, 0
	jnz .loop_reset
	jcxz .end
	jmp .loop_reset

.end:
	retn


		; S command - search for a string of bytes.
sss:
	dec si
	dec si			; -> at 'S'
	mov dx, msg.sleep
	call isstring?		; check for "SLEEP"
	je sleepcmd
	inc si			; skip 'S'
	lodsb			; load next

	clropt [internalflags3], dif3_sss_is_reverse

	mov bx, word [reg_ds]	; get search range
	_386_PM_o32		; xor ecx, ecx
	xor cx, cx
	call getrangeX		; get address range into BX:(E)DX..BX:(E)CX
	call skipcomm0
	_386_PM_o32		; push edx
	push dx
	_386_PM_o32		; push ecx
	push cx
	push bx

	mov dx, msg.reverse
	dec si
	call isstring?
	jne @F

	setopt [internalflags3], dif3_sss_is_reverse
	call skipwhite
	dec si
@@:
	mov dx, msg.range
	call isstring?
	lodsb
	jne .notrange

	mov bx, word [reg_ds]	; get search range
	xor cx, cx
	call getrangeX		; try to get second range
	call chkeol		; and insure end-of-line
				; successful if it returned
	_386_PM_o32		; mov esi, edx
	mov si, dx		; bx:esi-> source string
	_386_PM_o32		; sub ecx, edx
	sub cx, dx		; ecx = count - 1
	jmp short .setesedi

.notrange:
	call getstr		; get string of bytes
	sub di, line_out	; di = number of bytes to look for
	jz error
	mov cx, di
	dec di			;     minus one
	mov si, line_out
	push di
	call guard_auxbuff
	mov es, word [auxbuff_segorsel]
	xor di, di
	rep movsb		; move to auxbuff
	_386_PM_o32	; xor esi, esi
	xor si, si
	mov bx, es		; bx:esi -> auxbuff
	pop cx
_386_PM	movzx ecx, cx		; ecx = count - 1
.setesedi:
	push ss
	pop es
	mov di, search_results
	xor ax, ax
	mov word [sscounter], ax
	mov word [sscounter + 2], ax
	push cx
%if _PM
	mov cx, (6 * 16) >> 1
%else
	mov cx, (4 * 16) >> 1
%endif
	rep stosw
	pop cx

	call prephack		; set up for the interrupt vector hack
	call dohack
	mov ds, bx
	pop es
	_386_PM_jmpn .386init	; 386 -->
.init:
	pop bx
	pop dx
.init_popped:
	sub bx, dx		; bx = number of bytes in search range minus one
	sub bx, cx		; = number of possible positions of string minus 1
	jb error
	mov di, dx
	mov dx, cx
	mov cx, bx

		; ds:si-> search string, length (dx+1)
		; es:di-> data to search in, (cx+1) bytes
	testopt [ss:internalflags3], dif3_sss_is_reverse
	jnz .reverse
.loop:
	or al, 1		; NZ (iff cx==0, repne scasb doesn't change ZF)
	push si
	lodsb			; first character in al
	repne scasb		; look for first byte
	je .foundbyte
	scasb			; count in cx was cnt-1
	jne .done
.found_last_byte:
	call .handle_found_byte
	jmp .done

.foundbyte:
	call .handle_found_byte
	pop si
	jmp .loop		; cx = 0 if one to search,
				;  cx = 1 if two to search, etc

.reverse:
	add di, cx		; -> last position to check
.reverseloop:
	or al, 1		; NZ (iff cx==0, repne scasb doesn't change ZF)
	push si
	lodsb			; first character in al
	std			; no AMD erratum workaround needed
	repne scasb		; look for first byte
	je .reversefoundbyte
	scasb			; count in cx was cnt-1
	jne .done
.reversefound_last_byte:
	cld
	add di, 2
	cmp al, al		; ZR for case if dx = 0
	call .handle_found_byte
	sub di, 2
	jmp .done

.reversefoundbyte:
	cld
	add di, 2
	cmp al, al		; ZR for case if dx = 0
	call .handle_found_byte
	sub di, 2
	pop si
	jmp .reverseloop	; cx = 0 if one to search,
				;  cx = 1 if two to search, etc

.done:
	pop si			; discard
.commondone:
	cld
	push ss
	pop ds
	call unhack
	mov di, line_out

	mov ax, word [sscounter + 2]
	test ax, ax
	jz .nohighcounter
	call hexword
.nohighcounter:

	mov ax, word [sscounter]
	call hexword
	call putsline
	mov dx, msg.matches
	jmp putsz


		; INP:	ZR
.handle_found_byte:
	push cx
	push di
	mov cx, dx
	repe cmpsb		; compare string behind first byte
		; If we're searching for a single-byte value then
		;  dx is equal to zero here. In that case cx gets
		;  the value zero and then repe cmpsb does not
		;  alter ZF, meaning it will stay ZR (as noted for
		;  the comment INP section).
	pop di
	je .display		; if equal
.next:
	pop cx
	retn

.display:
	mov bx, es
	push di
	push ds
	push es
	 push ss
	 pop ds
	call unhack		; undo the interrupt vector hack and restore es
	push di
	cmp word [sscounter + 2], 0
	jne @F
	mov di, word [sscounter]
	cmp di, 16
	jae @F
%if _PM
	add di, di		; * 2
	mov ax, di
	add di, di		; * 4
	add di, ax		; * 4 + * 2 = * 6
%else
	add di, di
	add di, di		; * 4
%endif
	add di, search_results
	pop ax
	push ax
	dec ax
	stosw
%if _PM
	xor ax, ax
	stosw
%endif
	mov ax, bx
	stosw

@@:
	add word [sscounter], 1
	adc word [sscounter + 2], 0
	mov ax, bx
	mov di, line_out
	call hexword		; 4 (segment)
	mov al, ':'
	stosb			; +1=5
	pop ax
	dec ax
	call hexword
%if _SDUMP
	testopt [options], ss_no_dump
	jnz @F
	stc
	adc ax, dx		; -> behind result
	jbe .noresult		; end of segment
	mov si, ax
	mov ax, 32<<8|32
	stosw
	lea bx, [di+3*16]
	mov cx, si
	neg cx
	cmp cx, byte 16
	jbe .cxdone
	mov cx, 16
.cxdone:
	 pop ds
	 push ds		; restore search's segment
	push cx
.disploop:
	lodsb
	call dd_store
	mov al, 32
	stosb
	loop .disploop
	pop cx
	 push ss
	 pop ds
	neg cx
	add cx, byte 16
	jz .noblanks
.loopblanks:
	mov ax, 32<<8|32
	stosw
	stosb
	loop .loopblanks
.noblanks:
	mov byte [di-(1+(8*3))], '-'
	mov di, bx
.noresult:
@@:
%endif	; _SDUMP
	push dx
	call putsline_crlf
	call dohack
	pop dx
	pop es
	pop ds
	pop di
	jmp .next

%if _PM
.386init:
[cpu 386]
	pop ebx
	pop edx
	call ispm
	jnz .init_popped	; not PM -->
	sub ebx, edx		; ebx = number of bytes in search range minus one
	sub ebx, ecx		; = number of possible positions of string minus 1
	jb error
	mov edi, edx
	mov edx, ecx
	mov ecx, ebx

		; ds:esi-> search string, length (edx+1)
		; es:edi-> data to search in, (ecx+1) bytes
		; Although 386+ RM still uses 64 KiB segments, it allows
		; us to use the 32-bit addressing variant of the string
		; instructions as long as we never access any byte above
		; the 64 KiB limit. (Even if the index register contains
		; 00010000h after an instruction executed.)
	testopt [ss:internalflags3], dif3_sss_is_reverse
	jnz .386reverse
.386loop:
	or al, 1		; NZ (iff cx==0, repne scasb doesn't change ZF)
	push esi
	a32 lodsb		; first character in al
	a32 repne scasb		; look for first byte
	je .386foundbyte
	a32 scasb		; count in ecx was cnt-1
	jne .386done
.386found_last_byte:
	call .386handle_found_byte
	jmp .386done

.386foundbyte:
	call .386handle_found_byte
	pop esi
	jmp .386loop		; ecx = 0 if one to search,
				;  ecx = 1 if two to search, etc

.386reverse:
	add edi, ecx		; -> last position to check
.386reverseloop:
	or al, 1		; NZ (iff cx==0, repne scasb doesn't change ZF)
	push esi
	a32 lodsb		; first character in al
	std			; no AMD erratum workaround needed
	a32 repne scasb		; look for first byte
	je .386reversefoundbyte
	a32 scasb		; count in ecx was cnt-1
	jne .386done
.386reversefound_last_byte:
	cld
	add edi, 2
	cmp al, al		; ZR for case if edx = 0
	call .386handle_found_byte
	sub edi, 2
	jmp .386done

.386reversefoundbyte:
	cld
	add edi, 2
	cmp al, al		; ZR for case if edx = 0
	call .386handle_found_byte
	sub edi, 2
	pop esi
	jmp .386reverseloop	; ecx = 0 if one to search,
				;  ecx = 1 if two to search, etc

.386done:
	pop esi			; discard
	jmp .commondone


		; INP:	ZR
.386handle_found_byte:
	push ecx
	push edi
	mov ecx, edx
	a32 repe cmpsb		; compare string behind first byte
		; If we're searching for a single-byte value then
		;  edx is equal to zero here. In that case ecx gets
		;  the value zero and then a32 repe cmpsb does not
		;  alter ZF, meaning it will stay ZR (as noted for
		;  the comment INP section).
	pop edi
	je .386display		; if equal
.386next:
	pop ecx
	retn

.386display:
	mov bx, es
	push edi
	push ds
	push es
	 push ss
	 pop ds
	call unhack		; undo the interrupt vector hack and restore es
	push edi
	mov edi, dword [sscounter]
	cmp edi, 16
	jae @F
	add di, di		; * 2
	mov ax, di
	add di, di		; * 4
	add di, ax		; * 4 + * 2 = * 6
	add di, search_results
	pop eax
	push eax
	dec eax
	stosd
	mov ax, bx
	stosw

@@:
	inc dword [sscounter]
	mov ax, bx
	mov di, line_out
	call hexword		; 4 (segment)
	mov al, ':'
	stosb			; +1=5
	pop eax
	dec eax
	call testattrhigh
	jz .noa32
	call hexword_high
.noa32:
	call hexword
%if _SDUMP
	testopt [options], ss_no_dump
	jnz @F
	stc
	adc eax, edx		; -> behind result
	jbe .386noresult	; end of segment
	mov esi, eax
	mov ax, 32<<8|32
	stosw
	lea bx, [di+3*16]
	mov ecx, esi
	neg ecx
	cmp ecx, byte 16
	jbe .386cxdone
	mov cx, 16
.386cxdone:
	 pop ds
	 push ds		; restore search's segment
	push cx
.386disploop:
	a32 lodsb
	call dd_store
	mov al, 32
	stosb
	loop .386disploop
	pop cx
	 push ss
	 pop ds
	neg cx
	add cx, byte 16
	jz .386noblanks
.386loopblanks:
	mov ax, 32<<8|32
	stosw
	stosb
	loop .386loopblanks
.386noblanks:
	mov byte [di-(1+(8*3))], '-'
	mov di, bx
.386noresult:
@@:
%endif	; _SDUMP
	push dx
	call putsline_crlf
	call dohack
	pop dx
	pop es
	pop ds
	pop edi
	jmp .386next
__CPU__
%endif	; _PM


%if 0
getdebuggeebyte:
	push bp
	mov bp, sp
	sub sp, byte 4
	push bx
	push cx
%define _dedata -4
%define _bp 0
%define _ip 2
%define _adroffset 4
%define _adrsegment 8
	test byte [], memorydump
	jz .realmemory

	jmp short .return
.realmemory32:
.realmemory:
	mov ax, word [ bp + _adrsegment ]
	mov bx, word [ bp + _adroffset ]
	push ds
	mov ds, ax
	push word [ bx ]
	pop word [ bp + _dedata ]
	push word [ bx +2 ]
	pop word [ bp + _dedata +2 ]
	pop ds
;	test ax, ax
;	jnz .return
	mov dx, ax
	mov cl, 4
	shl ax, cl
	mov cl, 12
	shr dx, cl
	add ax, bx
	adc dx, byte 0
	jnz .return
	sub ax, 23h*4
	jb .return
	cmp ax, 2*4
	jae .return

	push ds
	xor bx, bx
	mov ds, bx
	push si
	push di
	mov si, 22h*4
	mov di, hackints.dummy22
	movsw
	movsw
	mov bl, 8
	add si, bx
	add di, bx
	movsw
	movsw

	mov cl, byte [ bx - 4 + hackints2324 ]
	mov byte [ bp + _dedata ], cl
.return:
	pop cx
	pop bx
	pop ax
	pop dx
	pop bp
	retn 6


		; Interrupt hack table
		;
		; This contains the Int23 and Int24 handler we want to show
		; the user. As we'll retrieve a dword per access,
	align 4, db 0
hackints:
.dummy22:	dd 0
.23:		dd 0
.24:		dd 0
.dummy25:	dd 0
%endif


lockdrive:
	push ax
	push bx
	push cx
	push dx
	mov bl, al
	inc bl
	mov bh, 0
	mov cx, 084Ah
	mov dx, 0001h
	mov ax, 440Dh
	int 21h
	pop dx
	pop cx
	pop bx
	pop ax
	retn

unlockdrive:
	push ax
	push bx
	push cx
	push dx
	mov bl, al
	inc bl
	mov bh, 0
	mov cx, 086Ah
	mov dx, 0001h
	mov ax, 440Dh
	int 21h
	pop dx
	pop cx
	pop bx
	pop ax
	retn


		; W command - write a program, or disk sectors, to disk.
ww:
%if _BOOTLDR
	testopt [internalflags], nodosloaded
	jz @F
	mov dx, msg.nobootsupp
	jmp putsz
@@:
%endif

	call parselw		; parse L and W argument format
	jz ww4			; if request to write program
%if _PM && _NOEXTENDER
	call ispm
	jnz .rm
	call isextenderavailable
	jc nodosextinst
.rm:
%endif
	testopt [ss:internalflags], newpacket| ntpacket
	jz .oldint
	mov dl, al		; A: = 0, ...
	mov si, 6001h		; write, assume "file data"
%if _VDD
	testopt [internalflags], ntpacket
	jnz .vdd
%endif
	inc dl			; A: = 1, ...
	call lockdrive
	mov ax, 7305h		; ds:(e)bx-> packet
	stc
	int 21h			; use int 21h here, not doscall
	pushf
	call unlockdrive
	popf
	jmp short .done
%if _VDD
.vdd:
	mov ax, word [hVdd]
	mov cx, 5
%if _PM
	add cl, byte [dpmi32]
%endif
	DispatchCall
	jmp short .done
%endif
.oldint:
	int 26h
.done:
	mov dx, writing
ww1:
	mov bx, ss		; restore segment registers
	mov ds, bx
	mov sp, word [savesp]
	mov es, bx
	jnc ww3			; if no error
	cmp al, 0Ch
	jbe ww2			; if in range
	mov al, 0Ch
ww2:
	cbw			; ah = 0
	mov bx, dskerrs		; -> byte table
	xlatb			; get offset from dskerrs
	add ax, bx		; -> message
	mov di, line_out
	mov si, ax
	call showstring
	mov si, dx
	call showstring
	mov si, drive
	call showstring
	call putsline_crlf
ww3:
	jmp cmd3		; can't return because Int26 leaves stack wrong

;	Write to file.  First check the file extension.
;   size of file is in client's BX:CX,
;   default start address is DS:100h

ww4:
	call InDos
	jnz not_while_indos

	mov al, byte [fileext]	; get flags of file extension
	test al, EXT_EXE + EXT_HEX
	jz ww5			; if not EXE or HEX
	mov dx, nowhexe
	jmp short ww6

ww5:
	cmp al, 0
	jnz ww7			; if extension exists
	mov dx, nownull
ww6:
	jmp ww16

		; File extension is OK; write it.  First, create the file.
ww7:
%if _PM
	call ispm
	jnz ww7_1
	mov dx, nopmsupp
	jmp putsz
ww7_1:
%endif
	mov bp, line_out
	cmp dh, 0FEh
	jb ww8			; if (dx < 0xFE00)
	sub dh, 0FEh		;  dx -= 0xFE00;
	add bx, 0FE0h
ww8:
	mov word [bp+10], dx	; save lower part of address in line_out+10
	mov si, bx		; upper part goes into si
	mov ah, 3Ch		; create file
	xor cx, cx		; no attributes
	mov dx, DTA
	doscall
	jc ww15			; if error
	push ax			; save file handle

		; Print message about writing.
	mov dx, wwmsg1
	call putsz		; print string
	mov ax, word [reg_ebx]
	cmp ax, 10h
	jb ww9			; if not too large
	xor ax, ax		; too large:  zero it out
ww9:
	mov word [bp+8], ax
	test ax, ax
	jz ww10
	call hexnyb
ww10:
	mov ax, word [reg_ecx]
	mov word [bp+6], ax
	call hexword
	call putsline		; print size
	mov dx, wwmsg2
	call putsz		; print string

		; Now write the file.  Size remaining is in line_out+6.
	pop bx			; recover file handle
	mov dx, word [bp+10]	; address to write from is si:dx
ww11:
	mov ax, 0FE00h
	sub ax, dx
	cmp byte [bp+8], 0
	jnz ww12		; if more than 0FE00h bytes remaining
	cmp ax, word [bp+6]
	jb ww12			; ditto
	mov ax, word [bp+6]
ww12:
	xchg ax, cx		; mov cx, ax
	mov ds, si
	mov ah, 40h		; write to file
	int 21h			; use INT, not doscall
	push ss			; restore DS
	pop ds
	cmp ax, cx
	jne ww13		; if disk full
	xor dx, dx		; next time write from xxxx:0
	add si, 0FE0h		; update segment pointer
	sub word [bp+6], cx
	lahf
	sbb byte [bp+8], 0
	jnz ww11		; if more to go
	sahf
	jnz ww11		; ditto
	jmp short ww14		; done

ww13:
	mov dx, diskful
	call putsz		; print string
	call ww14		; close file

	mov ah, 41h		; unlink file
	mov dx, DTA
	doscall
	retn

		; Close the file.
ww14:
	mov ah, 3Eh		; close file
	int 21h
	retn

		; Error opening file.  This is also called by the load command.
ww15:
	cmp ax, byte 2
	mov dx, doserr2		; File not found
	je ww16
	cmp ax, byte 3
	mov dx, doserr3		; Path not found
	je ww16
	cmp ax, byte 5
	mov dx, doserr5		; Access denied
	je ww16
	cmp ax, byte 8
	mov dx, doserr8		; Insufficient memory
	je ww16
	mov di, openerr1
	call hexword
	mov dx, openerr		; Error ____ opening file
ww16:
	jmp putsz


%ifn _EMS
xx: equ error
%else
		; X commands - manipulate EMS memory.
		;
		; Reference:
		;  http://www.nondot.org/sabre/os/files/MemManagement/LIMEMS41.txt

xx:	cmp al, '?'
	je xhelp		; if a call for help
	or al, TOLOWER
	cmp al, 'a'
	je xa			; if XA command
	cmp al, 'd'
	je xd			; if XD command
	cmp al, 'm'
	je xm			; if XM command
	cmp al, 'r'
	je xr			; if XR command
	cmp al, 's'
	je xs			; if XS command
	jmp error

xhelp:	lodsb
	call chkeol
	mov dx, msg.xhelp
	jmp putsz		; print string and return

		; XA - Allocate EMS.
xa:	call emschk
	call skipcomma
	call getword		; get argument into DX
	call chkeol		; expect end of line here
	mov bx, dx

	mov ax, 5A00h		; use the EMS 4.0 version to alloc 0 pages
	test bx, bx
	jz short .nullcnt
	mov ah, 43h		; allocate handle
.nullcnt:
	call emscall
	xchg ax, dx		; mov ax, dx
	mov di, xaans1
	call hexword
	mov dx, xaans
	jmp putsz		; print string and return

		; XD - Deallocate EMS handle.
xd:	call emschk
	call skipcomma
	call getword		; get argument into DX
	call chkeol		; expect end of line here

	mov ah, 45h		; deallocate handle
	call emscall
	xchg ax, dx		; mov ax,dx
	mov di, xdans1
	call hexword
	mov dx, xdans
	jmp putsz		; print string and return

		; XR - Reallocate EMS handle.
xr:	call emschk
	call skipcomma
	call getword		; get handle argument into DX
	mov bx, dx
	call skipcomm0
	call getword		; get count argument into DX
	call chkeol		; expect end of line here
	xchg bx, dx

	mov ah, 51h		; reallocate handle
	call emscall
	mov dx, xrans
	jmp putsz		; print string and return

		; XM - Map EMS memory to physical page.
xm:	call emschk
	call skipcomma
	call getword		; get logical page (FFFFh means unmap)
	mov bx, dx		; save it in BX
	call skipcomm0
	call getbyte		; get physical page (DL)
	push dx
	call skipcomm0
	call getword		; get handle into DX
	call chkeol		; expect end of line
	 pop ax			; recover physical page into AL
	 push ax
	mov ah, 44h		; function 5 - map memory
	call emscall
	mov di, xmans1
	xchg ax, bx		; mov ax, bx
	call hexword
	mov di, xmans2
	pop ax
	call hexbyte
	mov dx, xmans
	jmp putsz		; print string and return

		; XS - Print EMS status.
xs:
	call emschk
	lodsb
	call chkeol		; no arguments allowed

		; First print out the handles and handle sizes.  This can be done either
		; by trying all possible handles or getting a handle table.
		; The latter is preferable, if it fits in memory.
	mov ah, 4Bh		; function 12 - get handle count
	call emscall
	cmp bx, (line_out_end-line_out)/4
	jbe short xs3			; if we can do it by getting the table
	xor dx, dx		; handle

xs1:
		; try EMS 4.0 function 5402h to get total number of handles
	mov ax, 5402h
	call emscall.witherrors
	mov cx, bx		; cx = number of handles
	jz @F

	mov cx, 0FFh		; total number of handles (assumed)
				;  this does not match the prior code here,
				;  which used 100h handles assuming that
				;  0FFh is the last valid handle number.
				; however, if we assume that there are 0FFh
				;  valid handles then the last number is 0FEh!
@@:

	mov ah, 4Ch		; function 13 - get handle pages
	call emscall.witherrors
	jnz short .err
	xchg ax, bx		; mov ax,bx
	call hndlshow
.cont:
	inc dx			; increment handle number to access
	jz @F			; (if 0000h handles, do not loop forever)
	cmp dx, cx		; end of the loop ?
	jb short xs1		; if more to be done -->
@@:
	jmp short xs5		; done with this part

.err:
	cmp ah, 83h		; no such handle?
	je short .cont		; just skip -->
	jmp emscall.errorhandle	; if other error -->

		; Get the information in tabular form.
xs3:
	mov ah, 4Dh		; function 14 - get all handle pages
	mov di, line_out
	call emscall
	test bx, bx
	jz short xs5
	mov si, di
xs4:
	lodsw
	xchg ax, dx
	lodsw
	call hndlshow
	dec bx
	jnz short xs4		; if more to go

xs5:
	mov dx, crlf
	call putsz		; print string

		; Next print the mappable physical address array.
		; The size of the array shouldn't be a problem.
	mov ax, 5800h		; function 25 - get mappable phys. address array
	mov di, line_out	; address to put array
	call emscall
	mov dx, xsnopgs
	jcxz xs7		; NO mappable pages!

	mov si, di
xs6:
	push cx
	lodsw
	mov di, xsstr2b
	call hexword
	lodsw
	mov di, xsstr2a
	call hexbyte
	mov dx, xsstr2
	call putsz		; print string
	pop cx			; end of loop
	test cl, 1
	jz short xs_nonl
	mov dx, crlf		; blank line
	call putsz		; print string
xs_nonl:
	loop xs6
	mov dx, crlf		; blank line
xs7:
	call putsz		; print string

		; Finally, print the cumulative totals.
	mov ah, 42h		; function 3 - get unallocated page count
	call emscall
	mov ax, dx		; total pages available
	sub ax, bx		; number of pages allocated
	mov bx, xsstrpg
	call sumshow		; print the line
	mov ah, 4Bh		; function 12 - get handle count
	call emscall
	push bx			; number of handles allocated

		; try EMS 4.0 function 5402h to get total number of handles
	mov ax, 5402h
	call emscall.witherrors	; don't use emscall, this function may fail!
	mov dx, bx
	jz @F

	mov dx, 0FFh		; total number of handles
@@:
	pop ax			; ax = number of handles allocated
	mov bx, xsstrhd
	jmp sumshow		; print the line

		; Call EMS
emscall:
	call .witherrors
	jz short .ret		; return if OK
.errorhandle:
	mov al, ah
	cmp al, 8Bh
	jg short .ce2		; if out of range (signed comparison intended)
	cbw
	mov bx, ax
	shl bx, 1
	mov dx, word [emserrs+100h+bx]
	test dx, dx
	jnz short .ce4		; if there's a word there
.ce2:
	mov dx, emserrx
	call putsz
	mov di, line_out
	call hexbyte
	call putsline_crlf
	jmp cmd3		; quit

.witherrors:
%if _PM
	call ispm
	jnz short .rm
subcpu 286
	push word [ss:pspdbg]
	push 67h
	call intcall
	db __TEST_IMM16		; (skip int opcode)
subcpureset
.rm:
%endif
	int 67h
	test ah, ah
.ret:
emschk.ret:
	retn


		; Check for EMS
		; maybe should disable this while bootloaded ?
emschk:
	mov al, 67h
	call intchk		; ZR if offset = -1 or segment = 0
				; CHG: ax, dx, bx
	jz .failed
	mov ah, 46h
	call emscall.witherrors	; get version
	jz short .ret		; success -->
.failed:
	mov dx, emsnot
emscall.ce4:
	jmp prnquit		; otherwise abort with message -->

		; HNDLSHOW - Print XS line giving the handle and pages allocated.
		;
		; Entry	DX	Handle
		;	AX	Number of pages
		;
		; Exit	Line printed
		;
		; Uses	ax, di
hndlshow:
	mov di, xsstr1b
	call hexword
	mov ax, dx
	mov di, xsstr1a
	call hexword
	push dx
	mov dx, xsstr1
	call putsz		; print string
	pop dx
	retn

		; SUMSHOW - Print summary line for XS command.
		;
		; Entry	AX	Number of xxxx's that have been used
		;	DX	Total number of xxxx's
		;	BX	Name of xxxx
		;
		; Exit	String printed
		;
		; Uses	AX, CX, DX, DI
sumshow:
	mov di, xsstr3
	push di
	call trimhex
	xchg ax, dx		; mov ax,dx
	mov di, xsstr3a
	call trimhex
	pop dx			; mov dx,xsstr3
	call putsz		; print string
	mov dx, bx
	call putsz		; print string
	mov dx, xsstr4
	jmp putsz		; print string and return

		; TRIMHEX - Print word without leading zeroes.
		;
		; Entry	AX	Number to print
		;	DI	Where to print it
		;
		; Uses	AX, CX, DI.
trimhex:
	call hexword
	sub di, 4		; back up DI to start of word
	mov cx, 3
	mov al, '0'
.loop:
	scasb
	jne .done		; return if not a '0'
	mov byte [di-1], ' '
	loop .loop
.done:
	retn
%endif	; _EMS

		; Error handlers.
error:
	push ss
	pop es
	push ss
	pop ds
	mov cx, si
	sub cx, line_in+3
	cmp cx, 256
	ja .invalid
	add cx, word [promptlen]; number of spaces to skip
	db __TEST_IMM16		; (skip xor)
.invalid:
	xor cx, cx		; if we're really messed up
	mov sp, [throwsp]
	jmp near [throwret]
		; INP:	cx = number of spaces to indent

		; This is the default address in throwret.
		; Display the error, then jump to errret.
errhandler:
	call get_columns	; ax = columns
.:
	sub cx, ax
	jnc .
	add cx, ax
	jz err2
	mov al, 32
.loop:
	call putc
	loop .loop
err2:
	mov dx, errcarat
	call putsz		; print string
	mov ax, 01FFh
	call setrc
	mov word [lastcmd], dmycmd
				; cancel command repetition
	jmp near [errret]	; return to the prompt (cmd3, aa01)


setrc:
	cmp word [rc], 0
	jne .ret
	mov word [rc], ax
.ret:
	retn


		; Terminate the attached process, if any
		;
		; OUT:	NZ if now no process attached
		;	ZR if still a process attached,
		;	 ie we failed to terminate this one
terminate_attached_process:
	testopt [internalflags], attachedterm
	jnz @F

	clropt [reg_efl], 300h	; clear TF and IF
	mov word [reg_cs], cs
	mov word [reg_eip], terminate_00
	 push ax		; (dummy to take space for return address)
	mov word [reg_ss], ss
	mov word [reg_esp], sp	; save current ss:sp
	 pop ax			; (discard)
	xor ax, ax
	mov word [reg_eip+2], ax
	mov word [reg_esp+2], ax
%if _PM
	mov word [reg_es], ax
	mov word [reg_ds], ax
	mov word [reg_fs], ax
	mov word [reg_gs], ax	; insure valid segregs in PM
%endif
	call run
		; The dummy stack space above is to hold the return address
		; of this call. The debugger stack is used by this run.

	testopt [internalflags], attachedterm
@@:
	retn


;--- this is called by "run"
;--- better don't use INTs inside
;--- set debuggee's INT 23/24

		; Low-level functions to reset to debuggee's interrupt vectors 23h/24h
		; INP:	-
		; OUT:	-
		; CHG:	bx, (e)dx, cx, ax
		; STT:	ds = our segment
		;	Do not use Int21, even if not in InDOS mode
setint2324:
%if _BOOTLDR
	testopt [internalflags], nodosloaded
	jnz .ret		; don't touch int23/24 -->
%endif
%if _PM
	call ispm
	jz .pm
%endif
	push es
	push di
	push si

	xor di, di
	mov es, di
	mov di, 23h *4
	mov si, run2324
	movsw
	movsw
	movsw
	movsw

%if _PM
	call hook2F
%endif
	pop si
	pop di
	pop es
.ret:
	retn
%if _PM
.pm:
	push si
	mov si, run2324
	mov bx, 0223h
.loop:
	_386_o32		; mov edx, dword [si+0]
	mov dx, word [si+0]
	mov cx, word [si+4]
	mov ax, 0205h
	int 31h
	add si, 6
	inc bl
	dec bh
	jnz .loop
	pop si
	retn
%endif

		; Low-level functions to save debuggee's interrupt vectors 23h/24h
		;  and set our interrupt vectors instead
		; INP:	-
		; OUT:	-
		; CHG:	-
		; STT:	ds = our segment
		;	Do not use Int21, even if not in InDOS mode
getint2324:
%if _BOOTLDR
	testopt [internalflags], nodosloaded
	jnz .ret		; don't touch int23/24 -->
%endif
%if _PM
	call ispm
	jz .pm
%endif
	push si
	push di
	push es

	push ds
	pop es
	xor di, di
	mov ds, di
	mov di, run2324
	mov si, 23h *4
	push si
	movsw			; save interrupt vector 23h
	movsw
	movsw			; save interrupt vector 24h
	movsw
	pop di
	push es
	pop ds
	xor si, si
	mov es, si
	mov si, CCIV
	movsw
	movsw
	movsw
	movsw

	pop es
	pop di
	pop si
.ret:
	retn
%if _PM
subcpu 286
.pm:
	_386_o32
	pusha
	mov di, run2324
	mov bx, 0223h
.loop:
	mov ax, 0204h
	int 31h
	_386_o32		; mov dword [di+0], edx
	mov word [di+0], dx
	mov word [di+4], cx
	add di, byte 6
	inc bl
	dec bh
	jnz .loop
%if _ONLYNON386
	db __TEST_IMM8		; (skip pusha)
%else
	db __TEST_IMM16		; (skip pushad)
%endif

restoredbgi2324:
setdbgi2324:
	_386_o32
	pusha
	mov si, dbg2324
	mov bx, 0223h
_386	xor edx, edx
.loop:
	lodsw
	mov dx, ax
	mov cx, word [cssel]
	mov ax, 0205h
	int 31h
	inc bl
	dec bh
	jnz .loop
	_386_o32
	popa
	retn
subcpureset
%endif

%if 0
The next three subroutines concern the handling of Int23 and 24.
These interrupt vectors are saved and restored when running the
child process, but are not active when DEBUG itself is running.
It is still useful for the programmer to be able to check where Int23
and 24 point, so these values are copied into the interrupt table
during parts of the C, D, (DX, DI,) E, M, and S commands, so that
they appear to be in effect. The E command also copies these values
back.

Between calls to dohack and unhack, there should be no calls to DOS,
so that there is no possibility of these vectors being used when
DEBUG itself is running.

; As long as no DOS is loaded anyway, Int23 and Int24 won't be touched
by us, so the whole hack is unnecessary and will be skipped.
%endif

		; PREPHACK - Set up for interrupt vector substitution.
		; Entry	es = cs
prephack:
	cmp byte [hakstat], 0
	jne .err		; if hack status error -->
	push di
	mov di, sav2324		; debugger's Int2324
	call prehak1
	pop di
	retn

.err:
	push dx
	mov dx, ph_msg
	call putsz		; display error
	pop dx
	retn

		; INP:	di-> saved interrupt vectors
		; OUT:	-
		; CHG:	-
prehak1:
%if _PM
	call ispm
	jz .pm			; nothing to do
%endif
	push ds
	push si
	xor si, si
	mov ds, si
	mov si, 23h *4
	movsw
	movsw
	movsw
	movsw
	pop si
	pop ds
.pm:
	retn


		; DOHACK - Fake the interrupt vectors 23h and 24h to debuggee's
		; UNHACK - Restore interrupt vectors 23h and 24h to our values
		;	It's OK to do either of these twice in a row.
		;	In particular, the S command may do unhack twice in a row.
		; INP:	ds = our segment
		; OUT:	es = our segment
		; CHG:	-
		; STT:	Do not use Int21
dohack:
	push ss
	pop es
%if _BOOTLDR
	testopt [internalflags], nodosloaded
	jnz unhack.ret		; nothing to hack -->
%endif
	push si
	mov byte [hakstat], 1
	mov si, run2324		; debuggee's interrupt vectors
%if _PM
	call ispm
	jnz unhack.common
subcpu 286
	_386_o32
	pusha
	mov bx, 0223h
.pm_loop:
	_386_o32
	mov dx, word [si+0+0]
	mov cx, word [si+0+4]
	mov ax, 205h
	int 31h
	add si, byte 6
	inc bl
	dec bh
	jnz .pm_loop
	_386_o32
	popa
	pop si
	retn
subcpureset
%else
	jmp short unhack.common
%endif

unhack:
	push ss
	pop es
%if _BOOTLDR
	testopt [internalflags], nodosloaded
	jnz .ret		; nothing to hack -->
%endif
	mov byte [hakstat], 0
%if _PM
	call ispm
	jz restoredbgi2324
%endif
	push si
	mov si, sav2324		; debugger's interrupt vectors
.common:
	push di
	push es
	xor di, di
	mov es, di
	mov di, 23h *4
	movsw
	movsw
	movsw
	movsw
	pop es
	pop di
	pop si
.ret:
	retn


		; OUT:	NZ if InDOS mode
		;	ZR if not
		; CHG:	-
		; STT:	ss = ds
InDos:
%if _BOOTLDR
	testopt [internalflags], nodosloaded
	jnz .return		; always "in DOS" -->
%endif
	testopt [options], fakeindos
	jnz .return		; faking InDOS on anyway -->
.real_indos:
	push ds
	push si
	mov si, pInDOS + so16aSegSel
	call update_dosdata_segment
	lds si, [si - so16aSegSel]
	cmp byte [si], 0
	pop si
	pop ds
.return:
	retn


		; INP:	si -> word seg or sel, word segment, word selector
update_dosdata_segment:
	testopt [internalflags2], dif2_int31_segment
	jz .ret
	push dx
	push ax
	push bx
	mov al, 31h
	call get_86m_interrupt_handler_no_dos
%if _PM
	cmp word [si + soaSegment], dx
	je @F

	call ispm
	jnz .realmode

	mov bx, dx
	mov ax, 0002h
	int 31h

	mov word [si + soaSegSel], ax
	mov word [si + soaSelector], ax
	jmp @F

.realmode:
	mov word [si + soaSegSel], dx
	and word [si + soaSelector], 0

@@:
	mov word [si + soaSegment], dx
	pop bx
	pop ax
%else
	pop bx
	pop ax
	mov word [si + soaSegSel], dx
%endif
	pop dx
.ret:
	retn


;	PARSECM - Parse command line for C and M commands.
;	Entry	AL		First nonwhite character of parameters
;		SI		Address of the character after that
;	Exit	DS:ESI		Address from first parameter
;		ES:EDI		Address from second parameter
;		ECX		Length of address range minus one
;		[bAddr32]	Set if any high word non-zero

parsecm:
	call prephack
	mov bx, word [reg_ds]	; get source range
	xor cx, cx
	call getrange		; get address range into bx:(e)dx bx:(e)cx
	push bx			; save segment first address
	call skipcomm0
	mov bx, word [reg_ds]
	_386_PM_o32	; sub ecx, edx
	sub cx, dx		; number of bytes minus one
	_386_PM_o32	; push edx
	push dx
	_386_PM_o32	; push ecx
	push cx
%if _PM
	mov cl, byte [bAddr32]
	push cx
	call getaddr		; get destination address into bx:edx
	pop cx
	or byte [bAddr32], cl	; if either is 32-bit, handle both as 32-bit
%else
	call getaddr		; get destination address into bx:dx
%endif
	_386_PM_o32
	pop cx		; pop ecx
	_386_PM_o32	; mov edi, edx
	mov di, dx
	_386_PM_o32
	add dx, cx	; add edx, ecx
	jc short errorj7	; if it wrapped around
	call chkeol		; expect end of line
	mov es, bx
	_386_PM_o32	; pop esi
	pop si
	pop ds
	retn

errorj7:
	jmp error

;	PARSELW - Parse command line for L and W commands.
;
;	Entry	AL	First nonwhite character of parameters
;		SI	Address of the character after that
;
;	Exit	If there is at most one argument (program load/write), then the
;		zero flag is set, and registers are set as follows:
;		bx:(e)dx	Transfer address
;
;		If there are more arguments (absolute disk read/write), then the
;		zero flag is clear, and registers are set as follows:
;
;		DOS versions prior to 3.31:
;		AL	Drive number
;		CX	Number of sectors to read
;		DX	Beginning logical sector number
;		DS:BX	Transfer address
;
;		Later DOS versions:
;		AL	Drive number
;		BX	Offset of packet
;		CX	0FFFFh

	usesection lDEBUG_DATA_ENTRY
	align 4, db 0
packet:	dd 0		; sector number
	dw 0		; number of sectors to read
	dd 0		; transfer address Segm:OOOO
%if _PM
	dw 0		; transfer address might be Segm:OOOOOOOO!
%endif

	usesection lDEBUG_CODE
parselw:
	mov bx, word [reg_cs]	; default segment
	_386 xor edx, edx
	mov dx, 100h		; default offset
	call iseol?
	je plw2			; if no arguments
	call getaddr		; get buffer address into bx:(e)dx
	call skipcomm0
	call iseol?
	je plw2			; if only one argument
	push bx			; save segment
	push dx			; save offset
	mov bx, 80h		; max number of sectors to read
	neg dx
	jz plw1			; if address is zero
	mov cl, 9
	shr dx, cl		; max number of sectors which can be read
	mov di, dx
plw1:
	cmp byte [si], ':'	; drive letter specification ?
	jne @F			; no -->

	push ax
	call uppercase
	sub al, 'A'
	cmp al, 32		; valid drive ?
	mov dl, al		; put drive number
	inc si			; -> past the colon
	pop ax
	jb @FF			; got it -->
	dec si			; -> at colon

@@:
	call getbyte		; get drive number (DL)
	db __TEST_IMM8		; (skip lodsb)
@@:
	lodsb
	call skipcomm0
	push dx
	add dl, 'A'
	mov byte [driveno], dl
	call getdword		; get relative sector number
	call skipcomm0
	push bx			; save sector number high
	push dx			; save sector number low
	push si			; in case we find an error
	call getword		; get sector count
	dec dx
	cmp dx, di
	jae errorj7		; if too many sectors
	inc dx
	mov cx, dx
	call chkeol		; expect end of line
	testopt [internalflags], oldpacket| newpacket| ntpacket
	jnz plw3		; if using a packet -->
	pop si			; in case of error
	pop dx			; get LoWord starting logical sector number
	pop bx			; get HiWord
	test bx, bx		; just a 16-bit sector number possible
	jnz errorj7		; if too big
	pop ax			; drive number
	pop bx			; transfer buffer ofs
	pop ds			; transfer buffer seg
	test cx, cx		; NZ
plw2:
	retn

		; disk I/O packet for Int25/Int26, Int21.7305, VDD
plw3:
	pop bx			; discard si
	mov bx, packet
	pop word [bx+0]		; LoWord sector number
	pop word [bx+2]		; HiWord sector number
	mov word [bx+4], cx	; number of sectors
	pop ax			; drive number
	pop word [bx+6]		; transfer address ofs
	pop dx
	xor cx, cx
%if _PM
	call ispm
	jnz plw3_1
	cmp byte [dpmi32], 0
	jz plw3_1
[cpu 386]
	mov word [bx+10], dx	; save segment of transfer buffer
	movzx ebx, bx
	shr edx, 16		; get HiWord(offset)
	cmp byte [bAddr32], 1
	jz plw3_1
	xor dx, dx
__CPU__
plw3_1:
%endif
	mov word [bx+8], dx	; transfer address seg
	dec cx			; NZ and make cx = -1
	retn


%include "expr.asm"


%include "lineio.asm"


%include "ints.asm"


	usesection lDEBUG_CODE

%if _BOOTLDR
		; Determine the amount of actual memory
		;
		; This is important to call at the time we need the size,
		; not just save the size initially. Loading other pre-boot
		; installers or RPLs will change the size.
		;
		; INP:	-
		; OUT:	dx = segment behind usable memory (taking EBDAs & RPLs into account)
		;	ds = ss
		; CHG:	ax, cx, di, si, ds
bootgetmemorysize:
	push es
	xor ax, ax
	mov ds, ax
	int 12h					; get memory size in KiB
	mov cl, 6
	shl ax, cl				; *64, convert to paragraphs
	push ax
	lds si, [ 2Fh *4 ]			; get current Int2F
	inc si					; pointer valid (not 0FFFFh) ? (left increased!)
	jz .norpl				; no -->
	mov ax, ds
	test ax, ax				; segment valid (not zero) ?
	jz .norpl				; no -->
	times 2 inc si				; +3 with above inc
	push cs
	pop es
	mov di, .rpl
	mov cx, .rpl_size
	repe cmpsb				; "RPL" signature ?
	jne .norpl				; no -->
	pop dx
	mov ax, 4A06h
	int 2Fh					; adjust usable memory size for RPL
	db __TEST_IMM8				; (skip pop)
.norpl:
	pop dx
		; dx = segment behind last available memory
	 push ss
	 pop ds
	pop es
	retn

.rpl:	db "RPL"
	endarea .rpl
%endif


;--- ensure a debuggee is loaded
;--- set SI:DI to CS:IP, preserve AX, BX, DX

ensuredebuggeeloaded:
	push ax
	testopt [internalflags], attachedterm
	jnz @F			; not loaded, create -->
	pop ax
	retn			; done

@@:
	push bx
	push dx

	call set_efl_to_fl	; initialize EFL, and ax = 0
	mov di, regs
	mov cx, 15*2		; (8 standard + 6 seg + eip) * 2
	rep stosw		; initialize all regs
%if _BOOTLDR
	testopt [internalflags], nodosloaded
	jz .dos
	mov ax, 60h
	push ax
	mov di, reg_ds
	stosw
	scasw			; (skip dummy high word)
	stosw
	scasw
	stosw
	scasw
	stosw
	call adusetup
	call bootgetmemorysize
	sub dx, 60h
	cmp dx, 1000h
	jbe .bootbelow64kib	; if memory left <= 64 KiB
	xor dx, dx		; dx = 1000h (same thing, after shifting)
.bootbelow64kib:
	mov cl, 4
	shl dx, cl
	dec dx
	dec dx
	mov word [reg_esp], dx
	pop es
	xchg dx, di		; es:di = child stack pointer
	xor ax, ax
	stosw			; push 0 on client's stack

	cmp byte [bInit], 0
	jnz .bootnomemtouch
	inc byte [bInit]
	mov word [es:0], 019CDh	; place opcode for int 19h at cs:ip
.bootnomemtouch:
	jmp .return

.dos:
%endif
	mov byte [reg_eip+1], 100h>>8
	mov ah, 48h		; get size of largest free block
	mov bx, -1
	int 21h
	cmp bx, 11h		; enough for PSP + one paragraph for code/stack ?
	jb .return		; no -->
	mov ah, 48h		; allocate it
	int 21h
	jc .return		; (memory taken between the calls)

	push bx
	mov di, reg_ds		; fill segment registers ds,es,ss,cs
	stosw
	scasw			; (skip dummy high word)
	stosw
	scasw
	stosw
	scasw
	stosw
	call adusetup
	mov bx, word [reg_cs]	; bx:dx = where to load program
	mov es, bx
	pop ax			; get size of memory block
	mov dx, ax
	add dx, bx
	mov word [es:ALASAP], dx
	cmp ax, 1000h
	jbe .below64kib		; if memory left <= 64 KiB
	xor ax, ax		; ax = 1000h (same thing, after shifting)
.below64kib:
	mov cl, 4
	shl ax, cl
	dec ax
	dec ax
	mov word [reg_esp], ax
	xchg ax, di		; es:di = child stack pointer
	xor ax, ax
	stosw			; push 0 on client's stack

		; Create a PSP
	mov ah, 55h		; create child PSP
	mov dx, es
	mov si, word [es:ALASAP]
	clc			; works around OS/2 bug
	int 21h
	call setpspdbg		; reset PSP to ours

		; Finish up. Set termination address.
	mov ax, 2522h		; set interrupt vector 22h
	mov dx, int22
	int 21h
	mov word [es:TPIV], dx
	mov word [es:TPIV+2], ds

	cmp byte [bInit], 0
	jnz .nomemtouch
	inc byte [bInit]
	mov byte [es:100h], 0C3h	; place opcode for retn at cs:ip
.nomemtouch:

	mov word [pspdbe], es
	mov ax, es
	dec ax
	mov es, ax
	inc ax
	mov word [es:8+0], "DE"
	mov word [es:8+2], "BU"
	mov word [es:8+4], "GG"
	mov word [es:8+6], "EE"	; set MCB name
	mov word [es:1], ax	; set MCB owner
.return:
	clropt [internalflags], attachedterm
	push ss
	pop es

	mov si, word [reg_cs]
	mov di, word [reg_eip]
	pop dx
	pop bx
	pop ax
	retn


set_efl_to_fl:
	xor ax, ax		; initialize ax = 0 and FL = ZR NC etc
_no386	push ax			; dummy high word
	_386_o32	; pushfd
	pushf
	pop word [reg_efl]	; set to FL
	pop word [reg_efl+2]	; set to high word of EFL, or zero
	retn


%if _PM
		; Hook Int2F if a DPMI host is found. However for Win9x and DosEmu
		; Int2F.1687 is not hooked because it doesn't work. Debugging in
		; protected mode may still work, but the initial switch must be
		; single-stepped.
		;
		; CHG:	ax, bx, cx, dx, di, es
		; STT:	V86/RM
		;	ss = ds = debugger data segment
hook2F:
	call InDos
	jnz .return
	testopt [internalflags], hooked2F
	jnz .return		; don't hook now -->
.loop:
 %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 .return
 %endif
	mov ax, 1687h		; DPMI host installed?
	int 2Fh
	test ax, ax
	jnz .return
	mov word [dpmientry+0], di	; true host DPMI entry
	mov word [dpmientry+2], es
	mov word [dpmiwatch+0], di
	mov word [dpmiwatch+2], es
	testopt [internalflags], nohook2F
	jnz .return		; can't hook Int2F -->
	testopt [options4], opt4_int_2F_hook
	jz .return		; requested to not hook -->
	mov ax, 352Fh
	int 21h
	mov word [oldi2F+0], bx
	mov word [oldi2F+2], es
	mov dx, debug2F		; ds => lDEBUG_DATA_ENTRY
	mov ax, 252Fh
	int 21h

		; Test whether we can hook the DPMI entrypoint call.
	mov ax, 1687h
	int 2Fh
	test ax, ax
	jnz .nohost
	cmp di, mydpmientry	; our entrypoint returned ?
	jne .nohook
	mov ax, es
	mov bx, ds		; bx => lDEBUG_DATA_ENTRY
	cmp ax, bx
	jne .nohook		; no -->

	mov word [dpmiwatch+0], mydpmientry
	mov word [dpmiwatch+2], ds	; => lDEBUG_DATA_ENTRY

	setopt [internalflags], hooked2F
	setopt [internalflags4], dif4_int_2F_hooked
	call update_inttab_optional
%if _DISPHOOK
	mov ax, ds		; ax => lDEBUG_DATA_ENTRY
	push ds
	pop es
	mov di, dpmihookcs
	call hexword
	mov dx, dpmihook
	call putsz
%endif
.return:
	push ds
	pop es
	retn

.nohost:
.nohook:
	lds dx, [oldi2F]
	mov ax, 252Fh
	int 21h			; unhook
	push ss
	pop ds
	push ss
	pop es			; restore segregs
	setopt [internalflags], nohook2F
				; note that we cannot hook
	mov dx, msg.dpmi_no_hook
	call putsz		; display message about it
	jmp .loop
%endif


	usesection lDEBUG_DATA_ENTRY
	align 16, db 0
ldebug_data_entry_size	equ $-section.lDEBUG_DATA_ENTRY.vstart
	endarea ldebug_data_entry, 1

	usesection ASMTABLE1
	align 16, db 0
asmtable1_size		equ $-section.ASMTABLE1.vstart
	endarea asmtable1, 1

	usesection ASMTABLE2
	align 16, db 0
asmtable2_size		equ $-section.ASMTABLE2.vstart
	endarea asmtable2, 1


	numdef SHOWASMTABLESIZE, 0
%if _SHOWASMTABLESIZE
%assign ASMTABLESIZE asmtable1_size + asmtable2_size
%warning asmtables hold ASMTABLESIZE bytes
%endif


	usesection DATASTACK
%define SECTIONFIXUP -$$+100h+ldebug_data_entry_size \
			+asmtable1_size+asmtable2_size

		; I/O buffers
	alignb 2
line_in:	resb 1			; maximal length of input line
		resb 1			; actual length (must be one less than previous byte)
		resb 255		; buffer for 13-terminated input line
.end:
	; zero-initialisation starts here
..@init_first:
				; b_bplist and g_bplist are expected in that order by initcont
%if _BREAKPOINTS
	alignb 2
b_bplist:
.used_mask:	resb (_NUM_B_BP+7)>>3	; bitmask of used points
.disabled_mask:	resb (_NUM_B_BP+7)>>3	; bitmask of disabled points
 %if _BREAKPOINTS_STICKY
.sticky_mask:	resb (_NUM_B_BP+7)>>3	; bitmask of sticky points
					; desc: stay around during DEBUG's operation unless
					; explicitly removed/un-stickified. This allows
					; to keep breakpoints around while changing from PM.
					; Hits while in DEBUG are ignored though, use DDEBUG.
					; Disabling won't remove them, just ignores hits.
 %endif
	alignb 2
.bp:		resb _NUM_B_BP*BPSIZE
	alignb 2
.counter:	resw _NUM_B_BP
	alignb 2
.id:		resw _NUM_B_BP		; array of lengths/offsets
			; low 10 bits = offset into .idbuffer
			; high 6 bits = length (0 if unused)
	alignb 2
.when:		resw _NUM_B_BP		; array of pointers, 0 = unused

.idbuffer.length:	equ _NUM_B_ID_BYTES
.idbuffer.free:
		resw 1			; offset into .idbuffer of free space
					; (0..1024)

.whenbuffer.length:	equ _NUM_B_WHEN_BYTES
.whenbuffer.free:
		resw 1

.idbuffer:
		resb .idbuffer.length	; buffer holding ID strings
.whenbuffer:
		resb .whenbuffer.length	; buffer holding condition strings
%endif
%if _NUM_G_BP
	resb 1 - (($-$$) % 2)		; make g_bplist.bp aligned
g_bplist:
.used_count:	resb 1			; for the byte counter of saved breakpoints
.bp:		resb _NUM_G_BP*BPSIZE
.end:
%endif
%if _HISTORY && ! _HISTORY_SEPARATE_FIXED
historybuffer:	resb _HISTORY_SIZE
.end:
%endif

		; $ - $$	= offset into section
		; % 2		= 1 if odd offset, 0 if even
		; 2 -		= 1 if odd, 2 if even
		; % 2		= 1 if odd, 0 if even
	; resb (2 - (($-$$) % 2)) % 2
		; $ - $$	= offset into section
		; % 2		= 1 if odd offset, 0 if even
		; 1 -		= 0 if odd, 1 if even
	resb 1 - (($-$$) % 2)		; make line_out aligned
trim_overflow:	resb 1			; actually part of line_out to avoid overflow of trimputs loop
line_out:	resb 263
		resb 1			; reserved for terminating zero
line_out_end:
	alignb 2
line_out_overflow:	resw 1		; 2642h if line_out didn't overflow

	alignb 2
serial_save_irq_mask:	resw 1
serial_save_irq_off:	resw 1
serial_save_dl:		resw 1
serial_save_ier:	resb 1
serial_save_lcr:	resb 1
serial_save_mcr:	resb 1
%if _USE_TX_FIFO
 serial_fcr_setting:	resb 1
%endif
serial_use_intnum:	resb 1
serial_use_params:	resb 1
serial_use_fifo:	resb 1
	alignb 2
baseport:
serial_use_baseport:	resw 1
serial_use_dl:		resw 1
serial_use_irqmask:	resw 1

	alignb 2
rxhead:		resw 1
rxtail:		resw 1
txhead:		resw 1
txtail:		resw 1
	alignb 16
rxfifo:		resb _RXFIFOSIZE
	alignb 16
txfifo:		resb _TXFIFOSIZE

	; zero-initialisation ends here
..@init_behind:

		alignb 16		; stack might be re-used as GDT, so align it on a paragraph
stack:		resb _STACKSIZE
		alignb 2		; ensure stack aligned
stack_end:

datastack_size	equ $-section.DATASTACK.vstart
	endarea datastack, 1


	usesection INIT
initstart:

%include "init.asm"

	usesection INIT
		align 16, db 0
init_size	equ $-section.INIT.vstart
	endarea init, 1


	numdef SHOWINITSIZE, 0
%if _SHOWINITSIZE
%assign INITSIZE init_size
%warning init holds INITSIZE bytes
%endif


	usesection lDEBUG_CODE
	align 16, db 0
ldebug_code_size	equ $-section.lDEBUG_CODE.vstart
	endarea ldebug_code, 1


auxbuff_size: equ (_AUXBUFFSIZE+15) & ~15
	endarea auxbuff, 1

transimagepsp_size:	equ 100h+ldebug_data_entry_size \
			+asmtable1_size+asmtable2_size \
			+datastack_size+ldebug_code_size+init_size
	endarea transimagepsp, 1		; size of PSP and image during installation

pspsegment_size:	equ 100h+ldebug_data_entry_size \
			+asmtable1_size+asmtable2_size \
			+datastack_size
	endarea pspsegment, 1			; size of PSP and image when installed

resimagepsp_size:	equ pspsegment_size+ldebug_code_size+auxbuff_size
	endarea resimagepsp, 1			; size of PSP and image when installed


%if transimagepsp_size > resimagepsp_size
 %fatal "resimagepsp_size assumed larger"
%endif

 %assign __CHECK_RESIDENTSIZE pspsegment_size
 %if __CHECK_RESIDENTSIZE > (64 * 1024)
  %error resident size of PSP segment too large (%[__CHECK_RESIDENTSIZE])
 %endif

%if CODE_INSURE_COUNT
 %warning code_insure_low_byte_not_0CCh needed CODE_INSURE_COUNT times
%endif
