天天看點

[彙編]《彙編語言》第11章 标志寄存器

目錄

王爽《彙編語言》第四版 超級筆記

第11章 标志寄存器

11.1 ZF标志、PF标志、SF标志

11.2 CF标志、OF标志

11.3 adc指令、sbb指令、cmp指令

11.4 檢測比較結果的條件轉移指令、DF标志和串傳送指令

11.5 pushf和popf、标志寄存器在Debug中的表示

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寄存器是按位起作用的,也就是說,它的每一位都有專門的含義,記錄特定的資訊。

8086CPU的flag寄存器的結構如圖11.1所示。

[彙編]《彙編語言》第11章 标志寄存器

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

在這一章中,我們學習标志寄存器中的CF、PF、ZF、SF、OF、DF标志位,以及一些與其相關的典型指令。

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

比如,指令:

mov ax,1 sub ax,1

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

mov ax,2

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

對于zf的值,我們可以這樣來看,zf标記相關指令的計算結果是否為0,如果為0,則zf要記錄下“是0”這樣的肯定資訊。

在計算機中1表示邏輯真,表示肯定,是以當結果為0的時候zf=1,表示“結果是0”。如果結果不為0,則zf要記錄下“不是0”這樣的否定資訊。

在計算機中0表示邏輯假,表示否定,是以當結果不為0的時候zf=0,表示“結果不是0”。

and ax,0

執行後,結果為0,則zf=1,表示“結果是0”。

or ax,0

執行後,結果不為0,則zf=0,表示“結果非0”。

注意,在8086CPU的指令集中,有的指令的執行是影響标志寄存器的,比如,add、sub、mul、div、inc、or、and等,它們大都是運算指令(進行邏輯或算術運算);

有的指令的執行對标志寄存器沒有影響,比如,mov、push、pop等,它們大都是傳送指令。在使用一條指令的時候,要注意這條指令的全部功能,其中包括,執行結果對标志寄存器的哪些标志位造成影響。

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

mov al,1 add al,10

執行後,結果為00001011B,其中有3(奇數)個1,則pf=0;

or al,2

執行後,結果為00000011B,其中有2(偶數)個1,則pf=1;

sub al,al

執行後,結果為00000000B,其中有0(偶數)個1,則pf=1。

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

計算機中通常用補碼來表示有符号資料。計算機中的一個資料可以看作是有符号數, 也可以看成是無符号數。比如:

00000001B,可以看作為無符号數1,或有符号數+1;

10000001B,可以看作為無符号數129,也可以看作有符号數-127。

這也就是說,對于同一個二進制資料,計算機可以将它當作無符号資料來運算,也可以當作有符号資料來運算。比如:

mov al,10000001B add al,1

結果:(al)=10000010B。

可以将add指令進行的運算當作無符号數的運算,那麼add指令相當于計算129+1,結果為130(10000010B);也可以将add指令進行的運算當作有符号數的運算,那麼add指令相當于計算-127+1,結果為-126(10000010B)。

不管我們如何看待,CPU在執行add等指令的時候,就己經包含了兩種含義,也将得到用同一種資訊來記錄的兩種結果。關鍵在于我們的程式需要哪一種結果。

SF标志,就是CPU對有符号數運算結果的一種記錄,它記錄資料的正負。在我們将資料當作有符号數來運算的時候,可以通過它來得知結果的正負。如果我們将資料當作無符号數來運算,SF的值則沒有意義,雖然相關的指令影響了它的值。

這也就是說,CPU在執行add等指令時,是必然要影響到SF标志位的值的。至于我們需不需要這種影響,那就看我們如何看待指令所進行的運算了。

比如:

執行後,結果為10000010B,sf=1,表示:如果指令進行的是有符号數運算,那麼結果為負;

add al,01111111B

執行後,結果為0,sf=0,表示:如果指令進行的是有符号數運算,那麼結果為非負。

