Μικροεπεξεργαστές Σημειώσεις Μαθήματος 2013-14 Υπεύθυνος: Δρ Άρης Παπακώστας,
Η γλώσσα assembly είναι μια γλώσσα προγραμματισμού χαμηλού επιπέδου για συγκεκριμένους υπολογιστές ή άλλη προγραμματιζόμενη συσκευή ειδικά για μια συγκεκριμένη αρχιτεκτονική υπολογιστών, σε αντίθεση με τις περισσότερες γλώσσες προγραμματισμού υψηλού επιπέδου, οι οποίες είναι γενικά φορητές σε πολλαπλά συστήματα. Η γλώσσα assembly μετατρέπεται σε εκτελέσιμο κώδικα μηχανής από ένα βοηθητικό πρόγραμμα που αναφέρεται ως συναρμολογητής(compiler) όπως NASM, MASM, κλπ.
Πριν μπορέσουμε να διερευνήσουμε τη διαδικασία της γραφής προγραμμάτων ηλεκτρονικών υπολογιστών, πρέπει να πάμε πίσω στα βασικά και να μάθουμε ακριβώς τι είναι ένας υπολογιστής και πώς λειτουργεί. Κάθε υπολογιστής, δεν έχει σημασία πόσο απλός ή σύνθετος είναι, έχει στην καρδιά του ακριβώς δύο πράγματα: έναν επεξεργαστή (CPU) και μνήμη. Μαζί, αυτά τα δύο πράγματα είναι αυτά που καθιστούν ικανό τον υπολογιστή σας να εκτελέσει διάφορα προγράμματα. Στο πιο βασικό επίπεδο, ένα πρόγραμμα ηλεκτρονικού υπολογιστή δεν είναι τίποτα περισσότερο από μια συλλογή από αριθμούς που είναι αποθηκεμένοι στη μνήμη. Διαφορετικοί αριθμοί λένε στον επεξεργαστή (CPU) να κάνει διαφορετικά πράγματα. Ο επεξεργαστής (CPU) διαβάζει τους αριθμούς, ένα κάθε φορά, τα αποκωδικοποιεί και κάνει ό, τι λένε οι αριθμοί. Για παράδειγμα, εάν ο CPU διαβάζει τον αριθμό 64, ως μέρος ενός προγράμματος, θα προσθέτει το 1 έως τον αριθμό που είναι αποθηκευμένος σε μια ειδική θέση που ονομάζεται AX. Αν ο CPU διαβάζει τον αριθμό 146, θα ανταλλάξει τον αριθμό που αποθηκεύτηκε στο AX με τον αριθμό που είναι αποθηκευμένος σε μια άλλη τοποθεσία που ονομάζεται BX. Με το συνδυασμό πολλών αριθμών, εντολών και απλών πράξεων, όπως αυτές σε ένα πρόγραμμα, ένας προγραμματιστής μπορεί να κάνει τον υπολογιστή να εκτελεί πολλά και πολύπλοκα πράγματα. Ως ένα παράδειγμα, εδώ είναι οι αριθμοί από ένα απλό πρόγραμμα υπολογιστή : 184, 0, 184, 142, 216, 198, 6, 158, 15, 36, 205, 32. Εάν εισάγετε αυτούς τους αριθμούς στη μνήμη του υπολογιστή σας και τους εκτελέσετε σε περιβάλλον MS - DOS, θα δείτε το σύμβολο του δολαρίου ($) να τοποθετείται στην κάτω δεξιά γωνία της οθόνης σας, δεδομένου ότι είναι αυτοί οι αριθμοί λένε στον υπολογιστή σας να κάνει συγκεκριμένα πράματα.
Αν και οι αριθμοί του παραπάνω προγράμματος κάνουν τέλεια αίσθηση σε έναν υπολογιστή, είναι αρκετά ασαφής σε έναν άνθρωπο. Σαφώς, εισάγοντας τους αριθμούς με το χέρι δεν είναι και ο καλύτερος τρόπος για να γράψει κάποιος ένα πρόγραμμα. Δεν χρειάζεται όμως να γίνει μόνο με αυτόν τον τρόπο. Πριν από πολύ καιρό, κάποιος ήρθε με την ιδέα ότι τα προγράμματα ηλεκτρονικών υπολογιστών θα μπορούσαν να γραφούν χρησιμοποιώντας λέξεις αντί για αριθμούς. Ένα ειδικό πρόγραμμα που ονομάζεται assembler θα είναι αυτό που θα αναλάβει να μετατρέψει τα λόγια του προγραμματιστή σε αριθμούς όπου ο υπολογιστής θα μπορούσε να το καταλάβει. Αυτή η νέα μέθοδος, που ονομάζεται γράψιμο ενός προγράμματος σε συμβολική γλώσσα, έσωσε προγραμματιστές από χιλιάδες ώρες επίπονης εργασίας, δεδομένου ότι δεν είναι ούτε εύκολο ούτε πρακτικό για κάποιον να θυμάται αριθμούς, αλλά θα μπορούσε να χρησιμοποιεί απλές λέξεις αντ 'αυτού. Παρακάνω υπάρχει ένα πρόγραμμα, γραμμένο σε γλώσσα assembly: MOV AX, 47104 MOV DS, AX MOV [3998], 36 INT 32
Όταν ένας συναρμολογητής (assembler) διαβάζει αυτό το πρόγραμμα, μετατρέπει κάθε γραμμή του κώδικα σε ένα CPU-επίπεδο εντολών. Αυτό το πρόγραμμα χρησιμοποιεί δύο τύπους εντολών, MOV και INT. Στους επεξεργαστές Intel, η εντολή MOV μετακινεί δεδομένα, ενώ το INT μεταφέρει εντολές ελέγχου του επεξεργαστή. MOV AX, 47104 MOV DS, AX MOV [3998], 36 INT 32 Το πρόγραμμα εξακολουθεί να μην είναι αρκετά σαφές, αλλά είναι πολύ πιο εύκολο να γίνει κατανοητό παρά αν ήταν γραμμένο με σκέτους αριθμούς. Η πρώτη εντολή, MOV AX, 47104, λέει στον υπολογιστή για να αντιγράψει τον αριθμό 47104 στην AX θέση της μνήμης. Η επόμενη εντολή, MOV DS, AX, λέει στον υπολογιστή να αντιγράψει τον αριθμό AX στην DS θέση της μνήμης. Η επόμενη εντολή, MOV [3998], 36 λέει στον υπολογιστή να θέσει τον αριθμό 36 στη θέση μνήμης 3998. Τέλος, INT 32 βγαίνει από το πρόγραμμα και επιστρέφει στο λειτουργικό σύστημα.
Όταν ένας συναρμολογητής (assembler) διαβάζει αυτό το πρόγραμμα, μετατρέπει κάθε γραμμή του κώδικα σε ένα CPU-επίπεδο εντολών. Αυτό το πρόγραμμα χρησιμοποιεί δύο τύπους εντολών, MOV και INT. Στους επεξεργαστές Intel, η εντολή MOV μετακινεί δεδομένα, ενώ το INT μεταφέρει εντολές ελέγχου του επεξεργαστή. MOV AX, 47104 MOV DS, AX MOV [3998], 36 INT 32 Το πρόγραμμα εξακολουθεί να μην είναι αρκετά σαφές, αλλά είναι πολύ πιο εύκολο να γίνει κατανοητό παρά αν ήταν γραμμένο με σκέτους αριθμούς. Η πρώτη εντολή, MOV AX, 47104, λέει στον υπολογιστή για να αντιγράψει τον αριθμό 47104 στην AX θέση της μνήμης. Η επόμενη εντολή, MOV DS, AX, λέει στον υπολογιστή να αντιγράψει τον αριθμό AX στην DS θέση της μνήμης. Η επόμενη εντολή, MOV [3998], 36 λέει στον υπολογιστή να θέσει τον αριθμό 36 στη θέση μνήμης 3998. Τέλος, INT 32 βγαίνει από το πρόγραμμα και επιστρέφει στο λειτουργικό σύστημα.
Πριν προχωρήσουμε, θα ήθελα να εξηγήσω ακριβώς πώς λειτουργεί το πρόγραμμα. Μέσα στη Κεντρική Μονάδα Επεξεργασίας (CPU) είναι μια σειρά από περιοχές, που ονομάζονται καταχωρητές (registers), η οποία μπορεί να αποθηκεύσει έναν αριθμό. Ορισμένα μητρώα, όπως η θέση μνήμης AX, είναι γενικής χρήσης και δεν κάνουνε τίποτα το ιδιαίτερο. Άλλα μητρώα όμως, όπως τα DS ελέγχουν τον τρόπο που λειτουργεί η CPU. Το DS απλά συμβαίνει να είναι ένας καταχωρητής τμήματος (segment register), και χρησιμοποιείται για να επιλέξετε σε ποια περιοχή της μνήμης η CPU μπορεί να γράψει. Στο πρόγραμμά μας, βάζουμε τον αριθμό 47104 στο DS, το οποίο λέει την CPU να βρεί πρόσβαση στη μνήμη της κάρτας βίντεο. Το επόμενο πράγμα που κάνει το πρόγραμμά μας είναι να θέσει τον αριθμό 36 στη θέση 3998 στη μνήμης της κάρτας βίντεο. Θυμηθείτε ότι το 36 είναι ο κώδικας για το σύμβολο του δολαρίου, και 3998 είναι η θέση μνήμης στην κάτω δεξιά γωνία της οθόνης, έτσι όταν τρέξουμε το παραπάνω πρόγραμμα το σύμβολο του δολαρίου εμφανίζεται στην οθόνη μερικά μικροδευτερόλεπτα αργότερα. Τέλος, το πρόγραμμα μας λέει στην CPU να εκτελέσει αυτό που ονομάζεται μια διακοπή (interrupt). Μια διακοπή χρησιμοποιείται για να σταματήσει ένα πρόγραμμα και να εκτελέσει ένα άλλο στη θέση του. Στην περίπτωσή μας, θέλουμε να διακόψει το 32, το οποίο τελειώνει το πρόγραμμά μας και μας πηγαίνει πίσω στο MS - DOS, ή οποιοδήποτε άλλο πρόγραμμα χρησιμοποιήθηκε για να τρέξουμε το πρόγραμμά μας.
Εδώ αντί για δυαδικούς ή δεκαεξαδικούς κωδικούς χρησιμοποιούμε μνημονικά, που μπορούν να απομνημονευθούν εύκολα και που εκτός από ορισμένες εξαιρέσεις είναι εύκολο να πει κανείς τη λειτουργία που κάθε μνημονικό παριστάνει, γιατί το όνομα του μνημονικού παραπέμπει στη λειτουργία που καλείται να κάνει. Κάθε κατασκευαστής μικροεπεξεργαστών δίνει μαζί με τον μικροεπεξεργαστή και μία λίστα από μνημονικά, που είναι διαφορετικά για κάθε τύπο μικροεπεξεργαστή. Κάθε εντολή γλώσσας Assembly αποτελείται από δύο κύρια μέρη : Τον κωδικό εντολής, που είναι μνημονικό και δείχνει τη λειτουργία που καλείται να κάνει ο μικροεπεξεργαστής, καθώς επίσης τη μέθοδο διευθυνσιοδότησης που χρησιμοποιείται στη συγκεκριμένη εντολή. Τον τελεστέο, που δίνει άμεσα ή έμμεσα τα δεδομένα πάνω στα οποία θα γίνει η συγκεκριμένη λειτουργία. Μία εντολή πολλές φορές συνοδεύεται και από μία ετικέτα που προηγείται της εντολής καθώς επίσης και από σχόλια που έπονται της εντολής και εξηγούν την λειτουργία της. Έτσι μία εντολή έχει την παρακάτω μορφή :
LABEL OPCODE OPERAND COMMENTS (Ετικέτα) (Κωδικός εντολής) (Τελεστέος) (Σχόλια) LOOP LDA #$50 Φόρτωσε στον Α τον Αριθμό 50 Ένα πρόγραμμα σε συμβολική γλώσσα λέγεται και πρωτογενές πρόγραμμα. Παρακάτω φαίνεται ένα πρωτογενές πρόγραμμα σε συμβολική γλώσσα : Δ/νση μνήμης Μνημονικό εντολής Σχόλια $0100 LDA $40 Φόρτωσε τον Α με το [$40] $0102 ADC $41 Πρόσθεσε το [$41] $0104 STA $42 Αποθήκευσε το αποτέλεσμα στην $42
Οι εντολές που λένε στον μικροεπεξεργαστή τι να κάνει συνήθως αποτελούνται από δύο ή τρία bytes. Το πρώτο byte λέγεται κωδικός εντολής και καθορίζει το είδος της λειτουργίας που πρόκειται να γίνει και είναι η μικρότερη απαίτηση μιας εντολής. Για τις περισσότερες εντολές ο μικροεπεξεργαστής χρειάζεται περισσότερη πληροφορία από αυτή που δίνεται από τον κωδικό εντολής. Αυτό γίνεται με το να προστεθούν ένα ή περισσότερα bytes δεδομένων που είναι γνωστά ως τελεστέος. Υπάρχουν διάφορες μέθοδοι που καθορίζουν τον τρόπο με τον οποίο δίνεται η διεύθυνση των δεδομένων μιας εντολής και ονομάζονται μέθοδοι διευθυνσιοδότησης. Παρακάτω παρουσιάζονται οι πιο κοινές μέθοδοι διευθυνσιοδότησης. Απόλυτη διευθυνσιοδότηση. Σ αυτή τη μέθοδο η διεύθυνση που δίνει ο τελεστέος αποτελείται από δύο bytes και ακολουθεί τον κωδικό της εντολής, όπως στο παράδειγμα : LDA $3124 Διευθυνσιοδότηση μηδενικής σελίδας. Σ αυτή τη μέθοδο η εντολή αποτελείται από δύο bytes, επειδή ο τελεστέος αποτελείται από ένα μόνο byte, όπως στο παράδειγμα : LDA $50
Κάθε γραμμή αυτού του προγράμματος αποτελεί μία πλήρη εντολή. Κάθε εντολή μπορεί να αποτελείται από κωδικούς ενός, δύο ή τριών Bytes. Η στήλη Δ/νση μνήμης δίνει μόνο τη διεύθυνση που βρίσκεται το byte του κωδικού εντολής. Για παράδειγμα, ο κωδικός της εντολής LDA είναι αποθηκευμένος στη διεύθυνση $0100 και ο τελεστέος στη διεύθυνση $0101. Ο κωδικός της εντολής ADC θα βρίσκεται στη θέση διεύθυνσης $0102 κ.ο.κ. Η δεύτερη στήλη δίνει τα μνημονικά και τις διευθύνσεις των τελεστέων. Το σύμβολο $ σημαίνει ότι η διεύθυνση δίνεται σε δεκαεξαδική μορφή. Έτσι το μνημονικό LDA $40 σημαίνει στη συμβολική γλώσσα «φόρτωσε το συσσωρευτή με τα περιεχόμενα της δεκαεξαδικής διεύθυνσης $40». Η στήλη των σχολίων εξηγεί το τι κάνει κάθε εντολή ή ομάδες εντολών του προγράμματος. Για να εκτελεστεί ένα πρόγραμμα γραμμένο σε συμβολική γλώσσα θα πρέπει πρώτα να μετατραπεί σε γλώσσα μηχανής. Αυτό γίνεται μέσα από ένα πρόγραμμα που λέγεται συμβολομεταφραστής ( Assembler ) και είναι αποθηκευμένο κάπου στη μνήμη είτε σε ROM είτε σε RAM ή ακόμα και σε εύκαμπτο ή σκληρό δίσκο. Το μέγεθος του προγράμματος αυτού μπορεί να είναι από 2 μέχρι 16 ΚΒ, ανάλογα με τα χαρακτηριστικά του και τις ικανότητές του. Όταν ο χρήστης δεν διαθέτει τέτοιο πρόγραμμα, τότε η δουλειά της μετατροπής από συμβολική γλώσσα σε γλώσσα μηχανής, γίνεται από τον χρήστη με την βοήθεια κάποιου πίνακα που δίνει τους δεκαεξαδικούς κώδικες που αντιστοιχούν στα διάφορα μνημονικά. Οι μικροϋπολογιστές συνήθως χρησιμοποιούν ένα μίνι-συμβολομεταφραστή που κάνει ουσιαστικά τη δουλειά της μετατροπής που θα έκανε ο χρήστης με το χέρι. Η διαδικασία συμβολομετάφρασης που αναφέρεται σε ένα mini-assembler είναι η παρακάτω :