Chapter 13
Data Structures

An essential part of programming is determining how to organize the data. Homogeneous data is often grouped in an array, and heterogeneous data in a struct. In this chapter, we study how both these data structures are implemented.

13.1 Arrays

An array in C/C++ consists of one or more elements, all of the same type, arranged contiguously in memory. To access an element in an array you need to specify two address-related items:

For example, given the declaration:

   int array[50];

you can store an integer, say 123, in the ith element with the statement

     array[i] = 123;

In this example the beginning of the array is specified by using the name, and the number of the element is specified by the […] syntax, as illustrated by the program in Listing 13.1.

 
1/* 
2 * arrayElement.c 
3 * Stores a value in one element of an array. 
4 * Bob Plantz - 15 June 2009 
5 */ 
6 
7#include <stdio.h> 
8 
9int main(void) 
10{ 
11    int myArray[50]; 
12    int i = 25; 
13 
14    myArray[i] = 123; 
15    printf("The value is %i\n", myArray[i]); 
16 
17    return 0; 
18}
Listing 13.1: Storing a value in one element of an array (C).

We would expect this program to allocate 4 × 50 = 200 bytes for myArray, plus 4 bytes for i in the local variable area. Indeed, the gcc-generated assembly language in Listing 13.2 shows that this total (204) has been increased to a multiple of sixteen, and 224 bytes have been allocated in the stack frame.1

 
1        .file  "arrayElement.c" 
2        .section      .rodata 
3.LC0: 
4        .string"The value is %i\n" 
5        .text 
6        .globlmain 
7        .type  main, @function 
8main: 
9        pushq  %rbp 
10        movq  %rsp, %rbp 
11        subq  $224, %rsp          # myArray and i 
12        movl  $25, -212(%rbp)     # i = 25; 
13        movl  -212(%rbp), %eax    # load i 
14        cltq                        # convert to 64-bit 
15        movl  $123, -208(%rbp,%rax,4) # myArray[i] = 123; 
16        movl  -212(%rbp), %eax    # load i 
17        cltq                        # convert to 64-bit 
18        movl  -208(%rbp,%rax,4), %eax # esi <- myArray[i] 
19        movl  %eax, %esi 
20        movl  $.LC0, %edi 
21        movl  $0, %eax 
22        call  printf 
23        movl  $0, %eax 
24        leave 
25        ret 
26        .size  main, .-main 
27        .ident"GCC: (Ubuntu/Linaro 4.7.0-7ubuntu3) 4.7.0" 
28        .section      .note.GNU-stack,"",@progbits
Listing 13.2: Storing a value in one element of an array (gcc assembly language).

Next, we see that the number of the element that is being accessed, 25, is stored in the variable i. Then it is loaded into eax and converted from 32-bit to 64-bit.

12        movl  $25, -212(%rbp)     # i = 25; 
13        movl  -212(%rbp), %eax    # load i 
14        cltq                        # convert to 64-bit

The next instruction

15        movl  $123, -208(%rbp,%rax,4) # myArray[i] = 123;

uses an addressing mode for the destination that is new to you, indexed. The syntax in the GNU assembly language is

offset(base_register_name, index_register_name, scale_factor)

Intel® Syntax [base_register_name + index_register_name * scale_factor + offset]

When it is zero, the offset is not required.

indexed:
The data value is located in memory. The address of the memory location is the sum of the value in the base register plus the scale factor times the value in the index register, plus the offset.
syntax: place parentheses around the comma separated list — base_register, index_register, scale — and preface it with the offset. example: -16(%rdx, %rax, 4)
Intel® Syntax [rdx + rax*4 - 16]
 

The indexed addressing mode allows us to specify

in one instruction. The number of bytes in each element can be 1, 2, 4, or 8. Both registers, the beginning address register and the index register, must be the same size. (Hence the cltq instruction on line 14 of Listing 13.2 to convert the index value from a 32 to 64 bits in the rax register.)

So from the destination operand of the instruction on line 15, we can see that

Thus, the address of the element in the array is given by

effective address = 4× index in rax+ address in rbp− 208

Now that we know how a single array element is accessed, let us see how an entire array is processed.

 
1/* 
2 * clearArray1.c 
3 * Allocates an int array, stores zero in each element, 
4 * and prints results. 
5 * Bob Plantz - 16 June 2009 
6 */ 
7#include <stdio.h> 
8 
9int main(void) 
10{ 
11    int intArray[10]; 
12    int index; 
13 
14    index = 0; 
15    while (index < 10) 
16    { 
17        intArray[index] = 0; 
18        index++; 
19    } 
20    index = 0; 
21    while (index < 10) 
22    { 
23        printf("intArray[%i] = %i\n", index, intArray[index]); 
24        index++; 
25    } 
26    return 0; 
27}
Listing 13.3: Clear an array (C).

The gcc compiler generated the assembly language shown in Listing 13.4 for this array clearing program.

 
1        .file  "clearArray1.c" 
2        .section      .rodata 
3.LC0: 
4        .string"intArray[%i] = %i\n" 
5        .text 
6        .globlmain 
7        .type  main, @function 
8main: 
9        pushq  %rbp 
10        movq  %rsp, %rbp 
11        subq  $64, %rsp 
12        movl  $0, -52(%rbp)          # index = 0; 
13        jmp    .L2 
14.L3: 
15        movl  -52(%rbp), %eax        # load current index value 
16        cltq                           # convert to 64 bits 
17        movl  $0, -48(%rbp,%rax,4)   # intArray[index] = 0; 
18        addl  $1, -52(%rbp)          # index++; 
19.L2: 
20        cmpl  $9, -52(%rbp) 
21        jle    .L3 
22        movl  $0, -52(%rbp)          # index = 0; 
23        jmp    .L4 
24.L5: 
25        movl  -52(%rbp), %eax        # load current index value 
26        cltq                           # convert to 64 bits 
27        movl  -48(%rbp,%rax,4), %edx # load array element 
28        movl  -52(%rbp), %eax        # load current index value 
29        movl  %eax, %esi 
30        movl  $.LC0, %edi 
31        movl  $0, %eax               # no floats 
32        call  printf 
33        addl  $1, -52(%rbp)          # index++; 
34.L4: 
35        cmpl  $9, -52(%rbp) 
36        jle    .L5 
37        movl  $0, %eax 
38        leave 
39        ret 
40        .size  main, .-main 
41        .ident"GCC: (Ubuntu/Linaro 4.7.0-7ubuntu3) 4.7.0" 
42        .section      .note.GNU-stack,"",@progbits
Listing 13.4: Clear an array (gcc assembly language).

We can see from line 17

17        movl  $0, -48(%rbp,%rax,4)   # intArray[index] = 0;

that the address of the first element of this array is 0 4 48 = 48 from the address in rbp, and the address of the last element is 9 4 48 = 12 from the address in rbp. Since this function does not call any others, the array is stored in the red zone.

Indexing through the array is accomplished by loading the current value of the index variable into the rax register. Although this example simply increments the index through the array, you can see that the value used to index the array element is independent of maintaining the beginning address of the array.

The third value in the parentheses, 4, allows you to use the actual element number as the array index. This is clearly more convenient — hence, less error prone — than having to compute the number of bytes from the beginning of the array using the index value.

If we did not have this addressing mode, we would have to do something like:

# clear the array 
.L3: 
        movl    -4(%rbp), %eax 
        cltq 
        salq    $2, %rax         # multiply index by 4 
        leaq    -48(%rbp), %rsi  # address of array start 
        addq    %rax, %rsi       # address of current element 
        movl    $0, (%rsi)       # store zero there 
        addl    $1, -4(%rbp)     # index ++ 
.L2: 
        cmpl    $9, -4(%rbp) 
        jle     .L3

Although this is logically correct, it requires two more instructions and uses more registers.

RISC architectures (e.g., PowerPC, MIPS, Itanium) typically do not have the indexed addressing mode, hence this algorithm must be used.

Listing 13.5 shows the equivalent program written in assembly language.

 
1# clearArray2.s 
2# Allocates an int array, stores zero in each element, 
3# and prints results. 
4# Bob Plantz - 16 June 2009 
5 
6# Stack frame 
7        .equ    intArray,-40     # space for 10 ints in the array 
8        .equ    rbxSave,-48      # preserve registers 
9        .equ    r12Save,-56 
10        .equ    localSize,-64 
11 
12# Constant data 
13        .section  .rodata 
14format: .string "intArray[%i] = %i\n" 
15 
16# The progam 
17        .text 
18        .globl  main 
19        .type   main, @function 
20main: 
21        pushq   %rbp                 # save caller base pointer 
22        movq    %rsp, %rbp           # set our base pointer 
23        addq    $localSize, %rsp     # local variables 
24        movq    %rbx, rbxSave(%rbp)  # save regs. 
25        movq    %r12, r12Save(%rbp) 
26 
27# clear the array 
28        movq    $0, %rax             # index = 0 
29        leaq    intArray(%rbp), %rbx # beginning of array 
30clearLup: 
31        movl    $0, (%rbx,%rax,4)    # store zero 
32        incq    %rax                 # index++ 
33        cmpq    $9, %rax             # all filled? 
34        jle     clearLup             # do rest of elements 
35 
36# print the array 
37        movq    $0, %r12             # index = 0 
38        leaq    intArray(%rbp), %rbx # beginning of array 
39printLup: 
40        movl    (%rbx,%r12,4), %edx  # load element value 
41        movl    %r12d, %esi          # get index value 
42        movq    $format, %rdi        # format string 
43        movl    $0, %eax             # no floats 
44        call    printf 
45        incq    %r12                 # index++ 
46        cmpq    $9, %r12             # all filled? 
47        jle     printLup             # do rest of elements 
48 
49        movq    rbxSave(%rbp), %rbx  # restore regs. 
50        movq    r12Save(%rbp), %r12 
51        movl    $0,  %eax            # return 0; 
52        movq    %rbp, %rsp           # remove local vars 
53        popq    %rbp                 # restore caller base ptr 
54        ret                          # back to OS
Listing 13.5: Clear an array (programmer assembly language).

This version uses a do-while loop instead of a while loop entered at the bottom. The index and the address of the beginning of the array are maintained in registers.

Using a register for the index value presents a potential problem. Recall that some registers are guaranteed to be preserved by a function (Table 6.4, page 469). We have used r12 for the print do-while loop in this program because it calls another function — printf. The current value of index must be copied to the correct argument register for the call to printf:

41        movl    %r12d, %esi          # get index value

Although the operating system probably does not depend on registers being saved, we have done so in this program:

24        movq    %rbx, rbxSave(%rbp)  # save regs. 
25        movq    %r12, r12Save(%rbp)

and:

49        movq    rbxSave(%rbp), %rbx  # restore regs. 
50        movq    r12Save(%rbp), %r12

just to be safe.

13.2 structs (Records)

An array is useful for grouping homogeneous data items that are of the same data type. A record (struct in C/C++) is used for grouping heterogeneous data items, which may be of the same or different data types. For example, an array is probably better for storing a list of test scores in a program that works with the ith test score, but a struct might be better for storing the coordinates of a point on an x y graph.

The data elements in a struct are usually called fields. Accessing a field in a struct also requires two address-related items:

Consider the C program in Listing 13.6

 
1/* 
2 * structField1.c 
3 * Allocates two structs and assigns a value to each field 
4 * in each struct. 
5 * Bob Plantz - 16 June 2009 
6 */ 
7 
8#include <stdio.h> 
9 
10struct theTag 
11{ 
12   char aByte; 
13   int anInt; 
14   char anotherByte; 
15}; 
16 
17int main(void) 
18{ 
19    struct theTag x; 
20    struct theTag y; 
21 
22    x.aByte = a; 
23    x.anInt = 123; 
24    x.anotherByte = b; 
25    y.aByte = 1; 
26    y.anInt = 456; 
27    y.anotherByte = 2; 
28 
29    printf("x: %c, %i, %c and y: %c, %i, %c\n", 
30            x.aByte, x.anInt, x.anotherByte, 
31            y.aByte, y.anInt, y.anotherByte); 
32    return 0; 
33}
Listing 13.6: Two struct variables (C).

Assignment to each of the three fields in the “xstruct is:

21   x.aByte = a; 
22   x.anInt = 123; 
23   x.anotherByte = b;

The name of the struct variable is specified first, followed by a dot (.), followed by the field name. The field names and their individual data types are declared between the {…} pair of the struct declaration.

The amount of memory required by a struct variable is equal to the sum of the amount of memory required by each of its fields. Thus in the above program, the amount of memory required is:

aByte: 1 byte
anInt: 4 bytes
anotherByte: 1 byte


total = 6 bytes

If we were to allocate these six bytes of memory without some thought, the first char variable could occupy the first byte, the int variable the next four bytes, and the second char variable the following byte. That is, relative to the address of the beginning of the struct,

However, the ABI [25] specifies that the alignment of each element should be the same as that of the “most strictly aligned component.” In this example the int element should be aligned on a 4-byte boundary. So even though the char elements only require one byte, they should also be aligned on 4-byte boundaries. Also, as explained in Section 8.5 (page 586), we should allocate memory in multiples of sixteen for local variables (see Exercise 13-4). These requirements suggest that each struct variable will be allocated on the stack as shown in Figure 13.1.


PIC

Figure 13.1: Memory allocation for the variables x and y from the C program in Listing 13.6. Shaded areas are padding bytes used to properly align the address of each variable; no data is stored in them.


Thus we see that each of the struct variables in Listing 13.6 requires that we allocate sixteen bytes in the stack frame.

The next issue is how to access each of the fields in these two structs. You learned in Section 9.1 (page 601) that assignment in C is implemented with the mov instruction. So in this program assignment at the assembly language level is implemented:

        movb    $a, address_of_aByte_field_in_x 
        movl    $123, address_of_anInt_field_in_x 
        movb    $b, address_of_anotherByte_field_in_x

The base register plus offset addressing mode (page 575) provides a convenient way to access each field in a struct. Simply load the address of the struct variable into a register, then use the offset of the field. We can see how the compiler has implemented this in Listing 13.7.

 
1        .file  "structField1.c" 
2        .section      .rodata 
3        .align 8 
4.LC0: 
5        .string"x: %c, %i, %c and y: %c, %i, %c\n" 
6        .text 
7        .globlmain 
8        .type  main, @function 
9main: 
10        pushq  %rbp 
11        movq  %rsp, %rbp 
12        subq  $48, %rsp 
13        movb  $97, -32(%rbp)      # x.aByte = a’; 
14        movl  $123, -28(%rbp)     # x.anInt = 123; 
15        movb  $98, -24(%rbp)      # x.anotherByte = b’; 
16        movb  $49, -16(%rbp)      # y.aByte = ’1’; 
17        movl  $456, -12(%rbp)     # y.anInt = 456; 
18        movb  $50, -8(%rbp)       # y.anotherByte = ’2’; 
19        movzbl-8(%rbp), %eax      # load y.anotherByte 
20        movsbl%al, %esi 
21        movl  -12(%rbp), %r8d     # load y.anInt 
22        movzbl-16(%rbp), %eax     # load y.aByte 
23        movsbl%al, %edi 
24        movzbl-24(%rbp), %eax     # load x.anotherByte 
25        movsbl%al, %ecx 
26        movl  -28(%rbp), %edx     # load x.anInt 
27        movzbl-32(%rbp), %eax     # load x.aByte 
28        movsbl%al, %eax 
29        movl  %esi, (%rsp) 
30        movl  %r8d, %r9d 
31        movl  %edi, %r8d 
32        movl  %eax, %esi 
33        movl  $.LC0, %edi 
34        movl  $0, %eax 
35        call  printf 
36        movl  $0, %eax 
37        leave 
38        ret 
39        .size  main, .-main 
40        .ident"GCC: (Ubuntu/Linaro 4.7.0-7ubuntu3) 4.7.0" 
41        .section      .note.GNU-stack,"",@progbits
Listing 13.7: Two struct variables (gcc assembly language).

The compiler allocated 48 bytes in the stack frame. Thirty-two are for the two struct variables. The additional sixteen are needed for passing the seventh argument to the printf function (line 29), while maintaining 16-byte addressing of the stack pointer. Rather than load the address of each struct into a register, the compiler has computed the total offset to each of the fields in each of the structs.

As usual, we equate symbolic names to these numbers when writing in assembly language. We have tried to make our assembly language version (Listing 13.8) a little more readable than the version generated by gcc.

 
1# structField2.s 
2# Allocates two structs and assigns a value to each field 
3# in each struct. 
4# Bob Plantz - 18 June 2009 
5 
6# struct field offsets from start of struct 
7        .equ    aByte,0 
8        .equ    anInt,4 
9        .equ    anotherByte,8 
10        .equ    structSize,16 
11# Stack frame 
12        .equ    y,x-structSize 
13        .equ    x,-structSize 
14        .equ    localSize,y-8  # include space for 7th arg 
15# Read only data 
16        .section  .rodata 
17formatString: 
18        .string "x: %c, %i, %c and y: %c, %i, %c\n" 
19# Code 
20        .text 
21        .globl  main 
22        .type   main, @function 
23main: 
24        pushq   %rbp              # save frame pointer 
25        movq    %rsp, %rbp        # our frame pointer 
26        addq    $localSize, %rsp  # local variables 
27        andq    $-16, %rsp        # align stack pointer 
28 
29# fill the x struct 
30        leaq    x(%rbp), %rax     # the x struct 
31        movb    $a, aByte(%rax) # x.aByte = a 
32        movl    $123, anInt(%rax) # x.anInt = 123 
33        movb    $b, anotherByte(%rax) # x.anotherByte = b 
34 
35# fill the y struct 
36        leaq    y(%rbp), %rax     # the y struct 
37        movb    $1, aByte(%rax) # y.aByte = ’1’ 
38        movl    $456, anInt(%rax) # y.anInt = 456 
39        movb    $2, anotherByte(%rax) # y.anotherByte = ’2’ 
40 
41# print values 
42        movq    $formatString, %rdi 
43        leaq    x(%rbp), %rax     # the x struct 
44        movb    aByte(%rax), %sil 
45        movl    anInt(%rax), %edx 
46        movb    anotherByte(%rax), %cl 
47        leaq    y(%rbp), %rax     # the y struct 
48        movb    aByte(%rax), %r8b 
49        movl    anInt(%rax), %r9d 
50        movb    anotherByte(%rax), %al 
51        movb    %al, (%rsp)       # pass on stack 
52        movl    $0, %eax          # no floating point 
53        call    printf 
54 
55        movl    $0, %eax          # return 0; 
56        movq    %rbp, %rsp        # remove local vars 
57        popq    %rbp              # restore callers frame ptr 
58        ret                       # back to OS
Listing 13.8: Two struct variables (programmer assembly language).

The version written in assembly language loads the address of a struct variable into a register before accessing the fields. This allows the use of the symbolic names for each field:

30        leaq    x(%rbp), %rax     # the x struct 
31        movb    $a, aByte(%rax) # x.aByte = a 
32        movl    $123, anInt(%rax) # x.anInt = 123 
33        movb    $b, anotherByte(%rax) # x.anotherByte = b

and:

36        leaq    y(%rbp), %rax     # the y struct 
37        movb    $1, aByte(%rax) # y.aByte = ’1’ 
38        movl    $456, anInt(%rax) # y.anInt = 456 
39        movb    $2, anotherByte(%rax) # y.anotherByte = ’2’

This technique would be necessary with, say, an array of structs or a function that takes an address of a struct as an argument.

13.3 structs as Function Arguments

The general rules for passing arguments to functions are:

While C++ supports pass by reference for output parameters, C does not. In C, a pass by reference is simulated by passing a pointer to the variable to the function. Then the function can change the variable, thus affecting an output. At the assembly language level, pass by reference is implemented in C++ by passing a pointer, so these two rules can be restated:

Some languages, e.g., ADA, also support passing an “update.” In this case the function replaces the original value with a new value that depends upon the original value. Passing an argument for update is also implemented by passing its address.

