Ablakok.  Vírusok.  Jegyzetfüzetek.  Internet.  hivatal.  Segédprogramok.  Drivers

GBA ASM – 2. nap: Néhány információ az ARM assemblerről – WASM.RU archívum

Az ARM a GBA processzort gyártó cég. Az ARM processzorok RISC processzorok (ellentétben az INTEL processzorokkal). A RISC a Reduced Instruction Set Computers (CISC - Complex ...) rövidítése. Bár ezeknek a processzoroknak nincs sok utasítása (ami jó), ARM utasítások(és talán más RISC processzoroknak, nem tudom) sokféle felhasználási területük és kombinációjuk van, amelyek a RISC processzorokat olyan erőssé teszik, mint amilyenek.

Regiszterek

Más ARM processzorokat nem tudok, de a GBA-ban használt 16 regiszteres és az Intel processzorokkal (és a többiekkel) ellentétben minden regiszter biztonságosan használható (általában). A nyilvántartások a következők:

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

Azta! Sok! sorjában magyarázom.

ro: Csinálj, amit akarsz!

r2-r12: ugyanaz

r13: Egyes ARM rendszereken az r13 egy veremmutató (SP az INTEL processzorokon). Nem vagyok benne biztos, hogy az r13 ugyanazt a szerepet tölti-e be a GBA-ban, csak figyelmeztetni tudlak, hogy óvatosan bánj vele, ha a veremmel dolgozol.

r14: A meghívott eljárások visszatérési címét tartalmazza. Ha nem használod őket, akkor azt csinálhatsz vele, amit akarsz.

r15: Programszámláló és jelzők, ugyanaz, mint az IP (Instruction Pointer az Intel-en). Ez abban különbözik az Intel IP-regiszterétől, hogy ingyenes hozzáférése van hozzá, akárcsak bármely más regiszterhez, de vegye figyelembe, hogy ennek megváltoztatásával a vezérlés átkerül a kód másik szakaszába, és a jelzők megváltoznak.

Végezzünk egy kis matematikai gyakorlatot... 16 mínusz 3 (általában) 13 regisztert ad. nem menő? Vedd lazán.

Most talán azt kérdezi, mik is valójában a regiszterek. A regiszterek olyan speciális memóriaterületek, amelyek a processzor részét képezik, és nem rendelkeznek érvényes címmel, de csak a nevükről ismertek. A regiszterek 32 bitesek. Bármilyen assembly nyelven szinte minden regisztert használ, így Önnek is ismernie kell azokat, valamint rokonait.

ARM összeszerelő utasítások

Először is azzal szeretném kezdeni, hogy szerintem aki feltalálta az ARM assemblert, az egy zseni.

Másodszor, szeretném bemutatni jó barátomat, CMP-t. Köszönj neki, és talán, csak talán, ő is a barátod lesz. A CMP a Compare (összehasonlítás) rövidítése. Ez az utasítás összehasonlíthatja a regisztert és a számot, a regisztert és a regisztert, vagy a regisztert és a memóriahelyet. Ezután az összehasonlítás után a CMP állapotjelzőket állít be, amelyek megmondják az összehasonlítás eredményét. Mint emlékszik rá, az r15 regiszter zászlókat tartalmaz. Beszámolnak az összehasonlítás eredményéről. A CMP utasítás kifejezetten ezen jelzők értékének beállítására szolgál, semmi másra.

A zászlók a következő állapotokat tartalmazhatják:

    EQ Equal / Equal

    NE nem egyenlő

    VS túlfolyó készlet / túlcsordulás készlet

    VC oVerflow Clear

    HI Magasabb / Magasabb

    LS alacsonyabb vagy ugyanaz

    PL Plus / Plus

    MI MINus / Minus

    CS hordkészlet

    CC Carry Clear

    GE Nagyobb vagy egyenlő

    GT nagyobb, mint / több

    LE Kisebb vagy egyenlő

    LT Kevesebb mint

    Z nulla

    NZ nem nulla / nem nulla

Ezek az állapotok nagyon fontos szerepet játszanak az ARM assemblerben.

MEGJEGYZÉS: A zászlók csak a feltételeket tárolják (Egyenlő, Kevesebb, mint stb.). Már nem számítanak.

Feltétel utótagok

Láttad már a B (Elágazás) utasítást. A B utasítás az úgynevezett feltétel nélküli elágazást hajtja végre (mint a GoTo a Basicben vagy a JMP az INTEL összeállításban). De lehet utótagja (a fent felsoroltak egyike), ilyenkor ellenőrzi, hogy a zászlók állapota egyezik-e vele. Ha nem, akkor az ugrási utasítás egyszerűen nem hajtódik végre. Tehát ha ellenőrizni szeretné, hogy az r0 regiszter egyenlő-e az r4 regiszterrel, majd ugorjon a label34 nevű címkére, akkor meg kell írnia a következő kódot:

    CMP r0, r4; Az összeállítás nyelvi megjegyzései pontosvessző után jönnek (;)

    BEQ címke34 ; A B egy ugrási utasítás, az EQ pedig egy utótag jelentése

    ; "Ha egyenlő"

MEGJEGYZÉS: A Goldroad Assemblerben a címkéket nem kell követnie (, és a karakterláncon a címke nevén kívül semmi más nem szerepelhet.

II. MEGJEGYZÉS: Írjon CMP-t és BEQ-t nagybetűvel nem feltétlenül, csak azért, hogy világosabb legyen számodra.

Most már tudod, hogyan kell ugrani a zászlók állapotától függően, de azt nem, hogy a zászlók állapotától függően bármit megtehetsz, csak add hozzá a szükséges utótagot bármelyik utasításhoz!

Ezenkívül nem kell CMP-t használnia a jelzők állapotának beállításához. Ha például azt szeretné, hogy a SUB (Kivonás) utasítás jelzőket állítson be, adjon hozzá egy "S" utótagot az utasításhoz (a "Set flags" rövidítése). Ez akkor hasznos, ha nem akarja beállítani a jelzők állapotát egy extra CMP utasítással, így ezt megteheti és ugorhat, ha az eredmény nulla a következőképpen:

    SUBS r0,r1,0x0FF ; Beállítja a zászlókat a végrehajtás eredményének megfelelően

    ; utasítás

    ldrZ r0,=0x0FFFF ; Csak akkor töltse be az r0 0x0FFFF regiszterbe, ha az állapot

    zászlók nulla.

Felülvizsgálat

Ma (kicsit többet) tanultunk a regiszterekről. Megismertük az ARM utasítások rugalmasságát is, amelyek a flagek állapotától függően végrehajthatók (vagy nem hajthatók végre). Sokat tanultunk ma.

Holnap az ARM assembler ma megszerzett tudását felhasználva megjelenítjük a képet a GBA képernyőn.

Valami lehetetlen csak addig lehetetlen, amíg lehetségessé nem válik / Jean-Luc Picard, kapitány. , USS Enterprise/. Mike H, ford. Aquila

Ha a Raspbian disztribúciót használja mint operációs rendszer a Raspberry Pi-hez két segédprogramra lesz szüksége, nevezetesen az as (összeszerelő, amely az assembly nyelvű forráskódot bináris kóddá alakítja) és az ld (egy linker, amely létrehozza az eredményt. futtatható fájl). Mindkét segédprogram benne van a csomagban szoftver binutils , így előfordulhat, hogy már jelen vannak a rendszerében. Természetesen szükség lesz egy jó szövegszerkesztőre is; Mindig ajánlom a Vim használatát programfejlesztéshez, de nagy a belépési korlátja, ezért a Nano vagy bármely más szövegszerkesztő GUI szintén remekül illeszkedik.

Készen állsz a kezdésre? Másolja ki a következő kódot, és mentse el a myfirst.s fájlba:

Globális _start _start: mov r7, #4 mov r0, #1 ldr r1, =string mov r2, #stringlen swi 0 mov r7, #1 swi 0 .data string: .ascii "Ciao!\n" stringlen = . -húr

Ez a program egyszerűen kiírja a "Ciao!" a képernyőre, és ha olvasott cikkeket az assembly nyelv használatáról az x86-os CPU-kkal való munkavégzéshez, a használt utasítások egy része ismerős lehet az Ön számára. De mégis sok különbség van az x86 és az ARM architektúra utasításai között, ami a forráskód szintaxisában is elmondható, ezért részletesen elemezzük.

De előtte meg kell említeni, hogy a fenti kód összeállításához és az eredményül kapott objektumfájl futtatható fájlba való összekapcsolásához a következő parancsot kell használni:

Mint -o myfirst.o myfirst.s && ld -o myfirst myfirst.o

Most már futtathatja a létrehozott programot a ./myfirst paranccsal. Valószínűleg észrevette, hogy a végrehajtható fájlnak nagyon szerény méretű körülbelül 900 bájt - ha a C programozási nyelvet és a puts() függvényt használnád, akkor a bináris fájl mérete körülbelül ötször nagyobb lenne!

Saját operációs rendszer létrehozása a Raspberry Pi számára

Ha elolvasta az x86 assembly nyelvű programozási sorozat korábbi cikkeit, valószínűleg emlékszik arra, amikor először futtatta saját operációs rendszerét, amely üzenetet jelenített meg a képernyőn Linux segítség vagy más operációs rendszer. Ezt követően egy egyszerű felület hozzáadásával véglegesítettük parancs sor valamint a programok lemezről történő betöltésére és futtatására szolgáló mechanizmus, tartalékot hagyva a jövőre nézve. Nagyon érdekes, de nem túl nehéz munka volt, főleg a segítségnek köszönhetően BIOS firmware- egyszerűsített felületet biztosított a képernyő, a billentyűzet és a hajlékonylemez-meghajtó eléréséhez.

A Raspberry Pi esetében már nem állnak majd a rendelkezésére hasznos BIOS-funkciók, így magának kell eszközmeghajtókat fejlesztenie, ami önmagában is bonyolult és érdektelen munka a képernyőre rajzoláshoz és a natív program megvalósításához képest. végrehajtási mechanizmus. A neten azonban több útmutató található, amelyek részletesen leírják kezdeti szakaszaiban a Raspberry Pi rendszerindítási folyamat, a GPIO pin-elérési mechanizmus funkciói és így tovább.

Az egyik legjobb ilyen dokumentum a Baking Pi (www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/index.html) a Cambridge-i Egyetemen. Lényegében ez egy olyan kézikönyv, amely leírja, hogyan kell az assembly nyelven dolgozni a LED-ek bekapcsolásához, a képernyő pixeleinek eléréséhez, a billentyűzet bevitelének fogadásához stb. Olvasás közben sok mindent megtudhatsz róla hardver A Raspberry Pi és a kézikönyvek ezeknek az egykártyás számítógépeknek az eredeti modelljéhez készültek, így nincs garancia arra, hogy relevánsak lesznek az olyan modelleknél, mint az A+, B+ és Pi 2.

Ha a C programozási nyelvet részesíti előnyben, akkor olvassa el a Valvers erőforrás dokumentumát, amely a http://tinyurl.com/qa2s9bg címen található, és amely egy keresztfordító beállításának és egy egyszerű operációs rendszer kernel felépítésének folyamatának leírását tartalmazza, és a hasznos OSDev-forrás Wiki részében. Lásd a http://wiki.osdev.org/Raspberry_Pi_Bare_Bones webhelyet, ahol információt találhat arról, hogyan hozhat létre és futtathat egy Raspberry Pi rendszermagot.

Mint fentebb említettük, a legnagyobb probléma a ez az eset illesztőprogramokat kell fejleszteni különféle Raspberry Pi hardvereszközökhöz: USB vezérlő, SD kártyanyílás és így tovább. Hiszen még az említett eszközök kódja is több tízezer sort foglalhat. Ha továbbra is saját, teljesen működőképes operációs rendszert szeretne fejleszteni a Raspberry Pi számára, keresse fel a www.osdev.org fórumot, és kérdezze meg, hogy valaki fejlesztett-e már illesztőprogramokat ezekhez az eszközökhöz, és ha lehetséges, adaptálja azokat a rendszermaghoz. operációs rendszerét, ezáltal jelentős mennyiségű időt takaríthat meg.

Hogyan működik mindez

A kód első két sora nem CPU utasítások, hanem assembler és linker direktívák. Minden programnak rendelkeznie kell egy jól definiált belépési ponttal _start néven, és ez esetünkben a kód legelejére került. Így tájékoztatjuk a linkert, hogy a kód végrehajtását a legelső utasítástól kezdve el kell kezdeni, és nincs szükség további műveletekre.

Használva következő utasítás beírjuk a 4-es számot az r7 regiszterbe. (Ha még soha nem dolgozott assembly nyelvvel, akkor tudnia kell, hogy a regiszter egy memóriahely, amely közvetlenül a központi feldolgozó egységben található. A legtöbb modern CPU-k kevés regiszter van megvalósítva a több millió vagy milliárd cellához képest véletlen hozzáférésű memória, ugyanakkor a regiszterek nélkülözhetetlenek, hiszen sokkal gyorsabban működnek.) Chips ARM architektúrák nagyszámú nyilvántartást biztosítanak a fejlesztőknek Általános rendeltetésű: Egy fejlesztő legfeljebb 16 r0 - r15 nevű regisztert használhat, és ezeket a regisztereket nem köti semmilyen történelmi korlátozás, mint az x86 architektúra esetében, ahol a regiszterek egy része bizonyos célokra használható bizonyos időpontokban.

Tehát, bár a mov utasítás nagyon hasonlít az azonos nevű x86 architektúra utasításra, minden esetben figyelni kell a 4-es melletti hash szimbólumra, amely azt jelzi, hogy egy egész érték található mellette, és nem memóriacím. . Ebben az esetben a Linux kernel írási rendszerhívását szeretnénk használni a karakterlánc kinyomtatására; A rendszerhívások használatához ki kell töltenie a regisztereket a szükséges értékekkel, mielőtt megbocsátja a kernelnek, hogy elvégezze a munkáját. A rendszerhívás számának illeszkednie kell az r7 regiszterbe, ahol a 4 az írási rendszerhívás száma.

A következő mov utasítással az r0 regiszterbe helyezzük azt a fájlleírót, amelyben a "Ciao!" karakterláncot, vagyis a szabványos kimeneti folyam leíróját kell beírni. Mivel ebben az esetben a szabványos kimeneti adatfolyamot használjuk, ennek szabványos leírója, azaz 1 kerül a regiszterbe. Ezután az r1 regiszterbe be kell helyeznünk annak a karakterláncnak a címét, amelyet ki akarunk adni az r1 regiszterbe az ldr utasítás segítségével (a "load into register" utasítás; vegye figyelembe az egyenlőségjelet, amely azt jelzi, hogy ami ezután következik, az egy címke, nem pedig cím). A kód végén, nevezetesen az adat részben deklaráljuk ezt a sztringet ASCII karaktersorozat formájában. A "write" rendszerhívás sikeres használatához azt is meg kell mondanunk a működési törzs kernelnek, hogy mekkora a kimeneti karakterlánc hossza, ezért a stringlen értéket az r2 regiszterbe helyezzük. (A stringlen értéket úgy számítjuk ki, hogy a kezdőcímből kivonjuk a karakterlánc végcímét.)

Tovább Ebben a pillanatban minden nyilvántartást feltöltöttünk a szükséges adatokkal és készen állunk az irányítás átadására Linux kernel. Ehhez a swi utasítást használjuk, amelynek neve a "szoftvermegszakítást" jelenti, amely átkerül az operációs rendszer kernelterébe (nagyjából ugyanúgy, mint az x86 architektúráról szóló cikkekben található int utasítás). Az operációs rendszer kernel megvizsgálja az r7 regiszter tartalmát, megtalálja benne a 4-es egész értéket, és arra a következtetésre jut: "Tehát a hívó program egy karakterláncot akar nyomtatni." Ezt követően megvizsgálja a többi regiszter tartalmát, kiír egy karakterláncot, és visszaadja a vezérlést a programunknak.

Így a képernyőn a „Ciao!” sort látjuk, ami után már csak a program végrehajtását kell helyesen befejeznünk. Ezt a problémát úgy oldjuk meg, hogy a kilépési rendszer hívószámát az r7 regiszterbe helyezzük, majd hívjuk a nulla számú szoftvermegszakítási utasítást. És ez minden – az operációs rendszer kernelje befejezi programunk végrehajtását, és ismét áttérünk a parancshéjra.

Vim (balra) nagyszerű szöveg szerkesztő kód íráshoz assembly nyelven - fájl szintaxis kiemeléshez adott nyelv Az ARM architektúrához a http://tinyurl.com/psdvjen címen érhető el.

Tanács: ha assembly nyelvvel dolgozik, ne fukarkodjon a megjegyzésekkel. Nem használtuk egy nagy szám megjegyzéseket ebben a cikkben, hogy a kód a lehető legkevesebb helyet foglaljon el a magazin oldalain (és azért is, mert részletesen leírtuk az egyes utasítások célját). De olyan összetett programok fejlesztésekor, amelyek kódja első pillantásra nyilvánvalónak tűnik, mindig gondoljon arra, hogyan fog kinézni, ha részben elfelejti az ARM architektúra assembly nyelvi szintaxisát, és néhány hónap múlva visszatér a fejlesztéshez. Elfelejtheti a kódban használt összes trükköt és parancsikont, ami után a kód teljes halandzsának fog kinézni. A fentiek alapján érdemes minél több megjegyzést fűzni a kódhoz, még akkor is, ha ezek egy része jelenleg túl nyilvánvalónak tűnik!

visszafejtés

A bináris fájlok assembly nyelvű kódjává alakítása is hasznos lehet bizonyos esetekben. Ennek a műveletnek az eredménye általában nem túl jól formált kód, olvasható címkenevek és megjegyzések nélkül, ami ennek ellenére hasznos lehet az assembler által a kódon végrehajtott átalakítások tanulmányozásában. A myfirst bináris szétszereléséhez egyszerűen futtassa a következő parancsot:

Objdump -d myfirst

Ez a parancs lehetővé teszi a bináris fájl végrehajtható kódrészének szétszedését (de nem az adatszakaszt, mivel az ASCII szöveget tartalmaz). Ha egy pillantást vetünk a szétszedett kódra, észrevehetjük, hogy a benne lévő utasítások szinte megegyeznek az eredeti kód utasításaival. A szétszedőket főként akkor használják, ha egy olyan program viselkedését kell tanulmányozni, amely csak bináris formában érhető el, mint például egy vírus vagy egy egyszerű zárt forráskódú program. forráskód, akinek a viselkedését utánozni szeretné. Ugyanakkor mindig emlékezzen a vizsgált program szerzője által előírt korlátozásokra! A program bináris fájljának szétszedése és a kapott kód egyszerű másolása a projekt kódjába természetesen rossz ötlet; ugyanakkor a kapott kódot eléggé felhasználhatja a program elvének tanulmányozására.

Alprogramok, ciklusok és feltételes utasítások

Most, hogy tudjuk, hogyan kell egyszerű programokat megtervezni, összeállítani és összekapcsolni, térjünk át valami bonyolultabbra. A következő program szubrutinokat használ a karakterláncok kiadására (ezeknek köszönhetően újra felhasználhatjuk a kódrészleteket, és megkímélhetjük magunkat attól, hogy a regiszterek adatokkal való feltöltéséhez hasonló műveleteket kelljen végrehajtanunk). Ez a program egy fő eseményhurkot valósít meg, amely lehetővé teszi egy karakterlánc kimenetét, amíg a felhasználó be nem írja a „q”-t. Tanulmányozd a kódot és próbáld megérteni (vagy kitalálni!) az instrukciók célját, de ne csüggedj, ha valamit nem értesz, mert kicsit később mi is nagyon részletesen megnézzük. Vegye figyelembe, hogy az ARM architektúra assembly nyelvén a @ szimbólumok kiemelik a megjegyzéseket.

global _start _start: ldr r1, =string1 mov r2, #string1len bl print_string ciklus: mov r7, #3 @ read mov r0, #0 @ stdin ldr r1, =char mov r2, #2 @ két karakter swi 0 ldr r1, =char ldrb r2, cmp r2, #113 @ "q" karakter ASCII-kódja beq kész ldr r1, =string2 mov r2, #string2len bl print_string b ciklus kész: mov r7, #1 swi 0 print_string: mov r7, #4 mov r0, #1 swi 0 bx lr .data string1: .ascii "A kilépéshez írja be a q-t!\n" string1len = . - string1 string2: .ascii "Ez nem volt q...\n" string2len = . - string2 char: .word 0

A programunk azzal kezdődik, hogy a megfelelő regiszterekben elhelyezünk egy mutatót egy karakterlánc elejére, és egy értéket a hosszának a megfelelő regiszterekbe helyezünk, hogy a rendszerhívást követhesse, majd közvetlenül ezután a kód alatti print_string szubrutinra ugrik. Ennek az átmenetnek a megvalósításához a bl utasítást használjuk, melynek neve a "branch and link" ("elágazás a cím megőrzésével") rövidítése, és maga tárolja az aktuális címet a kódban, amely lehetővé teszi a visszatérést. később a bx utasítás segítségével. A print_string szubrutin egyszerűen kitölti a többi regisztert az írási rendszerhíváshoz ugyanúgy, mint az első programunkban, mielőtt a kerneltérbe ugrik, majd a bx utasítással visszatér a mentett kódcímre.

Visszatérve a hívókódra, egy loop nevű címkét találunk - a címke neve már arra utal, hogy egy idő után visszatérünk rá. Először azonban egy másik, read nevű rendszerhívást használunk (3-as számmal), hogy beolvassuk a felhasználó által a billentyűzet segítségével beírt karaktert. Ezért az r7 regiszterbe a 3-as értéket, az r0 regiszterbe a 0 értéket (standard input handle) írjuk, mivel a felhasználói bemenetet kell olvasnunk, nem egy fájlból származó adatokat.

Ezután az r1 regiszterbe helyezzük azt a címet, ahová az operációs rendszer kernel által beolvasott és elhelyezett karaktert tárolni szeretnénk - esetünkben ez az adatrész végén leírt char memóriaterület. (Igazából gépi szóra, vagyis két karakter tárolására memóriaterületre van szükségünk, mert az Enter billentyűkódot is tárolja. Az assembly nyelvvel való munka során fontos, hogy mindig emlékezzünk a memóriaterületek túlcsordulásának lehetőségére, mert nincsenek magas szintű mechanizmusok, amelyek készen állnak a segítségére!).

Visszatérve a fő kódhoz, látni fogjuk, hogy az r2 regiszterben a 2-es érték kerül, amely megfelel a menteni kívánt két karakternek, majd az OS kernelterületére való áttérés történik az olvasási művelet végrehajtásához. A felhasználó beír egy karaktert, és megnyomja az Enter billentyűt. Most ellenőriznünk kell, hogy milyen karakterről van szó: a memóriaterület címét (az adatrészben char) beírjuk az r1 regiszterbe, majd az ldrb utasítással betöltünk egy bájtot a memóriaterületről, amelyre a memóriaterület mutat. érték ebből a regiszterből.

A szögletes zárójelek ebben az esetben azt jelzik, hogy az adatokat a számunkra érdekes memóriaterületen tároljuk, és nem magában a regiszterben. Így az r2 regiszter most egyetlen karaktert tartalmaz az adatszakasz char memóriaterületéből, és pontosan ezt a karaktert írta be a felhasználó. Következő feladatunk az lesz, hogy az r2 regiszter tartalmát összehasonlítsuk a "q" karakterrel, amely az ASCII diagram 113. karaktere (lásd a www.asciichart.com címen található karaktertáblázatot). Most a cmp utasítást használjuk az összehasonlítási művelet végrehajtására, majd a beq utasítást, amelynek neve a „branch if equal” (elágazás, ha egyenlő), a done címkére ugorunk, ha az r2 regiszter értéke 113. Ha nem, kiadjuk a második sort, majd a b utasítással a ciklus elejére ugorunk.

Végül a done címke után közöljük az operációs rendszer kernelével, hogy az első programhoz hasonlóan be akarjuk fejezni a program végrehajtását. A program futtatásához egyszerűen össze kell szerelnie és össze kell kapcsolnia az első programhoz kapott utasítások szerint.

Tehát meglehetősen nagy mennyiségű információt vettünk figyelembe a legtömörebb formában, de jobb lesz, ha önállóan tanulmányozza az anyagot, kísérletezve a fenti kóddal. Nem Jobb út programozási nyelv ismerete, mint valaki más kódjának módosításával való kísérletezés és az elért hatás megfigyelése. Mostantól egyszerű ARM assembler programokat fejleszthet, amelyek olvasnak felhasználói bevitelés adatkimenet, hurkok, összehasonlítási műveletek és szubrutinok használata közben. Ha a mai napig nem találkozott az assembly nyelvvel, remélem, ez a cikk egy kicsit érthetőbbé tette a nyelvezetet az Ön számára, és segített eloszlatni azt a népszerű sztereotípiát, hogy ez egy misztikus mesterség, amely csak néhány tehetséges fejlesztő számára elérhető.

Természetesen a cikkben közölt információk az ARM architektúra assembly nyelvének használatáról csak a jéghegy csúcsa. Ennek a programozási nyelvnek a használata mindig rengeteg árnyalattal jár, és ha azt szeretné, hogy a következő cikkek egyikében írjunk róluk, csak tudassa velünk! Addig is azt javasoljuk, hogy látogassanak meg egy kiváló forrást, amely sok anyagot tartalmaz a programkészítés megtanulásához Linux rendszerek ARM architektúrájú CPU-kkal rendelkező számítógépeken fut, a következő címen: http://tinyurl.com/nsgzq89. Boldog programozást!

Korábbi cikkek az "Assembler School" sorozatból:

1. A valós idejű óraszámlálót engedélyezni kell (1); Az órajelforrás kiválasztási bit törlődik (2), ha az óra nem a fő órától származik.

2. Egy vagy mindkét megszakítási esemény kiválasztó bitet (3) be kell állítani. És kiválasztásra kerül, hogy mely események indítsák el a megszakítási kérelmet (5).

3. A megszakítási eseménymaszkokat (4, 7) be kell állítani.

2.5 Az ARM7 programozása assemblerben

Az ARM7 utasításkészlet (1.4. szakasz) mindössze 45 utasítást tartalmaz, amelyek a címzési módszerek, feltételes mezők és módosítók sokfélesége miatt meglehetősen összetettek. Az assembly nyelvű program nehézkes és

Val vel nehezen olvasható. Ezért az assemblert ritkán használják az ARM7 architektúra programozásában.

A magas szintű C nyelv azonban sok építészeti jellemzőt rejt a programozó elől. A programozó gyakorlatilag nem érinti az olyan eljárásokat, mint a kernel mód kiválasztása, a memória lefoglalása a verem számára, és a megszakítások kezelése. Ezen eljárások megtanulásához hasznos legalább egy egyszerű assembly nyelvű programot írni.

Ezenkívül még C használata esetén is az assembly nyelvhez kell folyamodni.

1. Irányítani kell A C fordító, amely nyomon követi, hogy az optimalizálás során kizárt-e fontos utasításokat, szükségtelennek tartotta-e azokat. A fordító rendkívül nem hatékony kódot generál egy viszonylag egyszerű művelethez, az elégtelen optimalizálás miatt. Megbizonyosodni arról, hogy a fordító valóban azokat a hardver erőforrásokat használja, amelyek célja egy adott algoritmus hatékonyságának növelése.

2. Hibák vagy kivételek okainak keresése közben (2.4.1. szakasz).

3. Teljesítmény vagy memóriafelhasználás szempontjából abszolút optimális kód beszerzése (2.2.20., 3.1.5. szakasz).

Tekintsük a program összeállításának alapvető technikáit assemblerben

Val vel a mikrokontroller által végrehajtott összes kód bemutatása, ahogy van, és közvetítő nélkül C fordító.

Az assembler alapú projekt létrehozásának folyamata majdnem ugyanaz, mint a C programok esetében (2.3.1–2.3.3 fejezet). Csak két kivétel van:

a) a *.S kiterjesztés hozzá van rendelve a forrásszövegfájlhoz;

b) itt feltételezzük, hogy a STARTUP.S fájl nem kapcsolódik a programhoz.

2.5.1 Alapvető szabályok az assemblerben történő programíráshoz

A program szövegét az assemblerben négy oszlopba szokás rendezni. Azt mondhatjuk, hogy minden sor négy mezőből áll, nevezetesen: címkék, műveletek, operandusok, megjegyzések mezői. A mezőket tabulátorok vagy szóközök választják el egymástól.

A fő mezők a műveletek és az operandusok. Az érvényes műveleteket és szintaxisukat a táblázat tartalmazza (1.4.2).

A címke egy parancs címének szimbolikus ábrázolása. A címke helyett mindenhol a címke előtti parancs címe lesz helyettesítve. A címkéket leggyakrabban a vezérlőátviteli parancsokban használják. Minden címkének egyedinek kell lennie, és nem kötelező. Sok más verziótól eltérően a RealView összeállításban a címkék nem végződnek kettősponttal (":").

A megjegyzések opcionálisan a sor végére helyezhetők, és pontosvesszővel ("; ") elválasztva.

Vegyünk egy egyszerű példát.

2.5.2 Álparancsok

A RealView assembler támogatja az úgynevezett pszeudo-parancsokat. Az álparancs az mnemonikus jelölés, amely valójában nem felel meg a processzor utasításkészletének, hanem egy vagy (ritkán) több utasítás helyettesíti. A pszeudo-parancsok egyfajta makrók, és a szintaxis egyszerűsítését szolgálják. A támogatott pszeudo-parancsok listája a (2.5.1) táblázatban található.

2.5.3 Összeszerelési irányelvek

Az utasításokkal ellentétben az direktívák nem hoznak létre végrehajtható kódot, amely betöltődik a mikrokontroller memóriájába. A direktívák csak utasítások az assembler számára, irányítják a végrehajtható kód képzését.

Vessünk egy pillantást a gyakran használt RealView 4 assembler direktívákra.

EQU név állandó

Egy konstanshoz a Név szimbólumnevet rendeli, amely az állandó szinonimája lesz. A fő cél az ellenőrzési regiszterek nevének bevezetése,

TERÜLET neve, opciók

Meghatároz egy memóriaterületet a megadott névvel. A paraméterek határozzák meg a memóriaterület célját, például DATA (adat) vagy CODE (kód). A meghatározott terület címei a kiválasztott célállomástól függenek. A CODE terület a 0x00000000 címtől kezdve, a DATA terület a 0x40000000 címtől kezdődik. A programnak rendelkeznie kell egy RESET nevű CODE régióval. A programmemóriában lefoglalt állandókat egy CODE, READONLY paraméterpárral kell deklarálni.

Kijelöli a program belépési pontját, megmutatja a "kezdetét". Egy ilyen direktívának mindig jelen kell lennie a programban. Általában közvetlenül az AREA RESET, CODE direktíva után kerül elhelyezésre.

2.5.1. táblázat – A RealView 4 assembler által támogatott pszeudo-parancsok

Mnemonikus jelölés

Művelet

Tényleges megvalósítás

és szintaxis

ADR (feltétel)

regisztrálni

Konstans összeadása vagy kivonása egy PC-ből

ADD vagy SUB parancsok

ADRL(feltétel)

regisztrálni

Dupla ADD vagy SUB PC-vel

(bővített címtartomány)

ASR(feltétel)(S)

Aritmetikai jobbra eltolás

ASR(feltétel)(S)

shift operandus

LDR (feltétel)

regisztrálni

címzés (PC + közvetlen eltolás)

Állandó elhelyezése

programmemóriában

