Studiul setului de instrucțiuni al procesorului ARM. Învățarea setului de instrucțiuni al procesorului ARM Assembler pentru biții de scanare a brațului
În prezent, pentru programarea chiar și a microcontrolerelor destul de simple, se folosesc limbaje de nivel înalt, de regulă, care sunt subseturi ale limbajului C sau C++.
Cu toate acestea, atunci când se studiază arhitectura procesoarelor și caracteristicile acesteia, este recomandabil să se utilizeze limbaje de asamblare, deoarece numai o astfel de abordare poate asigura identificarea caracteristicilor arhitecturii studiate. Din acest motiv, prezentarea ulterioară se realizează folosind limbajul Asamblare.
Înainte de a trece la luarea în considerare a comenzilor ARM7, este necesar să rețineți următoarele caracteristici ale acestuia:
Suport pentru două seturi de instrucțiuni: ARM cu instrucțiuni pe 32 de biți și THUMB cu instrucțiuni pe 16 biți. Următorul este un set de instrucțiuni pe 32 de biți, cuvântul ARM va însemna instrucțiuni aparținând acestui format, iar cuvântul ARM7 - CPU însuși.
Suport pentru două formate de adrese pe 32 de biți: procesor little-endian și procesor little-endian. În primul caz, bitul cel mai semnificativ (Most Significant Bit - MSB) este situat în bitul cel mai puțin semnificativ al cuvântului, iar în al doilea caz, în cel mai semnificativ. Acest lucru oferă compatibilitate cu alte familii de procesoare pe 32 de biți atunci când se utilizează limbaje de nivel înalt. Cu toate acestea, într-un număr de familii de procesoare cu un nucleu ARM, este folosit doar little endian (adică, MSB este cel mai important bit al adresei), ceea ce simplifică foarte mult lucrul cu procesorul. Deoarece compilatorul folosit pentru ARM7 funcționează cu cod în ambele formate, trebuie să vă asigurați că formatul cuvântului este setat corect, altfel codul rezultat va fi „întors pe dos”.
Abilitatea de a efectua diferite tipuri de schimburi pe unul dintre operanzii „pe trecere” înainte de a fi utilizat în ALU
Suport pentru executarea condiționată a oricărei comenzi
Posibilitatea de a interzice schimbarea steagurilor rezultatelor operațiunii.
Executarea comenzilor condiționate
Una dintre caracteristicile importante ale setului de instrucțiuni ARM este că acceptă execuția condiționată a oricărei instrucțiuni. În microcontrolerele tradiționale, singurele instrucțiuni condiționate sunt salturile condiționate și poate o serie de altele, cum ar fi instrucțiunile de verificare sau modificare a stării biților individuali. În setul de instrucțiuni ARM, cei 4 biți superiori ai codului de instrucțiune sunt întotdeauna comparați cu steagurile de condiție din registrul CPSR. Dacă valorile lor nu se potrivesc, comanda din etapa de decriptare este înlocuită cu comanda NOP (fără operare).
Acest lucru reduce semnificativ timpul de execuție al secțiunilor programului cu tranziții „scurte”. Deci, de exemplu, atunci când rezolvați ecuații pătratice cu coeficienți reali și rădăcini arbitrare cu un discriminant negativ, este necesar să schimbați semnul discriminantului înainte de a calcula rădăcina pătrată și să atribuiți rezultatul părții imaginare a răspunsului.
În soluția tradițională a acestei probleme, este necesar să introduceți o comandă de salt condiționat. Executarea acestei comenzi durează cel puțin 2 cicluri - decriptarea și încărcarea noii valori de adresă în contorul de program și un număr suplimentar de cicluri pentru a încărca conducta de comandă. Când se utilizează execuția de comandă condiționată cu un discriminant pozitiv, comanda de schimbare a semnului este înlocuită cu o operație goală. În acest caz, canalul de instrucțiuni nu este șters, iar pierderile nu depășesc un ciclu de ceas. Pragul la care înlocuirea instrucțiunilor condiționate cu instrucțiunea NOP este mai eficientă decât execuția instrucțiunilor tradiționale de ramificare condiționată și reumplerea conductei asociate cu aceasta este egală cu adâncimea acesteia, i.e. Trei.
Pentru a implementa această caracteristică, trebuie să adăugați oricare dintre cele șaisprezece prefixe care definesc stările de testare ale steagurilor de condiție la notația mnemonică de bază a instrucțiunilor de asamblare (și C, de asemenea). Aceste prefixe sunt date în tabel. 3. În consecință, există 16 opțiuni pentru fiecare echipă. De exemplu, următoarea comandă:
MOVEQ R1, #0x008
înseamnă că numărul 0x00800000 va fi încărcat în registrul R1 numai dacă rezultatul ultimei instrucțiuni de prelucrare a datelor a fost „egal” sau a fost obținut un rezultat 0 și indicatorul (Z) al registrului CPSR este setat corespunzător.
Tabelul 3
Prefixe de comandă
Sens |
||
Z instalat | ||
Z a scăzut | ||
Cu instalat |
Mai mare sau egal (nesemnat) |
|
C resetare |
Mai jos (nesemnat) |
|
N instalat |
Rezultat negativ |
|
N a scăzut |
Rezultat pozitiv sau 0 |
|
V instalat |
Revărsare |
|
V a căzut |
Fara preaplin |
|
Cu instalat, Z a scăzut |
Mai sus (nesemnat) |
|
Cu scăpat, Z instalat |
Mai mic sau egal (nesemnat) |
|
Mai mare sau egal (semnat) |
||
N nu este egal cu V |
Mai puțin decât (semnat) |
|
Z resetare ȘI (N este egal cu V) |
Mai multe (semnate) |
|
Z este setat SAU (N nu este egal cu V) |
Mai mic sau egal (semnat) |
|
(ignorat) |
Execuție necondiționată |
Procesoarele CISC efectuează operații destul de complexe într-o singură instrucțiune, inclusiv operații aritmetice și logice asupra conținutului celulelor de memorie. Instrucțiunile CISC CPU pot avea lungimi diferite.
În schimb, RISC are un set de instrucțiuni relativ simplu, cu o împărțire clară după tipul de operație:
- lucrul cu memorie (citirea din memorie în registre sau scrierea din registre în memorie),
- prelucrarea datelor în registre (aritmetică, logică, deplasări de date la stânga/dreapta sau rotație de biți în registru),
- instrucțiuni pentru salturi condiționate sau necondiționate la alte adrese.
De regulă (dar nu întotdeauna și numai dacă codul programului intră în memoria cache a controlerului), o instrucțiune este executată într-un ciclu de procesor. Lungimea instrucțiunii procesorului ARM este fixă - 4 octeți (un cuvânt computer). De fapt, un procesor ARM modern poate trece la alte moduri de operare, de exemplu, la modul THUMB, când lungimea instrucțiunii devine 2 octeți. Acest lucru vă permite să faceți codul mai compact. Cu toate acestea, nu acoperim acest mod în acest articol, deoarece nu este acceptat de procesorul Amber ARM v2a. Din același motiv, nu vom lua în considerare astfel de moduri precum Jazelle (optimizat pentru executarea codului Java) și nu vom lua în considerare comenzile NEON - comenzi pentru operații pe date multiple. Totuși, studiem un set de instrucțiuni ARM pur.
Registrele procesorului ARM.
Procesorul ARM are mai multe seturi de registre, dintre care doar 16 sunt disponibile pentru programator în prezent.Există mai multe moduri de funcționare a procesorului, în funcție de modul de funcționare, se selectează banca corespunzătoare de registre. Aceste moduri de operare sunt:
- modul aplicație (USR, modul utilizator),
- modul supervizor sau modul sistemului de operare (SVC, modul supervizor),
- modul de gestionare a întreruperilor (IRQ, mod de întrerupere) și
- modul de procesare „întrerupere urgentă” (FIRQ, mod de întrerupere rapidă).
Adică, de exemplu, atunci când are loc o întrerupere, procesorul însuși merge la adresa programului de gestionare a întreruperilor și „comută” automat băncile de registre.
Procesoarele ARM mai vechi, pe lângă modurile de operare de mai sus, au moduri suplimentare:
- Abort (utilizat pentru a gestiona excepțiile de acces la memorie),
- Nedefinit (folosit pentru implementarea programatică a coprocesorului) și
- modul de activitate privilegiat al sistemului de operare System.
Procesorul Amber ARM v2a nu are aceste trei moduri suplimentare.
Pentru Amber ARM v2a, setul de registre poate fi reprezentat după cum urmează:
Registrele r0-r7 sunt aceleași pentru toate modurile.
Registrele r8-r12 sunt comune doar pentru modurile USR, SVC, IRQ.
Registrul r13 este indicatorul stivei. El este în toate modurile.
Registrul r14 - registrul de revenire din subrutină este și el propriu în toate modurile.
Register r15 este un pointer către comenzi executabile. Este comun tuturor modurilor.
Se poate observa că modul FIRQ este cel mai izolat, are cele mai multe registre proprii. Acest lucru se face astfel încât unele întreruperi foarte critice să poată fi procesate fără a salva registrele în stivă, fără a pierde timp cu asta.
O atenție deosebită trebuie acordată registrului r15, cunoscut și sub denumirea de pc (Program Counter) - un pointer către comenzile executabile. Este posibil să se efectueze diverse operații aritmetice și logice asupra conținutului său, astfel execuția programului va trece la alte adrese. Cu toate acestea, pentru procesorul ARM v2a implementat în sistemul Amber există unele subtilități în interpretarea biților acestui registru.
Faptul este că în acest procesor, în registrul r15 ( pc), pe lângă indicatorul propriu-zis la comenzile executabile, conține următoarele informații:
Biții 31:28 - steaguri ale rezultatului unei operații aritmetice sau logice
Biții 27 - Masca IRQ de întrerupere, întreruperile sunt dezactivate când bitul este setat.
Biții 26 - Mască de întrerupere FIRQ, întreruperile rapide sunt dezactivate când bitul este setat.
Biți 25:2 - indicatorul către instrucțiunile programului în sine ocupă doar 26 de biți.
Biți 1:0 - modul de funcționare curent al procesorului.
3-Supervizor
2- Întreruperea
1-Întrerupere rapidă
0 - utilizator
La procesoarele ARM mai vechi, toate steagurile și biții de serviciu sunt localizați în registre separate. Registrul de stare curent al programului( cpsr ) și Registrul de stare program salvat ( spsr ), pentru care există comenzi speciale separate de accesat. Acest lucru se face pentru a extinde spațiul de adrese disponibil pentru programe.
Una dintre dificultățile stăpânirii asamblatorului ARM este numele alternative ale unor registre. Deci, așa cum am menționat mai sus, r15 este același computer. Există și r13 - acesta este același sp (Stack Pointer), r14 este lr (Link Register) - registrul adresei de retur din procedură. În afară de asta, r12 este același ip (Intra-Procedure -call scratch register), folosit de compilatorii C într-un mod special pentru a accesa parametrii din stivă. O astfel de denumire alternativă este uneori confuză atunci când te uiți în codul de program al altcuiva - există atât acelea, cât și aceste denumiri de registru.
Caracteristici ale executării codului.
În multe tipuri de procesoare (de exemplu, x86), doar un salt la o altă adresă de program poate fi efectuat în funcție de condiție. Nu este cazul cu ARM. Fiecare instrucțiune de procesor ARM poate fi executată sau nu în funcție de condiție. Acest lucru permite reducerea la minimum a numărului de salturi de program și, prin urmare, utilizarea mai eficientă a conductei (pipeline) a procesorului.
La urma urmei, ce este o conductă? O instrucțiune de procesor este acum preluată din codul programului, cea anterioară este deja decodificată, iar cea anterioară este deja executată. Acesta este cazul conductei de procesoare Amber A23 în 3 etape pe care o folosim în proiectul nostru pentru placa Mars Rover2Rover2. Modificarea procesorului Amber A25 are o conductă în 5 etape, este și mai eficientă. Dar, există un DAR mare. Instrucțiunile de salt forțează procesorul să elibereze conducta și să o reumple. Astfel, este selectată o nouă comandă, dar încă nu există nimic de decodat, cu atât mai puțin ceva de executat imediat. Eficiența execuției codului scade odată cu tranzițiile frecvente. Procesoarele moderne au tot felul de mecanisme de predicție a ramurilor care optimizează cumva umplerea conductei, dar nu este cazul procesorului nostru. În orice caz, ARM a fost înțelept să facă posibil ca fiecare instrucțiune să fie executată condiționat.
Într-un procesor ARM, în orice tip de instrucțiune, cei patru biți ai condiției de execuție a instrucțiunii sunt codificați în cei patru biți superiori ai codului de instrucțiune:
Există 4 indicatori de stare în procesor:
. Negativ - rezultatul operației este negativ,
. Zero - rezultatul este zero,
. Carry - la efectuarea unei operații cu numere nesemnate, a avut loc o transportare,
. oVerflow - la efectuarea unei operații cu numere semnate, a avut loc o depășire, rezultatul nu este plasat în registru)
Aceste 4 steaguri formează multe combinații posibile de condiții:
Codul | Sufix | Sens | Steaguri |
4"h0 | eq | Egal | Z set |
4"h1 | ne | nu este egal | Z clar |
4"h2 | cs/hs | Carry set / nesemnat mai mare sau același | Cset |
4"h3 | cc/lo | Purtați clar/nesemnat mai jos | C clar |
4"h4 | mi | minus/negativ | N set |
4"h5 | pl | Plus / pozitiv sau zero | N clar |
4"h6 | impotriva | revărsare | V set |
4"h7 | vc | fara preaplin | V clar |
4"h8 | Bună | Nesemnat mai sus | C set și Z clar |
4"h9 | ls | Nesemnat mai jos sau același | C clear sau Z set |
4" ha | GE | Semnat mai mare sau egal | N == V |
4"hb | lt | Semnat mai puțin de | N != V |
4"hc | gt | Semnat mai mare decât | Z == 0,N == V |
4" hd | le | Semnat mai mic sau egal | Z == 1 sau N != V |
4 "el | al | mereu (neconditionat) | |
4" hf | - | stare invalidă |
Acum, o altă dificultate în învățarea instrucțiunilor procesorului ARM decurge din aceasta - numeroasele sufixe care pot fi adăugate la codul de instrucțiuni. De exemplu, plus, cu condiția ca indicatorul Z să fie setat, este comanda addeq ca sufix add + eq. Salt la subrutină dacă flag N=0 este blpl ca bl + sufix pl .
Steaguri ( Negativ, Zero, Carry, Overflow ) nu la fel se stabilește întotdeauna în timpul operațiilor aritmetice sau logice, așa cum se întâmplă, să zicem, într-un procesor x86, ci doar atunci când dorește programatorul. Pentru a face acest lucru, există un alt sufix la mnemonicul comenzii: „s” (codat în codul de comandă de bitul 20). Astfel, comanda add nu schimbă steagurile, dar comanda add schimbă steagurile. Și poate exista și o comandă de adăugare condiționată, dar care schimbă steaguri. De exemplu: addgts . Este clar că numărul de combinații posibile de nume de comandă cu sufixe diferite de execuție condiționată și steaguri de setare face ca codul de ansamblu al procesorului ARM să fie foarte ciudat și dificil de citit. Cu toate acestea, cu timpul te obișnuiești și începi să înțelegi acest text.
Operatii aritmetice si logice (Procesarea datelor).
Procesorul ARM poate efectua diverse operații aritmetice și logice.
Codul efectiv de operare pe patru biți (Opcode) este conținut în biții de instrucțiuni ale procesorului.
Orice operație este efectuată asupra conținutului registrului și a așa-numitului shifter_operand . Rezultatul operației este plasat într-un registru. Rn și Rd pe patru biți sunt indici de registru din banca activă a procesorului.
În funcție de bitul I 25 shifter_operand este tratat fie ca o constantă numerică, fie ca un index al celui de-al doilea registru al operandului și chiar o operație de deplasare a valorii celui de-al doilea operand.
Exemple simple de comenzi de asamblare ar arăta, de exemplu, astfel:
adăugați r0,r1,r2 @ puneți în registrul r0 suma valorilor registrelor r1 și r2
sub r5,r4,#7 @ puneți diferența (r4-7) în registrul r5
Operațiile efectuate sunt codificate după cum urmează:
4"h0 și ȘI logic Rd:= Rn ȘI shifter_operand
4"h1 eor XOR Rd:= Rn XOR shifter_operand
4"h2 sub Scădere aritmetică Rd:= Rn - shifter_operand
4"h3 rsb Scădere inversă aritmetică Rd:= shifter_operand - Rn
4"h4 adaugă Adunarea aritmetică Rd:= Rn + shifter_operand
4"h5 adc Adunare aritmetică plus flag de transport Rd:= Rn + shifter_operand + Carry Flag
4"h6 sbc Carry Arithmetic Scădere Rd:= Rn - shifter_operand - NOT(Carry Flag)
4"h7 rsc Carry Arithmetic Scădere inversă Rd:= shifter_operand - Rn - NOT(Carry Flag)
4"h8 tst ȘI logic, dar fără a salva rezultatul, sunt modificate doar steagurile Rn AND shifter_operand S bit întotdeauna setat
4"h9 teq SAU exclusiv logic, dar fără a stoca rezultatul, sunt modificate doar steagurile Rn EOR shifter_operand
S bit întotdeauna setat
4 "ha cmp Comparație, sau mai degrabă scădere aritmetică fără a stoca rezultatul, doar steagurile Rn se schimbă - shifter_operand S bit întotdeauna setat
4 "hb cmn Comparație a adunării inverse, sau mai degrabă aritmetice fără a reține rezultatul, doar steagurile Rn + shifter_operand S bit întotdeauna setat sunt modificate
4"hc orr logic SAU Rd:= Rn SAU shifter_operand
4"hd mov Copiați valoarea Rd:= shifter_operand (fără primul operand)
4"he bic Reset biți Rd:= Rn AND NOT(shifter_operand)
4"hf mvn Copiați valoarea inversă Rd:= NOT shifter_operand (fără primul operand)
schimbător de butoi.
Procesorul ARM are o schemă specială „barrel shifter” care permite ca unul dintre operanzi să fie deplasat sau rotit cu un anumit număr de biți înainte de orice operație aritmetică sau logică. Aceasta este o caracteristică destul de interesantă a procesorului, care vă permite să creați cod foarte eficient.
De exemplu:
@ înmulțirea cu 9 înseamnă înmulțirea unui număr cu 8
@ prin deplasarea la stânga cu 3 biți plus un alt număr
adăugați r0, r1, r1, lsl #3 @ r0= r1+(r1<<3) = r1*9
@ înmulțirea cu 15 este înmulțirea cu 16 minus numărul
rsb r0, r1, r1, lsl #4 @ r0= (r1<<4)-r1 = r1*15
@ acces la un tabel de cuvinte de 4 octeți, unde
@r1 este adresa de bază a tabelului
@r2 este indexul elementului din tabel
ldr r0,
În plus față de deplasarea logică la stânga lsl, există și o deplasare logică la dreapta lsr și o deplasare aritmetică la dreapta asr (deplasare care păstrează semnul, bitul cel mai semnificativ este înmulțit de la stânga simultan cu deplasarea).
Există, de asemenea, o rotație a biților ror - biții sunt împinși spre dreapta, iar cei care sunt împinși în afară sunt împinși înăuntru spre stânga.
Există o schimbare de un bit prin indicatorul C - aceasta este comanda rrx. Valoarea registrului este deplasată la dreapta cu un bit. În stânga, steagul C este încărcat în bitul înalt al registrului
Deplasarea poate fi efectuată nu printr-o constantă numerică fixă, ci prin valoarea celui de-al treilea registru-operand. De exemplu:
se adaugă r0, r1, r1, lsr r3 @ este r0 = r1 + (r1>>r3);
se adaugă r0, r0, r1, lsr r3 @ este r0 = r0 + (r1>>r3);
Deci, shifter_operand este ceea ce descriem în instrucțiunile de asamblare precum „r1, lsr r3” sau „r2, lsl #5”.
Cel mai interesant lucru este că folosirea schimburilor în operațiuni nu costă nimic. Aceste schimbări nu necesită (de obicei) cicluri suplimentare de ceas și sunt foarte bune pentru performanța sistemului.
Utilizarea operanzilor numerici.
Operațiile aritmetice sau logice pot folosi ca al doilea operand nu numai conținutul registrului, ci și o constantă numerică.
Din păcate, există o limitare importantă aici. Deoarece toate comenzile au o lungime fixă de 4 octeți (32 de biți), nu va fi posibilă codificarea „orice” număr în ele. În codul de operare, deci 4 biți sunt ocupați de codul de condiție de execuție (Cond), 4 biți pentru codul de operare în sine (Opcode), apoi, 4 biți - registrul receptorului Rd și încă 4 biți - registrul primul operand Rn, plus mai multe steaguri diferite I 25 (doar indică o constantă numerică în codul operației) și S 20 (setarea steaguri după operație). În total, rămân doar 12 biți pentru o posibilă constantă, așa-numitul shifter_operand - am văzut asta mai sus. Deoarece 12 biți pot codifica numere doar într-un interval restrâns, dezvoltatorii procesorului ARM au decis să codifice constanta după cum urmează. Cei doisprezece biți ai shifter_operand sunt împărțiți în două părți: indicele de rotație de patru biți encode_imm și valoarea numerică reală de opt biți imm_8 .
Pe un procesor ARM, o constantă este definită ca un număr de 8 biți în interiorul unui număr de 32 de biți, rotit la dreapta cu un număr par de biți. Acesta este:
imm_32 = imm_8 ROR (encode_imm *2)
S-a dovedit destul de inteligent. Se pare că nu orice constantă numerică poate fi utilizată în comenzile de asamblare.
Poti sa scrii
adăugați r0, r2, #255 @ constantă zecimală
adăugați r0, r3, #0xFF @ constantă în hex
deoarece 255 este în intervalul de 8 biți. Aceste comenzi vor fi compilate astfel:
0: e28200ff adăugați r0, r2, #255; 0xff
4: e28300ff adăugați r0, r3, #255; 0xff
Și poți chiar să scrii
adăugați r0, r4, #512
adăugați r0, r5, 0x650000
Codul compilat va arăta astfel:
0: e2840c02 adăugați r0, r4, #512; 0x200
4: e2850865 adăugați r0, r5, #6619136; 0x650000
În acest caz, numărul 512 în sine, desigur, nu se încadrează într-un octet. Dar, pe de altă parte, ne imaginăm în formă hexazecimală 32'h00000200 și vedem că acesta este 2 întors la dreapta cu 24 de biți (1 ror 24). Coeficientul de rotație este de două ori mai mic decât 24, adică 12. Deci, shifter_operand = ( 4'hc , 8'h02 ) - aceștia sunt cei doisprezece biți cei mai puțin importanți ai comenzii. Este la fel și cu numărul 0x650000. Pentru acesta shifter_operand = ( 4'h8, 8'h65 ).
Este clar că nu se poate scrie
adăugați r0, r1,#1234567
sau nu poti scrie
mov r0, #511
deoarece aici numărul nu poate fi reprezentat ca imm_8 și encode_imm - factorul de rotație. Compilatorul de asamblare va arunca o eroare.
Ce să faci când o constantă nu poate fi codificată direct în shifter_operand? Trebuie să faci tot felul de trucuri.
De exemplu, puteți încărca mai întâi numărul 512 într-un registru gratuit, apoi puteți scădea unul:
mov r0, #511
sub r0,r0,#1
A doua modalitate de a încărca un anumit număr într-un registru este să-l citiți dintr-o variabilă special rezervată în memorie:
ldr r7,my_var
.....
my_var: .word 0x123456
Cel mai simplu mod de a scrie este astfel:
ldr r2,=511
În acest caz (observați semnul „=") dacă constanta poate fi reprezentată ca imm_8 și encode_imm , dacă poate fi încadrată într-un shifter_operand de 12 biți, atunci compilatorul de asamblare va compila automat ldr într-o instrucțiune mov. Dar dacă numărul nu poate fi reprezentat în acest fel, atunci compilatorul însuși va rezerva o celulă de memorie pentru această constantă în program și va da el însuși un nume acestei celule de memorie și va compila comanda în ldr .
Iată ce am scris:
ldr r7,my_var
ldr r8,=511
ldr r8,=1024
ldr r9,=0x3456
........
My_var: .word 0x123456
După compilare am primit asta:
18: e59f7030 ldr r7, ; cincizeci
1c: e59f8030 ldr r8, ; 54
20: e3a08b01 mov r8, #1024; 0x400
24: e59f902c ldr r9, ; 58
.............
00000050
50:00123456 .cuvânt 0x00123456
54:000001ff .cuvânt 0x000001ff
58:00003456 .cuvânt 0x00003456
Rețineți că compilatorul folosește adresarea memoriei în raport cu registrul computerului (aka r15 ).
Citirea unei celule de memorie și scrierea unui registru în memorie.
După cum am scris mai sus, procesorul ARM poate efectua numai operații aritmetice sau logice asupra conținutului registrelor. Datele pentru operații trebuie citite din memorie, iar rezultatul operațiunilor trebuie scris înapoi în memorie. Există comenzi speciale pentru aceasta: ldr (probabil din combinația „LoaD Register”) pentru citire și str (probabil „STOre Register”) pentru scriere.
S-ar părea că sunt doar două echipe, dar de fapt au multe variante. Este suficient să ne uităm la modalitățile de codificare a comenzilor ldr /str ale procesorului Amber ARM pentru a vedea câți biți de semnalizare auxiliari L 20 , W 21 , B 22 , U 23 , P 24 , I 25 - și determină specificul comportamentul comenzii:
- Bitul L 20 specifică dacă se scrie sau se citește. 1 - ldr , citit, 0 - str , scrie.
- Bitul B 22 determină dacă se citește/scrie un cuvânt de 32 de biți sau un octet de 8 biți. 1 înseamnă operare pe octeți. Când un octet este citit într-un registru, biții superiori ai registrului sunt setați la zero.
- Bitul I 25 specifică utilizarea câmpului Offset. Dacă I 25 ==0, atunci Offset este interpretat ca un offset numeric, care trebuie fie adăugat la adresa de bază din registru, fie scăzut. Dar a adăuga sau a scădea depinde de bitul U 23 .
(Cond) - condiție pentru efectuarea operației. Se interpretează în același mod ca și pentru comenzile logice/aritmetice - citirea sau scrierea pot fi condiționate.
Astfel, în textul de asamblare, puteți scrie ceva de genul acesta:
ldr r1, @ în registrul r1 citește cuvântul la adresa din registrul r0
ldrb r1, @ în registrul r1 citește octetul la adresa din registrul r0
ldreq r2, @ citire de cuvinte condiționată
ldrgtb r2, @ octet condiționat citit
ldr r3, @ citiți cuvântul la adresa 8 relativ la adresa din registrul r4
ldr r4, @ citiți cuvântul la adresa -16 relativ la adresa din registrul r5
După compilarea acestui text, puteți vedea codurile reale ale acestor comenzi:
0: e5901000 ldr r1,
4: e5d01000 ldrb r1,
8: 05912000 ldreq r2,
c:c5d12000 ldrbgt r2,
10: e5943008 ldr r3,
14: e5154010 ldr r4,
În exemplul de mai sus, folosesc doar ldr , dar str este folosit aproape în același mod.
Există moduri de acces la memorie pre-index și post-index write-back. În aceste moduri, indicatorul de acces la memorie este actualizat înainte sau după executarea instrucțiunii. Dacă sunteți familiarizat cu limbajul de programare C, atunci sunteți familiarizat cu constructele de acces pointer precum ( *psource++;) sau ( a=*++psource;). În procesorul ARM, acest mod de acces la memorie tocmai este implementat. Când se execută o comandă de citire, două registre sunt actualizate simultan - registrul receptor primește valoarea citită din memorie și valoarea din registrul-pointer către celula de memorie se deplasează înainte sau înapoi.
Scrierea acestor comenzi, în opinia mea, este oarecum ilogic. Este nevoie de mult timp să te obișnuiești.
ldr r3, ! @psrc++; r3 = *psrc;
ldr r3, , #4 @ r3 = *psrc; psrc++;
Prima comandă ldr incrementează mai întâi indicatorul, apoi îl citește. A doua comandă citește mai întâi, apoi crește indicatorul. Valoarea pointerului psrc este în registrul r0 .
Toate exemplele de mai sus au fost pentru cazul în care bitul I 25 din codul de comandă a fost resetat. Dar încă se poate instala! Atunci valoarea câmpului Offset nu va conține o constantă numerică, ci al treilea registru implicat în operație. Mai mult decât atât, valoarea celui de-al treilea registru poate fi încă pre-deplasată!
Iată exemple de posibile variații de cod:
0: e7921003 ldr r1, @ adresă de citire - suma valorilor din registrele r2 și r3
4: e7b21003 ldr r1, ! @ la fel, dar după citirea r2 va fi incrementat cu valoarea de la r3
8: e6932004 ldr r2, , r4 @ va citi mai întâi la r3, apoi r3 va fi incrementat cu r4
c: e7943185 ldr r3, @ citiți adresa r4+r5*8
10: e7b43285 ldr r3, ! @ adresa pentru a citi r4+r5*32, după citirea r4 va fi setat la valoarea acestei adrese
14: e69431a5 ldr r3, , r5, lsr #3 @ citiți adresa r4, după executarea comenzii r4 va fi setat la r4+r5/8
Acestea sunt variantele comenzilor de citire/scriere din procesorul ARM v2a.
La modelele mai vechi de procesoare ARM, această varietate de instrucțiuni este și mai mare.
Acest lucru se datorează faptului că procesorul permite, de exemplu, să citească nu numai cuvinte (numere de 32 de biți) și octeți, ci și jumătate de cuvinte (16 biți, 2 octeți). Apoi sufixul „h”, din cuvântul jumătate de cuvânt, este adăugat la comenzile ldr / str. Comenzile vor arăta ca ldrh sau strh. Există, de asemenea, comenzi pentru a încărca jumătăți de cuvinte ldrsh sau octeți ldrsb interpretați ca numere cu semn. În aceste cazuri, bitul cel mai semnificativ al cuvântului de bandă sau octetul încărcat este înmulțit în cei mai semnificativi biți ai întregului cuvânt din registrul de destinație. De exemplu, încărcarea semi-cuvântului 0xff25 cu comanda ldrsh are ca rezultat 0xffffff25 în registrul de destinație.
Citiri și scrieri multiple.
Comenzile ldr /str nu sunt singurele pentru accesarea memoriei. Procesorul ARM are și instrucțiuni care vă permit să efectuați transferul bloc - puteți încărca conținutul mai multor cuvinte consecutive din memorie în mai multe registre deodată. De asemenea, este posibil să scrieți succesiv valorile mai multor registre în memorie.
Mnemonicii comenzilor de transfer de bloc încep cu rădăcina ldm (LoaD Multiple) sau stm (Store Multiple). Dar apoi, ca de obicei în ARM, povestea începe cu sufixe.
În general, comanda arată astfel:
op(cond)(mod) Rd(, {Register list} !}
Sufixul (Cond) este de înțeles, aceasta este condiția pentru executarea comenzii. Sufixul (modul) este modul de transmisie, despre asta mai târziu. Rd este un registru care specifică adresa de bază din memorie de citit sau scris. Un semn de exclamare după registrul Rd indică faptul că acesta va fi modificat după o operație de citire/scriere. Lista registrelor care sunt încărcate din memorie sau descărcate în memorie este (Register list) .
Lista de registre este specificată în acolade separate prin virgule sau ca un interval. De exemplu:
stm r0,(r3,r1, r5-r8)
Scrierea în memorie va fi făcută în neregulă. Lista indică pur și simplu ce registre vor fi scrise în memorie și atât. Codul de comandă are 16 biți rezervați pentru Lista de registre, la fel ca și numărul de registre din banca procesorului. Fiecare bit din acest câmp indică ce registru va fi implicat în operație.
Acum pentru modul citire/scriere. Există unde să te încurci. Faptul este că diferite nume de mod pot fi folosite pentru aceeași acțiune.
Dacă faci o mică digresiune lirică, atunci trebuie să vorbești despre... stiva. Stiva este o modalitate de a accesa datele de tip LIFO - Last In First Out (wiki) - last in, first out. Stiva este utilizată pe scară largă în programare la apelarea procedurilor și salvarea stării registrelor la intrarea în funcții și restabilirea acestora la ieșire, precum și la trecerea parametrilor procedurilor apelate.
Stiva din memorie este, cine ar fi crezut, patru tipuri.
Primul tip este complet descendent. Acesta este momentul în care indicatorul de stivă indică un element de stivă ocupat și stiva crește în direcția adreselor descrescătoare. Când trebuie să puneți un cuvânt pe stivă, atunci mai întâi indicatorul stivei este decrementat (Decrement Before), apoi cuvântul este scris la adresa indicatorului stivei. Când trebuie să eliminați un cuvânt de calculator din stivă, atunci cuvântul este citit la valoarea curentă a indicatorului stivei, apoi indicatorul se mută în sus (Incrementează după).
Al doilea tip este complet ascendent. Stiva nu crește în jos, ci în sus, spre adrese mari. Pointerul indică și elementul ocupat. Când trebuie să puneți un cuvânt pe stivă, atunci mai întâi indicatorul stivei este incrementat, apoi cuvântul este scris de indicator (Increment Before). Când este necesar să scoatem din stivă, citim mai întâi prin indicatorul stivei, deoarece indică către elementul ocupat, apoi indicatorul stivei este decrementat (Decrement After).
Al treilea tip este Empty Descending. Stiva crește în jos, ca în cazul Full Descending , dar diferența este că pointerul stivei indică o celulă neocupată. Astfel, atunci când trebuie să puneți un cuvânt pe stivă, se face imediat o înregistrare, apoi indicatorul stivei este decrementat (Decrement After). Când se scoate din stivă, indicatorul este mai întâi incrementat, apoi citit (Increment Before).
Al patrulea tip este Empty Ascending. Sper că totul este clar - stiva crește. Indicatorul de stivă indică un element gol. Apăsați pe stivă înseamnă să scrieți un cuvânt la adresa indicatorului stivei și să incrementați indicatorul stivei (Increment After ). Ieșiți din stivă - reduceți indicatorul stivei și citiți cuvântul (Decrement Before ).
Astfel, în timpul operațiunilor cu stiva, trebuie să creșteți sau să micșorați indicatorul - (Increment / Decrement ) înainte sau după (Before / After ) citire / scriere în memorie, în funcție de tipul de stivă. Procesoarele Intel, de exemplu, au comenzi speciale pentru lucrul cu stiva, cum ar fi PUSH (puneți un cuvânt pe stivă) sau POP (despărțiți un cuvânt din stivă). Nu există comenzi speciale în procesorul ARM, dar sunt folosite comenzile ldm și stm.
Dacă implementați stiva folosind instrucțiunile procesorului ARM, atunci obțineți următoarea imagine:
De ce aceeași echipă trebuia să primească nume diferite? Nu înțeleg deloc... Aici, desigur, trebuie remarcat că standardul stivei pentru ARM este încă Full Descending.
Indicatorul stivei de pe un procesor ARM este registrul sp sau r13. Acesta este, în general, un astfel de acord. Desigur, scrierea stm sau citirea ldm se poate face și cu alte registre de bază. Cu toate acestea, trebuie să vă amintiți cum diferă registrul sp de alte registre - poate fi diferit în diferite moduri de operare a procesorului (USR, SVC, IRQ, FIRQ), deoarece au propriile bănci de registre.
Și încă o notă. Scrieți în codul de asamblare ARM o linie ca apăsați (r0-r3), Da, cu siguranță poți. Dar de fapt va fi aceeași comandă stmfd sp!,(r0-r3).
În cele din urmă, voi da un exemplu de cod de asamblare și textul său compilat dezasamblat. Avem:
stmfd sp!,(r0-r3)
stmdb sp!,(r0-r3)
apăsați (r0-r3)
@aceste trei instrucțiuni sunt aceleași și fac același lucru
pop(r0-r3)
ldmia sp!,(r0-r3)
ldmfd r13!,(r0-r3)
Stmfd r4,(r0-r3,r5,r8)
stmea r4!,(r0-r3,r7,r9,lr,pc)
ldm r5,(r0,buc)
Primim după compilare:
0: e92d000f push(r0, r1, r2, r3)
4: e92d000f apăsare (r0, r1, r2, r3)
8: e92d000f apăsare (r0, r1, r2, r3)
c:e8bd000f pop(r0, r1, r2, r3)
10: e8bd000f pop (r0, r1, r2, r3)
14: e8bd000f pop (r0, r1, r2, r3)
18: e904012f stmdb r4, (r0, r1, r2, r3, r5, r8)
1c: e8a4c28f stmia r4!, (r0, r1, r2, r3, r7, r9, lr, pc)
20: e8958001 ldm r5, (r0, pc)
Tranziții în programe.
Programarea nu este posibilă fără tranziții. În orice program, există atât execuția ciclică a codului, cât și apelurile la proceduri, funcții, există și execuția condiționată a secțiunilor de cod.
În procesorul Amber ARM v2a există doar două comenzi: b (din cuvântul Branch - ramură, tranziție) și bl (Sucursală cu Link - tranziție cu salvarea adresei de retur).
Sintaxa comenzii este foarte simplă:
b(cond) etichetă
bl(cond) etichetă
Este clar că orice tranziție poate fi condiționată, adică în program pot exista astfel de cuvinte ciudate formate din rădăcinile „b” și „bl” și sufixe de condiție (Cond), cuvinte ciudate:
beq, bne, bcs, bhs, bcc, blo, bmi, bpl, bvs, bvc, bhi, bls, bge, bgt, ble, bal, b
bleq, blne, blcs, blhs, blcc, bllo, blmi, blpl, blvs, blvc, blhi, blls, blge, blgt, blle, blal, bl
Varietatea este uimitoare, nu-i așa?
Instrucțiunea de salt conține un offset de 24 de biți. Adresa de salt este calculată ca suma valorii curente a indicatorului pc și a numărului de offset deplasat cu 2 biți la stânga, interpretat ca un număr cu semn:
PC nou = pc + Offset*4
Astfel, intervalul de salt este de 32 MB înainte sau înapoi.
Să luăm în considerare ce este o sucursală cu salvarea adresei de retur bl. Această comandă este folosită pentru a apela subrutine. O caracteristică interesantă a acestei instrucțiuni este că adresa de retur din procedura atunci când procedura este apelată nu este stocată pe stivă, ca în cazul procesoarelor Intel, ci în registrul obișnuit r14 . Apoi, pentru a reveni din procedură, nu este necesară o comandă ret specială, ca și în cazul acelorași procesoare Intel, ci pur și simplu puteți copia valoarea lui r14 înapoi pe pc . Acum este clar de ce registrul r14 are un nume alternativ lr (Link Register).
Luați în considerare rutina outbyte din proiectul hello-world pentru Amber SoC.
000004a0<_outbyte>:
4a0: e59f1454 ldr r1, ; 8fc< адрес регистра данных UART >
4a4: e59f3454 ldr r3, ; 900< адрес регистра статуса UART >
4a8: e5932000 ldr r2, ; citiți starea curentă
4ac: e2022020 și r2, r2, #32
4b0: e3520000 cmp r2, #0; verificați dacă UART nu este ocupat
4b4: 05c10000 strbeq r0, ; scrieți un caracter în UART numai dacă nu este ocupat
4b8: 01b0f00e movseq pc, lr ; revenire condiționată de la procedură dacă UART nu era ocupat
4bc: 1affff9 bne 4a8<_outbyte+0x8>; buclă pentru a verifica starea UART
Cred că din comentariile acestui fragment este clar cum funcționează această procedură.
O altă notă importantă despre tranziții. Registrul r15 (pc) poate fi utilizat în operații aritmetice sau logice normale ca registru de destinație. Așadar, o comandă de genul add pc,pc,#8 este destul de o instrucțiune pentru a trece la o altă adresă.
Mai trebuie făcută o remarcă despre tranziții. Procesoarele ARM mai vechi au, de asemenea, instrucțiuni suplimentare de salt bx, blx și blj. Acestea sunt comenzi pentru saltul la fragmente de cod cu un sistem de comandă diferit. Bx /blx vă permite să faceți o tranziție la codul THUMB pe 16 biți al procesoarelor ARM. Blj este un apel de procedură al sistemului de comandă Jazelle (suport pentru limbajul Java în procesoarele ARM). Amber ARM v2a nu are aceste comenzi.
Alte instrucțiuni ale procesorului ARM.
1. Contorul ceasului în timp real trebuie să fie activat (1); Bitul de selectare a sursei ceasului este șters (2) dacă ceasul nu este de la ceasul principal.
2. Unul sau ambii biți de selectare a evenimentului de întrerupere (3) trebuie să fie setați. Și se alege ce evenimente vor declanșa cererea de întrerupere (5).
3. Trebuie setate măștile pentru evenimente de întrerupere (4, 7).
2.5 Despre programarea ARM7 în asamblator
Setul de instrucțiuni ARM7 (secțiunea 1.4) include doar 45 de instrucțiuni, care sunt destul de complexe datorită varietății de metode de adresare, câmpuri condiționate și modificatori. Programul în limbaj de asamblare este greoi și
Cu greu de citit. Prin urmare, asamblatorul este rar folosit în programare pentru arhitectura ARM7.
Cu toate acestea, limbajul C de nivel înalt ascunde multe caracteristici arhitecturale de la programator. Programatorul practic nu atinge proceduri precum alegerea modului kernel, alocarea memoriei pentru stivă și gestionarea întreruperilor. Pentru a învăța aceste proceduri, este util să scrieți cel puțin un program simplu în limbaj de asamblare.
În plus, chiar și atunci când se folosește C, mai trebuie să se recurgă la limbajul de asamblare.
1. Ar trebui controlat Compilatorul C, ținând evidența dacă a exclus instrucțiuni importante în timpul optimizării, considerându-le inutile. Compilatorul generează cod extrem de ineficient pentru o operație relativ simplă, din cauza optimizării insuficiente. Pentru a vă asigura că compilatorul folosește cu adevărat acele resurse hardware care sunt concepute pentru a crește eficiența unui anumit algoritm.
2. În timpul căutării erorilor sau cauzelor excepțiilor (Secțiunea 2.4.1).
3. Pentru a obține un cod care este absolut optim în ceea ce privește performanța sau consumul de memorie (secțiunile 2.2.20, 3.1.5).
Luați în considerare tehnicile de bază pentru compilarea unui program în asamblator
Cu scopul de a demonstra întregul cod executat de microcontroler, așa cum este și fără intermediar compilator C.
Procedura de creare a unui proiect bazat pe asamblare este aproape aceeași ca și pentru programele C (secțiunile 2.3.1–2.3.3). Există doar două excepții:
a) extensia *.S este atribuită fișierului text sursă;
b) aici se presupune că fișierul STARTUP.S nu este conectat la program.
2.5.1 Reguli de bază pentru scrierea programelor în asamblator
Se obișnuiește să se aranjeze textul programului în asamblator în patru coloane. Putem spune că fiecare linie este formată din patru câmpuri și anume: câmpuri de etichete, operații, operanzi, comentarii. Câmpurile sunt separate între ele prin file sau spații.
Domeniile principale sunt operațiile și operanzii. Operațiile valide și sintaxa lor sunt date în tabelul (1.4.2)
O etichetă este o reprezentare simbolică a adresei unei comenzi. Peste tot, în locul etichetei, se va înlocui adresa comenzii precedată de etichetă. Cel mai adesea, etichetele sunt folosite în comenzile de transfer de control. Fiecare etichetă trebuie să fie unică și este opțională. Spre deosebire de multe alte versiuni, etichetele din ansamblul RealView nu se termină cu două puncte (":").
Comentariile sunt plasate opțional la sfârșitul unei linii și separate prin punct și virgulă ("; ").
Să luăm un exemplu simplu.
2.5.2 Pseudocomenzi
Asamblatorul RealView acceptă așa-numitele pseudo-comenzi. O pseudo-instrucțiune este o notație mnemonică care nu corespunde de fapt cu sistemul de instrucțiuni al procesorului, dar este înlocuită cu una sau (mai rar) mai multe instrucțiuni. Pseudo-comenzile sunt un fel de macrocomenzi și servesc la simplificarea sintaxei. Lista pseudo-comenzilor acceptate este dată în tabelul (2.5.1).
2.5.3 Directive de asamblare
Spre deosebire de instrucțiuni, directivele nu creează cod executabil care este încărcat în memoria microcontrolerului. Directivele sunt doar instrucțiuni pentru asamblator, ele controlează formarea codului executabil.
Să aruncăm o privire la directivele de asamblare RealView 4 utilizate în mod obișnuit.
Constanta numelui EQU
Atribuie desemnatorul simbolic Nume constantei, care devine sinonim pentru constantă. Scopul principal este introducerea denumirilor registrelor de control,
Nume ZONA, Opțiuni
Definește o regiune de memorie cu numele dat. Parametrii specifică scopul unei zone de memorie, cum ar fi DATE (date) sau COD (cod). Adresele zonei definite depind de destinația selectată. Zona COD este localizată începând de la adresa 0x00000000, zona DATE - de la adresa 0x40000000. Programul trebuie să aibă o regiune CODE numită RESET. Constantele alocate în memoria programului ar trebui să fie declarate într-o secțiune cu o pereche de parametri CODE, READONLY.
Desemnează punctul de intrare în program, arată „începutul” acestuia. O astfel de directivă trebuie să fie întotdeauna prezentă într-un program. De obicei plasat imediat după directiva AREA RESET, CODE.
Tabelul 2.5.1 - Pseudo-comenzi acceptate de asamblatorul RealView 4
Notație mnemonică |
Operațiune |
Implementarea efectivă |
||||||
și sintaxă |
||||||||
ADR(Cond) |
a înregistra |
Adăugarea sau scăderea unei constante dintr-un PC co- |
||||||
comenzi ADD sau SUB |
||||||||
ADRL(Cond.) |
a înregistra |
Dublu ADD sau SUB cu PC |
||||||
(gamă extinsă de adrese) |
||||||||
ASR(Cond)(S) |
Deplasare aritmetică la dreapta |
|||||||
ASR(Cond)(S) |
operand de schimbare |
|||||||
LDR(Cond) |
a înregistra |
|||||||
adresare (PC + offset direct) |
||||||||
Plasarea unei constante |
în memoria programului |
|||||||
LDR(de la adresa index- |
||||||||
ție. Offset-ul este PC. |
||||||||
LSL(Cond)(S) |
Deplasare logică la stânga |
|||||||
LSL(Cond)(S) |
operand de schimbare |
|||||||
LSR(Cond)(S) |
Schimbarea logica la dreapta |
|||||||
LSR(Cond)(S) |
operand de schimbare |
|||||||
POP(Conv.) |
Restaurați registrele din stivă |
Recuperare |
registre |
echipă |
||||
LDMIA R13!,(...) |
||||||||
PUSH(Cond) |
Conservare |
registre |
echipă |
|||||
STMDB R13!,(...) |
||||||||
ROR(Cond)(S) |
Invarte spre dreapta |
|||||||
ROR(Cond)(S) |
operand de schimbare |
|||||||
RRX(Conv.)(S) |
Rotiți direct |
|||||||
transferați la 1 cifră |
operand de schimbare |
|||||||
Nume SPAȚIU Dimensiune
Rezervă memorie pentru stocarea datelor cu dimensiunea dată. Numele devine sinonim cu adresa spațiului rezervat. Unitatea spațiului de adrese permite ca această directivă să fie aplicată atât memoriei permanente, cât și aleatorii. Scopul principal este de a crea variabile globale în RAM (în zona DATE).
Etichetă DCB/DCW/DCD Constant
„Cosă” datele (constantele numerice) în memoria programului. Eticheta devine sinonimă cu adresa la care vor fi scrise datele. Diferite directive (DCB , DCW și DCD ) sunt utilizate pentru date de diferite dimensiuni: octet, cuvânt de 16 biți, cuvânt de 32 de biți (respectiv).
Servește ca semn de sfârșit de fișier. Tot textul după această directivă este ignorat de către asamblator.
2.5.4 Macrocomenzi
O macrocomandă este o parte predefinită a unui program care efectuează o operațiune comună. Spre deosebire de subrutinele numite cu comenzi de transfer de control, utilizarea macro-urilor nu reduce performanța, dar nu reduce consumul de memorie de program. Pentru că la fiecare apel macro, asamblatorul înglobează întregul său text în program.
Următoarea construcție este folosită pentru a declara o macrocomandă
$Parameter1, $Parameter2, ... |
|
Parametrii vă permit să modificați textul unei macrocomenzi de fiecare dată când este apelată. În interiorul (în corpul) unei macrocomenzi, parametrii sunt utilizați și cu semnul „$” precedent. În locul parametrilor din corpul macro, sunt înlocuiți parametrii specificați în timpul apelului.
Macro-ul se numește astfel:
Nume Parametrul1, Parametrul2,...
Este posibil să se organizeze verificarea stării și ramificarea.
IF "$Parameter" == "Valoare"
Vă rugăm să rețineți că acest design nu duce la o verificare a programului a stării de către microcontroler. Condiția este verificată de către asamblator în timpul formării codului executabil.
Această secțiune descrie seturile de instrucțiuni ale procesorului ARM7TDMI.
4.1 Scurtă descriere a formatului
Această secțiune oferă o scurtă descriere a setului de instrucțiuni ARM și Thumb.
Cheia pentru tabelele setului de instrucțiuni este prezentată în Tabelul 1.1.
Procesorul ARM7TDMI se bazează pe arhitectura ARMv4T. Pentru o descriere mai completă a ambelor seturi de instrucțiuni, consultați „Manualul de referință pentru arhitectura ARM”.
Tabelul 1.1. Cheia pentru mese
Formatele setului de instrucțiuni ARM sunt prezentate în Figura 1.5.
Pentru mai multe informații despre formatele setului de instrucțiuni ARM, consultați „Manualul de referință pentru arhitectură ARM”.
Figura 1.5. Formate setului de instrucțiuni ARM
Unele coduri de instrucțiuni nu sunt definite, dar nu provoacă o căutare a instrucțiunilor nedefinite, cum ar fi o instrucțiune de multiplicare cu bitul 6 schimbat la 1. efectul lor poate fi modificat în viitor. Rezultatul executării acestor coduri de instrucțiuni ca parte a procesorului ARM7TDMI este imprevizibil.
4.2 Scurtă descriere a instrucțiunilor ARM
Setul de instrucțiuni ARM este prezentat în Tabelul 1.2.
Tabelul 1.2. Scurtă introducere a instrucțiunilor ARM
Operațiuni | Sintaxa de asamblare | |
Redirecționare | Redirecționare | MOV(cond)(S)Rd, |
Înainte NU | MVN(cond)(S)Rd, |
|
Transferați SPSR pentru a vă înregistra | MRS (cond) Rd, SPSR | |
Se transferă CPSR la înregistrare | MRS (cond) Rd, CPSR | |
Transfer de registru SPSR | MSR(cond) SPSR(câmp), Rm | |
Redirecționarea CPSR | MSR(cond) CPSR(câmp), Rm | |
Redirecționarea unei constante către steaguri SPSR | MSR (cond.) SPSR_f, #32bit_Imm | |
Redirecționarea unei constante către steaguri CPSR | MSR (cond.) CPSR_f, #32bit_Imm | |
Aritmetic | Plus | ADD (cond)(S) Rd, Rn, |
Supliment cu transport | ADC (cond)(S) Rd, Rn, |
|
Scădere | SUB (cond)(S) Rd, Rn, |
|
Scăderea cu purtare | SBC (cond)(S) Rd, Rn, |
|
scădere scădere inversă | RSB (cond)(S) Rd, Rn, |
|
Scădere scădere inversă cu purtare | RSC (cond)(S) Rd, Rn, |
|
Multiplicare | MUL (cond)(S) Rd, Rm, Rs | |
multiplica-acumulează | MLA (cond)(S) Rd, Rm, Rs, Rn | |
Înmulțiți numere lungi nesemnate | UMULL | |
Înmulțire - acumulare nesemnată de valori lungi | UMLAL (cond)(S) RdLo, RdHi, Rm, Rs | |
Înmulțiți lungi semnate | SMULL (cond)(S) RdLo, RdHi, Rm, Rs | |
Înmulțirea - acumularea semnată a valorilor lungi | SMLAL (cond)(S) RdLo, RdHi, Rm, Rs | |
Comparaţie | CMP (cond)Rd, |
|
Comparația este negativă | CMN(cond)Rd, |
|
joc de inteligență | Examinare | TST (cond)Rn, |
Verificarea echivalenței | TEQ(cond)Rn, |
|
Buturuga. Și | AND (cond)(S) Rd, Rn, |
|
Excl. SAU | EOR (cond)(S) Rd, Rn, |
|
ORR | ORR (cond)(S) Rd, Rn, |
|
Resetare bit | BIC (cond)(S) Rd, Rn, |
|
Tranziție | Tranziție | (cond) etichetă |
Urmând un link | (cond) etichetă | |
Saritura si schimbarea setului de instructiuni | (cond)Rn | |
Citind | cuvintele | LDR (cond)Rd, |
LDR (cond)T Rd, |
||
octeți | LDR (cond) B Rd, |
|
LDR (cond) BT Rd, |
||
octet semnat | LDR (cond)SB Rd, |
|
jumatate de cuvinte | LDR (cond)H Rd, |
|
semicuvinte cu semn | LDR (cond)SH Rd, |
|
operațiuni pe mai multe blocuri de date | - | |
LDM (cond)IB Rd(, !} |
||
LDM (cond)IA Rd(, !} |
||
LDM (cond)DB Rd(, !} |
||
LDM (cond)DA Rd(, !} |
||
LDM (cond.) |
||
LDM (cond.) |
||
operarea stivei cu registre de utilizatori | LDM (cond.) |
|
Înregistrare | cuvintele | STR (cond) Rd, |
cuvinte cu preferință pentru modul utilizator | STR (cond)T Rd, |
|
octeți | STR (cond) B Rd, |
|
octeți cu preferință pentru modul utilizator | STR (cond) BT Rd, |
|
jumatate de cuvinte | STR (cond)H Rd, |
|
operațiuni pe mai multe blocuri de date | - | |
STM (cond)IB Rd(, !} |
||
STM (cond)IA Rd(, !} |
||
STM (cond)DB Rd(, !} |
||
o urmată de o scădere | STM (cond)DA Rd(, !} |
|
STM (cond.) |
||
STM (cond.) |
||
schimb valutar | cuvinte | SWP (cond) Rd, Rm, |
octet | SWP (cond) B Rd, Rm, | |
coprocesor | Operație pe date | CDP(cond)p |
Transfer în registrul ARM de la coprocesor | MRC(cond)p |
|
Transfer la coprocesor din registrul ARM | MCR(cond)p |
|
Citind | LDC(cond)p |
|
Înregistrare | STC(cond)p |
|
Întreruperea software-ului | SWI 24bit_Imm |
Vă puteți familiariza cu sistemul de comandă în modul ARM în detaliu.
Moduri de adresare
Modurile de adresare sunt proceduri care sunt utilizate de diverse instrucțiuni pentru a genera valorile utilizate de instrucțiuni. Procesorul ARM7TDMI acceptă 5 moduri de adresare:
- Modul 1 - Operanzi de schimbare pentru instrucțiunile de prelucrare a datelor.
- Modul 2 - Citiți și scrieți cuvânt sau octet fără semn.
- Modul 3 - Citiți și scrieți jumătate de cuvânt sau încărcați octet de semn.
- Modul 4 - Citire și scriere multiplă.
- Modul 5 - Citire și scriere coprocesor.
Modurile de adresare cu indicarea tipurilor și codurilor mnemonice ale acestora sunt prezentate în Tabelul 1.3.
Tabelul 1.3. Moduri de adresare
Modul de adresare | Tipul sau modul de adresare | Cod mnemonic sau tip stivă |
Modul 2 |
Offset constantă | |
Registrul de compensare | ||
Registrul de scară offset | ||
Offset preindexat | - | |
Constant | ! | |
Inregistreaza-te | ! | |
registru de scară | ! | |
! | ||
! | ||
! | ||
! | ||
- | ||
Constant | , #+/-12bit_Offset | |
Inregistreaza-te | , +/-Rm | |
registru de scară | ||
Modul 2, privilegiat |
Offset constantă | |
Registrul de compensare | ||
Registrul de scară offset | ||
Offset urmat de indexare | - | |
Constant | , #+/-12bit_Offset | |
Inregistreaza-te | , +/-Rm | |
registru de scară | , +/-Rm, LSL #5bit_shift_imm | |
, +/-Rm, LSR #5bit_shift_imm | ||
, +/-Rm, ASR #5bit_shift_imm | ||
, +/-Rm, ROR #5bit_shift_imm | ||
Modul 3 |
Offset constantă | |
! | ||
Indexarea ulterioară | , #+/-8bit_Offset | |
Inregistreaza-te | ||
Pre-indexare | ! | |
Indexarea ulterioară | , +/-Rm | |
Modul 4, citire |
IA, majorare ulterioară | FD, complet descendent |
ED, gol descendent | ||
DA, diminuare ulterioară | FA, complet ascendent | |
DB pre-decrementare | EA, gol ascendent | |
Modul 4, înregistrare |
IA, majorare ulterioară | FD, complet descendent |
IB, pre-increment | ED, gol descendent | |
DA, diminuare ulterioară | FA, complet ascendent | |
DB pre-decrementare | EA, gol ascendent | |
Modul 5, transfer de date coprocesor |
Offset constantă | |
Pre-indexare | ! | |
Indexarea ulterioară | , #+/-(8bit_Offset*4) |
Operandul 2
Un operand este o parte a unei instrucțiuni care se referă la date sau la un periferic. Operanzii 2 sunt prezentați în tabelul 1.4.
Tabelul 1.4. Operandul 2
Câmpurile sunt prezentate în tabelul 1.5.
Tabelul 1.5. câmpuri
Câmpuri de condiții
Câmpurile de condiții sunt prezentate în Tabelul 1.6.
Tabelul 1.6. Câmpuri de condiții
Tipul câmpului | Sufix | Descriere | Condiție |
Stare (cond) | EQ | Egal | Z=1 |
NE | Nu este egal | Z=0 | |
CS | Nesemnat mai mare sau egal cu | C=1 | |
CC | Nesemnat mai puțin | C=0 | |
MI | negativ | N=1 | |
PL | pozitiv sau zero | N=0 | |
VS | Revărsare | V=1 | |
VC | Fara preaplin | V=0 | |
BUNĂ | Mai mult nesemnat | C=1, Z=0 | |
LS | Nesemnat mai mic sau egal cu | C=0, Z=1 | |
GE. | Mai mult sau egal | N=V (N=V=1 sau N=V=0) | |
LT | Mai puțin | NV (N=1 și V=0) sau (N=0 și V=1) | |
GT | Mai mult | Z=0, N=V (N=V=1 sau N=V=0) | |
LE | Mai puțin sau egal | Z=0 sau NV (N=1 și V=0) sau (N=0 și V=1) | |
AL | Întotdeauna adevărat | steagurile sunt ignorate |
4.3 Scurtă descriere a setului de instrucțiuni Thumb
Formatele setului de instrucțiuni Thumb sunt prezentate în Figura 1.6. Pentru mai multe informații despre formatele setului de instrucțiuni ARM, consultați Manualul de referință pentru arhitectură ARM.
Figura 1.6. Formate set de instrucțiuni pentru degetul mare
Setul de instrucțiuni Thumb este prezentat în Tabelul 1.7.
Tabelul 1.7. Scurtă descriere a setului de instrucțiuni Thumb
Operațiune | Sintaxa de asamblare | |
Redirecționare (copiere) | constante | MOV Rd, #8bit_Imm |
senior la junior | MOV Rd, Hs | |
junior până la senior | MOV HD, Rs | |
senior la senior | MOV Hd, Hs | |
Aritmetic | plus | ADD Rd, Rs, #3bit_Imm |
adăugați minor la minor | ADD Rd, Rs, Rn | |
adaugă mai în vârstă la mai tânără | ADD Rd, Hs | |
adăugați mai tânăr la mai în vârstă | ADAUGĂ HD, Rs | |
adăugați mai vechi la mai vechi | ADAUGĂ Hd, Hs | |
adaos cu o constantă | ADD Rd, #8bit_Imm | |
adaugă valoare SP | ADD SP, #7bit_Imm ADD SP, #-7bit_Imm | |
plus cu carry | ADC Rd, Rs | |
scădere | SUB Rd, Rs, Rn SUB Rd, Rs, #3bit_Imm | |
scădere constantă | SUB Rd, #8bit_Imm | |
scade cu carry | SBC Rd, Rs | |
inversarea semnelor | NEG Rd, Rs | |
multiplicare | MUL Rd, Rs | |
compara junior cu junior | CMP Rd, Rs | |
compara juniori si seniori | CMP Rd, Hs | |
comparați mai în vârstă cu mai tineri | CMP HD, Rs | |
compara mai vechi cu mai vechi | CMP Hd, Hs | |
compara negativ | CMN Rd, Rs | |
compara cu constanta | CMP Rd, #8bit_Imm | |
joc de inteligență | Și | ȘI Rd, Rs |
Excl. SAU | EOR Rd, Rs | |
SAU | ORR Rd, Rs | |
Resetare bit | BIC Rd, Rs | |
Înainte NU | MVN Rd, Rs | |
testarea biților | TST Rd, Rs | |
Shift/Rotire | Deplasare logică la stânga | LSL Rd, Rs, #5bit_shift_imm LSL Rd, Rs |
Schimbarea logica la dreapta | LSR Rd, Rs, #5bit_shift_imm LSR Rd, Rs | |
Deplasare aritmetică la dreapta | ASR Rd, Rs, #5bit_shift_imm ASR Rd, Rs | |
Rotire dreapta | ROR Rd, Rs | |
Tranziție | salturi condiționate | - |
Eticheta BEQ | ||
Eticheta BNE | ||
Eticheta BCS | ||
Eticheta BCC | ||
Eticheta IMC | ||
Eticheta BPL | ||
Eticheta BVS | ||
Eticheta BVC | ||
eticheta BHI | ||
Eticheta BLS | ||
Eticheta BGE | ||
Eticheta BLT | ||
Eticheta BGT | ||
eticheta BLE | ||
Salt necondiționat | eticheta B | |
Link lung | Eticheta BL | |
Schimbarea opțională a stării | - | |
BX Rs | ||
BX Hs | ||
Citind | cu constantă de offset | - |
Drumul LDR, | ||
LDRH Rd, | ||
LDRB Road, | ||
cu registru offset | - | |
Drumul LDR, | ||
LDRH Rd, | ||
Drumul LDRSH, | ||
LDRB Road, | ||
Drumul LDRSB, | ||
raportat la contorul de programe PC | Drumul LDR, | |
în raport cu indicatorul de stivă SP | Drumul LDR, | |
Abordare | - | |
ADD Rd, PC, #10bit_Offset | ||
ADD Rd, SP, #10bit_Offset | ||
Lectură multiplă | LDMIA Rb!, |
|
Înregistrare | cu constantă de offset | - |
STR Road, | ||
drumul strh, | ||
drumul strb, | ||
cu registru offset | - | |
STR Road, | ||
drumul strh, | ||
drumul strb, | ||
referitor la SP | STR Road, | |
Multiple intrari | STMIA Rb!, |
|
Împingere/popping din stivă | Împingeți registrele pe stivă | APĂSAŢI |
Apăsați LR și registrele pe stivă | APĂSAŢI |
|
Pop registre din stivă | POP |
|
Pop registre și PC din stivă | POP |
|
Întreruperea software-ului | - | SWI 8bit_Imm |