Od Środka Cz. 1

Mało kto lubi programy, które przy pierwszym lepszym błędzie lub wciśnięciu klawisza BREAK czyszczą cała pamięć komputera, nie pozostawiając po sobie żadnego śladu, albo „zawieszają się”, zmuszając do wciśnięcia RESET. Sytuacja przestaje być zabawna. adv mamy jakiś dobry program użytkowy, który chcemy przystosować do nietypowego sprzętu (lub grę do rzadko spotykanego joysticka) lub gdy chcemy zmienić w programie wszystkie teksty angielskie na polskie, a program nie daje się zatrzymać.

Chcielibyśmy przedstawić wam kilkuodcinkowy cykl artykułów. Kolejno więc przedstawimy niezbędne przy takiej pracy informacje o twoim (lub może pożyczonym) komputerze, takie jak mapa pamięci, sposób zapisu w pamięci poszczególnych linii BASIC-a, ważne zmienne systemowe, itp. Później zabierzemy się do wczytywania programów i bloków danych z taśmy w „bezpieczny” sposób, tzn. tak, by się nie uruchomiły i by można było obejrzeć ich zawartość. W końcu zajmiemy się także unieszkodliwianiem zabezpieczeń pisanych w języku wewnętrznym. Postaramy się wszystko to ilustrować konkretnymi przykładami, w oparciu o znane programy. Mamy nadzieję, że nasz wysiłek nie pójdzie na marne i ty także nauczysz się dostawać bez przeszkód do każdego programu.

Zacznijmy więc od podziału pamięci.

Ogólnie, pamięć podzielona jest na dwie główne części: ROM i RAM. ROM zajmuje adresy 0 — 16383, RAM natomiast adresy 16384 — 65535. Zawartością ROM-u nie będziemy się na razie zajmować, lecz za to przyjrzymy się dokładniej pamięci RAM. Jest ona podzielona na bloki spełniające różne funkcje w systemie BASIC-a (rys.1).

Pierwszym z nich jest obszar pamięci ekranu. Począwszy od adresu 16384 znajduje się tzw. „display file”, czyli obszar, w którym przechowywane są informacje o tym, czy kolejne punkty ekranu są zapalone, czy zgaszone. Zajmuje to 6144 bajtów. Następne 768 bajtów (od adresu 22528) to komórki pamięci określające kolory kolejnych pól ekranu (8x8 punktów). Obszar od adresu 23296 do 23551 to bufor drukarki. Jest on wykorzystywany tylko podczas współpracy komputera z drukarką. Jeśli nie używasz instrukcji dotyczących drukarki (takich jak LLIST. LPRINT, COPY), to jego zawartość nie ulega zmianie, możesz go więc wykorzystać do innych celów. Pamiętaj jednak, że użycie którejś z tych instrukcji, nawet bez podłączonej drukarki, wprowadza w tym obszarze zmiany.

Następnym fragmentem pamięci RAM są zmienne systemowe. Są to komórki pamięci wykorzystywane przez system do pamiętania niezbędnych do jego prawidłowego działania danych, takich jak np. adresy tzw. ruchomych bloków pamięci (o których zaraz powiemy), informacje o wykonywaniu programu w BASIC-u, tzn, która linia jest wykonywana, do której ma nastąpić skok, czy wystąpiły jakieś błędy, itp. W obszarze tym znajdują się także zmienne (tzn. komórki spełniające te funkcje), zawierające kod ostatnio wciśniętego klawisza, długość „beep” klawiatury i wiele jeszcze innych. Dokładniej zajmiemy się nimi później.

Bezpośrednio za zmiennymi systemowymi, które kończą się pod adresem 23733, zaczynają się tzw. ruchome obszary pamięci. Oznacza to, że adresy ich początków i końców (a także długość) mogą się zmieniać, w zależności od tego, czy są podłączone jakieś urządzenia zewnętrzne, jak długi jest program w BASIC-u, ile tworzy zmiennych, itp. Adresy ruchomych bloków RAM-u znajdują się w odpowiednich zmiennych systemowych.

Na rys. 1 i rys. 2 liczba pod strzałką oznacza adres początku wskazywanego bloku. Jeśli adres ten jest ruchomy, to zamiast liczby zapisana jest nazwa zmiennej systemowej zawierającej ten adres, oraz w nawiasie — adres tej zmiennej. W nawiasie kwadratowym znajduje się wartość tej zmiennej ustalana zaraz po włączeniu komputera (lub wykonaniu RESET), ale bez podłączonych żadnych urządzeń zewnętrznych (czyli jeśli odczytamy wartość zmiennej PROG, wykonując

PRINT PEEK 23635 + 256* PEEK 23636,

to otrzymamy wartość 23755).

