Many people learning x86 assembler have problems with the notation but once you understand how it works, it is in fact very clear precise notation that you know exactly what it is doing.
Here is a small tute showing how it works in PowerBASIC.
#IF 0 ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Build with PBCC50
Intel complex addressing mode.
BASE REGISTER --- INDEX --- MULTIPLIER --- DISPLACEMENT
mov esi, address ; ESI as base register
mov edi, 16 ; EDI as the INDEX
mov eax, [esi+edi*4+128]
copy the contents at address + index times 4 + displacement into register EAX
#ENDIF ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
FUNCTION PBmain as LONG
#REGISTER NONE
LOCAL svar as LONG
LOCAL uvar as DWORD
LOCAL parr as DWORD
dim iarr(0 to 9) as LONG ' dimension a 10 item integer array
iarr(0) = 0000
iarr(1) = 1111
iarr(2) = 2222
iarr(3) = 3333
iarr(4) = 4444
iarr(5) = 5555
iarr(6) = 6666
iarr(7) = 7777
iarr(8) = 8888
iarr(9) = 9999
parr = VarPtr(iarr(0)) ' get the address of the first element
! mov esi, parr ' use ESI as the BASE REGISTER
! xor edi, edi ' use EDI as the INDEX REGISTER
' --------------------------------
' read array by altering the INDEX
' --------------------------------
! mov eax, [esi+edi*4] ' get the content of member ZERO in one instruction
! mov uvar, eax
cout ustr(uvar)
! add edi, 6
! mov eax, [esi+edi*4] ' get the content of member SIX in one instruction
! mov uvar, eax
cout ustr(uvar)
' ---------------------------------------
' read array by altering the displacement
' ---------------------------------------
! mov eax, [esi+16] ' get the contents of member 4 in one instruction
! mov uvar, eax
cout ustr(uvar)
! mov eax, [esi+32] ' get the contents of member 8 in one instruction
! mov uvar, eax
cout ustr(uvar)
' -----------------------------------------
' read from a BYTE table in one instruction
' zero extend it to 32 bit and display it
' -----------------------------------------
' Using this notation the location "ctable" is effectively a DISPLACEMENT.
' For it to work it is an OFFSET determined at compile time.
! movzx eax, BYTE PTR ctable[51] ' disassembly >>> movzx eax,byte ptr [401453h]
! mov uvar, eax
cout ustr(uvar)
' setting a register like an INDEX changes the output OPCODE
' EDI becomes the BASE REGISTER and the location "ctable"
' is a DISPLACEMENT added to it.
! mov edi, 66 ' set the INDEX
! movzx eax, BYTE PTR ctable[edi] ' disassembly >>> movzx eax,byte ptr [edi+401420h]
! mov uvar, eax
cout ustr(uvar)
pause
FUNCTION = 0
Exit FUNCTION
#align 4
ctable:
! db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
! db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
! db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
! db 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0 ' numbers
! db 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ' upper case
! db 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0
! db 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ' lower case
! db 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0
! db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
! db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
! db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
! db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
! db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
! db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
! db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
! db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
End FUNCTION
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
DECLARE FUNCTION cc_out CDECL LIB "MSVCRT.DLL" ALIAS "puts" (BYVAL ptxt AS DWORD) AS DWORD
SUB cout(a$)
' ***** Supported escapes *****
' \0 = ascii zero 0
' \t = tab 9
' \n = newline 10
' \r = carriage return 13
' \q = double quote 34
' \\ = backslash 92
' *****************************
#REGISTER NONE
LOCAL src as DWORD
LOCAL dst as DWORD
LOCAL sln as DWORD
src = StrPtr(a$)
! mov esi, src
! mov edi, src
stlp:
! mov al, [esi]
! add esi, 1
! cmp al, "\"
! jne nxt
' ¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤
! cmp BYTE PTR [esi], "n"
! jne lb1
! add esi, 1
! mov BYTE PTR [edi], 10
! add edi, 1
! jmp stlp
lb1:
! cmp BYTE PTR [esi], "r"
! jne lb2
! add esi, 1
! mov BYTE PTR [edi], 13
! add edi, 1
! jmp stlp
lb2:
! cmp BYTE PTR [esi], "t"
! jne lb3
! add esi, 1
! mov BYTE PTR [edi], 9
! add edi, 1
! jmp stlp
lb3:
! cmp BYTE PTR [esi], "0"
! jne lb4
! add esi, 1
! mov BYTE PTR [edi], 0
! add edi, 1
! jmp stlp
lb4:
! cmp BYTE PTR [esi], "\"
! jne lb5
! add esi, 1
! mov BYTE PTR [edi], 92
! add edi, 1
! jmp stlp
lb5:
! cmp BYTE PTR [esi], "q"
! jne lb6
! add esi, 1
! mov BYTE PTR [edi], 34
! add edi, 1
! jmp stlp
lb6:
' ¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤
nxt:
! mov [edi], al
! add edi, 1
! test al, al
! jnz stlp
! sub edi, src
! mov sln, edi
cesc$ = left$(a$,sln)
cc_out StrPtr(cesc$)
END SUB
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
MACRO INPUT_HANDLE = -10&
DECLARE FUNCTION kbflush LIB "KERNEL32.DLL" ALIAS "FlushConsoleInputBuffer" ( _
BYVAL hConsoleInput AS DWORD) AS LONG
DECLARE FUNCTION hStdIn LIB "KERNEL32.DLL" ALIAS "GetStdHandle" ( _
BYVAL nStdHandle AS DWORD) AS DWORD
DECLARE FUNCTION SysYield LIB "KERNEL32.DLL" ALIAS "Sleep" ( _
BYVAL msWait AS DWORD) AS LONG
DECLARE FUNCTION keypress CDECL LIB "MSVCRT.DLL" ALIAS "_kbhit" () as DWORD
DECLARE FUNCTION putz CDECL LIB "MSVCRT.DLL" ALIAS "puts" (BYVAL ptxt AS DWORD) AS DWORD
' -------------------------------------------
SUB pause()
txt$ = "Press any key to continue ...."
putz StrPtr(txt$)
kbflush hStdIn(INPUT_HANDLE)
lbl0:
SysYield 20
keypress
! test eax, eax
! jz lbl0
END SUB
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
DECLARE FUNCTION ltoa CDECL LIB "MSVCRT.DLL" ALIAS "_ltoa" ( _
ByVal lval as LONG,ByVal pstr as DWORD,ByVal radix as DWORD) as DWORD
' -------------------------------------------
FUNCTION sstr(ByVal lval as LONG) as STRING
LOCAL astring as ASCIIZ * 32
ltoa(lval,VarPtr(astring),10)
FUNCTION = astring
End FUNCTION
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
DECLARE FUNCTION c_ultoa CDECL LIB "MSVCRT.DLL" ALIAS "_ultoa" ( _
ByVal uint as DWORD,ByVal pstr as DWORD,ByVal radix as DWORD) as DWORD
' -------------------------------------------
FUNCTION ustr(ByVal uint as DWORD) as STRING
LOCAL astring as ASCIIZ * 32
c_ultoa(uint,VarPtr(astring),10)
FUNCTION = astring
End FUNCTION
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
DECLARE FUNCTION c_atoi CDECL LIB "MSVCRT.DLL" ALIAS "atoi" (ByVal ptxt as DWORD) as DWORD
FUNCTION atoi(number$) as DWORD
FUNCTION = c_atoi(StrPtr(number$))
End FUNCTION
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
DECLARE FUNCTION c_atol CDECL LIB "MSVCRT.DLL" ALIAS "atol" (ByVal ptxt as DWORD) as LONG
FUNCTION atol(number$) as LONG
FUNCTION = c_atol(StrPtr(number$))
End FUNCTION
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Thank you Steve,
I think your code can be very helpful to many (http://www.fbsl.net/phpbb2/viewtopic.php?p=9217#p9217) for starters.
Mike Lobanovsky