The 8051

  Instruction Set Summary
  Instructions tell the processor which operation to carry out. For example, MOV A, #5EH tells the processor to move the data 5EH to the accumulator.
  Instructions result in the processor performing some operation on some data (the data being #5EH in the example above). The instruction above is an example of what's known as immediate addressing. The reason for this is because the data immediately follows the instruction in code memory.
  However, it is not always adequate to store the data in code memory itself. If we needed to read the information from a keyboard, for example, we would need to use something other than immediate addressing.
  There are eight different addressing modes in the 8051.
  Opcode and Operand
  The first byte of an instruction is known as the opcode (operation code) because this is the byte that is decoded by the processor - from this code the processor can work out what operation it must perform.
  For a one-byte instruction there is only the opcode.
  For a two-byte instruction the second byte is the operand. The operand can be data or an 8-bit address.
  For a three-byte instruction the second and third bytes make up the operand. A two-byte operand is usually a 16-bit address, as we shall see when we look at long addressing.
  Addressing Modes
  The eight addressing modes are:
  • Immediate
  • Register
  • Direct
  • Indirect
  • Relative
  • Absolute
  • Long
  • Indexed
  Immediate Addressing
  If the operand is a constant then it can be stored in memory immediately after the opcode. Remember, values in code memory (ROM) do not change once the system has been programmed and is in use in the everyday world. Therefore, immediate addressing is only of use when the data to be read is a constant. For example, if your program needed to perform some calculations based on the number of weeks in the year, you could use immediate addressing to load the number 52 (34H) into a register and then perform arithmetic operations upon this data.
MOV R0, #34H
  The above instruction is an example of immediate addressing. It moves the data 34H into R0. The assembler must be able to tell the difference between an address and a piece of data. The hash symbol (#) is used for this purpose (whenever the assembler sees # before a number it knows this is immediate addressing).
  This is a two-byte instruction.
If no H is appended to the number it is treated as decimal. For example, to place the decimal number 28 in R0, we write:

MOV R0, #28
If a hex number begins with a letter, it must be written with a leading zero. For example, to place A9H in R7, we write:

MOV R7, #0A9H
  Register Addressing
  Often we need to move data from a register into the accumulator so that we can perform arithmetic operations upon it. For example, we may wish to move the contents of R5 into the accumulator.
  This is an example of register addressing. It moves data from R5 (in the currently selected register bank) into the accumulator.


  The above is another example of register addressing. It adds the contents of R6 to the accumulator, storing the result in the accumulator. Note that in both examples the destination comes first. This is true of all instructions.
  Direct Addressing
  Direct addressing is used for accessing data in the on-chip RAM. Since there are 256 bytes of RAM (128 bytes general storage for the programmer and another 128 bytes for the SFRs). That means the addresses go from 00H to FFH, any of which can be stored in an 8-bit location.
MOV A, 67H
  The above instruction moves the data in location 67H into the accumulator. Note the difference between this and immediate addressing. Immediate addressing uses the data, which is immediately after the instruction. With direct addressing, the operand is an address. The data to be operated upon is stored in that address. The assembler realises this is an address and not data because there is no hash symbol before it.
ADD A, 06H
  The above instruction adds the contents of location 06H to the accumulator and stores the result in the accumulator. If the selected register bank is bank 0 then this instruction is the same as ADD A, R6.
  Indirect Addressing
  Register addressing and direct addressing both restrict the programmer to manipulation of data in fixed addresses. The address the instruction reads from (MOV A, 30H) or writes to (MOV 30H, A) cannot be altered while the program is running.
  There are times when it is necessary to read and write to a number of contiguous memory locations. For example, if you had an array of 8-bit numbers stored in memory, starting at address 30H, you may wish to examine the contents of each number in the array (perhaps to find the smallest number). To do so, you would need to read location 30H, then 31H, then 32H and so on.
  This can be achieved using indirect addressing. R0 and R1 may be used as pointer registers. We can use either one to store the current memory location and then use the indirect addressing instruction shown below.
MOV A, @Ri
  where Ri is either R0 or R1.
  Now, we can read the contents of location 30H through indirect addressing:

MOV R0, #30H
MOV A, @R0


The first instruction is an example of immediate addressing whereby the data 30H is placed in R0. The second instruction is indirect addressing. It moves the contents of location 30H into the accumulator.

  If we now wish to get the data in location 31H we use the following:
MOV A, @R0
  Once we see how to write a loop in assembly language, we will be able to read the entire contents of the array.
  Relative Addressing
  Relative addressing is used only with certain jump instructions. The system executes a jump by changing the contents of the PC to the address of the next instruction to be executed. For example, if we wished to jump to the instruction stored at location 4EH in code memory, the PC would be loaded with 4EH. Then, during the next execution cycle the contents of the PC (4EH) are placed on the address bus and the instruction at 4EH is retrieved.
  A relative address (or offset) is an 8-bit signed value, which is added to the PC to form the address of the next instruction to be executed.
  With 8-bit signed numbers, the MSB is used to determine whether the number is positive or negative. If the MSB is 0 then the number is positive, while if the MSB is 1 the number is negative.
  The instruction below shows how to jump six locations ahead.
  SJUMP is an unconditional jump and is a 2-byte instruction. The number following it is an offset address. If this instruction were stored in code memory at locations 100H and 101H, as shown below:
100H 80H
101H 06H
  The opcode for SJMP is 80H. The operand is the offset address. If this instruction were executed the PC would get the value 108H. This is what happens:
  • The PC contains 100H, therefore the instruction 80H is read into the IR.
  • The instruction is decoded as the 2-byte SJMP instruction.
  • The PC is incremented so that the operand may be retrieved.
  • The operand is read from code memory and the PC is incremented again (because this is a 2-bye instruction).
  • The operand (06H) is added to the PC (102H + 06H = 108H).
  • The next instruction (at 108H) is executed.
  Once we deal with 2's compliment and how negative numbers are dealt with in the CPU, we will look at a backward jump.
  The S in SJMP stands for short. The range of signed 8-bit numbers is -127 to 128. (Click here to see how signed numbers are stored in a microcontroller.) Therefore, using SJMP allows us to jump 127 locations forward or 128 locations backward. Hence the name short jump.
  When writing assembly programs we do not need to calculate the offset when using SJMP. Instead, we use labels. If we wished to jump to the instruction at 108H we would simply label the instruction with an appropriate name, for example THERE. We would then write the assembly code SJMP THERE. The assembler does the work of replacing the label with the correct offset.
  Absolute Addressing
  Absolute addressing is only used with the ACALL and AJMP instructions.
ACALL - subroutine call (2 byte instruction)
AJMP - unconditional jump (2 byte instruction)
  These instructions allow you to move to any instruction within the same 2K of memory.
  We will look at the AJMP instruction only (at a later date, when we begin dealing with subroutines we will deal with the ACALL instruction).
  The operation of the AJMP instruction is detailed below:
AJMP address
(PC) <- (PC) + 2
(PC10-PC0) <- address10 - address0
  Note that only the eleven least significant bits of the PC are altered. The five most significant bits remain the same. This means the AJMP will only allow you to jump to a location in the same 2K page as the instruction directly after the jump.
  For example:

If the label THERE represents an instruction at address 0F46H and the instruction AJMP THERE is in memory at locations 0900H and 0901H, the assembler will encode the instruction as


11100001 1st byte (A10 - A8 + opcode)
01000110 2nd byte (A7 - A0)

The underlined bits are the low-order 11 bits of the destination address, 0F46H = 0000111101000110B. The upper five bits in the program counter will not change when this instruction executes. Note that both the AJMP instruction and the destination are within the 2K page bounded by 0800H and 0FFFH, and therefore have the upper five address bits in common.
The 8051 Microcontroller Third Edition - I. Scott MacKenzie
  Note: it is not important to remember the different kinds of addressing modes and which instructions belong to which mode. It is far more important to understand how to get data from one place to another and how to perform operations upon the data. You will never be asked to memorise a processor's instruction set. This will always be provided. However, as we shall see when we start writing programs, it is important to be able to use the instruction set.
  Most of our early programs will deal with moving data into and out of the accumulator, performing arithmetic operations and jumping to different parts of the program.
  Complete Instruction Set
Copyright (c) 2005-2006 James Rogers