Chapter 8

Advanced Procedures

Modified
Overview
Call-by-value and call-by-reference parameters both have merits in given applications and are commonly supported by high level languages. The meaning here is that normally used in C++.

Call-by-value passes a copy of the value for the actual parameter via the stack from the caller to the callee.

Call-by-reference differs in that the offset of the actual parameter is passed via the stack from the caller to the callee.

The implications for the programmer is relatively straightforward and is summarized by the following examples.

Indirection Discussion

Indirection is necessary for implementing parameter passing via the stack. It is useful to first relate our usual notion of indirection to assembly language and the machine's architecture. C++ supports indirection using pointer variables that 1) hold an address that references 2) a variable, hence the indirection. The Intel processor uses registers (Bx, Si, Di, and Bp for 16-bit or any 32-bit) as pointers, these registers can hold an address used for the indirection. The following three examples are identical with variables i, an integer, and bx, a pointer to an integer. In the diagram at right, BX holds the address of variable i.

In the following Assembler program, we can examine the offsets of the variables and determine precisely what is the contents of eBx register. The instruction

Mov  eBx, Offset i
copies the offset of i, which is 32-bit 00000000416 to eBx.
C++ and Assembler Indirection Examples
C++ version                        

#include <iostream.h>             
                                        
void main ( void )                 
{                                      
     int     i;
     int     *ebx;                   
                                       
     i = 5;                      
     ebx = &i;       // Alias i
     cout << *ebx;   // Prints 5
}
 
             
Assembler version

.data
 00000000 	h	dword	3
 00000004	i	dword	5
 00000008	j	dword	9

.code
Main  Proc   

	Mov  eBx, Offset i    ;; Alias i, eBx=0000004
	Mov  eAx, [eBx]       ;; Get i value indirectly

	Call WriteDec         ;; Prints 5

	Mov  eAx, [eBx+4]     ;; Get j value indirectly

	Call WriteDec         ;; Prints 9

      Exit
Main  Endp
      End     Main
Indirection to Access Parameters

By enclosing in square brackets, [], registers such as eBx, eSi, eDi and other 32-bit registers act as pointers to reference memory.

Using 16-bit variables, Bx and Si can point to the data segment, Di a pointer to the extra segment, while Bp can be a pointer to the stack segment. In the above example Bx or Si could be used but not Bp since the variable i is in the data segment and Bp references the stack segment.

In the following assembly program examples, notice the use of eBp as a pointer to access the parameters on the stack. Suppose program fragments of:
 

Indirection to Access Stack Parameters C++ or Assembly 
C++ version

int p( int A, int b)
{
    return A + B;
}

void main( void ) {
     cout << p( 9, 7 );
}
Assembly version                        Stack after Mov eBp, eSp
     Push       7                ;             _________
     Push       9                ; Push  7    |    7    |  <- eBp+12  B
     Call       p                ;            |_________|
     Add        eSp, 8           ; Push  9    |    9    |  <- eBp+8   A
     Call       WriteDec         ;            |_________|
                                 ; Call  p    | Ret Addr|  <- eBp+4
p    proc                        ;            |_________|
     Push      eBp               ; Push  eBp  | Old eBp |  <- eBp+0 = eSp
     Mov       eBp, eSp          ;            |_________|
     Mov       eAx, [eBp+8]      ; eAx = 9       
     Add       eAx, [eBp+12]     ; eAx = 16
     Pop       eBp
     Ret       
p    Endp
Notice that after Mov eBp, eSP the eBp register can be used to access parameters by adding an appropriate displacement to eBp. For example, the instruction:
               Mov  eAx, [eBp+8]
adds 8 to the offset in eBp to indirectly access the stack location holding the A parameter. Because any calls made to this function would place the actual parameters at the same relative stack location, [eBp+8] will always correspond to the formal A parameter. This follows the protocol used by C++ in calling functions and passing value parameters.

 

General Rules for C++ Functions
Using functions requires following specific rules or protocol by both the caller and the callee. In general, the rules can be summarized by the following:
 
Caller Callee
  1. Save any used registers and flags.
  2. Push parameters on the stack in right-to-left order.
  3. Call the function.
  4. Remove parameters from stack by adding to eSp number bytes pushed.
  5. Restore any registers and flags.
  1. Save eBp and any segment registers.
  2. Point eBp to parameters on stack.
  3. Access the parameters on the stack.
  4. Modify Al, Ax, eAx or eDx:eAx to return a result.
  5. Restore eBp and any segment registers.
  6. Return.

 
General Rules for Calling Functions
Assembly version                            
     Caller                                    Callee     
                                                 p  proc            
1.   Push      eDx                             1.   Push      eBp      
     PushfD                                    2.   Mov       eBp, eSp
2.   Push      7      ; B                      3.   Mov       eAx, [eBp+12]  ; B=7   
     Push      9      ; A                      4.   Add       eAx, [eBp+8]   ; A=9
3.   Call      p                               5.   Pop       eBp
4.   Add       eSp, 8 ; Remove 2 parameters    6.   Ret
5.   PopfD                                       p  Endp
     Pop       eDx    
Stack after Mov eBp, eSp
       _________   
     |   eDx   |
     |_________|
     |  Flags  |
     |_________|
B    |    7    |  <- eBp+12
     |_________|
A    |    9    |  <- eBp+8
     |_________|
     | Ret Addr|  <- eBp+4
     |_________|
     | Old eBp |  <- eBp+0 = eSp
     |_________|
 

1.    Question

Draw the stack after Mov eBp, eSp.

What are eAx, eBx, and eCx prior to Pop eBp?

main  proc
     Push       1                
     Push       2                
     Push       3                
     Call       p               
     Add        eSp, 12        
     Call       WriteDec 
     exit
main endp     
                              
p    proc                  
     Push      eBp             
     Mov       eBp, eSp         
     Mov       eAx, [eBp+8]   
     Mov       eBx, [eBp+12]
     Mov       eCx, [eBp+16]
     Pop       eBp
     Ret       
p    endp

 

Call-by-Value versus Call-by-Reference

The following figure illustrates the difference between value (left figure) and reference (right figure) passing in the program examples that follow later. In value passing the function does not have access to the original parameter, in reference passing the reference allows indirect access to the caller's parameters.
 
Call by Value                                                           Call by Reference 
Value

#include <iostream.h>
int X, A, B, C, Highest;

int Max (int Q, int R) 
{
   if (Q > R)
      return Q;
   else return R; 
}

void main(void) {
   A=2; B=3; C=8;
   X = Max( B, C );
   Highest = Max( A, X );
   cout << Highest << endl;          
}
Reference

#include <iostream.h>
int X, A, B, C, Highest;

int Max (int &Q, int &R) 
{
   if (Q > R)
      return Q;
   else return R; 
}

void main(void) {
   A=2; B=3; C=8;
   X = Max( B, C );
   Highest = Max( A, X );
   cout << Highest << endl;          
}

 

Call-by-value Discussion

Requires the value of the actual parameter to be pushed onto the stack by the calling routine. The called function uses the value directly from the stack. The called function cannot alter parameters because it lacks a reference (i.e. the data segment offset of the actual parameter is unknown).
 
Caller - The caller's responsibility is to:
  1. Push the value parameters onto the stack in right-to-left order prior to the function call.
  2. Call the function.
  3. Remove the parameters from the stack by adding to eSp the number of parameter bytes.
Callee - The callee's responsibility is to:
  1. Preserve the segment registers (Ds, Cs, Es, and SS) and eBp, all others can be altered.
  2. Access the parameters on the stack through eBp.
  3. Return using the Ret instruction.

A more complete example of call-by-value implementation is given below using C++ calling methods.
 


Call-by-value C++
C++ version

#include <iostream.h>
int X, A, B, Y, Highest;

int Max (int Q, int R) 
{
  if (Q > R)
     return Q;
  else return R; 
}

void main(void) {
   A=2; B=3; Y=8;
   X = Max( A, B );
   Highest = Max( X, Y );
   cout << Highest << endl;
}
Call-by-value Assembly
.data       
     X          dword   ?
     Y          dword   8
     B          dword   3
     A          dword   2
     Highest    dword   ?
.code

Main Proc            ;; int Max( int Q, int R) {
     Push   B        ;; X=Max(A, B)
     Push   A          
     Call   Max
     Add    eSp, 8   ;; Remove A and B
     Mov    X, eAx

     Push   Y        ;; Highest=Max(X,Y)
     Push   X          
     Call   Max
     Add    eSp, 8   ;;Remove X and Y    
     Mov    Highest, eAx 

     Call   WriteDec

     Exit
Main Endp







;; int Max( int Q, int R)
;;             - Find maximum of two int values
;;  Arguments  - Q, R int Values     
;;  Returns    - eAx contains maximum Q & R on return
;;  Registers Altered - eAx, eBx, eSi            

R     Equ   dword ptr[eBp+12];; Rightmost parameter 
Q     Equ   dword ptr[eBp+8] ;; Leftmost parameter

Max   Proc            

      Push  eBp           ;; Save Caller's Frame Ptr
      Mov   eBp, eSp      ;; New Frame Ptr for Max

;;      ___________       Assuming that the call was
;;     |           |      Z = Max( A, B ); the stack 
;;     |___________|      appears at left after the
;; R   |   B = 3   | <- eBp+12   instruction sequence:
;;     |___________|                                
;; Q   |   A = 2   | <- eBp+8    Push    eBp      
;;     |___________|             Mov     eBp, eSp    
;;     |Return Addr| <- eBp+4                      
;;     |___________|
;;     | Old eBp   | <- eBp = eSp
;;     |___________|
                           ;; {
        Mov     eAx, R     ;;  
.IF     Q > eAx            ;;  if (Q > R)
        Mov     eAx, Q     ;;    eAx = Q;
 .ELSE  Mov     eAx, R     ;;  else eAx = R;
.ENDIF                     ;; } 

        Pop     eBp        ;; Restore Frame Pointer
        Ret                ;; Return maximum in eAx 
Max    Endp                 
       End           Main

2.    Question

Translate the C++ function p to Assembler.

int p( int X, int Y) {
    return X*Y;
}
.data
	A	dword	5
	B	dword 7
.code
main  proc
     Push       A                
     Push       B                
     Call       p               
     Add        eSp, 8        
     Call       WriteDec 
     exit
main endp  

 

Call-by-reference Discussion

The protocol is similar to call-by-value but rather than pushing values onto the stack, references to the parameters (offsets) are pushed.

The protocol requires the data segment offset of the actual parameter to be pushed onto the stack by the calling routine. The called function then accesses the location of the actual parameter using the data segment offset. Since the parameter's location in memory is now accessible by the called function, it can be directly referenced and altered. The responsibility of the caller and callee is nearly identical for value and reference parameter passing, except that offsets are used rather than values to access parameters.
 

Caller - The caller's responsibility is to:
  1. Push the reference parameters (offsets) onto the stack in right-to-left order prior to the function call.
  2. Call the function.
  3. Remove the parameters from the stack by adding to eSp the number of parameter bytes.
Callee - The callee's responsibility is to:
  1. Preserve the segment registers (Ds, Cs, Es, and SS) and eBp, all others can be altered.
  2. Access the parameters on the stack using indirection.
  3. Return using the Ret instruction.

A more complete example of call-by-reference implementation is given below using C++ calling methods.
 


Call-by-reference C++
C++ version

#include <iostream.h>
int X, A, B, Y, Highest;

int Max (int &Q, int &R) 
{
  if (Q > R)
     return Q;
  else return R; 
}

void main(void) {
   A=2; B=3; Y=8;
   X = Max( A, B );
   Highest = Max( X, Y );
   cout << Highest << endl;
}                          
Call-by-Reference Assembly 
.data       
00000000     X          dword   ?
00000004     Y          dword   8
00000008     B          dword   3
0000000C     A          dword   2
00000010     Highest    dword   ?
.code

Main Proc            ;; int Max( int Q, int R) {
     Push   offset B ;; X=Max(A, B)
     Push   offset A          
     Call   Max
     Add    eSp, 8   ;; Remove A and B
     Mov    X, eAx

     Push   offset Y ;; Highest=Max(X,Y)
     Push   offset X          
     Call   Max
     Add    eSp, 8   ;; Remove X and Y    
     Mov    Highest, eAx 

     Call   WriteDec

     Exit
Main Endp

;; int Max( int &Q, int &R)
;;             - Find maximum of two int references
;;  Arguments  - Q, R int reference
;;  Returns    - eAx contains maximum Q & R on return
;;  Registers Altered - eAx, eBx, eSi            

Max   Proc      
      Push  eBp           ;; Save Caller's Frame Ptr
      Mov   eBp, eSp      ;; New Frame Ptr for Max

;;      ___________       Assuming that the call was
;;     |           |      Z = Max( A, B ); the stack 
;;     |___________|      appears at left after the
;; R   |&B=00000008| <- eBp+12   instruction sequence:
;;     |___________|                                
;; Q   |&A=0000000C| <- eBp+8    Push    eBp      
;;     |___________|             Mov     eBp, eSp    
;;     |Return Addr| <- eBp+4                      
;;     |___________|
;;     | Old eBp   | <- eBp = eSp
;;     |___________|
                           
	Mov     	eBx, [eBp+12]
	Mov		eAx, [eBx] 
	Mov 		eSi, [eBp+8]
 .IF [eSi] > eAx 				;; if (Q > R)
	Mov 		eAx, [eSi] 		;;    eAx = Q;
 .Else 					;; else 
	Mov 		eAx, [eBx] 		;;    eAx = R;
 .EndIf 
	Pop     	eBp        		;; Restore Frame
	Ret                		;; Return max in eAx 
Max 	Endp                 
      End           Main                
 

3.    Question

Draw the stack after Mov eBp, eSp.

What are eAx, eSi, and eDi prior to Pop eBp?

.data
00000144	A	dword	5
00000148	B	dword 7
.code
main  proc
     Push       OFFSET A                
     Push       OFFSET B                
     Call       p               
     Add        eSp, 8        
     Call       WriteDec 
     exit
main endp     
                              
p    proc                  
     Push      eBp             
     Mov       eBp, eSp         
     Mov       eSi, [eBp+8]   
     Mov       eDi, [eBp+12]
     Mov       eAx, [eSi]
     Add       eAx, [eDi]
     Pop       eBp
     Ret       
p    endp

4.    Question

Translate the C++ function p to Assembler.

int p( int &X, int &Y) {
    return X*Y;
}
.data
	A	dword	5
	B	dword 7
.code
main  proc
     Push       OFFSET A                
     Push       OFFSET B                
     Call       p               
     Add        eSp, 8        
     Call       WriteDec 
     exit
main endp  

Dynamic (Local) Variables Discussion

In C++ procedures, dynamic variables are generally termed local variables, which are allocated each time the procedure is entered and deallocated on each exit. To allocate storage for a local or dynamic variable, the stack pointer is decremented, once for a byte variable, twice for a word variable, 200 times for an array of 100 words, etc. Deallocation is by restoring the stack pointer, by Mov eSp, eBp. References to the dynamic variable are still relative to eBp though the displacement is normally negative rather than positive. Generally, dynamic variables use requires all the same steps used when entering or exiting a procedure, summarized below:
     Entering
     1)   Push eBp        
     2)   Mov  eBp, eSp
     3)   Sub  eSp, #          # is the number of bytes to allocate for dynamic variables
     4)   Push Registers       Save any registers

     Exiting
     4)   Pop  Registers       Restore any registers
     3)   Mov  eSp, eBp       Restore eSp which deallocates dynamic variables
     2)   Pop  eBp
     1)   Ret

Dynamic (Local) Variables C++ and Assembler

int a = 5, b = 6;

void main(void) {
 cout << addup(a, b);	
}
.data
	a	dd	5
	b	dd	6
.code
main	proc	
	push	offset b
	push	a
	call	addup
	add	eSp, 8  
	call	WriteDec 
	Exit
main	endp
 int addup(int x, int &y)
 {	int sum;
	sum = y;
	sum = sum + x;
	return sum;
 }
 addup  PROC 	
	push	eBp
	mov	eBp, eSp
	sub	eSp, 4		; int sum
      mov	eBx, [eBp+12]	; sum = y;
	mov	eAx, [eBx]
	add	eAx, [eBp+8]	; sum = sum + x
	mov	[eBp-4], eAx
	mov	eAx, [eBp-4]	; return sum
	mov	eSp, eBp
	pop	eBp
        ret
 addup  ENDP

Dynamic (Local) Variables C++
C++ version

int  eAx;
#include <iostream.h>
int Factorial( int N ) {    
  int i;
  eAx = 1;   
  for (i=1; i<=N; i++) 
      eAx = eAx * i;
  return eAx;
}

void main (void) {
   cout << Factorial(6);
}
Dynamic Variables Assembly
.code

Main Proc    	      ; void main() {

     Push   6		
     Call   Factorial 	 
     Add    eSp, 4            

     Call   WriteDec	;  cout << Factorial(6);

     Exit
Main Endp		; }
;; int Factorial (int N); // Calculate N! 
;;  Parameters        - N positive integer value 
;;  Returns           - eAx return factorial value
;;  Registers Altered - eAx
;;      ___________           Assuming the call
;; N   |     6     | <- eBp+8 was Factorial( 6 );
;;     |___________|          the stack appears  
;;     |Return Addr| <- eBp+4 after the instruction
;;     |___________|          sequence of:  
;;     | Old eBp   | <- eBp+0   
;;     |___________|                              
;; i   |    ??     | <- eBp-4         Push   eBp   
;;     |___________|                  Mov    eBp,eSp
;; eDx |    ??     | <- eBp-8         Sub    eSp, 4
;;     |___________|                  Push   eDx 
;; Flag|    ??     | <- eBp-12 = eSp  PushfD
;;     |___________|
Factorial Proc    ;;int Factorial(int N)

  N       Equ  dword ptr [eBp+8]
  i       Equ  dword ptr [eBp-4]

          Push eBp
          Mov  eBp, eSp  ;;  ______________
          Sub  eSp, 4    ;; | Allocate i   |
                         ;; | 1 double word|
          Push eDx       ;; |______________| 
          PushfD               

          Mov  eAx, 1    ;; Factorial = 1
          Mov  i, 1
Fora:     Mov  eDx, i
          Cmp  eDx, N    ;; for(i=1;i<=N;i++)
	    Jbe  Doa
          Jmp  EndFora   ;;  Factorial*=i
     Doa:                ;;   
          Mul  i              
          Inc  i
          Jmp  Fora          
EndFora:                       
          PopfD                
          Pop  eDx        
                        ;;  ____________
          Mov  eSp, eBp ;; |Restore eSp |
          Pop  eBp      ;; |deallocate i|
          Ret           ;; |____________|     
Factorial Endp        
          End  Main

5.    Question

Draw the stack after Mov eSp, eBp.

What are eAx, eSi, and eDi prior to Pop eBp?

.data
00000144	A	dword	5
00000148	B	dword 7
.code
main  proc
     Push       A                
     Push       OFFSET B                
     Call       p               
     Add        eSp, 8        
     Call       WriteDec 
     exit
main endp     
                              
p    proc                  
     Push      eBp             
     Mov       eBp, eSp
     Sub       eSp, 4         
     Mov       eSi, [eBp+8]   
     Mov       eDi, [eBp+12]
     Mov       eAx, [eSi]
     Add       eAx, eDi
     Mov	   [eBp-4], eAx
     Mov       eSp, eBp
     Pop       eBp
     Ret       
p    endp

6.    Question

Translate the C++ function p to Assembler.

int p( int X, int Y) {
    int Z;

    Z = 3;
    return X+Y+Z;
}

.data
	A	dword	5
	B	dword 7
.code
main  proc
     Push       A                
     Push       B                
     Call       p               
     Add        eSp, 8        
     Call       WriteDec 
     exit
main endp  

 

Double Indirection

The swap function illustrates an appropriate use of reference parameters, that is when the parameter requires a side-effect. It also illustrates that call-by-reference parameters have an access cost of two indirections. To access the right-most variable B in the following example requires:

  1. Mov eBx, [eBp+12]     Access the reference parameter on the stack, the offset of B is 00008004.
  2. Mov eAx, [eBx]            Access the variable referenced, the variable B value is 6.
Call-by-Reference Swap Example
C++

void swap( int &L, int &R) {   
   int T;
   T = L;
   L = R;
   R = T;
}

void main(void) {
     int A = 5, B = 6;
     swap( A, B);
}


 Stack after Sub eSp, 2
   _______ 
R | 8004  | eBp+12   B
L | 8000  | eBp+8    A
  | ret   | eBp+4
  |Old eBp| eBp+0
T | ????  | eBp-4
 
 Data definitions
   ____  Offset
  || 00008000  A  DD  5
  || 00008004  B  DD  6







Assembly

.data       
     A          dd   5
     B          dd   6
.code

swap  Proc  
      Push  eBp
      Mov   eBP, eSp
      Sub   eSp, 4       ; int T    
      
      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    
swap  Endp
main  Proc  

      Push  Offset B
      Push  Offset A
      Call  swap
      Add   eSp, 8

      Push  0            
      Call  ExitProcess    
main  Endp
      End   main
 

  8.5     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	WriteDec 
	invoke	addup, a, b

	call	      WriteDec
 int addup(int x, int y)
 {
	return x + y;
 }
 addup  PROC 
	push	eBp
	mov	eBp, eSp
      mov   eAx, [eBp+8]
      add   eAx, [eBp+12]
	pop	eBp
      ret
 addup  ENDP
 addup  PROC    C, x:DWORD, y:DWORD
        mov     eAx, x
        add     eAx, y
        ret
 addup  ENDP

7.    Question

Translate the C++ function p to Assembler using invoke/proc.

int p( int X, int Y, int Z) {
    return X+Y+Z;
}
int a = 3, b = 35, c=4;
 cout << addup(a, b, c);	

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  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	WriteDec 
	invoke	addup, ADDR a, ADDR b

	call	      WriteDec
 int addup(int &x, int &y)
 {
	return x + y;
 }
 addup  PROC 
	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    C, x:PTR DWORD, y:PTR DWORD
        mov     eBx, x
	  mov     eAx, [eBx]
        mov     eBx, y
	  add     eAx, [eBx]
        ret
 addup  ENDP
 

8.    Question

Translate the C++ function p to Assembler using invoke/proc.

int p( int &X, int &Y, int Z) {
    return X+Y+Z;
}
int a = 3, b = 35, c=4;
 cout << addup(a, b, c);	

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  C,  x : DWORD,  y : DWORD
swap    PROTO  C,  L : PTR DWORD,  R : 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

int addup (int x, int y )
void swap ( int &L, int &R)

or these corresponding procedure declarations

addup   PROC  C,  x : DWORD, y : DWORD
swap    PROC  C, L : PTR DWORD, R : 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	WriteDec 
	invoke	addup, a, b

	call	WriteDec
 int addup(int x, int y)
 {	int sum = x + y;
	return sum;
 }
 addup  PROC 
	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 C, x:DWORD, y:DWORD

	local	sum : DWORD

	mov	eAx, x
	add   eAx, y
	mov	sum, eAx
	mov	eAx, sum
      ret
 addup  ENDP

9.    Question

Translate the C++ to Assembler.

int A = 10;
int B = 15;

A = p( A, B);

int p( int X, int &Y) {

    int Z = 5;

    return X+Y+Z;
}

Arrays

 int A[] = {1, 2, 3, 4, 5,6};
 cout << sum( A );	
.data       
     A          dword	1, 2, 3, 4, 5, 6
.code 
	invoke	sum, addr A

	call	      WriteDec
 int sum( int &X )
 {	eAx = 0;
      for( eCx=0; eCx < 6; eCx++)
         eAx = X[ eCx ] + eAx;
	return eAx;
 }
sum	Proc  C, X:PTR DWORD

      Mov   eBx, X 	
      Mov   eAx, 0
      Mov	eSi, 0
 @for:
      Cmp	eSi, 6
      Jl	@do
      Jmp	@endfor
  @do:	
	Add	eAx, [eBx+eSi*4]
      Inc	eSi
      Jmp	@for
 @endfor:
      Ret    
sum  Endp

10.    Question - Translate the C++ to Assembler.

int A[5];

p( A, 5);
void p( int X[], int n) {
    int i;
    for( i=0; i<n; i++)
        X[ i ] = 10;
}

 

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

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

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

      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	
	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  C, 
            L:PTR DWORD, 
            R: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