16位汇编第六讲汇编指令详解第二讲
1.比较指令
CMP指令
1.CMP指令是将目的操作数减去源操作数,按照定义相应的设置状态标志
2.CMP指令执行的功能与SUB指令(相减指令)一样,但是不同的是CMP指令之根据结果设置标志位
而不修改值
可以操作的指令格式
CMP reg,imm/reg/mem
CMP mem,imm/reg
上面是CMP指令的语法,具体的也可以查询帮助文档,inter手册
inter手册查的办法
第一个框代表了CMP指令的所有语法
比如
reg,reg 表示可以比较寄存器 CMP AX,BX ....
下面的则是机器的操作码.根据二进制的机器码可以反逆向出来汇编指令
比如:
机器码是39代表的是CMP指令
一般CMP的指令,都是设置标志位的,然后一般会和别的指令成对执行,比如比较完毕就判断结果.
汇编例子
代码语言:javascript复制cmp al,100
jz below
;al == 100会跳转到below执行(jz下面将,这里理解为跳转)
2.CPU的流水线,汇编的无分支,以及优化
什么是CPU的流水线,这样说吧,上面我们说了,CMP会和跳转一起使用,但是你知道这样的代码吗?
使用一条跳转,可以执行很多条指令.CPU的指令周期很长,这里说一下强制跳转JMP
看下JMP
可以看出,最快的需要15个指令周期,最慢的需要24 (EA:寻址方式,的有效寻址的周期)大小
那么我们可以优化一下吗
比如 C语言中的三目运算符 a == 0 ? 0:-1; 如果a < 0,成立,则a = 0,否则= -1
如果在汇编中你想怎么写, 是不是先判断ax == 0? 如果 ==0 ,我就跳转到0的地方,执行,否则跳转到-1的地方执行
最少需要两个跳转是吗
这样浪费了很多指令周期
所以我们可以写成这样
代码语言:javascript复制mov ax,3
neg ax
sbb ax,ax
我敢说,学过汇编的人从来都只是说学过,而汇编是一门艺术,我们学习汇编,并不是学习他的指令或者语法,比如上面简单的三条汇编指令,会汇编的人都能明白,但是我想问一下,什么意思懂吗?
这个就是无分支三目运算符
简单看一下吧
ax = 3 (浪费了4个指令周期,因为有立即数,要内存寻址,所以该浪费的还是得浪费)
neg ax 这个是对3求补,上堂课也说了,汇编的求补指令的原理就是 0 - 操作数(ax))的结果
0 - 3的话,计算机也不会算,所以要把-3变为补码,让0去相加
原码: 1000 0011 (负数的源码最高位是1)
反码 1111 1100 (反码最高位不变,其余各位取反)
补码: 1111 1101 (-3的补码)
0 上补码
0000 0000
1111 1101
1111 1101 (还是负数)
在计算机中表示 FD 此时CF位被设置,因为计算机判断进位的方法就是看最高位,如果最高位以前是0,那么经过计算最高位为1,则判断进位了.
CF = 1
SBB ax,ax
带借位的相减
首先ax的值就是FD了,两个相减没了,但是注意,这里有符号位,所以相减的同事要把符号位减去
1111 1101
-
1111 1110(需要加上符号位1,那么二进制就成为了这种)
= F (-1)那么看上面的推理过程
结果就是给一个3,对齐求补码,然后算出结果为-1
当然这个只是教怎么玩汇编,不过分析程序的时候可能遇到这种优化
CPU的流水线
上次我们说了一个JMP指令,指令周期特别长,我们可以优化成上面的那样,但是仅仅是优化了吗?
我们不妨这样想,上面的确实是优化了,但是其实内部还有CPU的流水线的优化
什么是流水线
比如一个工厂,组装汽车的,分为三步骤
第一步,取配件 (取指令)
第二步,组装 (译码(解析指令))
第三步,喷漆 (CPU执行)
我们需要三个工人,可以这样想,第一个人专门取配件,第一次执行的时候,组装的喷漆的都等待
当配件拿到手了,那么开始组装(这个时候第一个人又去取配件了),这时候喷漆的等着
当给了喷漆的了,那么这个时候喷漆,组装,已经可以正常开始工作了.
例如
取配件 组装 喷漆
取配件 组装 喷漆
取配件 组装 喷漆...
只有第一次执行的时候,组装需要等待取配件,喷漆等待组装,第一次组装的时候,第二次已经开始了
上面是什么意思那,就是说,组装需要等待,喷漆也要等待.我们可不可以错开,不让他们依赖指向性
比如流水线的代码
代码语言:javascript复制mov ax,1
sub ax,2
cmp ax,ax
每次的结果都需要等待上一次的结果,我们可以写成这样
代码语言:javascript复制1 mov ax,1
2
3 mov bx,1
4
5 sub ax,2
6
7 mov cx 3
8
9 cmp ax,ax
其中第二行,和倒数第二行都是我随便加的,就是为了不让指令依赖于上次执行,这就是最简单的流水线,不用等待.
在这里可以说下上面的三目运算符的优化了,为什么不光光是优化,以为JMP跳转的时候,CPU的流水线可能正常执行,比如已经知道到组装了,这个时候你来个跳转,那么又要从头开始,而且组装后面的都不执行了,所以不光光是为了优化掉跳转,还有流水线的作用,上面的代码看着很恶心,可是真是的环境就是这样,不是教你怎么去写,而是教你怎么去看,让你明白他为什么这样写.当然流水线的优化还有很多种.这里只是最简单的一个例子
3.乘法指令
MUL (无符号字节乘法)
指令格式: ax = al * r8/m8
ax(16位寄存器)存放 al * r8(八位寄存器)或者 m8(内存中八位的值)
看下inter手册
这里看一下,除法的指令周期很长,最低的70-77,所以也可以优化
这里可以看出 al要放乘数 其余寄存器放乘数
例如 ax = bl * al(他是乘数,你给多少,都是和他相乘的)
汇编例子
代码语言:javascript复制mov al,2 (倍数是2倍)
mov bl,8
mul bl
此时算出的记过就放在ax中,因为8位*8位的数字不会超过16位的
无符号的字乘法
当我们16位*16位的怎么办,8 *8的结果是放在ax中
16 *16 则放在 DX(数据寄存器)(AX累加寄存器)当中
高16位放到DX当中,低16位放到AX中
其中乘法的 操作数都需要我们自己给,比如 MUL bl, 算出bl的乘法,默认会和al相乘
乘法指令是利用 OF(overflow溢出标志)和CF(进位标志)来判断乘积的高一班是否具有有效数值
有符号的字节乘法
IMUL r8/m8
ax = al * r8/m8 (和上面一样,结果放到ax中,al可以×八位的寄存器,或者内存取出的数值的8位数值
和内存取出来的数值相乘(400的偏移处我给的是11所以最后ax结果是11)
有符号的字乘法
16*16的和无符号的一样
高位放到DX当中,低位放到AX当中
谈到这里我们发现,乘法的指令周期特别长,我们也可以做优化,可以用位运算
对标志位的影响:
会影响OF和CF标志
4.位运算,左移右移
1.左移指令
这个算是附加的,主要是我们要用位运算吧乘法优化掉
SHL 左移 ,高位补零,看下语法
可以是寄存器,内存,不支持立即数,因为立即数哪里都是- ,带有1的则是默认写的时候是左移一位,只能写1
如果给1以上,就要放到CL当中了
寄存器和CL低八位寄存器(注意这里是CL则我们写的时候要注意如果寄存器左移动的时候,则给CL指定个数)
左移,最高位补零
2的二进制
0000 00010 SHL 之后 0000 0100 (4)
8086不支持这样写
代码语言:javascript复制SHL ax,3
;支持这样写 SHL ax,1 默认的是往左移动一位
上面第一句,那些是以后出现的
说道左移,则可以用它来替代掉乘法
例如
代码语言:javascript复制mov al,2
mov bl,3
mul al
替换成
mov bl,3
mov cl,2
shl bl,cl
仔细看一下,我们转大了,inter指令周期最起码缩少了10倍,所以说有的时候写一行汇编代码,需要想很长时间,
比如
mov ax,0
你认为是很快了是吗,其实inter指令周期是4,不行的话自己可以查询看一下, reg,imm这一行
但是你写为
xor ax,ax (xor代表异或的意思,相同为假,不同为真,ax和ax肯定各个二进制位相同,此时相同为0,则都变成0了)
和上面的一样,ax都是变为0,而我则赚了一个1个指令周期,其实还有很多这样的汇编代码,都是这样做出来的
所以说学习汇编,把它当做一门艺术来看.
2.右移指令
SHR 逻辑右移,SAR算术右移
两个的不同
SHR 移动的时候,以0来填充
SAR 移动的时候,符号位填充,也就是真正的右移
和左移相反
右移也可以用于正数的除法
但是除法有除法优化的原理,以后讲,这里掌握两个指令即可.
5.除法指令
除法指令也分为有符号除法,和无符号除法
ax / r8,m8的商,放到AL中,余数放到AH中
16位除法
ax /r16,m16, 16位的商放到AX当中(也就是结果放到AX中),余数放到dx中
DIV (无符号字节除法)
指令 DIV r8/m8
或者 DIV r16/m16
6.符号扩展
什么是符号扩展?
符号扩展是指用一个操作数的符号位(也就是最高位)扩展变大,比如8位变为16位,符号扩展不改变数据大小
CBW al符号扩展至AH (字节扩展)
如果al的最高有效位为0,则AH ==00
AL的最高有效位为1,折AH = FFH AL不变
字扩展
CWD AX的符号扩展至DX
AX的最高有效位是0,则DX ==00
如果为1,则DX = FFFFH AX不变
学习资料:
链接:http://pan.baidu.com/s/1hsKi3sw 密码:yis3