8086汇编精讲3

学习学习

Posted by lll-yz on October 25, 2021

标志寄存器概述

特殊的寄存器:FLAG

CPU内部的寄存器中,有一种特殊的寄存器(对于不同的处理机,个数和结构都可能不同)具有以下3种作用:

  • 用来存储相关指令的某些执行结果。
  • 用来为CPU执行相关指令提供行为依据。
  • 用来控制CPU的相关工作方式。

这就是标志寄存器,8086CPU的标志寄存器有16位,其中存储的信息通常被称为程序状态字(PSW)。 (可以理解为状态寄存器)

FLAG内部结构:

5qNUs0.png

ZF

1、flag的第六位是ZF,零标志位。

2、它记录相关指令执行后,其结果是否为0。

3、如果结果为0,那么zf=1;如果结果不为0,那么zf=0。

PF

1、flag的第二位是PF,奇偶标志位。

2、记录相关指令执行后,结果的所有bit位中1的个数是否是偶数。

3、如果结果bit位中有偶数个1,则pf=1,奇数个1,pf=0。

SF

1、flag的第七位是SF(symbol flag),符号标志位。

2、它记录相关指令执行后,结果是否为负。

3、如果为负,sf=1;非负,则sf=0。

5qaCND.png

CF

1、flag的第0位是CF,进位标志位。

2、一般情况下,在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值。

5qa3gs.png

eg. 98H+98H=130H

mov al,98H
add al,al	;执行后,(al)=30H,CF=1,CF记录了从最高有效位向更高位的进位值
add al,al	;执行后,(al)=60H,CF=0,CF记录了从最高有效位向更高位的进位值

97H-98H=FFH

mov al,97H
sub al,98h	;执行后,(al)=FFH,CF=1,CF记录了向更高位的借位值
sub al,al	;执行后,(al)=0,CF=0,CF记录了向更高位的借位值

OF

谈谈溢出:有符号数运算时果超过了机器范围叫溢出。

mov al,98
add al,99
;(al)=(al)+99=98+99=197 >127(对于8位有符号数来讲)

1、flag的第11位是OF,溢出标志位。

2、OF记录了有符号数运算结果是否溢出。

3、如果溢出,OF=1,没有则OF=O。

mov al,98					mov al,0F0H
add al,99					add al,88H
;add指令执行后:CF=0,OF=1		add 指令执行后:CF=1,OF=1

mov al,0F0H
add al,78H
;add指令执行后:CF=1,OF=0

检测

1、补全程序,实现从内存1000:0000处开始执行程序

assume cs:code

stack segment
 db 16 dup (0)
stack ends

code segment
start:mov ax,stack
      mov ss,ax
      mov sp,16  ;初始化栈
      mov ax,(1000H)  ;cs的值
      push ax
      mov ax,(0)  ;IP的值
      push ax
      retf
code ends
  end start
      

CPU执行retf指令时,进行四步操作:

  1. (IP)=((ss)*16+sp)
  2. (sp)=(sp)+2
  3. (CS)=((ss)*16+sp)
  4. (sp)=(sp)+2 也就是相当于进行 pop IP pop CS

2、下面程序执行后,ax中的值是多少?

assume cs:codesg 
codesg segment  
 start:  
        mov ax,0 
        call s
        inc ax 
       s:pop ax
codesg ends   
end start
1000:0 	mov ax,0		;读取此条指令后IP=3 ,执行完该指令后IP=3
1000:3 	call s			;读取此条指令后IP=6 ,所以IP=6入栈,执行完该指令后IP=7,跳转到s处
1000:6 	inc ax
1000:7 	s:pop ax		;所以POP后,ax=6

ax=6

adc

adc是带进位加法指令,它利用了CF位上的记录的进位值。

格式:adc 操作对象1,对象2

功能:对象1=对象1+对象2+CF

比如:

mov ax,2
mov bx,1
sub bx,ax
adc ax,1	;执行后,(ax)=4,adc执行时,相当于计算2+1+1=4

sbb

sbb带借位减法指令。

格式:sbb 操作对象1,对象2

功能:对象1=对象1-对象2-CF

cmp

cmp是比较指令,cmp的功能相当于减法指令,但是不保存结果。

格式:cmp o1,o2

功能:o1-o2,但不保存结果,只对标志寄存器进行重置。

cmp ax,ax
做(ax)-(ax)运算,结果为0。
此时的flag相应变化:zf=1,pf=1,sf=0,cf=0,of=O
mov ax,8
mov bx,3
cmp ax,bx
;执行后:(ax)=8,zf=0,pf=1,sf=0,cf=0,of=0

5OnzKe.png

条件转移指令

5Ou1P0.png

eg.统计8的个数存于AX

