Gépi Kódú Programozás


Tartalom

Bevezetés

1. A gépi kódú programozás alapjai
1.1 Alapfogalmak
1.2 Az utasításkészlet
1.2.1 Adatmozgató és -cserélő utasítások
1.2.2 Logikai utasítások
1.2.3 Aritmetikai utasítások
1.2.4 Bitléptető és -rotáló utasítások
1.2.5 Bitkezelő utasítások
1.2.6 Ugróutasítások
1.2.7 Szubrutin utasítások
1.2.8 Input/Output (I/O) utasítások
1.2.9 A processzor vezérlőutasításai

2. A háttérrendszer
2.1 A memóriaszervezés
2.2 Az operációs rendszer szerkezete
2.3 Az EXOS működése
2.3.1 Inicializálás
2.3.2 A funkcióhívások
2.4 A megszakítások programozása
2.4.1 A megszakítási rendszer
2.4.2 Állapotsor saját használatra
2.4.3 Óra az állapotsorban
2.4.4 Stopper
2.4.5 Hangkeltés a megszakítási rutinban
2.5 Perifériakezelők
2.5.1 Nyomtatás saját karakterkészlettel
2.5.2 Nyomtatóvezérlés a szövegszerkesztőből
2.6 Rendszerbővítők

3. A BASIC rendszer
3.1 Inicializálás
3.2 Memóriafelosztás, változóterületek
3.2.1 Rendszerváltozók
3.2.2 Pufferterületek
3.2.3 A változók bázistáblája
3.2.4 RAM-térkép
3.2.5 Kulcsszótábla
3.2.6 Függvénytábla
3.2.7 A BASIC programtároló
3.2.8 Változóterület
3.2.9 BASIC munkaverem
3.3 A BASIC Restart rutinok
3.3.1 A BASIC funkcióhívások
3.4 A BASIC értelmező működése
3.4.1 Beolvasási főág
3.4.2 Sorelemzés, tokenizálás
3.4.3 Végrehajtás
3.4.4 Utasítások, függvények

4. Bővítési, kiterjesztési lehetőségek
4.1 USR rutinok
4.2 Utasítás és függvénybővítések
4.2.1 A kulcsszótábla kiterjesztése
4.2.2 A függvénytábla kiterjesztése
4.3 RAM-értelmező

Függelék
1. Karakterek és Z80-as utasítások
2. A Z80-as utasítások kódjai
3. Az EXOS memóriatérképe és rendszerváltozói
4. EXOS hibakódok és üzenetek
5. A perifériakezelők belépési pontjai

6. A BASIC kulcsszavak paramétertáblázata
7. A beépített BASIC függvények és változók táblaterülete
8. A képernyőkezelés fontosabb adatai
9. Az ASMON (SIMON) program használata


3. A BASIC rendszer

Az ENTERPRISE személyi számítógéphez modulként csatlakoztatható IS-BASIC értelmező tulajdonképpen felhasználói program (az előző fejezet szóhasználatával: rendszerbővítő), de kétségtelenül a legszélesebb körben alkalmazott a felhasználói programok körében. A legtöbb géptulajdonos éppen a BASIC rendszerből kiindulva, annak lehetőségeit kiterjesztve foglalkozik (vagy szeretne foglalkozni) a gépi kódú programozással. Ismerkedjünk meg tehát az EXOS után a BASIC felépülésével, szerkezetével, működésével is, hogy biztosan támaszkodhassunk rá mint környezetre, felhasználhassuk rutinjait, szolgáltatásait.

3.1 Inicializálás

A BASIC memóriatérképéről, felépüléséről itt is az inicializálás folyamatát követve nyerhetjük a legtöbb információt.
A BASIC mint rendszerbővítő, két esetben veszi át a vezérlést. A rendszer inicializálása után - mint láttuk - a hidegindítás akciókódja jár körbe a bővítők között, ill. nem jár körbe, mert a BASIC szegmens (rendszerünkben az 5-ös) ennek hatására aktuális programmá válik és elkezdi saját inicializálását. Ugyanez történik akkor is, ha 2-es akciókódot indít körbe egy másik felhasználói program (vagy akár maga a BASIC), méghozzá BASIC parancsfüzérrel (ez történik, ha kiadjuk a :BASIC parancsot vagy szövegszerkesztőben megnyomjuk az F8 gombot stb.). A füzér felismerése után a két eset "összetalálkozik" és egységesen hozza létre az értelmező alapállapotát:

C0A4
C0A6
C0A9
C0AA
C0AD
F7 00
31 00 28
FB
CD C6 C0
C3 BA C2
EXOS 00
LD SP,2800
EI
CALL C0C6
JP C2BA
; rendszer alaphelyzet
; verem az 1. LAP-on
; megszakítás engedélyezése
; rutinok a nullás lapra
; folytatásra

Itt álljunk meg egy pillanatra. A meghívott rutin a nullás lapra másolja a ROM-ból a BASIC nélkülözhetetlen RST rutinjait, érintetlenül hagyva természetesen a hasonló módon odatöltött EXOS rutinokat (a RST 30H-t és a megszakításkezelő bevezetőjét). Az új RST rutinokkal - köztük az értelmező talán legfontosabb rutinrendszerével, a RST 10H BASIC funkcióhívással - külön pontban foglalkozunk. A hívott rutin ezután beírja a szoftver megszakításkezelőjének címét (00FFH) a SOFT-ISR változóba (a 003D/EH címekre), majd saját szegmensszámát (a 3. LAP-ról kiolvasva) a 00C5H címen tárolja. Ez a momentum fontos lesz még, ha egy RAM-ba töltött "fantom" BASIC-et akarunk a rendszerbe illeszteni.
A nullás lap inicializálása után az indítási főág a BASIC munkaterületeket készíti elő. Először nullázza a rendszerváltozó és táblaterületeket, majd betölti a működés alapjául szolgáló munkatáblákat:

C28A
C28D
C290
C291
C292
C294
C297
C29A
C29E
C2A1
C2A4
C2A5
C2A8
C2A9
21 00 02
01 54 0E
AF
77
ED A1
EA 91 C2
22 46 02
DD 21 00 02
CD 63 ED
DD 75 0C
29
11 54 0E
19
CD 79 F5
LD HL,0200
LD BC,0E54
XOR A
LD(HL),A
CPI
JP PE,C291
LD (0246),HL
LD IX,0200
CALL ED63
LD (IX+0C),L
ADD HL,HL
LD DE,0E54
ADD HL,DE
CALL F579
; változóterület kezd.
; hossz
; A=0
; 0-val feltölt
; HL=HL+1: BC=BC-1: BC=0?
; tovább nulláz
; végcím változóba
; BASIC báziscím
; HL= működő RAM szegmensek száma (n)
; változóterületre
; HL=2*n
; RAM-térkép kezdőcím
; 2*n bájtot kihagy
; utána tölti a token- és függvénytáblát

A kulcsszótáblával és a függvények táblázatával, mint a memóriatérkép igen lényeges elemeivel, a 3.2 alfejezetben foglalkozunk részletesen.
A táblák végén tulajdonképpen kezdődhet az aktív BASIC munkaterület. Be is tölti ezt a címet a program a megfelelő rendszerváltozókba, de ez még nem a végleges érték. Az értelmező most ui. meghívja a BASICX rendszerbővítőt, lehetőséget adva ezzel a német verziónak a szükséges változtatásokra:

C2AC
C2AF
C2B2
C2B5
C2B8
22 1E 02
22 1C 02
22 20 02
11 0A FA
F7 1A
LD (021E),HL
LD (021C),HL
LD (0220),HL
LD DE,FA0A
EXOS 1A
; munkaterület kezdőcím
;       a
; rendszerváltozókba
; "BASICX" szöveg címe
; bővítők lekérdezése

Ez a változtatás gyakorlatilag a RST 10H rutin eltérítését jelenti: a 4. szegmens egy kis rutint ragaszt az imént kialakított táblaterület végéhez, amely a német verzióban sajátos kezelést igénylő BFH BASIC funkcióhívást (hibaüzenet kiírása) saját kezelőjére irányítja (l. 3.3 alfejezet).
A munkaterület kezdőcíme tehát néhány bájttal hátracsúszott (ezt be is jegyzi a bővítő), mire a vezérlés visszakerül a főágra. A következő lépések a törölt BASIC alapállapotot hozzák létre. Nulla program-hossznak megfelelően állítják be a programterület utáni BASIC változóterület mutatóit és a programterület kezdetére állítják a programszöveg mutatókat (pl. a DATA-mutatót). Itt történik meg a LOGO alapirány (90 fok) beállítása és a szükséges csatornák megnyitása a BASIC alapértelmezés szerint:

KEYBOARD: 69H (105D)
VIDEO: 66H (102D)
EDITOR: 00H (0D)
SOUND: 67H (103D)
NYOMTATÓ: 68H (104D).

Az előkészítés befejezéseként a rutin kiírja az INFO sorokat, majd a grafikus lap alapértelmezéséhez GRAPHICS 4-nek megfelelő változókat állít be:

C2BA
C2BD
C2C0
C2C3
C2C6
C2C7
C2CA
C2CD
C2CF
C2D2
CD 90 ED
CD C7 CC
D7 92 07
     96 19 0E
     00
CD D8 F1
CD 5B D8
3E 01
32 0F 02
32 10 02
CALL ED90
CALL CCC7
BASIC 92,07
BASIC 96,0E19
BASIC 00
CALL F1D8
CALL D85B
LD A,01
LD (020F),A
LD (0210),A
; RAM-térkép vizsgálat
; (NEW)
; PI/2 -> MREG
; MREG -> LOGO irány
; funkcióhívások vége
; csatornák inic.
; INFO kiírás
; GRAPH. alapérték
; MODE=1 (nagyfelbontás)
; COLOR=1 (4 szín)

A BASIC-ként megadott utasításokat, a RST 10H által kiváltott funkciókat, egyelőre fogadjuk el az adott formában. Ezekkel a 3.3 alfejezetben foglalkozunk. Az inicializálás utolsó mozzanataként még egy fontos lépés van hátra: a melegindítási címet 0104H-re állítja az értelmező, majd újra inicializálja a nullás lapon felépített RST rutinokat (ide már máshonnan is léphet a rendszer, tehát ez nem szükségtelen művelet):

C2D5
C2D7
C2D8
C2DA
C2DC
C2DF
C2E2
C2E3
C2E5
C2E8
DB B2
F5
3E FF
D3 B2
21 04 01
22 F8 BF
F1
D3 B2
CD C6 C0
DD CB 00 C6
IN A,(B2)
PUSH AF
LD A,FF
OUT (B2),A
LD HL,0104
LD (BFF8),HL
POP AF
OUT (B2),A
CALL C0C6
SET 0,(IX)
; pillanatnyi 2. LAP
; szegmense verembe
; rendszerszegmens
; a 2. LAP-ra
; melegindítási cím
; RST_ADDR változóba
; szegmens vissza
; eredeti érték
; nullás lap inic.
; "ok" írható

Ezzel a BASIC rendszer létrehozta a megfelelő alapállapotot és a végrehajtás a beolvasási főágba kerül. Mielőtt rátérnénk az alapállapot (változóterületek és táblák), majd a főágak konkrét megismerésére, tegyünk még 1-2 megjegyzést az eddig elmondottakkal, azok alkalmazásával kapcsolatban.
Az egyik érdekes momentum, hogy az inicializálás folyamán a rendszernek nem kellett tudnia, hogy melyik szegmensen fut. Az EXOS azt a szegmenst lapozta a 3. LAP-ra, amelyikben a bővítőt találta, az pedig innen olvassa be (és tárolja egyébként a 00C5H címen) saját szegmensszámát. Ha tehát pl. az egész 5. szegmenst átmásoljuk egy RAM-szegmensbe, majd ezt bővítőként beláncoljuk a rendszerbe (l. a 2.6 alfejezetet), a :BASIC utasításra ez a RAM-BASIC építi fel a rendszert, ebben fut a teljes értelmező. Egy RAM-BASIC pedig hallatlanul érdekes terület a kísérletezések számára.
Hasonlóan érdekes változtatási lehetőségekhez jutunk akkor is, ha egy saját "BASIC" bővítőt illesztünk a rendszerhez, hiszen mint láttuk, ezt meghívja az értelmező az inicializálás során.

Végül ténylegesen alkalmazzuk is a megszerzett információkat egy komolyabb beavatkozásra. Szerezzünk a BASIC munkaterület előtt egy olyan RAM-területet, amelyet szabadon használhatunk, amely érintetlenül marad programtörlésekkor, beolvasáskor stb. Egy védett terület a mindig belapozott nullás lapon kincset ér további munkánk során. A megoldás egyszerű: állítsuk HL-t a munkaterület új kezdőcímére, majd lépjünk be az inicializálási főágba és bízzuk a rendszerre a változások végigvitelét:

LD HL,2000H
LD A,5
OUT (B3H),A
JP C2ACH
21 00 20
3E 05
D3 B3
C3 AC C2

BASIC-ből betöltve:

100 ALLOCATE 100
110 CODE M=HEX$("21,00,20,3E,05,D3,B3,C3,A2,C2")
120 CALL USR(M,0)

Az INFO paranccsal azonnal meggyőződhetünk róla, hogy a futtatás után (ami persze törli a rutint is) a BASIC kevesebb bájttal dolgozik: hátracsúszott. A programszöveg régi és új kezdőcíme közötti RAM most már a miénk (a következő inicializálásig). Használhatjuk pl. megszakítási rutin tárolására (amelyet igen praktikus a nulláslapon elhelyezni), a BASIC utasítások körének bővítésére, stb.

3.2 Memóriafelosztás, változóterületek

Mint láttuk, az értelmező az inicializálás során alulról fölfelé építi fel a változóterületeit, tábláit. Vizsgáljuk tehát végig mi is ilyen sorrendben a különböző funkciójú tartományokat, méghozzá előbb egy átfogó térképen áttekintve, majd konkrétan is bemutatva az egyes területek határait, feladatait, szerkezetét. Példaként továbbra is a 128 kbájtos, angol/német verziót vizsgáljuk. Az egyes részeknél zárójelben megadott konkrét értékek, címek erre a változatra vonatkoznak.
A nulláslap elején felépített RST rutinokat külön vizsgáljuk a 3.3 alfejezetben. Most a 0200H címen kezdődő rendszerváltozó területtől tekintsük át a memóriatérképet:

 
  0200
BASIC rendszerváltozók
 
  024B
Szövegpuffer
 
  0347
0348
Mutató a szövegpuffer éppen feldolgozott elemére
 
  0379
Munkaterület
 
  0446
Puffer a hibaüzenetekhez
 
  0496 Szövegpuffer az INPUT-hoz
Ideiglenes tároló memóriaeltolásoknál
 
  0595 Numerikus puffer;
munkaterület az elemzéseknél
 
  059E
Puffer a kiírandó számok ASCII alakjához
 
  05B2 Numerikus puffer;
munkaterület a számkonverzióknál
 
  0DBA Z80-as verem
 
  0DBB
Munkaterület (magnetofonműveleteknél)
 
  0DCB
Változók bázistáblája: 32*2 bájt
 
  0E0B 1. rögzített numerikus munkaregiszter
(REG1) - 7 bájt
 
  0E12 2. rögzített numerikus munkaregiszter
(REG2) - 7 bájt
 
  0E19
LOGO irány: 7 bájt (alapérték PI/2)
 
  0E20
Általános munkaterület
 
  0E54 RAM-térkép: 2*(jó RAM-szegmensek száma)
(16 bájt)
 
(0232) (0E64)
A kulcsszavak pointereinek táblája (292 bájt)
 
  (0F7E)
Függvények és belső változók táblája
 
  (12CB) Kiegészítés az RST 10-hez
(német verzió; 16 bájt)
 
(021E) (12DB)
BASIC programtároló
 
    BASIC változóterület
 
(0234)  
Szabad terület
 
- - - - - - - - - - - - - - - - - - - - - - - - - -
(0228)
(0226)

(3FFF)
BASIC munkaverem
 
- - - - - - BASIC terület vége - - - - - -

A térkép természetesen hexadecimális formában tartalmazza a határok címadatait. A táblázatban bal oldalt megadott adatok a rendszerváltozókra utalnak, amelyek az adott címet tartalmazzák, míg a zárójeles értékek az adott verzióban kialakuló címek, ill. adatok.

3.2.1 Rendszerváltozók

A változó területek, táblák kiíratásához jól használhatjuk - esetenként némi módosítással - a 2.3.1 pontban kidolgozott 2.9 számú DUMP-programot. Ezzel a rendszerváltozók területe:

A változók egy része a feldolgozás, végrehajtás közben adódó paraméterek ideiglenes tárolására szolgál, többségüket viszont mi is használhatjuk gépi kódú programjainkban a BASIC rutinok hívásakor vagy saját bővítő készítésekor. Tekintsük át a fontosabb változók funkcióit (hexadecimális címekkel):

0200
Jelzőbájt. Bitjei az értelmező működésének különböző fázisaiban jelzik a végrehajtó rutinok, főágak számára a változásokat, üzemmódokat. Például a 4. bit a TRACE üzemmódot vezérli (ha 1, aktivizálódik a nyomkövető üzemmód), a 2. bit 1 értéke STOP-ot vált ki stb.
0201 Jelző; 0. bitje a szögek megadási módját jelöli ki (ha 0: radián, ha 1: fok).
0202 A BASIC sor elemzése során használt változó: a vizsgált elem típusa (erről még lesz szó a tokenizálás ismertetésénél és a BASIC 20H funkcióhívásnál).
0203 A vizsgált sorelem (pl. változónév) hossza.
0204 Jelzőbájt a sorelemzésnél. Műveleti jeleknél, sorvégnél a tárolt kód értéke.
0205 A billentyűzetről beolvasott karakter ideiglenes tárolója.
0206

A végrehajtás utáni teendők jelzőbájtja. A végrehajtó rutinok saját típusuknak megfelelően állítják be (vagy hagyják meg a 0 alapértéket). A kiváltott funkciók:

  • 0: a következő bájton folytatja a végrehajtást, kettőspontot elfogad a sorban (pl. LET),
  • 1: sorvégnek kell következnie,
  • 2: a következő sor elején folytatja a végrehajtást (pl. AUTO, DATA stb.)
  • 3: a 0216/7H sorra ugrik (pl. GOTO),
  • 4: az END DEF jelzi ezzel a visszatérést a végrehajtási főágba a szubrutinszerűen meghívott DEF alprogramról.
0208 Trace csatorna. Közvetlenül is állítható.
0209 Aktuális csatornaszám a kiíratásokhoz. Alapértéke 0 (EDITOR)
020A Az aktuális program száma (alapérték: 0).
020B A megosztott szegmens száma, ha már kiutalt ilyet az EXOS (alapérték: 0).
020C Működő RAM-szegmensek száma (alapérték: 8). Ez maximálja a nyitható programok számát.
020D A megosztott szegmenst kapott program száma (alapérték: 0; nincs ilyen program)
020E TEXT mód jelző. Ha 0: 40 karakteres mód, ha 2: 80 karakteres mód. Közvetlenül is írható, hatása a következő TEXT-nél érvényesül.
020F A grafikus lap VIDEO MODE aktuális értéke (alapérték: 1; HIRES).
0210 A grafikus lap VIDEO COLOUR aktuális értéke (alapérték: 1; 4 szín).
0211 RND alapszám
0212 A futó program száma
0214/5 Pointer: a feldolgozás alatt álló BASIC sor vizsgált elemére mutat.
0216/7 Pointer: a feldolgozás alatt álló sor elejére mutat.
0218/9 Kétbájtos egész. A BASIC sorszámként értelmezhető számkonstans (pl. GOTO argumentum) tárolója.
021A/B
Az aktuális program kezdőcíme. Esetünkben az érték: 12DB, tehát az inicializálás során felépített táblák utáni első szabad cím. A 0 program általában itt kezdődik, de innen hátratolhatja egy ALLOCATE utasítás vagy egy áthelyezhető modul betöltése. Az összes többi program a 4000H címen (az 1. LAP-on, egy másik szegmensben) kezdődik.
021C/D
A CODE utasítás következő szabad pozíciója (a legközelebbi CODE ettől a címtől kezdve fogja tárolni a bájtokat). Közvetlen beállításának lehetőségét már eddig is számos példaprogramban alkalmaztuk. Alapértéke a BASIC programterület kezdete (12DB).
021E/F A BASIC programterület kezdőcíme (12DB).
0220/1 A ALLOCATE-tel lefoglalható munkaterület kezdőcíme (általában megegyezik az előző értékkel).
0226/7
A BASIC terület felső határa. Programszámtól és mérettől függő érték. Esetünkben: 3FFF, a 0 LAP felső határa. Ha a program mérete (és a változók számára lefoglalt terület) nő, átlépheti a szegmenshatárt, ekkor a felső határ 7FFF, majd BFFF lesz (legalábbis ha másodszorra is teljes szegmenet kapott). Természetesen az 1. LAP-on induló, 0-tól különböző számú programnál már az alapérték 7FFF.
0228/9 Pointer: a felülről lefelé bővülő BASIC munkaverem aljára, egyben aktuális elemére mutat.
022A/B A szükséges memóriaméret ideiglenes tárolója.
022C/D READ mutató: a következő olvasható elem sorának elejére mutat (alapérték a BASIC terület kezdőcíme).
022E/F READ mutató: a következő adatra mutat.
0232/3 A kulcsszótábla kezdőcíme (0E64).
0234/5 A szabad terület kezdőcíme. Ez adja a következő változó tárolási címét.
0236/7 Ideiglenes kezdőcím a lokális változók tárolásához a DEF rutinok futása alatt.
0238/9 A változók számára potenciálisan lefoglalt terület felső határa. Valójában innen számítható a szabad terület.
023A/B A stack-pointer (SP) ideiglenes tárolója a futás során.
023C/D A legutóbbi hiba kétbájtos egészként tárolt típusa (EXTYPE).
023E/F A legutóbbi hibát kiváltó sor címe a memóriában. Esetünkben (0248H) a hiba az input-sorban történt, az előző , változó aktuális értéke (240DH = 9229D) szerint ez egy STOP volt.
0242/3 Puffercím a memóriába íráshoz. Az aktuális érték (BEDDH) éppen az állapotsor egyik pozíciója (végcím-2).
0246/7 Kétbájtos érték az RND számításhoz. A 0211 változóval együtt használja a rendszer.

3.2.2 Pufferterületek

A feldolgozás, kiértékelés fázisaiban használt pufferek számunkra jórészt érdektelenek, a kezelőik magánügyei. Közvetlen felhasználásra leginkább az üzenet- és bemeneti puffereknél kerülhet sor. A 0248H címen kezdődő bemeneti pufferrel, a beírt sor feldolgozásával a tokenizálás során foglalkozunk. A hibaüzenet-puffert viszont bemutatjuk egy szándékosan kiváltott hiba után:

3.2.3 A változók bázistáblája

A rögzített (0DCBH) címen kezdődő táblázat 32 db kétbájtos címet tartalmaz. Ezek a címek 32 párhuzamosan induló változólánc elejére mutatnak. Innen a változó tárolására szolgáló terület első két bájtjaként egy újabb pointer: a következő láncelem címe olvasható ki. Így folytatódnak a láncok mindaddig, amíg a pointer helyén 0000-t nem találunk.
Ezt a láncrendszert a BASIC építi fel az inicializálás során a belső függvényekből és változókból (mint pl. az ABS, ill. a PI). Az értelmező ezekhez a láncokhoz fűzi hozzá az értékadó műveleteknél az újabb felhasználói változókat. A báziscímről kiemeli az előző pointert, beírja az új változó tárolási címét (ami a BASIC változóterület végén lesz), majd az új elem első két bájtjára betölti a báziscímről kimentett pointert. Így a lánc nem szakad meg, a legutóbb felvett változó kerül a legelső helyre (így azt találja meg legelőször az értelmező) és utána következnek a lánc eredeti elemei.
Azt, hogy a változó a párhuzamos ágak közül melyikbe kerül, a változó neve határozza meg. Lehetne dönteni a név első karaktere alapján is, de az értelmező egy bonyolultabb eljárással egy sajátos ellenőrzőösszegszerű bájtot (pontosabban 0 és 62 közötti páros számot) képez a név karaktereiből, mint az a következő rutinon követhető:

D1A2
D1A5
D1A6
D1A9
D1AA
D1AB
D1AC
D1AE
D1B0
D1B1
2A 47 03
AF
DD 46 03
07
AE
23
10 FB
E6 3E
4F
C9
LD HL,(0347)
XOR A
LD B,(IX+03)
RLCA
XOR (HL)
INC HL
DJNZ D1A9
AND 3E
LD C,A
RET
; keresett név címe
; A=0
; név hossza
; görgetés
; és maszkolás
; a következő karakterre
;      ugrik
; 0<A<64 (páros) kód
; azonosítókód C-be

Az 1. szegmens rutinja adja azt az értéket tehát, amely meghatározza, hogy a 64-bájtos tábla melyik címéről, melyik ágán induljon el az értelmező, ha egy adott változót keres.
Ez a bázistábla az alapja minden változókeresésnek, a láncrendszerbe a BASIC minden pillanatnyilag létező változója fel van véve. Akár ki is írathatjuk az összes változó nevét (változó dump). Bár a változók tárolási struktúráját az illető területeknél tekintjük át, annyit közös tulajdonságként már most elmondunk, hogy a változót tároló elemben a két pointerbájt után egy típusbájt, majd a változó neve következik: elöl a név hossza; aztán a karakterek. Ezt használja fel a következő BASIC program, amely sorra fölfejti az összes láncot:

1 PROGRAM "FUGGVENY.BAS"
100 FOR C=0 TO 62 STEP 2
110   PRINT C;":";TAB(7);
120   LET BAZIS=3531+C
130   LET CIM=PEEK(BAZIS)+256*PEEK(BAZIS+1)
140   IF CIM=0 THEN 220
150   LET HOSSZ=PEEK(CIM+3)
160   FOR N=1 TO HOSSZ
170     PRINT CHR$(PEEK(CIM+3+N));
180   NEXT
190   PRINT " - ";
200   LET BAZIS=CIM
210   GOTO 130
220   PRINT
230 NEXT

A futás eredménye:

0 : COS -
2 : C - TRUNCATE - IP - HEX$ -
4 : SEC - RGB - LTRIM$ -
6 : BAZIS - RTRIM$ - RED - COT -
8 : COSH -
10 : STR$ - MAXLEN - EXSTRING$ - ACOS -
12 : SGN - POS - BLUE -
14 : N - HOSSZ - SIZE - REM - RAD - EXTYPE - BLACK -
16 : SIN - RND - CEIL -
18 : CIM - VERNUM - ROUND - ABS -
20 : TIME$ - LCASE$ - INKEY$ - BIN -
22 : VAL - PEEK -
24 :
26 : LBOUND - ASIN -
28 : TAN - ORD - IN - FP - DEG -
30 :
32 : USR -
34 : SPEEK - LOG2 - ATN -
36 : YELLOW -
38 : VER$ - EPS - ANGLE -
40 : PI - MIN - LOG - CSC -
42 : WORD$ - SINH -
44 : INT -
46 : MOD - MAX - JOY -
48 : LOG10 - DATE$ -
50 : TANH - EXLINE - CYAN -
52 : MAGENTA - LEN - FREE - EXP -
54 : UCASE$ - GREEN -
56 : UBOUND -
58 : CHR$ -
60 : SQR -
62 : WHITE - INF -

A listában megtaláljuk a program változóit (C, BAZIS, CIM, HOSSZ, N) a 2, 6, 14 és 18 relatív című lánc elején, majd az összes beépített változót és függvényt. Ez zavaró lehet akkor, ha minket csak a program változói érdekelnek. Ekkor felhasználhatjuk, hogy a program változói mind a BASIC kezdete után tárolódnak, másrészt a lánc elejére épülnek be. A keresést tehát már akkor leállíthatjuk, ha a kiolvasott pointer (CIM) a BASIC kezdete (esetünkben 12DBH =4827D) alá mutat:

140 IF CIM<4827 THEN GOTO 220

3.2.4. RAM-térkép

