IDZ DO
         PRZYK£ADOWY ROZDZIA£

                           SPIS TRE CI   Asembler.
                                         Sztuka programowania
           KATALOG KSI¥¯EK
                                         Autor: Randall Hyde
                      KATALOG ONLINE     T³umaczenie: Przemys³aw Szeremiota
                                         ISBN: 83-7361-602-0
       ZAMÓW DRUKOWANY KATALOG           Tytu³ orygina³u: The Art of Assembly Language
                                         Format: B5, stron: 968

              TWÓJ KOSZYK
                    DODAJ DO KOSZYKA     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,
         CENNIK I INFORMACJE             kompilacji i uruchamiania programów wykorzystuj¹cych ró¿ne, nawet najbardziej
                                         z³o¿one typy danych.
                   ZAMÓW INFORMACJE         • Wprowadzenie do jêzyka HLA
                     O NOWO CIACH           • Sposoby reprezentacji danych
                                            • Organizacja pamiêci i tryby adresowania
                       ZAMÓW CENNIK         • Typy danych
                                            • Podzia³ programu na procedury i modu³y
                                            • Sterowanie wykonaniem programu
                 CZYTELNIA                  • Instrukcje arytmetyczne
                                            • Operacje na plikach
          FRAGMENTY KSI¥¯EK ONLINE          • 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




Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl
Spis treści
Rozdział 1. Wstęp do języka asemblerowego...................................................... 13
               1.1. Wprowadzenie........................................................................................................ 13
               1.2. Anatomia programu HLA....................................................................................... 13
               1.3. Uruchamianie pierwszego programu HLA ............................................................. 16
               1.4. Podstawowe deklaracje danych programu HLA..................................................... 17
               1.5. Wartości logiczne ................................................................................................... 19
               1.6. Wartości znakowe................................................................................................... 20
               1.7. Rodzina procesorów 80x86 firmy Intel .................................................................. 21
                    1.7.1. Podsystem obsługi pamięci........................................................................... 24
               1.8. Podstawowe instrukcje maszynowe........................................................................ 26
               1.9. Podstawowe struktury sterujące wykonaniem programu HLA............................... 30
                    1.9.1. Wyra enia logiczne w instrukcjach HLA ..................................................... 31
                    1.9.2. Instrukcje if..then..elseif..else..endif języka HLA ......................................... 33
                    1.9.3. Iloczyn, suma i negacja w wyra eniach logicznych...................................... 35
                    1.9.4. Instrukcja while ............................................................................................ 37
                    1.9.5. Instrukcja for................................................................................................. 38
                    1.9.6. Instrukcja repeat............................................................................................ 39
                    1.9.7. Instrukcje break oraz breakif......................................................................... 40
                    1.9.8. Instrukcja forever.......................................................................................... 40
                    1.9.9. Instrukcje try, exception oraz endtry............................................................. 41
              1.10. Biblioteka standardowa języka HLA — wprowadzenie ......................................... 44
                  1.10.1. Stałe predefiniowane w module stdio ........................................................... 46
                  1.10.2. Standardowe wejście i wyjście programu ..................................................... 46
                  1.10.3. Procedura stdout.newln................................................................................. 47
                  1.10.4. Procedury stdout.putiN ................................................................................. 47
                  1.10.5. Procedury stdout.putiNSize .......................................................................... 48
                  1.10.6. Procedura stdout.put ..................................................................................... 49
                  1.10.7. Procedura stdin.getc...................................................................................... 51
                  1.10.8. Procedury stdin.getiN ................................................................................... 52
                  1.10.9. Procedury stdin.readLn i stdin.flushInput..................................................... 53
                 1.10.10. Procedura stdin.get ....................................................................................... 54
              1.11. Jeszcze o ochronie wykonania kodu w bloku try..endtry........................................ 55
                  1.11.1. Zagnie d one bloki try..endtry ..................................................................... 56
                  1.11.2. Klauzula unprotected bloku try..endtry......................................................... 58
                  1.11.3. Klauzula anyexception bloku try..endtry ...................................................... 61
                  1.11.4. Instrukcja try..endtry i rejestry...................................................................... 61
              1.12. Język asemblerowy a język HLA ........................................................................... 63
              1.13. Źródła informacji dodatkowych.............................................................................. 64
4                                                                                          Asembler. Sztuka programowania


Rozdział 2. Reprezentacja danych ...................................................................... 65
                2.1. Wprowadzenie........................................................................................................ 65
                2.2. Systemy liczbowe ................................................................................................... 66
                     2.2.1. System dziesiętny — przypomnienie............................................................ 66
                     2.2.2. System dwójkowy......................................................................................... 66
                     2.2.3. Formaty liczb dwójkowych........................................................................... 68
                2.3. System szesnastkowy.............................................................................................. 69
                2.4. Organizacja danych ................................................................................................ 72
                     2.4.1. Bity ............................................................................................................... 72
                     2.4.2. Półbajty......................................................................................................... 73
                     2.4.3. Bajty ............................................................................................................. 73
                     2.4.4. Słowa ............................................................................................................ 75
                     2.4.5. Podwójne słowa ............................................................................................ 76
                     2.4.6. Słowa poczwórne i długie............................................................................. 77
                2.5. Operacje arytmetyczne na liczbach dwójkowych i szesnastkowych....................... 77
                2.6. Jeszcze o liczbach i ich reprezentacji...................................................................... 78
                2.7. Operacje logiczne na bitach.................................................................................... 81
                2.8. Operacje logiczne na liczbach dwójkowych i ciągach bitów.................................. 84
                2.9. Liczby ze znakiem i bez znaku ............................................................................... 86
               2.10. Rozszerzanie znakiem, rozszerzanie zerem, skracanie, przycinanie....................... 91
               2.11. Przesunięcia i obroty .............................................................................................. 95
               2.12. Pola bitowe i dane spakowane................................................................................ 99
               2.13. Wprowadzenie do arytmetyki zmiennoprzecinkowej ........................................... 104
                   2.13.1. Formaty zmiennoprzecinkowe przyjęte przez IEEE ................................... 108
                   2.13.2. Obsługa liczb zmiennoprzecinkowych w języku HLA ............................... 112
               2.14. Reprezentacja liczb BCD ..................................................................................... 115
               2.15. Znaki .................................................................................................................... 117
                   2.15.1. Zestaw znaków ASCII................................................................................ 117
                   2.15.2. Obsługa znaków ASCII w języku HLA...................................................... 121
               2.16. Zestaw znaków Unicode....................................................................................... 125
               2.17. Źródła informacji dodatkowych............................................................................ 126
Rozdział 3. Dostęp do pamięci i jej organizacja ................................................ 127
                 3.1. Wprowadzenie...................................................................................................... 127
                 3.2. Tryby adresowania procesorów 80x86 ................................................................. 127
                      3.2.1. Adresowanie przez rejestr........................................................................... 128
                      3.2.2. 32-bitowe tryby adresowania procesora 80x86........................................... 129
                 3.3. Organizacja pamięci fazy wykonania ................................................................... 135
                      3.3.1. Obszar kodu................................................................................................ 137
                      3.3.2. Obszar zmiennych statycznych................................................................... 139
                      3.3.3. Obszar niemodyfikowalny .......................................................................... 140
                      3.3.4. Obszar danych niezainicjalizowanych ........................................................ 141
                      3.3.5. Atrybut @nostorage.................................................................................... 141
                      3.3.6. Sekcja deklaracji var ................................................................................... 142
                      3.3.7. Rozmieszczenie sekcji deklaracji danych w programie HLA ..................... 143
                 3.4. Przydział pamięci dla zmiennych w programach HLA......................................... 144
                 3.5. Wyrównanie danych w programach HLA ............................................................ 146
                 3.6. Wyra enia adresowe............................................................................................. 149
                 3.7. Koercja typów ...................................................................................................... 151
                 3.8. Koercja typu rejestru ............................................................................................ 154
                 3.9. Pamięć obszaru stosu oraz instrukcje push i pop .................................................. 155
                      3.9.1. Podstawowa postać instrukcji push............................................................. 155
                      3.9.2. Podstawowa postać instrukcji pop .............................................................. 157
                      3.9.3. Zachowywanie wartości rejestrów za pomocą instrukcji push i pop .......... 158
Spis treści                                                                                                                                    5


                     3.9.4. Stos jako kolejka LIFO ............................................................................... 159
                     3.9.5. Pozostałe wersje instrukcji obsługi stosu.................................................... 161
                     3.9.6. Usuwanie danych ze stosu bez ich zdejmowania ........................................ 163
                     3.9.7. Odwoływanie się do danych na stosie bez ich zdejmowania ...................... 165
               3.10. Dynamiczny przydział pamięci — obszar pamięci sterty ..................................... 166
               3.11. Instrukcje inc oraz dec.......................................................................................... 171
               3.12. Pobieranie adresu obiektu..................................................................................... 171
               3.13. Źródła informacji dodatkowych............................................................................ 172
Rozdział 4. Stałe, zmienne i typy danych.......................................................... 173
                4.1. Wprowadzenie...................................................................................................... 173
                4.2. Kilka dodatkowych instrukcji: intmul, bound i into ............................................. 174
                4.3. Typ tbyte............................................................................................................... 178
                4.4. Deklaracje stałych i zmiennych w języku HLA.................................................... 178
                     4.4.1. Typy stałych ............................................................................................... 182
                     4.4.2. Literały stałych łańcuchowych i znakowych............................................... 183
                     4.4.3. Stałe łańcuchowe i napisowe w sekcji const............................................... 185
                     4.4.4. Wyra enia stałowartościowe....................................................................... 186
                     4.4.5. Wielokrotne sekcje const i ich kolejność w programach HLA ................... 189
                     4.4.6. Sekcja val programu HLA .......................................................................... 190
                     4.4.7. Modyfikowanie obiektów sekcji val w wybranym miejscu
                            kodu źródłowego programu ....................................................................... 191
                4.5. Sekcja type programu HLA .................................................................................. 192
                4.6. Typy wyliczeniowe w języku HLA ...................................................................... 193
                4.7. Typy wskaźnikowe ............................................................................................... 194
                     4.7.1. Wskaźniki w języku asemblerowym........................................................... 196
                     4.7.2. Deklarowanie wskaźników w programach HLA ........................................ 197
                     4.7.3. Stałe wskaźnikowe i wyra enia stałych wskaźnikowych............................ 197
                     4.7.4. Zmienne wskaźnikowe a dynamiczny przydział pamięci ........................... 199
                     4.7.5. Typowe błędy stosowania wskaźników ...................................................... 200
                4.8. Moduł chars.hhf biblioteki standardowej HLA .................................................... 205
                4.9. Zło one typy danych ............................................................................................ 207
               4.10. Łańcuchy znaków................................................................................................. 208
               4.11. Łańcuchy w języku HLA...................................................................................... 210
               4.12. Odwołania do poszczególnych znaków łańcucha ................................................. 217
               4.13. Moduł strings biblioteki standardowej HLA i procedury manipulacji łańcuchami...219
               4.14. Konwersje wewnątrzpamięciowe ......................................................................... 231
               4.15. Zbiory znaków...................................................................................................... 232
               4.16. Implementacja zbiorów znaków w języku HLA................................................... 233
               4.17. Literały, stałe i wyra enia zbiorów znaków w języku HLA ................................. 235
               4.18. Operator in w wyra eniach logicznych
                     wysokopoziomowego rozszerzenia języka HLA.................................................. 237
               4.19. Obsługa zbiorów znaków w bibliotece standardowej HLA.................................. 237
               4.20. Wykorzystywanie zbiorów znaków w programach HLA ..................................... 241
               4.21. Tablice.................................................................................................................. 243
               4.22. Deklarowanie tablic w programach HLA ............................................................. 244
               4.23. Literały tablicowe................................................................................................. 245
               4.24. Odwołania do elementów tablicy jednowymiarowej ............................................ 246
                   4.24.1. Porządkowanie tablicy wartości ................................................................. 248
               4.25. Tablice wielowymiarowe ..................................................................................... 250
                   4.25.1. Wierszowy układ elementów tablicy .......................................................... 251
                   4.25.2. Kolumnowy układ elementów tablicy ........................................................ 255
               4.26. Przydział pamięci dla tablic wielowymiarowych ................................................. 256
               4.27. Odwołania do elementów tablic wielowymiarowych w języku asemblerowym... 258
6                                                                                           Asembler. Sztuka programowania


               4.28. Du e tablice i MASM (tylko dla programistów systemu Windows) .................... 259
               4.29. Rekordy (struktury) .............................................................................................. 260
               4.30. Stałe rekordowe.................................................................................................... 263
               4.31. Tablice rekordów.................................................................................................. 264
               4.32. Wykorzystanie tablic i rekordów w roli pól rekordów ......................................... 265
               4.33. Ingerowanie w przesunięcia pól rekordów ........................................................... 269
               4.34. Wyrównanie pól w ramach rekordu...................................................................... 270
               4.35. Wskaźniki na rekordy........................................................................................... 271
               4.36. Unie ...................................................................................................................... 273
               4.37. Unie anonimowe................................................................................................... 275
               4.38. Typy wariantowe .................................................................................................. 276
               4.39. Stałe unii............................................................................................................... 277
               4.40. Przestrzenie nazw ................................................................................................. 278
               4.41. Tablice dynamiczne w języku asemblerowym ..................................................... 281
               4.42. Obsługa tablic w bibliotece standardowej języka HLA ........................................ 284
               4.43. Źródła informacji dodatkowych............................................................................ 287
Rozdział 5. Procedury i moduły ........................................................................ 289
                5.1. Wprowadzenie...................................................................................................... 289
                5.2. Procedury.............................................................................................................. 289
                5.3. Zachowywanie stanu systemu .............................................................................. 292
                5.4. Przedwczesny powrót z procedury ....................................................................... 296
                5.5. Zmienne lokalne ................................................................................................... 297
                5.6. Symbole lokalne i globalne obiektów innych ni zmienne................................... 303
                5.7. Parametry.............................................................................................................. 304
                     5.7.1. Przekazywanie przez wartość ..................................................................... 305
                     5.7.2. Przekazywanie przez adres ......................................................................... 308
                5.8. Funkcje i wartości funkcji .................................................................................... 311
                     5.8.1. Zwracanie wartości funkcji......................................................................... 312
                     5.8.2. Zło enie instrukcji języka HLA .................................................................. 313
                     5.8.3. Atrybut @returns procedur języka HLA..................................................... 316
                5.9. Rekurencja............................................................................................................ 318
               5.10. Deklaracje zapowiadające .................................................................................... 322
               5.11. Procedury w ujęciu niskopoziomowym — instrukcja call.................................... 323
               5.12. Rola stosu w procedurach..................................................................................... 325
               5.13. Rekordy aktywacji................................................................................................ 328
               5.14. Standardowa sekwencja wejścia do procedury ..................................................... 331
               5.15. Standardowa sekwencja wyjścia z procedury....................................................... 333
               5.16. Niskopoziomowa implementacja zmiennych automatycznych ............................ 334
               5.17. Niskopoziomowa implementacja parametrów procedury..................................... 336
                   5.17.1. Przekazywanie argumentów w rejestrach ................................................... 337
                   5.17.2. Przekazywanie argumentów w kodzie programu........................................ 340
                   5.17.3. Przekazywanie argumentów przez stos....................................................... 342
               5.18. Wskaźniki na procedury ....................................................................................... 365
               5.19. Parametry typu procedurowego............................................................................ 368
               5.20. Nietypowane parametry wskaźnikowe ................................................................. 370
               5.21. Zarządzanie du ymi projektami programistycznymi............................................ 371
               5.22. Dyrektywa #include ............................................................................................. 372
               5.23. Unikanie wielokrotnego włączania do kodu tego samego pliku........................... 374
               5.24. Moduły a atrybut @external................................................................................. 375
                   5.24.1. Działanie atrybutu @external ..................................................................... 380
                   5.24.2. Pliki nagłówkowe w programach HLA....................................................... 382
               5.25. Jeszcze o problemie zaśmiecania przestrzeni nazw .............................................. 384
               5.26. Źródła informacji dodatkowych............................................................................ 386
Spis treści                                                                                                                                7


Rozdział 6. Arytmetyka.................................................................................... 389
                 6.1. Wprowadzenie...................................................................................................... 389
                 6.2. Zestaw instrukcji arytmetycznych procesora 80x86 ............................................. 389
                      6.2.1. Instrukcje mul i imul................................................................................... 389
                      6.2.2. Instrukcje div i idiv..................................................................................... 393
                      6.2.3. Instrukcja cmp ............................................................................................ 396
                      6.2.4. Instrukcje setXX ......................................................................................... 401
                      6.2.5. Instrukcja test.............................................................................................. 403
                 6.3. Wyra enia arytmetyczne ...................................................................................... 404
                      6.3.1. Proste przypisania ....................................................................................... 405
                      6.3.2. Proste wyra enia ......................................................................................... 406
                      6.3.3. Wyra enia zło one...................................................................................... 408
                      6.3.4. Operatory przemienne................................................................................. 413
                 6.4. Wyra enia logiczne .............................................................................................. 414
                 6.5. Idiomy maszynowe a idiomy arytmetyczne.......................................................... 417
                      6.5.1. Mno enie bez stosowania instrukcji mul, imul i intmul.............................. 417
                      6.5.2. Dzielenie bez stosowania instrukcji div i idiv............................................. 419
                      6.5.3. Zliczanie modulo n za pośrednictwem instrukcji and ................................. 420
                      6.5.4. Nieostro ne korzystanie z idiomów maszynowych .................................... 420
                 6.6. Arytmetyka zmiennoprzecinkowa ........................................................................ 421
                      6.6.1. Rejestry jednostki zmiennoprzecinkowej.................................................... 421
                      6.6.2. Typy danych jednostki zmiennoprzecinkowej ............................................ 429
                      6.6.3. Zestaw instrukcji jednostki zmiennoprzecinkowej ..................................... 430
                      6.6.4. Instrukcje przemieszczania danych............................................................. 431
                      6.6.5. Instrukcje konwersji.................................................................................... 433
                      6.6.6. Instrukcje arytmetyczne .............................................................................. 436
                      6.6.7. Instrukcje porównań ................................................................................... 442
                      6.6.8. Instrukcje ładowania stałych na stos koprocesora....................................... 445
                      6.6.9. Instrukcje funkcji przestępnych .................................................................. 445
                    6.6.10. Pozostałe instrukcje jednostki zmiennoprzecinkowej ................................. 447
                    6.6.11. Instrukcje operacji całkowitoliczbowych.................................................... 449
                 6.7. Tłumaczenie wyra eń arytmetycznych na kod maszynowy
                      jednostki zmiennoprzecinkowej........................................................................... 449
                      6.7.1. Konwersja notacji wrostkowej do odwrotnej notacji polskiej..................... 451
                      6.7.2. Konwersja odwrotnej notacji polskiej do kodu języka asemblerowego...... 453
                 6.8. Obsługa arytmetyki zmiennoprzecinkowej w bibliotece standardowej języka HLA.....455
                      6.8.1. Funkcje stdin.getf i fileio.getf ..................................................................... 455
                      6.8.2. Funkcje trygonometryczne modułu math.................................................... 455
                      6.8.3. Funkcje wykładnicze i logarytmiczne modułu math................................... 456
                 6.9. Podsumowanie...................................................................................................... 458
Rozdział 7. Niskopoziomowe struktury sterujące wykonaniem programu............ 459
                 7.1. Wprowadzenie...................................................................................................... 459
                 7.2. Struktury sterujące niskiego poziomu................................................................... 460
                 7.3. Etykiety instrukcji................................................................................................. 460
                 7.4. Bezwarunkowy skok do instrukcji (instrukcja jmp) ............................................. 462
                 7.5. Instrukcje skoku warunkowego ............................................................................ 465
                 7.6. Struktury sterujące „średniego” poziomu — jt i jf................................................ 468
                 7.7. Implementacja popularnych struktur sterujących w języku asemblerowym ......... 469
                 7.8. Wstęp do podejmowania decyzji .......................................................................... 469
                      7.8.1. Instrukcje if..then..else................................................................................ 471
                      7.8.2. Tłumaczenie instrukcji if języka HLA na język asemblerowy.................... 475
                      7.8.3. Obliczanie wartości zło onych wyra eń logicznych
                              — metoda pełnego szacowania wartości wyra enia................................... 480
8                                                                                             Asembler. Sztuka programowania


                       7.8.4. Skrócone szacowanie wyra eń logicznych ................................................. 481
                       7.8.5. Wady i zalety metod szacowania wartości wyra eń logicznych ................. 483
                       7.8.6. Efektywna implementacja instrukcji if w języku asemblerowym ............... 485
                       7.8.7. Instrukcje wyboru ....................................................................................... 490
                  7.9. Skoki pośrednie a automaty stanów...................................................................... 500
                 7.10. Kod spaghetti........................................................................................................ 503
                 7.11. Pętle...................................................................................................................... 504
                     7.11.1. Pętle while .................................................................................................. 505
                     7.11.2. Pętle repeat..until ........................................................................................ 506
                     7.11.3. Pętle nieskończone ..................................................................................... 508
                     7.11.4. Pętle for ...................................................................................................... 508
                     7.11.5. Instrukcje break i continue.......................................................................... 509
                     7.11.6. Pętle a rejestry ............................................................................................ 513
                 7.12. Optymalizacja kodu.............................................................................................. 514
                     7.12.1. Obliczanie warunku zakończenia pętli na końcu pętli ................................ 515
                     7.12.2. Zliczanie licznika pętli wstecz .................................................................... 517
                     7.12.3. Wstępne obliczanie niezmienników pętli.................................................... 518
                     7.12.4. Rozciąganie pętli ........................................................................................ 519
                     7.12.5. Zmienne indukcyjne ................................................................................... 521
                 7.13. Mieszane struktury sterujące w języku HLA........................................................ 522
                 7.14. Źródła informacji dodatkowych............................................................................ 524
Rozdział 8. Pliki .............................................................................................. 525
                  8.1. Wprowadzenie...................................................................................................... 525
                  8.2. Organizacja plików............................................................................................... 525
                       8.2.1. Pliki jako listy rekordów............................................................................. 526
                       8.2.2. Pliki tekstowe a pliki binarne...................................................................... 528
                  8.3. Pliki sekwencyjne ................................................................................................. 530
                  8.4. Pliki dostępu swobodnego .................................................................................... 538
                  8.5. Indeksowany sekwencyjny dostęp do pliku (ISAM) ............................................ 543
                  8.6. Przycinanie pliku .................................................................................................. 546
                  8.7. Źródła informacji dodatkowych............................................................................ 548
Rozdział 9. Zaawansowane obliczenia w języku asemblerowym......................... 549
                  9.1. Wprowadzenie...................................................................................................... 549
                  9.2. Operacje o zwielokrotnionej precyzji ................................................................... 550
                       9.2.1. Obsługa operacji zwielokrotnionej precyzji
                               w bibliotece standardowej języka HLA...................................................... 550
                       9.2.2. Dodawanie liczb zwielokrotnionej precyzji ................................................ 553
                       9.2.3. Odejmowanie liczb zwielokrotnionej precyzji............................................ 556
                       9.2.4. Porównanie wartości o zwielokrotnionej precyzji ...................................... 558
                       9.2.5. Mno enie operandów zwielokrotnionej precyzji ........................................ 562
                       9.2.6. Dzielenie wartości zwielokrotnionej precyzji ............................................. 565
                       9.2.7. Negacja operandów zwielokrotnionej precyzji ........................................... 575
                       9.2.8. Iloczyn logiczny operandów zwielokrotnionej precyzji.............................. 577
                       9.2.9. Suma logiczna operandów zwielokrotnionej precyzji................................. 577
                     9.2.10. Suma wyłączająca operandów zwielokrotnionej precyzji........................... 578
                     9.2.11. Inwersja operandów zwielokrotnionej precyzji .......................................... 578
                     9.2.12. Przesunięcia bitowe operandów zwielokrotnionej precyzji ........................ 578
                     9.2.13. Obroty operandów zwielokrotnionej precyzji............................................. 583
                     9.2.14. Operandy zwielokrotnionej precyzji w operacjach wejścia-wyjścia........... 583
                  9.3. Manipulowanie operandami ró nych rozmiarów.................................................. 604
                  9.4. Arytmetyka liczb dziesiętnych.............................................................................. 606
                       9.4.1. Literały liczb BCD...................................................................................... 608
                       9.4.2. Instrukcje maszynowe daa i das.................................................................. 608
