Tajemnice ATARI

FORTH - część 4

    Na początku tego odcinka chciałbym zacytować fragment listu, jaki otrzymałem od pewnego właściciela Amigi. Była to odpowiedź na moje pytanie o język FORTH na ten komputer.

Zacytuję: Chciałem Pana poinformować, że języki FORTH, FORTRAN nie istnieją w wersji na Amigę. Myślę że nie ma sensu programować w języku FORTH, jeżeli on praktycznie wychodzi z użytku.

   Mam nadzieję, że użytkownicy komputerów ATARI XL/XE nie podzielają poglądów zawartych w tych słowach. Aby rozwiać całkowicie wszelkie wątpliwości, postanowiłem skomentować powyższy fragment listu.

   Po pierwsze:

   Języki FORTH i FORTRAN łączą cztery rzeczy: F, O, R i T. Poza tym, są to języki całkowicie odmienne.

   Po drugie:

   Sądzę, że język FORTH nie zdążył jeszcze dobrze wejść do użytku, aby miał już z niego wychodzić. Ostatni standard pochodzi z roku 1983 i trudno tu mówić o czymś przestarzałym. A swoją drogą: oczywiście, język FORTH istnieje w wersji na Amigę.

   Przystąpmy jednak do kolejnej odsłony, ukazującej tajniki naszego języka.

ZADANIE

   Przed poznaniem kolejnych słów postawmy przed sobą następujące zadanie: Napisać słowo o nazwie PAUSE, zatrzymujące program na ustalony czas. Parametrem wejściowym tego słowa będzie liczba określająca czas postoju. Większość czytelników domyśla się zapewne, że słowo to stworzymy w oparciu o wewnętrzny zegar naszego komputera.

PĘTLE

   Aby wykonać postawione zadanie, musimy poznać podstawową strukturę każdego języka programowania. Jest nią pętla.

   Zacznijmy od tej najprostszej, a przy tym - najmniej pożytecznej. Ma ona następującą postać:

BEGIN ... AGAIN

   Pętla ta wykonuje bez końca wszystkie słowa zawarte między BEGIN a AGAIN. Uwaga! Wszystkie pętle w języku FORTH muszą występować wewnątrz zadeklarowanego słowa. Niedopuszczanie jest użycie takiej pętli w trybie bezpośrednim, np:

BEGIN 1 . AGAIN RETURN

   Każda pętla musi mleć swój początek i koniec wewnątrz jednego słowa, np:

: PET BEGIN 1 . AGAIN ; RETURN

   Wykonajmy teraz nasze słowo pisząc:

PET RETURN

   Słowo to będzie się wykonywało w nieskończoność i nie pomoże nam naciskanie klawisza BREAK.

    Jedynym ratunkiem jest niezastąpiony RESET, po którym FORTH wróci do stanu początkowego.
   Następną pętlą, którą chciałbym przedstawić jest tzw. pętla liczona. Ma ona następującą postać:

n2 n1 DO ... LOOP

n1 - to liczba określająca wartość początkową licznika pętli, znajdująca się na szczycie stosu

n2 - wartość końcowa licznika pętli, znajdująca się na stosie pod n1

DO - otwiera pętle liczoną

... - tu znajdują się dowolne słowa wewnątrz pętli

LOOP - zamyka pętlę liczoną

   Oczywiście, pętla ta może występować tylko wewnątrz zadeklarowanego słowa. Przykład:

: PEL 20 0 DO 1 . LOOP ; RETURN

i sprawdzamy:

PEL RETURN

   Na ekranie ukazało się 20 jedynek. Aby uzyskać dostęp do licznika pętli wprowadzono słowo I. Wywołanie tego słowa (tylko wewnątrz pętli liczonej) zostawia na stosie aktualny stan licznika pętli. Przykład:

: PET 20 0 DO I . LOOP ; RETURN

i wywołujemy:

PET RETURN

   Na ekranie ukazały się liczby od 0 do 19, a nie do 20, jak byśmy się spodziewali.

   Pętla była wykonywana, dopóki licznik nie osiągnął wartości końcowej, czyli w naszym przypadku 20. Wartość licznika jest zwiększana w słowie LOOP, tam też jest sprawdzany warunek końcowy. W przypadku osiągnięcia przez licznik wartości końcowej pętla zakończy się.

   FORTH rozporządza także bardziej skomplikowaną odmianą pętli liczonej. Ma ona następującą postać:

n2 n1 DO ... n +LOOP

   n2 i n1 -to liczby na stosie mające identyczne znaczenie jak w poprzedniej pętli,

DO - otwiera pętlę

... - słowa wewnątrz pętli

