Jarosław Kuchta Architektura Systemów Komputerowych ćwiczenie 3 Arytmetyka całkowita instrukcja laboratoryjna Wprowadzenie Celem ćwiczenia jest zapoznanie się z budową i sposobem działania jednostki arytmetyczno-logicznej (ALU) w architekturze IA32. Jednostka arytmetyczno-logiczna Podstawowym elementem budowy kaŝdego procesora jest jednostka arytmetycznologiczna (ALU Arithmetical and Logical Unit). Jednostka ta wykonuje rozkazy arytmetyczne, rozkazy logiczne, rozkazy porównań na liczbach całkowitych. Schemat pracy ALU ilustruje następujący rysunek: DST SRC FLAGS TMP Jednostka arytmetyczno-logiczna ma dwa wejścia wielobitowe (w architekturze IA32 32-bitowe). Na wejścia te są podawane wartości dwóch operandów rozkazu. Pierwszym z tych operandów (DST) jest zawsze rejestr ogólnego przeznaczenia, rejestr indeksowy lub rejestr wskaźnikowy. Drugim z operandów (SRC) jest rejestr ogólnego przeznaczenia, rejestr indeksowy, rejestr wskaźnikowy, komórka pamięci (podwójne słowo). MoŜe być on teŝ ustawiany na wartość stałą zapisaną w rozkazie. Wynik operacji arytmetycznej lub logicznej jest zapisywany w rejestrze tymczasowym (TMP) o tej samej długości, co operandy wejściowe. Po ustaleniu wyniku operacji jest on przepisywany z rejestru tymczasowego (TMP) do rejestru określonego jako pierwszy operand (DST) 1. W czasie wykonania rozkazu jednostka arytmetycznologiczna ustawia dodatkowo rejestr flag zgodnie z wartością wyniku i warunkami występującymi w czasie wykonania rozkazu. Rozkazy sterujące procesora mogą 1 W pierwszych procesorach Intela operandem DST mógł być tylko wybrany rejestr procesora, tzw. akumulator. Nazwa ta jest w architekturze IA32 przypisywana zgodnie z tradycją rejestrowi EAX (AX, AL), chociaŝ rejestr ten utracił juŝ swoją tradycyjną, wyróŝnioną rolę. 1
wykorzystywać znaczniki zapisane w rejestrze flag do sterowania dalszym wykonaniem programu. Rozkazy arytmetyczne operacje dodawania i odejmowania Procesor architektury IA32 realizuje cztery podstawowe operacje arytmetyczne: dodawania (rozkazy ADD, ADC), odejmowania (rozkazy SUB, SBB) mnoŝenia (rozkazy MUL, IMUL) dzielenia (rozkazy DIV, IDIV) Rozkazy dodawania i odejmowania operują na całkowitych liczbach 32-bitowych. Liczby te mogą być traktowane jako liczby bez znaku lub liczby ze znakiem (w kodowaniu U2). Sposób wykonywania operacji w obu przypadkach jest taki sam. Schemat wykonania operacji dodawania bazuje na sekwencji operacji dodawania dwóch bitów z ewentualnym dodaniem przeniesienia z poprzedniej pozycji i ewentualnym wygenerowaniem przeniesienia na następną pozycję: DST[i] SRC[i] C i+1 + C i TMP[i] Stany wejść i wyjść podstawowej komórki dodającej mogą być następujące: C i+1 TMP[i] DST[i] SRC[i] C i 0 0 0 0 0 0 1 0 0 1 0 1 0 1 0 1 0 0 1 1 0 1 1 0 0 1 0 1 0 1 1 0 1 1 0 1 1 1 1 1 Dwa rozkazy dodawania ADD i ADC róŝnią się tym, Ŝe w rozkazie ADD przeniesienie początkowe (C 0 ) jest ustawiane na 0, a w ADC przeniesienie początkowe jest ustawiane na wartość znacznika CF z rejestru flag. Przeniesienia z pozycji ostatniej (C 32 ) oraz przedostatniej (C 31 ) są wpisywane odpowiednio do znaczników CF (Carry Flag) i OF (Overflow Flag). Znacznik CF moŝe być wykorzystany dla dodawania liczb większych niŝ 32-bitowe w kilku krokach. Wówczas pierwszy rozkaz dodawania powinien być ADD, a drugi (i ew. kolejne ADC. Przykład: 2
Qword1 DQ? Qword2 DQ? MOV EAX, DWORD PTR Qword1 MOV EBX, DWORD PTR Qword2 ADD EAX, EBX MOV DWORD PTR Qword1, EAX MOV EAX, DWORD PTR Qword1+4 MOV EBX, DWORD PTR Qword2+4 ADC EAX, EBX MOV DWORD PTR Qword1+4, EAX Znacznik OF moŝe być wykorzystany do sprawdzenia, czy w czasie operacji na liczbach całkowitych ze znakiem (!) nie wystąpił nadmiar. Operacja odejmowania jest wykonywana analogicznie do operacji dodawania, z tą róŝnicą, Ŝe bit przeniesienia z pojedynczej komórki odejmującej jest traktowany jako poŝyczka z następnej pozycji. Stany komórki odejmującej mogą być następujące: C i+1 TMP[i] DST[i] SRC[i] C i 0 0 0 0 0 1 1 0 0 1 1 1 0 1 0 1 0 0 1 1 0 1 1 0 0 0 0 1 0 1 0 0 1 1 0 1 0 1 1 1 Rozkazy odejmowania SUB i SBB róŝnią się tym, Ŝe w rozkazie SUB poŝyczka początkowa (C 0 ) jest ustawiana na 0, a w SBB poŝyczka początkowa jest ustawiana na wartość znacznika CF z rejestru flag, co umoŝliwia kaskadowe odejmowanie liczb większych niŝ 32-bitowe. W rozkazach dodawania i odejmowania oprócz flag CF i OF ustawiane są teŝ pozostałe flagi rejestru FLAGS w sposób następujący: PF (Parity Flag) ustawiana na 1 jeśli 8 najmłodszych bitów wyniku ma parzystą liczbę jedynek, w przeciwnym wypadku ustawiana na 0. AF (Auxiliary carry Flag) ustawiana na przeniesienie z pozycji trzeciej (C 4 ) wykorzystywana przy rozkazach arytmetyki BCD. ZF (Zero Flag) ustawiana na 1 jeśli wynik jest równy 0, w przeciwnym wypadku ustawiana na 0. SF (Sign Flag) ustawiana na 1 jeśli bit znaku wyniku jest równy 1, w przeciwnym wypadku ustawiana na 0. Operacje mno enia W operacji mnoŝenia liczb całkowitych wynik mnoŝenia jest dwa razy dłuŝszy niŝ długość operandów. W architekturze IA32 istnieją trzy wersje rozkazu mnoŝenia MUL: mnoŝenie AL przez drugi operand (rejestr 8-bitowy lub bajt pamięci), wynik w AX (AH część starsza, AL część młodsza wyniku) 3
mnoŝenie AX przez drugi operand (rejestr 16-bitowy lub słowo pamięci), wynik w DX:AX (DX część starsza, AX część młodsza wyniku) mnoŝenie EAX przez drugi operand (rejestr 32-bitowy lub podwójne słowo pamięci), wynik w EDX:EAX (EDX część starsza, EAX część młodsza wyniku) Jeśli starsza część wyniku jest róŝna od zera, to do znaczników OF i CF wpisywane są jedynki, jeśli jest równy zero, to wpisywane są zera. Stan pozostałych znaczników jest nieokreślony. Istnieje rozkaz MUL dla mnoŝenia bez znaku oraz IMUL dla mnoŝenia ze znakiem. Rozkaz IMUL potrafi wymnoŝyć dowolny rejestr 8-, 16- lub 32-bitowy przez inny rejestr tej samej długości, tej samej długości komórkę pamięci lub wartość stałą. Wynik jest tej samej długości, co pierwszy operand. Rozkazy dzielenia Dzielenie jest operacją odwrotną do mnoŝenia. Operand pierwszy (dzielna) jest podwójnej długości. Oprócz wyniku dzielenia (który jest całkowity), podawana jest teŝ reszta z dzielenia. W architekturze IA32 istnieją trzy wersje rozkazu dzielenia: dzielenie AX przez drugi operand (rejestr 8-bitowy lub bajt pamięci), wynik w AL, reszta z dzielenia w AH dzielenie DX:AX przez drugi operand (rejestr 16-bitowy lub słowo pamięci), wynik w AX, reszta z dzielenia w DX dzielenie EDX:EAX przez drugi operand (rejestr 32-bitowy lub podwójne słowo pamięci), wynik w EAX, reszta z dzielenia w EDX Po dzieleniu stan wszystkich ww. znaczników jest nieokreślony. Jeśli dzielnik jest równy zero lub wynik dzielenia nie mieści się w odpowiednim rejestrze (AL, AX albo EAX), to generowany jest wyjątek. Istnieje rozkaz DIV dla dzielenia bez znaku i IDIV dla dzielenia ze znakiem Operacje logiczne Jednostka arytmetyczno-logiczna wykonuje trzy operacje logiczne na bitach operandów (w IA32 operandów 32-bitowych): operację AND operację OR operację XOR Operacje te są wykonywane na wszystkich bitach jednocześnie przeniesienie nie występuje. Stany pojedynczej komórki mogą być następujące: TMP[i] TMP[i] TMP[i] DST[i] SRC[i] (XOR) (OR) (AND) 0 0 0 0 0 1 1 0 0 1 1 1 0 1 0 0 1 1 1 1 4
PoniewaŜ nie występuje przeniesienie, więc: znaczniki OF i CF są ustawiane na 0, stan znacznika AF jest nieokreślony. Pozostałe znaczniki są ustawiane tak, jak dla operacji dodawania i odejmowania. Operacje negacji logicznej i arytmetycznej Są to operacje działające tylko na jednym operandzie. Rozkaz negacji logicznej (NOT) zamienia wszystkie bity operandu DST na przeciwne. Rozkaz negacji arytmetycznej (NEG) wpisuje do operandu DST wartość przeciwną. Rozkaz NOT moŝe być zrealizowany przez ALU jak rozkaz XOR z drugim operandem (SRC) ustawionym wewnętrznie na same jedynki. Rozkaz NEG moŝe być zrealizowany jako odejmowanie operandu DST od zera, albo przez zanegowanie wszystkich bitów i dodanie jedynki z przeniesienia C 0. Operacje porówna W architekturze IA32 występują dwa rodzaje porównań: porównanie arytmetyczne (CMP) i porównanie logiczne (TEST). Porównanie arytmetyczne (CMP) jest wykonywane tak samo jak operacja odejmowania (SUB), z tym jednym wyjątkiem, Ŝe wynik z rejestru tymczasowego TMP nie jest przepisywany do rejestru DST. Porównywanie logiczne (TEST) jest wykonywane samo jak operacja AND, z tym samym wyjątkiem. Operacje przesuni Operacje przesunięcia w lewo przypisują do kaŝdego bitu i rejestru TMP bit i-1 rejestru DST, a następnie przepisują cały rejestr TMP do rejestru DST. Analogicznie operacje przesunięcia w prawo przypisują do kaŝdego bitu i rejestru TMP bit i+1 rejestru DST. WyróŜnia się cztery rodzaje rozkazów przesunięcia w lewo/ w prawo: przesunięcie zwykłe (rozkazy SHL i SHR) na pierwszą pozycję (dla rozkazu SHL najmłodszą, dla rozkazu SHR najstarszą) wprowadzane jest zero, wartość ostatniego bitu jest wpisywana do znacznika CF; rotację (rozkazy ROL i ROR) na pierwszą pozycję wprowadzana jest wartość ostatniego bitu, wartość ta teŝ jest wpisywana do znacznika CF. rotację przez przeniesienie (rozkazy RCL i RCR) na pierwszą pozycję wprowadzana jest wartość znacznika CF, wartość ostatniego bitu jest wpisywana do znacznika CF; przesunięcie arytmetyczne (rozkazy SAL i SAR) uwzględnia bit znaku; dla przesunięcia w lewo na pierwszą pozycję (do najmłodszego bitu) wprowadzane jest zero, wartość przedostatniego bitu jest wpisywana do znacznika CF, bit znaku pozostaje bez zmian; dla przesunięcia w prawo wartość bitu znaku jest wpisywana na przedostatnią pozycję, wartość najmłodszego bitu jest wpisywana do znacznika CF. Istnieją wersje przesunięcia o 1 i o większą liczbę pozycji (zapisaną w rejestrze CL, albo podaną jako stała). Przy przesunięciu o 1 znacznik OF określa, czy nastąpiła zmiana znaku. Pozostałe flagi: 5
dla rozkazów rotacji: SF, ZF, AF, PF są nie zmieniane dla rozkazów przesunięć: SF, ZF, PF są ustawiane tak, jak dla rozkazów dodawania i odejmowania, AF jest nie zmieniany. Inne operacje ustawiania i testowania bitów Lista rozkazów procesora IA32 zawiera rozkazy ustawiające znacznik CF na 0, na 1 albo na logiczną wartość przeciwną (odpowiednio CLC, STC i CMC). Zawiera równieŝ rozkazy ustawiające wybrany bit rejestru lub komórki pamięci na 0, na 1 albo na logiczną wartość przeciwną (odpowiednio BTR, BTS i BTC). Rozkaz testujący wybrany bit rejestru lub komórki pamięci (BT) wpisuje wartość tego bitu do znacznika CF. Zadania dla studentów Zad.1. Dodawanie i odejmowanie liczb 32-bitowych 1. Wprowadź poniŝszy program:.686 extern _ExitProcess@4: near public _main _DATA SEGMENT dword public 'DATA' use32 Dword1 DD 1 Dword2 DD 2 Result DD 0 _DATA ENDS _TEXT SEGMENT dword public 'CODE' use32 ASSUME cs:_text, ds:_data _main: MOV EAX, Dword1 MOV EBX, Dword2 ADD EAX, EBX MOV Result, EAX ; zakończenie wykonywania programu push 0 call _ExitProcess@4 _TEXT END ENDS 2. Wykonaj program krok po kroku. W oknie rejestrów obserwuj zmianę wartości rejestrów i znaczników. 3. Wykonaj to samo zadanie modyfikując deklarację zmiennej Dword1. UŜyj po kolei wartości: a. 1000, b. 2147483645, c. 2147483646, d. 2147483647, e. 4294967293, f. 4294967294, 6
g. 4294967295. Podaj wartości dziesiętne i szesnastkowe wyniku oraz znaczniki CF i OF. 4. Zmień deklarację zmiennej Dword2 na 2. Wykonaj działania dla zmiennej Dword1 po kolei równej: a. 3, b. 2, c. 1, Podaj wartości dziesiętne i szesnastkowe wyniku oraz znaczniki CF i OF. 5. Zmień deklarację zmiennej Dword2 na 2 i rozkaz ADD na SUB. Wykonaj działania dla zmiennej Dword1 po kolei równej: a. 3, b. 2, c. 1, Podaj wartości dziesiętne i szesnastkowe wyniku oraz znaczniki CF i OF. Zaznacz róŝnice względem zadania z p.4. Zad.2. Dodawanie i odejmowanie liczb 64-bitowych 1. Wprowadź poniŝszy program:.686 extern _ExitProcess@4: near public _main _DATA SEGMENT dword public 'DATA' use32 Qword1 DQ 1 Qword2 DQ 2 Result DQ 0 _DATA ENDS _TEXT SEGMENT dword public 'CODE' use32 ASSUME cs:_text, ds:_data _main: MOV EAX, DWORD PTR Qword1 MOV EBX, DWORD PTR Qword2 ADD EAX, EBX MOV DWORD PTR Qword1, EAX MOV EAX, DWORD PTR Result+4 MOV EBX, DWORD PTR Qword2+4 ADC EAX, EBX MOV DWORD PTR Result+4, EAX ; zakończenie wykonywania programu push 0 call _ExitProcess@4 _TEXT END ENDS 2. Wykonaj program krok po kroku. W oknie rejestrów obserwuj zmianę wartości rejestrów i znaczników. Obserwuj zmianę zawartości pamięci. 7
3. Wykonaj to samo zadanie modyfikując deklarację zmiennej Qword1. UŜyj po kolei wartości: a. 4294967293, b. 4294967294, c. 4294967295, d. 4294967296, e. 4294967297. Podaj wartości dziesiętne i szesnastkowe wyniku z okna pamięci. 4. Zmień teraz rozkaz ADC na ADD. Wykonaj zadanie dla takich samych danych jak w poprzednim punkcie. Porównaj wyniki z uzyskanymi w poprzednim punkcie: a. 4294967293, b. 4294967294, c. 4294967295, d. 4294967296, e. 4294967297. 5. W powyŝszym programie zmień rozkaz ADD na SUB i ADC na SBB. Wartości Qword1 i Qword2 ustaw tak, jak w programie z p.1. Podaj wynik z pamięci w postaci dziesiętnej i szesnastkowej. 6. Zmień teraz rozkaz SBB na SUB. Wykonaj zadanie dla takich samych danych jak w poprzednim punkcie. Porównaj wynik z uzyskanym w poprzednim punkcie. Zad. 3. Mno enie liczb 1. Wprowadź program mnoŝący dwie umieszczone w pamięci liczby 32-bitowe i umieszczający wynik 64-bitowy w pamięci:.686 extern _ExitProcess@4: near public _main _DATA SEGMENT dword public 'DATA' use32 Factor1 DD 2 Factor2 DD 20 Result DQ 0 _DATA ENDS _TEXT SEGMENT dword public 'CODE' use32 ASSUME cs:_text, ds:_data _main: MOV EAX, Factor1 MOV EBX, Factor2 MUL EBX MOV DWORD PTR Result, EAX MOV DWORD PTR Result+4, EDX ; zakończenie wykonywania programu push 0 call _ExitProcess@4 8
_TEXT END ENDS 2. Wykonaj ten sam program dla liczb 2000 i 2 000 000. Podaj dziesiętną i szesnastkową wartość wyniku oraz wartości wskaźników CF i OF. Czy wynik mieści się na 32-bitach? 3. Wykonaj ten sam program dla liczb 2 000 000 i 2 000 000. Podaj dziesiętną i szesnastkową wartość wyniku oraz wartości wskaźników CF i OF. Czy wynik mieści się na 32-bitach? 4. Wykonaj ten sam program dla liczb 2000 i 2 000 000, 2000 i 2 000 000 oraz 2000 i 2 000 000. UŜyj mnoŝenia bez znaku (MUL) i ze znakiem (IMUL). Podaj dziesiętną i szesnastkową wartość wyniku oraz zawartość znaczników CF i OF. 5. Wykonaj ten sam program dla liczb 2 000 000 i 2 000 000, 2 000 000 i 2 000 000 oraz 2 000 000 i 2 000 000. UŜyj mnoŝenia bez znaku (MUL) i ze znakiem (IMUL). Za kaŝdym razem podaj dziesiętną i szesnastkową wartość wyniku oraz zawartość znaczników CF i OF. Zad.4. Dzielenie liczb 1. Wprowadź program dzielący dwie umieszczone w pamięci liczby 32-bitowe i umieszczający wynik (32-bitowy) oraz resztę z dzielenia w pamięci:.686 extern _ExitProcess@4: near public _main _DATA SEGMENT dword public 'DATA' use32 Divisor1 DD 21 Divisor2 DD 2 Result DD 0 Remainder DD 0 _DATA ENDS _TEXT SEGMENT dword public 'CODE' use32 ASSUME cs:_text, ds:_data _main: MOV EAX, Divisor1 MOV EDX, 0 MOV EBX, Divisor2 DIV EBX MOV Result, EAX MOV Remainder, EDX ; zakończenie wykonywania programu push 0 call _ExitProcess@4 _TEXT END ENDS 2. Wykonaj ten sam program dla liczb 2 000 001 i 2000. Podaj dziesiętną i szesnastkową wartość wyniku i reszty z dzielenia. Wyjaśnij, do czego konieczny jest rozkaz MOV EDX, 0 w powyŝszym programie. 9
3. Zmodyfikuj podany program tak, aby moŝliwe było wykonanie dzielenia dla liczb 2 000 000 000 001 i 2 000 000. Podaj dziesiętną i szesnastkową wartość wyniku i reszty z dzielenia. 4. Wykonaj ten program dla liczb 2 000 000 000 001 i 20. Jakie wyniki otrzymujesz? 5. Wykonaj ten sam program dla liczb 2 000 001 i 2000, 2 000 001 i 2000 oraz 2 000 001 i 2000. UŜyj dzielenia bez znaku (DIV) i ze znakiem (IDIV). Za kaŝdym razem podaj dziesiętną i szesnastkową wartość wyniku i reszty z dzielenia Zad. 5. Operacje logiczne i porównania 1. Napisz samodzielnie program zerujący rejestr EAX bez wykorzystywania rozkazu MOV. Wskazówka: uŝyj rozkazu XOR. Podaj główny rozkaz tego programu. 2. Napisz samodzielnie program porównujący, czy liczba 32-bitowa w rejestrze EAX ma wartość zero. UŜyj rozkazu: a. OR b. CMP c. TEST. Podaj operandy tych rozkazów 3. Napisz samodzielnie program wpisujący do rejestru EBX wartość przeciwną do EAX bez uŝycia rozkazów NEG i SUB. Zad. 6. Operacje przesuni 1. Napisz samodzielnie program przesuwający o 5 pozycji w prawo, a następnie o 5 pozycji w lewo liczbę 48 umieszczoną w rejestrze EAX. Czy otrzymałeś na koniec programu liczbę 48? 2. Zmodyfikuj program tak, aby uzyskać na koniec wyjściową liczbę 32-bitową niezaleŝnie od jej wartości. Wskazówka uŝyj dodatkowego rejestru i rozkazu rotacji. 3. Uprość program z poprzedniego punktu wykorzystując rozkaz SHLD i SHRD. 4. Porównaj działanie rozkazów SHL i SAL oraz SHR i SAR na liczbach 1 i 2 przy przesunięciu o 1 pozycję. Podaj wyniki w postaci szesnastkowej i dziesiętnej. Zad. 7. Operacje na bitach (dodatkowe) 1. Jakie trzy rozkazy moŝna alternatywnie wykorzystać do sprawdzenia, czy wartość 32 bitowa w rejestrze EAX jest ujemna (ma bit znaku równy 1)? Jakie są róŝnice między wykonaniem tych rozkazów w zakresie znaczników. 10