وو

وحید آنلاین . آرشیو وبلاگ وحیدمی دات آی آر . شرکت بیان. vahidmy.blog.ir

وو

وحید آنلاین . آرشیو وبلاگ وحیدمی دات آی آر . شرکت بیان. vahidmy.blog.ir

Integers in x86 Assembly


Integers in x86 Assembly   ..



The Integer Sizes


The very first thing a beginner has to learn about Assembly Language is that no operation can be done without knowing and taking care of the size(s), with anything, and particularily not with Numbers. 


As described in X86_Basics, the main Sizes are the Byte, the Word, the dWord (and qWord for some Instructions).


Also, the Processor does not care, at all, if you consider, say a Byte, as representing, for you, in your human representation, a Text Char, a Flag, an Integer, or whatever else. The only thing the Processor considers is that a Byte is a Byte. For Integers, let us first describe the behavior of the Processor when operating, for example, on a Byte: As you now, know, a Byte content may vary from 0 to 0FF (0 to 255). Lets set the Value of a Byte Register (same for a Memory Byte, of course), to 0:


> mov al 0


... then, let us subtract 1:


> sub al 1  ; same with dec al


... then, the al value will be 0FF. The reverse is also true:


> mov al 0FF | add al 1 ; same with inc al


... then, the al value will be 0. The value of the next coming Byte, ah, will not be modified, in any case. Same for a Memory Variable:


[MyVariable: D$ ?]


mov B$MyVariable 0FF | inc B$MyVariable | Hexprint D$MyVariable  ; The Hexprint Routine will show '0'.


(This behavior is, of course, identical for any other Size than a Byte).


Take care of the Size Markers when reading the upper two Lines: I have declared 'MyVariable' as a dWord, and I have pointed to it as a Byte for the operations. Then, I view it back again as a dWord. Notice that, though 'MyVariable' is declared a dWord, applying to it a Byte operation can not modify its second Byte.


RosAsm being an Assembler (and not a Compiler), you are perfectly allowed to implement such ''unconventional' manipulations of the Sizes. HLL Typings (defining, once for all, that a given Variable is of a given Size and/or of a given quality) are utterly incompatible with any kind of Assembly. This may be a problem for beginners coming from HLLs, who may feel deprived of a useful security, but, by no way we could implement such hard limitations as Typings in an Assembler. 




Accessing Bits


Bytes are the base unit of x86 Programming. Not Bits, as wrongly, but regularly stated. Accessing Bits requires performing Mask Operations. Examples:


Reading the lower bit of the second Byte of a dWord:


> mov ebx 00_101_10101010_00000000

> mov eax ebx

> and eax 00_1_00000000  ; eax = 0


> mov ebx 00_101_10101011_00000000

> mov eax ebx

> and eax 00_1_00000000  ; eax <> 0


Writing (setting on) the lower bit of the second Byte of a dWord:


> mov ebx 00_101_10101010_00000000

> or ebx 00_1_00000000  ; ebx = 00_101_10101011_00000000


Clearing (setting off) the lower bit of the second Byte of a dWord:


> mov ebx 00_101_10101011_00000000

> and ebx 00_11111111_11111111_11111110_11111111  ; ebx = 00_101_10101010_00000000


Reversing (whatever previous state) the lower bit of the second Byte of a dWord:


> mov ebx 00_101_10101011_00000000

> xor ebx 00_1_00000000  ; ebx = 00_101_10101010_00000000




Signed Integer


Though the Processor really offers several Instructions that do take care of Signs, for Integers (See for example, the S Flag in Flags_and_Jcc) the fact of considering an Integer as a Signed or as an unsigned Integer remains under your own interpretation choice, only. What makes the difference is only with considering the Higher Bit of a Byte, Word, dWord, as a Negative Sign marker, or not.


When having, say, 0FF Value in al, you may consider that the al Value is 255. Viewing it as a Signed Byte, you will read 0-1. This is to say that the Positive Value ranges, for signed Numbers, will be: 


Byte: 0 to 00_01111111  ( = 07F  = 127)


Word: 0 To 00_01111111_11111111  ( = 07FFF = 32,767)


dWords: 0 to 00_01111111_11111111_11111111_11111111 (07FFF_FFFF = 2,147,483,647)


... and the Negative Values ranges will be:


Byte: 0FF to 080  ( -1 to -128)


Word: 0FFFF To 08000  ( -1 to 32,767)


dWords: 0FFFF_FFFF to 08000_0000 (-1 to = -2,147,483,648)


(For each Size, the Binary of the maximum negative Value, is 00_10000000.....)

~~~~~~~


Numbers


Numbers   ..



Decimal Numbers


As several Base notations are used in programming, let us start with a  review of what the Base is for Decimal Numbers. When we say that, in Decimal notation, the base is 10, we mean that the computation of a Number is based upon powers of 10, and that each Digit represents a value from 0 to 9. Example: 2,643 Decimal notation may be represented as follow:


(3*10^0) +  (4*10^1) + (6*10^2) + (2*10^3)


First clearly understanding how we play with those powers of ten in Decimal Notation makes it much easier to understand HexaDecimal and Binary Notations, as they all may be expressed in the same manner.




Hexadecimal Numbers


Though it is  popularly said that Computers are basically 'Binary Systems', the true base of Numbers computing is not at all the Bit (0 or 1). The lowest accessable level for Numbers, either in  a Computers Registers or in Memory, is the single byte (8 bits) in Hexadecimal form. Accessing Bits is not the 'natural' way and requires extended manipulative logical efforts.


The Base for HexaDecimal is 16 and one 'Digit' is represented by a Byte, that is, a number ranging from 0 to 15. The smallest direct access to any Memory content or any register content is the Byte size. As we only have ten traditional Digits in Decimal Notation, HexaDecimal is represented by these 10 usual Digits, plus an additional range of letters, from 'A' to 'F':


0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F


If you see this for the first time, note, for example:


... that 0F + 1 = 010,


... that the maximum value in a Byte is 0FF,


... that if you add 1 to a 0FF Byte, its Value is zeroed,


...and that, in RosAsm syntax, what differentiates the various Notations are the leading zero Digits:


11  >  11 Decimal

011 >  11 Hexa (17 Decimal)

0011 >  11 Binary (3 Decimal)


02643 HexaDecimal notation may be represented as follow:


(3*16^0) +  (4*16^1) + (6*16^2) + (2*16^3)


All of the usual Numbers Sizes units are based upon Hexadecimal. The commonly used sizes are:


Byte > 1 Byte > 8 Bits (0_11)

Word > 2 Bytes > 16 Bits (0_2211)

dWord > 4 Bytes > 32 Bits (0_4433_2211)

qWord > 8 Bytes > 64 Bits (0_8877_6655_4433_2211)




Binary Numbers


In the binary number system, based upon powers of  2, the Digits are either 0 or 1. 


0_0110_1010  Binary notation may be represented as follow:


(0*2^0) + (1*2^1) + (0*2^2) + (1*2^3) + (0*2^4) + (1*2^5) + (1*2^6)


Accessing Bits, inside the real Hexadecimal of Memory or Registers requires particular indirect masking operations, that can be performed either by some Mnemonics or by masking the values 'by hand':


test eax 00_1000


and B§Memory (not 32)




Hexadecimal in Memory


Say we have an Hexadecimal dWord Value of  0_1122_3344 stored in a File. Then you search for this Value with any Hexadecimal Editor: You will not at first see it. Its real appearance in the dead File Bytes flow will be as this:


xx xx xx 44 33 22 11 xx xx xx


Why are the Byte parts of a dWord Number in the pseudo reverse order? Well, this may seem stupid, at first , but this its really not: Storing the Values this way has the great advantage of allowing the Values accesses with a certain consitency, no matter the accessed size:


Say the upper value is pointed by a symbol as 'Value'. Now, if you do:


mov al B$Value | mov bx W$Value | mov ecx D$Value


al = 044 // bx = 0_3344 // ecx = 0_1122_3344


This is to say that, if you have a qWord Number whose value is 1, stored somewhere, you also may read it either as dWord, Word or Byte, the returned Value will be 1 in all cases. Are you sure you would have preferred it the other way round?

~~~~~~~





Addressing


Addressing   ..



Addressing is the act of specifying a memory location (Address) for reading from or for writing to.



Immediate  Addressing


This is simply writing a given value into a Register or in Memory. Examples:


mov  edi ,  040301C


Under modern OSes, this way for Addressing is not used, as the actual Addresses of a  running Application (RVA) are not supposed to be known by the programmer. The Assemblers (and Linkers) do this job by computing the symbolic names  encountered in the Sources.  Example: the upper statement could be found in a disassembly; but, in the Source code, it could be:


mov  edi  MySymbol




Register Addressing


Is loading a Register with an Address value previously stored in another Register:


mov eax  ebx




Indirect Addressing


Is reading/writing from/to a Memory cell:


[Value: 124,  642,  0,  33]


mov ecx , D$Value




Register Indirect Addressing


Is reading/writing from/to Memory through a Register.


On the 80386+ you may specify any general purpose 32 bit register when using the register indirect addressing mode. D$eax, D$ebx, D$ecx, D$edx, D$esi, and D$edi all provide addresses. The D$esp always refers to the Stack and  D$ebp is usually reserved, too, for Stack pointing.


Note that you must use the 32 bit names of the registers. You cannot use the 16 bit names because the Addressing Memory space is the one of the 32 Bits Mode. The following examples show the legal forms Set:


mov eax D$eax

mov eax D$ebx

mov eax D$ecx

mov eax D$edx

mov eax D$esi

mov eax D$edi

mov eax D$ebp

mov eax D$esp


; Example with our upper Data: [Value: 124,  642,  0,  33]


mov esi Value

    mov ecx  D$esi          ; ecx = 124




Indexed Register Indirect Addressing


Is reading/writing from/to Memory  through a Register plus an Offset:


mov esi   Value

mov ecx  D$esi+4    ; ecx = 642




Base Indexed Addressing


Same as above, but instead of an immediate Offset, we stand one more Register:


; Example with our upper Data: [Value: 124,  642,  0,  33]


mov eax  4

mov ebx  Value

mov ecx  D$ebx+eax ; ecx = 642



All examples can be given with a Memory as first member and register as second one (this is to say, to write to Memory, instead of reading from Memory).



SIB Addressing (Effective address)


For addressing a Memory content, the x86  offers an organization of the target, described as Scaled Indexed addressing modes. This is to say the combination of a Base, an Index and a Displacement. 


[disp + index*n]

[base + index*n]

[disp + base + index*n]


Base and Index are any of the X86 32 bit general purpose registers and n  is the value 1, 2, 4 or 8.


When running the Instruction, the X86 computes the effective address by adding disp, base, and index*n together. Examples:


mov eax D$ebx+esi*4+8

mov eax D$Value+ebx+ebx*2

mov eax D$Value+esi*8



The full syntax (maximum instruction possibilities) for Memory addressing is:


Address = Base + (Index * Scale) + Displacement:


mov eax   D$Value+eax*4+ebx


Where ''eax*4'' is allowed by the so called SIB (Scale / Index / Base ) encoding, very  useful for addressing a table of Words (*2), of dWords (*4), of qWords (*8). (You just increase EAX to point to the next record).


The 'Scale' is: 2, 4, 8

The 'Index' is, here, eax

The 'Base' is, here, ebx

(Value is called 'Displacement').


As RosAsm syntax allows you to add an immediate to the given Displacement (here, Value), you could even write, for example:


mov eax  D$Value+32+eax*4+ebx


Take special notice that this single Instruction performs, all at once, the multiplication of eax by 4, and two Additions (the third addition of Value+32 is done by the Assembler, at Compile time).



Base Index Scale Displacement

 EAX EAX 1    None    

 EBX EBX                

 ECX ECX 2    8-Bits  

 EDX + EDX    *        +            

 ESP       3   16-Bits  

 EBP EBP                

 ESI ESI 4   32-Bits  

 EDI EDI                


