快速运算与x86内存定位公式
在编写x86汇编时,你是否有这样的苦恼?
- 做简单的乘法运算,都需要写很多行,还要将寄存器中的数据导来导去。
- 将寄存器放到其它寄存器中并加减一个数,都要写mov,add两条指令。
其实,借助x86灵活的内存定位方式,以及一个被“屈才”了的汇编指令,这些操作可以在一条汇编指令中实现。
地址定位公式
在8086汇编中,汇编指令支持简单的“地址定位公式”:
- [bx]
- [bx+立即数]
- [si/di+bx+立即数]
- ...
在x86中,地址定位支持了简单的乘法,寄存器的限制也变得宽松:
- [立即数]
- [寄存器] (可以是8个(32位)通用寄存器中的任意一个)
- [寄存器+立即数]
- [寄存器1*{2,4,6,8}+寄存器2]
- [寄存器1*{2,4,6,8}+寄存器2+立即数]
x86的灵活度大大提升,便利了内存的存取,也给了我们可乘之机。
LEA指令
说来有趣,绝大部分文档对LEA指令作用的说明中,只是将其称作有效地址传送指令,而笔者认为更贴切的说明应是“简单加乘运算指令”。
虽然在指令的实际执行中,LEA大部分情况还是被用来获得局部变量、数组元素等的地址,但cpu并不会检测所得“地址”是否可用,所以即使得到的是无效地址,指令也可以正常执行。
LEA指令在CPU内部执行的操作,便是把地址定位公式计算出来,放到目标寄存器中,由于无效地址也不影响执行,LEA便可以计算满足地址定位公式的表达式。
让我们解决刚开始提出的问题:
A.将一个寄存器(如ecx)的值放到其它寄存器(如edx)中并加一个数。
正常实现:
Mov Edx,Ecx
Add Ecx,idata
LEA实现:
Lea Ecx,[Edx+idata]
B:进行简单的加乘运算。
1)eax*=3
正常实现:
Push Eax
Add Eax,Eax
Add Eax,[Esp]
Add Esp,4
Lea实现:
Lea Eax,[Eax*2+Eax]
2)edx+=eax*8
正常实现:
Push Eax
Push Edx
Mov Edx,8
Mul Edx
Pop Edx
Add Edx,Eax
Pop Eax
Lea实现:
Lea Edx,[Eax*8+Edx]
可见,进行简单加乘运算时,用Lea更明了,简洁,效率也高些。
局限性
- LEA指令不会按照结果改变标志位,想要根据结果进行条件跳转还需要另加判断。
- 64位结果中的高32位会被丢弃。
- 乘法为无符号数乘法。
其它
笔者才疏学浅,若有错误疏漏,希望各路大神不吝赐教。
实例代码(出自MASM32Lib):
; #########################################################################
; ---------------------------------------------------------------
; 本程序最初由 Tim Roberts 编写
;
; Alexander Yackubtchik 优化了部分代码
; ---------------------------------------------------------------
.486
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
.code
; #########################################################################
atodw proc String:DWORD
; ----------------------------------------
; 十进制转dword
; eax储存返回值
; ----------------------------------------
push esi
push edi
xor eax, eax
mov esi, [String]
xor ecx, ecx
xor edx, edx
mov al, [esi]
inc esi
cmp al, 2D ;检测负号
jne proceed ;不是负号就跳转
mov al, byte ptr [esi]
not edx ;FFFFFFFF
inc esi
jmp proceed
@@:
sub al, 30h ;ascii->byte
lea ecx, dword ptr [ecx+4*ecx] ;ecx*=5
lea ecx, dword ptr [eax+2*ecx] ;ecx=ecx*2+eax
mov al, byte ptr [esi]
inc esi
proceed:
or al, al
jne @B ;非0(没处理完)上跳
lea eax, dword ptr [edx+ecx]
xor eax, edx
pop edi
pop esi
ret
atodw endp
; #########################################################################
end