PROWAREtech

articles » current » assembly » x86 » tutorial » page-08

Intel IA-32 Assembly Tutorial - A Guide to the Basics of x86 Assembly - Page 08

Language Elements (The PROC and PROTO Directives, Variable Types, More on Instructions: LEA).

The PROC Directive

A procedure is declared using the PROC and ENDP directives. The RET instruction ends the procedure.

	sample_proc PROC
		.
		.
		.
		ret
	sample_proc ENDP

The CALL instruction calls a procedure by directing the processor to begin execution at a new memory location. The procedure uses a RET instruction to bring the processor back to the point in the program where the procedure was called. The CALL instruction pushes its return address on the stack and copies the called procedure's address into the instruction pointer. When the procedure is ready to return, its RET instruction pops the return address from the stack into the instruction pointer register.

The USES operator is used with the PROC directive and lists the names of all the registers modified within a procedure. It automatically pushes and pops instructions specified.

	sample_proc PROC USES ecx edx
		inc edx
		add ecx,edx
		ret
	sample_proc ENDP

Local variables are declared using the LOCAL directive. It must be used immediately following the PROC directive.

	qsort PROC USES eax ebx ecx edx
		LOCAL temp:SDWORD, left:PTR SDWORD, right:PTR SDWORD, pivot:SDWORD, tempArray[5]:BYTE
		.
		.
		.
		ret
	qsort ENDP

Variable types consist of BYTE, SBYTE, WORD, SWORD, DWORD, SDWORD, FWORD, QWORD, TBYTE, PTR BYTE, PTR SBYTE, PTR WORD, PTR SWORD, PTR DWORD, PTR SDWORD, PTR QWORD and PTR TBYTE.

Local variables are created on the runtime stack. The STACK directive reserves stack space. Make sure there is enough stack space to hold the allocated local variables. For example:

	.stack 8192

Stack parameters comes is two varieties: register parameters and stack parameters. Register parameters are faster but make the code harder to read/more cryptic.

Values passed to a procedure are arguments.

When values are received by the called procedure, they are parameters.

High-level languages use the runtime stack for parameters.

The following little procedure increments its parameter by one and then returns its value. The parameter is passed by reference.

.386

.model FLAT

PUBLIC	_increment@4

_INCR	SEGMENT
_increment@4 PROC NEAR

	mov eax, [esp+4]
	inc DWORD PTR [eax]
	mov eax, DWORD PTR [eax] ;C/C++ expects the return value to be stored in EAX
	ret 4 ;add 4 to the stack pointer (ESP); this is STDCALL calling convention

_increment@4 ENDP
_INCR	ENDS
END

The previous procedure would be declared like this in C++:

extern "C" int __stdcall increment(int &x);

Here is the same procedure using a __cdecl calling convention.

.386

.model FLAT

PUBLIC	_increment

_INCR	SEGMENT
_increment PROC NEAR

	mov eax, [esp+4]
	inc DWORD PTR [eax]
	mov eax, DWORD PTR [eax]
	ret 0 ;stack pointer (ESP) will have 4 added to it by the calling procedure; this is CDECL calling convention

_increment ENDP
_INCR	ENDS
END

C++ declaration:

extern "C" int increment(int &x);

This procedure adds three 32-bit integers:

TITLE C++ declaration: 'extern "C" int add(int a, int b, int c);'

.386

.model FLAT

PUBLIC	_add

_TEXT	SEGMENT
_add PROC NEAR

	; this could be simplified
	mov edx, [esp+4]  ;first parameter: a
	mov ecx, [esp+8]  ;second parameter: b
	mov ebx, [esp+12] ;third parameter: c
	xor eax, eax
	add eax, edx
	add eax, ecx
	add eax, ebx

	ret  0

_add ENDP
_TEXT	ENDS
END

This procedure multiplies two 32-bit integers:

TITLE 'extern "C" int multiply(int a, int b);'
.386

.model FLAT

PUBLIC	_multiply

_TEXT	SEGMENT
_multiply PROC NEAR

	mov  eax, DWORD PTR [esp+4]
	imul eax, DWORD PTR [esp+8]

	ret  0
	
_multiply ENDP
_TEXT	ENDS
END

The PROTO Directive

The PROTO directive creates a prototype for an existing procedure. A prototype, just like in C/C++, declares a procedure's name and parameter list.

	ArraySum PROTO, ptrArray:PTR DWORD, sizArray:DWORD

	ArraySum PROC, ptrArray:PTR DWORD, sizArray:DWORD
	.
	.
	.
	ArraySum ENDP

Example calling some Windows API functions using CALL (these use STDCALL calling convention):

.686
.model flat,stdcall
.stack 8192
.data

WndMsg BYTE "Testing CALL instruction.",0
WndTtl BYTE "TEST",0

.code

MessageBoxA PROTO, hwnd:DWORD, message:PTR BYTE, windowTitle:PTR BYTE, options:DWORD
GetActiveWindow PROTO

pop PROC

	call GetActiveWindow
	push 0                ;options (0 = just an OK button)
	mov ebx,offset WndTtl
	push ebx
	mov ebx,offset WndMsg
	push ebx
	push eax              ;GetActiveWindow returned HWND value in EAX
	call MessageBoxA

	ret
pop ENDP
END

The LEA (load effective address) instruction returns the offset of any type of indirect operand.

The assembler OFFSET operator only returns constant assembly time offsets.

LEA gets the offset at runtime.

	increment PROC
		LOCAL num:DWORD

		lea esi,num        ;load effective address (get a pointer to num)
		xor eax,eax        ;zero out EAX
		mov [esi],eax      ;move zero into num
		inc DWORD PTR[esi] ;increment num using its address
	increment ENDP
<<<[Page 8 of 15]>>>

This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
CLOSE