Effective addressing

The notation to calculate the effective address of data in memory can look complicated but it is in fact very clear and precise notation.  In the range of allowable notation for Intel 80x86 assembler, an address in memory can be placed in a register and treated directly as a memory operand by enclosing it in square brackets.

! MOV EAX, lpArray ; Copy address into EAX

! MOV ECX, [EAX]   ; Copy 1st item in array into ECX

! ADD EAX, 4       ; Increment the array location by 4 bytes

! MOV ECX, [EAX]   ; Copy 2nd item in array into ECX

This works fine in simple situations where the register that has the address is manually incremented or decremented by the data size each time it is accessed, but there is a much more powerful and flexible technique available by using the standard Intel notation that is available.

The Intel 80x86 allows the following format to calculate the effective address of a value in memory:

[ Base Address + Index * Scale + Displacement ]

Base Address        The register that has the starting address or base address of the array (or buffer) in memory.

Index                      The register used to determine the offset from the base address.

Scale                      The data size based multiplier for the index.

Displacement       The additional offset adjustment from the base address.

For example:

[EBX + ECX * 4 + 8]

 

EBX is the Base Address

ECX is the Index

4   is the Scale based on the data size

8   is the Displacement in BYTES

Not all of the additional notation has to be used.  For example, in a Byte array, you can just use the base address and the index:

! MOV AL, [EBX + ECX]

The advantage of this technique is that you set the base address once and vary the index.  In the case above, ECX is the index.  In terms of flexibility, you have the choice of varying the base address, the index, and the displacement so that you can access data in memory by a number of different methods that best suit your code.

The only difference when using data sizes larger than Byte is that you multiply the "index" by the "scale" of the data size:

! MOV EAX, [EBX + ECX * 4]

To make a practical example let us assume we have an array of 64 items that were each 32-bits in size, and we wanted to read the 16th member of that 32-bit array.  In this case, we would copy the 16th member of the zero-based index into the register that we are using as the index.  Next, copy the address of the array into the register that you are using as the base address, and finally read the value of the array member into another register.

! MOV ESI, lpArray         ; Base address register

! MOV ECX, 15              ; Zero-based index register

! MOV EAX, [ESI + ECX * 4] ; Copy the value into EAX

These three lines of code read the target value from the array into the EAX register.

If we wanted to compare the 16th and 17th members of the array and not have to use an additional register, we can add the required displacement so that we only have an extra line of code:

! MOV EAX, [ESI + ECX * 4]

! CMP EAX, [ESI + ECX * 4 + 4]

To compare the 17th and 18th members of the array, all we need to do is increment the index:

! INC ECX

Writing to the array is simply the reverse of reading it.  With the same code as above:

! MOV ESI, lpArray          ; Base address register

! MOV ECX, 15               ; Zero-based index register

! MOV EAX, 1234

! MOV [ESI + ECX * 4], EAX

We can also write an immediate (literal) number to the array but it takes a slightly different notation:

! MOV DWORD PTR [ESI + ECX * 4], 1234

The extra notation "DWORD PTR" is because there is no way for the assembler to determine the data size from either the memory operand for the array or the immediate number.  Specifying the size tells PowerBASIC what data size should be written to the address contained in the memory operand.

A very similar notation is used when an array is placed on the stack by creating a LOCAL variable.  With a stack variable MyArray, PowerBASIC resolves this variable to an address on the stack, which will be something like this:

[EBP -(displacement)]

You can use the standard Intel notation in the same way as with a register:

! MOV EAX, MyArray[ECX * 4]

…which is effectively:

! MOV EAX, [EBP - (displacement) + ECX * 4]

 

See Also

The Inline Assembler

Addressing and pointers

Registers

Passing parameters