Tajemnice ATARI

FORTH część 9

    W tym odcinku podam pewne wiadomości związane z wykorzystaniem asemblera w języku FORTH. Znajomość działania samego FORTHa znacznie ułatwia pisanie programów w asemblerze, gdyż umożliwia pełne 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 FORTHa mogło to interesować. Chciałem jednak zadowolić także tych, którzy co nieco na temat FORTHa 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.

ASEMBLER, 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ą dwubajtowe. 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 FORTHa 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 liczby 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

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 FORTHa i usuwało parametry ze stosu?

    Możemy zastosować dwie metody. Pierwsza z nich polega na wykorzystaniu odpowiednich skoków powrotu do FORTHa: 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 FORTHa (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, 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 jedna 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 bardzo prosta rada: przed wykorzystaniem rejestru X należy przesłać jego zawartość do dowolnej komórki pamięci, a przed powrotem do FORTHa 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 przedstawianie asemblera FORTHa, 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 FORTHa 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ślenia kodu kontrolnego. Liczba ta musiała się składać jedynie z dwóch cyfr. Wprowadzenie układu trzydziestodwó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 poprzedniej 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 systemu szesnastkowego.

HEX RETURN
BASE ? RETURN

    Także teraz zobaczymy 10. Dlaczego tak się dzieje?

    Spróbujmy jeszcze 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ę pospieszyć.

    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
: K0 1 *K +! C@ *K @ * DUP EN +! + 1024 MOD ; 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 +! ." Wprowadź linie " ?L CR CR ; RETURN
: ?DOB DROP AF 6 + AF 32 CMOVE 32 AB +! WP 1 ; RETURN
: ?Z CR CR AF 1+ 37 TYPE 155 EMIT 28 EMIT 0 ; RETURN
: LL ." Teraz ma być 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 @ OYER MOD - DUP AB +! BLANKS ; RETURN
FORTH DEFINITIONS RETURN
: EDW EW El BEGIN AF 39 ERASE AF 1+ 37 EXPECT K0 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 PORTH ; RETURN

    A właściciele magnetofonów następujące linię:

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




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

Pixel 2002