The x86-64 track in Exercism uses NASM (The Netwide Assembler) as its assembler.
In NASM, comments are prefixed by a semicolon (;).
Comments may be placed anywhere in the program and everything that comes after a ; is ignored by the assembler.
An assembler-time constant can be defined in NASM using equ.
For instance, this defines a constant named UNIVERSE with the value 42:
UNIVERSE equ 42
Constants are evaluated once, when defined, and can not be redefined later.
In assembly, instead of variables, we typically use registers to store values. A CPU Register is a piece of fast memory inside a computer's processor. Most computations are carried out in the CPU's registers.
Some of the registers are used for a variety of different computations and are called General Purpose Registers (GPRs). Others have special or dedicated purposes.
In x86-64, there are 16 64-bit General Purpose Registers (GPRs), which can also be accessed as 32-bit, 16-bit, or 8-bit.
The GPRs are described below, where n in rn ranges from 8 to 15: r8, r9, r10, r11, r12, r13, r14 and r15.
| 64-bit | 32-bit | 16-bit | 8-bit |
|---|---|---|---|
| rax | eax | ax | ah/al |
| rbx | ebx | bx | bh/bl |
| rcx | ecx | cx | ch/cl |
| rdx | edx | dx | dh/dl |
| rsi | esi | si | sil |
| rdi | edi | di | dil |
| rbp | ebp | bp | bpl |
| rsp | esp | sp | spl |
| rn | rnd | rnw | rnb |
When using less than 64-bits, the bits accessed are usually from the lower portion of the register.
The exception to this rule are ah, bh, ch and dh, which access the upper 8-bits from the 16-bits portion of the register.
Illustration of how the bits are accessed for the rax register:
+--------+---------------------------------------+
| 64-bit | rax |
+--------+-------------------+-------------------+
| 32-bit | | eax |
+--------+-------------------+---------+---------+
| 16-bit | | ax |
+--------+-----------------------------+----+----+
| 8-bit | | ah | al |
+--------+-----------------------------+----+----+
Some of those registers must be preserved accross function calls: rbp, rsp, rbx, r12, r13, r14 and r15.
Failing to preserve them may lead to an error or to undefined behaviour.
The others are not preserved and may be used freely: rax, rcx, rdx, rdi, rsi, r8, r9, r10 and r11.
Later on in the syllabus, you will learn how to preserve registers.
For now, use only the second type of registers: rax, rcx, rdx, rdi, rsi, r8, r9, r10 and r11.
Instructions are pieces of computations a CPU can perform. They usually have the following form:
name destination, source
So, the name of the instruction is placed first, then at least one whitespace, followed by the destination operand, a comma (,) and finally a source operand.
The source operand isn't typically modified by an instruction, just the destination operand.
Both operands must have the same size unless otherwise noted. For example, the destination operand must also have 16 bits if the source operand has 16 bits.
For instance, to store a value in a register, we can use the mov instruction:
mov rax, rdx ; rax = rdx
; mov is the name, rax is the destination operand and rdx is the source operand
The snippet above copies the contents of all 64 bits of rdx, the source operand, to all 64 bits of rax, which is the destination operand for the instruction.
It is possible to use constant integer numbers as source operands (but not as destination operands) in most instructions:
mov rcx, 42 ; rcx = 42
; rcx is the destination operand and 42 is the source operand
Note that writing to a 32-bit register also clears the upper bits, so mov eax, 42 is the same as mov rax, 42.
This is not true for 16-bit and 8-bit registers.
For the arithmetic operations addition, subtraction, and multiplication, we can use the add, sub, and imul instructions:
add rax, rsi ; rax = rax + rsi
imul rax, rdi ; rax = rax * rdi
sub rax, r8 ; rax = rax - r8
Instructions are organized in functions. All functions are placed in the section .text of the source file.
A function declaration consists of:
:.ret.This track uses the System V AMD64 ABI calling convention and the six first integers arguments are passed to a function in registers.
They are passed in the following order: rdi, rsi, rdx, rcx, r8, and r9.
An integer value is returned from the function in the rax register.
To call a function, we use the call instruction.
A function can be called anywhere in the same source file.
In order to make a function visible to other source files, the global directive must be used.
For instance, this declares a function sum:
section .text ; functions are placed here
global sum ; sum is visible to other source files
sum:
; first argument is passed in rdi
; second argument is passed in rsi
; return value is placed in rax
mov rax, rdi ; rax is now equal to rdi
add rax, rsi ; rax = rax + rsi
ret ; function return
And this calls our sum function with the arguments 3 and 5:
mov rdi, 3 ; First argument in rdi
mov rsi, 5 ; Second argument in rsi
call sum
; The rax register now contains the value 8 (3 + 5), after sum returns
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.
These are the instructions mentioned in this concept:
| Instruction | Description |
|---|---|
| mov a, b | copies the contents from b to a |
| add a, b | a = a + b |
| sub a, b | a = a - b |
| imul a, b | a = a * b |
| call a | calls function a |
| ret | returns from a function |
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:
expected_minutes_in_oven();
// => 40
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.
remaining_minutes_in_oven(30);
// => 10
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.
preparation_time_in_minutes(2);
// => 4
Define the elapsed_time_in_minutes function that takes two parameters, in this order:
The function should return how many minutes you've worked on cooking the lasagna. This is the sum of the preparation time in minutes, and the time in minutes the lasagna has spent in the oven at the moment.
elapsed_time_in_minutes(3, 20);
// => 26
Sign up to Exercism to learn and master x86-64 Assembly with 22 concepts130 exercises, and real human mentoring, all for free.