There is an important exception to the rule of passing a copy for inputs. When the amount of data is large, making a copy of it is inefficient. So we organize it into a single entity and pass the address of that entity.

The most common example of this is an array. In fact, it is so common that in C arrays are automatically passed by address. Thus, in C/C++

     void f(int a, int b[]); 
 
       ---- 
     int x; 
     int y[100]; 
       ---- 
     f(x, y); 
       ----

means that x is passed by value and y is passed by address.

Another common example of passing a possibly large amount of data as input to a function is a struct. Of course, not all structs are large. And it is possible to pass the value in a single struct field, but the main reason for organizing data into a struct format is usually to treat several pieces of data as a more or less single unit.

Since structs are not automatically passed by address in C, we must use the address-of operator (&) on the name of the struct variable if we wish to avoid making a copy of the entire variable on the stack. The technique is exactly the same as passing an address of a simple variable.

Let us rewrite the C program from Listing 13.6 such that the main function calls another function to fill the two structs. In this example, the structs must be passed by address because the function, loadStruct, outputs values to them. This new version is shown in Listing 13.9.

 
1/* 
2 * structPass1.c 
3 * Demonstrates passing structs as arguments in c 
4 * Bob Plantz - 16 June 2009 
5 */ 
6 
7#include <stdio.h> 
8#include "loadStruct1.h"   // includes struct theTag def. 
9 
10int main(void) 
11{ 
12    struct theTag x; 
13    struct theTag y; 
14 
15    loadStruct(&x, a, 123, b); 
16    loadStruct(&y, 1, 456, 2); 
17 
18    printf("x: %c, %i, %c and y: %c, %i, %c\n", 
19           x.aByte, x.anInt, x.anotherByte, 
20           y.aByte, x.anInt, y.anotherByte); 
21 
22    return 0; 
23}
 
1/* 
2 * structPass1.h 
3 * Assigns values to the fields of a struct. 
4 * 
5 * precondition 
6 *     aStruct is the address of a theTag struct 
7 * postcondition 
8 *     firstChar is stored in the aByte field of aStruct 
9 *     aNumber is stored in the anInt field of aStruct 
10 *     secondChar is stored in the anotherByte field of aStruct 
11 * Bob Plantz - 16 June 2009 
12 */ 
13 
14#ifndef LOADSTRUCT_H 
15#define LOADSTRUCT_H 
16 
17struct theTag { 
18   char aByte; 
19   int anInt; 
20   char anotherByte; 
21}; 
22 
23void loadStruct(struct theTag* aStruct, char firstChar, 
24         int aNumber, char secondChar); 
25#endif
 
1/* 
2 * loadStruct1.c 
3 * Assigns values to the fields of a struct. 
4 * 
5 * precondition 
6 *     aStruct is the address of a theTag struct 
7 * postcondition 
8 *     firstChar is stored in the aByte field of aStruct 
9 *     aNumber is stored in the anInt field of aStruct 
10 *     secondChar is stored in the anotherByte field of aStruct 
11 * Bob Plantz - 16 June 2009 
12 */ 
13 
14#include "loadStruct1.h"   // includes struct theTag def. 
15 
16void loadStruct(struct theTag* aStruct, char firstChar, 
17         int aNumber, char secondChar) 
18{ 
19    aStruct->aByte = firstChar; 
20    aStruct->anInt = aNumber; 
21    aStruct->anotherByte = secondChar; 
22}
Listing 13.9: Passing struct variables (C). (There are three files here.)

In Listing 13.10 we examine the compiler-generated assembly language for the loadStruct function.

 
1        .file  "loadStruct1.c" 
2        .text 
3        .globlloadStruct 
4        .type  loadStruct, @function 
5loadStruct: 
6        pushq  %rbp 
7        movq  %rsp, %rbp 
8        movq  %rdi, -8(%rbp)   # save address of struct 
9        movl  %edx, -16(%rbp)  # save aNumber 
10        movl  %ecx, %eax       # move secondChar 
11        movb  %sil, -12(%rbp)  # save firstChar 
12        movb  %al, -20(%rbp)   # save secondChar 
13        movq  -8(%rbp), %rax   # load struct addresss 
14        movzbl-12(%rbp), %edx  # load firstChar 
15        movb  %dl, (%rax)      # aStruct->aByte = firstChar; 
16        movq  -8(%rbp), %rax   # load struct addresss 
17        movl  -16(%rbp), %edx  # load aNumber 
18        movl  %edx, 4(%rax)    # aStruct->anInt = aNumber; 
19        movq  -8(%rbp), %rax   # load struct addresss 
20        movzbl-20(%rbp), %edx  # load secondChar 
21        movb  %dl, 8(%rax)     # aStruct->anotherByte = secondChar; 
22        popq  %rbp 
23        ret 
24        .size  loadStruct, .-loadStruct 
25        .ident"GCC: (Ubuntu/Linaro 4.7.0-7ubuntu3) 4.7.0" 
26        .section      .note.GNU-stack,"",@progbits
Listing 13.10: Passing struct variables (gcc assembly language). Only the loadStruct function is shown.

The type declaration in the function signature, struct theTag* aStruct, together with the struct definition in the header file, loadStruct1.h, tell the compiler what offsets to use for the struct fields on lines 15, 18, and 21.

We have already covered all the assembly language instructions and addressing modes needed to express the program in Listing 13.9 in assembly language. However, the .include assembler directive will make things much easier. The syntax is

        .include "filename"

which causes the assembler to insert everything in the file named “filename” into the source at the point of the .include directive. This assembler directive is essentially the same as the #include directive in C/C++. The assembly language version of our structPass program is shown in Listing 13.11.

Pay particular attention to the header file, loadStruct.h, which defines the offset to each field within the struct and provides overall size of the struct. This header file must be .included in any file that uses the struct.

Note that specifying the overall size of the struct makes it easier to allocate space for it. For example, we use

8        .equ    y,x-structSize   # Space for y struct 
9        .equ    x,-structSize    # Space for x struct

in Listing 13.11 to compute the offsets to the x and y variables in the stack frame.

 
1 
2# loadStruct2.h 
3# The struct definition 
4# Bob Plantz - 16 June 2009 
5 
6# struct definition 
7        .equ    aByte,0 
8        .equ    anInt,4 
9        .equ    anotherByte,8 
10        .equ    structSize,16
 
