Monday, 27 April 2020

Hangkeltés a régi számítógépek basicjében

Régi számítógépeken zenét programozni legegyszerűbben a beépített basic programozási nyelven lehet. A hangchip négyszögjelét a legtöbb számítógépen a SOUND utasítással szólaltathatjuk meg, egyes gépeken a SOUND mellett vagy helyett más utasítás is rendelkezésre áll.

A SOUND utasítás

A SOUND utasításnak megadhatunk paramétereket. Így állíthatjuk be elsősorban a hang magasságát, hosszát, egyes számítógépeken a hang más tulajdonságait is, pl. hangerejét. Néhány gépen az egyes paramétereknek külön nevük is van, ilyenkor a paraméterek sorrendje tetszőleges. Más gépeken nincs külön nevük a paramétereknek, ilyenkor a paraméterek sorrendje kötött, az adott sorszámú paraméterrel lehet az adott hangtulajdonságot állítani.

Enterprise-on már a korábbi Enterpress számokból ismerhetjük a SOUND utasítás rejtelmeit. Az alap, hogy a PITCH paraméter után a hangmagasságot, a DURATION után a hosszt adhatjuk meg, de ezeken kívül is még sok paraméter megadható (LEFT, RIGHT, STYLE, SYNC, ENVELOPE, INTERRUPT):

   SOUND PITCH [hangmagasság],DURATION [hossz], ... 

      [hangmagasság] - 0 és 127 közötti érték

Hangmagasságból az 1 jelenti a legmélyebb C hangot, és félhangonként a magasabb hangok felé haladva eggyel növelni kell a szám értékét. Így a 2 Cisz, a 3 D, a 4 Disz, az 5 E, a 6 F hangot jelöl, és így tovább. A normál C hangot a 37 jelöli. Mindig 12-t kell hozzáadni az értékhez, ha egy oktávval magasabb hangot akarunk megszólaltatni. (Így a C hangot az egyes oktávokban a következő számok jelölik: 1, 13, 25, 37, 49, 61, 73, 85.) A legmagasabb hangot 127-tel érhetjük el elméletileg, gyakorlatilag azonban nem használunk ilyen magas hangokat a zenében.
A SOUND paramétereinek sorrendje Enterprise-on tetszőleges, hiszen a paraméter neve egyértelműen utal arra.
Burkológörbét is megadhatunk az ENVELOPE NUMBER utasítással. Ennek használatát biztos mindenki ismeri, a gépkönyvben részletesen le van írva.
Egyedülálló Enterprise-on a PING parancs, mely nem paraméterezhető, és egy magas, lecsengő hangot ad ki. Hasonló, mint amikor írógéppel a sor végére értünk.
Videoton TVC-n egyetlen hangcsatornát használhatunk. Ez azt jelenti, hogy lényegében csak egyetlen egy szólamban szólaltathatunk meg zenét, egyidejűleg több hang nem szólhat egyszerre. (Azonban megoldható, hogy két hang egymást nagyon gyorsan váltva szóljon párhuzamosan, így hasonló érzetet kelt, mintha egyszerre szólnának.)
TVC-n egy sound utasítás a következőképpen néz ki:

   SOUND; PITCH [hangmagasság],DURATION [hossz],VOLUME [hangerő]

      ; - nincs interrupt
      [hangmagasság] - 0 és 4095 közötti érték
      [hangerő] - 0 és 15 közötti érték

A DURATION után 1/50 másodpercben adhatjuk meg a hang hosszát, akárcsak Enterprise-on, tehát az 50-es érték 1 másodperc hosszú hangot eredményez. (Azonban TVC-n a DURATION 1 rövidebb hangot eredményez, mint EP-n, így TVC-n ezt kihasználva alaphelyzetben változatosabb hangzások állíthatók elő egyetlen hangcsatornán.)
A PITCH paramétere az azonos szavak ellenére teljesen más, mint Enterprise-on. A legmélyebb elérhető zenei hangot a 110 adja, ez egy G hang. Megadható ennél kisebb szám is, de még a 0 sem mélyebb annyival, hogy fél hanggal nagyobb legyen az eltérés a G-hez képest. A legmagasabb elérhető zenei hang a dokumentáció szerint 4047, ez egy H hang. (A dokumentáció pdf formátumban letölthető, annak az 57. oldalán található meg, milyen zenei hanghoz milyen PITCH érték tartozik.) Megadható ennél nagyobb szám is, azonban ilyen magas hangok esetében kevesebb frekvenciát tud a TVC hangchipje megszólaltatni, mint az alacsonyabb frekvenciáknál, és nem jelöl egyik egész érték sem olyan frekvenciát, amely adott zenei hangnak megfelelne, törteket pedig nem támogat a TVC PITCH paramétere (az Enterprise-é igen). Mély hangokból többet tud a gép megszólaltatni, mint magasból, egy mély G hangnak 110  felel meg, míg az emelletti Gisz-nek 334, tehát 224-et kell a 110-hez hozzáadni, hogy annál egy félhanggal magasabb hangot kapjunk, a többi szám köztes hangmagasságot jelöl. A 4047-tel jelölt H hangnál egy félhanggal mélyebb hangot a 4044-gyel adhatjuk meg, tehát itt már csak 3 a különbség, ami lefedi a köztes hangokat. A két félhang közötti PITCH-beli különbség fokozatosan csökken, ahogy a mély hangok felől a magas hangok felé haladunk. Bonyolult képlettel számítható ki, hogy egyes zenei hangokhoz milyen PITCH érték tartozik, így a leginkább az használható, hogy az előre elkészített táblázatból (lásd a fent linkelt dokumentációt) mindig megnézzük, milyen PITCH érték tartozik a kívánt zenei hanghoz.
TVC-n a SOUND utasítás alapból mindig megszakítja azt a hangot, amely korábban megszólalt, de még szól. (Olyan, mintha EP-n kiadnának a sound mellé az interrupt paramétert is.) Így alapból eléggé gyorsan „ledarálna" egy zenét a gép, ha sosem várná meg az előző hang végét. Hogy kikapcsoljuk ezt, meg kell adni egy pontosvesszőt a SOUND után, így az adott hang mindenképpen addig fog szólni, amekkora hosszúság meg van adva.
TVC-n a hangerőt a SOUND utasítás VOLUME paraméterével adhatjuk meg. A SOUND VOLUME 0 nem ad ki hangot, a VOLUME 1 a leghalkabb, a VOLUME 15 a leghangosabb. A SOUND VOLUME 16 nem ad ki hangot (tehát olyan, mintha 0-át adtunk volna meg), a SOUND 17 olyan, mint az 1, és ez így megy egészen 255-ig. A SOUND VOLUME n+(m*16) ugyanazt a hangerőt eredményezi, mint ha csak n lenne megadva, de csak amíg 256 alatt van az eredmény, mert a 256 már hibát okoz. (Enterprise-on a hangerőt a LEFT és a RIGHT paraméterekkel állíthatjuk, külön a bal és a jobb csatornán 0 és 255 számot megadva, 0 értékre nincs hang, 255 a leghangosabb. Enterprise-on sem adhatunk meg 255-nél nagyobb számot, és megadhatunk negatív számokat -256-ig: a -1 a leghangosabb, a -256-ra pedig nincs hang.)

A Plus 4 két hangcsatornát kezel, ezek közül az egyiken csak tiszta négyszögjel, a másikon vagy zaj, vagy tiszta négyszögjel szólaltatható meg, zenéhez inkább a tiszta négyszögjel praktikus, bár dobszerű effektekhez a zaj is használható lehet. Plus 4-en nem kell a SOUND utasítás mellett PITCH és DURATION szavakat megadni, hanem a hangmagasságot és hanghosszt jelölő számot egymástól vesszővel elválasztva adhatjuk meg, és a csatorna számát is mindig meg kell adni. Egyik paraméter sem hagyható el, sorrendjük kötött, mivel csak a sorrendjükből derül ki, mit jelölnek, hiszen PITCH és egyéb szavak nem utalnak a paraméterre:

   SOUND [csatorna száma],[hangmagasság],[hossz]

      [hangmagasság] - 0 és 1022 közötti érték

A csatorna száma 1, 2 vagy 3 lehet, attól függően, hogy melyik csatornát akarjuk megszólaltatni, ill. tiszta négyszögjelet vagy zajt akarunk-e hallani. Hangmagasságnak 0 és 1023 közötti szám adható meg, 0 a legmélyebb, 1022 a legmagasabb hang, 1023 pedig szintén a legmélyebb hang. A legmélyebb hang egy 2. oktávbeli A hang, ezt PAL rendszerben a 16-os szám jelöli. (Plus 4-en más hangmagasság-értékek tartoznak PAL és NTSC rendszerhez. Az egyszerűség kedvéért mi csak a PAL-lal foglalkozunk.) A 16-os A-nál egy félhanggal magasabb hangra, az Asz-ra a 73-as számmal hivatkozhatunk. A dokumentált elérhető legmagasabb zenei hang a 6. oktávbeli A hang, erre 961-gyel hivatkozhatunk. Az ennél egy félhanggal mélyebb hang száma 957. (A plus4world oldalán megtekinthető, mely zenei hangokhoz milyen értéket kell megadnunk.)
A hang hosszát az Enterprise-hoz és TVC-hez hasonlóan 1/50 másodpercekben adhatjuk meg, így az 50-es szám egy másodperc hosszúságú hangot eredményez. (NTSC rendszer esetén 50 helyett 60 jelöli az 1 másodperc hosszúságú hangot.)
A hangerőt nem a SOUND paramétereként adhatjuk meg, hanem külön utasítással állíthatjuk a globális hangerőt. A VOL után 0 és 8 közötti szám adható meg, 0 értékre csend van, 1 a leghalkabb, 8 pedig a leghangosabb hangerő. A VOL mindkét csatornára kifejti hatását, tehát nem állítható a két csatorna hangereje külön-külön.
Plus4-en tehát feltétlenül mind a 3 paramétert meg kell adni, és szigorúan a fenti sorrendben, hiszen a PITCH, DURATION, stb. szavak nem utalnak a paraméterre. Ennél több paramétere nincs ezen a gépen a SOUND utasításnak. Ehhez képest Enterprise-on és TVC-n tetszőleges sorrendben adhatjuk meg a hanghosszt, hangerőt, hangmagasságot, vagy akár el is hagyhatjuk egyik-másik paramtétert, vagy akár az összeset, mert minden paraméternek van alapértelmezése, így önmagában egy SOUND utasítás is eredményez hangot. Plus4-en ez nem lehetséges.

Videoton és Plus/4 gépen bonyolult képlettel számítható ki, hogy egy adott zenei hanghoz milyen hangmagasság-értéket kell megadnunk a SOUND utasításban. Bár az Enterprise-on csak 0 és 127 között vannak a hangmagasság értékei, ez a gép tudja a három közül a legtöbb hangmagasságot megszólaltatni, részben azért, mert törteket is megadhatunk PITCH értéknek, részben pedig azért, mert az egész PITCH értékeket használva is a hangterjedelem 8 oktáv. Az Enterprise a PITCH értékeket felhasználóbarát módon kezeli, az egész értékek mindig konkrét zenei hangokat jelentenek. Azonban magas hang kevesebb van Enterprise-on is, mint mély, bár a PITCH értékek eloszlása látszólag egyenletes. Ennek ellenére kb. 100 fölött több egymás melletti PITCH érték is ugyanazt a hangot jelöli, és a törtek is. Így ha lefuttatunk egy FOR ciklust, mely a gép egész hangterjedelmét megszólaltatja, Enterprise-on a magas hangok tartományában hosszabb ideig fogjuk hallani ugyanazt a hangot többször is, míg TVC-n és Plus/4-en ilyenkor gyorsabban követik egymást a hangok, mivel utóbbi gépeknél a hangmagasság-értékek mind konkrét, különböző hangokat jelölnek.

