Niniejszy dokument ma na celu zasygnalizowania istnienia architektur innych niż procesory Intela klasy 80x86. Krótki opis trzech z nich ma jedynie przybliżyć potencjalne róznice w programowaniu na maszynach w nie wyposażonych, a nie przedstawić kompleksowe wskazówki do tworzenia kodu w asemblerze (które to można znaleźć w zależności od potrzeb w sieci). Ma zwrócić uwagę na wielość rozwiązań, dzięki którym pewne funkcje systemowe muszą być realizowane (bądź mogą być optymalizowane) w specyficzny dla architektury sposób oraz pokazać zasadność rozdzielenia części kodu Linuxa na katalogi dla poszczególnych z nich.
W
dokumencie zostały przedstawione trzy architektury:
· ARM
· Alpha
· Sparc
Na
każdą z nich istnieje imlementacja systemu Linux.
ARM 2 |
ARM 2 or ARM 3 |
|
|
ARMv3 |
ARM6xx or ARM7xx |
|
|
ARMv4 |
StrongARM |
|
|
ARMv5 |
ARM9, XScale, etc |
|
|
Floating Point |
Hardware or (usually) software floating point, from ARM 2 (software)
to ARM7500FE (mostly hardware). No RISC floating point unit is entirely
hardware, it is a balance between basic FP instructions in hardware, and more
complex things (like RMF) in software. |
|
|
VFP |
Vector Floating Point, in ARMv5 (etc) processors |
|
|
Thumb |
16 bit-ised version of ARM as an option in ARMv5 (etc) processors |
Procesor
może pracować w jednym z czterech trybów.
Różnica pomiędzu IRQ i FIQ polega głównie na tym, że w obsługę FIQ staramy się jak najszybciej zakończyć. Dlatego ma "wycieniowanych" więcej rejestrów. FIQ nie może wywoływać SWI, ani być przerwanym przez IRQ. (IRQ może być przerwany przez FIQ)
Procesor ARM ma właściwie 27 rejestrów,
które są udostępniane w odpowiednich warunkach, ale tak na prawdę możemy używać
najwyżej 16 na raz.
Tabelka poniższa zestawia "używalność"
rejestrów.
User Mode SVC Mode IRQ Mode FIQ Mode APCS
R0 ------- R0 ------- R0 ------- R0 a1
R1 ------- R1 ------- R1 ------- R1 a2
R2 ------- R2 ------- R2 ------- R2 a3
R3 ------- R3 ------- R3 ------- R3 a4
R4 ------- R4 ------- R4 ------- R4 v1
R5 ------- R5 ------- R5 ------- R5 v2
R6 ------- R6 ------- R6 ------- R6 v3
R7 ------- R7 ------- R7 ------- R7 v4
R8 ------- R8 ------- R8 R8_fiq v5
R9 ------- R9 ------- R9 R9_fiq v6
R10 ------ R10 ------ R10 R10_fiq sl
R11 ------ R11 ------ R11 R11_fiq fp
R12 ------ R12 ------ R12 R12_fiq ip
R13 R13_svc R13_irq R13_fiq sp
R14 R14_svc R14_irq R14_fiq lr
------------- R15 / PC ------------- pc
Kolumna APCS zawiera nazwy
używane przez specyficzny mechanizm nazywany ARM Procedure Call Standard (jak
sama nazwa wskazuje – standaryzuje on wywoływanie procedur skompilowanych,
napisanych w różnych językach)
Rejest R15 zbudowany jest
następująco (tryb 26-bitowy)
Bit 31 30 29 28 27 26 25------------2 1 0
N Z C V I F Program Counter S1 S0
Oznaczenie flag:
N Negative - Ustawiany gdy wynik operacji jest negatywny
Z Zero - Ustawiany gdy wynik operacji jest zero
C Carry - Ustawiany gdy wystąpi "carry"
O Overflow - Ustawiany gdy wystąpi przepełnienie
I IRQ - Zablokowanie przerwań
F FIQ - Zablokowanie szybkich przerwań
S1 S0 Tryb
0 0 USR - User mode
0 1 FIQ - Fast Interrupt mode
1 0 IRQ - Interrupt mode
1 1 SVC - Supervisor mode
Jeżeli R15 jest używany jako pierwszy argument instrukcji, zwykle tylko część przechowująca adres jest wykorzystywana. Jednakże instrukcja poniższa spowoduje skopiowanie zawartości PC do R0 i dodanie do nie 256.
ADD R0,
R15, #256
Jeżeli jest używany jako drugi argument, dostępne są wszystkie 32 bity. Poniższy kod pokazuje rozpoznawanie trybu procesora.
MOV R0, #3 ; Load a bit mask (%11) into R0
AND R0, R0, PC ; AND R15 into R0, to get the mode status
CMP R0, #3 ; Compare mode with '3' (SVC)
BEQ svc ; If SVC mode, branch to 'svc'
CMP R0, #2 ; Compare mode with '2' (IRQ)
BEQ irq ; If IRQ mode, branch to 'irq'
CMP R0, #1 ; Compare mode with '1' (FIQ)
BEQ fiq ; If FIQ mode, branch to 'fiq'
CMP R0, #0 ; Compare mode with '0' (USR)
BEQ usr ; If USR mode, branch to 'usr'
To może być także używane do zmiany trybu procesora. NP. na SVC.
MOV R6, PC ; Store original state of PC in R6
ORR R7, R6, #3 ; Set SVC mode
TEQP R7, #0 ; Write mode flags (in R7) to PC
I żeby wrócić do
poprzedniego stanu.
TEQP R6, #0 ; Write previous mode flags (in R6) to PC
Jednakże procesory klasy
powyżej ARM 3 udostępniają możliwość adresowania 32-bitowego poprzez wyrzucenie
PSR (flag procesora) z R15 tak by cały mógł być zajmowany przez adres. Co
prawda RISC OS zwykle pracuje w trybie 26-bitowym (z wyjątkiem kilku
przypadków), ale uzycie trybu 32 wydaje się być konieczne gdy zainstalowane
jest więcej ni 28Mb pamięci.
PSR znajduję się wtedy w specjalnym rejestrze CPSR (oraz SPSR gdzie jest zachowany). Jego budowę przedstawia poniższa tabelka:
31 30 29 28 --- 7 6 - 4 3 2 1 0
N Z C V I F M4 M3 M2 M1 M0
0 0 0 0 0 User26 mode
0 0 0 0 1 FIQ26 mode
0 0 0 1 0 IRQ26 mode
0 0 0 1 1 SVC26 mode
1 0 0 0 0 User mode
1 0 0 0 1 FIQ mode
1 0 0 1 0 IRQ mode
1 0 0 1 1 SVC mode
1 0 1 1 1 ABT mode
1 1 0 1 1 UND mode
Zwykle procesor będzie używał trybów User26, SVC26, IRQ26 i FIQ26 i chociaż możliwe jest wejście w tryb 32 należy robić to bardzo ostrożnie ponieważ RISC OS może nie być na to przygotowany!
SWI jak już było wspomniane
to Software Interrupt. W RISC OS SWI są używane do wywoływania procedur
systemowych lub modułów (napisanych poza sysemem). Aplikacje zwykle używają
modułów by umożliwić nieskopoziomowy zewnętrzy dostęp dla innych aplikacji.
Przykładami SWI są:
Używanie SWI w ten sposób umożliwia systemowi budowę
modularną, co znaczy, że cały kod podzielony jest na wiele mniejszych
wyspecjalizowancyh części.
Kiedy
nadejdzie żądanie wykonania odpowiedniego SWI, SWI handler odnajduje pozycję procedury
i wywołuje ją przekazując dowolne dane. Wywołanie może wyglądać następująco:
SWI &02
jak i
SWI "OS_Write0"
Komputery z RISC OS pracują
na dwóch rodzajach adresów – logicznych i fizycznych. Pamięć logiczna to ta
widziana przez OS i programistę, jako używana. Każda aplikacja zaczyna się od
adresu &8000 i zajmuje pamięć powyżej niego. Pamięć fizyczna jest tą
faktycznie zainstalowaną w maszynie.
W RISC OS pamięć jest podzielona na strony.
W starszych wersjach były one rozmiarów 8/16/32K w zależności od zainstalowanej
pamięci, a teraz uzywa się stałej wartości 4K. I co najciekawsze w nowych
systemach działających na ARM6 i młodszych MMU (Memory Menagment Unit) jest
wbudowany w procesor. Zawiera on TLB (Translation Look-aside buffer - zawiera
64 przetłumaczone adresy) oraz mechanizmy kontroli dostępu (16 zdefiniowanych
"domen" z wyspecyfikowanymi prawami dostępu) i przechodzenia tablicy
translacji (tłumaczenie adresu wirtualnego na fizyczny).
Co ciekawe, zarówno kod i jaki dane (z
małymi wyjątkami oczywiście) kompilator próbuje zawsze wyrównać do adresów
32-bitowych, tak aby ułatwić dekompilację dowolnego fragmentu pamięci
(instrukcje mają 32-bitowe kody).
Schemat wielozadaniowości zastosowany w RISC OS jest relatywnie prosty i jasny. Aplikacja która otrzyma dostęp do procesora urzywa go tak długo jak chce, aż do zakończenia lub jawnej rezygnacji na rzecz systemu operacyjnego. Istnieją jednak implementacje (nie tylko Linux), które potrafią symulować inne modele kooperacji.
Informacje i przykłady zostały zaczerpnięte ze strony:
Systemy
Alpha posiadają dwa zasadniczo różniące sie typy rejestrów:
·
Integer registers
·
Floating-point
registers
Asemblerowych instrukcjie mają jednoznacznie
określone jakiego typu rejestów oczekują jako argumentów i wymienianie ich nie
jest możliwe.
Jest
to zestaw 32-óch 64-bitowych rejestrów o nazwach $0 - $31 (chociaż właściwie
używane może być tylko 31, bo $31 zawsze ma wartość 0). Niektóre z nich są
czasem odpowiednikami rejestrów "generalnych" występujących w innych
architekturach. Po dołączeniu odpowiedniej biblioteki (#include
<alpha/regdef.h>
) można
oprócz nazw numerycznych używać specyficznych alisów dla rejestrów (oprócz $28,
$29, $30, którye system operacyjny i asembler rezerwuje dla swoich celów).
Rejestry można stosować praktycznie dowolnie wymiennie oprócz $30, który jest
uzywany jako wskaźnik stosu przez specyficzne instrukcje PALcode (Privileged
Architecture Library code). Poniższa tabela opisuje zwykłe znaczenie
odpowiednich rejestrów.
Nazwa |
Nazwa programowa ( regdef.h) |
Używany jako |
|
|
Przechowywanie
obliczeń wyrażeń i wartości funkcji. Nie jest zachowywany przy
wywoływaniu procedury. |
|
|
Tymczasowe wartości obliczanych wyrażeń. Nie są
zachowywane przy wywoływaniu procedury. |
|
|
Rejestry zachowywane przy wywoływaniu procedur. |
|
|
W razie potrzeby zawiera "frame pointer". Jeśli
nie traktowany jest jak rejestr zachowywany. |
|
|
Używane do przekazywania pierwszych 6-ciu argumentów do
wywoływanych procedur. Nie są zachowywane. |
|
|
Tymczasowe wartości obliczanych wyrażeń. Nie są
zachowywane przy wywoływaniu procedury. |
|
|
Adres
powrotu (return address). Oczywiście zachowywany. |
|
|
Zawiera wartość procedury (procedure value), a także
używany do wyliczania wyrażeń. Nie zachowywany. |
|
|
Zarezerwowany dla asemblera. Nie zachowywany. |
|
|
Wskaźnik globalny (global pointer). Nie zachowywany. |
|
|
Wskaźnik stosu (stack pointer). Zachowywany. |
|
zero |
Zawsze ma wartość 0. |
Tych także mamy 32 64-bitowe rejestry. Każdy z nich może przechowywać liczbę
zmiennoprzecinkową pojedyńczej (32 bity) lub podwójnej (64-bity) precyzji. Są
nawywane kolejno od $f0 to $f31 (z czego znów ostatni nie jest używany, bo
zawsze musi mieć wartość 0.0). Poniżej opis znaczenia poszczególnych z nich.
Nazwa |
Używany jako |
|
Przechowywanie obliczeń wyrażeń i wartości funkcji zmienno
przecinkowej ($f0) albo zespolone ($f0 część rzeczywista, $f1 część urojona).
Nie jest zachowywany przy
wywoływaniu procedury. |
|
Rejestry zachowywane przy wywoływaniu procedur. |
|
Tymczasowe wartości obliczanych wyrażeń. Nie są
zachowywane przy wywoływaniu procedury. |
|
Używane do przekazywania pierwszych 6-ciu argumentów
(zmiennoprzecinkowych pojedyńczej lub podwójnej precyzji) do wywoływanych
procedur. Nie są zachowywane. |
|
Tymczasowe wartości obliczanych wyrażeń. Nie są
zachowywane przy wywoływaniu procedury. |
|
Zawsze ma wartość 0.0. |
·
Load quadword (ldq
)
·
Store quadword (stq
)
·
Load longword (ldl
)
·
Store longword (stl
)
·
Load word (ldw
)
·
Store word (stw
)
·
Load word unsigned (ldwu
)
·
Unaligned load quadword (uldq
)
·
Unaligned store quadword (ustq
)
·
Unaligned load longword (uldl
)
·
Unaligned store longword (ustl
)
·
Unaligned load word (uldw
)
·
Unaligned store word (ustw
)
·
Unaligned load word unsigned (uldwu
)
·
Load byte (ldb
)
·
Store byte (stb
)
·
Load byte unsigned (ldbu
)
System
Alpha generuje niektóre wyjątki bezpośrednio, a niktóre jako wynik pewnych
testów wpisanych w aseblerze. Poniżej opisane są cześciej występujące
·
Address error
exceptions – występuje gdy podany adres jest nieprawidłowy w kontekście
aktualnego procesu, zwykle gdy wystąpi odwołanie do niedopasowancyh danych
(unproperly aligned)
·
Overflow exceptions –
brak miejsca na zapisanie precyzji wyniku operacji arytmetycznej.
·
Bus exceptions – nieprawidłowy adres pamięci.
·
Divide-by-zero exceptions – dzielenie przez
zero.
·
Invalid operation exceptions:
o
Odejmowanie
nieskończoności, np. (+INF) - (+INF).
o
Mnożenie 0 przez
+/-INF.
o
Dzielenie 0 przez 0 lub
+/-INF przez +/-INF.
o
Zmiana liczby na format
integer (cvttq)
powoduje przepełnienie, albo argumentem była
nieskończoność lub NaN (not-a-number)
o
Operacje sygnalizujące
wynik NaN.
·
Divide-by-zero
exceptions – dzielenie przez zero.
·
Overflow exceptions –
wynik przekracza możliwości formatu
·
Underflow exceptions –
wynik traci dokładność, albo jest pomiędzi +/-2Emin (minimum expressible exponent).
·
Inexact exceptions –
wynik z nieskończoną precyzją różni się od zaokrąglonego.
Informacje zaczerpnięte ze strony:
http://h30097.www3.hp.com/docs/base_doc/DOCUMENTATION/V51A_HTML/ARH9LBTE/TITLE.HTM
1. Okna rejestrów
Architektura Sparc firmy Sun
Microsystem ma 32 rejestry ogólnego zastosowania widoczne dla programu przez
cały czas. 8 z nich stanowi rejestry globalne (global) a pozostałe 24 tworzą
okna (windows). Rejestry okien podzielone są na trzy grupy po 8 rejestrów
nazywane wejściowe, lokalne i wyjściowe (in, local, out). W zależności od
implementacji (rodzaju chip'a) liczba okien wynosi od 2 do 32 (zwykle 7 lub 8).
W danym momencie tylko jedno
okno jest widzialne (dostępne) na co wskazuje CWP (current window pointer),
który jest częścią PSR (processor status register). Jest to pięciobitowa
liczba, która jest zwiększana i zmniejszana przez intrukcje SAVE i RESTORE. Są
to intrukcje wywłania i powrotu z procedury. Ideą jest by rejestry wejściowe
zawierały parametry wejściowe dla procedury, a rejestry wyjściowe przekazywały
wynik. Lokalne mają być wykorzystwywane do wewętrznych obliczeń, a globalne do
trzymania danych na zewnątrz wywołań. Rejestry okna nakładają się w ten sposób
by wyjściowy po wykonaniu SAVE stał się wejściowym, po to aby w ciągu wywołań i
powrotów zredukować ruch w pamięci.
Idea jednak okazała się
niezbyt fortunna. Okazało się, że i tak często występuje konieczność odkładania
rejestrów na stos i przywracania ich stanu później. Pomyłka spowodowana była
ograniczonymi symulacjami (jedynie dla izolowanych programów i kompilowanych ze
słabą optymalizacją. Co więcej nowsze wersje ze względu na wsteczną
kompatybilność nie mogą usunąć tej cechy, co przyspożyło producentom wielu
problemów przy projektowaniu nowocześniejszych wersji procesora takich jak
SuperSparc).
Grupa rejestrów |
Mnemoni |
Adresy rejestrów |
global |
%g0-%g7 |
r[0]-r[7] |
out |
%o0-%o7 |
r[8]-r[15] |
local |
%l0-%l7 |
r[16]-r[23] |
in |
%i0-%i7 |
r[24]-r[31] |
Na
poniższy rysunku została zilustrowana idea nakładania się rejestrów w
implementacji z 8 oknami. Okna są tu ponumerowane od 0 do 7 (w0-w7). Składją
się z 24 rejestrów z czego 16 dzielonych jest pomiędzy sąsiednie okna. Zwykły
przypadek zmiany aktywnego okna to wywołanie SAVE lub RESTORE. Rzadziej RETT
(return from trap) lub obsługa zdarzenia pułapki (przerwanie, wyjątek lub
intrukcja TRAP).
|
Na
rysunku również zaznaczony jest rejestr WIM (window invalid mask). Jest to mapa
bitowa z zaznaczonym zawsze tylko jednym bitem pokazującym, które okno jest
pierwszym zajętym. Okna rejestrów zostały wymyślone do wpomogania wywoływania
procedur czyli są takim jakby cache'wym stosem. WIM wskazuje ile jeszcze
wywołań może być jeszcze wykonanych bez zapisywania danych do pamięci. Na
rysunku cała pojemność okien jest już wykorzystana i następne wywołanie
procedury spowoduje wywołanie "pułapki" Window Overflow. Z drugiej
strony występuje Window Underflow, kiedy "cache" jest pusty i trzeba
pobrać dane z pamięci.
Oprócz tego Sparc zawiera
rekomendację co do programowego używania rejestrów zapisaną w dokumentacji
Sparc ABI (application binary interface) a także w wielu innych miejscach (jak
np. pliki nagłówkow, dokumentacja kompilatora).
Poniżej
znajdue się tabelka opisująca typową zawartość rejestrwów:
%g0 (r00) always zero %g1 (r01) [1] temporary value %g2 (r02) [2] global 2 global %g3 (r03) [2] global 3 %g4 (r04) [2] global 4 %g5 (r05) reserved for SPARC ABI %g6 (r06) reserved for SPARC ABI %g7 (r07) reserved for SPARC ABI %o0 (r08) [3] outgoing parameter 0 / return value from callee %o1 (r09) [1] outgoing parameter 1 %o2 (r10) [1] outgoing parameter 2 out %o3 (r11) [1] outgoing parameter 3 %o4 (r12) [1] outgoing parameter 4 %o5 (r13) [1] outgoing parameter 5 %sp, %o6 (r14) [1] stack pointer %o7 (r15) [1] temporary value / address of CALL instruction %l0 (r16) [3] local 0 %l1 (r17) [3] local 1 %l2 (r18) [3] local 2 local %l3 (r19) [3] local 3 %l4 (r20) [3] local 4 %l5 (r21) [3] local 5 %l6 (r22) [3] local 6 %l7 (r23) [3] local 7 %i0 (r24) [3] incoming parameter 0 / return value to caller %i1 (r25) [3] incoming parameter 1 %i2 (r26) [3] incoming parameter 2 in %i3 (r27) [3] incoming parameter 3 %i4 (r28) [3] incoming parameter 4 %i5 (r29) [3] incoming parameter 5 %fp, %i6 (r30) [3] frame pointer %i7 (r31) [3] return address - 8 [1] wywołujący zakłada, że wartość się zmieni podczas wykonywania procedury [2] nie powinien być wykorzystywany w bibliotekach SPARC ABI [3] wywołujący zakłada, że wartość zostanie zachowana podczas wykonywania procedury |
Informacje zaczerpnięte ze strony: