1. Incrementing and Decrementing
Incrementing and Decrementing a 16 Bit Number can be described using "C"
before actually writing 8051 code.
Incrementing can be represented as:
if ( ++Low == 0 // Increment the Low eight bits
High++; // If ++Low == 0 then Increment High Byte
Which can be coded in 8051 assembler as:
inc Low ; Increment the Low eight bits
mov A,Low ; If ++Low != 0
jnz IncSkip ; Then Skip Over
inc High ; Increment the High Byte
IncSkip:
If the 16 bit variable is in a bank register, the "cjne" instruction can
be used to simplify the operation:
cjne Rlow, #0FFh, IncSkip
inc Rhigh ; If Low == 0FFh, then Increment High
IncSkip:
inc RLow ; Increment the Low 8 bits
Decrement requires the test for the low byte equal to zero and if it is,
then the high byte has to be decremented as well.
mov A,Low ; If Low != 0
jnz DecSkip ; Then Skip Over
dec High ; Decrement the High Byte
DecSkip:
inc Low ; Decrement the Low eight bits
These snippets will increment the 16 bit variable without affecting the
carry flag.
2. Addition and Subtraction
Addition and Subtraction are very easy in the 8051 with the "adc" (add
with Carry) and "subb" (subtract with Borrow) Instructions. The general
form can be expressed as:
Result = VarA + VarB
With 16 Bit the addition being:
mov A,VarA ; Add the Lower 8 Bits First
add A,VarB
mov Result,A
mov A,VarA+1 ; Add the Upper 8 Bits with Carry
adc A,VarB+1
mov Result+1,A
Subtraction is similar, but the carry flag is reset before the first subtraction
(there is no subtract without carry instruction):
mov A,VarA ; Do the Lower 8 Bits
clr C
subb A,VarB
mov Result,A
mov A,VarA+1 ; Do the Upper 8 Bits
subb A,VarB+1
mov Result+1,A
3. Multiplication
The 8 bit multiply instruction allows me to create a 16 Bit multiply based
on the polynomial multiplication equation:
Product = ( A + B ) * ( C + D )
= A*C + A*D + B*C + B*D
and knowing that a 16 bit number can be represented as the following polynominal:
16BitNumber = LowByte + ( HighByte * 0100h )
The advantages of using the code below for a 16 bit multiply is the constant
number of cycles the operation always takes.
mov Product+3,#0 ; Clear the Most Significant Byte
mov A,VarA ; Do "A*C" Multiply
mov B,VarB
mul ab
mov Product,A ; Store the 16 bit Product
mov Product+1,B
mov A,VarA ; Do "A*D" Multiply
mov B,VarB+1
mul ab
mov Product+2,B ; Store the High Byte of Product
add A,Product+1 ; Now, Add the Low Byte to previous
mov Product+1,A ; Product
jnc MulInside
inc Product+2 ; If Result is Greater than 0FFh,
mov A,Product+2 ; Increment Third Byte
jnz MulInside
inc Product+3 ; If 3rd Byte == 0 after Increment,
; then Increment High Byte
MulInside: ; Do "B*C" Multiply
mov A,VarA+1
mov B,VarB
mul ab
add A,Product+1 ; Add the Lower Byte
mov Product+1,A
mov A,B ; Add the Upper Byte
adc A,Product+2
mov Product+2,A
jnc MulLast ; If No Carry, then Don't Increment
inc Product+3 ; High Value
MulLast: ; Do "B*D Multiply
mov A,VarA+1
mov B,VarB+1
mul ab
add A,Product+2 ; Add Low 8 Bits of Product to
mov Product+2 ; the Result
mov A,B ; Add the High Byte Finally
adc A,Product+3
mov Product+3,A
Note that the product is 32 bits.
4. Division
Rather than carry out division by using repeated subtraction, I shift the
divisor until it is greater than the diviend and then shift down and subtract
if the Divisend is less than or equal to the divisor:
Count = 0; // Keep Track of the Shifting Bits
Quotient = 0;
while ( Dividend > Divisor ) {
Divisor = Divisor << 1; // Find the Shifted Divisor that is
Count++; // Greater than the Dividend
}
if ( Count != 0 ) { // Did we Actually Shift Something?
Divisor = Divisor >> 1; // Move the Divisor Back Down to
Count--; // where it can be Used
while ( Count != 0 ) { // Now, Find the Quotient
if ( Dividend >= Divisor ) { // Dividend - Divisor?
Dividend = Dividend - Divisor;
Quotient += ( 1 << Count ); // Update the Quotient with
} // the Divisor Shift Value
Divisor = Divisor >> 1; // Take Down Divisor by Power of 2
Count--;
}
}
Converting this to 8051 assembler, I came up with:
mov Count,#1 ; Keep Track of the Current Bit in
mov Count+1,#0 ; "Count" Variable
mov Quotient,#0 ; Clear the Quotient (Nothing Taken
mov Quotient+1,#0 ; Away yet)
DivLoop1: ; First Loop - See How Far to Shift
; Divisor
mov A,Dividend ; Subtract the Divisor and Check
clr C ; for the Carry Flag Set
subb A,Divisor
mov A,Dividend+1 ; Now, See what the High Value for
subb A,Divisor+1 ; Results in
jc DivEnd1 ; If Carry Set - Stop Shifting
; Divisor
mov A,Divisor ; Shift Divisor Up by 1
rlc a
mov Divisor,A
mov A,Divisor+1
rlc a
mov Divisor+1,A
mov A,Count ; "Increment" Count by Shifting
clr C ; Up
rlc Count
mov Count,A
mov A,Count+1
rlc Count
mov Count+1,A
ajmp DivLoop1 ; Repeat the operation
DivEnd1: ; Have Shifted the Divisor to
; a Value Greater than the
; Dividend, Take it Down
mov A,Count ; Did we Actually Shift?
dec A
jz DivEnd ; If Count is Still at the
clr C ; First Bit, No
mov A,Divisor+1
rrc a
mov Divisor+1,A
mov A,Divisor
rrc a
mov Divisor,A
mov A,Count+1 ; "Decrement" the Count
clr C
rrc a
mov Count+1,A
mov A,Count
rrc a
mov Count,A
DivLoop2: ; Now, Get the Count
mov A,Dividend ; Can We Take Away the
clr C ; Divisor?
sub A,Divisor
mov B,A ; Save Low 8 Bits for Later
mov A,Dividend+1 ; Take Away the High 8 Bits
subb A,Divisor+1
jc DivSkip2 ; If Carry, Dividend is Less
; than Divisor
mov Dividend,B ; Save the New Divisor
mov Dividend+1,A
mov A,Quotient ; Update the Quotient
add A,Count
mov Quotient,A
mov A,Quotient+1
add A,Count+1
mov Quotient+1,A
DivSkip2: ; Now, Take Down Divisor
; and Count
mov A,Divisor+1 ; Shift Divisor by 1 bit
clr C
rrc a
mov Divisor+1,A
mov A,Divisor
rrc a
mov Divisor,A
mov A,Count+1 ; Take down the Bit Value
clr C
rrc a
mov Count+1,A
mov A,Count
rrc a
mov Count,A
jnc DivLoop2 ; If Carry Not Loaded,
; Loop Around Again
The remainder is left in the "Dividend" Variable.
copyright © Myke Predko 1998 |