LDR(index címből-

ciója. Az eltolás PC.

LSL (feltétel) (S)

Logikai eltolás balra

LSL (feltétel) (S)

shift operandus

LSR(feltétel)(S)

Logikai jobbra váltás

LSR(feltétel)(S)

shift operandus

POP (konv.)

Regiszterek visszaállítása a veremből

Felépülés

regisztereket

csapat

LDMIA R13!,(...)

PUSH (feltétel)

Megőrzés

regisztereket

csapat

STMDB R13!,(...)

ROR(feltétel)(S)

Forgasd jobbra

ROR(feltétel)(S)

shift operandus

RRX(Konv.)(S)

Forgassa végig

átvitel 1 számjegyűre

shift operandus

Név SPACE Méret

Memóriát tart fenn az adott méretű adatok tárolására. A név a lefoglalt hely címének szinonimájává válik. A címtér egysége lehetővé teszi, hogy ezt az irányelvet állandó és véletlenszerű memóriára is alkalmazzák. A fő cél globális változók létrehozása a RAM-ban (az DATA területen).

Címke DCB/DCW/DCD állandó

A programmemóriában lévő adatokat (numerikus állandókat) "fűzze össze". A címke szinonimája lesz annak a címnek, ahová az adatokat írják. A különböző méretű adatokhoz különböző direktívák (DCB , DCW és DCD ) használatosak: bájt, 16 bites szó, 32 bites szó (illetve).

Fájlvégi jelként szolgál. Az assembler figyelmen kívül hagyja az utasítás utáni összes szöveget.

2.5.4 Makrók

A makró egy program előre meghatározott része, amely valamilyen általános műveletet hajt végre. Ellentétben a vezérlőátviteli parancsokkal meghívott szubrutinokkal, a makrók használata nem csökkenti a teljesítményt, de nem csökkenti a programmemória fogyasztását. Mert minden makróhívásnál az assembler a teljes szövegét beágyazza a programba.

A következő konstrukció egy makró deklarálására szolgál

$Paraméter1, $Paraméter2, ...

A paraméterek lehetővé teszik a makró szövegének módosítását minden alkalommal, amikor meghívják. A makró belsejében (törzsben) a paraméterek a megelőző "$" jellel is használatosak. A makrótörzsben szereplő paraméterek helyett a hívás során megadott paraméterek kerülnek behelyettesítésre.

A makrót így hívják:

Név Paraméter1, Paraméter2, ...

Lehetőség van állapotellenőrzés, elágazás megszervezésére.

IF "$Parameter" == "Érték"

Vegye figyelembe, hogy ez a konstrukció nem vezet program ellenőrzése mikrokontroller feltételei. A feltételt az assembler ellenőrzi a futtatható kód kialakítása során.

Tehát alkottunk új projekt, elvégeztük az alapbeállításokat, létrehoztunk és a projekthez csatlakoztattunk egy fájlt, amibe szeretnénk valami egyszerű programot írni assemblerben.

Mi a következő lépés? Valójában a Cortex-M3 mag által támogatott thumb-2 utasításkészlettel is írhat programot. A támogatott parancsok listája és leírása a dokumentumban található Cortex-M3 általános felhasználói kézikönyv(fejezet A Cortex-M3 utasításkészlet), amely a Keil uVision 5 projektmenedzser Könyvek lapján található. A hüvelykujj-2 parancsokkal kapcsolatos részleteket a cikk következő részeinek egyikében írjuk le, de most beszéljünk az STM32 programjairól. Tábornok.

Mint minden más assembler program, az STM32 program is olyan parancsokból és pszeudo-parancsokból áll, amelyek közvetlenül gépi kódokká lesznek lefordítva, valamint különféle direktívákból, amelyeket nem fordítanak le gépi kódokká, hanem szolgáltatási célokra használnak (programjelölés, karakterkonstansnevek hozzárendelése stb.)

Például egy speciális direktíva lehetővé teszi egy program külön szakaszokra bontását - TERÜLET. A következő szintaxissal rendelkezik: AREA Section_Name (, type) (, attr) ..., Ahol:

  1. Szakasz_neve- szakasz neve.
  2. típus- szakasz típusa. Az adatokat tartalmazó szakasznál meg kell adni az DATA típusát, a parancsokat tartalmazó szakasznál pedig a CODE típust.
  3. attr- további attribútumok. Például az readonly vagy readwrite attribútumok azt jelzik, hogy melyik memóriába kell a szakaszt elhelyezni, az align=0..31 attribútum azt határozza meg, hogy a szakaszt hogyan kell igazítani a memóriában, a noinit attribútum a nem szükséges memóriaterületek lefoglalására szolgál. inicializálni vagy nullára inicializálni (ennek az attribútumnak a használatakor nem kell megadni a szakasz típusát, mivel csak adatszelvényekhez használható).

Irányelv EQU valószínűleg mindenki számára ismerős, mivel minden assemblerben megtalálható, és szimbolikus nevek hozzárendelésére szolgál különféle állandókhoz, memóriacellákhoz stb. A következő szintaxissal rendelkezik: Név EQU számés közli a fordítóval, hogy az összes szimbólum találkozott Név számmal kell helyettesíteni szám. Mondjuk, ha úgy szám memóriacella címét használjuk, akkor ezt a cellát a jövőben nem a cím alapján lehet elérni, hanem a megfelelő szimbolikus jelöléssel ( Név).

Irányelv KAP fájl név nevű fájlból szöveget szúr be a programba fájl név. Ez analóg az AVR assembler include direktívájával. Használható pl külön fájl direktívák a különböző regiszterekhez való szimbolikus nevek hozzárendeléséhez. Azaz minden névadási hozzárendelést külön fájlba helyezünk, majd ahhoz, hogy ezeket a szimbolikus neveket használni tudjuk a programban, egyszerűen a GET direktívával beépítjük ezt a fájlt a programunkba.

Természetesen a fent felsoroltakon kívül van még egy csomó különféle irányelv, amelyek teljes listája a fejezetben található Irányelvek hivatkozása dokumentum Az Assembler használati útmutatója, amely a Keil uVision 5-ben a következő elérési úton található: tab Könyvek projektvezető -> Eszközök használati útmutatója -> A Felhasználói kézikönyv teljes kiválasztása -> Az Assembler használati útmutatója.

A legtöbb parancs, pszeudo-parancs és direktíva egy programban a következő szintaxissal rendelkezik:

(címke) SZIMBÓLUM (kifejezés) (,kifejezés) (,kifejezés) (; megjegyzés)

(címke) - címke. Erre azért van szükség, hogy meg tudjuk határozni az ezt a címkét követő parancs címét. A címke nem kötelező elem, és csak akkor használatos, ha tudnia kell egy parancs címét (például ugráshoz erre a parancsra). A címke előtt nem lehet szóköz (azaz a sor legelső pozíciójában kell kezdődnie), és a címke neve csak betűvel kezdődhet.

A SYMBOL egy parancs, pszeudo-parancs vagy direktíva. A parancsnak, a címkével ellentétben, a sor elejétől kell lennie némi behúzással, még akkor is, ha előtte nincs címke.

(kifejezés) (,kifejezés) (,kifejezés) — operandusok (regiszterek, konstansok…)

; - határoló. Az elválasztó utáni sorban lévő összes szöveg megjegyzésként kezelendő.

Nos, ahogy ígértem, a legegyszerűbb program:

TERÜLET START , KÓD , OLVASÁS dcd 0x20000400 dcd Program_start BELÉPÉS Program_start b Program_start END

AREA START, CODE, READONLY dcd 0x20000400 dcd Program_start BELÉPÉS Program_start b Program_start END

Ebben a programban csak egy szakaszunk van, ez a START. Ez a szakasz a flash memóriában van lefoglalva (mivel a csak olvasható attribútumot használják hozzá).

Ennek a szakasznak az első 4 bájtja a verem tetejének címét tartalmazza (esetünkben 0x20000400), a második 4 bájt pedig a belépési pont (a futtatható kód eleje) címét tartalmazza. Ezután maga a kód következik. A legegyszerűbb példánkban a végrehajtható kód egyetlen parancsból áll, amely feltétel nélkül a Program_start címkére ugrik, vagyis ugyanazt a parancsot hajtja végre.

Mivel a flashben csak egy szakasz van, ezért a programunk scatter fájljában First_Section_Name néven pontosan meg kell adni a nevét (azaz START).

Ebben az esetben kevert adatok és parancsok vannak. A verem tetejének címét és a belépési pont (adatok) címét dcd direktívák segítségével írjuk közvetlenül a kódszekcióba. Persze lehet így is írni, de nem túl szépen. Különösen, ha leírjuk a megszakítások és kivételek teljes táblázatát (ami elég hosszú lesz), és nem csak a visszaállítási vektort. Sokkal szebb, ha nem zsúfoljuk össze a kódot felesleges adatokkal, hanem a megszakítási vektortáblázatot külön szakaszba, vagy még jobb esetben külön fájlba helyezzük. Hasonlóképpen, a verem inicializálása elhelyezhető egy külön szakaszban vagy akár egy fájlban is. Például mindent külön szakaszokba fogunk helyezni:

TERÜLET FOGALOM, NOINIT, OLVASÁSI TERÜLET 0x400 ; 400 bájt kihagyása Stack_top ; és címke AREA RESET, DATA, READONLY dcd Stack_top ; címke címe Stack_top dcd Program_start ; címke címe Program_start TERÜLET PROGRAM, KÓD, CSAK OLVASÁSI BEJEGYZÉS ; belépési pont (a futtatható kód eleje) Program_start ; program start marker b Program_start END

Nos, itt van, ugyanaz a program (ami még mindig nem csinál semmi hasznosat), de most sokkal világosabbnak tűnik. A program szórófájljában meg kell adnia a RESET nevet First_Section_Name néven, hogy ez a szakasz legyen először a flash memóriában.

A CISC processzorok meglehetősen összetett műveleteket hajtanak végre egy utasításon belül, beleértve a memóriacellák tartalmával kapcsolatos aritmetikai és logikai műveleteket is. A CPU CISC utasításai különböző hosszúságúak lehetnek.

Ezzel szemben a RISC viszonylag egyszerű rendszer parancsok egyértelmű felosztással a művelet típusa szerint:

  • a memóriával való munka (olvasás a memóriából a regiszterekbe vagy írás a regiszterekből a memóriába),
  • adatfeldolgozás a regiszterekben (aritmetikai, logikai, adateltolás balra/jobbra vagy bitforgatás a regiszterben),
  • utasítások feltételes vagy feltétel nélküli ugráshoz más címekre.

Általános szabály, hogy (de nem mindig, és csak akkor, ha a programkód a vezérlő gyorsítótárába kerül) egy processzorciklusban egy utasítás végrehajtásra kerül. Az ARM processzor utasításhossza rögzített - 4 bájt (egy számítógépes szó). Valójában egy modern ARM processzor át tud váltani más üzemmódokra, például THUMB módra, amikor az utasítások hossza 2 bájt lesz. Ez lehetővé teszi a kód kompaktabbá tételét. Ebben a cikkben azonban nem térünk ki erre a módra, mivel az Amber ARM v2a processzor nem támogatja. Ugyanebből az okból kifolyólag nem vesszük figyelembe az olyan módokat, mint a Jazelle (Java kód végrehajtására optimalizálva), és nem vesszük figyelembe a NEON parancsokat – a több adaton végzett műveletek parancsait. Ennek ellenére egy tiszta ARM utasításkészletet tanulmányozunk.

ARM processzor regiszterek.

Az ARM processzor több regiszterkészlettel rendelkezik, amelyekből jelenleg csak 16 áll a programozó rendelkezésére. Ezek a működési módok a következők:

  • alkalmazás mód (USR, felhasználói mód),
  • felügyelő mód vagy operációs rendszer mód (SVC, felügyelő mód),
  • megszakításkezelési mód (IRQ, megszakítási mód) és
  • feldolgozási mód "sürgős megszakítás" (FIRQ, gyors megszakítási mód).

Azaz például megszakítás esetén a processzor maga megy a megszakításkezelő program címére, és automatikusan „átkapcsol” a regiszterbankok között.

A régebbi ARM processzorok a fenti működési módokon kívül további módokkal is rendelkeznek:

  • Megszakítás (memória-hozzáférési kivételek kezelésére szolgál),
  • Undefined (a társprocesszor programozott megvalósítására szolgál) és
  • Az operációs rendszer privilegizált feladatmódja Rendszer.

Az Amber ARM v2a processzor nem rendelkezik ezzel a további három móddal.

Amber ARM v2a esetén a regiszterkészlet a következőképpen ábrázolható:

Az r0-r7 regiszterek minden módban azonosak.
Az r8-r12 regiszterek csak az USR, SVC, IRQ módokban általánosak.
Az r13 regiszter a veremmutató. Minden módban van.
r14 regiszter - az alprogramból származó visszatérési regiszter is minden módban saját.
Az r15 regiszter a végrehajtható parancsokra mutató mutató. Minden módban közös.

Látható, hogy a FIRQ mód a legszigeteltebb, ennek van a legtöbb saját regisztere. Ez azért történik, hogy néhány nagyon kritikus megszakítást lehessen feldolgozni anélkül, hogy a regisztereket a verembe mentenék, anélkül, hogy időt pazarolnánk erre.

Különös figyelmet kell fordítani az r15 regiszterre, más néven pc (Program Counter) - a végrehajtható parancsokra mutató mutató. Tartalmán különféle aritmetikai és logikai műveleteket lehet végrehajtani, ezáltal a programvégrehajtás más címekre vált át. Azonban az Amber rendszerben megvalósított ARM v2a processzor esetében van néhány finomság a regiszter bitjeinek értelmezésében.

A tény az, hogy ebben a processzorban az r15 (pc) regiszterben a végrehajtható parancsokra mutató tényleges mutató mellett a következő információkat is tartalmazza:

31:28 bitek - egy aritmetikai vagy logikai művelet eredményének zászlói
27. bitek - Megszakítási IRQ maszk, a megszakítások le vannak tiltva, ha a bit be van állítva.
26. bitek - FIRQ megszakítási maszk, a gyors megszakítások letiltva, ha a bit be van állítva.
25:2 bitek - maga a programutasításokra mutató mutató csak 26 bitet foglal el.
1:0 bitek - a processzor aktuális üzemmódja.
3-Felügyelő
2- Megszakítás
1-Gyors megszakítás
0 - felhasználó

A régebbi ARM processzorokban minden jelző és szolgáltatásbit külön regiszterekben található. A program aktuális állapotának nyilvántartása( cpsr ) és Saved Program Status Register ( spsr ), amelyekhez külön speciális parancsok érhetők el. Ez a programok elérhető címterének bővítése érdekében történik.

Az ARM assembler elsajátításának egyik nehézsége egyes regiszterek alternatív nevei. Tehát, mint fentebb említettük, az r15 ugyanaz a számítógép. Van még r13 - ez ugyanaz az sp (Stack Pointer), r14 az lr (Link Register) - az eljárásból származó visszatérési cím regisztere. Ettől eltekintve az r12 ugyanaz az ip (Intra-Procedure -call scratch register), amelyet a C fordítók speciális módon használnak a verem paramétereinek eléréséhez. Az ilyen alternatív elnevezések néha zavaróak, ha valaki más programkódjába nézünk – ezek és ezek a regisztermegjelölések is vannak.

A kódvégrehajtás jellemzői.

Számos processzortípusban (például x86) csak egy másik programcímre való ugrás hajtható végre feltétel alapján. Az ARM esetében ez nem így van. Minden ARM processzor utasítás végrehajtható vagy nem hajtható végre feltétel szerint. Ez lehetővé teszi a programugrások számának minimalizálását és ezáltal a processzor folyamatának (pipeline) hatékonyabb felhasználását.

Végül is mi az a csővezeték? A programkódból most egy processzorutasítást kérünk le, az előzőt már dekódoljuk, az előzőt pedig már végrehajtjuk. Ez a 3-fokozatú Amber A23 processzor csővezeték esetében van így, amelyet projektünkben a Mars Rover2Rover2 kártyához használunk. Az Amber A25 processzor módosítása 5 fokozatú pipeline, még hatékonyabb. De van egy nagy DE. Az ugrásutasítások arra kényszerítik a processzort, hogy törölje a csővezetéket és töltse fel. Így egy új parancs kerül kiválasztásra, de még nincs mit dekódolni, és még kevésbé semmi, amit azonnal végre kell hajtani. A kódvégrehajtás hatékonysága csökken a gyakori átmenetekkel. BAN BEN modern processzorok mindenféle elágazás előrejelző mechanizmus létezik, ami valahogy optimalizálja a csővezeték kitöltését, de ez nincs meg a processzorunkban. Mindenesetre az ARM bölcsen tette lehetővé, hogy minden utasítás feltételesen végrehajtható legyen.

Egy ARM processzorban bármilyen típusú utasításban az utasítás-végrehajtási feltétel négy bitje az utasításkód felső négy bitjébe van kódolva:

4 állapotjelző van a processzorban:
. Negatív - a művelet eredménye negatív,
. Nulla - az eredmény nulla,
. Carry - előjel nélküli számokkal végzett művelet végrehajtása során átvitel történt,
. oTúlcsordulás - előjeles számokkal végzett műveletnél túlcsordulás történt, az eredmény nem kerül be a regiszterbe)

