Zozosoft instrukciói a  ZX Spectrum programok átírásáról

 

 

Akkor közkívánatra, tessék :)

 

Az SpV cikksorozatát a témáról ugye mindenki ismeri, aki mégse az letöltheti az összes SpV-t a sinclair.hu-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 betudja 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ő:

 

-Lefoglal egy videószegmenst, ami majd az 1-es lapra kerül mint alsó 16K Spectrum RAM, ha nem sikerül, akkor HIBA rutinon át kilépés.

-Lefoglal egy akármilyen szegmenst a 2-es lapra, ha nem sikerül, HIBA...

-Megpróbál lefoglalni egy akármilyen szegmenst a 3. lapra, ha nem sikerül, akkor jön az EP64 probléma...

-Megpróbál lefoglalni egy videószegmenst az LPT táblának, ez lehet megosztott is, ha van benne elég hely (EXOS felhasználói határ beállítással ellenőrizve).

 

Ha a 3. szegmens lefoglalásánál EP64 problémával találkozik, akkor a következő történik:

 

-Ellenőrzi, hogy ez a 3. szegmens megosztott, és van-e benne elég hely a nullás lap eleje+LPT tábla számára, ha nincs akkor HIBA...

-Ha van elég hely, akkor jön a trükk: a nullás lap első 800H bájtja (ebbe benne van az EXOS rutinja a lap elején, plusz a 100H-tól kezdődő programunk) át lesz másolva megosztott szegmens elejére, természetesen az felhasználói határ ennek megfelelően lesz beállítva, hogy EXOS ne tudjon lefele terjeszkedéssel bajt okozni. Átmásolás után a rendszerszegmens 0BFFCH címén átállítjuk a nullás lap számát. Az eredeti szám természetesen el lesz tárolva. Ezután a felszabadult nullás lap szegmens kerül a 3. lapon felhasználásra. Az új nulláslap szegmens száma kerül majd bejegyzésre az LPT szegmensnek is.

-Az LPT készítő rutin számára pedig bejegyezzük, hogy 800H-tól készítse majd a szegmensben az LPT-t.

 

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 (részletesen kifejtve az SpV cikk 6. és 7. részében). Mint tegnap említettem 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.

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

B424 DB          0DBH

 

A joystick olvasó IN A,(0DFH)-t pedig:

 

B42B CF          RST 08H

B42C 00          NOP

 

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

00          NOP

 

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.

 

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ásszö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

B473 LD HL,5800H

B476 LD DE,5801H

B479 LD BC,2FFH

B47C LD (HL),A

B47D LDIR

 

Könnyen felismerhető, hogy ez egy attribútum terület törlés, ráadásul 7-es értékkel, ami EP-n is 7-es, így ezzel nincs további dolgunk.

 

B4F8 LD HL,5800H

B4FB LD B,18H

B4FD PUSH BC

B4FE LD (HL),06H

B500 INC HL

B501 PUSH HL

B502 POP DE

B503 INC DE

B504 LD (HL),A

B505 LD BC,1CH

B508 LDIR

 

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

E79C ADD HL,DE

E79D LD (HL),C

 

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

E2E7 ADD HL,DE

E2E8 LD A,C

E2E9 LD (0E30FH),A

....

 

E30E LD (HL),06H

 

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.

 

Menü szöveg átírása már rutin munka, és kész is volt a Bumpy