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个寄存器可以看成通用寄存器。
假设将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指令类由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保存当前方法的栈帧指针。
指令 | 效果 |
---|---|
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 |
在2.1的寻址表格中,可以看到最后都会读取存储器的数据。
mov (%eax, %edx, 4), %ecx
M[%eax + %edx * 4]
复制到 %edx
lea (%eax, %edx, 4), %ecx
%eax + %edx * 4
复制到 %edx