
%if 0

lDebug D commands - Dump data

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

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

DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.

%endif


	usesection 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


	usesection lDEBUG_CODE

		; D command - hex/ASCII dump.
ddd:
%if _INT || _PM || _MCB || _DSTRINGS
	call uppercase
%endif
	xchg al, ah
	mov al, byte [si - 2]
	call uppercase
	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:
	cmp al, 'X'		; DX command ?
_386	je extmem		; yes -->
.notdx:
%endif
%if _MCB
	cmp al, 'M'		; DM command ?
	jne .notdm
	jmp mcbout		; yes -->
.notdm:
%endif
	mov cx, 1
	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
.d_suffix_size:
	mov byte [ddsize], cl
	call skipwhite
	call iseol?
	jne dd1			; jump to getting range --> (with new size)
	jmp lastddd		; default range (ADS:ADO length 128),
				;  but with new size -->

.not_d_suffix:
	call skipwh0
	call iseol?
	jne dd1_bytes		; if an address was given --> (set byte size)

lastddd:
		; 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 testattrhigh
_386_PM	jnz .32
	add dx, byte 7Fh
	jnc dd2_0
	or dx, byte -1
	jmp short dd2_0

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

dd1_bytes:
	mov byte [ddsize], 1
dd1:
	mov cx, 80h		; default length (128 bytes)
	mov bx, word [reg_ds]
	call getrangeX		; 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

	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

	mov ax, opt2_db_header
	cmp byte [ddsize], 2
	jb @F
	mov al, opt2_dw_header
	je @F
	mov ax, opt2_dd_header
@@:
	call dd_header_or_trailer

	call dd_display

	mov ax, opt2_db_trailer
	cmp byte [ddsize], 2
	jb @F
	mov al, opt2_dw_trailer
	je @F
	mov ax, opt2_dd_trailer
@@:
 		; 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
		; 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 cx, msg.header.length
	mov dx, msg.header
	test ax, opt2_db_header | opt2_dw_header | opt2_dd_header
	jnz @F
	mov cx, msg.trailer.length
	mov dx, msg.trailer
@@:
	call putsz		; put initial word
	neg 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 testattrhigh	; 32-bit segment ?
	jz .16			; no -->
	mov ax, 4 + 1 + 8 + 2	; length of address with 32-bit offset
.16:
%endif
	add cx, ax		; length of address minus length of word
				;  = length to pad
	mov al, 32
	mov di, line_out
	rep stosb		; pad
				; ch = 0

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

	push dx
	mov si, 16		; loop counter
	mov bx, [ddsize]	; ddsize
@@:
	mov al, dl		; next numerical offset
	call hexnyb		; display it
	mov cx, bx
	add cx, cx		; cx = 2 * ddsize
	mov al, 32
	rep stosb		; pad to next position
	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
@@:
	mov al, dl
	call hexnyb		; display an offset
		; Note that this will wrap around for the last 1, 2, or 3
		;  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:	word [d_addr + saSegSel] = segment/selector to dump
		;	(e)si = start offset
		;	(e)dx = end offset
		;	byte [ddsize] = 1, 2, or 4 (for byte, word, or dword)
		; 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
	call dd_display_offset.masklownybble
				; ax = offset & ~ 0Fh

	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
	 push ax
	mov ax, 32 << 8 | 32
	rep stosw
	 pop ax
	pop cx

	mov bx, (2+1)*16	; 16 bytes (2 digits each)
	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)
@@:
	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
	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
	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
	_386_PM_o32	; mov ecx, eax
	mov cx, ax
	_386_PM_o32
	add cx, strict byte 0Fh
	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
	and word [ddskipped], 0

	call dohack		; substitute interrupt vectors
	mov ds, word [d_addr + saSegSel]

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

dd6_simple:
	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
	_386_PM_a32
	lodsb			; al = data
	call dd_store		; stores number at es:di->, char at es:bx->
	 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]

dd9:
	test si, 0Fh		; space out till end of line
	jz dd10
	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
@@:
	inc si			; skip as many bytes
	test si, 0Fh
	jz dd10
	loop @B
	jmp short dd9

dd10:
	_386_PM_o32
	add si, word [ddoffset]
	_386_PM_o32
	sub si, word [ddskipped]

	mov cx, (1 + 8 * (2 + 1))	; go back 8 bytes (2 digits each)
	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)
@@:
	sub di, cx
	mov byte [di], '-'
	call unhack
	mov di, bx
	push dx
	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).
_386_PM	mov bx, word [d_addr + saSegSel]
				; reset bx
_386_PM	call testattrhigh	; 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
	call hexword
	mov al, ':'
	stosb
	_386_PM_o32	; mov eax, esi
	mov ax, si
%if _PM
	call testattrhigh	; 32-bit segment ?
	jz .16			; no --> (don't display zero high word)
	call hexword_high	; yes, display high word of address
	jmp short .common

		; Insure that the high word is zero.
.16:
;_386	test esi, ~0FFFFh
;_386	jnz .error
_386	test edx, ~0FFFFh
_386	jz .common
;.error:
_386	mov dx, msg.ofs32
_386	call putsz_error
_386	jmp cmd3
.common:
%endif
	call hexword
	mov ax, 32<<8|32
	stosw
	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
		;	(e)ax = offset & ~0Fh
.masklownybble:
	push si
	and si, ~0Fh
	_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:
	mov ah, al
	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-dependant characters ?
	jnz .noctrl		; yes -->
.ctrl:
	mov ah, '.'
.noctrl:
	mov byte [es:bx], ah
	inc bx
	push cx
	call hexbyte
	pop cx
	retn


%if _PM
		; DL command
descout:
	call skipwhite
	call getword	; get word into DX
	mov bx, dx
	call skipcomm0
	mov dx, 1
	cmp al, 13
	je .onlyone
	call uppercase
	cmp al, 'L'
	jne .notlength
	call skipcomma
.notlength:
	call getword
	call chkeol
.onlyone:
	inc dx		; (note js at nextdesc changed to jz)
	mov si, dx	; save count
	call ispm
	je nextdesc
	mov dx, nodesc
	jmp putsz
desc_done:
	retn
cpu 286
nextdesc:
	dec si
	jz desc_done
	mov di, descr
	mov ax, bx
	call hexword
	mov di, descbase
	push di
	mov ax, "??"
	stosw
	stosw
	stosw
	stosw
	add di, byte (desclim-(descbase+8))
	stosw
	stosw
	stosw
	stosw
	add di, byte (descattr-(desclim+8))
	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, desclim
	_no386_jmps use16desc
cpu 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, descattr
	call hexword
desc_out:
	mov dx, descr
	call putsz
	add bx, byte 8
	jmp short nextdesc
[cpu 286]
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
__CPU__
%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
	cmp al, ';'
	je .last
	cmp al, 13
	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]
	call 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 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

.char:
	push ss
	pop ds
	push ax
	call unhack		; restore state
	pop ax
	push si
	push cx
	call putc		; display
	pop cx
	pop si
	call handle_serial_flags_ctrl_c
	call dohack
	mov bx, word [dstringaddr]
	mov ds, word [bx + saSegSel]
				; go back to special state
	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:
	push ss
	pop ds			; restore ds
	_386_PM_o32	; mov dword [bx], esi
	mov word [bx], si
	call unhack
	mov al, 13
	call putc
	mov al, 10
	call putc
	retn
%endif

%if _INT
		; DI command
gateout:
	call skipwhite
	call getbyte	; get byte into DL
	xor dh, dh
	mov bx, dx
	call skipcomm0
	mov dx, 1
	cmp al, 13
	je .onlyone
	cmp al, ';'
	je .onlyone
	call uppercase
	cmp al, 'L'
	jne .notlength
	call skipcomma
	call getword	; get byte into DL
	or dx, dx
	jz .err
	cmp dx, 100h
	je .checkrange
	push ax
	and ah, 1Fh
	cmp ah, 8
	pop ax
	ja .err
.checkrange:
	push dx
	add dx, bx
	cmp dx, 100h
	pop dx
	jna .rangeok
.err:
	jmp error

.last:
	xor bx, bx
	mov bl, byte [lastint]
	mov dx, 1
	inc bl
	jnz .onlyone
	mov word [lastcmd], dmycmd
	retn

.notlength:
	call getbyte
	xor dh, dh
	sub dl, bl
	inc dx
.rangeok:
	call chkeol
.onlyone:
	mov word [lastcmd], .last
	call prephack
	mov si, dx	; save count
.next:
	mov byte [lastint], bl
	call dohack
	mov di, line_out
	mov ax, "in"
	stosw
	mov ax, "t "
	stosw
	mov al, bl
	call hexbyte
	mov al, 32
	stosb
%if _PM
	call ispm
	jnz .rm
cpu 286
	mov ax, 0204h
	cmp bl, 20h
	adc bh, 1	; if below, bh = 2
.loopexception:
	int 31h
	jc .failed
	mov ax, cx
	call hexword
	mov al, ':'
	stosb
	_386_PM_o32	; mov eax, edx
	mov ax, dx
	cmp byte [dpmi32], 0
	jz .gate16
	call hexword_high
.gate16:
	call hexword
	mov al, 32
	stosb
	mov ax, 0202h
	dec bh
	jnz .loopexception
	dec di
	call unhack
	push bx
	call putsline_crlf
	pop bx
	inc bx
	dec si
	jnz .next
	retn
.rm:
%endif
	mov cl, 2
	push bx
	push ds
	shl bx, cl
	xor ax, ax
	mov ds, ax
	mov ax, word [bx+2]
	mov dx, word [bx+0]
	pop ds
	pop bx
	call hexword
	mov al, ':'
	stosb
	mov ax, dx
%if _PM
	mov bh, 1
	jmp short .gate16

.failed:
	call unhack
	mov dx, gatewrong
	jmp putsz
%else
	call hexword
	call unhack
	push bx
	call putsline_crlf
	pop bx
	inc bx
	dec si
	jnz .next
	retn
%endif
%endif

%if _MCB
		; DM command
mcbout:
	call skipwhite
	mov dx, word [wMCB]
	cmp al, 13
	je .lolmcb
	cmp al, ';'
	je .lolmcb
	call getword
	call chkeol
.lolmcb:
	mov si, dx
	mov di, line_out
	mov ax, "PS"
	stosw
	mov ax, "P:"
	stosw
	mov al, 32
	stosb
	mov ax, word [pspdbe]
	call hexword
	call putsline_crlf	; destroys cx,dx,bx
	mov cl, 'M'
.next:
	cmp si, byte -1
	je .invmcb
	cmp si, byte 50h
	jae .valmcb
.invmcb:
	mov dx, msg.invmcbadr
	jmp putsz
.valmcb:
	mov di, line_out
	push ds
%if _PM
	call setds2si
%else
	mov ds, si
%endif
	mov ch, byte [0000]
	mov bx, word [0001]
	mov dx, word [0003]

	mov ax, si
	call hexword		; segment address of MCB
	mov al, 32
	stosb
	mov al, ch
	call hexbyte		; 'M' or 'Z'
	mov al, 32
	stosb
	mov ax, bx
	call hexword		; MCB owner
	mov al, 32
	stosb
	mov ax, dx
	call hexword		; MCB size in paragraphs

	mov al, 32
	stosb
	mov ax, dx		; ax = size in paragraphs
	push bx
	push ax
	push dx
	push cx
	xor dx, dx		; dx:ax = size in paragraphs
	mov cx, 16		; cx = 16, multiplier (get size in bytes)
	mov bx, 4+4		; bx = 4+4, width

	call disp_dxax_times_cx_width_bx_size.store
	pop cx
	pop dx
	pop ax
	pop bx

	test bx, bx
	jz .freemcb		; free MCBs have no name -->
	mov al, 32
	stosb
	push si
	push cx
	push dx

	push ds
	mov si, 8
	mov cx, 2
	cmp bx, si		; is it a "system" MCB? (owner 0008h or 0007h)
	ja @F
	cmp byte [si], "S"	; "S", "SD", "SC" ?
	je .nextmcbchar		; yes, limit name to two characters -->
	jmp .nextmcbchar_cx_si	; no, assume full name given
@@:
	dec bx			; => owner block's MCB
%if _PM
	call setds2bx
%else
	mov ds, bx
%endif
.nextmcbchar_cx_si:
	mov cx, si		; = 8
.nextmcbchar:			; copy name of owner MCB
	lodsb
	stosb
	or al, al
	loopnz .nextmcbchar	; was not NUL and more bytes left ?
	test al, al
	jnz @F
	dec di
@@:
	pop ds

	cmp word [1], 8
	jne .not_s_mcb
	cmp word [8], "S"	; S MCB ?
	jne .not_s_mcb

	mov ax, " t"
	stosw
	mov ax, "yp"
	stosw
	mov ax, "e "
	stosw

	xor ax, ax
	mov al, [10]
	call hexbyte

	 push ss
	 pop ds
	mov si, smcbtypes