某些指令将影響标志寄存器中的多個标記位,這些被影響的标記位比較全面地記錄了指令的執行結果,為相關的處理提供了所需的依據。

比如指令sub al,al執行後,ZF、PF、SF等标志位都要受到影響,它們分别為:1、1、0。

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

對于位數為N的無符号數來說,其對應的二進制資訊的最高位,即第N-1位,就是它的最高有效位,而假想存在的第N位,就是相對于最高有效位的更高位,如圖11.2 所示。

[彙編]《彙編語言》第11章 标志寄存器

我們知道,當兩個資料相加的時候,有可能産生從最高有效位向更高位的進位。比如,兩個8位資料:98H+98H,将産生進位。由于這個進位值在8位數中無法儲存,我們在前面的課程中,就隻是簡單地說這個進位值丢失了。

其實CPU在運算的時候,并不丢棄這個進位值,而是記錄在一個特殊的寄存器的某一位上。

8086CPU就用flag的CF位來記錄這個進位值。比如,下面的指令:

而當兩個資料做減法的時候,有可能向更高位借位。比如,兩個8位資料:97H-98H,将産生借位,借位後,相當于計算197H-98H。而flag的CF位也可以用來記錄這個借位值。比如,下面的指令:

我們先來談談溢出的問題。在進行有符号數運算的時候,如結果超過了機器所能表示的範圍稱為溢出。

那麼,什麼是機器所能表示的範圍呢?

比如說,指令運算的結果用8位寄存器或記憶體單元來存放,比如,add al,3,那麼對于8位的有符号資料,機器所能表示的範圍就是-128127。同理,對于16位有符号資料,機器所能表示的範圍是-3276832767。

如果運算結果超出了機器所能表達的範圍,将産生溢出。

注意,這裡所講的溢出,隻是對有符号數運算而言。下面我們看兩個溢出的例子。

mov al,98 add al,99

執行後将産生溢出。因為add al,99進行的有符号數運算是:

(al)=(al)+99=98+99=197。

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

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

執行後,将産生溢出。因為add al,088H進行的有符号數運算是:

(al)=(al)+(-l 20)=(-16)+(-120)=-136

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

如果在進行有符号數運算時發生溢出,那麼運算的結果将不正确。就上面的兩個例子來說:

add指令運算的結果是(al)=0C5H,因為進行的是有符号數運算,是以al中存儲的是有符号數,而C5H是有符号數-59的補碼。

如果我們用add指令進行的是有符号數運算,則98+99=-59這樣的結果讓人無法接受。造成這種情況的原因,就是實際的結果197,作為一個有符号數,在8位寄存器al中存放不下。

同樣,對于:

add指令運算的結果是(al)=78H,因為進行的是有符号數運算,是以al中存儲的是有符号數,而78H表示有符号數120。如果我們用add指令進行的是有符号數運算,則-16-120=120這樣的結果顯然不正确。造成這種情況的原因,就是實際的結果-136,作為一個有符号數,在8位寄存器al中存放不下。

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

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

一定要注意CF和OF的差別:CF是對無符号數運算有意義的标志位,而OF是對有符号數運算有意義的标志位。 比如:

add指令執行後:CF=0,OF=1。前面我們講過,CPU在執行add等指令的時候,就包含了兩種含義:無符号數運算和有符号數運算。

對于無符号數運算,CPU用CF位來記錄是否産生了進位;對于有符号數運算,CPU用OF位來記錄是否産生了溢出,當然,還要用SF位來記錄結果的符号。

對于無符号數運算,98+99沒有進位,CF=0;對于有符數運算,98+99發生溢出,OF=1。

mov al,0F0H add al,88H

add指令執行後:CF=1,OF=1。對于無符号數運算,0F0H+88H有進位,CF=1;對于有符号數運算,0F0H+88H發生溢岀,OF=1。

add al,78H

add指令執行後:CF=1,OF=0。對于無符号運算,0F0H+78H有進位,CF=1;對于有符号數運算,0F0H+78H不發生溢出,OF=0。

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

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

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

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

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

