; BIN_ASC 16-bit value
; This routine converts a 16-bit number into a counted string of
; ASCII characters compatible with Serout. It suppresses
; leading zeros so that numbers like 17 are sent as "17" not "00017".

	device	pic16c55,xt_osc,wdt_off,protect_off
	reset	start

buffer	=	31	; String buffer at end of memory.
ASC_0	=	'0'	; ASCII numbers: 30h thru 39h.
ASC_dp	=	'.'	; ASCII char for decimal point (period).
fix	=	3	; Position for fixed decimal point.

	org	8

dec_no	ds	1	; Decade (1,10,100...) to work on.
tempH	ds	1	; 16-bit temporary variable used by
tempL	ds	1	; the BIN_ASC routine.
hiB	ds	1	; MSB of number to convert.
lowB	ds	1	; LSB of number to convert.
flags	ds	1
zs	=	flags.0 ; Leading-zero suppression flag.

	org	0

decade	jmp	pc+w
	retw	39,16,3,232,0,100,0,10,0,1

start	mov	hiB, #0FFh	; To see routine's output, either
	mov	lowB, #0FFh	; use the PSIM PIC simulator, or
	call	BIN_ASC ; merge with Serout.
	jmp	$	; Endless loop

; This routine accepts a 16-bit number in hiB, lowB and converts it into
; a counted ASCII text string located at the address "buffer." The buffer
; grows downward in memory. The first (0th) item in buffer is the number
; of characters in the string.

BIN_ASC clr	buffer	; Clear char count (item 0 in buf).
	clr	flags	; Clear zs flag.
	clr	dec_no	; Clear decade no.
	mov	fsr,#buffer-1	; Reserve 0th byte of buffer for count.
:loop	mov	w,dec_no	; Get 1st hex digit of decade no.
	call	decade	; from the lookup table.
	mov	tempH,w
	inc	dec_no	; Get 2nd digit of decade no.
	mov	w,dec_no	; from the table.
	call	decade
	mov	tempL,w
	call	d_point ; Insert decimal point.
	call	sub_it	; Divide hiB,lowB by tempH,tempL
	mov	w,indirect	; returning answer in indirect.
	jb	zs,:no_zs	; If zs = 0 AND digit = 0 then
	jz	:no_zed ; digit is a leading zero to be
:no_zs	mov	indirect,w	;	ignored. Otherwise, it's either
	ADD	indirect,#ASC_0 ; an included zero (as in 7501)
	inc	buffer	; or a non-zero digit.
	dec	fsr	; Point to next memory location.
	setb	zs	; First non-zero digit sets zs bit.
:no_zed inc	dec_no	; Next decade.
	cse	dec_no,#8	; If dec_no = 8, we're down to ones.
	jmp	:loop	; Otherwise, do next decade.
	inc	dec_no	; Update dec_no
	call	d_point ; and call d_point.
	mov	indirect,lowB	; Whatever's left belongs in ones.
	ADD	indirect,#ASC_0 ; Add offset for conversion to ASCII.
	inc	buffer	; Add 1 to character count.
	ret
; This routine performs division by iterated subtraction. It is efficient
; in this application because the dividend and divisor keep getting smaller
; as BIN_ASC runs, so the quotient is never larger than nine. A general-
; purpose division routine would be slower (and longer).

sub_it	clr	indirect	; Clear to track no. of subtractions.
:loop	sub	lowB,tempL	; Subtract LSB.
	jc	:skip	; If no borrow, continue w/MSB.
	sub	hiB,#1	; Otherwise borrow from MSB.
	jc	:skip	; If borrow causes a carry, then
	inc	hiB	; add numbers back and return.
	add	lowB,tempL
	ret
:skip	sub	hiB,tempH	; Subtract MSB.
	jc	:skip2	; If no borrow, subtract again.
	ADD	lowB,tempL	; Otherwise, undo the subtraction
	snc	; by adding entire 16-bit no.
	inc	hiB	; back together and return.
	ADD	hiB,tempH
	ret
:skip2	inc	indirect	; No borrow, so do it again.
	jmp	:loop

; This routine adds a decimal point in the location set by "fix" in the
; equates at the beginning of the program. The location of the decimal point
; is in front of the "fix"ed digit, numbered starting with 0. If you fix the
; point at 0, the first (0th) character in the string produced by BIN_ASC
; will be a decimal point. If you don't want a decimal point, either move
; it out of range (fix = 6), or delete this routine and the "call d_point"
; in the body of BIN_ASC above.

d_point cse	dec_no,#fix*2+1
	ret
	setb	zs
	mov	indirect,#ASC_dp
	inc	buffer
	dec	fsr
	ret

