Zzzzip
A BASIC compiler

File-név: ZZZIPP.COM
Program neve: Zzzip Integer Basic compiler 1.1
Peter Hiner - 1986
Basic compiler

Az Enterprise IS-BASIC-je nagyságrendekkel többet tud mint a többi "kortárs gép" BASIC-je. Egy -sajnálatos- hátránya azért van: a tudásáért a programfutás sebességével fizetünk. Bár egyik BASIC nyelvnek sem erőssége a sebesség, de az IS-BASIC más BASIC változathoz képest is lassabb.
Tudnunk kell, hogy a BASIC interpreter a fordítást a program futása közben végzi (nem úgy mint pl. a PASCAL-ban, ahol a program gépi kódra fordítását, és a futtatható állomány generálását a program futása előtt végezzük el). Könnyen belátható, hogy ez drasztikusan befolyásolja a program sebességét, főleg ha tudjuk, hogy a ciklusban végrehajtott utasításokat az interpreter nem csak egyszer fordítja le, hanem minden esetben, amikor az utasításra kerül a végrehajtás, vagyis ugyanazt a munkát többször is elvégezzük.
Az IS-BASIC sebességét a gép operációs rendszere, az EXOS is befolyásolja. A kimeneti műveletek kezelését az EXOS csatornákon keresztül végzi. Amikor valamelyik csatornára adatot küldünk, az operációs rendszer olyan funkciót is elvégez, ami lehet, hogy a konkrét esetben felesleges. Erre a későbbiekben konkrét példát is fogunk látni.
A helyzet természetesen nem olyan rossz, mint ahogy az előbbiekből gondolhatjuk, ésszerű programozással (pl. a megfelelő algoritmus alkalmazásával) máris sokat tettünk a program hatékonyságának növelésére. A probléma alapvető megoldásához azonban más eszközhöz kell nyúlnunk, ez a BASIC Compiler.

Mi is az a compiler?
A compiler egy fordítóprogram, amely egy magasszintű nyelven (esetünkben BSIC-ben) megírt programot fordít gépi kódú programmá (pontosabban pszeudó-kóddá). Így lehetőségünk van gyorsabb programok készítésére, anélkül, hogy más programozási nyelvet kellene választanunk. A compiler-eknek két csoportja létezik: integer és real fordítók. Az integer fordítók csak egész számokkal tudnak dolgozni, cserébe viszont a lefordított program gyorsabban fut mint a real fordítókkal készített program. A real fordítók nem csak egész számokkal tudnak dolgozni, ezért azonban a lassabb programfutásssal fizetünk.

A Zzzip
Az Enterprise tulajdonosok sajnos nem hemzsegnek a fordító-programok özönében. Aki le szeretné fordítani programját, annak egy választása van: a Zzzip. A Zzzip egy integer fordító, ami eléggé behatárolja a lefordítható programokat.
A program indítása után a gép rögtön a fordítandó BASIC program neve után érdeklődik a program, majd a cél file nevét is meg kell adni. Lemezes környezet esetén a program elfogad olyan többletinformációkat, mint pl.: lemezegység azonosítója, elérési útvonal. Egylemezes rendszer esetén a szokásos lemezcserére szólító üzeneteket is ismeri a ZZZIP (Ha pl. A: egységről B:-re fordítunk.) Ha a lefordított programnak nem adunk meg nevet, akkor a ZZZIP az alapértelmezés szerinti nevet fogja használni. A file-név formátuma megegyezik az EXDOS által ismert 8 + 3 karakter rendszerrel. Amennyiben talál kiterjesztést, akkor azt a BASIC betöltőhöz csatolja, a lefordított programnak a ".Z" kiterjesztést adja. Ha nincs kiterjesztés, és a név nem hosszabb 8 karakternél, a ZZZIP egyszerűen hozzácsatolja a lefordított program nevéhez a ".Z" kiterjesztést.

