Chapter 8

Advanced Procedures
Recursion

Modified
© Ray Wisman
Overview
In general, recursion requires that each activation of a function have its own data, usually consisting of the parameters to the function and any localized data such as local variables. This data is often termed the activation record of the function. A recursive function that had been called or activated five times would have five activation records, one for each activation. Activation records are allocated on entry to a function and deallocated on exit, most easily implemented by use of the stack, pushing parameters onto the stack prior to a function call and dynamically allocating and deallocating localized data on entry and exit to the function. This is consistent with the parameter passing methods of call-by-value and call-by-reference developed earlier and followed by most recursive languages (e.g. Pascal, C++). The following examples are consistent with these methods.

Discussion

A series of examples below serve to illustrate the details of recursion and its common elements. The general assembly language elements of the calling a recursive function are the same as calling a non-recursive function as in the following:
Calling a procedure
invoke   Function, Parameters    
Push      Parameters

Call      Function

Add       eSp, <Number of Parameter bytes>
 

The recursive function differs from non-recursive functions only in that the recursive function contains a call to itself.

Recursive function
Function  Proc     C, Parameters

  .IF     Terminating Condition is true

          base case result

   .ELSE  recursive case result

          invoke   Function, Parameters    
  .ENDIF          
          Ret
Function Endp
Function Proc      
          Push      eBp
          Mov       eBp, eSp

  .IF     Terminating Condition is true
          base case result
   .ELSE  recursive case result
          Push      Parameters
          Call      Function
          Add       eSp, <Number of Parameter bytes>
  .ENDIF          
          Pop       eBp
          Ret
Function Endp
 

  Factorial Example

The recursive definition of the factorial function is:
0! = 1
n! = n * (n-1)!
Note that:
(n-1)! = (n-1) * (n-2)!
so for:
n = 3    3! = 3*2!
n = 2    2! = 2*1!
n = 1    1! = 1*0!
n = 0    0! = 1 by definition
Factorial Example
#include <iostream.h>

int Fact(int N) {
        if( N <= 1) return 1;        // base case result
        else return N*Fact(N-1);     // recursive case result
}
                    
void main(void) {
        cout << Fact(3);
}

Title Fact - Recursive function with one parameter, n!
.code

Fact     Proc  Near C, N:dword  ;; int Fact(int N) {    
  
 .IF     N <= 1                 ;; If (N <= 1)
	   Mov    eAx, 1          ;;      return 1
 .ELSE				   ;; Else return N*Fact(N-1);
         Mov    eCx, N          
         Dec    eCx                   

         invoke Fact, eCx            

         Mul  N                 
 .ENDIF
         Ret                        
Fact     Endp

Main  Proc     near

      invoke   Fact, 3         ;;  eAx = Fact( 3 );

      Call     WriteDec        ;; cout << eAx;
      invoke   ExitProcess, 0
Main  Endp
	End	   Main
;;      ___________             
;;     |   0003    | <- eBp+8    
;;     |___________|            
;; 1st |Return Addr| <- eBp+4   
;; Call|___________|            
;;     |  eBp      | <- eBp       
;;     |___________|_________ N=3
;;     |   0002    | <- eBp+8     
;;     |___________|
;; 2nd |Return Addr| <- eBp+4
;; Call|___________|
;;     |  eBp      | <- eBp
;;     |___________|_________ N=2
;;     |   0001    | <- eBp+8
;; 3rd |___________|
;; Call|Return Addr| <- eBp+4
;;     |___________|
;;     |  eBp      | <- eBp
;;     |___________|__________N=1

 

Example

Summing Example
#include <iostream.h>

int Sum(int N) {
        if( N == 0) return 0;        // base case result
        else return N+Sum(N-1);      // recursive case result
}
                    
void main(void) {
        cout << Sum(3);
}
.code

Sum     Proc  C, N:dword        ;; int Sum(int N) {    
  
 .IF     N == 0                 ;; If (N == 0)
	   Mov    eAx, 1          ;;      return 0
 .ELSE				   ;; Else return N+Sum(N-1);
         Mov    eCx, N          
         Dec    eCx                   

         invoke Sum, eCx            

         Add    eAx, N                 
 .ENDIF
         Ret                        
Fact     Endp

Main  Proc     near

      invoke   Sum , 3         ;;  eAx = Sum( 3 );

      Call     WriteDec        ;; cout << eAx;
      invoke   ExitProcess, 0
Main  Endp
	End	   Main

Question

  1. Write a function in C++ (or method in Java) using Recursion to print numbers from n to 0.
     

  2. Write a function in C++ (or method in Java) using Recursion to print numbers from 0 to n.
     

  3. Write a function in Assembler using Recursion to print numbers from n to 0. 
     

Power Example
Power Function
#include <iostream.h>

int Power(int X, int N) {
        if( N == 0 ) return 1;
        else return Power( X, N-1) * X;
}
                    
void main(void) {
        cout << Power(5,2);
}
Title    Power - Recursive function with two parameters, x^n
.code

Power   Proc   Near C, X:dword, N:dword
                                ;; int Power( int X, int N) {  
 .IF     N == 0                 ;; if (N == 0)
	   Mov    eAx,            ;;    return 1
 .ELSE
         Mov    eCx, N          ;; else        
         Dec    eCx             ;;   return Power( X,N-1 ) * X

         invoke Power, X, eCx                    

         Mul    X               ;;    eAx = eAx * X
 .ENDIF
         Ret                            
Power    Endp

Main  Proc     near

      invoke  Power, 5, 2       ;; eAx = Power( 5, 2 );

      Call     WriteDec         ;; cout << Ax
      invoke   ExitProcess, 0
Main  Endp
      End      Main
;;      _____________________
;;  N  |     2     | <- eBp+12
;;     |___________|               
;;  X  |     5     | <- eBp+8       
;;     |___________|               
;;     |Return Addr| <- eBp+4       
;; 1st |___________|               
;; Call|   eBp     | <- eBp+0  X=5
;;  ___|___________|___________N=2
;;  N  |     1     | <- eBp+12
;;     |___________|
;;  X  |     5     | <- eBp+8  
;;     |___________|
;;     |Return Addr| <- eBp+4
;; 2nd |___________|
;; Call|   eBp     | <- eBp+0  X=5
;;  ___|___________|___________N=1
;;  N  |     0     | <- eBp+12
;;     |___________|
;;  X  |     5     | <- eBp+8      
;;     |___________|
;;     |Return Addr| <- eBp+4
;; 3rd |___________|
;; Call|   eBp     | <- eBp+0  X=5
;;  ___|___________|___________N=0
 

Question

  1. Write a function in C++ using Recursion to check if a number n is prime. (You have to check whether n is divisible by any positive number less than n,  other than 1).
     

  2. Write the same function in Assembler.

 

Fibonacci Recursive functions with more than one point of recursion may require that several function call results be combined. For example, Fibonacci's algorithm is defined as:
Fibonacci(1) = 1
    Fibonacci(2) = 1
    Fibonacci(n) = Fibonacci(n-1) + Fibonacci(n-2)
There are two recursive calls with each producing a result. The result of the first call must be combined with the result of the second call using addition. For n=3:
    Fibonacci(3) = Fibonacci(2) + Fibonacci(1) = 1 + 1
The result of Fibonacci(2) must be temporarily saved, Fibonacci(1) computed, and the results of the two calls combined using addition. In Assembler:
 
invoke  Fibonacci, 2

Push    eAx           ; Save Fibonacci(2) result

invoke  Fibonacci, 1

Pop     eBx           ; Restore Fibonacci(2) result
Add     eAx, eBx      ; Combine Fibonacci(2) + Fibonacci(1)
The following figure gives the C++ algorithm and the results generated from each level of the recursive calls.
 
Fibonacci in C++ and Recursive Call Tree and Assembler
#include <iostream.h>

int Fib(int n) {
    if (n == 1) return 1;
    if (n == 2) return 1;
    return Fib(n-1) + Fib(n-2);
}	
void main(void) {
    cout << Fib(5);
}

Tree of recursive calls for Fib(5)

 
 
 
Title     Recursive Fibonacci
.code

Fib       Proc      Near C, N:dword  ;; int Fib(int N) 
 .IF      N <= 2 
          Mov       eAx, 1       ;; return 1 
 .ELSE
          Mov       eCx, n       ;; return Fib(n-1)+Fib(n-2) 
          Sub       eCx,1 

          invoke    Fib, eCx 
          Push      eAx          ;; save Fib(n-1) 

          Mov       eCx, n 
          Sub       eCx, 2

          invoke    Fib, eCx     ;; Fib(n-2) 

          Pop       eBx          ;; recover Fib(n-1) 
          Add       eAx, eBx  
 .ENDIF  
          Ret  
Fib       Endp 

Main      Proc     near

          invoke   Fib, 5 

          Call     WriteDec        ;; cout << Fib ( 5 ); 
          invoke   ExitProcess, 0  
Main      Endp

          End      Main

Exercise
  1. Write a function in C++ using recursion to compute binomial coefficients C(n, k) using the recursive definition:

    C(n,n) = 1
    C(n,0) = 1
    C(n,k) = C(n-1, k-1) + C(n-1,k) for (0<k<n) and n>1

  2. Write the same function in Assembler.

Ackermann's Example
Some algorithms require nested recursion where the result of one function call is a parameter to another function call. For example Ackermann's function is defined as:
A(m,n) = n + 1 if m == 0
A(m,n) = A(m-1, 1) if m > 0 && n == 0
A(m,n) = A(m-1, A(m, n-1)) if m > 0 && n > 0
Note functions return a result in the eAx register. The inner nested function result in eAx should be pushed onto the stack as a parameter to the outer nested function. For m=9 and n=7:
 
; A(9,7) == A(8, A(9,6))

  invoke  A, 9, 6     ;A(9,6)

  invoke  A, 8, eAx   ; A(8, A(9,6))

It is particularly interesting, for computational reasons, the depth of recursion due to the nested expression:

Ackermann( m-1, Ackermann(m, n-1))
For example, the execution of Ackermann(3,2) produces recursion of depth 30.
 
Ackermann's Function
#include <iostream.h>

int Ackermann(int m, int n) {
        if(m==0) return n+1;
        else if (m>0 && n==0) return Ackermann(m-1,1);
             else if (m>0 && n>0) return Ackermann( m-1, Ackermann(m, n-1));
}
                    
void main(void) {
        cout << Ackermann(3,2);
}
Title     Ackermann's function - Recursive function with multipule recursions
.code
Ackermann Proc  Near C, m:dword, n:dword 
                                   ; int Ackermann(int m, int n) {
 .IF		m == 0               	; if (m == 0)
		Mov  eAx, n   		;   return n+1;
		Inc  eAx                  
 .ELSE                             ;  else
    .IF     m > 0 && n == 0        ;   if (m > 0 && n == 0)                      
            Mov     eCx, m
            Dec     eCx
			              	;     return Ackermann(m-1, 1);
            invoke  Ackermann, eCx, 1
    .ELSE                          ;   else   
     .IF    m > 0 && n > 0         ;    if (m > 0 && n > 0)
                                   ;      return Ackermann(m-1, Ackermann(m, n-1))
            Mov     eCx, n       
            Dec     Cx             ; eAx = Ackermann(m, n-1) 

            invoke  Ackermann, m, eCx
 
            Mov     eCx, m
            Dec     eCx         
				        	; eAx = Ackermann(m-1, eAx)
            invoke  Ackermann, eCx, eAx
     .ENDIF
  .ENDIF
 .ENDIF
          Ret                      
Ackermann Endp

Main      Proc     near

          invoke  Ackermann, 3, 2 	;   eAx := Ackermann( 3, 2 );

          Call     WriteDec        ;   cout << eAx
          invoke   ExitProcess, 0
Main      Endp

          End      Main
 

Exercise - http://en.wikipedia.org/wiki/McCarthy_91_function

McCarthy 91, defined by computer scientist John McCarthy as a test case for formal verification within computer science.

The results of evaluating the function are given by M(n) = 91 for all integer arguments n ≤ 101, and M(n) = n − 10 for n > 101.

Implement the following M function in Assembler.

McCarthy 91 Function
#include <iostream.h>

int M(int n) {
        if( n > 100) return n-10;
        else if (n <= 100) return M( M(n+11));
}
                    
void main(void) {
        cout << M(1000);
}
 
Binary Search Exercise

Implement the following Binary Search function in Assembler.

Binary Search
#include <iostream.h>

// pre: $key ^ $low ^ $high ^ $A0
//        "j: 1 £ j £ n-1, Aj-1 £ Aj                                      A[] sorted low to high

// post:        "j:  0 £ j £ n-1, Aj ¹ key ® result = -1        if key != A[j] then result = -1
//         XOR k: 0 £ k £ n-1, Ak = key ® result = k    else key==A[k] then result = k

int BinarySearch (A, key, low, high) {

  if (low > high) return -1;

  int mid=(low+high)/2;

  if (key == A[mid]) return mid;

  if (key > A[mid])

          return BinarySearch(A, key, mid+1, high);

  else    return BinarySearch(A, key, low, mid-1);

}

 

int X[] = {2, 4, 5, 7, 8, 9, 12, 14, 17, 19, 22, 25, 27, 28, 33};

void main () {

cout << BinarySearch(X, 18, 0, 14);

}

 
 
Linked List Example
Linked List
Title     Linked list - Allocation, insertion and printing
;;        Insert - Insert into a linked list
;;        Print  - Print a linked list
;;        New    - Allocate storage for dynamic data structure. Allocates
;;                 from static array of bytes (HEAP) by incrementing pointer
;;                 (Avail) by the number of bytes to allocate, always pointing
;;                 to the next available byte to allocate.
List Data Structure Example

 NULL      Equ    -1
 HEAPSIZE  Equ    200                               ;; Allocateable memory size
                               
 Link      Struc                                    ;; struct Link {
           Next    dd      NULL                     ;;        Link  Next;
           Value   dd      ?                        ;;        word  Value;
 Link      Ends                                     ;; };

.data                                               ;; byte Heap[HEAPSIZE]
           Heap    db      HEAPSIZE dup (?)         ;;       
           Head    dd      ?                        ;; Link Head;
           Avail   dd      Heap                     ;; Avail points to Heap

       New   proto near C, N:dword
.code
Recursive Tail Insertion Example
Insert Proc   Near C, P:near ptr Link, Item:dword
                               				; void Insert( Link P, int Item )
       Mov    eBx, P           				;; if (p == NIL)   
 .IF   dword ptr [eBx] == NIL
                           
       invoke New, Size Link  

       Mov    [eBx], eAx       				;; {     p = new Link();
       Mov    eBx, eAx
       Mov    eAx, Item        				;;       p->Value = Item; 
       Mov    [eBx].Link.Value, eAx			;; }
       Mov    [eBx].Link.Next, NIL 
  .ELSE
       Mov    eBx, P           				;; else Insert( p->Next, Item);

       invoke Insert, [eBx].Link.Next, Item
 .ENDIF
       Ret    
 Insert Endp
Recursive Printing of List Example
 Print     Proc Near C, P:near ptr Link 
                                     			;; void Print( Link P)
 .IF       P != NIL                  			;; If (p != NIL)
           Mov       eBx, P      
           Mov       eAx, [eBx].Link.Value 
           Call      WriteDec        			;; {  cout << p->Value);
           Call      CrLf

           invoke    Print, [eBx].Link.Next 
 .ENDIF                              			;;    Print( p->Next );
           Ret                       			;; }
 Print     Endp
Dynamic Memory Allocation Example
;        Link& New( int N );
;           N - Number of byte to allocate

 New       Proc   Near C, N:dword   			;; Allocates N bytes from data area
           Mov    eAx, N            			;; Number of bytes to allocate
           Add    eAx, Avail        			;; Point to next available area
 .IF       eAx >= offset Heap + SIZEHEAP     
           Mov    eAx, 0            			;; If Heap exhausted then New = 0
 .ELSE					 			;;    else New = Avail
           Xchg   eAx, Avail        			;;         Avail = Avail + N
 .ENDIF 
           Ret     
 New       Endp
Main Procedure Example
 Main      Proc    near
           Mov     Head, NIL        			;; Head = NIL
           Call    ReadDec          			;; cin >> eAx ;
 .WHILE    eAx > 0                  			;; while (eAx > 0) {
           invoke  Insert, addr Head, eAx
                                    			;;       Insert( Head, eAx );
           Call    ReadDec          			;;       cin >> eAx;
 .ENDW                              			;; }
                                    			;; Print( Head );
           invoke Print, Head
           
           invoke  ExitProcess, 0
 Main      Endp
           End    Main