汇编原理自我总结(三)

汇编原理自我总结(三)

第十一章 标志寄存器

在8086CPU中,是一种特殊的寄存器,叫做flag。作用如下:

  1. 储存相关指令的某些执行结果;
  2. 用来为CPU执行某些指令提供依据;
  3. 用来控制CPU的相关工作方式;

flag是按位起作用的,每一位都具有专门的含义,记录特定的信息。

在这里插入图片描述

11.1 ZF标志

ZF是flag的第6位,用来指定相关执行指令后,其结果是否为0。如果为0,则为真,设1,反之设0。1表示逻辑真,0表示逻辑假。如:

mov ax,1

sub ax,1 ;此时,ZF为真,设1

mov ax,2

sub ax,1 ;(ax) != 0 ,ZF为假,设0

在指令集中,有些指令是影响标志寄存器的,如:

add, sub,mul,div,inc,and,or ;大多是运算指令

mov ,pop, push ;不影响,大多是传送指令

11.2 PF标志

PF是flag的第2位,用来标识相关指令执行后,其结果中的bit位中的1是否为偶数。如果是偶数,pf=1,反之,若为奇数,pf = 0。如:

mov al,1

add al,10 ;al = 00001011 pf=0

mov al,1

add al,2 ;al = 00000011 pf=1

sub al,3 ;al = 00000000 pf=1

11.3 SF标志

SF是flag的第7位,用来标识相关指令执行后,其结果是否为负。若为负,sf = 1,不为负,sf = 0。但是,在计算机中,同一数值可以解释为负数或正数,这就意味着同一信息包含两种不同的结果。SF正是对运算结果的记录,它记录数据的正负。如果我们数据当作有符号数来运算,可以通过它来得知数据结果。如:

mov al,10000001B

add al,1 ;al = 10000010B, sf=1 =>-126(补码)

mov al,10000001B

add al,01111111B ;al = 0 sf=0 表示若进行无符号运算,结果为正

11.4 CF标志

CF是flag的第一位,进位标志位,一般情况下,在进行无符号数运算的时候,记录了最高有效位向更高位的进位值,或从更高位的借位值。

当两个数进行运算时,有可能当前的位数不能够保存,就将进位值记录在CF上。

mov al,98H

add al,al ;(al)=30H, cf=1 进位

add al,al ;(al)=60H, cf=0

mov al,97H

sub al,98H ;(al)=ffH, cf=1 借位

sub al,al ;(al)=0,cf = 0

11.5 OF标志

flag的第11位,溢出标志位,进行有符号数运算的时候,有时会发生溢出,如:-89+(-98) = -187 ,而8位的有符号表示范围是-128~127,不能保存完整数值,所以发生溢出而造成结果的错误。如果发生溢出,OF=1。OF是对于有符号数来说的,而CF是对于无符号数说的,两者没有任何关系。

mov al,98

add al,99 ;于无符号数,cf=0.于有符号数,of=1

mov al,0f0H

add al,80H ;cf = 1,of = 1

11.6 adc指令

带位加法指令。利用了CF中的进位值。

adc object1,object2

objiect1 = object+object2+cf

将两个较大的数据相加,如超过16位的的数据相加,可以分为低16位相加,然后将高16位和进位值相加,这样就完成了大数据的加法,由于每次相加都会将进位放在进位标志中,所以此种方法可以将任意大的数据相加。如:

;1ef000H+201000H
mov ax,001eH
mov bx,0f000H
add ax,1000H; 低16位相加  设置cf
adc bx,0020H  ;高16位相加 利用cf
;1ef0001000h + 2010001ef0H
mov ax,001eH
mov bx,0f000H
mov cx,1000H
add cx,1ef0H
adc bx,1000H
adc cx,0020H

编写一个子程序,将两个128位数据进行相加:

;add128
;将两个128位数据进行相加
;参数:ds:di 指向第一个数的存储空间,8个字单元,由低到高依次存储128位数据的由低到高的各个字  ds:di指向第二个数。
add128:
		push ax
		push cx
		push si
		push di
		
		sub ax,ax  ;将cf设为0
		
		mov cx,8
	s:
		mov ax,[si]
		adc ax,[di]
		mov [si],ax
		inc si
		inc si
		inc di  ;不能用add si,2 这样会影响cf的值
		inc di
		
		loop s
		
		pop di
		pop si
		pop cx
		pop ax

11.7 sbb指令

