Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stack/functions: Add functions lab. #21

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions chapters/stack/functions/drills/tasks/print_rev_string/README.md
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the name of the folder to print-rev-string (using dashes -, not underscores _). Do the same for all folder names.

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Displaying the Reversed String

In the file `print_rev_string.asm`, add the `reverse_string` function so that you have a listing similar to the one below:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In the file `print_rev_string.asm`, add the `reverse_string` function so that you have a listing similar to the one below:
In the file `print_rev_string.asm`, add the `reverse_string()` function so that you have a listing similar to the one below:

Use () at the end of function names to distinguish them better from simple labels.


```Assembly
[...]
section .text
global main

reverse_string:
push ebp
mov ebp, esp

mov eax, dword [ebp+8]
mov ecx, dword [ebp+12]
add eax, ecx
dec eax
mov edx, dword [ebp+16]

copy_one_byte:
mov bl, byte [eax]
mov byte [edx], bl
Comment on lines +14 to +22
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mov eax, dword [ebp+8]
mov ecx, dword [ebp+12]
add eax, ecx
dec eax
mov edx, dword [ebp+16]
copy_one_byte:
mov bl, byte [eax]
mov byte [edx], bl
mov eax, [ebp + 8]
mov ecx, [ebp + 12]
add eax, ecx
dec eax
mov edx, [ebp + 16]
copy_one_byte:
mov bl, [eax]
mov [edx], bl

Using dword pr byte here is pointless because they can be inferred due to the other operand being a register.

dec eax
inc edx
loopnz copy_one_byte

inc edx
mov byte [edx], 0

leave
ret

main:
push ebp
mov ebp, esp
[...]
```

> **IMPORTANT:** When copying the `reverse_string` function into your program, remember that the function starts at the `reverse_string` label and ends at the `main` label. The `copy_one_byte` label is part of the `reverse_string` function.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
> **IMPORTANT:** When copying the `reverse_string` function into your program, remember that the function starts at the `reverse_string` label and ends at the `main` label. The `copy_one_byte` label is part of the `reverse_string` function.
> **IMPORTANT:** When copying the `reverse_string()` function into your program, remember that the function starts at the `reverse_string` label and ends at the `main` label.
> The `copy_one_byte` label is part of the `reverse_string` function.

Write one sentence per line. Everywhere. Also, notice the distinction I made above between the reverse_string() (with ()) function and the reverse_string label without (()).


The `reverse_string` function reverses a string and has the following signature: `void reverse_string(const char *src, size_t len, char *dst);`. This means that the first `len` characters of the `src` string are reversed into the `dst` string.

Reverse the `mystring` string into a new string and display that new string.

> **NOTE:** To define a new string, we recommend using the following construction in the data section:
>
> ```Assembly
> store_string times 64 db 0
> ```
>
> This creates a string of 64 zero bytes, enough to store the reverse of the string.
> The equivalent C function call is `reverse_string(mystring, ecx, store_string);`. We assume that the length of the string is calculated and stored in the `ecx` register.
>
> You cannot directly use the value of `ecx` in its current form. After the `printf` function call for displaying the length, the value of `ecx` is lost. To retain it, you have two options:
>
> 1. Store the value of the `ecx` register on the stack beforehand (using `push ecx` before the `printf` call) and then restore it after the `printf` call (using `pop ecx`).
> 2. Store the value of the `ecx` register in a global variable, which you define in the `.data` section.
>
> You cannot use another register because there is a high chance that even that register will be modified by the `printf` call to display the length of the string.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a line about going through the corresponding section in the reading material to the tasks where you haven't done so already.

Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
section .data

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add some explanation comments if this is the solution. I think students should read the comments rather than copy the code

mystring db "This is my string", 0
print_format db "String length is %d", 10, 0
store_string times 64 db 0

section .text

extern printf
extern puts
global main

reverse_string:
push ebp
mov ebp, esp

mov eax, dword [ebp+8]
mov ecx, dword [ebp+12]
add eax, ecx
dec eax
mov edx, dword [ebp+16]

copy_one_byte:
mov bl, byte [eax]
mov byte [edx], bl
dec eax
inc edx
loopnz copy_one_byte

inc edx
mov byte [edx], 0

leave
ret

main:
push ebp
mov ebp, esp

