匯編語言-11標志寄存器


Flag寄存器

CPU內部的寄存器中,有一種特殊的寄存器(對於不同的處理機,個數和結構都可能不同)具有以下3種作用。
(1)用來存儲相關指令的某些執行結果;
(2)用來為CPU執行相關指令提供行為依據;
(3)用來控制CPU的相關工作方式。

這種特殊的寄存器在8086CPU 中,被稱為標志寄存器。8086CPU的標志寄存器有16位,其中存儲的信息通常被稱為程序狀態字(PSW) 。已經使用過8086CPU的ax、bx、cx、dx、si、di、bp、sp、IP、cs、ss、ds、es等13個寄存器。

標志寄存器(簡稱為flag)和其他寄存器不一樣,其他寄存器是用來存放數據的,都是整個寄存器具有一個含義。而flag寄存器是按位起作用的,也就是說,它的每一位都有專門的含義,記錄特定的信息。

flag寄存器各位示意圖

flag的1、3、5、12、13、14、15位在8086CPU中沒有使用,不具有任何含義。而0、2、4、6、7、8、9、10、11位都具有特殊的含義,分別代表CF、PF、ZF、SF、OF、DF標志位。

ZF標志

flag的第6位是ZF,零標志位。它記錄相關指令執行后,其結果是否為0。如果結果為0,那么zf=1;如果結果不為0,那么zf=0。

mov ax,l
sub ax,l

執行后,結果為0, 則zf=1。

mov ax,2
sub ax,1

執行后,結果不為0, 則zf=0 。

在8086CPU的指令集中,有的指令的執行是影響標志寄存器的,比如,add、sub、mul、div、inc、or、and等,它們大都是運算指令(進行邏輯或算術運算);有的指令的執行對標志寄存器沒有影響,比如,mov、push、pop等,它們大都是傳送指令。在使用一條指令的時候,要注意這條指令的全部功能,其中包括,執行結果對標志寄存器的哪些標志位造成影響。

PF標志

flag的第2位是PF,奇偶標志位。它記錄相關指令執行后,其結果的所有bit位中l的個數是否為偶數。如果l的個數為偶數,pf=1,如果為奇數,那么pf=0

SF標志

flag的第7位是SF,符號標志位。它記錄相關指令執行后,其結果是否為負。如果結果為負,sf=1;如果非負,sf=0。

計算機中通常用補碼來表示有符號數據。計算機中的一個數據可以看作是有符號數,也可以看成是無符號數。

 

這也就是說,對於同一個二進制數據,計算機可以將它當作無符號數據來運算,也可以當作有符號數據來運算。

mov al ,10000001B
add al ,1
結果:(al)=10000010B 

 

mov al,10000001B
add al,1

執行后,結果為10000010B,sf=1,表示:指令進行的是有符號數運算,結果為負;

mov al,10000001B
add al,01111111B

執行后,結果為0,sf=0,表示:指令進行的是有符號數運算,結果為非負。

某些指令將影響標志寄存器中的多個標記位,這些被影響的標記位比較全面地記錄了指令的執行結果,為相關的處理提供了所需的依據。比如指令sub al,al 執行后,ZF、PF、SF等標志位都要受到影響,它們分別為:1、1、0。

CF標志

flag的第0位是CF,進位標志位。一般情況下,在進行無符號數運算的時候,它記錄了運算結果的最高有效位向更高位的進位值,或從更高位的借位值。

對於位數為N的無符號數來說,其對應的二進制信息的最高位,即第N-1位,就是它的最高有效位,而假想存在的第N位,就是相對於最高有效位的更高位。

 

當兩個數據相加的時候,有可能產生從最高有效位向更高位的進位。比如,兩個8位數據:98H+98H,將產生進位。由於這個進位值在8位數中無法保存,但是CPU在運算的時候,並不丟棄這個進位值,而是記錄在一個特殊的寄存器的某一位上。8086CPU就用flag的CF位來記錄這個進位值。

mov al,98H
add al,al ;執行后: (al)=30H,CF=1,CF 記錄了從最高有效位向更高位的進位值
add al,al ;執行后: (al) =60H,CF=0,CF 記錄了從最高有效位向更高位的進位值

當兩個數據做減法的時候,有可能向更高位借位。比如,兩個8位數據:97H-98H,將產生借位,借位后,相當於計算197H-98H。而flag的CF位也可以用來記錄這個借位值。

mov al,97H
sub al,98H  ;執行后:(al)=FFH, CF=1,CF記錄了向更高位的借位值
sub al,al  ;執行后:(al)=0,CF=0,CF 記錄了向更高位的借位值

