?? 匯編.txt
字號:
——引自XXX高人語錄
我總是想這樣去做,可每每照貓畫虎反類犬
以下摘自 老羅的繽紛天地之《組合語言之藝術》
這里下載 http://asp.7i24.com/netcool/laoluo/collections/index.htm
直接下載 http://asp.7i24.com/netcool/laoluo/collections/downloads/eBooks/Assembly%20Art.chm
第四節 指令應用
--------------------------------------------------------------------------------
匯編語言可以說是未經整理的、原始的計算機語言,讀者們大可下一番功夫,找出
其應用的規則,以發揮最高的效率。在下面,我僅就個人的經驗,提供一些淺見,以供
切磋研討。
要寫好程序,首先應熟記8088指令的時鐘脈沖(Clock )及指令長度,一般匯編語
言手冊中,都詳列了與各指令相關的數據。「工欲善其事,必先利其器」,此之謂也。
本節所討論的,是一般程序員容易忽略的細節,所有的例子都是從我所看過的一些
程序中摘錄下來的。看來沒什么大了不起,可是程序的效率,受到這些小地方的影響很
大。更重要的是,任何一個人,只要有「小事不做,小善不為」的習慣,我敢斷言,這
個人不會有什么大成就!
我最近才查到 Effective Address (EA) 的時鐘值,我覺得沒有必要死記。原則上,
以寄存器為變量,做間接尋址時為5個時鐘,用直接尋址則為6個;若用了兩組變量,
則為7至9個,三組則為11或12個。
為了便于敘述,下面以"T"表「時鐘脈沖」; "B"表字符。其中
時鐘脈沖T = 1 / 振蕩頻率
一、避免浪費速度及空間
匯編語言的效率建立在指令的運用上,如果不用心體會下列指令的有效用法,匯編
語言的優點就難以發揮。
1, CALL ABCD
RET
這種寫法,是沒有用心的結果,共享了 4B,23T+20T,完全相同的功能,如:
JMP ABCD 或
JMP SHORT ABCD
卻只要 2-3B,15T。
此外,上述的CALL XXXX 是調用子程序的格式,在直覺認知上,與JMP XXXX完
全不同。對整體設計而言,是不可原諒的錯誤,偵錯的時候,也很難掌握全盤的理念。
尤其是在精簡程序的時候,很可能會遇到 ABCD 這個子程序完全獨立,是則把
這段程序直接移到 ABCD 前,不僅能節省空間,而且使程序具有連貫性,易讀易用。
2, MOV AX,0
同樣,這條指令要 3B,4T,如果用:
SUB AX,AX 或
XOR AX,AX
只要 2B,3T, 唯一要注意的是,后者會影響旗號,所以不要用在有旗號判斷的指
令前面。
在程序寫作中,經常需要將寄存器或緩沖器清為0,有效的方法,是使某寄存
器保持為0,以便隨時應用。
因為,MOV [暫存器],[暫存器] 只要 2B,2T, 即使是清緩沖器,也比直接填
0為佳。
只是,如何令寄存器保持0,則要下一番功夫了。
還有一種情況,就是在一回路中,每次都需要將 AH 清0,此時對速度要求很
嚴,有一個指令 CBW 原為將一 個字符轉換為雙字符,只需 1B,2T 最有效率。可是應
該注意,此時 AL 必須小于 80H,否則 AH 將成為負數。
3, ADD AX,AX
需要 2B,3T不如用:
SHL AX,1
只要2B,2T。
4, MOV AX,4
除非這時 AH 必為0,否則,應該用:
MOV AL,4
這樣會少一個字符。
5, MOV AL,46H
MOV AH,0FFH
為什么不寫成:
MOV AX,0FF46H
不僅省了一個字符,四個時鐘,而且少打幾個字母!
6, CMP CX,0
需要 4B,4T, 但若用:
OR CX,CX
完全相同的功能,但只要 2B,3T。再若用:
JCXZ XXXX
則一條指令可以替代兩條,時空都省。不幸這條指令限用于CX ,對其他暫器無效。
7, SUB BX,1
這更不能原諒,4B,4T無端浪費。
DEC BX
現成的指令,1B,2T為何不用?
如果是
SUB BL,1
也應該考慮此時 BH 的情況,若可以用
DEC BX
取代,且不影響后果,亦不妨用之。
8, MOV AX,[SI]
INC SI
INC SI
這該挨罵了,一定是沒有記熟指令,全部共4B,21T。
LODSW
正是為這個目的設計,卻只要 1B,16T。
9, MOV CX,8
MUL CX
寫這段程序之時應先養成習慣,每遇到乘、除法,就該打一下算盤。因為它們
太浪費時間。8位的要七十多個時鐘,16位則要一百多。所以若有可能,盡量設法用簡
單的指令取代。
SHL AX,1
SHL AX,1
SHL AX,1
原來要 5B,137T,現在只要 6B,6T。如果CX能夠動用的話,則寫成:
MOV CL,3
SHL AX,CL
這樣更佳,而且CL之值越大越有利。用CL作為計數專 用暫存器,不僅節省空間,
且因指令系在 CPU中執行,速 度也快。
可是究竟快了多少? 我們做了些測試,以 SHL為例,在10MHZ 頻率的機器上,
作了3072 ×14270次,所測得時間為:
指 令 :SHL AX,CL SHL AX,n
CL = 0 , 23 秒 n = 0 , 無效
CL = 1 , 27 秒 n = 1 , 14 秒
CL = 2 , 32 秒 n = 2 , 28 秒
CL = 3 , 36 秒 n = 3 , 42 秒
CL = 4 , 40 秒 n = 4 , 56 秒
CL = 5 , 44 秒 n = 5 , 71 秒
CL = 6 , 49 秒 n = 6 , 85 秒
CL = 7 , 54 秒 n = 7 , 99 秒
由此可知,用CL在大于2時即較分別執行有效。
此外,亦可利用回路做加減法,但要算算值不值得,且應注意是否有調整余數
的需要。
10, MOV WORD PTR BUF1,0
MOV WORD PTR BUF2,0
MOV WORD PTR BUF3,0
MOV BYTE PTR BUF4,0
..
我見過太多這種程序,一見就無名火起! 在程序中,最好經常保留一個寄存器
為0,以便應付這種情況。即使沒有,也要設法使一寄存器為0,以節省時、空。
SUB AX,AX
MOV BUF1,AX
MOV BUF2,AX
MOV BUF3,AX
MOV BUF4,AL
14B,59T取代了 24B,76T,當然值得。只是,還是不 如事先有組織,考慮清楚各
個緩沖器間的應用關系。以前面舉的例來說,假定各緩沖器內數字,即為其實際位置關
系,則可以寫成:
MOV CX,3
如已知 CH 為0,則用:
MOV CL,3
SUB AX,AX
MOV DI,OFFSET BUF1
REP STOSW
STOSB
這段程序越長越占便宜,現在用10B,37T,一樣劃算。
11,子程序之連續調用:
CALL ABCD
CALL EFGH
如果 ABCD,EFGH 都是子程序,且調用的次數甚多,則上述調用的方式就有待
商榷了。因為連續兩次調用,不僅時間上不劃算,空間也浪費。
若ABCD一定與EFGH連用,應將ABCD放在EFGH之前:
ABCD:
..
EFGH:
..
像這樣,只要調用ABCD就夠了,但這種情形多半是程序員的疏忽所致,如兩個
子程序必需獨立使用,而上述連續調用的機會超過兩次以上,則應該改為:
CALL ABCDEF
而ABCDEF則應為:
ABCDEF:
CALL ABCD
EFGH:
..
這樣的寫法速度不會變慢,而空間的節省則與調用的次數成正比。
12,常有些程序,當從緩沖器中取數據時,必須將寄存器高位置為0。如:
SUB AH,AH
MOV AL,BUFFER
這時應該將 BUFFER 先設為:
BUFFER DB ?,0
然后用:
MOV AX,WORD PTR BUFFER
如此,不但速度快了,空間也省了。
13,有時看來多了一個指令,但因為指令的特性,反而更為精簡。如:
OR ES:[DI],BH
OR ES:[DI+1],BL
這樣需要8B,32T,如果改用下面的指令:
XCHG BL,BH
OR ES:[DI],BX
XCHG BH,BL
則需7B,28T。
14,PUSH 及 POP 是保存寄存器原值的指令,都只需一個字符,但卻很費時間。
PUSH 占 15T,POP 占12T,除非不得已,不可隨便使用。有時由于子程序說明
不清楚,程序員為了安全,又懶得檢查,便把寄存器統統堆在堆棧上。尤其是在系統程
序或子程序中,經常有到堆棧上堆、取的動作。實際上,花點功夫,把寄存器應用查清
楚,就可以增進不少效率。
要知道,系統程序及某些子程序常常應用,有關速度的效率甚大,如果掉以輕
心,就是不負責任!
保存原值的方法很多,其中較有效率的是放到一些不用的寄存器里。以我的經
驗,堆棧器用途最少,正好用作臨時倉庫。但最好的辦法,還是把程序中寄存器的應用
安排得合情合理,不要浪費,以免堆得太多。
還有一種方法,是在該子程序中,不用堆棧的手續,但另設一個入口,先將寄
存器堆起,再來調用不用堆棧的子程序。這兩個不同的入口,可以分別提供給希望快速
處理,或需要保留寄存器原值者調用。
當然,更簡單有效的方法,則是說明本段程序中某些寄存器將被破壞,而由調
用者自行保存之。
二、程序要條理通順
1,在比較判斷的過程中,鄰近值不必連比。
CMP AL,0
JE ABCD0
CMP AL,1
JE ABCD1
CMP AL,2
JE ABCD2
..
應為:
CMP AL,1
JNE ABCD0
ABCD1:
..
在標題為ABCD0 中,再作:
JA ABCD2
這種做法端視時間效益而定,似此 ABCD1之速度最快。
2,未經慎思的流程:
ADD AX,4
ABCD:
STOSW
ADD AX,4
ADD DI,2
LOOP ABCD
..
稍稍動點腦筋,就好得多了:
ABCD:
ADD AX,4
STOSW
INC DI
INC DI
LOOP ABCD
..
3,錯誤的處理方式:
MOV BX,SI
ABCD:
MOV BX,[BX]
OR BX,BX
JZ ABCD1
MOV SI,BX
JMP ABCD
ABCD1:
LODSW
..
上例應該寫成:
MOV BX,SI
ABCD:
LODSW
OR AX,AX
JZ ABCD1
MOV SI,BX
JMP ABCD
ABCD1:
..
4,錯誤的流程:
TEST AL,20H
JNZ ABCD
CALL CDEF[BX]
JMP SHORT ABCD1
ABCD:
CALL CDEF[BX+2]
ABCD1:
..
應該寫成:
TEST AL,20H
JZ ABCD
INC BX
INC BX
ABCD:
CALL CDEF[BX]
ABCD1:
..
5,下面是時間的損失:
PUSH DI
MOV CX,BX
REP STOSB
POP DI
PUSH,POP 很費時間,應為:
MOV CX,BX
REP STOSB
SUB DI,BX
同理,很多時候稍稍想一下,就可省下一些指令:
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -