
;****************************************************************************
;*  									    *
;*  Procedure msout	On entry ds:si -> text, terminated with 0.	    *
;*  									    *
;*  Display message on standard output, convert parameters to hex ascii or  *
;*  decimal ascii for each n, n, n in message where n=number of digits.  *
;*  Repeat character c for nc, where n=(number of repetitions plus 30h).   *
;*  									    *
;****************************************************************************

litsg	equ	''		;next character is to be output directly
hexsg	equ	''		;hexadecimal conversion signal
decsg	equ	''		;decimal conversion signal
decsgn	equ	''		;decimal conversion, leading spaces suppressed
decsgc	equ	''		;decimal conversion, use commas
repsg	equ	''		;repeat character signal
repsp	equ	''		;repeat space signal
sselsg	equ	''		;internal string select start signal
ssepsg	equ	''		;internal string separator
ssterm	equ	''		;internal string select terminator
xselsg	equ	''		;external string select signal
bitsg	equ	''		;binary conversion signal
skpsg	equ	''		;skip parameter
sgchk	equ	''		;all characters below this value are literals

msout	proc	near
	pushad
	push	es

	mov	ax,ds
	mov	es,ax
	mov	di,os iobuf	;output buffer
	mov	bx,os msval	;parameter list

	push	os 0		;end of message
	cld

msorc:	lodsb			;read character
	cmp	al,sgchk
	jnc	s msocc		;conversion character

msowc:	stosb			;write character to buffer
	or	al,al
	jnz	s msorc		;not at end

	dec	di		;address the destination terminator
msosx:	pop	si
	or	si,si
	jnz	s msorc		;continue with prior string

	mov	dx,os iobuf
	sub	di,dx		;length
	jz	msox		;what?
	mov	cx,di
	mov	ah,40h
	mov	bx,msdev	;stdout
	int	21h		;write line

msox:	pop	es
	popad
	ret

msocc:	jnz	s msosg		;check translation signal
	mov	ax,0a0dh
	stosw
	jmp	s msorc		;lowest conversion character is cr/lf


; Numeric Conversion Signals

msosg:	cmp	al,hexsg
	jz	mscvh		;hex conversion character
	xor	dh,dh		;decimal type flag
	cmp	al,decsg
	jz	mscvd		;decimal conversion
	mov	dh,40h		;leading space suppress, bit 6
	cmp	al,decsgn
	jz	mscvd		;decimal, suppress leading spaces
	shl	dh,1		;include commas, bit 7
	cmp	al,decsgc
	jz	mscvd		;decimal with commas

; String Select Signals

	cmp	al,ssterm
	jz	s msosx		;end of substring
	cmp	al,ssepsg
	jz	s msosx		;end of substring
	cmp	al,sselsg
	jnz	s msnst		;not substring select signal

	push	si		;save substring 0 address
mssft:	lodsb			;find terminator
	cmp	al,ssterm
	jz	s mssrt		;end of substring set
	or	al,al
	jnz	s mssft
msfer:	pop	si
msfet:	mov	al,0a8h
	jmp	s msowc		;format error, ignore signal

mssrt:	mov	bp,sp
	xchg	si,[bp]		;save end of set, address beginning
	movzx	cx,mb [bx]	;string number, zero based
	add	bx,4		;next message value
	jcxz	msorc		;first element selected

mssrl:	lodsb			;find separator
	cmp	al,ssepsg
	jz	s msssb		;beginning of substring
	or	al,al
	jnz	s mssrl
	jmp	s msfer		;format error, selector out of range

msssb:	loop	mssrl		;find substring (cx)
	jmp	s msxrc		;continue until separator or terminator

msnst:	cmp	al,xselsg
	jnz	s mschr		;not external string select

	push	si		;save current string offset
	mov	si,mw [bx]	;switch to specified string offset
	add	bx,4		;update current parameter address
	or	si,si
	jnz	s msxrc		;read from substring
	pop	si		;ignore zero offset
	jmp	s msxrc

; Character Repeat Signals

mschr:	mov	ah,20h
	cmp	al,repsp
	jz	s msrsp		;repeat spaces
	cmp	al,repsg
	jnz	s msskp		;not repeat character

	lodsb			;character to repeat
	mov	ah,al
msrsp:	lodsb			;get number of repeats
	sub	al,30h		;ascii zero based
	movzx	cx,al		;count
	mov	al,ah
	rep	stosb		;expand
	jmp	s msxrc

msskp:	cmp	al,skpsg
	jnz	s msolc		;not skip signal

	add	bx,4		;ignore parameter
msxrc:	jmp	msorc

msolc:	cmp	al,litsg
	jnz	msowc		;not literal signal
	lodsb			;no translation on character following
	stosb
	jmp	s msxrc


