; 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".

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

             P = pic16c55
;  device pic16c55,xt_osc,wdt_off,protect_off
 reset start
	org	0

; Table consisting of values 10,000, 1000, 100, 10, and 1.

decade	add	02, W
	retw	#39
	retw	#16
	retw	#3
	retw	#232
	retw	#0
	retw	#100
	retw	#0
	retw	#10
	retw	#0
	retw	#1

start	mov	W, #$FF	; To see routines output, either
	mov	hiB, W
	mov	W, #$FF	; use the PSIM PIC simulator, or
	mov	lowB, W
	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. The routine destroys the contents of
; hiB, lowB. If this value is required elsewhere, make a copy.

BIN_ASC	clr	buffer	; Clear char count (item 0 in buf).
	clr	flags	; Clear zs flag.
	clr	dec_no	; Clear decade no.
	mov	W, #buffer-1	; Reserve 0th byte of buffer for count.
	mov	fsr, W
BIN_ASC_loop
	test	dec_no
	call	decade	; from the lookup table.
	mov	tempH, W
	inc	dec_no	; Get 2nd digit of decade no.
	test	dec_no
	call	decade
	mov	tempL, W
	call	d_point	; Insert decimal point.
	call	sub_it	; Divide hiB,lowB by tempH,tempL
	test	indirect
	snb	flags.0	; If zs = 0 AND digit = 0 then
	jmp	BIN_ASC_no_zs
	snb	3.2	; digit is a leading zero to be
	jmp	BIN_ASC_no_zed
BIN_ASC_no_zs
	mov	indirect, W
             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	flags.0	; First non-zero digit sets zs bit.
BIN_ASC_no_zed
	inc	dec_no	; Next decade.
	mov	W, #8	; If dec_no = 8, we're down to ones.
	sub	dec_no, W
	sb	3.2
	jmp	BIN_ASC	; Otherwise, do next decade.
	inc	dec_no	; Update decade number for d_point.
	call	d_point	; Decimal point here?
	test	lowB
	mov	indirect, W
             ADD indirect,#ASC_0 	; Add offset for conversion to ASCII.
	inc	buffer	; Add 1 to character count.
	retw	#0h

; 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.
sub_it_loop	test	tempL	; Subtract LSB.
	sub	lowB, W
	snb	3.0	; If no borrow, continue w/MSB.
	jmp	sub_it_skip
	mov	W, #1	; Otherwise borrow from MSB.
	sub	hiB, W
	snb	3.0	; If borrow causes a carry, then
	jmp	sub_it_skip
	inc	hiB	; add numbers back and return.
	test	tempL
	add	lowB, W
	retw	#0h
sub_it_skip	test	tempH	; Subtract MSB.
	sub	hiB, W
	snb	3.0	; If no borrow, subtract again.
	jmp	sub_it_skip2
             ADD lowB,tempL		; Otherwise, undo the subtraction
	snb	3.0	; by adding entire 16-bit no.
	inc	hiB	; back together and return.
             ADD hiB,tempH
	retw	#0
sub_it_skip2	inc	indirect	; No borrow, so do it again.
	jmp	sub_it_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	mov	W, #fix*2+1
	sub	dec_no, W
	sb	3.2
	retw	#0h
	setb	flags.0
	mov	W, #ASC_dp
	mov	indirect, W
	inc	buffer
	dec	fsr
	retw	#0h