Ez a 4 zászló számos lehetséges feltétel-kombinációt alkot:

Kód Utótag Jelentése Zászlók
4"h0 ekv Egyenlő Z készlet
4"h1 ne nem egyenlő Z tiszta
4"h2 cs/hs Carry set / unsigned magasabb vagy azonos Cset
4"h3 cc/lo Hordja tiszta / előjel nélküli alsó C tiszta
4"h4 mi mínusz/negatív N készlet
4"h5 pl Plusz/pozitív vagy nulla N tiszta
4"h6 ellen túlcsordulás V készlet
4"h7 vc nincs túlcsordulás V tiszta
4"h8 Szia Előjel nélküli magasabb C set és Z tiszta
4"h9 ls Előjel nélküli alsó vagy ugyanaz C tiszta vagy Z beállítása
4" ha ge Nagyobb vagy egyenlő előjellel N == V
4"hb lt Aláírva kevesebb, mint N = V
4"hc gt Aláírva nagyobb, mint Z == 0, N == V
4" hd le Kisebb vagy egyenlő aláírva Z == 1 vagy N != V
4" ő al mindig (feltétel nélkül)
4"hf - érvénytelen állapot

Ebből az ARM processzor utasítások megtanulásának újabb nehézsége következik - az utasításkódhoz hozzáadható sok utótag. Például az összeadás, feltéve, hogy a Z jelző be van állítva, az addeq parancs add + eq utótag. Ugrás a szubrutinra, ha az N=0 jelző blpl bl + pl utótag.

Zászlók (Negatív, nulla, hordozás, túlcsordulás) ugyanezt nem mindig állapítják meg az aritmetikával ill logikai műveletek, mint mondjuk x86 processzorban, de csak akkor, ha a programozó akarja. Ehhez van egy másik utótagja a parancsmnemonikának: "s" (a parancskódban a 20-as bittel kódolva). Így az add parancs nem változtatja meg a jelzőket, de az add parancs módosítja a jelzőket. És lehet egy feltételes összeadás parancs is, amely megváltoztatja a zászlókat. Például: addgts . Nyilvánvaló, hogy a különböző feltételes végrehajtási utótagokkal és beállítási jelzőkkel ellátott parancsnevek lehetséges kombinációinak száma nagyon sajátossá és nehezen olvashatóvá teszi az ARM processzor összeállítás kódját. Idővel azonban megszokja, és elkezdi megérteni ezt a szöveget.

Aritmetikai és logikai műveletek (adatfeldolgozás).

Az ARM processzor különféle aritmetikai és logikai műveleteket tud végrehajtani.

A tényleges négybites műveleti kódot (Opcode) a processzor utasításbitjei tartalmazzák.

A regiszter és az úgynevezett shifter_operand tartalmával bármilyen műveletet végrehajtunk. A művelet eredménye egy regiszterbe kerül. A négybites Rn és Rd regiszterindexek a processzor aktív bankjában.

Az I. bittől függően a 25 shifter_operand vagy numerikus konstansként, vagy az operandus második regiszterének indexeként, sőt a második operandus értékén végrehajtott eltolási műveletként is kezelhető.

Az assembler parancsok egyszerű példái például így néznek ki:

add hozzá az r0,r1,r2 @ r0 regiszterbe az r1 és r2 regiszterek értékeinek összegét
sub r5,r4,#7 @ különbséget (r4-7) ír be az r5 regiszterbe

Az elvégzett műveletek kódolása a következő:

4"h0 és Logikai ÉS Rd:= Rn ÉS shifter_operand
4"h1 eor XOR Rd:= Rn XOR shifter_operand
4"h2 al Aritmetikai kivonás Rd:= Rn - shifter_operand
4"h3 rsb Aritmetikai vissza kivonás Rd:= shifter_operand - Rn
4"h4 add Aritmetikai összeadás Rd:= Rn + shifter_operand
4"h5 adc Aritmetikai összeadás plusz átviteli jelző Rd:= Rn + shifter_operand + Carry Flag
4"h6 sbc hordozó aritmetikai kivonás Rd:= Rn - shifter_operand - NOT (Carry Flag)
4"h7 rsc hordozó aritmetikai fordított kivonás Rd:= shifter_operand - Rn - NOT (Carry Flag)
4"h8 tst Logikai ÉS, de az eredmény mentése nélkül csak az Rn ÉS shifter_operand S bit mindig beállított jelzői módosulnak
4"h9 teq Logikai exkluzív VAGY, de az eredmény tárolása nélkül csak az Rn EOR shifter_operand jelzők módosulnak
S bit mindig beállítva
4 "ha cmp Összehasonlítás, vagy inkább aritmetikai kivonás az eredmény tárolása nélkül, csak az Rn flagek változnak - shifter_operand S bit mindig beállítva
4 "hb cmn Inverz, vagy inkább aritmetikai összeadás összehasonlítása az eredmény emlékezése nélkül, csak az Rn + shifter_operand S bit mindig beállított jelzői módosulnak
4"hc orr Logikai VAGY Rd:= Rn VAGY shifter_operand
4" hd mov érték másolása Rd:= shifter_operand (nincs első operandus)
4"he bic Reset bits Rd:= Rn AND NOT(shifter_operand)
4"hf mvn Inverz érték másolása Rd:= NEM shifter_operand (nincs első operandus)

hordó váltó.

Az ARM processzor egy speciális „hordóváltó” sémával rendelkezik, amely lehetővé teszi, hogy az egyik operandust adott számú bittel eltolja vagy elforgatja bármilyen aritmetikai vagy logikai művelet előtt. Ez a processzor egy meglehetősen érdekes funkciója, amely lehetővé teszi nagyon hatékony kód létrehozását.

Például:

@ A 9-cel való szorzás egy szám 8-cal való szorzása
@ balra tolással 3 bit plusz egy másik szám
add hozzá r0, r1, r1, lsl #3 @ r0= r1+(r1<<3) = r1*9

A @ 15-tel való szorzás 16-tal való szorzás mínusz a szám
rsb r0, r1, r1, lsl #4 @ r0= (r1<<4)-r1 = r1*15

@ hozzáférés egy 4 bájtos szavakat tartalmazó táblázathoz, ahol
@r1 a tábla alapcíme
@r2 az elem indexe a táblázatban
ldr r0,

A logikai balra eltolás lsl mellett van még egy logikai jobbra eltolás lsr és egy aritmetikai jobbra eltolás asr (jelmegőrző eltolás, a legjelentősebb bitet balról szorozzuk az eltolással egyidejűleg).

A ror bitek is forognak - a biteket jobbra, a kinyomottakat pedig balra tolják.
Egy bites eltolás van a C jelzőn keresztül - ez az rrx parancs. A regiszter értéke egy bittel jobbra tolódik el. A bal oldalon a C jelzőt a regiszter felső bitjébe töltjük be

Az eltolást nem egy fix számállandó, hanem a harmadik regiszter-operandus értéke hajthatja végre. Például:

összeadjuk r0, r1, r1, lsr r3 @ értéke r0 = r1 + (r1>>r3);
összeadjuk r0, r0, r1, lsr r3 @ értéke r0 = r0 + (r1>>r3);

Tehát a shifter_operand az, amit az assembler utasításokban írunk le, például "r1, lsr r3" vagy "r2, lsl #5".

A legérdekesebb dolog az, hogy a műszakváltások használata nem kerül semmibe. Ezek az eltolások (általában) nem igényelnek extra órajelet, és nagyon jók a rendszer teljesítményéhez.

Numerikus operandusok használata.

Az aritmetikai vagy logikai műveletek második operandusként nemcsak a regiszter tartalmát, hanem egy numerikus állandót is használhatnak.

Sajnos itt van egy fontos korlátozás. Mivel minden parancs fix hossza 4 bájt (32 bit), nem fog működni „bármilyen” szám kódolása. A műveleti kódban 4 bitet már elfoglal a végrehajtási feltétel kódja (Cond), 4 bitet magának a műveleti kódnak (Opcode), majd 4 bitet - az Rd vevőregisztert, és további 4 bitet - az első regisztert. Rn operandus, plusz egyéb különböző jelzők: I 25 (csak numerikus állandót jelöl a műveleti kódban) és S 20 (jelzők beállítása a művelet után). Összességében csak 12 bit marad egy lehetséges konstanshoz, az úgynevezett shifter_operandhoz – ezt láttuk fent. Mivel 12 bit csak szűk tartományban tud számokat kódolni, az ARM processzor fejlesztői úgy döntöttek, hogy a konstanst a következőképpen kódolják. A shifter_operand tizenkét bitje két részre oszlik: a négybites forgatási indexre encode_imm és a tényleges nyolcbites imm_8 numerikus értékre.

