The assembler processes your source code into a form that can be inserted at given location in main memory for execution by the processor. The assembler creates a “relocatable object file” from your source code - that is, it replaces the mnemonics in your source file with their machine code counterparts, and then writes the list of machine codes to a file that can be placed anywhere in the ARM memory map. Another program called the linker combines your program’s object file with any other needed system files (or other object files), resolves any shared resources (like data locations), and creates one file that can be placed in memory for execution. A simple loader program copies the unified object file produced by the linker to a predetermined memory location, sets a pointer to the first instruction of that file, and then enables the processor to execute that first instruction. From there, the processor keeps executing your program until it halts due to an error, or due to encountering a “breakpoint" (breakpoints are exceptions which cause the processor to halt normal operations and execute special/protected instructions - more on this topic in the next project).
Most assemblers are so-called “two pass" assemblers, because they read the source file twice. On the first pass, the assembler pays attention to labels, symbols, and directives, and builds the “symbol table". The symbol table creates a memory map that associates data items, labels, and symbols with particular memory addresses. The second pass converts the assembly instructions into machine codes, and uses the symbol table as needed to create numerical values (addresses) for labels used for branch addresses, data items, and other items.
Assembly Source Files
An assembly source file contains labels, comments, directives, and instructions. Labels are used in the source file to identify locations of instructions, data, and blocks of code. The assembler associates labels with memory addresses, and those addresses can be used by the executing program to locate data or blocks of code. All assembly programs start with a “main:" label to identify the address of the first instruction. The linker uses this label to “link” your source file with other system files as needed. All labels are text strings ending with a colon.
Directives are used to define symbols (or constants), define data storage areas, and to control the behavior of the assembler. In the Xilinx SDK tool, directives begin with a period followed by any one of several dozen predefined text strings. Every Assembler uses directives, but there are no standards for directives, so every Assembler tool will have its own collection of directives, and its own syntax for using them.
Vitis includes many Assembler directives, but only a few are commonly used. In our first program, we will use the .text, .global, .set, and .end directives. The text directive identifies the section of the source file that contains executable code, and it is always required. The global directive is used to identify a label that must be visible outside the current source file. For example, the .global directive is associated with the “main” label, and that makes “main” available outside of the source file (in this case, so Vitis can identifiy the first executable instruction of your program). The set directive allows symbols to be associated with numerical values (just like constants in other languages), and that can make your code more readable and easier to modify. The end directive tells the assembler where to stop the assembly process (everything after the end directive is ignored).
We will encounter a few more directives in later design projects.
Assembly program structure
Assembly programs in the Vitis tool all have a common format. The first line must contain the .text directive to define the source file section that contains executable instructions.
The next line must be the “.global main” directive to make the label “main” visible to other tools, and the first line of your assembly code must have the label “main”.
If you use constants, you can use the directive .set (or .equ) to define them in the next few lines.
After the label “main" come the assembly instructions, including comments (//) and labels. At the end of your program, the “.end directive informs the assembler there are no more instructions or directives.
.text .global main .set LED_BASEADDR, 0x4BB00000 main: /*Your code goes here*/ .end