汇编语言笔记(11)标志寄存器_汇编debug怎么看标志位-程序员宅基地

技术标签: 汇编语言  Powered by 金山文档  开发语言  

标志寄存器

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

(1) 用来存储相关指令的某些执行结果;

(2) 用来为CPU执行相关指令提供行为依据;

(3) 用来控制CPU的相关工作方式。

8086CPU中的标志寄存器有16位,其中存储的信息通常被称为程序状态字(PSW),标志寄存器以下简称为flag。标志位如图:

15  14  13   12   11   10   9    8    7    6    5   4    3    2   1   0
                  OF   DF   IF   TF   SF   ZF       AF        PF      CF

flag的1,3,5,12,13,14,15位在8086CPU中没有使用,不具有任何含义。而0,2,4,6,7,8,9,10,11位都具有特殊的含义。

ZF标志

flag的第6位是ZF,零标志位。它是记录相关指令执行后,其结果是否为0.结果如果为0,那么zf=1;如果结果不为0,那么zf = 0。

比如,指令:

mov ax,1

sub ax, 1

执行后结果为0,则zf = 1.

mov ax,2

sub ax,1

执行后结果不为0,则zf = 0

在计算机中1表示逻辑真,0表示逻辑假,所以,zf = 1结果为0,zf = 0结果不为0.

注意,在8086CPU的指令集中,有的指令的执行是影响标志寄存器的,比如, add, sub, mul, div, inc, or, and等,他们大都是运算指令(进行逻辑或算术运算):有的指令的执行对标志寄存器没有影响,比如,mov, push, pop等,他们大都是传送指令。在使用一条指令的时候,要注意这条指令的全部功能,其中包括,执行结果对标志寄存器的那些标志位造成影响。

PF标志

flag的第2位是PF,奇偶标志位。它记录相关指令执行后,其结果的所有bit位中1的个数是否为偶数。如果1的个数为偶数,pf=1,如果为奇数,那么pf = 0.

比如,指令:

mov al,1

add al,10

执行后结果为0000 1011B,其中3(奇数)个1,则pf = 0;

mov al, 1

or al, 2

执行后,结果为 0000 0011B,其结果有2(偶数)个1,则pf = 1;

SF标志

flag的第7位是SF,符号标志位。它记录相关指令执行后,其结果是否为负。如果结果为负,sf = 1;如果结果非负,sf = 0;

SF标志,就是CPU对有符号数运算结果的一种记录,它记录数据的正负。在我们将数据当作有符号数来运算的时候,可以通过它来得知结果的正负。如果我们将数据当作无符号数来运算,SF的值则没有意义,虽然相关执行影响了它的值。

这也就说,CPU在执行add等指令时,时必然要影响到SF标志位的值的。至于我们需不需要这种影响,

那就看我们如何看待指令所进行的运算了。

比如:

mov al,1000 0001B

add al,1

执行后,结果为1000 0010B,sf = 1,表示:如果指令进行的是有符号数运算,那么结果为负;

mov al,1000 0001B

add al, 0111 1111B

执行后,结果为0,sf = 0,表示:如果指令进行的是有符号数运算,那么结果为非负。

CF标志

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

比如:

mov al,98H

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

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

mov al, 97H

sub al, 98H ; 执行后:(al) = FFH,CF = 1, CF记录了向跟高位的借位值

sub al, al ; 执行后:(al) = 0,CF = 0, CF记录了向更高位的借位值

当两个数据相加的时候,有可能产生从最高有效位向更高位的进位。比如,两个8位数据:98H+98H,将产生进位。由于这个进位值在8位数中无法保存。其实CPU在运算的时候,并不丢弃这个进值位,而是记录在一个特殊的寄存器的某一位上。8086CPU就用flag的CF位来记录这个进位值。

OF标志

flag的第11位是OF,溢出标志位。一般情况下,OF记录了有符号数运算的结果是否发生了溢出。如果发生了溢出,OF = 1,如果没有,OF = 0。

这里的溢出只针对有符号数

比如:

mov al, 98

add al, 99

执行后将产生溢出。因为进行有符号数运算是:

(al) = (al) + 99= 99 + 98 = 197 注意:这里是十进制的数。

