Cikkek


Lehetőségek Páratlan Tárháza (LPT)
Tömbök rendezése
Betöltési problémák
Hanoi Tornyai
ENTERPRISE képviselet nyílt Budapesten
Az ismeretlen SPOKE és SPEEK
A közvetlen lemezkezelés rejtelmei
Az Enterprise DOS lemezek felépítése
Assemly segédrutinok
Sprite kezelés ENTERPRISE-on
RAM-szegmensek az EXOS alatt
A tömörített programokról (kiegészítve)
A bináris számokról
Cartridge átalakítások
Az életjáték
Karakterek tervezése
BASIC programok láncolása
Táblázatos Adatbevitel BASIC-ben
Microsoft BASIC programok futtatása Ep-n
A dBASE II adatbáziskezelő rendszer ismertetése
Címezzünk pontosan!
AZ ENTERPRISE rendszer-szegmens rögzített területének címei és azok funkciója
Compfair '94
Billentyűzet és joystick olvasás
B0-B7, BF regiszterek értelmezése
Felhasználói megszakításkezelés
Az Enterprise memórialapjainak kezelése
EXOS kompatibilis memóriakezelés


Tisztelt Olvasó!

Az ENTERPRISE számítógép 1987 májusában tűnt fel a hazai áruházakban. Sokan döntöttünk a gép megvétele mellett annak ellenére, hogy igazából nem tudtuk, mi rejtőzik a fekete dobozban. Kevés volt a szoftver, a szakirodalom. Akkori hírek szerint az ENTERPRISE cég csődbement, a gép nyugaton csúfosan megbukott, és nekünk csak az olcsó "vas" jutott. A helyzet meglehetősen ellentmondásos és zavaros volt.
Aztán teltek-múltak a hónapok. Lassan megismertük gépünket, és rájöttünk, hogy az ENTERPRISE a maga kategóriájában kiváló számítógép. A hazai forgalmazó sok ígéretet tett, igyekezett ezeket teljesíteni, bennünket ellátni. Feltűntek a komoly, professzionális felhasználói és játékprogramok, igyekeztünk ezekhez hozzájutni (ki így, ki úgy). Megvásárolhattuk a régóta áhított szakirodalmat, leírásokat. A tehetősebbek lemezegységet, egeret, beszédszintetizátort vehettek.
Mára a helyzet sokat tisztult. Nem igazolódtak azok a feltevések, amelyek a gép helyzetét negatívan ítélték meg. Természetesen igaz, hogy az ENTERPRISE lemaradt az iparszerű szoftver-és hardverfejlesztésről, de rendelkezünk a továbblépéshez szükséges alapszoftverekkel, információkkal. A géptulajdonosok egy része programokat készít, kisebb-nagyobb áramköri kiegészítőket tervez és illeszt a géphez, sokfajta tapasztalattal rendelkezik. Nagy kár, hogy miután megszületik valami, az csak néhány közeli baráthoz jut el, vagy egyszerűen elsüllyed egy fiók mélyén. Pedig könnyen lehet, hogy néhány kilométerrel arrébb valakinek pontosan erre lenne szüksége.
Az ENTERPRESS kiadásával szeretnénk megteremteni az ENTERPRISE tulajdonosok közötti kapcsolatot. Tudomásunk szerint tizennyolcezer felhasználó dolgozik, játszik ENTERPRISE géppel, de ez a szám hamarosan eléri a húszezret. Egy ekkora tábor jogosan vár használható, értékes tudnivalókat. A hazai szaksajtóban már jelentek meg vele kapcsolatos cikkek, de az összes ilyen lapra jellemző, hogy az ENTERPRISE-zal csak mellékesen foglalkoznak.
Célunk, hogy az ENTERPRESS-t igazi házi számítógépesekhez szóló kiadvánnyá formáljuk. Többféle sorozatot szeretnénk elkezdeni, ezekben egy-egy témával részletesen megismerkedhetne az érdeklődő. Például: Z80 Assembly és az EXOS, Pascal, dBase, a grafika (sprite-ok) és a hang kezelése, hardverelemek ismertetése. A Basic nyelvvel kapcsolatos tanfolyamot nem szándékozunk közölni, hiszen erről már sok könyv jelent meg, mi inkább kész listákat, megoldásokat közölnénk több-kevesebb magyarázattal kiegészítve. Igyekezni fogunk a játékprogramokhoz leírásokat, térképeket, örökéletkódokat szerezni, bár ezzel a témával több kiadvány foglalkozik, és nehezen tudnánk velük konkurálni.
Természetesen a lapot az olvasók beküldött írásai, programjai, ötletei, tapasztalatai is színesítenék. Jó tollú szerzők akár sorozatot is indíthatnak, ezért honoráriumot fizetünk. Hosszabb programokról csak leírást fogunk közölni, ezek kazettára másolva lesznek megrendelhetők. Biztosan népszerű lesz a levelezési és a hirdetési rovat. (Már most felhívjuk a nepperek figyelmét, hogy ránk ne számítsanak!) A leendő szerzőkre vonatkozó tudnivalókat később közöljük.

Néhány mondat az ENTERPRESS helyzetéről: a lapot tavaly, karácsony előtt akartuk megjelentetni, de szerencsésen mindig közbejött valami. Az újság tulajdonosa a székesfehérvári Mátrix Kft. Mint gazdálkodó, nyereséget váró gazdasági szervezet, a lapot valamelyes hasznot hozó vállalkozásként kezeli. (Nem ettől fognak nagy céggé fejlődni!) Manapság a lapkiadás - mint minden más - meglehetősen kockázatos. Az ENTERPRESS további léte kizárólag a fogadtatás sikerétől függ. Kedvező esetben jelentősen növeljük a példányszámot, kialakítva az elérhető árat is.
Az ENTERPRESS első száma valószínűleg kevesekhez jut el. Szeretnénk remélni, hogy előbb-utóbb minél több géptulajdonos szerez tudomást lapunk létezéséről, és annak elolvasása után már türelmetlenül várja a kővetkező számot.

A szerkesztők (1990 szeptember)

 

Lehetőségek Páratlan Tárháza (LPT)
Az eddig ENTERPRISE-a készült programok közül szinte csak a demókból kaphattunk némi ízelítőt számítógépűek grafikai képességeiből. Pedig a számunkra felkínált lehetőségek szinte korlátlanok. Ezeknek a látványos megoldásoknak a megismeréséhez szeretnék némi segítséget nyújtani ebben a rovatban.
Először a LPT felépítését ismertetem, majd kisebb-nagyobb példaprogramokkal próbálok bemutatni néhány bükköt, ötletet Természetesen ezek megértéséhez illetve használatához ismerni kell a assembly programozás alapjait valamint a ENTERPRISE memóriakezelését
Az ENTERPRISE grafikájáért a NICK chip felelős. A működésébe való beavatkozáshoz négy port áll rendelkezésünkre. Ezen portok és szerepük a következő:

A NICK chip porijai után lássuk a memóriakezelését. A NICK csak 64 kbájtot tud megcímezni, de azt közvetlenül teszi attól függetlenül, hogy az be van-e lapozva vagy nincs. Ezt a grafikus memóriaterületet a felső 4 szegmens alkotja (0FCh..0FFh). Ezeket a Z80 aszerint címzi, hogy melyik lapra vannak belapozva, a Nick azonban folytonosan 0000h-tól 0FFFFh-ig. A videómemóriához való hozzáférésben a Nick chipnek prioritása van, ezért itt a programok lassabban futnak és nem időzíthetők.
Minden LPT egyenként 16 bájtos sorparaméter blokkokból áll. Egy-egy LPB (Line Parameter Block - sorparaméter blokk) a képernyőnek egy részét definiálja. Ennek a képernyőrésznek MODSOR a neve. Egy sorparaméter blokk felépítése a következő:

Egy LPT definiálásához ismerni kell a TV-kép felépítését. A teljes kép 625 sora két egyenként 312.5 soros félképből áll. Az egyik félkép tartalmazza a páros sorszámú sorokat a másik a páratlanokat. Ahhoz tehát, hogy a kép ne hullámozzon pontosan 312.5 sorból kell állnia a LPT által definiált MODSOR-oknak. Ilyenkor a két félkép teljesen megegyezik egymással. Természetesen lehetőség vas a teljes 625 soros képernyő kihasználásához. Ezt interlace üzemmódnak hívják Ilyenkor az egyik félkép-definíció végén nem állítjuk be a RELOAD bitet, hanem folytatjuk a második félképpel és csak ennek a végen utasítjuk a Nick-et, hogy kezdje előröl a LPT olvasását.
A tökéletes állóképhez még egy dologra kell ügyelnünk, mégpedig a szinkronizációra. A pixelsorok vízszintes szinkronizációját a gép elvégzi helyettünk, a függőleges képszinkront azonban nekünk kell biztosítani. Ezt legegyszerűbben a következő két LPB-vel tehetjük meg:
0FCh,10h,00h,3Fh,00,00,00,00,00,00,00,00,00,00,00,00
0FFh,10h,3Fh,20h,00,00,00,00,00,00,00,00,00,00,00,00

Szinkronizációs módban a bal és a jobb margó a szinkron be-illetve kikapcsolásának a helyét vezérli a soron belül. Az első blokk négy pixelsort definiál (100h-FCh=04h). Ezen LPB legelején bekapcsolódik a szinkronizáció és bekapcsolva is marad egészen a második blokk feléig. Mivel a második LPB csak egy pixelsort jelent ezért a szinkron összesen 4.5 sor idejére van bekapcsolva. Tehát még 312.5-4.5=308 sort kell definiálnunk.
A Nick minden sort 57 szakaszra oszt fel és minden szakaszban két bájtot tud beolvasni. Az első nyolc szakaszban beolvassa a soron következő LPB-t Ezután a beolvasott sorparaméter blokk alapján előállítja a aktuális képsort. A sor a képernyőn a bal és jobb margó által meghatározott helyre kerül. Az utolsó három szakaszban történik a videómemória frissítése. A bal margó legkisebb lehetséges értéke tehát nyolc, a jobb margó pedig maximum 36h lehet A margókon kívül eső terület keretszínű lesz.

A videómódokat 3 fő részre oszthatjuk. Ezek a grafikus mód, a karakteres és a szinkron üzemmód. A szinkron problémájával már foglalkoztunk, most lássuk a grafikus képmódokat. Ebből három féle áll a rendelkezésünkre: pixel, attributum és lpixel. A pixel és Ipixel módok között csak a vízszintes felbontásban van különbség, mégpedig a lpixel mód feleakkora felbontású mint a pixel. Így memóriát takaríthatunk meg, ha megelégszünk a kiesebb felbontással. Ezen videómódokban a képernyőbájtok jelentése a színmódtól függ:

A grafikus videomód harmadik tagja az attribútum mód. Ezzel a képtípussal találkozhatunk legtöbbször a játékprogramokban, mivel a ZX Spectrum csak ezt ismeri, és a legtöbb játékot Spectrum-ról konvertálták át. Ezt a videomódot az ENTERPRISE programozók nem szívesen alkalmazzák, pedig hátrányai mellet sok előnyös tulajdonsága is van. De vajon hogyan épül fel egy attribútum kép?
A leglényegesebb hogy különválik a képpont- és a színterület. Az LPB-ben az elsődleges videó adatcím (LD2) adja meg a képterület memóriacímét, a másodlagos adatcím (LD1) pedig a színterület memóriacímét. A képpontok területe hasonló a kétszínű üzemmód memóriaterületéhez, vagyis minden képpontnak egy bit felel meg. Ha a bit beállított állapotú, akkor a bitnek megfelelő pont tintaszínű lesz, ellenkező esetben papírszínű. Azt, hogy a papír és tintaszínek valójában mit jelentenek, a színterületen adhatjuk meg. A képpont-memória minden bájtjához tartozik ugyanis egy attribútum-bájt, amelynek felépítése a következő:
b0..b3 Tintaszín palettaszám. (0..15)
b4..b7 Papírszín palettaszám. (0..15)

Mint az a fentiekből is látható, az attribútum módban 16 szín használatára van lehetőségünk, olyan felbontás mellett, mint amilyen a 4 színű pixel módé. Örömünk azonban nem lehet teljes, mert egy képbájthoz csak egy attribútum-bájt tartozhat. Ebből következően az egy bájtban lévő nyolc ponthoz csak két színből választhatjuk ki az aktuálisat, hasonlóan a C64-es képkezeléséhez. A legnagyobb hátrány azonban az attribútum mód használatakor az, hogy nincs olyan rajzolóprogram, amely megfelelően kezelné azt. Még egy fontos dologra fel kell hívnom a figyelmet: az attribútum videomódot kétszínű színmóddal kell használni, az LPB második bájtjának 14h-nak kell lennie.

Ezzel a grafikus videómódok ismertetésének végére értünk, most lássuk a karakteres képmódokat!
A karakteres üzemmódokból három féle áll a rendelkezésünkre, amelyek rendre a következők: 256 karakteres (CH 256), 128 karakteres (CH 128) és 64 karakteres (CH 64) mód. A három üzemmód csak a használható karakterek számában különbözik egymástól. Mindhárom módban az LD1 tartalmazza a karakterkódok memóriacímét, ahol a kijelzendő karakterek ASCII kódjait tároljuk. Az LD2 a karakterkészlet kezdőcímét tartalmazza, elosztva az üzemmódban használható karakterek számával, tehát CH 256 módban 256-tal, CH 128 módban 128-cal és CH 64 módban 64-gyel kell osztani a karakterkészlet Nick-címét. A karakterkészlet felépítése a következő: először a karakterek első bájtját tároljuk az ASCII kód szerinti sorrendben, majd a másodikat és így tovább. Azt, hogy hány bájt (illetve pont) magas legyen egy karakter, az LPB első bájtjával tudjuk beállítani, így 1 és 256 között bármilyen magasságú karaktereket használhatunk. A karakterkészletben lévő bájtok a képernyőn a színmódtól függően kerülnek megjelenítésre, hasonlóan a pixel grafikus módnál megismertekhez. A különböző színű karakterek megjelenítéséhez az LPB-ben két vezérlőbitet -nevezetesen az LS BALT-tot és az MS BALT-tor- használhatunk.

A színvezérlő-bitek használatát nem részleteztem, ezért most ez következik.
Egy LPB-ben négy színvezérlő-bit található, ezek az ALTIND0, ALTIND1, LS BALT és MS BALT nevet viselik. Az (eredeti) EXOS leírással ellentétben mindegyik színvezérlő-bit mind karakteres, mind grafikus üzemmódban használható. Most nézzük meg részletesen az egyes bitek jelentését!

Szeretnék néhány szót szólni a színekről, merthogy erről még nem volt szó.
Valószínűleg mindenki előtt ismert tény, hogy a TV-technikában a színek három összetevőből - a vörös, a zöld és a kék színárnyalatok keverékéből - állnak. Ugyanezt a módszert használják a számítógépek is. Az ENTERPRISE 8 árnyalatot tud megkülönböztetni a vörös és a zöld színből és négyet a kékből. Ez alapján könnyen kiszámolható, hogy összesen 8 x 8 x 4=256 színárnyalatot képes megjeleníteni. Ahhoz, hogy egy színnek megállapíthassuk a kódját ismernünk kell a színkódok felépítését. Ez a következő:

b7 b6 b5 b4 b3 b2 b1 b0
G0 R0 B0 G1 R1 B1 G2 R2

Ez egy bináris szám, ahol R a vörös, G a zöld, B a kék szint jelenti, a betűk melletti szám pedig a színerősség bináris értékének helyértéke. Például, ha 5-ös erősségű vöröset akarunk előállítani, akkor R2 R1 R0=101b vagyis a színkód 01010001b=81h. Ezek alapján már bármilyen szín kódját kiszámolhatjuk.

DEVIL - Enterpress 1990/2-1991/4

Tömbök rendezése
A számítógép - mint tudjuk - sok mindenre jó. Adatok, adatbázisok kezelésére különösen. Egy ilyen állományban sokkal gyorsabban érhetünk el információkat, ha a belsejében rend van. A karbantartó munkát valamilyen rendteremtő algoritmusra kell bíznunk. Rengeteg ilyen létezik, mi ezek közül most a beszúrásos rendezéssel (InsertionSort) ismerkedünk meg.
Összekevert számsort rendezünk növekvő sorrendbe. A beszúrásos módszer a következőket teszi: mozgat egy mutatót, amely az éppen feldolgozás alatt álló adatra mutat. A mutató a második pozíciótól az utolsó felé halad. Összehasonlítja a mutatónál lévő adatot az előtte levő, eggyel kisebb sorszámú adattal. Amennyiben a kisebb sorszámú adat értéke kisebb vagy egyenlő, mint az aktuális, akkor nincs semmi tennivalója csupán a mutatót kell eggyel növelnie. Ellenkező esetben a mutatónál lévő adatot megjegyzi, és elkezdi a mutató értékénél kisebb sorszámú adatokat egy hellyel "jobbra" eggyel nagyobb sorszámú pozícióra mozgatni, az aktuális mutatóértéktől lefelé a kisebb sorszámok felé haladva egészen addig, amíg a megjegyzett adatnál kisebbet vagy azzal egyenlőt nem talál. Ekkor a megüresedett helyre beírja a megjegyzett adatot, majd növeli a mutatót, és folytatja a rendezést.
A könnyebb érthetőség kedvéért nézzük a következő, félig már rendezett számsort:

Sorszám 1. 2. 3. 4. 5. 6. 7.
Adat 15 27 53 21 77 22 15

Legyen a mutató értéke éppen 4. Összehasonlítja a 21-et a 53-mal. A 21 nyilván rossz helyen van, megjegyzi a számot. Elindítja az adatok mozgatását "jobbra" a 3. pozíciótól kezdve a csökkenő sorszámok felé, egészen addig, amíg a 21-nél kisebbet vagy azzal egyenlőt nem talál. A mozgatás befejezésekor a 21-et beszúrja a megfelelő helyre. A módosult sor:

Sorszám 1. 2. 3. 4. 5. 6. 7.
Adat 15 21 27 53 77 22 15

Növeli a mutató értékét, így az az 5. adatra mutat. A 77 viszont nagyobb, mint az 53, tehát nincs semmilyen művelet, csak a mutatót kell növelnie. A rendezés véget ér, ha a mutató elérte a tömbhatárt, és az utolsó mozgatás is véget ért. Az algoritmust Basic-ben a listán látható program valósítja meg:

Az "A" tömb elemeit a "TOLT" inicializálja, a "KIIR" megjeleníti, a "REND" rendezi. Természetesen a "REND" eljárás másik programban is használható, a tömbnek új nevet, módosított hosszat is adhatunk. A legnagyobb tömbméretnek a gép memóriája és szabad időnk nagysága szab határt. A programot 200 elemmel próbáltam ki: a futási idő 4 perc 40 másodperc volt. Valószínű, hogy TURBO ENTERPRISE-on valamivel jobb eredmények születnének.
Ezek szerint nem elég hatékony a beszúrásos rendezés? Ebben az állapotban tényleg nem. De van megoldás: ZZZIP compiler! A rendezés tipikus példája a ZZZIP alkalmazásának. A program simán "lefordul", és 200 adat esetén 5 másodperc alatt lefut. Az idők önmagukért beszélnek, a javulás közel 60-szoros!
Természetesen 200 adat nem számít nagy mennyiségnek, komolyabb alkalmazásoknál több ezerrel kell számolni. Ezzel már a fordított Basic program is nehezen bírkózik meg. Hétezer adatnál negyedóra elteltével meguntam a várakozást. A rendezést úgy gyorsíthatjuk, ha más, hatékonyabb algoritmust állítunk munkába, és igazi, compiler típusú nyelven (Pascal) írjuk meg az adatrendező programot.

Enterpress 1990/1

10 PROGRAM "INSORT.BAS"
11 DEF TOLT
12   FOR I=1 TO UBOUND(A)
13     LET A(1)=RND(5000)
14   NEXT
15   LET A(0)=-INF
16 END DEF
17 DEF REND
18   FOR I=2 TO UBOUND(A)
19     IF A(I)<A(I-1) THEN
20       LET M=A(I)
21       LET Z=I-1
22       D0
23         LET A(Z+1)=A(Z)
24         LET Z=Z-1
25       LOOP UNTIL A(Z)<=M
26       LET A(Z+1)=M
27     END IF
28   NEXT
29 END DEF
30 DEF KIIR
31   FOR I=1 TO UBOUND(A)
32     PRINT I,A(1)
33   NEXT
34 END DEF
35 REM *** FOPROGRAM ***
36 RANDOMIZE
37 NUMERIC A(0 TO 20)
38 CLEAR SCREEN
39 CALL TOLT
40 CALL KIIR
41 CALL REND
42 CALL KIIR
43 END

Betöltési problémák
Valószínűleg az összes EXDOS felhasználónak az volt az első dolga, amikor üzembe helyezte lemezegységes rendszerét, hogy a kazettán lévő programjait átmásolta lemezre. A Spectrum-ról átírt játékprogramok másolásánál valószínűleg nem is volt sok gondja, csak a magnó lassúsága. Az eredeti ENTERPRISE játék- és felhasználói programok nagy része azonban már több gondot okozott. Most az ilyen programok lemezessé konvertálásához szeretnék néhány hasznos tanácsot adni.
A gép megjelenésekor még nem lehetett lemezmeghajtót illeszteni az ENTERPRISE-hoz, ezért az akkor készült programok eleve magnós konfigurációhoz készültek. Átírás szempontjából ezek a programok is több csoportba sorolhatók.
Általában minden programnak van egy betöltője, ami lehet egy rövid Basic program, vagy egy gépi kódú, 5-ös fejléccel rendelkező, ún. új alkalmazói program (NAP=New Application Program). Ez a betöltő elindulása után beolvassa a teljes programot, majd átadja annak a vezérlést.
Ahhoz, hogy be tudjunk tölteni valamit, először meg kell nyitni egy csatornát egy eszközre. Basic-ben ez egy

OPEN £csatorna:"eszköz:fájlnév"

utasítással lehetséges, ahol a csatorna a csatorna száma, az eszköz lehet TAPE: a magnó esetében, a lemezegységnél pedig a meghajtó azonosítója. Ha nem írunk eszköznevet, akkor a csatorna az alapértelmezésű eszközre fog vonatkozni, ami alapkonfigurációnál a magnó. Ha a géphez lemezegységet csatlakoztatunk, akkor az lesz az alapértelmezésben használt eszköz. Lehetőség van arra is, hogy fájlnevet sem adunk meg, ekkor a magnós konfigurációnál a szalagon elsőnek megtalált fájlt, lemeznél pedig a START nevű fájlt nyitja meg az EXOS.
A csatornanyitás gépi kódban sem sokkal bonyolultabb. Az A regiszterbe a csatornaszámot, a DE regiszterpárta pedig a fájlnév memóriabeli kezdőcímét kell betölteni, és végül egy EXOS 1 hívást kiadni. A fájlnév megadása hasonló, mint a Basic-ben, azzal a különbséggel, hogy az első bájt a karakterfüzér hosszát adja meg bájtokban.
A két lehetőség keveredésével is találkozhatunk: a betöltő Basic nyelvű, de gépi kódban történik a beolvasás. Ilyenkor a Basic program az ALLOCATE és CODE utasításokkal előállít egy gépi kódú rutint, majd CALL USR utasítással meghívja azt.
Az eddigi információk alapján már valószínűleg érthetővé válik az a jelenség, amelynél egy lemezre másolt programot próbálunk betölteni, és az első blokk után leáll a gép, amely aztán kazettáról akar olvasni, vagy egyszerűen lefagy. Ha az első eset fordul elő -amikor kazettáról akar a gép olvasni-, akkor a csatornanyitásban a TAPE: eszköznév szerepel. Ezen lehet a legkönnyebben segíteni, egyszerűen át kell írni a TAPE: szöveget a program második fájljának nevére.
Ha a betöltő Basic és találunk benne egy OPEN £csatorna:"TAPE:..." utasítást, akkor azt javítsuk ki úgy, hogy az idézőjelben csak a fájl neve szerepeljen. Ha a betöltő gépi kódú, akkor töltsük azt be egy assemblerbe (célszerű az ASMON-t használni, mert az megadja a töltés végcímét), és keressük meg a TAPE: stringet. Ha megtaláltuk, javítsuk ki, de vigyázzunk rá, hogy a hosszbájt is jó legyen. Basic-ből hívott gépi kód esetén a HEX$ függvény argumentumában kell megkeresnünk az "54,41,50,45,3A" számsort, ami a TAPE: sztring ASCII megfeleltje hexadecimális alakban. Ezt már könnyedén ki tudjuk javítani (itt is figyeljünk a hosszbájtra).
A másik esetben nem is próbál a gép továbbtölteni, hanem csak lefagy. Ez akkor fordul elő, ha a megnyitáskor sem eszköznév, sem pedig fájlnév nem volt megadva. Magnónál ez nem gond, mert a soron következő fájl betöltődik. Az EXDOS azonban megnézi, hogy van-e START nevű fájl a lemezen, és ha nincs, akkor egy hibakóddal visszatér. Ez az eset Basic betöltőnél nem fordul elő. Gépi kódnál (Basic-ből hívottnál is) meg kell keresni, hogy hol nyitja meg a csatornát a program. Ez gépi kódban egy EXOS 1 hívást, hexa-dumpnál egy "F7 01" számsort jelent. A hívás előtt mindig van egy "LD DE,cím" utasítás, ahol a "cím" a fájl nevének memóriabeli kezdőcímével egyenlő. A hexa-dumpban ez a következőképpen néz ki: "11 x1 x2", ahol az x1 az alsó, x2 a cím felső bájtja tehát a cím=x1+256*x2. Ha cím által meghatározott memóriarész tartalmát megnézzük, akkor látjuk, hogy ott egy 00 bájt szerepel. Általában itt nincs is akkora hely, amelyen elférne a betöltendő fájl neve, így kell keresnünk valahol annyi üres helyet a programban, ahol a név elfér (ha máshol nem, akkor a program végén). Ide beírjuk a fájl nevét hosszbájttal az elején, majd azt a címet ahová írtuk, beírjuk az "LD DE,cím" utasításba operandusként (xl, x2 helyére). A gépi kódú programot az assemblerbe az l0F0h címre érdemes tölteni, így a programban szereplő címek 1000h értékkel lesznek eltolva.
Ezek voltak az egyszerűbb esetek (már amennyire ez egyszerű lehet). Ezeknél valamivel bonyolultabb a helyzet, ha nem egy fájlt tölt be a betöltő, hanem többet és mindegyikhez ugyanaz a fajlnév van rendelve (ami lehet 00 is). Ebben az esetben mindegyik csatornanyitásnál át kell írni a majdani fájlnevek kezdőcímeit (ezek kerülnek DE-be) különböző értékekre (ott, ahol erre van hely a programban), és az egyes címekre be kell írni a fájlneveket. Ilyenkor általában már csak a program után van annyi helyünk, ahol ez elfér, tehát hosszabb lesz a betöltő. Ha az 10F0h címre töltöttük a programot, akkor az 10F2h címen található a hossza, amit át kell írni az új hosszértékre.
Ezeknél bonyolultabb esetek megoldására már csak a gépi kódú programozásban jártasak vállalkozhatnak eredményesen. Ilyen eset az, amikor a program konkrét memóriacímeket használ a rendszerszegmensben, vagy olyan memóriaterületet igénylő video-csatornát akar megnyitni, amelyhez nincs elég hely, ugyanis az EXDOS lefoglal kb. 4 kilobájtot. Ekkor a fájlok betöltése után elfelejtetjük a géppel a lemezegység meglétét, amit a következő módon lehet elérni: a rendszerszegmensben az EXOS ROM-ok táblázatot át kell írni úgy, hogy csak az 1-es és a Basic szegmens maradjon meg. Az ABBDh címtől 0,0,0,0,1,0,0,0,x,0,0,0,0 számsort kell beírni ahol az "X" a Basic szegmens száma (angol gépnél 4, németnél 5), majd a BF95h és a BF97h címre (a rendszerszegmensbe) ABBDh-t kell írni. Végezetül egy EXOS 0 hívást kall végrehajtatni a C regiszter 0 értéke mellett.
Az eddig felsorolt esetek a régebben (1985) készült programoknál fordultak elő. Az újabb készítésű programokat - gondolok itt az "a" Studio és a Novotrade programjaira - már szándékosan készítették úgy, hogy azok csak magnóról induljanak el, ez ugyanis egyfajta programvédelemként szolgál.
Az egyik megoldás az, amikor a programot saját betöltővel látják el, így ezeket a programokat az EXOS nem képes beolvasni. Ilyenek például az "EGGS OF DEATH", a "MIRROR_WORLD" című játékprogramok. A megoldás az, hogy a fájlokat a saját loaderükkel betöltjük, majd EXOS formátumban kimentjük. A betöltőt pedig átírjuk olyanra, hogy az az EXOS-t használja beolvasásra a speciális loader helyett.
A másik programvédelmi lehetőséget a "FINE PEN" sprite-tervező programnál figyelhetjük meg. Az elv a következő a betöltő egy 6-os fejlécű abszolút rendszerbővítő, amely a betöltődés után elindul. Korrekt működés helyett azonban végrehajt egy EXOS 0 hívást, melynek hatására újrainicializálódik a beépített és bővítő perifériák, valamint rendszerbővítők. Az alapperifériák után a rendszerbővítő ismét megkapja a vezérlést, hogy inicializálódjon. Ehelyett ismét mást csinál: átveszi a vezérlést, és elkezdi betölteni a főprogramot. Mivel csak az alapperifériák inicializálása történt meg, ezért az EXOS nem tud róla, hogy lemezegység is van a konfigurációban.

DEVIL - Enterpress 1991/1.

Hanoi Tornyai

A buddhista szerzetesek időtlen időkig üldögéltek templomaikban, igyekezve megoldani a Hanoi torony néven közismertté vált feladványt, biztosan tudva, hogy amint ez sikerül valamelyiküknek, igen nagy mennyei boldogság lesz az osztályrésze.
A feladat lényege, hogy a réz rúdra 64 darab lyukas korong van feltéve, ezek mindegyike valamivel kisebb az alatta lévőnél. A 64 korongot át kell tenni az arany rúdra, de egyszerre csak egy korong mozgatható, és bármelyik korong mindig csak nála kisebb korongra tehető rá. Segítségképpen (egy mai szakember azt mondaná, pufferként) igénybe vehető az ezüstrúd.
A feladat igen egyszerűnek látszik. Néhány ügyes mozdulattal sikerülhet is átpakolnunk 3, vagy akár 4 korongot is a szabályoknak megfelelően az aranyrúdra. Minél tovább megyünk azonban, annál kilátástalanabbnak tűnik a megoldás. Pedig nincs nagy vész, a 3 korongot 7 lépésben tudjuk átrakni, a 4 korongot 15 lépésben, az 5 koronghoz is csak 31 jól megválasztott lépés kell... Megállapíthatjuk, hogy n korong átrakásához (2^n)-1 lépés szükséges.

Mi már tudjuk, hogy a szegény szerzetesek igen reménytelen feladatba kezdtek. A 64 korong átrakása ugyanis (2^64)-1,azaz mintegy 1,8*10^19 lépés szükséges. Ez másként leírva 18446744073709551615 (vagyis az a maximális szám, amit 64 biten fel lehet írni). Ez olyan nagy szám, hogy elképzelni is lehetetlen. Másodpercenként 1 áthelyezést végezve éjjel-nappal, megállás nélkül, (86400*365,24 áthelyezés évente) kb. 585 milliárd év alatt végeznénk. Jó lesz hát sietni! Egy jó tanács: ne próbáljuk kivárni! A Föld "mindössze" 4,54 milliárd éves, és Nap 10 milliárd éves várható életkorának felénél járunk, mivel nagyjából még 5 milliárd évre elegendő a hidrogénkészlete a fúziós reakció táplálására.
Csak megjegyezzük, hogy véletlenül majdnem pont (2^64)-1 búzaszemet kapott volna a monda szerint a sakkjáték feltalálója jutalmul, ő a sakktábla minden mezejére kétszer annyi szemet kért, mint az előzőre. Az ő jutalmát majdnem 3 millió, egyenként egymillió tonnás óriás tartályhajó tudná elvinni...
A program bemutatja a Hanoi torony feladat megoldásának egy részletét. A program lényege a TORONY nevű rekurzív (azaz önmagát újra és újra meghívni képes) eljárás, a többi csak díszlet. A program legfeljebb 13 korongot képes átpakolni, de ehhez több mint 2 óra szükséges.

UL&BG - Enterpress 1991/2.

ENTERPRISE képviselet nyílt Budapesten
A képviselet meglétéről szerkesztőségünk már tavaly augusztusban tudomást szereztünk, hiszen a képviselet emberei hallva az ENTERPRESS készülődéséről, azonnal felkerestek bennünket. Olvasóinkat erről azért nem tájékoztattuk eddig mert az augusztusi találkozónk csak egy futó egyeztetés volt, igazából konkrét információkat még nem tartalmazott.
Sajnos rögtön a cikk legelején el kell árulnunk, hogy még mindig nincs itt az "ENTERPRISE kánnán": A VTGe Elektronikai Kft. - az ENTERPRISE Computers GmbH magyarországi képviselete - nem üzletet, hanem csak egy irodahelyiséget nyitott Budaörsön. Hogy pontosan mivel is foglalkoznak arra sok egyéb kérdésünk mellett Kopácsy Vilmos ügyvezető igazgató válaszolt.
A Kft. mostanában leginkább a gépek Szovjetunióbeli elterjesztésével foglalkozik - mondja Kopácsy úr - de az ottani belpolitikai helyzet miatt ez kissé vontatottan halad. A szovjet partner a gép liszenszét szeretné megvásárolni, a gépet pedig ottani alkatrészekből akarja összerakni. Úgy képzeli, hogy a gyártásnál mi csak a háta mögött állnánk. Még a Dave-et és a Nick-et is ó akarja az alaplapra tenni.
- Hány gép van már a Szovjetunióban?
Pontosan nem tudom, de úgy háromezer körül lehet a számuk. A gépeket hálózatba kapcsolva kazahsztáni iskolákban használják, és a gépek összerakása is itt folyna. Nagyobb érdeklődés mutatkozik az ENTERPRISE PC-k iránt.
- Mitől lesz ENTERPRISE egy PC?
A PC-k alaplapját az ENTERPRISE cég tervezte, és az alaplapokat Németországban készítik. Kínálatunk az XT termináltól kezdve egészen a 486-osig tart.
-Térjünk vissza a gépünkre! Úgy hallottuk, hogy Egyiptomban is van egy nagyobb adag belőle.
Igen, ott a 64K-s változatból kelt el négyezer darab.
- Milyen új szoftvereket várhatnak a hazai felhasználók?
Az "a" Studio programozói pillanatnyilag 15 játékprogramon dolgoznak, a Centrum megbízásából pedig 10 ilyen készül.
- És a játékprogramokon kívül készülnek-e más programok?
Most dolgozunk egy feliratozó szoftveren, amelyhez egy ún. genlocker kártya is kell. Ez utóbbinak az a feladata, hogy a kamera és a számítógép képjelei korrekt módon keverhetők legyenek. A számítógép és egy szintetizátor összekapcsolásához szükséges Midi interfész és szoftver fejlesztése is folyik.
- Milyen újdonságok vannak még?
A 128K-s alapgéphez ma már akár 160MB-os merevlemez is kapcsolható. Elkészült az az illesztőnk is, amellyel a PC-khez kapcsolható kártyák nagyrésze az alapgépbe dugható. Így akár VGA monitort is kapcsolhatunk a konfigurációnkhoz.
- Mikor kezdik az új termékek árusítását?
A szoftver és a hardverfejlesztés egyelőre nem rentábilis. Amíg a régi készleteink is megvannak, semmiképpen sem fogunk bele az árusításba.
- Mik ezek a régi készletek?
Főleg a 4000 darab Spectrum-emulátort és az SzJA'88 programot említhetném.
- Mekkora ennek a készletnek az összértéke, és hol van?
Összértékük ötvenmillió forint, és a Centrum raktáraiban vannak.
- Úgy hallottuk, hogy a Novotrade Rt. gondozásában megjelent "EXOS ROM0 visszafejtése" című könyvvel kapcsolatban valamilyen galiba volt...
Igen, a Novotrade kinti megbízottját beperelte az ENTERPRISE GmbH. Ez a megbízott a harmadfokú tárgyalás után többszázezer márka kártérítést fizetett az ENTERPRISE-nak. A ROM visszafejtésének közléséhez ugyanis az ENTERPRISE cég engedélye szükségeltetett volna...
- Megbízható forrásból tudjuk, hogy a könyv szerzője már elkészült a 1. szegmens visszafejtésével is. Ezek szerint ez nem fog megjelenni... Az ENTERPRESS az olvasók kívánságának megfelelve szeretné a gép és az EXDOS kapcsolási rajzát közölni. Mit szól ehhez?
Nem értem, hogy egy amatőrnek miért van szüksége a gép kapcsolási rajzára! Ez a cég belső magánügye.
-Talán így még inkább fellendülnének a fejlesztések...
Aki kapcsolási rajzot akar szerezni a gépről, az már ezt bármelyik klubban megteheti. Egy ilyet nekem is adtak, pedig tudták, hogy ki vagyok. Az ENTERPRESS egyébként azt közöl amit csak akar, de vállalnia kell a következményeit is.
- Hmmm... Sok olvasónk panaszolja hogy gondjai vannak az EP-PLUS kártyával, és hiába várnak választ az "a" Studio-tól.
Az "a" Studio-tól a VTGe Kft. hamarosan átveszi a leveleket, és mindegyikre válaszolni fog.
- Készül-e új ENTERPRISE modell?
Igen, bár itt alapvetően az 128K továbbfejlesztéséről van szó. Az új modell külsőleg leginkább egy lapos PC-hez hasonlítható, külső billentyűzettel. Alaplapján lesz az EXDOS, be lesz építve a Midi interfész, és a hardveres hálózat kezelő. Azt, hogy mekkora központi memóriája lesz, majd az akkori árak fogják eldönteni.
- Milyen processzorral és operációs rendszerrel lesz a gép felvértezve?
Valószínűleg a Hitachi Super Z80-asa lesz a gépben. Ennek a processzornak 21 bites címbusza van, és tartalmazza a matematikai kooprocesszort is. A gép operációs rendszere pedig az EXOS 3.0-ás verziója lesz.
- Ez mit tud ?
Mindazt amit az EXOS 2.1-es változat. Ezenkívül jellemzője, hogy felhasználói felülete csaknem teljesen grafikus, és a rendszer ikonvezérelt. A felület az EGI (ENTERPRISE Graphics Interface) névre hallgat, és leginkább az Atari GEM-re hasonlítható.

Szerkesztőségink nem állhatja meg hogy megjegyzéseit a cikk végére ne írja. Mint ahogyan azt Kopácsy úr is mondta a fejlesztések nem hozzák meg a kellő bevételt. Így nem várhatjuk, hogy a VTGe Kft. csupán emberbaráti szeretetből elhalmoz bennünket jobbnál jobb hardver és szoftver termékekkel. Az ENTERPRISE GmbH természetesen figyeli a magyarországi felhasználók sajátos helyzetét, de igazán döntő lépéseket nem tud tenni.
A beszélgetés során rengeteg szakmai kérdést tettünk fel Kopácsy Vilmosnak aki - lévén, hogy kisujjában van a gép - készséggel válaszolt ezekre. Ha egyébként olyan olvasói kérdések érkeznek hozzánk, melyeket szerkesztőségünk nem tud megválaszolni, akkor azokat átadjuk a képviseletnek.
Az új, fejlesztés alatt álló ENTERPRISE modellel kapcsolatban van néhány felvetésünk, mindenféle rossz szándék nélkül. Bár nem tudjuk, hogy mikorra készül el a gép, de ha már most sorozatgyártásban lenne, szerintünk már akkor is elkésettnek számítana. Nem hisszük hogy a mind olcsóbbá váló PC-knek, Amigáknak Atariknak komoly ellenfél lehet egy ilyen adottságú gép. A másik problémát pedig az új gépre nem létező szoftverek okozzák majd.
Persze mi felhasználók már sokszor tapasztalhattuk, hogy az ENTERPRISE fronton nehéz bármit is előre jósolni. Amíg van időnk és türelmünk, addig várjuk ki ennek is a végét.

Enterpress 1991/2.

Az ismeretlen SPOKE és SPEEK
Olvasóink kőzűl jónéhányan kifogásom hogy a kőzőlt programok egy részében magyarázat nélkül haszná juk a SPOKE és a SPEEK utasításokat. Kicsit értetlenül fogadjuk ezeket a kifogásokat, hisz nincs ezekben az utasításokban semmi különleges. Szeretnénk azonban elejét venni a további zsörtölődésnek így most egy külön cikket szentelünk e témának.
Gépünk 128KB-nyi RAM-mal rendelkezik, ezt a memóriát a gép tervezői 16KB-os részekre, ún. szegmensekre osztották. A teljes RAM az alapkiépítésű gépben 8 darab ilyen szegmensre tagolódik. A prospektusokból megtudhatjuk, hogy a gép maximálisan 4MB-os memóriát tud kezelni, ebbe a ROM és a RAM szegmensek egyaránt beleértendők. A Z80-as CPU 64KB memóriát képes egyszerre kezelni, így a CPU-n egy időben négy szegmens lóghat. A CPU a lapjain látja a szegmenseket. A lapok mérete igazodik a szegmensek méretéhez, egy lap egy 16KB-os tartományt fed le. A laptartományok:

0. lap: 00000-16383
1. lap: 16384-32767
2. lap: 32768-49151
3. lap: 49152-65536

A szegmensek cserélgetését memórialapozásnak, röviden csak lapozásnak nevezzük. Fizikailag a lapozás egy meglehetősen komplex feladat, nem véletlenül szükséges hozná egy külön, memórialapozó funkciókat (is) ellátó kooprocesszor, a Dave. Basic-ból is van lehetőség a direkt memórialapozásra: a 176-179 portok tartalma dönti el, hogy a CPU adott lapján melyik szegmens van. A 176-os port a 0. laphoz, a 179-es port a 3. laphoz tartozik; az IN és az OUT utasításokkal e portok tartalmát szabadon olvashatjuk, írhatjuk. (A 0. és a 3.lapokat lehetőség szerint ne zaklassuk!)
Minden szegmensnek saját azonosítószáma van, ezt szokás szegmensszámnak (vagy nagyzolós körökben "szegmenscímnek") nevezni. Például az EXDOS vezérlőprogramja a 32-es és a 33-as ROM szegmenseken helyezkedik el. Ha igazán jó programokat akarunk készíteni, akkor a szegmensek számozását és szerepüket pontosan kell ismernünk. Lehetetlenség lenne e cikk, de akár a teljes lap terjedelmében a gépben lévő szegmenseket, feladatukat ismertetni, így ettől eltekintünk. Itt most elegendő annyi ismeret, hogy az EXOS a RAM szegmenseket a 248-255 tartományba sorolja, és a 255-ös szegmens az ún. rendszerszegmens. Az alapvető lehetőségeknél (pl. gépi kód, POKE, PEEK) tudnunk kell, hogy melyik szegmensen és a szegmensen belül melyik címen lévő adattal akarunk műveleteket végezni, és hogy mindezt a CPU melyik lapján kívánjuk tenni.
A két utasítás (SPOKE, SPEEK) elsősorban a közvetlen memórialapozástól mentesít bennünket. Basic-ben tehát elegendő a szegmensszámot, és a szegmensen belüli címet az ún. ofszetcimet tudnunk, a tár állapotának könyvelésével (melyik szegmens melyik lapon van stb.) nem kell foglalkoznunk.

A SPOKE (Segment POKE) paranccsal bájtot tudunk letárolni egy adott szegmens ofszetcímére, az utasítás helyes formája:
SPOKE(szegmensszám,ofszetcím,érték)
A szegmensszám 0 és 255, az ofszetcím 0 és 16383, az érték 0 és 255 közötti, illetve azzal egyenlő is lehet. Az ofszetcím helyére a megadottnál nagyobb értéket is felvehetünk, az utasítás csak a cím alsó 14 bitjét veszi figyelembe. Így ha például a SPOKE(200,0,0) helyett SPOKE(200,65536,0) írunk, ugyanazt az eredményt kapjuk. Konkrét példaként színezzük át a képernyő első sorát a hagyományos POKE, majd pedig a SPOKE utasítással!
Basic alaphelyzetben a sorparaméter-tábla (ld. LPT sorozat) első COL0 bájtja a 47368-as címen van. Ha ekkor kiadjuk a

POKE 47368,255

parancsot, akkor a képernyő tetején egy fehér csík lesz látható. A sorparaméter-tábla mindig a 255-ös sorszámú szegmensen van, ez lesz tehát a szegmensszám. Az ofszetcímet megkaphatjuk, ha vesszük a cím 16384-es modulusát (osztás utáni maradékát), a MOD(47368,16384)-et. Ennek eredménye 14600, jöhet a

SPOKE 255,14600,255

parancs, az eredmény ugyanaz, mint fent. Van azonban egy igen lényeges különbség: ha valamilyen okból eltűnik a 2-es lapról a 255-ös szegmens, és mi kiadjuk a POKE 47368,255 parancsot, akkor egészen biztosak lehetünk abban, hogy rossz helyre ment a 255-ös érték. A POKE paranccsal egyébként is csínján kell bánni, mert igencsak katasztrófális helyzeteket idézhetünk elő a rendszerben... Ugyanez nem fordulhat elő a SPOKE-al! A ROM Basic ugyanis saját, belső memórialapozásával gondoskodik arról, hogy az érték garantáltan jó helyre kerüljön, megmentve a felhasználót ettől a feladattól.

A SPEEK (Segment PEEK) függvénnyel bájtot tudunk beolvasni egy szegmens adott címéről, a forma:
érték=SPEEK(szegmensszám,ofszetcím)
A szegmensszámra, az ofszetcímre, az értékre ugyanaz vonatkozik, mint ahogy azt fentebb megtudtuk.
Az előző példa folytatásaként olvassuk be a szín kódját, de a "látvány" kedvéért lapozzuk le a 255-ös szegmenet a 2-es lapról, adjuk ki:

OUT 178,0

Most teljesen közömbös, hogy miért épen a nullás szegmenet rakjuk a helyébe. A lényeg az, hogy ha a

PRINT PEEK(47368)

parancsot végrehajtja a Basic, akkor 252-öt fog kiírni, ami biztosan rossz, hiszen az imént 255-öt küldtünk oda (csak éppen másik szegmensre). De a

PRINT SPEEK(255,14200)

után a helyes 255 jelenik meg a képernyőn. A példa egy kicsit erőltetett, hiszen mi magunk tüntettük el a szegmenet, de ugyanezt az EXOS is teljes joggal megtehette volna.

Ha valaki elsősorban Basic-ben írja programjait, akkor előbb-utóbb gyorsítani szeretné őket, ekkor veti be a Zzzip Basic fordítót. Köztudott, hogy a Zzzip ún. integer fordító, azaz a programban szereplő számok csak a -32768...+32767 tartományba eshetnek. Ha egy ilyen programba a minimál Basic POKE, PEEK parancsait használjuk, akkor nagy valószínűséggel az őket követő címértékek láttán kiakad a fordító. Ezt a problémát a SPOKE, SPEEK kiküszöböli.
A cikk közepe táján azzal büszkélkedtünk, hogy a két utasítást használva nincs szükségünk memórialapozásra. Ez így is van, de ne feledkezzünk meg a 16KB-nál nagyobb memóriatartományok kezeléséről sem! Tegyük fel, hogy megnyitottunk egy grafikus lapot, amelynek mérete 20KB, azaz két videoszegmensen helyezkedik el. Ha ezt a 20KB-os tartományt kezelni akarjuk, akkor a műveletek előtt mindig el kell tudnunk dönteni, hogy az ofszetcím éppen melyik szegmensre vonatkozik.
Ha a közvetlen memóriakezelés vagy a SPOKE/SPEEK nyújtotta lehetőségek között kell döntenünk, akkor ugyanúgy járhatunk csak el, mint általában: a feladat pontos ismeretében, alapos mérlegelés után kell a megfelelő megoldást kiválasztanunk.

ZozoSoft kiegészítése:
Elegánsabb megoldás, ha nem találomra választunk nagyobb mennyiségű adat tárolására szegmenst, hanem az EXOS-tól igénylünk:

ALLOCATE 9
CODE GETSEGMENT=HEX$("F7,18,67,69,C9")
CODE FREESEGMENT=HEX$("4D,F7,19,C9")
LET WS=USR(GETSEGMENT,0)

WS-be kerül a kiutalt szegmens száma, melyet ettől kezdve szabadon felhasználhatunk adattárolásra. Természetesen így akár egymás után több szegmenst is igényelhetünk!
Ha WS értéke nagyobb mint 255, akkor hiba volt (ez lehet megosztott szegmens lefoglalásakor is), ekkor a felsőbájt (azaz az érték/256) a hibakód.
A programfutás befejezése előtt "illik" felszabadítani lefoglalt szegmenst (különben hamar elfogyhat a memória!):

CALL USR(FREESEGMENT,WS)

Ha valaki visszaretten a memóriakezelés eme módjáról, használhatja HSOFT ugyanezen elv szerint működő MEM nevű BASIC-bővítését, mely leveszi a vállunkról a közvetlen memóriakezelés terhét.

Enterpress 1991/3.

A közvetlen lemezkezelés rejtelmei
Az LPT sorozat befejezése óta nem sok újdonságot olvashattak az igazán profi ENTERPRISE programozók Most azonban ismét itt van egy olyan téma amelyben az ENTERPRISE legmélyebb lelkivilágába próbáljuk elvezetni a bátrabb olvasókat. Azt általában minden lemezegységgel rendelkező tudja, hogy a kontroller-kártya agyát egy WD1770-es vagy újabb változatokban egy WD1772-es floppy disk kontroller chip alkotja. De ezen IC közvetlen programozását már kevesen ismerik, ezért most erről lesz szó.
A WD1770 egy 28 lábú DIP tokozású NMOS alapú integrált áramkör, amely az egy- és kétoldalas lemezegységek meghajtására egyaránt alkalmas, de csak szimpla és dupla sűrűségű felírásra képes. Tehát normál esetben maximum 800 Kbájtos lemezeket képes kezelni. A WD1772 csak annyiban különbözik a 1770-estől, hogy gyorsabb az író/olvasó fej léptetése. Ennyi bevezető után rá is térhetünk a lényegre, vagyis a programozásra.
A koncrollerrel négy írható / olvasható porton keresztül lehet kommunikálni, ezek sorban a következők:

  Olvasásnál: Írásnál:
10h Állapot regiszter Parancs regiszter
11h Track regiszter Track regiszter
12h Szektor regiszter Szektor regiszter
13h Adat regiszter Adat regiszter

Ezeken kívül van még egy port amely a kontroller-kártyához tartozik, a 18h port, ennek használatáról később lesz szó, ugyanis ez nem a WD1770-es közvetlen portja- A kiadható parancsok a kővetkezők:

7 6 5 4 3 2 1 0 -bit
0 0 0 0 h V r1 r0 - Fej alaphelyzetbe álirtása
0 0 0 1 h V r1 r0 - Fej pozícionálása
0 0 1 u h V r1 r0 - Fej léptetése
0 1 0 u h V r1 r0 - Fej léptetése befelé
0 1 1 u h V r1 r0 - Fej léptetése kifelé
1 0 0 m h E 0 0 - Szektor olvasása
1 0 1 m h E P a - Szektor írása
1 1 0 0 h E 0 0 - Cím olvasása
1 1 1 0 h E 0 0 - Track olvasása
1 1 1 1 h E P 0 - Track írása
1 1 0 1 I3 I2 I1 I0 - Parancs megszakítás

A táblázatban használt betűjelzések jelentése:

Az állapotregiszter bitjeinek jelentése:

A lemezműveletek használata a következő: a megfelelő parancs kódját be kell írni a D parancsregiszterébe a 10h porton keresztül. Ezt csak akkor tehetjük meg, ha az állapotjelző bájt 0. bitje, vagyis a foglaltság jelző törölt állapotú. A művelet befejezése után a státuszbájt kiolvasásával ellenőrizhetjük, hogy a folyamat hibamentesen zajlott-e le vagy, hogy milyen hiba tőtrént. Most pedig következzen az egyes parancsok részletes leírása.

Fejmozgató parancsok
Ezen utasítások mindegyikénél a parancsbájt alsó két bitje adja meg a fejléptetés sebességét, vagyis, hogy két léptető impulzus kiadása között mennyi legyen a várakozási idő.
Az alaphelyzetbe állító parancsnak semmilyen más adatra nincs szüksége, hatására az író/olvasó fej a 0-ás sávra áll be. A művelet befejeztével a track-regiszter nullázódik.
Fej pozicionáló parancs kiadása előtt a Track regiszterbe az aktuális track számát kell beírni, az adatregiszterbe pedig a beállítandó sáv számát. Az utasítás befejeztével a fej a kívánt sávon fog állni és a track-regiszter is ezt az értéket fogja tartalmazni.
A léptetés paranccsal egy pozícióval léptethetjük a fejet abba az irányba amelyet utoljára használtunk. Az u-val jelzett bittől függően a WD a Track-regisztert is beállítja.
A fej befelé léptetésével a nagyobb számú sávok felé, a kifelé léptetéssel a 0-ás sáv felé lép a fej. Itt szintén az u bit állapotától függ, hogy a Track-regisztert is növeli / csökkenti vagy sem.

Szektorműveletek
Szektor írási és olvasási utasításoknál a parancskód kiadása előtt be kell állítani a fejet a kívánt sávra, majd be kell írni a szektor-regiszterbe a megfelelő szektor számát. Az E bit állapotától függően a parancs kiadása után vagy azonnal megkezdődik a végrehajtás vagy egy 30ms-os késleltetés után. Ha nem találja a megcélzott szektort, akkor szektor nem található hibával befejeződik a végrehajtás. Ha az m bit beállított állapolt, akkor az írás / olvasás nem csak egy szektorra vonatkozik, hanem egészen addig tart amíg a sávon található legnagyobb számú szektor is sorra kerül.
Szektor olvasásakor az egyes bájtokat az adatregiszterből lehet kiolvasni. Azt, hogy készen áll-e a bájt a kiolvasásra az állapotbájt 1-es bitjéből lehet megállapítani. Ha ez a bit egyes akkor, kiolvasható az adat, kiolvasás után a bit törlődik, és az is marad míg a következő bájt készen nem lesz. Ha egy adatbájt nem kerül kiolvasásra a következő bájt megérkezéséig, akkor az állapotbájt 2-es bitje beállított állapotúvá válik, ami az adatvesztést jelzi, de a beolvasás ekkor sem szakad meg.
Szektor írásakor a parancs kiadása után az adatregiszterbe kell sorban beírni az adatokat amit a lemezre akarunk írni. Az állapotbájt 1-es bitjének beállított állapota jelzi, ha küldhetjük a következd adatot. Ha nem írjuk be a bájtot az adatregiszterbe időben akkor beállított lesz az adatvesztés jelzőbit és a lemezre 0-ás bájt kerül kiírásra.
A cím olvasási parancs a szektor olvasáshoz hasonlóan működik, de itt nem lehet megadni szektorszámot, hanem a legelső megtalált szektorazonosító blokkot lehet beolvasni. Egy ilyen blokk 6 bájt hosszóságú, és a kővetkező adatokat tartalmazza:
1. bájt - Track száma (0-79)
2. bájt - lemezoldal száma (0-1)
3. bájt - Szektor száma. (0-255)
4. bájt - Szektor mérete. (0-3)
5. bájt - CRC 1
6. bájt - CRC 2

A szektorméretek az alábbiak lehetnek:
00 = 128 bájtos szektor
01 = 256 bájtos szektor
02 = 512 bájtos szektor
03 = 1024 bájtos szektor
IBM formátumú lemez mindig 512 bájtos szektorokat tartalmaz. Ez a funkció a fejpozicionálás ellenőrzésére használható.

Track-műveletek
Ide két parancs tartozik, a track olvasás és a track írás ami nem más, mint a formázás. A szektorműveletekhez hasonlóan itt is be kell pozícionálni a fejet a megfelelő sávra mielőtt kiadjuk az utasítást.
Sáv olvasása szinte teljesen megegyezik a szektorolvasással, de itt nem kell szektorszámot megadni. A betöltés az indexlyuk megtalálásával kezdődik, és egészen addig tart amíg másodszorra elérkezik az indexlyukhoz, vagyis amíg a lemez egyszer körbefordul. Ilyenkor minden betöltődik ami a lemezen van, tehát a szektorok adatain kívül a szektorközti területeket (lásd formázásnál) is. A különböző szinkronváltások miatt a Gap területen lévő bájtok hibásak is lehetnek, sőt akár adatvesztés is bekövetkezhet, de az index címjelzés, az index mező, az adat címjelzés, az adatmező és a CRC bájtok helyesek lesznek.
A sáv írása nem más mint a formattálás, vagyis fel kell írni a track-re a szektorazonosítókat, az adatmező helyét és a szektorközti területeket.

A sáv felépítés: Gap I, Szektorok területe, Kitöltés
A szektorok felépítése: Gap II, Szektorfej, Gap III, Adatbező, Gap IV

Mint látható, egy szektor nem csak magából az adatterületből áll, hanem a szektorazonosítókból, különböző jelzőbájtokból és kitöltő területekből (Gap-ekből) is.
A Gap-eknek fontos feladatuk van, nem csak foglalják a helyet foglalják, hanem időzítenek is. Egy szektor adatainak beolvasása után ugyanis szüksége van a WD-nek egy bizonyos feléledési időre, mire a következő olvasást elkezdheti. Formázáskor a szektorok felépítését annyiszor kell felírni a lemezre ahány szektorra akarjuk formázni a lemezt. Egy sáv mérete névlegesen 6250 bájt dupla sűrűségű felírás esetén, és 3125 bájt szimpla sűrűségnél, formázáskor azonban érdemes még rátenni egy kicsit, vagyis megnövelni a legutolsó kitöltő területet a sebességváltozások miatt, tehát dupla sűrűségnél 6500 bájtot, szimplánál 3250 bájtot kell kiküldeni a WD-nek. A Gap~k ajánlott felépítése: (a Gap II kivételével mindegyiktől el lehet térni, de csak az egyes bájtok számának növelésével.)

A Gap-ek felépítése
-FM
-MFM
Gap I - 16 bájt - 0FFh
32 bájt - 4Eh
Gap II - 11 bájt - 0FFh
22 bájt - 04Eh
6 bájt 00
12 bájt 00
- 3 bájt 0F5h
Gap II - 10 bájt - 0FFh
24 bájt - 4Eh
4 bájt - 00
8 bájt - 00
- 3 bájt 0F5h

Az adatok felírását a sávra a szektoríráshoz hasonlóan kell elvégezni. Fizikailag az írás az indexlyuk elérésénél kezdődik, és egy teljes lemezfordulatig tart. A szektorírástól eltérően itt nem minden bájt íródik fel egy az egyben a lemezre, hanem vannak speciális bájtok amelyeket a WD eltérően kezel. FM felírásnál (szimpla sűrűség) a F08h és 0FEh közötti bájtok a CRC ellenőrzés inicializálására valók, ugyanezt MFM felírásnál (dupla sűrűség) a 0F5h bájt kiküldésével érhetjük el. Mindkét felírási módnál 0F7h hatására két CRC bájt íródik a lemezre, 0FEh a szektorfej jelzése és 0FBh az adatmező jelzése. A szektorfej és az adatmező után is fel kell itatni a lemezre a két CRC bájtot, tehát egy 0F7h bájtot kell elküldeni a WD-nek. A fentiekből következik, hogy a Gap-ekben, a szektorfejben és az adatmezőben nem használhatók a 0F5h és 0FEh közti értékek.

Megszakítás
Ezzel a paranccsal különböző megszakításokat lehet kérni a WD-től, az I0-I3 bitek beállításától függően, azonban ENTERPRISE-on ezt nem tudjuk kihasználni, mert a megszakítási láncban nem szerepel. Ezért csak egy beállítás mellett van értelme a használatának, az I0-I3 biteket nullára állítva (0d0h) a folyamatban lévő parancsot tudjuk megszakítani.

Nem esett szó még egy pontról ami a kontroller-kártyához tartozik, de nem a WD közvetlen portja. Ez a 18h port amelyen a használni kívánt meghajtót tudjuk kiválasztani. Ezt az alsó négy bit valamelyikének beállításával tehetjük meg. Értelemszerűen a 0-ás bit az A meghajtót, az 1-es bit a B meghajtót stb. jelenti. A portya irt érték 4-es bitjével pedig a meghajtó 0-ás és 1-es oldala között tudunk szelektálni. Ha például 12h értéket küldünk a 18h ponca, akkor a következő parancs a B jelű lemezegység 1-es oldalára fog vonatkozni.

További információk

A szektorfej felépítése
Az adatterület felépítése
0FEh (Szektorfej jelzése)
0FBh (Adatterület jelzése)
Sáv száma (0-0F4h)
Szektorméretnek megfelelő számú adatbájt
Lemezoldal száma (0 vagy 1)
0F7h (CRC felírása)
Szektor száma (1-0F4h)
 
Szektor mérete (0,1,2,3)
 
0F7h (CRC felítása)
 

Egy tanács
Ha a közvetlen lemezkezeléssel kísérletezünk, akkor soha ne olyan lemezen próbáljuk ki, amelyen fontos adataink vagy programjaink vannak, mert könnyen tönkremehetnek egy esetleges programhiba miatt.

DEVIL - Enterpress 1992/1-1992/2

Az Enterprise DOS lemezek felépítése

A mágneslemez felépítése
A lemez lehet egy-, vagy kétoldalas. A kétoldalas meghajtóval mindkét formátumot tudjuk kezelni. A lemez homlokfelületén a koncentrikus körök - SÁVOK - hordozzák a mágneses információkat. Számuk 40, vagy - duplasűrű forma esetén - 80. A duplasűrű meghajtóval kezelhetünk szimplasűrű lemezt is, de ekkor csak a páros sávokat használjuk. (A sávok számát - korlátozott mértékben - növelhetjük is.) A sávokon 512 byte-os szektorok helyezkednek el. Minden szektor önálló szektor-azonosítóval van ellátva. A sávonkénti szektorszám (512 byte-os szektorokat feltételezve) 8 vagy 9, de 11-ig (ZOZOSOFT újításával 13-ig) lehetséges a számukat bővíteni.
A szektorazonosító felépítése:

byte    
0. 0-89 TRACK megadott értékek az általában használtak,
1. 0-1 SIDE 0 és 255 között bármi lehetséges,
2. 1-13 SECTOR másolásvédett lemezeknél
3. 0-3 BYTE/SECTOR (0=128, 1=256, 2=512, 3=1024)
4-5 0-FFFFH CRC

A DOS lemezek felépítése

BOOT szektor
A DOS lemezek 0. oldal, 0. sáv, 1. szektorát beolvasva megkapjuk a lemez BOOT szektorát. Az itt található adatok alapján meghatározható a lemez fizikai és logikai felépítése. Az alábbi táblázat lemezekre készült, de a csillaggal jelölt változók RAMDISK esetében is használhatók, a '-'-al jelölteket nem veszi figyelembe az EXDOS, a '!'-lel jelölteknél a megadott értékeken kívül eső érték esetén valamilyen szokásos értékre konvertál, esetleg kijelenti, hogy nem DOS lemez. A '@' jelölésűeket pedig az EPDOS hagyja figyelmen kívül. Az 'X' jelzi azt, hogy az adott értékre nincs ellenőrzés, ilyenkor nekünk kell gondoskodni arról, hogy értelmes adatok legyenek. (Például ne legyen több a BOOT szektorok száma a lemez szektorainak számánál.) A megadott értékek az EXDOS alatt helyesen kezeltek, zárójelben a használt érték. A ^ jellel jelölteket az MSDOS 6.0 sem kezeli, pedig a MICROSOFT találta ki a DOS formátumot.

00-02
-
        EBH. FEH, 90H IBM ( Ugrás a betöltő kódra)
03-10
-
        "EXDOS 1.0" rendszer (formázó software)
11-12  
*
!
 
^
#200 byte / szektor
13  
*
!
    1-2 szektor / cluster
14-15  
*
X
@
^
1-FFFFH (1) szektor / boot
16  
*
 
@
^
1-7 (2) FAT példányok száma
17-18  
*
X
    1-FFFFH (40H-70H) ROOT_DIR (főkönyvár MAX_FILE)
19-20  
*
      Formától függő szektor / disk
21
-
*
!
    F8H-FFH FORMA_TYPE
22-23  
*
X
    1-FFFFH szektor / FAT
24-25           8-10-11-13-18-22 szektor / track (EXDOS verziótól függ)
26-27    
!
    1-2 SIDE (oldalszám)
28-29  
*
X
@
^
0-FFFFH (0) fenntartott
Innentől csak EXDOS (vagy VT-DOS) lemezeken:
30
-
     
^
C9H RET, az MSX-DOS BOOT program belépési pontja
31-63
-
     
^
0 nullázás
64-69  
*
   
^
"VOL_ID" azonosító string
70  
*
   
^
0 UNDEL jelző-byte
71-74  
*
   
^
X1, X2, X3, X4 32 bites lemezazonosító
75-99
-
     
^
0 nullázás
100-511
-
     
^
E5H üres terület

A típus-byte RAMDISK esetében 2AH, lemezeknél F8H-FFH, ahol az alsó 3 bitnek formai jelentése van.

0. bit =1 oldalak száma 2
1. bit =1 sávonként 8 szektor
2. bit =1 szimplasűrű sávkiosztás
3-7. bit =1 beállított
3. bit = 0 - 3.5", HD lemez

FFH - DS/SD/8 (320K)
FEH - SS/SD/8 (160K)
FDH - DS/SD/9 (360K)
FCH - SS/SD/9 (180K)
FBH - DS/DD/8 (640K)
FAH - SS/DD/8 (320K)
F9H - DS/DD/9 (720K)
F8H - SS/DD/9 (360K)
F0H - DS/DD/18 (1440K)

Az EXDOS leellenőrzi, hogy a típus-byte F8H (F0H)-FFH közé esik-e, de ténylegesen nem ez alapján kezeli a lemezt, hanem a BOOT szektor adatai alapján. FAT-1 A file-ok raktározására az elemi adatblokkok (CLUSTER), illetve ezeknek a láncolata hivatott. A blokkok könyvelése a FAT-területen történik. A mutatók mindegyike 12 bites szám, melyek sorban helyezkednek el. Pl. az 5. cluster foglaltságát 5*12 bit-től kezdve olvashatjuk. Az értékek jelentése:

000H - szabad cluster
FFFH - file vége
FFEH - nem használható cluster
FF7H - hibás cluster
egyéb - annak a clusternek a száma, ahol a file folytatódik.

A rendszer 0-ás és 1-es clustert nem használ, ezért helyükön #Tp, #FF, #FF byte-ok találhatók. (Tp = típus-byte) FAT-2 (vagy az utolsó FAT példány) A FAT-1 minden írási művelet esetén átmásolódik a FAT2 területre. Törlés esetén a cluster-ek felszabadítása csak a FAT-1 területen történik. UNDEL esetén a FAT-2 alapján helyreállítható az eredeti állapot. Ha csak 1 FAT van akkor nincs lehetőség az UNDEL-re. Ha 2-nél több FAT példányt használunk, akkor az utolsó kivételével mind a FAT-1 másolata, és ha a FAT 1 fizikailag megsérül, akkor amíg az EXDOS legalább egy jó példányt talál, addig helyesen (hibajelzés nélkül) kezeli a lemezt.
ROOT DIR A főkönyvtár a file-ok könyvelésére szolgáló, kötött méretű terület. Minden file-hoz 32 byte-os leíró tartozik. Egyes byte-ok jelentése:

00 file név első betűje, E5H ha törölt a file, 0 ha szabad bejegyzés
01-07 file név
08-10 kiterjesztés
11 attribútum
12 törlés után a név első betűje, egyébként 0
13-21 nulla (nem használt)
22-23 idő
24-25 dátum
26-17 kezdő cluster
28-31 méret

attribútum 0. bit = 1 READ ONLY
  1. bit = 1 HIDDEN
  2. bit = 1 SYSTEM
  3. bit = 1 VOLUME
  4. bit = 1 DIRECTORY
  5. bit = 1 ARCHIVE
  6., 7. bit 0  

TIME, DATE byte:

File elhelyezési tábla (File Allocation Table, FAT)
A FAT 12 bites bejegyzéseket tartalmaz, a lemez minden alapegységéhez egyet. Az első két bejegyzés a 0. és az 1. alapegységhez tartozik, amelyek nem léteznek, így az első alapegységhez tartozó FAT bejegyzés a FAT 4. byte-ján kezdődik. A FAT első 3 byte-ja a következő :

0. byte : FATID byte, mindig F8h...FFh
1. byte : mindig FFh
2. byte : mindig FFh

A FATID byte a lemezformátum meghatározására szolgál, ha nincs UPB a boot-szektorban, egyébként nem használatos.
Minden alapegységhez tartozó bejegyzés nulla, ha az alapegység szabad, egyébként az alapegység része egy file-nak, és a bejegyzés értéke a file következő alapegységének a számát adja meg. A file utolsó alapegységét a FF8h...FFFh értékek valamelyike jelzi. Ezáltal a file-t alkotó alapegységek láncba fűzötten jelennek meg a FAT-ban.
A FF0h...FF7h értékek a fenntartott alapegységeket jelölik, ha azok nem alkotják egy lánc elemét sem: a FF7h jelzi a hibás alapegységeket, amelyeket nem szabad használni.
Egy lemez a FAT-ot akárhány példányban tartalmazhatja. A másodpéldányokat akkor használják, ha az első lemezhiba miatt olvashatatlanná válik. Rendszerint az összes példány egyforma, de létezik egy kivétel ez alól (a file-visszaállítás érdekében).

Rendszerváltozók

VERIFY Automatikus ellenőrzés írás után (0=be, FFh=ki)
DEF_UNIT Default Unit (meghajtó megadásának hiányában használt egység) száma : 1...26
BOOT_DRV Nem használt.
STEP_RATE Léptetés az UNITH számára (0=6ms, 1=12ms, 2=20ms, 3=30ms)
DSK_CHK 0 esetén engedélyezve a teljes diszk-változás ellenőrzés a már nyitott file-okra
AB_ERR ABORT hibát eredményező diszk-hibakód
BCH_DIS Ha nem 0, a DOS "elhiszi", hogy a diszk nem változott.

DISKIO hívások

A használt rövidítések:

TRC: transzfercím (IX-regiszter)
MEGH: meghajtó maszk (B-regiszter 0.bit=A, 1.bit=B, 2.bit=C, 3.bit=D)
OLD: oldalszám (SIDE) (0-1) (C-regiszter 0. bitje)
SÁV: sávszám (TRACK) (0-254) (D-regiszter) (a 255 az aktuális sáv)
SZEK: szektorszám (SECTOR) (1-13) (E-regiszter)
SZEKN: szektorok száma (1-13) (L-regiszter)
ST: státus (H-regiszter)
     bit 0-1 = fejléptető sebesség,
     bit 2 = előkompenzálás engedélyezése,
     bit 3 = duplasűrű meghajtó szimplasűrű lemezt kezel,
     bit 5 = felírási mód (0=MFM, 1=FM)

A hívás általános tudnivalói:

FISH hívások

A használt rövidítések:

STR: eszköz:\ösvény\fájlnév .kiterjesztés
UV: útvonal (ösvény)
MEGH: meghajtó száma (1 = A, 2 = B, 3 = C, stb, 0 = aktuális)
FMEGH: fizikai meghajtó száma
KFCB: kereső fájl kontrol blokk
MFCB: meghatározott fájl kontrol blokk
TRC: transzfercím (felhasználói puffercím)
SIZE: bájtok száma (fájl vagy blokk méret)
SZEK: logikai szektorszám (kezdő szektorszám)
SZEKN: szektorok száma (mozgatandó szektorok)

A hívás általános tudnivalói:

File Control Block-ok (FCB-k) formátuma

KFCB: (használatba vétel előtt a területet nullázni)

00 FF (keresési FCB)
01-05 kötelezően 0
06 Keresési attribútum (csak VOL-ra)
07 Meghajtó (0 = aktuális)
08-0F Fájlnév > (lehet ? is)
10-12 Kiterjesztés
13-1C ?
1D-1E Logikai szektorszám
1F A katalógushoz tartozó szektorok száma: (DEC ciklusszámláló 1 = utolsó)
20 A szektorhoz tartozó fájlleírók száma (DEC ciklusszámláló 16 = 1. fájl)
21-2B ?
2C-2F Lemez azonosító (ID szám)

MFCB: (megtalálva)

00 FE (meghatározott FCB)
01-05 0
06 Keresési attribútum
07 Meghajtó
08-0F Fájlnév (08-27 a beolvasott fájlleíró adatok)
10-12 Kiterjesztés
13 Fájl attribútum
14-1D  
1E-1F Idő
20-21 Dátum
22-23 Kezdő cluster
24-27 Fájlméret
28-2B Fájlmutató
2C-2F Lemez azonosító (ID szám)

MFCB: (megnyitva)

00 FE
01-05 0
06 Keresési attribútum
07 Meghajtó
08-12 Fájlnév + kiterjesztés
13 Attribútum
14-16 0
17-1A Fájl meglévő mérete (SIZE)
1B-1C Dátum
1D-1E Idő
1F ?
20 ? (ha bit 6 = 1 akkor írás történt)
21-22 Kezdő cluster
23-24 Aktuális cluster
25-27 Cluster számláló (0 = első)
28-2B Fájlmutató
2C-2F Lemez azonosító

Meghajtó adatterület: (60H méretű)

00 ? 0,2
01-02 Meghajtó RAM címe az 2. lapon (FISH IY)
03 Meghajtókezelő szegmensszáma
04-05 Meghajtókezelő címe a 3. lapon
06 ? (0 = létező meghajtó?)
07 Fizikai meghajtószám
08 Logikai meghajtószám
09 1-2 esetén csere ellenőrzés (64H>DEC 1/50 sec. időszámláló)
0A 0-1
0B 0-1
0C 1
0D 0
0E 2
0F 0
10 ROOT-DIR szektorok száma
11 FAT szektorok száma
12-13 0. cluster logikai szektorszáma
14-15 2. cluster logikai szektorszáma
16-17 legnagyobb lehetséges cluster + 1
18 0
19-1C Lemez azonosító (ID)
1D Formázás típusszám

A FISH ás EXOS változók összefüggése

64 (IY-5E) EXDOS-ROM szegmensszám
65 -5D P0
66 -5C P1 A transzfercím számoláshoz
67 -5B P2
68 -5A P3
69 -59 ECHO-flag
70 -58 VERIFY-flag
71 -57 aktuális meghajtó
72 -56 ISDOS-BOOT-DRV
73 -55 STEP-RATE
74 -54 DISK-CHANGE-FILE-flag
75 -53 ERROR-INP-csat
76 -52 ERROR-OUT-csat
77 -51 CLI-INP-csat
78 -50 CLI-OUR-csat
79 -4F DATE-TIME-forma
80 -4E ERROR-DISABLE-flag
81 -4D DISK-ERROR-code
82 -4C ISDOS-ERROR-code
83 -4B ISDOS-CLI-védelmi-flag
84 -4A RND0
85 -49 RND1 A VOL-ID-hez
86 -48 RND2
87 -47 RND3
88 -46 CLI-END-flag
89 -45 DISK-CHANGE-flag
90 -44 FAST-VIDEO-flag
91 -43 DIR-END-flag
92-99 -42-3B fenntartott terület
  -3A ERROR-seg
  -39 ERROR-prtL
  -38 ERROR-prtH

Hibakódok - hibaüzenetek

CFh .NOFIL File not found a file nem található
BFh .NOUPB   belső hiba
BDh .ICMD   belső hiba
BCh .IUNUM   belső hiba
BBh .ISECT   belső hiba
BAh .NRDY Not ready nem üzemkész
B9h .VERIFY Verify error hiba az ellenőrzésnél
B7h .SNF Sector not found a szektor nem található
B6h .WPROT Write protected disk írásvédett lemez
B5h .UFORM Unformatted disk formálatlan lemez
B4h .NDOS Not a DOS disk nem DOS lemez
B3h .WDISK Wrong disk rossz lemez
B2h .WFILE Wrong disk for file rossz lemez a file számára
B1h .WDRV Insert disk for drive x: helyezzen be lemezt az x: meghajtóba
B0h .ICALL   érvénytelen FISH funkciószám
AFh .IDRV Invalid drive érvénytelen meghajtó
AEh .IPARM Invalid parameter érvénytelen paraméter
ADh .DRFUL Root directory full a gyökérkönytár megtelt
ACh .DKFUL Disk full a lemez megtelt
ABh .DUPF Duplicate file name kétszeres file név
AAh .NODIR Directory not found a directory nem található
A9h .DIRE Invalid directory move érvénytelen directory áthelyezés
A8h .IPATH Invalid pathname string érvénytelen útvonalnév-string
A7h .FILRO Read only file csak olvasható file
A6h .IFNM Invalid filename érvénytelen file név
A5h .DIRNE Directory not empty a directory nem üres
A4h .ABORT Disk operation aborted a lemezművelet félbeszakadt
A3h .IFAT   Egy, a FAT táblából beolvasott érték kívül esik a lemez címtartományán.
A0h .IFCB   Érvénytelen volt a FISH-nek átadott FCB
9Fh .INP Invalid number of parameters érvénytelen számú paraméter
9Eh .DOT Invalid . or .. operation érvénytelen . vagy .. művelet
9Ch .BFUL Command too long túl hosszú parancs (max. 128 karakter)
9Bh .OV64k   A 64k-s területen túlnyúló mennyiségű adatot próbáltunk meg írni/olvasni
9Ah .FILE   A file alapegységlánca túl rövid a directory-bejegyzésben megadott mérethez.
99h .SYSX System file exists a rendszer-file létezik
98h .DIRX Directory exists a directory létezik
96h .NCOMP Incompatible disk nem kompatibilis lemez
95h .ICOPY File cannot be copied onto itself a file nem másolható önmagára

A FISH hibakezelés átvétele: (IY-3A-38)

A rutin a 0. vagy az 1. lapon lehet. A szegmensszám nem nulla értéke esetén, minden FISH hibánál megkapjuk a vezérlést.

B = hibakód     A = 0 - a hibakezelés visszaadása a FISH-nek
C = MEGH = 1 - ABORT
                    = 2 - RETRY
                    = 3 - IGNORE
                    = 4-255 - ABORT

HSoft & ZozoSoft - Enterpress 1993/6 - 1994/3-4

Assemly segédrutinok

A különböző nyelvek közül az assembly programozás igényli a legtöbb időt és energiát. Egyszerű feladatoknál különösen kitűnik, hogy a perifériális INPUT/OUTPUT, főképpen a hibakezelés mennyi munkába kerül. Vannak gyakran ismétlődő feladatok. Az ilyen rutinokat célszerű uniformizálni, és feladatuk szerint csoportosítani. A fáradtságunk gyorsan megtérül, mert csak egyszer kell őket megírni.
Az első rutinfájl a numerikus és sztringadatok kiírását hivatott könnyíteni. Az írás történhet a memóriába, ahol a kezdőcímet DE regiszterpárban tároljuk, és a visszatérés után a következő szabad bájtra fog mutatni. Másik lehetőségként a 4. EXOS változóval beállított alapértelmezésű) csatornára írhatunk. Visszatéréskor AF tartalmazza az EXOS hívás státuszát. A rutinok megőrzik a HL, az index, és az alter regiszterek tartalmát.

Sztringek írása: A CALL hívást követő DEFB adatokat küldi el blokkírással az első nulla bájtig, majd visszatér az ezt követő címre. A rutinnal nulla bájtot nem lehet kiírni, viszont a hossz nincs korlátozva.

Numerikus adatok írása: A számok mérete 8 vagy 16 bites lehet. Az egy bájtost az akkumulátorba, a két bájtost a HL regiszterpárba kell helyezni. A bináris számokat kiírhatjuk decimális vagy hexadecimális, a BCD számokat pedig a hexadecimális rutinnal. (A karakterek elküldése bájtonként történik.)

A szövegfájlt célszerű az ASMON editorával elkészíteni. Szeretném felhívni a figyelmet itt néhány hibalehetőségre: Aki 80 karakteres (ASMEN) editorral dolgozik, az INCLUDE fájlokat ne mentse ki CTR-I-vel (intelligens gyorsmentés), mert fordításnál hibát okoz. Valamilyen hiba folytán a mentés gyakran csonka. Ezt elkerülhetjük, ha az editorból F8-al kitépünk, majd E-vel visszalépünk. Ekkor F4-el választhatunk mentést (TURBO SAVE). Fájlnévnek "NUM_STR.ASM"-et adjunk.
Ha később valamilyen programunkban használni szeretnénk, INCLUDE NUM_STR.ASM direktivóval egyszerűen hozzáfordítjuk a kódunk végéhez. Példaképpen mellékelem a DUMP.ASM listát, mely több rutinszolgáltatást is igénybe vesz. A fordítás előtt [Z]-vel beállítjuk az assembler opciókat:

Assembly listing: OFF
List conditions: NO
Force Pass 2: NO
Memory assembly: YES
Memory offset: 0
Object file name:

[A]-val fordítunk. Hibátlan esetben [G]-vel megadjuk a 3000-s kezdőcímet, majd Enter-rel lefuttatjuk a kódot.
Végül a lényegesebb rutinnevek listája:

DECHLPRINT 16 bites decimális kiírás
DECAPRINT 8 bites decimális kiírás
DECHLDE 16 bites dec. memóriaírás
DECADE 8 bites dec. memóriaírás
HEXHLPRINT 16 bites hexadecimáis kíírás
HEXAPRINT 8 bites hexadecimális kiírás
HEXHLDE 16 bites hex. memóriaírás
HEXADE 8 bites hex. memóriaírás
STRPRINT sztring kíírás
STRDE sztring memóriaírás
APRINT akkumulátor kiírás
BPRINT B-regiszter kiírás
SPPRINT szóköz kiírása
CRLFPRINT újsorjel kiírása
JPHL vezérlésátadás
JPIX vezérlésátadás
JPIY vezérlésátadás

A NUM_STR.ASM rutinját egy újabbal bővítjük. A neve ERROR.ASM. Hívásakor az akkumulátorban lévő hibakód szöveges megfelelőjét az alapértelmezésű csatornára írja. A rutin a hibaszöveget EXOS 28-as hívással kéri le. DE regiszter a hibapufferre mutat. Visszatéréskor az első bájt megadja a hosszúságot, melyet a hibasztring követ. Szöveges magyarázat hiányában (nulla hossz), decimális és hexadecimális formában kiírja a hibakód számát. A rutin valamennyi regiszter tartalmát megőrzi. 64 bájtos RAM-puffert igényel, melynek a kezdőcímét ERRBUFF néven kell megadni.
A tesztelésére ASMON alatt futó kódot írtunk: HIBALIST.ASM. Kiíratjuk a 128-255 közötti EXOS-hibaüzeneteket a képernyőre. A [Z]-opcióval beállítjuk:

Memory assembly   YES
Memory offset     0

[A]-val fordítunk, [G]3000-rel futtathatjuk.

HSoft - Enterpress 1992/2.

Sprite kezelés ENTERPRISE-on
A sprite-ok, másnéven szellemek vagy manók, a számítógépes játékok legfőbb alkotóelemei, ezért azokban a gépekben, amelyeket főleg játékra terveztek egy külön hardver gondoskodik a mozgatásukról. Sajnos az ENTERPRISE nem büszkélkedhet ilyen képességekkel, tehát kénytelenek vagyunk programból megvalósítani ugyanezt. Megpróbálom néhány BASIC és ASSEMBLY rutin segítségével bemutatni a sprite-kezelés módszereit.
Az első és egyben legprimitívebb eljárás (amelyet általában a kezdő BASIC-programozók alkalmaznak előszeretettel), hogy kirajzoljuk a hátteret majd erre kirakjuk a manókat az aktuális pozíciójukba. Ezután elvégezzük a többi szükséges műveletet (billentyűzetfigyelés, ellenőrzések, számítások stb.), majd letöröljük a képernyőt és kezdjük elölről az egészet. Ez a módszer nagyon lassú, ezért az egész kép villog, méghozzá nem is kis mértékben. De mivel az ENTERPRISE-nak olyan remek videóchip-je van, mint a NICK, ezért még ezt a legősibb eljárást is megoldhatjuk villogásmentesen, mégpedig egy háttérképernyő segítségével. A lényeg, hogy két, méreteiben azonos videólapot nyitunk meg, amelyek közül mindig csak az egyiket kapcsoljuk be, és a azon dolgozunk, amelyik nem látszik (ezt hívjuk háttérképernyőnek). Miután mindent kiraktunk rá, egy DISPLAY utasítással átkapcsolunk erre a videólapra. Ha mindent jól csinálunk a képernyőn semmi nem fog villogni, azonban a programunk nagyon lassú és ezért élvezhetetlen lesz, ezért ezt a módszert csak olyanoknak ajánlom, akik most ismerkednek a programozással.
A második lehetőség, hogy a sprite-ok szélein egy vagy több pixel szélességű üres sávot hagyunk. Ha ezt úgy tesszük ki a képernyőre, hogy az előző fázis helyétől csak annyi képponttal tér el az új pozíció ahány egység szélességű üres sáv van a szélein, akkor ez teljesen letörli az előző fázis egyébként kilógó részét. Mint látható ennél a módszernél nincs külön kirakás és törlés, hanem mindkettő egyszerre hajtódik végre. Ezért ez az eljárás kellően gyors ahhoz, hogy akár BASIC-ben is alkalmazhassuk. Legnagyobb hátránya, hogy nem lehet a szellem mögött háttér mert mozgása közben azt is letörölné, a másik hibája, hogy csak kis egységenként lehet mozgatni (maximum anníi ponttal amilyen széles az üres sáv a szélén). Ezen hiányosságai ellenére BASIC-ben a legjobb megoldás a sprite-kezelésre, persze csak akkor ha nem alkalmazunk hátteret. Sajnos az ENTERPRISE BASIC-ben ezt a sprite kezelési módszert nem vagy csak nagyon körülményesen lehet megvalósítani, ugyanis ha grafikus lapra írunk a PRINT utasítással akkor az nem írja felül azt, ami kint van a képernyőn, hanem hozzámásolódik. Ezért a manó szélein hagyott keret nem fogja letörölni az előző fázist.
A következő megoldás, amikor a manót úgy rakjuk ki, hogy össze XOR-oljuk a képernyőn lévő háttérrel. Mivel ennek a logikai műveletnek az a tulajdonsága, hogy ha egy értéket kétszer össze XOR-olunk egy másikkal, akkor visszakapjuk az eredetit, ezért a sprite letörlése nagyon egyszerű, csak újra ki kell raknunk. A leírtakból már látszik, hogy ismét két fázisból áll a művelet, tehát lassabb lesz, mint az előző, de itt nyugodtan mozoghat a manó egy háttér előtt is, nem fogja azt letörölni. Természetesen a XOR művelet miatt színes képernyőn a végeredmény nem mindig a legszebb. Magasszintű programnyelvekben ez a legelterjedtebben alkalmazott módszer. Egy BASIC nyelvű példaprogram az 1. listán látható, ami alapján remélem mindenki számára világos lesz az eljárás.

100 PROGRAM "SPRITE.BAS"
200 GRAPHICS HIRES 16
120 SET KEY CLICK OFF
130 SET LINE MODE 3
140 LET SPRX=10:LET SPRY=5:LET E$=CHR$(154)
150 PRINT £101,AT SPRY,SPRX:E$
160 LET LSPRX=SPRX:LET LSPRY=SPRY
170 LET JS=JOY(0)
180 LET SPRX=SPRX-((JS BAND 1)=1 AND SPRX<20)+((JS BAND 2)=2 AND SPRX>1)
190 LET SPRY=SPRY-((JS BAND 4)=4 AND SPRY<10)+((JS BAND 8)=8 AND SPRY>1)
200 IF LSPRX=SPRX AND LSPRY=SPRY THEN 170
210 PRINT £101,AT SPRY,SPRX:E$
220 PRINT £101,AT LSPRY,LSPRX:E$
230 GOTO 160

A legjobb és gépi kódú programokban általánosan használt eljárás az ún. maszkolt sprite-kezelés. Itt az egyes fázisok két részből állnak, az egyik maga a megrajzolt alak, a másik pedig ennek a maszkja. A maszkot úgy készítjük el, hogy az alakzat körvonalán kívül eső pontok bitjeit kigyújtjuk a többit pedig kinullázzuk. A módszer lényege a következő: a képernyő azon részéről - ahová a sprite-ot ki akarjuk tenni - elmentjük az adatokat egy átmeneti ún. puffer-tárba, majd a képernyő megfelelő bájtját le AND-eljük a maszk megfelelő bájtjával és ehhez hozzá OR-oljuk a sprite ugyanazon bájtját, az eredményt pedig visszatesszük a képernyőre. A következő kirakás előtt pedig a pufferből visszamásoljuk a képernyő eredeti tartalmát. A jobb érthetőség érdekében a 2. listán látható egy példa a módszer megvalósítására. A TOROL címkéjű rutin végzi az előző fázis törlését, a KIRAK címkéjű pedig a kirakást. Ez utóbbinak a HL-ben kell átadni azt a képernyőcímet, ahová a manót akarjuk tenni (bal felső bájtjának a címe), természetesen hívása előtt be kell lapozni a megfelelő videószegmenst. Ezekkel a rutinokkal csak egy sprite mozgatását tudjuk elvégezni, de bárki könnyen átalakíthatja őket. Több sprite használata esetén figyelni kell arra, hogy a törlésnél először az utoljára kirakottat kell törölni, majd az előzőt és utoljára azt, amelyiket először raktunk ki és csak az összes szellem letörlése után kezdhetjük kirakni őket újra. Ha nem így csináljuk, akkor nem mehetnek egymásra a manók, mert összezavarodik a képernyő. Ha viszont így csináljuk, akkor sokáig nem lesznek kint a sprite-ok a képen, ezért villogni fognak. Ezt kiküszöbölhetjük úgy, hogy a sprite kezelést a videó megszakításban végezzük. Azonban ha nagyon sok manót akarunk használni akkor csak a háttérképernyő használata vezet eredményre, amelyről már volt szó.
Remélem mindenki megtalálta a neki legjobban tetsző sprite kezelést, és sok jó, villogásmentes játékkal fog gazdagodni az ENTERPRISE-tábor.

Devil - Enterpress 1992/5-6.

.RADX 16
SPRX EQU 2
SPRY EQU 10
LINESIZE EQU 50
SPRITEDAT EQU 4000
MASKDAT EQU 4020

;sprite vízsz. mérete byte-ban
;sprite függ. mérete pontban
;videólap vízsz. mérete
;sprite memóriacíme
;maszk memóriacíme
TOROL


SPROFF










KIRAK







SPRON2


SPRON



















LASTSPR
PUFFER

LD HL,(LASTSPR)
LD DE,PUFFER
LD A,SPRY
PUSH HL
LD BC,SPRX
EX DE,HL
LDIR
POP BC
EX DE,HL
LD HL,LINESIZE
ADD,HL,BC
DEC A
JP NZ,SPROFF
RET
LD (LASTSPR),HL
LD DE,HL
LD HL,SPRITEDAT
EXX
LD HL,MASKDAT
LD BC,PUFFER
EXX
LD B,SPRY
PUSH BC
PUSH DE
LD B,SPRX
LD A,(DE)
EXX
LD (BC),A
AND (HL)
INC BC
INC HL
EXX
OR (HL)
LD (DE),A
INC HL
INC DE
DJNZ SPRON
POP BC
EX DE,HL
LD HL,LINESIZE
ADD HL,BC
EX DE,HL
POP BC
DJNZ SPRON2
RET
DEFW 0
DEFS SPRX*SPRY

;az elmentett
;képernyőadatok
;visszamásolása













;HL-be SPR cím

;HL'-be maszk cím
;BC'-be a puffer cím





;A-ba a kép megf. byte

;eltárolása a puffer-be
;le AND-elése a maszkkal



;a sprite hozzá OR-olása
;visszaírás a videóm.


; ciklus ismétlése
; X méretétől függően

;a következő sor
;címeinek kiszámítása


;ciklus ismétlése a SPR
;Y méretétől függően
;utoljára kirakott fázis
;puffer

RAM-szegmensek az EXOS alatt
Egy megbízható program, nem használhat hasraütéssel kitalált RAM szegmenet. Erre több példa is rámutat. Más konfiguráció alatt is lehessen futtatni. Az operációs rendszer is használhatja a RAM-szegmenst. (RAMDISK, bővítés, csatorna, eszköz, stb.) Hiányozhat vagy hibás is lehet a kitalált szegmensszám. Egy szegmenset az operációs rendszertől lehet igényelni, ill. felszabadítani.

EXOS 24 szegmens igénylés
Vssza: A = státusz, C = kiutalt szegmensszám
DE=EXOS határ (4000H)

Ha nincs már szabad szegmens, akkor a rendszer által használt alsó szegmenst utalja ki megosztott szegmensként. Az ilyen RAM-ot nem célszerű használni, mert leblokkolja az operációs rendszert. Hiába szabadul fel időközben egy teljes szegmens, még egy csatorna nyitást is "nincs szabad memória" hibával fog elutasítani. Ezért az ilyen RAM-ot azonnal fel kell szabadítani.

LD C,szegmensszám
EXOS 25 szegmens felszabadítás
Vissza: A = státusz

Az ilyen jellegű hívással, (USER) felhasználói szegmenskezelés történik. Ennek lényege, hogy a foglaltságuk megszűnik amint egy új felhasználói programot töltünk be, vagy egy rendszerbővítés átveszi a vezérlést, ill. 40H, 60H jelzőkkel végrehajtott EXOS RESET hatására. A perifériák által végrehajtott szegmens foglalás-felszabadítást az EXOS külön kezeli, és az előző akciók hatására sem szűnik meg a foglaltságuk. A rendszerbővítéseket szintén perifériaszegmensre tölti be az EXOS. Ha ilyen szegmensre van szükségünk, akkor a következő trükkel oldhatjuk meg.

LD A,255
OUT (0B2H),A
LD HL,0BF79H
SET 0,(HL)
EXOS 24
RES 0,(HL)

;rendszerszegmens
;2. lapra
;kerneljelző
;periféria futás
;szegmens kérés
;felhasználói futás

Sok esetben nem elegendő az EXOS által elérhető szegmenskezelés. PI. adott szegmensszám vagy video-szegmens igénylése. Ilyenkor az EXOS-szal kompatibilisen, magunknak kell a kiutalást kézbe venni. A bekapcsolást követően végrehajtott TESZT alatt történik a RAM-szegmensek könyvtárba vétele. A kezdőcím (255-ös szegmens) 0ABD0H, ide kerül a 0FFH szegmensbejegyzés. Innen lefelé (csökkenő címekkel és szegmensszámokkal) következnek a RAM-tesztet kiálló működő szegmensek. A végére a legalsó szegmensszám kerül, melynek megkülönböztetett szerepe van. Ez lesz a nulla szegmens. (Értéke kiolvasható a 0BFFCH címről.) Mivel e szegmenst nem lehet kiutalni, az értékét nullára módosítja az EXOS. A címét letárolja 0BF9CH-n. (A RAM-ok alatt lesz a ROM-ok könyvtára.) A 255-ös szegmens állandó rendszerszegmens lesz, melynek száma jelen esetben 1. A későbbi csatorna nyitások hatására számuk megnőhet. Az. EXOS nyilvántartást vezet a RAM-ok kiutalásáról.

BF9A - EXOS határszegmens címe.
BF9C - RAM-szegmensek végcíme.
BF9E - A megosztott szegmensek száma.
BF9F - A szabad szegmensek száma.
BFA0 - A felhasználó által foglalt szegmensek száma.
BFA1 - A perifériák által foglalt szegmensek száma.
BFA2 - A rendszer által használt szegmensek száma.
BFA3 - A működő szegmensek száma.
BFA4 - A nem működő szegmensek száma.

Az EXOS 0BF9A-n tárolja a határszegmens címét. Ez a legalsó, rendszer által használt szegmens. A RAM-szegmens lista az alábbi sorrendben épül fel.

(BF9C) - 0
  FELHASZNÁLÓ N=(BFA0)
  PERIFÉRIA N=(BFAl)
  SZABAD N=(BF9F)
(BF9A) - RENDSZER N=(BFA2)
ABD0 - 255

Hsoft - Enterpress 1993 /1-2

A tömörített programokról
Egy-két éve megjelentek a tömörített játék programok Aki szeret játszani, annak előbb-utóbb gondot jelenthet a programok tárolása. A sok játék programhoz sok adathordozó is kell. Ezen próbált segíteni a DTF tömörítő. Sikerrel! A fájlok .DTF kiterjesztéssel szerepelnek. Betöltésük a DTF-betöltővel tovább tart, mint a hagyományos játékok betöltése. Megjegyezzük, hogy a Zozotools 1.6 rendszerbővítő :DL parancsa sokkal gyorsabban tölti a DTF-fájlokat, mint az eredeti DTF-betöltő.
Az EPDOS 1.5 rendszerbővítő is sikereket ért el a PACK neve tömörítőjével. Ehhez készült egy külső rendszerbővítő, amely csak az EPDOS-al használható. Ennek neve PP 1.1., amely jelentősen megkönnyíti a tömörítést. A tömörítendő fájlokat kijelöljük az EPDOS 1-es módjában, majd az EXT-menüben kiadjuk a PP parancsot. A céleszközön a tömörített fájl .PCK kiterjesztéssel jelenik meg. Betölteni az EPDOS Start-menüjével lehet. Az EPDOS Start-menüje egyébként be tudja tölteni a DTF, PCK-fájlokat, valamint elindítja a BASIC programokat, a VSAVE-vel elmentett képeket és természetesen a gépi kódú programokat is.
Nézzünk egy példát: milyen hatékonyan tömörítenek a tömörítők? Kiszemelt programunk a SPACE CRUSADE. 2009-ben IstvánV elkészítette a EPcompress-t és a DTF továbbfejlesztett változatát is. A táblázatban ezek is szerepelnek.

Eredeti méret:
115 464 byte
(Töltési idő: 15 sec.)
DTF-el tömörítve:
61 127 byte
(Töltési idő: 60 sec.)
PACK-al tömörítve:
68 750 byte
(Töltési idő 15 sec.)
DTF -p 1 5:
59 364 byte
(Töltési idő: 22 sec.)
DTF -lz:
47 848 byte
(Töltési idő: 8.5 sec.)
DTF -lz2:
46 237 byte
(Töltési idő: 14.8 sec.)

Megjegyezzük, hogy a kész DTF-fájlhoz még egy betöltőt is kell írnunk. A PACK-kal viszont saját magunk tömöríthetjük programjainkat. A DTF-tömörítőről csak annyit: használata rendkívül körülményes, felhasználói felülete egyáltalán nincs. A kész DTF-programokról pedig csak annyit: ha eszünkbe jut, hogy annál a játéknál kevésnek bizonyul a 3 élet... ...nem tudunk semmit tenni! A PACK-nál viszont ez nagyon könnyen megoldható.

Enterpress 1993 /1-2

Hogyan is működik a DTF tömörítő?
A DTF tömörítés két menetben történik:

Először az egymás után ismétlődő azonos byte-okat cseréli egy 3 byte-os kódra (RLE tömörítés), amely egy jelzőbyte-ból (ami tetszőleges érték lehet és a fejlécben tárolódik), az ismétlődő byte-ok számából (ha 256 byte ismétlődik, akkor 0-t kell tárolni), és az ismétlődő byte értékből áll. Ennek természetesen csak akkor van értelme, ha a 3 byte-os kód rövidebb, mint az eredeti sorozat, azaz (statisztikai tömörítés nélkül) a sorozat hossza legalább 4 byte. Azonban ha a bemeneti adatban a jelzőbyte fordul elő, akkor azt mindig sorozatként kell kódolni, még akkor is, ha ez méretnövekedést eredményez. Ezért a jelzőbyte értékét célszerű úgy választani, hogy ez a méretnövekedés a legkisebb legyen (erre a legegyszerűbb, de nem mindig optimális megoldás a legritkább byte használata).

A TOM formátum csak az előbb leírt RLE tömörítést használja, a fejléc a tömörített adat és a jelzőbyte együttes mérete 2 byte-on tárolva (az alsó byte az első), ezt a jelzőbyte követi, majd végül a tömörített adat.
DTF-nél viszont az RLE tömörítés után van még egy második menet is, amely minden lehetséges byte értékhez hozzárendel egy bitsorozatot, ami nem fix 8 bit méretű, hanem a gyakori byte-ok rövidebbek, a ritkák pedig hosszabbak lesznek. Ezzel a statisztikai tömörítéssel további méretcsökkenésre van lehetőség. Az optimális kódolt byte méret elvileg kiszámítható a gyakoriságból (log2(1/f) bit, ahol 'f' az előfordulási gyakoriság 0 és 1 között; tehát például ha egy byte 25% valószínűséggel fordul elő, akkor azt 2 biten érdemes tárolni, ha viszont csak 0.1% a valószínűsége, akkor kb. 10 biten). Azonban, mivel az egyszerűbb és EP-n is használható megoldásoknál csak egész számú bit használatára van lehetőség, az optimális kód keresése ennél valamivel nehezebb. Ha minden byte-hoz tetszőleges egész számú bitet lehetne rendelni, arra a Huffman algoritmus lenne használható.
A DTF kódolása viszont még egyszerűbb és korlátozottabb. Itt minden tömörített byte két részből áll: fix számú jelzőbitből, és azt követően a jelzőbitek értéke alapján egy táblázatban tárolt számú további bitből. Egy második táblázatban a lehetséges byte értékek találhatók, gyakoriság szerint csökkenő sorrendben. Egy egyszerű példa két jelzőbittel, ilyenkor az első táblázat mérete 4; ha "1,2,3,4" található a táblázatban:

Beolvasandó
bitek
száma
Második
táblázat
pozíció
Optimális
előfordulási
valószínűség
1
2
3
4
0 - 1
2 - 5
6 - 13
14 - 29
12.5%
6.25%
3.125%
1.5625%

Egy DTF formátumú adatblokk részletes leírása:

A DTF tömörítésű programok elején mindig egy TOM formátumú blokk van, ez a .com része a programnak, amelynek a 0. lapon el kell férnie. Ezt követi tetszőleges számú DTF adatblokk, amiket RST 28H hívásokkal lehet betölteni.

A legjobb eredményt István tömörítője éri el, nézzük meg, hogyan működik:

A formátum lényege, hogy a file-ban az ismétlődő byte-sorozatokat egy hosszúság és egy távolság paraméterre cseréli (azaz például "itt a 15 byte-al korábban előfordult 10 byte ismétlődik"), amelyeknek a kódolásához kevesebb bitre van szükség, mint az eredeti sorozat mérete, és így méretcsökkenést lehet elérni. Ezt a megoldást LZ77 vagy LZSS tömörítésnek is nevezik, és például a ZIP formátum is erre épül. Azonban, ha file egy adott részét nem lehet ilyen módon tömöríteni, akkor oda tömörítetlen sorozat kerül, ami azt jelenti, hogy egy hosszúság paraméter után az adott számú eredeti byte tárolódik. Ez természetesen méretnövekedést eredményez, azonban a gyakorlatban előforduló file-ok többségénél több bitet sikerül megtakarítani a tömörített sorozatokkal, mint amennyi a tömörítetlenekkel elveszik, tehát a file kisebb lesz.
A hosszúság és távolság értékeket érdemes változó hosszúságú kóddal tárolni (statisztikai tömörítés), mert például rövid sorozatok sokkal gyakrabban fordulnak elő, mint hosszúak, és a kis távolságok is gyakoribbak (ha itt nincs is akkora eltérés, mint a hosszúságnál). Ezen kívül a legrövidebb (pl. 2 byte-os) sorozatoknál a távolság átlagosan kisebb, mint a hosszabbaknál, tehát még azzal is lehet némi javulást elérni, ha ezek más kódolást használnak.
Az EPcompress esetében a statisztikai tömörítés az adott file-ra optimalizált, a "dtf -lz" paranccsal létrehozott tömörített file-ok viszont egyszerű fix kódolást használnak, amelyet néhány soros assembly rutinnal is be lehet olvasni. Így a betöltő mérete keretcsíkozás nélkül csak 146 byte és nem használ semmilyen táblázatot, de a tömörítési hatásfok rosszabb általában kb. 5 (néha több) százalékkal. Ez a "dtf -lz" statisztikai tömörítése (a lehetséges értéktartományokhoz rendelt bitsorozatok), először a hosszúság, és a távolság a 3 byte-os vagy hosszabb sorozatokhoz:
Érték
Hosszúság kód
Távolság kód

2 -
4 -
8 -
16 -
32 -
64 -
128 -
256 -
512 -
1024 -
2048 -
4096 -
8192 -
16384 -
32768 -
1
3
7
15
31
63
127
255
511
1023
2047
4095
8191
16383
32767
65535
0B
10xB
110xxB
1110xxxB
11110xxxxB
111110xxxxxB
1111110xxxxxxB
11111110xxxxxxxB
111111110xxxxxxxxB
1111111110xxxxxxxxxB
11111111110xxxxxxxxxxB
111111111110xxxxxxxxxxxB
1111111111110xxxxxxxxxxxxB
11111111111110xxxxxxxxxxxxxB
111111111111110xxxxxxxxxxxxxxB
1111111111111110xxxxxxxxxxxxxxxB
0000B
0001xB
0010xxB
0011xxxB
0100xxxxB
0101xxxxxB
0110xxxxxxB
0111xxxxxxxB
1000xxxxxxxxB
1001xxxxxxxxxB
1010xxxxxxxxxxB
1011xxxxxxxxxxxB
1100xxxxxxxxxxxxB
1101xxxxxxxxxxxxxB
1110xxxxxxxxxxxxxxB
1111xxxxxxxxxxxxxxxB

A hosszúsághoz tömörített sorozatnál egyet hozzá kell adni, mert a legrövidebb sorozat, amelyet ez a tömörítő ismételni tud, 2 byte-os. Ezen kívül a 2 byte-os sorozatok a távolságnál más kódolást használnak, amellyel a legnagyobb lehetséges érték 65535 helyett csak 510, ehhez viszont általában egy bittel kevesebb is elég, és így valamivel jobb tömörítési hatásfokot tesz lehetővé:

Érték
Hosszúság kód
1 -
3 -
7 -
15 -
31 -
63 -
127 -
255 -
2
6
14
30
62
126
254
510
000xB
001xxB
010xxxB
011xxxxB
100xxxxxB
101xxxxxxB
110xxxxxxxB
111xxxxxxxxB

Egy teljes "dtf -lz" adatblokk felépítése:

A tömörített adatot a végétől visszafelé haladva kell olvasni, és a kitömörített adat is az utolsó byte-tól visszafelé íródik. Ez azért van, mert a memóriában a betöltendő adatterület elejére kerül, és így ha a kitömörítés előre haladna, akkor felülírná a még feldolgozatlan bemeneti adatot. A "fordított" iránnyal viszont az írási pozíció kitömörítés közben fokozatosan közeledik az olvasási pozícióhoz, és a befejezéskor éri el. A biztonság kedvéért azonban van egy 2 byte-os olvasási puffer is, arra az esetre, ha nehezen tömöríthető adatnál az írási pozíció átmenetileg megelőzné az olvasási pozíciót egy-két byte-al.

A tömörített adat részletesebben:

És most a betöltő rutin leírása, amelynek a belépési pontja 0028H (RST 28H), és csak egy bemeneti paramétere van, a DE regiszter, amely a betöltés kezdőcíme. A csatornaszám mindig 1, és az adatblokk hosszát a fent leírt módon a tömörített file-ból olvassa:

0028
002B
002C
002D
002E
CD BE 00
AF
6F
67
18 38
CALL 00BE
XOR A
LD L, A
LD H, A
JR 0068

Itt az első CALL utasítás beolvas egy 16 bites értéket a BC regiszterbe, ez lesz a file-ból olvasandó tömörített adatblokk mérete. A HL regiszter és a Carry jelzőbit törlése után a rutin folytatására ugrik (az EXOS által foglalt 30H-5FH területen nincs betöltő kód).
A fent meghívott rutin:

00BE
00C1
00C2
00C3
00C5
00C7
00C8
00C9
CD C2 00
68
D5
3E 01
F7 05
D1
4D
C9
CALL 00C2
LD L, B
PUSH DE
LD A, 01
EXOS 05
POP DE
LD C, L
RET

Az RST 28H folytatása:

0068
006A
006D
006E
0070
0073
0074
0075
ED 52
22 09 00
3C
F7 06
CD BE 00
79
B0
28 EB
SBC HL, DE
LD (0009), HL
INC A
EXOS 06
CALL 00BE
LD A, C
OR B
JR Z, 0062

Itt először kiszámítja a -DE értéket (az előbbi HL, A, és Carry törlés ezt készítette elő), és tárolja egy LD HL, n utasítás paramétereként, amely majd azt fogja ellenőrizni, hogy a kitömörítés befejeződött-e. Majd EXOS 6 hívással beolvassa az 1-es csatornáról az egész tömörített adatot a kezdőcímre (a BC-ben már a tömörített méret van). Mivel a tömörített adat a betöltendő terület elejére került, a kitömörítésnek az adat végétől visszafelé kell majd haladnia a kezdőcím felé, hogy a még feldolgozatlan byte-ok ne íródjanak felül.
A tömörített blokk beolvasását követi egy újabb 16 bites érték olvasása, amely a tömörítetlen és tömörített méret közötti különbség. Ha a tömörítő nem tudott méretcsökkenést elérni, akkor az egész adatblokkot egyszerűen tömörítetlenül tárolja, a különbség nulla lesz, és a rutin futása itt befejeződik, és ide ugrik a 62H címre:

0060
0061
0062
0063
0064
0065
0066
0067
D1
D1
6B
62
AF
4F
FB
C9
POP DE
POP DE
LD L, E
LD H, D
XOR A
LD C, A
EI
RET

Ez az RST 28H-ból a visszatérés, a DE-ben és HL-ben az utolsó betöltött byte utáni cím lesz, az AF és BC regisztereket pedig sikeres EXOS 6 hívásnak megfelelően állítja be (azért csak a C-t törli, mert itt a BC már nulla, a másik lehetséges helyen, ahonnan ide lehet ugrani, pedig a B mindig nulla). Szintén itt történne a keretszín nullára állítása, ha a keretcsíkozás engedélyezve lenne.
A két POP DE-nek csak a tömörített formátum utáni visszatérésnél van jelentősége, az első egy visszatérési címet töröl.

Az RST 28H folytatása tömörített formátum esetén (BC > 0)

0077
0078
0079
007A
007B
007C
007D
007E
007F
0080
F3
D5
D9
E1
D9
EB
09
E5
2B
EB
DI
PUSH DE
EXX
POP HL
EXX
EX DE, HL
ADD HL, BC
PUSH HL
DEC HL
EX DE, HL