Az ARM processzoron a konstans egy 8 bites szám egy 32 bites számon belül, páros számú bittel jobbra elforgatva. Azaz:

imm_32 = imm_8 ROR (encode_imm *2)

Elég okosnak bizonyult. Kiderült, hogy nem minden számállandó használható az assembler parancsokban.

Tudsz írni

add hozzá r0, r2, #255 @ decimális állandó
add hozzá az r0, r3, #0xFF @ konstans hexadecimális értékét

mivel a 255 a 8 bites tartományban van. Ezek a parancsok a következőképpen lesznek összeállítva:

0: e28200ff add r0, r2, #255 ; 0xff
4: e28300ff add r0, r3, #255 ; 0xff

És még írni is tudsz

add hozzá r0, r4, #512
add hozzá: r0, r5, 0x650000

A lefordított kód így fog kinézni:

0: e2840c02 add r0, r4, #512 ; 0x200
4: e2850865 add r0, r5, #6619136 ; 0x650000

Ebben az esetben maga az 512-es szám természetesen nem fér bele egy bájtba. Másrészt elképzeljük, hogy hexadecimális formában 32'h00000200, és azt látjuk, hogy ez a 2 24 bittel jobbra fordult (1 ror 24). A forgatási együttható fele a 24-nek, azaz 12. Így kiderül, shifter_operand = ( 4'hc , 8'h02 ) - ez a parancs tizenkét legkisebb jelentőségű bitje. Ugyanez a helyzet a 0x650000 számmal. Ehhez shifter_operand = ( 4'h8, 8'h65 ).

Egyértelmű, hogy írni nem lehet

add hozzá r0, r1,#1234567

vagy nem tudsz írni

mov r0, #511

mivel itt a szám nem ábrázolható imm_8-ként és encode_imm - az elforgatási tényező. Az assembler fordítója hibát fog dobni.

Mi a teendő, ha egy állandót nem lehet közvetlenül a shifter_operand-ba kódolni? Mindenféle trükköt kell csinálnod.
Például először betöltheti az 512-es számot egy szabad regiszterbe, majd kivonhat egyet:

mov r0, #511
al r0,r0,#1

A második módja annak, hogy egy adott számot betöltsünk egy regiszterbe, az, hogy kiolvassuk egy speciálisan lefoglalt változóból a memóriában:

ldr r7,my_var
.....
my_var: .word 0x123456

Az írás legegyszerűbb módja a következő:

ldr r2,=511

Ebben az esetben (figyelje meg a "=" jelet), ha a konstans imm_8 és encode_imm formátumban ábrázolható, ha beilleszthető egy 12 bites shifter_operandba, akkor az assembler fordító automatikusan lefordítja az ldr-t egy mov utasításba. De ha a szám nem ábrázolható így, akkor a fordító maga lefoglal egy memóriacellát ennek a konstansnak a programban, és maga ad nevet ennek a memóriacellának, és lefordítja a parancsot az ldr -be.

Íme, amit írtam:

ldr r7,my_var
ldr r8,=511
ldr r8,=1024
ldr r9,=0x3456
........
Saját_var: .word 0x123456

Összeállítás után ezt kaptam:

18: e59f7030 ldr r7, ; 50
1c: e59f8030 ldr r8, ; 54
20: e3a08b01 mov r8, #1024 ; 0x400
24: e59f902c ldr r9, ; 58
.............
00000050 :
50:00123456 .word 0x00123456
54:000001ff .word 0x000001ff
58:00003456 .word 0x00003456

Ne feledje, hogy a fordító a pc regiszterhez (más néven r15) képest memóriacímzést használ.

Memóriacella beolvasása és regiszter írása a memóriába.

Ahogy fentebb is írtam, az ARM processzor csak aritmetikai vagy logikai műveleteket tud végrehajtani a regiszterek tartalmával. A műveletekhez szükséges adatokat ki kell olvasni a memóriából, és a műveletek eredményét vissza kell írni a memóriába. Vannak erre speciális parancsok: ldr (valószínűleg a „LoaD Register” kombinációból) az olvasáshoz és str (valószínűleg „ STore Register”) az íráshoz.

Úgy tűnik, hogy csak két csapat van, de valójában sok változatuk van. Csak nézze meg az Amber ARM processzor ldr /str parancsainak kódolását, és nézze meg, hány L 20 , W 21 , B 22 , U 23 , P 24 , I 25 kiegészítő jelzőbit van - és ezek határozzák meg a parancs viselkedését. :

  • Az L 20 bit határozza meg, hogy írjon vagy olvasson. 1 - ldr , olvasás, 0 - str , írás.
  • A B 22 bit határozza meg, hogy 32 bites szót vagy 8 bites bájtot kell-e olvasni/írni. Az 1 bájtos műveletet jelent. Amikor egy bájtot beolvasunk egy regiszterbe, a regiszter felső bitjei nullára kerülnek.
  • Az I. 25. bit az Eltolás mező használatát határozza meg. Ha I 25 ==0, akkor az Offset numerikus eltolásként értelmeződik, amelyet vagy hozzáadni kell az alapcímhez a regiszterből, vagy ki kell vonni. De az összeadás vagy kivonás az U 23 bittől függ.

(Cond) - a művelet végrehajtásának feltétele. Értelmezése ugyanúgy történik, mint a logikai/aritmetikai parancsok esetében – az olvasás vagy az írás lehet feltételes.

Így az assembler szövegbe valami ilyesmit írhat:

ldr r1, @ az r1 regiszterbe beolvassa a szót a címen az r0 regiszterből
ldrb r1, @ az r1 regiszterbe beolvassa a bájtot a címen az r0 regiszterből
ldreq r2, @ feltételes szóolvasás
ldrgtb r2, @ feltételes bájt beolvasása
ldr r3, @ beolvassa a szót a 8-as címen az r4 regiszterben lévő címhez képest
ldr r4, @ beolvassa a szót a -16 címen az r5 regiszterben lévő címhez képest

A szöveg összeállítása után láthatja a parancsok tényleges kódjait:

0: e5901000 ldr r1,
4: e5d01000 ldrb r1,
8: 05912000 ldreq r2,
c:c5d12000 ldrbgt r2,
10: e5943008 ldr r3,
14: e5154010 ldr r4,

A fenti példában csak az ldr -t használom, de az str is nagyjából ugyanúgy használatos.

Vannak módok az indexelés előtti és az indexelés utáni visszaírási memória elérésére. Ezekben az üzemmódokban a memória-hozzáférési mutató az utasítás végrehajtása előtt vagy után frissül. Ha ismeri a C programozási nyelvet, akkor ismeri a mutató hozzáférési konstrukciókat, mint például ( *psource++;) vagy ( a=*++psource;). Az ARM processzorban ez a memória-elérési mód valósult meg. Olvasási parancs végrehajtásakor két regiszter egyszerre frissül – a vevőregiszter fogadja a memóriából kiolvasott értéket, és a memóriacella regisztermutatójában lévő érték előre vagy hátra mozog.

Véleményem szerint ezeknek a parancsoknak az írása némileg logikátlan. Sok időbe telik megszokni.

ldr r3, ! @psrc++; r3 = *psrc;
ldr r3, , #4 @ r3 = *psrc; psrc++;

Az első ldr parancs először növeli a mutatót, majd beolvassa. A második parancs először beolvassa, majd növeli a mutatót. A psrc mutató értéke az r0 regiszterben található.

A fenti példák mindegyike arra az esetre vonatkozott, amikor a parancskód I 25. bitje visszaállításra került. De még telepíthető! Ekkor az Offset mező értéke nem numerikus állandót, hanem a műveletben érintett harmadik regisztert fogja tartalmazni. Sőt, a harmadik regiszter értéke még előre tolható!

Példák a lehetséges kódváltozatokra:

0: e7921003 ldr r1, @ beolvasási cím - az r2 és r3 regiszterekből származó értékek összege
4: e7b21003 ldr r1, ! @ ugyanaz, de az r2 beolvasása után az r3 értékével növekszik
8: e6932004 ldr r2, , r4 @ először az r3-at olvassa be, majd az r3 értéke r4-gyel nő
c: e7943185 ldr r3, @ r4+r5*8 ​​cím beolvasása
10: e7b43285 ldr r3, ! @ cím az r4+r5*32 olvasásához, az r4 beolvasása után ennek a címnek az értékére lesz beállítva
14: e69431a5 ldr r3, , r5, lsr #3 @ beolvassa az r4 címet, az r4 parancs végrehajtása után az r4+r5/8 értékre lesz állítva

Itt vannak az olvasási/írási parancsok változatai az ARM v2a processzorban.

Az ARM processzorok régebbi modelljeiben az utasítások sokfélesége még nagyobb.
Ez annak köszönhető, hogy a processzor például nem csak szavak (32 bites számok) és bájtok olvasását teszi lehetővé, hanem félszavak (16 bit, 2 bájt) olvasását is. Ezután a "h" utótag a félszó szóból hozzáadódik az ldr / str parancsokhoz. A parancsok így néznek ki: ldrh vagy strh . Vannak olyan parancsok is, amelyek előjeles számként értelmezik az ldrsh félszavakat vagy ldrsb bájtokat. Ezekben az esetekben a betöltött sávszó vagy bájt legjelentősebb bitje a célregiszterben a teljes szó legjelentősebb bitjére szorzódik. Például a 0xff25 félszó ldrsh paranccsal történő betöltése 0xffffff25-öt eredményez a célregiszterben.

Többszöri olvasás és írás.

