80x86家族的處理器提供了幾條與數(shù)組一起使用的指令。這些指令稱為串處理指令。它們使用變址寄存器(ESI和EDI)來執(zhí)行一個操作,然后這兩個寄存器自動地進(jìn)行增1或減1操作。FLAGS寄存器里的方向標(biāo)志位(DF) 決定了這些變址寄存器是增加還是減少。有兩條指令用來修改方向標(biāo)志位:
CLD 清方向標(biāo)志位。這種情況下,變址寄存器是自動增加的。
STD 置方向標(biāo)志位。這種情況下,變址寄存器是自動減少的。
80x86編程中的一個非常普遍的錯誤就是忘記了把方向標(biāo)志位明確地設(shè)置為正確的狀態(tài)。這就經(jīng)常導(dǎo)致代碼大部分情況下能正常工作(當(dāng)方向標(biāo)志位恰好就是所需要的狀態(tài)時),但并不能正常工作在所有情況下。

讀寫內(nèi)存
最簡單的串處理指令是讀或?qū)憙?nèi)存或同時讀寫內(nèi)存。它們可以每次讀或?qū)懸粋€字節(jié),一個字或一個雙字。圖5.7中的小段偽碼展示了這些指令的作用。這有點需要注意的。首先,ESI是用來讀的,而EDI是用來寫的。如果你能記得SI代表Source Index,源變址寄存器和DI代表Destination Index,目的變址寄存器,那么這個就很容易記住了。其次,注意包含數(shù)據(jù)的寄存器是固定的(AL,AX或EAX)。最后,注意存入串指令使用ES來決定需要寫的段,而不是DS。在保護(hù)模式下,這通常不是問題,因為它只有一個數(shù)據(jù)段,而ES應(yīng)自動地初始化為引用DS(和DS一樣)。但是,在實模式下,將ES初始化為正確的段值對于程序員來說是非常重要的3。圖5.8展示了一個使用這些指令將一個數(shù)組復(fù)制到另一數(shù)組的例子。
LODSx和STOSx指令的聯(lián)合使用(如圖5.8中的13和14行)是非常普遍的。事實上,一條MOVSx串處理指令可以用來完成這個聯(lián)合使用的功能。圖5.9描述了這些指令執(zhí)行的操作。圖5.8的第13和14行可以用MOVSD指令來替代,能得到同樣的效果。唯一的區(qū)別就是在循環(huán)時EAX寄存器根本就不會被使用。
REP前綴指令
80x86家族提供了一個特殊的前綴指令,稱為REP,它可以與上面的串處理指令一同使用。這個前綴告訴CPU重復(fù)執(zhí)行下條串處理指令一個指定的次數(shù)。ECX寄存器用來計算重復(fù)的次數(shù)(和在LOOP指令中的使用是一樣的)。使用REP前綴,在圖5.8中的12到15行的循環(huán)體可以替換成一行:
rep movsd
圖5.10展示了另一個例子:得到一個零數(shù)組。
串比較指令
圖5.11展示了幾個新的串處理指令:它們可以用來比較內(nèi)存和內(nèi)存或內(nèi)存和寄存器。在比較和查找數(shù)組方面,它們是很有用的。它們會像CMP指令一樣設(shè)置FLAGS寄存器。CMPSx 指令比較相應(yīng)的內(nèi)存空間,而SCASx 根據(jù)一指定的值掃描內(nèi)存空間。
圖5.12展示了一個代碼小片斷:在一個雙字?jǐn)?shù)組中查找數(shù)字12。行10里的SCASD指令總是對EDI進(jìn)行加4操作,即使找到了需要的數(shù)值。因此,如果你想得到數(shù)組中數(shù)12的地址,就必須用EDI減去4(正如行16所做的)。
REPx前綴指令
還有其它可以用在串比較指令中的,像REP一樣的前綴指令。圖5.13展示了兩個新的前綴并描述了它們的操作。REPE 和REPZ作用是一樣的(REPNE和REPNZ也是一樣)。如果重復(fù)的串比較指令因為比較的結(jié)果而終止了,變址寄存器同樣會進(jìn)行增量操作而ECX也會進(jìn)行減1操作;但是FLAGS寄存器將仍然保持著重復(fù)終止時的狀態(tài)。因此,使用ZF標(biāo)志位來確定重復(fù)的比較是因為一次比較而結(jié)束,還是因為ECX等于0而結(jié)束是可能的。
圖5.14展示了一個樣例子代碼片斷:確定兩個內(nèi)存塊是否相等。例子中行7中的JE指令是用來檢查前面指令的結(jié)果。如果重復(fù)的比較是因為它找到了兩個不相等的字節(jié)而終止,那么ZF標(biāo)志位就為0,也就不會執(zhí)行分支;但是,如果比較是因為ECX等于0而結(jié)束,那么ZF標(biāo)志位就為1,而且代碼將分支到equal標(biāo)號處。
更多建議: