Miesięcznik Bajtek 1993 /1 KLAN ZX SPECTRUM

 

Krótka rozprawa z taśmą (cz. 1) Jacek Trojański

 

W listach od czytelników często pada pytanie: jak przerabiać gry, aby „chodziły" z dyskiem?

Oczywiście nie ma na to prostej recepty, jednak szybko można sobie przyswoić zasady obowiązujące przy przerabianiu gier.

CZYM SIĘ BĘDZIEMY ZAJMOWAC?

 

Taśma magnetofonowa nie jest już uważana za poważny nośnik danych. 

Zalet szybkiej i niezawodnej pamięci dyskowej nie trzeba reklamować. zwłaszcza niecierpliwym graczom, którzy nie mogą się doczekać załadowania właśnie zdobytej, rewelacyjnej gry. 

Konieczność korzystania z magnetofonu do przechowywania programów może zniechęcić każdego. 

Definitywnym rozwiązaniem tego problemu jest zakup stacji dysków. Niestety, okazuje się, że autorzy programów (w tym także gier) nie przewidzieli, że akurat kupimy FDD 3000...

 

Celem niniejszego artykułu jest zaznajomienie czytelników z metodami „uzdatniania" gier dla systemu dyskowego FDD 3000 oraz z występującymi przy tym problemami.

 

Rozpoczynając od sprawy tak prostej jak „rozbrajanie" ładowaczy gier postaram się przedstawić najczęściej spotykane ich rodzaje, 

a następnie metody przenoszenia bloków kodu na dyskietkę. 

W kolejnych częściach artykułu omówione zostaną sposoby podmiany procedur (w kodzie gry) taśmowych na dyskowe, 

co daje pełną współpracę gry z dyskiem (ładowanie poziomów, zapamiętywanie bieżącego stanu gry itp)

 

Wszystkich czytelników uprzedzam, że do pełnego zrozumienia opisanych „zabiegów" wymagana jest znajomość języka maszynowego. 

Warto też przejrzeć cykl „TOS bez tajemnic", opublikowany w zeszłym roku.


NARZĘDZIA

 

Przed każdą poważną pracą zaczynamy od gromadzenia niezbędnych narzędzi. W naszym przypadku będą to oczywiście programy.

 

Pierwszy z nich to OPENER

Program ten pomoże nam w oglądaniu taśmowych loaderów. 

Można również posłużyć się jakimś jego odpowiednikiem, który potrafi załadować z taśmy zabezpieczony wszelkimi znanymi sposobami „ładowacz".

Do kopiowania plików z taśmy na dyskietkę warto używać programu ZEBRA COPY, lecz oczywiście można się bez niego obejść,

Rzeczą całkowicie niezbędną jest natomiast zestaw monitor - dis-asembler - asembler, na przykład MONS i GENS. 

Pierwszego z nich będziemy używać do analizowania kodu gry, drugiego do wykonywania wszelkich zmian w tymże kodzie. 

Warto również mieć pod ręką ołówek i dużo, naprawdę dużo papieru..


DO DZIEŁA

 

Na samym początku należy przygotować dyskietkę.

Najlepiej. jeżeli będzie ona zupełnie czysta - wszelkie zbędne pliki tylko się „plączą pod nogami" w trakcie pracy.

Na tej dyskietce będziemy umieszczać kawałki przenoszonej gry (czy też innego programu). 

Bardzo wygodnie jest sobie przygotować druga dyskietkę. na której znajdą się wszystkie wymienione programy narzędziowe.

 

Następnie ładujemy do pamięci program OPENER (lub jego odpowiednik).

Ustawiamy taśmę na początek gry i wgrywamy loader.

Po pieczołowitym spisaniu parametrów nagłówka (najważniejszy jest numer linii startowej) listujemy loader na ekranie. 

 

I w tym momencie mamy dwie możliwości, gdyż występują dwa podstawowe rodzaje programów ładujących:

składające się z instrukcji BASIC-a oraz napisane w kodzie maszynowym,


WARIANT PIERWSZY - PROSTSZY

Najprostszy jest ładowacz BASIC-owy. Może on wyglądać na przykład tak

 


 

 

 

 

 

Pierwsze dwie linie nie maja większego znaczenia. 

Rozkazy z linii 40 ładują i uruchamiają skompresowany obrazek. 

Linia 50 jest odpowiedzialna za załadowanie głównych bloków gry. 

Wreszcie linia 60 to dekompresja (USR 24500) i uruchomienie (USR 61586) gry. 

Zamiast dwóch oddzielnych instrukcji RANDOMIZE wystąpiła komenda DRAW, gdyż ma ona dwa parametry, 

a funkcja USR nie jest zbyt wybredna, jeśli chodzi o poprzedzający ją rozkaz.

 

Zamiast ręcznie poprawiać loader (trzeba dopisać gwiazdki przed LOAD, czasami trzeba też dopisać odpowiednie nazwy plików w cudzysłowach bądź adresy ładowania),

możemy w tym przypadku skorzystać z ZEBRA COPY.  Loader zostanie zmodyfikowany, a kolejne bloki kodu przeniesione prawie automatycznie. 

 

Jednak warto gdzieś zapisać wszystkie dane, zawarte w nagłówkach plików. Ułatwi to „grzebanie" w grze, jeśli ma ona np. ładować z dysku dalsze poziomy gry,


WARIANT DRUGI - DUŻO, DUŻO TRUDNIEJSZY

Nasze zadanie jest trudniejsze. gdy cały (lub prawie cały) loader jest napisany w kodzie maszynowym. 

 

Ten przypadek można rozpoznać po tym. że wśród linii w BASIC-u nie wstępuję instrukcje LOAD (lub jest ich mniej, niż bloków na taśmie). 

Ponadto występuje instrukcja RANDOMIZE USR adres. Adres ten musimy zapisać na boczku, a następnie przechodzimy do BASIC-a

(komenda B OPENERA) i nagrywamy loader na dysku (tak na zapas).

 

Tym razem nie obejdzie się bez programu monitora - dis-asemblera (np. MONS. MAD, Z80). 

Ładujemy go pod adres nie kolidujący z programem w BASIC-u ani ze stosem interpretera

(UWAGA! Nie należy zmieniać położenia tego stosu instrukcją CLEAR. gdyż możemy w ten sposób skasować kawałek programu maszynowego, zawartego w obszarze zmiennych BASIC-a).

 

Następnie oglądamy pamięć od adresu, który wcześniej zapisaliśmy na kartce. Bardzo często jest to liczba 23760. 

Pod tym adresem ujrzymy dłuższy lub krótszy program maszynowy. 

Bardzo często spotykany jest następujący programik. stosowany do ładowania gier „złamanych" za pomocą przerwania NMI:




 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Oczywiście, wszystkie liczby są zapisane szesnastkowo.

 

Po zapisaniu adresów ładowania dla wszystkich (w tym wypadku trzech) bloków, tworzymy zupełnie nowy „ładowacz":




 

 




Następnie kopiujemy ZEBRĄ wszystkie bloki na dysk i uruchamiamy stworzony przez nas loader. 

Jeśli gra poprawnie się załaduje i uda nam się trochę pograć, to znaczy, że odnieśliśmy pełny sukces.

Może się zdarzyć, że po załadowaniu program się zawiesza lub resetuje się komputer. 

Prawdopodobnie oznacza to, że instrukcja CALL 60FE niszczy obszar BASIC-a (o ile nie został popełniony jakiś banalny błąd - radzę sprawdzić wszystko od początku).

