X86汇编专题
{http://www.altdevblogaday.com/author/alex-darby/}
很基础的东西,by Alex Darby。一个做游戏的程序员写的。
{http://www.swansontec.com/sregisters.html}
介绍寄存器的选择问题,里面提到
- Intel的人一开始设计寄存器的时候,每个寄存器都有其特殊用途,这样可以方便对其进行优化
- 现在很多人使用寄存器已经不按照这些约定了
- 不过按照一些约定还是有很多好处的
- 按照约定使用寄存器使得每个寄存器self descriptive,这样你读汇编就和读到C的int i一样,那个i极大可能就是一个循环的counter
- 可以压缩代码。按照一样的模式写程序就会使得一样的代码不停的重复,使得代码压缩率提高
寄存器的约定:
- EAX - Accumulator Register 累积寄存器,应该把主要的计算操作放在这里
- EBX - Base Register 基地址寄存器,现在已经是一个general purpose register
- ECX - Counter Register 计数寄存器,和C的i类似,是一个universal loop counter
- EDX - Data Register 数据寄存器,与EAX合作,用来存储EAX运算所需的数据
- ESI - Source Index 源索引寄存器,loop要读取内存,ESI就是那个内存地址的指针
- EDI - Destination Index 目标索引寄存器,loop要写内存,EDI就是那个内存地址的指针
- EBP - Base Pointer 栈底寄存器,用来存储当前stack frame的地址,不过有时不需要产生新的stack frame,见下面EBP介绍
- ESP - Stack Pointer 栈指针寄存器,Sacred stack pointer。PUSH POP CALL和RET都影响ESP,不能派别的用途
EAX:
一共有三种主要的处理器架构(processor architecture):
- register:加减这些操作可以发生在任意两个寄存器中间
- stack:参与计算的参数是栈顶元素和栈里的其它元素
- accumulator:处理器只有一个计算寄存器叫做accumulator。所有的计算操作都发生在accumulator上,其它寄存器只作为数据存储空间。
X86不是accumulator架构的,但是它有一个寄存器EAX,它的功能和accumulator类似。比如九个基本操作(ADD,ADC,AND,CMP,OR,SBB,SUB,TEST,XOR)have special one-byte opcodes for operations between the accumulator and a constant. Specialized operations, such as multiplication, division, sign extension, and BCD correction can only occur in the accumulator.
因为很多操作都发生在EAX上,所以X86增加了很多优化指令从EAX移进移出数据。所以,计算的时候多多使用EAX
EDX
EDX是和EAX关系最紧密的寄存器。EDX作为EAX的64位扩展,EDX常常存储EAX计算所需的数据。EDX还用在IO指令里,EAX存储要读写的数据,EDX存储读写的源和目标地址
ECX
ECX和C语言里的计数变量i几乎是等价的,每个counting instruction使用ECX计数
EDI
要产生数据的loop都会把数据存在内存里,那么就需要一个moving pointer,这就是EDI。EDI保存所有字符串操作的写地址。STOS指令从accumulator里拷贝数据到内存并且增加EDI
ESI
ESI和EDI是类似的,只是它用于从内存里读数据
ESP EBP
这是X86函数调用的核心,他们是Stack Pointer按掉Base Pointer。函数调用发生时,必须把参数和返回地址压栈。在被调用函数里,函数吧base pointer设置成stack pointer,然后把函数内部的局部变量压栈,之后被调用函数里就通过base pointer来访问函数的参数和局部变量。为什么不用stack pointer呢?答案是:For some reason, the stack pointer lousy addressing modes. In 16-bit mode, it cannot be a square-bracket memory offset at all. In 32-bit mode, it can be appear in square brackets only by adding an expensive SIB byte to the opcode.
在代码里,stack pointer就是用来做栈顶指针,不能用来干别的,但是base pointer不同。如果你用寄存器传递参数,那么就没必要把stack pointer拷贝到base pointer了,那么base pointer就自由了,可以干啥都行
EBX
EBX是Base Register。现在是一个general purpose resiger,它的名字由来:请看英文原文
总结,看一个程序:
mov esi, source_address mov edi, destination_address mov ecx, loop_count my_loop: lodsd ;Do some calculations with eax here. stosd loop my_loop ;In this example, ECX is the loop counter, ESI points to the input data, and EDI points to the output data. Some calculations, such as a blur, filter, or perhaps a color look-up occur in the loop using EAX as a variable. This example is a bit simplistic, but hopefully it shows the general idea. A real routine would probably deal with much more complicated data than DWORD's, and would probably involve a bunch of floating-point as well.
{http://unixwiz.net/techtips/win32-callconv-asm.html}
Intel function call convention