8086CPU的标志寄存器有16位,其中存储的信息通常被成为程序状态字(PSW)。标志寄存器和其他寄存器是不一样的,其他寄存器是用来存放数据的,而flag寄存器是按位起作用的,它每一位都有专门的含义,记录特定的信息,其结构如下图所示。

§ 第11章 标志寄存器 - 图1

从上述图中可以看到标志寄存器中的0,2,4,6,7,8,9,10,11都是有特定含义的,下面就学习一下寄存器中的标志位和其相关的典型指令。

11.1 零标志位ZF

标志寄存器的第6位是零标志位ZF,表示相关指令执行后的结果是否为0,如果结果为0,则ZF=1;如果执行结果不为0,则ZF=0。

比如,指令

  1. mov ax,1
  2. sub ax,1

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

注意:在8086CPU的指令集中,有些指令的执行是会影响标志寄存器的,比如addsubmulincorand等,这些指令大多都是运算指令;而像movpushpop等这样的传送指令的执行对标志寄存器没有影响。

11.2 奇偶标志位PF

标志寄存器的第2位是奇偶标志位PF,记录相关指令执行后,其结果的所有位中1的个数是否为偶数。如果1的个数为偶数,则PF=1;如果为奇数,那么PF=0。

比如,指令

  1. mov al,1
  2. add al,10

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

  1. mov al,1
  2. or al,2

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

11.3 符号标志位SF

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

计算机通常用补码来表示有符号数据,计算机中的一个数据可以被看成是有符号数或者无符号数,例如

  1. 00000001B ; 有符号数+1 无符号数1
  2. 10000001B ; 有符号数-127 无符号数129

同样,计算机可以将一个二进制数据当作无符号数据计算,也可以当作有符号数据计算。例如

  1. mov al,10000001B
  2. add al,1

结果为:(al) = 10000010B

可以将add指令进行的运算当作无符号数的运算,那么add指令相当于计算§ 第11章 标志寄存器 - 图2,结果为130(10000010B);也可以当作有符号数的运算,那么add指令相当于计算§ 第11章 标志寄存器 - 图3,结果为§ 第11章 标志寄存器 - 图4(1000010B).

上述的CPU的运算默认是有两层含义的

  1. 有符号运算:使用符号标志位SF来记录数据的政府
  2. 无符号运算:符号标志为SF没有用,但是计算结果还是会影响它的值

某些指令会影响标志寄存器中的多个标志位,这些标志为能够较为全面地记录指令的执行结果,为相关的处理提供了所需的依据,例如

  1. sub al,al
  2. ; 执行后:ZF=1PF=1SF=0(虽然没什么用)

11.4 进/借位标志位CF

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

对于位数为N的无符号数来说,其对应的二进制信息的最高位,即第§ 第11章 标志寄存器 - 图5位,就是它的最高有效位,而假想存在的第N位,就是相对于最高有效位的更高位,如图11.2所示。

§ 第11章 标志寄存器 - 图6

§ 第11章 标志寄存器 - 图7 之前我们进行相加运算的时候,有可能产生从最高有效位向更高位的进位。比如,两个8位数据相加 § 第11章 标志寄存器 - 图8 就会产生进位。由于这个进位值在8位数中无法保存,我们在前面的课程中,就只是简单地说这个进位值丢失了。其实CPU在运算的时候,将这个值记录在了标志寄存器的进位标志位CF上。

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

§ 第11章 标志寄存器 - 图9 当两个数据做减法的时候,有可能向更高位借位。比如,连个数据相减 § 第11章 标志寄存器 - 图10,将产生借位。借位后,相当于计算 § 第11章 标志寄存器 - 图11,而标志寄存器的也可用来记录这个借位值

  1. mov al,97H
  2. sub al,98H ; 执行后,(al)=FFH,CF=1,由于借位1所以记录1
  3. sub al,al ; 执行后,(al)=0,CF=0,由于没有借位所以记录0

11.5 溢出标志位OF

当进行有符号数运算的时候,如果超过了机器所能表示的范围成为溢出,机器所能表示的范围是什么呢?

  • 当我们使用8位寄存器存放指令运算的结果的时候,对于8位有符号数据,机器所能表示的范围就是-128~127
  • 当我们使用16位寄存器存放指令运算的结果的时候,对于16位有符号数据,机器所能表示的范围就是-32768~32767