A lefordított program tehát két részből fog állni: egy BASIC betöltőből, és a lefordított programból. A két file neve az lesz amit megadtunk (vagy az alapértelmezett név), a betöltő kapja azt a kiterjesztést amit megadunk, a második file kiterjesztése .Z lesz. Ha esetleg a későbbiekben a lefordított programot át szeretnénk nevezni, az első (betöltő) file utolsó sorában át kell írni a nevét!
A fordítást négy menetben történik meg, ha a Zzzip hibát észlel (olyan programrészt talál, amit nem tud lefordítani) kiírja a hibát, és sorszámát annak a sornak, ami a hibát okozta. Fordítás közben a program kiírja a feldolgozás alatt álló BASIC sorok sorszámait., így lehetővé teszi a fordítás nyomonkövetését.

Ha a fordítás sikeres volt, a program megkérdezi, hogy kívánunk e más programot is lefordítani. Ha a válaszunk nemleges, kilépünk a programból. Ugyanez történik, ha megnyomjuk a STOP billentyűt.
Megjegyzem, hogy a Zzzip-el igen hatékony programvédelmet tudunk megvalósítani, ember legyen a talpán, aki a lefordított programot "vissza tudja fejteni", vagy módosítani tudja. Az előbbiekből következik, hogy a programunk eredeti példányát mindenképpen őrizzük meg!
A lefordított programok majdnem ugyanúgy kezelhetőek, mintha BASIC programok lennének. A legfontosabb, hogy egyszerre csak egy lefordított program tartható a memóriában és az a 0. program kell legyen. A programon belül a RUN parancs használható más programok betöltésére és futtatására, szabadon keverve BASIC és lefordított programokat.

Na akkor most következzen a feketeleves... A Zzzip-el sajnos nem minden programot fogunk tudni lefordítani. Készüljünk fel rá, hogy elég kevés programot fogunk találni, ami átalakítás nélkül le tudunk fordítani! Sajnos úgy tűnik, hogy a Zzzip az IS-BASIC egy régebbi változatához készült, ezért van néhány dolog, amire oda kell figyelnünk:

BASIC parancsok
A ZZZIP megtart csaknem minden olyan parancsot, amely egy BASIC programon belül végrehajtható.

ALLOCATE
A parancs felmozgat a memóriában egy BASIC programot és ily módon helyet biztosít a gépi kódú rutinoknak. A lefordított programok nem mozdíthatók el, ezért esetünkben egy 255 byte-nyi terület (az 1300H-tól felfelé) van fenntartva. Ennek a területnek a méretét az ALLOCATE parancs nem befolyásolja. Valójában 255 byte-nál is több rendelkezésre állhat annak a kockázatával, hogy esetleg átnyúlik 17DH címtől felfelé terjedő string-kezelő területre. Így tulajdonképpen kb. 1 kbyte-nyi terület tartalmazhat gépi kódú rutinokat.

DEF
A ZZZIP a legtöbb DEF parancsot elfogadja, de azért néhány korlátozás fennáll. A ZZZIP minden változót globálisként kezel. Ennek az az egyik oka, hogy a ZZZIP egy BASIC programot a sorok számozásának sorrendjében dolgoz fel, s ez valószínűleg nem fog megegyezi azzal a sorrenddel, amelyben a sorok programfutás során végrehajtódnak. Ezért visszakövetkeztetéssel nem tudja mindig meghatározni egy változó globális vagy lokális voltát. Ezért egy DEF blokkon belül a változóneveket úgy határozzunk meg, hogy a program egészére tekintve egyediek legyenek! A DEF blokkok nem használhatóak rekurzívan! Tegyük fel, hogy függvény fejléce a DEF FUNC(X) utasítás. A függvény tartalmazza a CALL FUNC(Y) utasítást. A BASIC minden alkalommal, amikor a függvény meghívja önmagát, megőrzi az X értékét és a későbbiekben, amikor a függvény meghívja önmagát visszaadja ezeket az egymást követő X értékeket. Ilyen helyzetben a függvény nem szolgáltat pontos eredményt.

PROGRAM
A parancs nincs hatással a lefordított programra, a ZZZIP a REM-hez hasonlóan kezeli.

RESTORE
Ha adott egy sorszám (pl. RESTORE 200), ez egy DATA utasítást tartalmazó sor kell, hogy legyen. Máskülönben a ZZZIP hibaüzenetet ad.

RUN
Ezzel a paranccsal a lefordított program újraindítható (az elejétől, vagy a kijelölt sorszámtól). Egy másik (BASIC vagy lefordított) program betöltésére és futtatására is használható, de egyik programból a másikba történő paraméterátadás már nem lehetséges.

STOP
Programban szereplő parancsként a STOP az END-hez hasonlóan kezelődik, s a BASIC-hez való kilépést eredményezi. A STOP billentyű egy lefordított program megszakítására is használható, akárcsak a BASIC-ben, de újraindítani csak elölről a START utasítással lehet, a CONTINUE nem használható.

EXCEPTION handling (megszakítás kezelése)
Az EXCEPTION szó már önmagában is azt jelenti, hogy hiba következett be. Ennek ellenére a ZZZIP megkísérli egy figyelmesen megírt hibakezelő biztosítását. Nem valószínű, hogy fordítás megszakad, mivel a ZZZIP minden kapcsolódó feladatot elfogad, beleértve a CONTINUE és a RETRY parancsokat is. A lehetséges problémák futás során jelentkeznek. A CAUSE EXCEPTION parancs nyomán előállt helyzetek nem okoznak zavart, de ha más forrásból, mint pl. egy EXOS-beli hibaellenőrzésből bekövetkező EXCEPTION után próbáljuk meg használni a CONTINUE parancsot, az eredményt illetően semmi biztosíték nincs!
A ZZZIP részéről két különleges korlátozás áll fenn: Csak egyszintű EXCEPTION kezelés biztosított, más szóval a WHEN EXCEPTION parancsok egymásba ágyazása nem megengedett. Kis mértékben a RETRY parancs is különbözik. A lefordított program ahelyett, hogy abba a sorba a menne vissza, amelyikben az EXCEPTION előfordult, a legutóbbi WHEN EXCEPTION parancshoz fog visszatérni. A kívánt visszatérést elérhetjük úgy, hogy a WHEN EXCEPTION sort közvetlenül az elé a sor elé tesszük, amelybe vissza akarunk térni (pl. egy INPUT sor elé).

CHAIN, IMAGE, PRINT USING, TRACE, TYPE
Ezeket a parancsokat a ZZZIP nem fogadja el és használatukkor a fordítás alatt hibaüzenetet eredményez.

Beépített függvények

BIN (X)
Azokra az X értékekre, amelyek nem haladják meg a 32767 értéket, a függvény a szokásos módon használható a zárójelben lévő változó vagy konstans érték operandussal. Nagyobb értékek esetén az operandus csak konstans lehet.

CEIL(X), INT(X), IP(X), ROUND(X,N), TRUNCATE(X,N)
Mivel a ZZZIP integer fordító, ezek a függvények gyakorlatilag nem csinálnak semmit, egyszerűen az X értéket adják vissza.

EPS(X)
Ez a függvény mindig az 1 értéked adja vissza. Az 1 az egész értékű matematikában az értékváltás legkisebb egysége.

EXLINE
Egy lefordított programban a végrehajtás alatt álló sor száma nem őrződik meg, ezért ez a függvény mindig a 0 értéket adja vissza.

FP (X)
Mindig a 0 értéket adja vissza.

FREE
A függvény a lefordított program által felhasználható byte-ok számát adja vissza, ahogy ezt el is várnánk. Ellenben ha ez az érték meghaladja a 32767-et, negatív számként fog megjelenni.

INF
A függvény mindig a 32767 értéket adja vissza. Ez az előjeles 16 bites számábrázolásban a legmagasabb érték.

PI
A függvény a 3 értéket adja vissza.

RGB
A függvény csak decimális konstansokat fogad el. Tehát az RGB(0,.4,1) elfogadható (Kivételes eset!), ellenben az RGB(0,3/7,7/7) vagy az RGB(X,Y,Z) már nem. A ZZZIP csak az első számjegyet használja a tizedespont után, s ezért az RGB(0,.42,.97)-et úgy tekinti, mint RGB(0,.4,.9)-et.

RND és RND(x)
Az RND(x) pontosan úgy működik, mint a BASIC-ban. Az RND önmagában mindig 0-át ad vissza. Az RND*X különleges eset, amikor a visszaadott érték megegyezik az RND (X) által szolgáltatott értékkel.

Trigonometrikus és logaritmikus függvények
A szögfüggvények értékei nem kompatibilisek az egész értékű matematikával. Ezért arányosító tényezőket kell létrehozni. Ezt a COS függvény esetében mutatjuk be.
Hogy használható eredményeket kapjunk, a COS(X) által visszaadott érték automatikusan arányosítódik (beszorzódik) az 1000-es tényezővel, azért, hogy egy 0 és 1000 közé eső egész értékké váljon (a 0 és 1 közé eső értékek helyett). A program további részében ez az érték már használható, de ne felejtsük el, hogy az erre alkalmas helyen osztanunk is kell 1000-rel, hogy (megközelítőleg) pontos eredményt kaphassunk. Természetesen az 1000-rel való osztás helyét gondosan kell megválasztanunk, hogy elkerüljük az eredmény 0-ra való csökkenését. Ez a hely a COS(X) értékének egy más értékkel való beszorzása után legyen! Ezt a külön lépést (az 1000-el való osztást) a BASIC program szokásos tesztelése után, a fordítás előtt helyezzük el a programban.
Ha a szögek meghatározásakor a DEGREES-t használjuk, a COS(X)-ben lévő X operandus megegyezik az eredeti BASIC programban szereplő értékével Ellenben, ha RADIANS-t használunk, az X-et arányosítani (szorozni) kell 1000-el a COS(X) használata előtt.

Függvények
Bejövő arányosító
Kimenő arányosító
ACOS
ASIN
ATN
1000
1 (fok)
vagy
1000 (radián)
COS
COT
CSC
SEC
SIN
TAN
1 (fok)
vagy
1000 (radián)
1000
DEG
1000
1
RAD
1
1000
EXP
1000
1
LOG
LOG2
LOG10
1
1000
COSH
SINH
TANH
1000
1000
ANGLE(X,Y)
1
1 (fok) vagy
1000 (radián)

 

A ZZZIP hibaüzenetei

MEMORY FULL (megtelt a memória)
Ha betöltés közben jelenik meg az üzenet, akkor a lefordítandó program túl nagy, ha fordítás közben, akkor a ZZZIP kifogyott a változónevek tárolására fenntartott területből

TOO MANY LABELS (túl sok címke)
A ZZZIP kifogyott a címlék (főleg a sorszámok) tárolására fentartott helyből.

"..." NOT SUPPORTED (... nem elfogadható)
A ZZZIP jelzi a kezelhetetlen elemet és annak sorszámát. Az elem lehet egy parancs, REF függvény, vagy lehet egy olyan változó amely egy konstans érték helyett határozza meg egy vektor méretét vagy egy karaktersorozat maximális hosszát. A ZZZIP ezeknek rögzített memóriaterületet tart fent, és ezért nem engedi meg a "dinamikus dimenzionálást".

SYNTAX?
Szintaktikai hiba, vagy BASIC parancsszavakat vagy függvényeket használunk változónévként.

GARBAGE
A program pontos betöltésének sikertelenségét jelzi.

NON INTEGER / OUT OF RANGE (nem egész / tartományon kívül)
A ZZZIP jelzi, ha egy konstans nem elfogadható, vagy azért, mert nem egész, vagy mert kívül esik a szokásos +/- 32767 intervallumon.

LOOP / BLOCK TERMINATION (ciklus / blokk lezárás)
Hiányzik egy ciklus lezárása.

REFERENCE NOT FOUND (nem található a hivatkozás)
Nincs a GOTO után sorszám, a CALL utasítással nem létező függvényt akarunk meghívni, stb.

IDENTIFER DECLARED TWICE (kétszer deklarált azonosító)
Kétszer akarunk deklarálni azonos nevű változót (pl. a DEF blokkon belüli lokális változót olyan néven szeretnénk létrehozni, ami a programban, mint globális változó már létezik).

