# Lab 6: Subprograms: Arguments¶

## Overview¶

We saw how to we can create a function or a subprogram to complete a specific task. Now, we will see how pass arguments to a subprogram.

Attention

### Arguments¶

The caller is responsible for pushing the arguments to the stack, which it does in reverse order. Then, the subprogram can access the data using the stack pointer.

Tip

Save the current stack pointer in `ESP` to prevent tracking the offset value when pushing additional data to the stack.

Consider this example that finds the largest of the three values. It has the prototype of `int maxofthree(int, int, int);`

1. The three arguments have been pushed to the stack in reverse order so that the first argument is at the top. Stack view (top) `| arg1 | arg2 | arg3 | ... |`

```int result = maxofthree(1, 2, 3);
Push arg3: |  3  | ... |
Push arg2: |  2  |  3  | ... |
Pash arg1: |  1  |  2  |  3  | ... |
```
2. The RET address is also pushed to the top when using CALL

```Stack:   | RET |   1   |   2   |    3    |   ...   |
Pointer: | ESP | ESP+4 | ESP+8 | ESP+12  | ESP+16  |
```
3. At the start of function, we push EBP to the top of the stack to save it. We will use use EBP as our pointer (the offset will not change).

```push    ebp        ; Save EBP value
mov     ebp, esp   ; set EBP to current pointer

Stack:       | PTR |  RET  |   1   |    2   |    3   |   ...  |
ESP Pointer: | ESP | ESP+4 | ESP+8 | ESP+12 | ESP+16 | ESP+20 |
EBP Pointer: | EBP | EBP+4 | EBP+8 | EBP+12 | ESP+16 | ESP+20 |
```
4. Lastly, we allocate space for local args (optional)

```EBP doesn't change when we allocate more memory.
sub     esp, 8         ; reserve X=4*N bytes for N local vars
; ESP   is v_1
; ESP+4 is v_2

Stack:       | v_1 |  v_2  |  PTR  |   RET  |    1   |    2   |    3   |   ...  |
ESP Pointer: | ESP | ESP+4 | ESP+8 | ESP+12 | ESP+16 | ESP+20 | ESP+24 | ESP+28 |
EBP Pointer: |  -  |   -   |  EBP  | EBP+4  | EBP+8  | EBP+12 | ESP+16 | ESP+20 |
```

Note

• Notice that the EBP pointer did not change.

• Our first arg is still EBP+8.

Sample program showing how to compare three args
 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52``` ```; ; Function prototype: int maxofthree(int, int, int) ; maxofthree: ; 1. Save EBP to the stack and set EBP to current pointer push ebp ; save caller's EBP value mov ebp, esp ; set EBP to current pointer ; 2. Put the args in registers EAX, ECX, EDX. ; The offset always starts at +8 for arg when using EBP ; ; We must save EBX if we use it. It is a preserved register. ; Alternatively, just use the value using ebp+N mov eax, [ebp+8] ; pointer offsets for first parameter mov ecx, [ebp+12] ; pointer offsets for second parameter ; mov edx, [ebp+16] ; pointer offsets for third parameter ; Optional space for local variables ; EBP doesn't change when we allocate more memory. ; sub esp, N ; reserve X=4*N bytes for N local vars ; 3. Compare the first two args, store largest in EAX ; ; // C equivalent ; if (eax < ecx) ; eax = ecx; cmp eax, ecx ; compare arg1 and arg2, then evaluate cmovl eax, ecx ; eax = ecx if eax < ecx ; perform move if previous comparison ; resulted in "less than" ; 4. Compare the last arg with the current result, store largest in EAX ; ; We don't have to use a register! We can access memory directly ; // C equivalent ; if (eax < *arg3) ; eax = *arg3; cmp eax, [ebp+16] ; compare the result with the third arg cmovl eax, [ebp+16] ; Move if less than ; 5. clean up local args mov esp, ebp ; remove space for local vars ; 6. Restore EBP pop ebp; ; Return caller's EBP value ; 7. Return EAX ret ; return eax, which is the result ```

### Subprogram template¶

Subprogram template with args that returns a value
```;---------------------------------------------------------------
;   FUNCTION: subprogram_name
;   This subprogram ... (describe functionality)
;   The function has prototype:
;
;    int subprogram_name(int*, int)
;
;   Takes the following parameters:
;     1. describe the first arg
;     2. describe the second arg
;
;   Returns the value in EAX
;---------------------------------------------------------------

global  subprogram_name

segment .data

segment .bss
returnvalue     resd     1   ; place in memory for the return value

segment .text
subprogram_name:

; Initialize the subprogram (we must reverse this processes to cleanup)
push    ebp             ; save caller's EBP value
mov     ebp, esp        ; set EBP to current pointer
sub     esp, N          ; reserve X=4*N bytes for N local vars
pusha                   ; save all

; Get arg data
mov     edx, [ebp+8]    ; arg1 (could be EAX, EBX, ECX, or EDX)
mov     ecx, [ebp+12]   ; arg2 (could be EAX, EBX, ECX, or EDX)

; Start the work!

. . .

; Clean up and then exit

; Put return values in EAX
mov     [returnvalue], eax      ; save return value in memory
popa                            ; restore data to registers
mov     eax, [returnvalue]      ; retrieve the saves return value

; Restore stack pointers
mov     esp, ebp        ; remove space for local vars
pop     ebp;            ; Return caller's EBP value

ret                     ; return eax (AEX contains the value of the stack pointer)
```

### Floating Points¶

Working with floating points requires the use of different commands than ADD or SUB and different registers than the four registers the we use commonly, such as EAX or ECX.

The FPU has 8 registers, st0 to st7, formed into a stack. Numbers are pushed onto the stack from memory, and are popped off the stack back to memory. FPU instructions generally will pop the first two items off the stack, act on them, and push the answer back on to the top of the stack.

Here is a list some floating point instructions. You can see that some commands operate similarly to their integer counterpart, but they have the `f` prefix. For example you would use `fadd` to add floating point numbers.

`fldz`

Push +0.0 onto the FPU register stack.

`fld1`

Push +1.0 onto the FPU register stack

`fadd`
• ST0 += VALUE

• Add the value to the top of the floating point stack (ST0)

`fsub`
• ST0 -= VALUE

• Subtract the value from the top of the floating point stack (ST0)

## Task 1: Create a Subprogram to Sum Integers¶

The first subprogram will accept an array of integers, sum them, and then return the value.

The the function will have the prototype of:

```int sum_int(int*, int length)
```
1. Create a new file called `sum_int.asm` and start with the Subprogram template from above.

Note

You should remove the code that you do not need and use correct reference names.

2. Compile to verify that you do not have a syntax error.

You will need to add a new target to your `Makefile` and then compile using `make sum_int.o`.

```# Build sum_int.asm
sum_int.o:
@nasm -f elf -d ELF_TYPE sum_int.asm -o sum_int.o
```

1. Create `sum.c` using this code:

sum.c
 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35``` ```/** * sum.c * * Demonstrates how to send an int array and a double array * to asm files to process. * * Return the summation of the values in the array * double sum_double(double[] array, int length) * int sum_int(int[] array, int length) * */ #include int sum_int(int*, int); int main() { // result = 210; array_size = 16 int array_int[] = {11,22,33,44,55,35,1,1,1,1,1,1,1,1,1,1}; int size_array_int = sizeof(array_int)/sizeof(int); printf(" int values: "); // print array values for (int i = 0; i < size_array_int; i++) { printf(" %d,", array_int[i]); } int sum_i = sum_int(array_int, size_array_int); printf("\n size: %d; sum: %i", size_array_int, sum_i); printf("\n"); return 0; } ```
2. Compile to verify that you do not have a syntax error.

You will need to add a new target to your `Makefile` and then compile using `make sum.o`.

```sum.o:
@gcc -m32 -c sum.c -o sum.o
```
3. Link the `sum.o` and `sum_int.o` in the sum target of the Makefile.

```sum: sum_int.o sum.o:
@gcc -m32 sum.o sum_int.o -o sum
```
4. Verify that your program prints the array values, displays the correct size of the array, and the sum contains a random value.

Expected Output
```make
Building sum
./sum
int values:  11, 22, 33, 44, 55, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
size: 16; sum: -2055764
```

## Task 3: Sum the Integer Values¶

Now, you are ready to sum the values in the array!

Here are the registers that you can:

`EDX`
• The pointer to the array (the first arg).

• Recall that arrays are always passed by reference in C.

`ECX`
• The length of the array (the second arg)

`EAX`
• The summation and return value