例:

mov bx,1 sub bx,ax adc ax,1

執行後,(ax)=4。adc執行時,相當于計算:(ax)+1+CF=2+1+1=4。

add ax,ax adc ax,3

執行後,(ax)=5。adc執行時,相當于計算:(ax)+3+CF=2+3+0=5。

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

執行後,(al)=34H。adc執行時,相當于計算:(al)+3+CF=30H+3+1=34H。

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

為什麼要加上CF的值呢?CPU為什麼要提供這樣一條指令呢?

先來看一下CF的值的含義。在執行adc指令的時候加上的CF的值的含義,是由adc指令前面的指令決定的,也就是說,關鍵在于所加上的CF值是被什麼指令設定的。

顯然,如果CF的值是被sub指令設定的,那麼它的含義就是借位值;如果是被add指令設定的,那麼它的含義就是進位值。我們來看一下兩個資料:0198H和0183H如何相加的:

[彙編]《彙編語言》第11章 标志寄存器

可以看出,加法可以分兩步來進行:

①低位相加;

②高位相加再加上低位相加産生的進位值。

下面的指令和add 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位進行設定。由于有這樣的功能,我們就可以對任意大的資料進行加法運算。看一個例子:

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

計算分3步進行:

(1)先将低16位相加,完成後,CF中記錄本次相加的進位值;

(2)再将次高16位和CF(來自低16位的進位值)相加,完成後,CF中記錄本次相加的進位值;

(3)最後高16位和CF(來自次高16位的進位值)相加,完成後,CF中記錄本次相加的進位值。

程式如下。

mov cx,1000H add cx,1EF0H adc bx,1000H

下面編寫一個子程式,對兩個128位資料進行相加。

名稱:addl28

功能:兩個128位資料進行相加。

參數:ds:si指向存儲第一個數的記憶體空間,因資料為128位,是以需要8個字單元,由低位址單元到高位址單元依次存放128位資料由低到高的各個字。運算結果存儲在第一個數的存儲空間中。

ds:di指向存儲第二個數的記憶體空間。

inc和loop指令不影響CF位。

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

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

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

比如指令sbb ax,bx實作的功能是:(ax)=(ax)-(bx)-CF

sbb指令執行後,将對CF進行設定。利用sbb指令可以對任意大的資料進行減法運算。比如,計算003E1000H-00202000H,結果放在ax,bx中,程式如下:

mov bx,1000H mov ax,003EH sub bx,2000H sbb ax,0020H

sbb和adc是基于同樣的思想設計的兩條指令,在應用思路上和adc類似。

在這裡,我們就不再進行過多的讨論。通過學習這兩條指令,我們可以進一步領會一下标志寄存器CF位的作用和意義。

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 cmp ax,bx

執行後:(ax)=8,zf=0,pf=1,sf=0,cf=0,of=0。

其實,我們通過cmp指令執行後,相關标志位的值就可以看岀比較的結果。

如果(ax)=(bx) 則(ax)-(bx)=0,是以:zf=1;

如果(ax)不等于(bx) 則(ax)-(bx)不等于0,是以:zf=0;

如果(ax)<(bx) 則(ax)-(bx)将産生借位,是以:cf=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的邏輯含義是比較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)

同add、sub指令一樣,CPU在執行cmp指令的時候,也包含兩種含義:進行無符号數運算和進行有符号數運算。

是以利用cmp指令可以對無符号數進行比較,也可以對有符号數進行比較。上面所講的是用cmp進行無符号數比較時,相關标志位對比較結果的記錄。下面我們再來看一下如果用cmp來進行有符号數比較時,CPU用哪些标志位對比較結果進行記錄。

我們以cmp ah,bh為例進行說明。

cmp ah,bh

如果(ah)=(bh) 則(ah)-(bh)=0,是以:zf=1;

如果(ah)不等于(bh) 則(ah)-(bh)不等于0,是以:zf=0;

