Asembler w kodzie linuxa | ||
---|---|---|
<<< Wstecz | Język niskopoziomowy dla procesorów SPARC | Dalej >>> |
Wyróżniamy trzy najważniejsze dyrektywy: .data, .text i .word. Dwie pierwsze (.data i .text) służą do wyznaczenia sekcji kodu i deklaracji zmiennych. Ostatnia (.word) daje możliwość zaalokowania i zainicjalizowania pamięci dla zmiennej. Jeśli chcemy podać wartości w systemie szesnastkowym, to poprzedzamy je "0x".
.data ! start a group of variable declarations x: .word 23 ! int x = 23; y: .word 0x3fce ! int y = 0x3fce; z: .word 42 ! int z = 42; |
SPARC dostarcza nam trzydzieści dwa rejestry ogólnego przeznaczenia. Każdy z nich jest 32-bitowy. Rejestry nazywają się następująco:
%r0, ..., %r31
%g0, ..., %g7 (inaczej %r0, ..., %r7)
%o0, ..., %o7 (inaczej %r8, ..., %r15)
%l0, ..., %l7 (inaczej %r16, ..., %r23)
%i0, ..., %i7 (inaczej %r24, ..., %r31)
Ważną rzeczą o której wspomnieć należy już na samym początku jest fakt, że rejestr %g0 (%r0) jest zawsze zerem. Jeśli jakaś instrukcja wykorzysta go jako rejestr przeznaczenia, to wynik zostanie porzucony.
Operacja ta służy do załadowania 32-bitowej liczby całkowitej do wskazanego rejestru.
set 0x42, %r2 set x, %r3 |
Operacja pierwsza załaduje wartość 0x42 do rejestru %r2. Druga natomiast do rejestru %r3 wprowadzi adres zmiennej x.
SPARC przyjął następującą składnie dla instrukcji: rejestr podany na końcu jest rejestrem przeznaczenia, a wszystkie wcześniejsze są argumentami tej instrukcji.
Bardzo charakterystyczną cechą SPARC jest to, że opiera się on na architekturze load/store. Oznacza to, że WSZYSTKIE operacje na danych muszą odbywać się w rejestrach.
W szczególności oznacza to, że jeśli w dwóch rejestrach mamy adresy do liczb, to nie możemy ich tak po prostu dodać. Najpierw trzeba korzystając z instrukcji load(ld) przepisać te liczby do wolnych rejestrów, wykonać obliczenie, a na koniec za pomocą instrukcji store(st) wysłać wynik w odpowiednie miejsce w pamięci.
ld [%r2], %r3 ! do rejestru %r3 wpisujemy to co znajduje się pod adresem w %r2 st %r3, [%r4] ! zawartość %r3 wysyłamy do pamięci wskazywanej przez %r4 |
SPARC dostarcza nam dwie instrukcje dla każdej z tych operacji. Obydwie jako pierwszy argument i przeznaczenie przyjmują rejestr, ale jako drugi argument mogą przyjmować zarówno rejestr, jak i liczbę (small constant value - oznacza to, że jej reprezentacja musi się zmieścić w 13-tu bitach). Obliczenia są przeprowadzane w arytmetyce 32-bitowej.
Poniższy przykład oblicza następujące wyrażenie: a = (a+b) - (c-d).
.data a: .word 0x42 b: .word 0x43 c: .word 0x44 d: .word 0x45 .text start: set a, %r1 ld [%r1], %r2 ! $a$ --> %r2 set b, %r1 ld [%r1], %r3 ! $b$ --> %r3 set c, %r1 ld [%r1], %r4 ! $c$ --> %r4 set d, %r1 ld [%r1], %r5 ! $d$ --> %r5 add %r2, %r3, %r2 ! $a+b$ --> %r2 sub %r4, %r5, %r3 ! $c-d$ --> %r3 sub %r2, %r3, %r2 ! $(a+b)-(c-d)$ --> %r2 set a, %r1 st %r2, [%r1] ! $(a+b)-(c-d)$ --> a end: ta 0 |
Operacja ta umożliwia przeniesienie wartości przechowywanej w jednym rejestrze, do drugiego. Ma ona prostą postać:
mov %r1, %r2 ! przeniesienie wartości z %r1 do % r2 mov 5, %r2 ! robi to samo co set 5, %r2 |
Rejestry warunkowe w SPARC składają się z czterech bitów: Z(zero), N(negative), C(carry), V(overflow). Standardowe operacje arytmetyczne nie zmieniają tych flag. Istnieje natomiast specjalny zestaw operacji służący ich ustawianiu (m.in. cmp - compare)
Korzystając z tych flag możemy poruszać się po kodzie. Służą do tego operacje nazywane Branch (w INTEL-u to po prostu Jump) Podstawowe z nich to ba - branch always; be - branch if equal, bg - branch if greater.
<<< Wstecz | Spis treści | Dalej >>> |
Język niskopoziomowy dla procesorów SPARC | Początek rozdziału | Okna rejestrów |