Tajemnice ATARI

BUDOWA PROGRAMU
W ATARI-BASIC

1. Wstęp.

   Chyba prawie każdy, kto przez jakiś czas pisał programy w BASIC-u, jest ciekawy, w jakiej postaci program taki jest przechowywany w pamięci komputera, a następnie na taśmie lub dyskietce. Informacja o tym może nie tylko zaspokoić ciekawość programującego, ale także pomóc w pisaniu bardziej zwartych programów (zajmujących mniej pamięci), oraz w odzyskiwaniu programów straconych (np. przez uszkodzenie sektora dyskietki).

2. Wpisywanie programu.

   Program wpisywany z klawiatury składa się z poszczególnych wierszy, opatrzonych numerem i zawierających jedną lub więcej instrukcji. Wiersz wpisujemy jako ciąg znaków ATASCII. Naciśnięcie klawisza RETURN powoduje, że wpisany wiersz programu zostaje zapamiętany jako część programu, ale już nie w postaci kodów ATASCII, ale w postaci tzw. półskompliowanej. W tej postaci wszystkie użyte nazwy i symbole funkcji, operatorów i instrukcji zostają zamienione na jednobajtowe tzw. tokeny. W jednolity sposób zostają też zapamiętane użyte zmienne, liczby i teksty. W czasie pisania programu są tworzone tabele nazw zmiennych i wartości zmiennych, a także uaktualniane są zawartości komórek zawierających adresy poszczególnych części programu.

3. Adresy części programu.

   W osiemnastu kolejnych komórkach pamięci BASIC umieszcza adresy charakteryzujące program: 128-129 ($80-81) LOMEM - początek pamięci dostępnej dla BASIC-a
130-131 ($82-83) VNTP - początek tabeli nazw zmiennych
132-133 ($84-85) VNTD - koniec tabeli nazw zmiennych
134-135 ($86-87) WTP - początek tabeli wartości zmiennych
136-137 ($88-89) STMTAB - początek właściwego programu
138-139 ($8A-8B) STMCUR - początek bieżącego wiersza BASIC
140-141 ($8C-8D) STARP - początek tabeli zmiennych indeksowanych (tekstów i tablic)
142-143 ($8E-8P) RUNSTK - początek stosu dla instrukcji GOSUB i FOR/NEXT
144-145 ($90-91) MEMTOP - koniec obszaru zajętego przez BASIC

4. Tabela nazw zmiennych.

   Tuż za LOMEM interpreter BASIC-a zostawia sobie jedną wolną stronę na bufor wydawniczy tokenów, a następnie rozpoczyna się tabela nazw zmiennych. Są w niej umieszczane nazwy zmiennych w takiej kolejności, w jakiej pojawiały się przy pisaniu programu. Nazwy zmiennych tekstowych jako ostatni znak mają $, a nazwy tablic pamiętane są do nawiasu otwierającego włącznie.

   Jeżeli mamy w pamięci dowolny program w BASIC-u, to możemy obejrzeć tabelę nazw zmiennych, np. w następujący sposób:
FOR A=PEEK(130)+256*PEEK(131) TO PEEK(132)+
256*PEEK(133): ?CHR$(PEEK(A));:NEXT A
   Widzimy, że ostatni znak każdej zmiennej jest w negatywie, tzn. ma ustawiony najstarszy bit. Np. jeżeli w programie występują zmienne: A, B, C1, WILK, KOZA$, KAPUSTA(1,1), to tabela wygląda następująco:


5. Tabela wartości zmiennych.

    Tuż za tabelą nazw zmiennych rozpoczyna się tabela wartości zmiennych. Zmienne występują w tej samej kolejności, co w tabeli nazw zmiennych. Każda zmienna zajmuje w tabeli 8 bajtów:

   Bajt 1 - typ zmiennej:
$00 - zmienna liczbowa
$40 lub 41 - tablica
$80 lub 81 - zmienna tekstowa.

   Bajt 2 - numer kolejny zmiennej. Numery liczone są od 0, maksymalny numer zmiennej - $7F (127).

   Bajty 3-8 - dla zmiennej liczbowej zawierają wartość zmiennej, a dla pozostałych - 3 kolejne pary bajtów zawierają parametry tekstu lub tablicy, a mianowicie kolejno:
dla tablicy: położenie w tabeli zmiennych indeksowanych, pierwszy i drugi wymiar tablicy;
dla zmiennej tekstowej: położenie w tablicy zmiennych indeksowanych, aktualna i zadeklarowana długość tekstu.

   Obejrzyjmy sobie tabelę wartości zmiennych. Najlepiej wyświetlić ją w kodzie szesnastkowym, co można zrobić najprościej w TURBO BASIC-u XL:
POKE 83,26: FOR A=DPEEK(134) TO DPEEK(136)-
1:?HEX$(PEEK(A)); " ";:NEXT A: POKE 83,39
   Jeżeli program nie był uruchomiony, zmienne liczbowe będą miały wartość 0 (6 bajtów zerowych). Jeżeli był uruchomiony i został zatrzymany, możemy zobaczyć aktualne wartości zmiennych. Aby je odczytać, trzeba znać sposób reprezentacji liczb w ATARI. Jest to podane w skrócie w dodatku A.

6. Budowa wiersza programu.

   Zaraz za tabelą wartości zmiennych umieszczone są kolejne wiersze programu. W każdym wierszu pierwsze dwa bajty oznaczają numer wiersza. Jest to liczba binarna w zakresie od 0 do 32767 (kolejność bajtów: młodszy, starszy). Trzeci bajt oznacza długość wiersza w bajtach (maks. 255), liczoną łącznie z dwoma pierwszymi bajtami. Czwarty bajt oznacza liczbę bajtów do końca instrukcji. Jeżeli wiersz zawiera jedną instrukcję, to trzeci i czwarty bajt są jednakowe. Jeżeli wiersz zawiera więcej instrukcji, to pierwszy bajt po każdym dwukropku oznacza liczbę bajtów od początku wiersza do końca bieżącej instrukcji. Na podstawie 3. i 4. bajtu można zorientować się, gdzie się zaczynają i kończą kolejne instrukcje.

   W następnych bajtach zakodowana jest instrukcja. Symbole funkcji, operatorów, instrukcji i znaków pomocniczych zakodowane są w postaci jednobajtowych tokenów (patrz dodatek B).

   Każda występująca w instrukcji nazwa zmiennej jest reprezentowana przez jednobajtowy numer zmiennej, z dodatkowo ustawionym najstarszym bitem (np. zmienna nr 4 ma kod $84). Każda stała liczbowa zajmuje 7 bajtów: pierwszy bajt $0E, następne 6 bajtów liczba w postaci zmiennoprzecmkowej (patrz dodatek A). Stała tekstowa zaczyna się od bajtu $0F, drugi bajt oznacza liczbę znaków tekstu, od trzeciego bajtu zaczyna się sam tekst w kodach ATASCII. Ciągi tekstowe, występujące po instrukcjach REM i DATA mają postać kodów ATASCII.

   Na końcu wiersza występuje bajt o kodzie $16 lub (np. na końcu wiersza z instrukcją DATA) - $9B (RETURN). Na końcu instrukcji, która nie jest ostatnią w wierszu występuje dwukropek o kodzie $14.

