Introduction to Assembly Language Addressing Modes
There are several Assembly Language Addressing Modes that we will discuss in this section: Immediate, Direct, Indirect, base-pointer, and Indexed mode. There are other modes, but these are the main modes you will use when starting out in assembly. Depending on which processor you are programming, the names of these modes may be different, but the basic concepts are the same.
These addressing modes are different ways of accessing data from our machine. We use each addressing mode for a particular purpose. For example, if we want to load data directly to a register, you might choose immediate addressing mode. If you need variable data from a memory location, then direct addressing is usually more practical. On the other hand, if you are working with arrays and loops, you might choose indexed or indirect modes.
Knowing which addressing modes are available helps us to plan out our program. Let’s take a look at each of these addressing modes, and how we would use them in our project.
Immediate Addressing
Immediate Addressing allows us to move a value directly into a constant. We might use this mode for the preset of how many times a loop might execute. Additionally, we might use this mode to set up registers we need for system calls, such as function codes, and byte lengths. Here is an example of how we would use immediate addressing to return a result code.
.section .data
.section .text
.globl _start
_start:
mov $5, %ebx
mov $1, %eax
int $0x80
Notice the dollar sign “$” in front of the values. In this assembler, and in this case, the dollar sign indicates the value is immediate. In other words, we are moving the numbers themselves into the registers. We are not moving the value of a memory location.
Remember, in the last post, I created a simple script that does the assembling and linking for us. Compile and link your code. After that, run your program. Type “echo $?”. You should get the result code of 5.
Direct Addressing
Direct addressing allows us to access memory. Keep in mind that each memory cell in your computer holds a value. We can move values out of memory cells, or into memory cells using Direct Addressing. For example, another part of our program might have calculated the current room temperature, and stored the temperature to a specific memory location. Here, we can access this memory location, and perform a compare to see if it’s below or above a certain value. If so, then we can take further action, such as turning on a heater or cooling unit.
Let’s take a look at a generic example with this code snippet.
.section .data
mydata:
.long 20
.section .text
.globl _start
_start:
mov mydata, %ebx
mov $1, %eax
int $0x80
As you can see, memory location “mydata” contains the value of 20. When we start the program, we simply move the data from “mydata”‘s memory location to the %ebx register. This will be the exit status code. Type “echo $?”, and you will see the result code is 20.
Indirect Addressing
With Indirect Addressing, we are not getting data directly from a register. The register we specify in our project simply tells us where the data is what we need to get. This is useful if you need to get data from different places in memory. For example, you might have various tanks in a system, and you need to retrieve the level from different tanks at different times.
Here is an example of indirect addressing:
.section .data
mydata:
.long 16
.section .text
.globl _start
_start:
mov $mydata, %rdx
mov (%rdx), %ebx
mov $1, %eax
int $0x80
As you can see, “mydata” holds the value of 15. After we start our program, we move the memory location of “mydata” to the %rdx register. In the second MOV instruction, we reference the memory location of “mydata” to get our value to load into the %ebx register. After that, we exit the program. Run your logic, then type “echo $?”. You will see the result code of our program is 16.
Base Pointer Addressing
Base Pointer Addressing works in a very similar way to indirect addressing with one exception. We add a base pointer to our starting address. For example, let’s say that we have 20 tanks, and we have labels that represent the bottom address for each of these tanks. The data for all of these tanks have the same structure. For example, the fourth value is the tank volume. We want to get this tank volume from each tank. In this case, not only would we specify the starting address for the tank, but we also need to apply an offset of 12 bytes. The reason we use 12 bytes is because each long integer is 4 bytes. For the first element, we wouldn’t apply an offset. The second element would be an offset of 4… likewise, the third element would be an offset of 8. Since we want the 4th element, our offset is 12.
.section .data
mydata:
.long 16, 14, 13, 9, 45, 76, 84
.section .text
.globl _start
_start:
mov $mydata, %rdx
mov 12(%rdx), %ebx
mov $1, %eax
int $0x80
Now, compile and run your program. Type “echo $?”, and you will see the result code is 9. This is because the value 9 is the fourth element of “mydata”.
Indexed Addressing
Indexed Addressing also allows us to have a variable address. Typically, this will be from a common reference point. This is very useful when we have data sets that we are operating on. For example, if we have a group of temperatures that we want to compare against a set point. We might be able to call for heat or cooling in each individual room using the same block of logic. This saves both memory, and programming time. Basically, with Indirect addressing, we look at a memory cell to determine what memory location to get data from.
Consider that we have five rooms. Each have a separate temperature sensor. We have these temperatures already stored to consecutive memory locations. We call this an “array”. Let’s take a look at how we would access each of these temperatures in the example below. We’ll assume that each temperature has the data type of “long”.
Normally, we would have a loop that increments our counter register, %ecx. However, to keep this simple, I’m just going to set %ecx to 5. Since loop counters start at zero, a value of five would be the sixth element.
.section .data
mydata:
.long 16, 14, 13, 9, 45, 76, 84
.section .text
.globl _start
_start:
mov $5, %ecx
mov mydata(,%rcx,4), %ebx ### Indexed Addressing
mov $1, %eax
int $0x80
In this case, we start with a base address, which is “mydata”. After that we multiply the value of %ecx by the last argument, which is 4. We would use a multiplier of 4 if we have LONG values. Again, this is because LONG integers consume 4 bytes each. Since we are using the sixth element as our result code, then we should return a value of 76. Assemble, and run your project. Type “echo $?”, and see if you get the proper result.
For more information, visit the Information Technology page!
— Ricky Bryce