数据格式

​ Intel处理器系列俗称x86,第一个版本是16位微处理器。Intel用术语 "字(word)"表示16位数据类型,变量通常以w为前缀,如wPort。用"双字(double word)"表示32位数据类型,变量通常以dw为前缀,如dwSize。用"四字(quad word)"代表64位,变量前缀通常为qw,如qwFileSize。

C语言与IA32对应的基本数据类型

C语言 Intel 汇编指令后缀 大小
char 字节 b 1
double 双精度 l 8
float 单精度 s 4
short w 2
int 双字 l 4
long int 双字 l 4
long long int - - 4
long double 扩展精度 t 10/12
char * 双字 l 4

mov指令有三个变种:movb(传送字节)、movw(传送字)、movl(传送双字)。使用后缀 “l” 表示双字是由于将32位数看成是"长字(long word)",沿用了16位体系的习惯。

访问信息

​ 一个IA32中央处理单元(CPU)包含一组8个存储32位值的寄存器,这8个寄存器都以%e开头,每个寄存器都有特殊用途。在平坦寻址(flat addressing,讲存储空间看作一个大字节数组)中,对特殊寄存器的需求极大降低。在大多数情况下,除了%ebp,%esp之外的6个寄存器可以看成通用寄存器。

%ah %al 31 15 8  7 0 %ch %cl %dh %dl %bh %bl %eax %ecx %edx %ebx %esi %edi %esp %ebp %ax %cx %dx %bx %si %di %sp %bp 栈顶指针 栈帧指针

操作数指示符

  • 立即数,即常数
  • 寄存器
  • 存储器引用

假设将R看成寄存器集合,Ea代表寄存器a,R[Ea]代表寄存器a的值。Mb[addr] 代表存储器中addr地址的b的字节,b通常省略。

类型 格式 操作数值 描述
立即数 $Imm Imm 立即数寻址
寄存器 Ea R[Ea] 寄存器寻址
存储器 Imm M[Imm] 绝对寻址
存储器 (Ea) M[R[Ea]] 间接寻址
存储器 Imm(Eb) M[Imm + R[Eb]] (基址+偏移)寻址
存储器 (Eb, Ei) M[R[Eb] + R[Ei]] 变址寻址
存储器 Imm(Eb, Ei) M[Imm + R[Eb] + R[Ei]] 变址寻址
存储器 (, Ei, s) M[R[Ei] * s] 比例变址寻址
存储器 Imm(, Ei, s) M[Imm + R[Ei] * s] 比例变址寻址
存储器 (Eb, Ei, s) M[R[Eb] + R[Ei] * s] 比例变址寻址
存储器 Imm(Eb, Ei, s) M[Imm + R[Eb] + R[Ei] * s] 比例变址寻址

举例:

地址 分割列 寄存器
0x100 0xFF %eax 0x100
0x104 0xAB %ecx 0x1
0x108 0x13 %edx 0x3
0x10C 0x11

求值:

操作数 解析
%eax 0x100 寄存器寻址
0x104 0xAB 存储器绝对寻址
$0x108 0x108 立即数
(%eax) 0xFF 寄存器间接寻址
4(%eax) 0xAB (基址+偏移)寻址,M[4 + 0x100] = M[0x104] = 0xAB
9(%eax, %edx) 0x100 变址寻址,M[9 + 0x100 + 0x3] = M[0x10C] = 0x11
260(%ecx, %edx) 0x13 变址寻址,M[260 + 0x1 + 0x3] = M[0x104 + 0x4] = 0x13
0xFC(, %ecx, 4) 0xFF 比例变址寻址,M[0xFC + 0x1 * 4] = M[0x100] = 0xFF
(%eax, %edx, 4) 0x11 比例变址寻址,M[0x100 + 0x3 * 4] = M[0x10C] = 0x11

数据传送指令

MOV指令

​ 将数据从一个位置复制到另一个位置。mov指令类由3条指令组成:movb(byte)/movw(word)/movl(long(double) word)。

指令 效果
MOV S, D D ⬅ S
movb 传送字节
movw 传送字
movl 传送双字
MOVS S, D D ⬅ S(符号扩展)
movsbw 将做了符号扩展的字节传送到字
movsbl 将做了符号扩展的字节传送到双字
movswl 将做了符号扩展的字传送到双字
MOVZ S, D D ⬅ S(零扩展)
movzbw 将做了零扩展的字节传送到字
movzbl 将做了零扩展的字节传送到双字
movzbl 将做了零扩展的字传送到双字
push S R[%esp] ⬅ R[%esp - 4]; //栈扩展4字节
M[R[%esp]] ⬅ S; //传送S到栈顶
pop D D ⬅ M[ R[%esp] ];//将栈顶数据传送到D
R[%esp] ⬅ R[%esp] + 4; // 栈收缩4字节

​ MOV类指令是将源操作数的值复制到目的操作数中。源操作数可以是:立即数/寄存器值/存储器值。目的操作数是一个位置:寄存器/存储器地址。IA32加了一条限制:源操作数和目的操作数不能同时为存储器值。MOV指令的物种可能组合:

指令 描述
movl $0x4050 %eax 立即数 ➡ 寄存器
movw %bp sp 寄存器 ➡ 寄存器
movb (%edi, %ecx), %ah 存储器 ➡ 寄存器
movb $-17 (%esp) 立即数 ➡ 存储器
movl %eax -12(%ebp) 寄存器 ➡ 存储器

​ movs和movz都是将较小的源数据复制到较大的位置中,高位可用符号扩展或零扩展。

  • 符号扩展:高位用符号位填充
  • 零扩展:高位用零填充

​ 在IA32中,程序栈存放于存储器的某个区域,如下图所示,栈向下增长,越往下地址越小,%esp寄存器保存栈顶指针,%ebp保存当前方法的栈帧指针。

0x108 地址增长 栈底 栈顶,esp指针 0x123 0x108 0x104 0x108 初始状态: %eax=0x123 %edx=0 %esp=0x108 pushl %eax之后: %eax=0x123 %edx=0 %esp=0x104 pupl %eax之后: %eax=0x123 %edx=0x123 %esp=0x108 栈顶,esp指针 栈顶,esp指针

算术和逻辑操作

指令 效果
leal S, D 将S的地址加载到D中
inc D 加1
dec D 减1
neg D 取负
not D 取反
add S, D
sub S, D
imul S, D
xor S, D 异或
or S, D
and
sal k, D 算术左移,右边补0
shl k, D 逻辑左移,右边补0,sal=shl
sar k, D 算术右移,左边补符号位
shr k, D 逻辑右移,左边补0

lea与mov的区别

​ 在2.1的寻址表格中,可以看到最后都会读取存储器的数据。

  • mov (%eax, %edx, 4), %ecx
    • M[%eax + %edx * 4] 复制到 %edx
  • lea (%eax, %edx, 4), %ecx
    • %eax + %edx * 4 复制到 %edx