
%if 0

lDebug D commands - Dump data

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

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

DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.

%endif

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

%if _LINK_COMPAT
	usesection lDEBUG_DATA_ENTRY
	align 4, db 0
%endif

%if _DHIGHLIGHT
	usesection lDEBUG_DATA_ENTRY

	align 2, db 0
dd_h_number:	dw 0
dd_h_text:	dw 0
%endif

%if _DNUM
	usesection lDEBUG_DATA_ENTRY
	align 4, db 0
ddoffset:	dw 0		; offset word for dd
				;  (number of skipped bytes at start of line)
 %if _PM
		dw 0		; high word initialised to and fixed at zero
 %endif
ddskipped:	dw 0
 %if _PM
		dw 0		; high word initialised to and fixed at zero
 %endif
ddsize:		dw 1		; size of dd item
ddoffset2:	db 0
%endif

	usesection lDEBUG_DATA_ENTRY
dd_text_and:	db 0FFh


	usesection lDEBUG_CODE

..@dd_access_start:

		; D command - hex/ASCII dump.
ddd:
%if _DTOP
	clropt [internalflags6], dif6_cpdepchars
%endif
%if _INT || _PM || _MCB || _DSTRINGS || 1
	call capitalise
%endif
	xchg al, ah
	mov al, byte [si - 2]
	call capitalise
	cmp al, 'D'
	xchg al, ah
	jne .not_d_suffix
%if _DSTRINGS
	cmp al, 'Z'		; DZ command ?
	je dz			; yes -->
	cmp al, '$'		; D$ command ?
	je dcpm			; yes -->
	cmp al, '#'		; D# command ?
	je dcounted		; yes -->
	cmp al, 'W'
	jne .notstring
	push ax
	lodsb
	cmp al, '#'		; DW# command ?
	pop ax
	je dwcounted		; yes -->
	dec si
.notstring:
%endif
%if _INT
	cmp al, 'I'		; DI command ?
	jne .notdi
%if 1
	push ax
	lodsb
	dec si
	and al, TOUPPER
	cmp al, 'P'		; distinguish 'di ...' and 'd ip'
	pop ax
	je .notdi
%endif
	jmp gateout		; yes -->
.notdi:
%endif
%if _PM
	cmp al, 'L'		; DL command ?
	jne .notdl
	jmp descout		; yes -->
.notdl:
%endif
%if _DX
	cmp al, 'X'		; DX command ?
_386	je extmem		; yes -->
.notdx:
%endif
%if _PM
	cmp al, '.'
	je descsubcommand
%endif
%if _MCB
	cmp al, 'M'		; DM command ?
	jne .notdm
	jmp mcbout		; yes -->
.notdm:
%endif
%if _DT
	cmp al, 'T'
	jne @F
 %if _DTOP
	call .handletop0
	je .d_top
 %endif
	jmp dumptable
@@:
%endif
%if _DNUM
 %if _DQ
	mov cx, 8
	cmp al, 'Q'
	je .d_suffix_size
	mov cl, 1
 %else
	mov cx, 1
 %endif
	cmp al, 'B'
	je .d_suffix_size
	inc cx			; = 2
	cmp al, 'W'
	je .d_suffix_size
	inc cx
	inc cx			; = 4
	cmp al, 'D'
	jne .not_d_suffix
%if 1
	push ax
	push si
	lodsb
	and al, TOUPPER
	cmp al, 'I'		; possibly "D DI" ?
	je .check_dd_separator
	cmp al, 'X'		; possibly "D DX" ?
	je .check_dd_separator
	cmp al, 'S'		; possibly "D DS" ?
	jne .check_dd_no_if_ZR	; no, --> NZ
.check_dd_separator:
	lodsb			; load next text
	nearcall isseparator?	; if a separator then cannot be valid "DD"
.check_dd_no_if_ZR:
	pop si
	pop ax
	je .not_d_suffix
%endif
.d_suffix_size:
	mov byte [ddsize], cl
%if _DTOP
	call .handletop
%else
	call skipwhite
%endif
	call iseol?
	jne dd1			; jump to getting range --> (with new size)
	jmp lastddd		; default range (ADS:ADO length 128),
				;  but with new size -->
%endif

%if _DTOP
.handletop:
	lodsb
.handletop0:
	call skipcomm0
	dec si
	mov dx, msg.top
	call isstring?
	lodsb
	jne @F			; --> NZ
	call skipcomm0
	setopt [internalflags6], dif6_cpdepchars
 %if _DT
	cmp al, al		; ZR
 %endif
@@:
	retn
%endif

.not_d_suffix:
%if _DTOP
	call .handletop0
%else
	call skipwh0
%endif
.d_top:
	call iseol?
%if _DNUM
	jne dd1_bytes		; if an address was given --> (set byte size)
%else
	jne dd1
%endif

lastddd:
_386_PM	xor eax, eax
	mov ax, word [dd_default_lines]
				; default length in lines, if nonzero
	test ax, ax
	jz @F
	js short .error
	mov word [getrange_lines], ax
	xor ax, ax
	jmp @FF

@@:
	mov word [getrange_lines], 8000h
	mov ax, word [dd_default_length]
	test ax, ax
	jz short .error
	dec ax
@@:
		; byte [ddsize] = size already set
	_386_PM_o32	; mov edx, dword [d_addr]
	mov dx, word [d_addr]	; compute range of 80h or until end of segment
	_386_PM_o32	; mov esi, edx
	mov si, dx
	mov bx, [d_addr + saSegSel]
_386_PM	call test_high_limit
_386_PM	jnz .32
	add dx, ax
	jnc dd2_0
	or dx, byte -1
	jmp short dd2_0

.error:
	jmp error

%if _PM
[cpu 386]
.32:
	add edx, eax
	jnc dd2_0		; if no overflow
	or edx, byte -1
	jmp short dd2_0
__CPU__
%endif

%if _DNUM
dd1_bytes:
	mov byte [ddsize], 1
%endif
dd1:
	mov cx, word [dd_default_length]
				; default length (128 bytes)
	mov di, word [dd_default_lines]
				; default length in lines, if nonzero
	mov bx, word [reg_ds]
	mov word [getrange_lines], 8000h
	nearcall getrangeX.lines	; get address range into bx:(e)dx

	call chkeol		; expect end of line here

	mov word [d_addr + saSegSel], bx
				; save segment (offset is saved later)
%if _PM
	call ispm
	jnz .86m
.pm:
	mov word [d_addr + saSelector], bx
	jmp @F
.86m:
	mov word [d_addr + saSegment], bx
@@:
%endif
	_386_PM_o32	; mov esi, edx
	mov si, dx		; bx:(e)si = start
	_386_PM_o32	; mov edx, ecx
	mov dx, cx		; bx:(e)dx = last
%if _PM && 0
	jmp short dd2_1
%endif

		; Parsing is done.  Print first line.
dd2_0:
%if _PM
	call ispm
	jnz dd2_1
[cpu 286]
	verr bx			; readable ?
__CPU__
	jz dd2_1
%if 1
	mov dx, .errmsg
	jmp putsz_error
	usesection lDEBUG_DATA_ENTRY
.errmsg:asciz "Segment is not readable.",13,10
	usesection lDEBUG_CODE
%else
	mov bx, word [reg_ds]
	mov word [d_addr + saSegSel], bx
%if _PM
	call ispm
	jnz .86m
.pm:
	mov word [d_addr + saSelector], bx
	jmp @F
.86m:
	mov word [d_addr + saSegment], bx
@@:
%endif
%endif
dd2_1:
%endif
	testopt [getrange_lines], 8000h
	jnz .notlines
	call dd_get_one_line_range

.notlines:

%if _DNUM
	mov ax, word [ddsize]
	dec ax			; 0 = byte, 1 = word, 3 = dword
	and ax, si		; how many bytes to skip at the beginning
	mov byte [ddoffset2], al
%endif

	mov ax, opt2_db_header
%if _DNUM
	cmp byte [ddsize], 2
	jb @F
	mov al, opt2_dw_header
	je @F
	mov ax, opt2_dd_header
	; fixme dq
@@:
%endif
	call dd_header_or_trailer


dd_loop_line:
%if _SYMBOLIC
dd_with_sym:
	lframe near
	lvar dword,	startlinear
	lvar dword,	endlinear
	lvar word,	sym_index
	lvar word,	sym_count
	lenter
	xor ax, ax
	lvar dword,	offset
	 push ax		; (zero-initialise high word)
	 push ax
	lvar dword,	adjust
	 push ax		; (zero-initialise high word)
	 push ax		; (zero-initialise offset (low) word)

	_386_PM_o32
	mov word [bp + ?offset], si

	push bx
	_386_PM_o32
	push si
	_386_PM_o32
	push dx

	testopt [internalflags3], dif3_nosymbols_1 | dif3_nosymbols_2
	jnz .justdisplay

	_386_PM_o32
	xchg dx, si		; bx:(e)dx = start address, bx:(e)si = end
	call getlinear_32bit	; dx:ax = start linear
	jc .justdisplay

	mov word [bp + ?startlinear + 2], dx
	mov word [bp + ?startlinear], ax
	push dx
	push ax

	_386_PM_o32
	xchg dx, si		; bx:(e)dx = end address
	call getlinear_32bit	; dx:ax = end linear

	mov word [bp + ?endlinear + 2], dx
	mov word [bp + ?endlinear], ax
	pop bx
	pop cx			; cx:bx = start linear
	jc .justdisplay

	xchg ax, bx
	xchg dx, cx		; cx:bx = end linear, dx:ax = start linear

	nearcall binsearchmain	; es:di -> first entry, cx = number, bx = index
	mov word [bp + ?sym_index], bx
	test cx, cx
	jz .justdisplay

	_386_PM_o32
	pop dx
	_386_PM_o32
	pop si
	pop bx

.loop:
	mov word [bp + ?sym_count], cx

.loop_no_cx:
	 push word [bp + ?sym_index]
	 push ax		; (reserve space, uninitialised)
	dualcall getfarpointer.main
	 pop di
	 pop es

	mov bx, word [bp + ?adjust + 2]
	mov cx, word [bp + ?adjust]
				; bx:cx = adjust

	add cx, word [bp + ?startlinear]
	adc bx, word [bp + ?startlinear + 2]
				; bx:cx = adjust + start linear (adjust linear)

	neg bx
	neg cx
	sbb bx, byte 0		; neg bx:cx

	add cx, word [es:di + smLinear]
	adc bx, word [es:di + smLinear + 2]
				; bx:cx = next linear - adjust linear
				; bx:cx = how far from adjust linear to next

	test bx, bx		; is there a chunk of ddsize at least ?
	jnz .chunk
%if _DNUM
	cmp cx, [ddsize]
%else
	cmp cx, 1
%endif
	jae .chunk		; yes, display a chunk -->

_386_PM	and ecx, 0FFFFh
	_386_PM_o32
	push si
	_386_PM_o32
	add si, cx
	push ss
	pop es
	mov di, line_out
	call dd_display_offset

	testopt [options], dd_no_blanks_sym
	jnz @FF

	pop ax			; ax = original si value
	push ax

	push si
	mov si, ax
%if _DNUM
	mov cx, word [ddsize]
%else
	mov cx, 1
%endif
	mov ax, cx
	dec cx
	and cx, si		; how many bytes to skip at the beginning
	sub si, cx		; = offset after skipped to first displayed
	add cx, cx		; how many digits to skip at the beginning
	and si, 0Fh		; = offset in single line
	add ax, ax		; = 8 for dword, 4 for word, 2 for byte
	inc ax			; = 9 for dword, 5 for word, 3 for byte
	db __TEST_IMM16		; (skip add in first iteration)
@@:
	add cx, ax		; (in subsequent iterations:) add blanks
%if _DNUM
	sub si, word [ddsize]	; still a whole unit to add ? (subtract it)
%else
	sub si, 1
%endif
	jae @B			; yes -->
				; cx = number of blanks to skip
	mov al, 32
	rep stosb		; store blanks for each byte
	pop si
@@:

	push dx
	call putsline		; puts offset + blanks

	 push word [bp + ?sym_index]
	dualcall displaystring	; puts symbol label

	 push word [bp + ?sym_index]
	 push ax
	dualcall getfarpointer.main
	 pop di
	 pop es
	xor dx, dx
