44
Wydawnictwo Helion ul. Chopina 6 44-100 Gliwice tel. (32)230-98-63 e-mail: [email protected] PRZYK£ADOWY ROZDZIA£ PRZYK£ADOWY ROZDZIA£ IDZ DO IDZ DO ZAMÓW DRUKOWANY KATALOG ZAMÓW DRUKOWANY KATALOG KATALOG KSI¥¯EK KATALOG KSI¥¯EK TWÓJ KOSZYK TWÓJ KOSZYK CENNIK I INFORMACJE CENNIK I INFORMACJE ZAMÓW INFORMACJE O NOWOCIACH ZAMÓW INFORMACJE O NOWOCIACH ZAMÓW CENNIK ZAMÓW CENNI K CZYTELNIA CZYTELNIA FRAGMENTY KSI¥¯EK ONLINE FRAGMENTY KSI¥¯EK ONLINE SPIS TRECI SPIS TRECI DODAJ DO KOSZYKA DODAJ DO KOSZYKA KATALOG ONLINE KATALOG ONLINE Asembler. Sztuka programowania Ksi¹¿ka „Asembler. Sztuka programowania” to podrêcznik programowania w jêzyku asemblera oparty na jêzyku HLA. Opisuje 32-bitow¹ architekturê procesorów Intel oraz zasady tworzenia programów w jêzyku HLA. Przedstawia sposoby pisania, kompilacji i uruchamiania programów wykorzystuj¹cych ró¿ne, nawet najbardziej z³o¿one typy danych. • Wprowadzenie do jêzyka HLA • Sposoby reprezentacji danych • Organizacja pamiêci i tryby adresowania • Typy danych • Podzia³ programu na procedury i modu³y • Sterowanie wykonaniem programu • Instrukcje arytmetyczne • Operacje na plikach • Operacje bitowe i ³añcuchowe • Makrodefinicje • Klasy i obiekty • Po³¹czenie asemblera z programami w innych jêzykach Przekonaj siê, jak prosty jest jêzyk asemblera Autor: Randall Hyde T³umaczenie: Przemys³aw Szeremiota ISBN: 83-7361-602-0 Tytu³ orygina³u: The Art of Assembly Language Format: B5, stron: 968

Asembler. Sztuka programowania

Embed Size (px)

DESCRIPTION

Kompletny przewodnik po języku asemblera Asembler, mimo że jest zdecydowanie trudniejszy w nauce niż języki programowania wysokiego poziomu, jest wśród programisów bardzo popularny. Tworzone w nim programy są mniejsze, zajmują mniej pamięci i miejsca na dysku, a przede wszystkim działają szybciej. Opracowano język HLA, dzięki któremu programowanie w asemblerze przebiega płynnie i polega na analizie implementacji poszczególnych, wysokopoziomowych konstrukcji, znanych z języków takich jak C, C++ czy Pascal. Książka "Asembler. Sztuka programowania" to podręcznik programowania w języku asemblera oparty na języku HLA. Opisuje 32-bitową architekturę procesorów Intel oraz zasady tworzenia programów w języku HLA. Przedstawia sposoby pisania, kompilacji i uruchamiania programów wykorzystujących różne, nawet najbardziej złożone typy danych. * Wprowadzenie do języka HLA* Sposoby reprezentacji danych* Organizacja pamięci i tryby adresowania* Typy danych* Podział programu na procedury i moduły* Sterowanie wykonaniem programu* Instrukcje arytmetyczne* Operacje na plikach* Operacje bitowe i łańcuchowe* Makrodefinicje* Klasy i obiekty* Połączenie asemblera z programami w innych językachPrzekonaj się, jak prosty jest język asemblera.

Citation preview

Page 1: Asembler. Sztuka programowania

Wydawnictwo Helion

ul. Chopina 6

44-100 Gliwice

tel. (32)230-98-63

e-mail: [email protected]

PRZYK£ADOWY ROZDZIA£PRZYK£ADOWY ROZDZIA£

IDZ DOIDZ DO

ZAMÓW DRUKOWANY KATALOGZAMÓW DRUKOWANY KATALOG

KATALOG KSI¥¯EKKATALOG KSI¥¯EK

TWÓJ KOSZYKTWÓJ KOSZYK

CENNIK I INFORMACJECENNIK I INFORMACJE

ZAMÓW INFORMACJEO NOWO�CIACH

ZAMÓW INFORMACJEO NOWO�CIACH

ZAMÓW CENNIKZAMÓW CENNIK

CZYTELNIACZYTELNIA

FRAGMENTY KSI¥¯EK ONLINEFRAGMENTY KSI¥¯EK ONLINE

SPIS TRE�CISPIS TRE�CI

DODAJ DO KOSZYKADODAJ DO KOSZYKA

KATALOG ONLINEKATALOG ONLINE

Asembler.Sztuka programowania

Ksi¹¿ka „Asembler. Sztuka programowania” to podrêcznik programowania w jêzyku

asemblera oparty na jêzyku HLA. Opisuje 32-bitow¹ architekturê procesorów Intel

oraz zasady tworzenia programów w jêzyku HLA. Przedstawia sposoby pisania,

kompilacji i uruchamiania programów wykorzystuj¹cych ró¿ne, nawet najbardziej

z³o¿one typy danych.

• Wprowadzenie do jêzyka HLA

• Sposoby reprezentacji danych

• Organizacja pamiêci i tryby adresowania

• Typy danych

• Podzia³ programu na procedury i modu³y

• Sterowanie wykonaniem programu

• Instrukcje arytmetyczne

• Operacje na plikach

• Operacje bitowe i ³añcuchowe

• Makrodefinicje

• Klasy i obiekty

• Po³¹czenie asemblera z programami w innych jêzykach

Przekonaj siê, jak prosty jest jêzyk asemblera

Autor: Randall Hyde

T³umaczenie: Przemys³aw Szeremiota

ISBN: 83-7361-602-0

Tytu³ orygina³u: The Art of Assembly Language

Format: B5, stron: 968

Page 2: Asembler. Sztuka programowania

Spis treści

Rozdział 1. Wstęp do języka asemblerowego...................................................... 131.1. Wprowadzenie........................................................................................................ 131.2. Anatomia programu HLA....................................................................................... 131.3. Uruchamianie pierwszego programu HLA ............................................................. 161.4. Podstawowe deklaracje danych programu HLA..................................................... 171.5. Wartości logiczne ................................................................................................... 191.6. Wartości znakowe................................................................................................... 201.7. Rodzina procesorów 80x86 firmy Intel .................................................................. 21

1.7.1. Podsystem obsługi pamięci........................................................................... 241.8. Podstawowe instrukcje maszynowe........................................................................ 261.9. Podstawowe struktury sterujące wykonaniem programu HLA............................... 30

1.9.1. Wyrażenia logiczne w instrukcjach HLA ..................................................... 311.9.2. Instrukcje if..then..elseif..else..endif języka HLA......................................... 331.9.3. Iloczyn, suma i negacja w wyrażeniach logicznych...................................... 351.9.4. Instrukcja while ............................................................................................ 371.9.5. Instrukcja for................................................................................................. 381.9.6. Instrukcja repeat............................................................................................ 391.9.7. Instrukcje break oraz breakif......................................................................... 401.9.8. Instrukcja forever.......................................................................................... 401.9.9. Instrukcje try, exception oraz endtry............................................................. 41

1.10. Biblioteka standardowa języka HLA — wprowadzenie ......................................... 441.10.1. Stałe predefiniowane w module stdio ........................................................... 461.10.2. Standardowe wejście i wyjście programu..................................................... 461.10.3. Procedura stdout.newln................................................................................. 471.10.4. Procedury stdout.putiN................................................................................. 471.10.5. Procedury stdout.putiNSize .......................................................................... 481.10.6. Procedura stdout.put ..................................................................................... 491.10.7. Procedura stdin.getc...................................................................................... 511.10.8. Procedury stdin.getiN ................................................................................... 521.10.9. Procedury stdin.readLn i stdin.flushInput..................................................... 53

1.10.10. Procedura stdin.get ....................................................................................... 541.11. Jeszcze o ochronie wykonania kodu w bloku try..endtry........................................ 55

1.11.1. Zagnieżdżone bloki try..endtry ..................................................................... 561.11.2. Klauzula unprotected bloku try..endtry......................................................... 581.11.3. Klauzula anyexception bloku try..endtry ...................................................... 611.11.4. Instrukcja try..endtry i rejestry...................................................................... 61

1.12. Język asemblerowy a język HLA ........................................................................... 631.13. Źródła informacji dodatkowych.............................................................................. 64

Page 3: Asembler. Sztuka programowania

4 Asembler. Sztuka programowania

Rozdział 2. Reprezentacja danych...................................................................... 652.1. Wprowadzenie........................................................................................................ 652.2. Systemy liczbowe ................................................................................................... 66

2.2.1. System dziesiętny — przypomnienie............................................................ 662.2.2. System dwójkowy......................................................................................... 662.2.3. Formaty liczb dwójkowych........................................................................... 68

2.3. System szesnastkowy.............................................................................................. 692.4. Organizacja danych ................................................................................................ 72

2.4.1. Bity ............................................................................................................... 722.4.2. Półbajty......................................................................................................... 732.4.3. Bajty ............................................................................................................. 732.4.4. Słowa ............................................................................................................ 752.4.5. Podwójne słowa ............................................................................................ 762.4.6. Słowa poczwórne i długie............................................................................. 77

2.5. Operacje arytmetyczne na liczbach dwójkowych i szesnastkowych....................... 772.6. Jeszcze o liczbach i ich reprezentacji...................................................................... 782.7. Operacje logiczne na bitach.................................................................................... 812.8. Operacje logiczne na liczbach dwójkowych i ciągach bitów.................................. 842.9. Liczby ze znakiem i bez znaku ............................................................................... 86

2.10. Rozszerzanie znakiem, rozszerzanie zerem, skracanie, przycinanie....................... 912.11. Przesunięcia i obroty .............................................................................................. 952.12. Pola bitowe i dane spakowane................................................................................ 992.13. Wprowadzenie do arytmetyki zmiennoprzecinkowej ........................................... 104

2.13.1. Formaty zmiennoprzecinkowe przyjęte przez IEEE................................... 1082.13.2. Obsługa liczb zmiennoprzecinkowych w języku HLA............................... 112

2.14. Reprezentacja liczb BCD ..................................................................................... 1152.15. Znaki .................................................................................................................... 117

2.15.1. Zestaw znaków ASCII................................................................................ 1172.15.2. Obsługa znaków ASCII w języku HLA...................................................... 121

2.16. Zestaw znaków Unicode....................................................................................... 1252.17. Źródła informacji dodatkowych............................................................................ 126

Rozdział 3. Dostęp do pamięci i jej organizacja ................................................ 1273.1. Wprowadzenie...................................................................................................... 1273.2. Tryby adresowania procesorów 80x86 ................................................................. 127

3.2.1. Adresowanie przez rejestr........................................................................... 1283.2.2. 32-bitowe tryby adresowania procesora 80x86........................................... 129

3.3. Organizacja pamięci fazy wykonania ................................................................... 1353.3.1. Obszar kodu................................................................................................ 1373.3.2. Obszar zmiennych statycznych................................................................... 1393.3.3. Obszar niemodyfikowalny .......................................................................... 1403.3.4. Obszar danych niezainicjalizowanych ........................................................ 1413.3.5. Atrybut @nostorage.................................................................................... 1413.3.6. Sekcja deklaracji var ................................................................................... 1423.3.7. Rozmieszczenie sekcji deklaracji danych w programie HLA ..................... 143

3.4. Przydział pamięci dla zmiennych w programach HLA......................................... 1443.5. Wyrównanie danych w programach HLA ............................................................ 1463.6. Wyrażenia adresowe............................................................................................. 1493.7. Koercja typów ...................................................................................................... 1513.8. Koercja typu rejestru ............................................................................................ 1543.9. Pamięć obszaru stosu oraz instrukcje push i pop.................................................. 155

3.9.1. Podstawowa postać instrukcji push............................................................. 1553.9.2. Podstawowa postać instrukcji pop .............................................................. 1573.9.3. Zachowywanie wartości rejestrów za pomocą instrukcji push i pop .......... 158

Page 4: Asembler. Sztuka programowania

Spis treści 5

3.9.4. Stos jako kolejka LIFO ............................................................................... 1593.9.5. Pozostałe wersje instrukcji obsługi stosu.................................................... 1613.9.6. Usuwanie danych ze stosu bez ich zdejmowania ........................................ 1633.9.7. Odwoływanie się do danych na stosie bez ich zdejmowania ...................... 165

3.10. Dynamiczny przydział pamięci — obszar pamięci sterty ..................................... 1663.11. Instrukcje inc oraz dec.......................................................................................... 1713.12. Pobieranie adresu obiektu..................................................................................... 1713.13. Źródła informacji dodatkowych............................................................................ 172

Rozdział 4. Stałe, zmienne i typy danych.......................................................... 1734.1. Wprowadzenie...................................................................................................... 1734.2. Kilka dodatkowych instrukcji: intmul, bound i into ............................................. 1744.3. Typ tbyte............................................................................................................... 1784.4. Deklaracje stałych i zmiennych w języku HLA.................................................... 178

4.4.1. Typy stałych ............................................................................................... 1824.4.2. Literały stałych łańcuchowych i znakowych............................................... 1834.4.3. Stałe łańcuchowe i napisowe w sekcji const ............................................... 1854.4.4. Wyrażenia stałowartościowe....................................................................... 1864.4.5. Wielokrotne sekcje const i ich kolejność w programach HLA ................... 1894.4.6. Sekcja val programu HLA .......................................................................... 1904.4.7. Modyfikowanie obiektów sekcji val w wybranym miejscu

kodu źródłowego programu ....................................................................... 1914.5. Sekcja type programu HLA .................................................................................. 1924.6. Typy wyliczeniowe w języku HLA ...................................................................... 1934.7. Typy wskaźnikowe ............................................................................................... 194

4.7.1. Wskaźniki w języku asemblerowym........................................................... 1964.7.2. Deklarowanie wskaźników w programach HLA ........................................ 1974.7.3. Stałe wskaźnikowe i wyrażenia stałych wskaźnikowych............................ 1974.7.4. Zmienne wskaźnikowe a dynamiczny przydział pamięci ........................... 1994.7.5. Typowe błędy stosowania wskaźników ...................................................... 200

4.8. Moduł chars.hhf biblioteki standardowej HLA .................................................... 2054.9. Złożone typy danych ............................................................................................ 207

4.10. Łańcuchy znaków................................................................................................. 2084.11. Łańcuchy w języku HLA...................................................................................... 2104.12. Odwołania do poszczególnych znaków łańcucha................................................. 2174.13. Moduł strings biblioteki standardowej HLA i procedury manipulacji łańcuchami...2194.14. Konwersje wewnątrzpamięciowe ......................................................................... 2314.15. Zbiory znaków...................................................................................................... 2324.16. Implementacja zbiorów znaków w języku HLA................................................... 2334.17. Literały, stałe i wyrażenia zbiorów znaków w języku HLA ................................. 2354.18. Operator in w wyrażeniach logicznych

wysokopoziomowego rozszerzenia języka HLA.................................................. 2374.19. Obsługa zbiorów znaków w bibliotece standardowej HLA.................................. 2374.20. Wykorzystywanie zbiorów znaków w programach HLA..................................... 2414.21. Tablice.................................................................................................................. 2434.22. Deklarowanie tablic w programach HLA............................................................. 2444.23. Literały tablicowe................................................................................................. 2454.24. Odwołania do elementów tablicy jednowymiarowej ............................................ 246

4.24.1. Porządkowanie tablicy wartości ................................................................. 2484.25. Tablice wielowymiarowe ..................................................................................... 250

4.25.1. Wierszowy układ elementów tablicy .......................................................... 2514.25.2. Kolumnowy układ elementów tablicy ........................................................ 255

4.26. Przydział pamięci dla tablic wielowymiarowych ................................................. 2564.27. Odwołania do elementów tablic wielowymiarowych w języku asemblerowym... 258

Page 5: Asembler. Sztuka programowania

6 Asembler. Sztuka programowania

4.28. Duże tablice i MASM (tylko dla programistów systemu Windows) .................... 2594.29. Rekordy (struktury) .............................................................................................. 2604.30. Stałe rekordowe.................................................................................................... 2634.31. Tablice rekordów.................................................................................................. 2644.32. Wykorzystanie tablic i rekordów w roli pól rekordów ......................................... 2654.33. Ingerowanie w przesunięcia pól rekordów ........................................................... 2694.34. Wyrównanie pól w ramach rekordu...................................................................... 2704.35. Wskaźniki na rekordy........................................................................................... 2714.36. Unie ...................................................................................................................... 2734.37. Unie anonimowe................................................................................................... 2754.38. Typy wariantowe.................................................................................................. 2764.39. Stałe unii............................................................................................................... 2774.40. Przestrzenie nazw ................................................................................................. 2784.41. Tablice dynamiczne w języku asemblerowym ..................................................... 2814.42. Obsługa tablic w bibliotece standardowej języka HLA........................................ 2844.43. Źródła informacji dodatkowych............................................................................ 287

Rozdział 5. Procedury i moduły ........................................................................ 2895.1. Wprowadzenie...................................................................................................... 2895.2. Procedury.............................................................................................................. 2895.3. Zachowywanie stanu systemu .............................................................................. 2925.4. Przedwczesny powrót z procedury ....................................................................... 2965.5. Zmienne lokalne ................................................................................................... 2975.6. Symbole lokalne i globalne obiektów innych niż zmienne................................... 3035.7. Parametry.............................................................................................................. 304

5.7.1. Przekazywanie przez wartość ..................................................................... 3055.7.2. Przekazywanie przez adres ......................................................................... 308

5.8. Funkcje i wartości funkcji .................................................................................... 3115.8.1. Zwracanie wartości funkcji......................................................................... 3125.8.2. Złożenie instrukcji języka HLA.................................................................. 3135.8.3. Atrybut @returns procedur języka HLA..................................................... 316

5.9. Rekurencja............................................................................................................ 3185.10. Deklaracje zapowiadające .................................................................................... 3225.11. Procedury w ujęciu niskopoziomowym — instrukcja call.................................... 3235.12. Rola stosu w procedurach..................................................................................... 3255.13. Rekordy aktywacji................................................................................................ 3285.14. Standardowa sekwencja wejścia do procedury ..................................................... 3315.15. Standardowa sekwencja wyjścia z procedury....................................................... 3335.16. Niskopoziomowa implementacja zmiennych automatycznych ............................ 3345.17. Niskopoziomowa implementacja parametrów procedury..................................... 336

5.17.1. Przekazywanie argumentów w rejestrach ................................................... 3375.17.2. Przekazywanie argumentów w kodzie programu........................................ 3405.17.3. Przekazywanie argumentów przez stos....................................................... 342

5.18. Wskaźniki na procedury ....................................................................................... 3655.19. Parametry typu procedurowego............................................................................ 3685.20. Nietypowane parametry wskaźnikowe ................................................................. 3705.21. Zarządzanie dużymi projektami programistycznymi............................................ 3715.22. Dyrektywa #include ............................................................................................. 3725.23. Unikanie wielokrotnego włączania do kodu tego samego pliku........................... 3745.24. Moduły a atrybut @external ................................................................................. 375

5.24.1. Działanie atrybutu @external ..................................................................... 3805.24.2. Pliki nagłówkowe w programach HLA....................................................... 382

5.25. Jeszcze o problemie zaśmiecania przestrzeni nazw .............................................. 3845.26. Źródła informacji dodatkowych............................................................................ 386

Page 6: Asembler. Sztuka programowania

Spis treści 7

Rozdział 6. Arytmetyka.................................................................................... 3896.1. Wprowadzenie...................................................................................................... 3896.2. Zestaw instrukcji arytmetycznych procesora 80x86 ............................................. 389

6.2.1. Instrukcje mul i imul................................................................................... 3896.2.2. Instrukcje div i idiv..................................................................................... 3936.2.3. Instrukcja cmp ............................................................................................ 3966.2.4. Instrukcje setXX ......................................................................................... 4016.2.5. Instrukcja test.............................................................................................. 403

6.3. Wyrażenia arytmetyczne ...................................................................................... 4046.3.1. Proste przypisania ....................................................................................... 4056.3.2. Proste wyrażenia ......................................................................................... 4066.3.3. Wyrażenia złożone...................................................................................... 4086.3.4. Operatory przemienne................................................................................. 413

6.4. Wyrażenia logiczne .............................................................................................. 4146.5. Idiomy maszynowe a idiomy arytmetyczne.......................................................... 417

6.5.1. Mnożenie bez stosowania instrukcji mul, imul i intmul.............................. 4176.5.2. Dzielenie bez stosowania instrukcji div i idiv............................................. 4196.5.3. Zliczanie modulo n za pośrednictwem instrukcji and ................................. 4206.5.4. Nieostrożne korzystanie z idiomów maszynowych .................................... 420

6.6. Arytmetyka zmiennoprzecinkowa ........................................................................ 4216.6.1. Rejestry jednostki zmiennoprzecinkowej.................................................... 4216.6.2. Typy danych jednostki zmiennoprzecinkowej ............................................ 4296.6.3. Zestaw instrukcji jednostki zmiennoprzecinkowej ..................................... 4306.6.4. Instrukcje przemieszczania danych............................................................. 4316.6.5. Instrukcje konwersji.................................................................................... 4336.6.6. Instrukcje arytmetyczne .............................................................................. 4366.6.7. Instrukcje porównań ................................................................................... 4426.6.8. Instrukcje ładowania stałych na stos koprocesora....................................... 4456.6.9. Instrukcje funkcji przestępnych .................................................................. 445

6.6.10. Pozostałe instrukcje jednostki zmiennoprzecinkowej ................................. 4476.6.11. Instrukcje operacji całkowitoliczbowych.................................................... 449

6.7. Tłumaczenie wyrażeń arytmetycznych na kod maszynowyjednostki zmiennoprzecinkowej........................................................................... 4496.7.1. Konwersja notacji wrostkowej do odwrotnej notacji polskiej..................... 4516.7.2. Konwersja odwrotnej notacji polskiej do kodu języka asemblerowego...... 453

6.8. Obsługa arytmetyki zmiennoprzecinkowej w bibliotece standardowej języka HLA.....4556.8.1. Funkcje stdin.getf i fileio.getf ..................................................................... 4556.8.2. Funkcje trygonometryczne modułu math.................................................... 4556.8.3. Funkcje wykładnicze i logarytmiczne modułu math................................... 456

6.9. Podsumowanie...................................................................................................... 458

Rozdział 7. Niskopoziomowe struktury sterujące wykonaniem programu............ 4597.1. Wprowadzenie...................................................................................................... 4597.2. Struktury sterujące niskiego poziomu................................................................... 4607.3. Etykiety instrukcji................................................................................................. 4607.4. Bezwarunkowy skok do instrukcji (instrukcja jmp) ............................................. 4627.5. Instrukcje skoku warunkowego ............................................................................ 4657.6. Struktury sterujące „średniego” poziomu — jt i jf................................................ 4687.7. Implementacja popularnych struktur sterujących w języku asemblerowym......... 4697.8. Wstęp do podejmowania decyzji .......................................................................... 469

7.8.1. Instrukcje if..then..else................................................................................ 4717.8.2. Tłumaczenie instrukcji if języka HLA na język asemblerowy.................... 4757.8.3. Obliczanie wartości złożonych wyrażeń logicznych

— metoda pełnego szacowania wartości wyrażenia................................... 480

Page 7: Asembler. Sztuka programowania

8 Asembler. Sztuka programowania

7.8.4. Skrócone szacowanie wyrażeń logicznych ................................................. 4817.8.5. Wady i zalety metod szacowania wartości wyrażeń logicznych ................. 4837.8.6. Efektywna implementacja instrukcji if w języku asemblerowym............... 4857.8.7. Instrukcje wyboru ....................................................................................... 490

7.9. Skoki pośrednie a automaty stanów...................................................................... 5007.10. Kod spaghetti........................................................................................................ 5037.11. Pętle...................................................................................................................... 504

7.11.1. Pętle while .................................................................................................. 5057.11.2. Pętle repeat..until ........................................................................................ 5067.11.3. Pętle nieskończone ..................................................................................... 5087.11.4. Pętle for ...................................................................................................... 5087.11.5. Instrukcje break i continue.......................................................................... 5097.11.6. Pętle a rejestry ............................................................................................ 513

7.12. Optymalizacja kodu.............................................................................................. 5147.12.1. Obliczanie warunku zakończenia pętli na końcu pętli ................................ 5157.12.2. Zliczanie licznika pętli wstecz .................................................................... 5177.12.3. Wstępne obliczanie niezmienników pętli.................................................... 5187.12.4. Rozciąganie pętli ........................................................................................ 5197.12.5. Zmienne indukcyjne ................................................................................... 521

7.13. Mieszane struktury sterujące w języku HLA........................................................ 5227.14. Źródła informacji dodatkowych............................................................................ 524

Rozdział 8. Pliki .............................................................................................. 5258.1. Wprowadzenie...................................................................................................... 5258.2. Organizacja plików............................................................................................... 525

8.2.1. Pliki jako listy rekordów............................................................................. 5268.2.2. Pliki tekstowe a pliki binarne...................................................................... 528

8.3. Pliki sekwencyjne ................................................................................................. 5308.4. Pliki dostępu swobodnego .................................................................................... 5388.5. Indeksowany sekwencyjny dostęp do pliku (ISAM) ............................................ 5438.6. Przycinanie pliku .................................................................................................. 5468.7. Źródła informacji dodatkowych............................................................................ 548

Rozdział 9. Zaawansowane obliczenia w języku asemblerowym......................... 5499.1. Wprowadzenie...................................................................................................... 5499.2. Operacje o zwielokrotnionej precyzji ................................................................... 550

9.2.1. Obsługa operacji zwielokrotnionej precyzjiw bibliotece standardowej języka HLA...................................................... 550

9.2.2. Dodawanie liczb zwielokrotnionej precyzji ................................................ 5539.2.3. Odejmowanie liczb zwielokrotnionej precyzji............................................ 5569.2.4. Porównanie wartości o zwielokrotnionej precyzji ...................................... 5589.2.5. Mnożenie operandów zwielokrotnionej precyzji ........................................ 5629.2.6. Dzielenie wartości zwielokrotnionej precyzji ............................................. 5659.2.7. Negacja operandów zwielokrotnionej precyzji ........................................... 5759.2.8. Iloczyn logiczny operandów zwielokrotnionej precyzji.............................. 5779.2.9. Suma logiczna operandów zwielokrotnionej precyzji................................. 577

9.2.10. Suma wyłączająca operandów zwielokrotnionej precyzji........................... 5789.2.11. Inwersja operandów zwielokrotnionej precyzji .......................................... 5789.2.12. Przesunięcia bitowe operandów zwielokrotnionej precyzji ........................ 5789.2.13. Obroty operandów zwielokrotnionej precyzji............................................. 5839.2.14. Operandy zwielokrotnionej precyzji w operacjach wejścia-wyjścia........... 583

9.3. Manipulowanie operandami różnych rozmiarów.................................................. 6049.4. Arytmetyka liczb dziesiętnych.............................................................................. 606

9.4.1. Literały liczb BCD...................................................................................... 6089.4.2. Instrukcje maszynowe daa i das.................................................................. 608

Page 8: Asembler. Sztuka programowania

Spis treści 9

9.4.3. Instrukcje maszynowe aaa, aas, aam i aad .................................................. 6109.4.4. Koprocesor a arytmetyka spakowanych liczb dziesiętnych ........................ 612

9.5. Obliczenia w tabelach........................................................................................... 6159.5.1. Wyszukiwanie w tabeli wartości funkcji .................................................... 6159.5.2. Dopasowywanie dziedziny ......................................................................... 6209.5.3. Generowanie tabel wartości funkcji............................................................ 6219.5.4. Wydajność odwołań do tabel przeglądowych............................................. 625

9.6. Źródła informacji dodatkowych............................................................................ 625

Rozdział 10.Makrodefinicje i język czasu kompilacji .......................................... 62710.1. Wprowadzenie...................................................................................................... 62710.2. Język czasu kompilacji — wstęp.......................................................................... 62710.3. Instrukcje #print i #error....................................................................................... 62910.4. Stałe i zmienne czasu kompilacji.......................................................................... 63110.5. Wyrażenia i operatory czasu kompilacji............................................................... 63210.6. Funkcje czasu kompilacji ..................................................................................... 635

10.6.1. Funkcje czasu kompilacji — konwersja typów........................................... 63610.6.2. Funkcje czasu kompilacji — obliczenia numeryczne ................................. 63810.6.3. Funkcje czasu kompilacji — klasyfikacja znaków ..................................... 63810.6.4. Funkcje czasu kompilacji — manipulacje łańcuchami znaków.................. 63910.6.5. Funkcje czasu kompilacji — dopasowywanie wzorców............................. 63910.6.6. Odwołania do tablicy symboli .................................................................... 64110.6.7. Pozostałe funkcje czasu kompilacji ............................................................ 64310.6.8. Konwersja typu stałych napisowych........................................................... 643

10.7. Kompilacja warunkowa........................................................................................ 64510.8. Kompilacja wielokrotna (pętle czasu kompilacji)................................................. 65010.9. Makrodefinicje (procedury czasu kompilacji) ...................................................... 653

10.9.1. Makrodefinicje standardowe....................................................................... 65410.9.2. Argumenty makrodefinicji.......................................................................... 65610.9.3. Symbole lokalne makrodefinicji ................................................................. 66310.9.4. Makrodefinicje jako procedury czasu kompilacji ....................................... 66610.9.5. Symulowane przeciążanie funkcji .............................................................. 667

10.10. Tworzenie programów czasu kompilacji .............................................................. 67210.10.1. Generowanie tabel wartości funkcji............................................................ 67310.10.2. Rozciąganie pętli ........................................................................................ 677

10.11. Stosowanie makrodefinicji w osobnych plikach kodu źródłowego ...................... 67910.12. Źródła informacji dodatkowych ........................................................................... 679

Rozdział 11.Manipulowanie bitami .................................................................... 68111.1. Wprowadzenie...................................................................................................... 68111.2. Czym są dane bitowe? .......................................................................................... 68111.3. Instrukcje manipulujące bitami............................................................................. 68311.4. Znacznik przeniesienia w roli akumulatora bitów ................................................ 69211.5. Wstawianie i wyodrębnianie łańcuchów bitów .................................................... 69311.6. Scalanie zbiorów bitów i rozpraszanie łańcuchów bitowych................................ 69611.7. Spakowane tablice łańcuchów bitowych .............................................................. 69911.8. Wyszukiwanie bitów ............................................................................................ 70111.9. Zliczanie bitów..................................................................................................... 704

11.10. Odwracanie łańcucha bitów.................................................................................. 70711.11. Scalanie łańcuchów bitowych .............................................................................. 70911.12. Wyodrębnianie łańcuchów bitów ......................................................................... 71011.13. Wyszukiwanie wzorca bitowego .......................................................................... 71211.14. Moduł bits biblioteki standardowej HLA ............................................................. 71311.15. Źródła informacji dodatkowych ........................................................................... 715

Page 9: Asembler. Sztuka programowania

10 Asembler. Sztuka programowania

Rozdział 12. Operacje łańcuchowe..................................................................... 71712.1. Wprowadzenie...................................................................................................... 71712.2. Instrukcje łańcuchowe procesorów 80x86............................................................ 717

12.2.1. Sposób działania instrukcji łańcuchowych ................................................. 71812.2.2. Przedrostki instrukcji łańcuchowych — repX ............................................ 71912.2.3. Znacznik kierunku ...................................................................................... 71912.2.4. Instrukcja movs........................................................................................... 72112.2.5. Instrukcja cmps........................................................................................... 72712.2.6. Instrukcja scas............................................................................................. 73112.2.7. Instrukcja stos............................................................................................. 73212.2.8. Instrukcja lods ............................................................................................ 73312.2.9. Instrukcje lods i stos w złożonych operacjach łańcuchowych .................... 733

12.3. Wydajność instrukcji łańcuchowych procesorów 80x86...................................... 73412.4. Źródła informacji dodatkowych............................................................................ 735

Rozdział 13. Instrukcje MMX............................................................................. 73713.1. Wprowadzenie...................................................................................................... 73713.2. Sprawdzanie obecności rozszerzenia MMX......................................................... 73813.3. Środowisko programowania MMX ...................................................................... 739

13.3.1. Rejestry MMX............................................................................................ 73913.3.2. Typy danych MMX .................................................................................... 741

13.4. Przeznaczenie instrukcji zestawu MMX............................................................... 74213.5. Arytmetyka z nasycaniem a arytmetyka z zawijaniem ......................................... 74313.6. Operandy instrukcji MMX ................................................................................... 74413.7. Instrukcje zestawu MMX ..................................................................................... 746

13.7.1. Instrukcje transferu danych......................................................................... 74713.7.2. Instrukcje konwersji.................................................................................... 74713.7.3. Arytmetyka operandów spakowanych ........................................................ 75213.7.4. Instrukcje logiczne...................................................................................... 75513.7.5. Instrukcje porównań ................................................................................... 75613.7.6. Instrukcje przesunięć bitowych................................................................... 76013.7.7. Instrukcja emms.......................................................................................... 762

13.8. Model programowania MMX............................................................................... 76313.9. Źródła informacji dodatkowych............................................................................ 774

Rozdział 14. Klasy i obiekty............................................................................... 77514.1. Wprowadzenie...................................................................................................... 77514.2. Wstęp do programowania obiektowego................................................................ 77514.3. Klasy w języku HLA............................................................................................ 77914.4. Obiekty ................................................................................................................. 78214.5. Dziedziczenie ....................................................................................................... 78414.6. Przesłanianie......................................................................................................... 78514.7. Metody wirtualne a procedury statyczne .............................................................. 78614.8. Implementacje metod i procedur klas ................................................................... 78814.9. Implementacja obiektu ......................................................................................... 793

14.9.1. Tabela metod wirtualnych .......................................................................... 79614.9.2. Reprezentacja w pamięci obiektu klasy pochodnej..................................... 798

14.10. Konstruktory i inicjalizacja obiektów................................................................... 80214.10.1. Konstruktor a dynamiczny przydział obiektu ............................................. 80414.10.2. Konstruktory a dziedziczenie...................................................................... 80614.10.3. Parametry konstruktorów i przeciążanie procedur klas .............................. 810

14.11. Destruktory........................................................................................................... 81114.12. Łańcuchy _initialize_ oraz _finalize_ w języku HLA .......................................... 81214.13. Metody abstrakcyjne ............................................................................................ 818

Page 10: Asembler. Sztuka programowania

Spis treści 11

14.14. Informacja o typie czasu wykonania (RTTI) ........................................................ 82214.15. Wywołania metod klasy bazowej ......................................................................... 82414.16. Źródła informacji dodatkowych ........................................................................... 825

Rozdział 15. Na styku asemblera i innych języków programowania...................... 82715.1. Wprowadzenie...................................................................................................... 82715.2. Łączenie kodu HLA i kodu asemblera MASM bądź Gas ..................................... 827

15.2.1. Kod asemblera MASM (Gas) rozwijany w kodzie języka HLA................. 82815.2.2. Konsolidacja modułów MASM (Gas) z modułami HLA............................ 832

15.3. Moduły HLA a programy języka Delphi (Kylix) ................................................. 83715.3.1. Konsolidacja modułów HLA z programami języka Delphi (Kylix) ........... 83815.3.2. Zachowywanie wartości rejestrów.............................................................. 84215.3.3. Wartości zwracane funkcji.......................................................................... 84315.3.4. Konwencje wywołań .................................................................................. 84915.3.5. Przekazywanie argumentów przez wartość i adres,

parametry niemodyfikowalne i wyjściowe ................................................. 85415.3.6. Skalarne typy danych w językach HLA i Delphi (Kylix) ........................... 85615.3.7. Przekazywanie łańcuchów znaków z Delphi do procedury HLA ............... 85815.3.8. Przekazywanie rekordów z programu w języku Delphi do kodu HLA....... 86215.3.9. Przekazywanie zbiorów z programu w języku Delphi do kodu HLA......... 866

15.3.10. Przekazywanie tablic z programu w języku Delphi do kodu HLA ............. 86715.3.11. Odwołania do obiektów programu pisanego w Delphi w kodzie HLA....... 867

15.4. Moduły HLA a programy języków C i C++......................................................... 87015.4.1. Konsolidacja modułów języka HLA z programami języków C i C++........ 87215.4.2. Zachowywanie wartości rejestrów.............................................................. 87515.4.3. Wartości funkcji ......................................................................................... 87615.4.4. Konwencje wywołań .................................................................................. 87615.4.5. Tryby przekazywania argumentów............................................................. 88015.4.6. Odwzorowanie typów skalarnych

pomiędzy językiem C (C++) a językiem HLA ........................................... 88115.4.7. Przekazywanie łańcuchów znaków

pomiędzy programem w języku C (C++) a modułem HLA........................ 88315.4.8. Przekazywanie rekordów

pomiędzy programem w języku C (C++) a modułem HLA........................ 88315.4.9. Przekazywanie tablic

pomiędzy programem w języku C (C++) a modułem HLA........................ 88615.5. Źródła informacji dodatkowych............................................................................ 886

Dodatek A Tabela kodów ASCII....................................................................... 887

Dodatek B Instrukcje procesorów 80x86......................................................... 891

Skorowidz...................................................................................... 927

Page 11: Asembler. Sztuka programowania

1.9. Podstawowe struktury sterujące

wykonaniem programu HLA

Instrukcje ���, ��� oraz ���, choć niezwykle użyteczne, nie wystarczają do napisaniasensownych programów. Aby takie programy mogły powstać, podstawowe instrukcjemuszą zostać uzupełnione o podstawowy zbiór struktur sterujących wykonaniem koduumożliwiających tworzenie pętli i podejmowanie decyzji w programie. Język HLAprzewiduje szereg wysokopoziomowych struktur sterujących wykonaniem charaktery-stycznych dla języków wyższego niż asembler poziomu. Programista HLA ma więc dodyspozycji konstrukcje ��� �������������������, � �������� ��, ������������ i tak dalej. Dopiero znajomość i umiejętność wykorzystywania owych konstrukcjipozwala na stworzenie prawdziwego programu.

Przed zagłębieniem się w cechach struktur sterujących należałoby podkreślić, że nie mająone odpowiedników w zestawie instrukcji maszynowych procesorów 80x86. Kompi-lator HLA tłumaczy owe struktury do postaci szeregu prawdziwych instrukcji maszyno-wych za programistę. Sposób, w jaki to czyni, opisany zostanie w dalszej części książki;wtedy też Czytelnik nauczy się pisać kod w czystym języku asemblerowym, bez wy-korzystania wysokopoziomowych cech kompilatora HLA. Na razie jednak, z racjiniewielkiego jeszcze zasobu wiedzy o języku asemblerowym, nieco sztuczne strukturysterujące HLA będą bardzo pomocne.

Warto przy tym pamiętać, że wysokopoziomowe na pierwszy rzut oka struktury sterującenie są w istocie tak wysokopoziomowe. Mają one bowiem jedynie ułatwić rozpoczęciepisania programów w języku asemblerowym — w żadnym razie nie zastępują asem-blerowych instrukcji sterujących wykonaniem kodu. Już wkrótce okaże się więc, że owewysokopoziomowe struktury sterujące mają szereg ograniczeń, które w miarę nabie-rania przez Czytelnika doświadczenia i w miarę komplikowania kolejnych programówstaną się dość uciążliwe. To zresztą efekt zamierzony — po osiągnięciu pewnego poziomuumiejętności programista sam stwierdzi, że w wielu przypadkach wygoda (i czytelnośćprogramu) wynikająca z zastosowania struktur sterujących HLA nie rekompensujeutraty efektywności, charakterystycznej dla kodu wykorzystującego wprost instrukcjemaszynowe procesora.

Niniejsze omówienie kierowane jest do tych Czytelników, którzy znają już przynajmniejjeden z popularnych języków wysokiego poziomu. Znajomość ta znakomicie uprościprezentację struktur sterujących wykonaniem programu HLA, pozwalając na pominięcieopisów sposobów zastosowania owych struktur w typowych zadaniach programistycz-nych. Czytelnicy potrafiący korzystać ze struktur sterujących wykonaniem programuw dowolnym z języków wysokiego poziomu nie powinni być zaskoczeni, kiedy odkryją,że analogiczne struktury wykorzystywane są w programach HLA w sposób dosłownieidentyczny.

Page 12: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 31

1.9.1. Wyrażenia logiczne w instrukcjach HLA

Niektóre z instrukcji sterujących wykonaniem programu HLA wymagają określeniaw miejsce operandu wartości logicznej („prawda” bądź „fałsz”); ścieżka wykonania pro-gramu zależy wtedy od wartości wyrażenia logicznego. Przykładami struktur wyma-gających określenia wyrażenia logicznego są instrukcje , � �� oraz ������������.Pierwszą oznaką ograniczeń struktur sterujących wykonaniem programu HLA jestskładnia owych wyrażeń. W tym jednym miejscu nawyki wyniesione z języków wyso-kiego poziomu zwracają się przeciwko programiście asemblera: w języku HLA wyra-żenia logiczne są bardzo ograniczone — nie sposób konstruować tu wyrażeń równiewymyślnych, z jakimi może mieć do czynienia programista języka C++.

Wyrażenia logiczne w języku HLA mogą przyjąć jedną z następujących postaci1:

����������������������������������� ���������������� �������������� �������������� ������������� ����� ������������������������� �������������������� ������������������������������� �������������������� ������

Znacznik może zostać określony za pośrednictwem jednego z symboli wyliczonychw tabeli 1.2.

Tabela 1.2. Symbole reprezentujące znaczniki

Symbol Znacznik Znaczenie

� Przeniesienie „Prawda” dla ustawionego (1) znacznika przeniesienia;

„fałsz” dla wyzerowanego (0) znacznika przeniesienia.

�� Brak przeniesienia „Prawda” dla wyzerowanego (0) znacznika przeniesienia;

„fałsz” dla ustawionego (1) znacznika przeniesienia.

� Zero „Prawda” dla ustawionego (1) znacznika zera; „fałsz”

dla wyzerowanego (0) znacznika zera.

�� Brak zera „Prawda” dla wyzerowanego (0) znacznika zera; „fałsz”

dla ustawionego (1) znacznika zera.

�� Przepełnienie „Prawda” dla ustawionego (1) znacznika przepełnienia;

„fałsz” dla wyzerowanego (0) znacznika przepełnienia.

��� Brak przepełnienia „Prawda” dla wyzerowanego (0) znacznika przepełnienia;

„fałsz” dla ustawionego (1) znacznika przepełnienia.

�� Znak „Prawda” dla ustawionego (1) znacznika znaku; „fałsz”

dla wyzerowanego (0) znacznika znaku.

��� Brak znaku „Prawda” dla wyzerowanego (0) znacznika znaku; „fałsz”

dla ustawionego (1) znacznika znaku.

1Istnieje jeszcze kilka postaci dodatkowych; zostaną one omówione w dalszych podrozdziałach

i rozdziałach.

Page 13: Asembler. Sztuka programowania

32 Asembler. Sztuka programowania

Wykorzystanie tych znaczników w wyrażeniach logicznych to już dość zaawansowaneprogramowanie w asemblerze. Sposób konstruowania wyrażeń logicznych angażującychznaczniki słowa stanu procesora zaprezentowany zostanie w następnym rozdziale.

W przypadku, kiedy wartość logiczna angażuje operand rejestrowy, operand ten możeodnosić się do zarówno do rejestru 8-bitowego, 16-bitowego, jak i 32-bitowego. Wyra-żenie takie przyjmuje wartość „fałsz”, jeśli rejestr zawiera wartość zero. Dla każdej innejwartości rejestru wyrażenie logiczne przyjmuje wartość „prawda”.

Jeśli w wyrażeniu logicznym uwikłana jest zmienna przechowywana w pamięci, jej war-tość jest sprawdzana, a wyrażenie przyjmuje wartość logiczną „prawdy” bądź „fałszu”w zależności od tej wartości, według reguł identycznych jak dla rejestrów. Należyjednak pamiętać, że w wyrażeniu logicznym powinna występować zmienna typu logicz-nego (�������). Uwikłanie w wyrażeniu logicznym zmiennej innego typu spowodujebłąd. W przypadku potrzeby określenia wartości logicznej zmiennej typu innego niż������� należy skorzystać z wyrażeń logicznych ogólnego przeznaczenia, omówio-nych poniżej.

Najbardziej ogólna postać wyrażenia logicznego w języku HLA pozwala na określenieoperatora relacji wraz z dwoma operandami. Listę dozwolonych kombinacji rodzajówoperandów i operatorów relacji zawiera tabela 1.3.

Tabela 1.3. Dozwolone wyrażenia logiczne

Operand lewy Operator relacji Operand prawy

Zmienna w pamięci bądź rejestr � albo �� Zmienna w pamięci, rejestr bądź stała

� albo ��

��

Nie można określić obu operandów jako operandów pamięciowych. Lewy operand moż-na więc utożsamić z operandem źródłowym, a operand prawy z operandem docelowymi stosować kombinacje rodzajów operandów takie jak dla instrukcji ��� czy ���.

Kolejna analogia operatorów relacji do instrukcji ��� i ��� objawia się w wymoguidentycznego rozmiaru operandów. I tak, oba operandy muszą być albo bajtami, albosłowami, albo podwójnymi słowami. Jeśli operand prawy jest stałą, jego wartość musinadawać się do zapisania w operandzie lewym.

I jeszcze jedno: jeśli lewy operand jest rejestrem, a prawy to stała dodatnia albo innyrejestr, porównanie odbywa się bez uwzględnienia znaku operandów. Efekty takiegoporównania omówione zostaną w następnym rozdziale; na razie wystarczy zapamiętać,że nie powinno się porównywać wartości ujemnej przechowywanej w rejestrze z warto-ścią stałą albo zawartością innego rejestru. Wynik porównania może bowiem odbiegaćod oczekiwań programisty.

Page 14: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 33

Operatory � i ����� pozwalają na wykonanie testu zawierania się wartości prze-chowywanej w rejestrze w określonym zbiorze liczb. Na przykład wyrażenie ���������������� daje wartość logiczną ����, jeśli wartość przechowywana w rejestrzeEAX to wartość z zakresu od 2000 do 2099 (włącznie). Operator ����� ma działanieodwrotne — da wartość logiczną ���� jedynie wtedy, kiedy zawartość rejestru toliczba spoza określonego zbioru. Na przykład wyrażenie ����������������� będziemiało wartość ���� tylko wtedy, kiedy znak przechowywany w rejestrze AL nie będziemałą literą alfabetu.

Oto kilka przykładów wyrażeń logicznych w języku HLA:

������������������ ��������������� �������������� ��� �!��������""#��������$�$��$$

1.9.2. Instrukcje if..then..elseif..else..endif języka HLA

Składnia instrukcji warunkowego wykonania kodu w wydaniu języka HLA prezen-towana jest na rysunku 1.10.

Rysunek 1.10. Składnia instrukcji if w języku HLA

Wyrażenia występujące w instrukcji muszą odpowiadać jednej z postaci wyrażeń lo-gicznych, zaprezentowanych w poprzednim ustępie. Jeśli wyrażenie przyjmie wartość lo-giczną „prawda”, wykonany zostanie kod umieszczony za klauzulą � ��. W przeciwnymprzypadku sterowanie przekazane zostanie do kodu w ramach klauzuli ���� bądź ����.

Page 15: Asembler. Sztuka programowania

34 Asembler. Sztuka programowania

Ponieważ klauzule ���� i ���� nie są obowiązkowe, instrukcja w najprostszejpostaci składa się z wyrażenia logicznego i pojedynczej klauzuli � ��, za którą wystę-puje instrukcja lub blok instrukcji wykonywanych warunkowo, w zależności od war-tości wyrażenia logicznego:

�%&� �!���"�'��# �

������(�)��*)�&�+,��(-�.������/011+2����'3

�(�%3

Jeśli w trakcie wykonania programu wyrażenie logiczne w instrukcji będzie miałowartość ����, wykonany zostanie kod znajdujący się pomiędzy słowami � �� i ���.W przypadku kiedy wyrażenie logiczne będzie miało wartość ����, kod ów zostaniepominięty.

Inną powszechnie występującą formą instrukcji jest forma z pojedynczą klauzulą����. Instrukcja taka wygląda następująco:

�%&� �!���"�'��# �

������(�)��*)�&�+,��(-�.������/011�.�4���4�+2����'3

��

������(�)��*)�&�+5�*��.���.������.�4���4�+2����'3

�(�%3

Jeśli wyrażenie logiczne przyjmie wartość ����, wykonany zostanie kod zawarty międzysłowami � �� a ����. W przypadku wartości ���� sterowanie przekazane zostanie dokodu ograniczonego klauzulą ���� i słowem ���.

Przez osadzanie opcjonalnych klauzul ���� w instrukcji można rozbudowywaćpodejmowanie decyzji o wykonaniu kodu. Na przykład, jeśli rejestr CH zawiera wartośćznakową, można na podstawie jego wartości dokonać wyboru spośród dostępnychopcji menu, konstruując następującą instrukcję :

�%&�#���$�$�'��# �

������(�)��*)�&�+67,�����*�78 �9 �)-�$�$+2����'3

�� �%&�#���$,$�'��# �

������(�)��*)�&�+67,�����*�78 �9 �)-�$,$+2����'3

�� �%&�#���$$�'

������(�)��*)�&�+67,�����*�78 �9 �)-�$$+2����'3

��

������(�)��*)�&�+���(-�.7,����*8���*���9 �)+2����'3 �(�%3

Page 16: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 35

Choć nie widać tego w powyższym przykładzie, język HLA nie wymaga umieszczaniapo sekwencji klauzul ���� klauzuli ����. Jednak przy podejmowaniu decyzji o wyko-naniu jednej z wielu ścieżek kodu warto uzupełnić instrukcję klauzulą ���� rozpo-czynającą ścieżkę kodu obsługi ewentualnych błędów wyboru. Nawet jeśli pozorniewykonanie owego kodu wydaje się niemożliwe, to należy wziąć pod uwagę ewentualnyrozwój aplikacji — w kolejnych wersjach kodu wybór ścieżki wykonania może miećinny przebieg; warto, aby wtedy kod decyzyjny uzupełniony został o obsługę sytuacjiwyjątkowych i niespodziewanych.

1.9.3. Iloczyn, suma i negacjaw wyrażeniach logicznych

Na liście operatorów zaprezentowanej w poprzednich punktach zabrakło bardzo ważnychoperatorów logicznych: operatora iloczynu logicznego (AND), logicznej sumy (OR)oraz logicznej negacji (NOT). Ich zastosowanie w wyrażeniach logicznych omówionezostanie tutaj — wcześniej omówienie takie nie miałoby racji bytu z uwagi na koniecz-ność poprzedzenia go omówieniem instrukcji warunkowego wykonania kodu . Bezmożliwości wybiórczego wykonywania kodu trudno zaprezentować realistyczne przy-kłady zastosowania operatorów logicznych.

W języku HLA operator iloczynu logicznego ma postać znaków ��. Jest to operatorbinarny i jako taki wymaga określenia dwóch operandów; operandy te muszą być po-prawnymi wyrażeniami czasu wykonania. Operator ten zwraca wartość logiczną „prawda”wtedy, kiedy oba operandy mają wartość „prawda”. Oto przykład:

�%&� �!���"�::�#�$�$�'��# �

����9��&� �!2� ,!�'3����9��&�$�$2�#�'3

�(�%3

Obie instrukcje ��� zostaną wykonane jedynie wtedy, kiedy równocześnie rejestr EAXbędzie miał wartość większą od 0, a rejestr CH będzie zawierał znak „a”. Jeśli który-kolwiek z tych warunków nie będzie spełniony, obie instrukcje ��� zostaną pominięte.

Warto pamiętać, że operandy operatora �� mogą być dowolnymi poprawnymi wyra-żeniami logicznymi języka HLA — nie muszą to być wyłącznie operatory relacji. Otokilka poprawnych logicznych z operatorem iloczynu logicznego:

��::�����������"������$�$��$$�::� ,!,���;���::�� �!

W języku HLA wykorzystywana jest metoda skróconego szacowania wartości wyrażeńoperandów operatora logicznego ��. Otóż jeśli lewy operand ma wartość „fałsz”, tooperand prawy nie jest już sprawdzany — wyrażenie logiczne natychmiast otrzymujewartość „fałsz” (co jest jak najbardziej poprawne, bo operator �� daje wartość „prawda”wyłącznie dla dwu operandów o wartości „prawda”). Stąd w ostatnim z zaprezentowanychwyżej wyrażeń najpierw sprawdzona zostanie wartość logiczna zmiennej �������; jeślibędzie to wartość ����, test wartości logicznej rejestru EAX zostanie pominięty.

Page 17: Asembler. Sztuka programowania

36 Asembler. Sztuka programowania

Nie sposób nie zauważyć, że wyrażenie z operatorem ��, np. ����������������������,jest wyrażeniem logicznym (zwraca albo wartość logiczną „prawda”, albo wartość„fałsz”), więc wyrażenie takie może występować w roli operandu innego wyrażeniaz operatorem logicznym. Stąd poprawność następującego wyrażenia:

�!� �"�::� ,!� �� �!���::���� !

Operator iloczynu logicznego cechuje się łącznością lewostronną, co oznacza, że w kodziewygenerowanym przez kompilator HLA ewaluacja (wartościowanie) wyrażenia następujeod strony lewej do prawej. Jeśli rejestr EAX zawiera wartość większą od zera, pozostałeoperandy nie będą sprawdzane. Analogicznie, jeśli EAX jest mniejszy od zera, ale EBXjest równy EAX, to sprawdzanie logicznej wartości wyrażenia �!� zostanie pominięte —całe wyrażenie od razu otrzyma wartość ����.

Operator sumy logicznej w języku HLA reprezentowany jest znakami "". Podobnie, jakoperator iloczynu, operator sumy logicznej wymaga określenia dwóch operandów będą-cych poprawnymi wyrażeniami logicznymi. Operator zwraca przy tym wartość logiczną„prawda”, jeśli którykolwiek z operandów ma wartość logiczną „prawda”. Równieżpodobnie jak w przypadku operatora iloczynu stosowana jest metoda skróconej ewalu-acji wyrażenia — jeśli już pierwszy operand ma wartość logiczną „prawda”, operatornatychmiast zwraca wartość „prawda”, niezależnie od wartości drugiego operandu. Otoprzykłady zastosowania operatora sumy logicznej:

��<<�������"������$�$��$$�<<� ,!�,���;���<<� �!

Podobnie jak operator �� operator logicznej sumy cechuje się łącznością lewostronną,więc w wyrażeniach z wieloma operatorami "" ewaluacja przebiega od strony lewej doprawej. Na przykład:

�!� �"�<<� ,!� �� �!����<<����� !

Jeśli powyższe wyrażenie zostałoby osadzone w wyrażeniu logicznym instrukcji , tokod po klauzuli � �� zostałby wykonany tylko wtedy, kiedy rejestr EAX miałby wartośćmniejszą od zera lub EBX nie byłby równy EAX bądź rejestr ECX zawierałby wartośćzero. Jeśli już pierwszy test dałby wartość ����, pozostałe warunki nie byłyby spraw-dzane. Gdyby pierwszy warunek miał wartość ����, ale drugi ����, to etap szacowaniawartości logicznej wyrażenia �!� zostałby pominięty. Test zawartości rejestru ECXnastąpiłby jedynie w obliczu niespełnienie wcześniejszych dwóch wyrażeń.

Jeśli w jednym wyrażeniu logicznym występują operatory iloczynu i sumy logicznej,pierwszeństwo przed operatorem sumy ma operator iloczynu. Weźmy następujące wy-rażenie:

�!� �"�<<� ,!� �� �!�::�� !

W interpretacji HLA powyższemu wyrażeniu równoważne jest wyrażenie następujące:

�!� �"�<<�& ,!� �� �!�::�� !'

Jeśli rejestr EAX będzie miał wartość mniejszą od zera, reszta wyrażenia nie będziesprawdzana — całość wyrażenia otrzyma wartość ����. Jeśli jednak rejestr EAX będzie

Page 18: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 37

zawierał wartość większą od zera, to aby całość wyrażenia otrzymała wartość ����,oba operandy operatora iloczynu musiałyby mieć wartość ����.

Podwyrażenia wykorzystujące operatory iloczynu i sumy logicznej można grupowaćwedle własnego uznania, otaczając je nawiasami. Oto przykładowe wyrażenie:

& �!� �"�<<� ,!� �� �!'�::�� !

Aby to wyrażenie otrzymało wartość logiczną „prawda”, rejestr ECX musi zawieraćwartość zero oraz albo wartość EAX musi być mniejsza od zera, albo rejestr EBX musizawierać wartość różną od rejestru EAX. Szacowanie wyrażenia przebiega więc od-miennie niż w wersji bez nawiasów.

Operator negacji logicznej ma w języku HLA postać znaku wykrzyknika ( ). Operan-dami operatora negacji mogą być jednak wyłącznie rejestry i zmienne w pamięci (np. �������) — operator negacji nie może być bezpośrednio aplikowany do złożonychwyrażeń logicznych. Aby wykonać logiczną negację wyrażenia logicznego, należy towyrażenie ująć w nawiasy i cały nawias opatrzyć przedrostkiem w postaci wykrzyknika:

�&� �!� �"'

Powyższe wyrażenie otrzymuje wartość ���� wtedy, kiedy rejestr EAX ma wartośćmniejszą od zera.

Operator logicznej negacji jest szczególnie użyteczny w odniesieniu do wyrażeń zło-żonych angażujących operatory logicznej sumy i logicznego iloczynu. W prostych wyra-żeniach, jak powyższe, lepiej jest zazwyczaj bezpośrednio określić stan logiczny wyra-żenia niż dodatkowo komplikować go operatorem negacji.

W języku HLA programista może też korzystać z operatorów bitowych " i �, ale te różniąsię znacząco od operatorów logicznych "" oraz ��. Szczegółowe omówienie operatorówbitowych i innych operatorów języka HLA znajduje się w dokumentacji kompilatora.

1.9.4. Instrukcja while

Składnię instrukcji � �� ilustruje rysunek 1.11.

Rysunek 1.11.Składnia instrukcji

while języka HLA

Wykonanie instrukcji � �� przebiega następująco: najpierw szacowana jest wartośćlogiczna wyrażenia warunku pętli. Jeśli ma ono wartość „fałsz”, sterowanie natych-miast przekazywane jest do kodu znajdującego się za klauzulą ���� ��. W przypadku

Page 19: Asembler. Sztuka programowania

38 Asembler. Sztuka programowania

wartości „prawda” wyrażenia warunku, procesor przystępuje do wykonania ciała pętli.Po wykonaniu wszystkich instrukcji ciała pętli następuje ponowne oszacowanie wartościlogicznej wyrażenia warunkowego. Cała procedura powtarzana jest aż do momentu,w którym wyrażenie warunkowe pętli przyjmie wartość logiczną „fałsz”.

W pętli � �� sprawdzenie wartości warunku wykonania pętli następuje podobnie jakw językach wysokiego poziomu, czyli przed wykonaniem pierwszej instrukcji ciałapętli. Z tego względu istnieje prawdopodobieństwo, że instrukcje ciała pętli nie zostanąwykonane ani razu, jeśli wyrażenie warunku będzie od początku miało wartość ����.Poza tym warto pamiętać, że przynajmniej jedna z instrukcji ciała pętli powinna mody-fikować wartość wyrażenia logicznego — w przeciwnym przypadku będziemy mielido czynienia z pętlą nieskończoną.

Oto przykład pętli � �� w języku HLA:

9��&�"2���'3.#�� &��� ��"�'�(�

������(�)��*)�&�+��+2��2����'3�����((&��2���'3

�(.#�� 3

1.9.5. Instrukcja for

W języku HLA pętla for przybiera następującą postać:

%��&���������� ������������3��������3��������� ��������'�(�

��������� �����

�(%��3

Instrukcja pętli �� odpowiada następującej pętli � ��:

��������� ������������3.#�� &���������'�(�

��������� ����������������� ��������3

�(.#�� 3

Wyrażenie inicjalizujące może być dowolną, pojedynczą instrukcją maszynową. Instruk-cja ta zwykle inicjalizuje rejestr albo zmienną przechowywaną w pamięci, której wartośćdecyduje o kontynuowaniu pętli (tzw. licznik pętli). Warunek to wyrażenie logicznejęzyka HLA w formacie takim jak dla instrukcji � ��. Wartość tego wyrażenia decy-duje o podjęciu kolejnej iteracji pętli i zwykle angażuje licznik pętli. Instrukcja licz-nika to typowo instrukcja zwiększająca licznik pętli; odpowiednikiem tej instrukcjijest instrukcja w pętli � ��, której wykonanie zmienia wartość logiczną warunkuwykonania pętli. Zwykle instrukcją tą jest instrukcja języka HLA, na przykład ���.

Page 20: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 39

Oto przykład ilustrujący równoważność pętli �� i � ��:

%��&�9��&�"2���'3��� ��"3��((&��2���'�'�(�

�����(�)��*)�&�+��+2��2����'3

�(%��3

// Analogiczna pętla for:9��&�"2���'3.#�� &��� ��"�'�(�

������(�)��*)�&�+��+2��2����'3�����((&��2���'3

�(.#�� 3

1.9.6. Instrukcja repeat

Instrukcja ������������ języka HLA wykorzystuje składnię prezentowaną na rysunku1.12. Programiści języków C, C++ oraz Java powinni od razu zauważyć, że instrukcjata jest odpowiednikiem dostępnej w tych językach instrukcji ����� ��.

Rysunek 1.12.Składnia instrukcji

repeat..until

języka HLA

Test warunku zatrzymania pętli ������������ odbywa się po zakończeniu wykony-wania ciała pętli. Z tego względu instrukcja (bądź blok instrukcji) ciała pętli zostaniewykonana przynajmniej raz, niezależnie od wartości początkowej wyrażenia warunkuzatrzymania pętli. Warunek ten jest badany po raz pierwszy dopiero po zakończeniupierwszej iteracji; jeśli wyrażenie ma wartość ����, pętla jest powtarzana2. Jeśli wa-runek otrzyma wartość ����, pętla jest zatrzymywana i sterowanie przekazywane jestdo instrukcji znajdujących się za klauzulą ����.

Oto prosty przykład wykorzystania pętli ������������:

9��&��"2� !�'3

� * ��

������(�)��*)�&�+ !���+2� !2����'3�����),&��2� !�'3

)����&� !���"�'3

2Znaczenie warunku jest więc w języku HLA odwrotne niż warunku w analogicznej konstrukcji

(���.#�� znanej z języków C, C++ i Java.

Page 21: Asembler. Sztuka programowania

40 Asembler. Sztuka programowania

Jeśli instrukcje ciała pętli mają być wykonane przynajmniej raz, niezależnie od warto-ści wyrażenia warunku pętli, wtedy w miejsce pętli � �� lepiej zastosować pętlę ��#���������� — konstrukcja taka będzie efektywniejsza.

1.9.7. Instrukcje break oraz breakif

Instrukcje ����$ i ����$ służą do przedwczesnego przekazywania sterowania pozapętlę. Składnię obu instrukcji ilustruje rysunek 1.13.

Rysunek 1.13.Składnia instrukcjibreak oraz breakif

języka HLA

Instrukcja ����$ powoduje bezwarunkowe przerwanie wykonywania pętli; instrukcja����$ uzależnia przekazanie sterowania poza pętlę od spełnienia warunku będącegowyrażeniem logicznym. Wyjście z pętli następuje wtedy wyłącznie w obliczu wartości���� tego wyrażenia.

Zastosowanie instrukcji ����$ oraz ����$ nie pozwala na przekazanie sterowania pozapętle zagnieżdżone. W języku HLA służą do tego osobne instrukcje, jak ��%������oraz ��� i ���. Szczegółowe informacje na ten temat zawiera dokumentacja językaHLA. Dalej, język HLA udostępnia instrukcje !������ oraz !������ pozwalającena przystąpienie do kolejnej iteracji pętli z pominięciem niewykonanych jeszcze instrukcjiciała pętli. Po szczegóły odsyłam Czytelników do stosownej dokumentacji.

1.9.8. Instrukcja forever

Składnię konstrukcji ������������� prezentuje rysunek 1.14.

Rysunek 1.14.Składnia instrukcjiforever języka HLA

Instrukcja ������ tworzy pętlę nieskończoną. Wewnątrz takiej pętli można jednakumieścić instrukcje ����$ bądź ����$ celem przerwania pętli w wybranym momencie.To chyba najczęstszy układ pętli ������. Ilustruje go następujący przykład:

%�� � �

������(�)��*)�&�+6*��.�(���, ���4�.����&9�� 8����(��"'-+�'3������(���= �&���'3����,� �4�%&��� ��"�'3������(�)��*)�&�+6*��.�(����.������9�����,7�9�� 8����(��"�+2����'3

�(%��3

Page 22: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 41

1.9.9. Instrukcje try, exception oraz endtry

Instrukcje ��&����!������������& służą do implementacji bardzo przydatnych blokówobsługi wyjątków. Składnia instrukcji prezentowana jest na rysunku 1.15.

Rysunek 1.15.Składnia instrukcji

obsługi wyjątków

Blok instrukcji ��&�������& służy do ochrony wykonania instrukcji. Jeśli wykonanieinstrukcji znajdujących się pomiędzy klauzulą ��& a pierwszą klauzulą ��!�����,tworzących tzw. blok chroniony, przebiegnie bez niespodzianek, sterowanie przeka-zywane jest do pierwszej instrukcji za klauzulą �����&. Jeśli jednak w czasie wyko-nywania instrukcji bloku chronionego zdarzy się błąd (wyjątek), wykonanie programujest przerywane, a sterowanie przekazywane jest do odpowiedniej klauzuli ��!�����(skojarzonej z wyjątkiem, który wystąpił). Każdy wyjątek ma unikalny identyfikatorw postaci liczby całkowitej. Niektóre z identyfikatorów wyjątków zadeklarowane zostaływ pliku nagłówkowym excepts.hhf biblioteki standardowej języka HLA, ale programi-sta może też definiować własne wyjątki. W momencie sprowokowania wyjątku systemporównuje identyfikator zgłoszonego wyjątku z identyfikatorami określonymi w klauzu-lach ��!�����. Jeśli wśród nich znajduje odpowiedni identyfikator, następuje wykonanieinstrukcji określonych w ramach klauzuli ��!�����. Po zakończeniu wykonywania tychinstrukcji sterowanie przekazywane jest do pierwszej instrukcji za klauzulą �����&.

Jeśli wyjątek zostanie sprowokowany poza blokiem kodu chronionego (tzn. poza ramamiinstrukcji ��&�������&) albo jeśli w ramach instrukcji ��&�������& brak jest klauzuliskojarzonej ze zgłoszonym wyjątkiem, program jest awaryjnie zatrzymywany. Zatrzy-maniu towarzyszy wyprowadzenie stosownego komunikatu na wyjście programu.

Instrukcje ��&�������& można na przykład wykorzystać do ochrony programu przedskutkami wprowadzenia niepoprawnych danych:

Page 23: Asembler. Sztuka programowania

42 Asembler. Sztuka programowania

� * ��

����9��&�%��� 2�>��(��� = ��'3���// Uwaga: zmienna GoodInteger musi być typu boolean������7

����������(�)��*)�&�+6*��.�(���, ���4�.���-�+�'3����������(���= �&���'3��������9��&���) 2�>��(��� = ��'3

���� ! *����&� !�?��� �����������'

����������(�)��*)�&�+/� *�*��.���.�����2�.*��.�(�8 � ���+2����'3

���� ! *����&� !�;��) @)�@%A��= �'

����������(�)��*)�&�+6�������*����4� �)2�.*��.�(�8 � ���+2����'3

���� �(��73

)����&�>��(��� = ��'3

Zastosowana w powyższym programie pętla ������ pozwala na wykonywanie koduciała pętli tak długo, jak długo użytkownik podaje niepoprawne wartości. Jeśli z powoduwprowadzenia niepoprawnej wartości jej przypisanie do zmiennej '���(���%�� będzieniemożliwe (na przykład z racji umieszczenia we wprowadzonym niedozwolonych dlaliczb znaków albo wprowadzenia zbyt wielkiej wartości), wykonany zostanie odpo-wiedni do rodzaju błędu blok instrukcji obsługi wyjątku. Obsługa wyjątków polega tuna wyświetleniu stosownego komunikatu i przekazaniu sterowania poza instrukcję��&�������&. Ponieważ z racji wyjątku nie udało się w ramach bloku chronionego ustawićwartości ���� dla zmiennej '���(���%��, pętla jest wykonywana ponownie. Jeśliby jednaw ramach bloku chronionego sprowokowany został inny niż obsługiwane wyjątek, pro-gram zakończyłby działanie po uprzednim wyprowadzeniu komunikatu o błędzie3.

W tabeli 1.4 zebrane zostały wyjątki, zdefiniowane w pliku excepts.hhf w czasie przy-gotowywania wydania niniejszej książki. Aktualną listę zdefiniowanych w bibliotecestandardowej wyjątków można znaleźć, zaglądając do pliku excepts.hhf dołączonegodo posiadanej wersji oprogramowania kompilatora HLA.

Większość z tych wyjątków dotyczy sytuacji, których omawianie w niniejszym rozdzialebyłoby przedwczesne. Zostały umieszczone w tabeli wyłącznie gwoli kompletnościomówienia. Każdy z wyjątków opisany jest szczegółowo w dokumentacji HLA ReferenceManual dokumentacji biblioteki standardowej języka HLA; ostateczną wykładnią jestzaś sam kod źródłowy biblioteki standardowej HLA. Najczęściej wykorzystywanymiwyjątkami są ���)��������*����, ��������+��+,��%� oraz ���-���%+������.

Omówienie bloku kodu chronionego podjęte zostanie ponownie nieco później. Czytelnikpowinien kontynuować edukację w zakresie podstawowym, zadowalając się na razieszczątkowymi informacjami o obsłudze wyjątków.

3Doświadczony programista zastanawia się zapewne, dlaczego w powyższym kodzie do sterowania

przebiegiem pętli wykorzystywana jest zmienna typu ,��� �� — przecież można by identyczny efekt

osiągnąć, stosując instrukcję ,� �4�%. Zastosowana konstrukcja ma uzasadnienie, które zostanieprzedstawione w dalszej części książki.

Page 24: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 43

Tabela 1.4. Wyjątki zdefiniowane w pliku nagłówkowym excepts.hhf

Identyfikator wyjątku Znaczenie

!������=@� �%��. Próba zapisania zbyt długiego łańcucha

!������=��( !����� Próba odwołania do znaku o indeksie spoza łańcucha

!�;��) @)�@%A��= Zbyt wielka wartość dla danego typu zmiennej

!���� =��?#�� Zaangażowanie w operacji znaku spoza dopuszczalnego zakresu

(kod ASCI spoza zakresu od 0 do 127)

!�?��� ���������� Niedozwolone znaki w konwersji łańcucha do wartości numerycznej

!���(B�� C��(� � Odwołanie do pliku za pośrednictwem niepoprawnego uchwytu pliku

!�B�� @* �B���)� Niemożność otwarcia pliku (np. brak pliku o zadanej nazwie)

!�B�� ?��� ����� Niemożność zamknięcia pliku

!�B�� 6��� ����� Błąd przy zapisie danych do pliku

!�B�� A �(����� Błąd przy odczycie danych z pliku

!�D��4B)������� Próba zapisania danych do pliku przy pełnym dysku

!���(@%B�� Próba odczytania bajta spoza końca pliku

!�E 9��7���������B���)� Brak pamięci do zaspokojenia żądania alokacji

!���� 9*�F�D � %/011 Próba odwołania do pamięci za pośrednictwem wskaźnika o wartości NULL

!�?�����B� E 9��7 Błąd przy operacji zwalniania pamięci

!�6�(�#F����= Zły format konwersji wartości liczbowej do łańcucha

!�F��E��7?9(1�5���9� Wiersz wywołania programu zawiera zbyt dużą liczbę argumentów

!�����7�#�* ;�������� Próba operowania na dwóch tablicach o różnych rozmiarach

!�����7��)�(� Próba odwołania do elementu spoza tablicy

!�������(D�� Operacja na niepoprawnej wartości daty

!�������(D�� B��9�� Błąd konwersji łańcucha do daty (łańcuch zawiera niedozwolone znaki)

!�F�9 @� �%��. Przepełnienie w operacji manipulowania informacją o czasie

!���� �����B��� ( Błąd instrukcji asercji

!��! )� (�,����� Próba wywołania metody klasy abstrakcyjnej

!�� ��;�������� Próba odwołania się do niedozwolonego adresu pamięci

!��� �45���� Program dotarł do punktu zatrzymania (przerwanie 3.)

!����=� �� * Program wykonywany jest z ustawionym znacznikiem wykonania

krokowego

!�5�������� Próba wykonania instrukcji charakterystycznej dla trybu nadzoru

!���� =������� Próba wykonania niedozwolonej instrukcji maszynowe

!���)�(����� Rejestr zawiera wartość spoza zakresu określonego w instrukcji ,�)�(

!���������� Wykonanie instrukcji ���� przy ustawionym znaczniku przepełnienia

!�D���( ����� Próba dzielenia przez zero bądź inny błąd dzielenia

!�%D ���9�� Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony

obliczeniom arytmetycznym)

!�%D���7G �� Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony

obliczeniom arytmetycznym)

Page 25: Asembler. Sztuka programowania

44 Asembler. Sztuka programowania

Tabela 1.4. Wyjątki zdefiniowane w pliku nagłówkowym excepts.hhf — ciąg dalszy

Identyfikator wyjątku Znaczenie

!�%�� !��A �)�� Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony

obliczeniom arytmetycznym)

!�%������(@* ������ Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony

obliczeniom arytmetycznym)

!�%@� �%��. Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony

obliczeniom arytmetycznym)

!�%���4?# 4 Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony

obliczeniom arytmetycznym)

!�%0�( �%��. Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony

obliczeniom arytmetycznym)

!�������(C��(� System operacyjny odrzucił niepoprawny uchwyt

!����4@� �%��. System operacyjny zgłosił przepełnienie stosu

1.10. Biblioteka standardowajęzyka HLA — wprowadzenie

Język HLA prezentuje się na tle tradycyjnego języka asemblerowego korzystnie dziękidwóm aspektom. Pierwszym jest składnia języka HLA zapożyczająca elementy znanez języków programowania wysokiego poziomu (jak struktury kontroli wykonania ko-du czy deklaracje zmiennych). Dostępność tych elementów pozwala na efektywne wyko-rzystanie umiejętności przeniesionych z programowania wysokopoziomowego i tymsamym znacznie szybszą naukę języka asemblerowego. Drugim elementem stanowią-cym o przewadze HLA nad tradycyjnym asemblerem jest dostępność biblioteki HLAStandard Library. Biblioteka standardowa języka HLA zawiera wiele prostych w użyciuprocedur języka asemblerowego realizujących typowe zadania programistyczne — ichdostępność eliminuje konieczność samodzielnego pisania wszystkich, nawet tych naj-bardziej typowych, elementów programu. Dostępność biblioteki standardowej znosi jednąz podstawowych trudności, w obliczu których stają programiści chcący poznać językasemblerowy — trudność tkwiącą w konieczności samodzielnej implementacji złożonychniekiedy procedur wejścia-wyjścia, niezbędnych w nawet najprostszych programach.Brak standaryzowanej biblioteki wywołań powodowałby, że każdy początkujący progra-mista poświęcałby mnóstwo czasu na zaimplementowanie tak podstawowych operacjijak wyprowadzanie napisów. Jej dostępność oznacza więc, że adept języka asemblerowegomoże skoncentrować się na nauce samego języka, nie poświęcając się od samego początkuzgłębianiu tajników podsystemu wejścia-wyjścia danego systemu operacyjnego.

Dostępność rozmaitych procedur bibliotecznych to nie jedyna zaleta języka HLA. W końcubiblioteki kodu asemblerowego to nie nowy wynalazek4. Jednak biblioteka standardowa

4Popularna jest na przykład biblioteka UCR Standard Library dla programistów języka asemblerowego

procesorów 80x86.

Page 26: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 45

języka HLA to nie tylko sam kod procedur, ale również wysokopoziomowy interfejswywołań tych procedur. W rzeczy samej język HLA powstał głównie z myślą o możli-wości tworzenia zestawów procedur bibliotecznych wysokiego poziomu. Ów wyso-kopoziomowy interfejs wywołań, w połączeniu z wysokopoziomową naturą wielu pro-cedur, daje programiście niespodziewanie wielkie i równocześnie łatwo dostępnemożliwości.

Biblioteka standardowa języka HLA składa się z szeregu modułów przypisanych dokilku kategorii. Część z dostępnych modułów zawiera tabela 1.55.

Tabela 1.5. Wybrane moduły biblioteki standardowej HLA

Nazwa modułu Funkcje

args Procedury przetwarzania argumentów wiersza polecenia wraz z procedurami

pomocniczymi

conv Procedury konwersji łańcuchów do wartości różnych typów i konwersji odwrotnych

cset Procedury operujące na zbiorach znaków

DateTime Procedury manipulowania datą i czasem

excepts Procedury obsługi wyjątków

fileio Procedury plikowych operacji wejścia-wyjścia

hla Specjalne stałe języka HLA i inne definicje

hll Implementacja instrukcji znanych z języków wysokopoziomowych

Linux Wywołania systemowe jądra systemu Linux (tylko w wersji HLA dla

systemu Linux)

math Procedury implementujące obliczenia arytmetyczne o zwiększonej precyzji,

funkcje trygonometrycznych i inne funkcje matematyczne

memory Procedury przydziału i zwalniania pamięci wraz z procedurami pomocniczymi

misctypes Definicje rozmaitych typów danych i operacji na tych typach

patterns Biblioteka procedur dopasowywania wzorców

rand Implementacja generatora liczb pseudolosowych i procedur pomocniczych

stdin Procedury obsługi wejścia

stdout Procedury obsługi wyjścia

stdlib Specjalny plik nagłówkowy grupujący wszystkie moduły biblioteki standardowej

języka HLA

strings Biblioteka procedur manipulowania łańcuchami znakowymi

tables Biblioteka procedur obsługi tablic asocjacyjnych

win32 Definicje stałych wykorzystywanych w wywołaniach systemu Windows

(tylko w wersji HLA dla środowiska Win32)

x86 Stałe i definicje charakterystyczne dla procesora 80x86

5Z racji ciągłego rozwoju biblioteki standardowej języka HLA niniejsza lista nie jest zapewne kompletna.

Aktualnej listy modułów należy szukać w dokumentacji języka HLA.

Page 27: Asembler. Sztuka programowania

46 Asembler. Sztuka programowania

Część z tych modułów doczeka się szczegółowego omówienia w dalszej części książki.Tutaj skupimy się na najważniejszych — z punktu widzenia początkującego progra-misty — procedurach biblioteki standardowej HLA, zebranych w module ����.

1.10.1. Stałe predefiniowane w module stdio

Omówienie modułu ���� biblioteki standardowej należałoby rozpocząć od przedsta-wienia najczęściej wykorzystywanych stałych. Na przykład w następującym kodzie:

��(�)��*)�&�+�#�82�*�7=�(��+2����'3

pojawia się stała �� reprezentująca sekwencję znaków przejścia do nowego wiersza.Identyfikator �� nie jest w kontekście programu HLA żadnym słowem zarezerwowanymjęzyka HLA; jego zastosowanie nie ogranicza się też do wywołania ����������. Otóż ��to jedynie stała, której wartością jest literał łańcuchowy odpowiadający sekwencji znakównowego wiersza (w systemie Windows sekwencja ta obejmuje znaki powrotu karetkii wysuwu wiersza; w Linuksie to pojedynczy znak wysuwu wiersza).

Oprócz stałej �� implementacja podsystemu wejścia-wyjścia w bibliotece standardo-wej HLA zawiera definicje kilku innych przydatnych stałych znakowych. Zostały onewymienione w tabeli 1.6.

Tabela 1.6. Stałe znakowe definiowane w module wejścia-wyjścia biblioteki standardowej HLA

Stała Definicja

��(���, �� Znak dzwonka; wydrukowanie tego znaku objawia się wygenerowaniem dźwięku przez

głośniczek systemowy

��(���,� Znak cofania kursora

��(�����, Znak tabulacji

��(����% Znak wysuwu wiersza

��(���� Znak powrotu karetki

Z wyjątkiem �� wszystkie zaprezentowane tu stałe zdefiniowane są w przestrzeninazw ���� (stąd wymóg poprzedzania nazw stałych przedrostkiem ����)6. Osadzenieowych stałych w przestrzeni nazw ���� pozwoliło na uniknięcie ewentualnych kon-fliktów z nazwami zmiennych definiowanymi przez programistę. Nazwa ��, jakoużywana wyjątkowo często, nie została osadzona w przestrzeni nazw ���� — koniecz-ność każdorazowego wpisywania ������� byłaby bardzo uciążliwa.

1.10.2. Standardowe wejście i wyjście programu

Wiele procedur wejścia-wyjścia biblioteki HLA jest prefiksowanych nazwą ���� bądź������. Technicznie oznacza to, że nazwy tych procedur zdefiniowane zostały wewnątrzodpowiedniej przestrzeni nazw. W praktyce zastosowanie przedrostka stanowi rów-nież sugestię co do pochodzenia danych (dane pobierane ze standardowego urządzenia

6Przestrzenie nazw omówione zostaną w jednym z kolejnych rozdziałów.

Page 28: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 47

wejściowego, ����) bądź ich przeznaczenia (dane kierowane do standardowego urzą-dzenia wyjściowego, ������). Najczęściej urządzeniem wejściowym jest klawiatura kon-soli systemowej. Urządzenie wyjściowe to zwykle ekran konsoli. W ogólności więc in-strukcje z przedrostkiem ���� bądź ������ realizują odczyty i zapisy danych z i dourządzenia konsoli.

Przy uruchamianiu programu z poziomu wiersza poleceń (zwanego w Linuksie powłoką)można dokonać przekierowania strumieni danych wejściowych i wyjściowych dourządzeń innych niż domyślne. Na przykład argument wywołania programu w postaci���$.�& powoduje przekierowanie standardowego wyjścia programu do pliku plik_wy.Z kolei argument wywołania postaci ���$.�� powoduje przypisanie standardowegowejścia programu do pliku źródłowego plik_we. Oto przykłady przekierowania stan-dardowego wejścia i wyjścia programu �����%� uruchamianego z poziomu wierszapoleceń7:

� ��*=9� ��*)��(���� ��*=9���)�*)���!�� ��*=9� ����!����)���!�

1.10.3. Procedura stdout.newln

Procedura ������������ powoduje zapisanie do urządzenia standardowego wyjścia se-kwencji nowego wiersza. Wywołanie owej procedury jest równoważne wywołaniu����������/����01. Wywołanie ������������ jest jednak niekiedy nieco wygodniejszebądź bardziej czytelne. Oto przykład wywołania procedury:

��(�)��� .��&'3

1.10.4. Procedury stdout.putiN

Procedury biblioteczne ����������2, �����������3 oraz ����������4� wyprowa-dzają na standardowe wyjście programu pojedynczą wartość, odpowiednio: 8-bitową,16-bitową i 32-bitową, interpretowaną jako liczba całkowita ze znakiem. Argumentemwywołania może być stała, rejestr bądź zmienna w pamięci — rozmiar argumentu musijednak odpowiadać rozmiarowi parametru formalnego procedury.

Omawiane procedury wyprowadzają przekazaną wartość na standardowe wyjście pro-gramu. Wyprowadzany napis reprezentujący wartość konstruowany jest z użyciem mini-malnej liczby znaków pozwalających na dokładne odwzorowanie wartości. Jeśli przeka-zana argumentem wywołania procedury wartość jest ujemna, napis zostanie poprzedzonyznakiem minusa. Oto kilka przykładów wywołań procedur �����������:

��(�)��*)���&�����'3��(�)��*)���H&�(!�'3��(�)��*)����&����;���'3

7Uwaga dla użytkowników systemu Linux: w zależności od wartości zmiennej środowiskowej 5�FC

nazwę programu trzeba być może poprzedzić znakami „./”, np. �I� ��*=9� ��*)��(���.

Page 29: Asembler. Sztuka programowania

48 Asembler. Sztuka programowania

1.10.5. Procedury stdout.putiNSize

Procedury ����������2-��, �����������3-�� oraz ����������4�-�� zapisują dostandardowego urządzenia wyjściowego wartości interpretowane jako liczby całkowiteze znakiem — a więc działają podobnie jak procedury �����������. Tyle że procedury�����������-�� pozwalają lepiej kontrolować format wyprowadzanych napisów —programista może odpowiednim argumentem wywołania procedury określić minimalnąszerokość wyprowadzanego napisu. W przypadku kiedy wyprowadzana wartość po kon-wersji będzie zajmowała mniejszą niż podano liczbę znaków, brakujące znaki uzupeł-niane są znakami wypełnienia — znak wypełnienia również określa się argumentemwywołania procedury. Składnia wywołania procedur �����������-�� jest następująca:

��(�)��*)����� &�������� � ��2���������2�������������'3��(�)��*)���H�� &�������� !" ��2���������2�������������'3��(�)��*)������ &�������� #$ ��2���������2�������������'3

W miejsce parametru ���������� można przekazać stałą, nazwę rejestru albo nazwęzmiennej w pamięci — podany argument musi jednak odpowiadać rozmiarem rozmia-rowi parametru. Parametr � ������� może przyjmować argumenty w postaci stałychcałkowitych z zakresu od –256 do 256. Argumentem tego parametru może być równieżzawartość rejestru bądź wartość zmiennej przechowywanej w pamięci. Argumentemparametru ����������� powinien być pojedynczy znak.

Podobnie jak procedury ����������� procedury �����������-�� wyprowadzają naurządzenie standardowego wyjścia napis reprezentujący przekazaną w wywołaniu wartośćliczbową interpretowaną jako liczbę całkowitą ze znakiem. W tej wersji programistamoże jednak określić szerokość napisu — określa ją jako minimalną liczbę znaków, jakąpowinien zajmować napis. Jeśli wyświetlana wartość zajmować będzie więcej znaków(na przykład przy próbie wyświetlenia wartości 1234 w polu o szerokości 2), to wypro-wadzony napis zostanie stosownie poszerzony. Jednak w przypadku, gdy liczba znakóww napisie jest mniejsza od założonego minimum, brakujące znaki są w kodzie proceduryuzupełniane znakami wypełnienia. Kiedy wyprowadzana wartość argumentu � ������� jest liczbą ujemną, napis zostanie wyrównany w ramach wyprowadzanego pola dolewej; wartość dodatnia powoduje wyrównywanie napisów do prawej.

Jeśli wartość bezwzględna parametru � ������� jest większa niż minimalna liczbaznaków potrzebnych do dokładnego reprezentowania wyprowadzanej wartości, pro-cedury �����������-�� uzupełniają napis znakami wypełnienia, dodając je z lewejbądź z prawej strony napisu (przed bądź za znakową reprezentacją liczby). Znak wy-pełnienia definiowany jest argumentem �����������. W zdecydowanej większościprzypadków znakiem tym jest znak spacji, jednak w sytuacjach szczególnych koniecznemoże okazać się wypełnienie innym znakiem. Należy tylko pamiętać, że argument pa-rametru ����������� powinien być wartością znakową. W języku HLA stałe znakoweujmowane są w znaki pojedynczego cudzysłowu. W miejsce tego parametru można teżwskazać ośmiobitowy rejestr.

Listing 1.4 prezentuje prosty program w języku HLA demonstrujący sposób wyko-rzystania procedury ����������4�-�� w celu wyświetlenia listy wartości w formietabelarycznej.

Page 30: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 49

Listing 1.4. Wyprowadzenie kolumn wartości liczbowych — zastosowanie procedury stdout.puti32Size

*��=��9�/)9���?��)9��3

J���)( &�+��(��,�##%+�'

����������-����������3����?��?��-������3

, =���/)9���?��)9��3

����9��&�KH2�����'3����9��&�"2�?��?���'3����.#�� &�������"�'�(�

���������%&�?��?�������'��# �

��������������(�)��� .��&'3������������9��&�"2�?��?���'3

�������� �(�%3����������(�)��*)������ &����2��2�$�$�'3���������),&��2�����'3���������((&��2�?��?���'3

���� �(.#�� 3������(�)��� .��&'3

�(�/)9���?��)9��3

1.10.6. Procedura stdout.put

Procedura ����������8 to najelastyczniejsza procedura wyprowadzania napisów nawyjście programu dostępna w module obsługi standardowego wyjścia. Procedura ta łączyfunkcje wszystkich pozostałych procedur wyjścia standardowego, udostępniając pro-gramiście elegancki i efektywny mechanizm wyprowadzania danych.

W najbardziej ogólnej postaci składnia wywołania procedury ���������� prezentujesię następująco:

��(�)��*)�&����� �������������% ���������'3

Lista argumentów wywołania procedury ���������� może zostać konstruowana zestałych, rejestrów i zmiennych; kolejne argumenty oddziela się przecinkami. Procedurawyprowadza na standardowe wyjście wartości wszystkich kolejnych argumentów wywo-łania. Jako że procedura ta była już wielokrotnie prezentowana w przykładach, Czytelnikorientuje się, przynajmniej w zakresie podstawowym, co do sposobu jej wywoływania.

8Tak naprawdę ��(�)��*)� to makrodefinicja, a nie procedura. Rozróżnienie pomiędzy makrodefinicją

a procedurą wykracza jednak poza zakres niniejszego rozdziału; zostanie ono uwzględnione w dalszej

części książki.

Page 31: Asembler. Sztuka programowania

50 Asembler. Sztuka programowania

Warto jedynie podkreślić, że niniejsza procedura udostępnia programiście szereg wła-ściwości, które nie były dotąd prezentowane w przykładach. W szczególności każdyz argumentów wywołania może być zadany w jednej z dwóch postaci:

��������������-��������

������� może być dowolną stałą, rejestrem albo zmienną przechowywaną w pamięci.W niniejszym rozdziale prezentowane były argumenty w postaci literałów łańcuchowychi zmiennych w pamięci. Jak dotychczas wszystkie argumenty wywołania odpowiadałypierwszej możliwej postaci argumentu. W postaci drugiej programista może po dwu-kropku określić minimalną szerokość napisu reprezentującego wartość — szerokośćta interpretowana jest podobnie jak w procedurach �����������-��9. Przykładowyprogram z listingu 1.5 generuje wydruk podobny do tego tworzonego programem5���(�)������, tyle że tutaj miejsce procedury �����������-�� zajęła procedura����������.

Listing 1.5. Demonstracja sposobu określenia szerokości napisów w argumentach procedury stdout.put

*��=��9�/)9���?��)9���3

J���)( &�+��(��,�##%+�'

����������-����������3����?��?��-������3

, =���/)9���?��)9���3

����9��&�KH2�����'3����9��&�"2�?��?���'3����.#�� &�������"�'�(�

���������%&�?��?�������'��# �

��������������(�)��� .��&'3������������9��&�"2�?��?���'3

�������� �(�%3����������(�)��*)�&����-��'3���������),&��2�����'3���������((&��2�?��?���'3

���� �(.#�� 3������(�)��� .��&'3

�(�/)9���?��)9��3

Procedura ���������� obsługuje jednak więcej atrybutów. Będą one omawiane w kolej-nych rozdziałach, w miarę potrzeby ich wprowadzania do programów przykładowych.

9W odróżnieniu od procedur stdout.putiNSize nie da się tu określić dowolnego znaku wypełnienia

— znakiem tym jest domyślnie znak spacji. W obliczu konieczności zastosowania innego wypełnienia

należy skorzystać z procedur stdout.putiNSize.

Page 32: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 51

1.10.7. Procedura stdin.getc

Procedura �����%��! wczytuje pojedynczy znak z bufora urządzenia standardowegowejścia10. Znak ten umieszczany jest po wyjściu z procedury w rejestrze AL. Zastosowa-nie procedury ilustruje program z listingu 1.6.

Listing 1.6. Demonstracja działania procedury stdin.getc

*��=��9�#����*)�3

J���)( &�+��(��,�#%%+�'

��������)�� �-������3

, =���#����*)�3

����// Poniższa pętla wykonywana jest dopóty,����// dopóki użytkownik potwierdza wykonanie kolejnej iteracji.

����� * ��

��������// Wyprowadź 14 wartości.

��������9��&��L2��)�� ��'3��������.#�� &��)�� ����"�'�(�

��������������(�)��*)�&��)�� �-��'3�������������),&��2��)�� ��'3

�������� �(.#�� 3

��������// Zaczekaj, aż użytkownik naciśnie 't' albo 'n'.

����������(�)��*)�&���2���2�+674����4�� 8����� ��8 �&�I�'M-+�'3��������%�� � �

��������������(���� �(1�&'3��������������(���= �&'3������������,� �4�%&������$�$�'3������������,� �4�%&������$�$�'3��������������(�)��*)�&�+5��� �.*��.�(����,��$�$2���,��$�$-+�'3

�������� �(%��3����������(�)��� .��&'3

����)����&������$�$�'3

�(�#����*)�3

10Bufor to w tym kontekście ładniejsza nazwa tablicy.

Page 33: Asembler. Sztuka programowania

52 Asembler. Sztuka programowania

W powyższym programie wywołanie procedury ���������6� służy do wymuszeniaprzejścia do nowego wiersza strumienia wejściowego. Szczegółowy opis tej proceduryznajduje się nieco dalej, w niniejszym rozdziale.

1.10.8. Procedury stdin.getiN

Procedury �����%��2, �����%���3 oraz �����%��4� realizują operację wczytaniaz urządzenia standardowego wejścia odpowiednio: 8-bitowej, 16-bitowej i 32-bitowejwartości liczbowej całkowitej. Wczytane wartości umieszczane są w rejestrach AL, AXbądź EAX. Niniejsze procedury stanowią standardowy sposób wczytywania liczb całko-witych ze znakiem do programów języka HLA.

Podobnie jak procedura �����%��!, procedury �����%��� wczytują ze standardowegowejścia sekwencję znaków. W sekwencji tej ignorowane są wszelkie początkowe znakiodstępów (spacje, znaki tabulacji i tak dalej); reszta sekwencji (zawierająca cyfry poprze-dzone opcjonalnym znakiem minusa) konwertowana jest do postaci liczbowej. W przy-padku, kiedy sekwencja wejściowa zawiera znaki inne niż dozwolone lub wprowadzonawartość nie mieści się w zakresie liczbowym charakterystycznym dla danej procedury,zgłaszany jest wyjątek (można go przechwycić, umieszczając wywołanie proceduryw bloku kodu chronionego w instrukcji ��&�������&). Procedura �����%��2 obsługujeliczby z zakresu od –128 do 127, �����%���3 — od –32 768 do 32 767; wartościwczytywane do programu za pośrednictwem procedury �����%��4� muszą zaś mieścićsię w przedziale od –2 147 483 648 do 2 147 483 647.

Sposób wykorzystania owych procedur ilustruje listing 1.7.

Listing 1.7. Przykład wykorzystania wywołań stdin.getiN

*��=��9������*)�3

J���)( &�+��(��,�##%+�'

���-������-���������3������H-��������H3�������-���������3

, =��������*)�3

����// Pobierz od użytkownika kilka liczb całkowitych różnej wielkości.

������(�)��*)�&�+6*��.�(��� .� �4����, ���4�.����&��4� �)���������N'-�+�'3������(���= ���&'3����9��&���2����'3

������(�)��*)�&�+6*��.�(�.� 4�����, ���4�.����&��4� �)�����NH������NHN'-�+'3������(���= ���H&'3����9��&��!2���H�'3

������(�)��*)�&�+6*��.�(���, ���4�.����&��4� �)��(����(����9�����(�.'-�+�'3������(���= ����&'3

Page 34: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 53

����9��&� �!2�����'3

����// Wyświetl podane wartości.

������(�)��*)�����&����������2��������+@���.*��.�(�� ���,7-+2���2���2��������+��,���.����,����4�.���-��+2���-��2���2��������+�H�,���.����,����4�.���-�+2���H-��2���2��������+���,���.����,����4�.���-�+2����-��2�������'3

�(������*)�3

Warto skompilować i uruchomić powyższy program i sprawdzić, co się stanie, jeśli wpro-wadzane liczby będą wykraczały poza dozwolony zakres albo ciągi wartości zawieraćbędą niedozwolone znaki.

1.10.9. Procedury stdin.readLn i stdin.flushInput

Moment wywołania procedury �����%��! czy �����%��4� nie musi być tożsamy z mo-mentem wprowadzenia danych przez użytkownika. Biblioteka standardowa języka HLAbuforuje dane wprowadzane na standardowe wejście, wczytując jednorazowo caływiersz tekstu wprowadzanego na to wejście. Wywołanie procedury wejścia może więczostać zrealizowane za pośrednictwem operacji odczytu danych z bufora wejściowego(o ile ten nie jest pusty). Choć buforowanie w ogólności poprawia efektywność mani-pulowania danymi wejściowymi, niekiedy wprowadza zamieszanie, jak w poniższymprzykładzie:

��(�)��*)�&�+6*��.�(��� .� �4����, ���4�.����&��4� �)���������N'-�+�'3��(���= ���&'39��&���2����'3

��(�)��*)�&�+6*��.�(�.� 4�����, ���4�.����&��4� �)�����NH������NHN'-�+�'3��(���= ���H&'39��&��!2���H�'3

Programista ma nadzieję, że wykonanie powyższego kodu da następujący efekt: programwyświetli monit o podanie liczby, zaczeka na wprowadzenie jej przez użytkownika,wyświetli kolejny monit i również zaczeka na wprowadzenie liczby. W rzeczywistościprogram może mieć nieco inny przebieg. Jeśli, na przykład, powyższy kod zostanie uru-chomiony, a użytkownik w odpowiedzi na pierwszy monit wprowadzi łańcuch „123 456”program nie będzie już po wyświetleniu drugiego monitu oczekiwał na wprowadzeniedanych — procedura �����%���3 odczyta po prostu drugi z wprowadzonych do buforawejściowego łańcuchów (456).

Procedury wejścia oczekują na wprowadzenie tekstu przez użytkownika jedynie wtedy,kiedy bufor wejściowy jest pusty. Dopóki zawiera on jakiekolwiek znaki, procedurywejścia ograniczają się do odczytu z bufora. Można to wykorzystać, konstruując na-stępujący kod:

Page 35: Asembler. Sztuka programowania

54 Asembler. Sztuka programowania

��(�)��*)�&�+6*��.�(�(.� ���,7���4�.�� -�+�'3��(���= ����&'39��&� �!2��������'3��(���= ����&'39��&� �!2�����# ����;���'3

Tutaj zezwala się wprost użytkownikowi na jednorazowe (w ramach pojedynczegowiersza) wprowadzenie dwóch liczb. Liczby te powinny zostać oddzielone jednym bądźkilkoma znakami odstępu. Pozwala to na zaoszczędzenie miejsca na ekranie. Tutajbuforowanie danych wejściowych okazało się korzystne. Kiedy indziej jednak możebyć odwrotnie.

Na szczęście biblioteka standardowa języka HLA przewiduje dwie procedury sterującedziałaniem bufora standardowego urządzenia wejściowego. Są to procedury ���������6�oraz �������� (����. Pierwsza z nich usuwa z bufora wszystko, co zostało w nimumieszczone wcześniej, i tym samym wymusza wprowadzenie przez użytkownika nowe-go wiersza danych. Druga po prostu usuwa całą zawartość bufora. Oczyszczenie buforapowoduje, że przy następnym wywołaniu dowolnej z procedur wejścia, użytkownikbędzie musiał wprowadzić nowy wiersz danych. Procedurę ���������6� wywołujesię zwykle bezpośrednio przez wywołaniem dowolnej procedury wejścia. Procedura�������� (���� wywoływana jest zaś typowo bezpośrednio po odczytaniu danychz bufora wybraną procedurą wejścia.

Jeśli w programie wykorzystywana jest procedura ���������6� i okaże się, żekonieczne jest dwukrotne wprowadzenie danych, to należy zastanowić się nad wyko-

rzystaniem w jej miejsce procedury �������� (����. W ogólności ta ostatnia proce-dura, pozwalająca na opróżnienie bufora przez kolejną operacją wejścia, jest wy-

korzystywana znacznie częściej. Konieczność zastosowania procedury ���������6�jest bardzo rzadka — należy ją stosować wyłącznie tam, gdzie konieczne jest wymu-szenie na użytkowniku wprowadzenia nowego, aktualnego wiersza danych.

1.10.10. Procedura stdin.get

Procedura �����%�� łączy w sobie przedstawione wcześniej procedury wejścia, udo-stępniając ich funkcje za pośrednictwem pojedynczego wywołania. Procedura �����%��jest przy tym nieco prostsza w użyciu niż ����������, ponieważ jedynymi argumen-tami wywołania tej pierwszej są nazwy zmiennych (lub rejestrów), w których majązostać umieszczone wczytane wartości.

Przyjrzyjmy się ponownie przykładowi z poprzedniego punktu:

��(�)��*)�&�+6*��.�(�(.� ���,7���4�.�� -�+�'3��(���= ����&'39��&� �!2��������'3��(���= ����&'39��&� �!2�����# ����;���'3

Równoważny przykład wykorzystujący procedurę �����%�� wyglądałby następująco:

��(�)��*)�&�+6*��.�(�(.� ���,7���4�.�� -�+�'3��(���= �&�������2�����# ����;���'3

Page 36: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 55

Jak widać, zastosowanie procedury �����%�� może program skrócić i zwiększyć jegoczytelność.

Należy przy tym pamiętać, że wywołanie procedury �����%�� powoduje umieszczeniewczytanych wartości od razu we wskazanych zmiennych; żadne z wczytanych wartościnie pojawią się w rejestrach, chyba że rejestry te wystąpią na liście argumentów wywo-łania. Argumentami wywołania mogą być zarówno nazwy zmiennych, jak i nazwyrejestrów.

1.11. Jeszcze o ochronie wykonaniakodu w bloku try..endtry

Jak zapewne Czytelnik pamięta, instrukcje ��&�������& otaczają blok kodu, któregowykonanie może potencjalnie sprowokować wyjątki zakłócające działanie programu.Wyjątki mogą mieć trzy źródła: mogą być zgłaszane sprzętowo (np. w przypadku dzie-lenia przez zero), generowane przez system operacyjny albo podnoszone wykonaniemodpowiedniej instrukcji języka HLA. Programista może przechwytywać i obsługiwaćsytuacje wyjątkowe, umieszczając stosowny kod w ramach klauzuli ��!�����. Typowyprzykład zastosowania instrukcji ��&�������& ilustruje listing 1.8.

Listing 1.8. Przykład zastosowania bloku try..endtry

*��=��9�� ����(��*)�3J���)( &�+��(��,�##%+�'3

�����-����)-������)���H3

, =���� ����(��*)�3

������7

����������(�)��*)�&�+6*��.�(���, ���4�.����, ���4)-�+�'3����������(���= �&�)�'3����������(�)��*)�&�+6*��.�(���-�+2�)2����'3

���� ! *����&� !�?��� �����������'

����������(�)��*)�&�+6*��.�(��7���=��.� ����� (�.���� ���4�+����'3

���� ! *����&� !�;��) @)�@%A��= �'

����������(�)��*)�&�+6*��.�(������,��8 ���,7��()�+����'3

���� �(��73

�(�� ����(��*)�3

Page 37: Asembler. Sztuka programowania

56 Asembler. Sztuka programowania

W języku HLA instrukcje znajdujące się za słowem ��&, a przed pierwszą klauzulą��!����� noszą miano instrukcji chronionych. Jeśli podczas wykonywania takichinstrukcji zgłoszony zostanie wyjątek, sterowanie zostanie przekazane do pierwszejklauzuli ��!����� skojarzonej z danym wyjątkiem, przeszukując kolejne klauzule podkątem zgodności identyfikatora wyjątku zadeklarowanego ze zgłoszonym11. Identyfi-kator wyjątku to po prostu 32-bitowa liczba. Stąd również każda wartość umieszczanaw nawiasach klauzuli ��!����� powinna być wartością 32-bitową. Predefiniowanewartości wyjątków języka HLA wymienione są w pliku excepts.hhf. I choć byłoby to nie-wątpliwie rażącym naruszeniem przyjętego powszechnie stylu programowania, w klau-zulach ��!����� dozwolone jest podawanie wprost wartości numerycznych wyjątkówbez korzystania z predefiniowanych nazw stałych.

1.11.1. Zagnieżdżone bloki try..endtry

Jeśli w wyniku przeszukania klauzul ��!����� nie nastąpi dopasowanie identyfikatorawyjątku, będzie miało miejsce przeszukanie klauzul nadrzędnego bloku ��&�������&,w którym blok bieżący jest zagnieżdżony dynamicznie. Spójrzmy na przykład z li-stingu 1.9.

Listing 1.9. Zagnieżdżanie instrukcji try..endtry

*��=��9�� ����(��*)��3J���)( &�+��(��,�##%+�'3

�����-����)-������)���H3

, =���� ����(��*)��3

������7

����������7

��������������(�)��*)�&�+6*��.�(���, ���4�.����, ���4)-�+�'3��������������(���= �&�)�'3��������������(�)��*)�&�+6*��.�(���-�+2�)2����'3

�������� ! *����&� !�?��� �����������'

��������������(�)��*)�&�+6*��.�(��7���=��.� ����� (�.���� ���4�+����'3

�������� �(��73

����������(�)��*)�&�+���(��� �.7��4���*� * ��� �����4� �)+����'3

���� ! *����&� !�;��) @)�@%A��= �'

11W programie HLA identyfikator ten umieszczany jest w rejestrze EAX. Stąd po przekazaniu sterowania

do odpowiedniej klauzuli ! *���� można odwoływać się do identyfikatora wyjątku, odwołując siędo rejestru EAX.

Page 38: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 57

����������(�)��*)�&�+6*��.�(������,��8 ���,7��()�+����'3

���� �(��73

�(�� ����(��*)��3

Na listingu 1.9 widać zagnieżdżone dwa bloki instrukcji ��&�������&. Jeśli w ramachwykonania instrukcji �����%�� użytkownik wprowadzi wartość większą od 4 miliardówz kawałkiem, procedura �����%�� podniesie wyjątek ��������+��+,��%�. Kiedy wyjątekten zostanie przekazany do systemu wykonawczego HLA, nastąpi przeszukanie klauzul��!����� bieżącego bloku ��&�������& (tego, w którym nastąpiło zgłoszenie wyjątku;w prezentowanym przykładzie jest to blok zagnieżdżony). Jeśli nie uda się dopasowaćidentyfikatora wyjątku ��������+��+,��%� w bieżącym bloku ��&�������&, systemwykonawczy HLA sprawdzi, czy bieżący blok ��&�������& nie został zagnieżdżonyw innym bloku ��&�������& (co akurat ma miejsce na listingu 1.9). W takim przypadkusystem wykonawczy HLA przeszuka również klauzule ��!����� bloku zewnętrznego(nadrzędnego). Na listingu 1.9 ów blok zawiera klauzulę z odpowiednią wartością iden-tyfikatora, więc sterowanie przekazywane jest do pierwszej instrukcji umieszczonej zaklauzulą ��!�����/��������+��+,��%�0.

Po opuszczeniu bloku ��&�������& system wykonawczy traktuje ów blok jako nieak-tywny i jeśli teraz nastąpi zgłoszenie wyjątku, system wykonawczy HLA nie będzieprzeszukiwał klauzul ��!����� tego bloku12. Pozwala to na różnicowanie procedurobsługi wyjątków w różnych miejscach programu.

Jeśli ten sam wyjątek obsługują dwa bloki ��&�������&, a jeden z tych bloków jestzagnieżdżony w bloku chronionym pierwszego, to w momencie zgłoszenia wyjątkupodczas wykonywania wewnętrznego bloku chronionego system wykonawczy HLAprzekaże sterowanie do kodu obsługi wyjątku w wewnętrznym bloku ��&�������&.Kod obsługi tego samego wyjątku w zewnętrznym bloku jest ignorowany — HLA nieprzekazuje sterowania do tego kodu.

W przykładzie z listingu 1.9 druga instrukcja ��&�������& została statycznie zagnież-dżona w pierwszym bloku ��&�������&13. Wcześniej wspomniano, że jeśli bieżący blok��&�������& nie zawiera kodu obsługi zgłoszonego wyjątku, następuje przeszukanieklauzul ��!����� kolejnych bloków ��&�������&, w których blok bieżący został zagnież-dżony dynamicznie. W przypadku zagnieżdżenia dynamicznego zagnieżdżenie nie wy-nika wprost ze struktury kodu źródłowego. Sterowanie może zostać przekazane z wnętrzabloku ��&�������& do zupełnie innego miejsca programu, niżby to wynikało z samejstruktury kodu. Jeśli w tamtym miejscu realizowany jest blok chroniony instrukcji��&�������&, to mamy do czynienia z zagnieżdżeniem dynamicznym. Zagnieżdżenie dy-namiczne można zrealizować na kilka sposobów, a najbardziej chyba intuicyjnym (i za-pewne znanym Czytelnikowi z języków wysokiego poziomu) z nich jest wywołanie

12Chyba że sterowanie zostanie wcześniej przekazane z powrotem do bloku ��7�� �(��7, na przykład

kiedy jest on umieszczony w ciele pętli.13Zagnieżdżenie statyczne należy rozumieć jako „fizyczne”, bo wynikające wprost ze struktury kodu

źródłowego, umieszczenie jednego bloku wewnątrz drugiego. Kiedy mowa o zagnieżdżaniu instrukcji,

zwykle chodzi o zagnieżdżanie statyczne.

Page 39: Asembler. Sztuka programowania

58 Asembler. Sztuka programowania

procedury. Kiedy więc w dalszej części książki omawiany będzie sposób pisania i wywo-ływania procedur w języku asemblerowym, należy pamiętać, że wywołanie proceduryz bloku kodu chronionego może doprowadzić do dynamicznego zagnieżdżenia bloków��&�������& — wystarczy, aby procedura również zawierała blok ��&�������& (i abyzostał on wykonany).

1.11.2. Klauzula unprotected bloku try..endtry

Wykonywanie instrukcji ��& powoduje zawsze zachowanie bieżącego środowiska wyjąt-ków i przygotowanie systemu wykonawczego (na wypadek zgłoszenia wyjątku) do prze-kazania sterowania do odpowiedniej klauzuli ��!�����. Jeśli program przebrnie przezblok instrukcji chronionych, pierwotne środowisko wyjątków jest przywracane, a ste-rowanie przekazywane jest do pierwszej instrukcji znajdującej się za klauzulą �����&.Bardzo ważną rolę w tej procedurze ma ostatni jej etap, czyli przywrócenie pierwot-nego środowiska wyjątków. W przypadku jego pominięcia wszelkie kolejne wyjątkipowodowałyby przekazanie sterowania do opuszczonego już bloku ��&�������&. Problemten ilustruje listing.1.10.

Listing 1.10. Niepoprawne opuszczenie bloku instrukcji try..endtry

*��=��9�� ����(��*)�L3J���)( &�+��(��,�##%+�'

�����������*)�-������)����3

, =���� ����(��*)�L3

����// Poniższa pętla jest kontynuowana dopóty, dopóki użytkownik wprowadza����// nieprawidłową liczbę; wyjście z pętli następuje w wyniku wykonania instrukcji break.

����%�� � �

����������7

��������������(�)��*)�&�+5�(�8���, ���4�.���-�+�'3��������������(���= �&���*)��'3��������������(�)��*)�&�+6*��.�(���-�+2���*)�2����'3������������,� �43

�������� ! *����&� !�;��) @)�@%A��= �'

��������������(�)��*)�&�+1�,��8 ���,7��()�3�*�.����.*��.�(��� �+����'3

�������� ! *����&� !�?��� �����������'

��������������(�)��*)�&�+?��=��.� ����� (�.���� ���4�3�*�.����.*��.�(��� �+���'3

�������� �(��73

���� �(%��3

Page 40: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 59

����// Uwaga: poniższy kod znajduje się poza pętlą i nie jest osadzony w bloku chronionym����// instrukcji try..endtry

������(�)��*)�&�+5�(�8�8 � �8 (�����, -�+�'3������(���= �&���*)��'3������(�)��*)�&�+/�.����,����-+2���*)�2����'3

�(�� ����(��*)�L3

Niniejszy przykład z pozoru implementuje niezawodny, odporny na błędy system wpro-wadzania danych: wokół bloku kodu chronionego rozciągnięta jest pętla wymuszającaponowne wprowadzenie danych przez użytkownika, jeśli w poprzedniej próbie nieudało się ich poprawnie odczytać. Pomysł jest dobry; problem tkwi w jego nieprawi-dłowej realizacji. Otóż instrukcja ����$ powoduje opuszczenie pętli �������������bez przywrócenia poprzedniego stanu środowiska wyjątków. Z tego względu, kiedyprogram wykonuje procedurę �����%�� umieszczoną poza pętlą (a tym bardziej pozablokiem kodu chronionego), system obsługi wyjątków wciąż traktuje jej kod jako chro-niony. Jeśli więc przy okazji jego wykonania zgłoszony zostanie wyjątek, sterowaniezostanie przekazane z powrotem do stosownej klauzuli ��!����� opuszczonego jużprzecież bloku ��&�������&. Jeśli identyfikatorem wyjątku będzie ��������+��+,��%�albo ���)��������*����, wyświetlony zostanie stosowny komunikat, a użytkownikponownie zmuszony będzie do wprowadzenia liczby. Tego zaś programista raczejnie przewidywał.

Przekazanie sterowania do nieodpowiedniego bloku ��&�������& to tylko część pro-blemu. Kod z listingu 1.10 obarczony jest inną poważną wadą, związaną ze sposobemzachowywania i następnie przywracania przez system wykonawczy HLA środowiskawyjątków. W szczególności bowiem HLA zachowuje informacje o stanie systemuwyjątków w specjalnym obszarze pamięci zwanym stosem. Jeśli po wyjściu z bloku��&�������& stan środowiska wyjątków nie zostanie przywrócony, informacje o tymśrodowisku pozostaną na stosie; obecność na nim nadmiarowych danych może zaś do-prowadzić do błędnego działania programu.

Jest już chyba jasne, że program nie powinien opuszczać bloku ��&�������& z pomi-nięciem etapu przywrócenia pierwotnego stanu środowiska wyjątków. Wiadomo, żesposób wprowadzania danych zaprezentowany na listingu 1.10 jest niepoprawny, alez drugiej strony sama koncepcja wymuszania powtarzania operacji wprowadzania ażdo skutku jest jak najbardziej słuszna. Można ją zaimplementować przy użyciu specjal-nej klauzuli bloku ��&�������&. Spójrzmy na kod z listingu 1.11.

Listing 1.11. Blok pozbawiony ochrony wewnątrz bloku try..endtry

*��=��9�� ����(��*)��3J���)( &�+��(��,�##%+�'

�����������*)�-����)����3

, =���� ����(��*)��3

Page 41: Asembler. Sztuka programowania

60 Asembler. Sztuka programowania

����// Poniższa nieskończona pętla wykonywana jest dopóty, dopóki użytkownik wprowadza����// niepoprawne dane. Wyjście z pętli następuje w wyniku wykonania instrukcji break.����// Instrukcja ta, mimo że obecna wewnątrz bloku try..endtry, znajduje się w bloku����// kodu niechronionego sygnalizowanego klauzulą unprotected.

����%�� � �

����������7

��������������(�)��*)�&�+5�(�8���, ���4�.���-�+�'3��������������(���= �&���*)��'3��������������(�)��*)�&�+6*��.�(���-�+2���*)�2����'3

��������)�*��� � (

������������,� �43

�������� ! *����&� !�;��) @)�@%A��= �'

��������������(�)��*)�&�+1�,��8 ���,7��()�3�*�.����.*��.�(��� �+����'3

�������� ! *����&� !�?��� �����������'

��������������(�)��*)�&�+?��=��.� ����� (�.���� ���4�3�*�.����.*��.�(��� �+���'3

�������� �(��73

���� �(%��3

����// Uwaga: poniższy kod znajduje się poza pętlą i nie jest osadzony w bloku chronionym����// instrukcji try..endtry

������(�)��*)�&�+5�(�8�8 � �8 (�����, -�+�'3������(���= �&���*)��'3������(�)��*)�&�+/�.����,����-+2���*)�2����'3

�(�� ����(��*)��3

Osiągnięcie klauzuli �������!��� inicjuje operację przywrócenia pierwotnego stanuśrodowiska wyjątków. Oczywiste jest, że po przejściu do bloku kodu niechronionegowykonanie kodu nie jest już chronione na wypadek zgłoszenia wyjątków. Klauzula�������!��� nie znosi jednak ochrony kodu realizowanej w dynamicznie zagnieżdżo-nych zewnętrznych blokach ��&�������& — klauzula �������!��� odnosi się jedyniedo tego bloku ��&�������&, w którym została umieszczona. Jako że na listingu 1.11instrukcja przerywająca pętlę, ����$, znajduje się za klauzulą �������!���, przekazaniesterowania poza pętlę odbywa się po uprzednim przywróceniu stanu wyjątków.

Słowo �������!���, jeśli już występuje w ramach instrukcji ��&, powinno znajdowaćsię bezpośrednio za blokiem kodu chronionego. Musi więc poprzedzać wszystkie klau-zule ��!�����.

Page 42: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 61

W obliczu zgłoszenia wyjątku system wykonawczy HLA automatycznie przywracapierwotny stan środowiska wykonania. Z tego względu w ramach kodu obsługi wyjątku,po klauzuli ��!����� można swobodnie korzystać z instrukcji ����$, nie obawiając sięo negatywne efekty charakterystyczne dla niepoprawnego opuszczenia bloku ��&�������&.

Z racji przywrócenia pierwotnego stanu wyjątków w bloku niechronionym oraz w kodzieobsługi wyjątku po klauzuli ��!�����, wystąpienie wyjątku w jednym z tych obszarówpowoduje natychmiastowe przekazanie sterowania do zewnętrznego bloku ��&�������&,w którym blok bieżący został dynamicznie zagnieżdżony. W przypadku braku takiegobloku program jest awaryjnie przerywany, a na standardowe wyjście wyprowadzanyjest stosowny komunikat o błędzie.

1.11.3. Klauzula anyexception bloku try..endtry

Typowo instrukcji ��& towarzyszy szereg klauzul ��!����� obsługujących wszelkiewyjątki, których wystąpienie w kodzie chronionym zdoła przewidzieć programista.Niekiedy to nie wystarcza — konieczne jest wtedy zapewnienie obsługi wszystkich moż-liwych (a nie tylko przewidzianych) wyjątków, aby program nie został przedwcześniezakończony. Jeśli programista jest autorem całości kodu bloku chronionego, powinienbyć zdolny do przewidzenia wszystkich sytuacji wyjątkowych związanych potencjalniez wykonaniem bloku. Jeśli jednak w bloku tym znajdują się wywołanie procedury biblio-tecznej, wywołanie funkcji interfejsu systemu operacyjnego czy inne instrukcje, niew pełni kontrolowane przez programistę, przewidzenie wszystkich możliwych wyjątkówmoże być niemożliwe. Tymczasem zgłoszenie wyjątku o identyfikatorze innym niżidentyfikatory podane w kolejnych klauzulach ��!����� może doprowadzić do załamaniaprogramu. Szczęśliwie język HLA przewiduje umieszczenie w bloku ��&�������&klauzuli ��&��!�����, do której dopasowywane są wszystkie wyjątki, których identy-fikatorów nie uda się odpasować do klauzul ��!�����.

Klauzula ��&��!����� nie różni się wiele od klauzuli ��!����� — jedyna odmiennośćtkwi w braku konieczności określania identyfikatora wyjątku (to oczywiste). Jeśli obokklauzul ��!����� w bloku ��&�������& znajduje się klauzula ��&��!�����, powinnaona być ostatnią klauzulą obsługi wyjątków w bloku. Dopuszczalne jest naturalnie, abyklauzula ��&��!����� była jedyną klauzulą obsługi wyjątków w bloku ��&�������&.

Po przekazaniu sterowania do klauzuli ��&��!����� identyfikator wyjątku umiesz-czany jest w rejestrze EAX. Identyfikator ten można w ramach kodu obsługi wyjątkusprawdzać, dopasowując obsługę do przyczyny zgłoszenia wyjątku.

1.11.4. Instrukcja try..endtry i rejestry

Każdorazowe wkroczenie do bloku ��&�������& oznacza konieczność zachowania nastosie 16 bajtów informacji o stanie środowiska wyjątków. Przywrócenie środowiskawyjątków po opuszczeniu tego bloku (albo w wyniku osiągnięcia klauzuli �������!���)wymaga odczytania właśnie owych 16 bajtów. Do momentu zgłoszenia wyjątku reali-zowanie kodu chronionego nie wpływa na zawartość żadnych rejestrów. Sytuacja zmieniasię w momencie zgłoszenia wyjątku w wyniku wykonania instrukcji kodu chronionego.

Page 43: Asembler. Sztuka programowania

62 Asembler. Sztuka programowania

Po przejściu do klauzuli ��!����� rejestr EAX zawiera identyfikator (numer) wyjątku.Reszta rejestrów ogólnego przeznaczenia zawiera wartości nieokreślone. Ponieważsystem operacyjny może zgłosić wyjątek w reakcji na błąd sprzętowy, nie powinnosię nawet zakładać, że rejestry ogólnego przeznaczenia zawierać będą po rozpoczęciuobsługi wyjątku wartości, które znajdowały się w nich pierwotnie. Kod generowany przezsystem HLA do obsługi wyjątków może być różny w różnych wersjach kompilatora,więc poleganie na zawartości rejestrów w kodzie obsługi wyjątków jest co najmniejryzykowne.

Jako że po przejściu do bloku kodu obsługi wyjątku nie można czynić żadnych założeńco do zawartości rejestrów (z wyjątkiem rejestru EAX), to jeśli kod kontynuowany zaklauzulą �����& zakłada obecność w rejestrach jakichś konkretnych wartości (np.wartości umieszczonych tam przed wkroczeniem do bloku kodu chronionego), należy tewartości przywrócić samodzielnie. Zaniedbanie tego może doprowadzić do błędnegodziałania programu, przy czym błędy tego rodzaju są tym trudniejsze do wykrycia, żesytuacje wyjątkowe z definicji zdarzają się rzadko, co utrudnia odtworzenie i diagnostykębłędu; dodatkowym utrudnieniem jest to, że nie zawsze zgłoszenie wyjątku musi zmienićwartość konkretnego rejestru. Sposób rozwiązania problemu zachowania wartości reje-strów prezentuje następujący fragment kodu:

����������)9-���������3O

����9��&�"2��)9�'3����%��&�9��&�"2� ,!�'3� ,!� ��3���&� ,!�'�'�(�

��������*)�#&� ,!�'3�����// Zachowanie wartości rejestru EBX na wypadek wyjątku.��������%�� � ���������������7

������������������(���= ����&'3����������������)�*��� � (�,� �43

������������ ! *����&� !�?��� �����������'

������������������(�)��*)�&�+/� (�.������.����PQ3��*��,)8�*���.�� -�+�'3

������������ �(��73�������� �(%��3��������*�*&� ,!�'3����// Przywrócenie zawartości rejestru EBX.���������((&� ,!2� �!�'3���������((&� �!2��)9�'3

�(%��3

Ponieważ mechanizm obsługi wyjątków powoduje potencjalne zmiany wartości reje-strów i ponieważ obsługa wyjątków to proces stosunkowo nieefektywny, nie powinno sięstosować instrukcji ��& w roli zwykłej struktury sterującej wykonaniem programu (naprzykład symulując w bloku ��&�������& działanie instrukcji wyboru znanych z językówwysokiego poziomu, jak !��� czy ���! ). Takie praktyki wpływają ujemnie na wy-dajność programu; mogą tez wprowadzać niepożądane, trudne do wykrycia i zdiagno-zowania efekty uboczne wynikające z zakłócania zawartości rejestrów.

Page 44: Asembler. Sztuka programowania

Rozdział 1. ♦ Wstęp do języka asemblerowego 63

Działanie mechanizmu wyjątków opiera się na założeniu, że rejestr EBP wykorzy-stywany jest wyłącznie w roli wskaźnika rekordów aktywacji (rekordy aktywacjiomawiane są w rozdziale poświęconym procedurom). Standardowo programy HLAwykorzystują ów rejestr właśnie w tej roli. Warunkiem poprawnego działania pro-gramów jest więc unikanie modyfikowania zawartości rejestru EBP. Jeśli rejestr tenzostanie użyty w roli rejestru ogólnego przeznaczenia, na przykład w obliczeniacharytmetycznych, obsługa wyjątków systemu wykonawczego HLA nie będzie działaćpoprawnie, pojawić się też mogą dodatkowe problemy. To samo tyczy się zresztą re-jestru ESP — jego również nie należy wykorzystywać jako rejestru ogólnego prze-znaczenia.