Χρησιμοποιούμε direct ή register indirect addressing mode για να έχουμε πρόσβαση σε δεδομένα που βρίσκονται αποθηκευμένα στη μνήμη δεδομένων. Σε προηγούμενη ενότητα δείξαμε πώς να χρησιμοποιούμε direct addressing mode (άμεση μέθοδος διευθυνσιοδότησης). Η register indirect addressing mode (η έμμεση με καταχωρητή μέθοδος διευθυνσιοδότησης) είναι μια σημαντική μέθοδος διευθυνσιοδότησης για τους AVRs.
Στην register indirect addressing mode, ένας καταχωρητής χρησιμοποιείται σαν δείκτης στη μνήμη δεδομένων. Στους AVR τρεις καταχωρητές χρησιμοποιούνται γι’ αυτό το σκοπό Χ, Υ και Ζ. Αυτοί είναι 16-bit καταχωρητές που βοηθούν στην πρόσβαση σε ολόκληρη την 65536 bytes της μνήμης δεδομένων των AVR.
Ο καθένας από αυτούς τους τρεις καταχωρητές είναι η ένωση δυο ειδικών καταχωρητών GPRs. Για παράδειγμα ο συνδυασμός των R26 και R27 αποτελούν τον Χ καταχωρητή. Σε αυτή την περίπτωση ο R26 είναι το χαμηλότερο byte του Χ και ο R27 το υψηλότερο byte. Οι Υ και Ζ καταχωρητές είναι ο συνδυασμός R29:R28 και R31:R30 αντίστοιχα. Στην εικόνα οι R26, R27, R28, R29, R30 και R31 καταχωρητές γενικού σκοπού αναφέρονται σαν XL, XH, YL, YH, ZL και ZH αντίστοιχα. Για παράδειγμα “LDI XL, 0x31” είναι το ίδιο με “LDI R26, 0x31” αφού το XL είναι ένα άλλο όνομα για το R26.

