-----------------------------------
使用 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 |