n - liczba określająca skok licznika pętli. Musi zawsze występować przed +LOOP a może przyjmować dowolne wartości, także ujemne.

+LOOP - słowo kończące pętlę

   Podczas korzystania z tej pętli należy uważać, aby licznik zbliżał się do górnej granicy, a nie oddalał.

   Błędny byłby zapis:

: PEM 20 0 DO I . -1 +LOOP ;

   Wartość początkowa licznika wynosi O, a z każdym krokiem pętli licznik jest zwiększany o -1, czyli zmniejszany. Licznik zamiast zbliżać się do górnej granicy, oddala się od niej. Pętla ta nie zostanie nigdy zakończona.

   Poprawny będzie jednak następujący zapis:

: PEM 0 20 DO I . -1 +LOOP ; RETURN

   Wartością początkową jest 20, a końcową 0. Słowo to po wywołaniu wyświetli liczby od 20 do 1.

   Skoro poznaliśmy już pętle liczone, przeprowadźmy test sprawdzający szybkość języka FORTH na naszym komputerze.

   Deklarujemy słowo TEST:

: TEST 30000 0 DO LOOP ; RETURN

   Wywołując słowo ze stoperem w ręku, możemy określić czas jego wykonania. Gdy ukaże się napis OK, oznacza to zakończenie działania słowa.

   Aby uzyskać większą dokładność pomiaru czasu, możemy wykorzystać stworzone w poprzednich odcinkach słowa: OZEGAR! i ZEGAR@ .

   Piszemy sekwencję:

0ZEGAR! TEST ZEGAR@ . RETURN

   Na ekranie pojawiła się liczba, która podzielona przez 50 da nam czas w sekundach.

   Tak, to nie przywidzenie, pętla wykonywała się około 3 sekund. Dla porównania, podobna pętla (FOR NEXT) w języku ATARI BASIC jest liczona 58 sekund.

PORÓWNANIA

   Aby przedstawić inne rodzaje pętli, muszę zapoznać czytelników ze słowami wykonującymi porównania. Zacznijmy od słowa =

   Operator ten pobiera i porównuje dwie liczby będące najbliżej szczytu stosu. Na ich miejscu zostawia jedną liczbę będącą tzw. znacznikiem porównania. Znacznik ten przybiera wartość 0 w przypadku fałszu i 1 dla prawdy.

   W języku FORTH każda liczba niezerowa jest znacznikiem prawdy. Należy o tym pamiętać, gdyż jak przekonamy się dalej. Jako znacznik można wykorzystać wynik z dowolnego działania matematycznego.

   Kolejnymi słowami porównania będą operatory:

< - mniejszości
> - większości

   Słowa te działają na stosie bardzo podobnie do operatora równości, lecz zostawiany wskaźnik jest obliczany następująco:

   Dla < znacznik przybiera wartość 1 (czyli prawdy), gdy liczba znajdująca się niżej na stosie jest mniejsza od liczby będącej na jej szczycie.

Dla > znacznik wyniesie 1 (prawda), gdy liczba w głębi stosu jest większa od liczby na szczycie.

   Sprawdźmy to w praktyce pisząc na komputerze:

10 5 = . RETURN

   Otrzymamy wynik 0. Jeszcze raz:

10 10 = . RETURN

   Tym razem otrzymamy oczywiście l.

   Dla nierówności:

10 5 > . RETURN

   jest l, a dla

10 5 < . RETURN

   oczywiście 0.

   Zauważmy, że znaki porównania wystąpiły w zapisie za liczbami, a nie między nimi. Wynika to z zastosowanej notacji zapisu działania (tzw. Odwrotnej Notacji Polskiej).

   Poznane dotychczas porównania interpretują dane wejściowe jako liczby ze znakiem. Aby umożliwić porównanie liczb bez znaku, należy zastosować słowo U< . Działa ono podobne jak <, lecz nie uwzględnia znaku liczby. Pamiętajmy o tym, że każda liczba większa od 32767 jest interpretowana przez większość słów jako ujemna. Była o tym mowa w poprzednich odcinkach tego cyklu. Spróbujmy:

20000 45000 < . RETURN

   Otrzymaliśmy wynik 0, a powinno być 1. Tak, lecz słowo < interpretuje liczby jako ujemne o całkowicie innych wartościach.

   Sprawdźmy, jak widzi te liczby słowo <. Piszemy:

20000 . RETURN

i

45000 . RETURN

   Widzimy, że nasze słowo zachowało się całkowicie poprawnie.

   Ten sam warunek powinien wyglądać następująco:

20000 45000 U< . RETURN

   Teraz otrzymaliśmy wartość 1.

   Słowo U< znajduje szerokie zastosowanie przy porównywaniu adresów, które - jak wiemy - są liczbami dodatnimi.

   Operatory przedstawione poniżej wymagają tylko jednego parametru wejściowego. Są nimi:

0- i 0<

   Wykonują one porównanie liczby będącej na szczycie stosu z zerem.

   Operator 0= pobiera liczbę ze szczytu stosu i zostawia na jej miejsce znacznik równy 1 (prawda), gdy liczba równa się 0, a 0 (fałsz), gdy jest inaczej.

   Operator 0< zostawia na stosie znacznik prawdy (1) w przypadku, gdy liczba na stosie była ujemna.

   Słowo 0= pełni w języku FORTH także funkcję operatora NOT, znanego nam z innych języków.

   Umożliwia ono zamianę znacznika (prawda) 1 na (fałsz) 0 lub odwrotnie.

   Czytelnicy zauważyli zapewne, że nie ma wśród standardowych słów języka FORTH słowa U> lub 0>. Co zrobić w przypadku, gdy zaistnieje konieczność wykonania takiego porównania? Nie ma z tym żadnych kłopotów, gdyż FORTH jest językiem umożliwiającym samodzielne tworzenie brakujących słów. Przykład:

: 0> 1 - 0< 0= ;

   Zapewne zrozumiałe jest dla czytelników zastosowanie w prezentowanej deklaracji słowa 0<, a następnie zaprzeczenie znacznika przez 0=. Po co jednak odjęliśmy 1 od liczby na stosie? Gdyby opuścić tę operację, powstałoby następujące słowo:

: 0>= 0< 0= ;

   Słowo to zostawiałoby znacznik prawdy także w przypadku, gdy liczba ze szczytu stosu równałaby się 0.

   Mam nadzieję, że ze słowem U> czytelnicy poradzą sobie sami.

   Kończąc opisywanie porównań, chciałbym zwrócić uwagę, że porównania wymagają zawsze parametrów na stosie. Niedopuszczalny byłby zapis:

10 5 < =

   gdyż dla słowa = zabraknie jednego parametru. W takim przypadku należy zastosować:

10 5 > 0=

   Aby zwiększyć czytelność możemy stworzyć odpowiednie słowo:

: <= > 0= ; RETURN

i zapisać:

10 5 <=

PĘTLE, CIĄG DALSZY...

   Skoro poznaliśmy już operatory porównań, powróćmy do pętli. Kolejna z nich będzie miała postać:

BEGIN ... f UNTIL

BEGIN - początek pętli

... - jak w poprzednich przykładach

f - znacznik logiczny zostawiany na stosie

UNTIL - koniec pętli

   Pamiętajmy, że znacznik f musi być zostawiany na stosie po każdym przebiegu pętli, gdyż słowo UNTIL pobiera go ze stosu.

   Pętla ta jest wykonywana tak długo, aż znacznik f przybierze wartość różną od zera (prawda). Słowa będące wewnątrz pętli muszą ten znacznik odpowiednio modyfikować. Przykład:

: ES 255 764 C! BEGIN 764
C@ 28 = UNTIL ; RETURN

   Zadeklarowaliśmy słowo o nazwie ES. Co ono wykonuje? Sprawdźmy:

ES RETURN

   Nic się nie dzieje, dopiero naciśnięcie klawisza ESC powoduje opuszczenie słowa. Jedyną jego funkcją jest właśnie czekanie na naciśnięcie tego klawisza.

   Ostatnia z prezentowanych pętli ma postać:

BEGIN ... f WHILE ... REPEAT

BEGIN - początek pętli

... - ciąg słów

f - znacznik logiczny na stosie

WHILE - słowo pobierające i sprawdzające znacznik. Gdy jest on równy 0 (fałsz), pętla jest opuszczana (skok za słowo REPEAT)

REPEAT - kończy pętlę

   Zauważmy, że pętla ta różni się od poprzedniej, nie tylko odmiennym interpretowaniem znacznika, ale także dowolnością umieszczenia słowa WHILE sprawdzającego warunek. Jeżeli umieścimy WHILE na początku pętli (zaraz po słowie BEGIN), może ona nie być wykonana ani raz, podczas gdy BEGIN UNTIL musi się wykonać co najmniej raz. Przykład:

: AA 255 764 C! BEGIN 1 . 764 C@
255 = WHILE 2 . REPEAT ; RETURN

   Nowo stworzone słowo AA wyświetla kolejno pary liczb 1 i 2. Gdy naciśniemy dowolny klawisz (komórka 764 przyjmie wartość różną od 255), pętla zostanie opuszczona. W ostatnim przebiegu pętli, słowa znajdujące się po WHILE nie będą wykonane (liczba 2 nie zostanie wyświetlona).

JESZCZE O STOSIE

   Prezentowane w tej części artykułu słowa dotyczą stosu i nie mają w związku z tym odpowiedników w innych językach. Są to:

DUP

   Stowo to tworzy duplikat liczby znajdującej się na szczycie stosu i zostawia go nad tą liczbą, powiększając stos.

SWAP

   Zamienia między sobą dwie liczby znajdujące się najbliżej szczytu stosu.

DROP

   Usuwa liczbę ze stosu.

OVER

   Tworzy kopię liczby znajdującej się pod liczbą ze szczytu i umieszcza ją nad liczbą ze szczytu, powiększając odpowiednio stos. Liczby znajdujące się dotychczas na stosie nie ulegają zmianie.

ROT

   Ustawia trzy liczby znajdujące się najbliżej szczytu w ten sposób, że trzecią wyciąga na szczyt, a dwie pozostałe obniża o jedno miejsce.

   Przetestujmy działanie dwóch ostatnich słów, gdyż wydają się one najtrudniejsze do zrozumienia. Piszemy:

2 3 OVER . . . RETURN

   Uzyskaliśmy ciąg liczb: 2 3 2. Liczba 2 została skopiowana nad dotychczasowym szczytem stosu, czyli liczbą 3. Następny przykład:

1 2 3 ROT . . . RETURN

   Tym razem zobaczymy 132.

   Pamiętajmy, że szczyt stosu przy wprowadzaniu znajduje się po prawej stronie ciągu i jest nim liczba 3. Przy wyświetlaniu - odwrotnie, gdyż najpierw będzie pobrana liczba ze szczytu. Widzimy więc, że liczba 1 przeskoczyła z trzeciego miejsca na szczyt stosu.

   Jeżeli twój FORTH posiada słowo S. lub .S to możesz je stosować do przeglądania zawartości stosu.

REALIZACJA ZADANIA

   Skoro poznaliśmy już wszystkie niezbędne struktury języka FORTH, możemy przystąpić do rozwiązania problemu postawionego na początku artykułu.

   Do napisania słowa PAUSE wykorzystamy wcześniej stworzone słowa 0ZEGAR! i ZEGAR@. Były one opisane w poprzednich odcinkach tego cyklu. Więc do dzieła!

: PAUSE 0ZEGAR! BEGIN DUP
ZEGAR@ = UNTIL DROP ;

0ZEGAR! - zeruje stan zegara

BEGIN - otwiera pętlę

DUP - wykonuje duplikat parametru wejściowego

ZEGAR@ - zostawia na stosie stan zegara

= - porównuje wartość pobraną z komórek zegara z parametrem wejściowym

UNTIL - powoduje opuszczenie pętli, gdy znacznik logiczny pozostawiony na stosie przez = ma wartość niezerową (prawda)

   Nasze nowe słowo PAUSE wymaga zawsze parametru wejściowego, np.:

50 PAUSE RETURN

   Liczba występująca przed PAUSE określa czas działania słowa. Dla wartości równej 50 będzie on wynosił dokładnie 1 sekundę.

   Sposób realizacji słowa PAUSE, przedstawiony przeze mnie, nie jest najlepszy, gdyż ingeruje w stan zegara.

   Mam nadzieję, że czytelnicy potrafią sami rozwiązać ten problem w taki sposób, aby wartość komórek zegara nie ulegała zmianie.

   Proszę więc o wasze rozwiązania słowa PAUSE, oczywiście w języku FORTH. Najlepsze z nich zostaną zaprezentowane na łamach TA.

Roland Pantoła


Sprostowanie


   Do poprzedniego odcinka opowieści o języku FORTH wkradło się kilka nieścisłości.

   Rada dla posiadaczy magnetofonów (str. 24) okazała się nietrafna. Zapisanie FORTH-a ze zmienioną wartością WARNING nic nie da, ponieważ FORTH odnawia jej stan po wprowadzeniu go do komputera. Należy więc każdorazowo ustawiać ją ręcznie.

   Pod hasłem Działania pierwszy wiersz powinien wyglądać:

+ - * /    n1 n2 --- wynik

   Złośliwy chochlik "złamał" ten wiersz na dwie części (inny złośliwy chochlik dopisał literkę i, która oczywiście nie jest symbolem działania.

   Przepraszamy. Poszukiwania winnych trwają.

Redaktor



Powrót na start | Powrót do spisu treści | Powrót na stronę główną

Pixel 2001