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

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 tanulmányozásához hasznos legalább egyet elkészíteni egy egyszerű program assemblerben.

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 TÉR 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 a direktívát mind állandó, mind pedig a számára használjuk véletlen hozzáférésű memória. 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.

Jelenleg még a meglehetősen egyszerű mikrokontrollerek programozásához is általában magas szintű nyelveket használnak, amelyek a C vagy C++ nyelv részhalmazai.

A processzorok architektúrájának és jellemzőinek tanulmányozásakor azonban célszerű assembly nyelveket használni, mivel csak ilyen megközelítéssel lehet a vizsgált architektúra jellemzőit azonosítani. Emiatt a további bemutatás az Assembly nyelven történik.

Mielőtt folytatná az ARM7 parancsok vizsgálatát, meg kell jegyezni a következő jellemzőket:

    Két utasításkészlet támogatása: ARM 32 bites utasításokkal és THUMB 16 bites utasításokkal. A következő egy 32 bites utasításkészlet, az ARM szó ehhez a formátumhoz tartozó utasításokat, az ARM7 szó pedig magát a CPU-t jelenti.

    Két 32 bites címformátum támogatása: with fordított sorrendben bitek (big-endian processzor és little-endian processzor). Az első esetben a legjelentősebb bit (Most Significant Bit – MSB) a szó legkisebb jelentőségű bitjében található, a második esetben pedig a legjelentősebb bitben. Ez kompatibilitást biztosít a 32 bites processzorok többi családjával magas szintű nyelvek használatakor. Azonban számos ARM maggal rendelkező processzorcsaládban csak kis endiant használnak (azaz az MSB a cím legjelentősebb bitje), ami nagyban leegyszerűsíti a processzorral való munkát. Mivel az ARM7-hez használt fordító mindkét formátumú kóddal működik, ezért meg kell győződni arról, hogy a szóformátum helyesen van beállítva, különben a kapott kód "kifelé fordul".

    Teljesítményképesség különféle típusok az egyik operandus eltolása "menetben", mielőtt az ALU-ban használná

    Bármely parancs feltételes végrehajtásának támogatása

    Lehetőség a műveleti eredmények zászlóinak megváltoztatásának megtiltására.

      1. Feltételes parancsvégrehajtás

Az ARM utasításkészlet egyik fontos jellemzője, hogy támogatja feltételes végrehajtás bármelyik csapat. A hagyományos mikrokontrollerekben az egyetlen feltételes utasítás a feltételes ugrás, és talán számos más, például az egyes bitek állapotának ellenőrzésére vagy megváltoztatására vonatkozó utasítások. Az ARM utasításkészletben az utasításkód felső 4 bitje mindig a CPSR regiszter feltételjelzőivel van összehasonlítva. Ha értékeik nem egyeznek, a visszafejtési szakaszban lévő parancsot a NOP (no operation) parancs váltja fel.

Ez jelentősen csökkenti a program "rövid" átmenetekkel rendelkező szakaszainak végrehajtási idejét. Így például ha másodfokú egyenleteket old meg valós együtthatókkal és tetszőleges gyökekkel negatív diszkriminánssal, akkor a számítás előtt szükséges négyzetgyök változtassa meg a diszkrimináns előjelét, és rendelje hozzá az eredményt a válasz képzeletbeli részéhez.

A probléma hagyományos megoldásában egy feltételes ugrás parancsot kell megadni. Ennek a parancsnak a végrehajtása legalább 2 ciklust vesz igénybe - dekódolás és az új cím értékének betöltése a programszámlálóba, valamint további ciklusok száma a parancsfolyamat betöltéséhez. Ha feltételes parancsvégrehajtást használ pozitív diszkriminánssal, az előjelváltási parancsot egy üres művelet helyettesíti. Ebben az esetben az utasítási csővezeték nem törlődik, és a veszteségek legfeljebb egy órajelet jelentenek. Az a küszöb, amelynél a feltételes utasítások NOP utasítással való helyettesítése hatékonyabb, mint a hagyományos feltételes elágazási utasítások végrehajtása és az ehhez kapcsolódó csővezeték újratöltése, megegyezik a mélységével, pl. három.