Οι 16-bit καταχωρητές Χ, Υ και Ζ συνήθως χρησιμοποιούνται σαν δείκτες. Μπορούμε να τους χρησιμοποιήσουμε μαζί με την εντολή LD για να διαβάσουμε την τιμή της θέσης που δείχνουν αυτοί οι καταχωρητές. Για παράδειγμα η επόμενη εντολή διαβάζει την τιμή της θέσης που δείχνει ο Χ
LD R24, X ; φόρτωσε στον R24 την τιμή της θέσης που δείχνει ο Χ
Παράδειγμα
Το ακόλουθο πρόγραμμα φορτώνει τα περιεχόμενα της θέσης 0x130 στον R18
LDI XL, 0x30 ; φόρτωσε τον R26 (το χαμηλότερο byte του Χ) με την τιμή 0x30
LDI XH, 0x01
LD R18, X ; αντέγραψε τα περιεχόμενα της θέσης 0x130 στον R18
Το προηγούμενο απόσπασμα κώδικα φορτώνει την τιμή 0x130 στον καταχωρητή Χ. Αυτό γίνεται φορτώνοντας την τιμή 0x30 στον R26 (το χαμηλότερο byte toy X) και το 0x1 στον R27 (το υψηλότερο byte του Χ). Μετά φορτώνει στον R18 τα περιεχόμενα της θέσης στην οποία δείχνει ο Χ.
Η εντολή ST χρησιμοποιείται για να γράψουμε μια τιμή σε μια θέση στην οποία δείχνει ένας από τους Χ, Υ ή Ζ. Για παράδειγμα το ακόλουθο πρόγραμμα αποθηκεύει τα περιεχόμενα του R23 στη θέση 0x139F
LDI ZL, 0x9F ; φορτώνει την τιμή 0x9F στο χαμηλότερο byte του Ζ
LDI ZH, 0x13 ; φορτώνει την τιμή 0x13 στο υψηλότερο byte του Ζ (Ζ = 0x139F)
ST Z, R23 ; αποθηκεύει τα περιεχόμενα της θέσης 0x139F της μνήμης δεδομένων
; που δείχνει ο Ζ στον καταχωρητή R23
Πλεονεκτήματα της register indirect addressing mode
Ένα από τα πλεονεκτήματα της μεθόδου register indirect addressing mode είναι ότι έχουμε πρόσβαση στα δεδομένα της μνήμης δυναμικά παρά στατικά, όπως η μέθοδος direct addressing mode. Στο ακόλουθο παράδειγμα φαίνονται οι τρεις περιπτώσεις αντιγραφής της τιμής $55 στη μνήμη RAM από τις θέσεις $140 έως $144. Σημειώστε ότι στη λύση (β) οι δυο εντολές επαναλαμβάνονται αρκετές φορές. Στη λύση (γ) δημιουργούμε ένα βρόγχο με αυτές τις δυο εντολές. Η λύση (γ) είναι περισσότερο αποδοτική από τις άλλες λύσεις και αυτό οφείλεται με την εφαρμογή της register indirect addressing mode.
Παράδειγμα
Γράψε ένα πρόγραμμα που να αντιγράφει την τιμή $55 στις θέσεις μνήμης $140 εως $144 χρησιμοποιώντας
(α) direct addressing mode
(β) register in direct addressing mode χωρίς βρόγχο και
(γ) με βρόγχο
(α)
LDI R17, 0x55 ; φόρτωσε στον R17 την τιμή 0x55
STS 0x140, R17 ; αντέγραψε τον R17 στη θέση μνήμης 0x140
STS 0x141, R17 ; αντέγραψε τον R17 στη θέση μνήμης 0x141
STS 0x142, R17 ; αντέγραψε τον R17 στη θέση μνήμης 0x142
STS 0x143, R17 ; αντέγραψε τον R17 στη θέση μνήμης 0x143
STS 0x144, R17 ; αντέγραψε τον R17 στη θέση μνήμης 0x144
(β)
LDI R16, 0x55 ; φόρτωσε τον R16 με την τιμή 0x55
LDI YL, 0x40 ; φόρτωσε τον R28 με την τιμή 0x40 (low byte of addr.)
LDI YH, 0x1 ; φόρτωσε τον R29 με την τιμή 0x1 (high byte of addr.)
ST Y, R16 ; αντέγραψε τον R16 στη θέση μνήμης δεδομένων 0x140
INC YL ; αύξηση του χαμηλότερου byte του Υ
ST Y, R16 ; αντέγραψε τον R16 στη θέση μνήμης δεδομένων 0x141
INC YL ; αύξηση του δείκτη
ST Y, R16 ; αντέγραψε τον R16 στη θέση μνήμης δεδομένων 0x142
INC YL ; αύξηση του δείκτη
ST Y, R16 ; αντέγραψε τον R16 στη θέση μνήμης δεδομένων 0x143
INC YL ; αύξηση του δείκτη
ST Y, R16 ; αντέγραψε τον R16 στη θέση μνήμης δεδομένων 0x144
(γ)
LDI R16, 0x5 ; R16 = 5 (R16 για τον μετρητή)
LDI R20, 0x55 ; φόρτωσε τον R20 με την τιμή 0x55
LDI YL , 0x40 ; φόρτωσε τον YL με την τιμή 0x40
LDI YH, 0x1 ; φόρτωσε τον ΥΗ με την τιμή 0x1
L1: ST Y, R20 ; αντέγραψε το R20 στη θέση μνήμης που δείχνει ο Υ
INC YL ; αύξηση του δείκτη
DEC R16 ; μείωση του μετρητή
BRNE L1 ; επανέλαβε μέχρι ο μετρητής δεν είναι μηδέν
Στο παραπάνω απόσπασμα κώδικα θα πρέπει να χρησιμοποιήσουμε την εντολή “INC YL” για να αυξήσουμε το δείκτη στην επόμενη θέση μνήμης διότι δεν υπάρχει εντολή όπως “INC Y”. Η χρήση βρόγχου δεν είναι δυνατή στην direct addressing mode και αυτό είναι η κύρια διαφορά μεταξύ direct and register indirect addressing modes. Για παράδειγμα αν θέλουμε να αντιγράψουμε μια ακολουθία δεδομένων που βρίσκεται σε συνεχόμενες θέσης στη μνήμη RAM είναι περισσότερο αποδοτικό η δυναμική χρήση της μεθόδου της register indirect addressing mode παρά η χρήση της μεθόδου direct addressing mode.
Αυτόματη αύξηση και μείωση του δείκτη στη μνήμη δεδομένων
Επειδή οι δείκτες (καταχωρητές) Χ, Υ και Ζ είναι 16-bit registers μπορούν να πάρουν τιμές από $0000 έως $FFFF οι οποίες καλύπτουν ολόκληρη την 64k περιοχή της μνήμης δεδομένων των AVR. Χρησιμοποιώντας την εντολή “INC ZL” για να αυξήσουμε το δείκτη θα υπάρξει πρόβλημα όταν μια διεύθυνση π.χ. $5FF αυξηθεί. Η εντολή “INC ZL” δεν μπορεί να διαδώσει το κρατούμενο στον καταχωρητή ΖΗ. Ο AVR μας δίνει την δυνατότητα για την αυτόματη αύξηση ή μείωση των δεικτών για να ξεπεράσουμε αυτό το πρόβλημα. Η σύνταξη που χρησιμοποιείται για την εντολή LD σε αυτές τις περιπτώσεις φαίνονται στον ακόλουθο πίνακα

Παράδειγμα
Θεωρούμε ότι στις θέσεις της μνήμης RAM από $190 έως $194 υπάρχει μια συμβολοσειρά με τους αντίστοιχους ASCII κωδικούς, με τους οποίους:
$190 = (‘Η’) $191=(‘Ε’) $192=(‘L’) $193=(‘L’) $194=(‘O’)
Γράψε ένα πρόγραμμα το οποίο θα παίρνει ένα – ένα χαρακτήρα και να τον στέλνει στην Port B. Χρησιμοποίησε (α) Direct addressing mode και (β) Register indirect addressing mode
(α)
Χρησιμοποιώντας direct addressing mode
LDI R20, 0xFF
OUT DDRB , R20 ; διαμόρφωσε τη θύρα Β σαν έξοδο
LDS R20, 0x190 ; R20 = περιεχόμενα της θέσης $190
OUT PORTB, R20 ; PORTB=R20
LDS R20, 0x191 ; R20 = περιεχόμενα της θέσης $191
OUT PORTB, R20 ; PORTB=R20
LDS R20, 0x192 ; R20 = περιεχόμενα της θέσης $192
OUT PORTB, R20 ; PORTB=R20
LDS R20, 0x193 ; R20 = περιεχόμενα της θέσης $193
OUT PORTB, R20 ; PORTB=R20
LDS R20, 0x194 ; R20 = περιεχόμενα της θέσης $194
OUT PORTB, R20 ; PORTB=R20
(β)
Χρησιμοποιώντας register indirect addressing mode
LDI R16, 0x5 ; R16=0x5 (R16 μετρητής)
LDI R20, 0xFF
OUT DDRB R20 ; διαμόρφωσε τη θύρα Β σαν έξοδο
LDI ZL, 0x90 ; το χαμηλότερο byte της διεύθυνσης (ZL = 0x90)
LDI ZH, 0x01 ; το υψηλότερο byte της διεύθυνσης (ΖΗ = 0x01)
L1: LD R20, Z ; διάβασε από τη θέση που δείχνει ο Ζ
INC ZL ; αύξηση του δείκτη
OUT PORTB, R20 ; έξοδος στην PortB τα περιεχόμενα του R20
DEC R16 ; μείωση του μετρητή
BRNE L1 ; εάν ο R16 δεν είναι μηδέν πήγαινε στην ετικέτα L1
Παράδειγμα
Γράψε ένα πρόγραμμα που να μηδενίζει τις 16 θέσεις της μνήμης δεδομένων ξεκινώντας από την θέση $160. Χρησιμοποίησε (α) INC Rn και (β)Auto – increment
(α)
LDI R16, 16 ; R16 = 16 (τιμή του μετρητή)
LDI XL, 0x60 ; XL = το χαμηλότερο byte της διεύθυνσης
LDI XH, 0x01 ; XH = το υψηλότερο byte της διεύθυνσης
LDI R20, 0x0 ; R20=0
L1: ST X, R20 ; μηδένισε τα περιεχόμενα της μνήμης RAM στα οποία δείχνει ο Χ
INC XL ; αύξησε τον δείκτη
DEC R16 ; μείωσε τον μετρητή
BRNE L1 ; επανάληψη μέχρι να μηδενιστεί ο μετρητής
(β)
LDI R16, 16 ; R16=16 (τιμή του μετρητή)
LDI XL, 0x60 ; το χαμηλότερο byte του Χ = 0x60
LDI XH, 0x01 ; το υψηλότερο byte του Χ = 0x01
LDI R20 , 0x0 ; R20=0
L1: ST X+, R20 ; μηδένισε τα περιεχόμενα της θέσης που δείχνει ο Χ
DEC R16 ; μείωσε τον μετρητή
BRNE L1 ; επανέλαβε μέχρι ο μετρητής γίνει μηδέν
Παράδειγμα
Θεωρούμε ότι στις θέσεις μνήμης δεδομένων από $240 έως $243 έχουμε τα ακόλουθα hex δεδομένα. Γράψε ένα πρόγραμμα που να τα αθροίζει μαζί και να τοποθετεί το αποτέλεσμα στις θέσεις $220 και $221
$240 = ($7D) $241 = ($EB) $242 = ($C5) $243 = ($5B)
.EQU L_BYTE = 0x220 ; θέση μνήμης για το L_Byte
.EQU H_BYTE = 0x221 ; θέση μνήμης για το Η_Byte
LDI R16, 4
LDI R20, 0
LDI R21, 0
LDI XL, 0x40
LDI XH, 0x02
L1: LD R22, X+ ; διάβασε τα περιεχόμενα της θέσης που δείχνει ο Χ
ADD R20, R22
BRCC L2 ; μεταπήδησε εάν C=0
INC R21 ; αύξησε τον R21
L2: DEC R16 ; μείωσε τον μετρητή
BRNE L1 ; επανάληψη μέχρι ο μετρητής γίνει 0
STS L_BYTE, R20 ; αποθήκευσε το χαμηλότερο byte του αποτελέσματος
; στη θέση $220 της RAM
STS H_BYTE, R21 ; αποθήκευσε το υψηλότερο byte του αποτελέσματος
; στη θέση $221 της ΡΑΜ
Παράδειγμα
Γράψε ένα πρόγραμμα που να αντιγράφει ένα μπλόκ από 5 bytes από την μνήμη δεδομένων που να ξεκινά από τη θέση $130 στη μνήμη που να ξεκινά από τη θέση $160
LDI R16, 5 ; R16 = 5 (μετρητής)
LDI XL, 0x30 ; το χαμηλότερο byte της διεύθυνσης
LDI XH, 0x01 ; το υψηλότερο byte της διεύθυνσης
LDI YL, 0x60 ; το χαμηλότερο byte της διεύθυνσης
LDI YH, 0x01 ; το υψηλότερο byte της διεύθυνσης
L1: LD R20, X+ ; διάβασε από την θέση που δείχνει ο Χ
ST Y+, R20 ; αποθήκευσε την τιμή του R20 στη θέση RAM που δείχνει ο Υ
DEC R16 ; μείωσε τον μετρητή
BRNE L1 ; επανάληψη μέχρι ο μετρητής γίνει 0
Πριν τρέξουμε το παραπάνω πρόγραμμα:
130 = (‘Η’) 131 = (‘Ε’) 132 = (‘L’) 133 = (‘L’) 134 = (‘O’)
Μετά που τρέξουμε το πρόγραμμα, οι διευθύνσεις $160 - $164 έχουν τα ίδια δεδομένα όπως στις θέσεις $130 - $134
130 = (‘Η’) 131 = (‘Ε’) 132 = (‘L’) 133 = (‘L’) 134 = (‘O’)
160 = (‘Η’) 161 = (‘Ε’) 162 = (‘L’) 163 = (‘L’) 164 = (‘O’)
Παράδειγμα
Δυο πολλαπλών byte αριθμοί είναι αποθηκευμένοι στις διευθύνσεις $130 – $133 και $150 $153. Γράψε ένα πρόγραμμα που να προσθέτει τους αριθμούς και να αποθηκεύει το αποτέλεσμα στις διευθλυνσεις $160 – $163
$C7659812 + $2978742A
LDI R16, 4 ; R16 = 4 (τιμή του μετρητή)
LDI XL, 0x30
LDI XH, 0x1 ; φόρτωσε τον δείκτη Χ = $130
LDI YL, 0x50
LDI YH, 0x01 ; φόρτωσε τον δείκτη Υ = $150
LDI ZL, 0x60
LDI ZH, 0x1 ; φόρτωσε τον δείκτη Ζ = $160
CLC ; μηδένισε το κρατούμενο
L1: LD R18, X+ ; αντέγραψε στον R18 τα περιεχόμενα της θέσης
; που δείχνει ο Χ και αύξησε τον Χ
LD R19, Υ+ ; αντέγραψε τα στον R19 τα περιεχόμενα της θέσης
; που δείχνει ο Υ και αύξησε τον Υ
ADC R18, R19 ; R18 = R18 + R19 + carry
ST Z+, R18 ; αποθήκευσε τον R18 στην μνήμη και αύξησε τον Z
DEC R16 ; μείωσε τον μετρητή R16
BRNE L1 ; επανάληψη μέχρι να μηδενιστεί ο μετρητής
Πριν την πρόσθεση έχουμε
133 = ($C7) 132 = ($65) 131 = ($98) 130 = ($12)
153 = ($29) 152 = ($78) 151 = ($74) 150 = ($2A)
Μετά την πρόσθεση έχουμε
163 = ($F0) 162 = ($DE) 161 = ($0C) 160 = ($3C)
Register indirect with displacement
Υποθέτουμε ότι θέλουμε να διαβάσουμε ένα byte το οποίο είναι μερικά bytes μετά από τη θέση που δείχνει ο καταχωρητής Ζ. Για να το κάνουμε μπορούμε να αυξήσουμε τον καταχωρητή Ζ ώστε να δείχνει στην επιθυμητή θέση και μετά να την διαβάσουμε.
Όμως υπάρχει ένας ευκολότερος τρόπος: μπορούμε να χρησιμοποιήσουμε τη μέθοδο της register indirect with displacement. Σε αυτή την μέθοδο διευθυνσιοδότησης μια σταθερή τιμή προστίθεται στον καταχωρητή Ζ. Για παράδειγμα όταν θέλουμε να διαβάσουμε τα περιεχόμενα μιας θέσης μνήμης RAM η οποία είναι 5bytes μετά τη θέση που δείχνει ο καταχωρητής Ζ μπορούμε να χρησιμοποιήσουμε την ακόλουθη εντολή:
LDD R20, Z+5 ; load from Z+5 into R20
Η γενική μορφή της εντολής είναι η εξής
LDD Rd, Z+q ; load from Z+q into Rd
Όπου q είναι ένας αριθμός μεταξύ 0 και 63 και Rd είναι οποιοδήποτε καταχωρητής γενικού σκοπού.
Για να αποθηκεύσουμε ένα byte σε μια θέση στη μνήμη δεδομένων, χρησιμοποιώντας την μέθοδο register indirect with displacement addressing mode μπορούμε να χρησιμοποιήσουμε την εντολή STD (Store with Displacement). Η σύνταξη αυτής της εντολής είναι η εξής:
STD Z+q, Rr ; store Rr into location Z+q
Για παράδειγμα, η επόμενη εντολή γράφει τα περιεχόμενα του R20 στη θέση η οποία είναι πέντε bytes μακρύτερα από τη θέση που δείχνει ο καταχωρητής δείκτης Ζ
STD Z+5, R20 ; store R20 into location Z+5
Παράδειγμα
Γράψε μια συνάρτηση η οποία να προσθέτει τα περιεχόμενα τριών συνεχόμενων θέσεων της μνήμης δεδομένων και να αποθηκεύει το αποτέλεσμα στην πρώτη θέση. Ο δείκτης Ζ θα πρέπει να δείχνει στην πρώτη θέση πριν η συνάρτηση κληθεί.
LDI R16, HIGH(RAMEND) ; αρχικοποίηση του δείκτη σωρού
OUT SPH, R16
LDI R16, LOW(RAMEND)
OUT SPL, R16
LDI ZL, 0x00 ; αρχικοποίηση του δείκτη Ζ
LDI ZH, 2
CALL ADD3LOC ; κλίση της add3loc
HERE: JMP HERE ; ατέρμον βρόγχος
ADD3LOC:
LDI R21, 0 ; R21 = 0
LD R20, Z ; R20 = περιεχόμενα της θέσης που δείχνει ο Ζ
LDD R16, Z+1 ; R16 = περιεχόμενα της θέσης που δείχνει ο Ζ+1
ADD R20 R16 ; R20 = R20 + R16
BRCC L1 ; μεταπήδησε όταν το κρατούμενο γίνει μηδέν
INC R21 ; αύξησε το R21 όταν έχουμε κρατούμενο
L1: LDD R16, Z+2 ; R16 = περιεχόμενα της θέσης Ζ+2
ADD R20, R16 ; R20 = R20 + R16
BRCC L2 ; μεταπήδησε όταν το κρατούμενο γίνει μηδέν
INC R21 ; αύξησε τον R21
L2: ST Z, R20 ; αποθήκευσε τον R20 στη θέση Ζ
STD Z+1, R21 ; αποθήκευσε τον R21 στη θέση Ζ+1
RET