Gépi Kódú Programozás
Tartalom |
|
1. A gépi kódú programozás alapjai 2. A háttérrendszer |
3. A BASIC rendszer 4. Bővítési, kiterjesztési lehetőségek Függelék |
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.
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 C2ACH21 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.
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:
|
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. |
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:
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
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.
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.
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).
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:
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.
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.
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).
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:
|
|||||||||||||||||||||
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:
|
|||||||||||||||||||||
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:
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.
|
|||||||||||||||||||||
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
|
|||||||||||||||||||||
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)
rendszerváltozóba tölti. Ezenkívül
|
|||||||||||||||||||||
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:
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. |
|||||||||||||||||||||
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:
|
|||||||||||||||||||||
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
|
|||||||||||||||||||||
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
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:
|
|||||||||||||||||||||
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). | |||||||||||||||||||||
|
|||||||||||||||||||||
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:
|
|||||||||||||||||||||
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:
|
|||||||||||||||||||||
BASIC B6H (1/C1F9) | |||||||||||||||||||||
Megkeresi a RAM-térképen a C. program bejegyzését. Visszatéréskor a státusz:
|
|||||||||||||||||||||
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.
|
|||||||||||||||||||||
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:
|
|||||||||||||||||||||
BASIC BBH (1/D546) | |||||||||||||||||||||
Az AUTO parancs végrehajtó rutinja. Beolvassa a programszövegben esetleg következő paramétereket és
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:
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.
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.
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:
- PRINT: BASIC kulcsszó,
- 12: numerikus konstans
LET SZAM=SIN(PI)+0,5
Itt már több elemet láthatunk:
- LET: BASIC kulcsszó,
- SZAM: numerikus változó,
- "=": jel,
- SIN: belső függvény,
- "(": jel,
- PI: belső változó,
- ")": jel,
- "+": jel,
- 0,5: numerikus konstans.
LET A$="NEV"
- LET: BASIC kulcsszó,
- A$: füzér változó,
- "=": jel,
- "NEV": füzér "konstans"
GOTO 100
- GOTO: BASIC kulcsszó,
- 100: olyan numerikus konstans, amely BASIC sorszámot jelöl
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
- 1.: típusjelző: 00H + a jel kódja. A jel kódja a 21H-2FH ASCII kód tartományban egyszerűen az ASCII kód -20H, általában pedig a következő:
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):
- 1.: típusjelző: 20H + a változónév karaktereinek száma (1-31)
- 2-(n+1).: a név karakterei (nagybetűsítve)
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):
- 1.: típusjelző: 40H + a név karaktereinek szám (max. 31)
- 2-(n+1).: a név karakterei (nagybetűsítve)
4. BASIC kulcsszó (60H típuskód)
Elem hossza: 2 bájt:
- 1.: típusjelző: 60H
- 2.: token: a kulcsszó kódja, tulajdonképpen a kulcsszótáblabeli sorszáma (l. pl. 6. Függelék)
5. Füzérkonstans (80H típuskód)
Elem hossza: 2 + a füzér karaktereinek száma:
- 1.: típusjelző: 80H
- 2.: hossz: a szöveg karaktercinek száma
- 3-(n+2).: a karakterek (idézőjelek nélkül)
6. BASIC sorszám (A0H típuskód)
Elem hossza: 3 bájt:
- 1.: típusjelző: A2H (A0H+2)
- 2.: LO: a sorszám alsó bájtja
- 3.: HI: a sorszám felső bájtja
7. Numerikus konstans (C0H típuskód)
Elem hossza 1+2 vagy 1+5 bájt:
- 1.: típusbájt:
- C2H, ha a konstans egész típusú (0 és 9999 között)
- C6H, ha a konstans lebegőpontos szám
- Egész típus esetén a további bájtok:
- 2.:LO: a konstans alsó bájtja
- 3.:HI: a konstans felső bájtja
- Lebegőpontos konstansnál:
- 2-6.: a konstans számjegyei BCD alakban
- 7.: a szokásos kitevőbájt
Meg kell említeni, hogy a negatív előjelet külön jelként tárolja az értelmező (0DH típusjelző) és a következő konstanst pozitív értékként írja a kódolt szövegbe.
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):
Az értelmező a sorszámmal kapcsolatos bevezető elemzések után megvizsgálja a sor első elemének típusát (C5A0H). Hibát vált ki, ha ez nem betűvel kezdődő karaktersorozat (formailag változónév, valójában kulcsszó).
Összehasonlítja az első szót a BASIC kulcsszavakkal (EF48H). Ennek alapja a kulcsszótábla (3.2.5 pont), amelynek pointerei a kulcsszavak alakját is tartalmazó paraméter blokkokra mutatnak. Azonosítás esetén kiolvassa a kulcsszó típusbájtját (B). C-ben az azonosított kulcsszó sorszáma (tokenje) van. Ha a tábla végéig nem találta meg a nevet, a LET tokenjét (28H) és típusbájtját állítja be.
Ellenőrzi (BIT 1,B), hogy az adott kulcsszó alkalmazható-e programban. A karaktersorozat helyére a kétbájtos kulcsszóelemet írja a sorba (ehhez szükség szerint csúsztatja el a hátralévő szövegrészt). Eddig minden sornál azonos a feldolgozás. A továbblépések viszont már erősen függenek az azonosított kulcsszótól.
A típusbájt alapján megvizsgálja, hogy kell-e tokenizálni a sor kulcsszót követő szöveget (BIT 6,B). Ha igen, akkor ezt megteszi (C41DH), azaz a szöveg elemei helyett azok kódolt alakját írja a pufferbe.
A következő lépés az általános tokenizálási eljárás utáni speciális kódolásra vagy a kulcsszónak megfelelő szintaktikai elemzésre ad lehetőséget. Az értelmező ui. meghívja (EFE0H) a kulcsszó 2. rutinját. A BASIC utasítások jelentős részénél ez nem lát el különösebb funkciót, néhány esetben azonban fontos feladata van. A RUN, GOTO és GOSUB 2. rutinja pl. az utasítást követő sorszám típusát ellenőrzi és megfelelő érték esetén (egész típus) a már tokenizált szövegben kijavítja, a típusjelző bájtot C2H-ról A2H-re (íme itt a válasz a felmerült kérdésre!).
Hasonlóan fontos a 2. rutin olyankor is, amikor formailag azonos nevekhez más tokeneket, végrehajtó rutinokat akar rendelni az értelmező (DEF, IF, END...). Például a DEF esetén a 2. rutin megvizsgálja, hogy egysoros, END nélküli definícióról van-e szó (ekkor meghagyja a tokenizáláskor beírt kódot) vagy DEF. ..END blokkról (ekkor a 0DH DEF-tokent kicseréli 0EH-ra). Az utasítások másik csoportjánál a rutin végigszalad a hátralévő részen és egy durva szintaktikai elemzést végez (pl. zárójelek), de ez már nem hívja meg pl. a függvények kiértékelő rutinjait, így azok szintaktikai szabályai a beíráskor nem érvényesülhetnek.
Ezután az értelmező, ha a vizsgált kulcsszó megenged több utasítást a sorban (BIT 4,B) és ":" következik saját rekurzív hívásával folytatja a sorelemzést. Egyébként viszont a kulcsszóhoz tartozó szöveg után sorvéget vár.
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
- 60 - token következik
- 38 - PRINT token
- C2 - numerikus konstans következik (egész)
- 0C - LO=12 dec
- 00 - HI=0 (konstans = 12+256*12=12)
- 00 - vége
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-1LET A$="NEV"
60 28 42 41 24 13 80 03 4E 45 56 00 LET 2: A $ = 3: N E VGOTO 100
60 21 A2 64 00 00 GOTO 2: LO HI
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.
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:
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
L1RST 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ő:
Mindezt a következő assembly program valósítja meg:
VP
L1RST 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?
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).
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
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 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 |