Wednesday, 23 December 2020

TVC - Enterprise konverzió

Felbontás, 4 színes üzemmód

Függőleges felbontás

A TVC alapesetben (4 színes mód) 24 karaktersort jelenít meg, és az Enterprise is. Enterprise-szal basic programozással még 3 karakterrel nagyobb videolap is megnyitható a teljes képernyőre, tehát a teljes képernyő, amit egyszerre láthatunk belőle Enterprise-on 27 karakter magas lehet basicből programozva. Gépi kódú programozással talán ennél magasabb videolapot is el lehet érni. TVC-n gépi kódú programozással írhatunk a 25. sorba is.

Ezek alapján azt gondolhatnánk, a TVC függőleges felbontása jóval kisebb, pedig nem. A TVC karakterei egy pixellel magasabbak, mint az EP-s karakterek, tehát minden karaktersorhoz hozzáadódik még 1 karakterpixelnyi terület. Így nem sokkal lesz kisebb a TVC függőleges felbontása 4 színű módban, mint Enterprise-on. Ha EP-n megnyitunk 27 karakter magas videolapot, akkor a legnagyobb függőleges koordináta 971 lehet, míg TVC-n 959. Enterprise-on ez az érték 24 karakter magas módban csak 863. Ezért ha TVC-ről írunk át EP-re, akkor célszerű 27 karakter magas videolapot nyitni, ha TVC-n a program a képernyő teljes magasságát kihasználja.

A fentiekből olyan probléma adódik, hogy ha a TVC-s programban a képernyőre vegyesen kerül szöveg is és rajz is, és a szöveget PRINT AT utasítással adják meg, akkor az el lesz csúszva a rajzhoz képest.

Vízszintes felbontás

TVC-n 32 karakter fér ki egy sorban, EP-n 42. (EP-n beállítható ennél kisebb videolap is.) Ha Enterprise-on TVC-s képernyőt akarunk emulálni, akkor 42 helyett 32 karakter széles videolapot kell nyitnunk. 4 színű módban a PLOT utasítással így pont ugyanannyi képpont használható ki, vagyis a vízszintes felbontás mindkét gépen 1023.

Attribútum mód és 4 színes mód

Szimpatikusnak tűnhet az ötlet, hogy Enterprise-on a 4 színes mód helyett attribútum módot használjunk TVC program átírásakor, így több színt lehet használni. A probléma az lehet, hogy ha a TVC-s program PLOT utasítással állítja be a ceruza helyét és oda fog írni PRINT utasítással, Enterprise-on ez nem működik. EP-n attribútum módban a PRINT és a PLOT függetlenítve vannak egymástól. A PRINT mindig oda ír, ahol korábban abbahagyta egy korábbi print, vagy a bal felső sarokba, attól függetlenül, hogy a PLOT-tal hova rajzoltunk. 4 színes módban a PLOT kijelöli a PRINT helyét is, de attribútum módban nem.

Tuesday, 15 December 2020

Midi zene konvertálása basic programmá

1. Bevezető


Egy korábbi posztban szó volt arról, hogyan szólaltathatunk meg zenét ill. hangeffektet néhány régi számítógép basic programozási nyelvének segítségével. A továbbiak erre épülnek: most bemutatom, egy hagyományos, PC-s, szintetizátoros midi fájlból hogyan tudjuk a zenei hangokat kinyerni és a zenét így basic programmal szólaltatni meg. Egy Enterprise-ra írt basic programot mutatok majd be, amely egy midi fájlt beolvas, és hangjaiból basic programot állít elő. Ez az előállított basic program Enterprise-on lesz futtatható, de röviden kitérek majd arra, hogyan lehet más gépeken is zenét facsarni a midi fájlból beolvasott adathalmazból.
A régi számítógépek nagy része négyszögjel kiadására alkalmas chipet tartalmaz. A négyszögjel egy sajátos számítógépes hangzás, melyet sok számítógép képes kiadni, és más eszközök is, például régebbi játékautomaták. A négyszögjel onnan kapta a nevét, hogy oszcilloszkópos képén négyszögekhez hasonló hullámforma jelenik meg. (Szemben például a szinuszrezgéssel, melyhez a hangvilla hangja áll a legközelebb.) Elektronikus eszközökkel ez a fajta hangzás állítható elő a legegyszerűbben.



