When it comes to crunching numbers, computers are far superior to humans. All computing devices contain an Arithmetic Logic Unit, which is responsible for performing mathematical operations at lightningfast speeds. A set of registers input data into the ALU on which the ALU performs operations based on the instructions it receives.
We would recommend you to read our previous article on data transfer instructions in 8051 to get a better idea of the components of instructions and how they execute in 8051. In this article, we will be studying the arithmetic instructions of the 8051 microcontroller. The PSW register plays a vital role in these operations as they have special flag bits to gain additional information about the results of these operations.
List of Arithmetic instructions of 8051
The table given below lists all the arithmetic operations offered by the 8051. We will be talking about each operation in great depth in the subsequent sections. In the table given below [A]=Accumulator; [Rn]=Registers in register bank; [DPTR]=Data Pointer; [B]=Special purpose register used in multiplication and division operations.
Operation  Opcode  Operand  Description 
Addition

ADD  A, Rn  [A]<[A]+[Rn] 
ADD  A, Address  [A]<[A]+[ Data at Address]  
ADD  A, @Rn  [A]<[A]+[ Data at Address pointed by Rn ]  
ADD  A, #data  [A]<[A]+[Data]  
ADDC  A, Rn  [A]<[A]+[Rn]+[Carry flag]  
ADDC  A, Address  [A]<[A]+[ Data at Address]+[Carry flag]  
ADDC  A, @Rn  [A]<[A]+[ Data at Address pointed by Rn]+[Carry flag]  
ADDC  A, #data  [A]<[A]+[Data]]+[Carry flag]  
Subtraction

SUBB  A, Rn  [A]<[A][Rn] 
SUBB  A, Address  [A]<[A][ Data at Address]  
SUBB  A, @Rn  [A]<[A][ Data at Address pointed by Rn ]  
SUBB  A, #data  [A]<[A][Data]  
Increment

INC  A  [A]<[A+1] 
INC  Rn  [Rn]<[Rn+1]  
INC  Address  [Data at Address]<[Data at Address+1]  
INC  @Rn  [Data at Address pointed by register]<[Data at Address pointed by register+1]  
INC  DPTR  [DPTR]<[DPTR +1]  
Decrement

DEC  A  [A]<[A1] 
DEC  Rn  [Rn]<[Rn1]  
DEC  Address  [Data at Address]<[Data at Address1]  
DEC  @Rn  [Data at Address pointed by register]<[Data at Address pointed by register1]  
Multiplication  MUL  A,B  [A]<[A]*[B] 
Division  DIV  A,B  [A]<[A]/[B] 
Decimal adjust  DA  A  Coverts binary addition to BCD 
Addition operation
The ADD (addition without carry) and ADDC (addition with carry) opcodes are used to perform addition. For the addition operation to occur, the destination operand should always be the accumulator, whereas the source operand can be a memory address, data, or a register.
Given below is a list of addition opcodes with their description.
Opcode

Operand

Description

Size

Execution time

Flags affected  
Carry  Overflow  Auxilary carry  
ADD

A, Rn  Adds the value stored in Rn with that of the accumulator and stores the result in the accumulator  1 byte  12 clock cycles  yes  yes  yes 
A, Address  Adds the value at an address with that of the accumulator and stores the result in the accumulator  2 bytes  12 clock cycles  yes  yes  yes  
A, @Rn  Uses the value stored in Rn as an address and adds the data at that address with that of the accumulator and stores the result in the accumulator  1 byte  12 clock cycles  yes  yes  yes  
A, #data  Adds the data given by the programmer with the value stored in the accumulator and stores the result in the accumulator  2 bytes  12 clock cycles  yes  yes  yes  
ADDC

A, Rn  Adds the value stored in Rn with that of the accumulator. It adds the carry bit to the result and then stores the result in the accumulator  1 byte  12 clock cycles  yes  yes  yes 
A, Address  Adds the value stored at a given address with that of the accumulator. It adds the carry bit to the result and then stores the result in the accumulator  2 bytes  12 clock cycles  yes  yes  yes  
A, @Ri  Uses the data stored in Rn as an address and adds the value at that address with the accumulator. It adds the carry bit to the result and then stores the result in the accumulator  1 byte  12 clock cycles  yes  yes  yes  
A,#Data  Adds the value given by the programmer with that of the accumulator. It adds the carry bit to the result and then stores the result in the accumulator  2 bytes  12 clock cycles  yes  yes  yes 
Examples
Addition of unsigned numbers
The addition of unsigned numbers is restricted only to positive numbers. The addition of signed numbers (Negative numbers) is represented by 2’s complement, as shown in the next section.
MOV A, #0F5H; Moves 0F5H(1111 0101B) to accumulator ADD A, #0BH; Adds 0BH(0000 1011B) to the value stored in the accumulator
The final result stored in the accumulator is (0000 0000B) with the carry flag in PSW set to 1 and auxiliary carry set to one as there is a carry transferred from D3 to D4. The parity bit is set to zero as the number of 1’s in the accumulator is even (zero). As the carry flag is 1, the final result is 100H. (If this confuses you, you can brush up your concepts on the PSW register here.)
In the case of 8bit addition, the carry does not need to be propagated, but for 16bit addition, this becomes important. If the carry is not added during the operation, the results could be wrong; hence the ADDC command is used while performing these operations. Let us look at a small example of adding two 16bit numbers 3CE7H and 3B8DH and placing the results in R6 (Lower 8 bits) and R7 (Upper 8 bits).
CLR C; makes carry flag=0 MOV A, #OE7H; loads the lower byte E7H to the accumulator ADD A, #8DH; adds the low byte 74H to the accumulator. This sets the carry flag to 1 (1110 0111B(E7H)+0111 0101B(74H)=0101 1100B(74H) and CY=1) MOV R6, A; saves the lower byte of the sum in R6 MOV A, #3CH; loads the higher byte into the accumulator ADDC A, #3BH; adds 3BH with the value in the accumulator. It also adds the carry from the previous addition to this(3BH + 3CH + 1 = 78) MOV R7,A; saves the higher byte of the sum to R7
Signed addition
The 8051 has an 8bit architecture, so it can store an unsigned number from 0 to 255. So you might be wondering what about signed numbers. Some programs need calculations using negative numbers. The way this is done is by using 7 bits of the registers as magnitude bits and the 8th bit as the sign bit.
If the 8th bit is ‘1’, then it signifies that the given number is negative, and if it is ‘0’, then it denotes a positive number.
Also, when the number has the MSB as one, the magnitude of the negative number is written in 2’s complement format. You can brush up on the concepts of 2’s complement here.
All of this can get a little complicated, so let us look at an example to simplify things a bit.
Let us look at 0F5H, which is 1111 0101 in binary. Now in case of an unsigned number, it represents 245. But as you might see that this number has its MSB bit set to one, which means that when we consider it as a signed number, this number is negative.
Well, in this case, 0F5H is 11. As mentioned earlier, the 8th bit is responsible for the sign. So 0F5H is negative. And the 2’s complement of 111 0101B (the binary equivalent of 0F5H) is 000 1011B, which is 11. So 0F5H is actually representing 11 and 245 at the same time.
It is the responsibility of the programmer to look at the carry and overflow flags in the PSW register to determine the correct answer. Now let’s revisit the example on the addition of unsigned numbers.
MOV A, #0F5H; Moves 0F5H(1111 0101B) to accumulator ADD A, #0BH; Adds 0BH(0000 1011B) to the value stored in the accumulator
But if we were dealing with signed numbers, then 0F5H is 11 and 0BH is 11; therefore, the answer should be 00H, which is, in fact, true as the accumulator holds 00H. So as a programmer, it is important to understand the bits in the PSW register to get the result you want.
Overflow flag
The overflow flag is another flag in the PSW register, which is used to alert the programmer if an arithmetic calculation has not provided the correct result. As mentioned earlier in the case of signed numbers, the 8051 uses 7 bits to represent the magnitude of numbers. This provides a range from +127 to 128. If the result of an arithmetic operation is out of this range, then the overflow bit is raised to tell the programmer that the result is incorrect.
Let us look at an example to gain a better insight into this issue.
MOV A,#60H; Moves 60H into the accumulator(96 in decimal) MOV R1,#46H; Moves 46H into the accumulator(70 in decimal) ADD A,R1; Adds the value stored in the accumulator with register R1 and stores the result in the accumulator
Now the final value which is stored in the accumulator is A6H (1010 0110B), which is both 166 and 90 in decimal. If you are performing unsigned addition, then the result is 166 or A6H is perfect.
But in the case of signed addition, A6H is 90, and this is a wrong answer. So to tell the programmer that the result is incorrect, the microcontroller raises the overflow flag. The overflow flag is raised whenever a number is greater than the specified range, which can be stored in the accumulator in this case (+127 to 128).
There are two conditions when the overflow flag is set
 When there is carry from D6 to D7, but no carryout from D7.
 When there is a carryout from D7, but no carry from D6 to D7.
Subtraction operation
Opcode

Operand

Description

Size

Execution time

Flags affected  
Carry  Overflow  Auxilary carry  
SUBB

A, Rn  Subtracts the value stored in Rn with that of the accumulator and stores the result in the accumulator  1 byte  12 clock cycles  yes  yes  yes 
A, Address  Subtracts the value at an address with that of the accumulator and stores the result in the accumulator  2 bytes  12 clock cycles  yes  yes  yes  
A, @Rn  Uses the value stored in Rn as an address and subtracts the data at that address with that of the accumulator and stores the result in the accumulator  1 byte  12 clock cycles  yes  yes  yes  
A, #data  Subtracts the data given by the programmer with the value stored in the accumulator and stores the result in the accumulator  2 bytes  12 clock cycles  yes  yes  yes 
Examples
In the case of most microcontrollers, there are two commands f0r subtraction; subtraction without borrow and subtraction with borrow. But the 8051 microcontroller has only one command; SUBB (subtraction without borrow).
So the carry flag has to be adjusted to perform subtraction with borrow.
Subtraction with the carry flag set to 0
There are three operations performed by the CPU to perform subtraction:
 Take 2’s complement of the subtrahend (source operand)
 Add to the minuend (A)
 Invert the carry
Once these three operations are performed, the accumulator shows the result of the subtraction operation. Let us look at an example to get a better understanding of the whole process.
CLR C ; makes CY=0 MOV A, #3FH; loads 3FH into A MOV R3, #23H; loads 23H into R3 SUBB A, R3; performs AR3 and places the result in the accumulator
If the carry flag is 0 after the execution of the SUBB command, then the result is positive and negative if the value is 1.
Subtraction with the carry flag set to 1
In the case of 8bit subtraction operations, the carry is not required, but for multibit operations, it becomes very important to keep the carry bit in check. Let us look at an example in which we subtract 1296H from 2762H.
CLRC MOVA, #62H; moves the lower nibble of minuend(62H) to the accumulator SUBB, #96H; subtracts the lower nibble of the subtrahend from the accumulator(as 96H is greater than 62H there is a carry from the higher nibble) cy=1) MOV R6,A; stores the lower nibble result in R6 MOVA,#27H; moves the upper nibble of minuend(27H) to the accumulator SUBBA,#12H; subtracts the lower nibble of the subtrahend(12H) from the accumulator(as the CY bit is 1 the microcontroller performs 27H12H1) MOVR7,A; Stores the result in R7
Increment operation
Opcode

Operand

Description

Size

Execution time

Flags affected  
Carry  Overflow  Auxilary carry  
INC

A  Increases the value stored in the accumulator by one  1 byte  12 clock cycles  no  no  no 
Rn  Increases the value stored in Rn register by one  1 byte  12 clock cycles  no  no  no  
Address  Adds one to the data stored at the specified address  2 bytes  12 clock cycles  no  no  no  
@Rn  Uses the value stored in Rn as an address and adds one to the data stored at that memory location  1 byte  12 clock cycles  no  no  no  
DPTR  Increases the value of DPTR by 1  1 byte  24 clock cycles  no  no  no 
Decrement operation
Opcode

Operand

Description

Size

Execution time

Flags affected  
Carry  Overflow  Auxilary carry  
DEC

A  Decreases the value stored in the accumulator by one  1 byte  12 clock cycles  no  no  no 
Rn  Decreases the value stored in Rn register by one  1 byte  12 clock cycles  no  no  no  
Address  Subtracts one from the data stored at the specified address  2 bytes  12 clock cycles  no  no  no  
@Rn  Uses the value stored in Rn as an address and subtracts one from the data stored at that memory location  1 byte  12 clock cycles  no  no  no 
Multiplication operation
Opcode  Operand  Description  Size  Execution time  Flags affected  
MUL  AB  Multiplies the values in registers A and B  1 byte  48 clock cycles  carry flag =0  the overflow flag is affected  The auxiliary carry flag =0 
The MUL instruction is used to perform multiplication of two 8 bit numbers. Both the operands should be placed in registers A and B to perform multiplication operation. The result from the multiplication operation is also stored in A (Lower 8 bits) and B (Upper 8 bits).
Example
MOV A, #25H; loads 25H to the accumulator MOV B, #65H; loads 65H into register B MUL AB; multiplies 25H * 65H =E99 where A = 99H, B = OEH
Division operation
Opcode  Operand  Description  Size  Execution time  Flags affected  
DIV  AB  Divides the values in registers A by B  1 byte  48 clock cycles  carry flag = 0  the overflow flag is affected  The auxiliary carry flag is not affected 
During the DIV operation, the numerator is stored in the accumulator, and the denominator is stored in register B. Once the operation is performed, the quotient is placed in A, and the remainder is stored in B.
MOV A, #95H; Moves 95H into the accumulator MOV B, #10H; Moves 10H into register B DIV AB; performs A/B and stores the quotient in A and remainder in B
Decimal Adjust
You might be familiar with the binary and hexadecimal formats of numbering, but there is another format that is widely used; the binary coded decimal or BCD. This format represents numbers in a different manner. For example, 11 in binary is 1011, but in BCD, it is 0001 0001. In BCD code, counting resets after 1001. Here’s a table to help you understand better.
When it comes to adding BCD numbers in microcontrollers, normal binary addition will give the wrong results. So to solve this issue, microcontrollers use the DA command, which converts the binary results to BCD. The DA command can take only one operand; A.
Example
MOV A,#47H; A=47H first BCD operand(0100 0111 BCD) MOV B, #25H ;B=25 second BCD operand(0010 0101 BCD) ADD A,B ;hex addition (A=6CH) DA A; adjusts for BCD addition (A=72H)
We hope that reading this article has helped you understand the arithmetic instructions in the 8051 microcontroller. If you have any issues with any of the examples, feel free to ask them in the comments, and we will get back to you in a jiffy.