Προς το περιεχόμενο

Ας ανακαλύψουμε ξανά την απ' ευθείας επικοινωνία με το Hardware και τη γλώσσα Assembly


White_Cat

Προτεινόμενες αναρτήσεις

Δημοσ. (επεξεργασμένο)

Κατά τα τελευταία 30 χρόνια η εξέλιξη των Η/Υ υπήρξε αλματώδης, τόσο σε ταχύτητα όσο και σε επεξεργαστική ισχύ. Παρ' όλ' αυτά υπάρχουν ακόμα και σήμερα ορισμένες βασικές αρχές της λειτουργίας τους οι οποίες παραμένουν αναλλοίωτες, με αποτέλεσμα να συνεχίζουν να διδάσκονται στα πανεπιστήμια μαθήματα όπως η ψηφιακή σχεδίαση, η αρχιτεκτονική υπολογιστών, αλλά και κάποια εκδοχή της γλώσσας προγραμματισμού Assembly.

Για τη διδασκαλία της Assembly συνήθως χρησιμοποιείται λογισμικό εξομοίωσης το οποίο προσομοιώνει μόνο ένα μέρος των λειτουργιών του επεξεργαστή που κατά περίπτωση εξετάζεται. Η πρακτική αυτή σίγουρα παρέχει κάποιες ευκολίες, αλλά έχει ως αποτέλεσμα πολλοί φοιτητές να πιστεύουν λανθασμένα ότι δήθεν η Assembly είναι πλέον κάτι εντελώς νεκρό, ένα κατάλοιπο του παρελθόντος της επιστήμης της πληροφορικής που δεν έχει καμία χρησιμότητα σήμερα.

Αυτός ακριβώς ο μύθος θα επιχειρηθεί να καταρριφθεί με αυτό το άρθρο. Προφανώς και δεν θεωρώ ότι οι προγραμματιστές είναι γενικώς παραγωγικότεροι όταν προγραμματίζουν σε Assembly αντί κάποιας άλλης γλώσσας υψηλού επιπέδου, ούτε εξασφαλίζεται η φορητότητα του λογισμικού. Υπάρχουν όμως καταστάσεις που η απ' ευθείας επικοινωνία με το υλικό του υπολογιστή παράγει ταχύτατη απόκριση, με αποτέλεσμα η Assembly σήμερα να χρησιμοποιείται σε περιπτώσεις όπως είναι ο προγραμματισμός μικρο-ελεγκτών, όπου ο χρόνος αποτελεί κρίσιμο παράγοντα.

Σ' αυτό λοιπόν το άρθρο θα επιχειρήσουμε να δείξουμε ότι η Assembly είναι δυνατόν να χρησιμοποιηθεί ακόμα και σε σύγχρονα συστήματα και κυρίως θα δούμε ότι μπορούμε να τρέξουμε ρουτίνες της γλώσσας, απ' ευθείας επάνω στο τρέχον υλικό του υπολογιστή, χωρίς να χρειάζεται λογισμικό προσομοίωσης.

Στην εποχή μας οι επεξεργαστές που κυριαρχούν στο εμπόριο για επιτραπέζιους υπολογιστές είναι αυτοί της Intel με αρχιτεκτονική 64 bits. Υποθέτοντας βάσιμα ότι ο αναγνώστης διαθέτει έναν τέτοιο επεξεργαστή, επιλέγουμε να μιλήσουμε μόνο για την αρχιτεκτονική αυτή. Επιπλέον, αντί των Windows, επιλέγουμε να χρησιμοποιήσουμε το Linux γιατί η δομή του και η συνολική φιλοσοφία του ως λειτουργικό σύστημα διευκολύνει την ενασχόληση του χρήστη απ' ευθείας με το υλικό του υπολογιστή.