Dzieje się tak, gdy w oryginalnym ładowaczu wstępują rozkazy LDIR (tzn. procedura maszynowa sama się re-lokuje w „bezpieczny" obszar pamięci). 

W takim wypadku czeka nas napisanie loadera w kodzie maszynowym.


WARIANT OSTATNI - NAJTRUDNIEJSZY

Wpierw przypomnijmy, jak wygląda procedura ładująca plik z dyskietki.

Zakładamy, że w buforze operacji dyskowych (#2000) znajduje się nazwa pliku zakończona znakiem CHR$ (0)

 

Ostatnie dwa rozkazy można zastąpić JP 0604h.

Najprostsza procedura obsługi błędów może wyglądać np. tak:

 

LD HL, 210Dh ; adres komunikatu o błędzie

CALL 03D8h ; wydruk komunikatu na ekranie

                      ; tu - oczekiwanie na klawisz lub tp.


Teraz możemy przystąpić do pisania loadera. Trzeba wybrać miejsce w pamięci, które nie zostanie zamazane w trakcie dekompresji gry (CALL 60FEh): tam umieścimy nasz program, 

Najprościej jest, wzorując się na oryginalnym loaderze, wykonać „nakładkę" na procedurę wgrywania bloku z taśmy (listing 4 przedstawia takie rozwiązanie dla gry TETRIS). 

 

 

 

 

Jednak nie zawsze jest to możliwe - procedura do obsługi dysku jest przeważnie dłuższa od procedury dla taśmy. 

W takim wypadku należy napisać procedurę, która sama się przekopiuje w wolny obszar pamięci i tam się uruchomi.

 

Przypuśćmy, że takim obszarem jest bufor drukarki (tj. 23296 do 23551).

Listing 5 przedstawia loader re-lokujący się do tego właśnie obszaru.

 

 

 

Zwróć uwagę, że konieczne jest przeliczenie niektórych adresów - np. zamiast:

LD HL, NAZWA

występuje

LD HL, NAZWA-START+23296

 

Wynika to właśnie z przemieszczenia programu pod inny adres.

Dla tej i poprzedniej wersji ładowacza trzeba jeszcze stworzyć część w BASIC-u, odpowiedzialną za uruchomienie procedury 

(i, niejako przy okazji, za ustawienie RAM-TOPu instrukcją CLEAR).

 

Można również wyczyścić ekran, ustawić kolory lub coś napisać na ekranie

Po instrukcji REM w linii 10 musi wystąpić pewna liczba spacji; jest ona równa długości naszej procedury maszynowej (może być też większa, lecz w żadnym wypadku mniejsza). 

Linia 20 ustawia stos interpretera poniżej wgrywanego bloku gry oraz uruchamia procedurę maszynową. 

Po wpisaniu linii w BASIC-u uruchamiamy GENS i wpisujemy część w asemblerze. 

Po kompilacji kod zostanie wpisany w linii dziesiątej za instrukcję REM.

 

 

 

Dlaczego procedura rozpoczyna się od adresu 23760 ? 

Otóż jest to adres początku programu w BASIC-u (23755) powiększony o pięć 

(dwa bajty dla numeru linii. następne dwa - dla długości linii i jeden dla instrukcji REM), czyli adres pierwszej spacji w linii 10.



Ci, którzy dokładnie śledzili cykl „TOS bez tajemnic" na pewno już zauważyli, że zamiast kopiować nazwy plików do bufora „na piechotę", 

prościej jest załadować do DE adres nazwy, do BC - jej długość i „skoczyć" pod adres 066Dh (oczywiście w ROM-ie interfejsu).

 

Procedura, która się tam znajduje, sama dopisze znacznik CHR$(0) na końcu oraz zwróci długość nazwy (ze znacznikiem) w rejestrach Ai B.

Jeżeli dane w nagłówkach wszystkich plików są właściwe (tzn. pliki mają się ładować dokładnie pod adresy zawarte w swoich nagłówkach), 

to loader można znacznie skrócić. 

Wystarczy całą procedurę ładowania napisać w postaci pętli (powinna się ona wykonać tyle razy, ile jest plików do załadowania), 

W takim przypadku musimy jednak wpisać zero jako adres ładowania. 

W ten sposób procedura w interfejsie stacji pobierze potrzebne adresy z nagłówków plików, a nasz programik skróci się o ponad połowę.

HAPPY END


Z drugiej części artykułu dowiemy się, co robić, gdy nie znajdziemy miejsca w pamięci na loader, 

Będą także zaklęcia, uczące grę ładowania kolejnych poziomów (lub innych danych) z dyskietki, oraz zapisywania rożnych danych na tejże dyskietce. 

A teraz możemy podłączyć joystick (o ile jesteśmy w jego posiadaniu) i czerpać podwójną przyjemność z własnoręcznie przerobionej gry.


JACEK TROJAŃSKI