带位减法指令,设计思想和adc一致,用法也一样。

sbb object1,object2

object1 = object1-object2-cf

;003e1000H-00202000H
mov ax,1000H
mov bx,003e
sub ax,2000H
sbb bx,0020H

11.8 cmp指令

cmp是比较指令,相当于不存储结果的减法指令,但是对标志寄存器产生影响,其它指令通过标志寄存器来得知比较结果。如:

mov ax,2

cmp ax,2 ;zf=1, pf=1 , sf=0,cf=0,of=0

mov ax,8

mov ax,5 ;zf=0, pf=1, sf=0,cf=0,pf=0

比较寄存器得知结果:

mov ax,bx

ax == bx ;zf=1

ax != bx ;zf=0

ax < bx ;产生借位,cf=1

ax >= bx ;不产生借位 , cf=0

ax > bx ;不借位,结果也不为0 ;cf=0, zf=0

ax<=bx ;可能有借位,也可能为0,cf=1, zf=1

所以,可以通过标志寄存器来得知比较的结果:

无符号数的比较:

cmp 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 => ax<=bx

有符号数的比较:

总结不清楚,所以直接贴图:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

mov ah,al

zf = 1 => ah == al

zf = 0 => ah != al

sf = 1, of=0 => ah<al

sf = 0,of = 0 => ah>al

sf =1,of = 1 => ah>al

sf = 0, of = 1 =>ah<al

11.9 检测比较结果的条件转移指令

所有的条件转移指令都是短转移【-128~127】。

大多数条件转移指令都根据标志寄存器的某些位来决定是否修改IP,是其中受cmp指令影响的那些位,根据比较结果设置的。这些条件转移指令通常和cmp指令配合使用。

无符号数的比较与转移指令

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 n => not a => above b => below

编程:如果al == ah,al = al+al,否则,ah = al+ah

com al,ah
je s
add al,al
jmp short ok
s:
add ah,al
ok:...

编程:统计ds:di中的8个数据中大于10H的数的个数:

assume cs:code
data segment
	db 20H, 2H,30H,25H,6H,34H,23H,7H
data ends

code segment
start:
	mov ax,data
	mov ds,ax
	mov ax,0
	mov di,0
	mov cx,8
s:
	cmp byte ptr [di],20H
	jna next
	inc ax
	
next:inc di
	loop s
	code ends
end start

编写指令计算在内存F000:0的32个字节数据中,在[32~128]的数据的个数:

assume cs:code
code segment
start:
	mov ax,0f000H
	mov ds,ax
	mov bx,0
	mov dx,0
	mov cx,32
	
s:
	mov al,[bx]
	cmp al,32
	jb s0
	cmp al,128
	ja s0
	inc dx
	
s0: inc bx
	loop s
	code ends
end start

11.10 DF标志和串传送指令

DF标志是flag的第10位,用来指定串传送的方向,即将di和si递增或是递减。

串传送指令:movsb,将ds:di中的内容传送到es:si中,单位是字节,次数由cx决定。

和rep指令配合使用

在传送之前,程序需要知道方向是如何的,这样才能从DF中获得信息,决定是递增还是递减。

cld:将df置0 => inc di, inc si

std:将df置1 => dec di,dec si

assume cs:code
data segment
	db 'Welcome to masm!'
	db 16 dup (0)
data ends
code segment
start:
	mov ax,data
	mov ds,ax
	mov es,ax
	mov si,0
	mov di,10H   ;源地址:es:si   data:0目的地址:ds:di  data:10H
	mov cx,16  ;长度
	cld           ;方向
	
	rep movsb
	
	code ends
	end start
	

将内存f000:0段中最后16个字符复制到data段中:

assume cs:code
data segment
	dw 16 dup (0)
data ends
code segment
start:
	mov ax,data
	mov ds,ax
	mov di,000fH
	mov ax,0f000H
	mov es,ax
	mov di,0ffffH
	std
	mov cx,16
	
	rep movsb
	
	code ends
	end start

11.11 pushf 和 popf

将标志寄存器的值压栈,和将标志寄存器的值出栈。

mov ax,0

push ax

popf

mov ax,0fff0h

add ax,0010h

pushf

pop ax

and al,11000101B

and ah,00001000B

将执行add ax,0010h指令后标志寄存器的值压栈,之后出栈后就是ax的值,再进行两个逻辑与运算。

11.12 标志寄存器在Debug中的表示

在这里插入图片描述

实验十一

编写一个子程序,将包含任意字符,以0结尾的字符串中的小写字母转化为大写字母。

;letterc
;将以0结尾的字符串中的小写字母转化为大写字母。
;ds:si指向字符串的首地址
assume cs:code
data segment
	db "Beginner's All-purpose Symbolic Instruction Code.",0
data ends

code segment
begin:
		mov ax,data
		mov ds,ax
		mov si,0
		call letterc
		
		mov ax,4c00h
		int 21h
		
letterc:
		push ax
	s:
		mov al,[si]
		cmp al,0
		je end
		cmp 97
		jb s0
		cmp 123
		ja s0
		
		sub al,20H
		mov [si],al
		
	s0: inc si
		loop s
		
	end:
		pop ax
		ret
	code ends
	end begin


第十二章 内中断

中断是指CPU在接收到来自外部或由内部产生的信息时,停止执行下一条指令,转而去处理这个信息。

12.1 内中断的产生

中断信息在什么情况下发生呢?8086CPU有以下4中内中断:

  1. 除法错误,如产生溢出
  2. 单步执行
  3. 执行into 指令
  4. 执行int 指令

要分别处理各种中断,就要区别各种中断。每种中断都有中断码来区分,所以中断信息中包含了终端码。中断码是一个字节,因此可以区分256中中断。

  1. 除法错误: 0
  2. 单步执行: 2
  3. 执行into指令: 4
  4. 执行int指令,指令格式为:int n,n是字节型立即数,提供给CPU的中断类型码。

12.2 中断处理程序

收到中断信息后,要对中断信息进行处理,而处理是由CPU执行中断处理程序来对信息做出反应的。中断处理程序由自己编写,一般不同的中断要编写不同的处理程序。既然要执行程序,就必须要让CS:IP指向该程序的第一条指令,就必须要知道程序的入口,即段地址:偏移地址。而中断码的设计正是用来解决此种问题。

12.3 中断向量表

中断向量是指中断处理程序的入口地址,那么中断向量表就是存放中断处理程序入口的列表。这样,就可以通过中断码来查找其相应的中断程序入口地址。但先决条件是找到中断向量表,中断向量存放在内存的固定位置。8086CPU中,它在内存0000:0000~0000:03ff的1024个内存单元中。一个表项存放一个入口地址,大小为两个字,高地址字存放段地址,低地址字存放偏移地址。

12.4 中断过程

中断过程是指取得终端码,通过中断向量表取得程序入口地址,设置CS:IP的过程,这一过程由硬件完成。由于执行完中断程序后,还要回到原来指令处执行,需要保存一些值,具体过程如下:

  1. 取得中断码N

  2. pushf 将标志寄存器入栈,因为后面有修改

  3. 将标志寄存器中IF与TF置0

  4. push CS

  5. push IP

  6. (IP) = (N*4) (CS) = (N*4+2)

最后一步完成后,CPU开始执行中断程序指令。

12.5 中断处理程序和iret指令

由于随时都可能处理中断信息,所以中断处理程序必须一直都在其内存位置,编写中断处理程序与编写子程序相差不多:

  1. 将用到的寄存器入栈
  2. 处理中断信息
  3. 恢复寄存器
  4. iret

iret指令与中断过程配合使用,先后将IP,CS,标志寄存器出栈。

12.6 除法错误中断的处理

执行下列指令:

mov ax,1000h

mov bh,01

div bh

将会产生除法错误,从而出现中断。但各个系统的中断处理程序不同。

12.7 编程处理0号中断

重新编写0号中断处理程序,功能是在屏幕中间出现“overflow”,之后返回系统。

要做的事有以下几项:

  1. 编写中断处理,显示“overflow”:do0;
  2. 将do0这段程序放在内存空间:0:200~0:2ff处(这段空间系统不会用,且256字节足够);
  3. 将中断向量表中对应的0号表项内容设为中断程序的入口地址:0:0200H;

12.8 安装

assume cs:code
code segment
start:
		mov ax,cs              ;源地址
		mov es,ax
		mov si,offset do0
		
		mov ax,0
		mov ds,ax
		mov di,200H          ;目的地址
		
		mov cx,offset do0end-offset do0;取得处理中断代码段长度
		
		cld
		rep movsb
		
		;设置中断向量表
		
		mov ax,4c00h
		int 21h
		
		do0:
		;显示字符串
		
		do0end:nop
		
		code ends
		end start

