##转移指令的原理 可以修改IP,或同时修改CS和IP的指令统称为转移指令。概括地讲,转移指令就是可以控制CPU执行内存中某处代码的指令。
8086CPU的转移行为有以下几类。
- 只修改IP时,称为段内转移,比如:jmp ax。
- 同时修改CS和IP时,称为段间转移,比如:jmp 1000:0。
由于转移指令对IP的修改范围不同,段内转移又分为:短转移和近转移。
- 短转移IP的修改范围为-128~127。
- 近转移IP的修改范围为-32768~32767。
8086CPU的转移指令分为以下几类。
- 无条件转移指令(如:jmp)
- 条件转移指令
- 循环指令(如:loop)
- 过程
- 中断
###操作符offset 操作符offset在汇编语言中是由编译器处理的符号,它的功能是取得标号的偏移地址。
;将s处的一条指令复制到s0处
assume cs:codesg
codesg segment
s: mov ax, bx ;(mov ax,bx 的机器码占两个字节)
mov si, offset s ;获得标号s的偏移地址
mov di, offset s0 ;获得标号s0的偏移地址
mov ax, cs:[si]
mov cs:[di], ax
s0: nop ;(nop的机器码占一个字节)
nop
codesg ends
ends
jmp指令
mp为无条件转移,转到标号处执行指令可以只修改IP,也可以同时修改CS和IP;
jmp指令要给出两种信息:
- 转移的目的地址
- 转移的距离(段间转移、段内短转移,段内近转移)
jmp short 标号
jmp near ptr 标号
jcxz 标号 loop 标号
等几种汇编指令,它们对 IP的修改是根据转移目的地址和转移起始地址之间的位移来进行的。在它们对应的机器码中不包含转移的目的地址,而包含的是到目的地址的位移距离。
####依据位移进行转移的jmp指令
jmp short 标号(段内短转移)
指令“jmp short 标号”的功能为(IP)=(IP)+8位位移,转到标号处执行指令
-
(1)8位位移=“标号”处的地址 - jmp指令后的第一个字节的地址;
-
(2)short指明此处的位移为8位位移;
-
(3)8位位移的范围为-128~127,用补码表示
-
(4)8位位移由编译程序在编译时算出。
assume cs:codesg
codesg segment
start:mov ax,0
jmp short s ;s不是被翻译成目的地址
add ax, 1
s:inc ax ;程序执行后, ax中的值为 1
codesg ends
end start
执行后ax的值是1,因为jmp short s跳过add ax,1 直接指向了s,执行了inc ax,所以ax的值是1!
书中详细分析了jump 的指令,具体可以看书,意思就是jump转移的目的地址不一样,但是机器码确实一样的,比如机器码是EB 03, 这个03就是指令位移长度,上下文位移长度都是03!目前的结论如下: CPU不需要这个目的地址就可以实现对IP的修改。这里是依据位移进行转移!
jmp short s指令的读取和执行过程:
- (CS)=0BBDH,(IP)=0006,CS:IP指向EB 03(jmp short s的机器码);
- 读取指令码EB 03进入指令缓冲器;
- (IP)=(IP)+所读取指令的长度=(IP)+2=0008,CS:IP指向add ax,1;
- CPU指行指令缓冲器中的指令EB 03;
- 指令EB 03执行后,(IP)=000BH,CS:IP指向inc ax
位移的具体计算如图所示:
jmp near ptr 标号 (段内近转移)
指令“jmp near ptr 标号”的功能为:(IP)=(IP)+16位位移。
- 16位位移=标号处的地址-jump指令后的第一个字节的地址;
- near ptr 指名此处的位移为16位位移,进行的是段内近转移;
- 16位位移的范围为-32768~32737,用补码表示;
- 16位位移由编译器在编译时算出;
####转移的目的地址在指令中的jmp指令
jmp far ptr 标号
(段间转移或远转移)
指令 jmp far ptr 标号
功能如下:
- (CS) = 标号所在段的段地址;
- (IP) = 标号所在段中的偏移地址。
- far ptr指明了指令用标号的段地址和偏移地址修改CS和IP。
assume cs:codesg codesg segment start: mov ax, 0 mov bx, 0 jmp far ptr s ;s被翻译成转移的目的地址0B01 BD0B db 256 dup (0) ;转移的段地址:0BBDH,偏移地址:010BH s: add ax,1 inc ax codesg ends end start
调试界面显示如下。可以看出这个包含了转移的目的地址。
####转移地址在寄存器或内存中的jmp指令
jmp 16位寄存器 功能:IP =(16位寄存器)
转移地址在内存中的jmp指令有两种格式:
- jmp word ptr 内存单元地址(段内转移) 功能:从内存单元地址处开始存放着一个字,是转移的目的偏移地址。
mov ax, 0123H
mov ds:[0], ax
jmp word ptr ds:[0]
;执行后,(IP)=0123H
- jmp dword ptr 内存单元地址(段间转移)
功能:从内存单元地址处开始存放着两个字,高地址处的字是转移的目的段地址,低地址处是转移的目的偏移地址。
- (CS)=(内存单元地址+2)
- (IP)=(内存单元地址)
mov ax, 0123H mov ds:[0], ax;偏移地址 mov word ptr ds:[2], 0;段地址 jmp dword ptr ds:[0] ;执行后, ;(CS)=0 ;(IP)=0123H ;CS:IP 指向 0000:0123。
####jcxz指令和loop指令
jcxz指令为有条件转移指令,所有的有条件转移指令都是短转移。 在对应的机器码中包含转移的位移,而不是目的地址。对IP的修改范围都为-128~127。 指令格式:jcxz 标号(如果(cx)=0,则转移到标号处执行。) 当(cx) = 0时,(IP) = (IP) + 8位位移
- 8位位移 = “标号”处的地址 - jcxz指令后的第一个字节的地址;
- 8位位移的范围为-128~127,用补码表示;
- 8位位移由编译程序在编译时算出。
当(cx)!=0时,什么也不做(程序向下执行)。
loop指令 loop指令为循环指令,所有的循环指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址。
对IP的修改范围都为-128~127。
指令格式:loop 标号 ((cx)) = (cx) - 1,如果(cx) ≠ 0,转移到标号处执行。 (cx) = (cx) - 1;如果 (cx) != 0,(IP)=(IP) + 8位位移。
- 8位位移=标号处的地址-loop指令后的第一个字节的地址;
- 8位位移的范围为-128~127,用补码表示;
- 8位位移由编译程序在编译时算出。
如果(cx)= 0,什么也不做(程序向下执行)。
##call和ret指令 call和ret指令都是转移指令,它们都修改IP,或同时修改CS和IP。常用来设计子程序。 ####ret和retf 这个要结合前面的栈操作知识点看,就知道为什么有两步了!
- ret指令用栈中的数据,修改IP的内容,从而实现近转移;
- retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移。
CPU执行ret指令时,相当于进行: pop IP:
-
(1)(IP) = ( (ss) * 16 + (sp) )
-
(2)(sp) = (sp) + 2
CPU执行retf指令时,相当于进行:pop IP, pop CS:
-
(1)(IP) = ( (ss) * 16 + (sp) )
-
(2)(sp) = (sp) + 2
-
(3)(CS) = ( (ss) * 16 + (sp) )
-
(4)(sp) = (sp) + 2
assume cs:code
stack seqment
db 16 dup (0)
stack ends
code segment
mov ax, 4c00h
int 21h
start: mov ax, stack
mov ss, ax
mov sp, 16 ;栈顶指针指向栈底
mov ax, 0
push ax ;ax入栈
mov bx, 0
ret ;ret指令执行后,(IP)=0,CS:IP指向代码段的第一条指令。可以push cs push ax retf
code ends
end start
####call 指令 all指令经常跟ret指令配合使用,因此CPU执行call指令,进行两步操作:
(1)将当前的 IP 或 CS和IP 压入栈中;
(2)转移(jmp)。
call指令不能实现短转移,除此之外,call指令实现转移的方法和 jmp 指令的原理相同。
call 标号(近转移)
CPU执行此种格式的call指令时,相当于进行:
- push IP
- jmp near ptr 标号
call far ptr标号(段间转移)
CPU执行此种格式的call指令时,相当于进行:
- push CS
- push IP
- jmp far ptr 标号
call 16位寄存器
CPU执行此种格式的call指令时,相当于进行: -push IP -jmp 16位寄存器
call word ptr 内存单元地址
CPU执行此种格式的call指令时,相当于进行:
- push IP
- jmp word ptr 内存单元地址
-
mov sp, 10h mov ax, 0123h mov ds:[0], ax call word ptr ds:[0] ;执行后,(IP)=0123H,(sp)=0EH
call dword ptr 内存单元地址
CPU执行此种格式的call指令时,相当于进行:push CS push IP jmp dword ptr 内存单元地址
mov sp, 10h
mov ax, 0123h
mov ds:[0], ax
mov word ptr ds:[2], 0
call dword ptr ds:[0]
;执行后,(CS)=0,(IP)=0123H,(sp)=0CH
####call 和 ret 的配合使用 分析下面程序:
assume cs:code
code segment
start: mov ax,1
mov cx,3
call s ;(1)CPU指令缓冲器存放call指令,IP指向下一条指令(mov bx, ax),执行call指令,IP值(指令mov bx,ax的偏移地址)入栈,jmp(将IP的值改变为标号s处的偏移地址)
mov bx,ax ;(4)IP重新指向这里 bx = 8
mov ax,4c00h
int 21h
s: add ax,ax
loop s;(2)循环3次ax = 8
ret;(3)return : pop IP 就是之前压入的Ip地址 mov bx,ax的值,送入IP 中,这样吓一跳指令就是mov bx,ax 这就是为什么call ret 通常用来设计子程序的原因了
code ends
end start
call 与 ret 指令共同支持了汇编语言编程中的模块化设计
编写子程序,书中也写了模块设计东东,批量数据东东,编写子程序东东,这个不在我们考虑范围内了,能看得懂就可以了,现在哪家公司很少让你改的,也不敢让你改的,很成熟的东东了!
##标志寄存器
CPU内部的寄存器中,有一种特殊的寄存器(对于不同的处理机,个数和结构都可能不同)具有以下3种作用。
-
(1)用来存储相关指令的某些执行结果;
-
(2)用来为CPU执行相关指令提供行为依据;
-
(3)用来控制CPU的相关工作方式。
这种特殊的寄存器在8086CPU中,被称为标志寄存器(flag)。其实很多单片机都有这种flag的啦!
8086CPU的标志寄存器有16位,其中存储的信息通常被称为程序状态字(PSW)
flag寄存器是按位起作用的,它的每一位都有专门的含义,记录特定的信息。
在8086CPU的指令集中,有的指令的执行是影响标志寄存器的,比如,add、sub、mul、div、inc、or、and等,它们大都是运算指令(进行逻辑或算术运算);有的指令的执行对标志寄存器没有影响,比如,mov、push、pop等,它们大都是传送指令。
####ZF
零标志位。它记录相关指令执行后,其结果是否为0。
如果结果为0,那么zf = 1(表示结果是0);如果结果不为0,那么zf=0。
mov ax, 1
sub ax, 1 ;执行后,结果为0,则zf=1
mov ax, 2
sub ax, 1 ;执行后,结果不为0,则zf=0
####PF 奇偶标志位。它记录相关指令执行后,其结果的所有bit位中1的个数是否为偶数。
如果1的个数为偶数,pf=1,如果为奇数,那么pf=0。
mov al, 1
add al, 10 ;执行后,结果为00001011B,其中有3(奇数)个1,则pf=0;
mov al, 1
or al, 2 ;执行后,结果为00000011B,其中有2(偶数)个1,则pf=1;
####SF 符号标志位。它记录相关指令执行后,其结果是否为负。
如果结果为负,sf=1;如果非负,sf=0。
计算机中通常用补码来表示有符号数据。计算机中的一个数据可以看作是有符号数,也可以看成是无符号数。
- 00000001B,可以看作为无符号数1,或有符号数+1;
- 10000001B,可以看作为无符号数129,也可以看作有符号数-127。
对于同一个二进制数据,计算机可以将它当作无符号数据来运算,也可以当作有符号数据来运算
CPU在执行add等指令的时候,就包含了两种含义:可以将add指令进行的运算当作无符号数的运算,也可以将add指令进行的运算当作有符号数的运算
SF标志,就是CPU对有符号数运算结果的一种记录,它记录数据的正负。在我们将数据当作有符号数来运算的时候,可以通过它来得知结果的正负。如果我们将数据当作无符号数来运算,SF的值则没有意义,虽然相关的指令影响了它的值。
mov al, 10000001B
add al, 1 ;执行后,结果为10000010B,sf=1,表示:如果指令进行的是有符号数运算,那么结果为负;
mov al, 10000001B
add al, 01111111B ;执行后,结果为0,sf=0,表示:如果指令进行的是有符号数运算,那么结果为非负
####CF
进位标志位。一般情况下,在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值。
97H - 98H 产生借位CF = 1 ==》 (al) = 197H - 98H = FFH。
####OF
溢出标志位。一般情况下,OF记录了有符号数运算的结果是否发生了溢出。
如果发生溢出,OF=1;如果没有,OF=0。
CF和OF的区别:CF是对无符号数运算有意义的标志位,而OF是对有符号数运算有意义的标志位
CPU在执行add等指令的时候,就包含了两种含义:无符号数运算和有符号数运算。对于无符号数运算,CPU用CF位来记录是否产生了进位;对于有符号数运算,CPU用OF位来记录是否产生了溢出,当然,还要用SF位来记录结果的符号。
mov al, 98
add al, 99 ;执行后将产生溢出。因为进行的"有符号数"运算是:(al)=(al)+ 99 = 98 + 99=197 = C5H 为-59的补码
;而结果197超出了机器所能表示的8位有符号数的范围:-128-127。
;add 指令执行后:无符号运算没有进位CF=0,有符号运算溢出OF=1
;当取出的数据C5H按无符号解析C5H = 197, 当按有符号解析通过SP得知数据为负,即C5H为-59补码存储,
mov al,0F0H ;FOH,为有符号数-16的补码 -Not(F0 - 1)
add al,088H ;88H,为有符号数-120的补码 -Not(88- 1)
;执行后,将产生溢出。因为add al,088H进行的有符号数运算结果是:(al)= -136
;而结果-136超出了机器所能表示的8位有符号数的范围:-128-127。
;add 指令执行后:无符号运算有进位CF=1,有符号运算溢出OF=1
####adc指令和sbb指令 adc是带进位加法指令,它利用了CF位上记录的进位值。
指令格式:adc 操作对象1, 操作对象2
功能:操作对象1 = 操作对象1 + 操作对象2 + CF。
mov ax, 2
mov bx, 1
sub bx, ax ;无符号运算借位CF=1,有符号运算OF = 0
adc ax, 1 ;执行后,(ax)= 4。adc执行时,相当于计算:(ax)+1+CF=2+1+1=4。
;计算1EF000H+201000H,结果放在ax(高16位)和bx(低16位)中。
;将计算分两步进行,先将低16位相加,然后将高16位和进位值相加。
mov ax, 001EH
mov bx, 0F000H
add bx, 1000H
adc ax, 0020H
####sbb指令 sbb是带借位减法指令,它利用了CF位上记录的借位值。
指令格式:sbb 操作对象1, 操作对象2
功能:操作对象1 = 操作对象1 - 操作对象2 - CF
;计算003E1000H-00202000H,结果放在ax,bx中,程序如下:
mov bx, 1000H
mov ax, 003EH
sub bx, 2000H
sbb ax, 0020H
####cmp指令 cmp是比较指令,cmp的功能相当于减法指令,只是不保存结果。cmp指令执行后,将对标志寄存器产生影响。
其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
cmp指令格式:cmp 操作对象1,操作对象2
例如:
指令cmp ax, ax
,做(ax)-(ax)的运算,结果为0,但并不在ax中保存,仅影响flag的相关各位。
指令执行后:zf=1,pf=1,sf=0,cf=0,of=0。
CPU在执行cmp指令的时候,也包含两种含义:进行无符号数运算和进行有符号数运算。
cmp ax, bx | 无符号比较时 |
---|---|
(ax) = (bx) | zf = 1 |
(ax) ≠ (bx) | zf = 0 |
(ax) < (bx) | cf = 1 |
(ax) ≥ (bx) | cf = 0 |
(ax) > (bx) | cf = 0 且 zf = 0 |
(ax) ≤ (bx) | cf = 1 且 zf = 1 |
上面的表格可以正推也可以逆推!
如果用cmp来进行有符号数比较时 SF只能记录实际结果的正负,发生溢出的时候,实际结果的正负不能说明逻辑上真正结果的正负。 但是逻辑上的结果的正负,才是cmp指令所求的真正结果,所以我们在考察SF的同时考察OF,就可以得知逻辑上真正结果的正负,同时就知道比较的结果。
mov ah, 08AH ; -Not(8A-1) = -118 即当成有符号数时为-118
mov bh, 070H ;有符号数时最高位为0为正数, 70H = 112
cmp ah, bh ;(ah)-(bh)实际得到的结果是1AH
;在逻辑上,运算所应该得到的结果是:(-118)- 112 = -230
;sf记录实际结果的正负,所以sf=0
cmp ah, bh
-
(1)如果sf=1,而of=0 。 of=0说明没有溢出,逻辑上真正结果的正负=实际结果的正负; sf=1,实际结果为负,所以逻辑上真正的结果为负,所以(ah)<(bh)
-
(2)如果sf=1,而of=1: of=1,说明有溢出,逻辑上真正结果的正负≠实际结果的正负; sf=1,实际结果为负。 实际结果为负,而又有溢出,这说明是由于溢出导致了实际结果为负,,如果因为溢出导致了实际结果为负,那么逻辑上真正的结果必然为正。 这样,sf=1,of=1,说明了(ah)>(bh)。
- (3)如果sf=0,而of=1。of=1,说明有溢出,逻辑上真正结果的正负≠实际结果的正负;sf=0,实际结果非负。而of=1说明有溢出,则结果非0,所以,实际结果为正。 实际结果为正,而又有溢出,这说明是由于溢出导致了实际结果非负,如果因为溢出导致了实际结果为正,那么逻辑上真正的结果必然为负。这样,sf=0,of=1,说明了(ah)<(bh)。
- (4)如果sf=0,而of=0 of=0,说明没有溢出,逻辑上真正结果的正负=实际结果的正负;sf=0,实际结果非负,所以逻辑上真正的结果非负,所以(ah)≥(bh)。
####检测比较结果的条件转移指令
可以根据某种条件,决定是否修改IP的指令
jcxz它可以检测cx中的数值,如果(cx)=0,就修改IP,否则什么也不做。
所有条件转移指令的转移位移都是[-128,127]。
多数条件转移指令都检测标志寄存器的相关标志位,根据检测的结果来决定是否修改IP
这些条件转移指令通常都和cmp相配合使用,它们所检测的标志位,都是cmp指令进行无符号数比较的时记录比较结果的标志位
根据无符号数的比较结果进行转移的条件转移指令(它们检测zf、cf的值)
指令 | 含义 | 检测的相关标志位 |
---|---|---|
je | 等于则转移 | zf = 1 |
jne | 不等于则转移 | zf = 0 |
jb | 低于则转移 | cf = 1 |
jnb | 不低于则转移 | cf = 0 |
ja | 高于则转移 | cf = 0 且 zf = 0 |
jna | 不高于则转移 | cf = 1 且 zf = 1 |
j:jump,e:equal,b:below,a:above,n:not 这样就好记了!
;编程,统计data段中数值为8的字节的个数,用ax保存统计结果。
mov ax, data
mov ds, ax
mov bx, 0 ;ds:bx指向第一个字节
mov ax, 0 ;初始化累加器mov cx,8
s:
cmp byte ptr[bx], 8 ;和8进行比较
jne next ;如果不相等转到next,继续循环
inc ax ;如果相等就将计数值加1
next:
inc bx
loop s ;程序执行后:(ax)=3
####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。
格式:rep movsb
movsb和movsw进行的是串传送操作中的一个步骤,一般来说,movsb和movsw都和rep配合使用,
功能:rep的作用是根据cx的值,重复执行后面的串传送指令。
8086CPU提供下面两条指令对df位进行设置。
- cld指令:将标志寄存器的df位置0
- std指令:将标志寄存器的df位置1
;将data段中的第一个字符串复制到它后面的空间中。
data segment
db 'Welcome to masm!'
db 16 dup (0)
data ends
mov ax, data
mov ds, ax
mov si, 0 ;ds:si 指向data:0
mov es, ax
mov di, 16 ;es:di指向data:0010
mov cx, 16 ;(cx)=16,rep循环16次
c1d ;设置df=0,正向传送
rep movsb
pushf和popf
pushf的功能是将标志寄存器的值压栈,而popf是从栈中弹出数据,送入标志寄存器中!
pushf和popf,为直接访问标志寄存器提供了一种方法。
##内中断
####内中断的产生
任何一个通用的CPU,都具备一种能力,可以在执行完当前正在执行的指令之后,检测到从CPU外部发送过来的或内部产生的一种特殊信息,并且可以立即对所接收到的信息进行处理。这种特殊的信息,我们可以称其为:中断信息。中断的意思是指,CPU不再接着(刚执行完的指令)向下执行,而是转去处理这个特殊信息。 中断信息可以来自CPU的内部和外部(内中断,外中断)。 内中断:当CPU的内部有需要处理的事情发生的时候,将产生中断信息,引发中断过程。这种中断信息来自CPU的内部。
8086CPU的内中断(下面四种情况将产生中断信息):
- 除法错误,比如,执行div指令产生的除法溢出;
- 单步执行;
- 执行into指令;
- 执行int指令。
中断信息中包含中断类型码,中断类型码为一个字节型数据,可以表示256种中断信息的来源(中断源)。
上述的4种中断源,在8086CPU中的中断类型码如下。
- 除法错误:0
- 单步执行:1
- 执行into指令:4
- 执行int指令,该指令的格式为int n,指令中的n为字节型立即数,是提供给CPU的中断类型码。
####中断处理程序、中断向量表、中断过程
中断处理程序
用来处理中断信息的程序被称为中断处理程序。
根据CPU的设计,中断类型码的作用就是用来定位中断处理程序。比如CPU根据中断类型码4,就可以找到4号中断的处理程序。
中断向量表
中断向量,就是中断处理程序的入口地址。中断向量表,就是中断处理程序入口地址的列表
CPU用8位的中断类型码通过中断向量表找到相应的中断处理程序的入口地址。
中断过程
中断过程的主要任务就是用中断类型码在中断向量表中找到中断处理程序的入口地址,设置CS和IP!
简要描述如下:
- 取得中断类型码N;
- pushf
- TF=0,IF=0 (为什么这样参考单步中断)
- push CS , push IP
- ( IP )=(N * 4),(CS)=(N * 4 + 2) 硬件在完成中断过程后,CS:IP将指向中断处理程序的入口(这个是硬件自己自动执行的),CPU开始执行中断处理程序。
####iret指令 CPU随时都可能执行中断处理程序,中断处理程序必须一直存储在内存某段空间之中 而中断处理程序的入口地址,即中断向量,必须存储在对应的中断向量表表项中。
中断处理程序的常规编写步骤:
- 保存用到的寄存器(存入栈);
- 处理中断;
- 恢复用到的寄存器;
- 用iret指令返回。
iret 指令描述为:pop IP
pop CS
popf
iret指令执行后,CPU回到执行中断处理程序前的执行点继续执行程序。
####除法错误中断的处理
mov ax, 1000h
mov bh, 1
div bh ;除法溢出错误
-
1、当CPU执行div bh时,发生了除法溢出错误,产生0号中断信息,从而引发中断过程,
-
2、CPU执行0号中断处理程序
-
3、系统中的0号中断处理程序的功能:显示提示信息“Divide overflow”后,返回到操作系统中。
编程实验
编程:编写0号中断处理程序do0,当发生除法溢出时,在屏幕中间显示“overflow!”,返回DOS。
1、0000:0200至0000:02FF的256个字节的空间所对应的中断向量表项都是空的,可以将中断处理程序do0传送到内存0000:0200处。
2、中断处理程序do0放到0000:0200,再将其地址登记在中断向量表对应表项。
- 0号表项的地址0:0。0:0字单元存放偏移地址,0:2字单元存放段地址
- 将do0的段地址0存放在0000:0002字单元中,将偏移地址200H存放在0000:0000字单元
ssume cs:code
code segment
start:
mov ax, cs
mov ds, ax
mov si, offset do0 ;设置ds:si指向源地址
mov ax, 0
mov es, ax
mov di, 200h ;设置es:di指向目的地址0000:0200
mov cx, offset do0end - offset do0 ;设置cx为传输长度 编译时给出do0部分代码长度
cld ;设置传输方向为正
rep movsb ;将do0的代码送入0:200处
mov ax, 0 ;设置中断向量表
mov es, ax
mov word ptr es:[0*4], 200h
mov word ptr es:[0*4+2], 0
mov ax,4c00h
int 21h
;do0程序的主要任务是显示字符串
do0: jmp short do0 start
db "overflow!"
do0start:
mov ax, cs
mov ds, ax
mov si, 202h ;设置ds:si指向字符串
mov ax, 0b800h
mov es, ax
mov di, 12*160+36*2 ;设置es:di指向显存空间的中间位置
mov cx, 9 ;设置cx为字符串长度
s: mov al, [si]
mov es:[di], al
inc si
add di, 1
mov al, 02h ;设置颜色
mov es:[di], al
add di, 1
loop s
mov ax, 4c00h
int 21h
do0end: nop
code ends
end start
####单步中断
CPU在执行完一条指令之后,如果检测到标志寄存器的TF位为1,则产生单步中断,引发中断过程。单步中断的中断类型码为1
Debug是如何利用CPU所提供的单步中断的功能进行调试?如使用t命令查看寄存器状态
Debug提供了单步中断的中断处理程序,功能为显示所有寄存器中的内容后等待输入命令
在使用t命令执行指令时,Debug将TF设置为1,在CPU执行完这条指令后就引发单步中断,执行单步中断的中断处理程序,所有寄存器中的内容被显示在屏幕上,并且等待输入命令。
在进入中断处理程序之前,设置TF=0。从而避免CPU在执行中断处理程序的时候发生单步中断。
####int 指令 int指令的格式为:int n ,n为中断类型码,它的功能是引发中断过程。
CPU执行int n指令,相当于引发一个n号中断的中断过程。
在程序中使用int指令调用任何一个中断的中断处理程序(中断例程)。
编写供应用程序调用的中断例程。
实验1:
;求2 * 3456^2
assume cs:code
code segment
start:
mov ax, 3456 ;(ax)=3456
int 7ch ; 调用中断7ch的中断例程,计算ax中的数据的平方
add ax, ax
adc dx, dx ;存放结果,将结果乘以2
mov ax,4c00h
int 21h
code ends
end start
;编程:安装中断7ch的中断例程
;功能:求一word型数据的平方。
;参数:(ax) = 要计算的数据。
;返回值:dx、ax中存放结果的高16位和低16位。
assume cs:code
code segment
start:
mov ax,cs
mov ds,ax
mov si,offset sqr ;设置ds:si指向源地址
mov ax,0
mov es,ax
mov di,200h ;设置es:di指向目的地址
mov cx,offset sqrend - offset sqr ;设置cx为传输长度
cld ;设置传输方向为正
rep movsb
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
sqr:
mul ax
iret ;CPU执行int 7ch指令进入中断例程之前,标志寄存器、当前的CS和IP被压入栈
;在执行完中断例程后,应该用iret 指令恢复int 7ch执行前的标志寄存器和CS、IP的
sqrend: nop
code ends
end start
实验2
;功能:将一个全是字母,以0结尾的字符串,转化为大写。
;参数:ds:si指向字符串的首地址。
;应用举例:将data段中的字符串转化为大写。
assume cs:code
data segment
db 'conversation',0
data ends
code segment
start: mov ax, data
mov ds, ax
mov si, 0
int 7ch
mov ax,4c00h
int 21h
code ends
end start
assume cs:code
code segment
start:
mov ax,cs
mov ds,ax
mov si,offset capital
mov ax,0
mov es,ax
mov di,200h
mov cx,offset capitalend - offset capital
cld
rep movsb
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
capital:
push cx
push si
change:
mov cl,[si]
mov ch,0
jcxz ok
and byte ptr [si],11011111b
inc si
jmp short change
ok:
pop si
pop cx
iret
capitalend:nop
code ends
end start
####BIOS和DOS所提供的中断例程
在系统板的ROM中存放着一套程序,称为BIOS(基本输入输出系统)。
BIOS中主要包含以下几部分内容
- 硬件系统的检测和初始化程序;
- 外部中断和内部中断的中断例程;
- 用于对硬件设备进行I/O操作的中断例程;
- 其他和硬件系统相关的中断例程。
程序员在编程的时候,可以用int 指令直接调用BIOS和DOS系统提供的中断例程,来完成某些工作。 和硬件设备相关的DOS中断例程中,一般都调用了BIOS的中断例程。
BIOS和DOS中断例程的安装过程
BIOS和DOS提供的中断例程是如何安装到内存中的呢?
1、开机后,CPU一加电,初始化(CS)= 0FFFFH,(IP)= 0,自动从FFFF:0单元开始执行程序。FFFF:0处有一条转跳指令,CPU执行该指令后,转去执行BIOS中的硬件系统检测和初始化程序。
2、初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中。 注意,对于BIOS所提供的中断例程,只需将入口地址登记在中断向量表中即可,因为它们是固化到ROM中的程序,一直在内存中存在。
3、硬件系统检测和初始化完成后,调用int 19h进行操作系统的引导。从此将计算机交由操作系统控制。
4、DOS启动后,除完成其他工作外,还将它所提供的中断例程装入内存,并建立相应的中断向量。
BIOS中断例程应用
一般来说,一个供程序员调用的中断例程中往往包括多个子程序,中断例程内部用传递进来的参数来决定执行哪一个子程序。
BIOS和DOS提供的中断例程,都用ah来传递内部子程序的编号。
int10h中断例程的设置光标位置功能。
mov ah,2 ;置光标
mov bh,0 ;第0页
mov dh,5 ;dh 中放行号
mov dl,12;dl 中放列号
bh中页号的含义: 内存地址空间中,B8000H~BFFFFH 共32kb的空间,为80*25彩色字符模式的显示缓冲区。一屏的内容在显示缓冲区中共占4000个字节。 显示缓冲区分为8页,每页4kb,显示器可以显示任意一页的内容。一般情况下,显示第0页的内容。也就是说,通常情况下,B8000H~B8F9FH中的4000个字节的内容将出现在显示器上。
int 10h 中断例程的在光标位置显示字符功能。
mov ah,9 ;在光标位置显示字符
mov al,'a' ;字符
mov bl,7 ; 颜色属性
mov bh,0 ;第0页
mov cx,3 ;字符重复个数
int 10h
(ah)=9 表示调用第10h号中断例程的9号子程序,功能为在光标位置显示字符,可以提供要显示的字符、颜色属性、页号、字符重复个数作为参数。
编程:在屏幕的5行12列显示3个红底高亮闪烁绿色的’a’。
assume cs:code
code segment
;int 10h中断例程的"设置光标位置"功能
mov ah, 2;设置光标调用第10h号中断例程的2号子程序,功能为设置光标位置(可以提供光标所在的行号、列号和页号作为参数)
;设置光标到第0页,第5行,第12列
mov bh, 0;第0页
mov dh, 5;dh中放行号
mov dl, 12;dl中放列号
int 10h
;int10h中断例程的"在光标位置显示字符"功能。
mov ah,9 ;调用第10h号中断例程的9号子程序,功能为在光标位置显示字符
;提供要显示的字符、颜色属性、页号、字符重复个数作为参数
mov al,'a' ;字符
mov b1,11001010b ;颜色属性
mov bh,0 ;第0页
mov cx,3 ;字符重复个数
int 10h
code ends
end
闪烁必须在全屏dos方式下才能看到!
####DOS中断例程应用
int 21h中断例程是DOS提供的中断例程,4ch号功能,即程序返回功能。
mov ah, 4ch ;调用第21h号中断例程的4ch号子程序,功能为程序返回,可以提供返回值作为参数
mov al, 0 ;返回值
int 21h
编程:在屏幕的5行12列显示字符串“Welcome to masm!”。
assume cs:code
data segment
db 'Welcome to masm', '$' ;“$”本身并不显示,只起到边界的作用
data ends
code segment
start: mov ah, 2 ;10号中断设置光标位置功能
mov bh, 0 ;第0页
mov dh, 5;dh中放行号
mov dl, 12 ;dl中放列号
int 10h
mov ax, data
mov ds, ax
mov dx, 0 ;ds:dx指向字符串的首地址data:0 (参数)
mov ah, 9 ;调用第21h号中断例程的9号子程序,功能为在光标位置显示字符串,可以提供要显示字符串的地址作为参数
int 21h
mov ax, 4c00h ;21号中断程序返回功能
int 21h
code ends
end start
端口
在PC机系统中,和CPU通过总线相连的芯片除各种存储器外,还有以下3种芯片。
- 各种接口卡(比如,网卡、显卡)上的接口芯片,它们控制接口卡进行工作;
- 主板上的接口芯片,CPU通过它们对部分外设进行访问;
- 其他芯片,用来存储相关的系统信息,或进行相关的输入输出处理。
在这些芯片中,都有一组可以由CPU读写的寄存器。这些寄存器,它们在物理上可能处于不同的芯片中,但是它们在以下两点上相同。
- 都和CPU的总线相连,这种连接是通过它们所在的芯片进行的;
- CPU对它们进行读或写的时候都通过控制线向它们所在的芯片发出端口读写命令。
从CPU的角度,将这些寄存器都当作端口,对它们进行统一编址,从而建立了一个统一的端口地址空间。 每一个端口在地址空间中都有一个地址。在访问端口的时候,CPU通过端口地址来定位端口。因为端口所在的芯片和CPU通过总线相连,
CPU可以直接读写以下3个地方的数据。
- CPU内部的寄存器;
- 内存单元;
- 端口。
####端口的读写 端口地址和内存地址一样,通过地址总线来传送。在PC系统中,CPU最多可以定位64KB个不同的端口。则端口地址的范围为0-65535。
端口的读写指令只有两条:in和out,分别用于从端口读取数据和往端口写入数据。
在in和out指令中,只能使用ax或al来存放从端口中读入的数据或要发送到端口中的数据。
;对0~255以内的端口进行读写时:
in al, 20h ;从20h端口读入一个字节
out 20h, al ;往20h端口写入一个字节
;对256~65535的端口进行读写时,端口号放在dx中:
mov dx, 3f8h ;将端口号3f8h送入dx
in al, dx ;从3f8h端口读入一个字节
out dx, al ;向3f8h端口写入一个字节
####CMOS RAM芯片 PC机中,有一个CMOSRAM芯片,一般简称为CMOS。此芯片的特征如下。
- 包含一个实时钟和一个有128个存储单元的RAM存储器
- 该芯片靠电池供电。关机后内部的实时钟正常工作,RAM中的信息不丢失
- 128个字节的RAM中,内部实时钟占用0~0dh单元来保存时间信息,其余大部分单元用于保存系统配置信息,供系统启动时BIOS程序读取。BIOS也提供了相关的程序,使我们可以在开机的时候配置CMOSRAM中的系统信息。
- 该芯片内部有两个端口,端口地址为70h和71h。CPU通过这两个端口来读写CMOS RAM
- 70h为地址端口,存放要访问的CMOSRAM单元的地址;71h为数据端口,存放从选定的CMOSRAM单元中读取的数据,或要写入到其中的数据。
可见,CPU对CMOS RAM的读写分两步进行,比如,读CMOS RAM的2号单元:
- 1.将2送入端口70h;
- 2.从端口71h读出2号单元的内容。
CMOSRAM中存储的时间信息
在CMOS RAM中,存放着当前的时间:年、月、日、时、分、秒。长度都为1个字节,存放单元为: 秒:0 分:2 时:4 日:7 月:8 年:9
CD码是以4位二进制数表示十进制数码的编码方法 4 == 0100B
一个字节可表示两个BCD码。则CMOS RAM存储时间信息的单元中,存储了用两个BCD码表示的两位十进制数,高4位的BCD码表示十位,低4位的BCD码表示个位。比如,00010100b表示14。
;编程,在屏幕中间显示当前的月份。
assume cs:code
code segment
start: mov al,8 ;从CMOS RAM的8号单元读出当前月份的BCD码。
out 70h,al
in al, 71h ;从数据端口71h中取得指定单元中的数据:
mov ah, al ;al中为从CMOSRAM的8号单元中读出的数据
mov cl, 4
shr ah, cl ;ah中为月份的十位数码值,左移四位空出四位
and al, 00001111b ;al中为月份的个位数码值
add ah, 30h ;BCD码值+30h=十进制数对应的ASCII
add al, 30h
mov bx, 0b800h
mov es, bx
mov byte ptr es:[160*12+40*2], ah ;显示月份的十位数码
mov byte ptr es:[160*12+40*2+2], al ;接着显示月份的个位数码
mov ax,4c00h
int 21h
code ends
end start
####shl和shr指令
shl和shr是逻辑移位指令
shl是逻辑左移指令,它的功能为:
- 1.将一个寄存器或内存单元中的数据向左移位;
- 2.将最后移出的一位写入CF中;
- 3.最低位用0补充。
shr是逻辑右移指令,同理。
mov al, 01001000b
shl al, 1 ;将a1中的数据左移一位执行后(al)=10010000b,CF=0。
mov al, 01010001b
mov cl, 3 ;如果移动位数大于1时,必须将移动位数放在cl中
shl al, c1
mov al, 10000001b
shr al, 1 ;将al中的数据右移一位执行后(al)=01000000b,CF=1。
将X逻辑左移一位,相当于执行X=X*2。 将X逻辑右移一位,相当于执行X=X/2。
##外中断
CPU在计算机系统中,除了能够执行指令,进行运算以外,还应该能够对外部设备进行控制,接收它们的输入,向它们进行输出(I/O能力)。
PC系统的接口卡和主板上,装有各种接口芯片。这些外设接口芯片的内部有若干寄存器,CPU将这些寄存器当作端口来访问。
外设的输入不直接送入内存和CPU,而是送入相关的接口芯片的端口中; CPU向外设的输出也不是直接送入外设,而是先送入端口中,再由相关的芯片送到外设。 CPU还可以向外设输出控制命令,而这些控制命令也是先送到相关芯片的端口中,然后再由相关的芯片根据命令对外设实施控制。
即:CPU通过端口和外部设备进行联系
当CPU外部有需要处理的事情发生的时候,比如说,外设的输入到达,相关芯片将向CPU发出相应的中断信息。CPU在执行完当前指令后,可以检测到发送过来的中断信息,引发中断过程,处理外设的输入。
PC系统中,外中断源有两类
1、可屏蔽中断
可屏蔽中断是CPU可以不响应的外中断。CPU是否响应可屏蔽中断,要看标志寄存器的IF位的设置。 当CPU检测到可屏蔽中断信息时,如果IF=1,则CPU在执行完当前指令后响应中断,引发中断过程;如果IF=0,则不响应可屏蔽中断。
可屏蔽中断信息来自于CPU外部,中断类型码是通过数据总线送入CPU的;而内中断的中断类型码是在CPU内部产生的。
中断过程中将IF置0的原因就是,在进入中断处理程序后,禁止其他的可屏蔽中断。 如果在中断处理程序中需要处理可屏蔽中断,可以用指令将IF置1。
8086CPU提供的设置IF的指令:sti,设置IF=1;cli,设置IF=0。
2、不可屏蔽中断
不可屏蔽中断是CPU必须响应的外中断。当CPU检测到不可屏蔽中断信息时,则在执行完当前指令后,立即响应,引发中断过程。
对于8086CPU,不可屏蔽中断的中断类型码固定为2,所以中断过程中,不需要取中断类型码。则不可屏蔽中断的中断过程为:①标志寄存器入栈,IF=0,TF=0;②CS、IP入栈;③(IP)=(8),(CS)=(0AH)。
几乎所有由外设引发的外中断,都是可屏蔽中断。当外设有需要处理的事件(比如说键盘输入)发生时,相关芯片向CPU发出可屏蔽中断信息。不可屏蔽中断是在系统中有必须处理的紧急情况发生时用来通知CPU的中断信息。
####PC机键盘的处理过程 键盘中有一个芯片对键盘上的每一个键的开关状态进行扫描。按下一个键时,开关接通,该芯片就产生一个扫描码,扫描码说明了按下的键在键盘上的位置。扫描码被送入主板上的相关接口芯片的寄存器中,该寄存器的端口地址为60h。松开按下的键时,也产生一个扫描码,扫描码说明了松开的键在键盘上的位置。松开按键时产生的扫描码也被送入60h端口中。
一般将按下一个键时产生的扫描码称为通码,松开一个键产生的扫描码称为断码。
扫描码长度为一个字节,通码的第7位为0,断码的第7位为1 即:断码 = 通码 + 80h。比如,g键的通码为22h,断码为a2h
键盘的输入到达60h端口时,相关的芯片就会向CPU发出中断类型码为9的可屏蔽中断信息。CPU检测到该中断信息后,如果IF=1,则响应中断,引发中断过程,转去执行int 9中断例程。
BIOS提供了int9中断例程,用来进行基本的键盘输入处理,主要的工作如下: (1)读出60h端口中的扫描码; (2)如果是字符键的扫描码,将该扫描码和它所对应的字符码(即ASCII码)送入内存中的BIOS键盘缓冲区; 如果是控制键(比如Ctrl)和切换键(比如CapsLock)的扫描码,则将其转变为状态字节写入内存中存储状态字节的单元; (3)对键盘系统进行相关的控制,比如说,向相关芯片发出应答信息。
BIOS键盘缓冲区可以存储15个键盘输入,一个键盘输入用一个字单元存放,高位字节存放扫描码,低位字节存放字符码。
0040:17
单元存储键盘状态字节,该字节记录了控制键和切换键的状态。键盘状态字节各位记录的信息如下。
0 | 右shift状态 | 置1表示按下右shift键 |
---|---|---|
1 | 左shift状态 | 置1表示按下左shift键 |
2 | Ctrl状态 | 置1表示按下Ctrl键 |
3 | Alt状态 | 置1表示按下Alt键 |
4 | ScrollLock状态 | 置1表示Scroll指示灯亮 |
5 | NumLock状态 | 置1表示小键盘输入的是数字 |
6 | CapsLock状态 | 置1表示输入大写字母 |
7 | Insert状态 | 置1表示处于删除态 |
编写int 9中断例程
;编程:在屏幕中间依次显示“a”~“z”,并可以让人看清。在显示的过程中,按下'Esc'键后,改变显示的颜色。
;完整功能代码:
assume cs:code
stack segment
db 128 dup (0)
stack ends
data segment
dw 0,0
data ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
mov ax,0
mov es,ax
push es:[9*4]
pop ds:[0]
push es:[9*4+2]
pop ds:[2] ;将原来的int 9中断例程的入口地址保存在ds:0、ds:2单元中
mov word ptr es:[9*4], offset int9
mov es:[9*4+2], cs ;在中断向量表中设置新的int 9中断例程的入口地址
;显示字符串
mov ax, 0b800h
mov es, ax
mov ah, 'a'
s:
mov es:[160*12+40*2], ah
call delay
inc ah
cmp ah, 'z'
jna s
mov ax,0
mov es,ax
push ds:[0]
pop es:[9*4]
push ds;[2]
pop es;[9*4+2] ;将中断向量表中int 9中断例程的入口恢复为原来的地址
mov ax,4c00h
int 21h
;将循环延时的程序段写为一个子程序
delay:
push ax
push dx
mov dx, 2000h ;用两个16位寄存器来存放32位的循环次数
mov ax, 0
s1:
sub ax, 1
sbb dx, 0
cmp ax, 0
jne s1
cmp dx, 0
jne s1
pop dx
pop ax
ret
;------以下为新的int 9中断例程--------------------
int9:
push ax
push bx
push es
in al, 60h;从端口60h读出键盘的输入
pushf ;标志寄存器入栈
pushf
pop bx
and bh,11111100b
push bx
popf ;TF=0,IF=0
call dword ptr ds:[0] ;对int指令进行模拟,调用原来的int 9中断例程
cmp al,1
jne int9ret
mov ax,0b800h
mov es,ax
inc byte ptr es:[160*12+40*2+1] ;属性增加1,改变颜色
int9ret:
pop es
pop bx
pop ax
iret
code ends
end start
CPU对外设输入的通常处理方法
- (1)外设的输入送入端口;
- (2)向CPU发出外中断(可屏蔽中断)信息;
- (3)CPU检测到可屏蔽中断信息,如果IF=1,CPU在执行完当前指令后响应中断,执行相应的中断例程;
- (4)可在中断例程中实现对外设输入的处理。
端口和中断机制,是CPU进行I/O的基础。
##直接定址表
assume cs:code
code segment
a : db 1,2,3,4,5,6,7,8 ;在后面加有“:”的地址标号,只能在代码段中使用,不能在其他段中使用。
b : dw 0
start :mov si,offset a
mov bx,offset b
mov cx,8
s : mov al,cs:[si]
mov ah,0
add cs:[bx],ax
inc si
loop s
mov ax,4c00h
int 21h
code ends
end start
程序中,code、a、b、start、s都是标号。这些标号仅仅表示了内存单元的地址。
####描述了单位长度的标号
assume cs:code
code segment
a db 1,2,3,4,5,6,7,8 ;标号a、b后面没有":",因此它们是可以同时描述内存地址和单元长度的标号。
;标号a,描述了地址code:0,和从这个地址开始,以后的内存单元都是字节单元
b dw 0 ;标号b描述了地址code:8,和从这个地址开始,以后的内存单元都是字单元。
start : mov si,0
mov cx,8
s : mov al,a[si]
mov ah,0
add b,ax
inc si
loop s
mov ax,4c00h
int 21h
code ends
end start
使用数据标号来描述存储数据的单元的地址和长度。
assume cs:code,ds:data ;用伪指令assume将标号所在的段和一个段寄存器联系起来(编译器需要)
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
data ends
code segment
start: mov ax,data
mov ds,ax ;真正确定ds寄存器
mov si,0
mov cx,8
s: mov al,a[si] ;编译为:mov al,[si+0] 默认所访问单元的段地址在ds
mov ah,0
add b,ax ;编译为:add [8],ax
inc si
loop s
mov ax,4c00h
int 21h
code ends
end start
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dw a, b ;等价于c dw offset a, offset b
;数据标号c处存储的两个字型数据为标号a、b 的偏移地址
data ends
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dd a,b ;等价于c dw offset a, seg a, offset b, seg b
;数据标号c处存储的两个双字型数据为标号a的偏移地址和段地址、标号b 的偏移地址和段地址
data ends
seg操作符,功能为取得某一标号的段地址。
建立一张表,表中依次存储字符“0”~“F”,我们可以通过数值0 ~ 15直接查找到对应的字符。
assume cs:code
code segment
start:
mov al,0eh
call showbyte
mov ax,4c00h
int 21h
;子程序:
;用al传送要显示的数据
showbyte:
jmp short show
table db '0123456789ABCDEF' ;字符表
show: push bx
push es
mov ah,al
shr ah,1
shr ah,1
shr ah,1
shr ah,1 ;右移4位,ah中得到高4位的值
and al,00001111b ;al中为低4位的值
mov bl,ah
mov bh,0
mov ah,table[bx] ;用高4位的值作为相对于table的偏移,取得对应的字符
mov bx,0b800h
mov es,bx
mov es:[160*12+40*2],ah
mov bl,al
mov bh,0
mov al,table[bx] ;用低4位的值作为相对于table的偏移,取得对应的字符
mov es:[160*12+40*2+2],al
pop es
pop bx
ret
code ends
end start
##指令系统总结 我们对8086CPU的指令系统进行一下总结。读者若要详细了解8086指令系统中的各个指令的用,可以查看有关的指令手册。
8086CPU提供以下几大类指令。
- 数据传送指令
mov、push、pop、pushf、popf、xchg
等都是数据传送指令,这些指令实现寄存器和内存、寄器和寄存器之间的单个数据传送。 - 算术运算指令
add、sub、adc、sbb、inc、dec、cmp、imul、idiv、aaa
等都是算术运算指令,这些指令实现存器和内存中的数据的算数运算。它们的执行结果影响标志寄存器的sf、zf、of、cf、pf、af位。 - 逻辑指令
and、or、not、xor、test、shl、shr、sal、sar、rol、ror、rcl、rcr
等都是逻辑指令。除了not指外,它们的执行结果都影响标志寄存器的相关标志位。 - 转移指令 可以修改IP,或同时修改CS和IP的指令统称为转移指令。转移指令分为以下几类。 (1)无条件转移指令,比如,jmp; (2)条件转移指令,比如,jcxz、je、jb、ja、jnb、jna等; (3)循环指令,比如,loop; (4)过程,比如,call、ret、retf; (5)中断,比如,int、iret。
- 处理机控制指令
对标志寄存器或其他处理机状态进行设置,
cld、std、cli、sti、nop、clc、cmc、stc、hlt、wait、esc、lock
等都是处理机控制指令。 - 串处理指令
对内存中的批量数据进行处理,
movsb、movsw、cmps、scas、lods、stos
等。若要使用这些指令方便地进行批量数据的处理,则需要和rep、repe、repne
等前缀指令配合使用。
##80386 CPU 三个模式
- 实模式:工作方式相当于一个8086.
- 保护模式: 提供支持多任务环境的工作方式,简历保护机制(这与VAX 得小型机类似)
- 虚拟8086模式: 可从保护模式切换至其中的一种8086工作方式。
虚拟8086其实就是我们常用的window下的dos!