Múzeum - Enterprise könyvek - Gépi Kódú Programozás
1. A gépi kódú programozás alapjai 2. A háttérrendszer |
3. A BASIC rendszer 4. Bővítési, kiterjesztési lehetőségek Függelék |
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:
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)
7FPRINT W$(16383)
3FFFPRINT 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.
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 LOOPSTART
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 LOOPSTART
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 LOOPSTART
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.
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=? 256F: 10. 0. 000
H,L=00,80START
HL=? 9F: 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: |
RRD |
fordított irányú forgatás: a félbájtok "forgalma": |
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.
|
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 B,0A LA A,C OUT (B5),A IN A,(B5) CPL LD (HL),A INC C INC HL DJNZ CIKL RET |
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.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.
A két belső ROM-ban tárolt alapszoftver a következőket tartalmazza:
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.
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 08 CD 25 B2 08 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 RET 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.
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:
A továbbiakat tekintsük át a legkevésbé drasztikus variációtól:
A megszakítás a hívóhoz való visszatéréskor (most és a további alternatíváknál is) tiltott.
|
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,A100 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ősorLá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 190A 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ódA 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ímA 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 mutatPé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ékA 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: |
|
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,C5 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én ; 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: |
|
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 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 |
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
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 C4F9 |
21 C0 BF CD 47 C6 28 19 5F 7E B7 20 F6 23 D1 |
LD HL,BFC0 CALL C647 JR Z,C500 LD E,A LD A,(HL) OR A JR NZ,C4E2 INC HL POP DE |
; 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.
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:
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.
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)
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 |
a belapozott szegmens kezdőcímére állítja a CODE-mutatót, majd betölti a kezelő rutinjait és tábláit (150-280)
egy újabb gépi rutinnal kéri a beírt kezelő beláncolását (130 és 290):
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).
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ó.
|
ORG 0C00AH POP BC PUSH DE LD A,255 |
; belépési cím |
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.
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).
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 |
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 4 - (VRES): karaktermódban 0, grafikus módban 1 (ha itt 0, a NICK-chip ismétli az előző sort)
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
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. |
Z: |
Assembler (A) opciók Lásd az Editor/Assembler leírásnál (a 9. Függelék további részében). |
[ |
PRINTER-opciók. |
\ |
Editor opciók Editor buffer start (0801) Editor buffer end (BFFF) |
] |
Modul betöltése |
@ |
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ő soraDEFB 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ároljaDEFS nn:
üresen hagy nn helyetEQU
szimbólum EQU nn" formában értéket ad a szimbólumnak, címkénekINCLUDE név
beolvassa és lefordítva a programba illeszti a kijelölt programotEXOS n
az n. EXOS funkcióhívást váltja ki. Lényegében DEFB OF7H,nEND
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:
Assembly listing OFF/ON
A fordítás közbeni lista készítését kapcsolhatjuk ki/be.
List conditions NO/YES
Feltételes elágazások listázása
Force Pass2 NO/YES
"YES" esetén akkor is megkísérli a 2. menetet a fordító, ha az elsőben hibát talált
Memory assembly NO/YES
"Yes" esetén az ORG címnek (és az offsetnek) megfelelően a memóriába töltődnek a lefordított gépi kódok (így készíthető futtatható rutin). Ebben az esetben a következő kérdés:
Memory offset
Megadható a címszámláláshoz hozzáadandó eltolási érték. PI. egy rendszerbővítőnek, majd a 3. LAP-on kell futnia, de az assembler nem töltheti ide, hiszen saját maga is itt működik. Ilyenkor:
ORG C000H
offset = 8000H
Object file name:
Ha nevet adunk meg, az ASMON a lefordított programot háttértárolóra viszi. Ehhez adhatunk meg további információkat
EXOS module header NO/YES
A fájl elé kell-e EXOS fej
EXOS modul type
Megadható a kivitt fájl típusa (pl.: 6, abszolút rendszerbővítő). Az ASMON nem kezeli a relatív fájl-formát (2. és 7.)
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.