Table of the Effective Address Computation.



~~~~~~~


     







Jumping


Jumping   ..



Jumps can be either short or long. Long jumps are coded on dWord and short jumps are coded on Bytes. As jumps can be either up or down (forward / backward), they are stored as signed values. A short jump can therefore only be 127 octets down or 128 octets up.


A jump can be either direct or indirect. Direct jumps are the ones which are known by the Assembler at compile time. Example:


MyLabel:

; ...

; ...

; ...

jmp MyLabel


At compile time, the Assembler knows where 'MyLabel' will be located in Memory at run time. So, it can  directly encode the desired immediate Address.


An indirect call is a call to some location that will be  known only at run time. This is what we do, for example when calling for an api after loading a DLL 'by hand'.


Call  D$SomeExternalFunction




Unconditional Jumps


This is the simple jmp instruction.




Conditional Jumps


They are all these 'j..' jmps following a comparison (cmp or test). The execution depends on the state of the Flags Register (see also Flags_and_Jcc):


Flags: O S Z P C

Simple Flags tests Instructions:

je / jz . . 1 . .

jne / jnz . . 0 . .

jno 0 . . . .

jnp / jpo . . . 0 .

jnz . 0 . . .

jo 1 . . . .

jp / jpe . . . 1 .

je . 1 . . .

Unsigned Math Instructions:

jb / jnae / jc . . . . 1

jbe / jna . . 1 . 1   (both ZF and CF set on)

jnb / jae / jn . . . . 0

jnbe / ja . . 0 . 0   (both ZF and CF set off)

Signed Math Instructions:

jl / jnge A B . . .    (NOT  (A=B))

jle / jng A B 1 . .    (NOT  (A=B))  OR  ZF

jnl / jge A B . . .    (A=B)

jnle / jg A B 0 . .


Read  j  as 'jump', e  as 'equal',  z  as  'Empty',  n  as 'not',  o  as 'overflow',  p as 'parity',  a  as 'above',  c  as 'carry',  b  as 'below',  g  as 'greater',  l  as 'lower'


cmp eax  &FALSE  |  jne  Error    ; Jmp if not Equal.


Error: 




Looping instructions


All  loop Instructions rely on ecx value (they loop ecx times). All  loop Instructions  address Short jumps; This is to say that the included code, inside a loop cannot exceed 128 octets. For greater chunks of code, we have to use, for example, dec / cmp / jmp...



loop


Example, storing '0123456789' at MyString:


mov ecx  10

mov edi  MyString

mov al  '0'

L0:  stosb  |  inc al  |  loop L0<


Beware that, if ecx value is zero before entering the loop, the chunk of code will be run 0FFFF_FFFF times (!!!!!) because The loop instruction first  decrements ecx and only after doing so tests if ecx = 0.


To ensure the ecx is *not* zeroed before entering such a loop with a variable value, we have a particular instruction that does the same as ''cmp ecx 0  |  je  exit'': This is ''jecxz  Exit''.




Loope / loopz


(2 mnemonics, 1 opcode)


Does the same job as loop, but stop looping if ZF if set on (1)


mov esi MyString  |  mov ecx 100

L0:      lodsb

               ; ...

; ...

; ...

cmp al 0

loope  L0<      ; loops until ecx = 0  and  while  al  =  0



Loopne / Loopnz


Same as above but with reverse added condition:


mov esi MyString  |  mov ecx 100

L0:      lodsb

               ; ...

; ...

; ...

cmp al 0

loopne  L0<      ; loops until ecx = 0  and  while  al  <>  0




Sub Routines Instructions


Call


Call is a jumping instruction that pushes the Address following the call onto the Stack before jumping. It is bound to:



Ret


... which pops the return Address and jumps back to it. ret may be followed by an immediate argument telling how many more bytes are to be stripped from the Stack. Useful for HLL organization of sub Routines with Parameters pushed onto the Stack before the call and the Stack being cleared by the callee.


ret  12 ; pops 3 dWords from the Stack and returns to caller.

~~~~~~~




Flags and Conditional Jumps


Flags and Conditional Jumps   ..



The Flags Register


The Flags Register is a particular Register that indicates the different states of CPU.  Meaningful bits of this register are:


__| __| __| __| OF| DF| IF| TF| SF| ZF| __| AF| __| PF| __| CF|


where the meaning of set bits are:


OF > Overflow of math integer operation

DF > Direction Flag (set by STD)

IF  > Interruption Flag

TF > Trap Flag

SF > Sign Flag (set by a negative result of integer Math operation)

ZF > Zero Flag (or Equal Flag)

AF > Auxiliary Carry Flag

PF > Parity Flag

CF > Carry Flag



Table Conditional Jumps


They are all these 'j..' jmps following a comparison (cmp or test). The execution depends on the state of the Flags Register:


Flags: O S Z P C

Simple Flags tests Instructions:

je / jz . . 1 . .

jne / jnz . . 0 . .

jno 0 . . . .

jnp / jpo . . . 0 .

jnz . 0 . . .

jo 1 . . . .

jp / jpe . . . 1 .

je . 1 . . .

Unsigned Math Instructions:

jb / jnae / jc . . . . 1

jbe / jna . . 1 . 1   (both ZF and CF set on)

jnb / jae / jn . . . . 0

jnbe / ja . . 0 . 0   (both ZF and CF set off)

Signed Math Instructions:

jl / jnge A B . . .    (NOT  (A=B))

jle / jng A B 1 . .    (NOT  (A=B))  OR  ZF

jnl / jge A B . . .    (A=B)

jnle / jg A B 0 . .


Read  j  as 'jump', e  as 'equal',  z  as  'Empty',  n  as 'not',  o  as 'overflow,  p as 'parity',  a  as 'above',  c  as 'carry',  b  as 'below',  g  as 'greater',  l  as 'lower'


~~~~~~~




The Stack



The Stack   ..



The Stack is a Memory area pointed to by the ESP Register (Stack Pointer).


In the early DOS times, the .com Files Format, for executables, was limited to 65,536 Bytes. This tiny room had to be used all at once, for Code, Data and Stack, so that, saving room, inside the executables was a high priority. Code and Data were then hosted downward from Byte 0100 and the Stack was organized backward (upward), from Byte 0FFFF (last Byte). The Code Part was coming usually first, then the Static Data, then, what we would call, nowadays, the Virtual Data, eventually growing down, while the Stack Data were eventually growing up.


This reversed organization of the Stack, due to these historical reasons, is still the one we have today. You may represent the Stack as a sock and the Stack Data as balls: Saving Data onto the Stack is like storing a ball into a sock, and retrieving Data from the Stack is like retrieving the first found ball on the sock top.


In the PE format, the Stack is provided to each uploaded Application by the OS. With RosAsm you can define the Minimum and Maximum Size of the Stack in the [File]/[Output] (if you understand what you are doing - there are few probabilities the default definitions would not fit your App requirements...-).



Pushing and Popping


Two x86 instructions are devoted to the Stack operations: PUSH and POP.


Pushing is storing (writing) some Data onto the Stack Top.


Popping is retrieving (reading) some data from the Stack Top.


Push and pop Instructions modify the esp Register Value by themselves. For the usual Operations, you are not supposed to modify esp by yourself, though this is also possible, for example, when creating the Procedure Stack Frame, as long as its initial Value is properly restored before exiting the Procedure.


The Stack must remain aligned on dWords boundaries (particularly under NT), so that, the natural size of Stack Data is the dWord. When you POP a dWord, ESP is decreased four bytes. Example with 'push esi', storing the actual value of esi on the Top of Stack:


__| __|

__| __|

__| __|

__| __|

__| __| <<< 012FEDC

__| __|

__| __|

__| __|

__| __| <<< 012FEE0

__| __|

__| __|

__| __|


(Before)  (After)


Though the Stack must remain aligned on dWord boundaries, the Words operations are also allowed (there is no possibility for Bytes). Going on with the previous example, you may, as well, 'pop ax':


__| __|

__| __|

__| __|

__| __|

__|  __| <<< 012FEDC

__|  __|

__|  __| <<< 012FEDE

__| __|

__| __|

__| __|

__| __|

__| __|


... and then  'pop bx':


__| __|

__| __|

__| __|

__| __|

__|  __| 

__|  __|

__|  __| <<< 012FEDE

__| __|

__| __| <<< 012FEE0

__| __|

__| __|

__| __|


... as long as, at the end, - and if you do not execute, for example, an Api call in between the two pops-, the Stack finally remains dWord aligned.



Calling and Returning


Two other Instructions make use of the Stack: CALL and RET.


When calling for a Routine, call is, internally, two operations: First, the Address of the next Instruction is pushed on the Stack. Second a jump to the targeted Routine is performed.


When the ret Instruction is executed, the return Address is popped from the Stack and the execution goes on with the first Instruction following the original call in the calling Routine.


Needless to say, any Stack fault in the calling Routine and/or in the called Routine will produce a hang. These hangs are most often due to unpaired push and pop statements. The absolute rule is that you must always pop what you have pushed, before processing any ret. A good practice, in order to prevent disastrous errors is to always indent you push and pop Instructions, and to avoid the Spaghetti_Style unless you perfectly know what you are doing.



The ebp Register


Ebp, (the Base Pointer), is usually reserved by Applications to keep track of a given Stack position, inside Procedures. For example, in a Disassembler, the way to point out the various Procedures in the Code Bytes Flow is to simply search all occurences of bytes [055, 08B, 0EC], which are the Code Bytes for the Instructions creating most Procedures Stack Frames initializations: push ebp // mov ebp esp.


During the Procedure life, ebp is then used as a fixed Pointer, keeping track of all transmitted Parameters (ebp+8, ebp+12, ebp+16, ...) and of the eventual private Data locations created by decreasing the Stack Pointer (ebp-4, ebp-8, ebp-12, ...).



Transmitting Parameters to Procedures


When you see, in most RosAsm Sources, Instructions like:


call MyProcedure Parameter1, Parameter2, Parameter3


the call is, in fact, a user defined Macro, unfolding this Statement into:


push Parameter3

push Parameter2

push Parameter1

call MyRoutine 


In ''MyProcedure'', you have:


Proc MyProcedure:

Arguments @Parameter1, @Parameter2, @Parameter3


... Where @Parameter1 is equal to ebp+8, @Parameter2 equal to ebp+12, @Parameter3, equal to ebp+16.


Then at the End of the Procedure, you see usually the EndP Macro, which restores the Stack, if for example, Local Variable and/or Structure have been declared on the Stack, and that finally runs  (going on with the previous example):


ret 12


Here, the Number following the ret, indicates that 3 dWords Parameters (12 Bytes) are to be erased from the Stack, so that the caller does not have to take any care of balancing the Stack, after the call, with as many:


pop Parameter1

pop Parameter2

pop Parameter3



Moving with Stack


As you may know, there is no such Instruction as:


mov D$Value1 D$Value2


in the x86 Instruction Set. For this operation, you have to write, for example:


mov eax D$Value2 | mov D$Value1 eax


if you fall short in Registers, you may, as well, write:


push D$Value2 | pop D$Value1


Neater with a simple Macro:


[Move | push #2 | pop #1]


move D$Value1 D$Value2



Splitting with Stack


On several occasions, the OS returns two Words Values in one single dWord. This is the case, for example, for the X and Y Positions of the Mouse Cursor, when your Application receives a &WM_COMMAND Message with a &WM_LBUTTONDOWN in wParam (the Mouse Position is in Lparam). Instead of masking, shr, and moving Instructions, you may simply state:


push D@Lparam | pop W$MousePosX | pop W$MousePosY



The Stack used for reversing


Because of the ''Pushed-first / Popped-last'' organization of the Stack, we can use it for reversing a flow of computed Data. Here is an example from the RosAsm Source. 


When we have to write the Ascii form of a Number, inside a Chunk of Text, if we want to output ''one Space'' Separators, the fact of having to analyse the Number from lower to upper Nibbles and to output the String from left to right is a problem. The simpler way is to output, first, this String chars in the wrong order, and then to reverse it all. We can do this the easy way by pushing the Chars on the Stack, along with the Computations, and by popping them back to the Destination String Buffer. This way for reversing is not very efficient (the Stack operations are not very fast), but it is easy to understand and fast enough for small usual computings:


WriteEax:

    mov ebx eax


    If ebx = 0

        mov B$edi '0' | inc edi | ret

    End_If


  ; End Mark first:

    push 0-1

  ; Select the Low-weight nibble:

L0: mov eax ebx | shr ebx 4 | and eax 0F

  ; Turn Ascii by use of a Translation Table (faster):

    mov al B$HexaTable+eax

  ; Push the Char (in dWord al):

    push eax

    cmp ebx 0 | ja L0<

    mov B$edi '0' | inc edi


  ; Now Pop and write to Destination set up to EDI by Caller:

L0: pop eax | cmp eax 0-1 | je L9>

    mov B$edi al | inc edi | jmp L0<

L9: ret



Other Stack Instructions


Intel designed two more Instructions for use in HLLs: ENTER and LEAVE, that were proposed for advanced Stack Frames Management in Procedures, with nested access to ascendant Stack Frames, this is to say that a child Procedure could have access to its parent(s) Stack Frame(s). These Instructions have been very rarely used, even by Compilers. See the Descriptions for more info.


~~~~~~~





  













Registers



Registers   ..



The CPU Registers may be represented as small Memory units directly located on the CPU. From this point of view, they are faster Memories than the real physical Memory. Also, several CPU Instructions make use of pre-defined Registers. In Win32 Assembly Applications programing, the commonly used Registers are:


GP (General Purpose) Registers:


eax, ax, ah, al

ebx, bx, bh, bl

ecx, cx, ch, cl

edx, dx, dh, dl


Strings Registers: 


esi

edi


Stack Registers:


ebp

esp


(There are others of no interest in the very first steps, and of very little interest in Win32 Application writing).


The Flags Register is a particular Register that indicates the different states of CPU. More infos in Flags_and_Jcc.



You can write and read some values in all the Registers, according with their sizes: The first thing to know about a Register, before using it, is its size.


eax, ebx, ecx, edx, esi, edi, ebp, esp    = dWord = 4 Bytes [0 > 4,294,967,295]

ax, bx, cx, dx                            = Words = 2 Bytes [0 > 65,535]

ah, al, bh, bl, ch, cl, dh, dl                    = 1 Byte  [0 > 255]


ah, al are parts of ax. ax is part of eax; idem for b... , c... , d... Like this:


____________________

__| __| __| __|       eax ; full 4 Bytes register


__| __| __| __|        ax    ; lower half 2 Bytes of eax


__| __| __| __|        ah    ; higher half of ax


__| __| __| __|        al    ; lower half of ax



This representation is easier to understand at a byte level. Reading from right to left, you have 4 Bytes in each Register. You can also view them as sets of 32 bits units, from Bit 0 to Bit 31. 


AL is called the LOW byte of EAX and of AX


AH is called the HIGH Byte of AX.


AX is called the LOW word of eax.


To read the first BYTE in the EAX register (bits 0 to 7), and store its Value into a Memory Address, you may use, for example:


mov B$byteval al ; move 1st byte size value into the 'byteval' variable.


To read the second BYTE in the EAX register (bits 8 to 15)), and store its Value into a Memory Address, you may use, for example:


mov B$byteval ah ; move 2nd byte size value into the 'byteval' variable.


Same for the first WORD (bits 0 to 15):


mov W$wordval ax ; move 1st word size value into the 'wordval' variable.


To get at bits 16 to 31, you must rotate the bits in the register so they can be accessed by the previous instructions. Rotating a 32 bit register in either direction by 16 bits moves the LOW 16 bits into the HIGH 16 bits and the HIGH 16 bits into the low 16 bits of the register.


If you need to read and store, in a Memory Word, say, only the higher Word of EAX, you have to modify the content of EAX with the ROL or with the ROR Instructions:


rol eax 16 ; rotate EAX by 16 bits

or

ror eax 16 ; rotate EAX by 16 bits


... Then, the Higher Word is now in AX.


In all x86 Instructions, involving a Source and a Destination, the sizes of the two members must be the same. You cannot mix, for example, Bytes and dWords in one single operation:


mov eax cl ; Error: eax is 32 bit, cl is 8 bit


Though the upper rule does not apply on MOVSX and MOVZX Mnemonics, whose purpose is exactly with saving you from such situation:


movzx eax cl    ; 'z' stands for 'zero' extended unsigned integer operation.


movsx eax cl    ; 'S' stands for 'sign' extended signed integer operation


In some cases you may use,


mov eax 0      ; clear eax.

mov al cl      ; copy cl into al.


Some less often used mnemonics also do this kind of conversion:


mov  al cl      ; copy cl into al

cbw              ; convert BYTE in AL to WORD in AX

cwde              ; convert WORD in AX to DWORD in EAX




Each Register may have some specific usage:



General Purpose Registers


eax, ebx, ecx, edx, are called 'General Purpose Registers', but they may have some specific task to assume under certain circumstances:


eax, 'Accumulator', is the most ''general purpose'' of all but it is slightly specialized for mathematical source and results.


ebx, 'Base', is often used for indexing.


ecx, 'Counter' is the required one for some counts.


edx, 'Data', is required for some integer Math operations



Offset Registers


esi and edi, Source and Destination Index, have specific usage in Strings_Instructions but can also be used for anything else you want.


ebp and esp, Base and Stack Pointers, are for Stack management. ebp can be used for what you want, but not esp. ebp is usually reserved for Stack pointing, in HLL style writing. esp is, most often, modified directly by several instructions (Call / Ret / Push / Pop ...). Out of the HLL macros for Stack frames managing, you should refrain from modifying esp or ebp.



~~~~~~~

X86_Basics



X86_Basics   ..




The Memory


X86 Assembly is a very simple and Flat Language. If you are used to Types, with some HLL Compiler, you will have to adapt to the fact that, in Assembly, the only corresponding concept is the one of the various available Sizes, as well for Memory accesses as for Registers.


The Basic Sizes of X86 Assembly are


Byte 8 Bits B$Address

Word 2 bytes W$Address

Double Word 4 bytes D$Address


Plus the New extensions ( MMX , XMM ):


quad word 8 bytes Q$Address  //  X$Address



As opposed to what you will read every now and then, the basic unit of memory is not the Bit, but the byte (packet of 8 Bits). You can not access an individual Bit, under X86, with any simple and direct operation. For reading / writing one single Bit, you have to make use of the Mask Operations as described in Logical .


I have several times been surprised to see that beginners may have some problems making up their mind with some mental representation of the Computer's Memory. This was made particularly clear, for example, with beginners' questions about how to work with Multi-Dimensional Arrays, in Assembly.  So, it seems useful to say that:


The Computer's Memory is nothing but a linear ensemble of consecutive Bytes having an Address range between 0 and xxxx ('xxxx' depending on the amount of available installed Memory). Under modern OSes, the first available Address is not really zero, but, instead, the default Application upload Address -Usually 0400000 for an EXE PE, under ReactOS-.


Any Data stored in the Computer's Memory is nothing but a Number, and the only particularity of a given Data is nothing but its Size. For example, considering that some Data in Memory is either a signed Byte, or an unsigned dWord, or a Text String, it is always, and only a matter of interpretation, from the programmer's perspective, depending only on the usage he makes of this given Data.


More detail on Data Formats in Numbers.



The CPU


The Central Processing Unit is the hardware executing the set of Instructions of the machine Language. All these instructions are most often very simple, and may, or may not, require particular Registers and/or Memory accesses, provided in their expected Source and Destination Members.


            ~~~~~~~




Strategy Optimization


Strategy Optimization    ..



Size_Optimization and Speed_Optimization should be rejected from the Asm32 programming area. They are ridiculous, of no use in most cases, and hardly kill Readability, which is the most important thing to be preserved, in programming.


The superiority of Assembly over any HLL does not rely on Code Size and speed optimization, but on Strategy optimization and on a Specific Programming style (as opposed to a Modular Programming style). Yes, Strategy is particularly reserved to Assembly programming, for the very simple reason that HLL programmers cannot know what they are doing. Its as simple as that.


If giving many examples of Strategy Optimization is difficult, while giving counter examples is easy. You can see them everyday when running C Applications. Just take a look at the time Acrobat-Reader takes to find occurrences of one searched word, and when you will have finished laughing and drying your tears, ask yourself  ''How is this possible???''. Well, the answer is that this requires sophisticated programming methods, which base is Modular Programming. 


Modular Programming is the ensured way for producing fat and slow Code, because, to turn a Routine reusable, and flexible enough, you have to first make it able to hold all expected (and unexpected) possibilities. There are few reasons why an Assembly written Application should be faster than a C draft, if you use Modular Programing in Asm. Better to use Visual Basic. The worst aspect of Modular programming, is that, once a Module is available, you will continously be tempted to make a simple call to it, in any circumstance, no matter whether this way is accurate or not: It does the expected action. Period. 


Good Asm Programing is re-inventing the wheel, or, at least, always reviewing how the wheel turns. This is good and desirable. Each time you re-invent a wheel, the wheel will be better. If you do not  want to work again and again upon one given computing routine, just Copy/Paste from a previous work (with RosAsm, use the Clip feature, which is a friendly Source Level Librarian). Doing it this way will always give you full control of what you are really doing, when reusing and adapting some Template.


A frequently encountered example of Code reuse by Modular Programing is some Procedure for outputting a flow of Bytes under the form of Text Hexa. Inside RosAsm Source, I have to implement many those outputs. Each time I have to do it, I re-write, because there is no such a thing in the real world as a general purpose Hexa outputting: In one case, you may want leading zeros, in another one, you may want either Bytes or bigger formating. In one other case, the source format is known, whereas in another one it would not be... Is the outputted Text under Raw-aligned format, or not?... and so on... and so on... If such a basic and utterly simple concept as having a simple Module for outputting the simple Hexa Text form of an Number is 100% stupid and ineffective, what about more complex Modules ???...




Some examples of Strategy Optimization



Length of Text


Computing the length of short and middle length Texts does not require any optimization. Processors are fast enough. Applied upon really huge Data, this computation may slow down processes, from the final users point of view. In such cases, the answer is quite simple: No optimization trick-and-tip will ever make any difference for the human perception of time . The only way is to not run this computation: Example, when you load a text File in Memory, the Reading Function provides you with the Length of the File. Simply store this Value in a Variable, and increase / decrease it at each Data modification. This takes no time.



Sorting Tables


One example of a problem  encountered was when developing the RosAsm Disassembler: When disassembling, each time an Instruction includes an evocation of some Code 'Symbol' (any Code Address, in fact), we have to store this pointer somewhere, in order to later write the targeted Label in the final Source output. In the very first version of the Disassembler, I simply stored those Pointers in a Table, and then sorted them in ascendant order between the two disassembling Passes. Result: Most of the time was then spent at sorting!!!


Solution: 'Fast Sort Algo???' > 'Ridiculous!!!'. After this first release, I declared a Check Table of the same length, in Bytes, as the code itself. Then each time a Code evocation was encountered, I have simply set to 1 the byte at the same displacement in the Check Table. This indice Table takes no time, neither at disassembling, nor at source building. Just a simple inside adjustment, and it is done. The total amount of time for a complete disassembly was then divided by 3.



Avoiding Tables