A szolgáltatás megvalósításához hozzá kell adnia a feltételjelzők tesztállapotait meghatározó tizenhat előtag bármelyikét az assembler utasítások (és a C) alapvető mnemonikus jelöléséhez. Ezeket az előtagokat a táblázat tartalmazza. 3. Ennek megfelelően minden csapat számára 16 lehetőség van. Például a következő parancs:

MOVEQ R1, #0x008

azt jelenti, hogy a 0x00800000 szám betöltése az R1 regiszterbe csak akkor történik meg, ha az utolsó adatfeldolgozási parancs eredménye „egyenlő” volt, vagy 0 eredmény érkezett és a CPSR regiszter jelzője (Z) ennek megfelelően van beállítva.

3. táblázat

Parancs előtagok

Jelentése

Z telepítve

Z leesett

Telepítve

Nagyobb vagy egyenlő (előjel nélküli)

C reset

Lent (aláíratlan)

N telepítve

Negatív eredmény

N leesett

Pozitív eredmény vagy 0

V telepítve

Túlcsordulás

V leesett

Nincs túlcsordulás

Telepítve,

Z leesett

Fent (jel nélkül)

Leejtéssel,

Z telepítve

Kisebb vagy egyenlő (előjel nélküli)

Nagyobb vagy egyenlő (előjeles)

N nem egyenlő V-vel

Kevesebb, mint (aláírva)

Z visszaállítás ÉS

(N egyenlő V)

Továbbiak (aláírva)

Z értéke VAGY

(N nem egyenlő V-vel)

Kisebb vagy egyenlő (jelű)

(figyelmen kívül hagyva)

Feltétel nélküli végrehajtás

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, amelyek közül Ebben a pillanatban mindössze 16 alkalom áll a programozó rendelkezésére A processzor működésének többféle módja van, a működési módtól függően a megfelelő regiszterkészlet kerül kiválasztásra. 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 ill operációs rendszer(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 megvalósítására szolgál programozottan) É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 lehet benne „bármilyen” számot kódolni. 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. De másrészt elképzeljük, hogy hexadecimális formában 32'h00000200, és azt látjuk, hogy ez a 2 24 bittel jobbra fordulva (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

Ezek 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ó. Azonban emlékeznie kell arra, hogy az sp regiszter miben különbözik a többi regisztertől - benne van különböző módok A processzorok működése (USR, SVC, IRQ, FIRQ) eltérő lehet, mert saját regiszter bankok 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 egy eljáráshívás a Jazelle parancsrendszerhez (támogatás Java nyelv ARM processzorokon). Az Amber ARM v2a nem rendelkezik ezekkel a parancsokkal.

Egyéb ARM processzor utasítások.

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-nek viszonylag egyszerű utasításkészlete van, világos 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, amelyet projektünkben használunk deszkák rover 2 rover 2. 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. A modern processzorok mindenféle elágazás-előrejelző mechanizmussal rendelkeznek, amelyek valahogy optimalizálják a csővezeték kitöltését, de ez a mi processzorunkban nem így van. 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) ez nem mindig van beállítva az aritmetikai vagy logikai műveletek során, mint mondjuk egy x86-os processzornál, hanem csak akkor, amikor 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 lehet benne „bármilyen” számot kódolni. 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. De másrészt elképzeljük, hogy hexadecimális formában 32'h00000200, és azt látjuk, hogy ez a 2 24 bittel jobbra fordulva (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

Ezek 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. Az Rd regiszter utáni felkiáltójel azt jelzi, hogy az olvasási/írási művelet után módosul. 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. Az Intel processzorok például speciális parancsokkal dolgoznak a veremmel, mint például PUSH (szót rak a verembe) vagy POP (szó kiemelése 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 az r14 regiszternek miért van alternatív neve lr (Link Register).

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 további bx, blx és blj ugrási utasításokkal is rendelkeznek. 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.

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