 Virus Labs & Distribution

```		   C O D E   O P T I M I Z A T I O N
An Assembly Guide
Written by
D   A   R   K   M   A   N   /   V   L   A   D

-------------------------------------------------
Use the AL/AX register instead of other registers
-------------------------------------------------
Sometimes it is more optimized to use the AL/AX register instead of the
other registers, comparing with registers could be done like this:

cmp     bx,1234h             ; Compare BX with 1234h (4 bytes)

A more optimized way of doing this is:

cmp     ax,1234h             ; Compare AX with 1234h (3 bytes)

However this can ONLY be done when the AL/AX register DOESN'T hold an
important value, but if the AL/AX register is used many times it COULD
be more optimized to use it even if you have to PUSH it and then POP it
afterwards.

----------------------------------------------
Use the Data Segment instead of other segments
----------------------------------------------
Moving a value from the memory to AX could be done like this:

mov     ax,es:[si]           ; Move ES:[SI] to AX (3 bytes)

A more optimized way of doing this is:

mov     ax,ds:[si]           ; Move DS:[SI] to AX (2 bytes)

----------------
Clear a register
----------------
Clearing a register could be done like this:

mov     ax,00h              ; Clear AX (3 bytes)

A more optimized way of doing this is:

sub     ax,ax               ; Clear AX (2 bytes)

Or a equally optimized way of this is:

xor     ax,ax               ; Clear AX (2 bytes)

---------------------
Clear the DX register
---------------------
Clearing the DX register could be done like this:

mov     dx,00h              ; Clear DX (3 bytes)

Or like this:

xor     dx,dx               ; Clear DX (2 bytes)

A more optimized way of doing this is:

cwd                         ; Convert word to doubleword (1 byte)

However this can ONLY be done if the AX register is less than 8000h.

---------------------------
Test if a register is clear
---------------------------
Testing if a register is clear could be done like this:

cmp     ax,00h              ; AX = 0? (3 bytes)

A more optimized way of doing this is:

or      ax,ax               ; AX = 0? (2 bytes)

----------------------------------------------------
Use a 16 bit register instead of two 8 bit registers
----------------------------------------------------
Moving a value to a 16 bit register could be done like this:

mov     ah,12h              ; Move 12h to AH (2 bytes)
mov     al,34h              ; Move 34h to AL (2 bytes)

A more optimized way of doing this is:

mov     ax,1234h             ; Move 1234h to AX (3 bytes)

However this can ONLY be done if the two 8 bit registers are the high and low
register of the same 16 bit register.

---------------------------------------------------------
Move the AL/AX register to another register or vice versa
---------------------------------------------------------
Moving the AL/AX register to another register could be done like this:

mov     bx,ax               ; Move AX to BX (2 bytes)

A more optimized way of doing this is:

xchg    ax,bx               ; Exchange AX with BX (1 byte)

However this can ONLY be done if the source registers value is unimportant
afterwards, because the source register will hold the destination registers
value.

-------------------------------
Use DI/SI as base instead of BP
-------------------------------
Moving a value from the memory to AX could be done like this:

mov     ax,ds:[bp]           ; Move DS:[BP] to AX (3 bytes)

A more optimized way of doing this is:

mov     ax,ds:[si]           ; Move DS:[SI] to AX (2 bytes)

If the DI/SI index is used many times it COULD be more optimized to used
the DI/SI index even if you have to PUSH it and then POP it afterwards.

--------------------------------------------------------
Use the CMPS, LODS, MOVS, SCAS, STOS and REP instuctions
--------------------------------------------------------
Moving a value from the memory to AX could be done like this:

mov     ax,ds:[si]           ; Move DS:[SI] to AX (2 bytes)

A more optimized way of doing this is:

lodsw                        ; Load AX with DS:[DI] (1 bytes)

Remember to clear/set the direction flag. Sometimes it COULD be more
optimized to use these instructions even if you have to PUSH and then POP the
used registers/segments afterwards.

---------------------------
Move a segment to a segment
---------------------------
Moving a segment to a segment is weird, because you can't move a segment to
another segment directly by:

mov     ds,cs               ; Can't do this!

Because of that, you could use a register as a temporary storage, like this:

mov     ax,cs               ; Move CS to AX (2 bytes)
mov     ds,ax               ; Move AX to DS (2 bytes)

But if an important value is held by the AX register, you have to PUSH and
the POP it afterwards, that would add the code by 2 bytes, a more optimized
way of doing this is:

push    cs                  ; Save CS at stack        (1 byte)
pop     ds                  ; Load DS from stack (CS) (1 byte)

-----------------------------------------------
Use SHL/SHR instead of the DIV/MUL instructions
-----------------------------------------------
Multiplying AL with 2 could be done like this:

mov     bh,02h              ; Move 02h to BH (2 bytes)
mul     bh                  ; Multiply AL with BL (2 bytes)

A more optimized way of doing this is:

shl     al,01h              ; Multiply AL with 02h (2 bytes)

This can ONLY be used if the source can be divided by 2.

----------------------------------------
Use object codes instead of instructions
----------------------------------------
A far call could be done like this:

call    far address         ; Make a far call (3 bytes)

A more optimized way of doing this is:

callfar      db      9ah                 ; Object code of a far call (1 byte)

This will ONLY optimize your code if the immediate value after the object
code is a word or greater.

--------------
Use procedures
--------------
If some code is used many times and it's size is large, it COULD be more
optimized to use it as a procedure, the following formula can calculate if
it is more optimized to use a procedure:

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.

------------------------
Make procedures flexible
------------------------
Make procedures flexible if possible, this CAN optimize your code, because
the redundant code is removed, a example from this is:

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

Use the formula from above to calculate if the flexible procedures can be

----------------------------------
Use all the information in the DTA
----------------------------------
DTA (Disk Transfer Area) is used by service 4eh and 4fh in the DOS functions
interrupt (INT 21h), the contents of the DTA block are:

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

- If your want to reset the files time and date afterwards, then use the
DTA instead of service 57h in INT 21h.
- If you only want to infect one file, then change the drive letter to a
illegal value instead of making redundant code in the exit part of the
program, if you change the drive letter to a illegal value an error will
occur.

However, this will ONLY optimize your code, if you are already using the DTA.

---------------------
Final tips and tricks
---------------------
- Remove all unnecessary NOPs
- Move your code around, to see if some JUMP NEAR instructions could be
replaced by JUMP SHORT instructions etc.
- Don't use instructions to calculate values that can be calculated
directly in a parentheses.
- Use LEA instead of MOV OFFSET.
- Use the stack to store temporary data, but be careful if it is a COM file.
- Use the CBW instructions to clear the AH register if AL is less than 80h.