Upload
ante-dilber
View
247
Download
11
Embed Size (px)
DESCRIPTION
Skripta iz kolegija Arhitetektura racunala na temu uvod u asemblersko programiranje u embestu
Citation preview
Uvod u assemblersko programiranje s ARM 32-bitnom porodicom
procesora
1. Uvod
Assemblerski jezici se koriste za programiranje raunala na razini koja je dovoljno
blizu hardveru kako bi mogli dobiti znaajan uvid u stvarni hardver i njegove
performanse.
Kao informatiari, mi smo zainteresirani za razumijevanje asembler programiranja
uglavnom zato to nam daje bolje razumijevanje naina na kojoj su implementriani
vii programnski jezici kao C/C++, to se podrazumijeva kada govorimo o pozivu
funkcije, for petlji ili pristupu nizu brojeva. Kako takve sloene strukture mogu biti
izraene, kad imamo i dostupni su nam registri, memorija i jednostavne instrukcije
sa tri operanda?
Ba kao i na razina vii programskih jezika i asemblerski programi se kompajliraju
na razinu strojnog koda. Ovaj proces se zove asembliranje, a softverski alat koji
ovo izvodi se zove assembler.
1.2 ARM arhitektura
ARM arhitektura predstavlja relativno jednostavna implementaciju load / store
arhitekture, odnosno arhitekture gdje se pristup glavnoj memoriji (RAM) obavlja
preko Load i Store instrukcija, dok se sve raunske operacije provode na
vrijednostima koje se dre u registrima.
Na primjer instrukcija, ADD R4, R1, R2 e pohraniti u registar R4 zbroj
vrijednosti koje se uvaju u registrima R1 i R2.
ARM arhitektura nudi 16 registara ope namjene, oznaenih od R0 do R15, od
koji je posljednja tri imaju posebnu vanost..
R13, takoer poznat kao SP, je stack pointer (pokaziva stoga) koji dri
adresu gornjeg elementa na programskom stogu. Ona se automatski koristi
u PUSH i POP instrukcijama za upravljanje pohrane i oporavak registara u stogu.
R14, takoer poznat kao LR je link registar, i dri povratnu adresu, koja
je adresa prve instrukcije koja se provodi nakon povratka iz trenutna funkcije.
R15, takoer poznat kao PC je programsko brojilo, i uva adresu slijedee
instrukcije koja se treba izvriti.
O ovim registrima emo detaljnije kasnije u tekstu.
2. Embest IDE i programiranje u assembleru
U naim vjebama za razvojno okruenje koristiti emo Embest IDE. Napisati
emo i analizirati jednostavni program zapisan preko Embest okruenja.
C Kod Assembler
int x=4;
int y=7;
z=x*y;
_start:
MOV r0, #0x4
MOV r1, #0x7
MUL r2, r1, r0
stop:
B stop
.end
Nakon instalacije i nakon pokretanja EmbestIDE programa dobijamo slijedee
korisniko suelje.
Prvo emo kreirati novi radni prostor (workspace ) te novi projekt.
(File->New Workspace)
Otvori se dijaloki okvir gdje zadajemo lokaciju u datotenom sustavu i naziv
projekta
Nakon to kliknemo u dijalokom okviru OK u
programu e se pojaviti radni prostor i projekt sa
nekoliko datoteka sa desne strane.
Potom moramo ii na File-> New gdje dobijemo
tekstualni okvir za upis naeg assemblerskog koda .
Upiemo kod iz tablice.
Nakon to upiemo tekst, spasimo sa File-> Save u
direktorij sa naim projektom pod nazivom zad1.asm
Nakon toga potrebno je ubaciti nau spremljenu datoteku zad1.asm u projekt na
nain prikazan na slici (desni klik mi na Project Source File)
Sada kada imamo ubaenu asemblersku datoteku moramo konfigurirati nain i gdje
e se izvesti sami kod ( File->Project->Settings)
Otvori se dijaloki okvir u koje postavljamo slijedee stavke kao na slikama.
U ovom trenutku potrebno je spasiti promjene kliknuti OK, te na glavnom meniju
ii na Build->Compile, te potom na Build-> Build Vjezba 1.
Nakon to se uvjerimo da nema greaka pri kompajliranju i stvaranju zadatka
(Build) potrebno je se opet vratiti na Project->Settings te u tabu Debug u kategoriji
General pronai elf datoteku u debug direktoriju naeg projekta kao na slici te
postaviti na Auto Download.
U kategoriji Download takoer pronai elf datoteku postaviti adresu preuzimanja
0x00008000 te oznaiti stavku Program Entry Point
Nakon svih predradnji ponovno kompajliramo i buildamo projekt te se spojima
na plou sa ARM procesorom sa naredbom Debug->Remote Connect. Sada
moemo izvoditi nae asemblerske programe.
3. Assembler Jezik
Kao to je prikazano u prvom primjeru, izjave u assembleru su strukturirane na
slijedei nain:
label: instruction
Komentari se mogu umetnuti u C stilu (razgranieno s / * komentar* / ). Oznaka
(label) je opcijska mogue je definirati instrukcije i bez oznake.
Oznaka se koristi tako da uva adresu instrukcije u kodu - ovo je potrebno ako
moramo napraviti grananje (branch) u kodu tzv. skok(jump) instrukcije. Za labele
se mogu koristiti bilo koje alfanumerike sekvence. Mogu se koristiti ('_') kao i
znak ('$') . Labele ne mogu zapoinjati sa brojevima.
Instrukcije se mogu podijeliti u tri razliite skupine:
Aritmetiki-Logike instrukcije izvode matematike operacije nad podacima:
to moe biti aritmetika (zbroj, oduzimanje, mnoenje), logika (boolean
operacije), ili relacijska (usporedba dvije vrijednosti) operacija.
Branch instrukcije ili grananja mijenjaju tijek izvoenja programa,
mijenjanjem vrijednosti programskog brojila (R15). One su potrebne za
provedbu uvjetnih izjava, petlji i poziva funkcija.
Load / Store instrukcije premjetaju podatke u i iz glavne memorije. Budui
sve druge operacije rade samo s neposrednim konstantnim vrijednostima
(kodirane u samoj instrukciji) ili s vrijednostima iz registara, load / store
instrukcije su potrebne da se bave sa svim podacima (osim najmanjim
skupovima podataka gdje koristimo naredbu MOV koja zaprima vrijednosti
do 255)
U ostatku ovog odjeljka, mi emo predstaviti podskup ARM seta naredbi
koji se mogu koristiti za pisanje cjelovitih programa. Ovaj podskup je mnogo manji
nego cijeli skup Arm naredbi.
3.1 Aritmetiko-Logike instrukcije
Aritmetike instrukcije ukljuuju sljedee naziv instrukcije te registre nad kojim
operiramo.
Rd- odredini registar
Rd- prvi registar sa podacima
Rm drugi registar sa podacima
#imm-umjesto drugog registra moemo korisiti 8 bitne neposredne
vrijednosti (npr, #5)
Sintaksa Semantika dodatno
objasnjenje
ADD Rd Rn RM / # IMM Rd = Rn + Rm / # IMM Zbrajanje
SUB Rd Rn Rm / # IMM Rd = Rn - Rm / # IMM Oduzimanje
MUL Rd Rn Rm Rd Rd = Rn* Rm / # IMM Mnoenje
AND Rd Rn Rm/#imm Rd=Rn & Rm/#imm (bitwise and)
EOR Rd Rn Rm/#imm Rd = Rn ^ Rm/#imm (bitwise exclusive
or)
ORR Rd Rn Rm/#imm Rd = Rn | Rm/#imm (bitwise or)
MVN Rd Rm/#imm Rd = ~ Rm/#imm (bitwise negacija)
CMP Rn Rm/#imm Status usporedba (, ==)
MOV Rd Rm/#imm Rd = Rm/#imm
Premjetanje iz
jednog u drugi
registar
Veina aritmetikih naredbi je poprilino jasna: sadraj registra Rd su zamjenjeni s
rezultatima operacija koje se izvode na vrijednostima Rm ili Rn (odnosno
konstantnom #imm).
Na primjer, instrukcija SUB R3 R2 #12 smanjuje vrijednost registra R2 za 12.
Potom se vrijednost registra R2 sprema u registar R3.
Takoer, lako je prevesti C jezik u ARM instrukcije sve dok su sve promjenjive
vrijednosti pohranjene u registrima. Razmotrimo slijedei fragment u C-u
int i =0 , j =2 , k =4;
i = k | j ; /* Bitwise or */
Pod pretpostavkom da su vrijednosti tri varijable pohranjeni u registre
R4, R5, i R6, tada se moe prevesti u fragment kako slijedi:
MOV R4 #0 /* i =0; */
MOV R5 #2 /* j =2; */
MOV R6 #4 /* k =4; */
ORR R4 R5 R6 /* i = k | j ; */
Vano je napomenuti da je mnoenje tee implementirati u hardveru od drugih
naredbi. Za razliku od veine drugih instrukcija, u MUL instrukciji registri Rd i
Rm ne mogu biti u istom registru.
Instrukcija usporedbe, CMP, ne sprema svoj rezultat u ope namjenski
registar. Umjesto toga ona koristi posebni (ne izravno dostupan prema programeru)
registar, koji se zove Processor Status Word. Rezultat usporedbe govori procesoru
da li je vrijednost u Rn je vea, manja ili jednaka registru Rm (ili
konstantnom operandu). Te se informacije mogu se koristiti da odluite hoete li
obavljati uzastopne uvjetne instrukcije grananja ili ne.
3.2 Branch Instrukcije
Osnovna funkcionalnost Branch instrukcija je promijeniti vrijednost programskog
brojila postavljanjem na novu vrijednost. Set instrukcija grananja ukljuuje
sljedee:
Sintaksa Semantika
B label Skoi na label bezuvjetno
BEQ label Skoi na label ako je prethodna usporedba jednaka
BNE label Skoi na label ako je prethodna usporedba nije jednaka
BGT label Skoi na label ako je prethodna usporedba Rn >Rm/#imm
BGE label Skoi na label ako je prethodna usporedba Rn >=Rm/#imm
BLT label Skoi na label ako je prethodna usporedba Rn
CMP R0 #0
BGT label
ADD R0 R0 #1
Kljuna naredba je CMP koja mijenja stanje u registru CPSR.
Najznaajniji bitovi u registru CPSR s krajnji N,Z,C iV.
N - odreuje da li je broj negativan ili je manji od onog s kojim usporeujemo
Z- odreuje da li je broj 0 odnosno da li su dva broja jednaka
C Da li je dolo do preljeva, prenoenja ili posuivanja kod raunskih operacija
V Preljev i prekoraenje kod brojeva sa predznakom
Na osnovu stanja u CPSR registru naredba BGT moe izvriti uvjetno grananje.
Prvo smo usporediti R0 sa 0, onda granamo na label ( ako i samo ako) je
vrijednost sadrana u R0 vea od nule. S druge strane ako nije vea ide se na
instrukciju 3.
3.2 Prevoenje uvjetne naredbe
Kljuno pitanje u razumijevanju assemblera je razumijevanje kako naredbe za
uvjetno izvoenje koda iz viih jezika mogu biti implementirani u asemblerskom
jeziku niskog nivoa. Prvi korak je nauiti kako predstaviti u assembler if the-
else konstrukt iz C-a.
Pretpostavimo da su x i y sauvani u R4 i R5, moemo prevesti C fragment na
slijedei nain:
C Kod Assembler
int x , y ;
...
if(x >0) {
y =1;
} else {
y =2;
}
return y ;
.equ x, 1
_start:
MOV r4, #x
CMP r4, #x
BLE else
MOV r5, #1
end:
B end
else:
MOV R5,#2
B end
Moemo vidjeti da u liniji 3 i 4 u assembleru postavljamo uvjetno grananje koje
doputa da preskoimo then dio koda (y=1; u naem primjeru) ako se uvjet (x>0)
ne izvri. S druge strane koristi se i provodi then dio. U kasnijem sluaju
moramo osigurati da se else ne izvrava u originalnom C konstruktu , dva dijela
su uzajmno iskljuiva : samo se jedna izvrava kada se uvjet ispunjava. Na taj
nain u liniji 8 dodajemo bezuvjetno grananje B kako bi preskoili else dio i
pomakli se direktno na kraj preko labele end.
C kod Assembler
int res;
int x=0;
int mx=4
int mn =7
.equ x, 0
.equ mx, 4
.equ mn, 7
_start:
.Lt_mx:
cmp r0,r1
bge stop
add r3,r0,r2
if(x>=mx)
res=mn+x-mx;
else if(x0)
{
y*=x;
x--;
}
.equ x, 5
.equ y, 1
_start:
MOV r5, #y
MOV r4, #x
loop:
CMP r4, #0
BLE end
MUL r6, r5, r4
MOV r5, r6
SUB r4, r4, #1
B loop
end:
MOV r0, r5
stop:
B stop
Moemo vidjeti u liniji 6 i 7 u asemblerskom kodu da postavljamo i izvravamo
uvjetno grananje koje doputa da izaemo iz petlje kada uvjet u izvornom kodu
(x>0) nije osiguran.. Nakon kraja tijela petlje moramo vratiti se i provjeriti uvjet
kako bi zapoeli sa iteracijom petlji. U liniji 12 dodajemo bezuvjetno grananje B
kako bi se vratili na poetak petlje oznaene sa oznakom labelom loop.
Sa do-while petljom moemo izvesti slian prijevod. Ovdje je C fragment koji
rauna istu funkciju faktorijel osim to je x==0, povratna vrijednost je 0 radije nego
1.
C Kod Assembler
int x , y ;
x=5;y =1;
do {
y *= x ;
x - -;
} while (x >0) ;
return y ;
.equ x, 5
.equ y, 1
_start:
MOV r5, #y
MOV r4, #x
loop:
MUL r6, r5, r4
MOV r5, r6
SUB r4, r4, #1
CMP r4, #0
BGT loop
MOV r0, r5
stop:
B stop
Jedina uoljiva razlika je u odnosu na while verziju u tome to sada se granamo na
poetak koji je uvjetan i trebamo samo jednu granu budui nemamo kontrole
provjere x>0 na poetku.
Moe se initi da prevoenje for petlje predstavljaju je tee budui je kosntrukcija
petlje kompleksnija. Ipak, nije tako budui se for petja se moe odmah prevesti u C
while petlju. Naprimjer razmotrimo slijedei fragment C koda:
int x , y ;
...
for ( y =1; x >0; x - -) y *= x ;
return y ;
Lako je vidjeti da petlja u liniji 3 ima isto ponaanje kao while petlja. Generalno , C
for petlja ima slijedeu strukturu:
for(inicijalizacija; uvjet; korak petlje) tijelo petlje.
Petlju for lako moemo prevesti u while petlju na slijedei nain:
Inicijalizacija
while(uvjet)
{
Tijelo-petlje
Korak_petlje
}
3.3 Load / Store Instrukcije
Posljednje, ali ne i najmanje vano, load i store instrukcije obavljaju prijenos
podataka iz memorije prema registrima i obrnuto.
Sljedee instrukcije omoguuju nam rad s podacima veliine rije (32 bita) i
veliine byte (8 bita)
Sintaksa Semantika
LDR Rd [Rn, Rm/#imm] Rd = mem[Rn+Rm/#imm] (32 bit copy)
STR Rd [Rn, Rm/#imm] mem[Rn+Rm/#imm] = Rd (32 bit copy)
LDRB Rd [Rn, Rm/#imm] Rd = mem[Rn+Rm/#imm] (8 bit copy)
STRB Rd [Rn, Rm/#imm] mem[Rn+Rm/#imm] = Rd (8 bit copy)
PUSH { register list } Push registers onto stack
POP { register list } Pop registers from stack
Load instrukcijama vrijednosti se itaju iz odreene memorijske adrese i uitavaju
u registar Rd, dok su store instrukcije Rd izvori iz kojih se podaci itaju ,a
vrijednosti spremaju u memoriji. Memorijska adresa se u oba sluaja rauna kao
suma baze Rn i odmaka (Rm ili kosntante). Uobiajeno programer koristi ove
naine da pristupe poljima podataka. Razmotrimo slijedei C kod:
1. char AA [10] , BB [10];
2. ...
3. AB [ i ]= BB [ i ];
Gdje su AA i BB polja. Ako se baza adrese AA i BB uvaju u R4 i R5, a indeks i
se uva u R1, tada se liniji 3 moe prevest kao
LDRB R6 R5 R1
STRB R6 R4 R1
Budui je memorija adresirana po byte-ovima (svaka adresa odgovara specifinom
byte-u) kada se pristupa polju integera moramo modificirati indeks polja kako bi
inkrementirali po koraku 4 umjesto po koraku 1 jer integer zauzima 4 byte-a.
int AA [10] , BB [10];
...
3 i =2;
4 AA [ i ]= AB [ i ];
Odgovarajui ARM prijevod koristi istu pohranu za adrese i varijable
MOV R1 #8 /* 2*4: addressing in bytes ! */
LDR R6 R5 R1
STR R6 R4 R1
LDR se koristit kada se sluimo rijeima (32 bita) dok LDRB kada imamo byte-
ove(8 bita). Slino je i za STR.
Push i Pop instrukcije se koristi da upravljaju programskim stogom koje se
kolekcija zapisa za sve funkcije aktivne u isto vrijeme kao rezultat ugnijeenih
poziva funkcija.
Globalna podatkovna sekcija
Svaki program vidi memorijski prostor koji je rezerviran za njega. Memorijski
prostor se moe koristiti za uvanje podataka: npr, moemo uvati globalne
skalarne varijable, stringove , polja ili matrice.
Kako bi rezervirali prostor namijenjen globalnoj podatkovnoj memoriji moramo
dati dovoljno informacija o veliini prostora potrebnog za memoriju kao i
inicijalizacijske vrijednosti. Direktive .int, .byte, .float, .string respektivno
rezerviraju memoriju za 32-bitni integer, 8-bitni integer 32- bitni floating point broj
i zero terminated string (veliina stringa je jedan byte po znaku plus jedan dodatna
znak za kraj stringa)
Ovim podacima moemo pristupiti sa load i store instrukcijama
Zadatak 7. Pronaimo najvei zajedniki djelitelj izmeu dva zadana broja preko
niza brojeva
C kod Assembler
while(a!=b)
{ if(a>b)
a=a-b;
else
b=b-a;
}
arr:
.byte 100,64
eoa:
.align
_start:
ldr r3, =arr
ldr r4, =eoa
ldrb r0, [r3], #1
ldrb r1, [r3 ], #1
gcd:
cmp r0, r1
beq stop
blt less
sub r0, r0, r1
bal gcd
less:
sub r1, r1, r0
bal gcd
stop:
B stop
U sluaju da koristimo .byte ili polu rijei tip podataka moramo koristiti direktiva
.align . Budui instrukcije zauzimaju 4 byte-a, adresa prvog byte mora biti djeljiva
sa 4 pa stoga umeemo dodatne byte-ove kako bi slijedea adresa instrukcije bila
viekratnik broja 4.
Zadatak 8. Zbrojite elemente niza
C kod Assembler
char arr[]={10,20,25}
int sum=0;
int i=0;
do
{
sum=sum+char[i];
i++;
}while(i
cmp r1, r0
Zadatka 9. Napiite program u assembleru kojim ete pronai duljinu niza znakova.
str: .asciz "Hello World
.equ nul, 0
.align
start:
ldr r0, =str
mov r1, #0
loop:
ldrb r2, [r0], #1
add r1, r1, #1
cmp r2, #nul
bne loop
sub r1, r1, #1
stop: b stop
Direktiva .asciz prihvata string znakove kao argument. String znakovi se smjetaju
u susjedne i kompaktne memorijske lokacije.
Stog
Stog je podatkovna struktura (nain na koji organziramo podatke). Stog se sastoji
od tri tipine operacije: pristup vrhu stoga, postavljanje na vrh (push), skidanje sa
vrha (pop). U ovisnosti o kontesktu moemo samo imati pristup vrhu stoga. Kada
govorimo o funkcijama to je dio memorije koju posjeduje funkcija, a koji se
dinamiki aktivira kada se pozove funkcija. Za kontrolu stoga sluimo se
pokazivaem stoga (stack pointer) odnosno registrom sp. U registru se samo nalazi
pohranjen vrh stoga. Podruje memorije koje se stvara kada se pozove funkcija
nazivamo lokalnom memorijom funkcije i sastoji se od poetne vrijednost sp a
kada je funkcija pozvana i trenutne vrijednost pokazivaa stoga (sp-a). Podruje se
se poveava kada dodajemo elemente, a smanjujemo kada skidamo elemente sa
stoga. Stavke se skidaju obrnutima redoslijedom u kojem su dodavane (LIFO).
.equ x, 45
.equ y, 64
.equ stack_top, 0x1000
.global _start
.text
_start:
MOV sp, #stack_top
MOV r0, #x
STR r0, [sp]
MOV r0, #y
LDR r1, [sp]
ADD r0, r0, r1
STR r0, [sp]
stop:
B stop
.end
/* definiramo varijablu x i dodajmo 45 */
/* definiramo varijablu y i dodajmo 64 */
/* definiramo vrh stoga 0x1000*/
/* simbol poetka programa */
/* dodaj vrijednost x u registar R0 */
/* spasi vrijednost R0 na stog*/
/* dodaj vrijednost y na R0 */
/* preuzmi vrijednost iz stoga na R1 */
/* program zavrava i ulazi u beskonanu
petlju*/
.equ stack_top, 0x1000
.global _start
.text
_start:
MOV sp, #stack_top
adr r4, pod
LDR r0,[r4],#4
LDR r1,[r4],#4
ADD r2,r0,r1
LDR r0,[r4],#4
MUL r3,r2,r0
STR r3,[r4]
STR r3,[sp]
stop:
B stop
pod:
.word 0x000f, 0x00ff, 0x0fff, 0xffff
.end
/*stavi na vrh stoga 0x1000*/
/*ulazna toka*/
/* postavlja se stack pointer na vrh stoga */
/* stavlja se u r4 adresa podataka */
/* u r0 prvi podatak iz memorije 0x000F */
/* u r1 drugi podatak iz memorije 0x00FF */
/*zaustavljanje grananja*/