A BASIC rendszer 16 bájton (más kiépítésben az érték: 2 (működő RAM-szegmensek száma) tárolja az összetartozó programszám / kiutalt szegmens adatokat:

A lista azt mutatja, hogy megnyitottuk az 1. programot (EDIT 1) és az a rendszertől az F9H szegmenst kapta. Ha a program átlép a szegmenshatáron, a rendszer új elemként, 128-cal megnövelt programszámmal ezt a lapot is felveszi a térképre (természetesen a túlnyúló résznek kiutalt szegmens számával).
Ez a térkép az alapja az EDIT, CHAIN műveleteknek, a programok belapozásának stb.

3.2.5 Kulcsszótábla

A BASIC kulcsszavak végrehajtó rutinjait, legalábbis belépési pontjaikat, verziónkban az 5. szegmens tartalmazza. A belépési pontokat, a kulcsszavak nevét és típusbájtját egy táblázat foglalja össze, amely a szegmens F5A7 címén kezdődik. A paramétertábla elemei a következő paramétereket adják meg (az első elem példáján):

F5A7: F8 CC 61 D0 53 08 ALLOCATE

Az értelmező nem közvetlenül ezen tábla alapján dolgozik, hanem a rugalmasságát, bővíthetőségét jelentősen megnövelő lépésként felépít egy táblázatot a nulláslapon (esetünkben a 0E64 címtől 0F7D-ig), amely sorban tartalmazza a kulcsszavak pointereit: a paramétertábla adott elemének címét és szegmensét (ez esetünkben mindig 5). A táblázat kezdőcímét a 0232/3H rendszerváltozó adja meg. A tábla két nulla bájttal kezdődik, majd a pointerek száma (tehát a rendszer által kezelt BASIC kulcsszavak száma) következik (5DH = 93D). Ezt a bevezetőt követik a 3-bájtos pointerek (itt tehát 93*3 bájt). Az első elem pl.:

0E67: A7 F5 05

Ez tehát az 5. szegmens F5A7 címére, a paramétertábla előbb látott ALLOCATE elemére mutat. A további táblaelemek innen számított relatív sorszáma (1.: ASK ,2.: AUTO, stb.) egyben a kulcsszavak azonosítókódja, tokenje is lesz az értelmező számára.
A tokentábla kiolvasására, a paraméterek kilistázására egy programot is érdemes készíteni:

1 PROGRAM "Kulcsszo.bas"
100 DEF H$(N)=CHR$(INT(N/16)+48-7*(INT(N/16)>9))&CHR$(MOD(N,16)+48-7*(MOD(N,16)>9))
110 PRINT " token"
120 PRINT " D   H  1.cim 2.cim tp    kulcsszo"
130 PRINT
140 LET TABLA=PEEK(562)+256*PEEK(563)
150 LET SZAM=PEEK(TABLA+2)
160 FOR N=0 TO SZAM-1
170   LET CIM=TABLA+(N+1)*3
180   LET LO=PEEK(CIM)
190   LET HI=PEEK(CIM+1)
200   LET SG=PEEK(CIM+2)
210   LET ROMCIM=HI*256+LO
220   PRINT N;TAB(5);H$(N);": ";
230   FOR I=0 TO 5
240     PRINT H$(SPEEK(SG,ROMCIM+I));" ";
250   NEXT I
260   LET HOSSZ=SPEEK(SG,ROMCIM+5)
270   FOR I=1 TO HOSSZ
280     PRINT CHR$(SPEEK(SG,ROMCIM+5+I));
290   NEXT I
300   PRINT
310 NEXT N

A futás eredményét a 6. függelékben adjuk meg. Ez a tokentábla az értelmező működésének alapja, mind tokenizáláskor, mind végrehajtáskor vagy listázáskor ezt a táblát használja. Mivel ez RAM-ban van, egy biztos és kényelmes módot ad a BASIC bővítésére, a funkciók megváltoztatására stb. Erre természetesen még visszatérünk.

3.2.6 Függvénytábla

A beépített függvények és változók kiértékelő rutinjait is a ROM (jelen esetben az 1. szegmens) tartalmazza. Itt is megtalálható egy összesített táblázat a függvények paramétereitvel (típus, hossz, kiértékelő rutin címe). A rendszer ebből is egy RAM-táblát készít az inicializálás során (közvetlenül a tokentábla után), de ez merőben más típusú, mint a kulcsszavak pointer táblázata.
A függvénytáblában változó formában tárolja az értelmező a belső változók és függvények paramétereit is. A változólánc leendő pointeréhez szükséges 2 bájt kihagyása után a ROM-ból a táblába másolja a következő elemeket:

Ezután 1-et ír az elem végére (a kiértékelő rutin szegmense). A 0F7E-12CA területen így kialakított függvénytábla első eleme pl.

0F7E: 00 00 0C 03 41 42 53 21 C3 01

Az ABS nevű függvény tehát numerikus és kiértékelő rutinját az 1. szegmens C321H címén találjuk.
A teljes függvénytáblát a 7. függelékben adjuk meg (hiszen ez is igen jól használható terület a BASIC-be való belenyúláshoz).
A függvényeket, belső változókat tehát a BASIC változók formai szabályai szerint tárolja a rendszer. A formai egységességnek kezelésbeli egységesség az eredménye: a tokenizálás, tárolás során a függvényeket a felhasználói változóktól gyakorlatilag semmi nem különbözteti meg. A végrehajtásnál is egységes a keresés, de a kiértékelés már nyilván nem: a belső függvényeknél az adatmező helyén a kiértékeléshez használandó rutin címét tárolja az értelmező.
Az egységes keresés érdekében természetesen be is kell vezetni a függvénytábla elemeit a változóláncokba. Ezt meg is teszi a rendszer az inicializálás során (mint annak eredményét a változók bázistáblájának (0DCBH) vizsgálatakor láttuk).

3.2.7 A BASIC programtároló

A programterület a függvénytábla - és a német verzióban utána tárolt RST 10H kiegészítés - fölött kezdődik. A terület kezdőcímét a 021E/FH rendszerváltozó tartalmazza (12DBH). Alaphelyzetben ettől a címtől kezdi tárolni az értelmező a beírt (vagy beolvasott) BASIC programot, de lehetőség van arra is, hogy n bájtot lefoglaljunk a terület elején egy ALLOCATE n utasítással. Ekkor a program (vagy a leendő program) n bájttal hátracsúszik. Az aktuális program tényleges kezdőcímét a 021A/BH rendszerváltozó adja meg számunkra (és az értelmező számára is).
Ez a rendszerváltozó akkor is érvényes címet tartalmaz, ha egy 0-tól különböző programot lapozunk be, amelyet mindig a 4000H címtől tárol a rendszer.
Példaként nézzük meg a memóriakiírató DUMP program első sorainak memóriabeli elhelyezését. A programban nincs ALLOCATE, így a programszöveget a 12DBH címtől kereshetjük:

A kiíratás során (az összehasonlítás érdekében) kilistáztuk a tárolt program első sorait is.
Az adott példán kövessük végig a programtárolás struktúráját a kezdőcímtől indulva:

3.2.8 Változóterület

Az értelmező a BASIC program futása során a tárolandó változókat (numerikus és füzérváltozókat, DEF rutinneveket stb.) a program utáni területen helyezi el. Egy-egy újabb elemet mindig a 0234/5H rendszerváltozó által mutatott szabad terület kezdetére tölt, de a hosszabb programfejlesztés és futás során kialakuló kép korántsem rendezett, nehezen áttekinthető (a nyúló-rövidülő program, a törlések után újra és újra bevezetett változók miatt az érintett memóriaterület meglehetősen kusza képet mutat). A fontos az - számunkra és az értelmező számára egyaránt -, hogy a változókeresés alapjául szolgáló bázistáblából induló változóláncok mindig tökéletesen egyértelműek. A memórialista elemzése helyett használjuk mi is ezt a lehetőséget a változótípusok bemutatására, a terület felhasználásának illusztrálására.
A változók tárolási címének megkeresésére fogadjuk el segédeszközként azt a gépi rutint (V-CIM), amelynek működését majd a BASIC funkcióhívások ismertetésekor mutatjuk be. A rutin a hívó BASIC szövegben az utasítás után írt változó tárolási címét keresi meg és adja vissza a BASIC-nek (a típusbájtra mutat).
Ami közös a változóterületen tárolt különböző típusú elemekben, az a már említett fejrész:

A típusbájt értékei:

A tényleges információt a fejrészt követő bájtok tárolják, de már típusonként lényegesen különböző módon. Ezt tekintsük át konkrét példákon (megismerve közben az értelmező számábrázolási logikáját is) a következő program segítségével:

1 PROGRAM "V-cim.bas"
100 ALLOCATE 100
110 NUMERIC CIM
120 CODE V_CIM=HEX$("D7,8E,00,E5,D7,20,00,E1,C9")
130 !
150 LET A=5
160 LET CIM=USR(V_CIM,0) A
170 CALL FEJ
180 PRINT "A=";A;TAB(16);
190 CALL ADAT(6)
200 !
240 DEF FEJ
250   FOR N=CIM-2 TO CIM
260     PRINT H$(PEEK(N));" ";
270   NEXT
280   LET HOSSZ=PEEK(N)
290   FOR I=1 TO HOSSZ
300     PRINT CHR$(PEEK(N+I));
310   NEXT
320   LET CIM=N+HOSSZ
330   PRINT
340 END DEF
350 DEF ADAT(H)
360   FOR N=1 TO H
370     PRINT H$(PEEK(CIM+N));" ";
380   NEXT
390   PRINT
400 END DEF
410 DEF H$(N)=CHR$(INT(N/16)+48-7*(INT(N/16)>9))&CHR$(MOD(N,16)+48-7*(MOD(N,16)>9))

A program első szakasza betölti az említett V-CIM gépi rutint, a felhasználói rutinok pedig kiírják a standard fejet (pointer-típus-név), ill. az adatmező megadható számú bájtját (hexadecimálisan). Magát a futást a 150-200 szakasz néhány sora irányítja.

Numerikus változó (00H típus)

Az adott esetben a futtatás eredménye:

A fej:

Az adatmezőn a változó értékét bináris egészként látjuk viszont. A számábrázolás módját leginkább kísérletezve, különböző értékeket adva lehet követni. Figyeljünk meg pl. egy előjelváltást az adatmezőn. Ehhez a program aktív részét is át kell kissé alakítani:

1 PROGRAM "V-cim2.bas"
150 LET A=3
160 LET CIM=USR(V_CIM,0) A
170 CALL FEJ
180 PRINT "A=";A;TAB(8);
190 CALL ADAT(6)
200 LET A=A-1
210 GOTO 180

Jól látható, hogy az adott esetekben, az értelmező kétbájtos előjeles egészként ábrázolta az értékeket. Az adatsor végén álló 7FH kód ezt a módot jelzi. Teljesen más a tárolási mód törtszámoknál, nagy egészeknél, olyan esetekben, amikor eleve nem alkalmazható a kétbájtos előjeles ábrázolás:

1 PROGRAM "V-cim3.bas"
150 LET A=1.234567891
160 LET CIM=USR(V_CIM,0) A
170 CALL FEJ
180 PRINT "A=";A;TAB(16);
190 CALL ADAT(6)

Feltűnő, hogy ha az első öt adatbájtot egymás elé illesztjük, megkapjuk a tárolt szám jegyeit. Ebben az esetben a tárolás tehát BCD alakban történik, méghozzá 5 bájton (10 decimális jegyen). A nagyságrend ábrázolási módját jól követhetjük a szám ismételt 10-es szorzásával:

1 PROGRAM "V-cim4.bas"
150 LET A=1.234567891
160 LET CIM=USR(V_CIM,0) A
170 CALL FEJ
180 PRINT "A=";A;TAB(16);
190 CALL ADAT(6)
200 LET A=A*10
210 GOTO 180

Mint látható, a tárolt alak egységesen 1.23 ... 10^n
ahol a szám nagyságrendjét - 10 kitevőjét - az adatmező 6. bájtja adja meg: kitevő = 6. bájt - 40H
Igaz ez 1-nél kisebb számokra is (itt természetesen a kitevő negatív):

150 LET A=1.234567891
160 LET CIM=USR(V_CIM,0) A
170 CALL FEJ
180 PRINT "A=";A;TAB(21);
190 CALL ADAT(6)
200 LET A=A/10
210 GOTO 180

Most már csak a negatív lebegőpontos számokra nézzünk példát:

150 LET A=-1.234567891
160 LET CIM=USR(V_CIM,0) A
170 CALL FEJ
180 PRINT "A=";A;TAB(21);
190 CALL ADAT(6)
200 LET A=A/10
210 GOTO 180

Mint látható, a negatív előjelet igen egyszerűen a kitevőbájt beírt 7. bitje jelzi.
Összefoglalva a számábrázolás módját:

Az egész típusú tárolás ábrázolási tartománya elvileg - 32768 és 32767 között van, azonban az értelmező, legalábbis a változó érték betárolásakor, csak a 10 000-nél kisebb abszolút értékű számokat ábrázolja ilyen módon (ezért 9999 a maximális BASIC sorszám). Az értékek megváltozásánál, a műveleteknél esetleg szükséges konverzió a két mód között automatikusan megtörténik a BASIC aritmetikai rutinjaiban.

Füzérváltozó (01H típus)

A karakterfüzérek tárolási módja lényegesen egyszerűbb:

1 PROGRAM "V-cim5.bas"
150 LET A$="STRING"
160 LET CIM=USR(V_CIM,0) A$
170 CALL FEJ
180 PRINT "A$=";A$;TAB(14);
190 CALL ADAT(8)
200 !

Az adatmező első bájtja a lefoglalt bájtok számát adja (84H = 132D), a 2. bájt a ténylegesen tárolt karakterek száma (6). Ezt a karakterek ASCII kódja követi. A lefoglalt terület hátralévő része érdektelen.
Az inicializálatlan változót a 2. bájt FFH értéke jelzi.

Numerikus tömb (02H típus)

Itt is egy példán követhetjük a tárolás módját:

1 PROGRAM "V-cim6.bas"
140 DIM A(3 TO 6,1 TO 2)
150 LET A(3,1)=1.23:LET A(4,1)=3.21:LET A(6,1)=PI
160 LET CIM=USR(V_CIM,0) A
170 CALL FEJ
180 !
190 CALL ADAT(79)
200 !

Az adatmezőn az indexek számait (1 vagy 2) megadó bájt (esetünkben 2) után 5 db 6-bájtos (szokásos alakú) numerikus érték következik:

Ez a bevezető rész teljes akkor is, ha nincs 2. index, csak akkor érdektelen a mező ezen része.
Ezután következnek sorra a tömb elemei (ugyancsak 6-6 bájton). Esetünkben a sorrend:

A(3,1)   A(3,2)
A(4,1)   A(4,2)
A(5,1)   A(5,2)
A(6,1)   A(6,2)

Most látunk példát az inicializálatlan (értéket nem kapott) elemek megjelölésére is (az 5. és 6. bájt: FF 7F)

Füzértömb (03H típus)

A fentivel megegyező bevezető rész után ugyanilyen sorrendben következnek a tömb elemei az egyszerű füzér adatmezőnek megfelelően.

Felhasználói függvény (04H és 05H típus)

Az adatmező első része egy (a függvénytípusnak megfelelő) érvénytelenített változó mező (a névnek megfelelő lokális változó ideiglenes tárolására), amit egy pointer követ: a függvényt definiáló BASIC sor memóriabeli címe.

Belső függvény vagy változó (0CH vagy 0DH típus)

Ezek a változók a függvénytábla elemei. Mint ott láttuk, az adatmező 3 bájt: a kiértékelő rutin címe (LO,HI) és szegmense.

3.2.9 BASIC munkaverem

Az értelmező a belső változók, visszatérési címek stb. tárolására a BASIC terület felső részét használja, méghozzá veremszerűen, analóg módon a Z80-as veremkezelésével. Ez az analógia a GOSUB-RETURN utasításoknál a legerősebb: az értelmező a GOSUB-ot követő sor címét tölti a verembe, majd a RETURN hatására onnan olvassa vissza az utasításszámlálóba éppen úgy, mint a mikroprocesszor a CALL-RET utasításoknál. Hasonló a memóriahasználat módja is: a BASIC is felülről lefelé növeli a vermet, az új elemet mindig az előző alá tölti és az aktuális elemre (egyben a szabad terület fölé) egy pointer, mondhatni BASIC STACK-POINTER mutat: a 0228/9H rendszerváltozó.
Az értelmező azonban sokféle elemet tárol ideiglenesen a veremben. Ezek közös tulajdonsága mindössze az, hogy mindegyik egy típusbájttal kezdődik. A futás során használt kódok:

2: numerikus adat (9 bájt)
A BASIC vermet az aritmetikai rutinok is használják. Ide töltik a konstansokat, többoperandusos műveletek adatait stb. Itt marad a kiértékelés végeredménye is, innen írja ki pl. a PRINT rutin. A veremben tárolt érték magját a szokásos 6 bájton tárolt érték adja (10 számjegy 5 bájton + a kitevő), de a számjegyek előtt és után még 1-1 bájtot tárol a rendszer a műveletek során várható túlcsordulások és alulcsordulások miatt.
Például PI értéke a BASIC veremben a következő módon tárolódik (hexadecimálisan):

02 00 54 26 59 41 31 00 40

3: füzér (2 bájt + a karakterek)
A karakterfüzérek a BASIC veremben is, a szokásos módon tárolódnak (a típusbájttal kiegészülve). Például a TEXT szöveg:

03 04 54 45 58 54

4: a FOR-NEXT ciklus paraméterei (25 bájt)
Az értelmező a program futása során a következő veremadat alapján dolgozik:

Nézzünk meg egy egyszerű példát illusztrációként:

1 PROGRAM "Verem.bas"
100 FOR I=0 TO 24
110   LET VEREM=PEEK(552)+256*PEEK(553)
120   PRINT H$(PEEK(VEREM+I));" ";
130 NEXT
140 PRINT
150 STOP
160 !
170 DEF H$(N)=CHR$(INT(N/16)+48-7*(INT(N/16)>9))&CHR$(MOD(N,16)+48-7*(MOD(N,16)>9))

Esetünkben tehát a paraméterek:

A ciklus megkezdése után tehát csak a ciklusváltozó marad igazi BASIC változó (értékét bármikor átírhatjuk a ciklus futása közben is), míg a lépésköz és végérték adatai belépéskori aktuális értékükön tárolódnak a ciklus befejezéséig. A tárolás módjának ismeretében viszont közvetlen beírással megváltoztathatjuk ezeket az értékeket is.

5: a DO-LOOP ciklus paraméterei (5 bájt)
A verembe töltött elem:

Az értelmező itt egyéb paramétereket nem tárol, 4 ciklusfeltételeket a DO, ill. a LOOP kiértékelésénél veszi figyelembe.

6: GOSUB paraméter (3 bájt)
Itt az értelmező a típusbájt (6) után a GOSUB-ot követő BASIC sor címét tölti a munkaverembe. Ez okozza azt az ellentmondást, hogy bár a rendszer formálisan elfogad több utasítást is a GOSUB sorban (pl. GOSUB 100: PRINT X), a RETURN után ezt mégsem veszi figyelembe: a következő sor elején folytatja a végrehajtást.

7: a felhasználói függvények (DEF rutinok) paraméterei (6 bájt)
A DEF rutinok és sorok a BASIC futás és kiértékelés szerves részei, lényegében csak a változóterület kezelésében különböznek némileg a program egyéb rutinjaitól és értékadásaitól. Ennek megfelelően hívásukkor az értelmező a munkaverembe tölti a változóterület pillanatnyi adatait és ezek alapján

A felhasználói függvény alapvetően kétféle módon hívható meg:

1. A PRINT RUTIN vagy A = RUTIN típusú hívás egy értéket vár vissza a veremben.

2. A CALL RUTIN hívás nem vár vissza adott értéket.

Ennek megfelelően a DEF paraméterblokk:

8: WHEN paraméterek (12 bájt)
Mivel a kezelni kívánt hiba a blokkutasításoktól függetlenül történhet meg, a hiba esetén használandó HANDLER címét és a blokkparamétereket feltétlenül veremben kell tárolni. A WHEN blokkok egymásba skatulyázhatók, egy adott hibamélységhez adott kezelő tartozik, tehát a skatulyázási mélységet is fel kell venni a WHEN paraméter blokkba:

Az értelmező a BASIC verem különböző típusú elemeit típusuk alapján ismeri fel, így határozza meg a hosszukat, így keres közöttük és így törli a feleslegessé vált elemeket. Ugyanakkor a gépi kódú programozásban - főként, ha a BASIC aritmetikát is fel kívánjuk használni - nélkülözhetetlen a verem saját célú igénybevétele. Különösen kell tehát ügyelnünk, hogy ne hagyjunk szemetet a veremben munkánk után (ezt BASIC funkcióhívások is segítik), vezérlésátadással járó rutinban pedig tekintettel kell lennünk az esetleg átlépett blokkhatárokra is. Ezért is adtuk meg a veremblokkok paramétereit viszonylag részletesen.

3.3 A BASIC Restart rutinok

Mint az inicializálási folyamatnál láttuk, a BASIC is felépíti a nulláslapon (a ROM-ból átmásolva) azokat a RST hívással aktivizálható alaprutinjait, amelyek nélkülözhetetlenek a működéséhez. Ezek funkciói:

BASIC környezetben futó, esetleg azt bővítő gépi kódú programjainknál mindegyik hívás hasznos lehet (sőt a RST 10H a BASIC leghasznosabb rutinja lesz számunkra az általa elérhető rutinkönyvtár révén). Közülük a RST 08H működését feltétlenül érdemes lépésenként is végigkövetni, már csak azért is, mert ezt egy közbenső belépéssel talán még praktikusabban használhatjuk, mint rendeltetésszerűen.
A rutin egy nagyon szellemes ötlettel oldja meg az azonos lapon, de különböző szegmensen futó rutinok át-, majd visszalapozását. A fogás lényege, hogy a célrutin címét egy mindig belapozott (nulláslap!) RAM-ba írt CALL nn utasítás argumentumába, tölti és ezen keresztül hívja meg:

0008
000B
000E

005C
005D
005E
0061
0062
0064
0067

0069
006B
006C
006D
0070

0071
0074
0075
0076
0079
007A
007B

00F5
00F8
00F9
00FB
00FD
00FE
32 68 00
22 F6 00
18 4C

E5
F5
3A FA 00
67
DB B3
32 FA 00
3E xx

D3 B3
F1
E3
CD F5 00
E3

CD F8 00
F5
7C
32 FA 00
F1
E1
C9

CD xx xx
F5
3E 05
D3 B3
F1
C9
LD (0068),A
LD (00F6),HL
JR 005C

PUSH HL
PUSH AF
LD A,(00FA)
LD H,A
IN A,(B3)
LD (00FA),A
LD A,xx

OUT (B3),A
POP AF
EX (SP),HL
CALL 00F5
EX (SP),HL

CALL 00F8
PUSH AF
LD A,H
LD (00FA),A
POP AF
POP HL
RET

CALL xxxx
PUSH AF
LD A,yy
OUT (B3),A
POP AG
RET
; A munkaterületre (xx)
; HL munkaterületre (xxxx)
; folytatásra ugrik

; regiszterek
;     verembe
; előző 3. LAP-paraméter
;     mentése
; pillanatnyi 3. LAP (yy)
; (yy) munkaterületre
; munkaterületre tárolt érték
;     visszaolvasása
; célrutin szegmense

; HL: belépési érték
; meghívja a célrutint
; visszaadott HL verembe,
;     3. LAP-paraméter vissza
; belépési 3. LAP vissza

; előző 3. LAP-paraméter
;     munkaterületre
; visszaadott A
; visszaadott HL


; meghívja a célrutint
; mentés
; előző érték
;     visszalapozása
;visszaadott A

A rutin lényegesen hatékonyabb, mint az EXOS által ugyancsak kiépített (B217H-) lapozórutin (amit most nem használhatunk, mert az a 0. szegmenst lapozza vissza kilépéskor).
Mint a listából kiolvasható, a RST 08H hívás bemenő paraméterei:

A rutin minden regisztert megőriz és átad a célrutinnak (majd visszaad a hívónak), a futás végére a hívó szegmensét lapozza vissza. Hatékonysága akkor használható ki igazán, ha mi töltjük a munkaterületekre rutinunkban a címet és a szegmenst (l. 0008H és 000BH sorok), majd A és HL tetszés szerinti értékével a 005CH címen hívjuk a lapozórutint. Sok praktikusan használható ROM-rutin igényli ui. az A és HL regisztereket is bemenő paraméterként.
A BASIC funkcióhívást elindító RST 10H rutin lépéseit funkcionálisan tekintsük át:

Ez a folyamat korlátlanul ismétlődhet mindaddig, amíg a beolvasott adat 00H nem lesz. Az ehhez tartozó végrehajtó rutin ui. visszaállítja a hívási regiszter- és memóriaállapotot, majd visszatér a hívóhoz. Ez a sorozatszerűen ismételhető funkcióhívás rendkívül hatékony aritmetikát tesz lehetővé, amiben könnyen építhetjük gépi kódú programjainkba a legbonyolultabb lebegőpontos műveletsorokat, sorfejtéseket stb. Több rutin segíti ugyanakkor az egyszerű beolvasást és kiíratást, szövegelemzést stb. A fontosabb, általánosan használható funkciókat a következő pontban írjuk le.
Most térjünk vissza a szétválogatás műveletéhez. A programszövegből beolvasott funkciószám értéke két tartományba eshet. A 00H-41H funkciókat a BASIC-UK szegmens (esetünkben az 5.) kezeli, végrehajtó rutinjaik belépési címeit a szegmens C0E8H-C16BH táblázata tartalmazza. A 80H-C5H funkciók rutinjai az 1. szegmensben vannak, táblázatuk a szegmens elején található (0000H-C08BH). A két táblázatot DUMP-szerűen megadjuk, ebből az olvasó is kikeresheti egy-egy rutin címét:

A szétválogatás menetébe a német verziójú gépeknél egy érdekes elem kerül. A német kiterjesztés bővítője az alaphelyzetben felépülő rutin néhány bájtját kiemeli és helyükre tölti a CALL nn utasítás kódjait (konkrétan a függvénytábla után beírt néhány bájtos kiegészítés címét adja meg). A kitérőben természetesen ellátja a kiemelt bájtok funkcióját és egy kód (a hibaüzeneteket kiírató BFH funkció) esetén saját rutincímét és szegmensszámát adja meg, egyébként az eredeti módon folytatja a kiértékelést. Erre azért hívtuk fel a figyelmet, mert némi gyakorlat után az olvasóban is felmerülhet egy-egy funkció elfogásának szükségessége.
A RST 18H és RST 20H rutinok a felhasználó számára a hibakezelést teszik igen egyszerűvé. A RST 18H hívás bemenő paramétere az A (EXOS hibakód) és

EXOS n
RST 18H

alakban használható az esetleges EXOS hibák azonnali kiszűrésére. Ha nincs hiba, akkor visszatér és a következő utasításnál folytatja a végrehajtást.
A RST 20H bemenő paramétere a HL-ben megadott BASIC hibakód (ezek felsorolását a gépkönyv is megadja).

3.3.1 A BASIC funkcióhívások

Mielőtt elkezdenénk a fontosabb BASIC funkcióhívások leírását, térjünk ki két terminológiai jellegű megjegyzésre.

1. A RST 10H-n keresztüli funkcióhívások szintaxisa egy assembly programban:

RST10H
DEFB n
DEFB 00H
; funkcióhívás bekapcsolás
; funkciószám
; kikapcsolás

Mi saját használatra a következőkben a funkcióra jobban utaló

BASIC n
BASIC 00H

jelölést alkalmazzuk, de természetesen egy pl. ASMON-ban megírt programban az assembly (pontosabban a fordító assembler) szabályait kell betartanunk.

2. A funkcióhívások egyik fő területe az aritmetika. Mint a 3.2.9 pontban láttuk, a BASIC verem szolgál a kiértékelés során az adatok feldolgozás közbeni ideiglenes tárolására. A numerikus adatokat egy 9-bájtos, a füzéreket egy (2 + füzérhossz) bájtos blokk tárolja. A továbbiakban ezeket a blokkokat a rövidebb leírás érdekében egységesen munkaregisztereknek (rövidítve MREG) nevezzük. Mivel több argumentumos kiértékeléseknél, számításoknál gyakori a több veremblokk használata, szükség esetén az elsőként megnyitott blokkra MREG1-ként hivatkozunk, a továbbiakra pedig MREG2, MREG3... jelöléssel. MREG1 tehát fizikailag a legfölső blokk a létrehozott elemek közül (a verem felülről lefelé bővül).

Tekintsük át ezek után a gépi kódú programjainkban is jól használható, fontosabb hívások funkcióit. Az áttekinthetőség érdekében alkalmazási példákat majd a fejezet végén adunk.

BASIC 00H (00DE)
 
A funkcióhívás-sorozat kikapcsolása, a belépés előtti memóriakonfiguráció visszalapozása és visszatérés a hívóhoz. Minden funkcióhívás (sorozat) végén a 00H kódnak kell állnia.
BASIC 01H (5/F301)
  Gyakorlatilag megegyezik a RST 20H hívással: a HL kódú hibát kezeli.
BASIC 02H (5/E8EF)
  HL értékét egész típusú numerikus adatként a munkaverembe tölti:
HL MREG
BASIC 03H (5/E35C)
  A MREG tartalmát kiíratható füzérré alakítja a 059EH pufferben:
MREG ASCII
A blokk a veremben marad.
BASIC 04H (5/EECD)
  HL (előjeles kétbájtos egész) előjelét megfordítja.
HL = -HL
BASIC 05H (5/E991)
  A MREG-ben lévő szám előjelének vizsgálata. Eredménye a státusz:
  • Z, ha pozitív vagy 0,
  • NZ, ha negatív.
BASIC 06H (5/E9CB)
  Megfordítja a MREG-ben lévő szám előjelét.
MREG = -MREG
BASIC 07H (5/E9C7)
  MREG = ABS (MREG)
BASIC 08H (5/E591)
  A MREG-ben lévő szám 10-es komplemensét veszi (a BCD kivonás előkészítése). Csak lebegőpontos MREG-tartalom esetén alkalmazható.
BASIC 09H (5/E58D)
  Ha a MREG-ben lévő szám negatív, annak 10-es komplemensét veszi (BASIC 08H)
BASIC 0AH (5/EE26)
  A MREG-ben lévő szám típusának vizsgálata. Eredmény:
  • Z, ha egész,
  • NZ, ha lebegőpontos,
  • A-ban a kitevőbájt.
BASIC 0BH (5/E7F9)
 
A MREG tartalmát (egész típusúra alakítva) HL-be tölti.
MREG HL
Az érékhatár túllépése esetén hibára ugrik. A rutin törli a kiolvasott blokkot a veremből. Érdekessége, hogy E7FAH belépésnél ki is értékeli a programszöveg következő elemét (pl. CAUSE EXCEPTION operandus)
BASIC 0CH (5/E501)
  Összeadás:
MREG1 = MREG1 + MREG2
A művelet után a MREG2 törlődik a veremből, az aktuális elem az eredmény (MREG1). Ez a következő műveletekre is vonatkozik.
BASIC 0DH (5/E4F8)
  Kivonás:
MREG1 = MREG1 - MREG2
BASIC 0EH (5/E5A5)
  Szorzás:
MREG1 = MREG 1 * MREG2
BASIC 0FH (5/E6C8)
  Osztás:
MREG1 = MREG1 / MREG2
BASIC 10H (5/E9E7)
  Összehasonlítja MREG1 és MREG2 tartalmát. A státusz a (MREG1 - MREG2) művelet eredményének megfelelően áll be:
  • Z, ha MREG1 = MREG2,
  • P, ha MREG 1 > MREG2,
  • M, ha MREG 1 < MREG2.

A művelet után mindkét elem törlődik a tárból.

BASIC 11H (5/EAB7)
  A MREG tartalmát elosztja 10^A-val (A az akkumulátor tartalma)
BASIC 12H (5/E9D1)
  A MREG-ben lévő szám nagyságát vizsgálja.
  • NZ, ha MREG <> 0,
  • Z, ha MREG = 0,
Ez utóbbi esetben egész típusra állítja az elemet.
BASIC 13H (5/E865)
  Egészről lebegőpontos típusúra konvertálja a MREG tartalmát.
BASIC 14H (5/E8BD)
  Egy BASIC változó tartalmát viszi át (a HL címről) a MREG-be. Hívása előtt nyilván meg kell keresni a változó tárolási címét (pl. BASIC 8BH)
MREG = X
BASIC 15H (5/EA8B)
  Egy blokkal előbbre is lemásolja a verem aktuális (numerikus vagy string) elemét:
MREG2 = MREG1
BASIC 16H (5/EDE8)
 
Törli a verem aktuális elemét (tetszőleges típus esetén). A BASIC verem Z80-as verem analógiában ez a POP utasításnak felel meg. Igen fontos hívás a verem rendbetételéhez. Valójában nem töröl semmit a veremben, csak a BASIC veremmutatót állítja a fölötte lévő elemre.
BASIC 17H (5/E979)
  Egész típusú 0-t tölt a MREG-be.
MREG = 0
A típusbájt (02) utáni érték:
00 00 00 00 00 00 00 7F
BASIC 18H (5/EA63)
  Lebegőpontos 0-t tölt a MREG-be:
MREG = 0.0
(00 00 00 00 00 00 00 3F)
BASIC 19H (5/EA6F)
  Egész típusú 1-et tölt a MREG-be:
MREG = 1
(00 01 00 00 00 00 00 7F)
BASIC 1AH (5/EA7B)
  Lebegőpontos 1-et tölt a MREG-be:
MREG = 1.0
(00 00 00 00 00 10 00 40)
BASIC 1BH (5/E90A)
  A numerikus MREG tartalmát a BASIC változóba tölti (a HL címre):
X = MREG
BASIC 1CH (E/E925)
  A füzér - MREG tartalmát a BASIC változóba tölti (a HL címre):
X$ = MREG
BASIC 1DH (5/E79C)
  Az A értékét írja a MREG B-vel kijelölt számjegyébe. A = 0 ... 9 lehet
  • B = 4 a legnagyobb helyi értékű digitet jelöli ki
  • B = 13 a legkisebb helyi érték (10 jegy)
BASIC 1EH (5/E7B7)
  Az 1DH hívás fordítottja: A-ba olvassa a MREG-ben lévő szám B-vel kijelölt számjegyét.
BASIC 1FH (5/E93F)
  A MREG-ben lévő számot normálalakra konvertálja: az első értékes karaktert a legnagyobb helyi értékű digit helyére lépteti.
BASIC 20H (5/F259)
 
A programszöveg következő elemének vizsgálata. A BASIC értelmező leggyakoribb hívása a sorok elemzése, végrehajtása közben. A tokenizálással előkészített programszöveg következő (a 0214/5H rendszerváltozóval megcímzett) elemének (változó, konstans, kulcsszótoken, sorszám vagy jel)
  • típuskódját a 0202H,
  • hosszát a 0203H,
  • jelek esetén kódját a 0204H.

rendszerváltozóba tölti. Ezenkívül

  • a 0347/8H mutatót a bevezető kódot követő bájtra állítja (változó, ill. függvény esetén ez a név első karaktere), ez lesz majd a változókeresés alapja;
  • számkonstans esetén az értéket beírja a 0596H pufferbe és - ha BASIC sorszámként értelmezhető 0 és 9999 közötti egész - a 0218/9H rendszerváltozóba is;
  • az átlépett elem utáni címet állítja be következő aktuális címként a 0214/5H változóban;
  • ha az elem típusbájtja 20H alatti (jel), az értéket A-ba is beolvassa (a sor végét A = 1 és Z státusz jelzi)
BASIC 21H (5/E2E4)
  Zárójeles aritmetikai vagy füzér kifejezés szintaktikai elemzése.
BASIC 22H (5/F253)
 
A következő sorelemnek A-nak kell lennie (sorvég, ")" jel, idézőjel stb., l. a 3.4.2 pontot.) Eltérő esetben szintaktikai hibát jelez, egyezésnél pedig a BASIC 20H-ra fut.
BASIC 23H (5/DEE2)
 
Numerikus kifejezés kiértékelése. Az aritmetika alaprutinja. A 0214/5H rendszerváltozó által megcímzett (és BASIC 20H-val már megvizsgált) programszövegben induló, tetszőleges bonyolultságú aritmetikai kifejezést értékel ki. Az eredményt a MREG-be tölti. A rutin tetszőleges mélységben újrahívhatja saját magát is (pl. a zárójeles kifejezések kiértékelésénél, függvényértékek kiszámításánál).
BASIC 24H (5/E096)
  Füzér kifejezés kiértékelése. Az eredmény itt is a MREG-be kerül (természetesen 03 típuskóddal).
BASIC 25H (5/FB9E)
 
Az INKEY$ alaprutinja: lekérdezi a billentyűzetcsatorna (69H) állapotát és ha van olvasható karakter, azt A-ba (és a 0205H ideiglenes tárolóba) tölti. Az A = 0 érték és státusz jelzi, ha nem volt karakter (nem nyomtunk meg billentyűt).
BASIC 26H (5/EEBE)
  A (HL) címen lévő karakter kódját C-be olvassa és nagybetűsíti (ha belépéskor A = 40H volt) vagy kisbetűsíti (A = 60H esetén).
BASIC 27H (5/F094)
  DE = 10 értékkel a következő funkcióra (BASIC 28H) fut.
BASIC 28H (5/F097)
 
Ellenőrzi, hogy van-e még DE bájtnyi szabad hely a BASIC memóriában. Szükség esetén akár a BASIC újabb szegmensekre való kiterjesztésével is megpróbál helyet szerezni. Ha ez nem sikerül, vagy a Z80-as verem nőtt túlságosan nagyra, hibajelzésre fut.
BASIC 29H (5/F010)
  A BASIC munkavermet lecsúsztatja a változóterület végéhez, így a lehető legnagyobb területet felszabadítja a felülről lefelé terjeszkedő EXOS számára.
BASIC 2AH (5/C6C2)
  Az elemzett programszövegben átlépi a szóközöket (20H) és TAB (09H) karaktereket.
BASIC 2BH (5/E41E)
 
A programszövegben talált ASCII formátumú számkonstanst alakítja egész vagy lebegőpontos formátumra és a 0595H pufferbe tölti (valamint a 0218/9H rendszerváltozóba is, ha az érték 0 és 9999 közötti egész).
BASIC 2CH (5/E407)
  A 0595H pufferbe töltött számot átírja a verembe, a MREG-be.
BASIC 2DH (5/D4D4)
  Az RND alaprutinja; A-ban és HL-ben egy 3-bájtos véletlen értéket állít elő.
BASIC 2EH (5/C3FD)
 
A HL sorszámot megkeresi az aktuális BASIC programban. Visszatéréskor HL a keresett sor első bájtjára mutat (ha nincs ilyen sor, HL a következő sor elejére áll és CY = 1).
BASIC 2FH (5/C858)
  A HL számú BASIC sort kilistázza az aktuális (0209H) csatornára.
BASIC 30H (5/C544)
  Törli a programból a HL számú sort, egyben üresre állítja a változóterületek mutatóit és a belső függvényekkel inicializálja a változóláncokat.
BASIC 31H (5/C54D)
  A BASIC-ben önállóan nem létező CLEAR rutin: törli a BASIC változókat, lezárja az 1-99 csatornákat stb.
BASIC 32H (5/EAD5)
 
Végigfut az aktuális BASIC programon és bevezeti a változóterületre a DEF és HANDLER sorok változóneveit (ennek alapján lehetséges pl., hogy programszerkesztés közben, törölt változók mellett is tudunk pl. LIST RUTIN-t kérni).
BASIC 33H (5/EF7A)
  Végigfut a program hátralévő részén és a vizsgált sorral azonos skatulyázási mélységű sort keres (blokkvéget).
BASIC 34H (5/EF19)
  A STOP végrehajtó rutinja.
BASIC 35H (5/EFD7)
  Megkeresi a C kódú kulcsszó paraméter blokkját és HL-t a típusbájtra állítja. Visszatéréskor E-ben a típusbájt van.
BASIC 36H (5/FBB5)
  Egy sort olvas be az aktuális csatornáról a 0248H szövegpufferbe.
BASIC 37H (5/C74B)
  A 0248H szövegpufferből - tokenizálás után - beilleszti a beolvasott sort a BASIC programba, majd törli a változókat, lezárja az 1-99 csatornákat. (BASIC 31H)
BASIC 38H (5/FB66)
  Kiírja az A-ban lévő karaktert az aktuális csatornára. Mivel ez minden BASIC kiíratás alaprutinja, nézzük meg, hogyan működik:
FB66
FB67
FB68
FB69
FB6A
FB6B
FB6E
FB70
C5
D5
E5
F5
47
3A 09 02
FE FF
20 0A
PUSH BC
PUSH DE
PUSH HL
PUSH AF
LD B,A
LD A,(0209)
CP FF
JR NZ,FB7C
; regiszterek
;     verembe


; B= írandó karakter
; A: aktuális csatorna
; =255? (memóriába?)
; kiírásra, ha nem
       ; Memóriába ír, ha az aktuális csatornaszám 255 volt:
FB72
FB75
FB76
FB77
FB7A
FBFB
FBFE
2A 42 02
70
23
22 42 02
AF
21 F7 07
DF
LD HL,(0242)
LD (HL),B
INC HL
LD (0242),HL
XOR A
LD HL,07F7
RST 18
; puffercím
; karakter a pufferbe
; következő pozíció
; pointer beállítása
; A=0
; ezen az ágon
;     érdektelen utasítások
       ; Ide lép, ha az aktuális csatorna nem 255 volt
   ; (B: írandó karakter, A: csatornaszám):
FB7C
FB7E
FB7F
FB80
FB81
BB82
FB83
F7 07
DF
F1
E1
D1
C1
C9
EXOS 07
RST 18
POP AF
POP HL
POP DE
POP BC
RET
; karakter írása
; hibaellenőrzés
; regiszterek
;     visszaolvasása

A BASIC aktuális csatornája a 0209H rendszerváltozó tartalma. Ha ez 255-től különböző, a kiíratás az EXOS-on keresztül történik. Ha viszont az érték 255 volt, a BASIC maga végzi el a kiírást: a karaktert a memóriába- írja a 0242/3H pointer által mutatott címre, majd a következő pozícióra állítja a pointert.
Az aktuális csatorna általában a #0 (EDITOR), ami a kiírást a képernyőre irányítja. Az olyan utasítások, amelyek más perifériába írnak (pl. LPRINT), csak a 0209H rendszerváltozót állítják át céljuknak megfelelően (printerhez 68H = 104D) mielőtt a közös végrehajtó rutinra futnának.

BASIC 39H (5/EEDA)
  Kiírja HL értékét az aktuális csatornára. (ASCII alakban). A kiírás formáját és az értéktartományt a C regiszter és az átvitelbit határozza meg:
  • NC státusz: 0-65 535 tartomány (5 jegy),
  • C státusz: 0-9999 tartomány (4 jegy),
  • a C regiszter az értékes jegyek előtti bevezető karakterek kódját adja
    (C=20H: SPACE, C=30H: "0")
BASIC 3AH (5/C4DE)
  A tokenizálás alprogramja. A kódolandó forrásszöveg hátralévő részét mozgatja, hogy a tokenizált alak beilleszthető legyen.
BASIC 3BH (5/EC83)
 
A tömbelem keresés alprogramja. Belépéskor HL a változó adatmező kezdetére mutat, C-ben a változó típuskódja van. Ha C tömbváltozót jelöl és B = 1, kiértékeli a megadott indexeket és HL-t a kijelölt elemre állítja.
BASIC 3CH (5/CE69)
 
Csatorna-argumentumot igénylő utasítások kiértékelő rutinja. A programszöveg aktuális pozíciójától kiértékeli és az A-ba tölti a #n paramétert. Hibára ugrik, ha nem ilyen következik vagy értéktartománya nem megfelelő.
BASIC 3DH (5/D068)
  A rutin a kódolt programszöveg hátralévő részének szintaktikai helyességét ellenőrzi.
BASIC 3EH (5/FB84)
  Kiírja a HL címen kezdődő füzért az aktuális csatornára (HL a karakterek számára mutat).
BASIC 3FH (5/E335)
  Kiírja a MREG-ben lévő számot az aktuális csatornára.
BASIC 40H (5/E093)
  Ellenőrzi a programszövegben következő füzér kifejezés szintaktikai helyességét.
BASIC 41H (5/DED9)
  Ellenőrzi a programszövegben következő numerikus kifejezés szintaktikai helyességét.
BASIC 80H (1/D1B2)
  A HL címen tárolt változót bevezeti a változóláncba.
BASIC 81H (1/D04D)
  Tárolja a változó fejrészét a BASIC változóterület végére (0234/5H)-től. A csonka változót be is láncolja.
BASIC 82H (1/D07C)
  A változóterületre bevezetett numerikus fejrész után beírja az adatmezőt is (nem inicializált alapértékkel).
BASIC 83H (1/D094)
  A változóterületre bevezetett füzér fejrész után lefoglalja az adatmezőt is (és nem inicializált alapértéket jegyez be).
A 84H-87H nem hívható funkciókódok (itt a belépési címtáblát másra használja a rendszer).
BASIC 88H (1/D0E7)
  Tetszőleges típusú (0-3 kódú) BASIC változót bevezet a változóterületre (nem inicializált alapértékkel).
BASIC 89H (1/D0AB)
  A program DEF és HANDLER típusú változóit bevezeti a változóterületre (hibára ugrik, ha már volt ilyen).
BASIC 8AH (1/D101)
 
A függvénykiértékelés közben talált referencia-paramétert vezeti be a változóterületre. Belépéskor HL az átadandó adatra mutat. A bejegyzett referenciaváltozót a típusbájt BIT 4 = 1 értéke jelzi.
BASIC 8BH (1/D189)
 
Megkeresi a változóterülten azt a változót, amelynek nevére (az elemzett programszövegben) a 0347/8H pointer mutat. Ha nem talál ilyen változót, bevezeti azt nem inicializált alapértékkel. Visszatéréskor HL az adatmező elejére mutat, C a változó típusa.
BASIC 8CH (1/D167)
 
Megkeresi a változó tárolási címét. Abban különbözik az előző (8BH) hívástól, hogy akkor is új elemként vezeti be a változóterületre, ha talált ugyan ilyen bejegyzést a láncban, de az belső függvény (vagy változó) volt.
BASIC 8DH (1/D117)
  A BASIC terület végén E bájtot lefoglal.
BASIC 8EH (1/D153)
 
Megkeresi a változó tárolási címét. Hasonló a BASIC 8BH-hez, de nem vezet be új elemet, ha nem találja a változót. Visszatéréskor HL a típusbájtra mutat, a státusz
  • NC, ha felhasználói változó;
  • C, ha beépített változó.
BASIC 8FH (1/D1D9)
  Inicializálja a változók bázistábláját (csak a beépített változókat és függvényeket hagyja meg a láncban).
BASIC 90H (1/C261)
  A HL-ben megadott címtől újratölti a függvények és belső változók tábláját, majd az alapján inicializálja a változók bázistábláját (0DCBH-tól)
BASIC 91H (1/C2AC)
 
A HL címen tárolt numerikus értéket a MREG-be tölti. Analóg a BASIC 14H hívással, de az BASIC változó típusú (5 számjegybájt + kitevő) adatot tölt be a verembe, míg ez 6 számjegybájttal dolgozik. Mint láttuk, a verem numerikus adatblokkja 7 számjegybájtot tartalmaz (a változók 5 bájtját elől-hátul kiegészíti egy-egy, a túlcsordulásokat felfogó bájttal). Ebből az alsó 6-ot mozgatja a BASIC 91H és a következő néhány funkcióhívás.
BASIC 92H,n (1/C28D)
 
Kétbájtos funkcióhívás: a konstanstábla (l. ROM C08CH címtől) n. elemét átviszi a MREG-be. A hívó gépi kódból még egy bájtot beolvas, ezt tekinti argumentumnak. Hívása tehát assembly programból

RST 10H
DEFB 92,n
...

alakban érhető el.

BASIC 93H, nn (1/C29F)
  Hárombájtos funkcióhívás. A hívó program következő két bájtján adható meg az a cím, ahonnan a BASIC 91H hívással be kell olvasni a numerikus adatot a MREG-be:

RST 10H
DEFB 93H,nLO,nHI
...

BASIC 94H (1/C2A4)
  Az 1. rögzített munkaregiszter (0E0BH) tartalmát tölti a MREG-be:
REG1 MREG
BASIC 95H (1/C1A9)
  A 2. rögzített munkaregiszter (0E12H) tartalmát tölti a MREG-be:
REG2 MREG
BASIC 96H,nn (1/C2C6)
  Hárombájtos funkcióhívás: a MREG tartalmát a hívó gépi kódú programban következő két bájton megadható címre tölti (a BASIC 93H,nn hívás fordítottja).
BASIC 97H (1/C2CC)
  A MREG tartamát az 1. rögzített munkaregiszterbe (0E0BH) tölti:
MREG REG1
Az átmásolt elemet törli a veremből.
BASIC 98H (1/C2D1)
  A MREG tartalmát a 2. rögzített munkaregiszterbe (0E12H) tölti:
MREG REG2
Az átmásolt elemet törli a veremből.
BASIC 99H (1/C2E4)
  Zárójeles numerikus kifejezés (függvény argumentum) kiértékelése. Az eredmény a MREG-be kerül.
BASIC 9AH (1/C2F0)
 
A programszövegben "(" jelet vár, az utána következő numerikus kifejezést kiértékeli és a MREG-be tölti. Több argumentumos függvények (pl. SPEEK (SG,CÍM)) első paraméterének kiértékelésére alkalmazható.
BASIC 9BH (1/C2ED)
  Folytatja a többtagú argumentum kiértékelését: ","-t vár, az utána következő numerikus kifejezést kiértékeli és a MREG-be tölti.
BASIC 9CH (1/C2F7)
  Zárójeles füzér kifejezés (füzérváltozós függvény argumentuma) kiértékelése. Az eredmény a MREG-be kerül.
BASIC 9DH (1/C2FF)
  "(" jelet vár, az utána következő füzér kifejezést kiértékeli és a MREG-be tölti (többtagú függvényargumentum első része).
BASIC 9EH (1/C2FC)
  ","-t vár, az utána következő füzérkijelzést kiértékeli és a MREG-be tölti.
BASIC 9FH (1/C306)
  Zárójelet ("(") és tömbváltozó nevet vár a programszövegben (egyébként hibát jelez). Kilépéskor HL a tömb adatmező kezdetére mutat.
BASIC A0H (1/C2E7)
  A programszövegben ")" jelet vár (többtagú argumentum vége).
BASIC A1H (1/C68D)
  Az aktuális programban még használható bájtok számát tölti a munkaregiszterbe (FREE változó kiértékelő rutinja).


A következő rutinok (A2H-B2H) trigonometriai és más matematikai függvények kiértékelő eljárásai. A hasonló funkciójú BASIC függvények (pl. ATN) kiértékelő rutinjai a függvényargumentum meghatározása után hívják meg ezeket az alprogramokat.

BASIC A1H (1/C399)
  Az arc tg függvény kiértékelő rutinja. Az argumentumot a MREG-ben várja és a függvényértéket (amelyet radiánban határoz meg) szintén oda tölti:
MREG = ATN(MREG)
BASIC A3H (1/C368)
 
Az arc sin függvény kiértékelő rutinja. Ellenőrzi a MREG-ben kapott argumentum értéktartományát (-1 <= x <= +1), egyébként hibajelzésre ugrik) és a radiánban meghatározott függvényértéket a MREG-be tölti
MREG = ASIN(MREG).
Az

összefüggés felhasználásával számítja ki az értéket.

BASIC A4H (1/CB86)
 
A sin függvény kiértékelő rutinja. Argumentumát - radiánban - a MREG-ben várja. Ellenőrzi az adat értékhatárait; meglehetősen misztikus módon csak az
   ABS(x) <= 180/PI
értéket fogadja el. Ezután a
   -(PI/2) - +(PI/2)
tartományba transzformálja az argumentumot (kihasználva a sin periodikusságát) és elvégzi a sorejtést. Az eredményt a MREG-be tölti: MREG = SIN(MREG)
BASIC A5H (1/C518)
  A cos függvény kiértékelő rutinja. Argumentumát a MREG-ben várja.
MREG = MREG+PI/2 transzformáció után a sin rutinra (BASIC A4H) fut.
BASIC A6H (1/CC9F)
  A tg függvény kiértékelő rutinja a

azonosság felhasználásával számítja ki a függvényértéket.

BASIC A7H (1/C5F0)
  Az e^x függvény kiértékelő rutinja. Az argumentumát a MREG-ben várja és ide tölti a függvényértéket is:
MREG = EXP(MREG)

BASIC A8H (1/CA80)

  A radiánban adott változót fokra számítja át, ha az aktuális üzemmód "fok":
MREG = 180/PI*MREG
BASIC A9H (1/C586)
  A fokban adott változót radiánra számítja át, ha az aktuális üzemmód "fok":
MREG = PI/180*MREG
BASIC AAH (1/C8A7)
  A tízes alapú logaritmus kiértékelő rutinja.
MREG = LOG10(MREG)
BASIC ABH (1/C98D)
 
A modulofüggvény kiértékelő rutinja. Az első argumentumot (az osztandót) MREG1-ben, a második argumentumot (az osztót) MREG2-ben várja. Belépéskor a BASIC veremmutató MREG2-re mutat. Kiértékelés után a függvényértéket (maradék) MREG1-be tölti és a veremmutatót is erre állítja:
MREG1 = MOD(MREG1,MREG2)
BASIC ACH (1/CC52)
 
A rutin a MREG-ben megkapott argumentum négyzetgyökét határozza meg (előjelvizsgálatot maga a rutin nem végez, tehát feltétlenül pozitív - vagy 0 - értéket kell átadnunk).
MREG = SQR(MREG)
BASIC ADH (1/CA8E)
  A rutin a MREG-ben megkapott numerikus érték reciprokát adja vissza.
MREG = 1/MREG
BASIC AEH (1/C778)
  Az INT függvény kiértékelő alprogramja.
MREG = INT(MREG)
BASIC AFH (1/CB17)
 
A ROUND függvény kiértékelésénél használt alprogram. Az első argumentumot (a kerekítendő számot) a MREG-ben várja, A-ban pedig a számjegyek kért számát (tizedespont előtt és után összesen) kell megadni: Az értékeket már nem ellenőrzi, tehát a megadott paramétereknek matematikailag feltétlenül értelmesnek kell lenniük.
BASIC B0H (1/C7B0)
  A hatványozás (xy) kiértékelő rutinja. Az alapértékét a MREG 1-ben, a kitevőt MREG2-ben várja. Belépéskor a BASIC veremmutató MREG2-re mutat.
BASIC B1H (1/C820)
  AZ IP függvény kiértékelő rutinja: a MREG-ben kapott argumentum tizedespont utáni jegyeit 0-ra állítja:
MREG = IP(MREG)
BASIC B2H (1/CBF9)
  A signumfüggvény kiértékelő rutinja. Argumentumát a MREG-ben kapja és az adat értéktartományától függően tölt +1-et, 0-t vagy -1-et a MREG-be.

A következő 4 BASIC funkcióhívás a különböző számon megnyitott BASIC programok memóriaigénylésének és nyilvántartásának alaprutinja.
BASIC B3H (1/C1D9)
 
Felszabadítja (az EXOS-on keresztül) a C. szegmenst, törli a RAM-térképből az ehhez a szegmenshez kapcsolódó elemet. Ha a szegmens előzőleg megosztott volt, törli a 020BH rendszerváltozót is.
BASIC B4H (1/C1B9)
 
Szegmenst kér a C sorszámú program számára az EXOS-tól. A kapott szegmenst a megadott programszámmal nyilvántartásba veszi a RAM-térképen. Ha megosztott szegmert kapott, a szegmens számát 020BH-be, a hozzá rendelt program számát 020DH-be is betölti.
Visszatéréskor a státusz:
  • Z, ha teljes szegmenet kapott;
  • NZ, P, ha megosztott szegmensi kapott;
  • M, ha az EXOS már nem tudott szegmensi kiutalni.
BASIC B5H (1/C1EF)
  Megkeresi a RAM-térképen (0E54H-től) a C. szegmens bejegyzését. Visszatéréskor HL a tárolt elemre mutat:
  • (HL): a szegmens száma,
  • (HL+1): a szegmensen tárolt program száma.
BASIC B6H (1/C1F9)
  Megkeresi a RAM-térképen a C. program bejegyzését. Visszatéréskor a státusz:
  • C, ha nincs bejegyezve;
  • NC, ha megtalálta. Ekkor HL a keresett elemre mutat:
         (HL): a programot tároló szegmens száma,
         (HL+1): a keresett program száma.
BASIC B7H (1/D1F3)
 
A SET utasítás végrehajtó alprogramja. Hívása előtt az értelmező csak az aktuális, csatornát állítja be. A rutin hívásakor a pointerek a programszöveg következő részére mutatnak.
BASIC B8H (1/D2AC)
  Az ASK, SET, TOGGLE után következő gépi feltételeket azonosítja. Visszatéréskor a státusz C, ha azonosította a kulcsszót.
  • A: csatornaszám a végrehajtáshoz (alapértelmezés). Pl. INK esetén A = 65H (grafikus csat.);
  • C: BIT 7,C = 0, ha a végrehajtás ESC szekvenciaként történhet (pl. PAPER);
         BIT 7,C = 1, ha a kulcsszó EXOS változót jelöl (pl. TIMER). Ekkor C alsó bitje a változó számát adja.
BASIC B9H (1/D430)
 
BASIC sorszámtartományt értékel ki az elemzett sor következő részében (100 TO 200, FIRST - stb. típus), majd meghívja a BASIC BAH funkciót, azaz listáz vagy töröl az adott tartományban.
BASIC BAH (1/D4A8)
 
