Rozdział 2
ZAPIS PROGRAMU W PAMIĘCI
Gdyby program zapisany był w pamięci komputera w taki
sposób, jak to widać na ekranie, to zajmowałby stosunkowo duży
obszar tej pamięci. Ponadto wykonywanie tak zapisanego programu
byłoby powolne ze względu na konieczność każdorazowego
rozpoznawania instrukcji, operacji i zmiennych. Dlatego też
interpreter tłumaczy każdy wprowadzony wiersz programu na
zestaw specjalnych kodów zwanych tokenami, sprawdzając przy tym
jego poprawność składniową, a następnie zakodowany
(stokenizowany) wiersz programu umieszcza w tablicy instrukcji.
Podział obszaru zajmowanego przez program był już opisany
we wprowadzeniu. Kolejne wiersze właściwego programu są
umieszczane w pamięci według kolejności ich numerów, przy czym
wiersze wprowadzone w trybie bezpośrednim otrzymują numer 32768
($8000) i są zapisywane na końcu tablicy instrukcji.
W dalszej części tego rozdziału zostanie przedstawiona
lista tokenów i sposób ich zapisu w pamięci komputera oraz
proces tokenizacji. Proces ten jest jednakże bardzo
skomplikowany ze względu na jednoczesną kontrolę składni
instrukcji i jego zrozumienie wymaga bardzo dobrej znajomości
języka maszynowego. Z tego powodu kontrola składni będzie
przedstawiona tylko w sposób przybliżony, a bardziej
zainteresowani tym tematem Czytelnicy muszą samodzielnie
dokonać dokładnej analizy.
2.1. Tokeny Atari Basic
Podczas tokenizacji wprowadzony wiersz programu jest
zamieniany na ciąg kodów. Rządzą tym oczywiście pewne reguły,
które teraz zostaną podane. Jako przykład wykorzystamy krótki
wiersz programu następującej postaci:
10 DIM NAZWA$(20):NAZWA$="ATARI":PRINT ALFA+5
Pierwsze dwa tokeny reprezentują numer wiersza w
standardowej postaci - młodszy bajt, starszy bajt - a więc w
naszym przypadku $0A, $00. Trzeci bajt określa długość wiersza
(tu $28), a czwarty długość instrukcji. Przez długość wiersza
należy rozumieć kolejny numer tokena symbolizującego koniec
wiersza (znak RETURN - token $16). Podobnie długość instrukcji
jest przedstawiona jako numer tokena oznaczającego dwukropek
pomiędzy instrukcjami (token $14) lub koniec wiersza. Gdy w
jednym wierszu znajduje się kilka instrukcji, to każda z nich
rozpoczyna się od tokena określającego jej długość (w
przykładzie są to kolejno $10, $1C i $28). Pozostałe tokeny
zawierają właściwą treść wiersza programu.
Stałe liczbowe są przedstawione w postaci liczby
zmiennoprzecinkowej poprzedzonej kodem określającym stałą
liczbową (razem siedem bajtów). Zapis stałych tekstowych także
zaczyna się od kodu stałej tekstowej, po którym znajduje się
liczba znaków stałej, a następnie stała zapisana znakami
ATASCII. Zmienne są zapisywane przy pomocy ich numerów w
tablicy nazw zmiennych. Numery te mają ustawiony najstarszy bit
(są zwiększone o $80).
Każda instrukcja programu zaczyna się od tokena
oznaczającego komendę języka Basic. Dalej umieszczone są tokeny
stałych i zmiennych oraz tokeny operatorów i funkcji. Znaczenie
tych tokenów jest przedstawione w poniższych tabelach.
Numeracja wszystkich tokenów odpowiada ich położeniu w
tablicach nazw instrukcji STNAME i operatorów OPFN oraz w
tablicach wektorów instrukcji STVTAB i operatorów OPVTAB.
Tabela 1. Tokeny instrukcji Atari Basic
token HEX i DEC instrukcja
----------------------------
$00 0 REM
$01 1 DATA
$02 2 INPUT
$03 3 COLOR
$04 4 LIST
$05 5 ENTER
$06 6 LET
$07 7 IF
$08 8 FOR
$09 9 NEXT
$0A 10 GOTO
$0B 11 GO TO
$0C 12 GOSUB
$0D 13 TRAP
$0E 14 BYE
$0F 15 CONT
$10 16 COM
$11 17 CLOSE
$12 18 CLR
$13 19 DEG
$14 20 DIM
$15 21 END
$16 22 NEW
$17 23 OPEN
$18 24 LOAD
$19 25 SAVE
$1A 26 STATUS
$1B 27 NOTE
$1C 28 POINT
$1D 29 XIO
$1E 30 ON
$1F 31 POKE
$20 32 PRINT
$21 33 RAD
$22 34 READ
$23 35 RESTORE
$24 36 RETURN
$25 37 RUN
$26 38 STOP
$27 39 POP
$28 40 ?
$29 41 GET
$2A 42 PUT
$2B 43 GRAPHICS
$2C 44 PLOT
$2D 45 POSITION
$2E 46 DOS
$2F 47 DRAWTO
$30 48 SETCOLOR
$31 49 LOCATE
$32 50 SOUND
$33 51 LPRINT
$34 52 CSAVE
$35 53 CLOAD
$36 54 opuszczone LET
$37 55 błąd składni
Wyjaśnienia wymagają dwa ostatnie tokeny. Jeśli nie
zostanie rozpoznana prawidłowa komenda, to interpreter zakłada,
że jest to nazwa zmiennej i wpisuje token instrukcji
przypisania z opuszczonym słowem LET. W przypadku napotkania
błędu składniowego w tokenizowanym wierszu na jego początku
zapisywany jest token błędu składniowego (STNTAX ERROR), a cały
wiersz jest przepisywany do tablicy instrukcji bez tokenizacji.
Pierwszą instrukcją naszego przykładu jest DIM, której
odpowiada token $14. Taka właśnie liczba jest piątym tokenem
wiersza. Kolejna instrukcja jest podstawieniem, w którym
opuszczone zostało słowo LET - odpowiada jej token $36 (pozycja
18). Ostatnią instrukcją jest PRINT - token $20 na pozycji 30.
Po każdej z wymienionych instrukcji następuje zmienna.
Pierwsza zmienna (NAZWA$) o numerze 0 jest oznaczona tokenem
$80 (pozycje 6 i 19), zaś druga zmienna (ALFA) o numerze 1 -
tokenem $81 (pozycja 31). Ponadto w przykładzie występują dwie
stałe liczbowe: 20 (pozycje 8-14) i 5 (pozycje 33-39) oraz
stała tekstowa "ATARI" (pozycje 21-27).
Na tym przykładzie wyraźnie widać oszczędność pamięci
uzyskiwaną przez zastosowanie zmiennych zamiast stałych. Każda
zmienna zajmuje miejsce w tablicy nazw (zwykle 2-3 bajty), w
tablicy wartości zmiennych (8 bajtów) i tyle miejsc,ile razy
wystąpi w programie, przy czym raz musi mieć nadaną wartość (12
bajtów). Jeśli stała występuje w programie cztery razy, to
zajmie 4*7=28 bajtów. Po zastąpieniu jej przez stałą mamy
2+8+12+4*1=26 bajtów. Warto więc każdą stałą liczbową
występującą w programie ponad trzy razy zastępować zmienną.
Podobnie jest ze stałymi tekstowymi, lecz opłacalność takiego
zabiegu zależy od długości stałej.
Tabela 2. Tokeny operatorów i funkcji Atari Basic
token HEX i DEC operator lub funkcja
----------------------------------------
$0E 14 stała liczbowa
$0F 15 stała tekstowa
$10 16 niewykorzystany
$11 17 niewykorzystany
$12 18 , -przecinek
$13 19 $ -znak dolara
$14 20 : -koniec instrukcji
$15 21 ; -średnik
$16 22 EOL -znak RETURN
$17 23 GOTO -w ON
$18 24 GOSUB -w ON
$19 25 TO -w FOR
$1A 26 STEP -w FOR
$1B 27 THEN -w IF
$1C 28 # -znak "numer"
$1D 29 <= -+
$1E 30 <> |
$1F 31 >= | porównanie
$20 32 < | liczb
$21 33 > |
$22 34 = -+
$23 35 ^ -potęgowanie
$24 36 * -mnożenie
$25 37 + -dodawanie
$26 38 - -odejmowanie
$27 39 / -dzielenie
$28 40 NOT
$29 41 OR
$2A 42 AND
$2B 43 ( -wyr. arytmet.
$2C 44 ) -kończący
$2D 45 = -LET liczbowe
$2E 46 = -LET tekstowe
$2F 47 <= -+
$30 48 <> |
$31 49 >= | porównanie
$32 50 < | tekstów
$33 51 > |
$34 52 = -+
$35 53 + -znak liczby
$36 54 - -znak liczby
$37 55 ( -zm. tekstowa
$38 56 ( -zm. tablicowa
$39 57 ( -wymiar zm. tabl.
$3A 58 ( -funkcja
$3B 59 ( -wymiar zm. tekst.
$3C 60 , -w zmiennej tablic.
$3D 61 STR$
$3E 62 CHR$
$3F 63 USR
$40 64 ASC
$41 65 VAL
$42 66 LEN
$43 67 ADR
$44 68 ATN
$45 69 COS
$46 70 PEEK
$47 71 SIN
$48 72 RND
$49 73 FRE
$4A 74 EXP
$4B 75 LOG
$4C 76 CLOG
$4D 77 SQR
$4E 78 SGN
$4F 79 ABS
$50 80 INT
$51 81 PADDLE
$52 82 STICK
$53 83 PTRIG
$54 84 STRIG
W tabeli tokenów operatorów zwraca uwagę duża liczba
różnych tokenów dla nawiasów oraz oddzielenie identycznych
operatorów wyrażeń tekstowych i liczbowych. Pomimo identycznego
wyglądu wykonywane przez nie funkcje różnią się znacznie i
zróżnicowanie tokenów powoduje następnie wybór różnych procedur
wykonawczych przy realizacji programu.
Wygląd stokenizowanego przykładowego wiersza programu jest
przedstawiony na następnej stronie.
pozycja token znaczenie
--------------------------------------
1 $0A ; numer wiersza
2 $00 ; $000A = 10
3 $28 długość wiersza (40)
4 $10 dł. instrukcji (16)
5 $14 instrukcja DIM
6 $80 zmienna numer 0
7 $3B ( -nawias wymiaru
8 $0E stała liczbowa
9 $40 |
10 $20 |
11 $00 | liczba 20
12 $00 | w kodzie BCD
13 $00 |
14 $00 |
15 $2C ) -nawias kończący
16 $14 : -koniec instrukcji
17 $1C dł. instrukcji (28)
18 $36 opuszczone LET
19 $80 zmienna numer 0
20 $2E = -LET tekstowe
21 $0F stała tekstowa
22 $05 długość stałej
23 $41 A |
24 $54 T |
25 $41 A | kody ATASCII
26 $52 R |
27 $49 I |
28 $14 : -koniec instrukcji
29 $28 dł. instrukcji (40)
30 $20 instrukcja PRINT
31 $81 zmienna numer 1
32 $25 + -dodawanie
33 $0E stała liczbowa
34 $40 |
35 $05 |
36 $00 | liczba 5
37 $00 | w kodzie BCD
38 $00 |
39 $00 |
40 $16 EOL - koniec wiersza
2.2. Proces tokenizacji
Tokenizacja wiersza programu jest nieodłącznie związana z
procesem kontroli jego składni. Czynności te są wykonywane
przez procedurę SYNTAX, która jest największym elementem
interpretera.
< SYNTAX >
|
+------------------+
| pobranie rekordu |<----------------+
+------------------+ |
| |
v |
/---------------------\ tak +-------------------+
< tylko numer wiersza ? >---->| usunięcie wiersza |
\---------------------/ +-------------------+
| nie
v
+------------------------+
| rozpoznanie instrukcji |
+->| i kontrola składni |
| +------------------------+
| |
| v
| /----------\ nie +---------------------+
| < poprawna ? >---->| przepisanie wiersza |
| \----------/ +---------------------+
| | tak |
| v |
| nie /----------------\ |
+<----< koniec wiersza ? ><------------+
\----------------/
| tak
v
+-------------------------+
| zapis wiersza w pamięci |
+-------------------------+
|
v
/------\ tak +----------------------+
< błąd ? >---->| wyświetlenie wiersza |
\------/ +----------------------+
| nie |
v |
/------------------\ nie v
< tryb bezpośredni ? >---->----->+
\------------------/ |
| tak |
v v
+----------------------+ < SYNTAX >
| wykonanie instrukcji |
+----------------------+
Rys.2. Struktura procedury SYNTAX
0100 ;check SYNTAX 0660 STA COX
0110 ; 0670 STA SXFLG
0120 AUXBR = $AF 0680 STA TMPIX
0130 BHLD1 = $B0 0690 STA BHLD1
0140 BHLD2 = $B1 0700 STA BHLD2
0150 BUFIX = $9F 0710 LDA VNTD
0160 CDST = $A000 0720 STA MAXLN
0170 CIX = $F2 0730 LDA VNTD+1
0180 COX = $94 0740 STA MAXLN+1
0190 CSTAD = $97 0750 JSR INBSS
0200 DELEL = $A8F7 0760 JSR PLINEN
0210 FNDCST = $A9A2 0770 JSR SAVTKN
0220 FR0 = $D4 0780 LDA FR0+1
0230 GETREC = $BDED 0790 BPL DCD
0240 GIRQST = $A9F2 0800 STA SXFLG
0250 GLNLEN = $A9DC 0810 DCD JSR INBSS
0260 INBSS = $DBA1 0820 LDY CIX
0270 INBUFP = $F3 0830 STY INIX
0280 INIX = $A8 0840 LDA (INBUFP),Y
0290 INSEL = $A87A 0850 CMP #$9B
0300 LOADFLG = $CA 0860 BNE DCDSTM
0310 LOMEM = $80 0870 BIT SXFLG
0320 LSTPLN = $B58E 0880 BMI SYNTAX
0330 MAXLN = $AD 0890 JMP DELPLN
0340 NXTSTM = $A9D0 0900 ;
0350 OUTIX = $A7 0910 DeCoDe STateMent
0360 PLINEN = $A19A 0920 ;
0370 PRCSTM = $A95E 0930 DCDSTM LDA COX
0380 PROMPT = $C2 0940 STA OUTIX
0390 PRTPLN = $B5AA 0950 JSR SAVTKN
0400 RECNAM = $A454 0960 JSR INBSS
0410 SAVTKN = $A2C4 0970 LDA # >STNAME
0420 STBV = $DA51 0980 LDY # <STNAME
0430 STMCUR = $8A 0990 LDX #$02
0440 STMSX = $A1BE 1000 JSR RECNAM
0450 STMTAB = $88 1010 STX CIX
0460 STNAME = $A49F 1020 LDA AUXBR
0470 SXFLG = $A6 1030 JSR SAVTKN
0480 TMPIX = $B3 1040 JSR INBSS
0490 VNTD = $84 1050 JSR STMSX
0500 ; 1060 BCC SAVL
0510 *= $A060 1070 LDY BUFIX
0520 ; 1080 LDA (INBUFP),Y
0530 SYNTAX LDA LOADFLG 1090 CMP #$9B
0540 BNE CDST 1100 BNE MCH
0550 LDX #$FF 1110 INY
0560 TXS 1120 STA (INBUFP),Y
0570 JSR STBV 1130 DEY
0580 LDA #$5D 1140 LDA #$20
0590 STA PROMPT 1150 MCH ORA #$80
0600 JSR GETREC 1160 STA (INBUFP),Y
0610 JSR GIRQST 1170 LDA #$40
0620 BEQ SYNTAX 1180 ORA SXFLG
0630 LDA #$00 1190 STA SXFLG
0640 STA CIX 1200 LDY INIX
0650 STA BUFIX 1210 STY CIX
1220 LDX #$03 1670 JSR NXTSTM
1230 STX OUTIX 1680 LDX #STMCUR
1240 INX 1690 JSR DELEL+1
1250 STX COX 1700 STORE LDY COX
1260 LDA #$37 1710 LP2 DEY
1270 LP1 JSR SAVTKN 1720 LDA (LOMEM),Y
1280 ; 1730 STA (STMCUR),Y
1290 ;MOVe ToKeNs 1740 TYA
1300 ; 1750 BNE LP2
1310 MOVTKN LDY CIX 1760 BIT SXFLG
1320 LDA (INBUFP),Y 1770 BVC EXIT
1330 INC CIX 1780 LDA BHLD2
1340 CMP #$9B 1790 ASL A
1350 BNE LP1 1800 ASL A
1360 JSR SAVTKN 1810 ASL A
1370 SAVL LDA COX 1820 LDX #STMTAB
1380 LDY OUTIX 1830 JSR DELEL
1390 STA (LOMEM),Y 1840 SEC
1400 LDY CIX 1850 LDA VNTD
1410 DEY 1860 SBC MAXLN
1420 LDA (INBUFP),Y 1870 TAY
1430 CMP #$9B 1880 LDA VNTD+1
1440 BNE DCDSTM 1890 SBC MAXLN+1
1450 LDY #$02 1900 LDX #VNTD
1460 LDA COX 1910 JSR DELEL+3
1470 STA (LOMEM),Y 1920 BIT SXFLG
1480 JSR FNDCST 1930 BPL LIST
1490 LDA #$00 1940 JSR PRTPLN
1500 BCS LEN 1950 JMP SYNTAX
1510 JSR GLNLEN 1960 LIST JSR LSTPLN
1520 LEN SEC 1970 LOOP JMP SYNTAX
1530 SBC COX 1980 EXIT BPL LOOP
1540 BEQ STORE 1990 JMP PRCSTM
1550 BCS SML 2000 ;
1560 EOR #$FF 2010 ;DELete Program LiNe
1570 TAY 2020 ;
1580 INY 2030 DELPLN JSR FNDCST
1590 LDX #STMCUR 2040 BCS LOOP
1600 JSR INSEL 2050 JSR GLNLEN
1610 LDA CSTAD 2060 TAY
1620 STA STMCUR 2070 JSR NXTSTM
1630 LDA CSTAD+1 2080 LDX #STMCUR
1640 STA STMCUR+1 2090 JSR DELEL+1
1650 BNE STORE 2100 JMP SYNTAX
1660 SML TAY
SYNTAX rozpoczyna się od wywołania procedury STBV (zob.
"Mapa pamięci Atari XL/XE. Podstawowe procedury systemu
operacyjnego"), która wpisuje adres bufora LBUFF (Line BUFFer)
do rejestru INBUFF (INput BUFfer Pointer). Następnie przez
wywołanie procedury GETREC (jest ona częścią PUTRET - zob.
rozdział 6.6.10) odczytywany jest z edytora wiersz programu.
Przed przystąpieniem do jego analizy wywoływana jest jeszcze
procedura GIRQST. Służy ona do sprawdzenia, czy został
naciśnięty klawisz BREAK. Jeżeli tak, to następuje skok do
początku procedury SYNTAX, a w przeciwnym wypadku zerowane są
niezbędne rejestry (CIX, COX, BUFIX, TMPIX, SXFLG, BHLD1 i
BHLD2), a wektor VNTD jest przepisywany do MAXLN. Dopiero po
tym wstępie rozpoczyna się kontrola składni wprowadzonego
wiersza i jego tokenizacja.
0100 ;Get IRQ STatus
0110 ;
0120 IRQSTAT = $11
0130 ;
0140 *= $A9F2
0150 ;
0160 LDY IRQSTAT
0170 BNE END
0180 DEC IRQSTAT
0190 TYA
0200 END RTS
Podczas tych operacji często są wywoływane procedury INBSS
i SAVTKN. Pierwsza z nich znajduje się w pakiecie procedur
zmiennoprzecinkowych i ustawia licznik bufora CIX (Current
Input indeX) na następnym znaku innym niż spacja. Druga
zapisuje przekazany jej w akumulatorze token na odpowiedniej
pozycji bufora tokenizacji. Pozycja ta jest wskazywana przez
licznik COX (Current Output indeX), a przekroczenie wartości
równej $FF powoduje sygnalizację nadmiernej długości wiersza
(błąd Line Too LonG ERror). Warto zauważyć, że dla
zaoszczędzenia bajtu pamięci SAVTKN korzysta z rozkazu RTS
umieszczonego w poprzedzającej ją procedurze.
0100 ;SAVe ToKeN
0110 ;
0120 COX = $94
0130 LOMEM = $80
0140 LTLGER = $B918
0150 ;
0160 *= $A2C4
0170 ;
0180 LDY COX
0190 STA (LOMEM),Y
0200 INC COX
0210 BNE $A2C3 ;RTS
0220 JMP LTLGER
Jako pierwszy rozpoznawany jest numer wprowadzonego
wiersza. Wykonuje to procedura PLINEN. Wywołuje ona najpierw
procedurę AFP (pakiet FP), która zamienia ciąg znaków ASCII na
liczbę. Jeśli jest to niemożliwe (znaki ASCII nie są cyframi),
to licznik CIX jest zerowany, a wiersz otrzymuje numer $8000,
co oznacza tryb bezpośredni. Po rozpoznaniu liczby wywoływana
jest krótka procedura GETINT zamieniająca otrzymany wynik na
dwubajtową liczbę całkowitą. Gdy wartość tej liczby przekracza
$8000, to także jest redukowana do tej wartości. Otrzymany w
rezultacie numer wiersza jest umieszczany w rejestrze CLNN
(Current LiNe Number) oraz przez dwukrotne wywołanie SAVTKN
przepisywany do bufora tokenizacji.
0100 ;Program LINE Number
0110 ;
0120 AFP = $D800
0130 CIX = $F2
0140 CLNN = $A0
0150 FR0 = $D4
0160 GETINT = $AD41
0170 SAVTKN = $A2C4
0180 ;
0190 *= $A19A
0200 ;
0210 JSR AFP
0220 BCC LIN
0230 DIR LDA #$00
0240 STA CIX
0250 LDY #$80
0260 BMI STR
0270 LIN JSR GETINT
0280 LDY FR0+1
0290 BMI DIR
0300 LDA FR0
0310 STR STY CLNN+1
0320 STA CLNN
0330 JSR SAVTKN
0340 LDA CLNN+1
0350 STA FR0+1
0360 JMP SAVTKN
Wspomniana wcześniej procedura GETINT została wprowadzona,
aby umożliwić właściwą sygnalizację błędu. Wywołuje ona
procedurę FPI z pakietu FP i, w przypadku niepoprawnego wyniku,
sygnalizuje błędną wartość przez skok do BVALER (Bad VALue
ERror).
0100 ;GET INTeger value
0110 ;
0120 BVALER = $B92E
0130 FPI = $D9D2
0140 ;
0150 *= $AD41
0160 ;
0170 JSR FPI
0180 BCS ERR
0190 RTS
0200 ERR JSR BVALER
Kolejne wywołanie procedury SAVTKN ma na celu zwiększenie
licznika COX, aby pozostawić miejsce na token określający
długość wprowadzonego wiersza. Jeśli wiersz ten jest w trybie
bezpośrednim, to znacznik SXFLG (SyntaX FLaG) otrzymuje wartość
$80. Teraz sprawdzany jest kod następnego znaku. Jeśli jest to
koniec wiersza ($9B) i wiersz jest w trybie bezpośrednim, to
następuje powrót do początku procedury SYNTAX. Gdy wiersz
zawiera tylko poprawny numer, następuje skok do etykiety
DELPLN, gdzie wykonywane jest usunięcie wiersza o podanym
numerze z tablicy instrukcji (zob. 2.2.1. Usuwanie wiersza).
W przypadku nierozpoznania końca wiersza aktualna zawartość
licznika COX jest przepisywana do rejestru OUTIX (OUTput IndeX)
i przez wywołanie SAVTKN rezerwowane jest miejsce na token
długości instrukcji. Kolejnym etapem jest przeszukiwanie
tablicy nazw instrukcji przez procedurę RECNAM. Tablica nazw
STNAME - oprócz samych nazw - zawiera wektory wskazujące
położenie w tablicy składni danych określających wymaganą
składnię instrukcji.
0100 ;STatement NAME table 0570 .CBYTE "LET"
0110 ; 0580 .WORD SIF-1
0120 SBYE = $A6B9 0590 .CBYTE "IF"
0130 SCOLOR = $A6B8 0600 .WORD SFOR-1
0140 SCLOSE = $A71B 0610 .CBYTE "FOR"
0150 SDATA = $A7C6 0620 .WORD SNEXT-1
0160 SDIM = $A75A 0630 .CBYTE "NEXT"
0170 SENTER = $A71E 0640 .WORD SCOLOR-1
0180 SFOR = $A6CD 0650 .CBYTE "GOTO"
0190 SGET = $A6E3 0660 .WORD SCOLOR-1
0200 SIF = $A78F 0670 .CBYTE "GO TO"
0210 SINPUT = $A6EF 0680 .WORD SCOLOR-1
0220 SLET = $A6BB 0690 .CBYTE "GOSUB"
0230 SLIST = $A72D 0700 .WORD SCOLOR-1
0240 SLOCAT = $A6DD 0710 .CBYTE "TRAP"
0250 SLPRNT = $A6FB 0720 .WORD SBYE-1
0260 SNEXT = $A6E5 0730 .CBYTE "BYE"
0270 SNOTE = $A744 0740 .WORD SBYE-1
0280 SON = $A75D 0750 .CBYTE "CONT"
0290 SOPEN = $A714 0760 .WORD SDIM-1
0300 SPOKE = $A757 0770 .CBYTE "COM"
0310 SPRINT = $A6F7 0780 .WORD SCLOSE-1
0320 SPUT = $A6B5 0790 .CBYTE "CLOSE"
0330 SREAD = $A6F0 0800 .WORD SBYE-1
0340 SREM = $A7C3 0810 .CBYTE "CLR"
0350 SRSTR = $A6EA 0820 .WORD SBYE-1
0360 SRUN = $A721 0830 .CBYTE "DEG"
0370 SSETC = $A755 0840 .WORD SDIM-1
0380 SSOUND = $A753 0850 .CBYTE "DIM"
0390 SSTAT = $A73B 0860 .WORD SBYE-1
0400 SXIO = $A712 0870 .CBYTE "END"
0410 ; 0880 .WORD SBYE-1
0420 *= $A49F 0890 .CBYTE "NEW"
0430 ; 0900 .WORD SOPEN-1
0440 .WORD SREM-1 0910 .CBYTE "OPEN"
0450 .CBYTE "REM" 0920 .WORD SENTER-1
0460 .WORD SDATA-1 0930 .CBYTE "LOAD"
0470 .CBYTE "DATA" 0940 .WORD SENTER-1
0480 .WORD SINPUT-1 0950 .CBYTE "SAVE"
0490 .CBYTE "INPUT" 0960 .WORD SSTAT-1
0500 .WORD SCOLOR-1 0970 .CBYTE "STATUS"
0510 .CBYTE "COLOR" 0980 .WORD SNOTE-1
0520 .WORD SLIST-1 0990 .CBYTE "NOTE"
0530 .CBYTE "LIST" 1000 .WORD SNOTE-1
0540 .WORD SENTER-1 1010 .CBYTE "POINT"
0550 .CBYTE "ENTER" 1020 .WORD SXIO-1
0560 .WORD SLET-1 1030 .CBYTE "XIO"
1040 .WORD SON-1 1300 .WORD SCOLOR-1
1050 .CBYTE "ON" 1310 .CBYTE "GRAPHICS"
1060 .WORD SPOKE-1 1320 .WORD SPOKE-1
1070 .CBYTE "POKE" 1330 .CBYTE "PLOT"
1080 .WORD SPRINT-1 1340 .WORD SPOKE-1
1090 .CBYTE "PRINT" 1350 .CBYTE "POSITION"
1100 .WORD SBYE-1 1360 .WORD SBYE-1
1110 .CBYTE "RAD" 1370 .CBYTE "DOS"
1120 .WORD SREAD-1 1380 .WORD SPOKE-1
1130 .CBYTE "READ" 1390 .CBYTE "DRAWTO"
1140 .WORD SRSTR-1 1400 .WORD SSETC-1
1150 .CBYTE "RESTORE" 1410 .CBYTE "SETCOLOR"
1160 .WORD SBYE-1 1420 .WORD SLOCAT-1
1170 .CBYTE "RETURN" 1430 .CBYTE "LOCATE"
1180 .WORD SRUN-1 1440 .WORD SSOUND-1
1190 .CBYTE "RUN" 1450 .CBYTE "SOUND"
1200 .WORD SBYE-1 1460 .WORD SLPRNT-1
1210 .CBYTE "STOP" 1470 .CBYTE "LPRINT"
1220 .WORD SBYE-1 1480 .WORD SBYE-1
1230 .CBYTE "POP" 1490 .CBYTE "CSAVE"
1240 .WORD SPRINT-1 1500 .WORD SBYE-1
1250 .CBYTE "?" 1510 .CBYTE "CLOAD"
1260 .WORD SGET-1 1520 .WORD SLET-1
1270 .CBYTE "GET" 1530 .CBYTE $00,$00
1280 .WORD SPUT-1 1540 .WORD $2A00
1290 .CBYTE "PUT" 1550 ERMSG .CBYTE "ERROR- "
Przed wywołaniem procedury RECNAM w akumulatorze i
rejestrze Y umieszczany jest adres przeszukiwanej tablicy.
Rejestr X zawiera natomiast indeks określający, od którego
znaku należy porównywać nazwę. Zwykle jest to zero, lecz dla
tablicy nazw instrukcji indeks jest równy 2, gdyż dwa pierwsze
bajty są wektorem do tablicy składni. Przed rozpoczęciem
przeszukiwania indeks z rejestru X jest przepisywany do
rejestru STPTR (STack PoinTeR).
Procedura RECNAM działa w pętli. Najpierw adres tablicy
jest zapisywany do rejestru POKADR (POKe ADdRess), a kolejny
numer sprawdzanej nazwy (licząc od zera) do rejestru AUXBR
(AUXiliary Basic Register). Z tablicy odczytywany jest bajt i
jeśli jest równy zero (koniec tablicy), to procedura się
kończy, a ustawiony bit Carry sygnalizuje niepowodzenie. W
przeciwnym wypadku kolejne znaki są odczytywane z bufora
wejściowego i porównywane z zawartością tablicy. Rezultat
porównania jest zapisywany na stosie. Przerwanie porównywania
znaków następuje po osiągnięciu końca nazwy w tablicy, a w
przypadku instrukcji także po odczytaniu z bufora kropki.
Koniec nazwy jest zawsze oznaczony w tablicy ustawieniem
najstarszego bitu w ostatnim znaku nazwy.
Teraz wynik porównania jest odczytywany ze stosu. Gdy jest
on negatywny, pętla się powtarza. Odnalezienie nazwy w tablicy
jest sygnalizowane przez skasowanie bitu Carry. W takim
przypadku po zakończeniu procedury RECNAM rejestr AUXBR zawiera
numer kolejny rozpoznanej nazwy, a w rejestrze X znajduje się
indeks następnego znaku w buforze wejściowym i po powrocie do
miejsca wywołania licznik CIX jest uaktualniany według tej
wartości. Przy przeszukiwaniu tablicy STNAME trzeba zwrócić
uwagę na fakt, że jeśli nie została rozpoznana poprawna nazwa
instrukcji, to rejestr AUXBR zawiera wartość $36, czyli token
instrukcji przypisania z opuszczonym słowem LET. Każda nazwa,
inna niż umieszczona w tablicy STNAME, jest więc traktowana
jako nazwa zmiennej.
0100 ;RECognize NAMe
0110 ;
0120 AUXBR = $AF
0130 CIX = $F2
0140 LBUFF = $0580
0150 POKADR = $95
0160 STPTR = $AA
0170 ;
0180 *= $A454
0190 ;
0200 RECNAM STX STPTR
0210 LDX #$FF
0220 STX AUXBR
0230 SAV STA POKADR+1
0240 STY POKADR
0250 INC AUXBR
0260 LDX CIX
0270 LDY STRPTR
0280 LDA (POKADR),Y
0290 BEQ END
0300 LDA #$00
0310 PHP
0320 RDC LDA LBUFF,X
0330 AND #$7F
0340 CMP #
0350 BEQ CST
0360 CHK EOR (POKADR),Y
0370 ASL A
0380 BEQ NXT
0390 PLA
0400 PHP
0410 NXT INY
0420 INX
0430 BCC RDC
0440 PLP
0450 BEQ $A452 ;CLC,RTS
0460 ;
0470 ;NeXT NAMe
0480 ;
0490 NXTNAM CLC
0500 TYA
0510 ADC POKADR
0520 TAY
0530 LDA POKADR+1
0540 ADC #$00
0550 BNE SAV
0560 END SEC
0570 RTS
0580 CST LDA #$02
0590 CMP STPTR
0600 BNE CHK
0610 CHR LDA (POKADR),Y
0620 BMI LST
0630 INY
0640 BNE CHR
0650 LST SEC
0660 BCS NXT
Rozkaz AND #$7F w procedurze RECNAM (wiersz 330) kasuje
najstarszy bit znaku pobranego z bufora wejściowego. Dzięki
temu - wbrew rozpowszechnionym opiniom - interpreter Atari
Basic rozpoznaje wszystkie instrukcje, funkcje i operatory
zapisane w negatywie (inverse video)! Sygnalizację błędu
powoduje jedynie wpisanie w negatywie stałych liczbowych oraz
nazw zmiennych. Przykład ten świadczy o tym, jak często
negatywne opinie o Atari są powtarzane bez sprawdzenia stanu
faktycznego.
Odczytany token instrukcji jest teraz zapisywany do bufora
tokenizacji i wywoływana jest procedura STMSX, która sprawdza
poprawność składniową tej instrukcji. Procedura STMSX jest
przedstawiona w rozdziale 2.2.2.
Jeśli został stwierdzony błąd składni, to miejsce jego
wystąpienia jest zaznaczane ustawieniem najstarszego bitu
znaku, a jako piąty token wiersza wpisywana jest wartość $37
(SYNTAX ERROR). Następnie cała zawartość bufora wejściowego
jest przepisywana do bufora tokenizacji. Dodatkowo w rejestrze
SXFLG ustawiany jest bit 6, co umożliwia późniejsze rozpoznanie
błędnego wiersza.
Po stokenizowaniu całej instrukcji aktualny stan licznika
COX jest zapisywany w miejscu wskazanym przez zawartość
rejestru OUTIX, czyli na początku instrukcji. W ten sposób
otrzymujemy token określający długość instrukcji. Jeśli
ostatnim odczytanym znakiem nie był znak końca wiersza (EOL),
to procedura się powtarza od rozpoznania nazwy następnej
instrukcji w wierszu. W przeciwnym razie stan licznika COX jest
wpisywany jako trzeci token, a więc jako długość wiersza.
Teraz stokenizowany wiersz musi być umieszczony w
odpowiednim miejscu tablicy instrukcji. Odszukanie tego miejsca
jest zadaniem procedury FNDCST. Przeszukuje ona całą tablicę
instrukcji, aż do napotkania wiersza o takim samym lub wyższym
numerze. Sygnalizuje przy tym rezultat poszukiwania przez
ustawienie bitu Carry, gdy numery są różne, lub jego
skasowanie, gdy są równe. Odpowiednio do tego w akumulatorze
jest umieszczana wartość zero lub długość wiersza odczytana
przez ponowne wywołanie procedury GLNLEN.
0100 ;FiND Current STatement
0110 ;
0120 CLNN = $A0
0130 GLNLEN = $A9DC
0140 NXTSTM = $A9D0
0150 SAVCUR = $BE
0160 STMCUR = $8A
0170 STMTAB = $88
0180 ;
0190 *= $A9A2
0200 ;
0210 LDA STMCUR
0220 STA SAVCUR
0230 LDA STMCUR+1
0240 STA SAVCUR+1
0250 LDA STMTAB+1
0260 LDY STMTAB
0270 STA STMCUR+1
0280 STY STMCUR
0290 LOOP LDY #$01
0300 LDA (STMCUR),Y
0310 CMP CLNN+1
0320 BCC NXT
0330 BNE END
0340 DEY
0350 LDA (STMCUR),Y
0360 CMP CLNN
0370 BCC NXT
0380 BNE END
0390 CLC
0400 END RTS
0410 NXT JSR GLNLEN
0420 JSR NXTSTM
0430 JMP LOOP
Przy przeszukiwaniu zawartości tablicy instrukcji są
wykorzystywane dwie krótkie procedury GLNLEN i NXTSTM. Pierwsza
z nich odczytuje token określający długość aktualnie
sprawdzanego wiersza. Druga dodaje tą długość do adresu wiersza
dając w wyniku adres następnego wiersza programu.
0100 ;Get LiNe LENgth
0110 ;
0120 STMCUR = $8A
0130 ;
0140 *= $A9DC
0150 ;
0160 LDY #$02
0170 LDA (STMCUR),Y
0180 RTS
0100 ;NeXT STateMent
0110 ;
0120 STMCUR = $8A
0130 ;
0140 *= $A9D0
0150 ;
0160 CLC
0170 ADC STMCUR
0180 STA STMCUR
0190 LDA STMCUR+1
0200 ADC #$00
0210 STA STMCUR+1
0220 RTS
Znajdująca się w akumulatorze długość wiersza jest
porównywana z długością nowego wiersza określoną przez stan
licznika COX. Zależnie od różnicy długości starego i nowego
wiersza programu wykonywana jest odpowiednia sekwencja
instrukcji. Jeśli nowy wiersz jest dłuższy, to miejsce dla
niego tworzone jest przez wywołanie procedury INSEL, a gdy
krótszy, to nadmiar miejsca kasuje procedura DELEL. Przy
równych długościach starego i nowego wiersza ten fragment jest
pomijany. Po przygotowaniu miejsca stokenizowany wiersz
programu jest przepisywany do tablicy instrukcji z bufora
tokenizacji.
Pozostaje jeszcze zakończyć procedurę SYNTAX. W celu
wybrania odpowiedniego wariantu zakończenia testowany jest
rejestr SXFLG. Gdy ma on ustawiony bit 6, to oznacza, że
wpisany wiersz jest niepoprawny. W takim przypadku przez
dwukrotne wywołanie procedury DELEL usuwane są z tablic nazw i
wartości zmiennych informacje wprowadzone tam podczas
tokenizacji tego wiersza. Następnie błędny wiersz jest
wyświetlany na ekranie przez procedurę LSTPLN (wiersz w trybie
programowym) lub PRTPLN (wiersz w trybie bezpośrednim), po czym
wykonywany jest skok do początku procedury SYNTAX.
Powyższa informacja przeczy obiegowej opinii, że
interpreter Atari Basic gromadzi w tablicach nazw i wartości
zmiennych śmieci powstałe na skutek błędów przy wpisywaniu
programu. Opinia ta jest prawdziwa TYLKO w przypadku
poprzedniej wersji interpretera (Revision B).
Jeżeli wiersz jest poprawny, to w przypadku trybu
programowego także następuje powrót do początku SYNTAX, zaś w
trybie bezpośrednim wykonywany jest skok do procedury PRCSTM,
która powoduje wykonanie wprowadzonego wiersza (zob. rozdział
2.2.3).
2.2.1. Usuwanie wiersza
Usuwanie wiersza z programu jest wykonywane przez część
procedury SYNTAX oznaczoną etykietą DELPLN. Jej działanie jest
bardzo proste. Najpierw przez wywołanie procedury FNDCST
odszukiwany jest w pamięci wiersz do usunięcia. Jeśli go nie
ma, to procedura jest przerywana skokiem do początku SYNTAX. Po
odnalezieniu wiersza odczytywana jest jego długość (przez
GLNLEN), a następnie obliczany jest adres początkowy kolejnego
wiersza (przez NXTSTM). Teraz wywoływana jest procedura DELEL,
która przesuwa znajdującą się wyżej zawartość pamięci i w ten
sposób kasuje wskazany element programu. Procedura DELPLN
kończy się skokiem do początku procedury SYNTAX.
Wielokrotnie już wymieniana procedura DELEL jest
odwrotnością procedury INSEL. Przemieszcza ona podobnie
zawartość obszaru pamięci, lecz w przeciwnym kierunku.
Stosowane są trzy sposoby wywołania procedury DELEL: od DELEL,
DELEL+1 i DELEL+3. W pierwszym i drugim przypadku następuje
przemieszczenie na odległość nie przekraczającą jednej strony
pamięci, gdyż starszy bajt wielkości przemieszczenia ma wtedy
wartość zero. Młodszy bajt jest przekazywany do procedury w
akumulatorze (przy wywołaniu od DELEL) lub w rejestrze Y (przy
wywołaniu od DELEL+1). Przemieszczenie obszaru pamięci na
odległość przekraczającą jedną stronę (256 bajtów) następuje po
wywołaniu od DELEL+3. Akumulator musi wtedy zawierać starszy
bajt wielkości przemieszczenia, a rejestr Y młodszy bajt.
0100 ;DELete program ELement
0110 ;
0120 APPMHI = $0E
0130 BMEMHI = $90
0140 LENPEL = $A4
0150 MEOLFLG = $92
0160 MRANGE = $A2
0170 NEWMHI = $9B
0180 OLDMHI = $99
0190 ;
0200 *= $A8F7
0210 ;
0220 TAY
0230 LDA #$00
0240 STY LENPEL
0250 STA LENPEL+1
0260 SEC
0270 LDA BMEMHI
0280 SBC $00,X
0290 EOR #$FF
0300 TAY
0310 INY
0320 STY MRANGE
0330 LDA BMEMHI+1
0340 SBC $01,X
0350 STA MRANGE+1
0360 LDA $00,X
0370 SBC MRANGE
0380 STA OLDMHI
0390 LDA $01,X
0400 SBC #$00
0410 STA OLDMHI+1
0420 STX NEWMHI
0430 LOOP SEC
0440 LDA $00,X
0450 SBC LENPEL
0460 STA $00,X
0470 LDA $01,X
0480 SBC LENPEL+1
0490 STA $01,X
0500 INX
0510 INX
0520 CPX #MEOLFLG
0530 BCC LOOP
0540 STA APPMHI+1
0550 LDA BMEMHI
0560 STA APPMHI
0570 LDX NEWMHI
0580 LDA $00,X
0590 SBC MRANGE
0600 STA NEWMHI
0610 LDA $01,X
0620 SBC #$00
0630 STA NEWMHI+1
0640 ;
0650 ;MOVe MEMory
0660 ;
0670 MOVMEM LDX MRANGE+1
0680 INX
0690 LDY MRANGE
0700 BNE MOVE
0710 DEX
0720 BNE MOVE
0730 RTS
0740 NEXT INC OLDMHI+1
0750 INC NEWMHI+1
0760 MOVE LDA (OLDMHI),Y
0770 STA (NEWMHI),Y
0780 INY
0790 BNE MOVE
0800 DEX
0810 BNE NEXT
0820 RTS
Szczegółowy opis działania procedury DELEL jest zbędny,
gdyż stanowi ona odwrotność INSEL i zasada działania obu
procedur jest jednakowa. Trzeba jednak zwrócić uwagę na
etykietę MOVMEM. Wywołanie procedury od tego miejsca umożliwia
przemieszczenie dowolnego obszaru pamięci w stronę niższych
adresów, a jeśli stary i nowy obszar nie pokrywają się, to
także w stronę wyższych adresów. Zamiast wymyślać do swoich
programów nowe procedury przemieszczeń warto więc wykorzystać
już istniejący fragment interpretera.
2.2.2. Kontrola składni
Po rozpoznaniu instrukcji wywoływana jest procedura STMSX,
która dokonuje kontroli składni tej instrukcji i jednocześnie
przeprowadza jej tokenizację. Przed przystąpieniem do tej
czynności wektor umieszczony w tabeli STNAME przed nazwą
instrukcji jest przepisywany do rejestrów PCNTC (Program
CouNTer Current register) i BPRCNT (Basic PRogram CouNTer).
Przepisywane są także stany liczników: COX do BOUTIX (Basic
OUTput IndeX) oraz CIX do BINIX (Basic INput IndeX).
0100 ;STateMent SyntaX 0550 LDX #$FF
0110 ; 0560 ADPC CLC
0120 BINIX = $0480 0570 ADC PCNTC
0130 BOUTIX = $0481 0580 PHA
0140 BPRCNT = $0482 0590 TXA
0150 BUFIX = $9F 0600 ADC PCNTC+1
0160 CIX = $F2 0610 PHA
0170 COX = $94 0620 JMP SAVCPM
0180 GTCCHR = $A293 0630 ;
0190 INCPRC = $A28C 0640 ;PusH ADdRess
0200 LTLGER = $B918 0650 ;
0210 PCNTC = $9D 0660 PHADR JSR STCCHR
0220 POKADR = $95 0670 PHA
0230 STIX = $A9 0680 JSR GTCCHR
0240 VALUE = $A29B 0690 PHA
0250 ; 0700 BCC SAVCPM
0260 *= $A1BE 0710 PLA
0270 ; 0720 TAY
0280 STMSX LDY #$01 0730 PLA
0290 LDA (POKADR),Y 0740 TAX
0300 STA PCNTC+1 0750 TYA
0310 STA BPRCNT+1 0760 PHA
0320 DEY 0770 TXA
0330 LDA (POKADR),Y 0780 PHA
0340 STA PCNTC 0790 EXIT RTS
0350 STA BPRCNT 0800 ;
0360 STY STIX 0810 ;SAVe Current ParaMeters
0370 LDA COX 0820 ;
0380 STA BOUTIX 0830 SAVCPM LDX STIX
0390 LDA CIX 0840 INX
0400 STA BINIX 0850 INX
0410 CHAR JSR GTCCHR 0860 INX
0420 BMI NEG 0870 INX
0430 CMP #$01 0880 BEQ ERR
0440 BCC PHADR 0890 STX STIX
0450 BNE CVL 0900 LDA CIX
0460 JSR PHADR 0910 STA BINIX,X
0470 JMP RSM 0920 LDA COX
0480 CVL CMP #$05 0930 STA BOUTIX,X
0490 BCC GPC 0940 LDA PCNTC
0500 JSR VALUE 0950 STA BPRCNT,X
0510 JMP RSM 0960 LDA PCNTC+1
0520 NEG SEC 0970 STA BPRCNT+1,X
0530 SBC #$C1 0980 PLA
0540 BCS ADPC 0990 STA PCNTC+1
1000 PLA 1190 CMP #$02
1010 STA PCNTC 1200 BCS NVL
1020 JMP CHAR 1210 JSR INCPRC
1030 ERR JMP LTLGER 1220 JSR INCPRC
1040 GPC LDX STIX 1230 BNE NXT
1050 BEQ EXIT 1240 NVL CMP #$03
1060 LDA BPRCNT,X 1250 BEQ GPC
1070 STA PCNTC 1260 BCS NXT
1080 LDA BPRCNT+1,X 1270 LDA CIX
1090 STA PCNTC+1 1280 CMP BUFIX
1100 DEX 1290 BCC GTIX
1110 DEX 1300 STA BUFIX
1120 DEX 1310 GTIX LDX STIX
1130 DEX 1320 LDA BINIX,X
1140 STX STIX 1330 STA CIX
1150 RSM BCS NXT 1340 LDA BOUTIX,X
1160 JMP CHAR 1350 STA COX
1170 NXT JSR GTCCHR 1360 JMP CHAR
1180 BMI NXT
Podczas kontroli składni z tablicy składni SXTAB są
odczytywane kolejne liczby, które oznaczają elementy składniowe
instrukcji. Odczyt ten jest wykonywany przez procedurę GTCCHR.
Najpierw zwiększa ona stan licznika PCNTC przez wywołanie
procedury INCPRC, a następnie odczytuje wskazany przezeń kod.
0100 ;GeT Current CHaRacter
0110 ;
0120 INCPRC = $A28C
0130 PCNTC = $9D
0140 ;
0150 *= $A293
0160 ;
0170 JSR INCPRC
0180 LDX #$00
0190 LDA (PCNTC,X)
0200 RTS
Procedura INCPRC jest bardzo prosta, gdyż jej zadaniem jest
jedynie zwiększenie stanu dwubajtowego licznika PCNTC. Opis
działania jest tu całkowicie zbędny.
0100 ;INCrease PRogram Counter
0110 ;
0120 PCNTC = $9D
0130 ;
0140 *= $A28C
0150 ;
0160 INC PCNTC
0170 BNE END
0180 INC PCNTC+1
0190 END RTS
Kontrola składni instrukcji jest bardzo złożonym i
skomplikowanym procesem. Postępowanie interpretera zależy od
kodu odczytanego z tabeli składni. Dla ułatwienia analizy tej
tablicy podam znaczenie poszczególnych kodów. Kod $00 oznacza
przepisanie dwóch kolejnych bajtów na stos, a następnie do
licznika PCNTC, a więc skok w inne miejsce tablicy (w wydruku
oznaczony jako jsr). Kod $01 oznacza wykonanie skoku do
procedury o adresie zawartym w dwóch następnych bajtach (jmp).
Możliwe odmiany składni są rozdzielone kodem $02 (or). Kod $03
sygnalizuje koniec fragmentu składniowego (end). Kod $0D
powoduje opuszczenie następnego znaku z bufora (skip). Kod $0E
wskazuje, że w tym miejscu wymagana jest wartość - stała lub
zmienna (value). Kod $0F nakazuje przepisanie następnego kodu
jako tokena do wprowadzanego wiersza (token). Kody większe od
$80 powodują skoki o wartość wyrażenia kod-$C1 (adresy skoków
są oznaczone przez Qnn). Pozostałe kody są kodami tokenów.
A oto cała tablica składni:
0100 ;SyntaX TABle
0110 ;
0120 MOVLIN = $A2DB
0130 NUMCNS = $A3F5
0140 NUMVAR = $A320
0150 SCDSTM = $A2CF
0160 STRCNS = $A41C
0170 STRVAR = $A324
0180 ;
0190 *= $A605
0200 ;
0210 Q01 .BYTE $CD,$C4,$02,$C2 ; Q03 Q02 or Q02
0220 .BYTE $03 ; end
0230 Q02 .BYTE $2B,$BA,$2C,$DB ; ( Q01 ) Q05
0240 .BYTE $02,$CD,$D8,$03 ; or Q04 Q05 end
0250 Q03 .BYTE $25,$0F,$35,$02 ; + token + or
0260 .BYTE $26,$0F,$36,$02 ; - token - or
0270 .BYTE $28,$03 ; NOT end
0280 Q04 .BYTE $FE,$02,$E8,$02 ; Q08 or L1 or
0290 .BYTE $01 ; jmp
0300 .WORD NUMCNS-1
0310 .BYTE $02,$00 ; or jsr
0320 .WORD L3-1
0330 .BYTE $03 ; end
0340 Q05 .BYTE $C4,$9C,$02,$03 ; Q06 Q01 or end
0350 Q06 .BYTE $23,$02,$25,$02 ; ^ or + or
0360 .BYTE $26,$02,$24,$02 ; - or * or
0370 .BYTE $27,$02,$1D,$02 ; / or <= or
0380 .BYTE $1F,$02,$1E,$02 ; >= or <> or
0390 .BYTE $20,$02,$21,$02 ; < or > or
0400 .BYTE $22,$02,$2A,$02 ; = or AND or
0410 .BYTE $29,$03 ; OR end
0420 L1 .BYTE $01 ; jmp
0430 .WORD NUMVAR-1
0440 .BYTE $C2,$03 ; Q07 end
0450 Q07 .BYTE $0D,$2B,$0F,$38 ; skip ( token ixv
0460 .BYTE $0E,$C4,$2C,$02 ; value L2 ) or
0470 .BYTE $03 ; end
0480 L2 .BYTE $12,$0F,$3C,$0E ; , token , value
0490 .BYTE $02,$03 ; or end
0500 Q08 .BYTE $44,$D2,$02,$00 ; ATN Q10 or jsr
0510 .WORD L7-1
0520 .BYTE $D3,$02,$C2,$03 ; Q11 or Q09 end
0530 Q09 .BYTE $3F,$2B,$0F,$3A ; USR ( token (
0540 .BYTE $00 ; jsr
0550 .WORD L9-1
0560 .BYTE $2C,$03 ; ) end
0570 Q10 .BYTE $2B,$0F,$3A,$0E ; ( token ( value
0580 .BYTE $2C,$03 ; ) end
0590 Q11 .BYTE $2B,$0F,$3A,$C7 ; ( token ( L4
0600 .BYTE $2C,$03 ; ) end
0610 L3 .BYTE $C4,$E3,$C2,$03 ; L4 Q16 L4 end
0620 L4 .BYTE $C8,$02,$CB,$02 ; Q12 or Q13 or
0630 .BYTE $01 ; jmp
0640 .WORD STRCNS-1
0650 .BYTE $03 ; end
0660 Q12 .BYTE $00 ; jsr
0670 .WORD L8-1
0680 .BYTE $A5,$03 ; Q10 end
0690 Q13 .BYTE $01 ; jmp
0700 .WORD STRVAR-1
0710 .BYTE $C2,$03 ; Q14 end
0720 Q14 .BYTE $2B,$0F,$37,$0E ; ( token ( value
0730 .BYTE $C4,$2C,$02,$03 ; Q15 ) or end
0740 Q15 .BYTE $12,$0F,$3C,$0E ; , token , value
0750 .BYTE $02,$03 ; or end
0760 Q16 .BYTE $1D,$0F,$2F,$02 ; <= token <= or
0770 .BYTE $1E,$0F,$30,$02 ; <> token <> or
0780 .BYTE $1F,$0F,$31,$02 ; >= token >= or
0790 .BYTE $20,$0F,$32,$02 ; < token < or
0800 .BYTE $21,$0F,$33,$02 ; > token > or
0810 .BYTE $22,$0F,$34,$03 ; = token = end
0820 SPUT .BYTE $1C,$0E,$12 ; # value ,
0830 SCOLOR .BYTE $0E ; value
0840 SBYE .BYTE $FA,$03 ; Q18 end
0850 SLET .BYTE $00 ; jsr
0860 .WORD L1-1
0870 .BYTE $22,$0F,$2D,$0E ; = token = value
0880 .BYTE $F1,$02,$86,$22 ; Q18 or Q13 =
0890 .BYTE $0F,$2E,$00 ; token = jsr
0900 .WORD L4-1
0910 .BYTE $E8,$03 ; Q18 end
0920 SFOR .BYTE $01 ; jmp
0930 .WORD NUMVAR-1
0940 .BYTE $22,$0F,$2D,$0E ; = token = value
0950 .BYTE $19,$0E,$C3,$DC ; TO value Q17 Q18
0960 .BYTE $03 ; end
0970 Q17 .BYTE $1A,$0E,$02,$03 ; STEP value or end
0980 SLOCAT .BYTE $0E,$12,$0E ; value , value
0990 .BYTE $12,$C4,$03 ; , SNEXT end
1000 SGET .BYTE $DD,$12 ; Q19 ,
1010 SNEXT .BYTE $01 ; jmp
1020 .WORD NUMVAR-1
1030 .BYTE $CB,$03 ; Q18 end
1040 SRSTR .BYTE $0E,$C8,$02,$C6 ; value Q18 or Q18
1050 .BYTE $03 ; end
1060 SINPUT .BYTE $F7 ; Q23
1070 SREAD .BYTE $DB,$C2,$03 ; Q21 Q18 end
1080 Q18 .BYTE $14,$02,$16,$03 ; : or EOL end
1090 SPRINT .BYTE $C9,$BB,$02,$EC ; Q19 Q18 or Q23
1100 SLPRNT .BYTE $00 ; jsr
1110 .WORD L5-1
1120 .BYTE $B5,$03 ; Q18 end
1130 Q19 .BYTE $1C,$0E,$03 ; # value end
1140 Q20 .BYTE $01 ; jmp
1150 .WORD NUMVAR-1
1160 .BYTE $02,$01 ; or jmp
1170 .WORD STRVAR-1
1180 .BYTE $03 ; end
1190 Q21 .BYTE $B8,$C2,$03 ; Q20 Q22 end
1200 Q22 .BYTE $12,$BC,$02,$03 ; , Q21 or end
1210 SXIO .BYTE $0E,$12 ; value ,
1220 SOPEN .BYTE $AC,$12,$F9,$12 ; Q19 , Q27 ,
1230 .BYTE $F3,$9A,$03 ; Q26 Q18 end
1240 SCLOSE .BYTE $A5,$97,$03 ; Q19 Q18 end
1250 SENTER .BYTE $ED,$94,$03 ; Q26 Q18 end
1260 SRUN .BYTE $EA,$91,$02,$8F ; Q26 Q18 or Q18
1270 .BYTE $03 ; end
1280 Q23 .BYTE $9A,$12,$02,$97 ; Q19 , or Q19
1290 .BYTE $15,$02,$03 ; ; or end
1300 SLIST .BYTE $DE,$85,$02 ; Q26 Q18 or
1310 .BYTE $DB,$12,$C4,$02 ; Q26 , Q24 or
1320 .BYTE $C2,$03 ; Q24 end
1330 Q24 .BYTE $00 ; jsr
1340 .WORD L6-1
1350 .BYTE $F4,$03 ; Q31 end
1360 SSTAT .BYTE $C3,$F1,$03 ; Q25 Q31 end
1370 Q25 .BYTE $82,$12,$00 ; Q19 , jsr
1380 .WORD L1-1
1390 .BYTE $03 ; end
1400 SNOTE .BYTE $BA,$12,$00 ; Q25 , jsr
1410 .WORD L1-1
1420 .BYTE $E4,$03 ; Q31 end
1430 Q26 .BYTE $00 ; jsr
1440 .WORD L4-1
1450 .BYTE $03 ; end
1460 Q27 .BYTE $0E,$12,$0E,$03 ; value , value end
1470 SSOUND .BYTE $0E,$12 ; value ,
1480 SSETC .BYTE $0E,$12 ; value ,
1490 SPOKE .BYTE $B8,$D5,$03 ; Q27 Q31 end
1500 SDIM .BYTE $ED,$D2,$03 ; Q33 Q31 end
1510 SON .BYTE $0E,$C4,$C7 ; value Q28 Q29
1520 .BYTE $CD,$03 ; Q31 end
1530 Q28 .BYTE $17,$02,$18,$03 ; GOTO or GOSUB end
1540 Q29 .BYTE $0E,$C2,$03 ; value Q30 end
1550 Q30 .BYTE $12,$BC,$02,$03 ; , Q29 or end
1560 Q31 .BYTE $14,$02,$16,$03 ; : or EOL end
1570 Q32 .BYTE $01 ; jmp
1580 .WORD NUMVAR-1
1590 .BYTE $0D,$2B,$0F,$39 ; skip ( token dixv
1600 .BYTE $0E,$00 ; value jsr
1610 .WORD L2-1
1620 .BYTE $2C,$02,$01 ; ) or jmp
1630 .WORD STRVAR-1
1640 .BYTE $2B,$0F,$3B,$0E ; ( token ( value
1650 .BYTE $2C,$03 ; ) end
1660 Q33 .BYTE $AA,$C3,$02,$03 ; Q32 Q34 or end
1670 Q34 .BYTE $12,$BB,$02,$03 ; , Q33 or end
1680 SIF .BYTE $0E,$1B,$C3,$9B ; value THEN Q35 Q31
1690 .BYTE $03 ; end
1700 Q35 .BYTE $01 ; jmp
1710 .WORD NUMCNS-1
1720 .BYTE $02,$01 ; or jmp
1730 .WORD SCDSTM-1
1740 L5 .BYTE $C9,$02,$D4,$C3 ; Q37 or Q39 Q36
1750 .BYTE $02,$03 ; or end
1760 Q36 .BYTE $C3,$02,$03 ; Q37 or end
1770 Q37 .BYTE $C3,$C8,$03 ; Q38 or end
1780 Q38 .BYTE $0E,$02,$00 ; value or jsr
1790 .WORD L4-1
1800 .BYTE $03 ; end
1810 .BYTE $C4,$B3,$02,$03 ; Q39 Q36 or end
1820 Q39 .BYTE $C6,$C2,$03 ; Q41 Q40 end
1830 Q40 .BYTE $BD,$02,$03 ; Q39 or end
1840 Q41 .BYTE $12,$02,$15,$03 ; , or ; end
1850 L6 .BYTE $0E ; value
1860 .BYTE $C3,$02,$03 ; Q42 or end
1870 Q42 .BYTE $12,$0E,$02,$03 ; , value or end
1880 SREM .BYTE $01 ; jmp
1890 .WORD MOVLIN-1
1900 SDATA .BYTE $01 ; jmp
1910 .WORD MOVLIN-1
1920 L7 .BYTE $40,$02,$41,$02 ; ASC or VAL or
1930 .BYTE $43,$02,$42,$03 ; ADR or LEN end
1940 L8 .BYTE $3D,$02,$3E,$03 ; STR$ or CHR$ end
1950 L9 .BYTE $0E,$C2,$03 ; value Q43 end
1960 Q43 .BYTE $12,$0F,$3C,$BA ; , token , L9
1970 .BYTE $02,$03 ; or end
W trakcie kontroli składni wywoływane są dodatkowe
procedury pomagające w tokenizacji elementów programu.
Najprostszą z nich jest procedura MOVLIN, wywoływana po
rozpoznaniu tokenu instrukcji REM lub DATA. Po ustawieniu
wszystkich bitów statusu wykonuje ona skok do etykiety MOVTKN,
co powoduje przepisanie bez zmian pozostałej części wiersza.
0100 ;MOVe LINe
0110 ;
0120 MOVTKN = $A0FB
0130 ;
0140 *= $A2DB
0150 ;
0160 LDX #$FF
0170 TXS
0180 JMP MOVTKN
Równie prosta procedura jest wywoływana po rozpoznaniu
instrukcji IF nie zakończonej numerem wiersza. Procedura SCDSTM
zapisuje w stokenizowanym wierszu długość instrukcji IF, a
następnie przechodzi do rozpoznawania następnych instrukcji
znajdujących się w tym samym wierszu.
0100 ;Start of ConDitional STateMent
0110 ;
0120 COX = $94
0130 DCDSTM = $A0B1
0140 LOMEM = $80
0150 OUTIX = $A7
0160 ;
0170 *= $A2CF
0180 ;
0190 LDX #$FF
0200 TXS
0210 LDA COX
0220 LDY OUTIX
0230 STA (LOMEM),Y
0240 JMP DCDSTM
Jeśli spodziewana jest stała liczbowa, to wywoływana jest
procedura NUMCNS. Najpierw zamienia ona przy pomocy procedury
AFP ciąg znaków ASCII na liczbę zmiennoprzecinkową. W przypadku
niepowodzenia ustawiany jest bit Carry i procedura się kończy.
Poprawny wynik zamiany powoduje wpisanie tokena $0E, który
oznacza stałą liczbową, oraz przepisanie sześciu bajtów liczby.
W tym przypadku przed opuszczeniem procedury bit Carry jest
kasowany.
0100 ;NUMber CoNStant
0110 ;
0120 AFP = $D800
0130 CIX = $F2
0140 COX = $94
0150 FR0 = $D4
0160 INBSS = $DBA1
0170 LOMEM = $80
0180 SAVTKN = $A2C4
0190 TIX = $AC
0200 ;
0210 *= $A3F5
0220 ;
0230 JSR INBSS
0240 LDA CIX
0250 STA TIX
0260 JSR AFP
0270 BCC SUCC
0280 LDA TIX
0290 STA CIX
0300 RTS
0310 SUCC LDA #$0E
0320 JSR SAVTKN
0330 INY
0340 LDX #$00
0350 LOOP LDA FR0,X
0360 STA (LOMEM),Y
0370 INY
0380 INX
0390 CPX #$06
0400 BCC LOOP
0410 STY COX
0420 CLC
0430 RTS
Zbliżone jest postępowanie przy tokenizacji stałej
tekstowej. Wykonuje to procedura STRCNS. Najpierw sprawdzany
jest pierwszy znak stałej, i jeśli nie jest to cudzysłów ("),
procedura jest przerywana z ustawionym bitem Carry. W
przeciwnym wypadku zapisywany jest token stałej tekstowej $0F,
po czym kolejne znaki z bufora wejściowego są przepisywane do
bufora tokenizacji, aż do napotkania drugiego cudzysłowu lub
końca wiersza. Po zapisaniu długości stałej procedura kończy
się ze skasowanym bitem Carry.
0100 ;STRing CoNStant
0110 ;
0120 CIX = $F2
0130 COX = $94
0140 INBSS = $DBA1
0150 INBUFP = $F3
0160 LOMEM = $80
0170 SAVTKN = $A2C4
0180 TOX = $AB
0190 ;
0200 *= $A41C
0210 ;
0220 JSR INBSS
0230 LDY CIX
0240 LDA (INBUFP),Y
0250 CMP #'"
0260 BNE $A3F3 ;SEC,RTS
0270 LDA #$0F
0280 JSR SAVTKN
0290 LDA COX
0300 STA TOX
0310 JSR SAVTKN
0320 LOOP INC CIX
0330 LDY CIX
0340 LDA (INBUFP),Y
0350 CMP #$9B
0360 BEQ END
0370 CMP #'"
0380 BEQ NXT
0390 JSR SAVTKN
0400 JMP LOOP
0410 NXT INC CIX
0420 END CLC
0430 LDA COX
0440 SBC TOX
0450 LDY TOX
0460 STA (LOMEM),Y
0470 CLC
0480 RTS
Jeżeli interpreter oczekuje nazwy zmiennej, to wywoływana
jest procedura odpowiednia do typu zmiennej. Dla zmiennej
liczbowej jest to procedura NUMVAR, zaś dla tekstowej STRVAR.
Przebieg obu procedur jest wspólny poza początkową fazą, w
której zapisywany jest w rejestrze VART (VARiable Type) typ
zmiennej ($00 - liczbowa, $80 - tekstowa).
0100 ADBT = $DBAF
0110 AUXBR = $AF
0120 BHLD1 = $B0
0130 BHLD2 = $B1
0140 CIX = $F2
0150 CMPLTR = $A3E8
0160 CSTAD = $97
0170 EXPSX = $A2E1
0180 INBSS = $DBA1
0190 INBUFP = $F3
0200 INSEL = $A87A
0210 LBUFF = $0580
0220 NXTNAM = $A482
0230 RECNAM = $A454
0240 SAVTKN = $A2C4
0250 STMNUM = $B2
0260 STMTAB = $88
0270 TIX = $AC
0280 TMVRER = $B92C
0290 VARN = $D3
0300 VART = $D2
0310 VNTD = $84
0320 VNTP = $82
0330 ;
0340 *= $A320
0350 ;
0360 ;NUMber VARiable
0370 ;
0380 NUMVAR LDA #$00
0390 BEQ EXE
0400 ;
0410 ;STRing VARiable
0420 ;
0430 STRVAR LDA #$80
0440 EXE STA VART
0450 JSR INBSS
0460 LDA CIX
0470 STA TIX
0480 JSR CMPLTR
0490 BCS BAD
0500 JSR EXPSX
0510 LDA BHLD1
0520 BEQ CCR
0530 LDY STMNUM
0540 LDA (INBUFP),Y
0550 CMP #'0
0560 BCC BAD
0570 CCR INC CIX
0580 JSR CMPLTR
0590 BCC CCR
0600 JSR ADBT
0610 BCC CCR
0620 LDA (INBUFP),Y
0630 CMP #'$
0640 BEQ STR
0650 BIT VART
0660 BPL DIM
0670 BAD SEC
0680 RTS
0690 STR BIT VART
0700 BPL BAD
0710 INY
0720 BNE FND
0730 DIM LDA (INBUFP),Y
0740 CMP #'(
0750 BNE FND
0760 INY
0770 LDA #$40
0780 ORA VART
0790 STA VART
0800 FND LDA TIX
0810 STA CIX
0820 STY TIX
0830 LDA VNTP+1
0840 LDY VNTP
0850 LDX #$00
0860 JSR RECNAM
0870 NXT BCS NEW
0880 CPX TIX
0890 BEQ SAV
0900 JSR NXTNAM
0910 JMP NXT
0920 NEW SEC
0930 LDA TIX
0940 SBC CIX
0950 STA CIX
0960 TAY
0970 LDX #VNTD
0980 JSR INSEL
0990 LDA AUXBR
1000 STA VARN
1010 LDY CIX
1020 DEY
1030 LDX TIX
1040 DEX
1050 PUT LDA LBUFF,X
1060 STA (CSTAD),Y
1070 DEX
1080 DEY
1090 BPL PUT
1100 LDY CIX
1110 DEY
1120 LDA (CSTAD),Y
1130 ORA #$80
1140 STA (CSTAD),Y
1150 LDY #$08
1160 LDX #STMTAB
1170 JSR INSEL
1180 INC BHLD2
1190 LDY #$02
1200 LDA #$00
1210 RST STA VART,Y
1220 INY
1230 CPY #$08
1240 BCC RST
1250 DEY
1260 GET LDA VART,Y
1270 STA (CSTAD),Y
1280 DEY
1290 BPL GET
1300 SAV BIT VART
1310 BVC BPS
1320 DEC TIX
1330 BPS LDA TIX
1340 STA CIX
1350 LDA AUXBR
1360 BMI ERR
1370 ORA #$80
1380 CLC
1390 JMP SAVTKN
1400 ERR JMP TMVRER
Rozpoznawanie zmiennej rozpoczyna się od sprawdzenia
poprawności jej zapisu. Najpierw pierwszy znak nazwy jest
kontrolowany przy pomocy procedury CMPLTR. Jeśli nie jest on
literą, to sygnalizowany jest błąd. Następnie wywoływana jest
procedura EXPSX, która przeszukuje tablicę nazw funkcji OPFN.
Gdy badana nazwa nie występuje w tej tablicy, to sprawdzane są
jej kolejne znaki, aż do napotkania znaku innego niż litera lub
cyfra. Jeżeli znakiem tym jest dolar ($), to rejestr VART musi
zawierać wartość $80. Wystąpienie błędu podczas wyżej opisanych
czynności jest sygnalizowane ustawieniem bitu Carry i
przerwaniem procedury.
Teraz sprawdzany jest pierwszy znak następujący po nazwie.
Jeśli jest to nawias, ustawiany jest bit 6 w rejestrze VART
(wymiar lub indeks zmiennej tablicowej). Ponieważ zmienna może
się już znajdować w tablicy nazw zmiennych, to teraz procedura
RECNAM przeszukuje tą tablicę. W rezultacie tego przeszukania w
rejestrze AUXBR znajduje się numer zmiennej (gdy zmienna nie
została znaleziona, jest to liczba o jeden większa od numeru
ostatniej zmiennej w tablicy). Nowa zmienna jest następnie
wpisywana do tablicy nazw i tablicy wartości. W tym celu jest
wykorzystywana opisana już procedura INSEL. Jeżeli zmienna jest
już w tablicy, to ten fragment jest pomijany.
Na samym końcu procedury kontrolowany jest jeszcze numer
zmiennej. Gdy jest on większy od $7F (więcej niż 128
zmiennych), to wykonywany jest skok do TMVRER w celu
zasygnalizowania błędu (Too Many VaRiables ERror). Poprawny
numer zmiennej jest zwiększany o $80 i przez wywołanie SAVTKN
zapisywany w buforze tokenizacji.
Do sprawdzenia znaku pobranego z bufora wejściowego służy
procedura CMPLTR. Odczytuje ona znak i sprawdza, czy jest on
literą z zakresu A-Z. Przy wywołaniu tej procedury od etykiety
CHKLTR sprawdzeniu jest poddawany znak zawarty w akumulatorze.
0100 ;CoMPare for LeTteR
0110 ;
0120 CIX = $F2
0130 INBUFP = $F3
0140 ;
0150 *= $A3E8
0160 ;
0170 CMPLTR LDY CIX
0180 LDA (INBUFP),Y
0190 ;
0200 ;CHecK for LeTteR
0210 ;
0220 CHKLTR CMP #'A
0230 BCC ERR
0240 CMP #'[
0250 RTS
0260 ERR SEC
0270 RTS
Kolejną procedurą wykorzystywaną podczas kontroli składni
jest VALUE. Wywoływana jest ona po napotkaniu kodu większego od
$04 i mniejszego od $80. Jeżeli jest to kod $0F, to następny
znak z tablicy składni jest przepisywany do bufora tokenizacji
i procedura się kończy. Gdy kodem tym jest $0E, to znajdujący
się na stosie adres jest zastępowany adresem początkowym
tablicy SXTAB. W pozostałych przypadkach wykonywany jest skok
do procedury EXPSX, przy czym kod $0D powoduje pominięcie
jednego znaku z bufora wejściowego.
0100 ;VALUE
0110 ;
0120 COX = $94
0130 EXPSX = $A2E1
0140 INCPRC = $A28C
0150 LOMEM = $80
0160 PCNTC = $9D
0170 SAVCPM = $A21B
0180 SXTAB = $A605
0190 ;
0200 *= $A29B
0210 ;
0220 CMP #$0F
0230 BEQ SAV
0240 BCS EXPSX
0250 CMP #$0D
0260 BNE CSX
0270 JSR INCPRC
0280 JMP EXPSX+3
0290 CSX PLA
0300 PLA
0310 LDA # <SXTAB-1
0320 PHA
0330 LDA # >SXTAB-1
0340 PHA
0350 JMP SAVCPM
0360 SAV JSR INCPRC
0370 LDY #$00
0380 LDA (PCNTC),Y
0390 LDY COX
0400 DEY
0410 STA (LOMEM),Y
0420 CLC
0430 RTS
Nazwy wszystkich legalnych operatorów i funkcji Atari Basic
są zapisane w tablicy OPFN. Jest ona zbliżona do tablicy nazw
instrukcji, lecz nie zawiera wektorów do tablicy składni.
Niektóre znajdujące się w niej operatory dotyczące zmiennych
różnych typów są powtórzone. Umożliwia to wybranie
odpowiedniego wariantu procedury przy realizacji programu.
0100 ;OPerator & Function Names
0110 ;
0120 *= $A7DE
0130 ;
0140 .CBYTE $02
0150 .CBYTE $00
0160 .CBYTE ","
0170 .CBYTE "$"
0180 .CBYTE ":"
0190 .CBYTE ";"
0200 .BYTE $9B ;EOL
0210 .CBYTE "GOTO"
0220 .CBYTE "GOSUB"
0230 .CBYTE "TO"
0240 .CBYTE "STEP"
0250 .CBYTE "THEN"
0260 .CBYTE "#"
0270 .CBYTE "<=" ;
0280 .CBYTE "<>" ;
0290 .CBYTE ">=" ;operatory
0300 .CBYTE "<" ;liczbowe
0310 .CBYTE ">" ;
0320 .CBYTE "=" ;
0330 .CBYTE "^"
0340 .CBYTE "*"
0350 .CBYTE "+"
0360 .CBYTE "-"
0370 .CBYTE "/"
0380 .CBYTE "NOT"
0390 .CBYTE "OR"
0400 .CBYTE "AND"
0410 .CBYTE "(" ;nawiasy
0420 .CBYTE ")" ;zwykłe
0430 .CBYTE "=" ;LET liczbowe
0440 .CBYTE "=" ;LET tekstowe
0450 .CBYTE "<=" ;
0460 .CBYTE "<>" ;
0470 .CBYTE ">=" ;operatory
0480 .CBYTE "<" ;tekstowe
0490 .CBYTE ">" ;
0500 .CBYTE "=" ;
0510 .CBYTE "+" ;znak
0520 .CBYTE "-" ;znak
0530 .CBYTE "(" ;wyr. tekstowe
0540 .CBYTE $00 ;zm. indeks.
0550 .CBYTE $00 ;wym. zm. ind.
0560 .CBYTE "(" ;wyr. funkc.
0570 .CBYTE "(" ;wym. zm. tekst.
0580 .CBYTE "," ;w indeksie zm.
0590 .CBYTE "STR$"
0600 .CBYTE "CHR$"
0610 .CBYTE "USR"
0620 .CBYTE "ASC"
0630 .CBYTE "VAL"
0640 .CBYTE "LEN"
0650 .CBYTE "ADR"
0660 .CBYTE "ATN"
0670 .CBYTE "COS"
0680 .CBYTE "PEEK"
0690 .CBYTE "SIN"
0700 .CBYTE "RND"
0710 .CBYTE "FRE"
0720 .CBYTE "EXP"
0730 .CBYTE "LOG"
0740 .CBYTE "CLOG"
0750 .CBYTE "SQR"
0760 .CBYTE "SGN"
0770 .CBYTE "ABS"
0780 .CBYTE "INT"
0790 .CBYTE "PADDLE"
0800 .CBYTE "STICK"
0810 .CBYTE "PTRIG"
0820 .CBYTE "STRIG"
0830 .BYTE $00
Powyższa tablica jest przeszukiwana przez procedurę EXPSX,
która służy do kontroli składni wyrażeń arytmetycznych,
logicznych i tekstowych. Znaleziony numer operatora lub funkcji
zapisywany jest do bufora tokenizacji. Poprawny wynik tej
operacji jest sygnalizowany skasowaniem bitu Carry.
0100 ;EXPression SyntaX
0110 ;
0120 AUXBR = $AF
0130 BHLD1 = $B0
0140 CIX = $F2
0150 INBSS = $DBA1
0160 OPFN = $A7DE
0170 PCNTC = $9D
0180 RECNAM = $A454
0190 SAVTKN = $A2C4
0200 STMNUM = $B2
0210 TMPIX = $B3
0220 ;
0230 *= $A2E1
0240 ;
0250 JSR INBSS
0260 LDA CIX
0270 CMP TMPIX
0280 BEQ CHK
0290 STA TMPIX
0300 LDA # >OPFN
0310 LDY # <OPFN
0320 LDX #$00
0330 JSR RECNAM
0340 BCS NFD
0350 STX STMNUM
0360 LDA AUXBR
0370 ADC #$10
0380 STA BHLD1
0390 CHK LDY #$00
0400 LDA (PCNTC),Y
0410 CMP BHLD1
0420 BEQ SAV
0430 CMP #$44
0440 BNE ERR
0450 LDA BHLD1
0460 CMP #$44
0470 BCC ERR
0480 SAV JSR SAVTKN
0490 LDX STMNUM
0500 STX CIX
0510 CLC
0520 RTS
0530 NFD LDA #$00
0540 STA BHLD1
0550 ERR SEC
0560 RTS
2.2.3. Wykonanie instrukcji
Jeżeli wprowadzony wiersz jest w trybie bezpośrednim, to
procedura SYNTAX kończy się skokiem do PRCSTM. Jej zadaniem
jest w tym wypadku wykonanie instrukcji zawartych we
wprowadzonym wierszu. Procedura ta jest także wywoływana przez
XRUN w celu wykonania programu znajdującego się w pamięci
komputera.
0100 ;PRoCeed STateMent
0110 ;
0120 BUFIX = $9F
0130 EXESTM = $A97E
0140 GHISTM = $A9E1
0150 GIRQST = $A9F2
0160 GSTMLN = $B819
0170 INIX = $A8
0180 NXTSTM = $A9D0
0190 OUTIX = $A7
0200 STMCUR = $8A
0210 WAITIN = $A05D
0220 XEND = $B78C
0230 XSTOP = $B792
0240 ;
0250 *= $A95E
0260 ;
0270 PRCSTM JSR GSTMLN
0280 STAT JSR GIRQST
0290 BEQ STOP
0300 LDY OUTIX
0310 CPY BUFIX
0320 BCS NEXT
0330 LDA (STMCUR),Y
0340 STA OUTIX
0350 TYA
0360 INY
0370 LDA (STMCUR),Y
0380 INY
0390 STY INIX
0400 JSR EXESTM
0410 NOP
0420 JMP STAT
0430 ;
0440 *= $A989
0450 ;
0460 NEXT LDY #$01
0470 LDA (STMCUR),Y
0480 BMI WAIT
0490 LDA BUFIX
0500 JSR NXTSTM
0510 JSR GHISTM
0520 BPL PRCSTM
0530 JMP XEND
0540 STOP JMP XSTOP
0550 WAIT JMP WAITIN
Pierwszą czynnością jest odczytanie długości wykonywanego
wiersza. Uzyskana wartość służy do zliczania tokenów i
umożliwia zakończenie realizacji wiersza po osiągnięciu jego
końca. Odczyt długości wiersza następuje przez wywołanie
procedury GSTMLN. Przy pomocy wektora STMCUR (STateMent CURrent
address) trzeci token, którego wartość określa długość wiersza,
jest pobierany z tabeli instrukcji i zapisywany do licznika
BUFIX (BUFfer IndeX). Indeks następnego tokena jest zapisywany
w liczniku OUTIX.
0100 BUFIX = $9F
0110 FNDCST = $A9A2
0120 OUTIX = $A7
0130 STMCUR = $8A
0140 ;
0150 *= $B816
0160 ;
0170 ;Find STatement & Get LeNgth
0180 ;
0190 FSTGLN JSR FNDCST
0200 ;
0210 ;Get STateMent LeNgth
0220 ;
0230 GSTMLN LDY #$02
0240 LDA (STMCUR),Y
0250 STA BUFIX
0260 INY
0270 STY OUTIX
0280 RTS
Następnie sprawdzany jest przez procedurę GIRQST rejestr
statusu przerwań. Jeśli sygnalizuje on naciśnięcie klawisza
BREAK, to wykonywanie wiersza jest przerywane skokiem do
procedury XSTOP. Naciśnięcie BREAK jest więc równoważne
umieszczeniu w wierszu instrukcji STOP. Teraz porównywane są
zawartości liczników BUFIX i OUTIX. Gdy są one różne, to
wykonywanie wiersza jest kontynuowane. Następny odczytany token
oznacza długość instrukcji, a właściwie indeks jej ostatniego
tokena. Jest on zapisywany do licznika OUTIX. Z kolei do
akumulatora pobierany jest token instrukcji, a indeks
następnego tokena do odczytu jest umieszczany w rejestrze INIX
(INput IndeX). Odpowiednia procedura realizacyjna instrukcji
jest wywoływana przez procedurę EXESTM i pętla się powtarza od
sprawdzenia klawisza BREAK.
Zrównanie zawartości rejestrów BUFIX i OUTIX oznacza
zakończenie wykonywania całego wiersza. Jeżeli był to wiersz w
trybie bezpośrednim, to wykonywany jest skok do etykiety WAITIN
i komputer oczekuje na następne polecenia. W trybie programowym
poprzez wywołanie procedury NXTSTM przepisywany jest do
rejestru STMCUR adres kolejnego wiersza. Starszy bajt tego
wiersza jest odczytywany przez procedurę GHISTM. Wartość $80
sygnalizuje, że następny wiersz jest w trybie bezpośrednim, a
więc cały program został już wykonany. W takim przypadku
wykonywany jest skok do procedury XEND, jeśli zaś następny
wiersz programu znajduje się w pamięci, to ponownie wykonywana
jest procedura PRCSTM.
0100 ;Get HIgh byte of STateMent
0110 ;
0120 STMCUR = $8A
0130 ;
0140 *= $A9E1
0150 ;
0160 GHISTM LDY #$01
0170 LDA (STMCUR),Y
0180 ;
0190 ;eXecute REM statement
0200 ;
0210 XREM RTS
Właściwe wywołanie procedury realizującej żądaną instrukcję
jest przeprowadzane przez procedurę EXESTM według wartości
tokenu przekazanej w akumulatorze. Zastosowano tu popularny w
systemie Atari sposób: adres procedury jest umieszczany na
stosie, po czym wykonywany jest rozkaz RTS. Powrót z procedury
wykonawczej następuje do miejsca wywołania EXESTM.
0100 ;EXEcute STateMent
0110 ;
0120 STVTAB = $A9FA
0130 ;
0140 *= $A97E
0150 ;
0160 ASL A
0170 TAX
0180 LDA STVTAB,X
0190 PHA
0200 LDA STVTAB+1,X
0210 PHA
0220 RTS
Adres procedury wykonawczej jest odczytywany z tablicy
wektorów STVTAB przy wykorzystaniu podwojonej wartości tokena
instrukcji (adresy są dwubajtowe). Tablica wektorów musi więc
zawierać adresy procedur w kolejności zgodnej z tablicą nazw
instrukcji STNAME.
0100 ;STatement Vectors TABle
0110 ;
0120 SNTXER = $B912
0130 XBYE = $A9E6
0140 XCLR = $B766
0150 XCLOAD = $BB64
0160 XCLOSE = $BC22
0170 XCOLOR = $BA1F
0180 XCONT = $B7B5
0190 XCSAVE = $BBD1
0200 XDEG = $B28D
0210 XDIM = $B206
0220 XDRAW = $BA27
0230 XDOS = $A9EC
0240 XEND = $B78C
0250 XENTER = $BAC5
0260 XFOR = $B67D
0270 XGET = $BC85
0280 XGOSUB = $B6D2
0290 XGOTO = $B6D5
0300 XGRAPH = $BA46
0310 XIF = $B778
0320 XINPUT = $B33E
0330 XLET = $AADA
0340 XLIST = $B4B5
0350 XLOAD = $BAFB
0360 XLOCAT = $BC9E
0370 XLPRNT = $B496
0380 XNEW = $A00C
0390 XNEXT = $B700
0400 XNOTE = $BC3D
0410 XON = $B7E4
0420 XOPEN = $BBF2
0430 XPLOT = $BA6C
0440 XPOINT = $BC54
0450 XPOKE = $B278
0460 XPOP = $B83E
0470 XPOS = $BA0C
0480 XPRINT = $B3DA
0490 XPUT = $BC78
0500 XRAD = $B291
0510 XREAD = $B2AE
0520 XREM = $A9E5
0530 XRSTR = $B296
0540 XRTRN = $BDA8
0550 XRUN = $B74C
0560 XSAVE = $BB6D
0570 XSETC = $B9AD
0580 XSOUND = $B9D3
0590 XSTAT = $BC2F
0600 XSTOP = $B792
0610 XTRAP = $B7D8
0620 XXIO = $BBEC
0630 ;
0640 *= $A9FA
0650 ;
0660 .DBYTE XREM-1 ;REM
0670 .DBYTE XREM-1 ;DATA
0680 .DBYTE XINPUT-1 ;INPUT
0690 .DBYTE XCOLOR-1 ;COLOR
0700 .DBYTE XLIST-1 ;LIST
0710 .DBYTE XENTER-1 ;ENTER
0720 .DBYTE XLET-1 ;LET
0730 .DBYTE XIF-1 ;IF
0740 .DBYTE XFOR-1 ;FOR
0750 .DBYTE XNEXT-1 ;NEXT
0760 .DBYTE XGOTO-1 ;GOTO
0770 .DBYTE XGOTO-1 ;GO TO
0780 .DBYTE XGOSUB-1 ;GOSUB
0790 .DBYTE XTRAP-1 ;TRAP
0800 .DBYTE XBYE-1 ;BYE
0810 .DBYTE XCONT-1 ;CONT
0820 .DBYTE XDIM-1 ;COM
0830 .DBYTE XCLOSE-1 ;CLOSE
0840 .DBYTE XCLR-1 ;CLR
0850 .DBYTE XDEG-1 ;DEG
0860 .DBYTE XDIM-1 ;DIM
0870 .DBYTE XEND-1 ;END
0880 .DBYTE XNEW-1 ;NEW
0890 .DBYTE XOPEN-1 ;OPEN
0900 .DBYTE XLOAD-1 ;LOAD
0910 .DBYTE XSAVE-1 ;SAVE
0920 .DBYTE XSTAT-1 ;STATUS
0930 .DBYTE XNOTE-1 ;NOTE
0940 .DBYTE XPOINT-1 ;POINT
0950 .DBYTE XXIO-1 ;XIO
0960 .DBYTE XON-1 ;ON
0970 .DBYTE XPOKE-1 ;POKE
0980 .DBYTE XPRINT-1 ;PRINT
0990 .DBYTE XRAD-1 ;RAD
1000 .DBYTE XREAD-1 ;READ
1010 .DBYTE XRSTR-1 ;RESTORE
1020 .DBYTE XRTRN-1 ;RETURN
1030 .DBYTE XRUN-1 ;RUN
1040 .DBYTE XSTOP-1 ;STOP
1050 .DBYTE XPOP-1 ;POP
1060 .DBYTE XPRINT-1 ;?
1070 .DBYTE XGET-1 ;GET
1080 .DBYTE XPUT-1 ;PUT
1090 .DBYTE XGRAPH-1 ;GRAPHICS
1100 .DBYTE XPLOT-1 ;PLOT
1110 .DBYTE XPOS-1 ;POSITION
1120 .DBYTE XDOS-1 ;DOS
1130 .DBYTE XDRAW-1 ;DRAWTO
1140 .DBYTE XSETC-1 ;SETCOLOR
1150 .DBYTE XLOCAT-1 ;LOCATE
1160 .DBYTE XSOUND-1 ;SOUND
1170 .DBYTE XLPRNT-1 ;LPRINT
1180 .DBYTE XCSAVE-1 ;CSAVE
1190 .DBYTE XCLOAD-1 ;CLOSE
1200 .DBYTE XLET-1 ;opuszczone LET
1210 .DBYTE SNTXER-1 ;ERROR
|