FORTH
W pierwszym odcinku prezentującym język FORTH poznaliśmy
dwa podstawowe słowa: ":" oraz ";"
(dwukropek i średnik). Znajomość
ich działania będzie nam obecnie
potrzebna, dlatego też radzę zajrzeć do poprzedniego numeru TA
Nie będę przedstawiał języka
FORTH w sposób chronologiczny.
Zwolenników tej metody odsyłam
do studiów na wydziale informatyki, gdzie pierwszy kontakt z językiem programowania zaczyna się
od poznania błędów, a kończy na
metodach poprawnego programowania. Postawmy przed sobą pierwsze praktyczne zadanie do rozwiązania w języku FORTH: Napisać
słowo odczytujące aktualny stan zegara. Nazwijmy je "ZEGAR@". Aby
rozwiązać ten problem, niezbędna
będzie znajomość podstawowych
operacji na liczbach.
W języku FORTH standardowa
liczba zajmuje dwa bajty. Jest to
więc liczba całkowita z zakresu
0-65535. W tym momencie musimy
wesprzeć ten wykład środkiem audiowizualnym, jakim jest komputer. Włączamy więc naszą ukochaną maszynkę i wczytujemy FORTH.
Gotowe? Jeśli tak, to napiszmy na
ekranie liczbę 3 i zatwierdźmy klawiszem RETURN. Pojawił się napis
OK, czyli w porządku. Co jednak
stało się z tą liczbą? Czy zginęła
w bezkresnych obszarach pamięci
naszego komputera? Napiszmy teraz "." (kropka), zatwierdzając jak
poprzednio klawiszem RETURN.
Napisana wcześniej liczba została
wyświetlona na ekranie. Stąd wniosek, że przebywała ona jakiś czas
w pamięci komputera. Czy było to
ściśle określone miejsce? Napiszmy
teraz kolejno trzy liczby:
1 RETURN
2 RETURN
3 RETURN
W miejscu gdzie widnieje znaczek RETURN należy nacisnąć taki
klawisz, nie należy tego przepisywać. Co stało się z tymi liczbami,
czy liczba następna skasowała poprzedną? Ano sprawdźmy,
piszemy:
. RETURN
. RETURN
. RETURN
Na ekranie pojawiły się kolejno:
3 2 1. Odzyskaliśmy więc wszystkie
wprowadzone liczby, ale od końca.
Wszyscy, którzy mieli do czynienia z ASEMBLEREM (a wśród czytelników TA na pewno takich nie
brakuje), znają ten sposób składowania liczb. Strukturą, która zapamiętała nasze liczby, jest STOS.
W języku FORTH stos pełni ważną
rolę "systemu nerwowego", bez którego zamarłby przepływ informacji
(danych) między poszczególnymi słowami. Zastępuje on w znacznej
części ZMIENNE wykorzystywane
do tęgo celu w innych językach.
CO TO JEST STOS?
Dla początkujących kilka słów wyjaśnienia, co to jest stos? Wyobraźmy
sobie, ze nasze liczby 1, 2, 3 zapiszemy na oddzielnych kartkach papieru
(mogą być gliniane tabliczki). Następnie kładziemy na stole kartkę z liczbą 1, na niej - z 2, a na końcu z liczbą 3. Tę stertę kartek nazywamy
STOSEM. Jeżeli chcemy odzyskać te
liczby, ściągamy je ze stosu. Na
wierzchołku mamy liczbę 3; po zdjęciu jej ze stosu uzyskujemy dostęp
do 2, a na końcu - 1. W taki to sposób postępował komputer ściągając
ze stosu 1 wyświetlając na ekranie
nasze liczby. Po co jednak komplikować sobie życie układając liczby jedną na drugiej? Można przecież rozłożyć kartki na całej powierzchni stołu,
mielibyśmy wtedy bezpośredni dostęp do każdej liczby. To samo można by wykonać w pamięci komputera. Zauważmy jednak, że składając
liczby na stos komputer nie potrzebował żadnej innej informacji oprócz
samej liczby. Chcąc umieścić liczbę
w dowolnym miejscu pamięci, musimy podać adres. Także podczas pobierania liczby należałoby określić
miejsce.
LICZBY...
Skoro znamy już zasadę funkcjonowania stosu, powróćmy do samych liczb. Wspomniałem, że zajmują one 2 bajty pamięci, a więc
jedna komórka stosu zajmuje tyle
samo, gdyż musi pomieścić całą
liczbę. Czy jest tak rzeczywiście?
Napiszemy teraz największą liczbę
dwubajtową, czyli:
65535 RETURN
Znanym już nam sposobem wywołujemy ją na ekran przy pomocy
sekwencji:
. RETURN
Ukazała się liczba: -1. Czyżby
komputer się pomylił ? Piszemy jeszcze raz:
65535 RETURN
Tym razem zamiast słowa "." (kropka) wpisujemy:
U. RETURN
Na ekranie widzimy wprowadzoną liczbę. Co się więc stało za pierwszym razem ? Słowo "." (kropka)
interpretuje liczbę na stosie jako
tzw. liczbę ze znakiem. Siódmy bit
bardziej znaczącego bajtu liczby
jest interpretowany jako znak minus. Dzięki temu uzyskujemy zakres od -32768 do 32767.
Dla początkujących wyjaśnię może, co to znaczy siódmy bit bardziej
znaczącego bajtu. Przedstawmy naszą liczbę w systemie dwójkowym,
a wszystko będzie jasne:
BZB MZB
11111111 11111111
7 bit bardziej znaczącego bajtu liczby zaznaczono strzałką. BZB to
bardziej znaczący bajt. MZB to
mniej znaczący bajt.
Nie będę na razie tłumaczył całego mechanizmu liczb ze znakiem,
należy jedynie pamiętać, że liczby
większe od 32767 będą traktowane
jako ujemne. Wykonajmy jeszcze
jedną próbę wprowadzania liczb na
stos; tym razem piszemy:
32768 RETURN
i wyświetlamy na ekranie:
. RETURN
Następnie wpisujemy:
32768 RETURN
i wyświetlamy:
U. RETURN
Mam nadzieję, że jest jasne dla
czytelników to, co się stało podczas
wykonywania tych działań.
Poznaliśmy dwa nowe słowa "."
i "U." (kropka i U kropka) wyświetlające liczby ze szczytu stosu
w dwóch różnych formatach. Przyjęło się, że litera U w nazwie oznacza interpretowanie danych na stosie jako liczby bez znaku.
DZIAŁANIA
Aby móc zrealizować zadanie postawione na początku artykułu,
musimy poznać podstawowe działania na liczbach. Są to: +, -, *, /.
Znamy je przecież dobrze ze szkoły. Tym, którzy do szkoły nie chodzili, przypominam, że są to odpowiednio: dodawanie, odejmowanie,
mnożenie i dzielenie. Chcąc je wypróbować, musimy poznać
ODWROTNĄ NOTACJĘ POLSKĄ
Wszystkim, którzy w tym momencie pragną porzucić dalszą lekturę
tego artykułu, radziłbym się nie
śpieszyć. Mam nadzieję, że uda mi
się objaśnić ten odstraszający termin. Zacznijmy może od stwierdzenia, że wszystkie słowa języka
FORTH (standardowe jak i stworzone przez nas) są równoprawne.
Można więc powiedzieć, że w języku FORTH panuje idealna demokracja. Poznane do tej pory słowa
"." lub "U." potrzebowały obecności
jednego parametru na stosie: była
to liczba zajmująca aktualny szczyt
stosu. Działania +, -, * i / są także
słowami FORTHa z tą różnicą, że
potrzebują dwóch parametrów (danych wejściowych). Wszystkim zapewne wiadomo, że operacja + jest
dokonywana na dwóch liczbach.
Ponieważ każde słowo języka
FORTH (wymagające parametrów
wejściowych) pobiera dane ze stosu, także działania matematyczne
szukają liczb na stosie. Uczono nas
w szkole, że do rozwiązania zadania są potrzebne dane wejściowe;
następnie wykonuje się zadanie i znajduje wartość szukaną. Logika
zapisu działań matematycznych wyniesiona ze szkoły jest jednak zupełnie inna. Przyjrzyjmy się zapisowi: 2+3. Najpierw piszemy pierwszą daną: 2, potem działanie,
które należy wykonać (ale jak wykonać
dodawanie na jednej danej?), i dopiero za działaniem znajduje się
druga potrzebna dana: 3. W języku
FORTH będzie to wyglądało następująco:
2 3 + RETURN
Pamiętać o spacjach! Działanie
przebiegło poprawnie i na ekranie
pojawił się napis OK. Co się jednak
stało z wynikiem? Nie został przecież wyświetlony na ekranie. Większość czytelników domyśla się zapewne,że wynik znajduje się na stosie. Sprawdźmy, pisząc:
. RETURN
Gdyby nie wyświetlać tej sumy na
ekranie, mogłaby ona posłużyć jako
dana wejściowa dla następnej operacji. Napiszmy więc jeszcze raz:
2 3 + RETURN
Mamy na stosie wyliczoną sumę,
dopiszmy jeszcze:
4 * RETURN
(czy wiesz, dlaczego jedna dana?)
i wyświetlamy wynik:
. RETURN
Zostało wykonane działanie, które
w szkolnym zapisie miałoby postać:
(2+3)*4
Widać wielką zaletę Odwrotnej
Notacji Polskiej, a mianowicie brak
nawiasów wymuszających pierwszeństwo dla danego działania.
Wszystkie działania w języku
FORTH są wykonywane zawsze
w kolejności zapisu. Czytelnikom
proponowałbym spróbować swych
sił w następującym przykładzie:
(1+2*3)+(1+2)*(2+3)
Proszę zapisać to wyrażenie w ję-
zyku FORTH i wyświetlić wynik.
Czytelnik, który pierwszy prześle
poprawny zapis na adres redakcji
otrzyma bezpłatnie grę: "A.D.2044".
Powróćmy jednak do naszego
głównego celu, a mianowicie stworzenia słowa ZEGAR@ (znak "@"
nazywa się po angielsku "at", czytaj: et; w Polsce bywa zwany robakiem,
embrionem, małpą). Niezbędne
okażą się nam jeszcze dwa bardzo
ważne słowa języka FORTH:
C@ i @
Pierwsze z nich umieszcza na
stosie zawartość komórki, której
adres znajdował się na stosie jako
dana wejściowa. Drugie słowo wykonuje to samo, lecz pobiera wartość z dwóch kolejnych komórek
pamięci, gdzie wartość spod wyższego adresu będzie traktowana jako starszy bajt liczby. Uwaga: słowa te interpretują oczywiście daną
wejściową (adres) jako liczbę bez
znaku. Wypróbujmy działanie tych
słów na komputerze. Piszemy:
82 C@ . RETURN
Na ekranie została wyświetlona
szerokość lewego marginesu. Spróbujmy jeszcze następującego zapisu:
88 @ . RETURN
Na ekranie pojawiła się liczba
określająca adres początku ekranu. A teraz ciekawostka: co wyświetli następująca sekwencja
słów:
88 @ C@ . RETURN
Przeważnie liczbę 0, gdyż została
pobrana wartość z pierwszej komórki ekranu, a tam znajdował się
kod ekranowy spacji. Poznaliśmy
do tej pory wszystkie słowa niezbędne do wykonania zadania postawionego na początku artykułu.
Musimy sobie jeszcze przypomnieć,
gdzie znajdują się komórki zegara.
Mają one adresy: 18, 19 i 20. Komórka 20 zwiększa swoją wartość
co 1/50 sekundy, komórka 19 - po
każdym przepełnieniu komórki 20,
a 18 - po przepełnieniu 19. Nasz
pierwszy zegar zbudujemy na
dwóch komórkach 19 i 20 . Nie
można wykorzystać słowa @ do pobrania zawartości tych komórek
(dlaczego?). Musimy zastosować
słowo C@ i pobrać wartości z każdej komórki oddzielnie. Piszemy
deklarację słowa ZEGAR@;
: ZEGAR@ 20 C@ 19 C@
256 * + ; RETURN
Wyjaśnię kolejne kroki naszego
postępowania:
: - służy do deklarowania słów (otwiera deklarację),
ZEGAR@ - taką nazwę będzie miało tworzone słowo.
Słowa znajdujące się między nazwą a średnikiem, zostaną zakodowane w pamięci i będą wykonane
dopiero po wywołaniu stworzonego
słowa. Będą one działały w następujący sposób:
20 C@ - pobierze wartość z komórki 20 i umieści ją na stosie,
19 C@ - to samo dla komórki 19,
256 * - wprowadzi na stos liczbę 256 i wykona mnożenie na dwóch
liczbach będących najbliżej szczytu
stosu. Będzie to zawartość komórki 19 i liczba 256.
+ - doda wyliczony iloczyn do wartości pobranej z komórki 20,
; - kończy deklarację nowego słowa.
Jeżeli kompilacja zadeklarowanego przez nas słowa przebiegła
prawidłowo, znajdzie się ono
w słowniku. Sprawdzamy:
VLIST RETURN
Teraz możemy sprawdzić jego
działanie:
ZEGAR@ . RETURN
i po chwili znów:
ZEGAR@ . RETURN
Liczby wyświetlone na ekranie
różnią się od siebie, a więc nasz zegar "chodzi". Mamy nowe słowo
w pamięci, lecz po wyłączeniu komputera tracimy je bezpowrotnie. Co
zrobić, aby tego uniknąć? Jeżeli posiadamy implementację Extended
fig-FORTH, sprawa jest prosta. Posiadacze magnetofonów piszą
CSAVE RETURN
i zapisują język FORTH rozbudowany o słowo ZEGAR@ na czystą
kasetę. Właściciele stacji dysków
robią to samo, lecz słowem
SAVE RETURN
Uwaga!: dyskietka musi być sformatowana w gęstości S lub E. Powinna być czysta, gdyż wszystkie
dane wcześniej zapisane (zwłaszcza w początkowych sektorach)
mogą ulec zniszczeniu.
Roland Pantoła
|