I sometimes get asked about the notation used in current 32 bit Intel x86
assembler when you get complex looking expressions in code like,
One of the good things about PowerBASIC is its full capacity in writing
code in direct assembler, a slightly different concept to what many other
languages call "inline assembler".
The basic notation mentioned above is related to dealing with addresses in
memory. By a simple mechanism, you can obtain the "value" at a given address
in memory by what is called "dereferencing".
If we create a simple PowerBASIC array in 32 bit LONG integers,
and fill it with numbers,
We now have 4 LONG values that follow each other in memory. Getting the
starting address is a simple function in PowerBASIC.
We then use the assembler to copy the address into a register.
If you check the number in ESI, it will be a DWORD size number that is an address
in memory. To get the "value" of the number AT that address, the address must
be "dereferenced". This is done by the following notation.
If you check the contents of ESI now, it will have the number in the array element,
Arr(0) which is 10. Note that the register that the address is "dereferenced" into
need not be the same register withing the square brackets, it can be any available
register.
This is fine for the first element but there is a notation that allows access at
any other element as well. In Intel assembler there is a build in capacity to
produce the required offset to get any value.
The notation is broken down as follows.
With an instruction like,
there are 4 elements that can be used to calculate the address of the member of the
array that is required.
We already have the base address in the ESI register. The "INDEX" and "SCALE" work
together to address the array element by its number in the array multiplied by the
size of the array element which in this case is 4 bytes. The displacement can be
added if it is required to change the memory offset by a set amount.
Now to get array element 2, we set the ECX register to that number and then copy the
value at the correct location to another register.
The register EAX now holds the value in the 3rd element of the array which is 30.
The real use of this technique is when you have to sequentially read or write to
members of the array. By making a loop that increments or decrements ECX, the
members of the array can be scanned in sequence at very high speed because of the
minimum amount of code required to do the scan.
Scan the array forward.
Scan the array in reverse.
If you wanted to increment every member of the array, the code is very simple.
For PowerBASIC programmers who have to work with arrays at high speed, this
built in capacity in the inline assembler allows you to do things that can
dramatically increase the speed of the operations being performed.
Regards,
[email protected]
PS. Sorry about the typos, its getting a bit late here.
[This message has been edited by Steve Hutchesson (edited September 11, 2001).]
assembler when you get complex looking expressions in code like,
Code:
mov eax, [esi+ecx*4+4]
code in direct assembler, a slightly different concept to what many other
languages call "inline assembler".
The basic notation mentioned above is related to dealing with addresses in
memory. By a simple mechanism, you can obtain the "value" at a given address
in memory by what is called "dereferencing".
If we create a simple PowerBASIC array in 32 bit LONG integers,
Code:
redim Arr(0 to 3) as LONG
Code:
Arr(0) = 10 Arr(1) = 20 Arr(2) = 30 Arr(3) = 40
starting address is a simple function in PowerBASIC.
Code:
lpArr = VarPtr(Arr(0))
Code:
! mov esi, lpArr ; copy address into ESI
in memory. To get the "value" of the number AT that address, the address must
be "dereferenced". This is done by the following notation.
Code:
! mov esi, [esi]
Arr(0) which is 10. Note that the register that the address is "dereferenced" into
need not be the same register withing the square brackets, it can be any available
register.
This is fine for the first element but there is a notation that allows access at
any other element as well. In Intel assembler there is a build in capacity to
produce the required offset to get any value.
The notation is broken down as follows.
With an instruction like,
Code:
! mov eax, [esi+ecx*4+4]
array that is required.
Code:
Base address Index Scale Displacement [ ESI + ECX * 4 + 4 ]
together to address the array element by its number in the array multiplied by the
size of the array element which in this case is 4 bytes. The displacement can be
added if it is required to change the memory offset by a set amount.
Now to get array element 2, we set the ECX register to that number and then copy the
value at the correct location to another register.
Code:
! mov ecx, 2 ! mov eax, [esi+ecx*4]
The real use of this technique is when you have to sequentially read or write to
members of the array. By making a loop that increments or decrements ECX, the
members of the array can be scanned in sequence at very high speed because of the
minimum amount of code required to do the scan.
Scan the array forward.
Code:
! xor ecx, ecx ; set ECX to 0 label: ! mov eax, [esi+ecx*4] ; copy the value into EAX ! inc ecx ; increment ECX ! cmp ecx, 4 ; see if its at the end of the array ! jne label ; jump back to loop start if its not
Code:
! mov ecx, 3 label: ! mov eax, [esi+ecx*4] ; copy the value into EAX ! dec ecx ; decrement ECX ! jnz label ; if ECX is NOT zero jump to label
Code:
! mov ecx, 4 label: ! inc [esi+ecx*4] ; increment the value in memory ! dec ecx ; decrement ECX ! jnz label ; if ECX is NOT zero jump to label
built in capacity in the inline assembler allows you to do things that can
dramatically increase the speed of the operations being performed.
Regards,
[email protected]
PS. Sorry about the typos, its getting a bit late here.
[This message has been edited by Steve Hutchesson (edited September 11, 2001).]
Comment