Download - Avr liput ja_branchit
AVR-mikrokontrollerien
Liput ja Branch-käskyt
(ehdolliset haaraumat)
A. Karttunen,
Metropolia,
Mikroprosessoritekniikka,
14.-15. marraskuuta 2011
AVR, Liput ja Branchit (1)
Ehtolauseet ja Silmukat
• Kaikissa vähääkään monimutkaisemmissa ohjelmissa tarvitaan ehtolauseita, joilla voidaan valita, mikä kahdesta tai useammasta vaihtoehtoisesta haarasta kulloinkin suoritetaan.
• Konekielessä/assemblerissa korkean tason kielten ”if-then-else” tyyppiset ehtolauseet joudutaan toteuttamaan branch- ja (R)JMP-tyyppisillä käskyillä pomppimalla ohjelman eri kohtien välillä ja yli.
• Myös korkean tason kielten ”while”, ”for” ja ”do while” (tai ”repeat until”) tyyppiset silmukat toteutetaan branch-käskyillä. Ainoastaan ikuiseen silmukkaan riittää (R)JMP-tyyppinen ehdoton haarautuminen.
• Ennen branch-käskyn käyttöä, prosessorin SREG-”lippujen” (eli ”flägien”) tilanteen on heijastettava haluttua ehtoa, mikä varmistetaan yleensä (mutta ei aina!) suorittamalla välittömästi ennen branch-käskyä jokin aritmeettis-looginen käsky, joka kohdistuu johonkin tiettyyn rekisteriin tai rekistereihin (esimerkiksi kahden rekisterin arvoa vertaileva käsky CP).
AVR, Liput ja Branchit (2)
if-then lauseen koodaus Jos meillä on vain ehtolauseen then-haara, mutta ei else-haaraa, niin silloin assembleriksi koodattaessa
ehto yleensä kääntyy vastakohdakseen, koska haluamme hypätä then-haaran tuottaman koodin yli
silloin kun ehto ei ole voimassa.
Esimerkiksi seuraavanlainen pätkä ”pseudo-C:tä”:
if(r0 == 17) then { sitten jotain…; }
saattaisi kääntyä seuraavanlaiseksi AVR-assembleriksi:
CPI R0,17
BRNE HYPPAA_YLI ;; Tässä käytetään Branch if Not Equivalent käskyä, koska hyppy
;; on suoritettava vain silloin kuin if-käskyn ehto ei ole
;; voimassa, ja ohjelman suoritusta jatketaan koko if-then käskyn
;; jälkeisestä käskystä.
;; Tähän väliin tulee ”sitten jotain” eli then-haarassa annetuista käskyistä muodostettu
;; assembler-koodi.
HYPPAA_YLI:
;; Tähän kohtaan koodia tullaan, joko sen jälkeen kun kaikki välittömästi edeltävät then-haaran
;; käskyt on suoritettu, tai sitten suoraan ehdosta BRNE-käskyllä hyppäämällä, mikäli R0 ei ole 17.
AVR, Liput ja Branchit (3)
if-then-else lauseen koodaus Samoin kokonaisesta if-then-else lauseesta muodostuu usein assembler-koodia jossa if-käskyn ehto
usein kääntyy vastakohdakseen. Esimerkiksi seuraavanlainen pätkä lause:
if(r0 == 17)
then { sitten jotain…; }
else { muuten jotain muuta…; }
saattaisi kääntyä seuraavanlaiseksi AVR-assembleriksi:
CPI R0,17
BRNE MUUTEN ;; Tässä käytetään nyt Branch if Not Equivalent käskyä, koska hyppy on
;; suoritettava silloin kuin if-käskyn ehto ei ole voimassa, ja pitääkin suorittaa else-haara.
;; Tähän väliin tulee ”sitten jotain” eli then-haarassa annetuista käskyistä muodostettu
;; assembler-koodi, jonka jälkeen tulee yleensä:
RJMP JATKUU ;; Kun koko ”then”-haara on suoritettu, hyppäämme else-haaran ohi.
MUUTEN: ;; Tähän kohtaan koodia tullaan vain mikäli R0 ei ollut 17.
;; Labeleiden MUUTEN ja JATKUU väliin tulee ”muuten jotain muuta”, ts. else-haarassa annetuista
;; käskyistä muodostettu assembler-koodi, jonka jälkeen tulee vielä:
JATKUU:
;; eli se kohta koodissa, johon then-haara lopuksi hyppää, ja johon else-haara luontaisesti ”valuu”
;; (englanniksi ”falls into/through”).
AVR, Liput ja Branchit (4)
if-then-else, toisella tavalla Toinen tapa tuottaa if-then-else lauseesta koodia on pitää annettu ehto ”samoinpäin”, mutta vaihtaa then- ja
else-haarojen paikkaa tuotetussa assembler-koodissa, jolloin else-haaran koodi tulee ensin, ja vasta sen jälkeen
then-haara. Esimerkiksi seuraavanlainen käsky:
if(r0 == 17)
then { sitten jotain…; }
else { muuten jotain muuta…; }
saattaisi kääntyä seuraavanlaiseksi AVR-assembleriksi:
CPI R0,17
BREQ SITTEN ;; Tässä käytetään nyt Branch if Equivalent käskyä, koska hyppy on suoritettava
;; silloin kuin if-käskyn ehto on voimassa, ja pitää suorittaa then-haara.
;; Tähän väliin tulee ”muuten jotain muuta” eli else-haarassa annetuista käskyistä muodostettu
;; assembler-koodi, jonka jälkeen tulee yleensä:
RJMP JATKUU ;; Kun koko ”else”-haara on suoritettu, hyppäämme ”ehdottomasti” (siis aina!)
;; then-haaran ohi, if-käskyn jälkeiseen koodiin.
SITTEN: ;; Tähän kohtaan koodia tullaan vain mikäli R0 oli 17.
;; Labeleiden SITTEN ja JATKUU väliin tulee ”sitten jotain”, ts. then-haarassa annetuista käskyistä
;; muodostettu assembler-koodi, jonka jälkeen tulee vielä:
JATKUU:
;; eli se kohta koodissa, johon else-haara lopuksi hyppää, ja johon then-haara luontaisesti ”valuu”
;; (englanniksi ”falls into/through”).
AVR, Liput ja Branchit (5)
while-silmukan koodaus Myös ohjelmasilmukoiden muodostamiseen tarvitaan ehdollisia hyppykäskyjä. (Poikkeuksena
tietenkin tarkoitukselliset idioottiluupit). Esimerkiksi seuraavanlainen pätkä ”pseudo-C:tä”:
while(r0 != 100){ tee jotain…; }
saattaisi kääntyä seuraavanlaiseksi AVR-assembleriksi:
SILMUKAN_ALKU:
CPI R0,100
BREQ ULOS_SILMUKASTA ;; Hypätään silmukasta ulos, vain mikäli R0 on 100.
;; Tähän väliin tulee ”tee jotain” eli while-käskyn rungossa annetuista käskyistä muodostettu
;; assembler-koodi, jonka joukossa (toivottavasti!) on jokin rekisterin R0 arvoa muuttava käsky,
;; esimerkiksi INC R0.
;; Tämän pätkän lopuksi tulee ehdoton hyppykäsky:
RJMP SILMUKAN_ALKU ;; Jolla palataan takaisin silmukan alkuun, testaamaan pitäisikö se
;; suorittaa vielä kerran.
ULOS_SILMUKASTA: ;; Tähän kohtaan koodia tullaan vasta sitten kun R0 on 100.
;; ja tästä myös jatkuu while-luupin jälkeinen koodi.
Huom! Nämä ovat vain tiettyjä perus-skeemoja, joita ei tarvitse seurata orjallisesti assembleria käsin
kirjoittaessaan, mikäli keksii optimoidumman tavan kirjoittaa sama asia.
AVR, Liput ja Branchit (6)
do while-silmukan koodaus C- ja Java-kielten ”do while” tyyppisen silmukan konvertoiminen assembleriksi on yksinkertaisempaa
kuin tavallisen while-luupin, koska siinä silmukanjatkamisehto tarkistetaan vasta lopuksi, jolloin emme
tarvitse kuin yhden (ehdollisen) hyppykäskyn, joka tulee aivan luupin loppuun.
Esimerkiksi seuraavanlainen pätkä ”pseudo-C:tä”:
do { tee jotain…; } while(r0 != 100);
saattaisi kääntyä seuraavanlaiseksi AVR-assembleriksi:
SILMUKAN_ALKU:
;; tähän väliin tulee ”tee jotain” eli do while-käskyn rungossa annetuista käskyistä muodostettu
;; assembler-koodi, jonka joukossa (toivottavasti! ;-) on jokin rekisterin R0 arvoa muuttava käsky,
;; esimerkiksi INC R0. Niiden jälkeen tulee:
CPI R0,100
BRNE SILMUKAN_ALKU ;; Hypätään takaisin silmukan alkuun, mikäli R0 ei ole 100.
;; ja tästä kohdasta jatkuu do-while-luupin jälkeinen koodi.
Paitsi että yllä olevan koodaaminen on ihmiselle helpompaa kuin tavallisen while-luupin, niin se myös
ajetaan nopeammin, koska jokainen suoritettu hyppy, oli se sitten ehdollinen (branch-käsky) tai
ehdoton (esim. RJMP) ”yskityttää” tai ”nikotuttaa” prosessorin liukuhihnaa ainakin yhden
kellojakson ajan.
AVR, Liput ja Branchit (7)
status-rekisteri eli ”liput” Useimmissa suorittimissa on ALU:un liittyvä rekisteri, jota kutsutaan statusrekisteriksi.
AVR-mikrokontrollerissa se tunnetaan lyhenteellä ”SREG”, ja siinä se koostuu seuraavista
kahdeksasta ”lipusta”, joille jokaiselle on vastaavat ”branch if set” ja ”branch if clear” käskyt:
• Z: ”Zero Flag”. Lippu joka kertoo, oliko viimeksi laskettu tulos tai rekisteri nolla.
• N: ”Negative Flag”. Lippu joka kertoo, oliko viimeksi lasketussa tuloksessa tai rekisterissä bitti-7
päällä.
• C: ”Carry Flag”. Lippu, johon monet käskyt siirtävät tietyn ”pois vuotavan” bitin. (Huom: kyse
ei ole virhetilanteesta, vaan hallitusta tavasta operoida tavua suuremmilla luvuilla.)
• V: “Two’s complement overflow indicator”. Lippu, joka kertoo tiettyjen etumerkillisten
(“signed”) aritmeettisten operaatioiden ylivuotamisesta. Tarkempi käsittely sivuutetaan tällä
kurssilla, mutta löydät aiheesta enemmän tietoa googlettamalla hakusanoilla “Overflow flag”.
• S: N ⊕ V, “For signed tests”. Päällä vain jos joko N tai V-lippu on, mutta ei molemmat yht’aikaa.
• H: ”Half Carry Flag”. Harvoin käytetty lippu, jonka tietyt käskyt asettavat kun bitti-positiosta
kolme siirtyy 1-bitti positioon neljä. Saatetaan tarvita BCD-koodattuja lukuja käsiteltäessä.
• T: “Transfer bit used by BLD and BST instructions”. AVR-spesifinen lisälippu yksittäisten
bittien siirtoon ja talletukseen, mikäli esimerkiksi C-lippu on jo varattu johonkin toiseen
tarkoitukseen.
• I: “Global Interrupt Enable/Disable Flag”. Lippu, joka määrää, voiko CPU:ssa tapahtua
keskeytyksiä, vai ei. Tämä lippu ei sinänsä liity mitenkään aritmeettisiin ehtoihin ja branchien
käyttöön. Keskeytyksistä tulemme puhumaan myöhemmin.
AVR, Liput ja Branchit (8)
Lippujen eksplisiittinen
asetus 0:ksi tai 1:seksi. Kaikki kahdeksan lippua voidaan eksplisiittisesti asettaa joko 0:ksi tai 1:seksi, seuraavankaltaisilla
”CLear” ja ”SEt”-käskyillä:
• Z: ”Zero Flag”. Käskyillä CLZ ja SEZ.
• N: ”Negative Flag”. Käskyillä CLN ja SEN.
• C: ”Carry Flag”. Käskyillä CLC ja SEC.
• V: “Two’s complement overflow indicator”. Käskyillä CLV ja SEV.
• S: N ⊕ V, “For signed tests”. Käskyillä CLS ja SES.
• H: ”Half Carry Flag”. Käskyillä CLH ja SEH.
• T: “Transfer bit used by BLD and BST instructions”. Käskyillä CLT ja SET.
• I: “Global Interrupt Enable/Disable Flag”. Käskyillä CLI ja SEI, joista tulemme puhumaan
enemmän keskeytysten yhteydessä.
Useimmiten liput (tai tarkemmin: jokin osajoukko niistä) kuitenkin asetetaan suorittamalla
jokin aritmeettis-looginen käsky, jonka jälkeen lippujen tilasta voidaan päätellä ko. käskyn tuloksesta
ja usein myös sen operandien alkuperäisistä arvoista monenlaisia asioita.
AVR, Liput ja Branchit (9)
Z-lipun käyttö: BREQ & BRNE Z-lippu kertoo, oliko viimeksi lippuihin vaikuttanut (eli siis muutettu tai testattu) rekisteri:
• Nolla vai ei-nolla. Esimerkiksi DEC-käskyllä tehdyn silmukkalaskurin vähennyksen jälkeen.
• Toisaalta Z-lipusta näkyy myös, onko CP tai CPI-käskyllä (”Compare”) vertaillut arvot yhtä
suuret, koska CP ja CPI eivät tee mitään muuta kuin vähentävät oikeanpuoleisen rekisterin tai
arvon vasemmanpuoleisesta rekisteristä, aivan samoin kuin SUB ja SUBI-käskyt tekisivät, mutta
tallettamatta varsinaista tulosta mihinkään. Sen sijaan ne asettavat ainoastaan liput tämän
poisheitettävän ”haamutuloksen” mukaan, jolloin on selvää, että jos vertailtavat arvot ovat yhtä
suuret, niin ”haamutulos” on nolla, ja Z-lippu tulee päälle.
• AVR-assemblerissa Z-lipun tilan mukaan hyppäävät käskyt kulkevat nimillä BREQ (”Branch if
Equivalent”) ja BRNE (”Branch if Not Equivalent”), ja ohjelmoijan täytyy muistaa käyttää
niitä, vaikka ajatuksena olisikin tarkastella nimenomaan sitä, tuliko jokin rekisteri nollaksi, siten
että BREQ-käskyllä hypätään viimeksi käsitellyn rekisterin ollessa nolla, ja BRNE-käskyllä silloin
taas kun se ei ole nolla.
• Joissakin muissa assemblereissa on yleensä tarjolla vastaavat ”Z-loppuiset synonyymit”,
esimerkiksi x86-assemblerissa JZ (”jump if zero”) = JE (”jump if equal”) ja
JNZ (”jump if not zero”) = JNE (”jump if not equal”).
AVR, Liput ja Branchit (10)
N-lipun käyttö: BRMI & BRPL N-lippu kertoo, oliko viimeksi lippuihin vaikuttaneessa rekisterissä tai tuloksessa ”ylin bitti”
(”most significant bit” eli bitti-7) päällä. Toisin sanoen, se kertoo, oliko tulos:
• ”Negatiivinen” vai ”ei-negatiivinen”. Tällöin ajatellaan, että testattava rekisteri sisältää
positiivisen (ml. nollan!) tai negatiivisen arvon, väliltä (-128 - +127) (”signed byte”). Kahden
komplementtinotaatiossa vain negatiivisissa arvoissa (-1 - -128) on ylin bitti, eli bitti-7 päällä.
Huom! Tarkkaan ottaen AVR-assemblerin BRPL (”Branch if Plus”) on hieman väärin nimetty,
nolla kun ei ole oikeasti sen enempää negatiivinen kuin positiivinenkaan luku, vaan jotain siltä
väliltä. Siitä huolimatta BRPL hyppää, vaikka tulos olisi nollakin. BRMI (”Branch if Minus”) taas
hyppää vain jos tavu (”signed byteksi” tulkittuna) todella on negatiivinen, eli < 0.
• Välillä 128-255, jos tavua ajatellaan aina absoluuttisena, ei-negatiivisena arvona (ns. ”unsigned
byte”).
• Huomaa, että sen enempää CPU kuin assembler-kääntäjäkään eivät välitä tuon taivaallista siitä,
kuinka ohjelmoija on tarkoittanut tavut ”oikeasti” tulkittavan, etumerkillisinä vai merkittöminä
lukuina, ASCII-merkkeinä vaiko mielivaltaisina kahdeksan bitin bittivektoreina. Kun CPU
suorittaa BRMI tai BRPL käskyn, niin ainoa mikä ratkaisee, on se, oliko edellä sorkitun rekisterin
bitti-7 päällä vai ei. Niinpä (näitäkin) käskyjä voi käyttää luovasti, muuhunkin kuin etumerkillisillä
luvuilla operointiin.
AVR, Liput ja Branchit (11)
S-lipun käyttö: BRLT & BRGE S-lippu, joka sisältää aina saman arvon kuin N ⊕ V, (eli XOR N-lipun ja V-lipun arvoista)
on tarpeen silloin kun oikeasti halutaan verrata kahta tavua ajateltuna ”signed-tavuina”
(välillä -128 … +127) jolloin voidaan CP tai CPI –käskyn jälkeen käyttää käskyjä BRLT
(”Branch if Less Than”), joka hyppää vain jos S-lippu on päällä ja BRGE (”Branch if
Greater or Equal”), joka hyppää vain mikäli S-lippu ei ole päällä. Tämän tarkempi
matemaattinen todistus sivuutetaan.
Huom! Yksinkertaisissa sovelluksissamme käytämme paljon useammin absoluuttisia,
ei-negatiivisia arvoja (luuppilaskurit, merkkijonojen pituudet, ym.) kuin lukuja jotka
voisivat saada negatiivisiakin arvoja.
Kyseisissä tapauksissa käskyjä BRLT ja BRGE ei pidä käyttää, ei silloinkaan kun haluaa
toteuttaa esimerkiksi ehdon if(a < b)… tai if(x >= y)…
vaan sen sijaan tulee käyttää haaraumakäskyjä BRNE tai BREQ (jotka riittävät useimpiin
tapauksiin, esimerkiksi silmukan loppuehdoksi) tai sitten käskyjä BRLO tai BRSH
(joista kerrotaan kahden kalvon päästä).
AVR, Liput ja Branchit (12)
C-lipun käyttö: BRCS & BRCC C-lippu (eli ”carry-flag”, suomenkielisissä esityksissä joskus myös ”muistibitti”) on eräänlainen
”tulosten yhdeksäs bitti”, johon monet aritmeettiset tai shift/rotate –tyyppiset käskyt siirtävät talteen
”pois vuotavan” bitin, jota voidaan tarvita myöhemmissä operaatioissa. C-lipun perusteella
haarautuvat mm. käskyt BRCS (”Branch if Carry Set”) ja BRCC (”Branch if Carry Cleared”).
AVR-suorittimessa ainakin seuraavat käskyt vaikuttavat C-lippuun:
• ADD, ADC ja ADIW. Nämä käskyt asettavat C-lipun vain jos summa kasvoi yli 255:n (tavuilla
operoivat ADD ja ADC) tai yli 65535:n (rekisteripareilla, ts. 16-bittisillä sanoilla operoiva ADIW).
Lisäksi käsky ADC summaa vanhan C-lipun mukaan tulokseen, joten sillä voidaan ketjuttaa
useampitavuisten lukujen (siis suurempien kuin 255) yhteenlasku. Joskus sillä voi myös korvata
varsinaisen branch-käskyn käytön.
• SUB, SUBI, SBC, SBCI ja SBIW. Nämä käskyt asettavat C-lipun vain jos vähentäjä on
absoluuttiselta arvoltaan (ajateltuna siis välille 0-255, paitsi käskyllä SBIW) suurempi kuin se
rekisteri josta vähennetään. Käskyissä SBC ja SBCI vähentäjässä on mukana paitsi
oikeanpuoleinen operandi, niin myös C-lipun vanha arvo. Kyseisillä käskyillä saadaan suoritettua
monitavuisten kokonaislukujen vähennyslaskun ”ketjutus”, sillä tässä tapauksessa C-lippu
toimiikin ”lainabittinä” (engl. ”borrow bit”).
(Aiheesta lisää, katso: http://en.wikipedia.org/wiki/Carry_flag )
AVR, Liput ja Branchit (13)
C-lipun käyttö
vertailuoperaatioissa
• Vertailukäskyt CP, CPI ja CPC (engl. ”compare”, useimmissa muissa CPU:issa yleensä
”CMP”) vastaavat edellisen kalvon käskyjä SUB, SUBI ja SBC, eli ne myös asettavat
C-lipun vain jos vähentäjä on absoluuttiselta arvoltaan (ajateltuna siis välille 0-255)
suurempi kuin se rekisteri jonka arvosta vähennetään. (Käskyssä CPC vähentäjässä on
mukana paitsi oikeanpuoleinen operandi, niin myös C-lipun vanha arvo). Ainoana
erona varsinaisiin subtract-käskyihin on siinä, että vertailukäskyt eivät kirjoita
vähennyslaskun varsinaista tulosta vasemmanpuoleisen operandi-rekisteriin, vaan
jättävät sen vanhan arvon ennalleen.
(Lisäksi vertailukäskyissä oikeanpuoleista operandia ei yleensä ajatella ”vähentäjänä”,
vaan vertailtavana arvona.)
• Edellisestä johtuen käskyt BRLO (“Branch if Lower”, unsigned) ja BRSH (“Branch if
Same or Higher”, unsigned) ovat itse asiassa vain käskyjen BRCS (”Branch if Carry
Set”) ja BRCC (”Branch if Carry Cleared”) synonyymejä.
AVR, Liput ja Branchit (14)
C-lipun käyttö
Shift-käskyissä (LSL) AVR-suorittimen käskyt LSL, LSR ja ASR siirtävät tavun bittejä tavua vasemmalle tai oikealle yhden
bitin verran, eli ”shiftaavat” sitä yhdellä (engl. ”shift”). Monissa muissa prosessoreissa (esim. x86)
voidaan tällaisille käskyille antaa shiftattavan rekisterin lisäksi vielä toinenkin operandi, joka kertoo
kuinka monella bitillä rekisteriä shiftataan.
Käsky LSL Rd (”Logical Shift Left”) siirtää rekisterin Rd bitit 0-6 positioihin 1-7, ja laittaa alimman
bitin (eli bitti-0:n) arvoksi nollan.
Toisin sanoen, LSL Rd kertoo rekisterin Rd sisällön kahdella. Tältä osin se siis vastaa C-kielen käskyä
rd <<= 1; (eli rd = rd << 1;) missä muuttuja rd on määritelty joko char rd; tai
unsigned char rd;
Tämän lisäksi LSL Rd laittaa myös rekisterin Rd vanhan ylimmän bitin (bitti-7:n) C-lipun uudeksi
arvoksi, mitä asiaa ei voi C-kielellä ilmaista.
Huomaa, että käskyn LSL Rd jälkeen C-lippu kertoo sen, oliko rekisterissä Rd ennen käskyä ylin bitti
(bitti-7) päällä vai ei.
AVR, Liput ja Branchit (15)
C-lipun käyttö
Shift-käskyissä (LSR) Käsky LSR Rd (”Logical Shift Right”) siirtää rekisterin Rd bitit 7-1 positioihin 6-0, laittaa
bitti-7:ään nollan, ja siirtää alimman bitin (eli bitti-0:n) C-lipun uudeksi arvoksi:
Toisin sanoen, se jakaa rekisterin Rd sisällön kahdella, mikäli sitä ajatellaan absoluuttisena
arvona, välillä 0-255. Se siis suunnilleen vastaa C-kielen käskyä rd >>= 1;
(eli rd = rd >> 1;) mikäli tätä ennen on määrittely unsigned char rd;
Itse C-lipun asetusta ei voi kuitenkaan C-kielellä ilmaista.
Huomaa, että käskyn LSR Rd jälkeen C-lippu kertoo sen, oliko Rd ennen käskyä
parillinen vai pariton.
AVR, Liput ja Branchit (16)
C-lipun käyttö
Shift-käskyissä (ASR) AVR-suorittimen käsky ASR (”Arithmetic Shift Right”) toimii muuten samoin kuin LSR
(”Logical Shift Right”), paitsi että se kopioi operandirekisterin vanhan bitti-7:n arvon
myös sen uudeksi bitti-7:ksi:
Toisin sanoen, ASR Rd jakaa rekisterin Rd sisällön kahdella, mikäli sitä ajatellaan ”signed
byte”-arvona, välillä (-128 … +127). Se siis vastaa suunnilleen C-kielen käskyä rd >>= 1;
(eli rd = rd >> 1;) mikäli tätä ennen on määrittely char rd; jolla kerrotaan
C-kääntäjälle, että muuttujaa rd halutaan käsitellä ”signed byte”:nä.
Itse C-lipun asetusta ei voi kuitenkaan C-kielellä ilmaista.
Huomaa, että käskyn ASR Rd jälkeen C-lippu kertoo sen, oliko Rd ennen käskyä
parillinen vai pariton.
Negatiivisten lukujen ilmaisemisesta ”kahden komplementtinotaatiolla”, kts. jokin toinen
kalvo tai kalvosarja.
AVR, Liput ja Branchit (17)
C-lipun käyttö
Rotate-käskyissä, (ROL & ROR) AVR-suorittimen käsky ROL (”Rotate Left”) toimii muuten samoin kuin LSL
(”Logical Shift Left”), paitsi että se sijoittaa operandirekisterin alimpaan bittiin nollan
sijasta C-lipun vanhan arvon, C-lipun uuden arvon tullessa vanhasta bitti-7:stä ja muiden
bittien siirtyessä yhden vasemmalle:
AVR-suorittimen käsky ROR (”Rotate Right”) toimii muuten samoin kuin LSR
(”Logical Shift Right”), paitsi että se sijoittaa operandirekisterin ylimpään bittiin nollan
sijasta C-lipun vanhan arvon, C-lipun uuden arvon tullessa vanhasta bitti-0:sta ja muiden
bittien siirtyessä yhden oikealle:
AVR, Liput ja Branchit (18)
Loogiset bittioperaatiot: AND Kahden operandin käskyt AND Rd,Rr ja ANDI Rd,K asettavat rekisterin Rd uudeksi
arvoksi bittioperaation(Rd ∧ Rr) (käskyllä AND) tai (Rd ∧ K) (käskyllä ANDI)
tuloksen, sovellettuna ”rinnakkain” kuhunkin kahdeksaan bittipositioon erikseen. Merkki ∧
tarkoittaa ”loogista konjunktiota” eli ”ja-operaatiota”, jonka totuustaulu on:
Toisin sanoen, tulosrekisteriin Rd tulee ykkösbitti vain niihin kohtiin, joissa kohdissa sekä
tulosrekisterin Rd vanhassa arvossa, että oikeanpuoleisessa operandissa (Rr tai K)
on molemmissa samassa kohtaa ykkösbitti päällä.
Yleinen käyttötarkoitus AND:ille on yhden tai useamman bitin poisto tai esillemaskaus
rekisteristä.
A B (A ∧ B)
0 0 0
0 1 0
1 0 0
1 1 1
AVR, Liput ja Branchit (19)
AND-operaatio, esimerkki 1: Yleinen käyttötarkoitus AND-operaatiolle, on tiettyjen bittien esille ”maskaus”.
Halutessamme esimerkiksi nopeasti (siis ”fast and dirty”) selvittää, onko rekisterissä oleva
tavu ASCII-arvoksi tulkittuna pieni vai iso kirjain, saamme asian selville maskaamalla sen
positiossa viisi olevan bitin esiin, mikä tapahtuu ”ändäämällä” se heksadesimaalisen arvon
0x20 kanssa, esimerkiksi operaatiolla ANDI R0,0x20 jonka jälkeen, mikäli oletamme,
että rekisterissä R0 oli arvo 0x6B eli pieni koo-kirjain, näemme että:
Tämän jälkeen voimme esimerkiksi käskyllä BRNE PIENET_KIRJAIMET hypätä
rutiinin sellaiseen kohtaan, jossa käsitellään pienet kirjaimet. Huomaa kuitenkin, että
rekisterin R0 arvo muuttui yllä 0x20:ksi, joten ennen maskausta se kannattaa kopioida
toiseen rekisteriin talteen, tai tehdä maskaus toisinpäin, tyyliin:
LDI Rmaski,0x20 ja AND Rmaski,Rkirjain ja BRNE HYPPY
(Huom! tämän voi suorittaa nopeamminkin AVR-spesifisillä käskyillä SBRC tai SBRS).
7 6 5 4 3 2 1 0 Heksana
R0 (vanha arvo) 0 1 1 0 1 0 1 1 0x6B (= ’k’)
”Maski” (oik.puoleinen operandi) 0 0 1 0 0 0 0 0 0x20
R0 (uusi arvo) 0 0 1 0 0 0 0 0 0x20
AVR, Liput ja Branchit (20)
AND-operaatio, esimerkki 2: Toinen yhtä yleinen käyttötarkoitus AND-operaatiolle, on tiettyjen bittien ”poismaskaus”.
Halutessamme esimerkiksi konvertoida rekisterissä olevan arvon (josta toivottavasti (!)
tiedämme jo, että se ASCII-arvoksi tulkittuna on kirjain, iso tai pieni) isoksi kirjaimeksi
(eli ”UPPERCASEKSI”), mikäli se ei sitä vielä ole, saamme tehtyä sen yhdellä käskyllä,
”ändäämällä” sen arvon 0x20 komplementin eli 0xDF kanssa, operaatiolla ANDI R0,0xDF
(tai assembler-aikaista komplementtioperaatiota hyväksikäyttäen: ANDI R0,~0x20)
Huomaa että tulos olisi sama, mikäli rekisterin R0 arvo olisi alunperinkin ollut 0x4B.
Toisin sanoen, mikäli kirjain on valmiiksi ”uppercasena”, kyseinen AND-operaatio ei muuta
sitä lainkaan.
Käsky CBR Rd,K (Clear Bits in Register) on vain lyhennysmerkintä käskylle
ANDI Rd,~K eli ylläoleva operaatio voitasiin tehdä myös sillä: CBR R0,0x20
7 6 5 4 3 2 1 0 Heksana
R0 (vanha arvo) 0 1 1 0 1 0 1 1 0x6B (= ’k’)
”Maski” (oik.puoleinen operandi) 1 1 0 1 1 1 1 1 0xDF (= ~0x20)
R0 (uusi arvo) 0 1 0 0 1 0 1 1 0x4B (= ’K’)
AVR, Liput ja Branchit (21)
Loogiset bittioperaatiot: OR Kahden operandin käskyt OR Rd,Rr ja ORI Rd,K asettavat rekisterin Rd uudeksi
arvoksi bittioperaation(Rd ∨ Rr) (käskyllä OR) tai(Rd ∨ K) (käskyllä ORI) tuloksen,
sovellettuna ”rinnakkain” kuhunkin kahdeksaan bittipositioon erikseen. Merkki ∨
tarkoittaa ”loogista disjunktiota”, eli tavallista, ei-poissulkevaa ”tai-operaatiota”:
Toisin sanoen, tulosrekisteriin Rd tulee ykkösbitti vain niihin kohtiin, joissa kohdissa joko
tulosrekisterin Rd vanhassa arvossa tai oikeanpuoleisessa operandissa (Rr tai K),
tai molemmissa operandeissa, on ykkösbitti päällä.
Yleinen käyttötarkoitus OR-operaatiolle on asettaa rekisteristä yksi tai useampi bitti päälle
(mikäli ne eivät ole jo valmiiksi). Sitä voi käyttää myös tarkistamaan esimerkiksi sen, että
useamman rekisterin joukossa kaikkien arvot ovat nollia.
A B (A ∨ B)
0 0 0
0 1 1
1 0 1
1 1 1
AVR, Liput ja Branchit (22)
OR-operaatio, esimerkki: Yleinen käyttötarkoitus OR-operaatiolle, on tiettyjen bittien asettaminen päälle rekisterissä,
riippumatta siitä, ovatko ne päällä jo valmiiksi.
Halutessamme esimerkiksi konvertoida rekisterissä olevan arvon (josta toivottavasti (!)
tiedämme jo, että se ASCII-arvoksi tulkittuna on kirjain, iso tai pieni) pieneksi kirjaimeksi
(eli ”lowercaseksi”), mikäli se ei sitä vielä ole, saamme tehtyä sen yhdellä käskyllä,
”oraamalla” sen arvon 0x20 kanssa, operaatiolla ORI R0,0x20
7 6 5 4 3 2 1 0 Heksana
R0 (vanha arvo) 0 1 0 0 1 0 1 1 0x4B (= ’K’)
”R0:n päälle orattava arvo”
(oikeanpuoleinen operandi)
0 0 1 0 0 0 0 0 0x20
R0 (uusi arvo) 0 1 1 0 1 0 1 1 0x6B (= ’k’)
Huomaa että tulos olisi sama, mikäli rekisterin R0 arvo olisi alunperinkin ollut 0x6B.
Toisin sanoen, mikäli kirjain on valmiiksi ”lowercasena”, kyseinen OR-operaatio ei muuta
sitä lainkaan.
AVR, Liput ja Branchit (23)
Loogiset bittioperaatiot: EOR Looginen bittioperaatio EOR Rd,Rr asettaa rekisterin Rd uudeksi arvoksi
bittioperaation(Rd ⊕ Rr) tuloksen, sovellettuna ”rinnakkain” kuhunkin kahdeksaan
bittipositioon erikseen. Merkki ⊕ tarkoittaa ”eksklusiivista disjunktiota” eli
”poissulkevaa tai-operaatiota” (tunnetaan paremmin nimellä XOR), jonka totuustaulu on:
Toisin sanoen, tulosrekisteriin Rd tulee ykkösbitti vain niihin kohtiin, joissa kohdissa
tulosrekisterin Rd vanhan arvon ja oikeanpuoleisen operandin Rr bitit eroavat toisistaan
(eli kun toisessa on nolla, niin toisessa on ykkönen). Ja nollabitti tulee taas niihin kohtiin,
joissa operandien biteillä on samat arvot.
”Xoraamalla” voidaan tehdä monenlaisia asioita, esimerkiksi kryptata ja dekryptata, laskea
erilaisia hasheja ja tarkistussummia, jne.
A B (A ⊕ B)
0 0 0
0 1 1
1 0 1
1 1 0
AVR, Liput ja Branchit (24)
Loogiset operaatiot (AND,OR,
EOR) ja liput
Loogiset bittioperaatiot AND, ANDI, OR, ORI ja EOR asettavat status-bitit seuraavasti:
Toisin sanoen, I-, T-, H- ja C-lippujen arvoihin ei kosketa, V-lippu laitetaan aina nollaksi,
ja Z-lipun arvo asetetaan sen perusteella tuleeko tulokseksi (eli Rd:n uudeksi arvoksi)
nolla vai ei, ja lisäksi tuloksen bitti-7 kopioidaan S- ja N-lippuihin.
• Käsky CBR Rd,K (Clear Bits in Register) on vain lyhennysmerkintä käskylle
ANDI Rd,~K eli sekin asettaa liput yllä olevan kaavion mukaisesti.
• Käsky TST Rd on vain lyhennysmerkintä käskylle AND Rd,Rd joten se pitää
rekisterin Rd sisällön ennallaan (mieti!), mutta asettaa vain Z-lipun sekä S- ja N-lippujen
arvot ylläesitetyllä tavalla. Sitä käytetään usein varmistamaan että kyseisten lippujen
arvot heijastavat juuri halutun rekisterin sisältöä ennen branch-käskyn suoritusta.
AVR, Liput ja Branchit (25)
Lippujen ”turmeltumisesta” Lippujen ja branchien käytössä on tärkeää varmistua siitä, että branch-käskyn kohdalla
liput tosiaan heijastavat juuri sen rekisterin tai vertailuoperaation tulosta, joka ohjelmoijalla
on mielessä. Tässä tapahtuu helposti virheitä, jos esimerkiksi CP tai CPI-käskyn ja
sen seuraksi tarkoitetun branch-käskyn (esimerkiksi BREQ) väliin lisätään vahingossa jokin
käsky, joka ehtii muuttaa lippujen arvot. Siksi vertailu- ja sitä vastaava branch-käsky onkin
yleensä parasta sijoittaa välittömästi peräkkäin.
Kaikki käskyt eivät kuitenkaan muuta lippujen arvoa (esimerkiksi branch-käskyt itsessään),
ja tätä voidaan käyttää hyväksi esimerkiksi testaamalla tuloksesta useampia eri ehtoja
perättäisillä branch-käskyllä.
Esimerkiksi seuraavat kolme käskyä tarkistavat että rekisteri R0:n arvo on välillä 1-127:
TST R0 ;; Aseta liput rekisterin R0 mukaan.
BRMI LIIAN_ISO ;; Hyppää jonnekin jos sen arvo > 127
BREQ LIIAN_PIENI ;; Hyppää jonnekin jos sen arvo on nolla.
AVR, Liput ja Branchit (26)
Mitkä käskyt eivät koske
lippuihin? AVR-suorittimessa muun muassa seuraavat käskyt jättävät liput täysin rauhaan:
• Kaikki branch-käskyt sekä kaikki muutkin hyppykäskyt (erilaiset JMP ja CALL-käskyt),
samoin kuin alirutiinista-paluukäsky (RET).
• Kaikki load-käskyt (LD, LDS, LPM) joihin kuuluu myös LDI (”Load Immediate”).
• Kaikki store-käskyt (ST, STS, SPM). Samoin pinokäskyt PUSH ja POP.
• Molemmat move-käskyt (MOV ja MOVW) . Samoin SWAP joka vaihtaa tavun ylä- ja ala-
nybblen (neljän bitin ryhmän) keskenään.
• Käsky CPSE Rd,Rr joka skippaa seuraavan käskyn mikäli rekistereiden Rd ja Rr
arvot ovat yhtäsuuret. (Tätä voi käyttää usein korvaamaan BREQ-käskyä.) Samoin sen
kaltaiset käskyt SBRC Rr,b ja SBRS Rr,b.
• Kaikki I/O-käskyt (IN, OUT, CBI, SBI, SBIC, SBIS).
Huom! Se, millaiset käskyt säilyttävät liput ja mitkä sotkevat ne, on hyvin
prosessoriperhekohtaista, ja jos siirryt koodaamaan jonkin muun arkkitehtuurin
assembleria, niin asia on aina tarkistettava sen omista käsikirjoista.
AVR, Liput ja Branchit (27)
Muita yllätyksiä lippujen
kanssa? Jotkut käskyt toimivat lippujen kanssa toisin kuin ehkä ensi alkuun olettaisi:
• Käskyt INC ja DEC eivät vaikuta C-lipun arvoon milloinkaan, mikä on usein
hyödyllistäkin. Käytä käskyjä LDI Rvapaa,1 ja ADD Rd,Rvapaa silloin kun
haluat että C-lippu (eikä ainoastaan Z-lippu) tulee päälle kun rekisterin Rd arvo kiertyy
ympäri (engl. ”wraps around”) arvosta 255 arvoon nolla. Käskyä DEC Rd riittää
vastaavanlaista käyttöä varten korvaamaan käsky SUBI Rd,1.
• Vähennyksessä carry-bitin kera, eli käskyissä CPC, SBC ja SBCI Z-lippu asetetaan
nollaksi (eli ”clearataan”), mikäli tulos oli nollasta poikkeava, mutta sen arvo ei
muutu, mikäli tulokseksi tulee nolla. Ideana on, että ensimmäiseksi CP, CPI, SUB tai
SUBI–käskyllä on vähennetty alimmat tavut toisistaan, mikä on asettanut Z-lipun
päälle, mikäli alimmat tavut olivat samat, ja sen jälkeen, koko vertailu- tai
vähennyslaskuketjutuksen jälkeen Z-lippu on päällä vain mikäli
vertailtavat/vähennettävät olivat samat (kaikilta tavuiltaan).
AVR, Liput ja Branchit (28)
Muita yllätyksiä lippujen
kanssa? (jatkoa…)
• Rekisterin komplementointikäsky COM Rd asettaa C-lipun arvoksi aina ykkösen.
• Rekisterin nollauskäsky CLR Rd, joka on oikeasti EOR Rd,Rd asettaa Z-lipun
arvoksi aina ykkösen, ja S-, V- ja N-lippujen arvoksi aina nollan. C-lippuun se ei koske.
Jos haluat nollata rekisterin koskematta lippuihin ollenkaan, käytä käskyä LDI Rd,0