Download as pdf or txt
Download as pdf or txt
You are on page 1of 22

AVR-mikrokontrollerien

Assembler-ohjelmointi,
kertaus
A. Karttunen,
Metropolia,
Mikroprosessoritekniikka,
2. joulukuuta 2011
AVR-kertaus (1)
AVR-mikrokontrollerit
• Atmelin AVR-sarjan mikrokontrollerit sisältävät 8-
bittisen, melko tyypillisen ”RISCimäisen”
keskussuorittimen (CPU:n), joissa pienehkö määrä
SRAM-muistia on integroitu itse piiriin mukaan, ja
I/O-nastoissa on monia erityisominaisuuksia, jotka
helpottavat erilaisten laitteiden ohjausta.
• Käskyjen suoritus on ”kevyesti liukuhihnoitettu”
(liukuhihnassa vain kaksi vaihetta, ”fetch” ja
”execute”), vailla sofistikoituneempia piirteitä kuten
branch-käskyjen ennakointia, superskalaarisuutta,
yms.
AVR-kertaus (2)
AVR:n CPU:n rakenne
Oheinen kaavio esittää AVR ATmega32-mikrokontrollerin
CPU-osaa. Siitä assembler-ohjelmoijalle näkyvät vain:

• Erilliset muistit ohjelmakoodille (”Program Flash”) ja


työdatalle (”SRAM”, eli staattinen RAM-muisti).
• 32 kahdeksanbittistä yleisrekisteriä (”General Purpose
Registers”).
• Ohjelmalaskuri eli PC (”Program Counter”), joka sisältää
seuraavaksi ohjelmamuistista haettavan käskyn osoitteen.
• Pino-osoitin eli SP (”Stack Pointer”), joka osoittaa
SRAM:ista väliaikaista pinomuistia varten varattuun osaan.
• Status-rekisteri (”Status Register”, eli SREG), jonka
sisältämät liput (engl ”flags”) ovat erittäin oleellisia ohjelman
suorituksen kannalta.
• ”Instruction register” ja ”Instruction decoder” liittyvät
CPU:n sisäiseen toteutukseen, eivätkä ne näy assembler-
ohjelmoijalle (ei tule kokeeseen!), sen enempää kuin ALU:n
eli aritmeettis-loogisen yksikön tarkempi toteutuskaan.
AVR-kertaus (3)
Rekisterit
• AVR:issä on 32 kahdeksanbittistä
yleisrekisteriä, R0-R31, jotka ovat siis
prosessorin sisäisiä, yhden tavun kokoisia
”muistipaikkoja”. Näistä kuutta viimeistä
voidaan käyttää myös pareittain 16-
bittisinä indeksirekistereinä X, Y ja Z,
joita käytetään epäsuorissa
muistiosoituksissa.

• Rekistereitä R0-R15 ei voi käyttää


seuraavissa käskyissä: ANDI, CPI, LDI,
ORI, SBCI ja SUBI.