Ξεκινώντας την περιήγησή μας στη γλώσσα, είναι σκόπιμο πριν απ' ο,τιδήποτε άλλο να μιλήσουμε για τους καταχωρητές των επεξεργαστών της Intel. Οι καταχωρητές οιουδήποτε επεξεργαστή είναι στην ουσία πολύ γρήγορες μνήμες που βρίσκονται πολύ κοντά στη CPU και κατά τον προγραμματισμό σε Assembly χρησιμοποιούνται κατά κόρον για την αποθήκευση και ανάκληση μικρών ποσοτήτων δεδομένων.

Οι επεξεργαστές της Intel με αρχιτεκτονική 64 bits διαθέτουν 16 καταχωρητές συνολικά που όπως αναμένεται ο καθένας τους χωράει δεδομένα 64 bits ή αλλιώς 8 bytes. Οι 14 εξ αυτών μπορούν να χρησιμοποιηθούν για οποιοδήποτε σκοπό ενώ οι υπόλοιποι δύο αποτελούν καταχωρητές ειδικής χρήσης.

Οι καταχωρητές γενικού σκοπού είναι :

RAX, RBX, RCX, RDX, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15

Να θυμάστε πάντα ότι αυτά τα 14 ονόματα καταχωρητών μπορούν να χρησιμοποιηθούν μόνο όταν θέλουμε να παράγουμε αποκλειστικά 64μπιτο εκτελέσιμο κώδικα.

Όσοι αναγνώστες τυχόν νοσταλγούν την Assembly 8086/88 της δεκαετίας του 80 ή 90 ασφαλώς και αναγνωρίζουν τα πρώτα 6 ονόματα καταχωρητών με μόνη διαφορά την προσθήκη του R στην αρχή τους.

Αυτό συμβαίνει επειδή οι σχεδιαστές των επεξεργαστών αυτών έχουν φροντίσει ώστε να υπάρχει συμβατότητα με παλαιότερα μοντέλα.

Για παράδειγμα, εάν θέλουμε να προσπελάσουμε τα 4 πρώτα bytes του καταχωρητή RAX τότε το όνομά του πρέπει να αλλάξει σε EAX και μπορούμε να τον χειριστούμε άφοβα σαν να ήταν καταχωρητής 32 bit σε παλαιότερο επεξεργαστή. Επίσης στην περίπτωση που θέλουμε να προσπελάσουμε τα bytes 0-1 του RAX, μπορούμε να χρησιμοποιήσουμε άφοβα τα ονόματα AH και AL όπως ακριβώς θα έκανε ένας προγραμματιστής σε επεξεργαστή 8086/88.

Το ίδιο ακριβώς συμβαίνει και με τους υπόλοιπους καταχωρητές και για να μη μακρηγορώ προτείνω σε όσους θέλουν να ανατρέξουν σε εγχειρίδια σχετικά με τον 8086/88.

Καταχωρητής 64 bit

Καταχωρητής 32 bit

Καταχωρητής 16 bit

Καταχωρητής 8 bit

RAX

EAX

AX

AL

RCX

ECX

CX

CL

RDX

EDX

DX

DL

RBX

EBX

BX

BL

RSI

ESI

SI

SIL

RDI

EDI

DI

DIL

RSP (ειδ. χρήση)

ESP (ειδ. χρήση)

SP (ειδ. χρήση)

SPL (ειδ. χρήση)

RBP (ειδ. χρήση)

EBP (ειδ. χρήση)

BP (ειδ. χρήση)

BPL (ειδ. χρήση)

R8

R8D

R8W

R8B

R9

R9D

R9W

R9B

R10

R10D

R10W

R10B

R11

R11D

R11W

R11B

R12

R12D

R12W

R12B

R13

R13D

R13W

R13B

R14

R14D

R14W

R14B

R15

R15D

R15W

R15B

 

Είναι προφανές ότι σε σχέση με την αρχιτεκτονική του 8086/88 οι σχεδιαστές της Intel φρόντισαν να αυξήσουν κατά πολύ τους καταχωρητές γενικού σκοπού -τόσο σε πλήθος, όσο και σε χωρητικότητα- αλλά παράλληλα κατέβαλαν μεγάλη προσπάθεια ώστε ακόμα και κώδικας που συντάχτηκε πριν από 20 χρόνια να μπορεί να λειτουργήσει με ελάχιστες αλλαγές.

Δεν κρίνω σκόπιμο να μιλήσουμε σήμερα για τους καταχωρητές ειδικής χρήσης, γιατί αυτοί αφορούν τη δομή δεδομένων του σωρού που θα μας χρειαστεί αργότερα.

Σήμερα θα προσπαθήσουμε να συντάξουμε και να τρέξουμε ένα απλό πρόγραμμα που θα τυπώνει ένα μήνυμα στην οθόνη.

Το πρόγραμμα που θα χρειαστούμε είναι ένας assembler που λέγεται yasm και είναι διαθέσιμος στα αποθετήρια λογισμικού όλων των γνωστών διανομών Linux. Πριν προχωρήσουμε, απλά εγκαταστήστε τον όπως ακριβώς θα εγκαθιστούσατε οποιοδήποτε άλλο λογισμικό στη διανομή σας.

Όπως είναι γνωστό ο assembler θα παράγει αποκλειστικά object code, οπότε θα χρειαστούμε κι έναν linker για να μας δώσει το τελικό εκτελέσιμο αρχείο. Αυτός ονομάζεται ld, αλλά συνήθως είναι προ-εγκατεστημένος στις πιο πολλές διανομές. Αν τυχαίνει να μην τον έχει ήδη εγκαταστήσει η δική σας διανομή, ακολουθείστε τα συνήθη βήματα για να τον εγκαταστήσετε.

Επόμενο βήμα είναι να ανοίξετε έναν οποιοδήποτε απλό κειμενογράφο και να πληκτρολογήσετε προσεκτικά τα παρακάτω :

	global _start
	_start: 
	mov rax, 1 ; Κλήση της υπηρεσίας 1 σημαίνει εκτύπωση μηνύματος
	mov rdi, 1 ; Εδώ ορίζουμε το που θα εκτυπωθεί το μήνυμα. 1 = Standard συσκ. εξόδου
	mov rsi, message ; Διεύθυνση RAM που αποθηκεύτηκε το μήνυμα 
	mov rdx, messagelen ; Μήκος μηνύματος σε bytes 
	syscall ; Κλήση του πυρήνα του Linux
	mov rax, 60 ; Κλήση της υπηρεσίας 60 για επιστροφή στο λειτουργικό σύστημα
	xor rdi, rdi ; Στον καταχωρητή RDI φορτώνουμε τον κωδικό εξόδου
	syscall ; Κλήση του πυρήνα του Linux
	section .data
	message: db "Άσπρος Γάτος", 10 
	messagelen equ $-message
	

 

Είναι πολλά αυτά που πρέπει να ειπωθούν εδώ και θα ξεκινήσω καλύτερα από την data section. Θα μπορούσα να την έχω γράψει στην αρχή του κώδικα αντί για το τέλος, αλλά αυτό δεν έχει σημασία.

Μέσα σ’ αυτό το τμήμα προγράμματος ορίζουμε τις σταθερές τιμές που θέλουμε να χρησιμοποιήσουμε στο πρόγραμμά μας. Είναι σαν τη δήλωση CONST ας πούμε στην Pascal.

Με την εντολή message db ορίζεται ότι θα έχουμε μία σταθερά που ονομάζεται message και θα είναι αλφαριθμητική. Ο αριθμός 10 στο τέλος είναι ο χαρακτήρας αλλαγής γραμμής.

Με την εντολή messagelen equ $-message ορίζεται ότι θα έχουμε μία αριθμητική σταθερά με όνομα messagelen που θα ισούται με το μήκος του αλφαριθμητικού της σταθεράς message.

Προσέξτε ότι οι αλφαριθμητικές σταθερές ορίζονται με τον τελεστή db και διπλά εισαγωγικά, ενώ οι αριθμητικές σταθερές ορίζονται με τον τελεστή equ. Επιπλέον η έκφραση “$-τάδε” σημαίνει “μήκος της σταθεράς τάδε σε bytes”.

Ας πάμε τώρα στον κυρίως κώδικα. Ξεκινάμε δηλώνοντας ότι θα αποτελείται από μία ρουτίνα με καθολική ορατότητα που ονομάζεται start. Αυτή η ρουτίνα είναι σαν την main() της C. Αυτήν απαιτεί να βρει ο linker για να ξέρει από που θα ξεκινήσει.

Όλοι όσοι έχουν ασχοληθεί έστω λίγο με Assembly στους επεξεργαστές Intel γνωρίζουν την εντολή MOV. H σύνταξή της είναι MOV καταχωρητής, τιμή και σημαίνει ότι απαιτούμε να φορτωθεί σε συγκεκριμένο καταχωρητή συγκεκριμένη τιμή. Φυσικά έχει κι άλλες μορφές, αλλά αυτή είναι η συνηθέστερη.

Γενικά στον προγραμματισμό σε Assembly λειτουργούμε ως εξής. Για ό,τι κι αν θελήσουμε να κάνουμε, ανοίγουμε τον εγχειρίδιο και βρίσκουμε ποια είναι η κατάλληλη υπηρεσία του λειτουργικού συστήματος που πρέπει να κληθεί για να κάνει τη δουλειά που θέλουμε. Έπειτα βρίσκουμε ποιες είναι οι κατάλληλες παράμετροι και τις φορτώνουμε στους σωστούς καταχωρητές, πάλι σύμφωνα με το εγχειρίδιο γιατί αυτά κανείς δεν τα μαθαίνει απ’ έξω. Τέλος χρησιμοποιούμε συνήθως την εντολή syscall για να καλέσουμε τον πυρήνα του λειτουργικού συστήματος να διαβάσει τις τιμές στους καταχωρητές και να ενεργήσει κατάλληλα.

Για να τυπώσουμε ένα μήνυμα θα χρειαστούμε την υπηρεσία υπ’ αριθμόν 1. Φορτώνουμε λοιπόν στον πρώτο καταχωρητή γενικού σκοπού τον αύξοντα αριθμό υπηρεσίας. Έπειτα στον καταχωρητή RDI φορτώνουμε το πού ακριβώς θα εκτυπωθεί το μήνυμά μας. Η τιμή 1 σημαίνει την οθόνη.

Στον καταχωρητή RSI φορτώνουμε τη διεύθυνση της RAM που έχει αποθηκευτεί το μήνυμα που θέλουμε να τυπώσουμε. Στον καταχωρητή RDX φορτώνουμε μια τιμή που αντιστοιχεί στο μήκος του μηνύματος σε bytes. Στο τέλος καλούμε το λειτουργικό σύστημα να ενεργήσει μέσω της εντολής syscall.

Φαινομενικά έχουμε τελειώσει, αλλά δεν είναι καθόλου έτσι. Τώρα πρέπει να δηλώσουμε ότι θέλουμε το λειτουργικό σύστημα να εγκαταλείψει το τρέχον πρόγραμμα και να ξαναπάρει τον έλεγχο. Να μας γυρίσει δηλαδή πίσω στο προτρεπτικό σήμα του Linux. Αν αυτό δεν το κάνουμε, το πρόγραμμα θα κολλήσει.

Η αντίστοιχη υπηρεσία του λειτουργικού συστήματος είναι η υπ' αριθμόν 60 και δέχεται μόνο μία παράμετρο στον καταχωρητή RDI. Η παράμετρος αυτή είναι ο κωδικός σφάλματος που θέλουμε να επιστρέφει το πρόγραμμά μας προς το ίδιο το Linux.

Αν η εκτέλεση έχει φτάσει μέχρι αυτό το σημείο, σημαίνει ότι όλα έχουν πάει καλά και ο κωδικός σφάλματος που θα επιστραφεί πρέπει να ισούται με 0. Κάνοντας αποκλειστική διάζευξη της τιμής του RDI με τον εαυτό της, προφανώς διασφαλίζουμε ότι όλα τα bits μηδενίστηκαν και στο τέλος καλούμε πάλι τον πυρήνα του Linux.

