n       8086CPUflag寄存器的结构:

 

 

n       flag13512131415位在8086CPU中没有使用,不具有任何含义。而02467891011位都具有特殊的含义

 

1ZF标志

n       flag的第6位是ZF,零标志位。

   它记录相关指令执行后,

n       结果为0 ZF = 1

n       结果不为0ZF = 0

n        例子:比如:

     mov ax,1

     sub ax,1 

   指令执行后,结果为0,则ZF = 1

        mov ax,2

     sub ax,1 

   指令执行后,结果为1,则ZF = 0

n       对于ZF的值,我们可以这样来看,ZF标记相关指令的计算结果是否为0,如果为0,则在ZF要记录下0”这样的肯定信息。

n       在计算机中1 表示逻辑真,表示肯定,所以当结果为0的时候 ZF=1,表示结果是0 ”。如果结果不为0,则ZF要记录下不是0”这样的否定信息。

n       在计算机中0表示逻辑假,表示否定,所以当结果不为0 的时候ZF=0,表示结果不是0”

 

2PF标志

n       flag的第2位是PF,奇偶标志位。

   它记录指令执行后,结果的所有二进制位中1的个数:

n       为偶数,PF = 1

n       为奇数,PF = 0

n       示例

n       指令:mov al,1

            add al,10

   执行后,结果为00001011B,其中有3(奇数)个1,则PF=0

n       指令:mov al,1

             or al,10

   执行后,结果为00000011B,其中有2(偶数)个1,则PF=1

3SF标志

n       flag的第7位是SF,符号标志位。

   它记录指令执行后,

n       结果为负,SF = 1

n       结果为正,SF = 0

n       有符号数与补码

n       示例

      mov al,10000001B

      add al,1

      结果: (al)=10000010B

解释:

n       我们知道计算机中通常用补码来表示有符号数据。计算机中的一个数据可以看作是有符号数,也可以看成是无符号数。

n       比如:

n       00000001B ,可以看作为无符号数 1 ,或有符号数+1

