2020.02.10-2020.02.16
汇编学习
offset
功能是取得标号的偏移地址
1 | assume cs:codesg |
offset取得了标号start和s的偏移地址:0和3
jmp
依据位移进行转移的jmp指令
1.jmp short 标号
实现段内短转移,对ip修改范围为:-128~127字节
例:
1 | .... |
执行后ax=1
“依据位移进行转移”:指令对应的机器码中不包含转移的目的地址,而是转移的位移,位移由编译器根据汇编指令计算
jmp short 标号 == (ip)+=8位位移
- 8位位移=标号处的地址 - jmp指令后第一个字节的地址
- short 指明位移为8位
- 8位位移在编译时算出,在机器码中用补码表示
2.jmp near ptr 标号
与jump short 标号 相似
功能为:ip+16位位移
转移的目的地址在指令中的jmp指令
1.jmp far ptr 标号
用标号的段地址和偏移地址修改cs和ip
实现的是段间转移(远转移)
功能为:
(cs)=标号所在段地址
(ip)=标号所在偏移地址
机器码:EA0B01BD0B
对应:jmp 0BBD:0B01
转移地址在寄存器中的jmp指令
1.jmp 16位寄存器
(ip)=(16位寄存器)
转移地址在内存中的jmp指令
1.jmp word ptr 内存单元地址(段内转移)
内存单元地址处开始存放的一个字作为偏移地址
内存单元地址可用寻址方式的任一格式给出
2.jmp dword ptr 内存单元地址(段间转移)
高地址处的字是转移到目的地段地址,低地址处是转移到目的偏移地址:
(cs)=(内存单元地址+2)
(ip)=(内存单元地址)
如:
1 | mov ax,0123 |
(cs)=0,(ip)=0123
jcxz
为有条件转移指令
所有有条件的转移指令都是短转移,在机器码中包含的是位移而不是目的地址,ip修改范围为:-128~127
格式:jcxz 标号
相当于:
1 | if((cx)==0) |
loop
所有循环指令都是短转移在机器码中包含的是位移而不是目的地址,ip修改范围为:-128~127
格式:loop 标号
相当于:
1 | (cx)--; |
根据位移进行转移的意义
在机器码中包含的是位移而不是目的地址—这种设计方便了程序段在内存中的浮动装配,在不同位置都可正确执行
call和ret指令
call和ret都是转移指令,修改ip或同时修改cs和ip
ret和retf
ret用栈中的数据修改ip实现近转移,相当于:
pop ip
retf用栈中的数据修改cs和ip实现远转移,相当于:
pop ip
pop cs
call
CPU执行call时:
1.将ip或cs和ip压入栈
2.实现长转移
call不能实现短转移
1.依据位移进行转移的call指令
call 标号
执行时进行如下操作:
1 | (sp)=(sp)-2 |
将当前ip压栈后,转到标号处
相当于
1 | push ip |
2.转移的目的地址在指令中的call指令
call far ptr 标号
执行时进行如下操作:
1 | (sp)=(sp)-2 |
相当于:
1 | push cs |
3.转移地址在寄存器中的call指令
call 16位寄存器
相当于
1 | push ip |
4.转移地址在内存中的call指令
4.1.call word ptr 内存单元地址
1 | push ip |
4.2.call dword ptr 内存单元地址
1 | push cs |
call 和 ret配合使用
实现子程序,用call指令执行子程序,再用ret指令转到call指令后的代码:
1 | 标号: |
mul指令
乘法指令
注意:
- 两个相乘的数要么都是8位,要么都是16位
1.如果是8位,一个默认放在al中,另一个放在8位寄存器或内存字节单元中。结果默认放在ax中
2.如果是16位,一个默认在ax中,另一个放在16位寄存器或内存字单元中。结果高位默认放在dx中,低位2放在ax中
格式:
1 | mul 寄存器/内存单元 |
如:
1.100*10
1 | mov al,100 |
结果:(ax)=1000
2.100*10000
1 | mov ax,100 |
结果:(ax)=4240h (dx)=000fh
(f4240h=1000000)
一个公式
将可能产生溢出的除法运算转变为多个不会产生溢出的除法运算(商小于65536)
1 | x/n=int (h/n)*65536+[ rem(h/n)*65536+l]/n |
x : 被除数(0,ffffffff)
n : 除数(0,ffff)
h : x高16位
l : x低16位
int() : 取商
rem() : 取余
标志寄存器
标志寄存器作用:
1.用来储存相关指令的执行结果
2.用来为CPU执行相关指令提供行为依据
3.用来控制CPU的相关工作方式
8086CPU有16位,其中储存的信息被称为程序状态字(psw)
flag是按位起作用的,每一位都有专门的含义,记录特定的信息
flag的1、3、5、12、13、14、15位在8086CPU中没有使用,其他位都有特殊含义
影响标志寄存器的大都是运算指令,没有影响的大都是传送指令
ZF标志
零标志位
记录相关指令执行后结果是否为0,如果为0那么ZF=1,如果不为0那么ZF=0
1 | mov ax,1 |
执行后zf=1
1 | mov ax,2 |
执行后zf=0
PF标志
奇偶标志位
记录相关指令执行后结果的所有bit位中 1 的个数是否为偶数,如果为偶数PF=1,如果不为偶数PF=0
1 | mov al,1(10) |
结果为00001011B,∴PF=0
1 | mov al,1 |
结果为00000011B,∴PF=1
SF标志
符号标志位
记录相关指令执行后结果是否为负,如果负sf=1,如果非负sf=0
计算机中通常用补码表示有符号数据,一个数据可以看作是 有符号数,也可以看成无符号数。不管如何看待,CPU在执行指令的时候就已经包含了两种含义,也将得到两种结果,关键在于程序需要哪种结果
sf标志是对于有符号数运算的一种记录,记录了数据的正负
将数据当作有符号数运算时,可以通过 sf 知道结果的正负
将数据当作无符号数运算时, sf 值无意义,虽然相关指令影响了它的值
1 | mov al,10000001B |
结果为10000010,sf=1,表示:如果指令进行的是有符号数的运算,那么结果为负。
1 | mov al,10000001B |
结果为0,sf=0,表示如果指令进行的是有符号数运算,那么结果为非负。
单纯地考查sf的值不能知道结果的正负,因为sf记录的只是可以在计算机中存放的相应位数的结果的正负(如果发生溢出)
CF标志
进位标志位
进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值
对于位数为N的无符号数来说,N-1位为它的最高有效位,假想存在的第N位就是相对于最高有效位的更高位
当两个数据相加的时候,可能产生从最高有效位向更高位的进位。CPU不丢弃这个高位进位值,而是保存在CF上
OF标志
溢出标志位
在进行有符号数运算时,如果结果超过了机器能表达的范围称为溢出
记录了有符号数运算的结果是否发生了溢出,如果发生溢出OF=1,如果没有OF=0
CF是对无符号数运算有意义的标志位,OF是对有符号数运算有意义的标志位。
对于无符号数运算,CPU用CF来记录是否产生进位;对于有符号数,CPU用OF来记录是否产生溢出,还要用SF来记录结果的符号
1 | mov al,98 |
执行后 CF=0,OF=1
1 | mov al,0f0H |
执行后CF=1,OF=0
adc指令
带进位加法指令,利用了CF位上记录的进位值
格式:adc 操作对象1,操作对象2
操作对象1=操作对象1+操作对象2+CF
比add指令多加了一个CF位的值
1 | add ax,bx |
adc指令执行后也可能产生进位值,所以也会对CF位进行设置
add指令和adc指令配合 可以对更大的数据进行加法运算
例:
计算1EF0001000H+2010001EF0H,结果放在ax,bx,cx中
1.将低16位相加,CF中记录相加的进位值
2.将次高16位和CF相加,CF中记录相加的进位值
3.高16位和CF相加,CF中记录相加的进位值
sbb指令
带借位减法指令,利用了CF位上的借位值
格式:sbb 操作对象1,操作对象2
功能:操作对象1=操作对象1-操作对象2-CF
可以对任意大的数据进行减法运算,思路同adc指令
cmp指令
比较指令,功能相当于减法指令,只是不保存结果,仅仅根据计算结果对标志寄存器进行设置
格式:cmp 操作对象1,操作对象2
cmp可以对无符号数进行比较,也可以对有符号数进行比较
通过cmp指令执行后,相关标志位的值可以看出比较的结果:
进行无符号数比较时:
1 | cmp ax,bx |
如果(ax)=(bx)则(ax)-(bx)=0,所以zf=1
如果(ax)!=(bx)则(ax)-(bx)!=0,所以zf=0
如果(ax)<(bx)则(ax)-(bx)将产生借位,所以cf=1
如果(ax)>=(bx)则(ax)-(bx)将不必借位,所以cf=0
如果(ax)>(bx)则(ax)-(bx)=0不必借位且结果不为0,所以cf=0,zf=0
如果(ax)<=(bx)则(ax)-(bx)=0可能借位,结果可能为0,所以cf=1或zf=1
进行有符号数比较时:
1 | cmp ah,bh |
如果(ah)=(bh)则(ah)-(bh)=0,所以zf=1
如果(ah)!=(bh)则(ah)-(bh)!=0,所以zf=0
如果sf=1,of=0,(ah)<(bh)
如果sf=1,of=1,(ah)>(bh)
如果sf=0,of=1,(ah)<(bh)
如果sf=0,of=0,(ah)>=(bh)
of=0,说明没有溢出,逻辑上真正结果的正负=实际结果的正负
如果因为溢出导致了实际结果为负(正),那么逻辑上真正的结果必然为正(负)
*zf:记录结果是否为0。如果为0那么ZF=1,如果不为0那么ZF=0
*cf:记录了无符号数运算结果的最高有效位向更高位的进位值
*of:记录了有符号数运算的结果是否发生了溢出,如果发生溢出OF=1,如果没有OF=0。 OF=0,说明逻辑上真正结果的正负=实际结果的正负
*sf: 记录相关指令执行后结果是否为负,如果负sf=1,如果非负sf=0
*pf:记录相关指令执行后结果的所有bit位中 1 的个数是否为偶数,如果为偶数PF=1,如果不为偶数PF=0
检测比较结果的条件转移指令
与call和ret类似,通常和cmp配合使用
检测被cmp影响的,表示比较结果的标志位
根据无符号数的比较结果进行转移的条件转移指令检测zf、cf:
1 | 指令 含义 检测标志位 |
根据有符号数的比较结果进行转移的条件转移指令检测sf、of、zf
DF标志和串传送指令
DF:方向标志位,在串传送指令中,控制每次操作后si、di的增减
df=0,每次操作后si、di递增
df=1,每次操作后si、di递减
-
movsb
功能:将ds:si指向的内存单元中的字节送入es:di中,然后根据标志寄存器df位的值将si和di递增或递减 -
movsw
功能:将ds:si指向的内存单元中的字送入es:di中,然后根据标志寄存器df位的值将si和di递增2或递减2
一般来说,movsb和movsw都和rep配合使用
格式: rep movsb
功能:
1 | s:movsb |
对df位进行设置的指令:
cld指令:将df位置0
std指令:将df位置1
使用串传送指令进行数据的传送,需要:
- 传送的原始位置:ds:si
- 传送的目的位置:es:di
- 传送的长度:cx
- 传送的方向:df (正向/反向传送,si、di递增/递减)
pushf和popf
pushf是将标志寄存器的值压栈
popf是从栈中弹出数据送入标志寄存器中
1 | mov ax,0 ;ax清零 |
tips:
正加正得负,或负加负得正,肯定溢出
一个正数和一个负数相加不可能溢出
内中断
任何一个cpu都可以在执行完当前正在执行的指令后,检测到从cpu外部发送来的或内部产生的一种特殊信息,并且可以立即对所接收到的信息进行处理,这种信息称为中断信息。
内中断的产生
cpu内部有4种情况可以产生需要及时处理的中断信息
处理中断信息首先要知道接收到的信息的来源,所以中断信息中必须包含识别来源的编码。8086cpu用中断类型码来标识中断信息的来源
中断类型码为一个字节型数据,即可以表示256种中断信息的来源(简称中断源)
- 除法错误,如执行div指令产生的除法溢出 中断类型码:0
- 单步执行 中断类型码:1
- 执行into指令 中断类型码:4
- 执行int 指令 指令格式为int n,n为字节型立即数,中断类型码:n
中断向量表
中断处理程序入口地址的列表
cpu用8位的终端类型码,通过中断向量表,找到相应的中断处理程序的入口地址
中断向量表在内存中保存,其中存放着256个中断源所对应的中断处理程序的入口
cpu知道了中断类型码就可以将中断类型码作为中断向量表的表项号,定位相应的表项,从而得到程序的入口地址
如果使用8086cpu,中断向量表就必须存放在0000:0000~0000:03FF中,一个表项占两个字,高地址存放段地址,低地址字存放偏移地址
*一个字节:8位
*一个字==两个字节