OF標志

在進行有符號數運算的時候,如結果超過了機器所能表示的范圍稱為溢出。

對於8位的有符號數據,機器所能表示的范圍就是-128~127。同理,對於16位有符號數據,機器所能表示的范圍是-32768~32767。

如果運算結果超出了機器所能表達的范圍,將產生溢出。注意,這里的溢出,只是對有符號數運算而言。

mov al,98
add al,99

結果197超出了機器所能表示的8位有符號數的范圍:-128 ~ 127

mov al,0F0H  ; F0H, 為有符號數-16 的補碼
add al,088H  ; 88H, 為有符號數-120 的補碼

+負數可以轉換為加上這個負數的補碼.

結果-136超出了機器所能表示的8位有符號數的范圍:-128~127

由於在進行有符號數運算時,可能發生溢出而造成結果的錯誤。則CPU需要對指令執行后是否產生溢出進行記錄。

flag的第11位是OF,溢出標志位。一般情況下,OF記錄了有符號數運算的結果是否發生了溢出。如果發生溢出,OF=1;如果沒有,OF=0。

一定要注意CF和OF的區別: CF是對無符號數運算有意義的標志位,而OF是對有符號數運算有意義的標志位

mov al,98
add al,99

add 指令執行后: CF=0, OF=1。

CPU在執行add等指令的時候,就包含了兩種含義:無符號數運算和有符號數運算。對於無符號數運算,CPU用CF位來記錄是否產生了進位;對千有符號數運算, CPU 用OF 位來記錄是否產生了溢出,當然,還
要用SF位來記錄結果的符號。對於無符號數運算,98+99沒有進位,CF=O;對於有符號數運算,98+99發生溢出,OF=1。

CF和OF所表示的進位和溢出,是分別對無符號數和有符號數運算而言的,它們之間沒有任何關系。

adc指令

adc是帶進位加法指令,它利用了CF位上記錄的進位值。

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

功能:操作對象l=操作對象l+操作對象2+CF
比如指令adc ax,bx實現的功能是:(ax)=(ax)+(bx)+CF

CF的值的含義。在執行adc指令的時候加上的CF的值的含義,是由adc指令前面的指令決定的,也就是說,關鍵在於所加上的CF值是被什么指令設置的。顯然,如果CF的值是被sub指令設置的,那么它的含義就是借位值;如果是被add指令設置的,那么它的含義就是進位值。

0198H和0183H如何相加:

add ax, bx等同於:

add al,bl    #al=al+bl,如果有進位則CF=1
adc ah,bh    #ah=ah+bh+CF

CPU提供adc指令的目的,就是來進行加法的第二步運算的。adc指令和add指令相配合就可以對更大的數據進行加法運算。

計算1EF000H + 201000H

計算1EF000H + 201000H,結果放在ax(高16位)和bx(低16位)中。

mov ax,001EH   #ax放第一個數的高位部分
mov bx,0F000H   #bx放第一個數的低位部分,十六進制第一位不能是字母所以補0
add bx,1000H    #bx加上第二個數的低位部分,進位CF=1
adc ax,0020H    #ax加上第二個數的高位部分+CF

adc指令執行后,也可能產生進位值,所以也會對CF位進行設置。有這樣的功能,我們就可以對任意大的數據進行加法運算。

計算1EF0001000H + 2010001EF0H

計算1EF0001000H + 2010001EF0H,結果放在ax(最高16位),bx(次高16位),cx(低16位)中。

mov ax,001EH    #第一個數的的最高位16位
mov bx,0F000H    #第一個數的次高16位
mov cx,1000H    #第一個數的低16位
add cx,1EF0H  #cx加上第二個數的低16位,CF記錄是否進位
adc bx,1000H  #bx加上第二個數的次高16位,CF記錄是否進位
adc ax,0020H  #ax加上第二個數的最高16位,CF記錄是否進位

兩個128 位數據相加

 

 實現

sbb指令

sbb是帶借位減法指令,它利用了CF位上記錄的借位值。

指令格式:sbb 操作對象1,操作對象2

功能:操作對象1 = 操作對象1 - 操作對象2 - CF
比如指令sbb ax,bx實現的功能是:(ax) = (ax) - (bx) - CF

cmp指令

cmp是比較指令,cmp的功能相當於減法指令,只是不保存結果。cmp指令執行后,將對標志寄存器產生影響。其他相關指令通過識別這些被影響的標志寄存器位來得知比較結果。

