ROZDZIAŁ PIĄTY ZMIENNE I STRUKTURY DANYCH, INFORMATYKA, „THE ART OF ASSEMBLY LANGUAGE” [PL]
[ Pobierz całość w formacie PDF ]
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
WYŁĄCZNOŚĆ DO PUBLIKOWANIA TEGO TŁUMACZENIA
POSIADA
RAG
„THE ART OF ASSEMBLY LANGUAGE”
tłumaczone by KREMIK
Konsultacje naukowe: NEKRO
wankenob@priv5.onet.pl
nekro@pf.pl
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
ROZDZIAŁ PIĄTY:
ZMIENNE I STRUKTURY DANYCH
Rozdział Pierwszy omawiał podstawowe formaty danych w pamięci. Rozdział Trzeci omawiał jak system
komputerowy fizycznie organizuje te dane. Ten rozdział kończy to omawianie poprzez połączenie koncepcji
reprezentacji danych z ich rzeczywistą fizyczną reprezentacją. Jak sugeruje tytuł, ten rozdział, zajmie się dwoma
głównymi tematami: zmiennymi i strukturami danych. Ten rozdział nie zakłada, że mamy jakąś znajomość struktur
danych ,chociaż taka umiejętność byłaby użyteczna.
5.0 WSTĘP
Ten rozdział omawia jak deklarować i uzyskać dostęp do zmiennych skalarnych, całkowitych,
rzeczywistych, typów danych, wskaźników, tablic i struktur. Musimy opanować te tematy przed przejściem do
następnego rozdziału. Deklarowanie i dostęp do tablic, wydaje się być problematyczne dla początkującego
programisty asemblerowego. Jednakże, reszta tego tekstu zależy od zrozumienia tych struktur danych i ich
reprezentacji w pamięci .Nie próbuj się prześlizgiwać przez ten materiał oczekując ,że nauczysz się go jeśli będziesz
go potrzebował później .Potrzebujesz go teraz a próbowanie nauczyć się tego materiału wraz z późniejszymi
materiałami, tylko pogmatwa ci w głowie.
5.1 KILKA DODATKOWYCH INSTRUKCJI:LEA,LES,ADD i MUL
Celem tego rozdziału nie jest przedstawienie zbioru instrukcji 80x86.Jednak,są cztery instrukcje dodatkowe
które okażą swoją przydatność przy omawianiu reszty tego rozdziału. Są to instrukcje
ładuj adres efektywny (lea),
ładuj adres dalekiego wskaźnika używając ES (LES), dodawanie całkowite (ADD)
i
mnożenie bez znaku
(MUL)
.Te instrukcje razem z instrukcją mov dostarczają wszystkich niezbędnych możliwości przy dostępie do
różnych typów danych omawianych w tym rozdziale.
Instrukcja lea przyjmuje formę:
lea reg
16
, pamięć
reg
16
jest 16 bitowym rejestrem ogólnego przeznaczenia. Pamięć, jest to komórka pamięci reprezentowana przez bajt
mod/reg/rm (poza tym musi być komórką pamięci ,nie może być rejestrem).
Ta instrukcja ładuje do 16 bitowego rejestru offset z komórki wyspecyfikowanej przez operand pamięci.
lea ax,1000h[bx][si],na przykład, załaduje ax adresem z komórki pamięci wskazywanej przez 1000h[bx][si].Jest to
oczywiście wartość 1000h+bx+si.Lea jest również całkiem użyteczną przy uzyskiwaniu adresu zmiennej. Jeśli mamy
gdzieś w pamięci zmienną I, lea bx, I załaduje do rejestru bx adres (offset) z I.
Instrukcja les przyjmuje formę:
les reg
16
, pamięć
32
Ta instrukcja ładuje rejestr es i jeden z 16 bitowych rejestrów ogólnego przeznaczenia z
wyspecyfikowanego adresu pamięci. Zauważmy, że każdy adres pamięci który możemy wyszczególnić bajtem
mod/reg/rm jest prawidłowy, ale tak jak dla instrukcji lea musi to być komórka pamięci nie rejestr.
Instrukcja les ładuje wyszczególniony rejestr ogólnego przeznaczenia słowem spod danego adresu ,ładuje rejestr es z
następnego słowa w pamięci. Ta instrukcja, i jej towarzysz lds (który ładuje ds.) są tylko instrukcjami dla maszyn
80386,które manipulują 32 bitami na raz.
Instrukcja add, podobnie jak jej odpowiednik w x86,dodaje dwie wartości w 80x86.Ta instrukcja przyjmuje
kilka form. Jest pięć form na których się tu skoncentrujemy. Są to:
add reg, reg
add reg, pamięć
add pamięć, reg
add reg, stała
add pamięć ,stała
Wszystkie te instrukcje dodają drugi operand do pierwszego, sumę zachowując w pierwszym operandzie. Na
przykład, add bx,5,oblicza bx:=bx+5.
Ostatnią instrukcją jaką się zajmiemy jest instrukcja mul (mnożenie).Ta instrukcja ma tylko jeden operand i
przybiera formę:
mul reg / pamięć.
Jest wiele ważnych szczegółów dotyczących mul, które w tym rozdziale pominiemy. Ze względu na
omówienie, które nastąpi, założymy, że rejestr lub komórka pamięci jest 16 bitowym rejestrem lub komórką pamięci.
W takim przypadku ta instrukcja oblicza dx:ax;=ax*reg/mem. Zauważmy, że nie ma bezpośredniego trybu dla tej
instrukcji.
5.2 DEKLAROWANIE ZMIENNYCH W PROGRAMIE JĘZYKA ASSEMBLERA
Chociaż prawdopodobnie się już domyślamy, że komórki pamięci i zmienne są w pewnym stopniu
powiązane, ten rozdział nie będzie wychodził poza wyciąganie silnych podobieństw między nimi dwoma. Cóż, czas
naprawić tą sytuację. Rozważmy następujący krótki (i bezużyteczny) program pascalowski:
program useless (input, output)
var i,j:integer;
begin
i:=10;
write (‘Podaj wartość dla j:’);
readln (j);
i:=i*j+j*j;
wrtieln(‘Wynik to’,j);
end.
Kiedy komputer wykona wyrażenie i:=10,zrobi kopię wartości 10 i jakoś zapamiętuję tą wartość dla
późniejszego użycia. Osiągamy to tak, że kompilator rezerwuje miejsce w pamięci, specjalnie dla wyłącznego
użytkowania zmiennej i .Zakładając, że kompilator określił arbitralnie lokację DS:10h dla naszego celu, możemy
użyć instrukcji mov ds:[10h],10 aby to osiągnąć. Jeśli i jest szesnastobitowym słowem, kompilator prawdopodobnie
przydzieli zmiennej j słowo startowe od lokacji 12h lub 0Eh.Zakładając,że jest to lokacja 12h,drugie wyznaczone
wyrażenie w tym programie mogłoby wyglądać jak następuje:
mov ax, ds:[10h] ;pobiera wartość z I
mul ds:[12h] ;mnoży przez j
mov ds:[10h],ax ;przechowuje w I (pomija przepełnienie)
mov ax, ds:[12h] ;pobiera J
mul ds:[12h] ;Oblicza J*J
add ds:[10h],ax ;Dodaje I*J+J*J, przechowuje w I
Chociaż jest kilka brakujących szczegółów w tym kodzie, jest dosyć prosty i możemy łatwo zobaczyć co będzie robił
ten program.
Teraz wyobraźmy sobie 5000 linijek programu takich jak ten, używający zmiennych takich jak:
ds:[10h],ds:[12h],ds:[14h] itd. Czy chcielibyśmy umiejscowić wyrażenie, tam gdzie przypadkowo przechowujemy
wynik obliczenia w j zamiast i? Dlaczego powinniśmy się martwić nawet ,że zmienna i jest pod lokacją 10h a j pod
12h?Dlaczego nie powinniśmy używać nazw takich jak i i j zamiast niepokoić się o te adresy numeryczne? Wydaje
się sensowne przepisanie tego powyższego kodu tak:
mov ax, i
mul j
mov i, ax
mov ax, j
mul j
add i, ax
Oczywiście możemy tak zrobić w języku asemblera! Istotnie, jedną z podstawowych zalet asemblera
takiego jak MASM, jest to, że pozwala nam na użycie nazw symbolicznych dla komórek pamięci. Ponadto asembler
będzie nawet przydzielał lokacje do nazw automatycznie. Nie potrzebujemy się martwić faktem, że zmienna i jest
rzeczywiście słowem z komórki pamięci DS:10h chyba ,że jesteśmy ciekawscy.
Nie będzie to dla nas żadna niespodzianka, że ds będzie wskazywał na dseg segment w pliku SHELL.ASM.
Istotnie, skonfigurujemy tak ds żeby wskazywał dseg jako jedną z pierwszych rzeczy kiedy wykona się główny
program SHELL.ASM. Dlatego też, wszystko co musimy zrobić to powiedzieć asemblerowi żeby zarezerwował
jakieś komórki dal naszych zmiennych w dseg i połączył offset wymienionych zmiennych z nazwami tych
zmiennych. Jest to bardzo prosty proces i jest tematem kilku następnych sekcji.
5.3 DEKLAROWANIE I DOSTĘP DO ZMIENNYCH SKALARNYCH
Skalarne zmienne przechowują proste wartości. Zmienne i i j z poprzedniej sekcji są przykładem zmiennych
skalarnych. Przykładami struktur danych ,które nie są skalarne są tablice ,rekordy ,zbiory i listy .Te ostatnie typy
danych są tworzone z wartości skalarnych. Są one typami zbiorowymi .Zobaczymy, typy zbiorowe trochę później
;najpierw musimy nauczyć się czegoś o typach skalarnych.
Aby zadeklarować zmienną w dseg, musimy użyć wyrażenia takiego jak następujące:
ByteVar byte ?
ByteVar jest etykietą. Powinna się zaczynać w pierwszej kolumnie
w segmencie dseg (to jest ,między wyrażeniami
segmentem dseg i dseg ends).Dowiemy się wszystkiego o etykietach w kilku rozdziałach ,teraz możemy założyć, że
większość poprawnych identyfikatorów w Pascalu/C/Adzie jest również ważnymi etykietami języka asemblera.
Jeśli potrzebujemy więcej niż jedną zmienną w naszym programie, wprowadzamy dodatkową linię w
segmencie dseg deklarującą te zmienne. MASM automatycznie przydzieli unikalne lokacji dla zmiennych (czyż nie
byłoby zbyt dobrze mieć i i j umiejscowione teraz pod tym samym adresem?).Po deklaracji wymienionych
zmiennych, MASM pozwala nam odnosić się do tych zmiennych przez nazwy zamiast przez lokacje w programie
.Na przykład, po wprowadzeniu powyższych wyrażeń do segmentu danych (dseg),możemy używać instrukcji takich
jak mov ByteVar, al. w programie.
Pierwszej zmiennej, którą umieścimy w segmencie danych zostaje przydzielona komórka pamięci
DS:0.Następnej zmiennej w pamięci zostaje przydzielona komórka za poprzednią zmienną. Na przykład, jeśli
zmienna spod lokacji zero była zmienną bajtową ,następnej zmiennej zostaje przydzielona komórka pamięci spod
DS:1.Jednak,jeśli pierwsza zmienna była słowem ,drugiej zmiennej zostaje przydzielona komórka pamięci DS:2
.MASM zawsze uważnie przydziela zmienne ,w taki sposób aby one na siebie wzajemnie nie zachodziły .Rozważmy
następującą definicję dseg:
dseg
segment
para public ‘data’
bytevar
byte
?
;bajt rezerwuje bajty
worvar
word
?
;word rezerwuje słowa
dwordvar
dword
?
;dword rezerwuje podwójne słowo
byte2
byte
?
word2
word
?
dseg ends
MASM przydziela pamięć dla bytevar dla lokacji DS:0.Ponieważ bytevar jest długości jednego bajta
,następną dostępną komórką pamięci będzie DS:1.MASM,zatem,przydzieli pamięć dla wordvar od lokacji
DS:1.Ponieważ słowo wymaga dwóch bajtów ,następna dostępna komórka pamięci po wordvar to DS:3,dla której
MASM przydziela dwordvar. Dwordvar jest długości czterech bajtów, więc MASM przydziela pamięć dla byte 2
zaczynając od DS:7.Podobnie MASM przydziela pamięć dla word2 od lokacji DS:8.gdybyśmy wpisali inną zmienną
po word2,MASM przydzieliłby dla niej lokację DS:0A.
Kiedy będziemy się odnosić do jednej z powyższych nazw, MASM automatycznie zastąpi stosowny offset.
Na przykład, MASM przetłumaczy instrukcję mov ax, wordvar jako mov ax ,ds:[1].Więc teraz możemy używać
nazw symbolicznych i kompletnie pominąć fakt, że te zmienne są w rzeczywistości komórkami pamięci z
odpowiednimi offsetami w segmencie danych.
5.3.1 DEKLAROWANIE I UŻYWANIE ZMIENNYCH BYTE
Więc po co są właściwie zmienne? Cóż ,możemy oczywiście przedstawiać różne typy danych, które mają
mniej niż 256 różnych wartości w pojedynczym bajcie. Obejmuje to kilka ważnych i często używanych typów
danych wliczając w to typ danych znakowych, typ danych boolowskich, większość typów danych wyliczeniowych i
mały typ danych całkowitych (ze znakiem i bez znaku),
wystarczy tylko wymienić.
Znaki w typowym kompatybilnym z IBM systemie używają ośmiobitowego zestawu znaków ASCII/IBM
(zobacz „A Zestaw znaków ASCII/IBM „) 80x86 dostarcza bogatego zbioru instrukcji do manipulowania danymi
znakowymi .Nie jest to żadna niespodzianka ,że większość zmiennych bajtowych przechowuje dane znakowe.
Typ danych boolowskich przedstawia tylko dwie wartości :prawda lub fałsz. Zatem, do przedstawienia
wartości boolowskich wykorzystujemy pojedynczy bit.Jednakże,80x86 w rzeczywistości chce pracować z danymi
przynajmniej o szerokości ośmiu bitów. W rzeczywistości używa
ekstra kod do manipulowania pojedynczym bitem
zamiast całym bajtem Dlatego ,powinniśmy używać całego bajtu dla przedstawiania wartości boolowskiej.
Większość programistów używa wartości zero dla przedstawienia fałszu i jeden dla przedstawiania prawdy.
Znacznik zera 80x86 wykonuje testy zero / nie zero bardzo łatwo .Zauważmy ,że ten wybór zera lub nie-zera jest
głównie dla wygody. Możemy używać każdej z dwóch wartości (lub dwóch różnych zbiorów wartości) dla
przedstawienia prawdy lub fałszu.
Większość języków wysokiego poziomu, które wspierają typ danych wyliczeniowych przekształca je (dla
użytku wewnętrznego) na liczby całkowite bez znaku .Pierwsza pozycja na liście to w zasadzie pozycja zero ,druga
pozycja na liście to pozycja jeden, trzecia pozycja do dwa itd.) Na przykład, rozważmy następujący pascalowski typ
wyliczeniowy:
Kolory = (czerwony,niebieski,zielony,purpurowy,pomarańczowy,żółt,biały,czarny);
Większość kompilatorów Pascala przydzieli wartość zero do czerwonego, jeden do niebieskiego itd.
Później zobaczymy jak w rzeczywistości tworzy się własne dane wyliczeniowe w asemblerze. Wszystko
czego potrzebujemy teraz, to jak przydzielić pamięć dla zmiennych które przechowują wartości wyliczeniowe
.Ponieważ jest niemożliwe ,aby było więcej niż 256 pozycji danych wyliczeniowych ,możemy użyć pojedynczej
zmiennej bajtowej dla przechowywania wartości. Jeśli ,powiedzmy ,mamy zmienną kolor typu kolory użyjemy
instrukcji mov color,2,co oznacza to samo co kolor :=zielony w Pascalu.(Później nauczymy się jak używać bardziej
sensownych wyrażeń, takich jak mov kolor, zielony dla przydzielenia koloru zielonego do zmiennej kolor).
Oczywiście, jeśli mamy małą wartość całkowitą bez znaku (0..255) lub małą wartość całkowitą ze znakiem
(-128..127) pojedyncza zmienna bajtowa jest najlepszym sposobem w większości przypadków
.
Zauważmy ,że
większość programistów traktuje wszystkie typy danych z wyjątkiem liczb całkowitych ze znakiem jako wartości
nieoznaczone. To znaczy, znaki ,wartości boolowskie, typy wyliczeniowe i liczby całkowite bez znaku są zawsze
wartościami bez znakowymi. W bardzo specjalnym przypadku możemy potraktować znak jako wartość że znakiem,
ale większość czasu znaki są wartościami bez znakowymi.
Są trzy główne wyrażenia dla deklaracji zmiennej bajtowej w programie. Oto one:
identyfikator db ?
identyfikator byte ?
identyfikator sbyte ?
Identyfikator przedstawia nazwę naszej zmiennej bajtowej. ”db” jest starszym terminem, z przed pojawienia się
MASM 6.x.Zobaczymy,że tej dyrektywy używano w innych programach (zwłaszcza tych, które nie używają MASM
6.x lub późniejszych) ale Microsoft uznał, że będzie to termin przestarzały; powinniśmy w zamian używać deklaracji
byte lub sbyte.
Deklaracja byte deklaruje zmienną bajtową bez znaku. Powinniśmy używać tej deklaracji dla wszystkich
zmiennych bajtowych z wyjątkiem małych liczb całkowitych ze znakiem. Dla liczb całkowitych ze znakiem
używamy dyrektywy sbyte (bajt ze znakiem).
Kiedy zadeklarujemy jakieś zmienne bajtowe w tych wyrażeniach, możemy odnosić się do tych zmiennych
wewnątrz naszego programu poprzez ich nazwy:
i
db
?
j
byte
?
k
sbyte
?
-
-
-
mov
i,0
mov
j,245
mov
k,-5
mov
al. ,i
mov
j, al.
itd.
Chociaż MASM 6,x wykonuje małą ilość sprawdzeń zgodności typów, nie powinniśmy ulec wrażeniu, że
język asemblera jest językiem z silną kontrola typów. Faktycznie MASM 6.x sprawdza tylko wartości które
przemieszczasz by sprawdzić ,czy będą się mieścić w lokacji docelowej. Wszystkie następujące instrukcje są
poprawne w MASM 6.x:
mov k,255
mov j,-5
mov i,-127
Ponieważ wszystkie z tych zmiennych są zmiennymi wielkości bajtu i wszystkie stale skojarzone i dopasowane do
ośmiu bitów, MASM na szczęście zezwala na każde z tych wyrażeń. Jeszcze jeśli patrzymy na nie, są one logicznie
niepoprawne. Co to znaczy przesunięcie -5 do zmiennej bajtowej bez znakowej? Ponieważ wartość bajtu ze znakiem
musi być z zakresu od -128..127,co się zdarzy, kiedy przechowamy wartość 255 wewnątrz zmiennej bajtowej ze
znakiem? Cóż ,MASM, po prostu skonwertuje te wartości do ich ośmiobitowych odpowiedników (-5 staje się
0FBh,255 staje się ) FFh [-1],itd.).
Być może późniejsze wersje MASM wprowadzą silniejsze badanie zgodności typów wartości, które
wkładamy do tych zmiennych
albo nie. Jednakże ,powinniśmy zawsze pamiętać ,że zawsze będzie możliwe
pominięcie tego sprawdzenia. Pozwoli nam to na pisanie poprawnych programów. Asembler nie pomaga nam tak
jak Pascal czy Ada. Oczywiście, nawet jeśli asembler odrzuci takie wyrażenie, będzie łatwo obejść zgodność typów.
Rozpatrzmy następującą sekwencję:
mov
-
;jakaś liczba wyrażeń które nie wpływają na AL.
-
mov j, al.
Niestety nie ma sposobu, żeby asembler mógł nas poinformować, że przechowujemy nieprawidłową wartość w j.
Rejestry ,z natury rzeczy, nie są znakowe ani bez znakowe. Dlatego też. asembler pozwala przechować rejestr
wewnątrz zmiennej bez względu na wartość jaka może być w rejestrze.
Chociaż asembler nie sprawdza czy oba operandy instrukcji są ze znakiem czy bez znaku, z dużą pewnością
sprawdza ich rozmiar. Jeśli rozmiar nie zgadza się asembler zgłosi stosowny komunikat błędu. Następujące
przykłady są niepoprawne:
mov i, ax ;nie można przenieść 16 bitów do ośmiu
mov i,300 ;300 przekracza 8 bitów
mov k,-130 ;-130 przekracza osiem bitów
Możemy zapytać „jeśli asembler rzeczywiście nie rozróżnia wartości ze znakiem i bez znaku, dlaczego
zawracamy sobie nimi głowę? Dlaczego nie używać po prostu db cały czas? ”Cóż, są dwie przyczyny. Po pierwsze
uczyni to nasze programy łatwiejsze do odczytania i zrozumienia jeśli jasno określimy
(poprzez użycie byte i sbyte)
które zmienne są ze znakiem a które bez znaku. Po drugie, kto mówił coś ,że asembler ignoruje czy zmienne są ze
znakiem czy bez znaku? Instrukcja mov ignoruje ale są inne instrukcje ,które nie ignorują.
W punkcie końcowym warto wspomnieć o sprawach dotyczących deklarowania zmiennych bajtowych .We
wszystkich deklaracjach widzimy, że pole operandu instrukcji zawsze zawiera pytajnik. Pytajnik mówi asemblerowi,
że zmienna powinna być pozostawiona niezainicjowaną
kiedy DOS ładuje program do pamięci. Możemy
wyspecyfikować wartość początkową dla zmiennej, która może być ładowana do pamięci przed rozpoczęciem
wykonywania programu., poprzez zastąpienie znaku zapytania naszą wartością początkową. Rozważmy następującą
deklarację zmiennej bajtowej:
i db 0
j byte 255
k sbyte -1
W tym przykładzie, asembler inicjuje odpowiednio i, j i k zerem,255 i -1,kiedy program ładuje się do pamięci. Ten
fakt okaże całą swoją użyteczność nieco później, zwłaszcza kiedy będziemy omawiali tablice .Asembler tylko
sprawdzi rozmiar operandu Nie sprawdza ,aby upewnić się, że operand dla dyrektywy byte jest pozytywny lub, że
wartość pola operandu sbyte jest z zakresu -128..127.MASM pozwala na wartość z zakresu -128..255 w polu
operandu każdego z tych wyrażeń.
W przypadku, gdy odniesiemy wrażenie, że nie istnieje rzeczywisty powód używania byte i sbyte w
programie, powinniśmy zauważyć, że MASM czasami ignoruje różnice w tych definicjach .Debugger Microsoft
CodeView nie. Jeśli zadeklarujemy zmienną jako wartość ze znakiem, CodeView wyświetli go jako taki (wliczając
w to znak minus jeśli to konieczne).Z drugiej strony CodeView zawsze wyświetla zmienne db i byte jako wartości
dodatnie.
5.3.2 DEKLAROWANIE I UŻYWANIE ZMIENNEJ WORD
Większość programów 80x86 używa wartości słowa dla trzech rzeczy: 16 bitowych wartości całkowitych ze
znakiem,16 bitowych wartości całkowitych bez znaku i offsetów (wskaźników).Z pewnością możemy używać słowa
al.,-5
[ 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
- Read Japanese Today, Języki obce, ćĄćśŹčŞź
- zanotowane.pl
- doc.pisz.pl
- pdf.pisz.pl
- streamer.htw.pl