;**** A P P L I C A T I O N N O T E A V R 2 0 4 ************************ ;* ;* Title: BCD Arithmetics ;* Version: 1.1 ;* Last updated: 97.07.04 ;* Target: AT90Sxxxx (All AVR Devices) ;* ;* Support E-mail: avr@atmel.com ;* ;* DESCRIPTION ;* This Application Note lists subroutines for the following Binary Coded ;* Decimal arithmetic applications: ;* ;* Binary 16 to BCD Conversion (special considerations for AT90Sxx0x) ;* Binary 8 to BCD Conversion ;* BCD to Binary 16 Conversion ;* BCD to Binary 8 Conversion ;* 2-Digit BCD Addition ;* 2-Digit BCD Subtraction ;* ;*************************************************************************** .include "8515def.inc" rjmp RESET ;reset handle ;*************************************************************************** ;* ;* "bin2BCD16" - 16-bit Binary to BCD conversion ;* ;* This subroutine converts a 16-bit number (fbinH:fbinL) to a 5-digit ;* packed BCD number represented by 3 bytes (tBCD2:tBCD1:tBCD0). ;* MSD of the 5-digit number is placed in the lowermost nibble of tBCD2. ;* ;* Number of words :25 ;* Number of cycles :751/768 (Min/Max) ;* Low registers used :3 (tBCD0,tBCD1,tBCD2) ;* High registers used :4(fbinL,fbinH,cnt16a,tmp16a) ;* Pointers used :Z ;* ;*************************************************************************** ;***** Subroutine Register Variables .equ AtBCD0 =13 ;address of tBCD0 .equ AtBCD2 =15 ;address of tBCD1 .def tBCD0 =r13 ;BCD value digits 1 and 0 .def tBCD1 =r14 ;BCD value digits 3 and 2 .def tBCD2 =r15 ;BCD value digit 4 .def fbinL =r16 ;binary value Low byte .def fbinH =r17 ;binary value High byte .def cnt16a =r18 ;loop counter .def tmp16a =r19 ;temporary value ;***** Code bin2BCD16: ldi cnt16a,16 ;Init loop counter clr tBCD2 ;clear result (3 bytes) clr tBCD1 clr tBCD0 clr ZH ;clear ZH (not needed for AT90Sxx0x) bBCDx_1:lsl fbinL ;shift input value rol fbinH ;through all bytes rol tBCD0 ; rol tBCD1 rol tBCD2 dec cnt16a ;decrement loop counter brne bBCDx_2 ;if counter not zero ret ; return bBCDx_2:ldi r30,AtBCD2+1 ;Z points to result MSB + 1 bBCDx_3: ld tmp16a,-Z ;get (Z) with pre-decrement ;---------------------------------------------------------------- ;For AT90Sxx0x, substitute the above line with: ; ; dec ZL ; ld tmp16a,Z ; ;---------------------------------------------------------------- subi tmp16a,-$03 ;add 0x03 sbrc tmp16a,3 ;if bit 3 not clear st Z,tmp16a ; store back ld tmp16a,Z ;get (Z) subi tmp16a,-$30 ;add 0x30 sbrc tmp16a,7 ;if bit 7 not clear st Z,tmp16a ; store back cpi ZL,AtBCD0 ;done all three? brne bBCDx_3 ;loop again if not rjmp bBCDx_1 ;*************************************************************************** ;* ;* "bin2BCD8" - 8-bit Binary to BCD conversion ;* ;* This subroutine converts an 8-bit number (fbin) to a 2-digit ;* BCD number (tBCDH:tBCDL). ;* ;* Number of words :6 + return ;* Number of cycles :5/50 (Min/Max) + return ;* Low registers used :None ;* High registers used :2 (fbin/tBCDL,tBCDH) ;* ;* Included in the code are lines to add/replace for packed BCD output. ;* ;*************************************************************************** ;***** Subroutine Register Variables .def fbin =r16 ;8-bit binary value .def tBCDL =r16 ;BCD result MSD .def tBCDH =r17 ;BCD result LSD ;***** Code bin2bcd8: clr tBCDH ;clear result MSD bBCD8_1:subi fbin,10 ;input = input - 10 brcs bBCD8_2 ;abort if carry set inc tBCDH ;inc MSD ;--------------------------------------------------------------------------- ; ;Replace the above line with this one ; ;for packed BCD output ; subi tBCDH,-$10 ;tBCDH = tBCDH + 10 ;--------------------------------------------------------------------------- rjmp bBCD8_1 ;loop again bBCD8_2:subi fbin,-10 ;compensate extra subtraction ;--------------------------------------------------------------------------- ; ;Add this line for packed BCD output ; add fbin,tBCDH ;--------------------------------------------------------------------------- ret ;*************************************************************************** ;* ;* "BCD2bin16" - BCD to 16-Bit Binary Conversion ;* ;* This subroutine converts a 5-digit packed BCD number represented by ;* 3 bytes (fBCD2:fBCD1:fBCD0) to a 16-bit number (tbinH:tbinL). ;* MSD of the 5-digit number must be placed in the lowermost nibble of fBCD2. ;* ;* Let "abcde" denote the 5-digit number. The conversion is done by ;* computing the formula: 10(10(10(10a+b)+c)+d)+e. ;* The subroutine "mul10a"/"mul10b" does the multiply-and-add operation ;* which is repeated four times during the computation. ;* ;* Number of words :30 ;* Number of cycles :108 ;* Low registers used :4 (copyL,copyH,mp10L/tbinL,mp10H/tbinH) ;* High registers used :4 (fBCD0,fBCD1,fBCD2,adder) ;* ;*************************************************************************** ;***** "mul10a"/"mul10b" Subroutine Register Variables .def copyL =r12 ;temporary register .def copyH =r13 ;temporary register .def mp10L =r14 :Low byte of number to be multiplied by 10 .def mp10H =r15 ;High byte of number to be multiplied by 10 .def adder =r19 ;value to add after multiplication ;***** Code mul10a: ;***** multiplies "mp10H:mp10L" with 10 and adds "adder" high nibble swap adder mul10b: ;***** multiplies "mp10H:mp10L" with 10 and adds "adder" low nibble mov copyL,mp10L ;make copy mov copyH,mp10H lsl mp10L ;multiply original by 2 rol mp10H lsl copyL ;multiply copy by 2 rol copyH lsl copyL ;multiply copy by 2 (4) rol copyH lsl copyL ;multiply copy by 2 (8) rol copyH add mp10L,copyL ;add copy to original adc mp10H,copyH andi adder,0x0f ;mask away upper nibble of adder add mp10L,adder ;add lower nibble of adder brcc m10_1 ;if carry not cleared inc mp10H ; inc high byte m10_1: ret ;***** Main Routine Register Variables .def tbinL =r14 ;Low byte of binary result (same as mp10L) .def tbinH =r15 ;High byte of binary result (same as mp10H) .def fBCD0 =r16 ;BCD value digits 1 and 0 .def fBCD1 =r17 ;BCD value digits 2 and 3 .def fBCD2 =r18 ;BCD value digit 5 ;***** Code BCD2bin16: andi fBCD2,0x0f ;mask away upper nibble of fBCD2 clr mp10H mov mp10L,fBCD2 ;mp10H:mp10L = a mov adder,fBCD1 rcall mul10a ;mp10H:mp10L = 10a+b mov adder,fBCD1 rcall mul10b ;mp10H:mp10L = 10(10a+b)+c mov adder,fBCD0 rcall mul10a ;mp10H:mp10L = 10(10(10a+b)+c)+d mov adder,fBCD0 rcall mul10b ;mp10H:mp10L = 10(10(10(10a+b)+c)+d)+e ret ;*************************************************************************** ;* ;* "BCD2bin8" - BCD to 8-bit binary conversion ;* ;* This subroutine converts a 2-digit BCD number (fBCDH:fBCDL) to an ;* 8-bit number (tbin). ;* ;* Number of words :4 + return ;* Number of cycles :3/48 (Min/Max) + return ;* Low registers used :None ;* High registers used :2 (tbin/fBCDL,fBCDH) ;* ;* Modifications to make the routine accept a packed BCD number is indicated ;* as comments in the code. If the modifications are used, fBCDH shall be ;* loaded with the BCD number to convert prior to calling the routine. ;* ;*************************************************************************** ;***** Subroutine Register Variables .def tbin =r16 ;binary result .def fBCDL =r16 ;lower digit of BCD input .def fBCDH =r17 ;higher digit of BCD input ;***** Code BCD2bin8: ;-------------------------------------------------------------------------- ;| ;For packed BCD input, add these two lines ;| mov tbin,fBCDH ;copy input to result ;| andi tbin,$0f ;clear higher nibble of result ;-------------------------------------------------------------------------- BCDb8_0:subi fBCDH,1 ;fBCDH = fBCDH - 1 brcs BCDb8_1 ;if carry not set ;-------------------------------------------------------------------------- ;| ;For packed BCD input, replace the above ;| ;two lines with these. ;| subi fBCDH,$10 ;MSD = MSD - 1 ;| brmi BCDb8_1 ;if Zero flag not set ;-------------------------------------------------------------------------- subi tbin,-10 ; result = result + 10 rjmp BCDb8_0 ; loop again BCDb8_1:ret ;else return ;*************************************************************************** ;* ;* "BCDadd" - 2-digit packed BCD addition ;* ;* This subroutine adds the two unsigned 2-digit BCD numbers ;* "BCD1" and "BCD2". The result is returned in "BCD1", and the overflow ;* carry in "BCD2". ;* ;* Number of words :19 ;* Number of cycles :17/20 (Min/Max) ;* Low registers used :None ;* High registers used :3 (BCD1,BCD2,tmpadd) ;* ;*************************************************************************** ;***** Subroutine Register Variables .def BCD1 =r16 ;BCD input value #1 .def BCD2 =r17 ;BCD input value #2 .def tmpadd =r18 ;temporary register ;***** Code BCDadd: ldi tmpadd,6 ;value to be added later add BCD1,BCD2 ;add the numbers binary clr BCD2 ;clear BCD carry brcc add_0 ;if carry not clear ldi BCD2,1 ; set BCD carry add_0: brhs add_1 ;if half carry not set add BCD1,tmpadd ; add 6 to LSD brhs add_2 ; if half carry not set (LSD <= 9) subi BCD1,6 ; restore value rjmp add_2 ;else add_1: add BCD1,tmpadd ; add 6 to LSD add_2: swap tmpadd add BCD1,tmpadd ;add 6 to MSD brcs add_4 ;if carry not set (MSD <= 9) sbrs BCD2,0 ; if previous carry not set subi BCD1,$60 ; restore value add_3: ret ;else add_4: ldi BCD2,1 ; set BCD carry ret ;*************************************************************************** ;* ;* "BCDsub" - 2-digit packed BCD subtraction ;* ;* This subroutine subtracts the two unsigned 2-digit BCD numbers ;* "BCDa" and "BCDb" (BCDa - BCDb). The result is returned in "BCDa", and ;* the underflow carry in "BCDb". ;* ;* Number of words :13 ;* Number of cycles :12/17 (Min/Max) ;* Low registers used :None ;* High registers used :2 (BCDa,BCDb) ;* ;*************************************************************************** ;***** Subroutine Register Variables .def BCDa =r16 ;BCD input value #1 .def BCDb =r17 ;BCD input value #2 ;***** Code BCDsub: sub BCDa,BCDb ;subtract the numbers binary clr BCDb brcc sub_0 ;if carry not clear ldi BCDb,1 ; store carry in BCDB1, bit 0 sub_0: brhc sub_1 ;if half carry not clear subi BCDa,$06 ; LSD = LSD - 6 sub_1: sbrs BCDb,0 ;if previous carry not set ret ; return subi BCDa,$60 ;subtract 6 from MSD ldi BCDb,1 ;set underflow carry brcc sub_2 ;if carry not clear ldi BCDb,1 ; clear underflow carry sub_2: ret ;**************************************************************************** ;* ;* Test Program ;* ;* This program calls all the subroutines as an example of usage and to ;* verify correct operation. ;* ;**************************************************************************** ;***** Main Program Register variables .def temp =r16 ;temporary storage variable ;***** Code RESET: ldi temp,low(RAMEND) out SPL,temp ldi temp,high(RAMEND) out SPH,temp ;init Stack Pointer (remove for AT90Sxx0x) ;***** Convert 54,321 to 2.5-byte packed BCD format ldi fbinL,low(54321) ldi fbinH,high(54321) rcall bin2BCD16 ;result: tBCD2:tBCD1:tBCD0 = $054321 ;***** Convert 55 to 2-byte BCD ldi fbin,55 rcall bin2BCD8 ;result: tBCDH:tBCDL = 0505 ;***** Convert $065535 to a 16-bit binary number ldi fBCD2,$06 ldi fBCD1,$55 ldi fBCD0,$35 rcall BCD2bin16 ;result: tbinH:tbinL = $ffff (65,535) ;***** Convert $0403 (43) to an 8-bit binary number ldi fBCDL,3 ldi fBCDH,4 rcall BCD2bin8 ;result: tbin = $2b (43) ;***** Add BCD numbers 51 and 79 ldi BCD1,$51 ldi BCD2,$79 rcall BCDadd ;result: BCD2:BCD1=$0130 ;***** Subtract BCD numbers 72 - 28 ldi BCDa,$72 ldi BCDb,$28 rcall BCDsub ;result: BCDb=$00 (positive result), BCDa=44 ;***** Subtract BCD numbers 0 - 90 ldi BCDa,$00 ldi BCDb,$90 rcall BCDsub ;result: BCDb=$01 (negative result), BCDa=10 forever:rjmp forever