1# structPass2.s 
2# Demonstrates passing structs as arguments in assembly language 
3# Bob Plantz - 16 June 2009 
4 
5        .include "loadStruct2.h" 
6 
7# Stack frame 
8        .equ    y,x-structSize   # Space for y struct 
9        .equ    x,-structSize    # Space for x struct 
10        .equ    round16,-16      # 0xfffffffffffffff0 
11        .equ    passArgs,-16     # Space for passing args 
12# Read only data 
13        .section .rodata 
14formatString: 
15        .string "x: %c, %i, %c and y: %c, %i, %c\n" 
16# Code 
17        .text 
18        .globl  main 
19        .type   main, @function 
20main: 
21        pushq   %rbp              # save frame pointer 
22        movq    %rsp, %rbp        # our frame pointer 
23        addq    $y, %rsp          # local variables 
24        andq    $round16, %rsp    # round down to 16-byte boundary 
25        addq    $passArgs, %rsp   # for passing 7th arg 
26 
27        leaq    x(%rbp), %rdi     # get address of x struct 
28        movb    $a, %sil        # 1st char 
29        movl    $123, %edx        # the int 
30        movb    $b, %cl         # 2nd char 
31        call    loadStruct 
32 
33        leaq    y(%rbp), %rdi     # get address of y struct 
34        movb    $1, %sil        # 1st char 
35        movl    $456, %edx        # the int 
36        movb    $2, %cl         # 2nd char 
37        call    loadStruct 
38 
39# print values 
40        movq    $formatString, %rdi 
41        leaq    x(%rbp), %rax     # the x struct 
42        movb    aByte(%rax), %sil 
43        movl    anInt(%rax), %edx 
44        movb    anotherByte(%rax), %cl 
45        leaq    y(%rbp), %rax     # the y struct 
46        movb    aByte(%rax), %r8b 
47        movl    anInt(%rax), %r9d 
48        movb    anotherByte(%rax), %al 
49        movb    %al, (%rsp)       # pass on stack 
50        movl    $0, %eax          # no floating point 
51        call    printf 
52 
53        movl    $0, %eax          # return 0; 
54        movq    %rbp, %rsp        # delete local vars. 
55        popq    %rbp              # restore frame pointer for OS 
56        ret                       # back to caller (OS)
 
1# loadStruct2.s 
2# Stores values in struct fields 
3# Calling sequence: 
4#      rdi <- address of the struct 
5#      sil <- first character to be stored 
6#      edx <- integer to be stored 
7#      cl <- second character to be stored 
8#      call loadStruct 
9# Bob Plantz - 16 June 2009 
10 
11        .include "loadStruct2.h" 
12 
13        .text 
14        .globl  loadStruct 
15        .type   loadStruct, @function 
16loadStruct: 
17        pushq   %rbp        # save callers frame pointer 
18        movq    %rsp, %rbp  # our frame pointer 
19 
20        movb    %sil, aByte(%rdi)      # 1st character 
21        movl    %edx, anInt(%rdi)      # the int 
22        movb    %cl, anotherByte(%rdi) # 2nd character 
23 
24        movq    %rbp, %rsp  # delete local vars. 
25        popq    %rbp        # restore frame pointer 
26        ret                 # back to caller
Listing 13.11: Passing struct variables (programmer assembly language). (There are three files here.)

The number in main,

10        .equ    round16,-16      # 0xfffffffffffffff0

and its use,

24        andq    $round16, %rsp  # round down to 16-byte boundary

deserve some discussion here. After allocating space for the two structs on the stack, there is no way to know if the stack pointer is aligned on a 16-byte boundary. If it is not, the lowest-order four bits will be non-zero. The andq instruction sets these bits to zero, thus rounding the address down to the next lower 16-byte address boundary. Notice that this works because the stack grows toward numerically lower addresses.

The prologue and epilogue in loadStruct are not really needed in this simple function. But it is good to get in the habit of coding them into all your functions. It certainly has a negligible effect on execution time, and they help establish a structure to the function if it is ever changed.

13.4 Structs as C++ Objects

In C++ the data that defines an instance of an object is organized as a struct. The name of the object is essentially the name of a struct variable. The class member functions have direct access to the struct’s fields, even if these fields are private data members. This direct access is implemented by passing the address of the object (the struct variable) as an implicit argument to the member function. In our environment, it is passed as the first (the left-most) argument, but it does not appear in the argument list.

Let’s look at a simple fraction class (Listing 13.12) as an example. The programs in this section assume the existence of the functions:

 
1/* 
2 * incFraction.cc 
3 * Gets a fraction from user and increments by one 
4 * Bob Plantz - 18 June 2009 
5 */ 
6 
7#include "fraction.h" 
8#include "writeStr.h" 
9 
10int main(void) 
11{ 
12    // char array is used because writeStr takes 
13    //     a pointer to a C-style string. 
14    char newline[] = "\n"; 
15    fraction x; 
16 
17    x.get(); 
18    x.add(1); 
19    x.display(); 
20    writeStr(newline); 
21    return 0; 
22}
 
1/* 
2 * fraction.h 
3 * simple fraction class 
4 * Bob Plantz - 18 June 2009 
5 */ 
6 
7#ifndef FRACTION_H 
8#define FRACTION_H 
9 
10class fraction 
11{ 
12  public: 
13    fraction();     // default constructor 
14    void get();     // gets users values 
15    void display(); // displays fraction 
16    void add(int);  // adds integer 
17  private: 
18    int num;        // numerator 
19    int den;        // denominator 
20}; 
21 
22#endif
 
1/* 
2 * fraction.cc 
3 * simple fraction class 
4 * Bob Plantz - 18 June 2009 
5 */ 
6 
7#include "writeStr.h" 
8#include "getUint.h" 
9#include "putUint.h" 
10#include "fraction.h" 
11 
12fraction::fraction() 
13{ 
14    num = 0; 
15    den = 1; 
16} 
17 
18void fraction::get() 
19{ 
20    // char arrays are used because writeStr takes 
21    //     a pointer to a C-style string. 
22    char numMsg[] = "Enter numerator: "; 
23    char denMsg[] = "Enter denominator: "; 
24 
25    writeStr(numMsg); 
26    num = getUint(); 
27 
28    writeStr(denMsg); 
29    den = getUint(); 
30} 
31 
32void fraction::display() 
33{ 
34    // char array is used because writeStr takes 
35    //     a pointer to a C-style string. 
36    char over[] = "/"; 
37 
38    putUint(num); 
39    writeStr(over); 
40    putUint(den); 
41} 
42 
43void fraction::add(int theValue) 
44{ 
45    num += theValue * den; 
46}
Listing 13.12: Add 1 to user’s fraction (C++). The C functions getUint, putUint, and writeStr are not shown here. (There are three files here.)

Let us consider the main function and see how arguments are passed on the stack. Recall that declaring an object in C++

     fraction x;

calls the constructor function. As we said above, the address of the object (actually a struct) is passed to the constructor function, even though it is not explicitly stated in the object declaration statement. When program flow is passed to the constructor, the address of the x object is placed in the rdi register. The same thing occurs when the other member functions are called. The add member function takes an explicit argument, which is actually the second argument to the function, so is passed in the rsi register.

Before showing how the program of Listing 13.12 could be implemented in assembly language, we look at a C implementation, since we already know a lot about the transition from C to assembly language.

In C, the member data would be explicitly implemented as a struct. Implementing the member functions in C may seem very straightforward, but there is an important issue to consider — how do the member functions gain access to the data members? Since the data members are organized as a struct, passing its address as an argument to the member functions will allow each of them access to the data members. This is effectively what C++ does. The address of the “object” (actually, a struct) is passed as an implicit argument to each member function.

Another issue arises if you think about the possible names of member functions. Different C++ classes can have the same member function names, but functions in C do not belong to any class, so each must have a unique name. (Actually, “free” functions in C++ must also have unique names.) The C++ compiler takes care of this by adding the class name to the member function name. This is called name mangling. (There is no standard for how this is actually done, so each compiler may do it differently.) We do our own “name mangling” for the C version of the program as shown in Listing 13.13.

 
1/* 
2 * createFraction.c 
3 * creates a fraction and gets users values 
4 * Bob Plantz - 16 June 2009 
5 */ 
6 
7#include "fraction.h" 
8#include "fractionGet.h" 
9#include "fractionAdd.h" 
10#include "fractionDisplay.h" 
11#include "writeStr.h" 
12 
13int main(void) 
14{ 
15    struct fraction x; 
16 
17    fraction(&x);      // "constructor" 
18    fractionGet(&x); 
19    fractionAdd(&x, 1); 
20    fractionDisplay(&x); 
21    writeStr("\n"); 
22    return 0; 
23}
 
1/* 
2 * fraction.h 
3 * A fraction "constructor" in C 
4 * Bob Plantz - 16 June 2009 
5 */ 
6 
7#ifndef FRACTION_H 
8#define FRACTION_H 
9 
10struct fraction 
11{ 
12    int num; 
13    int den; 
14}; 
15 
16void fraction(struct fraction* this); 
17 
18#endif
 
1/* 
2 * fraction.c 
3 * A fraction "constructor" in C 
4 * Bob Plantz - 16 June 2009 
5 */ 
6 
7#include "fraction.h" 
8 
9void fraction(struct fraction* this) 
10{ 
11    this->num = 0; 
12    this->den = 1; 
13}
 
1/* 
2 * fractionGet.h 
3 * Gets numerator and denominator from user. 
4 * Bob Plantz - 16 June 2009 
5 */ 
6 
7#ifndef FRACTION_ADD_H 
8#define FRACTION_ADD_H 
9 
10#include "fraction.h" 
11 
12void fractionAdd(struct fraction* this, int theValue); 
13 
14#endif
 
1/* 
2 * fractionGet.c 
3 * Gets user values for a fraction 
4 * Bob Plantz - 16 June 2009 
5 */ 
6 
7#include "writeStr.h" 
8#include "getUint.h" 
9#include "fractionGet.h" 
10 
11void fractionGet(struct fraction* this) 
12{ 
13    writeStr("Enter numerator: "); 
14    this->num = getUint(); 
15 
16    writeStr("Enter denominator: "); 
17    this->den = getUint(); 
18}
 
1/* 
2 * fractionAdd.h 
3 * adds an integer to the fraction 
4 * Bob Plantz - 16 June 2009 
5 */ 
6 
7#ifndef FRACTION_ADD_H 
8#define FRACTION_ADD_H 
9#include "fraction.h" 
10 
11void fractionAdd(struct fraction* this, int theValue); 
12 
13#endif
 
1/* 
2 * fractionAdd.c 
3 * adds an integer to the fraction 
4 * Bob Plantz - 16 June 2009 
5 */ 
6 
7 
8#include "fractionAdd.h" 
9 
10void fractionAdd(struct fraction* this, int theValue) 
11{ 
12    this->num += theValue * this->den; 
13}
 
1/* 
2 * fractionDisplay.h 
3 * Displays a fraction in num/den format 
4 * Bob Plantz - 16 June 2009 
5 */ 
6 
7#ifndef FRACTION_DISPLAY_H 
8#define FRACTION_DISPLAY_H 
9 
10#include "fraction.h" 
11 
12void fractionDisplay(struct fraction* this); 
13 
14#endif
 
1/* 
2 * fractionDisplay.c 
3 * Displays a fraction in num/den format 
4 * Bob Plantz - 16 June 2009 
5 * precondition 
6 *      this points to fraction, both num and den within 0 - 9 
7 * postcondition 
8 *      num/den displayed on the screen 
9 */ 
10 
11#include "writeStr.h" 
12#include "putUint.h" 
13#include "fractionDisplay.h" 
14 
15void fractionDisplay(struct fraction* this) 
16{ 
17    putUint(this->num); 
18    writeStr("/"); 
19    putUint(this->den); 
20}
Listing 13.13: Add 1 to user’s fraction (C). (There are nine files here.)

Notice the use of the this pointer in the C equivalents of the “member” functions. Its place in the parameter list coincides with the “implicit” argument to C++ member functions — that is, the address of the object. The this pointer is implicitly available for use within C++ member functions. Its use depends upon the specific algorithm. Listing 13.13 should give you a good idea of how C++ implements objects.

From the C version in Listing 13.13 it is straightforward to move to the assembly language version in Listing 13.14.

 
1# incFraction.s 
2# adds one to a fraction 
3# Bob Plantz - 18 June 2009 
4 
5# Include object data definition 
6        .include "fraction.h" 
7# Stack frame 
8        .equ    x,-fractionSize # Space for a fraction object 
9        .equ    localSize,x 
10# Read only data 
11        .section  .rodata 
12endl:   .string "\n" 
13# Code 
14        .text 
15        .globl  main 
16        .type   main, @function 
17main: 
18        pushq   %rbp            # save frame pointer 
19        movq    %rsp, %rbp      # our frame pointer 
20        addq    $localSize, %rsp  # local variables 
21        andq    $-16, %rsp      # ensure 16-byte boundary 
22 
23        leaq    x(%rbp), %rdi   # get address of object 
24        call    fraction        # construct it 
25 
26        leaq    x(%rbp), %rdi   # get address of object 
27        call    fractionGet     # get users values 
28 
29        movl    $1, %esi        # increment fraction by 1 
30        leaq    x(%rbp), %rdi   # get address of object 
31        call    fractionAdd     # add the value 
32 
33        leaq    x(%rbp), %rdi   # get address of object 
34        call    fractionDisplay # display result 
35 
36        movq    $endl, %rdi     # do next line 
37        call    writeStr 
38 
39        movl    $0, %eax        # return 0; 
40        movq    %rbp, %rsp      # delete local vars. 
41        popq    %rbp            # restore frame pointer 
42        ret                     # back to caller (OS)
 
1# fraction.h 
2# simple fraction class 
3# Bob Plantz - 18 June 2009 
4 
5# struct definition 
6        .equ    num,0            # numerator 
7        .equ    den,4            # denominator 
8        .equ    fractionSize,8   # total size needed for struct
 
1# fraction.s 
2# constructs a fraction to be 0/1 
3# Bob Plantz - 18 June 2009 
4# Calling sequence: 
5#       rdi <- address of object 
6#       call    decToUInt 
7# Include object data definition 
8        .include "fraction.h" 
9# Read only data 
10        .section  .rodata 
11zero:   .long   0 
12one:    .long   1 
13# Code 
14        .text 
15        .globl  fraction 
16        .type   fraction, @function 
17fraction: 
18        pushq   %rbp            # save frame pointer 
19        movq    %rsp, %rbp      # our frame pointer 
20 
21        movl    zero, %eax 
22        movl    %eax, num(%rdi) # numerator = 0 
23        movl    one, %eax 
24        movl    %eax, den(%rdi) # denominator = 1 
25 
26        movq    %rbp, %rsp      # delete local vars. 
27        popq    %rbp            # restore frame pointer 
28        ret                     # back to caller
 
1# fractionGet.s 
2# gets user values for a fraction 
3# Bob Plantz - 18 June 2009 
4# Calling sequence: 
5#       rdi <- address of object 
6#       call    fractionGet 
7# Include object data definition 
8        .include "fraction.h" 
9# local register save area 
10        .equ    this,-8        # pointer to object 
11        .equ    localSize,-16 
12# Read only data 
13        .section  .rodata 
14numPrompt: 
15        .string "Enter numerator: " 
16denPrompt: 
17        .string "Enter denominator: " 
18# Code 
19        .text 
20        .globl  fractionGet 
21        .type   fractionGet, @function 
22fractionGet: 
23        pushq   %rbp             # save frame pointer 
24        movq    %rsp, %rbp       # our frame pointer 
25        addq    $localSize, %rsp 
26        movq    %rdi, this(%rbp) # save this pointer 
27 
28        movq    $numPrompt, %rdi # prompt for numerator 
29        call    writeStr 
30        call    getUint          # get numerator 
31        movq    this(%rbp), %rdi # this pointer 
32        movl    %eax, num(%rdi)  # store in object 
33 
34        movq    $denPrompt, %rdi    # prompt for denominator 
35        call    writeStr 
36        call    getUint         # get denominator 
37        movq    this(%rbp), %rdi # this pointer 
38        movl    %eax, den(%rdi) # store in object 
39 
40        movq    %rbp, %rsp      # delete local vars. 
41        popq    %rbp            # restore frame pointer 
42        ret                     # back to caller
 
1# fractionDisplay.s 
2# Displays a fraction in num/den format 
3# Bob Plantz - 18 June 2009 
4# Calling sequence: 
5#       rdi <- address of object 
6#       call    fractionDisplay 
7# Include object data definition 
8        .include "fraction.h" 
9# local register save area 
10        .equ    this,-8       # pointer to object 
11        .equ    localSize,-16 
12# Read only data 
13        .section  .rodata 
14slash: 
15        .string " / " 
16# Code 
17        .text 
18        .globl  fractionDisplay 
19        .type   fractionDisplay, @function 
20fractionDisplay: 
21        pushq   %rbp             # save frame pointer 
22        movq    %rsp, %rbp       # our frame pointer 
23        addq    $localSize, %rsp # local vars 
24        movq    %rdi, this(%rbp) # save this pointer 
25 
26        movl  num(%rdi), %edi  # numerator 
27        call  putUint          # display it 
28 
29        movq    $slash, %rdi     # "over" 
30        call    writeStr 
31 
32        movq    this(%rbp), %rdi # get this pointer 
33        movl  den(%rdi), %edi  # denominator 
34        call  putUint          # display it 
35 
36        movq    %rbp, %rsp       # delete local vars. 
37        popq    %rbp             # restore frame pointer 
38        ret                      # back to caller
 
1# fractionAdd.s 
2# adds input value to a fraction 
3# Bob Plantz - 18 June 2009 
4# Calling sequence: 
5#       esi <- int to be added 
6#       rdi <- address of object 
7#       call    fractionAdd 
8# Include object data definition 
9        .include "fraction.h" 
10# local register save area 
11        .equ    this,-8         # pointer to object 
12# Code 
13        .text 
14        .globl  fractionAdd 
15        .type   fractionAdd, @function 
16fractionAdd: 
17        pushq   %rbp             # save frame pointer 
18        movq    %rsp, %rbp       # our frame pointer 
19        movq    %rdi, this(%rsp) # save this pointer in 
20                                 #     red zone 
21        movl    %esi, %eax       # int to be added 
22        mull    den(%rdi)        # times denominator 
23        movq    this(%rsp), %rdi # restore this pointer 
24        addl    %eax, num(%rdi)  # add to numerator 
25 
26        movq    %rbp, %rsp       # delete local vars. 
27        popq    %rbp             # restore frame pointer 
28        ret                      # back to caller
Listing 13.14: Add 1 to user’s fraction (programmer assembly language). (There are six files here; note that the assembly language header file, fraction.h, is different from the C++ version.)

The “header” file, fraction.h, contains offsets for the fields of the struct that defines the state variables for a fraction object. Notice on line 8 that it includes a symbolic name for the size of the object.

8        .equ    fractionSize,8   # total size needed for struct

This makes it easier to define offsets in the stack frame in main:

7# Stack frame 
8        .equ    x,-fractionSize # Space for a fraction object 
9        .equ    localSize,x

We then do:

20# Stack frame 
21        addq    $localSize, %rsp  # local variables 
22        andq    $-16, %rsp      # ensure 16-byte boundary

technique to allocate space on the stack and make sure the stack pointer is on a 16-byte boundary.

In the fraction constructor function we see the use of the field names that are defined in the header file to access the state variables of the object:

21        movl    zero, %eax 
22        movl    %eax, num(%rdi) # numerator = 0 
23        movl    one, %eax 
24        movl    %eax, den(%rdi) # denominator = 1

The fraction_add function is a leaf function. So we use the red zone for saving the this pointer:

16fraction_add: 
17        pushq   %rbp             # save base pointer 
18        movq    %rsp, %rbp       # our frame pointer 
19        movq    %rdi, this(%rsp) # save this pointer in 
20                                 #     red zone

Be careful not to use the red zone in non-leaf functions.

13.5 Instructions Introduced Thus Far

This summary shows the assembly language instructions introduced thus far in the book. The page number where the instruction is explained in more detail, which may be in a subsequent chapter, is also given. This book provides only an introduction to the usage of each instruction. You need to consult the manuals ([2][6], [14][18]) in order to learn all the possible uses of the instructions.