12.9 do0

assume cs:code
data segment
	db 'overflow!'
data ends
code segment
start:
		mov ax,cs              ;源地址
		mov es,ax
		mov si,offset do0
		
		mov ax,0
		mov ds,ax
		mov di,200H          ;目的地址
		
		mov cx,offset do0end-offset do0;取得处理中断代码段长度
		
		cld
		rep movsb
		
		;设置中断向量表
		
		mov ax,4c00h
		int 21h
		
		do0:
		;显示字符串
		mov ax,data
		mov ds,ax
		mov si,0    ;指向字符串
		
		mov ax,0b800h
		mov es,ax
		mov di,12*160+36*2 ;
		
		mov cx,9
	s:	mov al,[si]
		mov es:[di],al
		inc si
		add di,2
		loop s
		
		mov ax,4c00h
		int 21h
		do0end:nop
		
		code ends
		end start

以上的程序看似没有问题,但是不能正常工作。原因是数据被放在data段,但是程序安装完成后,内存空间要被释放,用作他途,又很大可能会被覆盖。所以要将数据放在处理中断程序当中,如下:

assume cs:code
code segment
start:
		mov ax,cs              ;源地址
		mov ds,ax
		mov si,offset do0
		
		mov ax,0
		mov es,ax
		mov di,200H          ;目的地址
		
		mov cx,offset do0end-offset do0;取得处理中断代码段长度
		
		cld
		rep movsb
		
		;设置中断向量表
		mov ax,0
		mov es,ax
		mov word ptr es:[0*4],0200h
		mov word ptr es:[0*4+2],0
		
		mov ax,4c00h
		int 21h
		
		do0:
		jmp short do0start  ;2字节
		db 'overflow!'
		;显示字符串
	do0start:
		mov ax,cs ;数据在程序段中,开头0:202H处,把cs写成了cx,我哭了
		mov ds,ax
		mov si,202h
		
		mov ax,0b800h
		mov es,ax
		mov di,160*12+32*2
		
		mov cx,9
	s:
		mov al,[si]
		mov es:[di],al
		inc si
		add di,2
		loop s
		mov ax,4c00h
		int 21h
		
		do0end:nop
		
		code ends
		end start

12.10 设置中断向量

mov ax,0

mov es,ax

mov word ptr es:[0*4],0200h

mov word ptr es:[0*4+2],0

12.11 单步中断

单步中断的中断码是1。当CPU检测到标志寄存器的TF = 1时,就触发了单步中断。Debug 正是利用了这一中断,其实单步中断就是为实现单步追踪程序而设计的机制。但是,单步中断程序本身也是程序指令,那中断的指令引发单步中断,之后的中断又引起新的中断,如此循环。将标志寄存器·入栈,之后再将TF置0,就解决了这个问题。所以步骤如下:

  1. 取得中断码1
  2. 标志寄存器入栈
  3. TF=0,IF=0
  4. cs,ip入栈
  5. (ip) =(1*4),(cs)=(1*4+2)

12.12 响应中断的特殊情况

有些情况下,即便是发生中断,也不会得到响应,例如:

mov ax,stack

mov ss,ax

mov sp,10h

其中,执行完mov ss,ax后,紧接着就执行mov sp,10h。这是因为如果发生中断,就只设置了ss,而却要将标志寄存器,cs ,ip等入栈,没有设置栈顶而导致错误。

实验十二

编写0号中断的处理程序,使其在屏幕中间显示“divide error!”;

assume cs:code
code segment
start:
	;安装
	mov ax,cs
	mov ds,ax
	mov si,offset do0

	mov ax,0
	mov es,ax
	mov di,0200h

	mov cx,offset do0end-offset do0
	
	cld
	rep movsb

	;设置中断向量表
	mov ax,0
	mov es,ax
	mov word ptr es:[0*4],0200h
	mov word ptr es:[0*4+2],0

	mov ax,4c00h
	int 21h
	;do0

do0:
	jmp short do0start
	db 'divide error!'
do0start:
	;function
	mov ax,cs
	mov ds,ax
	mov si,202h
	
	mov ax,0b800h
	mov es,ax
	mov di,160*12+33*2

	mov cx,13

s:	mov al,[si]
	mov es:[di],al
	inc si
	add di,2
	loop s
	
	mov ax,4c00h
	int 21h

do0end:	nop
code ends
end start

同上一程序没有差别。

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值