cmp指令格式:cmp 操作對象1,操作對象2
功能:計算操作對象1-操作對象2但並不保存結果,僅僅根據計算結果對標志寄存器進行設置。

cmp無符號數

指令cmp ax,ax,做(ax)-(ax)的運算,結果為0,但並不在ax中保存,僅影響flag的相關各位。指令執行后:zf=1,pf=1,sf=0,cf=0,of=0。

cmp ax,bx(無符號數)

指令cmp ax,bx(無符號數)的邏輯含義是比較ax和bx中的值,如果執行后:

同add、sub指令一樣,CPU在執行cmp指令的時候,也包含兩種含義:進行無符號數運算和進行有符號數運算。所以利用cmp指令可以對無符號數進行比較,也可以對有符號數進行比較。上面的是用cmp進行無符號數比較時,相關標志位對比較結果的記錄。

cmp有符號數

cmp ah,bh

對於有符號數運算,在(ah)<(bh)情況下,(ah) - (bh)顯然可能引起sf=1, 即結果為負。例如:

cmp 操作對象1,操作對象2,指令執行后,如果sf=1,並不能說明操作對象1 < 操作對象2。

sf只記錄結果的正負,當有符號運算的計算結果越界后符號位就不准確了,而sf依然只看符號位去記錄值。

08AH-070H = -118-112 = -230 = FF1A = 1111 1111 0001 1010,符號位為0,所以sf=0;

總結:

檢測比較結果的條件轉移指令

“轉移”指的是它能夠修改IP,而“條件”指的是它可以根據某種條件,決定是否修改IP。

jcxz就是一個條件轉移指令,它可以檢測cx中的數值,如果(cx)=O,就修改IP,否則什么也不做。所有條件轉移指令的轉移位移都是[-128,127]。

除了jcxz之外,大多數條件轉移指令都檢測標志寄存器的相關標志位,根據檢測的結果來決定是否修改IP。

條件轉移指令通常都和cmp相配合使用,就好像call和ret指令通常相配合使用一樣。

根據無符號數的比較結果進行轉移的條件轉移指令

第一個字母都是j,表示jump;后面的字母表示意義如下。

je檢測的是zf位置,不管je前面是什么指令,只要CPU 執行je指令時,zf=1, 那么就會發生轉移:

統計data段中數值為8 的字節的個數,用ax保存統計結果

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

DF標志和串傳送指令

DF標志(Direction Flag)

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

df=0 每次操作后si、di 遞增;
df=1 每次操作后si、di 遞減。

串傳送指令

movsb(move string byte)

格式: 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的功能

mov es:[di],byte ptr ds:[si]    #8086並不支持這樣的指令,這里只是個描述
#如果df=0:
inc si
inc di
#如果df=1:
dee si
dee di

movsw(move string word)

格式: movsw

movsw 的功能是將ds:si指向的內存字單元中的字送入es:di 中,然后根據標志寄存器df位的值,將si和di 遞增2或遞減2 。

用匯編語法描述movsw的功能

mov es:[di],word ptr ds:[si]    #;8086 並不支持這樣的指令,這里只是個描述

#如果df=0:
add si,2
add di,2
#如果df=1:
sub si,2
sub di,2

movsb和movsw進行的是串傳送操作中的一個步驟,一般來說, movsb和movsw都和rep配合使用,格式如下:
rep movsb
用匯編語法來描述rep movsb 的功能:

s :movsb
loops

rep的作用是根據cx的值,重復執行后面的串傳送指令。由於每執行一次movsb指令si和di會遞增或遞減指向后一個單元或前一個單元,則rep movsb就可以循環實現(cx)個字符的傳送。

同理,也可以使用這樣的指令: rep movsw。相當於:s :movsw    loop s

DF位操作指令

由於flag的df位決定着串傳送指令執行后,si和di改變的方向,所以CPU應該提供相應的指令來對df位進行設置,從而使程序員能夠決定傳送的方向。

8086CPU提供下面兩條指令對df 位進行設置:
cld指令(clear direction): 將標志寄存器的df位置0
std指令(set direction):將標志寄存器的df位置1

將data段中的第一個字符串復制到它后面的空間

data segment
  db 'Welcome to masm!'
  db 16 dup (0)
data ends

分析

 代碼

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    #(ex)=16,rep循環16次
cld    #設置df=0,正向傳送
rep movsb

pushf和popf

pushf的功能是將標志寄存器的值壓棧,而popf是從棧中彈出數據,送入標志寄存器中。
pushf和popf,為直接訪問標志寄存器提供了一種方法。

標志寄存器在Debug中的表示

 

各個標記的解釋

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM