Múzeum - Enterprise könyvek - Gépi Kódú Programozás


Tartalom

Bevezetés

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

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

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

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

Függelék
1. Karakterek és Z80-as utasítások
2. A Z80-as utasítások kódjai
3. Az EXOS memóriatérképe és rendszerváltozói
4. EXOS hibakódok és üzenetek
5. A perifériakezelők belépési pontjai
6. A BASIC kulcsszavak paramétertáblázata
7. A beépített BASIC függvények és változók táblaterülete

8. A képernyőkezelés fontosabb adatai
9. Az ASMON (SIMON) program használata


Bevezetés

Minden személyi számítógéppel ismerkedő, a gépet kreatívan használni akaró alkalmazó eljut - előbb vagy utóbb - arra a szintre, hogy érezni kezdi a BASIC programozási rendszer korlátait. Akár az elérhető működési sebességet érzi túl kicsinek, akár olyan különleges hatások keltik fel a figyelmét, amelyekkel gyári programokban, esetleg más gépeken találkozott - a felhasználók jelentős része érdeklődni kezd a gépi kódú programozás iránt.
Így van ez az ENTERPRISE esetén is. Ki kell emelni azonban, hogy nemigen találkozhattunk még a home computer (házi számítógép) kategóriájú, nálunk is elterjedt gépek között ennyire komfortos, a látványos, dinamikus programozást ilyen mértékben segítő BASIC értelmezővel.
Mire számíthat az, aki rászánja magát erre az útra, a gép mikroprocesszorának közvetlen utasításokkal való vezérlésére, programozására? Először bizony meglehetősen sok buktatóra, nem éppen kellemes meglepetésre. Az a baki, amit a BASIC rendszer ilyen-olyan (pl. szintaktikai) hibának minősített, a gépi kódú program futtatása során sajnos gyakran a rendszer összeomlásához vezethet, esetleg lefagy a gép (olyan végtelen ciklusba kerül, amiből csak a kikapcsolás menti ki). A gépi kódú programokat tehát sokkal fegyelmezettebben, körültekintőbben kell megírni, a tárban elhelyezni, majd futtatni, mint pl. a BASIC programokat. De a haszna is megvan a többletenergia ráfordításának. Ha csodákat nem is várhatunk a programok gépi szinten való megírásától (hiszen adott á fizikai háttér, az ún. hardver: a processzor működési sebessége, címzési kapacitása, a képmegjelenítés technikája stb.), azt mindenképpen mondhatjuk, hogy így sokkal hatékonyabb programokat tudunk készíteni.
Mit jelent ez a hatékonyság? Három fő területet lehet kiemelni:

  1. A gépi kódú program jóval tömörebb, mint a BASIC. Egy-két kilobájtnyi gépi programmal már egészen összetett feladatokat lehet megoldani.
  2. A futási sebesség egyes területeken nagyságrendekkel nagyobb, mint a hasonló feladatot megoldó BASIC program esetén. Van persze más magas szintű nyelv is (pl. a PASCAL), ami egyesíti a programozási komfortot és a nagy működési sebességet, de a mikroprocesszor közvetlen programozásával a lehető leggyorsabb programfutás érhető el.
  3. A közvetlen programozással semmiféle olyan szoftverkorlátozást nem kötelező figyelembe vennünk, amit a rendszer tervezői - pl. az egységes kezelés érdekében - szabtak. Hogy egy példát mondjunk erre: a képmegjelenítést végző - hallatlanul sokoldalú - NICK-chip lehetővé tenne akár pont-soronként különböző video üzemmódokat (szöveges vagy grafikus módok, színválaszték stb.), de az operációs rendszer beépített VIDEO kezelője csak karaktersoronként engedélyez módváltást. Ez is sokkal több a COMMODORE vagy ZX gépek lehetőségeinél. Mindenesetre igen vonzó lehetőség, hogy gépi kódú programozással elérhetjük a fizikai lehetőségek elvi határait. Mindezeken kívül vannak a szoftvernek olyan területei, amelyek elvileg is elérhetetlenek magas szintű nyelvekből (ilyen pl. a megszakítási rendszer), amelyek programozása csakis gépi kódban képzelhető el.

A gépi szintű programozáshoz rendelkezésre álló szakirodalom meglehetősen felemás. Míg bőven találunk a Z80-as mikroprocesszor utasításkészletével, programozásával foglalkozó könyveket, cikkeket, addig ezek ENTERPRISE gépen való alkalmazásával alig foglalkozik kiadvány. Nagyon sokat segített ezen a helyzeten a számítógép operációs rendszerének (EXOS 2.1-es verzió) leírása, amely nagyon sok hasznos információi nyújt az operációs rendszer felhasználásával kapcsolatban. Példákat azonban nem közöl, így a gépével most ismerkedő Olvasó számára viszonylag nehezen követhető az igényes, magas színvonalú leírás. Sokaknak hiányzik - tapasztalataink szerint - a gép működési logikájának leírása is, annak követése, hogy mi miért és hogyan történik a gépben. Nem foglalkozik a leírás a BASIC értelmezővel sem (hiszen ez már felhasználói program), pedig ennek bővítése, rutinjainak felhasználása ugyancsak hálás területe a gépi kódú programozásnak.
Mindezek alapján érthető, hogy könyvünket ezen hiányok pótlására, az érezhető igények kielégítésére szántuk. Mivel feltevésünk szerint az országban elterjedt többezer gép tulajdonosa - a BASIC-et már többé-kevésbé ismerő, de a gépi kódú programozással most ismerkedő fiatal - veszi elsősorban kezébe a könyvet, az 1. fejezetben röviden összefoglaljuk a gépi kódú programozáshoz szükséges alapismereteket. Ezután végigkövetjük előbb á háttérrendszer - az EXOS - majd a BASIC rendszer működését, felhasználását (sok példával, végig az alkalmazást, saját elképzeléseink megvalósítását, rutinjaink beillesztését tartva szem előtt).
A kényszerűség néha pozitív mellékhatásokkal is jár. A szakirodalom hiánya arra ösztönzi a felhasználókat, hogy kísérletezve, próbálkozva fedezze fel gépe tulajdonságait. Azt tanácsoljuk az Olvasónak, hogy kövesse azt az utat, amit - többek között - mi is követni kényszerültünk. Kísérletezzen, próbálja ki a példákat, a változtatási lehetőségeket, azok hatásait. Ez az út talán egy kicsit időigényes, nem zárja ki a tévedés lehetőségét sem, de minden bizonnyal a gép alapos megismeréséhez vezet.

1. A gépi kódú programozás alapjai

Mint a bevezetőben írtuk, az ENTERPRISE személyi számítógépek mikroprocesszoráról - a Z80-asról -, annak programozásáról igen sok színvonalas szakkönyv jelent meg. Ezen a területen tehát nincs szükség hiánypótlásra. A következő összefoglalással inkább azt szeretnénk biztosítani, hogy összeállításunk önmagában is használható legyen, akár a gépével, a számítástechnika e területével most ismerkedd felhasználó számára is. Aki tehát jól ismeri a Z80-as utasításait, alkalmazását, át is lapozhatja ezt a fejezetet - legalábbis első nekifutásra.
A gépi kódú programozás iránt kétségtelenül sokakban meglevő idegenkedést - tapasztalataink szerint - az alapfogalmak tisztázatlansága is erősíti. Mindeneke1őtt foglaljuk össze a gépi szintű programozás alapvető fogalmait, kifejezéseit.

1.1 Alapfogalmak
A számítógép működése során minden utasítást, adatot a különböző memóriák tárolnak. A memóriák elemi egységei, cellái egyetlen információt, egy bitet képesek megőrizni, aminek értéke 0 vagy 1 lehet. Valójában két jól megkülönböztethető fizikai állapotról van szó (pl. nincs feszültség van feszültség), amit a 0 és 1 kódokkal jelölünk. A memória törlése azt jelenti, hogy 0 állapotba hozzuk, beírása pedig az 1 állapotba billentést jelenti. Az olyan memóriát, amelynek rekeszeibe tetszés szerint írhatunk 0 vagy 1 értéket, RAM-nak nevezzük (Random Access Memory - tetszés szerint elérhető, azaz írható, olvasható memória). Vannak olyan memóriák is, amelyek celláinak állapotát gyárilag beégették, ezek a ROM-ok (Read Only Memory - csak olvasható memóriák). Ezek alkalmasak a gépet működtető alapprogramok, belső adatok (pl. PI) stb. tárolására.
A memória bitjei - a Z80-as fizikai sajátosságainak megfelelően - 8-as csoportokba, ún. bájtokba (gyakori a byte írásmód is) vannak rendezve. Egyetlen utasítással egy ilyen 8-bites blokk olvasható vagy írható. Egy bájt értéke már természetesen sokkal többféle lehet: az alkotó bitek értékétől függően 00000000-tól 11111111-ig 256 különféle értéket vehet fel. Jól látható, hogy a bájtok megadására, kezelésére kínálkozó egyik segédeszköz a kettes számrendszer. A bájtok által képviselt bináris (kettes számrendszerbeli) érték meghatározásánál a legutolsó bitet tekintjük a legalacsonyabb helyi értékűnek (ez a 0. bit), és előre haladva az egyes bitek egyre magasabb helyi értéket képviselnek. A bájtot alkotó legnagyobb értékű bit lesz a 7. Szokásos még az LSB (Least Significant Bit - legkisebb értékű bit), ill. az MSB (Most Significant Bit - legnagyobb értékű bit) megjelölés is.
Az n. bit értéke a bájtban 2^n:

BIT 0
1
BIT 1
2
BIT 2
4
BIT 3
8
BIT 4
16
BIT 5
32
BIT 6
64
BIT 7
128

Így lesz pl. a 00000101 bájt értéke decimálisan (azaz a megszokott tízes számrendszerben) 1*2^2 + 1*2^0 = 5. A bináris-decimális átalakítást segíti az ENTERPRISE BASIC-je (BIN(x)), de a decimális számok kettes számrendszerbe alakításához nekünk kell segédrutint készítenünk:

100 PROGRAM "bin.bas"
110 DEF BIN$(X)
120   LET B$=""
130   LET B=128
140   FOR N=0 TO 7
150     LET BN=INT(X/B)
160     LET B$=B$&STR$(BN)
170     LET X=X+B*(BN=1)
180     LET B=B/2
190   NEXT
200   LET BIN$=B$
210 END DEF
220 DO
230   INPUT PROMPT "Atirando szam? ":SZ
240   IF SZ>=0 AND SZ<=255 THEN PRINT "Binaris alak: ";BIN$(SZ)
250 LOOP

Érdemes ezt a rutint (vagy hasonlót) beírni a gyakorlás lehetőségén kívül azért is, mert jól használhatjuk majd a gépi kódú programok nyomkövetésénél.

A bináris alak nagyon praktikus akkor, ha egy-két kiemelt bit értékét kell figyelnünk, vagy pl. a grafikában, ahol a megjelenő alakzat pontjai egy az egyben megfeleltethetőek a bitek értékeinek, de túlzottan körülményes az átlagos számolásokhoz, kódok, címek megadásához stb. A gépi kódú programozásban igen elterjedt a bájt felépítését továbbra is tükröző, de a binárisnál jóval tömörebb tizenhatos számrendszerbeli (hexadecimális) megadás. A hexadecimális számjegyek jelölésére - jobb híján - a decimális jegyeket (0-9) és az A-F betűket használjuk:

Decimális
Bináris
Hexadecimális
0
0000
0
1
0001
1
2
0010
2
3
0011
3
4
0100
4
5
0101
5
6
0110
6
7
0111
7
8
1000
8
9
1001
9
10
1010
A
11
1011
B
12
1100
C
13
1101
D
14
1110
E
15
1111
F

Egy hexadecimális jegy tehát 4 bit minden lehetséges értékét képes leírni. A bájt értékét ezek szerint két hexadecimális jegy adja meg. Az első a tizenhatosok számát, a második az egyesek számát tartalmazza (mint ahogy a tízes számrendszerben pl. 35 esetén 3 a tízesek száma, 5 az egyesek száma). Így pl. az A5 hexadecimális szám értéke tízes számrendszerben:

10*16^1 + 5*16^0=165

A továbbiakban az egyértelműség érdekében a hexadecimális alakot a szám után írt H betűvel jelöljük. Például:

32H = 3*16^1 + 2*16^0=50

Megjegyezzük, hogy a téma szakirodalmában gyakori a #32 vagy $32 típusú jelölés is. A 0 és 255 közötti számok hexadecimális alakját (00H-FFH) az 1. függelék tartalmazza.
A memóriák elérésére, megcímzésére a Z80-as processzornak 16 vezetéke van, amelyek mindegyike 0 vagy 1 állapotú lehet. Egy-egy memóriahely címét (ahol 1-1 bájt adat van elhelyezve) ezek szerint egy 16 bites bináris szám (az ún. "szó", "word") határozza meg. A processzor tehát 2^16 = 65 536 memóriahelyet tud megkülönböztetni, megcímezni (és oda adatot írni vagy onnan olvasni). Hogy az ENTERPRISE gépek esetén ennél jóval nagyobb memóriát is képes kezelni, az már egy technikai trükknek köszönhető (erről a következő fejezetben részletesebben is írunk).
Hogyan lehet egy címet megadni? Alapvetően természetesen egy 16 bites bináris számmal. A előbbiek alapján logikusan következik, hogy jóval praktikusabb, tömörebb két bájttal (4 hexadecimális jeggyel) megadni ugyanazt az értéket. Például:

1100 0010 0101 0100B = C254H

(a bináris értéket B-vel jelöljük)
Az első 8 bitet szokás felső (High) bájtnak, a második 8 bitet alsó (Low) bájtnak nevezni. A felső bájt tulajdonképpen a kétszázötvenhatosok számát adja. Így az előbbi cím decimális értéke:

C2H = 194
54H = 84
cím = 194*256 + 84 = 49 748

A decimális-hexadecimális oda-vissza konverzióra feltétlenül érdemes rutint készíteni, olyan gyakran lesz szükség az átszámításra:

1 PROGRAM "DEC-HEX.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 DEF W$(WORD)=H$(INT(WORD/256))&H$(MOD(WORD,256))
120 DEF DEC(H$)
130   LET D=0:LET Q=1
140   FOR N=LEN(H$) TO 1 STEP-1
150     LET D=D+ORD(HEX$(H$(N:N)))*Q
160     LET Q=Q*16
170   NEXT
180   LET DEC=D
190 END DEF

A program lefuttatása után próbáljuk ki a következőket:

PRINT H$(127)
7F

PRINT W$(16383)
3FFF

PRINT DEC("F"),DEC("FF"),DEC("FFFF")
15     255     65535

Az egyszerűség (és gyorsaság) érdekében a H$(N), ill. W$(N) rutin nem ellenőrzi az argumentum értéktartományát (0-255, ill. 0-65 535). Szükség esetén ezt a hívónak kell megtennie.
A bit, a bájt és az összetartozó kétbájtos érték a gépi kódú programozás leggyakrabban használt egységei. Nagyobb memóriatartományok meghatározására gyakori még a kilobájt (kbyte, 1024 bájt) egység is. A számítógép minden műveletet (a gyökvonástól az ellipszisrajzolásig) ilyen egy- és kétbájtos adatok mozgatásával, rajtuk és közöttük elemi műveletek elvégzésével valósít meg. Tekintsük át az alapműveleteket is. A bájtok között elvégzett minden (logikai, aritmetikai) művelet a bitek közötti műveletekből származtatható. Ezeket a legegyértelműbben az ún. igazságtáblázattal adhatjuk meg: az összes lehetséges esethez megadjuk a művelet eredményét. Elsőként nézzük a logikai műveleteket, amelyeknél a művelet eredménye mindig 1 bit:

AND logikai ÉS művelet   OR logikai VAGY művelet
  a b x = a AND b     a b x= a OR b
  0 0 0     0 0 0
  0 1 0     0 1 1
  1 0 0     1 0 1
  1 1 1     1 1 1
                 
XOR logikai KIZÁRÓ VAGY művelet   NOT komplementálás, invertálás
  a b x = a XOR B     a x = NOT a
  0 0 0     0 1  
  0 1 1     1 0  
  1 0 1          
  1 1 0          

A bájtok közötti műveletet az azonos helyi értékű bitek között külön-külön elvégzett műveletek valósítják meg. A műveletek használhatóságáról majd az utasításkészlet tárgyalása során ejtünk még néhány szót. Az ún. aritmetikai műveletek haszna egészen nyilvánvaló, a számtani alapműveletekről van szó:

összeadás x = a + b
a b x
0 0 0
0 1 1
1 0 1
1 1 10

Az utolsó sorra érdemes felhívni a figyelmet: 1+1 = 0 és marad 1. A túlcsorduló 1-es már a magasabb helyi értékű bitek értékeit növeli (hasonlóan, mint a tízes számrendszerbeli összeadásnál). Több bites számok (pl. két bájt) összeadásánál először a legkisebb helyi értékű biteket kell összeadni, majd sorra a nagyobb értékűeket, mindig hozzáadva az összeadandó bitekhez az előző összeadás átvitelét is. Magától értetődő, hogy - ha az összeg nagyobb 255-nél - a legmagasabb helyi értékű bitekről is keletkezhet átvitel. Valójában tehát a 8 bites számok összeadásához szükség van még egy 9. bitre is, amely az esetleges átvitelt, a túlcsordulást jelzi. Mint látni fogjuk, a processzor rendelkezik is ilyen bittel.

kivonás x = a - b
A kivonás az első problematikus művelet. A nehézségét az adja, hogy az eredmény negatív is lehet, ami nem illik bele a számábrázolás eddigi rendszerébe. Ha szükség van a negatív számok kezelésére is - márpedig ez a számítógép alkalmazások legtöbbjében elengedhetetlen -, meg kell egyezni a negatív értékek ábrázolásának módjában.
A Z80-as mikroprocesszornál választott megoldás az ún. kettes komplemens ábrázolás: az egybájtos számok tartományában a -b érték helyett a 256-b értéket tárolja és használja a rendszer. Ez már újra egy bájton ábrázolható pozitív érték. Az így megadható számok értéke -128, és +127 között lehet:

Előjeles érték
Tárolt érték
 
decimálisan
hexadecimálisan
-128
256 - 128 = 128
80H
-127
256 - 127 = 129
81H
...
   
-1
256 - 1 = 255
FFH
0
0
00H
1
1
01H
...
   
127
127
7FH

Ez a megoldás több szempontból praktikus:

Most már csak az a kérdés, hogy ha pl. egy memóriacímről FEH értéket olvasunk be, honnan tudjuk, hogy ezt 254-nek vagy -2-nek kell-e tekintenünk. Ha tömören akarunk válaszolni, azt mondhatnánk, hogy sehonnan. Valójában ezt csak az érték előélete szabhatja meg. Lényegében mi döntjük el, hogy egy művelet operandusait és eredményét előjeles számoknak vagy egybájtos pozitív egészeknek tekintjük-e. A processzor mindkét értéktartományt egyformán kezeli, figyeli és jelzi mindkét esetre a határátlépéseket (átvitel, túlcsordulás), lehetőséget adva ezzel a tetszés szerinti kiértékelésre. Az elmondottakból logikusan következik, hogy ha kétbájtos értékek esetén akarunk előjeles számokat ábrázolni, -nn helyett ennek kettes komplemense (65 536 - nn) a megadandó érték (így -32 768 és 32 767 közötti számokat ábrázolhatunk).
Az eddigiek szerint a memóriaterület egy-egy címén tárolt egybájtos adat jelenthet 0 és 255 közötti egész értéket, vagy -128 és +127 közötti előjeles számot. Ezenkívül a rendszer még két kódolási formát alkalmaz az adatok tárolásánál, feldolgozásánál.

A tízes számrendszerbeli számok ábrázolására az ENTERPRISE általában az ún. BCD-kódrendszert alkalmazza (Binary Coded Decimal = binárisan kódolt decimális). Ennek lényege, hogy a tárolt érték hexadecimális alakja magukat a számjegyeket adja meg (a két hexadecimális karakter két számjegyet):

Kódolandó szám
Tárolt érték
0
00H
1
01H
...
 
9
09H
10
10H
11
11H
...
 

Ez a kódolási forma tehát csak annyiban tér el a már megismert hexadecimálistól, hogy tiltja annak A-F karaktereit. Így a lehetséges 256 különböző érték helyett egy memóriacímen csak 100-féle értéket tárolhatunk (0-99), de a memóriapazarlásért kárpótol minket az aritmetikai műveletek, a kijelzés egyszerűsége, sebessége, pontossága.
A következő program egy, az operációs rendszer által BCD-ben kezelt értéket olvas és ír ki folyamatosan: az óra másodperc értékét. A decimális alakon jól láthatók a BCD logika ugrásai (a tiltott állapotok átlépései), a hexadecimális kiírás viszont tökéletesen követhető:

1 PROGRAM "BCD.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 LET CIMSEC=49010
120 TEXT
130 DO
140   LET SEC=SPEEK(255,CIMSEC)
150   PRINT AT 1,1:SEC,H$(SEC)
160 LOOP

A szövegek, karakterek tárolásának alapja az ENTERPRISE-on az ASCII kódrendszer. Itt egy bájton egyetlen karaktert tárolunk, pontosabban annak kódját. Az alfanumerikus jegyek (számjegyek, matematikai jelek, az angol ABC betűi) nemzetközi szabvány szerinti kódjait (pl. A kódja 41H = 65Dec) az ENTERPRISE speciális karaktereinek kódjai egészítik ki. A kódokhoz tartozó karakterek kísérletként közvetlenül is megjeleníthetők a képernyő legfelső sorában. (az állapotsorban) a programfutás idejére (leállás után a BASIC rendszer felülírja ezt a területet):

100 LET CIM=48830
110 LET K=64
120 FOR I=0 TO 31
130   POKE CIM+I,K+I
140 NEXT
150 GOTO 150

Próbálja ki a rutint különböző K kezdőértékekkel! A kódokhoz rendelt karaktereket az 1. függelék is tartalmazza.

Az általános fogalmak összefoglalása után nézzük meg, hogy maga a Z80-as mikroprocesszor milyen feltételeket biztosít a gépi kódú programozáshoz.
Bár a processzor működésének lényege a külső memóriacímek olvasása, tartalmuk átírása, módosítása, de ennek segítésére jó néhány belső memóriarekesze (ún. regisztere) is van. Ezek a regiszterek általában 8-bitesek, de egy részüket párban - mint 16-bites regisztert - is képes használni a processzor.

Regiszterek:
A 8-bites aritmetikai és logikai műveletek alapja az A-regiszter, az ún. akkumulátor. Ebbe kell tölteni a műveletek előtt az (egyik) operandust és ez tartalmazza a művelet eredményét.

A flagregiszter (F) a művelet eredményére jellemző egybites információkat tartalmaz. Hat értékes bitje:

A 3. és 5. bit közvetlenül nem kérdezhető le, gyakorlatilag nincs kihasználva. Két dolgot már most érdemes kiemelni a jelzőbitekre vonatkozóan:

A regiszterkészlet további elemei:

A további regiszterekkel nekünk közvetlenül nemigen lesz dolgunk. Csak felsorolásszerűen:

Az utasítások áttekintése előtt (a bemutatandó példák érdekében) nézzük meg, hogy miként lehet egyáltalán az ENTERPRISE-on gépi kódú rutinokat betölteni és futtatni. Itt - éppen az egyszerűség érdekében - a BASIC rendszeren belül maradunk.
A program tárolásának, a kódok memóriába töltésének viszonylag kényelmes eszköze a CODE utasítás. Ez egy füzér (string) karaktereit helyezi el a memóriában. A tárolás kezdőcímét a 021C/DH BASIC rendszerváltozó tartalmazza. Ez alaphelyzetben a BASIC munkaterület kezdete. Óvatosan kell tehát bánni az utasítással, hiszen a BASIC program elejét felülírhatjuk vele. Ezt akadályozza meg egy a program elején kiadott ALLOCATE n utasítás, ami n bájttal hátracsúsztatja a programot, helyet csinálva a gépi kódú rutinunknak. Az utasítás törli a változókat, tehát valóban a program elején praktikus kiadni. A lefoglalt hellyel nem érdemes különösebben takarékoskodni. Az összecsúszás veszélye miatt, a bővítések, alakítások érdekében mindig lefoglalunk legalább 100 bájtot (ezért ne is csodálkozzon az Olvasó, ha egy néhány bájtos rutin előtt ALLOCATE 100 utasítást lát).
A 021C/DH rendszerváltozó (decimálisan 540/1) közvetlen átírásával a tárolási cím máshová is állítható. Például:

POKE 540,0
POKE 541,48

utasítások után a következő CODE már -a 3000H (12288Dec) címtől tárolja a bájtokat. Ezt is használjuk, ha pl. rögzített címén lévő adatmezőre vagy rutinra van szükség. Ez azért is fontos lehet, mert a rendeltetésszerűen elhelyezett gépi kódú rutin nincs teljes biztonságban a BASIC munkaterület kezdetén: a program szerkesztéskor visszacsúszhat erre a területre, felülírva a rutint. Ez nyilván megengedhetetlen egy olyan gépi kódú program esetén, amit az operációs rendszer hív a BASIC-tő1 függetlenül (pl. megszakításkezelő).
A példaként írt 3000H kezdőcím praktikus, mert külön védelem nélkül is viszonylag biztonságban van a BASIC terület közepén (de csak ha nem használunk nagy memóriaigényű 0-s BASIC programot).
A 0-16 383 címtartományban (nulláslap) kisebb szabad területet találhatunk még a 0180H címtől (ha BASIC-ben maradunk, 128 bájt) vagy néhány bájtot a memória legelején is (0000-0004). A programok elhelyezésére - legalábbis az egyszerűbb esetekben - használjuk ezt a nulláslapot, hiszen a memóriakonfiguráció további tartományairól nincs mindig biztos információnk (l. pl. 2. fejezet).
A CODE utasítás - mint írtuk - egy füzért vár operandusként

CODE = karakterkifejezés

alakban. Például a

CODE=CHR$(6) &"VIDEO:"

utasítás a szöveg hosszát, majd karaktereit helyezi el az aktuális tárolási címtől a memóriában (így várja ezt pl. a csatorna megnyitáskor a rendszer).
Sokat egyszerűsít a beírás munkáján a HEX$ függvény, amely az argumentumában megadott (vesszővel elválasztott) hexadecimális értékeket alakítja bináris számokká és fűzi karaktersorozatba.

CODE = HEX$ ("21,00,00,C9")

utasítás végrehajtása után pl. a 33,0,0,201 (decimális) értékeket találjuk a memóriában. Ha egy változónevet is megadunk a CODE után, az értelmező a változóba tölti a füzér aktuális tárolási címét. Esetünkben pl.:

ALLOCATE 3
ok
CODE M1=HEX$("01,02")
ok
CODE M2=HEX$("03")
ok
PRINT M1,M2
4827 4829
ok

Praktikus ezt a lehetőséget kihasználni, hiszen gyakran szükség lehet a rutinok, adatmezők kezdőcímére (pl. szubrutinhívásnál). A kezdőcímek felhasználását is támogatja az értelmező a WORD$ függvénnyel: ez az argumentumot (0-65 535 közötti érték) alakítja kétbájtos egésszé

A$=WORD$(12288)
ok
PRINT LEN(A$)
2
ok
PRINT ORD(A$(1:1)),ORD(A$(2:2))
0 48
ok

Mint a megadott esetben (3000H) is látható, a füzérben elöl az alsó bájt áll (0) azt követi a felső bájt (48Dec = 30H). Ez a forma - mint látni fogjuk - általános a gépi kódú programokban a címek megadására. A memóriában elhelyezett gépi kódú program futtatására a USR függvény alkalmas. Hívható pl.:

CALL USR(cím,hl)

alakban, de alkalmazható akár

PRINT 12*USR(cím,hl)+5

formában is. Az argumentumba természetesen a rutin kezdőcímét kell írni, valamint egy 0-65 535 közötti paramétert. Az értelmező a cím meghívása előtt a paraméter értékét (hl) a processzor HL regiszterébe tölti. Ezt felhasználhatjuk bemenő adatként rutinunkban. Ugyanígy vissza is adhatunk egy értéket (HL-ben) az értelmezőnek, amit az majd függvényértéknek tekint. A meghívott rutint "RET' utasítással kell zárnunk (C9H kód). Ennek hatására tér vissza a vezérlés a BASIC értelmezőhöz. A BASIC a visszaadott értéket előjeles kettes komplemens számnak tekinti. Jó tudni még azt is, hogy a felhasználói rutin meghívásakor a 3. lapra (a 0000H-FFFH tartományba) az 1. ROM-szegmens van lapozva (erről bővebben írunk a 2. fejezetben), tehát a megadott tartományban ennek a szegmensnek a rutinjait, adatait találjuk.

1.2 Az utasításkészlet

A Z80-as mikroprocesszor több száz utasítást különböztet meg és hajt végre. Ez csak úgy lehetséges, hogy a gyakori, egyszerű utasítások egybájtos kódjai mellett több-bájtos utasításkódok is vannak. Hogy ne vesszünk el ebben a kódtengerben, most általában nem is foglalkozunk az utasítások-kódjaival (ezt az 1. és 2. Függelék táblázataiban megtalálja az Olvasó). Bár a gépi kódú program valójában kódok egymásutánját jelenti, mi az áttekinthetőség érdekében egyszerű, a funkcióra utaló néhány betűs neveket adunk meg: a, gépi kódú programozás assembly nyelvének ún. mnemonikjait. A kódokkal való megadást mindenhol példaszerűen tekintjük át. Ebben a fejezetben minden kódot és operandust hexadecimális alakban adunk meg, ezért ezt most külön nem jelöljük.

1.2.1 Adatmozgató és -cserélő utasítások

A processzor talán legalapvetőbb utasítástípusa nagy területet ölel fel. A regisztereket konstanssal (vagy egy másik regiszter tartalmával) feltöltő utasítások a BASIC "LET" parancsával analógak (LET A = n, LET A = B stb.). Ugyanakkor a külső memóriát célzó adatmozgató utasításokban találhatjuk a "POKE" és "PEEK" analógiáit.

8 bites adatmozgatás - (LOAD)
Egy regisztert vagy memóriacímet tölt fel egy bájttal. Az érték forrása változatlan marad. A forrás és a cél igen sokféle lehet. Tekintsük át az első utasításnál részletesen a megadható módokat, hiszen ezzel ismerhetjük meg a processzor címzésmódjait. Először azokat az utasítástípusokat vegyük sorra, amelyek a A regiszterbe töltenek egy bájtot:

LD A,B A B regiszter tartalmát tölti A-ba. Ugyanígy a többi általános célú regiszter is megadható forrásként.
LD A,n A-ba tölti n értékét (00 <= n <= FF). n értékét az utasítás kódja után kell megadni. Jelen esetben:
3E,n
LD A,(nn)
A-ba tölti az nn memóriacím (0000 <= nn <= FFFF) tartalmát. Az A = PEEK (nn) utasítással analóg. A zárójel mindig azt jelenti, hogy a benne levő érték memóriacímként szolgál, az operandus az adott memóriacím tartalma. Az utasítás kódja után először a cím alsó bájtját kell írni, majd a felső bájtját. Például LD A,(0180) esetén:
3A,80,01
LD A,(HL)
ugyancsak a külső memória tartalmával tölti fel az A-t, de itt a memória címét a HL regiszterpár tartalmazza. Például, ha HL = 020AH (az aktuális BASIC programszámot tároló rendszerváltozó címe) az utasítás végrehajtása után A-ban az aktuális program száma lesz.
LD A,(IX+d)
A-t az IX+d memóriacímről tölti fel. Itt d egy előjeles egész (-128Dec <= d <= +127Dec). Ezzel az utasítással olvashatjuk be egy memóriatömb d. elemét (ahol az IX indexregiszter a tömb címét tartalmazza). Megadásakor az utasítás kétbájtos kódja után kell megadni d értékét:
DD,7E,d

Az előbbiekkel analóg módon fordított irányú adatmozgatást is kérhetünk:

LD B,A
LD (nn),A
LD (HL),A
LD (IX+d),A

Ezekkel az utasításokkal természetesen az A regiszter tartalmát tölthetjük a célregiszterbe, ill. a memóriacímre. A következő táblázat összefoglalja a 8-bites LD utasítás lehetséges (x) címzésmódjait:

 
A
B
C
D
E
H
L
(HL)
(BC)
(DE)
(IX+d)
(IY+d)
(NN)
N
LD A,
X
X
X
X
X
X
X
X
X
X
X
X
X
X
LD B,
X
X
X
X
X
X
X
X
   
X
X
 
X
LD C,
X
X
X
X
X
X
X
X
   
X
X
 
X
LD D,
X
X
X
X
X
X
X
X
   
X
X
 
X
LD H,
X
X
X
X
X
X
X
X
   
X
X
 
X
LD L,
X
X
X
X
X
X
X
X
   
X
X
 
X
LD (HL),
X
X
X
X
X
X
X
           
X
LD (BC),
X
                         
LD (DE),
X
                         
LD (IX+d),
X
X
X
X
X
X
X
           
X
LD (IY+d),
X
X
X
X
X
X
X
           
X
LD (nn),
X
                         

Valamint

LD A,I
LD A,R
LD I,A
LD I,R

Fontos hangsúlyozni, hogy a LD utasítások nem változtatják meg az F regiszter bitjeit, kivéve a LD A,I és LD A,R utasításokat. Attól tehát, hogy kiadunk egy
LD A,00
utasítást, még nem lesz Z a státusz.
Nézzünk egy-két elemi példát az utasítások alkalmazására. Egyelőre írjuk a mnemonikok mellé az utasítások kódjait is:

M1 LD H,L
LD L,00
RET
65
2E 00
C9

Ez a rutin áttölti a HL regiszterpár alsó bájtját a felsőbe, tehát tulajdonképpen 256-tal szorozza azt. BASIC programban betöltve és futtatva:

100 ALLOCATE 100
110 CODE M1=HEX$("65,2E,00,C9")
120 DO
130   INPUT PROMPT "L=? ":L
140   PRINT USR (M1,L)
150 LOOP

START
L=? 1
256
L=? 2
512
L=; 100
25600
L=? 128
-32768

A 2. rutin a HL-ben megadott címről olvas be egy bájtot és ezt adja vissza a BASIC-nek (tehát tulajdonképpen a BASIC "PEEK" utasítását hajtja végre):

M2 LD A,(HL)
LD L,A
LD H,00
RET
7E
6F
26 00
C9

100 ALLOCATE 100
110 CODE M2=HEX$("7E,6F,26,00,C9")
120 DO
120   INPUT PROMPT "CIM=? ":CIM
130   LET A=USR(M2,CIM)
140   PRINT "GEPI:";A,"PEEK:":PEEK(CIM)
150 LOOP

START
CIM=? 512
GEPI: 163 PEEK: 163
CIM=? 0
GEPI: 0 PEEK: 0
CIM=? 49152
GEPI: 178 PEEK: 178

16-bites adatmozgató utasítások
A kétbájtos adatátvitel a regiszterpárok tartalmát együtt mozgatja. Mnemonikja ugyancsak LD. Típusai:

LD HL,nn a HL regiszterpárba tölti az nn kétbájtos értéket: L-be az alsó bájt, H-ba a felső bájt kerül.
Például a LD HL,021C
kódjai: 21, 1C, 02
és végrehajtása után
L = 1C
H = 02
LD HL,(nn) a zárójel itt is azt jelenti, hogy a regiszterpárt memória tartalmával tölti fel a processzor:
L-be az nn memóriacím tartalmát
H-ba az nn+1 cím tartalmát
LD SP,HL a regiszterpárból regiszterpárta töltés csak néhány esetben működő címzési mód
LD (nn),HL a regiszterpár memóriába töltése a
POKE nn,L
POKE nn+1,H

utasításokkal analóg módon történik

Itt is foglaljuk össze táblázatosan a megengedett címzésmódokat:

 
BC
DE
HL
SP
IX
IY
nn
(nn)
LD BC,            
X
X
LD DE,            
X
X
LD HL,            
X
X
LD SP,    
X
 
X
X
X
X
LD IX,            
X
X
LD IY,            
X
X
LD (nn),
X
X
X
X
X
X
   

A 16-bites adatmozgató utasítások közé tartozik két különleges csoport: a PUSH és a POP utasítás. Az utasítások célja egy regiszterpár taralmának külső memóriába (a verembe) töltése, ill. visszaolvasása onnan. Specialitásuk abban áll, hogy a külső memóriára mutató regiszterpár (SP) értéke automatikusan állítódik minden művelet után:

PUSH HL H értéke az (SP-1) címre
L értéke az (SP-2) címre
SP új értéke SP-2
POP HL L-be az (SP) cím tartalmát tölti
H-ba az (SP+1) cím tartalmát tölti
SP új értéke SP+2

A verem tehát egy olyan tárterület, amelyet felülről lefelé vesz igénybe a processzor, miközben az SP veremmutató mindig a szabad terület fölé mutat.
Amellett, hogy a processzor számára más műveletnél (szubrutinhívások) nélkülözhetetlen a verem, a most megismert utasításokkal is igen jól használható:

A PUSH és POP utasítások az AF, BC, DE, HL, IX, IY regiszterpárokra alkalmazhatók.
A processzor státuszát (F) a 16-bites adatmozgató utasítások egyike sem változtatja meg.

Először itt is nézzünk két igen egyszerű példát az utasítások alkalmazására. Az M3 rutin a képernyő első sorának tárolási címét olvassa be a sorparaméter táblából.

M3 LD HL,(B914)
RET
2A 14 B9
C9

A cím felhasználható közvetlen képernyőíráshoz. Ehhez előbb a VIDEO cím tartománya alapján meg kell határoznunk a tárolószegmens számát (erről pl. a 2.1 alfejezetben lesz bővebben szó). Az utolsó sor a bal felső sarokba ír egy A betűt:

100 ALLOCATE 100
110 CODE M3=HEX$("2A,14,B9,C9")
120 TEXT 40
130 PRINT
140 LET VID_C=USR(M3,0)
150 IF VID_C<0 THEN LET VID_C=65536+VID_C
160 PRINT "Video-cim: ";VID_C
170 LET SGM=252+INT(VID_C/16384)
180 SPOKE SGM,VID_C,65

A második rutin felhasználásával lekérdezhetjük az állapotsor tetszőleges karakterét. Ehhez az IX indexregisztert is felhasználjuk, tehát gondoskodnunk kell a regiszter BASIC-beli értékének tárolásáról, majd visszaállításáról:

M4 PUSH IX
LD IX,BEBE
LD L,(IX+0)
POP IX
RET
DD E5
DD 21 BE BE
DD 6E 00
DD E 1
C9

A BASIC program a gépi rutin a LD L,(IX+0) utasításába, az index helyére írja be a kiolvasandó karakter sorszámát. Így a futtatáskor ezen a részen mindig a konkrét indexérték (az indexregiszterbe töltött báziscímhez képest relatív eltolás) lesz (pl. LD L,(IX+2)). A program a visszakapott kódot - a színinformáció letakarása után - karakterként ki is írja.

100 ALLOCATE 100
110 CODE M4=HEX$("DD,E5,DD,21,BE,BE,DD,6E,00,DD,E1,C9")
120 DO
130   INPUT PROMPT "Hanyadik karaktert olvassam? ":D
140   POKE M4+8,D
150   LET C$=CHR$(USR(M4,0) BAND 127)
160   PRINT D;". karakter: ";C$
170   PRINT
180 LOOP

START
Hanyadik karaktert olvassam? 0
0 - karakter: I

Hanyadik karaktert olvassam? 1
1 . karakter: S

Hanyadik karaktert olvassam? 23
23 . karakter: 0

Hanyadik karaktert olvassam? -6
-6 . karakter: C

Az indexregiszter a megadott értékkel az állapotsor "IS-BASIC ..." szövegének első karakterére mutat, így adódnak a kiolvasott karakterek. A (-6) érték a futtatáskor éppen érvényes CAPS üzemmód-szöveg C karakterét adta vissza.
Az előző rutinokat tulajdonképpen csak a kódbeírás, paraméterátadás, rutinmeghívás bemutatására szántuk. Ezek még nem végeztek olyan feladatot, amit BASIC-ben meg ne tudtunk volna oldani. Írjunk most egy olyan rutint a megismert utasítások alkalmazásával, amelyet jól tudunk majd használni a gépi kódú utasítások funkcióinak, sajátosságainak bemutatásához, megértéséhez.
A gépi kódú programozás egyik - sokakat távoltartó - problémája a folyamatok követhetetlensége. Egy jó segédprogram (monitor) egyik legfőbb szolgáltatása éppen az, hogy lépésenként tudjuk az egyes utasítások regiszterekre és jelzőbitekre való hatását követni. Lényegében arról van szó, hogy bármely utasítás után ki tudjuk íratni a (számunkra érdekes) regiszterek tartalmát.
Ezt gyári monitorprogram nélkül, saját rutin segítségével is megoldhatjuk. Írjunk egy olyan befejezést későbbi programrészleteinkhez, amely a regiszterek tartalmát a memória rögzített helyére tölti. Innen majd BASIC-ben kiolvashatjuk és kiírhatjuk a minket érdeklő paramétereket, jelzőket. A feladatot legegyszerűbben a veremműveletek segítségével oldhatjuk meg. Nem szabad megfeledkeznünk arról, hogy a rutin befejezése előtt visszaállítsuk az SP veremmutató eredeti tartalmát:

M5 LD (0190),SP
LD SP,0190
PUSH AF
PUSH BC
PUSH DE
PUSH HL
LD SP,(0190)
RET
ED 73 90 01
31 90 01
F5
C5
D5
E5
ED 7B 90 01
C9

Természetesen SP visszaolvasása előtt szükség esetén más regisztereket is kivihetünk (pl. PUSH IX). A rutin futtatása után a memóriaterületről kiolvashatjuk a Z80-as regiszterek visszatérés előtti tartalmát:

HEX DEC
0191
0190
018F
018E
018D
018C
C18B
018A
0189
0188
401 : SP felső bájt
400 : SP alsó bájt
399 : A
398 : F
397 : B
396 : C
395 : D
394 : E
393 : H
392 : L

A vizsgálandó rutin előtt praktikus a flagregisztert is törölni (így egyértelmű a vizsgált szakasz hatása). Erre egy lehetséges megoldás:

LD BC,0000
PUSH BC
POP AF
...
01 00 00
C5
F1

Itt egy 0-val feltöltött regiszterpár tartalmát írjuk át a verem közvetítésével AF-be.
A betöltő BASIC program tartalmazza az előkészítő (250. sor) és a befejező programszakaszokat (270. sor). Mivel a CODE utasítás folyamatosan tárolja a bájtokat a memóriában, a program így is életképes, de valódi haszna a két szakasz közé, a 260. sorba beírt utasítás- vagy rutinrészlet tesztelésében van. A program végrehajtó része a státusz könnyebb kiértékelése érdekében bitekre bontja a flagregisztert (szükség esetén ezt a többi regiszterrel is megtehetjük). A már ismert decimális-bináris átalakító rutint kissé módosítottuk, hogy az F regiszter határozatlan bitjei helyett egy-egy pont jelenjen csak meg. A további regisztereket - jelen formában - hexadecimális alakban írja ki a program.
Például a LD DE, 3322 (11,22,33) utasítás hatása:
Az A, F, B, és C regiszter az előkészítő szakaszban töltődik fel 0-val, H és L értéke a CALL USR (M,0) hívás paramétere (0), a D = 33H, C = 22H értékek pedig a tesztelt utasítás eredményei.

1 PROGRAM "Register.bas"
100 ALLOCATE 100
110 DEF H$(N)=CHR$(INT(N/16)+48-7*(INT(N/16)>9))&CHR$(MOD(N,16)+48-7*(MOD(N,16)>9))
120 DEF BIN$(X)
130   LET B$=""
140   LET B=128
150   FOR N=0 TO 7
160     LET BN=INT(X/B)
170     IF N<>2 AND N<>4 THEN LET B$=B$&STR$(BN)
180     IF N=2 OR N=4 THEN LET B$=B$&"."
190     LET X=X+B*(BN=1)
200     LET B=B/2
210   NEXT
220   LET BIN$=B$
230 END DEF
240 !
250 CODE M=HEX$("01,00,00,c5,f1")
260 !
270 CODE VEGE= HEX$("ed,73,90,01,31,90,01,f5,c5,d5,e5,ed,7b,90,01,c9")
280 !
290 CALL USR(M,0)
300 LET F=PEEK(398)
310 PRINT ,,"SZ,H,PNC"
320 PRINT " F:";,,BIN$(F)
330 PRINT " A=";H$(PEEK(399))
340 PRINT "B,C=";H$(PEEK(397));",";H$(PEEK(396))
350 PRINT "D,E=";H$(PEEK(395));",";H$(PEEK(394))
360 PRINT "H,L=";H$(PEEK(393));",";H$(PEEK(392))

Adatcserélő utasítások
16-bites regiszterpárok tartalmát cserélik meg egyetlen utasítással.

EX DE,HL a két regiszterpár tartalma felcserélődik, az összes többi regiszter (F is) változatlan marad
EX (SP),HL
HL-be a veremtár aktuális tartalma kerül (mint egy POP HL utasításnál) de ugyanakkor HL régi tartalma a verembe töltődik (mint egy PUSH HL utasítás esetén). SP értéke nem változik Az utasítás
EX (SP),IX
és EX (SP),IY
alakban is használható
EX AF,AF'
kicseréli az akkumulátort és a flagregisztert a háttérregiszter-készlet megfelelő rekeszeivel. Az utasítás kiválóan használható pl. egy sokszor használt bemenő paraméter (vagy ami az operációs rendszerben igen gyakori: egy jellemző státusz) ideiglenes - veremtől független - tárolására és ismételt elővételére a megfelelő pillanatban
EXX egyetlen utasítással megcseréli a
BC - BC'
DE - DE'
HL - HL'
regiszterpárokat; igen gyors adattárolásra alkalmas.

A regisztercserék után a processzor az eddigi háttérregiszterekkel dolgozik, míg az eredeti regiszterek értéke megőrződik a háttérben.

Blokkmozgató utasítások
A Z80-as mikroprocesszor talán leghatékonyabb utasításai a blokkutasítások. A gépi kódú programozásban gyakori feladat egy kisebb-nagyobb memóriaterület feltöltése egy rögzített adatterületről vagy egy adott bájttal. A Z80-as processzornál ezt egyetlen utasítással is elérhetjük.

LDI
a blokkmozgató utasítás egylépéses változata; a HL memóriacím tartalmát a DE memóriacímre tölti, majd 1-gyel növeli HL és DE, csökkenti BC értékét
LDIR
az LDI utasítást automatikusan ismétli mindaddig, amíg BC értéke nullára nem csökken. Ez a parancs tehát a HL címen kezdődő, BC hosszúságú memóriaterület tartalmát másolja át a DE kezdőcímű memóriaterületre
LDD
ez az ugyancsak egylépéses utasítás analóg az LDI-vel, de ez az adatátvitel után csökkenti HL és DE értékét (és persze BC-t is)
LDDR az LDD utasítást ismétli, amíg BC = 0 nem lesz. Analóg tehát az LDIR utasítással, de ez felülről lefelé haladva viszi át az HL címen végződő, BC hosszúságú memóriaterület tartalmát a DE végcímű memóriaterületre

A blokkmozgató utasításoknál HL mindig a forrás, a DE a cél címe, BC pedig az átviendő bájtok számát adja meg.
A mnemonikok betűjelei a funkciókra diainak:

Az F regiszter jelzőbitjeit a négy utasítás azonos módon állítja:

Alkalmazási példaként vigyük át az 1. szegmens E70FH címén kezdődő üzenet szövegét az állapotsorba (a BEBEH címtől). A szöveg ("1985 Intelligent Software Ltd") hossza 29 (1DH).

M6 LD HL,E70F
LD DE,BEBE
LD BC,001D
LDIR
21 0F E7
11 BE BE
01 1D 00
ED B0

A rutint illesszük be az előzőekben megismert regiszterkiírató programba és úgy futtassuk le:

260 CODE M=HEX$("21,0F,E7,11,BE,BE,01,1D,00,ED,B0")

(A programot célszerű végtelen ciklusban tartani, pl. egy 370 GOTO 370 sorral, hogy ne íródjon felül az állapotsor.)
A futtatás után természetesen
BC = 0
DE = BEDB = BEBE + 1D
HL = E72C = E70F + 1D

A forrás- és célmutató (HL, ill. DE) az átvitt, ill. feltöltött terület utáni bájtra mutat, tehát szükség esetén folytatható az átvitel, esetleg más területre (vagy területről).
Az LDIR és LDDR utasítások különbsége akkor válik lényegessé, ha a forrásterület és a feltöltendő blokk átlapolódik (van közös területük). Ekkor ui. előfordulhat, hogy az utasítás "beleír" a forrásterületbe. Hogy ezt elkerüljük:

Ezt természetesen nem kötelező alkalmazni. Ha pl. egy terület adott bájttal való feltöltése a célunk, nagyon is jól jön ez a tulajdonság: a karaktert csak az első címre kell betöltenünk, és DE = HL + 1 választással az LDIR ezt a karaktert töltögeti végig a blokkba:

M7 LD HL,BEBE
LD (HL),2A
PUSH HL
POP DE
INC DE
LD BC,001D
LDIR
...
21 BE BE
36 2A
E5
D1
13
01 1D 00
ED B0

Az állapotsor elejére a * karakter ASCII kódját (2AH) töltjük. A még ismeretlen INC DE utasítás a DE = DE + 1 műveletet végzi el.

260 CODE M=HEX$("21,BE,BE,36,2A,E5,D1,13,01,1D,00,ED,B0")

1.2.2 Logikai utasítások
A logikai alapműveleteket végzik el az akkumulátor és egy másik 8-bites adat (regiszter- vagy memóriatartalom) bitjei között. Az eredmény az A regiszterben képződik.

AND B - logikai ÉS az A és B bitjei között
OR B - logikai VAGY az A és B bitjei között
XOR B - logikai KIZÁRÓ VAGY az A és B bitjei között

A megengedett címzésmódok:

 
A
B
C
D
E
H
L
(HL)
(IX+d)
(IY+d)
n
AND
X
X
X
X
X
X
X
X
X
X
X
OR
X
X
X
X
X
X
X
X
X
X
X
XOR
X
X
X
X
X
X
X
X
X
X
X

Értelmes tehát pL. az OR (HL), de nem létező az OR(nn) utasítás. A logikai utasítások hatása a jelzőbitekre:

A logikai utasítások egyik gyakori alkalmazási területe a grafika. Például a HL címen lévő bájt bitjei felelnek meg a képpontoknak. Ekkor:

OR (HL)
LD (HL),A
- kigyújtja azokat a pontokat, ahol A bitjei 1 állapotúak;
AND (HL)
LD (HL),A
- törli azokat a pontokat, ahol A bitjei 0 állapotúak;
XOR (HL)
LD (HL),A
- átbillenti azokat a pontokat, ahol A bitjei 1 állapotúak.
Például: (HL) = 00110011
  A = 10101010
  OR: 10111011
  AND: 00100010
  XOR 10011001

Az AND utasítást jól használhatjuk egy adat minket érdeklő bitjeinek kiemelésére, maszkolására, az OR A utasítást igen gyakran alkalmazzák a ROM-rutinok, általában csak az átvitelbit (C) törlését várva tőle, a XOR A utasítás a legegyszerűbb módja az A = 0 értékadásnak stb.

1.2.3 Aritmetikai utasítások

8-bites utasítások
A Z80-as processzor az összeadást és a kivonást tudja elvégezni. A művelet egyik operandusa mindig az A-regiszter. Az eredmény is az akkumulátorban képződik. Típusai (ugyancsak a B operandus példáján):

ADD A,B
az A = A + B műveletet végzi el. Ha az eredmény meghaladja az ábrázolási tartományt, jelzi az átvitelt (az F regiszter C (carry) bitje 1)
ADC A,B
az összeadásnál figyelembe veszi az átvitelbit (C) előző állapotát is: A = A + B + Carry
SUB B A = A - B
SBC A,B A = A - B - Carry

A lehetséges operandusok mind a négy utasításra:

A
B
C
D
E
H
L
(HL)
(IX+d)
(IY+d)
n
X
X
X
X
X
X
X
X
X
X
X

A műveletek akkumulátorban keletkező egybájtos eredménye mellett fontos a jelzőbitek figyelembevétele is:

Érdemes egy kicsit gyakorolni a regiszterkiírató programunkkal az aritmetikai műveleteket, hatásukat a jelzőbitekre. Például:

M8 LD A,40
ADD A,50
3E 40
C6 50

Csak az érintett regisztereket íratva ki:

260 CODE M$=HEX$("3E,40,C6,50")
F : 10,0,100
A=90

Az eredmény 40H + 50H = 90H helyes, ha pozitív egészeknek tekintjük az értékeket (C = 0), de hibás (P = 1, itt helyesebb lenne V = 1-et írni), ha előjeles számokként értelmezzük az operandusokat és az eredményt, hiszen
40H + 50H <> -70H
Ugyanakkor A = 40H, n = E0H esetén

260 CODE M$=HEX$("3E,40,C6,E0")
F : 10,0,001
A=20

Az eredmény tehát hibás (C = 1) pozitív számábrázolásnál, hiszen
40H + E0H = 120H <> 20H
de helyes (P/V = 0) előjeles számábrázolásnál, hiszen
40H + (-20H) = 20H

Speciális aritmetikai utasításnak tekinthető az összehasonlító utasítás (CP), amely a SUB utasítással azonos módon állítja be a jelzőbiteket, de az eredményt nem tárolja (az akkumulátor változatlan marad). Például:

CP B
F regiszter bitjeinek beállítása az (A-B) eredménynek megfelelően. A használható operandusok is megegyeznek a fenti aritmetikai műveletek címzésmódjaival. Például a CP A utasítás Z státuszt állít be.
Az utasítás elsősorban karakter keresésére, azonosítására .használható. Léteznek tömbkereső változatai is (amelyek analógak a tömbátviteli utasításokkal).
CPI CP (HL)
HL = HL + 1
BC = BC - 1 (egy lépésben)
CPIR
automatikusan ismétli az összehasonlítást, végigpásztázva a HL-lel megcímzett, BC hosszúságú memóriaterületet. Lényeges különbség, hogy a keresés leáll a blokk vége előtt, ha közben megtalálta a keresett karaktert (A = (HL) volt). Ezt a Z = 1 jelzőbit mutatja
CPD CP (HL)
HL = HL - 1
BC = BC - 1 (egy lépésben)
CPDR automatikus ismétlés felülről lefelé, míg meg nem találja a keresett karaktert (vagy a blokk végére nem ér)

A jelzőbitek:

A CP utasítások leggyakrabban a feltételes elágazások kérdései (IF A = N...).
Igen praktikus, hogy a processzor képes az operandusok közvetlen (akkumulátortól független) növelésére és csökkentésére. Például:

INC B
1-gyel növeli a B regiszter tartalmát,
DEC B 1-gyel csökkenti a B regiszter tartalmát.

Az operandusok köre megegyezik az aritmetikai utasítások címzésmódjaival, kivéve a nyilván értelmetlen INC n típust:

A
B
C
D
E
H
L
(HL)
(IX+d)
(IY+d)
X
X
X
X
X
X
X
X
X
X

A jelzőbitek:

A két utasítás elsősorban számlálásra, ciklusszervezésre használható.

16-bites aritmetikai utasítások
A Z80-as processzor kétbájtos operandusokon is képes az elemi aritmetikai műveletek elvégzésére. Ilyenkor az alsó bájtról keletkező átvitelt automatikusan figyelembe veszi a felső bájtok összeadásánál, kivonásánál. A művelet egyik operandusa mindig a HL regiszterpár (ill. ennek kiterjesztéseként az ADD utasításnál IX vagy IY) és az eredményt is az első operandusban kapjuk. Típusai:

ADD HL,BC HL = HL + BC
ADC HL,BC HL = HL + BC + carry
SBC HL,BC HL = HL - BC - carry,

Címzésmódjai:

 
BC
DE
HL
SP
IX
IY
ADD HL,
X
X
X
X
   
ADD IX,
X
X
 
X
X
 
ADD IY,
X
X
 
X
 
X
ADD HL,
X
X
X
X
   
SBC HL,
X
X
X
X
   

Az utasítások hatása a jelzőbitekre:

Itt is nézzünk meg egy-két esetet. Például:

M9 LD HL,7FE0
LD DE,0040
ADC HL,DE
...
21 E0 7F
11 40 00
ED 5A

Általában a C-bit törléséről is gondoskodni kell az ADC utasítás előtt (ha nem éppen az, átvitel figyelembevétele a célunk), pL. egy OR A utasítással. Itt ezt elhagyhattuk, a bevezető szakasz törölte az átvitelbitet. Az adott értékekkel:

260 CODE M=HEX$("21,E0,7F,11,40,00,ED,5A")
F: 10,1,100
A=00
B,C=00,00
D,E=00,40
H,L=80,20

Itt is mi dönthetjük el, hogy milyen számábrázolási tartományban dolgozunk és ennek megfelelően helyes-e az eredmény, történt-e átvitel. Kétbájtos pozitív egészként az eredmény elfogadható, hiszen C = 0,
7FE0H + 0040H = 8020H
Előjeles számnak tekintve azonban már hibás az eredmény, hiszen
7FE0H + 0040H <> 7FE0H
Ezt a P/V = 1 érték jelzi.

HL = FFE0H és DE = 0040H értékekkel viszont:

260 CODE M=HEX$("21,E0,FF,11,40,00,ED,5A")
F: 00.1.001
A=00
B,C=00,00
D,E=00,40
H,L=00,20

Itt átvitel keletkezett (C = 1), tehát pozitív egészként az eredmény hibás:
FFE0H + 0040H = 10020H <> 0020H
A P/V = 0 érték viszont azt jelzi, hogy az előjeles számok tartományában helyes a művelet eredménye:
(-0020H) + 0040H = 0020H

A természetes aritmetikai alkalmazásokon kívül a fenti utasítások alkalmasak pl. két regiszterpár értékének összehasonlítására is. Például az
OR A
SBC HL,DE
ADD HL,DE

utasítások elvégzése után a regisztertartalmak változatlanok, de az átvitelbit:
C = 1, ha DE > HL
C = 0, ha DE<-HL

A processzor a 16-bites regiszterpárokon is képes az inkrementálás (növelés) és dekrementálás (csökkentés) elvégzésére. Például:
INC BC: BC = BC + 1
DEC BC: BC = BC - 1
A lehetséges operandusok a két műveletre:

 
BC
DE
HL
SP
IX
IY
INC
X
X
X
X
X
X
DEC
X
X
X
X
X
X

A jelzőbiteket a 16-bites INC és DEC utasítások nem változtatják (nincs tehát átvitel (carry) az FFFFH-0000H átlépésnél sem!).

Kiegészítő, általános célú utasítások

DAA az akkumulátor tartalmát BCD alakúra konvertálja az összeadások (ADD, ADC, INC) vagy kivonások (SUB, SBC, DEC, NEG) után.

Például a
LD A,08
ADD A,04

utasítások után az akkumulátorban a binárisan elvégzett összeadás eredménye van (0C)
Ha kiegészítjük a rutinrészletet egy DAA utasítással:

M11 LD A,8
ADD A,04
DAA
3E 08
C6 04
27

az átalakított eredmény (12H) a tízes számrendszerben elvégzett összeadás jegyeit adja.
Jelzőbitek:

CPL az akkumulátor tartalmát komplementálja (másképpen A = 255 - A, egyes komplemens)
Jelzőbitek:
-N és H= 1
- a többi bit nem változik
NEG
megfordítja az akkumulátor előjelét
A = -A (másképpen: A = 256 -A, kettes komplemens)
Jelzőbitek: a kivonás műveletnek megfelelően állítódnak. Az adott esetben C mindig 1 (kivéve, ha A = 0 volt), és P/V mindig 0, kivéve ha A = 80H volt. Erre az eredmény ugyancsak 80H, ami hibás, hiszen
-(-128) <> -128
SCF (Set Carry Flag): l-be állítja az átvitelbitet (C). Ezenkívül az N és H jelzőbitek törlődnek
CCF
(Complement Carry Flag): átbillenti a C-bitet. Ezenkívül N = 0, H határozatlan a művelet után. Az átvitelbit törlésére nincs közvetlen utasítás, de más egybájtos utasítással elérhető (pl. OR A).

1.2.4 Bitléptető és -rotáló utasítások
A bájtot alkotó bitsort léptetik balra vagy jobbra, pontosabban a magasabb vagy alacsonyabb helyi értékek felé. Attól függően, hogy mi történik a szélen kilépő bittel, ill. milyen bit lép be a szélen, több típusa van.

RLC B a B bitjeit balra forgatja; a 7. bitről kilépő érték visszakerül a 0. bitre és egyúttal az átvitelbitbe is ez íródik:

RRC B jobbra forgatás; 0. bitről kilépő érték visszakerül a 7. bitre és egyúttal az átvitelbitbe is:

RL B balra forgatás az átvitelbiten keresztül; a 7. bitről kilépő érték C-be kerül, de az átvitelbit előző értékét a 0. bitre tölti:

RR B ugyanez jobbra forgatással; a 0. bitről kilépő érték kerül C-be, de C előző értéke a 7. bitre töltődik:

SLA B a B regiszter bitjeinek balra léptetése; a kicsorduló 7. bit az átvitelbitbe kerül, a 0. bitbe 0-t tölt az utasítás:

SRA B jobbra léptetés; a 0. bitről kilépő érték az átvitelbe kerül. A 7. bit értéke változatlan marad:

SRL B jobbra léptet, mint az előző utasítás, de a 7. bitre itt 0 kerül:

Valamennyi utasítás a következő operandusokkal alkalmazható:

A
B
C
D
E
H
L
(HL)
(IX+d)
(IY+d)
X
X
X
X
X
X
X
X
X
X

Jelzőbitek:

Az első 4 utasításnak van egy egyszerűbb (csak az akkumulátorra használható, egybájtos, kevesebb jelzőbitet kezelő) -változata is:
RLCA
RRCA
RLA
RRA

Ezek funkcionálisan az RLC A, RRC A, RL A ill. RR A utasításokkal egyeznek meg, de érdemben csak az átviteljelző bitet állítják be (emellett itt is törlődik N és H).
A biteltoló utasítások elsődleges alkalmazási területe az aritmetika, a kettővel való szorzás, osztás. Egyértelmű az analógia, ha belegondolunk, hogy pL. tízes számrendszerben is úgy szorzunk tízzel, hogy a számjegy karaktereit egy helyi értékkel balra léptetjük, az utolsó helyre pedig 0-t írunk.
Több bájtos számok osztásánál, szorzásánál egyszerűen fel lehet használni az előző bájtról keletkezett átvitelt is. Osszuk el példaként a HL bemenő paramétert 2-vel:

M12 SRL H
RR L
CB 3C
CB 1D

A regiszterkiírató programot az osztórutin beírásán túl egy kissé ki is kell egészíteni az osztandó paraméter beviteléhez:

260 CODE M=HEX$("CB,3C,CB,1D")
285 INPUT PROMPT "HL=? ":HL
290 CALL USR(M,HL)

START
HL=? 256

F: 10. 0. 000
H,L=00,80

START
HL=? 9

F: 00.0.001
H,L=00,04

Az első bevitt érték 256 (100H), fele valóban megfelel a HL = 0080 kiírásnak. A második értékkel a helyes túlcsordulásjelzést szemléltetjük: 9/2 = 4,5, ennek felel meg a

HL = 0004 CY = 1

kijelzés.
Az utasítások másik gyakori alkalmazási területe a grafika, a képpontok jobbra-balra görgetése lehet.
A léptető utasítások kőzött két olyan is van, amely a BCD számok műveleteit segíti. Ezek nem biteket, hanem BCD számjegyeket (félbájtokat) léptetnek. Az operandus itt csak (HL) lehet (tehát a HL értéke által meghatározott memóriarekesz), átvitelként pedig az A regiszter alsó félbájtja (0-3. bit) szolgál.

RLD

balra forgatja HL BCD-jegyeit:
- (HL) alsó félbájtja a felsőbe kerül,
- (HL) felső félbájtja A alsó felébe kerül,
- A alsó félbájtja (HL) alsó 4 bitjére kerül,

RRD

fordított irányú forgatás: a félbájtok "forgalma":
- A alsó (HL) - felső
- (HL) felső - (HL) alsó
- (HL) alsó - A alsó

Jelzőbitek:

1.2.5 Bitkezelő utasítások
A Z80-as mikroprocesszor képes egybájtos operandusok tetszőleges bitjének lekérdezésére, törlésére vagy beírására. Nézzük meg példaként a B regiszter 3. bitjére vonatkozó utasítások mnemonikjait:

BIT 3,B az F regiszter Z bitjét a B regiszter 3. bitjének megfelelően állítja be:
Z = 1, ha BIT 3,B = 0 (Z státusz)
Z = 0, ha BIT 3,B = 1 (NZ státusz)
RES 3,B törli (0-ra állítja) a kijelölt bitet
SET 3,B beírja (1-re állítja) a kijelölt bitet

A lehetséges operandusok mindhárom utasításra azonosak:

 
A
B
C
D
E
H
L
(HL)
(IX+d)
(IY+d)
BIT n,
X
X
X
X
X
X
X
X
X
X
RES n,
X
X
X
X
X
X
X
X
X
X
SET n,
X
X
X
X
X
X
X
X
X
X

A kijelölt bit sorszáma (n) 0 és 7 közötti érték lehet.
Jelzőbitek:

Az utasítások a különböző jelzőfunkciójú bitek tesztelésén, beállításán túl jól használhatók pl. egy memóriacím tartományának megállapítására. A 2. fejezetben foglalkozunk részletesebben a memóriakiosztás, a lapozás kérdéseivel. Mint látni fogjuk, a 64 kbájtnyi címzési tartományt négy 16 kbájtos lapra osztjuk fel:

BIT 15,cím
BIT 14, cím
Címtartomány
DEC
HEX
0
0
0 - 16 383
0000 - 3FFF
0
1
16 384 - 32 767
4000 - 7FFF
1
0
32 768 - 49 151
8000 - BFFF
1
0
49 152 - 65 535
C000 - FFFF

Természetesen a tényleges lekérdezés (vagy beállítás) a kétbájtos cím felső bájtjainak 7. és 6. bitjén történik.

1.2.6 Ugróutasítások
A BASIC GOTO analógiája megtalálható a gépi kódú utasítások között is. Létezik feltétel nélküli ugróutasítás, amely egyszerűen a processzor programszámlálóját (PC) írja át, így a következő utasítást már az új memóriacímről olvassa a Z80-as, de a legnagyobb jelentősége a feltételes ugróutasításoknak van. Eddig számtalan utasítást láttunk, amely megfelelő módon beállította a státuszregiszter jelzőbitjeit, de most találkozunk az első olyan utasításcsoporttal, amely lekérdezni is képes ezeket a biteket (legalábbis egy részüket).

JP nn
feltétel nélküli ugrás az nn címre. A következő végrehajtandó utasítást az nn címről fogja beolvasni a processzor.
JP cc,nn

feltételes ugrás az nn címre. Itt cc a processzor státuszára vonatkozó rövidítés lehet: az elugrás feltétele.
A megadható feltételek és az érintett jelzőbitek:

  • NZ (Non Zero): ugrás, ha Z = 0
  • Z (Zero): ugrás, ha Z = 1
  • NC (Non Carry): ugrás, ha C = 0 (átvitel nincs)
  • C (Carry): ugrás, ha C = 1 (átvitel van)
  • PO (Parity Odd): ugrás, ha P/V = 0 (páratlan paritás)
  • PE (Parity Even): ugrás, ha P/V = 1 (páros paritás)
  • P (Positive): ugrás, ha S = 0 (pozitív előjel)
  • M (Negative): ugrás, ha S = 1 (negatív e1őjel)

Például a JP Z,0000 utasítás hatására a processzor - bárhol tartott is a programban - a 0000H címen folytatja a végrehajtást, de csak akkor, ha a státusz az utasítás beolvasásakor Z volt (zero, pl. egy kivonás eredményeként). Ez az utasítás tehát a BASIC
IF A = 0 THEN GOTO cím
analógiája. Ezzel az utasításcsoporttal lehet tehát feltételes elágazásokat létrehozni a gépi kódú programban, ami gyakorlatilag minden programhoz nélkülözhetetlen.
Az ugróutasítások másik csoportja nem a folytatás címét adja meg (két bájton), hanem az előre vagy visszafelé átlépendő utasítások számát. Ezek a relatív ugróutasítások. Mivel ezek argumentuma egyetlen bájt (egy előjeles kettes komplemens számnak tekintett érték), ezért így az utasítást követő CIM körüli (CIM - 128, CIM + 127) intervallumba lehet csak ugrani. Ez a kis hatósugár egy erős korlátozó tényező, de mégis gyakran alkalmazzuk ezt az ugrástípust a relatív címzésmód előnyei miatt:

A relatív ugrások is lehetnek feltétel nélküliek vagy feltételhez kötöttek, de itt kevesebb feltételt adhatunk meg:

JR n (Jump Relative): relatív ugrás
JR NZ,n relatív ugrás, ha Z = 0
JR Z,n relatív ugrás, ha Z = 1
JR NC,n relatív ugrás, ha C = 0
JR C,n relatív ugrás, ha C = 1

Az ugrás relatív címét elemi módon az átlépendő bájtok leszámolásával határozhatjuk meg (visszafelé ugrásnál a JR utasítás, az operandus és a következő utasítás 3 bájtját is figyelembe kell venni). Például:



CIM
JR CIM
LD HL,0000
LD A(,HL)
18 03
21 00 00
7E
illetve
CIM INC HL
LD A,(HL)
CP 40
JR NZ,CIM
RET
23
7E
FE 41
20 FA
C9

Az utóbbi rutinban (amely egyébként a memóriát pásztázza végig a belépési HL címtál egy A karaktert keresve) az átlépendő bájtok száma 6 (visszafelé: C9, FA, 20, 41, FE, 7E), tehát a relatív ugráscím: n = -6
Kettes komplemens ábrázolásban: n = 256 - 6 = 250 = FAH
Egy speciális - és igen hasznos - relatív ugróutasítás a DJNZ (Decrement and Jump if Non Zero), amely igen egyszerű ciklusszervezést tesz lehetővé:

DJNZ n B = B - 1, majd relatív ugrás, ha B nem lett 0.

Mindezeken kívül a Z80-as processzor ismer egy igen hatékony ugrástípust is, amikor az ugrás abszolút címét egy regiszterpár tartalmazza. Így lehet kiszámított ugrásokat végezni, vagy egy ugrástáblázatból kiolvasott rutincímre ugrani:

JP (HL) ugrás a HL címre (PC = HL)
JP (IX) PC = IX
JP (IY) PC = IY

Alkalmazási példaként írjuk át az állapotsor üzenetszövegének nagybetűs karaktereit (pl. 32 bájt hosszon) kisbetűkre. Egy blokkdiagram jól foglalja össze a megoldandó feladatokat:

Ennek megfelelően a gépi rutin:
M11

CIM






NEXT
LD HL,BEBE
LD B,20
LD A,(HL)
AND 7F
CP 40
JR C,NEXT
CP 5B
JR NC,NEXT
SET 5,(HL)
INC HL
DJNZ CIM
RET
21 BE BE
06 20
7E
E6 7F
FE 40
38 06
FE 5B
30 02
CB EE
23
10 F0
C9

BASIC betöltővel:

100 ALLOCATE 100
110 CODE M= HEX$("21,BE,BE,06,32,7E,E6,7F,FE,40,38,06,FE,5B,30,02,CB,EE,23,10,F0,C9")
120 CALL USR(M,0)
130 GOTO 130

1.2.7 Szubrutin utasítások
Egy összetettebb feladat áttekinthető, strukturált megoldásának feltétele a szubrutinok (a program más részeiből tetszés szerint hívható alprogramok) rendszerbe illeszthetősége. A Z80-as processzornál a szubrutin hívását a CALL, a szubrutinból való visszatérést a RET utasítások szolgálják. Mindkét utasítás alkalmazható feltétel nélkül vagy státuszfeltételhez kötötten.

CALL nn meghívja a memória nn címén kezdődő alprogramot. Ehhez:
-a veremtárba tölti a CALL-t követő utasítás címét (ide kell majd visszatérnie),
-a programszámlálóba tölti nn értékét, tehát a következő utasítást már az alprogram területéről olvassa be.
RET
visszatér az alprogramból a hívóhoz. Ehhez visszaolvassa a veremtárból a CALL-t követő utasítás címét és ezt tölti a programszámlálóba. Ez lesz tehát a következő végrehajtott utasítás.

A feltételes utasítások:
CALL cc,nn
RET cc

ahol cc a JP-utasításnál megismert 8-féle státuszfeltétel valamelyikének rövidítése:

 
NZ
Z
NC
C
PO
PE
P
M
cc:
X
X
X
X
X
X
X
X

Például a RET PO utasítás hatására a végrehajtás visszatér a veremben tárolt címre, ha az F regiszterben P/V = 0 (ami az előző művelet függvényében jelentheti azt, hogy az operandus páratlan paritású, de azt is, hogy érvényes előjeles egész).
A visszatérési cím az előbbiek szerint egy szubrutin futása alatt a veremtár legutoljára betárolt eleme. Ez a szubrutinkezelési mód a verem igen fegyelmezett használatát követeli meg. Egy pár nélküli PUSH vagy POP utasítás a rendszer összeomlását okozhatja, hiszen a legközelebbi RET hatására a Z80-as mindenképpen visszatérési címnek értelmezi a verem tetején talált kétbájtos értéket. Másrészt azonban programjainkban fel is használhatjuk ezt az információt: kiolvashatjuk, hogy honnan hívták a rutinunkat, onnan akár paramétereket is olvashatunk be (így működnek pl. a RST 30H (EXOS) és RST 10H (BASIC) funkcióhívások), esetleg tudatosan meg is változtathatjuk a visszatérési címet. Anélkül, hogy túlzottan előre akarnánk ugrani (a BASIC rendszerhez), elmondhatjuk, hogy a sokszor használt USR(CIM,HL) gépi kódú programhívó függvény egy olyan alprogram címével (mint visszatérési címmel) lép ki a felhasználói rutinba, amely alaphelyzetbe állítja a BASIC munkaterületét, majd HL értékét (mint előjeles kétbájtos konstanst) tölti oda. Így lehetséges, hogy pl. a PRINT USR ( ) hívás esetén a PRINT utasítás ezt az értéket írja ki. Ha ezt a visszatérési címet egyszerűen töröljük a tárból (egy POP utasítással), a BASIC munkaterületen a mi rutinunkban betöltött érték maradhat mint kiírandó (vagy máshogy felhasználható) érték. Például az 1. szegmens CA0BH rutinja a PI értékét tölti a munkaterületre. Használjuk fel ezt a ROM-programot szubrutinként:

M12 CALL CA0B
POP HL
RET
CD 0B CA
E1
C9

A program futtatása után PI értékét írja ki a rendszer:

100 ALLCATE 100
110 CODE M=HEX$("CD,0B,CA,E1,C9")
120 PRINT USR(M,0)

Folytatva a csoport további utasításainak bemutatását, két olyan RET típusú utasítással találkozunk (RETI és RETN), amelyeket az ENTERPRISE nem használ. Ezek a megszakításból, ill. nem maszkolható megszakításból való visszatérést kiváltó utasítások az alapfunkción túl még belső jelzőket is állítanak, ami az ENTERPRISE megszakítási rendszerében érdektelen.
A processzor rendelkezik néhány egybájtos szubrutinhívó utasítással is. Ezek a memóriaterület legelején, rögzített címen kezdődő rutinokat hívnak meg, hatásuk egyébként azonos a CALL utasításéval. Például:
RST 30H = CALL 0030H
A megengedett RST-utasítások (hexadecimális címekkel):

 
00
08
10
18
20
28
30
38
RST
X
X
X
X
X
X
X
X

Ezeket a hívásokat igen hatékonyan használja az operációs rendszer: az RST 30H utasítást az EXOS sajátította ki, az RST 08H, 10H, 18H, 20H utasításokat pedig a BASIC. Segítségükkel igen egyszerűen hívhatók a rendszerek nagybonyolultságú funkciói.

1.2.8 Input/Output (I/O) utasítások
A Z80-as processzor a 64 kbájtnyi memóriaterület elérése mellett 65 536 különböző Input/Output cím - ún. port - olvasására, írására is képes az IN és OUT utasításokkal. Az ENTERPRISE azonban fizikailag lényegesen kevesebb I/O portot használ. Ilyenek pl. a nagy bonyolultságú kiegészítő áramkörök (a NICK és a DAVE chip) különböző regiszterei. Ilyen regisztereket használ a gép az elérhető memóriaterület kiterjesztéséhez (a memória 16 kbájtos szegmenseinek a Z80-as címtartományába való lapozásához), a nyomtató kezeléshez, a videokijelzés bizonyos funkcióihoz, billentyűzet letapogatáshoz stb. Ezek címzéséhez bőségesen elegendő 1 bájt (256 I/O cím). A továbbiakban így adjuk meg az utasításokat (a Z80-as teljes I/O címzési lehetőségeit bármelyik Z80-as szakirodalom tartalmazza).

IN A,(n) az A regiszterbe olvassa az n című port adatát.
IN r,(C) bármelyik regiszterbe (r lehet: A, B; C, D, E, H, L) olvashatjuk az adatot. A port címét a C regiszter tartalma adja.
OUT (n),A az n című portra adja az akkumulátor tartalmát.
OUT (C),r a C tartalmának megfelelő portra adja az r regiszter tartalmát (r lehet A, B, C, D, E, H, L).

Jelzőbitek:
az IN A,(n), OUT (n),A és OUT (C),r utasítások nem változtatják a státuszt, míg az IN r,(C) utasítás végrehajtása után a Z, P/V (paritás), S és H bitek a beolvasott értéknek felelnek meg; N = 0 és C változatlan.

Az I/O utasításoknak van (a tömbmozgató utasításokkal analóg) adatsorozat átvitelét segítő változatuk is. Ezekben a kijelölt port címét mindig a C regiszter tartalmazza, míg az adat a HL címre kerül, ill. a HL cím tartalmát viszi ki a processzor a portra. Számlálóként a B regiszter szolgál.

INI C port adatát a HL címre írja
B = B-1
HL = HL + 1
INIR az INI utasítást automatikusan ismétli, amíg B = 0 nem lesz
IND IND: analóg az INI-vel, csak HL = HL - 1
INDR az IND utasítást automatikusan ismétli, amíg B = 0 nem lesz Ugyanezek az utasítások adatkivitelre:
OUTI a HL cím tartalmát a C portra viszi
B = B-1
HL = HL + 1
OTIR ismételt OUTI B = 0-ig
OUTD (HL) tartalma a C portra
B = B-1
HL = HL - 1
OTDR ismételt OUTD B = 0-ig

Jelzőbitek:

Alkalmazási példaként olvassuk be a billentyűzetmátrixot. Ehhez a mátrix tíz sorának számát sorra kiírjuk a B5H portra, mert ezzel jelöljük ki az olvasandó sort, majd az onnan beolvasott adatot (minden 0 bit egy lenyomott billentyűnek felel meg) kiírjuk az állapotsorba (ott természetesen az érték valamilyen karakterként jelenik majd meg):

M13



CIKL
LD HL,BEBE
LD C,00
LD ,0A
LA A,C
OUT (B5),A
IN A,(B5)
CPL
LD (HL),A
INC C
INC HL
DJNZ CIKL

BASIC betöltővel:

100 ALLOCATE 100
120 CODE M=HEX$= ("21,BE,BE,0E,00,06,0A,79,D3,B5,DB,B5,2F,77,0C,23,10,F5,C9")
120 CALL USR(M,0)
130 GOTO 120

1.2.9 A processzor vezérlőutasításai

NOP
üres utasítás, a processzor nem hajt végre semmilyen műveletet (a gépi kódú programban helykitöltésre, egy ütemnyi késleltetésre használható).
HALT
Felfüggeszti a processzor működését egy megszakításkérés érkezéséig. Elsősorban perifériaorientált rendszerben (pl. egy mérésvezérlő központi egységben) használják, ahol a processzor egyetlen feladata a külső eszköz kiszolgálása.

Maga a megszakításkérés a processzor INT vagy NMI csatlakozási pontjára (lábára) érkező impulzus. A kérést a processzor az első esetben csak akkor fogadja el, ha előzőleg a program engedélyezi a megszakítást, a második esetben pedig feltétel nélkül (NMI = Non Mascable Interrupt - nem maszkolható megszakítás).

EI
engedélyezi a megszakításokat
DI
letiltja a további maszkolható megszakításokat (a következő El utasításig).

A két utasítás természetesen csak a maszkolható megszakításokra vonatkozik. A pillanatnyi állapotot egy belső flag őrzi, amelynek értéke 1, ha a megszakítás engedélyezett, 0, ha tiltott. Az LD A,I vagy LD A,R utasítással olvashatjuk a státuszregiszterbe (P/V bit).
Hogy mit jelent az, hogy a processzor elfogadja a megszakításkérést, azt a pillanatnyi megszakítás üzemmód határozza meg. Maszkolható megszakításkérésnél először letiltja a további megszakításokat, majd a kérést a legutóbbi üzemmód-beállító utasítás szerint kezeli:

IM 0
(0-s megszakítás mód) a CPU azt az utasítást hajtja végre, amit a kiszolgálást kérő eszköz az adatbuszra ad.
IM 1
a processzor egy RST 38H (azaz CALL 0038H) utasítást hajt végre.
IM 2 a CPU egy olyan szubrutint hív meg, amelynek címét egy memóriabeli
táblázat tartalmazza. A táblázat adott elemének címét részben a megszakítást
kérő eszköz határozhatja meg:
- cím felső bájt: I regiszter,
- cím alsó bájt: a periféria adhatja a buszra.

Nem maszkolható megszakításkérésnél a processzor egy RST 66H utasítást hajt végre.
Az ENTERPRISE megszakítás rendszere az IM 1-es módot használja. Ez ugyan önmagában nem a leghatékonyabb (hiszen minden megszakításkérést a 0038H szubrutin szolgál ki), de a hardver megoldás még az IM 2-es módnál is nagyobb hatásfokúvá teszi. A megszakításkérések ui. nem közvetlenül, hanem egy kiegészítő áramkör (a DAVE chip) közvetítésével jutnak a processzorra. A megszakítás forrását a DAVE regiszterei alapján szoftver úton lehet megállapítani, sőt - ami minőségileg újat jelent - külön-külön is le lehet tiltani az egyes források kéréseit. A megszakítás rendszerről a 2.4 alfejezetben írunk bővebben.

A Z80-as mikroprocesszor megismert utasításkészletével nekivághatunk a gépi kódú programozásnak. Az eddig közölt példák megmutatták, hogy a közvetlen, kódokkal való programozás meglehetősen körülményes (főleg a relatív ugrások számítása nehézkes). Néhány tucat, esetleg 1-200 utasítás fölött már feltétlenül érdemes a gépi kódú programozást igen megkönnyítő segédprogramot, assemblert alkalmazni.
A mintapéldákban eddig is megadtuk a kódsorozat mellett az utasítások mnemonikjait. Például egy az akkumulátorba az A kódját töltő, majd vissztérő rutin gépi kódban:

3E 41 C9

Ennek assembly nyelvű kifejezése:

LD A,41 H
RET

Az assembly nyelven megírt programot gépi kódok sorozatára lefordító segédprogram az assembler. Valójában egy jó assembler lényegesen többet nyújt a mnemonikok kódokkal való helyettesítésénél. A relatív ugrásokat, szubrutinhívásokat stb. segítő címkéket használhatunk, operandusként paraméterek is megengedettek, sőt az aritmetikai műveletek egy részét is használhatjuk stb. A nyújtott szolgáltatásokat mindig a kiválasztott assembler leírása adja meg. Mi a talán legelterjedtebb SIMON (ASMON) (editor, assembler és monitor) programot használtuk. Ennek szabályait a 9. Függelékben foglaljuk össze. Most nézzünk meg egy-két olyan szintaktikai szabályt, amely a következő fejezetek mintapéldáinak, leírásainak megértéséhez szükséges.

A működés bemutatására, magyarázatára megadott ROM-részleteket ugyancsak a SIMON programmal disassembláltuk; fordítottuk vissza a kódsorozatot jól követhető assembly listává. Ezekben a listákban viszont a SIMON minden értéket (címet és operandust) hexadecimálisan ad meg, külön H jelzés nélkül.
Ezek után ismerkedjünk meg a gép két fő rendszerének felépítésével, működésével: a beépített operációs rendszer (az EXOS) és a leggyakrabban használt felhasználói program, az IS-BASIC gépi kódú programjainknak környezetet adó munkaterületeivel, változóival, általunk is felhasználható rutinjaival.

2. A háttérrendszer

2.1 A memóriaszervezés
Kezdjük a gép megismerését a memóriaszervezés tárgyalásával, hiszen ez minden későbbi működési, felhasználási információ alapja.
Az ENTERPRISE számítógép egyik fontos tulajdonsága, hogy igen nagy memóriaterületet kezel: akár 4 Mbájtot (256 * 16 384 bájtot). Ugyanakkor a gép központi egysége (a Z80-as mikroprocesszor) 16 címvezetékével csak 2 ^16 = 64 kbájtot (azaz 4 * 16 384 bájtot) képes megkülönböztetni. Ezt az ellentmondást csak hardver segédlettel lehet feloldani: a CPU címzési tartományát 4 - egyenként 16 kbájtos - intervallumra, ún. lapokra osztjuk fel és ezekre a területekre a gép nagy bonyolultságú periféria-áramköre (a DAVE-chip) lapozza az elérhető 256 db 16 kbájtos memóriaszelet bármelyikét (az ún. szegmenseket). A különböző memória-áramkörök áramkörbe csatlakoztatásával egyértelműen rögzítődik azok szegmensszáma. A szegmensszám tehát kötött fizikai paraméter: adott szegmenshez mindig (bármilyen lapozásnál) ugyanazok a memóriarekeszek tartoznak. Azt, hogy a CPU a 4 memóriatartományban - mint ablakon - melyik szegmenst látja, a DAVE-chip 4 írható, olvasható regisztere határozza meg:

176. regiszter (B0H): 0. LAP
177. regiszter (B1H): 1. LAP
178. regiszter (B2H): 2. LAP
179. regiszter (B3H): 3. LAP

A lapregiszterek mindegyike tetszőleges értékre beállítható a Z80-as CPU OUT utasításával (annak sincs akadálya, hogy akár mind a 4 lapon ugyanazt a szegmenst lássa a mikroprocesszor). Itt jegyezzük meg, hogy ugyanez a lapozás elvégezhető a BASIC OUT utasításával is, de ezt csak az 1. és 2. LAP-on használhatjuk, a 0. és 3. LAP átírása a rendszer összeomlását okozhatja. Az egyes lapokhoz tartozó címtartományok:

0. Lap:
0 - 16 383
(0000H - 3FFFH)
1. Lap:
16 384 - 32 767
(4000H - 7FFFH)
2. Lap:
32 768 - 49 151
(8000H - BFFFH)
3. Lap:
49 152 - 65 535
(C000H - FFFFH)

Az előbbiek szerint a

LD A,01H
OUT (B2H),A

utasítások végrehajtása után az 1. szegmens lapozódik a 2. LAP-ra: a 32 768 és 49 151 címtartományba eső minden írási vagy olvasási utasítást a DAVE-chip automatikusan az 1. szegmens megfelelő rekeszeihez irányít. Ez a lapozási technika az ENTERPRISE működtető szoftverének, operációs rendszerének egyik legmarkánsabb sajátossága, és egyben - sajnos - működési sebességének korlátozója is. Szerencsére (mint látni fogjuk) a szoftver sok eleme támogatja ezt a tárkezelést.
A következő kérdés, hogy milyen szegmenseken találunk ténylegesen tárakat, és milyen típusúak azok. Általánosan igaz, hogy a legalacsonyabb sorszámú szegmenseken a ROM-ok találhatóak: azok a tárak, amelyek a gépet működtető szoftvert, a gyárilag beégetett operációs rendszert, a BASIC értelmezőt stb. tartalmazzák. A saját fejlesztésű (vagy magnetofonról beolvasott) programokat, változó adatokat, képeket stb. befogadó RAM-memóriák felülről lefelé (255-től) számozódnak.
A továbbiakban példaként választott géptípus - a 128 kbájt RAM-mal felszerelt angol és német nyelvű verzió - esetén konkrétan a következő szegmenseket találjuk a gépben (csatlakoztatott IS-BASIC bővítő modul esetén):

A német (BRD) és angol (UK) BASIC szegmensek sorszáma gépenként (szériától függően) eltérő lehet, akár meg is cserélődhet. Ezt az operációs rendszer hidegindításkor figyelembe veszi. Mivel egyes alkalmazásoknál szükséges a konkrét szegmensszámok ismerete, javasoljuk az Olvasónak, hogy - pl. a következő fejezetek alapján - állapítsa meg saját gépe szegmenskiosztását. Mi a továbbiakban a választott példa szegmensszámait használjuk.

RAM szegmensek (8 * 16K)
248. szegmens (F8H)
249. szegmens (F9H)
250. szegmens (FAH)
251. szegmens (FBH)
252. szegmens (FCH)
253. szegmens (FDH)
254. szegmens (FEH)
255. szegmens (FFH)

A felsorolt RAM-szegmensek közül a felső 4-nek (FCH-FFH) kiemelt jelentőséget ad az a tény, hogy a videoáramkör (a képernyőn való megjelenítést végző NICK-chip) csak ezt a tartományt látja, ezt a 64 kbájtos memóriaterületet képes elérni.
A NICK-chip által használt videocím kötött, lapozástól független. Adott videocímen mindig ugyanaz a memória érhető el (akkor is, ha a Z80-as számára az adott szegmens be sincs lapozva!):

Videócím
Szegmens
0000 - 3FFFH
FCH
4000 - 7FFFH
FDH
8000 - BFFFH
FEH
C000 - FFFFH
FFH

 

2.2 Az operációs rendszer szerkezete
A továbbiakban ismerkedjünk meg a háttérszoftver szerkezetével, a gép működését szervező, irányító operációs rendszer működési logikájával.
A szoftver lelke az EXOS (EXtendable Operating System - bővíthető operációs rendszer). A 2.1-es EXOS változat funkcionális ismertetését a "Műszaki leírás" adja meg. Mi más szemmel tekintjük át a rendszert. Két legfontosabb - gépi kódú programozásnál alapvető jelentőségű - szempontunk az lesz, hogy

Mindenekelőtt tekintsük át az EXOS alapfeladatait.

  1. Bekapcsolása után inicializálja a rendszert (ezt kissé részletesebben áttekintjük a 2.3.1 pontban):
    -ellenőrzi a különböző memóriaterületeket és típusokat (ROM vagy RAM) az eredménynek megfelelően könyvtárazza azokat,
    -kijelzésre alkalmas állapotot hoz létre a VIDEO áramkör (NICK-chip) számára;
    -alapértéket ad a rendszerváltozóknak, RAM-ba helyez bizonyos nélkülözhetetlen rendszerrutinokat;
    -listát készít a rendszer-ROM-ban és a bővítőkben talált perifériakezelőkről.
  2. Lehetőséget ad a bővítőben csatlakoztatott alkalmazói programnak a vezérlés átvételére (ha nincs ilyen program, a szövegszerkesztő kapja meg a vezérlést).
  3. A háttérben továbbra is ellát minden olyan feladatot (az alkalmazói programban kiadott funkcióhívások útján), amely a memóriaszervezéssel, a perifériák írásával, olvasásával és a csatornaműveletekkel kapcsolatos.

A két belső ROM-ban tárolt alapszoftver a következőket tartalmazza:

2.3 Az EXOS működése

Az EXOS - operációs rendszer létére - szokatlanul sokoldalú, meglepően sok feladatot lát el. Ezek között vannak természetes alapfeladatok, pl. a különböző memóriaterületek ellenőrzése, alaphelyzetbe állítása stb., de - különösen a képernyőkezelés területén - ellát olyan feladatokat is, amelyek egy alkalmazói programnak is becsületére válnának. Elég azt megemlíteni, hogy ez a rendszer tartalmaz pl. ellipszisrajzoló vagy a zárt grafikus alakzatokat kitöltő alprogramokat is (a VIDEO perifériakezelőben). Tulajdonképpen a gép - igen komfortos - BASIC-jének alig van olyan (perifériákkal kapcsolatos) utasítása, amit ne támogatna az EXOS.
Ez a tény nagyon leegyszerűsíti a gépi kódú programozás bizonyos területeit is, hiszen bonyolult feladatokat oldhatunk meg egyetlen funkcióhívással (pl. képernyőlap kijelzése) anélkül, hogy akár a végrehajtó rutinok, akár az érintett memóriaterületek címeit ismernünk kellene.
Az EXOS bemutatása során - bár nem ROM-fejtés közlése a célunk - néhány belső rutint is elemzünk, részben a profi gépi kódú programozás tanulságai érdekében részben a felhasználás feltételeinek megismeréséhez. A megértéshez, felhasználáshoz példákat is adunk, általában BASIC-ben felépítve, hogy a gép elé leülő, a rendszerrel most ismerkedő Olvasó azonnal kipróbálhassa, ellenőrizhesse a leírtakat.
Ez annál is fontosabb, mert a forgalmazott gépek között több változat is előfordult. Az operációs rendszer alaptulajdonsága - nevéhez méltóan - a kiterjeszthetőség, bővíthetőség. A személyi számítógépek körében szinte páratlan mértékben képes memóriamérethez, verzióhoz alkalmazkodni. Ez a nagyfokú rugalmasság természetesen azzal jár, hogy a memóriakiosztás is képlékeny. Igazán hordozható, minden verzióban működő programot tehát csak az EXOS rendszer, a funkcióhívások felhasználásával építhetünk fel. Egy adott gépen dolgozva azonban mindenképpen praktikus a gép megismerése, feltérképezése, hiszen a konkrét adatok birtokában gyakran sokkal hatékonyabban (rövidebb programokkal és gyorsabban) oldhatjuk meg a feladatokat. A mi példáink - mint leírtuk - a 128 kbájtos, angol/német változatra vonatkoznak. A saját gépén az Olvasónak feltétlenül érdemes a közös báziscímekből kiindulva megállapítani (és rögzíteni) a konkrét adatokat és címeket.

2.3.1 Inicializálás

A bekapcsolás után az EXOS hidegindítási főága hozza létre azt a jól definiált hátteret, amelyben minden felhasználói program dolgozhat. Gépi kódú programot írva számunkra is ez lehet a munkánk bázisa, ez szabja meg a lehetőségeket és a korlátokat. Kövessük tehát végig a rendszer felépítésének folyamatát, természetesen azokat a részeket, elemeket kiemelve, amelyek valamilyen szempontból fontosak lesznek a későbbiekben.
A feszültség rákapcsolásakor a processzor a 0. szegmens legelső utasításait kezdi végrehajtani. Még a 0. LAP-on letiltja a megszakításokat és egyszer s mindenkorra az 1. megszakítási módba kapcsolja a processzort (részletesebben l. a 2.4 alfejezetben). Ezután elugrik a 3. LAP-ra, ahol - bekapcsolás után - szintén a 0. szegmens van belapozva, és most már itt folytatja a futást.
Érdemes megjegyezni, hogy a továbbiakban a különböző rendszerprogramok tartózkodási helye szinte kizárólag a 3. LAP lesz. Ide lapoz be minden programot, kezelőt is az EXOS, mielőtt átadná nekik a vezérlést. Ezért mi is a 3. LAP-on értelmezett címeket használjuk, pl. egy disassemblált ROM-részletben. Így lesz a 0. szegmens legelső bájtja a 0000H című utasítás, a szegmens végcíme pedig a FFFFH.

Memóriaellenőrzés és könyvtárazás
A 3. LAP-on elvégzett első érdemi funkció azonnal érdekes lehet a felhasználó számára. A 0. LAP-ra lapozott 4. szegmensen (ide, a bővítő cartridge-ban lévő EPROM-ba elvileg saját beégetett programunkat is tehettük) ellenőrzi, hogy nem a TEST-ROM szöveggel (54H, 45H, 53H, 54H, 5FH, 52H, 4FH, 4DH bájt-sorozat) kezdődik-e. Ha igen, azonnal el is ugrik a szöveget követő bájtra. Az EXOS rugalmasságára jellemző, hogy már az inicializálás pillanatában hajlandó átadni a vezérlést a felhasználónak, aki így akár egy teljesen más karakterű gépet is kihozhat az adott hardverből.
Mi azonban az ENTERPRISE alaphelyzetét, memóriakiosztását szeretnénk megismerni. Kövessük tehát tovább a főágat, a hidegindítás folyamatát.
Az EXOS külön ellenőrzi a később fontos célra szánt 255. szegmenet (FFH). Az ellenőrzés módszerét érdemes áttekinteni a később valamennyi RAM-szegmens vizsgálatára felhasznált rövid ROM-részletben. A vizsgált szegmens az 1. LAP-on van (4000H-7FFFH címtartomány), a teszteléshez felhasznált értékek a 0. szegmens (ROM) C000H-FFFFH bájtjai.

C20E
C211
C214
C215
C216
C217
C218
C219
C21A
C21B
C21C
C21E
21 00 40
01 00 C0
0A
77
BE
C0
2F
77
23
03
CB 78
20 F4
LD HL,4000
LD BC,C000
LD A,(BC)
LD (HL),A
CP (HL)
RET NZ
CPL
LD (HL),A
INC HL
INC BC
BIT 7,B
JR NZ,C214
; vizsgált szegmens (RAM)
; mintaszegmens (ROM)
; minta
; a RAM-ba
; tárolódott?
; vissza, ha nem RAM (NZ)
; komplementer
; tárolása
; következő RAM
; és minta
; 3. LAP-cím felső bit
; tovább, ha cím<=FFFFH
; Szegmens vége: visszafelé újra megvizsgálja a letárolt értékeket:
C220
C221
C222
C224

C225
C226
C227
C228
C229
C22A
0B
2B
CB 74
C8

0A
86
3C
77
C0
18 F4
DEC BC
DEC HL
BIT 6,H
RET Z

LD A,(BC)
ADD A,(HL)
INC A
LD (HL),A
RET NZ
JR C220
; következő minta
; és RAM-cím
; RAM-cím>=4000H?
; kész az ellenőrzés, ha
; vége a szegmensnek (Z)
; minta
; + saját komplementere
; +1 (=0, ha jól tárolt)
; 0 -> RAM
; RAM-hiba, ha nem 0
; tovább ellenőriz

Azt láthatjuk, hogy a tesztelés utolsó mozzanataként minden RAM-címre 0-t tölt a rutin. A RAM-szegmensek tehát törölve kerülnek ki az ellenőrzésből.
Ha a szegmens hibátlan, ez lesz a rendszerszegmens, ez tartalmazza majd a rendszerváltozókat, a báziscímeket, az EXOS futása alatt a Z80-as-vermet stb. Érthető tehát, hogy ha hibát talált az ellenőrzés, a rendszer ezen a ponton lefagy; pörgő keretszínnel végtelen ciklusba kerül:

C0AF
C0B0
C0B2
3D
D3 81
18 FB
DEC A
OUT (81),A
JR C0AF
; az összes szín előfordul
; keretszín beállítása
; végtelen ciklusban

Ezt igen könnyen kipróbálhatjuk, akár BASIC-ben is:

OUT 177,0
CALL USR (16559,0)
! A 0. szg. az 1 LAP-ra
! 40AFH belépési cím

(itt még szerencsére segít a RESET gomb).

A Z80-as veremmutató (Stack Pointer) az ellenőrzés alatt ideiglenesen a még ellenőrizetlen 254. szegmensbe kerül, de a továbbiakban (és általában az EXOS-ban) SP = B217H-ról indul a 255. (rendszer-) szegmensben.
A további ellenőrzés már kijelzés mellett folyik, de ennek még vannak előfeltételei: ebben a szakaszban még nincs meg a NICK-chip működéséhez nélkülözhetetlen sorparaméter-tábla (LPT), sőt a karaktergenerátor (a kijelezhető karakterek alakját, pontmátrixát tároló memóriaterület) sem. Mindezt a VIDEO perifériakezelő inicializálás rutinjának meghívásával (0. SZG. D260H belépési cím) hozza létre az EXOS.
A rutin hívása után a képernyő sötét, a képernyő legfelső sorában (az ún. állapotsorban) megjeleníthető memóriaterület pedig a belapozott rendszerszegmens BEB8H címén kezdődik. Ide tölti be a program közvetlenül a kijelzendő karaktereket, miután az állapotsort láthatóvá tette.
A következő lépésben a saját ROM-jait (0. és 1. szegmens) ellenőrzi az EXOS (ellenőrző összeg 0). Természetesen itt sem léphet tovább, ha hibát talál (ebben az esetben az "INTERNAL CHEKSUM ERROR" feliratot ír az előkészített állapotsorba).
Ezek után a RAM-szegmensek ellenőrzése következik, a már megismert módon. Felülről lefelé (a 254.-től a 8.-ig) végigvizsgál minden szegmenst (részletesen csak azokat, ahol legalább az első címen írható memóriát talált). Ez a kulcsa annak, hogy tetszőleges memóriakiterjesztés esetén korrekt RAM-könyvtárat készíthessen a rendszer.
Eközben az állapotsorban az

EXOS 2.1 TESTING ERROR

felirat látható (ill. az "ERROR" remélhetőleg nem, hiszen jó RAM-ok esetén ez fekete színű). A látható sor végére viszont kiírja (hexadecimális alakban) az ellenőrzött szegmens számát.
Az ellenőrzés során számlálja a jó és rossz RAM-okat és elkészíti a jó RAM-szegmensek táblázatát: az ABD0H címtől lefelé sorra bejegyzi minden jónak talált RAM szegmensszámát. Az eljárás végére ki is alakul néhány fontos rendszerváltozó értéke (zárójelben a vizsgált verzióban, hibamentes esetben adódó értékek):

BF9A / BH: a RAM-szegmensek táblázatának kezdőcíme (ABD0H)
BF9FH: a szabad szegmensek száma (6)
BFA2H: a rendszerszegmensek száma (1)
BFA3H: az összes működő RAM-szegmensek száma (8)
BFA4H: a hibás RAM-szegmensek száma (0)
BFFCH: a 0. LAP szegmense; USER-P0 (F8H)

A RAM-tábla:

ABCAH      
ABD0H
F9H
FAH
FBH
FCH
FDH
FEH
FFH

A szabad és rendszerszegmensek kezelésekor az EXOS nem veszi figyelembe a. legalsó jó RAM-ot (esetünkben a 248. (F8H) szegmens), ami igencsak kötött felhasználású: ez lesz majd a 0. LAP gyakorlatilag állandóan belapozott szegmense. Így érthető, hogy a rendszer leállásához vezet, ha az ellenőrzés nem talál legalább még egy jó szegmenst a már ellenőrzött 255. szegmens mellé.
Ezzel alakul ki a felsorolt változók értéke: a talált nyolc RAM-szegmensből hetet vehet figyelembe az EXOS (F9H-FFH), ebből a legmagasabb sorszámút (FFH) a rendszer már igénybe vette, tehát hat szegmens marad a felhasználó számára szabadon.
Mielőtt továbbmennénk, álljunk meg egy pillanatra egy terminológiai problémánál. Az előbbiekben megismert RAM-szegmens táblázathoz hasonlóan szinte minden táblázat, lánc felülről lefelé épül fel. Egy táblázat utáni bájt címe tehát (a táblázat végcíme -1).
Hogy elkerüljük a fogalmakban rejlő ellentmondást - és ezzel a félreértéseket -, a továbbiakban az előtti, utáni megjelölések helyett az adott esetekben az alatti, fölötti kifejezéseket használjuk (a "fölső" alatt természetesen mindig a magasabb címet értve).

A ROM-szegmensek könyvtárazása
Az inicializálás folyamatában a RAM-ok ellenőrzése után a következő lépés a ROM-ok nyilvántartásba vétele. Először a - tizenhatos számrendszerben - kerek értékű szegmenseket (F0H, E0H, ... 10H) vizsgálja végig a rendszer: ellenőrzi, hogy EXOS-ROM karakterekkel (45H, 58H, 4FH, 53H, 5FH, 52H, 4FH, 4DH bájtokkal) kezdődik-e az adott szegmens.
Ha igen, könyvtárazza azt. A RAM-szegmensek táblázata alatt felépített ROM-szegmens táblázatban 4-4 bájt tartozik minden bejegyzett ROM-hoz: a legfelső bájt a ROM-szegmens száma, alá majd később jegyzi be a rendszer a ROM számára esetleg kiosztott RAM-terület szegmensszámát és címét. Ha - pl. címdekódolási okok miatt - több szegmensszámon is megjelenik az adott ROM, az EXOS csak a legalsó számot veszi fel a nyilvántartásba.
Ezekre a kerek címekre vagy a 7.-4. szegmensre kell tehát dekódolni (és a fenti bájtokkal kezdeni) az IS-BASIC melletti további kiterjesztésre szánt esetleges EPROM-okat.
A következő lépésben a 7.-4. szegmenseket ellenőrzi és könyvtárazza a rendszer (esetünkben), végül bejegyzi a táblázatba az 1. szegmensszámot is. Az elkészített táblázat ebben a fázisban a vizsgált alapkiépítésű gép esetén:

ABB9H 00
ABBAH 00 00 00 01
ABBEH 00 00 00 04
ABC2H 00 00 00 05
ABC6H 00 00 00 00
; tábla vége (alsó bájt)
; 1. szegmens
; 4. szegmens
; 5. szegmens
; a tábla felső vége

Az EXOS beállítja a tábla két végét jelző rendszerváltozókat is:

BF9C / FH:
BF97 / 8H
a ROM-tábla legfelső bájtjára mutat (ABC9H)
a ROM-tábla legalsó bájtjára mutat (ABB9H)

Egyelőre ezzel azonos értékűre állítja a rendszer munkaterületének kezdőcímét is (BF95/6H, értéke ekkor ABB9H).
A táblázatok, elkészítése után az EXOS alapértéket ad néhány rendszerváltozónak:

BFE8H:
BFD5H:
BFE7H:
LV-TAPE (2, azaz 40 mV)
BAUD-SER (0FH, azaz 9600 bit/s)
PROTECT (FFH, azaz védett)

Rendszerrutinok kiépítése
Az inicializálás következő lépése a rendszer működése szempontjából alapvető fontosságú két RAM-terület feltöltése:

A nullás lapon kialakított terület lesz az EXOS n funkcióhívások belépési és visszatérési ága.
A rendszerszegmens B217H-B33DH területén kialakított alprogram az EXOS lapozási technikájának alaprutinja. Mint már említettük, a különböző rendszerek programjai általában a 3. LAP-on futnak. Ha tehát át akarunk térni egy másik szegmensre (vagy meg akarjuk hívni annak valamelyik alprogramját), meg kell oldanunk a lapozás alapproblémáját: az OUT (B3H),A utasítás nem lehet a 3. LAP-on futó program része, hiszen a végrehajtás után a következő bájtot (utasítást) már az átlapozott szegmensről olvassa be a CPU. Nincs tehát időnk a JP vagy CALL utasítás kiadására. Az átlapozásnak feltétlenül másik szegmensen kell történnie.
Az EXOS erre a célra a 2. LAP-on látszó rendszerszegmenst - abban az előbb bemásolt rutint - használja:


B217
B219
B21A
B21D
B21E
B21F
B221
B222

B223
B225

B226
B227
B229
B22A
B22D

D3 B3
0B
CD 25 B2
0B
AF
D3 B3
08
C9

D3 B3
E9

7C
D3 B3
7D
2A 7C BF
C3 51 00

OUT (B3),A
EX AF,AF'
CALL B225
EX AF,AF'
XOR A
OUT (B3),A
EX AF,AF'
RET

OUT (B3),A
JP (HL)

LD A,H
OUT (B3),A
LD A,L
LD HL,(BF7C)
JP 0051
; az A-ban hozott szegmenst
; belapozza a 3. LAP-ra
; átadandó reg. és státusz
; lényegében CALL (HL)
;
; a 0. szegmenst
; lapozza vissza
; visszaadott reg. + státusz
;

; hozott SZG. a 3. LAP-ra
; a célprogramra ugrik

; ereseti 3. LAP
; visszalapozása
; eredeti A
; eredeti HL
; EXOS visszatérési főágra

B230
B238
B240
B248
B250

57 72 69 74 74 65 6E 20
62 79 3A A0 CD F2 EC A0
C2 D4 A0 CE CD D6 A0 C7
CE C8 A0 C3 C7 C5 A0 C1
C5 CC

Written
by: Mrl
BT NMV G
NH CGE A
EL

 

B217H belépésekor A-ban és HL-ben a célrutin szegmensszáma és belépési címe van, AF'-ben paramétert és státuszt adhatunk át, a visszaadott paramétert és státuszt az AF-ben kapjuk meg. A többi regiszter szabadon használható.
A B223H belépés tetszőleges JP(HL), a B226H-B22DH szakasz pedig az EXOS n funkcióhívások visszatérési ágát szolgálja ki.
A rutinokkal együtt a rendszerszegmensbe másolt további bájtok copyright jellegű üzenetet hordoznak: a BF30H-BF51H területen a "Written by: Mrl BT NMV GNH CGE AEL" szöveg található, amit meg is jeleníthetünk az állapotsorban a POKE 49119,42 utasítással (l. a 2.4 pontot).
A lapozó rutin alapvető fontossága alapján érthető, hogy a rendszer fokozottan védi ezt a memóriaterületet: megváltoztatása azt eredményezi, hogy még a melegindítási kísérlet is (RESET) hidegindításba fordul. Már kevésbé indokolt a terület végének (a szövegtartománynak) ez a nagyfokú védelme. Ellentmondásban is áll a gép - egyébként minden ízében érezhető - felhasználó-centrikusságával, hiszen ezzel egy igen kényelmesen használható képernyőüzenet területet tettek problematikussá.
A rendszer felépítésének folytatásához az EXOS átugrik az 1. szegmens C00DH címére az előbbiekben tárgyalt lapozórutin segítségével, miután az egyébként sokoldalú belépési ponthoz a hidegindítást biztosító paramétert állított be (A = 6).
Az 1. szegmensen a vezérlés azonnal az E40FH címre, innen - A értéke alapján - az E66EH címre adódik. Itt folytatódnak a hidegindítás funkciói.
Itt a rendszer mindenekelőtt egy EXOS 0 funkcióhívást ad ki, C = 20H RESET jelzővel. A funkcióhívásokról a következő pontban írunk részletesebben, de mivel ez a hívás hozza létre azt a háttérállapotot, memóriakiosztást, amivel a felhasználó gazdálkodhat, kövessük itt is a hidegindítás folyamatát!
A funkcióhívások közös bevezető szakasza után az EXOS 0 végrehajtása (újra a 0. szegmensen) a C653H címen kezdődik. A rendszer mindenekelőtt törli (0-ra állítja) a

BFF8/9 RST ADDR
003D/EH SOFT-ISR
BFED/EH USER-ISR

rendszerváltozókat. Ezután a végrehajtás szétválik a RESET jelzők alapján. Esetünkben (C = 20H) a C23AH rutin következik.

A RAM-igények kielégítése
Az első érdemi funkció, amit itt a hidegindítás során elvégez, további EXOS változók alapértékének beállítása (ezeket a funkcióhívás leírásánál adjuk meg). Ezután viszont a RAM-felhasználást alapvetően érintő lépés következik: az EXOS végigkérdezi az előbbiekben felépített ROM-táblázat bekönyvtározott ROM-jait, hogy igényelnek-e RAM-ot a rendszertől. Mivel ez fontos lépés a memóriatérkép kialakításában, másrészt az igénylés és a kielégítés módja is fontos lehet a gépi kódban programozó számára, tekintsük át az eljárást. Az áttekinthetőség érdekében azt az utat kövessük, amit a rendszer az eddig tárgyalt alapkiépítés esetén jár be:

C2E3
C2E6
C2E7
C2E8
C2E9
C2EA
C2EB
C2EC
C2ED
C2EE
C2EF
C2F0
C2F2
C2F5
C2F8
C2F9
C2FA

C2FC
C2FD
C2FE
C2FF
C300
C301
2A 9C BF
E5
E1
2B
2B
2B
2B
7E
B7
C8
E5
0E 07
21 0A C0
CD 17 B2
79
B7
20 EB

78
42
4B
0F
F5
30 19
LD HL,(BF9C)
PUSH HL
POP HL
DEC HL
DEC HL
DEC HL
DEC HL
LD A,(HL)
OR A
RET Z
PUSH HL
LD C,07
LD HL,C00A
CALL B217
LD A,C
OR A
JR NZ,C2E7

LD A,B
LD B,D
LD C,E
RRCA
PUSH AF
JR NC,C31C
; ROM-szegmensek táblája
; (felső byte)
; verem -> báziscím
; -1
; -2
; -3
; -4
; a következő ROM
; szegmensszáma
; vissza, ha tábla vége
; báziscím -> verem
; akciókód: RAM-igény
; ROM belépési pont
; belapozza és meghívja
; visszaadott C
; ha 0: kell RAM
; következő ROM-ra, ha
; ez nem igényelt RAM-ot
; kért RAM típusa
; B=kért RAM nagysága MSB
; C= kért RAM nagysága LSB
; BIT 1,A -> CY
; (hol kér RAM-ot?)
; elugrik, ha az 1. LAP-on
; Az igényelt RAM-ot a 2. LAP-on kéri (a rendszerszegmensben):
C303
C306
C307

C309
C30B

C30E
C310
C312
C313
C316
C317
C318
C31A

C333

C339
C33A
C33B
C33C
C33D
C33E
C33F
C340
C341
2A 95 BF
B7
ED 42

38 11
11 E8 83

ED 52
38 0A
19
22 95 BF
F1
0F
3E FF
18 17

30 04

EB
E1
2B
77
2B
72
2B
73
18 AB
LD HL,(BF95)
OR A
SBC HL,BC

JR C,C31C
LD DE,83E8

SBC HL,DE
JR C,C31C
ADD HL,DE
LD (BF95),HL
POP AF
RRCA
LD A,FF
JR C333

JR NC,C339

EX DE,HL
POP HL
DEC HL
LD (HL),A
DEC HL
LD (HL),D
DEC HL
LD (HL),E
JR C2EB
; rendszerterület kezdő címe
; NC státusz
; szabad ter. kezdő címe -
; - igényelt RAM nagysága
; (a 2. LAP-on nem fér el)
; 03E8H hely kell még a
; rendszernek is
; így is elfér a 2. LAP-on?
; elugrik, ha nem
; (BF95)-BC visszaállítás
; rendszerterület új kezdőc.
; RAM-típus?
; az 1. LAP-on is megfelel?
; rendszerszegmens
;

; tovább, ha a 2. LAP-on kell

; DE = a kapott RAM kezdőcíme
; ROM-tábla: a szegmens címe
; -1
; a kapott szegmens száma
; -2
; RAM kezdőcím HI
; -3
; RAM kezdőcím LO
; a ROM-tábla következő elemére

A rutinból is látható, hogy a táblázatban tárolt ROM-ok belépési pontja a C00AH cím. A táblából megállapított szegmensszámú és a B217H lapozórutin által a 3. LAP-ra kapcsolt és meghívott ROM-ok a C = 7 akciókód hatására megadhatják a RAM-igényüket (a DE regiszterpárban a nagyság, B-ben a helye: B = 1 esetén a 2. LAP-on, B = 2 esetén az 1. LAP-on). Ha megvizsgáljuk a bejegyzett ROM-ok (sorrendben az 5., 4., 1) belépési pontjait (erről még lesz szó a továbbiakban), megállapíthatjuk, hogy közülük csak a BASIC-BRD (esetünkben a 4.) szegmens reagál a C = 7 akciókódra, innen C = 0: kell RAM; B = 0: a 2. LAP-on; DE =04B3H értékekkel tér vissza a rendszer).
Ezek szerint a ROM-okat lekérdező ciklusból csak a 4. szegmensnél lép ki a rendszer, és mivel ez a rendszerszegmensben kér RAM-ot, a C303H programrészletben kapja meg. A rendszerterület kezdő címe (BF95/6H) belépés előtt a ROM-táblák aljára mutatott (ABB9H), a kiosztott RAM lefoglalása után tehát a munkaterület az ABB9H - 04B3H = A706H cím alatt kezdődik. Ezzel alakul ki az érintett rendszerváltozók BASIC-ben is lekérdezhető értéke, valamint a ROM-tábla megváltozott eleme:

ABBEH 06 A7 FF 04

A beépített perifériakezelők könyvtárazása
Az EXOS 0 inicializálási folyamatában ezután a periférialánc felépítése következik. A rendszerszegmensben elkészülő lánc elemeit, a periférialeírókat a ROM-okban tárolt perifériainformációk (álleírók) alapján alakítja ki az EXOS.
Minden ROM-szegmens elején a 0008/9H címen található az a kétbájtos cím, ami az adott ROM első perifériájának adatmezőjére mutat (a cím az 1. LAP-on értelmezett cím kell legyen, mint látni fogjuk). Ez az adatmező is tartalmaz egy pointert (mutatót) a következő perifériára stb., a lánc végéig, amit a pointer 0000 értéke mutat (ha az adott ROM-ban nincs perifériakezelő, már a C008/9H címen 0 van). Lássunk egy konkrét példát (az EXOS leírás terminológiájával) az álleírók most figyelembe vett elemeire: a 0. szegmens C008/9H címén az 5780H pointer érték található. Ez a VIDEO periféria álleírójának méretmeghatározó (SIZE) mezőjére mutat. Az 1. LAP címtartományában az álleíró:

Cím Érték Funkció  
576F 6B7B (NEXT) mutató a következő periféria (SOUND) álleírójára
5771 FFFE (RAM) a leíróhoz szükséges RAM (itt: 0 bájt, l. a 2.5 alfejezetet)
5773 00 (TYPE) típus (itt 0-nak kell lennie)
5774 20 (IRQFLAG) milyen megszakításokat kezel (itt: VIDEO)
5775 01 (FLAGS) VIDEO periféria jelző (máshol ez 0)
5776 5781 (TAB) belépési pont táblázat címe
5778 00 (TAB-SEG) táblázat szegmense (értékét felülírja az EXOS)
5779 00 (UNIT-COUNT) lehet-e több periféria is ezen a néven (nem)
577A 05 (NAME) a periférianév hossza
577B     VIDEO a periférianév karakterei
5780 0D (SIZE) a leíróba átmásolandó bájtok száma (TYPE-tól SIZE-1-ig)

Az EXOS a munkaterület kezdőcímétől (BF95H értéke, eddig követett példánkban A706H) lefelé tárolja a leírókat. A leírók alatti szabad terület kezdőcímét (BF93H értéke) minden lépésben a befejezett leíró alá állítja.
Először a 0. szegmens periférialáncát követi (és könyvtárazza) végig, majd a ROM-táblázat alapján végigvizsgálja az összes - bejegyzett - ROM-ot és azok perifériáit is nyilvántartásba veszi. Az eljárás érdekessége, hogy ebben az esetben alulról felfelé veszi sorra a ROM-szegmenseket (0-1-4-5 sorrendben), tehát a magasabb sorszámú bővítőknek mindig lehetőségük van felülírni a beépített kezelőket.
Egy leíró bejegyzését a rendszer a következő lépésekben végzi:

A láncszemek felfűzését jól követhetjük a ROM - minden láncépítési folyamatban hívott - rutinján:

C636
C637
C638
C639
C63A
C63B
C63C
C63D
C63E
C63F
2B
46
77
2B
7E
72
2B
4E
73
EB
DEC HL
LD B,(HL)
LD (HL),A
DEC HL
LD A,(HL)
LD (HL),D
DEC HL
LD C,(HL)
LD (HL),E
EX DE,HL
; báziscím-1
; előző szegmensszám
; helyére az új érték
; báziscím-2
; előző cím HI
; helyére az új érték
; báziscím-3
; előző cím LO
; helyére az új érték
; HL: az új láncelem címe
; Az új láncelem alá beírja a báziscím alól kiemelt pointert:
C640
C641
C642
C643
C644
C645
C646
2B
70
2B
77
2B
71
C9
DEC HL
LD (HL),B
DEC HL
LD (HL),A
DEC HL
LD (HL),C
RET
; cím-1
; előző szegmensszám
; cím-2
; előző cím HI
; cím-3
; előző cím LO

Ezt az eljárást folytatja az EXOS egy ROM-on belül addig, amíg a periférialánc végére nem ér (NEXT = 0000H), a ROM-ok során pedig addig, amíg el nem éri a ROM-táblázat végét, amíg a ROM-szegmens száma helyéről 0-t nem olvas be.

A csatornaterület
A felülről lefelé kiépített periférialánc alatt kezdődhet az igazi EXOS munkaterület: a csatornák leíró és puffermemóriái. A periférialánc legalsó bájtjára a, BF93H redszerváltozó mutat. Erre állítja az EXOS a BFBDH alatti 3-bájtos mutatót (cím + szegmens). A BFBDH báziscím tehát a felhasználók által felépített csatornalánc kiindulási pontja lesz. Maga az EXOS 0 a hidegindítás folyamatában nem nyit tényleges csatornákat, csak egy töredék leírót (az alapértelmezés szerinti 0-s csatornához tartozó néhány bájtot) helyez el, ami alá kerülhet majd a hozzárendelt periféria mutatója és a munkaterület. Egyelőre ide - mint az EXOS munkaterület alsó határára - állítja be a rendszer a BF91/2H rendszerváltozó, az EXOS határ értékét (0-16 k közötti értékre transzformálva).
Ezzel lényegében kialakult a rendszermemória. Az EXOS legalábbis már csak az alapértelmezésű periféria nevét (TAPE) helyezi el rögzített munkaterületen (BF18H: hossz, BF19H-tól a karakterek), és a megszakítás engedélyezést állítja be a BFC5H rendszerváltozóban és a DAVE-chip B4H-es regiszterén.
A felhasználócentrikus logikára jellemző módon azonban még megadja a lehetőséget a perifériáknak, majd a rendszerbővítőknek a saját inicializálásra is.
A perifériakezelők - funkciójuknak, működési logikájuknak megfelelően - különböző feladatokat látnak el, amikor az EXOS meghívja belépési-cím táblázatuk 12. (inicializálás) címét. A VIDEO-kezelő pl. újra beállítja a sorparaméter-táblát (LPT), az 'EDITOR' csak egy bájttal jelzi saját munkaterületén, hogy megtörtént az inicializálás, a nyomtató-kezelő pedig semmit nem csinál.
A rendszerbővítők lekérdezésénél egy olyan báziscímhez is fordul az EXOS, amiről eddig még nem volt -hidegindításnál nem is lehetett - szó. A BFC3H báziscím alá írja majd a rendszer a magnetofonról beolvasott, vagy általunk kreált bővítők (pl. PASCAL, ASMON stb.) láncának mutatóját. A bővítők ui. ugyanolyan láncba fűzhetők, mint a periférialeírók vagy a csatornák. Mivel az így könyvtárazott bővítők státusza egészen különleges a gép életében, ezzel a kérdéssel még érdemes foglalkozni.
Az EXOS C = 8 akciókóddal (Inicializálás) először ezeket a bővítőket kérdezi le (de természetesen a hidegindítás alatt a bővítők lánca még üres), majd a ROM-táblázat alapján végigkérdezi a ROM-szegmenseket is. Az alapkonfigurációban egyedül a 4. szegmens lát el tényleges feladatokat ennél a hívásnál (előkészíti a német nyelvű hibaüzenetek kiírását, a német karakterkészletnek megfelelően állítja be a karaktergenerátort stb.).
Ezzel tulajdonképpen be is fejeződött a rendszer inicializálása; kialakult a jellemző memóriatérkép. Ez a rendszer azonban még nem képes kapcsolatot tartani a környezetével, nincsenek meg a gép és perifériái (pl. billentyűzet, képernyő stb.) közötti utak: a csatornák. Ezeket a felhasználónak kell igényei szerint kialakítani. Ezt teszi meg - ideiglenesen - az 1. szegmens is a bejelentkezés érdekében, miután az inicializálás után ide tér vissza a rendszer.

Bejelentkezés
A csatornaparaméterek beállítása, a csatornák megnyitásának módja számunkra is érdekes lehet. Kövessük hát ezt az eljárást.
A felhasznált F745H rutin a HL címen kezdődő táblázat alapján dolgozik. A 80H-nál kisebb beolvasott értékeket EXOS változó sorszámnak tekinti és beírja a következő táblaelemet. A nagyobb értékeket csatornaszámként kezeli és megnyitja azt arra a perifériára, amelynek nevére a tábla következő két eleme mutat:

F745
F746
F747
F749

F74B
F74D
F74E
F74F
F750
F752
08
4E
CB 79
20 09

06 01
23
56
23
F7 10
18 F2
EX AF,AF'
LD C,(HL)
BIT 7,C
JR NZ,F754

LD B,01
INC HL
LD D,(HL)
INC HL
EXOS 10
JR F746

; érték beolvasás
; EXOS változó?
; ugrik, ha csatorna

; "írás" jelző
; következő adatra
; D: írandó érték
; következő adatra
; EXOS változó írása
; olvasást folytat
; Csatornanyitás
F754
F755
F756
F757
F758
F759
F75A
F75C
F75E
F760
79
23
5E
23
56
23
FE FF
28 27
F7 01
C8
LD A,C
INC HL
LD E,(HL)
INC HL
LD D,(HL)
INC HL
CP FF
JR Z,F785
EXOS 01
RET Z
; A: csatornaszám
; következő adatra
; név címe LO

; név címe HL

; csatornaszám=FF?
; (itt nem)
; csatornanyitás
; vissza

Ezt a rutint felhasználva állítja be az E69DH rutin a bejelentkezéshez szükséges paramétereket, megnyit egy hardver szöveglapot az "1985 Intelligent..." üzenethez, egy 256 színű grafikus lapot a nagybetűs, villogó "ENTERPRISE" felirathoz és a billentyűzetcsatornát, hogy kiléphessünk ebből a végtelen ciklusból:

E69D
E6A0
E6A1
E6A4
E6A5
E6A8
E6A9
E6AC
E6AD
E6AF
E6B2
E6B5
E6B7
EAB8
E6BA
E6BD
E6C0
E6C2
21 3E E7
3C
CD 45 F7
C0
CD 45 F7
C0
CD 45 F7
C0
3E 98
01 01 01
11 15 02
F7 0B
C0
3E 97
01 01 01
11 0A 02
F7 0B
C0
LD HL,E73E
INC A
CALL F745
RET NZ
CALL F745
RT NZ
CALL F745
RET NZ
LD A,98
LD BC,0101
LD DE,0215
EXOS 0B
RET NZ
LD A,97
LD BC,0101
LD DE,020A
EXOS 0B
RET NZ
; tábla kezdőcím

; 98H csatorna
; (szöveglap)
; 97H csatorna
; (grafikus)
; 96H csatorna
; (billentyű)
; szöveglap
; első két sora
; kijelzés
; a 21. sortól

; grafikus lap
; első két sora
; kijelzés
; a 10. sortól
; "1985 Intelligent Software Ltd" kijelzés
E6C3
E6C6
E6C8
E6CB
E6CC
E6CE
E6D0
E6D1
21 06 E7
0E 98
CD F9 E6
C0
3E 96
F7 09
B1
C8
LD HL,E706
LD C,98
CALL E6F9
RET NZ
LD A,96
EXOS 09
OR C
RET Z
; szövegre mutat
; szöveglap
; kiírás

; billentyűzet
; karakterolvasás
; volt billentyű?
; vissza, ha igen
; Grafikus lap beállítása:
E6D2
E6D5
E6D8
E6D9
E6DA
E6DB
E6DD
E6DE
E6E1
E6E2
E6E3
E6E5
E6E6
E6E7
E6E9
E6EA
E6EC
E6ED
E6EF

E6F2
E6F3
E6F4
E6F5
21 2C E7
CD F7 E6
C0
7E
B7
28 EF
E5
21 04 01
77
2B
ED 5F
77
2B
36 49
2B
36 1B
2B
36 04
CD F7 E6

E1
C0
23
18 E2
LD HL,E72C
CALL E6F7
REN NZ
LD A,(HL)
OR A
JR Z,E6CC
PUSH HL
LD HL,0104
LD (HL),A
DEC HL
LD A,R
LD (HL),A
DEC HL
LD (HL),49
DEC HL
LD (HL),1B
DEC HL
LD (HL),04
CALL E6F7

POP HL
RET NZ
INC HL
JR E6D9
; kiírandó bájtokra
; kurzorpozíció
; beállítás (0,71)
; "ENTERPRISE" szöveg
; következő karaktere
; ismétlés, ha vége
; szövegcím verembe
; munkaterület
; karakter puferbe
; elé:
; véletlen érték
; pufferbe
; elé:
; "I": tintaszín
; elé:
; "ESC"
; elé:
; "szöveg" hossza
; karakter kiírása
; véletlen színnel
; szövegcím vissza

; következő betűre
; vissza
; Szöveg kiírása pufferből a csatornára:
E6F7
E6F9
E6FA
E6FB
E6FC
E6FD
E6FE
E6FF
E701
E702
E703
E705
0E 97
46
23
C5
79
46
23
F7 07
C1
C0
10 F6
C9
LD C,97
LD B,(HL)
INC HL
PUSH BC
LD A,C
LD B,(HL)
INC HL
EXOS 07
POP BC
RET NZ
DJNZ E6FB
RET
; csatornaszám
; szöveg hossza
; következő kar.

; A: csatornaszám
; B: írandó érték
; következő kar.-re
; karakter írása


; végig a szövegen

A felhasznált táblázatok:

E73E



E746
E749

E74D
E750
16 00 17 00 18 28 19 02



98 34 FE
16 01 17 03

97 34 FE
96 4C FE
MODE_VID=0
COLR_VID=0
X_SIZ_VID=40
Y_SIZ_VID=2
98h csatorna
MODE_VID=1
COLR_VID=3
97h csatorna
96h csatorna
; szöveglap
; 2 szín
; szélesség
; magasság
; "VIDEO:"
; "HIRES" gr.
; 256 szín
; "VIDEO:"
; "KEYBOARD:"

6706
670E
6716
671E
6726

672C
6734
673C

25 1B 6F 20 20 20 20 80
20 31 39 38 35 20 49 6E
74 65 6C 6C 69 67 65 6E
74 20 53 6F 66 74 77 61
72 65 20 4C 74 64

06 1B 41 00 00 47 00 45
4E 54 45 52 50 52 49 53
45 00

...    .
1985 In
telligen
t Softwa
re Ltd

.......E
NTERPRIS
E

A "VIDEO:" és "KEYBOARD:" szövegek az 1. lap FE34H, ill. FE4CH címén találhatóak. A rutinokat felhasználni kívánó Olvasónak jó tudni, hogy ezek mellett le vannak tárolva az "EDITOR:" (FE3BH) és "printer:" (FE43H) periférianevek is.
A megnyomott billentyű hatására a rendszer lezárja az ideiglenesen megnyitott csatornákat, majd (0-t írva a műveleti kód tárolóba (BF78H)) felkínálja a vezérlést a bővítőknek: egy EXOS 26 hívást hajt végre.
Ennek hatására az EXOS C=1 (Hidegindítás) kóddal meghívja minden rendszerbővítő, majd minden ROM belépési pontját (C00AH). Ekkor az alkalmazói program átveheti a vezérlést. Ha minden bővítőbő1 visszatérne a rutin (változatlan C-regiszterrel), a sor végén ott áll az 1. szegmens, annak WP alkalmazói programja (a szövegszerkesztő), ami csak erre a hívásra vár (ha nincs bővítőmodul a gépben, akkor éppen ez a helyzet áll elő: a bejelentkezés után a szövegszerkesztő kezd dolgozni). A vizsgált kiépítésben azonban a ROM-tábla első szegmense (az 5. BASIC-ROM) azonnal átveszi a vezérlést és kialakítja a BASIC rendszert.
Ezzel végre végetért az inicializálás (legalábbis az EXOS-é), a szó ettől a pillanattól az alkalmazóé. Mielőtt azonban továbblépnénk, beszéljünk még egy inicializálás vonatkozású folyamatról: a melegindításról. A felhasználó beavatkozásával ui. a tárgyalt indítási folyamat - a felépített rendszer és a felhasználói memóriák védelmében - lényegesen enyhébb irányba is fordítható. Ehhez az alkalmazónak egy 0-tól különböző értéket (praktikusan saját inicializáló belépési címét) kell betölteni a RST-ADDR rendszerváltozóba (BFF8/9H, 49144/5). A RESET gomb megnyomására a hidegindítási pontról elinduló programág ui. - mielőtt még "drasztikusabb" lépéseket tenne - megvizsgálja ezt a címet. Ha ez nem 0 (valamint a védett rendszerterületet (B217H-B251H) is épnek találja), akkor csak egy lényegesen kevesebb változással járó EXOS 0-t hajt végre, C = 10 RESET jelzővel. Előtte PUSH utasítással tárolja a talált RST-ADDR címet (így majd az EXOS 0 végén álló 'RET' hatására a felhasználó melegindítási rutinjára fog ugrani) és - ideiglenesen - 0-t ír a RST-ADDR cím helyére. Ezért, ha mégegyszer megnyomjuk a RESET gombot, mielőtt a melegindítással újraéled a rendszer, ez már valódi hidegindítást vált ki.

Befejezésként, az eddigiek szemléltetésére, rendszerezésére nézzünk meg egy-két programot, példát.
A 3. Függelékben összefoglalóan megadjuk a memória megismert térképét, a fontosabb rendszerváltozókat, mutatókat, de valószínűleg nem fog ártani egy konkrét példán (az eddig vizsgált verziójú gépen) megvizsgálni és értelmezni a ténylegesen kialakuló rendszermemóriát.
Ehhez készítsünk egy DUMP programot, egy olyan segédeszközt, amely (az áttekinthetőség érdekében hexadecimális formában) kiírja egy tetszőleges memóriaterület tartalmát. A program felhasználja a hexa-decimális és decimális-hexa konvertáló rutinokat (1. pl. az 1. fejezetet):

100 PROGRAM "Dump.bas"
110 NUMERIC D
120 DEF H$(N)=CHR$(INT(N/16)+48-7*(INT(N/16)>9))&CHR$(MOD(N,16)+48-7*(MOD(N,16)>9))
130 DEF DEC(H$)
140   LET D=0:LET Q=1
150   FOR N=LEN(H$) TO 1 STEP-1
160     LET D=D+ORD(HEX$(H$(N:N)))*Q
170     LET Q=Q*16
180   NEXT
190 END DEF
200 INPUT PROMPT "Kezdocim? ":A$
210 CALL DEC(A$)
220 LET KC=D
230 INPUT PROMPT "Vegcim? ":A$
240 CALL DEC(A$)
250 LET VC=D
260 INPUT PROMPT "Szegmens? ":A$
270 CALL DEC(A$)
280 LET SG=D
290 FOR N=KC TO VC STEP 8
300   LET A$=H$(N/256)&H$(MOD(N,256))
310   PRINT N;" ";A$;" ";
320   FOR I=0 TO 7
330     LET DT=SPEEK(SG,N+I)
340     PRINT H$(DT);" ";
350   NEXT
360   PRINT
370 NEXT
380 END

A program hexadecimális alakban kéri a kiírandó memóriaterület kezdő- és végcímét, valamint szegmensszámát. A kiíratásnál a kezdőcímek decimális alakját is megadja, így egy-egy cím közvetlenül is írható, olvasható.
Alkalmazásként próbáljuk ki programunkat az előzőekben felépített rendszerterületen, a rendszerváltozók tartományában:

Értelmezzük együtt ezt az - első látásra talán áttekinthetetlen - adattömeget, megadva a fontosabb változók, pointerek funkcióját:

Cím Jelenlegi érték Funkció, magyarázat
BF91/2
13C7
az EXOS határértéke 0-3FFF közé transzformálva (a 2. LAP-on ténylegesen 93C7 a határ) (a csatornaterület alja)
BF93/4
A65C
a periférialeíró terület alja (innen indul a csatornaterület)
BF95/6
A706
a ROM-ok részére kiutalt RAM-terület alja (ez alatt indul a periférialeírók területe)
BF97/8
ABB9
a ROM-tábla alja
BF9A/B
ABD0
a RAM-táblában annak a szegmensnek a címe, amelyikben az EXOS határ van
BF9C/D
ABC9
a ROM-tábla teteje (a RAM-tábla alatti cím)

A rendszerszegmens verem alatti, viszonylag állandó táblaterülete és az ez alatti mozgó csatornaterület a 8 hibátlan RAM-szegmenssel rendelkező, angol/német BASIC modullal felszerelt gép esetén a következőképpen alakul:

  ABD0
ABCA
RAM-tábla (8 bájt)
(BF9C)
(BF97)
ABC9
ABB9
ROM-tábla (4*4+1 = 17 bájt)

(BF95)
ABB8
A706
ROM-szegmens (4. szeg. 04B3H = 1203 bájt) pufferterülete

(BF93)
A705
A65C
Periférialeíró terület (170 bájt)

(!BF91)
A65B
93C7
Csatornaterület (igény szerint)
  8000 Szegmenshatár

Tovább vizsgálva a rendszerváltozókat

Cím Jelenlegi érték Funkció, magyarázat
BF9E
0
a megosztott szegmens száma (most 0, mert nincs ilyen: a felhasználó még nem kérte a rendszerszegmens kiutalását)
BF9F
6
a szabad szegmensek száma (az F8 szegmens kötött felhasználású, az FF-et pedig az EXOS használja)
BFA0
0
a felhasználónak kiutalt szegmensek száma (egyelőre a BASIC elfér az F8 szegmensen)
BFA1
0
perifériáknak kijelölt szegmensek száma
BFA2
1
a rendszerszegmensek száma (az EXOS egyelőre elfér az FF szegmensen)
BFA3
8
a működő RAM-szegmensek száma
BFA4
0
a hibás szegmensek száma

(A fenti 7 adat a rendszerállapotként lekérdezhető adatsor). Ezután a 3 legutóbb használt (legkönnyebben elérendő) csatorna adatait találjuk:

BFA5
BFA6/7
BFA8
6AH
61D0H
FFH
Csatornaszám + 1 (£105: KEYBOARD)
Csatornaleíró címe a 0. LAP-on
A csatornaleíró szegmense
BFA9
BFAA/B
BFAC
01H
5CD7H
FFH
£0: EDITOR
BFAD
BFAE/F
BFB0
67H
665CH
FFH
£102: VIDEO

A különböző láncok pointerei:

1. Csatornalánc
  BFBA/B
  BFBC

Báziscím: BFBD

665CH
FFH
az első leíró címe a láncban (1. LAP)
a leíró szegmense
2. Periférialánc
  BFBD/E
  BFBD
Báziscím: BFC0

6661H
FFH
az első leíró címe
szegmense
3. Bővítőlánc
  BFC0/1
  BFC2

Báziscím: BFC3
(jelenleg üres)
0000H
00H
belépési cím
bővítő szegmense (itt: lánc vége)

A láncokat egy külön programmal érdemes végigkövetni, de egyelőre nézzük végig a rendszerváltozókat:

BFC5 14H IRQ-ENABLE-STATE: 1 Hz-es és videomegszakítás engedélyezve
BFC6 0   nem használt cím, az 1. EXOS változót (FLAG-SOFT-IRQ) a BFF2H címen tárolja és kezeli a rendszer
BFC7 20H CODE-SOFT-IRQ (még nem törlődött a STOP kód)
BFC8 0 DEF-TYPE (nem file-kezelő periféria, magnetofon)
BFC9 0 DEF-CHAN (a £FF csatornahívás ezt használja)
BFCA 0 TIMER (nincs időzítés)
BFCB 1 LOCK-KEY (CAPS állapot)
BFCC 0 CLICK-KEY (engedélyezett a billentyűhang)
BFCD 0 STOP-IRQ (a STOP szoftver megszakítást okoz)
BFCE 255 KEY-IRQ (a többi billentyű nem okoz megszakítást)
BFCF 3 RATE-KEY (billentyűismétlési sebesség kb. 17/s)
BFD0 30 DELAY-KEY (első billentyűismétlés késleltetés 0,6 s)
BFD1 0 TAPE-SND (magnetofonhang engedélyezett)
BFD2 0 WAIT-SND  
BFD3 0 MUTE-SND (a belső hangszóró aktív)
BFD4 20 BUF-SND  
BFD5 15 BAUD-SER (9600 baud átviteli sebesség)
BFD6 0 FORM-SER  
BFD7 0 ADDR-NET (hálózati gépszám alapérték)
BFD8 0 NET-IRQ (vett adat szoftvermegszakítást okoz)
BFD9 FFH CHAN-NET  
BFDA FFH MATCH-NET  
BFDB 0 MODE-VID (hardver szövegmód)
BFDC 0 COLR-VID (két szín)
BFDD 40 X-SIZ-VID (az aktuális VIDEO-lap 40 karakter széles)
BFDE 24 Y-SIZ-VID (24 sor magas)
BFDF 0 ST-FLAG (állapotsor kijelezhető)
BFE0 0 BORD-VID keretszín fekete
BFE1 1 BIAS-VID  
BFE2 66H VID-EDIT (a szerkesztő videocsatorna száma)
BFE3 69H KEY-EDIT (a szerkesztő billentyűzetcsatornája)
BFE4 8 BUF-EDIT (a szerkesztő puffere 2 kbájt)
BFE5 18H FLAG-EDIT  
BFE6 0 SP-TAPE (gyors szalagátvitel)
BFE7 FFH PROTECT (védelem aktív)
BFE8 2 LV-TAPE  
BFE9 FFH REM1 (1. magnetofon távvezérlés kikapcsolva)
BFEA FFH REM2 (2. magnetofon távvezérlés kikapcsolva)
BFEB 0 SPRITE  
BFEC BAH RANDOM-IRQ (pillanatnyi érték a kiolvasáskor)
BFED/E 0000 USR-ISR (nincs felhasználói megszakítási rutin)
BFEF 0 CRDISP-GLAG (bejelentkezési üzenet engedélyezve)
BFF0/1 0ECC SECOND-COUNTER (másodpercszámláló pillanatnyi érték)
BFF2 0 FLAG-SORT-IRQ (nincs megszakításkérés)
BFF3 0 PORTB5  
BFF4/5 B900 LP-POINTER (a sorparaméter-tábla kezdőcíme 47360Dec)
BFF6/7 BEB8 SP-POINTER (az állapotsor címe)
BFF8/9 0104 RST-ADDR (a BASIC által beállított melegindítási cím)
BFFA/B AC3B STACK-LIMIT  
BFFC F8H USR-P0 az EXOS-t hívó
BFFD 04H USR-P1 memóriakonfiguráció
BFFE FFH USR-P2  
BFFF 01H USR-P3  

Ez a rengeteg rendszerváltozó szinte ingerel a kísérletezésre, próbálgatásra. Mi biztatjuk is erre az Olvasót, hiszen - mint a bevezetőben is írtuk - ez a legbiztosabb módja a gép megismerésének. Biztatásként nézzünk egy-két példát együtt is az EXOS memóriaszervezésére!
Nézzük meg külön a szegmenskiutalások nyilvántartását (a rendszerállapot-területet):

Tehát 6 szabad szegmens van. Nyissunk meg most egy új BASIC programot (EDIT), majd lapozzunk vissza a DUMP programhoz, hogy megnézzük, milyen változások történtek:

Látható, hogy az EXOS egy szabad szegmenst kiutalt a felhasználó (jelen esetben a BASIC rendszer) számára, és be is könyvelte ezt a változást.
Lássunk most egy olyan változást, amely az EXOS területét érinti. Például egy nagy grafikus lap nyitása sok helyet igényel, és ezt az EXOS biztosítja:

Ismét csökkent tehát a szabad szegmensek száma, és a lefoglalt szegmensi saját magának utalta ki az EXOS. Ha nagyobb területet íratunk ki, azt is láthatjuk, hogy most előbbre (a RAM-tábla FEH szegmensére) mutat a BF9A/B (hiszen ebbe a szegmensbe került az EXOS határ).
Egy másik érdekes területe a rendszermemória "böngészésének" a különböző láncok követése. A lánc felépítési logikáját láttuk e fejezetben, de nézzük meg a 0. szegmens rutinján, hogyan keresi vissza maga a rendszer az elemeket:

C647
C648
C649
C64A
C64B
C64C
C64D
C64E
C64F
C650
C652
2B
7E
2B
46
2B
6E
60
B7
C8
D3 B1
C9
DEC HL
LD A,(HL)
DEC HL
LD B,(HL)
DEC HL
LD L,(HL)
LD H,B
OR A
RET Z
OUT (B1),A
RET
; báziscím-1
; A: szegmensszám
; báziscím-2
; B: cím HI
; báziscím-3
; L: cím LO
; HL: a láncelem címe
; a szegmensszám 0?
; lánc vége, ha igen (Z)
; belapozza a láncelemet
; tároló szegmenst

A báziscím ismeretében mi is követhetjük ezt a logikát, akár egy BASIC programmal is.

100 PROGRAM "Lanc.bas"
110 NUMERIC D
120 DEF H$(N)=CHR$(INT(N/16)+48-7*(INT(N/16)>9))&CHR$(MOD(N,16)+48-7*(MOD(N,16)>9))
130 DEF DEC(H$)
140   LET D=0:LET Q=1
150   FOR N=LEN(H$) TO 1 STEP-1
160     LET D=D+ORD(HEX$(H$(N:N)))*Q
170     LET Q=Q*16
180   NEXT
190 END DEF
200 LET SGE=255
210 INPUT PROMPT "Baziscim? ":A$
220 CALL DEC(A$)
230 LET SG=SPEEK(SGE,D-1)
240 IF SG=0 THEN STOP
250 LET HL=SPEEK(SGE,D-3)+256*SPEEK(SGE,D-2)
260 LET A$=H$(HL/256)&H$(MOD(HL,256))
270 PRINT H$(SG);"/";A$;": ";
280 CALL LEIRO
290 LET SGE=SG
300 GOTO 220
310 DEF LEIRO
320   FOR N=HL-3 TO HL+7
330     LET DT=SPEEK(SG,N)
340     PRINT H$(DT);" ";
350   NEXT
360   CALL NEV
370 END DEF
380 DEF NEV
390   FOR N=1 TO DT
400     PRINT CHR$(SPEEK(SG,HL+7+N));
410   NEXT
420   PRINT
430 END DEF

A program a hexadecimális alakban beírható báziscím alól kiolvassa a következő láncelem szegmensszámát (a lánc végét jelzi, ha ez 0) és tárolási címét (HL), azaz a láncelem pointerét. Kiírja a pointert, majd a periférialeíró elemeit és a periféria nevét. Ezután a pointer értékét tekinti báziscímnek és így keresi a következő elemet. A LEIRO rutin ilyen formában kifejezetten a periférialeíró kiolvasására alkalmas.
A futtatás eredménye:

A rutin a pointer által címzett adat (a leíró TYPE-eleme) előtti 3 értéket is kiírja. Így pl. az 1. sor elemei:

A TYPE-elemek áttekintésével látható, hogy a 0. szegmens beépített KEYBOARD és EDITOR kezelőit a bővítőben csatlakoztatott 4. szegmens felülírta, érvénytelenítette. Ez a táblázat minden perifériaművelet alapja, tehát igen praktikusan felhasználható az esetleges átírásokra. Most csak egy durva beavatkozással szemléltetjük ezt a lehetőséget: érvénytelenítsük pl. a PRINTER-leírót, azaz írjunk 0-tól különböző értéket a TYPE mezőre. A mi példánkban a PRINTER-leíró (és egyben a TYPE adat) címe FF/66B3, azaz 255/26291. Írjuk be

SPOKE 255,26291,1

Ezután minden nyomtatóművelet "Gerat nicht vorhanden" ("Device does not exist") hibajelzéssel áll le, amíg vissza nem állítjuk az érvényes típust.

SPOKE 255,26291,0

A hasonló láncba felfűzött csatornák végigkövethetők ugyanezzel a programmal, de a leírók kilistázásához alakítsuk át a LEIRO rutint, hogy csak a pointer által megadott cím előtti 16 bájtot írja ki:

100 PROGRAM "Lanc2.bas"
310 DEF LEIRO
320   FOR N=HL-16 TO HL-1
330     LET DT=SPEEK(SG,N)
340     PRINT H$(DT);" ";
350   NEXT
360   PRINT
370 END DEF

A BASIC bejelentkezés után a következő (alapértelmezésűnek tekinthető) csatornák élnek a gépen:

A csatornákról még lesz szó, most a 16-bájtos csatornaleírók három elemére hívjuk fel a figyelmet (a kapott lista értelmezéséhez):

Hogy még itt is kísérletezzünk, egy kicsit, nyissunk meg egy csatornát, és figyeljük meg, hogyan jelenik ez meg a láncban.

Az 5. csatornát (6-os belső értékkel) a kialakult lánc aljára vette föl az EXOS (ezzel érdemes egy kicsit kísérletezni: mi történik kisebb vagy nagyobb pufferigényű csatorna nyitásakor, egy csatorna lezárásakor stb.).
A pointer alapján megfigyelhetd, hogy a hozzárendelt periféria (FF/66B3) valóban a PRINTER.
Érdemes még egybevetni az új láncelem adatait a rendszermemóriáról készült legelső DUMP-pel. Ott láthattuk, hogy az igénybevett rendszerterület alsó határa az EXOS határ; (BF91) (13C7) volt, ill. 1. LAP címként értve: 53C7H. Látható, hogy az új láncelemet éppen ettől a címtől illesztette a rendszermemóriához az EXOS (és a határt nyilván lefelé mozgatta).

Befejezésül próbálkozzunk egy-két olyan beavatkozással, aminek még hasznát is vehetjük a továbbiakban. Vessünk egy pillantást a rendszer által használható memóriaterületek könyvtárára, a ROM- és RAM-szegmensek táblázatára. A fejezetben leírtak és a rendszerváltozók aktuális értéke alapján a ROM-tábla alsó címe (BF97/8H értéke) ABB9H, a RAM-tábla felső címe pedig ABD0H (ez rögzített érték).

Láthatjuk a bejegyzett ROM-ok szegmensszámát (1,4,5), előttük a számukra kiutalt RAM-terület mutatóját (itt csak a 4. szegmens kért RAM-ot; az FFH szegmens A706H címén kezdődő területet kapta), majd a RAM szegmensszámokat.
Hogy szemléltessük, milyen nagy biztonsággal kezeli a rendszer a rendelkezésére álló memóriákat, próbáljunk ki egy drasztikus beavatkozást. Próbáljuk meg egyszerűen kitörölni az egyik RAM-szegmenst a táblázatból.
Nézzük meg ismét a rendszerállapotot és a RAM-táblázatot:

Írjunk 0-t pl. az FCH szegmens helyére:

POKE 43981,0

majd lássuk újra a két területet:

Láthatjuk, hogy az EXOS túltette magát az összezavart táblázattal elékerülő problémán. A START után elénk kerülő memóriakép már ismét egy egészséges táblát mutat, csak éppen hiányzik a listáról a kiiktatott szegmens. A rendszerállapot azt mutatja, hogy a változásnak megfelelően csökkent (5-re) a szabad szegmensek száma is. A hiányzó szegmenst az EXOS mint "felhasználónak kiutaltat" könyvelte el.
Ha BASIC programok nyitásával (alulról) vagy grafikus lapok nyitásával (felülről) megterheljük a memóriakezelést, meggyőződhetünk róla, hogy az EXOS valóban nem számol a törölt szegmenssel. Igen egyszerű módon - szinte hályogkovács módjára - hozzájutottunk tehát egy olyan szegmenshez, amely általunk szabadon használható, a rendszer viszont nem bolygatja a továbbiakban (a készítők persze nem ezt a módszert képzelték el a szegmensigényléshez; erről is lesz szó a következő pontban).
Reméljük, hogy kellőképpen felkeltettük az Olvasó érdeklődését. Szinte kínálkozik a következő lépés: a ROM-tábla! Abba nem nyúlhatnánk bele? Dehogynem, csak egy kicsit óvatosabban. Azonnal végrehajtható, előkészületet nem igénylő próbálkozásként iktassuk be a (rendszertől az imént elorzott) FCH szegmenst a BASIC-BRD (4.) ROM-szegmens helyére (lemondva persze ezzel a német verzió minden előnyéről). Előzőleg csak annyit tegyünk meg, hogy egy 'RET' utasítást helyezzünk az új ál-ROM belépési pontjára, hiszen valójában még semmilyen program nem fogadja a hozzá forduló EXOS-t:

SPOKE 252,10,201

Ezután már következhet a szegmensszámcsere:

POKE 43969,252

majd adjunk ki egy olyan utasítást, amely inicializálja a rendszert (pl. :BASIC).
Az újonnan induló rendszer már nem tartalmazza a 4. szegmens szolgáltatásait (erről meggyőződhetünk pl. a periférialánc kiolvasásával vagy egy :HELP utasítással), tartalmazhat viszont mindent, amit mi írunk a szegmensbe. Ha meggondoljuk, hogy ezzel a lépéssel egy teljes értékű ROM-hoz jutottunk, ami részt vehet a rendszer inicializálásában, saját perifériái lehetnek (amik megmaradnak akkor is, ha egy másik alkalmazói programba, pl. szövegszerkesztőbe lépünk) és ráadásul - az előéletnek köszönhetően - még egy védett, könnyen elérhető RAM-területtel is rendelkezhetünk, beláthatjuk, hogy a lehetőségeknek valóban csak a fantázia szab határt.

2.3.2 A funkcióhívások

Az EXOS rendkívül kényelmes lehetőséget ad a felhasználónak a rendszerszolgáltatások elérésére. Minden EXOS funkció - az inicializálástól a memóriagazdálkodáson át a perifériakezelők által megvalósított csatornaműveletekig - elérhető egy kétbájtos gépi kódú utasítással. Még ha hozzávesszük is ehhez, hogy a műveletek esetleges paramétereit (pl. kiíratandó szöveg kezdőcímét, igényelt memóriaterület nagyságát stb.) természetesen be kell állítanunk a hívás előtt a megfelelő regiszterekben, nyilvánvaló, hogy ennél tömörebb gépi kódú programírási lehetőséget semmilyen rendszer nem kínálhat. Minden alkalmazói program (pl. a BASIC értelmező is) rendszeresen él a funkcióhívások lehetőségével. Mi sem mellőzzük ezeket. Annyi mindent elvégez a rendszer a funkcióhívások végrehajtása során - végrehajtó rutinok és pufferterületek kikeresése, memóriaellenőrzés és rendezés, lapozás stb. -, hogy fölmerülhet a kérdés: érdemes-e egyáltalán a funkcióhívás megkerülésének, a végrehajtó rutinok közvetlen hívásának gondolatával foglalkozni. Nos, ha valóban mindazt meg kell csinálnunk, amit az EXOS elvégez, akkor nem érdemes! Lehetnek azonban olyan esetek (pl. egy kialakított képernyőlap és állandósult csatornaállapot), amikor a báziscímek egyszeri megkeresése után, a végrehajtó rutinok címeinek ismeretében túl körülményesnek (és főleg időigényesnek) tűnik a hosszadalmas ágak bejárása minden egyes műveletnél. Ez az érzésünk még valószínűleg fokozódik is, ha végigkövetjük azt az utat, ahogy az EXOS a funkcióhívás kiadásától eljut a konkrét végrehajtásig. Mégis érdemes ezt az elemzést megtennünk, ha mélyebben meg akarunk ismerkedni gépünkkel. Akkor is ismernünk kell a működés egyes részleteit, ha saját perifériakezelőt akarunk a rendszerhez illeszteni, amit viszont majd az EXOS hív a funkcióhívások során.
Ha fel akarjuk használni a háttérrendszer szolgáltatásait, feltétlenül ismernünk kell az alkalmazás korlátait és lehetőségeit, feltételeit és várható eredményeit. Ezekről a kérdésekről magas színvonalon ír az EXOS műszaki leírása is, mi viszont a működés követésével és konkrét információkkal próbálunk meg újat mondani. A következőkben a programozástechnikai szempontból talán legérdekesebb bevezető és záró szakaszt a ROM rutinjainak elemzésével követjük, a kevésbé áttekinthető, hosszadalmasabb ágakat pedig funkcionálisan írjuk le.
Kezdjük az alapproblémával: kétbájtos utasításról beszéltünk, ugyanakkor sokféle összetett funkcióról. A gépi kódú programozással esetleg most ismerkedő Olvasó talán ellentmondást érez a két állítás között. A rendszer kifejlesztőinek valóban egy trükköt kellett alkalmazniuk a megoldáshoz. A továbbiakban "EXOS n"-ként emlegetett funkcióhívás valójában egy. RST 30H Z80-as utasítás, amelyet a memóriában az argumentum: n követ (n értéke 0 és 11 vagy 16 és 34 közötti lehet). Az egybájtos utasítással hívható szubrutinnak (a 0030H címen kezdődő programnak) kell majd az őt hívó memóriaterületről az argumentumot beolvasni, és az értéknek megfelelően különböző funkciókat végrehajtani. Bonyolítja a helyzetet, hogy a hívások különböző csoportjait eltérő módon kell kezelni: egy részüket közvetlenül végrehajtja az EXOS, a csatornaműveleteket viszont a perifériakezelőknek kell átadni. Látható tehát, hogy a formai egyszerűségnek ára van, mind helyfoglalásban (ez a kevésbé érdekes, hiszen a gép készítői szokatlanul bőkezűen bántak a memóriával), mind futásidőben.
Hogy a továbbiakban tudjunk mire hivatkozni, megadjuk a funkcióhívások egyszerű felsorolását:

EXOS 0
EXOS 1
EXOS 2
EXOS 3
EXOS 4
EXOS 5
EXOS 6
EXOS 7
EXOS 8
EXOS 9
EXOS 10
EXOS 11
EXOS 16
EXOS 17
EXOS 18
EXOS 19
EXOS 20
EXOS 21
EXOS 22
EXOS 23
EXOS 24
EXOS 25
EXOS 26
EXOS 27
EXOS 28
EXOS 29
EXOS 30
EXOS 31
EXOS 32
EXOS 33
EXOS 34
EXOS inicializálás
Csatorna megnyitása
Csatorna létrehozása
Csatorna lezárása
Csatorna megszüntetése
Karakter olvasása
Blokk olvasása
Karakter írása
Blokk írása
Csatornakészenlét olvasása
Csatornaállapot beállítása és olvasása
Speciális funkció
EXOS változó olvasása, írása vagy átbillentése
Csatorna átirányítása olvasáshoz
Csatorna átirányítása íráshoz
Alapértelmezés szerinti periférianév beállítása
Rendszerállapot lekérdezése
Periféria felvétele
EXOS határ olvasása
Felhasználói határ beállítása
Szegmensigénylés
Szegmens-felszabadítás
Rendszerbővítők letapogatása
Csatornapuffer kijelölése
Hibakódok értelmezése
Modul betöltése
Áthelyezhető modul betöltése
Idő beállítása
Idő olvasása
Dátum beállítása
Dátum olvasása

Ezek után lássuk, mi történik, ha az alkalmazó programja egy EXOS hívást tartalmaz:

(hívó)
(hívó+1)
(hívó+2)
F7
n
CC
RST 30
DEFB n

; funkciószám
; tetszőleges kód, az
; alkalmazói program folytatása

A processzor a 0030H címre ugrik, az őt követő (a processzor feltevése szerint a visszatérési) cím pedig a verembe kerül.

0030
0031
0034
0035
0036

003F
0040
0041
0042
0045
0047
004A
004C
004E
F3
32 5A 00
B7
E3
18 07

7E
23
E3
32 59 00
DB B3
32 55 00
3E 00
D3 B3
C3 10 C4
DI
LD (005A),A
OR A
EX (SP),HL
JR 003F

LD A,(HL)
INC HL
EX (SP),HL
LD (0059),A
IN A,(B3)
LD (0055),A
LD A,00
OUT (B3),A
JP C410
; megszakítás letiltás
; eredeti A tárolása
; NC státusz
; HL: a RST 30H utasítász
; követő cím (hívó+1)

; funkcióhívás argumentum
; visszatérési cím (hívó+2)
; verembe
; argumentum tárolása
; hívó 3. LAP-szegmense
; munkaterületre
; EXOS szegmens
; a 3. LAP-ra
; elugrik a végrehajtásra

A vezérlés tehát a 0. ROM-szegmensre kerül. Itt

Ennek kettős funkciója van. Ha 7. bitje 1, az EXOS verem rendezett. További bitjein csak akkor van 0-tól különböző érték, ha valamilyen perifériaművelet közben történt a hívás. Ez lesz majd a tiltó feltétel bizonyos (perifériák számára tilos) funkcióknál (pl. csatornanyitás, -zárás).

C410
C412
C415
C417
C419
C41C
C41F
C420
C422
C424
C426
C42A
C42D
C42E
C42F
C430
DB B2
32 54 00
3E FF
D3 B2
22 7C BF
21 79 BF
7E
CB 7F
CB FE
20 07
ED 73 7A BF
31 17 B2
F5
17
B7
1F
IN A,(B2)
LD (0054),A
LD A,FF
OUT (B2),A
LD (BF7C),HL
LD HL,BF79
LD A,(HL)
BIT 7,A
SET 7,(HL)
JR NZ,C42D
LD (BF7A),SP
LD SP,B217
PUSH AF
RLA
OR A
RRA
; pillanatnyi 2. LAP
; tárolása
; rendszerszegmens
; a 2.LAP-ra
; eredeti HL letárolás
; EXOS flag-byte
; (alapérték 80H)
; veremrendezés kell?
; "verem rendben"
; átugorja, ha nem kell
; pillanatnyi SP tárolás
; EXOS verem
; flag státusz -> verem
; (A) <- CY
; NC státusz
; hívási státusz:
;    CY=0, ha EXOS n
;    CY=1, ha megszakítás
;    NZ, ha periféria hív

A kialakuló státuszban az átvitelbit csak akkor lehet 1, ha a vezérlés máshonnan (konkrétan a 0038H címen induló megszakításkezelőről) került ide. Ez a bit téríti majd el a közös előkészítés után a megszakítás rutint.
Ezután a hívó rendszerállapotának további mentése és a saját visszatérési ág előkészítése következik:

C431
C432
C433
C436
C437
C43A
C43D
C440
C441
C442
C444
C445
C448
C449
C44B
C44D
C44F
C452
C453
C454

C456
C459
08
F5
2A FE BF
E5
2A 54 00
22 FE BF
21 FD BF
7E
F5
DB B1
77
2A 7C BF
E5
FD E5
DD E5
3E C9
32 57 00
67
08
38 5C

3A 5A 00
08
EX AF,AF'
PUSH AF
LD HL,(BFFE)
PUSH HL
LD HL,(0054)
LD (BFFE),HL
LD HL,BFFD
LD A,HL
PUSH AF
IN A,(B1)
LD (HL),A
LD HL,(BF7C)
PUSH HL
PUSH IY
PUSH IX
LD A,C9
LD (0057),A
LD H,A
EX AF,AF'
JR C,4B2

LD A,(005A)
EX AF,AF'
; státusz háttérbe
; és verembe
; USR_P2,P3
; verembe
; a hívó P2, P3 SZG.-ei
; letárolás
; USR_P1 címe
; USR_P1
; verembe
; pillanatnyi P1
; letárolás
; eredeti HL vissza
; verembe
; verembe
; verembe
; "RET"
; a visszatérési ágba
;
; hívási státusz
; megszakítás kezelése,
; ha CY=1
; eredeti A
; háttérbe

Ennyi előkészítés után az EXOS végre előveszi a tárolt funkciókódot és elkezdi a szétválogatást. Az egyetlen funkció, amit azonnal lekérdez és minden feltétel nélkül végre is hajt, a rendszerváltozók elérése (EXOS 16):

C45A
C45D
C45F
3A 59 00
FE 10
CA F9 C8
LD A,(0059)
CP 10
JP Z,08F9
; EXOS argumentum
; EXOS 16?
; végrehajtása

Az összes további funkciót már csak akkor végzi el, ha a beállított CY bit sértetlenül túléli a saját visszatérési ágban való megfordulást:

C462
C463
C466
37
CD 56 00
CD 80 C5
SCF
CALL 0056
CALL 0580
; CY=1
; visszatérési ág
; szétválogatás

A C580H rutin ui. - mint látni fogjuk - mindenekelőtt a CY bitet ellenőrzi, és ha törlődött, ILLFN hibára fut. De vajon mi történhet ezzel a bittel a visszatérési ágban? Alapesetben itt csak egy EI:RET utasítássort talál, tehát nem változik. Ha azonban éppen megszakításkezelés folyik, a kezelő átírta ezt a címet OR A:RET-re. Megszakítások kezelése közben tehát az EXOS nem fogad el funkcióhívásokat (kivéve az EXOS változók írását, olvasását).
Egyelőre fogadjuk el, hogy megtörténik a funkcióhívások szétválogatása és végrehajtása. Most azt nézzük meg, hogyan tér vissza a rendszer a hívóhoz. Mindenekelőtt visszaállítja a hívó regisztereit és LAP-konfigurációját:

C469
C46A
C46C
C46D
C470
C472
C474
C475
C478
C47B
C47C
C47E
C47F
C480
C483
C484
C487
C488
C489
C48A
C48B

C48D
F3
26 B7
6F
22 54 00
DD E1
FD E1
E1
22 7C BF
21 FD BF
7E
D3 B1
F1
77
2A FE BF
E3
22 FE BF
E1
F1
08
F1
20 22

32 79 BF
DI
LD H, B7
LD L,A
LD (0054),HL
POP IX
POP IY
POP HL
LD (BF7C),HL
LD HL,BFFD
LD A,(HL)
OUT (B1),A
POP AF
LD (HL),A
LD HL,(BFFE)
EX (SP),HL
LD (BFFE),HL
POP HL
POP AF
EX AF,AF'
POP AF
JR NZ,C4AF

JR (BF79),A
; megszakítás letiltás
; "OR A"
; EXOS visszaadott A
; visszatérési ágba
; veremből
; veremből
; veremből
; ideiglenes tárolás
; USR_P1 címe
; hívó P1
; belapozza
; USR_P1 veremből
; vissza
; hívó P2,P3
; USR P2, P3 veremből
; vissza
; hívó P2, P3
; hívási státusz
;
; flag-byte
; átlépi a szoftver-
; megszakítás kezelést
; belépési értékre

Ezután pedig ellát egy - a felhasználó számára igen érdekes - feladatot: a szoftver megszakítás kezelését (pontosabban indítását). A rutin a BFF2H rendszerváltozó (FLAG-SOFT-IRQ) értékét teszteli. Ide pl. valamelyik megszakításkezelő (billentyűzet, hálózat) írhatott be értéket. Ha a rendszer 0-tól különböző értéket talál itt, és a 003D/E címen is értelmes (0-tól különböző) érték (a felhasználói szoftver megszakításkezelő címe) van, úgy változtatja meg saját visszatérési ágát, hogy onnan majd egy JP nn-re kerüljön:

C490
C493
C494
C496
C499
C49A
C49D
C49E

C4A1
C4A2
C4A3
C4A4
C4A6
C4A8
3A FC BF
B7
28 15
32 C7 BF
AF
32 F2 BF
E5
2A 3D 00

7C
B5
E1
28 05
3E 18
32 57 00
LD A,(BFF2)
OR A
JR Z,C4AB
LD (BFC7),A
XOR A
LD (BFF2),A
PUSH HL
LD HL,(003D)

LD A,H
OR L
POP HL
JR Z,C4AB
LD A,18
LD (0057),A
; FLAG_SOFT_IRQ
; értéke
; átlépi, ha nincs IRQ
; CODE_SOFT_IRQ beáll.
; A=0
; törli a FLAG_...-et
;
; felhasználói megszakítási
; rutin címe
;
; HL=0?
;
; átlépi, ha igen
; "JR"
; a visszatérési ágba
; ("JR 003C" beállítás)

Egy kis kitérőként próbáljuk ki ezt a lehetőséget:

10 PRINT 1
20 POKE 49138,33
30 GOTO 10

A szoftvermegszakítás egy EXOS funkcióhívás után történik meg, tehát a rendszer a második kiírásnál vette észre a megszakítási kódot. A megszakítás BASIC-ben való kezeléséről később lesz szó.
Térjünk vissza a funkcióhíváshoz és kövessük a befejezés lépéseit:

C4AB
C4AF

B226
B227
B229
B22A
B22D
ED 7B 7A BF
C3 26 B2

7C
D3 B3
7D
2A 7C BF
C5 51 00
LD SP,(BF7A)
JP B226

LD A,H
OUT (B3),A
LD A,L
LD HL,(BF7C)
JP 0051
; SP belépési érték
;

; hívó P3
; visszalapozás
; hívó P2
; eredeti HL vissza
; visszatérési ágra

A visszatérési ág változhat az előzményektől függően. Egy átlagos funkcióhívásnál már csak a hívó 2. LAP szegmensének visszalapozása van hátra a teljes rendszerállapot visszaállításához. Itt kapja meg az A egy ügyes trükkel azt az értéket, amit a végrehajtó rutin adott vissza (a hibakódot vagy 0-t), és a C46DH című utasítással tárolt le a rendszer éppen a LD A,n utasítás argumentumaként.

0051
0053
0055
0056
0057
D3 B2
3E XX
B7
FB
C9
OUT (B2),A
LD A,XX
OR A
EI
RET
; hívó P2 visszalapozás
; EXOS visszaadott A
; státusz
; megszakítás eng.
; vissza a hívóhoz

Ha szoftvermegszakítás történt, a rutin legvége másként alakul:

0057
003C
18 E3
C3 cc cc
JR 003C
JR cccc
;
; felhasználói megszakítási
; rutinra

Áttekintettük tehát az EXOS funkcióhívás főágát, működési logikáját. Nem láttuk viszont még a funkciók tényleges szétválogatását (kivéve az EXOS 16-ot). Térjünk tehát vissza ehhez a mozzanathoz.
A rutin először (az EXOS 0 kiemelése mellett) a hibás argumentumokat szűri ki (a törölt CY bit jelentésével már foglalkoztunk).

C580
C582
C583
C586
C588
C58B
C58D
C58F
C591
30 20
B7
CA 53 C6
FE 0C
DA 49 CB
FE 23
30 10
D6 11
38 0C
JR NC,C5A2
OR A
JP Z,C653
CP 0C
JP C,CB49
CP 23
JR NC,C59F
SUB 11
JR C,C59F
; (FE) hiba, ha NC
; EXOS argumentum (n)
; EXOS 0, végrehajtása
; n<12?
; ha igen, elugrik
; n>34?
; (FF) hiba, ha igen
; A=n-17
; (FF) hiba, ha 11<n<17

Mint látható, az EXOS 1-11 hívásokat egy csoportban kezeli a rendszer. Az EXOS 0 végrehajtási címe C653H, a 17-34 közötti funkcióhívások belépési pontjait pedig egy ROM-táblázatból határozza meg a program:

 

 

; EXOS 17-34 belépési címek megállapítása
; (0<=A<=17 alapján)
C593
C596
C597
C598
C599
C59A
C59B
C59C
C59D
C59E

C59F
C5A1

C5A2
C5A4
21 0A C0
87
85
6F
7E
23
66
6F
AF
E9

3E FF
C9

3E FE
C9
LD HL,C00A
ADD A,A
ADD A,L
LD L,A
LD A,(HL)
INC HL
LD H,(HL)
LD L,A
XOR A
JP (HL)

LD A,FF
RET

LD A,FE
RET
; belépési cím táblázat
; A=2*A
;
; HL=HL+2*A
; belépési cím LO
; HI címe
; belépési cím HI
; HL: belépési cím
; A=0
; végrehajtó rutinra

; ".IFUNC" hiba
; nem megengedett EXOS kód

; ".ILLFN" hiba
; nem hozzáférhető hívás

A táblázat:

C00A
C012
C01A
C022
C02A
82 C6 81 C6 E5 C7 08 C8
98 C6 F2 C6 FC C6 0E C7
8E C7 18 C8 5E CD 3A C9
5F C9 2C CB 3F CB 40 CB
3D CB 3E CB

Ezzel tehát megismertük a 0. és 16-34. funkcióhívások belépési pontjait. Foglaljuk össze, milyen feltételekkel indul ezeknek a rutinoknak a végrehajtása. A regiszterek általában eredeti értékükkel lépnek be. Kivétel ez alól a HL és az A, ezeket használta a rendszer a belépésig. Fontos viszont, hogy az AF' regiszter milyen értéket tartalmaz. A'-ben a hívás előtti akkumulátorértéknek kell lenni (pl. csatornaszám), F' pedig a C430H-n leírt hívási státusz.
Keményebb dió az 1.-11. funkciók csoportjának kezelése, a végrehajtókhoz való eljuttatás követése. Néhány dolgot érdemes összefoglalóan elmondani a perifériakezelőkről és a csatornákról, hogy jobban megérthessük a rendszer működését. Mi a közös az egy csoportban kezelt funkcióhívásokban? A felsorolásból láthatjuk, hogy ezek kivétel nélkül csatornákkal kapcsolatos műveleteket végeznek: nyitás, zárás, kivitel, beolvasás, vezérlés. Nyilvánvaló hogy a csatornaművelet önmagában értelmetlen dolog, a csatornáknak mindig valamilyen perifériához kell kapcsolódniuk. Ha pl. egy karaktert írunk a 104-es csatornába, ennek csak akkor van értelme, ha ehhez a számhoz egy perifériakezelő kötődik (jelen esetben a PRINTER), ami tudja, hogy mit kell kezdenie a kapott karakterrel.
Természetes tehát, hogy a csatornák élete a csatornaszám és perifériakezelő egymáshoz rendelésével (és az ezt nyilvántartó leíró elkészítésével) kezdődik és az összekapcsolás megszüntetésével fejeződik be. Ezt a funkciót még el tudná végezni belső rutinjaiban is az EXOS (pusztán az előző pontban megismert periférialánc adatai alapján), de a rendszer a csatornanyitási művelet során nemcsak ezt a (16-bájtos) leírót hozza létre. A csatornaműveletek egyszerűsítésére az EXOS közvetlenül a leíró alá illeszti a periféria által az adott esetben igényelt RAM-területet: a csatornapuffert (aminek felső határát meg is adja a perifériakezelőnek - mint látni fogjuk - minden híváskor az IX regiszterpárban). Azt pedig, hogy az adott csatorna létezéséhez, működtetéséhez mekkora pufferterületre van szükség, ismét csak a hozzárendelt perifériakezelő tudhatja. A PRINTER-kezelő pl. nem igényel külön RAM-ot a munkájához, a VIDEO rendszer viszont a legkülönbözőbb méretű puffereket kérheti, amit saját maga számol ki a kijelzési módot és a videolap méreteit meghatározó rendszerváltozók alapján.
Ezek alapján a csatornanyitási és -zárási műveleteket is a perifériakezelőkre kell bízni - legalábbis ami a szükséges pufferméret meghatározását illeti. A puffer kiutalását és nyilvántartásba vételét természetesen ismét a - minden memóriamenedzselési feladatot magára vállaló - rendszerprogramtól kell kérni (EXOS 27), majd a megkapott RAM-területtel ismét a kezelő rendelkezhet saját igényei szerint (pl. bejegyezheti a megnyitott videolap paramétereit).
A rendkívül felhasználócentrikus EXOS több lehetőséget is ad tetszőleges perifériakezelők létrehozására és rendszerbe illesztésére. Az egységes elérhetőség érdekében azonban ezeknek a kezelőknek - bizonyos pontjaikon - szabványosnak kell lenniük. Ilyen a periféria álleírója, amivel a 2.5 alfejezetben még foglalkozunk, és a kezelő végrehajtó rutinjainak belépési pontjait is egy egységes táblázatba kell rendezni. Ez a táblázat - kétbájtos címek egymásutánja - adott sorrendben tartalmazza a csatornaműveletek teendőit ellátó rutincímeket (és még néhány más alprogramot). Az EXOS 1, 2, ... stb. funkciók tényleges végrehajtóinak címe ezen táblázat 1., 2. stb. eleme. Az általában közvetlenül az álleíró után elhelyezett táblázat kezdőcímét a leíró TAB eleme adja meg (3. LAP címként).
Ahhoz, hogy az EXOS n funkcióhívás ebből a táblázatból megállapíthassa a végrehajtó kezelő alprogram címét, előbb a csatornaszám (a funkció hívásakor az A regiszterben megadott érték) alapján meg kell keresnie a hozzárendelt periférialeírót. Erre szolgál - többek között - a csatornaleírók lánca.
Mint az előbb már utaltunk rá, egy csatorna megnyitásakor a szükséges paramétereket az EXOS egy 16-bájtos csatornaleíróban könyvtárazza be. Ezeket a leírókat láncszerűen kapcsolja egymáshoz (erről az előbbi pontban már írtunk, és ki is olvastuk az aktuális leíróláncot). A BFBDH báziscím alatti pointer az első csatornaleíró fölé mutat. Ehhez az új báziscímhez képest a -1., -2. és -3. elem a következő csatornaleíró mutatója: a leírót tartalmazó szegmens számát (-1:) és címét (LO:-3., HL-2.) adja meg. A -5. bájt a belső csatornaszám (a hívó által megadott csatornaszám +1). Ezután a rendszer belső paramétereket tárol (pl. az igényelt területék nagyságát a puffer mozgatásához), majd egy újabb pointer következik: a csatornához rendelt periféria mutatója; szegmensszám a -12., leírócím a -13. és -14. bájton.
Hogy egy konkrét példát is lássunk, vizsgáljuk meg az előző fejezetben példaként kiíratott csatornalánc első leíróját. A BFBDH bázisszám alatti pointer: FF/665C. Az e cím előtti 16 bájt:
00 00 F9 66 FF 00 00 80 04 C0 00 67 00 DC 61 FF.
A leíró említett elemei:

665C - 01 FF
665C - 02 61
665C - 03 DC: a következő leíró báziscíme: FF/61 DC
665C - 05 67: a leíró a 66H csatornához tartozik (ez a BASIC £102-es VIDEO csatornája)
665C - 0C FF
665C - 0D 66
665C - 0E F9: a periférialeíró pointere.

A kijelölt periféria (ugyancsak az előző fejezet példájaként kiírt periférialánc alapján) a VIDEO.
A pointer alatti két bájtot ugyancsak a rendszer használja a csatornaátirányítások bejegyzésére. Mint az EXOS 17 és 18 funkcióknál látni fogjuk, az adott csatorna helyett használandó másodlagos csatorna számát a báziscím -15. (átirányítás olvasáshoz), ill. báziscím -16. (átirányítás íráshoz) címen jegyzi be a rendszer.
A csatornaterületeket a rendszermemória alján építi fel az EXOS. Ha a perifériakezelő az adott csatorna nyitásához nem igényel RAM-ot, a 16-bájtos leíró alsó címe lesz az új EXOS határ. Ha puffer is tartozik a csatornához, az itt kezdődik (az EXOS a (báziscím -16) értéket adja meg a kiutalt pufferterület báziscímeként az IX regiszterben). A következő csatornanyitáskor az új leíró már a lefoglalt csatornapuffer alatt kap helyet.
A csatornakezelés meggyorsításához még egy fogást alkalmaz az operációs rendszer. A rendszerváltozók területén, a BFA5H címtál kezdődően néhány bájtot fenntart azoknak a csatornáknak a paramétereihez, amelyekkel a legutóbb foglalkozott (és így feltételezhetően a leggyakrabban használt, leggyorsabban elérendő csatornák). Minden csatornához 4 bájtot rendel: a csatorna (belső) számát és a leíró pointerét. Három ilyen blokkot kezel ezen az aktuális csatornaterületen. Ha egy új csatornával kell foglalkoznia, egy blokkal (4 bájttal) feljebb tolja ezt a tartományt (ezzel a legrégebben használt csatornaparaméter blokkja kiesik) és felveszi az új csatorna paramétereit. Ez a 3 blokk éppen elég a számítógép működési idejének jelentós részét kitevő szerkesztési műveletekhez (EDITOR, KEYBOARD, VIDEO), így az alapcsatornákat nem kell a leíróláncban keresgélnie, mindig kéznél vannak (ezt az állapotot láthattuk az előző pont végén a rendszerváltozókról készített DUMP értelmezésénél).
Ezek után kövessük végig: milyen lépéseket tesz meg az EXOS, amíg a vezérlést átadja a funkcióhívást végrehajtó perifériakezelőnek.

1. A csatornanyitási, létrehozási funkció (EXOS 1, 2) esetén

BF84:
BF85:
BF86 / 7:
BF88:
BF89:
FLAG; 1, ha VIDEO periféria 0, egyébként
belső csatornaszám
a periférialeíró címe
a periférialeíró szegmense
jelzőbájt; FFH, amíg nincs puffer igényelve a csatornához;
C53E

C541
C542
C543
C544
C546

C547
C548
C549
C54A
C54C
C54D
01 19 53

08
7E
B7
3E FA
C0

C5
E3
39
3E FC
E1
D0
LD BC,5319

EX AF,AF'
LD A,(HL)
OR A
LD A,FA
RET NZ

PUSH BC
EX (SP),HL
ADD HL,SP
LD A,FC
POP HL
RET NC
; verem alsó határ
; (-ACECH)
; funkciószám verembe
; periférialeíró
; "TYPE"=0?
; ".NODEV"
; hiba, ha nem

;
; HL=alsó határ
; aktuális veremhatár
; ".STACK"

; hiba, ha SP a megengedett
; határ alatt van

A rendszer ezután beállítja a perifériakezelőnek átadandó IY és B' paramétereket, majd a belépési pont-táblázatból beolvassa az A-ban hozott funkciószámhoz tartozó címet.

C54E
C54F
C550
C554
C556
C558
C559
C55A
C55B
C55C
C55D
C55E
C55F
C560
C561
C563
C564
C565
C566
C568
C569
C56A
C56B
C56C
44
4D
FD 21 00 40
FD 09
DB B1
F5
23
23
23
4E
23
46
23
7E
D3 B1
08
87
6F
26 00
09
7E
23
66
6F
LD B,H
LD C,L
LD IY,4000
ADD IY,BC
IN A,(B1)
PUSH AF
INC HL
INC HL
INC HL
LD C,(HL)
INC HL
LD B,(HL)
INC HL
LD A,(HL)
OUT (B1),A
EX AF,AF'
ADD A,A
LD L,A
LD H,00
ADD HL,BC
LD A,(HL)
INC HL
LD H,(HL)
LD L,A
;
; BC: periférialeíró
;
; IY: leíró címe P2-n
; A: a leíró szegmense
; verembe
; (IRQ_FLAG)
; (FLAGS)
; TAB (alsó byte)
; C: táblacím LO
; TAB (felső byte)
; B: táblacím HI
; SEG
; a tábla szegmense
; az 1. LAP-ra
; A: funkciószám (n)
; A=2*n
;
; HL=2*n
; HL: n. táblaelem c.
; rutincím LO
;
; rutincím HI
; HL: n. rutin címe

Az előkészítés után meghívja a perifériakezelő végrehajtó rutinját a B217H lapozórutin közvetítésével. Előtte átállítja a hívási státuszt meghatározó BF79H jelzőbájtot, hiszen ezután perifériaprogram fut. Ez a bájt egészen a visszatérésig (amikor újra alapértékre áll) tilt bizonyos funkcióhívásokat.

C56D
C56E
C570
C571
C572

C573
C574
C575
C578
C579

C57A

C57D
C57E
C57F
7B
D3 B1
C1
7A
08

E5
D9
21 79 BF
34
E3

CD 17 B2

E1
35
C9
LD A,E
OUT (B1),A
POP BC
LD A,D
EX AF,AF'

PUSH HL
EXX
LD HL,BF79
INC (HL)
EX (SP),HL

CALL B217

POP HL
DEC (HL)
RET
; A: a csatornaleíró
; szegmense 1. LAP-ra
; B: perif.leíró szegm.
; A: csatornaszám
; A: végrehajtó rutin
; szegmense
;
;
; jelzőbyte címe
; "ettől kezdve perif."
; HL: végrehajtó rutin
; címe
; belapozás a 3. LAP-ra
; és végrehajtás
; HL=BF79
; jelzőbyte alapérték
;

A perifériakezelő végrehajtó rutinjából való visszatérés után - az 1. és 2. funkcióhívások szétválogató ágának befejezéseként még ellenőrzi a BF89H jelzőbájton (amit maga állított FFH-re a kezelő hívása előtt), hogy nem feledkezett-e meg a periféria csatornanyitásért felelős rutinja a pufferigénylésről (EXOS 27 hívás). Ez akkor is kötelező, ha a periféria nem igényel puffert, mert a csatornaleírót is ez a funkcióhívás hozza létre, majd - jelezve, hogy feladatát ellátta - 0-ra állítja a fenti jelzőbájtot. Erre tehát nekünk is ügyelnünk kell egy esetleges új periféria létrehozásakor.

2. A fennmaradó csatornaműveleteket (EXOS 3-11) másképp készíti elő rendszer.

CD5A
CD3C

CD3F

CD40
CD42
CD44
CD45
CD46
CD49
CD4C
CD4F
CD51
CD52
CD53
CD54

CD55
CD56
CD58
CD59
CD5A
0E 05
21 BD 7F

08

3E FF
D3 B1
08
08
22 81 BF
32 83 BF
CD 47 C6
06 00
C8
0C
0D
C8

08
ED 42
BE
09
20 E9
LD C,05
LD HL,7FBD

EX AF,AF'

LD A,FF
OUT (B1),A
EX AF,AF'
EX AF,AF'
LD (BF81),HL
LD (BF83),A
CALL C647
LD B,00
RET Z
INC C
DEC C
RET Z

EX AF,AF'
SBC HL,BC
CP (HL)
ADD HL,BC
JR NZ,CD45
; csatornaszám helye
; csatornalánc báziscím
; az 1. LAP-on
; keresett csatornaszám
; háttérbe
; rendszerszegmens
; az 1. LAP-ra
;
;
; b.cím munkaterülete
; szegmensszám munkat.
; pointer kiolvasása
;
; vissza, ha lánc vége
;     (Z)
;
; vissza, ha C=0 volt
;     (Z)
; A: keresett csat.szám
; csatornaszám címe
; a leíróban=A?
;
; tovább keres, ha nem

    ; Ha megtalálta a keresett csatornaszámot:
CD5C
CD5D
08
C9
EX AF,AF'
RET
; NZ státusz

Ha a rutinból (Z) státusszal tér vissza a program, nem találta meg a keresett csatornaszámot. Ha viszont megtalálta, HL-ben a leíró báziscíme, A-ban a szegmense található. Érdemes megjegyezni, hogy ezt a pointert a BF81/2H, ill. BF83H munkaterületre is letárolta a keresőrutin.
Az előkészítés befejezéseként az EXOS beállítja a végrehajtó rutinnak átadandó paramétereket. Az IX regiszterbe a csatornaleíró báziscíme -16 értéket tölt. Így IX a leíró legalsó bájtjára mutat, alatta kezdődik a csatorna RAM-területe. A perifériaművelet kezelése során tehát IX-ben van az adott csatornához kiutalt puffer báziscíme. Ezt tehát feltétlenül be kell állítanunk egy esetleges közvetlen hívás előtt is.
Ezután a leíróból beolvassa a csatornához rendelt periféria báziscímét, és a már megismert módon (a C53EH rutin felhasználásával) belapozza és meghívja a belépési címtáblázat funkcióhívásnak megfelelő sorszámú rutinját (tehát pl. EXOS 7 esetén a 7. rutint).
Ezzel gyakorlatilag be is fejezte az EXOS a csatorna I/O műveletek kezelését. A csatornát lezáró (EXOS 3, 4) funkcióhívásoknál még elvégzi a memóriarendezési feladatokat, majd a háttérregiszterek visszaállítása után innen is rátér a rendszerhívások - már megismert - visszatérési ágára.

Foglaljuk végül össze, milyen feltételeket biztosít, milyen paramétereket ad át az EXOS a funkcióhívások végrehajtóinak, a perifériakezelőknek:

0. LAP:
2. LAP:
3. LAP:
4. LAP:
a nulláslap szegmense (esetünkben F8H)
a csatornaleíró szegmense
az FFH rendszerszegmens
a perifériakezelő szegmense
A:
BC:
DE:
HL
B':
a csatornaszám
eredeti érték a hívótól
eredeti érték a hívótól
a használt (a meghívott) rutin címe
a futó perifériakezelő leírójának szegmensszáma
IY a periférialeíró báziscíme (előtte lehet a perifériának az inicializálás folyamán kiutalt RAM)
IX a csatorna RAM báziscíme; a használható terület ez alatt indul az (IX-1) címen

A kezelőtől az EXOS nem várja el egyik regiszter -megőrzését sem, de maga megőrzi és a hívónak továbbadja a kezelőtől visszakapott A, BC és DE regisztereket.
Végül a funkcióhívások konkrét áttekintése előtt még egy közös vonatkozású megjegyzés. A leírás során többször említettük, hogy adott esetben az EXOS valamilyen hibával tér vissza. Valójában maga az EXOS nem ad semmilyen hibaüzenetet, kizárólag az A visszaadott értékében (ill. a státuszban) jelzi, ha valami rendellenesség történt. A hívó feladata, hogy a visszatérés után ellenőrizze a státuszt és NZ esetén megtegye a szükséges lépéseket. A hibakódokat a 4. Függelékben soroljuk fel.
Ezek után vegyük sorra az EXOS funkcióhívásokat, konkrét adatokkal, példákkal segítve az alkalmazásukat. A példákat általában BASIC-ben építjük fel, hogy segédeszköz nélkül, könnyen kipróbálhatók legyenek. A kimenő adatok között nem említjük külön a minden hívásnál visszaadott (esetleges hibára utaló) státuszt.

EXOS 0: EXOS inicializálás
Belépési pont: C653H (az EXOS rutinok belépési címei a -s szegmensben vannak, ezt külön nem adjuk meg).
Bemenő adat: a C regiszter, amelynek bitjei az inicializálás különböző lépéseit váltják ki és a BF78H rendszerváltozó, amely ugyancsak jelzőbájtként szolgál.
Kimenő adat: nincs.
Funkció: A jelzők értékétől függetlenül törli a melegindítási címet (RST-ADDR; BFF8/9H), a felhasználói megszakítási rutin címét (USER-ISR); valamint a felhasználói szoftver megszakítási rutin címét (SOFT ISR; 003D/EH).
A további végrehajtás a C regiszter bitjeitől függően elágazik:
  • ha BIT 7,C = 1: teljes hidegindítást végez (egyenértékű a RESET gomb kétszeri megnyomásával).

A továbbiakat tekintsük át a legkevésbé drasztikus variációtól:

  • ha minden bit 0: visszatér a hívóhoz (ilyenkor tehát csak a fenti belépési címeket törli).

A megszakítás a hívóhoz való visszatéréskor (most és a további alternatíváknál is) tiltott.

  • Ha BIT 6,C = 1: alaphelyzetbe állítja a szegmenskiosztást (felszabadítja a felhasználónak kiutalt szegmenseket).
    BASIC-ben: nem törli a változókat, a megnyitott csatornákat, de törli a 0-tól különböző számon megnyitott programokat.
  • Ha bármelyik további bit 1: hidegindítás utáni szegmenskiosztást hoz létre.
    • Nullát tölt a:
      PORTB 5
      FLAG-SOFT-IRQ
      REM1
      REM2
      ST-FLAG
      NET-IRQ
      WAIT-SND
      STOP-IRQ
      TIMER
      DEF-CHAN
      DEF-TYPE
    • EXOS változókba és a
      BF89H (csatornapuffer-igénylés)
      BFA5H (aktuális csatorna)
      BFBCH (csatornalánc mutató)
      címekre
  • FFH alapértéket tölt a
    NET-IRQ
    MATCH-NET
    CHAN-NET
    EXOS változókba
  • (csak ha a BF78H jelzőbájt 0 volt): teljesíti a ROM-táblában levő szegmensek RAM-igényét (és 1-re állítja a jelzőbájtot),
  • (csak ha BIT 5,C = 1 volt): újraépíti a ROM-szegmensek periféria láncát, -törli a csatornaterületet,
  • újra TAPE-re állítja az alapértelmezésű eszköz nevét,
  • beállítja a video- és a 1 Hz-es megszakítások engedélyezését,
  • végighívja a perifériák, majd a RAM és ROM rendszerbővítők Inicializálás rutinjait
Felhasználás: minden olyan esetben, amikor egy újonnan induló rendszer tiszta lappal akar kezdeni. Így biztosíthatja az egyértelmű rendszermemóriát az alkalmazói program induláskor. Mi is használhatjuk ezt a funkcióhívást, ha olyan beavatkozást végeztünk a rendszer területén, hogy a változás végigvitelét az EXOS-ra kell bíznunk. Erre egy példát is adunk a következőkben.

A funkció közvetlen hívása nehezen megoldható (a sajátos visszatérési út miatt), de nem is igazán indokolt: a rendszerinicializálás nem az a terület, ahol a megtakarítható mikro- (vagy akár mili-) szekundumoknak jelentősége lenne. Szokásos úton való hívása viszont egyszerű:

LD C,XX
EXOS 00
RET

BASIC-ből betöltött gépi kódú rutinnal:

100 ALLOCATE 100
110 CODE M=HEX$("0E,XX,F7,00,C9")
120 CALL USR(M,0)

Természetesen "XX" helyére az inicializálási lépéseket kiváltó biteket tartalmazó hexadecimális értéket kell írni. A kísérletezve megismerés jegyében javasoljuk az Olvasónak, hogy próbálja ki a hívást különféle beavatkozások után a 00, 10, 20, 40, 60, 80 értékekkel (ez utóbbival csak akkor, ha nincs a gépben féltett program).
Hogy az első komolyabb beavatkozásunkat megtegyük (aminek még sok hasznát vehetjük a továbbiakban), próbáljuk meg saját igényeinknek megfelelően átalakítani a memóriatérképet. Építsünk be a ROM-szegmensek táblázatába egy új elemet valamelyik RAM-szegmens számával. Egy ilyen könyvtárazott ál-ROM előnyeiről már írtunk a 2.3.1 pontban, de ott csak egy igen drasztikus lépéssel, 4. szegmens feláldozásával, értünk el ilyen hatást. Ha valaki nem akar lemondani a 4. szegmens szolgáltatásairól (pl. mert a billentyűzet adott kiosztását szokta meg), vagy egyszerűen nincs is a gépén ilyen helyettesíthető szegmens (egynyelvű verzió), fölvetheti a hidegindítás folyamatának követése alapján: nem lehetne-e a ROM-táblát annyival lejjebb tolni, hogy a tetejére beírhassuk a felvenni kívánt szegmens számát, az így kialakult új táblahatártól pedig újra felépíttetni a rendszermemória ez alatti részeit (ROM-szegmensek RAM-területe, periférialánc, csatornalánc).
Nos, mindennek semmi akadálya, csak a konkrét végrehajtáshoz figyelmesen szemügyre kell venni a beavatkozás előtti rendszerállapotot (kinek-kinek a saját gépén). Az érintett változóterület listája (a 2.3.1 pontban ismertetett DUMP programmal):


A RAM- és ROM-táblák területe pedig:


Esetünkben az ABB9H-ABC9H közötti bájtokat kellene eltolni lefelé (helyet csinálva egy új 4-bájtos táblaelemnek). A konkrét címek ismeretében ezt egyszerűen megoldhatjuk, de nem különösebben bonyolult az általános (a mozgatandó terület határait és méretét a rendszerváltozók alapján megállapító) megoldás sem.
Milyen változókat kell figyelembe venni:
BF97/8H: a mozgatandó terület alsó címe (ROM-tábla alja)
BF9C/DH: a mozgatandó terület felső címe + 1 (RAM-tábla alja).
A két cím különbsége adja a mozgatandó terület nagyságát. Ezek alapján felírhatjuk a program mozgató részét.
A blokk eltolása után már csak két feladatunk maradt:

Ezek után a feladatokat ellátó assembly program:

  ORG 3000H
LD DC,(0BF97H)
LD HL,(0BF9CH)
OR A
SBC HL,DE
PUSH HL
POP BC
EX DE,HL
PUSH HL
LD DE,4
SBC HL,DE
EX DE,HL
POP HL
PUSH DE
LDIR
LD DE,5
SBC HL,DE
LD (HL),0FCH
POP HL
LD (0BF97H),HL
LD (0BF95H),HL
XOR A
LD (0BF78H),A
LD C, 60H
EXOS 00H
RET
; rutin tárolási címe
; ROM-tábla alja
; ROM tábla teteje+1
; NC státusz
; ROM-tábla hossza
;
; BC=mozgatandó hossz
;
;
;
; ROM-tábla alja-4
; DE: új alsó cím
; HL: régi alsó cím
;
; blokkmozgatás
;
; legfelső szegmens címe
; ál-ROM bejegyzése
; HL: tábla alja
; bejegyzés a
; rendszerváltozókba
; A=0
; a jelzőbyte-ba
; RESET-jelzők
; EXOS inicializálás

ill. ugyanez BASIC betöltővel:

100 POKE 540,0
111 POKE 541,48
120 CODE M=HEX$("ED,5B,97,BF,2A,9C,BF,B7,ED,52, E5,C1,EB,E5,11,04,00,ED,52,EB, E1,D5,ED,B0,11,04, 00,ED,52,36, FC,E1,22,97,BF,22,95,BF,AF,32, 78,BF,0E,60,F7,00, C9")
130 SPOKE 252,10,201
140 CALL USR(M,0)

A rutin tárolására választott 3000H cím a BASIC terület közepén van ugyan, de - éppen ezért - kis programok esetén különösebb védelem nélkül is megfelel a rutinok ideiglenes tárolására.
Az EXOS 0 kiváltotta inicializálási folyamatban már az újonnan bejegyzett ROM-szegmens is részt vesz, ezért ne feledkezzünk meg a rutin végrehajtása előtt a szegmens belépési pontját (C00AH a 3. LAP-on) megfelelően kialakítani. Ennek legegyszerűbb módját láthatjuk a BASIC betöltőprogram 130. sorában, ami - egyelőre - egy RET utasításkódot ír a szegmens 10. bájtjára.
Az EXOS 0 hívás törli a melegindítási címet, szoftvermegszakítási címet. Hogy egyértelművé tegyük a BASIC alaphelyzetet, a rutin futtatása után adjunk ki egy BASIC utasítást. Ez ugyan törli a betöltőprogramot is, de az már ellátta a feladatát.
Ellenőrizzük a rendszermemória területeit, változóit, hogy rendben megtörtént-e a változás a teljes rendszerben.

Örömmel láthatjuk, hogy a ROM-tábla alatti területekhez tartozó összes cím, mutató értéke 4-gyel csökkent. Lejjebb csúszott a periférialánc, a csatornaterület és - természetesen - az EXOS határ is. Maga a megváltoztatott ROM-tábla:

Ez tartalmazza (az 5. szegmens előző helyén) a bejegyezni kívánt ál-ROM-ot (FCH), a többi blokk pedig lefelé tolódott. A 4. szegmens blokkjában jól látható, hogy a rendszer újra kiutalta számára az igényelt RAM-területet (természetesen 4-gyel kisebb kezdőcímmel).
Végül ellenőrizzük, hogy a periférialánc hogyan épült újra. A 2.3.1 pontban leírt LANC program futtatásával (az ott kapott listával egybevetve) láthatjuk, hogy a ROM-tábla valamennyi perifériája beláncolódott a hidegindítással analóg módon, csak kisebb báziscímekkel:

A könyvtárazott ROM-szegmens most már a rendszerhez tartozik kikapcsolásig. Ha komoly feladatokat akarunk vele elláttatni, akkor persze a bejegyzett szegmenst védeni kell, meg kell akadályozni, hogy a rendszer felhasználhassa (pl. az EXOS 24 funkcióhívás segítségével).
Egyszerűbb kísérletezéshez azonban így is felhasználhatjuk a szegmenst. Érdekességként - a géppel való ismerkedést segítendő - írjunk pl. a belépési pontra egy egyszerű rutint, amely a hívási kód értékét (C regiszter) minden hozzáforduláskor kiírja az állapotsorba (az egyszerűség kedvéért egy nem törlődő, szabadon használható karakterhelyre).
A feladat megoldása igen egyszerű:

  LD HL,0BEBDH
LD A,30H
ADD A,C
LD (HL),A
RET
; állapotsor 6. byte
; ASCII "0"
; + hívási kód
; kaakter az állapotsorba

BASIC betöltővel:

100 ALLOCATE 100
110 CODE M=HEX$("C9,21,BD,BE,3E,30,81,77,C9")
120 FOR N=0 TO 10
130   SPOKE 252,10+N,PEEK (M+N)
140 NEXT

A 252. szegmens belépési pontjára áttöltött rutin továbbra is RET tel kezdődik. A kijelzési funkció így egy

SPOKE 252,10,0

utasítással aktivizálható és

SPOKE 252,10,201

utasítással kapcsolható ki. Érdemes kipróbálni a különböző - bővítőket is hívó - utasításokat (:HELP; :SZÖVEG; hibát okozó EXOS hívás, pl. nem létező csatornára írás; a RESET gomb megnyomása stb.). Az indítási kódokról még lesz szó a 2.6 alfejezetben.
Még figyelemfelkeltőbb a ROM-ok lekérdezése, ha valamilyen hanghatással is jár. Erre igen egyszerűen felhasználható pl. a BASIC PING utasítás végrehajtó rutinja (5. szegmens, D3BFH cím), csak azt a problémát kell megoldani, hogy a 3. LAP-on hívott bővítő hogyan hívja egy másik szegmens rutinját ugyanezen a lapon. Az egyik lehetséges megoldás, hogy a bővítő programja ellapozza saját magát (az 1. LAP-ra) és ott folytatja a futást:




CIM
LD A,0FCH
OUT (0B1H),A
JP (CIM-8000H)
PUSH DE
PUSH BC
LD A,5
OUT (0B3H),A
CALL 0D3BFH
POP BC
POP DE
RET
; saját szegmensszám
; 1. LAP-ra lapozza magát
; a folytatásra ugrik
;
;
; BASIC szegmens
; a 3. LAP-ra
; "PING" rutin"

Ennek megfelelően kiegészítve a BASIC betöltőt:

100 ALLOCATE 100
110 CODE M=HEX$("C9,21,BD,BE,3E,30,81,77,3E,FC, D3,B1,C3,19,40,D5,C5,3E,05,D3, B3,CD,BF,D3,C1,D1,C9")
120 FOR N=0 TO 26
130   SPOKE 252,10+N,PEEK (M+N)
140 NEXT

Jó kísérletezést!

EXOS 1-11

A csatornakezelő utasítások belépési pontjai csak közvetve adhatóak meg, hiszen ezeket - mint láttuk - az EXOS előkészítése után a csatornához rendelt perifériakezelők hajtják végre. A perifériakezelők belépési pontjait az 5. Függelék tartalmazza. A közvetlen hívás lehetőségével itt csak az EXOS 7 esetén foglalkozunk, ahol ennek a legnagyobb jelentősége lehet.

EXOS 1: csatorna megnyitása
EXOS 2: csatorna létrehozása
  A két hívásról együtt beszélhetünk, hiszen (a TAPE kivételével) a perifériakezelők is azonos módon kezelik ezeket.
Bemenő adatok: A: csatornaszám (0-254)
DE: a csatornához rendelt periféria nevére mutat (pontosabban a név előtti hosszbájtra)
EXOS: változók a végrehajtó periféria igénye szerin

A periférianév a hívó lapkonfigurációjában bármelyik LAP-on lehet, azt az EXOS megtalálja az előkészítés során. A név végén kettőspontnak kell állnia, különben a rendszer file-névnek tekinti és periférianévként a TAPE-t állítja be (alapértelmezésű periférianév).
Használatára egy egyszerű példát mutat a következő BASIC rutin:

100 ALLOCATE 100
110 CODE NEV=CHR$(8)&"RINTER:"
120 CODE M=HEX$("11")&WORD$(NEV)&HEX$("3E,01,F7,01,C9")
130 CALL USR(M,0)
140 PRINT £1:"SZOVEG"

Ez a

LD DE,NEV
LD A,1
EXOS 1
RET

gépi programot tölti a memóriába és hívja meg. A rutin futtatása után ugyanúgy írhatunk a nyomtatóra az £1-es csatornán, mint ha OPEN £1:"PRINTER:" BASIC utasítással nyitottuk volna meg. A név beírása helyett persze kihasználhatjuk azt is, hogy a ROM-szegmensekben is megtalálhatók a nyitáshoz szükséges periférianevek (mint ezt már említettük a 2.3.1 pontban):

100 ALLCLATE 100
120 CODE M=HEX$("11,43,FE,3E,01,F7,01,C9")
130 CALL USR(M,0)
140 PRINT £1:"SZOVEG"

Ebben az esetben az 1. szegmens FE43H címén tárolt névre állítottuk DE-t, felhasználva azt a tényt, hogy a USR függvény kiértékelésekor az 1. szegmens van belapozva a 3. LAP-on.
Ilyen névtárolási pontok:

A példaként választott PRINTER perifériakezelőhöz viszonylag egyszerűen nyitható csatorna. Itt ui. nem kell előkészíteni a megnyitást az EXOS változók területén. A VIDEO kezelő viszont ezekből a változókból szerez tudomást a megnyitandó csatorna (VIDEO lap) méretéről, kijelzési módjáról. A MODE-VID, COLR-VID, X-SIZ-VID és Y-SIZE-VID EXOS-változók beállítására és a csatorna nyitására már láttunk példát a 2.3.1 pontban, a bejelentkezési üzenet előkészítésénél. Most gyorsítsuk egy kicsit ezt az eljárást a változók közvetlen beírásával! Nyissunk meg egy 32*16 karakternyi méretű grafikus lapot 16 színű módban az EXOS 1 funkcióhívás segítségével (pl. az 5. csatornára), majd - egyelőre BASIC-ben - jelezzük ki az új lapot és rajzoljunk is rá, hogy ellenőrizzük a beállított paramétereket.

100 ALLOCATE 100
110 CODE NEV=CHR$(6)&"VIDEO:"
120 CODE M=HEX$("11")&WORD$(NEV)&HEX$("21,01,02,22,DB,BF,21,20, 10,22,DD,BF,3E,05,F7,01,C9")
130 CALL USR(M,0)
140 SET BORDER 10
150 DISPLAY £5:AT 5 FROM 1 TO 10
160 PLOT £5:500,400,ELLIPSE 150,150,

A változás a PRINTER-csatorna nyitásához képest:

  ...
LD HL,0201H
LD (0BFDBH),HL

LD HL,1020H
LD (0BFDDH),HL
...

;
; MODE_VID=1 (grafikus)
; COLR_VID=2 (16 szín)
;
; X_SIZ_VID=32
; Y_SIZ_VID=16

Megjegyzendő, hogy ha ezt a közvetlen módszert egy olyan programban alkalmazzuk, ahol határozatlan a 2. LAP tartalma, a rendszerszegmenst is be kell lapozni a változók letárolása előtt.
Végül még egy megjegyzést. A TAPE perifériakezelő megkülönbözteti az EXOS 1 és EXOS 2 hívásokat. Egy nem létező csatorna megteremtése, pufferfoglalás stb. az EXOS 2 funkcióhívással kérhető, míg az EXOS 1 egy már létező csatornát nyit meg az adatforgalomnak.

EXOS 3: csatorna lezárása
EXOS 4: csatorna megszüntetése
Bemenő adatok: A a csatorna száma (0-254)

A két funkciót minden beépített perifériakezelő azonos módon kezeli. Maguk a perifériák csak - a saját működésükhöz tartozó - részleges funkciókat látnak el (jelzőbájtok nullázása, a VIDEO-kezelőnél a lezárt csatornához tartozó éppen kijelzett sorok letakarása stb.). A nyilvántartásból való törlést, az ezzel járó memóriarendezést az EXOS végzi.
A funkció előkészítés nélkül hívható:

LD A,CSAT
EXOS 3

BASIC betöltővel (egyben ellenőrizve is, hogy valóban megtörtént-e a csatorna lezárása):

100 ALLOCATE 100
110 OPEN  £1:"VIDEO:"
120 CODE M=HEX$("3E,01,F7,03,C9")
130 CALL USR(M,0)
140 PRINT £1: "OK?"

(Német gép! # = £ csatorna hivatkozás!) A futás hibajelzéssel áll le a szóban forgó csatornára írni próbáló utasításnál, igazolva ezzel a lezárást.
Ha olyan csatornát próbálunk meg lezárni, amelyik nincs is nyitva (nincs benne a csatornaláncban), az EXOS hibakóddal tér vissza. Ellenőrizzük ezt a kódot, felhasználva, hogy a HL értékét a USR kiértékelő rutinja visszaadja a BASIC-nek. Az EXOS-ból való visszatérés után írjuk a hibakódot hordozó A értékét HL-be:

LD H,0
LD L,A

100 ALLOCATE 100
110 CODE M=HEX$("3E,01,F7,03,26,00,6F,C9")
120 PRINT "Hibakod:";USR(M,0)

Az indítás után kapott hibakód (FBH) ICHAN ("csatorna nem létezik") hibát jelez (l. a 4. Függeléket).

EXOS 5: karakterolvasás
Bemenő adat: A: csatornaszám
Kimenő adat: B: az olvasott érték

Az olvasott értékre csak a csatornák egy részénél illik a karaktermegnevezés, pl. grafikus módú csatornánál a VIDEO-kezelő a kurzorkoordináta pontjának palettaszínét adja vissza a hívás eredményeként.
Fel kell hívni a figyelmet, hogy egyes perifériák (a kezelőkön belül) várakoznak, amíg a karakter nincs készen az olvasásra (pl. a KEYBOARD kezelő vár addig, amíg meg nem nyomunk egy billentyűt).
Az első felhasználási példánk legyen a billentyűleolvasás. Az alapértelmezésben megnyitott 69H csatornáról olvassunk be egy karaktert és értékét adjuk vissza a BASIC-nek:

LD A,69H
EXOS 5
LD H,0
LD L,B
RET

A BASIC programban írjuk ki ezt az értéket, mint a megnyomott billentyű kódját és térjünk vissza az újabb karakter olvasására:

100 ALLOCATE 100
110 CODE M=HEX$("3E,69,F7,05,26,00,68,C9")
120 PRINT "A lenyomott billentyu kodja: ";USR(M,0)
130 GOTO 120

A rutinnal kiolvashatunk minden billentyűkódot (különböző shiftelési módok mellett). Figyelemre méltó a funkcióbillentyűk hatása. Azt láthatjuk, hogy a kezelő (saját pufferjéből) a billentyűhöz rendelt összes karaktert kiadja (egyenként) a megismételt olvasási funkcióhívások hatására (és akkor áll le újabb várakozásra, amikor a puffere kiürült).
Igen érdekes a funkció felhasználása egy magnetofonról beolvasható file letapogatására:

100 ALLOCATE 100
110 OPEN £1:""
120 CODE M=HEX$("3E,01,F7,05,26,00,68,C9")
130 PRINT USR(M,0)
140 GOTO 130

A program elindítása után állítsuk a kazettát egy tetszőleges rögzített file-ra és indítsuk el a lejátszást.
Befejezésül lássunk még egy példát a funkcióhívás felhasználására grafikus VIDEO-csatorna esetén. A program beolvassa - és a képernyő alsó sávjában kiírja - a pillanatnyi kurzorkoordináták palettaszínét.

1 PROGRAM "Paletta.bas"
100 ALLOCATE 10
110 CODE M=HEX$("3E,65,F7,05,26,00,68,C9")
120 LET X,Y=100
130 GRAPHICS
140 PRINT AT 2,1:"Palettaszin:"
150 PLOT 200,200,ELLIPSE 100,100,
160 PLOT 350,200,ELLIPSE 100,100,
170 SET INK 1
180 PLOT 200,200,PAINT
190 SET INK 2
200 PLOT 275,200,PAINT
210 SET INK 3
220 PLOT 350,200,PAINT
230 PLOT X,Y,
240 LET P=USR(M,0)
250 PRINT AT 2,15:P
260 SET INK 3+3*(P=3)
270 PLOT X,Y
280 SET INK P
290 PLOT X,Y
300 GET A$
310 IF A$="" THEN 230
320 IF ORD(A$)=176 THEN LET Y=Y+4
330 IF ORD(A$)=180 THEN LET Y=Y-4
340 IF ORD(A$)=184 THEN LET X=X-4
350 IF ORD(A$)=188 THEN LET X=X+4
360 GOTO 230

A villogó pontkurzor a beépített botkormánnyal vezérelhető pontonként a képernyő adott helyére. A rajzolás és a kurzorbeállítás egyelőre a BASIC program feladata, ezek gépi kódú megoldására a 8. Függelék ad információt.

EXOS 6: blokk olvasása
Bemenő adatok: A: csatornaszám
DE: puffercím
BC: az olvasandó blokk hossza
Kimenő adatok: DE: a puffer következő szabad helyére mutat
BC: a még olvasandó karakterek száma (ha hiba történt olvasás közben)

A DE-t egy szabad RAM-terület kezdőcímére kell állítani (itt is tetszőleges LAP-on). Az EXOS ettől kezdve tárolja az olvasott blokkot. A visszaadott értékek olvasási hiba esetén használhatók fel a továbbolvasáshoz.
A funkció szemléltetésére alakítsuk át az előző pont valamelyik példaprogramját. A kiolvasás szemmel követhető lesz, ha pufferterületként az állapotsorban kijelzett memóriaterületet jelöljük ki (amíg a program fut, ezt tetszőlegesen használhatjuk, csak a leállás - pl. STOP - hatására írja felül az operációs rendszer).
A pufferterületet hívás előtt törölni is kell. Így az assembly rutin:




L1
LD HL,0BEBEH
PUSH HL
LD B,20H
LD (HL),20H
INC HL
DJNZ L1
POP DE
LD BC,0020H
LD A,1
EXOS 6
RET
; puffer kezdőcím
; verembe
; 32 byte hossz
; "SPACE" pufferbe
;
; 32 hely törlése
; DE: puffercím
; BC: 32 (hossz)
; csatornaszám=1
; blokkolvasás

Használjuk ezt a rutint pl. a kazettafile beolvasására:

100 ALLOCATE 100
110 OPEN £1:""
120 CODE M=HEX$("21,BE,BE,E5,06,20,36,20,23, 10,FB,D1,01,20,00,3E,01,F7,06,C9")
130 CALL USR(M,0)
140 PING
150 GOTO 130

A rutinból való visszatérést hangadás jelzi, majd továbbolvassuk a csatornát. Érdemes más csatornák olvasásával is próbálkozni.

EXOS 7: karakter írása
Bemenő adatok: A: csatornaszám
B: az írandó karakter

Az EXOS számunkra legsokoldalúbban használható hívása (a blokkíró EXOS 8-cal együtt). A számítógép szinte minden látható és hallható kijelzési, sőt adatátviteli funkciója elérhető ezen keresztül. A kiírandó karakter (az EXOS főágában előkészített egyéb paraméterekkel együtt) a perifériakezelőhöz kerül. Itt dől el - a kezelő feladatának és a kivitt értéknek megfelelően -, hogy a kiírt bájt (bájtsorozat) a képernyő egy adott részén jelenik-e meg alfanumerikus karakterként, vagy magnetofonkazettára kerül-e, esetleg egy grafikus alakzat kitöltését indítja-e el stb. A funkció értelmezése tehát csak egy-egy adott periféria esetén lehetséges.
A hangkezelő (SOUND) pl. a képzendő hangok paramétereit és vezérlőkaraktereket vár ilyen formában. Van azonban egy olyan karakter (a 7-es) amelynek kiírása egymagában kiváltja egy alapértelmezésű hang megszólalását (PING):

LD A,67H
LD B,7
EXOS 7
RET

BASIC-ből betöltve és elindítva:

100 ALLOCATE 100
110 CODE M=HEX$("3E,67,06,07,F7,07,C9")
120 CALL USR(M,0)

Ezt is felhasználhatjuk gépi kódú programjainkban.

Ezek után nézzünk egy példát a perifériakezelő közvetlen hívására. Természetesen olyan példát, amelyben kézzel fogható ennek előnye. Legyen a feladat pl. 255 db azonos karakter (mondjuk A) kiírása a képernyőre.
A feladat az EXOS 7 hívással könnyen megoldható:


C1
LD B,FFH
PUSH BC
LD A,66H
LD B,51H
EXOS 7
POP BC
DJNZ C1
RET
; számláló
; verembe
; csatornaszám
; karakter (Q)
; kiiratás
; számláló vissza
; ismétlés 255-ször

A BASIC betöltő programot kiegészítettük, hogy a rutin futásidejét mérhetővé tegyük. A 140. sor folyamatosan átírja a kiírandó karakter kódját (így jobban tudjuk követni a képernyőn a folyamatot).

100 ALLOCATE 100
110 CODE M=HEX$("06,FF,C5,3E,66,06,51,F7,07,C1,10,F6,C9")
120 TIME "00:00:00"
130 FOR N=65 TO 90
140   POKE M+6,N
150   PRINT AT 1,1:
160   CALL USR(M,0)
170   PRINT AT 10,1:
180 NEXT
190 PRINT "Ido: "TIME$

Ezek után térjünk rá a VIDEO perifériakezelő kiírató rutinjának (0/D4D0H, l. 5. Függelék) közvetlen hívására.
Az EXOS főág követésével megállapítottuk, hogy a végrehajtó rutinnak átadandó, előkészítendő paraméter a kiíratandó karakteren kívül (B) a szóban forgó csatorna pufferének báziscíme (IX) is. Ugyanitt láttuk azt is, hogy milyen módon keresi ki az EXOS a csatorna leíróját. Ehhez a kereséshez használjuk mi is a 0. szegmens CD3AH rutinját. Ez az alprogram a BFCDH báziscímmel induló csatornaláncból kikeresi a csatornát a belső csatornaszámot tartalmazó A alapján. Belapozza a leíró szegmensét az 1. LAP-ra és HL-ben a leíró báziscímét adja vissza (ill. Z státusszal tér vissza, ha nem találja a keresett csatornát a láncban).
Mivel tudjuk, hogy maga a leíró 16 bájt hosszú, a leíró báziscíméből már könnyen meg tudjuk határozni a közvetlenül alatta kezdődő puffer báziscímét. Ezt IX-be töltve már hívhatjuk a végrehajtó rutint. Megjegyezzük, hogy - az egyszerűség kedvéért - itt most elhagytuk az EXOS által ugyancsak átadott periférialeíró pointer előkészítését, (B', IY), ezt ui. nem igényli az adott végrehajtó rutin.
A következő rutin az előbbi lépések szerint oldja meg a feladatot:














L1





L2
PUSH IX
IN (A),0B3H
PUSH AF
XOR A
OUT (0B3H),A
LD A,67
CALL 0CD3AH
JR Z,L2
LD BC,0FFF0H
ADD HL,BC
PUSH HL
POP IX
LD B,0FFH
PUSH BC
LD B,51H
CALL 0D4D0H

POP BC
DJNZ L1
POP AF
OUT (0B3H),A
POP IX
RET
; BASIC "IX" mentés
; pillanatnyi 3. LAP
; verembe
; A=0
; 3. LAP: 0. ROM
; !belső csatornaszám!
; csatornát keres
; végére, ha nincs
; -16
; leíró báziscím-16
;
; IX=puffer báziscím
; számláló
; verembe
; karakter (Q)
; VIDEO kiírás
; végrehajtó rurin
; számláló vissza
; ismétlés 255-ször
; eredeti 3. LAP
; visszalapozása
; BASIC "IX" vissza

Ill. ennek BASIC betöltője és hívója:

1 PROGRAM "Kiras.bas"
100 ALLOCATE 100
110 CODE M=HEX$("DD,E5,DB,B3,F5,AF,D3,B3,3E,67,CD, 3A,CD,28,12,01,F0,FF,09,E5,DD, E1,06,FF,C5,06,51,CD,D0,D4,C1, 10,F7,F1,D3,B3,DD,E1,C9")
115 TEXT
120 TIME "00:00:00"
130 FOR N=65 TO 90
140   POKE M+26,N
150   PRINT AT 1,1:
160   CALL USR(M,0)
180 NEXT
190 PRINT "Ido: ";TIME$

A BASIC program a gépi kódokat tartalmazó (110) és a kiírandó karakter kódját váltogató (140) sortól eltekintve megegyezik a feladat előző megoldásának programjával. Így összemérhető a két futásidő. Az EXOS funkcióhívással dolgozó rutin futásideje kb. 2-3-szor akkora, mint a közvetlen hívó második megoldásé. Nyilvánvaló tehát, hogy - legalábbis egyes feladatoknál - érdemes a formailag rendkívül egyszerű EXOS hívás helyett egy kicsit többet fáradni a közvetlen hívással.

EXOS 8: blokk írása
Bemenő adatok: A: csatornaszám
DE: a kiírandó blokk (szöveg) kezdőcíme
BC: a kiírandó bájtok száma
Kimenő adatok: BC: a még ki nem írt karakterek száma (ha hiba történik kiírás közben)
DE: a még hátralevő szöveg címe

Nagyobb tömegű adat, összetartozó karaktercsoport stb. ideális kiviteli eszköze.
Egyszerű alkalmazási példaként írassuk az 1. szegmens egyik copyright-üzenetét az aktuális csatornára:

100 ALLOCATE 100
110 CODE M=HEX$("11,80,F5,01,21,00,3E,FF,F7,08,C9")
120 CALL USR(M,0)

EXOS 9: csatornakészenlét olvasása
Bemenő adat: A: csatornaszám
Kimenő adat: C: készenlétjelző bájt

Olyan csatorna esetén praktikus az alkalmazása, amely olvasásnál várakozna a készenlétig (pl. KEYBOARD). Így várakozás nélkül is információt szerezhetünk arról, van-e olvasható karakter a perifériakezelő pufferében. A készenléti státuszt C hordozza:

A BASIC rendszerben a GET utasítás használja pl. ezt a hívást, mielőtt megkísérli az olvasást (nehogy bennragadjon a kezelőben).
A készenléti jelzőt írja ki a következő program az 1. sorba a billentyűzet csatornájáról a

LD A,69H
EXOS 9
LD H,0
LD L,C
RET

gépi rutin felhasználásával. A kész karaktert folyamatosan ki is olvassuk, mert különben egyetlen billentyű leütése után csak az állandó készenlétet látnánk.

100 ALLOCATE 10
110 CODE M=HEX$("3E,69,F7,09,26,00,69,C9")
120 LET KOD=USR(M,0)
130 PRINT AT 1,1:KOD;
140 IF KOD=0 THEN
150   GET A$
160   PRINT A$;
170 END IF
180 GOTO 120

EXOS 10: csatornaállapot megadása és olvasása

Az alapkiépítésű gépen nem használt funkció: egyetlen beépített perifériakezelő sem fogadja (NOFN hibakódot küldenek vissza). Saját perifériakezelőnkben tetszés szerint használhatjuk (a funkció pl. lemezkezelő bővítés esetén nyer értelmet).

EXOS 11: speciális funkció
Bemenő adatok: A: csatornaszám
B: a speciális funkció kódja
C, DE: átadható paraméterek a funkció igénye szerint
Kimenő adatok: BC, DE: tartalmazza a funkció által esetleg visszaadott értékeket

A perifériák vezérlőfunkciója. A beépített perifériák egy része (SERIAL, TAPE, PRINTER) nem kezeli ezt a hívást (ISPEC - Érvénytelen speciális funkcióhívás - hibakódot küld válaszként). A többi kezelő elfogadja a hívást a B értékének egy-egy tartományában:

1-4:
VIDEO
8-9:
KEYBOARD
16-17:
NET
24-26:
EDITOR

A várhatóan leggyakrabban hívott képernyő-, ill. billentyűzetkezelés esetén nézzük meg egy kicsit részletesebben az elérhető funkciókat és hívásuk módját:

B = 1: videolap kijelzése

BASIC-ben
DISPLAY £csat: FROM kezdősor TO végsor AT képernyősor
utasítással elvégezhető funkciót látja el: a képernyő adott sorától kezdődően kijelzi a szóban forgó csatornán felépített videolap kezdő- és végsor közötti részét.
Paraméterei:

A: kijelzendő csatornaszám
C: kezdősor
D: kijelzendő sorok száma (végsor - kezdősor + 1)
E: képernyősor

Lássunk egy rövid rutint a funkció alkalmazására:

  LD A,65H
LD C,L
LD B,1
LD DE,0A05H

EXOS 11
RET
; grafikus csatorna
; kezdősor BASIC-ből
; funkciókód: DISP
; D: 10 kijelzendő sor
; E: az 5. sortól
; funkcióhívás

Egy kis BASIC kiegészítéssel jól látható a funkció hatása. A kijelzendő laprészlet (10 sor) kezdő sorszámát BASIC-ből adjuk meg (a USR második argumentuma L-be, onnan a rutinban C-be kerül).

10 PROGRAM "Disp.bas"
100 ALLOCATE 100
110 CODE DISP=HEX$("3E,65,4D,06,01,11,05,0A,F7,0B,C9")
120 DISPLAY GRAPHICS
130 PLOT 600,350,ELLIPSE 400,300,
140 PLOT 0,700,
150 PRINT £101:"Felso resz:"
160 PLOT 0,100,
170 PRINT £101:"Also resz:"
180 DISPLAY TEXT
190 CALL USR(DISP,1)
200 WAIT 1
210 CALL USR(DISP,11)
220 WAIT 1
230 GOTO 190

A következő két speciális funkcióhívás az adott csatornához tartozó videolap paramétereit olvassa ki és adja vissza a hívónak.

B = 2: üzemmód lekérdezése

Kimenő adatai:
B: a videolap szélessége karakterekben (X)
C: a videolap magassága karakterekben (Y)
D: a kijelzési mód
E: a színmód

A négy regiszterben visszaadott érték tulajdonképpen az
X-SIZ-VID
Y-SIZ-VID
MODE-VID és
COLOR-VID
EXOS változók értéke a szóban forgó csatorna megnyitásakor.

B = 3: tárolási cím lekérdezése

Kimenő adatai:
BC: fő tárolási cím
DE: másodlagos tárolási cím

A video adatok tárolásáról (és közvetlen eléréséről) a 8. Függelékben írunk még. Példaként most alkalmazzuk ez utóbbi két speciális funkcióhívást TEXT 80 üzemmódban az alapértelmezésű VIDEO-csatorna lekérdezésére (66 H). A visszakapott paramétereket tároljuk a BASIC által nem használt 0180H-0187H területeken (decimálisan 384-391)

  LD A,66H
LD B,2
EXOS 11
LD (0180H),BC

LD (0182H),DE

LD A,66H
LD B,3
EXOS 11
LD (0184H),BC
LD (0186H),DE
RET
; VIDEO-csatorna
; "mód és méret" kód
; lekérdezés
; Y -> 0180H
; X -> 0181H
; COLR -> 0182H
; MODE -> 0183H
; VIDEO-csatorna
; "tár.cím" kód
; lekérdezés
; fő RAM címe -> 0184/5H
; 2. RAM címe -> 0186/7H

A BASIC betöltőprogramban egyúttal kiolvassuk és ki is íratjuk a funkcióhívás tárolt eredményeit.

100 ALLOCATE 100
110 CODE M=HEX$("3E,66,06,02,F7,0B,ED,43,80,01,ED,53,82,01")
120 CODE =HEX$("3E,66,06,03,F7,0B,ED,43,84,01,ED,53,86,01,C9")
130 TEXT 80
140 CALL USR(M,0)
150 PRINT "A"
200 FOR N=384 TO 391
210   PRINT N;PEEK(N)
220 NEXT

Értelmezzük együtt a futtatással kapott adatokat:

Itt is felhívjuk a figyelmet, hogy a két tárolási cím VIDEO cím. Az adatokat tároló szegmens száma a cím MSB tartományából határozható meg.
1. 80H < 9AH < BFH a fő cím az FEH szegmensben van
2. C0H < DEH < FFH a másodlagos cím az FFH szegmensben van.
Ezt a két kezdőcímet kérdezi le a fenti képernyőmásolat két sora. A fő címen lévő érték 24 (00011000) a képernyő bal felső sarkába kiíratott A karakter pontmátrixának felső sora. A másodlagos cím tartalma (65) ennek az A betűnek az ASCII kódja.

B = 4: karakterkészlet-inicializálás

A funkciónak külön be- és kimenő adata nincs. Még a megadott csatornaszám is csak annyiban kritikus, hogy VIDEO perifériához megnyitott csatorna legyen (ez biztosítja, hogy a hívás a VIDEO kezelőhöz jusson el).
A ROM-adatmezőből - hidegindítási folyamathoz hasonlóan - újratölti a RAM karaktergenerátor területet. Hatása - a hívott csatornától függetlenül - valamennyi videocsatornára kiterjed.

A következő két speciális funkcióhívás a billentyűzetkezelőé:

B = 8: funkcióbillentyűk programozása

Bemenő adatok:
A: a billentyűzetcsatorna száma (BASIC alapértelmezésben 69H)
C: a programozni kívánt funkcióbillentyű száma - 1 (0-7, ill. shiftelt gombokhoz 8-15)
DE: a beprogramozandó szöveg előtti hosszbájtra mutat

Példaként írjuk át a F8 funkciógombot (C = 7) úgy, hogy megnyomására AUTO üzemmódba kerüljön a gép, és a 100-as sorba írja is be a programjainkban igen gyakran használt ALLOCATE utasítást, de argumentum nélkül:

100 ALLOCATE 100
110 CODE SZOVEG=HEX$("10,A5")&"AUTO"&HEX$("A1,0D")&"ALLOCATE "
120 CODE FK=HEX$("3E,69,01,07,08,11")&WORD$(SZOVEG)&HEX$("F7,0B,C9")
130 CALL USR(FK,0)

A beprogramozott szövegben a hosszbájt 10H (16 karakter következik), az első kiírt bájt (A5H) visszatöröl a sor elejéig. Ezután következik az AUTO szöveg, törlés a sor végéig, majd a sor bevitele (0DH = ENTER). Az ezt követő ALLOCATE karakterekkel befejezzük a beprogramozott szöveget, nem zárjuk le a sort, így a kurzor a szöveg utáni pozíción marad; a programozó beírhatja a lefoglalni kívánt bájtok számát.

B = 9: a botkormányok olvasása

Bemenő adatok:
C: botkormány-választás
C = 0: belső
C = 1: külső 1.
C = 2: külső 2.

Kimenő adat:
C = olvasott érték

A botkormány és a tűzgomb (a beépített joystick esetén SPACE) pozícióját a C regiszter bitjei reprezentálják (1, ha megnyomott):

JOBB:
BAL:
LE:
FÖL:
TŰZ:
BIT 0,C
BIT 1,C
BIT 2,C
BIT 3,C
BIT 4,C

Végül a további (várhatóan ritkábban igényelt) speciális funkcióhívások (részleteket az EXOS leírás tartalmaz):

NET

EDITOR

EXOS 16: EXOS változó olvasása, írása, átbillentése
Belépési pont: C8F9H
Bemenő adatok:
  • B: az elvégzendő művelet kódja
    • B = 0: olvasás
    • B = 1: írás
    • B = 2: átbillentés
  • C: az EXOS változó száma
  • D: beírandó érték
Kimenő adat: D: a változó értéke

Ez az egyetlen EXOS hívás, amit még megszakításkezelés közben is elfogad a rendszer. Tulajdonképpen rögzített címen kezdődő változó táblázat adatainak kezelését végzi.
Ha a változó száma 39-nél nagyobb, E-ben a változó számával, C-ben az "EXOS változó" kóddal (4) végigkérdezi a rendszerbővítőket, lehetőséget adva így új változók bevezetésére, kezelésére.

C8F9
C8FC
C8FD
C8FE
C900
21 69 C4
E5
79
D6 28
30 2C
LD HL,C469
PUSH HL
LD A,C
SUB 28
JR NC,C92E
; visszatérési cím
; verembe
; változó száma
; -40
; bővítőkhöz, ha n>39

A 0 és 39 közötti (alapértelmezésű) változó hívása esetén megállapítja a hozzá tartozó táblázatcímet: HL = BFC5H + n. Az 1. változót külön kezeli (FLAG_SOFT_IRQ), ezt a BFF2H címen keresi és manipulálja. Az EXOS változók táblájának azonos nevű eleme voltaképpen csak ennek másolata lesz.

C902
C903
C906
C907
C909
C90B
C90D
F5
21 F2 BF
79
FE 01
28 03
C6 C5
6F
PUSH AF
LD HL,BFF2
LD A,C
CP 01
JR Z,C90E
ADD A,5
LD L,A
;
; FLAG_SOFT_IRQ
; változószám
; =1?
; cím marad, ha igen
; egyébként
; HL=BFC5+n (tábla)

A kiszámított címen elvégzi a kért műveletet:

C90E
C90F
C912
C914
C915
C916
C917
C918
05
FA 18 C9
28 03
7E
2F
57
72
56
DEC B
JP M,C918
JR Z,C917
LD A,(HL)
CPL
LD D,A
LD (HL),D
LD D,(HL)
; funkciókód?
; ha B=0 volt (olv.)
; ha B=1 volt (írás)
; változó értéke
; átbillentés
;
; új érték változóba
; (vissza)-olvasás

REM 1 és REM 2 - hívó esetén (36., ill. 37. változó) a megfelelő PORT-okon is beállítja az aktuális értéknek megfelelő szinteket, majd - a RET hatására - elugrik a belépéskor PUSH utasítással tárolt címre: az EXOS funkcióhívások visszatérési ágára:

C919
C91A
C91C
C91E
C920

C923
C924
F1
D6 FC
28 02
FE 01
CC 25 C9

AF
C9
POP AF
SUB FC
JR Z,C920
CP 01
CALL Z,C925

XOR A
RET
; kód-40
; kód-36
; ha kód=36
; kód=37?
; REM1, REM2 esetém
; PORT-okat állít
; A= 0 státusz
; (vissza az EXOS
; befejezésére)

A funkció közvetlenül is hívható (természetesen a visszatérési címet verembe töltő bevezető utasítások átlépésével, a C8FDH címen), de igazi jelentősége inkább a változók közvetlen elérésének van. Az ehhez szükséges címeket (és az inicializálás utáni értékeket) a 3. Függelék tartalmazza. Az alkalmazásra már láttunk példát az EXOS 1, 2 tárgyalásakor.

EXOS 17: csatorna átirányítása olvasáshoz
Belépési pont: C682H
EXOS 18: csatorna átirányítása íráshoz
Belépési pont: C681H
  A két hívás együtt tárgyalható, a rendszer is együtt kezeli ezeket. Mindkét hívás feladata a főcsatorna átállítása olyan módon, hogy ezután a rávonatkozó hívások a másodlagos csatornára irányítódjanak.
Bemenő adatok: A: a főcsatorna száma
B: a másodlagos csatorna száma (a C = FFH érték törli az átirányítást)

A két funkció érdekessége, hogy - bár csatornákat érintő utasításokról van szó - a rendszer nem a csatornautasítások között (1-11) kezeli őket. Ennek oka, hogy itt a perifériakezelőket nem kell bevonni a végrehajtásba. A rendszer a hívások hatására csak a főcsatornák leírójának egy-egy bájtját állítja át:

C681

C682
C683
C684
C685
C686

C689
C68B

C68D
C68F
C690

C692
C693
C694
C695
37

59
F5
08
3C
C4 3A CD

3E FB
28 09

0E 0F
F1
ED 42

1C
73
AF
C9
SCF

LD E,C
PUSH AF
EX AF,AF'
INC A
CALL NZ,CD3A

LD A,FB
JR Z,C696

LD C,0F
POP AF
SBC HL,BC

INC E
LD (HL),E
XOR A
RET
; CY=1, ha EXOS 18
; CY=0, ha EXOS 17
; E: másodlagos csat.
; státusz verembe
; A: fő csatornaszám
; belső csatornaszám
; keresi a láncban
; HL=leíró báziscím
; ".ICHAN" hibakód
; hiba, ha A=FFH
; vagy nincs megnyitva
; C=15
; státusz vissza
; HL=cím-15, ha EXOS 17
; HL=cím-16, ha EXOS 18
; 2. csat. belső szám
; a leíróba
; A=0 státusz

Az átirányítás megvalósítása tulajdonképpen - mint láttuk - a csatornahívásokat előkészítő EXOS főágban történik. Így ezt a funkciót a szóban forgó csatornaleíró közvetlen manipulálásával is kiválthatjuk.
A két funkcióhívás a BASIC

"CAPTURE FROM főcsatorna TO mellékcsatorna", ill.
"REDIRECT FROM főcsatorna TO mellékcsatorna"

gépi megvalósítója. Segítségükkel az igen praktikus átirányítás (pl. a képernyőkezelésre tervezett program átállítása egyetlen utasítással nyomtatókezelőre) gépi programjainkban is egyszerűen elérhető, pl. az említett esetre:

  ...
LD A,66H
LD C,68H
EXOS 18
...

; VIDEO-csatornáról
; PRINTER-csatornára
; átirányítás

 

EXOS 19: az alapértelmezésű periférianév beállítása
Belépési pont: C7E5H
Bemenő adatok:
  • C: a periféria típuskódja
    • C = 0: nem file-kezelő (magnetofon)
    • C = 1: file-kezelő (lemezegység)
  • DE: a periférianév előtti hosszbájtra mutat (a hívó memóriakioszlásában bármelyik LAP-on)

Mint a csatornakezelő EXOS hívások előkészítésénél láttuk, ha a csatornanyitáskor megadott periférianév 0 hosszúságú vagy nem tartalmaz kettőspontot, a rendszer az alapértelmezésű periféria nevével helyettesíti azt (TAPE). Ezt a nevet írhatjuk át az EXOS 19 hívással. A DE-vel megcímzett új név bármelyik könyvtárazott periféria neve lehet (a szövegvégi kettőspont itt nem kell). A hívás a nagybetűsített és a karakter tartományában ellenőrzött szöveget rögzített című pufferbe tölti:
BF18H: hossz
BF19-: karakterek
Ennek ismeretében akár közvetlenül is lekérdezhető vagy átírható az alapértelmezésű periférianév.
Használata praktikus lehet pl. abban az esetben is, ha igen gyakran nyitunk meg csatornát egy adott perifériához. A következő program VIDEO-ra írja át az alapértelmezésű nevet.

100 ALLOCATE 100
110 CODE NEV=CHR$(5)&"VIDEO"
120 CODE M=HEX$"(0E,00,11")&WORD$(NEV)&HEX$("F7,13,C9")
130 CALL USR(M,0)

A program futtatása után

OPEN £n:""

alakban nyithatunk meg videocsatornákat.

EXOS 20: rendszerállapot lekérdezése
Belépési pont: C808H
Bemenő adat: DE: a puffer címe, ahová az EXOS töltheti az információt hordozó bájtokat
Kimenő adatok: B: az EXOS verzió száma (BCD alak; esetünkben 21H)
pufferben az állapotadatok

Az egyszerű rutin a rendszerváltozó-terület 7 bájtját átmásolja a hívó pufferébe:

C808
C809
C80C
C80F
C811

C814
C816
C817
D5
CD 3A D2
21 9E BF
0E 07
CD 00 C8

06 21
D1
C9
PUSH DE
CALL D23A
LD HL,BF9E
LD C,07
CALL C800

LD B,21
POP DE
RET
; puffercím verembe
; pufferszg. 1 LAP-ra
; változóterület címe
; bájtok száma
; 7 bájtot másol
; a pufferbe
; verziószám
; puffercím vissza

Az átadott adatokkal már találkoztunk a hidegindítás folyamatának követésénél. Nézzük most meg az EXOS hívás segítségével a mi verziónk pillanatnyi paramétereit. Írjuk a programunkat egy 0-tól különböző programszámon (pl. EDIT 1), hogy ennek a hatását is láthassuk. Pufferként ismét a 0180H kezdőcímet adjuk meg.

100 ALLOCATE 100
110 CODE M=HEX$("11,80,01,F7,14,26,00,68,C9")
120 LET B=USR(M,0)
130 PRINT "A verzioszam";B
135 PRINT "A rendszerallapot:"
140 FOR N=0 TO 6
150   PRINT N;PEEK(384+N)
160 NEXT

A futás eredménye:

A verziószámban természetesen csak hexadecimális alakban (21H) ismerhetjük fel a 2.1-es verziószámot. A többi adat rendre:

EXOS 21: új periféria felvétele
Belépési pont: C698H
Bemenő adatok: DE: a bevezetni kívánt periféria leírójára mutat (TYPE)
BC: az igényelt RAM nagysága

Ha a felhasználó egy saját perifériát szeretne rendszerbe illeszteni, az eszköz és a kezelő paramétereit kötött formájú leíróban kell összefoglalni. Erről az inicializálás során volt már szó, és egy konkrét példa kapcsán foglalkozunk is még vele a 2.5 alfejezetben.
A hidegindítás során a ROM-szegmensek beépített perifériáinak leíróit a rendszerszegmensbe másolja át az EXOS. Az EXOS 21 funkcióhívással utólag beillesztendő leíró viszont megmarad saját szegmensében, csak a címmutatók állításával illeszti be a rendszer a leírók láncába. Ezért az ilyen perifériakezelőnek feltétlenül RAM-ban kell lennie. Eltér az inicializálás beláncolási műveletétől az a momentum is, hogy itt a perifériához igényelt RAM méretét nem a leíró (ill. álleíró) tartalmazza, hanem a beláncolás kérésekor kell megadni BC-ben.
Tekintsük át, milyen lépéseket követ az EXOS egy felhasználói periféria felvételekor:

Befejezésül az igényelt - és kiutalt - pufferterület címével IX-ben meghívja a belépési címtáblázat 12. rutinját (INICIALIZÁLÁS). Ekkor van lehetősége a perifériakezelőnek a maga számára mindig elérhető helyen letárolni a kiutalt RAM-terület címét.
A perifériakezelő felépítésére és beláncolására a 2.5 alfejezetben kidolgozunk egy példát.

EXOS 22: az EXOS határ kiolvasása
Belépési pont: C6F2H
Kimenő adatok: DE: az EXOS határ (0000H és 3FFFH közé transzformált érték)
C: a megosztott szegmens száma (0, ha nincs megosztott szegmens)

A rutin egyszerűen a - már ismert - rendszerváltozó értékeket olvassa ki és adja vissza:

C6F2
C6F5
C6F6
C6FA
C6FB
3A 9E BF
4F
ED 5B 91 BF
AF
C9
LD A,(BF9E)
LD C,A
LD DE,(BF91)
XOR A
RET
; a megosztott szg. száma
; C-be
; DE: EXOS határ
; A=0 státusz

A címek ismeretében az értékek közvetlenül is elérhetők.

EXOS 23: a felhasználói határ beállítása
Belépési pont: C6FCH
Bemenő adat: DE: a beállítani kívánt határ (0000 és 3FFFH közé transzformálva)

Ha van megosztott szegmens és a megadott felhasználói határ címe nem nagyobb az EXOS határnál, a rendszer bejegyzi DE értékét a rendszerváltozóba:

C6FC
C6FF
C700
C702

C703
C706
C708
C70A
C70B
C70C

C72A
C72E
3A 9E BF
B7
3E F4
C8

2A 91 BF
ED 52
3E F3
D8
AF
18 1C

ED 53 8F BF
C9
LD A,(BF9E)
OR A
LD A,F4
RET Z

LD HL,(BF91)
SBC HL,DE
LD A,F3
RET C
XOR A
JR C72A

LD (BF8F),DE
RET
; a megosztott szg. száma
; van megosztott szg.?
; ".ISEG" hibakód
; hiba, ha nincs

; HL=EXOS határ
; - felhasználói határ
; ".IBOUND" hibakód
; hiba, ha DE>HL
; A=0 egyébként
;

; felhasználói határt
; letárol

Ez utóbbi két hívás az EXOS memóriamenedzselésének fontos eleme. Ha a felhasználó (alulról felfelé tolódó) határa és a rendszer által (felülről lefelé) igénybevett terület határa - az EXOS határ - egy szegmensbe kerül, ez a két érték (pontosabban a köztük lévő különbség) korlátozza az elvégzendő műveleteket. Ezek kezelését, ellenőrzését minden hordozható, változó környezetben is alkalmazni kívánt felhasználói programnak el kell végeznie.
A BASIC értelmezőnek - nem tudván, hogy milyen memóriakiosztásba kerül - gyakorlatilag minden méretnövelő művelet (sorbeírás, változó tárolása stb.) előtt meg kell győződnie arról, hogy nem ütközik határokba. Fel kell készülnie arra is, hogy a megosztott szegmensben dolgozik, ahol lépésről-lépésre változik a két lefoglalt terület határa. Belátható, hogy ez a kötelezettség egy erős sebességkorlátozó tényező. Ha saját gépünk ismert kiosztású memóriáiban dolgozunk (esetleg egy lefoglalt szegmensben), sok időt - és energiát - takaríthatunk meg, elkerülve a folyamatos ellenőrzések kényszerét.

EXOS 24: szegmensigénylés
Belépési pont: C70EH
Bemenő adat: (közvetlen felhasználásnál) AF'-ben a hívási státusz, ez informálja a rendszert, hogy a szegmensigénylő egy felhasználói program (Z) vagy egy perifériakezelő (NZ).
Kimenő adatok: C: a kiutalt szegmens száma
DE: a szegmensben szabadon használható bájtok száma
A (státusz): 7FH, ha megosztott szegmenst utalt ki az EXOS

A szegmensigénylés, lefoglalás a memóriagazdálkodás alapfunkciója. Számunkra is fontos lehet a kiutalás módja, ennek nyilvántartása. Kövessük hát a funkció végrehajtását. A szegmensgazdálkodás alapja az a RAM-táblázat, amit az inicializálás során épített föl a rendszer, az ABDOH címtől lefelé bejegyezve a hibátlannak talált RAM-szegmensek számát (l. a 2.3.1 pontot). Ebben a táblázatban tehát (mint láttuk is) alaphelyzetben, 128 K-s gép estén az ABCAH és ABD0H címek között növekvő sorrendben találjuk az F9H-FFH szegmensszámokat (a kötött nulláslap-szegmenssel (F8H) nem számol a rendszer).
A nyilvántartás másik része a rendszerváltozó területen folyik:

Ezután lássuk, hogyan él a rendszer ezekkel az eszközökkel? Mindenekelőtt ellenőrzi, hogy van-e még szabad szegmens, amit kiutalhat a hívónak:

C70E
C711
C712
C713
21 9F BF
7E
B7
20 1A
LD HL,BF9F
LD A,(HL)
OR A
JR NZ,C72F
;
; A: szabad szg.-ek száma
; =0?
; ugrik, ha van még szabad
; szegmens

Ha nincs szabad szegmens, megkísérli a rendszer által már használatba vett memóriát (EXOS határ szegmense) kiutalni. Ezt viszont perifériának nem utalja ki, csak felhasználói programnak. Másrészt - természetesen - ezt csak akkor teheti meg, ha nem volt a kérdéses szegmens már előzőleg is megosztott.

C715
C716

C718
C71B
C71C

C71F
C720

C722
C723
C724
C726
C72A
C72E
08
20 3A

2A 9A BF
7E
21 9E BF

BE
28 30

77
4F
3E 7F
ED 5B 91 BF
ED 53 8F BF
C9
EX AF,AF'
JR NZ,C752

LD HL,(BF9A)
LD A,(HL)
LD HL,BF9E

CP (HL)
JR Z,C752

LD (HL),A
LD C,A
LD A,7F
LD DE,(BF91)
LD (BF8F),DE
RET
; EXOS hívási státusz
; hiba, ha periféria hív
; (".NOSEG")
; RAM-tábla: EXOS határ
; határt tartalmazó szg.
; HL: megosztott szg.
; számára mutat
; megegyeznek?
; ".NOSEG" hiba, ha már
; meg lett osztva
; megosztott szg. száma
; C: kiutalt szg. száma
; A: ":SHARE" kód
; DE: EXOS határ
; a rendszerváltozót is
; beállítja

Miután láthatjuk, megosztott szegmens kiutalása esetén DE az EXOS határ alatt szabadon használható bájtok számát tartalmazza és A = 7EH.
Ha még van szabad szegmens, a rendszer azt utalja ki a hívónak. Ekkor nem tesz különbséget a felhasználó és a perifériakezelő - mint igénylő - között, csak annyiban, hogy hová könyveli be a kiutalt szegmenst.

C72F
C730
C731
C732
C733
C734
C735
C736
C738
C739

C73A
47
35
23
23
08
7E
2B
28 02
AF
23

34
LD B,A
DEC (HL)
INC HL
INC HL
EX AF,AF'
LD A,(HL)
DEC HL
JR Z,C73A
XOR A
INC HL

INC (HL)
; B: szabad szg.-ek száma
; (szabad szg.-ek száma)-1
; BFA0
; BFA1
; EXOS hívási státusz
; periféria-szegmensek sz.
; BFA0
; ugrik, ha nem periféria
; átlépendő szegmensek sz.
;

; (hívónak kiutalt szegmensek
; száma)+1

Ha felhasználó kérte a memóriát és a perifériáknak már van kiutalva szegmens, az EXOS átrendezi a RAM-táblát: a most kiutalt felhasználószegmenst a perifériaszegmensek elé teszi:

C73B
C73E
C73F
C741
C742
C743
C744
C746
C747
C748
C749

C74B
C74C
C74D
C750
C751
2A 9A BF
2B
10 FD
4F
B7
7E
28 06
54
5D
2B
ED B8

12
4F
11 00 40
AF
C9
LD HL,(BF9A)
DEC HL
DJNZ C73E
LD C,A
OR A
LD A,(HL)
JR Z,C74C
LD D,H
LD E,L
DEC HL
LDDR

LD (DE),A
LD C,A
LD DE,4000
XOR A
RET
; RAM-tábla: EXOS határ
; előtte szabad szg.-ek
;
; BC: átlépendő szg-ek sz.
; =0?
; a legalsó szabad szegmens
; (nem kell átrendezni)
;
; DE: kiutalt szg. címe
; HL: felső perifériaszg.
; feltolta a perifériának
; kiutalt szegmenseket
; eléjük most a kiutalt
; C=kiutalt szg. száma
; DE=16 kbyte használható
; A=0 státusz

A RAM-szegmensek táblázatának a szerkezete tehát a következő:

Ezt a szerkezetet jól példázza az ASMON assembler és monitorprogram beolvasása és elindítása után kialakult helyzet.

ABC8
ABD0

BF98
BFA0
00 00 FB FC F9 FA FD FE
FF DD B1 00 00 00 00 00

AB F9 CF AB C9 AB 00 01
02 02 02 08 00 71 5C 66

A rendszerváltozó-terület és a RAM-tábla DUMP-ja alapján megállapíthatjuk, hogy a rendszer 2 szegmenst vett igénybe: FFH-t és FEH-t.
Az EXOS határ az FEH szegmensben van. Az egyetlen szabad szegmens az ez alatti FDH. A többit már kiutalta az EXOS:
-kettőt a perifériáknak (pontosabban a program beolvasásakor és elhelyezésekor a TAPE-nek), az F9H és FAH szegmenseket;
-ugyancsak kettőt a felhasználónak (ami most az ASMON program); az FBH és FCH szegmenseket.
Alapállapotú gépnél a táblázat szabad RAM-jai nagyság szerinti sorban állnak és a rendszer is sorban utalja ki azokat az egymás utáni kérésekre. Ezt illusztrálja a következő program, amelynek gépi kódú rutinja meghívja az EXOS 24 funkciót és annak kimenő adatait pufferbe tölti:

EXOS 24
LD HL,0180H
LD (HL),A
INC HL
LD (HL),C
INC HL
LD (HL),E
INC HL
LD HL,D
RET
; szegmensigénylés
; puffercím
; hibakód pufferbe
;
; szegmensszám
;
; méret LO
;
; méret HI

Maga a program ciklikusan újabb és újabb szegmenseket kér, majd kiírja az EXOS-tól kapott paraméterek értékét:

1 PROGRAM "Szegmens.bas"
100 ALLOCATE 100
110 CODE M=HEX$("F7,18,21,80,01,77,23,71,23,73,23,72,C9")
120 LET S=1
130 PRINT S;". hivas:"
140 CALL USR(M,0)
150 LET KOD=PEEK(384)
160 IF KOD>0 AND KOD<>127 THEN 230
170 PRINT "A kapott szegmens szama:";PEEK(385)
180 IF KOD=127 THEN PRINT "Megosztott!"
190 PRINT "A felhasznalhato bajtok szama:";PEEK(386)+256*PEEK(387)
200 PRINT
210 LET S=S+1
220 GOTO 130
230 PRINT "Elfogyott a memoria!"
240 END

A futtatás eredménye (a vizsgált kiépítésű gépnél):

EXOS 25: szegmens felszabadítása
Belépési pont: C78EH
Bemenő adatok: C: a felszabadítandó szegmens száma
AF: az EXOS hívási státusz (l. pl. EXOS 24)

Ezzel a hívással az EXOS 24 segítségével lefoglalt konkrét szegmenst lehet felszabadítani a rendszer vagy más felhasználó számára. Hogyan oldja ezt meg az EXOS? Nyilván az előbbi hívásnál megismert rendszerváltozók és RAM-tábla alapján.
Itt még egy mutatót használ a rendszer:
BF9C/DH tartalma a RAM-szegmensek táblázata alatti cím.

A rendszer mindenekelőtt ellenőrzi, hogy a megosztott szegmens felszabadítását kéri-e a hívó. Ha igen, a művelet egyszerű: 0-t ír a "megosztott szegmens száma" változóba (BF9EH) és a felhasználói határt is 0-ra állítja (BF8F = 90H). Más (teljes) szegmens esetén több lépést kell megtenni:

A funkcióhívás felhasználása (a természetes eseten kívül, amikor visszaadunk a rendszernek egy már nem használt memóriablokkot) akkor is praktikus lehet, ha nem tetszőleges szegmens lefoglalására van szükségünk, nem arra, amit az EXOS kiutal számunkra az első híváskor.
Gyakran szükséges pl. egy VIDEO-szegmens, lefoglalása (FCH-FFH), egy olyan szegmensé, amelyben a rendszertől háborítatlanul dolgozhatunk és munkánk eredményét megjeleníthetjük a képernyőn. A rendszer viszont először a legalacsonyabb számú szegmenseket utalja ki kérésünkre (EXOS 24). Természetesen lehet ismételni a szegmensigénylést, mindaddig amíg videoszegmenst nem kapunk, de ez nagy memóriaterület fölösleges lefoglalását jelenti. Ekkor alkalmazhatjuk a tárgyalt funkcióhívást a fölösleges szegmensek felszabadítására.
A következő gépi kódú rutin ezt a feladatot látja el. Addig ismétli a szegmensigénylést, amíg a kiutalt memóriablokk száma 251-nél nagyobb nem lesz. Eközben a kapott szegmensszámokat letárolja egy ideiglenes pufferbe (0180H-tól). Ha VIDEO-szegmenst kapott, rátér a rutin második részére (L2 címke), amelyben a pufferből visszaolvasott számú szegmenseket sorra felszabadítja:


L1









L2
L3
LD HL,0180H
PUSH HL
EXOS 24
POP HL
LD (HL),C
LD A,0FBH
CP C
JR C,L2
INC HL
JR L1

LD HL,0180H
LD C,(HL)
LD A,0FBH
CP C
RET C
PUSH HL
EXOS 25
POP HL
INC HL
JR L3
; puffercím
; verembe
; szegmensigénylés
; puffer
; kapott szg. pufferbe
;
; VIDEO-szegmens?
; kilép, ha igen
; puffer köv. cím
; ismétlés

; puffercím
; szegm.szám pufferből
;
; VIDEO-szegmens?
; vége, ha igen
;
; felszabadítás
; puffercím
; növelése
; vissza a következő szegmensre

A. BASIC betöltőprogram a rutin hívása után ki is írja a RAM-tábla területét:

100 ALLOCATE 100
110 CODE M=HEX$("21,80,01,E5,F7,18, E1,71,3E,FB,B9,38,03,23,18,F3, 21,80,01,4E,3E,FB,B9,D8,E5,F7, 19,E1,23,18,F4")
120 CALL USR(M,0)
200 FOR N=0 TO 7
210   PRINT N;PEEK:(43977+N)
220 NEXT

A táblázatban látható, hogy a végül is lefoglalva maradt FCH szegmens (decimálisan 252) került legalulra a többi felszabadítása után.

EXOS 26: rendszerbővítők lekérdezése
Belépési pont: C818H
Bemenő adatok: DE: az utasításszövegre mutat
(BF78H): funkciójelző bájt

A funkció alapfeladata egy utasításszöveg körbeadása a rendszerbővítőknek, lehetőséget adva ezzel a szöveg felismerésére és magyarázó üzenet kiírására vagy a vezérlés átvételére. Ha azonban a BF78H jelzőbájt értéke 0, a rendszer - a szöveg figyelmen kívül hagyásával - azonnal a bővítőkhöz fordul, C = 1 értékkel, hidegindítást várva tőlünk.
A funkció hívás a gépi kódú programjainkból talán kisebb jelentőségű (bár így lehet pl. visszaadni a BASIC rendszernek a kezelést), annál inkább fel kell készülnünk arra, hogy saját (bővítőként megírt és beillesztett) programrendszerünket hívja így az EXOS. Nézzük meg tehát, mik a hívás körülményei.
A funkcióhívás kiadásakor DE-t egy utasításszöveg előtti hosszbájtra kell állítani (a hívó memóriakiosztásának tetszőleges helyén). Ez a szöveg lehet pl.
a) HELP
b) HELP BASIC
c) BASIC
A három különböző típusú szövegre három különböző reakciót vár a bővítőtől az EXOS:
a) minden megadni kívánt információt írjon ki a bővítő,
b) a HELP-et követő utasításhoz tartozó információt írja ki a bővítő (ha felismeri az adott utasítást),
c) vegye át a vezérlést a bővítő, ha a megadott utasítás hozzá tartozik.
A funkcióhívás, végrehajtó rutinjának első szakasza az utasításszöveg előkészítését végzi. Felismeri, ha a szöveg HELP-pel kezdődik (az első szóközig vizsgál), a további (bővítő által vizsgálandó) szövegrészt nagybetűsítve pufferbe tölti (a BF8B/CH tartalma által kijelölt címtől). Ezután hívja meg a bővítőket ténylegesen körbekérdező C8ABH rutint a következű paraméterekkel:

Ugyanide lép a rendszer C = 1 értékkel, ha a (BF78H) jelzőbájt értéke 0 volt (és általában C egyéb értékeivel más pontokról is). Hogyan történik a bővítők lekérdezése?
Első lépésben (a háttérregiszterek tárolása után) a RAM-bővítőket kérdezi le az EXOS a BFC3H báziscím alapján. A bővítők pointerei (cím és szegmens) ugyanolyan láncba vannak fűzve, mint ahogy a periféria- vagy csatornaleíróknál láttuk:

C8AB
C8AC
C8AD
C8AE
C8AF
C8B0
C8B3
C8B4
C8B5
C8B7
C8B8


C8BB
C8BC

C8BE
C8BF
C8C1
C8C4
C8C5
D9
C5
D5
E5
D9
21 C3 BF
0C
0D
28 3C
C5
CD 47 C6


C1
28 09

E5
CB FC
CD 17 B2
E1
18 EC
EXX
PUSH BC
PUSH DE
PUSH HL
EXX
LD HL,BFC3
INC C
DEC C
JR Z,C8F3
PUSH BC
CALL C647


POP BC
JR Z,C8C7

PUSH HL
SET 7,H
CALL B217
POP HL
JR C8B3
; háttérregiszterek
; mentése
;
;
;
; bővítőlánc báziscím
; akciókód
; =0?
; végére, ha törlődött
;
; bővítő pointer
; HL: belépési cím
; A: szegmens
;
; ROM-okra, ha vége
; a bővítőláncnak
; belépési cím
; 3. LAP-ra
; bővítő hívása
; báziscím vissza
; a következő bővítőre

Ha a bővítőlánc végére ért, következik a ROM-szegmensek lekérdezése a ROM-táblázat alapján. A belépési cím itt minden esetben a C00AH, és az átadott paraméterek köre bővül: IY a szóban forgó ROM-szegmensnek kiutalt RAM-terület kezdőcímére mutat (a hidegindítás követésénél láttuk, hogy a példaként vizsgált kiépítésű gép esetén csak a 4. szegmens kért és kapott ilyén puffert).

C8C7
C8CA
C8CB
C8CC
C8CD
C8CE
C8CF
C8D0
C8D2
C8D3
C8D4
C8D5
C8D6
C8D7
C8D9
C8DA
C8DB
C8DC
C8DE
C8DF
C8E0
C8E1
C8E2
C8E3
C8E5
C8E6
C8E7
C8E8
C8EA
C8ED
C8F0

C8F2
C8F3
C8F4
C8F5
C8F6
C8F7
C8F8
2A 9C BF
2B
2B
2B
E5
0C
0D
28 20
D9
E1
2B
7E
B7
28 1B
08
2B
7E
D3 B1
2B
46
2B
4E
C5
FD E1
E5
D9
08
FE FF
21 0A C0
C4 17 B2
18 DC

E1
D9
E1
D1
C1
D9
C9
LD HL,(BF9C)
DEC HL
DEC HL
DEC HL
PUSH HL
INC C
DEC C
JR Z,C8F2
EXX
POP HL
DEC HL
LD A,(HL)
OR A
JR Z,C8F4
EX AF,AF'
DEC HL
LD A,(HL)
OUT (B1),A
DEC HL
LD B,(HL)
DEC HL
LD C,(HL)
PUSH BC
POP IY
PUSH HL
EXX
EX AF,AF'
CP FF
LD HL,C00A
CALL NZ,B217
JR C8CE

POP HL
EXX
POP HL
POP DE
POP BC
EXX
RET
; ROM-tábla címe
;
;
; következő elem
; báziscím a verembe
; akciókód
; =0?
; végére, ha törlődött
;
; tábla báziscím
;
; ROM-szegmens száma
; =0?
; ugrik, ha a tábla vége
; szg.-szám háttérbe
; kiutalt RAM-puffer
; szegmensszáma
; puffer az 1. LAP-ra
; puffercím
; LO
; puffercím
; HI
;
; IY: RAM-puffer címe
; táblacím verembe
; paraméterek vissza
; A: ROM szegmense
; törölt elem?
; belépési cím
; meghívása
; vissza a következő
; ROM-szegmensre
; veremrendezés
; háttérregiszterek
; visszaállítása

Látható, hogy a bővítők sorra kérdezésének kritériuma C>0 értéke. Az akciókódot tehát változatlanul kell hagynunk saját bővítőrutinjainkban, ill. - szükség esetén - törlésével leállíthatjuk a további közbelépéseket. Figyelemre méltó az is, hogy a letapogatás a RAM-bővítőkkel indul, tehát egy megfelelően felépített saját bővítő meghatározója lehet a rendszernek.
A rendszerbővítő felépítésével, beláncolásával egy konkrét példa kapcsán foglalkozunk. Befejezésként felsoroljuk milyen akciókódokkal fordulhat a rendszer a bővítőkhöz:

C = 1: hidegindítás
C = 2: parancsszöveg
C = 3: HELP-szöveg
C = 4: EXOS változó (n > 39 sorszám esetén)
C = 5: hibakód magyarázata
C = 6: modulbetöltés
C = 7: RAM-kijelölés
C = 8: inicializálás

EXOS 27: csatornapuffer igénylése
Belépési pont: CD5EH
Bemenő adatok: DE: az egy szegmensben igényelt puffer mérete
BC: annak a puffernek a mérete, amely szegmenshatárt is átléphet
Kimenő adat: IX: a kiutalt RAM-terület báziscíme (a puffer fölé mutat); a puffer szegmense az 1. LAP-on van

Ez a funkcióhívás csak a perifériakezelőkből, az EXOS 1-11 kezelése közben meghívott végrehajtó rutinokból adható ki. Igényli a munkaterület (EXOS által elvégzett) előkészítését, ezért nem hívhatjuk felhasználói programunkból sem közvetlenül, sem az EXOS-on keresztül.
A BC-ben megadott pufferméretet csak akkor veszi figyelembe, ha a szóban forgó periféria leírójának FLAG eleme 1 (a beépített perifériák közül ez csak a VIDEO esetén van így). Erre tekintettel kell lenni, ha esetleg saját perifériánkban vállalkozunk a szegmenshatárt átlépő pufferterület kezelésére.
A funkcióhívás az igényelt RAM-terület egyszerű lefoglalásán kívül (ami az egyszerű EXOS határ mozgatástól a meglehetősen összetett átrendezésekig, akár egész szegmensek áttöltéséig terjedhet) létrehoz egy leírót is (amint arról a csatornaműveletek - EXOS 1-11 - előkészítésénél már volt szó).

EXOS 28: hibakód értelmezése
Belépési pont: C93AH
Bemenő adatok: A: a magyarázandó hibakód
DE: a puffer címe, ahová az értelmező szöveget töltheti a program

A funkcióhívás B-ben a magyarázandó hibakóddal és C = 5 akciókóddal (Hibakód magyarázata) meghívja a rendszerbővítőket sorra kérdező (az EXOS 26-nál megismert) rendszerrutint. Mint ott láttuk, az EXOS addig hívja a rendszerbővítőket, amíg az egyikből C = 0-val nem tér vissza, vagy amíg a sor végére nem ér.
Ha tehát egy bővítő magyarázni kívánja az adott hibakódot, törölnie kell a C regisztert. A DE regisztert az adott értelmező szöveget tartalmazó memóriára állítja (a szöveg előtti hosszbájtra), míg B-ben az üzenet szegmensszámát adja vissza. A bővítőből való visszatérés után az EXOS rutin gondoskodik a magyarázó szöveg felhasználói pufferbe töltéséről, Visszatérés előtt DE-t a belépési értékre állítja vissza, tehát az üzenet hosszára mutat.
Ezt a funkciót egyszerűen fel lehet használni az EXOS hibakódok magyarázó szövegeinek kiíratására. Ehhez először egy puffercímet adunk meg (példánkban 0180H), A-ba töltjük a felhasználó által megadott magyarázandó kódot, majd meghívjuk az EXOS 28 rutint. A kapott üzenetet a blokkírás rendszerrutinnal íratjuk ki (ehhez C-be a hosszat kell írnunk, DE-t a szöveg első karakterére állítjuk). A szöveg rendezettsége érdekében a tételeket kocsivissza, soremelés karakterekkel zárjuk le:

  LD DE,0180H
LD A,L
EXOS 28
LD A,(DE)
LD B,0
LD C,A
INC DE
LD A,0
PUSH AF
EXOS 8
POP AF
PUSH AF
LD B,0AH
EXOS 7
POP AF
LD B,0DH
EXOS 7
RET
; üzenetpuffer címe
; A: hibakód
; értelmezése
; szöveg hossza
;
; BC=szöveg hossza
; DE: első karakterre
; EDITOR csatorna
; csatornaszám verembe
; blokkírás
; A: csatornaszám
;
; írandó karakter
; kiírás (soremelés)
; csatornaszám
; írandó karakter
; (kocsivissza)

A rutint betöltő és felhasználó BASIC program sorra kéri az EXOS-tól a hibakódok magyarázatát. A 4. Függelék tartalmazza a futás eredményét német, ill. angol módban (vagyis az EXOS által értelmezett hibakódok magyarázatát).

1 PROGRAM "Hibakod.bas"
100 ALLOCATE 100
110 DEF H$(N)=CHR$(INT(N/16)+48-7*(INT(N/16)>9))&CHR$(MOD(N,16)+48-7*(MOD(N,16)>9))
120 CODE M=HEX$("11,80,01,7D,F7,1C,1A,06,00,4F, 13,3E,00,F5,F7,08,F1,F5,06,0D, F7,07,F1,06,0A,F7,07,C9")
130 FOR L=255 TO 207 STEP-1
140   PRINT L;"(";H$(L);") ";
150   CALL USR(M,L)
160 NEXT

EXOS 29: modul betöltése
Belépési pont: C95FH
Bemenő adatok: DE: a felhasználó pufferének címe (ahová a modul fejrészét töltheti a rendszer)
B: a csatorna száma
Kimenő adatok:
  • A=0, ha az EXOS (vagy egy bővítő) betöltötte a modult
  • A = ECH (".NOMOD"), ha a betöltött modul "File vége" típusú
  • A = EEH (".TYPE"), ha az (egyébként ENTERPRISE formátumú) modul típusbájtját nem ismerte föl sem az EXOS, sem egy rendszerbővítő
  • A = EFH (".ASCII"), ha a modul nem ENTERPRISE formátumú (vagy típusbájtja 0), ill. megfelelő hibakód, ha beolvasási vagy elhelyezési hiba történt
  • B: a modul típusa, ha még nem történt meg a beolvasás

A funkcióhívást a megfelelő csatorna (pl. az alapértelmezésben a TAPE perifériához rendelt 6AH olvasásra való megnyitása után adhatja ki a felhasználó egy file-soron következő moduljának beolvasására.
Az EXOS rutin a modult - típusától függően - vagy maga tölti be, vagy a rendszerbővítőket hívja C=6 (Modul betöltése) akciókóddal. Ha azok sem töltik be, a felhasználó visszakapja a vezérlést. Az ENTERPRISE file-ok szerkezetét, típusait, betöltésüket az EXOS leírás tárgyalja igen részletesen.

Az utolsó 5 EXOS hívás (30.-34.) belépési pontja tulajdonképpen a 0. szegmensen van, de itt csak minimális előkészítés történik. A tényleges végrehajtó rutinok ezekben az esetekben az 1. szegmensen találhatók.

EXOS 30: áthelyezhető modul betöltése
Belépési pont: 0/CB2CH
Végrehajtó rutin: 1/E52BH
Bemenő adatok: B: csatornaszám
DE: kezdőcím a betöltéshez

A funkció egy adott modultípus (2-es) betöltését végzi. Akkor szükséges a hívása, ha az előző (EXOS 29) hívás nem töltötte be a modult és B = 2 típuskóddal tért vissza. A pufferben visszakapott fejrész 2. (LO) és 3. (HI) bájtja a betöltendő modul méretét adja. A felhasználónak kell gondoskodnia arról, hogy megadott kezdőcím fölött elférjen a betöltött egység. A betöltés után azonnal meg kell hívni az új programot az inicializálás érdekében.

A további funkcióhívások az 1 Hz-es megszakítások által léptetett óra/naptár rendszer írását, olvasását végzik.

EXOS 31: az óra beállítása
Belépési pont: 0/CB3FH
Végrehajtó rutin: 1/E43CH
Bemenő adatok:
(BCD kódban)
C: óra (0-23)
D: perc (0-59)
E: másodperc (0-59)

A funkcióhívás mindenekelőtt ellenőrzi a bevitt értékek érvényességét, majd a rögzített rendszerváltozókba tölti az óra, perc, másodperc adatokat:

E43C
E43D
E43F
E442
E443
E445
E448
E449
E44B
E44E
E44F
E453
E454
E457
E458
E459
7B
06 60
CD 17 E5
7A
06 60
CD 17 E5
79
06 24
CD 17 E5
F3
ED 53 72 BF
79
32 74 BF
FB
AF
C9
LD A,E
LD B,60
CALL E517
LD A,D
LD B,60
CALL E517
LD A,C
LD B,24
CALL E517
DI
LD (BF72),DE
LD A,C
LD (BF74),A
EI
XOR A
RET
; A = másodperc
; max. érték+1
; A<60? (hiba, ha nem)
; A = perc
; max. érték+1
; A<60?
; A = óra
; max. érték+1
; A<24?
;
; az adatokat
; beírja a
; rendszerváltozókba
;
; A=0 státusz

Az ellenőrzéshez felhasznált rutin azt vizsgálja, lehet-e az A regiszter tartalma BCD adat, majd azt, hogy az érték kisebb-e, mint a B-ben megadott határérték. Ha bármelyik kérdésre nemleges választ kap, hibakóddal (EBH, .TIME) tér vissza, de nem az órabeállító rutinhoz (azt a visszatérési címet kiemeli a veremből), hanem közvetlenül az EXOS főághoz.
Nézzük meg ezt a rutint is, hiszen a gépi kódú programozási gyakorlatban is szükséges lehet az ilyen jellegű adatok ellenőrzése:

E517
E518
E51A
E51C
E51E
E520
E522
E523
E524
F5
FE A0
30 0A
E6 0F
FE 0A
30 04
F1
B8
D8
PUSH AF
CP A0
JR NC,E526
AND 0F
CP 0A
JR NC,E526
POP AF
CP B
RET C
; ellenőrizendő érték
; 1. "karakter" BCD?
; hiba, ha nem
; 2. "karakter"
; BCD?
; hiba, ha nem
; ellenőrizendő érték
; A < max érték+1?
; vissza, ha igen
    ; Ha hibás volt az érték:
E525
E526
E527

E528
E52A
F5
F1
F1

3E EB
C9
PUSH AF
POP AF
POP AF

LD A,EB
RET
;
;
; visszatérési cím
; tölése
; ".TIME" hibakód
; vissza az EXOS-ba

Az EXOS 30 funkció végrehajtó rutinjához visszatérve, láthatjuk, hogy az óraadatokat tartalmazó rendszerváltozók (az FFH szegmensben):

49010 (BF72H): másodperc
49011 (BF73H): perc
49012 (BF74H): óra

Ezen címek ismeretében az óraadatok közvetlenül is beállíthatók. Ügyelnünk kell a BCD formára!
Például: a 23 órát a

POKE 49012,35

utasítással írhatjuk be.
Az ilyen módon bevitt adatokat nem ellenőrzi a rendszer, ez lehetőséget ad egészen szokatlan időértékek beállítására is.

EXOS 32: az óra olvasása
Belépési pont: 0/CB40H
Végrehajtó rutin: 1/E424
Kimenő adatok:
(BCD kódban)
C: óra (0-23)
D: perc (0-59)
E: másodperc (0-59)

Az. igen egyszerű végrehajtó rutin az előbb megismert rendszerváltozói értékeit olvassa a regiszterbe:

E424
E425

E429
E42C
E42D
E42E
E42F
F3
ED 5B 72 BF

3A 74 BF
4F
FB
AF
C9
DI
LD DE,(BF72)

LD A,(BF74)
LD C,A
EI
XOR A
RET
; megszakítás letiltása
; E = másodperc
; D = perc
; A = óra
; C = óra
; megsz. engedélyezés
; A=0 státusz

Az egyetlen momentum, amire itt fel kell hívni a figyelmet az, hogy a kiolvasás letiltott megszakítások mellett történik. Ez szükséges, hiszen az értékeket a megszakításrendszer lépteti (az éppen futó programtól függetlenül), és ha éppen két érték olvasása közben lépne az óra, az adatsor következetlen lenne (előfordulhatna pl., hogy az előző óra 59. perce és a közben átfordult következő óra lenne a kiolvasott érték).
Az óraadat kiolvasására a BASIC TIME$ változó a legjobb példa, a közvetlen kiolvasás lehetőségét pedig a megszakítás-programozásnál használjuk ki ("Óra az állapotsorban").

EXOS 33: a dátum beállítása
Belépési pont: 0/CB3DH
Végrehajtó rutin: 1/E45AH
Bemenő adatok:
(BCD kódban)
C: év (0-99)
D: hónap (1-12)
E: nap (1-a beállított hónapnak megfelelő max. érték)

A végrehajtó rutin feladata annyival összetettebb az EXOS 31-ben megismertnél, hogy

E45A
E45B
E45D
E460
E461
E463
E466

E469
E46A
E46B
E46E
E46F
E473
E474
E477
E478
E479
79
06 FF
CD 17 E5
7A
06 13
CD 14 E5
CD E5 E4

7B
04
CD 14 E5
F3
ED 53 75 BF
79
32 77 BF
FB
AF
C9

LD A,C
LD B,FF
CALL E517
LD A,D
LD B,13
CALL E514
CALL E4E5

LD A,E
INC B
CALL E514
DI
LD (BF75),DE
LD A,C
LD (BF77),A
EI
XOR A
RET
; A = név
; nincs felső határ
; BCD adat?
; A = hónap
; max érték+1
; 0<A<13?
; B = a honap napjainak
; száma
; A = nap adat
; max érték + 1
; 0<A<max+1?
;
; az adatokat
; beírja a
; rendszerváltozókba
;
; A=0 státusz

Az értékek ellenőrzéséhez itt is az EXOS 31-ben megismert rutint alkalmazza a rendszer, csak a hónap és nap értékeknél ezelőtt még 0-ra is ellenőriz.
A naptár adatait tároló rendszerváltozók ezek szerint:

49013 (BF75H): év (00-tól; a BASIC ehhez még 1980H-t hozzáad, így a naptár kezdőéve 1980)
49014 (BF76H): hónap
49015 (BF77H): nap

Sajátos következetlenség a rendszer részéről, hogy míg bevitelkor - természetesen - tiltja a 0. hónap, 0. nap beírását, addig ő maga megfeledkezik az értékek inicializálásáról (ezért indul a naptár 1980:00:00-ról).
Programozástechnikailag érdekes feladat a hónap napjainak megállapítása, különös tekintettel a szökőévekre. Kövessük hát - az utolsó elemzett EXOS rutinként -a rendszer módszerét.
A különböző hónapok napszámait egy táblázatban tárolja a program (E502H-tól): a hónap sorszámának megfelelő táblaelem tartalmazza az értékeket. Az adatsor készítésekor csak arra kellett ügyelni, hogy a sorszám BCD érték, így a szeptemberi adat a 9., az októberi viszont a 16. kell legyen (10H = 16Dec).
Külön kell figyelni a februári értékre, ami általában 28, de szökőévben (4-gyel osztható évszám esetén) 29 lesz. Ezt az esetet egy ügyes fogással figyeli a rendszer. A szökőévek:

E4E5
E4E6
E4E7
E4E8
E4EA
E4ED
E4EE
E4EF

E4F1
E4F3

E4F5

E4F7
E4F8
E4FA

E4FC
E4FE
E4FF

E500
E501
D5
C5
5A
16 00
21 01 E5
19
79
E6 13

28 02
EE 12

D6 01

7E
CB 7F
28 02

CE A8
C1
47

D1
C9
PUSH DE
PUSH BC
LD E,D
LD D,00
LD HL,E501
ADD HL,DE
LD A,C
AND 13

JR Z,E4F5
XOR 12

SUB 01

LD A,(HL)
BIT 7,A
JR Z,E4FE

ADC A,A8
POP BC
LD B,A

POP DE
RET
; adatok verembe
; adatok verembe
; hónap
; DE = hónap-sorszám
; tábla elé mutat
; HL = táblacím
; A = év
; 0, ha páros évtized
; és szökőév
;
; 0, ha páratlan évtized
; és szökőév
; C, h szökőév
; NC egyébként
; táblából napok sz.
; "február" jelzőbit?
; marad az érték, ha
; nem február
; 80+A8+CY=28 vagy 29
; adatok vissza
; B: a hónap napjainak
; száma
; adatok vissza
E502
E503
E504
E505
E506
E507
E508
E509
E50A
E50B
...
E510
E511
E512
E512
31
80
31
30
31
30
31
31
30
00

00
31
30
31
  ; 01: január
; 02: február (jelzőbit beáll.)
; 03: március
; 04: április
; 05: május
; 06: június
; 07: július
; 08: augusztus
; 09: szeptember
; 0A: -

; 0F: -
; 10: október
; 11: november
; 12: december

 

EXOS 34: a dátum olvasása
Belépési pont: 0/CB3EH
Végrehajtó rutin: 1/E430H
Kimenő adatok:
(BCD kódban)
C: év (0-99, 1980-tól számítható)
D: hónap
E: nap

A funkcióhívás végrehajtása tökéletesen analóg az idő olvasásával (EXOS 32), csak a kiolvasott rendszerváltozók módosulnak az előző pontnak megfelelően.

2.4 A megszakítások programozása

2.4.1 A megszakítási rendszer

A megszakítások programozása -- az operációs rendszer megszakítási rutinjainak kisebb-nagyobb módosítása - nem elemi, de mindenképpen igen hálás területe a gépi kódú programozásnak. A gép szokásos üzeme alatt; láthatatlanul a háttérből kiváltott képi hatások vagy hanghatások egyrészt igen vonzóak, másrészt gyakran egyedüli megoldásai egy-egy feladatnak (l. pl. STOPPER).
Sok személyi számítógépen találkozhatunk olyan - nyilvánvalóan a megszakítási rendszer által működtetett - hatásokkal, mint pl. a munka közben folyamatosan járó (és a képernyő adott helyén megjelenő) digitális óra, vagy a munkánkat kellemesen kísérő - de attól teljes egészében független - háttérzene stb. Egy ilyen megoldás természetesen minőségileg is többet jelent, mint pl. az óraadat egyszerű lekérdezése. Mégis sok felhasználó visszariad az ilyen jellegű feladatoktól, annak ellenére, hogy - legalábbis egy bizonyos szintig - ez sem bonyolultabb, mint a gépi kódú programozás más területei. Szerencsére az ENTERPRISE - mint látni fogjuk - szinte tálcán kínálja a lehetőséget a saját megszakítási rutin beillesztésére. Igaz ugyanakkor (és ez némileg indokolja az idegenkedést), hogy a belső megszakítási rendszer módosítása, a beépített rutinok kiegészítése, esetleg megkerülése az operációs rendszer mélyebb ismeretét igényli. Ezért mindenekelőtt tekintsük át az EXOS megszakításkezelését:
-milyen megszakításokat kezel a rendszer,
-milyen feladatokat lát el eközben,
-hol ad lehetőséget a felhasználónak a belenyúlásra, beavatkozásra (ez számunkra a legfontosabb).
A gép bekapcsolásakor, inicializálásakor az EXOS a végrehajtott legelső utasításokkal az 1-es megszakítási módba kapcsolja a CPU-t:

C000
C001
F3
ED 56
DI
IM1

Ezt az üzemmódot sem az operációs rendszer, sem a BASIC értelmező nem változtatja meg a működés során. Saját programunkkal beállíthatnánk ugyan az IM0-s vagy IM2-es módot is, de a hardvertámogatást ehhez megadó speciális interfész nélkül ennek nyilván nem lenne értelme.
A gép működése során tehát a processzor olyan üzemmódban dolgozik, amelyben bármilyen megszakításkérésre egy RST 38H utasítás végrehajtásával reagál:
Az éppen végrehajtott utasítást követő cím - mint visszatérési cím - PUSH utasítással történő elmentése után a 0038H (decimálisan 56) címen folytatja a végrehajtást.
Mielőtt elemeznénk a rutin működését, ennél a pontnál álljunk meg néhány megjegyzésre. A 0038H cím RAM-területen van (a PAGE-0-ra lapozott F8H szegmensben). A memória tartalma tehát tetszés szerint módosítható. Átírhatjuk mindjárt az első bájtot (értéke alaphelyzetben 245, azaz PUSH AF), pl. RET-re (201). Próbáljuk meg beírni:

POKE 56,201

Ezt mindenesetre akkor tegyük, ha nincs a gépben féltett adatunk, programunk, a hatás ui. meglehetősen kellemetlen. A rendszer nem hajthatja végre a megszakítási rutinokat - azonnal visszairányítjuk -, nem kezeli a billentyűzetet a szokásos módon, azaz egyszerűen nem tudunk beírni semmilyen utasítást. Szerencsére - hacsak nem változtattuk meg a rendszermemória kényesebb területeit - a RESET gomb még melegindítást vált ki: föléled a rendszer.
Az mindenesetre nyilvánvaló, hogy ezen a ponton eltéríthetjük a megszakításkezelést. Akár az egész megszakítási rendszert sajátunkkal helyettesíthetjük (természetesen a játékszabályok betartásával). Azt is megmutatta ez a kísérlet, hogy a rendszer a megszakításkezelés leállítása után sem omlik össze, ép marad a kijelzés, sőt mint erről meggyőződhetünk, ha a fenti utasítást programban adjuk ki a BASIC program is tovább működik, legalábbis a legtöbb utasítás végrehajtható. Természetesen nem várhatunk működő órát az adott körülmények között, sőt a GET és INPUT sem működik. Hasznos lehet viszont ez az utasítás, ha egy időigényes programrészlet előtt adjuk ki, majd a rutin lefutása után visszaállítjuk a memóriacím eredeti tartalmát. Nyilvánvaló, hogy a program futása felgyorsul, ha a másodpercenként sokezer (kizárólag a megszakításokat kezelő) utasítást nem kell elvégeznie. Próbálja ki ezt pl. a következő egyszerű program futtatásával:

100 ! POKE 56,201
110 FOR N=1 TO 500
120   PRINT AT 1,1:N
130 NEXT
140 POKE 56,245

A program futási ideje ebben a változatban kb. 40 s. Ha a megszakításokat leállítjuk

100 POKE 56,201

átírással, a végrehajtás kb. 30 s-ig tart.
A kb. 25%-os időmegtakarításon túl további előny, hogy a megszakítások kizárásával pontosabbá válik a BASIC utasítások végrehajtási ideje, megszűnik a futási idő esetlegessége.
Ez a módszer használható akkor is, amikor a rendszerbe való beavatkozás (pl. egy több bájtos adat vagy cím kiolvasása, ill. beírása) miatt le kellene tiltanunk a megszakításokat. Józan ésszel azt gondolnánk, ehhez elég egy saját gépi kódú rutinban a 'DI' (Disable megszakítási) Z80-as utasítást kiadni. Ez azonban nem vezet eredményre, hiszen az EXOS a futás során a megszakítási letiltást rendszeresen feloldja. Ezt a feladatot is a fenti "ál-letiltás" fogással oldhatjuk tehát meg legegyszerűbben.
Térjünk ezek után vissza az EXOS megszakításkezeléséhez. Milyen főbb feladatai vannak ennek a rendszernek?

Hogyan látja el ezeket a funkciókat az EXOS?
A 0038H-as címen induló rutin - bebillentett átvitelbittel - az EXOS leggyakrabban használt rutinján, a RST 30-on folytatja futását:

0038
0039
003A

0045
0047
004A
004C
004E
F5
37
18 09

DB B3
32 55 00
3E 00
D3 B3
C3 10 C4
PUSH AF
SCF
JR 0045

IN A,(B3)
LD (0055),A
LD A,00
OUT (B3),A
JP C410
; A regiszter elmentése
; CY=1, ez különbözteti meg
; a többi EXOS hívástól

; P3 pillanatnyi SZG-e
; átmeneti tárolóba
; a 0. szegmenst
; lapozza a 3. LAP-ra
; rendszer ROM-ra ugrik

A 0. ROM-szegmens C410H címétől egy szakaszon együtt folyik a megszakításkezelés előkészítése az 'EXOS n' végrehajtásával.
Mivel a megszakításkezelés során beírt átvitelbittel ér ide a program (l. 0039H), a C454H pontnál a végrehajtás elválik az EXOS főágtól.
A 0. szegmens C4B2H címén induló tényleges megszakításkezelő mindenekelőtt a verembe tárolja a még biztonságba nem helyezett regisztereket:

C4B2
C4B3
C4B4
C4B5
C4B6
C4B7
C5
D5
D9
C5
D5
E5
PUSH BC
PUSH DE
EXX
PUSH BC
PUSH DE
PUSH HL
; a további regiszterek
; verembe mentése

A továbbiakban a saját igényeinek megfelelően állítja be a visszatérési szakaszt:

C4B8
C4BA
3E B7
32 56 00
LD A,B7
LD (0056),A
; "OR A" kódja
; a visszatérési ágba

Bármilyen megszakítás történt, feltétel nélkül növeli 1-gyel a RANDOM-IRQ rendszerváltozót (ez a változó véletlenszámként való felhasználásának alapja):

C4BD
C4C0
21 EC BF
34
LD HL,BFEC
INC (HL)
; RANDOM_IRQ címe
; növeli a változót

A következő szakaszban a kezelő a megszakítást kiváltó okkal foglalkozik. Az EXOS leírás szerint a rendszerben alapvetően négyféle megszakítás történhet:
- hangmegszakítás,
- 1 Hz-es megszakítás,
- videomegszakítás (50 Hz),
- külső (hálózat) megszakítás.
Ezek mindegyikét a DAVE chip kezeli, közvetíti. Az egyes források megszakításkérését külön engedélyezhetjük, vagy tilthatjuk a B4H-es kimeneti ponton. Az EXOS ezt az IRQ-ENABLE-STATE rendszerváltozó megfelelő beírásával végzi el (ennek az eljárásnak a megváltoztatása könnyen a rendszer összeomlását okozhatja).
Amennyiben bármelyik engedélyezett forrás megszakítást kér, a DAVE egységesen továbbítja ezt a mikroprocesszor felé. A megszakítás lehetséges forrásait a szoftverből beolvasható B4H-es pont egyes bitjei reprezentálják:

BIT 1: hang
BIT 3: 1 Hz
BIT 5: video
BIT 7: külső

Adott esetben az a forrás váltotta ki az IRQ-t (Interrupt Request - megszakításkérés), amelyik bit értéke 1. Ezt vizsgálja a rutin folytatása:

C4C1
C4C3
C4C5
C4C6
C4C8
C4C9
DB B4
E6 AA
47
ED 44
A0
57
IN A,(B4)
AND AA
LD B,A
NEG
AND B
LD D,A
; a megszakítás forrása
; 10101010 maszkolás
; regiszterbe
; -A
; egyetlen 1-es marad
; D: megszakítási mód

A továbbiakban tehát a D regiszterben áll rendelkezésünkre az IRQ forrásának információja az előbbiek szerint.
Ennek alapján a megszakításkezelő minden videornegszakításnál (azaz másodpercenként 50-szer) aktualizálja a keretszínt a BORD-VID rendszerváltozó beírásával és a NICK-chip FIXBIAS regiszterét a BIAS-VID, a MUTE-SND és a SPRITE rendszerváltozók alapján.

C4CA
C4CC
CB 6A
C4 16 C5
BIT 5,D
CALL NZ,C516
; VIDEO megszakítás?
; rutinjára, ha igen

Ez az oka annak, hogy - bár a keretszín a 81H (129Dec) ponton közvetlenül is beírható - az így átírt színű keret legfeljebb egy villanásnyira jelenik meg.
Ezután érkezik el a megszakításkezelő ahhoz a ponthoz, amely számunkra talál a legfontosabb. Beolvassa a USER-ISR rendszerváltozót (a rendszerszegmens BFED/EH címének tartalmát) és ha ott nullától különböző értéket talál, azt felhasználói megszakítási rutin címének tekinti.

C4CF
C4D2
C4D3
C4D4
C4D5

C4D8
2A ED BF
7C
B5
D5
C4 25 B2

D1
LD HL,(BFED)
LD A,H
OR L
PUSH DE
CALL NZ,B225

POP DE
; HL = USR_ISR
;
; HL=0000?
; kód verembe
; felhasználói rutin
; meghívása (ha van)
; kód vissza

A B225H címen (az EXOS főág részeként) egy JP(HL) utasítás van, tehát a megszakításkezelő lényegében egy CALL USR-ISR hívást hajt végre.
Mivel a USER-ISR értéket tetszés szerint beállíthatjuk - akár BASIC-ből, akár gépi kódú rutinunkban az FF szegmens BFED/EH (decimálisan 49133/4) címén - ez a vizsgálat és elágazás adja a legkényelmesebb lehetőséget saját megszakítási rutinunk rendszerbe illesztésére. Foglaljuk össze tehát, milyen státuszban van a rendszer a rutin meghívásakor, mit használhatunk fel és mit kell megőriznünk.

Memóriakonfiguráció:

PAGE-0: az F8H-as RAM nulláslap-szegmens (mint mindig)
PAGE-1: megmarad a megszakítás pillanatában belapozott szegmens, tehát határozatlannak tekinthető
PAGE-2: az FFH-s RAM rendszerszegmens
PAGE-3: a 00H-s ROM-szegmens (ahol az EXOS megszakításkezelő fut)

Ha azt akarjuk, hogy rutinunk vége (RET) után a szokásos módon folytatódjon a megszakításkezelés, ezt a konfigurációt meg is kell őriznünk (vagy vissza kell állítani a visszatérés előtt), kivéve a PAGE-1 állapotát, amit a továbbiakban sem tekint határozottnak az EXOS, és vissza is állítja eredeti állapotát a megszakításkezelés végén. Mindezekből az is következik, hogy a felhasználói megszakítási rutin belépési címének a nulláslapon kell lennie (esetleg PAGE-2-n, a rendszerszegmensben, de itt könnyen ütközhetünk az EXOS-szal).

Regiszterek:
Az EXOS a processzor valamennyi regiszterét (a vesszős és az indexregisztereket is) elmentette, mire ehhez a ponthoz ért. Ebből a szempontból tehát szabad kezünk van: egyik regiszter tartalmát sem kell megőriznünk.
Hasznos információt a D regiszter tartalmaz: a fentiek szerint a megszakítás forrására utal. HL már a meghívott rutinra mutat, ezt esetleg szintén fel lehet használni.
A verem tetején a visszatérési cím - esetünkben C4D8H - található. Ezt akár le is emelhetjük innen (POP), ha meg akarjuk kerülni a megszakításkezelés ezután következő részeit. Ezzel mindenesetre körültekintőnek kell lenni, hiszen -mint látni fogjuk - hátra van még a szinte minden felhasználásban nélkülözhetetlen billentyűzetkezelés, másrészt az EXOS-szal megegyező módon kell rendbe tenni kilépés előtt a memóriakonfigurációt, regisztereket stb.
Ide tartozik még, hogy kilépéskor a megszakítások le vannak tiltva és nekünk sem, szabad engedélyezni azokat ('EI') a kezelő vége előtt.

Ezen információk birtokában már bátran vállalkozhatunk kisebb megszakítási rutin elkészítésére, beillesztésére (mint arra a továbbiakban adunk is néhány példát). Egyelőre azonban nézzük tovább a ROM-ot, tekintsük át, milyen feladatokat lát még el az EXOS megszakításkezelője.
Minden 1 másodperces megszakításnál lépteti az órát és (ha szükséges) a naptárat:

C4D9
C4DA
C4DC
AF
CB 5A
C4 3C CB
XOR A
BIT 3,D
CALL NZ,CB3C
; A=0
; 1 Hz-s megszakítás?
; óra, naptár léptetés

Ezután következik a perifériák lekérdezése: át kívánják-e venni a vezérlést az adott típusú megszakításnál.

C4DF
C4E2
C4E5
C4E7
C4E8
C4E9
C4EA

C4EC
C4ED
C4EE
C4EF
C4F0
C4F1
C4F2
C4F3
C4F6

C4F9
C4FA
C4FB
C4FC
C4FE

21 C0 BF
CD 47 C6
28 19
5F
7E
B7
20 F6

23
7E
A2
2B
E5
D5
7E
01 E5 53
C4 41 C5

D1
E1
7B
D3 B1
18 E2

LD HL,BFC0
CALL C647
JR Z,C500
LD E,A
LD A,(HL)
OR A
JR NZ,C4E2

INC HL
LD A,(HL)
AND D
DEC HL
PUSH HL
PUSH DE
LD A,(HL)
LD BC,53E5
CALL NZ,C541

POP DE
POP HL
LD A,E
OUT (B1),A
JR C4E2

; HL: báziscím
; a következő láncelem
; elugrik, ha lánc vége
; E: a leíró szegmense
; A: leíró TYPE eleme
; A=0?
; tovább keres, ha ez
; törölt leíró
; IRQ_FLAG címe
; A = IRQ_FLAG
; (D: megszakítási mód)
; TYPE címe
; regiszterek
; verembe
; A=0
; memóriallenőrzéshez
; hívja a kezelő megsza-
; kítás rutinját, ha kéri
; regiszterek
; vissza
; leíró szegmense
; az 1. LAP-ra
; következő láncelemre

Az eljárás alapja az a leírólánc, amiben az EXOS tárolja a létező perifériák adatait (l. inicializálás). Az első leíróra a BFC0H báziscím (49088D) mutat (pontosabban: BFBFH-n a leírót tároló szegmens száma van, BFBD/EH tartalma a leíró címe a PAGE-1-en).
Ez a cím ugyancsak bázisnak tekinthető: az előtte lévő 3 bájt a következő láncelemre mutat. A lánc mindaddig tart, amíg a következő leíró szegmensszámaként 0-t nem találunk. Ezt a láncot vizsgálja végig a rutin, ugyanúgy, ahogy mi is megtettük ezt a 2.3.1 pontban a. LANC programmal.
Tekintsük most át még egyszer a futtatás eredményeként megkapott periférialáncot.

Az EXOS leírás megadja az egyes bájtok jelentését (ezzel a 2.5 alfejezetben foglalkozunk részletesebben). Számunkra most a TYPE (0.) és IRQ-FLAG (1.) bájtok a legérdekesebbek. A láncban csak azok a leírók érvényesek, ahol TYPE = 0. Látható, hogy az inicializálás során felvett 0. szegmensbeli KEYBORD és EDITOR kezelőt mintegy felülírta a későbbiek során vizsgált 4. szegmens ugyanilyen nevű perifériája. (Német konfiguráció!)
A periférialeírók 1. eleme, az IRQ-FLAG az, amelyet a rendszer összehasonlít a megszakítás forrását reprezentáló D regiszterrel. Megegyezés esetén, - ha a periféria igényt tart az adott megszakítás kezelésére - meghívja annak megszakításkezelőjét (ehhez belapozza a periféria szegmensét PAGE-3-ra, majd a belépési pont táblázat 0. címére ugrik). Ez a harmadik (és egyben utolsó) pont, ahol módosíthatjuk az alapértelmezésű megszakítási rendszert. Akár a perifériakezelők paramétereit változtatjuk meg, akár új perifériát veszünk fel a rendszerbe, lehetőségünk van a beépített megszakításkezelés körének szűkítésére, bővítésére.
Tekintsük át az alaphelyzetben (angol és német nyelvű változat) kiépített periférialáncot. A táblázat alapján megállapíthatjuk, hogy

Az EDITOR a megszakításkezelés során gyakorlatilag az adott (német) BASIC verzió belapozottságán őrködik. Számunkra elsősorban a videomegszakítások érdekesek; az, hogy mi minden történik a háttérben, minden ötvened másodpercben.

KEYBOARD (4/DFC2):

SOUND (0/EBD6):
A hangkezelő a videomegszakítások alatt (tehát másodpercenként 50-szer) aktualizálja a hangparamétereket (a burkológörbéknek megfelelően).

VIDEO (0/D33D):
A videomegszakítás-kezelő az állapotsor kijelzéséről gondoskodik. A sorparaméter-tábla (LPT) 0. sorában beállítja a margókat és a megjelenítendő memóriaterület kezdőcímét. Az ST-FLAG rendszerváltozó (FF/BFDFH cím) értéke szerint:

A perifériák lekérdezésével véget is ér a hardvermegszakítások kezelése. A továbbiakban az IRQ-ENABLE-STATE rendszerváltozó alapján aktualizálja a DAVE-chip megszakításengedélyező regisztereit, visszaállítja az EXOS visszatérési szakasz és a regiszterek belépés előtti értékét, majd visszaugrik az EXOS főágra.

C500
C503
C504
C506
C508
C50B
C50C
C50D
C50E
C50F
C510
C511
C513
3A C5 BF
B2
D3 B4
3E FB
32 56 00
E1
D1
C1
D9
D1
C1
26 F1
C3 6D C4
LD A,(BFC5)
OR D
OUT (B4),A
LD A,FB
LD (0056),A
POP HL
POP DE
POP BC
EXX
POP DE
POP BC
LD H,F1
JP C46D
; IRQ_ENABLE_STATE
; (D: megszakítási kód)
; engedélyező reg.be
; "EI" kódja
; a visszatérési ágba
; regiszterek
; vissza
;
;
;
;
; "POP HL" kódjával
; ugrik az EXOS főágra

A főág a belépés előtti memóriakonfiguráció visszalapozása és a regiszterek visszaállítása után még egy megszakítás vonatkozású funkciót is ellát visszatérés előtt: a szoftver-megszakítás kezelését. Ezt bármelyik megszakításkezelő alprogram (pl. billentyűzet) vagy EXOS "n" végrehajtó rutin kiválthatja azzal, hogy 0-tót különböző értéket tölt a rendszerszegmens BFF2H címére (FLAG-SOFT-IRQ). Ha az EXOS a zárószakaszban nullától különböző értéket talál, beírja ezt, mint szoftver megszakítási kódot a CODE-SOFT-IRQ rendszerváltozóba (BFC7H). 0-t ír a FLAG-..be (megteremtve ezzel a következő megszakítás lehetőségét), majd ellenőrzi a nullás lap 3D/3EH címét: a felhasználói szoftvermegszakítási rutin címét (SOFT-SR). Ezt a címet a felhasználó tetszés szerint beállíthatja, pl. az adott BASIC verzióban, alapértéke 00FFH).
Nullától különböző rutincím esetén az EXOS átírja a nullás lapon saját visszatérési szakaszának legvégét: a szokásos C9H ("RET") helyére 18H, E3H, ("JR E3H") kerül. Ezután ugrik a kezelő (a rendszerszegmensen lévő EXOS betéten keresztül) a visszatérési szakaszra. Alaphelyzetben innen végre visszakerül a hívóhoz, míg szoftvermegszakítás esetén egy 'JP SOFT-ISR' utasításra ugrik és a felhasználói szoftver megszakításkezelőn át tér vissza vagy fejezi be a futást hibajelzéssel.
Itt is egyszerű lehetőségünk adódik tehát a beavatkozásra: a 3D/3EH címekre beírhatjuk saját szoftver megszakításkezelőnk címét. Ebben tetszés szerint dönthetünk a további teendőkről. Például a STOP-billentyű lenyomása által kiváltott szoftver-megszakítási esetén (CODE-SOFT-IRQ=21H) egyszerűen figyelmen kívül hagyjuk a megszakítást, míg a többi esetben a beépített kezelőre ugrunk.

Foglaljuk össze azokat a pontokat, ahol beavatkozhatunk a hardvermegszakítások kezelésébe:

2.4.2 Állapotsor saját használatra

A videokezelő - mint láttuk - minden ötvened-másodpercben kijelzi az állapotsorban a megfelelő memóriaterület tartalmát. A kijelzett információ (billentyűzetüzemmód, programszám stb.) igen hasznos, de egyes esetekben legalább ilyen hasznos lenne saját üzenetünk kiírása. Írhatunk ugyan a másik kijelezhető területre (a készítők monogramjainak helyére) is, de erre az EXOS meglehetősen kényes; védett területnek tekinti. Egyetlen bájt átírása is azzal jár, hogy a RESET gomb egyszeri megnyomása is hidegindítást vált ki. Akadályozzuk meg tehát a videokezel6 munkáját, és máris szabadon bánhatunk az állapotsorral. Ez az eddigi ismeretek alapján igen egyszerű: mindössze azt a bitet kell törölnünk a periférialeíróban, ami alapján a kezelő reagál a videomegszakításokra.
Az átírandó cím a periférialánc táblázatából állapítható meg. A VIDEO leíró báziscímét az előző leíró (SOUND) 'NEXT' paramétere adja. Esetünkben ez: 255. szegmens, cím HI = 66H = 102Dec, LO = F9H = 249Dec. A báziscím tehát

BC=249+256*102=26361

Az IRQ-FLAG címe: BC + 1, esetünkben 26 362.
A cím tartalma alaphelyzetben 20H (32Dec), azaz BIT 5 = 1 (a kezelő a videomegszakításra reagál).
Írjuk be tehát:

SPOKE 255,26362,0

és miénk az állapotsor (persze előbb ki-ki ellenőrizze a konkrét címeket saját gépén a fenti lépésekkel). Az így megszerzett lehetőség felhasználására láthatunk példát a következő megszakítás alkalmazásban.

2.4.3 Óra az állapotsorban

Sok személyi számítógépen láttunk már olyan programokat, amelyek hatására megjelenik a gép digitális órája a képernyő valamelyik zugában (általában ott, ahol más információt nem zavar), és folyamatosan jár, miközben mi dolgozunk a gépen: programot írunk vagy futtatunk stb.
Ennek a feladatnak a megoldása a megszakítási rendszer ismeretében szinte magától értetődik: be kell építeni egy felhasználói megszakításkezelő rutint, amely az 1 Hz-es megszakítások alatt kiolvassa a gépi órát és - a szokott formátumban - kijelzi a képernyőn. Különösen bátran indul neki a megoldásnak, aki a TIMES függvény (ill. változó) kiértékelését ismerve tudja, hogy az óraadat munkaregiszterbe töltése egyetlen ROM-rutin hívásával elérhető. Senkinek sem javasoljuk azonban ezt a magát szinte kínáló megoldást! Az 1. szegmenst C530H címén indító rutin alkalmazása ugyan problémamentes lenne, de a rutin lelke - az óra-, perc- és másodpercadatokat a C, D és E regiszterbe töltő EXOS 20H rutin - két buktatót is rejt. A minden EXOS hívásnál fellépő alapproblémát az utolsó példa kapcsán tárgyaljuk, de itt maga a végrehajtó rutin is alkalmazhatatlan megszakításkezelőben. Nézzük meg, miért! Az 1. szegmens E424H címén kezdődő rutin rendkívül egyszerű:

E424
E425
E429
E42C
E42D
E42E
E42F
F3
ED 5B 72 BF
3A 74 BF
4F
FB
AF
C9
DI
LD DE,(BF72)
LD A,(BF74)
LD C,A
EI
XOR A
RET
; megszakítás letiltása
; D = perc; E = s
; A = óra
; C = óra
; megszakítás eng.
; A=0; EXOS vissza-
; téréshez

Azonnal szembeötlik az alkalmazást kizáró ok: a rutin vége előtt kiadott 'EI' utasítás. Ez nyilván tilos egy megszakításkezelő kellős közepén. Szerencsére nem akkora ez a rutin, hogy ne írhatnánk be közvetlenül is saját kezelőnkbe. Az így megszerzett adatokkal problémamentesen felhasználhatjuk a TIME$ rutin hátralevő (az óraadatokat füzér alakba konvertáló és a munkaregiszterbe töltő) részét. Ezután már csak az a probléma, hogy hová írjuk ki az eredményt. Átvihetnénk p1. a mindenkori első sorba, amelynek címét a sorparaméter-táblából állapíthatjuk meg (B914/5H). Ez az út járható ugyan, de a képernyő görgetésekor egy-egy sorban "szemétként" megmaradhatnak a régebben odaírt óraadatok. Ez lehet ugyan szórakoztató, de előbb-utóbb bosszantóvá válik.
A legegyszerűbb megoldás a rendszerszegmens B230H címére írni (l. a következő példát), de ezzel elveszítjük a RESET gomb adta biztonságot. Alkalmazzuk tehát az előzőekben tárgyalt fogást: tiltsuk le a video megszakításkezelőt és ezután bármilyen videomemóriát (FCH-FFH szegmensek) megjeleníthetünk az állapotsorban. Mivel kevés helyre van szükségünk, egy érdekes megoldásként felhasználhatjuk a sorparaméter-tábla (LPT) legvégén álló, ki nem jelzett szinkronizáló sorainak cím- és palettabájtjait. Ennek a területnek a végét (BB20H) kell megadnunk munkaregiszterként, ha azt szeretnénk, hogy a TIME$ rutin közvetlenül a pufferbe töltse az órafüzér karaktereit.
Ezek után elkészíthető a megszakításkezelő rutin:

USR_ISR



IR_1Hz
ORG 0180H
BIT 3,D
JR NZ,IR_1Hz
RET
LD DE,(0BF72H)
LD A,(0BF74H)
LD C,A
IN A,(0B3H)
PUSH AF
LD A,01
OUT (0B3H),A
LD HL,(0228H)
PUSH HL
LD HL,0BB20H
LD (0228H),HL
CALL 0C532H
POP HL
POP AF
OUT (0B3H),A
LD (0228H),HL
RET
;
; 1 Hz-es megszakítás?
;
; vissza, ha nem
; D = perc; E = s
; A = óra
; C = óra
; pillanatnyi 3. LAP
; szegmense verembe
; 1. szegmenst
; lapozza a 3. LAP-ra
; HL: munkaregiszter
; ktuális érték verembe
; VIDEO puffercím
; mint munkaregiszter
; TIMES -> puffer
;
;
; 3. LAP vissza
; alapérték vissza

A rutin futását BASIC-ből elő kell készíteni, sőt az egész rutint is érdemes BASIC-ben beírni és indítani. Ezt végzi el a következő program.

100 ! VIDEO leiro cime
110 ! print 102*256+249
120 ! 26361
130 ! print speek(255,26362)
140 ! 32 (IRQ_FLAG=20H)
150 ! SPOKE 255,26362,0
160 POKE 47364,24 ! PUFFER LSB
170 POKE 47365,251 ! PUFFER MSB
180 POKE 47362,35 ! bal margo
190 POKE 47363,43 ! jobb margo
200 ! USR_ISR
210 POKE 540,128
220 POKE 541,1
230 CODE M=HEX$("CB,5A,20,01,C9,ED, 5B,72,BF,3A,74,BF,4F,DB,B3,F5, 3E,01,D3,B3,2A,28,02,E5,21,20, BB,22,28,02,CD,32,C5,E1,F1,D3, B3,22,28,02,C9")
240 CODE MB=HEX$("21,ED,BF,F3,36,80, 23,36,01,FB,C9")
250 CALL USR(MB,0)

A program első része az állapotsor megszerzésének lépéseit mutatja (REM-sorokban) az előző pont gondolatmenete alapján. A 160. sor ténylegesen végrehajtandó, természetesen az adott gépen érvényes címmel. A további sorok az FB18H videocím (FF szegmens, BB18H cím) kijelzésére állítják az állapotsort, a sor margóit pedig 8 karakter kijelzésére.
Ezután történik a felhasználói megszakításrutin beírása a memóriába és beillesztése a megszakításkezelésbe. Itt két dolog magyarázatot igényel:

  1. Hol tároljuk a rutint a memóriában?
    BASIC programról lévén szó, a legegyszerűbb az lett volna, ha ALLOCATE-tel helyet foglalunk a rutin számára a program előtt, és oda töltjük. Általában ez tökéletes megoldás, de - megszakításrutinról lévén szó - valamit hangsúlyoznunk kell. A BASIC munkaterület elejére elhelyezett gépi rutin nincs teljes biztonságban. Bizonyos esetekben (pl. program fejlesztéskor) a programszöveg visszacsúszhat a lefoglalt területre, felülírva ezzel az operációs rendszer által sűrűn meghívott rutint. Ez szinte biztosan a rendszer összeomlásához vezet. Ezért helyeztük el a rutint a BASIC által nem használt 0180H címtől kezdődően.
  2. Hogyan illesztjük be a rutint a megszakítási rendszerbe?
    A feladat igen egyszerűnek látszik: be kell írni a USR-ISR változóba (BFED/EH, ill. 49133/4Dec) a rutin címének alsó és felső bájtját (esetünkben 80H, 01H, ill. 128Dec, 1Dec). Valójában ezt nem tehetjük meg két egymás utáni POKE utasítással, mert ha a kettő között megszakításkérés érkezik, az EXOS a félig érvényes című rutinra ugrik. Két megoldást alkalmazhatunk: a megszakítást az átírás idejére letiltó gépi kódú programot, vagy a megszakításokat "eltérítő" BASIC utasításokat:
    ..10 POKE 56,201
    ..20 POKE 49133,128
    ..30 POKE 49134,1
    ..40 POKE 56,245

A példaprogramban az első megoldást alkalmaztuk:

MB LD HL,0BFEDH
DI
LD (HL),80H
INC HL
LD (HL),01H
EI
RET
; USR_ISR változó
;
; LO beírás
;
; HI beírás

A kijelzett óra azonos a TIME$ változó értékével, a TIME utasítással bármikor beállítható. A kijelzés fennmarad akkor is, ha az elindítás után a "hordozó" programot töröljük a tárból, saját programot írunk, futtatunk stb. Ha szükséges, a BASIC állapotsort bármikor visszakaphatjuk, ha visszaállítjuk a nullázott IRQ-FLAG (l. 160. sor) értékét 32-re, újra lehetővé téve ezzel a VIDEO periféria megszakítás-kezeléseit.

2.4.4 Stopper

Az előző példán felbátorodva sokakban felmerülhet a kérdés: ha ilyen könnyen beavatkozhatunk a megszakítási rendszerbe, miért elégedünk meg a gép 1 másodperc felbontású belső órájával. Sok játékhoz, reakcióidő vagy futásidő méréshez, esetleg komolyabb oktatástechnikai, mérési alkalmazáshoz nélkülözhetetlen egy sokkal finomabb lépésközű - pl. századmásodperces óra.
Az ENTERPRISE megszakítási rendszerének ismeretében megállapíthatjuk, hogy ha nem is századmásodperces, de 0,02 s felbontású időmérést minden probléma nélkül beépíthetünk. Ennyit tesz lehetővé az 50 Hz-es VIDEO megszakítás.
Tekintsük át, milyen feladatokat kell ellátnia megszakításkezelőnknek!
Ha megelégszünk 99,98 s-ig való méréssel, két számlálóra lesz szükségünk a századmásodpercek (REG1) és a másodpercek számára (REG2).
Foglaljuk egy egyszerű blokkdiagramba a teendőket!

A két regiszterhez válasszuk a 0000 és 0001 memóriacímet (bármilyen szabadon használható RAM megfelel). A számlálást végezzük BCD rendszerben. Ez egyrészt jobban megfelel a decimális kijelzési logikának, mint a bináris érték, másrészt igen egyszerű a 99-ből 00-ba való átfordulás detektálása.
Az óra állását jelezzük ki ##,## alakban. A BCD értékek 2 számjegyes ASCII alakra konvertálásához jól felhasználhatjuk a TIME$ és DATE$ rutinok alprogramját az 1. szegmens C56FH címén. Itt is felmerül az előző példa kérdése: hol és hogyan jelezzük ki a stopperünket? Ebben az esetben válasszuk az egyszerűbb megoldás nagyobb kockázat kompromisszumát (legalább erre is látunk példát): használjuk a rendszerszegmens B230H címen kezdődő "copyright" területét. A kockázat természetesen csak abban áll, hogy a készítők monogramjaiba való belenyúlás miatt a RESET gomb egyszeri megnyomása is hidegindítást eredményez. Ha ez megengedhetetlen, használja az előző rutin kijelzési megoldását!
Ezután lássuk a felhasználói megszakítási rutin assembly listáját:

USR_ISR


IR_VID













DISP
ORG 0180H
BIT 5,D
RET Z
LD HL,0000
LD A,(HL)
INC A
DAA
INC A
DAA
LD (HL),A
JR NZ,DISP
CCF
INC HL
LD A,(HL)
INC A
DAA
LD (HL),A
IN A,(0B3H)
PUSH AF
LD A,01
OUT (0B3H),A
LD A,(0000)
LD HL,0B23AH
CALL 0C56FH
DEC HL
LD (HL),2CH
LD A,(0001)
CALL 0C56FH
POP AF
OUT (0B3H),A
RET

; 50Hz-es megszakítás?
; vissza, ha nem
; munkaregiszter
; REG1: 1/50-es sec.
; számlál
; BCD korrekció
; számlál
; BCD korrekció
; REG1 =REG1+2
; kijelzésre, ha REG1<100


; REG2: másodperc
; számlál
; BCD korrekció
; REG2 = REG2+1
; pillanatnyi 3. lap
; szegmense verembe
; az 1. szegmenst
; lapozza a 3. LAP-ra
; A: századmásodperc
; HL: puffercím
; konverzió "##" alakra
; elé
; ":"
; A: másodperc
; konverzió "##" alakra
; szegmens-szám
; 3. LAP alapérték

A rutin működése nem igényel különösebb magyarázatot; lépésről-lépésre követi az előbbiekben tárgyalt megoldási logikát. Megírásához használhatunk assemblert, de nem igényel különösebb energia befektetést a BASIC-ből való betöltés sem. A program a gépi rutin betöltése előtt kijelzi és törli (szóközökkel tölti fel) a kiválasztott memóriaterületet. Betöltés előtt itt is (mint az előző pontban) egy gépi rutint alkalmazhatunk a felhasználói megszakítási rutin címének beállítására (USR-ISR). Az eltérés az, hogy itt a stopper leállítására is szükség van. A bekapcsoló (MB) és kikapcsoló (MK) rutin teljesen analóg, csak MK nullát tölt az USR-ISR rendszerváltozóba.

100 POKE 49119,42
110   FOR N=0 TO 33
120 POKE 45616+N,32
130 NEXT
140 POKE 540,128
150 POKE 541,1
160 CODE M=HEX$("CB,6A,C8,21,00,00,7E,3C,27,3C,27,77,20,06,3F, 23,7E,3C,27,77,DB,B3,F5,3E,01,D3,B3,3A,00,00,21,3A, B2,CD,6F,C5,2B,36,2C,3A,01,00,CD,6F,C5,F1,D3,B3,C9")
170 CODE MB=HEX$("F3,36,80,23,36,01,FB,C9")
180 CODE MK=HEX$("F3,36,00,23,36,00,FB,C9")
190 DEF STOPPER
200   CALL USR(MK,49133)
210   POKE 0,0
220   POKE 1,0
230   CALL USR(MB,49133)
240   FOR T=1 TO 548
250   NEXT
260   CALL USR(MK,49133)
270 END DEF

A program végén található STOPPER rutin egy egyszerű példa a megszakítási rutin felhasználására: a 240-250. programrészlet futásidejét méri (természetesen ennek helyére akármi mást is beírhatunk). A mérést a CALL STOPPER utasítással lehet indítani.

Érdekesebb alkalmazást tesz lehetővé a reflexidőt mérő következő program. A bevezető rész hasonló az előzőhöz: a 0,02 másodperces számláló megszakítási rutint tölti be. Mivel azonban itt nincs szükség a folyamatos kijelzésre, elhagyhatjuk az állapotsort előkészítő sorokat. Maga a gépi kódú rutin is egyszerűbb lett ezzel, hiszen abban is elhagyható a kijelző funkció: az előző assembly listában a DISP címke már a visszatérési pont lehet (RET). Ezzel egyben visszakaptuk a RESET gombot is.

100 RANDOMIZE
110 POKE 540,128:POKE 541,1
120 CODE M=HEX$("CB,6A,C8,21,00,00,7E,3C,27,3C,27,77,20,06,3F,23,7E,3C,27,77,C9")
130 CODE MB=HEX$("F3,36,80,23,36,01,FB,C9")
140 CODE MK=HEX$("F3,36,00,23,36,00,FB,C9")
150 CALL REAKCIOIDO
160 END
170 DEF REAKCIOIDO
180   LET J,H=0
190   LET MN=2:LET MX=0
200   TEXT 40
210   PRINT AT 5,7:"Nyomjon meg egy gombot, ha"
220   PRINT AT 7,18:"`*`"
230   PRINT AT 9,4:"jelenik meg a - - jelek kozott!"
240   PRINT AT 15,18:"- -"
250   WAIT 2
260   DO
270     CALL USR(MK,49133)
280     POKE 0,0:POKE 1,0
290     LET N=RND(5)
300     LET A$=CHR$(40+N)
310     PRINT AT 15,20:A$
320     CALL USR(MB,49133)
330     IF PEEK(1)>=1 THEN 270
340     GET A$
350     IF A$="" THEN GOTO 330
360     CALL USR(MK,49133)
370     IF N=2 THEN CALL OK
380     IF N<>2 THEN CALL HIBA
390   LOOP
400 END DEF
410 DEF HIBA
420   SOUND
430   PRINT AT 22,1:"Hiba! "
440   LET H=H+1
450   PRINT AT 1,1:"Hiba:";H
460   WAIT 2
470 END DEF
480 DEF OK
490   SOUND PITCH 45
500   LET IDO=PEEK(1)+PEEK(0)/100
510   PRINT AT 22,1:"Ido: ";IDO;"sec "
520   LET J=J+1
530   PRINT AT 1,22:"Jo reakcio:";J
540   LET MN=MIN(IDO,MN)
550   LET MX=MAX(IDO,MX)
560   PRINT AT 2,22:"Max. ido:";MX
570   PRINT AT 3,22:"Min. ido:";MN
580 WAIT 2
590 END DEF

Az akár játékként is használható program véletlenszerűen írja ki a 40-44 ASCII kódú karakterek valamelyikét a képernyő adott helyére és egyidejűleg elindítja a stoppert is (310. és 320.). A "játékosnak" csak a 'csillag' karakterre (CHRS(42)) szabad reagálnia, de arra minél gyorsabban, bármelyik billentyű lenyomásával. Ekkor a program leállítja a stoppert, majd kiértékeli és kijelzi a reakció helyességét és idejét. A futásidőt közvetlenül a számlálóból olvassa ki (500).
A HIBA és OK rutinok a kijelzésen kívül "adminisztrálják" is az elkövetett hibák (más karakterre való reagálások) és a jó reakciók számát, a futás alatt mért maximális és minimális reflexidőt. Ezeket a részleteket tehát érdemes még egy kissé "feldíszíteni", de ezt bizonnyal az Olvasóra bízhatjuk.

2.4.5 Hangkeltés a megszakítási rutinban

Utolsó alkalmazási példaként próbáljunk meg hangot is kelteni megszakításkezelő rutinunkban. Nem tűzünk ki magunk elé túl nagy célt (pl. zenét a háttérben), annak viszont semmi akadálya nincs, hogy - az 1 Hz-es megszakítást felhasználva - másodpercenként valamilyen jellegzetes géphangot adjunk ki. Azt ne kérdezze meg persze a kedves Olvasó, hogy mire jó egy ketyegő számítógép (hacsak nem egy várt vagy hívatlan vendég meghökkentése és teljes összezavarása a cél, közvetlen hasznát nemigen tudnánk megadni). Fontos lehet ugyanakkor az itt tárgyalt módszer, ahogyan megszakításkezelőből EXOS hívást indítunk.
A PING BASIC utasítás rendkívül egyszerű végrehajtó rutinja alapján (egyetlen EXOS hívásból áll), kézenfekvő megoldás lehetne a hangot ennek a rutinnak a hívásával (vagy másolásával) kelteni:

D3BF
D3C1
D3C3
D3C5
3E 67
06 07
F7 07
C9
LD A,67
LD B,07
EXOS 07
RET
; SOUND csatorna
; irandó kód
; kiiratás

Ennél egyszerűbb feladat nincs is - gondolhatnánk! Be is írhatjuk az előző példák alapján a néhány utasításos felhasználói megszakításkezelőt - végül is nem "száll el" a gép, csak éppen semmit sem hajt végre. Pedig nem a programban van a hiba! Ha a rutint CALL USR ( ) utasítással hívjuk meg, azonnal megszólal a várt hang.
Mi az a lényeges változás a megszakítási rutinból való hívás esetén, ami miatt az EXOS nem hajtja végre a "karakterírás" funkciót (sőt, ha ellenőrizzük az akkumulátor hívás utáni tartalmát, kiderül, hogy az EXOS-ból FEH hibakóddal, .ILLFN, azaz "nem hozzáférhető EXOS hívás" hibával tért vissza).
A magyarázathoz adott minden információ, csak figyelmesen át kell néznünk az EXOS n végrehajtás menetét és a megszakításkezelést. A válasz kulcsa a funkciókódok szerinti szétválogatásban, ill. az azt közvetlenül megelőző szakaszban van:

C462
C463
C466
...
C580
...
...
C5A2
C5A4
37
CD 56 00
CD B0 C5

30 20


LD A,FE
RET
SCF
CALL 0056
CALL C580

JR NC,C5A2


LD A,FE
RET
; CY = 1
; visszatérési szakasz
; szétválogatás

; hiba, ha közben
; törlődött a CY

; .ILLFN hibakód
; végrehajtás nélkül
; hibajelzéssel vissza

A. 0056/57H címen általában egy EI:RET utasításpár áll (az előző EXOS hívás így fejezte be). Az EXOS megszakításkezelő azonban a 0056 címre egy OR A utasításkódot töltött (C4B8/AH).
Ez az utasítás törli az átvitelbitet, ha a felhasználói megszakítási rutinból adunk ki funkcióhívást és emiatt szalad a jelzett ágra a program. Megszakításkezelés során tehát nem lehet EXOS n utasítást kiadni. És ha mégis szükségünk van rá? Akkor persze lehet egy-két trükkel próbálkozni.
A megoldás első lépése egyszerű: át kell írni a bajt okozó bájtot pl. 00-ra (a NOP nem fogja törölni a CY-t). A megszakítási feladat elvégzése után persze vissza kell állítani az eredeti OR A értéket (B7H), hogy a hátralévő kezelőknek szabályos memóriabeállítást hagyjunk hátra.
Ezzel a fogással már működik az EXOS hívás, de néha egészen sajátos jelenségeket tapasztalhatunk. Ennek oka nyilván az, hogy az EXOS n szokása szerint végrehajtás közben EI-re állította a sokat emlegetett 0056-os bájtot, és visszatérés előtt végre is hajtotta a megszakításengedélyezést, a megszakításkezelés közepén). Hogy javítsunk a helyzeten, tiltsuk le újra a megszakításokat közvetlenül az EXOS hívás után. Tegyünk meg még egy lépést, hogy a megmaradt minimális kockázatot is kizárjuk. A fejezet legelején megismert fogással (a 0038H megszakítás belépési cím ideiglenes átírásával) térítsük el az esetleg mégis becsúszó megszakításokat. A leírtakat a következő assembly rutin valósítja meg lépésről lépésre:


USR_ISR


IR_1Hz
ORG 0180H
BIT 3,D
JR NZ,IR_1Hz
RET
LD A,00
LD (0056H),A
LD A,0C9H
LD (0038H),A
LD A,67H
LD B,07H
EXOS 07H
DI
LD A,0F5H
LD (0038H),A
LD A,0B7H
LD (0056H),A
RET

; 1 Hz-es megszakítás?
;
; vissza, ha nem
; "NOP"
; az EXOS ágba
; "RET"
; az IR belépésre
; SOUND csatorna
; írandó kód
; kiiratás
; INT letiltás
; "PUSH AF"
; eredeti érték
; "OR A"
; eredeti érték

Ezt a gépi kódú rutint tölti be a BASIC program, majd a megszokott módon beilleszti a megszakítási rendszerbe mint felhasználói megszakítási rutint:

100 POKE 540,128
110 POKE 541,1
120 CODE M=HEX$("CB,5A,20,01,C9")
130 CODE A=HEX$("3E,00,32,56,00,3E, C9,32,38,00,3E,67,06,07,F7,07, F3,3E,F5,32,38,00,3E,B7,32,56, 00,C9")
140 CODE B=HEX$("F3,36,80,23,36,01,FB,C9")
200 CALL USR(B,49133)

Ezzel a megoldással más EXOS funkcióhívásokat is kiadhatunk (pl. karakterírás a képernyőre stb.).
Befejezésül még egy nagyon rövid példa arra, hogy nem mindig érdemes erőltetni az EXOS-on keresztüli hívást. Ha az előző feladatban csak a másodpercenkénti hangkeltés a cél, praktikusabb lehet a közvetlen hangkeltés. Hogy tényleg rövid legyen a példa, használjuk a billentyűzetkezelő TICK-hangot keltő rutinját (0. szegmens ECBAH). Ha az ECBCH címre lépünk be, és az A regiszternek különböző értékeket adunk, még kísérletezhetünk is a hanghatásokkal. A végrehajtandó feladat nagyon egyszerű: A-nak értéket adunk és meghívjuk a ROM-rutint. A program adataival egy óraketyegés jellegű hang szólal meg minden másodpercben. Próbálja visszafejteni a gépi kódú rutint és keresse meg azt a listaelemet, amelynek átírásával megváltoztatható a hang jellege!

100 POKE 540,1
110 POKE 541,1
120 CODE =HEX$("CB,5A,20,01,C9,3E, 48,CD,BC,EC,C9")
130 CODE MB=HEX$("F3,36,80,23,36,01,FB,C9")
140 CALL USR(MB,49133)

2.5 Perifériakezelők

Mint azt már láttuk, a számítógép és környezete között kapcsolatot teremtó eszközök (pl. billentyűzet, képernyő, magnetofon, stb.) kezelőprogramjait egységes leíróik alapján tartja nyilván és működteti az operációs rendszer. Ezek a leírók láncot alkotnak a memóriában; ez a lánc rögzített címről indul és minden elem tartalmazza a következő leíró címadatait.
A 2.3.1 pontban a rendszer felépülésének részeként elemeztük, hogy miként alakítja ki az EXOS ezt a periférialáncot és hogyan keres meg benne egy-egy leírót (a LANC programmal ezt magunk is végigkövettük). Tudjuk tehát, hogy a periférialánc báziscíme (a rendszerszegmensben) BFC0H, ez alatt található az első leíró pointere: a leírót tartalmazó szegmens száma (báziscím - 1) és tárolási címe (báziscím - 2: felső bájt; báziscím - 3: alsó bájt). A tárolási cím egyben báziscím is: alatta ugyanilyen módon a következő láncelem pointerét találjuk. A lánc végét a (báziscím - 1) = 0 érték jelzi.
Hogyan kerülhet egy eszközleíró a láncba?
Egyrészt az inicializálás folyamatában automatikusan, ha a kezelő egy ROM-szegmens bizonyos formai követelménynek eleget tévő része. Ekkor az EXOS a ROM-ból a rendszerszegmensbe másolja a leírót alkotó bájtokat.
A másik mód, hogy egy felhasználói program kéri az általa (RAM-területen) szabványosan kialakított leíró nyilvántartásba vételét (egy EXOS 21 funkcióhívással).
A két lehetőség közötti különbség elsősorban a leíróhoz esetleg igényelt rendszer-RAM igénylési és kiutalási módjában van. A másik lényeges különbség a funkcionálisan egyébként gyakorlatilag azonos leírók között, hogy egy, a periférialánc törlésével járó rendszerinicializálás után (pl. ha a BASIC-ből a szövegszerkesztőbe lépünk) csak a ROM-leírók lánca épül automatikusan újra. Ez fontos lehet akkor, ha pl. egy szövegszerkesztőből is használni kívánt különleges printer-kezelőt hozunk létre. Számunkra elsősorban a második mód adott a saját kezelő beillesztésére, de (éppen az előbbiekben tárgyalt szempont miatt) keressük a módot az első lehetőség kihasználására is.
A saját perifériakezelő beillesztéséhez mindenképpen nélkülözhetetlen a leíró ismerete. A periférialánc egy-egy eleme a következő formájú:

A báziscím előtti pointer 3 bájtját az EXOS írja be a -beláncoláskor, a leíró további elemeit viszont nekünk kell előkészítenünk.
Az eszközkezelők másik szabványos része a végrehajtó rutinok belépési címeit tartalmazó táblázat (amelyre a leíró TAB pointere mutat). Ezeket a rutinokat az EXOS hívja az illető perifériával kapcsolatos csatornaműveletek vagy más feladatok végrehajtásához.
A híváskor bizonyos paramétereket átad a kezelőnek, mint azt a 2.3.2 pontban (csatornaműveletek előkészítése) láttuk. Az IY regiszterpár a leíró címét tartalmazza (a 2. LAP tartományában), B'-ben van a leírót tartalmazó szegmens száma. Csatornaműveleteknél IX a pufferterület fölé mutat, A-ban a belső csatornaszám (hívott csatornaszám + 1) található.
A végrehajtó rutinok belépési pontjainak táblázata már 3. LAP-címként adja meg a címek alsó és felső bájtjait (hiszen a kezelő - mint minden más rendszerprogram is - a 3. LAP-on fut majd). Tekintsük át röviden a táblázat elemeinek funkcióit (a beépített kezelők példáin utalva az elvégezhető vagy elvárt feladatokra)!

A megismert leírót és belépési címtáblázatot kell tehát elkészítenünk, mielőtt saját kezelőnk beláncolását kérnénk. Az EXOS 21 funkcióhívás előtt DE-t a leíró TYPE elemének címére kell állítanunk, míg BC-ben megadhatjuk a rendszerszegmensben esetleg kért RAM-puffer nagyságát. A hívás során az EXOS érintetlenül hagyja a leírót abban a szegmensben, amelyikben felépítettük, csak beilleszti a periférialáncba. Ehhez viszont fontos, hogy a TYPE elem előtt még legyen 3 szabad RAM-hely (a következő láncelem pointerének beírására). Az így beláncolt kezelő csak egy újrainicializálás során törlődik (l. EXOS 0).

2.5.1 Nyomtatás saját karakterkészlettel

Építsünk fel egy olyan nyomtatókezelőt (programrendszert és leírót), amely a nyomtató grafikus üzemmódjának felhasználásával a kiírt szöveget az ENTERPRISE karakterkészletével jeleníti meg. Ha ezzel egyszerűen nyomtathatóvá tesszük a normál karakterkészleten túl a saját definiálású karaktereket is (ékezetes betűk, matematikai jelek, idegen karakterkészletek stb.), az egyszerű illusztráción túl jól használható lehetőséghez is jutunk. Először gondoljuk végig, milyen feladatokat kell ellátnia a kezelő 13 funkciórutinjának. A választott periféria eléggé egyszerű ahhoz, hogy a funkciók egy részénél (megszakítás (0), csatornalezárás (3) és megszüntetés (4), inicializálás (12) és puffermozgatás (13)) ne kelljen tényleges műveletet elvégezni, itt csak az A = 0 státuszt állítjuk be ("OK").
A funkciók másik csoportjának hívása az adott eszköznél hibának számít. A karakterolvasás (5), blokkolvasás (6), a csatornakészenlét olvasása (9) és a csatornaállapot beállítása (10) funkciókra ".NOFN" hibakóddal válaszolunk, ("HIBA"), az esetünkben nem kezelt speciális funkcióhívásból pedig A = EAH (".ISPEC") hibakóddal térünk vissza.
Nem törvényszerű, de nagymértékben leegyszerűsíti a kezelést, ha nem végezzük el a blokkírás funkciót sem (ezzel lényegében csak a "LIST £csat." lehetőséget veszítjük el), de ez a probléma is megkerülhető (COPY). Szükség esetén a beépített nyomtatókezelő 6. rutinjának (l. 5. Függelék) elemzésével kiegészíthető a saját kezelő is.
Érdemi funkciót tehát csak a csatornanyitás (1), ill. - létrehozás (2) és természetesen a karakterírás (7) hívásokra kell ellátnunk.
A csatornanyitáshoz mindössze egy EXOS 27 hívást adunk ki DE = 0000-val, hiszen nem igénylünk csatornapuffert (a kivitelhez szükséges pufferterület a saját RAM-szegmensben lehet).
A karakterírás funkciót alaposabban át kell gondolni. Az alapműveletet (egy bájt kivitele a nyomtatóra) nyugodtan elleshetjük a belső nyomtatókezelőtől, ennél jobban úgysem tudnánk megoldani (ez a felépített assembly program BYTE-KI rutinja).
Ez a rutin azonban a mi esetünkben csak végrehajtó alprogramként használható. Kivételt a nyomtatót vezérlő karakterek jelentenek: a kocsivissza (CR) vagy soremelés (LF) kódokat természetesen nem kell grafikusra alakítani. Kezelőnk a 32 alatti írandó kódok számára átlátszó, azokat közvetlenül a BYTE-KI rutinnak adja át.
Az írható karakterek esetén viszont a karakter kódja helyett az ENTERPRISE karakter-RAM (rendszerszegmens, B480H kezdőcím) adatai alapján meghatározott bájtsorozatot kell a nyomtatóra küldeni. A grafikus üzemmód az EPSON (és velük kompatibilis) nyomtatóknál egy vezérlőkarakter-sorozattal váltható ki. Az alapfelbontáshoz (ESC"K"): 1BH, 4BH, LO, HI
A LO és HI adatok a grafikusként értelmezendő bájtok számát adják meg (LO: alsó bájt, HI: felső bájt, max. érték: 480).
Esetünkben vigyük ki a grafikus adatokat karakterenként. Ez 8 adatbájtot jelent: LO = 8, HI = 0. A vezérlőkódokat követő bájtok egy-egy grafikus pontoszlop adatait adják a nyomtatónak: a legfelső bit (128-as helyi érték) a legfelső pontot jelenti.
A karaktergenerátor adatai viszont a megjelenítendő pontmátrix sorait adják. Az első sor adatának címe: a karakter-RAM kezdete + a karakter kódja. A következő sorok adatai 128-asával növekvő címeken találhatók.
A soradatok oszlopadatokká alakítása kezelőnk feladata. Ehhez a rutin egy 8-bájtos adatpuffert használ (ami az átalakítás után egyben a grafikus kivitel adatmezője is). A konverzió kulcsa a balra görgető RL utasítás: a sorban megjelenítendő biteket tartalmazó adatot ciklikusan balra léptetjük és a CY-be kicsordult bitet a puffer egymás utáni bájtjaiba léptetjük be. A 8 bit 8 oszlopbájtba kerül és ahogy egyre újabb soradatokat konvertálunk, egyre feljebb lépnek, míg végül kialakul a 8 adatbájt. Itt jegyezzük meg, hogy az ENTERPRISE karaktermátrixai 9 pontsorból állnak, de a nyomtatóknak csak 8 tűje vezérelhető az adat 8 bitjével. Programunk a felső 8 pontot jeleníti meg, de - a karakter-RAM kezdőcímének eltolásával - átállhatunk az alsó 8 bitre is.
Ezek után felépíthetjük a programrendszert a felsorolt feladatoknak megfelelően:


OK


HIBA


IRQ

OPEN





OPEN2
CLOSE
CLOSE2
OLV
BLOKK_OLV
BLOKKIRAS
ALL_OLV
ALL_IRAS

SPEC


INIC
PUF_MOZG

IRAS










C1



C2







ORG 0C000H
XOR A
RET

LD A,0E7H
RET

EQU OK

XOR A
LD D,A
LD E,A
EXOS 27
RET

EQU OPEN
EQU OK
EQU OK
EQU HIBA
EQU HIBA
EQU HIBA
EQU HIBA
EQU HIBA

LD A,0EAH
RET

EQU OK
EQU OK

LD A,B
CP 20H
JP C,BYTE_KI

LD E,B
XOR A
LD D,A
LD HL,PUFFER+4
LD IY,0B480H
ADD IY,DE
LD B,8
PUSH BC
LD A,(IY+0)
LD B,8
LD HL,PUFFER+4
RLA
RL (HL)
INC HL
DJNZ C2
LD BC,0080H
ADD IY,BC
POP BC
DJNZ C1

; A=0 (nincs hiba)


; ".NOFN" hibakód




; A=0
; DE=0000
; nincs pufferigény
; kötelező hívás











; ".ISPEC" hibakód





; írandó karakter
; alfanumerikus?
; azonnal kiviszi,
; ha vezérlőkarakter
; E: írandó karakter kódja
; A=0
; D=0
; adatpuffer
; karakter_RAM kezdőcím
; +00nn: írandó karakter
; 8 pontsort olvas
; számláló verembe
; pontmátrix adat
; 8 bitet figyel
; adatpuffer
; MSB -> CY
; CY -> (HL)
; adatpuffer köv. cím
; következő bit kivitele
; következő mátrixadat
; címe
; sorszámláló vissza
PUF_KI

C3

LD HL,PUFFER
LD B,12
PUSH BC
LD B,(HL)
CALL BYTE_KI
INC HL
POP BC
DJNZ C3
XOR A
RET
; puffercím
; 4 vezérlőkar. + 8 adat
; számláló verembe
; kiírandó byte
; nyomtatóra
; következő puffercím
; számláló vissza
; a következő adatbyte-ra
; A=0 (".OK")
BYTE_KI

C4
LD A,B
OUT (0B6H),A
LD A,(0BFF2H)
CP 20H
LD A,0E5H
RET Z
; kiírandó byte
; PORT-ra
; FLAG_SOFT_IRQ
; volt STOP?
; ".STOP" hibakód












LEIRO
TYPE
IN A,(0B6H)
BIT 3,A
JR NZ,C4

LD A,(0BFF3H)
OR 10H
OUT (0B5H),A
AND 0EFH
OUT (0B5H),A
XOR A
RET

DEFB 0,0,0
DEFB 0,0,0
DEFW TAB-8000H
DEFB 0FBH,0
DEFB 11,"GRAFPRINTER"
; PORT adat
; "READY?"
; vissza, ha a PRINTER
; nem kész
; PORTB5
; STROBE=1
; kivitel
; STROBE=0
; kivitel
; ".OK"


; 3 hely a láncoláshoz
; TYPE, ORQFLAG, FLAGS
; TAB_LO,TAB_HI
; TAB_SEG, UNIT_COUNT
; a belépési pontok táblázata
TAB DEFW IRQ
DEFW OPEN
DEFW OPEN2
DEFW CLOSE
DEFW CLOSE2
DEFW OLV
DEFW BLOKK_OLV
DEFW IRAS
DEFW BLOKKIRAS
DEFW ALL_OLV
DEFW ALL_IRAS
DEFW SPEC
DEFW INIC
DEFW PUF_MOZG

DEFB 1BH,4BH,8,0,0,0,0,0,0,0,0,0

A rutinok után kialakított leíró elemei is a fenti logikának felelnek meg:

A leíró TAB elemeivel foglalkozzunk egy kicsit részletesebben. A rutinokat és táblázatokat az 1. fejezetben említett ASMON assemblerében írtuk meg. Az ORG C000H utasítás miatt az ASMON úgy fordítja le az assembly programot, hogy a címkéket a 3. LAP C000H címétől számítja (OK = C000H, HIBA = C002H stb.). Így a belépési cím táblázatba helyesen, 3. LAP-címként kerülnek az értékek. A leíró TAB elemének azonban az 1. LAP-on kell megadnia a táblázat kezdőcímét; ezt szolgálja a -8000H eltolás.
A táblázat szegmense (a fenti listában FBH) természetesen mindig az a szegmens lesz, amelyet valamilyen módon lefoglaltunk és amelybe ténylegesen betöltöttük a kezelőt.
Ha ténylegesen a memóriába akarjuk tölteni a kezelő rutinjait és táblázatait, ügyelni kell arra, hogy ezt nem végezhetjük el a 3. LAP-on (ott maga az ASMON fut). Kénytelenek vagyunk pl. 1. LAP-ra lapozni a kiválasztott szegmenst és 8000H MEMORY OFFSET értékkel végeztetni a fordítást.
Szokás szerint itt is megadjuk a BASIC betöltőprogramot:

100 PROGRAM "Grafprin.bas"
110 ALLOCATE 100
120 CODE SZEGMENS=HEX$("F7,18,79,D3,B1,C9")
130 CODE BELANCOLAS=HEX$("11,6A,40,01,00,00,F7,15,C9")
140 CALL USR(SZEGMENS,0)
150 POKE 540,0:POKE 541,64
160 CODE OK=HEX$("AF,C9")
170 CODE HIBA=HEX$("3E,E7,C9")
180 CODE OPEN=HEX$("AF,57,5F,F7,1B,C9")
190 CODE SPEC=HEX$("3E,EA,C9")
200 CODE IRAS=HEX$("78,FE,20,DA,49,C0,58,AF,57,21,9D,C0, FD,21,80,B4,FD,19,06,08,C5,FD,7E,00,06,08,21,9D, C0,17,CB,16,23,10,FA,01,80,00,FD,09,C1,10,E9")
210 CODE PUF_KI=HEX$("21,99,C0,06,0C,C5,46,CD,49,C0,23, C1,10,F7,AF,C9")
220 CODE BYTE_KI=HEX$("78,D3,B6,3A,F2,BF,FE,20,3E,E5,C8, DB,B6,CB,5F,20,F2,3A,F3,BF,F6,10,D3,B5,E6,EF,D3, B5,AF,C9")
230 CODE LEIRO=HEX$("00,00,00")
240 CODE TYPE=HEX$("00,00,00,7D,40")
250 CODE =CHR$(IN(177))
260 CODE =HEX$("00,0B")&"GRAFPRINTER"
270 CODE TAB=HEX$("00,C0,05,C0,05,C0,00,C0,00,C0,02,C0, 02,C0,0E,C0,02,C0,02,C0,02,C0,0B,C0,00,C0,00,C0")
280 CODE PUFFER=HEX$("1B,4B,08,00")
290 CALL USR(BELANCOLAS,0)

A program itt lényegesen többet végez el a tulajdonképpeni betöltésnél:

EXOS 24
LD A,C
OUT(B1H),A
RET
F7 18
79
D3 B1
C9
LD DE,406AH
LD BC,000H
EXOS 21
RET
11 6A 40
01 00 00
F7 15
C9

Itt DE a leíró TYPE elemének tárolási címe, BC = 0, mert nem igénylünk perifériapuffert.
A program lefuttatása után létezik a GRAFPRINTER nevű periféria, megnyithatunk hozzá egy csatornát:

OPEN £1:"GRAFPRINTER:"

utasítással, amit a szokásos módon használhatunk numerikus vagy szöveges adatok kiírására, a szöveges képernyő (pontosabban az EDITOR) COPY-zására stb:

PRINT £1 :"Pi = ";PI

COPY FROM £0 TO £1

(Az alternatív karakterkészlet legfelső sorának kinyomtatása a karakter-RAM címének előbbre állításával érhető el: LD IY,B400H).
Befejezésül két megjegyzés.

2.5.2 Nyomtatóvezérlés a szövegszerkesztőből

Az ENTERPRISE beépített szövegszerkesztőjének egyik legnagyobb hiányossága, hogy nem küldhetünk a nyomtatóra vezérlőkaraktereket, így nem használhatjuk ki a nyomtató lehetőségeit, írásmódjait (betűtípus választás, aláhúzás, dőlt betű, vastagítás stb.).
Aki az előző pontot áttekintette, valószínűleg érzi: semmi akadálya, hogy a beépített PRINTER-kezelőt felváltsuk egy olyan kezelővel, amely a karakterkódok adott tartományában (pl. 127 fölött) a kód helyett egy belső táblázat vezérlőkaraktereit küldené a nyomtatóra. Nem is a kezelő létrehozása a gond, hanem az, hogy az előző fejezet módszerével beláncolt perifériakezelő törlődik, ha a szövegszerkesztőbe lépünk.
Az inicializálással járó átlépésnél csak a ROM-szegmensek beépített kezelőiből épül újra a periférialánc automatikusan. Nem kell azonban feladnunk az ötletet, ha felidézzük, hogy a 2.3.2 pontban 0 az EXOS 0 alkalmazásaként 0 sikerült (az operációs rendszer megtévesztésével) egy tetszőleges RAM-szegmenst felvétetni a ROM-szegmensek nyilvántartásába. Ezt a szegmenst a továbbiakban teljes értékű ROM-szegmensként kezeli a rendszer; a benne kialakított kezelő ezután beépítettnek számít.
A beépített perifériakezelők beláncolása ugyancsak leíróik alapján történik, de ezek a belső leírók (álleírók) néhány pontban különböznek az előbbiekben megismertektől. A tulajdonképpeni leírón kívül az átmásolásukhoz, megkeresésükhöz szükséges információkat is tartalmaznak:

A szegmens első kezelőjének címét a 8/9. bájton kell letárolni (ugyancsak 1. LAP címként). Itt indul a kezelőlánc keresése az inicializálás során.
Az igényelt RAM-területet ebben az esetben közvetlenül a leíró alatt foglalja le az EXOS, tehát ha a kezelőnkhöz fordul a rendszer, a puffért egyszerűen, az IY-1, IY-2 stb. címeken érhetjük el.
A másik sajátosság a ROM-szegmensek kezelőinek beláncolásakor, hogy az EXOS a TAB-SEG elemhez nem veszi figyelembe az álleíró adatát, ide mindenképpen azt a szegmensszámot írja, ahol a kezelőt megtalálta.
Ezután a fentiek és az előző pont alapján könnyen felépíthetjük nyomtatókezelőnket (pl. NYOMTATO néven):


MUTAT







NAME
SIZE
ORG 0C008H
DEFW SIZE-8000H

ORG 0C010H
DEFB 0,0
DEFB 0FEH,0FFH
DEFB 0,0,0
DEFW TAB-8000H
DEFB 0,0
DEFB 8,"NYOMTATO"
DEFB 0FH




; NEXT: lánc vége
; nincs RAM-igény
; TYPE, IRQFLAG, FLAGS
; TAN_LO; TAB_HI
; TAB_SEG, UNIT_COUNT
; periférianév
; 15 byte a TYPE-ig
  ; Belépési pontok táblázatai
TAB DEFW IRQ
DEFW OPEN
DEFW OPEN2
DEFW CLOSE
DEFW CLOSE2
DEFW OLV
DEFW BLOKK_OLV
DEFW IRAS
DEFW BLOKKIRAS
DEFW ALL_OLV
DEFW ALL_IRAS
DEFW SPEC
DEFW INIC
DEFW PUF_MOZG
 

OK

HIBA


IRQ
OPEN




OPEN2
CLOSE
CLOSE2
OLV
BLOKK_OLV
BLOKKIRAS
ALL_OLV
ALL_IRAS
SPEC

INIC
PUF_MOZG

XOR A
RET
LD A,0E7H
RET

EQU OK
XOR A
LD D,A
LD E,A
EXOS 27
RET
EQU OPEN
EQU OK
EQU CLOSE
EQU HIBA
EQU HIBA
EQU HIBA
EQU HIBA
EQU HIBA
LD A,0EAH
RET
EQU OK
EQU OK
 
IRAS LD A,B
CP 80H
JP C,BYTE_KI
SUB 80H
LD C,A
LD HL,TABLA
LD D,0
INC C
; írandó karakter
; A<128?
; kiírásra, ha igen
; vezérlőkód sorszám
; C = csatornaszám
; HL = tábla kezdőcíme
C1 DEC C
JR Z,C2

LD E,(HL)
INC E
ADD HL,DE
JR C1
; sorszám - 1
; kiolvasásra, ha elértük a
; keresett elemet
C2
C3
LD B,(HL)
INC HL
LD A,(HL)
CALL BYTE_KI
DJNZ C3
RET
; hány byte-ot vigyen ki
; a következő táblacím
; adat a táblából
; kiírás
; ismétlés B-szer
BYTE_KI
C4
OUT (0B6H),A
LD A,(0BFF2H)
CP 20H
LD A,0E5H
RET Z
IN A,(0B6H)
BIT 3,A
JR NZ,C4
LD A,(0BFF3H)
OR 10H
OUT (0B5H),A
AND 0EFH
OUT(0B5H),A
XOR A
RET
 
  ; A vezérlőkarakterek táblázata:

TABLA
ORG 0C100H
DEFB 1,15
DEFB 1,18
DEFB 2,27,52
DEFB 2,27,53
DEFB 2,27,77
DEFB 2,27,80
DEFB 2,27,69
DEFB 2,27,70
DEFB 2,27,71
DEFB 2,27,72
DEFB 3,27,87,1
DEFB 3,27,87,0
DEFB 3,27,45,1
DEFB 3,27,45,0
...

; CONDENSED típus
; COND. törlés
; dőlt
; dőlt vissza
; ELIT
; PICA
; fett
; fett vissza
; dupla
; dupla vissza
; széles
; széles vissza
; aláhúzott
; aláhúzott vissza
  (igény szerint tölthető fel)

A formai eltéréseken kívül az IRAS funkcióban változtattunk lényegesen a kezelőn:

A vezérlőkarakterek táblázatát saját igényeink szerint alakíthatjuk ki. Az első bájt a következő vezérlőkarakterek száma legyen.
Az így létrehozott kezelő már a szövegszerkesztőből is használható. Mivel a rendszer alapértelmezésben a PRINTER-kezelőt használja, a nyomtatóutasítás (F3) után a "Press ENTER for parallel printer or type device-name:", ill. "Falls Parallel-Drucker, so ENTER. Sonst Vorsichtungsnamen eingeben:" kérdésre nekünk kell megadnunk a használandó kezelő nevét: NYOMTATO:.
Példánkban nem foglalkoztunk a felhasznált szegmens lefoglalásával. A szegmensigénylés (EXOS 24) egyszerű módja itt nem jelent megoldást, mert az inicializálással járó átlépés (WP) törli a felhasználói szegmeskiutalást is. Ha olyan nagy fájlokkal dolgozunk, hogy felmerülhet a memóriavédelem igénye, perifériának kiutalt szegmensként kell lefoglalnunk a munkaterületet, amit akár közvetlen beavatkozással is megvalósíthatunk a nyilvántartási logika alapján (l. EXOS 24 és EXOS 25).

2.6 Rendszerbővítők

A nagyobb, valamilyen rendszert alkotó programok rendszerbővítőként kapcsolódnak az ENTERPRISE operációs rendszeréhez. Ilyennek tekinthető az alapkiépítésű gépben a modulban csatlakoztatott BASIC, de ilyen pl. a külső adathordozóról beolvasott PASCAL is. Ezek a rendszerek ténylegesen az EXOS bővítései: az operációs rendszer szolgáltatásainak körét szélesítik ki. Az EXOS a nyilvántartásba vett bővítőket sok esetben körbekérdezi (ilyen hívás váltható ki pl. BASIC-ben a :HELP vagy :WP Paranccsal), lehetőséget adva üzenetek kiírására, az EXOS hibák egyéni hibaüzeneteinek megadására vagy akár a vezérlés átvételére. Saját programunkat (programrendszerünket) is így tudjuk a legszorosabban csatolni a rendszerhez; az egyszerű hívási, aktivizálási lehetőség mellett mi magunk is így kaphatjuk a legtöbb szolgáltatást a rendszertől.
A rendszerbővítők két fő típusba sorelhatók.

Foglaljuk össze a rendszerbővítők kialakításához szükséges információkat (a hívások módját és eseteit már láttuk a 2.3.1 és 2.3.2 pontban).
A ROM-bővítőket és az ún.- abszolút rendszerbővítőket a 3. LAP-on, a C00AH belépési címen hívja az EXOS (a közvetlen beláncolással más kezdőcím is letárolható).
A rendszer 8 különböző esetben (8 különböző céllal) hívja a bővítőket C = 1,2...8 akciókóddal. Ezek egy részét (C = 2,3 esetleg C = 1) a rendszerbővítők maguk is kérhetik egy EXOS 26 funkcióhívással. A hívásokra nem kötelező válaszolni (a C nem kezelt értékeinél egy RET-re léphetünk), ha pedig elvégeztük a kért feladatot, ezt C = 0 állítással kell jeleznünk a rendszer számára (ekkor a körbekérdezés leáll). Ilyenkor A-ban is be kell állítanunk a hibakódot (alapesetben 0).

Ezek a regiszterek megőrzendőek, ha tovább adjuk a hívást az EXOS-nak. A többi regiszter tetszés szerint használható.

Az akciókódok
A továbbiakban a puszta felsoroláson túl utalunk a hívási körülményekre, a beépített egységek által ellátott feladatokra is.

C = 1: Hidegindítás
Az EXOS akkor adja körbe, amikor a vezérlés átvételét várja (pl. a bejelentkezés - villogó ENTERPRISE felirat - után). Először, mint a többi funkciónál is, a RAM-bővítőket hívja, majd sorra a ROM-okat (5., 4., 1. sorrendben). A felhasználói bővítőnek lesz tehát először lehetősége átvenni a vezérlést. Alapkiépítésben az 5. (BASIC), és az 1. szegmens (WP) fogadja a hívást (az utóbbihoz természetesen csak akkor jut el, ha nincs cartridge a gépben).

C = 2: Parancsfüzér
Az előbbi hívás célzott változata: egy adott szó (név) felismerésekor kell átvenni a vezérlést vagy elvégezni a funkciót. Ilyen hívás váltható ki pl. a BASIC ":UK" paranccsal. A bővítőbe lépéskor DE az azonosítandó szöveg előtti hosszbájtra mutat, B pedig az első szó hosszát adja meg. A beépített funkciók:
5. szegmens:
BASIC - a BASIC értelmező veszi át a vezérlést
4. szegmens:
VDUMP - a grafikus képernyő kivitele nyomtatóra,
VSAVE - a grafikus képernyő tárolása,
VLOAD - a grafikus képernyő beolvasása,
(ez utóbbi két funkció hibás, nem működik a forgalomban lévő gépeken)
BRD - német mód
(a karakterkészlet, a billentyűkezelés, a hibaüzenetek németesítése)
UK - angol mód
A fentieken kívül a szegmens tartalmazza még a BASICX funkciót is, ami a BASIC funkcióhívások német kiterjesztését végzi (ez közvetlen hívásnál hibajelzést vált ki, mert nem állít C = 0-t) valamint a "KRAM"&CHR$(255) funkciót a billentyű-RAM inicializálására.
1. szegmens:

WP - a szövegszerkesztő veszi át a vezérlést.

C = 3: HELP - füzér
Az EXOS a bővítőben kezelt funkciók listájának megadását várja. BASIC-ből a :HELP paranccsal aktivizálható. Ha a HELP után is következik szöveg, ennek címét az előző pont szerint adja meg a rendszer. Ilyenkor csak az adott név felismerése esetén kell (esetleg részletesebb) információt kiírni. A segítő szöveg a 255. csatornára írható, praktikusan a blokkírás EXOS hívással. Alapkiépítésű gép általános HELP-listája az előbbi szolgáltatásokat tartalmazza, és további copyright jellegű üzenetet ír ki a BASIC,WP és az ISL1985 füzérekre.

C = 4: EXOS változó írása, olvasása, átbillentése
A hívást a rendszer az EXOS 16 funkcióhívás kiterjesztéseként hívja meg 39-nél nagyobb változószám esetén. A ROM-bővítők közül a 4. szegmens kezel a német verzió belapozottságával kapcsolatos jelzőfunkciókra két új EXOS változót (90H és 91H).

C = 5: Hibakódok magyarázata
Ez EXOS funkcióhívások utáni hibakód (A) értelmezésére hívott rutin. A bővítő a hibakódot B-ben kapja meg. Ha hibaüzenetet ad, DE-ben az üzenet címét, B-ben az üzenetet tartalmazó szegmens számát kell visszaadnia és C = 0-t kell állítania.
Az alapértelmezésű gépben először a 4. szegmens kap szót és a kódok egy részére német nyelvű üzenetet ad, ha belapozott a német verzió (egyébként az 1. szegmenshez jut a hívás, ami angol magyarázatokat küld). Ha saját bővítőt illesztünk a rendszerhez, az a ROM-szegmensek előtt kapja meg a hívást, így lehetőségünk van pl. magyar nyelvű hibaüzeneteket adni.

C = 6: Modul betöltése
Akkor hívja meg az EXOS, ha maga nem ismeri fel a beolvasott modulfejrészt. A ROM-bővítők egyike sem válaszol erre a hívásra.

C = 7: RAM-igénylés
Ezt a hívást a hidegindítás során kapják meg a ROM-bővítők, hogy RAM-puffert kérhessenek a rendszerszegmensben. Az alapkiépítésű gépben csak a 4. szegmens kér pufferkiutalást a billentyűzetkezeléshez. Ezt a 2.3.1 pontban tárgyaltuk.

C = 8: Inicializálás
A hidegindítás és az EXOS 0 inicializálás során kapja ezt a hívást minden bővítő. Itt végezheti el a hozzá tartozó rendszer egyértelmű alaphelyzetbe állítását. A ROM-szegmensek közül itt is csak a 4. szegmens reagál a hívásra a német karakterkészlet, stb. kialakításával.

Rendszerbővítő kialakítása
Az elmondottak gyakorlati bemutatására készítsünk egy egyszerű bővítőt és illesszük a rendszerbe. Illusztrációnak szánt kiterjesztésünk feladata legyen pl. ez EXOS hibakódok magyar nyelvű értelmezése és az ezzel kapcsolatos alapműveletek elvégzése.
Bővítőnket a fentieknek megfelelően a szegmens C00AH címén (a 3. LAP tartományában C00AH belépési címmel) kezdjük el. Mint az assembly listában követhető, a vezérlés a C (akciókód) különböző értékeinél különböző programpontokra kerül. Itt most nem minden végrehajtó rutinban történik érdemleges művelet. Bővítőnk nem veszi át a vezérlést (nem kíván aktuális program lenni) így a hidegindítás kódjára (1) csak egy RET-tel válaszolunk. Ugyanígy azonnal visszatérünk az "EXOS változó"(4), "Modul betöltése"(6) és "RAM-igénylés"(7) hívásokból is (természetesen a bővítő ezeken a pontokon is igény szerint kiterjeszthető).
A ténylegesen ellátott funkciók a következők:

A bővítő összeállítása és a rutinok működése a megjegyzésekkel ellátott assembly listán követhető. Kiegészítésként csak annyit jegyzünk meg, hogy a HP-TEXT a BRD, ill. UK módot a BASIC RST 10H rutin különbözőképpen betöltött részletéről ismeri fel.
A lista végén helyeztük el az üzenettáblát (ill. annak csak önkényesen kiválasztott két sorát). Egy-egy elem szerkezete: EXOS hibakód, karakterek száma, szöveg. A tábla természetesen tetszés szerint folytatható, módosítható.




















FLAG

HIDEG

PARANCS





VEGE_OK

 



VEGE



HELP

ORG 0C00AH
LD A,C
DEC A
JR Z,HIDEG
DEC A
JR Z,PARANCS
DEC A
JR Z,HELP
DEC A
JR Z,VALTOZO
DEC A
JR Z,HIBA
DEC A
JR Z,MODUL
DEC A
JR Z,RAM
DEC A
JR Z,INIC
RET
DEFB 0

RET

PUSH DE
PUSH BC
CALL AZONOS?
JR NZ,VEGE
XOR A
LD (FLAG),A
POP BC
POP DE
LD C,0
XOR A
RET

POP BC
POP DE
RET

PUSH DE
PUSH BC
LD A,B
OR A
JR NZ,HP_TEXT

LD A,255
LD DE,SZOVEG
LD BC,S_HOSSZ
EXOS 8
JR VEGE

; belépési cím
;akciókód
;C=1 volt?
;-> hidegindítás
;C=2?
;-> parancsfüzér
;C=3?
;-> HELP-füzér
;C=4?
;-> EXOS változó
;C=5?
;-> hibakódok magyarázata
;C=6?
;-> modul betöltése
;C=7?
;->RAM-igénylés
;C=8?
;-> inicializálás
; vissza, ha egyik sem
;mod-flag (FLAG=0, ha HUN)

;nincs funkció

;regiszterek
; verembe
;a füzér="HUN"?
;vissza, ha nem
;HUN jelzőbyte
; a flag-be
;regiszterek
; vissza
;"akció elvégezve"
;"hiba nincs"


;regiszterek
; vissza
;továbbadja a hívast

;regiszterek
; verembe
;A=fűzér hossza
;A=0? (":HELP"?)
;további vizsgálatra, ha
; a füzér folytatódik
;aktuális csatorna
;kiírandó szöveg kezdőcíme
; és hossza
;blokkíras
;befejezésre ugrik


VALTOZO

RET

;nincs funkció
HIBA






HI1
LD A,(FLAG)
OR A
RET NZ
PUSH DE
PUSH BC
LD DE,0
LD HL,TABLA-1
INC HL
ADD HL,DE
LD A,(HL)
OR A
JP Z,VEGE
INC HL
LD E,(HL)
CP B
JR NZ,HI1
POP BC
POP DE
LD C,0
IN A,(0B3H)
LD B,A
EX DE,HL
RET
;mód-flag
;A=0? (HUN?)
;nem kezeli, ha nem HUN mód
;regiszterek
; verembe
;eltolt
;üzenettábla kezdőcíme-1
;üzenet első byte
;a következő üzenet címe
;EXOS hibakód
;A=0?
;tábla vége, ha igen
;üzenethossz címe
;E=üzenet hossza
;ez a keresett kód?
;a következő üzenetre, ha nem
;regiszterek
; vissza
;"akció elvégezve"
;saját szegmensszám
;B-be (az üzenet szegmense)
;DE: az üzenet kezdőcíme

MODUL

RAM

INIC



AZONOS?


A1







HP_TEXT















KIIRAS

RET

RET

LD A,255
LD (FLAG),A
RET

INC DE
LD HL,NEV
LD B,N_HOSSZ
LD A,(DE)
CP (HL)
RET NZ
INC DE
INC HL
DJNZ A1
RET

CALL AZONOS?
JR NZ,VEGE
LD A,255
LD DE,UZENET1
LD BC,U_HOSSZ
EXOS 8
LD A,(FLAG)
OR A
LD DE,.HUN
JR Z,KIIRAS

LD A,(00B8H)
CP 3EH
LD DE,.UK
JR Z,KIIRAS
LD DE,.BRD
LD BC,6
LD A,255
EXOS 8
JP VEGE_OK

;nincs funkció

;nincs funkció

;"Magyar mód törölve"
;kód a jelzőbyte-ba


;füzér első karakter címe
;bővítőnév címe
;bővítőnév hossza
;füzérkarakter
;=névkarakter?
;"nem azonos", ha eltérnek
;következő
; karakterek címe
;végigvizsgál


;(":HELP HUN"?)
;vissza, ha nem
;aktuális csatorna
;üzenet első rész címe
; és hossza
;blokkíras
;mod-flag
;A=0?
;" HUN" szövegre
;ha HUN mód
; (egyébként BRD vagy UK)
;RST 10H részlet
;csak UK módban ilyen
;" UK" szövegre
;ha UK mód
;egyébként " BRD" SZOVEG
;hossz
;aktuális csatorna száma
;blokkíras
;a befejezésre ugrik

SZOVEG
S_HOSSZ

UZENET1

U_HOSSZ

NEV
N_HOSSZ

.HUN
.BRD
.UK

TABLA

DEFB "HUN   magyar mod",13,10
EQU 18

DEFB "HUN   Magyar EXOS-hibauzenetek",13,10
DEFB "       A jelenlegi mod:"
EQU 55

DEFB "HUN"
EQU 3

DEFB " HUN",13,10
DEFB " BRD",13,10
DEFB " UK ",13,10

DEFB 255,24,"Nem megengedett EXOS kod"
DEFB 254,35,"Nem engedelyezett EXOS funkciohivas"
...
DEFB 0

Ezt a bővítőt már nemcsak kényelmi szempontból érdemes egy jó assembler program segítségével megírni. Az ASMON szolgáltatása pl., hogy a lefordított byte-sorozat (object code) magnetofonra vihető 6-os modulfejjel (abszolút rendszerbővítő). A magnetofonról később beolvasott file számára az EXOS szegmenst foglal és automatikusan beláncolja a bővítő nyilvntartásba.
A BASIC-ből felépített változatnál mindezt magunknak kell megtennünk.

1 PROGRAM "Hun.bas"
100 ALLOCATE 100
110 CODE M=HEX$("3E,FF,D3,B2,21,79,BF,34,E5, F7,18,E1,35,79,D3,B1,26,00,6F,C9")
120 LET SZEGMENS=USR(M,0)
130 POKE 540,10
140 POKE 541,64
150 CODE BELEPES=HEX$("79,3D,28,17,3D,28,15, 3D,28,26,3D,28,35,3D,28,33,3D,28, 52,3D,28,50,3D,28,4E,C9")
160 CODE FLAG=CHR$(255)
170 CODE HIDEG=HEX$("C9")
180 CODE PARANCS=HEX$("D5,C5,CD,77,C0,20,0A, AF,32,24,C0,C1,D1,0E,00,AF,C9,C1,D1,C9")
190 CODE HELP=HEX$("D5,C5,78,B7,20,45,3E,FF, 11,B4,C0,01,12,00,F7,08,18,EB")
200 CODE VALTOZO=HEX$("C9")
210 CODE HIBA=HEX$("3A,24,C0,B7,C0,D5,C5,11 ,00,00,21,11,C1,23,19,7E,B7,CA,37,C0,23, 5E,B8,20,F4,C1,D1,0E,00,DB,B3,47,EB,C9")
220 CODE MODUL=HEX$("C9")
230 CODE RAM=HEX$("C9")
240 CODE INIC=HEX$("3E,FF,32,24,C0,C9")
250 ! Segedrutinok es tablak
260 CODE AZONOS=HEX$("13,21,FD,C0,06,03,1A, BE,C0,13,23,10,F9,C9")
270 CODE HP_TEXT=HEX$("CD,77,C0,20,AD,3E,FF, 11,C6,C0,01,37,00,F7,08,3A,24,C0,B7,11, 00,C1,28,0D,3A,B8,00,FE,3E,11,0C,C1,28, 03,11,06,C1,01,06,00,3E,FF,F7,08,C3,31,C0")
280 CODE SZOVEG="HUN Magyar mod"&HEX$("0D,0A")
290 CODE UZENET1="HUN Magyar EXOS hibauzenetek"&HEX$("0D,0A")
300 CODE =" A jelenlegi mod: "
310 CODE NEV="HUN"
320 CODE HUN=" HUN"&HEX$("0D,0A")
330 CODE BRD=" BRD"&HEX$("0D,0A")
340 CODE UK=" UK "&HEX$("0D,0A")
350 CODE TABLA=HEX$("FF,18")&"Nem megengedett EXOS kod"
360 CODE =HEX$("FE,21")&"Nem megengedett EXOS funkciohivas"
370 CODE =HEX$("FD,16")&"Hibas EXOS szovegfuzer"
380 CODE =HEX$("FC,17")&"A rendszerverem megtelt"
390 CODE =HEX$("FB,14")&"Csatorna nem letezik"
400 CODE =HEX$("FA,15")&"Az eszkoz nem letezik"
410 ! ...
420 ! Belancolas
430 POKE 49088,10
440 POKE 49089,64
450 POKE 49090,SZEGMENS

A BASIC program középső része (150-400. sorok) betölti a bővítő rutinjait és tábláit az 1. LAP-ra lapozott szegmensbe. Ezzel kapcsolatban csak egy megjegyzésünk van: mivel a betöltő rész rögzített tábla- és szövegcímeket használ, a különböző kiírandó szövegeket pontosan a megadott formában (ill. azonos karakterszámmal) kell beírni. A hibaüzenet-tábla már tetszés szerinti üzeneteket tartalmazhat a következő formában:

A bővítő csak azokra a hibakódokra ad magyar nyelvű üzenetet, amelyeket beépítettünk a táblába.
A program első és záró része két érdemi funkciót lát el: a szegmenslefoglalás és a rendszerbe illesztés műveletét.
A bővítő aktív marad egyes inicializálások (l. EXOS 0) után is, de a felhasználó számára lefoglalt szegmens felszabadul egy-egy ilyen műveletnél. Ilyenkor két lehetőség közül választhatunk.

  1. Nem foglalkozunk a felhasznált szegmens lefoglalásával, mert - programunk méretének ismeretében - tudjuk, hogy a rendszer úgysem éri el a mi szegmensünket (pl. az FBH szegmenst).

  2. Felhasználjuk, hogy a perifériáknál: lefoglalt szegmensek nem szabadulnak fel nagyobb fokú inicializálás (pl. BASIC-ből WP-be lépés) során sem. Ehhez a rendszer mélyebb ismerete szükséges: az a módszer, ahogy az EXOS maga jelzi a perifériaműveleteket. Az operációs rendszer a szegmenskiutalás során - mint láttuk - közvetve a BF79H rendszerváltozó értéke alapján ismeri fel a perifériáktól érkező hívásokat. Ezek alapján egyszerű a rendszert becsapni:
LD A,FFH
OUT (B2H),A
LD HL,BF79H
INC (HL)
PUSH HL
EXOS 18H
POP HL
DEC (HL)
LD A,C
OUT (B1H),A
LD H,0
LD L,A
RET
3E FF
D3 B2
21 79 BF
34
E5
F7 18
E1
35
79
D3 B1
26 00
6F
C9

A szegmens lefoglalása és a bővítő betöltése után már csak a rendszerbe illesztés van hátra. Ha minden inicializálást (kivéve a hidegindítást) túlélő bővítőt szeretnénk, ágyazzuk be a szegmenst a ROM-könyvtárba (l. 2.5.2 pont).
Mi most a 430-450. sorokban a legegyszerűbb beláncolást alkalmaztuk: a BFC3H báziscím alá beírtuk a belépési cím alsó és felső bájtját (az 1. LAP tartományában!), majd a bővítő szegmensét. Ha már volt a rendszerben előzőleg RAM-bővítő, ezt a módszert ki kell egészíteni valódi láncolássá: a leírás előtt ki kell olvasni a báziscím alatti pointert és saját bővítőnk belépési címe előtt kell letárolni a 3 bájtot, a 4007H, 4008H, 4009H címekre.
A beillesztés után a bővítő aktívan kapcsolódik a rendszerhez, mint azt a következő képernyőmásolat is szemlélteti:

Függelék

3. Az EXOS memóriatérképe és rendszerváltozói

Az EXOS memória-térképe

 
  8000 Szegmenshatár
Szabad terület
 
(BF91)  
Csatornaterület
 
(BF93)  
Periférialeírók
 
(BF95)  
ROM-oknak kiutalt RAM-terület
 
(BF97)
(BF9C)
  ROM-tábla
 
 
ABD0
RAM-tábla
 
  ABD1
EXOS munkaterület
 
- - - - - - - - - - - - - - - - - - - - - - - - - -
 
B216
Z80 verem az EXOS futása során
 
  B217
B22F
EXOS lapozó rutinok
 
  B230
B252
"Written by ..." szöveg
 
    Perifériakezelők munkaterülete
 
  B480
B8FF
Karakter-generátor
 
  B900
BB1F
LPT: sorparaméter-tábla
 
    EXOS munkaterület
 
    Rendszerváltozók
 
  BFC5
BFEF
EXOS változók
 
  BFF0
BFFF
Rendszerváltozók
 

Rendszerváltozók és EXOS változók

BF8F Felhasználói határ a megosztott szegmensben
BF91 EXOS határ (0000 és 3FFF közötti érték: a szakad bájtok száma a rendszerszegmensben)
BF93 A periférialeírók területének alsó határa
BF95 A ROM-oknak kiutalt RAM alsó határa
BF97 A ROM-tábla alsó határa
BF9A A RAM-táblában az EXOS határt tartalmazó szegmensre mutat
BF9C A ROM-tábla felső határa
BF9E A megosztott szegmens száma (0, ha nincs ilyen)
BF9F A szabad szegmensek száma
BFA0 A felhasználónak kiutalt szegmensek száma
BFA1 A perifériáknak kiutalt szegmensek száma
BFA2 A rendszerszegmensek száma
BFA3 A hibátlan RAM-szegmensek száma
BFA4 A hibás RAM-szegmensek száma
BFA5 Munkaterület az aktuális csatornák kereséséhez
BFBA A csatornalánc első pointere (báziscím: BFBD)
BFBD A periférialánc első pointere (báziscím: BFC0)
BFC0 A bővítőlánc első pointere (báziscím: BFC3)
Az EXOS változók tárolási címe: BFC5 + a változó sorszáma (0 - 39)
BFED USR_ISR: felhasználói megszakítási rutin címe
BFEF CRDISP_FLAG: bejelentkezési üzenet engedélyezése
BFF0 SECOND_COUNTER: másodpercszámláló
BFF2 FLAG_SOF_IRQ: szoftver megszakításkérés jelző
BFF3 PORTB5: a B5H port aktuális értéke
BFF4 LP_POINTER: a sorparaméter-tábla kezdőcíme
BFF6 ST_POINTER: az állapotsor kezdőcíme
BFF8 RST_ADDR: felhasználói melegindítási cím
BFFA STACK_LIMIT: veremhatár a belső ellenőrzéshez
BFFC USR_P0: az EXOS hívás előtti LAP-konfiguráció
BFFD USR_P1
BFFE USR_P2
BFFF USR_P3

4. EXOS hibakódok és üzenetek

Az EXOS 28 funkcióhívás eredménye UK és BRD módban:

5. A perifériakezelők belépési pontjai

Leíró:
FF/6661: 74 66 FF 00 20 00 8F 5F 04 00 08 KEYBOARD
Belépési címek
(BRD szegmens):
57282
DFC2
0:
Megszakítás
58122
E30A
1:
Csatorna megnyitása
58122
E30A
2:
Csatorna létrehozása
57270
DFB6
3:
Csatorna lezárása
57270
DFB6
4:
Csatorna megszüntetése
58189
E34D
5:
Karakter olvasása
58508
E48C
6:
Blokk olvasása
58481
E471
7:
Karakter írása
58481
E471
8:
Blokk írása
58161
E331
9:
Csatornaállapot olvasása
58481
E471
10:
Csatornaállapot beállítása
58251
E38B
11:
Különleges funkció
57259
DFAB
12:
Inicializálás
57281
DFC1
13:
Puffermozgatás

 

Leíró:
FF/6674: 85 66 FF 00 08 00 36 4D 04 00 06 EDITOR
Belépési címek
(BRD szegmens):
49895
C2E7
0:
Megszakítás
52591
CD6F
1:
Csatorna megnyitása
52591
CD6F
2:
Csatorna létrehozása
52562
CD52
3:
Csatorna lezárása
52562
CD52
4:
Csatorna megszüntetése
53185
CFC1
5:
Karakter olvasása
53193
CFC9
6:
Blokk olvasása
53144
CF98
7:
Karakter írása
53152
CFA0
8:
Blokk írása
53109
CF75
9:
Csatornaállapot olvasása
56755
DDB3
10:
Csatornaállapot beállítása
52839
CE67
11:
Különleges funkció
52565
CD55
12:
Inicializálás
52567
CD57
13:
Puffermozgatás

 

Leíró:
FF/6685: 93 66 FF 00 80 00 D3 6D 01 00 03 NET
Belépési címek
(BRD szegmens):
60959
EE1F
0:
Megszakítás
61285
EF65
1:
Csatorna megnyitása
61285
EF65
2:
Csatorna létrehozása
61445
F005
3:
Csatorna lezárása
61445
F005
4:
Csatorna megszüntetése
61515
F04B
5:
Karakter olvasása
61730
F122
6:
Blokk olvasása
61742
F12E
7:
Karakter írása
61874
F1B2
8:
Blokk írása
61886
F1BE
9:
Csatornaállapot olvasása
61913
F1D9
10:
Csatornaállapot beállítása
61916
F1DC
11:
Különleges funkció
61984
F220
12:
Inicializálás
61985
F221
13:
Puffermozgatás

 

Leíró:
FF/6693: A4 66 FF 00 00 00 A7 6D 01 00 06 SERIAL
Belépési címek
(BRD szegmens):
61915
F1DB
0:
Megszakítás
61391
EFCF
1:
Csatorna megnyitása
61391
EFCF
2:
Csatorna létrehozása
61508
F044
3:
Csatorna lezárása
61508
F044
4:
Csatorna megszüntetése
61657
F0D9
5:
Karakter olvasása
61736
F128
6:
Blokk olvasása
61792
F160
7:
Karakter írása
61880
F1B8
8:
Blokk írása
61910
F1D6
9:
Csatornaállapot olvasása
61913
F1D9
10:
Csatornaállapot beállítása
61913
F1D9
11:
Különleges funkció
61980
F21C
12:
Inicializálás
61915
F1DB
13:
Puffermozgatás

 

Leíró:
FF/66A4: B3 66 FF 00 00 00 BB 67 01 00 04 TAPE
Belépési címek
(BRD szegmens):
03141
0C45
0:
Megszakítás
59354
E7DA
1:
Csatorna megnyitása
59488
E860
2:
Csatorna létrehozása
59634
E8F2
3:
Csatorna lezárása
59634
E8F2
4:
Csatorna megszüntetése
59664
E910
5:
Karakter olvasása
59741
E95D
6:
Blokk olvasása
59751
E967
7:
Karakter írása
59792
E990
8:
Blokk írása
59802
E99A
9:
Csatornaállapot olvasása
59219
E753
10:
Csatornaállapot beállítása
59351
E7D7
11:
Különleges funkció
59871
E9DF
12:
Inicializálás
59821
E9AD
13:
Puffermozgatás

 

Leíró:
FF/66B3: C5 66 FF 00 00 00 69 67 01 00 07 PRINTER
Belépési címek
(BRD szegmens):
03922
0F52
0:
Megszakítás
59272
E788
1:
Csatorna megnyitása
59272
E788
2:
Csatorna létrehozása
59306
E7AA
3:
Csatorna lezárása
59306
E7AA
4:
Csatorna megszüntetése
59219
E753
5:
Karakter olvasása
59219
E753
6:
Blokk olvasása
59278
E78E
7:
Karakter írása
58871
E5F7
8:
Blokk írása
59219
E753
9:
Csatornaállapot olvasása
59219
E753
10:
Csatornaállapot beállítása
59269
E785
11:
Különleges funkció
59307
E7AB
12:
Inicializálás
59307
E7AB
13:
Puffermozgatás

 

Leíró:
FF/66C5: D6 66 FF FF 00 00 82 70 00 00 06 EDITOR
Belépési címek
(BRD szegmens):
03666
0E52
0:
Megszakítás
61624
F0B8
1:
Csatorna megnyitása
61624
F0B8
2:
Csatorna létrehozása
61598
F09E
3:
Csatorna lezárása
61598
F09E
4:
Csatorna megszüntetése
62186
F2EA
5:
Karakter olvasása
62194
F2F2
6:
Blokk olvasása
62145
F2C1
7:
Karakter írása
62153
F2C9
8:
Blokk írása
62117
F2A5
9:
Csatornaállapot olvasása
49874
C2D2
10:
Csatornaállapot beállítása
61859
F1A3
11:
Különleges funkció
61601
F0A1
12:
Inicializálás
61606
F0A6
13:
Puffermozgatás

 

Leíró:
FF/66D6: E9 66 FF FF 20 00 55 70 00 00 08 KEYBOARD
Belépési címek
(BRD szegmens):
60687
ED0F
0:
Megszakítás
61303
EF77
1:
Csatorna megnyitása
61303
EF77
2:
Csatorna létrehozása
60682
ED0A
3:
Csatorna lezárása
60682
ED0A
4:
Csatorna megszüntetése
61349
EFA5
5:
Karakter olvasása
53660
D19C
6:
Blokk olvasása
49874
C2D2
7:
Karakter írása
49874
C2D2
8:
Blokk írása
61332
EF94
9:
Csatornaállapot olvasása
49874
C2D2
10:
Csatornaállapot beállítása
61389
EFCD
11:
Különleges funkció
60682
ED0A
12:
Inicializálás
60686
ED0E
13:
Puffermozgatás

 

Leíró:
FF/66E9: F9 66 FF 00 20 00 7C 6B 00 00 05 SOUND
Belépési címek
(BRD szegmens):
60374
EBD6
0:
Megszakítás
59464
E848
1:
Csatorna megnyitása
59464
E848
2:
Csatorna létrehozása
60410
EBFA
3:
Csatorna lezárása
60410
EBFA
4:
Csatorna megszüntetése
49874
C2D2
5:
Karakter olvasása
49874
C2D2
6:
Blokk olvasása
59532
E88C
7:
Karakter írása
53738
D1EA
8:
Blokk írása
49874
C2D2
9:
Csatornaállapot olvasása
49874
C2D2
10:
Csatornaállapot beállítása
60312
EB98
11:
Különleges funkció
60410
EBFA
12:
Inicializálás
59514
E87A
13:
Puffermozgatás

 

Leíró:
FF/66F9: 00 00 00 00 20 01 81 57 00 00 05 VIDEO
Belépési címek
(BRD szegmens):
54077
D33D
0:
Megszakítás
54117
D365
1:
Csatorna megnyitása
54117
D365
2:
Csatorna létrehozása
54433
D4A1
3:
Csatorna lezárása
54433
D4A1
4:
Csatorna megszüntetése
54450
D4B2
5:
Karakter olvasása
53656
D198
6:
Blokk olvasása
54480
D4D0
7:
Karakter írása
53734
D1E6
8:
Blokk írása
54650
D57A
9:
Csatornaállapot olvasása
49874
C2D2
10:
Csatornaállapot beállítása
54679
D597
11:
Különleges funkció
53856
D260
12:
Inicializálás
54866
D652
13:
Puffermozgatás

8. A képernyőkezelés fontosabb adatai

A videojelet a NICK-chip hozza létre. A megjelenítés alapja két memóriaterület: az, amelyik a megjelenítendő információt tartalmazza (adatmező) és az, amelyik a megjelenítés módját határozza meg (sorparaméter-tábla, LTP). Mindkét területnek a NICK-chip által elérhető (olvasható) memóriatartományban: az FCH-FFH RAM-szegmensekben kell lennie (video-RAM). A NICK-chip ezt a 4 (legfelső) szegmenst lapozás nélkül, mindig ugyanazokon a címeken látja:

0000H-3FFFH : FCH szegmens
4000H-7FFFH : FDH szegmens
8000H-BFFFH : FEH szegmens
C000H-FFFFH : FFH szegmens

A NICK-chip számára minden címet ilyen vdeocímként kell megadni. Egy trükk a videocím meghatározásához: a szegmensszám alsó két bitje adja a videocím felső két bitjét.
A megfelelő szabályok szerint felépített sorparaméter-tábla nélkül semmilyen megjelenítés nem képzelhető el. A tábla a video-RAM bármely tartományában lehet, de csak 16-tal osztható címen kezdődhet: 12 értékes bitje van. Ezt a 12 bitet kell beírni - a tábla felépítése után - a NICK-chip regisztereibe:

a további bitek a betöltést szinkronizálják (alaphelyzet: 1111 lehet).
A normál esetben felépülő LPT a B900H (47360D) címen kezdődik a 2. LAP-on lévő FFH rendszerszegmensben. Videocímként ez F900H (FFH szegmens!).
A sorparaméter-tábla 16-bájtos blokkokban tárolja egy-egy sor (1-255 pixelsor magas ablak) megjelenítéséhez szükséges mód, tárolási cím, szín stb. információkat a - következők szerint (zárójelben az EXOS-ban kialakuló tipikus értékeket is megadjuk):

0. bájt (SC): a sor magasságát adja meg,
értéke: 256 - (pixelsorok száma)
(247, azaz 9 pont magasságú sorok)
1. bájt (MB): bitjei a megjelenítés módját vezérlik:

BIT 7 - (VINT): ha értéke 1, a sor kiolvasásakor a NICK-chip megszakítást kér
BIT 6 - színmód
BIT 5 - színmód BIT 5-6:

00: 2 szín,
01: 4 szín,
10: 16 szín,
11: 256 szín

BIT 4 - (VRES): karaktermódban 0, grafikus módban 1 (ha itt 0, a NICK-chip ismétli az előző sort)
BIT 3 - video mód
BIT 2 - video mód
BIT 1 - video mód BIT 3-1:

000: a függőleges szinkronizációt biztosító kijelzés nélküli mód
001: nagyfelbontású grafikus mód
010: attributum grafikus mód
100: karaktermód (256 karakteres jelkészlet)
101: karaktermód (64-es karakterkészlet)
110: használatlan
111: kisfelbontású grafikus mód

BIT 0 (RELOAD) az LPT-cím újratöltését váltja ki a NICK-chipben (TEXT 40 módban a bájt értéke 8, GRAPHICS 4-nél 32H)

2. bájt (LM): bal margó (0-63)
+ BIT 7, BIT 6: kiegészítő színvezérlés karaktermódban
3. bájt (RM): jobb margó (0-63)
+BIT 7, BIT 6: kiegészítő színvezérlés karaktermódban
A margók értékét a NICK-chip RAM-kezelése korlátozza. A ténylegesen megjeleníthető terület:
LM > 8
RM < 54
(általában a margókat megadó alsó 6 bit: LM = 11, RM = 51)
4. bájt (LDIL) elsődleges adatcím (a megjelenítendő információ videocíme)
5. bájt (LD1H) elsődleges adatcím (a megjelenítendő információ videocíme)
6. bájt (LD2L) másodlagos adatcím
7. bájt (LD2H) másodlagos adatcím
8-15. bájt az első 8 palettaszín
A második 8 palettaszínt (a 0-255 színtartomány 32 db 8-as csoportjának valamelyikét) a NICK-chip FIXBIAS regiszterének (80H PORT) felső 5 bitjére írt érték (0-31) határozza meg.

Az adott sor megjelenítésekor kialakuló képei a video adatmező(k) és a sor paraméterei együtt határozzák meg.
Karaktermódban a megjelenítendő karakter kódját az LD1 címről, az adott kódú karakter alakját, pontmátrixát az LD2 által mutatott karakter-RAM-ból olvassa ki. Pontosabban:

LD2 = a karakter-RAM videocíme / a karakterkészlet mérete

(az EXOS által kialakított rendszerben LD2 = F480H / F80H = 01E9H)

Grafikus módban az LD1 címen kezdődő adatmezőről kiolvasott érték bitjei (vagy bitcsoportjai) jelennek meg képpontonként.

Színképzés
A NICK-chip a színek kialakításához általában az adott sor palettaszíneit használja. A karaktermódú megjelenítés alapvetően két színt használ (0: papír, 1: tinta)
A bal margó 6. és 7. bitje kiterjesztheti ezt a lehetőséget:
ha BIT 7, LM = 1, akkor szín = szín + 2 x(a megjelenítendő kód 7. bitje)
ha BIT 6, LM = 1, akkor szín = szín + 4 x(a megjelenítendő kód 6. bitje)

Grafikus módban:

A kétszínű grafikus képernyőre való többszínű karakteres írást támogatja a jobb margó (RM) 6. és 7. bitjének kiegészítő színvezérlése:
ha BIT 7, RM = 1, akkor szín = szín + 2 x(a kiolvasott bájt 7. bitje)
ha BIT 6, RM = 1, akkor szín = szín + 4 x(a kiolvasott bájt 0. bitje)
A színvezérlésre felhasznált biteket a rendszer lemaszkolja megjelenítéskor, így a karakterek soványabbak lesznek. A fentiek sajátos ötvözetét teszi lehetővé az attributum mód, amelyben az LD1-rő1 kiolvasott adat 8. bitje jelenik meg képpontként (2 szín), de a papír- és tintaszín az LD2 tartományról kiolvasott adat szerint karakterenként változhat:
papír: BIT 7-4
tinta: BIT 3-0

Ilyen sorparaméter-blokkból lehet összeállítani a teljes képernyőt meghatározó sorparaméter-táblát. Ez 312 pixelsor vezérlését jelenti.
A BASIC az EXOS-on keresztül 252 pixelsornyi képernyőt használ, a többi sor az alsó-felső keretet adja. A videoinformációkat tartalmazó blokkokat a függőleges szinkronizációt biztosító és az újraolvasást kiváltó zárószakasszal egészíti ki a rendszer 312 pixelsorra.
A leírtak szemléltetésére, saját tábla készítéséhez tanulságos lehet egy LPT megfigyelése. A következő sorparaméter-tábla pl. a GRAPHICS 4 utasítást követő állapotot mutatja.

B900
B908

B910
B918
B920
B928
.
.
.
BA40
BA48

BA50
BA58
BA60
BA68
.
.
.
BAB0
BAB8
F7 08 0B 73 B8 FE E9 01
00 36 00 49 FF 24 2D 36

F7 32 0B 33 2C AC E9 01
00 24 49 DB 92 2D 36 FF
F7 32 0B 33 FC AE E9 01
00 24 49 DB 92 2D 36 FF



F7 32 0B 33 PC E1 E9 01
00 24 49 DB 92 2D 36 FF

F7 08 0B 73 24 E5 E9 01
00 92 00 49 FF 24 2D 36
F7 08 0B 73 4C E5 E9 01
00 92 00 49 FF 24 2D 36



F7 08 3F 74 B8 FE E9 01
00 92 00 49 FF 24 2D 36
; Állapotsor (TEXT 40)
; FF/BEB8 kezdőcím

; 1. grafikus sor

; 2. grafikus sor




; 20. grafikus sor


; 21. sor (TEXT 40)

; 22. sor




;27. sor (TEXT 40)
Zárószakasz:
BAC0
BAC8
BAD0
BAD8
BAE0
BAE8
BAF0
BAF8
BB00
BB08
BB10
BB18
F2 92 3F 00 00 00 00 00
00 00 00 00 00 00 00 00
FD 10 3F 00 00 00 00 00
00 00 00 00 00 00 00 00
FE 10 06 3F 00 00 00 00
00 00 00 00 00 00 00 00
FC 10 3F 1C 00 00 00 00
00 00 00 00 00 00 00 00
F0 12 06 3F 00 00 00 00
00 00 00 00 00 00 00 00
EB 13 3F 00 00 00 00 00
00 00 00 00 00 00 00 00
; +14 pixelsor

; +3 pixelsor

; +2 pixelsor

; +4 pixelsor

; +16 pixelsor

; +21 pixelsor
(RELOAD)

Az EXOS igen nagy mértékben támogatja a NICK-chip fentiek szerinti programozását. A funkcióhívások segítségével a BASIC ismert grafikus és szöveges, szolgáltatásait (videolapok nyitását, megjelenítését, karakterek írását, grafikus műveleteket) egyszerűen elérhetjük gépi kódú programokból is.

A következőkben ös$zefoglaljuk a képernyőkezelést támogató EXOS hívások főbb tudnivalóit.
- Csatornanyitás (EXOS 1 vagy EXOS 2)
Videolapokat VIDEO: periférianévvel lehet nyitni. A létrehozandó lap méretét és módját a kezelő az EXOS változásból olvassa ki, ezért azokat hívás előtt megfelelően be kell állítani.

MODE VID: 0: hardver szövegmód (max. 42 karakter / sor, ezt használja a TEXT 40)
1: nagyfelbontású grafika (GRAPHICS)
2: szoftver szövegmód (max. 82 karakter / sor, ezt használja a TEXT 80)
4: kisfelbontású grafika
15: attributum mód
COLR VID: 0 : 2 szín
1: 4 szín
2: 16 szín
3: 256 szín

X SIZ VID: a videolap szélessége (TEXT 40 módú karakterszámban megadva) Y SIZ VID: a videolap magassága (karaktersorokban) Fontos tudni, hogy a csatornanyitással a lap még nem jelenik meg a képernyőn (l. EXOS 11)

- Csatornazárás (EXOS 3 vagy EXOS 4)
A csatornamutatók törlésén kívül letakarja a képernyőn a lezárt csatornához tartozó sorokat.

- Karakterírás (EXOS 7)
A legsokoldalúbb képernyőkezelő funkcióhívás. Amellett, hogy bármilyen video és színmódban kiírja a nyomtatható karaktereket a lapra (a megfelelő karaktermérettel), számos vezérlőutasítást is kezel. Ezek egy részét az ESC (CHR$ (27)) kód vezeti be.

Általános vezérlőkarakterek
0AH: soremelés (LF)
0DH: kurzor a sor elejére (CR)
1AH: laptörés, kurzor a kezdő pozícióba
1EH: kurzor a kezdő pozícióba
ESC K: karakter definiálása
A kiírandó bájtok:
CHR$(1BH); CHR$(4BH); CHR$(karakterkód); CHR$(D1); ....; CHR$(D9)
A D1-D9 adatok a karakter pontmátixát írják le a BASIC-ben szokásos módon.
ESC C: az elsó 8 palettaszín megadása
(1BH) (43H) (C1) .... (C8)
ESC c: egy palettaszín megadása:
(1BH) (63H) (N) (C)
Az N. palettaszín (0-7) értéke C lesz.
ESC 1: tintaszín kiválasztása:
(1BH) (49H) (N)
A tintaszín az N. palettaszín lesz
ESC P: papírszín kiválasztása
(1BH) (50H) (N)
A papírszín az N. palettaszín lesz.
ESC =: kurzorpozíció beállítása
(1BH) (3DH) (Y+20H) (X+20H)
A kurzor az X. karaktersor Y. oszlopára áll. Ha valamelyik adat 0, abba az irányba nem mozdul a kurzor.
Csak szöveglapon értelmezett vezérlőkarakterek
08H: kurzor balra
09H: kurzor jobbra
0BH: kurzor fel
16H: kurzor le
19H: törlés a sor végéig
ESC?: kurzorpozíció olvasása
A következő két olvasott bájt (EXOS 5) a fentiek szerinti (Y + 20H) és (X + 20H) lesz
ESC.: kurzorkarakter beállítása
(1BH) (2EH) (N)
Az írás után az N kódú karakter, lesz a lap kurzora
ESC M: kurzorszín beállítása
(1BH) (4DH) (N)
A kurzor színe az N. palettaszín lesz
ESC O: kurzorjelzés bekapcsolása
(1BH) (4FH)
ESC o: kurzorjelzés kikapcsolása
(1BH) (6FH)
ESC S: automatikus görgetés (scroll) bekapcsolása
(1BH) (53H)
ESC s: automatikus görgetés kikapcsolása
(1BH) (73H)
ESC V: fölfelé görgetés
(1BH) (55H) (K + 20H) (V + 20H)
A K-V tartományba eső sorok lépnek feljebb
ESC D: lefelé görgetés
(1BH) (44H) (K + 20H) (V + 20H)
A K-V tartomány sorai lépnek lejjebb
Csak grafikus lapokra értelmezett vezérlőkarakterek
ESC A: grafikus sugár pozicionálása
(1BH) (41H) (XLO) (XHI) (YLO) (YHI)
A sugár a kétbájtos X,Y koordinátákra kerül.
ESC @: sugárpozíció olvasása
(1BH) (40H)
A csatornáról olvasott 4 következő bájt a kurzorpozíciót adja
ESC R: relatív sugármozgás
(1BH) (52H) (XLO) (XHI) (YLO) (YHI)
A sugár vízszintesen X, függőlegesen Y pixelnyivel mozdul el.
ESC S: sugár bekapcsolása
(1BH) (53H)
A sugárpozíción az aktuális tintaszínnel képpont jelenik meg és a sugár a további mozgások során rajzol
ESC s: sugár kikapcsolása
(1BH) (73H)
ESC .: vonaltípus kiválasztása
(1BH) (2EH) (N)
N=1: folytonos vonal
N=2...14: szaggatott vonaltípusok
ESC M: vonalmód kiválasztása
(IBH) (4DH) (N)
N=0: felülírás
N=1: OR a régi és új videoadat között
N=2: AND
N=3: XOR
ESC a: attributum jelzőbájt beállítása
(1BH) (61H) (N)
ESC F: grafikus kitöltés (FILL)
ESC E: ellipszisrajzolás
(1BH) (45H) (XLO) (XHI) (YLO) (YHI)
A BASIC PLOT ELLIPSE X,Y utasításnak felel meg.

- Blokkírás (EXOS 8)
A karakterírás valamennyi funkciója használható

- Karakterolvasás (EXOS 5)

- Blokkolvasás (EXOS 6)
Az olvasott bájt alaphelyzetben
- szöveglapnál a kurzorpozíción álló karakterkódja
- grafikus lapnál a sugárpozíción álló képpont színe (palettaszín, ill. 255 színű módban a színkód)
Az ESC? ill. ESC@ írása után az olvasott bájtok a kurzor (sugár) pozícióját adják.

- Speciális funkció (EXOS 11)
Az alfunkciószámtól (B) függően elérhető funkciók:
B = 1: videolap kijelzése
B = 2: üzemmód és méret lekérdezése
B = 3: tárolási cím lekérdezése
B = 4: karakterkészlet inicializálására

9. Az ASMON (SIMON)

assembler-monitor program használata

Röviden összefoglaljuk a könyv anyagának összeállításánál használt assembler-monitor (az 1.3 verziójú SemiSoft Asmon program) használatához szükséges tudnivalókat.

A program a beolvasás után ":ASMON" paranccsal indítható. Ekkor a monitorprogram jelentkezik be. A képernyő felső részén (az üzenetmezőn) ekkor a pillanatnyi státusz látható. Ebben az EXOS rendszerállapot mellett a pillanatnyi lapkiosztás is látható:

A képernyő alsó részén az ASMON a regiszterek tartalmát és az általuk megcímzett memória környezetét jelzi ki.
A monitor a képernyő középső részén (INPUT-mező) várja a parancsot, illetve a hozzá tartozó címeket, adatokat.
Az értékeket az INPUT sorban kijelzett számrendszerben kell beírni: hexadecimálisan (HEX) vagy tízes számrendszerben (DECI). A pillanatnyi üzemmód CTRL + F8-cal billenthető át.
A parancsok egykarakteresek és ENTER nélkül írhatók be.
Az elfogadott utasítások listáját kiírathatjuk az üzenetmezőre (H, azaz help):

Az utasítások a fenti sorrendben:

A:

Lefordítja az Editor-ban megírt assembly programot a beállított opcióknak (l. Z) megfelelően.

B:

Töréspontokat (BREAKPOINT 1, illetve 2) helyez el a megadott címen (praktikusan a tesztelni kívánt gépi kódú programban). A rutin futtatásakor (G) erről a pontról visszatér a monitorba és visszaállítja az eredeti memóriatartalmat.

C:

A Start-tól az End-ig terjedő memóriatartományt másolja át a Destination (cél) kezdőcímű területre.

D:

Kiírja az üzenetmezőre a Start kezdőcímű memóriaterület tartalmát hexadecimális dump formában. A listázás folyamatos, de ENTER-rel megszakítható. ENTER-re soronként, SPACE-re újra folyamatosan léphetünk tovább. Az üzemmódból STOP-pal vagy az ESC gombbal léphetünk ki.

E:

Átlép Editor üzemmódba.

F:

Feltölti a memória Start-End tartományát a Value-ra megadott értékkel.

G:

Futtatja a Start címen kezdődő gépi kódú programot.

H:

Kiírja az utasítások listáját.

I:

a Key number-re megadott számú funkcióbillentyűt programozhatjuk a Key string szövegre (pl. G4000).

J:

Az AF, BC, DE, HL regisztereket a háttérregiszterekre cseréli (ezt - ALT-REGS - felirat jelzi).

K:

Beolvassa a File name nevű (lehet üres string is) assembly forrásprogramot az Editorba.

L:

Disassemblálja a Start címen kezdődő gépi kódú programot. Opciói beállíthatók (Y).

M:

Módosíthatjuk a hexadecimális dump és karakterdump formában kiírt memóriatartományt. A kurzor ALT+F8-cal váltható át az egyik mezőből a másikba. Kilépés STOP-pal vagy ESC-pel.

N:

Kiírja a Value-ra megadott érték (szám, kifejezés, címke stb.) hexadecimális, decimális, bináris és karakteres alakját. Alapértelmezése hexadecimális; az eltérő értéket jelezni kell (pl. 65D, 101B, "A" stb.).

O:

Portállítás: lényegében egy Out (Port), Value utasítás. Ezzel változtathatjuk meg például (a B0-B3 portokon) a lapkiosztást.

P:

Nyomtatókimenet nyitás/zárás. ON állásban a dump, disassemblált lista stb. a nyomtatóra is kikerül.

Q:

Kiírja a Port periféria aktuális értékét (IN).

R:

Betölti a Start-End memóriaterületre a File name nevű állományt.

S:

Kiírja a Start-End memóriatartományt File name néven bináris fájlként.

T:

Végrehajt a Start címen kezdőd6 gépi rutinban Steps lépést, miközben kijelzi a címeket és utasításokat.

U:

A fenti utasítással azonos funkció kijelzés nélkül.

V:

Kijelzi a státuszt (1. belépés).

W:

File name néven kiírja az Editor forrásprogramját.

X:

Beállíthatjuk a Regiszterpair regisztereket Value értékre.

Y:

Disassembler (L) opciók.
Memory offset: a címekhez hozzáadandó eltolási érték. Ezzel lehet pl. az l. LAP-on tárolt programot úgy listázni, hogy a címek a végleges, 3. LAP-tartományban jelenjen meg (offset = 8000).
Addresses YES/NO: Az assembly szöveg előtt a cím és a kód listázását kapcsolja be/ki. Átkapcsolás bármelyik billentyűvel, beírás az ENTER-rel. A letiltással lehet pl. forrásszöveg formátumú listát kapni meglévő programokról (l. még [).

Z:

Assembler (A) opciók Lásd az Editor/Assembler leírásnál (a 9. Függelék további részében).

[
(BRD: Ä):

PRINTER-opciók.
Output to PRINTER/EDITOR
Editor választásnál a listák, dump-ok stb. a szövegszerkesztőbe kerülnek, és innen háttértárolóra vihetők, beépíthetők a saját forrásprogramba stb.
Use: (PRINTER:)
A nyomtató meghajtására használandó kezelő neve
Return send CR+LF/CR Sorvégeken kiküldendő vezérlőkarakterek választása
Lines (72): oldalanként sorszám
Chars (72): soronkénti betűszám
Left margin (8): bal margó
Bottom margin (6): alsó margó
Header: fejléc-szöveg (az Asmon V 1.3 szöveggel és a lapszámmal együtt íródik ki)

\
(BRD: Ö):

Editor opciók
Szükség esetén változtatható az editorpuffer kezdő- és végcíme:

    Editor buffer start (0801)

    Editor buffer end (BFFF)

]
(BRD: Ü):

Modul betöltése

@
(BRD: §):

A forrásprogram lefordítása után kiírja a szimbólumok (címkék, változók) nevét és értékét

=:

Megkeresi a Start címen kezdődő memóriaterületen a Search kérdésre megadott bájtsorozatot (számok és idézőjelek közötti karakterek vesszővel elválasztva)

::

EXOS parancsszöveg (pl. ":BASIC")

Editor és assembler
A monitorból az E paranccsal léphetünk az editorba. Ez az üzemmód lényegében egy szövegszerkesztő, amelyben megírhatjuk az assembly forrásprogramot. Az editor szintaktikai ellenőrzést nem végez. Az utasítássorokat sorszám nélkül kell beírni. A sor első pozícióján kezdődő szöveget az assembler szimbólumnak (címkének) tekinti.
Az utasításokat praktikus az első tabulátorpozíciótól kezdeni. Az utasítások számértékű operandusait (pl. LD A, n) igen sokféleképpen megadhatjuk:

A Z80-as utasításokon kívül az ASMON assembler jó néhány saját utasítást (direktívát) is ismer.

ORG nn
A címszámláló kezdőértéke nn lesz (az assembler erre a címre tölti a lefordított gépi kódú programot). Általában a program első sora

DEFB n (vagy DB n)
Az operandus értékét tárolja az aktuális címen. Az n helyén egybájtos operandus, vagy ilyenek vesszővel elválasztott sorozata, valamint karaktersorozat állhat. Például:

DEFB 07H,"PRINTER:"
DEFW nn (vagy DW nn)

A kétbájtos értéket tárolja a programban.

DEFM füzér
A karaktersorozat kódjait tárolja

DEFS nn:
üresen hagy nn helyet

EQU
szimbólum EQU nn" formában értéket ad a szimbólumnak, címkének

INCLUDE név
beolvassa és lefordítva a programba illeszti a kijelölt programot

EXOS n
az n. EXOS funkcióhívást váltja ki. Lényegében DEFB OF7H,n

END
a forrásprogram vége

További direktívák a fordítás menetét vezérlik:

.PHASE nn
Ideiglenesen az nn értékre állítja a címszámlálót

.DEPHASE
Visszaállítja a címszámlálót az eredeti ORG-nek megfelelő címtartományra. A két utasítás közötti programszakaszt a főprogramban helyezi el az assembler, de címei egy másik tartományban érvényesek (kihelyezhető rutin).

IF kifejezés
1. rutin
ELSE
2. rutin
ENDIF

Az assembler a BASIC IF kezelésnek megfelelő rutint fordítja le.

Kiegészítő feltétel-lehetőségek:

Az ASMON-ban makrókat is definiálhatunk: olyan - általában rövid, de gyakran használt - programrészleteket, amelyekre programunkban nevükkel hivatkozhatunk. Fordítás közben az assembler a nevek helyére mindenhol a hivatkozott rutinrészletet tölti.

MACRO - makrokezdet
ENDM - makrovég
EXITM - makrobefejezés ENDM előtt (feltételes elágazásnál)

Például:

.PUSH

MACRO
PUSH HL
PUSH DE
PUSH BC
ENDM

Ezután a

.PUSH
LD HL, 1234H

programrészlet a következőképpen fordítódik:

PUSH HL
PUSH DE
PUSH BC
LD HL, 1234H
...

A makrónak paraméterek is átadhatók:

"név MACRO P1, P2,..."

Ezek a paraméterek operandusok vagy akár regiszternevek is lehetnek.
Például:

MASZK

MACRO P1, P2
LD A, P1
AND P2
LD P1, A
ENDM

 

Ennek felhasználásával a

LD HL, (CIM)
MASZK H, 0FH
MASZK L, 0F0H
...

rutin a fordítás után:

LD HL,(CIM)
LD A,H
AND 0FH
LD H,A
LD A,L
AND 0F0H
LD L,A
...

A programírást, szövegkezelést a funkcióbillentyűkkel hívható almenük segítik. A menükben a joystick-kel mozoghatunk, ENTER-rel választhatunk.

A funkciók röviden:

F1:

Find string: szövegkeresés
Find & repl: szövegkeresés és-helyettesítés
Cont search: továbbkeresés
Repl string: szöveghelyettesítés
Repl & cont: továbbkeresés helyettesítés után

F2:

Set tabmark: tabulátor beállítása a kurzorpozíción
Res tabmark: tabulátor törlése a kurzorpozíción
Clr tabmark: az összes tabulátor törlése
Stand tabmark: tabulátorok alaphelyzetbe

F3:

Mark start: blokk-kezdet a kurzor helyén
Mark end: blokkvég á kurzor helyén
Copy block: kijelölt blokk másolása a kurzor helyére
Del block: kijelölt blokk törlése
Prnt block: kijelölt blokk nyomtatása
Clr marks: blokkjelzők törlése

F4:

Load file: forrásszöveg beolvasása (MERGE)
Save file: forrásszöveg kivitele
Save block: kijelölt blokk kivitele
Kill text: forrásszöveg törlése

F6:

Információ:
Text: forrásszöveg hossza
Free: szabad puffer

F7:

Help (szövegszerkesztő funkciók)

F8:

visszatérés a monitorhoz

Az editorral megírt forrásprogramot a monitor A parancsa fordítja le két menetben (PASS1, szimbólumtábla készítése, PASS2: fordítás).
A fordítás opcióit a Z utasítással adhatjuk meg:

A fentiek alapján könnyen elkészíthető pl. a 2.6. alfejezet rendszerbővítője ASMON-ban. Az ORG C00AH kezdőcím és a Memory offset=8000H eltolás biztosítja, hogy a bővítő a C00AH címre fordítódjon, az

Object file name: "NEV"
EXOS module header: YES
EXOS module type: 6

opciók pedig azt, hogy abszolút rendszerbővítőként kerüljön kazettára. Beolvasáskor a bővítő automatikusan beláncolódik és így, elláthatja a funkcióit.

Vissza