Το track x86-64 στο Exercism χρησιμοποιεί το NASM (The Netwide Assembler) ως συμβολομεταφραστή του.
Στο NASM, τα σχόλια ξεκινούν με το σύμβολο ;.
Τα σχόλια μπορούν να τοποθετηθούν οπουδήποτε στο πρόγραμμα και ό,τι ακολουθεί μετά από ένα ; αγνοείται από τον συμβολομεταφραστή.
Μια σταθερά χρόνου συμβολομετάφρασης μπορεί να οριστεί στο NASM με το equ.
Για παράδειγμα, αυτό ορίζει μια σταθερά με όνομα UNIVERSE και τιμή 42:
UNIVERSE equ 42
Οι σταθερές αξιολογούνται μία φορά, όταν ορίζονται, και δεν μπορούν να επαναοριστούν αργότερα.
Στην assembly, αντί για μεταβλητές, συνήθως χρησιμοποιούμε καταχωρητές για να αποθηκεύουμε τιμές. Ένας καταχωρητής CPU είναι ένα κομμάτι γρήγορης μνήμης μέσα στον επεξεργαστή ενός υπολογιστή. Οι περισσότεροι υπολογισμοί εκτελούνται μέσα στους καταχωρητές της CPU.
Κάποιοι από τους καταχωρητές χρησιμοποιούνται για ποικίλους διαφορετικούς υπολογισμούς και ονομάζονται καταχωρητές γενικού σκοπού (GPRs). Άλλοι έχουν ειδικούς ή αποκλειστικούς σκοπούς.
Στο x86-64 υπάρχουν 16 καταχωρητές γενικού σκοπού (GPRs) των 64 bit, στους οποίους μπορεί επίσης να γίνει πρόσβαση ως 32 bit, 16 bit ή 8 bit.
Οι GPRs περιγράφονται παρακάτω, όπου το n στο rn κυμαίνεται από 8 έως 15: r8, r9, r10, r11, r12, r13, r14 και 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 |
Όταν χρησιμοποιούνται λιγότερα από 64 bit, τα bit στα οποία γίνεται πρόσβαση προέρχονται συνήθως από το χαμηλότερο τμήμα του καταχωρητή.
Εξαίρεση σε αυτόν τον κανόνα αποτελούν οι ah, bh, ch και dh, που έχουν πρόσβαση στα ανώτερα 8 bit από το τμήμα των 16 bit του καταχωρητή.
Απεικόνιση του τρόπου με τον οποίο γίνεται η πρόσβαση στα bit για τον καταχωρητή rax:
+--------+---------------------------------------+
| 64-bit | rax |
+--------+-------------------+-------------------+
| 32-bit | | eax |
+--------+-------------------+---------+---------+
| 16-bit | | ax |
+--------+-----------------------------+----+----+
| 8-bit | | ah | al |
+--------+-----------------------------+----+----+
Κάποιοι από αυτούς τους καταχωρητές πρέπει να διατηρούνται κατά τις κλήσεις συναρτήσεων: rbp, rsp, rbx, r12, r13, r14 και r15.
Η μη διατήρησή τους μπορεί να οδηγήσει σε σφάλμα ή σε απροσδιόριστη συμπεριφορά.
Οι υπόλοιποι δεν διατηρούνται και μπορούν να χρησιμοποιηθούν ελεύθερα: rax, rcx, rdx, rdi, rsi, r8, r9, r10 και r11.
Αργότερα στο πρόγραμμα σπουδών θα μάθεις πώς να διατηρείς τους καταχωρητές.
Για την ώρα, χρησιμοποίησε μόνο τον δεύτερο τύπο καταχωρητών: rax, rcx, rdx, rdi, rsi, r8, r9, r10 και r11.
Οι εντολές είναι κομμάτια υπολογισμών που μπορεί να εκτελέσει μια CPU. Συνήθως έχουν την εξής μορφή:
name destination, source
Έτσι, πρώτα τοποθετείται το όνομα της εντολής, μετά τουλάχιστον ένα κενό διάστημα, ακολουθούμενο από τον τελεστέο προορισμού, ένα κόμμα (,) και τέλος έναν τελεστέο πηγής.
Ο τελεστέος πηγής συνήθως δεν τροποποιείται από μια εντολή, μόνο ο τελεστέος προορισμού.
Και οι δύο τελεστέοι πρέπει να έχουν το ίδιο μέγεθος, εκτός αν αναφέρεται διαφορετικά. Για παράδειγμα, ο τελεστέος προορισμού πρέπει να έχει κι αυτός 16 bit αν ο τελεστέος πηγής έχει 16 bit.
Για παράδειγμα, για να αποθηκεύσουμε μια τιμή σε έναν καταχωρητή, μπορούμε να χρησιμοποιήσουμε την εντολή mov:
mov rax, rdx ; rax = rdx
; mov is the name, rax is the destination operand and rdx is the source operand
Το παραπάνω απόσπασμα αντιγράφει τα περιεχόμενα και των 64 bit του rdx, του τελεστέου πηγής, στα 64 bit του rax, που είναι ο τελεστέος προορισμού της εντολής.
Στις περισσότερες εντολές είναι δυνατό να χρησιμοποιηθούν σταθεροί ακέραιοι αριθμοί ως τελεστέοι πηγής (αλλά όχι ως τελεστέοι προορισμού):
mov rcx, 42 ; rcx = 42
; rcx is the destination operand and 42 is the source operand
Σημείωσε ότι η εγγραφή σε έναν καταχωρητή 32 bit καθαρίζει επίσης τα ανώτερα bit, οπότε το mov eax, 42 είναι το ίδιο με το mov rax, 42.
Αυτό δεν ισχύει για καταχωρητές 16 bit και 8 bit.
Για τις αριθμητικές πράξεις της πρόσθεσης, της αφαίρεσης και του πολλαπλασιασμού, μπορούμε να χρησιμοποιήσουμε τις εντολές add, sub και imul:
add rax, rsi ; rax = rax + rsi
imul rax, rdi ; rax = rax * rdi
sub rax, r8 ; rax = rax - r8
Οι εντολές οργανώνονται σε συναρτήσεις. Όλες οι συναρτήσεις τοποθετούνται στη section .text του αρχείου πηγαίου κώδικα.
Η δήλωση μιας συνάρτησης αποτελείται από:
:.ret.Αυτό το track χρησιμοποιεί τη σύμβαση κλήσης System V AMD64 ABI και τα έξι πρώτα ακέραια ορίσματα περνούν σε μια συνάρτηση μέσα σε καταχωρητές.
Περνούν με την εξής σειρά: rdi, rsi, rdx, rcx, r8 και r9.
Μια ακέραια τιμή επιστρέφεται από τη συνάρτηση στον καταχωρητή rax.
Για να καλέσουμε μια συνάρτηση, χρησιμοποιούμε την εντολή call.
Μια συνάρτηση μπορεί να κληθεί οπουδήποτε στο ίδιο αρχείο πηγαίου κώδικα.
Για να γίνει μια συνάρτηση ορατή σε άλλα αρχεία πηγαίου κώδικα, πρέπει να χρησιμοποιηθεί η οδηγία global.
Για παράδειγμα, αυτό δηλώνει μια συνάρτηση 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
Και αυτό καλεί τη συνάρτησή μας sum με ορίσματα 3 και 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
Σε αυτή την άσκηση θα γράψεις λίγο κώδικα για να σε βοηθήσει να μαγειρέψεις μια υπέροχη λαζάνια από το αγαπημένο σου βιβλίο μαγειρικής.
Έχεις τέσσερις εργασίες, όλες σχετικές με τον χρόνο που θα χρειαστείς για το μαγείρεμα της λαζάνιας.
Αυτές είναι οι εντολές που αναφέρονται σε αυτή την έννοια:
| Instruction | Description |
|---|---|
| mov a, b | αντιγράφει τα περιεχόμενα από το b στο a |
| add a, b | a = a + b |
| sub a, b | a = a - b |
| imul a, b | a = a * b |
| call a | καλεί τη συνάρτηση a |
| ret | επιστρέφει από μια συνάρτηση |
Όρισε τη συνάρτηση expected_minutes_in_oven, η οποία δεν παίρνει καμία παράμετρο και επιστρέφει πόσα λεπτά πρέπει να μείνει η λαζάνια στο φούρνο.
Σύμφωνα με το βιβλίο μαγειρικής, ο αναμενόμενος χρόνος στο φούρνο, σε λεπτά, είναι 40:
expected_minutes_in_oven();
// => 40
Όρισε τη συνάρτηση remaining_minutes_in_oven, η οποία παίρνει ως παράμετρο τα πραγματικά λεπτά που έχει ήδη μείνει η λαζάνια στο φούρνο και επιστρέφει πόσα λεπτά πρέπει να μείνει ακόμη, με βάση τον αναμενόμενο χρόνο στο φούρνο, σε λεπτά, από την προηγούμενη εργασία.
remaining_minutes_in_oven(30);
// => 10
Όρισε τη συνάρτηση preparation_time_in_minutes, η οποία παίρνει ως παράμετρο τον αριθμό των στρώσεων που πρόσθεσες στη λαζάνια και επιστρέφει πόσα λεπτά χρειάστηκες για να την προετοιμάσεις, υποθέτοντας ότι κάθε στρώση σου παίρνει 2 λεπτά.
preparation_time_in_minutes(2);
// => 4
Όρισε τη συνάρτηση elapsed_time_in_minutes, η οποία παίρνει δύο παραμέτρους, με αυτή τη σειρά:
Η συνάρτηση πρέπει να επιστρέφει πόσα λεπτά έχεις δουλέψει για το μαγείρεμα της λαζάνιας. Αυτό είναι το άθροισμα του χρόνου προετοιμασίας σε λεπτά και του χρόνου σε λεπτά που έχει περάσει η λαζάνια στο φούρνο μέχρι στιγμής.
elapsed_time_in_minutes(3, 20);
// => 26
Γράψου στο Exercism για να μάθεις και να κατακτήσεις x86-64 Assembly με 22 έννοιες130 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.