1092 - Język FORTH cz.9

W tym odcinku podam pewne wiadomości związane z wykorzystaniem asemblera w języku FORTH. Znajomość działania samego FORTH-a znacznie ułatwia pisanie programów w asemblerze, gdyż umożliwia pełnie współdziałanie obu języków. Wiadomości te są niezbędne dla bardziej zaawansowanych programistów. Ostatnie odcinki z tego cyklu zawierały wiadomości o asemblerze. Zdaję sobie sprawę, że nie wszystkich sympatyków FORTH-a mogło to interesować. Chciałbym jednak zadowolić także tych, którzy co nieco na temat FORTH-a już wiedzą i chcieliby praktycznie go wykorzystać.

Wszystkich zawierających dopiero znajomość z Panem FORTH odsyłam do drugiej części artykułu, gdzie poznamy kolejne słowa naszego języka. Na końcu zamieszczam jeszcze raz listing Edytora Wprowadzania. Mam nadzieję, że tym razem żadna siła nie zmieni tego wydruku. Wszyscy, którzy wprowadzili do komputera poprzedni edytor z zamieszczonymi później poprawkami, mogą przy nim pozostać. Tym, którzy tego nie uczynili, radziłbym wklepać wydruk znajdujący się na końcu tego artykułu, gdyż unikniemy konieczności wprowadzania poprawek z poprzedniego numeru TA.

ASEMBELR, ciąg dalszy

Do tej pory w programach pisanych w asemblerze nie używaliśmy rejestru X. Rejestr ten pełni bardzo ważną rolę w języku FORTH, gdyż jest on tzw. wskaźnikiem stosu parametrów. Znając więc wartość rejestru X możemy odczytać liczbę będącą na szczycie stosu bezpośrednio z asemblera. Wystarczy wykorzystać rozkaz 0 .X LDA, aby liczbę ze stosu parametrów załadować do akumulatora. Widzimy jednak, że rozkaz ten pobrał jedynie jeden bajt, a przecież liczby w języku FORTH są dwu bajtowe. Aby pobrać starszy bajt liczby, należy wykonać rozkaz 1 ,X LDA, .

Co jednak uczynić, gdy chcemy przy pomocy stosu parametrów przekazać więcej niż jedną wartość ? Aby to wykonać, należy wiedzieć, że stos naszego FORTH-a rośnie w dół pamięci. Znając działanie stosu łatwo się domyślić, że liczba leżąca na stosie jako druga może być pobrana przy pomocy rozkazów :

 2 ,X LDA, - młodszy bajt
 3 ,X LDA, - starszy bajt

Sposób pobrania trzeciej liczby ze stosu nie powinien sprawić Czytelnikom żadnych trudności. Należy pamiętać, że w rzeczywistości nie ściągamy liczb ze stosu, lecz ją kopiujemy do akumulatora. Liczba nie zostaje usunięta ze stosu. Przykład :

 0 VARIABLE WA RETURN
 CODE >WA 0 ,X LDA, WA STA, 1 ,X LDA, WA 1+
 STA, NEXT JMP, C; RETURN

Wykonujemy :

 257 >WA RETURN

i sprawdzamy wartość na stosie :

 WA ? RETURN
 . RETURN

Widzimy, że wprawdzie słowo >WA przekazało liczbę 257 do zmiennej WA, lecz nie usunęło jej ze stosu. Co zrobić, aby słowo napisane w asemblerze działało identycznie jak słowa FORTH-a i usuwało parametry ze stosu ?

Możemy zastosować dwie metody. Pierwsza z nich polega na wykorzystaniu odpowiednich skoków powrotu DO FORTH-a : POP oraz POPTWO. Pierwszy z nich usuwa ze stosu jeden parametr, a drugi dwa. Należy je stosować w miejsce skoku NEXT używanego we wcześniejszych przykładach. Sprawdzamy :

 SP! RETURN
 0 VARIABLE W1 RETURN
 CODE >W1 0 ,X LDA, W1 STA, 1 ,X LDA, W1 1+
 STA, POP JMP, C; RETURN
 257 >W1 RETURN
 W1 ? RETURN
 . RETURN

SP! - to słowo czyszczące stos. Tym razem na stosie nie została żadna liczba, gdyż nowa procedura powrotu do FORTH-a (POP) usunęła ją ze stosu. Co zrobić, gdy napiszemy w asemblerze słowo posiadające trzy parametry wejściowe ?

Należy wtedy zastosować inną, bezpośrednią metodę usuwania parametrów, a mianowicie wykorzystanie rozkazu INX przed opuszczeniem słowa. Aby to lepiej zobrazować, napiszmy nowe słowo wykonujące to samo zadanie co W1, w innej formie.

 0 VARIABLE W2 RETURN
 CODE >W2 0 ,X LDA, W2 STA, 1 ,X LDA, W2 1+
 STA, INX, NEXT JMP, C; RETURN