Listázza az aktuális BASIC program HL (kezdősorszám) és DE (végsorszám) közötti tartományát. Az elvégzendő műveletet a háttérstátusz (AF'!) határozza meg:
  • listáz, ha C;
  • töröl, ha NC.
BASIC BBH (1/D546)
  Az AUTO parancs végrehajtó rutinja. Beolvassa a programszövegben esetleg következő paramétereket és
  • kiírja az aktuális sorszámot,
  • beolvas egy sort,
  • tokenizálja,
  • beépíti a programba.

Ezt ciklikusan ismétli mindaddig, amíg a STEP értéknek megfelelően növelt aktuális sorszám 9999 fölé nem nő vagy más módon (üres sor, hiba, STOP) ki nem lépünk az üzemmódból.

BASIC BCH (1/D5CF)
  A RENUMBER parancs végrehajtó programja.
BASIC BDH (1/DA48)
  A PRINT (és LPRINT) utasítás másodrutinja. Ellenőrzi a kulcsszó utáni programszöveg (AT x,y; #CSAT; USING stb.) szintaktikai helyességét.
BASIC BEH (1/DA9D)
 
A PRINT utasítás végrehajtó programja. Hívása előtt az értelmező beállítja az aktuális csatorna értékét. A programszöveg pointerei a token utáni elemre mutatnak.
BASIC BFH
  Belépési címe:
  • UK módban 1/DF4B,
  • BRD módban 4/EDE0.

Kiírja a HL-ban megadott hibakódhoz tartozó magyarázó szöveget a 0446H hibaüzenet-pufferbe. A 9000-9999 kódtartományban az EXOS 28 hívással a rendszertől kér értelmezést (de ezt is a fenti szegmensek adják meg, ha más bővítő nem válaszol előbb)

BASIC C0H (1/D746)
  A WAIT utasítás végrehajtó programja.
BASIC C1H (1/D773)
  Végigfut az aktuális programon és a blokk-kezdet, ill. blokkvég utasítások alapján beírja a sorok skatulyázási mélységet jelző bájtjait.
BASIC C2H (1/D7B5)
  A TIME utasítás végrehajtó programja.
BASIC C3H (1/D7C5)
  A DATE utasítás végrehajtó programja.
BASIC C4H (1/CD2F)
  A MREG-ben lévő stringet nagybetűsíti vagy kisbetűsíti:
MREG = UCASE$(MREG), ha B = 60H
MREG = LCASE$(MREG), ha B = 40H
BASIC C5H (1/D812)
  Beállítja a funkcióbillentyűk alapértelmezését (az 1. szegmens C842H címen kezdődő táblája alapján).

A felsorolást áttekintve láthatjuk, hogy a BASIC funkcióhívásokkal egy igen jól használható programkönyvtárhoz jutottunk. Egy olyan rutingyűjtemény ez, amelyben a matematikai kiértékelő rutinok (a számtani alapműveletektől a trigonometriai és egyéb függvényekig), a szintaktikai elemzést, sorfeldolgozást segítő alprogramok és praktikus BASIC segédprogramok (beolvasás, kiíratás stb.) rendkívül széles körét találhatjuk meg. Ráadásul, ami már igazán a szolgáltatások "non plus ultra"-ja: mindez egybájtos gépi kódokkal érhető el (nem számítva a funkcióhívások sorozatát bekapcsoló D7H - RST10H - és befejező 00H kódokat). Úgy használhatjuk tehát gépi kódú programjainkban akár a legbonyolultabb lebegőpontos műveletet is, mint ha egy hallatlanul megokosított processzort programoznánk. Legalábbis formailag, hiszen azzal, hogy igénybe vesszük a BASIC funkcióhívások lehetőségét, a végrehajtást tulajdonképpen az értelmezőre bízzuk, elfogadva a hívás idejére annak sebességbeli, szintaktikai és egyéb korlátait is.

Befejezésként lássunk egy-két példát a BASIC funkcióhívások alkalmazására. Természetesen a legjobb példa a felhasználásra a BASIC bővítések felépítése lesz, de szemléltetésként talán itt sem árt néhány egyszerű, BASIC-ben kialakított rutinban bemutatni a legfontosabb hívások alkalmazását.
Kezdjük az aritmetikával! A két legalapvetőbb rutin talán a konstans bevitele a MREG-be, ill. kiíratása az aktuális csatornára. Ezeket egyetlen rövid rutinban is bemutathatjuk:

RST 10
BASIC 92,n
BASIC 3F
BASIC 00
RET
; funkcióhívások bekapcsolása
; n. konstans MREG-be
; kiíratás
; funkcióhívások vége

Építsük fel a rutint BASIC-ből úgy, hogy a betöltendő konstans sorszáma helyére (kezdőcím+2) POKE utasítással sorra beírjuk a 0-42 értékeket:

1 PROGRAM "Konstans.bas"
100 ALLOCATE 100
110 CODE M=HEX$("D7,92,00,3F,00,C9")
120 FOR N=0 TO 42
130   POKE M+2,N
135   PRINT N;TAB(4);":";
140   CALL USR(M,0)
150   PRINT
160 NEXT

Érdemes áttekinteni a futás eredményét is, hiszen amellett, hogy a konstansok egy része a sorfejtések együtthatója, néhány értéket esetleg mi is felhasználhatunk rutinjainkban:

0
0.5
1
1
2
0.999999999999E62
3
32768
4
3.141592654
5
57.29577951
6
1.74532925E-02
7
1.570796327
8
1.047197551
9
0.5235987756
10
6.283185307
11
0.22367
12
0.894427
13
0.316227766
14
-0.1666666661
15
8.333330721E-03
16
-1.984083282E-4
17
2.752397107E-06
18
-2.386834641E-08
19
-1.440083449
20
-0.7200268489
21
4.320250389
22
4.752225846
23
0.2679491924
24
1.732050808
25
0.7320508076
26
-2.915681438
27
3.163034916
28
-0.6735816015
29
-10.07040695
30
16.9669814
31
-8.190800455
32
0.8685889638
33
2.302585093
34
3.321928095
35
0.8685889638
36
1.151
37
2.92546497E-04
38
504.4648895
39
14.00829976
40
3.328736465E-02
41
1008.929779
42
112.0940811

Néhány alapkonstans tehát:

(Megjegyezzük, hogy a gép a konstansokat 6 bájton - 12 számjeggyel - tárolja, de a kiíró rutin ezt automatikusan 10 jegyre kerekíti!)
Ezek után végezzük el az alapműveleteket pl. az első két konstans között:

a, 1+05: MA

RST 10
BASIC 92,01
BASIC 92,00
BASIC 0C
BASIC 3F
BASIC 00
RET

; 1 -> MREG1
; 0,5 -> MREG2
; MREG1 = MREG1+MREG2
; kiíratás

b, 1-05: MB

RST 10
BASIC 92,01
BASIC 92,00
BASIC 0D
BASIC 3F
BASIC 00
RET

; 1 -> MREG1
; 0,5 -> MREG2
; MREG1 = MREG1-MREG2
; kiíratás

Ezek analógiájára már könnyen elkészíthető a

C, 1*0,5
d, 1/0,5

számítás rutinja is. A BASIC betöltő, és a futtatás eredménye:

1 PROGRAM "Alapmuv.bas"
100 ALLOCATE 100
110 CODE MA=HEX$("D7,92,01,92,00,0C,3F,00,C9")
120 CODE MB=HEX$("D7,92,01,92,00,0D,3F,00,C9")
130 CODE MC=HEX$("D7,92,01,92,00,0E,3F,00,C9")
140 CODE MD=HEX$("D7,92,01,92,00,0F,3F,00,C9")
150 CALL USR(MA,0):PRINT
160 CALL USR(MB,0):PRINT
170 CALL USR(MC,0):PRINT
180 CALL USR(MD,0):PRINT

Így persze nemigen lehet megkülönböztetni a kivonás és a szorzás hatását, de van elég konstans a további kísérletezéshez.
Hogy egy elemi függvényt is használjunk, határozzuk meg fokokban, hogy melyik az a szög, amelynek szinusza 0,5:

RST 10
BASIC 92,00
BASIC A3
BASIC 92,05
BASIC 0E
BASIC 3F
BASIC 00
RET

; 0,5 -> MREG
; MREG = ASIN(MREG)
; MREG2 = 180/PI
; MREG = 180/PI * ASIN(0,5)

A programot betöltve és futtatva meggyőződhetünk a számítás helyességéről:

100 ALLOCATE 100
110 CODE M=HEX$("D7,92,00,A3,92,05,0E,3F,00,C9")
120 CALL USR(M,0):PRINT

Minőségileg újat jelent, ha túllépünk a belső konstansok körén és a BASIC szövegből adunk át paramétert rutinunknak. Ehhez egyelőre használjuk fel továbbra is a CALL USR hívást, és a feldolgozni kívánt programszöveget írjuk egyszerűen az utasítás után. Ez a nem túl elegáns, de jól működő megoldás azon alapul, hogy az értelmező elvégzi a CALL USR(CIM,HL) utasítás elemzését és a következő elem vizsgálata után lép be a felhasználói gépi rutinba. Az aktuális programszöveg pointerei az utasítást követő elemre mutatnak. Ezt használtuk ki a változóterület bemutatásánál - még magyarázat nélkül használt - V-CIM rutinban, amely a

CALL USR (V_CIM,0) A

alakban az utasítás után írt változó tárolási címét kereste meg. Most már jól követhető a rutin működése:

RST 10
BASIC 8E
BASIC 00
PUSH HL
RST 10
BASIC 20
BASIC 00
POP HL
RET
; funkcióhívás indul
; HL = a változó tárolási címe
; hívás vége
; cím verembe
; funkcióhívás indul
; átlépi az elemet
; hívás vége
; cím vissza

Egy másik alkalmazásként írjunk egy köbszámító rutint:

RST 10
BASIC 23
BASIC 15
BASIC 15
BASIC 0E
BASIC 0E
BASIC 3F
BASIC 00
RET
; funkcióhívás indul
; következő szakasz kiértékelése
; MREG2 = MREG1 = X
; MREG3 = MREG2 = X
; MREG2 = MREG2*MREG3 = X^2
; MREG1 = MREG1*MREG2 = X^3
; kiíratás
; hívássorozat vége

A használatra a betöltőprogram ad példát:

100 ALLOCATE 100
110 CODE M=HEX$("D7,23,15,15,0E,0E,3F,00,C9")
120 INPUT PROMPT "A szam: ":SZAM
130 PRINT "A szam kobe:";
140 CALL USR(M,0) SZAM
150 PRINT
160 GOTO 120

Befejezésül próbáljuk ki a listázó funkcióhívást is:

LD HL,0002
LD DE,0004
OR A
EX AF,AF'
RST 10
BASIC BA
BASIC 00
RET
; kezdő sorszám (2)
; végsorszám (4)
; CY=1 (LIST)
; háttérbe
; funkcióhívás indul
; LIST 2-4
; hívás vége

Mindez a következőképpen működik a BASIC programban:

1 PROGRAM "Listaz.bas"
2 ALLOCATE 100
3 CODE M=HEX$("21,02,00,11,04,00,37,08,D7,BA,00,C9")
4 CALL USR(M,0)
5 END

3.4 A BASIC értelmező működése

Az értelmezőprogram, miután az inicializálás folyamán kiépítette saját hátterét (rendszerváltozók, csatornák stb.) gyakorlatilag egy végtelen ciklusban pörög:

Ez a legtöbb programnál előbb-utóbb végetér, és akkor az értelmező újra kezdi a ciklust a billentyűk figyelésével.
Az értelmező működése tehát három fő ágban:

Az ezekben elvégzett funkciókhoz, a használt rendszerváltozókhoz, pointerekhez nekünk is kapcsolódnunk kell, ha a gépi kódú programok írása során bővíteni (vagy akár csak használni) szeretnénk a BASIC rendszer szolgáltatásait. Ezért tekintsük át röviden (alkalmanként azért belenézve a működésbe) a három főágat.

3.4.1 Beolvasási főág

Az értelmező az inicializálás után közvetlenül a beolvasási főágba ér. Első teendője a BASIC által külön területen fenntartott Z80-as verem beállítása, majd az állapotsor üzenetszövegének kiíratása:

C2EC
C2EF
C2F2
C2F4
C2F5
C2F7
C2F9

C2FC
C2FF
C302
C303
C306

C309
C30A

C31E
C31F
C322
C324
C325
C328

C32B
C32C

C333
C334
31 BB 0D
CD D1 C3
DB B2
F5
3E FF
D3 B2
32 09 02

2A F6 BF
11 06 00
19
22 42 02
CD 00 CE

14
...

EB
DD 6E 0A
0E 20
37
CD DA EE
CD 00 CE

07
...

F1
D3 B2
LD SP,0DBB
CALL C3D1
IN A,(B2)
PUSH AF
LD A,FF
OUT (B2),A
LD (0209),A

LD HL,(BFF6)
LD DE,0006
ADD HL,DE
LD (0242),HL
CALL CE00

DEFB 14
DEFB "IS-BASIC     program"

EX DE,HL
LD L,(IX+0A)
LD C,20
SCF
CALL EEDA
CALL CE00

DEFB 07
DEFB "       "

POP AF
OUT (B2),A
; Z80 verem a BASIC-ben
; flag-állítás
; 2. LAP aktuális szg.
; verembe
; rendszerszegmens
; a 2. LAP-ra
; aktuális csatorna
;     =255 (memóriába ír)
; az állapotsor címe
; az első 6 karaktert
;     nem írja át
; puffercímként beír
; köv szöveg kiírása:

; szöveghossz


; puffercím DE-be
; L = aktuális prg. száma


; kiírja L értékét
; utána 7 szóközt ír:

; szöveghossz


; eredeti 2. LAP szg.
; visszalapozza

A végrehajtások, megindítás, hibakezelés után is mindig ide (pontosabban a C2F2H címre) lép a rendszer, valahányszor a beolvasási főág visszakapja a vezérlést.
Számunkra is hasznos lehet az itt is többször alkalmazott 5/CE00H rutin. Ez az aktuális csatornára (ez most éppen az állapotsor memóriaterülete) kiírja azt a szöveget, amely a hívó utasítást (CALL CE00H) követi (első a hosszbájt, utána a karakterek), majd a szöveget átlépve tér vissza a hívó rutinba.

Az értelmező többi üzenetváltása már az EDITOR csatornán (alapértelmezésben #0) történik. A továbbiakban tehát ezt állítja be az értelmező aktuális csatornaként, ellenőrzi és ide írja ki az "ok" üzenetet, ha megengedett (pl. programsor beírás után nem ír "ok"-t). A különböző üzemmódok jelzésére a 0200H rendszerváltozó, flag-bájt szolgál (mint láttuk, az IX regiszter a BASIC-ben mindig ezt a változót címzi meg).
Ezután már következhet a beolvasás. Előtte még beállítja a program a BASIC-ben szokásos EDITOR jelzőket, majd a sorbeolvasó rutinjára lép:

C336
C337
C33A
C33E
C342
C344
C347

C34A
C34B
C34D
C34E
C34F

C350
C354
C357
C359
C35C
C35E
A
32 09 02
DD CB 00 8E
DD CB 00 46
28 0C
CD D0 F1
CD 00 CE

05
6F
19
0D
0A

DD CB 00 C6
CD D0 F1
16 14
01 20 01
F7 10
CD D6 C3
XOR A
LD (209),A
RES 1,(IX)
BIT 0,(IX)
JR Z,350
CALL F1D0
CALL CE00

DEFB 05
DEFB "ok"
DEFB 19
DEFB 0D
DEFB 0A

SET 0,(IX)
CALL F1D0
LD D,14
LD BC,0120
EXOS 10
CALL C3D6
; A=0
; akt. csatorna (EDITOR)
; parancsmód
; "ok" írható?
; átlépi, ha nem
; EDITOR ellenőrzése
; "ok" kiíratás

; szöveghossz

; sorvégig töröl
; CR (kocsivissza)
; LF (soremelés)

; "ok" írható
; EDITOR ellenőrzés
; NO_SOFT, AUTO_ERA
; FLAG_EDIT írása
; jelzők az EDITOR-ba
; sor beolvasás

Itt álljunk meg egy pillanatra. A beolvasási főág leglényegesebb momentumához érkeztünk: az értelmező most vár a felhasználó utasítására, ami a további teendőket dönti el. A kurzor villog, a képernyőn sorra megjelennek a leütött karakterek. Érdekes módon a vezérlés ilyenkor nem is a BASIC-é: az operációs rendszer EDITOR perifériakezelője gondoskodik a gép és kezelője kapcsolatáról, a szerkesztési lehetőségről. A vezérlés akkor kerül vissza a BASIC-hez, amikor az utasítás, sor befejezésekor - lenyomjuk az ENTER billentyűt (esetleg a STOP-ot).
A beolvasó rutin először betölti az EDITOR-tól kapott karaktereket a szövegpufferbe (0248H: hossz, 0249H-tól a karakterek), majd bizonyos mértékig fel is dolgozza a puffer tartalmát:

C3D6
C3D7
C3DA
C3DB
C3DE
C3E1
C3E2
C3E5
C3E8
C3EB

C3ED
C3F0
C3F2
C3F4
C3F5
37
CD B6 FB
D8
21 48 02
22 16 02
23
22 14 02
CD A0 C5
CD 4B C7
30 E9

3A 04 02
FE 02
38 E2
B7
C9
SCF
CALL FBB6
RET C
LD HL,0248
LD (0216),HL
INC HL
LD (0214),HL
CALL C5A0
CALL C74B
JR NC,C3D6

LD A,(0204)
CP 02
JR C,C3D6
OR A
RET

; egy sor a pufferbe
; vissza, ha STOP volt
; puffer kezdőcím
; pointerbe
; 1. karakter címe
; pointerbe
; 1. elem vizsgálata
; beépíti, ha prc.-sor
; újraolvasás, ha nem volt
;     feldolgozandó sor
; 1. elem típusa
; sor vége?
; újraolvasás
; NC státusz

A sorfeldolgozással, a programszöveg elemeinek vizsgálatával, kódolásával (a következő pontban) külön is foglalkozunk, hiszen ez eredményezi a sokszor említett tokenizált szöveget. Most annyit figyeljünk meg, hogy a beolvasó rutinból ki sem lép az értelmező, ha csak egy üres sort írtunk be. Ha a beírt sor számmal kezdődik, azt - mint programsort - tokenizálás és szintaktikai elemzés után beépíti az értelmező az aktuális BASIC programba.
A beolvasási főágra csak akkor kerül vissza a vezérlés, ha a beírt sor egy feldolgozandó, majd végrehajtandó parancs (ill. ha STOP-ot nyomtunk meg).
Befejezésül az értelmező - felhasználva, hogy a beolvasó rutin már megvizsgálta a sor első elemének típusát - még különválaszt egy esetet: azonnal átadja a szöveget (egy EXOS hívással) a rendszerbővítőknek, ha az kettősponttal kezdődik (pl. :HELP vagy :BRD stb):

C361
C363
C365
C367
C369
38 ED
FE 0A
28 F7
FE 10
20 11
JR C,C350
CP 0A
JR Z,C35E
CP 10
JR NZ,C37C
; vissza, ha STOP volt
; az 1. elem "*"?
; újraolvasás, ha igen
; ":"?
végrehajtásra, ha nem
     ; Kettősponttal kezdődő sor (például :HELP) esetén:
C36B
C36E
C372
C373
C375
C376
C378
C379
3A 48 02
ED 5B 14 02
1B
D6 03
12
F7 1A
DF
C3 F2 C2
LD A,(0248)
LD DE,(0214)
DEC DE
SUB 03
LD (DE),A
EXOS 1A
RST 18
JP C2F2
; A = a sor hossza
; DE = 1. karakter címe
; elé
; hossz-3
; szöveg hossza
; bővítőkhöz
; EXOS hibakezelés
; főág elejére ugrik

A pufferben tehát most egy BASIC parancs vagy többutasításos sor van. Mielőtt követnénk az értelmezőt a végrehajtás folyamán, foglaljuk össze, hogy miként is elemez egy sort az értelmező, mit is jelentenek a különböző típusú elemek, hogyan néz ki végül egy tokenizált sor.

3.4.2 Sorelemzés, tokenizálás

Mindenekelőtt nézzünk meg néhány utasítást és próbáljuk elemekre bontani őket. Állapítsuk meg, hogy milyen elemeket lehet megkülönböztetni a szövegben.

PRINT 12
Az utasítás két eleme:

LET SZAM=SIN(PI)+0,5
Itt már több elemet láthatunk:

LET A$="NEV"

GOTO 100

Ezekben a példákban az értelmező valamennyi megkülönböztetett elemtípusát láthattuk, és egyben a programszöveg szeletelési módját is: az értelmezőprogram ugyanilyen elemekben vizsgálja, kódolja, majd hajtja végre az utasításokat.
Mit is jelent a már sokszor emlegetett kódolás, tokenizálás? Nos, az értelmező nem eredeti formájában tárolja pl. a programban a beírt sorokat. Saját munkáját megkönnyítendő, előre bejegyzi a sorokba a különböző feldolgozást, végrehajtást igénylő elemek típuskódját, hosszát, konstansok esetén a bináris vagy BCD értéket stb. Ez persze valamelyest meghosszabbítja a sorok bevitelét, de ez a veszteség sokszorosan megtérül a futásidőben.
Hány elemtípust kell végül is megkülönböztetni? Mint láthattuk, az utasítások elején mindig egy BASIC kulcsszó áll (kivétel ez alól a megengedett "A=..." típusok, amelynél nyilván az értelmezőnek kell egy LET kulcsszót az értékadás elé képzelnie). Lehetnek a szövegben még konstansok (numerikusak vagy szövegesek), formailag egy csoportba sorolható felhasználói- és belső változók vagy függvények és különböző jelek. Az IS-BASIC által használt típuskódok a következők:

Típuskód
Elem
00H
jel
20H
numerikus változó vagy függvény
40H
füzérváltozó vagy függvény
60H
BASIC kulcsszó
80H
füzérkonstans
A0H
BASIC sorszám (10000 alatti egész)
C0H
numerikus konstans

Az értelmező tehát kódoláskor nem különbözteti meg a változókat és függvényeket, de még a felhasználói vagy belső változatokat sem. Így könnyű a tokenizálás (a betűvel kezdődő karaktersorozatok automatikusan ebbe a csoportba kerülnek; még a kulcsszavak is az elemzés legelső stádiumában) és a kiértékeléskor válik majd szét a különböző típusok kezelése (a típuskódok alapján).
Látható, hogy a típuskódokban csak a felső 3 bit használt. Az elemekhez ragasztott típusjelző alsó 5 bitje tehát még további információkat hordozhat. Egyes típusoknál ki is használja ezt a rendszer (pl. a változónév hosszának beírására; ez maximálja a változók nevét 2^5-1 = 31 karakterben). A feldolgozás során a típuskód AND E0H maszkolással, a kiegészítő információ AND 1FH maszkolással emelhető ki.
Tekintsük át ezek után, hogy az egyes elemtípusokat milyen formában kódolja az értelmező.

1. Jel (00H típuskód)

Elem hossza: 1 bájt

Kód
Jel
 
Kód
Jel
 
Kód
Jel
HEX
DEC
 
HEX
DEC
   
HEX
DEC
01
1
!
 
0A
10
*
 
13
19
=
02
2
"
 
0B
11
+
 
14
20
>
03
3
#
 
0C
12
,
 
15
21
<>
04
4
$
 
0D
13
-
 
16
22
<=
05
5
%
 
0E
14
.
 
17
23
>=
06
6
&
 
0F
15
/
 
18
24
[
07
7
'
 
10
16
:
 
19
25
\
08
8
(
 
11
18
:
 
1A
26
]
09
9
)
 
12
18
<
 
1B
27
^

2. Numerikus változó vagy függvény (20H típuskód)

Elem hossza: 1 + a név betűinek száma (n):

3. Füzérváltozó vagy függvény (40H típuskód)

Elem hossza: 1 + a név betűinek száma (beleértve a "$" karaktert is):

4. BASIC kulcsszó (60H típuskód)

Elem hossza: 2 bájt:

5. Füzérkonstans (80H típuskód)

Elem hossza: 2 + a füzér karaktereinek száma:

6. BASIC sorszám (A0H típuskód)

Elem hossza: 3 bájt:

7. Numerikus konstans (C0H típuskód)

Elem hossza 1+2 vagy 1+5 bájt:

Felmerülhet a kérdés, hogy mi alapján különbözteti meg az értelmező az A0H és C0H típusú konstansokat a szövegben, mi a helyzet a REM-sorok kódolásával stb. Hogy ezekre, és hasonló kérdésekre válaszolni tudjunk, tekintsük át röviden egy programsor elemzésének folyamatát: azokat a lépéseket, amelyek az ENTER megnyomásától a tokenizált sor memóriába írásáig történnek. Azt már tudjuk, hogy a beírt sor karakterei a 0248H pufferbe kerülnek. A vizsgálatkor, elemzéskor néhány pointer jelzi az aktuális pozíciót (ezek majd a végrehajtás közben is hasonlóan funkcionálnak):

Ezek után nézzük a lépéseket (megadva a fontosabb rutinok címeit is, ezzel is segítve a ROM-ot elemezgető olvasó tájékozódását):

Ezzel befejeződött a sor elemzése, tokenizálása. Így írja be az értelmező a programba az új sort (kiegészítve a bevezető hossz, sorszám, skatulya és lezáró 0 bájtokkal; l. 3.2.7 pont).
Ezek után már jól követhető, hogyan tokenizálódnak a fejezet elején példaként felírt utasítások. Hexadecimális kódokkal:

PRINT 12

A többi példa esetén (most már minimális kommentárral):

LET SZAM=SIN(PI)+0,5

60
28
24
53
5A
41
4D
13
23
53
49
4E
   
LET
4:
S
Z
A
M
=
3:
S
I
N
   
                           
08
22
50
49
09
0B
C6
00
00
00
00
50
63
00
(
2:
P
I
)
+
6:
5.000000000
E-1
 

LET A$="NEV"

60
28
42
41
24
13
80
03
4E
45
56
00
LET
2:
A
$
=
 
3:
N
E
V
 

GOTO 100

60
21
A2
64
00
00
GOTO
2:
LO
HI
 

3.4.3 Végrehajtás

A sorkódolást bemutató rövid kitérő után térjünk vissza a beolvasási főághoz; ahhoz a ponthoz, ahol kiderült: a beírt és pufferbe töltött sor első eleme nem sorszám, tehát a sor parancsmódban végrehajtandó. Ebben a fázisban a 0249H pufferbe töltött szöveg még nincs tokenizálva, csak az első elem típusát vizsgálta meg az értelmező.
A parancsmódú végrehajtásnak is a kulcsszó felismeréssel kell kezdődnie, a következő lépésben pedig a sor hátralévő részét (a kulcsszótól függően) tokenizálni kell, hiszen a végrehajtó rutinok csak a kódolt paramétereket ismerik fel:

C37C

C37F

C381
C383
C386
C38A
C38B
C38C
C38D
C38F
C392
CD 48 EF

28 32

CB 46
CA 4C F5
ED 5B 14 02
D5
E5
C5
CB 76
C4 1D C4
C1
CALL EF48

JR Z,C3B8

BIT 0,(HL)
JP Z,F54C
LD DE,(0214)
PUSH DE
PUSH HL
PUSH BC
BIT 6,(HL)
CALL NZ,C41D
POP BC
; kulcsszó azonosítás
;     C: token (HL): típus
; LET-re, ha nem kulcsszó
;     az első elem
; parancsmódban jó?
; hibára, ha nem
; aktuális elem címe
; regiszterek
;     verembe

; kell tokenizálni?
; kódol, ha kell
; token vissza

Ezután az értelmező ugyanúgy meghívja az azonosított kulcsszó 2. rutinját, mint ahogy a programsor tokenizálásánál láttunk. A kulcsszó szintaktikájának megfelelő formai ellenőrzés, ill. az esetleges áttokenizálás után (pl. a RUN 2. rutinja írja át a kulcsszót követő sorszám típusbájtját A2H-re) kezdődhet a végrehajtás. A rutin szükség esetén (pl. START, DELETE stb.) törli a változókat, 1-99. csatornákat, majd a sor következő elemének vizsgálata (BASIC 20H) után meghívja az azonosított kulcsszó 1. rutinját a végrehajláshoz:

C393
C394
C397
C398
C399
C39B
C39E
C39F
C3A2
C3A5
C3A9
C5
CD E0 EF
C1
E1
CB 6E
C4 4D C5
E1
22 14 02
CD 59 F2
DD 36 09 00
CD E7 EF
PUSH BC
CALL EFE0
POP BC
POP HL
BIT 5,(HL)
CALL NZ,C54D
POP HL
LD (0214),HL
CALL F259
LD (IX-09),00
CALL EFE7
; token verembe
; 2. rutin hívása
; token vissza
; típusbájt címe
; kell CLEAR?
; töröl, ha kell
; aktuális elem címe
; vissza a pointerbe
; következő elem vizsg.
; aktuális csatorna #0
; 1. rutin végrehajtás

Az utasítások végrehajtó rutinjait természetesen itt is a kulcsszótábla és a token alapján keresi meg az értelmező. A tábla manipulálása (megváltoztatása, bővítése) tehát hatással lesz a leírt végrehajtási folyamatra.
A parancsmódú végrehajtás befejezéseként az értelmező ellenőrzi a végrehajtó rutinok által beállított 0206H rendszerváltozót: a folytatás módjának jelzőbájtját. Ennek a programfutás során van jelentősége, parancsmódban a gép gyakorlatilag nem kezel többutasításos sorokat. Utolsó lépésként egy jelzőbitet kérdez le a rendszer:

C3AC
C3AF
C3B1

C3C3
C3C6
C3CA

C3CD
C3D1
C3D5
3A 06 02
FE 02
18 10

DC 51 F2
DD CB 00 6E
C2 F2 C2

DD CB 00 CE
DD CB 00 EE
C9
LD A,(0206)
CP 02
JR C3C3

CALL C,F251
BIT (5,(IX)
JP NZ,C2F2

SET 1,(IX)
SET 5,(IX)
RET
; folytatás-kód?
; 0 vagy 1?


; sor véget vár
; flag törlődött?
; vissza a beolvasási
;     főágra, ha nem
; programmód
; flag alap
; veremcímre ugrik

A vizsgált bit általában 1, így a végrehajtás befejezésével a vezérlés ismét a beolvasási főágra kerül. Ha azonban egy kulcsszó végrehajtó rutinja törli ezt a bitet, lehetséges a főág szubrutinként való hívása vagy a parancsmódban beírt kulcsszó végrehajtó rutinjából a verem tetején lévő címre ugrás (CONTINUE).

Ezzel áttekintettük a parancsmódú végrehajtás lépéseit. Valójában a számítógép legfontosabb tulajdonsága a programfuttatás lehetősége. A programmódú végrehajtás menete némileg különbözik az eddig látottaktól.
Az alapot itt a már tokenizált, formailag ellenőrzött programszöveg adja. Nem lesz tehát szükség a 2. rutinok hívására. A másik lényeges különbség, hogy itt az utasítások, sorok szigorú tárolási szabályok szerint egymás után következő sorozatát kell feldolgozni. Az értelmezőnek csak ezen a láncon kell végighaladnia, sorra hívva a talált tokenekhez-tartozó végrehajtó rutinokat mindaddig, amíg a lánc végére nem ér (vagy az egyik rutin meg nem szakítja a végrehajtást). A ciklikus hívássorozatot végző rutin (CD68H, ide lép pl. a RUN végrehajtás is az esetleges paraméterek kiértékelése után):

Ha addig egy utasítás (STOP, END) ki nem váltja a megszakítást, a fenti ciklikus végrehajtás a program végén szakad meg - automatikusan (ha a következő sor elején 0 sorhosszúságot talál az értelmező). Ekkor a vezérlés néhány lépés után visszakerül a beolvasási főágba és újra kezdődhet a megismert működési folyamat.

3.4.4 Utasítások, függvények

Az előbbiekben végigkövettük, hogyan jut el az értelmező a kulcsszavakhoz tartozó végrehajtó rutinok meghívásáig. Nézzük meg most néhány, röviden leírható, konkrét esetben, hogy milyenek is ezek a rutinok.
Első példánk mintegy bemelegítésként egy olyan egyszerű utasítás legyen, amely semmilyen paramétert nem vár szövegkörnyezetétől és nem is ad vissza értéket. A PING utasítás egyetlen funkciója egy 7-es kód (ASCII BELL kód) írása a hangcsatornára:

D3BF
D3C1
D3C3
C3C5
3E 67
06 07
F7 07
C9
LD A,67
LD B,07
EXOS 07
RET
; SOUND csatorna
; írandó kód
; kiírás

Ez a rutin egyedülállóan egyszerű. A legtöbb utasítás paramétereket vár a működéséhez (vagy ad vissza). Ennek illusztrációjaként kövessük a POKE CIM, ADAT utasítás végrehajtását. Az egyébként igen egyszerű funkciót mindössze az bonyolítja kissé, hogy az értelmező az elfogadott paramétertartományban egységesen fölfelé kerekít:

D3C6

C3C9
C3CA

D3CB
D3CE
D3D1
D3D2
D3D5
D3D8
D3DB
D3DC
D3DD
D3DF
D3E2

E849
E84A
E84B

E84E
E84F
E851
E852

E855
E856
E857
CD CB D3

77
C9

CD E2 DE
21 00 80
E5
CD EF E8
CD F8 E4
CD F9 E7
D1
19
3E 0C
CD 53 F2
C3 49 E8

37
E5
CD FB E7

24
28 04
25
C4 BB F4

7D
E1
C9
CALL D3CB

LD (HL),A
RET

CALL DEE2
LD HL,8000
PUSH HL
CALL E8EF
CALL E4F8
CALL E7F9
POP DE
ADD HL,DE
LD A,0C
CALL F253
JP E849

SCF
PUSH HL
CALL E7FB

INC H
JR Z,E8E5
DEC H
CALL NZ,F4BB

LD A,L
POP HL
RET
; paraméter kiértékelés
;    HL=cím, A=adat
; végrehajtás


; BASIC 23H: CÍM -> MREG1
; 32768
; verembe
; BASIC 02H: HL -> MREG2
; BASIC 0DH: CÍM-32768
; BASIC 0BH: MREG -> HL
; DE=32768
; CÍM 0 és 65535 közé
; "," követi?
; ha nem, hiba
; 2. paraméter kiértékelésére
;     ugrik

; CÍM verembe
; BASIC 0BH: 2. paraméter
;     ADAT -> MREG -> HL
; H=255?

; H=0?
; hiba, ha az ADAT > 255
;     vagy ADAT < -255
; A: ADAT
; HL: CÍM
; vissza a POKE-hoz

A programszövegben következő paraméterek kiértékelése, a megkívánt szintaxis (",") ellenőrzése tehát a végrehajtó rutin feladata. A meghívott aritmetikai rutin (BASIC 23H) viszont már önállóan kiértékeli az aritmetikai egységet, beleértve a függvényhívásokat is.

A BASIC egyik alapfunkciója az értékadás. Nézzük meg ezt a LET végrehajtásában. Mivel a többszörös értékadás (pl. LET A,B = 1) kissé komplikálja a futást, talán nem árt összefoglalni az értékadás vázát:

Itt a többszörös értékadáshoz az értelmező mindaddig tárolja a Z80-as verembe (PUSH utasítással) az újabb és újabb változók tárolási címeit, amíg a sor végére (az "=" jelhez) nem ér. A beírandó kifejezés kiértékelése után az értéket sorra áttölti a veremből visszaolvasott tárolási címekre (itt a sor végét a PUSH utasítással először tárolt 0000 "előbukkanása" jelzi). Kövessük a végrehajtást pl. numerikus értékadás esetén:

D280
D283
D284
D285
D288
D28B
D28C
D28E
D28F

D292
D293
D295
D297
D299
D29C
D29D
D2A0
D2A1
D2A2
D2A5
D2A7

D2A9
D2AB
A2AE
D2AF
D2B1
D2B3
D2B6
D2B7
D2B8
D2B9

D2BC

D2BF
11 00 00
D5
15
CC 59 F2
3A 02 02
BA
28 0E
14
C2 1C F5

57
FE 20
28 05
FE 40
C2 1C F5
D5
CD 7B EC
D1
E5
3A 04 02
FE 0C
28 DC

3E 13
CD 53 F2
7A
FE 20
20 0E
CD E2 DE
E1
7C
B5
CA E8 ED

CD 0E E9

18 F5
LD DE,0000
PUSH DE
DEC D
CALL Z,F259
LD A,(0202)
CP D
JR Z,D29C
INC D
JP NZ,F51C

LD D,A
CP 20
JR Z,D29C
CP 40
JP NZ,F51C
PUSH DE
CALL EC7B
POP DE
PUSH HL
LD A,(0204)
CP 0C
JR Z,D285

LD A,13
CALL F253
LD A,D
CP 20
JR NZ,D2C1
CALL DEE2
POP HL
LD A,H
OR L
JP Z,EDE8

CALL E90E

JR D2B6
; sorozatvég jelző
; verembe
; D=FFH (első elem)
; BASUC 20H: típus?
; elemtípus
; = előző érték típusa?
; tovább, ha igen
; első változó?
; hiba, ha változott
;     a típus
; D=elemtípus
; numerikus változó?
; tovább, ha igen
; füzérváltozó?
; hiba, ha egyik sem
; típusjelző verembe
; változó tárolási címe

; tárolási cím verembe
; a következő elem
; ","? (újabb változó)
; ciklus elejére, ha
;     újabb változó köv.
; "=" követi?
; hiba, ha nem
; típusjelző
; numerikus?
; ha nem, füzérre
; BASIC 23H: kiértékelés
; HL=tárolási cím

; HL=0? (sorozat vége)
; veremrendezésre, ha
;     nincs több változó
; BASIC 1BH:
;     érték a változóba
; ciklus elejére

Figyelemre méltó, hogy míg a belépéskor az elem típusvizsgálata után került a vezérlés a rutinra, a további elemek (változónevek) típusvizsgálatát már a végrehajtó rutinnak kell kérnie (BASIC 20H). A BASIC 1BH hívás az értéknek a változó adatterületére való áttöltése után törölné is az elemet a BASIC veremből. Ezért alkalmazza itt az értelmező a köztes belépést (E90EH), így az érték a sorozat végéig a munkaregiszterben marad.

A BASIC utasítások másik csoportja a végrehajtási sorrendet módosítja. Ezt az IS-BASIC-ben igen egyszerűen meg lehet tenni. A feladat lényegében az elemzési pozíciót meghatározó pointer beállítása a következő végrehajtandó sorra. Jó példa erre a GOSUB végrehajtó rutinja.
Itt egyben a BASIC verem használatára is látunk egy példát. A rutin a GOSUB sort követő BASIC sor címét írja a BASIC verembe 6-os típuskóddal, a célcímet pedig a (0216H) pointerbe írja. A tényleges vezérlésátadás már a programvégrehajtási főág feladata:

D1B5
D1B8
D1BB

D1BE
D1C1
D1C2
D1C5
D1C6
D1C8
D1C9
D1CA
D1CD

D1D0
D1D2
D1D3
D1D6

D1DA
11 04 00
CD 97 F0
CD FA C3

DC 25 F5
E5
2A 16 02
4E
06 00
09
EB
2A 28 02
CD 06 DD

36 06
E1
22 16 02
DD 36 06 03

C9
LD DE,0004
CALL F097
CALL C3FA

CALL C,F525
PUSH HL
LD HL,(0216)
LD C,(HL)
LD B,0
ADD HL,BC
EX DE,HL
LD HL,(0228)
CALL DD06

LD (HL),06
POP HL
LD (0216),HL
LD (IX+06),03

RET
; blokkméret+1
; van még hely?
; BASIC 2EH: sorszám
;     keresés a programban
; hiba, ha nincs ilyen
; sor címe verembe
; GOSUB sor címe
; GOSUB sor hossza
; BC=sorhossz
; HL: a következő sor címe
; DE = -""-
; BASIC verem címe
; elé írja DE-t
;     (visszatérési cím)
; elé GOSUB típusbájt
; célcím
; pointerbe
; folytatás kódja:
;     a (0216) soron folytat

Hogy zárjuk a kört, nézzük meg az utasítás párját is. A RETURN rutinjának fordított feladata van: a BASIC veremből kiolvasott visszatérési címet kell hasonló módon pointerbe töltenie. A probléma mindössze az, hogy - mivel a RETURN is a végrehajtási sorrendet változtatja meg - előfordulhat, hogy egy megkezdett blokkból (pl. FOR-NEXT ciklusból) lépünk ki. Ezért a rutin visszaugrás előtt rendezi a BASIC vermet: törli a GOSUB blokk elé azóta beírt, nála kisebb típuskódú (1-5) elemeket. Így áll végül is a BASIC veremmutató (0228H) a GOSUB blokkra. Nem lehet viszont kilépni DEF rutinból vagy WHEN blokkból.

D82B
D82E
D831
D833
D835
D838

D83B
CD 51 F2
CD F3 ED
3B FB
FE 06
C2 14 F5
CD ED DC

C3 D6 D1
CALL F251
CALL EDF3
JR C,D82E
CP 06
JP NZ,F514
CALL DEED

JP D1D6
; sorvéget vár
; BASIC verem rendezés
; ciklikusan
; GOSUB blokk?
; hiba, ha nem
; blokk visszaolvasás
;     JL: visszatérési cím
; pointerbe, folytatás
;     a GOSUB soron

Az értékadó és vezérlésátadó utasítástípusok után praktikus lenne a kiíratással is foglalkozni. A PRINT utasítás elemzése azonban meglehetősen helyigényes és nehezen áttekinthető lenne, nem annyira az alapfunkció, mint inkább az IS-BASIC által megengedett sokféle paraméter, kiegészítés miatt (pl. AT, TAB, USING, vessző, pontosvessző stb.). A gépi kódú programjainkban is gyakran igényelt alapfeladat (numerikus érték vagy füzér kiíratása az aktuális csatornára) a BASIC funkcióhívások segítségével általában egyszerűen megoldható. Numerikus kifejezésre:

BASIC 23H - kiértékelés,
BASIC 3FH - kiíratás

míg füzér kifejezés esetén:

BASIC 24H - a kiértékelésre és
BASIC 3EH - a kiíratásra.

A BASIC veremtől, programszövegtől független konkrét kiíratási feladatokat is több hívás segíti (pl. BASIC 38H, 39H).

A kifejezések kiértékelésének van egy olyan momentuma, amely minket is érdekelhet, ha függvénybővítéseket is szeretnénk készíteni. Nem láttuk még a beépített változók, függvények kiértékelő rutinjainak működését, ellátandó feladataikat. Az alapalgoritmus jól követhető egy olyan egyszerű rutinon, mint pl. a PI kiértékelése. Mint a függvénytáblából megállapítható (l. 7. függelék), a rutin az 1. szegmens CA0BH címén kezdődik:

CA0B
CA0C
CA0E
CA0F
D7
92 04
00
C9
RST 10
BASIC 92,04
BASIC 00
RET
; funkcióhívás indul
; PI -> MREG
; funkcióhívás vége

A rutin feladata röviden megadható: a kiértékelés eredményét a BASIC verembe kell tölteni (MREG). A feladat nyilván komplikáltabb olyan rutin esetén, amely paramétert, argumentumot is vár a kiértékeléshez, de a végeredmény akkor is ugyanez: az eredmény a MREG-ben. Nézzük meg pl. a PEEK (CIM) függvény kiértékelését (már csak a POKE utasítással való analógia miatt is):

C9CE

C9D1
C9D4
C9D5
C9D7
C9D8
C9D9
C9DA
>CD E4 C2

CD DB C9
6E
26 00
D7
02
00
C9
CALL C2E4

CALL C9DB
LD L,(HL)
LD H,00
RST 10
BASIC 02
BASIC 00
RET
; BASIC 99: HL=CÍM
;     argumentum kiértékelés
; CÍM kerekítés
; L=PEEK(HL)
; H=0
; funkcióhívás indul
; HL -> MREG
; hívás vége

A címadat transzformációjához (kerekítéséhez) meghívott rutin ugyanazt az algoritmust követi, mint a POKE esetén.
A megszakításokkal foglalkozó fejezetben utalunk a TIME$ kiértékelésre. Nézzük most ezt meg:

C530
C532
C535
C536
C539
C53A
C53D
C53E
C541

C562
C563
C565
C566
C568
C56B
F7 20
2A 28 02
7B
CD 6F C5
7A
CD 6C C5
79
CD 6C C5
18 1F

2B
36 08
2B
36 03
22 28 02
C9
EXOS 20
LD HL,(0228)
LD A,E
CALL C56F
LD A,D
CALL 56C
LD A,C
CALL C56C
JR C562

DEC HL
LD (HL),08
DEC HL
LD (HL),03
LD (0228),HL
RET
; óra lekérdezése
; BASIC veremmutató
; A=sec
; "ss" -> MREG
; A=min
; "mm:" -> MREG
; A=óra (h)
; "hh:" -> MREG


; füzér elé
; hosszbájt
; elé
; füzér típus
; mutató új érték
; MREG = "hh:mm:ss"

Füzér értékű függvény esetén is a MREG-ben hagyott érték a rutin futásának eredménye. Innen lehet kiírni vagy tovább feldolgozni.
A lebegőpontos aritmetika legszebb példáit a trigonometrikus függvények kiértékelő rutinjaiban láthatjuk. Kövessük az egyik legegyszerűbb, a tangens-meghatározó TAN rutin futását; azt a módot, ahogy gyakorlatilag egyetlen BASIC funkcióhívás sorral eljut az argumentumtól a végeredményig:

CC9B
CC9C
CC9D
CC9E

CC9F
CCA0
CCA1
CCA2

CCA3
CCA4
CCA6

CCA7

CCA8
CCA9
CCAA
D7
99
A9
00

D7
15
98
A4

95
92
0C

A4

0F
00
C9
RST 10
BASIC 99
BASIC A9
BASIC 00

RST 10
BASIC 15
BASIC 98
BASIC A4

BASIC 95
BASIC 92,07
BASIC 0C

BASIC A4

BASIC 0F
BASIC 00
RET
; funkcióhívás indul
; argumentum -> MREG
; radiánra, ha kell
; hívás vége

; funkcióhívás indul
; MREG2=MREG1=x
; MREG2 -> REG2
; MREG1=SIN(MREG1)
;     =SIN(x)
; REG2 -> MREG2 (x)
; MREG3=PI/2
; MREG2=MREG2+MREG3
;     =x+PI/2
; MREG2=SIN(MREG2)=
;     =SIN(x+PI/2)=COS(x)
; MREG1=MREG1/MREG2
; hívás vége
; MREG=sin(x)/cos(x)

A rutin tényleges kiértékelő része a BASIC A6H funkcióhívás.

Befejezésül vizsgáljuk meg a gépi kódú programozásban különös jelentőségű USR funkciót. Az ezzel hívott gépi rutinban esetenként pontosan ismerni kell a hívás körülményeit (LAP-konfiguráció, veremállapot stb.). Mivel a USR kiértékelő rutinja az l. szegmensen van (CD3FH), az értelmező ezt lapozza a 3. LAP-ra a végrehajtáskor. A felhasználói rutinba való kilépéskor tehát két biztos pontunk van a memóriakonfigurációban: a 3. LAP-on az 1. szegmens, míg a 0. LAP-on, mint mindig, a nullás lapszegmens (esetünkben az F8H). Az 1. LAP határozatlan (hacsak nem ott fut a BASIC programunk), a 2. LAP-on általában az FFH rendszerszegmens van (de ide is benyúlhat a BASIC).
Maga a rutin két szempontból is eltér az eddigiektől. Egyrészt két argumentuma is van (USR(CIM,ADAT)), így ennek kiértékelésére is látunk példát. Sokkal lényegesebb a másik specialitás: a rutin a felhasználói program hívása előtt a verembe tölt egy visszatérési címet (CAECH). Így a felhasználói rutin RET utasítása nem a főágnak adja vissza a vezérlést, csak erre a címre lép. Ez igen fontos a paraméter visszaadás szempontjából, ui. akármilyen értéket helyez is rutinunk a munkaregiszterbe (kiíratáshoz vagy tovább feldolgozásra), ez a befejező szakasz rátölti a HL regiszterpár aktuális értékét. Visszatéréskor tehát ez a BASIC verem aktuális eleme, ezt írja ki pl. a PRINT USR(CIM,ADAT) utasítás. Ha mi saját adatunkat akarjuk visszaadni az értelmezőnek, ezt a befejezést el kell hagynunk, azaz a PUSH utasítással tárolt (el-PUSH-olt) visszatérési címet le kell emelnünk a veremből (POP). Ügyelni kell azonban arra, hogy ezzel mi vesszük magunkra a BASIC verem kezelésének felelősségét.
Ezek után kövessük a végrehajtást, ami egyébként magának a felhasználói rutinnak a hívására is a PUSH CIM: ... :RET utasításpárt használja:

CD3F
CD42
CD43
CD44
CD45
CD46
CD47
CD48
CD49
CD4A
CD4B
CD4C
CD4D

CAEC
CAED
CAEE
CAEF
21 EC CA
E5
D7
9A
0B
00
E5
D7
9B
A0
0B
00
C9

D7
02
00
C9
LD HL,CAEC
PUSH HL
RST 10
BASIC 9A
BASIC 0B
BASIC 00
PUSH HL
RST 10
BASIC 9B
BASIC A0
BASIC 0B
BASIC 00
RET

RST 10
BASIC 02
BASIC 00
RET
; visszatérési cím
; verembe
; funkcióhívás indul
; "(CIM" kiértékelése
; ÉRTÉK -> HL
; hívás vége
; CÍM verembe
; funkcióhívás indul
; ",ADAT" kiértékelése
; ")" jelet vár
; HL=ADAT
; hívás vége
; elugrik a CÍM-re

; funkcióhívás indul
; HL -> MREG
; hívás vége

Látható, hogy a rutin nem védi (nem őrzi meg) a regisztereket (ezért pl. az IX megváltoztatása a BASIC összeomlásához vezethet), sem a LAP-konfigurációt, így a 3. LAP-on meg kell őriznünk az 1. szegmenst (vagy vissza kell lapoznunk visszatérés előtt).

4. Bővítési, kiterjesztési lehetőségek

Az ENTERPRISE operációs rendszerének kiterjesztésére számos példát láttunk a 2. fejezetben (ilyenek voltak pl. a megszakításkezelő rutinok, perifériakezelők, és természetesen az általános célú rendszerbővítő is). Ebben a fejezetben a BASIC rendszer bővítési lehetőségeit tárgyaljuk.
A két terület nem mindig válik élesen szét, hiszen pl. az EXOS rendszerbővítő is kiterjesztheti a BASIC szolgáltatásainak körét (jó példa erre a német verzió ":VDUMP" funkciója (képernyőmásolás) vagy a német nyelvű hibaüzenetek kiírása). Mivel a rendszerbővítőkről már volt szó a 2.6 alfejezetben, itt már csak a BASIC-en belüli bővítésekkel foglalkozunk.
Új utasításokat, függvényeket a gép komfortos BASIC nyelvén is kialakíthatunk (DEF rutinok), de ezek működési sebessége nyilván lényegesen elmarad a BASIC alapkészletben kínált beépített kulcsszavak és függvények végrehajtásától, kiértékelésétől. Egyes esetekben pedig (pl. parancsmódban használható GOTO) feltétlenül gépi kódú rutinra van, szükség. A továbbiakban tehát a gépi kódban megírt, a BASIC rendszer lehetőségeit valamilyen irányban kiterjesztő, szolgáltatásait bővítő rutinok rendszerbe illesztéséről lesz szó. Maguk a konkrét funkciók elsősorban példaként szolgálnak, hiszen a bővítés irányát nyilván mindenkinél az igények szabják meg.
A gépi rutinok rendszerbe illesztésére, az értelmezőbe való belenyúlásra három - lényegesen különböző - módot tárgyalunk:

4.1 USR rutinok

A BASIC programban felépített és szokásos módon hívott USR rutinok bővítésként való alkalmazására már láttunk példát (3.2.8 pont). Az ilyen típusú felhasználásnak két kritikus pontja van: a rutin tárolási területe és a paraméterkezelés.
A szokásos módon (ALLOCATE) lefoglalt területen tárolt gépi rutin jól használható a programban, de könnyen zavarba jöhetünk, ha parancsmódban, esetleg programszerkesztés közben is szeretnénk élni a szolgáltatással. Ekkor ui. a program visszacsúszhat a lefoglalt területre, felülírva ezzel a gépi rutint, ami a rendszer összeomlásához vezethet a következő hozzáforduláskor. Az ilyen célra szánt rutint tehát a BASIC programtól független, lehetőleg védett területen kell elhelyezni.
A konkrét címre való betöltést a CODE utasítás báziscímének (021C/DH) beállításával érhetjük el:

POKE 540,LO
POKE 541,HI

ahol CÍM = 256*HI+LO

A védettséget egyszerűbb esetben, ideiglenes megoldásként a BASIC mindkét végétől kellő távolságra lévő cím (pl. 3000H) választásával érhetjük el, valódi megoldást azonban a hely lefoglalása jelent. A lehetőségeket az korlátozza, hogy a rutinnak egy mindig belapozott szegmensen kell lennie. Ez gyakorlatilag csak a nullás lapra teljesül. Néhány tucat szabad bájtot a rendszerterület elején is találhatunk (0180H-0200H), de praktikusabb a BASIC munkaterület tetszés szerinti feltolása (erre láttunk példát a 3.1 alfejezet végén).
A paraméterkezelés nem jelent gondot, ha a rutin nem igényel (és nem ad vissza) más adatot, mint az értelmező által kezelt kétbájtos (előjeles egész) értéket (a HL-be töltött, majd futás után onnan kiolvasott adatot).
A más jellegű vagy további paraméterek bevitele a BASIC szintaktikának megfelelően folytatott programszövegből lehetséges. Például:

CALL USR(CIM,HL)x,y

A formailag kissé erőltetett, de hatékonyságában a bővítésekkel egyenértékű megoldás lehetőségét az adja, hogy a USR kiértékelés csak a befejező zárójelig ellenőrzi és értékeli ki a programszöveget (így nem veszi észre az egyébként szintaktikusan hibás szöveget), majd a következő elem típusvizsgálata (BASIC 20H) után lép a felhasználói rutinba. Ebben a rutinban a tervezett feldolgozás szerint ellenőrizhetjük és értékelhetjük ki a paramétermezőt és formai szempontból ismét helyes aktuális pozícióban (a sor végén) adjuk vissza a vezérlést az értelmezőnek.
BASIC utasításnak szánt rutinunkban akkor járunk el helyesen, ha a BASIC vermet alaphelyzetben adjuk vissza a rendszernek. Ha azonban függvénybővítésként funkcionál a rutin, a futás eredménye a függvényérték. Ennek visszatéréskor a BASIC veremben kell maradnia. A 3.4.4 pontban (a USR kiértékelés elemzésekor) azt is bemutattuk, hogy miként lehet megakadályozni a USR befejező szakaszt, hogy HL értékét aktuális elemként a BASIC verembe tegye (egy pár nélküli POP utasítás volt a megoldás). Így a kiértékelés eredménye marad az aktuális MREG-ben, ezt írja ki (PRINT USR...) vagy dolgozza fel (A=B*USR...) az értelmező.
Ez a mód érezhető nehézkessége miatt elsősorban egy programon belüli ideiglenes célfeladatra, ill. az egyéb megoldások tesztelésére alkalmas. Az alkalmazásról eddig elmondottak szemléltetésére nézzünk néhány (függvény, ill. utasítás típusú) példát:

LINEPTR n (line-pointer)

Az n. BASIC sor tárolási címét adja meg. A megoldás a legegyszerűbb alkalmazásra mutat példát, hiszen a BASIC sorszám (0-9999) a USR természetes változójaként megadható és a kiértékelés eredménye is kétbájtos egész. A rutin megkapja HL-ben a keresett sorszámot az értelmezőtől és így is adhatja vissza a címet. A megoldás igen egyszerű a BASIC 2EH funkcióhívás felhasználásával:

LNPT RST 10H
DEFB 2EH
DEFB 00H
RET
; funkcióhívás indul
; BASIC 2E: HL. sor keresése
; funkcióhívás vége
;      HL=cím

A rutin sajátossága, hogy nem létező sorszám esetén is értelmes címet ad vissza (a következő létező sor címét, ill. a program végét). Így a vizsgálni kívánt sor keresése mellett egyszerűen megkaphatjuk az aktuális program kezdő- és végcímét is. Ha csak akkor szeretnénk 0-tól különböző címet, ha létező sor számát adtuk meg, felhasználhatjuk, hogy a BASIC 2EH után a státusz "C", ha nem találta a keresett sort:

LNPT2




L1
RST 10H
DEFB 2EH
DEFB 00H
JR NC,L1
LD HL,0000H
RET
; funkcióhívás indul
; BASIC 2E: HL. sor keresése
; funkcióhívás vége
; ugrik, ha talált ilyen sort
; HL=0, ha nem volt ilyen sor
;      HL=cím vagy 0

Az ideiglenes használatra szánt rutint az ALLOCATE-mezőn is elhelyezhetjük, de ha 0-tól eltérő számú programokban is használni szeretnénk, töltsük inkább rögzített címre, pl. a 3000H (12288D) címre. Nem túl hosszú 0-s BASIC program esetén itt biztonságban van:

1 PROGRAM "Lineptr.bas"
100 POKE 540,0
110 POKE 541,48 !3000H
120 CODE LNPT=HEX$("D7,2E,00,C9")
130 CODE LNPT2=HEX$("D7,2E,00,30,03,21,00,00,C9")
140 DEF LINEPTR(L)=USR(LNPT,L)
150 DEF LINEPTR2(L)=USR(LNPT2,L)

A program a DEF sorok segítségével azt is eléri, hogy valóban függvényként hívhatjuk rutinunkat, amint arra a képernyőmásolat néhány példát is mutat.
Ha más programban (0-tól, különböző programszámon) is használni kívánjuk a rutint, ott is kell írnunk egy hívás-sort (persze a rögzített címmel, hiszen az a program nem ismerheti az LNPT változót):

DEF LINEPTR(L)=USR(12288,L)

VARPTR X (variable-pointer)

Az argumentumban megadott változó tárolási címét keresi meg. A bővítések következő fokozata, amelyben az argumentum már nem adható meg a szokásos szintaktikai keretek között. Itt tehát a bevezetőben leírt módszerhez kell folyamodnunk: a felhasználói rutinba lépve tovább kell elemeznünk a programszöveget. Magát a keresés-funkciót is egy funkcióhívás szolgálja (BASIC 8EH). A feladat egyik megoldását már megadtuk a változók ábrázolási módjának bemutatásakor (3.2.8 pont, ill. a BASIC funkcióhívások alkalmazási példái között. Ott pl. az A változó címét a

CIM=USR(V-CIM,0) A

hívással kaptuk meg. A rutin feladata ebben az esetben:

Most tekintsük ezt a feladatot esettanulmánynak, amelyben megpróbáljuk túllépni a legegyszerűbb megoldás korlátait. Az idézett rutinnak ui. két hiányossága van, amely szerencsére az alkalmazások nagy részében nem okoz gondot:

Mindkét probléma megoldható a funkcióhívások bőséges szolgáltatásaival. Az algoritmus a következő:

  1. A programszövegben a rutint követő füzér kiértékelése. Az eredmény (a név karaktereit tartalmazó füzér) a BASIC verembe kerül (BASIC 24H).
  2. A név nagybetűsítése (az értelmező is így tárol, tehát így azonosítható) (BASIC C4H).
  3. A szövegelemzés pointereinek beállítása a verembe írt füzérre.
  4. Tárolási cím megkeresése (BASIC 8EH).
  5. A füzér (most már fölösleges) blokkjának törlése a veremből (BASIC 16H).
    A további lépések a cím - teljes tartományban való - helyes visszaadását szolgálják.
  6. A MREG-ben lévő cím előjelének vizsgálata. Ha pozitív (értsd: kisebb 32768-nál), a megoldás kész.
  7. Az értelmező által negatívként értelmezett cím transzformációja: MREG=65536+(-CIM)
  8. A befejező lépés a már többször említett veremrendezés annak érdekében, hogy az értelmező a MREG tartalmát (és ne HL értékét) kapja vissza.

Mindezt a következő assembly program valósítja meg:

VP





























L1
RST 10H
DEFB 24H
DEFB 00H
LD B,60
RST 10H
DEFB 0C4H
DEFB 00H
LD HL,(0228H)
INC HL
LD A,(HL)
LD (0203H),A
INC HL
LD (0347H),HL
RST 10H
DEFB 8EH
DEFB 02H
DEFB 97H
DEFB 16H

DEFB 20H
DEFB 94H
DEFB 05H
DEFB 00H
JR Z,L1
RST 10H
DEFB 92H,03H
DEFB 15H
DEFB 0CH
DEFB 0CH
DEFB 00H
POP BC
RET
; funkcióhívás indul
; BASIC 24: string -> MREG
; funkcióhívás vége
; "nagybetű"
; funkcióhívás indul
; BASIC C4: MREG nagybetűsítése
; hívás vége
; MREG kezdőcím (típusbájt)
; (füzérhossz)
; A=hossz
; rendszerváltozóba
; első karakter címe
; szöveg-pointerbe
; funkcióhívás indul
; BASIC 8E: HL=tárolási cím
; BASIC 02: HL -> MREG
; BASIC 97: MREG -> REG1
; BASIC 16: füzér törlése
;     BASIC veremből
; BASIC 20: köv. elem vizsgálata
; BASIC 94: cím vissza MREG-be
; BASIC 05: előjelvizsgálat
; hívás vége
; marad a cím, ha < 32768
; funkcióhívás indul
; BASIC 92, 03: 32768 -> MREG2
; BASIC 15: 32768 -> MREG3
; BASIC 0C: (+): MREG2=65536
; BASIC 0C: (+): MREG=(-CÍM)+64k
; hívás vége
; veremrendezés

A BASIC betöltő most a 0180H területen tárolja a rutint (éppen a feltételezett nagyméretű program, változómező miatt most nem használhatjuk a BASIC terület közepét). A program a bővítés alkalmazási módjára is mutat példát:

1 PROGRAM "Varptr.bas"
10 LET BC=1
20 LET A$="SAMU"
100 POKE 540,128
110 POKE 541,1 ! 0180H
120 CODE VP=HEX$("D7,24,00,06,60,D7,C4,00,2A,28,02,23,7E,32,03,02,23,22,47, 03,D7,8E,02,97,16,20,94,05,00,28,07,D7,92,03,15,0C,0C,00,C1,C9")
130 DEF VARPTR(NEV$)=USR(VP,0) NEV$
140 INPUT PROMPT "A keresett valtozo? ":N$
150 PRINT "Cim: ";VARPTR(N$)
160 GOTO 140

Befejezésül nézzünk egy példát az utasítás típusú bővítésre is. Valósítsuk meg pl. a DPOKE CIM,ADAT utasítást, ahol az ADAT egy kétbájtos egész:

ADAT=256*HI+LO

és a kért művelet tulajdonképpen:

POKE CIM,LO
POKE CIM+1,HI

Ha lemondunk a kerekítési problémák precíz kezeléséről (az értelmező az egészrész vételekor egyszerűen elhagyja a törtrészt, ezzel a pozitív értékeknél lefelé kerekít, de a negatív (azaz 32767 fölötti) értékeknél fölfelé), a feladat igen egyszerűen megoldható: a CIM a USR argumentumában átadható, az ADAT a következő programszövegből olvasható be és a feladat azonnal végrehajtható a paraméterekkel:

DP PUSH HL
RST 10H
DEFB 23H
DEFB 0BH
DEFB 00H
POP DE
EX DE,HL
LD (HL),E
INC HL
LD (HL),D
RET
; CÍM verembe
; funkcióhívás indul
; ADAT kiértékelés
; ADAT -> HL
; funkcióhívás vége
; DE=CÍM
; HL=CIM, DE=ADAT
; POKE HL,LO

; POKE HL+1,HI

Itt nem megoldható a közvetlen (DPOKE CIM,ADAT típusú) hívás. A beírás, beillesztés egyszerűségéért az alkalmazás formai nehézkességével (CALL DPOKE(CIM,ADAT) kell fizetnünk:

1 PROGRAM "DOKE.bas"
100 POKE 540,0
110 POKE 541,48
120 CODE DP=HEX$("E5,D7,23,0B,00,D1,EB,73,23,72,C9")
130 DEF DPOKE(CIM,ADAT)
140   CALL USR(DP,CIM) ADAT
150 END DEF

4.2 Utasítás és függvénybővítések

4.2.1 A kulcsszótábla kiterjesztése

Az utasítások szintaktikai ellenőrzésének és végrehajtásának bázisa - mint láttuk - a kulcsszótábla. Az alaphelyzetben kiépülő tábla 93 kulcsszó pointereit tartalmazza RAM-területen (tehát megváltoztathatóan). Ezek a pointerek 3 bájtból állnak (l. 3.2.5 pont):

A kulcsszó paraméterblokkja:

1-2. bájt: a végrehajtó rutin (1. rutin) címe,
1-4. bájt a szintaktikai elemzést, kiegészítő tokenizálást végző 2. rutin címe,
5. bájt: a típusbájt,
6. bájt: a kulcsszó hossza,
7. bájttól: a kulcsszó karakterei (nagybetűk).

A kulcsszótábla szolgál alapul mind a programszöveg elemzésénél (a kulcsszavak felismerésénél), mind a végrehajtásnál. A tábla kezelési módja lehetőséget ad újabb táblák rendszerbe láncolására. A legelső két bájtot ui. egy táblalánc pointerének tekinti az értelmező: ha itt 0000H-tól különböző értéket talál, az adott tábla végigfutása után az itt megadott címen keresi a következő táblát és ezen folytatja a keresést. Egy-egy tábla elemeinek (3-bájtos pointerek!) számát a tábla 3. bájtja adja meg. A legelső táblára a (0232/3H) rendszerváltozó mutat.
Alaphelyzetben egyetlen kulcsszótábla épül ki (a 0E64H-0F7DH tartományban). Az első két bájt 0: a lánc nem folytatódik. A 3. bájt értéke 5DH (93D), az alapértelmezésű kulcsszavak száma.
Mit kell még tudnunk ahhoz, hogy a táblát biztonsággal terjeszthessük ki? A memóriakonfiguráció bizonyos megkötéseit. Az értelmező adott (elemző, végrehajtó) szakaszainak működése során a 3. LAP-on az UK szegmens van belapozva. Ezen kívül csak a nullás lap állapota tekinthető biztosnak. Ezek szabják meg bővítéseink elhelyezését. Az esetleges újabb kulcsszótáblák, valamint a tábla pointerei által címzett paraméterblokkok ui. az előbbiek miatt csak a nullás lapra helyezhetők. A végrehajtó rutinok meghívása viszont a BASIC lapozórutinján keresztül történik, miközben a kulcsszótábla pointerének 3. bájtján megadott szegmens kerül a 3. LAP-ra. Amíg tehát egy bővítés pointerének a 0. LAP-on lévő paraméterblokkra kell mutatnia, a paraméterblokk által megadott két rutincím tetszés szerint lehet a nulláslapon (ekkor a pointer 3. bájtja érdektelen) vagy a 3. LAP-on (ekkor viszont a rutint ténylegesen tartalmazó szegmens számát kell megadni a pointerben).
Milyen módjai vannak tehát a BASIC utasítások megváltoztatásának, kiterjesztésének?

  1. A meglévő tábla pointereit átcímezhetjük RAM-területre (0. LAP!) és itt új paraméterblokkot létrehozva megváltoztathatjuk az adott kulcsszó nevét, szolgáltatásait, vagy az eredeti funkció feláldozásával új utasítást is készíthetünk. Ez a kevesebb előkészületet igénylő, egyszerűbb megoldás.
  2. Új kulcsszótáblát láncolhatunk az eredeti után (az eléláncolás is megoldható, ekkor az új kulcsszavakat találja meg először a rendszer, de ez felborítja az eredeti tokenhozzárendelést, ezért nem javasolható).

Ennyi bevezetés után nézzünk most már konkrét példákat az elmondottak szemléltetésére.

Első kísérletként írjunk új paraméterblokkot a LET kulcsszóhoz. Pontosabban írjuk először az eredeti blokkot a RAM-ba és címezzük erre a LET pointert, míg a szegmensszámot hagyjuk meg eredeti (5) értékén: maradjon az értelmező szegmense belapozva a LET rutinok futása alatt. A kulcsszótábla kezdőcímét a rendszerváltozóból olvassuk ki, KT a 0. pointerre mutat, a LET pointere a 40. a táblában (LET token: 28H):

100 LET KT=PEEK(562)+256*PEEK(563)+3
110 LET LETPTR=KT+40*3
120 POKE 540,0
130 POKE 541,48
140 CODE =HEX$("80,D2,67,D2,53,03")&"LET"
150 POKE LETPTR,0
160 POKE LETPTR+1,48

A futtatás után a LET minden szempontból az eredeti tulajdonságokkal bír. Ellenőrizzük, hogy most már valóban a mi paraméterblokkunk határozza meg ezeket a tulajdonságokat: adjunk első lépésként új nevet (pl. LEGYEN) az utasításnak:

1 PROGRAM "LEGYEN.bas"
100 LET KT=PEEK(562)+256*PEEK(563)+3
110 LET LETPTR=KT+40*3
120 POKE 540,0
130 POKE 541,48
140 CODE =HEX$("80,D2,67,D2,53,06")&"LEGYEN"
150 POKE LETPTR,0
160 POKE LETPTR+1,48

A futtatás után - mint az a képernyőmásolatból is látható - a listában új néven jelenik meg az utasítás, a továbbiakban értelmetlen a LET karaktersorozat, viszont felismeri (és értékadásként végre is hajtja) az értelmező a LEGYEN utasítást. Ezzel az egyszerű módszerrel akár az egész kulcsszókészlet is átnevezhető.

Írjuk át ezek után a 2. rutin címét pL. a PING végrehajtó rutinjára (persze ezzel elveszítjük az elemzés közbeni szintaktikai ellenőrzést):

135 CODE =HEX$("80,D2,BF,D3,53,06")&"LEGYEN"

Ez az átírás jól illusztrálja az elemzés lépését, hiszen mindig egy PING hang hallatszik, valahányszor az értelmező a beírt sor elemzésekor meghívja a LET 2. rutint. Próbálja beírni az utasítást parancsmódban vagy egy programsorba, és vesse össze a hallottakat az eddigi ismereteivel!

 

E kis kísérletezés után írjunk most már valódi bővítéseket a rendszerbe. Ehhez foglaljunk le ténylegesen védett területet a nulláslapon, hogy a továbbiakban ne legyen gondunk a rutinok, táblák elhelyezésével. A gép bekapcsolása után a 3.1 alfejezetben bemutatott egyszerű rutinnal toljuk el pl. 2000H-ra a BASIC munkaterületét:

1 PROGRAM "Eltol.bas"
100 ALLOCATE 100
110 CODE M=HEX$("21,00,20,3E,05,D3,B3,C3,AC,C2")
120 CALL USR(M,0)

Ezután (a BASIC újrainicializálásáig) szabadon használhatjuk az eredeti munkaterület 2000H alatti részét (az adott verzióban 12DBH-1FFFH). A továbbiakban tárgyalt példák ilyen eltolt BASIC rendszerben értendők, a mintaprogramban alkalmazott címek is erre az esetre vonatkoznak!
Először írjunk meg két olyan bővítést, amely az eredeti utasításkészlet elemeinek működését változtatják meg. Nincs tehát szükség új kulcsszavakra, csak a végrehajtási címeket kell átirányítani (mint a LET esetében).

Rugalmas POKE
írjuk át első lépésként a POKE utasítást úgy, hogy működhessen eredeti funkciójában is, de ha a megadott ADAT kétbájtos érték (nagyobb 255-nél), automatikusan az előző pontban bemutatott DPOKE funkciót hajtsa végre. A 2. rutin címét meghagyhatjuk eredeti értékén, de a végrehajtó rutint nekünk kell megírnunk:

POKE. RST 10H
DEFB 23H
DEFB 0BH
DEFB 00H
PUSH HL
LD A,0CH
RST 10H
DEFB 22H
DEFB 23H
DEFB 0BH
DEFB 00H
POP DE
EX DE,HL
LD (HL),E
LD A,D
OR A
RET Z
INC HL
LD (HL),D
RET
; funkcióhívás indul
; BASIC 23: kiértékelés (CIM)
; BASIC 0B: HL=CIM
; funkcióhívás vége
; CIM verembe
; "," kódja
; funkcióhívás indul
; BASIC 22: "," követi?
; BASIC 23: kiértékelés (ADAT)
; BASIC 0B: HL=ADAT
; funkcióhívás vége
; DE=CIM
; HL=CIM, DE=ADAT
; POKE CIM,LO
; A=HI
; A=0? (ADAT<256?)
; kész, haí igen

; POKE CIM+1,HI

A rutinban most sem foglalkoztunk a kerekítés problémájával. Ha ez szükséges, az argumentumok kiértékelése után beépíthető az eredeti POKE végrehajtásnál látott (3.4.4 pont) kerekítő algoritmus.
A BASIC betöltő az előbbi példával analóg módon ad új paramétertáblát a POKE utasításnak és címzi át a megfelelő pointert (a POKE tokenje: 36H = 54D). A rutin pikantériája, hogy a pointer megváltoztatása két lépésben történik (alsó és felső bájt, 170., 180. sorok) és a két lépés között ideiglenesen értelmetlen a POKE utasítás. Ezért használtuk ezekben a sorokban a SPOKE utasítást (a nulláslap szegmense esetünkben a 248.):

1 PROGRAM "BASext.bas"
100 LET KT=PEEK(562)+256*PEEK(563)+3
110 LET POKEPTR=KT+54*3
120 POKE 540,0
130 POKE 541,19
140 CODE =HEX$("10,13,61,D0,53,04")&"POKE"
150 POKE 540,16
160 CODE =HEX$("D7,23,0B,00,E5,3E,0C,D7,22, 23,0B,00,D1,EB,73,7A,B7,C8,23,72,C9")
170 SPOKE 248,POKEPTR,0
180 SPOKE 248,POKEPTR+1,19

A rutin futtatása után pl. a 120, 130. sorok helyett POKE 540,4864 is írható. A betöltés után a BASIC program törölhető, csak arra kell ügyelnünk, hogy a POKE paraméterblokk (1300H-) és a végrehajtó rutin (1310H-1324H) a továbbiak folyamán is megőrződjön.

GOTO parancsmódban
Az ENTERPRISE BASIC-jének bosszantó tulajdonsága (nem sok van!), hogy parancsmódban nem fogadja el a GOTO utasítást. Igen kellemetlen egy esetleg hosszú feldolgozás során nyert adattömeg elvesztése, ha valamilyen ok miatt (pl. egy meggondolatlan STOP) leáll a program futása és újraindítása csak RUN-nal lehetséges. Az ilyen esetek jelentős részében segíthet, ha - kiterjesztve a beépített GOTO hatáskörét - adattörlés nélkül léphetünk a kívánt sorra.
Annak közvetlen oka, hogy az értelmező parancsmódban nem fogadja el ezt az utasítást, a paraméterblokk típusbájtjában (52H) keresendő: törölt a "parancsmódban használható" jelentésű 0. bit. A paraméterblokk RAM-ba helyezésével ez a korlátozás kiküszöbölhető, de ez még nem oldja meg a problémát. Az így átírt utasítást ugyan elfogadja az értelmező, de parancsmódban nem hajt végre semmit. Ennek oka a GOTO végrehajtási logikájában található meg: a rutin nem ugrik el a keresett sorra, csak betölti annak címét a megfelelő pointerbe (0216H) és úgy állítja be folytatásjelzőt (0206H = 3), hogy a programvégrehajtási főág a célsoron folytassa a futást. Parancsmódban azonban nem a programvégrehajtási főágba tér vissza a vezérlés a GOTO-ból, és így nem is éri el a célját.
Ezek alapján már egyszerű a megoldás. A 2. rutint meghagyva eredeti címén, az elvégzi a célsorszám előkészítését (A2H típuskód). így nekünk nem kell a kiértékeléssel foglalkoznunk, a sorszámot a 0218/9H rendszerváltozóban készen kapjuk. A fenti rendszerváltozók beállítása után el is ugorhatnánk a végrehajtási főágba. Az eredeti GOTO rutinnak azonban van egy olyan szolgáltatása, amit nekünk is fel kell használnunk: ha egy megkezdett FOR-NEXT vagy DO-LOOP blokkból a blokk határain kívül kell ugrani, törli a BASIC veremből az érvénytelenné vált elemeket. Hívjuk hát meg saját rutinunkban is az eredeti GOTO rutint:

GOTO. CALL 0D1DF
JP 0CFA4
; eredeti GOTO rutin hívása
; elugrik a programfőágba

A betöltőprogram futtatása után már parancsmódban és programban egyaránt használhatjuk (eredeti szintaktikája szerint) az új utasítást.

100 LET KT=PEEK(562)+256*PEEK(563)+3

200 LET GOTOPTR=KT+33*3
210 POKE 540,0
220 POKE 541,20
230 CODE =HEX$("10,14,5A,D0,53,04")&"GOTO"
240 POKE 540,16
250 CODE =HEX$("CD,DF,D1,C3,A4,CF")
260 POKE GOTOPTR,0
270 POKE GOTOPTR+1,20

Ha új utasításokat szeretnénk létrehozni, megtartva és kiegészítve az értelmező eredeti utasításkészletét, a kulcsszótáblát kell megtoldani. Egy-két új utasítás esetén talán megoldást jelent a ritkán használt utasítások feláldozása (ilyen lehet pl. a CAPTURE, az OK, az EXT - elérhető másképp is -, a REM (van helyette "!" stb.). Perspektívát azonban csak egy új kulcsszótábla készítése és rendszerbe illesztése (beláncolása) jelent. Ezért erre nézzünk most néhány példát.

Rövidített kulcsszavak
Hosszadalmas programírás közben bizonyára sokan gondolnak nosztalgiával a SPECTRUM és COMMODORE 1-2 billentyűs kulcsszó beírási lehetőségére. Terjesszük ki ez irányba az ENTERPRISE lehetőségeit is: bővítsük a rendszert olyan utasításokkal, amelyek minden szempontból megegyeznek az eredeti funkciókkal, csak a nevük más: 1-2 karaker, igény szerint. Készítsünk pl. egy L (értsd: LIST) és egy PR (értsd: PRINT) elemet.
Az új tokentábla első 2 bájtja pointer a következő láncelemre (most nincs ilyen, tehát 0,0), 3. eleme pedig a tábla kulcsszavainak száma (itt 2). Ezután következnek a 3-bájtos pointerek, majd a paraméterblokkok. Az új tábla beláncolásához a tábla kezdőcímét kell beírni az alaptábla pointerébe:

100 LET KT=PEEK(562)+256*PEEK(563)+3

310 POKE 540,0
320 POKE 541,21
330 CODE TOKENTABLA_2=HEX$("00,00,02")
340 CODE LIST.=HEX$("10,15,05")
350 CODE PRINT.=HEX$("20,15,05")
380 POKE 540,16
390 CODE L=HEX$("2E,C8,9C,CA,41,01")&"L"
400 POKE 540,32
410 CODE PR=HEX$("08,D4,01,D4,53,02")&"PR"
430 ! Belancolas
450 POKE KT-3,0
460 POKE KT-2,21

Az új kulcsszavak könnyen (és gyorsan) használhatók, de megvan az a hátrányuk, hogy a listában is a rövid alakban jelennek meg és ami még nagyobb probléma, a velük írott programok csak a bővítő jelenlétében futnak (alaphelyzetű gépbe visszaolvasva hibát okoznak). Megoldható ez a gond is, ha a 2. rutint is mi írjuk meg, legalábbis a bevezető szakaszát és abban visszaírjuk az új tokén helyére az eredetit:

PR_2 LD HL,(0214H)
DEC HL
LD A,38H
LD (HL),A
JP 0D401H
; elemzési pointer
; vissza a tokenre
; PRINT token
; a PR token helyére
; elugrik a PRINT 2. rutinra

A kiegészítéshez szükséges új sorok:

410 CODE PR=HEX$("08,D4,30,15,53,02")&"PR"
412 POKE 540,48
414 CODE RUT2=HEX$("2A,14,02,2B,3E,38,77,C3,01,D4")

Befejezésként illesszünk két képernyőkezelő bővítést a rendszerhez. A videoprocesszor programozása, a képernyőkezelés, a grafika EXOS-on keresztüli vagy közvetlen elérése rendkívül nagy terület. Egész kötetet lehetne megtölteni a témára vonatkozó információkkal, alkalmazási példákkal, trükkökkel. Ebben az összeállításban csak ízelítőként mutatunk be néhány adalékot. A 8. Függelékben mindenesetre összefoglaljuk a legfontosabb hívásokat, adatokat, a következőkben pedig adunk néhány a - képernyőkezelést megkönnyítő - utasítás- és függvénybővítést.

POKE a videó RAM-ba
A videoszegmensek (FCH-FFH) elérésének nehézkességét az adja, hogy míg a megjelenítést végző NICK-chip a 0064 k címtartományban összefüggően látja ezeket (0000-3FFF:FC szegmens, ..., C000-FFFF:FF szegmens), addig mi csak szegmensként érjük el ezt a területet: a videocím értékéből kell meghatároznunk, hogy melyik szegmensről van szó. A videocím - pl. sorparamétertáblából kiolvasható - értéke alapján tehát nehézkes pl. POKE utasítással adatot vinni a képernyőterületre. Készítsük el ezért a

VPOKE CIM,ADAT

bővítést, amely a megadott videocím alapján meghatározza a hozzárendelt szegmensszámot és abba tölti az adatot.
A bővítés 2. rutinjaként alkalmazhatjuk a beépített POKE 2. rutinját (hasonló a szintaktika) és a végrehajtó rutin első szakasza is átvehető az előbbiekben megismert POKE kiterjesztésből.
Itt az adat értéktartománya korlátozott (0-255), ezt tehát ellenőriznünk kell (az esetleges hibajelzésre a BASIC RST 20H hibakezelőjét használjuk fel). Ezután következik az érdemi feladat. A címtartományt a címbájt első két bitje határozza meg. Ezt kiemelve és 0-3 közé transzformálva megkapjuk a relatív szegmensszámot. A rutin ezt a szegmenst lapozza az 1. LAP-ra, és a cím 4000-7FFF közé transzformálása után ide írja be az adatot:

VPOKE. RST 10H
DEFB 23H
DEFB 0BH
DEFB 00H
PUSH HL
LD A,0CH
RST 10H
DEFB 22H
DEFB 23H
DEFB 0BH
DEFB 00H
POP DE
LD A,H
OR A
JR Z,L1
LD HL,03E8H
RST 20H
; funkcióhívás indul
; BASIC23: kiértékelés (CIM)
; BASIC 0B: HL=CIM
; funkcióhívás vége
; CIM verembe
; "," kódja
; funkcióhívás indul
; BASIC 22: "," következik?
; BASIC 23: kiértékelés (ADAT)
; BASIC 0B: HL=ADAT
; hívás vége
; DE=CIM
; ADAT felső byte
; A=0?
; rendben, ha ADAT<256
; "Érték nem megfelelő"
; hibára
L1 EX DE,HL
LD A,H
AND 0C0H
RLCA
RLCA
ADD 0FCH
OUT (0B1H),A
RES 7,H
SET 6,H
LD (HL),E
RET
; HL=CIM, E=ADAT
; CIM felső byte
; 1100 0000 maszkolás
; érték eltolás
; 00-03 közé
; 0. videoszegmens + érték
; kijelölt szegmens az 1. LAP-ra
; cím transzformálás
; az 1. LAP tartományába
; POKE CIM,ADAT

A bővítés beillesztéséhez szükséges kiegészítés a következő:

330 CODE TOKENTABLA_2=HEX$("00,00,03")

360 CODE VPOKE.=HEX$("40,15,05")

500 POKE 540,64
510 CODE VPOKE=HEX$("50,15,61,D0,53,05")&"VPOKE"
520 POKE 540,80
530 CODE VP_RUT=HEX$("D7,23,0B,00,E5,3E,0C,D7,22, 23,0B,00,D1,7C,B7,28,04,21,E8,03,E7,EB,7C, E6,C0,07,07,C6,FC,D3,B1,CB,BC,CB,F4,73,C9")

A program futtatása után használható a VPOKE utasítás, amely pl.

VPOKE 1,255

esetén az FCH szegmens 1. címére tölt 255-öt, de

VPOKE 49153,255

esetén az adat az FEH szegmens 1. címére kerül (49153D = C001H).

Téglalaprajzolás
Befejező példánk egy grafikus bővítés: az alapértelmezésű (#101) grafikus csatornára egy téglalapot rajzol:

BOX X,Y

A téglalap bal alsó csúcsa az aktuális PLOT-pozícióba kerül, alapja X, magassága Y. A sugár ki- és bekapcsolását a bővítés nem vezérli, azt a szokásos módon kell beállítani.
A megoldás alapja a VIDEO perifériakezelő által támogatott

ESC R,xx,yy

karaktersorozat írás (relatív sugármozgás vízszintesen xx - kétbájtos érték -, függőlegesen yy lépéssel). Rutinunk hatékonysága érdekében felhasználtuk az UK (esetünkben az 5.) szegmens FE7CH rutinját, amely az aktuális csatornára sorra kiírja az 1BH(ESC),B,E,D,L,H értékeket, ill. regisztereket. Az algoritmus ezek után egyszerűen követhető. Lényegében a

PLOT X0,Y0; X0,Y0+yy; X0+xx,Y0+yy; X0+xx,Y0; X0,Y0

utasítás menetét követjük

BOX LD A,65H
LD (0209H),A
RST 10H
DEFB 23H
DEFB 98H
DEFB 00H
LD A,0CH
RST 10H
DEFB 22H
DEFB 23H
DEFB 97H
DEFB 94H
DEFB 0BH
DEFB 00H
LD DE,0000H
PUSH DE
LD B,52H
PUSH BC
CALL 0FE7CH
RST 10H
DEFB 95H
DEFB 0BH
DEFB 00H
EX DE,HL
POP BC
POP HL
PUSH HL
PUSH BC
CALL 0FE7CH
RST 10H
DEFB 94H
DEFB 06H
DEFB 0BH
DEFB 00H
POP BC
POP DE
PUSH DE
PUSH BC
CALL 0FE7CH
RST 10H
DEFB 95H
DEFB 06H
DEFB 0BH
DEFB 00H
EX DE,HL
POP BC
POP HL
CALL 0FE7CH
XOR A
LD (0209H),A
RET
; GRAPHICS csatorna
; beállítása aktuális csatornaként
; funkcióhívás indul
; BASIC 23: kiértékelés (X)
; BASIC 98: REG2=X
; hívás vége
; "," kódja
; funkcióhívás indul
; BASIC22: "," következik?
; BASIC 23: "kiértékelés (Y)
; BASIC 97: REG1=Y
; BASIC 94: MREG=Y
; BASIC 0B: HL=Y
; hívás vége
; DE=0
; konstans verembe
; "R": relatív sugármozgatás
; kód verembe
; ESC R,0,Y: bal oldal
; funkcióhívás indul
; BASIC 95: MREG=X
; BASIC 0B: HL=X
; hívás vége
; DE=X
; B="R"
; HL=0
; konstansok
; vissza a verembe
; ESC R,X,0: felső él
; funkcióhívás indul
; BASIC 94: MREG=Y
; BASIC 06: MREG=-Y
; BASIC 0B: HL=-Y
; hívás vége
; B="R"
; DE=0
; konstansok
; vissza a verembe
; ESC R,0,-Y: jobb oldal
; funkcióhívás indul
; BASIC 95: MREG=X
; BASIC 06: MREG=-X
; BASIC 0B: HL=-X
; hívás vége
; DE=-X
; B="R"
; HL=0
; ESC R,-X,0: alsó él
; A=0
; aktuális csatorna alapérték

Az értékek megfelelő tárolását és beolvasását, előjelváltását - egészekről lévén szó - talán valamivel egyszerűbben is megoldhattuk volna, de így legalább az aritmetikai funkcióhívásokat is gyakoroltuk egy kicsit.
Itt is megadjuk a BASIC betöltőprogram előbbiekhez illeszkedő kiegészítését.

330 CODE TOKENTABLA_2=HEX$("00,00,04")

370 CODE BOX.=HEX$("80,15,05")

540 POKE 540,128
550 CODE BOX=HEX$("90,15,61,D0,53,03")&"BOX"
560 POKE 540,144
570 CODE BX_RUT=HEX$("3E,65,32,09,02,D7,23,98,00,3E,0C,D7,22, 23,97,94,0B,00,11,00,00,D5,06,52,C5,CD,7C,FE,D7,95,0B,00,EB, C1,E1,E5,C5,CD,7C,FE,D7,94,06,0B,00,C1,D1,D5,C5, CD,7C,FE,D7,95,06,0B,00,EB,C1,E1, CD,7C,FE,AF,32,09,02,C9")

4.2.2 A függvénytábla kiterjesztése

A beépített függvények és változók nyilvántartásának alapja a függvénytábla. A tábla elemei lényegében változó jellegű blokkok, csak az adatmező speciális. A változó fejrésze a szokásos:

Ezután az adatmező következik, ami a 0CH, 0DH kódú változóknál 3-bájtos: a kiértékelő rutin címe és szegmense.
Ilyen változóként kell tehát nekünk is létrehoznunk saját függvénybővítéseink paraméterblokkját, majd a blokkot be kell táncoltatni a változónyilvántartásba (ehhez felhasználhatjuk a BASIC 80H funkcióhívást).
Fölmerülhet persze a kérdés, hogy a meglévő függvények esetleges átírásával nem lehetne-e egyszerűbben elérni a célunkat. Természetesen itt is feláldozhatunk egy-két ritkán használt függvényt, változót (pl. a VERNUM-ért vagy a VER$-ért nemigen fáj senkinek a szíve) és a név, valamint a kiértékelő rutin címe céljainknak megfelelően állítható be. Az igazi megoldást azonban itt is a védett területen felépített rutinok és paraméterblokkok beláncolása jelenti. Erre mutatunk a következőkben példát.
Először nézzük meg néhány illusztrációnak szánt, de esetenként jól használható függvénybővítés funkcióját, működését, majd a fejezet végén együtt foglalkozzunk a bővítők elhelyezésével és rendszerbe illesztésével.

DPEEK
Az előző pontban megvalósított DPOKE párjaként jól használható (pl. a gépi kódú programozással kapcsolatban is) a

DPEEK(CIM)

függvény, ami lényegében a

PEEK(CIM) + PEEK(CIM+1)*256

kétbájtos értéket adja vissza.
A feladat viszonylag egyszerűen megoldható. A CIM argumentum (itt zárójelben lévő numerikus kifejezés) kiértékelésére a BASIC 99H hívás alkalmas. A kiolvasott kétbájtos egész értéket az értelmező számunkra most kellemetlen módon előjelesként értékeli. Az értéktranszformációt a 4.1 alfejezetben már alkalmazott (VARPTR) módszerrel végezhetjük el:

DPEEK RST 10H
DEFB 99H
DEFB 0BH
DEFB 00H
LD E,(HL)
INC HL
LD D,(HL)
EX DE,HL
RST 10H
DEFB 02H
DEFB 05H
DEFB 00H
JR Z,L1
RST 10H
DEFB 92H,03H
DEFB 15H
DEFB 0CH
DEFB 0CH
DEFB 00H
; funkcióhívás indul
; BASIC 99: kiértékelés (CIM)
; BASIC 0B: HL=CIM
; hívás vége
; E=PEEK(CIM)

; D=PEEK(CIM+1)
; HL=DPEEK(CIM)
; funkcióhívás indul
; BASIC 02: HL -> MREG
; BASIC 05: előjel?
; hívás vége;
; kész, ha az eredmény pizitív
; funkcióhívás indul
; BASIC 92,03: MREG2=32768
; BASIC 15: MREG3=32768
; BASIC 0C: MREG2=65536
; BASIC 0C: MREG=65536+(-ADAT)
; hívás vége
L1 RET  

A kifejezést kiértékelő VAL
Az értelmező meglehetősen könnyedén veszi ezt a funkciót. Igaz, hogy a legtöbb személyi számítógép hasonló módon értelmezi, és csak a számjegyeket képes értékként visszaadni. Az első műveleti jelnél vagy változónévnél leáll az elemzés. Például:

VAL("2*PI") = 2

Csak aki a ZX-SPECTRUM kiértékelő VAL-ját ismeri (ott VAL("2*PI") = 6.28.), az tudja, milyen sokat veszített ezzel a hiányossággal. INPUT-ban megadható matematikai formák, a program menetétől függően füzérként összefűzhető vagy szeletelhető függvények mind olyan lehetőségek, amelyeket csak egy tényleges VAL-funkcióval érhetünk el.
A feladat nehézsége abban áll, hogy a kiértékelés csak tokenizált szövegen történhet. A megoldás algoritmusa a következő lehet:

A szokásos kiértékelés az első elem vizsgálata után következhet. Az eredmény a kifejezés numerikus értéke a BASIC veremben.
Ezzel a feladatot lényegében megoldottuk. A gyakorlatban nem szabad megfeledkezni az elrontott pointerek értékének visszaállításáról, a BASIC verem rendezéséről sem:

VAL2 RST 10H
DEFB 9CH
DEFB 00H
LD HL,(0214H)
PUSH HL
LD HL,(0203H)
PUSH HL
LD HL,(0228H)
INC HL
LD C,(HL)
INC HL
LD B,00H
LD DE,0249H
PUSH DE
LDIR
XOR A
LD (DE),A
RST 10H
DEFB 16H
DEFB 00H
POP HL
LD (0214H),HL
LD A,05H
OUT (0B3H),A
CALL 0C41DH
LD A,01H
OUT (0B3H),A
RST 10H
DEFB 20H
DEFB 23H
DEFB 00H
POP HL
LD (0203H),HL
POP HL
LD (0214H),HL
RET
; funkcióhívás indul
; BASIC 9C: füzér kiértékelése
; hívás vége
; pointer aktuális érték
; verembe
; változó aktuális érték
; verembe
; MREG kezdőcím
; hosszra mutat
; C = a füzér hossza
; HL: az első karakterre mutat
; BC = karakterek száma
; DE: puffercím
; verembe
; pufferbe másolja a füzért
; A = 0
; a végére lezáró 0-át ír
; funkcióhívás indul
; BASIC 16: BASIC verem törlése
; hívás vége
; puffercím
; elemzési pointerként
; BASIC szegmens
; a 3. LAP-ra
; tokenizálás
; 1. szegmens
; vissza
; funkcióhívás indul
; BASIC 20: az első sorelem vizsg.
; BASIC 23: kiértékelés
; hívás vége
; változó belépési érték
; visszaállítása
; pointer belépési érték
; vissza

A rutint VAL2-ként fűzzük a rendszerhez, hogy szükség esetére megmaradjon az eredeti VAL is.

VIDEO cím lekérdezése
A VIDEO kezelőhöz megnyitott csatornák közvetlen elérésének alapja a képet tároló terület kezdő címe (0000-FFFF közötti videocím). Ennek lekérdezését az EXOS lehetővé teszi egy speciális funkcióhívással. Bővítsük a BASIC rendszert egy

V_CIM(N)

függvénnyel, amely az N. csatorna elsődleges videocímét adja vissza.
A feladat megoldása néhány lépés:

V_CIM RST 10H
DEFB 99H
DEFB 0BH
DEFB 00H
LD A,L
LD B,03H
EXOS 0BH
RST 18H
PUSH BC
POP HL
...
; funkcióhívás indul
; BASIC 99: kiértékelés
; BASIC 0B: H = csatornaszám
; hívás vége
; A: csatornaszám
; B: alfunkciószám: ADDR
; speciális funkció (tár. cím)
; hibaellenőrzés
; elsődleges tárolási cím verembe
; HL = V_CIM
; értéktranszformáció, ha negatív

PEEK a videó RAM-ból
Az előző pont VPOKE utasításának párjaként készítsünk egy

VPEEK(CIM)

függvénybővítést, amely a 0000-FFFF közötti videocím alapján megkeresi a címhez rendelt szegmenst és abból olvassa ki az adatot.
A videocím kezelés algoritmusát láttuk a VPOKE utasításnál. Ezzel a VPEEK kiértékelése egyszerűen megoldható:

VPEEK RST 10H
DEFB 99H
DEFB 0BH
DEFB 00H
LD A,H
AND 0C0H
RLCA
RLCA
ADD 0FCH
LD C,A
IN A,(0B1H)
PUSH AF
LD A,C
OUT (0B1H),A
RES 7,H
SET 6,H
LD L,(HL)
LD H,00H
POP AF
OUT (0B1H),A
RST 10H
DEFB 02H
DEFB 00H
RET
; funkcióhívás indul
; BASIC 99: kiértékelés
; BASIC 0B: HL = CIM
; hívás vége
; felső byte
; 1100 0000 maszkolás
; érték eltolás
; 00 és 03 közé
; 0. videoszegmens + érték
; mentés
; pillanatnyi 1. LAP-szegmens
; verembe
; video szegmens
; az 1. LAP-ra
; cím transzformálás
; az 1. LAP tartományába
; PEEK
; HL = VPEEK(CIM)
; eredeti 1. LAP-szegmens
; visszalapozása
; funkcióhívás indul
; BASIC 02: ADAT -> MREG
; hívás vége

Ezek után rátérünk a példaként kidolgozott bővítések elhelyezésére és beláncolására. Hangsúlyozzuk, hogy a következő megoldás (az adott címekkel) csak az előző pont szerint eltolt BASIC munkaterület esetén lehetséges! A betöltőprogram mind sorszámaiban, mind tárolási területeiben illeszkedik a 4.2.1 pont utasításbővítéseihez, azokkal egy időben beírható, használható (így válik kerekké pl. a V_CIM, VPEEK, VPOKE bővítéssor).
A program sorban betölti (az áttekinthetőség érdekében hexadecimálisan kerek címekre) a függvénybővítések végrehajtó rutinjait, majd paraméterblokkjukat. Ez utóbbiak tartalmazzák a neveket is, amelyeket tetszés szerint meg is változtathatunk (ügyelve a név előtti hosszbájtra). Befejezésként a program M gépi rutinja (HL-ben a paraméter blokkok címével) ismételten meghívja a beláncoló BASIC 80H funkciót.

1000 POKE 540,0
1010 POKE 541,22
1020 CODE DPEEK.=HEX$("D7,99,0B,00,5E,23,56,EB,D7,02,05,00, 28,07,D7,92,03,15,0C,0C,00,C9")
1030 POKE 540,32
1040 CODE =HEX$("00,00,0C,05")&"DPEEK"&HEX$("00,16,01")
1050 POKE 540,48
1060 CODE VAL2.=HEX$("D7,9C,00,2A,14,02,E5,2A,03,02,E5,2A,28, 02,23,4E,23,06,00,11,49,02,D5,ED,B0,AF,12,D7,16,00,E1,22,14, 02,3E,05,D3,B3,CD,1D,C4,3E,01, D3,B3,D7,20,23,00,E1,22,03,02,E1,22,14,02,C9")
1070 POKE 540,128
1080 CODE =HEX$("00,00,0C,04")&"VAL2"&HEX$("30,16,01")
1090 POKE 540,144
1100 CODE V_CIM.=HEX$("D7,99,0B,00,7D,06,03,F7,0B,DF,C5,E1, D7,02,05,00,28,07,D7,92,03,15,0C,0C,00,C9")
1110 POKE 540,176
1120 CODE =HEX$("00,00,0C,05")&"V_CIM"&HEX$("90,16,01")
1130 POKE 540,192
1140 CODE VPEEK.=HEX$("D7,99,0B,00,7C,E6,C0,07,07,C6,FC,4F, DB,B1,F5,79,D3,B1,CB,BC,CB,F4,6E,26,00,F1, D3,B1,D7,02,00,C9")
1150 POKE 540,240
1160 CODE =HEX$("00,00,0C,05")&"VPEEK"&HEX$("C0,16,01")
1170 CODE M=HEX$("21,20,16,D7,80,00")
1180 CODE =HEX$("21,80,16,D7,80,00")
1190 CODE =HEX$("21,B0,16,D7,80,00")
1200 CODE =HEX$("21,F0,16,D7,80,00,C9")
1210 CALL USR(M,0)

A futtatás után használható a négy új függvény. Például GRAPHICS után

CIM=V_CIM(101)

a nagyfelbontású kép bal felső pontjának tárolási címe. A pont (ill. az azt tartalmazó bájt) lekérdezhető az X=VPEEK(CIM), írható a VPOKE CIM,X utasítással.
A listában látható paraméterblokkok (1040., 1080., 1120. és 1160. sorok) az első két bájtra (pointer) 0-t adnak meg. Ezt a két bájtot majd az értelmező tölti ki, létrehozva a folyamatos változóláncot (a beláncoláskor). Ezután viszont nem szabad újraindítani a programot, hiszen az újra nullázná a most már érvényes pointereket, megszakítva ezzel a kialakított láncokat.

Végül még egy megjegyzés. A bemutatott példákat a kötet szándékának megfelelően (minél kevesebb eszköz, minél egyszerűbb és gyorsabb beírás) BASIC-ből töltöttük és illesztettük a rendszerbe. A rutinokat, táblákat természetesen egy assembler program (pl. ASMON) segítségével is elkészíthetjük (ezt szolgálja a rutinok assembly listáinak megadása is) és egy betöltő, beláncoló rutinnal kiegészítve teljes bővítőprogramot nyerhetünk. Ha ezt az assembler lehetővé teszi, a lefordított programot felhasználói áthelyezhető modulként rögzíthetjük kazettán.
Az ilyen modult a BASIC a munkaterülete elé szúrja be (tehát hasonló területre, mint ahol mi tároltuk rutinjainkat).

4.3 RAM-értelmező

Mint a bevezetőben említettük, a rendszer inicilizálási logikája lehetőséget ad arra, hogy magát az értelmezőt (német gépek esetében az 5. szegmens tartalmát) is RAM-ba helyezzük, és ezt illesszük be a rendszerbe. Aki kísérletezni, játszani szeretne a lehetőségekkel, ennél többet nem is kaphatna a géptől.
Az áttöltéshez kérjünk egy szegmenst az EXOS-tól (méghozzá megtévesztve a rendszert, perifériaszegmensként kérjük, hogy a kiutalás igazán tartós maradjon). A kapott szegmensre töltsük át az 5. szegmens tartalmát és az új BASIC szegmens számát írjuk be a ROM-táblába, az 5. szegmens nyilvántartási helyére. Ez a mi verziónknál az ABC5H cím (l. 2.3 alfejezet):

LD A,0FFH
OUT (0B2H),A
LD HL,0BF79H
INC (HL)
PUSH HL
EXOS 18H
POP HL
DEC (HL)
LD A,C
LD (0ABC5H),A

OUT (0B1H),A
LD A,05H
OUT (0B2H),A
LD HL,8000H
LD DE,4000H
LD BC,4000H
LDIR
RET
; rendszerszegmens
; a 2. LAP-ra
; "periféria" jelzőbyte
; beállítása

; szegmens kérés
; jelzőbyte címe
; törlés
; A: a kapott szegmens száma
; beírja a ROM-táblába
; az 5. szegmes helyére
; kapott szegmens az 1. LAP-ra
; BASIC szegmens
; a 2. LAP-ra
; forrás: BASIC szegmens
; cél: kiutalt szegmens
; hossz: 16384 byte
; átmásolás a RAM-ba

Ugyanez BASIC-ből betöltve:

100 ALLOCATE 100
110 CODE M=HEX$("3E,FF,D3,B2,21,79,BF,34,E5,F7 ,18,E1,35,79,32,C5,AB,D3,B1,3E,05,D3, B2,21,00,80,11,00,40,01,00,40,ED,B0,C9")
120 CALL USR(M,0)
130 EXT "basic"

A program az áttöltés után BASIC inicializálást kér, azaz - a most már átírt ROM-tábla alapján - újraépül a BASIC rendszer. A BASIC funkcióhívások, a kulcsszórutinok mind-mind az új RAM-szegmenst használják, ami rendkívüli lehetőséget ad a változtatásokra, bővítésekre.
Hogy egy-két tippet is adjunk az első próbálkozásokhoz, írjuk át pl. az állapotsor üzenetszövegét tetszésünk szerint:

100 OUT 177,PEEK(197)
110 POKE 540,10
120 POKE 541,67
130 CODE ="RAM-BASIC"

Most már akár a szövegszerkesztőből való visszatéréskor is a mi kiírásunk jelenik meg az állapotsorban (de akár le is tilthatjuk ezt a funkciót a beolvasási főágban).
Most már közvetlenül is manipulálható a kulcsszavak paramétertáblája. Átírható pl. a LIST típusbájtja, amely megakadályozta az utasítás programbeli alkalmazását:

SPOKE PEEK(197),63357,67

Nagyobb változtatásoknál az egész értelmező elkészíthető (és háttértárolóra vihető) abszolút (6-os típusú) rendszerbővítőként is. Így egy a beolvasáskor automatikusan beláncolódó saját BASIC-et alakíthatunk ki.
A további lehetőségek az Olvasó fantáziáján, vállalkozókedvén múlnak. Jó ötleteket és kevés "elszállt" programot kívánunk a kísérletezéshez!

Függelék

6. A BASIC kulcsszavak paramétertáblázat

D
H
1. cím
2. cím
tp
kulcsszó
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
00:
01:
02:
03:
04:
05:
06:
07:
08:
09:
0A:
0B:
0C:
0D:
0E:
0F:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
1A:
1B:
1C:
1D:
1E:
1F:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
2A:
2B:
2C:
2D:
2E:
2F:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
3A:
3B:
3C:
3D:
3E:
3F:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
4A:
4B:
4C:
4D:
4E:
4F:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
5A:
5B:
5C:
F8 CC
06 CE
23 C8
34 CE
53 CE
AA DC
5C CE
25 D4
E6 CF
F0 CF
4E CD
22 D0
9C CA
9C CA
E7 D9
3D C8
04 EB
37 FC
B9 DC
AC CE
97 C7
CD D9
CD D9
AD CF
5F DB
5B DB
9C CA
9C CA
A5 DE
8C FE
E1 D0
60 DD
B5 D1
DF D1
8F FC
E7 D9
9C CA
4D D2
B6 D9
12 D6
80 D2
16 D6
2E C8
B5 CA
0F DD
8D CB
A7 CC
BC DD
6A EB
21 D3
93 D3
B4 F4
B3 D3
82 FD
C6 D3
E5 D3
08 D4
9C CA
C6 D4
0F D6
EE D7
9C CA
27 C8
06 D8
24 D8
2B D8
7E CE
FC C9
16 DC
54 D8
28 FF
78 CE
44 D9
5B D8
3A EB
08 FC
52 D9
6C D9
FE FB
D7 CB
48 DE
9C CA
2B C8
05 D4
3E D8
38 D1
29 D1
CF D2
BF D3
7B D0
77 D0
9F D9
9F D1
61 D0
61 D0
9C CA
61 D0
61 D0
61 D0
61 D0
44 D0
61 D0
61 D0
61 D0
61 D0
9C CA
7F D0
9C CA
9C CA
61 D0
44 D0
61 D0
61 D0
9C CA
A3 D9
61 D0
A6 D0
9C CA
9C CA
9C CA
9C CA
9C CA
44 D0
61 D0
61 D0
5A D0
5A D0
61 D0
61 D0
9C CA
13 D2
9C CA
F6 D4
67 D2
FD D4
9C CA
9C CA
61 D0
9C CA
9C CA
61 D0
61 D0
9C CA
61 D0
9C CA
61 D0
44 D0
61 D0
61 D0
01 D4
61 D0
61 D0
F0 D4
61 D0
9C CA
9C CA
F6 D7
61 D0
61 D0
F6 D7
9C CA
61 D0
44 D0
44 D0
9C CA
61 D0
61 D0
61 D0
61 D0
61 D0
44 D0
9C CA
9C CA
61 D0
9C CA
9C CA
01 D4
61 D0
44 D0
9C CA
9C CA
9C CA
9C CA
9C CA
9C CA
7C D1
53 08
53 03
41 04
53 04
53 07
4E 04
53 05
53 05
53 05
53 04
43 08
53 04
02 04
42 03
4A 03
61 06
42 03
53 07
4A 02
53 05
41 04
4E 04
4E 07
42 03
46 07
46 0B
46 06
46 0A
46 08
53 08
52 04
4A 03
52 05
52 04
53 08
4A 07
02 05
42 02
4A 02
52 05
53 03
52 04
41 04
61 04
46 04
61 05
61 03
46 04
42 07
53 04
43 06
41 02
53 03
53 04
53 04
53 05
53 05
42 07
52 09
52 04
53 08
03 03
41 08
52 07
42 05
52 06
53 03
61 04
4A 06
53 03
53 05
61 05
52 04
51 04
42 06
53 04
53 06
53 05
53 04
61 06
4A 04
13 01
41 05
53 06
53 03
53 03
53 05
43 04
53 04
53 04
53 04
53 04
42 02
ALLOCATE
ASK
AUTO
CALL
CAPTURE
CASE
CAUSE
CLEAR
CLOSE
CODE
CONTINUE
COPY
DATA
DEF
DEF
DELETE
DIM
DISPLAY
DO
CHAIN
EDIT
ELSE
ELSE IF
END
END DEF
END HANDLER
END IF
END SELECT
END WHEN
ENVELOPE
EXIT
FOR
GOSUB
GOTO
GRAPHICS
HANDLER
IMAGE
IF
IF
INPUT
LET
LINE
LIST
LOAD
LOOP
MERGE
NEW
NEXT
NUMERIC
OPEN
OPTION
OK
OUT
PLOT
POKE
SPOKE
PRINT
PROGRAM
RANDOMIZE
READ
REDIRECT
REM
RENUMBER
RESTORE
RETRY
RETURN
RUN
SAVE
SELECT
SET
SOUND
START
STOP
INFO
STRING
TEXT
TOGGLE
TRACE
TYPE
VERIFY
WHEN
!
LLIST
LPRINT
EXT
GET
FLUSH
LOOK
PING
DATE
TIME
WAIT
ON

7. A beépített BASIC függvények és változók táblaterülete

cím
ptr
tp
név
rutin
szg.
0F7E
0F88
0F93
0F9F
0FAA
0FB4
0FBE
0FCA
0FD5
0FE0
0FEB
0FF5
1000
100A
1014
101F
102B
1035
103F
104C
1056
1066
1073
107E
1087
1093
109E
10A7
10B4
10BE
10C8
10D1
10DB
10E8
10F5
10FF
1109
1114
1120
112D
113B
1145
1152
115C
1166
1170
117B
1187
1190
119A
11A4
11AE
11B8
11C2
11CC
11D8
11E5
11EF
11F9
1204
120F
1219
1224
122E
1238
1243
124F
125E
126B
1278
1282
128C
1297
14A4
12B0
12BD
00 00
00 00
00 00
00 00
00 00
00 00
00 00
00 00
00 00
00 00
00 00
00 00
00 00
00 00
00 00
00 00
00 00
93 0F
14 10
00 00
88 0F
BE 0F
4C 10
2B 10
00 00
00 00
7E 10
B4 0F
00 00
00 00
93 10
00 00
9F 0F
A7 10
73 10
0A 10
AA 0F
1F 10
00 00
F5 10
D1 10
56 10
FF 10
3B 11
9E 10
00 00
09 11
52 11
CA 0F
66 10
00 10
9A 11
20 11
D5 0F
7E 0F
A4 11
B8 11
C2 11
00 00
AE 11
90 11
45 11
00 00
66 11
3F 10
E8 10
CB 10
00 00
87 10
00 00
70 11
35 10
CC 11
B4 10
00 00
F9 11
0C 03
0C 04
0C 05
0C 04
0C 03
0C 03
0C 05
0C 04
0C 04
0D 04
0C 03
0C 04
0C 03
0C 03
0C 04
0D 05
0C 03
0C 03
0C 06
0C 03
0D 09
0C 06
0C 04
0C 02
0C 05
0D 04
0C 02
0D 06
0C 03
0C 03
0C 02
0C 03
0C 06
0D 06
0C 03
0C 03
0C 04
0C 05
0D 06
0C 07
0C 03
0C 06
0C 03
0C 03
0C 03
0C 04
0C 05
0C 02
0C 03
0C 03
0C 03
0C 03
0C 03
0C 03
0C 05
0D 06
0C 03
0C 03
0C 04
0C 04
0C 03
0D 04
0C 03
0C 03
0C 04
0D 05
0C 0B
0C 06
0D 06
0C 03
0C 03
0D 04
0C 06
0C 05
0C 06
0D 05
ABS
ACOS
ANGLE
ASIN
ATN
BIN
BLACK
BLUE
CEIL
CHR$
COS
COSH
COT
CSC
CYAN
DATE$
DEG
EPS
EXLINE
EXP
EXSTRING$
EXTYPE
FREE
FP
GREEN
HEX$
IN
INKEY$
INF
INT
IP
JOY
LBOUND
LCASE$
LEN
LOG
LOG2
LOG10
LTRIM$
MAGENTA
MAX
MAXLEN
MIN
MOD
ORD
PEEK
SPEEK
PI
POS
RAD
RED
REM
RGB
RND
ROUND
RTRIM$
SEC
SIN
SINH
SIZE
SGN
STR$
SQR
TAN
TANH
TIME$
TRUNCATE
UBOUND
UCASE$
USR
VAL
VER$
VERNUM
WHITE
YELLOW
WORD$
21 C3
26 C3
30 C3
62 C3
26 C4
2C C4
FA C4
00 C5
5D C4
A2 C4
14 C5
1F C5
2A C5
8B CA
09 C5
43 C5
7F C5
91 C5
D4 C5
ED C5
B2 C5
E6 C5
8D C6
7B C6
03 C5
EF C6
46 C7
58 C7
88 C6
75 C7
1B C8
31 C8
4C C8
28 CD
7F C8
9B C8
92 C8
A4 C8
12 C9
0C C5
2D C9
42 C9
6F C9
84 C9
B5 C9
CE C9
E7 C9
0B CA
10 CA
79 CA
FD C4
96 CA
BB C4
A9 CA
F0 CA
54 CB
7C CB
82 CB
EB CB
0A CC
F6 CB
30 CC
4B CC
9B CC
AB CC
30 C5
BB CC
E8 CC
25 CD
3F CD
4E CD
68 CD
C1 CD
0F C5
06 C5
CE CD
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01
01

Vissza