WARNING - GLOBAL VARIABLE (Figyelem - globális változó)
Azon problémák megoldása érdekében, amelyek a függvényekben előforduló lokális változókkal kapcsolatosak, a ZZZIP ellenőrzi az átadó paraméterként használatos változókat. Abban az esetben, ha egy eljárásban hozunk létre egy változót (amely BASIC programban lokális érvényű lenne), a ZZZIP figyelmeztet, hogy a lefordított programban globális érvényű lesz. Mivel ez nincs hatással a lefordított program működésére (kivéve, ha már létezett azonos nevű globális változó, ebben az esetben az IDENTIFER DECLARED TWICE hibaüzenetet kapjuk), az üzenet csak figyelmeztetés és a fordítás folytattódik.

COMPILLED PROGRAM DOES NOT RUN CORRECTLY
(a lefordított program hibásan fut)
A két legvalószínűbb ok az eljárásokban, függvényekben előforduló lokális / globális változók eltérő kezelése és az egész-aritmetika használatának hatásai. A használt egész aritmetikában a tizedespont utáni számok nincsenek figyelembe véve, és minden értéknek +/- 32767 tartományba kell esni. Ily módon az aritmetikai műveletekben alkalmazott műveleti sorrend nagyon fontos. Pl. a LET X=2/4*10 0 eredményt fog adni. Ahhoz, hogy a pontos értéket (5) kapjuk, a LET X=2*10/4 alakra kell módosítanunk.
A következő LET X=A/B*C utasítás már nem ilyen magától értetődő. Ilyen esetekben szükség lehet az A, B és C lehetséges értékeire. Bizonyos esetekben megengedett, hogy egy változó érték 32768 és 65535 közé essen, mégpedig akkor, ha az összeadás vagy a kivonás eredménye. Mindemellett óvatosan kell élnünk ezekkel az eredményekkel, mivel arra épül, hogy a legnagyobb helyértékű bit szokásos előjelbitként való használatát figyelmen kívül hagyja. Leírható pl. a LET X=25000+25000, és ezután az X a következő utasítástípusok bármelyikében használható:

LET X=X+Y
LET X=X-Y
POKE X,Y
SPOKE X,Y
LET Y=PEEK(X)
LET Y=SPEEK(S,X)
CALL FUNCTION(X,Y,Z)
LET A FUNCTION(X,Y,Z)
IF X=Y THEN ...

Ennek gyakorlati haszna a memória kezelésben nyilvánul meg, de figyelni kell arra, hogy csak a fent említett utasítástípusokat használhatjuk. Javasolt azonban a POKE / PEEK utasítások helyett a SPOKE / SPEEK utasítások használata, ebben az esetben nem szorulunk erre a nagy odafigyelést igénylő megoldásra.

Most nézzünk egy példát, mennyire hatékony a Zzzip!
Példaprogramunk remek példát fog mutatni, a bevezetőben már említett utalásra, amikor az EXOS lassítja a programvégrehajtást:

Basic-ből a program 38 másodperc alatt fut le, lefordítva 26 másodperc kell a programvégrehatáshoz. Gondolom a többség nem ezt az eredmény várta, a program alig gyorsult valamit. Ha megnézzük a programot láthatjuk, hogy elég sok információt iratunk ki PRINT paranccsal a szöveges képernyőre (102-es csatorna). Ilyenkor az EXOS elvégez olyan műveleteket is, mint pl. automatikus szóátvitel, képernyőgörgetés. A lefordított program tehát azért nem gyorsult jelentősen, mert a PRINT parancsokkal az EXOS "szöszmötöl"! Amire pl. programlistázás közben szükségünk van, nem biztos, hogy egy program közben is szükséges. A PRINT parancs az egyik leglassabb parancs az IS-BASIC-ben! Ha beírjuk a csatornára hivatkozást a PRINT parancsokba (160, 170, 180-as sorok) a parancs végrehajtása jelentősen felgyorsul. (A 160. sor tehát így fog kinézni: PRINT £102: I+2*I; ) Az már más kérdés, hogy ebben az esetben a képernyőgörgetés pont hiányozni fog...
A Basic program így 24 másodperc, a lefordított program 11 másodperc alatt fut le. A gyorsulás tehát most már több mint kétszeres! Természetesen ez egy szélsőséges példa, a lefordított programok általában többszörösen felgyorsulnak.

itt megtekinthetjük a szerző bemutatóját (video).