Az Exercism x86-64 kurzusa a NASM-et (The Netwide Assembler) használja assemblerként.
A NASM-ben a megjegyzések pontosvesszővel (;) kezdődnek.
A megjegyzések a program bármely pontján elhelyezhetők, és mindent, ami egy ; után következik, figyelmen kívül hagy az assembler.
A NASM-ben definiálhatsz assembler-időben kiértékelt konstanst az equ használatával.
Például ez egy UNIVERSE nevű konstanst definiál 42 értékkel:
UNIVERSE equ 42
A konstansok kiértékelése egyszer történik meg, a definiálásukkor, és később nem definiálhatók újra.
Assemblyben a változók helyett általában regisztereket használunk az értékek tárolására. A CPU-regiszter egy gyors memóriaterület a számítógép processzorán belül. A legtöbb számítás a CPU regisztereiben történik.
Néhány regisztert különféle számításokhoz használunk, ezeket általános célú regisztereknek (GPR-eknek) nevezzük. Mások speciális vagy dedikált feladatot látnak el.
Az x86-64-ben 16 darab 64 bites általános célú regiszter (GPR) van, amelyek 32, 16 vagy 8 bitesként is elérhetők.
A GPR-eket alább ismertetjük, ahol az rn-ben az n 8-tól 15-ig terjed: r8, r9, r10, r11, r12, r13, r14 és r15.
| 64 bites | 32 bites | 16 bites | 8 bites |
|---|---|---|---|
| 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 |
Ha 64 bitnél kevesebbet használunk, az elért bitek általában a regiszter alsó részéből származnak.
Ez alól kivétel az ah, bh, ch és dh, amelyek a regiszter 16 bites részének felső 8 bitjét érik el.
Ábra arról, hogyan érhetők el a bitek a rax regiszter esetében:
+--------+---------------------------------------+
| 64-bit | rax |
+--------+-------------------+-------------------+
| 32-bit | | eax |
+--------+-------------------+---------+---------+
| 16-bit | | ax |
+--------+-----------------------------+----+----+
| 8-bit | | ah | al |
+--------+-----------------------------+----+----+
Ezek közül néhány regisztert meg kell őrizni a függvényhívások során: rbp, rsp, rbx, r12, r13, r14 és r15.
Ha nem őrizzük meg őket, az hibához vagy nem definiált viselkedéshez vezethet.
A többieket nem kell megőrizni, és szabadon használhatók: rax, rcx, rdx, rdi, rsi, r8, r9, r10 és r11.
A tanterv későbbi részében megtanulod, hogyan kell megőrizni a regisztereket.
Egyelőre csak a második típusú regisztereket használd: rax, rcx, rdx, rdi, rsi, r8, r9, r10 és r11.
Az utasítások olyan számítási műveletek, amelyeket a CPU végre tud hajtani. Általában a következő formájuk van:
name destination, source
Az utasítás neve tehát az első, utána legalább egy szóköz következik, majd a céloperandus, egy vessző (,) és végül a forrásoperandus.
Az utasítás általában nem módosítja a forrásoperandust, csak a céloperandust.
Mindkét operandusnak ugyanakkora méretűnek kell lennie, hacsak másképp nem jelezzük. Például ha a forrásoperandus 16 bites, akkor a céloperandusnak is 16 bitesnek kell lennie.
Például egy érték regiszterben való tárolásához használhatjuk a mov utasítást:
mov rax, rdx ; rax = rdx
; mov is the name, rax is the destination operand and rdx is the source operand
A fenti kódrészlet a rdx forrásoperandus mind a 64 bitjének tartalmát átmásolja a rax mind a 64 bitjére, amely az utasítás céloperandusa.
A legtöbb utasításban lehet konstans egész számokat használni forrásoperandusként (de céloperandusként nem):
mov rcx, 42 ; rcx = 42
; rcx is the destination operand and 42 is the source operand
Vedd figyelembe, hogy a 32 bites regiszterbe írás a felső biteket is törli, így a mov eax, 42 ugyanaz, mint a mov rax, 42.
Ez nem igaz a 16 bites és 8 bites regiszterekre.
Az összeadás, kivonás és szorzás aritmetikai műveleteihez használhatjuk az add, sub és imul utasításokat:
add rax, rsi ; rax = rax + rsi
imul rax, rdi ; rax = rax * rdi
sub rax, r8 ; rax = rax - r8
Az utasítások függvényekbe szerveződnek. Minden függvény a forrásfájl section .text részében található.
Egy függvény deklarációja a következőkből áll:
: követ.ret.Ez a kurzus a System V AMD64 ABI hívási konvenciót használja, és az első hat egész argumentum regiszterekben kerül átadásra a függvénynek.
A sorrend a következő: rdi, rsi, rdx, rcx, r8 és r9.
A függvény egy egész értéket a rax regiszterben ad vissza.
Egy függvény meghívásához a call utasítást használjuk.
Egy függvény ugyanazon forrásfájl bármely pontjáról meghívható.
Ahhoz, hogy egy függvényt más forrásfájlok is láthassanak, a global direktívát kell használni.
Például ez deklarál egy sum függvényt:
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
És ez meghívja a sum függvényünket a 3 és 5 argumentumokkal:
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
Ebben a feladatban olyan kódot írsz, amely segít elkészíteni egy remek lasagnét a kedvenc szakácskönyvedből.
Négy részfeladatot kapsz, amelyek mind a lasagne elkészítésével töltött időhöz kapcsolódnak.
Ezek az utasítások szerepelnek a fogalomban:
| Utasítás | Leírás |
|---|---|
| mov a, b | átmásolja b tartalmát a-ba |
| add a, b | a = a + b |
| sub a, b | a = a - b |
| imul a, b | a = a * b |
| call a | meghívja az a függvényt |
| ret | visszatér egy függvényből |
Definiáld az expected_minutes_in_oven függvényt, amely nem vesz át egyetlen paramétert sem, és visszaadja, hogy hány percig kell a lasagnének a sütőben lennie.
A szakácskönyv szerint a sütőben töltendő várható idő 40 perc:
expected_minutes_in_oven();
// => 40
Definiáld a remaining_minutes_in_oven függvényt, amely paraméterként megkapja, hogy a lasagne hány perce van már a sütőben, és visszaadja, hogy az előző részfeladatban megadott várható sütési idő alapján hány percig kell még a sütőben maradnia.
remaining_minutes_in_oven(30);
// => 10
Definiáld a preparation_time_in_minutes függvényt, amely paraméterként megkapja, hogy hány réteget tettél a lasagnére, és visszaadja, hogy hány percet töltöttél a lasagne elkészítésével, feltéve, hogy egy réteg elkészítése 2 percet vesz igénybe.
preparation_time_in_minutes(2);
// => 4
Definiáld az elapsed_time_in_minutes függvényt, amely két paramétert vesz át, ebben a sorrendben:
A függvény adja vissza, hogy hány percet dolgoztál a lasagne elkészítésén. Ez az előkészítési idő percben és a lasagne által a sütőben eddig eltöltött idő összege.
elapsed_time_in_minutes(3, 20);
// => 26
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) x86-64 Assembly nyelvet 22 fogalom130 feladat segítségével, valódi emberi mentorálással, mindez ingyen.