XL
XH
YL
YH
ZL
ZH
AVR-kertaus (4)
Konekielen käskyt
• Konekielinen ohjelma koostuu useista, toinen toisensa jälkeen erittäin nopeasti suoritettavista
käskyistä.
• Käskyt voivat operoida joko CPU:n rekistereillä ja/tai sen status-rekisterin lipuilla, siihen
dataväylän kautta kytketyn I/O-avaruuden I/O-rekistereillä tai saman väylän kautta kytketyn
staattisen RAM-muistin (SRAM) muistipaikoilla. Lisäksi hyppykäskyt vaihtavat tai saattavat
vaihtaa ohjelmalaskurin (PC) arvon joksikin muuksi kuin normaalisti seuraavaksi suoritettavan
välittömästi seuraavan käskyn osoitteen.
• Useimmat käskyt ovat erittäin yksinkertaisia, esimerkiksi: ”kopioi rekisterin sisältö toiseen”, ”laske
kaksi 8-bittistä lukua yhteen”, ”vähennä rekisterin sisältöä yhdellä”, ”vertaile kahden rekisterin
sisältöä”, ”hyppää toiseen kohtaa ohjelmassa mikäli edellisen vähennyslaskun tulos oli nolla, tai
vertailtavat rekisterit sisälsivät saman arvon”.
• AVR-mikrokontrollereissa käskyt ovat vakiolevyisiä, 16-bittiä (paitsi LDS, STS, CALL ja JMP-
käskyjen tapauksessa 32-bittiä, kun käskyn leveyteen lasketaan mukaan itse käskykoodia seuraava
16-bitin osoite).
• AVR, joka käyttää Harvard-arkkitehtuuria, lukee käskyt erillisen väylän kautta ohjelmamuistista,
yksi kuudentoista bitin sana kerrallaan.
AVR-kertaus (5)
ALU-käskyt, Aritmeettiset
Tärkeimmän ryhmän käskyjä muodostavat ne, joihin sisältyy jokin aritmeettinen
tai looginen laskutoimitus, ja jotka (useimmiten) asettavat statusrekisterin liput
tuloksen mukaan. Nämä voidaan jakaa seuraaviin ryhmiin:

• Aritmeettiset: Lisäys ja vähennys yhdellä: INC ja DEC, 8-bittisen luvun


etumerkin vaihto kahden komplementtijärjestelmässä: NEG, yhteenlasku:
ADD, ADC ja ADIW, vähennyslasku SUB, SUBI, SBC, SBCI ja SBIW sekä
kertolasku: MUL, MULS, MULSU, FMUL, FMULS ja FMULSU.

• Vertailu- ja testauskäskyt. Näistä edelliset CP, CPI ja CPC (”compare”,


”compare (against) immediate” ja ”compare with carry”) toimivat muuten
samoin kuin vähennyskäskyt SUB, SUBI ja SBC, paitsi että ne asettavat
ainoastaan liput, mutta eivät talleta vähennyslaskun varsinaista tulosta
minnekään. Testauskäskyllä TST Rd voi tarkistaa vaikkapa onko rekisterin
arvo nolla, esimerkiksi juuri ennen BREQ- tai BRNE-hyppyä.
AVR-kertaus (6)
ALU-käskyt, Loogiset
• Loogiset käskyt COM, AND, OR ja EOR toteuttavat Boolen operaatiot
”ei” (siis komplementointi, ”COMplement”), ”ja”, ”tai” sekä ”ehdoton
tai” (eli ”eXclusive OR”, muissa CPU:issa useimmiten nimellä XOR).

• Käsky COM haluaa vain yhden rekisterioperandin (tyyliin COM Rd),


jossa kyseisessä rekisterissä siis jokainen 1-bitti vaihtuu nollaksi, ja
jokainen 0-bitti ykköseksi.

• Käskyt AND, OR ja EOR haluavat kaksi rekisterioperandia, ja ne


suorittavat kyseisen Boolen operaation kummankin rekisterin
vastaavien bittien välillä, ja kirjoittavat tuloksen vasemmanpuoleisen
rekisterin vanhan arvon päälle. Huomaa, että kunkin bitti-position (0-
7) tulos riippuu vain saman kohdan bitin vanhoista arvoista
kummassakin rekisterissä, mutta on täysin riippumaton siitä, mitä
vaikkapa sen viereisissä bittipositioissa on. (Ns. ”bit-level parallelism”).
AVR-kertaus (7)
ALU-käskyt, Shift & Rotate
• Käskyt LSL Rd (”Logical Shift Left”), LSR Rd (”Logical
Shift Right”) ja ASR Rd (”Arithmetic Shift Right”) siirtävät
rekisterin Rd bittejä vasemmalle tai oikealle yhden bitin verran,
eli ”shiftaavat” sitä yhdellä (engl. ”shift”, C-kielessä << tai
>>). ”Ulosvaluva” bitti siirtyy C-lippuun. Aritmeettisesti
tulkittuna rekisterin sisältö joko kerrotaan tai jaetaan kahdella.
• Käskyt ROL Rd (”Rotate Left”) ja ROR Rd (”Rotate
Right”) toimivat muuten samoin kuin LSL ja LSR, paitsi että
ne täyttävät rekisterin Rd alimman/ylimmän bitin nollan
sijasta C-lipun vanhalla arvolla.
• Käsky SWAP Rd vaihtaa rekisterin Rd ”nybblet” eli neljän
bitin kokoiset ala- ja yläpuoliskon päittäin keskenään. Se ei
koske statusrekisterin lippuihin ollenkaan.
AVR-kertaus (8)
ALU-käskyt, Lippujen asetus

• Useimmat aritmeettisloogiset käskyt asettavat ainakin jotkin lipuista. Katso


aiheesta tarkemmin oma kalvosarjassa ”Liput ja Bränchit”, kalvosta 7
eteenpäin:

http://users.metropolia.fi/~anttijka/AVR/AVR_liput_ja_branchit.pdf
AVR-kertaus (9)
Siirtokäskyt
• Käsky MOV Rd,Rs kopioi rekisterin Rs (lähderekisteri
eli ”source”) sisällön rekisteriin Rd (kohderekisteri eli
”destination”).

• Käsky MOVW Rd+1:Rd,Rs+1:Rs kopioi yhdellä


kertaa kahden rekisterin arvot toisiin kahteen rekisteriin.
Kummankin rekisteriparin rekisterien pitää olla
perättäiset, ja siten että Rs on jokin parillisista
rekistereistä (R0, R2, … R30) ja vastaavasti myös Rd on
jokin parillisista rekistereistä (R0, R2, … R30).
Esimerkiksi käsky MOVW YH:YL,XH:XL kopioi
indeksirekisterin X (siis sen puolikkaat, XL:n ja XH:n)
yhdellä kertaa indeksirekisteriin Y (siis sen puolikkaisiin
YL:ään ja YH:hon).
AVR-kertaus (10)
Latauskäskyt, LDI
• Käsky LDI Rd,K asettaa rekisterin Rd (missä Rd:n
pitää olla R16 tai suurempi AVR:n toteutusrajoitusten
takia) sisällöksi tavun K. Assembler-lähdekoodissa tavu K
voi olla paitsi negatiivinen luku välillä -1 - -128 tai ei-
negatiivinen luku välillä 0-255, niin myös esimerkiksi
heksadesimaalinen arvo väliltä 0x00 – 0xFF, tai jokin
Ascii-merkki tyyliin ’A’, ’b’ tai ’@’.
• Sillä voi myös alustaa jonkin indeksirekistereistä X, Y tai Z
osoittamaan tiettyä .dseg-osiossa määriteltyä
muistiosoitetta, tyyliin:
LDI XL,low(OMABUF) ;; Osoitteen alatavu XL:ään,
LDI XH,high(OMABUF);; ... ja ylätavu YL:ään.
AVR-kertaus (11)
Lataus- ja Talletuskäskyt,
LDS & STS
• Käsky LDS Rd,M lataa datamuistiavaruudesta
osoitteesta M yhden tavun rekisteriin Rd. Yleensä
M on assembler-koodissa jokin symbolinen osoite,
tyyliin: LDS Rd,OMAMUISTIPAIKKA
missä OMAMUISTIPAIKKA on määritelty
.dseg-osiossa esimerkiksi tyyliin:
OMAMUISTIPAIKKA: .byte 1

• Vastaavasti käsky STS M,Rd tallettaa rekisterin Rd


sisällön muistipaikkaan M.
AVR-kertaus (12)
Latauskäskyt, LD
• Käsky LD Rd,<epäsuora_viittaus> lataa
rekisteriin Rd datamuistiavaruudesta yhden tavun
indeksirekisterin X, Y tai Z osoittamasta paikasta.
Tässä <epäsuora_viittaus> tarkoittaa
jotakin muodoista:
– X, Y, Z (normaali epäsuora viittaus dataan, eli ”Data
Indirect Addressing”) tai
– X+, Y+, Z+ (post-increment moodi, jossa
indeksirekisteriä automaattisesti lisätään yhdellä tavun
latauksen jälkeen) tai
– -X, -Y, -Z (pre-decrement moodi, jossa indeksirekisteriä
automaattisesti vähennetään yhdellä ennen tavun latausta
muistista).
AVR-kertaus (13)
Tallennuskäskyt, ST
• Käsky ST <epäsuora_viittaus>,Rd tallettaa
(engl. ”store”) rekisterin Rd sisällön data-avaruuteen
indeksirekisterin X, Y tai Z osoittamaan paikkaan. Tässä
<epäsuora_viittaus> tarkoittaa jotakin
muodoista:

– X, Y, Z (normaali epäsuora viittaus dataan, eli ”Data Indirect


Addressing”) tai
– X+, Y+, Z+ (post-increment moodi, jossa indeksirekisteriä
automaattisesti lisätään yhdellä tavun tallennuksen jälkeen) tai
– -X, -Y, -Z (pre-decrement moodi, jossa indeksirekisteriä
automaattisesti vähennetään yhdellä ennen tavun tallennusta
muistiin).
AVR-kertaus (14)
Lataus- ja tallennuskäskyt,
LDD & STD
• Käsky LDD Rd,<epäsuora_viittaus_siirteellä>
lataa rekisteriin Rd datamuistiavaruudesta yhden tavun,
indeksirekisterin Y tai Z sisällön ja käskyssä annetun ns. siirteen
(engl. ”displacement”) summan osoittamasta paikasta.
Esimerkiksi käsky LDD R0,Y+1 lataa rekisteriin R0 yhden
tavun muistipaikasta, jonka osoite on yhtä suurempi kuin
indeksirekisterin Y sisältö.
• Käsky STD <epäsuora_viittaus_siirteellä>,Rd
tallettaa rekisterin Rd sisällön datamuistiavaruuteen,
indeksirekisterin Y tai Z sisällön ja käskyssä annetun ns. siirteen
(engl. ”displacement”) summan osoittamaan paikkaan.
Esimerkiksi käsky STD R0,Z-3 tallettaa rekisterin R0 sisällön
muistipaikkaan, jonka osoite on kolmea pienempi kuin
indeksirekisterin Z sisältö.
AVR-kertaus (15)
Lataus ohjelmamuistista, LPM
• Käsky LPM Rd,Z tai LPM Rd,Z+ lataa rekisteriin Rd
ohjelmamuistin ”Z:nnen tavun” (siis ohjelmamuistin osoitteesta
0x0000 lähtien, jossa sijaitsevat sen ”nollas” ja ensimmäinen tavu, jne.,
aina kaksi tavua kussakin ohjelmamuistipaikassa). Jälkimmäisessä
osoitusmuodossa indeksirekisteriä Z lisätään vielä automaattisesti
yhdellä latauksen jälkeen.
• Mikäli Z-rekisteri (ennen LPM-käskyn käyttöä) alustetaan osoittamaan
.cseg-sektiossa annettuun ohjelmamuistin osoitteeseen, pitää se
samalla muuntaa tavuosoitteeksi, kertomalla se kahdella. Assembler
osaa tehdä muunnoksen käännösaikana ekspressiolla << (eli C-kielestä
tuttu ”shift left”), tähän tyyliin:

LDI ZH, high(MJONO << 1) ; Alusta Z:n ylätavu


LDI ZL, low(MJONO << 1) ; … ja alatavu
AVR-kertaus (16)
Pinokäskyt, PUSH & POP
• Käsky PUSH Rd tallettaa rekisterin Rd sen hetkisen sisällön SRAM-
työmuistiin, CPU:n oman pino-osoittimen eli SP:n (”Stack Pointer”)
osoittamaan paikkaan, jonka jälkeen SP:tä vähennetään yhdellä. (Pino
”kasvaa negatiiviseen suuntaan” ja se osoittaa ensimmäistä vapaata
paikkaa viimeksi pinoon pushatun ”päällä”).
• Käsky POP Rd lataa rekisterin Rd uudeksi sisällöksi yhden tavun
SRAM-työmuistista, SP:n osoittamasta paikasta, sen jälkeen kun sitä
on ensin lisätty yhdellä. (Pino ”kutistuu positiiviseen suuntaan”).
• Pino-osoitin voidaan alustaa ohjelman alussa seuraavilla käskyillä, jotka
asettavat sen osoittamaan aivan käytettävissä olevan RAM-työmuistin
loppupäähän:

LDI R16,high(RAMEND)
OUT SPH,R16
LDI R16,low(RAMEND)
OUT SPL,R16
AVR-kertaus (17)
Pinon käyttö, Yleisimmät virheet
Pinoa käytettäessä yleisimmät ohjelmavirheet ovat:
• Pinon ylivuoto, ”Stack Overflow”. Syy: yleensä liikaa pusheja
suhteessa poppien määrään, tai esimerkiksi karannut rekursio.
• Pinon alivuoto, ”Stack Underflow”. Syy: yleensä liikaa poppeja
suhteessa pushien määrään.
• Pinon päällekirjoitushaavoittuvuus, eli ”Stack buffer overflow”,
mikä tarkoittaa sitä, että pinosta varattuun väliaikaiseen bufferiin (joka
on varattu esim. jotakin merkkijonoa varten) pystyy kirjoittamaan
enemmän dataa kuin sinne itse asiassa mahtuu, eikä ohjelma tarkista
mahtumista ajon aikana, joten ylimäärädatalla voidaan ylikirjoittaa
esimerkiksi pinossa oleva paluuosoite tai vastaava kriittinen tieto.
Yleinen huolimattomasti kirjoitetuissa C-kielisissä ohjelmissa.
• Aiheesta tarkemmin, katso kalvosarjasta
http://users.metropolia.fi/~anttijka/AVR/AVR_pino_ja_alirutiinit.pdf
kalvot 14-16.
AVR-kertaus (18)
Hyppykäskyt, ehdottomat
• Käskyt JMP OHJELMAOSOITE ja RJMP OHJELMAOSOITE
vaihtavat ohjelmalaskurin eli PC:n (”Program Counter”) sisällöksi
labelillä OHJELMAOSOITE: merkityn ohjelmamuistin kohdan.
Assembler-lähdekoodissa käskyjen syntaksi on sama, vaikka
käännetyssä koodissa edellinen muoto vie yhden 16-bittisen sanan
verran enemmän tilaa.

• Käskyt CALL ALIRUTIINI ja RCALL ALIRUTIINI vaihtavat


PC:n sisällöksi labelillä ALIRUTIINI: merkityn ohjelmamuistin
kohdan, pushattuaan sitä ennen pinoon kyseistä CALL-käskyä
välittömästi seuraavan käskyn osoitteen (ohjelmamuistin osoitteet
yhteensä kaksi tavua AVR ATmega328 suorittimissa).

• Käsky RET vaihtaa PC:n sisällöksi pinosta poppaamansa, CALL-


