PIC Microcontoller Radix Math Method

Binary to ASCII, 16 bit to 5 digits with leading zero suppression and fixed decimal point

from Dontronics and Scott Edwards

Also: PIC Microcontoller Radix Math Methods

; 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      ADDWF 02, _W
             RETLW 39
             RETLW 16
             RETLW 3
             RETLW 232
             RETLW 0
             RETLW 100
             RETLW 0
             RETLW 10
             RETLW 0
             RETLW 1

start        MOVLW $FF			; To see routines output, either
             MOVWF hiB
             MOVLW $FF			; use the PSIM PIC simulator, or
             MOVWF lowB
             CALL BIN_ASC		; merge with Serout.
             GOTO $			; 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      CLRF buffer		; Clear char count (item 0 in buf).
             CLRF flags			; Clear zs flag.
             CLRF dec_no		; Clear decade no.
             MOVLW buffer-1		; Reserve 0th byte of buffer for count.
             MOVWF fsr
BIN_ASC_loop
             MOVF dec_no ,_w
             CALL decade		; from the lookup table.
             MOVWF tempH
             INCF dec_no ,F		; Get 2nd digit of decade no.
             MOVF dec_no ,_w
             CALL decade                
             MOVWF tempL
             CALL d_point		; Insert decimal point.
             CALL sub_it		; Divide hiB,lowB by tempH,tempL
             MOVF indirect ,_w
             BTFSC flags, 0		; If zs = 0 AND digit = 0 then
             GOTO BIN_ASC_no_zs
             BTFSC 3,2			; digit is a leading zero to be
             GOTO BIN_ASC_no_zed
BIN_ASC_no_zs
             MOVWF indirect
             ADD indirect,#ASC_0 	; an included zero (as in 7501)
             INCF buffer, F		; or a non-zero digit.
             DECF fsr, F		; Point to next memory location.
             BSF flags, 0		; First non-zero digit sets zs bit.
BIN_ASC_no_zed
             INCF dec_no,F		; Next decade.
             MOVLW 8			; If dec_no = 8, we're down to ones.
             SUBWF dec_no ,_w
             BTFSS 3,2
             GOTO BIN_ASC		; Otherwise, do next decade.
             INCF dec_no ,F		; Update decade number for d_point.
             CALL d_point		; Decimal point here?
             MOVF lowB ,_w
             MOVWF indirect
             ADD indirect,#ASC_0 	; Add offset for conversion to ASCII.
             INCF buffer, F		; Add 1 to character count.
             RETLW 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       CLRF indirect		; Clear to track no. of subtractions.
sub_it_loop  MOVF tempL,_w		; Subtract LSB.
             SUBWF lowB, F
             BTFSC 3,0			; If no borrow, continue w/MSB.
             GOTO sub_it_skip
             MOVLW 1			; Otherwise borrow from MSB.
             SUBWF hiB, F
             BTFSC 3,0			; If borrow causes a carry, then
             GOTO sub_it_skip
             INCF hiB, F		; add numbers back and return.
             MOVF tempL,_w               
             ADDWF lowB, F
             RETLW 0h                   
sub_it_skip  MOVF tempH,_w		; Subtract MSB.
             SUBWF hiB, F
             BTFSC 3,0			; If no borrow, subtract again.
             GOTO sub_it_skip2
             ADD lowB,tempL		; Otherwise, undo the subtraction
             BTFSC 3,0			; by adding entire 16-bit no.
             INCF hiB, F		; back together and return.
             ADD hiB,tempH
             RETLW 0                   
sub_it_skip2 INCF indirect, F		; No borrow, so do it again.
             GOTO 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      MOVLW fix*2+1              
             SUBWF dec_no ,_w
             BTFSS 3,2
             RETLW 0h                   
             BSF flags, 0
             MOVLW ASC_dp               
             MOVWF indirect
             INCF buffer, F
             DECF fsr, F
             RETLW 0h

Questions:

Comments: