IS-FORTH

File-név: FORTH.ROM
Program neve: IS-Forth 1.1
Intelligent Software - 1985
rendszerbővítő, FORTH programozási nyelv

TARTALOM

Bevezetés

1. Az első lépések
1.1. A szótárról
1.2. Mi a verem?
1.3. Számolni tanulunk - kicsit szokatlan lesz...
1.4. Számábrázolás
1.5. Összehasonlító és logikai műveletek
1.6. Gyorsműveletek
1.7. A verem átrendezése
1.8. Még egy szó a kiírásról
1.9. Hogyan tároljuk programjainkat?

2. Vezérlési Szerkezetek
2.1. A feltételes utasítás
2.2. Indexes ciklusok
2.3. Index nélküli ciklusok
2.4. Rekurzió

3. Adattípusok
3.1. Előjeles és előjel nélküli értékek
3.2. A duplaszavak
3.3. Ismerkedés a memóriával
3.4. Változók és konstansok
3.5. Hol változik a változó? Ismerkedés a szótárral
3.6. A FORTH -füzér és a WORD
3.7. Adatszerkezetek létrehozása
3.8. Számkonverziók

4. A virtuális memória

5. A szótár és a címinterpreter
5.1. Szókészletek
5.2. A Forth Assembler
5.3. Változók és konstansok
5.4. Az EXECUTE
5.5. Hibakezelés

6. Azonnali szavak
6.1. Amikor az interpreter fordít
6.2. Amikor az interpreter mégsem fordít
6.3. Literálok
6.4. Azonnali szavak elhalasztása a [COMPILE])

7. Enterprise specifikus szavak
7.1. Kapcsolat az EXOS-al
7.2. Grafika az IS-FORTH-ban
7.3. Egyéb szavak

IS-FORTH szójegyzék


Bevezetés

Az alábbi "rövid ismertető" a FORTH programozási nyelv, pontosabban annak - az Intelligent Software Ltd által megvalósított - IS-FORTH rejtelmeibe kívánja bevezetni az olvasót. Az ismertető nem teljes, mindössze arra vállalkozik, hogy ízelítőt adjon a FORTH rejtelmeiből. A leírás Adorján Noémi - FORTH lépésről lépésre című könyve (Műszaki könyvkiadó - 1990) alapján készült, az IS-FORTH "nyelvjárásához" igazítva.

Néha nem is gondolnánk, hogy programgyűjteményünk egyes darabjaiban micsoda hatalmas" erők rejlenek. Közéjük tartozik az IS-FORTH. Ha valaki legális úton jutott a rendszerhez, gyakran még az eredeti dokumentációval a kezében sem megy sokra: nehéz megtenni az első lépéseket. Dokumentáció nélkül pedig végképp kudarcba fulladhatnak a FORTH-szal kapcsolatos kísérletek. Így könnyen hajlunk arra, hogy az egészet a sarokba dobjuk, pedig a FORTH igen komoly nyelv.
A Forth nyelvet az amerikai Charles H. Moore amerikai csillagász fejlesztette ki, 1968-ban készült el egy korai változattal. Akkoriban Kaliforniában, az Egyesült Államok Nemzeti Rádiócsillagászati Obszervatóriumában (NRAO) egy rádióteleszkóp vezérlési és adatfeldolgozási munkáin dolgozott. Egy gyors, kompakt, rugalmas és kibővíthető programozási nyelvre lett volna szüksége. Miután nem talált alkalmas nyelvet (pl.: FORTRAN, ALGOL, COBOL), nekilátott egy úgynevezett negyedik (fourth) generációs programozási nyelv elkészítéséhez. A cél főképp a programozó eredményességének javítása volt a gép hatékonyságának feláldozása nélkül. Munkáját az IBM 1130 (3. generációs) számítógépen végezte. Moore úgy érezte, hogy olyan nyelvet alkotott, amely a "majdani" negyedik generációs számítógépek általánosan használt programozási nyelve lehet. Ezért a nyelvet FOURTH-bak ("Negyedik") szerette volna elnevezni, azonban az IBM 1130 operációs rendszere csak legfeljebb ötkarakteres file-neveket fogadott el. Így lett a FOURTH-ból FORTH, amely nem kevésbé kifejező, jelentése: tovább, előre.
A legtöbb programozási nyelvben komoly megszorítások léteznek. Például ha gyors az assembler, akkor csak egy típusú processzorra használható, nehéz megtanulni és a programozás során ismerni kell az adott gép belső felépítését. Egy másik gépre való áttéréskor újra kell tanulni az adott gépet. A Basic-et könnyű megtanulni, elérhető a gépek nagyrészén, viszont a feladatok többségéhez nagyon lassú és sok dialektusa van. A Forth sokkal gyorsabb a Basic-nél, könnyebben megtanulható. Majdnem minden létező processzoron elérhető és nagy vonzerővel rendelkezik.
Moore eredményeinek híre szájhagyomány útján gyorsan terjedt először a rádió csillagászok körében, majd Amerika nyugati partjának tudományos köreiben. Moore és néhány kollégája megragadva a lehetőséget otthagyták régi munkájukat és 1971-ben megalakították a "Forth Inc." céget, mely a Forth implementációk kereskedelmi változatainak és szoftver csomagoknak az előállításával foglalkozik. Kezdetben nagy lelkesedés övezte a nyelvet, azonban ez rövid idő alatt lelohadt. Az ok abban kereshető, hogy a nyelv legfőbb előnyei, úgymint a tömörség és a nagy sebesség nem bizonyultak fontosabbnak, mint a korabeli nyelvek könnyebb elsajátíthatósága a nagyszámítógépek korában. Ezenkívül ebben az időben kevés helyen álltak a programírók rendelkezésére olyan interaktív eszközök (képernyő, billentyűzet), amelyek a FORTH használatához elengedhetetlenül szükségesek.
A FORTH a mikroszámítógépek megjelenésével került ismét az érdeklődés középpontjába az 1970-es évek végén. Itt már a szűkös memória, és viszonylag lassú központi egység jobb kihasználása tömör és gyors programozási nyelvet igényelt. Ezeknek a követelményeknek az addig méltatlanul mellőzött FORTH nyelv minden szempontból megfelelt. Így lett - a BASIC mellett - a FORTH a személyi számítógépek legáltalánosabban használt programozási nyelve. A mikroszámítógépek terjedésének köszönhetően felhasználói közösségek alakultak ki az USA-ban és Európában. Ezek a csoportok bővítették a nyelvet, új változatokat hoztak létre. Ezek közül kettő terjed el szélesebb körben:


Az IS-FORTH megfelel a Forth-83-nak, csak tartalmaz egy opcionális dupla pontosságú szókészletet és egy assembler szótárat. Korának legygyorsabb FORTH implementációja.
Az Enterprise implementáció több mint száz speciális szót tartalmaz, melyek lehetővé teszik a gép és az operációs rendszer által kínált lehetőségek kihasználását. Minden olyan esetben, ahol olyan szóra volt szükség, mely nem szerepel a szabványban, más Forth implementációkban már létező szavakat vett át az Intelligent Software.
A FORTH-ra legelfogultabb rajongója sem mondhatja, hogy könnyen tanulható. Előnyeit többek között annak köszönheti, hogy gépközeli nyelv (tehát használatához érteni kell egy kicsit a számítógép "lelkét"), hogy bővíthető, alakítható (tehát tudni kell valamennyire, hogyan működik maga a FORTH), és hogy sok benne az eredeti, szellemes, de nem feltétlenül könnyen érthető elgondolás. A FORTH megismerése tehát nem megy erőfeszítések nélkül. Mégis megéri, hiszen:

Mindenkinek, aki elszánta magát a FORTH tanulására, sok sikert és jó szórakozást!

1. Az első lépések

Indítsuk el a FORTH-ot! Az IS-Forth rendszerbővítő, tehát ha a gép bekapcsolása után nem az indul el, ezt a :FORTH paranccsal tehetjük meg. A FORTH interpreter a forgalmazó cég, és a (BASIC-hez hasonlóan) rendszerben lévő és a programozó számára szabadon maradt memória kiírásával indul. Ezután várja, hogy adjunk neki egy parancssort. Addig vár, amíg a neki szóló sort be nem fejeztük.
Honnan tudja, mikor fejeztük be? Onnan, hogy lenyomjuk a sor végét jelző ENTER billentyűt. A begépelt sort az ENTER adja át a FORTH-nak; az ENTER lenyomásáig tehát más nem történik, mint az, hogy a FORTH ránk vár. Ezt érdemes megjegyezni, ha nem akarunk sok időt azzal tölteni, hogy az ENTER nélkül adott utasításaink végrehajtására várunk.

Az IS-FORTH-ban a BASIC-ben megszokott EXOS szerkesztő funkciók változatlanul működnek. Kezdjük úgy, hogy mindjárt be is fejezzük: adjunk egy üres parancssort: Nyomjuk meg az ENTER-t! A feleletként kapott OK azt jelenti, hogy a FORTH a mondottakat (esetünkben a nagy semmit) hiánytalanul végrehajtotta. Az OK után a FORTH a következő sor elejére várja újabb kívánságainkat.
Egy egyszerű szó, amit megért:

CR

Az OK ezúttal egy sorral lejjebb jelenik meg, a FORTH kiírt egy kocsivissza és egy soremelés karaktert. A Basic nyelvtől eltérően az IS-FORTH-ban az Interpreter megkülönbözteti a kis és nagybetűket! Minden parancsot nagy betűvel kell írnunk. Ha kis betű is szerepel egy szóban a Forth nem érti meg! A FORTH indításakor a billentyűzet nagybetűs-üzemmódba kerül. A "biztonság kedvéért" minden ENTER leütésekor, - ha esetleg megszűntettük a CAPS üzemmódot - a Forth ismét nagybetűs üzemmódba vált.
Hogy a hatás látványosabb legyen, írjuk most ugyanezt egy sorba többször! Ehhez azt kell tudnunk, hogy:

a szavakat nagybetűvel kell írnunk,
az egyes szavakat egy soron belül (egy vagy több) szóközzel választjuk el egymástól.

Tehát, ha azt írjuk:

CR CR CR CR

a FORTH kiírja a négy üres sort és OK-val díjazza a szabályos feladatkiírást. Ha viszont azt írjuk, hogy

CRCR CR CR

akkor a FORTH az érthetetlen CRCR láttára megsértődik, és OK-ra sem méltatva minket, abbahagyja az egészet. A két "jó" CR-t már el sem olvassa. Játsszunk még "kiírósdit":

42 EMIT

Az EMIT szó kiírja azt a karaktert, amelyiknek a kódját megadtuk neki; a 42 a csillag kódja.
Definiáljunk egy csillagíró szót!

: CS 42 EMIT ;

Ezzel megírtuk első FORTH programunkat. Most már ez is ugyanolyan FORTH szó, mint akármelyik másik, tehát nevének leírásával futtatható:

CS

Sőt, új szavak alkotására is felhasználhatjuk:

: PONT CR CS ;
: VONAL CR CS CS CS

Az új szavakkal pedig tovább építkezhetünk:

: F CR VONAL PONT VONAL PONT PONT PONT CR ;

Az új szavakat érdemes azonnal ki is próbálni. Így lehet (és ajánlatos) "biztosra menni". A FORTH egyik legvonzóbb tulajdonsága éppen ez: az építőkövek, amelyekből a program végül összeáll, megírásuk után azonnal, külön-külön is kipróbálhatók.
A FORTH alapszavak nagy része ugyanígy FORTH-ban íródott, más alapszavak felhasználásával. Például a

SPACE

szó, amely egy szóközt ír a képernyőre, így épül fel:

: SPACE 32 EMIT ;

A már ismert CR pedig így:

: CR 13 EMIT 10 EMIT ;

Ha a képernyőt már teljesen "összefirkáltuk", a

CLS

paranccsal törölhetjük le.

1.1. A szótárról
Mitől lett a CS, az F stb. végrehajtható szó? Mi történik, amikor ilyen "kettőspontos szódefiníciót" írunk?
A FORTH a számára értelmezhető szavakat egy szótárban tartja. Betöltés után a szótárban a FORTH alapszavak vannak. Új szavak létrehozásával a szótárat - vagy, ha úgy tetszik, magát a FORTH nyelvet - bővítjük. A szótári szavak neveit a

VLIST

(Vocabulary List; a vocabulary, ejtsd: vokébjulöri szó, jelentése: szótár) szóval írathatjuk ki a képernyőre. A VLIST hatására meginduló "szóáradat" a STOP billentyű leütésével megállítható. Ha saját szavakat definiálunk és utána VLIST-tel szemügyre vesszük a szótárunkat, látjuk, hogy a legutoljára definiált szavak jelennek meg legelőször; előbbi működésünk után például a szótárlista valahogy így kezdődik:

F VONAL PONT CS

A FORTH interpreter, mikor egy szót értelmezni akar, először is elkezdi azt a szótárban keresni. Mégpedig az útoljára definiált szónál; ebben talál adatot arról, hogy hol kezdődik az utolsó előttinek írt szó, így ha kell, ott folytatja a keresést, és így tovább.
Ha tehát példánkban az F definíciója után írunk egy újabb F szót:

: F 70 EMIT ;

akkor az F szót "átdefiniáltuk"; a FORTH figyelmeztető hibajelzést ad és beírja az új szót a szótárba.

Álljunk meg egy pillanatra!
Jó, jó, hogy a CS, F stb. attól végrehajtható, hogy benne van a szótárban. Beletettük, mikor definiáltuk őket. Az is igaz lehet, hogy az EMIT benne van, hiszen alapszó. De mitől van benne a 42 meg a 70? Csak nincs benne az összes szám? Ha pedig valami nincs a szótárban, akkor miért nem szól miatta az interpreter, miért tesz úgy, mintha minden a legnagyobb rendben volna?
Elvből. Az elv az, hogy ami nem szótári szó, az biztosan szám, tehát a FORTH interpreter a szótárban való sikertelen keresés után megpróbálja számnak értelmezni a kapott karaktersorozatot. Ha nem megy ("számszerűtlen" karakterek vannak benne), akkor az tényleg hiba. Ha viszont igen, számról van szó, akkor ez a szám a verembe kerül.
Lehetőségünk van a szótár szavait újradefiniálni, azaz egy szónévhez két (sőt több) meghatározást rendelni. Hasonló ez egy igazi értelmező szótárhoz, ahol egy címszóhoz több jelentés, meghatározás is tartozhat. Azt, hogy a sok jelentés közül melyiket használjuk, a szó környezete általában egyértelműen meghatározza. A FORTH ennél sokkal egyszerűbben dönt arról, hogy éppen amelyik jelentést kell figyelembe vennie. Egy adott szó végrehajtása mindig a legutolsó definíció törzsének végrehajtását jelenti. Ne felejtsük el viszont, hogy a szó újbóli definiálása nem jelenti az előző definíció (vagy definíciók) törlését! Ezek a szótárban továbbra is megmaradnak, csak éppen új szó "eltakarja" a jelentését.
Mi legyen, ha ráuntunk a definícióinkra, nem akarjuk őket tovább használni? Például átdefiniáltunk egy szót, de megbántuk.

FORGET F

A FORGET elfelejti a megadott szót, ezenkívül az utána definiált (tehát a szótárban "fölötte levő") szavakat. Micsoda??? Mindent, amit utána definiáltunk? Ez így van. Elvileg ugyanis bármelyik szóban, amelyet az elfelejtendő után írtunk, használhattuk ezt az éppen törölni kívánt szót! A FORTH szótár szavai egymásra épül(het)nek, nem lehet belőle csak úgy, "középről" törölni. (Meg lehet viszont őrizni szavaink forrásszövegét, hogy hogyan, arról lesz még szó.) Most csak meg szeretnék nyugtatni mindenkit: nem kell majd egy hiba miatt mindig mindent újra begépelni!)
Melyik F szótól kezdve fog a FORGET felejteni, ha kettő is van? Szinte látatlanban meg lehet mondani: a "felsőtől", az utoljára definiálttól. A szavak keresése a szótárban, bármi célból történjen is, mindig felülről halad, ilyen irányban lehet a szótárat gyorsan végignézni. Ezzel példánkban kiássuk a régi, a csillagos F szót, és újra ez lesz az érvényes.

1.2. Mi a verem?
A verem (angol neve stack, ejtsd: sztek) igen fontos része a FORTH-nak. Ebben "leveleznek" egymással az egyes szavak. Például az EMIT a veremben keresi annak a karakternek a kódját, amelyet ki kell írnia a képernyőre; miután kiírta, le is pusztítja a veremről.
Azért hívják veremnek, mert több dolgot (esetünkben több számot) lehet benne tartani; ezek közül mindig csak egyhez férünk hozzá: ahhoz, amelyik legutoljára került oda, vagyis "legfelül van". Hogy ezt kitapasztalhassuk, egy új FORTH alapszót tanulunk.

Próbáljuk ki!

65
.

A veremre tettük a 65-öt (első sor). -tal "rákérdeztünk" (második sor). Vissza is írta! Egyúttal törölte is. Győződjünk meg erről. Írjunk még egy pontot. Hibajelzést kapunk, amely azt jelenti, hogy több elemet akartunk a veremből elhasználni, mint amennyi volt benne.
És ha nem? Könnyen előfordulhat, hogy a Kísérletező Olvasó már egy csomó mindent művelt, mire ide eljut. Esetleg már volt a veremben valami. A verem kiürítésének legegyszerűbb módja: begépelünk egy szót, amelyről tudjuk, hogy a FORTH nem ismeri. A "feldühödött" interpreter kiüríti a vermet; ha ezután próbálja ki valaki a fentieket, meglátja, hogy így igaz. A módszer hasznos lehet, mikor véletlenül rakjuk tele a vermet "szeméttel". (Mondjuk ciklusban felejtünk el valami fölöslegeset törölni.)
Próbáljuk ki ugyanezt több számmal:

1 2 3 . . .

Melyik számot fogja először kiírni? Azt, amelyik a verem tetején van, tehát amelyik utoljára került a verembe. Ezt egyúttal törli is; a következő tehát az alatta levő elemet írja ki és törli. A kapott válasz:

3 2 1

Az egyes lépések után a verem a következő ábra szerint néz ki.

A legtöbb szó a veremben legfelül található számmal / számokkal dolgozik. Ezeket a számokat eközben gyakran kiveszik onnan, holott a későbbiekben még szükségünk lenne rájuk. Ezért szükség lehet a veremben látható legfelső szám "lemásolására", így a műveletet a másolaton hajtjuk végre, az eredeti a veremben marad. Ez a szó a:

DUP ( n - - - - n n )

A nyelv hagyományai szerint az utasítások működését (hatását az adatveremre) megjegyzésként (azaz zárójelek között), az alábbi formában szokás dokumentálni: ( bemenő adatok - - -kimenő adatok )

Egyszerre akár két elemet (vagy egy dupla pontosságú elemet) is megduplázhatunk:

2DUP (n m - - - - n m n m )

A verem teljes tartalmát egyetlen lépésben törölhetjük az

SP! (n...m - - - - )

szó használatával.
Igen hasznos szó, a

.S ( - - - - )

mely kiírja a veremben lévő számokat, de nem távolítja el azokat. A kiírást a legalsó elemmel kezdi, és sorban kiírja valamennyi a veremben lévő számot. E parancs segítségével bármikor ellenőrizhetjük a verem tartalmát, anélkül, hogy bármit módosítanánk benne. Nagy segítség a tanuláshoz!

1.3. Számolni tanulunk - kicsit szokatlan lesz...
Az 1950-es években az elméleti matematikusok kidolgozták a vermen alapuló, úgynevezett veremgépek elméletét. Közöttük volt Jan Lukasiewicz lengyel származású matematikus, aki kidolgozta a veremgépekhez illeszkedő aritmetikai jelölésformát, amely fordított lengyel forma néven vonult be a szakirodalomba (reverse Polish notation). A FORTH nyelvben műveleteket csak a veremben levő számokkal végezhetünk. Például, ha végre akarjuk hajtani a 2+3 műveletet, ehhez először a 2 és a 3 számokat a verem tetejére kell helyeznünk, majd kiadhatjuk az összeadás elvégzésére a + utasítást.
Miből áll a FORTH aritmetika? Természetesen FORTH szavakból. Ezek nevei olyan rövidek, hogy a naivabbak műveleti jelnek is vélhetik. A négy alapművelet: + , - , * , / . Mindegyik a vermen várja a két számot, amelyeken a műveletet végzi (azaz a művelet két operandusát); ezeket le is emeli a veremről és helyükbe a művelet eredménye kerül. Erre a következő ábrán láthatunk példát.

(Az adott lépés után a veremben levő adatokat rajzoltuk meg.) A

2 3 + 4 *

sorozat ugyanazt a számítást végzi, mint a (mondjuk) BASIC nyelven írt (2+3)*4.
Az utóbbi, megszokottabb jelölésmódot infixnek nevezzük, szemben a FORTH (többeket visszariasztó) postfix jelölésével. Az elnevezések azt tükrözik, hogy a műveleti jel az infix írásmódban a két operandus között (in) van, a pontfixben pedig az operandusok után (post). A postfix megszokásához mankóul szolgálhat a következő:

Az operandusok sorrendje a postfix írásmódban ugyanaz, mint az infixben, csak a műveleti jel helye változik.
infix
postfix
1 + 1
1 1 +
2 - 4
2 4 -
6 / 3
6 3 /
(10+2)*2
10 2 + 2 *
3+2*4
2 4 * 3 +

Ez azt jelenti, hogy pl. kivonásnál a szó a kivonandót várja a verem tetején, alatta pedig a kisebbítendőt. A művelet elvégzése után a veremben a különbség lesz. A tevékenységet a következő ábra szemlélteti:

     
   
kivonandó
 
kissebbítendő
különbség
ilyen volt
ilyen lesz

Ezt a FORTH programoknál így szokás dokumentálni:

( kisebbítendő kivonandó - - - - különbség )

Zárójelet azért szoktunk írni, mert így az egyes szavak hatása a veremre a FORTH forrásszövegben is feltüntethető.
A ( ugyanis FORTH alapszó. Működése: a záró zárójelig "takarja" a szöveget, amelyet az interpreternek adunk; így a nyitó és záró zárójel közötti részt az interpreter el sem olvassa, nemhogy végrehajtaná. Tessék kipróbálni! A nyitó és záró zárójelnek egy sorban kell lennie. Így lehet FORTH-ban dokumentálni.
A veremhatás jelölésének sémája:

( előtte - - - - utána )

Ha az elemek sorrendjét nézzük, egyszerűen úgy kell képzelni, mintha a vermet jobbra döntenénk.
A négy alapművelet veremhatása:

+
( összeadandó1 összeadandó2 - - - - összeg )
-
( kisebbítendő kivonandó - - - - különbség )
*
( szorzandó1 szorzandó2 - - - - szorzat )
/
( osztandó osztó - - - - hányados )

A számolást segíti a következő néhány, az eddigiek alapján könnyen megérthető szó:

MIN ( n1 n2 - - - - min ) A két szám közül a kisebbet adja.
MAX ( n1 n2 - - - - max ) A két szám közül a nagyobbat adja.
MOD ( n1 n2 - - - - mar ) Az n1/n2 osztás maradékát adja.
/MOD ( n1 n2 - - - - mar hany ) Az n1/n2 osztás maradékát és hányadosát is megkapjuk.
ABS ( n - - - - n1 ) Az n abszolút értékét adja.
NEGATE ( n - - - - n1 ) A kapott szám -1-szeresét adja.

A Forth-nak egész számokkal dolgozó aritmetikája van. Ezért az osztást különleges figyelemmel kell kezelni. A példa kedvéért, ha a hetet elosztjuk hárommal, az eredmény kettő lesz. A pontos válasz persze az lenne, hogy a 7-ben a 3 kétszer van meg , a maradék pedig egy. A /MOD operátor megoldja az említett problémát. Eredményként itt megkapjuk a hányadost és a maradékot is a veremben.
Példa:

7 3 /MOD . .

A veremből kapott két szám a 2 és 1 lesz.
Ezzel szemben a MOD operátor csak a maradék osztását teszi a verembe.

A következő példaprogram hőmérsékletet számol át Fahrenheit-ből Clesius fokba:

: DEGCON CR 32 - 5 * 9 / . ;

Tegyük fel, hogy 144 Fahrenheit fokot akarunk átszámolni, Ehhez a következőt kell beírnunk: 144 DEGCON

Véletlenszám-generátor
Gyakran használatos játékoknál, demonstrációknál. Igen könnyű a használata. Véletlenszám generálható 0 és 65536 között. Ha például 0 és 100 között akarunk véletlenszámot generálni és azt akarjuk, hogy ez a verembe kerüljön, a következő utasítást kell adni:

101 RND

látható, hogy a verembe előzőleg egy számot kell tölteni. Ez a szám eggyel nagyobb kell, hogy legyen a kívánt felső határnál.

Példa: Dobókocka szimuláció
Írjunk egy olyan programot, ami egy dobókockát szimulál, Ennek kapcsán néhány új szóra is szükségünk lesz. A KEY megállítja a program futását, és csak akkor folytatja, ha a felhasználó leüt egy billentyűt. A BEGIN...REPEAT végtelen ciklus (később részletesen beszélünk róla), addig fut amíg le nem állítjuk a STOP billentyűvel.

: DICE BEGIN 6 RND 1+ . CR KEY REPEAT ;

Amikor a DICE végrehajtásra kerül, egy 0 és 5 közötti véletlenszám generálódik. Miután nekünk 1 és 6 közti számokra van szükségünk, hozzáadunk egyet a generált számhoz. Ezután kiíratjuk ezt, majd a CR hatására a kurzor egy sort lejjebb megy. Ekkor a program mindaddig fel van függesztve, amíg egy billentyűt le nem ütünk. A program mindaddig újraindul, amíg a STOP-ot le nem ütjük.

Számrendszerek
A FORTH alapértelmezésben 10-es számrendszerben dolgozik, de bármikor megváltoztathatjuk a használt számrendszert:

BINARY - Kettes (Bináris) számrendszer.
OCTAL - Nyolcas (oktális) számrendszer.
DECIMAL - Tízes (decimális) számrendszer
HEX - 16-os (hexadecimális) számrendszer

A fenti szavak a veremre semmilyen hatást nem gyakorolnak, a BASE rendszerváltozóba töltik a megfelelő értéket (az OCTAL szó pl. 8-at). (Lásd 3.4. fejezet) Ha átállítjuk a használt számrendszert, a veremben tárolt értékek azonnal az új számrendszerben használhatóak a továbbiakban.
A FORTH az összes aritmetikai műveletet bináris formában végzi. A bemeneti számokat binárissá alakítja, így számolja ki a művelet eredményét, majd a kiíratás előtt a számokat visszaalakítja a megfelelő számrendszerbe.
Példa: hogyan konvertáljuk az 1234 decimális számot hexadecimális számmá?

DECIMAL 1234 HEX . DECIMAL

A válasz 4D2. Megjegyezzük, a DECIMAL a sor elején azt jelenti, hogy biztosak legyünk abban, hogy tízes számrendszerben adjuk be az 1234-et. A végén kiadott DECIMAL pedig biztosítja, hogy a parancs végrehajtása után továbbra is tízes számrendszerben dolgozhassunk
Hogyan alakítunk bináris számod decimálissá?

BINARY 010001001010 DECIMAL .

A válasz 1098.
A BASE rendszerváltozó direkt állításával tetszőleges egyéb számrendszert is használhatunk:

DECIMAL 7 BASE !

Ez a gépet átállítja hetes számrendszerbe. Látszólag valószínűtlennek tűnik a 32-es számrendszer használata. A hasznossága ennek a számrendszernek abban áll, hogy az alfanumerikus karakterek számként tárolhatók, így szavakat tudunk számok formájában letárolni, összehasonlítani és egyéb műveleteket végezni velük.

1.4. Számábrázolás
Előjeles egyszeres pontosságú számok: A Forth-ban a számok általában 16 bites bináris számként vannak kezelve (egyszeres pontosság). Ebből az következik, hogy a számok -32768 és +32767 közöttiek lehetnek. Az eddigi példáinkban csak ilyen számok szerepeltek A " . " operátor a számokat ebben a formábaban értelmezi és olyan számrendszerré alakítva írja ki, amiben éppen dolgozunk.
Előjel nélküli egyszeres pontosságú számok: Ha csak pozitív számokat használunk, akkor lehetőség var a számok 0 és 65535 közti ábrázolására. Az ilyen számok kiíratására egy másik előjel nélküli kiíró utasítást kell használni, ami a következő:

U. (n - - - - )

Dupla pontosságú számok: Ha nagyobb számokra van szükségünk, dupla pontossági számokat kell használnunk. Ezek 32 bites mennyiségként tárolódnak és az előjeles típus -2147483648 és +2147483647 közt lehet, az előjel nélküli pedig 0 és 4294967295 közt. A legtöbb dupla pontosságú művelet "D" előjellel van ellátva Az alábbiakban megadjuk néhány gyakran használt funkció dupla pontossági változatát:

egyszeres pontosság:
dupla pontosság:
.
D.
+
D+
-
D-
*
D*
U.
DU.

Ahhoz, hogy egy dupla pontosságú számot tegyünk a verembe, csak annyit kell tenni, hogy egy tizedespontot teszünk a szám végére (vagy bárhova a számjegyek közé). Ha Forth-ban bármikor tizedespont szerepel a számban (akár több is), ez azt jelenti, hogy dupla pontosságúnak kell azt értelmezni. Például:

1.985
19.85
198.5
1985.
1.9.8.5.
1.985.

Alakban felírt dupla pontosságú számok értéke mindegyiknek 1985.
Példaképp nézzük a következőt:

1234. 2000. D+ D.

Az eredmény: 3234
Helytelen eredményt ad viszont a

66000. .

mert a "." szó csak a szám felső két byte-ját veszik ki a veremből, a másik kettőt "othagyja".

A dupla pontosságért sebességbeli hátránnyal kell megfizetni. Az előjeltelen dupla pontosságú számokat a következő utasítással tudjuk kiíratni:

DU. ( nn - - - - )

Hogyan konvertálunk 16 bites számokat 32 bitessé?
Egyszeres pontosságú számokat dupla pontosságúvá a következő jel segítségével alakíthatjuk át:

S->D (n - - - - nn)

Ha együtt használunk dupla és egyszeres pontosságú számokat, akkor ez gyakran használatos. Például:

1234 S->D D.

1.5. Összehasonlító és logikai műveletek
Hogyan hasonlítunk össze a FORTH-ban két számot? Természetesen az az első, hogy a veremre tesszük őket (így, mivel az operandusokat adjuk meg először, az összehasonlító műveletek írásmódja is postfix). Utána behívjuk valamelyik összehasonlító műveletet. Ezek a következők: < , > , <>, =. Használatukhoz nem árt észben tartani, hogy:

az operandusok sorrendje a postfix írásmódban ugyanaz, mint az infixben, csak a műveleti jel helye változik.

A

2 3 <

művelet eredménye például az lesz, hogy a < egy igaz értékű jelzőt tesz a veremre.

A jelző
A jelző, angolul flag (ejtsd: fleg) arra való, hogy valaminek az igaz vagy hamis voltát jelezze. Ennek a két lehetőségnek az ábrázolására általában - így a FORTH-ban is - számokat használunk. FORTH-ban:

megállapodás szerint a jelző hamis, ha értéke 0, és igaz, ha bármi más.

Az összehasonlító műveletek "jól nevelt" jelzőket szolgáltatnak, amelyek értéke 0 vagy -1.
A TRUE szó hatására -1 kerül a verem tetejére.
A FALSE szó hatására 0 kerül a verem tetejére.

Írjunk egy szót amely arról tudósít, hogy mit gondol a klaviatúránál ülő felhasználó! A tudósítás a veremre tett jelzővel történik. A felhasználó lelkivilágában pedig a következő kérdéssel mélyedünk el: IGEN VAGY NEM?
Most várunk, amíg megnyomja valamelyik billentyűt. A vermen akkor adunk igaz értéket, ha a nagy I betűt nyomta le.

Ehhez meg kell tanulnunk azt a szót, amelyik kivárja, hogy valamelyik billentyűt megnyomják a billentyűzeten, s a billentyűnek megfelelő kódot a veremre teszi. Ez a szó a KEY ( - - - - kód ) (A KEY (ejtsd: kí) angol szó több dolgot is jelent, itt valószínűleg a "billentyű" fordítás a legtalálóbb.) A

KEY

után minden megáll, amíg meg nem nyomunk egy billentyűt. A képernyőn nem látjuk, mit nyomtunk meg (nem írja vissza, mint máskor), csak, hogy az interpreter OK-t küld. A karakterkód a veremben van - EMIT-tel kiírathatjuk a karaktert vagy .-tal (pont) a kódját..
Kicsit kényelmesebb, ha az ember látja is, hogy mit ír. Íme egy program, amely a KEY-hez hasonlóan bevárja egy billentyű lenyomását és a veremre teszi a megfelelő kódot, sőt még ki is írja a karaktert a képernyőre:

: ECHO KEY DUP EMIT ;

Ezek után az igen-nem program (figyelembe véve, hogy az I betű kódja 73) a következő:

: IVN CR ." IGEN,vagy NEM?" ECHO 73 = ;

Az eddig látott adattípusok
Két, már ismert szó:

. ( szám - - - - ) kiírja a vermen talált számot a képernyőre;
EMIT ( kód - - - - ) kiírja a vermen megadott karakterkódnak megfelelő karaktert a képernyőre.

Mindkettő egy elemet használ a veremről. A verem egy eleme egy 16 bites gépi szó. (Gépi szó: 16 jegyű, 2-es számrendszerbeli - azaz bináris - szám, másképpen: egy 16 elemű, 0 és 1 értékeket tartalmazó sorozat.) A . ezen egy 16 bites, előjeles számot feltételez (látni fogjuk, hogyan lehet ennél hosszabb számokkal dolgozni), az EMIT pedig egy karakterkódot, amely egyébként elférne 1 byte-on (8 biten). Az EMIT a 2 byte-ból álló gépi szónak az egyik byte-ját egyszerűen figyelmen kívül hagyja!
Adott esetben például a vermen 42 hever (binárisan; ugyanis a veremben csak gépi ábrázolású számok vannak). Honnan lehet tudni, hogy ez "melyik" 42: előjeles szám, a karakter kódja, vagy - már tudjuk, ez is lehetséges - egy "igaz" jelző?

A FORTH-ban az adatok típusa csak attól függ, hogy milyen műveletet végzünk rajtuk.

A 42 tehát karakterkód, ha az EMIT használja, és előjeles szám, ha a . (pont)

42 EMIT * OK
42 . 42 OK

A + például előjeles számnak tekinti a verem felső két elemét. Ha valaki mégis a KEY-vel kapott karakterkódot felejti ott, az vessen magára.
A szó veremhatásának leírásakor az elemeket jelölő betűk az elemek típusát is közlik. Az eddig látott típusok:

c
karakterkód (character),
n
16 bites, előjeles szám (number),
f
jelző (flag).

Így dokumentáljuk az összehasonlító műveleteket:

<
( n1 n2 - - - - f ) igaz a jelző, ha n1<n2;
>
( n1 n2 - - - - f ) igaz a jelző, ha n1>n2;
<>
( n1 n2 - - - - f ) igaz a jelző, ha n1<>n2;
=
( n1 n2 - - - - f ) igaz a jelző, ha n1=n2;
U<
( n1 n2 - - - - f ) igaz a jelző, ha n1<n2; az előjeleket figyelmen kívül hagyja
D<
( nn1 nn2 - - - - f ) igaz a jelző, ha nn1<nn2; nn1 és nn2 dupla pontosságú érték
D=
( nn1 nn2 - - - - f ) igaz a jelző, ha nn1=nn2; nn1 és nn2 dupla pontosságú érték
DU<
( nn1 nn2 - - - - f ) igaz a jelző, ha nn1<nn2; nn1 és nn2 dupla pontosságú érték; az előjeleket figyelmen kívül hagyja

Miért kell egy jelzőnek "jól neveltnek" lennie?
Írunk egy szót, amely egy jelzővel közli, hogy a veremben talált szám 0 és 9 közé esik-e. A szó neve legyen 1JEGY, veremhatása pedig: ( n - - - - f ). Meg tudjuk már vizsgálni, hogy egy szám kisebb-e 10-nél (egész számokról lévén szó, ez ugyanaz, mintha a "nem nagyobb-e 9-nél" kérdésre válaszolnánk), és azt is, hogy nagyobb-e -1-nél. A két jelzőből egy ún. logikai ÉS művelettel kapjuk meg, hogy a két válasz egyszerre igaz-e.
A logikai ÉS két logikai értékből állít elő egy harmadikat: ha a két érték igaz volt, akkor a művelet eredménye igaz, egyébként hamis.A jelzők közötti ÉS műveletet az

AND

FORTH alapszóval lehet megvalósítani. (AND magyarul: ÉS.)
Vigyázat: az AND a logikai "és" műveletet a két operandus bináris alakjának minden egyes bitjével elvégzi! Ha p1. a veremben 2 és 1 volt, azaz binárisan

0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0

és

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1

akkor a logikai ÉS eredménye:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

vagyis 0 lesz, mivel az egymásnak megfelelő bitek közül az egyik mindig 0. Holott mi a 2-t is, az 1-et is igaz értéknek tekintjük, így az AND-nek a mi logikánk szerint igaz értéket kellett volna adnia.Erről a kényelmetlenségről (amely más alkalommal kényelem) tudnunk kell, pillanatnyilag azonban fölösleges miatta aggódnunk; az összehasonlító műveletek "jól nevelt", 0 vagy 1 értékű jelzővel kedveskednek, amelyekkel nem állhat elő a fenti félrekapcsolás.

: 1JEGY DUP -1 > SWAP 10 < AND ;

A másik fontos művelet a logikai VAGY, amely szintén két logikai értékből ad egy harmadikat. Az eredmény igaz lesz, ha a két logikai érték közül legalább az egyik igaz. Tehát akkor és csak akkor kapunk hamis-at, ha mindkét operaadus hamis volt. Láthatóan ez a VAGY nem felel meg a magyar nyelv VAGY szavának. Magyarul ilyeneket mondunk:

"Vagy láng csap az ódon, vad vármegyeházra vagy itt ül a lelkünk tovább, leigázva",

és ezt úgy értjük, hogy a két lehetőség kizárja egymást. Az előbbi VAGY-ot megengedő VAGY-nak hívjuk, hogy megkülönböztessük a magyar VAGY-ra jobban hasonlító kizáró VAGY-tól. A kizáró VAGY akkor ad igaz eredményt, ha a kapott logikai értékek közül az egyik igaz, a másik nem. VAGY-nak általában a megengedő VAGY-ot hívjuk, ha a kizáró VAGY-ra gondolunk, végigmondjuk a nevét. Ennek megfelelően a két FORTH szó:

OR (VAGY)

és XOR (eXclusive OR, Kizáró VAGY).

Ezek is bitenként működnek, mint az AND, de az összehasonlító műveletektől kapott "jól nevelt" jelzőknél ez nem jelent különbséget.
Nézzük az 1JEGY-gyel ellentétesen működő TOBBJEGY ( n - - - - f ) szót, amely akkor ad igaz jelzőt, ha a kapott szám nem esik 0 és 9 közé (azaz kisebb 0-nál vagy nagyobb 9-nél):

: TOBBJEGY DUP 0 < SWAP 9 > OR ;

Az 1JEGY-gyel ellentétesen működő TOBBJEGY-et persze könnyebb úgy megírni, hogy felhasználjuk az 1JEGY-et. Egy olyan művelet kell hozzá (negálás, komplementálás), amely megváltoztatja a vermen levő jelző jelentését: az igaz jelzőből 0-t, a hamis, azaz 0 értékű jelzőből 1 -et csinál. Ez a szó nem szerepel a szabvány fig-Forth 1.1. alapszavak között, de az IS-FORTH-ban megvan, és megírni sem nehéz:

: NOT 0 = ;

Így a

: TOBBJEGY 1JEGY NOT ;

működése ugyanaz lesz, mint az előbb definiált másik TOBBJEGY-é.

1.6. Gyorsműveletek
A legtöbb számítógépnek gyorsan működő gépi utasítása van arra, hogy valamit 1-gyel növeljen vagy csökkentsen, 2-vel szorozzon vagy osszon, megvizsgálja az előjelét. Ehhez képest az a sorozat, hogy 1 + (tegyél a veremre 1-et, hívd a + szót) lassú és nehézkes. Az ún. "gyorsműveletek" levágják a felesleges kanyarokat, és körülményeskedés nélkül elindítják a megfelelő gépi utasításokat. A gyorsműveletek:

1+
( n - - - - n1 ) eggyel növeli n értékét;
1-
( n - - - - n1 ) eggyel csökkenti n értékét;
2+
( n - - - - n1 ) kettővel növeli n értékét;
2-
( n - - - - n1 ) kettővel csökkenti n értékét;
2*
( n - - - - n1 ) megduplázza n értékét;
2/
( n - - - - n1 ) megfelezi n értékét;
0=
( n - - - - f ) f akkor igaz, ha n = 0;
D0=
(nn - - - - f) A 0= dupla pontosságú alakja;
0<
( n - - - - f ) f akkor igaz, ha n < 0;
0>
( n - - - - f ) f akkor igaz, ha n > 0;

Láthatóan a gyorsműveleteket végző szavak hasonlóan néznek ki, mint az ugyanúgy működő lépésenkénti parancsok, csak egy szóba írjuk az operandust a műveleti jellel; az 1+ szó ugyanazt a műveletet végzi, mint az 1 + sorozat, csak gyorsabban.

1.7. A verem átrendezése
A FORTH szavak elvárják, hogy a vermen a megfelelő sorrendben kapják a működésükhöz szükséges paramétereket. Ez nem mindig egyszerű. Időnként a paraméterek a veremben rossz sorrendben keletkeznek, lehet köztük felesleges, de az is előfordulhat, hogy valamelyikre még egyszer szükség lenne. Az ilyen gondok megoldására szolgálnak a következő szavak:

SWAP ( a b - - - - b a ) megcseréli a két felső elemet;
2SWAP ( a b c d - - - - c d a b ) párban megcseréli a négy (vagy két dupla pontosságú) legfelső elemet
DUP ( a - - - - a a ) megduplázza a legfelső elemet;
2DUP ( a b - - - - a b a b ) megduplázza a két (vagy egy dupla pontosságú) legfelső elemet;
?DUP ( a - - - - a a ) megduplázza a legfelső elemet, ha az nem nulla.
OVER ( a b - - - - a b a ) a második elemről készít egy másolatot a verem tetején;
2OVER ( a b c d - - - - a b c d a b ) a harmadik, negyedik (vagy a második dupla pontosságú) elemről készít másolatot a verem tetején;
ROT ( a b c - - - - b c a ) a harmadik elemet kiszedi alulról, és feldobja a tetőre;
2ROT (a b c d e f - - - - c d e f a b) az ötödik, hatodik (vagy a 3. dupla pontosságú) elemet kiszedi alulról, és feldobja a verem tetejére;
-ROT ( a b c - - - - c a b) a ROT-al ellenkező irányba forgatja a verem legfelső három elemét.
-2ROT   a két első elemet berakja az ötödik, hatodik helyre
DROP ( a - - - - ) eltávolítja a legfelső elemet;
2DROP ( a b - - - - ) eltávolítja a két (vagy egy dupla pontosságú) legfelső elemet.
NOT ( - - - - 0 ) logikai hamis, azaz 0 értéket tesz a verembe

Írjunk például egy olyan szót, amelynek hatása a veremre:
( X Y - - - - Z ), ahol Z = X * Y - (X + Y)

Nem kezdhetjük a dolgot aritmetikai művelettel, hiszen akkor elveszítenénk az x-et meg az y-t a veremről. Valamilyen módon konzerválnunk kell őket. Jó fogás erre az OVER kétszeri alkalmazása. Az egyes lépések mellett feltüntettük, hogy a lépés után mi lesz a veremben; ez a felírási mód igen hasznos, amíg nem válunk a verem rutinos bűvészévé. (Senkit ne zavarjon, hogy a definíciót több sorba írtuk!)

: XY
  OVER
  OVER
  *
  ROT
  ROT
  +
  -
( X Y ) (ez van a legelején a veremben)
( X Y X )
( X Y X Y )
( X Y szorzat )
( X szorzat Y )
( szorzat X Y )
( szorzat összeg )
( Z )

Hasznos, de nem szabványos szavak:
Van néhány veremkezelő FORTH szó, amely nincs benne a FIG FORTH alapszókészletben, az IS-FORTH-ban azonban szerepel:

DEPTH ( - - - - n )
A szó (jelentése: mélység) a verem tetejére teszi a verem elemeinek (a DEPTH végrehajtása előtti) számát.
PICK (n1 - - - - n2)
A verem bármely elemét a verem tetejére másolja. Használatakor a 0 jelenti a verem első elemét, az 1 a másodikat és így tovább. Ha például a negyedik elemet akarjuk legfelülre másolni és a verem tartalma a következő: 1 2 3 4 5, akkor adjuk írjuk be: 3 PICK
Ennek hatására a verem tartalma a következő lesz: 1 2 3 4 5 2
Érdemes megjegyezni, hogy a 0 PICK megegyezik a DUP-pal, az 1 PICK pedig az OVER-rel.
ROLL  
Kiszedi a verem n-edik elemét és a verem tetejére teszi. Hasonló a ROT-hoz, csak itt tetszőleges számú elem megforgatható (rotálható). Tegyük fel, hogy a verem tartalma a következő: 1 2 3 4 5 6 7
A 4 ROLL hatására az eredmény a következő: 1 2 4 5 6 7 3
A 3 ROLL a ROT, a 2 ROLL a SWAP szóval egyenlő hatású.
A veremhatás szokásos jelölésével a ROLL működését csak pontatlanul írhatjuk le.

Verem mélység
Egy helyesen futó program esetén nagyon valószínűtlen, hogy a verem kitöltené a rendszer memóriáját. Ennek ellenére, ha egy ciklusban maradék elemet hagyunk a veremben, előfordulhat a verem túlcsordulása. Az ilyen hibák elkerülésére tanácsos felkészülni. Ezt nagyban segíti a "DEPTH" (mélység) parancsszó. Ha használjuk, akkor a verem tetejére kerül a DEPTH végrehajtása előtti veremmélység. Ezután ez az érték kiíratható a "." operátorral. Ez szintén használható összetettebb ciklusok ellenőrzésére. Ha a ciklusmélység meghalad egy irreális értéket (pl. 2000), akkor ezt a hibát egy adott programrésszel lekezeljük. Ez könnyen megvalósítható egy feltételes vezérlésátadó (IF) utasítással. A jó programozási módszerek elkerülik ezt a típusú problémát.

1.8. Még egy szó a kiírásról
A

."

szó kiírja az utána megadott szöveget egészen a legközelebbi idézőjelig (") A záró idézőjelnek és a ." szónak egy sorban kell lennie! A ." szó csak szódefinícióban alkalmazva működik! Parancsmódban a .( szó használható. Két egyszerű példa:

: LOCSI-FECSI CR ." En vagyok az ENTERPRISE" CR ;

.( En vagyok az ENTERPRISE ) CR

Nem szabad elfeledkezni róla, hogy a ." és .( után szóközt kell hagyni! Így jelezzük, hogy a ." és .( külön szó. A szóköz nem számít bele a kiírandó szövegbe.

Formázott kiírásra ad lehetőséget a

.R ( n m - - - - )

A szó n-et egy m szélességű mezőben jobbra igazítva írja ki.

1.9. Hogyan tároljuk programjainkat?
Eddigi próbálkozásainkban az a bosszantó, hogy a programok szövegei nem maradnak meg, nem lehet őket kijavítva vagy változatlanul újra felhasználni. Megtehetjük, hogy a programokat nem közvetlenül adjuk át az interpreternek, hanem valamilyen adathordozóra, az ún. screen-ekbe írjuk őket; itt bármikor javíthatók vagy elolvastathatók az interpreterrel. A screen (ejtsd: szkrín) szó magyarul képernyőt jelent, amit mi rövidítve kernyőnek fogunk nevezni, de a szakirodalom blokként is említi.
A kernyő a szöveges információ tárolásának eszköze. Egy kernyőben annyi szövegnek van hely, amennyit egyszerre kezelhetünk a képernyőn.; ez a FORTH szabvány szerint 16 sor, egy sorban 64 karakterrel. 16x64 karakter = 1024 byte. Az IS-FORTH-ban is egy kernyő 1024 byte, de a sorok száma nincs korlátozva. A szabvány FORTH egy lemezt egy szektorhalmaznak tekint, amelyet feloszt magának kernyőkre. Az IS-FORTH ún. kernyőfile-okat használ, ezzel lehetővé téve, hogy ugyanazon lemezen más file-okat is tárolhassunk. A file neve a kernyő száma lesz (a kiterjesztése pedig 4TH). Az IS-FORTH nem csak a hagományos blokk-formátumot kezeli, mert azt magnós rendszerben nem kényelmes használni.
Lemezes rendszerben ezek a blokkok a lemezen helyezkednek el. A megfelelő részek szerkesztéskor programíráskor bekerülnek a memóriába (bufferekbe), majd később kiíródnak. Mindez láthatatlan a felhasználó számára. Amikor egy blokkot a BLOCK-kal el akarunk érni, akkor esetleg már az előbbi műveletek miatt bent van memóriában. Ebben az esetben a memóriabufferek tartalma jelenítődik meg szerkesztésre. Ha a kért blokk nincs a memóriában, akkor az egy üres bufferba másolódik. Amennyiben már az összes buffer foglalt, akkor a legrégebben használt visszakerül lemezre, és így felszabadul egy az új blokk számára.
Egy kazettás rendszerben, a blokkoknak mindenképp a bufferekben kell lenniük. A blokkok folyamatos mozgatása, a memória és a szalag közt hosszadalmas és kényelmetlen lenne. Ha nincs elég memória a blokkoknak, hibajelzést kapunk. A CREATE-BUFFERS hívás további helyeket szabadít fel (lásd referencia rész).

Az editor
A programok szerkesztéséhez a beépített szövegszerkesztő használható. A szövegszerkesztőt az

EDIT ( n - - - - )

paranccsal indíthatjuk. Az n szám a szerkeszteni kívánt kernyő sorszáma lesz. (Pl. 1 EDIT parancs kiadása után az 1. kernyőt (blokkot) szerkeszthetjük.) A blokkok sorszáma 1-től 32767-ig terjedhet. A szerkesztés közben a szabványos Exos szerkesztő-funkciókat használhatjuk. Talán nehéz is jelzőket találni arra, hogy ez mennyivel kényelmesebb megoldás, mint a fig-Forth parancssoros(!) szerkesztője... Amennyiben az adott kernyő-ben nem fejeződik be a program, a következő blokkra a

-->

szóval hivatkozhatunk a kernyő végén. Az így összefűzött blokkoknak egymás utáni, növekvő sorrendben kell lenniük.
Amikor a blokk szerkesztését befejeztük, az ESC megnyomásával léphetünk ki a szerkesztőből. Egy rövid szünet után a FORTH visszatér a normál szöveges vagy grafikus képernyőhöz. A begépelt szöveg most a bufferekben foglal helyet, és újra behívható a szerkesztés céljából. Kilépéskor a FORTH megadja a kernyőben felhasznált byte-ok számát. A szerkesztéshez használt buffer 2 K méretű, így előfordulhat, hogy 1 K-nál nagyobb programot írtunk be. Ilyenkor a következő üzenetet kapjuk az ESC lenyomása után:

Block too large: ABORT (Y/N)?

A felhasználónak négy lehetősége van ilyenkor:

Y Az editálás megszakad, és az editor buffer tartalma nem másolódik át egy Forth bufferba. A szerkesztett szöveg elvész.
N A szerkesztés nem fejeződik be, folytathatjuk mintha semmi sem történt volna. (Kitörölhetjük a program egy részét.)
ENTER
A szerkesztés befejeződik, az editor buffer tartalma nem másolódik át a Forth blokk bufferébe. Az editor buffer érintetlen marad, és ugyanannak a blokknak az újbóli editálásakor meglesz a régi szövegünk.
ESC A szerkesztés befejeződik és csak az editor buffer első 1 K-ja másolódik a Forth bufferébe. Az editor buffer érintetlen marad.

Ha bármely esetben leütjük az ESC billentyűt, a blokk buffer megjelölésre kerül mint módosított, és így lemezes rendszeren újraíródhat más blokkok elérése esetén.
A fenti esetekben amikor az editor buffer érintetlen marad, az azt jelenti, hogy ugyanazt a blokkot újra szerkesztve a blokk nem töltődik be az editor bufferba, hanem az utoljára ott lévő szöveget szerkeszthetjük.
A szerkesztés alatt néhány szó lezárja a csatornát és ezzel elvész a bent lévő szöveg. (Pl. TEXT, LORES, stb.)

A szerkesztést bármikor megszakíthatjuk a STOP billentyű megnyomásával. Ez a következő üzenetet eredményezi:

STOP key pressed: ABORT (Y/N)?

Programok kezelése
A kazettára mentéshez / betöltéshez a programnak nevet kell adni. Ezt a nevet a

NAME ( addr - - - - )

Változó tartalmazza. Ennek legegyszerűbb formája:

" név" NAME

Magnós rendszerben, ha nem használjuk a NAME változót, név nélkül is menthetünk.
Ha több buffert akarunk kezelni (így több blokk mentése válik lehetővé egy adott néven - kazettás rendszerben ezt a módot javasolt használni.), akkor a

BUFFERS ON

Szavakat használjuk. Ilyenkor az összes használt blokk egy file-ba kerül mentésre.
Amennyiben minden blokkot külön kívánunk kezelni (lemezes rendszerben) úgy a

BUFFERS OFF

használandó. Ez utóbbi esetben a használt blokkok külön-külön file-ba kerülnek a lemezre. A NAME változót ilyenkor figyelmen kívül hagyja a rendszer, a file-ok nevei a blokkszám lesz, a kiterjesztés pedig .4TH. (Tehát ha pl az 1-2. blokkokat használtuk, 1.4TH és 2.4TH file-ok jönnek létre. A .4TH kiterjesztésű blokk-file-ok 1024 byte-os szöveges állományok, míg a több blokkot tartalmazó formátum 01h típusbájtú EXOS modul.
Ha a FORTH lemezes rendszert detektál induláskor, automatikusan a BUFFERS OFF módit állítja be alapértelmezésnek, magnós konfigurációban pedig a BUFFERS ON módot.

SAVE-BUFFERS ( - - - - ) Adott néven szalagra / lemezre ment minden blokkot.
LOAD-BUFFERS ( - - - - ) Beolvassa szalagról / lemezről a SAVE-BUFFERS utasítással kimentett blokkokat.
EMPTY-BUFFERS ( - - - - ) Felszabadítja az összes buffert, de NEM menti el azokat!

Ha megszerkesztettünk egy kernyőt, akkor a

LOAD ( n - - - - )

szóval átadhatjuk az interpreternek, ha még nincs a memóriában, betölti lemezről a rendszer. (Lefuttathatjuk a programot.) A vermen a kernyő számát kell megadni. A LOAD hatására pontosan ugyanaz történik, mintha begépelnénk a megadott számú kernyőn található szöveget. Ha, mint többnyire, a kernyő definíciókat tartalmaz, akkor a betöltés, vagyis a LOAD hatására megjelennek a szótárban a definiált szavak. Ha több egymás utáni kernyőt akarunk betölteni, akkor a --> szót írjuk a kernyők végére. Betöltéskor ennek hatására az adott kernyő interpretálása abbamarad és a következő kezd el betöltődni. A

;S

szó hatására a kernyő interpretálása megszakad, az interpreter ott folytatja, ahol a LOAD volt.
Egyszerre több egymást utáni blokkot adhatunk át az interpreternek a

THRU ( n m - - - - )

szóval, ami az n és m közöti blokkokat tölti be. A 2 5 THRU utasítás a kettes blokktól az ötösig tölt.
Az interpreternek szinte mindegy, hogy az adott pillanatban billentyűzetről vagy kernyőről kapja-e a vezérlést. Azt a karakterfolyamot, amelyet az interpreter értelmez, az angol nyelvű irodalom input stream-nek nevezi, ezt itt befolyamnak fordítjuk.
Egy kernyő szövegét a

LIST ( n - - - - )

szóval írathatjuk ki a képernyőre.
Megállapodás szerint minden képernyő legfelső sora tartalmaz valamilyen utalást a kernyő tartalmára. Ezt használja fel az

INDEX ( tól ig - - - - )

szó, amely nem szabvány ugyan, de az IS-FORTH alapszókészlet tartalmazza. Az INDEX kilistázza a megadott két szám közötti számú kernyők legfelső sorát és ezzel mintegy tartalomjegyzéket ad a kernyőinkről.

2. Vezérlési Szerkezetek

2.1. A feltételes utasítás
Tudjuk már, hogyan kaphatunk jelzőt egy feltétel igaz vagy hamis voltáról. Most megtanuljuk, hogyan lehet a jelzőket használni.

Az IF ... THEN
(A fig-Forth-ban IF ... ENDIF szerkezetnek hívják). Szerkezete:

(feltétel vizsgálat) IF (az igaz ág szavai) THEN

Az IF ... THEN szerkezet működése: az IF megeszi a verem tetején levő jelzőt. Ha a jelző igaz, az IF és THEN közötti programrész végrehajtódik, ha nem, nem. Mielőtt példát mutatnánk rá, gyorsan szögezzük le:

Valamennyi szerkezet, amely vezérlésátadást tartalmaz (tehát a feltételes és ciklus
képző utasítások), csakis definícióban használható!

Ha legegyszerűbb példának okáért meg szeretnénk vizsgálni, hogy a verem tetején 6 van-e, és ha igen, kiíratjuk a "Helyes" szöveget, akkor a következő szót kell elkészíteni:

: HAT? 6 = IF ." Helyes" THEN ;

Ebben a definícióban a verem kiértékelődik, mint azt már láttuk. Ha a feltétel igaz, akkora a "Helyes" felirat megjelenik. Ha a feltétel hamis, semmilyen tevékenység nem történik.
Írjunk egy olyan szót, amely ha 0 és 9 közötti számot talál a vermen, kiírja a képernyőre: EGYJEGYU . Természetesen felhasználjuk az előző fejezetben definiált 1JEGY ( n - - - - f ) szavunkat. Az új szó veremhatása: ( n - - - - )

: EGYJ 1JEGY IF ." EGYJEGYU" THEN CR ;

fig-Forth-ban létezik a -DUP Szó, ami a verem felső elemét csak akkor duplázza meg, ha az nem nulla. Általában közvetlenül az IF előtt használjuk, hogy megtakarítsuk az egyetlen DROP-ot tartalmazó ELSE ág írását. Írjuk meg a Szót:

: -DUP DUP IF DUP THEN ;

Más magas szintű programnyelvek a THEN-t teljesen másképp használják; mielőtt hagynánk magunkat megkeverni, legjobb, ha az eszünkbe véssük: ez más, a THEN itt ENDIF-et jelent! Az IF-et és THEN-et nem használhatjuk egymás nélkül.

Az ELSE
Ha nemcsak egy adott feltétel teljesülésekor vannak teendőink, hanem az ellenkező esetben is, akkor ilyesféleképpen építjük a programunkat.

IF (itt van, amit akkor kell tenni, ha a jelző igaz);
ELSE (itt van, amit akkor kell tenni, ha nem)
THEN  

Például:

: HAT? 6 = IF ." Helyes" ELSE ." Nem helyes" THEN ;

: EGYJ 1JEGY IF ." EGYJEGYU" ELSE ." NEM EGYJEGYU" THEN CR ;

Olykor az ELSE ágon nincs más feladat, mint a jelzőről feleslegesen készült másolat eltávolítása. Hogy csak ennyiért ne kelljen ELSE ágat írni, van egy olyan FORTH alapszó, amely csak akkor duplázza a verem felső elemét, ha az nem 0.

?DUP

( n - - - - n n, ha n <> 0 )
( n - - - - n, ha n = 0 )

Egymásba skatulyázva
Az IF-ek egymásba is skatulyázhatók. Arra kell figyelni, hogy minden IF-hez tartozzon THEN, különben hibajelzést kapunk. A FORTH természete miatt sok IF-et használhatunk egy IF struktúrán belül.

: EVES-VAGYOK
   DUP
   10 <
   IF
     ." GYEREK"
     DROP
   ELSE
     20 <
     IF ." KAMSZ"
     ELSE ." FELNOTT"
     THEN
   THEN
   CR
;
( n - - - - )
( n n )
( n f1 ) ( ha n<10 )
( n )
( n ) ( a kiírás kész, de a )
    ( veremben "szemét" maradt )
( n ) ( ha n >= 10 )
( f2 )
( ha 20 -nál kisebb)
( ha nem )

Az IF vagy ELSE ágon újabb, feltételtől függő teendőink lehetnek. Például írjuk egy EVES-VAGYOK ( n - - - - ) programot. amely

  • n < 10-re GYEREK,
  • 10 <= n < 20-ra KAMASZ
  • 20 <=n-re FELNŐTT

Választ ír ki. Például a 3 EVES VAGYOK eredménye: GYEREK

(A FORTH-ban mindegy, hol kezdjük írni a sorokat, pusztán az olvashatóság kedvéért érdemes az "összetartozó blokkokat" beljebb kezdeni.)
Figyeljünk rá, hogy a "belső" IF-es szerkezet mindig teljes egészében a "külső"-nek valamelyik ágán van! Ez kulcs ahhoz, hogy egy FORTH szövegben melyik IF, ELSE és THEN tartozik össze. Ha látunk egy ilyesféle programrészletet:

IF A IF B ELSE C IF D THEN THEN ELSE E THEN

akkor ahhoz, hogy kiigazodjunk rajta, keressük meg a legbelső IF-et, és máris tudhatjuk, hogy az ENDIF-ek közül az utána leghamarabb jövő az övé! (Hasonló a helyzet a második legbelső IF-fel.) Láthatóan a második IF-nek ELSE ága is van.

IF
   A
   IF B
   ELSE
      C
      IF D
      THEN
   THEN
ELSE E
THEN

vagyis úgy, hogy az összetartozó IF, ELSE és THEN egymás alá kerül, az adott ágon végrehajtandók pedig egy kicsit beljebb.

2.2. Indexes ciklusok

A visszatérési verem
A FORTH interpreter két vermet használ. Az egyiket már ismerjük: ez a számítási verem vagy adatverem, erre gondolunk, amikor egyszerűen csak veremről beszélünk. A másikat főleg maga az interpreter használja, legtöbbször arra, hogy ott jegyezze meg: egy szó lefuttatása (elugrás a megfelelő címre, az ott talált kód végrehajtása) után hova kell visszatérnie. Ezért visszatérési veremnek, röviden viremnek hívják. A virem programjainkban a verem útban lévő elemeinek átmeneti tárolására használható, ha szem előtt tartjuk, hogy

a virem elemeit minden szónak (amely a virmet nem szándékosan és ravaszul használja a vezérlés módosítására) ugyanúgy kell hagynia, ahogy találta; a virem állapota csak egy-egy szón belül változhat.

A virmet kezelő szavak (itt is, mint mindenütt a számítási veremre vonatkozó veremhatást dokumentáljuk):

>R ( n - - - - ) A verem legfelső elemét áthelyezi a viremre.
R> ( - - - - n ) A virem legfelső elemét áthelyezi a veremre.
R@ ( - - - - n ) A virem legfelső elemét a verembe másolja.

Egy feladat, amelyhez jól jön a virem: írassuk ki a képernyőre a verem 4 eleme közül a legnagyobbat! A verem végső soron változatlan.

: .MAX
   DUP >R
   OVER MAX
   SWAP >R
   >R OVER >R
   MAX
   OVER MAX
   .
   R> R>
;

( n1 n2 n3 n4 - - - - n1 n2 n3 n4 )
( n1 n2 n3 n4 ) ( a virem: n4 )
( n1 n2 n3 max3,4 )
( n1 n2 max3, 4 ) ( a virem: n4 n3 )
( n1 n2 n1 max3,4 ) ( virem változatlan )
( n1 n2 max1,3,4 )
( n1 n2 max )
( kiírás )
(n1 n2 n3 n4) (és a virem is az eredeti )
( állapotba kerül )

Lépegetés egyesével
A

DO ... LOOP

egy újabb szerkezet, amelyet csak szódefinícióban használhatunk. Ciklusszervezésre való; arra, hogy a DO és LOOP közötti programrészt (a ciklusmagot) ismételtessük vele. Ne felejtsük el, hogy a DO ... LOOP ciklusmagja legalább egyszer mindig végrehajtódik! Szerkezete:

(határ) (index) DO (szavak) LOOP

Például írjuk le 10-szer: ORA ALATT NEM ENEKELEK.

: HAZI-FEL 10 0 DO CR ." ORA ALATT NEM ENEKELEK" LOOP ;

A DO ... LOOP ún. indexes ciklus. Ez azt jelenti, hogy van valahol egy ciklusindex - röviden cindex - vagy ciklusszámláló, amely számolja, hogy a ciklusmag hányszor hajtódik végre. A cindex kezdőértékét és az indexhatárnak nevezett végértéket a DO-nak adjuk meg. A DO veremhatása: ( indexhatár kezdőérték - - - - ). A DO ezt a két értéket a viremre teszi. A ciklusmag futása alatt a virmen legfelül a cindex pillanatnyi értéke van, alatta pedig a ciklushatár.

Tudjuk, hogy a FORTH alapszavak egy része FORTH-ban íródott, még "alapabb" szavak felhasználásával. Ilyen a

SPACES ( n - - - - )

is, amely a vermen adott számú szóközt ír a képernyőre. A SPACES lényegében egy DO ... LOOP-ba tett SPACE.

Sok FORTH-ban, így az IS-FORTH-ban is a cindexet az

I

a külső cindexet (a virem harmadik elemét) - azaz nem a legbelső, hanem az eggyel feljebb lévő indexértéket - a

J

szóval lehet előszedni. Mindkét szó veremhatása ( - - - - n ).
Példa:

: SZAMOL 100 0 DO CR I . LOOP ;

Az index számításhoz is használható. A következő példában a számok kétszeresének táblázata jelenik meg:

: TABLA 11 0 DO CR 2 DUP . ." * " I DUP . ." = " * . LOOP ;

Egy klasszikus iskolapélda:

( Szorzotabla )
: SZORZO
  80 -TEXT
  11 1 DO
    11 1 DO
      I J * 4 .R
    LOOP
    CR
  LOOP
;

Írjunk programot, amely megjeleníti a Fibonacci-számsor tetszőleges számú elemét!

( FIB - fibonacci-sor, max 23 elem )
: F ( n1 - - - n2)
  DUP 3 < IF
  ELSE
    1 2 ROT 2 DO
      SWAP OVER +
    LOOP
    SWAP DROP
  THEN
;

: FIB (n - - - - )
  DUP 24 < IF
     0 DO I F . ." " LOOP CR
  ELSE
    ." Tul nagy! " .
  THEN
;

Az F szó a sorozat n1-dik elemét teszi a verembe, a FIB szóval a sorozat tetszőleges számú, de max. 23 elemét jeleníthetjük meg.
Az utolsó példában írjunk olyan PRIM? (n - - - - f) szót, amely igaz értéket ad, ha n prímszám, azaz 1-en és önmagán kívül nincs osztója! A +1 -1 nem számít prímnek.

( ?PRIM - primszam teszt )
: PRIM? ( n --- f )
  DUP 0 > IF
    DUP 2 > IF
      1 OVER 2/ 2+ 2 DO
        OVER I MOD 0=
        IF 0= LEAVE THEN
      LOOP
      SWAP DROP
    ELSE 2 =
    THEN
  ELSE ." Nem termeszetes szam! "
  THEN
;

Az alábbi programmal kilistázhatjuk az 1 és n közötti prímszámokat:

: PRIMTEST ( n --- )
  CR
  1 DO
    I PRIM? IF I . THEN
  LOOP
;

Az eddig tanultakat felhasználva már ki tudjuk számolni Christian Zeller algoritmusával, hogy a megadott dátum a hét hányadik napja.

: WEEKDAY ( nap honap ev --- hetnapja)
  ( 1 hetfo, 2 kedd, ... , 7 vasarnap )
  OVER 3 < IF
    1- SWAP 12 + SWAP
  THEN
  100 /MOD
  DUP 4 / SWAP 2* -
  SWAP DUP 4 / + +
  SWAP 1+ 13 5 */ + +
  2- 7 MOD 1+
;

A WEEKDAY szót felhasználva bármelyik napról megmondhatjuk, hogy a hét hányadik napjára esett:

24 12 2000 WEEKDAY .

Lépegetés tetszőleges értékkel
Ha a ciklusban nem egyesével szeretnénk lépegetni a ciklusban a

DO ... +LOOP

szerkezetet kell használnunk. Ez is indexes ciklus, azonban a +LOOP szóhoz érve annyit adunk hozzá a ciklusszámlálóhoz, amennyit előtte megadtunk a veremben.
Példa:

: HARMAK 101 0 DO CR I . 3 +LOOP ;

Ne felejtsük, hogy a DO ... LOOP, DO ... +LOOP, IF ... ELSE ... THEN szerkezetek tetszés szerint egymásba ágyazhatók, de csak szódefinícióban használhatóak.

Kiszállás a ciklusból
Egy DO ... LOOP ciklust bármikor félbeszakíthatunk - a ciklusváltozó értékétől függetlenül Éppen ezt teszi a

LEAVE

szó. A Például írjunk egy BETU ( - - - - n ) szót, amely egészen az ENTER megnyomásáig karaktereket vár a billentyűzetről (az ENTER kódja 13), de legfeljebb 20-at. A BETU a vermen visszaadja a kapott karakterek számát, a szóközöket nem számítva. A karakterek bevárását és hasonlítgatását egy DO ... LOOP ciklusba tesszük. A ciklus futása alatt mindvégig ott lesz a vermen egy számláló, amelyhez 1-et adunk minden "valódi" karakternél.

: BETU
   0
   20 0 DO
      KEY DUP EMIT
      DUP 13 =
        IF DROP LEAVE
        ELSE
           32 -
           IF 1+
           THEN
        THEN
   LOOP
;

( ez lesz a számláló )
( ciklus 0-tól 20-ig )
( számláló kód )
( ha ENTER volt, )
( a kódra már nincs szükség, kiszállunk )

( ha szóköz volt nem számoljuk )
( ha nem szóköz, növeljük a számlálót )

2.3. Index nélküli ciklusok

A végtelen ciklus (BEGIN ... REPEAT)
Ez a Forth legegyszerűbb ciklusa. A

BEGIN ... REPEAT

szerkezettel a végtelenségig ismételtethetjük a BEGIN és REPEAT közötti ciklusmagot (vagy amíg a STOP billentyűvel meg nem szakítjuk a program futását). Ilyen BEGIN ... REPEAT ciklusban fut maga a FORTH nyelven írt FORTH interpreter is. (Valahogy így fest: BEGIN Olvass be egy sort! Hajtsd végre! REPEAT.)
A végtelen ciklusnak is véget vethetünk, erre való az

EXIT

szó. Nézzük, hogyan működik:

: EXIT R> DROP ;

például amikor így alkalmazzuk:

: BETUK (- - - - )
   BEGIN
      KEY DUP EMIT 13 =
      IF CR EXIT
      THEN
   REPEAT
;

Mikor a BETUK szó elkezd végrehajtódni, a virem tetejére az a cím kerül, ahol a BETUK végrehajtása után az interpreter folytatja a futást. Az EXIT-be való belépéskor az EXIT-ből (a BETUK-be) való visszatérés címe ennek tetejébe ül, de nem üldögél ott sokáig, mert az EXIT cselekménye éppen az, hogy őt onnan ledobja. Az EXIT tehát nem a BETUK-be tér vissza, hanem oda, ahova a BETUK-nek kéne, azaz a BETUK-et hívó szóba. Így lehet egy szó befejezését kikényszeríteni Az EXIT több FORTH változatban alapszó, a fig-Forth 1.1.-ben nem.

BEGIN ... UNTIL
A BEGIN ... UNTIL a két szó közötti ciklusmagot ismételgeti; a ciklusmag minden lefutása után az UNTIL megeszik egy jelzőt a veremről, eldöntendő, hogy visszamenjen-e a BEGIN-re, vagy menjen tovább. A ciklus addig hajtódik végre, ameddig a feltétel be nem következik. Az

UNTIL ( f - - - - )

akkor folytatja a ciklust, ha hamis jelzőt talál.
Példa:

: SZUNET BEGIN KEY 32 = UNTIL ;

A SZUNET szó addig olvassa a billentyűzetet (azaz várakozik), amíg a szóköz billentyűt le nem ütjük.
A következő példában írjunk egy LOG2 (n1 - - - - n2) szót! Ha n1 pozitív, n2 legyen a legkisebb olyan szám, amelyre 2^n2<=n1. ha nem, n2 legyen 0.

( LOG2 - Kettes alapu Logaritmus )
: LOG2 ( n1 --- n2 )
  0 MAX DUP
  IF
    0 >R 1
    BEGIN
      2*
      R> 1+ >R
      OVER OVER <
    UNTIL
    DROP DROP
    R> 1-
  THEN
;


( ha pozitív számot kaptunk )
( a viremben állítjuk elő a kitevőt, a veremben lesz az aktuális 2-hatvány )

( következő hatvány )
( következő kitevő )
( "túlnőttük" n1-et? )
  ( ha igen, vége a ciklusnak )
( hatvány és n1 nem kell )

( ha nem pozitív, jó a 0 )

Kiszállás a ciklus közepén
BEGIN ... WHILE ... REPEAT

A

WHILE ( f - - - - )

A veremről elhasznált jelző segítségével ellenőrzi, nincs-e vége a ciklusnak. A WHILE ( f - - - - ) akkor folytatja a ciklust, ha igaz jelzőt talál, azaz a ciklus addig fut, amíg a feltétel igaz. Az egyetlen megkötés tehát (mint azt az IF esetében már láttuk), hogy a UNTIL végrehajtása előtt a vermen egy logikai érték legyen. Az igaz jelzőre a WHILE továbbengedi a programot: végrehajtódik a WHILE és REPEAT közötti programrész, majd a REPEAT (feltétel nélkül) visszamegy a BEGIN-re. Ha pedig a WHILE hamis jelzőt talál, a program a REPEAT utáni szavakkal folytatódik. Szerkezete:

BEGIN (feltétel) WHILE (szavak) REPEAT

Például:

: DOLLAR BEGIN KEY 32 = WHILE . "$" REPEAT ;

Ez a példa addig írja a csillagokat, amíg a SPACE billentyűt tartjuk nyomva. Ha bármely más billentyűt nyomjuk le, vége a ciklusnak.

: TURELMES
   BEGIN
     CR ." KERSZ SPENOTOT? (I vagy N)?"
     KEY DUP EMIT
     73 -

   WHILE
     CR ." HELYTELEN VALASZ. PROBALJUK UJRA!"
   REPEAT
     CR ." ENNEK IGAZAN ORULOK!"
   CR
;



( a vermen a válasz: egy karakterkód )
( a vermen akkor van 0, azaz "hamis", )
( ha I betűt nyomunk )
( ide akkor jutunk, ha nem I betű volt )
( innen visszamegy a BEGIN-re )
( ide akkor jutunk, ha végre )
( I betűt nyomunk )

Az első két fejezet összegzéseként írjunk egy 21GAME szót, ami 21-es játékot játszik ellenünk. Ebben nulla kezdőértékhez felváltva 1-3-at hozzáadva kell 21 et elérni. Hogy ne legyen unalmas a játék, véletlenszerűen dőljön el, hogy ki kezd. Ehhez készítünk egy READKEY szót, amely egy szám lenyomására vár, majd visszaadja a számot. A Szó csak 1 és a vermen megadott határérték közötti számot fogad el. A játékszabályok kiírásához megelőlegezünk néhány szót a 7.2. fejezetből.
A 21GAME szó a veremben 3 értékkel zsonglőrködik: itt tárolja a halom pillanatnyi értékét, hogy melyik játékos következik (TRUE- játékos, FALSE - számítógép), és hogy mennyit kell hozzáadni az értékhez.

: READKEY
  1+ BEGIN
    KEY DUP 27 = ABORT" Bye!"
    48 - 2DUP > OVER 0 > AND IF
      DUP 48 + EMIT CR SWAP DROP EXIT
    THEN DROP
  REPEAT
;
: 21GAME CLS 102 #GRAPHICS
  0 2 RND 1-
  ." 21 is a two player game." CR
  ." The game is played by choosing a number (1, 2 or 3) to be added to the running total. "
  ." The game is won by the player whose chosen number causes the running total to reach exactly 21."
  248 EMIT
  ." The running total starts at zero. One player will be the computer."
  248 EMIT
  BEGIN
    NOT
    3 INK CR ." The sum is " OVER . CR
    1 INK
    SWAP OVER IF
      ." How many would you like add?"
      ."  (1-3) " 3 READKEY
    ELSE
      ." It is the computer's turn."
      4 OVER 1- 4 MOD -
      DUP 4 = IF 3 RND 1+ MIN THEN
      DUP CR ." Computer adds " . CR
    THEN + SWAP
  OVER 21 < NOT UNTIL
  CR 3 INK
  IF ." Congratulations. You win."
  ELSE ." Bad Luck. Computer wins."
  THEN CR DROP 1 INK 101 #GRAPHICS
;

A példában használjuk a nem szabványos

ABORT"

Szót, ami, ha a vermen lévő jelző igaz, kiírja a megadott üzenetet, kiüríti a vermet, a virmet, és végrehajtja a szabványos QUIT Szót, ami megszakítja a programfutást - a példában az ESC megnyomására történik ez. Ha a jelző hamis, akkor a jelzőt leveszi a veremről és a végrehajtást a következő Szóval folytatja. A szabványos ABORT szó mindezt üzenet kiírása nélkül teszi meg.
A QUIT mind interpreter, mint compiler üzemmódban használható. A QUIT törli a visszatérési vermet (az adatvermet változatlanul hagyja!) és hatására a FORTH interpreter módba kapcsol. Fontos tulajdonsága, hogy ha a QUIT végrehajtódik, akkor a FORTH nem írja ki a szokásos "ok" üzenetét. Ezért például táblázatok kiíratásánál használhatjuk, megakadályozandó az "ok" kiírását a táblázat végén.

2.4. Rekurzió
Ha egy Szó önmagát hívja meg, rekurzióról beszélünk. Bár a rekurzióra a FORTH-ban lehetőségünk van, általában ritkán alkalmazzuk. Ha mégis e módszer mellett döntünk, akkor Szavainkat mindig a legnagyobb figyelemmel tervezzük meg. A rekurziós Szavak a veremszintek legnagyobb fogyasztói. A felhasznált szintek száma mind az adat-, mind a visszatérési vermen minimum a hívási mélységgel egyezik meg. Ez jól működő szó esetén nem okoz gondot, de a programozók nagy előszeretettel készítenek végtelen rekurziókat. Ilyen esetben ne csodálkozzunk, ha a rossz szó betelíti a vermet. Ezért mindig jói gondoljuk át a rekurzív szó működését, biztosítsuk a megfelelő kilépési feltételeket. A rekurzív formula általában helyettesíthető valamilyen ciklusutasítással. A ciklussá való átfogalmazás a matematikai szabályok ismerete nélkül, józan logikával is megtehető.
A fentiekkel nem lebeszélni akartuk az olvasót a rekurzióról, mindössze a módszerben rejlő csapdákra szerettük volna felhívni a figyelmet.

A SMUDGE
A szódefiníciók szótárbeli feje tartalmaz egy úgynevezett smudge-bitet (smudge = piszok, folt). Ha ez a bit egy, akkor a FORTH a szótárbeli kereséskor az adott szót figyelmen kívül hagyja, azaz nem hajlandó felismerni. Ez a bit Szódefiníció közben mindaddig egyes marad, amíg a Szó fordítása sikeresen be nem fejeződik. Ezért nem írhatjuk le, hogy

: NÉV ... NÉV ... ;

mert a NÉV definíciója közben (a smudge-bit egy értéke miatt ) maga a NÉV még nem ismert. Hogy ezt a bitet nullára állítsuk, a SMUDGE szót kell használnunk. A SMUDGE a bitet mindig ellenkezőjére váltja, azaz ha egy volt, akkor nulla lesz és fordítva. Nézzünk használatára egy példát! Írjuk meg az N! faktoriálist számító Szó rekurzív változatát! A faktoriálisszámítás rekurzív képlete a következő:

ha n = 0, n! = 1
ha n > 0, n! = n*(n-1)!

A megoldás egyszerű, mindössze a fenti összefüggéseket kell FORTH-ban megfogalmazni (a negatív számokat, mint lehetséges bemenő paramétereket hagyjuk figyelmen kívül):

: N! ( n --- n! ; n>=0 )
  [ SMUDGE ] ?DUP
  IF DUP 1-
    N! *
  ELSE 1
  THEN
; SMUDGE

Az N! szó elején kiadjuk a SMUDGE utasítást, mégpedig szögletes zárójelek közé írva. A [ és ] szavakról a 6.3. szakaszban szólunk, most elégedjünk meg annyival, hogy a közéjük irt szó fordítás közben hajtódik végre, ahelyett, hogy címe a szótárba kerülne. Így a SMUDGE egyről nullára állítja a smudge-bitét, a FORTH tehát az N! definiálása közben is fel fogja ismerni a szó nevét. Mivel a definíció végén a ; is kiad egy SMUDGE-ot (normális esetben emiatt válik ismertté a szó ), a bit most megint egy lesz, ami ismeretlen szóvá teszi az N!-t. Ezért a ; után még egy SMUDGE szükséges a bit újbóli nullára állítása érdekében.

3. Adattípusok

3.1. Előjeles és előjel nélküli értékek
Etessük meg az interpreterrel a következő parancssort:

65535

A kapott válasz

-1

Pedig mi nem -1-et tettünk a veremre. Vagy mégis? A 65535 16 biten ábrázolva:

1111 1111 1111 1111

A szó a vermen talált számot előjelesnek tekinti, azaz az első bitet előjelbitnek, és ha az 1, akkor a számot negatívnak. A negatív számokat úgy szokás ábrázolni, hogy a számot és ellentettjét ábrázoló két bináris sorozat összege 0 legyen. Ez 16 biten úgy megy, hogy a -1-et és 1-et jelentő szám összege 2^16 azaz:

1 0000 0000 0000 0000

(a magas helyértékű 1 már kicsordul a tizenhat bitből, maradnak a 0 jegyek). És valóban:

 
1111 1111 1111 1111
+
1
 
1 0000 0000 0000 0000

vagy, hogy az otthonosabb tízes számrendszernél maradjunk:

65535 + 1 = 65536= 2^16.

Mi lesz a túl nagy számokkal?
Látjuk, hogy 16 biten 2^16-1 = 65535-nél nagyobb szám nem létezhet. A nagyobb értékek első jegyei egyszerűen "lecsordulnak", elvesznek:

65536 = 0

(a bináris ábrázolás: 1 0000 0000 0000 0000).

65537 = 1

(a bináris ábrázolás: 1 0000 0000 0000 0001) stb. Hasonlóképpen a

35537 30000 + .

hatására is 1 a válasz. A FORTH a túlcsordulást nem kezeli, magunknak kell rá figyelni.

3.2. A duplaszavak

Ha egy szám nem fér el egy szóban, akkor ábrázolhatjuk kettőben, azaz: egy 32 bites duplaszóban. A duplaszó a vermen úgy helyezkedik el, hogy a magasabb helyértékű szava (az "eleje") van felül. Tehát pl. az 1 0 sorozattal egy 1 értékű duplaszót tettünk a verembe.
Erről a

D. ( d - - - - )

szó segítségével győződhetünk meg; ez kiírja a képernyőre a duplaszót, amely a verem két felső eleméből áll:

1 0 D. = 1
-1 -1 D. = -1
-1 0 D. = 65535

A kiíratott duplaszavak binárisan a következő ábrán láthatók.

A D. szó nevében és a veremhatás jelölésében a d betű a doubleword angol szó rövidítése, bár akár a magyar "duplaszó" változaté is lehetne. Kevésbé körmönfont módon is lehet a verembe duplaszavakat csalni. Ugyanis:

azokat a számokat, amelyekben tizedespont van, az interpreter duplaszóba konvertálja.

Például:

100. D. = 100
10.0 D. = 100
10. D. = 10

Látjuk, hogy a veremre tett duplaszó értékét a tizedespont helye nem befolyásolja.
A duplaszavak éppúgy lehetnek előjelesek vagy előjel nélküliek, mint a szavak. Az előjel nélküli duplaszó jelölése a veremhatás leírásánál: ud. A veremhatás leírásában a d, ud jelölés duplaszót, azaz két elemet jelent.

Egy álcázott duplaszavas művelet
Szorzást és osztást kombinál a következő két szó

/* ( n1 n2 n3 - - - - n4 ),

ahol n4 = (n1 * n2) / n3. Az n1*n2 szorzatot a */ duplaszóban tárolja, így nem fordulhat elő, hogy a szorzás túlcsordulást eredményezzen. Ugyanezt teszi a

*/MOD ( n1 n2 n3 - - - - maradék hányados )

szó is, csak megkapjuk az osztás hányadosát is. Pl.:

2 5 3 */MOD . .

Az eredmény 3 és 1 lesz. A kettő és az öt összeszorzódik, és 10 keletkezik mint részeredmény (duplapontosságú formában tárolva). Ezután a 10 elosztódik 3-mal és a maradék bekerül a verembe (1). Ezután a hányados kerül a verembe, ami a legfelső elem lesz.
Miben különbözik például a */ a * /-től? A magyarázat egyszerű a */ a szorzás elvégzése után az eredményt 32 biten tárolja, ami azt jelenti, hogy a szám akár 2^31 is lehet, míg a * / szekvencia esetén az esetleges átvitelek figyelmen kívül maradnak. Ugyanezek vonatkoznak értelemszerűen a */MOD-ra is.
Lássunk egy példát! Számítsuk ki 3800-nak a 43 százalékát!

3800 43 100 */ .

az eredmény 1634. Míg a második változat:

3800 43 * 100 / .

323-at ad eredményül. A második megoldás a túlcsordulás miatt hibás.
Az

UM*

két 16 bites szám előjeltelen szorzását jelenti, az eredmény pedig egy 32 bites előjeltelen szám. Pl.:

1200 2000 UM* D.

Az eredmény 2400000.
Az

UM/MOD

eloszt egy 32 bites számot egy 16 bites számmal és a maradékot a veremben hagyja, a legfelső elem pedig a hányados lesz. Pl.:

1234567. 666 UM/MOD . .

Az eredmény 1853 és 469 lesz.

Az előjel nélküli, a duplaszavas és az előjel nélküli duplaszavas értékeket kezelő szavak:

DU< ( u1 u2 - - - - f ) Előjel nélküli dupla pontosságú értékek összehasonlítása. f igaz, ha u1<u2
D. ( d - - - - ) Kiírja a vermen talált duplaszót, utána szóközt.
D.R ( d n - - - - ) Kiírja a vermen talált duplaszót, egy n szélességű mezőben.
D+ ( d1 d2 - - - - d ) Duplaszavak (duplaszavas) összegét adja.
D- ( d1 d2 - - - - d ) Duplaszavak (duplaszavas) különbségét adja.
D* ( d1 d2 - - - -  d) Duplaszavak (duplaszavas) szorzatát adja.
D2/ (d1 - - - - d) A 2/ operátor duplaszavas változata, azaz a 32 bites számot megfelezi.
DABS ( d - - - - +d ) Duplaszó abszolút értékét adja.
DMAX ( d1 d2 - - - - max ) A két duplaszó közül a nagyobbat teszi a veremre.
DMIN ( d1 d2 - - - - min ) A két duplaszó közül a kisebbet teszi a veremre.
DNEGATE ( d - - - -  -d) Duplaszót negál

Jelölések:

E két utóbbi a vermen két elemet jelöl.

3.3. Ismerkedés a memóriával
A FORTH rendszert leggyakrabban 8-bites mikroszámítógépeken valósítják meg, így a memóriakezelés is ehhez a struktúrához igazodik. A memória egyes rekeszeit 16 bites címen érhetjük el (0...65535), az egyes rekeszek pedig 8 bites adatot tartalmazhatnak (1 bájt, értéke 0...255 lehet).

Byte-os műveletek
A számítógép memóriája számozott és számukkal címezhető byte-ok sorozata. A FORTH memóriakezelő szavai ezeket a byte-okat szabadon olvashatóvá és felülírhatóvá teszik, függetlenül attól, hogy az interpreter kódjának közepén, a veremben vagy pedig valami "szelídebb" helyen vannak. A programozó dolga, hogy ne nyúlkáljon rossz helyekre. Egy jó cím a nyúlkálásra, ahol átmenetileg adatot tárolhatunk: az ún. pad (ejtsd ped), ez egy 255 byte méretű tárolóterület, amely különösen jól használható pl. szövegek kezelésére. A pad szó jegyzettömböt, blokkot jelent. A pad címét a

PAD ( - - - - cím )

szó szolgáltatja.
Vigyázat! A pad a szótárterület fölött van, mégpedig állandó távolságra a szótár tetejétől (az utoljára definiált szó utáni byte-tól). Ezért mikor új szavakat definiálunk vagy felejtjük a régieket, a pad helye változik. Legjobb, ha a pad-be tett adatokat még ugyanabban a szóban felhasználjuk, amelyikben odatettük őket. Tartós adatmegőrzésre a változók szolgálnak, amelyekről a későbbiekben lesz szó.
A memória byte-jait kezelő szavak:

C@ ( cím - - - - c ) a cím-en tárolt byte tartalmát adja meg;
C! ( c cím - - - - ) a c értéket a cím-en levő byte-on tárolja (a byte eddigi tartalma elvész).

A veremhatás leírásában a cím memóriacímet jelent; típusára nézve ez egyszavas, előjel nélküli érték.

Szavas és duplaszavas műveletek
A memóriában nemcsak 8 bites, azaz byte-os értékeket tartunk, hanem szavasakat és duplaszavasakat is. Az erre szolgáló szavak:

@ ( cím - - - - tartalom ) a cím-en és az utána következő címen levő 2 byte-ot, azaz szót teszi a veremre;
! ( n cím - - - - ) az n 16 bites értéket a cím-en meg az utána következő byte-on tárolja (a 2 byte előző tartalma elvész);
2@ ( cím d-tartalom - - - - ) a cím-en és az utána következő 3 byte-on tárolt duplaszót teszi a verembe.
2! ( d cím - - - - ) a duplaszót a cím-en meg az utána következő 3 byte-on tárolja (a 4 byte előző tartalma elvész).

Tehát a

0 PAD 2!

sorral a PAD, PAD+1, PAD+2 és PAD+3 címeken levő byte-okat nulláztuk.
Speciális memóriaíró Szó a

+! ( n cím - - - - )

Ez a ! Szóval megegyező bemenő paramétereket vár, azonban a 16 bites adatot a címen lévő 16 bites adathoz hozzáadja, és az összeget helyezi el a cím...cím+1-re.
Egy címen lévő érték eggyel növelése vagy csökkentése olyan gyakori művelet, hogy gyorsművelet van rá:

1+! ( cím - - - - ) A címen lévő 16 bites szám értékét eggyel növeli;
1-! ( cím - - - - ) a címen lévő 16 bites szám értékét eggyel csökkenti.

3.4. Változók és konstansok

Egy biztos hely: a változó
Vannak adatok, amelyekre időnként szükség lehet (a verem fenekének címe, a szótár tetejének címe, saját számlálóink), de nem mindig. Az ilyeneket nem tarthatjuk a vermen, mert belebolondulnánk, ha még ezeket is kerülgetnünk kéne, sem a padben, amelynek időnként megváltozik a címe. Erre vannak a FORTH-ban (is) a változók, amelyek ugyanúgy szótári szavak, mint az aritmetika vagy a ciklusszervezés szavai.
A Forth változó kétféle formában létezik, egyszeres és dupla pontosságú. Minden változót deklarálni kel használat előtt. Saját 16 bites változókat a

VARIABLE

szóval definiálhatunk. A változó nevét közvetlenül a VARIABLE után kell megadnunk: Ha egy OSSZEG nevű egyszeres pontosságú változót akarunk deklarálni, a következőt gépeljük be:

VARIABLE OSSZEG

szóval definiálhatunk. Ezzel az OSSZEG szó megjelenik a szótárunkban (mint arról VLIST-tel egy pillanat alatt meggyőződhetünk).
A deklarált változóra csak azután hivatkozhatunk, ha értéket adunk neki. Ez a következőképp tehető meg:

15 OSSZEG !

A FORTH változók úgy működnek, hogy a veremre tesznek egy címet; ezen a címen tárolhatjuk az értéket, amelynek megőrzésére a változót létrehoztuk. Amikor az ÖSSZEG szó hajtódik végre, akkor a változó címe tárolódik a veremben (2*16 bit). A "!" operátor ("tárolás"-nak hívjuk) letárolja a harmadik veremelemet (most 15) a verem tetején lévő címre (az OSSZEG címe).
Egy változó kinullázásának egyszerű módja a következő:

OSSZEG 0!

A 0! szó nullát tesz az adott címre, jelen esetben a változó címére.
Változó értékének megjelenítése a

?

szóval lehetséges. Ahhoz, hogy az OSSZEG változót megjelenítsünk, a következőt írjuk be:

OSSZEG ?

Az OSSZEG tartalma kiíródik. A tartalom nem változik. A "?" nem használható számításoknál. Ez az operátor változatlanul hagyja a vermet.
Mielőtt egy változóval műveletet végeznénk, be kell tennünk azt a verembe. Erre szolgál a

@

azaz "felvétel" szó. A változó nevének végrehajtása esetén a változó címe kerül a verembe. A felvevő operátor a változó tartalmát teszi a verem tetejére. Például:

28 OSSZEG !
OSSZEG @
.
( az OSSZEG beallitasa 28-ra )
( felveszi a tartalmat )
(megjeleniti a verem tetejet )

Ez 28-at kell hogy kiírjon, azaz az ÖSSZEG tartalmát.
Létezik néhány, változókat kezelő operátor, ami nagyon hasznos:

Változók inkrementálása:

1+!

Az 1+! jelsorozat jelentése "egy hozzáadása". Ez egy gyakran használt utasítás grafikák, eredménye változtatásakor. A megnevezett változó tartalmához hozzáad egyet.
Pl.: OSSZEG 1+!

Változó dekrementálása:

1-!

Az 1-! jeleket "egy levonásá"-nak hívjuk. Ez eggyel csökkenti a változó értékét.
Pl: OSSZEG 1-!

Hozzáadás változókhoz:

+!

Nagyon gyakran van szükség számok összegzésére. Ez a funkció hasonló feladatokra készült. Ha 23-at akarunk hozzáadni az OSSZEG nevű változóhoz, a következőt kell beírnunk: 23 OSSZEG +!
A +!-t valahogy így írhatnánk meg, ha még nem lenne:

: +! ( n cím - - - - )
   SWAP OVER
   @ +
   SWAP
   !
;

Kivonás változókból:

-!

Ez kivonja a megnevezett változóból a verem legfelső elemét. Például, ha 12-őt akarunk kivonni az OSSZEG tartalmából, azt az alábbiak szerint tehetjük meg: 12 OSSZEG -!

A dupla pontosságú változók a fentiekhez hasonló módon működnek. A következő sor egy 32 bites változót deklarál ROBERT néven.

2VARIABLE ROBERT

Egy 32 bites verem elem tárolásához a

2!

operátort használjuk:

21346. ROBERT 2!

Ha ezt fel akarjuk venni a verembe, a

2@

utasítást használjuk. Az alábbi példa felveszi és megjeleníti a ROBERT tartalmát:

ROBERT 2& D.

8 bites változó kezelés: egy változó alsó nyolc bitje (alsó byte) a

C@

és a

C!

operátorok segítségével érhető el. A fő előnyük ezen operátoroknak a gyorsaság. 8 bit elérése egy 8-bites gépen kevesebb időt vesz igénybe, mint 16 vagy 32 bit elérése. Ezek az operátorok ugyanúgy működnek, mint a ! vagy a @.

Alkalmazási példa
Képzeljük el például, hogy listát készítünk a nyomtatón (a kimeneti csatorna állításával). Hogy mifélét, azt nem tudni, egy biztos: 60 sornak kell egy oldalon lennie. Új sort a listázóprogram mindig a CR szóval fog kezdeni. Definiáljuk át úgy a CR szót, hogy 60 soronként lapot dobjon (12 EMIT), és a laptetőre írja ki, hogy hányadik lapnál tart! Mivel nem tudjuk, mi történik listázás közben a veremmel, meg egyszerűbb is így, a sorokat és lapokat egy-egy változóban számláljuk:

VARIABLE SORSZ
VARIABLE LAPSZ
0 SORSZ !
0 LAPSZ !

Mindkét változót eggyel kell majd növelgetnünk. Eddigi ismereteink szerint egy változó tartalmát valahogy így növelnénk 1 -gyel:

SORSZ @
1+
SORSZ !

Mint már tudjuk a változókhoz (vagy bármely memóriaszóhoz) való hozzáadást egyszerűsítő FORTH alapszó a

+! ( n cím - - - )

amely a címen levő egyszavas értékhez hozzáadja n-et, és az összeget a cím-en tárolja. Ezzel növelhetjük a SORSZ tartalmát:

1 SORSZ +!

Sorok, lapok és egyebek számolgatásánál gyakran kell a változókat éppen 1-gyel növelni. Talán érdemes rá külön szót bevezetni, amely felhasználja az 1+ gyorsműveletet:

: INC ( cím - - - - )
DUP @
1+ SWAP !
;

A nyomtató programnak a következő dolgokat kell 60 sor kiírása után tenni:

: LAPDOB ( - - - - )
   12 EMIT   ( lapdobás )
   0 SORSZ ! ( a sorszámlálás újrakezdése )
   LAPSZ @ . (lapszám kiírása )
   ." lap" CR
   LAPSZ INC ( a lapszámláló növelése )
;

A kiírást mindjárt lapdobással fogjuk kezdeni; ez a megfelelő pillanat a LAPSZ kezdőértékének beállítására is:

: ELSO-LAPDOB ( - - - - )
   1 LAPSZ !
   LAPDOB ;

A listázóprogram dolga lesz, hogy a listázást az ELSO-LAPDOB hívásával kezdje. Az átdefiniált CR:

: CR ( - - - - )
   SORSZ INC (növeljük a sorszámlálót )
   SORSZ @ 60 = ( lap vége? )
   IF LAPDOB ELSE CR THEN
;

A FORTH interpreter maga is használ változókat, ezek az ún. rendszerváltozók.

Példa a rendszerváltozóra: A BASE
A BASE (ejtsd: béz, jelentése: alap) változó a kiíráskor, beolvasáskor használt számrendszer alapszámát tartalmazza. Eddig ez mindig 10 volt. Ha mondjuk a 2-es számrendszerben akarunk dolgozni, akkor ennek a változónak az értékét 2-re állítjuk:

2 BASE !

Próbálgassuk, milyen az élet kettes számrendszerben:

1 1 + = 10

Nem, a 9-es számjegyet most nem veszi be az interpreter "gyomra", hiszen kettes számrendszerben vagyunk, ahol ennek semmi értelme. Meglepetten tapasztalja viszont a kísérletező Olvasó, hogy a 2, 3 számok - amelyek szintén "értelmetlenek", működnek:

2 . = 10

Ennek az az oka, hogy az első néhány szám benne van a szótárban; ezeket gyakran használjuk. Az interpreter munkáját gyorsítja, hogy "készen találja" őket, nem kell konvertálnia. A leggyakrabban használt két számrendszer a 10-es és a 16-os; ezek beállítására vannak FORTH alapszavak. Az is IS-FORTH-ban ezen kívül a 8-as és a 2-es számrendszerre is van szava. Működésük a forrásszövegükből világos:

: DECIMAL 10 BASE ! ;
: HEX 16 BASE ! ;

Hogyan tudhatjuk meg, hogy az adott pillanatban mi a konverzió alapszáma? Egyszerű, mondhatnánk meggondolatlanul, csak elő kell venni a változóból az alapszámot és kiíratni:

BASE @ .

Mit tudtunk meg ebből? Hogy a számrendszer alapszámát az adott rendszerben egy 1 és egy 0 jegy ábrázolja, vagyis hogy megegyezik a számrendszer alapszámával. Többre megyünk, ha az eggyel kisebb számot íratjuk ki:

BASE @ 1- .

Ebből a válaszból tudhatjuk, hogy pl. F, azaz 15 a legkisebb egyjegyű szám, ezek szerint a 16-os számrendszerben vagyunk.
Állítsuk most 5-re az alapszámot, és definiáljunk új szót az 5-ös számrendszerben:

5 BASE !
: KIIR 10 .
KIIR

Mi történik, ha a KIIR-t a 10-es számrendszerben próbáljuk ki?

DECIMAL KIIR

A kapott válasz. 5. A szótári szavakban binárisan vannak a számok, a KIIR definíciójakor
0000 0000 0000 0101
került oda, ez a tízes számrendszerben 5.

Írjunk egy .BASE ( - - - - ) szót, amely a BASE tartalmát decimálisan írja a képernyőre, de nem rontja el! (vagy pontosabban: elrontás után visszaállítja.)

: .BASE ( - - - - )
   BASE @ DUP
   DECIMAL
   .
   BASE !
;

Példa a rendszerváltozóra: Az OUT
Tudjuk már, hogy a képernyőre író FORTH alapszavak mind az EMIT-tel írják ki az egyes karaktereket. Az EMIT eggyel növeli az OUT (ejtsd: aut, jelentése: ki) változó tartalmát. Így, ha az OUT-ot nullázzuk, utána bármikor megtudhatjuk, hogy a nullázás óta hány karakter került a képernyőre.
Képzeljük el például, hogy egyforma sorokat akarunk egymás alá írni, amelyek két (előre nem tudni, milyen hosszú) számból állnak; azt kívánjuk, hogy az első szám a képernyősor 3., a második pedig a 13. pozíciójában kezdődjék. Feltesszük, hogy a két szám a vermen van. Megírjuk azt a szót, amely a fentiek szerint írja ki őket:

: TABULAL
   CR
   0 OUT !
   3 SPACES .
   13 OUT @ -
   SPACES
   .
;
( n1 n2 - - - - )

( " nullázzuk" az OUT változót )
( 3 szóköz, kiírjuk az első számot )
( 13-ra egészítjük ki a kiírt karakterek számát)

( kiírjuk a második számot )

Konstansok
Ha egy értéket úgy akarunk megőrizni a szótárban, hogy soha nem változtatjuk, akkor egyszerűbb olyan szavakat használni, amelyek az értéknek nem a címét, hanem magát az értéket adják a vermen. Ezek a konstansok. Ilyenek például az 1, 2, 3 szavak, amelyek benne vannak a szótálban és az 1, 2, 3 értéket teszik a veremre.
A Forth kétféle konstanst ismer: 16 és 32 bitest. Leggyakrabban az egyszeres pontosság (16 bit) használatos. A konstansokat használat előtt deklarálni kell. A definíció a következő szóval történik:

CONSTANT ( n - - - - )

A dolog meglehetősen hasonlít a változók definiálására: (mennyiség) CONSTANT (név). Például:

42 CONSTANT CSIL

ezzel a CSIL nevű konstans szót definiáltuk. A CSIL 42-t tesz a veremre. A következő példa ezek után már egyértelmű:

365 CONSTANT NAPOK

Miután ez bekerül a Forth szótárába (például úgy, hogy begépeljük), bármikor hivatkozunk a NAPOK szóra, a rendszer a verembe 365-öt helyez el későbbi használat céljára.

Példa:
Ez az egyszerű program pénznemeket konvertál. Jelenleg mondjuk egy angol font 67 forint. Ha fontból forintot akarunk számolni, egyszerűen az átváltási számmal meg kell szorozni a fontok számát. Az átváltási arány konstans, ugyanis a program futása alatt nem változik azért annyira nem gyors az infláció). Az alkalmazásunk a következőképp fog kinézni:

67 CONSTANT FORINT
: FONT CR FORINT * . ." forint" CR ;

Ha 15 fontot akarunk forinttá számolni, a következő: kell beütni:

15 FONT

A válasz a következő lesz: 1085 forint.
Ha egy nagyon nagy alkalmazásunk van, ami sok helyen használja az átváltást és megváltozik időközben az árfolyam, akkor egyszerűen a deklarációt kell megváltoztatni a programban. Ha szükséges, egy konstans egy alkalmazáson belül is újradeklarálható.

Dupla pontosságú konstansok
32 bites konstanst egy másik szóval deklarálhatunk, melynek működése pontosan ugyanolyan, mint az előbb megismert CONSTANT. A következőképp használatos:

234000. 2CONSTANT NAGYSZAM

Ezután NAGYSZAM végrehajtása esetén 234000 kerül a verem tetejére duplapontosságú formában. A szám után lévő pont jelzi, hogy duplapontosságú számról van szó.

3.5. Hol változik a változó? Ismerkedés a szótárral
Azt már tudjuk, hogy a FORTH-ban a szótár a szavak tárolására szolgál. Szerepe kettős. Egyrészt compiler módban az a funkciója, hogy a FORTH megkeresse a szódefiníció törzsében levő szavakat, valamint meghatározza címüket. Másrészt interpreter módban a FORTH a szótárban levő szó "meghatározás" részére adja a vezérlést, ami a szó végrehajtását jelenti. Egy szó címén azt a címet értjük, ahol a szótári bejegyzés kezdődik az adott szóról. Emellett a szóhoz további címeket is rendelhetünk. A szó a szótárban két funkcionálisan különálló részre osztható:

A szótár maga két részből áll:

  1. Alap- vagy beépített szótár. Ez tartalmazza a konkrét algoritmusokat. Az alapszótár is további két részre bontható:
    - Gépi kódban irt szavak (primitívák). Ezek képezik a FORTH alapját.
    - FORTH-ban irt szavak. Ezek mind gépi kódban, mind FORTH-ban megírt szavakat fel használhatnak.
  2. Felhasználó által definiált szótár. Ezen szótár szavai FORTH-ban készülnek (bár a beépített assembler (segítségével gépi kódú szavak is létrehozhatók).

Az alap- és a felhasználói szótár az esetek többségében teljesen egyenértékű a FORTH szempontjából. Néhány esetben mégis a FORTH a két szótárt eltérően értelmezi.

A szótármutató
A FORTH programozás során a szótár mérete állandóan változik. Hogy éppen hol van a szótár "teteje", a szótár utáni első szabad hely, ahova a következő szótári szó kerül majd, azt az interpreter a DP (Dictionary Pointer, ejtsd: diksöneri pointer, jelentése: szótármutató) rendszerváltozóban tartja. A DP változó értékét sokat használjuk, ezért kiolvasására külön alapszó van:

HERE ( - - - - cím )

A szó forrásszövege:

: HERE ( - - - - cím ) DP @ ;

Már korábban volt róla szó, hogy a pad a szótár tetejétől állandó távolságra van. A jelenséget megmagyarázza a PAD szó forrásszövege:

: PAD ( - - - - cím ) HERE 68 + ;

Munkánk során előfordulhat, hogy a szótár eddig el készült részét meg szeretnénk védeni a véletlen kitörléstől. A DP változó bármikor kiolvasható a HERE szóval, így mindig megtudhatjuk azt, hogy a FORTH hol tart a szótár betöltésével. A HERE szó segítségével például megállapíthatjuk, hogy a CS Szó hány byte-ot foglal el a szótárban.

HERE . 10478 ok
: CS 42 EMIT ;
HERE . 10493 ok

Az első sorban kiírattuk a szótármutató értékét. Ez 10478 volt. A következő sorban definiáltuk a CS szót, majd a harmadik sorban ismét kiírattuk a DP értékét. A szódefiníció után ez 10493 lett. A két címet kivontuk egymásból és eredményül 15-öt kaptunk. Ez azt jelenti, hogy a CS szó a szótárban 15 bájt helyet foglal el. Láttuk tehát, hogyan férhetünk hozzá a szótármutató (D ) aktuális értékéhez. Ezt, valamint a FORGET szó egy eddig nem említett tulajdonságát fogjuk felhasználni szavaink véletlen törlés elleni védelmére.
A FORGET ugyanis úgy működik, hogy egy szót (és persze az azt követőeket) csak akkor törli a szótárból, ha annak címe nagyobb annál a címnél, amit a FORTH a FENCE (kerítés) nevű változójában tárol. A FENCE a rendszer indításakor az alapszótár végére, tehát a majdani felhasználói szótár elejére mutat. Ily módon - teltéve, hogy a FENCE értékét nem változtattuk meg - akár az egész felhasználói szótárat kitörölhetjük egyetlen FORGET-tel. ( Ez akkor történik, ha az általunk definiált szótár első szavát "felejtetjük el"). De nézzük meg, mi történik, ha a következő módon járunk el:

: ECHO KEY DUP EMIT ;
HERE FENCE !

Az első sorban egy szódefiníció szerepel. Tegyük fel, hogy az ECHO Szót kipróbáltuk, jónak találtuk és ezek után meg szeretnénk védeni a véletlen kitörléstől. Erre szolgál a második sor: A HERE a veremre teszi a szótármutató (DP) értékét, amelyet ezután beírunk a FENCE változóba (FENCE !). Így az ECHO és előtte definiált szavakra már hiába adjuk ki a FORGET utasítást, az hatástalan marad, hiszen címeik kisebbek a FENCE-ben tárolt címnél. A FENCE beállítása után definiált szavak persze bármikor kitörölhetőek. Ha ezeket is meg akarjuk védeni, csak a HERE FENCE ! sort kell definíciójuk után megismételni. Természetesen, ha a védelmet fel szeretnénk oldani, a FENCE-t eredeti értékére kell visszaállítanunk. Ezért érdemes rendszerindításkor a FENCE-ben tárolt címet megjegyezni.

Mi van a szótárban?
Egy szótárelem a következő részekből áll:

A paramétermező - teljesen önkényesen "paramzőnek" rövidítjük - fizikailag a szó legvégén van. Így változó definiálása után a szótármutató pontosan az új változó paramzője mögé mutat. Ha ekkor a szótármutató értékét 1-gyel, 2-vel stb. növeljük, akkor ezzel megtoldjuk a változó paramzőjét 1, 2 stb. byte-tal. A szótármutató növelésére szolgáló alapszó, az

ALLOT ( n - - - - )

forrásszövege:

: ALLOT ( n - - - - ) DP +! ;

Az ALLOT szó ( n --- ) n darab byte-ot lefoglal az operatív tár felhasználói szótár utáni részében. Végrehatása után a DP rendszerváltozó a lefoglalt terület utáni első szabad byte-ra mutat.

Hosszú változók
Ezzel a kezünkben van a lehetőség arra, hogy duplaszavas vagy hosszabb változókat definiáljunk.

87 VARIABLE DUPLA

88 HERE !

2 ALLOT

A DUPLA tehát két szavas, az első szó kezdőértéke 87, a másodiké 88. A paramzőbe való kezdőérték-tárolásra, egyúttal a paramző "tágítására" valók a következő alapszavak:

, ( n - - - - )

(vesző) Az n-et a HERE címen, egy szón tárolja, a szótármutató (DP) értékét 2-vel növeli, így az a következő szabad helyre mutat.

C, ( C - - - - )

Az n-et a HERE címen, 1 byte-on tárolja, a szótármutató (DP) értékét 1-gyel növeli.

A két szó forrásszövege:

: , ( n - - - - )
   HERE !
   2 ALLOT;

: C, ( c - - - - )
   HERE C!
   1 ALLOT ;

Végül következzen négy, a változók használatát segítő szó:

? ( addr - - - - ) A változó címéről kiírja a változó értékét.
0! ( addr - - - - ) A változót nullázza.

1+!

( addr - - - - ) A változó értékét eggyel növeli.
1-! ( addr - - - - ) A változó értékét eggyel csökkenti.

3.6. A FORTH-füzér és a WORD

A FORTH-füzérek (karakterláncok)
Kezdetben a szövegek kezelésének megismerése és megszokása egy kis türelmet vesz igénybe a Forth-nál. Nincs például az INKEY$-nak megfelelő operátor. A legtöbb szöveg konstansok és változók formájában van jelen. A szövegek, számok, amelyeket beolvasunk vagy kiírunk, a memóriában karakterfüzéreket alkotnak. (A karakterfüzér angol elnevezése string, ejtsd: sztring.) Egy ilyen füzérrel akkor tudunk dolgozni, ha ismerjük a kezdőcímét és a hosszát. A FORTH-ban gyakori tárolási mód: a füzért az ún. hosszúságbyte-tal kezdjük, amely a füzér hosszát tartalmazza, és ezután jönnek a karakterkódok. Az így tárolt füzért FORTH-füzérnek nevezzük. Ismerkedjünk meg a

COUNT ( ff-cím - - - - cím hossz )

alapszóval, amely a FORTH-füzér címéből előállítja az első karakter címét és a füzér hosszát (azaz a TYPE kiíró szó hívásához szükséges paramétereket)! A "LOVE" szót tartalmazó FORTH füzér byte-jai:

A COUNT szó forrása:

: COUNT ( ff-cím - - - - cím hossz )
DUP 1+
SWAP C@
;

FORTH-füzér alakjában szolgáltatja a szövegeket a FORTH interpreter egyik lelke, a WORD szó is.
Egy string operátorral már találkoztunk:

." szöveg"

Ennek korlátozott használhatósága miatt általánosabb szövegtárolási és kezelési formákra is szükség van. A string kezelő szavak a PAD-et használják. Ez csak átmeneti célokra használható, terület, mert az új szavak átírhatják a PAD-et.

A string változó
Ahogy a numerikus változóknál szó volt róla, a változó nevének végrehajtása azt eredményezi, hogy a változó címe bekerül a verembe, készen arra, hogy a további operátorok dolgozzanak vele. Ez ugyanígy megy a string változóknál is, annyi különbséggel, hogy a string első byte-ja a szöveg hosszát tartalmazza. Ez azt jelenti, hogy egy karakterlánc maximum 255 byte hosszú lehet.
A string változó nevét és maximális hosszát deklarálni kell, innen tudja a fordító, mennyi helyet kell lefoglalni az adott szöveg változónak. Példa:

80 $VARIABLE FRED$

Ez egy string változót deklarál, maximum 80 byte hosszt FRED$ néven. Jó ötlet a karakterlánc változók végé dollár jelet tenni (de nem kötelező), mert a programban így könnyű megkülönböztethetők a string változók a numerikus változóktól. A FRED$ végrehajtásakor a string kezdőcíme bekerül a verembe.

A string konstansok
Ez főleg olyan esetekben használatos, amikor egy változatlan szövegre a programban több helyen akarunk hivatkozni. A deklaráció a következőképpen néz ki:

" Hello" $CONSTANT UDVOZLET$

Ez letárolja a "Hello" szöveget a szótárban, és amikor az UDVOZLET$ végrehajtódik, akkor a string címe bekerül a verembe.

String változók kiírására a már említett

TYPE

használhatjuk. A formája a következő: (cím) (a byte-ok száma) TYPE
pl:

UDVOZLET$ 5 TYPE CR

Nem egészen azt kapjuk amit vártunk, az "o" lemaradt a végéről. Megemlítjük újra, hogy a string első byte-ja a hossz-byte (azaz a példában 6 karaktert kellene kiiratnunk). Egy olyan operátorra van szükségünk, ami leveszi a számlálót a szöveg elejéről és egyet hozzáad a címhez, hogy az valóban a szöveg kezdetére mutasson. Így olyan formához jutunk, amit a TYPE vár. Ez az amit a COUNT megtesz. Például:

UDVOZLET$ COUNT TYPE CR

Ezek alapján definiáljunk egy PRINT szót, ami kiír egy stringet:

: PRINT COUNT TYPE ;

Ez egy nagyon hasznos definíció. Használatakor mindössze a stringváltozó vagy konstans nevét és az PRINT-et kell leírni:

UDVOZLET$ PRINT

A fenti példa a COUNT és a TYPE bemutatására szolgáltak. Valójában a Forth-ban létezik egy olyan string operátor, ami pontosan a PRINT feladatát látja el. Ez a szó a

$

(string kiírás). Példa:

UDVOZLET$ $.

A String változók inputja
Erre többféle lehetőség van. Az EXPECT operátor egy olyan karaktersorozatot vesz be a billentyűzetről, ami egy kocsi vissza (RETURN, CR) karakterrel lett lezárva, és kevesebb karaktert tartalmaz, mint amennyit megadtunk. (de Maximum 255 karaktert).
Példa:

PAD 255 EXPECT

Ez maximum 255 karakteres szöveget vár, ami az ENTER leütésével lett lezárva, és tárolja a PAD-ben. Nem használhatjuk ezt közvetlenül string változók fel töltésére, mert EXPECT nem adja meg a szöveg hosszát. A SPAN szó az EXPECT után kiadva a verembe teszi azt a címet ahol a Forth a bevett szöveg hosszát tárolja.

Példa string változó inputjára: Az EXPECT és a SPAN használatával definiálhatunk egy olyan szót, ami bevesz egy maximum 255 hosszú szöveget, letárolja a PAD-ben, vagy bármilyen más, 255 hosszúra deklarált változóban. Példa:

: $? DUP 1+ 255 EXPECT SPAN C@ SWAP C! ;

Ez a szó beveszi a stringet, elteszi a hosszt, és mindezt tárolja azon a címen, amit előzőleg a verembe tettük. Példa:

PAD $?

A gép ezután várakozik az input szövegre. Miután megadtuk a stringet, gépeljük be a következőt:

PAD $.

Erre a beadott szöveg kiíródik.

String tárolás és string felvétel
A $@ operátor felvesz egy stringet és elhelyez a PAD-ben, aminek a címét a veremben találjuk. A $! operátor a PAD-ben lévő stringet teszi be egy megadott string változóba. Példa:

" Fred" $CONSTANT NEV$
NEV$ $@ $.

A fentiekben deklaráltuk a NEV$-t és betettük a PAD-be. Ezután kiíratjuk a NEV$-t.

Szöveg input belsőleg definiált szóból

" (idézőjel)

Ez az operátor nagyon hasznos string változók inicializálásához. Eredményképp az idézőjelben lévő szövegek bekerülnek a PAD-be. Gyakran használatos operátorral együtt és az EXOS-nak való paraméter átadáskor. Lényeges, hogy az első karakter és az első idézőjel közt egy szóköznek kell lennie. Például:

80 $VARIABLE JANI$
" En vagyok a Jani!" JANI$ $!

Stringek összefűzése
Nagyon gyakran van szükség két string összekapcsolására. A CONCAT szó a veremben tárolt kezdőcímű karakterláncot hozzákapcsolja a PAD-ben lévő szöveg végéhez és az így létrejött szöveg kezdőcímét a verembe teszi. Például:

" alacsony" $CONSTANT MA$
" magas-"
MA$ CONCAT $.

String összehasonlítás
A $<> operátor megvizsgálja, hogy két string különbözik-e egymástól. Általában ez egy IF ... ELSE ... THEN struktúrában használatos. Billentyűzet input ellenőrizhető ezzel az összehasonlító operátorral.

Példaprogram a string-változók használatára

" Szia! Mi a csaladi neved?" $CONSTANT H1$
" Es a keresztneved?" $CONSTANT H2$
" Orulok, hogy megismerkedtunk " $CONSTANT VALASZ$
"  " $CONSTANT URES$
20 $VARIABLE CSALNEV$
20 $VARIABLE KERNEV$
: $? DUP 1+ 255 EXPECT SPAN C@ SWAP C! ;
: SZIA CR H1$ $. CR CSALNEV$ $? CR H2$ $. CR
   KERNEV$ $? CSALNEV$ $@ URES$ CONCAT
   KERNEV$ CONCAT CR VALASZ$ $. PAD $. CR ;

A programot a SZIA szóval hívhatjuk meg.

Byte-sorozatokat kezelő, kényelmes alapszavak:

TYPE ( cím hossz - - - - )
Kiírja a kimeneti eszközre (alapértelmezés szerint a képernyőre) a cím-en lévő karaktersorozatot, a megadott hosszon.
CMOVE ( honnan-cím hova-cím hossz - - - - ) Byte-sorozat mozgatása egyik címről a másikra. Az átvitel az alacsonyabb címeken kezdődik.
FILL ( cím hossz karakter - - - - ) A memóriát a cím-től kezdve hossz számú karakterrel tölti fel. Ha a hossz 0, nem történik semmi.
ERASE ( cím hossz - - - - ) A memóriát a cím-től kezdve hosz számú 0 byte-tal tölti fel. Ha a hossz 0, nem történik semmi.

A WORD

WORD ( c - - - - )

(ejtsd: vörd, jelentése: szó) tördeli a befolyamot a vermen megadott határolókarakter alapján. Azaz: a WORD beolvassa a befolyam karaktereit, egészen a határolóig, átállítja a megfelelő rendszerváltozókat (hogy a legközelebbi WORD ott kezdje az olvasást, ahol ez befejezte), a beolvasott szöveget pedig FORTH-füzér formájában a HERE címre teszi. A WORD a sor végét mindenképpen határolónak érzi.
Egy példa a WORD alkalmazására a LEVEL szó, amelyet így lehet majd használni:

LEVEL KATINAK
DRAGA KATI, HALALOMIG IMADLAK
PITYU

avagy

LEVEL ELEONORANAK
DRAGA ELEONORA, HALALOMIG IMADLAK
PITYU

Lássuk a megvalósulást:

: LEVEL
  32
  WORD


  CR CR
  ." DRAGA"
  HERE COUNT
  3 -
  TYPE
  ." HALALOMIG IMADLAK"
  CR 20 SPACES
  ." PITYU" CR
;


( a szóköz kódja)
( beolvassa a LEVEL után megadott nevet )
( az első szóközig, FORTH-fűzér formában )
( leteszi HERE címre )
( a fontos vallomásokat új sorban kezdjük )

( a vermen a név címe és hossza )
( "nak", "nek" nem kell )

A Kísérletező Olvasó esetleg kipróbálja a következőt:

32 WORD ABRAKADABRA HERE COUNT TYPE

és azt várja, hogy az ABRAKADABRA szót kapja válaszul, hiszen azt olvasta be a WORD. Ehelyett a TYPE szót fogja kapni, mivel az interpreter a parancssorok elemzésére maga is a WORD-öt használja, így, mire a TYPE végrehajtódik, az utolsónak végrehajtott WORD-őt rámolta a HERE címre.

Honnan ismerős? (WORD-del működő alapszavak)
A WORD-ben az a szokatlan, hogy nemcsak a veremről vesz adatot, hanem maga mögül, a befolyamból is kivesz egy szövegrészt és felhasználja. A jelenséggel már találkoztunk egyes FORTH alapszavaknál; ezek a szavak mind a WORD-öt hívják.
A legegyszerűbb ilyen alapszó a ( , amelynek a forrásával is megismerkedünk:

: ( 41 WORD ;

(41 a záró zárójel kódja). A nyitó zárójel beolvassa, amit maga mögött talál, egészen a záró zárójelig (vagy a sor végéig). A WORD úgy állítja át a megfelelő rendszerváltozókat, hogy az interpreter legközelebb a záró zárójel utánról olvasson; így a befolyanmak a két zárójel közötti része egyszerűen nem rúg labdába.
A másik alapszó, egy csökkent értékű változata:

: ." 34 WORD HERE COUNT TYPE ;

(34 az idézőjel kódja). A WORD beolvassa a befolyamot az idézőjelig és FORTH-füzér formájában elhelyezi a HERE címen; innen egy COUNT TYPE sorozattal kiíratható.
Mennyivel tud ennél többet az igazi szó? A különbség akkor látszik, mikor a szót definíció belsejében akarjuk használni. Az igazi a definícióban megadott szöveget írja ki, az itteni változat a definiált szó végrehajtódásakor olvassa el a befolyamból a kiírandó szöveget.
WORD-del olvassa be az elfelejtendő szó nevét a FORGET is. A WORD munkál minden olyan szó belsejében, amellyel más szavakat definiálhatunk:

VARIABLE CONSTANT ;

Pontosabban, a CREATE szó hozza létre az új szótárelemet, a CREATE pedig a WORD-del olvassa be a létrehozandó szótárelem nevét.

3.7. Adatszerkezetek létrehozása
A programok két összetevőből állnak: algoritmusokból és adatstruktúrákból. Az adatszerkezetek lehetnek egyszerű adattípusok (egész, duplapontos egész), valamint összetett adatobjektumok (karakterfüzér, vektor, tömb, rekord stb.).
A ma használatos számítógépek a Neumann-elven működnek. Ez többek között azt jelenti, hogy a program és az adatok egyaránt a számítógép operatív tárában helyezkednek el. A jelen esetben ez azzal a következménnyel jár, hogy ha új adatstruktúrát kívánunk létrehozni, úgy nekünk kell biztosítani az adatobjektum elemeinek elhelyezésére szolgáló helyet. Karakterfüzért biztosít a FORTH, bonyolultabb adatszerkezetet a programozónak kell létrehoznia.
A könnyebb megértés végett először tisztáznunk kell a futásidő és fordításidő fogalmát. A FORTH rendszerekben fordításidő az az idő amelyben a lefordítja a begépelt definíciókat. Futásidőről pedig akkor beszélünk, ami kor egy szó végrehajtása történik. Ennek szemléltetésére tekintsünk egy példát:

: FORDIDO ." forditasido" ; IMMEDIATE
: FUTIDO FORDIDO ." futasido " ;
FUTIDO

A FUTIDO szó definiálásakor "forditasido"-t ír ki, végrehajtáskor "futasido"-t.
A FORTH-ban léteznek olyan szavak - generáló szavak amelyek működésük során új szavakat hoznak létre (CONSTANT, VARIABLE). A CREATE létrehoz egy szótári elemet, a DOES> pedig meghatározza végrehajtási idő alatti hatását
Példaként hozzunk létre egy CONST szót, amelynek funkciója megegyezik a CONSTANT szóéval, vagyis konstansok generálását oldja meg.

: CONST CREATE ( n --- )
  , ( HERE-re leteszi n-et )
  DOES>
    @ ( cim --- n )
;

Példa a használatára:

136 CONST BROWN

Elemezzük BROWN fordításidejét: végrehajtódik a , utasítás, azaz a 136 bekerül a DP által mutatott helyre (a futásidőben majd ez a cím kerül a veremre!), majd DP megnövekszik kettővel. Most vegyük szemügyre a futásidejű működést: a verem tetején először megjelenik a BROWN szótérbeli címe, majd a CONST szó DOES> utáni részére kerül a vezérlés, vagyis erről a címről a @ Szó felteszi a veremre a 136 számot.

Példa adatszerkezet létrehozására
n-elemű vektor létrehozása:

: VECTOR CREATE ( n --- )
  2* ALLOT  ( n*2 byte lefoglalása )
  DOES>
    SWAP 1- 2* +  (n. elem címe )
;

Most hozzunk létre egy 10 elemű vektort SZÁMSOR néven:

10 VECTOR SZAMSOR

Azt már tudjuk, hogy a SZAMSOR meghívásakor előbb a SZAMSOR nevű szó címe a veremre kerül, majd végrehajtódik a generáló szó (jelen esetben a VECTOR DOES> utáni része. Ha például a harmadik elembe be akarunk tenni 66-ot, akkor a kővetkezőt kell leírnunk:

66 3 SZAMSOR !

Ennek hatására a következő történik:

( 66 3 --- 66 3 cím)
(SWAP 66 3 cím --- 66 cím 3 )
( 1- 66 cím 3 --- 66 cím 2 )
( 2* 66 cím 2 --- 66 cím 4 )
( +  66 cím 4 --- 66 cím+4 )

Adjunk hozzá a 3. eleméhez 5-öt:

5 3 SZAMSOR +!

Írassuk ki a tartalmát:

3 SZAMSOR ?

3.8. Számkonverziók

Megfelelő típushoz megfelelő kiíratás
A . .R D. egyöntetűen egy-két számjegy-előállító alapszóra épül. Ugyanezekkel az alapszavakkal mi is bármikor írhatunk hasonló szavakat, amelyek valamilyen formában kiírják a vermen levő, adott típusú értéket.
A vermen levő bináris érték kiírható számjegyekké konvertálását a

<#

szó kezdi el és a

#>

fejezi be.

A konvertált jegyeket előállító szavakat (#, #S, HOLD, SIGN) a <# hívása után, a #> hívása előtt futtathatjuk.

A konverzió során a memóriában létrejön a konvertált jegyekből és kiegészítő jelekből álló füzér, amelynek címét és hosszát a #> a veremre teszi (így TYPE-pal kiírathatjuk). Aki már próbált egy számot az egyik számrendszerből a másikba átírni, az tudja, hogy ez csak visszafelé, az utolsó jegytől kezdve megy. A konvertáló szavak is "visszafelé hordanak": a kiíratandó karakterekből álló füzér hátulról kezdve készül el. A füzér utolsó (elsőnek elkészülő) byte-ja a pad első byte-ja. (Ne csodálkozzunk tehát, ha a konvertáló szavak elrontják a pad elején tárolt adatainkat!) A

# ( ud1 - - - - ud2 )

szó beilleszti a füzérbe a (hátulról) következő jegyet. Működése: az ud1 előjel nélküli duplaszót elosztja a BASE tartalmával. Az ud2 hányadost a veremre teszi, hogy a konverzió folytatódhasson; kikalkulálja a maradékból a megfelelő számrendszerbeli számjegy kódját és beírja a füzérbe. A

#S

végigkonvertálja a vermen levő előjel nélküli duplaszót, annyi jegyet tesz a már előállítottak elé, amennyi szükséges (tehát értéktelen, vezető 0-kat nem). A vermen egy duplaszavas 0 marad. A #S forrása egyébként:

: #S ( ud - - - - dupla-0 )
  BEGIN
    # ( egy jegy )
    OVER OVER OR 0 = ( a vermen heverő duplaszó 0? )
  UNTIL ( ha nem 0 volt az utolsó duplaszó, folytatjuk )
;

A duplaszót, amelyben 3 konverzió részeredményeit tartottuk, a #> dobja el a veremről. A #, így a #S is a BASE-nek megfelelő számrendszerbeli jegyeket állít elő. A

HOLD ( c - - - - )

szó beírja a kapott c karaktert az eddig konvertált jegyek elé. Ha például duplaszavas, előjel nélküli számot akarunk úgy kiíratni, hogy az utolsó két jegy tizedesjegynek látszódjék:

: 2TJE
  <#
    # #
    46 HOLD
    #S
  #>
TYPE
;
( ud - - - - )
( konverzió eleje )
( előállt a két utolsó jegy )
( tizedespont )
( további jegyek )
( konverzió vége )

Vagy, ha magunk szeretnénk a vermen megadni, hogy hány tizedesjegy legyen:

: TJE
  <#
    0 DO # LOOP
    46 HOLD
    #S
  #>
  TYPE
;
( ud tizedesjegyek - - - - )
( konverzió eleje )
( tizedesjegyek )
( tizedespont )

Ha előjel nélküli, egyszavas értéket akarunk kiíratni, az sem gond. Könnyen csinálhatunk belőle kétszavasat, hiszen (pozitív számról lévén szó) a duplaszavas érték magasabb helyi értékű szava 0, egyszerűen ezt kell csak a veremre tenni. Erre szolgál az

U. (u - - - - )

Szó. Forrása:

: U. ( u - - - - )
   0 ( a duplaszó magas helyiértékű szava )
   <# #S #> ( a szükséges számjegyek előálltak )
   TYPE SPACE
;

A fentebb ismertetett utasításokat a következőképpen használjuk: <# utasítássor #> ahol az utasítássor bármilyen FORTH utasítást tartalmazhat (általában #, #S, HOLD használatos ). Ebben a formában előjel nélküli duplapontos számok formázására használható. Ha 16 bites számot akarunk átalakítani, úgy előzetesen egy nullát kell a verem tetejére helyeznünk.
Többnyire azonban előjeles értékekkel dolgozunk. Ehhez meg kell ismerkednünk a

SIGN ( n d - - - - d )

szóval, amely szintén a <# és #> között használatos. A SIGN az a (azaz a verem harmadik eleme) előjelétől függően tesz a konvertált sorozatba mínuszjelet vagy sem. Példa a . szó lehetséges forrása:

: . ( n - - - - )
   DUP ABS ( a felső példánynak nincs előjele )
   0 ( könnyű belőle előjel nélküli duplaszót csinálni )
   <# #S SIGN #>
   TYPE SPACE
;

A <# utasítássor SIGN #> szekvencia előjeles duplapontos számok átalakítására használható. Ekkor az adatvermen a szám abszolút értéke alatt ott kell lenni a szám felső két bájtjának, hogy a SIGN helyesen működjék. Ezt a következő utasítássorozattal készíthetjük elő:

SWAP OVER DABS

Szimpla előjeles egész számok esetén a veremszintek a következőképpen alakulnak: legalul a szám, felette a szám abszolút értéke, legfelül egy 0.
Végül egy szellemes példa Leo Brodie-tól, a STARTING FORTH című könyvből: Tegyük fel, hogy a vermen heverő duplaszó a másodpercek száma, és mi óra: perc :másodperc formában akarjuk kiíratni ezt az időtartamot. Lavíroznunk kell a 10-es és a 6-os számrendszer között.

: SEXTAL ( áttérés 6-os számrendszerre )
   6 BASE ! ;

: OO
   # ( egy decimális jel előállítása )
   SEXTAL
   # (egy "szextális jel előállítása )
   DECIMAL
   58 HOLD ( kettőspont )
;

A fenti programocska, ha jobban belegondolunk, a 60-nal való osztás maradékát írja ki, mégpedig tízes számrendszerben. (A hányadost pedig továbbadja a vermen.) Így a kívánt szót már könnyen megírhatjuk:

: SEC ( ud - - - - )
   <# OO OO #S #> TYPE SPACE
;

4. A virtuális memória

Alapok
Mire ide eljut, az Olvasó bizonyára rengeteg programot írt már. Nagy részüket kernyőre szerkesztette, hogy javíthassa őket. A kernyők az ún. virtuális memóriában vannak. A virtuális memória a memória kiegészítése lemezzel, lemezes kernyőfile-lal vagy kazettával. (A legutóbbi kicsit nehézkes, mert a FORTH virtuális memória kezelési elvei közvetlen hozzáférést igényelnek).
A virtuális szó itt látszólagosat jelent. A virtuális memória elnevezés azt tükrözi, hogy a virtuális memória blokkjait - bár adathordozón vannak - programozáskor úgy látjuk, mintha a memóriában lennének. Mégpedig azért látjuk úgy, mert a

BLOCK ( blokksorszám - - - - memóriacím )

szó gondoskodik róla, hogy a blokk, amelyre hivatkozunk, beolvasódjék a memóriába, ha eddig nem volt ott; a visszakapott címtől kezdve megtaláljuk a memóriában a blokk tartalmát. A blokk itt az olvasás-írás fizikai egységét jelenti, általában (lemezről lévén szó) egy szektort. A BLOCK szó:

Az utóbbi esetben nem fordulunk a lemezhez; időt takarítunk meg anélkül, hogy erre programíráskor ügyelnünk kéne.
A virtuális memória blokkjait az interpreter a memória ún. blokkpuffereiben tartja. A BLOCK végignézi, hogy nincs-e a keresett blokk valamelyik blokkpufferben; ha megtalálta, akkor a blokkpuffer címét kapjuk, ha nem, akkor beolvas, de melyik pufferbe? A válasz egyszerű, ha van üres blokkpuffer. A blokkpufferek azonban kevesen vannak a virtuális memória blokkjaihoz képest. Többnyire valamelyik foglalt blokkpuffert kell felülírni. Mi történik ilyenkor a puffert foglaló blokkal? Elvész a tartalma, vagy visszaíródik az adathordozóra?
Ezt mi magunk döntjük el. Ha nem teszünk semmit, a puffer tartalma nem íródik vissza, tehát csak olvassuk a virtuális memóriát. Azt, hogy egy adott blokkot majd vissza is akarunk íratni, a blokkra való hivatkozás (BLOCK) után az

UPDATE ( - - - - )

szóval közöljük. Ettől az adott blokk "update-elt", vagyis módosított lesz. A szóhasználat egy kicsit félrevezető; a blokkpuffer tartalmát majd ezután fogjuk módosítani, az adathordozó blokkjait pedig még később, mikor a pufferre szükség van, vagy mikor minden módosított puffert visszaírunk.

Az UPDATE mindig arra a blokkra vonatkozik, amelyre a legutoljára hivatkoztunk a BLOCK szóval.

(Ez egyszerűen úgy történik, hogy a BLOCK leteszi a PREV rendszerváltozóba a blokk számát, az UPDATE pedig ott találja meg).
Az összes módosított blokkot a

FLUSH ( - - - - )

szóval írathatjuk ki (például, mikor felállunk a gép mellől). A fig-FORTH FLUSH néven, a FORTH-79 rendszerek SAVE-BUFFERS ismeri ezt az utasítást. Az IS-FORTH-ban mindkét Szót használhatjuk, funkciójuk ugyanaz.
Ha viszont vissza szeretnénk csinálni mindazt, amit a blokkpufferekben elkevertünk, az EMPTY-BUFFERS ( ---- ) után tiszta lappal indulhatunk; az interpreter üresnek tekinti az összes blokkpuffert, anélkül, hogy egyet is visszaírna (vagy a blokkpufferek tényleges tartalmát megváltoztatná. Az

EMPTY-BUFFERS

nem a puffereket törli, hanem a pufferek nyilvántartását írja át.). Egy blokk tartalmát a

WIPE ( blokksorszám - - - - )

Szóval törölhetjük. A WIPE a blokk tartalmát feltölti szóköz karakterekkel.
Hogy a virtuális memóriában tárolt szövegeket ugyanúgy végrehajtathatjuk az interpreterrel, mint a billentyűzetről beírtakat, az (többek között) azon múlik, hogy az interpreternek mindig a WORD adja át a következő interpretálandó szót. A WORD a BLK rendszerváltozó tartalmától függően működik:

itt érdemes megemlíteni, hogy a parancsuffer címét a

TIB ( - - - - cím )

rendzserváltozó trtalmazza. A FORTH editorok gyakran az aktuális kernyő soraira vonatkozó műveletekből állnak. Az aktuális kernyő számát az SCR rendszerváltozó tartalmazza. Például a LIST is aktuálissá teszi a kilistázott kernyőt, azaz a számát elhelyezi az SCR változóban.
A BLOCK, az UPDATE, a FLUSH és az EMPTY-BUFFERS a virtuális memóriakezelés alapjai.

5. A szótár és a címinterpreter

Kettőspontos szavak
A FORTH programozás legalapvetőbb mozzanata: már meglevő szavakra új, célunkhoz közelebb vivő szavakat építeni, hogy később ezekre tovább építhessünk. Az ilyen, más szavakból összeállított szavak mutatókat tartalmaznak az őket alkotó szavakra; működésük lényege az őket alkotó szavak végrehajtása a mutatók alapján. A mutatóknak ezt a fajta kiértékelését egy pici, gyors programocska, az ún. belső interpreter vagy címinterpreter végzi. (Sok múlik azon, hogy a címinterpreter gyors legyen, hiszen a FORTH szavak végrehajtásakor nagyrészt ő fut.)
Ha egy szódefiníciót kettősponttal kezdünk, akkor ezzel azt közöljük, hogy az új szó régebbi szavakból fog felépülni. Így, mivel szótári szavak működését a kódmező tartalma határozza meg,

valamennyi, kettősponttal definiált szó kódmezőjében a címinterpreter címe van.

A mutatók, amelyeken majd a címinterpreternek tovább kell mennie, a kettőspontos szavak paramzőjében vannak; ezek a szót alkotó szavak kódmező-címe.
Így már megérthetjük, hogyan fejti vissza a kettőspontos szavakat a decompiler. Ha már eldöntötte, hogy kettőspontos definícióról van szó (a kódmezőben a címinterpreter címe van), végigmegy a paramzőn, s az ott talált kódmezőcímekből kiagyalja az összetevő szavak nevét. A kettőspontos szavak paramzőjében az utolsó kódmezőcím a ;S szóé; ez a szó küldi vissza a címinterpretert a hívó szóba. (A ;S hatására befejeződik a kernyő interpretálása.) A ;S kódmezőcímét a ; szó bűvöli bele az új szó paramzőjébe.

Primitívák
A FORTH szavak jó részét tehát a címinterpreter működteti: sorra elmegy a címekre, ahová a szó paramzőjében talált mutatók küldik. Ott esetleg újabb mutatókat talál, amelyeken továbbmehet. A sok küldözgetésnek egyszer vége kell, hogy szakadjon: előbb-utóbb valamelyik hívott szónak ki kell rukkolnia vele, hogy mit is kell csinálni. Ezeket a végső szavakat, amelyek tehát nem más szavakra utaló mutatókat, hanem végrehajtható gépi kódokat tartalmaznak, primitíváknak nevezzük. A primitívák kódmezőjében a mutató a primitíva saját paramzőjébe mutat. A paramzőben gépi kód van, ez hajtódik végre, mikor a primitívát futtatjuk. A primitívák létezése alapja az alábbi kijelentésnek:

Amit egy gépen lehet, azt a rajta futó FORTH-ban is lehet.

A primitívákat arra is felhasználhatjuk, hogy az operációs rendszer rutinjait hívjuk vele. Primitívák létrehozására az ASSEMBLER szótár szavai szolgálnak, ezek teszik lehetővé, hogy gépi kódok helyett mnemonikokat használjunk, hogy az assembler programozásban is a rendelkezésünkre álljanak a vezérlési struktúrák stb.

5.1. Szókészletek
Az IS-Forth támogatja a szótár különböző részeinek létrehozását és használatát. Ezeket a részeket szókészleteknek nevezzük. A szókészletek általában egy adott alkalmazáshoz szükséges szavakat tartalmazzák. Tartalmazhat az alkalmazás egy elkülönült részéhez tartozó másik szókészletet is.

A szókészlet struktúra
Bár minden új szó a memóriában lineárisan a szótár tetejéhez íródik hozzá, a szavak oly módon vannak összekapcsolva, hogy egy fa struktúrájú szótár jön létre. A fa minden ága egy szókészletnek felel meg.
Minden szókészlet bármilyen szót használhat annak a veszélye nélkül, hogy átdefiniálna egy olyan - már létező - szót, amire majd a későbbiekben szükség lehet. Például: Az Assembler szókészletben az A egy konstansként van definiálva. Ha egy másik szókészlet az A szót, mint változót akarja használni, megteheti, ha az a szókészlet lesz az első a keresésben (pl: a CONTEXT szókészlet). Ha ez az alkalmazás tartalmaz gépi kódú részeket az assembler használatával, akkor a CONTEXT beállíthatja az ASSEMBLER szókészletet. Miután így az A-t változóként használhattuk, visszatérhetünk a CONTEXT szókészlethez, és ekkor A visszakapja az eredeti értékét.

Új szókészlet létrehozása
Egy új szókészletet a következőképp hozunk létre:

VOCABULARY (név)

Amint a (név) végrehajtódik, letárol egy saját magára mutató pointert a CONTEXT-ben. Ez biztosítja, hogy ez az új szókészlet ELŐBB kerül végignézésre. Ha új szó definiálása szükséges, be kell lépnünk a (név) szókészletbe, és a

DEFINITIONS

beteszi a CONTEX-ben lévő értéket a CURRENT-be. Így aktuális cím a CONTEXT-ben lesz és a CURRENT nem érvényes.
Amikor egy szókészlet végrehajtás alatt van és nincs meg az adott szó, akkor a keresés az "apa ágon" folytatódik felfelé. Így, ha létrehozunk egy FRED nevű szókészlet a FORTH szókészleten belül, akkor azokat a szavakat amelyek nincsenek a FRED-ben, a rendszer a Forth szókészletben fogja keresni.
A VLIST szó az aktuális keresés összes szavát listázza ki, keresési sorrendben. A gyökér szókészlet elválasztása az ágakon lévő szókészletektől egy plusz szóközzel történik.

5.2. A Forth Assembler

Az IS-Forth olyan szótárat is tartalmaz, amely a Z80 assembler programozást segíti. Olyan esetben, ha a kész programot (alkalmazást) fel kell gyorsítani, ezt úgy érik el, hogy a sebesség-kritikus részeket újraírják ("kihegyezik") Forth-assemblerben, majd az egész programot újrafordítják.
A Forth-assembler módba az

ASSEMBLER

paranccsal léphetünk be. Ettől kezdve Forth-assembler szavakat is használhatunk a FORTH szavak mellett. A "normál" FORTH-módba a

FORTH

szóval léphetünk vissza.

Az assembler, Forth, és a fordított lengyel jelölés
Mint a Forth része, az assembler is a fordított lengyel jelölés elvén dolgozik, amely szerint az operandusok (paraméterek) megelőzik az operációt (műveletet).
A Z80 regiszterek konstansként vannak definiálva, a többi utasítás pedig olyan szóként működik, amely a paramétereit a veremről szerzi meg, a helyes műveleti kód előállításához, majd pedig ezt a kódot fordítja be a következő szabad szótárbeli memóriahelyre. A regiszterek, és feltételkódok 16-bites kostansként vannak definiálva, a felső (magasabb helyértékű) bájt tartalmazza regiszter típusát (8-bites, 16-bites), az alsó bájt pedig azt, hogy pontosan melyik regisztert kell a műveletnél (az előbb leírt formában) felhasználni. A típus bájt értékei a hexadecimális C000 felett vannak.
A regiszter konstansok és értékeik (hexadecimálisan):

B F000   (BC) F800
C F001   (DE) F810
D F002   I F807
E F003   R F80F
H F004      
L F005   BC E000
(HL) F006   DE E001
A F007   HL E002
(IX+) F606   AF E004
(IX-) F706   IX EE02
(IY+) F406   IY EC02
(IY-) F506   SP E003

A feltételkódok (a feltételes vezérlésátadó utasításokhoz) szintén konstansok. Az értékek a következőképpen alakulnak:

NC F000
C F001
NZ F002
Z F003
PO F004
PE F005
P F006
M F007

Látható, hogy az értékek megegyeznek a regisztereknél definiáltakkal. A IX és IY regiszterek a következőképpen használhatók A Z80 (IY+d) ill. (IX-d) hivatkozások megfelelnek a (IY+) és (IX-) kódoknak olyan formában, hogy az eltolás (displacement) megelőzze a kódot. Például az

LD A,(IY+10)

utasítás a Forth-assemblerben a

10 (IY+) A LD,

szekvenciának felel meg.

Az utasításkészlet
Az utasítások a Zilog Z80 által használt mnemonikokat követik, persze a fordított lengyel jelölésnek megfelelően. Itt következnek az utasítások (Z80 formában, és a megfelelő Forth írásmódban):

Z80 Forth   Z80 Forth
ADC A,r r A ADC,   IND IND,
ADC A,n n A ADC,   INDR INDR,
ADC HL,rr rr HL ADC,   INI INI,
ADD A,r r A ADD,   INIR INIR,
ADD A,n n A ADD,   JP (HL) HL JP(),
ADD HL,rr rr HL ADD,   JP (IX) IX JP()
ADD IX,rr rr IX ADD,   JP (IY) IY JP()
ADD IY,rr rr IY ADD,   JP nn nn JP()
AND r r AND,   JP cc,nn nn cc ?JP,
AND n n AND,   JR d addr JR,
BIT b,r r b BIT,   JR cc,d addr cc ?JR,
CALL nn nn CALL,   LD (BC),A A (BC) LD,
CALL cc,nn nn cc ?CALL,   LD (DE),A A (DE) LD,
CCF CCF,   LD R,A R A LD,
CP r r CP,   LD I,A A I LD,
CP n n CP,   LD A,(BC) (BC) A LD,
CPD CPD,   LD A,(DE) (DE) A LD,
CPDR CPDR,   LD A,R A R LD,
CPI CPI,   LD A,I I A LD,
CPIR CPIR,   LD r,r' r' R LD,
CPL CPL,   LD r,n n r LD,
DAA DAA,   LD (nn),A A nn () LD,
DEC r r DEC,   LD (nn),HL HL nn () LD,
DEC rr rr DEC,   LD (nn),DE DE nn () LD,
DI DI,   LD (nn),SP SP nn () LD,
DJNZ d addr DJNZ,   LD (nn),BC BC nn () LD,
EI EI,   LD rr,nn nn rr LD,
EX (SP),HL HL SP () EX,   LD (rr),nn nn rr () LD,
EX (SP),IX IX SP () EX,   LD SP,HL HL SP LD,
EX (SP),IY IY SP () EX,   LD SP,IX IX SP LD,
EX AF,AF' AF' AF EX,   LD SP,IY IY SP LD,
EX DE,HL HL DE EX,   LDD LDD,
EXX EXX,   LDDR LDDR,
HALT HALT,   LDI LDI,
IM n n IM,   LDIR LDIR,
IN A,(C) C A IN(),   NEG NEG,
IN A,(n) n A IN(),   NOP NOP,
INC r r INC,   OR r r OR,
INC rr rr INC,   OR n n OR,
OTDR OTDR,   RR r r RR,
OTIR OTIR,   RRC r r RRC,
OUTD OUTD,   RRD RRD,
OUTI OUTI,   RST n n RST,
POP rr rr POP,   SBC A,r r A SBC,
PUSH rr rr PUSH   SBC A.n n A SBC,
RES b,r r b RES,   SBC HL,rr rr HL SBC,
RET RET,   SCF SCF,
RET cc cc ?RET,   SET b,r r b SET,
RETI RETI,   SLA r r SLA,
RETN RETN,   SRA r r SRA,
RL r r RL,   SRL r r SRL,
RLA RLA,   SUB r r SUB,
RLC r r RLC,   SUB n N SUB,
RLCA RLCA,   XOR r r XOR,
RLD RLD,   XOR n N XOR,

A jelölések magyarázata:

Amint az látható, a Forth-assemblerben a szavak végét vessző (,) jelzi. Mindez a következőkre vezethető vissza:

Ugrások
Az ugrások mindegyike a verembe teszi a célállomás címét.

JR, ?JR,JP, ?JP, CALL, ?CALL, DJNZ

Ezek hátrafelé ugrásra használhatók. A BEGIN szó az aktuális címet beteszi a verembe egy későbbi visszaugráshoz. Például a BEGIN használandó az ugrások rutinhívások céljának megjelölésére.
Az előreugrásokhoz a következő egyszerű szavak állnak rendelkezésre:

JR>, ?JR>, JP>, ?JP>, CALL>, ?CALL>, DJNZ>

Ezek befordítják az utasításkódot, és fenntartanak egy vagy két byte-ot a cím számára. A fenntartott byte-(ok) címét a verembe teszik.
Az END és az ENDR használatosak később az ugrás céljának megjelölésekor a fenntartott helyek kitöltésére.
Az END használatos a következőknél: JP>, ?JP>, CALL>, ?CALL
Az ENDR kell a következők céljának jelölésére: JR>, ?JR>, DJNZ>

Forth-assembler szavak definiálása:

CODE név ... END-CODE

vagy

:név ... :CODE ... END-CODE

ahol a név természetesen a szó nevét jelenti a ... pedig assembler-, illetve (a második forma első részében) Forth-szavakat jelent.

Rregiszterek
Bizonyos Z80-as regisztereket használ az IS-FORTH Ezeknek a regisztereknek a megváltoztatása csak konvencióknak megfelelően lehetséges.

SP A Z-80 veremmutatója a paraméter verembe mutat. Ennek a veremnek a felső két eleme regiszterekben van letárolva (lásd később).
IY A visszatérési veremben utoljára tárolt bites érték alacsony byte-jára mutat. (Mint a normál Z80 verempointere.)
IX
A NEXT nevű "belső interpreterre" mutat. Minden esetben, amikor egy új szót hívunk, ez aktivizálódik. Így a gépi kódú Forth szavak kódolásnál, a kódnak IX JP() (azaz JP (IX)) utasítással kell végződnie. A NEXT egy definiált konstans is, így a NEXT JP utasítással szintén befejezhetjük a gépi kódú programunkat.
HL'
Ez az "interpreter mutató", ami az aktuális végrehajtandó kettőspont definíció következő szavára mutat. Amikor egy új szót hívunk, ez az érték eltárolódik a visszatérési veremben (IY mutat rá), és az új szóra állítódik be.
DE'
Amikor egy szót hívunk, DE' a definícióban lévő fordítási cím utáni adatokra mutat. Így, ha ." Forth szót gépi kódban írnánk meg, a pontosan az idézőjel utáni szöveg elejére mutatna, amikor a kód futásra kerül.
HL, DE
HL tartalmazza a paraméter verem legfelső elemét, és DE a következőt. Ezért fontos, hogy a gépi kódú programrészek ne változtassák meg ennek a két regiszterpárnak a tartalmát, hacsak újra nem akarják a verem tartalmát írni.
AF'
Néhány jelzőbitet tartalmaz a stop/break billentyűről, a háttér szóról, a megszakítási szavakról (ha van), és arról, hogy az IS-FORTH "gyors" módban van vagy sem. Az AF' használata függ az adott IS-FORTH megvalósítástól, így azt nem szabad megváltoztatni.

A Forth szavak meggyorsítása
Annak használata, hogy a verem felső két eleme a HL és DE regiszterekben van tárolva, és hogy az IX a NEXT-re mutat, gyakran felgyorsítja a Forth szavakat. Például az lS-FORTH-ban a SWAP szó így néz ki:

EX DE,HL
JP (IX)

A Z-80 Forth megvalósítások nagy részénél ez így néz ki:

POP HL
EX (SP),HL
PUSH HL
JP NEXT

Ez 50%-kal hosszabb, és 24%-kal lassúbb az előzőnél. Egy másik példa az 1+ operátor. IS-FORTH-ban ez így valósul meg:

INC HL
JP (IX)

Az átlagosabb megoldás:

POP HL
INC HL
PUSH HL
JP NEXT

Ez 50%-kal hosszabb és 38%-kal lassabb.

Ajánlott, hogy a fejlesztés első fázisában ne írjunk gépi kódot a programba. Az egész alkalmazás megírható FORTH-ban, majd amikor az algoritmus helyesnek bizonyul, akkor a sebesség szempontjából kritikus szavak átírhatók gépi kódra.

5.3. Változók és konstansok

Valamennyi változó ugyanúgy működik: a veremre teszi azt a címet, ahol a változó értéke van. Így talán nem meglepő, hogy változóink kódmezői mind ugyanazt a címet tartalmazzák. Annál meglepőbb, hogy a rendszerváltozóké (BLK, DPL stb.) nem ugyanaz. Az ilyen különleges változók:

HLD Számok karaktersorozattá konvertálásakor az utoljára konvertált karakter címét tartalmazza.
R# Az editor használja a kurzorpozíció tárolására.
CSP
A veremmutató értékének átmeneti tárolására szolgál. Fordítás alatt hibaellenőrzésre használjuk.
Ezt a változót a FORTH rendszer használja az adatveremmutató átmeneti tárolására. A veremmutató CSP-be való írására külön szó szolgál, ez a !CSP. A FORTH megköveteli, hogy egy szódefiníció végén a verem mélysége megegyezzen a szódefiníció kezdetén fennálló mélységgel. Tehát : és ; között amit a veremre teszünk, azt le is kell venni, onnan. Ezért a : szó végrehajt egy !CSP utasítást, a ; pedig egy ?CSP-t. A ?CSP szó összehasonlítja a veremmutató aktuális értékét a CSP-ben tárolttal, és ha a kettő nem egyezik meg, akkor egy hibaüzenetet generál. A változót és a hozzá tartozó két szót magunk is használhatjuk, de szódefiníció közben ekkor a CSP tartalmát el kell menteni és a ; előtt vissza kell állítani.
FLD A numerikus output konverziójánál legutoljára végrehajtott <# óta konvertált karakterek számát tartalmazza.
DPL Szám tizedespont utáni jegyeinek számát tartalmazza. Ha nem volt tizedespont, az értéke -1.
BASE Az input és output konverziók (számrendszer) alapszáma. Számkiíratáskor e számrendszer alapján íródnak ki a számok, beolvasáskor pedig úgyszintén BASE alapján történik a számkonverzió.
STATE A rendszer fordítási vagy végrehajtási állapotát jelzi. Értéke végrehajtás közben 0, a nem 0 érték fordítást jelent.
CURRENT A CURRENT-ben annak a "szótárnév" szónak a csatolómezőcíme van, amely által kijelölt szótárba a definíciók kerülnek.
CONTEXT Az aktuális keresési szótár csatolómezőjének címét tartalmazza. Ezen csatolómező mutat a szótár első szavára. A szótárbeli keresés mindig a CONTEXT által kijelölt szótár első szavával kezdődik. Alapértelmezésben a változóban levő címen a FORTH alapszótár első szavának névmezőcíme található.
SCR Az aktuális (LIST-el legutoljára listázott) kernyő számát tartalmazza.
OUT Értékét az EMIT növeli. A programozó változtathatja vagy vizsgálhatja, hogy a kiírás formátumát vezérelje.
BLK Az éppen interpretált blokk számát tartalmazza. Ha 0, akkor az interpreter a parancspufferből dolgozik.
VOC-LINK Az utoljára definiált szótárra tartalmaz mutatót. A szótárnevek ezen keresztül fel vannak fűzve egy láncra.
DP A szótár fölötti első szabad memóriacím. Értéke a HERE szóval olvasható, az ALLOT szóval változtatható.
FENCE Azt a címet tartalmazza, amelynél alacsonyabb címről a FORGET nem törölhet (a szótár védett).
PREV A legutóbb használt blokk pufferének címét teszi a verembe. Az UPDATE parancs ezt a puffert fogja módosítottnak jelölni.
TIB A parancspuffer címét tartalmazza.
R0 A viremmutató kezdeti értékét tartalmazza.
S0 A veremmutató kezdeti értékét tartalmazza.

A rendszerváltozók (USER, ejtsd: júzer változók) egy táblázatban vannak. A konstansok kódmezője is egyforma: mind arra a kódra mutat, amely a paramző tartalmát a veremre teszi. Így egy szótári szó kódmezőjéből megállapíthatjuk, hogy kettőspontos szóról, primitíváról vagy egyébről van szó.
Előre definált konstansként vannak definiálva:

5.4. Az EXECUTE

Az

EXECUTE ( kódmezőcím - - - - )

az a szó, amely végrehajtja a vermen megadott kódmezőcímű másik szót.
Ezt a kódmezőcímet előállíthatja pl. a

RECURSE ( - - - 16b)

Szó, ami megadja a definiálás alatt álló Szó fordítási címét.

5.5. Hibakezelés

A FORTH-83 Standard sok "hiba feltételt" definiál. Néhányat ezekből (pl. nullával való osztást) az IS-FORTH figyelmen kívül hagy. A többi általában egy üzenetként jelenik meg a képernyőn. A következő üzenetek és hibaszámok lehetségesek:

hibakód hibaüzenet nagyar jelentés
1. (név) not found (név) hiányzik
2. (név) contol error (név) kontroll hiba
3. (név) below FENCE (név) szótárhatár alatt
4. Incomplete definition Hiányos definíció
5. Stack overflow Verem túlcsordulás
6. Stack underflow Verem alulcsordulás
7. CURRENT not CONTEXT CURRENT nem CONTEXT
8. Can't FORGET CURRENT A CURRENT nem törölhető
9. Buffers full Bufferek megteltek
10. Invalid condition Érvénytelen feltétel (Assembler)
11. Invalid register Érvénytelen regiszter (Assembler)
12. Invalid displacement Érvénytelen eltolás (Assembler)

Ha a hiba szám nem alakítható a fentiek egyikévé, akkor az alábbi üzenetet kapjuk:

Error type n.
(n típusú hiba)

Egy hibakezelő a következőképp aktivizálható:

(ABORT) (név)

Ahol a (név) egy korábban definiált szót jelent). Ezután a (név) feladata, hogy hibaüzenetet adjon a felhasználónak. Amikor (és ha egyáltalán) a (név) visszaadja a vezérlést, akkor az ABORT szó kerül végrehajtásra. Ez visszaállítja a vermeket, és további felhasználói inputra vár mindenféle üzenet és a kurzor újrapozícionálása nélkül.
A (név) végrehajtásakor a hibaszám a verembe kerül, BLK bemásolódik SCR-be és >IN bekerül R#-be.
Használható a következő szó:

n ERROR

Itt n a hibaszám. Hatása, hogy egy hiba generálódik, ahogyan azt a fentiekben leírtuk, annyi különbséggel, hogy esetleg definiált (ABORT) szavak nem kerülnek végrehajtásra. Így lehetséges egy (ABORT) szón belül a hibaüzenetet kiadni a felhasználónak.
Az (ABORT) szavak hatástalanítása a következőképp lehetséges:

(ABORT) ERROR

6. Azonnali szavak

6.1. Amikor az interpreter fordít

A kettőspontos szódefiníciókat bevezető : szó létrehozza az új szó név-, kód- és láncmezőjét, majd ún. fordítási állapotba teszi az interpretert. Ez annyit jelent, hogy az interpreter a befolyam szavait nem hajtja végre, hanem előkeresi a kódmezőcímüket és a szóval tárolja a HERE címen (az éppen létrejövő szó paramzőjében).
Hogy a befolyamban talált szavakat az interpreter végrehajtja vagy lefordítja, az a STATE rendszerváltozó (USER változó) értékétől függ. Ha a STATE értéke nem 0, akkor a befolyam szavai lefordítódnak. Ennek megfelelően, ha a STATE 0, végrehajtási, ha nem, fordítási állapotról beszélünk. A STATE értékét (többek között) a definíciókat bevezető . és a befejező ; szó állítja.

6.2. Amikor az interpreter mégsem fordít

Nézzünk egy meglehetősen közönséges definíciót:

: COUNT DUP 1+ SWAP C@ ;

A fentiek szerint a COUNT paramzője így néz ki:

DUP kódmezőcíme
1+ kódmezőcíme
SWAP kódmezőcíme
C@ kódmezőcíme
;S kódmezőcíme

Egészítsük most ki ugyanezt a definíciót egy kommenttel:

: COUNT ( ff-cím - - - - cím hossz ) DUP 1+ SWAP C@ ;

A fentiekből az következne, hogy a második COUNT paramzőjében megjelenik a
( szó kódmezőcíme (hiszen a ( szó feltűnésekor a STATE nem 0, a rendszer fordítási állapotban van). A józan ész viszont azt súgja, hogy a két definíció eredménye azonos kell, hogy legyen, ha egyszer csak egy kommentben térnek el egymástól. A józan észnek van igaza: a fordítási állapotban sem minden szó fordítódik le. A szabályt erősítő kivételek az azonnali (immediate, ejtsd: immediét) szavak.

Az azonnali szavak akkor sem fordítódnak le, mikor az interpreter fordítási állapotban van; valahányszor az interpreter felleli őket, azonnal végrehajtódnak.

A ( egy azonnali szó. Azonnali szavakat magunk is írhatunk; csak az kell hozzá, hogy a szó definíciója után a friss szót az

IMMEDIATE

szóval azonnalivá tegyük.

6.3. Literálok

A literálok azok a számok, amelyeket a definícióban adunk meg; ezeket az interpreter beírja a paramzőbe. Például a

: .HAT 6 . ;

Sok Olvasóban talán már megfogalmazódott a kérdés, vajon hogyan tárolja a FORTH a számkonstansokat? Minden bizonnyal nem úgy, hogy a szám önmagában áll a szótárban, hiszen akkor mi különbözteti meg a címektől? Hogyan jön rá a FORTH arra, hogy a 6-ot a veremre keli majd tenni a .HAT meghívásakor, és nem a 6-os címen kezdődő szót kell végrehajtania? Ha a FORTH fordítás közben egy számot talál, akkor előbb elhelyezi a szótárban a LIT szó címét, majd a következő memóriarekeszbe beírja a számot. (A LIT a literal szóból ered, jelentése: szó szerint. Azaz a LIT-et követő értéket nem címként, hanem számként kell értelmezni.) Jegyezzük meg, hogy a LIT-et a FORTH automatikusan beírja a szótárba a számkonstansok előtt!
Most nézzük meg, mi a teendő akkor, ha azt szeretnénk, hogy ne egy szótörzsben leírt számkonstans kerüljön a szótárba, hanem egy vermen levő szám! Előfordulhat, hogy a szódefinícióban használandó érték egy művelet eredménye; mondjuk, azt akarjuk felhasználni, hogy egy nap perceinek száma 60*24. Ezt ugyan ki lehet számolni előre, de akkor nem látszik rajta, hogy mi ez és hogyan számoltuk ki; a szorzást be is lehet írni a definíció szövegébe, de akkor minden egyes futáskor végrehajtódik, ami időpocsékolás. Ehelyett azt is megtehetjük, hogy a fordítási állapotot a

[

szóval megszüntetjük, előállítjuk a vermen a kívánt értéket, és a

]

szóval visszatérünk a fordítási állapotba. A vermen levő értéket a

LITERAL

szó fogja literálként a paramzőbe fordítani:

: PERCEK [ 60 24 * ] LITERAL . ." perces egy nap" ;

Nézzük, hogyan működik ez a változat! A "60 24 *" szögletes zárójelek között van, azaz a PERCEK fordítása közben végrehajtódik. Emiatt a veremre kerül az 1440. A következő szó a LITERAL. A LITERAL egy közvetlen szó, amely a szótárba fordítja a LIT címét, majd a LIT után elhelyezi a verem tetején található számot. Esetünkben tehát a LIT után most a 1440 kerül a szótárba. Mt jelent ez? Futási időben nem vesz el időt a szorzat kiszámítása, mert az már fordításkor megtörtént. A programszöveget viszont a "60 24 *" érthetőbbé és logikusabbá teszi. A [, ] párt és a LITERAL-t egyébiránt főleg a tárgyalt struktúra megvalósítására szoktuk használni. Ne feledjük tehát, hogy a ... [ számítás ] LITERAL ... utasítássorozatot érdemes minél többször alkalmazni, ezzel is növelve programjaink áttekinthetőségét.

Tekintsük a LITERAL egy másik alkalmazását! Szeretnénk egy olyan tömböt létrehozni, amely egymás után tíz darab 16 bites számot tud tárolni. Az egyes elemeket valamilyen egyszerű módon akarjuk elérni:

HERE DUP 20 + DP !
: ELEM 2* LITERAL [ 0 ] + ; DROP

A működés a következő: a HERE a veremre teszi a szótár következő üres bájtjának címét. Ehhez hozzáadunk húszat, mivel a tíz darab 16 bites szám húsz bájtot foglal el. Az eredményt beírjuk a DP-be. Ezzel tíz memóriaszónyi üres hely keletkezett a szótárban. A DP régi értéke (vagyis az üres helyek kezdőcíme) még ott van a vermen a DUP miatt. Az első sor egyszerűbben HERE 20 ALLOT formában is irható.
Ezután jön az ELEM szó definíciója. Előbb bekerül a szótárba a 2*, azután a LITERAL beírja a LIT-et és a címet. Vegyük észre, hogy a LITERAL által a szótárban elhelyezett szám most a szódefiníción kívülről jött. Ez nem baj, de az viszont igen, hogy a FORTH elvárja a verem mélységének azonosságát a : és a ; végrehajtásakor. Ha a ; elérésekor a verem mélysége különbözik attól, ami a :-nál volt, a FORTH hibajelzést ad (Incomplete definition.). Mivel a LITERAL egy számot levett a veremről, helyette egy másikat kell odatenni. Ezért szükséges a [ 0 ], amely a verem eredeti mélységét visszaállítja. A + szótárba fordítása után a szódefiníció befejeződik. A DROP-pal most már megszabadulhatunk a felesleges nullától.
Hogyan kaphatjuk meg az i-edik 16 bites szó címét? A 0. címe cím+0, az elsőé cím+2 a másodiké cím+4 és így tovább. Az i-edik elem címe általánosan cím+2*i. Vegyük észre, hogy az ELEM szó éppen ezt a számítást végzi el! Megszorozza kettővel az elem sorszámát, majd hozzáadja a kezdőcímet (ami a LIT cím miatt kerül a veremre, amit viszont a LITERAL tett a szótárba). Az ötödik elembe így írhatunk:

93 5 ELEM !

és belőle így olvashatunk:

5 ELEM @ .

Bár célunkat, a tömb létrehozását elértük, különösebben boldogok nem lehetünk. Igaz, a bemutatott módszer nem túl bonyolult, viszont nem elég rugalmas. A CREATE és DOES> szavak egy sokkal könnyebben kezelhető módszert biztosítanak számunkra az adatszerkezetek létrehozására.
A LITERAL szó természetesen azonnali. Ha nem definícióban használjuk, a LITERAL nem csinál semmit.
Duplapontos számok fordítására szolgál a 2LITERAL. Működése a LITERAL-nál elmondottak vonatkoznak.

6.4. Azonnali szavak elhalasztása a [COMPILE])

Ismerkedjünk meg egy új szóval:

[COMPILE]

Lefordítja a mögötte álló azonnali szót (amely a [COMPILE] nélkül nem fordítódna le, hanem végrehajtódna).
A [COMPILE] maga is közvetlen szó. Működésének lényege, hogy szódefiníció közben végrehajtódva mintegy ellopja a FORT elől a következő közvetlen szót, előállítja címét és azt a szótárban elhelyezi. Így nyílik lehetőségünk arra, hogy egy Szó végrehajtás helyett mégiscsak fordításra kerüljön. Ne keverjük össze a COMPILE Szóval!

A

[']

pedig lefordítja a mögötte álló azonnali szót, amely a [COMPILE] nélkül nem fordítódna le, hanem végrehajtódna.
Használata:

['] név

A <név> fordítási címét literálként fordítja úgy, hogy annak a definíciónak, amelyben ['] található, későbbi használatkor a cím a verembe kerül.
Alkalmazása olyan esetekben hasznos, amikor futás közben szeretnénk egy parancsot megtalálni a szótárban. Példaként szolgálhat a Hanoi tornyai feladvány furfangos megoldása:

( Towers of Hanoi )
: LEFT ." left  " ;
: RIGHT ." right " ;
: MIDDLE ." middle" ;

: MOVE-DISK ( v t f n --- v t f )
   DUP 0= IF DROP EXIT THEN
   1- >R
   ROT SWAP R@ RECURSE
   ROT SWAP
   2DUP CR ." Move disk from " EXECUTE
   ." to " EXECUTE
   SWAP ROT R> RECURSE
   SWAP ROT
;
: HANOI ( n --- )
   1 MAX >R
   ['] RIGHT ['] MIDDLE ['] LEFT
   R> MOVE-DISK DROP DROP DROP CR

;

7. Enterprise specifikus szavak

7.1. Kapcsolat az EXOS-al

Az IS-FORTH-ból elérhetjük valamennyi EXOS változót. Erre a következő két szó szolgál:

E! ( n e - - - - ) Eltarolja n értéket az e EXOS változóba.
E@ ( e - - - - ) A stack-re másolja az e EXOS változó értékét. (Ugyanerre a célra szolgál az ASK szó.)

A kővetkező EXOS voltozókat egyszerűbben - a nevükre hivatkozva - is kezelhetjük. Sok EXOS változó Foth konstansként van deklarálva. A definiált EXOS változók a következők:

BUFFER TIMER STOP SPEAKER
BAUD FORMAT MODE COLOURS
COLORS STATUS X Y
BORDER REM1 REM2 BIAS

Az EXOS változók állítására rendelkezésre állnak a következő szavak is:

ON - bekapcsolás (255-öt ad)
OFF - kikapcsolás (0-át ad)
TOGGLE - átkapcsolás (a változó értékének komplemensét képezi)

Példák, felhasználva a E! és E@ szavakat:

0 BORDER E! Fekete színű keretet állít be.
STATUS ON Látható lesz a státuszsor.
REM1 TOGGLE Magnó távvezérlés

A Forth és a csatornák
Az Enterprise-on minden I/O művelet a csatornái keresztül zajlik. Ez a rendszer részletesen le van írva az Enterprise EXOS Leírásásban, ezért itt csak azzal foglalkozunk, hogy a Forth-ból miként lehet ezeket a lehetőségeket elérni. Sok Forth szónak kell csatornára hivatkoznia. Gyakran több szó ugyanarra a csatornára utal. Azért, hogy felhasználónak ne kelljen csatornaszámokat megadni, Forth rendelkezik egy átirányítási táblával, ahol megvan, mely csatornaszámok használhatóak. Így például egy PLOT-ot akarunk kiadni egy nem alapértelmezett grafikus csatornára, akkor az átirányítási tábla grafikus csatorna sora meg fog változni.
A következő szavak beállítják az alapértelmezés szerinti (default) csatornaszámot a verem tetején lévő értékre:

#EDITOR
#GRAPHICS
#TEXT
#STATUS

Például a 99 #GRAPHICS átirányítja a grafikus csatornát az 99-es számra. Az

ATTRIBUTE
HIRES
LORES
TEXT

szavak az eredeti értékeket állítják viasza az átirányítási táblában.
Csatornakezelésre szolgálnak a

GET ( ch - - - - n )
PUT ( n ch - - - - )

szavak is, ahol ch a csatorna száma, n pedig a kiküldendő érték.
Az átirányítási táblázat alapértékei, valamint az őket használó csatornák a következők:

101
102
103
104
105
106
107
108
VIDEO:
VIDEO:
SOUND
PRINTER:
KEYBOARD:
TAPE: vagy DISK:
VIDEO:
EDITOR:

(grafikus lap)
(szöveges lap)
(hangkezelő)
(nyomtató)
(billentyűzet)
(háttértároló)
(Az EDIT végrehajtása alatt)
(Az EDIT végrehajtása alatt)

Megjegyezzük, hogy bizonyos szavak - mint a CLOSE és a GET - nem nézik az átirányítási táblát, hanem a csatornaszámot a veremből veszik, mint paramétert.
Nézzünk egy példát a csatornák használatára! Írassuk ki a VLIST-re megjelenő Szólistát egy szöveges file-ba:

" A:\VLIST.TXT" 10 OPENOUT 10 #EDITOR VLIST 100 #EDITOR 10 CLOSE

Először megnyitjuk a 10-es csatornát írásra, majd az editor eszközt átirányítjuk erre a csatornára. A Szótári elemek felsorolása után visszaállítjuk az EDITOR-t az alapértelmezett 100-as csatornára, majd lezárjuk a 10-es csatornát.

A FORTH rendszerből indíthatunk más rendszerbővítőket is:

EXT (addr - - - - )

Az adott címen levő parancs-sztringet (az első byte a név hossza) az EXOS kapja meg, s elindítja azt, amennyiben lehetséges.
Pl. az

" DIR" EXT

szavak hatása megegyezik a

:DIR

EXDOS parancséval. (Ez utóbbi formát ugyan kiadhatjuk a FORTH interpreter-ből, de szóban nem szerepelhet.)

7.2. Grafika az IS-FORTH-ban

Az IS-FORTH-ban az IS-BASIC-hez hasonló (igen széles) lehetőségek nyílnak meg előttünk a grafikai megvalósításban. Szerencsére a parancsok ugyanazok mint a BASIC-ban, csak a "fordított" formához kell hozzászoknunk. A rendelkezésre álló videó üzemmódok a BASIC-ben megismertek, tehát 2, 4, 16, 256 színes HIRES vagy LORES felbontású videólapokat nyithatunk. Nyissunk meg például egy 4 színes nagyfelbontású videólapot:

4 HIRES GRAPHICS

További szavak:

PALETTE ( n8 n7 n6 n5 n4 n3 n2 n1 - - - - )
A videólap színpalettáját állíthatjuk be. Az n1-n8 értékek a színek kódjai, a nullás palettaszín az n1 lesz. Bármilyen színüzemmódot állítunk be, a szó mindig 8 kódot vár a veremből.

INK ( n - - - - )
A tinta színét n értékre állítja be.

PLOT ( x y - - - - )
Az aktuális tintaszínnel pontot rajzolunk a videólap x y pozíciójába. A rajzolósugár x y pozícióban marad.
Példa: 10 10 PLOT

+PLOT ( m n - - - - )
A grafikus sugarat kikapcsolja és elviszi az X+m,Y+n pozícióba, majd újra bekapcsolja, ahol x és y az aktuális sugár koordináta. Azaz átteszi a rajzolósugarat az aktuális pozícióból egy relatív pozícióba és tesz oda egy pontot. Így m és n lehet negatív érték is.

DRAW ( x y - - - - )
A rajzolósugár aktuális pozíciójából vonalat húz x y pozícióba. A rajzolósugár x y pozícióban marad.
Példa: 10 10 PLOT 500 100 DRAW

+DRAW ( m n - - - - )
A grafikus sugarat bekapcsolja és elviszi az X+m,Y+n pozícióba, ahol x és y az aktuális rajzolósugár koordináta. Azaz vonalat húz az aktuális pozícióból egy relatív pozícióba. Így természetesen n és m lehet negatív szám is.

MOVE (m n ----)
Kikapcsolja a grafikus rajzolósugarat és a (m, n) pozícióba viszi.

+MOVE ( m n - - - - )
A grafikus sugarat kikapcsolja és elviszi az X+m,Y+n pozícióba.

ELLIPSE ( r1 r2 - - - - )
r1 r2 sugarú ellipszis (vagy kör, ha r1 = r2) rajzolása. A z ellipszis (kör) középpontja a rajzolósugár aktuális pozíciója.

A következő nyolc színre nemcsak kódszámukkal, hanem nevükkel is hivatkozhatunk:

BLACK - fekete (0)
BLUE - kék (36)
CYAN - világoskék (182)
GREEN - zöld (146)
MAGENTA - lila (109)
RED - piros (73)
WHITE - fehér (255)
YELLOW - sárga (219)

A színek "kikeverésére" rendelkezésre áll a BASIC-ből szintén ismerős:

RGB ( n1 n2 n3 - - - - )

Példa a grafikai utasítások használatára a Felhasználói Kézikönyv első programja alapján:

: ELLIP
  4 HIRES GRAPHICS
  640 360 MOVE
  BEGIN
     1 250 DO
     3 RND 1+ INK
     I I ELLIPSE PAINT
  -16 +LOOP
  REPEAT
;

Példa a Felhasználói Kézikönyv Tűzcsatorna programja alapján:

( TUZCSATORNA )
: TUZLABDA ( n --- )
  3 PLOTTING INK
  2 0 DO I
    650 1 DO
      I I ELLIPSE
    30 +LOOP
  LOOP
  0 PLOTTING
;
: CSATORNA 256 HIRES GRAPHICS
  255 1 DO
    I INK
    420 I - 310 I - 2DUP MOVE
    OVER 410 I + R@ DRAW
    860 I + DUP R> DRAW
    OVER DRAW DRAW
  LOOP
  640 360 MOVE
  100 1 DO
    254 RND 1+ TUZLABDA
  LOOP
;

A következő példában megnyitunk egy 20x10 karakter méretű 4 színű videólapot, amit a 3 sortól jelenítünk meg, majd rajzolunk rá világoskék színnel egy ellipszist.

20 X E! 10 Y E!
1 COLOURS E! 1 MODE E!
" VIDEO:" 1 OPENOUT ( 1-es videócsatorna megnyitása )
1 #GRAPHICS
1 3 10 1 DISPLAY ( videócsatorna megjelenítése )
0 0 0 0 WHITE RED CYAN BLUE PALETTE ( színek beállítása )
320 180 MOVE 240 120 ELLIPSE

A megjelenő videólapot az

1 CLOSE

paranccsal zárhatjuk be.

7.3. Egyéb szavak

FREE ( - - - - u ) A szabad memória nagyságát adja meg bájtban (ez a terület szolgál a stack-nak és a szótárnak). A veremben kapott eredmény előjel nélküli szám.
INFO ( - - - - ) Az aktuális memória foglaltságát jelzi ki.
JOY ( n1 - - - - n2 )
A botkormányt olvassa le. (n1 = 0 - belső, n1 = 1 - EXT1, n1 = 2 - EXT2) A beolvasott bájt bitjei (b4 ... b0) tartalmazzák az irányra és a tűzgombra vonatkozó adatokat. A bitek növekvő sorrendben a bal, jobb, fel, le irányt és a tűzgomb lenyomását jelentik.
FONT ( - - - - ) Visszaállítja az eredeti karakterkészletet (mint a CLEAR FONT). A font jelet kettőskereszt jelnek definiálja, mivel sok FORTH Szó használja ezt.

A karakterek átdefiniálására szolgál a

CHARACTER (n1 n2 n3 n4 n5 n6 n7 n8 n9 n10 --- )

Szó, ami az n10-es ASCII-kódú karaktert definiálja át. n1 a karakter alsó, n9 a legfelső sora, tehát pont fordított a sorrend a BASIC SET CHARACTER utasításhoz képest. Ez mind szép és jó, gond nélkül csinálhatunk pl. ékezetes karaktereket definiáló HFONT Szót, de a grafikus karaktereket (a 127-159 ASCII kódú karakterek) sem a .", sem a .(, Szó nem jeleníti meg, ahogy változóból a TYPE Szó sem! Egyedüli megoldás az EMIT.

Az üzemmódok
A fejlesztést, majd a használatot a

SLOW ( - - - - )

illetve

FAST ( - - - -)

üzemmódok segítik A SLOW utasítás kiadása után a hibakövetés állandó jelleggel megtörténik. Ezzel szemben a FAST "üzemmód" már a kész programok futtatására szolgál, ekkor a hibafigyelés kikapcsolt állapotú, s ez jelentősen gyorsítja a programok futását. A rendszer indítása után a FAST üzemmód az alapértelmezésű.

Az IS-FORTH szójegyzék ABC sorrendben tartalmazza az összes IS-FORTH szót.
A szójegyzéknek is érdemes átolvasni, mivel ebben a leírásban nem említett szavakkal is megismerkedhetünk!

Vissza