197超出了机器所能表示的8位有符号数的范围: -128~127

add执行后:CF = 0,OF= 1,即他没有产生进位,但发生了溢出。进位对于无符号数,溢出对于有符号数。

所以对于机器码进行add或者sub等都会影响OF,CF。至于要使用那种影响要看如何看待这段机器码。

adc指令

adc是带位加法指令,它利用了CF位上记录的进位值 (可以理解位带进位的加法)

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

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

比如指令 adc ax, bx 实现的功能是:(ax) = (ax) + (bx) + CF

例:

mov ax, 2

mov bx, 1

sub bx, ax

adc ax ,1

执行后, (ax) = 4. adc执行时, 相当于计算:(ax) + 1 + CF = 2 + 1 + 1 = 4.

看一下CF的含义。在执行adc指令的时候加上的CF的值的含义,是由adc指令前面的指令决定的,也就是说,关键在于加上的CF值是被什么指令设置的。显然,如果CF的值是被sub指令设置的,那么它的含义就是错位值;如果是被add指令设置的,那么它的含义就是进位值。我们来看一下两个数据:0198H和0183H如何相加的:

01 98

01 83

+

03 1B

可以看出,加法可以分两步进行:1,低位相加;2高位相加再加上低位相加产生的进位值。

下面的指令和adc ax, bx 具有相同的结果:

add al, bl

adc ah,bh

看来CPU提供adc指令的目的,就是来进行加法的第二步运算的。adc指令和add指令相配合就可以对更大的数据进行加法运算。我们来举一个例子:

编程计算1EF000H+201000H,结果放在ax(高16位)和bx(低16位)中。

因为两个数据的位数都大于16,用add指令无法进行计算。我们将计算分两步进行,先将低16位相加,然后将高16位和进位值相加。程序如下。

mov ax , 001EH

mov bx, 0F000H

add bx, 1000H

adc ax, 0020H

adc指令执行后,也可能产生进位值,所以也会对CF位进行设置。由于有这样的功能,我们就可以对任意大的数据进行加法运算。

例如:

编程,计算 1E F000 1000H + 20 1000 1EF0H, 结果放在ax(最高16位),bx(次高16位),cx(低位16位)中。

计算分3步进行:

(1)先将低16位相加, 完成后,CF中记录本次相加的进位值;

(2)再将次高16位和CF(来自低16位的进位制)相加,完成后,CF中记录本次相加的进位值;

(3)最后高16位和CF(来自次高16位的进位值)相加,完成后,CF中记录本次相加的进位值。

程序如下。

mov ax, 001EH

mov bx, 0F000H

mov cx, 1000H

add cx, 1EF0H

adc bx, 1000H

adc ax 0020H

sbb指令

sbb是带借位减法指令,它利用了CF位上记录的借位值。

指令格式:sub操作对象1,操作对象2

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

比如指令 sbb ax, bx 实现的功能是: (ax) = (ax) - (bc) - CF

sbb指令执行后,将对CF进行设置。利用sbb指令可以对任意大的数据进行减法运算。比如,计算003E1000H - 00202000H,结果放在ax,bx中,程序如下:

mov bx, 1000H

mov ax, 003EH

sub bx, 2000H

sbb ax, 0020H

sbb和adc是基于同样的思想设计的两条指令,在应用思路上和adc类似。

cmp指令

cmp是比较指令,cmp的功能相当于减法指令,只是不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。

cmp指令格式:cmp 操作对象 1, 操作对象2

功能:计算操作对象1 -操作对象2但并不保存结果,仅仅根据计算结果对标志寄存器进行设置。

比如,指令cmp ax,ax,做(ax) - (ax)的运算,结果为0,但并不在ax中保存,仅影响flag的相关各位,指令执行后:zf = 1, pf = 1, sf = 0, cf = 0, of = 0.

下面指令:

mov ax, 8

mov bx,3

执行后:(ax) = 8, zf = 0, pf = 1, sf = 0, cf = 0, of = 0.

其实,我们通过cmp指令执行后,相关标志位的值就可以看出比较的结果。

无符号数比较

cmp ax, bx

如果(ax) = (bx) 则(ax) - (bx) = 0,所以:zf = 1;