Generally speaking, Tables, used for wide ranges of possible events, makes programming easier, but also slower. Searching one given value inside a set of Data that can not be ordered is a heavy time cost. Long flow of Cases Selections (If and friends, or others...), are much more killing to write, but much faster to run, because, in most cases, if not all, the selection(s) Cases tend to organize in some form of Selections Trees by themselves. This is the way I wrote the RosAsm Encoder and Disassembler: Zero Tables for the Encoder, one Table for the Disassembler. Results: When some Assemblers take 10 seconds, RosAsm takes 1 second; when other Disassemblers spend 1 minute, RosAsm spends 1 second.



Exchanging Pointers


One regular example given for Strategy is the pointers exchanging. Let us suppose you have two tables (same sizes), and you want to move all the Data from Table1  into Table2. (Of course, both Tables are dynamically attributed, and so, we have two Pointers-Variables to them). Instead of moving all the data, you just have to:


push D§Table1Pointer

    push D§Table2pointer

        pop D§Table1Pointer

            pop D§Table2Pointer


and it's done. Or better with a Macro:


[Exchange | push #1 | push #2 | pop #1 | pop #2]


Exchange D§Table1Pointer D§Table2pointer





Splitting Tables


Once the Data, that an Application has to parse, grows in size, searching for a particular Item inside the huge Table(s) may dramatically slow down the overall Application processes. This is why you will find here and there so many various fast search Algos. Another very simple way is to organize your Tables Data in some kind of  'Sub-Tables', which are to be built by applying to the Data one or several Check and split criterium.


Let us take an example with a huge Table, which is a flow of zero ended Names followed by a numeric Value. Your Application Parser has to return the Value according to a given Name.


Sorting the Table Names in alphabetic order will allow the Parser to exit the search as soon as a 'DestinationName' will be found 'bigger' than the 'SourceName'. This is to say that the overall searches will be made twice as fast, on average. But this trick only applies in particular cases, when the sort time is shorter than the usage time, and when the Search Exit does not have to run into an error case and stop the user actions.


A much better way is to implement one other Pointers Table to each new first Char into the Name Table. I mean another Table of Pointers (Check-Table), the first one pointing to the first 'a-leading' Name, the second one pointing to the first 'b-leading' Name, and so on. Then, your search Algo speed will be multiplied by 26 and, instead of having to first entirely sort all of your strings in alphabetic order (which may take some time...), you will just have to sort your Strings Table on the first Character of each Name (which is much faster), if half of the Table user does not consume more time than the sort does.


I used this method for several RosAsm Assembler Parsers, working upon Source symbols sorted by sizes, (alphabetically sorted or not in the 'Sub-Table' -depending on the comparison of time required for alphabetically parsing the Data and the overall usage time-).




CheckSums Search


When having to search, for example, for a Value corresponding to some KeyWord, the search of the KeyWords references, in a Table, may severely slow down an Application, if the List of KeyWords is huge. Instead of storing the KeyWords List in a Table, you may, first, build a CheckSums Table: A Table of the CheckSums of all the KeyWords, and build a corresponding Table for the Values. Then, you sort the CheckSums Table in ascending order. When sorting, at the same time you modify the position of a CheckSum, in the CheckSum Table, you modify also the corresponding Value position, in the Value Table (associated Sorts).


Once done, pointing out a Value corresponding to a Name becomes quite simple, and blazing fast: You compute the CheckSum of the Name, and, when you have this Checksum, you search for it, in the CheckSum Table... with a 2n Search. When the position of the CheckSum is pointed out, in the CheckSum Table, the Value is at the same displacement, in the Value Table.  Really killing fast.


What is a 2n Search:


esi > End of Table // ecx = Size of Table // eax = Searched CheckSum


Value at esi < eax  >>>  shl ecx 1 | sub esi ecx


Value at esi > eax >>> shl ecx 1 | add esi ecx


Loop upper two operations until found or ecx = 0


Note: If the Table size can not be a power of two, when ecx = 0, you also have to add a linear search upward or downward until smaller, or bigger, or out of Boundaries. Also, if the Table size may be power of two, you have to pad the empty part with 0-1, of course.


What is a CheckSum:


Any Algo computing a Number from a flow of Bytes. For the CheckSum Searches, you have to carefully choose an Algo providing uniformly distributed CheckSums (avoiding, as far as possible, producing the same CheckSums for two different Names -the Computing must have a verification security for this - ).




End


As a general rule, for the Assembly programmer, the answer to speed problems is quite simple: One task is not fast enough??? Simple: Do not do it!!! There is zero relationship between Ticks saved at Code level, on one hand, and the final user perception of time, on the other hand. The guys who save Ticks by Code level optimization are putting a sheet of paper on the floor, then they put their two feet on the paper and say: 'Hey! Look! I am closer to the moon'. Usually, saving 20 or 25% of run time by Code Level Optimization is considered a great and exceptional performance achievement. At the final user point of view, the cases when 20 or 25% of usage time may make some real difference are very exceptional, and, usually, if 10 Seconds are too long... 8 Seconds will also be too long!


Nevertheless, there are some valuable Code Level optimizations tricks that do not kill readability, that are cheap and that may seriously increase the overall speed:


All Data should be aligned on their own Boundary. This means that Words should be aligned on Words, dWords on dWords, qWords on qWords. This is no cost. When bad alignements are encountered, the Processors requires two Memory accesses instead of one.


Shift Instructions should be preferred for all Multiplication / Division of integers by powers of 2. This is not more difficult to read, requires zero extra Registers (as opposed to mul / div), and the execution is really much faster. See Multiplying Without MUL and IMUL in Machine_and_Arithmetic_Idioms.


LEA is very interesting for computing at once several Integer Math operations. See INTEGER MULTIPLY Paragraph in Speed_Optimization Chapter.

~~~~~~~




Size Optimization



Size Optimization (DeVilMan) .



Intro


In this tutorial we will see how to optimize code in size, making it smaller to fit your needs. These kinds of optimizations are often used by virusers and crackers that need to write routines as small as possible to be put into really small caves. Anyway these tricks can be obviouvsly applied to ''normal'' programming, so have a look at them and keep trying to make your code ever more smaller and faster! This is the true ASM programmer spirit.


Firstly, I apologize for my bad english, I hope you'll understand what I mean , at least looking at examples.



LET'S BEGIN  , HaVe PHuN!




Zeroing a register


MOV register 0 is not a good way to do this. Instead use:


xor reg reg ; 1 byte.


or


mov reg reg ; 1 byte.


Use CDQ to zero out EDX if EAX is lower than 080000000h.


Remember that sometimes there's no need to clean a register before moving data into it. Consider his example:


xor eax eax ; 2 bytes.

mov ax W§esi+4 ; 4 bytes.


It can be optimized to:


movzx eax W§esi+4 ; 4 bytes.


, saving 2 bytes. 




Testing for zero


Well, NEVER use:


CMP reg 0 ; 6 bytes.

jz L2> ; 2 bytes.


it's a waste of space. Instead use:


OR reg reg ; 2 bytes.

jz L2> ; 2 bytes.


I think it's worth the effort, you've halved its size!




Moving data between registers


XCHG is a powerful instruction. It EXCHANGES the contents of two registers, but it takes only 1 byte if one of them is the accumulator (EAX). So XCHG eax, ecx will take only 1 byte, being very useful in some situations. 


For example:


MOV ebx eax

XOR eax eax


can be optimized to:


XCHG ebx eax

XOR eax eax


In fact it doesn't matter if you are putting the contents of ebx in eax, 'cause you're gonna clean it with the next instruction.


NOTE: XCHG takes less space than MOV , but it is slower and not pairable.




String instructions


String instructions (MOVS, SCAS, CMPS, STOS, LODS) can be very useful while dealing with strings. Even if they are pretty slow, they can be used to save a lot of bytes.


Have a look at these simple routines:


- find the end of an ASCIIZ string


mov edi StringAsciiZ

xor al al

L0: scasb | jnz L0<


- append two strings ( if you don't know their length)

mov edi StringAsciiZ

xor al al

L0: scasb | jnz L0<

mov esi SourceString

L1: lodsb

stosb | or al al | jnz L1<


-append two strings (if you know the length)

mov edi EndOfStringAsciiZ

mov esi SourceString

mov ecx D§SourceStringLenght

rep movsb




Pushing and Popping


A lot of Windows APIs require a lot of parameters which sometimes are optional or redundant, so you must push NULL many times. You can save some bytes in this way:


Instead of:


push &NULL ; 2 bytes.

push &NULL ; 2 bytes.

push &NULL ; 2 bytes.

call ...

use:


xor eax eax ; 2 bytes.

push eax ; 1 byte.

push eax ; 1 byte.

push eax ; 1 byte.

call ...


Also, if you are going to push and pop many registers 


push ecx

push ebx

push edx

push esi

push edi

...

pop edi

pop esi

pop edx

pop ebx

pop ecx

mov eax D§esi


you'd better use PUSHAD and POPAD 


PUSHAD

...

POPAD

mov eax D§esi


obviouvsly only if you don't mind to save the value of a particular register.




Checking for -1


There are some APIs that return -1 (0ffffffffh), often if an error is occurred. For example CreateFile returns -1 if it failed. A common and wasteful way to do this is:


call 'KERNEL32.CreateFileA' ...

CMP eax, 0_FFFF_FFFF ; 6 bytes

jz err_invalid_handle ; 2 bytes.


Think of it in this way:


call 'KERNEL32.CreateFileA' ...

inc eax ; 1 byte.

jz err_invalid_handle ; 2 bytes.

dec eax ; 1 byte.


in fact 0FFFFFFFF+1=0




Wiping the HIGHWORD


A nice way to do this is using a mask:


AND ecx, 0FFFF ; 6 bytes.


BUT , if ecx is lower than 08000h , you can halve the size using CWDE (Convert Word to Extended Doubleword), which extends the sign bit of AX throughout EAX.


XCHG eax ecx ; 1 byte.

CWDE ; 1 byte.

XCHG eax ecx ; 1 byte.

 

Nice, isn't it?




Words registers


Try to avoid the use of word registers , because 16-bit instructions executing in 32-bit mode require an operand-size prefix, which occupies 1 more additional byte.




Moving Immediate Values


MOV always takes a 4 byte immediate operand even if it's a byte sized one. This means that a


MOV eax 10 ; 5 bytes.


takes 5 bytes!


Instead, you can PUSH a byte sized immediate value in only 2 bytes:


PUSH 10 ; 2 bytes.


Thus you can save some bytes with a PUSH immediate/ POP reg combo.


PUSH 10 / pop EAX ; 3 bytes




Powerful IMUL instruction


IMUL can be used as to perform a multiplication by an immediate value


IMUL eax 30 ; 3 bytes


instead of using:


PUSH 30 ; 2 bytes.

POP ecx ; 1 byte.

MUL ecx ; 2 bytes


but also to perform a multiplication by an immediate and a move in only 3 bytes!


IMUL eax ecx 025 ; 3 bytes


which multiplies ecx by 025h an puts the result in eax ( ecx is not touched).




Random values


Instead of using a randomize() and then a random() functions, you can in some cases use RDTSC instruction (  Read Time-Stamp Counter), but remember it's a Pentium instruction. It loads the current value of the processor's time-stamp counter into the EDX:EAX registers. EAX, the lower 32 bits, is a kind of ''random'' value(it's NOT random at all, but it changes so fast that lower bits can be considered random).


So to do something , let's say, a time every 4 consider the following example:


RDTSC

cmp al 63

jnb over

  ; Do something

over:



Or if you want a pseudo-random value between 0 and 3 do the following:


RDTSC

shr eax 30 ; leaving only 2 bits, you've got a number between 0 and 3


NOTE: put a BSWAP EAX before shifting if you want a more random value...




LAST WORDS

I hope you've find these info useful :) Remember that in the PC world SMALL things are usually better than LARGE ones. (heh...not like real life, where lots of things SHOULD be as large as possible....). 



DeVilMAn  - 2001


< devilman@flymail.it >


EvIl GeNiuSes foR a BetTeR ToMoRrow


            ~~~~~~~