高級語言提供高級的控制結(jié)構(gòu)(例如,if 和while語句)來控制執(zhí)行的順序。匯編語言并沒有提供像這樣的復(fù)雜控制結(jié)構(gòu)。它使用聲名狼藉的goto來替代,如果使用不恰當(dāng)可能會導(dǎo)致非常復(fù)雜的代碼。但是,它是能夠?qū)懗鼋Y(jié)構(gòu)化的匯編語言程序?;镜牟襟E是使用熟悉的高級語言控制結(jié)構(gòu)來設(shè)計程序的邏輯,然后將這個設(shè)計翻譯成恰當(dāng)?shù)膮R編語言(就像一個編譯器要做的一樣)。
比較
控制結(jié)構(gòu)決定做什么是基于數(shù)據(jù)的比較的。在匯編語言中,比較的結(jié)果儲存在FLAGS寄存器中,以便以后使用。80x86提供CMP指令來執(zhí)行比較操作。FLAGS寄存器根據(jù)CMP指令的兩個操作數(shù)的不同來設(shè)置。具體的操作是相減,然后FLAGS根據(jù)結(jié)果來設(shè)置,但是結(jié)果是不在任何地方儲存的。
如果你需要結(jié)果,可以使用SUB來代替CMP指令。
對于無符號整形,有兩個標志位(在FLAGS寄存器里的位) 是非常重要的:零標志位(zero flag(ZF)) 和進位標志位(carryflag(CF)) 。如果比較的結(jié)果是0的話,零標志位將置成(1) 。進位標志位在減法中當(dāng)作一個借位來使用??紤]這個比較:
cmp vleft, vright
vleft - vright的差別被計算出來,然后相應(yīng)地設(shè)置標志位。如果CMP執(zhí)行后得到差別為0,即vleft = vright那么ZF就被置位了(也就是: 1),但是CF不被置位(也就是: 0)。如果vleft > vright,那么ZF就不被置位而且CF也不被置位(沒有借位)。如果vleft < vright,那么ZF就不被置位,
而CF就被置位了(有借位)。
對于有符號整形,有三個標志位非常重要:零標志位(zero °ag (ZF)),溢出標志位(over°ow °ag(OF))和符號標志位(sign °ag (SF))。如果一個操作的結(jié)果上溢(下溢),那么溢出標志位將被置位。如果一個操作的結(jié)果為負數(shù),那么符號標志位將被置位。如果vleft = vright,那么ZF將被置
位(正好與無符號整形一樣)。如果vleft > vright,那么ZF不被設(shè)置,而且SF = OF。如果vleft < vright,那么ZF不被設(shè)置而且SF 6= OF。
不要忘記其它的指令同樣會改變FLAGS寄存器,不僅僅CMP可以。
分支指令
分支指令可以將執(zhí)行控制權(quán)轉(zhuǎn)移到一個程序的任意一點上。換言之,它們像goto一樣運作。有兩種類型的分支:無條件的和有條件的。一個無條件的分支就跟goto一樣,它總會產(chǎn)生分支。一個有條件分支可能也可能不產(chǎn)生分支,它取決于在FLAGS寄存器里的標志位。如果一個有條件分支沒
有產(chǎn)生分支,控制權(quán)將傳遞到下一指令。
JMP (jump的簡稱)指令產(chǎn)生無條件分支。它唯一的參數(shù)通常是一個指向分支指向的指令的代碼標號。匯編器和連接器將用指令的正確地址來替代這個標號。這又是一個乏味的操作數(shù),通過這個,匯編器使得程序員的日子不好過。能認識到在JMP指令后的指令不會被執(zhí)行,除非另一條分支指令
指向它,是非常重要的。
這兒有jump指令的幾個變更形式:
SHORT 這個跳轉(zhuǎn)類型局限在一小范圍內(nèi)。它僅僅可以在內(nèi)存中向上或向下移動128字節(jié)。這個類型的好處是相對于其它的,它使用較少的內(nèi)存。它使用一個有符號字節(jié)來儲存跳轉(zhuǎn)的位移。位移表示向前或向后移動的字節(jié)數(shù)(位移須加上EIP)。為了指定一個短跳轉(zhuǎn),需在JMP指令里的變量之前使用關(guān)鍵字SHORT。
NEAR 這個跳轉(zhuǎn)類型是無條件和有條件分支的缺省類型,它可以用來跳到一段中的任意地方。事實上,80386支持兩種類型的近跳轉(zhuǎn)。其中一個的位移使用兩個字節(jié)。它就允許你向上或向下移動32,000個字節(jié)。另一種類型的位移使用四個字節(jié),當(dāng)然它就允許你移動到代碼段中的任意位置。四字節(jié)類型是386保護模式的缺省類型。兩個字節(jié)類型可以通過在JMP指令里的變量之前放置關(guān)鍵字WORD來指定。
FAR 這個跳轉(zhuǎn)類型允許控制轉(zhuǎn)移到另一個代碼段。在386保護模式下,這種事情是非常鮮見的。
有效的代碼標號遵守與數(shù)據(jù)變量一樣的規(guī)則。代碼標號通過在代碼段里把它們放在它們標記的聲明前面來定義它們。有一個冒號放在變量定義的地方的結(jié)尾處。這個冒號不是名字的一部分。條件分支有許多不同的指令。它們都使用一個代碼標號作為它們唯一的操作數(shù)。最簡單的就是看FLAGS寄存器里的一個標志位來決定是否要分支??幢?.3得到關(guān)于這些指令的列表。(PF是奇偶標志位(parity flag) ,它表示結(jié)果中的低8位1的位數(shù)值為奇數(shù)個或偶數(shù)個。)
下面的偽碼:
if ( EAX == 0 )
EBX = 1;
else
EBX = 2;
可以寫成匯編形式,如:
其它比較使用在表2.3里的條件分支并不是很容易。為了舉例說明,考
慮下面的偽碼:
if ( EAX >= 5 )
EBX = 1;
else
EBX = 2;
如果EAX大于或等于5,ZF可能被置位或不置位,而SF將等于OF。這是測試這些條件的匯編代碼(假定EAX是有符號的):
上面的代碼使用起來非常不便。幸運的是,80x86提供了額外的分支指令使這種類型的測試條件更容易些。每個版本都分為有符號和無符號兩種。表2.4展示了這些指令。等于或不等于分支(JE和JNE)對于有符號和無符號整形是相同的。(事實上,JE和JZ,JNE和JNZ基本上完全相同。) 每個其它的分支指令都有兩個同義字。例如:看JL (jump less than)和JNGE(jump not greater than or equal to)。有相同的指令這是因為:
無符號分支使用A代表大于而B代表小于,替換了L和G。
使用這些新的指令,上面的偽碼可以更容易地翻譯成匯編語言:
循環(huán)指令
80x86提供了幾條專門為實現(xiàn)像for一樣的循環(huán)而設(shè)計的指令。每一個這
樣的指令帶有一個代碼標號作為它們唯一的操作數(shù)。
LOOP ECX自減,如果ECX 6= 0,分支到代碼標號指向的地址
LOOPE, LOOPZ ECX自減(FLAGS寄存器沒有被修改),如果ECX 6= 0
而且ZF = 1,則分支
LOOPNE, LOOPNZ ECX自減(FLAGS沒有改變),如果ECX 6= 0
而且ZF = 0,則分支
最后兩個循環(huán)指令對于連續(xù)的查找循環(huán)是非常有用的。下面的偽碼:
sum = 0;
for( i=10; i >0; i?? )
sum += i;
可以翻譯在匯編語言,如:
更多建議: