A microcontroller sequentially executes instructions but in some cases, transferring this control to another block of code becomes essential. The branching instructions in the 8051 microcontroller are responsible for performing this operation. Tasks like looping, calling delays, and conditional execution of code can be performed using these branching instructions.
But before we get into that, we would like to talk about the memory architecture of the ROM of the 8051. This is something very important and will help you understand the concepts mentioned later in the article. As we have repeated a few times in this 8051 course, the 8051 has a 16-bit address bus which gives it the ability to access 64KB of memory.
To understand this concept, let us make an analogy between a book and the memory of the 8051. So as a book is divided into pages of a certain number of words, the memory of 8051 is divided into pages of 2KB each. This concept of paging will come in handy when we discuss the concept of ACALL and AJMP. Paging or banking is a concept used to access larger address space.
Given below is a list of all the branching instructions in 8051. We will be talking about all these instructions in great detail in the coming sections so hang on and brew a cup of coffee because this can get pretty interesting.
Contents
List of Branching Instructions in 8051
Operation | Mnemonics | Description |
Call
|
ACALL Address11 | Calls a subroutine in the maximum address range of 2K bytes |
LCALL Address16 | Calls a subroutine in the maximum address range of 64K bytes | |
Return
|
RET | Returns the control from subroutine |
RETI | Returns the control from an interrupt subroutine | |
Jump
|
AJMP Address11 | Jumps to an address in a 2KB range |
LJMP Address16 | Jumps to an address in a 64KB range | |
SJMP Relative address | Jumps to an address in a 256-byte range (0 to 127 (0-7FH) range and -1 to -128 (FFH-80H). | |
JMP @A+DPTR | [DPTR]<-[DPTR+A] | |
JZ Relative address | Jumps to address when accumulator=0 | |
JNZ Relative address | Jumps to address when accumulator!=0 | |
CJNE A, Direct address, Relative address | Jumps to relative address when accumulator=data stored at a direct address | |
CJNE A, #Data,Relative address | Jumps to relative address when accumulator=data given by the programmer | |
CJNE @Rn, #Data,Relative address | Jumps to relative address when data at memory location stored in register=data given by the programmer | |
DJNZ Rn, Relative address | Decrements value in Rn and jump to relative address till Rn!=0 | |
DJNZ Direct address, Relative address | Decrements value at memory location stored in a register and jump to relative address till memory location stored in register =0 |
Call and Return instructions in 8051
In some cases, a microcontroller needs to perform the same tasks multiple numbers of times across the program, such as generating a delay. A subroutine is responsible for performing these repetitive tasks. Using subroutines saves memory and makes the program more efficient.
Instead of repeating the same few lines of code for some task you need to execute multiple times, you can just write it once and give it a label. Every time you need those lines to execute, just use the label to jump to the area where you had stored the labeled code. In essence, a subroutine is like a function, and it is placed at a different memory location then the program memory.
The call instruction is used to transfer the control from the presently executing program code to the subroutine, and the return instruction is used to return the control to the program code.
Stack and control transfer
The stack of the 8051 plays a crucial role in the transfer of control when it comes to call instructions. When the call instruction executes, the program counter increments so that it points to the next instruction. The contents of the program counter are pushed into the stack (lower byte first). After this, the program counter is loaded with the starting instruction of the subroutine transferring the control.
Once the RET instruction is encountered, the contents stored in the stack are popped back into the program counter, and the microcontroller starts executing the program code from where it had left it.
RET
instruction is used to return from interrupt subroutines and works in the same way as the call instructions.LCALL Instruction
Now that you are familiar with the working of the call instruction let us look at the LCALL instruction.
Opcode
|
Operand
|
Description
|
Size
|
Execution Time
|
Flags affected | ||
Carry | Overflow | Auxilary carry | |||||
LCALL | ADDRESS(16 BIT) | Transfers the control to the specified address(64KB) | 3 bytes | 24 clock cycles | Unaffected | Unaffected | Unaffected |
The table given above shows that the LCALL instruction can access an address of 16 bits. Due to this reason, this instruction can access any memory location in the ROM space, but this instruction takes up 3 bytes of space and can waste memory resources. To save memory, the ACALL instruction is used.
Example
ORG 0000H; Directive for starting address of the program code MOV A,#50H; Moves 50H into the accumulator MOV R0,#25H; Moves 25H into the accumulator LCALL Delay; Calls delay subroutine ADD A, R0; Adds A and R0. stores the result in A ORG 1000H; Directive for starting address of delay subroutine DELAY: NOP; Performs no operation for 1 machine cycle NOP; Performs no operation for 1 machine cycle RET; Returns control to program code
ACALL Instruction
Opcode
|
Operand
|
Description
|
Size
|
Execution Time
|
Flags affected | ||
Carry | Overflow | Auxilary carry | |||||
ACALL | ADDRESS(11 BIT) | Transfers the control to the specified address restricted to a page of 2KB | 2 bytes | 24 clock cycles | Unaffected | Unaffected | Unaffected |
The ACALL instruction is 2 bytes in size and can be used to access any address in a 2KB page. This instruction affects only 11 bits or the program counter as compared to 16 in the case of LCALL.
When this instruction is executed, the PC saves its current value on the stack, then the lower byte of the ADDRESS
is stored in the lower byte section of PC, the remaining 3 bits of ADDRESS
are stored in bits 0, 1, and 2 of PC. The bits 3-7 of the PC are empty.
Example
ORG 0000H; Directive for starting address of the program code MOV A,#50H; Moves 50H into the accumulator MOV R0,#25H; Moves 25H into the accumulator ACALL Delay; Calls delay subroutine ADD A, R0; Adds A and R0. stores the result in A ORG 0300H; Directive for starting address of delay subroutine DELAY: NOP; Performs no operation for 1 machine cycle NOP; Performs no operation for 1 machine cycle RET; Returns control to program code
RET Instruction
Opcode
|
Operand
|
Description
|
Size
|
Execution Time
|
Flags affected | ||
Carry | Overflow | Auxilary carry | |||||
RET | NONE | Returns control to the program code | 1 byte | 24 clock cycles | Unaffected | Unaffected | Unaffected |
RETI
instruction is used to return from interrupt subroutines and works in the same way as the RET instruction if used outside a subroutine. When used inside a subroutine, RETI
first enables interrupts of equal and lower priorities to the interrupt that it is used in. The program execution continues at the address that is calculated by popping the topmost 2 bytes off the stack. The most-significant-byte is popped off the stack first, followed by the least-significant-byte.Jumps in 8051
The jump instruction is also used to transfer control in the 8051 microcontroller. But unlike a Call instruction, it does not call a subroutine and jumps to an address in the same program memory. Jumps in the 8051 microcontroller are used to perform looping and conditional execution of program code. Jumps in the 8051 are of two types
- Unconditional jumps- as the name suggests these jumps do not evaluate a condition to transfer the control to another address.
- Conditional jumps- these jumps evaluate a particular condition to transfer the control to another address in the program code. All conditional jumps are short jumps.
Unconditional jumps
Opcode
|
Operand
|
Description
|
Size
|
Execution Time
|
Flags affected | ||
Carry | Overflow | Auxilary carry | |||||
LJMP | ADDRESS(16 BITS) | Transfers control within program code. The maximum range of this jump is 64KB | 3 bytes | 24 clock cycles | Unaffected | Unaffected | Unaffected |
AJMP | ADDRESS(11 BITS) | Transfers control within program code. The maximum range of this jump is 2KB | 2 bytes | 24 clock cycles | Unaffected | Unaffected | Unaffected |
SJMP | Relative Address | Uses the given address as an offset to the executing address and transfers the control to the new address. The maximum range of 256 bytes | 2 bytes | 24 clock cycles | Unaffected | Unaffected | Unaffected |
Example
MOV TMOD #0110000B ;sets counter1, mode 2,C/T=1(counts external pulses) MOV TH1, #0 ;clears TH1 register AGAIN:SETB P3.5 ;sets P3.5 as input port (all ports are configured as output ports by default) BACK: MOV A,TL1 ;sends the value in TL1 to accumulator MOV P2, A ;sends values to port 2 JNB TF1,BACK ;checks overflow condition CLR TR1 ;stops the counter CLR TF1 ;makes TF=0 SJMP AGAIN ;performs tasks infinitely
Conditional jumps in 8051
Opcode
|
Operand
|
Description
|
Size
|
Execution Time
|
Flags affected | ||
Carry | Overflow | Auxilary carry | |||||
JZ | Relative Address | Uses the given address as an offset to the executing address and transfers the control to the new address if the value in the accumulator=0. The maximum range of 256 bytes | 2 bytes | 24 clock cycles | Unaffected | Unaffected | Unaffected |
JNZ | Relative Address | Uses the given address as an offset to the executing address and transfers the control to the new address if the value in the accumulator !=0. The maximum range of 256 bytes | 2 bytes | 24 clock cycles | Unaffected | Unaffected | Unaffected |
CJNE
|
A, Direct address, Relative address | Compares the data stored in the accumulator with the data stored at the direct address. If the values are the same, then the control is transferred to the relative address | 3 bytes | 24 clock cycles | Unaffected | Unaffected | Unaffected |
A, #Data,Relative address | Compares the data stored in the accumulator with the data given by the programmer. If the values are the same, then the control is transferred to the relative address | 3 bytes | 24 clock cycles | Unaffected | Unaffected | Unaffected | |
@Rn, #Data,Relative address | Compares the data stored at the address stored in the register with the data given by the programmer. If the values are the same, then the control is transferred to the relative address | 3 bytes | 24 clock cycles | Unaffected | Unaffected | Unaffected | |
DJNZ
|
Rn, Relative address | This instruction is used for looping in 8051. It decrements the value stored in Rn and jumps to the relative address till the value in the register!=0 | 2 bytes | 24 clock cycles | Unaffected | Unaffected | Unaffected |
Direct address, Relative address | This instruction is used for looping in 8051. It decrements the value stored at the given address and jumps to the relative address till the value in the address!=0 | 3 bytes | 24 clock cycles | Unaffected | Unaffected | Unaffected |
Example
The program given below adds 3 ten times to the accumulator. It uses the DJNZ instruction for looping.
MOV A,#0; clears the accumulator MOV R2,#10; moves 10 into r2 which cats as a counter AGAIN: ADD A,#03; adds 3 into the accumulator till r2!=0 DJNZ R2,AGAIN; decrements value in r2 and jumps to again label till value !=0 MOV R5,A; Moves the result into R5
Now that you understand branching instructions, you can better understand the process of subroutines and interrupts. If you’ve noticed, some of the branching instructions here operate on single bits of data instead of entire bytes. In the next post, we will summarize all the bit-wise instructions in the 8051 instruction set.