Inside The 8051
Inside The 8051
Inside The 8051
8051
1. If values 0 to F are moved into an 8-bit register, the rest of the bits are assumed
to be all zeros. For example, in "MOV A, #5" the result will be A = 05; that
is, A = 00000101 in binary.
1. Moving a value that is too large into a register will cause an error.
MOV A,#7F2H ;ILLEGAL: 7F2H > 8 bits (FFH)
R2,#456 ;ILLEGAL: 456 > 255 decimal (FFH)
MOV
4. A value to be loaded into a register must be preceded with a pound sign (#).
Otherwise it means to load from a memory location. For example "MOV
A, 17H" means to move into A the value held in memory location 17H, which
could have any value. In order to load the value 17H into the accumulator we
must write "MOV A, #17H" with the # preceding the number. Notice that the
4
absence of the pound sign will not cause an error by the assembler since it is a
valid instruction. However, the result would not be what the programmer
intended. This is a common error for beginning programmers in the 8051.
ADD instruction
The ADD instruction has the following format:
ADD A,source ;ADD the source operand
;to the accumulator
The ADD instruction tells the CPU to add the source byte to register A and put the result in
register A. To add two numbers such as 25H and 34H, each can be moved to a register and
then added together:
MOV A,#25H /load 25H into A MOV R2,#34H ;load 34H into
R2 ADD A,R2 /add R2 to accumulator
; (A = A + R2)
Executing the. program above results in A - 59H (25H + 34H = 59H) and R2 = 34H. Notice
that the content of R2 does not change. The program above can be written in many ways,
depending on the registers used. Another way might be:
MOV R5,#25H ;load 25H into R5 (R5=25H) MOV R7,#34H ;load
34H into R7 (R7=34H) MOV A,#0 /load 0 into A (A=0,clear
A) ADD A,R5 ;add to A content of R5
; where A = A + R5 ADD A,R7 /add to A content of R7
; where A = A + R7
The program above results in A = 59H. There are always many ways to write the same
program. One question that might come to mind after looking at the program above, is
whether it is necessary to move both data items into registers before adding them together.
The answer is no, it is not necessary. Look at the following variation of the same program:
MOV A,#25H ;load one operand into A (A=25H)
ADD A,#34H ;add the second operand 34H to A
In the above case, while one register contained one value, the second value followed the
instruction as an operand. This is called an immediate operand. The examples shown so far
for the ADD instruction indicate that the source operand can be either a register or
immediate data, but the destination must always be register A, the accumulator. In other
words, an instruction such as "ADD R2 , #12H" is invalid since register A (accumulator)
must be involved in any arithmetic operation. Notice that "ADD R4, A" is also invalid for
the reason that A must be the destination of any arithmetic operation. To put it simply: In
the 8051, register A must be involved and be the destination for all arithmetic operations.
The foregoing discussion explains why register A is referred to as the accumulator. The
format for Assembly language instructions, descriptions of their use, and a listing of legal
operand types are provided in Appendix A. 1.
There are two 16-bit registers in the 8051: PC (program counter) and DPTR (data pointer).
The importance and use of the program counter are covered in Section 2.3. The DPTR
register is used in accessing data and is discussed in Chapter 5 where addressing modes are
covered.
Brackets indicate that a field is optional, and not all lines have them. Brackets should not be
typed in. Regarding the above format, the following points should be noted.
1.
The label field allows the program to refer to a line of code by name. The label
field cannot exceed a certain number of characters. Check your assembler for
the rule.
2.
The Assembly language mnemonic (instruction) and operand(s) fields togeth
er perform the real work of the program and accomplish the tasks for which
the program was written. In Assembly language statements such as
ADD A,B MOV A,#67
ADD and MOV are the mnemonics, which produce opcodes; and "A, B" and "A, #67" are
the operands. Instead of a mnemonic and an operand, these two fields could contain
assembler pseudo-instructions, or directives. Remember that directives do not generate any
machine code (opcode) and are used only by the assembler, as opposed to instructions that
are translated into machine code (opcode) for the CPU to execute. In Program 2-1 the
commands ORG (origin) and END are examples of directives (some 8051 assemblers use
.ORG and .END). Check your assembler for the rules. More of these pseudo-instructions
are discussed in detail in Section 2.5.
3.
The comment field begins with a semicolon comment indicator ";".
Comments may be at the end of a line or on a line by themselves. The assem
bler ignores comments, but they are indispensable to programmers. Although
comments are optional, it is recommended that they be used to describe the
program and make it easier for someone else to read and understand, or for the
programmer to remember what they wrote.
4.
Notice the label "HERE" in the label field in Program 2-1. Any label referring
to an instruction must be followed by a colon symbol, ":". In the SJMP (short
jump instruction), the 8051 is told to stay in this loop indefinitely. If your sys
tem has a monitor program you do not need this line and it should be deleted
from your program. In the next section we will see how to create a ready-torun program.
machine code. The assembler will produce an object file and a list file. The extension for
the object file is "obj" while the extension for the list file is "1st".
1.
Assemblers require a third step called linking. The link program takes one or
more object files and produces an absolute object file with the extension "abs"
This abs file is used by 8051 trainers that have a monitor program.
2.
Next, the "abs" file is fed into a program called "OH" (object to hex convert
er), which creates a file with extension "hex" that is ready to burn into ROM.
This program comes with all 8051 assemblers. Recent Windows-based assem
blers combine steps 2 through 4 into one step.
More about "asm" and "obj" files
The "asm" file is also called the source file and for this reason some assemblers require that
this file have the "src" extension. Check your 8051 assembler to see which extension it
requires. As mentioned earlier, this file is created with an editor such as DOS EDIT or
Windows Notepad. The 8051 assembler converts the asm file's Assembly language
instructions into machine language and provides the obj (object) file. In addition to creating
the object file, the assembler also produces the 1st file (list file).
1st file
The 1st (list) file, which is optional, is very useful to the programmer because it lists all the
opcodes and addresses as well as errors that the assembler detected. Many assemblers
assume that the list file is not wanted unless you indicate that you want to produce it. This
file can be accessed by an editor such as DOS EDIT and displayed on the monitor or sent to
the printer to produce a hard copy. The programmer uses the list file to find syntax errors. It
9
is only after fixing all the errors indicated in the 1st file that the obj file is ready to be input
to the linker program.
SECTION 2.4: THE PROGRAM COUNTER AND ROM SPACE IN THE 8051
In this section we examine the role of the program counter (PC) register in executing an
8051 program. We also discuss ROM memory space for various 8051 family members.
Program counter in the 8051
Another important register in the 8051 is the PC (program counter). The program counter
points to the address of the next instruction to be executed. As the CPU fetches the opcode
from the program ROM, the program counter is incremented to point to the next
instruction. The program counter in the 8051 is 16 bits wide. This means that the 8051 can
access program addresses 0000 to FFFFH, a total of 64K bytes of code. However, not all
members of the 8051 have the entire 64K bytes of on-chip ROM installed, as we will see
soon. Where does the 8051 wake up when it is powered? We will discuss this important
topic next.
Where the 8051 wakes up when it is powered up
One question that we must ask about any microcontroller (or microprocessor) is: At what
address does the CPU wake up upon applying power to it? Each microprocessor is
different. In the case of the 8051 family (that is, all members regardless of the maker and
10
variation), the microcontroller wakes up at memory address 0000 when it is powered up.
By powering up we mean applying Vcc to the RESET pin as discussed in Chapter 4. In
other words, when the 8051 is powered up,.the PC (program counter) has the value of 0000
in it. This means that it expects the first opcode to be stored at ROM address OOOOH. For
this reason in the 8051 system, the first opcode must be burned into memory location
OOOOH of program ROM since this is where it looks for the first instruction when it is
booted. We achieve this by the ORG statement in the source program as shown earlier.
Next, we discuss the step-by-step action of the program counter in fetching and executing a
sample program.
Placing code in program ROM
To get a better understanding of the role of the program counter in fetching and executing a
program, we examine the action of the program counter as each instruction is fetched and
executed. First, we examine once more the list file
of the sample program and how the code is placed in the ROM of an 8051 chip. As we can
see, the opcode and operand for each instruction are listed on the left side of the list file.
Program21:ListFile
ROM Address
After the prografn is burned into ROM of an 8051 family member such as 8751 or AT8951
or DS5000, the opcode and operand are placed in ROM memory locations starting at 0000
as shown in the list below.
11
The list shows that address 0000 contains 7D, which is the opcode for moving a value into
register R5, and address 0001 contains the operand (in this case 25H) to be moved to R5.
Therefore, the instruction "MOV R5,#25H" has a machine code of "7D25", where 7D is the
opcode and 25 is the operand. Similarly, the machine code "7F34" is located in memory
locations 0002 and 0003 and represents the opcode and the operand for the instruction
"MOV R7,#34H". In the same way, machine code "7400" is located in memory locations
0004 and 0005 and represents the opcode and the operand for the instruction "MOV A, #0".
The memory location 0006 has the opcode of 2D, which is the opcode for the
instruction "ADD A, R5" and memory location 0007 has the content 2F, which is the
opcode for the "ADD A, R7" instruction. The opcode for the instruction "ADD A, #12H" is
located at address 0008 and the operand 12H at address 0009. The memory location OOOA
has the opcode for the SJMP instruction and its target address is located in location OOOB.
The reason the target address is FE is explained in the next chapter.
Executing a program byte by byte
Assuming that the above program is burned into the ROM of an 8051 chip (or 8751,
AT8951, or DS5000), the following is a step-by-step description of the action of the 8051
upon applying power to it.
1. When the 8051 is powered up, the PC (program counter) has 0000 and starts
to fetch the first opcode from location 0000 of the program ROM. In the case
of the above program the first opcode is 7D, which is the code for moving an
operand to R5. Upon executing the opcode, the CPU fetches the value 25 and
places it in R5. Now one instruction is finished. Then the program counter is
incremented to point to 0002 (PC = 0002), which contains opcode 7F, the
opcode for the instruction "MOV R7 , . .".
2. Upon executing the opcode 7F, the value 34H is moved into R7. Then the pro
gram counter is incremented to 0004.
3. ROM location 0004 has the opcode for the instruction "MOV A, #0". This
instruction is executed and now PC = 0006. Notice that all the above instruc
tions are 2-byte instructions; that is, each one takes two memory locations.
4. Now PC = 0006 points to the next instruction, which is "ADD A, R5". This is
a 1-byte instruction. After the execution of this instruction, PC = 0007.
5. The location 0007 has the opcode 2F, which belongs to the instruction "ADD
A,R7". This also is a 1-byte instruction. Upon execution of this instruction,
PC is incremented to 0008. This process goes on until all the instructions are
12
fetched and executed. The fact that the program counter points at the next
instruction to be executed explains why some microprocessors (notably the
x86) call the program counter the instruction pointer.
ROM memory map in the 8051 family
As we saw in the last chapter, some family members have only 4K bytes of on-chip ROM
(e.g., 8751, AT8951) and some, such as the AT89C52, have 8K bytes of ROM. Dallas
Semiconductor's DS5000-32 has 32K bytes of on-chip ROM. Dallas Semiconductor also
has an 8051 with 64K bytes of on-chip ROM. The point to remember is that no member of
the 8051 family can access more than 64K bytes of opcode since the program counter in the
8051 is a 16-bit register (0000 to FFFF address range). It must be noted that while the first
location of program ROM inside the 8051 has the address of 0000, the last location can be
different depending on the size of the ROM on the chip. Among the 8051 family members,
the 8751 and AT8951 have 4K bytes of on-chip ROM. This 4K bytes of ROM memory has
memory addresses of 0000 to OFFFH. Therefore, the first location of on-chip ROM of this
8051 has an address of 0000 and the last location has the address of OFFFH. Look at
Example 2-1 to see how this is computed.
Example 2-1
Find the ROM memory address of each of the following 8051 chips.
(a) AT89C51 with 4KB (b) DS89C420 with 16KB (c) DS5000-32 with 32KB
Solution:
14
Either single or double quotes can be used around ASCII strings. This can be
useful for strings, which contain a single quote such as "O'Leary". DB is also used
to allocate memory in byte-sized chunks.
Assembler directives
The following are some more widely used directives of the 8051.
ORG (origin)
The ORG directive is used to indicate the beginning of the address. The number
that comes after ORG can be either in hex or in decimal. If the number is not
followed by H, it is decimal and the assembler will convert it to hex. Some
assemblers use ". ORG" (notice the dot) instead of "ORG" for the origin directive.
Check your assembler.
EQU (equate)
This is used to define a constant without occupying a memory location. The EQU
directive does not set aside storage for a data item but associates a constant value
with a data label so that when the label appears in the program, itp constant value
will be substituted for the label. The following uses EQU for the counter constant
and then the constant is used to load the R3 register.
When executing the instruction "MOV R3, ttCOUNT", the register R3 will be loaded with
the value 25 (notice the # sign). What is the advantage of using EQU? Assume that there is
a constant (a fixed value) used in many different places in the program, and the
programmer wants to change its value throughout. By the use of EQU, the programmer can
change it once and the assembler will change* all of its occurrences, rather than search the
entire program trying to find every occurrence.
END directive
Another important pseudocode is the END directive. This indicates to the assembler the end
of the source (asm) file. The END directive is the last line of an 8051 program, meaning
that in the source code anything after the END directive is ignored by the assembler. Some
assemblers use ". END" (notice the dot) instead
of "END".
15
By choosing label names that are meaningful, a programmer can make a program much
easier to read and maintain. There are several rules that names must follow. First, each label
name must be unique. The names used for labels in Assembly language programming
consist of alphabetic letters in both uppercase and lowercase, the digits 0 through 9, and the
special characters question mark (?), period (.), at (@), underline (_), and dollar sign ($).
The first character of the label must be an alphabetic character. In other words it cannot be
a number. Every assembler has some reserved words that must not be used as labels in the
program. Foremost among the reserved words are the mnemonics for the instructions. For
example, "MOV" and "ADD" are reserved since they are instruction mnemonics. In
addition to the mnemonics there are some other reserved words. Check your assembler for
the list of reserved words.
16
17
18
19
There are 128 bytes of RAM in the 8051 (some members, notably the 8052, have 256 bytes
of RAM). The 128 bytes of RAM inside the 8051 are assigned addresses 00 to 7FH. As we
will see in Chapter 5, they can be accessed directly as memory locations. These 128 bytes
are divided into three different groups as follows.
Figure 2-5. RAM Allocation in the 8051
1. A total of 32 bytes from locations
00 to IF hex are set aside for reg
ister banks and the stack.
2. A total of 16 bytes from locations
20H to 2FH are set aside for bitaddressable read/write memory. A
detailed discussion of bit-address
able memory and instructions is
given in Chapter 8.
3. A total of 80 bytes from locations
30H to 7FH are used for read and
write storage, or what is normally
called a scratch pad. These 80
locations of RAM are widely used
for the purpose of storing data and
parameters by 8051 programmers.
We will use them in future chap
ters to store data brought into the
CPU via I/O ports. 1
20
each bank has 8 registers, RO - R7. RAM locations from 0 to 7 are set aside for bank 0
of RO - R7 where RO is RAM location 0, Rl is RAM location 1, R2 is location 2, and
so on, until memory location 7, which belongs to R7 of bank 0. The second bank of
registers RO - R7 starts at RAM location 08 and goes to location OFH. The third bank
of RO - R7 starts at memory location 10H and goes to location 17H. Finally, RAM
locations 18H to 1FH are set aside for the fourth bank of RO - R7. The following shows
how the 32 bytes are allocated into 4 banks:
to these RAM locations with names such as RO, R1, and so on, than by their memory
locations. Example 2-6 clarifies this concept.
22
only once. Notice also that to push the registers onto the stack we must use their RAM
addresses. For example, the instruction "PUSH 1" pushes register Rl onto the stack.
24
25
26
JDtxl
Figure 2-8.128-Byte Memory Space from Pro View 32 Simulator
27
SUMMARY
This chapter began with an exploration of the major registers of the 8051, including A, B,
RO, Rl, R2, R3, R4, R5, R6, R7, DPTR, and PC. The use of these registers was
demonstrated in the context of programming examples. The process of creating an
Assembly language program was described from writing the source file, to assembling it,
linking, and executing the program. The PC (program counter) register always points to the
next instruction to be executed. The way the 8051 uses program ROM space was explored
because 8051 Assembly language programmers must be aware of where programs are
placed in ROM, and how much memory is available.
28
29
In the program in Example 3-1, the R2 register is used as a counter. The counter is first set
to 10. In each iteration the instruction DJNZ decrements R2 and checks its value. If R2 is
not zero, it jumps to the target address associated with the label "AGAIN". This.looping
action continues until R2 becomes zero. After R2 becomes zero, it falls through the loop
and executes the instruction immediately below it, in this case the "MOV R5 , A"
instruction.
Notice in the DJNZ instruction that the registers can be any of RO - R7. The counter can
also be a RAM location as we will see in Chapter 5.
Example 3-2
What is the maximum number of times that the loop in Example 3-1 can be repeated?
Solution:
Since R2 holds the count and R2 is an 8-bit register, it can hold a maximum of FFH (255
decimal); therefore, the loop can be repeated a maximum of 256 times.
Loop inside a loop
As shown in Example 3-2, the maximum count is 256. What happens if we want to
repeat an action more times than 256? To do that, we use a loop inside a loop, which
is called a nested loop. In a nested loop, we use two registers to hold the count. See
Example 3-3.
Example 3-3
Write a program to (a) load the accumulator with the value 55H, and (b) complement the
ACC 700 times.
Solution:
Since 700 is larger than 255 (the maximum capacity of any register), we use two registers
to hold the count. The following code shows how to use R2 and R3 for the count.
30
In this program, R2 is used to keep the inner loop count. In the instruction "DJNZ R2 ,
AGAIN", whenever R2 becomes 0 it falls through and "DJNZ R3 , NEXT" is executed.
This instruction forces the CPU to load R2 with the count 70 and the inner loop starts
again. This process will continue until R3 becomes zero and the outer loop is finished.
Other conditional jumps
Conditional jumps for the 8051 are summarized in Table 3-1. More details of each
instruction are provided in Appendix A. In Table 3-1, notice that some of the
instructions, such as JZ (jump if A = zero) and JC (jump if carry), jump only if a
certain condition is met. Next we examine some conditional jump instructions with
examples.
JZ (jump if A = 0)
In this instruction the content of register A is checked. If it is zero, it jumps to the
target address. For example, look at the following code.
OVER:
Table 3-1: 8051 Conditional Jump Instructions
31
In this program,. if either RO or Rl is zero, it jumps to the label OVER. Notice that the JZ
instruction can be used only for register A. It can only check to see whether the
accumulator is zero, and it does not apply to any other register. More importantly, you don't
have to perform an arithmetic instruction such as decrement to use the JNZ instruction. See
Example 3-4.
Example 3-4
32
33
is divided into forward and backward jumps; that is, within -128 to +127 bytes of memory
relative to the address of the current PC (program counter). If the jump is forward, the
target address can be within a space of 127 bytes from the current PC. If the target address
is backward, the target address can be within -128 bytes from the current PC. This is
explained in detail next.
Calculating the short jump address
In addition to the SJMP instruction, all conditional jumps such as JNC, JZ, and DJNZ are
also short jumps due to the fact that they are all two-byte instructions. In these instructions
the first byte is the opcode and the second byte is the relative address. The target address is
relative to the value of the program counter. To calculate the target address, the second byte
is added to the PC of the instruction immediately below the jump. To understand this, look
at Example 3-6.
Example 3-6
Using the following list file, verify the jump forward address calculation.
Solution:
First notice that the JZ and JNC instructions both jump forward. The target address for a
forward jump is calculated by adding the PC of the following instruction to the second byte
of the short jump instruction, which is called the relative address. In line 4 the instruction
"JZ NEXT" has opcode of 60 and operand of 03 at the addresses of 0004 and 0005. The 03
is the relative address, relative to the address of the next instruction INC RO, which is
0006. By adding 0006 to 3, the target address of the label NEXT, which is 0009, is
generated. In the same way for line 9, the "JNC OVER" instruction has opcode and
operand of 50 and 05 where 50 is the opcode and 05 the relative address. Therefore, 05 is
added to GOOD, the address of instruction "CLR A", giving 12H, the address of label
OVER.
34
Example 3-7
Verify the calculation of backward jumps in Example 3-6.
Solution:
In that program list, "JNC AGAIN" has opcode 50 and relative address F2H. When the
relative address of F2H is added to 15H, the address of the instruction below the jump, we
have 15H + F2H = 07 (the carry is dropped). Notice that 07 is the address of label AGAIN.
Look also at '.'SJMP HERE", which has 80 and FE for the opcode and relative address,
respectively. The PC of the following instruction, 0017H, is added to FEH, the relative
address, to get 0015H, address of the HERE label (17H + FEH = 15H). Notice that FEH is
-2 and 17H + (-2) = 15H. For further discussion of the addition of negative numbers, see
Chapter 6.
Jump backward target address calculation
While in the case of a forward jump, the displacement value is a positive number
between 0 to 127 (00 to 7F in hex), for the backward jump the displacement is a
negative value of 0 to -128 as explained in Example 3-7.
It must be emphasized that regardless of whether the SJMP is a forward or backward
jump, for any short jump the address of the target address can never be more than 128 to +127 bytes from the address associated with the instruction below the SJMP.
If any attempt is made to violate this rule, the assembler will generate an error stating
the jump is out of range.
35
1. Notice the DELAY subroutine. Upon executing the first "LCALL DELAY",
the address of the instruction right below it, "MOV A, #OAAH", is pushed onto
the stack, and the 8051 starts to execute instructions at address 300H.
2. In the DELAY subroutine, first the counter R5 is set to 255 (R5 = FFH); there
fore, the loop is repeated 256 times. When R5 becomes 0, control falls to the
RET instruction, which pops the address from the stack into the program
counter and resumes executing the instructions after the CALL.
3.
The amount of time delay in Example 3-8 depends on the frequency of the 8051. How to
calculate the exact time will be explained in detail in Chapter 4. However you can increase
the time delay by using a nested loop as shown below.
36
The stack and stack pointer were covered in the last chapter. To understand the
importance of the stack in microcontrollers, we now examine the contents of the
stack and stack pointer for Example 3-8. This is shown in Example 3-9.
Example 3-9
37
It needs to be emphasized that in using LCALL, the target address of the subroutine can be
anywhere within the 64K-byte memory space of the 8051. This is not the case for the other
call instruction, ACALL, which is explained next.
38
39
40
41
Very often we calculate the time delay based on the instructions inside the loop and ignore
the clock cycles associated with the instructions outside the loop.
In Example 3-15, the largest value the R3 register can take is 255; therefore, one way to
increase the delay is to use NOP instructions in the loop. NOP, which stands for "no
operation," simply wastes time. This is shown in Example 3-16.
Loop inside loop delay
Another way to get a large delay is to use a loop inside a loop, which is also called a nested
loop. See Example 3-17.
Example 3-16
42
Solution:
The time delay inside the HERE loop is [250(1 + 1 + 1 + 1 + 2)] x 1.085 us =1500 x 1.085
us = 1627.5 us. Adding the two instructions outside the loop we have 1627.5 us +
3xl.085|is= 1630.755 us.
If machine cycle timing is critical to your system design, make sure that you check the
manufacture's data sheets for the device specification. For example, the DS89C420 has 3
machine cycles instead of 2 machine cycles for the RET instruction.
Example 3-17
For the HERE loop, we have (4 x 250) xl.085 us = 1085 us. The AGAIN loop repeats the
HERE loop 200 times; therefore, we have 200 x 1085 us = 217000, if we do not include the
overhead. However, the instructions "MOV R3,#250" and "DJNZ R2, AGAIN" at the
beginning and end of the AGAIN loop add (3 x 200 x 1.085 us) = 651 us to the time delay.
As a result we have 217000 + 651 = 217651 us = 217.651 milliseconds for total time delay
associated with the above DELAY subroutine. Notice that in the case of a nested loop, as
in all other time delay loops, the time is approximate since we have ignored the first and
last instructions in the subroutine.
of the 8051 microcontrollers. While the original 8051 design used 12 clock
periods per machine cycle, many of the newer generations of the 8051 use
much fewer clocks per machine cycle. For example, the DS5000 uses 4 clock
periods per machine cycle while the DS89C4xO uses only one clock per
machine
cycle.
The
8051
i
3-2:
Clocks
per
Machine
Cycle
(MC)
for
TaD e
Various 8051 Versions
Example 3-18
44
45
46
Example 3-22
From the above discussion we conclude that use of the instruction in generating time delay
is not the most reliable method. To get more accurate time delay we use timers as described
in Chapter 9. Meanwhile, to get an accurate time delay for a given 8051 microcontroller,
we must use an oscilloscope to measure the exact time delay.
47
SUMMARY
48
The four ports PO, Pi, P2, and P3 each use 8 pins, making them 8-bit ports. All the ports
upon RESET are configured as inputs, ready to be used as input ports. When the first 0 is
written to a port, it becomes an output. To reconfigure it as an input, a 1 must be sent to the
port. To use any of these ports as an input port, it must be programmed, as we will explain
throughout this section. First, we describe each port.
Port 0
Port 0 occupies a total of 8 pins (pins 32 -39). It can be used for input or output. To use the
pins of port 0 as both input and output ports, each pin must be connected externally to a
lOK-ohm pull-up resistor. This is due to the fact that PO is an open drain,
unlike PI, P2, and P3, as Figure 4-2. Port 0 with Pull-Up Resistors we will soon see.
Open drain is a term used for MOS chips in the same way that open collector is used for
TTL chips. In any system using the 8051/52 chip, we normally connect PO to pull-up
resistors. See Figure 4-2. In this way we take advantage of port 0 for both input and output.
For example, the following code will continuously send out to port 0 the alternating values
of 55H and AAH.
It must be noted that complementing 55H (01010101) turns it into AAH (10101010). By
sending 55H and AAH to a given port continuously, we toggle all the bits of that port.
Port 0 as input
With resistors connected to port 0, in order to make it an input, the port must be
programmed by writing 1 to all the bits. In the following code, port 0 is configured first as
an input port by writing Is to it, and then data is received from that port and sent to P1.
49
Port 1 as input
If port 1 has been configured as an output port, to make it an input port again, it must
programmed as such by writing 1 to all its bits. The reason for this is discussed in Appendix
C.2. In the following code, port 1 is configured first as an input port by writing Is to it, then
data is received from that port and saved in R7, R6, and R5.
Port 2
50
Port 2 occupies a total of 8 pins (pins 21 through 28). It can be used as input or output. Just
like PI, port 2 does not need any pull-up resistors since it already has pull-up resistors
internally. Upon reset, port 2 is configured as an input port. The following code will send
out continuously to port 2 the alternating values 55H and AAH. That is, all the bits of P2
toggle continuously.
Port 2 as input
To make port 2 an input, it must programmed as such by writing 1 to all its bits. In the
following code, port 2 is configured first as an input port by writing 1 s to it. Then data is
received from that port and is sent to PI continuously.
Port 3 occupies a total of 8 pins, pins 10 through 17. It can be used as input or output. P3
does not need any pull-up resistors, just as PI and P2 did not. Although port 3 is configured
as an input port upon reset, this is not the way it is most commonly used. Port 3 has the
additional function of providing some extremely important signals such as interrupts. Table
4-1 provides these alternate functions of P3. This information applies to both 8051 and
8031 chips.
Functions
P3.0 and P3.1 are used for the RxD and TxD serial communications signals. See Chapter 10
to see how they are connected. Bits P3.2 and P3.3 are set aside for external interrupts, and
are discussed in Chapter 11. Bits P3.4 and P3.5 are used for timers 0 and 1, and are
discussed in Chapter 9 where timers are discussed. Finally, P3.6 and P3.7 are used to
provide the WR and RD signals of external memories connected in 8031-based systems.
Chapter 14 discusses how they are used in 8031-based systems. In systems based on the
8751, 89C51, or DS89C4xO, pins 3.6 and 3.7 are used for I/O while the rest of the pins in
port 3 are normally used in the alternate function role.
52
53
54
8051
I/O
BIT
PROGRAMMING
MANIPULATION
Notice that PI.2 is the third bit of PI, since the first bit is Pl.O, the second bit is P1.1,
and so on. Table 4-3 shows the bits of the 8051 I/O ports. See Example 4-2 for an
example of bit manipulation of I/O bits. Notice in Example 4-2 that unused portions of
ports 1 and 2 are undisturbed. This single-bit addressability of I/O ports is one of most
powerful features of the 8051 microcontroller and is among the reasons that many
designers choose the 8051 over other microcontrollers. We will see the use of the bitaddressability of I/O ports in future chapters.
Table 4-3: Sinele-Bit Addressability of Ports
55
Example 4-2
56
In this program, instruction "JNB PI . 2 , AGAIN" (JNB means jump if no bit) stays in the
loop as long as P1.2 is low. When P1.2 becomes high, it gets out of the loop, writes the
value 45H to port 0, and creates an H-to-L pulse by the sequence of instructions SETB and
CLR.
57
Example 4-4
Assume that bit P2.3 is an input and represents the condition of an oven. If it goes high, it
means that the oven is hot. Monitor the bit continuously. Whenever it goes high, send a
high-to-low pulse to port PI.5 to turn on a buzzer.
Example 4-5
A switch is connected to pin PI .7. Write a program to check the status of SW and perform
the following:
1. If SW=0, send letter 'N' to P2.
2. If SW=1, send letter 'Y' to P2.
58
We can also use the carry flag to save or examine the status of a single bit of the port. To
do that, we use the instruction "MOV C, Px. y" as shown in the next two examples.
Example 4-6
A switch is connected to pin P1.7. Write a program to check the status of the switch and
perform the following:
1. If switch = 0, send letter 'N' to P2.
2. If switch = 1, send letter 'Y' to P2.
Use the carry flag to check the switch status. This is a repeat of the last example.
Solution:
Example 4-7
A switch is connected to pin PI .0 and an LED to pin P2.7. Write a program to get the
status of the switch and send it to the LED.
Notice in Examples 4-6 and 4-7 how the carry flag is used to get a bit of data from the port.
We must make a distinction between these two categories of instructions since confusion
between them is a major source of errors in 8051 programming, especially where external
hardware is concerned. We discuss these instructions briefly. However, readers must study
59
and understand the material on this topic and on the internal working of ports that is given
in Appendix C.2.
Instructions for reading input ports
As stated earlier, to make any bit of any 8051 port an input port, we
must write 1 (logic high) to that bit. After we configure the port bits as
input, we can use only certain instructions in order to get the external
data present at the pins into the CPU. Table 4-6 shows the list of such
instructions.
Reading latch for output port
Some instructions read the contents of an internal port latch instead of
reading the status of an external pin. Table 4-6 provides a list of these
instructions. For example, look at the "ANL PI, A" instruction. The
sequence of actions taken when such an instruction is executed is as
follows.
1. The instruction reads the internal latch of the port and brings that data into
the
CPU.
1. This data is ANDed with the contents of register A.
2. The result is rewritten back to the port latch.
3. The port pin data is changed and now has the same value as the port latch.
From the above discussion, we conclude that the instructions that read the port
latch normally read a value, perform an operation (and possibly change it), then
rewrite it back to the port latch. This is often called "Read-Modify-Write".
Read-modify-write feature
The ports in the 8051 can be accessed by the read-modify-write technique. This
feature saves many lines of code by combining in a single instruction all three
actions of (1) reading the port, (2) modifying its value, and (3) writing to the
60
port. The following code first places 01010101 (binary) into port 1. Next, the
instruction "XLR Pi, #OFFH" performs an XOR logic operation on PI with 1111
1111 (binary), and then writes the result back into PI.
Notice that the XOR of 55H and FFH gives AAH. Likewise, the XOR of AAH
and FFH gives 55H. Logic instructions are discussed in Chapter 6.
caution
We strongly recommend that you study Section C.2 (Appendix C) if you are
connecting any external hardware to your 8051 system. Failure to use the right
instruction or the right connection to port pins can damage the ports of your
8051 system.
SUMMARY
This chapter focused on the I/O ports of the 8051. The four ports of the 8051,
PO, PI, P2, and P3, each use 8 pins, making them 8-bit ports. These ports can be
used for input or output. Port 0 can be used for either address or data. Port 3 can
be used to provide interrupt and serial communication signals. Then I/O
instructions of the 8051 were explained, and numerous examples were given. We
also showed the bit-addressability of the 8051 ports.
61
IMMEDIATE
AND
ADDRESSING MODES
REGISTER
The CPU can access data in various ways. The data could be in a register, or
in memory, or be provided as an immediate value. These various ways of
accessing data are called addressing modes. In this chapter we discuss
8051/52 addressing modes in the context of some examples.
The various addressing modes of a microprocessor are determined when it is
designed, and therefore cannot be changed by the programmer. The 8051
provides a total of five distinct addressing modes. They are as follows:
1.
2.
3.
4.
5.
immediate
register
direct
register indirect
indexed
In Section 5.1 we look at immediate and register addressing modes. In Section 5.2 we cover
accessing memory using the direct, register indirect, and indexed addressing modes.
Section 5.3 discusses the bit-addressability of RAM, registers, and I/O ports. In Section 5.4
we show how to access the extra 128 bytes of RAM in the 8052.
SECTION 5.1: IMMEDIATE AND REGISTER ADDRESSING MODES
In this section, first we examine immediate addressing mode and then register
addressing mode.
Immediate addressing mode
Although the DPTR register is 16-bit, it can also be accessed as two 8-bit registers, DPH
and DPL, where DPH is the high byte and DPL is the low byte. Look at the following code.
In this addressing mode, the source operand is a constant. In immediate
addressing mode, as the name implies, when the instruction is assembled, the
operand comes immediately after the opcode. Notice that the immediate data
must be preceded by the pound sign, "#". This addressing mode can be used to
load information into any of the registers, including the DPTR register.
Examples follow.
62
Also notice that the following would produce an error since the value is larger than 16 bits.
Notice that we can also use immediate addressing mode to send data to 8051 ports. For
example. "MOV PI, #55H" is a valid instruction.
Register addressing mode
Register addressing mode involves the use of registers to hold the data to be manipulated.
Examples of register addressing mode follow.
It should be noted that the source and destination registers must match in size. In other
words, coding "MOV DPTR, A" will give an error, since the source is an 8-bit register and
the destination is a 16-bit register. See the following.
Notice that we can move data between the accumulator and Rn (for n 0 to 7) but
movement of data between Rn registers is not allowed. For example, the instruction "MOV
R4, R7" is invalid.
In the first two addressing modes, the operands are either inside one of the registers or
tagged along with the instruction itself. In most programs, the data to be processed is often
in some memory location of RAM or in the code space of ROM. There are many ways to
access this data. The next section describes these different methods.
63
ACCESSING
MEMORY
USING
VARIOUS ADDRESSING MODES
SECTION 5.2: ACCESSING MEMORY USING VARIOUS ADDRESSING MODES
We can use direct or register indirect addressing modes to access data stored either in RAM
or registers of the 8051. This topic will be discussed thoroughly in this section.
We will also show how to access on-chip ROM containing data using indexed
addressing mode.
Direct addressing mode
As mentioned in Chapter 2, there are 128 bytes of RAM in the 8051. The RAM has been
assigned addresses 00 to 7FH. The following is a summary of the allocation of
these 128 bytes.
1.
2.
RAM locations 00 - 1FH are assigned to the register banks and stack.
1.
RAM locations 20 - 2FH are set aside as bit-addressable space to save singlebit data. This is discussed in Section 5.3.
RAM locations 30 - 7FH are available as a place to save byte-sized data.
Although the entire 128 bytes of RAM can be accessed using direct addressing mode, it is
most often used to access RAM locations 30 - 7FH. This is due to the fact that
register bank locations are accessed by the register names of RO - R7, but there
is no such name for other RAM locations. In the direct addressing mode, the
data is in a RAM memory location whose address is known, and this address is
given as a part of the instruction. Contrast this with immediate addressing
mode, in which the operand itself is provided with the instruction. The "#" sign
distinguishes between the two modes. See the examples below, and note the
absence of the "#" sign.
64
The above examples should reinforce the importance of the "#" sign in 8051 instructions.
See the following code.
Although it is easier to use the names RQ - R7 than their memory addresses, RAM
locations 3 OH to 7FH cannot be accessed in any way other than by their addresses since
they have no names.
SFR registers and their addresses
Among the registers we have discussed so far, we have seen that RO - R7 are part of the
128 bytes of RAM memory. What about registers A, B, PSW, and DPTR? Do they also
have addresses? The answer is yes. In the 8051, registers A, B, PSW, and DPTR are part of
the group of registers commonly referred to as SFR (special function registers). There are
many special function registers and they are widely used, as we will discuss in future
chapters. The SFR can be accessed by their names (which is much easier) or by their
addresses. For example, register A has address EOH, and register B has been designated the
address FOH, as shown in Table 5-1. Notice how the following pairs of instructions mean
the same thing.
Table 5-1 lists the 8051 special function registers (SFR) and their addresses. The following
two points should be noted about the SFR addresses.
1.
The special function registers have addresses between 80H and FFH. These
addresses are above 80H, since the addresses 00 to 7FH are addresses of RAM
memory inside the 8051.
2.
Not all the address space of 80 to FF is used by the SFR. The unused locations
80H to FFH are reserved and must not be used by the 8051 programmer.
65
Regarding direct addressing mode, notice the following two points: (a) the address value is
limited to one byte, 00 - FFH, which means this addressing mode is limited to accessing
RAM locations and registers located inside the 8051. (b) if you examine the 1st file for an
Assembly language program, you will see that the SFR registers' names are replaced with
their addresses as listed in Table 5-1.
66
Example 5-1
Stack and direct addressing mode
Another major use of direct addressing mode is the stack. In the 8051 family, only direct
addressing mode is allowed for pushing onto the stack. Therefore, an instruction such as
"PUSH A" is invalid. Pushing the accumulator onto the stack must be coded as "PUSH
OEOH" where OEOH is the address of register A. Similarly, pushing R3 of bank 0 is coded
as "PUSH 03". Direct addressing mode must be used for the POP instruction as well. For
example, "POP 04" will pop the top of the stack into R4 of bank 0.
Example 5-2
Show the code to push R5, R6, and A onto the stack and then pop them back them into R2,
R3, and B, where register B = register A, R2 = R6, and R3 = R5.
pointers, that is, when they hold the addresses of RAM locations, they must be
preceded by the "@" sign, as shown below.
Notice that RO (as well as Rl) is preceded by the "@" sign. In the absence of the "@"
sign, MOV will be interpreted as an instruction moving the contents of register RO to
A, instead of the contents of the memory location pointed to by RO.
Example 5-3
67
68
An example of how to use both RO and Rl in the register indirect addressing mode in a
block transfer is given in Example 5-5.
69
In this program, assume that the word "USA" is burned into ROM locations starting at
200H, and that the program is burned into ROM locations starting at 0. Analyze how the
program works and state where "USA" is stored after this program is run.
In the above program ROM locations 200H - 202H have the following contents. 200=('U')
201=('S') 202=('A'')
We start with DPTR = 200H, and A = 0. The instruction "MOVC A, @A+DPTR" moves
the contents of ROM location 200H (200H + 0 = 200H) to register A. Register A contains
55H, the ASCII value for "U". This is moved to RO. Next, DPTR is incremented to make
DPTR = 201H. A is set to 0 again to get the contents of the next ROM location 201H,
which holds character "S". After this program is run, we have RO = 55H, Rl = 53H, and R2
= 41H, the ASCII values for the characters "U", "S" and "A".
Example 5-7
Assuming that ROM space starting at 250H contains "America", write a program to transfer
the bytes into RAM locations starting at 40H.
70
Example 5-8
Write a program
to P2, continuously.
to
get
the
value
from
PI
and
send
x2
In addition to being used to access program ROM, DPTR can be used to access memory
externally connected to the 8051. This is discussed in Chapter 14.
Another register used in indexed addressing mode is the program counter. This is discussed
in Appendix A.
72
In many of the examples above, the MOV instruction was used for the sake of clarity, even
though one can use any instruction as long as that instruction supports the addressing mode.
For example, the instruction "ADD A, @RO" would add the contents of the memory
location pointed to by RO to the contents of register A. We will see more examples of using
addressing modes with various instructions in the next few chapters.
Indexed addressing mode and MOVX instruction
As we have stated earlier, the 8051 has 64K bytes of code space under the direct control of
the Program Counter register. We just showed how to use the MOVC instruction to access
a portion of this 64K-byte code space as data memory space. In many applications the size
of program code does not leave any room to share the 64K-byte code space with data. For
this reason the 8051 has another 64K bytes of memory space set aside exclusively for data
storage. This data memory space is referred to as external memory and it is accessed only
by the MOVX instruction. In other words, the 8051 has a total of 128K bytes of memory
space since 64K bytes of code added to 64K bytes of data space gives us 128K bytes. One
major difference between the code space and data space is that, unlike code space, the data
space cannot be shared between code and data. This is such an important topic that we have
dedicated an entire chapter to it: Chapter 14.
Accessing RAM Locations 30 - 7FH as scratch pad
As we have seen so far, in accessing registers RO - R7 of various banks, it is much easier to
refer to them by their RO - R7 names than by their RAM locations. The only problem is
that we have only 4 banks and very often the task of bank switching and keeping track of
register bank usage is tedious and prone to errors. For this reason in many applications we
use RAM locations 30 - 7FH as scratch pad and leave addresses 8 - 1FH for stack usage.
That means that we use RO - R7 of bank 0, and if we need more registers we simply use
RAM locations 30-7FH. Look at Example 5-10.
Example 5-10
73
74
In order to avoid confusion regarding the addresses 00 - 7FH, the following two
points must be noted.
1. The 128 bytes of RAM have the byte addresses of 00 - 7FH and can be
accessed in byte size using various addressing modes such as
direct and register-indirect, as we have seen in this chapter and previous chapters.
These 128 bytes are accessed using byte-type instructions.
2. The 16 bytes of RAM locations 20 - 2FH also have bit addresses of 00
- 7FH since 16 x 8 = 128(00-7FH). In order to access these 128 bits of RAM
locations and other bit-addressable space of 8051 individu ally, we can use only
the single-bit instructions such as SETB. Figure 5-1. 16 Bytes of Internal RAM.
Table 5-2 provides a list Note: They are both bit- and byte-accessible.
of single-bit instructions. Notice that the single-bit instructions use only one
addressing mode and that is direct addressing mode. In the first two sections of this
chapter we showed various addressing modes of byte-addressable space of the
8051, among them indirect addressing mode. It must be noted that there is no
indirect addressing mode for single-bit instructions.
75
76
As we mentioned earlier in this chapter, every SFR register is assigned a byte address and
ports PO -P3 are part of the SFR. For example, PO is assigned byte address 80H, and PI has
address of 90H as shown in Figure 5-2. While all of the SFR registers are byte-addressable
some of them are also bit-addressable. The PO - P3 are among this category of SFR
registers. From Figure 5-2 we see that the bit addresses for PO are 80H to 87H, and for PI
are 90H to 97H, and so on.
Notice that when code such as "SETB PI. 0" is assembled, it becomes "SETB 9OH" since
Pl.O has the RAM address of 90H. Also notice from Figures 5-1 and 5-2 that bit addresses
00 - 7FH belong to RAM byte addresses 20 - 2FH, and bit addresses 80 - F7H belong to
SFR of PO, ICON, PI, SCON, P2, etc. The bit addresses for PO - P3 are shown in Table 53. and discussed next.
77
1.
2.
3.
4.
5.
6.
Example 5-12
78
Registers bit-addressability
While all I/O ports are bit-addressable, that is not the case with registers, as seen from
Figure 5-1. Only registers A, B, PSW, IP, IE, ACC, SCON, and TCON are bit-addressable.
Of the bit-addressable registers, we will concentrate on the familiar registers A, B, and
PSW. The rest will be discussed in future chapters.
Now let's see how we can use bit-addressability of registers such as A and PSW. As we
discussed in Chapter 2, in the PSW register two bits are set aside for the selection of the
register banks. See Figure 5-3. Upon RESET, bank 0 is selected. We can select any other
banks using the bit-addressability of the PSW as was shown in Chapter 2. The bit
addressability of PSW also eliminates the need for instructions such as JOV (Jump if
OV=1). See Example 5-14.
Examine the next few examples of bit-addressabilty to gain a better understanding of this
important feature of the 8051.
Example 5-13
Example 5-14
While there are instructions such as JNC and JC to check the carry flag bit (CY), there are
no such instructions for the overflow flag bit (OV). How would you write code to check
OV?
Solution:
The OV flag is PSW.2 of the PSW register. PSW is a bit-addressable register; therefore, we
can use the following instruction to check the OV flag.
79
Example 5-15
Write a program to see if the RAM location 37H contains an even value. If so, send it to
P2. If not, make it even and then send it to P2.
Example 5-16
Assume that bit P2.3 is an input and represents the condition of a door. If it goes high, it
means that the door is open. Monitor the bit continuously. Whenever it goes high, send a
low-to-high pulse to port PI.5 to turn on a buzzer.
Example 5-17
The status of bits PI.2 and PI.3 of I/O port PI must be saved before they are changed. Write
a program to save the status of PI. 2 in bit location 06 and the status of PI. 3 in bit
location 07.
80
Example 5-18
Write a program to save the status of bit PI.7 on RAM address bit 05. Solution:
Example 5-19
Write a program to get the status of bit pin PI.7 and send it to pin P2.0. Solution:
This is similar to Example 5-16, except the use of BIT directive allows us to assign the
OVEN_HOT and BUZZER bit to any port. This way you do not have to search the
program for them.
81
Example 5-21
An LED is connected to pin PI.7. Write a program to toggle the LED forever. Solution:
Example 5-22
A switch is connected to pin PI .7 and an LED to pin P2.0. Write a program to get the
status of the switch and send it to the LED.
Example 5-23
Assume that RAM bit location 12H holds the status of whether there has been a phone call
or not. If it is high, it means there has been a new call since it was checked the last time.
Write a program to display "New Messages" on an LCD if bit RAM 12H is high. If it is
low, the LCD should say "No New Messages"
Solution:
We can also use the EQU directive to assign addresses, as shown in the next few examples.
Notice that in Example 5-24 the ports are defined by their names, while in Example 5-25,
they are defined by their addresses.
Example 5-24
A switch is connected to pin P1.7. Write a program to check the status of the switch and
make the following decision.
1. If SW = 0, send "NO" to P2.
2. If SW = 1, send "YES" to P2.
Example 5-25
A switch is connected to pin PI .7. Write a program to check the status of the switch and
make the following decision.
(a) IfSW = 0, send'0'toP2.
(b)IfSW- l,send'l'toP2.
Use EQU to designate the I/O ports.
Solution:
83
In addition to the above two features, the 8052 has another 128 bytes of on-chip RAM with
addresses 80 - FFH. This extra 128 bytes of on-chip RAM is often called upper memory to
distinguish it from the lower 128 bytes of 00 - 7FH. The only problem is, the address space
80 - FFH is the same address space assigned to the SFRs. In other words, they are
physically two separate memories, but they have the same addresses. This parallel address
space in the 8052 forces us to use two different addressing modes to access them as
described next.
1. To access the SFRs, we use direct addressing mode. The instruction "MOV
90H, #55H" is an example of accessing the SFR with direct addressing
mode. Since 90H is the address of PI, this is same as "MOV PI, #55H".
To access the upper 128 bytes, we use the indirect addressing mode, which
uses RO and Rl registers as pointers. Therefore, instructions "MOV @RO , A"
and "MOV @R1, A" are employed to access the upper memory as long as reg
isters RO and Rl have values of 80H or higher. For example, the following
codes will put 55H into address 90H of the upper 128 bytes of RAM.
Figure 5-4 shows the parallel space shared between the SFR and the upper 128 bytes
of RAM in the 8052. Example 5-26 shows how to access the upper 128 bytes of onchip RAM in the 8052 microcontroller.
84
Example 5-26
Write a program for the 8052 to put 55H into the upper RAM locations of 90 - 99H.
Solution:
Run the above program on your simulator and examine the upper memory to see the result.
(See Figures 5-5 and 5-6 for screen shots.)
85
Example 5-27
Assume that the on-chip ROM has a message. Write a program to copy it from code space
into the upper memory space starting at address 80H. Also, as you place a byte in upper
RAM, give a copy to PO.
Solution:
Run the above program on your simulator and examine the upper memory to see the result.
86
Figure 5-5. Franklin Software's Pro View Upper Memory for the 8052
SUMMARY
This chapter described the five addressing modes of the 8051. Immediate addressing mode
uses a constant for the source operand. Register addressing mode involves the use of
registers to hold data to be manipulated. Direct or register indirect addressing modes can be
used to access data stored in either RAM or registers of the 8051. Direct addressing mode is
also used to manipulate the stack. Register indirect addressing mode uses a register as a
pointer to the data. The advantage of this is that it makes addressing dynamic rather than
static. Indexed addressing mode is widely used in accessing data elements of look-up table
entries located in the program ROM space of the 8051.
A group of registers called the SFR (special function registers) can be accessed by their
names or their addresses. We also discussed the bit-addressable ports, registers, and RAM
locations and showed how to use single-bit instructions to access them directly.
87
ARITHMETIC INSTRUCTIONS
This chapter describes all 8051 arithmetic and logic instructions. Program examples are
given to illustrate the application of these instructions. In Section 6.1 we discuss
instructions and programs related to addition, subtraction, multiplication, and division of
unsigned numbers. Signed numbers are discussed in Section 6.2. In Section 6.3, we discuss
the logic instructions AND, OR, and XOR, as well as the COMPARE instruction. The
ROTATE instruction and data serialization are discussed in Section 6.4. In Section 6.5 we
provide some real-world applications such as BCD and ASCII conversion and checksum
byte testing.
The instruction ADD is used to add two operands. The destination operand is always in
register A while the source operand can be a register, immediate data, or in memory.
Remember that memory-to-memory arithmetic operations are never allowed in 8051
Assembly language. The instruction could change any of the AF, CF, or P bits of the flag
register, depending on the operands involved. The effect of the ADD instruction on the
overflow flag is discussed in Section 6.3 since it is used mainly in signed number
operations. Look at Example 6-1.
Example 6-1
Show how the flag register is affected by the following instructions.
After the addition, register A (destination) contains 00 and the flags are as follows:
CY = 1 since there is a carry out from D7.
P = 0 because the number of Is is zero (an even number).
AC = 1 since there is a carry from D3 to D4.
88
At the end when the loop is finished, the sum is held by registers A and R7, where A has
the low byte and R7 has the high byte.
89
When adding two 16-bit data operands, we need to be concerned with the propagation of a
carry from the lower byte to the higher byte. The instruction ADDC (add with carry) is used
on such occasions. For example, look at the addition of 3CE7H + 3B8DH, as shown below.
When the first byte is added (E7 + 8D = 74, CY = 1). The carry is propagated to the higher
byte, which results in 3C + 3B + 1 = 78 (all in hex). Example 6-3 shows the above steps in
an 8051 program.
Example 6-3
Write a program to add two 16-bit numbers. The numbers are 3CE7H and 3B8DH. Place
the sum in R7 and R6; R6 should have the lower byte.
Unpacked BCD
In unpacked BCD, the lower 4 bits of the number represent the BCD number, and the rest
of the bits are 0. For example, "0000 1001" and "0000 0101" are unpacked BCD for 9 and
5, respectively. Unpacked BCD requires 1 byte of memory or an 8-bit register to contain it.
Packed BCD
In packed BCD, a single byte has two BCD numbers in it, one in the lower 4 bits, and
one in the upper 4 bits. For example, "0101 1001" is packed BCD for 59H. It takes
only 1 byte of memory to store the packed BCD operands. And so one reason to use
packed BCD is that it is twice as efficient in storing data.
There is a problem with adding BCD numbers, which must be corrected. The
problem is that after adding packed BCD numbers, the result is no longer BCD. Look
at the following.
Adding these two numbers gives 0011 111 IB (3FH), which is not BCD! A BCD
number can only have digits from 0000 to 1001 (or 0 to 9). In other words, adding
two BCD numbers must give a BCD result. The result above should have been 17 +
28 = 45 (0100 0101). To correct this problem, the programmer must add 6 (0110) to
the low digit: 3F + 06 = 45H. The same problem could have happened in the upper
digit (for example, in 52H + 87H = D9H). Again to solve this problem, 6 must be
added to the upper digit (D9H + 60H = 139H) to ensure that the result is BCD (52 +
87 = 139). This problem is so pervasive that most microprocessors such as the 8051
have an instruction to deal with it. In the 8051 the instruction "DA A" is designed/to
correct the BCD addition problem. This is discussed next.
DA instruction
The DA (decimal adjust for addition) instruction in the 8051 is provided to correct
the aforementioned problem associated with BCD addition. The mnemonic "DA" has
as its only operand the accumulator "A". The DA instruction will add 6 to the lower
nibble or higher nibble if needed; otherwise, it will leave the result alone. The
following example will clarify these points.
91
After the program is executed, register A wili contain 72H (47 + 25 = 72). The "DA"
instruction works only on A. In other words, while the source can be an operand of
any addressing mode, the destination must be in register A in order for DA to work.
It also needs to be emphasized that DA must be used after the addition of BCD
operands and that BCD operands can never have any digit greater than 9. In other
words, A - F digits are not allowed. It is also important to note that DA works only
after an ADD instruction; it will not work after the INC instruction.
Summary of DA action
After an ADD or ADDC instruction,
1. If the lower nibble (4 bits) is greater than 9, or if AC = 1, add 0110 to the lower
4 bits.
1. If the upper nibble is greater than 9, or if CY = 1, add 0110 to the upper 4 bits.
In reality there is no other use for the AC (auxiliary carry) flag bit except for BCD addition
and correction. For example, adding 29H and 18H will result in 41H, which is incorrect as
far as BCD is concerned.
Since AC = 1 after the addition, "DA A" will add 6 to the lower nibble. The final result is
in BCD format.
Example 6-4
Assume that 5 BCD data items are stored in RAM locations starting at 40H, as shown
below. Write a program to find the sum of all the numbers. The result must be in BCD.
40= (71) 41=(11) 42=(65) 43=(59) 44=(37)
92
In many microprocessors there a^e two different instructions for subtraction: SUB
and SUBB (subtract with borrow). In the 8051 we have only SUBB. To make SUB
out of SUBB, we have to make CY = 0 prior to the execution of the instruction.
Therefore, there are two cases for the SUBB instruction: (1) with CY = 0, and (2)
with CY = 1. First we examine the case where CY = 0 prior to the execution of
SUBB. Notice that we use the CY flag for the borrow.
These three steps are performed for every SUBB instruction by the internal hardware of the
8051 CPU, regardless of the source of the operands, provided that the addressing mode is
supported. After these three steps the result is obtained and the flags are set. Example 6-5
illustrates the three steps.
Example 6-5
93
The flags would be set as follows: CY = 0, AC - 0, and the programmer must look at the
carry flag to determine if the result is positive or negative.
Show the steps involved in the following.
If the C Y = 0 after the execution of SUBB, the result is positive; if C Y = 1, the result is
negative and the destination has the 2's complement of the result. Normally, the result is left
in 2's complement, but the CPL (complement) and INC instructions can be used to change
it. The CPL instruction performs the 1 's complement of the operand; then the operand is
incremented (INC) to get the 2's complement. See Example 6-6.
Example 6-6
94
Solution:
After the SUBB, A = 62H - 96H = CCH and the carry flag is set high indicating there is a
borrow. Since CY = 1, when SUBB is executed the second time A = 27H - 12H -1 = 14H.
Therefore, we have 2762H - 1296H = 14CCH.
In byte-by-byte multiplication, one of the operands must be in register A, and the second
operand must be in register B. After multiplication, the result is in the A and B registers; the
lower byte is in A, and the upper byte is in B. The following example multiplies 25H by
65H. The result is a 16-bit data that is held by the A and B registers.
Table
6-1: Unsigned Multiplication Summary (MUL AB)
Note: Multiplication of operands larger than 8 bits takes some manipulation. It is left
to the reader to experiment with.
Division of unsigned numbers
In the division of unsigned numbers, the 8051 supports byte over byte only. The syntax is
as follows.
95
When dividing a byte by a byte, the numerator must be in register A and the denominator
must be in B. After the DIV instruction is performed, the quotient is in A and the remainder
is in B. See the following example.
96
The input value from PI is in the hex range of 00 - FFH or in binary 00000000 to
11111111. This program will not work if the input data is in BCD. In other words,
this program converts from binary to decimal. To convert a single decimal digit to
ASCII format, we OR it with 30H as shown in Sections 6.4 and 6.5.
Example 6-9
Analyze the program in Example 6-8, assuming that PI has a value of FDH for data.
Solution:
To convert a binary (hex) value to decimal, we divide it by 10 repeatedly until the quotient
is less than 10. After each division the remainder is saved. In the case of an 8-bit binary
such as FDH we have 253 decimal as shown below (all in hex).
Therefore, we have FDH = 253. In order to display this data it must be converted to ASCII,
which is described in a later section in this chapter.
and negative numbers: The most significant bit (MSB) is set aside for the sign (+ or -),
while the rest of the bits are used for the magnitude. The sign is represented by 0 for
positive (+) numbers and 1 for negative (-) numbers. Signed byte representation is
discussed below.
Signed 8-bit operands
In signed byte operands, D7 I (MSB) is the sign and DO to D6 are set aside for the
magnitude of the number. If D7 = 0, the operand is positive, and if D7 = 1, it is negative.
Negative numbers
For negative numbers, D7 is 1; however, the magnitude is represented in its 2's
complement. Although the assembler does the conversion, it is still important to understand
how the conversion works. To convert to negative number representation (2's complement),
follow these steps.
1. Write the magnitude of the number in 8-bit binary (no sign).
2. Invert each bit.
3. Add 1 to it.
98
Example 6-10
Example 6-11
Example 6-12
Examples 6-10, 6-11, and 6-12 demonstrate these three steps.
From the examples above it is clear that the range of byte-sized negative
numbers is -1 to -128. The following lists byte-sized signed number ranges:
The above explains the mystery behind the relative address of-128 to +127 in the
short jump discussed in Chapter 3.
Overflow problem in signed number operations
99
When using signed numbers, a serious problem arises that must be dealt with.
This is the overflow problem. The 8051 indicates the existence of an error by
raising the OV (overflow) flag, but it is up to the programmer to take care of the
erroneous result. The CPU understands only Os and Is and ignores the human
convention of positive and negative numbers. What is an overflow? If the result
of an operation on signed numbers is too large for the register, an overflow has
occurred and the programmer must be notified. Look at Example 6-13.
Example 6-13
Examine the following code and analyze the result.
According to the CPU, the result is -90, which is wrong. The CPU sets OV = 1
to indicate the overflow.
In Example 6-13, +96 is added to +70 and the result according to the CPU was 90. Why? The reason is that the result was larger than what A could contain.
Like all other 8-bit registers, A could only contain up to +127. The designers
of the CPU created the overflow flag specifically for the purpose of
informing the programmer that the result of the signed number operation is
erroneous.
When is the OV flag set?
In 8-bit signed number operations, OV is set to 1 if either of the following
two conditions occurs:
1. There is a carry from D6 to D7 but no carry out of D7 (CY = 0).
2. There is a carry from D7 out (CY = 1) but no carry from D6 to D7.
100
According to the CPU, the result is +126, which is wrong (OV = 1).
Example 6-15
Observe the following, noting the OV flag.
Example 6-16
From the above examples we conclude that in any signed number addition, OV
indicates whether the result is valid or not. If OV = 1, the result is erroneous; if OV
101
= 0, the result is valid. We can state emphatically that in unsigned number addition
we must monitor the status of CY (carry flag), and in signed number addition, the
OV (overflow) flag must be monitored by the programmer. In the 8051, instructions
such as JNC and JC allow the program to branch right after the addition of unsigned
numbers, as we saw in Section 6.1. There is no such instruction for the OV flag.
However, this can be achieved by "JB PSW.2" or "JNB PSW.2" since PSW, the flag
register, is a bit-addressable register. This is discussed later in this chapter.
Instructions to create 2's complement
The 8051 does not have a special instruction to
make the 2's complement of a number. To do that, we
can use the CPL (complement) instruction and ADD,
as shown next.
CPL A ; 1's complement (Invert)
1.
102
LOGIC
AND
INSTRUCTIONS
COMPARE
This instruction will perform a logical AND on the two operands and place the result in the
destination. The destination is normally the accumulator. The source operand can be a
register, in memory, or immediate. See Appendix A.I for more on the addressing modes for
this instruction. The ANL instruction for byte-size operands has no effect on any of the
flags. The ANL instruction is often used to mask (set to 0) certain bits of an operand. See
Example 6-17.
Logical AND Function
OR
103
Logical OR Function
Inputs Output
X Y X OR Y
ORL destination,source ;dest = dest OR source
The destination and source operands are ORed, and the result is placed in the destination.
The ORL instruction can be used to set certain bits of an operand to 1. The destination is
normally the accumulator. The source operand can be a register, in memory, or immediate.
See Appendix A for more on the addressing modes supported by this instruction. The ORL
instruction for byte-size operands has no effect on any of the flags. See Example 6-18.
Example 6-18
XOR
XRL destination,source ;dest = dest XOR source
This instruction will perform the XOR operation on the two operands, and place the result
in the destination. The destination is
104
normally the accumulator. The source operand can Logical XQR Function be a register, in
memory, or immediate. See Appendix A.I for the addressing modes of this instruction. The
XRL instruction for byte-size operands has no effect on any of the flags. See Examples 619 and 6-20.
XRL can also be used to see if two registers have the same value. "XRL A, Rl" will
exclusive-or register A and register Rl, and put the result in A. If both registers have the
same value, 00 is placed in A. Then we can use the JZ instruction to make a deci
Example 6-20
The XRL instruction can be used to clear the contents of a register by XORing it with
itself. Show how "XRL A, A" clears A, assuming that A = 45H.
Example 6-21
Read and test PI to see whether it has the value 45H. If it does, send 99H to P2; otherwise,
it stays' cleared.
105
In the program in Example 6-21 notice the use of the JNZ instruction. JNZ and JZ
test the contents of the accumulator only. In other words, there is no such thing as a
zero flag in the 8051.
Another widely used application of XRL is to toggle bits of an operand. For
example,
to
toggle
bit
2
of
register
A,
we
could
use
the
following code. This code causes D2 of register A to change to the opposite value,
while all the other bits remain unchanged.
106
meriting a byte, the data must be in register A. Although the CPL instruction cannot be
used to complement RO-R7, it does work on PO-P3 ports. See Appendix A to see which
addressing mode is available for the CPL instruction.
Compare instruction
The 8051 has an instruction for the compare operation. It has the following syntax.
CJNE destination,source,relative address
In the 8051, the actions of comparing and jumping are combined into a single instruction
called CJNE (compare and jump if not equal). The CJNE instruction compares two
operands, and jumps if they are not equal. In addition, it changes the CY flag to indicate if
the destination operand is larger or smaller. It is important to notice that the operands
themselves remain unchanged. For example, after the execution of the instruction "CJNE
A, #67H, NEXT", register A still has its original value. This instruction compares register
A with value 67H and jumps to the target address NEXT only if register A has a value other
than 67H.
Example 6-23
Examine the following code, then answer the following questions.
1. Will it jump to NEXT?
2. What is in A after the CJNE instruction is executed?
Solution:
3. Yes, it jumps because 55H and 99H are not equal.
4. A = 55H, its original value before the comparison.
5.
107
In CJNE, the destination operand can be in the accumulator or in one of the Rn registers.
The source operand can be in a register, in memory, or immediate. See Appendix A for the
addressing modes of this instruction. This instruction affects the carry flag only. CY is
changed as shown in Table 6-3.
Example 6-24
Write code to determine if register A contains the value 99H. If so, make Rl = FFH;
otherwise, make Rl = 0.
The following shows how the comparison works for all possible conditions.
Notice in the CJNE instruction that any Rn register can be compared with an
immediate value. There is no need for register A to be involved. Also notice that CY
is always checked for cases of greater or less than, but only after it is determined that
they are not equal. See Examples 6-25 through 6-27.
Example 6-25
Assume that PI is an input port connected to a temperature sensor. Write a program to read
the temperature and test it for the value 75. According to the test results, place the
temperature value into the registers indicated by the following.
108
Example 6-26
Write a program to monitor PI continuously for the value 63H. It should stop monitoring
only if PI = 63H.
Example 6-27
Assume internal RAM memory locations 40H - 44H contain the daily temperature for five
days, as shown below. Search to see if any of the values equals 65. If value 65 does exist
in the table, give its location to R4; otherwise, make R4 = 0.
40H=(76) 41H=(79) 42H=(69) 43H=(65) 44H=(62)
109
The compare instruction is really a subtraction, except that the values of the operands do
not change. Flags are changed according to the execution of the SUBB instruction. It must
be emphasized again that in the CJNE instruction, the operands are not affected, regardless
of the result of the comparison. Only the CY flag is affected. This is despite the fact that
CJNE uses the subtract operation to set or reset the CY flag.
In rotate right, the 8 bits of the accumulator are rotated right one bit, and bit DO exits from
the least significant bit and enters into D7 (most significant bit). See the code and diagram.
In rotate left, the 8 bits of the accumulator are rotated left one bit, and bit D7 exits from the
MSB (most significant bit) and enters into DO (least significant bit). See the code and
diagram.
110
In RRC A, as bits are rotated from left to right, they exit the LSB to the carry flag, and the
carry flag enters the MSB. In other words, in RRC A the LSB is moved to CY and CY is
moved to the MSB. In reality, the carry flag acts as if it is part of register A, making it a 9bit register.
In RLC A, as bits are shifted from right to left they exit the MSB and enter the carry flag,
and the carry flag enters the LSB. In other words, in RCL the MSB is moved to CY (carry
flag) and CY is moved to the LSB. See the following code and diagram.
111
Serializing data
Serializing data is a way of sending a byte of data one bit at a time through a single
pin of microcontroller. There are two ways to transfer a byte of data serially:
1. Using the serial port. In using the serial port, programmers have very limited
control over the sequence of data transfer. The details of serial port data trans
fer are discussed in Chapter 10.
1. The second method of serializing data is to transfer data one bit at a time and
control the sequence of data and spaces in between them. In many new
generations of devices such as LCD, ADC, and ROM, the serial versions of these
devices are becoming popular since they take less space on a printed circuit
board. We discuss this important topic next.
Serializing a byte of data
Serializing data is one of the most widely used applications of the rotate instruction.
We showed in Chapter 5 how the CY flag status can be moved to any pin of ports
PO - P3. Using that concept and the rotate instruction, we transfer a byte of data
serially (one bit at a time). Repeating the following sequence 8 times will transfer an
entire byte, as shown in Example 6-28.
Example 6-29 shows how to bring in a byte of data serially one bit at a time. We will
see how to use these concepts in Chapter 13 for a serial ADC chip.
Example 6-28
Write a program to transfer value 41H serially (one bit at a time) via pin P2.1. Put two
highs at the start and end of the data. Send the byte LSB first.
Solution:
112
Example 6-29
Write a program to bring in a byte of data serially one bit at a time via pin P2.7 and save it
in register R2. The byte comes in with the LSB first.
Aside from the fact that the carry flag (CY) is altered by arithmetic and logic instructions,
in the 8051 there are also several instructions by which the CY flag can be manipulated
directly. These instructions are listed in Table 6-4.
Of the instructions in Table 6-4, we have shown the use of JNC, CLR, and SETS in many
examples in the last few chapters. The next few examples give simple applications of the
instructions in Table 6-4, including some dealing with the logic operations AND and OR.
Example 6-30
Write a program to save the status of bits P1.2 and PI.3 on RAM bit locations 6 and 7,
respectively.
Solution:
MOV C,P1.2 ;save status of PI.2 on CY
MOV 06 ,C ,-save carry in RAM bit location 06
MOV C,P1.3 ;save status of PI.3 on CY
MOV 07,C /save carry in RAM bit location 07
Example 6-31
Assume that bit P2.2 is used to control an outdoor light and bit P2.5 a light inside a
building. Show how to rum on the outside light and turn off the inside one.
Solution:
SETB C ;CY = 1
ORL C,P2.2 ;CY = P2.2 ORed with CY
MOV P2.2,C /turn it "on" if not already "on"
114
CLR C ;CY = 0
ANL C,P2.5 ;CY = P2.5 ANDed-with CY
MOV P2.5,C /turn it off if not already off
Example 6-32
SWAP A
Another useful instruction is the SWAP instruction. It works only on the
accumulator (A). It swaps the lower nibble and the higher nibble. In other words,
the lower 4 bits are put into the higher 4 bits, and the higher 4 bits are put into
the lower 4 bits. See the diagrams below and Example 6-33.
Example 6-33
1. Find the contents of register A in the following code.
1. In the absence of a SWAP instruction, how would you exchange the nibbles?
Write a simple program to show the process.
Solution:
115
BCD,
ASCII,
AND
APPLICATION PROGRAMS
OTHER
116
ASCII numbers
On ASCII keyboards, when the key "0" is activated, "Oil 0000" (30H) is provided to
the computer. Similarly, 31H (011 0001) is provided for the key "1", and so on, as
shown in Table 6-5.
It must be noted that although ASCII is standard in the United States (and many other
countries), BCD numbers are universal. Since the keyboard, printers, and monitors all
use ASCII, how does data get converted from ASCII to BCD, and vice versa? These
are the subjects covered next.
117
After this conversion, the packed BCD numbers are processed and the result will
be in packed BCD format. As we saw earlier in this chapter, a special instruction,
"DA A", requires that data be in packed BCD format.
Example 6-34
Example 6-35
118
Assume that the lower three bits of PI are connected to three switches. Write a program to
send the following ASCII characters to P2 based on the status of the switches.
To perform the checksum operation, add all the bytes, including the checksum byte.
The result must be zero. If it is not zero, one or more bytes of data have been
changed (corrupted). To clarify these important concepts, see Example 6-36.
Example 6-36
Assume that we have 4 bytes of hexadecimal data: 25H, 62H, 3FH, and 52H. (a) Find the
checksum byte, (b) perform the checksum operation to ensure data integrity, and (c) if the
second byte 62H has been changed to 22H, show how checksum detects the error.
119
Checksum Program
120
subroutine converts the decimal digits to ASCII digits and saves them. We are saving the
low digit in the lower address location and the high digit in the higher address location.
This is referred to as the Little-Endian convention, that is, low-byte to low-location and
high-byte to high-location. All Intel products use the Little-Endian convention.
SUMMARY
This chapter discussed arithmetic instructions for both signed and unsigned data in the
8051. Unsigned data uses all 8 bits of the byte for data, making a range of 0 to 255 decimal.
Signed data uses 7 bits for data and 1 for the sign bit, making a range of-128 to +127
decimal.
Binary coded decimal (BCD) data represents the digits 0 through 9. Both packed and
unpacked BCD formats were discussed. The 8051 contains special instructions for
arithmetic operations on BCD data.
122
In coding arithmetic instructions for the 8051, special attention has to be given to the
possibility of a carry or overflow condition.
This chapter also defined the logic instructions AND, OR, XOR, and complement. In
addition, 8051 Assembly language instructions for these functions were described.
Compare and jump instructions were described as well. These functions are often used for
bit manipulation purposes.
The rotate and swap instructions of the 8051 are used in many applications such as serial
devices. This chapter also described checksum byte data checking, BCD and ASCII
formats, and conversions.
123