Nem az ldr /str parancsok az egyedüliek a memória elérésére. Az ARM processzornak vannak olyan utasításai is, amelyek lehetővé teszik a blokkátvitelt - egyszerre több regiszterbe töltheti be a memóriából több egymást követő szó tartalmát. Lehetőség van arra is, hogy több regiszter értékeit egymás után írjuk a memóriába.

A blokkátviteli parancsok megjegyzései az ldm gyökérrel (LoaD Multiple ) vagy az stm (Többszörös tárolása ) gyökérrel kezdődnek. De aztán, ahogy az ARM-ben lenni szokott, a történet utótagokkal kezdődik.

Általában a parancs így néz ki:

op(kond)(mód) Rd(, {Register list} !}

Az utótag (Cond) érthető, ez a parancs végrehajtásának feltétele. Az utótag (mód) az átviteli mód, erről később. Az Rd egy regiszter, amely megadja a memóriában lévő báziscímet az olvasáshoz vagy az íráshoz. Felkiáltójel az Rd regiszter után azt jelenti, hogy az olvasási/írási művelet után megváltozik. A memóriából betöltött vagy a memóriába kirakott regiszterek listája a (Register list) .

A regiszterek listája kapcsos zárójelben van megadva, vesszővel elválasztva vagy tartományként. Például:

stm r0,(r3,r1, r5-r8)

A memóriába írás nem megfelelő sorrendben történik. A lista egyszerűen jelzi, hogy mely regiszterek kerülnek a memóriába, és ennyi. A parancskódban 16 bit van fenntartva a Regiszterlista számára, akárcsak a processzorbank regisztereinek száma. Ebben a mezőben minden bit jelzi, hogy melyik regiszter vesz részt a műveletben.

Most az olvasási/írási mód. Van hol összezavarodni. A helyzet az, hogy ugyanahhoz a művelethez különböző módnevek használhatók.

Ha tesz egy kis lírai kitérőt, akkor beszélnie kell ... a veremről. A verem egy módja a LIFO típusú adatok elérésének - Last In First Out (wiki) - Last in, First Out. A verem széles körben használatos a programozásban eljárások meghívásakor és a regiszterek állapotának elmentésekor a függvények bejáratánál, illetve kilépéskor visszaállításánál, valamint paraméterek átadásakor a meghívott eljárásoknak.

Ki gondolta volna, négyféle a verem a memóriában.

Az első típus a Full Descending . Ilyenkor a veremmutató egy foglalt veremelemre mutat, és a verem a csökkenő címek irányába növekszik. Ha egy szót kell a verembe tenni, akkor először a veremmutató csökken (Decrement Before), majd a szó a veremmutató címére kerül. Ha el kell távolítania egy számítógépes szót a veremből, akkor a szó a veremmutató aktuális értékén kerül beolvasásra, majd a mutató felfelé mozog (Increment After).

A második típus a Full Ascending. A verem nem lefelé, hanem felfelé nő, a nagy címek felé. A mutató a foglalt elemre is mutat. Ha egy szót kell a verembe tenni, akkor először a veremmutató növekszik, majd a szót a mutató írja (Increment Before). Amikor ki kell venni a veremből, először a veremmutatóval olvasunk, mert az a foglalt elemre mutat, majd a veremmutatót csökkentjük (Decrement After).

A harmadik típus az Empty Descending . A verem lefelé nő, mint a Full Descending esetén, de a különbség az, hogy a veremmutató egy üres cellára mutat. Így amikor egy szót kell a verembe tenni, azonnal rögzítésre kerül, majd a veremmutató csökken (Decrement After). A veremből való eltávolításkor a mutató először növekszik, majd beolvasásra kerül (Increment Before).

A negyedik típus az Empty Ascending . Remélem, minden világos - a verem nő. A veremmutató egy üres elemre mutat. Nyomja meg a verem, hogy írjon egy szót a veremmutató címére, és növelje a veremmutatót (Increment After ). Nyissa ki a veremből - csökkentse a veremmutatót, és olvassa el a szót (Csökkentse az előtt).

Így a veremmel végzett műveletek során növelni vagy csökkenteni kell a mutatót - (Növekedés / Csökkentés ) az olvasás/memóriaírás előtt vagy után (Előtte / Utána), a verem típusától függően. BAN BEN Intel processzorok, például vannak speciális parancsok a veremmel való munkához, például PUSH (szót rakjon a verembe) vagy POP (szó eltávolítása a veremből). Az ARM processzorban nincsenek speciális parancsok, de az ldm és az stm parancsok használatosak.

Ha az ARM processzor utasításaival valósítja meg a veremet, akkor a következő képet kapja:

Miért kellett ugyanannak a csapatnak különböző neveket adni? Egyáltalán nem értem... Itt persze meg kell jegyezni, hogy az ARM stack szabványa továbbra is Full Descending.

Az ARM processzor veremmutatója az sp vagy r13 regiszter. Ez általában egy ilyen megállapodás. Természetesen az stm írása vagy az ldm olvasása más alapregiszterekkel is megoldható. Emlékeztetni kell azonban arra, hogy az sp regiszter miben különbözik a többi regisztertől - eltérő lehet a processzorok különböző üzemmódjaiban (USR, SVC, IRQ, FIRQ), mert saját regiszter bankjaik vannak.

És még egy megjegyzés. Írjon be egy sort az ARM összeállítási kódba push (r0-r3), Természetesen lehet. De valójában ugyanaz a parancs lesz stmfd sp!,(r0-r3).

Végül adok egy példát az assembly kódra és a lefordított szétszedett szövegére. Nekünk van:


stmfd sp!,(r0-r3)
stmdb sp!,(r0-r3)
push (r0-r3)

@ez a három utasítás ugyanaz, és ugyanazt csinálják
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,pc)

Összeállítás után kapjuk:

0: e92d000f push(r0, r1, r2, r3)
4: e92d000f push (r0, r1, r2, r3)
8: e92d000f push (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)

Átmenetek a programokban.

A programozás nem lehetséges átmenetek nélkül. Bármely programban van kód ciklikus végrehajtása és eljárások, függvények hívásai, van kódszakaszok feltételes végrehajtása is.

Az Amber ARM v2a processzorban csak két parancs található: b (a Branch szóból - elágazás, átmenet) és bl (Branch with Link - átmenet a visszatérési cím elmentésével).

A parancs szintaxisa nagyon egyszerű:

b(cond) címke
bl(cond) címke

Nyilvánvaló, hogy bármilyen átmenet lehet feltételes, vagyis a programban lehetnek olyan furcsa szavak, amelyek a "b" és a "bl" gyökökből és a feltétel utótagokból (Cond) alakultak:

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

A változatosság elképesztő, nem?

Az ugrási utasítás 24 bites eltolást tartalmaz. Az ugráscímet a pc mutató aktuális értékének és az eltolási számnak 2 bittel balra eltolt összegeként számítjuk ki, előjeles számként értelmezve:

Új pc = pc + Offset*4

Így az ugrási tartomány 32 MB előre vagy hátra.

Nézzük meg, mi az az elágazás a bl visszatérési cím elmentésével. Ez a parancs szubrutinok hívására szolgál. Ennek az utasításnak egy érdekessége, hogy az eljárásból származó visszatérési cím az eljárás meghívásakor nem a veremben van tárolva, mint az Intel processzorok esetében, hanem a szokásos r14 regiszterben. Ezután az eljárásból való visszatéréshez nincs szükség speciális ret parancsra, mint ugyanazon Intel processzoroknál, hanem egyszerűen visszamásolhatja az r14 értékét a számítógépre. Most már világos, hogy miért van az r14 regiszter alternatív név lr (Link regisztráció).

Tekintsük az Amber SoC hello-world projektjének outbyte rutinját.

000004a0<_outbyte>:
4a0: e59f1454 ldr r1, ; 8fc< адрес регистра данных UART >
4a4: e59f3454 ldr r3, ; 900< адрес регистра статуса UART >
4a8: e5932000 ldr r2, ; olvassa el az aktuális állapotot
4ac: e2022020 és r2, r2, #32
4b0: e3520000 cmp r2, #0 ; ellenőrizze, hogy az UART nem foglalt-e
4b4: 05c10000 strbeq r0, ; csak akkor írjon karaktert az UART-ba, ha az nem foglalt
4b8: 01b0f00e movseq pc, lr ; feltételes visszatérés az eljárásból, ha az UART nem volt foglalt
4bc: 1affff9 bne 4a8<_outbyte+0x8>; hurok az UART állapotának ellenőrzéséhez

Úgy gondolom, hogy ennek a töredéknek a megjegyzéseiből egyértelműen kiderül, hogyan működik ez az eljárás.

Egy másik fontos megjegyzés az átmenetekkel kapcsolatban. Az r15 regiszter (pc) használható normál aritmetikai vagy logikai műveletekben célregiszterként. Tehát egy olyan parancs, mint az add pc,pc,#8, elég utasítás egy másik címre ugráshoz.

Még egy megjegyzést kell tenni az átmenetekkel kapcsolatban. A régebbi ARM processzorok több további parancsokátmenetek bx , blx és blj . Ezek a parancsok a kódrészletekre való ugráshoz egy másik parancsrendszerrel. A Bx /blx lehetővé teszi az ARM processzorok 16 bites THUMB kódjára való átállást. A Blj a Jazelle parancsrendszer eljáráshívása (Java nyelv támogatása az ARM processzorokban). Az Amber ARM v2a nem rendelkezik ezekkel a parancsokkal.

Egyéb ARM processzor utasítások.

Ha hibát észlel, jelöljön ki egy szövegrészt, és nyomja meg a Ctrl + Enter billentyűket
OSSZA MEG: