ROZDZIAŁ DZIEWIĄTY OPERACJE ARYTMETYCZNE I LOGICZNE, INFORMATYKA, „THE ART OF ASSEMBLY LANGUAGE” [PL]
[ Pobierz całość w formacie PDF ]
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
WYŁĄCZNOŚĆ DO PUBLIKOWANIA TEGO TŁUMACZENIA
POSIADA
AG
„THE ART OF ASSEMBLY LANGUAGE”
tłumaczone by KREMIK
konsultacja naukowa: NEKRO
wankenob@priv5.onet.pl
nekro@pf.pl
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
ROZDZIAŁ DZIEWIĄTY:
OPERACJE ARYTMETYCZNE I LOGICZNE
Dużo więcej jest potrzebne do posługiwania się asemblerem niż znajomość mnogich
operacji. Musimy nauczyć się jak je stosować i co one robią, Wiele instrukcji jest użytecznych dla operacji, które
mają mało pracy z ich matematycznymi lub oczywistymi funkcjami .Rozdział ten omawia jak skonwertować
wyrażenia z języka wysokiego poziomu do asemblera, Omawia również zaawansowane operacje arytmetyczne i
logiczne wliczając w to wielokrotnej precyzji i sztuczki ,którymi możemy zabawić się z różnymi instrukcjami.
9.0 WSTĘP
Rozdział ten omawia sześć głównych tematów: konwertowanie wyrażeń arytmetycznych HLL’a na
język asemblera, wyrażenia logiczne, arytmetyczne i logiczne operacje o podwyższonej precyzji, działanie na
operandach o różnych rozmiarach, styl maszynowy i arytmetyczny i operacje maskowania. Podobnie jak
poprzednie rozdziały, rozdział ten zawiera obszerny materiał, którego musimy się nauczyć natychmiast jeśli
jesteśmy początkującymi programistami asemblerowymi. Poniższe części, które mają przedrostek „•” są
niezbędne. te części z „⊗” omawiają zaawansowane tematy, które możemy odłożyć na jakiś czas.
•
Wyrażenia arytmetyczne
•
Proste przypisania
•
Proste wyrażenia
•
Wyrażenia złożone
•
Operatory przemienności
•
Wyrażenia logiczne
•
Operacje wielokrotnej precyzji
•
Operacje dodawania o wielokrotnej precyzji
•
Operacje odejmowania o wielokrotnej precyzji
•
Porównania o podwyższonej precyzji
⊗ Mnożenie o podwyższonej precyzji
⊗
Dzielenie o podwyższonej precyzji
⊗
Negacja o rozszerzonej precyzji
•
AND, OR, XOR i NOT o rozszerzonej precyzji
⊗
Operacje przesunięcia i obrotu o podwyższonej precyzji
⊗
Działanie na operandach o różnych rozmiarach
•
Mnożenie bez MUL i IMUL
⊗
Dzielenie bez DIV i IDIV
⊗
Zastosowanie AND do obliczania reszt
⊗
Licznik Modulo – n z AND
⊗
Testowanie dla 0FFFF...FFFh
•
Operacje testowe
⊗
Znaki testujące z instrukcją XOR
⊗
Operacje maskowania
⊗
Maskowanie z instrukcją AND
⊗
Maskowanie z instrukcją OR
⊗
Pakowanie i rozpakowywanie typów danych
⊗
Tablice połączeń
Żaden z tych materiałów nie jest szczególnie trudny do zrozumienia. Jednak, jest tu wiele nowych
tematów a zabranie się za nie po kilka na raz pozwoli nam lepiej wchłonąć materiał. Te tematy z przedrostkiem
„•” są jedynymi, stosowanymi przez nas często; Stąd jest dobrym pomysłem zacząć studiować je jako pierwsze.
9.1 WYRAŻENIA ARYTMETYCZNE
Prawdopodobnie największym szokiem dla początkujących odkrywających asembler po raz pierwszy
jest brak dobrze znanych wyrażeń arytmetycznych. Wyrażenia arytmetyczne, w większości języków wysokiego
poziomu ,wygląda podobnie do tego algebraicznego równania:
X := Y*Z;
W asemblerze, potrzebujemy kilku instrukcji do wykonania tego samego zadania np.
mov
ax, y
imul
z
mov
x, ax
Oczywiście wersja dla HLLi jest dużo łatwiejsza do napisania, czytania i zrozumienia. Punkt ten, bardziej niż
inne, jest odpowiedzialny za odstraszanie ludzi od asemblera.
Chociaż jest wiele zawiłości, konwersja wyrażeń arytmetycznych do języka asemblera nie jest wcale
trudne. Przez zaatakowanie problemu krok po kroku, chociaż może rozwiązalibyśmy problem ręcznie, możemy
łatwo podzielić każde arytmetyczne wyrażenie na odpowiednią sekwencję instrukcji asemblerowych. Ucząc się
jak skonwertować takie wyrażenia na asembler w trzech krokach ,odkryjemy, że jest to trochę trudniejsze
zadanie.
9.1.1 PROSTE PRZYPISANIA
Najłatwiejszymi wyrażeniami do konwersji na język asemblera są proste przypisania. Proste
przypisania kopiują pojedynczą wartość do zmiennej i przybierają jedną z dwóch form:
zmienna := stała
lub
zmienna := zmienna
Jeśli zmienna pojawia się w bieżącym segmencie danych (np. DSEG), konwersja pierwszej postaci na
język asemblera jest łatwe, po prostu używamy instrukcji asemblera:
mov zmienna , stała
Ta instrukcja move bezpośrednio kopiuje stałą do zmiennej.
Drugie powyższe przypisanie jest nieco bardziej skomplikowane ponieważ 80x86 nie dostarcza
instrukcji mov pamięć do pamięci. Dlatego też, aby skopiować jedną zmienną pamięci do innej, musimy
przesunąć dane przez rejestr. Jeśli spojrzymy na kodowanie dla instrukcji mov w dodatku, zauważymy ,że
instrukcje mov ax, pamięć i mov pamięć, ax są krótsze niż przesuwanie wymagającego innych rejestrów. Na
przykład,
var1 := var2
staje się
mov ax, var2
mov var1, ax
Oczywiście, jeśli zastosujemy rejestry ax do czegoś innego, wystarczy jeden z innych rejestrów. Mimo to
musimy zastosować rejestr do przeniesienia jednej komórki pamięci do innej.
Omówienie to oczywiście zakłada, że obie zmienne są w pamięci. Jeśli to możliwe, powinniśmy
spróbować stosować rejestr do przechowywania wartości zmiennej.
9.1.2 PROSTE WYRAŻENIA
Wyższym stopniem złożoności od prostego przypisania jest proste wyrażenie. Proste wyrażenie
przybiera formę:
var := term
1
op term
2
;
Var jest zmienną,term
1
i term
2
są zmiennymi lub stałymi a op jest jakimś arytmetycznym operatorem (dodawania
odejmowania, mnożenia itp.)
Większość wyrażeń przyjmuje taką formę. Nie powinno być to dla nas niespodzianka, że architektura
80x86 została zoptymalizowana właśnie dla takiego typu wyrażeń.
Typowa konwersja dla tego typu wyrażenia przyjmuje postać:
mov
ax, term1
op ax, term2
mov var, ax
Op jest mnemonikiem, który odpowiada wyszczególnionej operacji (np. „+” = add, „-„ = sub, itp.)
Jest kilka niekonsekwencji, których musimy być świadomi. Po pierwsze, instrukcje {i}mul 80x86 nie
pozwalają na operandy bezpośrednie na procesorach wcześniejszych niż 80286.Dalej,żaden procesor nie
pozwala na bezpośredni operand z {i}div. Dlatego też, jeśli operacją jest mnożenie lub dzielenie a jeden z
warunków jest wartością stałą, będziemy musieli załadować tą stałą do rejestru lub komórki pamięci a potem
pomnożyć lub podzielić ax przez tą wartość. Oczywiście, kiedy zajmujemy się mnożenia i dzielenia na
8086/8088,musimy stosować rejestry ax i dz. Nie możemy zastosować przypadkowego rejestru jak możemy
uczynić to z inna operacją .Również, nie zapomnijmy instrukcji rozszerzenia znaku jeśli wykonujemy operację
dzielenia i dzielimy jedną 16/32 bitową liczbę przez inna. W końcu, nie zapomnijmy, że pewne instrukcje mogą
powodować przepełnienie .Możemy chcieć sprawdzić warunek przepełnienia (lub niedomiaru) po operacji
arytmetycznej.
Przykłady prostych wyrażeń:
X := Y + Z;
mov
ax, Y
add
ax, Z
mov
x, ax
X := Y – Z;
mov ax, y
sub ax, z
mov x, ax
X := Y * Z; {bez znaku}
mov
ax, y
mul
z
;stosujemy IMUL dla arytmetyki znakowej
mov
x, ax
;nie zapomnijmy tym zmieść dx
X := Y div Z; {dzielenie bez znaku}
mov
ax, y
mov
dx, 0
;rozszerzenie zerem AX do DX
div z
mov x, ax
X := Y div Z {div ze znakiem}
mov
ax, y
cwd
; rozszerzenie znakiem AX do DX
idiv z
mov x, ax
X := Y mod Z {reszta bez znaku}
mov
ax, y
mov
dx, 0
;rozszerzenie zerem AX do DX
div
z
mov
x, dx
;reszta jest w DX
X := Y mod Z { reszta ze znakiem}
mov
ax, y
cwd
;rozszerzenie znakiem AX do DX
idiv z
mov x, dx ;reszta jest w DX
Ponieważ jest możliwe wystąpienie błędu arytmetycznego, powinniśmy ogólnie testować wynik
każdego wyrażenia na błąd przed lub po ukończonej operacji .Na przykład, bez znakowe dodawanie,
odejmowanie i mnożenie ustawiają flagę przeniesienia jeśli wystąpi przepełnienie. Możemy zastosować
instrukcje bezpośrednio po odpowiedniej sekwencji instrukcji dla testu na przepełnienie. Podobnie, możemy
użyć instrukcji jo lub jno po tej sekwencji do testowania dla przepełnienia przy arytmetyce ze znakiem .Następne
dwa przykłady demonstrują jak zrobić to dla instrukcji add:
X := Y + Z; {bez znaku}
mov
ax, y
add
ax, z
mov
x, ax
jc
uOverflow
X := Y + Z; {ze znakiem}
mov ax, y
add ax, z
mov x, ax
jo sOverflow
pewne operacje jednoargumentowe również kwalifikują się jako proste wyrażenia. Dobrym przykładem
operacji jednoargumentowej jest negacja .W językach wysokiego poziomu negacja przyjmuje jedną z dwóch
możliwych form:
var := - var or var
1
:= - var
2
Zauważmy, że var := - stała jest w rzeczywistości prostym przypisaniem, nie prostym wyrażeniem. Możemy
wyszczególnić stałą ujemną jako operand instrukcji mov:
mov var , -14
Przy operowaniu pierwszą formą operacji negacji stosujemy pojedynczą instrukcją asemblerową:
neg var
Jeśli są stosowane dwie różne zmienne, wtedy stosujemy jak następuje:
mov ax, var
2
neg ax
mov var
2
, ax
Przepełnienie wystąpi wtedy gdy spróbujemy zanegować największą wartość (-128 dla wartości ośmio
bitowej, -32768 dla szesnasto bitowej .itd.).W tym przypadku 80x86 ustawia flagę przepełnienia ,więc możemy
testować dla przepełnienia arytmetycznego stosując instrukcje jo lub jno We wszystkich innych przypadkach
80x86 zeruje flagę przepełnienia. Flaga przeniesienia nie ma znaczenia po wykonaniu instrukcji neg ponieważ
neg (oczywiście) nie stosuje operandu bez znakowego.
9.1.3 WYRAŻENIA ZŁOŻONE
Wyrażenie złożone jest każdym wyrażeniem arytmetycznym zawierającym więcej niż dwa warunki i
jeden operator. Takie wyrażeniami są powszechnie znajdowane w programach pisanych w językach wysokiego
poziomu. Wyrażenia złożone może zawierać nawiasy z operatorami pierwszeństwa ,wywołania funkcji, dostęp
do tablic itp. Konwersja jakiegoś złożonego wyrażenia na język asemblera jest dosyć proste, inne wymagają
wysiłku. Ta sekcja naszkicuje zasady jakich używamy przy konwersji takich wyrażeń.
Złożona funkcja która jest łatwa do konwersji na asembler jest funkcją, która zawiera trzy warunki i
dwa operatory ,na przykład:
W := W- Y – Z;
Konwersja tej instrukcji na język asemblera wymaga dwóch instrukcji sub. Jednakże, nawet w
wyrażeniu tak prostym jak to, konwersja nie jest banalna. Faktycznie są dwa sposoby konwersji powyższej
instrukcji na asembler:
mov
ax, w
sub
ax, y
sub
ax, z
mov
w, ax
i
mov ax, y
sub ax, z
sub w, ax
Druga konwersja, ponieważ jest krótsza ,wygląda lepiej. Jednakże, tworzy ona niepoprawny wynik (zakładając
Pascalową składnię dla oryginalnej instrukcji).Problemem jest prawo łączności. Druga z powyższych sekwencji
oblicza W:=W-(Y-Z),która nie jest tym samym co W:=(W-Y)-Z .Jak umieścimy nawiasy wokół podwyrażeń
możemy wpływać na wynik. Zauważmy, że jeśli jesteśmy zainteresowani krótszą forma, możemy zastosować
poniższą sekwencję:
mov ax, y
add ax, z
sub w, ax
Oblicza to W:=W-(Y+Z).Jest to odpowiednik W:=(W-Y)-Z.
Inną kwestią jest pierwszeństwo. Rozważmy wyrażenie Pascalowskie:
X := W*Y+Z;
Znowu mamy dwa sposoby w jaki możemy ocenić to wyrażenie:
X := (W*Y)+Z;
lub
X := W*(Y+Z)
Prawdopodobnie teraz myślisz, że ten tekst jest szalony. Każdy wie, że poprawnym sposobem oceny tych
wyrażeń jest druga forma przedstawiona w tym drugim przykładzie. Jednakże ,mylisz się, jeśli myślisz w ten
sposób .Język programowania APL ,na przykład, ocenia wyrażenia wyłącznie od prawej do lewej i nie daje
pierwszeństwa jednemu operatorowi przed innym.
Większość języków wysokiego poziomu używa stałego zbioru zasad pierwszeństwa opisujących
porządek oceniania w wyrażeniach wymagających dwóch lub więcej różnych operatorów. Większość języków
programowania oblicza mnożenie i dzielenie przed dodawaniem i odejmowaniem. Te które wspierają
podnoszenie do potęgi (np. FORTRAN i BASIC) zazwyczaj obliczają je przed mnożeniem i dzieleniem. Zasady
te są intuicyjne ponieważ prawie każdy uczył się o nich w szkole. Rozważmy wyrażenie:
X op
1
Y op
2
Z
Jeśli op
1
posiada pierwszeństwo przed op2,wtedy wyliczamy to (X op
1
Y) op
2
Z w przeciwnym razie jeśli op
2
ma pierwszeństwo przed op
1
wtedy wyliczamy to jako X op
1
(Y op
2
Z).W zależności od tego czy są wymagane
operatory i operandy te dwa obliczenia mogą tworzyć różne wyniki.
Kiedy konwertujemy wyrażenie tej postaci do języka asemblera, musimy najpierw obliczyć
podwyrażenie z najwyższym priorytetem. Poniższy przykład demonstruje tą technikę:
; W := X+Y *Z;
mov bx, x
mov ax, y ;musimy najpierw obliczyć Y*Z ponieważ „*” ma najwyższy
mul z ;priorytet.
add bx, ax ;teraz dodajemy iloczyn i wartość Xa
mov w, bx ;zachowujemy wynik
Ponieważ dodawanie jest operacją przemienną ,możemy zoptymalizować powyższy kod tworząc:
; W := X+Y*Z;
mov ax, y ;musimy obliczyć najpierw Y*Z ponieważ „*” ma najwyższy
mul z ;priorytet.
add ax, x ;teraz dodajemy iloczyn i wartość Xa
mov w, ax ;zachowujemy wynik
Jeśli dwa operatory pojawiające się wewnątrz wyrażenia mają taki sam priorytet, wtedy określamy
porządek wyliczania używając zasady łączności .Większość operatorów jest lewo łącznych w znaczeniu, że są
wyliczane od lewej do prawej Dodawanie, odejmowanie, mnożenie i dzielenie wszystkie są lewo łączne. Prawo
łączne operatory są wyliczane od prawej do lewej. Operator potęgowania w FORTRANie i BASICu jest dobrym
przykładem operatora prawo łącznego:
2^2^3 jest równy 2^(2^3) nie (2^2)^3
Zasady pierwszeństwa i łączności określają porządek wyliczania. Pośrednio te zasady mówią nam gdzie
umieścić nawiasy w wyrażeniu dla określenia pierwszeństwa i łączności. Jednak, ostatecznie nasz kod
asemblerowy musi ukończyć pewne działania przed innymi aby poprawnie wyliczyć wartość danego wyrażenia.
Poniższy przykład demonstruje ta zasadę:
; W := X-Y-Z
mov
ax, x
;wszystkie operatory są takie same, więc musimy obliczać
sub
ax, y
;od lewej do prawej ponieważ wszystkie maja taki
sub
ax, z
;sam priorytet
mov
w, ax
;W := X+Y*Z
mov
ax,y
,najpierw musimy obliczyć Y*Z ponieważ mnożenie ma
imul
z
;wyższy priorytet niż dodawanie
add
ax, x
mov
w, ax
;W := X/Y –Z
mov
ax, x
;tu musimy najpierw obliczyć dzielenie ponieważ ma wyższy
cwd
;priorytet
idiv
y
sub
ax, z
mov
w, ax
;W := X*Y*Z
mov
ax, y
;dodawanie i mnożenie są przemienne, dlatego porządek obliczania
imul
z
;nie ma znaczenia
imul
x
mov
w, ax
Jest jeden wyjątek od zasady łączności. Jeśli wyrażenie wymaga mnożenia i dzielenia, zawsze lepiej
najpierw wykonać mnożenie .Na przykład, dane jest wyrażenie w postaci:
W := X/Y *Z
[ Pobierz całość w formacie PDF ]
-
Linki
- Home
- Psych.9 - Psych 9 (2010) BRRip.XviD.x264.NAPiSY PL, sporo filmów do ściągnięcia
- Psych.9 - Psych 9 (2010) BRRip.XviD.NAPiSY PL, sporo filmów do ściągnięcia
- RED - Red (2010) BDRip.XviD.Napisy PL 3, sporo filmów do ściągnięcia
- RED - Red (2010) BDRip.XviD.Napisy PL, sporo filmów do ściągnięcia
- RED - Red (2010) BDRip.XviD.Napisy PL 2, sporo filmów do ściągnięcia
- Rambo-First.Blood.Part.II[1985]DvDrip-aXXo.pl, ● Filmy ●, -Rambo- kolekcja
- Radical 2017 PL, WĘDKARSTWO, Dokumenty wędkarskie PDF i KATALOGI
- Resident Evil Degeneration 2008 napisy PL, napisy do filmow
- Rambo.III[1988]DvDrip-aXXo.pl, ● Filmy ●, -Rambo- kolekcja
- Radioelektronik 03 2006, Radioelektronik 2006
- zanotowane.pl
- doc.pisz.pl
- pdf.pisz.pl
- streamer.htw.pl