Skip to main content

Lab 7 - Structures, Vectors and Strings

Task: Fibonacci

You will solve this exercise starting from the fibonacci.asm file located in the drills/tasks/fibonacci directory.

Calculate the Nth Fibonacci number, where N is given through the eax register.

NOTE: The fibonacci series goes as follows : 0, 1, 1, 2, 3, ... where each element f[i] = f[i-2] + f[i-1], except for the first two elements.

TIP: For example, if the value stored in eax is equal to 5, a correct solution will display 3 and for 7, it will display 8.

If you're having difficulties solving this exercise, go through this reading material.

Task: Modifying a Structure

Write code within the main function to modify the fields of the sample_student structure so that:

  • the birth year is 1993

  • the age is 22

  • the group is 323CA

WARNING: Do not modify what is displayed, modify the structure code. Do not touch the display code, that code must remain the same. You need to add at the beginning of the main function, in the place marked with TODO, the code to modify the structure. WARNING: You need to modify the content of the structure in the code, meaning you need to write to the memory area corresponding to the field in the structure. Do not modify the structure in the .data section, you need to use code to modify the structure. TIP: For modifying the group, you will need to change the third byte/character of the group field (i.e., the byte/character with index 2).

If you're having difficulties solving this exercise, go through this reading material.

Task: Getter-Setter

Write in the getter_setter_printf.asm file the instructions needed to display the values of the int_x, char_y, and string_s fields from the sample_obj object.

The program's output after a correct solution should be:

int_x: 1000
char_y: a
string_s: My string is better than yours

Next, overwrite the values of the int_x, char_y, string_s fields in the sample_obj object with the values from the new_int, new_char, and new_string variables.

Validate the results using the display sequence written earlier. After a correct solution, the program's output is:

int_x: 2000
char_y: b
string_s: Are you sure?

Follow the comments marked with TODO.

If you're having difficulties solving this exercise, go through this reading material.

Task: Finding a Substring in a String

Find all occurrences of the substring substring in the source_text string in the find_substring.asm file.

Display the results as follows:

Substring found at index: <N>

IMPORTANT: You cannot use the strstr library function (or similar) for this subtask.

TIP: For display, you can use both the PRINTF32 macro and the printf function, as in previous exercises. The steps for display using printf are as follows:

  • push the value you want to display onto the stack (the position where the substring was found)
  • push the address of the print_format string onto the stack
  • call the printf function
  • clean up the parameters added earlier from the stack by adding the value 8 to the esp register (each parameter is 4 bytes).

If you're having difficulties solving this exercise, go through this reading material.

Task: Mul-arrays

You will solve the exercises starting from the mul_arrays.asm file located in the drills/tasks/mul-arrays directory. Write the Assembly instructions through which, for each pair of one-byte elements with identical indexes from array1 and array2, their product is saved in array3. Then print array3.

The program's output after a correct solution should be:

The array that results from the product of the corresponding elements in array1 and array2 is:
405 12801 1330 59397 2024 6151 280 8449 289 48385

Follow the comments marked with TODO.

IMPORTANT: The product should be computed between two one-byte numbers. Therefore, the result is stored on two bytes.

If you're having difficulties solving this exercise, go through this reading material.

Task: Courses

You will solve the exercises starting from the courses.asm file located in the drills/tasks/courses directory. Fill in the instructions to display the course in which each student is enrolled. We will assume that each student is enrolled in at most one course.

The program's output after a correct solution should be:

