avr liput ja_branchit

29
AVR-mikrokontrollerien Liput ja Branch-käskyt (ehdolliset haaraumat) A. Karttunen, Metropolia, Mikroprosessoritekniikka, 14.-15. marraskuuta 2011

Upload: tili-poistettu

Post on 05-Jul-2015

179 views

Category:

Documents


0 download

DESCRIPTION

A short introduction (in Finnish) to using the processor status register flags, branch statements and boolean bit-statements AND, OR, EOR of Atmel AVR (8-bit) microcontrollers. Note the slight mistake of using R0 in examples involving for example CPI statement. (Only registers R16-R31 are actually allowed with instructions where the other operand is immediate!)

TRANSCRIPT

Page 1: Avr liput ja_branchit

AVR-mikrokontrollerien

Liput ja Branch-käskyt

(ehdolliset haaraumat)

A. Karttunen,

Metropolia,

Mikroprosessoritekniikka,

14.-15. marraskuuta 2011

Page 2: Avr liput ja_branchit

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).

Page 3: Avr liput ja_branchit

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.

Page 4: Avr liput ja_branchit

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”).

Page 5: Avr liput ja_branchit

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”).

Page 6: Avr liput ja_branchit

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.

Page 7: Avr liput ja_branchit

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.

Page 8: Avr liput ja_branchit

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.

Page 9: Avr liput ja_branchit

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.

Page 10: Avr liput ja_branchit

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”).

Page 11: Avr liput ja_branchit

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.

Page 12: Avr liput ja_branchit

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ä).

Page 13: Avr liput ja_branchit

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 )

Page 14: Avr liput ja_branchit

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ä.

Page 15: Avr liput ja_branchit

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.

Page 16: Avr liput ja_branchit

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.

Page 17: Avr liput ja_branchit

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.

Page 18: Avr liput ja_branchit

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:

Page 19: Avr liput ja_branchit

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

Page 20: Avr liput ja_branchit

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

Page 21: Avr liput ja_branchit

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’)

Page 22: Avr liput ja_branchit

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

Page 23: Avr liput ja_branchit

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.

Page 24: Avr liput ja_branchit

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

Page 25: Avr liput ja_branchit

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.

Page 26: Avr liput ja_branchit

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.

Page 27: Avr liput ja_branchit

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.

Page 28: Avr liput ja_branchit

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).

Page 29: Avr liput ja_branchit

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