指令: 1:计算机中的指令有微指令、机器指令和伪(宏)指令之分; 2:微指令是微程序级命令,属于硬件范畴; 3:伪指令是由若干机器指令组成的指令序列,属于软件范畴; 4:机器指令介于二者之间,处于硬件和软件的交界面; 5:令汇编指令是机器指令的汇编表示形式,即符号表示 机器指令和汇编指令一一对应,它们都与具体机器结构有关,都属于机器级指令 。
操作码指计算机程序中所规定的要执行操作的那一部分指令或字段(通常用代码表示),其实就是指令序列号,用来告诉CPU需要执行哪一条指令或者执行什么功能。与地址不同,地址是所在的位置。
操作数:在应用指令中,内容不随指令执行而变化的操作数为源操作数,内容随执行指令而改变的操作数为目标操作数。操作数在C语言中包括常量、标识符、字符串、函数调用、下标表达式、成员选择符和复杂表达式,在汇编语言中也是重要的一部分,通过将操作数与操作符相结合的方式或者通过将操作符放在括号内的方式形成。
寻址方式:就是处理器根据指令中给出的地址信息来寻找有效地址的方式,是确定本条指令的数据地址以及下一条要执行的指令地址的方法。
立即数:通常把在立即寻址方式指令中给出的数称为立即数。 获得立即数方式:每个立即数由一个8位的常数循环右移偶数位得到。其中循环右移的位数由一个4位二进制的两倍表示。如果立即数记作,8位常数记作immed_8,4位的循环右移值记作rotate_imm,则有: =immed_8循环右移(2*rotate_imm) 这样并不是每一个32位的常数都是合法的立即数,只有能够通过上面构造方法得到的才是合法的立即数。 下面的常数是合法的立即数: 0xff,0x104,0xff0,0xff00 下面的数不能通过上述构造方法得到,则不是合法的立即数: 0x101,0x102,0xFF1
汇编指令是机器指令的符号表示(可能有不同的格式) mov、movb、bx、%bx等都是助记符, 指令的功能为:M[R[bx]+R[di]-6]←R[cl] ; R:寄存器内容;M:存储单元内容;
补充:(百度知道链接)
1:数据是储存在内存里,物理地址对应实际内存地址,是CPU访问存储器时实际使用的地址,为20位地址。 2:段地址指的是段起始地址的高16位;偏移地址指的是段内相对于段起始地址的偏移值(16位),偏移地址是相对的。 3:内存中的一个物理地址是可以映射到一个或多个逻辑段。
计算机中的数据存放 存储地址的描述与操作数的数据结构有关。 指令中需给出的信息: 操作性质(操作码) 源操作数1 或/和 源操作数2 (立即数、寄存器编号、存储地址):因为算术运算指令只对寄存器进行操作,为了访问存储器中的一个字, 指令必须给出存储器地址(address)。 目的操作数地址 (寄存器编号、存储地址) 变址寻址就是将寄存器(该寄存器一般称作基址寄存器)的内容与指令中给出的地址偏移量相加,从而得到一个操作数的有效地址。
PC程序计数器:下一条指令的地址; 内存:可寻址字节数组,支持程序代码和用户数据以及堆栈; **寄存器文件:**频繁使用的程序数据; 条件码:存储关于最新算术或逻辑操作的状态信息;是程序分支和程序循环的依据,也是双精度或高精度运算和浮点运算的基础; 最常用的条件码有: CF:进位标志。最近的操作使最高位产生了进位。可以用来检查无符号操作数据的溢出。 ZF:零标志。最近的操作得出的结果为0. SF:符号标志。最近的操作得到的结果为负数。 OF: 溢出标志。最近的操作导致一个补码溢出–正溢出或负溢出;
寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。 图片来源于金士顿 博文 入栈:esp = esp - 4;eax -> [ esp ]; 出栈 esp = esp + 4;[ esp ] -> eax; pop ebp;出栈 栈扩大4byte 因为ebp为32位; push ebp;出栈,栈减少4byte; ebp只是存取某时刻的esp,这个时刻就是进入一个函数内后,cpu会将esp的值赋给ebp,此时就可以通过ebp对栈进行操作,比如获取函数参数,局部变量等,实际上使用esp也可以;
程序代码理解:
int main() { Print(int a,int b); }在main函数中: 假设执行前的esp = t; push a: esp = esp - 4H; push b: esp = esp - 4H = t - 8H; call Print:函数的返回地址入栈(保存),esp = t - CH(12位)= esp - 4H;
在Print函数中: push ebp: 保护先前的ebp指针,ebp入栈,esp = t - 10H= esp - 4; mov esp,ebp:将ebp作为当前的esp; 并且ebp + 0CH = t - 4H;(a的位置); ebp + 08H = t - 8H;(b的位置); 此时的esp = t - 10H,即相差16位大小的空间;
实例 函数内部操作:
int sum(int x, int y) { int t = x + y; accum += t; return t; }汇编代码
//注意:accum为全局变量 sum: pushl %ebp //保护先前的ebp指针,ebp入栈,esp = t - 10H= esp - 4; movl %esp, %ebp //将ebp作为当前的esp; movl 12(%ebp), %eax //ebp + 12H = esp - 4H,即x 赋值给eax; addl 8(%ebp), %eax //ebp + 8H = esp - 8H,即y的值加上eax再赋给eax,就是t; addl %eax, accum //将eax的值加上accum再赋给accum; popl %ebp //将ebp退出栈,恢复到主函数中的栈帧底部,esp = t - 08H; ret //返回函数参数,返回eax的值,esp = t;约定:用“$”后面跟两个字符来表示一个寄存器**
寻址方式 SR段寄存器(间接)确定操作数所在段的段基址; 有效地址给出操作数在所在段的偏移地址;
在这个里面,每一个的段基址我们可以认为是esp所在的位置,如 a[100]的初始esp = 104,后面的元素的表示都是esp + i X 4; 二维数组b[ i ] [ j ]的地址就是504 + i x 8 + j x 2;因为 前面的a的数组占用的空间是100 + 100 x 4 = 500的大小,那么二位数组的段基址就是esp = 504; 而d[ i ] 的表示是544 + i x 8,因为double类型占用8位字节;
那么寻址的方式 1:寄存器寻址: movl %eax, %edx eax -> edx; 2:立即数寻址: movl $0x123, %edx 数字->寄存器 3:直接寻址: movl 0x123, %edx 直接访问内存地址数据,edx = *(int32_t *)0x123; 4:间接寻址: movl (%ebx), %edx %ebx 是个内存地址,(%ebx)指的是该地址中的数据,edx = (int32_t)ebx; 5:变址寻址: movl 4(%ebx), %edx edx = (int32_t)(ebx+4);
mov eax,[ebx * 4] ; 带比例的变址寻址 mov eax,[esi * 2+80h] ; 带比例的相对变址寻址 mov eax,[ebx+esi * 4] ; 带比例的基址变址寻址 mov eax,[ebx+esi * 8-80h] ; 带比例的相对基址变址寻址 (Rb,Ri) Mem[Reg[Rb]+Reg[Ri]]; D(Rb,Ri) Mem[Reg[Rb]+Reg[Ri]+D] (Rb,Ri,S) Mem[Reg[Rb]+S*Reg[Ri]]
主存以字节为可寻址单位,所以地址的加减是以字节为单位,比例1、2、4和8对应8、16、32和64位数据的字节个数,便于以数组元素为单位寻址相应数据。
例如指令 movl base(%ebx, %esi, 4), %eax 表示 %eax = [ base + %ebx + %esi * 4 ] 将 base + %ebx + %esi*4 指向的内存位置的值赋值给eax寄存器。 例如 leal 32(, %edx, 8), %eax ; %eax = 32 + ( %edx * 8 ) = 8 * (4 + %edx) 通常可以用lea指令表示一些乘法运算。
接着上面那个例子: a[i]:104+i×4,比例变址+位移; d[i]:544+i×8,比例变址+位移; b[i][j]: 504+i×8+j×2,基址+比例变址+位移(offset(base, index, scale)),将b[i][j]取到EAX中的指令可以是:“movw 504(%ebp,%esi,2), %eax”其中, i×8在ebp中,j在esi中,2为比例因子;
除了ebp和esp其他几个通用;
void swap (long *xp, long *yp) { long t0 = *xp; long t1 = *yp; *xp = t1; *yp = t0; } Register Value %rdi xp %rsi yp %rax t0 %rdx t1 swap: movq (%rdi), %rax # t0 = *xp movq (%rsi), %rdx # t1 = *yp movq %rdx, (%rdi) # *xp = t1 movq %rax, (%rsi) # *yp = t0 ret一个寄存器的大小占用32位的空间,如图寄存器与内存地址对应:
swap: movq (%rdi), %rax # t0 = *xp movq (%rsi), %rdx # t1 = *yp movq %rdx, (%rdi) # *xp = t1 movq %rax, (%rsi) # *yp = t0 ret其中xp,yp就是内存中的地址的表示,*xp 相当于(%rdi),*yp相当于(%rsi),就是数据值;示例
1:传送指令 通用数据传送指令 MOV:一般传送,包括movb、movw和movl等 MOVS:符号扩展传送,如movsbw、movswl等 MOVZ:零扩展传送,如movzwl、movzbl等 XCHG:数据交换 PUSH/POP:入栈/出栈,如pushl,pushw,popl,popw等 地址传送指令 LEA:加载有效地址,如leal (%edx,%eax), %eax”的功能为R[eax]←R[edx]+R[eax],执行前,若R[edx]=i,R[eax]=j,则指令执行后,R[eax]=i+j 输入输出指令 IN和OUT:I/O端口与寄存器之间的交换 标志传送指令 PUSHF、POPF:将EFLAG压栈,或将栈顶内容送EFLAG
2:顶点算术运算指令 定点算术运算指令 加 / 减运算(影响标志、不区分无/带符号) ADD:加,包括addb、addw、addl等 SUB:减,包括subb、subw、subl等 增1 / 减1运算(影响除CF以外的标志、不区分无/带符号) INC:加,包括incb、incw、incl等 DEC:减,包括decb、decw、decl等 取负运算(影响标志、若对0取负,则结果为0/CF=0,否则CF=1) NEG:取负,包括negb、negw、negl等 比较运算(做减法得到标志、不区分无/带符号) CMP:比较,包括cmpb、cmpw、cmpl等 乘 / 除运算(不影响标志、区分无/带符号) MUL / IMUL:无符号乘 / 带符号乘 DIV/ IDIV:带无符号除 / 带符号除
3:按位运算指令 逻辑运算(仅NOT不影响标志,其他指令OF=CF=0,而ZF和SF根据结果设置:若全0,则ZF=1;若最高位为1,则SF=1 ) NOT:非,包括 notb、notw、notl等 AND:与,包括 andb、andw、andl等 OR:或,包括 orb、orw、orl等 XOR:异或,包括 xorb、xorw、xorl等 TEST:做“与”操作测试,仅影响标志 移位运算(左/右移时,最高/最低位送CF) SHL/SHR:逻辑左/右移,包括 shlb、shrw、shrl等 SAL/SAR:算术左/右移,左移判溢出,右移高位补符 ROL/ROR: 循环左/右移,包括 rolb、rorw、roll等 RCL/RCR: 带循环左/右移,将CF作为操作数一部分循环移位
4:控制转移指令 指令执行可按顺序 或 跳转到转移目标指令处执行 无条件转移指令 JMP DST:无条件转移到目标指令DST处执行 条件转移 Jcc DST:cc为条件码,根据标志(条件码)判断是否满足条件,若满足,则转移到目标指令DST处执行,否则按顺序执行 条件设置 SETcc DST:将条件码cc保存到DST(通常是一个8位寄存器 ) 调用和返回指令 (用于过程调用) CALL DST:返回地址RA入栈,转DST处执行 RET:从栈中取出返回地址RA,转到RA处执行
示例 leaq a(b, c, d), %rax:作用是将(a + b + c * d )的计算地址赋给rax,地址就是 (a + b + c * d );相当于乘法运算; 两个操作数的指令的使用方法 一个操作数的指令