8051 Tutorial: Program Flow

When an 8051 is first initialized, it resets the PC to 0000h. The 8051 then begins to execute instructions sequentially in memory unless a program instruction causes the PC to be otherwise altered. There are various instructions that can modify the value of the PC; specifically, conditional branching instructions, direct jumps and calls, and "returns" from subroutines. Additionally, interrupts, when enabled, can cause the program flow to deviate from its otherwise sequential scheme.
Conditional Branching
The 8051 contains a suite of instructions which, as a group, are referred to as "conditional branching" instructions. These instructions cause program execution to follow a non-sequential path if a certain condition is true.
Take, for example, the JB instruction. This instruction means "Jump if Bit Set." An example of the JB instruction might be:

    JB 45h,HELLO
    NOP
    HELLO:....
In this case, the 8051 will analyze the contents of bit 45h. If the bit is set program execution will jump immediately to the label HELLO, skipping the NOP instruction. If the bit is not set the conditional branch fails and program execution continues, as usual, with the NOP instruction which follows.
Conditional branching is really the fundamental building block of program logic since all "decisions" are accomplished by using conditional branching. Conditional branching can be thought of as the "IF...THEN" structure in 8051 assembly language.
An important note worth mentioning about conditional branching is that the program may only branch to instructions located withim 128 bytes prior to or 127 bytes following the address which follows the conditional branch instruction. This means that in the above example the label HELLO must be within +/- 128 bytes of the memory address which contains the conditional branching instruction.
Direct Jumps
While conditional branching is extremely important, it is often necessary to make a direct branch to a given memory location without basing it on a given logical decision. This is equivalent to saying "Goto" in BASIC. In this case you want the program flow to continue at a given memory address without considering any conditions.
This is accomplished in the 8051 using "Direct Jump and Call" instructions. As illustrated in the last paragraph, this suite of instructions causes program flow to change unconditionally.
Consider the example:

    LJMP NEW_ADDRESS
    .
    .
    .
    NEW_ADDRESS:....
The LJMP instruction in this example means "Long Jump." When the 8051 executes this instruction the PC is loaded with the address of NEW_ADDRESS and program execution continues sequentially from there.
The obvious difference between the Direct Jump and Call instructions and the conditional branching is that with Direct Jumps and Calls program flow always changes. With conditional branching program flow only changes if a certain condition is true.
It is worth mentioning that, aside from LJMP, there are two other instructions which cause a direct jump to occur: the SJMP and AJMP commands. Functionally, these two commands perform the exact same function as the LJMP command--that is to say, they always cause program flow to continue at the address indicated by the command. However, SJMP and AJMP differ in the following ways:

  • The SJMP command, like the conditional branching instructions, can only jump to an address within +/- 128 bytes of the SJMP command.
  • The AJMP command can only jump to an address that is in the same 2k block of memory as the AJMP command. That is to say, if the AJMP command is at code memory location 650h, it can only do a jump to addresses 0000h through 07FFh (0 through 2047, decimal).
You may be asking yourself, "Why would I want to use the SJMP or AJMP command which have restrictions as to how far they can jump if they do the same thing as the LJMP command which can jump anywhere in memory?" The answer is simple: The LJMP command requires three bytes of code memory whereas both the SJMP and AJMP commands require only two. Thus, if you are developing an application that has memory restrictions you can often save quite a bit of memory using the 2-byte AJMP/SJMP instructions instead of the 3-byte instruction.
Recently, I wrote a program that required 2100 bytes of memory but I had a memory restriction of 2k (2048 bytes). I did a search/replace changing all LJMPs to AJMPs and the program shrunk downto 1950 bytes. Thus, without changing any logic whatsoever in my program I saved 150 bytes and was able to meet my 2048 byte memory restriction.
NOTE: Some quality assemblers will actually do the above conversion for you automatically. That is, they'll automatically change your LJMPs to SJMPs whenever possible. This is a nifty and very powerful capability that you may want to look for in an assembler if you plan to develop many projects that have relatively tight memory restrictions.
Direct Calls
Another operation that will be familiar to seasoned programmers is the LCALL instruction. This is similar to a "Gosub" command in Basic.
When the 8051 executes an LCALL instruction it immediately pushes the current Program Counter onto the stack and then continues executing code at the address indicated by the LCALL instruction.
Returns from Routines
Another structure that can cause program flow to change is the "Return from Subroutine" instruction, known as RET in 8051 Assembly Language.
The RET instruction, when executed, returns to the address following the instruction that called the given subroutine. More accurately, it returns to the address that is stored on the stack.
The RET command is direct in the sense that it always changes program flow without basing it on a condition, but is variable in the sense that where program flow continues can be different each time the RET instruction is executed depending on from where the subroutine was called originally.
Interrupts
An interrupt is a special feature which allows the 8051 to provide the illusion of "multi-tasking," although in reality the 8051 is only doing one thing at a time. The word "interrupt" can often be subsituted with the word "event."
An interrupt is triggered whenever a corresponding event occurs. When the event occurs, the 8051 temporarily puts "on hold" the normal execution of the program and executes a special section of code referred to as an interrupt handler. The interrupt handler performs whatever special functions are required to handle the event and then returns control to the 8051 at which point program execution continues as if it had never been interrupted.
The topic of interrupts is somewhat tricky and very important. For that reason, an entire chapter will be dedicated to the topic. For now, suffice it to say that Interrupts can cause program flow to change.

No comments:

Post a Comment