Letiltja a megszakításokat (kisebb sebességnövekedés), a HL' regiszterben tárolja az utolsó beolvasott byte utáni címet (ez lesz majd a tömörített adat olvasási címe), ehhez hozzáadva a méretkülönbséget kiszámítja az utolsó kitömörített byte utáni címet és tárolja a veremben (ahonnan majd a fenti második POP DE utasítás fogja olvasni), és a DE regiszter, amely az aktuális írási cím, pedig ennél eggyel kevesebb lesz.

0081
0084
01 01 10
DF
LD BC, 1001
RST 18

Itt a C regiszter a bitek olvasásához kell, minden tömörített bit beolvasásakor ez a regiszter léptetődik jobbra, és a bit a Carry-be kerül. Azért 1 a kezdőértéke, mert ez a bit jelzi, hogy mikor fogyott el az összes bit; ha a jobbra léptetés eredménye nulla, akkor előbb új byte-ot kell beolvasni, majd megint jobbra léptetni úgy, hogy a legfelső bitnek 1 lépjen be. Az 1 tehát azt jelenti, hogy a regiszter üres, és az első bitnél új byte-ot kell majd olvasni.
Az RST 18H a B regiszternek megfelelő számú bitet olvas a HL regiszterbe egy 1 bit után, tehát pl. B=3 esetén HL=1xxxB lesz. A B 0 is lehet, ilyenkor mindig 1 a visszatérési érték.
Itt ennek csak az a célja, hogy 16 bitet, azaz két byte-ot olvasson, hogy az olvasáshoz használt két byte-os puffer (a BC' regiszter) feltöltődjön.

0085 CF RST 08

Az RST 08H egy hosszúság paramétert olvas tömörítetlen sorozathoz - ez a fent leírt kitömörítő ciklus első pontja.
A hosszúságok kódolásának a lényege, hogy először annyi 1 bit van, amennyi bitet az RST 18H-nak be kell majd olvasnia, majd egy 0 bit, végül pedig a beolvasandó bitek (lásd fent).

0008
000B
000C
000E
21 00 00
19
38 03
18 50
LD HL, 0000
ADD HL, DE
JR C, 0011
JR 0060

Itt először annak az ellenőrzése történik, hogy a kitömörítés befejeződött-e már. Az LD HL utasítás paramétere valójában a még az RST 28H elején kiszámított -KEZDŐCÍM. Tehát ha a következő byte írási címe már kisebb, mint a kezdőcím, akkor a kitömörítés befejeződött. Érdemes megjegyezni, hogy ez a megoldás nem működik akkor, ha a 0000H címre is töltődne adat.
Az RST 08H feltétezi, hogy a hívásakor a B regiszter 0.

0010
0011
0013
0016
04
CB 39
CC 86 00
38 F8
INC B
SRL C
CALL Z, 0086
JR C, 0010

Itt történik a hosszúság paraméter elején az 1 bitek számolása. A CALL 0086H tömörített byte olvasását végzi, ha a C regiszterből elfogytak a bitek. A B regisztert a ciklus minden beolvasott 1 bit után növeli.

0018
0019
001C
001D
04
21 01 00
05
C8
INC B
LD HL, 0001
DEC B
RET Z

Ez az RST 18H rutin, amely a fent leírt módon B bitet olvas a HL regiszterbe. Először ellenőrzi, hogy a B nem 0-e: ha igen, akkor a HL inicializálása után azonnal visszatér.
Végül a bitolvasó ciklus:

001E
0020
0023
0025
0027
CB 39
CC 86 00
ED 6A
10 F7
C9
SRL C
CALL Z, 0086
ADC HL, HL
DJNZ 001E
RET

Megjegyzendő, hogy a Z bit visszatéréskor 1, ha híváskor a B 0 volt, egyébként (szabályos bemeneti adat esetén, azaz ha B <= 15) mindig 0. Ezt a kód máshol felhasználja.

A tömörítetlen sorozatot másoló ciklus, egyben tömörített byte olvasása a C regiszterbe (a Carry bittől függően: az RST 08H/18H mindig Carry=0-val tér vissza, a C regiszterből az utolsó bitet kiléptető SRL utasítás viszont beállítja a Carry-t):

0086
0087
0088
0089
008A
008B
008C
008E
008F
0090
D9
78
41
2B
4E
D9
30 03
1F
4F
C9
EXX
LD A, B
LD B, C
DEC HL
LD C, (HL)
EXX
JR NC, 0091
RRA
LD C, A
RET

Itt a BC' regiszter a 2 byte-os bemeneti puffer, és a tömörített adat olvasási címe a 007AH-nál inicializált HL' regiszter. Ha lenne keretcsíkozás, akkor az olvasott byte-ot a 81H portra is kiírná.
Ha nem a C regiszter feltöltése történik (Carry=1), akkor folytatódik a másoló ciklus:

0091
0092
0093
0094
0095
0096
12
1B
2B
7D
B4
20 EE
LD (DE), A
DEC DE
DEC HL
LD A, L
OR H
JR NZ, 0086

Kiírja az olvasott byte-ot, csökkenti az írási címet, és folytatja a következő byte-al, amíg a HL-ben tárolt hosszúság elfogy. Érdekesség, hogy a tömörítetlen byte-okat nem bitenként olvassa, hanem egyszerűen a következő byte-ot a bemeneti adatból - így ugyan a bitek "kevert" sorrendben vannak, és bonyolultabb a file írása, viszont azonos file méret mellett egyszerűbb és gyorsabb lehet a kitömörítő.

Tömörítetlen sorozat után mindig egy tömörített következik:

0098
0099
CF
E5
RST 08
PUSH HL

Ez szintén hosszúság olvasással kezdődik (amit egyelőre elment a verembe), de itt a hosszúsághoz hozzá kell majd adni egyet, mert a legrövidebb sorozat 2 byte-os.
Tömörített sorozatnál természetesen van egy távolság paraméter is, amelynek a dekódolása valamivel bonyolultabb. A formátum különböző 2 byte-os, és hosszabb sorozatoknál. Az első esetben előbb be kell olvasni 3 bitet, majd az így kapott értéknél eggyel több bitet olvasni RST 18H-val, és végül az eredményből kivonni egyet; így csak 1 és 510 közötti távolság tárolható, viszont kevesebb biten, mint a hosszabb sorozatoknál. Azoknál ugyanis egyszerűen 4 bitet kell olvasni, majd azzal az értékkel (0-15) meghívni az RST 18H-t; ez 1 és 65535 közötti távolságot tesz lehetővé, de általában eggyel több biten.

009A
009B
009D
68
06 04
28 16
LD L, B
LD B, 04
JR Z, 00B5

Először törli az L regisztert (a B RST 18H után mindig 0), majd a B-t "hosszú" sorozatot feltételezve 4 bitre állítja. Azonban, ha 2 byte-os a sorozat (Z=1), akkor ez történik:

00B5
00B8
00B9
00BA
00BB
00BC
CD 25 00
2C
45
DF
2B
18 E6
CALL 0025
INC L
LD B, L
RST 18
DEC HL
JR 00A4

Ez először ráugrik a bitolvasó ciklus DJNZ utasítására, amely így csak 3 bitet olvas 4 helyett, majd az olvasottnál eggyel több bitet léptet 1 után a HL regiszterbe. Végül a távolság értéket eggyel csökkenti, hogy a tartomány 2-511 helyett 1-510 legyen.

A "hosszú" sorozatok távolság dekódolása, majd a tömörített sorozat másolása:

009F
00A2
00A3
00A4
00A5
00A6
00A8
00A9
00AB
CD 1E 00
45
DF
19
79
ED A8
C1
ED B8
4F
CALL 001E
LD B, L
RST 18
ADD HL, DE
LD A, C
LDD
POP BC
LDDR
LD C, A

A CALL 001EH beolvas 4 bitet az L regiszterbe, majd az ennek megfelelő számú (0-15) bit léptetése történik RST 18H hívással.
00A4H-nál már minden lehetséges hosszúságnál a dekódolt távolság van a HL regiszterben, tehát ezt a DE-hez (írási cím) hozzáadva - a fordított irány miatt SBC helyett ADD kell - kiszámítható a másolás forráscíme. Az LDD átmásolja az első byte-ot, ezt követően a hosszúság-1-et a veremből a BC regiszterbe olvasva az LDDR-el már befejezhető a másolás. Közben természetesen a bitléptetéshez használt C regisztert menteni kell.

00AC
00AE
00B1
00B3
CB 39
CC 86 00
30 D2
18 E3
SRL C
CALL Z, 0086
JR NC, 0085
JR 0098

Végül a ciklus befejezése: a jelzőbitnek megfelelően tömörítetlen (0085H) vagy tömörített (0098H) sorozattal folytatódik a kitömörítés. Ha itt már vége az adatnak, akkor a jelzőbit ugyan szemét lesz, de az értékétől függetlenül mindig hosszúság olvasás következik, amellyel befejeződik az RST 28H hívás.

IstvánV (2009)

A Bináris számokról
A digitális számítógépek és a digitális technikával készült alkatrészek olyan áramkörökből állnak, amelyek lényegében kétállapotúak (binárisak). Mivel minden alkatrész gyorsabban tudja felismerni az adandó funkciót, ha csak két esetleges állapotot kell ismerniük. Kevesebb a zavarás és a hibalehetőség. Például ezért találhatóak számítógépekben kizárólagosan csak digitális alkatrészek. Tehát a számítógép által használt jelek binárisak, így a változói is binárisan értékelhetőek.

Számítások Bináris számokkal
Összeadás
A kettes számrendszer egyik előnye, hogy a műveletek végzése sokkal egyszerűbb mint például a tízes számrendszerben. Ahhoz, hogy a tízes alapú számokat összeadjuk, kifejezetten szükséges az iskolában megtanult "összeadás" táblázat. De mivel a számok sorrendje a kettes számrendszer esetében nem közömbös, ezért így több száz állapot lehetséges már igen kis tagszámú számsornál is az összeadáskor.
A kettes számrendszerben könnyű a dolgunk, mert csak a következő négy állapot lehetséges:
00 + 00 = 00
01 + 00 = 01
00 + 01 = 01
01 + 01 = 10
Mindezt példával is szemléltetem, hogy érthető legyen, tízes számrendszerben is elvégzem a műveletet:

11101
29
+01100
+12
=111001
=41

Az összeadást itt is a legkisebb helyértékű számjeggyel kezdjük, és úgy haladunk jobbra.
Az iskolában valahogy így tanultunk számolni: Kilenc, meg kettő az tizenegy, leírjuk az egyet, marad egy, kettő meg egy az három meg egy az négy. Leírom a négyet, és a végeredmény negyvenegy.
Ez most is igaz. De itt most a jelenlegi példánkban így szól: Egy meg nulla az egy, leírjuk az egyet, átvitel nincs. Nulla meg nulla az nulla, leírjuk a nullát. És így tovább az előbbi táblázat szerint.
Most már tudunk bináris számokat összegezni, amint figyeltük nem kellett hozzá csak néhány perc. Persze nem árt ezt még egy kicsit gyakorolni, és tízes számrendszerben ellenőrizni számításaink pontosságát.

Kivonás
A kivonás a kettes számrendszerben lényegesen nehezebb mint az összeadás, de nem is érdemes vele foglalkozni, mert a számítógépek teljesen másképp végzik; de azért megemlítem: A kivonás ez esetben is úgy történik, mint a tízes számrendszerben. Jobbról a legalacsonyabb helyértékű részről indulunk bal felé. A kivonást számjegyenként kell elvégeznünk. Ha a kivonandó magasabb, akkor a magasabb helyértektől veszünk kölcsön.

11011001
217
-01101111
-111
=01101010
=106

Szorzás
Most is hasonlóan végezzük a műveletet, mint a tízes alapnál. Minden lépésben egy számjeggyel szorzunk végig minden értéket. A részszorzatot is eltolhatjuk egy vagy több hellyel. Hogy látható legyen ez milyen egyszerű, megmutatom a szorzótáblát:

A
B
A*B
00
00
00
00
01
00
01
00
00
01
01
01

Szorzatok 2 hatványaival:
Most már minden baj nélkül tudunk szorozni 2 hatványaival. Egyszerű, hiszen kettővel csak úgy kell szorozni, hogy a végeredmény után 0-át írunk.
11 x 10 = 110
Az osztást azért nem írom le, mert a számítógép teljesen másképp használja az osztást mint mi. A bonyolultság szempontjából ez teljesen egyedülálló.

Piotr - Enterpress 1993/1-2

Cartridge átalakítások
Az ENTERPRESS 91/5. számában jelent meg egy cikk "Mi lakik a cartridge-ben" címmel, amely nem sok gyakorlati tanácsot adott, pedig ha az EPROM-ha égethető programok hosszú listáját nézzük, akkor igencsak elkellene egy kis "lakás bővítés". Kezdetnek vegyünk egy egyszerű 16 Kilobájtos EPROM helyet és nézzük, hogy lesz ebből 32 Kilobájtos:
Természetesen be kell kötni még egy címvezetéket, ehhez a foglalat 27-es lábáról le kell kötni a +5 Volt-ot (általában a 28-as lábbal van összekötve), és rákötni az A14-et, ez a művelet az eredeti EXDOS kártyákon nagyon egyszerű mivel tervezéskor már gondoltak erre, ezért be is van jelölve az átvágás és az átkötés helye. (De csak az 1.0-ás EXDOS-t tartalmazó kártyákban van 16 Kilobájtos EPROM, az EXDOS 1.3 már 32 Kilobájtos.)
A kétnyelvű gépek kétszer 16 Kilobájtos cartridge-iben ezenkívül még gondoskodni kell a megfelelő címkiválasztásról, hogy az EPROM-ok ne fedjék át egymást:

Ha 32 Kilobájtos EPROM-helyünk van, akkor kétféle dolgot csinálhatunk belőle:
- 64 Kilobájtos EPROM helyet,
- 32 Kilobájtos SRAM helyet.
Elsőként nézzük a másodikat, mivel arról eddig még nem sokat írtak:
Mire is jó a cartridge-ben a statikus RAM? Ugyanúgy használhatjuk, mint egy EPROM-ot, csak mivel RAM, ezért bármikor átírhatjuk a tartalmát: azt a (EPROM-ba égethető) programot tölthetjük bele amelyikre éppen szükségünk van, és tartalma hidegindítás után is megmarad (megfelelő bekötés esetén kikapcsolás után is!), így nagy segítség lehet ilyen programok íróinak is.
Ha pedig nem EXOS 2.1-et, vagy valami más megfelelő gyorstesztet használunk, akkor ha úgy tartja kedvünk, akár memória bővítésnek is felhasználhatjuk, esetleg "szünetmentes" RAMDISK-nek!
Lássuk tehát mit fed a CMOS SRAM-ok EPROM-okhoz hasonló lábkiosztása:
Mivel RAM-ról van szó, ezért szükséges az írás engedélyező jel (R/W), ami a SRAM (62256) 27-es lábára kerül, ezért az EPROM-on (27256) itt található A14 máshová kerül, mégpedig a SRAM 1-es lábára, amiről le kell kötni a +5 Volt-ot.
Ha azt is el akarjuk érni, hogy a SRAM ne veszítse el tartalmát, akkor gondoskodni kell a tápfeszültségről a gép kikapcsolása után is. Ez megoldható elemmel is, de szerintem egyszerűbb a kondenzátoros változat: kössünk egy kondenzátort a SRAM 14-es és 28-as lába közé. A 28-as lábat egy diódával (a dióda "csíkos" vége legyen a láb felé) kössük a +5 Volt-ra, különben a kondenzátor nem csak a SRAM-ot látja el feszültséggel. Nem árt ha egy 4.7 Kohm-os felhúzó ellenállást kötünk a +5 Volt (1-es láb) és a CE (20-as láb) közé, a bekapcsolásnál esetleg bekövetkező adatvesztés ellen.
(Megjegyzem: ez a statikus RAM "buli" nem a legolcsóbb. A 62256-os 900-1000 forintba kerül.)

Most pedig lássuk a 64 Kilobájtos (27512) EPROM-okat: ismét szükségünk lesz egy új címvezetékre, amit a foglalat 1-es lábára kötünk, de előtte kössük le arról a +5 Volt-ot! Ha ezt az eredeti EXDOS kártyán játsszuk el akkor kössük össze az EPROM (U2) 28-as lábát a WD (U1) 15-ös lábával, hogy az is megkapja a tápfeszültséget. (Vigyázzunk arra, hogy az EPROM foglalatok 1-es és 28-as lába általában alul-felül össze van kötve!)
Ezt az átalakítást eddig (a cartridge-ben) csak az 1*32 Kilobájtos típuson lehetett elvégezni, mivel egyetlen 27512-es EPROM kitölti a cartridge 64 Kilobájtos címtartományát. De Zozosoft&Apuci úgy döntöttek, hogy akkor növeljük meg a címtartományt 128 Kilobájtra...

Ehhez már a gépet is át kell alakítani, tehát csak a bátrabb olvasók próbálják meg saját kezűleg elvégezni! Először az "emelet":

Akkor kell a cartridge-t engedélyezni, ha a legfelső 4 címvezeték (A18-A21)=0, és ha az A16=1 (így különböztetjük meg a belső ROM-tól), így a cartridge a 04h,05h,06h,07h,0Ch,0Dh,0Eh,0Fh szegmenseket jelenti.
Szükségünk lesz egy 74138-as IC-re, ami lehetőleg minél gyorsabb típusú legyen (ALS, HCT, F), ennek az engedélyező és kiválasztó bemeneteire kötjük az A16, A18, A19, A20, A21 címvezetékeket:

Ezek után nézzük a cartridge-et:
Erre a célra a 2*32 Kilobájtos (ha 2*16 Kilobájtos, akkor előbb alakítsuk át) eredeti, vagy az alapján készült cartridge-ek használhatók.

Ahhoz, hogy az így kibővített cartridge-be helyezett programokat meg is találja a gép, megfelelő gyorsteszt is kell (ilyen már több is készült), vagy a Zozotools RL NEW parancsával láncolhatjuk be a programokat. (Pl.: RL NEW,30h,20h,0Fh,0Ch,07h,06h,04h,03h,02h)
Nézzük a kompatibilitási kérdéseket:

Két áramkörös, kétállású kapcsoló bekötése:
DAVE CART - - U31 3-as láb
cartridge B4 - - cartridge B3
74138 1-es láb - - A17

Az átalakításhoz segítségül néhány csatlakozó kiosztás, és az IC lábkiosztása:

EP BUS
 
Cartridge
Billentyűzet felöli oldal
 
hűtőborda felöli oldal
A  
B
  A  
B
AUDIO R
1
AUDIO L
  GND
1
+5V
RFSH
2
WR
  D3
2
D4
RD
3
IORQ
  D2
3
??? (U31 3-as láb)
+5V (a gépen üres)
4
+5V (a gépen üres)
  D5
4
CART
MREQ
5
NMI
  D6
5
A15
A8
6
A9
  D1
6
A14
A10
7
A11
  D0
7
A13
A12
8
A13
  D7
8
R/W
A14
9
A15
  A1
9
OE
A0
10
A1
  A2
10
A12
A2
11
A3
  A5
11
A11
A4
12
A5
  A4
12
A10
A6
13
A7
  A3
13
A9
D0
14
D1
  A0
14
A8
D2
15
D3
  A6
15
A7
D4
16
D5
  GND
16
+5V
D6
17
D7
       
RESET
18
INT
       
WAIT
19
GND
 
Felső RAM panel
MI
20
GND
 
EXP2
1 Mhz
21
GND
  A  
B
CLK
22
GND
  GND
1
+5V
8 Mhz
23
GND
  GND
2
GND
EC0
24
EC1
  A21
3
A20
EC2
25
EC3
  A19
4
A18
EXTC
26
A16
  A17
5
A16
A17
27
A18
       
A19
28
A20
 
EXP1
A21
29
14 Mhz
  RFSH
6
MREQ
GND
30
VSYNC
  D6
7
D7
GND
31
Üres
  D4
8
D5
HSYNC
32
GND
  D2
9
D3
GND (a gépen +9V!)
33
GND (a gépen +9V!)
  D0
10
D1
Innentől már csak a buszbűvítőn vagy hídon
  A6
11
A7
Üres
34
Üres
  A4
12
A5
GND
35
+4V
  A2
13
A3
GND
36
GND
  A0
14
A1
Üres
37
Üres
  A14
15
A15
        A12
16
A13
        A10
17
A11
        A8
18
A9
        WR
19
RD
       
Billentyűzet felöli oldal

IC-k lábkiosztása

27512
62256
27256
27128
27128
27256
62256
27512
64 Kbyte
32 Kbyte
32 Kbyte
16 Kbyte
16 Kbyte
32 Kbyte
32 Kbyte
64 Kbyte
A15
A14
Vpp
Vpp
+5 V
+5 V
+5 V
+5 V
A12
A12
A12
A12
PGM
A14
R/W
A14
A7
A7
A7
A7
A13
A13
A13
A13
A6
A6
A6
A6
A8
A8
A8
A8
A5
A5
A5
A5
A9
A9
A9
A9
A4
A4
A4
A4
A11
A11
A11
A11
A3
A3
A3
A3
0E
0E
0E
0E
A2
A2
A2
A2
A10
A10
A10
A10
A1
A1
A1
A1
CE
CE
CE
CE
A0
A0
A0
A0
D7
D7
D7
D7
D0
D0
D0
D0
D6
D6
D6
D6
D1
D1
D1
D1
D5
D5
D5
D5
D2
D2
D2
D2
D4
D4
D4
D4
GND
GND
GND
GND
D3
D3
D3
D3

ZozoHard - Enterpress 1993/3

Az életjáték

Az Életjáték lassan több évtizede a matematikusok (és nem matematikusok) kedvenc időtöltése. Elegendő hozzá egy kockás papír és egy ceruza, meg némi türelem; persze, igazi reneszánszát a játék a számítógépek elterjedésével éli.
Az Életjáték a sejtautomaták tudományának egy "mellékterméke". A sejtautomaták elmélete egymás mellett elhelyezett sok-sok egyszerű elemből felépülő rendszerekkel foglalkozik. Ezek az elemek az élő szervezet sejtjeihez hasonló elrendezésben töltik ki a síkot vagy a teret, az egyes sejtek csak a közvetlen szomszédjaikkal képesek kommunikálni. A sejtautomaták régóta izgatják a szakemberek fantáziáját, hiszen köztudomású, hogy az emberi agy felépítéséhez ezek sokkal közelebb állnak, mint a jelenleg alkalmazott számítógépek. Elképzelhető, hogy a sejtautomaták segítségével olyan problémák is megoldhatók lesznek, amelyeket eddig a hagyományos számítógépekkel nem, vagy csak nagyon körülményesen lehetett megoldani. Csak példaképpen: bizonyított tény, hogy építhető olyan sejtautomata, amelyik képes önmaga reprodukálására, azaz szaporodásra; ez azt jelenti, hogy rendelkezik az élő szervezet egyik jellemző tulajdonságával. Az önreprodukálásra majd látunk egy egyszerűsített példát.
Az élő szervezetek, különösen pedig az ember másik jellemzője a tanulás és a képzettársítás; ezen a területen a sejtautomaták egyik ága, az úgynevezett neuronhálózatok - az emberi agy szerkezetét és működését utánozni próbáló rendszerek - mutattak fel egyre izgalmasabb eredményeket.
De térjünk vissza az Életjátékhoz! A játéktér egy - elvileg - végtelen kiterjedésű sejttér, amit egy négyzetrácsos (a köznyelvben egyszerűen kockás) papírral modellezünk. A sejttér minden elemének - a játék hagyományos formájában - két állapota lehet: vagy tartalmaz egy élő sejtet, vagy üres. A játék valamilyen - véletlenszerű vagy célzatosan megválasztott - kiinduló konfigurációval indul. A továbbiakban az élő sejtek jól definiált szabályok szerint tovább élnek vagy elpusztulnak; az üres mezőkön pedig új sejtek születhetnek. Mindezekről az határoz, hogy az adott mezővel szomszédos mezők mit tartalmaznak.
Minden mezőnek 8 lehetséges szomszédja van, a négy oldalszomszéd és a négy sarokszomszéd. Az ábrán a körrel jelölt ("lakott") mező nyolc szomszédos mezejét ponttal jelöltük.

A sejtek úgy viselkednek, mint az élő sejtek: egyedül, egymás nélkül elpusztulnak, ugyanakkor a zsúfoltságot sem tudják elviselni, agyonnyomják egymást. Ha viszont kedvező körülmények uralkodnak, az üres mezőben egy új sejt keletkezik.
Az Életjáték eredeti, Conway által kigondolt formájában a születési, meghalási és életbenmaradási szabályok a következők:
Ha egy élő sejtnek nincs szomszédja, vagy csak egy szomszédja van, elpusztul az egyedülléttől. Ha négy vagy több szomszédja van, ugyancsak elpusztul a túlzsúfoltságtól. A túléléshez tehát két vagy három szomszéd szükséges.
Ha egy üres mezőnek pontosan három élő szomszédja van, a mezőben új sejt születik. A sejtek pusztulása és az új sejtek születése az adott konfigurációban egyszerre, "vezényszóra" megy végbe, így a változás nem függ a kiértékelés sorrendjétől.

Az Életjáték azért jelent igazi kihívást a játékos elmének, mert sokszor egészen egyszerű kiinduló alakzatból sok száz vagy sok ezer lépésen keresztül változó, fejlődő sejtközösség alakul ki.
A következő ábrán látható kezdő konfigurációban (három sejt vízszintesen egymás mellett) a két szélső sejtnek csak egy-egy szomszédja van, ezért a következő lépésben ezek elpusztulnak; a középső sejtnek viszont két-két szomszédja van, ezért ez megmarad. Hogy a dolog ne legyen ilyen szomorú, a középső sejt alatti és feletti mezőnek három-három szomszédja van, ezért ebben a két mezőben egy-egy új sejt születik. Eredményül a jobb oldali ábrán látható elrendezést kapjuk (mintha a három sejtből álló alakzat elfordult volna 90 fokkal). Nem nehéz rájönni, hogy a következő lépésben újra visszakapjuk a kiinduló alakzatot, és ez így fog ismétlődni az idők végezetéig. Ez az alakzat az Életjáték egyik nevezetes figurája (a neve villogó), gyakran keletkezik bonyolultabb alakzatok szétesésekor. Az alakzat 2 időegység alatt visszakerül a kiindulási állapotába.

Itt mutatunk még három ábrát (a kockát, a cipót és a vitorlázórepülőt). Javasoljuk, hogy próbálja meg végigkövetni fejlődésüket! Próbáljuk meg az öt és a hét, vízszintesen (vagy függőlegesen) egymás mellett lévő sejt életútját megjósolni!

A kocka és a cipó stabil alakzat, nem változik; a vitorlázórepülő viszont változik, de 4 időegységnyi periódussal ismétli önmagát, miközben átlósan lefelé "siklik" a sejttérben.
Eláruljuk, hogy több hasonló vitorlázórepülő létezik; keressük meg ezeket!
Érdekes probléma, hogy létezik-e olyan alakzat, amelyik mozog, és haladtában gőzmozdonyként füstcsíkot húz maga után. És van-e olyan, amelyik járműként végighalad az úton, azaz egymás mellett sorban elhelyezkedő sejteken? Létezik-e ágyú, amelyik valamilyen mozgó alakzatot képes kilőni magából? Mi történik két mozgó alakzat, például két vitorlázórepülő összeütközésekor? Mindezekre a kérdésekre csak úgy érdemes választ keresni, ha nem papíron és ceruzával próbálkozunk, hanem elkészítjük a megfelelő programot.
Mielőtt belevetnénk magunkat a programírásba, néhány apró kérdést meg kell vizsgálni. Először, a konfiguráció változásának egyszerre kell végbemennie. Ehhez külön tárolnunk kell az éppen aktuális konfigurációt, valamint a következő lépésben kialakuló helyzetet (vagy legalább annak egy részét).
A másik, hogy egy-egy életrevalóbb konfiguráció könnyen leszalad a képernyőről. A baj nem is igazán az, hogy ilyenkor nem látjuk a túlszaladt részt, hanem az, hogy a képernyő szélén lévő sejteknél esetleg hibásan generáljuk a következő állapotot.
Ennek elkerülésére a tényleges játékteret érdemes nagyobbra választani, mint ami a képernyőn elfér, és külön mechanizmussal figyelni, ha a növekvő sejtkupac mégis elérné a játéktér szélét.
A harmadik probléma előreláthatóan a sebesség lesz; mivel egy elég nagy területen kell a sejtek változását figyelni, félő, hogy a program rendkívül lassú lesz, különösen BASIC-ben. Meg kell próbálni olyan eszközöket felhasználni, amelyekkel a végrehajtás felgyorsítható.

Most lássunk hozzá a programtervezésnek! Mielőtt bárki nekiesne, hogy

10 LET I=1 ...

vagy a mindenre, csak korszerű programozásra nem alkalmas folyamatábrát kezdenénk rajzolni, próbáljunk nagyobb léptékben gondolkodni. A program két nagyobb egységre bontható. Az egyikben mindenféle beállítást végzünk: kezdőértéket adunk a használt változóknak, letöröljük a képernyőt, beállítjuk a kiinduló konfigurációt stb. Nevezzük ezt a részt ELŐKÉSZÍTÉS-nek. A másik programrész a tulajdonképpeni játék, amelyben az újabb és újabb állapotokat generáljuk. Legyen ennek a neve JÁTÉK. Programunk tehát most így néz ki:

ÉLETJÁTÉK:
   ELŐKÉSZÍTÉS;
   JÁTÉK;
END. (ÉLETJÁTÉK)

Mielőtt bármilyen elhamarkodott következtetést tennénk e két nagyobb egység felépítésére vonatkozóan (tehát mielőtt

100 FOR I=1 TO 24
110   FOR J=1 TO 40

jellegű dolgokat kezdenénk írkálni), gondoljuk át, milyen módon lehet az egymást követő generációkat a gépen ábrázolni! Ehhez nyilván valamiféle táblázatra lesz szükségünk. A feladat megfogalmazásából azonban az is következik, hogy egyetlen táblázat nem elegendő, hiszen az adott elemkonfiguráció csak akkor változhat meg, ha már a teljes jelen konfigurációt kiértékeltük, ellenkező esetben a megoldás módjától függő, hamis eredményt kapnánk.
Kézenfekvő volna egy olyan táblázatot használni, amelynek minden eleme két-két állapotot tárolna: a jelenlegit és a kiértékelés alatti következőt. Sajnos, a BASIC nyelvek többsége, így az IS-BASIC sem engedi meg összetett típusok használatát. Maradna egy háromdimenziós tömb lehetősége, amelyben az első két dimenzió értelemszerűen a sor-és az oszlopkoordináta volna, a harmadik pedig mindössze két értékkel bírna, a jelenlegi és a következő állapot tárolására. Sajnos, a BASIC csak kétdimenziós tömböt enged meg. Summa summárom, bármennyire ellenkezik a józan programozói gondolkodással, marad a két különböző táblázat használata. Ennél most ne is menjünk tovább.
A program pszeudokódja most így néz ki:

ÉLETJÁTÉK:
VAR
   TÁBLA-1
   TÁBLA-2
BEGIN
   ELŐKÉSZÍTÉS;
   JÁTÉK;
END.

A program JÁTÉK részéről tudjuk, hogy ismétlődő folyamat:

JÁTÉK:
   REPEAT
      LÉPÉS
   END-REPEAT;
END. (JÁTÉK)

Az ELŐKÉSZÍTÉS részt pedig így bontjuk ki:

ELŐKÉSZÍTÉS:
   ALAPHELYZET (TÁBLA-1, TÁBLA-2);
   KEZDŐKONFIGURÁCIÓ (TÁBLA-1);
   EGYÉB;
END

Önkényesen TÁBLA-1-et választottuk a kezdő konfiguráció számára. A másik alternatíva ugyanilyen jó lett volna; mivel a döntésnek nincs igazán jelentősége, nem töprengünk rajta sokáig (a tett halála az okoskodás).
Most értünk el oda, hogy a táblázatok pontosabb definiálását már nem lehet tovább halasztani. Kézenfekvő, hogy valami ilyesfajta deklarációjuk legyen:

VAR TÁBLA-1, TÁBLA-2 : ARRAY [SOROK, OSZLOPOK] OF ADATELEM;

Egyáltalán nem sietünk viszont meghatározni, mit értünk sorokon és oszlopokon, sem pedig adatelemen.
Ha viszont ismerjük a táblázatok szerkezetét, pontosíthatjuk a JÁTÉK programrész LÉPÉS elemét:

LÉPÉS:
   SOROKON-VÉGIG DO
      OSZLOPOKON-VÉGIG DO
         TEDD-AMIT-KELL;
      END-DO;
   END-DO:
END (LÉPES)

Tovább nem tudunk menni, mert nem tudjuk, MIT-KELL-TENNI (sic). Előbb valamiféle döntést kell hoznunk az adatok ábrázolásáról. Egy pillanatra megfeledkezve rossz BASIC-es beidegződéseinkről és az azzal járó 0 és 1 vagy hasonló választékról, meghatározzuk a táblázatelemek típusát:

TYPE ADATELEM : (ÉL, NEM-ÉL);

Ennek alapján most már elég részletes megoldást tudunk adni az ALAPHELYZET programrészre:

ALAPHELYZET:
   SOROKON-VÉGIG DO
      OSZLOPOKON-VÉGIG DO
         TÁBLA-1 [SOR, OSZLOP] := NEM-ÉL;
         TÁBLA-2 [SOR, OSZLOP] := NEM-ÉL;
      END DO
   END-DO
END (ALAPHELYZET)

A két táblázatot alaphelyzetbe állíthattuk volna két külön ciklusban is, de intuitív módon gyorsabbnak találtuk ezt a megoldást. A kezdő konfiguráció beállításával most nem törődünk; a program kipróbálásához majd megadunk valami egyszerű elrendezést.
Itt az ideje, hogy komolyan belenézzünk a megoldás részleteibe. Jól tudjuk, hogy egy adott cellában az ott lévő sejt megmaradása vagy elpusztulása, illetve egy üres cellában egy sejt születése vagy nem születése valamilyen módon a cella szomszédaitól, egészen pontosan a szomszédai számától függ. Ez pontosan elegendő információ a folytatáshoz. Definiálunk egy FÜGGVÉNY nevű függvényt, amelynek bemenő paraméterei a szomszédok száma és a cella saját állapota (ÉL vagy NEM-ÉL), a függvényérték pedig ugyanilyen típusú, vagyis azt mondja meg, hogy a következő pillanatban ugyanitt lesz-e sejt vagy nem.

TEDD-AMIT-KELL:
   TÁBLAEELEM (MÁSIK-TÁBLA) :=
      FÜGGVÉNY (SZOMSZÉDOK, TÁBLAELEM (EGYIK-TÁBLA));
END (TEDD-AMIT-KELL)

Most segítene, ha a táblázatokat értelmesen meg tudtuk volna határozni, így kénytelenek vagyunk egy ostobább megoldást alkalmazni (a "...maga mindent kétszer mond, kétszer mond..." fajtából):

TEDD-AMIT-KELL:
   IF MUTATÓ = EGYIK-ÁLLAPOT THEN
      TÁBLA-2 [SOR, OSZLOP] := FÜGGVÉNY(SZOMSZÉDOK,TÁBLA-1 [SOR,OSZLOP]);
   ELSE
      TÁBLA-1 [SOR, OSZLOP] := FÜGGVÉNY(SZOMSZÉDOK,TÁBLA-2 [SOR,OSZLOP]);
   END-IF;
END (TEDD-AMIT-KELL)

Ez feltételezi a globális MUTATÓ változó létezését, amellyel sok bajunk lesz: először is deklarálni kell; másodszor kezdőértéket kell kapnia; harmadszor, minden LÉPÉS után másik (nem más!) állapotba kell billentenünk. Ezt adminisztráljuk:

VAR MUTATÓ: (EGYIK-ÁLLAPOT, MÁSIK-ÁLLAPOT);
LÉPÉS:
   SOROKON-VÉGIG DO
      OSZLOPOKON-VÉGIG DO
         TEDD-AMIT-KELL
      END-DO
   END-DO
   INVERTÁL (MUTATÓ);
END (LÉPÉS)

és

ELŐKÉSZÍTÉS:
   ALAPHELYZET (TÁBLA-1, TÁBLA-2);
   KEZDŐ-KONFIGURÁCIÓ (TÁBLA-1);
   MUTATÓ := EGYIK-ÁLLAPOT;
   EGYÉB;
END (ELŐKÉSZÍTÉS)

Most már csak SZOMSZÉDOK és a FÜGGVÉNY függvény szorul némi pontosításra. Az Életjátékban az elem négy él- és négy sarokszomszédja számít. Egy pillanatra elcsábulnánk, hogy írjunk egy kettős ciklust, amelyik az elem előtti sortól és oszloptól az utána következő sorig és oszlopig megy, nem felejtve el kihagyni a számításból magát az elemet; de lerázzuk a sematizmus béklyóját, megérezve, hogy a megoldás lassú lenne. (IS-BASIC-ben a kettős ciklus nagyon lassú. Vegyük észre, hogy a LÉPÉS saját két ciklusával együtt négyszeresen skatulyázott ciklusunk volna!) Inkább leírjuk nyolcszor egymás után (a "kétszer mond" effektust figyelembe véve, 16-szor) az indexkifejezést (egy jó szövegszerkesztővel ez nem probléma):

SZOMSZÉDOK (SOR, OSZLOP):
   IF MUTATÓ = EGYIK-ÁLLAPOT THEN
      RETURN WITH TÁBLA-1 [SOR - 1, OSZLOP - 1]
         + TÁBLA-1 [SOR - 1, OSZLOP] + TÁBLA-1 [SOR - 1, OSZLOP + 1]
         + TÁBLA-1 [SOR, OSZLOP - 1]
         { itt kimarad az elem maga }
         + TÁBLA-1 [SOR, OSZLOP + 1] + TÁBLA-1 [SOR + 1, OSZLOP - 1]
         + TÁBLA-1 [SOR + 1, OSZLOP] + TÁBLA-1 [SOR + 1, OSZLOP + 1]
   ELSE
      RETURN WITH {ugyanaz, mint az előbb, csak TÁBLA-1 helyett TÁBLA-2 mindenütt}
   END-IF
END (SZOMSZÉDOK)

FÜGGVÉNY (SZOMSZ, SAJÁTÉRTÉK):
   IF SAJÁTÉRTÉK = NEM-ÉL THEN
      IF SZOMSZ = 3 THEN
         RETURN WITH ÉL
      ELSE
         RETURN WITH NEM-ÉL
      END-IF
   ELSE { azaz ha él a sejt }
      IF SZOMSZ = 2 OR SZOMSZ = 3 THEN
         RETURN WITH ÉL
      ELSE
         RETURN WITH NEM-ÉL
      END-IF
   END-IF
END (FÜGGVÉNY)

Már majdnem készen vagyunk, csak azt kell eldöntenünk, hogy mit jelentsen a SOROKON-VÉGIG és az OSZLOPOKON VÉGIG. Mivel a táblázat legszélén a SZOMSZÉDOK függvény nem működik (kilóg a lóláb, akarom mondani, az index az indexhatárból), a LÉPÉS célszerűen a második sortól és oszloptól az utolsó előtti sorig és oszlopig megy majd. Mielőtt elhamarkodnánk, definiálunk néhány konstanst:

CONST
   SORSZÁM = 24;
   OSZLOPSZÁM = 38;

Első próbálkozásként alkalmazkodunk a megjeleníthető mérethez, nem elhanyagolva keserű tapasztalatunkat, hogy az utolsó két oszlopot az EDITOR "nem szereti", esetleg átviszi kedves sejtjeinket a következő sorba. Később úgyis rájövünk, hogy ez az élettér komolyabb konfiguráció esetén kevés, akkor csak ezt a két konstanst kell majd módosítani. Akkor viszont majd ügyelni kell arra, hogy mi látszik az ábrából.
Erről eszembe jut, hogy jelenleg semmi sem látszik. Kacérkodunk a gondolattal, hogy esetleg egy kettős ciklussal minden LÉPÉS után végigsöprünk az aktuális táblán, és kirajzoljuk a képernyőre a tartalmát. Ez persze rettenetesen lassú lenne. Azután eszünkbe jut, hogy egy-egy lépés során a konfiguráció nem nagyon változhat, hiszen minden sejt csak a közvetlen környezetére gyakorol hatást. Ezért úgy döntünk, hogy mindig csak a változást rajzoljuk, ehhez pedig (bár programozói énünk - helyesen - berzenkedik ellene) a rajzolást becsempésszük a FÜGGVÉNY függvénybe (ott ugyanis mindig tudjuk, van-e változás). Emiatt a FÜGGVÉNY paraméterei kibővülnek a sor- és az oszlopkoordinátával.
Ha az ELŐKÉSZÍTÉS eljárás KEZDŐKONFIGURÁCIÓ részébe beleügyeskedünk egy egyszerű kezdő ábrát, nem feledkezve meg annak kirajzolásáról sem, akkor egy Pascal-szerű nyelvet használva szinte változtatás nélkül indíthatnánk a programot; ha beziklábasan (bocsánat, BASIC-ben) dolgozunk, akkor viszont egy kevés kódolási munkával az alábbi programhoz jutunk:

100 PROGRAM "Lifegame.bas"
110 LET SORSZ=23:LET OSZLSZ=38
120 NUMERIC E(1 TO 23,1 TO 38)
130 NUMERIC F(1 TO 23,1 TO 38)
140 NUMERIC SOR1,SOR2,OSZL1,OSZL2,Q
150 LET SOR1=1:LET SOR2=SORSZ:LET OSZL1=1:LET OSZL2=OSZLSZ:LET Q=1
160 DEF KEZD
170   FOR II=1 TO SORSZ
180     FOR JJ=1 TO OSZLSZ
190       LET E(II,JJ),F(II,JJ)=0
200     NEXT JJ
210   NEXT II
220   LET E(2,4),E(3,2),E(3,4),E(4,3),E(4,4)=1
230   PRINT AT 2,4:"*"
240   PRINT " * *"
250   PRINT "  **"
260 END DEF
270 DEF SZOMSZ(S,O)
280   IF Q>0 THEN
290     LET SZOMSZ=(E(S-1,O-1)+E(S-1,O)+E(S-1,O+1)+E(S,O-1)+E(S,O+1)+E(S+1,O-1)+E(S+1,O)+E(S+1,O+1))
300   ELSE
310     LET SZOMSZ=(F(S-1,O-1)+F(S-1,O)+F(S-1,O+1)+F(S,O-1)+F(S,O+1)+F(S+1,O-1)+F(S+1,O)+F(S+1,O+1))
320   END IF
330 END DEF
340 DEF FUGGV(SZ,P,K,L)
350   IF P=0 THEN
360     IF SZ=3 THEN
370       LET FUGGV=1
380       PRINT AT K,L:"*";
390     ELSE
400       LET FUGGV=0
410     END IF
420   ELSE
430     IF SZ<2 OR SZ>3 THEN
440       LET FUGGV=0
450       PRINT AT K,L:" ";
460     ELSE
470       LET FUGGV=1
480     END IF
490   END IF
500 END DEF
510 DEF LEP
520   NUMERIC I,J
530   FOR I=1+1 TO SORSZ-1
540     FOR J=1+1 TO OSZLSZ-1
550       IF Q>0 THEN
560         LET F(I,J)=FUGGV(SZOMSZ(I,J),E(I,J),I,J)
570       ELSE
580         LET E(I,J)=FUGGV(SZOMSZ(I,J),F(I,J),I,J)
590       END IF
600     NEXT J
610   NEXT I
620 END DEF
630 TEXT 40
640 CALL KEZD
650 DO
660   CALL LEP
670   LET Q=-Q
680 LOOP

Ne indítsuk el! Borzalmasan lassú lesz. Az előkészítés mintegy fél percig megy - ezt még kibírnánk - viszont egy-egy generáció generálása (sic) két és fél percig tart, ami tarthatatlan (sic). Az egyetlen gyors megoldás, ha a programot azonmód lefordítjuk a ZZZZZZZZZZZZIP-el (itt egy kicsit megszaladtak a Z betűk, elég lesz belőlük három is). (Ez megmagyarázza a lokális változók suta megválasztását: a ZZZIP nem fogad el azonos nevű változókat. A ZZZIP által ránk erőszakolt megkötések nélkül egyébként még egyszerűbben és szebben meg lehetett volna csinálni a programot.)
Így már elfogadható a sebesség, de még nem mondtuk ki az utolsó szót. Akinek kedve van, megpróbálhatja gyorsítani azt. Természetesen nem bitfaragásra gondolunk, hanem az algoritmus javítására.
Egy optimalizálást már "elkövettünk" menet közben: nem hagytuk magunkat rábeszélni a teljes képmező újrarajzolására. Enélkül a program csak elméleti érdekességgel bírna, használni nem lehetne. Ezzel étjük el egyébként a legnagyobb javulást a sebességben, és nemcsak BASIC-ben, hanem más, gyorsabb programnyelveken is.
Egy további lehetőség a SZOMSZÉDOK függvény optimalizálása: a megoldás jelenleg nem vesz tudomást arról, hogy a kiszámolandó összeg egy része az előző cellánál kiszámolt összegben már "benne van". Kérdés, hogy ezzel lehet-e időt megtakarítani.
Egy másik (nagyon hatásos) beavatkozás, ha nem "faltól-falig" megyünk, hiszen az ábra általában csak a sejttérnek egy kis részét foglalja el. Itt azt lehet kihasználni, hogy az ábra alsó és felső, illetve bal és jobb szélső eleme minden lépésben legfeljebb egy mezőnyivel terjeszkedhet. Elegendő tehát egy minden lépésben újra meghatározandó korlátpár-pár között vizsgálni a táblázatot.
Természetesen csak akkor jutunk használható programhoz, ha megoldjuk a kiinduló konfiguráció berajzolását. Nem árt figyelni, hogy a sejthalmaz nem terjed-e túl a rendelkezésre álló területen. (Ilyenkor továbbmenve hamis eredményt kapunk.) Ha pedig az optimalizálást túlzásba vittük, nem árt a lépésenkénti végrehajtásra felkészülni, hogy egyáltalán lássunk is valamit. (Bár, lehet, hogy ez még odébb van...)

Ha eluntuk az Életjátékot, a SZOMSZÉDOK és a FÜGGVÉNY függvény átírásával egyéb sejtszimulációt is előállíthatunk. Nagyon érdekes pl. az az eset, amikor csak az élszomszédokat vesszük figyelembe, és a szám paritását vizsgáljuk (páros számú szomszéd esetén lesz sejt a cellában, páratlan számúnál nem lesz). Ilyenkor a sejtalakzat néhány lépésben megnégyszerezi önmagát. ("Kész az önreprodukáló sejtautomata!" - kiáltunk fel, de korai az öröm, a szakemberek szerint ez nem az!).
"A mű kész, az alkotó pihen" - mondta egy fehér szakállú programozó, amikor egy kicsit más jellegű életjátéka végre úgy-ahogy elindult (a program azóta sem hibátlan, pedig alkotója már minimum a 6.2 verziónál tart vele). Csak azt akartuk megmutatni, hogy átgondolt, korszerű programtervezési módszerrel nem kell hat nap egy ekkora program létrehozásához. Körülbelül egy órai munkával (az eszközök: papír, ceruza, fej) és tízegynéhány percnyi begépeléssel azonnal működő, toldozást-foldozást nem igénylő program készíthető.

Ujlaki László

Karakterek tervezése

Az IS-Basic SET CHARACTER parancsával a gép karakterképeit könnyedén megváltoztathatjuk. A kezdők számán azonban bonyolultnak tűnik a szükséges jellemzők meghatározása, a karakterek tervezése. Nekik szántuk az alábbi írást.
Mint a számítógépben minden, így a karakterképek szerkesztése is a kettes számrendszerhez kapcsolódik. Az egyes karakterek 8 bit szélesek, és 9 bit magasak, a teljes karaktert tehát 72 bit íja le. Tudjuk, hogy a bit tulajdonképpen egy kétállapotú kapcsoló, amely ha be van kapcsolva, akkor értéke egy, egyébként pedig nulla. Ha a kapcsolóállapotot levetítjük a karakterekre, akkor azt mondhatjuk, hogy a bekapcsolt bit látszik, a kikapcsolt nem.

A karakter - tervezés szempontjából - 8 egység széles, 9 egység magas mátrixként fogható fel. A karaktert ebben a mátrixban kell megrajzolni (célszerű kockás papírt használni). Nem véletlen, hogy 8 egységből, azaz 8 bitből áll össze a karakter egy-egy sora: ezt bájtként lehet értelmezi, a gép ekként is kezeli A bájtban tehát 8 bit foglal helyet, minden bitnek helyértéke van.
A karakter egyes sorai bájtokat takarnak, 9 ilyen bájt van egymásra helyezve a karakterben. A bájt jobb szélső bitje a 0., a bal szélső a 7. sorszámot viseli. Mivel a bit két állapotot vehet fel, így a 0. bit helyértéke 2^0=1, az 1. bité 2^1=2, a 2. bité 2^2=4 és ígytovább egészen 2^7=128-ig, ezek az értékek láthatók az ábra alján.
A sorokat alkotó bájtok értékeit egyszerűen úgy kell meghatározni, hogy összeadjuk az azon bitekhez tartozó helyértékeket, ahol a bit értéke egy. A melléklet ábra legfelső sorának értéke így 16+8=24, hiszen a negyedik és a harmadik bit van egybe állítva. Ezt az eljárást követve mind a kilenc sornál elvégezzük a számítást.
Ha elkészültünk ezzel, akkor a kapott számokat közölnünk kell a géppel, ezt a SET CHARACTER paranccsal tehetjük meg. A parancs első paramétere az az ASCII kód, amelyhez tartozó karakter képét át kívánjuk definiálni. Az ezt követő kilenc paraméter a karaktert alkotó kilenc értéket jelenti, elsőnek a karakter tetején állót kell megadni. Az utasítás formája tehát

SET CHARACTER ascii_kód,sor1,sor2,sor3,sor4,sor4,spr6,sor7,sor8,sor9

Az ábrázott karaktert tegyük az "A" betű helyére, ennek ASCII kódja a 65.

SET CHARACTER 65,24,36,66,126,66,66,0,0,0

Bizonyára sokan furcsálják, hogy miért nekünk kell ezeket az értékeket kiszámítani, miért nem bízzuk ezt a gépre? Nekik teljesen igazuk van, hiszen: "Dolgozzanak a gépek, gondolkozzanak az emberek". A BASIC rendelkezik a BIN() függvénnyel, amellyel az egyes sorok értékei kiszámíthatók. A függvény felhasználására láthatunk példát a következő programlistán:

100 PROGRAM "CharDef.bas"
110 NUMERIC C(1 TO 9)
120 LET C(1)=BIN(0011000)
130 LET C(2)=BIN(0100100)
140 LET C(3)=BIN(1000010)
150 LET C(4)=BIN(1000010)
160 LET C(5)=BIN(1111110)
170 LET C(6)=BIN(1000010)
180 LET C(7)=BIN(0)
190 LET C(8)=BIN(0)
200 LET C(9)=BIN(0)
210 SET CHARACTER ORD("A"),C(1),C(2),C(3),C(4),C(5),C(6),C(7),C(8),C(9)

Az Enterprise-Plus BASIC-bővítés még egyszerűbbé teszi a karakterek átdefiniálását. Erre két utasítás szolgál: Az CHRDEF egy adott karakter definiálásának kezdetét jelzi. Ezután kilenc, nem feltétlenül egymást követő CHRLIN utasítás kell, hogy kövesse. A CHRLIN utasítás a karakter egy-egy sorát adja meg: ebben a szóköz hatására a megfelelő bit törlődik, "." (pont) hatására megmarad az eredeti karakter pontja, minden más karakter pedig egybe állítja a bitet.

100 PROGRAM "CharDef.bas"
120 CHRDEF "A"
130 CHRLIN "   xx   "
140 CHRLIN "  x  x  "
150 CHRLIN " x    x "
160 CHRLIN " xxxxxx "
170 CHRLIN " x    x "
180 CHRLIN " x    x "
190 CHRLIN "        "
200 CHRLIN "        "
210 CHRLIN "        "

BASIC programok láncolása
Az Enterprise memóriájában több programot is tárolhatunk egyidőben. Az egyes programokat 0 és 127 közé eső sorszámmal különböztetjük meg egymástól. A nullás számú program számára legfeljebb 42K, minden további számára legfeljebb 32K helyet biztosít a BASIC rendszer. Az EDIT <sorszám>, vagy EDIT<név> paranccsal határozzuk meg, hogy éppen melyik programot akarjuk szerkeszteni. Ennek számát a gép a képernyő felső részén ki is írja. Az éppen szerkesztett programot a sima SAVE és LOAD parancsokkal tudjuk elmenteni és betölteni, illetve a NEW-val törölni. (Ha valaki nem tudná: ha a program elején a PROGRAM "név" utasítást használjuk, akkor a SAVE után nem kell kiírni a nevet, mert tudja.) Ha az összes programot csoportosan akarjuk kezelni: a SAVE ALL parancs az összes programot az első neve alatt tárolja, a LOAD parancs az így tárolt programokat egyben visszatölti, a NEW ALL parancs pedig az összes, memóriában lévő programot törli.
Lehetőség van rá, hogy az egyes programok egymást indítsák, így láncot tudunk létrehozni. Az utasítás formája:

CHAIN <sorszám> (paraméterek)

vagy:

CHAIN <név> (paraméterek)

Paramétereket csak akkor kell adni, ha a másik programnak adatokat akarunk átadni.
A láncolásra a legegyszerűbb példa:

0. PROGRAM:
10 CHAIN 1

1. PROGRAM:
10 PRINT "Hello!"

Ha a 0. programot elindítjuk, azonnal meghívja az egyes számút, amely végrehajtja a PRINT utasítást. Amikor a futás befejeződik, az egyes program területén maradunk (lásd a számot a képernyő tetején).
Az alábbi példában a paraméterátadást figyeljük meg:

0. PROGRAM:
10 PROGRAM "nulla"(a,b,c)
20 PRINT "Nulladik progi"
30 PRINT a,b,c
40 CHAIN 1 (1,2,3)

1. PROGRAM:
10 PROGRAM "egy"(a,b,c)
20 PRINT "Egyes progi"
30 PRINT a,b,c
40 CHAIN 0

Mivel a két program kölcsönösen indítja egymást, végtelen ciklust kapunk, és a képernyőn, amíg a STOP billentyűvel le nem állítjuk, az alábbi feliratok fognak futni:
Nulladik progi
0 0 0
Egyes progi
1 2 3
Magyarázat: az egyes programot a=1, b=2 és c=3 paraméterekkel hívtuk meg, az ő számára tehát a, b és c értéke ennyi. A nulladik programot paraméterek nélkül hívtuk meg, ezért az ő számára az a, b és c változók értéke nulla. Ha most az egyes program 40. sorába paramétereket is írunk, pl:

40 CHAIN 0 (4,5,6)

akkor ezt kapjuk:
Nulladik progi
0 0 0
Egyes progi
1 2 3
Nulladik progi
4 5 6
Egyes progi
1 2 3
Nulladik progi
4 5 6
Egyes progi
1 2 3
és így tovább.
Az első indításkor a nulladik program paramétereinek még nincs értéke, amikor azonban az egyes program hívja meg, már van.
A CHAIN hatására a meghívott program mindig az elejétől indul! Ezért sajnos "szubrutinként" nem használhatunk egy másik programot, mert annak végrehajtása után, ha visszaadjuk a vezérlést a főprogramnak, az újra elölről fog indulni, és nem a CHAIN utasítás utáni sortól. Ha azonban a nulladik program pl. csak egy menüt ír ki, akkor már lehetőségünk van a tárban lévő programok közül aszerint meghívni egyiket vagy másikat, hogy a kezelő melyik menüpontot választotta. (Kiváló példa erre a Reversi, Dáma, Awari című játékprogram.)
Ha a hívott programot nem az elejétől szeretnénk indítani, megtehetjük, hogy paraméterként adjuk át annak a sornak a számát, vagy eljárásnak a nevét, ahonnan a meghívott programot futtatni kell. Például:

0. PROGRAM:
10 CHAIN 1("puff')

1. PROGRAM:
10 PROGRAM "egyes"(A$)
20 SELECT CASE A$
30 CASE "piff"
40   CALL PIFF
50 CASE "paff"
60   CALL PAFF
70 CASE "puff"
80   CALL PUFF
90 END SELEC
T

Nevet azért jobb paraméterként átadni, mint sorszámot, mert ha az egyes program szerkezetén változtatunk, és a sorszámok megváltoznak, arról a nullásnak nem kell tudnia. Megjegyzendő, hogy az egyes program indulásakor a változók mindenképpen törlődnek (ahogy egy normál RUN, vagy START után is), egy esetleges előző futásból tehát semmi nem őrződik meg.

Szalontai Andrea - Enterpress 1994/2.

Táblázatos Adatbevitel BASIC-ben

A mellékelt program táblázatos adatbevitelt valósít meg BASIC nyelven, struktúráit stílusban. A beolvasó eljárás (a kiíró blokkal együtt) önállóan is használható. Paraméterei: a beolvasás koordinátái a képernyőn, a beolvasott sztring maximális megengedett hossza és kezdeti értéke. Az eljárás lehetővé teszi a kurzor mozgatását, beszúrást a kurzor pozíciójában, és törlést mindkét irányba, miközben nem engedi meg a felhasználónak, hogy vezérlőkarakterekkel kilépjen a sorból, vagy a megengedettnél hosszabb sztringet vigyen be, amivel elrontaná a képernyőt. Nem megengedett billentyű leütésekor hangjelzést ad. Az eljárás egyszerűen a karakterláncok szétvágásával és újra összeragasztásával dolgozik. Mivel egy karakterhelyre nem lehet egyszerre kiírni egy betűt és a kurzort is, a szövegen belül mozgó kurzor a szöveget kettévágja. Kurzorkarakternek választhatunk más jelet is, pl. egy aláhúzást, ami nem annyira zavaró. Akárhol nyomjuk le az ENTER-t, az eljárás a teljes sztringet beolvassa, miután először kiszedi belőle a kurzorkaraktert. A kiíró eljárás mindig felülírja a szöveget, ezért a gépet ne felejtsük beszúrás üzemmódban, mert elrontja a képet! A T$ változó célja a szöveg törlése az újraírás előtt, ennek hossza szükség szerint változtatható.
A főprogram ennek az eljárásnak a segítségével két szöveg- és egy számtáblázat adatit olvassa be. Mivel a táblázatok hosszúak, egy képernyőoldalra nem férnek ki, ezért lapozni kell őket. A program fő változói:

A képernyőt a KEPERNYO eljárás rajzolja fel. A 106. sorban az elemek sorszámának kiírása természetesen elhagyható, ha nincs elég helyünk. A főcímben úgyis benne van. Figyeljük meg a 105. sorban található feltételt, amely arra szolgál, hogy az utolsó oldalon, amely nincs teljesen tele, magakadályozza, hogy kimenjünk a táblázatból (UBOUND változó!). Hasonló feltétel szerepel a kurzorvezérlő eljárásban is, a lefelé léptetésnél (158. sor).
Amikor megválasztjuk, hogy a táblázatokból hány sort teszünk egy oldalra, hagyjunk helyet az alábbiaknak: felül a főcím, plusz egy sorkihagyás; itt ugyan nem szerepel, de ha az egyes oszlopoknak saját fejlécet akarunk kiírni, akkor az plusz egy sor és egy kihagyás; valamint alul két státuszsor és fölötte egy sorkihagyás. Marad tehát a táblázatra maximum 17 sorunk.
A programnak van egy érdekessége. Mivel a képernyő kiírása lassú, a felhasználónak lehetőséget kell adni arra, hogy még mielőtt egy lap teljesen kiíródik, továbbmenjen a következőre. A képernyőkiíró eljárás minden elemsor után megvizsgálja, hogy a kiírást megkísérelték-e megszakítani. Ha volt billentyűlenyomás, megnézi, hogy mi volt az? A kiírást megszakítani ugyanis csak a lefelé és felfelé lapozó billentyűkkel engedi, és azokkal is csak akkor, ha ez nem okozna hibát (lásd a feltételeket a 113-114. sorokban). Ha ezt nem vizsgálnánk itt meg, a főprogram kerülne végtelen hangjelzést adó hibaciklusba, mivel az újbóli lapozást a főprogrammal végeztetjük el! (Lásd 133-137. sorok, ha a képernyőkiíró ciklus a HIBA változóval azt üzente, hogy már van parancsbillentyű lenyomva, akkor a főprogram nem áll neki újra billentyűre várni, hanem azt vizsgálja meg, ami már benne van az x$ változóban, és mivel az egy lapozó parancs, az OFFSET beállítása után újra meghívja a képernyőkiíró eljárást.) Figyeljük meg azt is, hogy a biztonság kedvéért először csak a FOR ciklusból ugrunk ki, és csak utána a DEF blokkból. (Az ember sose tudhatja, mitől vadul meg a gépe, nem árt óvatosnak lenni. Így legalább a kurzor-koordinátáknak is mindig van értelmes értéke.)
A főprogram a 187-200. sorokban határozza meg, hogy a beolvasó eljárásnak melyik táblázat hányadik elemét kell átadnia. Itt is figyelemre méltó a KEZD, OFFSET és SORSZAM változók használata, valamint az, hogy a beolvasandó elemet referencia-paraméterként adjuk át (lásd a REF kulcsszót a beolvasó eljárás fejlécében). Ez azt jelenti, hogy az eljárás megváltoztathatja az elem tartalmát.
A program végén a teljesség kedvéért bemutatom, hogyan lehet a kész táblázat adatait lemezre, vagy magnóra menteni. A 214. sorban az adatok törlése csak arra való, hogy meggyőződhessünk róla: a program valóban a lemezről olvasta vissza az adatokat és nem a memóriában maradtakat írja ki újra.
A program természetesen szabadon átsorszámozható, saját programjainkba beilleszthető (MERGE), és módosítható is. A főprogram inicializáló részében deklarált változóknak globálisaknak kell lenniük!

Beolvaso

Szalontai Andrea - Enterpress 1995/3-4.

Microsoft BASIC programok futtatása Ep-n

Mostani cikkünk tárgya a Golden Baton ismertetőjének egyik félmondata, nevezetesen: "A korabeli BASIC nyelvű játékok.". Először is azt érdemes körüljárni, mit értünk "korabelinek"? Az első otthoni felhasználásra szánt (megfizethető) számítógépek 1977-ben jelentek meg pár hónap eltéréssel: az Apple II (június), TRS-80 Model I (augusztus), és a Commodore PET (október). Ezek közül a TRS-80 számítógépek futották be a legnagyobb "karriert", elsősorban 600 dolláros kezdőárának köszönhetően (szemben az Apple II 1298 dolláros árával). Az előbbi árak természetesen csak a 4 KB-os kiszerelésre értendőek (monitorral), később a 16K-s változatok terjedtek el. A három gép paraméterei teljesen eltérőek voltak, eltérő processzorok dolgoztak bennük, egy valamiben azonban nagyon is hasonlítottak: mindháromban (a TRS-80 gépcsaládban 1978-tól) a Microsoft BASIC valamely változata működött, akárcsak később - igaz csonkítva(!) - a VIC20-ban és C64-ben is, illetve még később - bővítve - Plus-4-en. A TRS-80 Model II számítógépet közvetve mi magyarok is jól ismerhetjük: a magyar gyártású HT-1080Z iskolaszámítógép ennek - kicsit megkésett (1983-as (!)) - klónja.
Mindhárom gép erősen korlátozott grafikai képességekkel bírt, így pont alkalmasak voltak a korábban nagyszámítógépeken készült BASIC-játékok futtatására. Emellett kézenfekvőnek tűnt - Az Adventure sikerén felbuzdulva - szöveges kalandjátékokkal enyhíteni a játszani vágyó amatőrök programéhségét. Amellett, hogy sok szöveges BASIC játék került kereskedelmi forgalomba (mai szemmel nézve megdöbbentő árakon) újságok, könyvek is közöltek - emberfeletti munkával - begépelhető játékokat. A leggrandiózusabb ilyen jellegű könyv talán az 1985-ben a Virgin Books kiadásában megjelent Castles & Kingdoms című könyv, amely 15 begépelhető szöveges kalandjátékot tartalmaz C64-re. Talán nem nehéz belátni, hogy a 15 játék megoldásánál lényegesen nagyobb kihívást jelenthetett azok (hibamentes) begépelése: a közismerten olvashatatlan Microsoft BASIC programlisták - a magyarázatokkal és "körítéssel" együtt 178 oldalt töltöttek meg. (Valószínűleg olcsóbb lett volna a programokat lemezen kiadni, és a vevő is jobban járt volna.). Emellett több könyv is megjelent kimondottan szöveges kalandjátékok írásának mikéntjéről. Ebből mi magyarok is kaphattunk ízelítőt - és többen kedvet -, amikor Frank DaCosta, 1982-es Writing BASIC Adventure program for the TRS-80 című könyve 1986-ban(!) magyar fordításban is megjelent A kalandprogram írásának rejtelmei címmel. Mint az az eredeti címből is látszik a könyv TRS-80-hoz készült, de tökéletesen használható volt HT-1080Z-hez is. Ennek első példaprogramja volt az idehaza nagy karriert befutott Kardhalak és Kincsek program, melynek több, a hazai piacon fellelhető gépre készült, többé - de inkább kevésbé működő maszek verziója (Ep-re már van működő változat!). A könyv magyarországi megjelenése egybeesik hazánkban a szöveges kalandjátékok írásának afféle kisebbfajta "népmozgalommá" válásának kezdeteivel, amiből sajnos a Spectrum kimaradt.
Nem úgy a második iskolaszámítógép, azaz a Plus-4! Tihor Miklós első kalandjátékai a Sárkányölő, a Gengszter még C64-re készültek, ez utóbbi a Commodore Egyesületi lap 1986/4. számában jelent meg, begépelhető formában. A Hős lovag lett az első kereskedelmi forgalomba került magyar nyelvű kalandjáték: C16-ra jelent meg a Novotrade kiadásában (250 Forintért). A játék egy 1980-ban megjelent, eredetileg TRS-80 játék, a Dragonquest alapján készülhetett. (A Castles & Kingdoms című könyvben jelent meg C64 változat belőle, megerősítendő, hogy a C64-es szöveges BASIC kalandjátékok jelentős része még a nagy elődökön készült.) Ezen korai kalandjátékok általános jellemzője - a szűkös (jellemzően 16K, de vannak alapkiépítésű VIC20-ra készült 3,5K-s kalandjátékok is.) memóriakapacitásra tekintettel, hogy nem túl bőbeszédűek, mondhatni "távirati stílusban" kommunikálnak a játékossal. Mindazonáltal lenyűgöző, milyen ötletes játékokat tudtak írni alig 16K-ban, egészen "elvetemült" programozási stílusban... Az eleve C64-re készült kalandjátékok már valamivel közlékenyebbek, pl. Olessák Róbert első - még BASIC-ben készült - kalandjátéka, a Brekk! - Avagy a békává varázsolt királylány.

Ezzel a bevezetővel el is értünk a mostani írás témájához: a Microsoft BASIC változatainak és az IS-BASIC összehasonlítása, hogyan is lehetne az említett játékokat futtatni Ep-n? Azt a felhasználói kézikönyvből tudjuk, hogy "Az Enterprise legtöbbször az ANSI X3J2/82-17 Draft Proposal for Standard BASIC-nak megfelelően jár el." (akármit is jelentsen ez), a futtatni kívánt szöveges kalandjátékok lehetőségei pedig alig mutatnak túl a Minimal BASIC-en. Vegyük sorra az eltéréseket, mit kell módosítani, ha már pl. CBM prg Studio programmal szöveges állományba tudtuk menteni az eredeti programlistát!

Szintaxis
A Microsoft BASIC "leglátványosabb" tulajdonsága, hogy az egyes nyelvi elemek között álló szóköz elválasztó karakter(ek) elhagyható(k). A legtöbb programban el is hagyták a byte-spórolás jegyében (ez a program olvashatóságát persze nagymértékben rontja).
C64-en a legutolsó programsor sorszáma max. 63999 lehet, Ep-n 9999. C64-en RENUMBER nincs, a CBM prg Studio-ban viszont van.

Egy programsor IS-BASIC-ben 254 karakter hosszú lehet, C64-en 80 karakter. Ez több helyen a program egyszerűsítését teszi lehetővé. C64-en ha a THEN után több utasítás áll(na), ami már nem fér bele a 80 karakterbe, kényszerűségből egymás után ugyanazt a feltételt vizsgálva végzik el a műveleteket (vagy GOTO-t használnak), a 80 karakternél többet tároló szöveges változók is csak több sorban kaphatnak értéket (összefűzéssel), ezek mind összevonhatók, akárcsak a rövidebb DATA sorok.

A közhiedelemmel ellentétben Ep-n is egy programsorban több utasítást is írhatunk, kivéve a blokkszervező utasításokat (FOR - NEXT, DO - LOOP, DEF - END DEF, WHEN - END WHEN, HANDLER - END HANDLER), valamint a NUMERIC, STRING, CONTINUE, IMAGE, ON GOTO, ON GOSUB, REM, END utasításokat, melyek minden esetben külön sorban írandók! Figyelni kell még a GOSUB használatára: ez minden esetben a sor utolsó utasítása legyen, mert az ezután lévő utasításokat már nem hajtja végre az interpreter!
Némi hajhullásra adhat még okot a Microsoft BASIC-ben "közkedvelt" IF ... THEN NEXT: A=A+1 típusú szerkezet, ahol a feltétel teljesülése esetén a NEXT hajtódik végre, egyéb esetben a kettőspont után álló további utasítás(ok).

Változók
A Microsoft BASIC-ben a változók neveinek első karaktere csak betű lehet (A-Z), ezt tetszőleges számú alfanumerikus karakter követheti (0-9, A-Z), de csak az első két karakter szignifikáns. A változónév nem tartalmazhat BASIC alapszót (pl. a VALI, WORD nem megengedett). A változó nevekkel tehát nem lesz sok gondunk IS-BASIC-ben bármi jó, ami Microsoft BASIC-ben jó, mindössze az egész típusú változókat jelölő % jelet kell a végéről kivenni.
Nagyobb gond, hogy míg Microsoft BASIC-ben megengedett, hogy egy numerikus változó és egy tömbváltozó ugyanazt a nevet viselje, az IS-BASIC-ben nem. Márpedig úgy tűnik a korabeli programozók ebből sportot csináltak, rendszeresen találkozhatunk READ O(O) típusú - amúgy nagyon csúnya - értékadásokkal. Itt értelemszerűen az egyik változót át kell nevezni.
Anno, akik más gépeken kezdték az ismerkedést a BASIC nyelvvel, rendszeresen szidták az IS-BASIC-et, amiért a változókat - nagyon helyesen - első használat előtt deklarálni kell. Microsoft BASIC-ben nem kell, egy újonnan bevezetett változó automatikusan 0 értéket vagy üres stringet vesz fel. Sőt, a maximum 10 elemű tömbök deklarálása is elhagyható! Ezt ki is használták a programkészítők, a 0 kezdőértékkel rendelkező változók - a memóriaspórolás okán - következetesen nincsenek deklarálva. Talán meglepő, de a Microsoft BASIC ezen "szolgáltatása" felderíthetetlen és így igen gyakori hibát okoz a játékokban. A C64-es kalandjátékok legalább fele(!) tartalmaz a játékmenetet befolyásoló hibát. Ha a program egy esemény megtörténtét pl. O(30)=1000 feltétellel vizsgálná, de helyette O(30)=l000 került a programba, az adott esemény sosem fog bekövetkezni (mert az L000 változó értéke mindig 0 lesz). Ez néhány esetben nem a megoldás egyszerűsödését okozza, hanem épp ellenkezőleg, a játék teljesíthetetlen lesz. Hogy az ilyen hiba mennyire felderíthetetlen, jól példázza, hogy 'A Hős lovag' úgy került kereskedelmi forgalomba, hogy megoldhatatlan. Az ilyen hibákat az IS-BASIC szerencsére jelzi.
Kevesen tudják talán, hogy IS-BASIC-ben az értékadások összevonhatók, egyetlen LET utasítással egyszerre több változónak is adhatunk értéket: LET, A,B,C,D=0.

Szövegfüzérek
Microsoft BASIC-ben a stringek végét jelző idézőjel elhagyható, IS-BASIC-ben nem.
Microsoft BASIC-ben egy szöveges változó maximum 255 karaktert tárolhat. Ep-n 254-et, ha azt a STRING utasítással deklaráljuk, egyébként max. 132 karaktert. Ez nem szokott gondot jelenteni, mert ilyen hosszú sort Microsoft BASIC-ben nem lehet beírni. Szöveges tömbváltozóknál gond lehet viszont a mérettel, mert a Microsoft BASIC másként tárolja a szövegfüzér típusú változókat: a változóterületen csak a string hosszát és egy mutatót tárol. A szövegfüzért a program után a string-területen (a memória végétől lefele terjeszkedve) tárolja úgy, hogy a szövegfüzér-változó helyfoglalása ténylegesen a tárolt karakterek számától függ. Ez a módszer egyrészt nagyon gazdaságosan bánik a memóriával, ha Ep-n ugyanúgy DIM utasítással deklarálnánk egy nagyobb méretű tömböt (mert sok helyszín van a játékban), betelt a memória hibaüzenet kapunk. A STRING utasítást kell használnunk, megadva a maximális hosszt. Másrészt - könnyű belátni - hogy a Microsoft-féle megoldás elképesztően lassítja a szövegfüzér műveleteket: egy 168 szót tároló tömbben a lineáris keresés 14 másodperc C64-en, Ep-n 2 másodperc.

Logikai kifejezések
Az AND művelet természetesen számokra is értelmezhető, de ilyen esetben a Microsoft BASIC-ben a művelet mindig bitenként hajtódik végre. Vagyis a

IF (A AND 47)<>N THEN ...

kifejezés javítandó:

IF (A BAND 47)<>N THEN ...

A szövegfüzéreken értelmezhető műveletek
C64-en a konkatenáció (összekapcsolás) jele a "+", Ep-n "&".
Az általunk ORD néven ismert függvényt (a megadott string-kifejezés első karakterének ASCII kódját adja) a Microsoft BASIC-ben ASC-nek hívják.
A részstring-képzés Ep-n túl logikus és egyszerű, a Microsoft ehelyett rögtön 3 függvényt is bevezetett ugyanerre:

LEFT$(<string>,<aritmetikai kif.>)

A string-ből az első <kif.> számú karaktert adja.

RIGHT$(<string>,<aritmetikai kif.>)

A string végéről <kif.> számú karaktert ad.

MID$(<string>,<kif1>, <kif2>)

A string <kif1> karakterétől <kif2> számú karaktert ad. <kif2> elhagyható, ebben az esetben a string végéig másolja a karaktereket. Úgy tűnik elég nagy zavar lehetett a fejekben e három függvénnyel kapcsolatban, senki ne csodálkozzon, ha pl. MID$(x$,LEN(x$)-N+1) típusú produkciót lát, ez magyarra lefordítva RIGHT(X$,N).

DATA sorok
Microsoft BASIC-ben a DATA sorokban álló szövegfüzéreket csak akkor kell idézőjelbe tenni, ha vesszőt tartalmaznak. Ezzel szemben IS-BASIC-ben akkor is, ha felkiáltójelet is tartalmaz (mert a '!' a megjegyzés jele is).
Microsoft BASIC-ben bájt spórolás célzattal a DATA sorok adatlistáiban a 0 értékeket nem kell kiírni. Vagyis a

DATA 0,1,2,0,0,0,3,4,5,0

és a

DATA ,1,2,,,,3,4,5,

ugyanazt az adatlistát jelenti. IS-BASIC-ben az adatlista utolsó elemét viszont mindenképen ki kell írni, vagyis:

DATA ,1,2,,,,3,4,5,0

Függvények
A C64 BASIC-ben is van POS függvény, de teljesen mást jelent, mint az IS-BASIC-ben: kiszámítja a kurzor helyzetét az aktuális képernyő sorban. A lehetséges értéke 0-255 intervallumba eshet. Ez nem a képernyő 40 karakteres sorában lévő pozíció, hanem annak mértéke, amennyivel a kurzort a sor elejétől elmozgattuk (pl. egymás utáni sortörés nélküli PRINT utasításokkal). A függvény argumentuma lényegtelen. Ez az egyik leghasznavehetetlenebb függvény, bár szöveges kalandjátékokban szokták használni egyfajta nyakatekert szóátvitel megvalósítására. Az EXOS-ban ez alap, így ezen programrészletek egy az egyben törölhetők.

Véletlenszám generálására az RND(<aritmetikai kif.>) függvény szolgál, azaz paramétert kötelező megadni. Az aritmetikai kifejezés előjele határozza meg, milyen eljárás segítségével állítja elő az interpreter a következő számot. Amennyiben az argumentum pozitív, az interpreter "számítja" ki a következő számot, ha az argumentum nulla, a CIA chip óra-regisztereit használja. Ha az érték negatív, a függvény konstans értéket ad. Lényeg a lényeg: a paraméter nélküli RND függvényt kell használnunk, és természetes a program elején a RANDOMIZE utasítást.

Az SPC(<aritmetikai kif.>) adott számú szóközt ír ki. Ha szükség van rá, a TAB függvénnyel, vagy - favágó módon - space karakterek kiíratásával helyettesíthető.

A TI és TI$ a C64 belső órájának segítségével a gép bekapcsolása óta eltelt időt mérik. A TI valós változó, ami azonban csak olvasható. A gép "szabadon futó órája" másodpercenként körülbelül ötvenszer aktualizálódik, és a TI/60 a gép bekapcsolása óta eltelt másodperceket mutatja. A TI$ egy hat karakteres szövegfüzért tárol, amelynek két-két karaktere rendre az órát, a percet és a másodpercet adja. TI$ kezdőértéke értékadó utasítással megadható (pl. TI$="081500"). Szöveges kalandjátékok a legritkább esetben használják időmérésre. Ha mégis szükséges, kivételes esetek kezelésével (HANDLER) helyettesíthető.

File-műveletek
File műveletek végzésére az OPEN, CLOSE, INPUT#, GET# és a PRINT# utasítások szolgálnak. IS-BASIC-ben ugyanazon elvek mentén kezelhetjük értelemszerűen a file-okat, apróbb szintaktikai eltérésekkel. Például az alábbi mentést (4600. sortól) és betöltést (4700. sortól) végző rutin:

4600 V$=CHR$(13):OPEN1,1,1,"m":PRINT#1,I;V$Q;V$N;V$M;V$Y;V$B;V$W;V$D;V$T;V$C;V$G;V$A
4610 FORJ=1TOO:PRINT#1,O$(J)V$P(J):NEXT:FORJ=1TOL:PRINT#1,D$(J):NEXT:CLOSE1:GOTO820
4700 OPEN1:INPUT#1,I,Q,N,M,Y,B,W,D,T,C,G,A
4710 FORJ=1TOO:INPUT#1,O$(J),P(J):NEXT:FORJ=1TOL:INPUT#1,D$(J):NEXT:CLOSE1:GOTO700

így fog kinézni:

4600 LET V$=CHR$(13)
4602 OPEN #1:"m.dat" ACCESS OUTPUT
4604 PRINT #1:I;V$;Q;V$;N;V$;M;V$;Y;V$;B;V$;W;V$;D;V$;T;V$;C;V$;G;V$;A
4610 FOR J=1 TO O
4620   PRINT #1:O$(J);V$;P(J)
4630 NEXT
4640 FOR J=1 TO L
4650   PRINT #1:D$(J)
4660 NEXT
4670 CLOSE #1
4680 GOTO 820
4700 OPEN #1:"m.dat"
4705 INPUT #1:I,Q,N,M,Y,B,W,D,T,C,G,A
4710 FOR J=1 TO O
4720   INPUT #1:O$(J),P(J)
4730 NEXT
4740 FOR J=1 TO L
4750   INPUT #1:D$(J)
4760 NEXT
4770 CLOSE #1
4780 GOTO 700

A Plus4-es játékok módosítása sem bonyolultabb (grafikát tartalmazó játékokkal most nem foglalkozunk), egyetlen említésre méltó eltérés az IF-THEN szerkezetekben használható ELSE-ág. Ez annyira megtetszett az amatőr programozóknak, hogy több programban egyágú szelekciókat is "sikerült" megoldani IF-THEN-ELSE használatával.

Enterpress 2018/4-5.

A dBASE II adatbáziskezelő rendszer ismertetése

I. Alapelemek
A dBASE II (ejtsd: dibéz) - amelynek Enterprise-on a kettes változata fut, PC-n már a négyes is létezik - egy átgondoltan kidolgozott, saját programozási nyelvvel rendelkező adatbáziskezelő rendszer. Lényege, hogy ellátja helyettünk azokat a rendszerközeli feladatokat, amelyek az adatbáziskezelés alapját képezik, és amelyeket programozási szempontból a legnehezebb megoldani. (Nevezetesen: az adatok lemezre írása és karbantartása, a rekordok fizikai szerkezetének kidolgozása, indexelés, keresés.) Olyan magasszintű utasításokat adhatunk ki, mint például: lépj három rekordot előre, keresd meg az első "B"-vel kezdődő nevet, rendezd az adatbázist a "családnév" mező szerint, stb. és nem kell foglalkoznunk azzal, hogy mindezt a rendszer milyen módszerrel csinálja meg. Ha már létrehoztuk az adatbázis szerkezetét, jelentést (REPORT) is képes generálni a program, valamint az adatok kezelésére saját programot is írhatunk, amit aztán bármely felhasználó a rendszer utasításainak ismerete nélkül is kezelni tud. Éppen a programozhatóságban rejlenek a rendszer igazi lehetőségei.
A program csak lemezről működik, és csak a CP/M operációs rendszer alatt. A programhoz egy korrektül, de sajnos német vagy angol nyelven megírt, 24 oldalas help file tartozik. Ez 62 utasítást, 29 állítható paramétert és 17 beépített függvényt tartalmaz.

A rendszer fő maximális paraméterei:

egyszerre nyitott adatállományok száma
16
rekordok száma egy adatállományban
65 535
mezők száma egy rekordban
32
karakterek száma egy rekordban
1 000
karakterek száma egy mezőben
254
memóriaváltozók száma
64
karakterek száma a parancssorban
254
függőben lévő GET-ek száma
64
REPORT fejléc hossza karakterben
254
REPORT-ban szereplő mezők száma
24
indexkulcs hossza karakterben
99
SUM és REPLACE utasítás műveleteinek száma
5
számítási pontosság, számjegyekben
10
számábrázolási tartomány, kb.
+/-1*10^-63 - +/-1.8*10^63

A végrehajtható programok hossza a leírás szerint "korlátlan". Nos, ez annyiban igaz, hogy a DO parancs segítségével több programrészt ágyazhatunk egymásba, így ha kifutnánk a rendszer szövegszerkesztőjének memóriájából, egy hosszú programot darabokban (modulokban) is tárolhatunk a lemezen. Ez az áttekinthetőség szempontjából is előnyös.
A billentyűzet kezelése sajnos eléggé nehézkes, a billentyű-táblázatot jó mindig kéznél tartani. (A mellékelt INSTALL programmal nem lehet megváltoztatni a beállítást, ez PC terminálok csatolására való.) A használható billentyű-kombinációk:

Valamennyi parancs esetében:

ctrl-X a kurzor a következő mezőre lép (ctrl-F is ugyanezt teszi)
ctrl-E a kurzor az előző mezőre lép (ctrl-A is ugyanezt teszi)
ctrl-D a kurzor a következő karakterre (jobbra) lép
ctrl-S a kurzor az előző karakterre (balra) lép
ctrl-G kurzor alatt karakter törlése (jobbratörlés)
DEL visszafelé lép, de nem töröl
ctrl-V felülírás/beszúrás üzemmód kapcsoló
ctrl-Q kilépés a változtatások tárolása nélkül
ctrl-W kilépés a változtatások lemezre írásával

EDIT parancs esetében:

ctrl-U törlésre jelölés be/ki
ctrl-C a rekordot lemezre írja és a következőre áll
ctrl-R a rekordot lemezre írja és az előzőre áll

Ezeket APPEND parancs esetében ne használjuk!

BROWSE parancs esetében:

ctrl-U törlésre jelölés be/ki
ctrl-B a képernyőablak jobbra mozgatása
ctrl-Z a képernyőablak balra mozgatása
ctrl-C a következő rekordra lép
ctrl-R az előző rekordra lép

MODIFY COMMAND parancs esetében:

ctrl-T sor törlése a szöveg felzárásával
ctrl-Y sor törlése helykihagyással
ENTER lefelé lép a sorokon
ctrl-C fél képernyőt lép lefelé
ctrl-R felfelé lép a sorokon
ctrl-N üres sor beszúrása a kurzor alatti sor helyére

APPEND parancs esetében:

ENTER amikor a kurzor üres rekord elején áll, adatbevitel befejezése

Parancssorban:

ctrl-R az utolsó parancs végrehajtása újra

A programot a CP/M (IS-DOS) betöltése után a dBASE paranccsal indíthatjuk, és ha végeztünk a munkával, a QUIT paranccsal szállhatunk ki belőle. A rendszer promptja egy pont. Ha nagyon összekutyulnánk a képernyőt, ERASE-zel törölhetjük. Ha elrontottuk egy parancs beírását, a rendszer UNKNOWN COMMAND, vagy SYNTAX ERROR üzenettel válaszol, és megkérdezi, hogy kívánjuk-e módosítani a parancsot. (Módosítani nem érdemes, mert körülményesebb, mint újraírni az egészet.)
Új adatbázist a CREATE paranccsal hozhatunk létre. Ha nem adunk meg utána nevet, akkor rákérdez. A kiterjesztést nem kell megadni, ez automatikusan DBF, azaz adatbázis lesz. Ezután egy fejlécet kapunk, és a megjelenő mezősorszámok mellett meg kell adnunk, hogy milyen mezőket tartalmazzon az adatbázisunk. Először írjuk be a mező nevét, majd a típusát. A mezők típusa lehet

A típus után következik a mező hossza karakterekben. Számmezőnél, ha törtszámot is várunk, a tizedespont utáni helyek számát is meg kell adni. A logikai mezők értéke igaz (true, t) vagy hamis (false, f) lehet, így ezek tárolására egy karakter is elég. A bevitt paramétereket vesszővel kell elválasztani.
ENTER-rel fejezzük be a rekordszerkezet kialakítását. (Egy kész szerkezet később szükség esetén módosítható, ennek módszerét később mutatom be.) Ekkor a program megkérdezi, hogy akarunk-e rögtön adatokat bevinni. Ha igen, akkor APPEND (hozzáfűzés) üzemmódba kerülünk, és kitölthetjük az üres képernyőmaszkot. Az utolsó mező kitöltése után a következő rekordra áll a rendszer, egészen addig, amíg ENTER-rel (vagy bármilyen ctrl-billentyűkombinációval, szabálytalanul) be nem fejezzük az adatbevitelt.

A kész adatbázist a USE paranccsal lehet megnyitni, azaz beolvasni a lemezről, és ugyanezzel lehet lezárni, vagyis lemezre tárolni is a munka befejezésekor. (A változtatások csak ekkor tárolódnak le teljesen, ezért hosszabb munka esetén érdemes többször lezárni és újra megnyitni az állományt.) Ha az adatbázist kezelő parancsot adunk ki olyankor, amikor üres a memória, a program rákérdez, hogy melyik adatbázist olvassa be a lemezről, és ezután ezen hajtja végre az utasítást.
A bevitt adatokat később megnézhetjük és módosíthatjuk A BROWSE parancs egymás alá írja ki a rekordokat, köztük a fent leírt billentyűkombinációkkal mozoghatunk. Módosítani is lehet. Ha törlésre jelölünk egy rekordot, akkor a képernyő felső sorában a DELETED felirat jelenik meg, mindig, amikor erre a rekordra lépünk. Ez egyelőre csak megjelölés, a végleges törlést a PACK parancs végzi el, amely az összes megjelölt rekordot eltávolítja az adatbázisból.
APPEND paranccsal az adatbázis végéhez fűzhetünk újabb rekordo(ka)t, amely(ek) képernyőmaszkját a szokásos módon tölthetjük ki. Az APPEND BLANK parancs egy üres rekordot fűz be, amelyet egyelőre nem töltünk ki. (Programozáskor hasznos, ha az adatokat a felhasználó írja be.) Az EDIT paranccsal egy adott sorszámú rekordot szerkeszthetünk. Ha nem adjuk meg a számát (pl. EDIT 3), akkor a program rákérdez. Ha nem létező számot adunk meg, RECORD OUT OF RANGE hibaüzenetet kapunk.

Az egyes rekordokat kiírathatjuk a LIST, vagy DISPLAY - rövidítve DISP - parancsokkal is. Önmagában kiadva a LIST az összes rekordot felsorolja, a DISP csak az aktuálisat írja ki, amelyiken éppen állunk. Paraméterek is megadhatók utánuk, pl: LIST (DISP) CSALADNEV - ha van CSALADNEV nevű mezőnk, akkor csak azt írja ki. Több mezőnév is megadható, vesszővel elválasztva. LIST (DISP) ALL OFF - valamennyi rekord kiírása, de a rekordsorszám nélkül. A törlésre kijelölt rekordokat csillaggal jelzi a rendszer.
A rekordok között a SKIP paranccsal mozoghatunk. Ez alapértelmezésben egyet lép előre, de megadható utána a lépés is, pl. SKIP -2 két rekordot lép hátra. A törlésre kijelölt rekordokat kihagyja! Ezekre csak GOTO (rövidítve GO) [sorszám] paranccsal lehet rálépni. Amelyik rekordon éppen állunk, azt a DELETE-tel törlésre jelölhetjük, RECALL-al pedig visszahívhatjuk, ha meggondoltuk magunkat. Ezek is paraméterezhetők, pl.: DELETE RECORD 15 - a 15. rekord törlése, DELETE ALL FOR CSALADNEV="Kovacs" - az összes "Kovacs" tartalmú családnév mezőt tartalmazó rekord törlése, RECALL ALL - az összes törlésre kijelölt rekord helyreállítása.

Az adatbázis szerkezetét a DISPLAY STRUCTURE (rövidítve DISP STRU) paranccsal írathatjuk ki. Ekkor megjelenik az adatbázis neve, a rekordok száma, az utolsó módosítás dátuma, a használati prioritás (akkor érdekes, ha több adatbázis van a memóriában egyszerre), a mezők sorszáma, neve, típusa és hossza, valamint a mezők összes száma.
A DISPLAY MEMORY parancs a használt memóriaváltozókat írja ki.

Matematikai műveletek eredményét és változók tartalmát kérdőjellel írathatjuk ki. Pl.: ? 6/3 eredménye 2 lesz. (A kérdőjel után szóközt kell hagyni.)

A lemezen található adatbázisokat sorolja fel a LIST FILES parancs, a bennük levő rekordok számával és az utolsó módosítás dátumával együtt. Ha a lemezen (az aktuális tartalomjegyzékben) található összes állomány listájára vagyunk kíváncsiak, akkor írjuk be: LIST FILES LIKE *.* (egyéb maszk is megadható). A DELETE FILE [név] paranccsal törölhetünk egy állományt a lemezről. Ha nem adunk meg kiterjesztést, akkor a program DBF-et keres. Ha mást akarunk törölni, a kiterjesztést is írjuk hozzá. A program közli, hogy az adott állományt törölte, vagy nem találta meg.

Az adatbázist több mező szerint is indexelhetjük (sorba rendezhetjük), úgy, hogy különböző indexállományokat készítünk hozzá. Pl: INDEX ON CSALADNEV TO CSALAD. Itt a "CSALADNEV" a mező neve, ami szerint rendezni akarunk, a "CSALAD" pedig annak az indexállománynak a neve, amit a program a lemezen létrehoz. Ha ezután kilistázzuk a rekordjainkat, láthatjuk, hogy az ABC-sorrend valóban létrejött. Ha legközelebb megint használni akarjuk az indexelt adatbázist, USE CIMEK INDEX CSALAD paranccsal kell megnyitni! (Ahol a "CIMEK" természetesen az adatbázis neve, amivel létrehoztuk.) Lezárni továbbra is sima USE paranccsal lehet. VIGYÁZAT! Ha egyszer indexállomány nélkül nyitjuk meg az adatbázist, változtatunk rajta és lezárjuk, az indexállomány nem lesz érvényes. A REINDEX paranccsal a legközelebbi megnyitáskor aktualizálhatjuk az indexet. Persze, mindig elkészíthetjük az indexállományt teljesen újra, hiszen csak egyetlen parancsot kell kiadni hozzá. Az indexállomány kiterjesztése a lemezen automatikusan NDX lesz. Ha több indexünk van, megnyitáskor valamennyit soroljuk fel! Pl. USE CIMEK INDEX CSALAD,TELEFON,LAKCIM. Próbáljunk a lemezes állománynak (akár csak egy betűvel is) eltérő nevet választani, mint a rekordban használt mezőknek, hogy ne zavarjuk meg a programot. Mező és memóriaváltozó neve sohasem lehet ugyanaz!
Ha már van egy kulcsmezőnk, akkor aszerint kereshetünk a FIND paranccsal. Pl. FIND "Kovacs" megkeresi azt az első rekordot, amelyben a CSALADNEV kulcsmező tartalma "Kovacs". Ha több ilyen is van, SKIP-pel kell továbblépnünk, hogy a következőket is megtaláljuk. Csak a szó eleje szerint is lehet keresni, pl. FIND "Ko". A kis- és nagybetűket megkülönbözteti a program!
Ha több indexünk van és nem az elsőt akarjuk használni, a SET INDEX TO parancsot kell kiadni, ami után az index-állomány neve áll.

Ha a rekordjainkat fizikailag is át akarjuk rendezni, használjuk a SORT parancsot. Pl: SORT ON TELEFON TO TEL a használatban lévő adatbázist a TELEFON mező szerint rendezi, és egy új példányt készít belőle a lemezen TEL.DBF néven. A mezőnek, ami szerint rendezünk, nem kell kulcsmezőnek lennie. Ha megadjuk a DESCENDING paramétert is a végén, akkor csökkenő sorrendet alakít ki. (Megérti az ASCENDING - növekvő sorrend - paramétert is, de mivel ez az alapértelmezés, nem kell külön kiírni.)

Végül, néhány a beállítható paraméterek közül:

II. A rendszer programozása
Programok készítésére a MODIFY COMMAND parancs szolgál. Ezután meg lehet adni a program nevét; ha nem adjuk meg, rákérdez. Amennyiben van már ilyen nevű program a lemezen, akkor azt betölti. Ha nincs, kiírja, hogy NEW FILE, és csak ezután jelentkezik be a beépített szövegszerkesztő.
(Megjegyzés: A program szövege normál szöveges állományként tárolódik, tehát nem kódolva, mint a BASIC programok.)
Minden utasítást külön sorba kell írni. Az első sor ne kezdődjön szóközzel! A többinél már nem számít, tehát a szöveget tetszés szerint tabulálhatjuk.
A beírás végén a programot a CTRL-W kombinációval tároljuk el, amivel ki is lépünk a szerkesztőből. Ezután a programot a DO <PROGRAMNÉV> paranccsal futtathatjuk.

Legjobb, ha az utasításokat néhány példaprogramon keresztül ismerjük meg. Az első program megnyit egy címeket tartalmazó adatbázist, az első rekordra áll, beírat egy család- és egy keresztnevet a kezelővel, majd tárolja ezeket a rekordban.

use CIMEK index CSALAD
go top
store CS_NEV to CS
store K_NEV to k
@ 2,2 say "Csaladnev:" get CS
@ 3,2 say "Keresztnev:" get K
read
replace CS_NEV with CS
replace K_NEV with K
disp all
use

A programban a változónevek nagybetűvel, az utasítások kisbetűvel vannak írva.
Az első sorban megnyitjuk a CIMEK nevű adatbázist, ami a CSALAD indexállománnyal van indexelve. A "go top" utasítás az első rekordra áll. Ezután kezdőértéket adunk két változónak: a CS-ben a beolvasott családnevet, a K-ban a keresztnevet tároljuk majd. (Ha kezdőértéket nem adunk, a változó nem létezik, és futás közbeni hibaüzenetet kapunk.) A szöveges változókat nem kell a név után írt "$" jellel megkülönböztetni, értékadáskor dől el, hogy a változó milyen típusú lesz.
Vegyük észre, hogy a változók kezdőértékeként tárolt neveket abból a rekordból olvassuk ki, amelyen állunk. (Feltételezzük, hogy a CS_NEV a családnév mező, a K_NEV pedig a keresztnév mező neve a rekordban.)
Arra mindenki rá fog jönni, hogy a következő utasítások mire valók. A "say" egy szöveget ír ki a képernyőre a megadott koordinátáktól kezdve. A @ jel után egy szóközt kell hagyni! A "get" pedig, természetesen, a változók beolvasására való, azaz a BASIC INPUT utasításának felel meg. Önmagában azonban még nem olvas be semmit, csak elkészíti a képernyőmaszkot, amelyben annyi helyet hagy, amennyi a változó kezdőértékének hossza volt. A tényleges beolvasást a következő sorban a "read" utasítással kezdjük meg. Ilyenkor a rendszer lehetővé teszi, hogy a kezelő a képernyőmaszkba beírja az adatokat, az egyes mezők között akármilyen sorrendben lépkedve. A bevitel végét az utolsó mezőben lenyomott ENTER jelenti.
Most látjuk értelmét annak, hogy az előbb a változókba a rekord mezőinek tartalmát tároltuk. Ugyanis a "get" utasítás a beolvasott változók eredeti tartalmát kiírja a képernyőmaszkba, így mindig látjuk, hogy mit módosítunk. Amikor lenyomjuk az ENTER-t, a rendszer mindig azt a szöveget tárolja el, ami éppen a képernyőn látható.
E tulajdonsága miatt a "get" beolvasás nélkül, csak a mezők tartalmának megjelenítésére is jó. Ilyenkor a kiírás után nem a "read", hanem a "clear gets" utasítást kell kiadni.
Az utolsó lépésben a mezők tartalmát kicseréljük (replace) a beolvasott változókra. (Az újonnan beírt nevet a rendszer az index szerint be is rendezi.) Ezután ellenőrzésképpen kiíratjuk az összes rekordot (disp all) a képernyőre, majd lezárjuk az adatbázist.

Természetesen egy valódi programban bizonyos szempontok szerint kell kikeresni a módosítandó rekordot, és a mező tartalmának átírása előtt a kezelőnek biztonsági kérdést illik feltenni. Nézzünk most egy "menükészítő" példaprogramot, amelynek segítségével a kezelő választhat a program szolgáltatásai közül. Tegyük fel, hogy a menüpontokat már kiírtuk a képernyőre (say).

accept "Valasszon!" to W
do case
  case w="1"
    do RESZ_1
  case w="2"
    do RESZ_2
  case w="3"
    do RESZ_3
  otherwise
    @ 10,2 say "Ilyen pont nincs!"
endcase

Nem nehéz felismerni a BASIC-ből már ismert CASE ágak megfelelőit. A RESZ_1, RESZ_2 és RESZ_3 alprogramok nevei, amelyeket külön írtunk meg és a lemezen tároltunk. Az "otherwise" (angolul "egyébként") természetesen az ELSE ágnak felel meg. Az első sor "accept" utasítása kiírja a megadott szöveget a képernyőre, majd egy billentyű lenyomását várja, és azt tárolja a W változóban.
A következő program az összes rekord tartalmát egyenként kiírja a képernyőre, és vár, amíg a kezelő elolvassa azokat és lenyom egy billentyűt.

do while .not. eof
  disp
  accept "Ha elolvasta, nyomjon meg egy billentyut!" to W
  skip
enddo

Ciklusszervezést látunk, ami már szintén ismerős. Az első sor azt jelenti, hogy a ciklust addig kell ismételni, amíg még nem értük el (.not.) a file végét (eof = end of file). A relációs operátorokat (not, and és or) valóban két pont közé kell tenni, nem sajtóhiba. Kiírjuk az aktuális rekordot (disp), majd billentyűlenyomásra várunk, végül a következő rekordra lépünk (skip). Egyébként várakozásra a rendszer a "wait" parancsot is ismeri, ami WAITING... üzenetet ír a képernyőre, de ezután más szöveg nem adható meg.

Végül ismerkedjünk meg a feltételes utasítással is. Írassuk ki az összes rekord tartalmát, kivéve azokat, ahol a családnév mező tartalma "Kovacs". Az alábbi program (amelyet a HELP szövegből alakítottam át) ezt egy kissé csavarosan oldja meg, de működik.

do while .not. eof
  if CS_NEV="Kovacs"
    skip
    loop
  endif
  disp
  skip
enddo

Vagyis: ha a családnév Kovacs, akkor kihagyjuk ezt a rekordot (skip), és visszaadjuk a vezérlést a "do while" utasításra (ezért ilyen ravasz a példa, hogy a "loop" használatát is be lehessen mutatni vele). Ellenkező esetben az "if" utasítás utánra kerül a vezérlés, ahol is kiíratjuk a rekord tartalmát és csak utána lépünk a következőre.
Kissé egyszerűbben ugyanez:

do while .not. eof
  if CS_NEV<>"Kovacs"
    disp
  endif
  skip
enddo

Természetesen az "if" utasításnak "else" ága is lehet. Hogy még mindig ennél a példánál maradjunk:

do while .not. eof
  if CS_NEV="Kovacs"
    remark Kihagyva.
  else
    disp
  endif
enddo

Könnyű kitalálni, hogy a "remark" utasítás az utána következő szöveget kiírja a képernyőre. Itt jó ezt használni, mert nem kell neki koordinátát adni, mint a "say"-nek. A szöveget nem kell idézőjelbe tenni.
Még egy kis példa, kommentár nélkül:

remark Nyomd meg!
wait
remark Na vegre!

A "say" és a "get" utasítás után egyaránt használhatunk kiírási mintát. Erre a két utasítás esetében két különböző kulcsszó szolgál, amelyeknek más-más mintát adhatunk meg:

@ <koordináták> say <változó- vagy mezőnév> using "kép"

@ <koordináták> say <változó- vagy mezőnév> get <változó- vagy mezőnév> picture "kép"

@ <koordináták> get <változó- vagy mezőnév>

"Using" után megadható paraméterek:

Bármely egyéb jellel a mező/változó tartalmát a képernyőn felülírja.

"Picture" után megadható paraméterek:

Bármely egyéb jel a kiírásban elválasztójelként jelenik meg, és ezeket a beolvasáskor a kurzor átlépi. Pl. telefonszámba tehetünk kötőjeleket, vagy a körzetszámnak zárójeleket:
picture "+(###)-##-##-##"

A programban használt memóriaváltozók értékeit bármikor lemezre menthetjük (save to <filenév>), majd visszaolvashatjuk (restore from <filenév>). A "release" utasítás pedig a változókat törli és az általuk használt memóriaterületet felszabadítja.
Ha nyomtatni szeretnénk, adjuk ki a "set format to print" utasítást. Ezután minden "say" a nyomtatóra fog írni, egészen addig, amíg "set format to screen" utasítással vissza nem állunk a képernyőre.

A memóriában egyszerre két adatbázis is lehet. Ilyenkor a két adatbázis adatterülete között a "select primary" (elsődleges) és "select secondary" (másodlagos) utasításokkal lehet oda-vissza lépkedni. Változóterület csak egy van, tehát a két adatbázishoz nem rendelhetünk azonos nevű memóriaváltozókat.

III. A beépített függvények

chr(szám) azt a karaktert adja, amelynek ASCII kódja a megadott szám.
int(szám) a megadott tizedesszám egész része.
len(string) a karakterlánc hossza.
trim(string) a karakterláncról a végén levő szóközöket levágja.
val(string) a karakterlánc számértéke.
!(string) a karakterláncot nagybetűsíti.
file(filenév) igaz, ha a lemezen a megadott file létezik, hamis, ha nem.
type(mezőnév) a megadott mező típusa (karakteres, szám vagy logikai, azaz C, N vagy L).
date() a rendszerdátumot adja meg.
str(számértékű kifejezés, mezőszélesség, tizedesek száma)
  a megadott számot vagy a kifejezés értékét karakterlánc formára alakítja, ahol a mezőszélesség a kívánt karakterlánc összes szélességét, a tizedesek száma pedig ezen belül a tizedesjegy-helyek számát adja meg.
rank() egy karakter ASCII-kódját adja meg.

Természetesen ahol a felsorolásban a zárójelben "string" szerepel, ez lehet szöveges változó, vagy szöveg típusú mező neve is.

IV. Jelentés (report) generálása
A rendszer a "report" parancsra megpróbál egy kezdetlegesen formázott jelentést készíteni a rekordjainkról. Az utasítás kiadása után az alábbi kérdéseket teszi fel:

Enter options, m=left margin, l=lines/page, w=page width.
Erre be kell írnunk, hogy hol legyen a bal margó, hány sor legyen egy oldalon, és milyen széles legyen egy oldal. Például: m=2, l=15, w=50.

Page heading required? Y/N
Kérünk-e fejlécet az oldalra? Ha igen, a következő kérése, hogy gépeljük be:
Enter page heading.

Double space report? Y/N
Dupla sortávolsággal írja-e a jelentést?

Are totals required? Y/N
Akarunk-e összegezni? Ha igen, akkor további kérdések következnek, amelyek során megadhatjuk, hogy mely mezők tartalmát akarjuk összeadni.

Col. width, contents
001

Itt írjuk be, hogy melyik oszlopban mi legyen, és milyen szélességben. Pl:
001   20, CS_NEV
002   10, K_NEV
003   <ENTER>

Minden oszlop megadása után rákérdez a föléje írandó fejlécre is (Enter heading).

Ha mindez megvan, a formátumot egy file-ba tárolja, FMT kiterjesztéssel. A nevét nekünk kell megadnunk, és ezt a formátumot ezek után máskor is használhatjuk. Ha minden kész, a jelentés megjelenik a képernyőn. Ez persze csak hevenyészett tájékozódásra jó, szebb jelentéseket programból lehet készíteni.

V. Adatbázis szerkezetének módosítása
Ha már adatok vannak az adatbázisunkban és akkor derül ki, hogy a szerkezetén módosítani kell (mezőket törölni, hozzáadni vagy a hosszukat változtatni), az alábbi eljárást kövessük (programból nem lehet, egymás után kell kiadni a parancsokat):

.use CIMEK

megnyitjuk az adatbázist,

.copy structure to IDEIG

ideiglenes állományba másoljuk a szerkezetét,

.use

lezárjuk az adatbázist. (*)

.use IDEIG

megnyitjuk az ideiglenes állományt,

.modify structure

módosítjuk a szerkezetet

.append from CIMEK

az adatokat átmásoljuk az ideiglenes állományba

.delete file CIMEK

töröljük a lemezről a CIMEK.DBF állományt.

.use

lezárjuk az ideiglenes állományt,

.rename IDEIG to CIMEK

átnevezzük a létrehozott új állományt a régi névre.
(Megjegyzés: Ha az adatokkal tele állomány szerkezetét módosítjuk, az adatok elvesznek. Ezt az eljárást teljes egészében a HELP szövegből vettem. A csillaggal jelölt sort én tettem hozzá, mert az elsőként megnyitott adatbázist az új megnyitása előtt szerintem le is kéne zárni.)
Mielőtt ezt kipróbáljuk, készíthetünk biztonsági másolatot az adatainkról a "copy to <állománynév>" paranccsal. Visszaolvasás az "append from <állománynév>" paranccsal lehetséges.

Szalontai Andrea - Enterpress 1994/1-3.

Címezzünk pontosan!
A félreértések elkerülése végett nem a Magyar Posta bérelt fel, hogy nagy példányszámú, sűrűn megjelenő lapunkon keresztül serkentse kis hazánk némileg slendrián népét a postai küldemények címzettjének akkurátusabb feltüntetésére, most is kedvenc ENTERPRISE-unkról folyik majd a szó.
Mint tudjuk, gépünk 4 megabájt memóriát, és 64 kilobájtnyi I/O-egységet képes megcímezni, ez egy ilyen kis masinától szép teljesítmény, elvileg el is férne minden bővítés ekkora nagy helyen. Gond akkor lép fel, ha két kártya, mely külön-külön pl. csak 4 bájtot fogyasztana, ugyanarra a 4 bájtra veti ki a hálóját. Ennek rendszerint lefagyás, súlyosabb esetben multimédia (különböző fény- és hangjelenségek), vagy akár az ENTERPRISE elhalása a következménye. Ezért igen fontos, hogy a nagy kártyagyártók (mint pl. én: 190 cm, és 95 kiló), és egyéb buherálók (nem csak ZozoHard!) megállapodjanak a piac felosztásában. Ez ügyből kifolyólag az alábbiakban megteszem javaslatomat:

Memória

Szegmensek (hex) Eszköz
00 - 03: EXOS (a gép belsejében, fix)
04 - 07: cartridge
10 - 1F: microTEAM-kártya 1. EPROM
20 - 2F: EXDOS (microTEAM-kártya 2. EPROM)
30 - 3F: microTEAM-kártya 3. EPROM
40 - 5F: microTEAM-kártya 512 KB RAM
60 - 63: EPROM / SRAM-bővítő kártya SRAM
64 - 77: EPROM / SRAM-bővítő kártya EPROM
10 - 1F, 30 - 7F: eredeti régi EPROM-bővítő kártya
70 - 7F, 90 - DF: buherált régi EPROM-bővítő kártya
78 - AF, F0 - F7: 2. 1 MB-os RAM-bővítés
B0 - EF: 1. 1 MB-os RAM-bővítés
EC - F7: 320 KB belső RAM-bővítés egy része
F8 - FB: 64 KB a gép belsejében, bővítőkártyán, vagy a 320 KB belső bővítés másik része
FC - FF: 64 KB az alaplemezen (fix)
08 - FB: egyéb buhera és takera

Mint látható, itt máris összeveszések történtek. Persze nem annyira vészes a dolog, hiszen pl. akinek belső 320 KB-os RAM-bővítése van, annak tudok pl. A0-DF közé eső 1 MB-os RAM-bővítőt készíteni, ha nagyon muszáj. A helyzetet tovább javítandó, minden régi EPROM-bővítő kártyát amennyiben jogos tulajdonosa igényt tart rá - az új címzésének megfelelően (60 - 77) átalakítok. Ez sajnos nincs ingyen, a szükséges GAL miatt, az ár 400 Ft. Ezenkívül kell még a játékhoz a ZozoSoft-féle EXOS 2.3, amely minden szegmenst végignéz rezidens rendszerbővítőket keresve (egész ügyes), ez csekély 1000 Ft-ba kerül. Persze ha valakinek jól működik a rendszere, és nem kíván (vagy nincs pénze) újabb bővítéseket beszerezni, használja egészséggel továbbra is úgy, ahogy van!

I/O cím eszköz
00 - 07: soros (egér-) illesztő kártya
10 - 1F: lemezvezérlő kártya
30 - 37: 48-vonalas digitális I/O-kártya
40 - 4F: Spectrum-emulátor
50 - 57: koprocesszor-kártya (próbapéldány)
60 - 62: XT-billentyűzet illesztő
70 - 71: elemes óra/naptár
80 - 8F NICK
A0 - B7, BF DAVE

Aki tud egyéb kártyákról, és azok címeiről, kérem, tegye közkinccsé, mindannyiunk érdekében! Ha pedig valaki kártyát épít, vagy átépít, válasszon még üres címtartományokat, és választását lehetőleg terjessze (pl. lapunkban), megakadályozandó a későbbi problémákat! Végül, ha valaki merészelne egyéb, a fentiektől eltérő javaslattal előállni, ám tegye! Valahogy majd csak megegyezünk...

Mészáros Gyula - Enterpress 1994/1-2.

AZ ENTERPRISE rendszer-szegmens rögzített területének címei és azok funkciója

ABD1-D2: A csatornapuffer-kijelölés EXOS-funkció ide menti el az SP-t.
AC3B-3C: STACK_LIMIT változó alapértéke. Idáig terjedhet a verem.
ACE7-B216: Az EXOS saját rendszer-verme. (kezdőérték B217)
B217-2F: A kernel segédrutinjai. Ezeken keresztül hívja meg azokat a rutinokat, melyek 3. lapú címekkel más szegmensen futnak (pl. rendszerbővítők; az 1. szegmensen lévő EXOS-rutinok), valamint az a rutin amelyen keresztül a vezérlés elhagyja a kernelt.
B230-50: A különleges státuszüzenet (ha az ST_FLAG tartalma 2AH).
B251-52: EDITOR változó címe-2
B253-8C: EDITOR munkaterület: (angol gépnél)
B253-54: Szövegterület kezdőcíme.
B255-56: Szövegterület vége. (vonalzósor kezdőcíme-1)
B257-58: Szövegmutató.
B259-5A: Szövegmutató.
B25B-5C: Szövegmutató.
B25D-5E: Szabad terület nagysága.
B25F: Editor video csatornaszám.
B260: Editor keyboard csatornaszám.
B261: Editor csatornaszáma különleges funkciónál, szövegfájl kimentés / betöltés esetén C értéke vagy a hiba kódja.
B262: Editor ESC ;80=ESC jelzés; <80=ESC szekvencia karakterszámláló
B263: Kurzor beállítás Y koordináta.
B264: Kurzor beállítás X koordináta.
B265: Video Y
B266: Video X
B268: Vonalzósor állapota. (0=ki, 1=be)
B26D: Bal margópozíció alapértelmezése.
B26E: Jobb margópozíció alapértelmezése.
B26F: Bal margópozíció.
B270: Jobb margópozíció.
B271: Margó-feloldás jelző. (0=érvényes, 2A=alapértelmezés)
B272: Színkód+128
B273: Kurzor Y
B274: Kurzor X
B277: Vezérlőkarakter kódja.
B27B: Hibajelző (b0=1 videó csat., b1=1 keyboard csat.)
B27D: Szövegpuffer vezérlőkarakterek kiírásához.
B27E: Puffer.
B27F-80: Puffer.
B282: Módosított FLG_EDIT értéke.
B283: Számláló.
B286: Beszúrás-felülírás jelző
B287: Újraformázás-kiegyenlítés jelző.
B288-89: IX értéke.
B28A: P1-es szegmens száma.
B28B: Használat alatti EDITOR csatorna száma.
B28D-E2: TAPE munkaterület:
B28D: A 0038 cím eredeti tartalma.
B28E: Az aktuálisan működtetett távvezérlő.
B28F: Olvasásra megnyitott fájl csatornakódja +1 vagy 0 ha nincs.
B290: Írásra megnyitott fájl csatornakódja +1 vagy 0 ha nincs.
B291: Az input fájl távvezérlője: 00=REM1, más=REM2.
B292-AE: Az input fájl neve.
B2AF: Az output fájl távvezérlője: 00=REM1, más=REM2.
B2B0-CC: Az output fájl neve.
B2CD-E: Az input pufferben lévő bájtok száma.
B2CF-D0: Az output pufferben lévé bájtok száma.
B2D1-2: A input puffer aktuális címe.
B2D3-4: Az output puffer aktuális címe.
B2D5: EOF-jelző: <>00= a fájlból nem lehet olvasni.
B2D6: Az olvasott szelet típusa: 00=adat, FF=fej.
B2D7: Adat a csatornalezárásnak: 00=van még adat a pufferben.
B2D8: A védelmi bájt értéke: 00=a védelem bekapcsolva.
B2D9: 00=nem volt hiba, FF=CRC hiba.
B2DA-DB: Az SP regiszterpár átmeneti tárolója.
B2DC: A kimentés jelszintje (a 0ABH portya kikerülő érték).
B2DD-E0: Konstansok a megfelelő felvételi sebességhez (a 0A0H porta).
B2E1: A státuszsor eredeti COL2 palettaszíne.
B2E2: A státuszsor eredeti COL3 palettaszíne.
B2E3-47F: SOUND munkaterület:
B2E3-F2: Az A0-AF portok aktuális értékei.
B2F3: Foglaltság jelző. 0=nincs megnyitva. Használat alatt a puffer szegmensszáma az első lapon.
B2F4-F5: A burkoló tárterület kezdőcíme. (0=nincs)
B2F6-F7: A burkoló tárterület végcíme / várakozási OR kezdőcíme.
B2F8: SYNC
B2FA: Hangforrás sorszáma.
B2FB-37A: Hangsorok 8 bájtonként.
B37B: 6*fázisok száma
B37C-7D: hangkezelő escape-szekvenciáit kezelő rutin címe.
B37E-7F: A hangkezelő escape-szekvencia argumentum következő bájtjának címe.
B380: Hangkezelő ESC jelző / számláló. 0=szabad karakter olvasódik be, 1-7F=ESC utáni vezérkarakter olvasódik be, 80-FF = a még beolvasandó paraméterbájtok számának kettes komplemense.
B381-47F: SOUND pufferterület: (ESC+vezérkód utáni paraméterek számára)
B480-C7F: Video munkaterület:
B480-8FF: Karakterkészlet (a CH128 módú karaktergenerátor)
B900-B1F: Az alapértelmezés szerinti LPT. (1+27+6 sor * 16 bájt)
BB20-46: Nem használt.
BB47: Az attribútum jelzőbájt másolata (IX-85)
BB48-49: Terület nagysága. (IX-54), (IX-53)
BB4A: b0-b3=tinta b4-b7=papír (IX-81)
BB4B: Video_mode (IX-2)
BB4C: Vonaltípus (IX-79)
BB4D: Vonalmód rutin paramétere (IX-80)
BB52-53: Y koordináta.
BB54-6B: Puffer a pontok számolásához.
BB6C-6D: X oldal hossza.
BB78-79: Rutin címének tárolója.
BB7E-7F: Ellipszis sugár X.
BB84-85: Rutin címének tárolója.
BB8E-8F: A másodlagos kijelzés RAM címe (IX-48), (IX-47)
BB90-91: Fő kijelzés RAM címe (IX-50), (IX-49)
BB92-93: Veremmutató tárolására.
BB94: PAPER vonalbájt.
BB95-96: X oldal karakteres hossza.
BB97: Pontbitek.
BB98: Távolság jobbra.
BB99-9A: Pont videocíme.
BB9B-9C: Előző pontszám.
BB9D-9E: Jelzőbitek és a letett pontok száma.
BB9F: Szegmens száma.
BBA2-BC: 27 bájt: csatornaszámok az LPTsorok kirakására.
BBBD-D7: 27 bájt: csatornán belüli sor száma az LPT-hez.
BC80-97: NET-SERIAL munkaterület:
BC80-81: A CRC regiszter.
BC82: A soros pufferben a következő bájtra mutató ofszet.
BC83: A soros pufferben lévő bájtok száma.
BC84-85: Várakozási ciklus-számláló a megfelelő baud értékhez.
BC87: SERIAL foglaltság jelző. (0=nincs csatorna)
BC90-D0F: NET eszközök leírói 4 bájtonként. (max. 32 gép 4*32=128 bájt)
BD16-EB7: KEYBOARD munkaterület: (angol gépnél)
BD16-1F: A legutoljára leolvasott billentyűmátrix töröli SHIFT-ekkel.
BD20-29: Az előző billentyűmátrixból képzett segédadatok.
BD2A-EA9: A funkcióbillentyű sztringek 24-24 bájton.
BEAA: Ha <>0 akkor a BEAE tartalma érvényes (van éppen lenyomott billentyű).
BEAB: Ha FF akkor HOLD állapot van érvényben.
BEAC: A váltóbillentyűk állapota: b1=SHIFT, b2=CTRL, b3=ALT.
BEAD: Ha FF akkor van nyitott billentyűzetcsatorna.
BEAE: Az éppen lenyomott billentyű kódja.
BEAF: Ha FF akkor LOCK állapot van érvényben.
BEB0-B1: A billentyűzet-puffer legfelső elemének címe.
BEB2: A pufferben tárolt billentyűkódok száma.
BEB3: Számláló a billentyűkésleltetés megvalósításához.
BEB4: Számláló az automatikus ismétlés megvalósításához.
BEB5-B6: Annak a billentyűsornak a címe, amelyben megváltozott állapotú billentyű van.
BEB7: A megváltozott állapotú mátrixsor képe.
BEB8-BFFF: Kernel terület
BEB8-DF: 40 bájtos státuszsor memória
BEE0-F07: Az EXOS ide generálja az általa kezelt hibaszöveget.
BF08-17: A betöltés alatt álló modul fejrésze.
BF18: Az alapértelmezésbeli egységszám.
BF19-35: Az alapértelmezésbeli eszköznév a határoló ":" és egységszám nélkül.
BF36-53: Eszköznév-puffer: eszköz- / fájlnév megadásakor itt képződik a kernel által kezelhető eszköznév a határoló ":" és egységszám nélkül.
BF54-71: Fájlnév-puffer: eszköz- / fájlnév megadásakor itt képződik a kernel által kezelhető fájlnév.
BF72-77: Az EXOS által kezelt naptár és óra. (BCD)
BF72: Másodperc.
BF73: Perc.
BF74: óra.
BF75: Nap.
BF76: Hónap
BF77: Év-80.
BF78: Indításjelző: b0=1 akkor a ROM-bővítők már jelöltek ki RAM-ot. Nulla esetén: EXOS 1A=Rendszerbővítők hívása hidegindítással. (C=10,20,60) EXOS 00=Rendszerbővítők RAM kiutalása.
BF79: Futásjelző: b0=1 akkor egy periféria futás közben van. b7=1 akkor a kernel futás közben van, tehát nem kell áttérni
a rendszer-veremtárra. (innen állapítja meg, ha rekurzívan fut)
BF7A-7B: A kernel itt tárolja az SP belépés elölti értékét.
BF7C-7D: A kernel itt tárolja a HL regiszterpát belépés előtti értékét.
BF7E: 1. ROM szegmens ellenőrző kód: / kontroll-summa. Értéke: A7H. Képzése: ACH XOR (01:FFD8-tól 19H össze XOR-ozva). (időnként megszakításból ellenőrzi a rendszer)
BF7F: A relokálható betöltés futási lapja (gyakorlatilag a cím MSB).
BF80: A csatornaszám, ahonnan a modult be kell tölteni (nincs eggyel megnövelve!).
BF81-83: A csatornaláncban történő kereséskor a következő elem címe. A periféria-alprogramokat hívó szubrutin itt várja a csatornaleíró 24 bites első lapú címét.
BF84-8E: A csatornapuffer-kijelölési funkció területe:
BF84: Az igényelt puffer típusa: 01=video
BF85: A puffert igénylő csatorna száma +1.
BF86-88: Az igénylő eszköz periféria-leírójának 24 bites címe.
BF89: A pufferigénylés jelzője: FF=nem volt még igénylés, 0=érvénytelen hívás (van már puffer).
BFBA: A legkisebb működő videoszegmens száma.
BF8B-8C: Utasításszöveg címe/puffer. A bővítő-letapogatás is használja.
BF8D-BE: A videocsatornák által elfoglalt RAM utolsó bájtjának Nick címe.
BF8F-90: A felhasználói határ értéke. (osztott szegmensnél használt)
BF91-92: Az EXOS-határ értéke. (Csatornaterület vége)
BF93-94: A csatornaterület kezdőcíme. (Perifériaterület vége)
BF95-96: A periféria terület kezdőcíme. (ROM-ok RAM-területének vége)
BF97-98: A ROM-bővítők RAM-területének kezdőcíme. (ROM-lista vége)
BF99: A RAM-bővítőknek kiutalt szegmensek listájának első eleme.
BF9A-9B: Az EXOS határszegmens címe.
BF9C-9D: A ROM-bővítők listájának kezdőcíme. (RAM szegmens lista vége)
BF9E-A4: A rendszerállapotot leíró bájtok, ahogy azt a 14H kódú EXOS-funkció adja vissza.
BF9E: Az EXOS határszegmens száma. (0 ha nincs)
BF9F: A szabad szegmensek száma.
BFA0: A felhasználó részére kiutalt szegmensek száma.
BFA1: A perifériák és RAM-bővítők részére kiutalt szegmensek száma.
BFA2: A rendszer által használt szegmensek száma.
BFA3: A működő RAM-szegmensek száma.
BFA4: A nem működő RAM-szegmensek száma.
BFA5-B1: A három legutóbb használt csatorna kódja és leírójuk 24 bites címe, valamint egy lezáró 00 a BFB1 címen. A legalacsonyabb bájton a csatornakód eggyel megnövelt értéke áll, majd a cím és a szegmensszám. A csatornakezelő funkciók esetén az EXOS a BFA5 címtől kezdve végignézi ezt a táblát, hogy szerepel-e benne a keresett csatornakód. A tábla végét 00 csatornakód jelzi. Az EXOS próbálja lerövidíteni a keresést a láncban.
BFB2-83: 0
BFB4-B5: A csatorna-RAM területének kezdőcíme (egy eltolási érték a BFBD címhez képest).
BFB6-89: 0
BFBA-BC: A csatornaleíró lánc első elemének 24 bites címe.
BFBD-BF: Az eszközleíró lánc első elemének 24 bites címe. A pointer a leíró DD-TYPE mezéjére mutat.
BFC0-C2: A RAM-bővítő lánc első elemének 24 bites első lapú címe. A pointer a belépési pontra mutat.
BFC3-C4: A REM1 és REM2 rendszerváltozó állításakor használt segédrutin címét tartalmazza az 1. szegmensen értelmezve. A magnókezelő inicializáló rutinja E9C1-re állítja be. Az itt lévő rutin a megfelelő rendszerváltozó aktuális értékét beírja a PORTB5 rendszerváltozóba és a /WR0 (0B5H) portra.
BFC5-EC: Az EXOS 10H hívással kezelhető EXOS rendszerváltozók:
BFC5: 0 IRQ_ENABLE_STATE /b0=hang, b2=1 Hz, b4=video, b6=DAVE (NET)
BFC6: 1 FLAG_SOFT_IRQ /szoftver megszakítás (tényleges címe BFF2)
BFC7: 2 CODE_SOFT_IRQ /szoftver megszakítás kádja
BFC8: 3 DEF_TYPE / fájl kezelő (0=magnó, 1=diszk)
BFC9: 4 DEF_CHAN / alapértelmezésű csatorna
BFCA: 5 TIMER / 1 Hz visszaszámláló (0-nál szoftver megszakítás)
BFCB: 6 LOCK KEY / 0=normál, 1=CAPS, 2=SHIFT, 8=ALT
BFCC: 7 CLICK KEY / 0=billentyűhang
BFCD: 8 STOP_IRQ / 0=stopra szoftvermegszakítás
BFCE: 9 KEY_IRQ / 0=billentyű lenyomásra szoftvermegszakítás
BFCF: 10 RATE_KEY / billentyűismétlés 1 /50Hz
BFD0: 11 DELAY_KEY / várakozás az ismétlésig 1/50Hz
BFD1: 12 TAPE_SND / 0=magnóhang bekapcsolva
BFD2: 13 WATT_SND / 0=SOUND várakozik ha megtellek a puffer
BFD3: 14 MUTE_SND / 0=belső hangszóró bekapcsolva
BFD4: 15 BUF_SND / hangburkoló mérete fázisokban
BFD5: 16 BAUD_SER / soros vonal sebesség
BFD6: 17 FORM_SER / soros vonal formátum
BFD7: 18 A R_NET / hálózati cím (NET szám)
BFD8: 19 NET_IRQ / 0=hálózat híváskor szoftver megszakítás
BFD9: 20 CHAN_NET / hálózatra blokkot küldő csatorna száma
BFDA: 21 MACH_NET / feladó gép NET száma
BFDB: 22 MODE_VID / video üzemmód
BFDC: 23 COLOR_VID / video színmód
BFDD: 24 X_SIZ_VID / video X méret
BFDE: 25 Y_SIZ_VID / video Y méret
BFDF: 26 ST_FLAG / 0=van státuszsor
BFE0: 27 BORD_VID / keret színe
BFE1: 28 BIAS_VID / 8-15 palettaszínek felső 5 bitje
BFE2: 29 VID_EDIT / erre a csatornára ír az editor
BFE3: 30 KEY_EDIT / erről a csatornáról olvas az editor
BFE4: 31 BUF_EDIT / editorpuffer = BUF_EDIT*100H (max. 63)
BFE5: 32 FLG_EDIT / az editor olvasás jelzője
     b7=szerkesztés nélküli azonnali olvasás
     b6=az egész szöveg olvasása
     b5=szövegszerkesztés ESC-ig
     b4=nem soronként hanem bekezdésenként kell olvasni
     b3=a bekezdésből csak a prompt utáni részt kell olvasni
     b2=a sor auto törlése (nyomtatható karakternél)
BFE6: 33 SP_TAPE / 0=magnó gyorsmentés
BFE7: 34 PROTECT / 0=magnó másolásvédelem
BFE8: 35 LV TAPE / felvételi szint (0-1=20, 2=40, 3=80, 4=170, 5=375, 6-255=700mV)
BFE9: 36 REM1 / 0=1. távirányító bekapcsolva
BFEA: 37 REM2 / 0=2. távirányító bekapcsolva
BFEB: 38 SPRITE / b0-b1=külső SPRITE színprioritása
BFEC: 39 RANDOM_IRQ / megszakítás számláló
BFED-FF: Az EXOS általános rendszerváltozói:
BFED-EE: Felhasználói megszakítási rutin címe. 0=nincs ilyen igény.
BFEF: A bejelentkezési üzenet elnyomásának jelzője.
BFF0-F1: 16 bites másodpercszámláló.
BFF2: A szoftvermegszakítás indítása.
BFF3: A B5 kiviteli port aktuális értéke.
BFF4-F5: Az LPT elejének címe.
BFF6-F7: A státuszsor memória címe.
BFF8-F9: A felhasználói melegindítás címe.
BFFA-FB: A rendszerveremtár ellenőrzéséhez használt 16 bites érték.
BFFC: 0. lapregiszter.
BFFD: 1. lapregiszter.
BFFE: 2. lapregiszter.
BFFF: 3. lapregiszter.

HSoft & EGO - Enterpress 1994/5.

Compfair '94
Ismét eltelt egy év. Nem csak az ENTERPRESS újság, hanem a számítástechnika életében is. Idén ismét megrendezésre került a számítástechnikát kedvelők és szeretők szakkiállítása és vására a Compfair. Az elmúlt évben (években) hazánkban rendkívüli módon megnövekedett a számítógépek iránti érdeklődés. Gondoljunk csak arra, hogy a jelenlegi piaci árak lehetővé teszik egy 286-os PC megvásárlását olyan áron, mely árat anno az ENTERPRISE 128-as megvásárlásakor fizettünk kedvenc gépünkért.
Egy kis kitérőt szeretnék tenni az árakkal kapcsolatban:
- Nagyon elszomorítanak azok az újsághirdetések ahol 5-8 ezer forintért eladásra kínálnak Enterprise számítógépet. Minden tiszteletem és megértésem azoké, akik komoly grafikus alkalmazások, vagy megélhetésük biztosítása érdekében lecserélik egy nagyobb teljesítményű PC-re kis gépüket. Ezek a 386, 486-os kiépítések árai meghaladják a 100 ezer forintot is.
- Viszont nem értem azokat akik egy olcsó 286-os PC-t vesznek. A mai viszonyok között ezek a gépek a PC-k rangsorában kb. ott állnak, ahol a ZX 81-es áll az Amigák, Atarik, Commodore és Enterprise gépek között a HC kategóriában.
Én azt hiszem nem szabad nagyon elszédülni attól a fogalomtól, hogy PC. Tessék körbenézni a lelkes programírók és átírók, hardware kiegészítőket gyártók között. Sok EP-s valószínűleg soha nem hallott róluk! Több hirdetőt felhívtam telefonon, volt olyan közöttük, akit sikerült saját táborunkban tartani és jelenleg is megelégedéssel használja a számára és talán még nagyon sok más számára ismeretlen lehetőségeket, melyeket az EP kínál.
E kis kitérő után irány a Compfair!
Az "A" pavilont zsúfolásig megtöltő standok látványa fogadta a látogatókat. A standok kisebbek voltak a megszokottnál, de ez a kiállítók magas számával magyarázható. A látogatók életkora az egészen pici, még nem "önjáró" gyermekektől a 80-as éveikben járó nagypapákig széles skálán mozgott. Volt aki öltönyt és nyakkendőt, volt aki farmer szerelést viselt. Egy valami mégis egyenlővé tette őket a látogatás idejére: a számítástechnika szeretete.
Mindenki vérmérsékletének megfelelő módon, de kigúvadt szemekkel és kihegyezett fülekkel ámuldozhatott a grafikus ábrázolások és multimédiás rendszerek keltette hang- és fényeffektek sokaságában.
Szimpatikus jelenség volt számomra, hogy több kiállító lehetőséget adott a látogatóknak termékeik kipróbálására. Öröm volt látni, hogy tizenéves srácok önfeledten ültek a gépek előtt és a legújabb alkalmazásokkal kötöttek ismeretséget pár perc leforgása alatt. Különböző játékos vetélkedőkön is részt vehetett a nagyérdemű közönség, melyeken mágneslemezeket és CD-ROM lemezeket lehetett nyerni. Aki biztosra akart menni, az - természetesen anyagi lehetőségeihez képest - meg is vásárolhatta a kiállított termékeket. Ezek közül sokat árengedménnyel kínáltak a vásárlóknak, de úgy érzem az igazán nagy üzleti haszonnal járó nyereség nem a magánszemélyek zsebéből vándorolt a cégek számláira. Volt ott minden csoda ami szem-szájnak ingere. Részletes felsorolása szinte lehetetlen. Talán csak néhány kiragadott példa:
- Szakkönyvek és Windows alatt futó alkalmazások tömkelege.
- Hatalmas és gyönyörű monitorok (méreteik miatt az áruk is tetemes volt).
- És természetesen a számítástechnikai kiegészítők, kártyák, processzorok, egerek, billentyűzetek, kütyük és tengeri herkentyűk áradata.
A "tengeri" szó szerint értendő, mert volt akváriumban víz alatt tökéletesen működő merevlemez meghajtó is. A HDD-k jelenleg a több gigabájtos (!) tárolókapacitásnál tartanak. (Ezekben egy kommandós kiképzést kapott, sok szektort megjárt vírus is eltévedne és kétheti bolyongás után önként átadná magát a "SCAN"-nek.) A Weller cég fantasztikus kínálatot mutatott be a számítástechnikát érintő tervezések és javítások kivitelezésére alkalmas miniatűr forrasztó pákák és az ehhez tartozó nagyító lencsék terén. Nekem mint irodagép műszerésznek fájt a szívem a sok csoda után, de az árjegyzék átolvasása utána mai napig is a régi megszokott forrasztópákámmal teszem ki atrocitásoknak az elém kerülő elektronikai berendezéseket.
Különböző fajtájú kivetítő berendezéseket is láthattunk, melyekkel monitor nélkül óriásvetítő vászonra, vagy akár a fehér falra megjeleníthetjük a számítógépünkből kijövő képet. Ez nagy segítség lehet azoknak akik gyengébb látással rendelkeznek. Azt hiszem szót kell ejtenem egy olyan nyomtató megjelenéséről, amely még jelenlegi állapotában sorozatgyártásra nem alkalmas, de bíztató ütemben halad a fejlesztése. A szóban forgó találmány a vakok számára, a normál nyomtatóktól eltérően pont írás (Braille) formájában jeleníti meg a nyomtatni szánt szöveget. Nem ez az első ilyen próbálkozás ezen a téren, de ha hozzátesszük azt is, hogy ez néhány középiskolai tanuló fejlesztése, akkor igazán nagy eredménynek számít!
Visszatekintve a kiállításon eltöltött pár órára, úgy érzem nem volt időpocsékolás a megtekintése. Mindenki megtalálhatta a számára érdekes újdonságokat, aktualitásokat és rövid időre egy olyan világba léphetett be amely már egy kicsit a jövő évezred hangulatát árasztotta magából.

Kecskés Sándor (SASA) - Enterpress 1994/6

Billentyűzet és joystick olvasás

Billentyűzet kiolvasási mátrix táblázat
(Assembler programozáshoz használható)

Használati utasítás képlete:

LD A,n azt a sort írjuk "n" helyére amely sorban van a kiválasztott billentyű. (0-9)
OUT (0B5H),A kiküldjük a B5 portra a kiválasztott sort
IN A,(0B5H) beolvassuk a kiválasztott sor értékeit.
AND N "N" helyére azt a hexadecimális számot írjuk amely oszlopban a kiválasztott billentyű van.
JR Z,N Ha a kiválasztott billentyűt nyomtuk meg akkor a "Z" jelző billentyű 1-re billen és a feltétel teljesül.

80H
40H
20H
10H
08H
04H
02H
01H
 

Példa:
Például kiválasztottuk az INS billentyűt.

LD A,08H
OUT (0B5H),A
IN A,(0B5H)
AND 80H
JR Z,...

SH. bal
Z
X
V
C
B
\
N
0
CTRL
A
S
F
D
G
LOCK
H
1
TAB
V
E
T
R
Y
Q
U
2
ESC
2
3
5
4
6
1
7
3
F1
F2
F7
F5
F6
F3
F8
F4
4
 
ERASE
^
0
-
9
 
8
5
 
]
:
l
;
K
 
J
6
ALT
ENTER
JOY. bal
STOP
JOY. fel
JOY. jobb
JOY. fel
HOLD
7
INS
SPACE
SH. jobb
.
/
,
DEL
M
8
   
[
p
@
O
 
I
9

Billentyűzet és joystick olvasás

Billentyűzet olvasás: Joystick olvasás:
LD A,X
OUT (0b5H),A
IN A,(0B5H)
BIT X,A
JP Z,LENYOMVA
LD A,X
OUT (0B5H),A
IN A,(0B6H)
BIT 0,A
JP Z,LENYOMVA
X
bit 7
bit 6
bit 5
bit 4
bit 3
bit 2
bit 1
bit 0
bit 0
0
Bal SH.
Z
X
V
C
B
\
N
TŰZ J1
1
CTRL
A
S
F
D
G
LOCK
H
FEL
2
TAB
W
E
T
R
Y
Q
U
LE
3
ESC
2
3
5
4
6
1
7
BAL
4
F1
F2
F7
F5
F6
F3
F8
F4
JOBB
5
 
ERASE
^
0
-
9
 
8
TŰZ J2
6
 
]
:
L
;
K
 
J
FEL
7
ALT
ENTER
BAL
HOLD
FEL
JOBB
LE
STOP
LE
8
INS
SPACE
jobb SH.
.
/
,
DEL
M
BAL
9
   
[
P
@
0
 
I
JOBB

Ferenci József - Enterpress 1995/3-4.

B0-B7, BF regiszterek értelmezése
OUT (B0),A
0. lapregiszter írása
IN A,(B0)
0. lapregiszter olvasás
OUT (B1),A
1. lapregiszter írása
IN A,(B1)
1. lapregiszter olvasás
OUT (B2),A
2. lapregiszter írása
IN A,(B2)
2. lapregiszter olvasás
OUT (B3),A
3. lapregiszter írása
IN A,(B3)
3. lapregiszter olvasás

OUT (B4),A

  • Bit 7 = NET megszakítás tár. törlés
  • Bit 6 = NET megszakítás engedélyezés
  • Bit 5 = VIDEO megszakítás tár. törlés
  • Bit 4 = VIDEO megszakítás engedélyezés
  • Bit 3 = 1 Hz megszakítás tár. törlés
  • Bit 2 = 1 Hz megszakítás engedélyezés
  • Bit 1 = Program megszakítás tár. törlés
  • Bit 0 = Program megszakítás engedélyezés

IN A,(B4)

  • Bit 7 = NET megszakítás tár. állapot
  • Bit 6 = NET osztó állapot
  • Bit 5 = VIDEO megszakítás tár. állapot
  • Bit 4 = VIDEO osztó állapot
  • Bit 3 = 1 Hz megszakítás tár. állapot
  • Bit 2 = 1 Hz osztó osztó állapot
  • Bit 1 = Program megszakítás tár. állapot
  • Bit 0 = Hanggenerátor osztó állapota

OUT (B5),A

  • Bit 7 = REM2
  • Bit 6 = REM1
  • Bit 5 = Magnóhang csatolás
  • Bit 4 = Printer STROBE output (impulzus)
  • Bit 0-3 = Billentyű, joystick sora

IN A,(B5)
Bit 0-7 = Billentyű mátrix input

OUT (B6),A
bit 0-7 = Printer DATA output

IN A,(B6)

  • Bit 7 = Tape DATA input
  • Bit 6 = Tape LEVEL input
  • Bit 5 = NET STATUS input
  • Bit 4 = NET DATA INPUT
  • Bit 3 = Printer READY input
  • Bit 0 = Joy input

OUT (B7),A

  • bit 1 = NET STATUS output
  • bit 0 = NET DATA output
 

OUT (BF),A

  • Bit 2-3
    • = 0 = memória műveleteknél várakozás, kivéve a VIDEO RAM-ot
    • = 1 = M1-nél várakozás, kivéve a VIDEO RAM-ot
    • = 2-3 = Nincs várakozás (gyors perifériákhoz)
  • Bit 1 = Órajel: 0=8 Mhz, 1=12 Mhz (DAVE osztó számára)
  • Bit 0 = Beépített RAM: 0-64 K; 1=16K (WRAM kimenethez a VIDEO RAM kapuzáshoz)

HSOFT - Enterpress 1995/3-4

Felhasználói megszakításkezelés
Több levelet kaptam már olyan kéréssel, hogy az ENTERPRESS-ben publikáljak cikkeket az ENTERPRISE programozással kapcsolatban. Az egyik levélíró a megszakításkezelést hiányolta. Az újság várhatóan rövid életkilátására tekintettel, nem sorozatban hanem egyetlen részbe sűrítettem lehetőségeket. Mindenesetre, aki komolyabban szeretne assemblyben ENTERPRISE-t programozni, annak javaslom az EXOS 2.1 változat MŰSZAKI LEÍRÁS valamint a DONÁT JÁNOS Z80 SOFTWARE TÁBLÁZATOK könyvek beszerzését.
A rendkívüli események kezelését megszakításból célszerű végezni, ugyanis a főprogramból figyelve jelentős késés és teljesítmény csökkenés keletkezik. Az ENTERPRISE hardware-megszakítását a DAVE-chip kezeli. A DAVE két belső és két külső maszkolható megszakítást tud lekezelni. (DI-EI utasítással ki és bekapcsolható) A külső megszakítások a NICK általi videó-megszakítás valamint a NET általi hálózati megszakítás. A DAVE belső megszakításai a programozható idejű valamint az ENTERPRISE órakezelésére használt 1 Hz. A 4 megszakítás-regiszter egymástól függetlenül engedélyezhető ill. tiltható. A kezelését a bővítők igényének megfelelően az EXOS operációs rendszer végzi, valamint meghívja a perifériák és a felhasználó megszakításkezelő rutinjait.
Hogyan zajlik le egy ENTERPRISE megszakítás? A 0. EXOS változó valamely bitjével jelzett megszakításforrást az EXOS törölte és engedélyezte az újbóli megszakítást az előző megszakítás végén. A megszakításforrás pozitív vagy negatív felfutási éle bekapcsolja a megszakítás-regisztert, s ettől kezdve a DAVE folyamatos jelzést küld a Z80 felé. Amikor EI-vel engedélyezett a megszakítás, a Z80 DI-vel letiltja a processzor további megszakítás lehetőségét és RST 38H rutinhívást végez. (Ezért nem szabad kilapozni a nullás lapot engedélyezett megszakításnál!) A 0038H EXOS megszakításrutin a következőket végzi. Elmenti a Z80 regisztereket és az aktuális szegmenslapokat. Beolvassa a DAVE megszakítás-regiszterét, melynek alapján eldönthető a megszakítás forrása. Igény esetén meghívja a felhasználói megszakításkezelő rutint, D-regiszterben jelezve a forrást. Végiglapozza a perifériákat és a megszakítás igényük alapján meg is hívja a megszakításrutinjukat. Törli és újra engedélyezi a kiszolgált DAVE megszakítás-regisztert, hatására megszűnik a DAVE jelzése a Z80 felé. Helyreállítja a szegmenseket és a Z80 regisztereket. EI-vel engedélyezi a megszakítást, majd RET-tel visszatér a főprogramhoz.
Címek és portok:

Példaprogram,

A forráslista magyarázata:
A lefordított felhasználói program a négyből három megszakításforrást is használ. Felsorolva: videó, 1 Hz, programozható megszakítás. Az első kettő nem okoz nehézséget, mivel az EXOS perifériák már használják. Problémát okoz viszont a programozható megszakítás, ha nem az 1 KHz-et szeretnénk alkalmazni. A DAVE A0-AF portokat videómegszakításból a SOUND periféria, HOLD állapot alatt a KEYBOARD periféria is átírja. Ezért a SOUND perifériát letiltjuk egy azonos nevű, de feladatot nem végző felhasználói periféria felvételével. A KEYBOARD-HOLD miatt videómegszakításból frissítjük a DAVE általunk használt 3 regiszterét. Az új megszakításforrást bevezetjük a 0. EXOS változóba, majd megadjuk a saját megszakításrutinunk címét. A csatornák megnyitása némi magyarázatot kíván. Az OPEN rutin után felsorolom a szükséges EXOS változók új értékét, a végét nullával jelzem. Utánuk megadom a csatornák számát és nevét, a végét 255-tel adom meg. (rövidebb mint a macro.) A PRINT rutin hasonló módon a hívó helyről  olvassa be a paramétereket. Hossz, csatornaszám, sztring. Utána kirakjuk a képernyőre a videólapot. Megadjuk a szoftvermegszakítás kezelőnk címét. Ezt a megszakítást többnyire a perifériák generálják, de a felhasználói program is indíthatja az 1. EXOS változó írásával. (FF:BFF2) A STOP hatására tehát a KILEP rutin EXOS RESET-et hajt végre, (SOUND perifériánk törlése) kijavítja a karakterkészlet módosított szóközét, majd kilép BASIC-be. A főprogramot tovább követve, információt írunk a képernyőre majd olvassuk a billentyűzetcsatorna állapotát. Itt addig fogunk várakozni amíg érvényes karaktert nem kapunk.
Töröljük a képernyőt és meghívjuk az input rutint. A kapott szöveget átadjuk az EXOS parancsértékelőjének, majd kezdjük elölről az egészet. (A magnós alaprendszer csak két parancsszót ismer WP, BASIC, valamint a HELP, HELP BASIC, HELP WP információ kérést.) Ezalatt a megszakításkezelőnk a következőket végzi. Szelektálja a D-ben átadott forrást és meghívja a megfelelő megszakításkezelő alrutint. Másodpercenként felcseréli a videólapunk papír és tintaszínét. 50 Hz-el frissíti a programozható megszakításunk alapértékeit, kezdőértéket ad a keretszínnek, majd véletlenszerűen módosítja a karakterkészlet szóköz definícióját 9 bájton. A programozható megszakításunkat 352 Hz-re adtuk meg, ilyenkor történik a keretszín növelése. Itt korlátot jelent az EXOS megszakítás kiszolgáló sebessége, ezért magasabb frekvenciákat csak az EXOS kihagyásával lehet alkalmazni. (pl. EPDOS 2.1 háttérnyomtatása 1 KHz-el)

Kiegészítés a megszakítás kezelés című cikkhez:
A Z80-nak két megszakítás kérő bemenete van. A nem maszkolható megszakítás NMI, prioritásban erősebb a maszkolható megszakításnál INT. Létezik még CPU_RESET bemenet is. A Z80 a megszakításkezeléshez két tárolót is használ. IFF=1 engedélyezi a maszkolható megszakítást, míg IFF2 csak adatmentésre alkalmazott.
Az elfogadott NMI (CALL) a 66H címet hívja meg. Előtte a Z80 IFF1 megszakítás engedélyező regiszterét nullázza, vagyis tiltja a maszkolható megszakítást. Az elmentett IFF2 tesztelhető is. Értéke megjelenik az LD A,I vagy LD A,R utasítások F-státuszában a paritásjelző biten. (P\V) Az NMI visszatérésére a RET helyett RETN utasítás használható, mely helyreállítja az IFF1et. Az NMI-megszakítást az ENTERPRISE gépben nem használják.
Az INT megszakítás 3 féle lehet, melyek beállítására az IM1, IM2, IM3 utasítás szolgál. A RESET a nullás módot állítja be. Az elfogadás törli az IFF1-IFF2-t.
MODE 0: Az adatsínről olvassa be a végrehajtandó utasítást (többnyire RST) vagy az esetleges hosszabb hívást (pl. CALL bájtbájt)
MODE 1: Automatikusan végrehajtja az RST 38H utasítást.
MODE 2: Az adatsínről beolvassa a megszakításvektor alacsony bájtját (páros érték). A magas bájtot az l-regiszter adja, melynek értékét az előzetesen kiadott LD I,A utasítás állította be. Erről a címről beolvasott 16 bites érték adja a megszakítás-kezelő tényleges címét, melyet CALL utasítással fog meghívni. A visszatérés RETI utasítással történik. A kiegészítő áramkörök ennek kódját közvetlenül az adatsínen felismerik és oldják a prioritási láncot.
Az ENTERPRISE az IM1-et használja. Az IM2 nem alkalmas erre a gépre, mivel hiányoznak a kiszolgáló áramkörök. A beolvasott megszakításvektor többnyire FF, így I=00 esetén a 00FF címről kapná a megszakításkezelő címet.

HSOFT - Enterpress 1995/3-4

Az Enterprise memórialapjainak kezelése
Az EP-ben Z80-as processzor dobog, mely a memóriát 16 biten címzi meg ami csak 64 kilobyte tartományt eredményez. A Z80 illesztését a DAVE-vel oldották meg, mely a következő feladatokat végzi:

Az EP 22 bites tárat tud kezelni, ami 4 Megabyte tartománynak felel meg. A címzés a Z80 és a DAVE tartalmától függ. Az alsó 14 bitet a Z80, a felső 8-at pedig a DAVE generálja az alábbi módon. 4 írható-olvasható lapregisztere van, melyben 8 bites értékeket tárol. A Z80 2 felső bitje megcímzi a lapregiszterek egyikét és a benne tárolt értéket küldi ki helyette.

Az ábrából kitűnik, hogy az EP 64 K-s Z80-ja 4 Megát tud címezni. Ehhez viszont időnként módosítani kell a DAVE lapregiszter tartalmát. Ezt lapozásnak nevezzük.
Miért kell lapozni? Mert a memória meghaladja a Z80 által kezelhető 64 kilobyte-ot. Amikor egy könyvet olvasunk, 2 oldalt láthatunk egy időben. A többit lapozással érhetjük el. Az EP-nek 4 lapja van, egyenként 14 bites, azaz 16 kilobyte memória kapacitással. Bármely lapra, bármely szegmens belapozható, így akár mind a 4 lapra belapozható ugyanaz a szegmens. A szegmensek száma 256, ebből a valóságban keveset használunk. A gép bővítésével számuk növelhető. A memória 2 féle lehet. Csak olvasható ROM, írható és olvasható RAM. A ROM-memória kikapcsolás után is megőrzi a tartalmát. Hiányában elképzelhetetlen a hidegindítás. A ROM-okon nem csak az operációs rendszer rezidens részét tárolhatjuk, hanem tetszőleges bővítéseket, programokat és adatokat.
Egyszerűbb vezérlési rendszerek akár csak ROM kiépítésű memóriával is működhetnek. A bonyolultabb feladatokhoz, ilyen pl. a képernyő kezelés, már elengedhetetlen a RAM memória. Az EP operációs rendszere szintén megköveteli a minimum 2 szegmensnyi RAM-ot. Mit, mikor, hova lapozzunk? Az alapkiépítésű EP a következő memóriaszegmenseket tartalmazza:

A NICK videó chip a Z80-tól független módon kezeli a képernyőt, és a 64 K-s videó RAM-ot a NICK prioritásban elsődlegesen tudja olvasni. A címei úgy alakulnak, mintha FC FD FE FF szegmenseket lapoznánk be a Z80-nak. A valóságban ilyen belapozással nem futtatunk programot, ezért a videó RAM egy-egy szegmensét belapozzuk valamelyik lapra és átszámoljuk a NICK címét Z80 címre, vagyis a 16 bites érték 2 felső bitjét módosítjuk. Az EP hidegindítás és RESET utáni környezete: A NICK 4 regiszterének értéke 0. Ezek a FIXBIAS, BORDER, LPL, LPH. A DAVE 16 hang regisztere, 4 lapregisztere, 1 megszakítás regisztere, 3 I/O regisztere, 1 memória elérést szabályzó regisztere szintén 0. A Z80 PC-regisztere szintén 0. Ebből kiindulva tehát mind a 4 lapon a nullás szegmens látható. A Z80 az induló kódot tehát a nullás lap 0000 címéről olvassa, melyen a nullás ROM szegmens van belapozva. A megszakítás tiltott állapotban van. IM 1-re kapcsol, majd elugrik a 3. lapra ahol szintén a nullás romszegmens látható. A 4. szegmenset a nullás lapra lapozva ellenőrzi a "TEST_ROM" kezdést. Ha ilyet talál, átadja neki a vezérlést 0008 címen. Így működnek a gyorstesztes bővítések. Tovább futva ellenőrzi a melegindítási mód lehetőségét. Ez annyit jelent, hogy némi inicializálás után visszaadja a vezérlést az aktuálisan futó felhasználói programnak. Tovább futva megkezdődik a hidegindítás. RAM teszt, illetve a RAM szegmensek listába vétele.
Ekkor már kialakul az operációs rendszer lapkiosztása. A nulladik lapon a legalacsonyabb értékű RAM-szegmens található, melynek értéke alap gép esetén F8. Ide másolódik 0030-005A közötti területre az operációs rendszer nulláslapú rutinjai. Az RST 30 az EXOS funkcióhívásokat, az RST 38 pedig a megszakításokat szolgálják. Ezt a nulláslapot tehát nem szabad soha elállítani, kivéve úgy, hogy tiltott megszakítás mellett ellapozzuk majd helyreállítjuk! Az első lap tartalma virtuális, ez a munkaszegmens. A második lapra az FF RAM-szegmens, vagyis a rendszerszegmens kerül. Itt van kiépítve az operációs rendszer veremtára, tehát ki lapozás alatt gondoskodni kell arról, hogy ne történjen megszakítás, illetve ne legyen vermet igénylő művelet. A harmadik lapon a nullás ROM-szegmens marad.
A továbbiakban történik a ROM-szegmensek könyvtárba vétele, ROM-ok RAM igényének kiszolgálása, alapperifériák könyvtárba vétele és inicializálása, majd a ROM-bővítők inicializálása. Ezt követi a bejelentkezés, mely le is tiltható. Utána a hidegindítás akciókóddal megtörténik a bővítők letapogatása. Amikor egyetlen bővítő sem igényli, akkor a 01-es ROM szegmens elfogadja és elindítja a WP felhasználói programot.
Az operációs rendszer alatt két féle program futtatható: Felhasználói és rendszerbővítő. Mindkét program létrehozhat felhasználói periféria bővítést, mely leegyszerűsítve harmadik lapon futó rutinkönyvtár, belépési táblázattal.
Az EP alatt minden esetben egyetlen felhasználói program futhat. E programból EXOS-hívás vagy megszakítás által meghívható az operációs rendszer. Az operációs rendszer tovább hívhatja a bővítőket és perifériákat. Ebből következik, hogy legnagyobb szabadsággal a felhasználói program rendelkezik. A nullás laptól eltekintve a szegmenseket szabadon lapozhatja. Veremtárát a harmadik lap kivételével bárhol kialakíthatja. Felhasználói RAM memóriát igényelhet vagy szabadíthat fel az operációs rendszeren keresztül. Amikor egy másik felhasználói program veszi át az irányítást, akkor az operációs rendszer feladata a memória, perifériák és bővítések inicializálása. Felhasználói futást kétféleképpen lehet kialakítani. Fájl-típusú vagy bővítésből indított. Fájl-típus: A fejléc 00, 05-tel kezdődik. Ezt követi a két bájtos betöltési méret. A rendszer betöltési rutinja nem teljesen korrekt. 0100-7FFF-ig hibátlan. 0100-BFFF-ig töltve a szegmenseket nem lapozza be, tehát ki kell talál ni. Ezért azt javaslom, hogy a 7FFF-et ne haladja meg az 5-ös fejlécű programfájl. A további adatokat a program is beolvashatja egy másik fájlról. A betöltő 5-ös fejlécű programnál felszabadítja a felhasználó által foglalt memóriát, lefoglalja a betöltéshez szükséges memóriát, majd végrehajtja a betöltést. Töltési hiba esetén hibaüzenetet küld az alapértelmezésű csatornára, majd hidegindítás akciókóddal meghívja a bővítőket. Hibátlan töltésnél inicializálja a perifériákat és bővítőket, felhasználói futásra kapcsolja a rendszert és 0100-tól elindítja a programot. Újabb hibája hogy a megszakítást nem tiltja, pedig a felhasználói program még nem építette ki a veremtárát. Első lépés tehát az LD SP,? vagy a DI programsor legyen!
Kis veremigényű programoknál bevált szokás az LD SP, 100H utasítás.
A lapok kiosztása ebben az esetben:

Bővítőből indított felhasználói futás: hidegindítás akciókód elfogadása esetén a teendők.

LD C,0
EXOS 0
LD SP,?
EI

Parancs sztringgel indított felhasználói futásnál a felismerés utáni ág:

LD C,60H
EXOS 0
LD SP,?
EI

A felhasználói program lépései a továbbiakban:

A felhasználói program nem fejezheti be a futását mint egy bővítő rutin.
Kilépési lehetőségek:

A bővítő rutinja visszatérés előtt a nullás és második lapot állítsa helyre. Vigyázz a lapozásoknál mivel:

Javasolt könyv:
az EXOS 2.1 Változat Műszaki Leírása

Haluska László
(Az 1999-es Enterpress News tervezett 1. számából)

EXOS kompatibilis memóriakezelés
Az ENTERPRISE a maga korában, sőt talán a 8 bites gépek történetében nézve összességében is egyedülálló módon rugalmasan bővíthetőre lett megtervezve, így például a maximálisan kezelhető memória méret az akkoriban szinte elképzelhetetlen 4 megabájtban lett meghatározva. Mindez ráadásul nagyon tisztán, áttekinthetően lett megoldva: a Z80 64K-s címtartományát felosztjuk 4 db 16K-s lapra, a 4 megabájtot pedig 256 db 16K-s szegmensre. És bármelyik szegmenst belapozhatjuk bármelyik lapra, sőt még azt is megtudhatjuk, hogy melyik lapra melyik szegmens lett belapozva.
Sok más gépen csak irigykedni lehet egy ilyen egyszerűen használható rendszerre, majd egyszer - a Spectrum programok átírása kapcsán - részletezem, hogy milyen "barkácsolással" lett a Spectrum 48-ból 128-as gép, ill. milyen további ronda barkácsolásokkal érték el az oroszok a 256K esetleg 1 mega memória használatát... De a szocialista kistestvér, azaz a TVC esetén is jelentősen lebutítva és egyben bonyolítva lett a rendszer, csak, hogy pár logikai kaput megspóroljanak...
Persze a hardware lehetőség kevés, kell mindehhez egy olyan operációs rendszer, ami mindezt tudja kezelni, kihasználni. Szerencsére mi egy igazán remek rendszert kaptunk, amely teljességgel rászolgált az Enterprise eXtendable Operating System névre! Az EXOS feladata az aktuális konfiguráció felderítése, a használható memória leltárba vétele, majd pedig a felhasználói programok ill. különböző rendszer összetevők által támasztott memória igények kiszolgálása, nyilvántartás vezetése az aktuálisan foglalt ill. szabad memória területekről. Akárcsak egy mai modern operációs rendszer esetén!
A kortárs gépeken nem is igazán lehet operációs rendszerről beszélni, memóriakezelés is gyakorlatilag csak a BASIC interpreteren belül létezik, változók stb. nyilvántartása. Így például egy Spectrum program se tudhatja meg rendszerszinten, hogy 48-as, 128-as vagy netán egy orosz Scorpion 256-os gépen fut. Itt ha nem érdekel minket a BASIC, gyakorlatilag nincs semmi más amire figyelni kellene, ráadásul minden fix, és változatlan, a képernyő-memória is teljesen rögzített helyen van, semmi akadálya a "POKE-PEEK" stílusú programozásnak.
Enterprise esetén egyrészt alapból is több többé-kevésbé különböző géptípus került kiadásra (64, 128, EXOS 2.0 és 2.1, angol és német), másrészt a különböző bővítésekkel számtalanra növekedett a különböző létező konfigurációk száma.
Sajnos a cég sikertelensége azzal is járt, hogy nem volt hivatalos támogatás, oktatás, cikkek, könyvek, arra vonatkozóan, hogy ezt a kitűnő rendszert hogyan is kell helyesen kihasználni, hogyan kell megfelelően programozni. Így a géppel foglalkozó programozók leginkább a más gépeken megszokott fix környezetet használó stílust vették át, az elérhető dokumentációkból csak annyit hasznosítva, hogy egyes fix(nek) gondolt pontokat megkeressék. Ez nálunk különösen jellemző volt, egyrészt mivel a legjobban eltérő EP64-es nálunk nem került forgalomba, másrészt a hivatalos dokumentációk is csak késve és nem túl jó minőségben fordítva jelentek meg. Ráadásul a megjelent könyvek, cikkek nagy része is ezt a fix memória konfigurációs "POKE-PEEK" stílust hirdette.
Milyen problémák adódnak ebből? Anno a hírhedt angol-német gépek közötti inkompatibilitás, mind ennek volt köszönhető. Ez különösen a BASIC-gépi kód keverék programokra volt jellemző. Ennek köszönhető sok összevissza barkácsolt, kapcsolóval megspékelt cartridge...

Ezek ugyan már át lettek bütykölve, de a dolog most fokozottan megismétlődik a winchester vezérlővel, hiszen itt még több terület lesz lefoglalva a rendszerszegmensben, és itt különösen nem egészséges, ha beleírunk a lefoglalt memóriába. Ennek akár nagyobb adatvesztés is lehet a vége!
A "Világ EP tulajdonosai egyesüljetek" mozgalmunk keretében került előtérbe az a probléma, hogy a létező EP programok kb. 98%-a nem fut EP64-en, annak ellenére, hogy jelentős részük fizikailag beférne a 64K-ba! És hiába vesz mondjuk egy MICROTEAM kártyát az EP64 tulajdonos, az így 576K-ra növelt memóriával se fog futni a 128-as programoknak még mindig vagy 96%-a... mivel az F8-FB szegmensek nem léteznek egy ilyen gépen. (Sajnos az ep128emu egyik hiányossága, hogy ilyen "lukas" memória konfigurációkat nem lehet emulálni, ezt csak EP32-ben lehet elkészíteni.)
F8-FB szegmensek megvalósítása után még mindig sok programnak gondot okozna az EXOS 2.0. És amire nem került sor, de hatalmas balhé lett volna: ha kijött volna a tervezett szuper EP az EXOS 3.0-val, várhatóan szintén rengeteg program nem működött volna vele...
Szintén lettek volna problémák, ha anno kiadják a winchester vezérlőt (amit az eladatlan SZJA 88 készletek miatt visszatartottak...)

Akkor menjünk bele a részletekbe, először egy kis összefoglaló:
Van 4MB címtartományunk, ez fel van osztva 256 db 16K-s szegmensre. Meglepő módon 0-255-ig számozzuk őket, hexában 00-FF. Alapvetően bármelyik lehet RAM vagy ROM vagy maradhat üresen, kivéve amit maga az alaplap határoz meg:

Ha a RAM-ot nézzük, egy 64K-s gépben van FC-FF. 128-asban F8-FF. Ha a korábban emlegetett MICROTEAM + EP64 konfigot nézzük, akkor pedig 40-5F, FC-FF, ami darabra bőven jó, csak hiányzik az a bizonyos F8-FB, amire az EXOS-t nem használó 128-as gépen programozók által írt programok nagy része hivatkozik közvetlenül. A helyes programozáshoz felejtsük is el ezeket a számokat, egyedül az FC-FF-et kell megjegyezni, kitüntetett videó memória mivoltuk miatt. A többiről csak annyit kell tudnunk, hogy nekünk hány darabra van szükségünk, a konkrét szegmensszámokat majd megmondja az EXOS!
Most nézzük az EXOS szerinti RAM felosztást: két szegmensnek van kitüntetett szerepe, az egyik az FF ami a rendszerszegmens, és a legalacsonyabb sorszámú RAM szegmens ami a nulláslap szegmens. Itt található meg az EXOS hívások, illetve a megszakítási program belépési pontja. És ide kerülnek 100H címtől töltve az 5-ös fejlécű programok is. Egy 128-as gépen az az F8 szegmens. De ha pl. van egy MICROTEAM kártyánk, akkor már a 40-es lesz az. És ez máris gondot okoz sok programnak (általában a komplett módosított Spectrum ROM-ot tartalmazó béna átiratoknak)... De bővítős gépen is lehet F8 a nulláslap, ha VENUS-t használunk, ill. az EPDOS 2.1-nek is van ilyen lehetősége. Ez az eset meg egy másik adag programnak okoz gondot...
A maradék RAM négy csoportba tartozhat: rendszer, eszköz, felhasználói, szabad. Ha pl. csatornákat nyitunk meg, különösen, ha nagy RAM igénnyel járó videó lapokat, akkor az EXOS elkezd lefelé terjeszkedni, és ha kihízza az FF szegmenst, akkor további szegmensek válhatnak rendszer által lefoglalttá.
A különböző beláncolt EXOS periféria kezelők által igényelt teljes RAM szegmensek az eszköz (device) kategóriában kerülnek lefoglalásra. Erre tipikus példa a RAMDISK. Szintén ebben a kategóriába kerülnek lefoglalásra a betöltött rendszerbővítők által elfoglalt szegmensek is.
És végül van az aktív felhasználói program, ez lehet egy rendszerbővítő vagy egy 5-ös fejléccel betöltött "új alkalmazói program". Az ezek által igényelt szegmensek a felhasználói kategóriában kerülnek lefoglalásra. Ami nagyon fontos: a felhasználói program csak felhasználói szegmenst tud felszabadítani, eszköz vagy rendszer szegmens felszabadításához nincs joga!

És ezzel el is érkeztünk ahhoz a bizonyos Spectrum Világ-os hibás módszerhez:

  LD C,0FAH
EXOS 25
LD A,0FAH
OUT (0B2H),A

Egyrészt a szándék dicséretes, hiszen nem csak bumm belapozza a szegmenst, hanem szabaddá teszi... csak sajnos a nem túl sikeres fordítású EXOS leírást félreértelmezve. Mert az EXOS 25 hívás csak akkor lesz sikeres, ha előtte az a szegmens nekünk, azaz felhasználóiként ki lett utalva. Ha az FAH már mondjuk a RAMDISK része, vagy egy betöltött EXOS bővítő van ott, akkor mivel eszköz szegmensnek van lefoglalva, hibajelzést kapunk vissza, hogy a szegmens nem szabadítható fel. A program viszont ennek ellenére nyugodtan neki áll használni. És ott van még az a eset is, hogy nem is létezik ez a szegmens az adott konfigurációban, mert pl. EP64-ről van szó...
A helyes megoldás itt az, hogy kérünk kiutalni egy szegmenst az EXOS-tól, és azt használjuk. Ha hibajelzést kapunk vissza, mert elfogyott a szabad memória, akkor azt a programtól függő módon le kell kezelni (pl. kilépés, vagy lehet folytatni korlátozott funkcionalitással a program futását).

  EXOS 24
JP NZ,HIBA
LD A,C
OUT (0B2H),A

Kicsit macerásabb, ha egy konkrét számú szegmensre vágyunk: ekkor ciklusban kell ismételgetni az EXOS 24 hívást, mindaddig, amíg meg nem kapjuk a kívánt szegmenst, vagy pedig elfogy a memória, és ekkor megyünk a HIBA rutinra. De erre a módszerre új program írása esetén csak egy esetben lehet szükség: ha videó szegmensre van szükség. Ekkor addig ismételjük a hívást ameddig FC vagy nagyobb szegmenst nem kapunk.
És akkor kell még ehhez a módszerhez folyamodni, ha egy régebbi fix címzéses programot akarunk kibővíteni úgy, hogy amit a program használ, az legyen szabályosan lefoglalva, mint ahogy ezt tettem a TUSKER-rel. Ez volt Attus eredeti rutinja, amely az SpV módszeren alapult:


SZABAD
LD BC,5FAH
PUSH BC
EXOS 25
POP BC
INC C
DJNZ SZABAD

Ebből kiderül, hogy az FA-tól kell nekünk 5 szegmens, illetve később kiderült, hogy az LPT táblát az FF szegmens elejére helyezi el.
És íme mindez helyesen:

FOGLAL




EZKELL















VISSZAAD
EXOS 24
JP NZ,HIBA
LA A,C
CP 0F9H
JR NZ,FOGLAL
LD BC,5FAH
PUSH BC
EXOS 24
LD A,C
POP BC
CP C
JP NZ,HIBA
INC C
DJNZ EZKELL
EXOS 24
CP 7FH
JP NZ,HIBA
LD DE,3200
EXOS 23
JP NZ,HIBA
LD L,0F9H
LD C,L
EXOS 25
DEC L
JR NZ,VISSZAAD

Az elején addig foglalunk, amíg az F9-ig el nem jutunk. Ha közben kifogyunk a memóriából, akkor ugrás a HIBA-ra. Ezután kérünk még 5 szegmenst, aminek FA,FB,FC,FD,FE-nek kell lennie, különben HIBA... És még egyet igényelünk, itt már csak az FF jöhet, és "megosztott szegmens" hibajelzést kell kapnunk (ez a 7F hibakód), különben hiba.
És itt jön még egy fontos dolog: megosztott szegmens használata esetén meg kell mondanunk az EXOS-nak, hogy mi meddig akarunk terjeszkedni, erre szolgál az EXOS 23: felhasználói határ beállítása. Ha itt hibajelzést kapunk, akkor nincs annyi szabad hely amennyire nekünk szükségünk van, tehát ugrás a HIBA-ra.
Ha minden ok, akkor egy nem túl szép, nem túl gyors, de egyszerű módszerrel visszaadjuk a felesleges szegmenseket. Vagyis F9-től lefelé mindet megpróbáljuk felszabadítani, úgyis csak az fog sikerülni amit felhasználónak utaltak ki. A teljesen korrekt megoldás természetesen az, ha az elején eltároljuk sorban a kapott felesleges szegmenseket, és ezen lista alapján szabadítunk itt fel. Erre akkor van szükség, ha az 5-ös fejlécű programunk már nem fér el a nulláslapon, mert ekkor a további programszegmensek is felhasználóiként kerülnek lefoglalásra, és ezzel a primitív módszerrel azokat is felszabadítanánk, pedig azt
nem lenne szabad. A TUSKER betöltő esetén nincs ilyen gond, jó így is.

Jogosan merülhet fel a kérdés: Konkrét szegmens igénylésére nincsen valami elegánsabb módszer? Mert így szélsőséges esetben akár 249 szegmenst is lefoglalhatunk magunknak, míg végre megkapjuk pl. a hőn áhított FC (video) szegmensünket. És akkor ezután még fel kell szabadítani a fölöslegesen lefoglalt szegmenseket is, hátha kellenek még azok valamire...
Sajnos nincs... Ez egy pici hiányosság az EXOS-ban. Jó lenne ha külön lehetne jelezni, hogy videó szegmenst szeretnénk kérni. Erre egyedül a periféria kezelő programon keresztül van lehetőség, ha videó periféria típust állítunk be. Így jobb híján marad ez a ciklusos megoldás. Lehetséges még közvetlenül az EXOS rendszerterületeiben matatni, ezt több könyvben, cikkben tárgyalták is. De az ilyen programok máris elszállnak EXOS 2.0 esetén... esetleg meg lehetne írni külön két szubrutint EXOS 2.0 ill. 2.1 esetére. De mi van ha"véletlenül" készül újabb EXOS verzió? Azon megint csak nem fognak futni az ilyen "kotorászós" programok... Szóval szerintem jobb maradni a szabályos megoldásnál, az a pár tizedmásodperc futásidő amibe legrosszabb esetén kerülhet, nem egy nagy veszteség a kompatibilitás oltárán.
Ez volt a fix szegmensek használata szabályosan című fejezet.

Ott tartunk, hogy szeretnénk megszabadulni a fix szegmensszámoktól, ily módon a különböző konfigurációkhoz maximálisan alkalmazkodó programot írni. Nézzük mi a teendő ha memóriára van szükségünk?
Kezdetnek ott van a korábban emlegetett nulláslap. Ebből az EXOS a 0030-005BH területet használja, ill. az 5-ös fejlécű program ide kerül betöltésre 0100H címtől. A többi szabadon felhasználható az éppen aktív felhasználói program számára. Mint korábban már említettem, a nulláslap szegmensszáma különböző lehet a különböző konfigurációkban, az aktuális számot megtudhatjuk a B0H portról beolvasva, ill. a BFFCH címen lévő EXOS rendszerváltozó is tartalmazza (a rendszerváltózók címe úgy érvényes, ha az FFH rendszerszegmens a 2. lapra van lapozva). Ez később még fontos lesz, amikor majd kicsit trükközünk a nulláslappal), de alapesetben nem szokás piszkálni.

Nézzük tovább, mi van ha a nulláslap már nem elég, vagy nem felel meg az igényeinknek?
Nagyon egyszerű, kérünk további szegmenseket az EXOS-tól. Mivel már nem ragaszkodunk a fix szegmensszámokhoz, sokkal egyszerűbb a dolog, minden amit kapunk, az jó nekünk! Célszerű a kapott szegmensek számát eltárolni, későbbi lapozási műveletekhez nem árt tudni melyik szegmensekről van szó, ill. majd kilépéskor fel kell szabadítanunk ezeket.
Egy lehetséges módszer, például foglaljunk le 4 szegmenst:



FOGLAL
LD HL,RAMLISTA
LD A,4
EX AF,AF'
EXOS 24
JZ NZ,HIBA
LD (HL),C
INC HL
EX AF,AF'
DEC A
JR NZ,FOGLAL

Később pedig pl a 3. szegmensre így hivatkozhatunk:

  LD A,(RAMLISTA+2)
OUT (0B3H),A

Itt megemlítem azt is, hogy a Spectrum Világ cikksorozatának az elején, amikor a Spectrum kazetta beolvasó programot tárgyalják, még a helyes memóriakezelésről van szó:
"EXOS 24 - Szegmens kijelölés. Memóriát célszerű az EXOS-tól kérni, mivel így biztosan nem kezd el más program is az általunk használni kívánt memóriában dolgozni. C regiszterben a kiutalt szegmens száma lesz. Programunk 3 db szegmenst foglal le, ez 48k, így minden SPECTRUM program belefér a tárba".

  EXOS 24

LD A,C
OUT (0B1H),A
EXOS 24
LD A,C
OUT (0B2H),A
EXOS 24
OUT (0B3H),A
; Egy 16 kbyte méretű RAM szegmens
; lefoglalása
; és belapozása
; az 1. lapra (4000H-7FFFH címtartomány)
; Még két szegmens lefoglalása
; és belapozása.
; Ezekre fog töltődni a program.

Kimaradt a hibakezelés, így kevés memória esetén, hiába szól az EXOS, hogy elfogyott, a program nem veszi észre, és hibásan fog működni.
Ezzel a sima szegmens igényléses módszerrel már remekül el lehet lenni, ha nincs szükségünk képernyő kezelésre, ill. ha megoldjuk az EXOS videó kezelőjén keresztül. Ha viszont magunk akarjuk a képet létrehozni, saját LPT táblával, ahhoz saját videó szegmens(ek)re lesz szükségünk! Itt kezdődnek a bonyodalmak...
Mi is a bonyodalom a videó szegmensekkel? Az egyik már felmerült kérdésként: nem tudunk közvetlenül videószegmenst igényelni, így kénytelenek vagyunk a már megismert ciklusos igényelgetéses módszerhez folyamodni.

"míg végre megkapjuk pl. a hőn áhított FC szegmensünket"
Ez viszont megint helytelen hozzáállás. Mert áhítozhatunk rá, de EP64-en nem fogjuk megkapni, mivel az csücsül a nulláslapon. Tehát a helyes hozzáállás az, hogy FC vagy nagyobb ami elfogadható. Jól eltesszük ennek is a számát, hol itt a gond? Az, hogy lesz itt még egy kis pluszmunkánk!
Általános célú szegmenseknél teljesen mindegy, hogy melyiket is kaptuk meg. Videó szegmens esetén ez már nem teljesen igaz! A miérthez nézzük meg a Nick chip memória kezelését, amiről eddig még nem volt szó: Azt már tudjuk, hogy az alaplapi 64K egyben a videómemória is. Ezt a Nick saját maga is címzi, az általa használ címeket nevezzük videócímeknek. Természetesen ezek is 0000-FFFFH tartományban lehetnek. De ezek a címek teljesen függetlenek a Z80 címeitől! A 0000-3FFFH tartomány jelenti az FCH szegmenst, 4000-7FFFH az FDH, 8000-BFFFH az FEH, C000-FFFFH az FFH. És ezek függetlenek attól, hogy a kérdéses szegmensek a Z80 számára hova vannak lapozva, vagy hogy egyáltalán be vannak-e lapozva.
Így amikor a videómemóriával végzünk műveleteket, szükséges lehet azt a címet amivel a Z80 érte el a kérdéses területet átszámolni videócímre a Nick számára. És van még egy számolgatás, amire akkor van szükség amikor az LPT tábla címét adjuk meg a Nick chipnek, mivel itt csak 12 bitet adunk át. Ezt legegyszerűbben úgy lehet megspórolni, ha a 0000H videócímre tesszük az LPT táblát, vagyis az FC szegmens elejére, ahogy teszi ezt pl. az SpV féle Spectrum átírat betöltő is. Az LPT táblában pedig meg kell adnunk a képernyőadatok videócímét is. Az egyszerűség kedvéért az említett betöltő erre a célra az FD szegmenst használja, így 4000H videócímtől fog kezdődni a képernyő, ezt a kezdőcímet használja fixen az LPT generáló rutin. Nekünk viszont pont az a célunk, hogy a fix szegmensz számoktól megszabaduljunk!
Az említett Spectrum képernyő előállításához két videószegmensre lesz szükségünk, egy kell az LPT táblának, egy pedig a Spectrum képernyőnek, ami egyben a Spectrumos memória első 16K-ja is lesz. Mivel az LPT tábla nem tölt ki egy teljes szegmenst (szokásos Spectrum LPT tábla 3200 bájt), így ezt akár megosztott szegmensben is el lehet helyezni. Ezért a célszerű folyamat az, hogy az ismert módszer szerint addig igényelgetünk szegmenseket, amíg egy videószegmenst nem kapunk, ezt eltesszük képernyőnek, a következőt pedig LPT-nek. Ha fordítva csinálnánk, akkor előfordulhatna, hogy a nem teljes szegmensnyi LPT tábla kap egy teljes szegmenst, a teljes szegmenst igénylő videómemória pedig lehet, hogy márt csak megosztottat kap, ami hibához vezet...
128-as gépen ezzel pont fordított konfguráció jön létre, mint az SpV betöltőben: az FCH lesz a videószegmens, míg az FDH az LPT szegmens. Ezzel az eredeti Spectrum LPT építő rutin máris működésképtelenné vált, módosítani kell.

Hogyan számoljuk a videócímeket:
Kelleni fog a Z80-as cím, ill. a szegmensszám. A Z80-as címből a legfelső két bitet "dobjuk ki", és helyére a szegmensszám alsó két bitjét kell betenni. Amikor pedig az LPT címét mondjuk meg a Nick-nek, akkor az alsó 4 bitet kell "eldobni".

Folytassuk cikkünket gyakorlatiasabb részekkel!
Én ezt a rutint használom a Spectrum átirat betöltőimben videó szegmens igénylésre:

VID

KER



NAMIVAN






NEMKER


VISSZA
LD HL,VEGE
LD (HL),0
EXOS 24
JR Z,NAMIVAN
CP 7FH
JP NZ,HIBA
EX AF,AF'
LD A,C
CP 0FCH
JR NC,NEMKER
INC HL
LD (HL),C
JR KER
EX AF,AF'
PUSH BC
PUSH AF
LD C,(HL)
EXOS 25
DEC HL
JR Z,VISSZA
POP AF
POP BC
OR A
RET

A rutin a VEGE címtől (amely logikusan a programkód után helyezkedik el) tárolja el a felesleges szegmenseket. Miután akadt egy (amely lehet megosztott is), a felesleget visszaadja az EXOS-nak. Először a képernyőnek kérünk egy teljes szegmenst:

  CALL VID
JP NZ,HIBA
LD A,C
CP 255
JP Z,HIBA
LD (VIDS),A
LD DE,0
RRA
RR D
RRA
RR D
LD (VIDCIM1),DE

A kapott szegmensszámból kiszámoljuk a terület kezdetének címét, erre később az LPT generáláskor szükség lesz.
Később kérünk egy LPT szegmenst is, ami lehet megosztott is, ha elfér benne az LPT tábla (azaz az EXOS határ beállítható a megfelelő helyre.)









NEMHIBA

NEMHIBA1
CALL VID
JR Z,NEMHIBA
CP 7FH
JP NZ,HIBA
LD DE,200*16
EXOS 23
JP NZ,HIBA
LD C,255
LD A,C
LD (LPTS),A
LD DE,(VIDCIM2)
LD HL,0
RRA
RR H
RRA
RR H
ADD HL,DE
LD (VIDCIM2),HL

Itt is kiszámoljuk a videó címet, ezúttal az LPT tábla kezdetét. A figyelmes szemlélő észreveheti, hogy itt még játékba áll a VIDCIM2-n korábban lévő érték. Ez alapban nulla, viszont 64K-s gépen úgy fog átrendeződni a memória, hogy nem a szegmens elejére kerül az LPT tábla, ekkor a szükséges eltolás mértéke került már ekkora a VIDCIM2-re.
Ezekután nézzük meg, hogyan változik a SpV-ban is közölt LPT generáló rutin! Íme az eredeti rutin kezdete:

  LD A,0FCH
OUT (0B1H),A
LD A,192
LD DE,4000H
EXX
LD DE,4000H
LD HL,4004H
LD BC,13

És ez lesz belőle:

  LD A,(LPTS)
OUT (0B1H),A
LD A,192
LD DE,(VIDCIM2)
RES 7,D
SET 6,D
EXX
LD DE,(VIDCIM1)
LD IX,VIDCIM1
LD HL,(VIDCIM2)
RES 7,H
SET 6,H
INC HL
INC HL
INC HL
INC HL
LD BC,13

A fix szegmensszám helyett az eltároltat használjuk. A DE fog mutatni az LPT elejére az 1-es lapon, de a korábban említett esetleges eltolás miatt ezt az LPT videócíméből számoljuk vissza 1-es lapi címre. Az EXX utáni DE a videómemóriánk kezdetére fog mutatni, ez az eredeti rutinban fix 4000H volt, mivel az FDH volt fixen erre a célra használva. A HL pedig szintén az LPT területre fog mutatni, 4 bájt eltolással, ő az LPT sorokba való címbeírásnál van használva.
Nem esett még szó az IX-ről. Ő a videó memóriánk címét tároló változóra mutat. Azért mert, még egy helyen hozzá kellett nyúlni az eredeti rutinhoz, még pedig az attributum címek kiszámolásánál:

  LD A,D
RRA
RRA
RRA
AND 3
OR 58H
LD (HL),A

Módosítva:

  LD A,D
RRA
RRA
RRA
AND 3
OR 18H
OR (IX+1)
LD (HL),A

Ezekután már csak az LPT aktiváló OUT utasításokat kell módosítani, az eredeti nagyon egyszerű, mivel a fix 0000H videó címet használja:

  XOR A
OUT (82H),A
LD A,192
OUT (83H),A

És módosítva, hogy a kiszámolt LPT címünket használja:

  XOR A
LD HL,VIDCIM2+1
RRD
RLCA
RLCA
RLCA
RLCA
OUT (82H),A
OR 0C0H
RRD
OUT (83H),A

Megjegyzés, ez a módszer elrontja a VIDCIM2 változó értékét, de erre az adott esetben úgysincs többé szükség. De itt egy másik módszer is, itt az eredeti EXOS LPT visszaállítására van használva:




FORG4
LD HL,(0BFF4H)
SET 6,H
LD B,4
SRL H
RR L
DJNZ FORG4
LD A,L
OUT (82H),A
LD A,H
OR 0C0H
OUT (83H),A

A címet az EXOS LP_POINTER változójából olvassuk ki (tehát legyen belapozva a rendszerszegmens a 2. lapra), az utána következo SET 6 azért van, hogy videócímre konvertáljuk az EXOS változót, ha saját kiszámolt videócímünket használjuk, akkor erre nincs szükség.

ZozoSoft

Vissza