欢迎来看郑州大学汇编语言程序设计课程[doge]
传送指令
MOV指令
数据传送把数据从一个位置传送到另外一个位置,是计算机中最基础也是程序设计中最常用的指令。
除了标志寄存器传送指令外,均不影响状态标志。
上面图片展示了MOV操作指令的作用,即将源操作数传送到目的操作数的位置,此传送是复制传送,源操作数仍旧存在。
上面的图片展现了mov指令源操作数和目的操作数的可选情况,其中立即数不能做目的操作数,段寄存器和主存储器不能够同时为源操作数目的操作数。
cpu不能够同时访问同一个内存单元
mov al,200
mov ax,200
mov eax,200
;三种长度的传送,由于源操作数为立即数,因此传送的长度以目的操作数而定
mov bvar,byte ptr 200
mov [eax],word ptr 200
mov [esi+8],dword ptr 200
;按照指定长度进行数据的传送
常见语法错误
- 要注意源操作数和目的操作数的类型必须一致
- 源操作数和目的操作数不能够同时没有明确的数据类型
- 注意存储器相对寻址时,若偏移量有数据类型,那么这个寻址数据就有数据类型。
- CPU不能够同时访问内存单元(不能同时读写内存单元)
- 立即数不能直接传送段寄存器,对于段寄存器,不能够直接赋值,需要使用寄存器等中转一下
- 对于指令指针寄存器EIP,没有对其显示的操作指令,基本上不允许用户直接对他操作
XCHG指令
- 将源操作数和目的操作数内容互换
- 但是仅支持通用寄存器和存储器两种类型
- 源操作数和目的操作数不能够同时为存储器寻址
- 三种数据类型均支持
如下面的例子:
dvar dword 12345678H
mov al,byte ptr dvar
xchg al,byte ptr dvar+3
mov byte ptr dvar,al
mov al,byte ptr dvar+1
xchg al,byte ptr dvar+2
mov byte ptr dvar+1,al
首先将dvar变量的低8位移到通用寄存器中,然后将通用寄存器内容与变量高8位互换,最后再将通用寄存器内容放至变量低8位,即实现了变量高八位与低八位的互换。最后对于中间的十六位采用同样的方式进行互换,最后就完成了此变量小端存储方式和大端存储方式的互换。
NOP
这是一条空操作指令,没有什么实际的操作,等价于:
xchg eax,eax
但是这条指令的执行需要花费时间,并且占用一个字节的存储空间,可以使用NOP空操作指令来达到一个延时的效果,并且可以临时先占用代码的空间
LEA指令
- 这是一个地址传送指令,用来获取存储器操作数的地址
LEA r16/r32,mem
可以将存储单元的有效地址放到十六位或者三十二位寄存器中
LEA与offset的区别
- offset指令是在汇编阶段获取变量的偏移地址 而lea指令是在执行到这条语句时计算出偏移地址
- 因此offset指令更加快速,但是不如lea机动性强,对于一开始无法确定偏移地址的来讲需要使用lea指令,对于可以得来讲,使用offset要快
- lea指令在计算地址时可以进行加和位移操作而offset不能够这样使用
堆栈
- FILO存取原则
IA-32处理器在主存空间构建堆栈段
- 堆栈段寄存器SS指向堆栈段的起始位置
- 堆栈指针寄存器ESP指向当前栈顶
数据传输通过当前堆栈的栈顶,传输过程中栈顶会发生改变
而对于IA-32处理器,特殊的地方在于,这个堆栈是向下生长的,上面(高地址)是栈底,下面(低地址)作为栈顶。当使用PUSH压栈时ESP栈顶指针减小,使用POP弹出数据,栈顶指针ESP变大。
堆栈的作用
PUSH和POP指令
PUSH
push r16/m16/i16/seg
push r32/m32/i32
push指令可以将十六位或者三十二位的寄存器、存储器数据或者立即数,以及段寄存器数据压到栈中。
其原理是,先将栈顶指针ESP根据操作数的数据类型减2或者减4,然后将栈顶指针ESP所指的内存单元赋为操作数的值。
下面是几个例子:
push eax
;等同于下面两个指令
sub esp,4
mov [esp],eax
POP
同PUSH,减变为加
把当前栈顶的数据弹出给操作数
pop eax
;等同于下面两个指令
mov eax,[esp]
add esp,4
运算指令
状态标志是处理器最基本的标志
- 作为加减运算和逻辑运算的辅助结果
- 构成各种条件、实现程序分支
进位标志CF(Carry Flag)
当加减运算结果最高位有进位的时候或者有借位的时候,进位标志置为1,否则的话为0
对于无符号整数,这个标志为可以用来判断结果是否超出表达范围
[
0
,
2
N
−
1
]
[0,2^{N}-1]
[0,2N−1]
溢出标志OF(Overflow Flag)
用来表示有符号整数的运算结果是否有溢出,即加减结果是否超出表达范围,溢出置为1否则为0
[
−
2
N
−
1
,
2
N
−
1
−
1
]
[-2^{N-1},2^{N-1}-1]
[−2N−1,2N−1−1]
同符号数相加减结果符号位改变则溢出
零标志ZF(Zero Flag)
结果为0,ZF=1否则为0
符号位SF(Sign Flag)
运算结果最高位为1,则SF=1否则为0
奇偶标志PF(Parity Flag)
当运算结果的低八位中1的个数为偶数或0个时,PF=1否则为0
ADD指令
add reg,imm/reg/mem
add mem,imm/reg
加法指令令目的操作数加上源操作数赋值给目的操作数
- 按照定义影响状态标志位
SUB指令
sub reg,imm/reg/mem
sub mem,imm/reg
减法指令将目的操作数减去源操作数然后结果赋值给目的操作数
- 按照定义影响状态标志位
ADC指令
带进位的加法指令,
如下面的例子计算六十四位加运算:
mov eax,dword ptr qvar1
add eax,dword ptr qvar2
mov edx,dword ptr qvar1+4
adc edx,dword ptr qvar2+4
首先取六十四位的低三十二位进行add加法运算,然后如果这个运算有了进位,CF就会被设置为1,然后取高三十二位进行adc带标志位的加法运算,最后就完成了六十四位的加法运算
SBB指令
同adc一样,先取第三十二位sub相减,然后再高三十二位sbb相减
INC指令
增量指令(increment)
- 不影响进位标志,但是影响其他标志
INC reg/mem
经常用于计数器和地址指针的调整
DEC指令
减量指令(decrement)
同INC不过是自减
NEG指令
对操作数执行求补运算,即用0减去操作数
NEG reg/mem
对标志位的影响与减法指令sub一样
可用于对负数的求补或者由补码求其绝对值
MUL与IMUL乘法指令
MUL reg/mem;无符号整数乘法指令
IMUL reg/mem;有符号整数乘法指令
上面的乘法运算只有一个操作数,实际上进行操作的时候是借用了寄存器。以八位乘法为例,我们可以先将一个八位乘法的数据存储到AL寄存器中,然后再使用MUL指令对另一个八位数据操作,最后乘法的结果会保存到AX寄存器中。
以上的双操作数指令和三操作数指令,第一个操作数都是指定存放结果数据的位置,只能够是通用寄存器,并且我们要保证指定的寄存器要放得下乘积的结果
双操作数是两个操作数相乘放到目的操作数位置
三操作数是后两个操作数相乘放到目的操作数位置
DIV与IDIV除法指令
商的符号和除数的符号一致,余数的符号和被除数的符号一致
零位扩展
无符号数零位扩展前面加0实现
零位扩展指令MOVZX
前面补零放到目的寄存器中
movzx r16,r8/m8
movzx r32,r8/r16/m8/m16
符号扩展指令MOVSX
符号扩展前面加符号位扩展
movsx r16,r8/m8
movsx r32,r8/m8/r16/m16