Τώρα που τελειώσαμε απλά σώστε τη ρουτίνα σε απλό αρχείο κειμένου (έστω firstprog.asm) και μεταγλωττίστε το με τις εξής εντολές προς το Linux

yasm -f elf64 firstprog.asm && ld firstprog.o

Η παράμετρος -f elf64 ορίζει ότι θα παράγουμε αποκλειστικά 64μπιτο κώδικα. Όταν εκτελεστεί ο yasm παράγει ένα αρχείο object code που λέγεται firstprog.o ενώ η εντολή μετά το διπλό & καλεί τον linker για να δώσει το τελικό εκτελέσιμο. Παρατηρώ ότι το μέγεθος του τελικού εκτελέσιμου αρχείου είναι μόλις 928 bytes, πράγμα που δεν συμβαίνει με άλλες γλώσσες υψηλού επιπέδου. Αν θέλετε να παράγετε κώδικα στα 32 bits θα πρέπει να αλλάξετε το elf64 σε elf32 αλλά και να αλλάξετε τον κώδικα της ρουτίνας μας, ώστε να χρησιμοποιεί αποκλειστικά ονόματα 32μπιτων καταχωρητών, δηλ. πχ EAX αντί RAX κλπ.

Ελπίζω με το κείμενο αυτό να έχω καταφέρει να δώσω μία ιδέα περί του πώς λειτουργεί η γλώσσα Assembly σε σύγχρονους επεξεργαστές. Αν επίσης έχω καταφέρει να αναζωπυρώσω το ενδιαφέρον για τη γλώσσα αυτή και για την απ' ευθείας επικοινωνία με το υλικό του Η/Υ θα έχω πετύχει το στόχο μου. Σε περίπτωση που υπάρξει αρκετό ενδιαφέρον και ανταπόκριση στο κείμενό μου αυτό, υπόσχομαι να επανέλθω.

Σας ευχαριστώ πολύ,

Ο Άσπρος Γάτος

Επεξ/σία από White_Cat
  • Like 16
  • Thanks 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • 2 εβδομάδες αργότερα...
Δημοσ. (επεξεργασμένο)

 Σήμερα θα συνεχίσουμε την εξερεύνηση της γλώσσας Assembly από εκεί που είχαμε σταματήσει την προηγούμενη φορά. Πριν απ' ό,τιδήποτε άλλο όμως, θα ήθελα να ευχαριστήσω θερμά όλους τους φίλους για τη θετική ανταπόκρισή τους, η οποία και με ενθαρρύνει να εξακολουθήσω να γράφω.
  Όπως είναι ήδη προφανές από όσα έχουμε πει ως τώρα, η Assembly είναι μια πρωτόλεια γλώσσα, όπου δεν συναντούμε τις ευκολίες εκείνες στις οποίες είναι συνηθισμένοι οι σημερινοί προγραμματιστές. Παρ' όλ' αυτά όμως, ακόμα κι εδώ υπάρχουν κάποιες λίγες διευκολύνσεις που μπορούμε να χρησιμοποιήσουμε ώστε να μας λύσουν κάπως τα χέρια.
 Η βασικότερη από αυτές είναι οι λεγόμενες μακροεντολές, οι οποίες είναι συμπαγή τμήματα κώδικα που εκτελούν τετριμμένες και επαναλαμβανόμενες διαδικασίες και μπορούν να επαναχρησιμοποιούνται, ώστε ο προγραμματιστής να μην απαιτείται να γράφει παρόμοιο κώδικα ξανά και ξανά. Μια μακροεντολή δηλαδή δέχεται έναν αριθμό παραμέτρων και θυμίζει εν πολλοίς για παράδειγμα τις συναρτήσεις της γλώσσας C, ή τα υποπρογράμματα (procedures) της PASCAL.
 Αξίζει να θυμόμαστε ότι οι μακροεντολές συντάσσονται πριν από το κυρίως πρόγραμμα και σε γενικές γραμμές ορίζονται ως εξής :

	%macro όνομα μακροεντολής αριθμός παραμέτρων
	κώδικας μακροεντολής
	%endmacro
	

