Programowanie w asemblerze Uwagi o ARM 17 stycznia 2017
Organizacja pamięci Trzy możliwości Dostęp bezpośredni do pamięci fizycznej, brak zarzadzania. Używane w mikrokontrolerach. MPU (Memory Protection Unit): podział pamięci na regiony, ochrona dostępu (uprawnienia). MMU (Memory Management Unit): pamięć wirtualna.
Architektura wersja 5 v5t: nadzbiór ARMv4T. Nowe instrukcje: BLX, CLZ i BKPT. v5te Nowe instrukcje do cyfrowego przetwarzania sygnałów. Nowe instrukcje mnożenia dla DSP: SMULxy, SMLAxy, SMULWy, SMLAWy i SMLALxy. Arytmetyka z nasyceniem: flaga Q, instrukcje QADD, QSUB, QDADD i QDSUB. Pre-load hinty dla ładowania z pamięci. v5tej: przyśpieszenie sprzętowe dla języka Java.
Architektura wersja 6 v6 Obsługa danych mixed endian : SETEND, REV, REV16, REVSH. Ponad 60 nowych instrukcji SIMD, np. SMUSD, SMUADX, USAD8, USADA8. Ładowanie danych niewyrównanych. Nowe instrukcje synchronizacji: LDREX, STREX. v6t2 z Thumb-2: rozszerzona, kompletna wersja Thumb. Zwiastun nowej architektury Cortex dla profilu wbudowanego ARMv6M.
ARMv6 Instrukcje PKHBT i PKHTB do pakowania 16-bitowych liczb w jeden rejestr wynikowy (z dwóch rejestrów źródłowych). Mini-operacje wektorowe: dodawanie/odejmowanie par liczb 16-bitowych i czwórek liczb 8-bitowych, np. SADD16, USUB8. Te same operacje z odpowiednim nasyceniem, np. QADD8. Nasycanie do podanej liczby bitów SSAT r3,#8,r2 USAT r3,#12,r2,lsl #3 Instrukcje zamiany kolejności bajtów dla całego rejestru i dla dwóch połówek 16-bitowych: REV i REV16. Wybór endianness dla przesyłania danych SETEND BE SETEND LE
ARMv6 Nowe bity w rejestrze stanu: GE[3:0]: Dla SIMD, flaga większy-lub-równy dla każdego 8/16-bitowego wycinka. E: Aktualny ustawienie endianness, można zmieniać przez SETEND. A: maskowanie imprecise data abort exceptions
Tasowanie Trzy nowe instrukcje (wszystkie z wykonaniem warunkowym): REV r1,r2 Odwraca kolejność bajtów. REV16 r1,r2 Odwraca kolejność bajtów w pierwszej i drugiej parze. REVSH r1,r2 Zamienia miejscami dwa dolne bajty, po czym rozszerza bit znaku.
Synchronizacja Dwie nowe instrukcje: LDREXww r1,[r2] Ładuje z pamięci do rejestru, po czym ustawia monitor obserwujacy ten adres. STREXww r0,r1,[r2] Zapisuje r1 do pamięci i zwraca w r0 sukces, jeśli w międzyczasie nie było innych zapisów ani odczytów.
Architektura wersja 7 Nazwana Cortex core (wszystkie poprzednie to ARM core ). v7a, v7r Dynamic Compiler Support. Execution Environment (Thumb-2EE). VFP v3 (Vector Floating Point). NEON advanced SIMD. Thumb-2 obowiazkowo. v7m Minimalna wersja dla zastosowań wbudowanych. Tylko instrukcje Thumb-2.
Architektura wersja 7 Uniwersalny asembler, tłumaczacy podobno na oba zestawy bazowych instrukcji Technologia NEON to 64/128-bitowa zaawansowana architektura SIMD do przyśpieszenia aplikacji multimedialnych i DSP. Daje dla nich co najmniej 3-krotne przyśpieszenie względem ARMv5 i dwukrotne względem ARMv6.
Zestawy instrukcji Bazowy 32-bitowy zbiór instrukcji ARM Ograniczony 16-bitowy zbiór Thumb (mała zajętość pamięci) Nowy mieszany 16/32-bitowy zestaw Thumb-2 Jazelle DBX do bajtkodów Javy Zestaw NEON do 64/128-bitowego SIMD Zestaw VFP do wektorowego przetwarzania liczb zmiennopozycyjnych.
Dodatkowe opcje/zestawy instrukcji TrustZone: podział procesora na dwie części: zaufana i nie. Jazelle: opkody dla maszyny Javy, deprecated. SIMD: wiadomo. Proste operacje. NEON: advanced SIMD. VFP: (wektorowa) arytmetyka rzeczywista. CRYPTO: elementarne wsparcie dla kryptografii
NEON Termin NEON oznacza osobny zestaw instukcji do Advanced SIMD, dodawany do bazowego zestawu. Używa koprocesorów 10 i 11, tych samych co VFP. Jeśli oba rozszerzenia obecne, to dziela rejestry, ponadto VFP zyskuje dodatkowe. 32 rejestry 64-bitowe: d0 d15 Można je łaczyć w pary, otrzymujac 16 rejestrów 128-bitowych q0 q15.
NEON: instrukcje W asemblerze GNU trzeba podać opcję -mfpu=neon ładowanie kilku rejestrów z pamięci vld1.8 d0.d1.d2,[r0] z przeplotem vld3.8 d0.d1.d2,[r0] zapis analogicznie (vst).
NEON: instrukcje Dodawanie wektorowe vadd.i32 q0,q0,q0 Dla C zdefiniowano intrinsics: #include <arm_neon.h> uint32x4_t double_elements (uint32x4_t input) { return vaddq_u32(input, input); }
Wsparcie dla kryptografii Elementarne instrukcje, do prostych zastosowań. 2 instrukcje encode/decode dla AES, działaja na 128-bitowych rejestrach Advanced SIMD. Wsparcie dla SHA-1 i SHA-256 Running hash trzymany w 2 128-bitowych rejestrach. Instrukcje haszujace w jednym kroku po 4 nowe słowa danych. Instrukcje do przyśpieszenia generowania kluczy.
Architektura wersja 8 Wyraźny podział na profile: ARMv8-A application profile: platformy obliczeniowe, wydajność. ARMv8-R real-time profile: wbudowane aplikacje o określonym czasie reakcji (samochody, sterowanie przemysłowe). ARMv8-M embedded profile: mikrokontrolery itp.
Application Profile ARMv8-A 32 bity i 64 -bity 3 zestawy instrukcji: A32, T32 i A64. Pamięć wirtualna Mocne systemy operacyjne
Real-time Profile ARMv8-R 32 bity 2 zestawy instrukcji: A32 i T32s Protected memory system (pamięć wirtualna opcjonalnie) Zoptymalizowany na systemy czasu rzeczywistego
Microcontroller Profile ARMv8-M 32 bity Tylko zestaw instrukcji T32/Thumb Protected memory system??? Zoptymalizowany na mikrokontrolery
AArch64: organizacja 31 uniwersalnych rejestrów 64-bitowych (X0 X30) Licznik rozkazów (PC) i wskaźnik stosu (SP) nie sa rejestrami uniwersalnymi Dedykowany rejestr z zerem dostępny dla większości instrukcji. 32- lub 64-bitowe argumenty instrukcji Duża pamięć wirtualna, adresy 64-bitowe (w teorii)
AArch64: organizacja Wszystkie rejestry wektorowe 128-bitowe: Vx[127:0] Zmiennopozycyjna arytmetyka skalarna używa dolnych 64 (double) lub 32 (single precision) bitowe Zgodnośc z IEEE: tryby zaokraglania, denormalizacja, NaN. MMU: tylko 48 bitów adresu. Górne 8 wolne np. na tagged pointers.
AArch64: organizacja 4 poziomy uprzywilejowania (wyjatków), EL3 najbardziej uprzywilejowany EL3: EL2: EL1: EL0: (TrustZone) Monitor Virtual Machine Monitor lub nic Guest OS lub Secure WorldOS App lub Trusted App Każdy poziom ma osobna tablicę wektorów, wektor dla każdego typu: synchroniczne, IRQ, FIQ, System Error.
Narzędzia ARM Software Development Toolkit (SDT) lekko przestarzały ARM Developer Suite (ADS) też RealView Compiler Tools (RVCT) RealView Development Suite (RVDS) Inne firmy: Keil (obecnie w ARM), Green Hills i Metrowerks.
Narzędzia Kompilator C z ARM Developer Suite version 1.1 (ADS1.1) to armcc: armcc -c -o test.o test.c fromelf -text/c test.o > test.txt Jest też asembler aasm. arm-elf-gcc to kompilator GNU: arm-elf-gcc -fomit-frame-pointer -c -o test.o test.c arm-elf-objdump -d test.o > test.txt
Uwagi ogólne C char jest unsigned, bo takie ładowanie z pamięci Unikać typów char i short dla liczników pętli, bo trzeba w kodzie ręcznie badać zakresy (rejestry tylko 32-bitowe, więc brak sygnalizacji przepełnienia/przeniesienia)
Dzielenie Ponieważ dzielenie jest symulowane programowo, należy go unikać. Instrukcja C current = (current + increment) % size; zajmuje 50 cykli. Natomiast poniższy kod current += increment; if (current >= size) current -= size; zajmuje podobno tylko 3 cykle (dla armcc).
Liczby rzeczywiste Standardowo brak sprzętowych liczb zmiennopozycyjnych, symulowane programowo W ARM7500FE jest Floating Point Accelerator (FPA). Jest też Vector Floating Point (VFP) akcelerator.
Asemblacja Asembler ARM to firmowo armasm. Użycie armcc -c main.c armasm proc.s armlink -o main.axf main.o square.o ARM ostatnio wprowadził nowa wersję składni, tzw. UAL (Unified Assembler Language), obejmujac a zarówno ARM jak i Thumb i zdejmujac a pewne ograniczenia na kolejność modyfikatorów instrukcji.
Składnia Dyrektywy ALIGN używa się, żeby wyrównać do granic 4 bajtów, np. po napisie (ciagu znaków) DEFB służy do definiowania ciagów bajtów (także stringi) DEFW służy do definiowania słów (4 bajty).
C API Konwencje użycia rejestrów: ARM-Thumb Procedure Call Standard (ATPCS) r0 r3 (a1 a4): argumenty/wartości funkcji, kolejne argumenty na stosie, nie trzeba zachowywać r4 r8 (v1 v5): rejestry dla zmiennych, trzeba je zachowywać i odtwarzać r9 (v6, sb): rejestr dla zmiennych, w position independent kodzie adres bazy statycznej, adres bazy stosu (przy kontroli), trzeba zachowywać r10 (v7, sl): rejestr dla zmiennych, adres ograniczenia stosu (przy kontroli), trzeba zachowywać r11 (v8, fp): rejestr dla zmiennych, dawniej frame pointer, trzeba zachowywać r12 (ip): scratch register, nie trzeba zachowywać
C API Jeśli funkcja ma więcej niż 4 argumenty, warto próbować część z nich łaczyć w struktury. Argumenty 64-bitowe (long long, double) przekazywane w parach rejestrów. Zwracane w <r0,r1>.
Przykład ;; Hello World Version 2 B main hello DEFB "Hello World\n\0" goodbye DEFB "Goodbye Universe!\n\0" ALIGN main ADR R0,hello ;get the start address of ;the "Hello World" string SWI 3 ;print the message ADR R0,goodbye ;point at the goodbye string SWI 3 ;print the message SWI 2 ;stop the program
Inny przykład ;; Increment R0 until it reaches same value as in R1 ;; Then print a success message B fred ;nothing special about "main"! four DEFW 4 success DEFB "Register 0 has reached the value of \0" ALIGN fred LDR R1,four MOV R0,#1 next CMP R0,R1 BNE skip ADR R0,success SWI 3 MOV R0,R1 SWI 4 MOV R0,#10 SWI 0 SWI 2 skip ADD R0,R0,#1 B next ;LDR loads R1 with *contents* of location four ;put the value 1 (decimal) into R1 ;does R0 now have same number in it as R1 does? ;get start address of success message ;print the message ;move value from R1 into R0 for printing ;print the decimal value that is now in R0 ;stop the program
Przekazywanie parametrów w kodzie Rejestr łacz acy upraszcza przekazywanie parametrów bezpośrednio w kodzie BL Copy DCD BufferLength ;długość w bajtach DCD Buffer1 ;adres początkowy DCD Buffer2 ;adres początkowy Po wywołaniu rejestr łacz acy zawiera adres poczatku bloku parametrów. Procedura (po ewentualnym zachowaniu rejestrów) pobiera parametry równocześnie ustawiajac rejestr łacz acy na właściwy adres Copy LDR R0,[LR],#4 LDR R1,[LR],#4 LDR R2,[LR],#4
Parametry na stosie (1) AREA.text,CODE,READONLY EXPORT sumof ; int sumof(int N,...) n RN 0 ;pierwszy parametr sum RN 1 ;suma (zainicjowana) sumof SUBS n,n,#1 MOVLT sum,#0 SUBS n,n,#1 ADDGE sum,sum,r2 SUBS n,n,#1 ADDGE sum,sum,r3 MOV r2,sp ;gdy 0 elementów ;gdy jest drugi element ;gdy jest trzeci element ;do chodzenia po stosie
Parametry na stosie (2) loop SUBS n,n,#1 LDMGEFD r2!,{r3} ADDGE sum,sum,r3 BGE loop MOV r0,sum MOV pc,lr END ;gdy jest kolejny element
Wykonanie warunkowe Następujacy kod w C if (c == a ) c = e c = i c == o ) licznik++; może być zapisany bez instrukcji skoku TEQ r1,# a TEQNE r1,# e TEQNE r1,# i TEQNE r1,# o ADDEQ r2,r2,#1 ;r1=c ;r2=licznik
Wykonanie warunkowe Zliczania liter w kodzie ASCII if ((c >= A && c <= Z ) (c >= a && c <= z ) licznik++; można dokonać następujaco (używajac porównań dla liczb bez znaku) SUB r3,r1,# A CMP r3,# Z - A SUBHI r3,r1,# a CMPHI r3,# z - a ADDLS r2,r2,#1
Pakowanie małych liczb Małe liczby całkowite można często pakować parami do rejestrów, na przykład kod w C short index,increment;... next = table[index]; index += increment; można zapisać następujaco LDRB r2,[r4,r3,lsr #16] ;r2 = next ADD r3,r3,r3,lsl #16 ;r3 = index increment gdzie to rejestr bazowy r4 zawiera adres tablicy table, zaś indeks bieżacy i krok sa upakowane w r3.
Pola bitowe Do wydobycia pola bitowego można oczywiście użyć maski wraz z instrukcja AND Ale można też inaczej, np. dla bitów 4 8 z r1 MOV r2,r1,lsl #24 MOV r2,r2,lsr #28 Jeśli liczba ma być ze znakiem, to w drugiej instrukcji należy użyć ASR.