是以,根據cmp指令執行後zf的值,就可以知道兩個資料是否相等。

我們繼續看,如果(ah)<(bh)則可能發生什麼情況呢?

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

(ah)=1,(bh)=2;則(ah)-(bh)=0FFH,0FFH為-1的補碼,因為結果為負,是以 sf=1。

(ah)=0FEH,(bh)=0FFH;則(ah)-(bh)=-2-(-1)=0FFH,因為結果為負,是以 sf=1。

通過上面的例子,我們是不是可以得到這樣的結論:“cmp 操作對象1,操作對象2”指令執行後,sf=1,就說明操作對象1<操作對象2?

當然不是。

我們再看兩個例子。

(ah)=22H,(bh)=0A0H;則(ah)-(bh)=34-(-96)=82H,82H是-126的補碼

是以sf=1

這裡雖然sf=1,但是并不能說明(ah)<(bh)因為顯然34>-96。

兩個有符号數A和B相減,得到的是負數,那麼可以肯定A<B,這個思路沒有錯誤,關鍵在于我們根據什麼來斷定得到的是一個負數。

CPU将cmp指令得到的結果記錄在flag的相關标志位中。我們可以根據指令執行後,相關标志位的值來判斷比較的結果。單純地考查sf的值不可能知道結果的正負。因為sf記錄的隻是可以在計算機中存放的相應位數的結果的正負。

比如add ah,al執行後,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不能說明在邏輯上,運算所得的正确結果的正負。

我們考慮一下,兩種結果之間的關系,實際結果的正負,和邏輯上真正結果的正負,它們之間有多大的距離呢?

從上面的分析中,我們知道,實際結果的正負,之是以不能說明邏輯上真正結果的正負,關鍵的原因在于發生了溢岀。如果沒有溢出發生的話,實際結果的正負和邏輯上真正結果的正負就一緻了。

是以,我們應該在考查sf(得知實際結果的正負)的同時考查(得知有沒有溢出),就可以得知邏輯上真正結果的正負,同時就可以知道比較的結果。

下面,我們以cmp ah,bh為例,總結一下CPU執行cmp指令後,sf和of的值是如何來說明比較的結果的。

(1)如果sf=1,而of=0

of=0,說明沒有溢出,邏輯上真正結果的正負=實際結果的正負;

因sf=1,實際結果為負,是以邏輯上真正的結果為負,是以(ah)<(bh)。

(2)如果sf=1,而of=1

of=1,說明有溢出,邏輯上真正結果的正負不等于實際結果的正負;

因sf=1,實際結果為負。

實際結果為負,而又有溢出,這說明是由于溢出導緻了實際結果為負,簡單分析一下,就可以看岀,如果因為溢出導緻了實際結果為負,那麼邏輯上真正的結果必然為正。

這樣,sf=1,of=1,說明了(ah)>(bh)。

(3)如果sf=0,而of=1

因sf=0,實際結果非負。而of=1說明有溢岀,則結果非0,是以,實際結果為正。

實際結果為正,而又有溢出,這說明是由于溢出導緻了實際結果非負,簡單分析一下,就可以看岀,如果因為溢出導緻了實際結果為正,那麼邏輯上真正的結果必然為負。

這樣,sf=0,of=1,說明了(ah)<(bh)。

(4)如果sf=0,而of=0

因sf=0,實際結果非負,是以邏輯上真正的結果非負,是以(ah)>=(bh)。

我們深入讨論了cmp指令在進行有符号數和無符号數比較時,對flag相關标志位的影響和CPU如何通過相關的标志位來表示比較的結果。

在學習中,要注意領會8086CPU這種工作機制的設計思想。實際上,這種設計思想對于各種處理機來說是普遍的。

下面的内容中我們将學習一些根據cmp指令的比較結果(即cmp指令執行後,相關标志位的值)進行工作的指令。

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

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

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

它們檢測的是哪些标志位呢?

就是被cmp指令影響的那些,表示比較結果的标志位。這些條件轉移指令通常都和cmp相配合使用,就好像call和ret指令通常相配合使用一樣。

因為cmp指令可以同時進行兩種比較,無符号數比較和有符号數比較,是以根據cmp指令的比較結果進行轉移的指令也分為兩種,即根據無符号數的比較結果進行轉移的條件轉移指令(它們檢測zf、cf的值)和根據有符号數的比較結果進行轉移的條件轉移指令(它們檢測sf、of和zf的值)。

下面是常用的根據無符号數的比較結果進行轉移的條件轉移指令。

[彙編]《彙編語言》第11章 标志寄存器

這些指令比較常用,它們都很好記憶,它們的第一個字母都是j,表示jump;後面的字母表示意義如下。

e:表不 equal ne:表示 not equal b:表示 below nb:表不 not below a:表不 above na:表示 not above

注意觀察一下它們所檢測的标志位,都是cmp指令進行無符号數比較的時候,記錄比較結果的标志位。

比如je,檢測zf位,當zf=1的時候進行轉移,如果在je前面使用了cmp指令,那麼je對zf的檢測,實際上就是間接地檢測cmp的比較結果是否為兩數相等。下面看一個例子。

程式設計實作如下功能:

如果(ah)=(bh)則(ah)=(ah)+(ah),否則(ah)=(ah)+(bh)。

上面的程式執行時,如果(ah)=(bh),則cmp ah,bh使zf=1,而je檢測zf是否為1,如果為1,将轉移到标号s處執行指令add ah,ah。這也可以說,cmp比較ah、bh後所得到的相等的結果使得je指令進行轉移。進而很好地展現了je指令的邏輯含義,相等則轉移。

雖然je的邏輯含義是“相等則轉移”,但它進行的操作是zf=1時則轉移。“相等則轉移”這種邏輯含義,是通過和cmp指令配合使用來展現的,因為是cmp指令為“zf=1”賦予了“兩數相等”的含義。

至于究竟在je之前使不使用cmp指令,在于我們的安排。je檢測的是zf位置,不管je前面是什麼指令,隻要CPU執行je指令時,zf=1,那麼就會發生轉移,比如:

執行後,(ax)=1。add ax,0使得zf=1,是以je指令将進行轉移。可在這個時候發生的轉移的确不帶有“相等則轉移”的含義。

因為此處的je指令檢測到的zf=1,不是由cmp等比較指令設定的,而是由add指令設定的,并不具有“兩數相等”的含義。但無論“zf=1”的含義如何,是什麼指令設定的,隻要是zf=1,就可以使得je指令發生轉移。

對于jne、jb、jnb、ja、jna等指令和cmp指令配合使用的思想和je相同,可以自己分析一下。

雖然我們分别讨論了cmp指令和與其比較結果相關的有條件轉移指令,但是它們經常在一起配合使用。

是以我們在聯合應用它們的時候,不必再考慮cmp指令對相關标志位的影響和je等指令對相關标志位的檢測。因為相關的标志位,隻是為cmp和je等指令傳遞比較結果。

我們可以直接考慮cmp和je等指令配合使用時,表現出來的邏輯含義。它們在聯合使用的時候表現出來的功能有些像進階語言中的IF語句。

我們來看下面的一組程式。

data段中的8個位元組如下:

(1)程式設計,統計data段中數值為8的位元組的個數,用ax儲存統計結果。

程式設計思路:初始設定(ax)=0,然後用循環依次比較每個位元組的值,找到一個和8相等的數就将ax的值加1。程式如下。

這個程式也可以寫成這樣:

比起第一個程式,它直接地遵循了“等于8則計數值加1”的原則,用je指令檢測等于8的情況,但是沒有第一個程式精簡。第一個程式用jne檢測不等于8的情況,進而間接地檢測等于8的情況。要注意在使用cmp和條件轉移指令時的這種程式設計思想。

