Spectrum Programok átírása
Általános áttekintés
Módosítás hardware oldalról
Hasznos tanácsok
A kód átvitele
A képernyő
A betöltő rutin
Megszakítás
Billentyűzetfigyelés
ROM hívások
Botkormány
Színkonverzió
128K-s programok
Zozosoft instrukciói a ZX Spectrum programok átírásáról
István instrukciói a 128K-s hangok átírásáról
Scan, OCR: gafz, 2006
Az ENTERPRISE gép piacra kerülésekor, és azóta is nagy probléma a programok hiánya (akár játék-, akár felhasználói programról is legyen szó). Ezzel szemben ott van pl. a SPECTRUM, amely a maga néhány ezer (!) software termékével a viszonylag jól ellátott gépek közé tartozik. Előbb-utóbb mindenkiben felvetődik a kérdés: hogy lehet a SPECTRUM programokat futtatni az ENTERPRISE gépen?
A futtatásra több módszer is lehetséges
Mi a most induló módszertani segédletben a második módszerrel szeretnénk foglalkodni, hasznos tanácsokat adva a kedves Olvasónak, azoknak akik elég önbizalommal (és tudással) rendelkeznek ahhoz, hogy egyénileg nekivágjanak egy ilyen mélységű munkának.
Mindenekelőtt azt kell tisztáznunk, hogy mi szükséges egy valamely gépen futó program másik gépre történő átkonvertálásához:
Egy játékprogram esetében három dolgot kell megfelelően átalakítanunk: grafika, hang, irányítás.
Egyéb akadályok
Fejtörést okozhat több dolog is, pl. az IM2-es megszakítás szimulálása. Ez az a dolog, ami miatt több program nem fut az EMULÁTOR-on sem. Problémát okoz az 'EI' utasítás is (ENTERPRISE-on másképp kell végrehajtani), a 128K memórialapozás, a 128K zene (AY-3 8912 chip szimulálása), egyes ROM hívások, stb. Az ilyen speciális problémákra egy-egy aktuális rész kifejtése során fogunk kitérni.
A program átírási manipulációhoz nem árt beszerezni egy disassembler-monitor programot, mivel e nélkül kicsit nehézkes lenne a dolgunk. Mi a SIMON (ASMON) nevű Z-80 fejlesztő-rendszert használtuk, ugyanis ez nem csak disassembler, hanem assembler is. Minden - később ismertetésre kerülő - példaprogram ezzel a rendszerrel lett előállítva.
Az sem árt, ha van a közelünkben egy SPECTRUM, persze ez nem létszükséglet, csak az eredetivel való egybevetéshez, ill. a BASIC betöltök listázásához kell. Célszerű egy software SPECTRUM EMULÁTOR beszerzése is, mivel a BASIC listázására ez is alkalmas.
Miért kell listázni a SPECTRUM verzió BASIC betöltőjét? Nos, a SPECTRUM programok általában egy BASIC betöltővel indulnak, a betöltő (LOADER) tölti be a program további részét (részeit). Ez egyrészt kényelmi szempontból jó (elég a LOAD"" utasítás, nem kell kiírni mögé a CODE függvényt), másrészt így egyszerien biztosítható az AUTOSTART). Ha nem listázzuk ki a BASIC betöltőt, akkor nem tudjuk a blokkok számát, az egyes blokkok betöltési címét, hosszát, s nem utolsó sorban a legfontosabbat, a program indítási címét. Természetesen a programok többsége nem engedi magát listázni, esetleg a betöltő maga is gépi kódú, vagy rejtett, ezekre a speciális esetekre is a megfelelő módszer ismertetésénél fogunk kitérni.
A SPECTRUM programok átírásának első lépcsőfoka magának a kódnak az átvitele egyik gépről a másikra. Ez esetünkben kézenfekvő, mivel a programok elnyomó többsége kazettán kerül az amatőrök kezébe. A feladat tehát az, hogy a SPECTRUM által felírt kazettákat elolvassuk. Szerencsére mindkét gépnek hasonló a kazettás magnót kezelő hardware felépítése.
Kicsit konkrétabban: a SPECTRUM 254-es (0FEH) porijának 5. bitje a kazettás adatrögzítés bemeneti bitje. A szalagról bejövő jel nagyságától függően vagy 1-be, vagy 0-ba állítja ezt a bitet.
ENTERPRISE-on a 182-es (0B6H) porton a 7. bit nagyjából hasonló feladatot lát el. Vagyis feladatunk eléggé leegyszerűsödik, mivel az eredeti loader-t kivehetjük a SPECTRUM ROM programjából, csak a hardware különbségeket kell korrigálni. Mi is ezt tettük a közölt programban. A példaprogram egy kicsit bonyolultabb, mert különböző luxusszolgáltatásai is vannak. Megértéséhez szükséges az EXOS (az ENTERPRISE operációs rendszere) ismerete.
Az EXOS használata a csatornák használatán alapul. Erre a SPECTRUM BASIC esetében is történtek próbálkozások, de korántsem sikerült annyira logikusan. A csatornák lényege az, hogy ha valamilyen egységet kezelni akarunk, akkor nem kezdünk el szeleburdi módon ROM-rutinokat hívni, hanem nyitunk számára egy kommunikációs csatornát. Azt, hogy milyen egységgel akarunk "szót váltani" a rendszer onnan tudja, hogy a csatorna megnyitásakor megadjuk az eszköz nevét. Ilyen eszközök a billentyűzet (KEYBOARD:), a nyomtató (PRINTER:) stb. Az EXOS onnan ismeri fel az eszközt, hogy kettőspont van a végén. Vannak olyan eszközök, amelyek file alapúak, és vannak olyanok is, amelyeknek csak a csatorna létrejötte szükséges. Például, ha kazettán létrehozunk egy file-t, akkor annak meg kell adnunk a nevét, adatokat kell beleírnunk, majd le kell zárni. A billentyűzet esetén nem akarunk file-t sem olvasni, sem írni, mi csak az éppen leütött billentyűre vagyunk kíváncsiak.
Egy csatorna, ha már létrejött, sok mindenre jó. Tudunk bele adatokat írni, adatokat olvasni. Csatornát EXOS funkcióhívással tehet megnyitni. Az EXOS funkcióhívás a következőképpen néz ki:
RST 30H
DEFB funkciókód
Általában az A regiszter tartalmazza a csaturna számot, DE-ben és BC-ben adunk át paramétereket. Visszatéréskor az "A" regiszter egy hibakódot tartalmaz, ami 0, ha sikeres volt a művelet. Mivel így elég nehézkes használni, ezért erre létrehoztak az ASMON-ban egy előre definiált makrót EXOS néven. Ennek használata: "EXOS funkciókód" ahol a funkciókód értelemszerűen a kívánt operációsrendszer szolgáltatás száma.
A programban használt EXOS hívások:
A
B=1
C=1
D=20
E=1a csatornaszám
Az 1. funkció kijelölése
A videolapon az 1.sortól kezdve legyen megjelenítés
20 sort kell kijelezni
A képernyőn az 1. sorban kezdődjön a videolap
B=1
C=v
D=új érték.A változót írni óhajtjuk
C regiszterben az EXOS változó száma
A hívás után ez lesz a változó új értéke.
Az operációs rendszer nagy vívmánya a szövegszerkesztő (EDITOR:). Nagysága abban áll, hogy ha valaki valamilyen szöveget akar a felhasználótól kicsikarni, nem kell új adatbekérő programot írnia. Ilyen esetekben elég, ha nyit egy csatornát "EDITOR:" néven, és máris javában olvashat a felhasználótól. Csatornamegnyitás előtt be kell állítani néhány paramétert (EXOS változót). Ezek:
Ennyi kitérő után lássuk, mit is tud a program:
Indítás után megkérdezi az átvitel módját, folyamatos, vagy nem folyamatos. Folyamatos mód esetén betölt egy SPECTRUM file-t, majd ezt kimenti, és ezt addig csinálja, amíg le nem lövik. A nem folyamatos mód abban tér el, hogy minden kimentés előtt vár egy billentyűleütésre, így ilyenkor lehet kazettát cserélni. A program bekér egy nevet, mögé tesz egy kiterjesztést formában, ahol y és xx szimbólikus jelölés. "y" helyére egy 1 karakteres jelet tesz, ami az első file esetén 0, ezután 1,2 stb. "xx" a beolvasott blokk típusát adja hexadecimálisan. Innen lehet felismerni, hogy ez most fejléc volt avagy programblokk.
A program az "ASMON" nevű assemblerrel készült, ha valaki csak "HISOFT GEN ASSEMBELER"-rel rendelkezik, ne csüggedjen, hanem az első sorok elé írja be a következőket:
EXOS MACRO@1
RST 30H
DEFB @1
ENDM
Az EXOS a sor elején kezdődjön, a többi sor egy (vagy több) karakterrel beljebb. Ez érvényes az egész programban is. A szimbólumokat bevezető SPACE nélkül írjuk be, míg a többi sorban lévő utasítások elé legalább egy SPACE-t tegyünk. Ez a programlistából is előtűnik. A szimbólumok és a mögötte álló utasítások közé szintén legalább egy SPACE szükséges. A bevitel módja ASMON-ra: a program bejelentkezése után nyomjuk meg az "E" billentyűt. Ekkor bekerülünk egy szövegszerkesztőbe. Itt írjuk be programunkat, majd nyomjuk meg az "F8" funkcióbillentyűt. Ekkor visszakerülünk a monitorba. Itt a "Z" billentyű lenyomásával beállíthatjuk az assembler opciókat. Addig nyomjuk az ENTER-t, amíg el nem jutunk az "Object file name:" kérdéshez. Ekkor írjuk be a program nevét (mindenkinek az ízlésére van bízva a névválasztás). Ezután újra ENTER, ekkor az "EXOS module header NO" üzenet látható. Nyomjunk meg egy fekete billentyűt (tehát nem a STOP-ot!!!), ekkor változik a kép, most már az "EXOS module header YES" szöveg olvasható. Adjunk ENTER-t. Most azt olvashatjuk, hogy "EXOS module type 0". Üssük be az "5" számot, majd újra az ENTER-t. Visszatértünk a "Command>" módba. Már csak egy teendőnk van, lefordítani a forrásszöveget. Ezt az "A" billentyű leütésével eszközölhetjük. Előtte ne felejtsük el felvételre állítani a magnót. Ha lemezegységünk van, akkor készüljünk fel arra, hogy a lemezen létrejön a programunk olyan néven, amit megadtunk neki. Ha az assembler valamilyen hibaüzenettel leáll, akkor nézzük meg, mit rontottunk el. A forrásszöveget is kimenthetjük a "W" paranccsal. Ekkor meg kell adnunk a file nevét, majd ENTER-t kell nyomni. A kimentett forrásszöveg a "K" paranccsal tölthető vissza. A GEN esetén be kell írni a forrásszöveget, majd "A,,R 5" és ENTER, ekkor lefordítja. Ha rendben ment a fordítás, akkor beírhatjuk: "O"név" és kimentődik a használható program.
Eme módfelett magasröptű program beírása után már akadálytalanul tudunk áthozni mindenféle programterméket. Ez így nagyon szép, de még ezzel sem tudunk működő SPECTRUM átiratokat gyártani, mert egyrészt még SPECTRUM képernyőnk sincsen, másrészt nem tudjuk betölteni sem. Ez a nemes feladatot megvalósító programocska lesz következő értekezésünk tárgya.
Miután már mindenki profi módon tud SPECTRUM programokat beolvasni, jogosan vetődik fel a tisztelt felhasználókban a kérdés, hogy az átmenekített adatokkal mit kezdjen. Egyszóval: hogyan tovább? A következő lépcsőfok az előző részben leírt módon emberközelibbé tett programok olyan környezetbe helyezése, ami minél inkább emlékeztet egy SPECTRUM-ra. A környezet legfontosabb eleme a képernyő. Enélkül a programok nagy része meglehetősen egysíkúvá válik. Első és egyben legmagasztosabb feladatunk elkövetni egy olyan képernyő formációt, ami kísértetiesen hasonlít a SPECTRUM-ra. Ez meglehetősen nehéz feladat, mivel a SINCLAIR gép eléggé Istentől elrugaszkodott módon végzi a kép kirakását. Vegyük sorra a jellegzetességeket:
0.sor | 0 | 8.sor | 32 | |
1.sor | 256 | 9.sor | 288 | |
2.sor | 512 | 10.sor | 544 | |
3.sor | 768 | 11.sor | 800 | |
4.sor | 1024 | 12.sor | 1056 | |
5.sor | 1280 | 13.sor | 1312 | |
6.sor | 1536 | 14.sor | 1568 | |
7.sor | 1792 | 15.sor | 1824 |
Ennyi bevezető után térjünk rá programunk muködésére:
Először szerez egy kis memóriát, majd emulálja a SPECTRUM video üzemmódját. Ezt 192 db LPB segítségével teszi (az LPT felépítéséről már volt szó e hasábokon). Ezután betölt két file-t. Az ADDR1, BYTE1 ill. ADDR2, BYTE2 címkéket a betöltendő programtól függően kell értékkel feltölteni.
ADDR1 az első blokk, ADDR2 a második blokk betöltési címe. BYTE1 az első blokk, BYTE2 a második blokk hossza. A betöltött program indítási címe a USR szimbólumban kell hogy legyen tárolva.
Ha ezzel a betöltővel felfegyverkezve a vállalkozóbb szellemű Olvasó betölt egy játékprogramot, a betöltési képet meglátva valószínűleg elátkozza eme sorok íróját. Annak, hogy a tapasztalt szépséghibának mi az oka, hogyan lehet kiküszöbölni, a következő részben olvashatják a téma iránt érdeklődők!
Az eddigiek során elfutottunk odáig, hogy fel tudunk építeni a SPECTRUM képernyőjéhez nagymértékben hasonlító SCREEN-t. Hogy ez miért csak hasonlító, és miért nem azonos, annak több oka van. Az előzőekben már volt szó az attribútum üzemmódról. Az attribútum memória az 5800H-5AFFH címtartományban helyezkedik el. Eme tartománynak a programátírások során igen nagy szerepe van, ezért érdemes jól megjegyezni. Vizsgáljunk meg egy attribútumbyte-ot! Kezdjük a legmagasabb helyiértékkel, a 7. bittel (b7)! Ha ez a bit be van állítva (értéke 1), akkor a papír és a tinta szín rövid időközönként invertálódik (magyarán: az adott karakter villog). Na ez az, amit az ENTERPRISE nem tud. Ha egyszer a sors szeszélye folytán olyan program kerül kezeink köze, amelyben létfontosságú a FLASH, akkor készüljünk fel a legrosszabbra. Nem lehetetlen ezt is szimulálni, de ez általában a programfutás sebességének rovására megy.) Tovább haladva, nézzük a 6. bitet (b6)! Ez a bit az un. BRIGHT (fényesség) bit. Ha ennek értéke 0, akkor a normál színek, míg a bit beállítása esetén a fényes színek jelennek meg. Az esetek túlnyomó többségében ez a bit okozza a galibát. A következő bitcsoport (b5,b4,b3) a papír színét (a nullás bitek a bittérképben), míg a legalsó 3 bit (b2,b1,b0) a tintaszínt (az egyes bitek a bittérképben) adja meg. Mivel ez 3 bit, így 8 szín kijelölésére van lehetőség. Ehhez jön még a BRIGHT bit, így kis jóindulattal azt mondhatjuk, hogy 16 szín között válogathat a felhasználó. A színképzést egyszerűen oldották meg: a három alapszínhez (vörös, zöld, kék) hozzárendeltek egy-egy bitet. A hozzárendelés: b0=kék: b1 =vörös, b2=zöld. (Ez így természetesen csak a tintaszínre igaz, papírszín esetén b0 helyett b3, b1 helyett b4 és b2 helyett b5 értendő.) Miután tisztáztuk a SPECTRUM színképzését, most a másik oldal következik. Az ENTERPRISE attribútum üzemmódja lényegesen egyszerűbb, nincsenek benne helyi specialitások (FLASH, BRIGHT). Az attribútumbyte két részre oszlik: alsó 4 bit és felső 4 bit. A felső 4 bit a papír, az alsó 4 bit a tinta színét jelöli ki (ahogy azt a logika diktálja). Látható tehát, hogy itt valóban 16 szín használatára van lehetőség. Ebből a 16 színből az alsó 8 (a 0xxx értékek) tetszőleges lehet. (A sorparaméter táblában, az LPB utolsó nyolc byte-ja éppen ezt a nyolc színt tartalmazza.) Előző mintaprogramunkban alkalmazott paletta jó közelítéssel megfelel a SPECTRUM normál színeinek. Amennyiben csak normál (nem fényes) színek fordulnak elő, akkor az egyedüli problémát az okozza, hogy a papír színek egy bittel alacsonyabban vannak, mint ahogy azt az ENTERPRISE megkívánná. Mielőtt bárki is fellelkesülne, sajnos le kell hűteni a kedélyeket. Már említettük a BRIGHT bitet. Nos, ha egy programban fényes színek is előfordulnak, akkor (mivel a b6 bit egyes állapotú) a fekete háttér helyett egy kellemes zöld alapszínt kapunk (no meg frászt, mivel semmit nem látunk a programból). Ilyen szempontból nagyon kellemes a fényes zöld tinta és a fekete papír kombinációja, ugyanis ennek értéke 44H. Ez azért jó, mert ez ENTERPRISE-on zöld papíron zöld tintát jelent, magyarán semmit. A megoldás bizonyára több olvasóban is felötlött: a fényes színeknek feleltessük meg a fennmaradó 8 színt (az 1xxx kombinációkat). Igen ám, de ez nem olyan egyszerű! Ezt a nyolc színt nem lehet csak úgy beállítani, használni kell a FIXBIAS regisztert. Ez a nevezetes regiszter a 128 (80h) port nagy részén terpeszkedik. Ez a port is bitenként értelmezett:
b7:
b6,b5:
b4-b0:ha értéke 1, akkor a belső hangszóró néma.
a külső színbemenetek prioritását szabályozza.
FIXBIAS regiszter.
Mint látjuk ez a regiszter 5 bites. Tudni kell még azt is, hogy az ENTERPRISE a színeket 8 biten ábrázolja, így 256 színt képes használni. A felső nyolc szín (az 1xxx kombinációk) úgy képződik, hogy a FIXBIAS regiszter lesz a színbyte felső 5, míg a kiválasztott szín 3 bites érteke az alsó 3 bitje. A már említett programunk a FIXBIAS regiszterbe 0-t tölt. A NICK-chip bit-szín hozzárendelése azonban különbözik a SPECTRUM-étól: b0-vörös. b1-zöld, b2-kék. Vagyis ami SPECTRUM-on kék, az ENTERPRISE-on vörös. A megoldás egyszerű: a biteket úgy kell megcserélni, hogy a színek azonosak legyenek. Ez annyit jelent, hogy a programban el kell mélyedni, a színkezeléseket felderíteni, majd a kívánt értékre módosítani. Általában a programátírásban ez a mozzanat tart a legtovább, ez igényli a legnagyobb gyakorlatot. Tehát összefoglalva: ahhoz, hogy egy SCREEN hasonló színekben pompázzon, mint az eredeti, a következő módosítások kellenek:
b7 b6 b5 b4 b3 b2 b1 b0SPECTRUM attribútumbyte 0 1 s5 s4 s3 s2 s1 s0ENTERPRISE attribútumbyte 1 s3 s5 s4 1 s0 s2 s1
E kis ábra után bizonyára mindenki tudna kreálni egy táblázatot, azonban, hogy az Olvasó idejét kíméljük íme az összes attribútumérték és a kiszámított ENTERPRISE megfelelő (forma: SPECTRUM-> Enterprise):
00=>00 | 01=>01 | 02=>02 | 03=>03 | 04=>04 |
05=>05 | 06=>06 | 07=>07 | 08=>10 | 09=>11 |
0A=>12 | 0B=>13 | 0C=>14 | 0D=>15 | 0E=>16 |
0F=>17 | 10=>20 | 11=>21 | 12=>22 | 13=>23 |
14=>24 | 15=>25 | 16=>26 | 17=>27 | 18=>30 |
19=>31 | 1A=>32 | 1B=>33 | 1C=>34 | 1D=>35 |
1E=>36 | 1F=>37 | 20=>40 | 21=>41 | 22=>42 |
23=>43 | 24=>44 | 25=>45 | 26=>46 | 27=>47 |
28=>50 | 29=>51 | 2A=>52 | 2B=>53 | 2C=>54 |
2D=>55 | 2E=>56 | 2F=>57 | 30=>60 | 31=>61 |
32=>62 | 33=>63 | 34=>64 | 35=>65 | 36=>66 |
37=>67 | 38=>70 | 39=>71 | 3A=>72 | 3B=>73 |
3C=>74 | 3D=>75 | 3E=>76 | 3F=>77 | 40=>88 |
41=>8C | 42=>89 | 43=>8D | 44=>8A | 45=>8E |
46=>8B | 47=>8F | 48=>C8 | 49=>CC | 4A=>C9 |
4B=>CD | 4C=>CA | 4D=>CE | 4E=>CB | 4F=>CF |
50=>98 | 51=>9C | 52=>99 | 53=>9D | 54=>9A |
55=>9E | 56=>9B | 57=>9F | 58=>D8 | 59=>DC |
5A=>D9 | 5B=>DD | 5C=>DA | 5D=>DE | 5E=>DB |
5F=>DF | 60=>A8 | 61=>AC | 62=>A9 | 63=>AD |
64=>AA | 65=>AE | 66=>AB | 67=>AF | 68=>E8 |
69=>EC | 6A=>E9 | 6B=>ED | 6C=>EA | 6D=>EE |
6E=>EB | 6F=>EF | 70=>B8 | 71=>BC | 72=>B9 |
73=>BD | 74=>BA | 75=>BE | 76=>BB | 77=>BF |
78=>F8 | 79=>FC | 7A=>F9 | 7B=>FD | 7C=>FA |
7D=>FE | 7E=>FB | 7F=>FF |
Ennek segítségével már lehetséges a színek átalakítása, azonban hosszabb adatmezőket már nem érdemes kézzel konvertálni. Mivel a számítógép az ember barátja, illő, hogy dolgozzon is egy kicsit. Az alábbi kis programmal SPECTRUM SCREEN-eket lehet konvertálni:
A program használata: írjuk be az ASMON szerkesztőjébe. Amennyiben nem akarjuk minden egyes alkalommal újragépelni, akkor mentsük ki a <W> billentyű segítségével. Ekkor a "File name:" kérdésre adjunk egy nevet (ezt az Olvasó fantáziájára bízzuk). Célszerű egy külön kazettát (vagy lemezt) fenntartani a segédprogramoknak. Ha már kimentettek, legközelebbi alkalommal a begépelés helyett töltsük be ASMON-ba a "K" parancs segítségével. Az ismételten megjelenő "File name:" kérdésre adjuk meg az általunk kreált fantázianevet. Betöltődés után állítsuk be az assembler opciókat ("Z' parancs) az alábbiakra:
Assembly listing OFF
List conditions NO
Force PASS 2 NO
Memory assembly YES
Memory offset 0
Object file name:
Ez utóbbi esetben egyszerűen adjunk <ENTER>-t a kérdésre. Ezután fordítsuk le ("A" parancs) a programot, majd töltsünk be egy SPECTRUM SCREEN-t. Ez a SPECTRUM-on a 4000H címre töltődik be ás 1B00H hosszú (6912). A betöltés az <R> (Read BIN file) billentyű leütésével kezdhető el. Ekkor megjelenik a "Start:" kérdés. Erre a 4000 begépelésével válaszoljunk, majd <ENTER>. Ezután a "End:" kérdésre 5AFF-el válaszoljunk, majd ismét <ENTER>. Most már csak a "File name:"-re kell megadnunk a SCREEN nevét, és elindul a betöltés. A töltés végén megjelenik az "End of file" hibaüzenet, alatta pedig (ha jó csináltunk mindent) a "Last address:5AFF" szöveg. Ekkor futtassuk a programunkat: "G". majd a "Start:" kérdésre válaszoljuk a kezdőcímet, vagyis a 3000-et, ismét <ENTER>. Ha jól fordítottunk, akkor rövidesen a "Returned from CALL at 3000" választ kapjuk. Ekkor a memóriában már ott a konvertált SCREEN, csak ki kell menteni. Nyomjuk le az <S> gombot (Save BIN file). A "Start"-ra adjuk meg a 4000-et, "Stop"-ra 5AFF-et,"File name:"-re egy nevet. Az így kimentett SCREEN-t visszatölthetjük az előző részben ismertetett LOADER-rel. Végezetül annyit, hogy a program nem csak SCREEN-ek konvertalására jó, de minden más esetben módosítani kell a kezdőcímet és a hosszt. Természetesen csak SPECTRUM formátumról már ENTERPRISE file-formátumra hozott SCREEN-ek konvertálhatók a leírt módszerrel.
Az eddigi számokban megpróbáltuk az alapokat tisztázni. Ezek megértése fontos a továbblépéshez. Az ASMON assembler-monitor program kezelését célszerű mihamarabb elsajátítani. Ezután a további fogásokat már egy konkrét példán keresztül fogjuk bemutatni. Az általunk választott program a "MOONCRESTA" nevet viseli, egy nemes mondanivalóval bíró űrhajós játékprogram. A választásnak több oka van:
Első lépésként próbáljuk kilistázni a BASIC részt (ha van rá lehetőségünk). Itt kezdődnek a problémák: a programot nem tudjuk leállítani. Ha valaki SPECTRUM-on próbálkozik, akkor szakirodalomban utána tud nézni az ilyen piszkos trükkök elleni védekezésnek. Mi ENTERPRISE-on, egy software SPECTRUM-emulátorral próbálkoztunk, ezeknek megvan az a jó tulajdonságuk, hogy le lehet BREAK-elni a programot közvetlenül betöltés után. Ha ez sikerült, még mindig nem érünk vele semmit, mert programlista helyett egy "O.K. boy" feliratot, és egy telefonszámot látunk. Megsúgjuk, hogy a sorszám nullára van állítva, ezért nem lehet vele mit kezdeni. Ezt a huncutságot egy POKE 23755,0:POKE 23756,1 parancssorral kivédhetjük. Ekkor az első sor száma 1 lesz. Ezt nyugodtan törölhetjük, mivel csak az üzenetet tartalmazza. Ha ezt megcsináltuk, akkor ismét nem tudunk mit kezdeni a programunkkal, mivel a védelem készítője a második sor sorszámát is kinullázta. Ezen a naivságon már csak gúnyosan mosolygunk, és megismételjük az előbbi POKE-okat. Most már van egy 1. sorszámú sorunk. Ezt a sort immáron önfeledten javíthatjuk. A sor elejéről a nem oda való grafikus karaktereket törölve előtűnik teljes pompájában a BASIC loader.
1 CLS: PRINT AT 10,10; FLASH 1;"loading"; FLASH 0;AT 12,9;"please wait ;TAB 7;"cracked by zec":
POKE PEEK 23633+256*PEEK 23634,PEEK 23647:POKE (PEEK 23633+256*PEEK 23634)+1,PEEK 23648
Ebből megtudjuk azt a fontos információt, hogy egy "zec" nevű úriember törte fel az eredeti védelmet. Megnyugodhatunk tehát, a tolvajtól lopni nem bún. Ha ezt a sort is töröljük, akkor még mindig marad egy sor, amivel viszont már nem tudunk elbánni. Nem hát, mivel ez a gépi kódú loader, amit a BASIC igen rafináltan elindít. Disassemblálva a gépi kódú loader-t, a következű lista tárul ámuló szemünk elé:
LD SP,5BFFH
LD A,FFH
SCF
LD IX,B3B0H
LD DE,0100H
CALL 0556H
JP B3B0H
Egy ROM-listát előszedve tapasztalhatjuk, hogy ez nem csinál egyebet, mint beolvas egy fejléc nélküli programot a 0B3B0H címre, és ráugrik. Csavaros eszű Olvasóink bizonyára sejtik, hogy ez ismét egy loader lesz.
Elszántabb olvasóink okulva az előző részben leírtakból, már bizonyára betekintést nyertek a nevezetes 256 byte hosszú LOADER lelkivilágába. Aki még - kellő önbizalom hiányában - visszariadt ettől a lépéstől, annak megvilágítjuk e probléma hát terét. Mielőtt belemélyednénk, szeretnénk néhány tanácsot adni:
Ennyi kis kitérő után térjünk rá a lényegre:
Töltsük be az eredeti helyére a LOADER-t (ez az a kivétel ami a szabályt erősíti). Az eredeti hely esetünkben 0B3B0H, ez még a szabad memória területen belül van. A betöltött programot listázzuk ki ("L" parancs). Mivel tudjuk az indítási címét, érdemes itt próbálkozni (aki kőkemény egyéniség, természetesen próbálkozhat máshol is, de kijelentjük, hogy igazat szóltunk). Az indítási cím esetünkben adódik magától, mivel megegyezik a betöltési címmel. Ha elkezdjük listázni, akkor a következő látvány tárul ámuló szemeink elé:
B3B0 | 31 FF 5B | LD SP,5BFF | ;A verem beállítása |
Mielőtt valaki a szavahihetőségünket kétségbe vonná, sietünk leszögezni, hogy a megjegyzések, valamint a logikai egységelv szétválasztása a mi merényletünk. Látható, hogy a LOADER 6 logikai egységből áll. Az első egy normál vagy mezei LD SP,5BFF utasítás. Ennek a verem állításán kívül semmi szerepe sincs. A második egység már érdekesebb. Mivel látjuk, hogy 4000H-re töltődik, ráadásul 1B00H a hossza, besorolhatjuk a SCREEN skatulyába. A konvertálására az előző epizódban leírt játékszabályok érvényesek.
A harmadik és a negyedik egység végzi a munka oroszlánrészét: ők töltik be a tulajdonképpeni programot, ráadásul két részletben. Az egyik E8C4H-ra 173CH, a másik 7776H-ra 38A4H mennyiségű byte-ot tölt.
Az ötödik egység végzi a program végleges elindítását, érdemes megfigyelni, hogy EAE8H-n indul a MOONCRESTA.
Végül a hatodik egység, a LOAD szubrutin, amely a kazettáról betölti az egyes file-okat. Tévedés ne essék, ez nem egy sorból áll, sőt ez a leghosszabb szubrutin a LOADER-ben, viszont nem láttuk értelmét teljes egészében közölni. Akit érdekel, az tanulmányozhatja, nincs benne semmi extravagáns.
Ha valaki ennyi balszerencse ás sok-sok viszály után már a hetedik menyországban érezné magát, azt ki kelt ábrándítani. A cracker nagy showman lehetett, mivel még egy kis meglepetést tartogat a tarsolyában. Töltsük be a két modult! Az elsőt A8C4H-ra (ez már a 4000H-va alacsonyabb cím!), a másodikat 3776H-ra. Az első BFFFH-ic tart, a második 7018H-ig, erre a kimentésnél legyünk tekintettel! Listázzuk ki a programot az indítási címtől kezdve.
AAE8 | 3E 00 | LD A,00 | ;A keret (BORDER) fekete |
Ezek után láthatjuk, milyen gonosz volt az illető. Nézzük végig a programot. Az E8C4H-n található rutin előállít egy számot. Ezt kiszámolhatjuk az eredeti SCREEN-ből, de felesleges, elég megnézni az E8D6H címen levő byte-ot. Az első néhány sort mindjárt megspórolhatjuk. EB01H-n van az első lényeges rész, a XOR-olás. Mivel tudjuk az ellenőrző kódot (ha valaki nem tudná, 22H), cselesen írhatunk egy kis szubrutint, ami elvégzi helyettünk eme nemes cselekedetet.
Mazochisták persze kézzel is megpróbálhatják. Mivel az elején található programrészre nem lesz szükségünk, ide beírhatjuk alkotásunkat. Ehhez szálljunk ki a listázásból (STOP vagy az ESC billentyű), majd nyomjuk meg az "M"-et. A módosítás címének kérésére adjuk az AAE8-at. Az ENTER lenyomása után kiíródik egy memóriadump, amiben tudunk módosítani. Szép sorban, az ENTER lenyomása nélkül írjuk be a következő számokat:
21 11 AB 06 4A 7E EE 22 77 23 10 F9 C9
Majd szálljunk ki a módosításból (STOP vagy ESC). Ha kilistázzuk ezt a kis programocskát, a következőket láthatjuk:
AAE8 | 21 11 AB | LD HL,AB11 |
Futtassuk le ("G" parancs, majd AAE8, ENTER), majd ha rendesen megkaptuk a "Retumed from CALL at AAE8" üzenetet, listázzuk ki az AB11 H címet. Micsoda változás!!
AB11 | 21 77 77 | LD HL,7777 |
|
Tanulságul álljon itt a program által használt két szubrutin is!
AB47 | 7E | LD A,(HL) |
Látható, hogy a program többi része itt is el van rejtve. Ismét módosítani vagyunk kénytelenek. A módszer hasonló az előző programocska beviteléhez, de itt az AB0F címet kell módosítanunk az alábbi byte-okra:
16 22 21 77 37 01 A4 38
CD 47 AB 21 00 AE 01 00
12 CD 47 AB 21 77 37 01
A4 38 CD 51 AB 21 00 AE
01 00 12 CD 51 AB C9
Ha ezt kilistázzuk, a következőket kell látnunk:
AB0F | 16 22 | LD D,22 |
Ha nem ezt látjuk, akkor vagy valami belerepült a szemünkbe, vagy elírtunk valamit (minden bizonnyal a Tisztelt Olvasó). Miután ezzel megvagyunk, futtassuk le ezt is ("G" AB0F, ENTER), és már meg is van a futtatható (átírható) programunk. Már csak egy akadály van hátra, nevezetesen az indítási cím. Aki azt hiszi, hogy ilyet már találtunk eleget, téved. Meg kell keresnünk a - sorrendben a harmadik - helyes belépési pontot. További tortúrák helyett ezt már a tudomására hozzuk azoknak a fanatikus programozóknak, akik idáig kitartottak (jutalom): 77B2H (illetve a 4000H eltolásos módszer esetében 37B2H). Hogy ez honnan származik? Ezt az Olvasóra bízzuk (csak annyit segítségül, hogy A710H-2F5EH az pontosan 77B2H, valamint a SPECTRUM ROM-ban a 6FH címen egy szál utasítás van, ez pediglen egy JP (HL) ).
Miután így sikerült lefegyvereznünk a védelmet, akár rá is térhetnénk a program tulajdonképpeni átírására. Sajnos azonban ez a kis ujjgyakorlás sok helyet elvett, így a lényeg legközelebbre marad. Addig is, senki ne feledje el kimenteni a verejtékes munkával feltört programrészeket. Míg mindenki szívszorongva várja következő próféciánkat, el lehet kezdeni az ismerkedést a programozók stílusával, meg lehet próbálkozni egy ENTERPRISE LOADER készítésével.
Végezetül érdemes összefoglalni az eddigi file-ok adatait:
SCREEN 4000H-ra kell tölteni, 1B00H hosszban. CODE1 7776H-ra kell tölteni, 38A4H hosszban. CODE2 E8C4H-ra kell tölteni, 173CH hosszban.
Ha mindent betöltöttünk, el kell ugrani a 77B2H címre.
Kellemes bogarászást!
Ha valaki vette a fáradságot és előállította a program két blokkját, valamint átalakította a közölt betöltőt, csodálkozva tapasztalta, hogy a program nem igazán rendeltetésszerűen működik. Általánosságban elmondhatjuk, hogy ha egy program "elszáll", akkor annak két oka van:
Esetünkben mindkét esetre látunk majd példát, most foglalkozzunk a fontosabbal, a felhasználói megszakítással. A megszakítás olyan alprogram, amely a főprogram futása közben egy előre meghatározott esemény bekövetkeztekor hajtódik végre. Ez az esendő esemény esetünkben esetlegesen esik meg (jó mi?) mégpedig minden 1/50-ed másodpercben. A megszakítás elfogadásakor a processzor elmenti az aktuális programszámlálót (a PC regisztert), majd reagál, az üzemmódtól függően más ás más módon.
A Z80-as mikroprocesszornak 3 féle megszakításkezelése van:
FONTOS! A megszakítást kiszolgáló alprogramnak nem szabad megváltoztatni EGYETLEN regiszter értéket sem, amelyet a főprogram használ! A megszakítás elfogadásakor tiltódik a további megszakítások elfogadása, ezt általában az interrupt alprogram végén szokták engedélyezni. Ez mind nagyon szép - mondhatja az Olvasó - de mi szükség van ennek ismeretére? Nagyon is van, mivel az ENTERPRISE bonyolultabb megszakítás elektronikával rendelkezik. Nem egy-, hanem mindjárt négyféle megszakítási forrás áll rendelkezésre, amelyek külön-külön tilthatok, engedélyezhetők. Erre egy külön perifériacím szolgál, melynek címe a 0B4H. Ez különbözik OUT és IN műveletek esetén. Minket csak az OUT érdekel, ezért csak ezt részletezzük.
A port bitenként értelmezett:
b0 A hanggenerátor által adott megszakítás engedélyezése. (1 érték által)
b1 A hanggenerátor megszakítás tároló törlése.
b2 Az 1 Hz-es megszakítás engedélyezése.
b3 Az 1 Hz-es megszakítás tároló törlése.
b4 A video-megszakítás engedélyezése.
b5 A video-megszakítás tároló törlése.
b6 A soros vonal megszakításának engedélyezése.
b7 A soros vonal megszakítás tárolójának törlése.
Az, hogy az engedélyezésnek mi a szerepe, elég egyértelmű, ha a megfelelő helyre egy egyes bitet írunk, akkor az a forrás képes megszakítást generálni. Ha nullát írunk, akkor letiltottuk az eszközt. Az 50 Hz-es megszakítás generálásához célszerű a NICKchip által generált megszakítást használni, amit az LPT generálásakor beállítottunk.
A másik feladat - a megszakítás tároló törlése - már nem ilyen egyértelmű. A DAVE-chip (amely a megszakításrendszert istápolja) tárolja az interrupt kéréseket. Amíg nem töröltük a tárolóját, addig elnyom minden ilyen irányú próbálkozást. Tehát: ha azt akarjuk, hogy az 1/50 másodpercenként érkező megszakítások ne vesszenek el (vagyis ne "fagyjon le" a program), minden megszakítást kiszolgáló alprogram végén (vagy elején) törölnünk kell a DAVE-chip megfelelő tárolóját. A video-megszakítás újraengedélyezését végző rutin az alábbi:
LD A,30H | ;b4 +b5, video IT. engedélyezés, tároló törlése |
Arra természetesen figyeljünk, hogy mentsük az "A" regiszter a művelet előtt.
Most nézzük meg, hogy a nagy fáradsággal átmentett "MOON CRESTA" hogyan csinálja. A program indítási címe 77B2H.
7782 | F3 | DI | ;Megszakítás tiltása. |
Biztos mindenki megdöbbent a 257 elemű táblázaton. Erre azért van szükség, mivel a táblázat alsó byte-ja (amit a periféria ad) nem eléggé biztos. (Általában FFH) Ezzel a módszerrel minden kombinációra bebiztosították magukat a programozók. Az ilyen táblázatos módszerek leggonoszabbja az, amikor a táblazat csupa 255-ból áll, tehát a megszakítási rutin címe 65535 (FFFFH). Erre az egy byte-ra leraknak egy "JR" utasításnak megfelelő 18H-t, amelynek operandusa a 0 címen lévő (SPECTRUM BASIC) 0F3H. Ez a relatív ugrás visszaugrik a 0FFF4H címre, ahol egy ugró utasítás visz a tulajdonképpeni megszakítási alprogramra. A következő LOADER a kész játék végső betöltője, ezért érdemes beírni. A megszakítási problémát úgy küzdöttek le, hogy 7777H-ra (az IT rutin) letettünk egy "JP" utasítást, előbb engedélyezzük a DAVE-chipben az interruptot, majd ráugrunk a tulajdonképpeni rutinra.
Mint látható, a program két blokkját nemes egyszerűséggel csak "ALSO" és "FELSO" névvel illettük, ezáltal is jelezve az elhelyezkedésüket. A betöltő elején található rutinok méltatására a későbbiekben visszatérünk. Ezzel a betöltővel futtatva a "MOON CRESTA"-t már hajlandó elindulni. (Igaz sok köszönet nincs benne!)
És most essék néhány szó a SPECTRUM billentyűzetfigyeléséről: Az összes gomb egy mátrixba van kötve. A mátrix sorát mi közöljük a géppel igen ravasz módon, az oszlopot pedig szerény kérésünkre válaszként kapjuk. A sor kiválasztását igen érdekesen oldották meg. Kihasználták a mikroprocesszor ama tulajdonságát, hogy IN/OUT műveletek esetén a címbusz felső 8 bitjén is értékes adat van. Vegyünk példának egy IN A,(0FEH) utasítást!
Ekkor a címbusz alsó 8 bitje a periféria címét (jelen esetben a 0FEH-t), míg a felső 8 bit az "A" regiszter beolvasás előtti értékét tartalmazza. A másik fajta "IN" utasításcsoport esetén (az IN r,(C) ) a "B" regiszter kerül hasonló helyzetbe.
A SPECTRUM a billentyűket egy 8*5 elemű mátrixban kezeli. A 8 sor eléggé sokatmondó, mivel pontosan ennyi bit van egy byteban. A 8 sornak tehát megfelel 8 bit, amelyik bit 0 értékű, azt a sort olvassa be a processzor. Mivel egy byte-on belül bármelyik bit lehet 0, ezért egy utasítással több sort is le lehet kérdezni. A kiválasztott sort a 254-es (0FEH) porton lehet beolvasni, az alsó 5 bit hordozza a billentyűzet információit. Konkrétan: az 1,2,3,4,5 gombok vizsgálata az alábbi kis programocskával történhet.
1.variáció | 2.variáció |
LD A,0F7H IN A,(0FEH) | LD BC,0F7FEH IN A,(C) |
Működés szempontjából a két módozat egyenértékű, a 2. variáció azonban használja a "BC" regiszterpárt. A gyakorlatban mindkét variáció előfordul.
Ha billentyűfigyelést keresünk egy programban, akkor a következő utasításokra figyeljünk fokozottan:
Utasítás | HEXA kód |
IN A,(0FEH) | DB FE |
Mielőtt bárki azt hinné, hogy az első két utasításon kívül mást nem használnak, az nem jár messze az igazságtól, viszont elrettentésül megjegyeznénk, hogy leltünk mar IN C,(C) utasítást is, amely az irányítás nemes feladatát volt hivatva betölteni. Gyakran előfordul a teljes klaviatúra lekérdezése is. Ez SPECTRUM-on egyszerű, ENTERPRISE-on viszont már nem. (Ha valaki nem tudná, egy
XOR A
IN A,(0FEH)
nagyon megfelelő ilyen célokra.)
A téma lezárásaként itt van a sokat emlegetett mátrix.
BIN | DEC | DEC | 1 b0 | 2 b1 | 4 b2 | 8 b3 | 16 b4 |
11111110 | 254 | FE | Caps | Z | X | C | V |
Miután a SPECTRUM klaviatúra-kérdést ilyen jól helybenhagytuk, rátérhetünk az ENTERPRISE oldalára. A mátrix módszer itt is alkalmazott, viszont - a több gomb miatt - némileg eltér a fent említettől. Először is, nem 8 sor van, hanem 10. Ez előrevetíti ama szomorú tény árnyékát, hogy nem fér egy byte-ban (vagyis csak a külön bites módszerrel nem). Másodszor: nem 5 oszlopot olvasunk be, hanem 8-at. Ezzel a módszerrel 80 gomb figyelésére van mód, ebből néhány nem használt. Azt, hogy melyik sort (0-tól 9-ig számozva) akarjuk olvasni, a 0B5H című porton kell közölnünk. Miután ezt közöltük, rendelkezésünkre áll ugyanezen a porton az adat. Előző példánknál maradva az 1-5 billentyűket az
LD A,3
OUT (0B5H),A
IN A,(0B5H)
utasításokkal figyelhetjük. Látható, hogy az előbbi 4 byte-tal szemben most ugyanez 6 byte hosszan sikerült. Vagyis nem fér be az eredeti helyére. Itt három dolgot tehetünk:
Kétségkívül az első módszer a legegyszerűbb, viszont a harmadik a legszimpatikusabb, ennek ellenére mi a másodikat fogjuk bemutatni. Ennek okai: minek riasszuk el az Olvasót ilyen korai stádiumban? Végezetül íme, az ENTERPRISE billentyűzetmátrix:
Sor | b7 128 80H | b6 64 40H |
b5 32 40H |
b4 16 10H |
b3 8 08H |
b2 4 04H |
b1 2 02H |
b0 1 01H |
0 1 2 3 4 5 6 7 8 9 |
B.SH. CTRL TAB ESC F1 ALT INS |
Z A W 2 F2 ERASE ] ENTER SPACE | X S E 3 F7 ^ : BAL J.SH. ] | V F T 5 F5 0 L HOLD . P | C D R 4 F6 - ; FEL / @ | B G Y 6 F3 9 K JOBB , O | \ LOCK Q 1 F8 LE DEL |
N H U 7 F4 8 J STOP M I |
Miután sikerült leimádkoznunk a védelmet a programról, valamint tisztáztunk néhány alapfogalmat, rátérhetünk munkánk érdemi részére.
Az előző részben már elindítottuk a "MOON CRESTA"-t, de egy kis DEMO-n kívül mást nem hajlandó csinálni. Az ok kézenfekvő: azért nem reagál a billentyűkre, mivel még a SPECTRUM módszerével figyeli azokat! Tehát a feladat:
Töltsük be a két a programfile-t, majd használjuk az ASMON keresés funkcióját. (A funkció aktivizálására szolgáló billentyűt legcélszerűbb a "H" (HELP) billentyű lenyomása után megjelenő menüből kinézni. Angol gépeken a hatványjel.)
Először próbálkozzunk a legkézenfekvőbbel, az IN A,(0FEH) utasítással. A keresés billentyűjének lenyomása után megjelenik egy "Start:" kérdés, majd utána egy szám. Első kereséskor ide írjuk be a kezdőcímet, további keresésekhez elegendő "ENTER"-t nyomni, ekkor folytatja a keresést. Miután kijelöltük a kezdőcímet, a "Search:" kérdésre írjuk be az IN utasítás Z80 kódját. Aki olvasta az előző részt, már fejből tudja, aki nem olvasta, az szégyellje magát! De már-már közmondásos nagylelkűségünknek engedve eláruljuk, hogy 0DBH. Tehát a "Search:" kérdésre gépeljük be azt, hogy "DB FE". Az idézőjel természetesen nem kell, mivel ha kitesszük az idézőjeleket, akkor azt a karaktersorozatot fogja keresni. Miután leütöttük az "ENTER"-t, kis gondolkozás után a "Found at:" üzenet ás egy memóriacím, alatta pedig három sorban memóriadump jelenik meg. Ha nem ez történne, akkor áldásos tevékenységünk mégsem volt annyira áldásos.
Most vegyük a kedvezőbb esetet, tételezzük fel, hogy sikeres volt az akciónk. Közbevetőleg egy jótanács! Keresésnél a kiírt címnél mindig néhány byte-tal alacsonyabb címtől kezdjük a listázást!
Nos, nézzük, mit találtunk.
3887 | 06 00 | LD B,00 |
(A memóriacímek természetesen 4000H-val magasabban értendők!)
Mit is csinál ez a kis rutin? A lényeg a
XOR A
IN A,(FE)
rész. A "XOR A" utasítás saját magával végez "kizáró VAGY" kapcsolatot. Ezt bitenként végzi, vagyis a 0.bitet a 0.bittel stb... A "XOR" művelet akkor ad "1" értéket, ha a két bit különböző. Mivel az "A" regiszter teljesen illogikus módon megegyezik saját magával, ezért a "XOR A" művelet eredménye mindig 0-t ad. Egyébként ez a módszer nagyon elterjedt a "LD A,0" (2 byte) utasítás helyett (mivel ez csak egy byte). Az "IN A,(FE)" utasításról volt már szó. Ha az "A" regiszterben nulla van, az az összes sort kijelöli, vagyis a teljes billentyűzetet leolvassa.
A következő, "AND 1F" utasítás csak a billentyűzetről származó adatokat tartja meg. Ezután megnézi, hogy volt-e lenyomott billentyű (CP 1F). Ha volt, akkor az "A" nem 1FH lesz, ekkor elugrik a 79E3H címre, ha nem volt lenyomott billentyű, akkor vár 1/50 másodpercet, majd újra leolvassa a klaviatúrát, és ez így megy 256-szor. (A "DJNZ" utasítást illik ismerni!)
Miután így a felderítést letudtuk, a következő feladat a probléma leküzdése. Ez már kicsit bonyolultabb, de nem kell kétségbe esni! Az valószínűleg világos, hogy ide nem fér be az általunk elkészíteni szándékozott helyettesítő szubrutin. Az előző számban közöltünk egy LOADER-t a programhoz. Annak az elején volt néhány szubrutin, amit nem részleteztünk kellőképpen (mondhatni sehogyan). Örömmel közöljük, hogy ez a felemelő pillanat most érkezett el! (Legalábbis részben.)
A betöltő így kezdődik:
ORG 256 |
|
A megjegyzés rovatban az egyes JP utasítások címei lettek feltüntetve hexadecimálisan. Az ilyen JP utasításokból álló programrészt hívják ugrótáblázatnak. Kicsit nagyképűen mi is ilyen névvel fogjuk illetni. Mint látható, mindegyik egy-egy rutinra ugrik. Teljesen jogos a kérdés, hogy mi szükség van erre, amikor a rutinokat direkten is lehetne hívni? Ez teljesen igaz, viszont, ha egy rutint megváltoztatunk (például beszúrunk egy byte-ot), az összes többi is megváltozik. Ekkor az összes hivatkozást meg kellene változtatni, ami esetünkben pI., az "ALLKEY" nevű szubrutin esetén eléggé munkaigényes (és felesleges). Az ugrótáblázat alkalmazásával ez a probléma elveszti jelentőségét, mivel itt csak az egyes JP-okat kell módosítani.
Természetesen, ha valaki úgy írja meg a kellő részeket, hogy azokat később nem kell módosítani, akkor nincs szükség az itt leírtakra. Azonban - minden ellenkező híreszteléssel ellentétben - senki sem tökéletes, mindenki követhet el hibákat (sőt, csak azt!), így szerény véleményünk szerint az általunk eloadott módszer a legkevésbé fáradságos!
Az imént már emlegettük az "ALLKEY" szubrutint, a szemfülesebbek biztosan kitalálták, hogy nem véletlenül! Mint neve is sejtetni engedi, ez a teljes billentyűzetet lekérdező rutin. vizsgáljuk meg a működését!
ALLKEY | PUSH BC | ;A BC regiszterpár elmentése ;Ha valamelyik sorban volt lenyomva |
Ezzel a kis programmal szimuláljuk a teljes billentyűzetleolvasást, egy "CALL 106H' utasítással hívhatjuk. Látható, hogy ez is három byte a
XOR A
IN A,(FE)
-hez hasonlóan, tehát gond nélkül elhelyezhető elődje helyén.
A "CALL 106H" utasítás gépi kódja: CD 06 01 (hexában), tehát az előző programrészt módosítsuk a következőre:
3887 | 06 00 | LD B,00 |
Talán feltűnt, hogy az "AND 1F" utasítást "AND FF"-re és a "CP 1F"-et "CP FF"-re cseréltük, ez a teljes billentyűzet figyelése miatt szükséges.
Miután ily módon átestünk a tűzkeresztségen, folytathatjuk a keresést.
3967 | 06 14 | LD B,14 |
Ezek mind egy kaptafára készültek, kár is rájuk több szót vesztegetni, az eddigiek alapján csak rutinmunka az átírásuk.
A most következő viszont annál érdekesebb!
3C10 | AF | XOR A |
Ez, ugyebár ismerős?
3C1A | 06 00 | LD B,00 |
Akik rendelkeznek az előző számban szereplő, nagy kaliberű SPECTRUM billentyű-táblázattal, már tudhatják, hogy ez a "6" billentyűt figyeli.
3C25 | 3E F7 | LD A,F7 |
Az "A" regiszterben az "12345" billentyűknek megfelelő sor.
3C29 | CB 47 | BIT 0,A |
Az "1" billentyű esetén a 87C2H memóriacímre betölt 01H-t, majd ráugrik a 7B5EH címre.
3C36 | CB 4F | BIT 1,A |
A "2" billentyű hasonlóképpen.
3C43 | CB 57 | BIT 2,A |
A "3" billentyű.
3C4E | CB 5F | BIT 3,A |
A "4" billentyű.
3C5A | CB 67 | BIT 4,A |
"5" esetén folytatja a 7CDBH címen, egyébként várakozik, majd újraolvassa a billentyűzetet 256-szor.
3C5F | 76 | HALT |
7852H-n folytatja, ha több mint 5 másodpercig nem nyomjuk meg az 1-6 billentyűk közül valamelyiket.
Talán kitalálták már, hogy ez itt a menü volt. Most mit tegyünk?
Az látható, hogy itt sem fér be a módosított rutin az eredeti helyére, viszont van a mi kis betöltőnkben egy "KEY" nevű szubrutin (103H a belépési pontja). Eléggé furcsán néz ki:
KEY | EX (SP),HL | ;HL és a STACK-ban lévő 16 bites |
A szubrutin hívása némi hasonlóságot mutat az EXOS hívásával:
CALL 103H
DEFB SOR
Például, ha a számok sorát akarjuk beolvasni, adjuk ki a
CALL 103H
DEFB 3
utasításokat.
Előző programrészletünk módosítva:
3C10 | CD 06 01 | CALL 0106 |
|
Ha ezzel is megvagyunk, folytathatjuk önfeledt módosítgatásainkat:
3D15 | 06 19 | LD B,19 |
És most ismét kizökkent bennünket valami kedvenc szórakozásunkból!
3FD9 | 06 19 | LD B,19 |
|
Ezt már akár bekötött szemmel is meg lehet csinálni!
3FD9 | 06 19 | LD B,19 |
|
Ezután már csak egy fontos dolgunk van:
42A4 | AF | XOR A |
Még ezután is van billentyűzetfigyelés, de azt már legközelebbre hagyjuk.
Ha az imént leírt módosításokat elvégezzük (és ki is mentjük!), már be tudjuk állítani a vezérlés módját, a játékosok számát, valamint mindezek méltó megkoronázásaként el is tudjuk indítani a programot. Játszani ugyan nem tudunk vele - mivel igen gyorsan lefagy -, de megtettük az első fontos lépést. Azt, hogy miért fagy le, valamint az egyéb ROM-hívások előtalálásának módozatait legközelebb ecseteljük.
Örömmel közölhetjük, hogy sorozatunk egy történelmi pillanathoz érkezett!
Az alábbiakban leírtak szerint eljárva végre működőképessé varázsolhatjuk mindenki szeretett MOON CRESTA-ját. Legutóbb odáig jutottunk, hogy elindul a játék, de rögvest meg is dermed. Már említettük - valamikor a múlt ködébe veszően - a "lefagyás" lehetséges okait. Mivel az első számú közellenséget (az interruptot) már kétvállra fektettük, marad a nem kevésbé alattomos ROM hívás. (Természetesen más ok előfordulhat, például elfelejtettük bekapcsolni a gépet.) Miután a követendő utat szellemünk lángoló fáklyaként már bevilágította, már csak elő kellene találni a hívások helyeit. Erre alkalmas az itt következő kis program:
| ORG 1000H | ;Ide fordítjuk |
A program megértéséhez annyit kell tudni. hogy SPECTRUM-on a ROM a 0000H-3FFFH címtartományban helyezkedik el. A "CALL" Z80 utasítás kódja HEXA 0CDH, a "JP" kódja pedig 0C3H. Fordítsuk le a programot a memóriába 0 oflsettel. IX regiszterbe írjuk be a program kezdőcímét ("X" parancs, majd a "Registerpair:" kérdesre írjuk be az "IX"-et, a "Value:" kérdésre pedig a program kezdőcímét.) Ezután "G", majd a "Start:" kérdésre adjunk 1000-et. Amennyiben a program talált valamit, egy hanghatás után visszatér az ASMON-ba. Ekkor IX a ROM hívás operandusára fog mutatni.
FIGYELEM!
Nem mind arany, ami fénylik! Véletlenszemen is előfordulhatnak olyan byte kombinációk, amik megegyeznek a ROM hívással. Ezért amint találtunk valamit, a reménybeli hívás címétől számoljunk vissza néhány byte-ot, majd innen listázzuk ki a programot. Ha a listázás során értelmes kódot latunk, valamint a ROM hívás is látható, akkor valószínűleg megtaláltuk. A programban szereplő ÖSSZES hívást javítsuk ki.
Leggyakrabban eloforduló hívások:
006FH
007CH
028EH
03R5H
04C2H
055CH
22B0HEgy "JP (HL)" utasítás.
Egy "RET" utasítás.
KEY-SCAN rutin (billentyűzet leolvasó)
BEEP rutin (hangkiadó rutin)
SAVE rutin
LOAD rutin
PIXEL-ADD rutin (képpont címének kiszámítása)
Kanyarodjunk vissza kedvenc MOON CRESTA programunkhoz. Ha itt keressük a ROM hívásokat, elég sokat találunk. Köszönhető ez annak, hogy a billentyű figyeléseket már kijavítottuk saját rutin hívásokra, amik az alsó 16k területen vannak, imigyen kimerítve a SPECTRUM ROM fogalmát. Viszont a sok álhívás mögött meglapulva akad néhány igazi is. Ezek:
5398 | 2A C4 87 | LD HL,(87C4) |
(A címekhez 4000H-t hozzá kell adni!)
A 03B6H ugyan kimaradt a felsorolásból, de ez mégis a BEEP rutin, csak a kezdeti "DI" utasítást ugorja át a program. A LOADER-ben a 010CH címen kezdődik egy rutin amely feladatáról nem szóltunk, ez a BEEP rutin ENTERPRISE környezetre honosítva. Tehát a javítás így néz ki:
53A2 | CD 0C 01 | CALL 0100 |
Még két ilyen ilyen hívás van, ezeket nem részletezzük:
561A | 11 01 00 | LD DE,0001 |
Ha a javításokat kimentjük, majd betöltjük a programot, már elindul, zenél és játszana, ha tudnánk irányítani. (A zene idézőjelben értendő, nem sokkal múlja felül pl. a BROS színvonalát)
Előfordulhat olyan Olvasó is, aki maga is szeretne aktív részese lenni a játéknak (elégedetlenek mindig vannak). Az ilyen extrém kívánságok teljesítését szolgálja a betöltőben lévő "JOY" nevezetű szubrutin. Működésérői csak annyit, hogy ENTERPRISE-on a két külső joystick leolvasása a 0B6H porton lehetséges. A módszer teljesen hasonló, mint a billentyűzet esetében. A kívánt iránynak megfelelő értéket a 0B5H portra kell kiküldeni, majd a választ a 0B6H port 0.bitjén olvashatjuk be. Ha aktív az irány, akkor a bit 0 értékű. Az irányoknak megfelelő értékek (a külső 1. joystick az EXT1, a külső 2. pedig az EXT2 nevet viseli).
0 - EXT1 tűz
1 - EXT1 fel
2 - EXT1 le
3 - EXT1 balra
4 - EXT1 jobbra
5 - EXT2 tűz
6 - EXT2 fel
7 - EXT2 le
8 - EXT2 balra
9 - EXT2 jobbra
A JOY rutin leolvassa mindkét joystick állapotát, majd ezt az értéket visszaadja az "A" regiszterben, a következő bitkiosztásban:
b0 - jobbra
b1 - balra
b2 - le
b3 - fel
b4 - tűz
A rutin az "RST 28H" utasítással hívható, az aktív irányt a bit 1 állapota jelzi. Az egyszerűség kedvéért a játékban csak a joystick vezérlést írtuk át. A program SPECTRUM-on a KEMPSTON-rendszerű illesztővel üzemel (a JOYSTICK CONTROL územmódban). KEMPSTON joystick esetében a botkormány állapotát a decimális 31 (HEXA 1 FH) portról lehet beolvasni. A bitkiosztás megegyezik rutinunk bitkiosztásával, itt is az 1 állapot az aktív. Tehát nincs más teendőnk, mint előkeresni az összes "IN A,(1FH)" utasítást (2 byte) és kicserélni egy "RST 28H" utasításra (1 byte, a másik byte-ot 0-ra írjuk át).
A joystick figyelés előfordulási helyei:
502BH 5032H 5039H B023H B053H |
6246H 6259H 626EH |
Ezek az "ALSO" file-ban vannak Ezek a "FELSO" file-ban vannak. |
Például: 5028 DB 1E Ezt kell 5028 EF 00 javítani. |
IN A,(1F)
RST 28-ra |
Ha így felszereljük programunkat, betöltjük, majd a '4' billentyűvel JOYSTICK CONTROL opcióba állítjuk, végezetül pedig a '6' billentyűvel elindítjuk a játékot, már lőhetjük is önfeledten a gonosz ellenséges űrhajókat. Azaz...
A színek nem mondhatók éppen ideálisnak - sőt - és a harmadik pályán zöld alapon zöld szín adódik, amitől a zöld csillagokat is vörösnek látjuk. Ezen probléma (amely egyben az utolsó) leküzdéséhez legközelebb adunk tanácsokat...
Örömmel tudatjuk Olvasóinkkal, hogy elérkeztünk az utolsó, és egyben legnehezebb feladatunkhoz, Talán még emlékeznek, hogy kedvenc MOON CRESTA nevű programunk már elindul, játszani lehet vele, de a színek összeállítása meglehetősen avantgard. Az alábbiakban eme kellemetlenség megszüntetéséhez szeretnénk néhány ötletet közölni.
A színek megváltozásának okáról a betöltőkép javítása során már volt szó, így ezt nem részletezzük. A program által generált színek esetében csak annyival bonyolultabb a helyzet, hogy ilyenkor nem tudni, a program melyik részében foglalkozik az attributum memóriával. A sikeres színjavítás feltétele, hogy képesek legyünk a vitális részeket megkeresni a programban.
Hogyan lássunk neki?
Először is: meg kell keresni minden abszolút címhivatkozást, amely az attribútummemóriára mutat, pI.: LD HL,5800H stb. Általában a HL regiszterpárra hivatkoznak, a DE kevésbé népszerű, a BC pedig szinte elhanyagolható. A megfelelő hivatkozásokat a ROM hívás kereső programunk megfelelő átalakításával tehetjük meg. A LD HL,nn utasítás Z80 kódja: 21H. Ha megvan a hivatkozás, utána végig kell követni, mikor tölt adatot erre a helyre, Ez az adat az attribútum-byte. Kijavítása a korábbi számban közölt színtáblázat segítségével történik.
Másodszor: a színbeállítás történhet direkt töltéssel is (pI.: LD (5800H),A). Ezek keresésére és javítására ugyanazok vonatkoznak, mint az előzőekre.
Harmadszor: a bitmap címéből számítják ki a hozzátartozó attribútum címet.
Ennek két leggyakoribb változata (HL pixelcím):
LD A,H
RRA
RRA
RRA
AND 3
OR 58H
LD H,A
avagy
LD A,H
AND 18H
RRA
RRA
RRA
OR 58H
LD H,A
Ezt a módszert leggyakrabban a szövegkiíráskor használják. Megjegyeznénk, hogy az "OR 58H" utasítás helyett az "ADD A,58H" utasítást is előszeretettel alkalmazzák. Természetesen akadnak ettől eltérő (klinikai) esetek is, de ezek a leggyakoribbak. Persze ez nem vigasztalja azt, aki mindjárt az elegyen egy ilyet fog ki.
Ennyi bevezető után lássuk konkrétan, szeretett programunk milyen módszerrel dolgozik. Először próbálkozzunk az attribútumcím számolás keresésével. A jól ismert "FIND" parancs segítségével kereshetünk egy kombinációt. Az "OR 58H" utasítás gépi kódja: F6 58. Az alábbi rutinra bukkanunk: (a teljes rutin):
65AD | E1 | POP HL | ;visszatérési cím |
Talán mindenki rájött, hogy ez egy kiíratórutin (PRINT), Hívása a "CALL A5AD" utasítással történik, utána DEFB utasításokkal definiálja a szöveget. A szövegben a 20H-nál kisebb karakterek után áll egy attribútumbyte. Esetünkben a programozók az 1 kódú karaktert használták kizárólagosan. Tehát adott a következő pont: fel kell deríteni a hívásokat, majd ki kell javítani a színeket.
Célszerűnek látszik az attribútumbyte tárolására szolgáló memóriaváltozó értékadásait is megvizsgálni. És valóban, találunk is ilyet:
5216 | 3E 43 | LD A,43 |
Miután kijavítottuk a szubrutinhívásokat, térjünk át a másik módszerre.
Ebből is akad néhány:
389B | 21 00 58 | LD HL,5800 |
Majd egy bonyolultabb:
3B5E | 21 60 58 | LD HL,5860 |
|
A rutin első fele az opciók beállítását szolgálja. A kiválasztott opció villog. (FLASH bit beállítva) Ezt úgy tudjuk kiküszöbölni a legegyszerűbben, hogy inverz hátteret állítunk be A második fél egyszerű színbeállítást végez.
Még van egy rejtett színállítás a 6643 címen:
6643 | E6 47 | AND 47 |
A programsor környezetének megvizsgálása után nyilvánvalóbb lesz a dolog, ezt az Olvasókra bízzuk. Ezek után a MOON CRESTA már színhelyesen kezdődik, de játék közben még bőven akadnak színhibák (a SPRITE-ok). A játék így még eléggé élvezhetetlen, de úgy gondoljuk, aki idáig szorgalmasan kitartott, annak egy egyszerű ujjgyakorlat a további munka, így nem lőjük le a poént.
Végezetül néhány szó a 128k SPECTRUM-ra írt programokról: Két nagy csoportra oszthatjuk a mezőnyt.
Egyrészt vannak a 48/128 fedőnevű programok. Ezek a mezei 48k-s SPECTRUM-on is működnek, általában a pályákat külön-külön töltik be. Ezek a 128-ason egy részletben betöltődnek, gyakran zenét, beszédet, plusz animációt stb. tartalmaznak. Ilyen pl. a ROBOCOP, amely zenét, valamint digitalizált (igen jó minőségű) beszédet is tartalmaz amellett, hogy minden szint a memóriában tartózkodik (nem ideiglenesen).
A másik csoport, amely kizárólag 128k-s gépen fut, esetleg 48k-s változata nem is létezik. Ezek a programok a legjobbak, mivel hosszuk többszöröse az előző csoporténak, így bonyolultabb a játék. Ezen csoport éllovasa a "WHERE TIME STOOD STILL" című program, amely bonyolultsága, grafikai kidolgozása. zenéje igazi mestermunka. Ide sorolható még a BEDLAM, THE MUNCHER, GHOULS 'n' GHOSTS, valamint a STARGLIDER 2 című program is.
Az ENTERPRISE szemszögéből az első csoport könnyen átírható, a második csoport pedig rizikós. Nehézséget az okoz, hogy míg SPECTRUM-on a teljes 128k kihasználható, addig ENTERPRISE-on az LPT-nek kell helyet hagyni. Ha nincs ennyi memória sem: akkor a reménybeli átíró makacsságán múlik a dolog. Példaképpen: az említett WHERE TIME STODD STILL programban nem volt 200 byte szabad hely sem, de azért...
A 128k SPECTRUM memóriakezeléséről már volt szó. Ezt úgy lehet átírni, hogy minden SPECTRUM szegmenshez hozzárendelünk egy ENTERPRISE szegmenst, a
LD BC,7FFDH
LD A,x
OUT (C),A
utasításokat pedig átírjuk egy
LD A,y
OUT (0B3H),A
utasításcsoportra. x az eredeti, y a módosított szegmensszám.
A zene átírása már keményebb dió. Az AY-3-8912 regisztereinek leírása a SINCLAIR SPECTRUM JÁTÉK ÉS PROGRAM V. c. kötetében található (LSI ATSz. 1988.). A feladat az hogy olyan szubrutint készítsünk, amely a megfelelő AY és DAVE regisztereket egymáshoz rendeli. Ezt a problémát még egyedüli és üdvözítő módon nem sikerült megoldani.
A problémák:
Nos, ennyi lett volna ez a bűvös tudomány. Ha valaki végigolvasott minden részt, attól még nem biztos, hogy képes is önállóan végrehajtani egy ilyen műveletet (sőt!), de a kiindulás talán világosabb lett. Akit hidegen hagyott az a perspektíva, hogy saját maga írhat át programokat, esetleg az egyes rutinok programozási módszereibol okulhatott valamit, hiszen eléggé átfogó jelleggel kellett használni mind a gépet, mind az operációs rendszert.
Utolsó gondolatként annyit, hogy reményeink szerint sokan kaptak kedvet a témához, és futószalagon szállítják a jobbnál-jobb átiratokat. Mivel eredeti ENTERPRISE programok már nemigen készülnek és sok embernek van ilyen gépe, napjainkban egyre nagyobb szerepe lesz az amatőröknek.
Zozosoft instrukciói a ZX Spectrum programok átírásáról
Ahogy a cikkben is írva van, az első lépés a program fájljainak megszerzése. A cikkben leírt program anno bekerült a Spectrum Emulátor ROM-jába is, SCOPY néven. Viszont manapság nem túl valószínű, hogy valaki tényleg kazettáról akarna Spectrum programot beolvasni, sokkal valószínűbb, hogy a netről szerzi be az ember a Spectrum progit, .TAP vagy .TZX formában. Ezért a nemrég elkészült 4.1-es verziójú Emulátor ROM nem csak be tudja tölteni ezeket, hanem az SCOPY is ki lett egészítve ezeknek a kezelésével. Így néz ki a Bumpy szétszedése:
![]() |
Mint az Emulátoron való futtatáshoz, itt is célszerűbb a .TAP verzió választása, ez mentes a spéci betöltőktől és egyéb trükkös védelmektől.
Spectrumon a legtöbb játék egy BASIC betöltővel kezdődik, így van ez jelen esetben is. Tehát első lépésben ennek tartalmát kell megtudnunk. Erre is kitűnően használható az Emulátor, a programot MERGE "" paranccsal töltsük be, így nem indul el egyből, ki lehet listázni:
![]() |
Ezután már tudjuk a program betöltési címét (25000) és indítási címét (43900).
Ezután jön a betöltő megszerkesztése, ismét a már jól ismert betöltőmet használtam fel. A memória lefoglalás, ennek függvényében videocím kiszámítás, a rendszerhez való visszatérést biztosító HIBA eljárás a szokásos, "100% EXOS kompatibilis"
Amit most megemlítenék, hogy az elmúlt félórában tovább fejlesztettem memóriakezelést, így most már nemcsak, hogy EXOS 2.0 kompatibilis, hanem RAM bővítés nélküli EP64-en is fut a program! Gyakorlati értelme nincs sok, de szeretem a kihívásokat, a vajon hogyan lehetne megoldani problémákat. De hátha majd előkerül a világból egy EP64 tulajdonos, és nagyon fog örülni, hogy végre egy játék ami az ő gépén is fut. Gyakorlatilag a hivatalosan kiadott néhány játékon kívül nincs egyetlen program se ami menne EP64-en.
Egyik ok, a közvetlen szegmenscímek használata, ami megakadályozza azt is, hogy felbővített EP64-en fussanak a programok (kivéve, ha F8-FB címeken is van bővítés). Másik ok, hogy nincs elég RAM egy 64K-s gépben, ami első hallásra elég furcsa, hiszen egy 48K-s program miért ne férne el egy 64K-s gépben? Egy átlag Spectrum átírat 4 szegmenst használ: egy videó szegmens az alsó 16K Spectrum memóriának, amiben található a képernyő memória is. Két másik szegmenst a felső 32K Spectrum memóriának, és kell még egy videó szegmens az LPT tábla számára. Viszont egy 64K-s gépen csak 2 szabad szegmens van... a másik kettő a nullás lap ill. a rendszerszegmens céljára van felhasználva.
Azt már eddig is tudta a betöltőm, hogy az LPT tábla akár lehet megosztott szegmensben is, tehát ha van elég hely benne, a rendszerszegmens elejére kerül az LPT tábla, így máris megspóroltunk egy szegmenst. De még mindig kéne egy... Egyszerű a megoldás: a nullás lapról tegyük át az EXOS cuccait, no meg a saját programunkat a rendszerszegmensbe, állítsuk be a rendszerszegmenst nullás lapnak, és máris miénk az eddigi nullás lap szegmens!
Gyakorlatban így működik ez a fejlesztett betöltő:
Ha a 3. szegmens lefoglalásánál EP64 problémával találkozik, akkor a következő történik:
Ennek megfelelően módosult egy kicsit az LPT készítő rutin is, hogy az előre megadott címen kezdje készíteni az LPT szegmensben az LPT-t. Ez normál esetben nulla, ha EP64 módban fut a program akkor lesz 800H. Természetesen ezzel módosult egy kicsit az LPT cím kiadása is a NICK-nek, hiszen így a 8-11-es címbitekkel is kell foglalkozni.
A HIBA eljárás is bővítve lett, ha lett eltárolva nulláslap szegmensszám, akkor kilépés előtt helyreállítja azt, visszamásolva az első 800H bájtot. Ily módon még EP64 módban futva is megőriztük a rendszer teljes épségét, töltési hiba, vagy meleg reset esetén szépen kilép az EP logóhoz a program!
Kisebb módosítások voltak az EXOS 2.0 kompatibilitáshoz (Amit már a CPC betöltőmben is megcsináltam), a kép alján lévő státuszsor az EXOS-tól lesz átvéve, ill kilépéskor a rendszerszegmensből lesz kiolvasva a visszaállítandó EXOS LPT címe.
Miután sikerült a szükséges memóriát megszerezni, elkészíti az LPT táblát (részletesen lásd az SpV cikkben), majd betölti az SCR fájlt, ami az SCOPY-s kicsomagolás után kapott BUMPY.3FF átnevezésével készült. A kép betöltése után átkonvertája az attribútum terület minden egyes bájtját EP formátumra. Egy bájt konvertálása a következő módon történik: egy 256 elemű táblázatra rácímezve (H a cím felső 8 bitje, L a konvertálandó attributúm) kiolvasható a Spectrumos attribútum bájt EP-s megfelelője, amire le lesz cserélve az adott bájt.
Ez a táblázat az Emulátor új ROM-jának fejlesztésekor készült, ebben benne van az is, hogy az elvileg villogó kombinációk inverzek legyenek, legalább így különbözve a FLASH nélküli azonos kombinációtól. Az Emulátor eredeti programja, meg sok átírat nem foglalkozik ezzel a problémával, így akad jó pár játék, ahol nem tudni, hol áll a menüben a kurzor, mert az eredetileg villogna... ezzel a módszerrel inverz lesz a kurzor, és így már használható lesz.
Hogy a táblázatra címezni tudjunk, annak xx00H címen kell kezdődni. Eredetileg úgy működött a betöltőm, hogy a programban lévő táblázatot 1000H címre másolta induláskor, és itt volt használva. Most az EP64 kompatibilitás miatt helyspórolás okán lekerült 100H-ra. Így viszont induláskor hiányzik az első három bájt, mivel ott a táblázatott átugró JP található, így ez a 3 bájt az első konvertálás előtt lesz kitöltve.
Miután a betöltő kép attribútumai át lettek konvertálva, átkapcsolunk a Spectrumos LPT-re, ezután következik a PRG betöltése (ami eredetileg a BUMPY.5FF fájl volt).
A sikeres betöltés után jön még egy nagyon fontos dolog. Mivel a letöltött .TAP fájl mellett rögtön ott volt egy örökélet POKE is, így ezt is beletettem, hátha valaki élni akar ezzel a lehetőséggel. A kérdést a státuszsorba kiírva tesszük fel, majd az Y és N billentyűk figyelése következik végtelen ciklusban. Y válasz esetén elvégezzük a POKE-t, és ezután jöhet a program indítása. (Mellesleg ennek a kérdésnek az is a mellékhatása, hogy floppyról töltve is meg lehet nézni alaposan a betöltő képet, nem tűnik el egyből. Ezért más programoknál ha nincs is ilyen kérdés, akkor is be szoktam rakni egy billentyűre várakozást.)
Visszatérve a Bumpy átírásához: miután van remek betöltőnk, el is indul a program, persze a menü kiírásánál többet nem igen csinál, hiszen nincs még billentyűfigyelés se... A CPC programoknál azt láthatjuk, hogy leginkább ROM hívásokat használnak, ami nem véletlen, hiszen elég bonyolultan lehet közvetlenül programozni a CPC HW-t (pl. a billentyűzetlekérdezés is hasonlóan bonyolult, mint ahogy a hangchip regisztereit lehet kezelni).
Spectrumon jóval egyszerűbben kezelhető a HW, így a játékok nagy része ezt a módszert alkalmazza billentyű, joystick lekérdezésre, hang generálásra. Így nem működik az a módszer, amit a Popup esetén láttunk, hogy a betöltőben elkészítve a megfelelő ROM rutinok szimulációját, gyakorlatilag az eredeti program fájlokhoz hozzá se nyúlva működőképes lesz a program. Meg kell keresni, és át kell írni az ilyen közvetlen HW hívatkozásokat.
Kezdjük az irányítással, ehhez a FEH prot olvasásokat kell megtalálnuk. A keresgélést FENAS-ban célszerű végezni, így a helyére tölthető a PRG fájl, Bumpy.prg esetén az 61A8H-F8D3H területet jelent. (Talán nem véletlen, hogy Moonlight-ék úgy alkották meg a FENAS-t, hogy 4000h-FFFFh-ig szabad a memória Így erre a célra sokkal alkalmasabb a FENAS-t használni, hiszen így az eredeti helyére tölthetőek a program darabok. A disassembler rész is sokkal használhatóbb, mivel nem egy gyorsan elszaladó listát látunk, hanem a fel/le gombokkal tudunk mozogni a programban.)
A program az AB7CH címen indul. Szerencsére ez esetben nincsenek olyan trükkös átkódolós védelmek, mint az SpV-ben ismertetett Moon Cresta esetén, (persze más programnál simán bele lehet futni ilyesmibe), így egyből lehet a lényegre koncentrálni. FEH portolvasást egy helyen találni:
![]() |
A rutin első része sorban lekérdezi az összes billentyű sort, és eltárolja. A ciklus után van még egy igen érdekes IN A,(0DFH)! Ilyen port elvileg nem is létezik Spectrumon. A megfejtés: A legtöbb Spectrum HW nem alkalmaz teljes címdekódolást (így meg lehet pár IC-t spórolni), ezért az egyes I/O portok több címen is láthatóak. Pl. az ULA 0FEH portja az összes páros című porton... Jelen esetben a Kempston típusú joystick illesztő lekérdezésről van szó, ami normál esetben az 1FH porton történik. Még szerencse, hogy közvetlenül a billentyű lekérdezés mellett van, különben órákig lehetett volna keresgélni, hogy hol is kérdezi le, amikor nincs 1FH port hivatkozás.
A program módosításához már készen állnak a betöltőben a kész rutinok, az elv hasonló az SpV-ben leírtakhoz. A fontos rutinok címeit én a nem használt RST rutin belépési pontokra teszem, így 3 bájtos CALL helyett elég egy bájtos RST utasítás a meghíváshoz, így mindenhova befér.
Jelen esetben minket a PORTFE rutin érdekel elsőként, ezt szint a Spectrum Emulátor új ROM-jához fejlesztettem ki eredetileg. Ez nemcsak a FEH port olvasását tudja emulálni, hanem annak írását is, azaz a hang generálást, ill. a keretszín (konvertált tehát színhelyes beállítását is).
Paraméterként felhasználja az RST utasítás utáni bájtot is, 0DBH jelenti az IN A-t, 0D3H az OUT A-t, 0EDH az IN A,(C)-t, de szükség esetén más kombinációk is legyárthatók (IN B,(C), stb). Beolvasásnál a megadott sor(okat) olvassa be, és adja vissza, teljesen Spectrumos módon. A számbillentyűknél be van keverve a dologba az Internal+ALT is, a Sinclar joystick illesztőt szimulálva. Az Emulátoros verzióban az External joy-ok is itt voltak bekeverve, az átírásos verzióban azokat a Kempston illesztő szimulálására tartogatjuk, JOY névre hallgató rutinban.
Bumpy indulásakor a PORTFE az RST 28H-ra kerül a betöltőben, a JOY pedig az RST 8-ra. Így csak annyit kell tenni, hogy B423H címen található IN A,(0FEH) utasítást kicseréljük:
B423 | EF | RST 28H |
A joystick olvasó IN A,(0DFH)-t pedig:
B42B | CF | RST 08H |
Ezekután már lehet is játszani, csak hang nincs még, és itt-ott színhibás a program.
Hang
Következik a hang: erre sajnos nem túl sok szót vesztegettek a SpV cikkben. A Moon Cresta a Spectrum ROM hangrutinját használta, amelynek EP-re módosított változatát berakták a betöltőbe BEEP néven. És annyival elintézték az ügyet, hogy a ROM rutin hívását cseréljük ki ennek a rutinnak a hívására.
Először is itt volt egy nagyon csúnya nyomdahiba!!! Az ott közölt rutinban az 0A7H portra küldik ki az adatot, ami teljesen értelmetlen! A helyes az 0A8H vagy 0ACH! (0-ás hangcsatorna bal ill. jobb hangerő). Ennek a nyomdahibának köszönhetően elég sok néma átírat került forgalomba, mivel a hibás 0A7H címet használták... jó pár ilyet ki is javítottam már.
De mi is a helyzet a közvetlen port piszkálással történő zenéléssel Spectrumon? Hiszen a legtöbb játék ezt használja. Itt szintén a 0FEH port kap szerepet, ezúttal OUT utasítással kezelve. Az ide írt adat 0-2 bitje a keretszínt határozza meg. A 3. bit a magnókimenetre vonatkozik, ez minket nem érdekel, a 4. bit jelképezi pedig a ZX Spectrum összes zenei képességét 1 bites D/A kimenet formájában.
Először tehát meg kell keresnünk az összes 0FEH portra vonatkozó utasítást, és utána kezdeni velük valamit. A betöltő induláskor D/A módba kapcsolja a DAVE 0-ás hangcsatornáját, ezután az előbb emlegetett portok segítségével tudunk hangot generálni. Sok átiratban egyszerűen csak kicserélik az OUT (0FEH),A utasításokat OUT (0A8H),A-ra (vagy 0ACH-ra) ami végül is működő megoldás, de van 1-2 probléma vele. Egyrészt nem foglalkozik a keretszín problémájával, másrészt mivel csak az egyik hangerőt állítja a DAVE-ben, így ha sztereó kimeneten át hallgatjuk kedvenc EP-nket, akkor csak az egyik oldal fog szólni, ami elég zavaró, esetleg a programozásban kevésbé jártas felhasználó elkezdi keresni a kontaktos kábelt, hogy miért nem szól a másik oldal.
A már emlegetett PORTFE rutinunk mivel a Spectrum Emulátorhoz készült, OUT esetén természetesen kezeli a keretszín problémáját is, és persze a hangot is mindkét oldalra adja ki. Egyetlen probléma, hogy ez így elég sok utasításból áll, ami intenzívebb hangpiszkálásnál észrevehető lassulást okozhat. Köztes megoldásként készült a CSAKOUT nevű rutin, ami egyrészt nincs beágyazva az általános PORTFE rutinba, így már egy csomó utasítást megspóroltunk, másrészt a keretszínnel se foglalkozik. Magyarán egy OUT (0A8H) és egy OUT (0ACH) utasítás az egész.
A Bumpy esetén először az általános rutint alkalmaztam, majd úgy találtam nem az igazi a hang, viszont a keret mindig fekete, így végül a CSAKOUT módszer lett a végleges.
OUT (0FEH) utasítás a következő címeken volt a Bumpy-ban: B485H, EC4AH, ED1BH, EF34H, F514H, F525H, F53BH, F550H, F569H, F59CH. Itt mindenütt az
OUT (0FEH),A
helyett:
C7 | RST 00H |
utasításokra lett átírva. Ezután már hang is van. A Bumpy-val idáig eljutni a .TAP letöltésétől számítva kb. 10 perc volt.
Attribútumok
Az átírás utolsó fázisa pedig az attribútumok kijavítása... ez a legnehezebb, legidőigényesebb művelet.
Itt nincs semmilyen általános módszer, minden programban más és más módszert alkalmaznak. A felderítésre a SpV cikk 9. része ad útmutatást. A lényeg az, hogy az 5800H-5AFFH közti memória területekre irányuló műveleteket kell figyelni.
Ahol fix érték beírását találjuk, ott kinézzük a konvertáló táblánkból (ami a betöltő forrászszövegének elején található) a megfelelő értéket, és kijavítjuk.
Ennél sokkal gyakoribb, az amikor egy regiszterben megkapott érték kerül beírásra, leggyakrabban egy LD (HL),A utasítás formájában. Itt a legegyszerűbb kicserélni ezt az utasítást egy olyan rutin meghívására, ami már a konvertált értéket írja be. Erre szolgál a KONV nevezetű rutin a betöltőben, ami RST 10H-re kerül, tehát a megtalált ilyen LD (HL),A utasításokat RST 10H-ra kell cserélni.
Az adott programtól függően persze más módszerek is előfordulnak, a KONV alapján könnyen legyárthatóak. Pl. a BUMPY esetén előfordul LD (HL),C variáció, ez KONV2 néven található meg a betöltőben, és az RST18H-ra kerül.
Szintén nagyon gyakori, még az LDIR, LDI (néha LDDR, LDD) alkalmazása, általában akkor amikor a játék valamely előre elkészített grafikai eleme (falak, tárgyak,stb) kerül a képernyő memóriába másolásra.
Bumpy esetén LDI fordul elő, ez KONV4 néven kerül megvalósításra, ez az RST 20H-ra kerül. Ha a KONV4-ben a kipontosveszőzött JR utasítást visszatesszük, akkor LDIR-t kapunk, az INC HL, INC DE utasítások DEC-re cseréjével pedig az LDD, LDDR állítható elő.
Konkrétan:
B471 | LD A,07H |
Könnyen felismerhető, hogy ez egy attribútum terület törlés, ráadássul 7-es értékkel, ami EP-n is 7-es, így ezzel nincs további dolgunk.
B4F8 | LD HL,5800H |
Az LD (HL),06H szintén maradhat, viszont az LD (HL),A-t ki kell cserélni egy RST10H-ra
B6F1-en meg van egy LD DE,5803H és utána egy nagy rakás LDI, amit most lusta vagyok ide bemásolni ezeket mind RST 20H-ra kellett kicserélni.
F0D8-on pedig szintén egy attributum terület törlés megadott értékkel, itt az F0E4-en lévő LD (HL),A-t kell RST 10H-ra cserélni.
E799 | LD DE,5800H |
Ezt az LD (HL),C-t kell RST 18H-ra cserélni, E7A8-on ven még egy.
E2E4-en van még egy LD DE,5800H, ADD HL,DE című rész, amihez végül E30E címen tartozik egy LD (HL),06H. 6-os attributum tehát lehet békén hagyni...
Ezekután már egész jól ment a program, csak a felvehető tárgyak voltak makacsul világoszöld alapúak...
Itt kezdődött kb 3-4 órás szívás amíg próbáltam újabb helyeket találni, amik az attributum területeket piszkálják...
Végül rájöttem, hogy az előbb átugrott E30EH címen lévő LD (HL),n utasítás a bűnös!
Igaz, hogy ott alapban 6-van amit nem kell bántani, de itt látható egy szép példája a Neumanni elvek alkalmazásának, miszerint a program és adat ugyanabban a memóriában tárolódik. Jelesül, hogy itt egy önmagát módosító programról van szó!
E2E4 | LD DE,5800H |
Na itt a bűnös! Az E2E9H-en lévő utasítás írja át az E30EH címen lévő LD paraméterét!
Ennek megvalósítására készült egy E30F nevű rutin a betöltőben, amely szabad RST hiányában az 0005H címre kerül, és az E2E9H az LD utasítás CALL 0005H utasításra lett cserélve.
A fakó szín problémáról:
Spectrumon 8 szín van, plusz van egy BRIGHT bit, így megkapjuk ezek fényes változatát, azaz még 7 színt (fényes fekete nincs).
Enterprise-on 8 színt szabadon választhatunk, 8 színt csak egyben a FIXBIAS által.
Hova állítsuk a FIXBIAS-t?
Sok program esetén (gondolom az SPV-t követve) 0-ra állítják. (illetve van, hogy nem is állítják, így hidegindítás után jól fut a program, mivel 0 az alapérték, de ha már valami más program átállította, akkor előjönnek színhibák). Ez esetben az a jó, hogy FIXBIAS-ban lesz fekete, és a palettában is beállítunk feketét, így lekezeltük azt, hogy csak egyféle fekete van.
A rossz viszont az, hogy a fekete mellé kapott másik szín túl sötét még normál Spectrum színeknek is. És ha nem akarunk túl nagy kontraszt különbséget, akkor a palettába választott színek is sötétebbek lesznek mint a fényes Spectrum színek. Ez különösen a fehérnél szokott feltűnni.
Van még egy nagy hiba az SPV-ben: "A már említett programunk a FIXBIAS regiszterbe 0-t tölt. Az így előálló színkombinációk megfelelnek a SPECTRUM BRIGHT színeknek."
Na ezt nagyon nem kellett volna! Ezek a színek normálnak is sötétek, nemhogy fényesnek... Ha valaki ebből indul ki, akkor az eredeti fényes színek lesznek nagyon sötétek, a normálok meg a fényesnél világosabb de sötétek!
Én onnan indultam ki, hogy a fehér az legyen fehér! Vagyis 255-ös FIXBIAS. Itt a fehér mellé kötelezően kapott színek egész jól megfelelnek a Spectrum fényes színeinek. Emellé már gyerekjáték a palettába összeválogatni közepes fényerejű színeket Spectrum normál színeknek.
Egyedüli probléma, hogy a BIAS-ban kapunk egy fényes feketét is, tehát konvertáláskor ügyelni kell, hogy a fényes fekete normál feketére legyen konvertálva (de néha akár ki is lehet használni ezt a plusz színt is!). Összehasonlításképpen néhány paletta:
Spectrum | ![]() |
Spectrum Világ | ![]() |
BIAS=0, nagy kontraszt | ![]() |
Wec Le Mans (Attus) | ![]() |
ZozoSoft | ![]() |
Az Enterprise teljes színpalettája (a bal oldali hasábban a BIAS):
Itt egy kis BASIC program ami egy tömbben összerendeli a Spectrum színeket az EP színekkel. EP-n olyan palettát feltételezve, ahol a 8 szín megfelel sorban a normál Spectrum színeknek, a FIXBIAS pedig a fényes színeknek, a fényes fekete sima feketéhez lesz rendelve. A második részében pedig minden lehetséges attributum kombinációhoz kiszámolja az EP megfelelőt. A FLASH-t invertálással helyettesíti. Ezt a táblázatot fájlba menti.
Ez a tábla van használva a Spectrum Emulátor programjában, ill. az átirataimban is.
Menü szöveg átírása már rutin munka, és kész is volt a Bumpy.
István instrukciói a 128K-s hangok átírásáról
Az SPV cikke csak érintőlegesen tesz csak említést a 128K-s hangok megvalósításáról. Az egyik kardinális problémaként a hardware burkológörbéket említi.
A burkológörbe megszakításból emulálható. Természetesen ez elfogyaszt némi CPU időt, és nagyobb burkológörbe frekvencia esetén a minőség sem tökéletes, de gyakran elfogadható eredményt lehet elérni. CPC átiratoknál (melyben szintén az AY chip felelős a hang előállításáért) jól használható a 300 Hz-es megszakítás, de ha fontos a minőség, akkor CPC és Spectrum átiratnál is lehet 1 kHz-es megszakításból emulálni a burkológörbét (ilyenkor kisebb problémát jelent, ha a játék HALT utasításokat használ időzítésre) - így még a Wec Le Mans motorhangja is, ha nem is tökéletes, de azért felismerhető. Az is megoldható, hogy ne minden 1 kHz-es megszakítás frissítse a burkológörbét, hanem csak például minden negyedik (rosszabb minőség, de kisebb CPU igény).
Ha egy csatornán egyszerre szól(na) a zaj és a tiszta hang, ezt úgy oldottam meg, hogy fordításkor választhatóan csak négyszögjel vagy csak zaj legyen ilyenkor, vagy négyszögjel valamilyen torzítással.
AY emuláció ismertetése előtt nézzük át röviden az AY regisztereit:
0:0.0000 4:0.0423 8:0.1691 12:0.5704 1:0.0137 5:0.0618 9:0.2647 13:0.6873 2:0.0205 6:0.0847 10:0.3527 14:0.8482 3:0.0291 7:0.1369 11:0.4499 15:1.0000
Amint látható, a 0-5 regiszterek nagyjából megfelelnek a DAVE A0h-A5h portjainak, de a DAVE négyszögjel frekvenciája eltérő módon számítható:
125000 / (N + 1) (ha a BFh I/O port 1. bitje 0) 83333.33 / (N + 1) (ha a BFh I/O port 1. bitje 1)
Ezek az értékek 4 MHz-es gépre érvényesek, egyébként az órajellel arányosan nagyobbak.
Zajgenerátornak felhasználható a DAVE első három csatornája is 17 bites polinom számlálóval (ehhez az A6h portra 10h-t kell írni, ami felcseréli a 7 és 17 bites számlálót, azaz így az 1-3. csatorna használhatja a 17 bites, a 4. (nem használt) csatorna pedig a 7 bites számlálót). Figyelni kell azonban arra, hogy a zajgenerátort ez a négyszögjel frekvenciájának a kétszeresével mintavételezi, azaz a fel- és lefutó élnél is.
Ezek alapján a Spectrum és CPC frekvencia értékek így közelíthetők:
Spectrum négyszögjel: N * 1.125 - 1 Spectrum zaj: N * 2.25 - 1 CPC négyszögjel: N * 2 - 1 CPC zaj: N * 4 - 1
A Spectrumnál az 1.125 ugyan nem egészen pontos, de az eltérés kisebb, mint 0.25%, és ez az arány egyszerűen számítható néhány Z80 utasítással.
Spectrum négyszögjel frekvencia konverzió (HL AL):
.l1:
.l3:sra b
rra
sra b
rra
adc a,l
ld l, a
dec hl
ld a, b
adc a,h
cp 10h
jr nc,.l3
...
inc l
inc a
jr z,.l1
ld l, 0ffh
ld a, 0fh
jp .l1
; overflow?
Spectrum zaj frekvencia konverzió (A A):
cp 1
adc a, 0
ld h, a
rra
sla h
rra
adc a,h
dec a
Ezek figyelnek a túlcsordulásra, a "speciális" N=0 esetre, ami azonos az N=1-el, és még arra is, hogy az eredmény ne lefelé legyen kerekítve, hanem a pontos (tört) értékhez a legközelebb legyen. Mindkét esetben a kód feltételezi, hogy a felső, AY által nem támogatott bitek 0-ra vannak állítva.
A CPC jóval egyszerűbb. Négyszögjel (HL AL):
.l1:
.l3:add hl,hl
dec hl
ld a,h
cp 10h
jr nc,.l3
...
inc l
inc a
jr z,.l1
ld l,0ffh
ld a,0fh
jp .l1
; overflow?
Ez ugyan nem egészen jó, mert a 0 frekvenciát 0-ra konvertálja, pedig 1-re kellene. De mivel normál esetben (torzítás nélkül) egyik sem hallható, ez talán nem jelent komolyabb problémát. De egyszerűen javítható is az INC L helyére LD L, 1-et írva.
CPC zaj (A A):
cp 1
adc a,0
add a,a
add a,a
dec a
A hangerőt egyszerű táblázattal lehet konvertálni (de ha nem fontos, hogy pontos legyen, megfelelhet a 4-el való szorzás is):
defb 0,1,2,3,4,5,6,9
defb 12,17,22,28,36,44,53,63
A hangerő táblázatot több helyen lehet látni például különböző PC-s CPC és Spectrum emulátorokban (kisebb eltérésekkel, az alkatrészek szórása és mérési pontatlanságok miatt), de talán az eredeti AY dokumentáció is említi, hogy nem lineáris, csak nem jó értékeket adtak meg. Az itt látható táblázat a FUSE Spectrum emulátorból van.