十三 過程
組合語言的過程可以被理解為方法,過程呼叫採取如下形式
call pname
pname為過程名,call為呼叫指令
pname過程的格式為
pname procproc 告知編譯程式過程pname的開始,endp告訴編譯程式過程pname的結束。ret指令表明何時返回到呼叫程式中,;過程體
retpname endp
ret和高階語言return不一樣,ret不返回任何值給呼叫程式。ret是過程中必不可少的呼叫指令。
舉個過程的例項:
sample proc如果過程中使用到ecx,eax,ebx, edx等通用暫存器,而不確定外界是否也用到這些通用暫存器,.if eax ==
0mov eax, 1
.else
mov edx, 0
.endif
retsample endp
那麼在過程中需要將外界用到的暫存器資料儲存,可以使用臨時變數,也可以使用入棧。
譬如ecx資料在呼叫程式中使用,同時過程mult中也用到ecx,那麼使用ecx之前將ecx入棧。
mult proc出了過程中明顯能從**中看到一些暫存器會被修改,還有一些指令會導致暫存器資料被修改。push
ecx
mov eax, 0
.if x !=
0mov ecx, 1
.while ecx <= y
addeax, x
incecx
.endw
.endif
popecx
retmult endp
invoke指令會導致eax,ecx,edx內容被修改
imul會導致eax,edx被修改。所以可以通過pushad和popad指令儲存和恢復這些暫存器的內容
舉例
blankln proc過程的宣告都是放在main主程式之後。而且過程更看重的是節約空間。pushad
.repeat
invoke printf, addr blnkfmt
decebx
.until ebx <=
0popad
retblacnkln endp
十三 巨集
1 巨集和過程都可以避免編寫同樣功能**,巨集的速度比過程快,但是傾向於浪費一些空間達到提高效率的目的。
巨集的宣告放在main函式之前。
巨集的結構如下:
mname macro巨集的結構和過程的結構不同,巨集內部沒有ret,並且macro 表示巨集的開始 macro前邊為巨集的名字。;巨集體endm
endm為巨集的結束,但是endm之前沒有巨集的名字。另外巨集的呼叫不需要使用call指令,直接寫巨集的名字即可。
可以通過檢視彙編列表檔案看看巨集擴充套件是什麼,以及巨集被插入到程式段是什麼指令,下面為某段**呼叫兩次swap巨集
展開後:
88 1d 00000046 表示指令mov ebx , num1,該指令在程式位址為00000000處。
另外需要注意的是巨集擴充套件後,注釋也會擴充套件在指令後邊。雖然會占用一定記憶體,但是注釋可以幫助程式人員排除邏輯錯誤。
如果想使用注釋,僅僅在巨集過程中可見那麼採取;;替代;,這樣注釋不會出現在巨集擴充套件中,使用者可以通過巨集的宣告看到注釋。
呼叫巨集的時候,呼叫幾次巨集就在相應的呼叫位置擴充套件幾次,而過程無論呼叫多少次,僅僅擴充套件一次。這就是過程節省空間的
原因。另外巨集內部盡量不要使用暫存器,如果使用暫存器也不需要儲存和恢復暫存器內容,這些操作放在呼叫巨集的程式裡,因為儲存和恢復
操作一則浪費空間,二則會減少巨集執行的效率。
2 帶引數的巨集
swap macro p1, p2p1,p2為巨集的引數,可以理解為高階語言巨集的兩個引數,呼叫程式呼叫swap時會將實參替代形參完成巨集呼叫。movebx, p1
xchg
ebx, p2
movp1, ebx
endm
呼叫swap時會用num1替代p1,num2替代p2,
用x替代p1, y替代p2
如果開發人員只傳入乙個引數怎麼辦?
比如 swap num1?
這樣巨集展開就出現問題。可以通過:req語句指明引數是必須傳入不可缺少的。
swap macro p1:req, p2:這樣要求呼叫swap必須傳入兩個引數,缺少引數則提示報錯。req
movebx,p1
xchg
ebx, p2
movp1, ebx
endm
考慮這樣乙個問題,如果呼叫程式呼叫swap num1,1怎麼辦?
xchg ebx,1出錯。
另外如果 swap ebx, ebx 怎麼辦?這種沒必要呼叫swap,雖然呼叫swap不會出錯,
但是造成了空間的浪費。這些問題都可以通過條件彙編解決。
十四 條件彙編
條件彙編和條件指令不同,條件彙編控制編譯程式,而不是控制程式的執行,
條件彙編根據不同的條件將不同的指令或**包含到程式中。
條件彙編指令if
if和之前介紹的高階彙編指令.if不一樣,if後邊的引數為 eq, ne, lt, le, gt, ge, or, and等。
舉例:if num eq 3
;該條件滿足將指令插入程式
endif
除此之外
ifb 判斷如果為空,則執行if下邊的邏輯。
ifnb 判斷如果不為空
ifidn 判斷如果相同
ifidni 不區分大小寫,如果相同
ifdif 如果不相同
ifdifi 不區分大小寫,如果不同
這類指令後邊要用《引數形式》
舉例:
addacc macro parm在程式中通過幾種方式呼叫addaccifb
inceax
else
addeax, parm
endif
endm
.lst檔案內容如下所示,根據不同條件擴充套件為不同的巨集
根據不同的條件生成了不同的機器指令,達到了節約空間和控制編譯的目的。
僅僅在一部分位址和機器指令行包含了彙編指令,其餘沒有機器指令和位址的彙編**不會
被包含在程式中。
使用條件彙編將之前的swap設計的更完善
swap macro p1:req, p2:通過條件彙編,使swap功能更健全和高效。req ifidni ,
xchg
p1, ebx
elseifidni ,
xchg
ebx, p2
else
movebx, p1
xchg
ebx, p2
movp1, ebx
endif
endm
十五 過程和巨集對比和總結
1 過程在被呼叫的時候只有乙份程式副本出現,而巨集在被呼叫的時候,每一次對巨集的呼叫都會出現一次巨集**的副本。
2 過程通常會儲存和恢復暫存器的內容,而巨集通常不會去儲存和恢復暫存器的內容。
3 過程傾向於節省記憶體空間,而巨集傾向於節省執行時間
4 呼叫過程的時候,使用call指令,後面跟著過程的名字,而呼叫巨集的時候直接寫巨集的名字。
5 過程中必須包含ret指令,但是巨集中一定不能寫ret
6 把過程名字放到endp語句之前的標記字段,但是endm之前的標記欄位不需要寫巨集的名字。
7 如果要求呼叫巨集必須傳入引數,可在引數後加:req
8 條件編譯if在使用or或and邏輯時需要將兩個引數用括號括起來,如if(x lt 0)or(y gt 1)
組合語言學習筆記 四
存放資料,最大容量ffffh 16bit暫存器 可以分割成高八 ah 低八 al 兩個暫存器 為什麼能分割?cpu最低讀取單位為位元組 8bit,且 8086 的暫存器為 16 位暫存器,8086 cpu 可以處理 兩種尺寸的資料 字 1byte 8bit,8 位暫存器 位元組 2byte 16bi...
組合語言學習四
本人比較懶,可能懶得對前面的文章進行整理,也不怎麼喜歡寫很多,呵呵。今天往後的系列可能會更懶一些,基本翻譯jones 1 and.bartlett.publishers.introduction.to.80x86.assembly.language.and.computer.architecture...
組合語言學習筆記
學習參考資料 大灰狼 講彙編 資料匯流排,位址匯流排,控制匯流排。位址匯流排有多少條就決定了cpu最大的記憶體使用量。80386有32位位址匯流排,所以它的定址能力就是4g.暫存器 通用暫存器,段暫存器,ax暫存器 通用暫存器,存放資料。高位位元組ah,低位位元組al。實體地址表示方法 位址加法器,...