,
AOA 15AOA 15, inforamtyka, art of assembly language
[ 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ĘTNASTY: CIĄGI ZNAKÓW I ZESTAWY ZNAKÓW Ciąg jest zbiorem obiektów przechowywanych w przylegających do siebie komórkach pamięci. Ciągi są zazwyczaj tablicami bajtów, słów lub (w 80386 i późniejszych procesorach) podwójnych słów Procesory z rodziny 80x86 wspierają kilka instrukcji specjalnie zaprojektowanych do kopiowania ciągów. Rozdział ten zgłębi kilka zastosowań tych instrukcji ciągów. 8088, 8086, 80186 i 80286 mogą przetwarzać dwa typy ciągów: ciągi bajtów i ciągi słów. 80386 i późniejsze procesory działają również na ciągach podwójnych słów. Mogą one przenosić ciągi, porównywać ciągi, szukać określonej wartości wewnątrz ciągu, inicjalizować ciąg stałą wartością i inne elementarne operacje na ciągach. Instrukcje ciągów 80x86 również są użyteczne dla manipulowania tablicami, tabelami i rekordami. Możemy łatwo przypisać lub porównać takie struktury danych używając instrukcji ciągów. Używając tych instrukcji możemy znacznie przyspieszyć kod działający na tablicach. 15.0 WSTĘP Rozdział ten przedstawia przegląd operacji instrukcji ciągów 80x86. Potem omawia jak przetwarzać ciągi znaków używając tych instrukcji . W końcu, omawia instrukcje ciągów dostępne w Bibliotece Standardowej UCR. Poniższe sekcje, które mają prefiks „ •” są niezbędne. Te sekcje z „⊗” omawiają zaawansowane tematy, które możemy odłożyć na później. • Instrukcje ciągów 80x86 • Ciągi znaków • Funkcje ciągów znaków • Funkcje ciągów w Bibliotece Standardowej UCR ⊗ Zastosowanie instrukcji ciągów w innych typach danych 15.1 INSTRUKCJE CIĄGÓW 80X86 Wszyscy członkowie rodziny 80x86 wspierają pięć różnych instrukcji ciągów: movs, cmps, scas, lods i stos. Są one ciągami elementarnymi ponieważ możemy zbudować większość innych operacji ciągów z tych pięciu instrukcji. Jak używać tych pięciu instrukcji , jest tematem następnych kilku sekcji. 15.1.1 JAK DZIAŁAJĄ INSTUKCJE CIĄGÓW Instrukcje ciągów działają na blokach (tablicach o liniowej ciągłości) pamięci. Na przykład, instrukcja movs przesuwa sekwencję bajtów z jednej lokacji pamięci do innej. Instrukcja cmps porównuj dwa bloki w pamięci. Instrukcja scas przeszukuje blok pamięci pod katem określonej wartości. Te instrukcje ciągów często wymagają trzech operandów, adresu bloku przeznaczenia, adresu bloku źródłowego i (opcjonalnie) elementu zliczającego. Na przykład, kiedy używamy instrukcji movs do kopiowania ciągu, potrzebujemy adresu źródłowego i licznika (liczby elementów ciągu do przesunięcia) W odróżnieniu od innych instrukcji które działają na pamięci, instrukcje ciągów są instrukcjami jednobajtowymi, które nie maja żadnych jasnych operandów. Argumenty dla instrukcji ciągów to: • Rejestr si (indeks źródła) • Rejestr di (indeks przeznaczenia • Rejestr cx (licznik) • Rejestr ax i • Flaga kierunku w rejestrze FLAGS A przykład jeden wariant instrukcji movs (przenieś ciąg) kopiuje ciąg spod adresu określonego przez ds:si do adresu określonego przez es:di, o długości cx. Podobnie instrukcja cmps porównuje ciąg wskazywany przez ds:si o długości cx z ciągiem wskazywanym przez es:di Nie wszystkie instrukcje mają operand źródłowy i przeznaczenia (tylko movs i cmps) . Na przykład instrukcja scas (przeszukaj ciąg) porównuje wartość w akumulatorze z wartością w pamięci. Pomimo różnic, instrukcje ciągów wszystkie mają jedną rzecz wspólną – używanie ich wymaga abyśmy działali na dwóch segmentach, segmencie danych i dodatkowym segmencie. 15.1.2 PRZDROSTKI REP / REPE / REPZ I REPNZ / REPNE Instrukcje ciągów same z siebie nie działają na ciągach danych. Instrukcja movs na przykład będzie przesuwała pojedynczy bajt, słowo lub podwójne słowo. Kiedy będzie się wykonywała, instrukcja movs zignoruje wartość z rejestru cx. Przedrostki powtórki mówią 80x86 aby wykonał wielobajtową operację na ciągach. Składnia dla przedrostków powtórki to: Pole: Etykieta repeat mnemonik argument ;komentarz Dla MOVS: rep movs {argumenty} Dla CMPS: repe cmps {argumenty} repz cmps {argumenty} repne cmps {argumenty} repnz cmps {argumenty} Dla SCAS: repe scas {argumenty} repz scas {argumenty} repne scas {argumenty} repnz scas {argumenty} Dla STOS: rep stos {argumenty} Zazwyczaj nie będziemy używali przedrostków powtórzenia z instrukcją lods. Jak widzimy, obecność przedrostków powtórzenia wprowadza nowe pole w lini źródłowej – pole przedrostka powtórzenia. Pole to pojawia si tylko w lini źródłowej zawierającej instrukcje ciągów. W naszym pliku źródłowym: • etykieta pola powinna zawsze zaczynać się w kolumnie jeden • pole powtórzenia powinno zaczynać się od pierwszego miejsca tabulacji , i • pole mnemonika powinno zaczynać się od drugiego miejsca tabulacji Kiedy określamy przedrostek powtórzenia przed instrukcją ciągu, instrukcja ta jest powtarzana cx razy. Bez tego przedrostka, instrukcja działa tylko na pojedynczym bajcie, słowie lub podwójnym słowie. Możemy użyć przedrostka powtórzenia do przetwarzania całego ciągu pojedyncza instrukcją. Możemy użyć instrukcji ciągów, bez przedrostka powtórzenia, jako elementarnych operacji na ciągach do zsyntetyzowania bardziej złożonych operacji na ciągach. Pole operacji jest opcjonalne. Jeśli jest obecne, MASM po prostu używa go do określenia rozmiaru ciągu na jakim będzie działał. Jeśli pole operandu jest nazwą zmiennej bajtowej, instrukcja ciągu będzie działała na bajtach. Jeśli argument jest adresem słowa, instrukcja działa na słowie. Podobnie z podwójnym słowem. Jeśli pole argumentu nie jest obecne, musimy dołączyć „B”, „W” lub „D” na końcu instrukcji ciągu dla oznaczenia rozmiaru np. movsb, movsw lub movsd. 15.1.3 FLAGA KIERUNKU Oprócz rejestrów si, di si i ax, jednym z rejestrów sterujących instrukcjami ciągów 80x86 jest rejestr flag. Ściśle, flaga kierunku w rejestrze sterującym flag, jeśli CPU przetwarza ciągi. Jeśli flaga kierunku jest wyzerowana, CPU zwiększa si i di po operacji na każdym elemencie ciągu. Na przykład, jeśli flaga kierunku jest wyzerowana, wtedy wykonanie movs przeniesie bajt, słowo lub podwójne słowo spod ds:si do es:di i zwiększy si i di o jeden, dwa lub cztery. Kiedy wyspecyfikujemy przedrostek rep przed tą instrukcją, CPU zwiększy si i di dla każdego elementu w ciągu. Po zakończeniu, rejestry si i di będą wskazywały pierwszą pozycję poza ciągiem. Jeśli flag kierunku jest ustawiona, wtedy 80x86 zmniejsza si i di po przetworzeniu każdego elementu ciągu. Po powtórzeniu operacji na ciągu, rejestry si i di będą wskazywały na pierwszy bajt lub słowo przed ciągiem, jeśli flaga kierunku była ustawiona. Flaga kierunku może być ustawiona lub zerowana przy użyciu instrukcji cld (zeruj flagę kierunku) i std (ustaw flagę kierunku). Kiedy używamy tych instrukcji wewnątrz procedury, zapamiętajmy, że modyfikują one stan maszynowy. Dlatego też musimy zachować flagę kierunku podczas wykonywania tej procedury. Poniższy przykład wskazuje rodzaje problemów jakie możemy napotkać: StringStuff: cld <jakieś operacje> call Str2 <jakiś operacje na ciągach wymagające D=0> - - - Str2 proc near Std <jakieś operacje na ciągach> ret Str2 endp Kod ten nie pracuje właściwie. Kod wywołujący zakłada, że flaga kierunku jest wyzerowana po powrocie Str2.Jednakże, nie jest to prawda. Dlatego też, operacje na ciągach wykonywane po wywołaniu Str2 nie funkcjonują poprawnie. Jest parę sposobów zadziałania z tym problemem. Pierwszy, i prawdopodobnie najbardziej oczywisty, to zawsze wprowadzać instrukcje cld i std bezpośrednio przed wykonywaniem instrukcji na ciągach. Inną alternatywą jest zachowanie i przywrócenie flagi kierunku używając instrukcji pushf i popf. Po zastosowaniu tych dwóch technik powyższy kod będzie wyglądał jak następuje: Zawsze wstawimy cld i std przed instrukcje ciągów: StringStuff: cld <jakieś operacje> call Str2 cld <operacje wymagające D = 0> - - - Str2 proc near std <jakieś operacje na ciągach> ret Str2 endp Zachowanie i przywrócenie rejestr flag: StringStuff: cld <jakieś operacje> call Str2 <operacje wymagające D = 0> - - - Str2 proc near pushf std <jakieś operacje> popf ret Str2 endp Jeśli użyjemy instrukcji pushf i popf do zachowania i przywrócenia rejestru flag, zapamiętajmy, że zachowujmy i przywracamy wszystkie flagi. Dlatego też, taki podprogram nie może zwracać żadnych informacji we flagach. Na przykład, nie będziemy mogli zwrócić warunku błędu we fladze przeniesienia jeśli użyliśmy pushf i popf. 15.1.4 INSTRUKCJA MOVS Instrukcja movs przybiera cztery podstawowe formy. Movs przenosi bajty, słowa lub podwójne słowa, movsb przesuwa ciąg bajtowy, movsw przenosi ciąg słów a movsd przenosi ciąg podwójnego słowa ( na 80386 lub późniejszych procesorach). Te cztery instrukcje używają następującej składni: {REP} MOVSB {REP} MOVSW {REP} MOVSD ;dostępna na 80386 lub późniejszych procesorach {REP} MOVS Przeznaczenie, Źródło Instrukcja movsb (przenieś ciąg bajtowy) pobiera bajt spod adresu ds.:si, przechowuje go pod adresem es:di, a potem zwiększa lub zmniejsza rejestry si i di o jeden. Jeśli jest obecny przedrostek rep, CPU sprawdza cx aby zobaczyć czy zawiera zero. Jeśli nie, wtedy przenosi bajt z ds.:si do es:di i zmniejsza rejestr cx. Ten proces będzie się powtarzał dopóki cx nie będzie zawierał zero. Instrukcja movsw (przenieś ciąg słów) pobiera słowo spod adresu ds.:si, przechowuje go pod adresem es:di a potem zwiększa lub zmniejsza si i di o dwa. Jeśli jest przedrostek rep, wtedy CPU powtarza tą procedurę tak długo jak jest to określone w cx. Instrukcja movsd działa w podobny sposób na podwójnych słowach. Zwiększa lub zmniejsza si i di o cztery dla każdego przesunięcia danych. MASM automatycznie wylicza rozmiar instrukcji movs poprzez „rzut oka” na rozmiar określonych argumentów. Jeśli zdefiniujemy dwa argumenty dyrektywą byte (lub porównywalną), wtedy MASM wygeneruje instrukcję movsb. Jeśli zadeklarujemy dwie etykiety przez word (lub porównywalne), MASM wygeneruje instrukcję movsw. Jeśli zadeklarujemy dwie etykiety jako dword, MASM wygeneruje instrukcję movsd. Asembler również sprawdzi segmenty argumentów aby zapewnić, że pasują one do aktualnie założonych (przez dyrektywę assume) rejestrów es i ds. Zawsze powinniśmy używać postaci movsb, movsw i movsd i zapomnieć o postaci movs. Chociaż teoretycznie, forma movs wydaje się być eleganckim sposobem manipulowania instrukcjami przenoszenia ciągów, w praktyce tworzy więcej problemów niż jest to warte. Co więcej ta forma instrukcji przenoszenia ciągów sugeruje, że movs ma jawne argumenty, kiedy w rzeczywistości rejestry si i di pośrednio określają argumenty. Z tego powodu zawsze używajmy instrukcji movsb, movsw i movsd. Kiedy używamy przedrostka rep, instrukcja movsb przeniesie liczę bajtów określoną w rejestrze cx. Poniższy fragment kodu kopiuje 384 bajty z String1 do String2: cld lea si, String1 lea di, String2 mov cx, 384 rep movsb - - - String1 byte 384 dup (?) String2 byte 384 dup (?) Kod ten oczywiście zakłada, że String1 i String2 są w tym samym segmencie i oba rejestry ds. i es wskazują ten segment. Jeśli zastąpimy movsb przez movsw, wtedy powyższy kod przeniesie384 słowa (768 bajtów) zamiast 384 bajtów: cld lea si, String1 lea di, String2 mov cx, 384 rep movsw - - - String1 word 384 dup (?) String2 word 384 dup (?) Pamiętamy, że cx zawiera element liczącym nie licznik bajtów. Kiedy używamy instrukcji movsw, CPU przesuwa liczbę słów określonych w rejestrze. Jeśli ustawimy flagę kierunku przed wykonaniem instrukcji movsb / movsw / movsd, CPU zmniejszy rejestry si i di po przeniesieniu każdego elementu ciągu. To znaczy, że si i di muszą wskazywać koniec ich właściwych ciągów przed wywołaniem instrukcji movsb, movsw lub movsd. Na przykład std lea si, String1+383 lea di,String2+383 mov cx, 384 rep movsb - - - String1 byte 384 dup (?) String2 byte 384 dup (?) Chociaż są chwile kiedy przetwarzanie ciągu od końca do początku jest użyteczne (zobacz opis cmps w następnej sekcji), generalnie będziemy przetwarzać ciągi w kierunku do przodu ponieważ jest to dużo prostsze do zrobienia Jest jedna klasa operacji na ciągach gdzie przetwarzanie ciągów w obu kierunkach jest całkowicie obowiązkowe: przetwarzanie ciągów kiedy blok źródłowy i przeznaczenia zachodzą na siebie. Rozważmy co zdarzy się w następującym kodzie: cld lea si, String1 lea di, String2 mov cx, 384 rep movsb - - - String1 byte ? String2 byte 384 dup (?) [ Pobierz całość w formacie PDF ] |
Podobne
|