Egy normál C hangot a következő utasítással tudunk megszólaltatni a három gépen egy másodpercig:

   Enterprise:
     SOUND PITCH 37,DURATION 50

   Videoton TVC:
     SOUND PITCH 3349,DURATION 50

   Commodore Plus 4:
     SOUND 1,600,50

A következő basic programok a Boci-boci tarka című világhírű dalt játsszák le a három gépen:

Enterprise:
Videoton TVC:
Commodore Plus 4:


A 264-es családba tartozik a Plus/4-gyel együtt a Commodore 16 és Commodore 116 is többek között, ezek egymással kompatibilisek (megfelelő memóriabővítés esetén), a basic-jük is teljesen ugyanaz, a SOUND utasítás mindegyikben ugyanazon az elven működik.
A régi számítógépek basic-je nagyon hasonló módon kezeli a hangkeltést. Mindegyiknek az a lényege, hogy a hangot kiadó utasítás (a legtöbb esetben SOUND) után számokkal kell megadni a hangmagasságot és a hosszt, csak az egyes hangmagasságokat gépenként eltérő számok jelölik. Az eddig tárgyalt gépeknél a hossz hasonló elven adható meg, 1/50-ed másodpercben.
Az Amstrad CPC sound utasításának felépítése nagyon hasonló a Plus/4-eséhez, egymástól vesszővel elválasztva kell megadni első helyen a csatorna számát, utána a hangmagasságot, végül a hanghosszt. További paraméterek is megadhatóak itt, többek között a hangerő, de ezek a paraméterek elhagyhatók, akárcsak a hanghossz, csak az első két paraméter kötelező. Így egy minimalista sound utasítás CPC-n minimum két paraméterrel rendelkezik, pl.

   SOUND 1,239

ahol csak a csatorna számát és a hangmagasságot adtuk meg, a többi paraméter alapértelmezés szerinti.
CPC-n három csatorna létezik, ezeket A, B és C betűjellel jelölik. Az A csatorna balról, a B csatorna jobbról, a C középről szól. Az A csatornához a SOUND utasításban az 1, a B csatornához a 2, a C-hez a 4-es számot kell megadni. Ha egy hangot egyszerre több csatornán is meg akarunk szólaltatni, akkor a megfelelő csatornák számjegyét (1, 2 ill. 4) össze kell adni. A 3-as szám tehát nem a C csatornát jelöli, hanem azt, hogy az 1-es és a 2-es csatorna is aktív. (Enterprise-on hasonló elven működik a joy függvény, ahol a megfelelő irányok értékeit kell összeadnunk azok kombinációjának eléréséhez.) A csatorna számához különböző értékek hozzáadásával az egyes csatornákat összehangolhatjuk, késleltethetjük, vagy az Enterprise-on ismert CLEAR SOUND utasításéhoz hasonló hatást élhetünk el. A késleltetett hangokat a RELEASE paranccsal szólaltathatjuk meg.
Hangmagasságnak 0 és 4095 közötti szám adható meg a TVC-hez hasonlóan, azonban minél kisebb ez a szám, annál magasabb hangot jelöl, a legmélyebb hang pedig a 4095. (Ezzel szemben Enterprise-on, TVC-n és Plus/4-en is minél kisebb a szám, annál mélyebb a hang.) A hangterjedelem itt 8 oktáv. Hanghossznak 100-at megadva kapunk 1 másodperces hangot.
Egy egy másodperces, normál C hang:

   SOUND 1,239,100

A cpcpwiki oldalon egy PDF fájlban megtalálható a gép dokumentációja, melyben a 7. fejezet 24. oldalán megtalálható a táblázat, hogy melyik zenei hangnak milyen szám felel meg. Tehát, a TVC-hez és a Plus4-hez hasonlóan bonyolult képlettel számítható ki CPC-n is, hogy mely zenei hangnak milyen érték felel meg. A hanghosszt jelölő szám pont fele olyan hosszú hangot jelöl, mint Enterprise-on, TVC-n vagy Plus/4-en, ott az 50 jelöli az 1 másodperc hosszúságú hangot, míg CPC-n a 100. Ha nem adjuk meg a hanghosszt, az alapértelmezés 20 lesz (0.2 másodperc).
A SOUND első két paraméterét (csatorna és hangmagasság) CPC-n kötelező megadni. A harmadik paramétert, a hosszt nem kötelező megadni, és a további paraméterek sem kötelezőek. Összesen 7 paraméter adható meg:

   SOUND [csatorna száma],[hangmagasság],[hossz],[hangerő],[volume envelope],[tone envelope],[zaj típusa]

