DIRECT ADDRESSING

Posted by Atezaz | 6:38 PM | | 0 comments »

Now we will rewrite our first program such that the numbers 5, 10, and 15 are stored as memory variables instead of constants and we access them from there.

Example 2.1

001 ; a program to add three numbers using memory variables
002 [org 0x0100]
003 mov ax, [num1] ; load first number in ax

22


004 mov bx, [num2] ; load second number in bx
005 add ax, bx ; accumulate sum in ax
006 mov bx, [num3] ; load third number in bx
007 add ax, bx ; accumulate sum in ax
008 mov [num4], ax ; store sum in num4
009
010 mov ax, 0x4c00 ; terminate program
011 int 0x21
012
013 num1: dw 5
014 num2: dw 10
015 num3: dw 15
016 num4: dw 0

2 Originate our program at 0100. The first executable instruction should be placed at this offset.

3 The source operand is changed from constant 5 to [num1]. The bracket is signaling that the operand is placed in memory at address num1. The value 5 will be loaded in ax even though we did not specified it in our program code, rather the value will be picked from memory. The instruction should be read as “read the contents of memory location num1 in the ax register.” The label num1 is a symbol for us but an address for the processor while the conversion is done by the assembler.

13 The label num1 is defined as a word and the assembler is requested to place 5 in that memory location. The colon signals that num1 is a label and not an instruction.

Using the same process to assemble as discussed before we examine the listing file generated as a result with comments removed.


1
2 [org 0x0100]
3 00000000 A1[1700] mov ax, [num1]
4 00000003 8B1E[1900] mov bx, [num2]
5 00000007 01D8 add ax, bx
6 00000009 8B1E[1B00] mov bx, [num3]
7 0000000D 01D8 add ax, bx
8 0000000F A3[1D00] mov [num4], ax
9
10 00000012 B8004C mov ax, 0x4c00
11 00000015 CD21 int 0x21
12
13 00000017 0500 num1: dw 5
14 00000019 0A00 num2: dw 10
15 0000001B 0F00 num3: dw 15
16 0000001D 0000 num4: dw 0

The first instruction of our program has changed from B80500 to A11700. The opcode B8 is used to move constants into AX, while the opcode A1 is used when moving data into AX from memory. The immediate operand to our new instruction is 1700 or as a word 0017 (23 decimal) and from the bottom of the listing file we can observe that this is the offset of num1. The assembler has calculated the offset of num1 and used it to replace references to num1 in the whole program. Also the value 0500 can be seen at offset 0017 in the file. We can say contents of memory location 0017 are 0005 as a word. Similarly num2, num3, and num4 are placed at 0019, 001B, and 001D addresses.

When the program is loaded in the debugger, it is loaded at offset 0100, which displaces all memory accesses in our program. The instruction A11700 is changed to A11701 meaning that our variable is now placed at 0117 offset. The instruction is shown as mov ax, [0117]. Also the data window can be used to verify that offset 0117 contains the number 0005.

23


Execute the program step by step and examine how the memory is read and the registers are updated, how the instruction pointer moves forward, and how the result is saved back in memory. Also observe inside the debugger code window below the code for termination, that the debugger is interpreting our data as code and showing it as some meaningless instructions. This is because the debugger sees everything as code in the code window and cannot differentiate our declared data from opcodes. It is our responsibility that we terminate execution before our data is executed as code.

Also observe that our naming of num1, num2, num3, and num4 is no longer there inside the debugger. The debugger is only showing the numbers 0117, 0119, 011B, and 011D. Our numerical machine can only work with numbers. We used symbols for our ease to label or tag certain positions in our program. The assembler converts these symbols into the appropriate numbers automatically. Also observe that the effect of “dw” is to place 5 in two bytes as 0005. Had we used “db” this would have been stored as 05 in one byte.

Given the fact that the assembler knows only numbers we can write the same program using a single label. As we know that num2 is two ahead of num1, we can use num1+2 instead of num2 and let the assembler calculate the sum during assembly process.

Example 2.2

001 ; a program to add three numbers accessed using a single label
002 [org 0x0100]
003 mov ax, [num1] ; load first number in ax
004 mov bx, [num1+2] ; load second number in bx
005 add ax, bx ; accumulate sum in ax
006 mov bx, [num1+4] ; load third number in bx
007 add ax, bx ; accumulate sum in ax
008 mov [num1+6], ax ; store sum at num1+6
009
010 mov ax, 0x4c00 ; terminate program
011 int 0x21
012
013 num1: dw 5
014 dw 10
015 dw 15
016 dw 0