Spis treści                                                                                                                                9


                      9.4.3. Instrukcje maszynowe aaa, aas, aam i aad .................................................. 610
                      9.4.4. Koprocesor a arytmetyka spakowanych liczb dziesiętnych ........................ 612
                 9.5. Obliczenia w tabelach........................................................................................... 615
                      9.5.1. Wyszukiwanie w tabeli wartości funkcji .................................................... 615
                      9.5.2. Dopasowywanie dziedziny ......................................................................... 620
                      9.5.3. Generowanie tabel wartości funkcji............................................................ 621
                      9.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 .......................................... 627
               10.1. Wprowadzenie...................................................................................................... 627
               10.2. Język czasu kompilacji — wstęp .......................................................................... 627
               10.3. Instrukcje #print i #error....................................................................................... 629
               10.4. Stałe i zmienne czasu kompilacji.......................................................................... 631
               10.5. Wyra enia i operatory czasu kompilacji............................................................... 632
               10.6. Funkcje czasu kompilacji ..................................................................................... 635
                   10.6.1. Funkcje czasu kompilacji — konwersja typów........................................... 636
                   10.6.2. Funkcje czasu kompilacji — obliczenia numeryczne ................................. 638
                   10.6.3. Funkcje czasu kompilacji — klasyfikacja znaków ..................................... 638
                   10.6.4. Funkcje czasu kompilacji — manipulacje łańcuchami znaków .................. 639
                   10.6.5. Funkcje czasu kompilacji — dopasowywanie wzorców............................. 639
                   10.6.6. Odwołania do tablicy symboli .................................................................... 641
                   10.6.7. Pozostałe funkcje czasu kompilacji ............................................................ 643
                   10.6.8. Konwersja typu stałych napisowych........................................................... 643
               10.7. Kompilacja warunkowa........................................................................................ 645
               10.8. Kompilacja wielokrotna (pętle czasu kompilacji)................................................. 650
               10.9. Makrodefinicje (procedury czasu kompilacji) ...................................................... 653
                   10.9.1. Makrodefinicje standardowe....................................................................... 654
                   10.9.2. Argumenty makrodefinicji.......................................................................... 656
                   10.9.3. Symbole lokalne makrodefinicji ................................................................. 663
                   10.9.4. Makrodefinicje jako procedury czasu kompilacji ....................................... 666
                   10.9.5. Symulowane przecią anie funkcji .............................................................. 667
              10.10. Tworzenie programów czasu kompilacji.............................................................. 672
                  10.10.1. Generowanie tabel wartości funkcji............................................................ 673
                  10.10.2. Rozciąganie pętli ........................................................................................ 677
              10.11. Stosowanie makrodefinicji w osobnych plikach kodu źródłowego ...................... 679
              10.12. Źródła informacji dodatkowych ........................................................................... 679
Rozdział 11. Manipulowanie bitami .................................................................... 681
               11.1. Wprowadzenie...................................................................................................... 681
               11.2. Czym są dane bitowe? .......................................................................................... 681
               11.3. Instrukcje manipulujące bitami............................................................................. 683
               11.4. Znacznik przeniesienia w roli akumulatora bitów ................................................ 692
               11.5. Wstawianie i wyodrębnianie łańcuchów bitów .................................................... 693
               11.6. Scalanie zbiorów bitów i rozpraszanie łańcuchów bitowych................................ 696
               11.7. Spakowane tablice łańcuchów bitowych .............................................................. 699
               11.8. Wyszukiwanie bitów ............................................................................................ 701
               11.9. Zliczanie bitów ..................................................................................................... 704
              11.10. Odwracanie łańcucha bitów.................................................................................. 707
              11.11. Scalanie łańcuchów bitowych .............................................................................. 709
              11.12. Wyodrębnianie łańcuchów bitów ......................................................................... 710
              11.13. Wyszukiwanie wzorca bitowego .......................................................................... 712
              11.14. Moduł bits biblioteki standardowej HLA ............................................................. 713
              11.15. Źródła informacji dodatkowych ........................................................................... 715
10                                                                                          Asembler. Sztuka programowania


Rozdział 12. Operacje łańcuchowe..................................................................... 717
                12.1. Wprowadzenie...................................................................................................... 717
                12.2. Instrukcje łańcuchowe procesorów 80x86............................................................ 717
                    12.2.1. Sposób działania instrukcji łańcuchowych ................................................. 718
                    12.2.2. Przedrostki instrukcji łańcuchowych — repX ............................................ 719
                    12.2.3. Znacznik kierunku ...................................................................................... 719
                    12.2.4. Instrukcja movs........................................................................................... 721
                    12.2.5. Instrukcja cmps........................................................................................... 727
                    12.2.6. Instrukcja scas............................................................................................. 731
                    12.2.7. Instrukcja stos............................................................................................. 732
                    12.2.8. Instrukcja lods ............................................................................................ 733
                    12.2.9. Instrukcje lods i stos w zło onych operacjach łańcuchowych .................... 733
                12.3. Wydajność instrukcji łańcuchowych procesorów 80x86 ...................................... 734
                12.4. Źródła informacji dodatkowych............................................................................ 735
Rozdział 13. Instrukcje MMX............................................................................. 737
                13.1. Wprowadzenie...................................................................................................... 737
                13.2. Sprawdzanie obecności rozszerzenia MMX......................................................... 738
                13.3. Środowisko programowania MMX ...................................................................... 739
                    13.3.1. Rejestry MMX............................................................................................ 739
                    13.3.2. Typy danych MMX .................................................................................... 741
                13.4. Przeznaczenie instrukcji zestawu MMX............................................................... 742
                13.5. Arytmetyka z nasycaniem a arytmetyka z zawijaniem ......................................... 743
                13.6. Operandy instrukcji MMX ................................................................................... 744
                13.7. Instrukcje zestawu MMX ..................................................................................... 746
                    13.7.1. Instrukcje transferu danych......................................................................... 747
                    13.7.2. Instrukcje konwersji.................................................................................... 747
                    13.7.3. Arytmetyka operandów spakowanych ........................................................ 752
                    13.7.4. Instrukcje logiczne...................................................................................... 755
                    13.7.5. Instrukcje porównań ................................................................................... 756
                    13.7.6. Instrukcje przesunięć bitowych................................................................... 760
                    13.7.7. Instrukcja emms.......................................................................................... 762
                13.8. Model programowania MMX............................................................................... 763
                13.9. Źródła informacji dodatkowych............................................................................ 774
Rozdział 14. Klasy i obiekty............................................................................... 775
                14.1. Wprowadzenie...................................................................................................... 775
                14.2. Wstęp do programowania obiektowego................................................................ 775
                14.3. Klasy w języku HLA ............................................................................................ 779
                14.4. Obiekty ................................................................................................................. 782
                14.5. Dziedziczenie ....................................................................................................... 784
                14.6. Przesłanianie......................................................................................................... 785
                14.7. Metody wirtualne a procedury statyczne .............................................................. 786
                14.8. Implementacje metod i procedur klas ................................................................... 788
                14.9. Implementacja obiektu ......................................................................................... 793
                    14.9.1. Tabela metod wirtualnych .......................................................................... 796
                    14.9.2. Reprezentacja w pamięci obiektu klasy pochodnej..................................... 798
               14.10. Konstruktory i inicjalizacja obiektów................................................................... 802
                   14.10.1. Konstruktor a dynamiczny przydział obiektu ............................................. 804
                   14.10.2. Konstruktory a dziedziczenie...................................................................... 806
                   14.10.3. Parametry konstruktorów i przecią anie procedur klas .............................. 810
               14.11. Destruktory........................................................................................................... 811
               14.12. Łańcuchy _initialize_ oraz _finalize_ w języku HLA .......................................... 812
               14.13. Metody abstrakcyjne ............................................................................................ 818
Spis treści                                                                                                                           11


              14.14. Informacja o typie czasu wykonania (RTTI) ........................................................ 822
              14.15. Wywołania metod klasy bazowej ......................................................................... 824
              14.16. Źródła informacji dodatkowych ........................................................................... 825
Rozdział 15. Na styku asemblera i innych języków programowania...................... 827
               15.1. Wprowadzenie...................................................................................................... 827
               15.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................. 828
                   15.2.2. Konsolidacja modułów MASM (Gas) z modułami HLA............................ 832
               15.3. Moduły HLA a programy języka Delphi (Kylix) ................................................. 837
                   15.3.1. Konsolidacja modułów HLA z programami języka Delphi (Kylix) ........... 838
                   15.3.2. Zachowywanie wartości rejestrów.............................................................. 842
                   15.3.3. Wartości zwracane funkcji.......................................................................... 843
                   15.3.4. Konwencje wywołań .................................................................................. 849
                   15.3.5. Przekazywanie argumentów przez wartość i adres,
                           parametry niemodyfikowalne i wyjściowe ................................................. 854
                   15.3.6. Skalarne typy danych w językach HLA i Delphi (Kylix) ........................... 856
                   15.3.7. Przekazywanie łańcuchów znaków z Delphi do procedury HLA ............... 858
                   15.3.8. Przekazywanie rekordów z programu w języku Delphi do kodu HLA....... 862
                   15.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 ............. 867
                  15.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++ ......................................................... 870
                   15.4.1. Konsolidacja modułów języka HLA z programami języków C i C++........ 872
                   15.4.2. Zachowywanie wartości rejestrów.............................................................. 875
                   15.4.3. Wartości funkcji ......................................................................................... 876
                   15.4.4. Konwencje wywołań .................................................................................. 876
                   15.4.5. Tryby przekazywania argumentów ............................................................. 880
                   15.4.6. Odwzorowanie typów skalarnych
                           pomiędzy językiem C (C++) a językiem HLA ........................................... 881
                   15.4.7. Przekazywanie łańcuchów znaków
                           pomiędzy programem w języku C (C++) a modułem HLA........................ 883
                   15.4.8. Przekazywanie rekordów
                           pomiędzy programem w języku C (C++) a modułem HLA........................ 883
                   15.4.9. Przekazywanie tablic
                           pomiędzy programem w języku C (C++) a modułem HLA........................ 886
               15.5. Źródła informacji dodatkowych............................................................................ 886
Dodatek A Tabela kodów ASCII....................................................................... 887
Dodatek B Instrukcje procesorów 80x86......................................................... 891
               Skorowidz...................................................................................... 927
1.9. Podstawowe struktury sterujące
wykonaniem programu HLA
   Instrukcje OQX, CFF oraz UWD, choć niezwykle u yteczne, nie wystarczają do napisania
   sensownych programów. Aby takie programy mogły powstać, podstawowe instrukcje
   muszą zostać uzupełnione o podstawowy zbiór struktur sterujących wykonaniem kodu
   umo liwiających tworzenie pętli i podejmowanie decyzji w programie. Język HLA
   przewiduje szereg wysokopoziomowych struktur sterujących wykonaniem charaktery-
   stycznych dla języków wy szego ni asembler poziomu. Programista HLA ma więc do
   dyspozycji konstrukcje KHVJGPGNUGKHGNUGGPFKH, YJKNGGPFYJKNG, TGRGCV
   WPVKN i tak dalej. Dopiero znajomość i umiejętność wykorzystywania owych konstrukcji
   pozwala 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 racji
   niewielkiego jeszcze zasobu wiedzy o języku asemblerowym, nieco sztuczne struktury
   sterujące HLA będą bardzo pomocne.

   Warto przy tym pamiętać, e wysokopoziomowe na pierwszy rzut oka struktury sterujące
   nie są w istocie tak wysokopoziomowe. Mają one bowiem jedynie ułatwić rozpoczęcie
   pisania 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 owe
   wysokopoziomowe struktury sterujące mają szereg ograniczeń, które w miarę nabie-
   rania przez Czytelnika doświadczenia i w miarę komplikowania kolejnych programów
   staną się dość ucią liwe. To zresztą efekt zamierzony — po osiągnięciu pewnego poziomu
   umiejętności programista sam stwierdzi, e w wielu przypadkach wygoda (i czytelność
   programu) wynikająca z zastosowania struktur sterujących HLA nie rekompensuje
   utraty efektywności, charakterystycznej dla kodu wykorzystującego wprost instrukcje
   maszynowe procesora.

   Niniejsze omówienie kierowane jest do tych Czytelników, którzy znają ju przynajmniej
   jeden z popularnych języków wysokiego poziomu. Znajomość ta znakomicie uprości
   prezentację struktur sterujących wykonaniem programu HLA, pozwalając na pominięcie
   opisów sposobów zastosowania owych struktur w typowych zadaniach programistycz-
   nych. Czytelnicy potrafiący korzystać ze struktur sterujących wykonaniem programu
   w 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łownie
   identyczny.
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ślenia
             w 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 KH, YJKNG oraz TGRGCVWPVKN.
             Pierwszą oznaką ograniczeń struktur sterujących wykonaniem programu HLA jest
             skł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ównie
             wymyś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:
               PCEPKM
                PCEPKM
               TGLGUVT
                TGLGUVT
               OKGPPCNQIKEPC
                OKGPPCNQIKEPC
               RCOKúèTGLGUVT QRGTCVQTTGNCELK RCOKúèTGLGUVTUVC C
               TGLGUVT KP UVC C
ITCPKECFQNPCUVC C
ITCPKECIÎTPC
               TGLGUVT PQV KP UVC C
ITCPKECFQNPCUVC C
ITCPKECIÎTPC

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

Tabela 1.2. Symbole reprezentujące znaczniki
    Symbol         Znacznik             Znaczenie
    E             Przeniesienie        „Prawda” dla ustawionego (1) znacznika przeniesienia;
                                        „fałsz” dla wyzerowanego (0) znacznika przeniesienia.
    PE            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.
    P            Brak zera            „Prawda” dla wyzerowanego (0) znacznika zera; „fałsz”
                                        dla ustawionego (1) znacznika zera.
    Q             Przepełnienie        „Prawda” dla ustawionego (1) znacznika przepełnienia;
                                        „fałsz” dla wyzerowanego (0) znacznika przepełnienia.
    PQ            Brak przepełnienia   „Prawda” dla wyzerowanego (0) znacznika przepełnienia;
                                        „fałsz” dla ustawionego (1) znacznika przepełnienia.
    U             Znak                 „Prawda” dla ustawionego (1) znacznika znaku; „fałsz”
                                        dla wyzerowanego (0) znacznika znaku.
    PU            Brak znaku           „Prawda” dla wyzerowanego (0) znacznika znaku; „fałsz”
                                        dla ustawionego (1) znacznika znaku.


1
    Istnieje jeszcze kilka postaci dodatkowych; zostaną one omówione w dalszych podrozdziałach
    i rozdziałach.
32                                                             Asembler. Sztuka programowania


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

        W przypadku, kiedy wartość logiczna anga uje operand rejestrowy, operand ten mo e
        odnosić 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 innej
        wartoś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 y
        jednak pamiętać, e w wyra eniu logicznym powinna występować zmienna typu logicz-
        nego (DQQNGCP). Uwikłanie w wyra eniu logicznym zmiennej innego typu spowoduje
        błąd. W przypadku potrzeby określenia wartości logicznej zmiennej typu innego ni
        DQQNGCP 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ślenie
        operatora relacji wraz z dwoma operandami. Listę dozwolonych kombinacji rodzajów
        operandó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 docelowym
        i stosować kombinacje rodzajów operandów takie jak dla instrukcji CFF czy UWD.

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

        I jeszcze jedno: jeśli lewy operand jest rejestrem, a prawy to stała dodatnia albo inny
        rejestr, porównanie odbywa się bez uwzględnienia znaku operandów. Efekty takiego
        poró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.
Rozdział 1. ♦ Wstęp do języka asemblerowego                                               33


        Operatory KP i PQV KP pozwalają na wykonanie testu zawierania się wartości prze-
        chowywanej w rejestrze w określonym zbiorze liczb. Na przykład wyra enie GCZ KP
         daje wartość logiczną VTWG, jeśli wartość przechowywana w rejestrze
        EAX to wartość z zakresu od 2000 do 2099 (włącznie). Operator PQV KP ma działanie
        odwrotne — da wartość logiczną VTWG jedynie wtedy, kiedy zawartość rejestru to
        liczba spoza określonego zbioru. Na przykład wyra enie CN PQV KP C   będzie
        miało wartość VTWG tylko wtedy, kiedy znak przechowywany w rejestrze AL nie będzie
        małą literą alfabetu.

        Oto kilka przykładów wyra eń logicznych w języku HLA:
           E
           $QQNAXCT
           CN
           '5+
           '#:  '$:
           '$: 
           K  
           K 
           CN  K
           GCZ KP 
           EJ PQV KP C  



1.9.2. Instrukcje if..then..elseif..else..endif języka HLA
        Składnia instrukcji warunkowego wykonania kodu KH 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 KH 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ą VJGP. W przeciwnym
        przypadku sterowanie przekazane zostanie do kodu w ramach klauzuli GNUGKH bądź GNUG.
34                                                        Asembler. Sztuka programowania


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

              UVFQWVRWV
 DNCF YCTVQUE 07..  PN 

       GPFKH

     Jeśli w trakcie wykonania programu wyra enie logiczne w instrukcji KH będzie miało
     wartość VTWG, wykonany zostanie kod znajdujący się pomiędzy słowami VJGP i GPFKH.
     W przypadku kiedy wyra enie logiczne będzie miało wartość HCNUG, kod ów zostanie
     pominięty.

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

              UVFQWVRWV
 DNCF YCTVQEU 07.. YUMCPKMC  PN 

       GNUG

              UVFQWVRWV
 2QRTCYPC YCTVQUE YUMCPKMC  PN 

       GPFKH

     Jeśli wyra enie logiczne przyjmie wartość VTWG, wykonany zostanie kod zawarty między
     słowami VJGP a GNUG. W przypadku wartości HCNUG sterowanie przekazane zostanie do
     kodu ograniczonego klauzulą GNUG i słowem GPFKH.

     Przez osadzanie opcjonalnych klauzul GNUGKH w instrukcji KH 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ępnych
     opcji menu, konstruując następującą instrukcję KH:
       KH
 EJ  C  VJGP

              UVFQWVRWV
 9[DTCPQ RQ[ELG OGPW C  PN 

       GNUGKH
 EJ  D  VJGP

              UVFQWVRWV
 9[DTCPQ RQ[ELG OGPW D  PN 

       GNUGKH
 EJ  E 

              UVFQWVRWV
 9[DTCPQ RQ[ELG OGPW E  PN 

       GNUG

           UVFQWVRWV
 $NCF Y[DQT QRELK URQC OGPW  PN 
       GPFKH
Rozdział 1. ♦ Wstęp do języka asemblerowego                                              35


       Choć nie widać tego w powy szym przykładzie, język HLA nie wymaga umieszczania
       po sekwencji klauzul GNUGKH klauzuli GNUG. Jednak przy podejmowaniu decyzji o wyko-
       naniu jednej z wielu ście ek kodu warto uzupełnić instrukcję KH klauzulą GNUG rozpo-
       czynającą ście kę kodu obsługi ewentualnych błędów wyboru. Nawet jeśli pozornie
       wykonanie owego kodu wydaje się niemo liwe, to nale y wziąć pod uwagę ewentualny
       rozwó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ę sytuacji
       wyjątkowych i niespodziewanych.


1.9.3. Iloczyn, suma i negacja
w wyrażeniach logicznych
       Na liście operatorów zaprezentowanej w poprzednich punktach zabrakło bardzo wa nych
       operatorów logicznych: operatora iloczynu logicznego (AND), logicznej sumy (OR)
       oraz logicznej negacji (NOT). Ich zastosowanie w wyra eniach logicznych omówione
       zostanie tutaj — wcześniej omówienie takie nie miałoby racji bytu z uwagi na koniecz-
       ność poprzedzenia go omówieniem instrukcji warunkowego wykonania kodu KH. Bez
       mo 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 operator
       binarny 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:
          KH
 GCZ      EJ C  VJGP

              OQX
 GCZ GDZ 
              OQX
     EJ 

          GPFKH

       Obie instrukcje OQX zostaną wykonane jedynie wtedy, kiedy równocześnie rejestr EAX
       bę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 OQX 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. Oto
       kilka poprawnych logicznych z operatorem iloczynu logicznego:
              CN KP 
          CN KP C        GDZ
          DQQN8CT     GCZ

       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”, to
       operand prawy nie jest ju sprawdzany — wyra enie logiczne natychmiast otrzymuje
       wartość „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 zaprezentowanych
       wy ej wyra eń najpierw sprawdzona zostanie wartość logiczna zmiennej DQQN8CT; jeśli
       będzie to wartość HCNUG, test wartości logicznej rejestru EAX zostanie pominięty.
36                                                        Asembler. Sztuka programowania


     Nie sposób nie zauwa yć, e wyra enie z operatorem , np. GCZ      GDZ  GCZ,
     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 enia
     z operatorem logicznym. Stąd poprawność następującego wyra enia:
       GCZ      GDZ  GCZ            GEZ

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

     Operator sumy logicznej w języku HLA reprezentowany jest znakami ^^. Podobnie, jak
     operator 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”, operator
     natychmiast zwraca wartość „prawda”, niezale nie od wartości drugiego operandu. Oto
     przykłady zastosowania operatora sumy logicznej:
        ^^ CN  
       CN KP C   ^^ GDZ
        DQQN8CT ^^ GCZ

     Podobnie jak operator operator logicznej sumy cechuje się łącznością lewostronną,
     więc w wyra eniach z wieloma operatorami ^^ ewaluacja przebiega od strony lewej do
     prawej. Na przykład:
       GCZ   ^^ GDZ  GCZ      ^^      GEZ

     Jeśli powy sze wyra enie zostałoby osadzone w wyra eniu logicznym instrukcji KH, to
     kod po klauzuli VJGP 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ść VTWG, pozostałe warunki nie byłyby spraw-
     dzane. Gdyby pierwszy warunek miał wartość HCNUG, ale drugi VTWG, to etap szacowania
     wartości logicznej wyra enia GEZ zostałby pominięty. Test zawartości rejestru ECX
     nastą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:
       GCZ   ^^ GDZ  GCZ      GEZ

     W interpretacji HLA powy szemu wyra eniu równowa ne jest wyra enie następujące:
       GCZ   ^^ 
GDZ  GCZ      GEZ

     Jeśli rejestr EAX będzie miał wartość mniejszą od zera, reszta wyra enia nie będzie
     sprawdzana — całość wyra enia otrzyma wartość VTWG. Jeśli jednak rejestr EAX będzie
Rozdział 1. ♦ Wstęp do języka asemblerowego                                                 37


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

         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:
            
GCZ   ^^ GDZ  GCZ      GEZ

         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 musi
         zawierać 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.
          GCZ  ) — operator negacji nie mo e być bezpośrednio aplikowany do zło onych
         wyra eń logicznych. Aby wykonać logiczną negację wyra enia logicznego, nale y to
         wyra enie ująć w nawiasy i cały nawias opatrzyć przedrostkiem w postaci wykrzyknika:
             
 GCZ  

         Powy sze wyra enie otrzymuje wartość VTWG 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ów
         bitowych i innych operatorów języka HLA znajduje się w dokumentacji kompilatora.


1.9.4. Instrukcja while
         Składnię instrukcji YJKNG ilustruje rysunek 1.11.

Rysunek 1.11.
Składnia instrukcji
while języka HLA




         Wykonanie instrukcji YJKNG 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ą GPFYJKNG. W przypadku
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ści
     logicznej 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 YJKNG sprawdzenie wartości warunku wykonania pętli następuje podobnie jak
     w językach wysokiego poziomu, czyli przed wykonaniem pierwszej instrukcji ciała
     pę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ść HCNUG.
     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 mieli
     do czynienia z pętlą nieskończoną.

     Oto przykład pętli YJKNG w języku HLA:
       OQX
  K 
       YJKNG
 K    FQ

            UVFQWVRWV
 K  K PN 
            CFF
  K 

       GPFYJKNG



1.9.5. Instrukcja for
     W języku HLA pętla for przybiera następującą postać:
       HQT
 Y[TC GPKGKPKELCNKWLæEG YCTWPGM KPUVTWMELCNKEPKMC FQ

            EKC QRúVNK

       GPFHQT

     Instrukcja pętli HQT odpowiada następującej pętli YJKNG:
       Y[TC GPKGKPKELCNKWLæEG
       YJKNG
 YCTWPGM  FQ

            EKC QRúVNK
            KPUVTWMELCNKEPKMC

       GPFYJKNG

     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 logiczne
     języka HLA w formacie takim jak dla instrukcji YJKNG. 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 instrukcji
     jest instrukcja w pętli YJKNG, której wykonanie zmienia wartość logiczną warunku
     wykonania pętli. Zwykle instrukcją tą jest instrukcja języka HLA, na przykład CFF.
Rozdział 1. ♦ Wstęp do języka asemblerowego                                                    39


          Oto przykład ilustrujący równowa ność pętli HQT i YJKNG:
             HQT
 OQX
  K  K   CFF
  K   FQ

                UVFQWVRWV
 K  K PN 

             GPFHQT

             // Analogiczna pętla for:
             OQX
  K 
             YJKNG
 K    FQ

                 UVFQWVRWV
 K  K PN 
                 CFF
  K 

             GPFYJKNG



1.9.6. Instrukcja repeat
          Instrukcja TGRGCVWPVKN języka HLA wykorzystuje składnię prezentowaną na rysunku
          1.12. Programiści języków C, C++ oraz Java powinni od razu zauwa yć, e instrukcja
          ta jest odpowiednikiem dostępnej w tych językach instrukcji FQYJKNG.

Rysunek 1.12.
Składnia instrukcji
repeat..until
języka HLA




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

          Oto prosty przykład wykorzystania pętli TGRGCVWPVKN:
             OQX
  GEZ 
             TGRGCV

                 UVFQWVRWV
 GEZ   GEZ PN 
                 UWD
  GEZ 

             WPVKN
 GEZ   


2
    Znaczenie warunku jest więc w języku HLA odwrotne ni warunku w analogicznej konstrukcji
    FQYJKNG znanej z języków C, C++ i Java.
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 YJKNG lepiej zastosować pętlę TG
         RGCVWPVKN — konstrukcja taka będzie efektywniejsza.


1.9.7. Instrukcje break oraz breakif
         Instrukcje DTGCM i DTGCMKH słu ą do przedwczesnego przekazywania sterowania poza
         pętlę. Składnię obu instrukcji ilustruje rysunek 1.13.

Rysunek 1.13.
Składnia instrukcji
break oraz breakif
języka HLA



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

         Zastosowanie instrukcji DTGCM oraz DTGCMKH nie pozwala na przekazanie sterowania poza
         pętle zagnie d one. W języku HLA słu ą do tego osobne instrukcje, jak DGIKPGPF
         oraz GZKV i GZKVKH. Szczegółowe informacje na ten temat zawiera dokumentacja języka
         HLA. Dalej, język HLA udostępnia instrukcje EQPVKPWG oraz EQPVKPWGKH pozwalające
         na przystąpienie do kolejnej iteracji pętli z pominięciem niewykonanych jeszcze instrukcji
         ciała pętli. Po szczegóły odsyłam Czytelników do stosownej dokumentacji.


1.9.8. Instrukcja forever
         Składnię konstrukcji HQTGXGTGPFHQT prezentuje rysunek 1.14.

Rysunek 1.14.
Składnia instrukcji
forever języka HLA




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

                 UVFQWVRWV
    9RTQYCF NKEDG ECNMQYKVC 
OPKGLUC QF  
                 UVFKPIGV
 K   
                 DTGCMKH
 K     
                 UVFQWVRWV
    9RTQYCFQPC YCTVQUE OKCNC D[E OPKGLUC QF   PN 

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


1.9.9. Instrukcje try, exception oraz endtry
         Instrukcje VT[GZEGRVKQPGPFVT[ słu ą do implementacji bardzo przydatnych bloków
         obsł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 VT[GPFVT[ słu y do ochrony wykonania instrukcji. Jeśli wykonanie
         instrukcji znajdujących się pomiędzy klauzulą VT[ a pierwszą klauzulą GZEGRVKQP,
         tworzących tzw. blok chroniony, przebiegnie bez niespodzianek, sterowanie przeka-
         zywane jest do pierwszej instrukcji za klauzulą GPFVT[. Jeśli jednak w czasie wyko-
         nywania instrukcji bloku chronionego zdarzy się błąd (wyjątek), wykonanie programu
         jest przerywane, a sterowanie przekazywane jest do odpowiedniej klauzuli GZEGRVKQP
         (skojarzonej z wyjątkiem, który wystąpił). Ka dy wyjątek ma unikalny identyfikator
         w postaci liczby całkowitej. Niektóre z identyfikatorów wyjątków zadeklarowane zostały
         w 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 system
         porównuje identyfikator zgłoszonego wyjątku z identyfikatorami określonymi w klauzu-
         lach GZEGRVKQP. Jeśli wśród nich znajduje odpowiedni identyfikator, następuje wykonanie
         instrukcji określonych w ramach klauzuli GZEGRVKQP. Po zakończeniu wykonywania tych
         instrukcji sterowanie przekazywane jest do pierwszej instrukcji za klauzulą GPFVT[.

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

         Instrukcje VT[GPFVT[ mo na na przykład wykorzystać do ochrony programu przed
         skutkami wprowadzenia niepoprawnych danych:
42                                                               Asembler. Sztuka programowania


             TGRGCV

                  OQX
 HCNUG )QQF+PVGIGT    // Uwaga: zmienna GoodInteger musi być typu boolean
                  VT[

                      UVFQWVRWV
 9RTQYCF NKEDG ECNMQYKVC     
                      UVFKPIGV
 K 
                      OQX
 VTWG )QQF+PVGIGT 

                  GZEGRVKQP
 GZ%QPXGTUKQP'TTQT 

                      UVFQWVRWV
 0KGRQRTCYPC YCTVQUE YRTQYCF LGUEG TC  PN 

                  GZEGRVKQP
 GZ8CNWG1WV1H4CPIG 

                      UVFQWVRWV
 9CTVQUE URQC CMTGUW YRTQYCF LGUEG TC  PN 

                  GPFVT[

             WPVKN
 )QQF+PVGIGT 

          Zastosowana w powy szym programie pętla TGRGCV pozwala na wykonywanie kodu
          ciała pętli tak długo, jak długo u ytkownik podaje niepoprawne wartości. Jeśli z powodu
          wprowadzenia niepoprawnej wartości jej przypisanie do zmiennej )QQF+PVGIGT będzie
          niemo liwe (na przykład z racji umieszczenia we wprowadzonym niedozwolonych dla
          liczb 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 tu
          na wyświetleniu stosownego komunikatu i przekazaniu sterowania poza instrukcję
          VT[GPFVT[. Poniewa z racji wyjątku nie udało się w ramach bloku chronionego ustawić
          wartości VTWG dla zmiennej )QQF+PVGIGT, pętla jest wykonywana ponownie. Jeśliby jedna
          w 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 bibliotece
          standardowej wyjątków mo na znaleźć, zaglądając do pliku excepts.hhf dołączonego
          do posiadanej wersji oprogramowania kompilatora HLA.

          Większość z tych wyjątków dotyczy sytuacji, których omawianie w niniejszym rozdziale
          byłoby przedwczesne. Zostały umieszczone w tabeli wyłącznie gwoli kompletności
          omówienia. Ka dy z wyjątków opisany jest szczegółowo w dokumentacji HLA Reference
          Manual dokumentacji biblioteki standardowej języka HLA; ostateczną wykładnią jest
          zaś sam kod źródłowy biblioteki standardowej HLA. Najczęściej wykorzystywanymi
          wyjątkami są GZ%QPXGTUKQP'TTQT, GZ8CNWG1WV1H4CPIG oraz GZ5VTKPI1XGTHNQY.

          Omówienie bloku kodu chronionego podjęte zostanie ponownie nieco później. Czytelnik
          powinien kontynuować edukację w zakresie podstawowym, zadowalając się na razie
          szczątkowymi informacjami o obsłudze wyjątków.
3
    Doświadczony programista zastanawia się zapewne, dlaczego w powy szym kodzie do sterowania
    przebiegiem pętli wykorzystywana jest zmienna typu DQQNGCP — przecie mo na by identyczny efekt
    osiągnąć, stosując instrukcję DTGCMKH. Zastosowana konstrukcja ma uzasadnienie, które zostanie
    przedstawione w dalszej części ksią ki.
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
 GZ5VTKPI1XGTHNQY            Próba zapisania zbyt długiego łańcucha
 GZ5VTKPI+PFGZ'TTQT          Próba odwołania do znaku o indeksie spoza łańcucha
 GZ8CNWG1WV1H4CPIG           Zbyt wielka wartość dla danego typu zmiennej
 GZ+NNGICN%JCT               Zaanga owanie w operacji znaku spoza dopuszczalnego zakresu
                              (kod ASCI spoza zakresu od 0 do 127)
 GZ%QPXGTUKQP'TTQT           Niedozwolone znaki w konwersji łańcucha do wartości numerycznej
 GZ$CF(KNG*CPFNGT            Odwołanie do pliku za pośrednictwem niepoprawnego uchwytu pliku
 GZ(KNG1RGP(CKNWTG           Niemo ność otwarcia pliku (np. brak pliku o zadanej nazwie)
 GZ(KNG%NQUG'TTQT            Niemo ność zamknięcia pliku
 GZ(KNG9TKVG'TTQT            Błąd przy zapisie danych do pliku
 GZ(KNG4GCF'TTQT             Błąd przy odczycie danych z pliku
 GZKUM(WNN'TTQT             Próba zapisania danych do pliku przy pełnym dysku
 GZ'PF1H(KNG                 Próba odczytania bajta spoza końca pliku
 GZ/GOQT[#NNQECVKQP(CKNWTG   Brak pamięci do zaspokojenia ądania alokacji
 GZ#VVGORV6QGTGH07..        Próba odwołania do pamięci za pośrednictwem wskaźnika o wartości NULL
 GZ%CPPQV(TGG/GOQT[          Błąd przy operacji zwalniania pamięci
 GZ9KFVJ6QQ$KI               Zły format konwersji wartości liczbowej do łańcucha
 GZ6QQ/CP[%OF.P2CTCOU        Wiersz wywołania programu zawiera zbyt du ą liczbę argumentów
 GZ#TTC[5JCRG8KQNCVKQP       Próba operowania na dwóch tablicach o ró nych rozmiarach
 GZ#TTC[$QWPFU               Próba odwołania do elementu spoza tablicy
 GZ+PXCNKFCVG               Operacja na niepoprawnej wartości daty
 GZ+PXCNKFCVG(QTOCV         Błąd konwersji łańcucha do daty (łańcuch zawiera niedozwolone znaki)
 GZ6KOG1XGTHNQY              Przepełnienie w operacji manipulowania informacją o czasie
 GZ#UUGTVKQP(CKNGF           Błąd instrukcji asercji
 GZ'ZGEWVGF#DUVTCEV          Próba wywołania metody klasy abstrakcyjnej
 GZ#EEGUU8KQNCVKQP           Próba odwołania się do niedozwolonego adresu pamięci
 GZ$TGCM2QKPV                Program dotarł do punktu zatrzymania (przerwanie 3.)
 GZ5KPING5VGR                Program wykonywany jest z ustawionym znacznikiem wykonania
                              krokowego
 GZ2TKX+PUVT                 Próba wykonania instrukcji charakterystycznej dla trybu nadzoru
 GZ+NNGICN+PUVT              Próba wykonania niedozwolonej instrukcji maszynowe
 GZ$QWPF+PUVT                Rejestr zawiera wartość spoza zakresu określonego w instrukcji DQWPF
 GZ+PVQ+PUVT                 Wykonanie instrukcji KPVQ przy ustawionym znaczniku przepełnienia
 GZKXKFG'TTQT               Próba dzielenia przez zero bądź inny błąd dzielenia
 GZHGPQTOCN                 Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony
                              obliczeniom arytmetycznym)
 GZHKX$[GTQ                Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony
                              obliczeniom arytmetycznym)
44                                                                Asembler. Sztuka programowania


Tabela 1.4. Wyjątki zdefiniowane w pliku nagłówkowym excepts.hhf — ciąg dalszy
    Identyfikator wyjątku       Znaczenie
    GZH+PGZCEV4GUWNV           Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony
                                obliczeniom arytmetycznym)
    GZH+PXCNKF1RGTCVKQP        Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony
                                obliczeniom arytmetycznym)
    GZH1XGTHNQY                Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony
                                obliczeniom arytmetycznym)
    GZH5VCEM%JGEM              Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony
                                obliczeniom arytmetycznym)
    GZH7PFGTHNQY               Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony
                                obliczeniom arytmetycznym)
    GZ+PXCNKF*CPFNG            System operacyjny odrzucił niepoprawny uchwyt
    GZ5VCEM1XGTHNQY            System operacyjny zgłosił przepełnienie stosu




1.10. Biblioteka standardowa
języka HLA — wprowadzenie
           Język HLA prezentuje się na tle tradycyjnego języka asemblerowego korzystnie dzięki
           dwóm aspektom. Pierwszym jest składnia języka HLA zapo yczająca elementy znane
           z 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 tym
           samym znacznie szybszą naukę języka asemblerowego. Drugim elementem stanowią-
           cym o przewadze HLA nad tradycyjnym asemblerem jest dostępność biblioteki HLA
           Standard Library. Biblioteka standardowa języka HLA zawiera wiele prostych w u yciu
           procedur języka asemblerowego realizujących typowe zadania programistyczne — ich
           dostę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ęzyk
           asemblerowy — trudność tkwiącą w konieczności samodzielnej implementacji zło onych
           niekiedy 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 operacji
           jak wyprowadzanie napisów. Jej dostępność oznacza więc, e adept języka asemblerowego
           mo e skoncentrować się na nauce samego języka, nie poświęcając się od samego początku
           zgłę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ńcu
           biblioteki kodu asemblerowego to nie nowy wynalazek4. Jednak biblioteka standardowa

4
    Popularna jest na przykład biblioteka UCR Standard Library dla programistów języka asemblerowego
    procesorów 80x86.
Rozdział 1. ♦ Wstęp do języka asemblerowego                                                              45


              języka HLA to nie tylko sam kod procedur, ale równie wysokopoziomowy interfejs
              wywoł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ępne
              mo liwości.

              Biblioteka standardowa języka HLA składa się z szeregu modułów przypisanych do
              kilku 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




5
    Z 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.
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 UVFKQ.


1.10.1. Stałe predefiniowane w module stdio
            Omówienie modułu UVFKQ biblioteki standardowej nale ałoby rozpocząć od przedsta-
            wienia najczęściej wykorzystywanych stałych. Na przykład w następującym kodzie:
                 UVFQWVRWV
 #JQL RT[IQFQ  PN 

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

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

Tabela 1.6. Stałe znakowe definiowane w module wejścia-wyjścia biblioteki standardowej HLA
    Stała           Definicja
    UVFKQDGNN      Znak dzwonka; wydrukowanie tego znaku objawia się wygenerowaniem dźwięku przez
                    głośniczek systemowy
    UVFKQDU        Znak cofania kursora
    UVFKQVCD       Znak tabulacji
    UVFKQNH        Znak wysuwu wiersza
    UVFKQET        Znak powrotu karetki

            Z wyjątkiem PN wszystkie zaprezentowane tu stałe zdefiniowane są w przestrzeni
            nazw UVFKQ (stąd wymóg poprzedzania nazw stałych przedrostkiem UVFKQ)6. Osadzenie
            owych stałych w przestrzeni nazw UVFKQ pozwoliło na uniknięcie ewentualnych kon-
            fliktów z nazwami zmiennych definiowanymi przez programistę. Nazwa PN, jako
            u ywana wyjątkowo często, nie została osadzona w przestrzeni nazw UVFKQ — koniecz-
            ność ka dorazowego wpisywania UVFKQPN 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ą UVFKP bądź
            UVFQWV. Technicznie oznacza to, e nazwy tych procedur zdefiniowane zostały wewnątrz
            odpowiedniej przestrzeni nazw. W praktyce zastosowanie przedrostka stanowi rów-
            nie sugestię co do pochodzenia danych (dane pobierane ze standardowego urządzenia

6
    Przestrzenie nazw omówione zostaną w jednym z kolejnych rozdziałów.
Rozdział 1. ♦ Wstęp do języka asemblerowego                                                      47


          wejściowego, UVFKP) bądź ich przeznaczenia (dane kierowane do standardowego urzą-
          dzenia wyjściowego, UVFQWV). 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 UVFKP bądź UVFQWV realizują odczyty i zapisy danych z i do
          urzą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 do
          urządzeń innych ni domyślne. Na przykład argument wywołania programu w postaci
           RNKMAY[ powoduje przekierowanie standardowego wyjścia programu do pliku plik_wy.
          Z kolei argument wywołania postaci RNKMAYG powoduje przypisanie standardowego
          wejścia programu do pliku źródłowego plik_we. Oto przykłady przekierowania stan-
          dardowego wejścia i wyjścia programu VGUVRIO uruchamianego z poziomu wiersza
          poleceń7:
             VGUVRIO KPRWVFCVC
             VGUVRIO QWVRWVVZV
             VGUVRIO KPVZV QWVVZV



1.10.3. Procedura stdout.newln
          Procedura UVFQWVPGYNP powoduje zapisanie do urządzenia standardowego wyjścia se-
          kwencji nowego wiersza. Wywołanie owej procedury jest równowa ne wywołaniu
          UVFQWVRWV
 PN . Wywołanie UVFQWVPGYNP jest jednak niekiedy nieco wygodniejsze
          bądź bardziej czytelne. Oto przykład wywołania procedury:
             UVFQWVPGYNP




1.10.4. Procedury stdout.putiN
          Procedury biblioteczne UVFQWVRWVK, UVFQWVRWVK oraz UVFQWVRWVK wyprowa-
          dzają na standardowe wyjście programu pojedynczą wartość, odpowiednio: 8-bitową,
          16-bitową i 32-bitową, interpretowaną jako liczba całkowita ze znakiem. Argumentem
          wywołania mo e być stała, rejestr bądź zmienna w pamięci — rozmiar argumentu musi
          jednak 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 poprzedzony
          znakiem minusa. Oto kilka przykładów wywołań procedur UVFQWVRWVK0:
             UVFQWVRWVK
  
             UVFQWVRWVK
 FZ 
             UVFQWVRWVK
 K8CT 



7
    Uwaga dla u ytkowników systemu Linux: w zale ności od wartości zmiennej środowiskowej 2#6*
    nazwę programu trzeba być mo e poprzedzić znakami „./”, np. VGUVRIO KPRWVFCVC.
48                                                      Asembler. Sztuka programowania


1.10.5. Procedury stdout.putiNSize
     Procedury UVFQWVRWVK5KG, UVFQWVRWVK5KG oraz UVFQWVRWVK5KG zapisują do
     standardowego urządzenia wyjściowego wartości interpretowane jako liczby całkowite
     ze znakiem — a więc działają podobnie jak procedury UVFQWVRWVK0. Tyle e procedury
     UVFQWVRWVK05KG 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ę argumentem
     wywołania procedury. Składnia wywołania procedur UVFQWVRWVK05KG jest następująca:
       UVFQWVRWVK5KG
 YCTVQ èDKV UGTQMQ è Y[RG PKGPKG 
       UVFQWVRWVK5KG
 YCTVQ èDKV UGTQMQ è Y[RG PKGPKG 
       UVFQWVRWVK5KG
 YCTVQ èDKV UGTQMQ è Y[RG PKGPKG 

     W miejsce parametru YCTVQ è0DKV mo na przekazać stałą, nazwę rejestru albo nazwę
     zmiennej w pamięci — podany argument musi jednak odpowiadać rozmiarem rozmia-
     rowi parametru. Parametr UGTQMQ è mo e przyjmować argumenty w postaci stałych
     cał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. Argumentem
     parametru Y[RG PKGPKG powinien być pojedynczy znak.

     Podobnie jak procedury UVFQWVRWVK0 procedury UVFQWVRWVK05KG wyprowadzają na
     urządzenie standardowego wyjścia napis reprezentujący przekazaną w wywołaniu wartość
     liczbową interpretowaną jako liczbę całkowitą ze znakiem. W tej wersji programista
     mo 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ów
     w napisie jest mniejsza od zało onego minimum, brakujące znaki są w kodzie procedury
     uzupełniane znakami wypełnienia. Kiedy wyprowadzana wartość argumentu UGTQ
     MQ è jest liczbą ujemną, napis zostanie wyrównany w ramach wyprowadzanego pola do
     lewej; wartość dodatnia powoduje wyrównywanie napisów do prawej.

     Jeśli wartość bezwzględna parametru UGTQMQ è jest większa ni minimalna liczba
     znaków potrzebnych do dokładnego reprezentowania wyprowadzanej wartości, pro-
     cedury UVFQWVRWVK05KG uzupełniają napis znakami wypełnienia, dodając je z lewej
     bądź z prawej strony napisu (przed bądź za znakową reprezentacją liczby). Znak wy-
     pełnienia definiowany jest argumentem Y[RG PKGPKG. W zdecydowanej większości
     przypadków znakiem tym jest znak spacji, jednak w sytuacjach szczególnych konieczne
     mo e okazać się wypełnienie innym znakiem. Nale y tylko pamiętać, e argument pa-
     rametru Y[RG PKGPKG powinien być wartością znakową. W języku HLA stałe znakowe
     ujmowane 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 UVFQWVRWVK5KG w celu wyświetlenia listy wartości w formie
     tabelarycznej.
Rozdział 1. ♦ Wstęp do języka asemblerowego                                                         49


Listing 1.4. Wyprowadzenie kolumn wartości liczbowych — zastosowanie procedury stdout.puti32Size
              RTQITCO 0WOU+P%QNWOPU

               KPENWFG
 UVFNKDJJH 

              XCT
                    K    KPV
                    %QN%PV KPV

              DGIKP 0WOU+P%QNWOPU

                    OQX
  K 
                    OQX
  %QN%PV 
                    YJKNG
 K   FQ

                       KH
 %QN%PV    VJGP

                           UVFQWVPGYNP

                           OQX
  %QN%PV 

                       GPFKH
                       UVFQWVRWVK5KG
 K       
                       UWD
  K 
                       CFF
  %QN%PV 

                    GPFYJKNG
                    UVFQWVPGYNP


              GPF 0WOUKP%QNWOPU




1.10.6. Procedura stdout.put
           Procedura UVFQWVRWV8 to najelastyczniejsza procedura wyprowadzania napisów na
           wyjście programu dostępna w module obsługi standardowego wyjścia. Procedura ta łączy
           funkcje 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 UVFQWVRWV prezentuje
           się następująco:
              UVFQWVRWV
 NKUVCY[RTQYCFCP[EJYCTVQ EK 

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

8
    Tak naprawdę UVFQWVRWV 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.
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 dy
           z argumentów wywołania mo e być zadany w jednej z dwóch postaci:
              YCTVQ è
              YCTVQ èUGTQMQ è

           9CTVQ è mo e być dowolną stałą, rejestrem albo zmienną przechowywaną w pamięci.
           W niniejszym rozdziale prezentowane były argumenty w postaci literałów łańcuchowych
           i zmiennych w pamięci. Jak dotychczas wszystkie argumenty wywołania odpowiadały
           pierwszej 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 UVFQWVRWVK05KG9. Przykładowy
           program z listingu 1.5 generuje wydruk podobny do tego tworzonego programem
           0WOU+P%QNWOPU, tyle e tutaj miejsce procedury UVFQWVRWVK05KG zajęła procedura
           UVFQWVRWV.

Listing 1.5. Demonstracja sposobu określenia szerokości napisów w argumentach procedury stdout.put
              RTQITCO 0WOU+P%QNWOPU

               KPENWFG
 UVFNKDJJH 

              XCT
                    K    KPV
                    %QN%PV KPV

              DGIKP 0WOU+P%QNWOPU

                    OQX
  K 
                    OQX
  %QN%PV 
                    YJKNG
 K   FQ

                       KH
 %QN%PV    VJGP

                           UVFQWVPGYNP

                           OQX
  %QN%PV 

                       GPFKH
                       UVFQWVRWV
 K 
                       UWD
  K 
                       CFF
  %QN%PV 

                    GPFYJKNG
                    UVFQWVPGYNP


              GPF 0WOUKP%QNWOPU


           Procedura UVFQWVRWV 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.

9
    W 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.
Rozdział 1. ♦ Wstęp do języka asemblerowego                                                    51


1.10.7. Procedura stdin.getc
            Procedura UVFKPIGVE wczytuje pojedynczy znak z bufora urządzenia standardowego
            wejś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
               RTQITCO EJCT+PRWV

                KPENWFG
 UVFNKDJHH 

               XCT
                     EQWPVGT KPV

               DGIKP EJCT+PRWV

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

                     TGRGCV

                         // Wyprowadź 14 wartości.

                         OQX
  EQWPVGT 
                         YJKNG
 EQWPVGT   FQ

                              UVFQWVRWV
 EQWPVGT 
                              UWD
  EQWPVGT 

                         GPFYJKNG

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

                         UVFQWVRWV
 PN PN 9[MQPCE MQNGLPC KVGTCELG 
VP! 
                         HQTGXGT

                              UVFKPTGCF.P

                              UVFKPIGVE

                              DTGCMKH
 CN  P 
                              DTGCMKH
 CN  V 
                              UVFQWVRWV
 2TQUG YRTQYCFKE CNDQ V  CNDQ P  

                         GPFHQT
                         UVFQWVPGYNP


                     WPVKN
 CN  P 

               GPF EJCT+PRWV




10
     Bufor to w tym kontekście ładniejsza nazwa tablicy.
52                                                                   Asembler. Sztuka programowania


        W powy szym programie wywołanie procedury UVFKPTGCF.P słu y do wymuszenia
        przejścia do nowego wiersza strumienia wejściowego. Szczegółowy opis tej procedury
        znajduje się nieco dalej, w niniejszym rozdziale.


1.10.8. Procedury stdin.getiN
        Procedury UVFKPIGVK, UVFKPIGVK oraz UVFKPIGVK realizują operację wczytania
        z urządzenia standardowego wejścia odpowiednio: 8-bitowej, 16-bitowej i 32-bitowej
        wartości liczbowej całkowitej. Wczytane wartości umieszczane są w rejestrach AL, AX
        bądź EAX. Niniejsze procedury stanowią standardowy sposób wczytywania liczb całko-
        witych ze znakiem do programów języka HLA.

        Podobnie jak procedura UVFKPIGVE, procedury UVFKPIGVK0 wczytują ze standardowego
        wejścia sekwencję znaków. W sekwencji tej ignorowane są wszelkie początkowe znaki
        odstę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 wprowadzona
        wartość nie mieści się w zakresie liczbowym charakterystycznym dla danej procedury,
        zgłaszany jest wyjątek (mo na go przechwycić, umieszczając wywołanie procedury
        w bloku kodu chronionego w instrukcji VT[GPFVT[). Procedura UVFKPIGVK obsługuje
        liczby z zakresu od –128 do 127, UVFKPIGVK — od –32 768 do 32 767; wartości
        wczytywane do programu za pośrednictwem procedury UVFKPIGVK 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
           RTQITCO KPV+PRWV

            KPENWFG
 UVFNKDJJH 

           XCT
                  K     KPV
                  K    KPV
                  K    KPV

           DGIKP KPV+PRWV

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

                  UVFQWVRWV
 9RTQYCF PKGYKGNMC NKEDG ECNMQYKVC 
 CMTGUW      
                  UVFKPIGVK

                  OQX
 CN K 

                  UVFQWVRWV
 9RTQYCF YKGMUC NKEDG ECNMQYKVC 
 CMTGUW   
           
                  UVFKPIGVK

                  OQX
 CZ K 

                  UVFQWVRWV
 9RTQYCF NKEDG ECNMQYKVC 
 CMTGUW QF  FQ  OKNKCTFQY     
                  UVFKPIGVK
Rozdział 1. ♦ Wstęp do języka asemblerowego                                                  53


              OQX
 GCZ K 

              // Wyświetl podane wartości.

              UVFQWVRWV
              

                  PN
                   1VQ YRTQYCFQPG NKED[  PN PN
                   DKVQYC NKEDC ECNMQYKVC   PN
                   DKVQYC NKEDC ECNMQYKVC   PN
                   DKVQYC NKEDC ECNMQYKVC   PN
              

          GPF KPV+PRWV


       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 UVFKPIGVE czy UVFKPIGVK nie musi być to samy z mo-
       mentem wprowadzenia danych przez u ytkownika. Biblioteka standardowa języka HLA
       buforuje dane wprowadzane na standardowe wejście, wczytując jednorazowo cały
       wiersz tekstu wprowadzanego na to wejście. Wywołanie procedury wejścia mo e więc
       zostać 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 szym
       przykładzie:
          UVFQWVRWV
 9RTQYCF PKGYKGNMC NKEDG ECNMQYKVC 
 CMTGUW    
          UVFKPIGVK

          OQX
 CN K 

          UVFQWVRWV
 9RTQYCF YKGMUC NKEDG ECNMQYKVC 
 CMTGUW       
          UVFKPIGVK

          OQX
 CZ K 

       Programista ma nadzieję, e wykonanie powy szego kodu da następujący efekt: program
       wyś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ści
       program 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 wprowadzenie
       danych — procedura UVFKPIGVK odczyta po prostu drugi z wprowadzonych do bufora
       wejś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, procedury
       wejścia ograniczają się do odczytu z bufora. Mo na to wykorzystać, konstruując na-
       stępujący kod:
54                                                          Asembler. Sztuka programowania


       UVFQWVRWV
 9RTQYCF FYKG NKED[ ECNMQYKVG     
       UVFKPIGVK

       OQX
 GCZ KPVXCN 
       UVFKPIGVK

       OQX
 GCZ #PQVJGT+PV8CN 

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

     Na szczęście biblioteka standardowa języka HLA przewiduje dwie procedury sterujące
     działaniem bufora standardowego urządzenia wejściowego. Są to procedury UVFKPTGCF.P
     oraz UVFKPHNWUJ+PRWV. Pierwsza z nich usuwa z bufora wszystko, co zostało w nim
     umieszczone wcześniej, i tym samym wymusza wprowadzenie przez u ytkownika nowe-
     go wiersza danych. Druga po prostu usuwa całą zawartość bufora. Oczyszczenie bufora
     powoduje, e przy następnym wywołaniu dowolnej z procedur wejścia, u ytkownik
     będzie musiał wprowadzić nowy wiersz danych. Procedurę UVFKPTGCF.P wywołuje
     się zwykle bezpośrednio przez wywołaniem dowolnej procedury wejścia. Procedura
     UVFKPHNWUJ+PRWV wywoływana jest zaś typowo bezpośrednio po odczytaniu danych
     z bufora wybraną procedurą wejścia.

       Jeśli w programie wykorzystywana jest procedura UVFKPTGCF.P i okaże się, że
       konieczne jest dwukrotne wprowadzenie danych, to należy zastanowić się nad wyko-
       rzystaniem w jej miejsce procedury UVFKPHNWUJ+PRWV. 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 UVFKPTGCF.P
       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 UVFKPIGV łączy w sobie przedstawione wcześniej procedury wejścia, udo-
     stępniając ich funkcje za pośrednictwem pojedynczego wywołania. Procedura UVFKPIGV
     jest przy tym nieco prostsza w u yciu ni UVFQWVRWV, 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:
       UVFQWVRWV
 9RTQYCF FYKG NKED[ ECNMQYKVG     
       UVFKPIGVK

       OQX
 GCZ KPVXCN 
       UVFKPIGVK

       OQX
 GCZ #PQVJGT+PV8CN 

     Równowa ny przykład wykorzystujący procedurę UVFKPIGV wyglądałby następująco:
       UVFQWVRWV
 9RTQYCF FYKG NKED[ ECNMQYKVG     
       UVFKPIGV
 KPVXCN #PQVJGT+PV8CN
Rozdział 1. ♦ Wstęp do języka asemblerowego                                                55


         Jak widać, zastosowanie procedury UVFKPIGV mo e program skrócić i zwiększyć jego
         czytelność.

         Nale y przy tym pamiętać, e wywołanie procedury UVFKPIGV powoduje umieszczenie
         wczytanych wartości od razu we wskazanych zmiennych; adne z wczytanych wartości
         nie 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 nazwy
         rejestrów.



1.11. Jeszcze o ochronie wykonania
kodu w bloku try..endtry
         Jak zapewne Czytelnik pamięta, instrukcje VT[GPFVT[ otaczają blok kodu, którego
         wykonanie 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 wykonaniem
         odpowiedniej instrukcji języka HLA. Programista mo e przechwytywać i obsługiwać
         sytuacje wyjątkowe, umieszczając stosowny kod w ramach klauzuli GZEGRVKQP. Typowy
         przykład zastosowania instrukcji VT[GPFVT[ ilustruje listing 1.8.

Listing 1.8. Przykład zastosowania bloku try..endtry
            RTQITCO VGUV$CF+PRWV
             KPENWFG
 UVFNKDJJH 

            UVCVKE
                W        WPU

            DGIKP VGUV$CF+PRWV

                VT[

                      UVFQWVRWV
 9RTQYCF NKEDG ECNMQYKVC DG PCMW   
                      UVFKPIGV
 W 
                      UVFQWVRWV
 9RTQYCFQPQ  W PN 

                GZEGRVKQP
 GZ%QPXGTUKQP'TTQT 

                      UVFQWVRWV
 9RTQYCFQP[ EKCI CYKGTC PKGFQYQNQPG PCMK PN 

                GZEGRVKQP
 GZ8CNWG1WV1H4CPIG 

                      UVFQWVRWV
 9RTQYCFQPC NKEDC LGUV D[V FWC PN 

                GPFVT[

            GPF VGUV$CF+PRWV
56                                                                  Asembler. Sztuka programowania


            W języku HLA instrukcje znajdujące się za słowem VT[, a przed pierwszą klauzulą
            GZEGRVKQP noszą miano instrukcji chronionych. Jeśli podczas wykonywania takich
            instrukcji zgłoszony zostanie wyjątek, sterowanie zostanie przekazane do pierwszej
            klauzuli GZEGRVKQP skojarzonej z danym wyjątkiem, przeszukując kolejne klauzule pod
            ką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ść umieszczana
            w nawiasach klauzuli GZEGRVKQP powinna być wartością 32-bitową. Predefiniowane
            wartoś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 GZEGRVKQP dozwolone jest podawanie wprost wartości numerycznych wyjątków
            bez korzystania z predefiniowanych nazw stałych.


1.11.1. Zagnieżdżone bloki try..endtry
            Jeśli w wyniku przeszukania klauzul GZEGRVKQP nie nastąpi dopasowanie identyfikatora
            wyjątku, będzie miało miejsce przeszukanie klauzul nadrzędnego bloku VT[GPFVT[,
            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
               RTQITCO VGUV$CF+PRWV
                KPENWFG
 UVFNKDJJH 

               UVCVKE
                   W          WPU

               DGIKP VGUV$CF+PRWV

                   VT[

                         VT[

                               UVFQWVRWV
 9RTQYCF NKEDG ECNMQYKVC DG PCMW   
                               UVFKPIGV
 W 
                               UVFQWVRWV
 9RTQYCFQPQ  W PN 

                         GZEGRVKQP
 GZ%QPXGTUKQP'TTQT 

                               UVFQWVRWV
 9RTQYCFQP[ EKCI CYKGTC PKGFQYQNQPG PCMK PN 

                         GPFVT[

                         UVFQWVRWV
 $NCF PKG Y[PKMC  RTGRGNPKGPKC CMTGUW PN 

                   GZEGRVKQP
 GZ8CNWG1WV1H4CPIG 




11
     W programie HLA identyfikator ten umieszczany jest w rejestrze EAX. Stąd po przekazaniu sterowania
     do odpowiedniej klauzuli GZEGRVKQP mo na odwoływać się do identyfikatora wyjątku, odwołując się
     do rejestru EAX.
Rozdział 1. ♦ Wstęp do języka asemblerowego                                                       57


                       UVFQWVRWV
 9RTQYCFQPC NKEDC LGUV D[V FWC PN 

                   GPFVT[

              GPF VGUV$CF+PRWV


            Na listingu 1.9 widać zagnie d one dwa bloki instrukcji VT[GPFVT[. Jeśli w ramach
            wykonania instrukcji UVFKPIGV u ytkownik wprowadzi wartość większą od 4 miliardów
            z kawałkiem, procedura UVFKPIGV podniesie wyjątek GZ8CNWG1WV1H4CPIG. Kiedy wyjątek
            ten zostanie przekazany do systemu wykonawczego HLA, nastąpi przeszukanie klauzul
            GZEGRVKQP bie ącego bloku VT[GPFVT[ (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 GZ8CNWG1WV1H4CPIG w bie ącym bloku VT[GPFVT[, system
            wykonawczy HLA sprawdzi, czy bie ący blok VT[GPFVT[ nie został zagnie d ony
            w innym bloku VT[GPFVT[ (co akurat ma miejsce na listingu 1.9). W takim przypadku
            system wykonawczy HLA przeszuka równie klauzule GZEGRVKQP 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 za
            klauzulą GZEGRVKQP
GZ8CNWG1WV1H4CPIG.

            Po opuszczeniu bloku VT[GPFVT[ system wykonawczy traktuje ów blok jako nieak-
            tywny i jeśli teraz nastąpi zgłoszenie wyjątku, system wykonawczy HLA nie będzie
            przeszukiwał klauzul GZEGRVKQP tego bloku12. Pozwala to na ró nicowanie procedur
            obsługi wyjątków w ró nych miejscach programu.

            Jeśli ten sam wyjątek obsługują dwa bloki VT[GPFVT[, a jeden z tych bloków jest
            zagnie d ony w bloku chronionym pierwszego, to w momencie zgłoszenia wyjątku
            podczas wykonywania wewnętrznego bloku chronionego system wykonawczy HLA
            przeka e sterowanie do kodu obsługi wyjątku w wewnętrznym bloku VT[GPFVT[.
            Kod obsługi tego samego wyjątku w zewnętrznym bloku jest ignorowany — HLA nie
            przekazuje sterowania do tego kodu.

            W przykładzie z listingu 1.9 druga instrukcja VT[GPFVT[ została statycznie zagnie -
            d ona w pierwszym bloku VT[GPFVT[13. Wcześniej wspomniano, e jeśli bie ący blok
            VT[GPFVT[ nie zawiera kodu obsługi zgłoszonego wyjątku, następuje przeszukanie
            klauzul GZEGRVKQP kolejnych bloków VT[GPFVT[, 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ętrza
            bloku VT[GPFVT[ do zupełnie innego miejsca programu, ni by to wynikało z samej
            struktury kodu. Jeśli w tamtym miejscu realizowany jest blok chroniony instrukcji
            VT[GPFVT[, 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

12
     Chyba e sterowanie zostanie wcześniej przekazane z powrotem do bloku VT[GPFVT[, na przykład
     kiedy jest on umieszczony w ciele pętli.
13
     Zagnie 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.
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 procedury
         z bloku kodu chronionego mo e doprowadzić do dynamicznego zagnie d enia bloków
         VT[GPFVT[ — wystarczy, aby procedura równie zawierała blok VT[GPFVT[ (i aby
         został on wykonany).


1.11.2. Klauzula unprotected bloku try..endtry
         Wykonywanie instrukcji VT[ 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 GZEGRVKQP. Jeśli program przebrnie przez
         blok instrukcji chronionych, pierwotne środowisko wyjątków jest przywracane, a ste-
         rowanie przekazywane jest do pierwszej instrukcji znajdującej się za klauzulą GPFVT[.
         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ątki
         powodowałyby przekazanie sterowania do opuszczonego ju bloku VT[GPFVT[. Problem
         ten ilustruje listing.1.10.

Listing 1.10. Niepoprawne opuszczenie bloku instrukcji try..endtry
            RTQITCO VGUV$CF+PRWV
             KPENWFG
 UVFNKDJJH 

            UVCVKE
                KPRWV         WPU

            DGIKP VGUV$CF+PRWV

                 // 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.

                 HQTGXGT

                     VT[

                           UVFQWVRWV
 2QFCL NKEDG ECNMQYKVC 
                           UVFKPIGV
 KPRWV 
                           UVFQWVRWV
 9RTQYCFQPQ  KPRWV PN 
                           DTGCM

                     GZEGRVKQP
 GZ8CNWG1WV1H4CPIG 

                           UVFQWVRWV
 .KEDC LGUV D[V FWC RQYVQT YRTQYCFCPKG PN 

                     GZEGRVKQP
 GZ%QPXGTUKQP'TTQT 

                           UVFQWVRWV
 %KCI CYKGTC PKGFQYQNQPG PCMK RQYVQT YRTQYCFCPKG PN
            

                     GPFVT[

                 GPFHQT
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

               UVFQWVRWV
 2QFCL LGUEG LGFPC NKEDG 
               UVFKPIGV
 KPRWV 
               UVFQWVRWV
 0QYC NKEDC VQ  KPRWV PN 

           GPF VGUV$CF+PRWV


        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ąca
        ponowne wprowadzenie danych przez u ytkownika, jeśli w poprzedniej próbie nie
        udało się ich poprawnie odczytać. Pomysł jest dobry; problem tkwi w jego nieprawi-
        dłowej realizacji. Otó instrukcja DTGCM powoduje opuszczenie pętli HQTGXGTGPFHQT
        bez przywrócenia poprzedniego stanu środowiska wyjątków. Z tego względu, kiedy
        program wykonuje procedurę UVFKPIGV umieszczoną poza pętlą (a tym bardziej poza
        blokiem 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, sterowanie
        zostanie przekazane z powrotem do stosownej klauzuli GZEGRVKQP opuszczonego ju
        przecie bloku VT[GPFVT[. Jeśli identyfikatorem wyjątku będzie GZ8CNWG1WV1H4CPIG
        albo GZ%QPXGTUKQP'TTQT, wyświetlony zostanie stosowny komunikat, a u ytkownik
        ponownie zmuszony będzie do wprowadzenia liczby. Tego zaś programista raczej
        nie przewidywał.

        Przekazanie sterowania do nieodpowiedniego bloku VT[GPFVT[ to tylko część pro-
        blemu. Kod z listingu 1.10 obarczony jest inną powa ną wadą, związaną ze sposobem
        zachowywania i następnie przywracania przez system wykonawczy HLA środowiska
        wyjątków. W szczególności bowiem HLA zachowuje informacje o stanie systemu
        wyjątków w specjalnym obszarze pamięci zwanym stosem. Jeśli po wyjściu z bloku
        VT[GPFVT[ 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 VT[GPFVT[ z pomi-
        nięciem etapu przywrócenia pierwotnego stanu środowiska wyjątków. Wiadomo, e
        sposób wprowadzania danych zaprezentowany na listingu 1.10 jest niepoprawny, ale
        z 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 VT[GPFVT[. Spójrzmy na kod z listingu 1.11.

Listing 1.11. Blok pozbawiony ochrony wewnątrz bloku try..endtry
           RTQITCO VGUV$CF+PRWV
            KPENWFG
 UVFNKDJJH 

           UVCVKE
               KPRWV     WPU

           DGIKP VGUV$CF+PRWV
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.

            HQTGXGT

                VT[

                      UVFQWVRWV
 2QFCL NKEDG ECNMQYKVC 
                      UVFKPIGV
 KPRWV 
                      UVFQWVRWV
 9RTQYCFQPQ  KPRWV PN 

                WPRTQVGEVGF

                      DTGCM

                GZEGRVKQP
 GZ8CNWG1WV1H4CPIG 

                      UVFQWVRWV
 .KEDC LGUV D[V FWC RQYVQT YRTQYCFCPKG PN 

                GZEGRVKQP
 GZ%QPXGTUKQP'TTQT 

                      UVFQWVRWV
 %KCI CYKGTC PKGFQYQNQPG PCMK RQYVQT YRTQYCFCPKG PN
       

                GPFVT[

            GPFHQT

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

            UVFQWVRWV
 2QFCL LGUEG LGFPC NKEDG 
            UVFKPIGV
 KPRWV 
            UVFQWVRWV
 0QYC NKEDC VQ  KPRWV PN 

       GPF VGUV$CF+PRWV


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

     Słowo WPRTQVGEVGF, jeśli ju występuje w ramach instrukcji VT[, powinno znajdować
     się bezpośrednio za blokiem kodu chronionego. Musi więc poprzedzać wszystkie klau-
     zule GZEGRVKQP.
Rozdział 1. ♦ Wstęp do języka asemblerowego                                               61


       W obliczu zgłoszenia wyjątku system wykonawczy HLA automatycznie przywraca
       pierwotny stan środowiska wykonania. Z tego względu w ramach kodu obsługi wyjątku,
       po klauzuli GZEGRVKQP mo na swobodnie korzystać z instrukcji DTGCM, nie obawiając się
       o negatywne efekty charakterystyczne dla niepoprawnego opuszczenia bloku VT[GPFVT[.

       Z racji przywrócenia pierwotnego stanu wyjątków w bloku niechronionym oraz w kodzie
       obsługi wyjątku po klauzuli GZEGRVKQP, wystąpienie wyjątku w jednym z tych obszarów
       powoduje natychmiastowe przekazanie sterowania do zewnętrznego bloku VT[GPFVT[,
       w którym blok bie ący został dynamicznie zagnie d ony. W przypadku braku takiego
       bloku program jest awaryjnie przerywany, a na standardowe wyjście wyprowadzany
       jest stosowny komunikat o błędzie.


1.11.3. Klauzula anyexception bloku try..endtry
       Typowo instrukcji VT[ towarzyszy szereg klauzul GZEGRVKQP obsługujących wszelkie
       wyją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śnie
       zakończony. Jeśli programista jest autorem całości kodu bloku chronionego, powinien
       być zdolny do przewidzenia wszystkich sytuacji wyjątkowych związanych potencjalnie
       z wykonaniem bloku. Jeśli jednak w bloku tym znajdują się wywołanie procedury biblio-
       tecznej, wywołanie funkcji interfejsu systemu operacyjnego czy inne instrukcje, nie
       w pełni kontrolowane przez programistę, przewidzenie wszystkich mo liwych wyjątków
       mo e być niemo liwe. Tymczasem zgłoszenie wyjątku o identyfikatorze innym ni
       identyfikatory podane w kolejnych klauzulach GZEGRVKQP mo e doprowadzić do załamania
       programu. Szczęśliwie język HLA przewiduje umieszczenie w bloku VT[GPFVT[
       klauzuli CP[GZEGRVKQP, do której dopasowywane są wszystkie wyjątki, których identy-
       fikatorów nie uda się odpasować do klauzul GZEGRVKQP.

       Klauzula CP[GZEGRVKQP nie ró ni się wiele od klauzuli GZEGRVKQP — jedyna odmienność
       tkwi w braku konieczności określania identyfikatora wyjątku (to oczywiste). Jeśli obok
       klauzul GZEGRVKQP w bloku VT[GPFVT[ znajduje się klauzula CP[GZEGRVKQP, powinna
       ona być ostatnią klauzulą obsługi wyjątków w bloku. Dopuszczalne jest naturalnie, aby
       klauzula CP[GZEGRVKQP była jedyną klauzulą obsługi wyjątków w bloku VT[GPFVT[.

       Po przekazaniu sterowania do klauzuli CP[GZEGRVKQP identyfikator wyjątku umiesz-
       czany jest w rejestrze EAX. Identyfikator ten mo na w ramach kodu obsługi wyjątku
       sprawdzać, dopasowując obsługę do przyczyny zgłoszenia wyjątku.


1.11.4. Instrukcja try..endtry i rejestry
       Ka dorazowe wkroczenie do bloku VT[GPFVT[ oznacza konieczność zachowania na
       stosie 16 bajtów informacji o stanie środowiska wyjątków. Przywrócenie środowiska
       wyjątków po opuszczeniu tego bloku (albo w wyniku osiągnięcia klauzuli WPRTQVGEVGF)
       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 zmienia
       się w momencie zgłoszenia wyjątku w wyniku wykonania instrukcji kodu chronionego.
62                                                           Asembler. Sztuka programowania


     Po przejściu do klauzuli GZEGRVKQP 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 powinno
     się nawet zakładać, e rejestry ogólnego przeznaczenia zawierać będą po rozpoczęciu
     obsługi wyjątku wartości, które znajdowały się w nich pierwotnie. Kod generowany przez
     system 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 najmniej
     ryzykowne.

     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 za
     klauzulą GPFVT[ zakłada obecność w rejestrach jakichś konkretnych wartości (np.
     wartości umieszczonych tam przed wkroczeniem do bloku kodu chronionego), nale y te
     wartości przywrócić samodzielnie. Zaniedbanie tego mo e doprowadzić do błędnego
     działania programu, przy czym błędy tego rodzaju są tym trudniejsze do wykrycia, e
     sytuacje 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:
       UVCVKE
           UWO      KPV


            OQX
  UWO 
            HQT
 OQX
  GDZ  GDZ   KPE
 GDZ   FQ

                 RWUJ
 GDZ      // Zachowanie wartości rejestru EBX na wypadek wyjątku.
                 HQTGXGT
                     VT[

                         UVFKPIGVK

                         WPRTQVGEVGF DTGCM

                     GZEGRVKQP
 GZ%QPXGTUKQP'TTQT 

                         UVFQWVRWV
 0KGFQYQNQPC YCTVQ è URTQDWL RQPQYPKG     

                     GPFVT[
                 GPFHQT
                 RQR
 GDZ     // Przywrócenie zawartości rejestru EBX.
                 CFF
 GDZ GCZ 
                 CFF
 GCZ UWO 

       GPFHQT

     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 VT[ w roli zwykłej struktury sterującej wykonaniem programu (na
     przykład symulując w bloku VT[GPFVT[ działanie instrukcji wyboru znanych z języków
     wysokiego poziomu, jak ECUG czy UYKVEJ). 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.
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 aktywacji
       omawiane są w rozdziale poświęconym procedurom). Standardowo programy HLA
       wykorzystują ó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 ten
       zostanie u yty w roli rejestru ogólnego przeznaczenia, na przykład w obliczeniach
       arytmetycznych, 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.

Asembler. Sztuka programowania

  • 1.
    IDZ DO PRZYK£ADOWY ROZDZIA£ SPIS TRE CI Asembler. Sztuka programowania KATALOG KSI¥¯EK Autor: Randall Hyde KATALOG ONLINE T³umaczenie: Przemys³aw Szeremiota ISBN: 83-7361-602-0 ZAMÓW DRUKOWANY KATALOG Tytu³ orygina³u: The Art of Assembly Language Format: B5, stron: 968 TWÓJ KOSZYK DODAJ DO KOSZYKA 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, CENNIK I INFORMACJE kompilacji i uruchamiania programów wykorzystuj¹cych ró¿ne, nawet najbardziej z³o¿one typy danych. ZAMÓW INFORMACJE • Wprowadzenie do jêzyka HLA O NOWO CIACH • Sposoby reprezentacji danych • Organizacja pamiêci i tryby adresowania ZAMÓW CENNIK • Typy danych • Podzia³ programu na procedury i modu³y • Sterowanie wykonaniem programu CZYTELNIA • Instrukcje arytmetyczne • Operacje na plikach FRAGMENTY KSI¥¯EK ONLINE • 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 Wydawnictwo Helion ul. Chopina 6 44-100 Gliwice tel. (32)230-98-63 e-mail: helion@helion.pl
  • 2.
    Spis treści Rozdział 1.Wstęp do języka asemblerowego...................................................... 13 1.1. Wprowadzenie........................................................................................................ 13 1.2. Anatomia programu HLA....................................................................................... 13 1.3. Uruchamianie pierwszego programu HLA ............................................................. 16 1.4. Podstawowe deklaracje danych programu HLA..................................................... 17 1.5. Wartości logiczne ................................................................................................... 19 1.6. Wartości znakowe................................................................................................... 20 1.7. Rodzina procesorów 80x86 firmy Intel .................................................................. 21 1.7.1. Podsystem obsługi pamięci........................................................................... 24 1.8. Podstawowe instrukcje maszynowe........................................................................ 26 1.9. Podstawowe struktury sterujące wykonaniem programu HLA............................... 30 1.9.1. Wyra enia logiczne w instrukcjach HLA ..................................................... 31 1.9.2. Instrukcje if..then..elseif..else..endif języka HLA ......................................... 33 1.9.3. Iloczyn, suma i negacja w wyra eniach logicznych...................................... 35 1.9.4. Instrukcja while ............................................................................................ 37 1.9.5. Instrukcja for................................................................................................. 38 1.9.6. Instrukcja repeat............................................................................................ 39 1.9.7. Instrukcje break oraz breakif......................................................................... 40 1.9.8. Instrukcja forever.......................................................................................... 40 1.9.9. Instrukcje try, exception oraz endtry............................................................. 41 1.10. Biblioteka standardowa języka HLA — wprowadzenie ......................................... 44 1.10.1. Stałe predefiniowane w module stdio ........................................................... 46 1.10.2. Standardowe wejście i wyjście programu ..................................................... 46 1.10.3. Procedura stdout.newln................................................................................. 47 1.10.4. Procedury stdout.putiN ................................................................................. 47 1.10.5. Procedury stdout.putiNSize .......................................................................... 48 1.10.6. Procedura stdout.put ..................................................................................... 49 1.10.7. Procedura stdin.getc...................................................................................... 51 1.10.8. Procedury stdin.getiN ................................................................................... 52 1.10.9. Procedury stdin.readLn i stdin.flushInput..................................................... 53 1.10.10. Procedura stdin.get ....................................................................................... 54 1.11. Jeszcze o ochronie wykonania kodu w bloku try..endtry........................................ 55 1.11.1. Zagnie d one bloki try..endtry ..................................................................... 56 1.11.2. Klauzula unprotected bloku try..endtry......................................................... 58 1.11.3. Klauzula anyexception bloku try..endtry ...................................................... 61 1.11.4. Instrukcja try..endtry i rejestry...................................................................... 61 1.12. Język asemblerowy a język HLA ........................................................................... 63 1.13. Źródła informacji dodatkowych.............................................................................. 64
  • 3.
    4 Asembler. Sztuka programowania Rozdział 2. Reprezentacja danych ...................................................................... 65 2.1. Wprowadzenie........................................................................................................ 65 2.2. Systemy liczbowe ................................................................................................... 66 2.2.1. System dziesiętny — przypomnienie............................................................ 66 2.2.2. System dwójkowy......................................................................................... 66 2.2.3. Formaty liczb dwójkowych........................................................................... 68 2.3. System szesnastkowy.............................................................................................. 69 2.4. Organizacja danych ................................................................................................ 72 2.4.1. Bity ............................................................................................................... 72 2.4.2. Półbajty......................................................................................................... 73 2.4.3. Bajty ............................................................................................................. 73 2.4.4. Słowa ............................................................................................................ 75 2.4.5. Podwójne słowa ............................................................................................ 76 2.4.6. Słowa poczwórne i długie............................................................................. 77 2.5. Operacje arytmetyczne na liczbach dwójkowych i szesnastkowych....................... 77 2.6. Jeszcze o liczbach i ich reprezentacji...................................................................... 78 2.7. Operacje logiczne na bitach.................................................................................... 81 2.8. Operacje logiczne na liczbach dwójkowych i ciągach bitów.................................. 84 2.9. Liczby ze znakiem i bez znaku ............................................................................... 86 2.10. Rozszerzanie znakiem, rozszerzanie zerem, skracanie, przycinanie....................... 91 2.11. Przesunięcia i obroty .............................................................................................. 95 2.12. Pola bitowe i dane spakowane................................................................................ 99 2.13. Wprowadzenie do arytmetyki zmiennoprzecinkowej ........................................... 104 2.13.1. Formaty zmiennoprzecinkowe przyjęte przez IEEE ................................... 108 2.13.2. Obsługa liczb zmiennoprzecinkowych w języku HLA ............................... 112 2.14. Reprezentacja liczb BCD ..................................................................................... 115 2.15. Znaki .................................................................................................................... 117 2.15.1. Zestaw znaków ASCII................................................................................ 117 2.15.2. Obsługa znaków ASCII w języku HLA...................................................... 121 2.16. Zestaw znaków Unicode....................................................................................... 125 2.17. Źródła informacji dodatkowych............................................................................ 126 Rozdział 3. Dostęp do pamięci i jej organizacja ................................................ 127 3.1. Wprowadzenie...................................................................................................... 127 3.2. Tryby adresowania procesorów 80x86 ................................................................. 127 3.2.1. Adresowanie przez rejestr........................................................................... 128 3.2.2. 32-bitowe tryby adresowania procesora 80x86........................................... 129 3.3. Organizacja pamięci fazy wykonania ................................................................... 135 3.3.1. Obszar kodu................................................................................................ 137 3.3.2. Obszar zmiennych statycznych................................................................... 139 3.3.3. Obszar niemodyfikowalny .......................................................................... 140 3.3.4. Obszar danych niezainicjalizowanych ........................................................ 141 3.3.5. Atrybut @nostorage.................................................................................... 141 3.3.6. Sekcja deklaracji var ................................................................................... 142 3.3.7. Rozmieszczenie sekcji deklaracji danych w programie HLA ..................... 143 3.4. Przydział pamięci dla zmiennych w programach HLA......................................... 144 3.5. Wyrównanie danych w programach HLA ............................................................ 146 3.6. Wyra enia adresowe............................................................................................. 149 3.7. Koercja typów ...................................................................................................... 151 3.8. Koercja typu rejestru ............................................................................................ 154 3.9. Pamięć obszaru stosu oraz instrukcje push i pop .................................................. 155 3.9.1. Podstawowa postać instrukcji push............................................................. 155 3.9.2. Podstawowa postać instrukcji pop .............................................................. 157 3.9.3. Zachowywanie wartości rejestrów za pomocą instrukcji push i pop .......... 158
  • 4.
    Spis treści 5 3.9.4. Stos jako kolejka LIFO ............................................................................... 159 3.9.5. Pozostałe wersje instrukcji obsługi stosu.................................................... 161 3.9.6. Usuwanie danych ze stosu bez ich zdejmowania ........................................ 163 3.9.7. Odwoływanie się do danych na stosie bez ich zdejmowania ...................... 165 3.10. Dynamiczny przydział pamięci — obszar pamięci sterty ..................................... 166 3.11. Instrukcje inc oraz dec.......................................................................................... 171 3.12. Pobieranie adresu obiektu..................................................................................... 171 3.13. Źródła informacji dodatkowych............................................................................ 172 Rozdział 4. Stałe, zmienne i typy danych.......................................................... 173 4.1. Wprowadzenie...................................................................................................... 173 4.2. Kilka dodatkowych instrukcji: intmul, bound i into ............................................. 174 4.3. Typ tbyte............................................................................................................... 178 4.4. Deklaracje stałych i zmiennych w języku HLA.................................................... 178 4.4.1. Typy stałych ............................................................................................... 182 4.4.2. Literały stałych łańcuchowych i znakowych............................................... 183 4.4.3. Stałe łańcuchowe i napisowe w sekcji const............................................... 185 4.4.4. Wyra enia stałowartościowe....................................................................... 186 4.4.5. Wielokrotne sekcje const i ich kolejność w programach HLA ................... 189 4.4.6. Sekcja val programu HLA .......................................................................... 190 4.4.7. Modyfikowanie obiektów sekcji val w wybranym miejscu kodu źródłowego programu ....................................................................... 191 4.5. Sekcja type programu HLA .................................................................................. 192 4.6. Typy wyliczeniowe w języku HLA ...................................................................... 193 4.7. Typy wskaźnikowe ............................................................................................... 194 4.7.1. Wskaźniki w języku asemblerowym........................................................... 196 4.7.2. Deklarowanie wskaźników w programach HLA ........................................ 197 4.7.3. Stałe wskaźnikowe i wyra enia stałych wskaźnikowych............................ 197 4.7.4. Zmienne wskaźnikowe a dynamiczny przydział pamięci ........................... 199 4.7.5. Typowe błędy stosowania wskaźników ...................................................... 200 4.8. Moduł chars.hhf biblioteki standardowej HLA .................................................... 205 4.9. Zło one typy danych ............................................................................................ 207 4.10. Łańcuchy znaków................................................................................................. 208 4.11. Łańcuchy w języku HLA...................................................................................... 210 4.12. Odwołania do poszczególnych znaków łańcucha ................................................. 217 4.13. Moduł strings biblioteki standardowej HLA i procedury manipulacji łańcuchami...219 4.14. Konwersje wewnątrzpamięciowe ......................................................................... 231 4.15. Zbiory znaków...................................................................................................... 232 4.16. Implementacja zbiorów znaków w języku HLA................................................... 233 4.17. Literały, stałe i wyra enia zbiorów znaków w języku HLA ................................. 235 4.18. Operator in w wyra eniach logicznych wysokopoziomowego rozszerzenia języka HLA.................................................. 237 4.19. Obsługa zbiorów znaków w bibliotece standardowej HLA.................................. 237 4.20. Wykorzystywanie zbiorów znaków w programach HLA ..................................... 241 4.21. Tablice.................................................................................................................. 243 4.22. Deklarowanie tablic w programach HLA ............................................................. 244 4.23. Literały tablicowe................................................................................................. 245 4.24. Odwołania do elementów tablicy jednowymiarowej ............................................ 246 4.24.1. Porządkowanie tablicy wartości ................................................................. 248 4.25. Tablice wielowymiarowe ..................................................................................... 250 4.25.1. Wierszowy układ elementów tablicy .......................................................... 251 4.25.2. Kolumnowy układ elementów tablicy ........................................................ 255 4.26. Przydział pamięci dla tablic wielowymiarowych ................................................. 256 4.27. Odwołania do elementów tablic wielowymiarowych w języku asemblerowym... 258
  • 5.
    6 Asembler. Sztuka programowania 4.28. Du e tablice i MASM (tylko dla programistów systemu Windows) .................... 259 4.29. Rekordy (struktury) .............................................................................................. 260 4.30. Stałe rekordowe.................................................................................................... 263 4.31. Tablice rekordów.................................................................................................. 264 4.32. Wykorzystanie tablic i rekordów w roli pól rekordów ......................................... 265 4.33. Ingerowanie w przesunięcia pól rekordów ........................................................... 269 4.34. Wyrównanie pól w ramach rekordu...................................................................... 270 4.35. Wskaźniki na rekordy........................................................................................... 271 4.36. Unie ...................................................................................................................... 273 4.37. Unie anonimowe................................................................................................... 275 4.38. Typy wariantowe .................................................................................................. 276 4.39. Stałe unii............................................................................................................... 277 4.40. Przestrzenie nazw ................................................................................................. 278 4.41. Tablice dynamiczne w języku asemblerowym ..................................................... 281 4.42. Obsługa tablic w bibliotece standardowej języka HLA ........................................ 284 4.43. Źródła informacji dodatkowych............................................................................ 287 Rozdział 5. Procedury i moduły ........................................................................ 289 5.1. Wprowadzenie...................................................................................................... 289 5.2. Procedury.............................................................................................................. 289 5.3. Zachowywanie stanu systemu .............................................................................. 292 5.4. Przedwczesny powrót z procedury ....................................................................... 296 5.5. Zmienne lokalne ................................................................................................... 297 5.6. Symbole lokalne i globalne obiektów innych ni zmienne................................... 303 5.7. Parametry.............................................................................................................. 304 5.7.1. Przekazywanie przez wartość ..................................................................... 305 5.7.2. Przekazywanie przez adres ......................................................................... 308 5.8. Funkcje i wartości funkcji .................................................................................... 311 5.8.1. Zwracanie wartości funkcji......................................................................... 312 5.8.2. Zło enie instrukcji języka HLA .................................................................. 313 5.8.3. Atrybut @returns procedur języka HLA..................................................... 316 5.9. Rekurencja............................................................................................................ 318 5.10. Deklaracje zapowiadające .................................................................................... 322 5.11. Procedury w ujęciu niskopoziomowym — instrukcja call.................................... 323 5.12. Rola stosu w procedurach..................................................................................... 325 5.13. Rekordy aktywacji................................................................................................ 328 5.14. Standardowa sekwencja wejścia do procedury ..................................................... 331 5.15. Standardowa sekwencja wyjścia z procedury....................................................... 333 5.16. Niskopoziomowa implementacja zmiennych automatycznych ............................ 334 5.17. Niskopoziomowa implementacja parametrów procedury..................................... 336 5.17.1. Przekazywanie argumentów w rejestrach ................................................... 337 5.17.2. Przekazywanie argumentów w kodzie programu........................................ 340 5.17.3. Przekazywanie argumentów przez stos....................................................... 342 5.18. Wskaźniki na procedury ....................................................................................... 365 5.19. Parametry typu procedurowego............................................................................ 368 5.20. Nietypowane parametry wskaźnikowe ................................................................. 370 5.21. Zarządzanie du ymi projektami programistycznymi............................................ 371 5.22. Dyrektywa #include ............................................................................................. 372 5.23. Unikanie wielokrotnego włączania do kodu tego samego pliku........................... 374 5.24. Moduły a atrybut @external................................................................................. 375 5.24.1. Działanie atrybutu @external ..................................................................... 380 5.24.2. Pliki nagłówkowe w programach HLA....................................................... 382 5.25. Jeszcze o problemie zaśmiecania przestrzeni nazw .............................................. 384 5.26. Źródła informacji dodatkowych............................................................................ 386
  • 6.
    Spis treści 7 Rozdział 6. Arytmetyka.................................................................................... 389 6.1. Wprowadzenie...................................................................................................... 389 6.2. Zestaw instrukcji arytmetycznych procesora 80x86 ............................................. 389 6.2.1. Instrukcje mul i imul................................................................................... 389 6.2.2. Instrukcje div i idiv..................................................................................... 393 6.2.3. Instrukcja cmp ............................................................................................ 396 6.2.4. Instrukcje setXX ......................................................................................... 401 6.2.5. Instrukcja test.............................................................................................. 403 6.3. Wyra enia arytmetyczne ...................................................................................... 404 6.3.1. Proste przypisania ....................................................................................... 405 6.3.2. Proste wyra enia ......................................................................................... 406 6.3.3. Wyra enia zło one...................................................................................... 408 6.3.4. Operatory przemienne................................................................................. 413 6.4. Wyra enia logiczne .............................................................................................. 414 6.5. Idiomy maszynowe a idiomy arytmetyczne.......................................................... 417 6.5.1. Mno enie bez stosowania instrukcji mul, imul i intmul.............................. 417 6.5.2. Dzielenie bez stosowania instrukcji div i idiv............................................. 419 6.5.3. Zliczanie modulo n za pośrednictwem instrukcji and ................................. 420 6.5.4. Nieostro ne korzystanie z idiomów maszynowych .................................... 420 6.6. Arytmetyka zmiennoprzecinkowa ........................................................................ 421 6.6.1. Rejestry jednostki zmiennoprzecinkowej.................................................... 421 6.6.2. Typy danych jednostki zmiennoprzecinkowej ............................................ 429 6.6.3. Zestaw instrukcji jednostki zmiennoprzecinkowej ..................................... 430 6.6.4. Instrukcje przemieszczania danych............................................................. 431 6.6.5. Instrukcje konwersji.................................................................................... 433 6.6.6. Instrukcje arytmetyczne .............................................................................. 436 6.6.7. Instrukcje porównań ................................................................................... 442 6.6.8. Instrukcje ładowania stałych na stos koprocesora....................................... 445 6.6.9. Instrukcje funkcji przestępnych .................................................................. 445 6.6.10. Pozostałe instrukcje jednostki zmiennoprzecinkowej ................................. 447 6.6.11. Instrukcje operacji całkowitoliczbowych.................................................... 449 6.7. Tłumaczenie wyra eń arytmetycznych na kod maszynowy jednostki zmiennoprzecinkowej........................................................................... 449 6.7.1. Konwersja notacji wrostkowej do odwrotnej notacji polskiej..................... 451 6.7.2. Konwersja odwrotnej notacji polskiej do kodu języka asemblerowego...... 453 6.8. Obsługa arytmetyki zmiennoprzecinkowej w bibliotece standardowej języka HLA.....455 6.8.1. Funkcje stdin.getf i fileio.getf ..................................................................... 455 6.8.2. Funkcje trygonometryczne modułu math.................................................... 455 6.8.3. Funkcje wykładnicze i logarytmiczne modułu math................................... 456 6.9. Podsumowanie...................................................................................................... 458 Rozdział 7. Niskopoziomowe struktury sterujące wykonaniem programu............ 459 7.1. Wprowadzenie...................................................................................................... 459 7.2. Struktury sterujące niskiego poziomu................................................................... 460 7.3. Etykiety instrukcji................................................................................................. 460 7.4. Bezwarunkowy skok do instrukcji (instrukcja jmp) ............................................. 462 7.5. Instrukcje skoku warunkowego ............................................................................ 465 7.6. Struktury sterujące „średniego” poziomu — jt i jf................................................ 468 7.7. Implementacja popularnych struktur sterujących w języku asemblerowym ......... 469 7.8. Wstęp do podejmowania decyzji .......................................................................... 469 7.8.1. Instrukcje if..then..else................................................................................ 471 7.8.2. Tłumaczenie instrukcji if języka HLA na język asemblerowy.................... 475 7.8.3. Obliczanie wartości zło onych wyra eń logicznych — metoda pełnego szacowania wartości wyra enia................................... 480
  • 7.
    8 Asembler. Sztuka programowania 7.8.4. Skrócone szacowanie wyra eń logicznych ................................................. 481 7.8.5. Wady i zalety metod szacowania wartości wyra eń logicznych ................. 483 7.8.6. Efektywna implementacja instrukcji if w języku asemblerowym ............... 485 7.8.7. Instrukcje wyboru ....................................................................................... 490 7.9. Skoki pośrednie a automaty stanów...................................................................... 500 7.10. Kod spaghetti........................................................................................................ 503 7.11. Pętle...................................................................................................................... 504 7.11.1. Pętle while .................................................................................................. 505 7.11.2. Pętle repeat..until ........................................................................................ 506 7.11.3. Pętle nieskończone ..................................................................................... 508 7.11.4. Pętle for ...................................................................................................... 508 7.11.5. Instrukcje break i continue.......................................................................... 509 7.11.6. Pętle a rejestry ............................................................................................ 513 7.12. Optymalizacja kodu.............................................................................................. 514 7.12.1. Obliczanie warunku zakończenia pętli na końcu pętli ................................ 515 7.12.2. Zliczanie licznika pętli wstecz .................................................................... 517 7.12.3. Wstępne obliczanie niezmienników pętli.................................................... 518 7.12.4. Rozciąganie pętli ........................................................................................ 519 7.12.5. Zmienne indukcyjne ................................................................................... 521 7.13. Mieszane struktury sterujące w języku HLA........................................................ 522 7.14. Źródła informacji dodatkowych............................................................................ 524 Rozdział 8. Pliki .............................................................................................. 525 8.1. Wprowadzenie...................................................................................................... 525 8.2. Organizacja plików............................................................................................... 525 8.2.1. Pliki jako listy rekordów............................................................................. 526 8.2.2. Pliki tekstowe a pliki binarne...................................................................... 528 8.3. Pliki sekwencyjne ................................................................................................. 530 8.4. Pliki dostępu swobodnego .................................................................................... 538 8.5. Indeksowany sekwencyjny dostęp do pliku (ISAM) ............................................ 543 8.6. Przycinanie pliku .................................................................................................. 546 8.7. Źródła informacji dodatkowych............................................................................ 548 Rozdział 9. Zaawansowane obliczenia w języku asemblerowym......................... 549 9.1. Wprowadzenie...................................................................................................... 549 9.2. Operacje o zwielokrotnionej precyzji ................................................................... 550 9.2.1. Obsługa operacji zwielokrotnionej precyzji w bibliotece standardowej języka HLA...................................................... 550 9.2.2. Dodawanie liczb zwielokrotnionej precyzji ................................................ 553 9.2.3. Odejmowanie liczb zwielokrotnionej precyzji............................................ 556 9.2.4. Porównanie wartości o zwielokrotnionej precyzji ...................................... 558 9.2.5. Mno enie operandów zwielokrotnionej precyzji ........................................ 562 9.2.6. Dzielenie wartości zwielokrotnionej precyzji ............................................. 565 9.2.7. Negacja operandów zwielokrotnionej precyzji ........................................... 575 9.2.8. Iloczyn logiczny operandów zwielokrotnionej precyzji.............................. 577 9.2.9. Suma logiczna operandów zwielokrotnionej precyzji................................. 577 9.2.10. Suma wyłączająca operandów zwielokrotnionej precyzji........................... 578 9.2.11. Inwersja operandów zwielokrotnionej precyzji .......................................... 578 9.2.12. Przesunięcia bitowe operandów zwielokrotnionej precyzji ........................ 578 9.2.13. Obroty operandów zwielokrotnionej precyzji............................................. 583 9.2.14. Operandy zwielokrotnionej precyzji w operacjach wejścia-wyjścia........... 583 9.3. Manipulowanie operandami ró nych rozmiarów.................................................. 604 9.4. Arytmetyka liczb dziesiętnych.............................................................................. 606 9.4.1. Literały liczb BCD...................................................................................... 608 9.4.2. Instrukcje maszynowe daa i das.................................................................. 608
  • 8.
    Spis treści 9 9.4.3. Instrukcje maszynowe aaa, aas, aam i aad .................................................. 610 9.4.4. Koprocesor a arytmetyka spakowanych liczb dziesiętnych ........................ 612 9.5. Obliczenia w tabelach........................................................................................... 615 9.5.1. Wyszukiwanie w tabeli wartości funkcji .................................................... 615 9.5.2. Dopasowywanie dziedziny ......................................................................... 620 9.5.3. Generowanie tabel wartości funkcji............................................................ 621 9.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 .......................................... 627 10.1. Wprowadzenie...................................................................................................... 627 10.2. Język czasu kompilacji — wstęp .......................................................................... 627 10.3. Instrukcje #print i #error....................................................................................... 629 10.4. Stałe i zmienne czasu kompilacji.......................................................................... 631 10.5. Wyra enia i operatory czasu kompilacji............................................................... 632 10.6. Funkcje czasu kompilacji ..................................................................................... 635 10.6.1. Funkcje czasu kompilacji — konwersja typów........................................... 636 10.6.2. Funkcje czasu kompilacji — obliczenia numeryczne ................................. 638 10.6.3. Funkcje czasu kompilacji — klasyfikacja znaków ..................................... 638 10.6.4. Funkcje czasu kompilacji — manipulacje łańcuchami znaków .................. 639 10.6.5. Funkcje czasu kompilacji — dopasowywanie wzorców............................. 639 10.6.6. Odwołania do tablicy symboli .................................................................... 641 10.6.7. Pozostałe funkcje czasu kompilacji ............................................................ 643 10.6.8. Konwersja typu stałych napisowych........................................................... 643 10.7. Kompilacja warunkowa........................................................................................ 645 10.8. Kompilacja wielokrotna (pętle czasu kompilacji)................................................. 650 10.9. Makrodefinicje (procedury czasu kompilacji) ...................................................... 653 10.9.1. Makrodefinicje standardowe....................................................................... 654 10.9.2. Argumenty makrodefinicji.......................................................................... 656 10.9.3. Symbole lokalne makrodefinicji ................................................................. 663 10.9.4. Makrodefinicje jako procedury czasu kompilacji ....................................... 666 10.9.5. Symulowane przecią anie funkcji .............................................................. 667 10.10. Tworzenie programów czasu kompilacji.............................................................. 672 10.10.1. Generowanie tabel wartości funkcji............................................................ 673 10.10.2. Rozciąganie pętli ........................................................................................ 677 10.11. Stosowanie makrodefinicji w osobnych plikach kodu źródłowego ...................... 679 10.12. Źródła informacji dodatkowych ........................................................................... 679 Rozdział 11. Manipulowanie bitami .................................................................... 681 11.1. Wprowadzenie...................................................................................................... 681 11.2. Czym są dane bitowe? .......................................................................................... 681 11.3. Instrukcje manipulujące bitami............................................................................. 683 11.4. Znacznik przeniesienia w roli akumulatora bitów ................................................ 692 11.5. Wstawianie i wyodrębnianie łańcuchów bitów .................................................... 693 11.6. Scalanie zbiorów bitów i rozpraszanie łańcuchów bitowych................................ 696 11.7. Spakowane tablice łańcuchów bitowych .............................................................. 699 11.8. Wyszukiwanie bitów ............................................................................................ 701 11.9. Zliczanie bitów ..................................................................................................... 704 11.10. Odwracanie łańcucha bitów.................................................................................. 707 11.11. Scalanie łańcuchów bitowych .............................................................................. 709 11.12. Wyodrębnianie łańcuchów bitów ......................................................................... 710 11.13. Wyszukiwanie wzorca bitowego .......................................................................... 712 11.14. Moduł bits biblioteki standardowej HLA ............................................................. 713 11.15. Źródła informacji dodatkowych ........................................................................... 715
  • 9.
    10 Asembler. Sztuka programowania Rozdział 12. Operacje łańcuchowe..................................................................... 717 12.1. Wprowadzenie...................................................................................................... 717 12.2. Instrukcje łańcuchowe procesorów 80x86............................................................ 717 12.2.1. Sposób działania instrukcji łańcuchowych ................................................. 718 12.2.2. Przedrostki instrukcji łańcuchowych — repX ............................................ 719 12.2.3. Znacznik kierunku ...................................................................................... 719 12.2.4. Instrukcja movs........................................................................................... 721 12.2.5. Instrukcja cmps........................................................................................... 727 12.2.6. Instrukcja scas............................................................................................. 731 12.2.7. Instrukcja stos............................................................................................. 732 12.2.8. Instrukcja lods ............................................................................................ 733 12.2.9. Instrukcje lods i stos w zło onych operacjach łańcuchowych .................... 733 12.3. Wydajność instrukcji łańcuchowych procesorów 80x86 ...................................... 734 12.4. Źródła informacji dodatkowych............................................................................ 735 Rozdział 13. Instrukcje MMX............................................................................. 737 13.1. Wprowadzenie...................................................................................................... 737 13.2. Sprawdzanie obecności rozszerzenia MMX......................................................... 738 13.3. Środowisko programowania MMX ...................................................................... 739 13.3.1. Rejestry MMX............................................................................................ 739 13.3.2. Typy danych MMX .................................................................................... 741 13.4. Przeznaczenie instrukcji zestawu MMX............................................................... 742 13.5. Arytmetyka z nasycaniem a arytmetyka z zawijaniem ......................................... 743 13.6. Operandy instrukcji MMX ................................................................................... 744 13.7. Instrukcje zestawu MMX ..................................................................................... 746 13.7.1. Instrukcje transferu danych......................................................................... 747 13.7.2. Instrukcje konwersji.................................................................................... 747 13.7.3. Arytmetyka operandów spakowanych ........................................................ 752 13.7.4. Instrukcje logiczne...................................................................................... 755 13.7.5. Instrukcje porównań ................................................................................... 756 13.7.6. Instrukcje przesunięć bitowych................................................................... 760 13.7.7. Instrukcja emms.......................................................................................... 762 13.8. Model programowania MMX............................................................................... 763 13.9. Źródła informacji dodatkowych............................................................................ 774 Rozdział 14. Klasy i obiekty............................................................................... 775 14.1. Wprowadzenie...................................................................................................... 775 14.2. Wstęp do programowania obiektowego................................................................ 775 14.3. Klasy w języku HLA ............................................................................................ 779 14.4. Obiekty ................................................................................................................. 782 14.5. Dziedziczenie ....................................................................................................... 784 14.6. Przesłanianie......................................................................................................... 785 14.7. Metody wirtualne a procedury statyczne .............................................................. 786 14.8. Implementacje metod i procedur klas ................................................................... 788 14.9. Implementacja obiektu ......................................................................................... 793 14.9.1. Tabela metod wirtualnych .......................................................................... 796 14.9.2. Reprezentacja w pamięci obiektu klasy pochodnej..................................... 798 14.10. Konstruktory i inicjalizacja obiektów................................................................... 802 14.10.1. Konstruktor a dynamiczny przydział obiektu ............................................. 804 14.10.2. Konstruktory a dziedziczenie...................................................................... 806 14.10.3. Parametry konstruktorów i przecią anie procedur klas .............................. 810 14.11. Destruktory........................................................................................................... 811 14.12. Łańcuchy _initialize_ oraz _finalize_ w języku HLA .......................................... 812 14.13. Metody abstrakcyjne ............................................................................................ 818
  • 10.
    Spis treści 11 14.14. Informacja o typie czasu wykonania (RTTI) ........................................................ 822 14.15. Wywołania metod klasy bazowej ......................................................................... 824 14.16. Źródła informacji dodatkowych ........................................................................... 825 Rozdział 15. Na styku asemblera i innych języków programowania...................... 827 15.1. Wprowadzenie...................................................................................................... 827 15.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................. 828 15.2.2. Konsolidacja modułów MASM (Gas) z modułami HLA............................ 832 15.3. Moduły HLA a programy języka Delphi (Kylix) ................................................. 837 15.3.1. Konsolidacja modułów HLA z programami języka Delphi (Kylix) ........... 838 15.3.2. Zachowywanie wartości rejestrów.............................................................. 842 15.3.3. Wartości zwracane funkcji.......................................................................... 843 15.3.4. Konwencje wywołań .................................................................................. 849 15.3.5. Przekazywanie argumentów przez wartość i adres, parametry niemodyfikowalne i wyjściowe ................................................. 854 15.3.6. Skalarne typy danych w językach HLA i Delphi (Kylix) ........................... 856 15.3.7. Przekazywanie łańcuchów znaków z Delphi do procedury HLA ............... 858 15.3.8. Przekazywanie rekordów z programu w języku Delphi do kodu HLA....... 862 15.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 ............. 867 15.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++ ......................................................... 870 15.4.1. Konsolidacja modułów języka HLA z programami języków C i C++........ 872 15.4.2. Zachowywanie wartości rejestrów.............................................................. 875 15.4.3. Wartości funkcji ......................................................................................... 876 15.4.4. Konwencje wywołań .................................................................................. 876 15.4.5. Tryby przekazywania argumentów ............................................................. 880 15.4.6. Odwzorowanie typów skalarnych pomiędzy językiem C (C++) a językiem HLA ........................................... 881 15.4.7. Przekazywanie łańcuchów znaków pomiędzy programem w języku C (C++) a modułem HLA........................ 883 15.4.8. Przekazywanie rekordów pomiędzy programem w języku C (C++) a modułem HLA........................ 883 15.4.9. Przekazywanie tablic pomiędzy programem w języku C (C++) a modułem HLA........................ 886 15.5. Źródła informacji dodatkowych............................................................................ 886 Dodatek A Tabela kodów ASCII....................................................................... 887 Dodatek B Instrukcje procesorów 80x86......................................................... 891 Skorowidz...................................................................................... 927
  • 11.
    1.9. Podstawowe strukturysterujące wykonaniem programu HLA Instrukcje OQX, CFF oraz UWD, choć niezwykle u yteczne, nie wystarczają do napisania sensownych programów. Aby takie programy mogły powstać, podstawowe instrukcje muszą zostać uzupełnione o podstawowy zbiór struktur sterujących wykonaniem kodu umo liwiających tworzenie pętli i podejmowanie decyzji w programie. Język HLA przewiduje szereg wysokopoziomowych struktur sterujących wykonaniem charaktery- stycznych dla języków wy szego ni asembler poziomu. Programista HLA ma więc do dyspozycji konstrukcje KHVJGPGNUGKHGNUGGPFKH, YJKNGGPFYJKNG, TGRGCV WPVKN i tak dalej. Dopiero znajomość i umiejętność wykorzystywania owych konstrukcji pozwala 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 racji niewielkiego jeszcze zasobu wiedzy o języku asemblerowym, nieco sztuczne struktury sterujące HLA będą bardzo pomocne. Warto przy tym pamiętać, e wysokopoziomowe na pierwszy rzut oka struktury sterujące nie są w istocie tak wysokopoziomowe. Mają one bowiem jedynie ułatwić rozpoczęcie pisania 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 owe wysokopoziomowe struktury sterujące mają szereg ograniczeń, które w miarę nabie- rania przez Czytelnika doświadczenia i w miarę komplikowania kolejnych programów staną się dość ucią liwe. To zresztą efekt zamierzony — po osiągnięciu pewnego poziomu umiejętności programista sam stwierdzi, e w wielu przypadkach wygoda (i czytelność programu) wynikająca z zastosowania struktur sterujących HLA nie rekompensuje utraty efektywności, charakterystycznej dla kodu wykorzystującego wprost instrukcje maszynowe procesora. Niniejsze omówienie kierowane jest do tych Czytelników, którzy znają ju przynajmniej jeden z popularnych języków wysokiego poziomu. Znajomość ta znakomicie uprości prezentację struktur sterujących wykonaniem programu HLA, pozwalając na pominięcie opisów sposobów zastosowania owych struktur w typowych zadaniach programistycz- nych. Czytelnicy potrafiący korzystać ze struktur sterujących wykonaniem programu w 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łownie identyczny.
  • 12.
    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ślenia w 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 KH, YJKNG oraz TGRGCVWPVKN. Pierwszą oznaką ograniczeń struktur sterujących wykonaniem programu HLA jest skł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ównie wymyś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: PCEPKM PCEPKM TGLGUVT TGLGUVT OKGPPCNQIKEPC OKGPPCNQIKEPC RCOKúèTGLGUVT QRGTCVQTTGNCELK RCOKúèTGLGUVTUVC C TGLGUVT KP UVC C ITCPKECFQNPCUVC C ITCPKECIÎTPC TGLGUVT PQV KP UVC C ITCPKECFQNPCUVC C ITCPKECIÎTPC Znacznik mo e zostać określony za pośrednictwem jednego z symboli wyliczonych w tabeli 1.2. Tabela 1.2. Symbole reprezentujące znaczniki Symbol Znacznik Znaczenie E Przeniesienie „Prawda” dla ustawionego (1) znacznika przeniesienia; „fałsz” dla wyzerowanego (0) znacznika przeniesienia. PE 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. P Brak zera „Prawda” dla wyzerowanego (0) znacznika zera; „fałsz” dla ustawionego (1) znacznika zera. Q Przepełnienie „Prawda” dla ustawionego (1) znacznika przepełnienia; „fałsz” dla wyzerowanego (0) znacznika przepełnienia. PQ Brak przepełnienia „Prawda” dla wyzerowanego (0) znacznika przepełnienia; „fałsz” dla ustawionego (1) znacznika przepełnienia. U Znak „Prawda” dla ustawionego (1) znacznika znaku; „fałsz” dla wyzerowanego (0) znacznika znaku. PU Brak znaku „Prawda” dla wyzerowanego (0) znacznika znaku; „fałsz” dla ustawionego (1) znacznika znaku. 1 Istnieje jeszcze kilka postaci dodatkowych; zostaną one omówione w dalszych podrozdziałach i rozdziałach.
  • 13.
    32 Asembler. Sztuka programowania Wykorzystanie tych znaczników w wyra eniach logicznych to ju dość zaawansowane programowanie w asemblerze. Sposób konstruowania wyra eń logicznych anga ujących znaczniki słowa stanu procesora zaprezentowany zostanie w następnym rozdziale. W przypadku, kiedy wartość logiczna anga uje operand rejestrowy, operand ten mo e odnosić 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 innej wartoś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 y jednak pamiętać, e w wyra eniu logicznym powinna występować zmienna typu logicz- nego (DQQNGCP). Uwikłanie w wyra eniu logicznym zmiennej innego typu spowoduje błąd. W przypadku potrzeby określenia wartości logicznej zmiennej typu innego ni DQQNGCP 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ślenie operatora relacji wraz z dwoma operandami. Listę dozwolonych kombinacji rodzajów operandó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 docelowym i stosować kombinacje rodzajów operandów takie jak dla instrukcji CFF czy UWD. Kolejna analogia operatorów relacji do instrukcji UWD i CFF objawia się w wymogu identycznego rozmiaru operandów. I tak, oba operandy muszą być albo bajtami, albo słowami, albo podwójnymi słowami. Jeśli operand prawy jest stałą, jego wartość musi nadawać się do zapisania w operandzie lewym. I jeszcze jedno: jeśli lewy operand jest rejestrem, a prawy to stała dodatnia albo inny rejestr, porównanie odbywa się bez uwzględnienia znaku operandów. Efekty takiego poró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.
  • 14.
    Rozdział 1. ♦Wstęp do języka asemblerowego 33 Operatory KP i PQV KP pozwalają na wykonanie testu zawierania się wartości prze- chowywanej w rejestrze w określonym zbiorze liczb. Na przykład wyra enie GCZ KP daje wartość logiczną VTWG, jeśli wartość przechowywana w rejestrze EAX to wartość z zakresu od 2000 do 2099 (włącznie). Operator PQV KP ma działanie odwrotne — da wartość logiczną VTWG jedynie wtedy, kiedy zawartość rejestru to liczba spoza określonego zbioru. Na przykład wyra enie CN PQV KP C będzie miało wartość VTWG tylko wtedy, kiedy znak przechowywany w rejestrze AL nie będzie małą literą alfabetu. Oto kilka przykładów wyra eń logicznych w języku HLA: E $QQNAXCT CN '5+ '#: '$: '$: K K CN K GCZ KP EJ PQV KP C 1.9.2. Instrukcje if..then..elseif..else..endif języka HLA Składnia instrukcji warunkowego wykonania kodu KH 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 KH 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ą VJGP. W przeciwnym przypadku sterowanie przekazane zostanie do kodu w ramach klauzuli GNUGKH bądź GNUG.
  • 15.
    34 Asembler. Sztuka programowania Poniewa klauzule GNUGKH i GNUG nie są obowiązkowe, instrukcja KH w najprostszej postaci składa się z wyra enia logicznego i pojedynczej klauzuli VJGP, za którą wystę- puje instrukcja lub blok instrukcji wykonywanych warunkowo, w zale ności od war- tości wyra enia logicznego: KH GCZ VJGP UVFQWVRWV DNCF YCTVQUE 07.. PN GPFKH Jeśli w trakcie wykonania programu wyra enie logiczne w instrukcji KH będzie miało wartość VTWG, wykonany zostanie kod znajdujący się pomiędzy słowami VJGP i GPFKH. W przypadku kiedy wyra enie logiczne będzie miało wartość HCNUG, kod ów zostanie pominięty. Inną powszechnie występującą formą instrukcji KH jest forma z pojedynczą klauzulą GNUG. Instrukcja taka wygląda następująco: KH GCZ VJGP UVFQWVRWV DNCF YCTVQEU 07.. YUMCPKMC PN GNUG UVFQWVRWV 2QRTCYPC YCTVQUE YUMCPKMC PN GPFKH Jeśli wyra enie logiczne przyjmie wartość VTWG, wykonany zostanie kod zawarty między słowami VJGP a GNUG. W przypadku wartości HCNUG sterowanie przekazane zostanie do kodu ograniczonego klauzulą GNUG i słowem GPFKH. Przez osadzanie opcjonalnych klauzul GNUGKH w instrukcji KH 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ępnych opcji menu, konstruując następującą instrukcję KH: KH EJ C VJGP UVFQWVRWV 9[DTCPQ RQ[ELG OGPW C PN GNUGKH EJ D VJGP UVFQWVRWV 9[DTCPQ RQ[ELG OGPW D PN GNUGKH EJ E UVFQWVRWV 9[DTCPQ RQ[ELG OGPW E PN GNUG UVFQWVRWV $NCF Y[DQT QRELK URQC OGPW PN GPFKH
  • 16.
    Rozdział 1. ♦Wstęp do języka asemblerowego 35 Choć nie widać tego w powy szym przykładzie, język HLA nie wymaga umieszczania po sekwencji klauzul GNUGKH klauzuli GNUG. Jednak przy podejmowaniu decyzji o wyko- naniu jednej z wielu ście ek kodu warto uzupełnić instrukcję KH klauzulą GNUG rozpo- czynającą ście kę kodu obsługi ewentualnych błędów wyboru. Nawet jeśli pozornie wykonanie owego kodu wydaje się niemo liwe, to nale y wziąć pod uwagę ewentualny rozwó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ę sytuacji wyjątkowych i niespodziewanych. 1.9.3. Iloczyn, suma i negacja w wyrażeniach logicznych Na liście operatorów zaprezentowanej w poprzednich punktach zabrakło bardzo wa nych operatorów logicznych: operatora iloczynu logicznego (AND), logicznej sumy (OR) oraz logicznej negacji (NOT). Ich zastosowanie w wyra eniach logicznych omówione zostanie tutaj — wcześniej omówienie takie nie miałoby racji bytu z uwagi na koniecz- ność poprzedzenia go omówieniem instrukcji warunkowego wykonania kodu KH. Bez mo 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 operator binarny 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: KH GCZ EJ C VJGP OQX GCZ GDZ OQX EJ GPFKH Obie instrukcje OQX zostaną wykonane jedynie wtedy, kiedy równocześnie rejestr EAX bę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 OQX 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. Oto kilka poprawnych logicznych z operatorem iloczynu logicznego: CN KP CN KP C GDZ DQQN8CT GCZ 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”, to operand prawy nie jest ju sprawdzany — wyra enie logiczne natychmiast otrzymuje wartość „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 zaprezentowanych wy ej wyra eń najpierw sprawdzona zostanie wartość logiczna zmiennej DQQN8CT; jeśli będzie to wartość HCNUG, test wartości logicznej rejestru EAX zostanie pominięty.
  • 17.
    36 Asembler. Sztuka programowania Nie sposób nie zauwa yć, e wyra enie z operatorem , np. GCZ GDZ GCZ, 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 enia z operatorem logicznym. Stąd poprawność następującego wyra enia: GCZ GDZ GCZ GEZ Operator iloczynu logicznego cechuje się łącznością lewostronną, co oznacza, e w kodzie wygenerowanym przez kompilator HLA ewaluacja (wartościowanie) wyra enia następuje od strony lewej do prawej. Jeśli rejestr EAX zawiera wartość większą od zera, pozostałe operandy nie będą sprawdzane. Analogicznie, jeśli EAX jest mniejszy od zera, ale EBX jest równy EAX, to sprawdzanie logicznej wartości wyra enia GEZ zostanie pominięte — całe wyra enie od razu otrzyma wartość HCNUG. Operator sumy logicznej w języku HLA reprezentowany jest znakami ^^. Podobnie, jak operator 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”, operator natychmiast zwraca wartość „prawda”, niezale nie od wartości drugiego operandu. Oto przykłady zastosowania operatora sumy logicznej: ^^ CN CN KP C ^^ GDZ DQQN8CT ^^ GCZ Podobnie jak operator operator logicznej sumy cechuje się łącznością lewostronną, więc w wyra eniach z wieloma operatorami ^^ ewaluacja przebiega od strony lewej do prawej. Na przykład: GCZ ^^ GDZ GCZ ^^ GEZ Jeśli powy sze wyra enie zostałoby osadzone w wyra eniu logicznym instrukcji KH, to kod po klauzuli VJGP 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ść VTWG, pozostałe warunki nie byłyby spraw- dzane. Gdyby pierwszy warunek miał wartość HCNUG, ale drugi VTWG, to etap szacowania wartości logicznej wyra enia GEZ zostałby pominięty. Test zawartości rejestru ECX nastą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: GCZ ^^ GDZ GCZ GEZ W interpretacji HLA powy szemu wyra eniu równowa ne jest wyra enie następujące: GCZ ^^ GDZ GCZ GEZ Jeśli rejestr EAX będzie miał wartość mniejszą od zera, reszta wyra enia nie będzie sprawdzana — całość wyra enia otrzyma wartość VTWG. Jeśli jednak rejestr EAX będzie
  • 18.
    Rozdział 1. ♦Wstęp do języka asemblerowego 37 zawierał wartość większą od zera, to aby całość wyra enia otrzymała wartość VTWG, oba operandy operatora iloczynu musiałyby mieć wartość VTWG. 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: GCZ ^^ GDZ GCZ GEZ 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 musi zawierać 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. GCZ ) — operator negacji nie mo e być bezpośrednio aplikowany do zło onych wyra eń logicznych. Aby wykonać logiczną negację wyra enia logicznego, nale y to wyra enie ująć w nawiasy i cały nawias opatrzyć przedrostkiem w postaci wykrzyknika: GCZ Powy sze wyra enie otrzymuje wartość VTWG 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ów bitowych i innych operatorów języka HLA znajduje się w dokumentacji kompilatora. 1.9.4. Instrukcja while Składnię instrukcji YJKNG ilustruje rysunek 1.11. Rysunek 1.11. Składnia instrukcji while języka HLA Wykonanie instrukcji YJKNG 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ą GPFYJKNG. W przypadku
  • 19.
    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ści logicznej 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 YJKNG sprawdzenie wartości warunku wykonania pętli następuje podobnie jak w językach wysokiego poziomu, czyli przed wykonaniem pierwszej instrukcji ciała pę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ść HCNUG. 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 mieli do czynienia z pętlą nieskończoną. Oto przykład pętli YJKNG w języku HLA: OQX K YJKNG K FQ UVFQWVRWV K K PN CFF K GPFYJKNG 1.9.5. Instrukcja for W języku HLA pętla for przybiera następującą postać: HQT Y[TC GPKGKPKELCNKWLæEG YCTWPGM KPUVTWMELCNKEPKMC FQ EKC QRúVNK GPFHQT Instrukcja pętli HQT odpowiada następującej pętli YJKNG: Y[TC GPKGKPKELCNKWLæEG YJKNG YCTWPGM FQ EKC QRúVNK KPUVTWMELCNKEPKMC GPFYJKNG 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 logiczne języka HLA w formacie takim jak dla instrukcji YJKNG. 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 instrukcji jest instrukcja w pętli YJKNG, której wykonanie zmienia wartość logiczną warunku wykonania pętli. Zwykle instrukcją tą jest instrukcja języka HLA, na przykład CFF.
  • 20.
    Rozdział 1. ♦Wstęp do języka asemblerowego 39 Oto przykład ilustrujący równowa ność pętli HQT i YJKNG: HQT OQX K K CFF K FQ UVFQWVRWV K K PN GPFHQT // Analogiczna pętla for: OQX K YJKNG K FQ UVFQWVRWV K K PN CFF K GPFYJKNG 1.9.6. Instrukcja repeat Instrukcja TGRGCVWPVKN języka HLA wykorzystuje składnię prezentowaną na rysunku 1.12. Programiści języków C, C++ oraz Java powinni od razu zauwa yć, e instrukcja ta jest odpowiednikiem dostępnej w tych językach instrukcji FQYJKNG. Rysunek 1.12. Składnia instrukcji repeat..until języka HLA Test warunku zatrzymania pętli TGRGCVWPVKN odbywa się po zakończeniu wykony- wania ciała pętli. Z tego względu instrukcja (bądź blok instrukcji) ciała pętli zostanie wykonana przynajmniej raz, niezale nie od wartości początkowej wyra enia warunku zatrzymania pętli. Warunek ten jest badany po raz pierwszy dopiero po zakończeniu pierwszej iteracji; jeśli wyra enie ma wartość HCNUG, pętla jest powtarzana2. Jeśli wa- runek otrzyma wartość VTWG, pętla jest zatrzymywana i sterowanie przekazywane jest do instrukcji znajdujących się za klauzulą WPVKN. Oto prosty przykład wykorzystania pętli TGRGCVWPVKN: OQX GEZ TGRGCV UVFQWVRWV GEZ GEZ PN UWD GEZ WPVKN GEZ 2 Znaczenie warunku jest więc w języku HLA odwrotne ni warunku w analogicznej konstrukcji FQYJKNG znanej z języków C, C++ i Java.
  • 21.
    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 YJKNG lepiej zastosować pętlę TG RGCVWPVKN — konstrukcja taka będzie efektywniejsza. 1.9.7. Instrukcje break oraz breakif Instrukcje DTGCM i DTGCMKH słu ą do przedwczesnego przekazywania sterowania poza pętlę. Składnię obu instrukcji ilustruje rysunek 1.13. Rysunek 1.13. Składnia instrukcji break oraz breakif języka HLA Instrukcja DTGCM powoduje bezwarunkowe przerwanie wykonywania pętli; instrukcja DTGCMKH uzale nia przekazanie sterowania poza pętlę od spełnienia warunku będącego wyra eniem logicznym. Wyjście z pętli następuje wtedy wyłącznie w obliczu wartości VTWG tego wyra enia. Zastosowanie instrukcji DTGCM oraz DTGCMKH nie pozwala na przekazanie sterowania poza pętle zagnie d one. W języku HLA słu ą do tego osobne instrukcje, jak DGIKPGPF oraz GZKV i GZKVKH. Szczegółowe informacje na ten temat zawiera dokumentacja języka HLA. Dalej, język HLA udostępnia instrukcje EQPVKPWG oraz EQPVKPWGKH pozwalające na przystąpienie do kolejnej iteracji pętli z pominięciem niewykonanych jeszcze instrukcji ciała pętli. Po szczegóły odsyłam Czytelników do stosownej dokumentacji. 1.9.8. Instrukcja forever Składnię konstrukcji HQTGXGTGPFHQT prezentuje rysunek 1.14. Rysunek 1.14. Składnia instrukcji forever języka HLA Instrukcja HQTGXGT tworzy pętlę nieskończoną. Wewnątrz takiej pętli mo na jednak umieścić instrukcje DTGCM bądź DTGCMKH celem przerwania pętli w wybranym momencie. To chyba najczęstszy układ pętli HQTGXGT. Ilustruje go następujący przykład: HQTGXGT UVFQWVRWV 9RTQYCF NKEDG ECNMQYKVC OPKGLUC QF UVFKPIGV K DTGCMKH K UVFQWVRWV 9RTQYCFQPC YCTVQUE OKCNC D[E OPKGLUC QF PN GPFHQT
  • 22.
    Rozdział 1. ♦Wstęp do języka asemblerowego 41 1.9.9. Instrukcje try, exception oraz endtry Instrukcje VT[GZEGRVKQPGPFVT[ słu ą do implementacji bardzo przydatnych bloków obsł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 VT[GPFVT[ słu y do ochrony wykonania instrukcji. Jeśli wykonanie instrukcji znajdujących się pomiędzy klauzulą VT[ a pierwszą klauzulą GZEGRVKQP, tworzących tzw. blok chroniony, przebiegnie bez niespodzianek, sterowanie przeka- zywane jest do pierwszej instrukcji za klauzulą GPFVT[. Jeśli jednak w czasie wyko- nywania instrukcji bloku chronionego zdarzy się błąd (wyjątek), wykonanie programu jest przerywane, a sterowanie przekazywane jest do odpowiedniej klauzuli GZEGRVKQP (skojarzonej z wyjątkiem, który wystąpił). Ka dy wyjątek ma unikalny identyfikator w postaci liczby całkowitej. Niektóre z identyfikatorów wyjątków zadeklarowane zostały w 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 system porównuje identyfikator zgłoszonego wyjątku z identyfikatorami określonymi w klauzu- lach GZEGRVKQP. Jeśli wśród nich znajduje odpowiedni identyfikator, następuje wykonanie instrukcji określonych w ramach klauzuli GZEGRVKQP. Po zakończeniu wykonywania tych instrukcji sterowanie przekazywane jest do pierwszej instrukcji za klauzulą GPFVT[. Jeśli wyjątek zostanie sprowokowany poza blokiem kodu chronionego (tzn. poza ramami instrukcji VT[GPFVT[) albo jeśli w ramach instrukcji VT[GPFVT[ brak jest klauzuli skojarzonej ze zgłoszonym wyjątkiem, program jest awaryjnie zatrzymywany. Zatrzy- maniu towarzyszy wyprowadzenie stosownego komunikatu na wyjście programu. Instrukcje VT[GPFVT[ mo na na przykład wykorzystać do ochrony programu przed skutkami wprowadzenia niepoprawnych danych:
  • 23.
    42 Asembler. Sztuka programowania TGRGCV OQX HCNUG )QQF+PVGIGT // Uwaga: zmienna GoodInteger musi być typu boolean VT[ UVFQWVRWV 9RTQYCF NKEDG ECNMQYKVC UVFKPIGV K OQX VTWG )QQF+PVGIGT GZEGRVKQP GZ%QPXGTUKQP'TTQT UVFQWVRWV 0KGRQRTCYPC YCTVQUE YRTQYCF LGUEG TC PN GZEGRVKQP GZ8CNWG1WV1H4CPIG UVFQWVRWV 9CTVQUE URQC CMTGUW YRTQYCF LGUEG TC PN GPFVT[ WPVKN )QQF+PVGIGT Zastosowana w powy szym programie pętla TGRGCV pozwala na wykonywanie kodu ciała pętli tak długo, jak długo u ytkownik podaje niepoprawne wartości. Jeśli z powodu wprowadzenia niepoprawnej wartości jej przypisanie do zmiennej )QQF+PVGIGT będzie niemo liwe (na przykład z racji umieszczenia we wprowadzonym niedozwolonych dla liczb 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 tu na wyświetleniu stosownego komunikatu i przekazaniu sterowania poza instrukcję VT[GPFVT[. Poniewa z racji wyjątku nie udało się w ramach bloku chronionego ustawić wartości VTWG dla zmiennej )QQF+PVGIGT, pętla jest wykonywana ponownie. Jeśliby jedna w 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 bibliotece standardowej wyjątków mo na znaleźć, zaglądając do pliku excepts.hhf dołączonego do posiadanej wersji oprogramowania kompilatora HLA. Większość z tych wyjątków dotyczy sytuacji, których omawianie w niniejszym rozdziale byłoby przedwczesne. Zostały umieszczone w tabeli wyłącznie gwoli kompletności omówienia. Ka dy z wyjątków opisany jest szczegółowo w dokumentacji HLA Reference Manual dokumentacji biblioteki standardowej języka HLA; ostateczną wykładnią jest zaś sam kod źródłowy biblioteki standardowej HLA. Najczęściej wykorzystywanymi wyjątkami są GZ%QPXGTUKQP'TTQT, GZ8CNWG1WV1H4CPIG oraz GZ5VTKPI1XGTHNQY. Omówienie bloku kodu chronionego podjęte zostanie ponownie nieco później. Czytelnik powinien kontynuować edukację w zakresie podstawowym, zadowalając się na razie szczątkowymi informacjami o obsłudze wyjątków. 3 Doświadczony programista zastanawia się zapewne, dlaczego w powy szym kodzie do sterowania przebiegiem pętli wykorzystywana jest zmienna typu DQQNGCP — przecie mo na by identyczny efekt osiągnąć, stosując instrukcję DTGCMKH. Zastosowana konstrukcja ma uzasadnienie, które zostanie przedstawione w dalszej części ksią ki.
  • 24.
    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 GZ5VTKPI1XGTHNQY Próba zapisania zbyt długiego łańcucha GZ5VTKPI+PFGZ'TTQT Próba odwołania do znaku o indeksie spoza łańcucha GZ8CNWG1WV1H4CPIG Zbyt wielka wartość dla danego typu zmiennej GZ+NNGICN%JCT Zaanga owanie w operacji znaku spoza dopuszczalnego zakresu (kod ASCI spoza zakresu od 0 do 127) GZ%QPXGTUKQP'TTQT Niedozwolone znaki w konwersji łańcucha do wartości numerycznej GZ$CF(KNG*CPFNGT Odwołanie do pliku za pośrednictwem niepoprawnego uchwytu pliku GZ(KNG1RGP(CKNWTG Niemo ność otwarcia pliku (np. brak pliku o zadanej nazwie) GZ(KNG%NQUG'TTQT Niemo ność zamknięcia pliku GZ(KNG9TKVG'TTQT Błąd przy zapisie danych do pliku GZ(KNG4GCF'TTQT Błąd przy odczycie danych z pliku GZKUM(WNN'TTQT Próba zapisania danych do pliku przy pełnym dysku GZ'PF1H(KNG Próba odczytania bajta spoza końca pliku GZ/GOQT[#NNQECVKQP(CKNWTG Brak pamięci do zaspokojenia ądania alokacji GZ#VVGORV6QGTGH07.. Próba odwołania do pamięci za pośrednictwem wskaźnika o wartości NULL GZ%CPPQV(TGG/GOQT[ Błąd przy operacji zwalniania pamięci GZ9KFVJ6QQ$KI Zły format konwersji wartości liczbowej do łańcucha GZ6QQ/CP[%OF.P2CTCOU Wiersz wywołania programu zawiera zbyt du ą liczbę argumentów GZ#TTC[5JCRG8KQNCVKQP Próba operowania na dwóch tablicach o ró nych rozmiarach GZ#TTC[$QWPFU Próba odwołania do elementu spoza tablicy GZ+PXCNKFCVG Operacja na niepoprawnej wartości daty GZ+PXCNKFCVG(QTOCV Błąd konwersji łańcucha do daty (łańcuch zawiera niedozwolone znaki) GZ6KOG1XGTHNQY Przepełnienie w operacji manipulowania informacją o czasie GZ#UUGTVKQP(CKNGF Błąd instrukcji asercji GZ'ZGEWVGF#DUVTCEV Próba wywołania metody klasy abstrakcyjnej GZ#EEGUU8KQNCVKQP Próba odwołania się do niedozwolonego adresu pamięci GZ$TGCM2QKPV Program dotarł do punktu zatrzymania (przerwanie 3.) GZ5KPING5VGR Program wykonywany jest z ustawionym znacznikiem wykonania krokowego GZ2TKX+PUVT Próba wykonania instrukcji charakterystycznej dla trybu nadzoru GZ+NNGICN+PUVT Próba wykonania niedozwolonej instrukcji maszynowe GZ$QWPF+PUVT Rejestr zawiera wartość spoza zakresu określonego w instrukcji DQWPF GZ+PVQ+PUVT Wykonanie instrukcji KPVQ przy ustawionym znaczniku przepełnienia GZKXKFG'TTQT Próba dzielenia przez zero bądź inny błąd dzielenia GZHGPQTOCN Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony obliczeniom arytmetycznym) GZHKX$[GTQ Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony obliczeniom arytmetycznym)
  • 25.
    44 Asembler. Sztuka programowania Tabela 1.4. Wyjątki zdefiniowane w pliku nagłówkowym excepts.hhf — ciąg dalszy Identyfikator wyjątku Znaczenie GZH+PGZCEV4GUWNV Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony obliczeniom arytmetycznym) GZH+PXCNKF1RGTCVKQP Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony obliczeniom arytmetycznym) GZH1XGTHNQY Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony obliczeniom arytmetycznym) GZH5VCEM%JGEM Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony obliczeniom arytmetycznym) GZH7PFGTHNQY Wyjątek operacji zmiennoprzecinkowej (patrz rozdział poświęcony obliczeniom arytmetycznym) GZ+PXCNKF*CPFNG System operacyjny odrzucił niepoprawny uchwyt GZ5VCEM1XGTHNQY System operacyjny zgłosił przepełnienie stosu 1.10. Biblioteka standardowa języka HLA — wprowadzenie Język HLA prezentuje się na tle tradycyjnego języka asemblerowego korzystnie dzięki dwóm aspektom. Pierwszym jest składnia języka HLA zapo yczająca elementy znane z 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 tym samym znacznie szybszą naukę języka asemblerowego. Drugim elementem stanowią- cym o przewadze HLA nad tradycyjnym asemblerem jest dostępność biblioteki HLA Standard Library. Biblioteka standardowa języka HLA zawiera wiele prostych w u yciu procedur języka asemblerowego realizujących typowe zadania programistyczne — ich dostę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ęzyk asemblerowy — trudność tkwiącą w konieczności samodzielnej implementacji zło onych niekiedy 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 operacji jak wyprowadzanie napisów. Jej dostępność oznacza więc, e adept języka asemblerowego mo e skoncentrować się na nauce samego języka, nie poświęcając się od samego początku zgłę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ńcu biblioteki kodu asemblerowego to nie nowy wynalazek4. Jednak biblioteka standardowa 4 Popularna jest na przykład biblioteka UCR Standard Library dla programistów języka asemblerowego procesorów 80x86.
  • 26.
    Rozdział 1. ♦Wstęp do języka asemblerowego 45 języka HLA to nie tylko sam kod procedur, ale równie wysokopoziomowy interfejs wywoł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ępne mo liwości. Biblioteka standardowa języka HLA składa się z szeregu modułów przypisanych do kilku 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 5 Z 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.
  • 27.
    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 UVFKQ. 1.10.1. Stałe predefiniowane w module stdio Omówienie modułu UVFKQ biblioteki standardowej nale ałoby rozpocząć od przedsta- wienia najczęściej wykorzystywanych stałych. Na przykład w następującym kodzie: UVFQWVRWV #JQL RT[IQFQ PN pojawia się stała PN reprezentująca sekwencję znaków przejścia do nowego wiersza. Identyfikator PN nie jest w kontekście programu HLA adnym słowem zarezerwowanym języka HLA; jego zastosowanie nie ogranicza się te do wywołania UVFQWVRWV. Otó PN to jedynie stała, której wartością jest literał łańcuchowy odpowiadający sekwencji znaków nowego wiersza (w systemie Windows sekwencja ta obejmuje znaki powrotu karetki i wysuwu wiersza; w Linuksie to pojedynczy znak wysuwu wiersza). Oprócz stałej PN implementacja podsystemu wejścia-wyjścia w bibliotece standardo- wej HLA zawiera definicje kilku innych przydatnych stałych znakowych. Zostały one wymienione w tabeli 1.6. Tabela 1.6. Stałe znakowe definiowane w module wejścia-wyjścia biblioteki standardowej HLA Stała Definicja UVFKQDGNN Znak dzwonka; wydrukowanie tego znaku objawia się wygenerowaniem dźwięku przez głośniczek systemowy UVFKQDU Znak cofania kursora UVFKQVCD Znak tabulacji UVFKQNH Znak wysuwu wiersza UVFKQET Znak powrotu karetki Z wyjątkiem PN wszystkie zaprezentowane tu stałe zdefiniowane są w przestrzeni nazw UVFKQ (stąd wymóg poprzedzania nazw stałych przedrostkiem UVFKQ)6. Osadzenie owych stałych w przestrzeni nazw UVFKQ pozwoliło na uniknięcie ewentualnych kon- fliktów z nazwami zmiennych definiowanymi przez programistę. Nazwa PN, jako u ywana wyjątkowo często, nie została osadzona w przestrzeni nazw UVFKQ — koniecz- ność ka dorazowego wpisywania UVFKQPN 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ą UVFKP bądź UVFQWV. Technicznie oznacza to, e nazwy tych procedur zdefiniowane zostały wewnątrz odpowiedniej przestrzeni nazw. W praktyce zastosowanie przedrostka stanowi rów- nie sugestię co do pochodzenia danych (dane pobierane ze standardowego urządzenia 6 Przestrzenie nazw omówione zostaną w jednym z kolejnych rozdziałów.
  • 28.
    Rozdział 1. ♦Wstęp do języka asemblerowego 47 wejściowego, UVFKP) bądź ich przeznaczenia (dane kierowane do standardowego urzą- dzenia wyjściowego, UVFQWV). 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 UVFKP bądź UVFQWV realizują odczyty i zapisy danych z i do urzą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 do urządzeń innych ni domyślne. Na przykład argument wywołania programu w postaci RNKMAY[ powoduje przekierowanie standardowego wyjścia programu do pliku plik_wy. Z kolei argument wywołania postaci RNKMAYG powoduje przypisanie standardowego wejścia programu do pliku źródłowego plik_we. Oto przykłady przekierowania stan- dardowego wejścia i wyjścia programu VGUVRIO uruchamianego z poziomu wiersza poleceń7: VGUVRIO KPRWVFCVC VGUVRIO QWVRWVVZV VGUVRIO KPVZV QWVVZV 1.10.3. Procedura stdout.newln Procedura UVFQWVPGYNP powoduje zapisanie do urządzenia standardowego wyjścia se- kwencji nowego wiersza. Wywołanie owej procedury jest równowa ne wywołaniu UVFQWVRWV PN . Wywołanie UVFQWVPGYNP jest jednak niekiedy nieco wygodniejsze bądź bardziej czytelne. Oto przykład wywołania procedury: UVFQWVPGYNP 1.10.4. Procedury stdout.putiN Procedury biblioteczne UVFQWVRWVK, UVFQWVRWVK oraz UVFQWVRWVK wyprowa- dzają na standardowe wyjście programu pojedynczą wartość, odpowiednio: 8-bitową, 16-bitową i 32-bitową, interpretowaną jako liczba całkowita ze znakiem. Argumentem wywołania mo e być stała, rejestr bądź zmienna w pamięci — rozmiar argumentu musi jednak 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 poprzedzony znakiem minusa. Oto kilka przykładów wywołań procedur UVFQWVRWVK0: UVFQWVRWVK UVFQWVRWVK FZ UVFQWVRWVK K8CT 7 Uwaga dla u ytkowników systemu Linux: w zale ności od wartości zmiennej środowiskowej 2#6* nazwę programu trzeba być mo e poprzedzić znakami „./”, np. VGUVRIO KPRWVFCVC.
  • 29.
    48 Asembler. Sztuka programowania 1.10.5. Procedury stdout.putiNSize Procedury UVFQWVRWVK5KG, UVFQWVRWVK5KG oraz UVFQWVRWVK5KG zapisują do standardowego urządzenia wyjściowego wartości interpretowane jako liczby całkowite ze znakiem — a więc działają podobnie jak procedury UVFQWVRWVK0. Tyle e procedury UVFQWVRWVK05KG 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ę argumentem wywołania procedury. Składnia wywołania procedur UVFQWVRWVK05KG jest następująca: UVFQWVRWVK5KG YCTVQ èDKV UGTQMQ è Y[RG PKGPKG UVFQWVRWVK5KG YCTVQ èDKV UGTQMQ è Y[RG PKGPKG UVFQWVRWVK5KG YCTVQ èDKV UGTQMQ è Y[RG PKGPKG W miejsce parametru YCTVQ è0DKV mo na przekazać stałą, nazwę rejestru albo nazwę zmiennej w pamięci — podany argument musi jednak odpowiadać rozmiarem rozmia- rowi parametru. Parametr UGTQMQ è mo e przyjmować argumenty w postaci stałych cał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. Argumentem parametru Y[RG PKGPKG powinien być pojedynczy znak. Podobnie jak procedury UVFQWVRWVK0 procedury UVFQWVRWVK05KG wyprowadzają na urządzenie standardowego wyjścia napis reprezentujący przekazaną w wywołaniu wartość liczbową interpretowaną jako liczbę całkowitą ze znakiem. W tej wersji programista mo 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ów w napisie jest mniejsza od zało onego minimum, brakujące znaki są w kodzie procedury uzupełniane znakami wypełnienia. Kiedy wyprowadzana wartość argumentu UGTQ MQ è jest liczbą ujemną, napis zostanie wyrównany w ramach wyprowadzanego pola do lewej; wartość dodatnia powoduje wyrównywanie napisów do prawej. Jeśli wartość bezwzględna parametru UGTQMQ è jest większa ni minimalna liczba znaków potrzebnych do dokładnego reprezentowania wyprowadzanej wartości, pro- cedury UVFQWVRWVK05KG uzupełniają napis znakami wypełnienia, dodając je z lewej bądź z prawej strony napisu (przed bądź za znakową reprezentacją liczby). Znak wy- pełnienia definiowany jest argumentem Y[RG PKGPKG. W zdecydowanej większości przypadków znakiem tym jest znak spacji, jednak w sytuacjach szczególnych konieczne mo e okazać się wypełnienie innym znakiem. Nale y tylko pamiętać, e argument pa- rametru Y[RG PKGPKG powinien być wartością znakową. W języku HLA stałe znakowe ujmowane 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 UVFQWVRWVK5KG w celu wyświetlenia listy wartości w formie tabelarycznej.
  • 30.
    Rozdział 1. ♦Wstęp do języka asemblerowego 49 Listing 1.4. Wyprowadzenie kolumn wartości liczbowych — zastosowanie procedury stdout.puti32Size RTQITCO 0WOU+P%QNWOPU KPENWFG UVFNKDJJH XCT K KPV %QN%PV KPV DGIKP 0WOU+P%QNWOPU OQX K OQX %QN%PV YJKNG K FQ KH %QN%PV VJGP UVFQWVPGYNP OQX %QN%PV GPFKH UVFQWVRWVK5KG K UWD K CFF %QN%PV GPFYJKNG UVFQWVPGYNP GPF 0WOUKP%QNWOPU 1.10.6. Procedura stdout.put Procedura UVFQWVRWV8 to najelastyczniejsza procedura wyprowadzania napisów na wyjście programu dostępna w module obsługi standardowego wyjścia. Procedura ta łączy funkcje 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 UVFQWVRWV prezentuje się następująco: UVFQWVRWV NKUVCY[RTQYCFCP[EJYCTVQ EK Lista argumentów wywołania procedury UVFQWVRWV mo e zostać konstruowana ze stałych, rejestrów i zmiennych; kolejne argumenty oddziela się przecinkami. Procedura wyprowadza na standardowe wyjście wartości wszystkich kolejnych argumentów wywo- łania. Jako e procedura ta była ju wielokrotnie prezentowana w przykładach, Czytelnik orientuje się, przynajmniej w zakresie podstawowym, co do sposobu jej wywoływania. 8 Tak naprawdę UVFQWVRWV 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.
  • 31.
    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 dy z argumentów wywołania mo e być zadany w jednej z dwóch postaci: YCTVQ è YCTVQ èUGTQMQ è 9CTVQ è mo e być dowolną stałą, rejestrem albo zmienną przechowywaną w pamięci. W niniejszym rozdziale prezentowane były argumenty w postaci literałów łańcuchowych i zmiennych w pamięci. Jak dotychczas wszystkie argumenty wywołania odpowiadały pierwszej 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 UVFQWVRWVK05KG9. Przykładowy program z listingu 1.5 generuje wydruk podobny do tego tworzonego programem 0WOU+P%QNWOPU, tyle e tutaj miejsce procedury UVFQWVRWVK05KG zajęła procedura UVFQWVRWV. Listing 1.5. Demonstracja sposobu określenia szerokości napisów w argumentach procedury stdout.put RTQITCO 0WOU+P%QNWOPU KPENWFG UVFNKDJJH XCT K KPV %QN%PV KPV DGIKP 0WOU+P%QNWOPU OQX K OQX %QN%PV YJKNG K FQ KH %QN%PV VJGP UVFQWVPGYNP OQX %QN%PV GPFKH UVFQWVRWV K UWD K CFF %QN%PV GPFYJKNG UVFQWVPGYNP GPF 0WOUKP%QNWOPU Procedura UVFQWVRWV 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. 9 W 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.
  • 32.
    Rozdział 1. ♦Wstęp do języka asemblerowego 51 1.10.7. Procedura stdin.getc Procedura UVFKPIGVE wczytuje pojedynczy znak z bufora urządzenia standardowego wejś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 RTQITCO EJCT+PRWV KPENWFG UVFNKDJHH XCT EQWPVGT KPV DGIKP EJCT+PRWV // Poni sza pętla wykonywana jest dopóty, // dopóki u ytkownik potwierdza wykonanie kolejnej iteracji. TGRGCV // Wyprowadź 14 wartości. OQX EQWPVGT YJKNG EQWPVGT FQ UVFQWVRWV EQWPVGT UWD EQWPVGT GPFYJKNG // Zaczekaj, a u ytkownik naciśnie 't' albo 'n'. UVFQWVRWV PN PN 9[MQPCE MQNGLPC KVGTCELG VP! HQTGXGT UVFKPTGCF.P UVFKPIGVE DTGCMKH CN P DTGCMKH CN V UVFQWVRWV 2TQUG YRTQYCFKE CNDQ V CNDQ P GPFHQT UVFQWVPGYNP WPVKN CN P GPF EJCT+PRWV 10 Bufor to w tym kontekście ładniejsza nazwa tablicy.
  • 33.
    52 Asembler. Sztuka programowania W powy szym programie wywołanie procedury UVFKPTGCF.P słu y do wymuszenia przejścia do nowego wiersza strumienia wejściowego. Szczegółowy opis tej procedury znajduje się nieco dalej, w niniejszym rozdziale. 1.10.8. Procedury stdin.getiN Procedury UVFKPIGVK, UVFKPIGVK oraz UVFKPIGVK realizują operację wczytania z urządzenia standardowego wejścia odpowiednio: 8-bitowej, 16-bitowej i 32-bitowej wartości liczbowej całkowitej. Wczytane wartości umieszczane są w rejestrach AL, AX bądź EAX. Niniejsze procedury stanowią standardowy sposób wczytywania liczb całko- witych ze znakiem do programów języka HLA. Podobnie jak procedura UVFKPIGVE, procedury UVFKPIGVK0 wczytują ze standardowego wejścia sekwencję znaków. W sekwencji tej ignorowane są wszelkie początkowe znaki odstę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 wprowadzona wartość nie mieści się w zakresie liczbowym charakterystycznym dla danej procedury, zgłaszany jest wyjątek (mo na go przechwycić, umieszczając wywołanie procedury w bloku kodu chronionego w instrukcji VT[GPFVT[). Procedura UVFKPIGVK obsługuje liczby z zakresu od –128 do 127, UVFKPIGVK — od –32 768 do 32 767; wartości wczytywane do programu za pośrednictwem procedury UVFKPIGVK 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 RTQITCO KPV+PRWV KPENWFG UVFNKDJJH XCT K KPV K KPV K KPV DGIKP KPV+PRWV // Pobierz od u ytkownika kilka liczb całkowitych ró nej wielkości. UVFQWVRWV 9RTQYCF PKGYKGNMC NKEDG ECNMQYKVC CMTGUW UVFKPIGVK OQX CN K UVFQWVRWV 9RTQYCF YKGMUC NKEDG ECNMQYKVC CMTGUW UVFKPIGVK OQX CZ K UVFQWVRWV 9RTQYCF NKEDG ECNMQYKVC CMTGUW QF FQ OKNKCTFQY UVFKPIGVK
  • 34.
    Rozdział 1. ♦Wstęp do języka asemblerowego 53 OQX GCZ K // Wyświetl podane wartości. UVFQWVRWV PN 1VQ YRTQYCFQPG NKED[ PN PN DKVQYC NKEDC ECNMQYKVC PN DKVQYC NKEDC ECNMQYKVC PN DKVQYC NKEDC ECNMQYKVC PN GPF KPV+PRWV 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 UVFKPIGVE czy UVFKPIGVK nie musi być to samy z mo- mentem wprowadzenia danych przez u ytkownika. Biblioteka standardowa języka HLA buforuje dane wprowadzane na standardowe wejście, wczytując jednorazowo cały wiersz tekstu wprowadzanego na to wejście. Wywołanie procedury wejścia mo e więc zostać 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 szym przykładzie: UVFQWVRWV 9RTQYCF PKGYKGNMC NKEDG ECNMQYKVC CMTGUW UVFKPIGVK OQX CN K UVFQWVRWV 9RTQYCF YKGMUC NKEDG ECNMQYKVC CMTGUW UVFKPIGVK OQX CZ K Programista ma nadzieję, e wykonanie powy szego kodu da następujący efekt: program wyś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ści program 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 wprowadzenie danych — procedura UVFKPIGVK odczyta po prostu drugi z wprowadzonych do bufora wejś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, procedury wejścia ograniczają się do odczytu z bufora. Mo na to wykorzystać, konstruując na- stępujący kod:
  • 35.
    54 Asembler. Sztuka programowania UVFQWVRWV 9RTQYCF FYKG NKED[ ECNMQYKVG UVFKPIGVK OQX GCZ KPVXCN UVFKPIGVK OQX GCZ #PQVJGT+PV8CN Tutaj zezwala się wprost u ytkownikowi na jednorazowe (w ramach pojedynczego wiersza) wprowadzenie dwóch liczb. Liczby te powinny zostać oddzielone jednym bądź kilkoma znakami odstępu. Pozwala to na zaoszczędzenie miejsca na ekranie. Tutaj buforowanie danych wejściowych okazało się korzystne. Kiedy indziej jednak mo e być odwrotnie. Na szczęście biblioteka standardowa języka HLA przewiduje dwie procedury sterujące działaniem bufora standardowego urządzenia wejściowego. Są to procedury UVFKPTGCF.P oraz UVFKPHNWUJ+PRWV. Pierwsza z nich usuwa z bufora wszystko, co zostało w nim umieszczone wcześniej, i tym samym wymusza wprowadzenie przez u ytkownika nowe- go wiersza danych. Druga po prostu usuwa całą zawartość bufora. Oczyszczenie bufora powoduje, e przy następnym wywołaniu dowolnej z procedur wejścia, u ytkownik będzie musiał wprowadzić nowy wiersz danych. Procedurę UVFKPTGCF.P wywołuje się zwykle bezpośrednio przez wywołaniem dowolnej procedury wejścia. Procedura UVFKPHNWUJ+PRWV wywoływana jest zaś typowo bezpośrednio po odczytaniu danych z bufora wybraną procedurą wejścia. Jeśli w programie wykorzystywana jest procedura UVFKPTGCF.P i okaże się, że konieczne jest dwukrotne wprowadzenie danych, to należy zastanowić się nad wyko- rzystaniem w jej miejsce procedury UVFKPHNWUJ+PRWV. 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 UVFKPTGCF.P 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 UVFKPIGV łączy w sobie przedstawione wcześniej procedury wejścia, udo- stępniając ich funkcje za pośrednictwem pojedynczego wywołania. Procedura UVFKPIGV jest przy tym nieco prostsza w u yciu ni UVFQWVRWV, 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: UVFQWVRWV 9RTQYCF FYKG NKED[ ECNMQYKVG UVFKPIGVK OQX GCZ KPVXCN UVFKPIGVK OQX GCZ #PQVJGT+PV8CN Równowa ny przykład wykorzystujący procedurę UVFKPIGV wyglądałby następująco: UVFQWVRWV 9RTQYCF FYKG NKED[ ECNMQYKVG UVFKPIGV KPVXCN #PQVJGT+PV8CN
  • 36.
    Rozdział 1. ♦Wstęp do języka asemblerowego 55 Jak widać, zastosowanie procedury UVFKPIGV mo e program skrócić i zwiększyć jego czytelność. Nale y przy tym pamiętać, e wywołanie procedury UVFKPIGV powoduje umieszczenie wczytanych wartości od razu we wskazanych zmiennych; adne z wczytanych wartości nie 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 nazwy rejestrów. 1.11. Jeszcze o ochronie wykonania kodu w bloku try..endtry Jak zapewne Czytelnik pamięta, instrukcje VT[GPFVT[ otaczają blok kodu, którego wykonanie 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 wykonaniem odpowiedniej instrukcji języka HLA. Programista mo e przechwytywać i obsługiwać sytuacje wyjątkowe, umieszczając stosowny kod w ramach klauzuli GZEGRVKQP. Typowy przykład zastosowania instrukcji VT[GPFVT[ ilustruje listing 1.8. Listing 1.8. Przykład zastosowania bloku try..endtry RTQITCO VGUV$CF+PRWV KPENWFG UVFNKDJJH UVCVKE W WPU DGIKP VGUV$CF+PRWV VT[ UVFQWVRWV 9RTQYCF NKEDG ECNMQYKVC DG PCMW UVFKPIGV W UVFQWVRWV 9RTQYCFQPQ W PN GZEGRVKQP GZ%QPXGTUKQP'TTQT UVFQWVRWV 9RTQYCFQP[ EKCI CYKGTC PKGFQYQNQPG PCMK PN GZEGRVKQP GZ8CNWG1WV1H4CPIG UVFQWVRWV 9RTQYCFQPC NKEDC LGUV D[V FWC PN GPFVT[ GPF VGUV$CF+PRWV
  • 37.
    56 Asembler. Sztuka programowania W języku HLA instrukcje znajdujące się za słowem VT[, a przed pierwszą klauzulą GZEGRVKQP noszą miano instrukcji chronionych. Jeśli podczas wykonywania takich instrukcji zgłoszony zostanie wyjątek, sterowanie zostanie przekazane do pierwszej klauzuli GZEGRVKQP skojarzonej z danym wyjątkiem, przeszukując kolejne klauzule pod ką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ść umieszczana w nawiasach klauzuli GZEGRVKQP powinna być wartością 32-bitową. Predefiniowane wartoś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 GZEGRVKQP dozwolone jest podawanie wprost wartości numerycznych wyjątków bez korzystania z predefiniowanych nazw stałych. 1.11.1. Zagnieżdżone bloki try..endtry Jeśli w wyniku przeszukania klauzul GZEGRVKQP nie nastąpi dopasowanie identyfikatora wyjątku, będzie miało miejsce przeszukanie klauzul nadrzędnego bloku VT[GPFVT[, 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 RTQITCO VGUV$CF+PRWV KPENWFG UVFNKDJJH UVCVKE W WPU DGIKP VGUV$CF+PRWV VT[ VT[ UVFQWVRWV 9RTQYCF NKEDG ECNMQYKVC DG PCMW UVFKPIGV W UVFQWVRWV 9RTQYCFQPQ W PN GZEGRVKQP GZ%QPXGTUKQP'TTQT UVFQWVRWV 9RTQYCFQP[ EKCI CYKGTC PKGFQYQNQPG PCMK PN GPFVT[ UVFQWVRWV $NCF PKG Y[PKMC RTGRGNPKGPKC CMTGUW PN GZEGRVKQP GZ8CNWG1WV1H4CPIG 11 W programie HLA identyfikator ten umieszczany jest w rejestrze EAX. Stąd po przekazaniu sterowania do odpowiedniej klauzuli GZEGRVKQP mo na odwoływać się do identyfikatora wyjątku, odwołując się do rejestru EAX.
  • 38.
    Rozdział 1. ♦Wstęp do języka asemblerowego 57 UVFQWVRWV 9RTQYCFQPC NKEDC LGUV D[V FWC PN GPFVT[ GPF VGUV$CF+PRWV Na listingu 1.9 widać zagnie d one dwa bloki instrukcji VT[GPFVT[. Jeśli w ramach wykonania instrukcji UVFKPIGV u ytkownik wprowadzi wartość większą od 4 miliardów z kawałkiem, procedura UVFKPIGV podniesie wyjątek GZ8CNWG1WV1H4CPIG. Kiedy wyjątek ten zostanie przekazany do systemu wykonawczego HLA, nastąpi przeszukanie klauzul GZEGRVKQP bie ącego bloku VT[GPFVT[ (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 GZ8CNWG1WV1H4CPIG w bie ącym bloku VT[GPFVT[, system wykonawczy HLA sprawdzi, czy bie ący blok VT[GPFVT[ nie został zagnie d ony w innym bloku VT[GPFVT[ (co akurat ma miejsce na listingu 1.9). W takim przypadku system wykonawczy HLA przeszuka równie klauzule GZEGRVKQP 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 za klauzulą GZEGRVKQP GZ8CNWG1WV1H4CPIG. Po opuszczeniu bloku VT[GPFVT[ system wykonawczy traktuje ów blok jako nieak- tywny i jeśli teraz nastąpi zgłoszenie wyjątku, system wykonawczy HLA nie będzie przeszukiwał klauzul GZEGRVKQP tego bloku12. Pozwala to na ró nicowanie procedur obsługi wyjątków w ró nych miejscach programu. Jeśli ten sam wyjątek obsługują dwa bloki VT[GPFVT[, a jeden z tych bloków jest zagnie d ony w bloku chronionym pierwszego, to w momencie zgłoszenia wyjątku podczas wykonywania wewnętrznego bloku chronionego system wykonawczy HLA przeka e sterowanie do kodu obsługi wyjątku w wewnętrznym bloku VT[GPFVT[. Kod obsługi tego samego wyjątku w zewnętrznym bloku jest ignorowany — HLA nie przekazuje sterowania do tego kodu. W przykładzie z listingu 1.9 druga instrukcja VT[GPFVT[ została statycznie zagnie - d ona w pierwszym bloku VT[GPFVT[13. Wcześniej wspomniano, e jeśli bie ący blok VT[GPFVT[ nie zawiera kodu obsługi zgłoszonego wyjątku, następuje przeszukanie klauzul GZEGRVKQP kolejnych bloków VT[GPFVT[, 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ętrza bloku VT[GPFVT[ do zupełnie innego miejsca programu, ni by to wynikało z samej struktury kodu. Jeśli w tamtym miejscu realizowany jest blok chroniony instrukcji VT[GPFVT[, 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 12 Chyba e sterowanie zostanie wcześniej przekazane z powrotem do bloku VT[GPFVT[, na przykład kiedy jest on umieszczony w ciele pętli. 13 Zagnie 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.
  • 39.
    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 procedury z bloku kodu chronionego mo e doprowadzić do dynamicznego zagnie d enia bloków VT[GPFVT[ — wystarczy, aby procedura równie zawierała blok VT[GPFVT[ (i aby został on wykonany). 1.11.2. Klauzula unprotected bloku try..endtry Wykonywanie instrukcji VT[ 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 GZEGRVKQP. Jeśli program przebrnie przez blok instrukcji chronionych, pierwotne środowisko wyjątków jest przywracane, a ste- rowanie przekazywane jest do pierwszej instrukcji znajdującej się za klauzulą GPFVT[. 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ątki powodowałyby przekazanie sterowania do opuszczonego ju bloku VT[GPFVT[. Problem ten ilustruje listing.1.10. Listing 1.10. Niepoprawne opuszczenie bloku instrukcji try..endtry RTQITCO VGUV$CF+PRWV KPENWFG UVFNKDJJH UVCVKE KPRWV WPU DGIKP VGUV$CF+PRWV // 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. HQTGXGT VT[ UVFQWVRWV 2QFCL NKEDG ECNMQYKVC UVFKPIGV KPRWV UVFQWVRWV 9RTQYCFQPQ KPRWV PN DTGCM GZEGRVKQP GZ8CNWG1WV1H4CPIG UVFQWVRWV .KEDC LGUV D[V FWC RQYVQT YRTQYCFCPKG PN GZEGRVKQP GZ%QPXGTUKQP'TTQT UVFQWVRWV %KCI CYKGTC PKGFQYQNQPG PCMK RQYVQT YRTQYCFCPKG PN GPFVT[ GPFHQT
  • 40.
    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 UVFQWVRWV 2QFCL LGUEG LGFPC NKEDG UVFKPIGV KPRWV UVFQWVRWV 0QYC NKEDC VQ KPRWV PN GPF VGUV$CF+PRWV 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ąca ponowne wprowadzenie danych przez u ytkownika, jeśli w poprzedniej próbie nie udało się ich poprawnie odczytać. Pomysł jest dobry; problem tkwi w jego nieprawi- dłowej realizacji. Otó instrukcja DTGCM powoduje opuszczenie pętli HQTGXGTGPFHQT bez przywrócenia poprzedniego stanu środowiska wyjątków. Z tego względu, kiedy program wykonuje procedurę UVFKPIGV umieszczoną poza pętlą (a tym bardziej poza blokiem 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, sterowanie zostanie przekazane z powrotem do stosownej klauzuli GZEGRVKQP opuszczonego ju przecie bloku VT[GPFVT[. Jeśli identyfikatorem wyjątku będzie GZ8CNWG1WV1H4CPIG albo GZ%QPXGTUKQP'TTQT, wyświetlony zostanie stosowny komunikat, a u ytkownik ponownie zmuszony będzie do wprowadzenia liczby. Tego zaś programista raczej nie przewidywał. Przekazanie sterowania do nieodpowiedniego bloku VT[GPFVT[ to tylko część pro- blemu. Kod z listingu 1.10 obarczony jest inną powa ną wadą, związaną ze sposobem zachowywania i następnie przywracania przez system wykonawczy HLA środowiska wyjątków. W szczególności bowiem HLA zachowuje informacje o stanie systemu wyjątków w specjalnym obszarze pamięci zwanym stosem. Jeśli po wyjściu z bloku VT[GPFVT[ 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 VT[GPFVT[ z pomi- nięciem etapu przywrócenia pierwotnego stanu środowiska wyjątków. Wiadomo, e sposób wprowadzania danych zaprezentowany na listingu 1.10 jest niepoprawny, ale z 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 VT[GPFVT[. Spójrzmy na kod z listingu 1.11. Listing 1.11. Blok pozbawiony ochrony wewnątrz bloku try..endtry RTQITCO VGUV$CF+PRWV KPENWFG UVFNKDJJH UVCVKE KPRWV WPU DGIKP VGUV$CF+PRWV
  • 41.
    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. HQTGXGT VT[ UVFQWVRWV 2QFCL NKEDG ECNMQYKVC UVFKPIGV KPRWV UVFQWVRWV 9RTQYCFQPQ KPRWV PN WPRTQVGEVGF DTGCM GZEGRVKQP GZ8CNWG1WV1H4CPIG UVFQWVRWV .KEDC LGUV D[V FWC RQYVQT YRTQYCFCPKG PN GZEGRVKQP GZ%QPXGTUKQP'TTQT UVFQWVRWV %KCI CYKGTC PKGFQYQNQPG PCMK RQYVQT YRTQYCFCPKG PN GPFVT[ GPFHQT // Uwaga: poni szy kod znajduje się poza pętlą i nie jest osadzony w bloku chronionym // instrukcji try..endtry UVFQWVRWV 2QFCL LGUEG LGFPC NKEDG UVFKPIGV KPRWV UVFQWVRWV 0QYC NKEDC VQ KPRWV PN GPF VGUV$CF+PRWV Osiągnięcie klauzuli WPRTQVGEVGF inicjuje operację przywrócenia pierwotnego stanu środowiska wyjątków. Oczywiste jest, e po przejściu do bloku kodu niechronionego wykonanie kodu nie jest ju chronione na wypadek zgłoszenia wyjątków. Klauzula WPRTQVGEVGF nie znosi jednak ochrony kodu realizowanej w dynamicznie zagnie d o- nych zewnętrznych blokach VT[GPFVT[ — klauzula WPRTQVGEVGF odnosi się jedynie do tego bloku VT[GPFVT[, w którym została umieszczona. Jako e na listingu 1.11 instrukcja przerywająca pętlę, DTGCM, znajduje się za klauzulą WPRTQVGEVGF, przekazanie sterowania poza pętlę odbywa się po uprzednim przywróceniu stanu wyjątków. Słowo WPRTQVGEVGF, jeśli ju występuje w ramach instrukcji VT[, powinno znajdować się bezpośrednio za blokiem kodu chronionego. Musi więc poprzedzać wszystkie klau- zule GZEGRVKQP.
  • 42.
    Rozdział 1. ♦Wstęp do języka asemblerowego 61 W obliczu zgłoszenia wyjątku system wykonawczy HLA automatycznie przywraca pierwotny stan środowiska wykonania. Z tego względu w ramach kodu obsługi wyjątku, po klauzuli GZEGRVKQP mo na swobodnie korzystać z instrukcji DTGCM, nie obawiając się o negatywne efekty charakterystyczne dla niepoprawnego opuszczenia bloku VT[GPFVT[. Z racji przywrócenia pierwotnego stanu wyjątków w bloku niechronionym oraz w kodzie obsługi wyjątku po klauzuli GZEGRVKQP, wystąpienie wyjątku w jednym z tych obszarów powoduje natychmiastowe przekazanie sterowania do zewnętrznego bloku VT[GPFVT[, w którym blok bie ący został dynamicznie zagnie d ony. W przypadku braku takiego bloku program jest awaryjnie przerywany, a na standardowe wyjście wyprowadzany jest stosowny komunikat o błędzie. 1.11.3. Klauzula anyexception bloku try..endtry Typowo instrukcji VT[ towarzyszy szereg klauzul GZEGRVKQP obsługujących wszelkie wyją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śnie zakończony. Jeśli programista jest autorem całości kodu bloku chronionego, powinien być zdolny do przewidzenia wszystkich sytuacji wyjątkowych związanych potencjalnie z wykonaniem bloku. Jeśli jednak w bloku tym znajdują się wywołanie procedury biblio- tecznej, wywołanie funkcji interfejsu systemu operacyjnego czy inne instrukcje, nie w pełni kontrolowane przez programistę, przewidzenie wszystkich mo liwych wyjątków mo e być niemo liwe. Tymczasem zgłoszenie wyjątku o identyfikatorze innym ni identyfikatory podane w kolejnych klauzulach GZEGRVKQP mo e doprowadzić do załamania programu. Szczęśliwie język HLA przewiduje umieszczenie w bloku VT[GPFVT[ klauzuli CP[GZEGRVKQP, do której dopasowywane są wszystkie wyjątki, których identy- fikatorów nie uda się odpasować do klauzul GZEGRVKQP. Klauzula CP[GZEGRVKQP nie ró ni się wiele od klauzuli GZEGRVKQP — jedyna odmienność tkwi w braku konieczności określania identyfikatora wyjątku (to oczywiste). Jeśli obok klauzul GZEGRVKQP w bloku VT[GPFVT[ znajduje się klauzula CP[GZEGRVKQP, powinna ona być ostatnią klauzulą obsługi wyjątków w bloku. Dopuszczalne jest naturalnie, aby klauzula CP[GZEGRVKQP była jedyną klauzulą obsługi wyjątków w bloku VT[GPFVT[. Po przekazaniu sterowania do klauzuli CP[GZEGRVKQP identyfikator wyjątku umiesz- czany jest w rejestrze EAX. Identyfikator ten mo na w ramach kodu obsługi wyjątku sprawdzać, dopasowując obsługę do przyczyny zgłoszenia wyjątku. 1.11.4. Instrukcja try..endtry i rejestry Ka dorazowe wkroczenie do bloku VT[GPFVT[ oznacza konieczność zachowania na stosie 16 bajtów informacji o stanie środowiska wyjątków. Przywrócenie środowiska wyjątków po opuszczeniu tego bloku (albo w wyniku osiągnięcia klauzuli WPRTQVGEVGF) 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 zmienia się w momencie zgłoszenia wyjątku w wyniku wykonania instrukcji kodu chronionego.
  • 43.
    62 Asembler. Sztuka programowania Po przejściu do klauzuli GZEGRVKQP 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 powinno się nawet zakładać, e rejestry ogólnego przeznaczenia zawierać będą po rozpoczęciu obsługi wyjątku wartości, które znajdowały się w nich pierwotnie. Kod generowany przez system 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 najmniej ryzykowne. 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 za klauzulą GPFVT[ zakłada obecność w rejestrach jakichś konkretnych wartości (np. wartości umieszczonych tam przed wkroczeniem do bloku kodu chronionego), nale y te wartości przywrócić samodzielnie. Zaniedbanie tego mo e doprowadzić do błędnego działania programu, przy czym błędy tego rodzaju są tym trudniejsze do wykrycia, e sytuacje 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: UVCVKE UWO KPV OQX UWO HQT OQX GDZ GDZ KPE GDZ FQ RWUJ GDZ // Zachowanie wartości rejestru EBX na wypadek wyjątku. HQTGXGT VT[ UVFKPIGVK WPRTQVGEVGF DTGCM GZEGRVKQP GZ%QPXGTUKQP'TTQT UVFQWVRWV 0KGFQYQNQPC YCTVQ è URTQDWL RQPQYPKG GPFVT[ GPFHQT RQR GDZ // Przywrócenie zawartości rejestru EBX. CFF GDZ GCZ CFF GCZ UWO GPFHQT 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 VT[ w roli zwykłej struktury sterującej wykonaniem programu (na przykład symulując w bloku VT[GPFVT[ działanie instrukcji wyboru znanych z języków wysokiego poziomu, jak ECUG czy UYKVEJ). 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.
  • 44.
    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 aktywacji omawiane są w rozdziale poświęconym procedurom). Standardowo programy HLA wykorzystują ó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 ten zostanie u yty w roli rejestru ogólnego przeznaczenia, na przykład w obliczeniach arytmetycznych, 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.