Tajemnice ATARI

FORTH część 7

   
W poprzednim odcinku przedstawiłem Edytor Wprowadzania. Dzięki niemu będę mógł prezentować czytelnikom dłuższe programy w języku FORTH, a ich wprowadzenie do komputera nie powinno sprawiać ładnych trudności.


    Jeżeli ktoś nie wklepał jeszcze Edytora Wprowadzania, powinien zrobić to natychmiast, gdyż tylko wtedy będzie mógł uzyskać inne programy. W tym odcinku przedstawiam ASSEMBLER FORTHa. Na dyskietce zawierającej Extended fig FORTH znajdują się dwa asemblery. Pierwszy z nich nie zasługuje moim zdaniem na większą uwagę. Natomiast drugi - to bardzo dobry, strukturalny (sic!) asembler. Polecałbym go wszystkim czytelnikom, także tym, którzy stawiają pierwsze kroki w programowaniu. Asembler ten ma bardzo ciekawie rozwiązany sposób zapisu pętli oraz skoków warunkowych. Istnieją w nim struktury znane z języków wyższego rzędu np. BEGIN-UNTIL lub IF-ELSE-THEN. Jest to pewne odstępstwo od formy zapisu w innych implementacjach asemblera. Jednak korzyści z tego wynikających jest wiele. Tekst źródłowy staje się bardziej przejrzysty, a początkujący programista zostaje zmuszony do bardziej uporządkowanego sposobu rozwiązywania zadań programowych. Wprowadzenie symboli porównania takich jak np. 0< 0= jest, moim zdaniem, bardziej czytelne od nazw skoków warunkowych stosowanych powszechnie w innych asemblerach. Nie ma jednak róży bez kolców i prezentowany asembler ma pewne wady. Jeżeli ktoś stara się uzyskać większą szybkość działania programu kosztem przejrzystości (np. stosując skoki w dowolne miejsca programu), to srodze się na nim zawiedzie.

    Przedstawiony na końcu tego artykułu tekst źródłowy nie jest wierną kopią asemblera znajdującego się na dyskietce. Usunąłem z niego pewne błędy (brakowało niektórych instrukcji) oraz rozbudowałem o dwa dodatkowe słowa. Ponieważ prezentowany asembler nie jest mojego autorstwa (poza drobnymi zmianami), przedstawiam go raczej z tzw. mieszanymi odczuciami. Nie mam jednak innej możliwości kontynuowania tego cyklu, a zwłaszcza prezentacji programów korzystających z asemblera. Posiadacze magnetofonów mogą tylko w taki sposób włączyć asembler do swojego FORTHa. We wszelkich publikacjach dotyczących języka FORTH bardzo często wykorzystuje się listingi z implementacji; myślę więc, że czytelnicy nie ocenią zbyt surowo tego występku. Po wprowadzeniu (z kodami!) tekstu źródłowego asemblera do Edytora Wprowadzania i kompilacji możemy przystąpić do tworzenia nowych stów w asemblerze.

ASSEMBLER FORTHa

   Jeżeli posiadamy już ASSEMBLER w swoim komputerze, możemy przejrzeć słownik z nowymi nazwami. Aby to uczynić, należy napisać ASSEMBLER, a następnie VLIST i zatrzymać w odpowiednim miejscu naciskając spację.

    Deklarowanie nowych słów w asemblerze nie różni się zbytnio od deklaracji w języku FORTH. Zamiast znanego nam dwukropka piszemy słowo CODE. Za nim podajemy nazwę tworzonego słowa, a następnie nazwy rozkazów asemblera. Deklarację kończymy pisząc słowo END-CODE lub C; - jest to znak dla kompilatora, aby przeszedł do FORTHa. Tak zadeklarowane słowo jest dostępne w języku FORTH po podaniu jego nazwy. Także wewnątrz słów zadeklarowanych w asemblerze przez CODE mamy dostęp do nazw słów zadeklarowanych wcześniej, zarówno w asemblerze jak i FORTHcie.

    Mnemoniczne nazwy rozkazów są w większości wypadków, takie same jak w innych asemblerach (z wyjątkiem skoków). Różnica polega na tym, że na końcu nazwy rozkazu dodajemy przecinek np.

LDA, zamiast LDA

    Druga zasadnicza zmiana to, oczywiście, stosowanie Odwrotnej Notacji Polskiej i w związku z tym wszystkie parametry podajemy przed nazwą np.

18 LDA, zamiast LDA 18

    Podawanie parametru przed nazwą ma tę zaletę, że można go podać w formie wyrażenia zapisanego w języku FORTH, gdyż wartość parametru jest pobierana ze szczytu stosu. Przykład:

3 4 * 12 + 6 / LDA,

    W skład wyrażenia mogą wchodzić dowolne słowa wcześniej skompilowane (także stworzone przez użytkownika). Uwaga! Słowa nie wchodzące w skład asemblera, a znajdujące się między CODE a C; lub END-CODE , będą wykonywane podczas kompilacji, a nie zostaną skompilowane.

    Czas wreszcie na przykład deklaracji nowego słowa:

0 VARIABLE WA RETURN
CODE WA+ WA LDY, INY, WA STY, NEXT JMP, C; RETURN

    Stworzyliśmy w asemblerze słowo WA+ , na początku jednak zadeklarowaliśmy zmienną WA , która będzie wykorzystana do przekazywania wartości z FORTHa do asemblera i z powrotem. Przed zakończeniem deklaracji słowa WA+ , występuje sekwencja NEXT JMP, - jest to skok do interpretera języka FORTH. Skok ten musi występować zawsze, gdy zadeklarowane słowo będziemy wywoływać z FORTHa.

    Czytelnicy, którzy programują w języku Basic i próbowali kiedykolwiek wykorzystać wstawki asemblerowe, wiedzą dobrze, że sprawia to kłopotów. Także inne języki na naszą "maszynkę" nie posiadają w pełni rozwiązanego problemu włączania kodu maszynowego. Wiele problemów występuje także przy przekazywaniu parametrów z języka do kodu i z powrotem. W języku FORTH nie istnieje problem przekazywania parametrów, gdyż jak łatwo zauważyć, te same struktury danych są dostępne zarówno w asemblerze jak i w FORTH. Co wykonuje nasze słowo? Sprawdźmy:

10 WA ! RETURN
WA+ RETURN
WA ? RETURN

    Słowo to zwiększyło wartość zmiennej WA o 1, należy jednak pamiętać, że zmienna w języku FORTH ma 2 bajty i dla liczb większych od 254 należy je rozbudować. To samo zadanie można było zapisać prościej, wykorzystując rozkaz INC, .

    W utworzonym przez nas słowie wykorzystaliśmy adresowanie absolutne. Aby umożliwić inne tryby adresowania, należy wstawić między parametr (operand) a nazwę rozkazu asemblera odpowiedni znaczek określający rodzaj adresowania. Uwaga: wszystkie części rozdzielamy spacją.

    Poszczególne tryby mają następujące symbole:

• # - tryb natychmiastowy
• .A - akumulatora
• X) - pośredni X
• )Y -pośredni Y
• ) - pośredni
• ,X - strony zerowej lub absolutny X
• ,Y - strony zerowej lub absolutny Y

    Brak znaczka oznacza tryb absolutny lub strony zerowej, w zależności od wartości operandu.

    Przykłady:

10 # LDA,

    załaduje liczbę 10 do akumulatora (tryb natychmiastowy).

0 ,X LDA,

    załaduje liczbę znajdującą się na stronie zerowej w komórce, której wartość zawiera rejestr X (tryb strony zerowej X).

    Ostatni z wymienionych trybów nie jest może zbyt często wykorzystywany przez programistów. Jednak w asemblerze FORTHa umożliwia bezpośredni dostęp do stosu FORTHa.

    A oto wspomniany na początku listing ASSEMBLERA:
018S ( RAGSDALE ASSEMBLER 1.1 )
0200
0371 VOCABULARY ASSEMBLER IMMEDIATE
04LF ASSEMBLER DEFINITIONS
05FE BASE @ HEX
0600
07MD 0 VARIABLE INDEX -2 ALLOT
08AM 0909 , 1505 , 0115 , 8011 ,
09TK 8009 ,
0AHU 1D0D , 8019 , 8080 , 0080 ,
0BIN 1404 , 8014 , 8080 , 8080 ,
0CKO 1C0C , 801C , 2C80 ,
0D00
0E9N 2 VARIABLE MODE : .A 0 MODE ! ;
0FE0 : # l MODE ! ; : MEM 2 MODE ! ;
0GUA : ,X 3 MODE ! ; : ,Y 4 MODE ! ;
0HSH : X) 5 MODE ! ; : )Y 6 MODE ! ;
0I9C : ) F MODE ! ; : BOT ,X 0 ;
0JLH : SEC ,X 2 ; : RP) ,X 101 ;
0K42 : UPMODE IF MODE @ 8 AND 0=
0LI5 IF 8 MODE +! THEN THEN
0M4I 1 MODE @ F AND -DUP IF 0 DO DUP
0NG0 + LOOP THEN OVER 1+ @ AND 0= ;
0OJ4 : CPU <BUILDS C, DOES> C@ C,
0PR5 MEM ;
0Q00
0R13 00 CPU BRK, 18 CPU CLC,
0S4B D8 CPU CLD, 58 CPU CLI,
0T4G B8 CPU CLV,
0UHL CA CPU DEX, 88 CPU DEY,
0V2E E8 CPU INX, C8 CPY INY,
10LT EA CPU NOP, -->
1100
1200
13DA 48 CPU PHA, 08 CPU PHP,
14HU 68 CPU PLA, 28 CPU PLP,
155G 40 CPU RTI,
16EI 60 CPU RTS, 38 CPU SEC,
17CL F8 CPU SED, 78 CPU SEI,
186Q AA CPU TAX,
19EN A8 CPU TAY, BA CPU TSX,
1AA5 8A CPU TXA, 9A CPU TXS,
1B5I 98 CPU TYA,
1C00
1DTL : MCP <BUILDS C, , DOES> DUP
1E28 1+ @ 80 AND IF 10 MODE +! THEN
1F59 OVER FF00 AND UPMODE UPMODE
1G04 IF MEM CR LATEST ID. 3 ERROR
1HNB THEN
1IEV C@ MODE C@ INDEX + C@ + C,
1JIV MODE C@ 7 AND IF MODE C@
1K9I F AND 7 <
1LGF IF C, ELSE , THEN THEN MEM ;
1M00
1NM0 1C6E 60 MCP ADC, 1C6E 20
1OOL MCP AND, 1C6E C0 MCP CMP,
1PDS 1C6E 40 MCP EOR, 1C6E A0 
1QFH MCP LDA, 1C6E 00 MCP ORA, 
1R28 1C6E E0 MCP SBC, 1C6C 80
1SGR MCP STA, 0D0D 01 MCP ASL,
1T00
1UA1 -->
1V00
2000
2100
2200
2328 0C0C Cl MCP DEC, 0C0C E1
24SH MCP INC, 0D0D 41 MCP LSR,
2540 0D0D 21 MCP ROL, 0D0D 61
261M MCP ROR, 0414 81 MCP STX,
2707 0486 E0 MCP CPX, 0486 C0
28PJ MCP CPY, 1496 A2 MCP LDX,
291P 0C8E A0 MCP LDY, 048C 80
2AO4 MCP STY, 0480 14 MCP JSR,
2BCP 8480 40 MCP JMP, 0484 20
2C7F MCP BIT,
2D00
2EM3 : BEGIN, HERE l ; IMMEDIATE
2F00
2GRK : UNTIL, ?EXEC >R 1 ?PAIRS R>
2HFP C, HERE 1+ - C, ; IMMEDIATE
2I00
2JCJ : IF, C, HERE 0 C, 2 ;
2K5S IMMEDIATE
2L00
2M13 : THEN, ?EXEC 2 ?PAIRS HERE
2NF8 OVER C@ IF SWAP ! ELSE OVER 1+
2OM3 - SWAP C! THEN ;  IMMEDIATE
2P00
2Q9O : ELSE, 2 ?PAIRS HERE 1+ 1 JMP,
2REO SWAP HERE OVER 1+ - SWAP C! 2 ;
2S5S IMMEDIATE
2T00
2UA1 -->
2V00
3000
3133 ( --- )
3200
33MK : NOT 20 + ;
3400
35HR 90 CONSTANT CS D0 CONSTANT 0=
366I 10 CONSTANT 0< 90 CONSTANT >=
3700
380L : C; CURRENT @ CONTEXT !
39LD ?EXEC ?CSP SMUDGE ; IMMEDIATE
3AHC : END-CODE [COMPILE] C; ;
3B5S IMMEDIATE
3CCM FORTH DEFINITIONS DECIMAL
3D00
3EAU : CODE ?EXEC CREATE [COMPILE]
3F36 ASSEMBLER ASSEMBLER MEM !CSP ;
3GQU IMMEDIATE ' ASSEMBLER CFA
3H38 ' ;CODE 8 + !
3I1E LATEST 12 +ORIGIN !
3J8J HERE 28 +ORIGIN ! HERE 30
3KFF +ORIGIN ! HERE FENCE !
3LLF ' ASSEMBLER 6 + 32 +ORIGIN !
3M1U BASE ! FORTH
1N3P : MAC  -FIND 0= 0 ?ERROR
3O4V DROP 32 C, , ; IMMEDIATE
3PD2 ( END )

Roland Pantoła




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

Pixel 2002