n       10000001B ,可以看作为无符号数129,也可以看作有符号数-127

   为什么有符号的数就是-`127呢?

   解释:

        1.    将该数的所有位取反(invert),也就是将所有的零改为一,所有的一改为零。

2.    将取反后的数加一(忽略溢出)。

例如,以下就是计算十进制数-58位表示的步骤:

%0000_0101

(二进制)

%1111_1010

所有位取反

%1111_1011

加一,得到−5 (二进制补码表示)

现在对-5再取负,结果就是5%0000_0101),正如我们所预期的一样:

%1111_1011

-5的二进制补码表示

%0000_0100

所有位取反

%0000_0101

加一,得到5 (二进制)

-59的补码表示为:

 

结合上面的例子:

10000001B 如果先声明了为有符号数,那么第一位就是代表符号,此处为1,表示负数,然后将此此数取反+101111111B=64+32+16+8+4+2+1=127,也就是-127

3). 理解有符号数和无符号数

我们所讲的数都是正数。同样是年纪和工资,前者不需要有负值,但后者可能需要——至少所有的老板都这样认为。

那么,负数在计算机中如何表示呢?

这一点,你可能听过两种不同的回答。

一种是教科书,它会告诉你:计算机用补码表示负数。可是有关补码的概念一说就得一节课,这一些我们需要在第6章中用一章的篇幅讲2进制的一切。再者,用补码表示负数,其实一种公式,公式的作用在于告诉你,想得问题的答案,应该如何计算。却并没有告诉你为什么用这个公式就可以和答案? 

另一种是一些程序员告诉你的:用二进制数的最高位表示符号,最高位是0,表示正数,最高位是1,表示负数。这种说法本身没错,可是如果没有下文,那么它就是错的。至少它不能解释,为什么字符类型的-1用二进制表示是“1111 1111”(16进制为FF);而不是我们更能理解的“1000 0001”。(为什么说后者更好理解呢?因为既然说最高位是1时表示负数,那1000 0001不是正好是-1吗?)。

让我们从头说起。

1、你自已决定是否需要有正负。

就像我们必须决定某个量使用整数还是实数,使用多大的范围数一样,我们必须自已决定某个量是否需要正负。如果这个量不会有负值,那么我们可以定它为带正负的类型。

在计算机中,可以区分正负的类型,称为有符类型,无正负的类型(只有正值),称为无符类型。

数值类型分为整型或实型,其中整型又分为无符类型或有符类型,而实型则只有符类型。

字符类型也分为有符和无符类型。

比如有两个量,年龄和库存,我们可以定前者为无符的字符类型,后者定为有符的整数类型。

2、使用二制数中的最高位表示正负。

首先得知道最高位是哪一位?1个字节的类型,如字符类型,最高位是第7位,2个字节的数,最高位是第15位,4个字节的数,最高位是第31位。不同长度的数值类型,其最高位也就不同,但总是最左边的那位(如下示意)。字符类型固定是1个字节,所以最高位总是第7位。

(红色为最高位)

单字节数: 1111 1111

双字节数: 1111 1111 1111 1111

四字节数: 1111 1111 1111 1111 1111 1111 1111 1111

 

当我们指定一个数量是无符号类型时,那么其最高位的10,和其它位一样,用来表示该数的大小。

当我们指定一个数量是无符号类型时,此时,最高数称为符号位。为1时,表示该数为负值,为0时表示为正值。

 

3、无符号数和有符号数的范围区别。

无符号数中,所有的位都用于直接表示该值的大小。有符号数中最高位用于表示正负,所以,当为正值时,该数的最大值就会变小。我们举一个字节的数值对比:

无符号数: 1111 1111   值:255 1* 27 + 1* 26 + 1* 25 + 1* 24 + 1* 23 + 1* 22 + 1* 21 + 1* 20

有符号数: 0111 1111   值:127         1* 26 + 1* 25 + 1* 24 + 1* 23 + 1* 22 + 1* 21 + 1* 20

 

同样是一个字节,无符号数的最大值是255,而有符号数的最大值是127。原因是有符号数中的最高位被挪去表示符号了。并且,我们知道,最高位的权值也是最高的(对于1字节数来说是27次方=128),所以仅仅少于一位,最大值一下子减半。

不过,有符号数的长处是它可以表示负数。因此,虽然它的在最大值缩水了,却在负值的方向出现了伸展。我们仍一个字节的数值对比:

无符号数:                       0 ----------------- 255

有符号数:        -128 --------- 0 ---------- 127

 

同样是一个字节,无符号的最小值是 0 ,而有符号数的最小值是-128。所以二者能表达的不同的数值的个数都一样是256个。只不过前者表达的是0255256个数,后者表达的是-128+127256个数。

一个有符号的数据类型的最小值是如何计算出来的呢?

有符号的数据类型的最大值的计算方法完全和无符号一样,只不过它少了一个最高位(见第3点)。但在负值范围内,数值的计算方法不能直接使用1* 26 + 1* 25 的公式进行转换。在计算机中,负数除为最高位为1以外,还采用补码形式进行表达。所以在计算其值前,需要对补码进行还原。这些内容我们将在第六章中的二进制知识中统一学习。

这里,先直观地看一眼补码的形式:

以我们原有的数学经验,在10进制中:1 表示正1,而加上负号:-1 表示和1相对的负值。

那么,我们会很容易认为在2进制中(1个字节): 0000 0001 表示正1,则高位为1后:1000 0001应该表示-1

然而,事实上计算机中的规定有些相反,请看下表:

 

二进制值(1字节)

十进制值

1000 0000

-128

1000 0001

-127

1000 0010

-126

1000 0011

-125

...

...

1111 1110

-2

1111 1111

-1

 

首先我们看到,从-1-128,其二进制的最高位都是1(表中标为红色),正如我们前面的学。

然后我们有些奇怪地发现,1000 0000 并没有拿来表示 -0;而1000 0001也不是拿来直观地表示-1。事实上,-1 1111 1111来表示。

怎么理解这个问题呢?先得问一句是-1大还是-128大?

当然是 -1 大。-1是最大的负整数。以此对应,计算机中无论是字符类型,或者是整数类型,也无论这个整数是几个字节。它都用全1来表示 -1。比如一个字节的数值中:1111 1111表示-1,那么,1111 1111 - 1 是什么呢?和现实中的计算结果完全一致。1111 1111 - 1 = 1111 1110,而1111 1110就是-2。这样一直减下去,当减到只剩最高位用于表示符号的1以外,其它低位全为0时,就是最小的负值了,在一字节中,最小的负值是1000 0000,也就是-128

 

4).汇编中的确良有符号数和无符号数探讨 
这个问题,要是简单的理解,是很容易的,不过要是考虑的深了,还真有些东西呢。
下面我就把这个东西尽量的扩展一点,深入一点和大家说说。

只有一个标准!

在汇编语言层面,声明变量的时候,没有 signed unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分有符号还是无符号然后用两个标准来处理,它统统当作有符号的!并且统统汇编成补码!也就是说,db -20 汇编后为:EC ,而 db 236 汇编后也为 EC 。这里有一个小问题,思考深入的朋友会发现,db 是分配一个字节,那么一个字节能表示的有符号整数范围是:-128 ~ +127 ,那么 db 236 超过了这一范围,怎么可以?是的,+236 的补码的确超出了一个字节的表示范围,那么拿两个字节(当然更多的字节更好了)是可以装下的,应为:00 EC,也就是说 +236的补码应该是00 EC,一个字节装不下,但是,别忘了截断这个概念,就是说最后汇编的结果被截断了,00 EC 是两个字节,被截断成 EC ,所以,这是个美丽的错误,为什么这么说?因为,当你把 236 当作无符号数时,它汇编后的结果正好也是 EC ,这下皆大欢喜了,虽然汇编器只用一个标准来处理,但是借用了截断这个美丽的错误后,得到的结果是符合两个标准的!也就是说,给你一个字节,你想输入有符号的数,比如 -20 那么汇编后的结果是符合有符号数的;如果你输入 236 那么你肯定当作无符号数来处理了(因为236不在一个字节能表示的有符号数的范围内啊),得到的结果是符合无符号数的。于是给大家一个错觉:汇编器有两套标准,会区分有符号和无符号,然后分别汇编。其实,你们被骗了。:-)

存在两套指令!

第一点说明汇编器只用一个方法把整数字面量汇编成真正的机器数。但并不是说计算机不区分有符号数和无符号数,相反,计算机对有符号和无符号数区分的十分清晰,因为计算机进行某些同样功能的处理时有两套指令作为后备,这就是分别为有符号和无符号数准备的。但是,这里要强调一点,一个数到底是有符号数还是无符号数,计算机并不知道,这是由你来决定的,当你认为你要处理的数是有符号的,那么你就用那一套处理有符号数的指令,当你认为你要处理的数是无符号的,那就用处理无符号数的那一套指令。加减法只有一套指令,因为这一套指令同时适用于有符号和无符号。下面这些指令:mul div movzx … 是处理无符号数的,而这些:imul idiv movsx … 是处理有符号的。
举例来说:
内存里有 一个字节x 为:0x EC ,一个字节 y 为:0x 02 。当把xy当作有符号数来看时,x = -20 y = +2 。当作无符号数看时,x = 236 y = 2 。下面进行加运算,用 add 指令,得到的结果为:0x EE ,那么这个 0x EE 当作有符号数就是:-18 ,无符号数就是 238 。所以,add 一个指令可以适用有符号和无符号两种情况。(呵呵,其实为什么要补码啊,就是为了这个呗,:-)
乘法运算就不行了,必须用两套指令,有符号的情况下用imul 得到的结果是:0x FF D8 就是 -40 。无符号的情况下用 mul ,得到:0x 01 D8 就是 472 (

n       如果在进行有符号数运算时发生溢出,那么运算的结果将不正确。

n       就上面的两个例子来说:
    mov al,98
    add al,99

n       197 1100 0101=128+64+4+1=197

 

n       32+16+8+2+1=

n       7 6 5 4 3 2 1 0

n       0 0 1 1 1 0 1 1

n       1 1 0 0 0 1 0 1

 

n             

n             

n       add指令运算的结果是(al)=0C5H ,因为进行的是有符号数运算,所以 al中存储的是有符号数,而0C5H是有符号数-59的补码

 

 

1.  Je的用法:

 MOV AX,DATAS
    MOV DS,AX
    mov ax,1
    mov bx,1
    cmp ax,bx
    je print

 

2. 为举例方便说一下JNZJZ
   
测试条件
JZ   ZF=1
JNZ  ZF=0
JZJUMP IF ZERO (结果为0则设置ZF零标志为1,跳转)
JNZ
JUMP IF NOT ZERO

 

3. TEST属于逻辑运算指令

功能: 执行BITBIT之间的逻辑运算
   
测试(两操作数作与运算,仅修改标志位,不回送结果).
TEST
对两个参数(目标,源)执行AND逻辑操作,并根据结果设置标志寄存器,结果本身不会保存。TEST AX,BX AND AX,BX 命令有相同效果

语法: TEST R/M,R/M/DATA
影响标志: C,O,P,Z,S(其中CO两个标志会被设为0)

运用举例:
1.TEST
用来测试一个位,例如寄存器:

TEST EAX, 100B;          B
后缀意为二进制
JNZ  ******;            
如果EAX右数第三个位为1,JNZ将会跳转

是这样想的,JNZ跳转的条件是ZF=0,ZF=0意味着ZF(零标志)没被置位,即逻辑与结果

 

2.TEST的一个非常普遍的用法是用来测试一方寄存器是否为空

TEST ECX, ECX
JZ SOMEWHERE

如果ECX为零,设置ZF零标志为1,JZ跳转

 

4.neg求补码

0000 0001

1111 11110+1=1111 1111

 

 

 

 

5.sbb

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

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

n       功能:

   操作对象1=操作对象1–操作对象2–CF

n       比如:sbb ax,bx

   实现功能: (ax) = (ax) – (bx) – CF

 

 

6.adc带进位加法指令

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

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

n       功能:

   操作对象1=操作对象1+操作对象2+CF

n       比如:adc ax,bx 实现的功能是:

   (ax)=(ax)+(bx)+CF

n       adc指令示例(一)

n       mov ax,2
mov bx,1
sub bx,ax
adc ax,l

  执行后,(ax)=4

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

 

n       adc指令示例(三)

n       mov al,98H
add al,al
adc al,3

  执行后,(ax)=34H

  adc执行时,相当于计算: (ax)+3+CF=30H+3+1=34H

n       可以看出,adc指令比add指令多加了一个CF位的值。

   为什么要加上CF的值呢?

   CPU为什么要提供这样一条指令呢?

n       在执行 adc 指令的时候加上的 CF 的值的含义,由 adc指令前面的指令决定的,也就是说,关键在于所加上的CF值是被什么指令设置的。

n       显然,如果CF 的值是被sub指令设置的,那么它的含义就是借位值;如果是被add指令设置的,那么它的含义就是进位值。

 

 

7.cmp指令

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

n       cmp 指令执行后,将对标志寄存器产生影响。

   其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果

n       cmp指令

n       格式:cmp 操作对象1,操作对象2

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

n       比如:cmp ax,ax

   (ax)–(ax)的运算,结果为0,但并不在ax中保存,仅影响flag的相关各位。

   指令执行后:

      ZF=1

      PF=1

      SF=0

      CF=0

      OF=0

 

n       下面的指令:

       mov ax,8

       mov bx,3

       cmp ax,bx

    执行后: (ax)=8

        ZF=0

        PF=1

        SF=0

        CF=0

        OF=0

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

n       cmp ax,bx

 

 

 

 

 

posted on 2009-03-29 16:27  jasonM  阅读(1386)  评论(0编辑  收藏  举报