Διακλαδώσεις και βρόγχοι

Πολλές φορές θέλουμε να πάμε τη ροή της εκτέλεσης ενός προγράμματος Assembly σε άλλη εντολή. Αυτό μπορούμε να το κάνουμε  με την υπό συνθήκη ή χωρίς συνθήκη διακλάδωση. Με την υπό συνθήκη διακλάδωση είναι οι εντολές: π.χ. BRNE  ή  BRSH ή με χωρίς συνθήκη είναι: οι JMP ή RJMP

Χρησιμοποιώντας την εντολή BRNE για την δημιουργία βρόγχων

Η εντολή BRNE (branch if not equal) χρησιμοποιεί σαν συνθήκη τη σημαία Z (zero) του καταχωρητή κατάστασης για να αποφασίσει αν η ροή θα διακλαδιστεί ή όχι: Η εντολή BRNE χρησιμοποιείται ως ακολούθως:

BACK:  . . . . .
       . . . . .
       . . . . .
DEC Rn
BRNE BACK

Στις δυο τελευταίες εντολές ο καταχωρητής Rn (π.χ.  R16  ή  R17) μειώνεται κατά ένα και αν δεν πάρει την τιμή μηδέν η ροή του προγράμματος διακλαδίζεται πίσω στην διεύθυνση που αντιπροσωπεύει η ετικέτα BACK. Εάν ο Rn πάρει την τιμή μηδέν η ροή εκτέλεσης θα πάει στην από κάτω επόμενη εντολή.

Πριν την έναρξη του βρόγχου ο καταχωρητής Rn φορτώνεται με τον αριθμό επαναλήψεων του βρόγχου.

Σημειώστε ότι η εντολή BRNE αναφέρεται στη σημαία του καταχωρητή κατάστασης που διαμορφώνεται από την προηγούμενη εντολή DEC.

Παράδειγμα

Στο ακόλουθο πρόγραμμα ο καταχωρητής R16 χρησιμοποιείται σαν απαριθμητής. Αυτός ο απαριθμητής φορτώνεται στην αρχή με την τιμή 10. Σε κάθε επανάληψη η εντολή DEC μειώνει το περιεχόμενο του καταχωρητή R16 κατά ένα και θέτει τις σημαίες κατάλληλα. Εάν ο R16 δεν είναι μηδέν (Z=0) η ροή του προγράμματος πηγαίνει στην διεύθυνση που ορίζεται με την σημαία “AGAIN”. Ο βρόγχος συνεχίζεται μέχρι ο R16 γίνει μηδέν. Μετά που ο R16 πάρει την τιμή μηδεν (Ζ=1) η ροή μεταπηδά στην επόμενη εντολή έξω και ακριβώς κάτω από τον βρόγχο, που εδώ είναι η “OUT PORTB, R20”

Γράψε ένα πρόγραμμα το οποίο (α) να μηδενίζει τον R20 μετά (β) να προσθέτει τον αριθμό 3 στον R20 για δέκα φορές και (γ) να στέλνει το άθροισμα στην PORTB. Χρησιμοποίησε τη σημαία μηδενισμού και την εντολή BRNE:

; this program adds value 3 to the R20 ten times
         LDI   R16, 10   ; R16 = 10  (decimal)  for counter
         LDI   R20,   0  ; R20 = 0
         LDI   R21,   3  ; R21 = 3
AGAIN:   ADD R20,  R21   ; add 03 to R20 (R20 = sum)
         DEC  R16        ; decrement until COUNT = 0
         BRNE  AGAIN     ; repeat until COUNT = 0
         OUT PORTB, R20  ; send sum to PORTB

Βρόγχος μέσα σε βρόγχο

Στο προηγούμενο παράδειγμα ο μέγιστος αριθμός επαναλήψεων είναι 255. Τι πρέπει να κάνουμε εάν θέλουμε να επαναλάβουμε ένα σύνολο εντολών περισσότερο από 255 φορές; Για να το κάνουμε αυτό χρησιμοποιούμε βρόγχο μέσα σε βρόγχο, όπου αυτή η διαδικασία ονομάζεται φωλιασμένος βρόγχος. Σε ένα φωλιασμένο βρόγχο χρησιμοποιούμε  δυο καταχωρητές για την απαρίθμηση.

Παράδειγμα

Γράψτε ένα πρόγραμμα (α) που να φορτώνει τον καταχωρητή PORTB με την τιμή 0x55 και (β) να αναστρέφει τα bits της Port B για 700 φορές.

Λύση
Επειδή το 700 είναι μεγαλύτερο από 255 θα χρησιμοποιήσουμε δυο καταχωρητές για να κρατήσουμε την απαρίθμηση. Ο επόμενος κώδικας δείχνει πώς να χρησιμοποιήσουμε τον R20 και R21 σαν καταχωρητή απαρίθμησης.

.ORG 0
       LDI   R16, 0x55 ; R16 = 0x55
       OUT PORTB, R16  ; PORTB = 0x55
       LDI   R20, 10   ; load 10 into R20 (outer loop count)
LOP_1: LDI  R21, 70    ; load 70 into R21 (inner loop count)
LOP_2: COM R16         ; complement R16
       OUT  PORTB, R16 ; load PORT SFR with the complemented value
       DEC  R21        ; dec R21 (inner loop)
       BRNE  LOP_2     ; repeat it 70 times
       DEC R20         ; dec R20 (outer loop)
       BRNE LOP_1      ; repeat it 10 times 

Σε αυτό το πρόγραμμα ο καταχωρητής R21 χρησιμοποιείται για να κρατήσει την απαρίθμηση του εσωτερικού βρόγχου. Στην εντολή “BRNE LO_2” όταν ο R21 γίνει 0 η ροή εκτέλεσης εξέρχεται από τον εσωτερικό βρόγχο και εκτελείτε η εντολή “DEC R20”. Η επόμενη εντολή φορτώνει τον απαριθμητή του εσωτερικού βρόγχου με την τιμή 70 και εάν R20 δεν είναι μηδέν, τότε ο εσωτερικός βρόγχος ξεκινά να εκτελείται. Αυτή η διαδικασία θα συνεχίσει να εκτελείται μέχρι ο R20 γίνει μηδέν και ο εξωτερικός βρόγχος τελειώσει. 

Άλλες υπό συνθήκη διακλαδώσεις

Στο διπλανό πίνακα μπορείς να δεις τις υπό συνθήκη εντολές διακλάδωσης. Για παράδειγμα βλέπεις τις BREQ (διακλάδωση όταν Ζ=1) και BRLO (διακλάδωση όταν C=1), οι οποίες προκαλούν διακλάδωση στη σειρά εκτέλεσης του κώδικα κάτω από μια συνθήκη.

BREQ (branch if equal, branch if Z=1)

Σε αυτή την εντολή, η σημαία Ζ ελέγχεται. Εάν είναι Z=1, η CPU μεταπηδά στην καθορισμένη διεύθυνση που ορίζει η ετικέτα. Για παράδειγμα δες το ακόλουθο απόσπασμα κώδικα:

OVER:  IN  R20, PINB   ; read PINB and put it in R20
       TST R20         ; set the flags according to R20
       BREQ  OVER      ; jump if R20 is zero

Σε αυτόν τον κώδικα εάν η τιμή του καταχωρητή ΡΙΝΒ είναι μηδέν, η CPU μεταπηδά στην ετικέτα OVER. Παραμένει στον βρόγχο μέχρι ο καταχωρητής ΡΙΝΒ πάρει μην μηδενική τιμή. Η εντολή TST χρησιμοποιείται για να θέσει τις σημαίες στον καταχωρητή κατάστασης σύμφωνα με τα περιεχόμενα του καθορισμένου καταχωρητή (εδώ του R20) χωρίς να εκτελέσει αριθμητική πράξη όπως μείωση ενός καταχωρητή. Όταν η εντολή TST εκτελείται, εάν ο καταχωρητής έχει την μηδενική τιμή, η σημαία Ζ τίθεται σε 1 αλλιώς τίθεται σε 0. Επίσης θέτει τη σημαία Ν σε 1 εάν το bit D7 του καταχωρητή έχει τιμή 1, αλλιώς η σημαία Ν τίθεται σε 0 (Ν=0).

Παράδειγμα:

Γράψε ένα πρόγραμμα το οποίο αν η θέση 0x200 της μνήμης RAM έχει την τιμή 0 να γράψει εκεί την τιμή 0x55. Λύση:

     .EQU MYLOC = 0x200
      LDS R20, MYLOC
      TST R20         ; καθορίζει τη σημαία
                 ; (Z=1 εάν R20 έχει την μηδενική τιμή)
      BRNE NEXT        ; διακλάδωση εάν ο R20 δεν είναι μηδέν (Z=0)
      LDI R20, 0x55    ; βάλε την τιμή 0x55 εάν ο R20 έχει την μηδενική τιμή
      STS MYLOC, R20   ; και βάλε ένα αντίγραφο στη θέση μνήμης $200
ΝΕΧΤ: . . . .

BRSH (branch if same or higher, branch if C=0)

Σε αυτή την εντολή η σημαία κρατουμένου του καταχωρητή κατάστασης χρησιμοποιείται για να αποφασίσει η CPU εάν κάνει διακλάδωση. Με την εντολή “BRSH label” η CPU εξετάζει τη σημαία κρατουμένου. Εάν C=0 η CPU ξεκινά να εκτελεί τις εντολές από την διεύθυνση της ετικέτας. Εάν C=1 δεν έχουμε διακλάδωση και εκτελούνται οι εντολές μετά την BRSH. Σημειώστε ότι υπάρχει η εντολή “BRLO label” με την οποία εάν C=1 η CPU μεταπηδά στην διεύθυνση προορισμού.

Παράδειγμα

Βρες το άθροισμα των τιμών 0x81, 0xE9 και 0xF6. Βάλε το άθροισμα στους R16 (low byte) και R17 (high byte). Λύση:

