First Steps in Assembly


Introduction
Reduced set of assembly instructions to know
The registers in assembly
          The general purpose registers
          The address registers
          ESP, Stack Pointer register
Syntax of the address
          The rules of addressing forms
Declaring our Variables
          Syntax of C++ Statements
          char
          BYTE
          short
          WORD
          int or long
          DWORD
          float
          Initialized and constant variables
                    Using constants whereby #define directive
          Declaring Structures
                    How accessing to the C structures from assembly

Introduction

This document has not the claim to explain the assembly language for x386 processor but only to give the minimal information for our target: typing few asm code to manage the features of trng services and tomb4 procedures.
We'll no need to create a complete program in assembly but only few rows to call already present procedures, to read and write values in critical zones where we can get important effects.
More, the assembly we'll use is the inline assembly supplied in Microsoft Visual Studio compiler and this is a very simplified version of assembly. and where
There will be no issues about advanced concepts like stack, segments, assembly directives or 16 bit addressing.

Reduced set of assembly instructions to know

In the 99% of ASM code that we'll type, we'll use only this reduced set of assembly instructions.
Theorically you are free to gain infos from other documents on the net about assembly, studying the other dozens of asm instructions, anyway I suggest to begin studying this fundamentals instructions because they are really the most important for our targets.

ASM Instructions - Reduced Set

The list is not alphabetically sorted. The instructions are sorted by importance following a logical order
Instruction Example Description
MOV MOV eax, 234
MOV ecx, eax
MOV cl, 0
MOV ax, word ptr [MyVariable]
The most used instruction!
It moves the value from right operand to left operand.
Note: in spite I'm using the word "move" in the reality it is only a "copying" from destination to source:

For example, if ECX register contains value 83, the instruction:

MOV eax, ecx

it will move the 83 value in eax and at end in both register there will be the same value (83)

Since the left operand will receive it will be always register or an memory address.
Anyway in the assembly we'll use we can't use the syntax:

mov dword ptr [$403000], ecx

to move the value from ecx to memory with address $403000 but we'll use or a mnemonic variable, when it has been defined in our source code:

// defined in C++ source side:
DWORD BreastSize;
// while we'll use it in ASM section in following way:
mov dword ptr [BreastSize], ecx

or, when we have to type a value in some tomb4 address, we'll have to copy that address in a register in this way:

MOV eax, $403000 ;our address where type a value
MOV DWORD PTR [eax], ecx ;move value in ecx in $403000 address memory


MOVSX MOVSX EAX, BX
MOVSX CX, AL
MOVSX EDX, byte ptr [MyVar]
MOVSX is a moving instruction like MOV (see above) but the "...SX" says to use SIGN eXtending when we move a value from a short register to a longer register. (see registers table)

For example since AX is 16 bits wide, while ECX is 32 bits wide, we have to say to assembly how filling the highest 16 bits of ECX not covered by AX.
When we use MOVSX we say to extend the Sign of AX in higher 16 bit of ECX.
This means that we know that in AX there was a signed number and you wish that the sign attribute remains also when the value has been moved in ECX:

MOVSX ecx, ax

You'll use MOVSX syntax with signed numbers and different wideness of source and destination operands
MOVZX MOVZX ecx, ax
MOVZX eax, cl
MOVZX edx, word ptr [MyVariable]
MOVZX works like MOVSX with the difference that in this case we have a Zero eXtending.
We'll use MOVZX when the source is an unsigned value and we wish having Zero in higher side of destination operand.


ADD ADD eax, 4
ADD word ptr [MyVariable], 32h
The ADD instruction perform an adding between two operands, letting the result in destination (left) operand.

For example

ADD eax, 5

the value in eax register will be increased by 5
SUB SUB edx, 4
SUB dword ptr [MyVar], ecx
The SUB instruction subtracts the right operand to the left operand.

Example:

sub edx, 5

the value in edx will be reduced by 5
CMP CMP eax, 34h
CMP cl, 1
CMP word ptr [eax], 30
CMP performs a comparation between two operands, and then, in following instructions, it will be possible performing a conditional jumping in according with the result of previous CMP instruction. See also Jxx instructions.

You'll use CMP when you wish perform different zone of the code in according with some condition.

Example, if eax = 12 we can perform following CMP e conditional jumps

CMP eax, 10
ja GoEaxAbove10
jb GoEaxBelow10


CMP eax, 12
je GoEaxEqual12

As you can see, the left operand will be compared with right operand, for this reason the condition:

CMP eax, 10

has as result "above" (or greater) and the jump

ja Label

