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:
dear,
I look for a converter code (binary to ASCII) and i think that the code of Dontronics and Scott Edwards is good for me.
But the instruction "ADD indirect,#ASC_0" & "ADD lowB,tempL" are used. I don't uderstand and i don't find this instruction in the data sheet of Microchip...
I use a PIC17C756, could you help me, please?
Sébastien Desmet
Comments:
; This routine accepts a 16-bit number in hiB, lowB and converts it into
; a null terminated ASCII text string located at the address "buffer."
; The routine destroys the contents of hiB, lowB.
PRDEC:
clrf flags ; Clear zs flag.
clrf dec_no ; Clear decade no.
movlw buffer ; get buffer addr
movwf FSR ; set-up indirect adressing
prdeclp:
movf dec_no,w
call decade ; table lookup
movwf tempH ; save it
incf dec_no,f ; Get 2nd digit of decade no.
movf dec_no,w ; incremented decade counter
call decade ; table lookup
movwf tempL ; save it
; call d_point ; Insert decimal point.
call sub_it ; Divide hiB,lowB by tempH,tempL
movf INDF,w ; get the result
btfsc flags,0 ; If zs = 0 AND digit = 0 then
goto prdecnzs
btfsc STATUS,2 ; digit is a leading zero to be
goto prdecnz
prdecnzs: ;no leading 0 supression
movwf INDF
movlw a'0'
addwf INDF,f ; add ascii '0'
incf FSR,f ; Point to next memory location.
bsf flags,0 ; First non-zero digit sets zs bit.
prdecnz:
incf dec_no,f ; Next decade.
movlw 0x08 ; If dec_no = 8, we're down to ones.
subwf dec_no,w
btfss STATUS,2
goto prdeclp ; Otherwise, do next decade.
incf dec_no,f ; Update decade number for d_point.
; call d_point ; Decimal point here?
movf lowB,w
movwf INDF
movlw a'0'
addwf INDF,f ; add ascii '0'
incf FSR,f
movlw 0x00
movwf INDF ; null terminated
call PRLINE
retlw 0x00
; 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 INDF ; Clear to track no. of subtractions.
sub_it_loop:
movf tempL,w ; Subtract LSB.
subwf lowB,f
btfsc STATUS,0 ; If no borrow, continue w/MSB.
goto sub_it_skip
movlw 0x01 ; Otherwise borrow from MSB.
subwf hiB,f
btfsc STATUS,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 0x00
sub_it_skip:
movf tempH,w ; Subtract MSB.
subwf hiB,f
btfsc STATUS,0 ; If no borrow, subtract again.
goto sub_it_skip2
movf tempL,w
addwf lowB,f ; Otherwise, undo the subtraction
btfsc STATUS,0 ; by adding entire 16-bit no.
incf hiB,f ; back together and return.
movf tempH,w
addwf hiB,f
retlw 0x00
sub_it_skip2
incf INDF,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 STATUS,2
retlw 0x00
bsf flags,0
movlw a'.'
movwf INDF
incf FSR,f
retlw 0x00
; Table consisting of values 10,000, 1000, 100, 10, and 1.
decade addwf PCL,f
retlw d'39' ; 10,000
retlw d'16'
retlw d'3' ; 1,000
retlw d'232'
retlw d'0' ; 100
retlw d'100'
retlw d'0' ; 10
retlw d'10'
retlw d'0' ; 1
retlw d'1'