HTRIS - Part 1

HTRIS - Part 1

Aug 26, 2023

Strukturált programozás a Z80-as assembly nyelvben.

Az alábbi játékprogram, a közismert TETRIS oktatási céllal készült verziója, a 80-as években használatos HT-1080Z típusú, informatikai oktatást segítő iskola számítógépre.

Mivel a használatos grafikai felbontás (64*48 pixel), és a CPU is megegyezik, így a program adaptálva lett a 80-as évek angliai kis kedvencére, a SINCLAIR ZX81-re.
Később a magyar fejlesztésű Homelab2-re is portoltam.

Hosszasan lehetne elemezni, hogy mi is az a strukturált programozás, hogyan használjuk, de itt most nem ennek a kivesézése a cél. A Wikipedia jól leírja, akinek ez ismeretlen, ott bővebben olvashat róla. A tömör lényeg: A programot több kisebb, egymáshoz kapcsolódó algoritmusokra, egy-egy feladatot ellátó blokkokra osztjuk. Ezek lesznek a al-programok (subrutinok). A fő-program csak ezek irányításával foglalkozik.
Ennek a cikksorozatnak nem az assembly nyelvben való programozás elsajátításának a célja, és nem is a program (forráskód) sorról sorra történő ismertetése. Némi jártasság nem árt hozzá, de ha nincs, az sem baj, még a végén kedvet kapsz a Z80-as gépek assembly nyelvű programozásához.
A célom, annak bemutatása, hogy hogyan építsünk fel egy programot, hogyan szervezzük meg a szubrutinok struktúráját, egymásba ágyazását. Ez az ismeret más nyelveken is hasznosítható. Bemutatom, hogy hogyan tároljunk változókat, adatokat, adatmátrixokat.
De azért egy-két érdekes, trükkös megoldást, szubrutint teljes egészében közkincsé teszek.

A program szerkezeti felépítése

Elsőként osszuk fel a programunkat a legfőbb részekre. Ez nem csak a valós forráskódot jelenti, hisz lesznek előre elkészített grafikák is, és a Z80 regiszterein kívül még nagyon sok változóra és adatmátrixokra is szükség lesz. Ezeket mind a forrásba integráljuk bele. Még azoknak a helyét is meg kell tervezni, amiket a program futás közben fog generálni.

  • Fő-program

  • Alprogramok (subrutinok)

  • Adatbázis

    • Változók

    • Grafikai elemek (alakzatok)

    • Munkaterület (pálya területek)

A játékmotor

Mielőtt a programot tovább boncolgatnánk, nézzük át, hogy hogyan is épül fel a játék. Ezt hívjuk a játék motorjának, vagy más néven a mechanikájának, illetve logikájának. Ez lesz valójában a főprogram alapja.

  • Adott egy X szélességű, és Y magasságú játéktér.

  • Van 7 különböző alakzat.

  • A gép random választ egyet, és elkezdi felűről "leejteni". Ha már nem tudja, a játékos vesztett.

  • A játékos jobbra-balra tologatva, és forgatva tudja a helyére irányítani az alakzatot. Minden egyes megtett sorért kap egy pontot.

  • Minden 100. sornál egy szinttel feljebb lép. A 2000. sornál győz.

  • Ha az alakzat már leért (nem tud tovább esni), megtörténik a kitöltött sorok ellenőrzése. Ha van ilyen, az kitörlődik, a felette lévők lejjebb esnek, és a játékos kap 10 bónusz pontot. A játék folytatódik a 3. ponttól.

Most, hogy már ismert a feladat, kezdjük felépíteni a programot. Mielőtt nekiállnánk kódokat írogatni, tervezzük meg a játéktáblát, alakzatokat, változókat, és hozzuk létre ezek adatbázisát.

Az adatbázis

Ez az a memóriaterület, ahol a program forráskódján kívül, minden olyan adatot tárolunk, ami a program működéséhez szükséges. Lehetnek ezek előre meghatározott értékek, de a program futása közben is létrejöhetnek (generálódhatnak). Ezeket három főbb részre bontva mutatom be.

A munkaterület

Ez maga a játéktábla, ahol az alakzatok mozognak és gyűlnek össze egy tornyot alkotva. Ez a terület nem látható, mégis a játék legfontosabb része. Ezért is kezdjük ezzel. Sokan egyből a képernyőre rajzolnák az alakzatokat. Igen, lehetne úgy is, de akkor egy bonyolultabb pozíció-ellenőrzés kellene, illetve még villogás is előfordulhat az átrajzolások során, vagy bonyolultabb kirajzoló rutin kellene, ennek kizárására.

Én az úgynevezett "Shadow Screen", árnyék képernyő módszert preferálom. Nem kell a lapozhatóság, ezt a HT nem is tudja. A ZX81 tudja, de ott sincs most erre szükség, nem úgy mint a Krystal demómban.

A HT 16 soros karakteres képernyővel rendelkezik, így adja magát, hogy a pája is annyi lesz. Pontosabban, 15 sor mozgástér, plusz egy sor talaj. A mátrix így 16 soros lesz. Oszlopok száma legyen 10. A teszteléshez jobbra-balra egy-egy falat is raktam, így már 12. Kerekítsük 16-ra, mert azzal a debugerben szépen látható mezővé válik. Ez a mező a program által generált, úgynevezett háttér lesz. Kezdőcíme: 7800h-tól. Nem csak ezt az U alakot tartalmazza, hanem ide kerülnek majd berajzolásra a leesett blokkok is.

A játéktér valójában 3 adatmátrix. Az egyik az előbb említett háttér. A második egy olyan üres mező, ahova az aktuális alakzat (SHAPE) kirajzolódik, az éppen aktuális pozícióra. Ez a mátrix két sorral több, hogy az alakzat "előbújhasson a tetején". Ennek a kezdőcíme: 7A00h-tól.

Végül, a harmadik mező nem más, mint az előbbi kettő összemásolt eredménye, de az alakzat mezőből csak az utolsó 16 sor, azaz, a 3. sortól másolódik be. A kezdőcíme: 7900h-tól, így a másik kettő között foglal helyet. Pontos helyüknek, sorrendjüknek nincs jelentősége, csak legyenek valahol a szabad memóriaterületen, és 8000h alatt, hogy 16K-s gépen is fusson.

A játéktér ilyen módon történő felosztásának az okára, a továbbiakban fény derül.

A grafikai elemek

Még ne a "csilli-villi" grafikára gondoljunk. Pusztán az alakzatok, és a játéktér fizikai alakját, mondhatni egy maszkot/vázszerkezetet kell csak itt letárolni, illetve azok kiegészítő információit.

A falak és a talaj "képe" az alábbi egyszeű két sorral meghatározható. Valójában erre csak a teszteléshez és az egyéb algoritmusok működéséhez van szükségünk.

Az alakzatokat a megszokott négy részestől eltérően csak 3*3-as mezőbe rendeztem, így az I alakzat csak 3 egység hosszú. Forgatását a középpontban végezzük. Mind a 4 pozícióját binárisan ábrázolva a 3*3-as mátrixban az alábbiakat kapjuk.

Az alakzat első pozícióját sorokra bontva, s egymás után rakva, ezt kapjuk: 010010010
Ezt megtesszük a másik 3 pozíciójával, és a többi alakzaton is. A kapott eredményeket az assembler adatbázisban az alábbi módon tárolhatjuk. A nullákat és egyeseket @ illetve j karakterre cseréltem, a későbbi könnyebb használat miatt. A falak és a talaj jelölésénél is ezért használtam azokat a karaktereket.