The students list is:
Vlad ---- Assembly
Andrew ---- Linear Algebra
Kim ---- Linear Algebra
George ---- Physics
Kate ---- Student unassigned :(

Follow the comments marked with TODO.

If you're having difficulties solving this exercise, go through this reading material.

Structured Data - Structures

We will introduce the concept of structures in assembly language and work with specialized operations on strings.

Structures

Structures are used to group data of different types that can be used together to create a composite type.

Next, we will go through the steps necessary to use a structure: declaration, instantiation, and accessing the fields of a structure.

Declaring a Structure

In NASM, a structure is declared using the construction struc <structure name>, followed by a list of fields and terminated with endstruc.

Each field of the structure is defined by the following: a label (used to access members), the type specifier, and the number of elements.

Example:

struc mystruct
a: resw 1 ; a will refer to a single word-sized element
b: resd 1 ; b will refer to a single double-word-sized element
c: resb 1 ; c will refer to a single byte-sized element
d: resd 1 ; d will refer to a single double-word-sized element
e: resb 6 ; e will refer to 6 byte-sized elements
endstruc

NOTE: Here pseudo-instructions from the NASM res family are used to define the data type and the number of elements for each of the structure fields. For more details about the res syntax, please follow this link: NASM Documentation

Each label defining a field represents the offset of the field within the structure. For example, b will have the value 2, because there are 2 bytes from the beginning of the structure to the b field (the first 2 bytes are occupied by the a word).

WARNING: If you want to use the same field name in two different structures, you must prefix the label name with a dot (.) like this:

struc mystruct1
.a: resw 1
.b: resd 1
endstruc

struc mystruct2
.a: resd 16
.b: resw 1
endstruc

Use the construction mystruct2.b to find the offset value of 'b' within the structure mystruct2.

Instantiating a Structure

One way to have a structure in memory is to declare it statically in the .data section. The syntax uses NASM macros istruc and iend along with the at keyword.

In the following example, static instantiation of the structure declared above is presented, where struct_var is the memory address where the data begins.

struct_var:
istruc mystruct
at a, dw -1
at b, dd 0x12345678
at c, db ' '
at d, dd 23
at e, db 'Gary', 0
iend

If you define the structure fields using a dot (.), the instantiation of the structure is done as follows:

struct_var:
istruc mystruct
at mystruct.a, dw -1
at mystruct.b, dd 0x12345678
at mystruct.c, db ' '
at mystruct.d, dd 23
at mystruct.e, db 'Gary', 0
iend

WARNING: To avoid initializing members incorrectly, you must ensure that for each field, the data type in instantiation matches the type in declaration.

Accessing Values from a Structure

To access and/or modify a specific member of the instantiated structure, we need to know its address. This address can be obtained by calculating the sum of the starting address of the structure and the offset within the structure of the desired member.

The following code sequence demonstrates setting a value in the b field of the structure and subsequently displaying the value of this field.

mov eax, 12345
mov dword [mystruct + b], eax ; the address of field b is the base address of the statically instantiated structure + the offset of the field (given by the label 'b')

mov ebx, dword [mystruct + b] ; putting the value from field b into the ebx register for display
PRINTF32 `%d\n\x0`, ebx

Guide: Students

To follow this guide, you'll need to use the students.asm file located in the guides/students/support directory.

This program iterates through the array of structures representing students and prints the name of each student.

Structured Data - Arrays

Arrays

We can consider an array as a sequence of elements of the same type, placed contiguously in memory. You might have noticed something similar in previous labs when declaring static character strings in the .data section.

Declaring an Array

In general, declared static data can be initialized or uninitialized. Differentiation is made both by providing an initial value for initialized data and by the NASM syntax used.

For example, to declare an array of 100 words initialized with the value 42, we will use the construction:

section .data
my_vect: times 100 dw 42

On the other hand, if we want to declare an uninitialized array of 20 double-word elements, we use instructions from the "res" family as follows:

section .bss
my_vect: resd 20

Arrays of Structures

Often, we'll need arrays that contain elements larger than a double word. To achieve this, we'll combine the two concepts presented earlier and use arrays of structures. Of course, string operation instructions will not work, so we'll have to resort to the classic method of accessing elements: explicit memory addressing.

For the example in this section, we create a structure representing a point in a 2D space.

struc point
.x: resd 1
.y: resd 1
endstruc

Declaring an Array of Structures

Since NASM doesn't support any mechanism to explicitly declare an array of structures, we'll need to effectively declare a data section to accommodate our array.

Suppose we want a zero-initialized array of 100 elements of the structure type point (which is 8 bytes in size), we need to allocate 100 * 8 (= 800) bytes.

We obtain:

section .data
point_array: times 800 db 0

In addition, NASM provides an alternative to manually calculating the size of a structure by automatically generating the macro <structure name>_size. Thus, the previous example can become:

section .data
point_array: times point_size * 100 db 0

If we want to declare an uninitialized array of structures, we can use:

section .bss
point_array: resb point_size * 100

Traversing an Array of Structures

As mentioned before, to access a field of an element in an array, we need to use normal addressing (particularly "based-indexed with scale" addressing). The formula to find the address of the element is base_of_array + i * size_of_struct.

Assuming we have the start address of the array in the ebx register and the index of the element we want to access in the eax register, the following example demonstrates printing the value of the y field of this element.

mov ebx, point_array                            ; Move the start address of the array into ebx
mov eax, 13 ; Assume we want the 14th element
mov edx, [ebx + point_size * eax + point.y] ; Calculate the address of the desired field between []
; and then transfer the value from that address
; into the edx register

PRINTF32 `%u\n\x0`, edx

We traverse the array, having the current index in the eax register at each iteration. We can print the values from both fields of each element in the array with the following program:

struc   point
.x: resd 1
.y: resd 1
endstruc

section .data
point_array: times point_size * 100 db 0

section .text
global CMAIN

CMAIN:
push ebp
mov ebp, esp

xor edx, edx
xor eax, eax

label:
mov edx, [point_array + point_size * eax + point.x] ; access x member
PRINTF32 `%u\n\x0`, edx

mov edx, [point_array + point_size * eax + point.y] ; access y member
PRINTF32 `%u\n\x0`, edx

inc eax ; increment input index
cmp eax, 100
jl label

leave
ret

Guide: Max

To follow this guide, you'll need to use the max.asm file located in the guides/max/support directory.

The program finds the maximum value in an array of 16-bit integers (array). It iterates through the array, updating the maximum value (dx) when it finds a larger value. Finally, it prints the maximum value using the printf() function.

Note: For a detailed description of the instruction, check out the following page: Assembly Arrays Tutorial