A Plus/4-gyel párhuzamba állítható a csatornaszám, hangmagasság, hanghossz megadása. Utána sorrendben következik a hangerő, utána a hangerő burkolójának (envelope) azonosítója, a hangmagasság burkolójának azonosítója, végül a zaj típusa.
A hangerő a TVC-hez hasonlóan 0 és 15 közötti szám lehet.
Míg Enterprise-on az ENVELOPE NUMBER paranccsal definiálhatunk burkológörbét, melyben a hangmagasság és a hanghossz változásait is megadhatjuk, CPC-n külön kell definiálnunk burkológörbét a hangerőnek és a hangmagasságnak. Az előbbit az ENV paranccsal (V mint Volume), az utóbbit az ENT paranccsal (T mint Tone) hozhatjuk létre. Például az ENV 1,50,20,20 parancsban az 1. szám jelzi a burkológörbe azonosítóját (hasonlóan az ENVELOPE NUMBER esetéhez), ezt az azonosítót kell megadni a SOUND utasítás 5. paramétereként. Az ENT 2,10,-4,20 olyan burkológörbét definiál, melynél a hang lejátszása során a hangmagasság fog változni. Ennek a burkolónak a bekapcsolásához itt a SOUND utasítás 6. paraméterében 2-t kell megadni.
Az utolsó paraméterrel megadhatjuk, hogy akarunk-e zajt megszólaltatni az adott csatornán, és ha igen, akkor mi legyen a frekvenciája. A zaj paramétere 0 és 31 közötti szám lehet, 0-ra nincsen zaj, az 1 a legmélyebb, a 31 a legmagasabb hangú zajt eredményezi. A zaj hasonló, mint Enterprise-on a magas torzításnál megadott PITCH értékek, csak jóval kisebb a zaj frekvenciatartománya. CPC-n tehát egyetlen csatornán egyszerre szólhat zaj és tiszta hang is, a SOUND második paramétereként a tiszta hang frekvenciáját, a 7. paramétereként a zaj frekvenciáját adhatjuk meg, a két frekvencia tehát egymástól független. Ha az adott csatornán a tiszta hangot vagy a zajt ki akarjuk kapcsolni, akkor az adott paraméterbe nullát kell megadni, illetve  a zaj paraméterét a legvégéről el is hagyhatjuk.
CPC-n a paraméterek sorrendje a SOUND parancsban kötött. Ha nem akarjuk megadni valamelyik paramétert, de a következő paramétert igen, akkor egyszerűen ki kell hagynunk, ilyenkor vesszők kerülnek egymás mellé. Ha például nem adunk meg sem a hangerőhöz, sem a hangmagassághoz burkológörbét, de zajt meg akarunk adni, akkor így nézhet ki egy SOUND utasítás:

   SOUND 1,300,100,7,,,10

A CPC dokumentációjában (linket lásd feljebb) bővebben olvashatunk a SOUND utasításról és a burkológörbék működéséről az 1. fejezet 65. oldalától kezdődően, valamint a 8. fejezet 35. oldalától kezdődően.

A BEEP utasítás és egyebek

ZX Spectrumon sem túlságosan eltérő a helyzet. Ott azonban a BEEP utasítást használhatjuk hangkeltésre. A paraméterek sorrendje kötött, de eltér a Plus/4-től vagy a CPC-től, először kell megadni a hanghosszt másodpercekben, utána a hangmagasságot:

    BEEP [hanghossz másodpercekben],[hangmagasság]

A 0 jelöli a normál C hangot, és félhangonként felfele haladva mindig egyet kell ehhez hozzáadni, hasonlóan az Enterprise-hoz. (Így pl. Cisz 1, D 2, Disz 3, E 4.) Azonban az Enterprise-nál az 1 a legmélyebb C hangot jelöli, és nem a normál C hangot, mint Spectrumon. Ha a normál C hangnál mélyebb hangokat akarunk, akkor negatív számokat kell megadni (pl. -1 lesz a H). Továbbá, Enterprise-on az 1-es szám jelöl C hangot (a 0 egy H hangot jelöl), Spectrumon pedig a 0 jelöl C hangot. Így, ha a BEEP-ben megadott hangmagasságot számoljuk át SOUND PITCH értékre, 1-et hozzá kell adnunk (illetve ezen kívül még 3*12-t is hozzá kell adnunk, hogy ugyanabban az oktávban legyen az adott hang).
Spectrumon egy másodperces normál C hang így fest:

   BEEP 1,0

Tehát az 1 utal az egy másodpercre, a 0 pedig a C hangra. A BEEP 1,12 egy oktávval magasabb C hangot eredményez. A Worldofspectrum oldalán bővebben olvashatunk a BEEP utasítás használatáról, illetve a Sam Coupé felhasználói kézikönyve (lásd lejjebb) is elég bőven kifejti, hogy mely zenei hangok milyen BEEP értékekkel érhetők el.

Spectrum 128k-n a BEEP mellett a PLAY utasítás áll még rendelkezésre. Ezzel az utasítással sokkal könnyebben írhatunk zenét, mint a többi gépen, ahol az egyes zenei hangokhoz tartozó számokban nem mindig van sok logika. Három szöveges változót adhatunk meg a PLAY utasítás után, melyekben a három csatorna hangjait adhatjuk meg:

    PLAY a$,b$,c$

A hangokra a zenében is használt nevükkel hivatkozhatunk. A hangok nevei mellett természetesen egyéb karaktereket is elhelyezhetünk, melyekkel a hang hosszát, az oktávot, a tempót és egyéb paramétereket állíthatjuk. Egy C hang így szólaltatható meg:

   PLAY "c"

A PLAY utasításról bővebben a World of Spectrum oldalán lehet olvasni, illetve a Spectrum Világ 5. számában.

Sam Coupén is használható a BEEP, ezen kívül létezik SOUND utasítás is. Utóbbi használata kissé bonyolult, mivel közvetlenül a hangchip regisztereit piszkálhatjuk vele. Egyedülálló ezen a géptípuson a ZAP, POW, ZOOM, BOOM parancs, melyek négy különböző hangeffektet szólaltatnak meg. Ez a megoldás kissé az Enterprise PING utasítására emlékeztet. A speccy.cz oldalán megtalálható a Sam Coupé felhasználói kézikönyve, ebben a 93. oldalon kezdődik a hangkeltéssel foglalkozó fejezet.

Commodore 64-en a hangkeltéshez használható basic utasítás a poke, ez egy külön világ.

Összegzésként, a hangmagasság paraméterezésében megkülönböztethetünk felhasználóbarát és nem felhasználóbarát megoldásokat. Felhasználóbarát az Enterprise SOUND, és a ZX Spectrum ill. számos más gép BEEP utasítása. Ezeken a gépeken aránylag könnyen kiszámolható, hogy adott zenei hanghoz milyen értéket kell megadnunk, míg pl. TVC-n, Plus/4-en és CPC-n egy átlagos, zenéhez értő felhasználó számára nem mondható egyszerűnek a hangmagasság értékeinek a kiszámolása. A  BEEP utasítás és az Enterprise SOUND utasítása annyiból is előnyös, hogy adott számú félhangnak megfelelő számjegy hozzáadásával egy dallam valamennyi hangmagasság-értékéhez könnyen transzponálhatjuk a dallamot. Például, 12-t hozzáadva a hangmagasságot jelölő változóhoz egy oktávval magasabb hangon játszhatjuk le a dallamot, bármilyen hangmagasságról legyen is szó. Más gépeken ez nem lehetséges.
A legemberbarátibb a Spectrum 128 PLAY utasítása, mivel maguknak a zenei hangoknak a nevét adhatjuk meg.

Friday, 24 April 2020

Xorka - logikai játék. Az Enterprise változat átírása TVC-re

A Xorka logikai játék átírása IS-BASIC-ből TVC basicbe.
Avagy: Hogyan írjunk át Enterprise basic programokat TVC-re?


A játék egyszerűen leprogramozhatónak tűnt, először Enterprise-ra írtam meg, természetesen basic nyelven, lásd az előző posztban. Az Enterprise jó tulajdonsága, hogy egymás alá több, különböző paraméterezésű videólapot is meg lehet nyitni, mindegyikben beállítani egyénileg a grafikus módot és a palettát. Bár például attribútum módban csak 16 színt lehet használni (a feketével együtt) a 256-ból, mindegyik videólapnak külön beállíthatjuk a palettáját, így szép színes képernyőt nyerhetünk. A menüben is két videólapot állítottam be, és a játék alatt is egyik videólapon látszódik a játéktér, a másikon az eredmények: pontszám, lépések száma, stb.

Jött az ötlet, miért ne lehetne Videoton TVC-re is megcsinálni. Kilistáztam az Enterprise változatot egy txt fájlba, és nekiestem. Leírom, hogyan tettem futtathatóvá a programot TVC-n.

1. Utasítások, melyek TVC-n nem értelmezhetők, teljesen kihagyandók:
- A program legelejéről a PROGRAM utasítást töröltem legelőször. A hangulat kedvéért beírtam REM mögé a program címét. A SET STATUS OFF, SET KEY CLICK OFF, CLEAR SOUND utasításokat is töröltem.

2. A hangkeltés
- Az ENVELOPE NUMBER-t nem ismeri a TVC-t, ezeket is töröltem.
- A SOUND utasításokból kitöröltem az ENVELOPE, SOURCE, SYNC, STYLE paramétereket, csak a PITCH és DURATION paramétereket hagytam meg. A DURATION kb. ugyanúgy működik mindkét gépen. A PITCH szólni fog akkor is, ha az EP-s érték marad, de teljesen más lesz a hang, így ezt majd át kell írni, hogy jól hangozzon. Az EP-s PITCH értékek TVC-n mind közel ugyanazt a mély hangot adják.
- Enterprise-on a hangpufferbe 25 hang fér. Ha kiadunk több SOUND utasítást is egymás után, nem várakozik a program addig, amíg nem játssza le mindet, hanem fut tovább, és közben zenél. Ezért általában elindítjuk a zenét, és például utána írjuk ki a Congratulations feliratot, így a pompázó felirat közben zenei élményben is lehet részünk. TVC-n, ha ugyanezzel a módszerrel programoznánk, akkor nem jelenne meg a felirat, csak miután lement a zene. Ezért ilyen esetekben a SOUND utasításokat át kell helyezni későbbre, ha ugyanazt a hatást akarjuk elérni, mint EP-n.
A hangot legcélszerűbb elölről írni, ennek módjára talán később visszatérek majd.

3. SET CHARACTER: Ez az utasítás ugyanúgy működik mindkét gépen, csak TVC-n eggyel több számot kell megadni, mert eggyel több pixelsort tartalmaznak a karakterek. 
- Volt két SET CHARACTER a program elején. Ezek a karakterdefiníciók talán maradhatnak, de mivel a TVC karakterei egy pixellel magasabbak, lehetséges, hogy érdemes újratervezni a karaktereket. Az EP-s karakterdefiníciót TVC-n alkalmazva a karakter utolsó sora biztosan üres lesz, ami sok esetben nem is zavaró. (Mivel, ha az utolsó sort definiáló 0-át lehagyjuk a végéről, az akkor is 0 marad.) Kritikus esetben általában vagy az egész elejére kell 0-át írni, ami lejjebb tolja a karaktert, vagy pedig az utolsó sor adatait még egyszer beírni, így az egymás alatt lévő karakterek TVC-n is összeérnek.
Enterprise-on a karakterkészlet bármely eleme áttervezhető. Fontos, hogy TVC-n nem lehet akármelyik karaktert átdefiniálni, így új kódokat kell megadni, majd a program további részében is megkeresni a hivatkozásokat az átdefiniált karakterekre, és megfelelően módosítani azokat. Olykor előfordul, hogy EP-n már meglévő karaktert definiálunk át, erre TVC-n nincs mindig lehetőség. A TVC-s karakterkészlet három csoportra osztható:
  1. előre definiált karakterek, amelyeket nem tudunk átdefiniálni (például a betűk, számok);
  2. előre definiált karakterek, amelyeket át tudunk definiálni (például az ékezetes betűk).
  3. definiálatlan, üres karakterek, melyeket mi tervezhetünk meg.

Érdemes általában a 3. csoporton belül definiálni karaktereket. Az Enterprise programban az X betűt definiáltam át, ez lett a kurzor a játék közben. TVC-n ez nem oldható meg, másik, üres karaktert definiáltam kurzornak. Kellett még egy karakter a kurzor inverzének (amikor tereptárgyon van a kurzor, hogy az is látsszon), és egy teljesen tömör karaktert is kellett definiálni (csupa 255 mindegyik sora), amely a fal ill. tereptárgy. TVC-n a könnyen megjegyezhető 200-as kódú karakterrel kezdtem az új karakterek definiálását.

4. Tömbök: Enterprise-on STRING, NUMERIC, DIM is van, TVC-n csak DIM van.
- Jó szokás szerint felsoroljuk jobb esetben a program elején a változóneveket STRING ill. NUMERIC utasítás után. Erre TVC-n nem volt szükség (mivel nincsenek külön külső és belső változók), és egyébként sem ismeri a TVC a STRING és a NUMERIC utasítást. Volt egy tömb, amit STRING-gel adtam meg, ezt átírtam DIM-re, a TVC csak ezt ismeri. A STRING-ben és a NUMERIC-ben a tömb alsó és felső határát is meg kell adni, a DIM-ben csak a felsőt, az alsó mindig 0. Eszerint átírtam a STRING-et DIM-re.
Mivel a DIM alsó határa nem adható meg, így keletkezett egy felesleges tömbtartomány, de ez nem okoz problémát.

5. Videolapok, videocsatornák:
- A videolapok megnyitását és megjelenítését egyből kitöröltem mindet: az OPEN #csatornaszám:"video:" és a DISPLAY utasításokat. Két videólap volt egyszerre a képernyőn, így arra is figyelni kell, hogy a PRINT utasításból ha kivesszük a csatornaszámot, akkor az EP-n még két videólapra író utasítás TVC-n ugyanarra a lapra fog írni, így például a PRINT #1,AT 2,2:"X" és a PRINT #50,AT 2,2:"Score" felülírják egymást. Így a képernyőn más helyet kell keresni a két videólapra írt karaktereknek.
- A PRINT utasításokból kitöröltem a csatornaszámokat. Át kell írni úgy az egészet, hogy a régebbi két videólap ne keveredjen össze.

- Jó szokása az Enterprise-nak, hogy ha megjelenítünk egy videólapot, azt a képernyő megadott soraiban pontosan középre teszi, és jobb és bal oldalon is pont ugyanannyi fog kimaradni mellette. Így például a PRINT #1,AT 1,1 nem feltétlenül a képernyő vagy nem is egy képernyősor bal oldalára fog írni, hanem annál általában beljebb. Ugyanakkor a TVC-n a PRINT AT 1,1 minden esetben a képernyő bal felső sarkát jelenti. Így a koordinátákhoz hozzá kell adni bizonyos értéket. Mivel ebben a játékban még többféle méretű játéktér is lehet, ez külön érdekes probléma volt. Csak a megjelenítésben kell arra törekedni, hogy középen legyen a játéktér. A program ettől még a játéktér elemeit tároló tömbben az 1,1-es koordinátán fogja tárolni a játéktér bal felső elemét, de a képernyőre való kiíráskor arra 1+n,1+m-mel kell hivatkozni. Hogy mennyi lesz az n és m értéke, az az aktuális játéktér méretétől függ, valószínű az összes játéktérhez külön kell definiálni értékeket.

- Az Enterprise szöveges képernyője alapból 24x42 karaktert tud megjeleníteni. (Basic programozással ennél magasabb lap is létrehozható.) TVC-n az ehhez a legközelebb álló videomódban kevesebb karakter lehet a képernyőn vízszintesen, csak 32, de függőlegesen szintén 24. Ha egy programban fontos, hogy 32-nél több karakter legyen egy sorban, akkor vagy valami rafinériát kell kitalálni a programtól függően, vagy a nagyobb felbontású videomódba (GRAPHICS 2) kell a programot átírni, ilyenkor a színekről le kell mondanunk.


6. Színek:
- A SET INK és SET PALETTE utasításokból is ki kell venni a csatornaszámokat. A tinta színét és a palettát külön ki kell kísérletezni. Az egyéb EP specifikus grafikai beállításokat (pl. SET BIAS) is el kell távolítani.

7. Változók kezelése:
- Enterprise-on több változónak is megadhatjuk egyszerre ugyanazt az értéket (pl. LET WIN,SUM=0). TVC-n ez nem lehetséges, ott külön-külön kell minden változónak megadni az értékét minden esetben (pl. LET WIN=0:LET SUM=0).

8. Strukturált programozás: eljárások, blokkok, feltételek, ciklusok
- Át kell írni a strukturált programozásra jellemző részeket: SELECT, CASE, END SELECT; IF, ELSE, END IF. A legcélszerűbb ezeket IF-fé alakítani, és a THEN után odazsúfolni mindent, ami EP-n a feltételes eljárásban szerepel utána. Ilyenkor figyelni kell egy dologra: Az Enterprise a CASE ágaknál,ha teljesül a feltétel, akkor nem nézi tovább a többi feltételt, hogy azok is teljesülnek-e, hanem a következő CASE ágakat ilyenkor már átugorja. Ha TVC-n a CASE ágakat IF utasításokká alakítjuk, akkor a feltétel teljesülésekor nem utasítja még semmi arra a gépet, hogy a következő feltételeket ne nézze végig. Ezért a soron következő IF-eket is végignézi a program, ami lassíthat a futásán. Nem csak lassíthat, hanem lehetséges olyan eset, amikor a CASE ágon belül átírtuk valamelyik változónak az értékét, pont azét, amelyikét vizsgáljuk, így a következő IF utasításnál pont igaz lesz a feltétel már újra, így a program nem fog megfelelően működni. Ennek megoldása, ha a feltétel teljesülésekor egy GOTO-val utasítjuk a gépet, hogy ugorjon oda, ahol EP-n az END SELECT volt. A CASE ELSE ágra nincs szükség, ha nem hajt végre semmit a program, így TVC-n azt nyugodtan törölhetjük.
A CALL- DEF részeket át kell írni GOSUB - RETURN eljárásokra.
A DO - LOOP helyett a DO helyére egy REM-et kell írni, a LOOP helyére pedig egy GOTO-t, mely a REM sorszámára hivatkozik. LOOP UNTIL helyett IF ... THEN GOTO-t kell használni.