A 4 részre tagolás csak a jobb átláthatóság miatt van, de szükséges is, hisz azon kívül, hogy az egyes alakzatok kaptak egyedi címkét (SHAPE1-SHAPE7), az egyes elforgatott pozíciójuk a kezdőcíme még nem ismert. Ezt egy kezdőcímet módosító adatsorral tudjuk kompenzálni. Az elsőhöz nem kell hozzáadni semmit. A többinél, 9-esével emelkedő számsor ad megoldást. Egy bájtban is letudnánk írni az értéket, de azért van a "szó", azaz duplabájt használva, mert az egyszerűen hozzáadható a kezdőcímet tároló regiszterhez (pl.: HL), s nem kell az esetleges túlcsordulással is foglalkozni.

Van még egy paramétere az alakzatnak, amit figyelembe kell vanni, ez pedig a szélessége. Mivel, forgatás közben nem egyforma széles és nem is mindig a 3*3-as blokk bal szélétől kezdődik, ezért az x irányú mozgathatósága (faltól-falig) nem írható le két egyszerű számmal. Itt is ki kell valamit találni. A leg egyszerűbb az, ha a minimum (bal), és a maximum (jobb) pozíciókat alakzatonként, azok elforgatott verzióit is, egy-egy táblázatban letároljuk. A szükséges számokat a háttérmező bal szélétől számolva hozzuk létre.

A változók

A magas szintű programozási nyelvekkel ellentétben, a gépikódú programozásban nagyon nehézkes a változók használata. Vannak az ismert regiszterek (Wikipédia) és kész. Lehet ezeket verembe pakolgatni, meg cserélgetni őket, de még így is kevés tud lenni nagyobb programok esetén. Főleg akkor, ha több alprogram is ugyanazon változók értékeivel dolgozna. Az assembly ad némi lehetőséget plusz változók használatára, de azok inkább amolyan technikai változók, amik a forráskód lefordítása után fix számokká alakulnak. Ezek a címkék (label), amik az urási cimeket, vagy egyéb tárcímet meghatározó változók.

A megoldás, a magas szintű programozási nyelvekhez hasonló. Ami a háttérben történik, s nem látunk, de gondolom ismerős, ha a BASIC nyelv váltózó-tárolási módszerét említem. A memóriában létrehozunk egy változóhalmazt, ahol a címkék lesznek a neveik, és az algoritmusok így tudnak majd hivatkozni rájuk. Ezekről a helyekről töltjük be az éppen szükséges változót az adott regiszterbe. Ha kell, ide vissza is írhatjuk a változót, ha a vele történt változást tárolni akarjuk. Például az alakzat X, Y koordinátái. Egyes változók csak konstans számok, amik időnként más változók kezdő értékét határozzák meg. Például az alakzatok leesési sebessége. A változók kedőcímét a játékmezők utánra állítom. Természetesen, az itt látható változókat a program írása során folyamatosan hoztam létre, ahogy szükség lett rájuk. Meghatározhatjuk előre is a szükséges változókat, de biztos lesz olyan amit menet közben találunk ki, mert nem gondoltunk rá.

A fent említett grafikai adatbázis pedig ezek után következhet. Mi mással, ha nem a játék során kiírandó üzenetekkel.

Letöltés

A programot az ENTARY GAMES oldalamról lehet letölteni.

Előzetes

A következő részben a főprogram, s annak működése kerül ismertetésre. Meghatározásra kerülnek a főbb alprogramok, azok kapcsolódási módjai. Ezt egy folyamatábrával is tervezem bemutatni.

Köszönetnyilvánítás

Köszönet Képes Gábornak, a játék névadójának grafikai ötletéért, hogy a címlapon a Magyar Parlament épületének sziluettje szerepeljen, és azért, hogy a programot tesztelte.

Köszönöm a figyelmet!

Ha tetszett a cikkem, és meghívsz egy kávéra, bátorítasz, hogy írjak még hasonló érdekességeket számodra.

Enjoy this post?

Buy NICKMANN Studio a coffee

More from NICKMANN Studio