下面看关于溢出的例子

  1. ; 例子1:正数溢出
  2. mov al,98
  3. add al,99
  4. ; 执行后将会造成溢出
  5. ; 99+98=197,而197超过了8位有符号数的表示范围:-128~127
  6. ; 例子2:负数溢出
  7. mov al,0F0H ; F0H为有符号数-16的补码
  8. add al,088H ; 88H为有符号数-120的补码
  9. ; 执行后,将产生溢出
  10. ; -16-120=-136,而-136超出了机器所能表示的范围:-128~127

我们使用add指令计算出的结果为(al)=0C5H,而这样的运算结果所能表示的可能有两种

  • 正数:197
  • 负数:因为我们使用补码表示的,所以实际负数为-59,但是这个结果显然不可能是真实的,所以这个是错的§ 第11章 标志寄存器 - 图12

还记得上面的进位标志位SF,上面是记录是否进位的,而进位是不是就相当于变相的溢出呢?对于无符号运算,其范围是0~256;而对于有符号运算,其范围是-127~128。

§ 第11章 标志寄存器 - 图13

  1. mov al,98
  2. mov al,99
  3. ; 无符号数:add指令后98+99=197 < 256,所以不需要进位
  4. ; 有符号数:add指令后98+99=197 > 128,所以需要溢出

11.6 带位加法指令adc

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

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

功能:操作对象1=操作对象1+操作对象2+进位对象CF,比如adc ax,bx实现的功能是:(ax)=(ax)+(bx)+CF

例如

  1. ; 例子1
  2. mov ax,2
  3. mov bx,1
  4. sub bx,ax ; bx-ax需要借位,所以CF=1
  5. adc ax,1
  6. ; 执行后,(ax)=4,相当于计算(ax)+1+CF = 2+1+1 = 4
  7. ; 例子2
  8. mov ax,1
  9. add ax,ax
  10. adc ax,3
  11. ; 执行后,(ax)=5,相当于计算(ax)+3+CF = 2+3+0 = 5
  12. ; 例子3
  13. mov al,98H
  14. add al,al ; al+al产生借位,所以CF=1
  15. adc al,3
  16. ; 执行后,(al)=34H,相当于计算(al)+3+CF = 30H+3+1 = 34H

相比于add指令,adc多加了一个CF位的值,为什么要加上CF的值呢?CPU为什么要提供这样一条指令呢?

先来看一下CF的值的含义,在执行adc指令的时候加上的CF的值的含义是由adc指令前面的指令决定的。

  • 如果CF的值是被sub指令设置的,那么它的含义就是借位值
  • 如果CF的值是被add指令设置的,那么它的含义就是进位值

现在我们来看一下数据0198H和0183H是如何相加的?

§ 第11章 标志寄存器 - 图14

可以看出,加法分为两步进行的

  1. 低位相加
  2. 高位相加再加上低位相加产生的进位值

而上面的步骤add ax,bx可以用下面的指令表示

  1. add al,bl
  2. adc ah,bh

§ 第11章 标志寄存器 - 图15 现在来看CPU提供adc指令的目的就是来进行加法的第二步运算的,**adc**指令和**add**指令相配合就可以对更大的数据进行加法运算。我们现在看一个例子:

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

因为两个数据的位数都大于16,用add指令无法进行计算。我们将计算分为两步进行

  1. 先将低16位相加
  2. 将高16位和进位值相加

如下所示

  1. mov ax,001EH
  2. mov bx,0F000H
  3. add bx,1000H
  4. adc ax,0020H

§ 第11章 标志寄存器 - 图16 adc指令执行后,也可能产生进位值,所以也会对CF位进行设置。由于有这样的功能,我们就可以对任意大的数据进行加法运算,看下面这个例子:

编程,计算1EF0001000H+2010001EF0H,结果放在ax(最高16位)和bx(次高16位)和cx(低16位)中。

计算分为三步进行:

  1. 先将低16位相加,完成后,CF中记录本次相加的进位值
  2. 再将次高16位和CF(来自低16位的进位值)相加,完成后,CF中记录本次相加的进位值
  3. 最后高16位和CF(来自次高16位的进位值)相加,完成后,CF中记录本次相加的进位值

