Pierwszy program

Słowo wstępu

Asembler jest najniższym z możliwych "mostem" łączącym nas programistów i komputer. Jest to język maszynowy, a ściślej mówiąc język procesora. Operuje on na dwóch stanach 1 i 0 (prąd płynie albo nie płynie).
Sam język Asembler nie jest jednakże tylko i wyłącznie zbiorem komend i rozkazów, dotyka on bezpośrednio zagadnienia jakim jest architektura komputerów. Przeplatają się tu zagadnienia rejestrów, taktowania, koprocesora i wielu innych.

Dlaczego warto umieć assemblera?
Programy asemblerowe są nieporównanie szybsze od tych pisanych w HLL (High-Level-Language). Odpowiednio zoptymalizowany program pisany w asm może być dziesiątki razy szybszy od swoich odpowiedników pisanych w innych językach. Również pamięciochłonność takiego programu zależy tylko i wyłącznie od programisty, przeciętnie program taki jest dziesięciokrotnie mniejszy od programów pisanych w C++ lub Pascal.
Prymitywność? Asembler mimo, że jest językiem niskiego poziomu i został wymyślony dawno temu nie jest językiem prymitywnym, przeciwnie - ma on wręcz nieograniczone możliwości. Znaczna liczba wirusów jest pisana właśnie w Asemblerze.


Rejestry procesora:
Rejestry są to fizycznie istniejące w komputerze elementy elektroniczne mające zdolność zapamiętywania.

Procesory 8086/8088 mają ich 14.

To za ich pomocą realizowane są polecenia w programie. Więcej informacji o rejestrach: Rejestry.

Program, który omówię będzie liczył średnią arytmetyczną z 12 elementowej tablicy liczb naturalnych.
Jest to tylko przykład programu asemblerowego oraz prostego algorytmu, program nie wyświetla natomiast wyniku.
Zamiana liczby na ciąg znaków ASCII to już osobna historia.
Wersja TASM. Przed przystąpieniem do pracy należy zaopatrzyć się w "ulubiony" edytor tekstowy, programy do kompilacji i linkowania oraz koniecznie TruboDebuger.
tasm.zip (459.49 kB)



.model  TINY                                 ;dyrektywa model, mowi kompilatorowi
                                                   ;na jakim modelu pamieci bedzie operowal

.code                                            ;dyrektywa .code rozpoczyna czesc kodu
org    100h                                    ;dyrektywa org 100h, jest informacja
                                                    ;dla kompilatora, ze program jest typu .com
Start:                 
        mov        bx, OFFSET Tablica     ;przeslanie do bx offsetu tablicy "Tablica"
        mov        cx, DL_TABLICY         ;przeslanie do cx (licznika petli) dlugosci tablicy
        xor        ax, ax                         ;zerowanie rejestru ax poprzez xor

Petla:                                            ;etykieta petli
        add        al, byte ptr [ds:bx]       ;dodaj do al bajt z tablicy
        inc        bx                                ;przesun sie o jedno miejsce dalej  w tablicy
loop    Petla                                   ;skocz do "Petla" póki cx <> 0
 
        mov        bx, dl_tablicy              ;przeslij do bx dlugosc tablicy
        div        bx                               ;podziel ax przez bx, wynik w ax
        mov        [Srednia], ax              ;przeslij zawartosc ax, do miejsca w pamieci
                                                    ;oznaczonego jako Srednia
        mov        ah, 4Ch                      ;zakonczenie programu
        int        21h
;==========================================================================
;miejsce z danymi
dl_tablicy        EQU     12       
Tablica                DB      01h, 02h, 00h, 10h, 09h, 30h
                DB      13h, 08h, 12h, 08h, 0Ah, 05h
Srednia                DW      00h

END Start






Po kolei..

.model TINY

Dyrektywa ta określa na jakim modelu pamięci będzie operować program.
Jeden segment pamięci to 65535 bajtów czyli 64KB, wynika to z faktu, że
maksymalną wartość jaką można zapisać w rejestrach to 2^16.
Rozróżnia się kilka modeli pamięci:

TINY, SMALL, MEDIUM, COMACT, LARGE oraz HUGE

Wybór odpowiedniego modelu może zależeć od rozmiaru danych, "kaprysu" programisty oraz specyficznych zastosować programu.


.code

Wskazuje początek segmentu gdzie znajduje się kod programu. Nie oznacza to, że nie można umieszczać tam również danych. Otóż można, tak jak zrobiliśmy to my, należy umieszczać je w takim miejscu aby nie stały się częścią kodu programu. Gdyby procesor potraktował daną jako instrukcje mogłoby to mieć fatalne konsekwencje.
W bardziej rozbudowanych programach wykorzystuje się również .data - określający segment danych oraz .stack - stos programu.


org 100h

Ta linia jest informacją dla kompilatora, że program wynikowy będzie typu .com. Informuje ona również o tym, że nasz kod znajdować się będzie po uruchomieniu pod adresem 100h (256 dziesiętnie).
Więcej informacji na temat adresowania oraz przechowywania programu w pamięci można znaleźć w internecie oraz w książkach o asm.


Start:
mov        bx, OFFSET Tablica                       
mov        cx, DL_TABLICY
xor        ax, ax         

Start: może być to dowolna nazwa z dwukropkiem. Określa ona początek kodu w programie.
mov jest to rozkaz przesyłania zawartości prawego argumentu do lewego argumentu. Argumentami mogą być rejestry, pamięć lub wartość bezpośrednia.

OFFSET jest to operator wydzielający z wyrażenia (np. tabela) adres początku względem początku segmentu.
tak więc

mov bx, OFFSET Tablica
jest to więc przesłanie do rejestru bx, "adresu" tablicy "Tablica".

Użyte przeze mnie słowo adres nie jest do końca prawidłowe, de facto jest to offset adresu.
Adres: nazywany adresem fizycznym uzyskuje się na podstawie zapisu Segment:Offset i wyraża się wzorem
adres = 16*segment + offset.

mov cx, DL_TABLICY
jest to przesłanie liczby zdefiniowanej jako DL_TABLICY do licznika pętli cx. Rejestr ten będzie wykorzystany w następnych liniach programu jako licznik pętli.

xor ax, ax
jedna z metod zerowania rejestrów, nad matematycznym znaczeniem tego rozkazu nie ma sensu się rozpisywać.


Petla:
       add            al, byte ptr [ds:bx]
       inc            bx
loop    Petla

add rozkaz dodawania dwóch argumentów, wynik dodawania zapisywany jest w lewym argumencie.
Umieszczony w nawiasach kwadratowych adres [ds:bx] jest adresem fizycznym (segment:offset) i jest wskazaniem na miejsce w pamięci znajdujące się pod tym adresem. W naszym przypadku jest to wskazanie na pierwszy element tablicy "Tablica".
inc bx skrót od increment (zwiększ), zwiększa operand o 1.

loop Petla skok do etykiety Petla gdy CX≠0.

mov        bx, dl_tablicy
div        bx
mov        [Srednia], ax

div bx dzielenie argumentów bez znaku (tj. liczb nieujemnych). Jest to dzielenie 32 bitowe
(AX←DX:AX\dzielnik), wynik zapisywany w ax.

mov [Srednia], ax przesłanie do komórki pamięci operacyjnej znajdującej się pod adresem o nazwie "Srednia" wartości rejestru ax.

mov        ah, 4Ch
int        21h

Jest to funkcja 4Ch przerwania 21 (DOS) - Zakończenie procesu. Funkcja kończy wykonywany proces, zamykając wszystkie otwarte pliki i zwalniając przydzieloną pamięć.
Czym są właściwie przerwania? Są to podprogramy realizujące pewne usługi, np. wyświetlanie tekstu na ekranie, pobieranie znaku, zapisywanie na dysku. Stanowią one podstawą języka Asembler, a już na pewno programisty. Można powiedzieć, że miarą umiejętności programowania w tym języku jest znajomość przerwań.  

dl_tablicy        EQU     12       
Tablica                DB      01h, 02h, 00h, 10h, 09h, 30h
                DB      13h, 08h, 12h, 08h, 0Ah, 05h
Srednia                DW      00h

END

Deklaracje zmiennych w programach odbywa się według schematu:
nazwa typ wartość
Nazwa jest to dowolny napis akceptowalny przez kompilator.
Typ:

DB - zmienna składająca się z jednego/wielu bajtów
DW - zmienna składająca się z jednego/wielu słów (16 bajtów)
DD - zmienna składająca się z jednego/wielu słów podwójnych (32 bajty)

Wartość mogą to być oddzielone przecinkami liczby lub też ciąg liter zgrupowany w cudzysłów
Napis DB "Napis"

Asembler ma możliwość definiowania zmiennych, przypisać nazwie symbolicznej określoną wartość podaną jako argument.  Jest to odpowiednik define w C++.

END Start zakończenie kodu.






Do pisania programów w asemblerze można wykorzystać dowolny edytor tekstowy. Wygodnie jest aby numerował on wiersze, ja korzystam z "dev C++".
Program zapisujemy z rozszerzeniem .asm
Kompilacja programu odbywa się pod DOSem więc uruchamiamy konsole dosową. (Start→Uruchom→cmd).
Przenosimy się do folderu gdzie znajduje się nasz program oraz kompilator TASM.

X:\myprog>tasm myprog.asm
Turbo Assembler Version 3.1 Copyright <c> 1988, 1992 Borland International
Assembling  file:                     myprog.asm
Error messages:                None
Warning messages:        None
Passes :                             1
Remaining  memory:        452k

X:\myprog>tlink/t myprog.obj
Turbo Link Version 5.1 Copyright <c> 1992 Borland International

X:\myprog>


Po uruchomieniu programu oczywiście nic się nie wyświetli.
Efekty obejrzymy w Turbo Debugerze.

X:\myprog>td myprog.com

Jeżeli wszystko zostało poprawnie skompilowane powinno ukazać się okno turbodebugera.
Klikamy ok, możemy powiększyć okienko - klawisz F5.
Kolejne komendy wywołujemy klawiszem F7 (dokładne śledzenie) lub F8 (to samo co F7 tylko pomija pętle).
W głównym oknie jest lista kolejnych rozkazów, po prawej stronie aktualny stan rejestrów.
Dla porównania: Ten program zajmuje 56 bajtów, program w C++ lub Pascal 56KB.


Dla bezpieczeństwa własnego dodam że nie ponoszę odpowiedzialności za ten program, ale trzeba by mieć wielkiego pecha żeby zniszczyć sobie komputer tym programem, jest tam zaledwie parę operacji dodawania i dzielenia.
Informacje
Ostatnia modyfikacja 04-01-2009 14:08 Ostatni autor GieKaA
Ilość wyświetleń 12751 Wersja 5
Komentarz
bordeux dnia 27-08-2009 21:52
szkoda ze to ryzykowna zabawka. Moze ktos napisac jak bardzo? Jest mozliwosc uszkodzenia procka?
flammin dnia 04-02-2009 20:56
@Ciamajda,
Czy jest to język maszynowy? Odsyłam do specyfikacji procesorów z rodziny x88, x86 i tak dalej.
W asemblerze posługujesz się mnemoniką: mov, xor, shl i tak dalej, które są niczym innym odpowiednimi poleceniami procesora? No tak... a to w takim razie cout w C++ jest też mnemoniką procka.. ale nie jest, ponieważ na cout składa się kilkanaście linii kodu.
Co do wydajności to w pewnych przypadkach jest on dużo szybszy. Sam przeprowadzałem test szybkości na sortowaniu bąbelkowym, już dla 65000 elementów różnica jest mniej więcej 10:6 (w porównaniu z C++). Również wykorzystanie koprocesora dla funkcji sinus daje znaczne rezultaty w porównaniu z innymi językami i jest to potwierdzone.
Kolego, asm to nie tylko konsola, możliwa jest również praca okienkowa pozwalająca na pełną kontrolę obsługi zdarzeń oraz pisanie bibliotek DLL odwołuje tu do NASM.
Wirusy mogą być pisane w asm, a czy są? zapytaj się tych którzy je piszą i zapytaj się tych którzy znają asm na dostatecznie wysokim poziomie:).
... i bynajmniej nie upieram się przy asemblerze. Programowanie w nim nie jest ani łatwe ani piękne mimo to jego stosowanie może niekiedy pomóc, szczególnie tam gdzie liczy się szybkość i pełna kontrola nad kodem.
Ciamajda dnia 06-01-2009 17:52
Sprostowanie: asembler NIE JEST językiem maszynowym. Język maszynowy to ciąg bajtów, który jest interpretowany przez procesor. Asembler jest prostym językiem, który umożliwia stosowanie zamiast wartości liczbowych prostych mnemoników, np. add, inc, mov.

Sprostowanie drugie: autor pisze "Odpowiednio zoptymalizowany program pisany w asm może być dziesiątki razy szybszy od swoich odpowiedników pisanych w innych językach. Również pamięciochłonność takiego programu zależy tylko i wyłącznie od programisty, przeciętnie program taki jest dziesięciokrotnie mniejszy od programów pisanych w C++ lub Pascal." Gdzie jakieś porównania? Czy autor rzeczywiście to sprawdzał? Skąd ma takie wiadomości? Czy nie są to aby życzenia oparte na tezie "to wiedzą wszyscy, więc tak jest"? Jestem programistą i wiem (ale też nie powiem skąd ;P), że dobry kompilator robi lepszą optymalizację skomplikowanych algorytmów niż jest to w stanie zrobić człowiek ;) Chyba, że mówimy tu o optymalizacji dla konkretnej rodziny procesorów (np. P4 z HT), ale tego autor nie zaznaczył ;)

Sprostowanie trzecie: "ma on wręcz nieograniczone możliwości. Znaczna liczba wirusów jest pisana właśnie w Asemblerze." Nie sądzę, by wirusy były pisane w asemblerze. Z tego, co jest mi wiadome, są one pisane po prostu w czystym API, czyli bez korzystania z dodatkowych bibliotek wprost wywołują funkcje systemowe. Taki program (możliwy do napisania nawet w C++Builder), zajmuje kilka do kilkunastu kilobajtów (sam exec), więc do osiągnięcia małej objętości wcale asembler nie jest potrzebny.

Ogólnie jednak pochwalam :) Programiści asemblerowi nie są może bardzo poszukiwani na rynku pracy, ale jak już jest taka potrzeba (np. sterowniki do specjalistycznych urządzeń), to znaleziony programista może dyktować warunki, bo konkurencja jest niewielka :) Warto, żeby wiedza ta nie zginęła :)
DALAILAMER dnia 21-09-2008 08:25
ej manfredek zdaje sie ze cos porabales , z tego co wiem to do wartosci konkretnej cyfry trzeba dodac dec(48) zeby uzyskac kod ASCII danej cyfry np.  ASCII cyfry zero = 41 dec = 30 hex
... a jezeli wyswietlanie liczb jest proste dla ciebie to sie swietnie squada bo za cholere nie umiem napisac funkcji wyswietlajacej rejestr st(0) w postaci dec .... to moze moglbys pomoc
Potwoor_ dnia 24-06-2008 01:53
może źle się kompilowało cy coś, sprawdzę też na XP ale trochę boję się o klumpa po moim pierwszym doświadczeniu z tasm'em =D ( patrz pierwszy komentarz )
flammin dnia 23-06-2008 10:40
@manfredek, wypisanie cyfry polega na dodaniu '0', wypisanie liczby na ekran jest to po kolei dzielenie liczb i wypisywanie reszty z dzielenia na ekran i tak dalej...

@Potwoor_, ja testowałem na 2ch maszynach z XP

Pozdrawiam.
Potwoor_ dnia 23-06-2008 09:41
hej, ja jakoś tak z ciekawości ztasmowałem ten przykładowy kod, odpaliłem .com'a i dostałem coś takiego:

"Program spowodował błąd przepełnienia przy dzieleniu, jeśli problem się powtórzy, zwróć sie do dostawcy"

=D ( to było na win98 na maszynie testowej )
manfredek dnia 23-06-2008 08:10
autor napisał:
Zamiana liczby na ciąg znaków ASCII to już osobna historia.


Tak, tylko dodanie '0' (kod ASCII zera, a nie samego zera!) do liczby...
Potwoor_ dnia 22-06-2008 21:41
tasm jest super =) cieszę się że bawiłem sie nim na maszynie do pisania ( P133 ) bo grzebałem sobie w menu, kliknąłem Run, czarny ekran, kompik ucichł a mnie zatkało =D wciskam Power a tu nic, to cisnę jeszcze kilka razy, wciąż to samo, ale ożył po odłączeniu i podłączeniu zasilania =D

a więc 2 minuty zabawy w asemblerze i popsułem komputer, niezłe osiągniecie według mnie =D

Katalog
Copyright © 2000-2006 by Coyote Group 0.9.3-pre3
Czas generowania strony: 0.0890 sek. (zapytań SQL: 10)