The previous simple example illustrates a trivial byte array and how we can access its members. The same would apply to arrays of words, double words, or quad words with a few additions:
- We cannot use XLAT on arrays bigger than 256 bytes, nor if members of an array are bigger than 8 bits
- We would need to use SIB addressing (scale index base) in order to access array members bigger than one byte
- On 32-bit systems we would not be able to read a quad word into a single register
For the sake of a simple example, let's consider using a lookup table for the calculation of the factorial for numbers in the range of 0 to 12 (this code is for 32-bit and factorials of larger numbers would not fit into a double word). Although the algorithm of factorial calculation is rather simple, using a lookup table even for such a short range is much more convenient.
First, put the following into the data section (you may put this into the code section too, as we are not going to change any value here, but let's keep data with the data):
ftable dd 1,\ ; 0!
1,\ ; 1!
2,\ ; 2!
6,\ ; 3!
24,\ ; 4!
120,\ ; 5!
720,\ ; 6!
5040,\ ; 7!
40320,\ ; 8!
362880,\ ; 9!
3628800,\ ; 10!
39916800,\ ; 11!
479001600 ; 12!
This is our lookup table containing 13 values of factorials for numbers in the range of 0 to 12, where each entry is double word (32 bit). Now, let's write a procedure that would use this table. The procedure will be implemented in accordance with the stdcall calling convention; it receives a single parameter, the number for which we need a factorial, and returns a factorial for the given number or 0 if the number is not in the allowed range (as 0 cannot be a value of factorial). Put the following code into the code section:
factorial:
push ebp
mov ebp, esp
;-------------
virtual at ebp + 8 ; Assign a readable name to
arg0 dd ? ; a location on stack where
end virtual ; the parameter is stored
;-------------
mov eax, [arg0] ; Load parameter from the stack
cmp eax, 0x0c ; Check whether it is in range
ja .oops ; Go there if not
mov eax, [ftable + eax * 4] ; Retrieve factorial from
; the lookup table
@@:
leave
ret 4
.oops:
xor eax, eax ; Set return value to 0
jmp @b
virtual at ebp + 8
arg0 dd ?
arg1 dd ?
; the rest
end virtual
Here, arg1 would be translated to ebp+12, arg2 (if defined), ebp+16, and so on.
The procedure is indeed very simple as all it does is this:
- Checks whether the parameter fits the range
- Returns 0 if the parameter does not fit the range
- Uses the parameter as an index into the lookup table and returns a value referenced by the base address of the tables plus index (our parameter) times size of entries in the table