Tajemnice ATARI

FORTH część 8

    W numerze 2 Tajemnic Atari ogłosiłem mały konkurs na zapis pewnego wyrażenia w języku FORTH. Ponieważ konkurs został już rozstrzygnięty, jestem winien czytelnikom przedstawienie zwycięzcy. Został nim Grzegorz Wysocki z Warszawy. Prawidłowy zapis powinien wyglądać następująco:

2 3 * 1 + 1 2 + 2 3 + * + . RETURN

    Gratuluję wygranej i dziękuję czytelnikom za liczny udział w konkursie. W niedługim czasie zajmiemy się tworzeniem nowych słów w języku FORTH. Mam nadzieję, że wielu czytelników przedstawi swoje własne rozwiązania. Aby tego dokonać należy dołączyć do posiadanej wersji Extended fig FORTH, programy zamieszczone w 6/7 i 8 numerze Tajemnic Atari.

    Niestety, w zamieszczonym wcześniej listingu edytora wprowadzania jakaś tajemna siła usunęła dwa słowa, co uniemożliwia uruchomienie tego programu. Słowo K0 zadeklarowane wewnątrz edytora powinno wyglądać następująco:

: K0 1 *K +! C@ *K @ * DUP EN +! + 1024 MOD ; RETURN

    Dla właścicieli magnetofonów zmieniamy ostatnią deklarację:

: EDW-COMP AD0 0 WARNING ! BEGIN EL 1024 + CD @ UNTIL DROP ; RETURN

    Po wprowadzeniu (kompilacji) całego edytora musimy wpisać jeszcze następującą sekwencję:

71 ' E1 6 - C! RETURN

    Także w zamieszczonym fragmencie próbnym zmieniamy linię z deklaracją słowa ZEGAR@:

04O1 : ZEGAR@ 20 C@ 19 C@ 256 * + ; RETURN

    Za powstałe błędy przepraszam wszystkich wielbicieli FORTHa.

ASEMBLER, ciąg dalszy

    W tym odcinku będziemy w dalszym ciągu poznawać możliwości asemblera FORTHa. Przypominam, że listing tego programu został zamieszczony w poprzednim numerze Tajemnic Atari. Asembler FORTHa zajmuje około 1.5 kB pamięci, a mimo to w połączeniu ze słowami FORTHa daje programiście duże możliwości działania.

    Wszystkie asemblery procesora 6502 mają w swoim zestawie instrukcje skoków warunkowych takich jak BCC, BCS, BNE, BEQ, BPL, BMI. Nasz asembler nie posiada takich słów w swoim zestawie. W jaki więc sposób wykonać skoki warunkowe, będące postawą programowania?

Odgałęzienia

    Otrzymujemy do dyspozycji struktury zapożyczone z języków wyższego rzędu.

    Pierwsza z nich to:

IF, ... ELSE, ... THEN,

    Każde z prezentowanych słów ma na końcu nazwy przecinek, aby nie zostało pomylone przez kompilator ze słowami FORTHa o podobnej nazwie. Kropkami zostały oznaczone miejsca, w których możemy umieścić dowolne rozkazy asemblera. Słowo IF, (podobnie jak w języku FORTH) sprawdza warunek i jeżeli jest on spełniony wykonywane są rozkazy znajdujące się między IF, a ELSE, W przeciwnym wypadku między ELSE, a THEN. W języku FORTH sprawa była prosta, gdyż IF, pobierało znacznik ze szczytu stosu. Co jednak ma sprawdzać słowo IF, w asemblerze? Przed zapisem tego słowa należy podać jeden z następujących symboli:

CS >= 0- 0<

    Symbol CS oraz >= sprawdza znacznik przeniesienia C. Gdy jest on ustawiony oznacza to prawdę, gdy nie - fałsz. Dziwić może występowanie symbolu >= oznaczającego sprawdzanie znacznika C. Użycie go po rozkazie porównania, np. CMP, znacznie polepsza czytelność zapisu. Kolejnym symbolem jest 0= i oznacza sprawdzanie znacznika Z czyli zera. Słowo IF, odczyta prawdę, gdy wynik z działania ostatniego rozkazu wyniósł 0. Ostatnim symbolem jest 0< i oznacza sprawdzanie znacznika N, czyli wyniku ujemnego. Słowo IF, odczyta prawdę, gdy wynik ostatniej operacji przybrał wartość ujemną.

    Po tych rozważaniach teoretycznych przejdźmy do praktyki. Wcześniej jednak usuńmy lewy margines, wpisując 0 82 C! RETURN , a posiadacze magnetofonów obowiązkowo 0 WARNING ! RETURN

    Przykład:

0 VARIABLE WA RETURN
CODE ZMW WA LDA, 100 # CMP, >= IF, WA DEC,
ELSE, WA INC, THEN, NEXT JMP, C; RETURN

    Nasze słowo ZMW będzie działało na zmiennej WA. Co ono wykonuje? Wprowadźmy do zmiennej liczbę 10:

10 WA ! RETURN

i wykonajmy

ZMW (RE) oraz WA ? (RE) RETURN

    Widzimy, że słowo ZMW (napisane w asemblerze) zwiększyło zmienną o 1. Wykonajmy teraz następującą sekwencję:

150 WA ! RETURN
ZMW RETURN
WA ? RETURN

    Tym razem wartość zmiennej została zmniejszona o 1.

    Analizując tekst źródłowy słowa ZMW łatwo zauważyć, że rozkaz WA DEC, będzie wykonany tylko wtedy, gdy wartość zmiennej WA jest większa lub równa 100. W przeciwnym wypadku wykonany będzie rozkaz WA INC, czyli zwiększenie wartości zmiennej o 1. Instrukcja porównania (100 # CMP,) jest konieczna, gdyż wpływa ona na odpowiednie ustawienie znacznika C określonego przez symbol >= dla słowa IF, W asemblerze FORTHa występuje także symbol NOT . Możemy go używać tylko w połączeniu z innym wcześniej poznanym symbolem. Przykład:

0= NOT IF, ... THEN,

    Rozkazy znajdujące się między IF, a THEN, będą wykonane tylko wtedy, gdy znacznik zera Z nie będzie ustawiony. Oznacza to, że symbol NOT neguje wartość logiczną poprzedniego symbolu. Spróbujmy sprawdzić działanie NOT i utworzyć słowo działające odwrotnie niż ZMW.

0 VARIABLE WA RETURN
CODE ZM< WA LDA, 100 # CMP, >= NOT IF, WA
DEC, ELSE, WA INC, THEN, NEXT JMP, C; RETURN

    Widzimy, że wszystko jest takie samo, lecz NOT zmieniło wartość logiczną symbolu >= na przeciwną. Sprawdzenie działania tego słowa pozostawiam czytelnikom.

    UWAGA: symbolu NOT należy używać zawsze za innym symbolem logicznym, nigdy odwrotnie.

    Symbole logiczne występujące przed IF, nie zostaną skompilowane oddzielnie, informują one jedynie kompilator jaki skok warunkowy (BNE, BEQ itp.) znajdzie się na miejscu słowa IF, po kompilacji.

    Kompilacją słów napisanych przez nas w asemblerze zajmuje się standardowy kompilator FORTHa. Program nazwany przez nas asemblerem to jedynie zestaw słów informujących kompilator FORTHa w jaki sposób ma tego dokonać. Widzimy więc, że można by względnie łatwo stworzyć zestaw słów umożliwiających kompilację programu w PASCALu lub C. Ambitny programista może się pokusić o stworzenie kompilatora swojego własnego języka. Realizacja tego zadania w języku FORTH, byłaby o wiele prostsza od podobnego przedsięwzięcia w asemblerze. Niestety, czas kompilacji programu byłby zawsze kilkakrotnie dłuższy.

    Powróćmy jednak do naszego asemblera.

PĘTLE

Poznaliśmy odgałęzienia warunkowe. Jak jednak zrealizować pętlę, gdy nie mamy bezpośredniego dostępu do skoków warunkowych. Jak w przypadku odgałęzienia, tak i tu mamy dostęp do struktury zaczerpniętej z języków wyższego rzędu. Aby zbudować pętlę, należy użyć następującej sekwencji:

BEGIN, ... UNTIL,

    Także w tym przypadku słowo rozpoczynające i kończące pętlę ma w nazwie przecinek. W miejscu kropek mogą występować dowolne instrukcje asemblera. Pętla będzie wykonywana tak długo, aż słowo UNTIL, odczyta wartość "prawda" z odpowiedniego wskaźnika C, Z lub N. Aby określić wskaźnik podający znacznik logiczny dla UNTIL, należy użyć przed tym słowem jeden z poznanych wcześniej symboli: CS >= 0= lub 0<. Zasada postępowania jest identyczna, jak w przypadku odgałęzienia warunkowego, lecz tym razem prawda spowoduje opuszczenie wnętrza pętli. Także w tym przypadku można zanegować wartość logiczną symbolu przez NOT. Przykład:

CODE C$ 119 # LDY, 4 # LDA, BEGIN, 88 )Y
STA, DEY, 0< UNTIL, NEXT JMP, C; RETURN

    Sprawdźmy działanie naszego słowa:

C$ RETURN

    Widzimy, że słowo to wypełniło w mgnieniu oka trzy pierwsze linie ekranu znakiem $.

    W deklaracji słowa symbol 0< występuje bezpośrednio po słowie DEY,, brak jest instrukcji porównania CPY,. Nie jest ona jednak potrzebna, gdyż rozkaz DEY, oddziaływuje bezpośrednio na znacznik N, dający wartość logiczną dla słowa UNTIL ,.

    Istnienie elementów strukturalnych w asemblerze wpływa korzystnie na czytelność programu. Programista zmuszony zostaje do zachowania większego porządku. Nasz asembler posiada więc cechy języka edukacyjnego, wpływającego korzystnie na formę rozwiązania algorytmów przez początkujących adeptów programowania. Mimo swej prostoty jest językiem pozwalającym rozwiązywać także bardzo skomplikowane zadania.

PODPROGRAMY

    Program pisany w asemblerze FORTHa może składać się z podprogramów już wcześniej skompilowanych. Możliwe jest zatem budowanie programu z gotowych klocków, co jest cechą wszystkich języków strukturalnych. Do asemblera FORTHa dodałem słowo MAC, umożliwia ono wywołanie gotowego podprogramu przez podanie jego nazwy w odpowiednim miejscu tworzonego słowa. Jako przykład wpiszmy do komputera:

0 VARIABLE WA RETURN
CODE WA+ CLC, WA ADC, CS IF, WA 1+ INC,
THEN, WA STA, RTS, C; RETURN

    Widzimy, że tym razem przed słowem C; (kończącym deklarację) występuje rozkaz RTS, a nie NEXT JMP,.

    Zmiana ta jest konieczna, gdyż nasze słowo będziemy wywoływać z asemblera, a nie z FORTHa.

    Uwaga: wywołanie tak zadeklarowanego słowa z języka FORTH, spowoduje zawieszenie komputera, gdyż brak jest w nim skoku powrotnego (NEXT JMP,). Co robi nasz podprogram? Jak łatwo zauważyć zwiększa on zmienną WA o wartość znajdującą się w akumulatorze. Jako elementu przekazującego parametr wejściowy, używać będziemy więc akumulatora.

    Piszemy dalej:

CODE WA5+ 5 # LDA, MAC WA+ NEXT JMP, C;

    Tym razem zadeklarowaliśmy słowo, które będzie wywoływane z języka FORTH, dlatego też kończy się ono skokiem NEXT JMP,. Wewnątrz deklaracji widzimy niespotykany w asemblerze zapis - MAC WA+ , który jest wywołaniem podprogramu o nazwie WA+.

    Wydaje się, że prościej byłoby użyć wywołania podprogramu pisząc WA+ JSR,. Nie można jednak tego zrobić w takiej formie, gdyż słowo WA+ jest słowem FORTHa i podanie jego nazwy (wspominałem o tym wcześniej) spowoduje jego wykonanie podczas kompilacji.

Roland Pantoła




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

Pixel 2002