Εισαγωγή
στην Assembly
Περιεχόμενα:
1. Εισαγωγή
b. Πλεονεκτήματα / Μειονεκτήματα
a. Δεκαδικό
b. Δυαδικό
c. Δεκαεξαδικό
a.
Bit , Nibble , Byte , Word , Double word
b. Η στοίβα
4. Ο επεξεργαστής x86 της intel
a. Εισαγωγή
b. Καταχωρητές
iii.
Καταχωρητές index
v.
Σημαίες
d. Interrupts
a. Εισαγωγή
d. Αποθήκευση του προγράμματος στο δίσκο
6. Επίλογος
Τι είναι η Assembly
H Assembly είναι μια γλώσσα προγραμματισμού υπολογιστών . Είναι μια γλώσσα πολύ χαμηλού επιπέδου , αφού επιτρέπει πρόσβαση στις λειτουργίες του επεξεργαστή . Αυτό σημαίνει ότι υπάρχουν πολλές διαφορετικές γλώσσες Assembly , μια για κάθε είδος επεξεργαστή . Στο κείμενο αυτό θα μας απασχολήσει η Assembly του επεξεργαστή x86 της intel , που είναι σήμερα ο πιο διαδεδομένος επεξεργαστής . Αν έχετε 286 , 386 ή ακόμα και 8086 (γενικώς ότι τελειώνει σε 86) ή ακόμη και Pentim , τότε ο επεξεργαστής σας υποστηρίζει τις εντολές που θα αναφερθούν εδώ .
Η Assembly λέγεται αλλιώς και συμβολική γλώσσα , αφού στην ουσία τα προγράμματα σε αυτή είναι συμβολικές ονομασίες εντολών , που αποτελούν μνημονικά ονόματα (στην Αγγλική γλώσσα) κάθε λειτουργίας του επεξεργαστή.
Βέβαια , όπως είναι γνωστό , οι υπολογιστές καταλαβαίνουν μόνο το 0 και το1 , οπότε τα σύμβολα της γλώσσας Assembly πρέπει να μεταφραστούν σε 0 και 1 (δηλαδή σε γλώσσα μηχανής) από ένα πρόγραμμα που καλείται συμβολομεταφραστής (Assembler) , κατά τρόπο ανάλογο με τη μετάφραση προγραμμάτων γλωσσών υψηλού επιπέδου από μεταγλωτιστές και διερμηνείς .
Για να ακολουθήσετε το κείμενο αυτό δεν χρειάζεται να προμηθευτείτε κανένα συμβολομεταφραστή , αφού αυτός υπάρχει ήδη στο σύστημά σας και λέγεται debug . Για την ακρίβεια δεν είναι ακριβώς συμβολομεταφραστής , αλλά μπορούμε να τον χρησιμοποιήσουμε σαν τέτοιο . Θα εξετάσουμε αναλυτικά το debug στο κεφάλαιο 5.
Το βασικότερο πλεονέκτημα της Assembly είναι η ταχύτητα των προγραμμάτων που γράφονται σε αυτή . Πραγματικά τα προγράμματα αυτά είναι ταχύτατα και μπορούν να αποδειχθούν εξαιρετικά χρήσιμα σε εφαρμογές όπου η ταχύτητα παίζει σημαντικό ρόλο , όπως πχ στα γραφικά .
Ένα επιπλέον πλεονέκτημα είναι ότι ο προγραμματιστής σε Assembly καταλαβαίνει ακριβώς πως λειτουργεί ο υπολογιστής του . Καταλαβαίνει τον τρόπο σκέψης του και τον προγραμματίζει με τέτοιο τρόπο , ώστε τα προγράμματά του να είναι πιο γρήγορα και σωστά δομημένα , ακόμα και όταν χρησιμοποιεί γλώσσα υψηλότερου επιπέδου από την Assembly . H Assembly είναι μια γλώσσα ιδανική για αυτούς που ενδιαφέρονται να μάθουν πώς ακριβώς λειτουργούν τα πράγματα .
Υπάρχουν όμως και σοβαρά μειονεκτήματα . Ένα από αυτά είναι η αργή συγγραφή προγραμμάτων . Για παράδειγμα χρειαζόμαστε ένα πρόγραμμα 10 γραμμών για να εκτυπώσουμε ένα αλφαριθμητικό στην οθόνη , πράγμα που σε BASIC γίνεται με μια και μόνο εντολή . Ένα άλλο μειονέκτημα είναι ότι η γλώσσα Assembly δεν διαθέτει καμία μέριμνα αποφυγής σφαλμάτων και καμία λογική δόμησης , οπότε μπορούν να προκύψουν προγράμματα με σφάλματα και με κώδικα «σπαγγέτι» πολύ εύκολα . Η μέριμνα σε αυτή την περίπτωση αφήνεται εξολοκλήρου στον προγραμματιστή για καθαρό κώδικα και αποφυγή σφαλμάτων . Αυτός είναι και ο κύριος λόγος που η Assembly δεν συνίσταται για αρχάριους προγραμματιστές .
Παρόλα αυτά τα μειονεκτήματα μπορούν να παρακαμφθούν με διάφορες προσεγγίσεις :
ΚΕΦΑΛΑΙΟ
2: Συστήματα αρίθμησης
Οι άνθρωποι , για να μπορούν να συννενοούνται σε σχέση με τις διάφορες ποσότητες χρησιμοποιούν το δεκαδικό σύστημα αρίθμησης . Οι αριθμοί δηλαδή που χρησιμοποιούν οι άνθρωποι αποτελούνται από 10 ψηφία (0-9) βαλμένα στη σωστή σειρά . Όπως όμως αναφέραμε , ο υπολογιστής καταλαβαίνει μόνο το 0 και το 1 . Δηλαδή μπορεί να εργαστεί μόνο με δύο ψηφία . Αυτό επίσης σημαίνει ότι όλοι οι αριθμοί που καταλαβαίνει ο υπολογιστής κωδικοποιούνται σε ένα σύστημα αρίθμησης που ονομάζεται δυαδικό (με 2 ψηφία 0 και 1 βαλμένα στη σωστή σειρά) , και που αν θυμάστε είχαμε διδαχθεί στο δημοτικό . Για όσους δεν θυμούνται γίνεται εδώ μια αναφορά στα διάφορα συστήματα αρίθμησης , αφού είναι βασικής σημασίας στον προγραμματισμό σε Assembly . Όσοι θυμούνται μπορούν να το παρακάμψουν .
Ξεκινάμε λοιπόν με το δεκαδικό , μιας και το χρησιμοποιούμε καθημερινά .
Ένας αριθμός στο δεκαδικό σύστημα κωδικοποιείται με τα δέκα γνωστά μας ψηφία 0-9 ως εξής:
Το πρώτο από τα δεξιά ψηφίο είναι ο αριθμός των μονάδων . Έτσι ο αριθμός 7 σημαίνει «7 μονάδες» . Το δεύτερο ψηφίο είναι οι 10δες . Δηλαδή ο αριθμός 37 σημαίνει «3 δεκάδες και 7 μονάδες» . Το τρίτο ψηφίο είναι των εκατοντάδων , το τέταρτο των χιλιάδων κοκ .
Παρατηρούμε εδώ ότι αν χρησιμοποιήουμε δυνάμεις , οι μονάδες παριστάνονται σαν (10^0) , οι δεκάδες σαν (10^1) , οι εκατοντάδες σαν (10^2) κοκ . Η βάση λοιπόν του συστήματος είναι το 10 και το σύστημα καλείται «δεκαδικό» ή «Decimal» . Ο αριθμός 1234 σημαίνει λοιπόν :
1 x 10^3 +2 x 10^2 + 3 x 10^2 +4 x 10^0.
Το δυαδικό σύστημα , αν και διαθέτει μόνο 2 ψηφία , δεν έχει τίποτα να ζηλέψει από το δεκαδικό , αφού σε δυαδικό συμβολισμό μπορούμε να αναπαραστήσουμε οποιονδήποτε αριθμό .
Αν θυμηθούμε τη λογική του δεκαδικού , είναι εύκολο να καταλάβουμε πώς λειτουργεί και το δυαδικό . Έτσι το πρώτο ψηφίο από δεξιά είναι οι μονάδες (2^0) . Το δεύτερο είναι οι δυάδες (2^1) , το τρίτο οι τετράδες (2^2) κοκ . Η βάση είναι το 2 και γι αυτό το σύστημα λέγεται δυαδικό (Binary) .
Παράδειγμα:
Ο αριθμός 10 του δεκαδικού μπορεί να παρασταθεί σαν δυαδικός ως εξής :
Παρατηρούμε ότι το 10 έχει μια οκτάδα (2^3) , καμία τετράδα , μια δυάδα και καμία μονάδα . Έτσι γράφουμε :
10 = 1 x (2^3) + 0 x (2^2) +1 x (2^1) +0 x (2^0)
οπότε ο δυαδικός συμβολισμός του 10 είναι 1010b . Το b χρησιμεύει για να μας δείξει ότι ο αριθμός είναι δυαδικός και να μην συγχέεται με το χίλια-δέκα του δεκαδικού .
Αντίστροφα , ο αριθμός 1111b σε δεκαδικό συμβολισμό παριστάνεται ως εξής :
1111b = 1 x (2^3) + 1 x (2^2) +1 x (2^1) +1 x (2^0) =8+4+2+1 =15
Βεβαίως η ενασχόληση του ανθρώπου με το δυαδικό σύστημα είναι επίπονη . Η χρήση δε του δεκαδικού είναι και αυτή δύσκολη , αφού απαιτείται μετατροπή από το δεκαδικό στο δυαδικό . Η λύση είναι το δεκαεξαδικό σύστημα .
Από την περιγραφή των 2 προηγούμενων συστημάτων θα έχετε ήδη καταλάβει τι συμβαίνει με το δεκαεξαδικο (hexadecimal) σύστημα . Είναι ένα σύστημα με βάση το 16 , όπου το πρώτο ψηφίο δηλώνει τις μονάδες (16^0) , το δεύτερο τις δεκαεξάδες (16^1) , το τρίτο τις 256άδες (16^2) και πάει λέγοντας …
Αυτό που ίσως δεν συμπεράνατε είναι το ποια ψηφία χρησιμοποιούνται . Όπως το δυαδικό χρειάζεται 2 και το δεκαδικό 10 ψηφία , έτσι και το 16δικό χρειάζεται 16 ψηφία . Ποια είναι αυτά ?
Τα πρώτα 10 ψηφία είναι τα γνωστά 0-9 του δεκαδικού . Τα υπόλοιπα 6 είναι τα A,B,C,D,E,F του αγγλικού αλφαβήτου . Για να γίνει κατανοητό πως δουλεύει αυτό το σύστημα ρίξτε μια ματιά στον παρακάτω πίνακα :
Δεκαεξαδικό |
Δυαδικό |
Δεκαδικό |
0 |
0000 |
0 |
1 |
0001 |
1 |
2 |
0010 |
2 |
3 |
0011 |
3 |
4 |
0100 |
4 |
5 |
0101 |
5 |
6 |
0110 |
6 |
7 |
0111 |
7 |
8 |
1000 |
8 |
9 |
1001 |
9 |
Α |
1010 |
10 |
B |
1011 |
11 |
C |
1100 |
12 |
D |
1101 |
13 |
E |
1110 |
14 |
F |
1111 |
15 |
Αυτό που πρέπει να παρατηρήσουμε είναι ότι 4 ψηφία του δυαδικού συστήματος αντιστοιχούν σε 1 του δεκαεξαδικού . Έτσι όταν έχουμε ένα μεγάλο δυαδικό αριθμό όπως ο 11010101001011101010b , μπορούμε να τον χωρίσουμε σε τετράδες , δηλαδή :
1101 0101 0010 1110 1010b
και μετά , σύμφωνα με τον παραπάνω πίνακα να βρούμε ποιο δεκαεξαδικό ψηφίο αντιστοιχεί σε κάθε τετράδα :
1101 0101 0010 1110 1010b = D52EΑh (To h χρησιμοποιείται για να δηλώσει δεκαεξαδικό συμβολισμό )
Φαίνονται λοιπόν τα πλεονεκτήματα του δεκαεξαδικού συμβολισμού . Είναι πολύ πιο συμπαγής τρόπος για να γράφουμε αριθμούς και μετατρέπεται πολύ εύκολα στο δυαδικό σύστημα (και αντίστροφα) . Επιπλέον είναι πολύ ευκολότερο να μετατρέψουμε το D52EΑh σε δεκαδικό συμβολισμό , παρά το 11010101001011101010b . Έτσι το δεκαεξαδικό σύστημα αρίθμησης είναι μια πολύτιμη «γέφυρα» ανάμεσα στη λογική του ανθρώπου και του υπολογιστή .
ΑΣΚΗΣΗ: Γράψτε σε τρία χαρτιά τυχαίους δεκαδικούς , δεκαεξαδικούς και
δυαδικούς αριθμούς αντίστοιχα , και μετατρέψτε
τους αριθμούς στα άλλα δύο συστήματα .
Κεφάλαιο 3 : Οργάνωση της μνήμης
Και αφού λοιπόν ο υπολογιστής καταλαβαίνει και «θυμάται» μόνο αλληλουχίες από 0 και 1 , έπεται ότι η βασική μονάδα μνήμης του υπολογιστή είναι το δυαδικό ψηφίο . Στα αγγλικά αυτό μεταφράζεται Bainary Digit , ή πιο σύντομα bit . Βέβαια , ένα bit μόνο του δεν σημαίνει και πολλά . Με πολλά bits στη σειρά όμως μπορούμε να κάνουμε τα πάντα! Να αποθηκεύσουμε ένα μουσικό κομμάτι σε CD , να φτιάξουμε όλα τα γραφικά σαν αυτά τα φοβερά της ταινίας Final Fantasy ή να γράψουμε ένα κείμενο , όπως κάνω εγώ αυτή τη στιγμή …
…βέβαια για να γίνουν όλα αυτά απαιτείται και κάποια οργάνωση . Αν τα bits τοποθετούνταν απλώς στη σειρά , και χρειαζόμασταν 1 MB δεδομένων θα επικρατούσε ένα χάος . Έτσι υπάρχει ένα σύστημα με το οποίο τα bits ομαδοποιούνται .
Καταρχήν τα bits χωρίζονται σε τετράδες . Οι τετράδες αυτές λέγονται Nibbles . Όπως είδαμε και πριν , ένα nibble μπορεί να παρασταθεί με ένα δεκαεξαδικό ψηφίο . Τα bits μέσα σε ένα nibble αριθμούνται ως εξής :
Bit #3 ή υψηλής τάξης bit |
#2 |
#1 |
Bit #0 ή χαμηλής τάξης bit |
Αν συνδυάσουμε 8 bits μαζί (ή 2 nibbles) , παίρνουμε 1 byte . Τα bits μέσα σε ένα byte αριθμούνται με παρόμοιο τρόπο:
Bit #7 ή υψηλής τάξης bit |
#6 |
#5 |
#4 |
#3 |
#2 |
#1 |
Bit #0 ή χαμηλής τάξης bit |
To byte αποτελείται από 2 nibbles που λέγονται υψηλής και χαμηλής τάξης nibbles
Nibble υψηλής τάξης (high order nibble) |
Nibble χαμηλής τάξης (low order nibble) |
Και φυσικά αφού το nibble αναπαρίσταται με ένα δεκαεξαδικό ψηφίο , to byte αναπαρίσταται με δύο .
Το byte είναι μια πολύ βασική μονάδα μνήμης , αφού ο κώδικας ASCIL είναι κώδικας του 1 byte . Ο κώδικας αυτός αντιστοιχίζει δυαδικούς αριθμούς του ενός byte σε αλφαριθμητικούς χαρακτήρες . Με 8 bits (1 byte) , μπορούμε να αναπαραστήσουμε αριθμούς από το 00000000b ως το 11111111b . Δηλαδή από το 0 ως το 255 . Ο κώδικας ASCLIL έχει 256 χαρακτήρες . Από αυτούς οι 128 είναι αλφαριθμητικοί χαρακτήρες της Αγγλικής γλώσσας , ενώ οι υπόλοιποι 128 χρησιμοποιούνται για διάφορους λόγους (όπως για υποστήριξη Ελληνικών στο MS DOS) . Πρόσφατα έχει δημιουργηθεί μια νέα τυποποίηση που λέγεται Unicode . Ο κώδικας αυτός είναι 16μπιτος και μπορεί να αναπαραστήσει 65536 χαρακτήρες (πολύ περισσότεροι από τους 256 του ASCIL) , που περιλαμβάνουν όλες τις γλώσσες! Παρόλα αυτά , ο παλιός καλός ASCIL κώδικας δεν έχει αντικατασταθεί πλήρως και αυτόν θα χρησιμοποιούμε στα προγράμματα σε Assembly .
Μιλώντας για τον 16μπιτό κώδικα Unicode , η μονάδα που αποτελείται από 16 bits ονομάζεται word (λέξη) . Μια λέξη ως εκ τούτο αποτελείται από 2 bytes . Ένα υψηλής και ένα χαμηλής τάξης , κάθε ένα από τα οποία έχει τα δικά του υψηλής και χαμηλής τάξης nibbles .
Επίσης υπάρχει και μια μονάδα μνήμης των 32 bits , που λέγεται Double word ή Dword και περιλαμβάνει μια υψηλής και μια χαμηλής τάξης λέξη , καθεμιά από τις οποίες έχει ένα υψηλής και ένα χαμηλής τάξης byte κοκ …
Η στοίβα (stack) είναι ένας χαρακτηριστικός τρόπος αποθήκευσης των δεδομένων , που πρόκειται να μας λύσει τα χέρια στον προγραμματισμό σε Assembly .
Η στοίβα συμπεριφερεται ακριβώς όπως μια στοίβα από δίσκους σερβιρίσματος σε μια καφετέρια . Αν πάνω στη στοίβα τοποθετηθεί ένας δίσκος αυτός θα είναι και ο πρώτος δίσκος που θα πάρουμε από αυτή ,όταν τον χρειαστούμε.
Η στοίβα λοιπόν ακολουθεί τη λογική LIFO (Last In , First Out) . Θα μας φανεί ιδιαίτερα χρήσιμη όταν θα χρειάζεται σε ένα πρόγραμμα να αποθηκεύσουμε κάποιον (ή όλους) τους καταχωρητές . Περισσότερα για τις εντολές στοίβας θα δούμε στο επόμενο κεφάλαιο .
Η μνήμη του υπολογιστή μας μπορεί να θεωρηθεί σαν ένα μεγάλο «σωληνάριο» που περιέχει «καραμέλες» τη μία μετά την άλλη . Κάθε καραμέλα είναι ένα byte .
Ο επεξεργαστής μας , μπορεί να διαβάζει και να γράφει bytes στη μνήμη . Αυτό μπορεί να το κάνει με 2 τρόπους :
Μιας και ο προγραμματισμός σε protected mode γίνεται στα windows και εμείς θα ασχοληθούμε με το MS DOS , μπορούμε να τον αγνοήσουμε (προς το παρόν) .
Ο προγραμματισμός σε real mode βασίζεται στη λογική Segment:Offset . Δηλαδή μια διεύθυνση μνήμης αντιστοιχίζεται σε ένα ζεύγος αριθμών των 32 bit . Αυτοί λέγονται Segment και Offset . To Segment είναι ο αριθμός του «τμήματος» της μνήμης , ενώ το offset είναι η σχετική διεύθυνση της μνήμης μέσα σε ένα ορισμένο τμήμα .
Ένα τμήμα μνήμης , είναι μια περιοχή της μνήμης που περιλαμβάνει FFFFh bytes (ή 64 K) . Τα τμήματα δεν είναι αυτόνομα . Δηλαδή μια περιοχή της μνήμης μεγέθους 64 K δεν αντιστοιχεί μόνο σε ένα τμήμα . Αντίθετα τα τμήματα είναι επικαλυπτόμενα . Ρίξτε μια ματιά στο παρακάτω σχήμα για να καταλάβετε.
__________________________________________________
._________ ___
.|Τμήμα 1 | |
.| |
16 bytes
.| |
| Κάθε
τμήμα έχει την αρχή του
.|
__________--- ___ στο 16ο byte του προηγούμενου
.| | Τμήμα 2 | | τμήματος
.| | |
16 bytes
.| | |
|
.| | __________ ---
.| | | Τμήμα 3
|
.| | | |
κοκ …
.| | | |
.|
| | …κοκ… |
Για να βρούμε την φυσική διεύθυνση που αντιστοιχεί σε μια δυάδα Segment:Offset κάνουμε τον ακόλουθο υπολογισμό:
Διεύθυνση = Segment*16 +Offset
Ή
Διεύθυνση=Segment*10h + Offset
Μπορούμε λοιπόν σε Real mode να αναφερθούμε σε FFFFh τμήματα , κάθε ένα από τα οποία έχει FFFFh Offsets . Άρα η μέγιστη διεύθυνση που μπορούμε να έχουμε σε real mode είναι:
Μπορούμε λοιπόν να αναφερθούμε μόνο σε 1 MB μνήμης , αλλά αυτό δεν είναι και μεγάλο μειονέκτημα , γιατί τα προγράμματά μας δεν θα ξεπεράσουν τα μερικά KiloBytes . Από ην άλλη όποιος μπορεί να γράψει ένα τόσο μεγάλο πρόγραμμα σε Assembly , μάλλον δε θα δυσκολευτεί να μάθει και τον proteceted mode …
ΚΕΦΑΛΑΙΟ
4 : Ο επεξεργαστής x86
της intel
Ο επεξεργαστής (συχνά αναφέρεται και ως Κεντρική Μονάδα Επεξεργασίας – ΚΜΕ ή Central Prossessing Unit - CPU) αποτελεί το «νοήμον» τμήμα του υπολογιστή μας . Εκεί γίνονται όλες οι διεργασίας – αριθμητικές πράξεις – επαναλήψεις – λήψεις αποφάσεων , που κάνουν το μηχάνημά μας τόσο έξυπνο . Κάθε επεξεργαστής έχει τη δική του αρχιτεκτονική και το δικό του σετ εντολών . Στοιχεία αυτών των δύο θα καλύψουμε για τον επεξεργαστή x86 της intel . Για πιο εκτενή κάλυψη των θεμάτων αυτών ανατρέξτε σε ένα εγχειρίδιο αναφοράς του x86 , που μπορείτε να βρείτε στο internet .
Βασικό στοιχείο της αρχιτεκτονικής ενός επεξεργαστή είναι οι καταχωρητές του . Οι καταχωρητές είναι θέσεις μνήμης του επεξεργαστή , που μοιάζουν κάπως με τις μεταβλητές της BASIC , με μόνη διαφορά ότι δεν ορίζονται από το χρήστη , αφού είναι ενσωματωμένοι με τον επεξεργαστή και δεν αποτελούν μέρος της κάρτας μνήμης του υπολογιστή , αλλά βρίσκονται «μέσα» στον επεξεργαστή .
Ο επεξεργαστης έχει την «εξυπνάδα» να μην «πελαγώνει» από τις σύνθετες διαδικασίες , γιατί κάνει μια δουλειά κάθε φορά . Αθροίζοντας τις στοιχειώδεις αυτές διαδικασίες (πχ προσθέσεις , λήψεις αποφάσεων , εκχωρήσεις τιμών) ολοκληρώνονται πιο πολύπλοκες . Οι καταχωρητές λοιπόν είναι το μόνο μέρος στο οποίο μπορεί ο επεξεργαστής να επιδράσει δραστικά (πχ να κάνει μαθηματικές πράξεις με αυτούς κλπ) . Στην υπόλοιπη μνήμη και στους δίσκους μπορεί απλώς να διαβάζει και να γράφει .
Ο επεξεργαστής x86 έχει διαφόρων ειδών καταχωρητές ,οι οποίοι παρουσιάζονται συνοπτικά παρακάτω .
Καταχωρητής
|
Όνομα
|
Περιγραφή
|
AX |
Accumulator |
Συνήθως χρησιμεύει στις πράξεις αριθμητικής , σε διαδικασίες εισόδου/εξόδου και στην κλήση των ρουτίνων του MS DOS . |
BX |
Base |
Δείκτης . |
CX |
Counter |
Ότι λέει και η λέξη . χρησιμοποιείται σαν μια μεταβλητή καταμέτρησης στις επαναλληπτικές διαδικασίες (όπως το FOR…NEXT της BASIC). |
DX |
Displacement |
Συνήθως χρησιμεύει στην έξοδο δεδομένων από το πρόγραμμα , όπως στην έξοδο χαρακτήρων στην οθόνη . |
Βεβαίως δεν είναι μόνο αυτές οι χρήσεις τους . Ονομάζονται γενικοί καταχωρητές , αφού ο προγραμματιστής έχει τη δυνατότητα να τους αλλάξει κατά βούληση και να τους χρησιμοποιήσει όπως θέλει . Όλοι οι γενικοί καταχωρητές μπορούν να «σπάσουν» σε δύο δεκαεξάμπιτους καταχωρητές . Πχ Ο AX αποτελείται από τον AH και τον AL , όπου ο AH αποτελεί την υψηλής τάξης λέξη (H.O. ή High Order) και ο AL τη χαμηλής τάξης λέξη (L.O. ή Low Order) , οπότε μην τρομάξετε αν δείτε έναν καταχωρητή BO , αφού είναι απλώς ο μισός καταχωρητής BX .
Καταχωρητής
|
Όνομα
|
Περιγραφή
|
CS |
Code Segment |
Δείκτης στο τμήμα κώδικα του προγράμματός μας (εκεί δηλαδή που μπαίνουν οι εντολές) . |
DS |
Data Segment |
Δείκτης στο τμήμα δεδομένων . πχ εκεί αποθηκεύουμε τις μεταβλητές μας . |
ES |
Extra Segment |
Μας χρησιμεύει στα γραφικά . |
SS |
Stack Segment |
Δείκτης τμήματος στοίβας . |
ΙΙΙ. Καταχωρητές Index
Καταχωρητής
|
Όνομα
|
Περιγραφή
|
SI |
Source index |
Δείχνει το Offset ενός αλφαριθμητικού ή πίνακα . |
DI |
Destination index |
Δείχνει το offset προορισμού ενός αλφαριθμητικού ή πίνακα . |
IP |
Instruction Pointer |
Δείχνει το offset της επόμενης εντολής που πρέπει να εκτελέσει ο επεξεργαστής . |
IV. Καταχωρητές
στοίβας
Καταχωρητής
|
Όνομα
|
Περιγραφή
|
BP |
Base Pointer |
Συνδυάζοντάς τον με τον δείκτη τμήματος στοίβας , μπορεί να χρησιμοποιηθεί για διαδικασίες στοίβας . |
SP |
Stack Pointer |
Το offset της στοίβας . |
V.
Σημαίες
Ενώ όλοι οι υπόλοιποι καταχωρητές του x86 είναι καταχωρητές των 32 bit , οι σημαίες είναι καταχωρητές του 1 bit . Δηλαδή μπορεί να έχουν την τιμή 1 (true) ή 0 (false) . Οι κυριότερες σημαίες είναι οι Carry Flag , Zero Flag , Overflow Flag κλπ . Θα μας χρησιμεύσουν στη διακλάδωση υπό συνθήκη στα προγράμματά μας .
Οι εντολές του x86 που θα μας φανούν χρήσιμες εδώ είναι οι εξής :
Εντολή |
Χρήση |
Παράδειγμα |
MOV |
Εκχώρηση τιμής σε ένα καταχωρητή . Μπορεί να είναι μια οποιαδήποτε τιμή ή η τιμή ενός άλλου καταχωρητή |
MOV AX,1 MOV AH,1 MOV {Καταχωρητής},{τιμή} MOV AX,BX MOV
{destination},{source} |
ADD |
Προσθέτει μια τιμή σε έναν καταχωρητή ή την τιμή ενός καταχωρητή σε έναν άλλο |
ADD AX,1 (Η τιμή του AX αυξάνει κατά 1) ADD AX,BX (Η τιμή του ΑΧ Αυξάνει κατά την τιμή του ΒΧ) |
SUB |
Αφαιρεί μια τιμή από έναν κταχωρητή ή την τιμή ενός κατάχωρητή από έναν άλλο |
SUB AX,1 (Η τιμή του ΑΧ ελαττώνεται κατά 1) SUB AX,BX (Η τιμή του ΑΧ ελαττώνεται κατά την τιμή του ΒΧ) |
CALL / RET |
Η εντολή CALL ακολουθούμενη από μια διεύθυνση , έχει σαν αποτέλεσμα την κλήση μιας υπορουτίνας . Η διεύθυνση που δίνουμε είναι η διεύθυνση της υπορουτίνας . Η εντολή RET τερματίζει την υπορουτίνα και επιστρέφει στη διεύθυνση από όπου είχε κληθεί . |
CALL 150 ….. υπόλοιπο πρόγραμμα… <διεύθυνση 150> …εντολές υπορουτίνας… RET (επιστροφή μετά την εντολή CALL και κανονική εκτέλεση του υπόλοιπου προγράμματος) |
PUSH/POP |
Χρησιμεύουν για τη διαχείριση των δεδομένων της στοίβας . Η εντολή PUSH εισάγει ένα καταχωρητή στη στοίβα και η POP τον ανακαλεί. Θυμηθείτε τη LIFO λογική της στοίβας |
ΠΑΡΑΔΕΙΓΜΑ 1MOV AX,12 ;AX=12h MOV BX,23
;BX=23h PUSH AX :AX Πάνω στη στοίβα PUSH BX ;BX πάνω από το ΑΧ MOV AX,0
; MOV BX,0
;AX=BX=0 POP BX ; POP AX ;Επαναφορά ΑΧ,ΒΧ ΠΑΡΑΔΕΙΓΜΑ 2 MOV AX,12 MOV BX,23 PUSH AX PUSH BX POP AX POP BX (Έγινε ανταλλαγή των τιμών των καταχωρητών ΑΧ και ΒΧ – LIFO λογική) |
INT |
Κλήση μιας διακοπής (βλ. παρακάτω) |
INT 20 (έξοδος από το πρόγραμμα) |
Χρησιμοποιήστε τον πίνακα απλώς σαν αναφορά . Μην τον αποστηθίσετε προς το παρόν .
Οι interrupts (διακοπές) είναι ό,τι ακριβώς λέει και η λέξη . Το πρόγραμμά μας διακόπτεται και συμβαίνει κάποια διαδικασία .Κατόπιν το πρόγραμμα συνεχίζει να εκτελείται κανονικά από το σημείο που έγινε η διακοπή . Μπορούμε να θεωρήσουμε μια διακοπή σαν μια υπορουτίνα , μόνο που οι διακοπές είναι ήδη καθορισμένες και δεν τις προγραμματίζουμε εμείς .
Οι διακοπές κάνουν δουλειές ρουτίνας , όπως τοποθέτηση αλφαριθμητικών στην οθόνη , καθορισμός της κατάστασης της οθόνης , pixel plotting κλπ . Μερικές σημαντικές interrupts είναι οι παρακάτω .
Interrupt |
Λειτουργία |
Περιγραφή |
Χρήση |
INT 20h |
- |
Έξοδος στο DOS (Μόνο για προγράμματα COM) |
INT 20 |
INT 21h |
1 |
Είσοδος ενός χαρακτήρα από το πληκτρολόγιο . Ο χαρακτήρας επιστρέφεται στον καταχωρητή AL |
MOV AH,1 INT 21 |
INT 21h |
2 |
Εμφανίζει ένα χαρακτήρα στην οθόνη . Ο κωδικός ASCIL του χαρακτήρα πρέπει να είναι στον καταχωρητή DL |
MOV AH,2 MOV DL,
{ASCIL} INT 21 |
9 |
Εμφανίζει ένα αλφαριθμητικό στην οθόνη . Η διεύθυνση όπου είναι αποθηκευμένο το αλφαριθμητικό πρέπει να είναι στον καταχωρητή DX . |
MOV AH,9 MOV
DX,{String adress} INT 21 |
ΚΕΦΑΛΑΙΟ
5 : Το πρώτο μας πρόγραμμα
Και μετά όλο το ομολογουμένως βαρετό υπόβαθρο , έφτασε η ώρα να γράψουμε ένα πρόγραμμα σε γλώσσα Assembly . Συνήθως το πρώτο πρόγραμμα που γράφουμε όταν μαθαίνουμε μια γλώσσα είναι το γνωστό “Hello , world!” . Αυτό θα κάνουμε κι εδώ .
Όπως είπαμε και πριν , για να μεταφράσουμε τα περίεργα σύμβολα της Assembly σε εκτελέσιμο κώδικα , χρειαζόμαστε ένα πρόγραμμα που λέγεται συμβολομεταφραστής . Προς το παρόν όμως δε θα χρειαστεί να κατεβάσετε και να εγκαταστήσετε κανέναν συμβολομεταφραστή , αφού το σύστημά σας περιέχει ήδη ένα πρόγραμμα που μπορούμε να χρησιμοποιήσουμε για το σκοπό αυτό . Λέγεται debug (απεντόμωση) και βρίσκεται σε όλα τα συστήματα DOS (και WINDOWS) . Ας δούμε λοιπόν λίγα πράγματα για τη χρήση του …
Το Debug και η χρήση του
Με το debug θα γράψουμε προγράμματα .COM . Για να ξεκινήσουμε το Debug , ανοίγουμε ένα κέλυφος MS DOS (Start – MS DOS Prompt) και γράφουμε DEBUG
Για να φορτώσουμε ένα αρχείο COM που έχουμε ήδη φτιάξει χρησιμοποιούμε την ακόλουθη σύνταξη :
C:>
DEBUG FILENAME.COM
Όταν έχουμε ανοίξει το debug , βλέπουμε μια παύλα , που είναι το προτρεπτικό σήμα για διαταγές . Οι κυριότετρες διαταγές του Debug είναι οι εξής :
Λειτουργία |
|
-q |
Έξοδος (quit) |
-r |
Προβολή όλων των καταχωρητών και των τιμών τους |
-r {register} |
Αλλαγή της τιμής του καταχωρητή {register} πχ –r ax για να αλλάξετε τον AX |
-a {adress} |
Εκχώρηση των εντολών που θέλουμε στην διεύθυνση {asress} |
-u {adress} |
Προβολή στην οθόνη των εντολών που υπάρχουν στη διεύθυνση {adress} |
-e {adress} |
Αλλαγή του byte που βρίσκεται στη διεύθυνση {adress} . Μπορούμε να αλλάξουμε και τα επόμενα bytes , πατώντας το SPACE |
-d {adress} |
Εμφανίζει τα περιεχόμενα της διεύθυνσης {adress} στην οθόνη |
-n {name} |
Ονομάζει το πρόγραμμά μας με το όνομα {name} |
-w |
Αποθηκεύει το πρόγραμμά μας στο δίσκο , με filename αυτό που δηλώσαμε παραπάνω στην εντολή –n . Πρέπει δηλαδή η εντολή –n να προηγείται της –w . Ακόμη πριν αποθηκεύσουμε το πρόγραμμά μας πρέπει να πούμε στο debug πόσο μεγάλο είναι σε bytes , πράγμα που γίνεται με τους καταχωρητές BX:CX . Βάζουμε δηλαδή στους καταχωρητές αυτούς την τιμή που αντιστοιχεί στο πόσα bytes είναι το πρόγραμμά μας (χρησιμοποιούμε την εντολή -r) , και κατόπιν αποθηκεύουμε το πρόγραμμα στο δίσκο |
Για να εκτυπώσουμε ένα αλφαριθμητικό στην οθόνη θα χρησιμοποιήσουμε την interrupt 21h , λειτουργία 9 του DOS .
Για να δηλώσουμε ότι θέλουμε τη λειτουργία 9 , βάζουμε το 9 στον καταχωρητή AH :
MOV AH,9
O καταχωρητής DX πρέπει να δείχνει τη σχετική διεύθυνση του μηνύματος . Αυτή είναι η 109h :
MOV DX,109
Πώς μαντέψαμε ότι η σχετική διεύθυνση είναι η 109h ? Απλώς γράφουμε πρώτα όλο το πρόγραμμα και μετά κοιτάμε σε ποια διεύθυνση τελειώνει . Εκεί ακριβώς θα τοποθετήσουμε και το αλφαριθμητικό μας . Θα μπορούσαμε βέβαια να το τοποθετήσουμε και σε μια διεύθυνση πολύ μακριά πχ 200h , ώστε να μην μας απασχολέι που τελειώνει ο κώδικας . Στην περίπτωση αυτή όμως όλα τα δεδομένα μεταξύ 109h-200h θα γραφτούν στο δίσκο σαν σκουπίδια (αν και το πρόγραμμά μας θα τρέξει κανονικά ) , πράγμα που είναι λάθος και πρέπει να το αποφύγουμε . Ούτως ή άλλως όμως το σημείο αυτό λίγο μας ενδιαφέρει , αφού όταν θα δουλέψουμε με το συμβολομεταφραστή , δεν θα ανησυχούμε για τις διευθύνσεις μνήμης , αφού αυτός θα αναλαμβάνει να τις καθορίζει για μας .
Για να εκτελεστεί η interrupt 21h , δίνουμε την εντολή :
INT 21
Και τέλος για να τελειώσει το πρόγραμμα :
ΙΝΤ 20
Όλο το πρόγραμμα λοιπόν είναι :
MOV AH,9
MOV DX,109
INT 21
INT 20
Θα το γράψουμε με την εντολή a (assemble)
(Debug
Prompt) – a 100
(δώστε μια-μια τις εντολές και πατήστε ENTER . Όταν τελειώσετε πατήστε 2 φορές ENTER)
Τώρα το πρόγραμμα που είδαμε συν κάποιες ακατανόητες εντολές που βρίσκονται μετά την τελευταία μας εντολή MOV 20 . Αυτά είναι τα περιεχόμενα που είχε η μνήμη μας . Παρατηρούμε ότι η αμέσως επόμενη εντολή από την τελευταία που δώσαμε βρίσκεται στη διεύθυνση 109h.
Σε αυτή τη διεύθυνση λοιπόν θα πρέπει να βάλουμε και το αλφαριθμητικό μας , ώστε να μην υπάρχει άχρηστος χώρος μεταξύ του κώδικα και του αλφαριθμητικού και το πρόγραμμά μας να είναι μικρό και συμπαγές .
Για να βάλουμε ένα αλφαριθμητικό στη μνήμη πρέπει να εισάγουμε έναν έναν τους κωδικούς ASCIL του αλφαριθμητικού , με την εντολή e του debug .
(Debug prompt)- e 109 (αφού θέλουμε το αλφαριθμητικό να αρχίζει στη διεύθυνση 109)
(δώστε τους αριθμούς 48 , 65 , 6c , 6c , 6f , 20 , 77 , 6f , 72 , 6c , 64 , 21 , 24 χωρισμένους με κενά . Όταν τελειώσετε πατήστε ENTER)
Οι αριθμοί μπορούν πολύ εύκολα να βρεθούν από οποιονδήποτε πίνακα ASCIL . Εναλλακτικά μπορείτε να χρησιμοποιήσετε το πρόγραμμα str2asc.exe που σας δίνω στο αρχείο zip .
Ο τελευταίος χαρακτήρας είναι ο 24h , δηλαδή το σήμα του δολαρίου . Αυτό χρησιμεύει για να δείξει το τέλος του αλφαριθμητικού και δεν εκτυπώνεται στην οθόνη .
Τώρα αν τρέξουμε το πρόγραμμά μας`(εντολή -g) θα εμφανιστεί στην οθόνη ένα μήνυμα (αν όλα πήγαν καλά , αν όχι , διπλοτσεκάρετε ότι κάνατε όλα τα προηγούμενα σωστά) .
Αν όλα πήγαν καλά το πρόγραμμά μας είναι έτοιμο και μένει μόνο να το γράψουμε στο δίσκο …
Όταν γράφουμε το πρόγραμμά μας στο δίσκο , το debug απλώς παίρνει μια
περιοχή της μνήμης και την αποθηκεύει σε ένα αρχείο . Καταρχήν πρέπει να του
πούμε πόσο μεγάλο είναι το πρόγραμμά μας . Αν κάνουμε τους υπολογισμούς ,
προκύπτει ότι το πρόγραμμά μας (κώδικας + δεδομένα αλφαριθμητικού) είναι όλο 16h bytes . Για να πούμε στο debug ότι το πρόγραμμά μας
είναι 16h bytes
βάζουμε στον καταχωρητή CΧ
, την τιμή 16h . Δηλαδή:
(Debug
prompt) – r cx
(εισάγετε 16 - ENTER)
(Αν ο καταχωρητής BX δεν είναι 0 , κάντε τον 0 με την ίδια εντολή , αφού ο αριθμός των bytes κρατιέται από τον συνδυασμό BX:CX που είναι ένας 64μπιτος αριθμός) .
Κατόπιν πρέπει να δώσουμε ένα όνομα στο αρχείο μας . Έστω HELLO.COM
(Debug
prompt) – n HELLO.COM
Τέλος γράφουμε το αρχείο μας στο δίσκο :
(Debug prompt) –w
Βγαίνουμε από το Debug:
(Debug prompt)-q
Και εκτελούμε το πρόγραμμά μας από το DOS:
C:>HELLO.COM
Το κείμενο αυτό σε καμία περίπτωση δεν αποτελεί ένα ολοκληρωμένο
εισαγωγικό tutorial για την Assembly του x86
. Αντίθετα καλύπτει τα ελάχιστα μόνο σημεία που απαιτούνται για την κατασκευή ενός
πολύ απλού προγράμματος σε Assembly
. Σκοπός του κειμένου αυτού είναι να «ξύσουμε» λίγο την επιφάνεια της πολύ ισχυρής
αυτής γλώσσας , και να γνωρίσουμε έννοιες και εργαλεία χρήσιμα για το coding σε
αυτή .
Βέβαια , θα υπάρξουν και επόμενα κείμενα που θα καλύπτουν πιο πολύπλοκα θέματα , αλλά θα τα γράψω μόνο αν εκδηλωθεί αντίστοιχο ενδιαφέρον από τους αναγνώστες του code . Επικοινωνήστε μαζί μου στο msamurai@freemail.gr .