Sinclair Spectrum Játék és Program 2.
Bevezetés
A "Sinclair Spectrum játék és program" c. könyv megjelenése után - a nagy érdeklődésre való tekintettel - vetődött fel az újabb kötet elkészítésének gondolata. Ennek tartalmát elsősorban a felhasználók igénye szerint állítottuk össze, követve persze az előző könyv szerkezetét.
A terjedelem viszont csökkent, elsősorban azért, mert úgy érezzük az olcsóbb, de tartalmas könyvek népszerűbbek a felhasználók körében, ugyanakkor igyekeztünk arra vigyázni, hogy az oldalszám csökkentése ne menjen a tartalom rovására, melynek törzsét a játékleírások mellett most is a felhasználói programok és egyéb rutinok alkotják.
Mikrolexikonunk azonban megváltozott! Elmaradtak az egyértelműen "szubjektív" osztályzatok, és nem volt célunk a legutolsó "salátáig" az összes olyan játékot felsorolni, amelyik kimaradt az előző lexikonból. Ebből a megfontoláshól a lexikon nagyobb részét azok a programok alkotják, amelyek zöme a könyv megjelenésével párhuzamosan terjed el a Spectrumosok táborában.
A "Mit játsszunk, hogy játsszunk?" c. fejezet összeállításánál természetesen előnyben részesítettük az 1986-os év legnépszerűbb játékait. Célunk most sem az volt, hogy leírjuk egy-egy játék folyamatát elejétől a végéig, hanem az, hogy lehetőséget biztosítsunk a játékban való nagyobb elmélyedésre. A leírások mélysége - úgy érezzük - ehhez éppen elegendő.
A Mega Basic a Beta Basic után az egyik legelterjedtebb Basic bővítő. A felhasználók igényén túlmenően mi is úgy éreztük, hogy ennek. a programnak az ismertetésére szükség van. A demo lista szemléletesen bemutatja a program néhány fontosabb alkalmazási lehetőségét.
A Spectrumon alkalmazott Compiler-ek ismertetése idáig kimaradt a megjelent szakirodalmakból. Ötödik fejezetünk egy összehasonlító ismertetőt közöl arról az 5 compiler-ről, melyek jelenleg a legelterjedtebbek a felhasználók körében.
A könyv utolsó fejezete azoknak szól, akik számára eddig megközelíthetetlen volt a LOAD/SAVE rutinokba történő beavatkozás. Megismerhetjük a "trükkös" betöltők titkait, és tippeket kapunk a turbósításra is.
Tartalom
Műveletek a magnetofont kezelő LOAD / SAVE rutinokkal
A fejezet első részében a ROM-ban található LOAD/SAVE rutinok elhelyezkedését, működését, hívási lehetőségeit ismertetjük. A későbbi felhasználás céljából elsősorban a végrehajtó rutinra lesz szükségünk, így annak közöljük a teljes assembly listáját, az utasítás értelmező és vezérlő rutinok részletes ismertetésére nem térünk ki. Tesszük ezt azért, mert bármikor disassemblálható a ROM területről, másrészt a kommentált lista megtalálható a "ZX Spectrum ROM Programja" c. könyvben - Ipari Informatikai Központ 1985.
A fejezet második részében néhány programozástechnikai tanácsot adunk arra, hogyan lehet a LOAD/SAVE rutinokba "belepiszkálni", elérve a játékprogramokból is jól ismert "trükkös" keret-csíkozást, visszafelé töltést, stb.
A harmadik rész a turbósításhoz ad segítséget, lehetőséget nyújt bárki számára, hogy egyszerű módon turbo programot készítsen, és azt alkalmazni is tudja.
A fejezet végén néhány tanáccsal szolgálunk a LOAD/SAVE rutinok felhasználását illetően.
A ROM LOAD/SAVE rutin felépítése
A ROM-ban elhelyezett rutin az 1218-as (HEX 04C2) címtől tart egészen a 2547-es (HEX 09F3) címig, és három eltérő blokkra bontható:
Most tekintsük át az egyes blokkokat külön-külön.
1. blokk:
A végrehajtó szakasz önmagában is futtatható, de elsősorban csak adott címen és adott hosszon elhelyezkedő adatblokk fejléc nélküli kimentésére/betöltésére. A gyakorlatban ennek a rutinnak a hívását igen sokszor használják főként a játékprogramokban. Mielőtt a konkrét hívási lehetőségeket áttekintenénk, nézzük meg az első blokk részletes assembly listáját (erre még szükségünk lesz a későbbiekben is).
A SAVE-BYTES (adatok magnetofonra mentése) szubrutin: | |||
1218 1221 1222 1225 1227 1229 1232 1233 1234 1236 1237 1239 | 33, 63, 5 229 33,128,31 203,127 40, 3 33, 152, 12 8 19 221, 43 243 62, 2 71 | LD HL,1343 PUSH HL LD HL,8064 BIT 7,A JR Z,1232 LD HL,3224 EX AF,AF' INC DE DEC IX DI LD A,2 LD B,A | ; Kimentjük a verembe a SAVE/LOAD-RETURN ; kezdőcímét. ; A megadott konstans alapján kb. 5 sec. bevezető a fejléchez ; Fejléc tárolásnál ugrás történik. ; A konstans alapján kb. 2 sec. bevezető szakasz ; program, ill. adatblokk esetén. ; Tároljuk a jelzőbiteket. ; Növeljük a hosszt és csökkentjük a báziscímet. ; A tárolás helyére tiltjuk a maszkolható ; megszakítást. ; Piros BORDER szín. ; Ezt áttöltjük B-be. |
Most következik a bevezető szinkron-jel impulzusainak előállítása: | |||
1240 1242 1244 1246 1248 1249 1251 1252 1253 | 16, 254 211, 254 238, 15 6, 164 45 32, 245 5 37 242, 216, 4 | DJNZ 1240 OUT (254),A XOR 15 LD B,164 DEC L JR NZ,1240 DEC B DEC H JP P,1240 | ; Időzítő ciklus. ; A BORDER szín végrehajtásonként piros és ; világoskék között vált. ; Időzítő konstans ; Csökkentjük a számláló alsó byte-ját. ; Ugrás vissza a következő impulzushoz. ; A hosszabb ciklus esetén csökkentjük ; a számláló felső byte-ját. ; Ha a bevezető rész még nem ért véget, ugrás vissza. |
Ezután történik a szinkronizáló impulzus kiadása: | |||
1256 1258 1260 1262 1264 1266 1268 | 6, 47 16, 254 211, 254 62, 13 6, 55 16, 254 211, 254 | LD B,47 DJNZ 1258 OUT (254),A LD A,13 LD B,55 DJNZ 1266 OUT (254),A | ; A MIC kimenet kikapcsolt 667 T ideig. ; ; A MIC kimeneten aktív jel, a BORDER piros. ; Az inaktív kimeneti jel, a BORDER világoskék jelzése. ; A MIC kimenet aktív 735 T ideig. ; ;A MIC kimeneten inaktív jel, a BORDER szín világoskék |
A következőkben tárolódik az azonosító állapotbíte: | |||
1270 1273 1274 1275 | 1, 14, 59 8 111 195, 7, 5 | LD BC,15118 EX AF,AF' LD L,A JP 1287 | ; A 14 alapján a MIC kimeneten aktív a jel és ; sárga a BORDER szín, míg az 59 egy időzítési konstans. ; Visszahívjuk a jelzőbiteket, és A-t áttöltjük ; L-be. ; Előre ugrunk a mentési ciklusra. |
A szinkronizáló impulzust kimentettük, következhet a byte-ok magnetofonra mentése: | |||
1278 1279 1280 1282 1285 1286 1288 1290 1291 1294 1295 | 122 179 40, 12 221, 110, 0 124 173 62, 1 55 195, 37, 5 108 24, 244 | LD A,D OR E JR Z,1294 LD L,(IX+00) LD A,H XOR L LD A,1 SCF JP 1317 LD L,H JR 1285 | ; Ellenőrizzük a hossz-számlálót. ; Abban az esetben, ha eléri a zérust, ; ugrás történik. ; Lehívjuk a soron következő tárolandó adat-byte-ot, ; majd az aktuális paritást is, ; és annak vizsgálata után visszatöltjük. ; A MIC kimeneten aktív jel van, és a BORDER kék. ; Az egyes byte-ok nyolc bites határainak jelzéséhez ; az átviteli jelzőbitet 1-re állítjuk. ; ugrás történik előre. ; Beállítjuk a paritás végső értékét, ; és ugrás vissza. |
Belső ciklus az aktuális impulzusok előállítására: | |||
1297 1298 1300 1302 1304 1306 1308 1310 1312 1314 1315 1316 | 121 20, 120 16, 254 48, 4 6, 66 16, 254 211, 254 6, 62 32,239 5 175 60 | LD A,C BIT 7,B DJNZ 1300 JR NC,1308 LD B,66 DJNZ 1306 OUT (254),A LD B,62 JR NZ,1269 DEC B XOR A INC A | ; A MIC kimeneten inaktív jel, a BORDER szín sárga ; A zérus jelzőbit 1 állapota jelzi a ciklus második ; végrehajtását. ; Időzítő ciklus (a második végrehajtásnál 801 T idő) ; Ugrás a rövidebb ciklusra, ha zérust kell magnóra menteni. ; Ha 1-et kell tárolni, hozzáadunk 855 T időt ; ; A végrehajtás első részében a MIC kimeneten aktív a jel ; és a BORDER szín kék, a második részben a MIC kimeneten ; inaktív jel, és a BORDER szín sárga. ; Beállítjuk az időzítő konstans második végrehajtásához. ; Az első végrehajtás után ugrás vissza. ; Máskülönben csökkentjük a konstanst T idővel. ; Az átviteli jelzőbitet töröljük, A-t 1-re ; állítjuk (aktív MIC kimenet, és kék BORDER szín). |
Itt lépünk be a "8 bites ciklus" szubrutinba: | |||
1317 | 203, 21 194, 20, 5 27 221, 35 6, 49 62, 127 219, 254 31 208 122 60 6, 59 16, 254 201 | RL L | ; A "Byte vége" jelző balra lép 8 bit után tehát az ; átviteli jelzőbitbe. ; Ha még nincs kész a byte, akkor tároljuk az aktuális bitet. ; A ciklusszámlálót (melynek alapértéke a kimentendő byte-ok ; hossza) csökkentjük, a báziscímet pedig növeljük. ; ; Az időzítő konstanssal beállunk a következő byte első bitjéhez. ; BREAK-vizsgálat. Ha a BREAK-et megnyomtuk kimentés közben ; akkor átlépünk a SAVE/LOAD RETURN-be, ; és a kimentésnek vége. ; ; Máskülönben megvizsgáljuk a ciklusszámlálót és még akkor is ; visszaugrunk, ha elértük a zérust, mivel a paritást is ki kell ; küldeni a szalagra az adatok végén. ; Visszatérés előtt rövid szünet. ; ; szubrutin vége |
A LOAD/SAVE szubrutin: | |||
1343 1344 1347 1349 1350 1351 1352 1354 1356 1358 1359 1360 1362 1363 1364 1365 | 245 58, 72, 92 230, 56 15 15 15 211, 254 62, 127 219, 254 31 251 56, 2 207 12 241 201 | PUSH AF LD A,(23624) AND 56 RRCA RRCA RRCA OUT (254),A LD A,127 IN A,(254) RRA EI JR C,1364 REST 08 DEFB 12 POP AF RET | ; Tároljuk az átviteli jelzőbitet, ; a BORDER rendszerváltozóból lehívjuk az eredeti ; BORDER színt, majd azt a 2-es, 1-es és a 0-és bitekbe ; töltjük ; ; ; A BORDER-t beállítjuk az eredeti színértékre. ; A BREAK leolvasása. ; ; ; Engedélyezzük a megszakítást. ; Ha nincs megszakítás, ugrás történik. ; Meghívjuk a hibakezelő rutint, és megjelenik ; a D BREAK-CONT repeats hibaüzenet. ; Visszaírjuk az átviteli jelzőbitet és ; visszatérünk a programból. |
A LOAD-BYTES (adatok betöltése magnetofonról) szubrutin (HEX kezdőcim-0556) | |||
1366 1367 1368 1369 1370 1372 1374 1377 1378 1380 1381 1383 1385 1386 | 20 8 21 243 62, 15 211, 254 33, 63, 5 229 219, 254 31 230, 32 246, 2 79 191 | iNC D EX AF,AF' DEC D DI LD A,15 OUT (254),A LD HL,1343 PUSH HL IN A,(254) RRA AND 32 OR 2 LD C,A CP A | ; Töröljük A zérus jelzőbitet ; Fejléc esetén 0, adatblokk esetén 255 az "A" regiszter ; tartalma, míg az átviteli jelzőbit 0 esetén VERIFY-t ; 1 esetén LOAD-ot jelent. ; D eredeti értéke ; Tiltjuk a maszkolható megszakítást. ; Fehér BORDER szín. ; A veremben tároljuk a SAVE/LOAD RETURN kezdőcímét. ; Előzetesen leolvastuk a 254-es port-ot. ; Ciklikus léptetés. Nem tartjuk meg, csak az ; EAR bitet (6. bit) ; A BORDER szín piros ; C-ben tároljuk. (02-"be", 34-"ki" állapot) ; A zérus jelzőbitet 1-be állítjuk. |
A jelváltás vizsgálata: | |||
1387 1388 1391 1393 1396 1398 1399 1400 1401 1403 1406 | 192 205, 231, 5 48, 250 33, 21, 4 16, 254 43 124 181 32, 249 205, 227, 5 48, 235 | RET NZ CALL 1511 JR NC,1387 LD HL,1045 DJNZ 1396 DEC HL LD A,H OR L JR NZ,1396 CALL 1507 JR NC,1387 | ; BREAK esetén visszatérés ; Meghívjuk az 1511-es címen lévő szubrutint a következő élig ; terjedő időzítéshez ; Visszatéréskor ha 14000 T ideig nem volt jelváltás, az átviteli ; jelzőbit zérus, máskülönben világoskékre vált a BORDER. ; Beállítjuk a várakozás hosszát, ez kb 1 sec ; Ezt a láncot addig végzi, amíg a megadott ; időben két jelváltást nem talál. ; ; ; Ha talál, ugrás történik. ; BREAK esetén megáll |
Bevezető jel fogadása: | |||
1408 1410 1413 1415 1417 1418 1420 1421 | 6, 156 205, 227, 5 48, 228 62, 198 184 48, 224 36 32, 241 | LD B,156 CALL 1507 JR NC,1387 LD A,198 CP B JR NC,1388 INC HJR NZ,1408 | ; Időzítési állandó. ; Itt csak akkor folytatja ha a megadott időben ; két jelváltást talál. ; BREAK esetén megáll. ; A jelváltások elhelyezkedése kb 3000 T időn ; belül kell hogy legyen ; ; Máskülönben ugrás vissza. ; A "H" regiszter tárolja a jelváltás-párokat ; egészen 255 párig. |
A szinkronizáló impulzus "ki"/"be" részei | |||
1423 1425 1428 1430 1431 1433 1435 1438 | 6, 201 | LD B, 201 CALL 1511 JR NC,1387 LD A,B CP 212 JR NC,1423 CALL 1511 RET NC | ; Időzítési állandó. ; A jelváltásokat addig vizsgálja amíg ; két egymáshoz közeli élt nem talál. ; Ezek az impulzis "ki" ; kezdő és befejező élei. ; Meg kell jelennie a "be" impulzus ; befejező élének is. |
Fejléc ill. program byte-ok adatainak betöltése: | |||
1439 1440 1442 1443 1445 1447 | 121 238, 3 79 38, 0 6, 176 24, 31 | LD A,C XOR 3 LD C,A LD H,0 LD B,176 JR 1480 | ; A BORDER szín kék/sárga. ; ; ; Zérusra állítjuk a paritás-ellenőrző byte-ot ; A jelzőbit időzítő konstansa. ; Ugrás előre |
Byte-betöltő ciklus: | |||
1449 1450 1452 1454 1457 1459 1461 1462 1463 1464 1465 1466 1467 | 8 32, 7 48, 15 221, 117, 0 24, 15 203, 17 173 192 121 31 79 19 24, 7 | EX AF,AF' JR NZ,1459 JR NZ,1469 LD (IX+00),L JR 1474 RL C XOR L RET NZ LD A,C RRA LD C,A INC DE JR 1476 | ; Lehívjuk a jelzőbiteket. |
VERIFY összehasonlító művelet: | |||
1469 1472 1473 | 221, 126, 0 173 192 | LD A,(IX+00) XOR L RET NZ | ; Az eredeti byte behívása. ; Összehasonlítjuk a behívott byte-tal. ; Visszatérés, ha a kettő nem egyezik. |
Újabb byte behívása szalagról: | |||
1474 1476 1477 1478 1480 | 221, 35 27 8 6, 178 46, 1 | INC IX DEC DE EX AF,AF' LD B,178 LD L,1 | ; Növeljük a célhely címét és ; csökkentjük a ciklusszámlálót. ; A jelzőbiteket tároljuk. ; Időzítési állandó. ; A jelzőbit kivételével töröljük az aktuális regisztert. |
Egy byte felépítése - ciklus: | |||
1482 1485 1486 1488 1489 1491 1493 | 205, 227, 5 208 62, 203 184 203, 21 6, 176 210, 202, 5 | CALL 1507 RET NC LD A,203 CP B RL L LD B,176 JP NC,1482 | ; Megállapítjuk a következő bit "ki" és "be" impulzusának hosszát. ; Visszatérés ha az időtatam nagyobb. ; Összehasonlítjuk 2400 T ciklussal. ; Zérus eseténtörli, 1-esetén 1-be állítja az ; átviteli jelzőbitet, majd új bitet küld az "L" regiszterbe. ; Időzítési állandó beállítása a következő bitre ; Ha van még hátra a 8 bitből akkor ugrás vissza. |
Paritás ellenőrző byte frissítése: | |||
1496 1497 1498 | 124 173 103 | LD A,H XOR L LD H,A | ; A pritás-ellenőrző byte-ot lehívjuk, ; ráillesztjük az új byte-ot, ; és visszatöltjük. |
Ciklus lezárása: | |||
1499 1500 1501 1503 1504 1506 | 122 179 32, 202 124 254 201 | LD A,D OR E JR NZ,1449 LD A,H CP 1 RET | ; Ha DE még nem érte el a zérust, ; úgy a ciklus újra végrehajtódik. ; A paritás ellenőrző byte-ot lehívjuk. ; Visszatéréskor az átviteli jelzőbit annak ; megfelelően alakul. ; Ha a paritás ellenőrző byte zérus, az átviteli jelzőbit ; "1" lesz, és fordítva. |
A LOAD rutin törzse: (Belépéskor az időzítési állandó a "B" regiszterben, BORDER szín ill. "impulzus"-él típusa "C" regiszterben található.) | |||
1507 1510 1511 1513 1514 1516 | 205, 231, 5 208 62, 22 61 32, 253 167 | CALL 1511 RET NC LD A,22 DEC A JR NZ,1513 AND A | ; Az 1511-es címen kezdődő szubrutin kétszer lesz fittatva. ; Hiba esetén visszatérés. ; Várakozás 358 T ideig, ; a mintavételezési ciklus ; indítása előtt. |
Mintavételezési ciklus: | |||
1517 1518 1519 1521 1523 1524 1525 1526 1528 | 4 200 62, 127 219, 254 31 208 169 230, 32 40, 243 | INC B RET Z LD A,127 IN A,(254) RRA RET NC XOR C AND 32 JR Z,1517 | ; Számoljuka ciklusokat. |
Módosítás egy új él találása esetén: | |||
1530 1531 1532 1533 1535 1537 1539 1540 | 121 | LD A,C CPL LD C,A AND 7 OR 8 OUT (254),A SCF RET | ; Az "utolsó él" típusát megváltoztatja, |
Mint ahogy az a rutinból is szemléletesen látható, az adatok magnetofonra mentéséhez az 1218-as (HEX 04C2) címen kell belépnünk, míg az adatok magnetofonról történő betöltésére az 1366 (HEX 0556) címen van lehetőség.
Az első blokk több különálló szubrutinból tevődik össze, néhány szubrutint (pl. SAVE/LOAD-RETURN) a SAVE és a LOAD művelet is használ. Az adatok elmentéséhez ill. betöltéséhez nem elegendő meghívni a megadott címet, előtte néhány regiszter tartalmát megfelelően be kell állítani.
Ezek sorban:
Ezen kívül betöltés esetén az átviteli jelzőbitet 1-re kell átváltani egy megfelelő SCF utasítással.
Nézzünk egy mintapéldát:
Fejléc nélküli adatblokk formájában akarjuk kimenteni a képernyő-memória tartalmát. A mentő rutint (amely majd a ROM rutint is hívja) helyezzük el a 60000. címtől. A szinkron-byte értéke 255, a startcím (IX tartalma) 16384, a hossz (DE tartalma) pedig 6912. A mentő rutin tehát a következő formájú lesz:
60000 60002 60006 60009 60012 | 62, 255 | LD A,255 LD (IX+00),16384 LD DE,6912 CALL 1218 RET | ; szinkron-byte |
Néhány byte-ról lévén szó, az adatok beolvasása BASIC utasítások segítségével egyszerűen elvégezhető:
10 FOR i=60000 T0 60012
20 READ a: POKE i,a: NEXT i
30 DATA 62,255,221,33,0,64,17,0,27,205,194,4,201
Adjuk ki RUN (ENTER), és a mentő rutin a memóriában van. Készítsünk egy tetszőleges színes grafikát a képernyőre, majd adjuk ki 'RANDOMIZE USR 60000', de még ne nyomjuk meg az ENTER-t! Előbb indítsuk el a magnetofont felvétel üzemmódban, és csak ezután ENTER, mert a kitöltés azonnal indul. Itt egy apró probléma felvetődhet, a kép kimentésekor az alsó két sor lemarad az utasítás kiadása miatt. Ez elkerülhető, ha teljes képernyős (24 soros) grafikánkat előbb eltesszük a memória hátrébb eső területére pl. az LDIR utasítás segítségével, és onnan mentjük ki. Az itt említett műveletre szemléletes példát találunk a "RUTINRÓL-RUTINRA" c. könyvben - LSI ATSz.1986. A képünk most már a magnetofonon található, fejléc nélkül. Behívásához meg kell írnunk a betöltő rutint, az egyszerűség kedvéért ez a mentő rutin után közvetlenül helyezkedjen el.
Formája a következő lesz:
60013 60015 60016 60020 60023 60026 | 62. 255 | LD A,255 SCF LD (IX+00),16384 LD DE,6912 CALL 1366 RET | ; szinkron-byte |
Az előbbihez hasonló módon olvassuk be ezt a rutint is a memóriába, majd csévéljük vissza a kazettát és adjuk ki: RANDOMIZE USR 60013 (ENTER). Indítsuk el a magnetofont, s előzőleg kimentett képünk megjelenik a képernyőn.
2.blokk:
Ez a szintaktikai vizsgáló és vezérlő szakasz. Mind a négy szalag-kezelő utasítás esetén (LOAD/SAVE/VERIFY ill. MERGE) a belépés helye az 1541-es (HEX 0605) cím. A rutinba való belépés előtt az utasításnak megfelelően beállítódik a 23668-as (T-ADDR-lo) rendszerváltozó. Értéke:
Ez a belépés után automatikusan kiolvasásra kerül, és meghatározza az utasítás jellegét. A 2. blokk a megfelelő ellenőrzések és beállítások után szubrutinként adja át a vezérlést a már előbb megismert 1. blokknak, és az adott művelet elvégződik.
A 2. blokk részletes assembly listáját nem közöljük, csak a fontosabb belépési címeket ismertetjük, megnevezés-decimális cím-hexadecimális cím formában.
Megnevezés | DEC cím | HEX cím |
Fejléc információ előállítása a munkaterületen | 1541 | 0605 |
Programnév vizsgálata | 1604 | 0644 |
Név áttöltése a munkaterületre | 1611 | 0648 |
Utasítások után álló paraméterek vizsgálata
|
1618 |
0652 |
A VERIFY vezérlő rutinja
|
1995 |
07CB |
Egy adatblokk betöltése | 2050 | 0802 |
A LOAD vezérlő rutinja
|
2056 |
0808 |
A MERGE vezérlő rutinja
|
2230 |
08B6 |
A SAVE vezérlő rutinja
|
2416 |
0970 |
3. blokk:
Ebben a blokkban tárolták el a magnetofon kezelésével kapcsolatos üzeneteket. (CR=kocsi vissza-soremelés) Az egyes üzenetek elhelyezkedését az előző felosztáshoz hasonlóan közöljük:
"Start tape then press any key" üzenet | 2466 | 09A2 |
CR+"Program:" | 2497 | 09C1 |
CR+"Number array:" | 2507 | 09CB |
CR+"Character array:" | 2522 | 09DA |
CR+"Bytes:" | 2540 | 09EC |
Az üzenettáblázat elején, és az egyes üzenetek után un. delimiter (elhatárolójel) lett elhelyezve.
Manipulációk a LOAD/SAVE rutinnal
Először is ahhoz, hogy a LOAD/SAVE rutinban bármit is megváltoztatnánk, azt ki kell menteni RAM területre. Célszerű a ROM rutint a memória végére átmenteni. Egyenlőre csak az 1.blokkra lesz szükségünk, de mivel a későbbiekben a turbo-manipulációkhoz kell majd a 2. és a 3. blokk is, így célszerű az új területen elhelyezkedő rutin kezdőcímét ezek figyelembevételével megválasztani. Válasszuk kezdőcímnek a 63962-t. Az 1. blokk áthelyezési rutinját pedig olvassuk be az 50000. címtől:
50000 50003 50006 50009 50011 | 33, 194, 4 | LD HL,1218 LD DE,63962 LD BC, 323 LDIR RET | ; az átmozgatandó blokk kezdőcíme |
A beolvasáshoz egyszerűen készítsünk egy BASIC programot:
10 FOR i=50000 TO 50011
20 READ a: POKE i,a: NEXT i
30 DATA 33,194,4,17,218,249,1,67,1,237,176,201
Adjuk ki: RUN (ENTER), majd RANDOMIZE USR 50000 (ENTER) és az 1. blokk átíródik a 63962. címtől.
Az 1. blokk az új helyen egyenlőre még futásképtelen, az abszolút címhivatkozásokat át kell írnunk. Gépeljük be és futtassuk az 50. sortól a következőt:
50 POKE 63963,87: POKE 63964,250:
POKE 63998,240: POKE 63999,249:
POKE 64020,31: POKE 64021,250
60 POKE 64036,61: POKE 64037,250:
POKE 64064,44: POKE 64065,250:
POKE 64080,22: POKE 64081,250
70 POKE 64133,255: POKE 64134,250:
POKE 64148,251: POKE 64149,250:
POKE 64155,251: POKE 64156,250
80 POKE 64170,255: POKE 64171,250:
POKE 64180,255: POKE 64181,250:
POKE 64227,251: POKE 64228,250
90 POKE 64238,226: POKE 64239,250:
POKE 64252,255: POKE 64253,250
Adjuk ki: RUN 50 (ENTER)
Az 1. blokk az új helyen most már futtatható. Felvetődhet a kérdés, mi az oka annak, hogy teljesen logikátlanul hol relatív, hol abszolút ugrások lettek elhelyezve a rutinban, amikor mindegyik abszolút hivatkozás kiváltható lenne relatív címzéssel. Erre mi sem tudjuk a pontos választ, de az biztos hogy ezzel megnehezítik azoknak a dolgát, akik a ROM-ból bármit is ki akarnak másolni.
Egy fontos dolog! Ha még idáig nem történt meg, akkor még most feltétlenül adjuk ki: CLEAR 63000 (ENTER), mert a későbbiekben problémába ütközünk.
Ha megvolt a CLEAR a RAMTOP áthelyezéséhez, nyugodt szívvel adjunk egy NEW-t, az eddigi BASIC törléséhez. Első lépésként nézzük meg, hogyan lehetséges a jól ismert piros/világoskék ill. a kék/sárga BORDER csíkok színeinek megváltoztatása.
Ahhoz, hogy a csík-effektus változatlanul maradjon, csak a színek változzanak, a következőket tehetjük.
Feltétlenül helyezzünk el megint a memóriában egy segéd-mentő/töltő rutint, amely beállítja a kezdőcímet és a hosszt a már megismert módon. Az egyszerűség kedvéért helyezzük el ismét az előző fejezetpontban ismertetett rutinokat a 60000-60026. címek között a képernyőmemória mentésére ill. töltésére.
Természetesen most már két sort ki kell cserélnünk:
60009 60023 | 205, 218, 249 | CALL 63962 CALL 64110 | ; az adatok magnetofonra mentése a |
Ha ezek a rutinok is a memóriában vannak, próbáljuk ki őket:
RANDOMIZE USR 60000
problémamentesen a magnetofonra küldi az adatokat, ill.
RANDOMIZE USR 60013
segítségével ezek visszahívhatók.
Azt, hogy a keret-csíkozás ne a hagyományos színkombinációban, vagy érdekes hatásokkal jelenjen meg, csak a töltő rutinban érdemes kiváltani, hiszen általában egy kész program betöltését színesítik. Annak sok értelme nincs, hogy kész programunk saját magunk szórakoztatására úgy kerüljön kazettára, hogy közben a képernyő-keret a szivárvány színeiben tündököl.
A töltő rutinban (1366-1540 ; HEX 0556-0604) több helyen is eszközölhetünk változtatást.
Az eredeti rutinban az 1384-es címen, az áthelyezett kódban így a 64128-as címen található az a konstans, amelyik a bevezető TONE alaptónusát meghatározza (alapértéke = 2). Próbáljuk megváltoztatni ezt az értéket, és ismételjük meg a betöltést. Láthatjuk, hogy a csíkok színe megváltozik. Pl. 1 esetén a TONE kék/sárga lesz, 0 esetén pedig fekete/fehér.
A ROM rutin 1441-es, így az áthelyezett kód 64185-ös címén az alapérték 3. Próbáljuk meg ezt is megváltoztatni. A két cím együttes változtatása újabb kombinációkat eredményez.
Felvetődik a kérdés, de vajon hogyan lehet megvalósítani azt, amikor betöltés közben a keret egy színű, vagy szivárványosan tölt ?
Tekintsünk vissza ismét a ROM rutin assembly listájára. A BORDER szín végleges beállítása és megjelenítése az 1530-1538 címek között
történik. Az 1532. és 1533. címek közé kell beszúrnunk egy vagy több olyan utasítást, amely a BORDER-t megfelelően módosítja. Ehhez először is az 1. blokk végét az 1533. címtől arrébb kell tolnunk.
Ezt a vágást ennek megfelelően a RAM rutinban a 64276/64277 között kell elvégezni, és az attól jobbra levő byte-okat két byte-tal jobbra pakoljuk.
Ehhez gépeljük be a következőt:
100 POKE 64277,0: POKE 64278,0:
POKE 64279,230: POKE 64280,7:
POKE 64281,246: POKE 64282,8:
POKE 64283,211: POKE 64284,254:
POKE 64285,55: POKE 64286,201
Ezután RUN 100 (ENTER).
Próbáljunk ismét betölteni, egyenlőre semmi változást nem tapasztalunk. A 64277-64278-as címekre tetszés szerint beírhatjuk a megfelelő kódokat.
Csak néhány variációt közlünk, a további próbálgatást az olvasóra bízzuk.
POKE 64277,62: POKE 64278,szín (LD A,szín) | - ahol szín=0-7 és az adott színt megtartja "csíkozás" nélkül betöltés közben a képernyő-keretre. |
POKE 64277,120: POKE 64278,0 (LD A,B) | - szép, szivárványos betöltés |
POKE 64277,122: POKE 64278,0 (LD A,D) | - nincs "csíkozás", a színek a teljes keretre 256 byte-onként váltják egymást. |
POKE 64277,123: POKE 64278,0 (LD A,E) | - szivárványszerű betöltés, változó, igen érdekes "csík"-hatásokkal. |
POKE 64277,124: POKE 64278,0 (LD A,H) | - különleges hatás |
POKE 64277,237: POKE 64278,95 (LD A,R) | - szivárványszerű (ezt alkalmazták pl. a COMMANDO c. játékban). |
POKE 64277,170: POKE 64278,0 (XOR D) | - a csíkozás színeinek 256 byte-onként periódikus változása. |
POKE 64277,166: POKE 64278,0 (AND (HL)) | - fekete tónusú, vibráló keret. |
Ezek után bizonyára többekben felvetődik a kérdés. Van-e lehetőség kikapcsolni software úton betöltéskor a belső hangszórót. A belső huzalozás révén erre software oldalról nincs lehetőség, de nem is lenne sok értelme lekapcsolni.
Bizonyára több játékban találkozott már a felhasználó olyan hatással, hogy a képernyő nem hagyományos módon töltődött be, hanem pl. alulról felfelé stb.
A legegyszerűbb variáció az, amikor un. visszafele töltést valósítunk meg, vagyis a memóriában nem a növekvő, hanem a csökkenő címek felé töltjük a programot. Ennek az egyik alapfeltétele, hogy azt már eleve ilyen formában kell kazettára is mentenünk.
Az eredeti ROM rutinban az 1323/24 címeken található INC IX utasítás a kimentéshez, ill. betöltéshez az 1474/75 címeken. Ezt mindkét helyen egy DEC IX utasítással kell helyettesítenünk, vagyis az áthelyezett blokkban a 64068. és a 64219-es címek tartalmát kell 35-ről 43-ra megváltoztatnunk.
Gépeljük be:
POKE 64068,43: POKE 64219,43
Nézzünk a visszafelé töltésre is mintapéldát, látványosan természetesen most is használjuk a képernyőmemóriát. Használjuk fel ismét a 60000. címtől elhelyezett mentő, és 60013. címtől elhelyezett töltő rutint. Most IX-be tehát nem a kezdő, hanem a végcímet kell megadnunk, a többi paraméter nem változik. A változás tehát:
60002 60016 | 221, 33, 255, 90 | LD (IX+00),23295 LD (IX+00),23295 | ; IX-ben a végcím |
Készítsünk ismét egy tetszőleges grafikát, és mentsük ki:
RANDOMIZE USR 60000
Csévéljük vissza a kazettát, és a RANDOMIZE USR 60013 (ENTER) utasítással töltsük vissza a képet. Láthatjuk, a töltés visszafelé indul meg, ezt még szivárványos kerettel is kombinálhatjuk, úgy mint a HIGHWAY ENCOUNTER c. játékban.
Tegyünk egy próbát is, töltsük be előző képünket, láthatjuk meglehetős zagyvaság jelenik meg a képernyőn. A visszafelé töltést előnyessen alkalmazzák nagyobb kódok töltésére, hogy a "kíváncsiak" a kód önmagában történő betöltésekor csak értelmetlen adathalmazt találjanak. Mielőtt a turbo-ra rátérünk, még egy kitérőt teszünk a betöltök egy speciális változatára.
Több programban (pl. ROMMEL'S REVENGE, MIKIE stb.) találkozunk a képernyő betöltés teljesen szabálytalan megoldásával. A CRYSTAL Software által ismert variáció, amikor a képernyőt képpontsoronként lefelé haladva tölti be a betöltő rutin. Ma a leggyakoribb forma, amikor a karakternek egymás után betöltődik az egymás alatti 8 byte-ja, majd ráhívódik az ATTR byte is, és következik az újabb karakter. Igen látványos, de időigényes manipuláció. Mindenekelőtt szét kell bontani a betöltő rutint ott, ahol az IX (aktuális báziscím) növelése (INC IX) történik. Ez a hely a ROM-ban az 1474-es cím, az áthelyezett kódban tehát 64218. Ezen a ponton meg kell oldani a mindenkori aktuális báziscím előállítását. A báziscímeket célszerű egy adattömb formájában külön eltenni a memóriában, olyan sorrendben, ahogyan majd a kiolvasás történik. Kiolvasáskor a mutató tovább lép a következő báziscímre. Az adattömb elkészítésére célszerű egy egyszerű BASIC programot írnunk. Természetesen a mentő rutint is ennek megfelelően kell módosítanunk, hiszen az adatokat is ilyen sorrendben kell a magnetofonra kiküldeni.
Több fokozatú TURBO készítése
A TURBO program közismert más géptípusra is. A jel-átviteli sebesség (BAUD RATE) megnövelését segíti elő. Ez előnyös a gazdaságos tárolás ill. töltési/mentési idők csökkentése szempontjából, de hátrányos is, mert a kazettás magnetofonnal bizonytalanabbá válik a hibátlan adatrögzítés.
A Spectrum alaphelyzetben 1500 baud (bd) jel-átviteli sebességgel dolgozik. A baud egy mértékegység, a másodpercenként átvitt bitek számát fejezi ki (bit/sec).
Egy turbo csak akkor hatékony, ha lehetővé teszi nem csak a gépi kód, hanem BASIC program ill. adat-és stringtömb gyorsítását is. Ehhez először is ki kell menteni a RAM-ba a 2. és 3. blokkot is. Eddigi rutinunk a RAM-ban a 64286-os címig tart, vagyis a 2. és 3. blokk áthelyezésének startcíme: 64287.
Az áthelyező mechanizmus a következő:
51000 51003 51006 51009 51011 | 33, 5, 6 | LD HL,1541 LD DE,64287 LD BC,1007 LDIR RET | ; a 2-3. blokk kezdőcíme |
Olvassuk be a kódot, majd adjuk ki: RANDOMIZE USR 51000 (ENTER) és a 2. ill. 3. blokk is a RAM memóriába kerül.
Egyenlőre a 2. és 3. blokk is futásképtelen az új helyen, az abszolút címhivatkozásokat megfelelően át kell írni. Gépeljük be és futtassuk a következő sorokat:
200 POKE 64440,116: POKE 64441,252:
POKE 64633,138: POKE 64634,254:
POKE 64649,110: POKE 64650,250
210 POKE 64734,34: POKE 64735,253:
POKE 64739,208: POKE 64740,253:
POKE 64797,110: POKE 64798,250
220 POKE 64907,28: POKE 64908,253:
POKE 64974,28: POKE 64975,253:
POKE 64997,28: POKE 64998,253
230 POKE 65030,70: POKE 65031,254:
POKE 65090,70: POKE 65091,254:
POKE 65189,218: POKE 65190,249
240 POKE 65209,218: POKE 65210,249
ezután RUN 200 (ENTER).
Az új területen helyet foglaló rutint is a hagyományos Spectrum BASIC utasításokkal fogjuk kezelni, de ezt meg kell előznie egy USR hivatkozásnak, mert máskülönben a ROM-beli rutinokra adódna át a vezérlés.
Az utasítás szerkezete a következő lesz:
LET a=USR xxxx: aaaa "név" stb. (ENTER)
A kettőspont után a hagyományos töltési/mentési parancs szerkezeteket kell alkalmazni (aaaa = LOAD/SAVE/VERIFY/MERGE ). A belépés viszont nem ilyén egyszerű. Egy külön belépési rutint kell írnunk, melynek kezdőcíme az xxxx cím, és innen adjuk át a vezérlést a RAM-beli 2.blokk kezdetének, ami jelenleg a 64287. címen található.
A kiegészítő-belépési rutinnak meg kell oldania a mutatók megfelelő átállítását, és az utasítás típusának megfelelően a T-ADDR rendszerváltozó beállítását. A kiegészítő rutint az 1. blokk elé érdemes elhelyezni. Hossza 57 byte, vagyis xxxx=63905.
A kiegészítő rutin a következő:
63905 63908 63909 63912 63915 63918 63921 63922 63925 63928 93929 93930 63933 63935 63937 63938 63940 63942 63943 63945 63947 63948 63950 63952 63955 63959 | 58, 71, 92 | LD A,(23623) INC A LD (23623),A LD HL,0 LD (23563),HL LD HL,(23645) INC HL LD (23645),HL LD BC,6880 RST 18 INC HL LD (23645),HL CP 248 JR Z,63955 INC BC CP 239 JR Z,63955 INC BC CP 214 JR Z,63955 INC BC CP 213 JR Z,63955 JP 7306 LD(23668),BC JP 64287 | ; a SUBPPC mutató |
Ha a kódot beolvastuk a memóriába, ki is próbálhatjuk a rendszer jelenlegi működését. Pl.:
LET a=USR 63905: SAVE "név"
egy tetszőleges és éppen a memóriában levő BASIC elmentéséhez, vagy pl.:
LET a=USR 63905: SAVE "név" CODE 0,1000
egy tetszőleges memóriaterület kimentéséhez.
A mentés és a töltés ebben a formájában semmiben nem különbözik a ROM-beli állapottól.
Most egy táblázatot közlünk, amely 24 különböző sebességi fokozatra adja meg a megváltoztatandó byte-ok értékeit.
Az egyes oszlopok a baud sebességekre vonatkoznak, a sorok pedig az adott címen beállítandó értéket határozzák meg. Itt zárójelben megjegyeztük a ROM rutinban megfelelő címet is, hogy az ismertetett assembly listában megkönnyítsük az eligazodást.
cím / baud | 1500 | 1600 | 1700 | 1800 | 1900 | 2000 | 2100 | 2200 |
64049 (1305) | 66 | 62 | 58 | 55 | 52 | 49 | 47 | 45 |
64055 (1311) | 62 | 57 | 52 | 48 | 46 | 45 | 42 | 40 |
64070 (1326) | 49 | 43 | 39 | 36 | 33 | 31 | 29 | 27 |
64190 (1446) | 176 | 181 | 186 | 191 | 197 | 202 | 206 | 209 |
64223 (1479) | 178 | 183 | 188 | 193 | 199 | 204 | 208 | 211 |
64231 (1487) | 203 | 207 | 211 | 215 | 217 | 219 | 221 | 223 |
64236 (1492) | 176 | 181 | 186 | 191 | 197 | 202 | 206 | 209 |
64256 (1512) | 22 | 22 | 21 | 21 | 21 | 20 | 19 | 18 |
cím / baud | 2300 | 2400 | 2500 | 2600 | 2700 | 2800 | 2900 | 3000 |
64049 (1305) | 43 | 41 | 39 | 37 | 35 | 33 | 32 | 31 |
64055 (1311) | 38 | 36 | 34 | 32 | 30 | 29 | 28 | 26 |
64070 (1326) | 25 | 23 | 21 | 19 | 17 | 15 | 14 | 13 |
64190 (1446) | 211 | 212 | 214 | 215 | 216 | 217 | 219 | 220 |
64223 (1479) | 213 | 215 | 216 | 217 | 218 | 219 | 221 | 222 |
64231 (1487) | 225 | 227 | 228 | 229 | 230 | 231 | 232 | 233 |
64236 (1492) | 211 | 212 | 214 | 215 | 216 | 217 | 219 | 220 |
64256 (1512) | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 |
cím / baud | 3100 | 3200 | 3300 | 3400 | 3500 | 3600 | 4500 | 7500 |
64049 (1305) | 30 | 29 | 28 | 27 | 27 | 26 | 21 | 16 |
64055 (1311) | 25 | 24 | 23 | 22 | 22 | 21 | 18 | 12 |
46070 (1326) | 12 | 11 | 10 | 10 | 9 | 8 | 5 | 2 |
64190 (1446) | 221 | 223 | 224 | 225 | 225 | 226 | 230 | 240 |
64223 (1479) | 223 | 225 | 226 | 226 | 227 | 228 | 232 | 242 |
64231 (1487) | 234 | 235 | 236 | 236 | 237 | 237 | 240 | 248 |
64236 (1492) | 221 | 223 | 224 | 225 | 225 | 226 | 230 | 240 |
64256 (1512) | 9 | 9 | 8 | 8 | 7 | 7 | 4 | 2 |
Az igaz, hogy a 4500 bd. érték 3-szoros, a 7500 pedig 5-szörös sebességgyorsulást eredményez, de ha kipróbáljuk ezeket az értékeket, azt fogjuk tapasztalni, hogy a magnetofon ill. a kazetta minőségétől függően max. csak 2400-2700 baud sebességre tudunk gyorsítani, és sajnos már ezeknél az értékeknél is igen nagy a hibázás lehetősége. A gyakorlat azt mutatja, hogy kisebb hordozható kazettás magnetofonnal még elfogadhatóan visszatölthető a 2000-2200 bd. gyorsítás, jobb minőségű deck + jó minőségű kazetta együttes eredménye "néha" elviszi a 2700 bd. értéket is.
Próbálkozhatunk szalagos magnetofonnal ill. video készülékkel is több kevesebb sikerrel, de nagy eredményeket ne várjunk! Akik úgy gondolják, hogy eddigi programjaik sebességét 1500 bd.-ról 2200-ra kívánják megnövelni igaz hogy nagy helymegtakarítást érnek el (több kazetta felszabadulhat), de jól fontoljuk meg, hogy megéri-e a fáradtságot, hiszen a legtöbb játék memóriabeli méreténél fogva meg sem engedi a turbo használatát, másrészt a megbízhatóság jelentősen romlik.
Egy utolsó javaslat a töltési idő ill. helyigény csökkentésére. Ha fejléccel mentünk ki valamit, a fejléc és a byte-ok között kb. 1 másodperc szünetet hagy a gép. Ezt az időtagot kivehetjük a RAM-beli rutinból:
POKE 65194,1: POKE 65195,0
Ekkor a késleltetés elmarad, és szinte folyamatosan menti ki a byte-okat a fejléccel együtt a rutin.
Néhány hasznos tanács
Gyakran problémába ütközhetünk, ha fejléces blokkokat használunk, és betöltött SCREEN$ file-unk épségét meg akarjuk őrizni. Természetesen azt mondhatjuk, ez kikerülhető, ha a képernyő felrajzolása után a többi blokkot fejléc nélkül hívjuk be.
Ennél viszont van egyszerűbb módszer is, nézzünk egy mintapéldát:
Programunk három részbál áll:
- BASIC betöltő
- SCREEN$ file
- gépi kódú rutin(ok)
A file-ok mind fejléccel rendelkeznek, képünk viszont a teljes képernyőt, vagyis mind a 24 sort kihasználja. Valamilyen módon meg kell akadályoznunk, hogy a harmadik file fejléc információja "belevágjon" a képernyőbe.
A BASIC betöltő legyen a következő:
10 CLEAR xxxx
20 LOAD""SCREEN$
30 POKE 23739,111
40 LDAD""CODE: POKE 23739,244
50 RANDOMIZE USR nnnn
A 30. sorban elhelyezett utasítás a megoldás kulcsa. A 23739/40 címek a CHANNEL INFORMATION (csatorna információk) területéhez tartoznak. Ez a két cím a mindenkori PRINT-OUT mutató. A PRINT-OUT rutin a 2548-as (HEX 09F4) címen kezdődik, vagyis alaphelyzetben a 23739/40 címek tartalma 244/9. Ha a 23739. cím tartalmát 111-re változtatjuk meg, a mutató a 2415-ös ROM címre mutat, ahol 201-es (RET) kód van, és az üzenet nem jelenik meg a képernyőn. Természetesen a mutatót adott helyen célszerű visszaállítani.
Hasonló módszerrel lehetséges a másoló-programokból is ismert auto-kimentés megoldása is. Több programrészt vagy adatblokkot úgy tudunk kimenteni, hogy az egyes blokkok között a gép nem vár billentyű megnyomásra. Igaz egy pillanatra megjelenik a képernyőn a "Start tape..." felirat, de azonnal törlődik is.
Nézzünk erre is egy mintapéldát. Mentsünk ki egymás után egy BASIC-et, egy SCREEN$ file-t és egy gépi kódú rutint egyfolytában, minden beavatkozás nélkül.
9000 SAVE "program" LINE nnnn
9010 POKE 23736,181
9020 SAVE "SCREEN$"SCREEN$
9030 POKE 23736,181
9040 SAVE "kód"CODE nnnn,mmmm
A 23736/37 címek is a csatorna-információk területének elemei. Alaphelyzetben tartalmuk: 168/16, azaz a két cím tartalma a 4264-es (HEX 10A8) KEY INPUT rutin mutatója. Ez a rutin vár egy billentyű megnyomására. Ha ezt átlépjük, vagyis a 4264. helyett a 4277. címen lépünk be a rutinba, akkor a várakozást kikerültük, és minden automatikusan onnan folytatódik tovább, mint amikor a várakozás után egy billentyűt megnyomunk. Kimentés után az alapérték (168) visszaíródik, vagyis minden file kimentése előtt újra és újra át kell írni a cím tartalmát, ha folyamatos mentést akarunk.
Ebben a fejezetben még nagyon sok mindent le lehetett volna írni a LOAD/SAVE rutinokkal megvalósítható trükkökről, reméljük az itt közölt információk lehetőséget adnak a felhasználó számára a témában való bővebb elmélyedéshez.