Skip to content

Commit

Permalink
x86-64-assembly/basics: add solution
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikSchierboom committed Nov 15, 2023
1 parent d8b908e commit 8173027
Show file tree
Hide file tree
Showing 11 changed files with 302 additions and 0 deletions.
10 changes: 10 additions & 0 deletions x86-64-assembly/basics/.exercism/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"blurb": "Learn about the basics of x86-64 assembly by following a lasagna recipe.",
"authors": ["bergjohan"],
"forked_from": ["csharp/lucians-luscious-lasagna"],
"files": {
"solution": ["basics.asm"],
"test": ["basics_test.c"],
"exemplar": [".meta/exemplar.asm"]
}
}
1 change: 1 addition & 0 deletions x86-64-assembly/basics/.exercism/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"track":"x86-64-assembly","exercise":"basics","id":"8c7660921c3a498b87f85737ac342f82","url":"https://exercism.org/tracks/x86-64-assembly/exercises/basics","handle":"ErikSchierboom","is_requester":true,"auto_approve":false}
45 changes: 45 additions & 0 deletions x86-64-assembly/basics/HELP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Help

## Running the tests

To run the tests, execute the following command:

```bash
make
```

## Skipped tests

Solving an exercise means making all its tests pass. By default, only one test
(the first one) is executed when you run the tests. This is intentional, as it
allows you to focus on just making that one test pass. Once it passes, you can
enable the next test by removing the `TEST_IGNORE()` line. When all tests have
been enabled and your implementation makes them all pass, you'll have solved
the exercise!

## Submitting your solution

You can submit your solution using the `exercism submit basics.asm` command.
This command will upload your solution to the Exercism website and print the solution page's URL.

It's possible to submit an incomplete solution which allows you to:

- See how others have completed the exercise
- Request help from a mentor

## Need to get help?

If you'd like help solving the exercise, check the following pages:

- The [x86-64 Assembly track's documentation](https://exercism.org/docs/tracks/x86-64-assembly)
- The [x86-64 Assembly track's programming category on the forum](https://forum.exercism.org/c/programming/x86-64-assembly)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)

Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.

To get help if you're having trouble, you can use one of the following resources:

- [The NASM Manual](https://www.nasm.us/doc/)
- [x86 and amd64 instruction reference](https://www.felixcloutier.com/x86/)
- [StackOverflow](http://stackoverflow.com) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.
31 changes: 31 additions & 0 deletions x86-64-assembly/basics/HINTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Hints

## General

- You need to make the functions [visible to other files][global].
- The [NASM documentation][interfacing] describes how function arguments are passed and return values are retrieved.

## 1. Define the expected oven time in minutes

- There is an [instruction][mov] to store a value in a register.

## 2. Calculate the remaining oven time in minutes

- There is an [instruction][sub] for subtracting values.

## 3. Calculate the preparation time in minutes

- There is an [instruction][imul] for multiplying values.

## 4. Calculate the elapsed time in minutes

- You can [call][call] one of the other functions you've defined previously.
- There is an [instruction][add] for adding values.

[global]: https://www.nasm.us/xdoc/2.15.02/html/nasmdoc7.html#section-7.7
[interfacing]: https://www.nasm.us/xdoc/2.15.02/html/nasmdo12.html#section-12.3
[mov]: https://www.felixcloutier.com/x86/mov
[sub]: https://www.felixcloutier.com/x86/sub
[imul]: https://www.felixcloutier.com/x86/imul
[call]: https://www.felixcloutier.com/x86/call
[add]: https://www.felixcloutier.com/x86/add
43 changes: 43 additions & 0 deletions x86-64-assembly/basics/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
AS = nasm

CFLAGS = -g -Wall -Wextra -pedantic -Werror
LDFLAGS =
ASFLAGS = -g -F dwarf -Werror

ifeq ($(shell uname -s),Darwin)
ifeq ($(shell sysctl -n hw.optional.arm64 2>/dev/null),1)
ALL_CFLAGS = -target x86_64-apple-darwin
endif
ALL_LDFLAGS = -Wl,-pie -Wl,-fatal_warnings
ALL_ASFLAGS = -f macho64 --prefix _
else
ALL_LDFLAGS = -pie -Wl,--fatal-warnings
ALL_ASFLAGS = -f elf64
endif

ALL_CFLAGS += -std=c99 -fPIE -m64 $(CFLAGS)
ALL_LDFLAGS += $(LDFLAGS)
ALL_ASFLAGS += $(ASFLAGS)

C_OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
AS_OBJS = $(patsubst %.asm,%.o,$(wildcard *.asm))
ALL_OBJS = $(C_OBJS) $(AS_OBJS)

CC_CMD = $(CC) $(ALL_CFLAGS) -c -o $@ $<

all: tests
@./$<

tests: $(ALL_OBJS)
@$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $(ALL_OBJS)

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

%.o: %.c
@$(CC_CMD)

clean:
@rm -f *.o tests

.PHONY: all clean
128 changes: 128 additions & 0 deletions x86-64-assembly/basics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Basics

Welcome to Basics on Exercism's x86-64 Assembly Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)

## Introduction

x86-64 assembly is a low-level language. In assembly there are no variables,
instead we use registers to store values. Some registers have a special purpose
such as returning a value from a function, or passing function arguments. To
store a value in a register, we use the `mov` instruction:

```nasm
mov rax, 42 ; rax = 42
```

An assembly program is divided into sections. The text section holds the
executable instructions of a program and is declared as follows:

```nasm
section .text
```

A function is a set of instructions that perform a specific task. A function
declaration consists of a label with the name of the function, the instructions
that define the function, and the return instruction. The following declares a
function called `foo`, which returns the value 42:

```nasm
foo:
mov rax, 42
ret
```

The value in the `rax` register specifies the value returned by the function.

To change the visibility of a function, and be able to call it from any file in
our program we use the `global` directive:

```nasm
global foo
```

When a function is called, the first argument is stored in the `rdi` register,
and the second argument is stored in the `rsi` register. Here's an example of a
function that takes a single argument and returns it, also known as an identity
function:

```nasm
global identity
identity:
mov rax, rdi
ret
```

For the arithmetic operations addition, subtraction, and multiplication, we can
use the `add`, `sub`, and `imul` instructions. They take two operands, a source
operand (first operand), and a destination operand (second operand), performs
the arithmetic operation, and stores the result in the destination operand.
Here's an example of a function that takes two arguments, adds them together,
and returns the result:

```nasm
global sum
sum:
mov rax, rdi
add rax, rsi ; rax += rsi
ret
```

To call a function, we use the `call` instruction. For example, to call our
`sum` function with the arguments 3 and 5, we would do the following:

```nasm
mov rdi, 3 ; First argument in rdi
mov rsi, 5 ; Second argument in rsi
call sum
; The rax register now contains the value 8
```

## Instructions

In this exercise you're going to write some code to help you cook a brilliant lasagna from your favorite cooking book.

You have four tasks, all related to the time spent cooking the lasagna.

## 1. Define the expected oven time in minutes

Define the `expected_minutes_in_oven()` function that does not take any parameters and returns how many minutes the lasagna should be in the oven. According to the cooking book, the expected oven time in minutes is 40:

```c
expected_minutes_in_oven();
// => 40
```

## 2. Calculate the remaining oven time in minutes

Define the `remaining_minutes_in_oven()` function that takes the actual minutes the lasagna has been in the oven as a parameter and returns how many minutes the lasagna still has to remain in the oven, based on the expected oven time in minutes from the previous task.

```c
remaining_minutes_in_oven(30);
// => 10
```
## 3. Calculate the preparation time in minutes
Define the `preparation_time_in_minutes()` function that takes the number of layers you added to the lasagna as a parameter and returns how many minutes you spent preparing the lasagna, assuming each layer takes you 2 minutes to prepare.
```c
preparation_time_in_minutes(2);
// => 4
```

## 4. Calculate the elapsed time in minutes

Define the `elapsed_time_in_minutes()` function that takes two parameters: the first parameter is the number of layers you added to the lasagna, and the second parameter is the number of minutes the lasagna has been in the oven. The function should return how many minutes you've worked on cooking the lasagna, which is the sum of the preparation time in minutes, and the time in minutes the lasagna has spent in the oven at the moment.

```c
elapsed_time_in_minutes(3, 20);
// => 26
```
## Source
### Created by
- @bergjohan
28 changes: 28 additions & 0 deletions x86-64-assembly/basics/basics.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
section .text

global expected_minutes_in_oven
expected_minutes_in_oven:
mov rax, 40
ret

global remaining_minutes_in_oven
remaining_minutes_in_oven:
call expected_minutes_in_oven
sub rax, rdi
ret

global preparation_time_in_minutes
preparation_time_in_minutes:
mov rax, rdi
imul rax, 2
ret

global elapsed_time_in_minutes
elapsed_time_in_minutes:
call preparation_time_in_minutes
add rax, rsi
ret

%ifidn __OUTPUT_FORMAT__,elf64
section .note.GNU-stack noalloc noexec nowrite progbits
%endif
Binary file added x86-64-assembly/basics/basics.o
Binary file not shown.
16 changes: 16 additions & 0 deletions x86-64-assembly/basics/basics_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <assert.h>
#include <inttypes.h>

int64_t expected_minutes_in_oven(void);
int64_t remaining_minutes_in_oven(int64_t actual_minutes_in_oven);
int64_t preparation_time_in_minutes(int64_t number_of_layers);
int64_t elapsed_time_in_minutes(int64_t number_of_layers, int64_t actual_minutes_in_oven);

int main(void) {
assert(40 == expected_minutes_in_oven());
assert(15 == remaining_minutes_in_oven(25));
assert(2 == preparation_time_in_minutes(1));
assert(8 == preparation_time_in_minutes(4));
assert(32 == elapsed_time_in_minutes(1, 30));
assert(16 == elapsed_time_in_minutes(4, 8));
}
Binary file added x86-64-assembly/basics/basics_test.o
Binary file not shown.
Binary file added x86-64-assembly/basics/tests
Binary file not shown.

0 comments on commit 8173027

Please sign in to comment.