==== ARM processzor (Advanced RISC Machines) ==== Ebben a fejezetben megmutatjuk, hogyan kell közvetlenül egy mikroprocesszorra programozni. Az ARM processzort választottuk, hiszen nagy valószínűséggel olyan mobiltelefonunk van ami ezt a típust használja. Nem törekszünk teljességre, csak az adatmozgatás, összehasonlítás, indirekt memória elérés és a verem alaphasználatát mutatjuk be. Ezen a linken találunk egy teljes értékű ARM CPU szimulátort: https://cpulator.01xz.net/?sys=arm , a továbbiakban ezt használjuk a próbáink során. ==== Összeadás ==== Rögtön készítsük el az első programunkat! Másoljuk be a következő program szöveget a weblapon középen elhelyezkedő szövegmezőbe: .global _start _start: mov r1, #2 mov r2, #3 add r4, r1, r2 bkpt A fenti program kiszámítja a 2 + 3 összeadás értékét. Először az r1 regiszterbe másolja a 2-t (mov r1, #1), majd r2-be a 3-mat (mov r2, #3), majd r4-be helyezi az r1 és r2 összegét. A "Compile and Load (F5)" feliratú gombot megnyomva a szöveges assembly program lefordul és a bináris változata betöltődik a memória 00000000 címére: {{tanszek:oktatas:szamitastechnika:asm_1.png|}} Az **Address** oszlop a memória címet mutatja, az **opcode** a gépi kódu utasítást. A fenti programkód gépi kódú alakja a következő: **e3a01002e3a02003e0814002e1200070** Ez a 16 byte jelenik meg a memóriában a nullás címtől kezdve a szimulátorban. Az eredeti forráskódot a **Disassembly** oszlopban piros kicsi számokkal jelölve látjuk. Rögtön észrevehető, hogy vannak olyan jelölések pl: _start, aminek nincs konkrét gépi kódú megfelelője. A **Disassembly** visszafordítást jelent, a rendszer megpróbálja visszaalakítani a gépi kódú utasításokat assembly nyelvű szöveggé. A sárgával kijelölt sor mutatja, hogy az utasításszámláló melyik memóriacímet fogja végrehajtani. Nyomjuk meg többször a F2 billenytyűt, ami a soronkénti végrehajtást jelenti, közben figyeljük meg a regiszterek változását a bal oldalon (pirossal kiemeli a szimulátor a változásokat). A **bkpt** parancsig elég elmenni. {{tanszek:oktatas:szamitastechnika:asm_2.png|}} Jól látható, hogy a megfelelő regiszterekben megjelentek a számok, és az eredmény is a r4-esben. === Összehasonlítás === Az előző ábrán látható a **Current Program Status Register (CPSR)** regiszter legalul (ez az állapot regiszter). Ennek egyes bitjei jelölik azt, hgy egy művelet eredménye nulla, negatív, átvitelt eredményez, stb. Nem kell tudni, hogy a 32 bitből ezek melyek pontosan, a mellette megjelenő **NZCVI** szöveg egyes betűi jelölik a főbb állapotbiteket. Vátsunk vissza a forráskód szerkesztő fűlre: ctrl+e, vagy alul rákattintva, majd másoljuk be az alábbi kódot a szerkesztőbe, majd fordísuk és kövessük nyomon: .global main _start: mov r0, #2 /* r0 = 2 */ cmp r0, #3 /* r0 - 3. Negatív bit egyre fog váltani */ addlt r0, r0, #5 /* lt => less than. Ha (r0 < 3) akkor adj hozzá 5-öt */ cmp r0, #3 /* r0 - 3. Zero bit 1 lesz. Negative bit 0 lesz */ addlt r0, r0, #5 /* Ha (r0 < 3) akkor adj hozzá 1-et az r0-hoz */ bkpt A 2. sorban a **cmp** (**c**o**mp**are) összehasonlítja az argumentumait. Utána az **addlt** csak akkor ad hozzá 5-öt az r1 hez ha (less than) feltételnek megfelelően az alábbi táblázatban N!=V azaz a N és V kapcsolók értéke nem egyezik meg. |Feltétel kód| Jelentés| Státusz bitek| |EQ| Equal| Z==1| |NE| Not Equal| Z==0| |GT| Signed Greater Than| (Z==0) és (N==V)| |LT| Signed Less Than| N!=V| |GE| Signed Greater Than or Equal| N==V| |LE| Signed Less Than or Equal| (Z==1) vagy (N!=V)| |MI| Negative (or Minus)| N==1| |PL| Positive (or Plus)| N==0| === Elágazások === .global main _start: mov r4, #10 loop_label: sub r4, r4, #1 cmp r4, #0 bne loop_label A fenti kódrészlet 10-től 0-ig számol visszafelé, az r1 regiszterben tárolja a 10-et kezdetben, majd lépésenként csökkenti 1-el. ** sub r4, r4, #1 ** jelentése r4 = r4 - 1, azaz mentsd el az r4 aktuális értékét mínusz 1-et az r4 ben. * A cmp összehasonlítja 0-val a r4-et, a **bne** (branch if not equals) ágazz el ha nem egyenlő. === Memória kezelés === .data /* .data szekció dinamikusan jön létre, előre nehéz megmondani, hogy hol fog elhelyezkedni */ var1: .word 3 /* var1 változó */ var2: .word 4 /* var2 változó */ .text /* kód kezdete */ .global _start _start: ldr r0, adr_var1 @ töltsük be a var1 memória címét az r0-be ldr r1, adr_var2 @ töltsük be a var2 memória címét az r1-be ldr r2, [r0] @ r2 be töltsük be az r0-regiszter által mutatott címen lévő értéket str r2, [r1] @ r2 értékét írjuk ki az r1 által mutatott memória címre bkpt adr_var1: .word var1 /* a var1 címe lesz letárolva itt */ adr_var2: .word var2 /* a var2 címe lesz letárolva itt */ A példában látható hogyan lehet változókat tárolni és a memóriacímeket ahol el vannak tárolva, hogyan lehet módosítani, illetve betölteni a regiszterekbe tartalmukat. A legutolsó utasítás felülírja adr_var2 helyen tárolt értéket, ezt a módosulást a harmadik memory fülön tudjuk ellenőrizni. === Verem === A verem adattárolásra szolgál, memóriacímét az **sp** regiszter mutatja és új érték verembe helyezésekor az sp regiszter csökken 4 byteot. .global _start _start: mov r0, #2 /* r0 = 2 */ push {r0} /* r0 értékének tárolása a veremben */ mov r0, #3 /* r0 = 3 */ pop {r0} /* r0 korábbi értékének visszatöltése a veremből. */ bkpt A push utasítás után a "memory" fülön megnézhetjük hogy a 000000 memóriacím elé 4 byte-al kezdődően beíródott a 2 egy 32 bites egészként.