5.0 Module V: Assembly Language Programming for the 8051
5.1 The Role of Assembly Language
While high-level languages like C offer greater productivity and portability, assembly language plays a critical role in embedded systems development. It provides direct, low-level control over the microcontroller’s hardware. This level of control is indispensable for performance optimization, creating precise timing-sensitive operations, and gaining a deep understanding of the underlying architecture.
Assembly language is a low-level programming language that uses human-readable mnemonics as substitutes for the binary machine-level instructions that a processor understands. A special program called an assembler is used to translate these mnemonics into machine code (also known as object code or opcode). This is in contrast to a compiler, which translates a high-level language program (like C) into machine code.
5.2 Structure of an Assembly Language Program
An 8051 assembly language program consists of a series of statements, where each statement is either an instruction or a directive. An instruction tells the CPU what to do, while a directive gives instructions to the assembler. Each statement follows a four-field structure:
[ label: ] mnemonics [ operands ] [ ;comment ]
- Label (Optional): A symbolic name that refers to the memory address of that line of code. It allows other parts of the program to jump to this line by name. Labels must be followed by a colon (:).
- Mnemonic: The human-readable command for the instruction (e.g., MOV, ADD) or directive (e.g., ORG, END).
- Operands (Optional): The data or memory locations that the instruction will operate on. An instruction can have zero, one, or two operands.
- Comment (Optional): Any text following a semicolon (;) is ignored by the assembler and is used by the programmer to explain the code.
Let’s analyze a sample program:
ORG 0H ; Start (origin) at memory location 0
MOV R5,#25H ; Load the hexadecimal value 25 into register R5
MOV R7,#34H ; Load the hexadecimal value 34 into register R7
MOV A,#0 ; Load the value 0 into the Accumulator (A)
ADD A,R5 ; Add the content of R5 to the Accumulator
ADD A,R7 ; Add the content of R7 to the Accumulator
HERE: SJMP HERE ; Stay in this loop indefinitely (Short Jump to “HERE”)
END ; End of the assembly source file
In this program, ORG and END are assembler directives. MOV, ADD, and SJMP are instruction mnemonics. HERE: is a label. R5, #25H, and A are operands.
Rules for Labels:
- Each label name must be unique.
- Names can consist of letters, numbers (0-9), and special characters like ?, ., @, _, and $.
- The first character must be a letter.
- Reserved words (like instruction mnemonics) cannot be used as labels.
5.3 The Assembly and Execution Workflow
Creating and running an 8051 program involves a multi-step toolchain:
- Editing: The programmer uses a text editor to create a source file containing the assembly language code. This file is typically saved with a .asm extension.
- Assembling: The .asm source file is fed into an assembler. The assembler translates the mnemonics into machine code, producing two output files: an object file (.obj) and a list file (.lst). The list file is a human-readable output that shows the original code alongside the generated machine code and memory addresses, and it is very useful for debugging.
- Linking: A linker program takes one or more .obj files and combines them to produce a single absolute object file (.abs). This step is crucial for projects split across multiple source files.
- Hex Conversion: An object-to-hex converter program (OH) takes the .abs file and creates a hexadecimal file (.hex). This file contains the final machine code in a format that can be “burned” into the microcontroller’s ROM using a hardware programmer.
5.4 Assembler Directives and Data Types
The 8051 microcontroller has a single 8-bit data type. Any data larger than 8 bits must be broken down into 8-bit chunks for processing. Assembler directives are used to guide the assembly process and define data.
- DB (Define Byte): The most widely used directive for defining 8-bit data. It can be used to allocate memory for data in various formats:
- DATA1: DB 28 (Decimal)
- DATA2: DB 00110101B (Binary)
- DATA3: DB 39H (Hexadecimal)
- DATA4: DB “Hello” (ASCII String)
- ORG (Origin): Sets the starting memory address for the code that follows it. For example, ORG 0H tells the assembler to place the next instruction at memory location 0000H.
- EQU (Equate): Defines a constant by associating a label with a value. It does not occupy memory space. This is useful for making code more readable and maintainable. For example, COUNT EQU 25. Later in the code, MOV R3, #COUNT will be assembled as MOV R3, #25.
- END: Marks the end of the source file. The assembler will ignore any text that appears after this directive.
5.5 Addressing Modes
Addressing modes are the different ways an instruction’s source operand can be specified. The 8051 supports five distinct addressing modes.
- Immediate Addressing Mode: The operand is a constant value provided directly in the instruction itself. The value is preceded by a # sign.
- Example: MOV A, #6AH (Moves the hexadecimal value 6A into the Accumulator).
- Direct Addressing Mode: The operand is the 8-bit memory address of the data. This mode is used to access the internal RAM and Special Function Registers (SFRs).
- Example: MOV A, 04H (Moves the content of RAM location 04H into the Accumulator).
- Register Direct Addressing Mode: The operand is one of the R registers (R0-R7). The instruction operates directly on the data held within that register.
- Example: MOV A, R4 (Moves the content of register R4 into the Accumulator).
- Register Indirect Addressing Mode: A register (@R0 or @R1) is used as a pointer. The instruction operates on the data in the memory location whose address is held in the specified register.
- Example: MOV A, @R0 (If R0 contains the value 20H, this instruction moves the content of RAM location 20H into the Accumulator).
- Indexed Addressing Mode: The effective address of the data is formed by adding the content of the Accumulator to either the Data Pointer (DPTR) or the Program Counter (PC). This mode is used for reading look-up tables from program memory.
- Example: MOVC A, @A+DPTR (The address of the data is the sum of the values in the Accumulator and the DPTR).
5.6 The 8051 Instruction Set: Jumps and Calls
Control transfer instructions alter the sequential flow of program execution.
Looping Instructions
- DJNZ reg, label (Decrement and Jump if Not Zero): This is the primary instruction for creating loops. It decrements a specified register and, if the result is not zero, jumps to the target label.
- Limitation: DJNZ can only repeat a loop up to 256 times. For more iterations, nested loops are used, with an outer loop controlling an inner loop.
Conditional Jumps
These instructions cause a jump to a target address only if a specific condition is met. All conditional jumps are “short jumps,” meaning the target address must be within a relative range of -128 to +127 bytes from the current instruction.
| Instruction | Action |
| JZ | Jump if Accumulator is Zero |
| JNZ | Jump if Accumulator is Not Zero |
| JC | Jump if Carry Flag is set (CY=1) |
| JNC | Jump if Carry Flag is not set (CY=0) |
| JB | Jump if specified bit is 1 |
| JNB | Jump if specified bit is 0 |
| JBC | Jump if specified bit is 1, then clear the bit |
Unconditional Jumps
- LJMP (Long Jump): A 3-byte instruction that can jump to any memory location within the entire 64K address space.
- SJMP (Short Jump): A 2-byte instruction that performs a relative jump within a -128 to +127 byte range.
CALL Instructions
CALL instructions are used to execute subroutines—reusable blocks of code that perform a specific task.
- LCALL (Long Call): A 3-byte instruction that can call a subroutine located anywhere in the 64K address space.
- ACALL (Absolute Call): A 2-byte instruction that can call a subroutine within a 2K address range. When a CALL is executed, the address of the next instruction (the return address) is pushed onto the stack. The subroutine must end with a RET (Return) instruction, which pops the return address from the stack back into the Program Counter, resuming execution from where it left off.
Now that we have covered the software instructions for program control, we will explore the specialized hardware peripherals they are used to manage, such as timers and interrupts.