如果(ax)(bx) 则(ax) - (bx) = 0,所以:zf = 0;

如果(ax)<(bx) 则(ax) - (bx)将产生借位,所以:zf = 1;

如果(ax)(bx)则 (ax) - (bx)不必借位,所以:cf = 0;

如果(ax)>(bx)则(ax) - (bx)既不必借位,结果又不为0,所以:cf = 0并且zf = 0;

如果(ax)(bx) 则(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)

有符号数比较

cmo ah, bh

zf = 1,说明(ah)=(bh)

zf = 0,说明(ah) (bh)

sf = 1&& of = 0 (ah) < (bh)

// of = 0,说明没有溢出,逻辑上真正结果正负=实际结果正负;

sf = 1, 而of = 1 (ah) > (bh)

//of = 1, 说明有溢出,如果因为溢出导致了结果为负,那么逻辑上真正的结果必然为正

sf = 0, of = 1 (ah) < (bh)

//of = 1, 说明有溢出,如果因为溢出导致了实际结果为正,那么逻辑上真正的结果必然为负。

sf = 0, of = 0 (ah)≥(bh)

单纯地考查sf的值不可能知道结果的正负。因为sf记录的是ah中的8位二进制信息所表示的数据的正负。cmp, ah, bh执行后,sf记录的是(ah) - (bh)所得到的8位结果数据的正负。

虽然这个结果没有在我们能够使用的寄存器或内存单元中保存,但是在指令执行的过程中,它暂存在CPU内部的暂存器中。

所得到的相应结果的正负,并不能说明,运算所应该得到的结果的正负。这是因为在运算的过程中可能发生溢出。如果有这种情况发生,那么,sf的值就不能说明任何问题。比如:

mov ah, 22H

mov bh, 0A0H

sub ah, bh

结果 sf = 1,运算实际得到的结果是(ah) = 82H,但是在逻辑上,运算所应该得到的结果是:34 -(-96) = 130. 就是因为130这个结果作为一个有符号数超出了-128~127这个范围,在ah中不能表示,而ah中的结果被CPU当作有符号数解释位-126。而sf被用来记录这个实际结果的正负,所以sf = 1。但是sf = 1不能说明在逻辑上,运算所得到的正确结果的正负。

又比如:

mov ah 08AH

mov bh,070H

cmp ah,bh

结果 sf = 0,运算(ah) - (bh)实际得到的结果是1AH,但是在逻辑上,运算所应该得到的结果是:(-118) - 112 = -230. sf记录实际结果的正负,所以sf = 0.但sf = 0不能说明在逻辑上,运算所得到的正确结果。

所以。我们应该在考察sf(得知实际结果的正负)的同时考查of(得知有没有溢出),就可以得知逻辑上真正结果的正负,同时就可以知道比较的结果。

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

因为cmp指令可以同时进行两种比较,无符号数比较和有符号数比较,所以根据cmp指令的比较结果进行转移的指令也分为两种,即根据无符号数的比较结果进行转移的条件转移指令(他们检测zf,cf的值)和根据有符号数的比较结果进行转移的条件转移指令(他们检测sf,of和zf的值)

下面是常用的根据无符号数的比较结果进行转移的条件转移指令

指令 含义 检测的相关标志位

jcxz cx值为0则转移 cx = 0

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

ne:表示not equal

b:表示below

nb:表示not below

a:表示above

na:表示not above

比如:

编程,统计data段中数值为8的字节的个数,用ax保存统计结果

data段的8个字节如下:

data segment

db 8, 11, 8, 1, 8, 5, 63,38

data ends

程序如下:

    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
    这个程序也可以这样写:
    mov ax, data
    mov ds, ax
    mov bx, 0                                      ;ds:bx指向第一个字节
    mov ax, 0                                      ;初始化累加器
    mov cx , 8
    s: cmp byte ptr [bx], 8                   ;和8进行比较
    je ok                                               ;如果相等转到ok
    jmp short next                               ;如果不相等就转到next。继续循环
    ok:inc ax                                         ;如果相等就将计数值+1
    next:inc bx
    loop s

比起第一个程序,他直接遵循了“等于8则计数+1”的原则,用je指令检测等于8的情况,但是没有第一个程序精简。第一个程序用jne检测不等于8的情况,从而间接检测等于8的情况。要注意在使用cmp和条件转移指令时的这种编程思想。