käskyn sinne aiemmin tallettaman paluuosoitteen.
AVR-kertaus (19)
Hyppykäskyt, ehdolliset
• Branch-käskyt tarkastelevat jotakin prosessorin statusrekisterin lipuista, ja sen
perusteella joko hyppäävät labelin kertomaan osoitteeseen tai sitten jatkavat
ohjelman suoritusta välittömästi branch-käskyä seuraavasta käskystä.
Tärkeimmät käyttämämme branch-käskyt ovat:
– BREQ LABEL – hyppää LABEL:in kertomaan ohjelman kohtaan, mikäli Z-lippu on
päällä (= 1), muuten jatka seuraavasta käskystä.
– BRNE LABEL – hyppää LABEL:in kertomaan ohjelman kohtaan, mikäli Z-lippu ei ole
päällä (siis on 0), muuten jatka seuraavasta käskystä.
– BRCS LABEL – hyppää LABEL:in kertomaan ohjelman kohtaan, mikäli C-lippu on
päällä (= 1), muuten jatka seuraavasta käskystä. (Synonyymi: BRLO).
– BRCC LABEL – hyppää LABEL:in kertomaan ohjelman kohtaan, mikäli C-lippu ei ole
päällä (siis on 0), muuten jatka seuraavasta käskystä. (Synonyymi: BRSH).
– BRMI LABEL – hyppää LABEL:in kertomaan ohjelman kohtaan, mikäli N-lippu on
päällä (= 1), muuten jatka seuraavasta käskystä. N-lippu tulee päälle, mikäli viimeksi
lasketussa tuloksessa tai testatussa rekisterissä ylin bitti, eli bitti-7 on ykkönen.
– BRPL LABEL – hyppää LABEL:in kertomaan ohjelman kohtaan, mikäli N-lippu ei ole
päällä (siis on 0), muuten jatka seuraavasta käskystä. Toisin sanoen, mikäli viimeksi
lasketussa tuloksessa tai testatussa rekisterissä ylin bitti, eli bitti-7 on nolla.
AVR-kertaus (20)
I/O-käskyt, IN, OUT, SBI & CBI
• Käsky IN Rd,A lukee rekisterin Rd uudeksi sisällöksi I/O-
rekisteristä A yhden tavun. Esimerkiksi IN R0,PINB lukee input-
portiksi konfiguroidusta AVR:n portti-B:stä yhden tavun rekisteriin R0.
(Yleisimmille I/O-porteille kuten PINB, PORTB, jne. on määritelty
symboliset nimet mm. include-tiedostossa m328Pdef.inc)
• Käsky OUT A,Rr kirjoittaa rekisterin Rr sisällön I/O-rekisteriin A.
Esimerkiksi OUT PORTD,R22 kirjoittaa rekisterin R22 sisällön
output-portiksi konfiguroituun AVR:n portti D:hen.
• Käsky SBI A,b asettaa ainoastaan bitin b (missä b=0-7) päälle
output-portiksi konfiguroidussa I/O-rekisterissä A. (Siis kirjoittaa sinne
ykkösen).
• Käsky CBI A,b vastaavasti nollaa (eli ”clearaa”) yksittäisen bitin
output-portiksi konfiguroidussa I/O-rekisterissä A.
AVR-kertaus (21)
I/O-käskyt, SBIC & SBIS
• Käsky SBIC A,b (”Skip if Bit in I/O Register is Cleared”) tarkastelee
ainoastaan bittiä b (missä b=0-7) inputiksi konfiguroidussa portissa A, ja
mikäli se on nolla, skippaa seuraavan käskyn, muuten suorittaa sen
normaalisti.
• Käsky SBIS A,b (”Skip if Bit in I/O Register is Set”) tarkastelee
ainoastaan bittiä b (missä b=0-7) inputiksi konfiguroidussa portissa A, ja
mikäli se on ykkönen, skippaa seuraavan käskyn, muuten suorittaa sen
normaalisti.
• Esimerkiksi jos haluamme tarkistaa onko push-buttonia numero 3 painettu
portti-B:hen invertoidusti kytketyissä painonapeissa (jolloin kun nappia
painetaan saadaan 0, ja kun nappia ei paineta, saadaan 1) niin se käy
seuraavilla käskyillä:
SBIS PINB,3 ;; Jos nappia 3 ei paineta, skipataan seuraava käsky.
RJMP NAPPI3KASITTELY ;; Jos taas nappia 3 painetaan, hypätään
sopivaan kohtaan ohjelmassa
<ohjelman suoritus jatkuu tästä mikäli invertoitua nappia 3 ei painettu>

You might also like