Miesięcznik Bajtek 1993/2 KLAN ZX SPECTRUM



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


PAMIĘĆ POD PAMIĘCIĄ, CZYLI INTERFEJS STACJI


Po przeczytaniu pierwszej części artykułu, każdy średnio zaawansowany czytelnik (to znaczy taki, który na widok wydruku w asemblerze nie ucieka w najdalszy kąt osłaniając głowę jedną ręka, a drugą sięgając do wyłącznika prądu), powinien być w stanie zmusić niemal każda grę do współpracy (z dyskiem, oczywiście).

Napisałem: niemal każdą grę, ponieważ niektóre z nich są wyjątkowo „odporne" na wszelkie próby. Tak się dzieje na przykład, gdy któryś z załadowanych do pamięci bloków rozpakowuje się, niszcząc przy tym obszar programu w BASIC-u (a tam właśnie znajduje się procedura ładująca kolejne bloki). Trzeba wtedy pomyśleć o umieszczeniu ładowacza w innym miejscu pamięci - na przykład „pod sufitem", czyli od adresu 65000 i wyżej.

Jednak nie zawsze to jest możliwe - niektóre gry korzystają z każdego wolnego skrawka pamięci, także z pamięci ekranu. Lecz na pewno nie używają one pamięci RAM interfejsu stacji - bo większość z programów przybywa do nas z krajów, w których stacja Timex FDD jest mało popularna.


Z instrukcji obsługi do stacji dysków możemy się dowiedzieć, że obszar od #2156 do #23FF jest wolny (nie wykorzystywany przez system). Miejsca jest więc aż nadto, ale pojawia się pewien problem: tej pamięci nie da się wykorzystać z poziomu BASIC-a w prosty sposób. Aby kod maszynowy loadera przenieść z linii REM do pamięci interfejsu trzeba napisać krótką procedurkę, przełączającą pamięć i kopiującą zawartość:



(listing 1)


Także w tym przypadku obowiązuje przeliczanie etykiet

(rozkaz LD HL, NAZWA-START+2156h).


Metodę tą można kombinować z loaderami omówionymi w 1 części artykułu; często zdarza się, że tylko jeden (ostatni) plik wymaga ładowania „spod ROM-u" (czyli z pamięci interfejsu).

Loader składa się wtedy z trzech części: ładującej bloki „normalnie", relokującej ciąg dalszy oraz części relokowanej, ładującej blok w opisany właśnie sposób.


RATUJMY NORNIKI


Każdy ogrodnik ukamieniowałby mnie na sam widok tego tytułu (dlaczego? Spytaj ogrodnika). A tymczasem chodzi o specyficznego członka rodziny norników, niewielkiego gryzonia zwanego pod tajemniczą nazwą LEMING.

Chyba wszyscy już wiedzą, na czym gra LEMMINGS polega, gdyż cieszyła się ona ogromną popularnością kilka miesięcy temu.

Na każdym ze 120 (w przypadku Spectrum - z 60) poziomów mamy za zadanie uratować kilka z tych przemiłych stworzonek, zmuszając je do wytężonej pracy fizycznej. Jednak ta gra szybko może się znudzić, gdy kolejne poziomy ładujemy z taśmy...


Na początku „rozbrajamy" program ładujący:


10 CLEAR 24575

20 LOAD '' " CODE

30 RANDOMIZE USR 25000


Może się on trochę różnić od zamieszczonego, gdyż każdy hacker lubi coś zmienić i dopisać się w loaderze. Linia dwudziesta ładuje blok kodu, a linia trzydziesta uruchamia ten kod. Oczywiście, zgodnie ze wskazówkami z pierwszej części artykułu przerabiamy programik:


1O CLEAR 24575

20 LOAD * "LEMMINGS.1" CODE

30 RANDOMIZE USR 25000


W ten sposób załaduje się pierwszy z trzech bloków programu - dwa pozostałe będą ładowane z poziomu kodu maszynowego - oryginalna procedura jest „zaszyta" w pliku LEMMINGS.1 (wszystkie pliki najlepiej skopiować przy użyciu ZEBRA COPY).

Trzeba więc sięgnąć po MONS-a (lub inny monitor) i zlokalizować miejsca ładowania dalszych bloków.

W tym celu kasujemy linię trzydziestą i uruchamiamy loader.


Najprościej jest szukać rozkazu skoku do procedury LOAD (CALL 0556h). lecz nie zawsze jest to metoda skuteczna.

Znacznie prościej jest wyszukać rozkazy ładowania rejestru IX - jest on używany przede wszystkim przy operacjach taśmowych.

W ten sposób odnajdujemy adres #6527 (25895) Warto jednak cofnąć się o kilkanaście rozkazów, by znaleźć właściwy początek procedury (listing 2)








Z analizy listingu wynika, że pierwszy z dwóch bloków ładuje się pod adres C000h (49152)

i ma długość 0DC0h (3520) bajtów, natomiast drugi - odpowiednio 8F0Dh (36621) i 6EF3h (25403) bajtów. Ponieważ mało kto używa ZX Spectrum 128 ze stacją Timex, najprostsza przeróbka procedury nie korzysta z banków pamięci (listing 3).





Na uwagę zasługuje takt. że nie jest sprawdzana poprawność ładowania, gdyż w przypadku dysku błąd jest mało prawdopodobny.

Po kompilacji i zapamiętaniu (na przykład pod nazwą NAKLADKA.BIN) procedury, musimy ją „nakładkować" na oryginalny blok kodu. Chociażby w taki sposób:


LOAD "LEMMINGS.1"CODE:

LOAD""NAKLADKA.BIN" CODE 25867:

SAVE""LEMMINGS.1" CODE 24576,10125

Wcześniej warto jest sporządzić kopię pliku LEMMINGS.1 na innej dyskietce, aby w razie pomyłki nie przenosić go ponownie z taśmy.


Następnie bierzemy się za procedurę wgrywania leveli.

Może się ona znajdować w każdym z trzech bloków gry! Ładujemy więc wszystkie trzy pliki do pamięci (listing 4) nie uruchamiając ich, a następnie wgrywamy program monitora, najlepiej taki, który można umieścić w pamięci ekranu




Zlecamy programowi wyszukanie słów, które zauważyliśmy w trakcie ładowania leveli z taśmy – na przykład słówka LOAD. Znajdujemy je pod adresem 912Fh (37167) - czyli w pliku LEMMINGS.3.

Cofając się o kilka rozkazów możemy obejrzeć taką oto procedurkę:





Procedura ta ma za zadanie wyświetlić odpowiedni tekst (leżący bezpośrednio za rozkazem CALL) w odpowiednim miejscu ekranu (określonym zawartością rejestrów BC).

W tym wypadku jest to komunikat o błędzie w trakcie wgrywania - czyli procedura ta jest wywoływana zaraz po komendzie ładowania pliku.

Szukamy więc sekwencji 29h, 91h - występuje ona kilkakrotnie.

Fragment odpowiedzialny za ładowanie poziomów przedstawia listing piąty.





Komórka F55Ah (62810) zawiera numer poziomu (od 1 do 60), a pliki są ładowane zawsze pod adres 5B68h (23400) i mają długość 20D5h (8405).


Teraz bierzemy się za napisanie odpowiedniej procedury, którą zainstalujemy analogicznie, jak w przypadku programiku z listingu trzeciego.

Ponieważ wszystkich poziomów jest 60, najlepiej jest numer zakodować dwiema literami. Załóżmy, że będziemy używać jedynie pierwszych szesnastu liter (od A do P). Niech 4 starsze bity będą kodowane na pierwszej literze, a 4 młodsze - na drugiej literze.

Wobec tego level numer 1 powinien być oznakowany sekwencją AB, numer 2 - literami AC, a numer 60 - sekwencją DM. Widać, że wszystkie poziomy nie zmieszczą się na jednej dyskietce - trzeba więc obsługiwać błąd ładowania pliku, wyświetlając komunikat proszący o zmianę dysku - w tym wypadku można wykorzystać analogiczną procedurę znajdującą się pod adresem 9129h (37161).








Po kompilacji programu z listingu szóstego i zapisaniu go jako LEVELS.BIN. warto jest przeszukać plik LEMMINGS.3 i pozamieniać komunikaty dotyczące taśmy na odpowiednie, dotyczące dysku.


Następnie wpisujemy:


LOAD * "LEMMINGS.3" CODE 36621: LOAD * "LEVELS.BIN" CODE 37047:

SAVE * "LEMMINGS.3" CODE 36621,28403


a następnie stajemy na rzęsach, by na 47 poziomie lemingi nie spadły w przepaść, bo kończy się to obfitym chlapnięciem krwi we wszystkie strony.


Należy pamiętać, żeby nie wciskać BREAK w trakcie ładowania z dysku, gdyż kończy się to najczęściej pozdrowieniami od producenta komputera. Można tego uniknąć przez odpowiednią obsługę BREAK, lecz proście] jest po prostu o tym pamiętać i w ten sposób zaoszczędzić nieco pamięci.


W trzeciej. ostatniej części, mowa będzie o tym, co zrobić z gra, która chce coś nagrać na taśmie.


JACEK TROJAŃSKI