data segment
	db 8,11,8,1,8,5,63,38
data ends
assume cs:code,ds:data
data segment
	db 8,11,8,1,8,5,63,38
data ends

code segment
	mov ax,data
	mov ds,ax
	mov bx,0
	mov ax,0
	mov cx,0
s:
	cmp byte ptr [bx],8
	jne next
	inc ax
next:
	inc bx
	loop s
	
	mov ax,4c00h
	int 21h
code ends

DF和串传送指令

第十位是DF(definition flag),控制每次操作后si、di的增减。

df=0,每次操作后si di递增。

df=1,每次操作后si di递减。

串传送指令:movsb

movsb指令相当于相当于进行下面几步:

1、((es)*16+(di))=((ds)*16+(si))

2、如果 df=0 则:inc si

​ inc di

​ 如果 df=1 则:dec si

​ dec di

这里的b是byte,所以相对偏移量为1。

除了movsb,还有movsw,显然这货就是按字操作,id和si增减单位为2字节。

mov es:[di],word ptr ds:[si]

如果 df=0:

add si,2

add di,2

如果 df=1

sub si,2

sub di,2

movsb和movsw都是串传送操作的一个步骤,一般配合rep使用。

rep: 重复执行该语句直至寄存器cx为0

rep movsb

相当于:s: movsb

​ loop s

DF的特权:

  • cld:df位清0
  • std:df位置1

(1)编程,用串传送指令,将data段中的第一个字符串复制到它后面的空间中。

data segment
	db 'Welcome to masm!'
	db 16 dup(0)
data ends
code segment
start:
	mov ax,data
	mov ds,ax
	mov si,0	;ds:si指向data:0
	mov es,ax
	mov di,16	;es:di指向data:001h
	mov cx,16
	cld
	rep movsb
	
	mov ax,4c00h
	int 21h
code ends
end start

pushf和popf

pushf的功能是将标志寄存器的值压栈,而popf就是将其出栈,扔入flag。

下面的程序执行后:(ax)=?

mov ax,0
push ax
popf
mov ax,0fff0h
add ax,0010h
pushf
pop ax
and al,11000101B
and al,00001000B

执行add ax,0010h后,flag寄存器中的情况:

5OaorR.png

ax=45h

debug中的标志位

5OdLmn.png

检测

1、下面程序执行后,ax中的数值为多少?

内存地址	    汇编指令
1000:0	      mov ax,0
1000:3        call far ptr s ;将1000:0008存入栈中 先cs后IP
1000:8        inc ax
1000:9        s:pop ax		;弹出IP的值8,所以AX=8
              add ax,ax  	;ax=ax+ax = 16=10H
              pop bx		;弹出CS的值1000,所以BX=1000
              add ax,bx		;所以ax=10H(即16)+1000H=1010H 即1010H,所以指令执行后,AX=1010

中断

官方的回答就不说了,懂得都懂,不懂自己看去。

中断的意识就是说,CPU不再向下执行,而是去处理这个信息,再回去。

内中断的产生

当CPU内部有什么事发生的时候,将产生需要马上处理的中断信息呢?对于8086来说,当CPU内部有下面的情况发生时,产生相应的中断:

  • 除法错误,比如,执行div指令产生的除法溢出;
  • 单步执行;
  • 执行into指令;
  • 执行int指令。

8086用称为中断类型码的数据来识别中断信息的来源。中断类型码位一个字节型数据,可以表示256种中断信息的来源。

上面的四种中断源,在8086中中断类型码如下:

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

中断处理程序

顾名思义,就是对中断信息进行处理的程序。

中断向量表

中断向量表在内存中保存,其中存放着256个中断源所对应的中断处理程序的入口。

5X0pF0.png

对于8086pc机,中断向量表指定放在内存地址0处。从0000:0000到0000:03FF的1024个单元中存放着中断向量表。

一个表项占两个字,高字节放段地址,低字节放偏移地址。

中断过程

中断类型码–>中断向量表–>修改CS:IP–>中断处理程序

这个过程由CPU自动完成,这一过程叫中断过程。另外,中断执行后,一般还要返回原指令处,所以类似CALL,还要将原来的CS和IP保存起来。

简洁描述:

1、取得中断类型码N

2、pushf

3、TF=O,IF=O

4、push CS

5、push IP

6、(IP)=(N*4),(CS)=(N*4+2)

然后,执行程序员编写的中断处理程序。

中断信息可能随时被检测到,所以中断处理程序必须一直存储在内存某段空间之中。中断处理程序入口地址,即中断向量,必须存储在对应表项中。

1、保存用到的寄存器; 2、处理中断 3、恢复用到的寄存器 4、用iret指令返回。

iret就相当于:

pop ip
pop cs
popf