Σε αυτή την άσκηση, θα ορίσεις τη σύνταξη και το μοντέλο εκτέλεσης μιας μικρής γλώσσας εμπνευσμένης από τη γλώσσα assembly x86-64.
Στόχος είναι να επισημοποιήσεις τον τρόπο με τον οποίο ο κώδικας γράφεται, αναλύεται και εκτελείται μέσα σε ένα περιορισμένο περιβάλλον.
Ο κώδικας assembly σου θα γράφεται μέσα σε ένα ειδικό κατασκεύασμα:
let program := assemble!(
-- assembly code here
)
Αυτό το κατασκεύασμα πρέπει να παράγει ένα πρόγραμμα, το οποίο προσομοιώνει μια συνάρτηση που εκτελεί τον κώδικα assembly και επιστρέφει μια τιμή.
Στη συνέχεια, αυτό το πρόγραμμα καλείται με την ακόλουθη σύνταξη, όπου τα a, b, c, κ.λπ. είναι ορίσματα:
let result := program(a, b, c, ...)
Όλα τα ορίσματα και η τιμή επιστροφής είναι 64-bit προσημασμένοι ακέραιοι. Επιτρέπονται το πολύ 6 ορίσματα.
Η τιμή επιστροφής και όλα τα ορίσματα περνούν μέσα από καταχωρητές. Η γλώσσα σου πρέπει να υποστηρίζει τους ακόλουθους καταχωρητές:
| καταχωρητής | ρόλος |
|---|---|
rdi |
1ο όρισμα |
rsi |
2ο όρισμα |
rdx |
3ο όρισμα |
rcx |
4ο όρισμα |
r8 |
5ο όρισμα |
r9 |
6ο όρισμα |
rax |
τιμή επιστροφής |
Οι καταχωρητές ξεκινούν με προεπιλεγμένη τιμή 0, εκτός αν χρησιμοποιούνται για να περάσουν ορίσματα στη συνάρτηση.
Για παράδειγμα, στο παρακάτω πρόγραμμα, όλοι οι καταχωρητές έχουν τιμή 0 με εξαίρεση τον rdi, ο οποίος αρχικοποιείται με 10, και τον rsi, ο οποίος αρχικοποιείται με -20:
program(10, -20)
Η τιμή επιστροφής του προγράμματος αποθηκεύεται πάντα στον rax.
Τα ονόματα των καταχωρητών δεν έχουν διάκριση πεζών-κεφαλαίων.
Αυτό σημαίνει ότι τα rax, RAX και rAx αναφέρονται όλα στον ίδιο καταχωρητή.
Κάθε γραμμή στον κώδικα assembly μπορεί να έχει μία από δύο μορφές:
Εκτός αν τροποποιηθεί από κάποια εντολή, η ροή εκτέλεσης του προγράμματος προχωρά γραμμικά. Αυτό σημαίνει ότι μια προηγούμενη γραμμή εκτελείται πλήρως πριν εκτελεστεί η επόμενη.
Οι ετικέτες έχουν την ακόλουθη σύνταξη:
<label>:
Δεν αλλάζουν την τιμή κανενός καταχωρητή ούτε έχουν καμία άλλη επίδραση στο πρόγραμμα πέρα από το να σημαδεύουν συγκεκριμένα σημεία του κώδικα, ώστε να μπορούν να χρησιμοποιηθούν από εντολές.
Οι ετικέτες έχουν διάκριση πεζών-κεφαλαίων.
Αυτό σημαίνει ότι τα Start, start και sTart είναι όλα διαφορετικές ετικέτες.
Οι περισσότερες εντολές έχουν την ακόλουθη σύνταξη:
<opcode> <destination>, <source>
Το opcode δηλώνει την πράξη που εκτελείται, χρησιμοποιώντας τις τιμές των τελεστέων destination και source.
Το αποτέλεσμα της πράξης αποθηκεύεται στον τελεστέο destination.
Για παράδειγμα, η εντολή add αθροίζει τις τιμές των τελεστέων destination και source και αποθηκεύει αυτό το αποτέλεσμα στον τελεστέο destination.
Ο τελεστέος destination είναι πάντα ένας καταχωρητής, ενώ ο τελεστέος source μπορεί να είναι ένας καταχωρητής ή ένας αριθμητικός λεκτικός.
Αυτή είναι μια λίστα με τις εντολές που πρέπει να υποστηρίζει το πρόγραμμά σου:
| opcode | πράξη που εκτελείται |
|---|---|
mov |
destination := source |
add |
destination := destination + source |
sub |
destination := destination - source |
mul |
destination := destination * source |
div |
destination := destination / source |
xor |
destination := destination ^^^ source |
and |
destination := destination &&& source |
or |
destination := destination ||| source |
shl |
destination := destination <<< source |
shr |
destination := destination >>> source |
Εκτός από αυτές, το πρόγραμμά σου πρέπει να υποστηρίζει την εντολή cmp με δύο τελεστέους.
Αυτή η εντολή δεν τροποποιεί κανέναν καταχωρητή, αλλά αντ' αυτού συγκρίνει τους τελεστέους destination και source και θέτει μια εσωτερική κατάσταση του προγράμματος σε μία από τρεις επιλογές:
destination > source
destination == source
destination < source
Το πώς παρακολουθείς εσωτερικά αυτή την κατάσταση είναι δική σου επιλογή.
Υπάρχουν κάποιες εντολές που αλλάζουν τη ροή του προγράμματος, μεταφέροντας την εκτέλεση σε άλλο σημείο του κώδικα. Ονομάζονται εντολές άλματος.
Όλες δέχονται μόνο έναν τελεστέο, ο οποίος είναι μια ετικέτα που δηλώνει τον προορισμό του άλματος, δηλαδή το σημείο του κώδικα από όπου θα συνεχιστεί η εκτέλεση:
<opcode> <label>
Η εντολή jmp κάνει πάντα το άλμα στην ετικέτα-στόχο.
Οι υπόλοιπες εντολές άλματος κάνουν το άλμα μόνο αν η εσωτερική κατάσταση του προγράμματος έχει μια συγκεκριμένη τιμή.
Αυτό σημαίνει ότι συνήθως προηγείται μια εντολή cmp.
Αυτές είναι οι εντολές άλματος που πρέπει να υποστηρίζει το πρόγραμμά σου:
| εντολή | πράξη που εκτελείται |
|---|---|
jmp |
άλμα χωρίς συνθήκη. Το άλμα γίνεται πάντα |
je |
κάνει άλμα αν η εσωτερική κατάσταση είναι ίση |
jl |
κάνει άλμα αν η εσωτερική κατάσταση είναι μικρότερη από |
jg |
κάνει άλμα αν η εσωτερική κατάσταση είναι μεγαλύτερη από |
Οι κωδικοί των εντολών, είτε είναι δύο τελεστέων είτε ενός τελεστέου, δεν έχουν διάκριση πεζών-κεφαλαίων.
Αυτό σημαίνει ότι τα add, ADD και aDd αναφέρονται όλα στην ίδια εντολή.
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Lean με 100 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.