Jeśli do twojego komputera podłączony jest „ Interface 1” lub interface innej szybkiej pamięci masowej, to od adresu 23734 do adresu o 1 mniejszego niż zawartość zmiennej CHANS, znajduje się „mapa microdri- ve’u” — obszar wykorzystywany jako bufor do transmisji danych, jako zbiór dodatkowych zmiennych systemowych itp. Jeśli nie jest podłączone żadne z tych urządzeń, obszar ten po prostu nie istnieje — zmienna CHANS zawiera adres 23734, Określa ona początek bloku pamięci, w którym zawarte są informacje o istniejących kanałach. Są one konieczne do prawidłowego działania instrukcji PRINT, LIST, INPUT i podobnych. W ostatniej komórce tego obszaru znajduje się liczba 128 (heksadecymalnie 80), sygnalizująca koniec tego bloku (jest to tzw. znacznik końca). Następny obszar pamięci zawiera tekst wpisanego programu w BASIC-u. Adres jego początku pamiętany jest w zmiennej PROG. Bezpośrednio za tekstem programu (od adresu wskazywanego przez zmienną VARS), znajduje się obszar, w którym interpreter umieszcza zmienne tworzone przez program. Jest on zakończony znacznikiem końca. Następnie, począwszy od adresu zawartego w zmiennej E_LINE znajduje się obszar wykorzystywany podczas edycji linii BASIC-a oraz wpisywaniu komend z klawiatury (tzn. gdy na dole ekranu miga kursor i wpisujemy instrukcje w BASIC-u). Na końcu tego obszaru znajdują się dwa bajty, o zawartościach: 13 (ENTER — koniec linii) i 128 (koniec tego obszaru). Zaraz potem, od adresu wskazywanego przez zmienną systemową WORKSP, znajduje się podobny obszar, ale służący do wpisywania danych podczas wykonywania przez interpreter instrukcji INPUT (zakończony znakiem ENTER).

Za buforem INPUT (który jest automatycznie kasowany po wykonaniu tej instrukcji) znajduje się „chwilowa przestrzeń pracy” — miejsce pamięci wykorzystywane do najrozmaitszych celów. Tam między innymi ładowane są nagłówki wczytywanych z taśmy programów, tam jest wczytywany program umieszczany w pamięci przez MERGE””, zanim zostanie dołączony do już istniejącego programu. Obszar ten jest wykorzystywany wtedy, gdy na pewien czas potrzebujemy trochę wolnej pamięci, ale tylko do chwilowego wykorzystania — potem nie jest dla nas ważne co się z jej zawartością stanie.

Od adresu wskazywanego przez STKBOT, Znajduje się stos kalkulatora. Są tam odkładane liczby w trakcie wykonywania obliczeń przez interpreter BASIC-a. Stos ten rozrasta się w górę pamięci, tzn. w kierunku coraz wyższych adresów. Zmienna systemowa STKEND określa jego koniec. Za nim znajduje się obszar nie wykorzystywanej pamięci.

Do systemu BASIC-a należy jednak obszar aż do komórki pamięci wskazywanej przez zmienną systemową RAMTOP. Pod tym adresem znajduje się liczba 62 (3E hex), która oznacza koniec obszaru wykorzystywanego przez BASIC.

Idąc teraz w dół pamięci, trafiamy na jeden bajt nie wykorzystywany*). Zaraz za tym bajtem (idąc cały czas w dół pamięci), zaczyna się „stos GOSUB”. Odkładane są na nim numery linii programu, z których zostały wykonane instrukcje skoku do podprogramu, aby interpreter wiedział dokąd ma „wrócić” instrukcja RETURN. Jeżeli interpreter nie znajduje się w żadnym podprogramie (wywołanym właśnie przez GOSUB), to stos ten po prostu nie istnieje — nie jest na nim zapisana żadna wartość. Niżej znajduje się stos maszynowy, wykorzystywany bezpośrednio przez mikroprocesor. Obydwa te stosy są odkładane w dół pamięci.

Specjalną rolę pełni zmienna systemowa ERRSP. Procedura obsługująca błąd BASIC-a (wywoływana przez rozkaz mikroprocesora RST 8) umieszcza wartość tej zmiennej w rejestrze SP, po czym wykonuje RET, odczytując w ten sposób ostatni zapisany na stosie adres (podczas wykonywania programu jest on równy 4867). Pod tym adresem w ROM-ie znajduje się procedura drukująca komunikat o błędzie.

Powyżej komórki wskazywanej przez RAMTOP znajduje się 168 bajtów zarezerwowanych na definicje znaków UDG (można je zlikwidować np. przez CLEAR 65535). Adres ostatniej komórki pamięci (równy 65535, jeśli twój komputer jest całkowicie sprawny) jest pamiętany w zmiennej P_RAMT. Jeżeli część pamięci RAM jest uszkodzona, to zmienna ta zawiera adres ostatniej sprawnej komórki.

To by było wszystko, jeśli chodzi o podział pamięci Spectrum. Za miesiąc, zajmiemy się już włamywaniem do programów napisanych w BASIC-u oraz nagłówkami zbiorów zapisanych na taśmie.

 

*) Bajt ten tworzy wraz z bajtem wskazywanym przez RAMTOP jakby jedną, dwubajtową liczbę (jest jej młodszym bajtem), konieczną do prawidłowego działania instrukcji RETURN. Gdy podczas jej wykonywania stos GOSUB-ów będzie już pusty, to liczba ta spełni rolę jego przedłużenia. Ponieważ jednak jest ona większa niż 15872 (62*256), a linie BASIC-a nie posiadają tak wysokiej numeracji, więc zostanie to wykryte jako błąd i zasygnalizowane przez komunikat „RETURN without GOSUB”.

Tomasz Surmacz, Robert Dudzik