(2)程式設計,統計data段中數值大于8的位元組的個數,用ax儲存統計結果。

程式設計思路:初始設定(ax)=0,然後用循環依次比較每個位元組的值,找到一個大于8的就将ax的值加1。

程式執行後:(ax)=3

(3)程式設計,統計data段中數值小于8的位元組的個數,用ax儲存統計結果。

程式設計思路:初始設定(ax)=0,然後用循環依次比較每個位元組的值,找到一個小于8的就将ax的值加1。程式如下。

程式執行後:(ax)=2

上面講解了根據無符号數的比較結果進行轉移的條件轉移指令。根據有符号數的比較結果進行轉移的條件轉移指令的工作原理和無符号的相同,隻是檢測了不同的标志位。

我們在這裡主要探讨的是cmp、标志寄存器的相關位、條件轉移指令三者配合應用的原理,這個原理具有普遍性,而不是逐條講解條件轉移指令。對這些指令感興趣的讀者可以檢視相關的指令手冊。

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

df=0 每次操作後si、di遞增;

df=1 每次操作後si、di遞減。

我們來看下面的一個串傳送指令。

格式:movsb

功能:執行movsb指令相當于進行下面幾步操作。

(1)((es)x16+(di))=((ds)x16+(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:

dec si

dec di

可以看出,movsb的功能是将ds:si指向的記憶體單元中的位元組送入es:di中,然後根據标志寄存器df位的值,将si和di遞增或遞減。

當然,也可以傳送一個字,指令如下。

格式:movsw

movsw的功能是将ds:si指向的記憶體字單元中的字送入cs:di中,然後根據标志寄存器df位的值,将si和di遞增2或遞減2。

用彙編文法描述movsw的功能如下。

mov es:[di],word ptr ds:[si] ;8086并不支援這樣的指令,這裡隻是個描述

add si,2

add di,2

sub si,2

sub di,2

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

rep movsb

用彙編文法來描述rep movsb的功能就是:

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

同理,也可以使用這樣的指令:rep movsw。

相當于:

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

8086CPU提供下面兩條指令對df位進行設定。

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

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

我們來看下面的兩個程式。

(1)程式設計,用串傳送指令,将data段中的第一個字元串複制到它後面的空間中。

我們分析一下,使用串傳送指令進行資料的傳送,需要給它提供一些必要的資訊,它們是:

傳送的原始位置:ds:si;

傳送的目的位置:es:di;

傳送的長度:cx;

傳送的方向:df。

在這個問題中,這些資訊如下。

傳送的原始位置:data:0;

傳送的目的位置:data:0010;

傳送的長度:16;

傳送的方向:因為正向傳送(每次串傳送指令執行後,si和di遞增)比較友善,是以設定df=0。

好了,明确了這些資訊之後,我們來編寫程式:

(2)程式設計,用串傳送指令,将F000H段中的最後16個字元複制到data段中。

我們還是先來看一下應該為串傳送指令提供什麼樣的資訊。

要傳送的字元串位于F000H段的最後16個單元中,那麼它的最後一個字元的位置:F000:FFFF,是顯而易見的。可以将ds:si指向F000H段的最後一個單元,将es:di指向data段中的最後一個單元,然後逆向(即從高位址向低位址)傳送16個位元組即可。

傳送的原始位置:F000:FFFF;

傳送的目的位置:data:000F;

傳送的方向:因為逆向傳送(每次串傳送指令執行後,si和di遞減)比較友善,所 以設定df=1。

pushf的功能是将标志寄存器的值壓棧,而popf是從棧中彈出資料,送入标志寄存器中。

pushf和popf,為直接通路标志寄存器提供了一種方法。

在Debug中,标志寄存器是按照有意義的各個标志位單獨表示的。在Debug中,我們可以看到下面的資訊。

[彙編]《彙編語言》第11章 标志寄存器

下面列出Debug對我們已知的标志位的表示。

[彙編]《彙編語言》第11章 标志寄存器

繼續閱讀