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 elementf[i] = f[i-2] + f[i-1]
, except for the first two elements.TIP: For example, if the value stored in
eax
is equal to5
, a correct solution will display3
and for7
, it will display8
.
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 withTODO
, 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 thegroup
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 theres
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 structuremystruct2
.
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