Přesuny dat
Procesory ARM obecně implementují Load/Store architekturu. Procesory tohoto typu musí nejdříve přesunout data z paměti do registrů, potom nad nimi provedou požadované operace a výsledek pak uloží zpět do paměti.
Load a Store
Cortex-M3 je vybaven řadou variant Load/Store instrukcí se širokou paletou možností adresace paměti. Základní instrukcí pro přesun dat mezi pamětí a registrem je instrukce LDR - Load register, která provádí načtení 32-bitového slova (Word) z paměti do registru. Místo v paměti je nepřímo adresované hodnotou uloženou v bázovém registru. V příkladu je cílovým operandem registr R0, adresa v paměti je uložena v registru R1. Uložení obsahu registru do paměti provádí instrukce STR - Store register.
V obou případech jde o přesun 32-bitového slova. Adresa v paměti by měla být zarovnaná na délku slova, dva nejnižší bity adresy by měly být nulové, tzn. adresa v paměti by měla být beze zbytku dělitelná čtyřmi. Oproti svým předchůdcům, jako je třeba ARM7TDMI, umožňuje procesor Cortex-M3 čtení nebo zápis slova na nezarovnané adrese, ale takový přístup procesoru k paměti je pomalejší. Některé oblasti v paměti nezarovnaný přístup neumožňují.
LDR R0, [R1] @ R0 = mem_32[R1] STR R0, [R1] @ mem_32[R1] = R0
Variantou těchto dvou instrukcí je dvojice pro načtení a uložení 16-bitového půlslova (Halfword) a další dvojice pro načtení a uložení 8-bitového bajtu (Byte). Adresa při práci s půlslovem by měla být dělitelná dvěma.
LDRH R0, [R1] @ R0 = mem_16[R1] STRH R0, [R1] @ mem_16[R1] = R0 LDRB R0, [R1] @ R0 = mem_8[R1] STRB R0, [R1] @ mem_8[R1] = R0
Vedle bajtu a půlslova lze jednou instrukcí přenést také dvojité slovo (Doubleword). Instrukce LDRD načte do dvou vyjmenovaných registrů dvě 32-bitová slova, která jsou uložena od adresy obsažené v bázovém registru. Uložení registrů do paměti provede instrukce STRD.
LDRD R0, R1, [R2] @ R0, R1 = mem_64[R2] STRD R0, R1, [R2] @ mem_64[R2] = R0, R1
Adresace offsetem
K adrese uložené v bázovém registru může být připočteno posunutí (offset). Offset může být buď přímá hodnota nebo jméno registru, jehož obsahem je hodnota offsetu. Pokud použijeme offset definovaný přímým operandem, máme k dispozici také adresování s automatickou změnou bázového registru, pre-indexování a post-indexování. Všechny tyto způsoby adresace lze použít i pro instrukce, které přesunují data s jinou velikostí než je 32-bitové slovo (byte, halfword, doubleword).
- adresování s offsetem v registru
- syntaxe je [Rn, Rm], případně [Rn, Rm, LSL #n]
- obsah registru Rm (offset) je přičten k obsahu bázového registru Rn a výsledek je použit jako adresa pro přístup k paměti
- případně je obsah registru Rm posunut vlevo o n bitů (násobení dvěma) a výsledek je přičten k obsahu bázového registru Rn
- posun n může nabývat hodnoty 0 až 3
- adresování s offsetem s přímou hodnotou
- syntaxe je [Rn, #offset]
- hodnota offsetu je přičtena k obsahu registru Rn a výsledek je použit jako adresa pro přístup k paměti
- offset může nabývat hodnoty -255 až 4095
- pre-indexová adresace
- syntaxe je [Rn, #offset]!
- hodnota offsetu je přičtena k obsahu registru Rn, výsledek je použit jako adresa pro přístup k paměti a tento výsledek je dosazen do registru Rn (rozdíl je ve vykřičníku za pravou hranatou závorkou)
- offset může nabývat hodnoty -255 až 255
- post-indexová adresace
- syntaxe je [Rn], #offset
- obsah registru Rn je použit jako adresa pro přístup k paměti, hodnota offsetu je přičtena k obsahu registru Rn a výsledek je dosazen do registru Rn
- offset může nabývat hodnoty -255 až 255
@ adresace offsetem v registru LDR R0, [R1, R2] @ R0 = mem_32[R1 + R2] STR R0, [R1, R2] @ mem_32[R1 + R2] = R0 LDR R0, [R1, R2, LSL #2] @ R0 = mem_32[R1 + R2 * 4] @ adresace offsetem s přímou hodnotou LDR R0, [R1, #4] @ R0 = mem_32[R1 + 4] STR R0, [R1, #4] @ mem_32[R1 + 4] = R0 @ pre-indexová adresace LDR R0, [R1, #4]! @ R0 = mem_32[R1 + 4], R1 = R1 + 4 STR R0, [R1, #4]! @ mem_32[R1 + 4] = R0, R1 = R1 + 4 @ post-indexová adresace LDR R0, [R1], #4 @ R0 = mem_32[R1], R1 = R1 + 4 STR R0, [R1], #4 @ mem_32[R1] = R0, R1 = R1 + 4
Operace nad zásobníkem
Přesuny dat mezi registry a pamětí lze provádět také instrukcemi pro operace nad zásobníkem. Pro uložení obsahu registrů na zásobník slouží instrukce PUSH, vyjmutí ze zásobníku provádí instrukce POP. Při provádění instrukce PUSH, procesor nejdříve odečte hodnotu čtyři od obsahu ukazatele vrcholu zásobníku SP a následně na tuto adresu uloží požadovanou hodnotu. Vyjmutí hodnoty ze zásobníku, které provádí instrukce POP, má opačný průběh. Procesor z adresy vrcholu zásobníku přečte uloženou hodnotu a následně k vrcholu zásobníku přičte hodnotu čtyři.
Na zásobník se vždy ukládá celé 32-bitové slovo, adresy ukazující na zásobník musí být beze zbytku dělitelné čtyřmi. Operandem pro obě instrukce je seznam registrů oddělených čárkou, případně rozsah registrů, uzavřený ve složených závorkách.
PUSH {R0, R3 - R5} @ ulož na zásobník R0, R3, R4, R5 PUSH {R6} @ ulož na zásobník R6 POP {R0 - R2} @ vyjmi ze zásobníku R0, R1, R2 POP {R7} @ vyjmi ze zásobníku R7
Instrukce PUSH a POP mají následující omezení:
- seznam registrů nesmí obsahovat registr SP
- seznam registrů instrukce PUSH nesmí obsahovat registr PC
- pokud je v seznamu registrů instrukce POP registr LR, seznam nesmí obsahovat registr PC
Mezi registry
Přesuny dat mezi registry se provádí instukcí MOV (Move) případně MVN (Move NOT). Instrukce MVN provede logickou negaci všech bitů zdrojového registru a výsledek zapíše do cílového registru. Instrukci MOV lze také použít k naplnění registru přímou hodnotu.
MOV R0, R1 @ R0 = R1 MVN R0, R1 @ R0 = not R1 MOV R0, #21 @ R0 = 21
Přímou hodnotu, která zabere maximálne 16 bitů, lze do registru dosadit také instrukcí MOVW (Move Wide). Pokud potřebujeme naplnit registr přímou hodnotou, která zabere více než 16 bitů, lze pro naplnění horního slova registru použít instrukci MOVT (Move Top).
@ R0 = 0xE000E100 MOVW R0, #0xE100 @ R0[15:0] = 0xE100 MOVT R0, #0xE000 @ R0[31:16] = 0xE000
Druhou variantou je pseudoinstrukce LDR, =hodnota, kterou assembler přeloží jako instrukci pro načtení obsahu paměti relativně k obsahu PC. Použití této pseudoinstrukce zlepšuje oproti kombinaci MOVW, MOVT čitelnost programu.
LDR R0, =0xE000E100 @ R0 = 0xE000E100
Speciální registry
Čtení ze speciálních registrů a zápis do speciálních registrů umožňují instrukce MRS a MSR. Pomocí instrukce MRS přesuneme obsah speciálního registru do obecného registru, instrukce MSR provádí přesun opačným směrem, z obecného do speciálního registru. Přístup ke speciálním registrům je možný pouze v privilegovaném režimu. Výjimkou je čtení registru APSR, které může provádět i neprivilegovaný (user) kód.
MRS R0, IPSR @ R0 = IPSR MSR BASEPRI, R1 @ BASEPRI = R1 MRS R2, APSR @ R2 = APSR