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

GBA ASM - Ziua 2: Câteva informații despre asamblatorul ARM - Arhiva WASM.RU

ARM este compania care produce procesorul GBA. Procesoarele ARM sunt procesoare RISC (spre deosebire de procesoarele INTEL). RISC înseamnă Reduced Instruction Set Computers (CISC - Complex ...). În timp ce aceste procesoare nu au multe instrucțiuni (ceea ce este un lucru bun), instrucțiunile ARM (și poate și alte procesoare RISC, nu știu) au multe utilizări și combinații diferite, ceea ce face ca procesoarele RISC să fie la fel de puternice ca și ele.

Registrele

Nu știu despre alte procesoare ARM, dar cel folosit în GBA are 16 registre și spre deosebire de procesoarele Intel (și altele), toate registrele pot fi folosite în siguranță (de obicei). Registrele sunt după cum urmează:

r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15

Wow! Mult! explic in ordine.

ro: Fă ce vrei!

r2 la r12: la fel

r13: Pe unele sisteme ARM, r13 este un pointer de stivă (SP pe procesoarele INTEL). Nu sunt sigur dacă r13 joacă același rol în GBA, pot doar să vă avertizez să fiți atenți cu el atunci când lucrați cu stiva.

r14: Conține adresa de retur pentru procedurile apelate. Dacă nu le folosiți, atunci puteți face ce doriți cu ele.

r15: Contor de program și steaguri, la fel ca IP (Instruction Pointer pe Intel). Acesta diferă de registrul IP al Intel prin faptul că aveți acces gratuit la acesta, la fel ca orice alt registru, dar rețineți că schimbarea acestuia va determina transferul controlului într-o altă secțiune de cod, iar steagurile se vor schimba .

Să facem un mic exercițiu de matematică... 16 minus 3 (de obicei) ne oferă 13 registre. Nu e misto? Ia-o usor.

Acum s-ar putea să vă întrebați ce sunt registrele cu adevărat. Registrele sunt zone speciale de memorie care fac parte din procesor și nu au o adresă validă, ci sunt cunoscute doar după numele lor. Registrele sunt de 32 de biți. Aproape totul în orice limbaj de asamblare folosește registre, așa că ar trebui să le cunoașteți la fel de bine ca și rudele dvs.

Instrucțiuni de asamblare ARM

În primul rând, vreau să încep prin a spune că, în opinia mea, cine a inventat asamblatorul ARM este un geniu.

În al doilea rând, vreau să-l prezint pe bunul meu prieten CMP. Salută-l și poate, doar poate, va deveni și el prietenul tău. CMP înseamnă CoMPare (compara). Această instrucțiune poate compara registrul și numărul, registrul și registrul sau registrul și locația de memorie. Apoi, după comparație, CMP setează steaguri de stare care vă spun rezultatul comparării. După cum vă amintiți, registrul r15 conține steaguri. Ei raportează rezultatul comparației. Instrucțiunea CMP a fost concepută special pentru a seta valoarea acestor steaguri și nimic altceva.

Steagurile pot conține următoarele stări:

    EQ Equal / Equal

    NE Nu este egal

    Set VS Overflow / Set Overflow

    VC overflow Clear

    HI Mai mare / Mai sus

    LS mai jos sau la fel

    PL Plus / Plus

    MI minus / minus

    Set de transport CS

    CC Carry Clear

    GE Mai mare sau egal

    GT Mai mare decât / Mai mult

    LE Mai mic sau egal

    LT Mai puțin decât

    Z este zero

    NZ nu este zero / nu este zero

Aceste stări joacă un rol foarte important în asamblatorul ARM.

NOTĂ: Indicatoarele stochează numai condițiile (Egal cu, Mai mică decât și așa mai departe). Nu mai contează.

Sufixe de condiție

Ați văzut deja instrucțiunea B (Filială). Instrucțiunea B face ceea ce se numește o ramură necondiționată (cum ar fi GoTo în Basic sau JMP în asamblarea INTEL). Dar poate avea un sufix (unul dintre cele enumerate mai sus), caz în care verifică dacă starea steagurilor îi corespunde. Dacă nu, atunci instrucțiunea de salt pur și simplu nu este executată. Deci, dacă doriți să verificați dacă registrul r0 este egal cu registrul r4 și apoi să săriți la o etichetă numită label34, atunci trebuie să scrieți următorul cod:

    CMP r0, r4; Comentariile în limbajul de asamblare vin după punct și virgulă (;)

    BEQ label34 ; B este o instrucțiune de săritură și EQ este un sens sufix

    ; „Dacă egal”

