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中的表示
各個標記的解釋