; Binary to Hex Ascii Conversion

; ds:si->source text, es:di->output text, ds:bx->current binary parameter

mscvh:	lodsb			;read character after signal
	dec	al
	and	al,7		;number of digits (0-7 for 1-8)
	shl	al,2		;times bits per digit
	movzx	cx,al		;number of bits minus 4
	mov	eax,[bx]	;get parameter
	add	bx,4		;address next parameter
	ror	eax,cl		;bits 3-0 are ms digit
	shr	cx,2
	inc	cx		;number of digits

mshxd:	mov	dh,al		;save 2 digits
	call	cvhn		;convert al{3:0}
	mov	al,dh		;restore 2 digits
	rol	eax,4		;pull up next digit
	loop	mshxd
	jmp	s msxrc		;continue with text

; Convert Binary in al to Hex Ascii at es:[di]+
;	Affects: ax, di

cvhb:	cld
	push	ax		;save low digit
	shr	al,4
	call	cvhn		;convert high digit
	pop	ax		;convert low digit
cvhn:	and	al,0fh
	or	al,90h		;90-99, 9A-9F
	daa			;90-99, 100-105
	adc	al,40h		;D0-D9, 41-46
	daa			;130-139, 41-46
	stosb			;store ascii 0-9, A-F
	ret


; Binary to Decimal Ascii Conversion

; ds:si->source text, es:di->output text, ds:bx->current binary parameter

; dh = 80h, include commas; dh = 40h, suppress leading spaces

mscvd:	lodsb			;read character after signal
	xor	ecx,ecx		;clear for use as index
	cmp	al,41h
	jc	s msdlp		;32-bit conversion (digit)
	cmp	al,61h
	jnc	s msdwp		;16-bit conversion (a-e)

	mov	1[bx],cl	;8-bit conversion, zero high byte of low word
msdwp:	mov	2[bx],cx	;zero high word for 8/16-bit

msdlp:	dec	al
	and	al,0fh
	mov	cl,al		;number of decimal digits (0-9 for 1-10)
	mov	ebp,[bx]	;get parameter
	add	bx,4		;update current parameter address
	push	bx

msdsc:	sub	al,3
	jnc	s msdsc		;reduce al mod 3
	add	al,3		;add remainder
	mov	bl,al		;number of digits before first comma
	push	si		;save place in source text

	jcxz	msd1		;one digit only
	lea	si,dcval[ecx*4]	;index into 10 list for number of digits

msdxd:	std			;count downward
	lodsd			;get 10 where n is current digit
	cld			;count upward

	mov	dl,2fh		;ascii count
msdxi:	inc	dl
	js	s msdov		;overflow
	sub	ebp,eax		;subtract 10 from parameter value
	jnc	s msdxi
msdov:	add	ebp,eax		;leave remainder

	cmp	dl,3ah
	jc	s msdno		;no overflow
	stc
	rcr	ebp,1		;make sure it propagates
	mov	dl,'*'		;overflow character

msdno:	or	dh,dl		;bits 3-0 are zero if this is a leading zero
	test	dh,0fh		;check whether all zeros so far
	mov	al,dl		;current digit
	jnz	s msdpc		;display non-leading-zero
	mov	al,' '		;change leading zero to space

	test	dh,40h
	jnz	s msdx		;skip leading spaces and commas

msdpc:	stosb			;write digit to output buffer

	or	dh,dh
	jns	s msdx		;no commas

	dec	bl		;decrement comma counter
	jns	s msdx		;no action until zero
	cmp	al,' '
	jz	s msdco		;leave leading space
	mov	al,','
msdco:	stosb			;write comma to output buffer

	mov	bl,2		;reload counter
msdx:	loop	msdxd		;next digit
msd1:	mov	ax,bp		;last digit
	cmp	ax,10
	jc	s msd1n		;no overflow
	mov	al,1ah		;display *
msd1n:	xor	al,30h
	stosb

	pop	si		;restore current index in source text
	pop	bx		;restore current parameter index
	jmp	msorc		;continue with text

	align	2

msdev:	dw	1		;message output device (stdout)

	align	4

dcval	equ	$-4		;10 table

	dd	10,100,1000,10000,100000,1000000
	dd	10000000,100000000,1000000000

msval:				;parameter list
mv1:	dd	0
mv2:	dd	0
mv3:	dd	0
mv4:	dd	0
mv5:	dd	0
mv6:	dd	0
mv7:	dd	0
mv8:	dd	0
mv9:	dd	0
mv10:	dd	0
mv11:	dd	0
mv12:	dd	0

iobuf:				;output buffer

msout	endp