004 The second number is read from num1+2. Similarly the third

number is read from num1+4 and the result is accessed at num1+6.
The labels num2, num3, and num4 are removed and the data there
013-016
will be accessed with reference to num1.


Every location is accessed with reference to num1 in this example. The expression “num1+2” comprises of constants only and can be evaluated at the time of assembly. There are no variables involved in this expression. As we open the program inside the debugger we see a verbatim copy of the previous program. There is no difference at all since the assembler catered for the differences during assembly. It calculated 0117+2=0119 while in the previous it directly knew from the value of num2 that it has to write 0119, but the end result is a ditto copy of the previous execution.

Another way to declare the above data and produce exactly same results is shown in the following example.

Example 2.3

001 ; a program to add three numbers accessed using a single label
002 [org 0x0100]
003 mov ax, [num1] ; load first number in ax
004 mov bx, [num1+2] ; load second number in bx


24


005 add ax, bx ; accumulate sum in ax
006 mov bx, [num1+4] ; load third number in bx
007 add ax, bx ; accumulate sum in ax
008 mov [num1+6], ax ; store sum at num1+6
009
010 mov ax, 0x4c00 ; terminate program
011 int 0x21
012
013 num1: dw 5, 10, 15, 0

13 As we do not need to place labels on individual variables we can save space and declare all data on a single line separated by commas. This declaration will declare four words in consecutive memory locations while the address of first one is num1.

The method used to access memory in the above examples is called direct addressing. In direct addressing the memory address is fixed and is given in the instruction. The actual data used is placed in memory and now that data can be used as the destination operand as well. Also the source and destination operands must have the same size. For example a word defined memory is read in a word sized register. A last observation is that the data 0500 in memory was corrected to 0005 when read in a register. So registers contain data in proper order as a word.

A last variation using direct addressing shows that we can directly add a memory variable and a register instead of adding a register into another that we were doing till now.

Example 2.4

01 ; a program to add three numbers directly in memory
02 [org 0x0100]
03 mov ax, [num1] ; load first number in ax
04 mov [num1+6], ax ; store first number in result
05 mov ax, [num1+2] ; load second number in ax
06 add [num1+6], ax ; add second number to result
07 mov ax, [num1+4] ; load third number in ax
08 add [num1+6], ax ; add third number to result
09
10 mov ax, 0x4c00 ; terminate program
11 int 0x21
12
13 num1: dw 5, 10, 15, 0

We generate the following listing file as a result of the assembly process described previously. Comments are again removed.


1
2 [org 0x0100]
3 00000000 A1[1900] mov ax, [num1]
4 00000003 A3[1F00] mov [num1+6], ax
5 00000006 A1[1B00] mov ax, [num1+2]
6 00000009 0106[1F00] add [num1+6], ax
7 0000000D A1[1D00] mov ax, [num1+4]
8 00000010 0106[1F00] add [num1+6], ax
9
10 00000014 B8004C mov ax, 0x4c00
11 00000017 CD21 int 0x21
12
13 00000019 05000A000F000000 num1: dw 5, 10, 15, 0


The opcode of add is changed because the destination is now a memory location instead of a register. No other significant change is seen in the listing file. Inside the debugger we observe that few opcodes are longer now and the location num1 is now translating to 0119 instead of 0117. This is done automatically by the assembler as a result of using labels instead of


hard coding addresses. During execution we observe that the word data as it is read into a register is read in correct order. The significant change in thi s example is that the destination of addition is memory. Method to access memory is direct addressing, whether it is the MOV instruction or the ADD instruction.

The first two instructions of the last program read a number into AX and placed it at another memory location. A quick thought reveals that the following might be a possible single instruction to replace the couple.

mov [num1+6], [num1] ; ILLEGAL

However this form is illegal and not allowed on the Intel architecture. None of the general operations of mov, add, sub etc. allow moving data from memory to memory. Only register to register, register to memory, memory to register, constant to memory, and constant to register operations are allowed. The other register to constant, memory to constant, and memory to memory are all disallowed. Only string instructions allow moving data from memory to memory and will be discussed in detail later. As a rule one instruction can have at most one operand in brackets, otherwise assembler will give an error.

0 comments