.ORG 0
        LDI R17, 0  ; μηδένισε το high byte R17=0
        LDI R16, 0  ; μηδένισε το low byte R16=0
        LDI R20, 0x81
        ADD R16, R20 ; R16 = 0 + 0x81 = 0x81, C=0
        BRSH LINE_1  ; εάν C=0 πρόσθεσε τον επόμενο αριθμό
        INC R17      ; C = 1
LINE_1: LDI R20, 0xE9
        ADD R16, R20  ; R16 = 0x81 + 0xE9 = 0x6A και C=1
        BRSH LINE_2   ; διακλάδωση εάν C = 0
        INC R17       ; C = 1
LINE_2: LDI R20, 0xF6
        ADD R16, R20   ; R16 = 0x6A + 0xF6 = 0x60 και C = 1
        BRSH OVER      ; διακλάδωση εάν C = 0
        INC R17        ; C = 1
OVER:          ; τώρα low byte = 0x60  και high byte = 02

Όλες οι υπό συνθήκη διακλαδώσεις είναι κοντινά άλματα

Όλες οι υπό συνθήκη διακλαδώσεις όπως οι BRSH, BREQ και BRNE πάνε την εκτέλεση σε κοντινές αποστάσεις. Η διεύθυνση προορισμού είναι σχετική της τιμής του program counter (PC). Εάν η σχετική διεύθυνση είναι θετική η διακλάδωση γίνεται προς τα μπροστά και εάν η σχετική διεύθυνση είναι αρνητική η διακλάδωση γίνεται προς τα πίσω. Η σχετική διεύθυνση μπορεί να έχει τιμή από -64 έως +63 επί του PC. Για να υπολογίσουμε τη διεύθυνση προορισμού, η σχετική διεύθυνση προστίθεται στον program counter (PC) της επόμενης εντολής.

Υπό χωρίς συνθήκη εντολές διακλάδωσης

Οι υπό χωρίς συνθήκη εντολές διακλάδωσης είναι άλματα στα οποία ο έλεγχος μεταφέρεται χωρίς συνθήκη στην διεύθυνση προορισμού. Στους μικροελεγκτές AVR υπάρχουν τρεις χωρίς συνθήκη εντολές διακλάδωσης οι: JMP (jump),  RJMP (relative jump) και IJMP (indirect jump). Η εντολή που θα χρησιμοποιήσουμε εξαρτάται από την διεύθυνση προορισμού.

JMP (JMP is a long jump)

H JMP είναι μια χωρίς συνθήκη εντολή διακλάδωσης στην οποία ο έλεγχος μπορεί να μεταφερθεί σε οποιαδήποτε θέση της μνήμης προγράμματος μέσα στις 4Μ (word) θέσεις διευθύνσεων της μνήμης. Η εντολή JMP καταλαμβάνει μέγεθος 4 byte (32-bit) στην περιοχή προγράμματος και επιτρέπει άλματα μέσα σε 4Μ(words) της μνήμης από τις διευθύνσεις 000000 έως $3FFFFF και έτσι καλύπτει όλο το διάστημα διευθύνσεων.

Τα μέλη της οικογένειας AVR δεν περιέχουν τόση μεγάλη περιοχή μνήμης προγράμματος. Μερικά μέλη μικροελεγκτών AVR έχουν μόνο 4Κ – 32Κ πάνω στο τσιπ ROM σαν μνήμη προγράμματος. Για αυτό υπάρχει η RJMP (relative jump) εντολή διακλάδωσης η οποία είναι μια 2byte εντολή σε αντίθεση με την 4byte  JMP εντολή. Αυτή μπορεί να μας οικονομήσει μερικά bytes μνήμης ROM σε πολλές εφαρμογές στις οποίες η μνήμη ROM δεν είναι μεγάλη σε μέγεθος.

RJMP (relative jump)

Σε αυτή την 2-byte υπό χωρίς συνθήκη εντολή διακλάδωσης, η διεύθυνση προορισμού αναφέρεται με την σχετική διεύθυνση της ετικέτας προορισμού ως προς την διεύθυνση τις τρέχων τιμής του program counter (PC). Η σχετική διεύθυνση ορίζεται από -2048 έως +2047 πάνω στην διεύθυνση του program counter και δίνει την διεύθυνση προορισμού. Εάν το άλμα είναι προς τα μπροστά η σχετική διεύθυνση είναι θετική. Εάν το άλμα είναι προς τα πίσω η σχετική διεύθυνση είναι αρνητική.

IJMP (indirect jump)

H IJMP είναι μια 2-byte εντολή. Όταν η εντολή εκτελείται, ο PC φορτώνεται με τα περιεχόμενα του καταχωρητή Ζ και έτσι η εκτέλεση πάει στην διεύθυνση που ορίζεται από τον καταχωρητή Ζ. Ο Ζ είναι ένας 2 byte καταχωρητής και έτσι το άλμα με την εντολή IJMP μπορεί να είναι μέσα στα χαμηλότερα 64Κ words της μνήμης προγράμματος.

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