【原创】汇编语言的基本知识(三)
九,标志(flag)寄存器
1,标识寄存器是一类特殊的寄存器,具有以下3种作用:
(1)用来存储相关指令的某些执行结果
(2)用来为cpu执行相关指令提供行为依据
(3)用来控制cpu的相关工作方式
2,8086cpu的flag寄存器结构为:0~15
CF:第0位:进位标志位,
PF:第2位:奇偶标志位,记录相关指令结束后,其结果的所有bit位中1的个数是否为偶数。为偶数则PF=1
ZF:第6位:零标志位,记录相关指令结束后,其结果是否为0。为0则ZF=1
SF:第7位:符号标志位,记录相关指令结束后,其结果是否为负。为负则SF=1
OF:第11位:溢出标志位,记录了有符号数运算的结果是否发生了溢出。溢出则OF=1
DF:第10位:方向标志位,在串处指令中,控制每次操作后si,di的增减。DF=0时si,di递增,反之递减
AF:
TF:
IF:
3,adc指令:带进位加法指令,利用了CF位上记录的进位值
格式: adc 对象1,对象2
功能:对象1=对象1+对象2+CF
4,sbb指令:带进位加法指令,利用了CF位上记录的错位值
格式: sbb 对象1,对象2
功能:对象1=对象1-对象2-CF
5,cmp指令:比较指令,相当于减法指令,只是不保存结果
比如,指令cmp ax,ax 做(ax)-(ax)的运算,结果为0,但并不在ax中保存,仅影响flag的相关各位。
指令执行后:ZF=1,PF=1,SF=0,CF=0,OF=0
6,条件转移指令
(1)"转移"指的是它能修改IP,条件指的是可以根据某种条件决定是否修改IP
(2)jcxz就是一个条件转移指令,可以根据检测cx中的数值,如果(cx)=0,就修改IP,否则什么也不做
(3)大多数条件转移指令都检测flag寄存器的相关标志位:这些标志位就是被cmp指令影响的那些,所有这些指令通常都和cmp相配合使用,就像call和ret指令通常配合使用一样
(4)cmp指令可以同时进行2中比较:无符号数比较和有符号数比较
无符号数的比较(检测ZF、CF的值)有符号数的比较(SF、OF、ZF的值)
7,pushf和popf,为直接访问flag寄存器提供了一种方法,
pushf:将标志寄存器的值压栈
popf:从栈中弹出数据,送入标志寄存器中
十,直接定址表和标号
1,我们一直在代码中使用标号来标记指令、数据、段的起始地址:如下
assume cs:code code segment a: db 1,2,3,4,5,6,7,8 ;注意a后面的: db内存单元都是字节单元 b: dw 0 ;dw 描述的内存单元都是字单元 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都是标号。这些标号仅仅表示了内存单元的地址
2,我们还可以使用一种标号,这种标号不但表示了内存单元的地址,还表示了内存单元的长度,上面的程序可以改写为
assume cs:code code segment a db 1,2,3,4,5,6,7,8 ;a后面没有: b dw 0 start: mov si,0 mov cx,8 s: mov al,a[si] mov ah,0 inc si loop s mov ax,4c00h int 21h code ends end start
3,一般来说,我们不在代码段中定义数据,而是将数据定义在其他段中
下面的程序将data段中的a标号处的8个数据累加,结果存储到b标号处的字中
assume cs:code ds:data 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 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
4,用查表的方法编写相关程序
编写子程序:以十六进制的形式在屏幕中显示给定的字节型数据
(1)分析:
一个字节需要2个十六进制的数码来表示,所以需要在屏幕上显示2个ASCII字符;
因为数值0~15和字符"0"~"F"之间没有一致的映射关系存在没,所以我们应该建立这种映射关系
用al传递要显示的数据
showbyte: jmp short show table db '012345678ABCDEF' ;字符表 show: push bx push es mov ah,al shr ah,1 shr ah,1 shr ah,1 shr ah,1 and al,00001111b mov bl,ah mov bh,0 mov ah,table[bx] mov bx,0b800h mov es,bx mov es:[160*12+40*2],ah mov bl,al mov bh,0 mov al,table[bx] mov es:[160*12+40*2+2],al pop es pop bx ret
5,程序入口地址的直接定址表:
我们可以在直接定址表中存储子程序的地址,从而方便实现不同子程序的调用
(1)用ah寄存器传递功能号;
(2)将不同功能分别写入各个子程序中。
(3)将这些功能的子程序的入口地址存储在一个表中,它们在表中的位置和功能号相对应。
如下:
省略子程序的代码 ... setscreen: jmp short set table dw sub1,sub2,sub3,sub4 set: push bx cmp ah,3 ja sret mov bl,ah mov bh,0 add bx,bx call word ptr table[bx] sret: pop bx ret
也可以如下实现,但扩展性不好
setscreen: cmp ah,0 je do1 cmp ah,1 je do2 cmp ah,2 je do3 cmp ah,3 je do4 jmp short sret do1: call sub1 jmp short sret do2: call sub2 jmp short sret do3: call sub3 jmp short sret do4: call sub4 jmp short sret sret:ret
十一,内中断
1,中断信息:通用的cpu都具备一种能力,可以在执行完当前正在执行的指令后,检测到从cpu外部发送过来或者内部产生的一种特殊信息,并且立即对所接收到的信息进行处理。这种信息就是中断信息
2,内中断:类别和中断类型码
(1)除法错误:0;当cpu执行div等除法指令的时候,如果发生了除法溢出错误时产生
(2)单步中断:1;基本上cpu在执行完一条指令后,如果检测到标志寄存器的TF位为1,则产生单步中断
(3)执行into指令:4;
(4)执行int指令;该指令1的格式为 int n,可以引发一个n号中断,其中n是提供给cpu的中断类型码
3,一般来说,需要对不同的中断信息编写不同的处理程序,cpu在收到中断信息后,应该立即执行该中断信息的处理程序。即将CS:IP指向它的入口(程序第一条指令的地址)。
4,中断向量表:cpu用8位的中断类型码通过中断向量表找到对应的中断处理程序的入口地址,
中断向量表就是中断处理程序的入口地址的列表,在内存中保存。
5,中断处理程序的编写方法:
(1)保存用到的寄存器
(2)处理中断
(3)恢复用到的寄存器
(4)用iret指令返回
6,0号中断的处理过程
1,取得中断类型码0 2,标志寄存器入栈,TF、IF设置为0 3,CS、IP入栈 4,(IP)=(0*4),(CS)=(0*4+2)
7,单步中断的处理过程
1,取得中断类型码1 2,标志寄存器入栈,TF、IF设置为0 3,CS、IP入栈 4,(IP)=(1*4),(CS)=(1*4+2)
8,int指令中断的处理过程
1,取得中断类型码n 2,标志寄存器入栈,TF、IF设置为0 3,CS、IP入栈 4,(IP)=(n*4),(CS)=(n*4+2)
9,深入理解 int金额iret指令
assume cs:code code segment start:mov ax,0b800h mov es,ax mov di,160*12 mov bx,offset s-offset se ;设置从标号se到标号s的转移位移 mov cx,80 s: mov byte ptr es:[di],'!' add di,2 int 7ch ;如果(cx)不等于0,转移到标号s处 se: nop mov ax,4c00h int 21h code ends end start
上例可见:
(1),在中断例程中可以从栈中取得s的段地址和标号se的片偏移地址
(2),我们将栈中的se的偏移地址+bx中的转移位移,则栈中的se的偏移地址就变为了s的偏移地址。我们在使用iret指令,用栈中的内容设置CS、IP,从而实现转移斗到标号s处;7ch中断例程如下
lp: push bp mov bp,sp dec cx jcxz lpret add [bp+2],bx lpret: pop bp iret
10,BIOS和DOS所提供的中断例程
(1)在系统版的ROM中存放着一套程序,称为BIOS(基本输入输出系统),该程序系统包含以下几个部分
1,硬件系统的检测和初始化程序 2,外部中断和内部中断的中断例程 3,用于对硬件设备进行I/O操作的中断例程 4,其他和硬件系统相关的中断例程
(2)操作系统DOS也提供了中断例程,从操作系统的角度来看,DOS的中断例程就是操作系统向程序员提供的编程资源;和硬件设备相关的DOS中断例程中,一般都调用了BIOS的中断例程
11,BIOS中断例程的应
(1)一个供程序员调用的中断例程往往包含多个子程序,中断例程内部用传递进来的参数来决定执行哪一个子程序。BIOS和DOS提供的中断例程,都用ah来传递内部子程序的编号;
举例:用int 10h中断例程的设置光标位置功能
mov ah,2 ;置光标 mov bh,0 ;第0页 mov dh,5 ;dh中放行号 mov dl,12 ;dl中放列号 int 10h
12,DOS中断例程的应用
(1)我们一直使用的是int 21h中断例程的4ch号功能,即程序返回功能;
mov ah,4ch ;程序返回 mov al,0 ;返回值 int 21h
(2)程序返回也就好理解了
mov ax,4c00h int 21h
十二,端口和外中断
1,和cpu通过总线相连的芯片除各种存储器外,还有以下3种芯片:
1,各种接口卡上的接口芯片,他们控制接口卡进行工作 2,主板上的接口芯片,cpu通过他们对部分外设进行访问 3,其他芯片,用来存储相关的系统信息,或进行相关的的输入输出处理
这些芯片都有一组可以由cpu读写的寄存器,它们在物理上处于不同的芯片中,且有以下2个共同点
1,都和cpu的总线相连,这种连接是通过它们所处的芯片进行的 2,cpu对它们进行读写时都通过控制线向它们所在的芯片发出端口读写命令
从cpu的角度,将这些寄存器都当作端口,对它们进行统一编址,从而建立了一个统一的端口地址空间。
端口地址和内存地址一样,通过地址总线来传递。
2,cpu可以直接读写以下3个地方的数据
(1)cpu内部的寄存器
(2)内存单元
(3)端口
3,端口的读写指令只有2条:in和out
in al,60h ;从60h号端口读入一个字节
out 20h,al ;往20h号端口写入一个字节
4,shl和shr指令
(1)如下:
mov al,01001000b shl al,1 ;将al中的数据左移一位
结果(al)=10010000b,CF=0
mov al,10010000b
shl al,1 ;
结果(al)=00100000b,CF=1
执行后(al)=10010000b,CF=0
shl是逻辑左移指令,其功能为
(1)将一个寄存器或内存单元中的数据向左位移 (2)将最后移出的一位写入CF中 (3)最低位用0补充
(2)如果移动位数大于1,必须将移动位数放在cl中,比如指令:
mov al,01010001b mov cl,3 shl al,cl
(3)可以看出,将X逻辑左移一位,相当于执行 X=X*2
(4)shr是逻辑右移指令,它所进行的操作刚好相反
5,cpu除了运算能力外,还要有I/O能力。比如按下a键,然后在显示器上显示。
所以cpu需要解决2个问题
(1)外设的输入随时可能发生,cpu如何得知
(2)cpu从何处得到外设的输入:
6,可屏蔽中断:cpu可以不响应的外中断
cpu是否响应可屏蔽中断,要看标志寄存器的IF位的设置。IF=1,则响应;IF=0,则不响应
7,不可屏蔽中断是cpu必须响应的外中断。cpu在执行完当前指令后,立即响应,引发中断过程
某些cpu的的不可屏蔽中断的中断类型码固定为2,所以中断过程不需要取中断类型码
不可屏蔽中断的的中断过程为:
1,标志寄存器入栈,IF=0,TF=02,CS,IP入栈 3,(IP)=(8),(CS)=(0AH)