_386_PM	call test_high_limit	; 32-bit segment ?
_386_PM	jz .16			; no --> (don't display zero high word)
_386_PM	push esi
_386_PM	pop si
_386_PM	pop dx
.16:
	cmp dx, word [es:di + smOffset + 2]
	mov dx, msg.dd_after_symbol.non_wrt
	jne .wrt
	cmp si, word [es:di + smOffset]
	je .non_wrt
.wrt:
	mov dx, msg.dd_after_symbol.1_wrt
	call disp_message

	mov ax, word [d_addr + 4]
	push ss
	pop es
	mov di, line_out
	call hexword
	push bx
	push cx
	call putsline
	pop cx
	pop bx

	mov dx, msg.dd_after_symbol.2_wrt
.non_wrt:
	call disp_message	; puts after
	pop dx

	_386_PM_o32
	pop si

	inc word [bp + ?sym_index]
				; point to next symbol's SYMMAIN (if any)
	mov cx, word [bp + ?sym_count]
	loop .j_loop		; loop if any more to go
	jmp .justdisplay_no_pop	; if none, just display remainder -->


.j_loop:
	jmp .loop


		; Display a chunk.
		;
		; INP:	(e)si = start offset to display
		;	(e)dx = end offset to display
		;	bx:cx = how far from adjust linear to next
		;		(there is always a next symbol if we are here)
.chunk:
%if _DNUM
	mov ax, word [ddsize]
	dec ax
	not ax
	and cx, ax
%endif

	add word [bp + ?adjust], cx
	adc word [bp + ?adjust + 2], bx

_386_PM	push word [bp + ?adjust + 2]
_386_PM	push ax
_386_PM	pop eax
	mov ax, word [bp + ?adjust]
				; (e)ax = adjust
	_386_PM_o32
	dec ax			; (e)ax = adjust - 1

		; have:	(e)si = prior start offset, (e)dx = end offset
		; want:	(e)si = unchanged, (e)dx = intermediate end offset,
		;	preserve intermediate start offset, stack = end offset
	_386_PM_o32
	push dx			; stack := end offset
	_386_PM_o32
	mov dx, word [bp + ?offset]
				; (e)dx := start offset
	_386_PM_o32
	add dx, ax		; (e)dx := intermediate end offset

	call dd_display		; display, (e)dx := intermediate start offset

		; have:	(e)si scrambled, (e)dx = intermediate start offset,
		;	stack = end offset
		; want:	(e)si = intermediate start offset, (e)dx = end offset
	_386_PM_o32
	mov si, dx		; (e)si := intermediate start offset
	_386_PM_o32
	pop dx			; (e)dx := end offset
	jmp .loop_no_cx

.justdisplay:
	_386_PM_o32
	pop dx
	_386_PM_o32
	pop si
	pop bx

.justdisplay_no_pop:
	lleave
%endif

	call dd_display

	testopt [getrange_lines], 8000h
	jnz .notlines
	dec word [getrange_lines]
	jz .linesdone
	_386_PM_o32
	mov si, dx		; = new start offset
	call dd_get_one_line_range
				; get a new end offset
	jmp dd_loop_line

.linesdone:
.notlines:
	mov ax, opt2_db_trailer
%if _DNUM
	cmp byte [ddsize], 2
	jb @F
	mov al, opt2_dw_trailer
	je @F
	mov ax, opt2_dd_trailer
	; fixme dq
%endif
@@:
		; fall through


		; INP:	ax = flag value to check
		;	 (determines whether "header" or "trailer" is written,
		;	  and which flag must be set in word [options2])
		;	byte [ddoffset2] = how many bytes to skip at the start
		;	bx = segment/selector
		; CHG:	ax, cx, di
		; STT:	ds = es = ss
dd_header_or_trailer:
	test word [options2], ax
	jz .ret
	push bx
	push si
	push dx

	mov di, line_out
%if _40COLUMNS
	testopt [options6], opt6_40_columns
	jz @F
	mov ax, bx
	call hexword
	mov al, ':'
	stosb
 %if _PM
	mov cx, -5
	mov ax, 4 + 2
	call test_high_limit	; 32-bit segment ?
	jz .add			; no -->
	mov al, 8 + 2
	jmp .add
 %else
	mov cx, 1
	jmp .blank
 %endif
@@:
%endif

%if _LINK
	mov cx, msg.header.length
 wlcalc word_subr_0, equ $ - 2
%else
	mov cx, - msg.header.length
%endif
	mov dx, msg.header
	test ax, opt2_db_header | opt2_dw_header | opt2_dd_header	; fixme dq
	jnz @F
%if _LINK
	mov cx, msg.trailer.length
 wlcalc word_subr_0, equ $ - 2
%else
	mov cx, - msg.trailer.length
%endif
	mov dx, msg.trailer
@@:
	call putsz		; put initial word
				; cx = minus length of initial word
	mov ax, 4 + 1 + 4 + 2	; length of address with 16-bit offset
%if _PM
	; mov bx, word [d_addr + saSegSel]
	call test_high_limit	; 32-bit segment ?
	jz .16			; no -->
	mov al, 4 + 1 + 8 + 2	; length of address with 32-bit offset
.16:
%endif
.add:
	add cx, ax		; length of address minus length of word
				;  = length to pad
.blank:
	mov al, 32
	rep stosb		; pad
				; ch = 0

	mov ax, '0 '		; al = '0', ah = blank
%if _DNUM
	mov cl, byte [ddoffset2]; cx = ddoffset2
	jcxz @FF		; if none to skip -->
@@:
	stosw
	inc ax			; increment the number (up to '7')
	loop @B			; loop for skipping -->
@@:
%endif
	sub al, '0'		; = back to numerical (0 .. 7)
	mov dx, ax		; dl = numerical offset

	push dx
%if _DNUM
	mov bx, [ddsize]	; ddsize
%else
	mov bx, 1
%endif
	mov si, 16		; loop counter
%if _40COLUMNS
	testopt [options6], opt6_40_columns
	jz @F
	shr si, 1
%endif
@@:
	mov al, dl		; next numerical offset
	call hexnyb		; display it
%if _DNUM
	mov cx, bx
	add cx, cx		; cx = 2 * ddsize
	mov al, 32
	rep stosb		; pad to next position
%else
	mov al, 32
	stosb
	stosb
%endif
	add dx, bx		; increment dl by how many positions we use
	sub si, bx		; decrement loop counter
	ja @B			; don't jump if si was below-or-equal-to bx
	pop dx

	mov cx, 16		; loop counter
%if _40COLUMNS
	testopt [options6], opt6_40_columns
	jz @F
	shr cx, 1
%endif
@@:
	mov al, dl
	call hexnyb		; display an offset
		; Note that this will wrap around for the last 1 to 7
		;  characters if byte [ddoffset2] is non-zero.
	inc dx			; increment offset
	loop @B			; loop

	call putsline_crlf

	pop dx
	pop si
	pop bx
.ret:
	retn


		; INP:	(e)si = start
		;	word [ddsize] = size of element, 1 or 2 or 4
		; OUT:	(e)dx = end
dd_get_one_line_range:
	_386_PM_o32
	mov dx, si
%if _DNUM
_386_PM	xor eax, eax
	mov ax, word [ddsize]
	dec ax
	and ax, si
%else
	_386_PM_o32
	xor ax, ax
%endif
%if _40COLUMNS
	or dl, 7
	testopt [options6], opt6_40_columns
	jnz @F
%endif
	or dl, 15
@@:

 %if _PM
	push bx
	mov bx, word [d_addr + saSegSel]
	call test_high_limit	; 32-bit segment ?
	pop bx
	jz .16			; no -->
.32:
	_386_PM_o32
	add dx, ax
	jnc @F
	_386_PM_o32
	or dx, strict byte -1
@@:
	retn
.16:
 %endif
	add dx, ax
	jnc @F
	or dx, strict byte -1
@@:
	retn


		; INP:	word [d_addr + saSegSel] = segment/selector to dump
		;	(e)si = start offset
		;	(e)dx = end offset
		;	if _DNUM:
		;	 byte [ddsize] = 1, 2, 4, or 8
		;	 (for byte, word, dword, or qword)
		; OUT:	(d)word [d_addr] updated
		;	(e)dx = (d)word [d_addr]
		;	displayed
dd_display:
	push ss
	pop es
dd2_loop:
	call handle_serial_flags_ctrl_c

	mov word [lastcmd], lastddd

	mov di, line_out	; reset di for next line
%if _40COLUMNS
	mov bx, ~0Fh
	testopt [options6], opt6_40_columns
	jz @F
	mov bl, ~7
@@:
%endif
	call dd_display_offset.masklownybble
				; ax = offset & ~ 0Fh
%if _40COLUMNS
	mov bx, ax
%endif

%if _DNUM
	mov cx, word [ddsize]
	push cx
	dec cx			; 0 = byte, 1 = word, 3 = dword
	and cx, si		; how many bytes to skip at the beginning
		; eg:	si = 101h, cx = 1, skip 1 byte,  ax = 101h
		;	si = 102h, cx = 3, skip 2 bytes, ax = 102h
		;	si = 103h, cx = 3, skip 3 bytes, ax = 103h
		;	si = 103h, cx = 1, skip 1 byte,  ax = 101h
		;	si = 10Fh, cx = 1, skip 1 byte,  ax = 101h
		;	si = 10Fh, cx = 3, skip 3 bytes, ax = 103h
	add ax, cx		; = where to start
	mov word [ddoffset], cx
%endif
	 push ax
	mov ax, 32 << 8 | 32
%if _DNUM
	rep stosw
%endif
%if _40COLUMNS
 %if (opt6_40_columns | opt6_40_indent_odd) & 0FFFF_00FFh
  %error Expected option flags in same byte
 %endif
	testopt [options6], opt6_40_columns | opt6_40_indent_odd
	jz @F			; neither set -->
	jpo @F			; only one set -->
	test bl, 8
	jz @F
	stosb
@@:
%endif
	 pop ax
%if _DNUM
	pop cx
%else
	mov cx, 1
%endif

	mov bx, (2+1)*16	; 16 bytes (2 digits each)
%if _DNUM
	cmp cl, 2
	jb @F			; if it is 1 -->
	mov bl, (4+1)*8		; 8 words (4 digits each)
	je @F			; if it is 2 -->
				; it is 4
	mov bl, (8+1)*4		; 4 dwords (8 digits each)
 %if _DQ
	cmp cl, 4
	je @F
	mov bl, (16+1)*2	; 2 qwords (16 digits each)
 %endif
@@:
%endif
%if _40COLUMNS
	testopt [options6], opt6_40_columns
	jz @F
	shr bx, 1		; half as many items
@@:
%endif
	add bx, di
	call prephack		; set up for faking int vectors 23 and 24

	push ax
		; blank the start of the line if offset isn't paragraph aligned
dd3:
	cmp ax, si		; skip to position in line
	je dd4			; if we're there yet
	ja .error
%if _DNUM
	push ax
	mov ax, 32 << 8| 32
	 push cx
	rep stosw		; store two blanks (2 * 1) if byte,
				;  four blanks (2 * 2) if word,
				;  eight blanks (2 * 4) if dword
	 pop cx
	stosb			; store additional blank as separator
	 push cx
@@:
	mov byte [es:bx], al
	inc bx
	loop @B			; store as many blanks in text dump as bytes
	 pop cx
	pop ax
	add ax, word [ddsize]	; -> behind the byte/word/dword just written
%else
	push ax
	mov ax, 32 << 8| 32
	stosw			; store two blanks
	stosb			; store additional blank as separator
	mov byte [es:bx], al	; store a blank in text dump
	inc bx
	pop ax
	inc ax			; -> behind the byte just written
%endif
	jmp short dd3


.error:
	mov dx, .msg_internal_error
	call putsz_error
	mov ax, 0601h
	call setrc
	jmp cmd3

	usesection lDEBUG_DATA_ENTRY
.msg_internal_error:
		asciz "Internal error in dd3.",13,10
	usesection lDEBUG_CODE


		; Begin main loop over lines of output.
dd4:
	pop ax
%if _40COLUMNS
	_386_PM_o32	; xor ecx, ecx
	xor cx, cx
	mov cl, 0Fh
	testopt [options6], opt6_40_columns
	jz @F
	mov cl, 7
@@:
 %if _PM
	push bx
	mov bx, word [d_addr + saSegSel]
	call test_high_limit	; 32-bit segment ?
	pop bx
	jz .16			; no -->
	_386_PM_o32	; add ecx, eax
 %endif
.16:
	add cx, ax
%else
	_386_PM_o32	; mov ecx, eax
	mov cx, ax
 %if _PM
	push bx
	mov bx, word [d_addr + saSegSel]
	call test_high_limit	; 32-bit segment ?
	pop bx
	jz .16			; no -->
	_386_PM_o32	; add ecx, strict byte 0Fh
 %endif
.16:
	add cx, strict byte 0Fh
%endif
	jc @F
	_386_PM_o32	; cmp ecx, edx
	cmp cx, dx		; compare with end address
	jb dd5			; if we write to the end of the line -->
@@:
	;_386_PM_o32	; mov ecx, edx
	mov cx, dx		; only write until (e)dx, inclusive
dd5:
	;_386_PM_o32	; sub ecx, esi
	sub cx, si
	;_386_PM_o32	; inc ecx
	inc cx			; cx = number of bytes to print this line
				;      up to 16. no 32-bit register required
%if _DNUM
	and word [ddskipped], 0
%endif

	call dohack		; substitute interrupt vectors
%if _DHIGHLIGHT
	mov word [dd_h_number], di
	mov word [dd_h_text], bx
%endif
	mov ds, word [d_addr + saSegSel]

dd6:
%if _DNUM
	mov ax, word [ss:ddsize]
	cmp ax, cx		; ddsize <= left bytes ?
	jbe dd6_simple		; yes, display ddsize bytes -->

	push ax
	push cx
	push di
	neg cx			; - left bytes
	add cx, ax		; ddsize - left bytes = how many skipped
	mov word [ss:ddskipped], cx

	mov cx, ax		; 1 = bytes, 2 = words, 4 = dwords
	dec cx			; 0 = bytes, 1 = words, 3 = dwords
	mov ax, 'XX'
	rep stosw		; fill filler digits not to be written
	pop di
	pop cx
	pop ax
%endif

dd6_simple:
%if _DNUM
	add ax, ax		; 2 = bytes, 4 = words, 8 = dwords
	push ax
@@:
	dec ax
	dec ax
		; first iteration: 0 = bytes, 2 = words, 6 = dwords
		; second iteration: 0 = words, 4 = dwords
		; third iteration: (0 = 3byte,) 2 = dwords
		; fourth iteration: 0 = dwords
	push di
	add di, ax		; -> where to write next 2 hex digits
	 push ax
%endif
	_386_PM_a32
	lodsb			; al = data
	call dd_store		; stores number at es:di->, char at es:bx->
%if _DNUM
	 pop ax
	pop di			; -> start of hex digits space
	test ax, ax		; did we write the left-most digits?
	loopnz @B		; not yet --> (or no more bytes to display)
	pop ax			; = how many digits we wrote
	add di, ax		; -> after right-most digit
	mov al, 32
	stosb			; store a blank
	test cx, cx
	jnz dd6			; (16-bit. cx <= 16)

	push ss			; restore ds
	pop ds
	_386_PM_o32
	sub si, word [ddoffset]
	_386_PM_o32
	add si, word [ddskipped]
%else
	mov al, 32
	stosb			; store a blank
	loop dd6
	push ss			; restore ds
	pop ds
%endif

dd9:
%if _40COLUMNS
	mov ax, 0Fh
	testopt [options6], opt6_40_columns
	jz @F
	mov al, 7
@@:
	test si, ax
%else
	test si, 0Fh		; space out till end of line
%endif
	jz dd10
%if _DNUM
%if _40COLUMNS
	 push ax
%endif
	mov ax, 32 << 8 | 32
	mov cx, word [ddsize]
	push cx
	rep stosw		; store blanks for the number
	stosb			; store additional blank as separator
	pop cx
 %if _40COLUMNS
	 pop ax
 %endif
@@:
	_386_PM_o32
	inc si			; skip as many bytes
 %if _40COLUMNS
	test si, ax
 %else
	test si, 0Fh
 %endif
	jz dd10
	loop @B
%else
 %if _40COLUMNS
	 push ax
 %endif
	mov ax, 32 << 8 | 32
	stosw			; store blanks for the number
	stosb			; store additional blank as separator
	_386_PM_o32
	inc si			; skip as many bytes
 %if _40COLUMNS
	 pop ax
 %endif
%endif
	jmp short dd9

dd10:
%if _DNUM
	_386_PM_o32
	add si, word [ddoffset]
	_386_PM_o32
	sub si, word [ddskipped]
%endif

	mov cx, (1 + 8 * (2 + 1))	; go back 8 bytes (2 digits each)
%if _DNUM
	cmp byte [ddsize], 2
	jb @F				; if it is 1 -->
	mov cl, (1 + 4 * (4 + 1))	; go back 4 words (4 digits each)
	je @F				; if it is 2 -->
					; it is 4
	mov cl, (1 + 2 * (8 + 1))	; go back 2 dwords (8 digits each)
 %if _DQ
	cmp byte [ddsize], 4
	je @F
	mov cl, (1 + 1 * (16 + 1))	; go back 1 qwords (16 digits each)
 %endif
%endif
@@:
%if _40COLUMNS
	testopt [options6], opt6_40_columns
	jz @F
	testopt [options6], opt6_40_dash
	jz .nodash			; do not write a dash
 %if 1
		; calculate dash position
	dec cx
	shr cx, 1
	inc cx
 %else
		; dispatch for dash position
	mov cx, (1 + 4 * (2 + 1))	; go back 4 bytes (2 digits each)
	cmp byte [ddsize], 2
	jb @F				; if it is 1 -->
	mov cl, (1 + 2 * (4 + 1))	; go back 2 words (4 digits each)
	je @F				; if it is 2 -->
					; it is 4
	mov cl, (1 + 1 * (8 + 1))	; go back 1 dwords (8 digits each)
 %endif
 %if _DQ
	cmp byte [ddsize], 4
	ja .nodash
 %endif
@@:
%endif
	sub di, cx
	mov byte [di], '-'
.nodash:
	call unhack
	mov di, bx
	push dx
%if _DHIGHLIGHT
	testopt [options3], opt3_d_highlight
	jz .justputs
	mov ax, 13 | 10 << 8
	stosw
	push si
%if 0
	mov si, line_out
	mov dx, si
@@:
	lodsb
	cmp al, 32
	jne @B
@@:
	lodsb
	cmp al, 32
	je @B
	dec si
	mov cx, si
	sub cx, dx
	call puts
%else
	mov dx, line_out
	mov si, word [dd_h_number]
	mov cx, si
	sub cx, dx
	call puts
	mov dx, si
.hl:
	call emit_highlight
@@:
	lodsb
	cmp si, word [dd_h_text]
	jae .text_dx
	cmp al, 'X'
	je @B
	nearcall getnyb
	jnc @B
	mov cx, si
	sub cx, dx
	call puts
	mov dx, si
	cmp si, word [dd_h_text]
	jae .text
@@:
	lodsb
	cmp si, word [dd_h_text]
	jae .text_dx
	cmp al, 32
	je @B
	cmp al, '-'
	je @B
	call emit_unhighlight
@@:
	lodsb
	cmp si, word [dd_h_text]
	jae .text_dx
	cmp al, 'X'
	je @B
	nearcall getnyb
	jnc @B
	mov cx, si
	sub cx, dx
	call puts
	mov dx, si
	cmp si, word [dd_h_text]
	jae .text
@@:
	lodsb
	cmp si, word [dd_h_text]
	jae .text_dx
	cmp al, 32
	je @B
	cmp al, '-'
	je @B
	jmp .hl

.text_dx:
	mov cx, si
	sub cx, dx
	call puts
	mov dx, si
.text:
	call emit_highlight
	lodsb
	cmp al, 13
	je .done_unhl
	mov cx, 1
	call puts
	mov dx, si
	call emit_unhighlight
	lodsb
	cmp al, 13
	je .done
	mov cx, 1
	call puts
	mov dx, si
	jmp .text

.done_unhl:
	call emit_unhighlight
.done:
	mov dx, si
	dec dx
	mov cx, 2
	call puts
 %endif
	pop si
	jmp @F
%endif
.justputs:
	call putsline_crlf
@@:
	pop dx
	_386_PM_o32	; dec esi
	dec si
	_386_PM_o32	; cmp esi, edx
	cmp si, dx
	_386_PM_o32	; inc esi
	inc si
	jb dd2_loop		; display next line -->
dd11:
		; This check is necessary to wrap around at FFFFh (64 KiB)
		; for 16-bit segments instead of at FFFFFFFFh (4 GiB).
	mov bx, word [d_addr + saSegSel]
				; reset bx (also set segment for trailer)
_386_PM	call test_high_limit	; 32-bit segment ?
_386_PM	jz .16			; no -->
	_386_PM_o32	; inc edx
.16:
	inc dx			; set up the address for the next 'D' command.
	_386_PM_o32	; mov dword [d_addr], edx
	mov word [d_addr], dx
	retn


		; INP:	(e)si = offset (to display)
		;	(e)dx = end offset (for range check of 16-bit segment)
		;	word [d_addr + saSegSel] = segment/selector
		;	es:di -> where to write to
		; OUT:	bx = segment/selector
dd_display_offset:
.:
	mov ax, word [d_addr + saSegSel]
	mov bx, ax
%if _40COLUMNS
	testopt [options6], opt6_40_columns
	jnz @F
%endif
	call hexword
	mov al, ':'
	stosb
@@:
	_386_PM_o32	; mov eax, esi
	mov ax, si
_386_PM	call test_high_limit	; 32-bit segment ?
_386_PM	jz .386_16		; no --> (don't display zero high word)
_386_PM	call hexword_high	; yes, display high word of address
.common:
	call hexword
	mov ax, 32<<8|32
	stosw
	retn

		; Insure that the high word is zero.
%if _PM
.386_16:
;_386	test esi, ~0FFFFh
;_386	jnz .error
_386	test edx, ~0FFFFh
_386	jz .common
;.error:
_386	mov ax, 013Ch
_386	call setrc
_386	mov dx, msg.ofs32
_386	call putsz_error
_386	jmp cmd3
%endif

		; INP:	(e)si = offset (to display)
		;	(e)dx = end offset (for range check of 16-bit segment)
		;	word [d_addr + saSegSel] = segment/selector
		;	es:di -> where to write to
		;	if _40COLUMNS:
		;	 bx = mask to apply to si
		; OUT:	bx = segment/selector
		;	(e)ax = offset & ~0Fh (or offset & bx)
.masklownybble:
	push si
%if _40COLUMNS
	and si, bx
%else
	and si, ~0Fh
%endif
	_386_PM_o32
	push si
	call .
	_386_PM_o32
	pop ax
	pop si
	retn


		; Store a character into the buffer. Characters that can't
		; be displayed are replaced by a dot.
		;
		; INP:	al = character
		;	es:bx-> buffer for displayed characters
		;	es:di-> buffer for hexadecimal number
		; OUT:	es:bx-> behind displayed character
		;	es:di-> behind hexadecimal number and space
		; CHG:	ax
		; STT:	ds unknown
dd_store: section_of_function
	call hexbyte		; dump hexadecimal number
	and al, byte [ss:dd_text_and]
				; for MSDebug compatibility
	cmp al, 32		; below blank ?
	jb .ctrl		; control char -->
	cmp al, 127		; DEL ?
	je .ctrl		; yes, control char -->
	jb .noctrl		; below, not a control char -->
	testopt [ss:options], cpdepchars
				; allow CP-dependent characters ?
	jnz .noctrl		; yes -->
%if _DTOP
	testopt [ss:internalflags6], dif6_cpdepchars
				; allow CP-dependent characters ?
	jnz .noctrl		; yes -->
%endif
.ctrl:
	mov al, '.'		; replace by dot
.noctrl:
	mov byte [es:bx], al	; store dot or printable text byte
	inc bx
	retn


%if _PM
	usesection lDEBUG_DATA_ENTRY
	align 2, db 0
daresult:	dw -1

	usesection lDEBUG_CODE

descalloc:
	call skipwhite
	call chkeol
	xor ax, ax
	mov cx, 1
	int 31h
	jc .error
	mov di, msg.d.a_success_sel
	call hexword
	mov dx, msg.d.a_success
.display:
	call putsz
	mov word [daresult], ax
	retn

.error:
	mov di, msg.d.a_error_code
	call hexword
	mov dx, msg.d.a_error
	cmp ax, 8000h
	jae @F
	mov ax, 0801h
@@:
	call setrc
	mov ax, -1
	jmp .display


descdealloc:
	call skipwhite
	nearcall getword
	call chkeol
	mov ax, 1
	mov bx, dx
	int 31h
	jc .error
	mov dx, msg.d.d_success
.display:
	jmp putsz

.error:
	mov di, msg.d.d_error_code
	call hexword
	mov dx, msg.d.d_error
	cmp ax, 8000h
	jae @F
	mov ax, 0802h
@@:
	call setrc
	jmp .display


descbase:
	call skipwhite
	nearcall getword
	mov cx, dx
	nearcall getdword
	call chkeol
	xchg cx, bx		; cx:dx = base, bx = desc
	mov ax, 7
	int 31h
	jc .error
	mov dx, msg.d.b_success
.display:
	jmp putsz

.error:
	mov di, msg.d.b_error_code
	call hexword
	mov dx, msg.d.b_error
	cmp ax, 8000h
	jae @F
	mov ax, 0803h
@@:
	call setrc
	jmp .display


desclimit:
	call skipwhite
	nearcall getword
	mov cx, dx
	nearcall getdword
	call chkeol
	xchg cx, bx		; cx:dx = limit, bx = desc
	mov ax, 8
	int 31h
	jc .error
	mov dx, msg.d.l_success
.display:
	jmp putsz

.error:
	mov di, msg.d.l_error_code
	call hexword
	mov dx, msg.d.l_error
	cmp ax, 8000h
	jae @F
	mov ax, 0804h
@@:
	call setrc
	jmp .display


desctype:
	call skipwhite
	nearcall getword
	mov bx, dx
	nearcall getword
	call chkeol
	mov cx, dx		; cx = type, bx = desc
	mov ax, 9
	int 31h
	jc .error
	mov dx, msg.d.t_success
.display:
	jmp putsz

.error:
	mov di, msg.d.t_error_code
	call hexword
	mov dx, msg.d.t_error
	cmp ax, 8000h
	jae @F
	mov ax, 0805h
@@:
	call setrc
	jmp .display


descsubcommand:
	lodsb
	cmp al, '?'
	je deschelp
	call ispm
	jne display_nodesc
	call capitalise
	cmp al, 'A'
	je descalloc
	cmp al, 'D'
	je descdealloc
	cmp al, 'B'
	je descbase
	cmp al, 'L'
	je desclimit
	cmp al, 'T'
	je desctype
	jmp error

deschelp:
	lodsb
	call chkeol
	mov dx, msg.deschelp
	jmp putsz_exthelp	; print string and return


		; DL command
descout:
	call skipwhite
	nearcall getword; get word into DX
	mov bx, dx
	call skipcomm0
	mov dx, 1
	call iseol?
	je .onlyone
	call capitalise
	cmp al, 'L'
	jne .notlength
	call skipcomma
.notlength:
	nearcall getword
	call chkeol
.onlyone:
	inc dx		; (note js at nextdesc changed to jz)
	mov si, dx	; save count
	call ispm
	je nextdesc
display_nodesc:
	mov dx, nodesc
	mov ax, 0800h
	call setrc
	jmp putsz
desc_done:
	retn
subcpu 286
nextdesc:
	dec si
	jz desc_done
	mov di, descriptor
	mov ax, bx
	call hexword
	mov di, descriptor.base
	push di
	mov ax, "??"
	stosw
	stosw
	stosw
	stosw
%ifn _LINK
	add di, byte (descriptor.limit - (descriptor.base + 8))
%else
	add di, strict byte -8
 wlcalc byte_ext_descriptor.limit, equ $ - 1
 wlcalc byte_minusext_descriptor.base, equ $ - 1
%endif
	stosw
	stosw
	stosw
	stosw
%ifn _LINK
	add di, byte (descriptor.attrib - (descriptor.limit + 8))
%else
	add di, strict byte -8
 wlcalc byte_ext_descriptor.attrib, equ $ - 1
 wlcalc byte_minusext_descriptor.limit, equ $ - 1
%endif
	stosw
	stosw
	pop di
;	lar ax, bx
;	jnz skipdesc	; tell that this descriptor is invalid
	mov ax, 6
	int 31h
	jc desc_o1
	mov ax, cx
	call hexword
	mov ax, dx
	call hexword
desc_o1:
	mov di, descriptor.limit
	_no386_jmps use16desc
subcpu 386
	lsl eax, ebx
	jnz desc_out
	push ax
	shr eax, 16
	call hexword
	pop ax
	call hexword
	lar eax, ebx
	shr eax, 8
desc_o2:
	mov di, descriptor.attrib
	call hexword
desc_out:
	mov dx, descriptor
	call putsz
	add bx, byte 8
	jmp short nextdesc
subcpureset	; subcpu 386
use16desc:
	lsl ax, bx
	jnz desc_out
	call hexword
	mov ax, 32<<8|32
	stosw
	stosw
	lar ax, bx
	shr ax, 8
	jmp short desc_o2
subcpureset	; subcpu 286
%endif

%if _DSTRINGS
		; D$ command
dcpm:
	mov byte [dstringtype], 36
	mov word [dstringaddr], dcpm_addr
	jmp short dstring

		; DW# command
dwcounted:
	mov byte [dstringtype], 0FEh
	mov word [dstringaddr], dwcount_addr
	jmp short dstring

		; D# command
dcounted:
	mov byte [dstringtype], 0FFh
	mov word [dstringaddr], dcount_addr
	jmp short dstring

		; DZ command
dz:
	mov byte [dstringtype], 0
	mov word [dstringaddr], dz_addr

		; common code for all string commands
dstring:
	call skipwhite
	call iseol?
	jne .getaddr		; if an address was given
.last:
	mov bx, word [dstringaddr]
	_386_PM_o32	; mov edx, dword [bx]
	mov dx, word [bx]
	jmp short .haveaddr	; edx = offset, [bx + saSegSel] = segment
.getaddr:
	mov bx, word [reg_ds]
	nearcall getaddrX	; get address into bx:(e)dx
	call chkeol		; expect end of line here
%if _PM
	push bx
%endif
	push bx
	mov bx, word [dstringaddr]
	pop word [bx + saSegSel]; save segment (offset behind string is saved later)
%if _PM
	call ispm
	jnz .86m
.pm:
	pop word [bx + saSelector]
	jmp @F
.86m:
	pop word [bx + saSegment]
@@:
%endif
.haveaddr:
	mov word [lastcmd], dstring.last
	call prephack
	_386_PM_o32	; mov esi, edx
	mov si, dx
	setopt [internalflags], usecharcounter
	mov byte [ charcounter ], 1
				; initialize
	call dohack
	mov di, line_out	; es:di -> line_out
	mov ds, word [bx + saSegSel]
				; ds:(e)si-> string
	cmp byte [ss:dstringtype], 0FEh
	jb .terminated		; terminated string -->
	lahf
	_386_PM_a32
	lodsb			; load first byte
	xor cx, cx
	mov cl, al		; low byte of count
	sahf
	jne .counted		; only byte count -->
	_386_PM_a32
	lodsb			; load second byte
	mov ch, al		; high byte of count
.counted:
	jcxz .done		; length zero -->
.loop:
	_386_PM_a32
	lodsb			; get character
	call .char		; display
	loop .loop		; until done -->
	jmp short .done

		; INP:	al = text to display
		;	di -> after last buffered text in line_out
		; OUT:	di -> after text stored in buffer
		;	 (in range line_out + 1 .. line_out_end)
		;	buffer content so far dumped if buffer was full
		; STT:	es = ss
		;	ds unknown
		;	in hacked state (dohack called)
		; CHG:	dx
.char:
	cmp di, line_out_end	; end of buffer ?
	jb @F			; not yet, only store to buffer -->

	push ds
	call .dump
				; go back to special state
	call dohack
	pop ds
	mov di, line_out	; reset buffer to start

@@:
	stosb			; store the text byte to buffer
	retn

		; INP:	di -> after last stored text in line_out
		; OUT:	ds set to ss
		;	unhack called
		;	putsline called
		; CHG:	ds, dx
		; STT:	es = ss
.dump:
	push ax
	push cx
	push bx
	 push ss
	 pop ds
	call unhack		; restore state
	call putsline		; write buffered content
	call handle_serial_flags_ctrl_c
				; handle Control-C
	pop bx
	pop cx
	pop ax
	retn

.terminated:
	_386_PM_a32
	lodsb			; load character
	cmp al, byte [ss:dstringtype]
	je .done		; it's the terminator -->
	call .char		; display
	jmp short .terminated	; and get next -->

.done:
		; Finally dump the last chunk (may be empty).
	call .dump		; this resets ds and calls unhack
	_386_PM_o32	; mov dword [bx], esi
	mov word [bx], si
	mov al, 13
	call putc
	mov al, 10
	call putc
	retn
%endif

 %define extcall nearcall
 %define extcallcall nearcall
 %imacro internalcoderelocation 0-*.nolist
 %endmacro
 %imacro internaldatarelocation 0-*.nolist
 %endmacro
 %imacro linkdatarelocation 0-*.nolist
 %endmacro
 %define relocated(address) address
 %assign ELD 0

%if _INT
 %include "dishared.asm"
%endif

%if _MCB
 %include "dmshared.asm"
%endif	; _MCB

%if _DX
 %include "dxshared.asm"
%endif

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

%if _PM
setds2si: section_of_function
	mov bx, si
setds2bx: section_of_function
	call ispm
	jnz sd2s_ex
	mov dx, bx
	call setrmsegm
sd2s_ex:
	mov ds, bx
	retn
%elif _EXTENSIONS
setds2si: section_of_function
	mov bx, si
setds2bx: section_of_function
	mov ds, bx
	retn
%endif


		; INP:	dx:ax = numerator
		;	cx = multiplier (0 to take si:dx:ax as numerator)
		;	bx = field width
		;	es:di -> buffer where to store
		; STT:	UP, ds = ss
		; OUT:	written to buffer, es:di -> behind written string
disp_dxax_times_cx_width_bx_size: section_of_function
	db __TEST_IMM8		; (skip stc, NC)
.store: section_of_function disp_dxax_times_cx_width_bx_size
	stc

	lframe near
	lequ 4 + 4 + 2,		buffer_size
		; 4: "2048" or "26.5" (maximum number)
		; 4: " ?iB" (IEC prefixed unit)
		; 2: ???
	lvar ?buffer_size,	buffer
	lvar 6,			dividend
	lenter
	lvar word,		bit_0_is_store
	 pushf
	lvar word,		width
	 push bx
	push si
	push ds
	push cx
	push ax
	push dx
	push es
	push di

	 push ss	; push cs
	 pop ds
	 push ss
	 pop es

	jcxz .use_si

	push dx
	mul cx
	xchg ax, di
	xchg dx, si		; si:di = first mul

	pop ax
	mul cx
	add ax, si
	adc dx, 0		; dx:ax = second mul + adj, dx:ax:di = mul

	jmp @F

.use_si:
	mov di, ax
	xchg ax, dx
	mov dx, si

@@:
	mov word [bp + ?dividend], di
	mov word [bp + ?dividend + 2], ax
	mov word [bp + ?dividend + 4], dx

		; set up divisor for the unit prefixes
	mov cx, 1024		; 1000 here if SI units
	testopt [options], use_si_units	; SI units ?
	jz @F			; no -->
	mov cx, 1000		; yes, use 1000
@@:

	mov si, msg.prefixes	; -> first prefix (blank)
	xor dx, dx		; init remainder (not really needed)
.loop:
	cmp word [bp + ?dividend + 4], 0
	jnz .divide
	cmp word [bp + ?dividend + 2], 0
	jnz .divide
	cmp word [bp + ?dividend], 2048
				; is dividend <= 2048 ?
	jbe .end		; yes, end unit division loop -->
.divide:
	inc si			; -> next prefix

	xor dx, dx
	mov di, 6
.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
	jmp .loop

.end:
				; dx = last remainder
	mov ax, 10		; ax = 10
	mul dx			; dx:ax = last remainder * 10
	div cx			; divide by unit multiplier,
				;  ax = 0..9 tenth digit
	xchg dx, ax		; dl = 0..9 tenth digit

	lea di, [bp + ?buffer + ?buffer_size - 1]
	std			; _AMD_ERRATUM_109_WORKAROUND does not apply
	mov al, "B"
	stosb
	lodsb			; al = prefix (blank or kMGT)
	cmp al, 32		; unit is Bytes ?
	je @FF			; yes, skip past unit handling -->

	testopt [options], use_si_units
				; SI units ?
	jnz @F			; yes -->
	and al, ~20h		; uppercase, don't do this if SI units
	testopt [options], use_jedec_units
				; JEDEC units ?
	jnz @F			; yes -->
	push ax
	mov al, "i"
	stosb			; don't store this if SI or JEDEC units
	pop ax			; preserve unit prefix
@@:
	stosb			; store prefix letter
@@:
	cmp al, 32		; unit is Bytes ?
	 mov al, 32
	 stosb			; (store a blank)
	 mov ax, word [bp + ?dividend]
				; (ax = dividend, <= 2048)
	je @F			; yes, no tenth digit to display -->
	cmp ax, 100		; displaying above-or-equal 100 ?
	jae @F			; yes, display no tenth digit -->
	xchg ax, dx		; al = tenth digit
	add al, '0'		; make it text
	stosb			; store fractional digit
	mov al, '.'
	stosb			; store decimal point
	xchg ax, dx		; get back ax = dividend
@@:
	mov cx, 10
.loop_write:
	xor dx, dx
	div cx
	xchg ax, dx
				; ax = remainder (next digit)
				; dx = result of div
	add al, '0'
	stosb
	xchg ax, dx		; ax = result of div
	test ax, ax		; any more ?
	jnz .loop_write		; loop -->

	cld

	inc di			; -> first digit
	lea bx, [bp + ?buffer + ?buffer_size]
				; -> behind 'B'
	sub bx, di		; = length of string
	mov si, di

	pop di
	pop es			; restore es:di
				; -> where to store (if storing)

	mov cx, [bp + ?width]
	sub cx, bx
	jbe .none_blank
	mov al, 32
	test byte [bp + ?bit_0_is_store], 1
	jnz @F
.loop_blank_disp:
	nearcall disp_al
	loop .loop_blank_disp
		; now cx = 0 so the rep stosb is a nop
@@:
	rep stosb
.none_blank:


	mov cx, bx
	test byte [bp + ?bit_0_is_store], 1
	jnz @F

		; ! note ss = ds
	mov dx, si		; ds:dx -> string
	call disp_message_length_cx
	db __TEST_IMM16		; (skip rep movsb)
@@:
		; ! note ss = ds
		; ds:si -> string, cx = length
	rep movsb

	pop dx
	pop ax
	pop cx
	pop ds
	pop si
	pop bx
	lleave
	lret

%if _DT
		; DT command
dumptable:
	call skipcomma
	xor bx, bx
	call capitalise
	cmp al, 'T'
	jne @F
	call skipcomma
	inc bx
@@:
	push bx
	call iseol?
	je .table
	nearcall getdword
	call chkeol
	pop si
.loopsingle:
	push bx
	push dx
	xchg ax, dx
	mov ah, -1
	call .item
	pop dx
	pop bx
	call .blank
	mov dl, dh
	mov dh, bl
	mov bl, bh
	mov bh, 0
	test bl, bl
	jnz .loopsingle
	test dx, dx
	jnz .loopsingle
	jmp trimputs


.table:
	mov si, msg.tableheader
	test bx, bx
	jz @F
	mov si, msg.tableheadertop
@@:
	call copy_single_counted_string
	pop si
	mov al, 0		; low nybble is base, high nybble column

.line:
	test si, si
	jz @F
	or al, 128
@@:
	mov ah, 8		; reset items per row
.column:
	call .item

	add al, 16		; next column is 16 codepoints higher
	dec ah			; more columns to go ?
	jnz .column		; yes -->

	push ax
	call trimputs		; display
	mov di, line_out
	pop ax

	inc ax			; increment base (low nybble)
	and al, 15		; test low nybble
	jnz .line		; loop if not yet overflowed -->

	retn


.item:
	mov cx, 3		; width = 3 columns
	xor dx, dx
	 push ax
	mov ah, 0		; dx:ax = number
	call dec_dword_minwidth	; decimal
	  push di
	sub di, cx		; -> start of decimal number written
	dec cx
	mov al, '0'		; ASCII zero
@@:
	scasb			; a leading zero ?
	jne @F			; no -->
	mov byte [di - 1], 32	; replace by blank
	loop @B			; check for another leading zero -->
@@:
	  pop di		; restore -> after decimal number written
	 pop ax
	call .blank
	call hexbyte		; hexadecimal
	call .blank
	mov bl, al		; = index
	cmp al, 32
	jb .named

	mov bl, (asciitablenames.del - asciitablenames.) / 3
				; point to DEL entry
	cmp al, 127
	je .named
	mov bl, (asciitablenames.top - asciitablenames.) / 3
				; point to top entry
	jb .printable
	test si, si
	jnz .printable
.named:
	mov bh, 0		; bx = index
	mov dx, bx
	add bx, bx
	add bx, dx		; times 3
	add bx, asciitablenames	; -> padded 3-byte text
	xchg si, bx
	movsw
	movsb
	xchg bx, si
	jmp @F
.printable:
	call .quote
	stosb
	call .quote
@@:
	call .blank
	cmp al, 80
	jb @F

.blank:
	push ax
	mov al, 32
	jmp .retstosb

.quote:
	test ax, ax
	jns @F
	push ax
	cmp al, "'"
	mov al, "'"
	jne .retstosb
	mov al, '"'
.retstosb:
	stosb
	pop ax
@@:
	retn


	usesection lDEBUG_DATA_ENTRY

%imacro asciitableentry 1.nolist
 %defstr %%string %1
	fill 3, 32, db %%string
%endmacro

asciitablenames:
.:	asciitableentry NUL
	asciitableentry SOH
	asciitableentry STX
	asciitableentry ETX
	asciitableentry EOT
	asciitableentry ENQ
	asciitableentry ACK
	asciitableentry BEL
	asciitableentry BS
	asciitableentry TAB
	asciitableentry LF
	asciitableentry VT
	asciitableentry FF
	asciitableentry CR
	asciitableentry SO
	asciitableentry SI
	asciitableentry DLE
	asciitableentry DC1
	asciitableentry DC2
	asciitableentry DC3
	asciitableentry DC4
	asciitableentry NAK
	asciitableentry SYN
	asciitableentry ETB
	asciitableentry CAN
	asciitableentry EM
	asciitableentry SUB
	asciitableentry ESC
	asciitableentry FS
	asciitableentry GS
	asciitableentry RS
	asciitableentry US
.del:	asciitableentry DEL
%if (.del - .) / 3 != 32
 %error Wrong table size
%endif
.top:	asciitableentry top

	usesection lDEBUG_CODE

%endif

..@dd_access_end:
