Μέθοδοι διευθυνσιοδότησης – μέρος 2ο

Χρησιμοποιούμε 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 σε αυτές τις περιπτώσεις φαίνονται στον ακόλουθο πίνακα

%%%

Σημειώσεις (α) Ο πίνακας δείχνει την σύνταξη για την LD εντολή, αλλά χρησιμοποιείται και για τις υπόλοιπες τέτοιες εντολές (β) Η αυτόματη αύξηση ή μείωση επιδρά σε ολόκληρη την περιοχή των 16-bit καταχωρητών δείκτη χωρίς να έχει επίδραση στον καταχωρητή κατάστασης. Αυτό σημαίνει ότι όταν ένας καταχωρητής δείκτη που πηγαίνει από FFFF σε 0000 δεν θα επηρεάσει καμία σημαία του καταχωρητή κατάστασης (status register)

Παράδειγμα

Θεωρούμε ότι στις θέσεις της μνήμης 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