Asembler x86/Zmienne/GNU AS
Segmenty
[edytuj]W asemblerze GNU mamy następujące segmenty:
- text
- kod
- data
- dane
Aby zdefiniować segment należy w tym celu użyć dyrektywy . Oto schemat użycia:
.nazwa
Przy czym w pole nazwa wpisujemy jedną z powyższych nazw.
Zmienne
[edytuj]Tworzenie zmiennych
[edytuj]Aby utworzyć nową zmienną należy użyć schematu:
nazwa: .typ wartość
W polu nazwa wpisujemy nazwę dla naszej zmiennej (pole to jest opcjonalne); w polu typ wpisujemy typ zmiennej; zaś w polu wartość wpisujemy startową wartość dla naszej zmiennej. W celu ułatwienia adresowania zmienne należy deklarować wewnątrz sekcji danych. Oto przykład:
.data
liczba: .string "\10\18\3"
Pamiętając o tym, że w czystym asemblerowym kodzie bez specjalnych dyrektyw to co jest niżej w kodzie jest dalej w pamięci oraz pliku wykonywalnym, możemy się odwoływać do 2 pozostałych zmiennych bez nazw dodając do adresu liczby ich przesunięcie względem niej (więcej w podrozdziale Adresowanie).
Grupę zmiennych do których odwołujemy się przy użyciu tej samej nazwy nazywamy tablicą i w niektórych przypadkach łańcuchem. Tablica jest również łańcuchem, gdy poszczególne jej elementy to znaki ASCII. Aby utworzyć ciąg takich znaków możemy napisać, przy użyciu nabytej właśnie wiedzy:
.data
lancuch: .string "napis$"
Jak z pewnością zauważyłeś na końcu naszego łańcucha stoi $. Gdy znamy z góry długość naszego łańcucha jest on zbędny, lecz gdy korzystamy z zewnętrznych funkcji, nie znają one tej długości. Problem rozwiązano właśnie poprzez kończenie każdego łańcucha dolarem. Każda funkcja przetwarzając nasz łańcuch (np. funkcja API wyświetlająca nasz łańcuch) kończy zabawę z naszym łańcuchem, gdy napotka dolar. Niektóre funkcje jako symbol końca łańcucha traktują inne wartości.
Systemy liczbowe
[edytuj]Do tej pory używaliśmy jedynie stałych zapisywanych w systemie dziesiętnym. Aby zaznaczyć, że dana liczba jest zapisana w innym systemie liczbowym musimy dodać odpowiedni przyrostek lub przedrostek:
- system szesnastkowy - przedrostek 0x lub przyrostek h albo H. W przypadku zastosowania przyrostka należy dodać 0 na początku naszej liczby, jeśli pierwszy jej znak to litera, a nie cyfra.
- system dziesiętny - bez przedrostków/przyrostków.
- system ósemkowy - przyrostek o, O, q lub Q.
- system binarny - przyrostek b, B, y lub Y.
Przykłady: Przykłady:
liczba = 0xFFliczba = FFH#źle, pierwszy znak to litera, brakuje zera liczba = 0FFH #to poprawny zapis szestnastkowy liczba = 18 liczba = 3Q liczba = 1010Y
Rozmiary zmiennych
[edytuj]. - zmienna
Adresowanie
[edytuj]Aby odnieść się do konkretnego adresu w pamięci możemy użyć tak jak to robiliśmy do tej pory nazwy symbolicznej lub też konkretnej liczby albo adresu zawartego w dowolnym rejestrze. Aby oświadczyć, że dana wartość ma być traktowana jako offset (przesunięcie względem początku segmentu) musimy umieścić ją między nawiasami okrągłymi. Oto przykłady, które powinny zobrazować zagadnienie:
movb %ds:($4), %eax # do eax kopiowana jest wartość spod offsetu 4 w rejestrze DS
movb $zmienna, %ecx # do ecx kopiowana jest wartość spod adresu wskazywanego przez zdefiniowaną nazwę symboliczną
movb (%eax), %edx # do edx kopiowana jest wartość spod offsetu przechowywanego w EAX w segmencie DS
W przypadku użycia konkretnych liczb definiujemy o który segment nam chodzi, gdy używamy nazwy symbolicznej segment zależny jest od miejsca definicji naszej zmiennej, zaś skąd wiemy z którego segmentu będzie czytać procesor w przypadku korzystania z rejestrów, tak jak w ostatnim przypadku? We wszystkich przypadkach procesor odnosi offset względem segmentu DS chyba że rejestrem adresującym jest EBP lub ESP, gdyż w ich przypadku procesor odnosi offset względem segmentu SS. Oto przykłady:
movb %edx, %eax # do eax kopiowana jest wartość spod adresu DS + EDX
movb %ebp, %ecx # do ecx kopiowana jest wartość spod adresu SS + EBP
movb %ebp, %edx # pod adres DS + EDX kopiowana jest wartość ebp
Między nawiasami można stosować przesunięcie i skalowanie. Operator przeniesienia może być stosowany tylko z liczbami 8-, 16- i 32-bitowymi, zaś operator skalowania może być użyty w wyrażeniu tylko jeden raz oraz współczynnikiem skalowania może być tylko 2, 4 lub 8. Oto przykłady
movb 10(%edx), %eax # do eax kopiowana jest wartość spod adresu DS + EDX + 10
movb %eax, (, %ebp, 2) # pod adres SS + 2EBP kopiowana jest wartość rejestru eax
movb 4(%eax, %ecx), %ebx # do ebx kopiowana jest wartość spod adresu DS + EAX + 4 + ECX
movb (%eax,%ecx,8),%ebx # do ebx kopiowana jest wartość spod adresu DS + EAX + 8ECX
movb (,%eax,3), %ebx # źle, nie wolno skalować przez 3!
A co jeśli do obliczenia adresu zastosujemy jednocześnie rejestr EBP (który odnosi się względem segmentu SS) oraz np. rejestr EAX (który odnosi się do segmentu DS)? W tym przypadku jeden z nich traktowany jest jako główny i to jego przyporządkowanie do segmentu jest brane pod uwagę. Który z nich ma być główny? Istnieją dwie zasady:
- jeśli między nawiasami występuje skalowanie to rejestrem głównym jest skalowany rejestr
- w każdym innym przypadku rejestrem głównym jest pierwszy rejestr w wyrażeniu
Pola bitowe
[edytuj]Obecnie asembler GNU nie obsługuje pól bitowych jako takich. Same w sobie nie wnoszą nic do funkcjonalności asemblera, gdyż można je zastąpić definiując różne nazwy symboliczne.