View Course Path

Arithmetic instructions in 8051 – with examples

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 lightning-fast 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]<-[A-1]
DEC Rn [Rn]<-[Rn-1]
DEC Address [Data at Address]<-[Data at Address-1]
DEC @Rn [Data at Address pointed by register]<-[Data at Address pointed by register-1]
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.

Memory to memory arithmetic operations cannot be performed in the 8051.

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 8-bit addition, the carry does not need to be propagated, but for 16-bit 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 16-bit 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 8-bit 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

  1. When there is carry from D6 to D7, but no carryout from D7.
  2. 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.

Here’s an interesting fact. Most 8-bit microcontrollers don’t have a dedicated subtraction unit in the ALU. They use the circuitry meant for addition to perform the subtraction operation. To perform subtraction using addition circuitry, the 2’s complement of the subtrahend (source) is performed, and the result is added to the accumulator {A+(-source(2’s complement))}

Subtraction with the carry flag set to 0

There are three operations performed by the CPU to perform subtraction:

  1. Take 2’s complement of the subtrahend (source operand)
  2. Add to the minuend (A)
  3. 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 A-R3 and places the result in the accumulator

subtaction_in_8051
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.

Note that the result of the SUBB operation is in 2’s complement format in the case of negative numbers. To change this, the CPL (complement) and INC (increment) command is used.

Subtraction with the carry flag set to 1

In the case of 8-bit subtraction operations, the carry is not required, but for multi-bit 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 27H-12H-1)
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).

In the multiplication operation, the carry flag is always cleared, and the overflow flag is set to 1 if the value is out of range.

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
The division operation always sets CY flag to 0 and overflow flag to 0 if the denominator is not 0. If the denominator is 0, then the overflow bit is set to 1 to show an error.

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.

binary to bcd equivalent table

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.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.