will be performed jumping to Label
Jxx JA GoAbove
JB GoBelow
JAE GoAboveOrEqual
JBE GoBelowOrEqual
JZ GoZero
JNZ GoNotZero
JE GoEqual
JNZ GoNotZero
JG GoGreater
JL GoLower
JGE GoGreaterOrEqual
JLE GoLowerOrEqual
JC GoCarry
JNC GoNotCarry
JS GoSigned
JNS GoNotSigned
Jxx instruction change its name in according with wich conditional bits you mean perform the conditional jump.
These bits will be set after a CMP instruction between two operands (see description of CMP instruction) but there are also other instructions those can change these status bits.

This is the list of main conditional jumping you can use:

JA
Jump if Above (used after comparison between unsigned numbers)

JB
Jump if Below (used after comparison between unsigned numbers)

JAE
Jump if Above or Equal (unsigned numbers)

JBE
Jump if Below or Equal (unsigned numbers)

JZ
jump if two numbers, after a CMP instruction, are equal, or if after the SUB, DEC, INC, AND, TEST, OR instructions the result is zero


JNZ
jump if two numbers, after a CMP instruction, are not equal, or if after the SUB, DEC, INC, AND, TEST, OR instructions the result is different than zero

JE
Jump if Equal
This instruction is a synonymous of JZ

JNE
Jump if Not Equal
This instruction is a synonymous of JNZ

JG
Jump if Greater
When the first (left) operand is greater than second (right) operand, in a signed comparison

JL
Jump if Less
When the first (left) operand is less than second (right) operand, in a signed comparison

JGE
Jump if Greater or Equal
When the first (left) operand is greater or equal than second (right) operand, in a signed comparison

JLE
Jump if Less or Equal
When the first (left) operand is less or Equal than second (right) operand, in a signed comparison

JC
Jump if Carry is set (=1)
The carry should be used to verify if after an addition (ADD) or subtraction (SUB) there have been a carry.

JNC
Jump if Carry is Not set (=0)

JS
Jump if Signed bit is set (=1)
The signed bit is the highest bit of a signed number. When S is set the number is negative.

JNS
Jump if Signed is Not set (=0)
When the siged is 0 the number is positive.
JMP JMP GoThere JMP perform in unconditional way a jumping in new code set in the label following JMP

Example

JMP LoopRooms

go to the code where we place the label "LoopRooms"

LoopRooms:
CALL CALL MySub
CALL [eax]
Perform the portion of code at address of operand, a label or absolute address, and then the control came back to the next instruction after the call.

Example:

MySubToAddTwoNumbers:
add ecx, eax
retn


mov ecx, 4
mov eax, 10
call MySubToAddTwoNumbers
; and now the ecx register will contain 14, because the above code "add ecx, eax"
; has been performed with the call instruction
mov eax, 0 ;and then perform this instruction

Remark: many subprocedure have a C++ function style, like it happens also in tomb raider engine, in this case the argument will be passed before performing the "call" pushing in the stack the value for the the arguments, and at end of the call in the EAX register could be a result
For example a C++ function requiring two arguments could be called in this way:

push 4 ;second argument
push 10 ;first argument
CALL SumTwoNumbers
add esp, 08h ;remove from stack two arguments (see PUSH/POP instructions)
; and now in EAX could be the result of SumTwoNumbers() function
RET
RETN
RET
RETN
The RET instruction performs a Return to the instruction following the CALL instruction that called current code.

Example:

mov eax, 3
mov ecx, 2
call MyCoolProcedure
cmp eax, 0

;then, in some side of our code there will be the MyCoolProcedure, like
;this:

MyCoolProcedure:
add eax, ecx
;.... other code
retn ;this instruction move the code execution of instruction "cmp eax, 0" following the call MyCoolProcedure

Note: see also the CALL instruction

Remark: the instruction RETN, where the final "N" is for "Near", is the same of RET instruction, since we use flat 32 assembly. The speech should be different using segmented 16 bit assembly, but this topic is not interesting for us.
PUSH
POP
PUSH EAX
PUSH ecx
PUSH 34000h
POP EDX
POP ECX
POP EAX
To understand the meaning of PUSH and POP instructions it is necessary introducing the concept of "stack".
The stack is a reserved memory used from the microprocessor to store temporary some values.
Our code may store value in the stack, too.
The access to the stack is not direct, like for common memory with specific address.
We can store a value in the stack using a PUSH instruction.

PUSH 45000

Above instruction will save (but we use the word "push", of course) the value 45000 in the stack
To exctract newly this value we'll have to use the opposite instruction:

POP EAX

now in the eax there will be the last value pushed in the stack with PUSH instruction. In our example eax will be = 45000

We can store more values in the stack and then extract them with many POP instruction but in these operations we have to follow some rules to avoid troubles.

First rule is that the push and correspoding pair of POP have to be always correctly nested.

For example, we'll use very ofter the push/pop instruction to save the content of some register to restore them in a second time.

PUSH EAX
PUSH EBX
;here we change the value of eax and ebx for our computes
mov ax, 16
mov bx, word ptr [FlagsOfRollingBall]
test bx, ax
jz Ignore
call ExplodeRollingBall
Ignore:
;and now we restore previous value of eax ebx
POP EBX
POP EAX

Above code is correct, because the sorting of push instructions is the opposite of following pop instructions, this is "nested" meaning.

But if after "ignore:", the end part of above code, we had typed:

Ignore:
POP EAX
POP EBX

We'll have taken a mistake, because the values of eax and ebx, should be inverted.

Second rule is that we can't jumping out of nested push/pop pair with JMP, Jxx or RET instructions.
This means that, everytime we push something in the stack, then we have to remember to extract it with POP before letting out that zone of code.

A typical error is showed in following code:

;saving some registers
push esi
push edi
push ebx

; perform some code
;....

cmp eax, 3
ja GoGreater
;other code
;...

;restore registers
pop ebx
pop edi
pop esi

GoGreater:
retn


In above code, when eax is above than 3 the code to restore register will be skipped (jumping to the "GoGreater" label) and this is bad. In this case when the "retn" instruction will be performed there will be a crash (exception) because the code will be performed in chaotic way.
You should remember that the stack is used from microprocessor also to store the return address after a call, and if you push or pop in uncorrect way the values in the stack, the control will go in bad zone of code because the MP will be no more able to remember the correct back address after a call.

Third rule is about the requirement to compensate the push instructions used to pass argument to some C++ function (procedure).
This rule is not so different than the second, but in this case we have another solution to get our target.
We can simply changing the pointer to the stack, "to say" to microprocessor that some values have been removed without using the usual "POP" instructions.
The stack pointer is a register named ESP (Extended Stack Pointer)

For example:

push ecx ; argument 3
push 901 ; argument 2
push esi ; argument 1
call FunctionDoesStuff

add esp, 12

The last instruction "add esp, 12" works like wheter 3 value (of 4 bytes for each) have been POPPED from the stack.
It's true the we might use the instructions:

POP ESI
POP EAX
POP ECX
to extract the three arguments, but since we are not interested to have those values, we can save time saying only "ignore the last three values that I popped in the stack"
Remember that the push operations have always size DWORD and so each PUSH operation is 4 bytes saved in the stack, for this reason we have to multyply by 4 the number of PUSH instruction to extract to get the value to use with "add ESP, ..." instruction.
PUSHAD
POPAD
PUSHAD
POPAD
PUSHAD and POPAD works lisk PUSH and POP (see description of PUSH/POP instructions) but in this case these instructions push or pop ALL DWORD registers into/from the stack.

This couple of istructions is useful when you save all (or almost all) the registers without typing a lot of nested PUSH and then POP instructions.

It could be useful use a couple of PUSHAD and POPAD before calling a C++ function to avoid that the C++ function changed some register in its computes.
For examples:

PUSHAD ;save all registers

push 34 ; arg3
push ecx ; arg2
push esi ; arg1
call MisteriousCFunction
add esp, 0ch ;0ch in hex = 12 in decimal, to remove arguments

POPAD ;restore all registers

in above code we are sure that all registers will have the same value they had before of PUSHAD instruction.
OR OR ax, 10h
OR ecx, 1
OR word ptr [MyVariable], 20h
OR instruction allows to set single bits in destination operand.
These operations on single bits should remember to us the "flags" or ocb codes to set in tomb raider objects.
Really there are many circustances where we'll use single bits to remember micro informations to use in a second moment.
When we set a single bit in value (like a flag), we use the OR instruction.

Example:

OR ax, 10h

we set the bit with value 10h (i.e. 16 in decimal) in ax register

OR word ptr [MyOcb], 1

we set the bit with value 1 in our variable MyOcb

Then we'll be able to check the presence of these bits using the TEST instruction, or remove them with the AND instruction.
AND AND ax, 0FEh
AND dl, 03h
AND word ptr [MyOcb], ~16
AND instruction performs an and operations between two operand.
With the AND instruction you change the destination operand with a mask in source operand, where in destination operand will remain bit "1" only when they were present in source AND in destination.

For example, if we have in eax register the value "2" and we perform the instruction

AND eax, 2

in eax will continue to remain the value "2", because it was present in source and destination operands.
While if we have in eax the value 6 (that is the sum of bit 2 + bit 4) and we perform the instruction

AND eax, 2

in eax now there will be 2 and not 6, because the "4" bit was missing in source.
Pratically we use AND to clear specific bits of a value, like flags to remove.
In this case we'll use in source (right) operand a mask, where there are all bits set to 1, execpting that we wish clear in destination operand.
For example if we wish clear the first bit of operand (with value 1), we'll use this instruction

AND al, 254

254 is all bit of a byte (255 value), less the bit 1
Anyway there is a nice trick you can use to simply the compute of the mask to use.
If you use the "~" sign in front of a number, you create the mask for that number, i.e. the mask to clear that bit in destination operand.
For example we can clear the bit with value 4 in this way:

AND word ptr [MyOcb], ~4

and now the bit with value 4 will be zero in MyOcb, while all other bits will keep same previous value.
TEST TEST al, 01h
TEST cx, dx
TEST instructions perform a sort of AND between two operand, but differently by AND, no operand will be really changed.
Tne only effect of TEST instruction is to set the status bits you can use for conditional jumps Jxx.
Pratically we'll use TEST to verify if some single bit of a value is set (1) or clear (0).
We know the OCB of object in tomb raider, and other flags in new trng features. Well, with TEST instruction we can discover if in some OCB value there is, a specific flag enabled or less.
For example if we wish know if in the [MyOcbValue] is set the flag 32 ($20 in hex) we can use these istructions:

TEST word ptr [MyOcbValue], 32
jnz GoIsPresent

When the the result is Not Zero (jnz as jump instruction) the bit is present, while if the result is Zero (jz as jump instruction) the bit is missing
SHL
SAL
SAL ax, 3
SHL word ptr [MyVariable], 1
SAL and SHL perform a shift to left of bits in the operand.
This operation correponding to multyply the value by 2^x where x is the value of second (right) operand.
For example if eax = 4 the instruction

SHL eax, 3

it's like multiply eax by 8 (2^3 = 8) and for this reason now eax will be = 32

Theorically the difference between SHL and SAL is that the SHL works with unsigned numbers while SAL with signed numbers.
In the reality, in this case, with a left shift, there is no difference between the two instructions. The case is different for the opposite SAR and SHR instructions.

The left shift will be used to have a fast mutliply or to move a mask bit to perform then an AND or TEST instruction.


SHR
SAR
SHR ax, 4
SAR edx, 1
SHR and SAR instructions perform a right shift of bits of destination operand for source operand times. It corresponds to dividing by 2^x the destination operand, where the "x" is the value in source operand.

For example the instruction:

SAR edx, 4

it corresponds to dividing by 16 (2^4=16) the value in edx.

The SAR is a Shift Arithmetic Right, and it works with signed numbers, while the SHR works with unsigned numbers.
The difference is important because if you use a SHR (unsigned shift) on a signed number, you'll lose the sign attribute and the number will become positive, with a meaningless result.
XOR XOR al, 07h
XOR eax, eax
XOR bx, cx
XOR performs an eXlusive OR, an operation on binary nature of the values.
The xor set 1 when the two corresponding bits of operands are different, while set 0 when the two corresponding bits have same value.
Anyway the above target is not very used.
You'll find a XOR instruction more often as fast trick to clear (set to 0) a register, using this syntax:

XOR eax, eax

In fact, using the same register for source and target operand, we'll be sure that after this instruction eax will be = 0
It's curious that this trick was used more often than the specific instruction CLR (CLear Register) born own to set 0 in a register but the reason is that the xor trick is faster to be performed than CLR instruction.
NEG NEG ax
NEG bl
NEG word ptr [MyVariable]
The NEG instruction invert the sign of the value in the operand.
For example if the ax register contain 12 the instruction:

NEG ax

will transform the value in -12
LEA LEA ebx, [eax*2]+3
LEA ecx, [edx*8][ecx+24]
LEA eax, [esi-3][ebx*4]
LEA esi, MyVector[eax*2]
LEA instruction is for Load Effective Address and it computes the result of address in right (source) operand like it was a formula.
To understand the usefulness of this instruction we can suppose that is useful converting a complicated address in a single register to have a more easy way to access many times this address, saving also the usage of other registers used to compute the formula.

For example, if we had to locate the begin of a record in a complex structure we could use formula like this:

BaseVector+4[eax][esi*8]

then we wish typing different values in this address and those immediatly followings, we discover thas is more expensive in time terms (and typing terms) keep always in above form the address.

For example:

mov word ptr BaseVector+4[eax][esi*8], 1
mov word ptr BaseVector+6[eax][esi*8], 2
mov word ptr BaseVector+8[eax][esi*8], 3

please note the +4, +6, +8 to write the three values (1,2,3) in three next contiguous words.

While using the LEA method we'll have

lea eax, BaseVector+4[eax][esi*8]
mov word ptr [eax], 1
mov word ptr [eax+2], 2
mov word ptr [eax+4], 3

with the further advantage then after the LEA instruction we can use the ESI register as we wish because it will be no more used to keep the position of the address where we wish write.

Note: very often in tomb4 code you'll find these lea instructions to compute a very complicated address, in these cases the lea instructions, combined with addressing skills (see below) and SHL instruction, will be used to replace a multiplication.
This happens because the single MUL (multiplication) instruction is very slower than a list of LEA or SHL instructions and therefor it will be used a list of LEA, SHL both than a single MUL instruction.

For example in tomb4 code to multiply the index of a moveable by the size of a single item record (5622 or $15f6 in hex) will be used this list of instructions:

movsx eax, bx ; in bx there is the index to mutliply by 5622
lea ecx, [eax][eax*2]
lea ecx, [eax][ecx*4]
lea edx, [ecx][ecx*8]
lea ecx, [eax][edx*8]
mov edx, dword ptr [pVetItems] ;in pVetItems there is the base of items
lea ecx, [ecx][ecx*2]
lea esi, [edx][ecx*2]

well, now in esi there is the address with the structure of item with index bx.
But we will use, in easier way, the MUL instruction, of course.
In this way:

movzx eax, bx
imul eax, 5622
mov ecx , dword ptr [pVetItems]
lea esi, [ecx][eax]

and in esi we'll have the same address.
MUL
IMUL
MUL edx, 2034
IMUL eax, 23
The MUL and IMUL perform a multiply.
The IMUL is for signed numbers, while MUL for unsigned numbers.
Theorically there are different way to use the MUL instruction in implicit way or explicit, with BYTE, WORD or DWORD scope, anyway to avoid futile complications I suggest to use these instruction always following this syntax:

MUL Register32, Number
or
MUL Register32, Register32

Where register32 is for extended registers: eax, ebx, ecx, edx, esi, edi

for example:

MUL eax, 3000 ;mutiply the value in eax by 3000, the result will be in eax
MUL ecx, ebx ;multuply the value in ecx by the value in ebx

Note: if the result is greater than 32 bits value, the higher side will be put in edx register.
DIV
IDIV
DIV ecx
DIV ebx
DIV DWORD PTR [MyDivisor]
The DIV and IDIV instructions divide a number in the register eax with the divisor supplied as argument.
There are different way to work in according with the size of divisor but I suggest to use always a 32bit divisor to have always same working mode.

To divide a number you have to move the value in eax, then if you use a 32bit divisor, you have to use CDQ instruction to extend eax in edx register, get in this way the quadword used as dividend. Then you'll type in explicit way the divisor. The quozient will be in eax while in edx you'll find the remainder.

Example.

;we wish dividing the value in ecx register by 1000

mov eax, ecx
cdq ;warning: this clears the edx register
DIV 1000

now in eax we'll have the result (quotient), while in edx the further remainder.

The difference between DIV and IDIV is that IDIV is for signed numbers while DIV for unsigned numbers.
DEC DEC eax
DEC word ptr [MyVar]
DEC bl
The DEC instruction decrements by 1 the value in the given operand.
Example:

DEC eax

the value in ecx will decremented by 1
Note: this instruction set the further Z and S bit status, in according with the final value of the value.
Therefor, if the after the DEC instruction the value become zero, the Z bit status will be set 1 and you can check this condition with a JZ or JNZ conditional jump.
Example

mov ecx, 5
MyLoop:
mov byte ptr [MyArray][ecx], 0
dec ecx
jnz MyLoop

in this case the loop will continue until ecx becomes zero

using the conditional jump:

jns MyLoop

the loop will continue until the ecx value is positive
INC INC ecx
INC al
IND DWORD PTR [MyCounter]
The INC instruction add 1 to value of the operand.
See also the DEC instruction.


The registers in assembly

The registers are like variables with fixed names you can use to perform arithmetic operations or to move values from a memory zone to another.
Some registers have special targets while other may be used for any operation.

The general purpose registers




The EAX, EBX, ECX, and EDX are the general purpose registers.
Looking above image you can see as they are divided in sub-register with less size.
The dword register are those with the "e" in front, where "e" is for "Extended".
These register have 32 bits and they can host value upto 2 billions
The word AX, BX, CD and DX, have 16 bits and they can host value upto 65535 for unsigned values, or in the range -32536 + 32535 for signed values
The byte registers ending with "l" (low) or "h" (high) have only 8 bits and they can host values upto 255 as unsigned, or in the range -128 +127 for signed values.
Looking the image it's important understand that there is an overlapping between register with same literal name, for example eax, ax, al, ah.
This means that, when you type a value in eax, you change also ax, al and ah.
It's also true that changing a little register as AL you change also AX and EAX (but not AH)

The address registers

The following registers:

ESI, EDI, EBP

have not division for bytes like general purpose register EAX, EBX, ECX and EDX, anyway also the address registers have the word and extended form:

ESI : dword 32 bits
SI: word 16 bits
EDI: dword 32 bits
DI: word 16 bits
EBP: dword 32 bits
BP: word 16 bits

As for above speech about overlapping, when you change a word type register you change the value also for his dword type.
For example changing SI you change also ESI ect.
In spite these registers were born for addressing and others (ax,bx,cx,dx) for arithmetic operation, now you can use both for all targets.

ESP, Stack Pointer register

You should take care about ESP register because it used directly by microprocessor to manage its stack memory and you should avoid to change it directly.
The only instruction you can use on ESP is the ADD esp, value to compensate the pushing arguments in C functions.
Example:
if you pass three arguments to a C function, at end you'll have to restore the stack poiter to remove the previous three arguments:


          push eax ; arg3
          push ecx ; arg2
          push edx ;arg1
          call FunctionCStuff
          add ESP, 12


in this case we have three arguments, since each of them is dword of 4 bytes, 4 * 3 = 12, and to restore position of stack pointer we have to add 12 to ESP
See also the description of PUSH/POP instruction in Reduced set of assembly instructions to know table

Remark: the suggestion to not change the ESP register it's not about the simple reading or writing the values POINTED from ESP register.
This operation, on the contrary, will be performed very often.
For example, inside a C++ function the arguments will be passed own pushing them in the stack.
If you type the code to read these argument inside the function, you'll have to do an intensive usage of mov ..., [ESP...] instructions.

To read three arguments from the stack we'll use a code like this:


MainCFunction:
          mov eax, [esp+4] ; first argument
          mov ecx, [esp+8] ;second argument
          mov edx, [esp+12] ; third argument


We can also typing a value in the memory pointed from the ESP register, just knowing well where we are writing these values.
For example, continuing in above sample, we could also use for our targets the esp memory used previously to host the arguments:


          mov dword ptr [esp+4], 0 ;use old memory for first argument for other targets

In all above example we have not changed the ESP register but only used it to point to the stack memory.

Syntax of the Address

In previous Reduced set of assembly instructions to know table, we saw the main instructions but it's necessary explaining better the syntax of an assembly instruction about the mode to compute an address where reading or writing a value.
We can use very complex mixage of registers and + or * (multiplication) but there are some rules and limits about these addressing operations.

The easiest address form is when we'll read (or write) from/to a specific variable we declared, using its name (label)

For example:


WORD MyVariable;

          mov word ptr [MyVariable], 0 ;set 0 in MyVariable
          mov dx, word ptr [MyVariable] ;read the value from MyVariable and copy it to the dx register



Above example was very easy but what will it happen if we had an array, i.e. a list of variables, and we wish access to some field of this array whereby an index?

We could do in this way:


WORD MyArray[80];

          ;we wish read the word in 12th position of MyArray[]
          mov ecx, 12 ;our index
          mov ax, MyArray[ecx*2]

In above example we used the ecx register to store the index and we multiplied by 2 it, in immediate way (with the "*" operator).

When we have to access a memory zone that we have not declared in immediate way (in our source) but we have only a pointer of it, we have to use another register to identifiy the base of that memory zone.
For example, we suppose to have a memory zone where it begins a list of dword, an array where each dword is 4 bytes.
We have not declared this memory zone but we know the absolute address where it begins, for example 4340000h address.
If we wish type 0 in 24th index of this array, we'll use a code like this:

          mov edi, 4340000h ;the start of memory zone outside our plugin
          mov edx, 24 ;index of dword we wish clear
          mov dword ptr [edi][edx*4], 0


Now, we consider the final and more complicated situation.
We know the address of a memory zone where are stored a list of structures.
A structure is a group of different variables, we could name it also like a record.
This structure has a size of 8 bytes.
We wish read the 5^ structure, and in this structure reading the value at 6th byte, i.e. the word that has an offest 6 respect to the begin of that structure.
we should use a code like this to reach this target:

          mov ebx, 512000h ;the begin of memory zone
          mov ecx, 5 ;the index of structure we wish read
          mov ax, word ptr [ebx+6][ecx*8]


The rules of addressing forms

Now after we have seen some examples, we try to describe the effective rules and limits about syntax to compute the addresses.

  1. We cann't use more than TWO registers for a single operand of our instruction.
    Therefor we can use a form like this:
              mov ... , [eax][ecx]
    but not this:
              mov ... , [eax][ecx][edx]
  2. when we compute an address we have to place the register names always in squared parenthesis.
    This is right:
              mov ..., [edx][esi]
    while this is wrong:
              mov ..., [edx] esi
  3. We can multiply a register only by number between 2,4 or 8 but not others
    This is right:
              mov ..., [edx][esi*8]
    while this is not:
              mov ..., [edx*6][esi]
  4. We can multiply only one register by 2,4,8 but not both registers:
    This is right:
              mov ..., [eax][esi*4]
    but this is wrong:
              mov ..., [eax*2][esi*4]

  5. We have a lot of freedom about the position of registers, squared parenthesis and numbers to add, but we should try not exagerate
    These forms are right:
              mov ..., [eax*2][ecx+9]
              mov ..., 9[eax+ecx*8]
              mov ..., [edx+7]+3[esi*2]
              mov ..., [eax+4][ecx*2+5]
    but these not:
              mov ..., [edx]5[ecx*2]
              mov ..., [ecx*2][esi]7
  6. We are forced to specify the size of value we are moving or changing with PTR operator if the other operand is a number (instead of a register)
    For example these instructions are right:
              mov ax, [edx*2][esi]
              mov cl, [ebx+355]
              mov [esi][ecx*2], edx
    while these are wrong because is not possible discover what is the size of value to move:
              mov [ecx], 0
              mov [eax*2][esi], 543
    we could correct above instructions in following ways:
              mov word ptr [ecx], 0
              mov dword ptr [eax*2][esi], 543
  7. We cann't use immediate address in both operands of a mov instruction
    This instruction is wrong:
              mov word ptr [MyAlfaVar], word ptr [MyBetaVar]
    we'll have to use an intermediate instruction to move a value between two cell of memory:
              mov ax, word ptr [MyBetaVar]
              mov word ptr [MyAlfaVar], ax


Declaring our Variables

About our plugin project, we'll use in the most of the cases memory already allocated in tomb4 executable and in tomb_nextgeneration.dll.
In these two situations we'll have not to allocate or declare our variables, we'll get the addresses of these external memory zone and we'll access to them whereby pointer stored in a register.
Anyway in many circustance we'll need of our own variables and memory zone for our procedures and in this case we'll have to declare them in our plugin source.

Syntax of C++ Statements

Since that the in-line assembler of Visual Studio doesn't contemplate native assembly directives we'll have to use the C++ declaration of variables and then we'll be able to use these variables both in assembly and also in C++ procedures.

NOTE: remember that you have to declare your variable always, OUTSIDE of assembly procedures (or asm blocks).
You should declare them in top side of the source, or at least above and outside of the assembly procedure where you'll access to them.
If you wish that your variable was global, i.e. the same for all your assembly procedure, you have to declare it outside of all C functions.
While if you wish having only a local (temporary) variable to use only in that single asm procedure, you should declare it in the C container function of the asm procedure or block.

The statement to declare a variable in C++ is very easy.
In first position there will be the TYPE of variable, followed by the name we assigned to this variable.
If we wish having many of these variables in a contiguous zone (a vector or array) just placing at right a couple o squared parenthesis with inside the number of items we wish.
At end of each C++ declaration we'll type a semicolon ";" character to complete the line.
In spite there were very much types in C, we'll use always one of following few types:

char
Examples:


char LittleNumber;
char MexType[] = "Game Over";

char should be a signed byte, where you can store numbers in the range: -128 +127
However the char is more often used to declare strings of text.

BYTE
Examples:


BYTE MyLittleNumber;
BYTE VetPickedSecrets[99];

The BYTE type is for unsigned numbers in the range 0 +255

short
Examples:

short NumberOfDarts;
short VectLevels[50];

Short is a 16 bits signed number, it can hosting values in the range -32536 +32535

WORD
Examples:

WORD NMediPacks;
WORD MyTable[30*16];

WORD is a 16 bits unsigned number, it can hosting values in the range 0 +65535

int or long
Examples:

int LastYLara;
long ElapsedTime;

The types int and long are synonymous, at least in "our" 32 bits flat assembly that we'll use.
They are 32 bits signed numbers, they can hosting values in the range: -2,147,483,648 +2,147,483,647

DWORD
Examples:

DWORD LastTickFrames;
DWORD CordX;

The DWORD type is an unsigned 32 bits number, it can hosting values in the range 0 + 4,294,967,296

float
Examples:

float FogDistanceMin;
float FogDistanceMax;

While other types we saw were always integers values, with no hosting for digits after point, the float is a floating point value where we can host values with the point and many digits after the point. The range is approximately 3.4E–38 to 3.4E+38
Tomb4 engine uses integers DWORD or int values for coordinates of moveables and statics (X and Z are dwords, Y is long), anyway when tomb4 builds the 3d world those integer values will be converted in float values.
For this reason in some circustances we need to manage also float values.

Initialized and constant variables

When we declare a variable we can also initialize it with some values.
In the case we mean do not change evermore those values, we are using constant values, and it should be weird call them: "variables", anyway we can need them for some compute and in this case we can declare these values directly in the source, both by loading them from some binary file from disk. The choice depends by the quantity of these values, of course.

To initialize a single variable, just using a "=" character after the name of variable, followed by the value to assign to it.
Examples:

WORD MyVar= 54;
float FogDistance = 15.00;

More interesting is when we wish fill a multisize variable, like a vector or an array.
In this case we can type a list of values, divided by commas, for numbers, or store all characters in the quotes, for the text.
Examples:

char MexWarning[] = "Warning: this plugin might be not compatible with current tomb_nextgeneration.dll version. You should use 1.2.2.9 version";

WORD VetGrowingPower[]={1, 1, 1, 2, 2, 3, 3, 4, 5, 7, 9, 11, 15};

int Vettore[40]={2,2,2,4,5,6};

About above examples, we can see that in many circustances we'll not type any value in the squared parenthesis, where we should type the number of items.
The reason is because when you omit the dimension number (the value in squared parenthesis), the compiler will resize our vector to host all values you typed after the "=" character.
Since this solution is ideal in the most of cases, we very often will use this chance.
Anyway theorically we can also set the dimension of our vector and then to type also first values for it.

Using constants whereby #define directive

If we mean use a single variable with a constant value the only reason is to have a mnemonic name to remember that number.
In this case it's better do not use a variable but a constant definition using the #define directive.
Example:

#define HOLD_PISTOLS 1
#define HOLD_REVOLVER 2
#define HOLD_UZI 3
#define HOLD_SHOTGUN 4
#define HOLD_GRENADEGUN 5
#define HOLD_CROSSBOW 6

Above list is really present (you find it in Tomb_NextGeneration.h source of your plugin) and it used to have a descriptive name for those values that correspond to some weapon type in tomb4 engine.
The reason to use mnemonic constant is clear: it's more easy remember a name than a number, and when we'll place these names we'll be able to understand immediatly what they mean, while this doesn't happen using the single number.
For example look this code:

          ;in ax we have the code for current selected weapon
          cmp ax, HOLD_PISTOLS
          jz GoSkip
          cmp ax, HOLD_UZI
          jz GoSkip
          ;perform some code
          ....
          ....
          ....
GoSkip:
          retn

It's very clear what means. Just giving a look to understand that we'll skip (goSkip) some compute, when lara holds a weapon like pistols, or UZI.
Now try the same code with no mnemonic constant names but only numbers

          ;in ax we have the code for current selected weapon
          cmp ax, 1
          jz GoSkip
          cmp ax, 3
          jz GoSkip
          ;perform some code
          ....
          ....
          ....
GoSkip:
          retn

It's very less clear...
For this reason it's very useful giving a name to all specific "codes", i.e. the code is a number that means something of specific.

About the syntax of #define directive please take care about some execption respect to the common variable declaration: we do not use a "=" character to introduce the initial value, and we will neither use a final semicolon ";" character at end of the row.
The prototype for #define is:

#define NAME VALUE

Where the spaces are the only separators.
After this definition you can use NAME in any side of your source, everywhere you could use a number.

Note: in the reality the syntax of #define directive could be more complicated and powerful, but in this document we'll consider only what will be really useful and used in our project.

Declaring structures

A structure is a mix of different variables that we can define as a new multple variable type.
Example:

typedef struct StrTexInfoTr4 {
          WORD Attribute;
          WORD Tail;
          WORD Flags;
          float TopLeft[2];
          float TopRight[2];
          float BottomRight[2];
          float BottomLeft[2];
}robaTexInfo;

In tomb4 there are a lot of structures to host different infos about the game.
The above structure, for example, is that used for the tail infos, i.e. the infos about how placing every texture piece.
Once we declared the type of a new structure, we can using this type like it was a default variable type, like WORD, short, long.
For example following rows show different correct way to use the previous (above) definition of the StrTexInfoTr4:

StrTexInfoTr4* pTex;

StrTexInfoTr4 VectTailInfo[512];

StrTexInfoTr4 CurrentTail;

About above declaring we can see that the first has the pointer operator "*" (asterisk)

StrTexInfoTr4* pTex;

Indeed, while in assembly we use the statement PTR (ex.. mov eax, dword ptr [ecx]) to signal the usage of a pointer, in C language we use the "*" to declare a pointer, while in the C instruction we'll use the "->" couple of charater to access to the pointed value.
For example:

          WORD MyFlags;

          MyFlags = pTex->Flags;

While when the declared structure is not a pointer but the real instance on our source, we can access to its single field using the "." (point) operator, in this way:

          WORD MyFlags;
          float OrgX;
          float OrgY;
          StrTexInfoTr4 CurrentTail;

          CurrentTail.TopLeft[0] = OrgX;
          CurrentTail.TopLeft[1] = OrgY;

Also for a vector of structures the access mode doesnt' change:

          WORD MyFlags;
          // read the flags of the tail structure with index 43
          MyFlags = VectTailInfo[43].Flags;



How accessing to the C structures from assembly

In above examples we saw how declare a structure and how accessing to its fields (single variables that form it) from C language, but how can we perform the same target in assembly language?
This operation is different and also a bit more complicated.

See the How to combine assembly with the C++ skills document, to discover the management of C++ structures from assembly language and many other stuffs about assembly / C language mixing.