分享

Declaring Procedure Parameters with the PROC Directive Calling Procedures with the INVOKE Directive

 迎风初开 2013-12-06

Declaring Procedure Parameters with the PROC Directive
Calling Procedures with the INVOKE Directive

With the PROC directive, you can specify registers to be saved, define parameters to the procedure, and assign symbol names to parameters (rather than as offsets from eBp). This section describes how to use the PROC directive to automate the parameter-accessing techniques described earlier.

Value - The following illustrates pass-by-value parameters in C as implemented by hand pushing parameters and access through eBp and using the combined PROC and INVOKE directive. Note that the INVOKE pushes parameters on the stack in right-to-left order.

 int a = 3, b = 35;
 cout << addup(a, b);	
	push	b
	push	a
	call	addup
	add	eSp, 8  
	call	PutDec 
	invoke	addup, a, b
	call	PutDec
 int addup(int x, int y)
 {
	return x + y;
 }
 addup  PROC NEAR
	push	eBp
	mov	eBp, eSp
        mov     eAx, [eBp+8]
        add     eAx, [eBp+12]
	pop	eBp
        ret
 addup  ENDP
 addup  PROC NEAR C, x:DWORD, y:DWORD
        mov     eAx, x
        add     eAx, y
        ret
 addup  ENDP

Reference - If the arguments for a procedure are pointers, the assembler does not generate any code to get the value or values that the pointers reference; your program must still explicitly treat the argument as a pointer. In the following example, even though the procedure declares the parameters as near pointers, you must code two MOV instructions to get the values of the parameters. The first MOV gets the address of the parameters, and the second MOV gets the parameter.

 int a = 3, b = 35;
 cout << addup(a, b);	
	push	offset b
	push	offset a
	call	addup
	add	eSp, 8  
	call	PutDec 
	invoke	addup, ADDR a, ADDR b
	call	PutDec
 int addup(int &x, int &y)
 {
	return x + y;
 }
 addup  PROC NEAR
	push	eBp
	mov	eBp, eSp
	mov	eBx, [eBp+8]
        mov     eAx, [eBx]
        mov	eBx, [eBp+12]
	add	eAx, [eBx]
	pop	eBp
        ret
 addup  ENDP
 addup  PROC NEAR C, x:NEAR PTR DWORD, y:NEAR PTR DWORD
        mov     eBx, x
	mov	eAx, [eBx]
        mov	eBx, y
	add     eAx, [eBx]
        ret
 addup  ENDP

Procedure Prototypes

Prototypes perform the same function as prototypes in C and other high-level languages. A procedure prototype includes the procedure name, the types, and (optionally) the names of all parameters the procedure expects. Prototypes usually are placed at the beginning of an assembly program or in a separate include file so the assembler encounters the prototype before the actual procedure. Prototypes enable the assembler to check for unmatched parameters and are especially useful for procedures called from other modules and other languages.

The following example illustrates how to define and then declare two typical procedures.

addup   PROTO NEAR C,  x : DWORD,  y : DWORD
swap    PROTO NEAR C,  L : NEAR PTR DWORD,  R : NEAR PTR DWORD

When you call a procedure with INVOKE, the assembler checks the arguments given by INVOKE against the parameters expected by the procedure. If the data types of the arguments do not match, MASM reports an error or converts the type to the expected type.

Calling Procedures with INVOKE

INVOKE generates procedure calls automatically which:

  1. Converts arguments to the expected types.

  2. Pushes arguments on the stack in the correct order specified by the model. C and stdcall models parameters are pushed right-to-left order.

  3. Cleans the stack of parameters when the procedure returns.

Procedures with these prototypes

addup   PROTO NEAR C,  x : DWORD, y : DWORD
swap    PROTO NEAR C, L : NEAR PTR DWORD, R : NEAR PTR DWORD

or these procedure declarations

addup   PROC NEAR C,  x : DWORD, y : DWORD
swap    PROC NEAR C, L : NEAR PTR DWORD, R : NEAR PTR DWORD

can be called with INVOKE statements as:

INVOKE  addup,   eAx,  eBx
INVOKE  swap, ADDR A, ADDR B

Using Local Variables

In high-level languages, local variables are visible only within a procedure and are usually stored on the stack. In assembly-language programs, you can also have local variables. The LOCAL directive automatically allocates local variable space on the stack at the start of the procedure, defines a reference to the variable by its position in the stack, at the end of the procedure the local variable is deallocated by restoring the stack pointer.

In the following, the local variable sum is allocated by Sub eSp, 4 and deallocated by restoring the stack pointer in Mov eSp, eBp. Note that the local statement can also be used without the invoke.

 int a = 3, b = 35;
 cout << addup(a, b);	
	push	b
	push	a
	call	addup
	add	eSp, 8  
	call	PutDec 
	invoke	addup, ADDR a, ADDR b
	call	PutDec
 int addup(int x, int y)
 {	int sum = x + y;
	return sum;
 }
 addup  PROC NEAR
	push	eBp
	mov	eBp, eSp
	sub	eSp, 4
	mov	eAx, [eBp+8]
        add	eAx, [eBp+12]
	mov	[eBp-4], eAx
	mov	eAx, [eBp-4]
	mov	eSp, eBp
	pop	eBp
        ret
 addup  ENDP
 addup  PROC NEAR C, x:DWORD, y:DWORD
	local	sum : DWORD
	mov	eAx, x
	add     eAx, y
	mov	sum, eAx
	mov	eAx, sum
        ret
 addup  ENDP

A slightly more challenging example that illustrates the added clarity of use of PROC/INVOKE/LOCAL directives.

 int a = 5, b = 6;
 swap(a, b);	
.data       
     	A	dd   5
	B	dd   6
.code 

main  	Proc  Near
      	push	offset B
	push	offset A
	call	swap
	Add	eSp, 8

	Push  	0            
	Call  	ExitProcess    
main	Endp
      	End   	main
.data       
     A          dd   5
     B          dd   6
.code 

main  Proc  Near
      invoke  swap, addr A, addr B

      invoke  ExitProcess, 0   
main  Endp
      End   main
 void swap(int &L, int &R)
 {	int T;
	T=L;
	L=R;
	R=T;
 }
 swap  	proc	near
	push	eBp
	mov	eBp, eSp
	sub	eSp, 4

      	Mov   	eBx, [eBp+8]	; T = L
      	Mov   	eAx, [eBx]
      	Mov   	[eBp-4], eAx

      	Mov   	eBx, [eBp+8] 	; L = R
      	Mov   	eSi, [eBp+12]
      	Mov   	eAx, [eSi]
      	Mov   	[eBx], eAx

      	Mov   	eAx, [eBp-4] 	; R = T
      	Mov   	eSi, [eBp+12]
      	Mov   	[eSi], eAx

	mov	eSp, eBp
	pop	eBp
        ret
 addup  ENDP
swap  Proc  Near C, 
		L:NEAR PTR DWORD, R:NEAR PTR DWORD
      LOCAL T : DWORD    
      Mov   eBx, L 	; T = L
      Mov   eAx, [eBx]
      Mov   T, eAx

      Mov   eBx, L 	; L = R
      Mov   eSi, R
      Mov   eAx, [eSi]
      Mov   [eBx], eAx

      Mov   eAx, T 	; R = T
      Mov   eSi, R
      Mov   [eSi], eAx

      Ret    
swap  Endp

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多