asembler

1. Kilka słów wstępu...


Skoro czytasz tą stronę drogi czytelniku zapewne jesteś zainteresowany poznaniem tak wspaniałej rzeczy jaką jest język asemblera. A może zastanawiasz się w jakim celu w dzisiejszych czasach - czasach języków coraz to "wyżej" poziomowych. Pewnie dręczy cię pytanie - w jakim celu uczyć się języka niskopoziomowego jakim jest asembler. Po co męczyć się godzinami pisząc setki - jak nie tysiące linijek strasznie ciężkiego do zrozumienia (ale tylko pozornie) kodu - kiedy w zasadzie wszystko czego zapragniesz można "wyklikać". Poprzez serię tych artykułów postaram się przedstawić wam zalety (jak i wady) asemblera, sposoby jego wykorzystania - zwłaszcza w ukierunkowaniu na programowanie gier komputerowych. Postaram się sprawić abyście pokochali programowanie w języku asemblera tak jak ja je pokochałem ;). Tak więc zapraszam do podążania za mną do krainy gdzie każda jedynka czy zero ma znaczenie ;).

2. Po co nam asembler?


Po co nam asembler? - lub - w jakim celu programujemy w asemblerze? - to pytanie pewnie dręczy was tak jak mnie kiedyś dręczyło. Więc - odpowiedź na to pytania jest banalnie prosta: Asembler jest nam potrzebny z trzech prostych powodów:
- Szybkość
- Szybkość
- Szybkość
Programy powstałe w asemblerze nie tylko są bardzo szybkie (zawierają tylko kod niezbędny do wykonania programu - czego nie uzyskamy programując w językach wysokiego poziomu), ale także bardzo małe (brak zbędnego kodu = mniejszy rozmiar samego pliku programu). Ze względu na te powyższe elementy asembler jest przydatny w wielu przypadkach - ale do tego stopniowo sami dojdziemy ;)

3. Trochę niezbędnych informacji o samym kursie.


Nudne - ale trzeba przez to przejść aby nie było jakiś niepotrzebnych pytań ani problemów. Na początek należy wspomnieć o odmianach języka asemblera (tak, tak - nie jest on jeden ;)). My zajmiemy się językiem asemblera dla architektury x86 - czyli dla procesorów firmy Intel - Pentium, Pentium Pro, Pentium MMX, Pentium II, Pentium III, Pentium 4, Core 2, etc. - i tutaj wyprzedzając pytania - zdecydowana większość kodu który pojawi się w tym kursie powinna bezproblemowo działać pod procesorami AMD (K5,K6,K7,K8,K10) jak i również na niektórych procesorach innych producentów. Tutaj odsyłam zainteresowanych do poczytania więcej na temat architektury x86 - nie ma sensu abym się tutaj teraz na ten temat rozpisywał. Później pojawią się pewne elementy które niestety nie będą działać na procesorach firm innych niż Intel - ale o tym wspomnę w odpowiednim momencie.

Dobór kompilatora (asemblera) - tutaj są dwa najpopularniejsze nurty (chociaż tak naprawdę jest ich trochę więcej):

NASM - używający składni Intel'owskiej
GASM - GNU Assembler - używający składni AT&T (tej będę używał w tym kursie)

System operacyjny - tez bardzo ważna kwestia. Programowanie w języku asemblera pod Windowsem/Dosem rożni się od programowania pod Linux'em. Ja będę używał systemu Gentoo Linux 2008.0 AMD64 (architektura 64-bitowa) jednak kody które pojawią się w tym kursie powinny działać bez większych problemów pod dowolną dystrybucją linux'a. Windowsowców zachęcam do zainstalowania jakiejkolwiek dystrybucji Linux chociażby jako wirtualną maszynę (ja używam Sun xVM VirtualBox). Będziemy później programować w języku asemblera pod Windowsem/Dosem ale do poznania podstaw potrzebny będzie Linux.

Instalacja GASM - wybraliśmy (w zasadzie to ja wybrałem ;P) system operacyjny jak i kompilator (asembler).
Instalacja kompilatora pod Gentoo będzie wręcz banalnie prosta - wystarczy w konsoli wpisać

emerge binutils


i wszystko co nam potrzebne (asembler i linker) powinien się sam ściągnąć, skompilować i zainstalować. Powinniśmy uzyskać w konsoli dostęp do dwóch ważnych komend - as (kompilacja) i ld (linkowanie).

4. Co dalej?


W następnej części spróbujemy napisać nasz pierwszy program, poznamy rejestry oraz porównamy składnię Intela i AT&T - zrobimy to w prostym celu - będziemy mogli sobie tłumaczyć kod z składni Intel owskiej na AT&T oraz odwrotnie. Dzięki temu będziemy mogli korzystać z kompilatorów używających składni Intel-owskiej jak i również zrozumieć wiele kodów napisanych w składni Intel owskiej. W kolejnych częściach poznamy podstawy programowania w języku asemblera - napiszemy parę ciekawych programów, następnie zaczniemy korzystać z koprocesora i zrobimy parę operacji na liczbach zmiennoprzecinkowych. Poznamy rozszerzenia instrukcji procesora takie jak MMX, SSE, SSE2, SSE3 (prawdopodobnie również SSE4) - dla AMD-owców postaram się trochę omówić rozszerzenie 3DNow!, następnie powykorzystujemy (:P) trochę funkcje biblioteczne języka C/C++ jak i będziemy pisać w języku asemblera pojedyncze funkcje które później wykorzystywać będziemy w programach pisanych w C. Na koniec najprawdopodobniej napiszemy jakąś grę (po to tu jesteśmy w końcu) :P

Jeśli macie jakieś pytania zapraszam na nasze forum ;) Do zobaczenia wkrótce.




1. No to jedziemy ;)



Skoro czytasz ten tekst drogi czytelniku oznacza to że w dalszym ciągu chcesz nauczyć się programować w języku asemblera i pierwsza część kursu Cię do tego nie zniechęciła ;). Jednak zanim zaczniemy cokolwiek na poważnie pisać w języku asemblera musimy przebrnąć przez część teoretyczną. Jeśli to przetrwacie - to przetrwacie już wszystko ;). A tak na poważnie to teraz pora na poznanie konstrukcji samego procesora. Będzie to niezbędne jeśli chcemy dobrze programować w języku asemblera. Pod każdą architekturę język asemblera wygląda inaczej (co jest logiczne) - my zajmiemy się (jak już było wspomniane) programowaniem pod architekturę x86. Większość kodów które napiszemy w dalszej części tego kursu powinna działać zarówno pod procesorami firmy Intel jak i pod procesorami firmy AMD - chociaż jak później się okaże niektóre niestety pod AMD działać nie będą. Do rzeczy.

 

2. Rejestry procesora x86 i x86_64



Ponieważ procesory 64-bitowe są już jakiś czas na rynku, zapewne większość z was taki posiada. Dlatego głupio było by nie wspomnieć o tymże procesorze (wykracza to trochę poza tematykę bo to już architektura x86_64 lub AMD64... jak zwał, tak zwał). Jednak wszystkie kody w tym kursie będą pisane pod procesory 32-bitowe (na końcu ewentualnie możemy napisać coś pod 64 bity).

Rejestry procesora są to niewielkie komórki pamięci umieszczone w samym procesorze - przez co bardzo szybko dla procesora dostępne. Przechowuje się w nich wyniki tymczasowych obliczeń, adresy etc. Procesor architektury x86 zawiera następujące rejestry (dla architektury x86_64 rejestry te są większe - 64 bitowe - jak pokazane poniżej):

Uwaga: rejestry RAX,RBX,RCX,RDX,RBP,RSI,RDI,RSP,RIP występują tylko w procesorach 64-bitowych

8 rejestrów ogólnego użytku:

1. Akumulator (rejestr służący głównie do wykonywania działań matematycznych):

RAX (64 bity) = EAX (młodsze 32 bity) + starsze 32 bity

EAX (32 bity) = AX (młodsze 16 bitów) + starsze 16 bitów

AX (16 bitów) = AL (młodsze 8 bitów) + AH (starsze 8 bitów)

 

RAX (64 bity)


EAX (32 bity)




AX (16 bitów)







AH (8 bitów)

AL (8 bitów)

 

2. Rejestr bazowy (wskaźnik do danych w pamięci np. tablicy):

RBX (64 bity) = EBX (młodsze 32 bity) + starsze 32 bity

EBX (32 bity) = BX (młodsze 16 bitów) + starsze 16 bitów

BX (16 bitów) = BL (młodsze 8 bitów) + BH (starsze 8 bitów)

 

RBX (64 bity)


EBX (32 bity)




BX (16 bitów)







BH (8 bitów)

BL (8 bitów)

 

3. Licznik (rejestr przydatny np. w pętlach):

RCX (64 bity) = ECX (młodsze 32 bity) + starsze 32 bity

ECX (32 bity) = CX (młodsze 16 bitów) + starsze 16 bitów

CX (16 bitów) = CL (młodsze 8 bitów) + CH (starsze 8 bitów)

 

RCX (64 bity)


ECX (32 bity)




CX (16 bitów)







CH (8 bitów)

CL (8 bitów)

 

4. Rejestr danych (przechowywanie różnych adresów etc.)

RDX (64 bity) = EDX (młodsze 32 bity) + starsze 32 bity

EDX (32 bity) = DX (młodsze 16 bitów) + starsze 16 bitów

DX (16 bitów) = DL (młodsze 8 bitów) + DH (starsze 8 bitów)

 

RDX (64 bity)


EDX (32 bity)




DX (16 bitów)







DH (8 bitów)

DL (8 bitów)

 

5. Rejestr indeksowy (indeks źródłowy)

RSI (64 bity) = ESI (młodsze 32 bity) + starsze 32 bity

ESI (32 bity) = SI (młodsze 16 bitów) + starsze 16 bitów

 

RSI (64 bity)


ESI (32 bity)




SI (16 bitów)

 

6. Rejestr indeksowy (indeks docelowy)

RDI (64 bity) = EDI (młodsze 32 bity) + starsze 32 bity

EDI (32 bity) = DI (młodsze 16 bitów) + starsze 16 bitów

 

RDI (64 bity)


EDI (32 bity)




DI (16 bitów)

 

7. Wskaźnik bazowy

RBP (64 bity) = EBP (młodsze 32 bity) + starsze 32 bity

EBP (32 bity) = BP (młodsze 16 bitów) + starsze 16 bitów

 

RBP (64 bity)


EBP (32 bity)




BP (16 bitów)

 

8. Wskaźnik stosu

RSP (64 bity) = ESP (młodsze 32 bity) + starsze 32 bity

ESP (32 bity) = SP (młodsze 16 bitów) + starsze 16 bitów

 

RSP (64 bity)


ESP (32 bity)




SP (16 bitów)

 

Rejestr FLAGS (flagi procesora):

1. CF (carry flag) - flaga przeniesienia

Przyjmuje wartość 1 jeśli w wyniku ostatnio wykonanej operacji nastąpiło przeniesienie z bitu najstarszego na zewnątrz lub nastąpiła pożyczka z zewnątrz do bitu najstarszego. Jeśli nic z tych rzeczy nie nastąpiło flaga jest zerowana.

2. PF (parity flag) - flaga parzystości

Przyjmuje wartość 1 jeśli w wyniku ostatnio wykonanej operacji liczba bitów o wartości 1 w młodszym bajcie jest parzysta. W przeciwnym wypadku flaga jest zerowana.

3. AF (auxiliary flag) - flaga przeniesienia pomocniczego

Jak CF tylko w przypadku pożyczki z bitu 4 na 3 lub z 3 na 4. W przeciwnym wypadku flaga jest zerowana.

4. ZF (zero flag) - flaga zera

Przyjmuje wartość 1 jeśli wynik ostatnio wykonanej operacji jest równy 0. W przeciwnym wypadku flaga jest zerowana.

5. SF (sign flag) - flaga znaku

Wartość flagi jest zgodna z bitem znaku (najbardziej znaczącym) w otrzymanym w wyniku ostatniej operacji wyniku.

6. TF (trap flag) - flaga pracy krokowej

Wartość równa 1 wywołuje wprowadzenie procesora w tryb pracy single step modus (umożliwiającą po każdym wykonanym rozkazie wywołanie przerwania). Wartość 0 oznacza normalną pracę procesora.

7. IF (interrupt flag) - flaga przerwania

Wartość równa 1 powoduje obsługiwanie przez procesor systemu przerwań. W przeciwnym wypadku przerwania są ignorowane.

8. DF (direction flag) - flaga kierunku

Wartość równa 1 powoduje przetwarzanie łańcuchów przy rosnących adresach. W przeciwnym wypadku - przy malejących

9. OF (overflow flag) - flaga przepełnienia

Przyjmuje wartość 1 w przypadku wystąpienia przepełnienia (wystąpiło przeniesienie na bit znaku, lub pożyczka z tegoż) i jednocześnie CF=0. W przeciwnym wypadku flaga jest zerowana.

Rejestr EIP:

1. Wskaźnik instrukcji (mówi procesorowi skąd ten ma pobierać kolejne instrukcje)

RIP (64 bity) = EIP (młodsze 32 bity) + starsze 32 bity

EIP (32 bity) = IP (młodsze 16 bitów) + starsze 16 bitów

Oprócz tego mamy jeszcze:

8  rejestrów koprocesora (80-bitowych) - O koprocesorze napiszę w którejś kolejnej części tego kursu. Na razie wystarczy wiedzieć że są ;)

8  rejestrów MMX (64-bitowych, mapowanych na rejestrach koprocesora) - też wspomniemy o nich później

8  rejestrów XMM (128-bitowych) - j/w

3. Podsumowanie



Poznaliśmy właśnie rejestry procesora architektury x86 (oraz wiemy o istnieniu ich rozszerzonej wersji w x86_64). Będziemy z tej wiedzy cały czas korzystać więc radzę ją dosyć dobrze opanować. W następnej części napiszemy swój pierwszy program w języku asemblera ;)



Wyszukiwarka

Podobne podstrony:
Asembler ARM przyklady II
Asembler dla procesorow Intel Vademecum profesjonalisty asinvp
asembler 8051 opis rozkazow
asembler podrecznik uzytkownika OOJHQHHGL4XKD5C7LJZPZGPQDNDQ6CI23AUGTVQ
Procedury arytmetyczne w języku Asembler ST7
Asembler Kurs Programowania Dla Srednio Zaawansowanych S Kruk www !OSIOLEK!com
Praktyczny kurs asemblera pkasem
Asembler Przykłady
Asembler wykład 09-10-2000
Asembler w Linuksie - optymalizowanie kodu, Asembler w Linuksie - optymalizowanie kodu
[PL tutorial] Asembler kurs - HTML, Asembler, Asembler
Programowanie w języku asemblera
Asembler wykład 16-10-2000, Zaczynamy (pracę) z programem Turbo Assembler, Rozdział 1
Asembler2
Asembler programy1
21 Pisanie i uruchamianie programów w asemblerze
Asembler ARM przyklady
Asembler Sztuka programowania Wydanie II asesz2
10 Asembler