主题:[轉貼]程式碼最佳化
程式碼最佳化
-----------------------------------
使用 AL/AX 暫存器而不用其它的暫存器
-----------------------------------
有時使用 AL/AX 暫存器,比起其它的暫存器,可達到更多的最佳化。下面是
一個比較:
cmp bx,1234h ; Compare BX with 1234h (4 bytes)
另一個更好的方式是用:
cmp ax,1234h ; Compare AX with 1234h (3 bytes)
然而,這只能在 AL/AX 暫存器中「未」存有重要數值時使用。就算你在程式
中多次使用到它,只要你記得先 PUSH 再 POP 回來即可。
----------------------------
使用 DATA 節區而不用其它節區
----------------------------
(註:翻成節區是我的習慣,有人有不同的看法)
從記憶體中移動一個值至 AX 可以是這樣:
mov ax,es:[si] ; Move ES:[SI] to AX (3 bytes)
另一個更好的方式是用:
mov ax,ds:[si] ; Move DS:[SI] to AX (2 bytes)
----------
清除暫存器
----------
清除暫存器可以是這樣:
mov ax,00h ; Clear AX (3 bytes)
另一個更好的方式是用:
sub ax,ax ; Clear AX (2 bytes)
另一個同樣好的方式是用:
xor ax,ax ; Clear AX (2 bytes)
--------------
清除 DX 暫存器
--------------
清除 DX 暫存器可以是這樣:
mov dx,00h ; Clear DX (3 bytes)
或是這樣:
xor dx,dx ; Clear DX (2 bytes)
另一個更好的方式是用:
cwd ; Convert word to doubleword (1 byte)
但這只能用在 AX 暫存器值小於 8000h 時。
--------------------
測試暫存器是否已清除
--------------------
測試暫存器是否清除可以是這樣:
cmp ax,00h ; AX = 0? (3 bytes)
另一個更好的方式是用:
or ax,ax ; AX = 0? (2 bytes)
-------------------------------------
使用 16 位元暫存器而不用 8 位元暫存器
-------------------------------------
移動一個數值至一個 16 位元暫存器可以是這樣:
mov ah,12h ; Move 12h to AH (2 bytes)
mov al,34h ; Move 34h to AL (2 bytes)
另一個更好的方式是用:
mov ax,1234h ; Move 1234h to AX (3 bytes)
然而,這只能用在上述兩個 8 位元暫存器是同個 16 位元暫存器的高低位元組時。
-------------------------------------
移動 AL/AX 暫存器至其它的暫存器或反之
-------------------------------------
移動 AL/AX 暫存器至其它的暫存器可以是這樣:
mov bx,ax ; Move AX to BX (2 bytes)
另一個更好的方式是用:
xchg ax,bx ; Exchange AX with BX (1 byte)
然而,你必須確定來源暫存器中的值不重要,因為它將保存目的暫存器的值。
----------------------------
使用 DI/SI 為基底索引而非 BP
----------------------------
從記憶體中移動一個值至 AX 可以是這樣:
mov ax,ds:[bp] ; Move DS:[BP] to AX (3 bytes)
另一個更好的方式是用:
mov ax,ds:[si] ; Move DS:[SI] to AX (2 bytes)
若 DI/SI 使用很頻繁,你只要記得先 PUSH 再 POP 回來即可。
---------------------------------------------
使用 CMPS, LODS, MOVS, SCAS, STOS 及 REP 指令
---------------------------------------------
從記憶體中移動一個值至 AX 可以是這樣:
mov ax,ds:[si] ; Move DS:[SI] to AX (2 bytes)
另一個更好的方式是用:
lodsw ; Load AX with DS:[DI] (1 bytes)
記得先設定或清除方向旗標。有時,更佳是做法是先 PUSH 再 POP 回來。
------------------------
移動一節區之值至另一節區
------------------------
移動一個節區之值至另一節區,你必須動點手腳,而不能像這樣直接:
mov ds,cs ; Can't do this!
因此,你必須使用一個暫存器做中介:
mov ax,cs ; Move CS to AX (2 bytes)
mov ds,ax ; Move AX to DS (2 bytes)
但若是 AX 有重要的值,那你必須先 PUSH 再 POP 回來,這樣便增加了 2
Bytes,所以一個更好的方法是用:
push cs ; Save CS at stack (1 byte)
pop ds ; Load DS from stack (CS) (1 byte)
--------------------------------
使用 SHL/SHR 而不用 DIV/MUL 指令
--------------------------------
以 AL 乘以 2 可以是這樣:
mov bh,02h ; Move 02h to BH (2 bytes)
mul bh ; Multiply AL with BL (2 bytes)
一個更好的方法是:
shl al,01h ; Multiply AL with 02h (2 bytes)
但這只能用在來源值是 2 的倍數。
----------------------------------
使用目的碼(Object Codes)而不用指令
----------------------------------
一個遠程呼叫可以是這樣:
call far address ; Make a far call (3 bytes)
address dd ? ; Address of a procedure (4 bytes)
一個更好的方法是:
callfar db 9ah ; Object code of a far call (1 byte)
address dd ? ; Address of a procedure (4 bytes)
這只能在目碼之後的值是「字組」或更大時達到最佳化的功能。
--------------
使用procedures
--------------
假若有些程式碼常被用到,那就可以用副程序來達到最佳化。節省的空間計
算如下:
Bytes saved = (procedure size - 4) * number of invocations - procedure size
Figure 4 in the parentheses of the formula is there because the size of the
CALL and RET instructions together are 4 bytes.
----------------
讓副程序更有彈性
----------------
當副程序可以混用時,這可以最佳化你的程式,因為多餘的部分已經消失了:
movefptrend proc near ; Move file pointer to the end
mov al,02h ; " " " " "
movefileptr proc near ; Move file pointer to end/beginning
cwd ; Convert word to doubleword
movefpointer proc near ; Move file pointer to a offset
xor dx,dx ; Clear DX
mov ah,42h ; Move file pointer
int 21h ; Do it!
ret ; Return!
endp
endp
endp
你可以由上面的示意計算出節省的空間。
---------------------
使用 DTA 中的所有資訊
---------------------
DTA (Disk Transfer Area) 是被 INT 21h 的 4eh 及 4fh 號呼叫使用的。
內容如下:
----------------------------------------
Offset Size Contents
----------------------------------------
00 Byte Drive letter
01-0B Bytes Search template
0C-14 Bytes Reserved
15 Byte File attribute
16-17 Word File time
18-19 Word File date
1A-1D DWord File size
1E-3A Bytes ASCIIZ filename + extension
----------------------------------------
- 若你想重設檔案時間及日期,使用 DTA 比 INT21h's 57h 更好。
- 若你想感染一個檔案,只要將磁碟機代碼換成一個不合法值即可,不用再寫
多餘的程式在退出部分。換不合法代碼會造成錯誤發生。(然後....我也不知道)
然而,這也只能在你本來就利用 DTA 時有效。
----------------
最後的忠告及秘訣
----------------
- 清除所有不需的 NOP
- 移動你的程式,看看能否把 JUMP NEAR 換成 JUMP SHORT
- 不要把一次可以得到的值分多次計算
- 用 LEA 而不用 MOV OFFSET
- 使用堆疊存暫時資料一但注意是否為 COM 檔案。
- 使用 CBW 指令清除 AH ,當 AL < 80h 時。
- 使用 DEC/INC 而不用 ADD/SUB 做加/減1
- 使用 DEC/INC 時宜用 16bits 暫存器而非 8bits
--
Jimmy Chung ( Chung Yuan-Kai ) u801403@Winkie.Oz.nthu.edu.tw
National Tsing Hua University bugger@ftp.cis.nctu.edu.tw
Hsin-Chu, Taiwan Bugger.bbs@bbs.nsysu.edu.tw