1. First, you will need to get the values from the parameters. You can do this by accessing them in memory starting with `ebp+8`.

```; get arg data
mov     edx, [ebp+8]    ; address of argument (array)
mov     ecx, [ebp+12]   ; length of array
```
2. Next, initialize the values and check the array for a non-positive length

Note

The loop operate as a `do-while` loop if we test the condition at the end of the loop. In which case, we should test the condition to protect against an array with a zero or negative length.

Initialize and validate input
```mov     eax, 0          ; initialize the sum and return value
cmp     ecx, 0          ; guard against non-positive lengths
jle     done            ; exit if the array length < 1
```
3. The primary algorithm is a loop with a summation variable.

1. Loop through the array

2. Get the value at memory location [EDX]

3. Add that value to EAX: EAX = EAX + [EDX]

4. Increment the array pointer (EDX += 4)

Loop with summation variable
```next:
add     eax, [edx]      ; [edx] contains the current value in the array add to eax
add     edx, 4          ; move to next array element
; 4 == sizeof(int); int data type has a size of 4.

dec     ecx             ; count down
jnz     next
```
4. That’s it! Your code should loop through the array and sum the values.

Expected Results
```make
Building sum
./sum
int values:  11, 22, 33, 44, 55, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
size: 16; sum: 210
```

## Task 4: Sum the Double Values¶

This task is almost identical to adding integers, but will use different commands.

The the function will have the prototype of:

```double sum_double(double*, int);
```

### Part 1¶

First, you will create the file, add references to the Makefile, and create the test code.

Note

Do this part first! Otherwise, you will have a hard time debugging because you won’t know if the error is in the ASM file, Makefile, or in the C file.

1. Create a copy of `sum_int.asm` and call it `sum_double.asm`.

2. Change the references in the file, subprogram name, comment header, etc.

3. Add the new targets to the Makefile for `sum_double.asm`.

4. Execute `make all` to verify that there are no errors.

5. Add the function prototype to your `sum.c` file:

```double sum_double(double*, int);
```
6. Add this code block to your `sum.c` file

```// result = 23.331000; array_size = 6
double array_double[] = {1.111,2.222,3.333,4.444,5.555, 6.666};
int size_array_double = sizeof(array_double)/sizeof(double);

printf(" double values: ");

// print array values
for (int i = 0; i < size_array_double; i++) {
printf(" %lf,", array_double[i]);
}

double sum_d = sum_double(array_double, size_array_double);
printf("\n size: %d; sum: %lf", size_array_double, sum_d);
printf("\n");
```
7. Rebuild your project. It should run without crashing, but will not produce the expected results. Notice the `-nan`

 ```1 2 3 4 5 6 7 8``` ```make all Cleaning up... Building sum ./sum int values: 11, 22, 33, 44, 55, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, size: 16; sum: 210 double values: 1.111000, 2.222000, 3.333000, 4.444000, 5.555000, 6.666000, size: 6; sum: -nan ```

### Part 2¶

Now, you can complete work on the ASM file knowing that the rest of the project operates without errors.

Note

A floating point value requires 8 bytes, or a `qword`

1. First, you will initialize the summation registers using command `fldz`, which writes a 0.0 to `st0`.

Replace the integer initialization code with floating commands
```; mov     eax, 0      ; initialize the sum to 0
fldz                  ; initialize ST0 to 0.0
```
2. Next Replace the `add` summation command with `fadd` command. Also, we must specify the memory size of the value.

```; add     eax, [edx]
fadd    qword [edx]      ; ST0 += [edx]
```
3. The last required change to make is to increment the array. Our integer values incremented the pointer by 4 bytes (`add edx, 4`). However, a floating point value is 8 bytes. Therefore, we must move the pointer accordingly.

Increment array pointer
```; 4 == sizeof(int); int data type has a size of 4.
; add     edx, 4    ; move to next array element (int data size)

; 8 == sizeof(double); double data type has a size of 8.
add     edx, 8      ; move to next array element (double data size)
```
4. We should remove the `returnvalue` code because we don’t use it to store the floating point result.

That’s it! You can rebuild and view the output.

Expected Output
```int values:  11, 22, 33, 44, 55, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
size: 16; sum: 210
double values:  1.111000, 2.222000, 3.333000, 4.444000, 5.555000, 6.666000,
size: 6; sum: 23.331000
```

Lab 6 Solutions