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 with in 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.
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, theyll
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.
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.
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.
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.