13.5.1 Instructions

data movement:
opcode source destination action page





cbtw convert byte to word, al ax 696





cwtl convert word to long, ax eax 696





cltq convert long to quad, eax rax 696





cwtd convert word to long, ax dx:ax 786





cltd convert long to quad, eax edx:eax 786





cqto convert quad to octuple, rax rdx:rax 786





cmovcc %reg/mem %reg conditional move 706





movs $imm/%reg %reg/mem move 506





movs mem %reg move 506





movsss $imm/%reg %reg/mem move, sign extend 693





movzss $imm/%reg %reg/mem move, zero extend 693





popw %reg/mem pop from stack 566





pushw $imm/%reg/mem push onto stack 566










s = b, w, l, q; w = l, q; cc = condition codes

arithmetic/logic:
opcode source destination action page





adds $imm/%reg %reg/mem add 607





adds mem %reg add 607





ands $imm/%reg %reg/mem bit-wise and 747





ands mem %reg bit-wise and 747





cmps $imm/%reg %reg/mem compare 676





cmps mem %reg compare 676





decs %reg/mem decrement 699





divs %reg/mem unsigned divide 777





idivs %reg/mem signed divide 784





imuls %reg/mem signed multiply 775





incs %reg/mem increment 698





leaw mem %reg load effective address 579





muls %reg/mem unsigned multiply 769





negs %reg/mem negate 789





ors $imm/%reg %reg/mem bit-wise inclusive or 747





ors mem %reg bit-wise inclusive or 747





sals $imm/%cl %reg/mem shift arithmetic left 756





sars $imm/%cl %reg/mem shift arithmetic right 751





shls $imm/%cl %reg/mem shift left 756





shrs $imm/%cl %reg/mem shift right 751





subs $imm/%reg %reg/mem subtract 612





subs mem %reg subtract 612





tests $imm/%reg %reg/mem test bits 676





tests mem %reg test bits 676





xors $imm/%reg %reg/mem bit-wise exclusive or 747





xors mem %reg bit-wise exclusive or 747










s = b, w, l, q; w = l, q

program flow control:
opcode location action page




call label call function 546




ja label jump above (unsigned) 683




jae label jump above/equal (unsigned) 683




jb label jump below (unsigned) 683




jbe label jump below/equal (unsigned) 683




je label jump equal 679




jg label jump greater than (signed) 686




jge label jump greater than/equal (signed) 686




jl label jump less than (signed) 686




jle label jump less than/equal (signed) 686




jmp label jump 691




jne label jump not equal 679




jno label jump no overflow 679




jcc label jump on condition codes 679




leave undo stack frame 580




ret return from function 583




syscall call kernel function 587








cc = condition codes

13.5.2 Addressing Modes

__________________________________________________________

register direct:

The data value is located in a CPU register.

syntax: name of the register with a “%” prefix.

example: movl    %eax, %ebx



immediate data:

The data value is located immediately after the instruction. Source operand only.

syntax: data value with a “$” prefix.

example: movl    $0xabcd1234, %ebx



base register plus offset:

The data value is located in memory. The address of the memory location is the sum of a value in a base register plus an offset value.

syntax: use the name of the register with parentheses around the name and the offset value immediately before the left parenthesis.

example: movl    $0xaabbccdd, 12(%eax)



rip-relative:

The target is a memory address determined by adding an offset to the current address in the rip register.

syntax: a programmer-defined label

example: je     somePlace



indexed:

The data value is located in memory. The address of the memory location is the sum of the value in the base_register plus scale times the value in the index_register, plus the offset.

syntax: place parentheses around the comma separated list (base_register, index_register, scale) and preface it with the offset.

example: movl    $0x6789cdef, -16(%edx, %eax, 4)



13.6 Exercises

13-1

13.1) Write a program in assembly language that allocates a twenty-five element integer array and stores the index value in each element. That is, the first element will be assigned zero, the second element one, etc. Use four-byte integers.

After the array has been completely initialized, display the contents of the array in a column.

13-2

13.1) Write a program in assembly language that allocates a ten element integer array and prompts the user to enter an integer to be stored in each element of the array. Use four-byte integers.

After the user’s values have been stored in the array, compute the sum of the integers. (Do not accumulate the sum as the numbers are entered.) Display the sum.

13-3

13.1) Write a program in assembly language that allocates a ten element integer array and prompts the user to enter an integer to be stored in each element of the array. Use four-byte integers.

After the user’s values have been stored in the array, compute the average of the integers. (Do not accumulate the sum as the numbers are entered.) Display the average.

13-4

13.2) Modify the program in Listing 13.6 so that it displays the total number of bytes allocated for the struct. Hint: use the C sizeof operator.

13-5

13.2) Modify the program of Exercise 13-4 such that it also displays the offset of each field within a struct. Hint: use the C & operator to get addresses.

13-6

13.2) Enter the program in Listing 13.8 and make sure that you understand how it works.

13-7

13.3) Enter the three files from Listing 13.11 and get the program to work.

a)

Create a makefile to assemble and link the files into a program.

b)

Using the debugger, gdb, set breakpoints in the main function at each call to loadStruct and at the instruction immediately following each call.

c)

Use the debugger to observe the values that are stored in the fields of the aByte, anInt, and anotherByte fields each time loadStruct is called. Hint: Note the address in rdi just before executing each function call.

13-8

13.3) Modify the program in Listing 13.8 such that it has separate functions for:

Add two more struct variables to the program. Your program will then call the first function three times, once for each variable. Then it calls the second function three times, also once for each variable.

13-9

13.3) Write a program in assembly language that allocates three variables of the type:

     struct item { 
         char name[50]; 
         int cost; 
     };

That is, each variable will have two fields, one for the name of the item, and one for its cost.

The user will be prompted to enter the name and cost of each item, and the user’s input will be stored in the respective variables.

After the data for all three items is entered, the program will list the name and cost of each of the three items and then display the total cost for all three items.

13-10

13.4) Implement the program in Listing 13.14 such that it allows the user to enter an integer value to be added to the fraction.

13-11

13.4) Modify the program in Exercise 13-10 such that it allows the user to enter a fractional value, then adds the two fractions.

13-12

13.4) Write a program that allows the user to maintain an address book. Each entry into the address book should allow the user to enter

The user should be able to display the entries.

13-13

13.4) Modify the program in Exercise 13-12 so that the user can sort the address book entries on any of the five fields.

13-14

13.4) Write a program that allows the user to set up and maintain two bank accounts. Each account should have a unique account name. The user should be able to credit or debit the account.

13-15

13.4) Modify the program in Exercise 13-14 so that it requires a pin number in order to access each of the bank accounts.