FORTH część 82 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 dalszyWszystkie 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łęzieniaPierwsza 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ĘTLEBEGIN, ... 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. PODPROGRAMY0 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
|