include compiler.inc
	ttl	_XN, 1.04, 08-28-86 jwk

;binary to ascii conversion
;
;		call is _x?(n,bfr,base) unsigned long n; char *bfr;
;			int base; /* only _xn takes base */
;		BUFFER MUST BE AT LEAST 12 BYTES, NO CHECK MADE!!!
;
;
;	REGISTERS at entry--
;		AX:	unknown
;		BX:	unknown
;		CX:	unknown
;		DX:	unknown
;		SI:	unknown
;		DI:	unknown
;		BP:	Frame pointer of calling routine.
;		DS:	unknown
;		ES:	unknown
;
;		  at exit--
;		AX:	Offset of "bfr" start, same as supplied.
;		BX:	zero
;		CX:	zero
;		DX:	Segment of "bfr" start (if not small model).
;		SI:	Restored.
;		DI:	Restored.
;		BP:	Restored.
;		DS:	Restored.
;		ES:	Segment of "bfr"
;
;	SIDE EFFECTS (including effect on RAM):
;		Converts its argument (as unsigned long) to ASCII,
;		using supplied base in range from 2 to 36 inclusive, and
;		stores characters in buffer as null-terminated string.
;

	dseg

	cseg

	procdef	_xn <<n1,word>,<n2,word>,<bfr,ptr>,<base,word>>
	pushreg
	mov	si,base
	jmp	short	xc
;
	entrdef	_xh
	pushreg
	mov	si,16
	jmp	short	xc
;
	entrdef	_xo
	pushreg
	mov	si,8
	jmp	short	xc
;
	entrdef	_xd
	pushreg
	mov	si,10

xc:
	mov	ax,n1		;LSW of number
	mov	dx,n2		;MSW of number
	ldptr	di,bfr		;buffer address to use
	call	cvtl		;macro not needed, no args to pass
	clc
	retptrm	bfr

	pend	_xn

	internal	cvtl
;
;	internal routine to perform the conversion and store chars
;	(chars are stored in reverse sequence, then un-reversed)
;
;	REGISTERS at entry--
;		AX:	LSW of number to convert
;		BX:	unknown
;		CX:	unknown
;		DX:	MSW of number to convert
;		SI:	base quantity (2 to 36)
;		DI:	buffer address offset to start
;		BP:	Frame pointer of calling routine (_xn).
;		DS:	unknown
;		ES:	buffer address segment
;
;		  at exit--
;		AX:	last pair of chars swapped
;		BX:	zero
;		CX:	zero
;		DX:	zero
;		SI:	offset near middle of buffer
;		DI:	offset near middle of buffer
;		BP:	Restored
;		DS:	unchanged
;		ES:	buffer address segment
;
;	SIDE EFFECTS (including effect on RAM):
;		Stores remainders, converted to ASCII, at ES:DI,
;		then reverses chars in buffer to the correct sequence
;		using the K&P "swap the ends" algorithm.

	push	bp		;don't use macro here; bp-4 offset
	mov	bp,sp		;...must not be modified!
	push	di		;save buffer start for "reverse"
	xor	bx,bx		;clear work register
	push	bx		;clear the dividend work reg
	cld			;set for ascending STOSB action
	xchg	ax,bx
	stosb			;put in terminating NULL first
	xchg	ax,bx
;
;	this does 32-bit division/remainder arithmetic
;
cv0:
	mov	cx,32		;32 bits must be shifted
cv1:
	xchg	cx,[bp-4]	;swap loop count/work reg
	shl	ax,1		;puts 0 bit in quotient
	rcl	dx,1
	rcl	bx,1
	rcl	cx,1
	or	cx,cx		;will it go?
	jnz	cv2		;yes since base is < 8 bits
	cmp	si,bx		;maybe...
	ja	cv3		;no
cv2:
	sub	bx,si		;32-bit subtract
	sbb	cx,0
	inc	ax		;sets low bit of Q to 1
cv3:
	xchg	cx,[bp-4]	;swap back
	loop	cv1		;do it for all 32 bits
;
;	quotient in DX:AX, remainder ( < base ) in BL, BH==0
;
	xchg	ax,bx		;set up to save remainder
	add	al,'0'		;convert to ASCII
	cmp	al,'9'
	jbe	cv4		;not in A-F range
	add	al,7		;map to UC alpha
cv4:
	stosb			;put it in buffer
	mov	al,ah		;clear work reg for next time
	xchg	ax,bx		;get LSW of quotient back
	or	ax,ax		;and test for conversion done
	jnz	cv0		;no, go do another divide
	or	dx,dx		;maybe...
	jnz	cv0		;no
;
;	this point is not reached until conversion is complete
;
cv5:
	pop	ax		;balance the stack (work reg word)
	pop	si		;offset to front of buffer
cv6:
	dec	di
	cmp	si,di		;check for overlap (or same)
	jae	cv7		;yep, all swapped around
	mov	ah,es:[si]	; note that lodsb/stosb would
	mov	al,es:[di]	; take more code because one
	mov	es:[di],ah	; is incrementing while the
	mov	es:[si],al	; other is decrementing
	inc	si
	jmp	cv6
cv7:
	mov	sp,bp
	pop	bp
	ret

cvtl	endp			;pend macro is only for public procs
;
	finish
