Serwis Edukacyjny
w I-LO w Tarnowie
obrazek

Materiały dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

Autor artykułu: mgr Jerzy Wałaszek

©2024 mgr Jerzy Wałaszek
I LO w Tarnowie

ROZDZIAŁ 10

Basic ATARI

Podrozdziały

Czym jest BASIC ATARI?

Język ATARI BASIC jest językiem interpretowanym. Oznacza to, iż programy można uruchamiać po wprowadzeniu ich bez pośrednich etapów kompilacji i łączenia. Interpreter ATARI BASIC umieszczony jest w 8 KB kartridżu ROM w lewym slocie komputera,


ATARI 800 ze zdjętą pokrywą obudowy. Widoczne karty pamięci i kartridż BASIC.

W komputerach ATARI XL/XE kartridż BASIC'a został wbudowany we wnętrzu komputera. BASIC zajmuje obszar pamięci od adresu $A000 do $BFFF. Aby uruchomić BASIC potrzebne jest co najmniej 8 KB pamięci RAM.

Aby efektywnie korzystać z języka ATARI BASIC, musisz znać jego mocne i słabe strony. Informacja ta pozwoli ci tworzyć programy wykorzystujące zalety i własności ATARI BASIC'a.

Mocne strony ATARI BASIC'a:

Słabe strony ATARI BASIC'a:


do podrozdziału  do strony 

Sposób działania BASIC'a ATARI

Praca interpretera języka BASIC jest podsumowana następująco:

Szczegóły tych operacji są dyskutowane w następnych czterech podrozdziałach.


do podrozdziału  do strony 

Proces tokenizacji

W prostych słowach tokenizacja wiersza kodu w języku BASIC wygląda tak:
  1. BASIC dostaje wiersz wejściowy.
  2. Sprawdza on następnie, czy wiersz ma poprawną składnię. Jeśli nie, raportowany jest błąd.
  3. Podczas sprawdzania składni wiersz jest tokenizowany.
  4. Stokenizowany wiersz jest umieszczany w programie.
  5. Jeśli wiersz jest w trybie natychmiastowym (nie posiada numeru wiersza), to zostaje wykonany.

Aby lepiej zrozumieć proces tokenizacji, niektóre terminy muszą zostać zdefiniowane:

Token/symbol 8-bitowy bajt zawierający określony kod do interpretacji.
Polecenie Pełne "zdanie" z tokenów, które nakazuje wykonanie BASIC'owi jakiegoś zadania. W postaci listingu polecenia są rozdzielone dwukropkami.
Wiersz Jedno lub więcej poleceń poprzedzone numerem wiersza w zakresie od 0 do 32767 lub wiersz w trybie natychmiastowym bez numeru wiersza.
Rozkaz Pierwszy wykonywalny token polecenia, który informuje BASIC, jak ma zinterpretować dalsze tokeny, które występują za tokenem rozkazowym.
Zmienna Token będący pośrednim wskaźnikiem do swojej rzeczywistej wartości; w ten sposób wartość ta może być zmieniana bez zmiany tokenu.
Stała 6-bajtowa wartość BCD poprzedzona specjalnym tokenem. Wartość ta pozostaje niezmieniona przez całe wykonanie programu.
Operator Dowolny z 46 tokenów, które w pewien sposób modyfikują wartości występujące za nimi.
Funkcja Token, który po wykonaniu zwraca jakąś wartość do programu.
EOL Koniec wiersza (ang. End Of Line). Jest to znak o kodzie szesnastkowym 9B.
BCD Liczba dziesiętna kodowana dwójkowo (ang. Binary Coded Decimal). Liczba używająca trybu dziesiętnego mikroprocesora 6502.

BASIC rozpoczyna tokenizację przez pobranie wiersza danych wejściowych. Te dane są otrzymywane z jednego ze sterowników w systemie operacyjnym. Normalnie wiersz wejściowy pochodzi z edytora ekranowego, jednakże za pomocą rozkazu ENTER można określić dowolne urządzenie. Wywołaniem używanym przez BASIC jest rozkaz odczytu rekordu, a dane są zwracane w postaci ciągu znaków ATASCII zakończonego znakiem EOL. Dane te zostają umieszczone przez CIO w buforze wejściowym wiersza BASIC od adresu $580 do $5FF.

Po zwróceniu tego rekordu rozpoczyna się proces sprawdzania składni i tokenizacji. Najpierw BASIC szuka numeru wiersza. Jeśli taki znajdzie, zamienia go na 2-bajtową liczbę całkowitą. Jeśli numer wiersza jest nieobecny, to zostaje przyjęty tryb natychmiastowy i wierszowi przydziela się numer $8000. Będą to dwa pierwsze tokeny tokenowego wiersza. Wiersz ten jest budowany w buforze wyjściowym o długości 256 bajtów przebywającym na końcu zarezerwowanej pamięci RAM systemu operacyjnego.

Następnym tokenem jest bajt zarezerwowany na zliczanie bajtów od początku tego wiersza do początku następnego wiersza. Za nim jest kolejny bajt z odległością od początku tego wiersza do początku następnego polecenia. Wartości te zostaną ustawione, gdy zakończy się tokenizacja wiersza i polecenia. Użycie tych wartości przedyskutowano w rozdziale dotyczącym procesu wykonywania programu.

Teraz BASIC szuka rozkazu dla pierwszego polecenia wiersza wejściowego. Następuje sprawdzenie poprawności tego rozkazu przez przeszukanie listy dozwolonych rozkazów w ROM. Jeśli zostanie znaleziona zgodność to następnym bajtem w kodowanym wierszu staje się numer tego elementu listy w ROM, który był zgodny. Jeśli nie stwierdzi się żadnej zgodności, to bajtowi temu przypisywany jest token błędu składniowego i BASIC przerywa tokenizację, kopiuje resztę bufora wejściowego w formacie ATASCII do bufora wyjściowego i wypisuje wiersz z błędem.

Zakładając, iż wiersz jest dobry, jeden z siedmiu elementów może wystąpić za rozkazem: zmienna, stała, operator, funkcja, cudzysłów, inne polecenie lub znak EOL. BASIC sprawdza, czy następny znak wejściowy jest numeryczny. Jeśli nie, to porównuje ten znak wraz ze znakami za nim z elementami w tablicy nazw zmiennych. Jeśli jest to pierwszy wiersz kodu wprowadzonego w programie, to nie zostanie znaleziona zgodność. Znaki te są następnie porównywane z tablicami funkcji i operatorów. Jeśli nie zostanie znaleziona żadna zgodność, to BASIC zakłada, że jest to nazwa nowej zmiennej. Ponieważ jest to pierwsza zmienna, zostaje jej przypisany pierwszy element w tablicy nazw zmiennych. Znaki zostają skopiowane z wiersza wejściowego i umieszczone w tablicy nazw z ustawionym najbardziej znaczącym bitem w ostatnim bajcie nazwy. Następnie osiem bajtów jest rezerwowane w tablicy wartości zmiennych dla tego elementu. (Zobacz na dyskusję na temat tablicy wartości zmiennych w podrozdziale "Struktura pliku tokenowego".)

Tokenem, który trafia do tokenowego wiersza jest numer zmiennej minus jeden z ustawionym najstarszym bitem. W ten sposób pierwsza wprowadzona zmienna otrzyma numer szesnastkowy $80, druga będzie miała numer $81 i tak dalej aż do $FF dla maksymalnej liczby 128 zmiennych.

Jeśli zostanie znaleziona funkcja, to tokenowi będzie przypisany numer jej wpisu w tablicy operatorów i funkcji. Funkcje wymagają pewnego ciągu parametrów; te są zawarte w tablicach składniowych, a jeśli się nie będą zgadzać, powstanie w wyniku błąd składni.

Jeśli zostanie znaleziony operator, to tokenowi będzie nadany numer jego wpisu w tablicy. Operatory mogą występować jeden za drugim w dosyć skomplikowany sposób (np. wielokrotne nawiasy), zatem sprawdzanie ich składni jest trochę złożone.

W przypadku cudzysłowu BASIC zakłada, że jest to łańcuch znakowy i nadaje wartość $0F tokenowi wyjściowemu oraz rezerwuje dodatkowy bajt na długość łańcucha. Znaki są przenoszone z bufora wejściowego do bufora wyjściowego aż do momentu, gdy zostanie znaleziony następny cudzysłów. Bajt długości zostaje wtedy ustawiony na liczbę znaków łańcucha.

Jeśli następne znaki w buforze wejściowym są numeryczne, to BASIC konwertuje je na 6-bajtową stałą BCD. Do bufora wyjściowego jest wstawiany token $0E, po którym następuje 6-bajtowa stała.

Gdy zostanie napotkany dwukropek, do bufora wyjściowego jest wstawiany token o kodzie $14, a przesunięcie od początku wiersza zostaje umieszczone w bajcie, który był zarezerwowany na liczbę bajtów do następnego polecenia. W tym punkcie rezerwowany jest kolejny bajt i proces wraca do początku, aby pobrać następny rozkaz.

Gdy zostanie znaleziony znak końca wiersza EOL, zapisywany jest token o kodzie $16, a liczba bajtów od początku wiersza umieszczana jest w zarezerwowanym do tego celu bajcie. W tym punkcie tokenizacja dobiegła końca i BASIC przenosi wiersz z kodami tokenów do programu. Najpierw poszukuje w programie numeru wiersza. Jeśli go znajdzie, to zamienia stary wiersz nowym. Jeśli go nie znajdzie, to wstawia nowy wiersz w prawidłowej kolejności. W obu przypadkach dane występujące po tym wierszu będą przesuwane w górę lub w dół pamięci, aby pozwolić na rozrost lub skurczenie się programu.

Teraz BASIC sprawdza, czy wiersz tokenowy jest wierszem w trybie natychmiastowym. Jeśli tak, wiersz zostaje wykonany zgodnie z metodami opisanymi w procesie interpretacyjnym; jeśli nie, BASIC wraca, aby pobrać następny wiersz wejściowy.

Jeśli w dowolnym momencie podczas procesu tokenizacji długość wiersza tokenowego przekroczy 256 bajtów, na ekran zostanie przesłana wiadomość o błędzie nr 14 (wiersz zbyt długi), a BASIC wróci do pobrania następnego wiersza wejściowego.

Przykładowy wiersz wejściowy oraz jego stokenizowana postać wygląda następująco (wszystkie kody tokenów są podane w systemie szesnastkowym):

10 LET X=1 : PRINT X 
0A 00 13 0F 06 80 2D 0E 40 01 00 00 00 00 14 13 20 80 16
Wiersz nr 10 Line
Offset
Offset
polecenia
LET X = Stała
liczbowa
1 Koniec
polecenia
Offset
polecenia
PRINT X Koniec
wiersza

Rys.10-1 Przykład stokenizowanego wiersza wejściowego

ROZKAZY OPERATORY FUNKCJE
HEX DEC   HEX DEC   HEX DEC  
00
01
02
03
04
05
06
07
08
09
0A
0B
0C
0D
0E
0F
10
11
12
13
14
15
16
17
18
19
1A
1B
1C
1D
1E
1F
20
21
22
23
24
25
26
27
28
29
2A
2B
2C
2D
2E
2F
30
31
32
33
34
35
36
37
00
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
REM
DATA
INPUT
COLOR
LIST
ENTER
LET
IF
FOR
NEXT
GOTO
GO TO
GOSUB
TRAP
BYE
CONT
COM
CLOSE
CLR
DEG
DIM
END
NEW
OPEN
LOAD
SAVE
STATUS
NOTE
POINT
XIO
ON
POKE
PRINT
RAD
READ
RESTORE
RETURN
RUN
STOP
POP
?
GET
PUT
GRAPHICS
PLOT
POSITION
DOS
DRAWTO
SETCOLOR
LOCATE
SOUND
LPRINT
CSAVE
CLOAD

[IMPLIKOWANE LET]
ERROR[SKŁADNIA]
0E
0F
10
11
12
13
14
15
16
17
18
19
1A
1B
1C
1D
1E
1F
20
21
22
23
24
25
26
27
28
29
2A
2B
2C
2D
2E
2F
30
31
32
33
34
35
36
37
38
39
3A
3B
3C
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
[STAŁA LICZBOWA]
[STAŁA ŁAŃCUCHOWA]
[NIEUŻYWANE]
[NIEUŻYWANE]

,
$
: [KONIEC POLECENIA]
;
[KONIEC WIERSZA]
GOTO
GOSUB

