Programowanie niskopoziomowe / Moduł 5: Techniki programowania asemblerowego: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Akokno (dyskusja | edycje)
Nie podano opisu zmian
Akokno (dyskusja | edycje)
Nie podano opisu zmian
 
(Nie pokazano 1 pośredniej wersji utworzonej przez tego samego użytkownika)
Linia 28: Linia 28:
|valign="top" width="500px"|[[Grafika:PNP_M5_S05.png]]
|valign="top" width="500px"|[[Grafika:PNP_M5_S05.png]]
|valign="top"|  
|valign="top"|  
Przykład pzedstawia trzy procedury, służące do wypisywania na konsoli danych 4-, 8- i 16-bitowych w postaci szesnastkowej.
Przykład przedstawia trzy procedury, służące do wypisywania na konsoli danych 4-, 8- i 16-bitowych w postaci szesnastkowej.


Procedura dla słowa 8-bitowego korzysta z procedury wypisywania tetrady, a procedura dla słowa 16-bitowego – z procedury dla bajtu. Argument wywołania jest przekazywany w rejestrze AX lub jego mniej znaczącej części - AL.
Procedura dla słowa 8-bitowego korzysta z procedury wypisywania tetrady, a procedura dla słowa 16-bitowego – z procedury dla bajtu. Argument wywołania jest przekazywany w rejestrze AX lub jego mniej znaczącej części - AL.
Linia 44: Linia 44:
W pierwszej wersji procedury używamy licznika pętli w jako indeksu danej. Wadami takiego rozwiązania są: konieczność użycia dodatkowej zmiennej jako licznika pętli i  użycie złożonego trybu adresowania; zaletą – potrzeba inkrementacji tylko jednej zmiennej.
W pierwszej wersji procedury używamy licznika pętli w jako indeksu danej. Wadami takiego rozwiązania są: konieczność użycia dodatkowej zmiennej jako licznika pętli i  użycie złożonego trybu adresowania; zaletą – potrzeba inkrementacji tylko jednej zmiennej.


W drugiej wersji pętla jest odliczana w dół. W porównaniu z wersją pierwszą mamy tu jeden rejstr użyty do zliczania iteracji i konieczność modyfikacji dwóch rejestrów w każdym obieu pętli.
W drugiej wersji pętla jest odliczana w dół. W porównaniu z wersją pierwszą mamy tu jeden rejestr użyty do zliczania iteracji i konieczność modyfikacji dwóch rejestrów w każdym obiegu pętli.
|}
|}
<hr width="100%">
<hr width="100%">
Linia 52: Linia 52:
Wersja trzecia używa wskaźnika danych do określenia warunku wyjścia z pętli. W tym celu przed pętlą jest wyliczana wartość wskaźnika dla stanu po ostatniej iteracji.
Wersja trzecia używa wskaźnika danych do określenia warunku wyjścia z pętli. W tym celu przed pętlą jest wyliczana wartość wskaźnika dla stanu po ostatniej iteracji.


Wersja czwarta korzysta z instrukcji iteracyjnych x86. Instrukcja LODSB ładuje bajt spd adresu zawartego w rejestrze ESI do AL i inkrementuje ESI. Instrukcja LOOP dekrementuje ECX i wykonuje skok, jeśli nie osiągnięto wartości 0. Procedura jest krótka w zapisie, ale współczesne procesory x86 wykonują instrukcje iteracyjne na ogół wolniej, niż odpowiadające im sekwencje instrukcji prostych.
Wersja czwarta korzysta z instrukcji iteracyjnych x86. Instrukcja LODSB ładuje bajt spod adresu zawartego w rejestrze ESI do AL i inkrementuje ESI. Instrukcja LOOP dekrementuje ECX i wykonuje skok, jeśli nie osiągnięto wartości 0. Procedura jest krótka w zapisie, ale współczesne procesory x86 wykonują instrukcje iteracyjne na ogół wolniej, niż odpowiadające im sekwencje instrukcji prostych.
|}
|}
<hr width="100%">
<hr width="100%">
Linia 80: Linia 80:
|valign="top" width="500px"|[[Grafika:PNP_M5_S12.png]]
|valign="top" width="500px"|[[Grafika:PNP_M5_S12.png]]
|valign="top"|  
|valign="top"|  
...
Przesunięcie o jeden bit danej wielokrotnej precyzji można uzyskać poprzez przesunięcie pierwszego elementu, w wyniku czego bit „wysuwany” trafi do znacznika przeniesienia, a następnie użycie instrukcji rotacji przez przeniesienie. Podczas kolejnych rotacji bit z poprzedniej fazy jest wsuwany do słowa poddawanego rotacji, a bit wysuwany ze słowa jest zapamiętywany w znaczniku przeniesienia.
 
W postaci ogólnej, dla danej o dowolnej długości, do przesuwania używamy wyłącznie instrukcji rotacji przez przeniesienie, a przed rozpoczęciem przesuwania ustawiamy znacznik przeniesienia w stan zero. Można to zrobić przy użyciu dedykowanej instrukcji zerowania przeniesienia lub poprzez wykonanie dowolnej instrukcji logicznej (np. OR AL, AL).
 
W procesorach rodziny x86 od modelu 386 w górę istnieją specjalne instrukcje przesunięć 64-bitowych z zapisem 32 bitów wyniku – SHLD i SHRD. Można ich użyć do syntezy przesunięć  wielokrotnej precyzji o dowolną liczbę bitów (<32).
|}
|}
<hr width="100%">
<hr width="100%">
Linia 92: Linia 96:
|valign="top" width="500px"|[[Grafika:PNP_M5_S14.png]]
|valign="top" width="500px"|[[Grafika:PNP_M5_S14.png]]
|valign="top"|  
|valign="top"|  
...
Przykład ilustruje usunięcie skoku poprzez dość wyrafinowane zastosowanie instrukcji korekcji dziesiętnej. Sekwencja czterech pierwszych instrukcji procedury hex_digit zamienia wartość mniej znaczącej tetrady rejestru AL na jej reprezentację znakową w postaci cyfry szesnastkowej w kodzie ASCII.
|}
|}
<hr width="100%">
<hr width="100%">
Linia 104: Linia 108:
|valign="top" width="500px"|[[Grafika:PNP_M5_S16.png]]
|valign="top" width="500px"|[[Grafika:PNP_M5_S16.png]]
|valign="top"|  
|valign="top"|  
...
Kończąca procedurę hex_word sekwencja CALL hex_byte; RET została zastąpiona instrukcją skoku JMP hex_byte. Ponieważ etykieta hex_byte występuje bezpośrednio po tej instrukcji – instrukcja skoku może zostać usunięta.  
 
Analogicznego zabiegu dokonano w procedurze hex_byte.
 
Procedura hex_digit kończy się skokiem do procedury wyświetlania znaku.
 
W ten sposób powstaje interesująca konstrukcja programu – procedury wywołującej swoje własne zakończenie. Konstrukcja taka nie ma swojego odpowiednika we współczesnych językach wysokiego poziomu.
|}
|}
<hr width="100%">
<hr width="100%">

Aktualna wersja na dzień 14:24, 18 paź 2006


...


...


...


...


Przykład przedstawia trzy procedury, służące do wypisywania na konsoli danych 4-, 8- i 16-bitowych w postaci szesnastkowej.

Procedura dla słowa 8-bitowego korzysta z procedury wypisywania tetrady, a procedura dla słowa 16-bitowego – z procedury dla bajtu. Argument wywołania jest przekazywany w rejestrze AX lub jego mniej znaczącej części - AL.


...


W pierwszej wersji procedury używamy licznika pętli w jako indeksu danej. Wadami takiego rozwiązania są: konieczność użycia dodatkowej zmiennej jako licznika pętli i użycie złożonego trybu adresowania; zaletą – potrzeba inkrementacji tylko jednej zmiennej.

W drugiej wersji pętla jest odliczana w dół. W porównaniu z wersją pierwszą mamy tu jeden rejestr użyty do zliczania iteracji i konieczność modyfikacji dwóch rejestrów w każdym obiegu pętli.


Wersja trzecia używa wskaźnika danych do określenia warunku wyjścia z pętli. W tym celu przed pętlą jest wyliczana wartość wskaźnika dla stanu po ostatniej iteracji.

Wersja czwarta korzysta z instrukcji iteracyjnych x86. Instrukcja LODSB ładuje bajt spod adresu zawartego w rejestrze ESI do AL i inkrementuje ESI. Instrukcja LOOP dekrementuje ECX i wykonuje skok, jeśli nie osiągnięto wartości 0. Procedura jest krótka w zapisie, ale współczesne procesory x86 wykonują instrukcje iteracyjne na ogół wolniej, niż odpowiadające im sekwencje instrukcji prostych.


...


...


Dodawanie 64-bitowe jest realizowane poprzez dodawanie mniej znaczących słów 32-bitowych, a następnie dodawanie bardziej znaczących słów z uwzględnieniem przeniesienia wchodzącego (instrukcja ADC), wygenerowanego w pierwszym dodawaniu.

W analogiczny sposób można zrealizować odejmowanie, zastępując instrukcję ADD instrukcją SUB,a instrukcję ADC - instrukcją SBB (odejmowania z uwzględnieniem pożyczki wchodzącej).

Przy uogólnieniu dla liczb o dowolnej długości, sumowanie poszczególnych fragmentów danej jest realizowane w pętli przy użyciu instrukcji ADC, a przed pętlą znacznik przeniesienia jest zerowany. Należy zadbać, by żadna inna instrukcja w pętli nie modyfikowała znacznika przeniesienia W tym celu do inkrementacji adresów o 4 użyto instrukcji LEA.


Przesunięcie o jeden bit danej wielokrotnej precyzji można uzyskać poprzez przesunięcie pierwszego elementu, w wyniku czego bit „wysuwany” trafi do znacznika przeniesienia, a następnie użycie instrukcji rotacji przez przeniesienie. Podczas kolejnych rotacji bit z poprzedniej fazy jest wsuwany do słowa poddawanego rotacji, a bit wysuwany ze słowa jest zapamiętywany w znaczniku przeniesienia.

W postaci ogólnej, dla danej o dowolnej długości, do przesuwania używamy wyłącznie instrukcji rotacji przez przeniesienie, a przed rozpoczęciem przesuwania ustawiamy znacznik przeniesienia w stan zero. Można to zrobić przy użyciu dedykowanej instrukcji zerowania przeniesienia lub poprzez wykonanie dowolnej instrukcji logicznej (np. OR AL, AL).

W procesorach rodziny x86 od modelu 386 w górę istnieją specjalne instrukcje przesunięć 64-bitowych z zapisem 32 bitów wyniku – SHLD i SHRD. Można ich użyć do syntezy przesunięć wielokrotnej precyzji o dowolną liczbę bitów (<32).


...


Przykład ilustruje usunięcie skoku poprzez dość wyrafinowane zastosowanie instrukcji korekcji dziesiętnej. Sekwencja czterech pierwszych instrukcji procedury hex_digit zamienia wartość mniej znaczącej tetrady rejestru AL na jej reprezentację znakową w postaci cyfry szesnastkowej w kodzie ASCII.


...


Kończąca procedurę hex_word sekwencja CALL hex_byte; RET została zastąpiona instrukcją skoku JMP hex_byte. Ponieważ etykieta hex_byte występuje bezpośrednio po tej instrukcji – instrukcja skoku może zostać usunięta.

Analogicznego zabiegu dokonano w procedurze hex_byte.

Procedura hex_digit kończy się skokiem do procedury wyświetlania znaku.

W ten sposób powstaje interesująca konstrukcja programu – procedury wywołującej swoje własne zakończenie. Konstrukcja taka nie ma swojego odpowiednika we współczesnych językach wysokiego poziomu.


...


...