NOTĂ: În Goldroad Assembler, etichetele nu trebuie să fie urmate de (și nu ar trebui să existe nimic pe șir în afară de numele etichetei.

NOTĂ II: Nu este necesar să scrieți CMP și BEQ cu majuscule, aceasta este doar pentru a vă facilita înțelegerea.

Acum știi să sari în funcție de starea steagurilor, dar ceea ce nu știi este că poți face orice în funcție de starea steagurilor, doar adaugă sufixul necesar oricărei instrucțiuni!

De asemenea, nu trebuie să utilizați CMP pentru a seta starea steagurilor. Dacă doriți, de exemplu, ca instrucțiunea SUB (Scădere) să seteze steaguri, adăugați un sufix „S” la instrucțiune (sunt de la „Setați steaguri”). Acest lucru este util dacă nu doriți să setați starea steagurilor cu o instrucțiune CMP suplimentară, astfel încât să puteți face acest lucru și să săriți dacă rezultatul a fost zero, după cum urmează:

    SUBS r0,r1,0x0FF ; Setează steaguri în funcție de rezultatul execuției

    ; instrucțiuni

    ldrZ r0,=0x0FFFF ; Încărcați în registrul r0 0x0FFFF numai dacă starea

    steaguri este zero.

Revizuire

Astăzi am învățat (un pic mai mult) despre registre. Am aflat, de asemenea, despre flexibilitatea instrucțiunilor ARM, care pot fi executate (sau nu) în funcție de starea steagurilor. Am învățat multe astăzi.

Mâine vom folosi cunoștințele de asamblare ARM dobândite astăzi pentru a afișa imaginea pe ecranul GBA.

Ceva imposibil este imposibil doar până când devine posibil / Jean-Luc Picard, Capt. , USS Enterprise/. Mike H, trad. Acvila

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

      1. 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 -
  • cu pre-increment
  • LDM (cond)IB Rd(, !} {^}
  • urmat de increment
  • LDM (cond)IA Rd(, !} {^}
  • cu decrementare prealabilă
  • LDM (cond)DB Rd(, !} {^}
  • urmată de o scădere
  • LDM (cond)DA Rd(, !} {^}
  • operațiunea stivei
  • LDM (cond.) Rd(, !}
  • operarea stivei și recuperarea CPSR
  • LDM (cond.) Rd(, !} ^
    operarea stivei cu registre de utilizatori LDM (cond.) Rd(, !} ^
    Î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 -
  • cu pre-increment
  • STM (cond)IB Rd(, !} {^}
  • urmat de increment
  • STM (cond)IA Rd(, !} {^}
  • cu decrementare prealabilă
  • STM (cond)DB Rd(, !} {^}
    o urmată de o scădere STM (cond)DA Rd(, !} {^}
  • operațiunea stivei
  • STM (cond.) Rd(, !}
  • operarea stivei cu registre de utilizatori
  • STM (cond.) Rd(, !} ^
    schimb valutar cuvinte SWP (cond) Rd, Rm,
    octet SWP (cond) B Rd, Rm,
    coprocesor Operație pe date CDP(cond)p , , CRd, CRn, CRm,
    Transfer în registrul ARM de la coprocesor MRC(cond)p , , Rd, CRn, CRm,
    Transfer la coprocesor din registrul ARM MCR(cond)p , , Rd, CRn, CRm,
    Citind LDC(cond)p , CRd,
    Înregistrare STC(cond)p , CRd,
    Î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
  • C=1, Z=0
  • eticheta BHI
  • C=0, Z=1
  • Eticheta BLS
  • N=1, V=1 sau N=0, V=0
  • Eticheta BGE
  • N=1, V=0 sau N=0, V=1
  • Eticheta BLT
  • Z=0 și ((N sau V=1) sau (N sau V=0))
  • Eticheta BGT
  • Z=1 sau ((N=1 sau V=0) sau (N=0 și V=1))
  • eticheta BLE
    Salt necondiționat eticheta B
    Link lung Eticheta BL
    Schimbarea opțională a stării -
  • la adresa in ml. Inregistreaza-te
  • BX Rs
  • la adresa din st. Inregistreaza-te
  • BX Hs
    Citind cu constantă de offset -
  • cuvintele
  • Drumul LDR,
  • jumatate de cuvinte
  • LDRH Rd,
  • octeți
  • LDRB Road,
    cu registru offset -
  • cuvintele
  • Drumul LDR,
  • jumatate de cuvinte
  • LDRH Rd,
  • semn jumătate de cuvânt
  • Drumul LDRSH,
    LDRB Road,
  • octet semnat
  • Drumul LDRSB,
    raportat la contorul de programe PC Drumul LDR,
    în raport cu indicatorul de stivă SP Drumul LDR,
    Abordare -
  • prin PC
  • ADD Rd, PC, #10bit_Offset
  • folosind SP
  • ADD Rd, SP, #10bit_Offset
    Lectură multiplă LDMIA Rb!,
    Înregistrare cu constantă de offset -
  • cuvintele
  • STR Road,
  • jumatate de cuvinte
  • drumul strh,
  • octeți
  • drumul strb,
    cu registru offset -
  • cuvintele
  • STR Road,
  • jumatate de cuvinte
  • drumul strh,
  • octeți
  • 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