DF标志和串传送指令

flag的第10位是DF,方向标志位。在串处理指令中,控制每次操作后si,di的增减。

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

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

串传送指令

格式:movsb

功能:执行movsb指令相当于进行下面几步操作。

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

(2)如果df = 0则:(si) = (si) + 1

(di) = (di) + 1

如果df = 1则:(si) = (si) - 1

(di) = (di) - 1

movsb的功能是将ds:si指向的内存单元中的字节送入es:di中,然后根据标志寄存器df位的值,将si和di递增或递减

格式:movsw

movsw的功能是将ds:si指向的内存单元中的字送入es:di中,然后根据标志寄存器df位的值,将si和di递增2或递减2

movsb和movsw进行的是串传送操作中的一个步骤,一般来说,movsb和movsw都和rep配合使用,格式如下:

rep movsb

用汇编语法来描述rep movsb的功能就是:

s:movsb

loop s

可见,rep的作用是根据cx的值,重复执行后面的串传送指令。由于每执行一次movsb指令si和di都会递增或递减指向后一个单元或前一个单元,则rep movsb就可以循环实现(cx)个字符的传送。

由于flag的df位决定着串传送指令执行后,si和di改变的方向,所以CPU应该提供相应的指令来对df位指令进行设置,从而是程序员能够决定传送的方向。

8086CPU提供下面两条执行对df位进行设置。

cld指令:将标志寄存器的df位置0

std指令:将标志寄存器的df位置1

编程,用串传送指令,将data段中的第一个字符串复制到它后面的空间中。
data segment
    db 'Welcome to masm!'
    db  16 dup(0)
data ends
我们分析一下一下,使用串传送指令进行数据的传送,需要给它提供一些必要的信息,它们是:
1 传送的原始位置:ds:si;
2 传送的目的位置:es:di;
3 传送的长度:cx;
4 传送的方向:df;
在这个问题中,这些信息如下。
1 传送的原始位置:data:0;
2 传送的目的位置:data:0010;
3 传送的长度:16;
4 传送的方向:因为正方向传送(每次串传送指令执行后,si和di递增)比较方便,所以设置df = 0。

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次
cld                          ;设置df = 0, 正向传送
rep movsb

pushf 和 popf

pushf的功能是将标志寄存器的值压栈,而popf是从栈中弹出数据,送入标志寄存器中。

pushf和popf,为直接访问标志寄存器提供了一种方法。

标志寄存器在Debug中的表示

在Debug中,标志寄存器是按照有意义的各个标志位单独表示的。在Debug中,我们可以看到下面的信息。

下面列出Debug对我们已知的标志位的表示。

标志 值为1的标记 值为0的标记

of OV NV

sf NG PL

zf ZR NZ

pf PE PO

cf CY NC

df DN UP

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_62750704/article/details/128705165

智能推荐

Tessent scan & ATPG (1)scan chain基本原理_tessent lpct-程序员宅基地

文章浏览阅读1.6w次,点赞27次,收藏193次。从制造缺陷开始,介绍scan chain的基本原理_tessent lpct

创建DOTA2本地数据库(一)-程序员宅基地