程序如下

  1. mov ax,001EH
  2. mov bx,0F000H
  3. mov cx,1000H
  4. add cx,1EF0H
  5. adc bx,1000H
  6. adc ax,0020H

§ 第11章 标志寄存器 - 图17 下面编写一个子程序,对两个128位数据进行相加

  • 名称:add128
  • 功能:两个128位数据进行相加
  • 参数:ds:si指向存储第一个数的内存空间,因数据为128位,所以需要8个子单元,由低地质单元到高地质单元依次存放128位数据由低到高的各个字。运算结果存储在第1个数的存储空间中。ds:di指向存储第二个数的内存空间。

程序如下:

  1. add128:push ax
  2. push cx
  3. push si
  4. push di
  5. sub ax,ax ; CF设置为0
  6. mov cx,8
  7. s:mov ax,[si]
  8. adc ax,[di]
  9. mov [si],ax
  10. inc si
  11. inc si
  12. inc di
  13. inc di
  14. loop s
  15. pop di
  16. pop si
  17. pop cx
  18. pop ax
  19. ret

incloop指令不影响CF位,思考一下,上面的程序中能不能将4个inc指令,用

  1. add si,2
  2. add di,2

来取代?

11.7 带借位减法指令sbb

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

  • 指令格式:sbb 操作对象1, 操作对象2
  • 功能:操作对象1 = 操作对象1 - 操作对象2 - CF
  • 比如指令sbb ax, bx实现的功能是:(ax) = (ax) - (bx) - CF

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

  1. mov bx,1000H
  2. mov ax,003EH
  3. sub bx,2000H ; 借位标识符CF=1
  4. sbb ax,0020H ; 003E - 0020 - 1

sbbadc非常类似,所以不用过多关注。

11.8 比较指令cmp

cmp是比较指令,cmp的功能相当于减法指令,但是不保存结果。cmp指令执行后,将对标志寄存器产生影响。

  • cmp指令格式:cmp 操作对象1, 操作对象2
  • 功能:计算 操作对象1 - 操作对象2但并不保存结果,仅仅根据计算结果对标志寄存器进行设置。
  • 比如:指令cmp ax, ax相当于进行(ax) - (ax)的运算,结果为0,但并不在ax中保存,仅仅影响标志寄存器中的标志位。执行后,ZF=1, PF=1, SF=0, CF=0, OF=0

下面的指令

  1. mov ax,8
  2. mov bx,3
  3. cmp ax,ax

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

§ 第11章 标志寄存器 - 图18 通过cmp指令执行后的相关标志位的值就可以知道两个数的关系,例如(ax)和(bx)

cmp ax,bx

  • 如果§ 第11章 标志寄存器 - 图19%3D(bx)#card=math&code=%28ax%29%3D%28bx%29&id=wkDLI),则§ 第11章 标志寄存器 - 图20-(bx)%3D0#card=math&code=%28ax%29-%28bx%29%3D0&id=nhpaK),所以:ZF=1
  • 如果§ 第11章 标志寄存器 - 图21%5Cne(bx)#card=math&code=%28ax%29%5Cne%28bx%29&id=WuzP2),则§ 第11章 标志寄存器 - 图22-(bx)%5Cne0#card=math&code=%28ax%29-%28bx%29%5Cne0&id=gfUtz),所以:ZF=0
  • 如果§ 第11章 标志寄存器 - 图23%3C(bx)#card=math&code=%28ax%29%3C%28bx%29&id=jUkyX),则§ 第11章 标志寄存器 - 图24-(bx)#card=math&code=%28ax%29-%28bx%29&id=l3xpP)将产生借位,所以:CF=1
  • 如果§ 第11章 标志寄存器 - 图25%5Cgeqslant(bx)#card=math&code=%28ax%29%5Cgeqslant%28bx%29&id=Jdzi9),则§ 第11章 标志寄存器 - 图26-(bx)#card=math&code=%28ax%29-%28bx%29&id=eshAF)不必借位,所以:CF=0
  • 如果§ 第11章 标志寄存器 - 图27%3E(bx)#card=math&code=%28ax%29%3E%28bx%29&id=nLsnD),则§ 第11章 标志寄存器 - 图28-(bx)#card=math&code=%28ax%29-%28bx%29&id=ieHam)既不必借位,结果也不为0,所以:CF=0 并且 ZF=0
  • 如果§ 第11章 标志寄存器 - 图29%5Cgeqslant(bx)#card=math&code=%28ax%29%5Cgeqslant%28bx%29&id=yKMmb),则§ 第11章 标志寄存器 - 图30-(bx)#card=math&code=%28ax%29-%28bx%29&id=YsRR4)既可能借位,结果可能为0,所以:CF=1 或 ZF=1

§ 第11章 标志寄存器 - 图31 指令cmp ax,bx的逻辑含义是比较ax和bx中的值,如果执行后:

  • ZF=1,说明§ 第11章 标志寄存器 - 图32%3D(bx)#card=math&code=%28ax%29%3D%28bx%29&id=Piy9g)
  • ZF=0,说明§ 第11章 标志寄存器 - 图33%5Cne(bx)#card=math&code=%28ax%29%5Cne%28bx%29&id=QSODC)
  • CF=1,说明§ 第11章 标志寄存器 - 图34%3C(bx)#card=math&code=%28ax%29%3C%28bx%29&id=z8wbG)
  • CF=0,说明§ 第11章 标志寄存器 - 图35%5Cgeqslant(bx)#card=math&code=%28ax%29%5Cgeqslant%28bx%29&id=UzWR6)
  • CF=0 并且 ZF=1,说明§ 第11章 标志寄存器 - 图36%3E(bx)#card=math&code=%28ax%29%3E%28bx%29&id=nnDdS)
  • CF=1 或 ZF=1,说明§ 第11章 标志寄存器 - 图37%5Cleqslant(bx)#card=math&code=%28ax%29%5Cleqslant%28bx%29&id=BSStC)

§ 第11章 标志寄存器 - 图38addsub指令一样,CPU在执行cmp指令的时候,包含两种含义

  • 进行无符号运算:对无符号数进行比较
  • 进行有符号运算:对有符号数进行比较

接下来看的是如何对有符号数进行比较,并且CPU使用哪些标志位对结果进行记录,以cmp ah,bh为例进行说明

  1. cmp ah,bh

§ 第11章 标志寄存器 - 图39 有两种结果

  • 如果§ 第11章 标志寄存器 - 图40%3D(bh)#card=math&code=%28ah%29%3D%28bh%29&id=jv83E),则§ 第11章 标志寄存器 - 图41-(bh)%3D0#card=math&code=%28ah%29-%28bh%29%3D0&id=zvTai),所以ZF=1;
  • 如果§ 第11章 标志寄存器 - 图42%5Cne%20(bh)#card=math&code=%28ah%29%5Cne%20%28bh%29&id=o28Vm),则§ 第11章 标志寄存器 - 图43-(bh)%5Cne0#card=math&code=%28ah%29-%28bh%29%5Cne0&id=FwEOX),所以ZF=0.所以,根据cmp指令执行后ZF的值就可以知道两个数是否相等

§ 第11章 标志寄存器 - 图44 如果§ 第11章 标志寄存器 - 图45%3C(bh)#card=math&code=%28ah%29%3C%28bh%29&id=Ejcyp)可能会发生什么呢?

对于有符号数运算,在§ 第11章 标志寄存器 - 图46%3C(bh)#card=math&code=%28ah%29%3C%28bh%29&id=sIGiM)情况下,§ 第11章 标志寄存器 - 图47-(bh)#card=math&code=%28ah%29-%28bh%29&id=l7fFq)显然可能引起SF=1,即结果为负。那么如果SF=1,是否就能说明§ 第11章 标志寄存器 - 图48%3C(bh)#card=math&code=%28ah%29%3C%28bh%29&id=VBjvm)呢?

当然不是,我们再看两个例子

(ah) = 22H

(bh) = 0A0H

则(ah)-(bh) = 34-(-96) = 82H,82H是-126的补码,所以SF=1。**这里虽然SF=1,但是并不能说明(ah)<(bh),因为显然34>-96。

两个有符号数单纯利用ZF来判断就不可以了。

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

转移指的是它能够修改IP,而条件指的是它可以根据某种条件决定是否修改IP,下面是常用的根据无符号数的比较结果进行转移的条件转移指令

§ 第11章 标志寄存器 - 图49

另外,像jcxz就是一个条件转移指令,它可以检测cx中的数值,如果(cx)=0,就修改IP,否则什么也不做。所有条件转移指令的转移位移都是[-128, 127]。除了jcxz指令外,CPU提供的其他条件转移指令大多与cmp指令相互配合使用,就像是上面的jn,jne等。