TO
STEP
THEN
#
<= [LICZBY]
<>
>=
<
>
=

 
*
+
-
/
NOT
OR
AND
(
)
= [PRZYPISANIE LICZBOWE]
= [PRZYPISANIE ŁAŃCUCHOWE]
<= [ŁAŃCUCHY]
<>
>=
<
>
=
+
[JEDNOARGUMENTOWY]
-
( [NAWIAS ŁAŃCUCHOWY]
( [NAWIAS TABLICOWY]
( [NAWIAS TABLICY W DIM]
( [NAWIAS W FUNKCJI]
( [NAWIAS ŁAŃCUCHOWY W DIM]
, [PRZECINEK TABLICOWY]
3D
3E
3F
40
41
42
43
44
45
46
47
48
49
4A
4B
4C
4D
4E
4F
50
51
52
53
54
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
STR$
CHR$
USR
ASC
VAL
LEN
ADR
ATN
COS
PEEK
SIN
RND
FRE
EXP
LOG
CLOG
SQR
SGN
ABS
INT
PADDLE
STICK
PTRIG
STRIG

do podrozdziału  do strony 

Struktura pliku tokenowego

Plik tokenowy zawiera dwa główne segmenty: (1) grupę wskaźników ze strony zerowej, które wskazują dane wewnątrz pliku tokenowego i (2) sam plik tokenowy. Wskaźniki ze strony zerowej są 2-bajtowymi wartościami, które wskazują różne sekcje pliku tokenowego. Jest dziewięć 2-bajtowych wskaźników i znajdują się one w komórkach o adresach od $80 do $91. Poniżej przedstawiono listę tych wskaźników wraz z sekcjami pliku tokenowego, które są adresowane przez wskaźniki.
Wskaźnik Sekcja pliku tokenowego (ciągłe bloki)
LOMEM
$80,$81
Wyjściowy bufor tokenowy – jest to bufor używany przez BASIC do tokenizacji jednego wiersza kodu. Ma długość 256 bajtów. Bufor ten znajduje się na końcu pamięci RAM przydzielonej przez system operacyjny
VNTD
$82,$83
Wskaźnik tablica nazw zmiennych (ang. Variable Name Table Pointer) – tablica ta jest listą wszystkich nazw zmiennych, które wprowadzono w programie. Są one zapisywane jako znaki ATASCII, każda nowa nazwa zapisana w kolejności jej wprowadzenia. Istnieją trzy typy elementów:
  1. Zmienne skalarne – w ostatnim znaku nazwy ustawiony jest najstarszy bit.
  2. Zmienne łańcuchowe – ostatnim znakiem jest znak $ z ustawionym najstarszym bitem.
  3. Zmienne tablicowe – ostatnim znakiem jest znak ( z ustawionym najstarszym bitem.
VNTP
$84,$85
Wskaźnik końca tablicy nazw zmiennych – BASIC używa tego wskaźnika do wskazywania końca tablicy nazw. Normalnie wskazywany jest bajt zero tuż za tablicą, jeśli jest mniej niż 128 zmiennych. Gdy obecne jest 128 zmiennych, wskaźnik ten wskazuje ostatni bajt ostatniej nazwy zmiennej.
VVTP
$86,$87
Wskaźnik tablicy wartości zmiennych (ang. Variable Value Table Pointer) – tablica ta zawiera bieżącą informację o każdej zmiennej. Dla każdej zmiennej w tablicy nazw tablica wartości rezerwuje osiem bajtów. Informacja dla każdej zmiennej jest następująca:
Numer bajtu 1 2 3 4 5 6 7 8
Skalar $00 Nr zmiennej 6-bajtowa stała BCD
Tablica (z DIM)
(jeszcze bez DIM)
$41
$40
Nr zmiennej Przesunięcie od
STARP($8C,$8D)
pierwszy
wymiar + 1
drugi
wymiar + 1
Łańcuch (z DIM)
(jeszcze bez DIM)
$81
$80
Nr zmiennej Przesunięcie od
STARP
długość wymiar

Zmienna skalarna zawiera wartość liczbową. Przykładem jest X=1. Skalarem jest X, a jego wartością jest 1 zapisane w 6-bajtowym formacie BCD. Tablica zbudowana jest z elementów liczbowych przechowywanych w obszarze tablic i łańcuchów i posiada jeden element w tablicy wartości. Łańcuch zbudowany jest z elementów znakowych w obszarze tablic i łańcuchów, również posiada jeden element w tej tablicy.

Pierwszy bajt każdego elementu wartości oznacza typ zmiennej: $00 dla skalara, $40 dla tablicy i $80 dla łańcucha. Jeśli tablica lub łańcuch został zdefiniowany przez rozkaz DIM, to najmłodszy bit zostaje ustawiony w pierwszym bajcie.

Drugi bajt zawiera numer zmiennej. Element pierwszej zmiennej ma numer zero, a jeśli jest obecne 128 zmiennych, to ostatnia będzie miała numer $7F.

W przypadku zmiennej skalarnej bajty od numeru trzy do osiem zawierają 6-bajtową liczbę BCD, która została bieżąco przypisana zmiennej.

Dla tablic i łańcuchów bajty trzeci i czwarty zawierają przesunięcie od początku obszaru tablic i łańcuchów (opisanego niżej) do początku danych.

Bajty pięć i sześć tablicy zawierają jej pierwszy wymiar. Wartość ta jest 16-bitową liczbą całkowitą o jeden większą od danej wprowadzonej przez użytkownika. Bajty siedem i osiem są drugim wymiarem również o jeden większym od wartości podanej przez użytkownika.

Bajty pięć i sześć łańcucha są 16-bitową liczbą, która zawiera jego bieżącą długość. Bajty siedem i osiem są jego wymiarem (do 32767 bajtów).

STMTAB
$88,$89
Tablica poleceń (ang. Statement Table) – Ten blok danych obejmuje wszystkie wiersze kodu, które wprowadził użytkownik do programu i które stokenizował BASIC, jak również wiersz trybu natychmiastowego. Format tych wierszy opisany jest na przykładzie wiersza tokenowego w poprzednim podrozdziale.
STMCUR
$8A,$8B
Bieżące polecenie (ang. Current Statement) – ten wskaźnik używany jest przez BASIC do adresowania kolejnych tokenów wewnątrz wiersza tablicy poleceń. Gdy BASIC oczekuje na dane wejściowe, wskaźnik ten zostaje ustawiony na początek wiersza trybu natychmiastowego.
STARP
$8C,$8D
Wskaźnik obszaru łańcuchów i tablic (ang. String/Array area Pointer) – ten blok zawiera dane wszystkich łańcuchów i tablic. Znaki łańcuchów są przechowywane jako jednobajtowe elementy w kodzie ATASCII, zatem łańcuch 20 znaków będzie wymagał 20 bajtów. Tablice są przechowywane jako ciągi liczb 6-bajtowych w kodzie BCD. Tablica 10-elementowa będzie wymagała 60 bajtów. Obszar ten jest przydzielany i następnie powiększany przez każdy napotkany rozkaz DIM, powiększenie równe jest rozmiarowi łańcucha lub sześciokrotnemu rozmiarowi tablicy.
RUNSTK
$8E,$8F
Stos czasu wykonywania programu (ang. Run Time Stack) – ten programowy stos zawiera elementy GOSUB i FOR/NEXT. Element GOSUB składa się z czterech bajtów. Pierwszy ma wartość 0, co oznacza GOSUB, po nim następuje 2-bajtowa liczba całkowita zawierająca numer wiersza, z którego nastąpiło wywołanie. Po niej występuje przesunięcie w tym wierszu, aby rozkaz RETURN mógł wrócić i wykonać następne polecenie. Element FOR/NEXT zawiera 16 bajtów. Pierwsze 6 bajtów zawiera granicę, którą może osiągnąć zmienna. Następne 6 bajtów zawierają wartość przyrostu zmiennej licznikowej pętli. Obie liczby są w zmiennoprzecinkowym formacie BCD. Trzynasty bajt określa numer zmiennej licznikowej z ustawionym najstarszym bitem. W bajtach czternastym i piętnastym znajduje się numer wiersza z rozkazem FOR, a w bajcie szesnastym jest przesunięcie od początku tego wiersza do rozkazu FOR.
MEMTOP
$90,$91
Szczyt pamięci RAM aplikacji – jest to koniec programu użytkownika. Rozszerzanie programu może wystąpić z tego miejsca do końca niezajętej pamięci RAM, który definiuje początek listy wyświetlania. Funkcja FRE zwraca ilość wolnej pamięci RAM przez odjęcie MEMTOP od HIMEM ($2E5,$2E6). Zapamiętaj, zmienna MEMTOP w języku BASIC nie jest tą samą zmienną MEMTOP systemu operacyjnego.

do podrozdziału  do strony 

Proces wykonywania programu

Wykonywanie wiersza kodu jest procesem wymagającym odczytywania tokenów utworzonych podczas procesu tokenizacji. Każdy token posiada szczególne znaczenie, które powoduje, iż BASIC wykonuje ciąg określonych operacji. BASIC pobiera z pamięci programu token za tokenem i kolejno przetwarza je. Token jest indeksem w tablicy skoków do procedur, tak więc token rozkazu PRINT wskazuje pośrednio procedurę przetwarzającą ten rozkaz. Gdy to przetwarzanie się zakończy, BASIC wraca, aby pobrać następny token. Wskaźnik używany do pobierania kolejnych tokenów nazywa się STMCUR i znajduje się pod adresem $8A i $8B.

Pierwszym wierszem kodu wykonywanym w programie jest wiersz trybu natychmiastowego. Zwykle zawiera on rozkaz RUN lub GOTO. W przypadku RUN BASIC przechodzi do pierwszego wiersza tokenów z tablicy pleceń (program tokenowy) i przetwarza go. Jeśli cały kod programu znajduje się w kolejnych wierszach, to BASIC po prostu przetwarza te wiersze po kolei.

Jeśli zostanie napotkany rozkaz GOTO, to musi być znaleziony wiersz, do którego należy przejść. Tablica poleceń zawiera połączoną listę stokenizowanych wierszy BASIC'a. Wiersze te są przechowywane w porządku rosnącym ich numerów. Aby znaleźć jakiś wiersz w środku tej tablicy, BASIC rozpoczyna od znalezienia pierwszego wiersza programu.

Adres pierwszego wiersza jest zawarty we wskaźniku STMTAB pod adresem $88 i $89. Adres ten zostaje teraz umieszczony w tymczasowym wskaźniku. Pierwsze dwa bajty pierwszego wiersza są jego numerem, który zostaje porównany z żądanym numerem wiersza. Jeśli ten pierwszy numer jest mniejszy, to BASIC przechodzi do następnego wiersza przez dodanie trzeciego bajtu pierwszego wiersza do tymczasowego wskaźnika, który teraz zacznie wskazywać drugi wiersz programu. Ponownie dwa pierwsze bajty tego nowego wiersza są porównywane z pożądanym numerem wiersza, a jeśli są mniejsze, trzeci bajt zostaje dodany do wskaźnika i proces się powtarza. Jeśli numer wiersza pasuje, to zawartość tymczasowego wskaźnika jest przenoszona do STMCUR i BASIC pobiera następny token z nowego wiersza. Jeśli pożądany numer wiersza nie zostanie znaleziony, generowany jest błąd ERROR 12.

GOSUB wymaga więcej przetwarzania niż GOTO. Procedura znajdowania wiersza jest taka sama, lecz zanim BASIC przejdzie do tego wiersza, tworzy element na stosie czasu wykonywania (ang. Run Time Stack). Przydziela on cztery bajty na końcu stosu i umieszcza 0 w pierwszym bajcie, aby oznaczyć na stosie element  GOSUB. Następnie umieszcza w kolejnych dwóch bajtach numer wiersza, który wykonywał, gdy napotkał rozkaz GOSUB. Ostatni bajt zawiera offset (przesunięcie) w bajtach od początku tego wiersza do miejsca, w którym znaleziono token GOSUB. Wtedy BASIC wykonuje wiersz, który wyszukał. Gdy zostanie napotkany rozkaz RETURN, element na stosie jest pobierany, a BASIC wraca do wiersza wywołującego.

Rozkaz FOR powoduje, iż BASIC rezerwuje na stosie czasu wykonania 16 bajtów. Pierwsze sześć są granicą w formacie 6-bajtowym BCD, którą może osiągnąć zmienna licznikowa. Drugie sześć bajtów zawiera wartość kroku w tym samym formacie. Po nich BASIC umieszcza numer zmiennej licznikowej z ustawionym najstarszym bitem. A na koniec umieszcza bieżący numer wiersza (dwa bajty) oraz offset w tym wierszu. Wtedy wykonywana jest reszta poleceń tego wiersza.

Gdy BASIC znajdzie rozkaz NEXT, patrzy na ostatni element na stosie. Upewnia się, iż zmienna przy NEXT jest tą samą zmienną na stosie, modyfikuje odpowiednio licznik pętli, a następnie sprawdza, czy licznik pętli osiągnął lub przekroczył granicę. Jeśli nie, to BASIC wraca do wiersza z instrukcją FOR i kontynuuje wykonanie. Jeśli granica została osiągnięta, to element FOR jest usuwany ze stosu i wykonanie jest kontynuowane od tego miejsca.

Gdy jest obliczana wartość wyrażenia, to operatory są umieszczane na stosie operatorów, po czym są kolejno pobierane ze stosu i obliczane. Kolejność umieszczania operatorów na stosie może być albo implikowana, w którym to przypadku BASIC przeszukuje tablicę kolejności operatorów umieszczoną w ROM lub kolejność może być bezpośrednio określona przez położenie nawiasów.

Naciśnięcie w dowolnej chwili klawisza BREAK powoduje, iż system operacyjny ustawia pewien znacznik, aby oznaczyć to zdarzenie. BASIC sprawdza ten znacznik po przetworzeniu każdego tokenu. Jeśli odkryje, że znacznik ten jest ustawiony, to zapamiętuje numer wiersza, w którym się to zdarzyło, wypisuje wiadomość "STOPPED AT LINE XXXX" (ZATRZYMANE W WIERSZU XXXX), zeruje znacznik BREAK i czeka na wprowadzenie danych przez użytkownika. W tym miejscu użytkownik może wpisać CONT i wykonanie programu zostanie wznowione od następnego wiersza.


do podrozdziału  do strony 

Współdziałanie z systemem

BASIC komunikuje się z systemem operacyjnym głównie poprzez wykorzystywanie wywołań we/wy do Centralnej Procedury We/Wy (ang. Central I/O Utility, CIO). Poniżej znajduje się lista wywołań użytkownika w języku BASIC wraz z odpowiadającymi im ustawieniami Bloku Sterującego We/Wy (ang. Input/Output Control Block, IOCB).
BASIC System Operacyjny
OPEN #1,12,0,"E:" IOCB=1
Rozkaz=3 (otwarcie)
Aux1=12 (we/wy)
Aux2=0
Adres bufora=ADR("E:")
GET #1,X IOCB=1
Rozkaz=7 (Pobranie znaków)
Długość Bufora=0
Znak zwracany w akumulatorze
PUT #1,X IOCB=1
Rozkaz=11 (Zapis znaków)
Długość Bufora=0
Znak wyprowadzany poprzez akumulator
INPUT #1,A$ IOCB=1
Rozkaz=5 (Pobranie rekordu)
Długość Bufora=Długość A$ (nie więcej niż 120)
Adres Bufora=Bufor wejściowy wejścia
PRINT #1,A$ IOCB=1
BASIC używa specjalnego wektora zapisu znaku w IOCB, aby porozumiewać się bezpośrednio ze sterownikiem.
XIO 18,#6,12,0,"S:" IOCB=6
Rozkaz=18 (Specjalny – Wypełnianie)
Aux1=12
Aux2=0

ZAPIS/ODCZYT (ang. SAVE/LOAD): Gdy program tokenowy jest zapisywany na jakimś urządzeniu, zapisane zostają dwa bloki informacji. Pierwszy blok składa się z siedmiu z dziewięciu wskaźników na stronie zerowej, które BASIC używa to zarządzania plikiem tokenowym. Są to wskaźniki od LOMEM ($80,$81) do STARP ($8C,$8D). We wskaźnikach tych dokonywana jest jedna zmiana przy ich zapisie: wartość LOMEM zostaje odjęta od każdego z tych 2-bajtowych wskaźników i te ich nowe wartości zostają zapisane na danym urządzeniu. W ten sposób pierwsze dwa zapisywane bajty będą miały wartość 0,0.

Drugi zapisywany blok informacji składa się z następujących sekcji pliku tokenowego: (1) tablica nazw zmiennych, (2) tablica wartości zmiennych, (3) program tokenowy i (4) wiersz trybu natychmiastowego.

Gdy ten program jest ładowany do pamięci, BASIC patrzy na zmienną systemową MEMLO ($2E7,$2E8) i dodaje jej wartość do każdego z 2-bajtowych wskaźników ze strony zerowej, gdy są one odczytywane z urządzenia. Wskaźniki te są umieszczane z powrotem na stronie zerowej, a wtedy wartości RUNSTK ($8E,$8F) i MEMTOP ($90,$91) zostają ustawione na wartość w STARP.

Następnie w pamięci jest rezerwowane 256 bajtów ponad wartością MEMLO, aby przydzielić miejsce dla wyjściowego bufora tokenowego. Teraz zostaje wczytana informacja pliku tokenowego od obszaru tablicy nazw zmiennych do wiersza trybu natychmiastowego. Dane te są umieszczane w pamięci bezpośrednio za wyjściowym buforem tokenowym .

Rys. 10-2 Wskaźniki systemu operacyjnego i BASIC'a (bez DOS'u)


do podrozdziału  do strony 

Poprawa wydajności programu

Wydajność programu można poprawić na dwa sposoby. Po pierwsze czas wykonania może zostać zmniejszony (program będzie działał szybciej) a po drugie można zmniejszyć jego rozmiar, co pozwoli zużyć mniej pamięci RAM. Aby osiągnąć te dwa cele, można skorzystać ze wskazówek na poniższych listach. Metody ulepszeń na każdej z list zostały głównie uporządkowane w kolejności coraz mniejszej efektywności. Stąd metody na wierzchu listy będą wywierały większy efekt od tych na spodzie.

Przyspieszanie programu w języku BASIC

  1. Zakoduj ponownie – Ponieważ BASIC w ATARI nie jest językiem strukturalnym, zapisany w nim kod jest skłonny do nieefektywności. Po wielu poprawkach sytuacja się nawet pogarsza. Dlatego warto spędzić czas na ponownym ułożeniu kodu.
  2. Sprawdź logikę algorytmu – Upewnij się, iż kod wykonujący jakiś proces jest tak efektywny, jak to jest możliwe.
  3. Umieść często wywoływane podprogramy i pętle FOR/NEXT na początku programu – BASIC rozpoczyna szukanie wiersza o danym numerze na początku programu, zatem odwołania do wierszy leżących na końcu zajmą więcej czasu na ich odnalezienie.
  4. Dla często wykonywanych operacji w pętli używaj raczej kodu w tej pętli, a nie podprogramów – Szybkość programu można poprawić tutaj, ponieważ BASIC spędza czas na dodawaniu i usuwaniu elementów ze stosu czasu wykonania.
  5. Ustaw najczęściej zmieniającą się pętle zagnieżdżonego zbioru jako najgłębszą – W ten sposób stos czasu wykonania będzie zmieniany najmniejszą liczbę razy.
  6. Uprościj rachunki zmiennoprzecinkowe wewnątrz pętli – Jeśli wynik jest osiągany za pomocą mnożenia stałej przez jakiś licznik, czas można zaoszczędzić przez zmianę tej operacji na dodawanie stałej.
  7. Utwórz pętle jako wielokrotne polecenia w jednym wierszu – W ten sposób interpreter języka BASIC nie będzie musiał przechodzić do następnego wiersza przy kontynuacji pętli.
  8. Wyłącz wyświetlanie obrazu na ekranie – Jeśli informacja ekranowa nie jest istotna przez pewien okres czasu, to można zaoszczędzić do 30 procent czasu przez POKE 559,0.
  9. Użyj szybszego trybu graficznego lub krótszej listy wyświetlania – Jeśli nie jest konieczny pełny obraz, to można zaoszczędzić do 25% czasu.
  10. Użyj kodu w języku asemblera – Czas można zaoszczędzić przez zakodowanie pętli w asemblerze i użycie funkcji USR.

Zaoszczędzanie miejsca w programie w języku BASIC

  1. Ponownie zakoduj program – jak wspomniano poprzednio, przebudowa programu uczyni go bardziej efektywnym. Zaoszczędzi również miejsce.
  2. Usuń komentarze – komentarze są przechowywane jako dane ATASCII i jedynie zajmują miejsce w programie.
  3. Zastąp stałą używaną trzy lub więcej razy zmienną. BASIC rezerwuje na każdą stałą siedem bajtów, natomiast na odwołanie do zmiennej tylko jeden bajt, zatem sześć bajtów zostaje zaoszczędzone przy każdym zastąpieniu stałej zmienną, której przypisano wartość tej stałej.
  4. Inicjuj zmienne rozkazem READ – parametry rozkazu DATA są przechowywane jako kod ATASCII, po jednym bajcie na znak, natomiast polecenie przypisania wymaga siedmiu bajtów na jedną stałą.
  5. Staraj się zastępować liczby używane wielokrotnie przez działania na zdefiniowanych wcześniej zmiennych – na przykład Z1 przypisano 1, Z2 przypisano 2, a jeśli jest potrzebne 3, to zamień je wyrażeniem Z1 + Z2.
  6. Ustaw często używane numery wierszy (w GOSUB i GOTO) w predefiniowanych zmiennych – jeśli wiersz 100 jest wywoływany 50 razy, to można zaoszczędzić w przybliżeniu 300 bajtów przez przypisanie zmiennej Z100 wartości 100 i użycie jej w wywołaniach.
  7. Utrzymuj minimalną liczbę zmiennych – każda nowa zmienna wymaga 8 bajtów więcej w tablicy wartości zmiennych plus kilka bajtów na jej nazwę.
  8. Wyczyść tablicę wartości i nazw zmiennych – elementy zmiennych nie są usuwane z tablic wartości i nazw nawet po usunięciu z programu wszystkich odwołań do tych zmiennych. Aby je usunąć wylistuj program rozkazem LIST na dysk lub kasetę, wpisz NEW, a następnie wczytaj program rozkazem ENTER.
  9. Utrzymuj nazwy zmiennych tak krótkie jak to możliwe – każda nazwa zmiennej jest umieszczana w tablicy nazw jako informacja ATASCII. Im krótsze są te nazwy, tym krótsza jest ta tablica.
  10. Zamień powtarzający się tekst łańcuchem – na ekranach z dużą ilością tekstu można zaoszczędzić miejsce przez przypisanie łańcuchowi często używany zbiór znaków.
  11. Inicjuj łańcuchy poleceniami przypisania – Przypisanie łańcucha z danymi w cudzysłowach wymaga mniej miejsca od rozkazu READ i funkcji CHR$.
  12. Połącz wiersze w wielokrotne polecenia – trzy bajty można zaoszczędzić za każdym razem, gdy dwa wiersze zostaną zastąpione jednym wierszem z dwoma poleceniami.
  13. Zastępuj jednokrotnie wywoływane podprogramy kodem bezpośrednim – rozkazy GOSUB i RETURN marnują bajty, jeśli są używane tylko jeden raz.
  14. Zastępuj tablice liczbowe łańcuchami, jeśli wartości danych nie przekraczają 255 – elementy tablicy liczbowej wymagają sześć bajtów każdy, natomiast elementy łańcucha potrzebują tylko jednego.
  15. Zastępuj polecenia SETCOLOR rozkazami POKE – zaoszczędzi to 8 bajtów.
  16. Zamiast poleceń POSITION używaj znaków kontrolnych kursora. Polecenie POSITION wymaga 15 na parametry X,Y, natomiast znaki edycyjne kursora są pojedynczymi bajtami.
  17. Usuwaj wierszy kodu poprzez program – zobacz do następnego podrozdziału.
  18. Modyfikuj wskaźnik łańcucha/tablicy, aby załadować predefiniowane dane – przez zmianę wartości w STARP można zaoszczędzić informacji o łańcuchu i tablicy.
  19. Małe procedury asemblerowe mogą być umieszczane w wywołaniach USR – na przykład X=USR(ADR("hhh*LVd"),16).
  20. Łącz programy – przykładem może być procedura inicjalizacyjna, która zostaje uruchomiona najpierw, po czym ładuje ona program główny.

do podrozdziału  do strony 

Zaawansowane techniki programowania

Zrozumienie podstawowych zasad działania interpretera ATARI BASIC stwarza możliwości pisania pewnych interesujących aplikacji. Mogą to być operacje ściśle wykonywane przez BASIC lub mogą one wykorzystywać cechy systemu operacyjnego.

Przykład nr 1 – Inicjalizacja łańcucha – Ten program ustawi wszystkie bajty łańcucha dowolnej długości na tę samą wartość. BASIC kopiuje pierwszy bajt łańcucha źródłowego do pierwszego bajtu łańcucha docelowego itd. Przez ustawienie łańcucha docelowego jako łańcuch rozpoczynający się od drugiego bajtu łańcucha źródłowego ten sam znak zostanie umieszczony wzdłuż całego łańcucha.

10 REM INICJALIZACJA ŁAŃCUCHA
20 DIM A$(1000)
30 A$(1)="A":A$(1000)="A"
40 A$(2)=A$

Przykład nr 2 – Usuwanie wierszy kodu – Przez wykorzystanie pewnej cechy systemu operacyjnego program może usuwać lub modyfikować wiersze kodu wewnątrz siebie samego. Edytor ekranowy można ustawić na przyjmowanie danych z ekranu bez ich wprowadzania przez użytkownika. W ten sposób przez ustawienie najpierw ekranu, umieszczenie kursora na górze, a następnie zatrzymanie programu BASIC będzie otrzymywał rozkazy wydrukowane na ekranie.

10 REM PRZYKŁAD USUWANIA WIERSZY
20 GRAPHICS 0:POSITION 2,4
30 ? 70:? 80:? 90:? "CONT"
40 POSITION 2,0
50 POKE 842,13:STOP
60 POKE 842,12
70 REM TE WIERSZE
80 REM ZOSTANĄ
90 REM USUNIĘTE

Przykład nr 3 – Grafika graczy i pocisków w łańcuchach – W tym przykładzie pokazano pewien sposób szybkiego przenoszenia danych grafiki graczy i pocisków. Zwymiarowany łańcuch ma zmienioną wartość swojego offsetu obszaru łańcuchów/tablic, tak aby wskazywać na obszar grafiki P/M. Teraz zapis do tego łańcucha za pomocą polecenia przypisania zapisze te dane w obszarze P/M z szybkością języka asemblera.

100 REM PRZYKŁAD GRAFIKI GRACZY/POCISKÓW
110 DIM A$(512),B$(20)
120 X=X+1:READ A:IF A<>-1 THEN B$(X,X)=CHR$(A):GOTO 120
130 DATA 0,255,129,129,129,129,129,129,129,129,255,0,-1
2000 POKE 559,62:POKE 704,88
2020 I=PEEK(106)-16:POKE 54279,I
2030 POKE 53277,3:POKE 710,224
2040 VTAB=PEEK(134)+PEEK(135)*256
2050 ATAB=PEEK(140)+PEEK(141)*256
2060 OFFS=I*256+1024-ATAB
2070 HI=INT(OFFS/256):LO=OFFS-HI*256
2090 POKE VTAB+2,LO:POKE VTAB+3,HI
3000 Y=60:Z=100:V=1:H=1
4000 A$(Y,Y+11)=B$:POKE 53248,Z
4010 Y=Y+V:Z=Z+H
4020 IF Y>213 OR Y<33 THEN V=-V
4030 IF Z>206 OR Z<49 THEN H=-H
4420 GOTO 4000

do podrozdziału  do strony 

Zespół Przedmiotowy
Chemii-Fizyki-Informatyki

w I Liceum Ogólnokształcącym
im. Kazimierza Brodzińskiego
w Tarnowie
ul. Piłsudskiego 4
©2024 mgr Jerzy Wałaszek

Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.