ΔΙΔΑΚΤΙΚO BOHΘHMA ΓΙΑ ΤΟ ΘΕΩΡΗΤΙΚΟ ΜΕΡΟΣ ΤΟΥ ΜΑΘΗΜΑΤΟΣ TEI ΠΕΙΡΑΙΑ ΤΜΗΜΑ ΑΥΤΟΜΑΤΙΣΜΟΥ ΔΙΔΑΚΤΙΚO BOHΘHMA ΓΙΑ ΤΟ ΘΕΩΡΗΤΙΚΟ ΜΕΡΟΣ ΤΟΥ ΜΑΘΗΜΑΤΟΣ ΔΟΜΗ ΚΑΙ ΛΕΙΤΟΥΡΓΙΑ ΜΙΚΡΟΫΠΟΛΟΓΙΣΤΩΝ Χ. ΚΑΡΑΪΣΚΟΣ
ΠΕΡΙΕΧΟΜΕΝΑ Κεφάλαιο 0: Εισαγωγή Κεφάλαιο 1: Η γλώσσα του Ζ80 Κεφάλαιο 2: Το σύστημα διακοπών του Ζ80 Κεφάλαιο 3: Το προσαρμοστικό ΡΙΟ
ΔΙΔΑΚΤΙΚΕΣ ΕΝΟΤΗΤΕΣ ΤΟΥ ΜΑΘΗΜΑΤΟΣ Ενότητα 1: Εισαγωγικές έννοιες Ενότητα 2: Η εσωτερική δομή του Z80 Ενότητα 3: Εντολές φόρτωσης 8 και 16 bit Ενότητα 4: Εντολές αριθμητικών και λογικών πράξεων 8 bit Ενότητα 5: Εντολές άλματος και εντολές κλήσης ρουτινών Ενότητα 6: Εφαρμογές Ενότητα 7: Εντολές περιστροφής και ολίσθησης και εντολές χειρισμού bits Ενότητα 8: Λοιπές εντολές της γλώσσας του Z80
Ενότητα 9: Εφαρμογές Ενότητα 10: Διακοπές του Ζ80 αποκρύψιμες και μη Ενότητα 11: Εφαρμογές Ενότητα 12: Το προσαρμοστικό ΡΙΟ και η λειτουργία του με απενεργοποιημένες τις διακοπές Ενότητα 13: Διακοπές από πόρτα του ΡΙΟ σε mode 0 και 1 Ενότητα 14: Διακοπές από πόρτα του ΡΙΟ σε mode 3 Ενότητα 15: Εφαρμογές
Κεφάλαιο 0: Εισαγωγή Α. Γενικά Ζ80 = Μικροεπεξεργαστής (microprocessor – μP) ή κεντρική μονάδα επεξεργασίας (central processing unit – CPU) Ζ80 + Μνήμη ROM + Μνήμη RAM + Προσαρμοστικά (ΡΙΟ, CTC κ.λπ.) στα οποία είναι συνδεμένες περιφερειακές μονάδες (πληκτρολόγιο, οθόνη, αισθητήρια κ.λπ.) = Μικροϋπολογιστικό Σύστημα. Η σύνδεση των στοιχείων του μικροϋπολογιστικού συστήματος μεταξύ τους γίνεται μέσω του διαύλου δεδομένων (data bus), του διαύλου διευθύνσεων (address bus) και του διαύλου ελέγχου (control bus). Χρήση του Ζ80 κυρίως για υλοποίηση διατάξεων ειδικού σκοπού (υλοποίηση εφαρμογών ενσωματωμένων σε μεγαλύτερα συστήματα). Πρόγραμμα: Γράφεται στη γλώσσα μηχανής του Ζ80 (σε μορφή διαδοχής οκταψήφιων δυαδικών αριθμών – bytes) και αποθηκεύεται σε μια περιοχή της ROM. Το ίδιο πρόγραμμα γράφεται και στην πιο κατανοητή στον άνθρωπο συμβολική γλώσσα (assembly language). H αντιστοίχιση των εντολών γλώσσας μηχανής και συμβολικής γλώσσας είναι μία προς μία.
Ο Ζ80 διαθέτει 40 ακροδέκτες από τους οποίους: 8 είναι ακροδέκτες δεδομένων (σύνδεση με το data bus) 16 είναι ακροδέκτες διευθύνσεων (σύνδεση με το address bus) 6 (οι ακροδέκτες Μ1, MREQ, IORQ, RD και WR) αφορούν στον έλεγχο του συστήματος 5 (οι HALT, WAIT, INT NMI και RESET) αφορούν στον έλεγχο της CPU 2 (oι BUSREQ και BUSACK) αφορούν στον έλεγχο των διαύλων 2 (oι +5 Volt και GND) είναι οι ακροδέκτες τροφοδοσίας με τάση και 1 (ο CLK) είναι ο ακροδέκτης στον οποίο οδηγείται η παλμοσειρά που δημιουργεί εξωτερικό ρολόϊ Επανάληψη γνωστών εννοιών: Με 8 bits γράφονται 256 δυαδικοί αριθμοί, οι 00000000, 00000001, 00000010, …, 11111111. Για να βρούμε την τιμή ενός δυαδικού αριθμού στο δεκαδικό σύστημα πολλαπλασιάζουμε καθένα bit του επί την αντίστοιχη δύναμη του 2 και προσθέτουμε τα αποτελέσματα. Π. χ. 10101101=1×27+0×26+1×25+0×24+1×23+ 1×22+0×21+1×20=128+32+8+4+1=173. Με 4 bits γράφονται 16 αριθμοί, οι 0000=0, 0001=1, 0010=2, 0011=3, 0100=4, 0101=5, 0110=6, 0111=7, 1000=8, 1001=9, 1010=10=Α, 1011=11=Β, 1100=12=C, 1101=13=D, 1110=14=E και 1111=15=F.
Ένας αριθμός του δεκαεξαδικού συστήματος μετατρέπεται στο δεκαδικό σύστημα πολλαπλασιάζοντας τα ψηφία του επί τις αντίστοιχες δυνάμεις του 16 και προσθέτοντας τα γινόμενα. Π. χ. 3ΑF= 3×162+A×161 +F×160=943. Για να πάμε από το δεκαδικό σύστημα στο δυαδικό κάνουμε διαδοχικές διαιρέσεις δια 2 και γράφουμε τα υπόλοιπα με ανάστροφη σειρά. Αντίστοιχα, για να πάμε από το δεκαδικό στο δεκαεξαδικό σύστημα οι διαιρέσεις γίνονται δια 16. Ένας αριθμός δηλώνεται ότι είναι του δυαδικού συστήματος αν ακολουθείται από το σύμβολο Β και του δεκαεξαδικού συστήματος αν ακολουθείται από το σύμβολο Η. Π. χ. 10111010Β, 7Ε5Η κ.λπ. Πάμε απ’ ευθείας από το δυαδικό στο δεκαεξαδικό σύστημα αν ομαδοποιήσουμε τα bits του αριθμού σε ομάδες των 4 bits και για κάθε ομάδα γράψουμε το αντίστοιχο δεκαεξαδικό ψηφίο. Π. χ. 10010111Β=97Η αφού 1001=9 και 0111=7. Αντίστροφα, για να πάμε απ’ ευθείας από το δεκαεξαδικό σύστημα στο δυαδικό γράφουμε για κάθε δεκαεξαδικό ψηφίο την αντίστοιχη τετράδα bits. Π. χ. Α4ΕΗ=1010010011110Β αφού Α=1010, 4=0100 και Ε=1110. Έτσι, ένας 8-bit αριθμός = 1byte γράφεται ως διψήφιος αριθμός του δεκαεξαδικού συστήματος κι ένας 16-bit αριθμός = 2 bytes γράφεται ως τετραψήφιος αριθμός του δεκαεξαδικού συστήματος.
Ο Ζ80 χειρίζεται 8-bit δεδομένα και 16-bit διευθύνσεις μνήμης Ο Ζ80 χειρίζεται 8-bit δεδομένα και 16-bit διευθύνσεις μνήμης. Παραδείγματα δεδομένων που χειρίζεται ο Ζ80: 72Η, 00Η, ACH, FFH κ.λπ. Παραδείγματα διευθύνσεων που χειρίζεται ο Ζ80: Α936Η, 0000Η, 1234H, FFFFH κ.λπ. Νοητά, μια μνήμη με την οποία συνδέεται ο Ζ80 αποτελείται από μια σειρά από θέσεις αποθήκευσης δεδομένων του ενός byte τοποθετημένες η μια κάτω από την άλλη. Η πάνω-πάνω θέση έχει διεύθυνση 0000Η και η κάτω-κάτω διεύθυνση FFFFH. Κάποιο μπλοκ διευθύνσεων μπορεί να υλοποιείται από μνήμη ROM, κάποιο από μνήμη RAM και κάποιο να μην υλοποιείται από chip μνήμης. Μια εντολή της γλώσσας μηχανής του Ζ80 αποτελείται από ένα ή δύο bytes που συνιστούν τον κώδικα λειτουργίας της (operation code – opcode) και πιθανώς ένα ή δύο ακόμα bytes που συνιστούν τον τελεστή της (operand). O τελεστής είναι δεδομένο ή διεύθυνση μνήμης. Το πρόγραμμα γλώσσας μηχανής αποτελείται από τα bytes των εντολών του αποθηκευμένα σε μια περιοχή της μνήμης ROM το ένα μετά το άλλο. Για να εκτελέσει μια εντολή του προγράμματος ο Ζ80 πρώτα διαβάζει από τη μνήμη ένα-ένα τα bytes που την αποτελούν. Αυτό αποτελεί τη φάση ανάκλησης (fetching) της εντολής. Στη συνέχεια, εκτελεί την εντολή, δηλ. κάνει ό,τι αυτή υπαγορεύει. Αυτό αποτελεί τη φάση εκτέλεσης (execution) της εντολής. Οι εντολές ενός προγράμματος ανακαλούνται από τη μνήμη και εκτελούνται η μια μετά την άλλη.
Β. Η εσωτερική δομή του Ζ80 Ο Ζ80 διαθέτει κυκλώματα εκτέλεσης αριθμητικών πράξεων (παράλληλος αθροιστής 8-bit δυαδικών αριθμών), λογικών πράξεων κ.λπ. Επίσης, διαθέτει και καταχωρητές με δυνατότητα ολίσθησης του περιεχομένου τους. Μερικοί καταχωρητές του Ζ80 επηρεάζονται άμεσα από τις εντολές της γλώσσας του Ζ80. Αυτοί είναι: Α) 8-bit καταχωρητές: Κύριο σετ: A (Accumulator = Συσσωρευτής), F, B, C, D, E, H, L, I και R και εναλλακτικό σετ: Α’, F’, Β’, C’, D’, E’, H’ και L’. B) 16-bit καταχωρητές: PC (Program Counter = Mετρητής Προγράμματος), SP (Stack Pointer = δείκτης Σωρού), IX, IY και τα ζευγάρια 8-bit καταχωρητών BC, DE, HL και AF. O καταχωρητής F ονομάζεται καταχωρητής σημαιών (flag register). Τα bits αυτού δείχνουν κάτι σχετικά με το αποτέλεσμα που προκύπτει από την εκτέλεση μιας εντολής. Το bit υπ’ αριθ. 7 αυτού συμβολίζεται με S (Sign flag), το bit υπ’ αριθ. 6 συμβολίζεται με Ζ (Zero flag), το bit υπ’ αριθ. 4 συμβολίζεται με H (Half carry flag), το bit υπ’ αριθ. 2 συμβολίζεται με P/V (Parity/oVerflow flag), το bit υπ’ αριθ. 1 συμβολίζεται με N (Negation flag) και το bit υπ’ αριθ. 0 συμβολίζεται με C (Carry flag). Τα υπόλοιπα δύο bits του καταχωρητή F είναι αδιάφορα. O μετρητής προγράμματος PC περιέχει πάντα τη διεύθυνση της εντολής που θα εκτελεστεί αμέσως μετά την τρέχουσα εντολή.
Κεφάλαιο 1: Η γλώσσα του Ζ80 Οι εντολές της γλώσσας του Ζ80 ομαδοποιούνται σε 11 ομάδες ομοειδών εντολών, τις: Εντολές φόρτωσης 8-bit αριθμών (8-bit load group) Εντολές φόρτωσης 16-bit αριθμών (16-bit load group) Εντολές αριθμητικών και λογικών πράξεων μεταξύ 8-bit αριθμών (8-bit arithmetic and logical group) Εντολές αριθμητικών πράξεων μεταξύ 16-bit αριθμών (16-bit arithmetic group) Εντολές περιστροφής και ολίσθησης (rotate and shift group) Εντολές άλματος (jump group) Γενικού σκοπού αριθμητικές εντολές και εντολές ελέγχου της CPU (General purpose arithmetic and CPU control group) Εντολές κλήσης ρουτινών (call and return group) Εντολές εισόδου/εξόδου (input and output group) Εντολές χειρισμού bits (bit set, reset and test group) Εντολές ανταλλαγής και μεταφοράς και αναζήτησης μπλοκ δεδομένων (exchange group and block transfer and search group) Ξεκινάμε περιγράφοντας στη συμβολική γλώσσα (assembly language) τις πιο συχνά χρησιμοποιούμενες από τις εντολές αυτές.
Α) Φόρτωση 8-bit δεδομένων Φόρτωση από 8-bit καταχωρητή σε 8-bit καταχωρητή. Μορφή εντολής: LD r,r’. Το περιεχόμενο του καταχωρητή r’ φορτώνεται (αντιγράφεται) στον καταχωρητή r. Είναι r, r’=A, B, C, D, E, H ή L. Π. χ. η εντολή LD E,C φορτώνει (αντιγράφει) το περιεχόμενο του καταχωρητή C στον καταχωρητή Ε. Φόρτωση 8-bit δεδομένου n σε 8-bit καταχωρητή r. Μορφή εντολής: LD r,n. Π. χ. η εντολή LD A,35H φορτώνει το δεδομένο 35Η στο συσσωρευτή Α. Φόρτωση 8-bit δεδομένου σε θέση μνήμης. α) Μορφή εντολής: LD (HL),r. Φορτώνει το περιεχόμενο του καταχωρητή r στη θέση μνήμης της οποίας η διεύθυνση είναι γραμμένη στον καταχωρητή HL. β) Μορφή εντολής: LD (HL),n. Φορτώνει το 8-bit δεδομένο n στη θέση μνήμης της οποίας η διεύθυνση είναι γραμμένη στον καταχωρητή HL. γ) Μορφή εντολής: LD (IX+d),r. Φορτώνει το περιεχόμενο του καταχωρητή r στη θέση μνήμης της οποίας η διεύθυνση προκύπτει από την πρόσθεση του 16-bit περιεχομένου του καταχωρητή ΙΧ με τον 8-bit προσημασμένο αριθμό d. Το d παίρνει τιμές από –128 μέχρι +127. Στη γλώσσα μηχανής το d γράφεται χρησιμοποιώντας για τους αρνητικούς αριθμούς την παράσταση συμπληρώματος ως προς 2. Στη γλώσσα assembly το d μπορεί να γραφεί και ως ένας συνηθισμένος προσημασμένος αριθμός του δεκαδικού συστήματος. δ) Άλλες μορφές: LD (IX+d),n LD (IY+d),r και LD (IY+d),n που είναι προφανές τι κάνουν.
Φόρτωση 8-bit δεδομένου από θέση μνήμης σε 8-bit καταχωρητή Φόρτωση 8-bit δεδομένου από θέση μνήμης σε 8-bit καταχωρητή. α) Μορφή εντολής: LD r,(HL). Φορτώνει στον καταχωρητή r το περιεχόμενο της θέσης μνήμης της οποίας η διεύθυνση είναι γραμμένη στον HL. β) Άλλες μορφές: LD r,(IX+d) και LD r,(IY+d) που είναι προφανές τι κάνουν. Ειδικά και μόνο για το συσσωρευτή Α μπορεί να χρησιμοποιηθεί σε παρένθεση άμεσα η διεύθυνση της θέσης μνήμης. Π. χ. LD (A349H),A ή LD A,(CEF2H). Επίσης, ειδικά για τον Α υπάρχουν και οι εντολές που προκύπτον από την LD A,(HL) ή την LD (HL),A αν στη θέση του HL χρησιμοποιηθεί ο BC ή o DE. Τέλος υπάρχουν και οι εντολές LD A,I, LD I,A, LD R,A και LD A,R. Παράδειγμα: Πρόγραμμα στη γλώσσα του Ζ80 που ανταλλάσσει τα περιεχόμενα των καταχωρητών Β και C. Προφανώς θα πρέπει να χρησιμοποιηθεί και βοηθητικός καταχωρητής, εδώ ο Α. Τα προγράμματά μας θα τελειώνουν με την εντολή JP $ (άλμα στη θέση όπου βρίσκεται η εντολή αυτή, που πρακτικά σημαίνει σταμάτημα) 0100Η: LD A,C LD C,B LD B,A JP $ To 100Η: σημαίνει ότι το μεταφρασμένο σε γλώσσα μηχανής πρόγραμμα βρίσκεται αποθηκευμένο από τη θέση μνήμης 100Η και κάτω.
Β) Φόρτωση 16-bit δεδομένων Φόρτωση 16-bit δεδομένου σε 16-bit καταχωρητή. Μορφή εντολής: LD ss,nn. Φορτώνει στον 16-bit καταχωρητή ss το 16-bit δεδομένο nn. Είναι ss=BC, DE, HL, SP, IX ή ΙΥ. Π. χ. η εντολή LD HL,35AEH φορτώνει στον καταχωρητή HL το δεδομένο 35ΑΕΗ. Φόρτωση από 16-bit καταχωρητή σε 16-bit καταχωρητή. Υπάρχουν μόνο οι εντολές LD SP,HL, LD SP,IX και LD SP,IY που φορτώνουν τον SP με το περιεχόμενο του HL, του ΙΧ ή του ΙΥ, αντίστοιχα. Ως σωρός χρησιμοποιείται από τον Ζ80 το τμήμα της μνήμης από τη διεύθυνση που είναι γραμμένη στον SP (κορυφή του σωρού) μέχρι την τελευταία διεύθυνση της μνήμης, την FFFFH. Εντολές σπρωξίματος (αποθήκευσης) στο σωρό: PUSH ss, όπου ss=BC, DE, HL, AF, IX ή ΙΥ. Τα δύο bytes του ss πάνε πάνω από την κορυφή του σωρού με το πιο σημαντικό byte του 16-bit δεδομένου από κάτω και το λιγότερο σημαντικό byte από πάνω. Ο δείκτης σωρού SP μειώνεται κατά 2 για να δείχνει τη νέα κορυφή του σωρού. Εντολές τραβήγματος (ανάγνωσης) από το σωρό: POP ss, όπου ss=BC, DE, HL, AF, IX ή ΙΥ. Τα δύο πάνω-πάνω bytes του σωρού διαβάζονται και πηγαίνουν ως 16-bit δεδομένο στον καταχωρητή ss. Ο δείκτης σωρού αυξάνεται κατά 2. Αποθήκευση στη μνήμη του περιεχομένου ενός 16-bit καταχωρητή: LD (nn),qq, όπου qq=BC, DE, HL, SP, IX ή ΙΥ. Το χαμηλής τάξης byte του qq πάει στη θέση μνήμης που έχει διεύθυνση nn και το ψηλής τάξης byte του qq πάει στη θέση μνήμης που έχει διεύθυνση nn+1 (ακριβώς κάτω από το χαμηλής τάξης byte). Το παράξενο είναι ότι η αντίστοιχη εντολή για 8-bit δεδομένα LD (nn),r υπάρχει μόνο για r=A. ·
Φόρτωση 16-bit καταχωρητή με δεδομένα δύο διαδοχικών θέσεων της μνήμης: LD qq,(nn), όπου qq=BC, DE, HL, SP, IX ή ΙΥ. Στο χαμηλής τάξης τμήμα του qq πάει το περιεχόμενο της θέσης μνήμης που έχει διεύθυνση nn και στο ψηλής τάξης τμήμα του qq πάει το περιεχόμενο της θέσης μνήμης που έχει διεύθυνση nn+1. Πάλι, η αντίστοιχη εντολή για 8-bit δεδομένα LD r,(nn) υπάρχει μόνο για r=A. Παράδειγμα: Πρόγραμμα που θα δίνει στον δείκτη σωρού SP τιμή Β007Η και στους καταχωρητές BC και ΙΧ τιμές 1234Η και 5678Η, αντίστοιχα. Ύστερα, το πρόγραμμα θα σπρώχνει τον BC στο σωρό και από κει θα τραβάει το δεδομένο που έσπρωξε και θα το πηγαίνει στον ΙΥ. Με τον τρόπο αυτό «εξομοιώνουμε» την ανύπαρκτη εντολή LD IY,BC. Στη συνέχεια, το πρόγραμμα θα ανταλλάσσει τα περιεχόμενα των καταχωρητών BC και ΙΧ. 1000Η: LD SP, B007H LD BC,1234H LD IX,5678H PUSH BC POP IY PUSH IX POP BC POP IX JP $ Αν θέλαμε, με τις εντολές ΡΟΡ, στους καταχωρητές BC και ΙΧ να επέστρεφαν από το σωρό τα δικά τους δεδομένα θα έπρεπε οι εντολές να είναι με την εξής σειρά: ΡΟΡ ΙΧ και POP BC.
Γ) Εντολές αριθμητικών και λογικών πράξεων μεταξύ 8-bit δεδομένων Η εντολή ADD A,s, όπου s=r, n, (HL), (IX+d) ή (IY+d), προσθέτει στον συσσωρευτή Α το 8-bit δεδομένο που προσδιορίζει ο s και αποθηκεύει το 8-bit αποτέλεσμα στον Α. Παράλληλα, ενημερώνει τα flags όπως πρέπει. Δηλ. στο flag C βάζει ως τιμή το τελικό κρατούμενο της πρόσθεσης, στο flag Z δίνει τιμή 1 όταν και μόνον όταν το 8-bit αποτέλεσμα της πρόσθεσης είναι 0, στο flag S δίνει ως τιμή το MSB του 8-bit αποτελέσματος, στο flag Η δίνει ως τιμή το ενδιάμεσο κρατούμενο που δημιουργείται όταν η πρόσθεση βρίσκεται στη μέση της (δηλ. έχουν προστεθεί τα ζευγάρια bits χαμηλής τάξης), στο flag N δίνει τιμή 0 και στο flag P/V δίνει τιμή 1 όταν και μόνον όταν το αποτέλεσμα της πρόσθεσης, θεωρούμενο ως προσημασμένος δυαδικός αριθμός, βρίσκεται έξω από το διάστημα (-128, +127). Για 8-bit δεδομένα, η εντολή ADD μόνο με τον Α συντάσσεται. Στις εντολές που ακολουθούν το s παριστάνει ό,τι κι εδώ. Η εντολή ADC A,s, (ADd with Carry) προσθέτει στον Α τον αριθμό που προσδιορίζει ο s καθώς και την τιμή (0 ή 1) που είχε το flag C πριν την εκτέλεση της εντολής ADC. Ενημερώνονται τα flags όπως και με την ADD. Η εντολή SUB s (SUBtract) αφαιρεί από τον Α τον 8-bit αριθμό που προσδιορίζει ο s. Η εντολή δίνει το ίδιο αποτέλεσμα που βρίσκουμε εμείς κάνοντας την αφαίρεση με το χέρι. Θεωρώντας τους αριθμούς απροσήμαστους, η τιμή που παίρνει το flag C είναι το τελικό δανεικό της αφαίρεσης. (Είναι χρεωστικό, δηλ. έχει βαρύτητα –28). Η εντολή SBC A,s (SuBtract with Carry) αφαιρεί από τον Α τον αριθμό που προσδιορίζει ο s και την τιμή (0 ή 1) που είχε το flag C πριν την εκτέλεση της εντολής SBC.
Η εντολή AND s κάνει bit προς bit τη λογική πράξη AND μεταξύ του περιεχομένου του καταχωρητή Α και του αριθμού που προσδιορίζει ο s. Το αποτέλεσμα μένει ως νέα τιμή στον Α. Θυμηθείτε τις ταυτότητες της άλγεβρας Boole x0=0 και x1=x. Η εντολή ΟR s κάνει bit προς bit τη λογική πράξη OR μεταξύ του περιεχομένου του καταχωρητή Α και του αριθμού που προσδιορίζει ο s. Το αποτέλεσμα μένει ως νέα τιμή στον Α. Θυμηθείτε τις ταυτότητες της άλγεβρας Boole x+0=x και x+1=1. Η εντολή ΧΟR s κάνει bit προς bit τη λογική πράξη XOR μεταξύ του περιεχομένου του καταχωρητή Α και του αριθμού που προσδιορίζει ο s. Το αποτέλεσμα μένει ως νέα τιμή στον Α. Θυμηθείτε τις ταυτότητες της άλγεβρας Boole x0=x και x1=x’ (συμπλήρωμα του x). H εντολή INC q, (INCrement=αυξάνω) όπου q=r, (HL), (IX+d) ή (IY+d) αυξάνει κατά 1 το περιεχόμενο του καταχωρητή r ή της θέσης μνήμης που προσδιορίζει το (HL), το (IX+d) ή το (IY+d). Αντίστοιχα, η DEC q (DECrement=μειώνω) κάνει μείωση κατά 1. Τελειώνουμε παρουσιάζοντας την εντολή σύγκρισης CP (ComPare=συγκρίνω). Σύνταξη: CP s, όπου s=r, n, (HL), (IX+d) ή (IY+d). Αυτή συγκρίνει το περιεχόμενο του Α με τον αριθμό που προσδιορίζει ο s κι ενημερώνει τα flags. Η σύγκριση γίνεται εκτελώντας την αφαίρεση Α-s, χωρίς όμως το αποτέλεσμα να αποθηκεύεται στον Α. Μέσω των flags C και Ζ δίνεται η απάντηση τη σύγκρισης, αν οι συγκρινόμενοι αριθμοί συγκρίνονται ως απροσήμαστοι, και μέσω των flags S, P/V και Z δίνεται η απάντηση της σύγκρισης, αν οι συγκρινόμενοι αριθμοί συγκρίνονται ως προσημασμένοι, όπως δείχνουν οι παρακάτω πίνακες:
Αριθμοί απροσήμαστοι Αριθμοί προσημασμένοι Ενημέρωση των flags από την εντολή σύγκρισης CP s Αριθμοί απροσήμαστοι Συνθήκη Flag Z Flag C A>s A=s 1 A<s Αριθμοί προσημασμένοι Συνθήκη Flag Z Flag S Flag P/V Α=s 1 A>s A<s
Δ) Εντολές άλματος Η εντολή άλματος χωρίς συνθήκη είναι JP nn (κάνει άλμα στην εντολή που είναι αποθηκευμένη από τη διεύθυνση nn και κάτω). Αντί για 16-bit αριθμό nn μπορούμε να χρησιμοποιήσουμε κάποια ετικέτα για την εντολή προορισμού του άλματος, όπως LABEL, ΝΕΧΤ κ.λπ. Τότε η εντολή άλματος έχει τη μορφή JP LABEL. Οι εντολές άλματος υπό συνθήκη (εντολές διακλάδωσης) είναι: JP C, TOM (κάνει άλμα στην εντολή με ετικέτα ΤΟΜ αν το flag C έχει τιμή 1). JP ΝC, TOM (κάνει άλμα στην εντολή με ετικέτα ΤΟΜ αν το flag C έχει τιμή 0). JP Ζ, TOM (κάνει άλμα στην εντολή με ετικέτα ΤΟΜ αν το flag Ζ έχει τιμή 1, δηλ. αν η πιο πρόσφατη εντολή που επηρεάζει το flag Z έδωσε αποτέλεσμα ίσο με 0). JP ΝΖ, TOM (κάνει άλμα στην εντολή με ετικέτα ΤΟΜ αν το flag Ζ έχει τιμή 0, δηλ. αν η πιο πρόσφατη εντολή που επηρεάζει το flag Z έδωσε αποτέλεσμα διάφορο του 0). JP Μ, TOM (κάνει άλμα στην εντολή με ετικέτα ΤΟΜ αν το flag S έχει τιμή 1. M=minus=-). JP Ρ, TOM (κάνει άλμα στην εντολή με ετικέτα ΤΟΜ αν το flag S έχει τιμή 0. P=plus=+). JP PE, TOM (κάνει άλμα στην εντολή με ετικέτα ΤΟΜ αν το flag P/V έχει τιμή 1. PE=parity even=άρτια ισοτιμία που θα πούμε τι σημαίνει). JP PO, TOM (κάνει άλμα στην εντολή με ετικέτα ΤΟΜ αν το flag P/V έχει τιμή 0. PO=parity odd=περιττή ισοτιμία που θα πούμε τι σημαίνει). Αν δεν ισχύει μια συνθήκη το πρόγραμμα συνεχίζει με την αμέσως επόμενη εντολή. Υπάρχουν και εντολές σχετικού άλματος που γράφονται με JR, συντάσσονται όπως οι αντίστοιχες εντολές JP και κάνουν τα ίδια με τις JP. Μόνο που ο μηχανισμός υλοποίησής τους είναι διαφορετικός. Αν χρησιμοποιούμε ετικέτες, εμείς δεν καταλαβαίνουμε καμιά διαφορά. Διαφορές υπάρχουν στη γλώσσα μηχανής. Εντολές σχετικού άλματος υπό συνθήκη υπάρχουν μόνο αυτές που έχουν συνθήκη C, NC, Z και NΖ.
Παράδειγμα 1ο: Πρόγραμμα που συγκρίνει τα περιεχόμενα των καταχωρητών B και C και αποθηκεύει το μεγαλύτερο απ’ αυτά στον Β και το μικρότερο στον C. Οι αριθμοί θεωρούνται απροσήμαστοι. 200Η: LD A,B CP C JP NC, TELOS LD B,C LD C,A TELOS: JP $ Παράδειγμα 2ο: Πρόγραμμα που θα προσθέτει τα περιεχόμενα των καταχωρητών Β και C και θα αποθηκεύει το πλήρες αποτέλεσμα στον καταχωρητή DE. Θεωρήστε ότι οι χρησιμοποιούμενοι αριθμοί είναι απροσήμαστοι (δηλ. παίρνουν τιμές από 0 μέχρι 255). Το άθροισμα δύο τέτοιων αριθμών μπορεί να πάρει τιμές από 0 μέχρι 510, οπότε χρειάζεται 9 bits για να γραφεί. Το κρατούμενο εξόδου είναι το ένατο bit (έχει βαρύτητα 28) και πρέπει να μπει μπροστά από το 8-bit άθροισμα. Θα πετύχουμε αυτό που μας ζητάει η άσκηση αν μετά την πρόσθεση αποθηκεύσουμε τον Α στον Ε και δώσουμε στον D τιμή 1 αν και μόνον αν flag C=1. Αυτό το κάνει το πρόγραμμα: LD D,0 LD A,B ΑDD A,C JP NC, NEXT INC D NEXT: LD E,A JP $
Παράδειγμα 3ο: Πρόγραμμα που γεμίζει τις θέσεις του μπλοκ μνήμης Α000Η-Α012Η με το byte 25Η. Αυτό μπορεί να προγραμματιστεί ως μια διαδικασία 13Η επαναλήψεων. Το 13Η προκύπτει ως Α012Η-Α000Η+1, αφού τόσες είναι οι θέσεις μνήμης που θα γεμίσουν με 25H. Σε κάθε επανάληψη θα φορτώνεται το byte 25Η σε μια θέση του μπλοκ μνήμης. Θα χρειαστούμε ένα μετρητή. Συνήθως χρησιμοποιούμε τον καταχωρητή Β, που κάθε φορά θα δείχνει πόσες ακόμα επαναλήψεις μένουν να γίνουν. Φυσικά, θα ξεκινάει με Β=13Η και με κάθε επανάληψη θα μειώνεται κατά 1. Επίσης, θα χρησιμοποιήσουμε έναν από τους 16-bit καταχωρητές ΗL, ΙΧ και ΙΥ που θα κρατάει τη διεύθυνση της θέσης μνήμης, στην οποία θέλουμε κάθε φορά να φορτώσουμε το 25Η. Το πρόγραμμα είναι: LD B,13H LD HL,A000H LOOP: LD (HL),25H INC HL DEC B JP NZ,LOOP JP $ Παράδειγμα 4ο: Πρόγραμμα που υπολογίζει το πλήρες άθροισμα των περιεχομένων των θέσεων μνήμης Α010Η-Α019Η και αποθηκεύει το αποτέλεσμα στον καταχωρητή HL κι από κει στον ΙΧ. Προφανώς πρόκειται για ένα πρόγραμμα επαναληπτικής διαδικασίας (βρόχου). Ξεκινάμε μηδενίζοντας τους Α και Η και επαναληπτικά προσθέτουμε στον Α ένα-ένα τα δεδομένα του μπλοκ. Όταν βγαίνει κρατούμενο 1, θα αυξάνουμε κατά 1 τον Η. Στο τέλος θα φορτώνουμε τον Α στον L κ.λπ. Το πρόγραμμα ακολουθεί:
2000H: LD SP,FFFFH LD H,0 LD A,0 LD IX,A000H LD B,0AH LOOP: ADD A, (IX) JP NC, NEXT INC H NEXT: INC IX DEC B JP NZ, LOOP LD L,A PUSH HL POP IX JP $ Από τώρα και στο εξής θα θεωρούμε ότι η μνήμη RAM του μικροϋπολογιστικού μας συστήματος καλύπτει το τελευταίο μπλοκ του χάρτη μνήμης του Ζ80. Δηλ. ότι η τελευταία διεύθυνση της RAM είναι η διεύθυνση FFFFH. Στην αρχή του προγράμματός μας με την εντολή LD SP,FFFFH εξασφαλίζουμε η κορυφή του σωρού να βρίσκεται στη μνήμη RAM (κι όχι κατά λάθος στη μνήμη ROM ή σε διεύθυνση που δεν υλοποιείται από chip μνήμης) και μάλιστα στην τελευταία θέση της μνήμης RAM (δηλ. ο σωρός να είναι άδειος).
Κλείνουμε την παρουσίαση των εντολών άλματος αναφέροντας και τις υπόλοιπες εντολές της ομάδας αυτής. Η εντολή JP (HL) κάνει άλμα στην εντολή του προγράμματος που είναι αποθηκευμένη στη διεύθυνση που είναι γραμμένη στον HL. Ανάλογα συμβαίνουν για τις εντολές JP (IX) και JP (IY), στις οποίες οι εντολές προορισμού του άλματος έχουν διεύθυνση το περιεχόμενο του ΙΧ ή του ΙΥ αντίστοιχα. Η εντολή DJNZ ETIKETA μειώνει το περιεχόμενο του Β κατά 1 και αν αυτό δεν είναι 0 κάνει άλμα στην εντολή που έχει ετικέτα ΕΤΙΚΕΤΑ. Είναι συγκερασμός των εντολών DEC B και JP NZ, ETIKETA. Ενώ η DEC συντάσσεται με οποιονδήποτε καταχωρητή, η DJNZ μειώνει και ελέγχει για μηδενισμό αποκλειστικά και μόνο τον καταχωρητή Β.Η εντολή DJNZ είναι εντολή σχετικού άλματος.
Ε) Εντολές περιστροφής και ολίσθησης Η εντολή RRCA (Rotate Right Circularly A) κάνει δεξιά κυκλική περιστροφή του περιεχομένου του συσσωρευτή Α. Tα bits του Α πάνε όλα μια θέση δεξιά και το bit που βγαίνει από δεξιά (LSB) γυρίζει στην πιο αριστερή θέση (MSB) του Α και ταυτόχρονα πάει και στο flag C. H εντολή RLCA (Rotate Left Circularly A) κάνει αριστερή κυκλική περιστροφή του περιεχομένου του Α και το flag C δέχεται το MSB που βγαίνει από αριστερά και που επίσης γυρίζει και στην LSB (πιο δεξιά) θέση του Α. H εντολή RRA (Rotate Right A) κάνει (μη κυκλική) δεξιά περιστροφή του Α. Το περιεχόμενο του Α μαζί με το flag C συνιστούν μια ‘αλυσίδα’ από 9 bits που γυρίζει όλη μαζί μια θέση προς τα δεξιά. Η εντολή RLΑ (Rotate Left A) κάνει (μη κυκλική) περιστροφή του περιεχομένου του Α προς τα αριστερά, αντίστοιχη της RRA. Έχουμε τις εντολές RRC r, RLC r, RR r και RL r, όπου r=B, C, D, E, H, L ή A, που κάνουν τις αντίστοιχες περιστροφές στο περιεχόμενο του καταχωρητή r. Το flag C ενημερώνεται ανάλογα. Παρατηρήστε ότι υπάρχει η εντολή RRCA αλλά και η RRC A κ.λπ. Επίσης, έχουμε τις εντολές RRC (HL), RRC (IX+d) και RRC (IY+d) που κάνουν δεξιά κυκλική περιστροφή στο περιεχόμενο μιας θέσης της μνήμης και ενημέρωση του flag C. Για περιστροφή του περιεχομένου μιας θέσης μνήμης υπάρχουν και αντίστοιχες εντολές RLC, RR και RL. Η εντολή SLA (Shift Left Arithmetic) s, όπου s=r, (HL), (IX+d) ή (ΙΥ+d) κάνει ολίσθηση του δεδομένου που προσδιορίζει ο s μια θέση προς τα αριστερά. Το bit που βγαίνει από αριστερά πάει στο flag C ενώ στη θέση του LSB πάει 0.
Η εντολή SRL s (Shift Right Logic) κάνει αντίστοιχη δεξιά λογική ολίσθηση. Το bit που βγαίνει από δεξιά πάει στο flag C ενώ στη θέση του ΜSB πάει 0. Τέλος, η εντολή SRΑ s (Shift Right Arithmetic) κάνει δεξιά αριθμητική ολίσθηση. Λειτουργεί όπως η SRL αλλά στη θέση του MSB παραμένει η τιμή του MSB (αυτό ονομάζεται επέκταση προσήμου). Η ομάδα εντολών περιστροφής και ολίσθησης κλείνει με τις εντολές RLD και RRD, οι οποίες βρίσκουν ειδικές μόνο εφαρμογές και δεν τις παρουσιάζουμε εδώ. Παράδειγμα: Πρόγραμμα που μετράει τον αριθμό των 1 του περιεχομένου του συσσωρευτή Α και αποθηκεύει το αποτέλεσμα στον καταχωρητή Ε. Μηδενίζουμε πρώτα τον Ε και ύστερα, με 8 κυκλικές περιστροφές του Α, περνάμε ένα-ένα τα bits αυτού μέσα από το flag C. Αν προκύπτει flag C=1 αυξάνουμε κατά 1 τον Ε και πάμε στην επόμενη περιστροφή. Αν προκύπτει flag C=0 πάμε στην επόμενη περιστροφή. 0100H: LD E,0 LD B,8 LOOP: RRCA JP NC, NEXT INC E NEXT: DEC B JP NZ, LOOP JP $
ΣΤ) Εντολές κλήσης ρουτινών Ρουτίνα είναι ένα αυτοδύναμο κομμάτι προγράμματος που καλείται όσες φορές θέλουμε από άλλο πρόγραμμα (το κύριο πρόγραμμα). Μια ρουτίνα που η πρώτη εντολή της βρίσκεται αποθηκευμένη στη θέση μνήμης που έχει διεύθυνση nn καλείται από ένα πρόγραμμα με την εντολή CALL nn. Πολύ συχνά, στη συμβολική γλώσσα, στην πρώτη εντολή μιας ρουτίνας αντιστοιχίζουμε-γράφουμε μια ετικέτα΄και το κυρίως πρόγραμμα καλεί τη ρουτίνα με την ετικέτα της. Π. χ. CALL DELAY. Καθοδηγούμε τον συμβολομεταφραστή ώστε αυτός, κατά τη φάση της μετάφρασης, να αντιστοιχίσει στην ετικέτα την πραγματική διεύθυνση μνήμης και να χρησιμοποιήσει αυτή στη γλώσσα μηχανής. Με την ολοκλήρωση της εκτέλεσης της ρουτίνας πραγματοποιείται επιστροφή στο κυρίως πρόγραμμα ακριβώς κάτω από την εντολή CALL που κάλεσε τη ρουτίνα. Παράδειγμα: Θα γράψουμε πρόγραμμα που «αναβοσβήνει» συνεχώς όλα μαζί τα bits του καταχωρητή Ε με συχνότητα 1 Hz. Ένα bit είναι «αναμμένο» όταν του έχουμε δώσει τιμή 1 και είναι «σβηστό» όταν του έχουμε δώσει τιμή 0. Περίοδος ανναβοσβησίματος είναι το 1/(1 Hz)=1 sec, οπότε κάθε άναμμα των bits θα διαρκεί 0,5 sec και κάθε σβήσιμο θα διαρκεί 0,5 sec. Έστω ότι γράψαμε ρουτίνα με ετικέτα ΚΑΤΗΥ που, όταν κληθεί, προκαλεί καθυστέρηση 0,5 sec. Το κυρίως πρόγραμμά μας είναι: 1000H: LD SP, FFFFH LOOP: LD E,FFH CALL KATHY LD E,00H JP LOOP
Τη στιγμή που καλείται μια ρουτίνα, ο μετρητής προγράμματος περιέχει τη διεύθυνση της επόμενης εντολής, που είναι και η εντολή στην οποία θα γίνει επιστροφή από τη ρουτίνα. Πριν κάνει άλμα στη ρουτίνα, ο Ζ80 αποθηκεύει στο σωρό το περιεχόμενο του PC για να το θυμάται. Η ρουτίνα τελειώνει με εντολή επιστροφής RET. Κατά την εκτέλεση της εντολής επιστροφής ο Ζ80 ανακαλεί από το σωρό τα δύο πάνω-πάνω bytes και τα πάει στον PC, εξασφαλίζοντας έτσι τη σωστή επιστροφή στο κυρίως πρόγραμμα. Για σωστή επιστροφή στο κυρίως πρόγραμμα, η ρουτίνα ή δεν θα πρέπει να χρησιμοποιεί το σωρό ή όσες εντολές PUSH περιέχει άλλες τόσες εντολές ΡΟΡ να περιέχει επίσης. Ας δούμε τώρα την επόμενη ρουτίνα καθυστέρησης DELAY: DELAY: PUSH BC 10Τ LD B,F0H 7Τ LOOPD: NOP 4Τ NOP 4Τ DEC B 4Τ JP NZ,LOOPD 10Τ POP BC 10Τ RET 10Τ Δίπλα σε κάθε εντολή της ρουτίνας έχουμε γράψει τον αριθμό περιόδων ρολογιού που απαιτεί η εντολή για την εκτέλεσή της (βρίσκονται από πίνακες). Η εντολή ΝΟΡ (No OPeration) δεν κάνει τίποτα. Απλώς περνάει ο χρόνος των 4 περιόδων ρολογιού Τ. Ο βρόχος που αρχίζει με την ετικέτα LOOPD εκτελείται F0H=240 φορές. Η καθυστέρηση που προκαλεί η DELAY είναι 10+7+(4+4+4+4+10)F0H+10+10 = 37+26240 = 6277T. Mε γνωστή τη συχνότητα ρολογιού του μικροϋπολογιστικού συστήματος, υπολογίζουμε την περίοδο Τ αυτής σε sec κι
ύστερα την καθυστέρηση της ρουτίνας σε sec. Π. χ ύστερα την καθυστέρηση της ρουτίνας σε sec. Π. χ. με συχνότητα ρολογιού 1 MHz έχουμε Τ=1 μsec και η καθυστέρηση είναι 6277 μsec, δηλ. λίγα msec. Για να πετύχουμε καθυστέρηση 0,5 sec μπορούμε να γράψουμε μια άλλη ρουτίνα με ετικέτα KATHY που θα καλεί την DELAY 500000/627780(=50H) φορές. Τώρα, η καθυστέρηση που προκαλεί η εκτέλεση των επί μέρους εντολών της KATHY είναι αμελητέα, σε σύγκριση με την καθυστέρηση που προκαλεί η DELAY εκτελούμενη 80 φορές, και δεν τη λαμβάνουμε υπ΄ όψη (ειδικά όταν δεν χρειαζόμαστε καθυστέρηση ακριβώς 0,5 sec). Η ρουτίνα ΚΑΤΗΥ ακολουθεί: ΚΑΤΗY: PUSH BC LD B,50H LOOPΚ: CALL DELAY DEC B JP NZ,LOOPK POP BC RET Παρατηρούμε ότι έχουμε μια ρουτίνα, την ΚΑΤΗΥ, να καλεί μια άλλη, την DELAY. Επίσης, κ;ι οι δύο ρουτίνες χρησιμοποιούν ως μετρητή επαναλήψεων τον καταχωρητή Β. Δεν είναι απαραίτητο ούτε και κακό. Οι εντολές PUSH και ΡΟΡ μπαίνουν ώστε οι ρουτίνες να μη χαλάνε πιθανές χρήσιμες για τα καλούντα προγράμματα τιμές του καταχωρητή Β.
Κλείνουμε περιγράφοντας και τις υπόλοιπες εντολές κλήσης ρουτίνας ή επιστροφής απ΄αυτήν. Αυτές είναι: Εντολή κλήσης ρουτίνας υπό συνθήκη. Η συνθήκη είναι η τιμή κάποιου από τα flags C, Z, S και P/V.Γενική μορφή: CALL cc, nn (ή ΕΤΙΚΕΤΑ). Η συνθήκη cc είναι C, NC, Z, NZ, P, M, PE ή ΡΟ. Aν ισχύει η συνθήκη cc, καλείται η ρουτίνα από τη διεύθυνση nn ή η ρουτίνα με ετικέτα ΕΤΙΚΕΤΑ. Αν όχι, το πρόγραμμα συνεχίζει με την επόμενη εντολή. Εντολή επιστροφής υπό συνθήκη. Γενική μορφή: RET cc. Η συνθήκη cc είναι μια από τις προαναφερθείσες. Αν ισχύει η συνθήκη γίνεται επιστροφή στο κυρίως πρόγραμμα. Διαφορετικά το πρόγραμμα συνεχίζει με την επόμενη εντολή. Προφανώς μια ρουτίνα δεν μπορεί να έχει ως τελευταία εντολή μια εντολή επιστροφής υπό συνθήκη. Υπάρχουν και οι εντολές RETN, RETI και RST p που είναι εντολές επιστροφής από ρουτίνες εξυπηρέτησης διακοπών που θα γνωρίσουμε αργότερα.
Ζ) Εντολές εισόδου/εξόδου Με τις εντολές αυτές ο Ζ80 διαβάζει δεδομένα που είναι τοποθετημένα σε πόρτες εισόδου ή γράφει δεδομένα σε πόρτες εξόδου. Κατά κανόνα , οι πόρτες εισόδου ή εξόδου υλοποιούνται από chips προσαρμοστικών (interfaces) που συνδέονται με τον Ζ80 και παρέχουν προς τον έξω κόσμο πόρτες στις οποίες συνδέονται εξωτερικές συσκευές. Η μετακίνηση δεδομένων μέσω θυρών εισόδου και εξόδου γίνεται με τις εντολές ΙΝ και OUT, τις οποίες θα γνωρίσουμε όταν μάθουμε σχετικά με το προσαρμοστικό ΡΙΟ. Η) Εντολές χειρισμού bits Η εντολή SET b,r, όπου b=0, 1, 2, …, 7 και r=A, B, C, D, E, H ή L δίνει τιμή 1 στο υπ’ αριθ. b bit του καταχωρητή r. Π. χ. η εντολή SET 0,E κάνει 1 το MSB του καταχωρητή Ε. Ομοίως, οι εντολές SET b,(HL), SET b,(IX+d) και SET b,(IY+d) δίνουν τιμή 1 στο υπ’ αριθ. b bit του περιεχομένου της θέσης μνήμης που προσδιορίζεται κατά το γνωστό τρόπο από τα (HL), (IX+d) και (IY+d). Οι εντολές RES b,r, RES b,(HL), RES b,(IX+d) και RES b,(IY+d) λειτουργούν όπως και οι αντίστοιχες εντολές SET αλλά δίνουν τιμή 0 στο bit b. Η εντολή BIT b,r εξετάζει το υπ΄ αριθ. b bit του καταχωρητή r και ενημερώνει το flag Z. Aν το bit είναι 0 κάνει Ζ=1 (ένδειξη μηδενικού αποτελέσματος) και αν το bit είναι 1 κάνει Z=0 (ένδειξη μη μηδενικού αποτελέσματος). Το ίδιο κάνουν και οι εντολές BIT b,(HL), BIT b,(IX+d) και BIT b, (IY+d) που εξετάζουν το υπ’ αριθ. b bit του περιεχομένου μιας θέσης μνήμης και ενημερώνουν το flag Z.
Παράδειγμα: Πρόγραμμα που μετράει το πλήθος των άρτιων περιεχομένων του μπλοκ θέσεων μνήμης Α010Η-Α019Η και αποθηκεύει το αποτέλεσμα στον καταχωρητή Ε. 0100Η: LD B,0AH LD E,0 LD HL,A010H LOOP: BIT 0,(HL) JP NZ,NEXT INC E NEXT: INC HL DEC B JP NZ, LOOP JP $
Θ) Εντολές αριθμητικών πράξεων μεταξύ 16-bit αριθμών Η εντολή ADD HL,ss, όπου ss=BC, DE, HL ή SP, προσθέτει στο περιεχόμενο του HL το περιεχόμενο του καταχωρητή ss και αποθηκεύει το αποτέλεσμα στον καταχωρητή HL. Ομοίως, η εντολή ADC HL,ss, όπου ss=BC, DE, HL ή SP, προσθέτει στο περιεχόμενο του HL το περιεχόμενο του καταχωρητή ss και την τιμή του flag C και αποθηκεύει το αποτέλεσμα στον καταχωρητή HL. H εντολή SBC HL,ss, όπου ss=BC, DE, HL ή SP, αφαιρεί από το περιεχόμενο του HL το περιεχόμενο του καταχωρητή ss και την τιμή του flag C και αποθηκεύει το αποτέλεσμα στον καταχωρητή HL. Η εντολή ADD ΙΧ,pp, όπου pp=BC, DE, IX ή SP, προσθέτει στο περιεχόμενο του IX το περιεχόμενο του καταχωρητή ss και αποθηκεύει το αποτέλεσμα στον καταχωρητή IX. Η εντολή ADD IY,rr, όπου rr=BC, DE, IY ή SP, προσθέτει στο περιεχόμενο του IY το περιεχόμενο του καταχωρητή ss και αποθηκεύει το αποτέλεσμα στον καταχωρητή IY. Oι εντολές INC BC, INC DE, INC HL, INC SP, INC IX και INC IY αυξάνουν κατά 1 το περιεχόμενο των αντίστοιχων καταχωρητών. Oι εντολές DEC BC, DEC DE, DEC HL, DEC SP, DEC IX και DEC IY μειώνουν κατά 1 το περιεχόμενο των αντίστοιχων καταχωρητών.
Ι) Εντολές ανταλλαγής και εντολές μπλοκ μεταφοράς και αναζήτησης Η εντολή EX DE,HL ανταλλάσσει τα περιεχόμενα των καταχωρητών DE και ΗL. Η εντολή EX ΑF,AF’ ανταλλάσσει τo περιεχόμενo του καταχωρητή A με το περιεχόμενο του καταχωρητή Α’ και το περιεχόμενο του καταχωρητή F με το περιεχόμενο του καταχωρητή F’. Η εντολή EXX ανταλλάσσει τα περιεχόμενα των καταχωρητών B, C, D, E, H και L με τα περιεχόμενα των καταχωρητών B’, C’, D’, E’, H’ και L’, αντίστοιχα. Η εντολή EX (SP),HL ανταλλάσσει τα δύο πάνω-πάνω bytes του σωρού με το περιεχόμενο του καταχωρητή HL. Ομοίως, η εντολή EX (SP),ΙΧ ανταλλάσσει τα δύο πάνω-πάνω bytes του σωρού με το περιεχόμενο του καταχωρητή ΙΧ και η εντολή EX (SP),ΙΥ ανταλλάσσει τα δύο πάνω-πάνω bytes του σωρού με το περιεχόμενο του καταχωρητή ΙΥ. Αφήνουμε τις εντολές μπλοκ μεταφοράς και αναζήτησης ως εντολές που έχουν ειδικές εφαρμογές. Εξ άλλου, μπορούμε πάντοτε να γράψουμε προγράμματα που κάνουν την ίδια δουλειά με τις εντολές αυτές.
ΙΑ) Γενικού σκοπού αριθμητικές εντολές και εντολές ελέγχου της CPU Η εντολή CPL (complement) αντικαθιστά το περιεχόμενο του συσσωρευτή Α με το συμπλήρωμα αυτού ως προς 1. Η εντολή NEG (negation) αντικαθιστά το περιεχόμενο του συσσωρευτή Α με το συμπλήρωμα αυτού ως προς 2. M’ άλλα λόγια, αντικαθιστά το περιεχόμενο του συσσωρευτή Α με το αντίθετό του. Η εντολή CCF (complement carry flag) αντικαθιστά το flag C με το συμπληρωματκό του. Η εντολή SCF (set carry flag) κάνει 1 το flag C. Όπως έχουμε πει, η εντολή ΝΟΡ δεν κάνει τίποτα. H εντολή HALT σταματά την εκτέλεση του προγράμματος (όπως η εντολή stop σε άλλες γλώσσες). Για τις εντολές DI, EI, IM 0, IM 1, και ΙΜ 2 θα μιλήσουμε στο επόμενο Κεφάλαιο που αφορά στο σύστημα διακοπών του Ζ80. Αφήνουμε την ειδικών εφαρμογών εντολή DAA.
Κεφάλαιο 2: To σύστημα διακοπών του Ζ80 Όταν στον ακροδέκτη ΝΜΙ του Ζ80 δώσουμε αρνητικό μέτωπο τάσης (δηλ. όταν τον πάμε από +5Volt σε 0 Volt), προκαλείται μη αποκρύψιμη διακοπή (Non Maskable Interrupt) του εκτελούμενου προγράμματος και καλείται η ρουτίνα εξυπηρέτησης της διακοπής από τη διεύθυνση 0066Η. Εμείς εκεί μπορεί απλώς να έχουμε βάλει μια εντολή άλματος JP που θα στέλνει στη ρουτίνα εξυπηρέτησης της διακοπής, αν την έχουμε γράψει κάπου αλλού. Η ρουτίνα εξυπηρέτησης διακοπής ΝΜΙ πρέπει να τελειώνει με την εντολή RΕΤΝ. Όταν το επίπεδο τάσης του ακροδέκτη ΙΝΤ του Ζ80 το κατεβάσουμε στα 0 Volt, στέλνουμε σήμα αποκρύψιμης διακοπής INT. Αν ένα κατάλληλο φλιπ-φλοπ, το φλιπ-φλοπ διακοπών IFF1 έχει τιμή 1 (αυτό το κάνει η εντολή ενεργοποίησης διακοπών ΕΙ) το σήμα διακοπής ΙΝΤ γίνεται δεκτό. Αν είναι IFF1=0 (αυτό το κάνει η εντολή απενεργοποίησης διακοπών DI) το σήμα διακοπής ΙΝΤ απορρίπτεται και συνεχίζεται η εκτέλεση του προγράμματος. Τα σήματα διακοπής ΝΜΙ δεν επηρεάζονται από το IFF1 και γίνονται πάντα δεκτά. Αν στον Ζ80 φθάσει και γίνει δεκτό ένα σήμα διακοπής ΙΝΤ, αυτός ακολουθεί έναν από τρεις διαφορετικούς δρόμους για να εντοπίσει τη ρουτίνα εξυπηρέτησης της διακοπής, ανάλογα με το αν ο Ζ80 βρίσκεται με κατάσταση διακοπών 0, 1 ή 2 (μπαίνει σ’ αυτές με τις εντολές ΙΜ 0, ΙΜ 1 και ΙΜ 2, αντίστοιχα). Αν ο Ζ80 βρίσκεται σε ΙΜ 1, η ρουτίνα εξυπηρέτησης καλείται από τη διεύθυνση 0038Η. Αν ο Ζ80 βρίσκεται σε ΙΜ 0, η ρουτίνα εξυπηρέτησης καλείται από μία από τις διευθύνσεις 0000Η, 0008Η, 0010Η, 0018Η, 0020Η, 0028Η, 0030Η και 0038Η. Για να δει ο Ζ80 σε ποια απ’ αυτές θα κάνει άλμα, εξετάζει το δεδομένο που βρίσκεται τότε στο δίαυλο δεδομένων (data bus). Εκεί ο χρήστης που ζήτησε τη διακοπή οφείλει να έχει τοποθετήσει τον κώδικα λειτουργίας της αντίστοιχης από τις εντολές RST 00H, RST 08H, RST 10H, RST 18H, RST 20H, RST 28H, RST 30H και RST 38H. O κώδικας λειτουργίας (opcode) μιας εντολής RST είναι 11abc111. Η τριάδα abc καθορίζει μια από τις 8 εντολές RST. Με abc=000 έχουμε την εντολή RST 00H, με 001 την εντολή RST 08H, … και με 111 την εντολή RST 38H. Π. χ. ο
κώδικας λειτουργίας της εντολής RST 28H είναι 11101111=ΕFΗ κώδικας λειτουργίας της εντολής RST 28H είναι 11101111=ΕFΗ. Επειδή ανάμεσα σε κάθε ζευγάρι από τις διευθύνσεις 0000Η, 0008Η, …, 0038Η υπάρχουν μόνο 8 θέσεις μνήμης, δεν χωράνε να γραφούν εκεί οι ρουτίνες εξυπηρέτησης διακοπών (θα έχουμε αλληλοκάλυψή τους). Γι αυτό, στις θέσεις μνήμης 0000Η, 0008Η, …, 0038Η γράφουμε από μια απλή εντολή άλματος JP που μας πάνε στις αντίστοιχες ρουτίνες που έχουμε γράψει κάπου αλλού. Για τις διακοπές με τον Ζ80 σε ΙΜ 2 θα μιλήσουμε αργότερα που θα μιλήσουμε για το προσαρμοστικό ΡΙΟ. Οι ρουτίνες εξυπηρέτησης διακοπών ΙΝΤ με τον Ζ80 σε ΙΜ 0 ή ΙΜ 1 πρέπει να τελειώνουν με την εντολή RET. Επίσης, πριν απ’ αυτήν πρέπει να έχουμε βάλει την εντολή ΕΙ γιατί με κάθε κλήση ρουτίνας εξυπηρέτησης διακοπών ΙΝΤ απενεργοποιούνται οι διακοπές ΙΝΤ. Παράδειγμα προγραμματισμού διακοπών ΝΜΙ: Από τη διεύθυνση 0100Η και κάτω γράφουμε (κυρίως) πρόγραμμα που αυξάνει συνεχώς το περιεχόμενο του καταχωρητή D κατά 1 με ρυθμό μια αύξηση ανά sec. Επίσης, από τη διεύθυνση 0500Η και κάτω γράφουμε ρουτίνα εξυπηρέτησης διακοπών ΝΜΙ, με ετικέτα ONOFFE που αναβοσβήνει όλα μαζί τα bits του καταχωρητή Ε τρεις φορές με συχνότητα 0.5 Hz, δηλ. το άναμμα (Ε=FFH) να διαρκεί 1 sec και το σβήσιμο (Ε=00Η) να διαρκεί 1 sec. Στη διεύθυνση 0066Η γράφουμε την εντολή JP 0500Η (ή JP ONOFFE). Θα χρειαστούμε ρουτίνα καθυστέρησης DELAY που, όταν κληθεί, προκαλεί καθυστέρηση 1 sec. Αφού ξέρουμε πώς θα τη γράψουμε, δεν τη γράφουμε εδώ. Το πρόγραμμα και η ρουτίνα εξυπηρέτησης διακοπών ΝΜΙ ακολουθούν:
0100H: LD SP,FFFFH ONOFFE (0500Η): PUSH BC LD D,0 LD B,03H LOOP: INC D LOOP1: LD E,FFH CALL DELAY CALL DELAY JP LOOP LD E,00H CALL DELAY 0066H: JP ONOFFE DEC B JP NZ,LOOP1 POP BC RETN Παράδειγμα προγραμματισμού διακοπών ΙΝΤ σε ΙΜ 1: Τη ρουτίνα ONOFFE που γράψαμε προηγουμένως θα την κάνουμε τώρα ρουτίνα εξυπηρέτησης διακοπών ΙΝΤ. Το κυρίως πρόγραμμα θα αυξάνει πάλι συνεχώς το περιεχόμενο του καταχωρητή D κατά 1. Επίσης, θα βάζει τον Ζ80 σε ΙΜ 1 και με σήμα διακοπής ΙΝΤ θα καλείται η ρουτίνα ONOFFE. Το πρόγραμμα και η ρουτίνα ακολουθούν: 0100H: LD SP,FFFFH ONOFFE (0500Η): PUSH BC LD D,0 LD B,03H IM 1 LOOP1: LD E,FFH EI CALL DELAY LOOP: INC D LD E,00H CALL DELAY CALL DELAY JP LOOP DEC B 0038H: JP ONOFFE POP BC EI RET
Παράδειγμα προγραμματισμού διακοπών ΙΝΤ σε ΙΜ 0: Το κυρίως πρόγραμμα και η ρουτίνα ONOFFE παραμένουν όπως στην προηγούμενη διαφάνεια. Επίσης, από τη διεύθυνση 0750Η και κάτω γράφουμε μια άλλη ρουτίνα εξυπηρέτησης διακοπών ΙΝΤ, την ONOFFL, που θα αναβοσβήνει τρεις φορές όλα μαζί τα bits του καταχωρητή L με συχνότητα 0.5 Hz. Θ αλλάξουμε το κυρίως πρόγραμμα ώστε να βάζει τον Ζ80 σε ΙΜ 0 και με σήμα ΙΝΤ να καλείται η ρουτίνα ONOFFE, αν ο χρήστης, πριν στείλει το σήμα διακοπής ΙΝΤ, έχει βάλει στο data bus τον κώδικα λειτουργίας της εντολής RST 18H (που είναι 11011111=DFH), και η ρουτίνα ONOFFL, αν ο χρήστης, πριν στείλει το σήμα διακοπής ΙΝΤ, έχει βάλει στο data bus τον κώδικα λειτουργίας της εντολής RST 30H (που είναι 11110111=F7H). Το πρόγραμμα και οι ρουτίνες ακολουθούν: (0500Η) (0750Η) 0100H: LD SP,FFFFH ONOFFE: PUSH BC ONOFFL:PUSH BC LD D,0 LD B,03H LD B,03H IM 0 LOOP1: LD E,FFH LOOP2: LD L,FFH EI CALL DELAY CALL DELAY LOOP: INC D LD E,00H LD L,00H CALL DELAY CALL DELAY CALL DELAY JP LOOP DEC B DEC B JP NZ,LOOP1 JP NZ,LOOP2 0018H: JP ONOFFE POP BC POP BC 0030Η: JP ONOFFL EI EI RET RET
Κεφάλαιο 3: To προσαρμοστικό ΡΙΟ Το ΡΙΟ διαθέτει προς τον έξω κόσμο δύο 8-bit παράλληλες πόρτες Α και Β. Η πόρτα Α συνοδεύεται και από τους ακροδέκτες χειραψίας ASTB (A strobe) και ARDY (A ready) και η πόρτα Β από τους ακροδέκτες χειραψίας BSTB (B strobe) και BRDY (B ready). Κάθε chip ΡΙΟ καταλαμβάνει στο χάρτη διευθύνσεων θυρών του Ζ80 τέσσερις 8-bit διευθύνσεις: Τη διεύθυνση της πόρτας Α (για χρήση), τη διεύθυνση της πόρτας Β (για χρήση), τη διεύθυνση προγραμματισμού της πόρτας Α και τη διεύθυνση προγραμματισμού της πόρτας Β. Το ποιες διευθύνσεις θυρών καταλαμβάνει κάθε chip ΡΙΟ εξαρτάται από τον τρόπο σύνδεσής του με τον Ζ80 (τι κύκλωμα αποκωδικοποίησης διευθύνσεων χρησιμοποιείται). Εμείς θα υποθέτουμε ότι διεύθυνση πόρτας Α=04, διεύθυνση πόρτας Β=05, διεύθυνση προγραμματισμού πόρτας Α=06 και διεύθυνση προγραμματισμού πόρτας Β=07. Μια πόρτα του ΡΙΟ μπορεί να προγραμματιστεί να είναι ολόκληρη έξοδος (mode 0), ολόκληρη είσοδος (mode 1), ολόκληρη είσοδος και έξοδος ταυτόχρονα (mode 2) (μόνο η πόρτα Α) ή ανά bit είσοδος ή έξοδος χωριστά και ανεξάρτητα για κάθε bit (mode 3). Για να διαβαστεί από τον Z80 μια πόρτα εισόδου (το περιεχόμενο του καταχωρητή εισόδου της) που έχει διεύθυνση n χρησιμοποιούμε την εντολή IN A,(n). Το δεδομένο που διαβάζεται πάει στον συσσωρευτή Α. Για να σταλεί από τον Ζ80 δεδομένο σε πόρτα εξόδου με διεύθυνση n χρησιμοποιούμε την εντολή OUT (n),A. To περιεχόμενο του συσσωρευτή Α πάει στον καταχωρητή εξόδου της πόρτας και εμφανίζεται αμέσως στους ακροδέκτες της. Μια άλλη μορφή των εντολών εισόδου και εξόδου: Η εντολή ΙΝ r,(C) διαβάζει το δεδομένο της πόρτας που έχει ως διεύθυνση το περιεχόμενο του καταχωρητή C και το πάει στον καταχωρητή r. Η εντολή OUT (C),r στέλνει το περιεχόμενο του καταχωρητή r στην πόρτα
που έχει ως διεύθυνση το περιεχόμενο του καταχωρητή C που έχει ως διεύθυνση το περιεχόμενο του καταχωρητή C. Μας χρησιμεύουν όταν ο Α έχει κάποιο χρήσιμο περιεχόμενο που δεν θέλουμε να το χαλάσουμε χρησιμοποιώντας τον με τις εντολές ΙΝ και OUT. Μια πόρτα γίνεται έξοδος αν στείλουμε (με εντολή OUT) στη διεύθυνση προγραμματισμού της τον κωδικό 0FH (ή, γενικότερα, 00ΧΧ1111Β, Χ=αδιάφορο bit). Γίνεται είσοδος αν στείλουμε τον κωδικό 4FH (ή, γενικότερα, 01ΧΧ1111Β), γίνεται ταυτόχρονα είσοδος και έξοδος (μόνο η πόρτα Α) αν στείλουμε τον κωδικό 8FH (ή, γενικότερα 10ΧΧ1111Β) και γίνεται ανά bit είσοδος ή έξοδος αν στείλουμε τον κωδικό CFH (ή, γενικότερα, 11ΧΧ1111Β). Όταν μια πόρτα είναι προγραμματισμένη είσοδος και τοποθετήσουμε κάποιο δεδομένο στους ακροδέκτες της, αυτό περνάει στον καταχωρητή εισόδου της (απ’ όπου μπορεί ύστερα να διαβαστεί με εντολή ΙΝ) μόλις δώσουμε θετικό μέτωπο στον ακροδέκτη strobe αυτής. Μάλιστα, τότε αμέσως το ΡΙΟ κάνει 0 τον ακροδέκτη ready της πόρτας, δείχνοντας έτσι ότι το δεδομένο που μπήκε στον καταχωρητή εισόδου της πόρτας δεν έχει διαβαστεί ακόμα από τον Ζ80. Όταν όμως διαβαστεί το δεδομένο με εντολή ΙΝ, ο ακροδέκτης ready ξαναπηγαίνει σε 1. Όπως θα δούμε αμέσως μετά, μια πόρτα μπορεί να στείλει στον Ζ80 σήμα διακοπής ΙΝΤ, αρκεί οι διακοπές ΙΝΤ της πόρτας να είναι ενεργοποιημένες. Ενεργοποιούνται στέλνοντας στη διεύθυνση προγραμματισμού της πόρτας τον κωδικό 83Η (ή, γενικότερα, 1ΧΧΧ0011Β) και απενεργοποιούνται στέλνοντας τον κωδικό 03Η (ή, γενικότερα, 0ΧΧΧ0011Β). Παράδειγμα 1ο: Πρόγραμμα που θα κάνει την πόρτα Α του ΡΙΟ είσοδο και την πόρτα Β έξοδο. Διακοπές και από τις δύο πόρτες απενεργοποιημένες. Το πρόγραμμα θα διαβάζει συνεχώς την πόρτα Α και το δεδομένο της θα το αυξάνει κατά 1 και θα το εμφανίζει στην πόρτα Β. Όποτε θέλουμε, θα τοποθετούμε στην πόρτα Α νέο δεδομένο που θα εισάγουμε καταχωρητή εισόδου της στέλνοντας θετικό μέτωπο στον ακροδέκτη ASTB. To (εύκολο) πρόγραμμα ακολουθεί:
LD A,4FH OUT (06),A LD A,0FH OUT (07),A LD A,03 LOOP: IN A,(04) INC A OUT (05),A JP LOOP Παράδειγμα 2ο: Πρόγραμμα που θα κάνει την πόρτα Α του ΡΙΟ είσοδο και την πόρτα Β έξοδο. Οι διακοπές και από τις δύο πόρτες θα είναι απενεργοποιημένες. Το πρόγραμμα θα εμφανίζει στην πόρτα Β έναν 1 να κινείται (να περιστρέφεται) συνεχώς προς τα δεξιά ή προς τ’ αριστερά με ταχύτητα μια θέση ανά sec. Το πρόγραμμα, για να αποφασίσει σε κάθε βήμα αν η μετακίνηση του 1 θα γίνει προς τα δεξιά ή προς τ’ αριστερά, θα ελέγχει το MSB του δεδομένου που θα έχουμε εισαγάγει εμείς στην πόρτα Α του ΡΙΟ. Αν MSB=0 η μετακίνηση θα γίνεται προς τα δεξιά και αν MSB=1 θα γίνεται προς τ’ αριστερά. Το πρόγραμμα ακολουθεί:
LD SP,FFFFH LD A,4FH OUT (06),A LD A.0FH OUT (07),A LD A,03H LD C,04 LD A,01H (=00000001Β) LOOP: OUT (05),A IN D,(C) BIT 7,D JP Z,DEXIA RLCA JP NEXT DEXIA: RRCA NEXT: CALL DELAY JP LOOP
Δημιουργία σήματος διακοπών από το ΡΙΟ: Είπαμε προηγουμένως ότι όταν μια πόρτα του ΡΙΟ είναι είσοδος και δώσουμε θετικό μέτωπο στον ακροδέκτη strobe αυτής, το δεδομένο που βρίσκεται στους ακροδέκτες της περνάει στον καταχωρητή εισόδου της. Ταυτόχρονα όμως στέλνεται και σήμα διακοπής ΙΝΤ στον Z80, αρκεί οι διακοπές της πόρτας να είναι ενεργοποιημένες. Για να γίνει δεκτό από τον Ζ80 το σήμα διακοπής ΙΝΤ πρέπει οι διακοπές ΙΝΤ να είναι ενεργοποιημένες και από τον Ζ80 (να είναι IFF1=1) και για να εξυπηρετηθεί το αίτημα διακοπής πρέπει ο Ζ80 να βρίσκεται σε ΙΜ 2. Μόλις ο Ζ80 λάβει το σήμα διακοπής ΙΝΤ από το ΡΙΟ, παίρνει το περιεχόμενο του καταχωρητή του Ι, έστω XYH, και το άρτιο byte, έστω ZWH, που μόλις έχει τοποθετήσει το ΡΙΟ στο data bus, και μ’ αυτά συνθέτει τη διεύθυνση XYZWH. Η σελίδα XYH της μνήμης (δηλ. οι θέσεις της μνήμης που οι διευθύνσεις τους έχουν ως πρώτο byte το XYH) αποτελεί τον πίνακα διανυσματικών διακοπών. Το άρτιο byte ZWH που τοποθετεί το ΡΙΟ στα data bus ονομάζεται διάνυσμα διακοπής της πόρτας. Από τις διευθύνσεις XYZWH και XYZWH+1 ο Ζ80 ανακαλεί δύο bytes με τα οποία συνθέτει τη διεύθυνση από την οποία αρχίζει η ρουτίνα εξυπηρέτησης των διακοπών που προκαλούνται από την πόρτα και κάνει άλμα σ’ αυτήν. Ας δούμε αυτά στο πρόγραμμα που ακολουθεί: Πρόγραμμα που θα κάνει την πόρτα Α του ΡΙΟ είσοδο και την πόρτα Β έξοδο. Οι διακοπές από την πόρτα Α να είναι ενεργοποιημένες και από την πόρτα Β απενεργοποιημένες. Το κυρίως πρόγραμμα θα αυξάνει συνεχώς το περιεχόμενο του καταχωρητή D κατά 1 με ρυθμό μια αύξηση ανά sec. Μόλις δώσουμε θετικό μέτωπο στον ακροδέκτη ASTB να γίνεται άλμα στη ρουτίνα εξυπηρέτησης διακοπών της πόρτας Α, στην οποία να διαβάζεται το δεδομένο που μόλις πέρασε στον καταχωρητή εισόδου της πόρτας Α από τους ακροδέκτες της, όπου το είχαμε βάλει εμείς, να αυξάνεται το δεδομένο αυτό κατά 1 και να στέλνεται στην πόρτα Β. Ύστερα να γίνεται επιστροφή στο κυρίως πρόγραμμα. Πριν την επιστροφή να ενεργοποιούνται ξανά οι διακοπές εκ μέρους του Ζ80 γιατί κάθε εξυπηρέτηση διακοπής ΙΝΤ τις απενεργοποιεί.
Το κυρίως πρόγραμμα να αρχίζει από τη διεύθυνση 0100Η, η ρουτίνα εξυπηρέτησης διακοπών της πόρτας Α να αρχίζει από τη διεύθυνση 0200Η, πίνακας διανυσματικών διακοπών του Ζ80 να είναι η σελίδα B0H της μνήμης και διάνυσμα διακοπής της πόρτας Α να είναι το 42Η. Το πρόγραμμα: 0100Η:LD SP,FFFFH ;Η κορυφή του σωρού μπαίνει στην τελευταία θέση EI ;της RAM IM 2 LD A,B0H ;Πίνακας διανυσματικών διακοπών η σελίδα B0H LD I,A ;της μνήμης RAM LD HL,0200H ;Στις διευθύνσεις Β0242Η και Β043Η γράφονται τα LD (B042H),HL ;δύο bytes της διεύθυνσης 0200Η LD A,4FH ;Πόρτα Α = είσοδος OUT (06),A LD A,0FH ;Πόρτα Β=έξοδος OUT (07),A LD A,83H ;Διακοπές πόρτας Α ενεργοποιημένες LD A,03 ;Διακοπές πόρτας Β απενεργοποιημένες LD A,42H ;Διάνυσμα διακοπής πόρτας Α=42Η LD D,0 LOOP: INC D CALL DELAY JP LOOP
0200H: IN A,(04) INC A OUT (05),A EI RETI Η ρουτίνα καθυστέρησης DELAY είναι η γνωστή μας ρουτίνα και δεν τη γράφουμε εδώ. Παρατηρήστε ότι εδώ, όπως και στο Παράδειγμα 1, τα δεδομένα που βάζουμε εμείς στην πόρτα Α αυξάνονται κατά 1 και εμφανίζονται στην πόρτα Β. Όμως εδώ κάθε δεδομένο διαβάζεται μόνο μια φορά, όταν πρωτομπαίνει στον καταχωρητή εισόδου της πόρτας Α έχοντας εμείς δώσει θετικό μέτωπο στον ακροδέκτη ASTB. Τον υπόλοιπο χρόνο ο Ζ80 ασχολείται με κάτι άλλο (αυξάνει συνεχώς τον D κατά 1). Αντίθετα, στo Παράδειγμα 1 ο Ζ80 ασχολείται αποκλειστικά με το διάβασμα της πόρτας Α χωρίς να κάνει κάτι άλλο. Σημειώστε τη διαφορά. Παρόμοια είναι ο προγραμματισμός των διακοπων ΙΝΤ που προκαλεί μια πόρτα του ΡΙΟ αν αυτή είναι σε mode 0 ή mode 2. Το σήμα διακοπής ΙΝΤ προκαλείται μόλις δώσουμε θετικό μέτωπο τάσης στον ακροδέκτη Strobe της πόρτας. Θα κλείσουμε το Κεφάλαιο αναφορικά με το ΡΙΟ παρουσιάζοντας τον προγραμματισμό των διακοπών που προκαλεί στον Ζ80 μια πόρτα του ΡΙΟ που είναι σε mode 3.
Διακοπές από πόρτα mode 3: Μετά την αποστολή στη διεύθυνση προγραμματισμού μιας πόρτας του κωδικού CFH (ή γενικότερα του κωδικού 11ΧΧ1111Β) που την βάζει σε mode 3, στέλνουμε στην ίδια διεύθυνση τον κωδικό D7D6D5D4D3D2D1D0. Αν Dk=0 ο ακροδέκτης k της πόρτας γίνεται έξοδος και αν Dk=1 γίνεται είσοδος, k=0,1,…,7.. Στη συνέχεια, αφού ενεργοποιήσουμε τις διακοπές στέλνοντας στη διεύθυνση προγραμματισμού της πόρτας τον κωδικό 83Η (ή γενικότερα τον κωδικό 1ΧΧΧ0011Β), θα πρέπει να προγραμματίσουμε τις διακοπές εκ μέρους της πόρτας. Τώρα οι ακροδέκτες χειραψίας strobe και ready δεν χρησιμοποιούνται (είναι ανενεργοί). Οι διακοπές ΙΝΤ προγραμματίζονται στέλνοντας στη διεύθυνση προγραμματισμού της πόρτας τον κωδικό D7D6D5D40111B. Με D7=1 οι διακοπές από την πόρτα ενεργοποιούνται και με D7=0 απενεργοποιούνται. Παρατήρηση: Οι διακοπές έχουν ήδη ενεργοποιηθεί με τον κωδικό 83Η. Ακόμα κι αν εδώ βάζαμε D7=0, πάλι θα ήταν ενεργοποιημένες. Δηλ. ο κωδικός 83Η ή 03Η είναι ισχυρότερος από το bit D7 της παρούσας λέξης ελέγχου. Μόνο αν παραλείψουμε να στείλουμε τον κωδικό 83Η ή 03Η στη διεύθυνση προγραμματισμού της πόρτας η ενεργοποίηση των διακοπών γίνεται με το bit D7 της παρούσας λέξης ελέγχου. Με D6=1 καθορίζουμε ότι θα προκληθεί σήμα διακοπής ΙΝΤ με το AND των επιλεγμένων ακροδεκτών εισόδου (θα δούμε πώς προσδιορίζονται αυτοί). Με D6=0 σήμα διακοπής προκαλείται με το OR των επιλεγμένων ακροδεκτών εισόδου. Με D5=1 ενεργό επίπεδο των επιλεγμένων ακροδεκτών εισόδου είναι το υψηλό (λογικό 1) και με D5=0 το χαμηλό (λογικό 0). Mε D4=1 δηλώνουμε ότι θα αποσταλεί αμέσως μετά μια μάσκα, η οποία θα επιλέγει ποιοι από τους ακροδέκτες εισόδου θα συμμετέχουν στη δημιουργία του σήματος διακοπής. Με D4=0 δηλώνουμε ότι δεν θα ακολουθήσει μάσκα, οπότε όλοι οι ακροδέκτες εισόδου συμμετέχουν στη δημιουργία του σήματος διακοπής. Αν έχουμε κάνει D4=1 στέλνουμε αμέσως μετά τη μάσκα D7D6D5D4D3D2D1D0. Αν είναι Dk=0 ο ακροδέκτης k της πόρτας επιλέγεται για να συμμετάσχει (η τιμή του να ληφθεί υπ’ όψη) στη δημιουργία του σήματος διακοπής. Αν είναι
Dk=1, ο ακροδέκτης k δεν λαμβάνεται υπ’ όψη στη δημιουργία σήματος διακοπής. Είναι πάλι k=0,1,…,7. Φυσικά, ένας ακροδέκτης για να επιλεγεί πρέπει να είναι ακροδέκτης εισόδου. Η εκφώνηση του προγράμματος: Το κυρίως πρόγραμμα να αυξάνει συνεχώς το περιεχόμενο του καταχωρητή D κατά 1 με ρυθμό μια αύξηση ανά sec. Οι πόρτες Α και Β του ΡΙΟ να μπουν και οι δύο σε mode 3. Οι ακροδέκτες Α7-Α4 να είναι ακροδέκτες εισόδου, οι Α3-Α0 εξόδου, οι Β7-Β4 εξόδου και οι Β3-Β0 εισόδου. Να προκαλείται διακοπή ΙΝΤ από την πόρτα Α όταν και μόνον όταν και οι τρεις ακροδέκτες Α7, Α6 και Α5 (λειτουργία AND) βρίσκονται σε λογικό 0 και τότε οι ακροδέκτες Α2-Α0 να αναβοσβήνουν τρεις φορές όλοι μαζί με ρυθμό ένα αναβοσβήσιμο ανά δύο sec. Να προκαλείται διακοπή ΙΝΤ από την πόρτα Β όταν και μόνον όταν έστω και ένας από τους ακροδέκτες Β1 και Β0 (λειτουργία OR) πάει σε λογικό 1 και τότε στους ακροδέκτες Β7-Β4 να εμφανίζεται ένας 1 που θα ‘πηγαινοέρχεται’ τρεις φορές απ’ άκρου εις άκρον στους ακροδέκτες αυτούς με ταχύτητα μια θέση ανά sec. Η ρουτίνα εξυπηρέτησης διακοπών της πόρτας Α να αρχίζει από τη θέση Α032Η και της πόρτας Β από τη θέση Ε356Η, το διάνυσμα διακοπής της πόρτας Α να είναι 42Η και το διάνυσμα διακοπής της πόρτας Β να είναι 44Η. Ο πίνακας διανυσματικών διακοπών να είναι η σελίδα Β0Η της μνήμης. Ο κωδικός προγραμματισμού λειτουργίας και των δύο θυρών θα είναι CFH. Για την πόρτα Α θα πρέπει να ακολουθήσει ο κωδικός 11110000Β και για την πόρτα Β ο κωδικός 00001111Β που θα προγραμματίσουν τη λειτουργία εισόδου ή εξόδου των ακροδεκτών των θυρών. Ο κωδικός για τον προγραμματισμό των διακοπών της πόρτας Α θα πρέπει να είναι 11010111Β (γιατί;) και η μάσκα 00011111Β. Ο κωδικός για τον προγραμματισμό των διακοπών της πόρτας Β θα πρέπει να είναι 10110111Β (γιατί;) και η μάσκα 111111100Β.
Η ρουτίνα εξυπηρέτησης διακοπών της πόρτας Α θα στέλνει στην πόρτα Α τρεις φορές τους αριθμούς 00000111Β και 00000000Β. Ο απλούστερος τρόπος για να γίνει αυτό που ζητά το πρόβλημα για τη ρουτίνα εξυπηρέτησης διακοπών της πόρτας Β είναι να στέλνεται πρώτα σ’ αυτήν το byte 10000000Β (=80Η), ύστερα το 40Η, ύστερα το 20Η, ύστερα τον 10Η και στη συνέχεια το αντίστροφο κι αυτά να γίνονται τρεις φορές. Το πρόγραμμα και οι ρουτίνες εξυπηρέτησης διακοπών: LD SP,FFFFH EI IM 2 LD A,B0H LD I,A LD HL,A032H LD (B042),HL LD HL,E356H LD (B044H),HL LD A,CFH OUT (06),A LD A,11110000B LD A,11010111B LD A,00011111B OUT (07),A
LD A,00001111B OUT (07),A LD A,10110111B LD A,11111100B LD A,42H OUT (06),A LD A,44H LD A,83H LD D,00H LOOP: INC D CALL DELAY JP LOOP A032H: PUSH BC PUSH AF LD B,03H LOOP1: LD A,00000111B OUT (04),A LD A,00H
DEC B JP NZ,LOOP1 POP AF POP BC EI RETI E356H: PUSH BC PUSH AF LD B,03H LOOP2: LD A,10000000B OUT (05),A CALL DELAY LD A,01000000B LD A,00100000B LD A,00010000B
LD A,01000000B OUT (05),A CALL DELAY DEC B JP NZ,LOOP2 POP AF POP BC EI RETI Δεν γράψαμε τη γνωστή μας ρουτίνα καθυστέρησης DELAY που, όταν κληθεί, προκαλεί καθυστέρηση 1 sec.