例子,编程实现如下功能:

如果§ 第11章 标志寄存器 - 图50%20%3D%20(bh)#card=math&code=%28ah%29%20%3D%20%28bh%29&id=FmMKc)则§ 第11章 标志寄存器 - 图51%3D(ah)%2Bah#card=math&code=%28ah%29%3D%28ah%29%2Bah&id=sFPee),否则§ 第11章 标志寄存器 - 图52%3D(ah)%2B(bh)#card=math&code=%28ah%29%3D%28ah%29%2B%28bh%29&id=qQCtc)

  1. cmp ah,bh ; 如果(ah)=(bh),则ZF=1
  2. je s ; 检测ZF是否为1,如果为1跳转到s标号处执行程序,下面两行将被忽略
  3. add ah,bh
  4. jmp short ok
  5. s:add ah, ah
  6. ok:...

之前,我们也学习过影响零标志位ZF的指令有很多,像add指令,如下所示

  1. mov ax,0
  2. add ax,0
  3. je s
  4. inc ax
  5. s:inc ax

所以一定要主意好这个因素,别让其它指令干扰了你想要的效果!

下面看一下条件转移指令在编程中的用法。我们看下面的一组程序,data段中的8个字节如下所示

  1. data segment
  2. db 8,11,8,1,8,5,63,38
  3. data ends

§ 第11章 标志寄存器 - 图53 问题一:编程,统计data段中数值为8的字节的个数,用ax保存统计结果。

编程思路:初始设置§ 第11章 标志寄存器 - 图54%3D0#card=math&code=%28ax%29%3D0&id=xLBQh),然后用循环依次比较每个字节的值,找到一个和8相等的数就将ax的值加1,程序如下

  1. mov ax,data
  2. mov ds,ax
  3. mov bx,0 ; ds:bx指向第一个字节
  4. mov ax,0 ; 初始化累加器
  5. mov cx,8
  6. s:cmp byte ptr [bx],8 ; 8进行比较
  7. jne next ; 如果不相等就跳转到next,继续循环
  8. inc ax ; 如果相等就将计数值加1
  9. next:inc bx
  10. loop s ; 程序执行后:(ax)=3

§ 第11章 标志寄存器 - 图55 问题二:编程,统计data段中数值大于8的字节的个数,用ax保存统计结果

编程思路:初始设置§ 第11章 标志寄存器 - 图56%3D0#card=math&code=%28ax%29%3D0&id=NU2Ok),然后用循环一次比较每个字节的值,找到一个大于8的就将ax的值加1,程序如下。

  1. mov ax,data
  2. mov ds,ax
  3. mov ax,0 ; 初始化累加器
  4. mov bx,0 ; ds:bx指向第一个字节
  5. mov cx,8
  6. s: cmp byte ptr [bx],8 ; 8进行比较
  7. jna next ; 如果不大于8就转到next,继续循环
  8. inc ax ; 如果大于8就将计数值加1
  9. next: inc bx
  10. loop s

程序执行后:§ 第11章 标志寄存器 - 图57%3D3#card=math&code=%28ax%29%3D3&id=oQ3tL)

§ 第11章 标志寄存器 - 图58 问题三:编程,统计data段中数值小于8的字节的个数,用ax保存统计结果

编程思路:初始设置§ 第11章 标志寄存器 - 图59%3D0#card=math&code=%28ax%29%3D0&id=aBj7i),然后用循环一次比较每个字节的值,找到一个小于8的就将ax的值加1,程序如下。

  1. mov ax,data
  2. mov ds,ax
  3. mov ax,0 ; 初始化累加器
  4. mov bx,0 ; ds:bx指向第一个字节
  5. mov cx,8
  6. s: cmp byte ptr [bx],8 ; 8进行比较
  7. jnb next ; 如果不小于于8就转到next,继续循环
  8. inc ax ; 如果小于8就将计数值加1
  9. next: inc bx
  10. loop s

程序执行后:§ 第11章 标志寄存器 - 图60%3D2#card=math&code=%28ax%29%3D2&id=yOWix)

11.10 方向标志位DF和串传送指令

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

  • § 第11章 标志寄存器 - 图61:每次操作后si和di递增
  • § 第11章 标志寄存器 - 图62:每次操作后si和di递减

我们来看下面的一个串传送指令

串传送指令movsb

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

  1. § 第11章 标志寄存器 - 图6316%20%2B%20(di))%20%3D%20((ds)16%2B(si))#card=math&code=%28%28es%29%2A16%20%2B%20%28di%29%29%20%3D%20%28%28ds%29%2A16%2B%28si%29%29&id=cnb62)
  2. 如果§ 第11章 标志寄存器 - 图64,则§ 第11章 标志寄存器 - 图65%3D(si)%2B1#card=math&code=%28si%29%3D%28si%29%2B1&id=nAhrr),§ 第11章 标志寄存器 - 图66%3D(di)%2B1#card=math&code=%28di%29%3D%28di%29%2B1&id=ewM0B);否则递减

用汇编语法描述movsb的功能如下

  1. mov es:[di],byte ptr ds:[si] ; 8086并不支持这个指令,这里只是一个描述
  2. ; 如果df=0
  3. inc si
  4. inc di
  5. ; 如果df=1
  6. dec si
  7. dec si

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

串传送指令movsw

**movsb**是将字节进行传送,**movsw**则是将字进行传送movsb的功能是将 ds:si 指向的内存单元中的字送入 es:di 中,然后根据标志寄存器DF的值,将si和di递增2或递减2。

用汇编语法描述movsw的功能如下

  1. mov es:[di],word ptr ds:[si] ; 8086并不支持这个指令,这里只是一个描述
  2. ; 如果df=0
  3. add si,2
  4. add di,2
  5. ; 如果df=1
  6. sub si,2
  7. sub si,2

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

  1. rep movsb

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

  1. s:movsb
  2. loop s

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

改变方向标志位DF

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

8086CPU提供下面两条指令对DF位进行设置

  • cld指令:将标志寄存器的方向标志位DF置0
  • std指令:将标志寄存器的方向标志位DF置1

下面看两个程序

§ 第11章 标志寄存器 - 图67 问题一:编程,使用串传送指令,将data段中的第一个字符串复制到它后面的空间中

  1. data segment
  2. db 'Welcom to masm'
  3. db 16 dup (0)
  4. data ends

我们分析一下,使用串传送指令进行数据的传送,需要给它提供一些必要的信息,他们是

  1. 传送的原始位置:ds:si
  2. 传送的目的位置:es:di
  3. 传送的长度:cx
  4. 传送的方向:df

在这个问题中,这些信息如下

  1. 传送的原始位置:data:0
  2. 传送的目的位置:data:0010
  3. 传送的长度:16
  4. 传送的方向:因为正向传送比较方便,所以设置DF=0

所以程序如下所示

  1. mov ax,data
  2. mov ds,ax
  3. mov si,0 ; ds:si指向data:0
  4. mov es,ax
  5. mov di,16 ; es:di指向data:0010
  6. mov cx,16 ; (cx)=16,rep循环16
  7. cld ; 设置DF=0,正向传送
  8. rep movsb

§ 第11章 标志寄存器 - 图68 问题二:编程,用串传送指令,将F0000H段中的最后16个字符复制到data段中

  1. data segment
  2. db 16 dup (0)
  3. data ends

要传送的字符串位于F000H段的最后16个单元中,那么它的最后一个字符的位置为:F000:FFFF。可以将ds:si指向F000H段的最后一个单元,将es:di指向data段的最后一个单元,然后逆向传送16个字节即可

  1. 传送的原始位置:F000:FFFF
  2. 传送的目的位置:data:000F
  3. 传送的长度:16
  4. 传送的方向:因为逆向传送比较方便,所以设置DF=1

所以程序如下所示

  1. mov ax,0f000h
  2. mov ds,ax
  3. mov si,0ffffh ; ds:si指向f000:ffff
  4. mov ax,data
  5. mov es,ax
  6. mov di,15 ; es:di指向data:000F
  7. mov cx,16 ; (cx)=16,rep循环16
  8. std ; 设置DF=1,逆向传送
  9. rep movsb

11.11 pushf和popf

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

pushfpopf为直接访问标志寄存器提供了一种方法。

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

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

§ 第11章 标志寄存器 - 图69

而其中的标志寄存器就如图中部分所示,他们的值表示为

§ 第11章 标志寄存器 - 图70