#1 hlucheucho
; Content of "U_b" before, during & after the conversion:
; (L/H stand for LOW/HIGH - Nibble)
;
; positive argument:
; adr in out
;------------------------------
; +5 L0 D(100,000)
; +4 H0 D(10,000)
; +3 A3 L1 D(1,000)
; +2 A2 H1 D(100)
; +1 A1 L2 D(10)
; &U_b A0 D(1)
; negative argument:
; adr in out
;------------------------------
; +5 '-'
; +4 L0 D(10,000)
; +3 A3 H0 D(1,000)
; +2 A2 L1 D(100)
; +1 A1 H1 D(10)
; &U_b A0 L2 D(1)
; Content of the registers during conversion:
; (main loop, "LOOP_DIGIT")
;
; R0 ... "Nibble" Pointer
; R1 ... "Digit" Pointer
; R2 ... "Nibble" Counter
; R3 ... "Digit" Counter
; positive argument:
; R0 R1 R2 R3
; +1 0 5 6
; +1 +1 5 5
; +2 +2 4 4
; +3 +3 3 3
; +4 +4 2 2
; +5 +5 1 1
; negative argument:
; R0 R1 R2 R3
; 0 0 5 5
; +1 +1 4 4
; +2 +2 3 3
; +3 +3 2 2
; +4 +4 1 1
;--------------------------------------
$XREF
$NOMOD51
B DATA 0F0H
ACC DATA 0E0H
NAME BCD
?PR?bin2bcd?BCD SEGMENT CODE
EXTRN DATA (U_b)
PUBLIC bin2bcd
RSEG ?PR?bin2bcd?BCD
USING 0
bin2bcd:
MOV A,U_b
JNB ACC.7,POSITIVE
CLR C ; negative argument
CLR A
SUBB A,U_b+3
MOV U_b+3,A
CLR A
SUBB A,U_b+2
MOV U_b+2,A
CLR A
SUBB A,U_b+1
MOV U_b+1,A
CLR A
SUBB A,U_b
JNZ ERROR ; should be ZERO
MOV R3,#5 ;"Digit" Counter
MOV R1,#U_b ;"Digit" Pointer
MOV R2,#5 ;"Nibble" Counter
MOV R0,#U_b ;"Nibble" Pointer
MOV U_b+5,#0FH ; 0x0F as '-' Character
; (will be converted for
; 7-seg. LCD)
MOV A,U_b+1 ; LSB2 LN -> 0
MOV U_b,A
ANL U_b,#0FH
ANL A, #0F0H ; should be ZERO
JNZ ERROR
MOV A,U_b+3 ; LSB0 LN -> 4
MOV U_b+4,A
ANL U_b+4,#0FH
SWAP A
MOV U_b+3,A ; LSB0 HN -> 3
ANL U_b+3,#0FH
MOV A,U_b+2 ; LSB1 LN -> 2
ANL U_b+2,#0FH
SWAP A
MOV U_b+1,A ; LSB1 HN -> 1
ANL U_b+1,#0FH
CLR A
SJMP LOOP_DIV ; everything is in place now
POSITIVE:
MOV A,U_b+1
ANL A, #0F0H ; should be ZERO
JNZ ERROR
MOV A,U_b+3 ; LSB0 LN -> 5
MOV U_b+5,A
ANL U_b+5,#0FH
SWAP A
MOV U_b+4,A ; LSB0 HN -> 4
ANL U_b+4,#0FH
MOV A,U_b+2 ; LSB1 LN -> 3
MOV U_b+3,A
ANL U_b+3,#0FH
SWAP A
MOV U_b+2,A ; LSB1 HN -> 2
ANL U_b+2,#0FH
ANL U_b+1,#0FH ; LSB2 LN -> 1
MOV R3,#6 ;"Digit" Counter
MOV R1,#U_b ;"Digit" Pointer
MOV R2,#5 ;"Nibble" Counter
MOV R0,#U_b+1 ;"Nibble" Pointer
CLR A
SJMP LOOP_DIV ; everything is in place now
LOOP_DIGIT:
MOV AR0,AR1
MOV AR2,AR3
CLR A
LOOP_DIV:
XCHD A,@R0
MOV B,#10 ; DIVISOR
DIV AB
XCHD A,@R0
MOV A,B
SWAP A ; Remainder in AKKU HIGH NIBBLE
INC R0
DJNZ R2,LOOP_DIV
MOV A,@R1 ; should be ZERO
JNZ ERROR
MOV @R1,B
INC R1
DJNZ R3,LOOP_DIGIT
END1:
RET
ERROR:
MOV A,#0FH ; 0x0F as '-' Character
MOV U_b,A
MOV U_b+1,A
MOV U_b+2,A
MOV U_b+3,A
MOV U_b+4,A
MOV U_b+5,A
END2:
RET
END
union
{
unsigned char buf[6];
long in;
} idata U_b;
void bin2bcd(void);
From fmayr@electronic.tu-graz.ac.at Thu Feb 6 10:24:30 1997
Date: Wed, 05 Feb 1997 15:36:00 +0100
From: Friedrich Mayr <fmayr@electronic.tu-graz.ac.at>
To: 8051code@keil.com
Subject: Fast LONG to BCD conversion
This function was written within the Keil C51 environment
by Dr. Roehrer (Inst. f. Elektronik, TU-Graz) and me
to be called from a C - Program as: "void bin2bcd(void)"
The following global variable (must be located in the
internal RAM of the processor, "data" or "idata" ) is
used both to pass the argument (a "long" value) to the
function, as well as to receive the result (6 digits):
union
{
unsigned char buf[6];
long in;
} idata U_b;
The purpose of the code is to convert a signed long value
to BCD - code with leading sign if needed. Values exceeding
the range of 6(-:5) Digits should all be displayed by '-'.
The usage of memory was critical because no external RAM is
present in the system, so only the Accu, B, R1, R2, R3, R4
are used aside of the digit- (and argument-) buffer "U_b".
The trick applied in the function is to use a
"small" (nibble by nibble) division and the extensive
use of the '51 - opcodes XCHD and SWAP.
This results in very fast execution.
The function can easily be modified for example
to time-calculations or extended to the full "long"
range .