mov eax, mystring
xor ecx, ecx
test_one_byte:
mov bl, byte [eax]
test bl, bl
jz out
inc eax
inc ecx
jmp test_one_byte

out:
; save ecx's value since it can be changed by printf
push ecx
Comment on lines +16 to +51
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mov eax, dword [ebp+8]
mov ecx, dword [ebp+12]
add eax, ecx
dec eax
mov edx, dword [ebp+16]
copy_one_byte:
mov bl, byte [eax]
mov byte [edx], bl
dec eax
inc edx
loopnz copy_one_byte
inc edx
mov byte [edx], 0
leave
ret
main:
push ebp
mov ebp, esp
mov eax, mystring
xor ecx, ecx
test_one_byte:
mov bl, byte [eax]
test bl, bl
jz out
inc eax
inc ecx
jmp test_one_byte
out:
; save ecx's value since it can be changed by printf
push ecx
mov eax, [ebp + 8]
mov ecx, [ebp + 12]
add eax, ecx
dec eax
mov edx, [ebp + 16]
copy_one_byte:
mov bl, [eax]
mov [edx], bl
dec eax
inc edx
loopnz copy_one_byte
inc edx
mov [edx], 0
leave
ret
main:
push ebp
mov ebp, esp
mov eax, mystring
xor ecx, ecx
test_one_byte:
mov bl, [eax]
test bl, bl
jz out
inc eax
inc ecx
jmp test_one_byte
out:
; Save ecx's value since it can be changed by printf.
push ecx


push ecx
push print_format
call printf
add esp, 8

pop ecx

push store_string
push ecx
push mystring
call reverse_string
add esp, 12

push store_string
call puts
add esp, 4

leave
ret
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
AS = nasm
CC = gcc
RM = rm

SRCS := $(shell find . -name "*.asm")
OBJS := $(SRCS:.asm=.o)

UTILSDIR := ../utils/

ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)"
CFLAGS ?= -Wall
LDFLAGS ?= -m32 -no-pie

all: print_reverse_string

print_reverse_string_: print_reverse_string.o
$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@

%.o: %.asm
$(AS) $(ASFLAGS) $< -o $@

.PHONY: clean

clean:
$(RM) -r *.o print_reverse_string
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
%include "../utils/printf32.asm"

section .data
mystring db "This is my string", 0

section .text

extern puts
extern printf
global main

main:
push ebp
mov ebp, esp

mov eax, mystring
xor ecx, ecx
test_one_byte:
mov bl, byte [eax]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mov bl, byte [eax]
mov bl, [eax]

test bl, bl
je out
inc eax
inc ecx
jmp test_one_byte

out:
PRINTF32 `[before]: %s\n[after]: \x0`, mystring
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
PRINTF32 `[before]: %s\n[after]: \x0`, mystring
PRINTF32 `[before]: %s\n[after]: \x0`, mystring

Change this to a call to printf as a "model" for how it should be called.


; TODO: print reverse string

leave
ret
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
; SPDX-License-Identifier: BSD-3-Clause