Επί παραδείγματι, την προηγούμενη φορά μιλήσαμε για το system call με αριθμό 60, το οποίο χρησιμοποιείται στο τέλος κάθε προγράμματος, ώστε το λειτουργικό σύστημα να ανακτήσει τον έλεγχο του υπολογιστή. (Αν δεν θυμάστε πώς ακριβώς καλείται, ανατρέξτε στο προηγούμενό μας κείμενο.)
 Είναι σκόπιμο λοιπόν για τη δική μας ευκολία να ορίσουμε μία μακροεντολή που θα ονομάζεται exit και θα κάνει ακριβώς αυτή τη δουλειά, χωρίς να χρειάζεται να δέχεται κάποια παράμετρο.

%macro exit 0
    mov rax,60
    xor rdi,rdi
    syscall
%endmacro
	

Την προηγούμενη φορά είχαμε αναφερθεί και στο system call με αριθμό 1 που το χρησιμοποιούμε για να τυπώνουμε μηνύματα στην οθόνη. Για να φτιάξουμε μια μακροεντολή γι' αυτό πρέπει πρώτα να σκεφτούμε το πλήθος των παραμέτρων που αλλάζουν κάθε φορά που το καλούμε. Οι παράμετροι αυτές είναι κατ' αρχήν η διεύθυνση μέσα στη RAM όπου βρίσκεται αυτό που πρέπει να τυπώσουμε και δεύτερον το μήκος αυτού σε bytes. Άρα οι παράμετροι που θα δέχεται η συγκεκριμένη μακροεντολή θα είναι δύο. Όπως ίσως θα θυμάστε, πριν από τη χρήση της εντολής syscall, φορτώνουμε τη διεύθυνση μνήμης όπου βρίσκεται το μήνυμά μας στον καταχωρητή RSI, ενώ το μήκος του σε bytes φορτώνεται στον καταχωρητή RDX. Άρα η μακροεντολή μπορεί να οριστεί ως εξής :

%macro print 2
    mov rax,1
    mov rdi,1
    mov rsi,%1
    mov rdx,%2
    syscall
%endmacro
	

Στο παραπάνω τμήμα κώδικα αξίζει να προσεχθούν οι γραμμές 4 και 5, ώστε να γίνει κατανοητή η χρήση του συμβόλου % κατά τον ορισμό μακροεντολών. Η γραμμή 4 λέει «φόρτωσε στον RSI την πρώτη παράμετρο που δέχτηκε η εν λόγω μακροεντολή», ενώ η 5 λέει «φόρτωσε στον RDX τη δεύτερη παράμετρο». Έτσι λοιπόν για να τυπώσω ένα μήνυμα αρκεί μέσα στο κυρίως πρόγραμμα να γράψω μια γραμμή που θα μοιάζει κάπως έτσι :

	print message message_length
	

Ένα ακόμα πολύ βασικό ζήτημα για το οποίο θα ήθελα να μιλήσω είναι ο ορισμός μεταβλητών μέσα σε ένα πρόγραμμα. Την περασμένη φορά αναφερθήκαμε μόνο στον ορισμό σταθερών τιμών και είπαμε ότι αυτές ορίζονται μέσα στο τμήμα του προγράμματος που ονομάζεται data section. Για τον ορισμό μεταβλητών υπάρχει το ανάλογο τμήμα προγράμματος που ονομάζεται bss section, όπου ορίζουμε το όνομα κάθε μεταβλητής και το πόσα bytes θα δεσμευτούν γι' αυτήν μέσα στη μνήμη.

	section .bss
    name resb 40
	

Εδώ ορίζουμε μια μεταβλητή που ονομάζεται name και δεσμεύει 40 bytes. Η λέξη-κλειδί RESB σημαίνει «reserve bytes».

Στο σημείο αυτό θα πρέπει να αναφερθώ σ' ένα ακόμα πολύ σημαντικό system call που χρησιμοποιείται όταν θέλουμε να διαβάσουμε δεδομένα από το πληκτρολόγιο.
Για να το χρησιμοποιήσουμε πρέπει κατ' αρχήν να έχουμε ορίσει μια μεταβλητή (όπως παραπάνω) στην οποία θα αποθηκευτούν τα δεδομένα.
Έπειτα φορτώνουμε στον RAX την τιμή μηδέν (αριθμός system call), ενώ στον RDI φορτώνουμε επίσης την τιμή μηδέν (που αντιστοιχεί στην standard input). Στον RSI φορτώνουμε τη διεύθυνση της RAM όπου βρίσκεται ο δεσμευμένος μας χώρος για τη μεταβλητή που ορίσαμε πριν, ενώ στον RDX φορτώνουμε τον αριθμό των bytes που ζητήσαμε να δεσμευτούν.  Τέλος χρησιμοποιούμε την εντολή syscall ώστε να κληθεί ο πυρήνας του λειτουργικού συστήματος.
Θεωρώ σκόπιμο να ορίσω κατ' ευθείαν μία μακροεντολή για το συγκεκριμένο system call που θα την ονομάσω read και θα δέχεται δύο παραμέτρους. Η πρώτη θα είναι το όνομα της μεταβλητής όπου αποθηκεύονται τα δεδομένα που θα διαβάσουμε και η δεύτερη το μέγιστο μήκος των δεδομένων σε bytes.

%macro read 2
    mov rax,0
    mov rdi,0
    mov rsi,%1
    mov rdx,%2
    syscall
%endmacro
	

Κλείνοντας ας επιχειρήσουμε να φτιάξουμε ένα ολοκληρωμένο πρόγραμμα με όσα είπαμε σήμερα. Το πρόγραμμά μας απλά θα ζητάει το όνομα του χρήστη και μετά θα τυπώνει ένα μήνυμα χαιρετισμού, το οποίο θα περιέχει το όνομα που δόθηκε.  Με τα όσα έχουμε πει ως εδώ, πιστεύω ότι πλέον ο κώδικας είναι απολύτως κατανοητός. Το πρόγραμμα μπορεί να μεταγλωττιστεί και να εκτελεστεί σύμφωνα με τα όσα είπαμε την προηγούμενη φορά.

%macro print 2
    mov rax,1
    mov rdi,1
    mov rsi,%1
    mov rdx,%2
    syscall
%endmacro
%macro read 2
    mov rax,0
    mov rdi,0
    mov rsi,%1
    mov rdx,%2
    syscall
%endmacro
%macro exit 0
    mov rax,60
    xor rdi,rdi
    syscall
%endmacro
section .data
    input_msg: db "Πώς σε λένε ; ", 10
    input_msglen equ $-input_msg
           output_msg: db " Τα πας πολύ καλά με την Assembly, φίλε "
           output_msglen equ $-output_msg
section .bss
    name resb 40
    
section .text
	global _start
    _start:
    print input_msg, input_msglen
        read name,40
    print output_msg,output_msglen
    print name,40
    exit
	

Στο σημείο αυτό θα ήθελα να ευχαριστήσω για ακόμα μια φορά όλους όσοι διάβασαν ένα ακόμα κείμενό μου ως το τέλος. Βασικός στόχος μου με τα κείμενα αυτά είναι να κάνω τα μέλη του forum να αγαπήσουν ξανά την Assembly. Πώς να το κάνουμε, αυτό το έχω πάρει «πατριωτικά» και με ευχαριστεί πάρα πολύ, ειδικά όταν ο βαθμός ανταπόκρισής σας είναι μεγάλος, όπως συνέβη την πρώτη φορά.

Πολλές ευχές σε όλους,

Ο Άσπρος Γάτος

Επεξ/σία από White_Cat
  • Like 8
  • Thanks 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...