.s_mcb_loop:
	cmp word [si], -1
	je .s_mcb_unknown
	cmp word [si], ax
	je .s_mcb_known
	add si, 4
	jmp .s_mcb_loop

.s_mcb_known:
	mov si, word [si + 2]
	jmp .s_mcb_common

.s_mcb_unknown:
	mov si, smcbmsg_unknown
.s_mcb_common:
	mov al, 32
@@:
	stosb
	lodsb
	test al, al
	jnz @B

.not_s_mcb:
	pop dx
	pop cx
	pop si
.freemcb:

	pop ds
	cmp ch, 'M'
	je .disp
	cmp ch, 'Z'
	je .disp
.ret:
	retn

.disp:
	mov cl, ch
	push dx
	push cx
	call putsline_crlf	; destroys cx,dx,bx
	pop cx
	pop dx
	add si, dx
	jc .ret			; over FFFFh, must be end of chain --> (hmm)
	inc si
	jz .ret
	jmp .next

%if _PM
setds2si:
	mov bx, si
setds2bx:
	call ispm
	jnz sd2s_ex
	mov dx, bx
	call setrmsegm
sd2s_ex:
	mov ds, bx
	retn
%endif	; _PM
%endif	; _MCB

;--- DX command. Display extended memory

%if _PM
[cpu 386]
extmem:
	mov dx, word [x_addr+0]
	mov bx, word [x_addr+2]
	call skipwhite
	cmp al, ';'
	je extmem_1
	cmp al, 13
	jz extmem_1
	call getdword		; get linear address into bx:dx
	call chkeol		; expect end of line here
extmem_1:
	mov word [lastcmd], extmem
	push bx
	push dx
	pop ebp

	mov di, stack		; create a GDT for Int15.87
	xor ax, ax
	mov cx, 8
	rep stosw
	mov ax, 007Fh
	stosw
	mov ax, dx
	stosw
	mov al, bl
	stosb
	mov ax, 0093h
	stosw
	mov al, bh
	stosb
	mov ax, 007Fh
	stosw
	mov ax, line_in+128
	mov bx, word [pspdbg]
	movzx ebx, bx
	shl ebx, 4
	movzx eax, ax
	add eax, ebx		; eax = flat address of line_in+128
	stosw
	shr eax, 16
	stosb
	mov bl, ah
	mov ax, 0093h
	stosw
	mov al, bl
	stosb
	mov cx, 8
	xor ax, ax
	rep stosw

	call ispm
	mov si, stack
	mov cx, 0040h
	mov ah, 87h
	jnz extmem_rm
	push word [pspdbg]
	push 15h
	call intcall
	jmp short i15ok
extmem_rm:
	int 15h
i15ok:
	jc extmem_exit
	mov si, line_in+128
	mov ch, 8h
nexti15l:
	call handle_serial_flags_ctrl_c
	mov di, line_out
	mov eax, ebp
	shr eax, 16
	call hexword
	mov ax, bp
	call hexword
	mov ax, 32<<8|32
	stosw
	mov bx, line_out+10+3*16
	mov cl, 10h
nexti15b:
	lodsb
	call dd_store
	mov al, 32
	stosb
	dec cl
	jnz nexti15b
	mov byte [di-(8*3+1)], '-'	; display a '-' after 8 bytes
	add di, 16
	push cx
	call putsline_crlf
	pop cx
	add ebp, byte 10h
	dec ch
	jnz nexti15l
	mov dword [x_addr], ebp
extmem_exit:
	retn
__CPU__
%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:
	db __TEST_IMM8		; (skip stc, NC)
.store:
	stc

	lframe near
	lequ 4 + 4 + 2,		buffer_size
		; 4: "2048" (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)
.loop:
	cmp word [bp + ?dividend + 4], 0
	jnz .divide
	cmp word [bp + ?dividend + 2], 0
	jnz .divide
	cmp word [bp + ?dividend], 2048
	jbe .end
.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:
	lea di, [bp + ?buffer + ?buffer_size - 1]
	std			; _AMD_ERRATUM_109_WORKAROUND does not apply
	mov al, "B"
	stosb
	mov al, [si]
	cmp al, 32
	je @FF

	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
@@:
	stosb
@@:
	mov al, 32
	stosb

	mov ax, word [bp + ?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:
	call 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