9. Gépi kóddal kapcsolatos utasítások:
- Szerencsére POKE nem volt a programban. Illetve egy volt, ami a program ideiglenes gyorsítását hivatott elvégezni a megszakítások kikapcsolása által, a program indításakor, hogy előbb megjelenjen a menü. Ezt törölni kellett, nincs is rá szükség, mert a TVC basicje gyorsabb, mint az EP-é. A gyorsító OUT 191,12-t is törölni kellett. Egyéb POKE utasításoknál külön utána kellene nézni, hogy amit el akarunk akkor végezni, azt TVC-n hogyan lehetséges.

10. Joystick figyelése:
- A JOY függvény helyett mást kell használni. A beépített botkormány TVC-n billentyűleütésekként (is) lekérdezhető az INKEY$ változóból, ezt használtam, JOY-hoz hasonló függvényt nem találtam TVC-n. A különbség, hogy a JOY függvény értéke mindig tükrözi azt, hogy el van-e döntve a botkormány valamilyen irányba, tehát akkor is, ha az INKEY$ változóban nem jelenik meg az értéke, mert a billentyűismétlés gyakorisága pl. alapértelmezés. Így ha TVC-n az INKEY$-ből olvassuk ki a botkormány mozgatását, akkor nem lesz olyan folyamatos, mint EP-n a JOY függvénnyel. A kívánt eredményhez a SET DELAY ill. SET RATE értékét megfelelően be kell állítani (EP-n SET KEY DELAY, SET KEY RATE).
A JOY függvény értékeit a következő, TVC-s INKEY$ értékekre kell átírni:

--------------ENTERPRISE JOY()-----------TVC INKEY$
jobbra.....................1.................................4
balra.......................2 ...............................19
le............................4................................24
fel...........................8................................5
tűz.........................16...............................32 (space)

11. Egyebek:
- TVC-n a WAIT DELAY utasítás ismeretlen, ezt  át kell alakítani FOR-NEXT ciklussá.


A jelentősebb problémát talán a videólapok megszüntetése miatt megváltozó PRINT utasítás koordinátái okozták. Nem igazán kell a felső matematikát ismerni ahhoz, hogy kiszámoljuk, hogyan állítható be minden pálya pont a képernyő közepére, de számolni tudni kell.
Problémát okozhat még, ha az IF eljárásban sok utasítás volt több sorban, és ezek nem férnek el TVC-n a THEN után. Ilyenkor GOSUB-bal kell egy külön részt meghívni a programban, amely elvégzi a szükséges feltételhez tartozó tennivalókat. Ilyen probléma most nem volt, több utasítás is kifért egy sorba.
Külön össze kell válogatni a színeket és el kell készíteni a hangeffekteket, zenét. Ahol EP-n sok szín volt, vagy összetettebb a zene, ott TVC-n meg kell elégednünk valami egyszerűbbel.

Thursday, 16 April 2020

Xorka - logikai játék a xor elvén. Enterprise változat elkészítése

Spectrum számítógépre készítette el Alexander Zavgorodniy 2015-ben basic nyelven ezt a játékot, a Spectrum Computing oldalán is megtalálható.



A játék egy 10x10-es felületen játszódik, ahol a célunk a színes (a képen zöld) mezők eltüntetése. A pályán egy kurzort (X alakú) mozgathatunk. A tűzgomb megnyomására a gép egy vízszintes és egy függőleges vonalat rajzol úgy, hogy azok a kurzor pozíciójában metszik egymást. Pontosabban a vonalak kirajzolása közben a pályán oda, ahol nincs pályaelem, pályaelemet tesz, ahol pedig már van valami, onnan letörli. Tehát a xor elvén az adott vízszintes és függőleges pozícióban átállítja a 0 jelű elemeket 1 jelűvé, az 1 jelűeket pedig 0 jelűvé. A játék indításakor kirajzol véletlenszerűen vízszintes és függőleges vonalakat, ebből kell nekünk visszaállítani az üres pályát.
Az ep128.hu oldalon ennél tömörebben és érthetőbben van leírva a játék lényege: Feladatunk a megadott méretű játéktér megtisztítása a sötét mezőktől. Ennek egyetlen lehetséges módja, hogy a mozgatható célkereszt oszlopában és sorában a tűz gomb megnyomásával ellentétes színűre változtatjuk az összes mezőt (vagyis amelyik mező eddig sötét volt, most világos lesz, a világos mezőkből pedig sötét).
Az Enterprise változat a Xorgame 2 nevet kapta. Az első rész, a Xorgame Endi és IstvánV közös, gépi kódú programja, melyet Endi PC-re írt meg, majd István ebből elkészítette az Enterprise változatot. A Xorgame kapcsán botlottunk bele a Xorka nevű játékba, így merült fel, hogy ezt is meg lehetne írni Enterprise-ra.



A program működése:


