Assembling Machine Code from Assembly Instructions
In this lesson, you will learn how the assembler bridges the gap between human-readable mnemonics and the raw binary machine code that your processor actually executes.
After the compiler generates assembly files from your C source code, the assembler takes over to perform the final translation into a relocatable object file. While C code is high-level and abstract, assembly language is a one-to-one mapping with the instruction set architecture (ISA). The assembler’s primary job is to parse these mnemonics and convert them into the specific opcode patterns defined by the chip’s manual.
The Translation Process
Consider a simple instruction meant to move a value into a register on an ARM-based microcontroller:
MOV R0, #10
When you feed this into an assembler (like gas or armasm), it performs a lookup in the instruction encoding table. It identifies the MOV operation, determines the destination register R0 (encoded as binary 0000), and embeds the immediate value 10 into the remaining bits of the instruction word. The result is a 32-bit machine code instruction, such as 0xE3A0000A in hex.
Why Relocatable Object Code?
The output of the assembler is typically a .o file, which is "relocatable." Because the assembler doesn't yet know where your final code will reside in the flash memory, it cannot resolve absolute memory addresses. Instead, it generates a binary structure containing:
- The Machine Instructions: The translated opcodes.
- The Symbol Table: A map of function names and global variables.
- Relocation Entries: A list of "to-do" notes for the linker, flagging which parts of the machine code need to be updated once the final memory layout is decided.
By creating these relocatable units, the assembler allows you to build your embedded application in modular pieces—compiling and assembling each source file independently—before the linker finally stitches them into a single, executable binary image. This modular approach is essential for managing the complexity of modern firmware.
Key Takeaway: The assembler acts as a translator that maps assembly mnemonics to binary opcodes and generates relocatable objects, leaving placeholders for the linker to finalize memory addresses during the final stage of system construction.