;;; macro to use printf with 32bit parameters:
;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0`
;;; escape \n and \x0 only work with backquotes
;;; - rest of parameters MUST be 32bit
;;; - gen purpose and flags are preserved
;;; - stack is cleaned
%macro PRINTF32 1-*
pushf
pushad
jmp %%endstr
%%str: db %1
%%endstr:
%rep %0 - 1
%rotate -1
push dword %1
%endrep
push %%str
call printf
add esp, 4*%0
popad
popf
%endmacro
23 changes: 23 additions & 0 deletions chapters/stack/functions/drills/tasks/rot13/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Bonus: Rot13

Create and use a function that performs [rot13](https://rot13.com/) translation of a string.

# Bonus: Rot13++
Comment on lines +1 to +5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Bonus: Rot13
Create and use a function that performs [rot13](https://rot13.com/) translation of a string.
# Bonus: Rot13++
# Rot13
Create and use a function that performs [rot13](https://rot13.com/) translation of a string.
# Rot13++


Implement `rot13` on an array of strings: the strings are contiguous in memory separated by the string terminator (`NULL`-byte, `0`). For example, `ana\0are\0mere\0` is an array of three strings.

Apply `rot13` to alphabetical characters and replace the string terminator with a space (`' '`, blank, character `32`, or `0x20`). Thus, the initial string `ana\0are\0mere\0` will translate to `nan ner zrer`.

> **NOTE:** To define the array of strings containing the string terminator, use a construction like:
>
> ```Assembly
> mystring db "ana", 0, "are", 0, "mere", 0
> ```
Comment on lines +7 to +15
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the example text to something in English. Or use Lorem ipsum, just make it usable outside Romania.


> **NOTE:** You will need to know when to stop traversing the array of strings. The simplest way is to define a length variable in the `.data` section, like so:
>
> ```Assembly
> len dd 10
> ```
>
> where you either store the total length of the string (from the beginning to the last `NULL` byte) or the number of strings in the array.
8 changes: 8 additions & 0 deletions chapters/stack/functions/drills/tasks/string_print/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Displaying a String

To display a string, we can use the internal macro `PRINTF32`. Alternatively, we can use a function such as `puts`. In the file `print_string.asm`, displaying a string using the `PRINTF32` macro is implemented.

Following the example of the `hello_world.asm` file, implement string display using `puts` as well.


Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

If you're having difficulties solving this exercise, take a peek at [hello_world.asm](../../../guides/hello_world/).
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
%include "../utils/printf32.asm"

section .data
mystring db "This is my string", 0

section .text

extern puts
extern printf
global main
main:
push ebp
mov ebp, esp

PRINTF32 `[PRINTF32]: %s\n[PUTS]: \x0`, mystring


push mystring
call puts
add esp, 4

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Students will not understand why this add esp, 4 is needed when the call returns


leave
ret
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
AS = nasm
CC = gcc
RM = rm

SRCS := $(shell find . -name "*.asm")
OBJS := $(SRCS:.asm=.o)

UTILSDIR := ../utils/

ASFLAGS ?= -f elf32 -F dwarf -I "$(UTILSDIR)"
CFLAGS ?= -Wall
LDFLAGS ?= -m32 -no-pie

all: print_string

print_string: print_string.o
$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@

%.o: %.asm
$(AS) $(ASFLAGS) $< -o $@

.PHONY: clean

clean:
$(RM) -r *.o print_string
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
%include "../utils/printf32.asm"

section .data
mystring db "This is my string", 0

section .text

extern puts
extern printf
global main
main:
push ebp
mov ebp, esp

PRINTF32 `[PRINTF32]: %s\n[PUTS]: \x0`, mystring

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change


; TODO: call puts on string

leave
ret

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly recommand a new line at the end of file. See [1]
[1] https://gist.github.com/OleksiyRudenko/d51388345ea55767b7672307fe35adf3

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
; SPDX-License-Identifier: BSD-3-Clause

;;; macro to use printf with 32bit parameters:
;;; - 1st parameter MUST be an immediate in backquotes `EAX=%d ECX=%x \n\x0`
;;; escape \n and \x0 only work with backquotes
;;; - rest of parameters MUST be 32bit
;;; - gen purpose and flags are preserved
;;; - stack is cleaned
%macro PRINTF32 1-*
pushf
pushad
jmp %%endstr
%%str: db %1
%%endstr:
%rep %0 - 1
%rotate -1
push dword %1
%endrep
push %%str
call printf
add esp, 4*%0
popad
popf
%endmacro
20 changes: 20 additions & 0 deletions chapters/stack/functions/drills/tasks/string_print_len/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Displaying the Length of a String

The program `print_string_len.asm` displays the length of a string using the `PRINTF32` macro. The calculation of the length of the `mystring` string occurs within the program (it is already implemented).

Implement the program to display the length of the string using the `printf` function.

At the end, you will have the length of the string displayed twice: initially with the `PRINTF32` macro and then with the external function call `printf`.

> **NOTE:** Consider that the `printf` call is of the form `printf("String length is %u\n", len);`. You need to construct the stack for this call.
>
> The steps to follow are:
>
> 1. Mark the symbol `printf` as external.
> 2. Define the format string `"String length is %u", 10, 0`.
> 3. Make the function call to `printf`, i.e.:
> 1. Put the two arguments on the stack: the format string and the length.
> 2. Call `printf` using `call`.
> 3. Restore the stack.
>
> The length of the string is found in the `ecx` register.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a link to a relevant reading/ section in case students find this task difficult.

Loading
Loading