文章浏览阅读222次。在APP中,用本地数据库好于频繁的联网去获取相关数据。我使用SQLite作为本地的数据库,比较轻巧。英雄首先先建立英雄的数据库,暂时我先只设置ID,name,loaclized_name这三种,我这是在窗体应用程序里写的,不要在意那些细节,其实我们只是要先创建这么一个.db文件 public Form1() { I...

响应式编程库Reactor 3 Reference Guide参考文档中文版(v3.2.0)-程序员宅基地

文章浏览阅读231次。Project Reactor 是 Spring WebFlux 的御用响应式编程库,与 Spring 是兄弟项目。关于如何基于Spring的组件进行响应式应用的开发,欢迎阅读系列文章《响应式Spring的道法术器》。官方参考文档地址:http://projectreactor.io/docs/core/release/reference/中文翻译文档地址:http://htmlprevi..._reactor 指南中文版v3.0

TeamTalk部署问题及解决方案_teamtalk ../daeml: 没有那个文件或目录-程序员宅基地

文章浏览阅读727次。TeamTalk部署问题及解决方案1、部分源下载地址2、编译安装libiconv报错3、找不到tt4、编译im-server5、缺少daeml6、找不到mysql.h7、centos7 mini 安装后无法连接到网络8、使用mwget提高下载速度9、nginx: [emerg] unknown log format "access" in错误解决方法10、PHP报错1、部分源下载地址gmpwget ftp://ftp.gnu.org/gnu/gmp/gmp-5.1.3.tar.gzmpfrwge_teamtalk ../daeml: 没有那个文件或目录

java中将多文件字节流压缩成zip-程序员宅基地

文章浏览阅读2.4k次。java中将多文件字节流压缩成zip核心就是使用java.util.zip包中的ZipOutputStream直接上核心代码/** * * @param zipFilePath zip保存路径 * @param zipFileName zip文件名 * @param byteList 文件字节码Map,k:fileNam..._java将多个字节流压缩成zip

Python-Django毕业设计在线考试系统(程序+Lw)_django在线考试系统-程序员宅基地

文章浏览阅读141次。该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程项目运行环境配置:Pychram社区版+ python3.7.7 + Mysql5.7 + HBuilderX+list pip+Navicat11+Django+nodejs。项目技术:django + python+ Vue 等等组成,B/S模式 +pychram管理等等。环境需要1.运行环境:最好是python3.7.7,我们在这个版本上开发的。其他版本理论上也可以。2.pycharm环境:pycharm都可以。_django在线考试系统

随便推点

Config配置搜索路径_configservices路径-程序员宅基地

文章浏览阅读812次。前面所有的GIT远程端配置文件都放在是根目录的,所有请求默认都是根目录,但是有时候,项目很多,配置文件需要根据子目录来划分,这时候,就需要来配置搜索路径了;比如aaa项目的配置文件放aaa目录下,bbb项目的配置文件放bbb目录下,不配置的话是找不到的那些配置文件的,需要配置search-paths属性来实现在microservice-config-server-4001项目的yml文件加个配..._configservices路径

c++笔记-程序员宅基地

文章浏览阅读62次。多行代码的函数:std::cout << R"()";

【产品】PRD需求文档:云迹扶教(公益支教APP)_公益类app用户画像描述-程序员宅基地

文章浏览阅读2.2k次。简介:借助CSDN平台发布自己编写的需求文档。参考了前辈们的经验,编写如下(持续更新):正文:PRD:云迹扶教APP产品需求文档一、文档综述1.1版本历史 制订时间 制订人 制订内容 2021.06.13 Max 撰写文档 1.2输出环境 文档名称 云迹扶教A_公益类app用户画像描述

std::string转QString中文乱码_c++ 中文 string 转qstring乱码-程序员宅基地

文章浏览阅读2k次,点赞2次,收藏8次。std::string cstring;QString qstring;//从std::string 到QStringqstring = QString(QString::fromLocal8Bit(cstring.c_str()));//从QString 到 std::stringcstring = std::string((const char *)qstring.toLocal8Bit().constData());_c++ 中文 string 转qstring乱码

html符号中文含义大全特殊,中文标点符号大全名称-程序员宅基地

文章浏览阅读2.6k次。中文的标点符号包括句号,逗号,感叹号,问号,引号,冒号等等,接下来分享常见的中文标点符号名称。常见的中文标点符号1.句号:【。】用于句子末尾,表示陈述语气。有时也可表示较缓和的祈使语气和感叹语气。2.问号:【?】用于句子末尾,表示疑问语气(包括反问、设问等疑问类型)。在多个问句连用或表达疑问语气加重时,可叠用问号。3.叹号:【!】用于句子末尾,主要表示感叹语气,有时也可表示强烈的祈使语气、反问语气..._html顿号

【加密算法】5 种常见的摘要、加密算法_摘要加密-程序员宅基地

文章浏览阅读994次。那么上面提到的这些能力,我们都可以利用哪些加密算法来实现呢?咱们接着往下看。加密算法整体上可以分为:不可逆加密、可逆加密。可逆加密又可以分为:对称加密、非对称加密。_摘要加密