A program az elején létrehoz egy kétdimenziós tömböt, ebben fogja tárolni az egész pályát. Játék közben ennek a tömbnek a megfelelő elemét fogja majd vizsgálni a program, illetve ennek a tömbnek az elemeit fogja módosítani xor végrehajtásakor (tűzgomb játék közben). Ahogy a képernyőre kiír ill. onnan letöröl pályaelemeket, azzal párhuzamosan a tömb tartalmát is módosítja. A tömb helyett kiolvashatná éppen a képernyőről is, hogy az adott pozícióban 0 vagy 1 van (vagyis van-e pályaelem vagy nincs), azonban a játék attribútum képernyőn fut, hogy több szín lehessen, grafikus képernyőről pedig nem tudunk karaktereket kiolvasni. (Elvileg megoldható lenne az is, hogy ne a karaktereket, hanem a színüket olvassa ki a program, ezzel még nem próbálkoztam.)

A játéknak két fő része van:


1. a kurzor (X jel) mozgatása a pályán
2. a xor művelet végrehajtása a tűzgombra (ez a XOR nevű eljárás a DEF XOR blokkban, amit a CALL XOR paranccsal hívunk meg mindig, amikor a játékos megnyomja a tűzgombot).

A pálya kirajzolása nem külön rész, hanem a program véletlenszerűen kiválaszt kurzorpozíciókat, és ott végrehajtja a xor műveletet (meghívja a XOR eljárást a CALL XOR-ral), ezzel rak a pályára vonalakat.

1. Az X mozgatása a pályán: figyeli a program, hogy a botkormány el van-e döntve valamelyik irányba. Ha igen, akkor megvizsgálja, hogy az adott irányba lehet-e tovább lépni, nem vagyunk-e már a pálya szélénél. Ha még lehet lépni, akkor a kurzort letörli a helyéről, az adott koordinátájához hozzáad egyet, majd kirajzolja az új helyén. Közben arra is figyelni kell, hogy milyen mezőre lép a kurzor, oda, ahol van pályaelem, vagy oda, ahol üres a pálya. Bárhova rakjuk ki a kurzort, az letörli, ami éppen ott van, tehát a pályaelem helyére és az üres helyre kirakva is teljesen ugyanúgy fog kinézni. Így nem lehetne eldönteni, a kurzor helyén pályaelem van-e vagy sem. Ezért az X kirakása előtt előbb meg kell vizsgálni, hogy az adott pozícióban mi van, és ha pályaelem, akkor inverz X-et kell kirakni. Az inverz X karakter definiálásáról a program elején gondoskodunk.
Akár meg lehetett volna csinálni, hogy az X villogjon, így látszana az is, ami alatta van. De azt is meg lehetett volna csinálni, hogy villogjon is, és a pályaelemhez is hasonuljon.
Fontos még, hogy a kurzor mozgatásakor, amikor a régi pozíciójából letörli és az új pozíciójába kirakja, akkor a régi pozíciójából ne csak letörölje, hanem az ott lévő pályaelemet rakja ki maga után. Ezt a tömbből kell kiolvasni. Ha nem tömböt használnánk, hanem a képernyőről olvasná ki, milyen karakter van adott pozícióban, akkor adott pozícióba lépés előtt egy stringben el kellene tárolnia az adott pozícióban lévő karaktert, utána kéne rálépnie, majd onnan ellépéskor kiírni a kurzor régi helyére, amit kiolvasott onnan odalépéskor.

2. A XOR művelet végrehajtása nem bonyolult. Két FOR ciklus kell hozzá. Az első a pálya felső szélétől a az alsó széléig, a második a pálya jobb szélétől a bal széléig rajzol vagy pályaelemet minden pozícióba, vagy vonalat, attól függően, hogy mi van ott: megnézi a program minden pozícióban a tömb adott elemét, és ha az 1 (pályaelem), akkor átírja 0-ra. Ha pedig 0, akkor átírja 1-re.
Amikor a keret kirajzolása után a pályát felépíti a gép véletlenszerű pozíciókba xorolással, akkor az 1-esek lerakásakor megnöveli 1-gyel a SUM változó értékét, mely kezdetben 0. Ha pedig egy 1-et átváltoztat 0-ra, akkor csökkenti 1-gyel a SUM értékét. Így tehát azt tárolja a SUM változóban, hogy jelenleg hány 1 (pályaelem) van a képernyőn. Erre azért van szükség, hogy játék során, ha sikerül az összes pályaelemet lepucolnunk, akkor a játszma befejeződhessen, és kiírja a gép a képernyőre a gratulációját. Figyelni kell arra is, hogy a XOR műveleteket végző eljárást a pálya kirajzolásakor, vagy pedig játék közben hívjuk-e meg. Erre azért van szükség, mert a pálya kirajzolásakor is előállhat az az állapot, hogy eltűnik, minden 1-es a pályáról, mert egymás után kétszer hajt végre a kirajzoló ugyanabban a pozícióban xor műveletet, így egyszer kirajzolja az 1-eseket, utána pedig letörli, és ebben a helyzetben a játék gratulációját fejezi ki, és vége a játéknak. Először bele is estem ebbe a hibába, hogy erre nem figyeltem, utólag tettem bele annak figyelését, hogy játékból vagy pedig a pálya kirajzolásából hívjuk-e meg a XOR eljárást, az előbbi esetben a GAME változó értéke 1, utóbbi esetben 0.
XOR művelet legelején a kurzor pozíciójában lévő karaktert átváltoztatja az ellentétére. Az eredeti program írója úgy oldotta meg, hogy ahol a két vonal metszi egymást, ott az maradjon a pályaelem, ami előtte volt, így én is ezt az elvet követtem. Elméletileg az se lenne baj, ha a vonalak metszéspontjában ellenkezőjére váltana a karakter, mert az egyik vonal felülírja azt, a másik vonal pedig ismét felülírja.