|
|
Programowanie Główna assembler Rejestry
Gdybyś chciał dodać do siebie dwie liczby (zapomnij tego, że jesteś informatykiem i używasz do wszystkiego komputera ;-) to musiałbyś zapamiętać te dwie liczby , a później je zsumować. Jeśli byś ich nie pamiętał to nie mógł byś ich dodać, czyż nie? Otocz procesor aby dodać, pomnożyć i zrobić cokolwiek z jakimikolwiek liczbami musi najpierw umieścić je w swojej pamięci, a następnie dopiero ich użyć. Jest to jednak jego pamięć, nie RAM. Jego pamięć nie dzieli się na kilobajty i bajty, a rejestry. Rejestry te są komórkami po dwa bajty każdy (2 bajty to word, czyli po polsku słowo). Umieszczać w nich możesz każdą liczbę z zakresu od 0 do 65535. To właśnie na nich procesor wykonuje większość swoich działań. Jako, że nikt nie lubi działać w pojedynkę rejestry połączyły się w grupy. Jedna z takich grup są rejestry ogólnego przeznaczenia. Są to komórki pamięci procesora, które służą mu do prawie wszystkich operacji. To właśnie ich używa do dodawania, odejmowania, mnożenia i dzielenia. Rejestry ogólnego przeznaczenia nie maja jakiś kiper - skomplikowanych numerów, a jedynie nazwy, zamiast których używa się dwuliterowych skrótów:
AX (Accumulator) BX (Base register) CX (Counter register) DX (Data register)
SI (Source Index) DI (Destination Index) SP (Stack Pointer) BP (Base Pointer)
Aby przypisać coś do jakiegokolwiek rejestru powinieneś użyć polecenie MOV DO,SKAD (Mov jak MOVE - przenieś). Oczywiście assembler jest zakręconym językiem, wiec zamiast najpierw pisać co, a później do czego będziesz musiał robić dokładnie na odwrót :-). Najpierw więc podajesz rejestr do którego chcesz przenieść liczbę, a później dopiero jaka to liczba. Popatrz na poniższy przykład:
1 2 3 4
| asm MOV AX,2 MOV DI,4 end; |
Trochę zakręcone, ale my, zwykli śmiertelnicy nie jesteśmy w stanie pojąć geniuszu twórców tego języka. Powyższy kod źródłowy przeniesie do rejestru AX liczbę dwa, a do rejestru BX liczbę cztery. Oczywiście na tym nie koniec zamotania. Otóż każdy z rejestrów kończących się na X można podzielić na dwie polowy! Da to polowe wyższa (Higher), oraz niższą (Lower). Połówki rejestru AX będą wiec nazywać się AL i AH i każda z nich zajmować będzie jeden bajt. Obie połówki można wiec ująć równaniem
AX=AL+AH*256 rn Rozumiesz? Jeśli do rejestru AL wrzucisz np. 4 , a do AH wrzucisz 1 to w AX pojawi się \'Automatycznie\' 1*256+4=260. Rozumiesz? Jako prace domowa dostajesz przeanalizowanie poniższego programu:
1 2 3 4 5 6 7 8 9 10 11 12
| program mnoz; var a:byte; wyn:word; begin readln(a); asm mov al,0 mov ah,a mov wyn,ax end; writeln(wyn); end; |
Oczywiście jako, ze nikt nie lubi odrabiać prac domowych to powiem, ze mnoży on wpisana liczbę przez 256 :-) Rejestr AX jest typu word (2 bajty), wiec każda z jego jednobajtowych polówek będzie typu byte. Na początku odczytywana jest liczba zapisana w zmiennej a, a następnie przenoszona jest do rejestru AH. Rejestr AX zawiera wiec AH*256. Po przeniesieniu jego zawartości do zmiennej wynik wyświetlamy ja na ekranie. Tak, oczy Cię nie mylą. Polecenie mov może nie tylko przenosić wartości do rejestrów, ale także z rejestrów do zmiennych. Czyż nie jest to proste?
Oczywiście gdy zaczynasz wszystko powoli rozumieć z pomocą przychodzi kolejna porcja Asemblera, która całkowicie już wszystko zamiesza :-) Oprócz rejestrów, które Ci przedstawiłem istnieje także pewien taki jeden, do którego nie możesz zapisać wartości liczbowych! Otóż jest on rejestrem flag. Flaga to takie cos, które może być ustawione, lub też nie. Taki bit. Jak już wiesz każdy rejestr składa się z 16 bitów. Ktoś więc wpadł na pomysł, że możnaby niektóre z bitów jednego rejestru zamienić na tzw. Flagi. Do pozostałych z nich nie można oczywiście niczego zapisać. Tak prawdę mówiąc to do flag także nie możesz nic zapisać ani odczytać. Przynajmniej nie bezpośrednio. Są one ustawiane przez polecenia przy czym niektóre polecenia zmieniają wartości wszystkich flag, niektóre niektórych, a niektóre żadnych. Flagi oznacza się dwoma literami, z czego ta druga to F (czasami jest pomijana). Jak na razie jedyna flaga, która Cię zainteresuje to ZF , czyli Zero Flag. Jest ona ustawiana, gdy wynik ostatniej operacji jest równy zeru. Abyśmy mogli zrozumieć to polecenie najlepiej poznać najpierw polecenia DEC X i INC X, gdzie X to rejestr. Pierwsze z tych poleceń zmniejsza X o jeden, drugie zaś - zwiększa:
1 2 3 4 5 6 7
| asm mov AX,1 dec AX {AX=0} inc AX {AX=1} end; |
Skoro już to zrozumiałeś to zauważ, ze po poleceniu DEC wynik umieszczony w AX jest równy zeru. Ustawiona zatem zostanie ZF. Po co? Ano po nic. Ale co to komu szkodzi? Jak już powiedziałem - człowiek zaczyna cos rozumieć i znowu coś nowego. Po pierwsze - nie wszystkie polecenia zmieniają flagi. Nawet kiedy powinny. MOV AX,0 daje w wyniku zero, lecz mimo to nie ustawi ZF. Dlaczego? Tylko Bóg wie.. Jedna z rzeczy, która może Ci się przydać jest porównywanie rejestrów (i liczb) za pomocą CMP A,B. Polecenie to porówna A i B, a wynik zamieści w odpowiedniej fladze. Jeśli będą równe to zostanie ustawione ZF. Tylko nie pytaj czemu..
1 2 3 4 5
| asm mov ax,2 mov cx,2 cmp ax,cx end; |
Hmm.. W powyższym programie brakuje jednego. Porównujemy liczby, lecz nic z tego nie mamy.. Z pomocą przychodzą tu instrukcje warunkowe. O ile w Pascalu było to zwykle if o tyle w Asemblerze mamy ich kilkanaście. Po jednym/dwu na każdą flagę. W Asemblerze oczywiście nie ma mowy o jakiś Beginach i Endach oznaczających procedury do wykonania po ifie. Tu instrukcje warunkowe po prostu wykonują skok do podanej etykiety. JZ Etykieta skoczy do podanej etykiety, gdy ZF jest ustawione:
1 2 3 4 5 6 7 8
| asm mov ax,2 mov cx,2 cmp ax,cx jz @zawsze mov dx,1999 @zawsze: end; |
W przypadku tego programu przeniesienie wartości 1999 do rejestru DX nie zostanie nigdy wykonane.
Rejestrów i Flag jest wiele , lecz na razie nie będę ich opisywał, gdyż Ci się nie przydadzą. Poznasz je później, w stosownym czasie, tymczasem powtórz wszystko jeszcze raz.
|
|