A Function Example


Let's take a look at how a function call works in a real program. The function we are going to write is the power function. We will give the power function two parameters - the number and the power we want to raise it to. For example, if we gave it the parameters 2 and 3, it would raise 2 to the power of 3, or 2*2*2, giving 8. In order to make this program simple, we will only allow numbers 1 and greater.

The following is the code for the complete program. As usual, an explanation follows. Name the file power.s.

  #PURPOSE:  Program to illustrate how functions work  #          This program will compute the value of  #          2^3 + 5^2  #  #Everything in the main program is stored in registers,  #so the data section doesn't have anything.  .section .data  .section .text  .globl _start _start:  pushl $3                   #push second argument  pushl $2                   #push first argument  call  power                #call the function  addl  $8, %esp             #move the stack pointer back  pushl %eax                 #save the first answer before                             #calling the next function  pushl $2                   #push second argument  pushl $5                   #push first argument  call  power                #call the function  addl  $8, %esp             #move the stack pointer back  popl   %ebx                #The second answer is already                             #in %eax. We saved the                             #first answer onto the stack,                             #so now we can just pop it                             #out into %ebx  addl   %eax, %ebx          #add them together                             #the result is in %ebx  movl   $1, %eax            #exit (%ebx is returned)  int    $0x80  #PURPOSE:   This function is used to compute  #           the value of a number raised to  #           a power.  #  #INPUT:     First argument - the base number  #           Second argument - the power to  #                             raise it to  #  #OUTPUT:    Will give the result as a return value  #  #NOTES:     The power must be 1 or greater  #  #VARIABLES:  #           %ebx - holds the base number  #           %ecx - holds the power  #  #           -4(%ebp) - holds the current result  #  #           %eax is used for temporary storage  #  .type power, @function power:  pushl %ebp           #save old base pointer  movl  %esp, %ebp     #make stack pointer the base pointer  subl  $4, %esp       #get room for our local storage  movl  8(%ebp), %ebx  #put first argument in %ebx  movl  12(%ebp), %ecx #put second argument in %ecx  movl  %ebx, -4(%ebp) #store current result power_loop_start:  cmpl  $1, %ecx       #if the power is 1, we are done  je    end_power  movl  -4(%ebp), %eax #move the current result into %eax  imull %ebx, %eax     #multiply the current result by                       #the base number  movl  %eax, -4(%ebp) #store the current result  decl  %ecx           #decrease the power  jmp   power_loop_start #run for the next power end_power:  movl -4 (%ebp), %eax #return value goes in %eax  movl %ebp, %esp      #restore the stack pointer  popl %ebp            #restore the base pointer  ret 

Type in the program, assemble it, and run it. Try calling power for different values, but remember that the result has to be less than 256 when it is passed back to the operating system. Also try subtracting the results of the two computations. Try adding a third call to the power function, and add its result back in.

The main program code is pretty simple. You push the arguments onto the stack, call the function, and then move the stack pointer back. The result is stored in %eax. Note that between the two calls to power, we save the first value onto the stack. This is because the only register that is guaranteed to be saved is %ebp. Therefore we push the value onto the stack, and pop the value back off after the second function call is complete.

Let's look at how the function itself is written. Notice that before the function, there is documentation as to what the function does, what its arguments are, and what it gives as a return value. This is useful for programmers who use this function. This is the function's interface. This lets the programmer know what values are needed on the stack, and what will be in %eax at the end.

We then have the following line:

  .type power,@function 

This tells the linker that the symbol power should be treated as a function. Since this program is only in one file, it would work just the same with this left out. However, it is good practice.

After that, we define the value of the power label:

 power: 

As mentioned previously, this defines the symbol power to be the address where the instructions following the label begin. This is how call power works. It transfers control to this spot of the program. The difference between call and jmp is that call also pushes the return address onto the stack so that the function can return, while the jmp does not.

Next, we have our instructions to set up our function:

  pushl %ebp  movl  %esp, %ebp  subl  $4, %esp 

At this point, our stack looks like this:

 Base Number    <--- 12(%ebp) Power          <--- 8(%ebp) Return Address <--- 4(%ebp) Old %ebp       <--- (%ebp) Current result <--- -4 (%ebp) and (%esp) 

Although we could use a register for temporary storage, this program uses a local variable in order to show how to set it up. Often times there just aren't enough registers to store everything, so you have to offload them into local variables. Other times, your function will need to call another function and send it a pointer to some of your data. You can't have a pointer to a register, so you have to store it in a local variable in order to send a pointer to it.

Basically, what the program does is start with the base number, and store it both as the multiplier (stored in %ebx) and the current value (stored in -4(%ebp)). It also has the power stored in %ecx It then continually multiplies the current value by the multiplier, decreases the power, and leaves the loop if the power (in %ecx) gets down to 1.

By now, you should be able to go through the program without help. The only things you should need to know is that imull does integer multiplication and stores the result in the second operand, and decl decreases the given register by 1. For more information on these and other instructions, see Appendix B

A good project to try now is to extend the program so it will return the value of a number if the power is 0 (hint, anything raised to the zero power is 1). Keep trying. If it doesn't work at first, try going through your program by hand with a scrap of paper, keeping track of where %ebp and %esp are pointing, what is on the stack, and what the values are in each register.




Programming from the Ground Up
Programming from the Ground Up
ISBN: 0975283847
EAN: 2147483647
Year: 2006
Pages: 137

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net