Jelentősebb különbségek vannak a régi retro számítógéptípusok hangzásbeli képességeiben. Például a Primo-n egyáltalán nem lehet zenét és hangeffekteket megszólaltatni. A TVC egyetlen hangot tud megszólaltatni egyszerre, a Plus4 két szólamot is kezelhet, az Enterprise három hangot tud megszólaltatni egyszerre és egy zajcsatornát. A Commodore 64 legfeljebb három szólamot tud kezelni, és a négyszögjelen kívül más hullámformák is előállíthatók. (Ezek a paraméterek persze csak alapesetben igazak. Bonyolultabb programozással pl. TVC-n is megszólaltatható több hang is egyszerre, például digitális hangmintákkal.

2. Zeneszerkesztés régen és most. Vagy akár egyszerre mindkettőt?

A midi egy általánosan elterjedt zenei formátum, mely gyakorlatilag a kottát tárolja. Többek között arra jó, hogy ha szintetizátoron játszunk valamit, azt midi formátumba rögzíthetjük, amit később PC-n is vissza tudunk hallgatni (ha a kottához a megfelelő hangszerhangokat is biztosítani tudjuk) vagy átszerkeszthetjük. A netről számos könnyűzenei és komolyzenei darab letölthető midi formátumban, amiket a zenészek különféleképpen hasznosítani tudnak. Szinte minden PC-s program, amivel elektronikus zenét lehet szerkeszteni, támogatja a midi formátumot. Léteznek külön midi fájlok szerkesztéséhez szoftverek, amelyekkel pl. zenét írhatunk kotta formájában, meg tudhatjuk jeleníteni egy midi fájl kottáját, vagy a PC-vel összekötött szintetizátorról vagy midi billentyűzetről játszott zenét rögzíthetjük midi fájlba.
Mire lehet jó midi fájlokat basic programmá alakítani? Mint írtam, a netről sok zene letölthető midi formátumban, és így ezeket könnyen meg lehet szólaltatni a régi számítógépeken is. A másik, amire jó lehet ez, hogy ha valaki maga akar zenét írni egy játékhoz, DATA sorok után számok formájában kéne a hangmagasságot és a hanghosszt beírni. Nem feltétlen könnyen megjegyezhető, hogy melyik hangot melyik szám jelzi. Így jóval egyszerűbb lehet PC-n midi szerkesztővel megszerkeszteni egy zenét, és azt konvertálni át régi számítógépbe, és ott végül basic játékprogramba beilleszteni. Erről fog szólni ez az írás, hogy egy midi formátumú zenét vagy annak részét hogyan lehet átalakítani Enterprise-on (vagy némi átalakítással akár más gépen) megszólaló basic programmá. Videoton és Plus4 gépen bonyolult képlettel számítható ki, hogy egy adott zenei hanghoz milyen értéket kell megadnunk a SOUND utasításban. Zenészek számára ez nem ideális. Enterprise-on még csak-csak ki lehet számolni, hogy hány félhanggal térünk el a 37-es C hanghoz képest, de akinek nincs ebben gyakorlata, annak ez sem túl jó módszer. Zenészek sokkal könnyebben írnak kottát.
Éppen ezért lehet célszerű egy olyan program, mely a midi fájlban található zenét átalakítja SOUND utasítások sorozatává.

3. Hogyan alakítható át a midi fájlban lévő zene SOUND utasítások sorozatává?


Zenét tehát úgy érhetünk el a legegyszerűbben a régi számítógépeken, hogy sound utasítások sorozatát adjuk meg, eltérő hangmagasság és hanghossz értékekkel. Azonban a megadható hangmagasság-értékekkel zsonglőrködni nem túl egyszerű. Hogyan lehetne írni programot, mely a midi fájlban lévő dallamot sound utasítások sorozatává alakítja? Először is, hozzunk létre egy üres midi fájlt, írjunk bele egymás után egy C, E, G hangjegyet azonos hanghosszokkal! Mentsük el, majd nyissuk meg egy hex editorral! Mit látunk?






Feltűnő, hogy a fájl vége felé három rövid, egymáshoz nagyon hasonló bájtsorozat ismétlődik, a képen a 90-nel jelölt érték után. (A 90h (decimálisan 144) az 1. midi csatorna kezdetét jelzi. A 91h (145) a 2. midi csatorna kezdete, a 92h (146) a 3.-é, és így tovább.) Nyilván ezekben tárolódik a három hang, amit a fájlba beírtunk. Mindegyik bájtsorozat 4 elemből áll és a középső két elem mindegyiknél ugyanaz. Ez a két középső elem jelöli a hangerőt és a hanghosszt, ezeket nem állítottuk külön, tehát mindhárom hangnál egyformának kell lenniük. A bájtsorozatok első és utolsó bájtja viszont mind a három esetben eltér, ez jelöli a hangmagasságot, hiszen mindegyik hangnak más hangmagasságot adtunk meg. Ez az első és utolsó bájt mindegyik bájtsorozatnál ugyanaz, az első a hang megszólalását (note on), az utolsó a hang elhallgatását (note off) jelenti. Tehát a kép alapján a 3C jelöli a C hangot, a 40 az E hangot, a 43 pedig a G hangot. Milyen logika alapján tárolódhat itt a hangmagasság? A 3Ch tízes számrendszerben 60, a 40h 64, a 43h pedig 67. Az Enterprise PITCH paraméterénél a C hangnak 37, az E hangnak 41, a G hangnak 44 felel meg. Nézzük csak... 60, 64, 67 és 37, 41, 44. Látható, hogy két szomszédos hangot jelölő szám különbsége a midi fájlban és az Enterprise PITCH értékében is ugyanaz! Lehetséges, hogy ugyanazon az elv alapján működnek az Enterprise PITCH értékei, mint a midiben a hangmagasságot jelölő szám? Bizony ám! A midiben szereplő 60-ból (tízes számrendszerbe átszámolva) előállíthatjuk a 37-et, ha 23-at kivonunk belőle. Tehát, a midi fájlban tárolt hangmagasság-értékekből 23-at kell kivonni ahhoz, hogy az Enterprise sound utasításának a megfelelő PITCH értékét megkaphassuk! A hangmagasság konvertálásához nincs más dolgunk, mint egy programmal beolvasni a midi fájlt, megkeresni benne azt a részt, ahol a hangok kezdődnek, ezt általában 90h jelzi, ahogy a képen is, és utána a hangmagasságok értékéből 23-at ki kell vonni, a kapott értékeket pedig elmenteni, amit aztán egy Enterprise basic program be fog olvasni és a PITCH paramétereként meg fog szólaltatni.
Na, de a hangmagasság önmagában még kevés, a hang hosszára is szükség van. Ez hogyan tárolódhat a midi fájlban? Nyissunk meg újra egy üres midi fájlt, írjunk bele 3 azonos magasságú, de eltérő hosszú hangot, és nézzük meg, hogyan fest ez hex editorban! Próbaképp egy negyed, nyolcad és egy tizenhatod hosszúságú C hangot írtam bele a midi fájlba:



Láthatjuk, hogy a három bitsorozatnál most mindhárom esetben azonos a hangmagasságot jelölő szám, ez 3Ch. Ami különbözik, az az utolsó előtti szám lesz, az első hangnál ez 78h, a következőnél 3Ch, az utolsónál pedig 1Eh. Tízes számrendszerbe átszámolva: 120 (negyed), 60 (nyolcad) és 30 (tizenhatod). Tehát fele olyan hosszú hangot fele akkora számmal tárol a midi fájl. A három kedvenc számítógépem is ugyanezen az elven határozza meg a hang hosszát. Hogy mi lesz egy másodperc hosszú (50-es duration érték), az már attól függ, hogy tempónak mennyi van beállítva a midi fájlban. A midi fájl tempójával nem feltétlen kell foglalkoznunk, mert a konvertáló programban megadhatjuk mi magunk is, hogy a lehető legrövidebb hanghosszoknak (az egészre, félre, negyedre, stb. megadva) hányszorosát akarjuk hallani. Így kísérletezhetünk, hogyan kapjuk a számunkra legmegfelelőbb tempót. Az egyszerűség kedvéért attól is eltekintünk most, amikor a dallam közben a tempó dinamikusan változik.
Nézzük meg, mi történik, ha szüneteket szúrunk be a hangok közé! Ha például az első és második hang közé nyolcad hosszúságú szünetet teszünk, hex editorban akkor a két hang között nem 00 00 fog megjelenni, hanem 00 3C. A 00 tehát azt jelenti, hogy szünet van ott, az ezt követő szám pedig a szünet hosszát jelenti. Ha 00 00 áll ott, akkor nincs szünet.
Ha a negyed kétszeresét (felet) adjuk meg hanghossznak, akkor azt a hex editorban már két bájton ábrázolva láthatjuk. Ez lenne „midis értelmezésben" a duration 240, ezt 81 70-nek ábrázolja a hex editor, azaz 129 112 szerepel. Hogyan lesz ebből 240? Ha 127-nél hosszabb hangot adunk meg, akkor azt már nem tudja egy bájton tárolni, a 128-nak például 81 00 lesz az értéke. A hanghosszt jelölő bájtok első bitje mindig jelzőbit. Ha az értéke 1, akkor az adott bájt feldolgozása után még nem értünk az érték beolvasásának végére, a következő bájt is a hanghossz kifejezéséhez kell. Ha az értéke 0, akkor az adott bájt feldolgozása után elértük az érték beolvasásának végét.
Ha a midi fájlban tárolt hanghosszokat basic duration értékekké akarjuk átszámolni, akkor valami hasonló algoritmust lehet elképzelni:
  • Nullázzuk az értéket tároló változót. (pl.: LET HOSSZ = 0)
  • Beolvassuk a soronkövetkező bájtot. (pl.: beolvassuk a C$ nevű változóba)
  • Ha nagyobb az értéke 127-nél, akkor a tárolt értéket megszorozzuk 128-cal és hozzáadjuk az alsó hét bit értékét. (pl.: LET HOSSZ = HOSSZ * 128 + ORD(C$) - 128)
  • Ha kisebb az értéke 128-nál, akkor a tárolt értéket megszorozzuk 128-cal és hozzáadjuk az alsó hét bit értékét. (pl.: LET HOSSZ = HOSSZ * 128 + ORD(C$))
  • Ha nagyobb az értéke 127-nél akkor folytatjuk a 2. ponttól.
Köszönjük ErgoGnomik segítségét a hanghossz kiolvasását elvégző programrész megírásában!
A fenti ábrákon is látható bájtsorozatokban, melyekből most a hangmagasságot és az időtartamot olvastuk ki, a hangerő is megtalálható. Ez a bájtsorozatoknak a második eleme. A fenti ábrákon ez mindig 64h volt. Semmi akadálya, hogy ezt is kiolvassuk, és a basic program a midi fájlhoz hasonlóan változtassa a hangerőt is, de az egyszerűség kedvéért most csak a hangmagassággal és a hanghosszal foglalkozunk.

Tegyük hát át gyakorlatba az eddigieket! 
Írjunk a fentiek alapján egy programot, mely egy midi fájl sávját basic programmá alakítja, mely az adott dallamot lejátssza! Ahogy láthattuk a fenti ábrákon, a midi fájl eleje fejléc, csak a 90h-val jelölt bájttól kezdődik maga a dallam. Így az első bájtokat elég csak beolvasnunk, és keresni kell azt a bájtot, ahol 90h (144) áll, például így:

100 OPEN #1:"dallam.mid" ACCESS INPUT
110 DO
120   GET #1:A$
130 LOOP UNTIL A$=CHR$(144)

Hozzunk létre egy-egy tömböt a leendő basic programban tárolt dallam hangjainak, egyet a hangmagasságnak, egyet a hossznak!

140 NUMERIC TRACK(1 TO 3,1 TO 500),DUR(1 TO 500)

Feltételezzük, hogy az adott midi sávban csak egyetlen szólam lesz, tehát nem szólal meg egyszerre két hang. Egyelőre maximum 500 hangból álló dallam konvertálására készülünk fel. Szükség esetén ezt a számot megnövelhetjük 500-nál nagyobbra. A TRACK tömb kétdimmenziós, az első paraméter a sáv (szólam) számát jelöli. Egyelőre egyetlen szólam konvertálásával fogunk foglalkozni, később megnövelhetjük a szólamok számát. A szólam (aktuális midi sáv) száma lehetne Enterprise esetén 1 és 3 közötti szám, vagy akár 4 is, ha a zajcsatorna hangjait is valamilyen módon a midi fájlban adjuk meg. Ennek értékét a LAYER nevű változóban adom meg, esetünkben ez mindig 1 lesz. A konvertáló programrészt többször is meg kell hívni több szólam esetén, más LAYER értékekkel. TVC-n elvileg csak egy szólam lehet, Plus/4-en kettő, azonban ha nagyon gyorsan (1/50 másodpercenként) váltogatunk két (vagy akár több) hangot, akkor többszólamú hangzáshoz hasonlót kaphatunk, így TVC-n is érdemes legalább két szólamot fenntartani. Plus/4-en pedig akár a zaj megszólaltatásához használhatunk egy külön midi sávot, ami gyakorlatilag harmadik szólamnak számít, csak nem szólhat egyszerre a másodikkal, hiszen ugyanaz a hangcsatorna állítja elő a kettőt.
A fenti hexdumpokon látható, hogy a sáv végét FFh 2Fh 00h (255, 47, 0) bájtsorozat jelzi. A basic programban addig kell konvertálni a hangmagasságot és a hanghosszt, amíg ezt a bájtsorozatot nem találjuk. Beolvassuk tehát azt a négy bájtot, mely a hangmagasságot, hangerőt, hanghosszt tárolja. A bájtsorozatokat nem FOR, hanem DO ciklussal fogjuk kiolvasni, ezért előtte megadunk egy ciklusváltozót, ez legyen mondjuk SG nevű:

 LET SG=1
 DO
   GET #1:A$
   GET #1:B$
   GET #1:C$
   IF A$=CHR$(255) AND B$=CHR$(47) AND C$=CHR$(0) THEN EXIT DO
   GET #1:D$
...
   LET SG=SG+1
LOOP

Az A$-ban megtalálható a hangmagasság, a B$-ban a hangerő, a C$-ban a hanghossz, és a D$-ban újra a hangmagasság (note off esemény). Lehetséges, hogy a hang hosszabb annál, mint amit egyetlen bájton lehet tárolni, így még utána nem a note off esemény jön, hanem még mindig a hanghosszt jelölő bájt. Ehhez meg kell nézni a C$-ban beolvasott bájt jelzőbitjének az értékét, és ha az 1, akkor még a D$ beolvasása előtt egy másik, a hanghosszt definiáló bájtot is be kell olvasnunk, vagy nagyon hosszú hang esetében többet is.

555    LET HOSSZ=0
556    IF C$>CHR$(127) THEN
557      LET HOSSZ=HOSSZ*128+ORD(C$)-128
558      GET #1:C$
559      GOTO 556
560    END IF
561    LET HOSSZ=HOSSZ*128+ORD(C$)

Ahogy szó volt róla, a midi fájlban tárolt hangmagasság 23-mal nagyobb szám (decimálisba átszámolva), mint az annak megfelelő pitch érték Enterprise-on. Így a megfelelő pitch értéket könnyen előállíthatjuk:

610    LET TRACK(1, SG)=ORD(A$)-23

A hanghossz adott esetben akár ugyanannyi is maradhat, mint amennyit a midi fájl megadott, de ez általában hosszabb hangot eredményez, ez csak nagyon lassú zenéknél lehet jó. Legegyszerűbb, ha ezt az értéket elosztjuk mondjuk 5-tel. A végén előállított basic programban még úgyis szerkeszthetjük ezt is ízlés szerint. Persze lenne itt is több lehetőség is, hogy például a lehető legrövidebb hangnak valahányszorosát vegyük, de az egyszerűség kedvéért maradjunk az 5-tel osztásnál:

612   LET DUR(SG)=HOSSZ/5

A hangjegyet leíró négy bájt után ki kell olvasni a következő két bájtot: GET #1:E$ és GET #1:F$. Az E$-ban 00 lesz, az F$-ban pedig vagy szintén 00, vagy pedig, ha szünet következik, akkor a szünet hosszát leíró bájt. Hosszú szünet esetén a következő bájtok is a szünet hosszát írják le. Szünetnek célszerű hangmagasságnak Enterprise-on 127-et adni meg, ami a legmagasabb hang, olyan magas, hogy már nem hallatszik. Ennek annyi ideig kell szólnia, amíg a 00h utáni hanghossz jelzi. Mivel már az előbb szerepelt az a rész, mely a hanghosszt jelölő bájtokat értelmezi, és a szünet hosszát is ugyanúgy tárolja a midi fájl, érdemes lenne előbb megvizsgálni, szünet jön-e, és ha igen, akkor simán továbbmenne a program a hossz generálására. Tegyük az esetleges szünetet tartalmazó bájtok kiolvasásáért felelős részt rögtön a ciklus elejére! Először megvizsgálja a program, 00 00 áll-e a két bájtban. Ha igen, akkor nincs mit tenni, újra be kell olvasni a két bájtot. Ha azonban a két bájt közül csak az első 00, akkor szünetet kell letárolni hangmagasságként, és a következő bájtokból generálni kell a szünet hosszát. Ha pedig éppen nem 00 áll az első bájtban, hanem hangmagasságot jelölő bájt, akkor utána ugyanúgy annak a hosszát kell kiolvasni és értelmezni:

  510   DO
  520     GET #1:A$
  530     GET #1:B$
  535     IF ORD(A$)=0 AND ORD(B$)=0 THEN GOTO 520 !00 00, újra elölről
  536     IF ORD(A$)=0 THEN C$=B$:GOTO 555  ! Szünet lesz, a hangerő nem releváns, annak kiolvasását át kell ugrani, jöhet a hossz, amit C$-ban tárolunk.
  540     GET #1:C$ ! Ha nem szünet volt, akkor most olvassuk be a hosszt.
  550     IF A$=CHR$(255) AND B$=CHR$(47) AND C$=CHR$(0) THEN EXIT DO ! Ez a 3 bájt jelzi a sáv végét. Ha ezek jönnek, befejezzük az átalakítást, kilépünk a ciklusból.
  555     LET HOSSZ=0 ! Elkezdjük a hossz generálását, ehhez kiindulásként 0-ról indulunk.
...
  600     IF ORD(A$)<>0 THEN GET #1:D$ ! Ha nem szünet volt (00h), akkor még van itt egy note off jelzés, ennek a bájtját is be kell olvasni.
  610     LET TRACK(LAYER,SG)=ORD(A$)-23 ! A PITCH értékét állítjuk elő az aktuális hangjegynek. A midi bájt decimális alakjából 23-at kell levonni, lásd fent.
   611     IF ORD(A$)=0 THEN LET TRACK(LAYER,SG)=127 ! Ha 00h volt az elején, azaz szünet, akkor hangmagasságnak 127-et célszerű megadni, lásd fent.
  612     IF LAYER=1 THEN LET DUR(SG)=HOSSZ/5 ! Átszámítjük a midiben tárolt hosszt a DURATION-ban megadható értékre. Ennél elegánsabb módszerrel is lehetne, egyelőre beérjük ezzel.
  613     LET SG=SG+1 ! A ciklusváltozót növeljük.
  670   LOOP 

Ha több szólamot akarunk beolvasni, akkor a következő midi sáv(ok) elejéről is el kell kezdeni az adatok beolvasását, a LAYER változó értéket előtte eggyel megnövelni, így a TRACK tömb első dimenziójának következő értéktartományába kerülhetnek a következő szólam(ok) hangjai.
Több szólam esetén is a hanghosszt csak egyszer kell beolvasni. Ez azt jelenti, hogy akárhány szólam van, azok összes párhuzamos hangja teljesen egyszerre szólal meg és hallgat el, hiszen egy hanghosszt rendelünk akár több szólamhoz is. Tehát, nem tehetjük azt, hogy míg pl. az első szólamban egyetlen hang szól, addig a második szólamban egymás után több hang is megszólal, hanem ilyen esetben az első szólamban is a második szólam minden egyes hangjával párhuzamosan meg kell szólaltatni ugyanazt a hangot. Erre a basic nyelv korlátai miatt van szükség. Így nem is lehet olyan midi fájlt helyesen konvertálni, ahol az egyes szólamok párhuzamos hangjainak hossza nem egyezik meg. Tehát nekünk kell megszerkeszteni úgy a midi fájlt, hogy az megfelelően szóljon majd, ha több szólamot is konvertálni akarunk. Ezért ugyan kiolvassuk több szólamhoz is a hangok hosszát, de elég csak egyszer letárolni, amikor a legelső szólam hangjait olvassuk ki. Ezért is van a 612-es sor elején az IF, hogy csak az első szólam konvertálásakor vegye figyelembe a gép a hang hosszát.

Ezzel tehát kiolvastuk a midi fájlból a hangjegyek magasságának és hosszának az értékeit, átalakítottuk Enterprise PITCH és DURATION paraméterekkel értelmezhető számokká, letárolva mindent a TRACK és a DUR tömbökben.
Ha nem Enterprise, hanem TVC vagy Plus/4 (C16) gépre akarjuk átkonvertálni a midi fájlt, akkor a hanghosszhoz nem kell más módszer, viszont más számokat kell az egyes PITCH értékekhez hozzárendelni. Ez nem is olyan bonyolult:

1. A dokumentációból ki kell másolni DATA sorokba az egyes hangmagasságoknak megfelelő értékeket a legmélyebbtől a legmagasabb felé haladva.

2. Létre kell hozni egy tömböt (pl. NOTE(31 TO 110), amelybe majd beolvassuk a DATA sorokból az egyes TVC-s ill. Plus 4-es  PITCH értékeket, amit hozzárendelhetünk egy-egy midis hangmagasság értékhez.

3. A 610-es sorban nem LET TRACK(LAYER,SG)=ORD(A$)-23 lesz, hanem LET TRACK(1,SG)=NOTE(ORD(A$))

4. A szünetet nem 127-tel jelöljük, így TVC-n 4095-öt, Plus/4-en pedig 1022-t kell megadni a 611. sorban.

A TVC hangtartománya kisebb, mint az EP-é, a Plus/4 hangtartománya pedig még kisebb. Ezeken a gépeken kevesebb a mély és a magas hang is, nagyon mély és nagyon magas hangok kibocsátására már nem alkalmasak. Így a nagyon mély és nagyon magas hangokhoz érdemes lehet szünetet hozzárendelni, hogy konvertálásnál ne legyen hibaüzenet a nem létező hangmagasság érték miatt, ha esetleg túl magas vagy túl mély hang fordulna elő a midi fájlban.

Végezetül pedig létre kell hozni egy kimeneti fájlt, és abba szöveges formátumban kinyomtatni a basic programot, először a lejátszó részt, majd DATA sorokba rendezve a TRACK és a DUR tömb elemeit. Lejátszó résznek megadhatunk több lehetőséget is, amihez a konvertálás elején megkérdezzük a felhasználót, hogy melyiket szeretné választani. Bár ez nem olyan fontos, valószínűleg úgyis később kísérletezéssel fogjuk beállítani a legmegfelelőbb hangzást és azzal fogjuk végül elmenteni a programot.

Ez a módszer persze nem igazán kifinomult. Lehetne DO helyett FOR ciklusba is tenni a bájtsorozatok kiolvasását, és ehhez előtte kiolvassuk a midi fájl elejéről, hogy milyen hosszú a sáv, mennyit kell a FOR változóba írni. Igaz, végeredmény szempontjából ennek nincs jelentősége, mert az FF 2F 00 mindig jelzi a sáv végét, ahol abba kell hagyni.
Egy midi fájl a hangjegyek hangmagasságán, időtartamán, hangerején kívül sok más információt is tárolhat, amire a konvertáló program nincs felkészítve, így ilyen esetben a konvertálás nem sikerül. Az általam ismertetett módszer nem arra való, hogy tetszőleges midi fájlból kinyerjük a dallamot és basic programmá alakítsuk, hanem hogy mi magunk midi formátumban szerkesszük meg a zenét a basic programba az elejétől kezdve, így eleve nem is teszünk olyat a midibe, ami a basic program létrehozásához nem szükséges. Lehet netről letöltött midiket is konvertálni, csak előbb egy midi szerkesztővel át kell nézni azt és törölni kell belőle a számunkra felesleges információkat (pl. vezérlőket, hajlításokat). Arra is ügyelni kell, hogy egy midi sávon belül egyszerre mindig csak egy hang szólhasson. Ahol több hang van, csak az egyiket kell meghagyni, a többinek esetleg külön midi sávokat hozhatunk létre.
Több szólam esetén problémát okozhat az a jelenség, amikor nem szól egyszerre mindegyik szólamon zene, hanem valamelyik szólamon szünetek vannak. Ez azért problémás, mert a szünetet is fel kellene osztani olyan részletekre, mind ahogy a másik szólamon a hangok követik egymást. Erre egy megoldás lehet, ha olyan hangokat írunk a midi szerkesztővel a szünet helyére, melyek máshol nem fordulnak elő a zenében, így akár konvertálás után a txt fájlban ezeket az értékeket átírhatjuk a szünetet jelölő számokra. Ha pl. TVC-n a gyorsan, felváltva szólnak a két szólam hangjai lehetőséget választjuk, akkor a szünetet tartalmazó helyekre a másik szólam hangjait kell bemásolni.

5. Zárszó


--
A midibe konvertálásról a Plus4-re vonatkozó ismeretek a 264-es család minden tagjára érvényesek. Semmi akadálya, hogy a midiből basicbe konvertáló programban a DATA sorokat átírjuk úgy, hogy a CPC-nek megfelelő hangmagasság értékek szerepeljenek benne.
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, ezeket kell beírni a DATA sorokba, de a legnagyobb számtól a legkisebb felé haladva! A NOTE() tömb tartományát is kibővítjük, hogy 8 oktávnyi hangjegyek férjenek bele, és be kell állítani a szünetet jelölő számot is megfelelően. Mivel a hanghosszt jelölő szám pont fele olyan hosszú hangot jelöl, mint a másik három gépen, a hanghosszok átszámítását is kicsit módosítani kell.
Így aztán Spectrumra sem túl bonyolult átkonvertálni a hangmagasságot midiből, csak nem 23-at kell kivonni a midiben tárolt bájt értékéből, hanem kicsit többet, kb. 60-at, hogy a BEEP paraméterének megfelelő értéket megkapjuk. A hanghossz konvertálásánál persze be kell vetni valami praktikát, hiszen az 1-es BEEP érték és mondjuk a midiben megadott 120-as érték (negyed hosszúságú hang) nincsenek túlzottan köszönőviszonyban egymással. Talán a legjobb, ha 120-szal elosztjuk azt, ami a midi fájlban szerepel hanghosszként - adott tempó esetében...

Tuesday, 21 July 2020

A számrendszerekről


Ez az írás azoknak szól, akik még nem is nagyon hallottak a számrendszerekről. Egy kis bevezető, az alapok megértéséhez. Basic programozáshoz talán nincs túl nagy szükség más számrendszerekre a tízesen kívül, bár az elv jól jöhet.

A hétköznapokban a tízes számrendszert használjuk. A tízes számrendszer azt jelenti, hogy tíz számjegyet használunk összesen (a 0-val együtt, így 0 és 9 között összesen tíz számjegy van). Mivel kilencnél nagyobb számot jelölő számjegy nincs, a tízet már két számjeggyel tudjuk kifejezni: a legnagyobb értéket jelölő számjegynél (9) nagyobb nincs, így helyette a legkisebbet, a 0-át írjuk, viszont elé teszünk egy 1-est. Ez a két számjegy fejezi ki azt az értéket, ami a 9 utáni eggyel nagyobb szám. A két számjegy értékét külön helyi értéknek nevezzük. A tízes helyén a tíznél kisebb számoknál is van helyi érték, ami nulla, ezt azonban nem szokás kiírni (pl. az 1 úgy is felírható lenne, hogy 01, a 2 úgy, hogy 02, stb.) Ha a tízhez hozzáadogatunk 1-et, és újra elfogynak a számjegyek 19-nél, jön a váltás újra, és 20-at írunk., és ez így megy a végtelenségig.

Használhatunk más számrendszereket is, ahol tíznél több vagy kevesebb számjegyet használunk (a nullával együtt). Ilyenkor a „váltás” nem tíznél jön, vagyis nem tíznél írunk eggyel több számjegyet az adott érték jelölésére, hanem akkor jön, ahányas a számrendszer. Például a legkisebb elképzelhető számrendszer a kettes. Egyes számrendszerről azért nem beszélhetünk, mert abban az egyetlen számjegy a 0 lenne, ezzel nem fejezhetnénk ki nagyobb értéket. Illetve az egyetlen számjegy lehetne az 1, ami 1-et jelöl. Akkor a kettőt 11, a hármat 111 jelölné, ezzel azonban már tíz alatt is komoly problémáink lennének, ha így akarnánk a számokat jelölni.
Maradjunk tehát abban, hogy a legkisebb számrendszer a kettes. Itt a 0 mellett még egy számjegy létezik, ez az 1, tehát összesen két számjegyünk van. Ezért már a kettőt sem tudjuk egyetlen számjeggyel felírni. A kettes számrendszerben a kettőt jelöljük úgy, hogy 10 (kiolvasva nem „tíz", hanem „egy-nulla"). Ha ehhez hozzáadunk egyet, 11 lesz, ez jelöli a hármat. Ha ehhez is hozzáadunk egyet, megint kevés a számjegy, így megint bővül a helyi értékek száma, így 100 fogja jelölni a négyet. 101 jelöli az ötöt, 110 jelöli a hatot, 111 jelöli a hetet, 1000 jelöli a nyolcat, és így tovább.
Használhatunk olyan számrendszert is, ahol még a tízet és a tíznél nagyobb számokat is külön számjegyekkel jelöljük, például nagybetűkkel. Így ha a kilenchez hozzáadunk egyet, akkor azt jelölhetjük A-val, ez a tíz. A tizenegy lehet B, a tizenkettő C, a tizenhárom D, a tizennégy E, a tizenöt F. Ha csak a tizenhatnál alkalmazunk helyi érték váltást, és a tizenhatot jelöljük 10-val (kiolvasva itt a 10 ugyancsak „egy-nulla”), akkor ez most a tizenhatos számrendszer lesz. Hozzáadogathatunk mindig egyet az 10-val jelölt számhoz: 11 az tizenhét, 12 az tizennyolc … 19 az huszonöt, a következő számot pedig 1A-val jelöljük, ez a huszonhat, utána 1B a huszonhét, 1C a huszonnyolc … 1F a harmincegy, a 20 pedig harminckettő. A 30 a negyvennyolc, a 40 a hatvannégy:



hexadecimális (16-os)
számrendszerben
decimális (10-es)
számrendszerben
10
16
20
32
30
48
40
64
50
80
60
96
70
112
80
128
90
144
100
256
200
512
300
768
400
1024
1000
4096


Sunday, 21 June 2020

Az Aknakereső működési elve

A Windowsban alapból benne van az aknakereső játék, Minesweeper néven is ismert.




Elgondolkoztam, hogy ha ilyen játékot akarnék írni basicben, akkor hogyan kéne megírni. Maga az irányítás nem probléma, de amikor odakattintunk egy mezőre, és végignézi a gép a fél pályát és felfedi az üres mezőket minden irányban a falakig, és még azt is figyelembe veszi, ha útközben nem üres a pálya, és azt is körbekerüli... ebbe belegondolva elvesztettem a fonalat. Ha üres mezőt talál, akkor abból mindig nyílhatnak újabb és újabb üres mezők, és erre az összes mezőre hivatkozást letárolni valahol, majd ha megnéztük, törölni a tárolási helyéről... nem is tudom elképzelni, ezt hogyan lehetne megcsinálni. Egyelőre még gondolkozom az elven. Addig is ErgoGnomik fórumtárs elképzelése az algoritmusról:

  • Vizsgáld meg a kattintott mezőt! Ha bomba, véget ért a játék, ha bombával szomszédos mező jelenítsd meg a számot és vége a műveletnek.
  • Jelenítsd meg az üres mezőt!
  • Vizsgáld meg a körülötte található nyolc mezőt! Ha pályán kívüli, akkor nem csinálsz semmit, ha bomba, akkor hiba történt, ha szám, akkor jelenítsd meg, ha üres, akkor jelenítsd meg, és vedd fel a mezőt egy listába!
  • Válaszd a lista soron következő (kezdéskor első) elemét!
  • Vizsgáld meg a körülötte található nyolc mezőt! Ha pályán kívüli, akkor nem csinálsz semmit, ha bomba, akkor hiba történt, ha szám, akkor jelenítsd meg, ha üres, akkor jelenítsd meg, és vedd fel a mezőt egy listába!
  • Folytasd a 4. ponttól, amíg el nem fogynak a lista elemei!
Kicsit gyakorlatiasabban:

Miután az IS-BASIC-ben nincs igazán dinamikus adatszerkezet, ezért nincs más hátra, mint tömbökkel helyettesíteni. Mivel nem egyszerű pontos becslést adni arra, hogy maximum mennyi elem kerülhet az ellenőrzés során függő elemként bele, mondhatjuk azt, hogy a játékterület nagysága mínusz az aknák száma legyen a tömb mérete. Ezzel biztosan felülbecsültük az egyszerre tárolandó elemek számát.

Lehet két tömb, amiben a megjegyzett elemek vízszintes és függőleges koordinátáit tárolod, nyilván egy koordináta pár a két tömb azonos indexű elemébe lesz beírva.

  • Kell egy változó (pl. E), ami mindig a lista aktuális elejére mutat, aminek induló értéke a tömb első indexe.
  • Egy másik változó (pl. V) mutasson mindig a lista aktuális utolsó elemére, ami induláskor szintén az első tömbindex. Ez utóbbi változót mindig eggyel növeled, amikor új elemet veszel fel.
  • Amikor feldolgozod a listába beírt elemeket, akkor kiolvasod az első változóhoz (E) tartozó tömbelemet, és növeled ezt a változót addig, amíg az ismételt feldolgozásokkal el nem érsz a lista vége változó (V) értékéig.
  • Közben természetesen egy harmadik változót (pl. K), aminek a kiinduló értéke a feldolgozás megkezdésekor vegye fel a vége pozíció értékét, arra használsz, hogy adminisztrálhasd az újonnan hozzáadandó elemeket az értékének hozzáadásonkénti növelésével.
  • Ekkor meg lehet tenni, hogy a vége és a harmadik változó közötti elemeket a tömb elejére másolod, az eleje változót alapértékre állítod, a vége változó új értékét a harmadikból a vége kivonásával (V=K-V) kapod, a harmadik meg vegye át a vége értékét (K=V), és újra megkezdheted a feldolgozást.
  • A feldolgozásnak akkor lesz vége, ha az előre másolás megkezdésekor a vége és a harmadik változó értéke egyező (V=K).
Egy másik probléma lehet a program megírásakor az aknák elhelyezésével kapcsolatban. Elvileg véletlenszerűen szórhatjuk szét az aknákat a pályán. Azonban figyelni kell, hogy bizonyos helyzeteket elkerüljünk, amikor a játék nem lesz teljesíthető logikával, csak szerencsével. Például, amikor egy mezőt mind a 8 oldalról körbevesznek aknák, nem tudhatja kikövetkeztetni a játékos, mi van azon a mezőn. Vagy, ha a pálya szélénél, ill onnan kettő karakterrel beljebb váltakozva fordulnak elő aknák, akkor se lehet mindig egyértelműen tudni, hogy egyes aknák mögött mi lehet.  Lehetséges, hogy a windows-os aknakereső sem mindig véletlenszerűen szórja el az aknákat, hanem előre elkészített pályákon játszódik a játék.

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.