来自于《Intel汇编语言》(第四版)第八章的一段程序。程序非常简单,以下为代码:
.data
count = 100
array WORD count DUP(?)
.code
push OFFSET array
push COUNT
call ArrayFill
ArrayFill PROC
push ebp
mov ebp,esp
pushad
mov esi,[ebp+12] ; offset of array
mov ecx,[ebp+8] ; array size
cmp ecx,0 ; ECX < 0?
jle L2 ; yes : skip over loop
L1:
mov eax,10000h ; get random 0-FFFFh
call RandomRange ; from the link library
mov [esi],eax
add esi,TYPE WORD
loop L1
L2:
popad
pop ebp
ret 8 ; clean up the stack
ArrayFill ENDP
让我们来看一下代码:
首先定义了一个count常量和一个WORD类型(16位)的数组。
然后程序首先将数组的偏移地址和COUNT压入堆栈:
push OFFSET array
push COUNT
此时的堆栈框架如下图所示:
offset(array) <---------- [EBP + 12]
count <---------- [EBP + 8]
return address <---------- [EBP + 4]
EBP <---------- [ EBP,ESP]
所以随后才会使用(全部相对于EBP来取地址):
mov esi,[ebp+12] ; offset of array
mov ecx,[ebp+8] ; array size
来取得数组的偏移地址和count,分别送到esi(数组游标)和ecx(数组大小)中去。
(保护模式下,堆栈上以4个字节为单位保存参数,所以参数的地址才分别为EBP+4,+8,+12)
之后的循环体中的代码主要如下:
mov eax,10000h ; get random 0-FFFFh
call RandomRange ; from the link library
mov [esi],eax
add esi,TYPE WORD
因为我们要为数组赋值16位随机整数,所以范围是 0到FFFFh,所以为eax中赋值10000h(1000h-1=FFFFh),之后调用作者Irvine的库函数RandomRange生成随机数,然后将此随机数放置到eax寄存器中,接着程序将此值赋值给[esi],然后esi接着移动到下一个数组元素位置。
这样便完成了使用16位随机整数填充数组的程序。
不过汇编语言中还有一个更方便的返回任意类型的间接操作数的偏移地址的伪指令,这就是LEA,LEA在获取堆栈参数的地址时特别有用。
下面的程序同样来自于《Intel汇编语言程序设计》,FillString使用随机的ASCII数字0~9来填充字符串:
FillString PROC USES eax esi
LOCAL string[20] : BYTE
;Create and fill a 20-bytes string with ASCII digits.
lea esi , string ; load effective address
mov ecx,20
L1: mov eax,10
call RandomRange ; AL = 0..9
add al,30h ; Covert to ASCII character
mov [esi],al
add esi,1
Loop L1
ret
FillString ENDP
这里使用lea方便的取得了局部数组变量的地址。
注意:string是一个间接操作数,因此下面的语句将会产生一个错误(MOV..OFFSET 只能用于直接操作数):
mov eax , OFFSET string ; error
另外关于LEA与OFFSET,摘自原书:
LEA指令返回任意类型的间接操作数的偏移。由于间接操作数可能使用一个或多个寄存器,因此其偏移值必须在运行时计算。相反,汇编运算符OFFSET仅返回编译时的偏移常量。