Jak pisać programy w języku asembler?
Wstęp
Pewnie wiele osób spośród Was słyszało już o tym, jaki ten asembler jest straszny
. Ale te
opinie możecie już śmiało wyrzucić do Kosza (użytkownicy Linuksa mogą skorzystać z /dev/null),
gdyż przyszła pora na przeczytanie tego oto dokumentu.
Na początek, przyjrzyjmy się niewątpliwym zaletom języka asembler:
(przeskocz zalety)
- Mały rozmiar kodu.
Tego nie przebije żaden kompilator żadnego innego języka. Dlaczego? Oto kilka powodów:
- Jako programista języka asembler (
asma
) wiesz co i gdzie w danej chwili się
znajduje. Nie musisz ciągle przeładowywać zmiennych, co zabiera i miejsce, i czas.
Możesz eliminować instrukcje, które są po prostu zbędne.
- Do Twojego pięknego kodu nie są dołączane żadne biblioteki z dziesiątkami procedur,
podczas gdy Ty używasz tylko jednej z nich. Co za marnotrawstwo!
- Jako znawca zestawu instrukcji wiesz, które z nich są krótsze.
- Duża szybkość działania.
Znając sztuczki optymalizacyjne, wiedząc, które instrukcje są szybsze, eliminując zbędne
instrukcje z pętli, otrzymujesz kod nierzadko dziesiątki razy szybszy od tych napisanych
w językach wysokiego poziomu (high-level languages, HLLs).
Nieprzebijalne.
- Wiedza.
Nawet jeśli nie piszesz dużo programów w asmie, zdobywasz wprost bezcenną wiedzę o tym, jak
naprawdę działa komputer i możesz zobaczyć, jak marną czasem robotę wykonują kompilatory
HLL-ów. Zrozumiesz, czym jest wskaźnik i na czym polegają często popełniane błędy z nim związane.
- Możliwość tworzenia zmiennych o dużych rozmiarach, a nie ograniczonych do 4 czy 8 bajtów.
W asemblerze zmienne mogą mieć dowolną liczbę bajtów.
- Wstawki asemblerowe.
Jeśli mimo to nie chcesz porzucić swojego ulubionego dotąd HLLa, to w niektórych językach
istnieje możliwość wstawiania kodu napisanego w asemblerze wprost do Twoich programów!
Teraz przyszła kolej na rzekome
argumenty przeciwko językowi asembler:
(przeskocz wady)
- Nieprzenośność kodu między różnymi maszynami.
No cóż, prawda. Ale i tak większość tego, co napisane dla procesorów Intela będzie
działało na procesorach AMD i innych zgodnych z x86. I na odwrót.
Nieprzenośność jest chyba najczęściej używanym argumentem przeciwko asemblerowi.
Jest on zwykle stawiany
przez programistów języka C, którzy po udowodnieniu
, jaki to język C jest wspaniały,
wracają do pisania dokładnie w takim samym stopniu nie-przenośnych programów...
Nie ukrywajmy: bez zmian kodu to tylko programy niewiele przewyższające Witaj, świecie
skompilują się i uruchomią pod różnymi systemami.
A nowoczesne kompilatory i tak produkują najlepszy kod...
Nieprawda, i to z kilku powodów:
- Kompilator używa zmiennych. No i co z tego, pytacie? A to, że pamięć RAM (o dyskach itp.
nie wspominając) jest wiele, wiele razy wolniejsza niż pamięć procesora (czyli
rejestry). Nawet pamięć podręczna (cache) jest sporo wolniejsza.
- Kompilator nie wie, co się znajduje na przykład w danym rejestrze procesora, więc pewnie wpisze
tam tę samą wartość. Co innego z programistą asma.
- Kompilator nie jest w stanie przewidzieć, co będzie w danej chwili w innych rejestrach.
Więc do rejestru, który chce zmienić i tak wpisze jakąś wartość zamiast użyć innego
rejestru, co prawie zawsze byłoby szybsze, a więc i lepsze.
Co innego zrobiłby programista asma.
- Kompilator może używać dłuższych lub wolniejszych instrukcji.
- Kompilator nie zawsze może poprzestawiać instrukcje, aby uzyskać lepszy kod. Programista
asma widzi, co się w jego kodzie dzieje i może wybrać inne, lepsze rozwiązanie (na przykład
zmniejszyć rozmiary pętli czy pozbyć się zależności między instrukcjami)
- Kompilator może nie być świadomy technologii zawartych w procesorze. Programista asma wie,
jaki ma procesor i za darmo ściąga do niego pełną dokumentację.
- Brak bibliotek standardowych.
I znowu nieprawda. Istnieje co najmniej kilka takich. Zawierają procedury wejścia, wyjścia,
alokacji pamięci i wiele, wiele innych.
Nawet sam taką jedną bibliotekę napisałem...
Kod wygląda dziwniej. Jest bardziej abstrakcyjny.
Dziwniej - tak, ale nie oszukujmy się. To właśnie języki wysokiego poziomu są abstrakcyjne!
Asembler przecież
operuje na tym, co fizycznie istnieje w procesorze - na jego własnych rejestrach przy użyciu
jego własnych instrukcji.
- Mniejsza czytelność kodu.
Kod w języku C można tak napisać, że nie zrozumie go nawet sam autor. Kod w asmie można tak
napisać, że każdy go zrozumie. Wystarczy kilka słów wstępu i komentarze. W HLLach trzeba
byłoby wszystkie struktury objaśniać.
A wygląd i czytelność kodu zależą tylko od tego, czy dany programista jest dobry, czy nie.
Dobry programista asemblera nie będzie miał większych kłopotów z odczytaniem kodu w asmie niż
dobry programista C kodu napisanego w C.
- Brak łatwych do zrozumienia instrukcji sterujących (if, while, ...)
Przecież w procesorze nie ma nic takiego!
Programista asma ma 2 wyjścia: albo używać prawdziwych instrukcji albo napisać własne makra,
które takie instrukcje będą udawać (już są takie napisane). Ale nie ma nic uniwersalnego.
Na jedną okazję można użyć takich instrukcji, a na inną - innych. Jednak zawsze można wybrać
najszybszą wersję według własnego zdania, a nie według zdania kompilatora.
Asembler może i nie jest z początku łatwy do zrozumienia, ale wszystko przyjdzie wraz
z doświadczeniem.
- Trzeba pisać dużo kodu.
No, tak. Jak się komuś męczą palce, to niech zostanie przy HLLach i żyje w świecie abstrakcji.
Prawdziwym programistom nie będzie przecież takie coś przeszkadzać!
Mówi się, że ZŁEJ baletnicy nawet rąbek u sukni przeszkadza.
Poza tym, programista nad samym pisaniem kodu spędza około 30% czasu przeznaczonego na program
(reszta to plan wykonania, wdrażanie, utrzymanie, testowanie...). Nawet jeśli programiście
asma zabiera to 2 razy więcej czasu niż programiście HLLa, to i tak zysk lub strata wynosi 15%.
Dużo pisania sprawia, że umysł się uczy, zapamiętuje składnię instrukcji i
nabiera doświadczenia.
- Assmebler jest ciężki do nauki.
Jak każdy nowy język. Nauka C lub innych podobnych przychodzi łatwo, gdy już się zna na przykład
Pascala. Próba nauczenia się innych dziwnych
języków zajmie dłużej, niż nauka asma.
- Ciężko znajdować i usuwać błędy.
Na początku równie ciężko, jak w innych językach. Pamiętacie jeszcze usuwanie błędów ze swoich
pierwszych programów w C czy Pascalu?
- Programy w asemblerze są ciężkie w utrzymaniu.
Znowu powiem to samo: podobnie jest w innych językach. Najlepiej dany program zna jego autor,
co wcale nie oznacza, że w przyszłości będzie dalej rozumiał swój kod (nawet napisany w
jakimś HLLu). Dlatego ważne są komentarze. Zdolność do zajmowania się programem w przyszłości
także przychodzi wraz z doświadczeniem.
- Nowoczesne komputery są tak szybkie, że i tak szybkość nie robi to różnicy...
Napiszmy program z czterema zagnieżdżonymi pętlami po 100 powtórzeń każda. Razem 100 000 000
(sto milionów) powtórzeń. Czas wykonania tego programu napisanego w jakimś HLLu liczy się w
minutach, a często w dziesiątkach minut (czasem godzin - zależy od tego, co jest w pętlach). To samo
zadanie napisane w asemblerze daje program, którego czas działania można liczyć w sekundach
lub pojedynczych minutach!
Po prostu najszybsze programy są pisane w asemblerze. Często otrzymuje się program
5-10 razy szybszy (lub jeszcze szybszy) niż ten w HLLu.
- Chcesz mieć szybki program? Zmień algorytm, a nie język
A co jeśli używasz już najszybszego algorytmu a on i tak działa za wolno?
Każdy algorytm zawsze można zapisać w asemblerze, co poprawi jego wydajność.
Nie wszystko da się zrobić w HLLu.
- Nowoczesne komputery i tak mają dużo pamięci. Po co więc mniejsze programy?
Wolisz mieć 1 wolno działający program o rozmiarze 1 MB, napisany
w HLLu i robić 1 czynność w danej chwili, czy może
wolisz wykonywać 10 czynności na raz dziesięcioma programami w asemblerze po 100kB każdy
(no, przesadziłem - rzadko który program w asmie sięgnie aż tak gigantycznych rozmiarów!)?
To był tylko wstęp do bezkresnej wiedzy, jaką każdy z Was zdobędzie.
Ale nie myślcie, że całkowicie odradzam Wam języki wysokiego poziomu.
Ja po prostu polecam Wam asemblera.
Najlepsze programy pisze się w czystym asemblerze, co sprawia niesamowitą radość,
ale można przecież łączyć języki. Na przykład, część programu odpowiedzialną za wczytywanie danych
lub wyświetlanie wyników można napisać w HLLu, a intensywne obliczeniowo pętle pisać w asmie,
albo robiąc wstawki w kod, albo pisząc w ogóle oddzielne moduły i potem łączyć wszystko w całość.
Nauka tego wspaniałego języka przyjdzie Wam łatwiej, niż myślicie.
Pomyślcie też, co powiedzą znajomi, gdy się dowiedzą, co umiecie!