Tym razem zamiast procedury powrotu POP zastosowaliśmy rozkazy INX, działające bezpośrednio na rejestr X. Zauważmy, że bardziej logiczne wydaje się zastosowanie rozkazu DEX, do obniżenia stosu. Musimy jednak pamiętać, że stos rośnie w kierunku niższych komórek i w związku z tym zmniejszenie stosu nastąpi w momencie zwiększenia adresu szczytu.

Korzystając z drugiej metody usuwania parametrów należy zachować szczególną ostrożność, gdyż metoda ta nie sprawdza zawartości stosu, co może doprowadzić do zawieszenia systemu. Najlepszym wyjściem wydaje się korzystanie z metody pierwszej, a dopiero w przypadku trzech parametrów (i więcej) - dodanie metody drugiej.

Na koniec jeszcze jednak ważna sprawa. Wydaje się, że stosowanie rejestru X bezpośrednio w języku FORTH uniemożliwia zastosowanie go w asemblerze. Jest jednak na to prosta rada: przed wykorzystaniem rejestru X należy przesłać jego zawartość do dowolnej komórki pamięci, a przed powrotem do FORTH-a odtworzyć jego wartość.

Twórcy implementacji Extended fig-FORTH przewidzieli do tego celu zmienną XSAVE. Jeżeli nasze słowo wykorzystuje rejestr X, to pierwszym jego rozkazem musi być XSAVE STX, a jednym z ostatnich XSAVE LDX, .

FORTH - operatory logiczne

Ponieważ zakończyłem przedstawienie asemblera FORTH-a, chciałbym w tej chwili zająć się słowami umożliwiającymi działanie na najmniejszych wycinkach pamięci. Język FORTH, podobnie jak C, zaliczany jest do języków asemblerowych. Języki te posiadają rozkazy umożliwiające wykorzystanie możliwości dostępnych zwykle tylko z asemblera. Do takich słów w języku FORTH można zaliczyć bitowe operatory logiczne.

Aby nie mylić ich z operatorami wykorzystywanymi przy określaniu znacznika prawdy (np. przed IF) użyłem określenia "bitowe". Operatorami tymi są słowa : AND. OR. XOR.

Jak już wspomniałem wcześniej, nie należy ich stosować do określania znacznika prawdy, gdyż do tego wykorzystujemy: "*" jako and (1) oraz "+" jako or (lub).

Natomiast bitowe operatory logiczne mają tę właściwość, że przetwarzają bity dwóch liczb na szczycie stosu i zastępują je odpowiednim wynikiem operacji logicznej.

Poniżej przedstawiam tabelę działania na dwóch bitach BIT1 i BIT2 odpowiednich operatorów :

BIT1 BIT2 AND OR XOR
0 0 0 0 0
1 0 0 1 1
0 1 0 1 1
1 1 1 1 0
  • AND - jest logicznym odpowiednikiem spójnika "i" (koniunkcja),
  • OR - jest odpowiednikiem "lub" (alternatywa),
  • XOR - odpowiada łącznikowi "albo"

Działanie tych operatorów na liczbach może się wydawać początkującym programistom nie bardzo zrozumiałe. Przykład :

 9 12 AND . RETURN

Jako wynik otrzymaliśmy liczbę 8. Jakie dziwne działanie zostało wykonane i jak został obliczony taki wynik ?

Wszystko stanie się jasne, gdy przejdziemy na system dwójkowy. Aby tego dokonać, musimy poznać metody zamiany układu liczbowego. FORTH posiada prosty system przejścia z jednego układu do drugiego przez wywołanie odpowiedniego słowa.

Należy pamiętać, że wywołany układ liczbowy pozostaje aż do momentu wywołania innego.

Słowo DECIMAL ustawia FORTH-a na dziesiętny system liczbowy, a HEX - na szesnastkowy. W naszym przypadku (zamiany na system dwójkowy) nie możemy ich wykorzystać. Ale FORTH posiada możliwości korzystania z dowolnego układu liczbowego.

Za podstawę aktualnego systemu liczbowego odpowiada zmienna o nazwie BASE . Jeżeli w tę zmienną wpiszemy np. liczbę 2, to FORTH przejdzie na system dwójkowy. Aby ułatwić sobie zadanie z ciągłym wpisywaniem liczby 2, stwórzmy słowo BIN wykonujące tę operację.

 : BIN 2 BASE ! ; RETURN

Teraz po każdym wywołaniu słowa BIN działamy w systemie binarnym.

Zamiana układu liczbowego na dowolny system może znacznie uprościć program. Na przykład, gdy pisałem Edytor Wprowadzania, chciałem zastosować możliwie dużą liczbę do określania kodu kontrolnego. Liczba ta musiała się składać jedynie z dwóch cyfr. Wprowadzenie układu trzydziesto-dwójkowego znacznie uprościło to zadanie.

Skoro mamy już niezbędne narzędzie, powróćmy do przykładu działania bitowych operatorów logicznych. Przykład :

 BIN RETURN
 1001 RETURN
 1100 RETURN
 AND CR . RETURN

Cały przykład został napisany w słupku, aby była możliwość porównania bitów na odpowiednich miejscach. Jeśli teraz zajrzymy do odpowiedniej tabelki, to wszystko będzie jasne. Początkującym programistom radziłbym wypróbować tą metodą wszystkie operatory.

Do czego mogą one być wykorzystane ? Zastosowań jest wiele, np. porównując przy pomocy operatora AND liczbę, mającą ustawiony 1 wybrany bit, z komórką joysticka (patrz Mapa Pamięci w TA 5), otrzymujemy znacznik prawdy dla odpowiedniego położenia drążka.

Na zakończenie pobawmy się jeszcze w zmiany systemu liczbowego. Napiszmy :

 DECIMAL RETURN
 BASE ? RETURN

Na ekranie pojawiło się 10, czyli wszystko w porządku, gdyż w zmiennej BASE powinna znajdować się taka liczba dla systemu dziesiętnego. Spróbujmy jeszcze raz dla szesnastkowego.

 HEX RETURN
 BASE ? RETURN

Także teraz zobaczymy 10. Dlaczego tak się dzieje ? Spróbujmy jeszcze raz wpisać bezpośrednio :

 5 BASE ! RETURN
 BASE ? RETURN

Nic się nie zmieniło, dalej jest 10. Może zmienna BASE znajduje się w pamięci ROM, gdyż jej wartość nie uległa zmianie ? A może jednak nie ? Czytelnik, który pierwszy przyśle rozwiązanie powyższego problemu, otrzyma dowolnie wybraną grę firmy AVALON. Zagadka jest bardzo prosta, więc radzę się pośpieszyć.

A oto wspomniany listing. Właściciele magnetofonów powinni najpierw wprowadzić :

 0 WARNING ! RETURN

--- * --- * ---

 ( EDYTOR WPROWADZANIA ) RETURN
 : <=< OVER > >R < R> * ; RETURN
 VOCABULARY EW IMMEDIATE RETURN
 EW DEFINITIONS RETURN
 37000 CONSTANT AD0 RETURN
 0 VARIABLE AB RETURN
 0 VARIABLE *K RETURN
 0 VARIABLE LIN RETURN
 0 VARIABLE EN RETURN
 : AF AD0 AB @ + ; RETURN
 : B32 32 BASE ! ; RETURN
 : NK IF 0 0 ELSE B32 AF NUMBER DECIMAL 1024 U/ ENDIF ; RETURN
 : N1 0 AF COUNT OVER + SWAP ; RETURN
 : NU N1 DO I C@ 47 OVER 58 <=< 64 ROT 87 <=< + 0= + LOOP NK ; RETURN
 : K1 DO I K0 I C@ 0= IF I 38 BLANKS LEAVE ENDIF LOOP ; RETURN
 : KP 0 *K ! 4 AF C! 32 AF 5 + C! NU 0 EN ! ; RETURN
 : KOD= KP >R >R 0 AF 38 + AF 6 + K1 R> = R> LIN @ = SWAP OVER * ; RETURN
 : .R2 0 <# # # #> TYPE SPACE ; RETURN
 : ?L LIN @ B32 .R2 DECIMAL ; RETURN
 : WP 125 EMIT 1 LIN +! ."Wprowadz linie " ?L CR CR ; RETURN
 : ?DOB DROP AF 6 + AF 32 CMOVE 32 AB +1 WP 1 ; RETURN
 : ?Z CR CR AF 1+ 37 TYPE 155 EMIT 28 EMIT 0 ; RETURN
 : LL ." Teraz ma byc linia " ?L ; RETURN
 : ?ZLE 125 EMIT 0= IF LL ELSE ."Popraw:" ENDIF ?Z ; RETURN
 : KO KOD= IF ?DOB ELSE ?ZLE ENDIF EN @ 1442 = * ; RETURN
 : GO 125 EMIT CR ." GOTOWE!" CR ; RETURN
 : E1 1 82 C! 0 AB ! 0 LIN ! WP ; RETURN
 : BBL AF 1024 AB @ OVER MOD - DUP AB +! BLANKS ; RETURN
 FORTH DEFINITIONS RETURN
 : EDW EW E1 BEGIN AF 39 ERASE AF 1+ 37 EXPECT KO UNTIL BBL GO FORTH ; RETURN
 
Właściciele stacji dysków wpisują :
: BSAV OVER + SWAP DO DUP I 0 R/W B/BUF + LOOP DROP ; RETURN : EDW-SAVE EW AD0 SWAP B/SCR * AB @ B/BUF / BSAV FORTH ; RETURN
A właściciele magnetofonów następujące linie :
1 VARIABLE CG RETURN : --> 0 CG ! ; RETURN : BF< 16 8 DO DUP I 8 - B/BUF * + I BUFFER B/BUF CMOVE LOOP DROP ; RETURN : EL 0 WARNING ! EMPTY-BUFFERS BF< 1 CG ! 1 LOAD ; RETURN : EDW-COMP EW AD0 >R BEGIN R EL R> 1024 + >R CG @ UNTIL R> DROP FORTH ; RETURN

Roland Pantoła
Tajemnice Atari Nr.10/1992r.