7. Zakończenie programu.

   Po zakończeniu ostatniej instrukcji programu następuje bieżący wiersz programu. Jest to wiersz, który był ostatnio wpisywany. Jeżeli był to wiersz pisany w trybie bezpośrednim (bez numeru linii), to jako numer linii występuje liczba 32768 (bajty: $00, $80).

   Bezpośrednio za bieżącym wierszem jest miejsce na tabelę wartości zmiennych indeksowanych, tzn. zmiennych tekstowych i tablic. Każdy element zmiennej tekstowej zajmuje 1 bajt, każdy element tablicy - 6 bajtów.

   Za tą tabelą BASIC umieszcza stos przeznaczony do zapamiętywania adresów powrotnych Instrukcji GOSUB i FOR/NEXT. Każda czynna instrukcja GOSUB zajmuje na tym stosie 4 bajty, a FOR/NEXT - 16 bajtów. W przypadku GOSUB pierwszym bajtem jest 0 jako kod Instrukcji, następne 2 bajty mieszczą numer wiersza zawierającego tę instrukcję, a czwarty bajt położenie tej instrukcji w wierszu. Dla pętli FOR/NEXT pierwsze 6 bajtów zawiera wartość końcową licznika (to co jest po TO), następne 6 bajtów - krok inkrementacji (STEP), następny bajt - numer zmiennej, kolejne dwa - numer wiersza programu, a ostatni - położenie instrukcji FOR w wierszu.

8. Zapis programu.

   Program może być zapisany na taśmie lub dyskietce w dwóch postaciach:

   1. Postać źródłowa (listing) - zapis instrukcjami LIST "C:", LIST "D:NAZWA".

   2. Postać "półskompilowana" - zapis instrukcjami CSAVE, SAVE "C:", SAVE "D:NAZWA".

    Przy zapisie w postaci listingu zapisywane są na taśmie lub dysku w postaci kodów ATASCII kolejne bajty listingu, tak jak to widzimy na ekranie po napisaniu LIST. Przy zapisie w postaci półskompilowanej program zapisywany jest w takiej postaci, w jakiej jest pamiętany w pamięci komputera, od początku tabeli nazw zmiennych do końca bieżącego wiersza. Dodatkowo na samym początku zapisywane jest 14 bajtów zawierających 7 adresów poszczególnych części programu. Adresy te pochodzą z komórek 128-141, przy czym od każdego adresu odejmowana jest wartość LOMEM (zawartość komórek 128-129). Tak więc pierwsze dwa bajty są zerowe, następne dwa zawierają liczbę $100 (kolejno $00, $01).

   Przy pomocy prostego triku możemy obejrzeć cały stokenizowany program (w postaci znaków ATASCII). Napiszmy:
SAVE "S:"
a zobaczymy cały program w takiej postaci, jak jest zapisywany na dysku lub kasecie.

   Panuje powszechne przekonanie, że zapis w postaci listingu jest dłuższy. Przy zapisie na taśmie jest to prawda, ale przede wszystkim dlatego, że zapis odbywa się z długimi przerwami. Natomiast liczba zapisanych bajtów na ogół jest mniej więcej taka sama, a często nawet mniejsza w przypadku listingu. Dlaczego tak jest, wyjaśnię na przykładzie. Napiszmy wiersz programu:
10 SOUND 0,0,0,0
Listing zawiera 17 znaków (łącznie ze spacjami i znakiem RETURN). Ten sam wiersz w postaci półskompilowanej ma długość 37 bajtów - ponad 2 razy więcej niż listing. Co prawda instrukcja SOUND zajmuje tylko 1 bajt, ale za to każda liczba zajmuje 7 bajtów.

9. Efektywne pisanie programu.

   Jeżeli chcemy, żeby program zajmował mało miejsca w pamięci i na taśmie lub dysku (np. program jest bardzo długi, lub potrzebuje dużo pamięci na tablice, albo też chcemy jak najbardziej skrócić czas wczytywania), to przede wszystkim trzeba często występujące stałe liczbowe zastąpić zmiennymi. Wiersz programu, podany jako przykład w poprzednim rozdziale, po zastąpieniu stałej 0 zmienną X, której wcześniej przypisano wartość 0, będzie miał postać:
10 SOUND X,X,X,X
i zajmie 13 bajtów zamiast 37.

   Inne sposoby skracania programu, np. pisanie wielu instrukcji w jednym wierszu, stosowanie krótkich nazw zmiennych, zmniejszanie liczby zmiennych - są także skuteczne, ale skracają program w znacznie mniejszym stopniu, niż sposób podany wyżej.

   Przy skracaniu programu trzeba mieć na uwadze, że może się nieco pogorszyć czytelność listingu, a także zmieni się czas wykonywania programu, co często bywa ważne.

    Operacją, którą powinno się wykonać po napisaniu programu, jest pozbycie się niepotrzebnych zmiennych. Chodzi o to, że w czasie pisania programu dokonuje się licznych zmian, poprawek, popełnia się błędy, a wiele z wprowadzanych przy okazji napisów ląduje w tabeli zmiennych, nie występując w samym programie. Co prawda, jak podano w [3], najnowsza wersja interpretera ATARI-BASIC eliminuje zmienne, które powstały po wpisaniu błędnego wiersza, ale i tak dużo "śmieci" pozostaje. Przeglądałem wiele programów różnych autorów (m.in. programy nadawane w audycji RADIOKOMPUTER). Tabele zmiennych niektórych z tych programów zawierały ponad 40 niepotrzebnych zmiennych! Oznacza to kilkaset bajtów niepotrzebnie zajętych w tabelach nazw i wartości zmiennych, a w konsekwencji na taśmie lub dysku. Ciekawostką jest, że w wielu programach występuje nazwa ADY, która jest resztką napisu READY.

   Aby pozbyć się zmiennych nie występujących w programie, należy zapisać program na taśmę lub dysk instrukcją LIST, usunąć program z pamięci instrukcją NEW, odczytać instrukcją ENTER i dopiero zapisać przez SAVE lub CSAVE. Przy LIST tabela zmiennych nie jest zapisywana, a przy ENTER jest tworzona na nowo tylko z tych zmiennych, które występują w programie.

10. Zabezpieczanie programów.

   Sposobów zabezpieczania programów przed listowaniem lub zatrzymaniem jest wiele i nie jest to tematem niniejszego artykułu. Tutaj wspomnę tylko o paru sprawach związanych z budową programu.

   Ponieważ w programie używane są numery zmiennych, a nie ich nazwy, więc postać tabeli nazw zmiennych nie est ważna dla działania programu. Gdy program jest gotowy, do tabeli nazw zmiennych można wpisać zupełnie dowolne kody. Program będzie działał, ale listingi mogą wyglądać dziwnie. Spróbujmy np. wypełnić tabelę nazw zmiennych bajtami o kodzie znaku RETURN (155=$9B). Jest to wykorzystywane w zabezpieczaniu przed listowaniem.

   Innymi sposobami są zmiany niektórych adresów zawartych w komórkach 128-141 lub np. wpisanie liczby 0 do bajtu oznaczającego długość jednego z wierszy.

   Aby dokładnie przyjrzeć się programowi i ewentualnie dokonać w nim jakichś wymyślnych zmian, wygodnie jest posłużyć się dowolnym programem typu monitor.

   Mam nadzieję, że informacje podane w tym artykule przyczynią się do lepszego poznania komputera przez użytkowników.

Literatura

   1. ATARI PEEKs and POKEs

   2. The ATARI BASIC Source Book, (COMPUTE! Publications Inc.)

   3. Zientara W. - Mapa pamięci ATARI XL/XE. Procedury interpretera BASIC-a

Dodatek A

   Liczby zmiennoprzecinkowe

   Każda liczba w ATARI zajmuje 6 bajtów. W pierwszym bajcie mieści się znak liczby (najstarszy bit: 0 - liczba dodatnia) i wykładnik (pozostałe 7 bitów: wykładnik potęgi liczby 100 powiększony o 64). W pozostałych pięciu bajtach mieści się 10-cyfrowa mantysa w kodzie BCD. Pierwszy bajt mantysy jest niezerowy i uważa się, że po nim występuje kropka dziesiętna. Wyjątkiem jest liczba 0, którą reprezentuje 6 bajtów zerowych.

   Przykłady reprezentacji liczb w ATARI:

1 40 01 00 00 00 00
46 40 46 00 00 00 00
32768 42 03 27 68 00 00
0.0003=3*100^-2 3E 03 00 00 00 00
-65535=6.5535*100^2 C2 06 55 35 00 00
-1 C0 01 00 00 00 00

Dodatek B

   Tokeny ATARI-BASIC

   Tokeny są jednobajtowymi odpowiednikami instrukcji, operatorów, funkcji, zmiennych, zgodnie z następującym podziałem (wszystkie liczby - w zapisie szesnastkowym):

   1. Stałe, zmienne, operatory i funkcje:

00-0D nieużywane
0E oznaczenie stałej liczbowej
0F oznaczenie stałej tekstowej
10-3C operatory
3D-54 funkcje
55-7F nieużywane
80-FF nazwy zmiennych

   2. Nazwy instrukcji: 00-36

   Oto szczegółowe tabele tokenów:

OPERATORY:

, 12
$ 13
: 14 rozdzielenie instrukcji w wierszu
; 15
CR 16 koniec wiersza
GOTO 17 po ON
GOSUB 18 po ON
TO 19
STEP 1A
THEN 1B
# 1C
<= 1D relacje między liczbami
<> 1E relacje między liczbami
>= 1F relacje między liczbami
< 20 relacje między liczbami
> 21 relacje między liczbami
= 22 relacje między liczbami
^ 23 operacje arytmetyczne
* 24 operacje arytmetyczne
+ 25 operacje arytmetyczne
- 26 operacje arytmetyczne
/ 27 operacje arytmetyczne
NOT 28 operacje logiczne
OR 29 operacje logiczne
AND 2A operacje logiczne
( 2B
) 2C
= 2D operacja przyporządkowania (dla liczb)
= 2E operacja przyporządkowania (dla tekstów)
<= 2F relacje między tekstami
<> 30 relacje między tekstami
>= 31 relacje między tekstami
< 32 relacje między tekstami
> 33 relacje między tekstami
= 34 relacje między tekstami
+ 35 znak liczby
- 36 znak liczby
( 37 nawias długości tekstu
( 38 lewy nawias tablicy
( 39 lewy nawias DIM tablicy
( 3A lewy nawias funkcji
( 3B lewy nawias DIM tekstu
, 3C oddzielenie wymiaru tablicy

funkcje instrukcje
STR$ 3D REM 00 POINT 1C
CHR$ 3E DATA 01 XIO 1D
USR 3F INPUT 02 ON 1E
ASC 40 COLOR 03 POKE 1F
VAL 41 LIST 04 PRINT 20
LEN 42 ENTER 05 RAD 21
ADR 43 LET 06 READ 22
ATN 44 IF 07 RESTORE 23
COS 45 FOR 08 RETURN 24
PEEK 46 NEXT 09 RUN 25
SIN 47 GOTO 0A STOP 26
RND 48 GO TO 0B POP 27
FRE 49 GOSUB 0C ? 28
EXP 4A TRAP OD GET 29
LOG 4B BYE 0E PUT 2A
CLOG 4C CONT 0F GRAPHICS 2B
SQR 4D COM 10 PLOT 2C
SGN 4E CLOSE 11 POSITION 2D
ABS 4F CLR 12 DOS 2E
INT 50 DEG 13 DRAWTO 2F
PADDLE 51 DIM 14 SETCOLOR 30
STICK 52 END 15 LOCATE 31
PTRIG 53 NEW 16 SOUND 32
STRIG 54 OPEN 17 LPRINT 33
LOAD 18 CSAVE 34
SAVE 19 CLOAD 35
STATUS 1A DOMYSLNE
NOTE 1B LET 36


Lech Bogusz



Powrót na start | Powrót do spisu treści | Powrót na stronę główną

Pixel 2001