Rozdział 5
PROCEDURY OPERATORÓW I FUNKCJI
Opisana w poprzednim rozdziale instrukcja przypisania
wykorzystuje wiele różnorodnych operatorów i funkcji. Sposób
wywołania wszystkich procedur wykonawczych operatorów i funkcji
jest jednakowy (zob. opis procedury EXEOP). Elementy składowe
instrukcji przypisania można podzielić na sześć podstawowych
grup: operatory arytmetyczne, operatory logiczne, operatory
relacji, operatory przypisania, nawiasy oraz funkcje. Są one
kolejno opisane w tym rozdziale.
5.1. Operatory arytmetyczne
Do operatorów arytmetycznych zaliczamy operatory dodawania
(+), odejmowania (-), mnożenia (*), dzielenia (/) i potęgowania
(^) oraz znaki plus (+) i minus (-). Najprostszym operatorem
jest znak plus. Nie zmienia on nic w wartości liczby i cała
procedura wykonawcza ogranicza się do rozkazu RTS. Znajduje się
on w procedurze RSTPTR, która została opisana w poprzednim
rozdziale (str. 64).
Wykonanie operatora znaku minus (-), który zmienia znak
liczby, jest realizowane przez procedurę XMIN. Liczba do
operacji jest pobierana z bufora tokenizacji przez procedurę
GETVAR. Następnie zmieniana jest wartość najstarszego bitu w
pierwszym bajcie liczby FP, co powoduje zmianę znaku tej
liczby. XMIN kończy się skokiem do procedury PUTVAR, która
zapisuje wynik ponownie do bufora tokenizacji.
0100 ;eXecute MINus sign
0110 ;
0120 FR0 = $D4
0130 GETVAR = $ABE9
0140 PUTVAR = $ABB2
0150 ;
0160 *= $AC95
0170 ;
0180 JSR GETVAR
0190 LDA FR0
0200 BEQ EXIT
0210 EOR #$80
0220 STA FR0
0230 EXIT JMP PUTVAR
Działanie procedury GETVAR jest odwrotne do PUTVAR. Wartość
odczytana z licznika STPTR i zmniejszona o jeden wskazuje - po
pomnożeniu przez osiem - ostatni bajt ostatniej liczby w
buforze. Według tej wartości osiem bajtów liczby jest
przepisywane z bufora do rejestrów VART, VARN i FR0.
0100 ;GET VARiable
0110 ;
0120 LOMEM = $80
0130 STPTR = $AA
0140 VART = $D2
0150 ;
0160 *= $ABE9
0170 ;
0180 LDA STPTR
0190 DEC STPTR
0200 ASL A
0210 ASL A
0220 ASL A
0230 TAY
0240 DEY
0250 LDX #$07
0260 LOOP LDA (LOMEM),Y
0270 STA VART,X
0280 DEY
0290 DEX
0300 BPL LOOP
0310 RTS
5.1.1. Dodawanie
Operacja dodawania (+) jest realizowana przez procedurę
XADD. Składa się ona jedynie z trzech wywołań innych procedur.
GETVRS pobiera z bufora dwie liczby, BADD wykonuje ich
dodawanie, a PUTVAR umieszcza rezultat ponownie w buforze.
0100 ;eXecute ADDition
0110 ;
0120 BADD = $AD26
0130 GETVRS = $ABFD
0140 PUTVAR = $ABB2
0150 ;
0160 *= $BDFA
0170 ;
0180 JSR GETVRS
0190 JSR BADD
0200 JMP PUTVAR
Procedura GETVRS służy do odczytania dwóch liczb dla
operacji arytmetycznej i jest złożeniem dwóch procedur GETVAR.
Pierwsze wywołanie GETVAR pobiera jedną liczbę z bufora
tokenizacji i umieszcza ją w rejestrze FR0. Liczba ta jest
przepisywana przez procedurę FMOV01 (wchodzi ona w skład
pakietu zmiennoprzecinkowego) do rejestru FR1. Kończący
procedurę GETVRS skok do GETVAR powoduje pobranie drugiej
liczby do rejestru FR0.
0100 ;GET VaRiableS
0110 ;
0120 FMOV01 = $DDB6
0130 GETVAR = $ABE9
0140 ;
0150 *= $ABFD
0160 ;
0170 JSR GETVAR
0180 JSR FMOV01
0190 JMP GETVAR
Zadaniem procedury BADD jest jedynie wywołanie procedury
zmiennoprzecinkowej FADD. Użycie BADD zamiast bezpośredniego
wywołania FADD ma na celu umożliwienie poprawnej sygnalizacji
błędu powstałego podczas dodawania. Podobne jest znaczenie
procedur BSUB, BMUL i BDIV. Zakończenie operacji
zmiennoprzecinkowej z ustawionym bitem Carry oznacza
wystąpienie błędu. W takich przypadkach wykonywany jest skok do
procedury OVUNER (OVerflow/UNderflow ERror).
0100 FADD = $DA66
0110 FDIV = $DB28
0120 FMUL = $DADB
0130 FSUB = $DA60
0140 OVUNER = $B91E
0150 ;
0160 *= $AD26
0170 ;
0180 ;Basic ADDition
0190 ;
0200 BADD JSR FADD
0210 BCS ERR
0220 RTS
0230 ;
0240 ;Basic SUBtraction
0250 ;
0260 BSUB JSR FSUB
0270 BCS ERR
0280 RTS
0290 ;
0300 ;Basic MULtiplication
0310 ;
0320 BMUL JSR FMUL
0330 BCS ERR
0340 RTS
0350 ;
0360 ;Basic DIVision
0370 ;
0380 BDIV JSR FDIV
0390 BCS ERR
0400 RTS
0410 ERR JSR OVUNER
5.1.2. Odejmowanie
Operacja odejmowania (-) jest realizowana identycznie, jak
dodawania. Po odczytaniu dwóch liczb przez GETVRS odejmowanie
jest wykonywane przez BSUB (zob. wyżej). Uzyskany wynik jest
zapisywany w buforze tokenizacji przez procedurę PUTVAR.
0100 ;eXecute SUBtraction
0110 ;
0120 BSUB = $AD2C
0130 GETVRS = $ABFD
0140 PUTVAR = $ABB2
0150 ;
0160 *= $AC7A
0170 ;
0180 JSR GETVRS
0190 JSR BSUB
0200 JMP PUTVAR
5.1.3. Mnożenie
Także przebieg operacji mnożenia (*) jest analogiczny do
przebiegu poprzednio opisanych operacji. W celu realizacji
obliczenia jest tu wywoływana procedura BMUL.
0100 ;eXecute MULtiplication
0110 ;
0120 BMUL = $AD32
0130 GETVRS = $ABFD
0140 PUTVAR = $ABB2
0150 ;
0160 *= $AC83
0170 ;
0180 JSR GETVRS
0190 JSR BMUL
0200 JMP PUTVAR
5.1.4. Dzielenie
Ostatnią z prostych operacji arytmetycznych jest dzielenie
(/) wykonywane przez procedurę XDIV. Jedyną różnicą w stosunku
do poprzednio opisanych procedur wykonawczych jest zastosowanie
do realizacji obliczenia procedury BDIV.
0100 ;eXecute DIVision
0110 ;
0120 BDIV = $AD38
0130 GETVRS = $ABFD
0140 PUTVAR = $ABB2
0150 ;
0160 *= $AC8C
0170 ;
0180 JSR GETVRS
0190 JSR BDIV
0200 JMP PUTVAR
5.1.5. Potęgowanie
Znacznie bardziej skomplikowanym działaniem jest
podnoszenie do potęgi (oznaczone operatorem ^). Jest ono
wykonywane przez procedurę XRPW. Na jej początku sprawdzane są
wartości liczb. Gdy wykładnik jest równy zero, to przez skok do
procedury ONEP (wewnątrz XLEQ - zob. rozdział 5.3.1) wynik jest
ustalany na jeden. Jeśli liczba potęgowana jest równa zero, to
dla ujemnej wartości wykładnika sygnalizowana jest niepoprawna
wartość (skok do procedury VALER w XSQR - zob. rozdział 5.2.4),
zaś dla pozostałych wykładników wynikiem jest zero (następuje
bezpośredni skok do procedury PUTVAR).
0100 BMUL = $AD32
0110 CONVFN = $BA76
0120 EXP10 = $DDCC
0130 FR0 = $D4
0140 FR1 = $E0
0150 GETVRS = $ABFD
0160 LOG10 = $DED1
0170 ONEP = $ACF0
0180 OVUNER = $B91E
0190 PUTVAR = $ABB2
0200 VALER = $B15B
0210 ZTEMP2 = $F7
0220 ;
0230 *= $B15E
0240 ;
0250 ;eXecute Raise to PoWer function
0260 ;
0270 XRPW JSR GETVRS
0280 LDA FR1
0290 BEQ ONE
0300 ROL A
0310 LDY FR0
0320 BNE EXE
0330 BCS VALER
0340 ;
0350 ;PUT RESult
0360 ;
0370 PUTRES JMP PUTVAR
0380 ONE JMP ONEP
0390 EXE LDX #FR0
0400 JSR CONVFN
0410 ROR A
0420 PHA
0430 LDX #FR1
0440 JSR CONVFN
0450 TYA
0460 BPL POS
0470 AND #$7F
0480 STA FR0
0490 BCS GOOD
0500 PLA
0510 BCC VALER
0520 GOOD LDA FR1
0530 BPL BPS
0540 CLC
0550 BPS PHP
0560 LDX ZTEMP2
0570 CPX #$05
0580 BCS PLS
0590 LDA FR1+1,X
0600 ROR A
0610 BCC PLS
0620 LDA #$80
0630 BNE MIN
0640 POS LDA FR1
0650 BPL SST
0660 CLC
0670 SST PHP
0680 PLS LDA #$00
0690 MIN PHA
0700 LDX #$05
0710 LP1 LDA FR1,X
0720 PHA
0730 DEX
0740 BPL LP1
0750 JSR LOG10
0760 LDX #$00
0770 LDY #$05
0780 LP2 PLA
0790 STA FR1,X
0800 INX
0810 DEY
0820 BPL LP2
0830 JSR BMUL
0840 JSR EXP10
0850 BCS ERR
0860 PLA
0870 ORA FR0
0880 STA FR0
0890 PLP
0900 PLA
0910 BPL PUTRES
0920 BCC PUTRES
0930 LDX #FR0
0940 JSR CONVFN
0950 BCS PUTRES
0960 LDA FR0
0970 SEC
0980 AND #$7F
0990 SBC #$3F
1000 CMP #$06
1010 BCS PUT
1020 TAX
1030 TAY
1040 SED
1050 SEC
1060 LP3 LDA FR0,X
1070 ADC #$00
1080 STA FR0,X
1090 DEX
1100 BNE LP3
1110 CLD
1120 BCC NXT
1130 INC FR0
1140 INC FR0+1
1150 NXT INY
1160 CPY #$06
1170 BCS PUT
1180 STX FR0,Y
1190 BCC NXT
1200 PUT JMP PUTVAR
1210 ERR JSR OVUNER
Dla wszystkich pozostałych wartości liczby potęgowanej i
wykładnika przeprowadzane jest obliczenie. Przed jego opisaniem
trzeba jednak wyjaśnić działanie pomocniczej procedury CONVFN.
Wymaga ona jako danej wejściowej adresu rejestru FRn w
rejestrze X. Jeżeli wskazany rejestr zawiera liczbę ujemną, to
procedura CONVFN jest przerywana ze skasowanym bitem Carry. Dla
liczb dodatnich ich wykładnik jest umieszczany w rejestrze
ZTEMP2 (Zeropage TEMPorary register), a do akumulatora są
odczytywane dwie pierwsze cyfry różne od zera stojące poniżej
pozycji setek. Na przykład dla liczby 1234 będzię to 34. Jeśli
nie ma takich cyfr, to bit Carry jest ustawiany.
0100 ;CONVert Floating Number
0110 ;
0120 ZTEMP1 = $F5
0130 ZTEMP2 = $F7
0140 ;
0150 *= $BA76
0160 ;
0170 SEC
0180 LDA $00,X
0190 AND #$7F
0200 SBC #$40
0210 BCC END
0220 STA ZTEMP1
0230 STA ZTEMP2
0240 TXA
0250 ADC ZTEMP1
0260 INX
0270 INX
0280 INX
0290 INX
0300 INX
0310 INX
0320 STX ZTEMP1
0330 TAX
0340 LOOP INX
0350 CPX ZTEMP1
0360 BCS END
0370 LDA $00,X
0380 BEQ LOOP
0390 END RTS
Procedura CONVFN jest najpierw wywoływana dla liczby
potęgowanej, a zawartość akumulatora podzielona przez dwa jest
zapisywana na stosie. Przy drugim wywołaniu CONVFN sprawdzany
jest wykładnik potęgi. Teraz jest wyznaczany znak wyniku, przy
czym sposób obliczenia zależy od znaku potęgowanej liczby.
Następnie (w liniach 700-850 na wydruku) przeprowadzane jest
właściwe obliczenie potęgi według wzoru:
A^B=10^(log10A*B)
W tym miejscu procedura XRPW kończy się dla następujących
przypadków: ujemny wykładnik, ujemna liczba potęgowana lub brak
cyfry różnej od zera poniżej pozycji setek dla dodatniej liczby
potęgowanej. Obliczenia są kontynuowane tylko dla cyfr
znaczących mniejszych od setek, jeśli liczba potęgowana jest
mniejsza od 10,000,000,000 (1005).
5.2. Funkcje
Ważnym elementem interpretera są procedury obliczania
różnorodnych funkcji. Ich wykorzystanie przy pisaniu własnych
programów w języku maszynowym pozwala zaoszczędzić dużo pracy.
Programując zaś w Basicu trzeba znać ograniczenia tych
procedur, aby uniknąć błędów powodowanych przez niepoprawne
wartości lub zbyt przybliżone wyniki. Atari Basic posiada 24
funkcje, których opis znajduje się poniżej, poza funkcją SGN
opisaną razem z operatorami relacji.
5.2.1. Funkcja ABS
Funkcja ABS zwraca moduł czyli wartość bezwzględną podanej
liczby. Dla uzyskania takiego efektu wystarczy skasowanie
najstarszego bitu w pierwszym bajcie liczby. Czynność ta jest
wykonywana przez procedurę XABS. Liczba poddawana tej operacji
jest pobierana z bufora przez procedurę GETVAR, a ponownie
zapisywana przez PUTVAR.
0100 ;eXecute ABS function
0110 ;
0120 FR0 = $D4
0130 GETVAR = $ABE9
0140 PUTVAR = $ABB2
0150 ;
0160 *= $B099
0170 ;
0180 JSR GETVAR
0190 LDA FR0
0200 AND #$7F
0210 STA FR0
0220 JMP PUTVAR
5.2.2. Funkcja RND
Funkcja RND zwraca wartość losową z przedziału <0,1) czyli
liczbę o wartości większej lub równej zero i mniejszej od
jeden. Realizowana jest ona przez procedurę XRND. Najpierw
stała RNDC o wartości 65536 jest przepisywana do rejestru FR1.
Znajdujące się tu wywołanie procedury GETVAR jest konieczne dla
usunięcia z bufora argumentu funkcji, który nie ma żadnego
znaczenia. Następnie dwa przypadkowe bajty z rejestru
sprzętowego RANDOM są umieszczane w rejestrze FR0 i zamieniane
na liczbę zmiennoprzecinkową z zakresu od 0 do 65535. Liczba ta
jest dzielona przy pomocy procedury BDIV przez stałą 65536. Na
końcu uzyskany wynik jest przepisywany do bufora przez
procedurę PUTVAR.
0100 ;eXecute RND function
0110 ;
0120 BDIV = $AD38
0130 FLD1R = $DD98
0140 FR0 = $D4
0150 GETVAR = $ABE9
0160 IFP = $D9AA
0170 PUTVAR = $ABB2
0180 RANDOM = $D20A
0190 ;
0200 *= $B076
0210 ;
0220 LDX # <RNDC
0230 LDY # >RNDC
0240 JSR FLD1R
0250 JSR GETVAR
0260 LDY RANDOM
0270 STY FR0
0280 LDY RANDOM
0290 STY FR0+1
0300 JSR IFP
0310 JSR BDIV
0320 JMP PUTVAR
0330 ;
0340 ;RaNDom Constant
0350 ;
0360 RNDC .BYTE $42,$06,$55
0370 .BYTE $36,$00,$00
Trzeba zwrócić uwagę na to, że argument funkcji RND nie ma
żadnego znaczenia pomimo, iż jest umieszczany w rejestrze FR0.
Procedura IFP zamienia na liczbę zmiennoprzecinkową tylko dwa
pierwsze bajty tego rejestru, a więc tylko te, które zostały
odczytane z RANDOM.
5.2.3. Funkcja INT
Funkcja INT zwraca część całkowitą podanego argumentu, przy
czym liczba jest zawsze zaokrąglana do mniejszej wartości.
Oznacza to, że INT(5.5)=5, lecz INT(-5.5)=-6! Procedura
realizacyjna tej operacji - XINT - jest bardzo zbliżona do
procedur operacji arytmetycznych. Składa się ona z wywołań
trzech procedur: odczytu liczby (GETVAR), wykonania obliczenia
(BINT) i zapisu wyniku (PUTVAR).
0100 ;eXecute INT function
0110 ;
0120 BINT = $B0D2
0130 GETVAR = $ABE9
0140 PUTVAR = $ABB2
0150 ;
0160 *= $B0C8
0170 ;
0180 JSR GETVAR
0190 JSR BINT
0200 JMP PUTVAR
Zasadnicze obliczenia w funkcji INT są wykonywane przez
procedurę BINT. Rozpoczyna się ona od obliczenia pozycji
przecinka w liczbie poddawanej przekształceniu. Następnie
wszystkie cyfry po przecinku są zerowane, przy czym ich
dotychczasowe wartości są sumowane w akumulatorze. Jeżeli
liczba była dodatnia (FR0 mniejsze od $80) lub całkowita ujemna
(akumulator równy zero), to BINT jest opuszczana przez skok do
procedury normalizującej format - NFR0. Niecałkowita wartość
liczby ujemnej wymaga jeszcze odjęcia jedności od wyniku
(dokładnie jest to dodanie -1). Po tej operacji normalizacja
formatu nie jest już potrzebna.
0100 ;Basic INTeger routine
0110 ;
0120 BADD = $AD26
0130 FR0 = $D4
0140 FR1 = $E0
0150 NFR0 = $DC00
0160 ZFR0 = $DA44
0170 ;
0180 *= $B0D1
0190 ;
0200 LDA FR0
0210 AND #$7F
0220 SEC
0230 SBC #$3F
0240 BPL EXE
0250 LDA #$00
0260 EXE TAX
0270 LDA #$00
0280 TAY
0290 LOOP CPX #$05
0300 BCS ADD
0310 ORA FR0+1,X
0320 STY FR0+1,X
0330 INX
0340 BNE LOOP
0350 ADD LDX FR0
0360 BPL NRM
0370 TAX
0380 BEQ NRM
0390 LDX #FR1
0400 JSR ZFR0+2
0410 LDA #$C0
0420 STA FR1
0430 LDA #$01
0440 STA FR1+1
0450 JSR BADD
0460 RTS
0470 NRM JMP NFR0
5.2.4. Funkcja SQR
Funkcja SQR zwraca wartość pierwiastka kwadratowego
podnego argumentu. Także tutaj zastosowano wywołanie
zasadniczej procedury wykonawczej SQR po odczytaniu liczby
przez GETVAR. Natomiast poprawność wyniku jest rozpoznawana w
procedurze XSQR przez sprawdzenie bitu Carry. Błąd jest
sygnalizowany przez BVALER (Bad VALue ERror), zaś sukces
powoduje skok do PUTRES (znajduje się tam tylko rozkaz skoku
JMP PUTVAR). Obie użyte tu etykiety - RESULT i VALER - są
wykorzystywane przez procedury innych funkcji, co pozwala na
zastąpienie rozkazów skoków bezwzględnych (JMP i JSR) rozkazami
skoków względnych (B..). Każda taka zamiana daje jeden bajt
oszczędności.
0100 ;eXecute SQR function
0110 ;
0120 BVALER = $B92E
0130 GETVAR = $ABE9
0140 PUTRES = $B16C
0150 SQR = $BF43
0160 ;
0170 *= $B153
0180 ;
0190 XSQR JSR GETVAR
0200 JSR SQR
0210 ;
0220 ;RESULT
0230 ;
0240 RESULT BCC PUTRES
0250 ;
0260 ;VALue ERror
0270 ;
0280 VALER JSR BVALER
Ze względu na znaczny stopień skomplikowania procedury SQR
jej opis zostanie pominięty, a dociekliwi Czytelnicy mogą
dokonać analizy samodzielnie. Należy jednak zauważyć, że poza
wykorzystywaniem standardowych procedur obliczeń
zmiennoprzecinkowych (FADD, FSUB, FMUL i FDIV) oraz
przemieszczeń liczb FP (FLD.., FST.. i FMOV..) są tu jeszcze
używane dwie tablice: TLOG i SQRC. Pierwsza z nich zawiera
współczynniki logarytmowania i znajduje się w pakiecie FP.
0100 DIGRT = $F1
0110 ESIGN = $EF
0120 FADD = $DA66
0130 FDIV = $DB28
0140 FLD0R = $DD89
0150 FLD1R = $DD98
0160 FMOV01 = $DDB6
0170 FMUL = $DADB
0180 FPSCR = $05E6
0190 FPSCR1 = $05EC
0200 FR0 = $D4
0210 FR1 = $E0
0220 FST0R = $DDA7
0230 FSUB = $DA60
0240 SQRC = $BAF1
0250 TLOG = $DF66
0260 ;
0270 *= $BF41
0280 ;
0290 ERR SEC
0300 RTS
0310 ;
0320 ;SQuare Root routine
0330 ;
0340 SQR LDA #$00
0350 STA DIGRT
0360 LDA FR0
0370 BMI ERR
0380 CMP #$3F
0390 BEQ LBL1
0400 CLC
0410 ADC #$01
0420 STA DIGRT
0430 STA FR1
0440 LDA #$01
0450 STA FR1+1
0460 LDX #$04
0470 LDA #$00
0480 NXT1 STA FR1+2,X
0490 DEX
0500 BPL NXT1
0510 JSR FDIV
0520 LBL1 LDA #$06
0530 STA ESIGN
0540 LDX # <FPSCR
0550 LDY # >FPSCR
0560 JSR FST0R
0570 JSR FMOV01
0580 LDX # <SQRC
0590 LDY # >SQRC
0600 JSR FLD0R
0610 JSR FSUB
0620 LDX # <FPSCR
0630 LDY # >FPSCR
0640 JSR FLD1R
0650 JSR FMUL
0660 LOOP LDX # <FPSCR1
0670 LDY # >FPSCR1
0680 JSR FST0R
0690 JSR FMOV01
0700 LDX # <FPSCR
0710 LDY # >FPSCR
0720 JSR FLD0R
0730 JSR FDIV
0740 LDX # <FPSCR1
0750 LDY # >FPSCR1
0760 JSR FLD1R
0770 JSR FSUB
0780 LDX # <TLOG+6
0790 LDY # >TLOG+6
0800 JSR FLD1R
0810 JSR FMUL
0820 LDA FR0
0830 BEQ LBL2
0840 LDX # <FPSCR1
0850 LDY # >FPSCR1
0860 JSR FLD1R
0870 JSR FADD
0880 DEC ESIGN
0890 BPL LOOP
0900 LBL2 LDX # <FPSCR1
0910 LDY # >FPSCR1
0920 JSR FLD0R
0930 LDA DIGRT
0940 BEQ END
0950 SEC
0960 SBC #$40
0970 CLC
0980 ROR A
0990 CLC
1000 ADC #$40
1010 AND #$7F
1020 STA FR1
1030 LDA DIGRT
1040 ROR A
1050 LDA #$01
1060 BCC LBL3
1070 LDA #$10
1080 LBL3 STA FR1+1
1090 LDX #$04
1100 LDA #$00
1110 NXT2 STA FR1+2,X
1120 DEX
1130 BPL NXT2
1140 JSR FMUL
1150 END RTS
Druga tablica, umieszczona w interpreterze Atari Basic
zawiera stałą o wartości 2 (2*1000). Stała ta jest
wykorzystywana jako wartość stopnia pierwiastkowania.
0100 ;SQuare Root Constant
0110 ;
0120 *= $BAF1
0130 ;
0140 .BYTE $40,$02
0150 .BYTE $00,$00
0160 .BYTE $00,$00
Konieczna jest tu jeszcze jedna dygresja. Uważny Czytelnik
powinien zauważyć niezgodność adresu procedury SQR z podanym w
pierwszej części "Mapy" ("Podstawowe procedury systemu
operacyjnego"). Prawdziwy jest oczywiście adres podany tu, choć
we wszystkich dostępnych źródłach jest inaczej. Podawany tam
bowiem adres procedury SQR jest adresem w interpreterze Basic
Revision B!, a więc w poprzedniej wersji. Uwaga ta dotyczy
także pozostałych procedur zmiennoprzecinkowych: SIN, COS i
ATAN.
5.2.5. Funkcja EXP
Funkcja EXP zwraca wartość podniesienia liczby e
(2.71828183) do potęgi podanej jako argument. Do realizacji tej
funkcji wykorzystywana jest przez procedurę XEXP standardowa
procedura pakietu FP - EXP. Przedtem jednak argument funkcji
jest pobierany z bufora przez wywołanie GETVAR. Uzyskany wynik
jest natomiast przesyłany do procedury RESULT, gdzie dokonywana
jest ocena jego poprawności i zapisanie do bufora.
0100 ;eXecute EXP function
0110 ;
0120 EXP = $DDC0
0130 GETVAR = $ABE9
0140 RESULT = $B159
0150 ;
0160 *= $B14A
0170 ;
0180 JSR GETVAR
0190 JSR EXP
0200 JMP RESULT
5.2.6. Funkcje LOG i CLOG
Dla podanego argumentu funkcja LOG zwraca wartość logarytmu
naturalnego (przy podstawie e), a funkcja CLOG - logarytmu
dziesiętnego (przy podstawie 10). Wykonanie tych funkcji jest
przeprowadzane przez procedury XLOG i XCLOG. Są one niemal
identyczne, a różni je tylko wywoływana procedura pakietu FP:
dla XLOG jest to LOG, zaś dla XCLOG - LOG10. Najpierw liczba do
logarytmowania jest odczytywana z bufora przez GETVAR i gdy
jest równa zero, to niepoprawna wartość jest sygnalizowana
przez skok do VALER. W przeciwnym przypadku wywoływana jest
odpowiednia procedura zmiennoprzecinkowa. Od tego miejsca
przebieg obu procedur (XLOG i XCLOG) jest wspólny.
0100 ;eXecute LOG function
0110 ;
0120 FR0 = $D4
0130 GETVAR = $ABE9
0140 LOG = $DECD
0150 LOG10 = $DED1
0160 PUTRES = $B16C
0170 VALER = $B15B
0180 ;
0190 *= $B121
0200 ;
0210 XLOG JSR GETVAR
0220 LDA FR0
0230 BEQ VALER
0240 JSR LOG
0250 ;
0260 ;STore LOGarithm
0270 ;
0280 STLOG BCS VALER
0290 LDA FR0
0300 EOR #$3B
0310 BNE PUTRES
0320 LDA FR0+1
0330 AND #$F8
0340 BNE PUTRES
0350 STA FR0
0360 BEQ PUTRES
0370 ;
0380 ;eXecute CLOG function
0390 ;
0400 XCLOG JSR GETVAR
0410 LDA FR0
0420 BEQ VALER
0430 JSR LOG10
0440 JMP STLOG
Przede wszystkim ustawiony bit Carry powoduje sygnalizację
błędu przez skok do VALER. Następnie uzyskany rezultat jest
przetwarzany do poprawnej postaci i procedura jest opuszczana
skokiem do PUTRES.
5.2.7. Funkcja SIN
Funkcja SIN zwraca wartość sinusa podanego argumentu.
Procedura wykonawcza tej funkcji - XSIN - ma standardową
strukturę: odczyt argumentu (GETVAR), wykonanie obliczenia
(SIN) oraz sprawdzenie i zapisanie wyniku (RESULT).
0100 ;eXecute SIN function
0110 ;
0120 GETVAR = $ABE9
0130 RESULT = $B159
0140 SIN = $BE05
0150 ;
0160 *= $B106
0170 ;
0180 JSR GETVAR
0190 JSR SIN
0200 JMP RESULT
Zasadnicza procedura obliczeniowa jest wspólna dla obu
funkcji trygonometrycznych (SIN i COS). Różna jest tylko
wartość początkowa w tej procedurze. Do rejestru FCHRFLG (First
CHaRacter FLaG) jest wpisywana wartość $01 dla cosinusa, $02
dla sinusa liczby ujemnej i $04 dla sinusa liczby dodatniej.
Szczegółowy opis tej procedury zostanie pominięty. Trzeba
jednak zwrócić uwagę na dwie tablice wykorzystywane przez nią.
Tablica TRIGC zawiera wartość 1.
0100 ;TRIGonometric's Constant
0110 ;
0120 *= $BECF
0130 ;
0140 .BYTE $40,$01,$00
0150 .BYTE $00,$00,$00
0100 DIGRT = $F1
0110 FCHRFLG = $F0
0120 FDIV = $DB28
0130 FLD0R = $DD89
0140 FLD1R = $DD98
0150 FMOV01 = $DDB6
0160 FMUL = $DADB
0170 FPSCR = $05E6
0180 FR0 = $D4
0190 FR1 = $E0
0200 FST0R = $DDA7
0210 FSUB = $DA60
0220 PLYEVL = $DD40
0230 RADFLG = $FB
0240 TRIGC = $BECF
0250 TSCC = $BE9F
0260 ;
0270 *= $BE03
0280 ;
0290 ERR SEC
0300 RTS
0310 ;
0320 ;SINus routine
0330 ;
0340 SIN LDA #$04
0350 BIT FR0
0360 BPL EXE
0370 LDA #$02
0380 BNE EXE
0390 ;
0400 ;COSinus routine
0410 ;
0420 COS LDA #$01
0430 EXE STA FCHRFLG
0440 LDA FR0
0450 AND #$7F
0460 STA FR0
0470 LDA #$BD
0480 CLC
0490 ADC RADFLG
0500 TAX
0510 LDY #$BE
0520 JSR FLD1R
0530 JSR FDIV
0540 BCC CNT
0550 RTS
0560 CNT LDA FR0
0570 AND #$7F
0580 SEC
0590 SBC #$40
0600 BMI LBL2
0610 CMP #$04
0620 BPL ERR
0630 TAX
0640 LDA FR0+1,X
0650 STA DIGRT
0660 AND #$10
0670 BEQ LBL1
0680 LDA #$02
0690 LBL1 CLC
0700 ADC DIGRT
0710 AND #$03
0720 ADC FCHRFLG
0730 STA FCHRFLG
0740 STX DIGRT
0750 JSR FMOV01
0760 LDX DIGRT
0770 LDA #$00
0780 LOOP STA FR1+2,X
0790 INX
0800 CPX #$03
0810 BCC LOOP
0820 JSR FSUB
0830 LBL2 LSR FCHRFLG
0840 BCC LBL3
0850 JSR FMOV01
0860 LDX # <TRIGC
0870 LDY # >TRIGC
0880 JSR FLD0R
0890 JSR FSUB
0900 LBL3 LDX # <FPSCR
0910 LDY >FPSCR
0920 JSR FST0R
0930 JSR FMOV01
0940 JSR FMUL
0950 BCS ERR
0960 LDA #$06
0970 LDX # <TSCC
0980 LDY # >TSCC
0990 JSR PLYEVL
1000 LDX # <FPSCR
1010 LDY # >FPSCR
1020 JSR FLD1R
1030 JSR FMUL
1040 LSR FSCHFLG
1050 BCC END
1060 CLC
1070 LDA FR0
1080 BEQ END
1090 EOR #$80
1100 STA FR0
1110 END RTS
Druga tablica (TSCC - stosowana tylko do obliczania funkcji
sinus i cosinus) zawiera siedem wartości dla procedury
przeliczania wielomianowego PLYEVL (znajduje się ona w pakiecie
FP - zob. "Mapa pamięci Atari XL/XE. Podstawowe procedury
systemu operacyjnego"). Są to kolejno liczby: -3.55149939*10-6,
1.60442752*10-4, -4.681754355*10-7, 0.0796926239, -0.6459640867,
1.57079632 i 90.
0100 ;Table: SIN & COS Constants
0110 ;
0120 *= $BE9F
0130 ;
0140 .BYTE $BD,$03,$55
0150 .BYTE $14,$99,$39
0160 .BYTE $3E,$01,$60
0170 .BYTE $44,$27,$52
0180 .BYTE $BC,$46,$81
0190 .BYTE $75,$43,$55
0200 .BYTE $3F,$07,$96
0210 .BYTE $92,$62,$39
0220 .BYTE $BF,$64,$59
0230 .BYTE $64,$08,$67
0240 .BYTE $40,$01,$57
0250 .BYTE $07,$96,$32
0260 .BYTE $40,$90,$00
0270 .BYTE $00,$00,$00
Książka "Mapa pamięci Atari XL/XE. Podstawowe procedury
systemu operacyjnego" podaje adresy procedur SIN i COS
zawartych w poprzedniej wersji interpretera Atari Basic
(Revision B). Uwaga ta dotyczy także pozostałych procedur
zmiennoprzecinkowych: SQR i ATAN.
5.2.8. Funkcja COS
Funkcja COS zwraca wartość cosinusa podanego argumentu. Jej
procedura wykonawcza - XCOS - jest niemal identyczna z
procedurą funkcji SIN (zob. wyżej).
0100 ;eXecute COS function
0110 ;
0120 COS = $BE0F
0130 GETVAR = $ABE9
0140 RESULT = $B159
0150 ;
0160 *= $B10F
0170 ;
0180 JSR GETVAR
0190 JSR COS
0200 JSR RESULT
5.2.9. Funkcja ATN
Funkcja ATN zwraca wartość arcus tangens podanego
argumentu. Także tu procedura wykonawcza - XATN - jest niemal
identyczna z procedurą funkcji SIN (zob. wyżej).
0100 ;eXecute ATN function
0110 ;
0120 ATAN = $BED5
0130 GETVAR = $ABE9
0140 RESULT = $B159
0150 ;
0160 *= $B118
0170 ;
0180 JSR GETVAR
0190 JSR ATN
0200 JSR RESULT
Odmienna jest natomiast właściwa procedura obliczeniowa
ATAN. Ponieważ jej struktura jest bardzo skomplikowana i
zawiera wiele kolejnych przekształceń matematycznych, to opis
zostanie pominięty.
0100 ;Arcus TANgent routine
0110 ;
0120 DIGRT = $F1
0130 FADD = $DA66
0140 FCHRFLG = $F0
0150 FDIV = $DB28
0160 FLD1R = $DD98
0170 FMOV01 = $DDB6
0180 FMUL = $DADB
0190 FPSCR = $05E6
0200 FR0 = $D4
0210 FST0R = $DDA7
0220 PLYEVL = $DD40
0230 RADFLG = $FB
0240 RSQT = $DE95
0250 TATAN = $DFAE
0260 TATNC = $BEC9
0270 ;
0280 *= $BED5
0290 ;
0300 LDA #$00
0310 STA FCHRFLG
0320 STA DIGRT
0330 LDA FR0
0340 AND #$7F
0350 CMP #$40
0360 BMI LBL1
0370 LDA FR0
0380 AND #$80
0390 STA FCHRFLG
0400 INC DIGRT
0410 LDA #$7F
0420 AND FR0
0430 STA FR0
0440 LDX # <TATAN+$3C
0450 LDY # >TATAN+$3C
0460 JSR RSQT
0470 LBL1 LDX # <FPSCR
0480 LDY # >FPSCR
0490 JSR FST0R
0500 JSR FMOV01
0510 JSR FMUL
0520 BCS END
0530 LDA #$0B
0540 LDX # <TATAN
0550 LDY # >TATAN
0560 JSR PLYEVL
0570 BCS END
0580 LDX # <FPSCR
0590 LDY # >FPSCR
0600 JSR FLD1R
0610 JSR FMUL
0620 BCS END
0630 LDA DIGRT
0640 BEQ LBL2
0650 LDX # <TATAN+$42
0660 LDY # >TATAN+$42
0670 JSR FLD1R
0680 JSR FADD
0690 LDA FCHRFLG
0700 ORA FR0
0710 STA FR0
0720 LBL2 LDA RADFLG
0730 BEQ END
0740 LDX # <TATNC
0750 LDY # >TATNC
0760 JSR FLD1R
0770 JSR FDIV
0780 END RTS
Wykorzystywana przez procedurę ATAN tablica TATNC zawiera
stałą liczbową o wartości 0.01745329.
0100 ;Table: ATN's Constant
0110 ;
0120 *= $BEC9
0130 ;
0140 .BYTE $3F,$01,$74
0150 .BYTE $53,$29,$25
Książka "Mapa pamięci Atari XL/XE. Podstawowe procedury
systemu operacyjnego" podaje adres procedury ATAN zawartej w
poprzedniej wersji interpretera Atari Basic (Revision B). Uwaga
ta dotyczy także pozostałych procedur zmiennoprzecinkowych:
SQR, SIN i COS.
5.2.10. Funkcja CHR$
Funkcja CHR$ zwraca znak, którego kod ASCII został podany
jako argument, jej wynikiem jest więc stała tekstowa o długości
jednego znaku. Argument pobrany przez procedurę GETVAR jest
zamieniany przy pomocy GETINT na dwubajtową liczbę całkowitą, a
jej młodszy bajt przepisywany jest do bufora LBUFF (Line
BUFFer). Starszy bajt jest przy tym całkowicie pomijany, więc
argument większy od 255 ($FF) da w rezultacie znak, którego kod
ASCII jest równy reszcie z dzielenia argumentu przez 256.
Trzeba jednak pamiętać, że argument większy od 65535 ($FFFF)
spowoduje błąd podczas realizacji procedury GETINT.
Ponieważ wynik jest zapisywany zawsze w to samo miejsce
bufora LBUFF, to jednocześnie można wykonać tylko jedną funkcję
CHR$. Na przykład, porównanie CHR$(A)=CHR$(B) będzie zawsze
prawdziwe, gdyż przed wykonaniem porównania nowa wartość
funkcji CHR$ skasuje poprzednią.
0100 ;eXecute CHR$ function
0110 ;
0120 FR0 = $D4
0130 GETINT = $AD41
0140 GETVAR = $ABE9
0150 LBUFF = $0580
0160 PUTVAR = $ABB2
0170 VARN = $D3
0180 VART = $D2
0190 ;
0200 *= $B052
0210 ;
0220 JSR GETVAR
0230 JSR GETINT
0240 LDA FR0
0250 STA LBUFF+$40
0260 LDA # >LBUFF+$40
0270 STA FR0+1
0280 LDA # <LBUFF+$40
0290 STA FR0
0300 LDA #$01
0310 STA FR0+2
0320 ;
0330 ;STRing TYPe
0340 ;
0350 STRTYP LDA #$00
0360 STA FR0+3
0370 STA VARN
0380 LDA #$83
0390 STA VART
0400 JMP PUTVAR
Adres znaku w buforze LBUFF umieszczany jest teraz w dwóch
pierwszych bajtach rejestru FR0, a w dwóch następnych wartość
1, która oznacza długość stałej tekstowej. Odpowiednio do
rejestru VARN wpisywane jest zero, a do VART - $83, co oznacza
stałą tekstową. Procedura XCHR kończy się skokiem do PUTVAR, a
więc zapisaniem wyniku do bufora tokenizacji.
5.2.11. Funkcja STR$
Funkcja STR$ zamienia podany argument liczbowy na ciąg
znaków ASCII. Procedura wykonawcza XSTR pobiera liczbę przez
GETVAR i przy pomocy procedury FASC zamienia ją na ciąg znaków.
Adres bufora zawierającego ten ciąg jest wpisywany do rejestru
FR0 (dwa pierwsze bajty). Następnie obliczana jest długość
ciągu, a wynik umieszczany jest w trzecim bajcie FR0. Teraz
następuje skok do etykiety STRTYP (wewnątrz procedury XCHR),
gdzie zapisywany jest numer i typ wartości.
Ta funkcja może być użyta w wyrażeniu tylko jeden raz, gdyż
podobnie jak XCHR, umieszcza wynik zawsze w tym samym miejscu.
Ponadto trzeba uważać przy jednoczesnym użyciu funkcji STR$ i
CHR$, gdyż obie korzystają z bufora LBUFF. Jeśli ciąg wynikowy
funkcji STR$ będzie miał długość większą od 64 znaków, to
zajmie miejsce przeznaczone na wynik funkcji CHR$. Ten
rezultat, który był wcześniej obliczony, będzie więc
niepoprawny.
0100 ;eXecute STR$ function
0110 ;
0120 FASC = $D8E6
0130 FR0 = $D4
0140 GETVAR = $ABE9
0150 INBUFP = $F3
0160 STRTYP = $B069
0170 ;
0180 *= $B034
0190 ;
0200 JSR GETVAR
0210 JSR FASC
0220 LDA INBUFP
0230 STA FR0
0240 LDA INBUFP+1
0250 STA FR0+1
0260 LDY #$FF
0270 LOOP INY
0280 LDA (INBUFP),Y
0290 BPL LOOP
0300 AND #$7F
0310 STA (INBUFP),Y
0320 INY
0330 STY FR0+2
0340 BNE STRTYP
5.2.12. Funkcja USR
Funkcja USR powoduje wywołanie procedury w języku
maszynowym od adresu podanego jako argument. Ponadto dalsze
argumenty - jeśli są - zostają przekazane jako parametry do tej
procedury. Do realizacji tej funkcji służy procedura XUSR.
Zasadnicza operacja odczytu parametrów i wywołania procedury
maszynowej użytkownika jest wykonywana przez PHDAT. Przekazany
przez nią wynik jest zamieniany na liczbę zmiennoprzecinkową
(przez IFP) i przy pomocy procedury PUTVAR zapisywany w
buforze.
0100 ;eXecute USR function
0110 ;
0120 IFP = $D9AA
0130 PHDAT = $B0AE
0140 PUTVAR = $ABB2
0150 ;
0160 *= $B0A5
0170 ;
0180 JSR PHDAT
0190 JSR IFP
0200 JMP PUTVAR
Procedura PHDAT rozpoczyna się od odczytania z rejestru
BHLD1 liczby parametrów funkcji USR i umieszczenia jej w
liczniku ACNT1 (Auxiliary CouNTer 1). Następnie w pętli
sterowanej stanem tego licznika wywoływana jest procedura
GETNUM, która odczytuje kolejne parametry. Są one odkładane na
stosie w kolejności bajtów młodszy/starszy. Na końcu wpisywana
jest na stos liczba parametrów, natomiast ostatnia odczytana
liczba pozostaje w rejestrze FR0. Liczba ta jest wykorzystywana
jako adres skoku kończącego procedurę PHDAT.
0100 ;PusH user's DATa
0110 ;
0120 ACNT1 = $C6
0130 BHLD1 = $B0
0140 FR0 = $D4
0150 GETNUM = $ABDA
0160 ;
0170 *= $B0AE
0180 ;
0190 LDA BHLD1
0200 STA ACNT1
0210 LOOP JSR GETNUM
0220 DEC ACNT1
0230 BMI EXIT
0240 LDA FR0
0250 PHA
0260 LDA FR0+1
0270 PHA
0280 JMP LOOP
0290 EXIT LDA BHLD1
0300 PHA
0310 JMP (FR0)
Wspomniana procedura GETNUM jest częścią składową LETNUM.
Przez wywołanie XLET wartość wyrażenia jest obliczana i
umieszczana w buforze. Teraz (od etykiety GETNUM) z bufora
pobierana jest liczba zmiennoprzecinkowa, którą procedura
GETINT zamienia na dwubajtową liczbę całkowitą.
0100 GETINT = $AD41
0110 GETVAR = $ABE9
0120 XLET = $AADA
0130 ;
0140 *= $ABD7
0150 ;
0160 ;LET NUMber
0170 ;
0180 LETNUM JSR XLET
0190 ;
0200 ;GET NUMber
0210 ;
0220 GETNUM JSR GETVAR
0230 JMP GETINT
5.2.13. Funkcja LEN
Funkcja LEN zwraca wartość długości ciągu podanego jako
argument. Wykonująca tą operację procedura XLEN najpierw
odczytuje przy pomocy procedury FTARV wartość z bufora
tokenizacji i umieszcza ją w rejestrze FR0. Procedura FTARV
jest wywoływana zamiast bezpośredniego wywołania GETVAR,
ponieważ sprawdza ponadto poprawność argumentu. Następnie
wartość określająca aktualną długość ciągu jest przepisywana z
trzeciego i czwartego bajtu rejestru FR0 do jego dwóch
pierwszych bajtów. Przepisanie zawartości akumulatora i
rejestru Y do FR0 zostało oznaczone etykietą STNUM i jest
wykorzystywane także przez inne procedury.
0100 ;eXecute LEN function
0110 ;
0120 FR0 = $D4
0130 FTARV = $AB90
0140 IFP = $D9AA
0150 PUTVAR = $ABB2
0160 VARN = $D3
0170 VART = $D2
0180 ;
0190 *= $AFB5
0200 ;
0210 XLEN JSR FTARV
0220 LDA FR0+2
0230 LDY FR0+3
0240 ;
0250 ;STore NUMber
0260 ;
0270 STNUM STA FR0
0280 STY FR0+1
0290 ;
0300 ;CHange TYPe
0310 ;
0320 CHTYP JSR IFP
0330 ;
0340 ;NUMber TYPe
0350 ;
0360 NUMTYP LDA #$00
0370 STA VART
0380 STA VARN
0390 JMP PUTVAR
Teraz liczba całkowita jest zamieniana na zmienno-
przecinkową (jest to oznaczone przez CHTYP). Po wpisaniu do
rejestrów VART i VARN wartości oznaczających stałą liczbową
procedura XLEN kończy się skokiem do PUTVAR, gdzie wynik
przepisywany jest do bufora.
W celu odczytania adresu ciągu będącego argumentem
procedura FTARV najpierw wywołuje GETVAR. Jeśli zawartość
rejestru VART oznacza stałą tekstową, to procedura FTARV się
kończy. Skasowany bit 0 tego rejestru sygnalizuje natomiast, że
zmienna nie była zadeklarowana i powoduje skok do procedury
błędu DIMER (DIMension ERror).
0100 ;Find Table or ARray Value
0110 ;
0120 DIMER = $B922
0130 FR0 = $D4
0140 GETVAR = $ABE9
0150 STARP = $8C
0160 VART = $D2
0170 ;
0180 *= $AB90
0190 ;
0200 JSR GETVAR
0210 LDA #$02
0220 BIT VART
0230 BNE END
0240 ORA VART
0250 STA VART
0260 ROR A
0270 BCC ERR
0280 CLC
0290 LDA FR0
0300 ADC STARP
0310 STA FR0
0320 TAY
0330 LDA FR0+1
0340 ADC STARP+1
0350 STA FR0+1
0360 END RTS
0370 ERR JSR DIMER
Gdy zmienna jest poprawna, to przez dodanie zawartości
STARP i FR0 obliczany jest jej adres w pamięci. Ta część
procedury jest zbędna przy wykonywaniu funkcji XLEN, a
wykorzystuje ją tylko funkcja XADR (zob. niżej).
5.2.14. Funkcja FRE
Funkcja FRE zwraca wartość określającą wielkość pozostałej
jeszcze, wolnej pamięci RAM. Procedura wykonawcza tej funkcji -
XFRE - rozpoczyna się od wywołania GETVAR w celu odczytania z
bufora argumentu, który nie ma jednak żadnego znaczenia.
Właściwe obliczenie polega na odjęciu zawartości rejestrów
MEMTOP (MEMory TOP) i BMEMHI (Basic MEMory HIgh address).
Uzyskany rezultat określa liczbę bajtów pomiędzy górną granicą
obszaru pamięci zajmowanego przez program w Basicu i dolną
granicą programu ANTIC-a. Doprowadzenie wyniku odejmowania,
który jest dwubajtową liczbą całkowitą do poprawnej postaci,
jest osiągane przez skok do procedury CHTYP (wewnątrz XLEN).
0100 ;eXecute FRE function
0110 ;
0120 BMEMHI = $90
0130 CHTYP = $AFC0
0140 FR0 = $D4
0150 GETVAR = $ABE9
0160 MEMTOP = $02E5
0170 ;
0180 *= $AFD6
0190 ;
0200 JSR GETVAR
0210 SEC
0220 LDA MEMTOP
0230 SBC BMEMHI
0240 STA FR0
0250 LDA MEMTOP8+1
0260 SBC BMEMHI+1
0270 STA FR0+1
0280 JMP CHTYP
Przy wykorzystywaniu wyniku funkcji FRE do określenia, czy
pozostaje wystarczający obszar pamięci na umieszczenie
dodatkowych danych lub procedur w języku maszynowym, trzeba
pamiętać, że górna granica programu Basica jest ruchoma. Każde
otwarcie pętli FOR/NEXT zajmuje 16 bajtów, a każde wywołanie
procedury GOSUB/RETURN - cztery bajty. Dostępny obszar pamięci
należy więc zmniejszyć o liczbę ustaloną w zależności od
maksymalnej liczby jednocześnie aktywnych pętli i procedur.
5.2.15. Funkcja PEEK
Funkcja PEEK zwraca wartość zapisaną w komórce pamięci,
której adres został podany jako argument. W tym celu procedura
GETNUM odczytuje ze stosu argument i zamienia go na liczbę
całkowitą. Liczba ta jest następnie wykorzystywana jako adres
odczytu zawartości komórki pamięci. Doprowadzenie wyniku do
postaci zmiennoprzecinkowej i zapisanie w buforze jest
dokonywane przez procedurę STNUM wchodzącą w skład XLEN (zob.
wyżej).
0100 ;eXecute PEEK function
0110 ;
0120 FR0 = $D4
0130 GETNUM = $ABDA
0140 STNUM = $AFBC
0150 ;
0160 *= $AFCC
0170 ;
0180 JSR GETNUM
0190 LDY #$00
0200 LDA (FR0),Y
0210 JMP STNUM
5.2.16. Funkcja ADR
Funkcja ADR zwraca wartość określającą adres pierwszego
znaku ciągu podanego jako argument. Procedura realizująca tą
operację zawiera jedynie wywołanie FTARV i skok do CHTYP.
Opisywana już wcześniej (zob. 5.2.13) procedura FTARV umieszcza
w dwóch pierwszych bajtach rejestru FR0 adres podanego ciągu.
Adres ten jest przekształcany do postaci stałej liczbowej w
procedurze CHTYP, która znajduje się wewnątrz XLEN.
0100 ;eXecute ADR function
0110 ;
0120 CHTYP = $AFC0
0130 FTARV = $AB90
0140 ;
0150 *= $B007
0160 ;
0170 JSR FTARV
0180 JMP CHTYP
5.2.17. Funkcja ASC
Funkcja ASC zwraca wartość kodu ATASCII pierwszego znaku
ciągu podanego jako argument, jest więc odwrotnością funkcji
CHR$. Jej procedura wykonawcza XASC przebiega podobnie do
procedury XADR. Jednak do CHTYP jest przekazywany nie adres,
lecz odczytany według tego adresu kod pierwszego znaku ciągu.
0100 ;eXecute ASC function
0110 ;
0120 FR0 = $D4
0130 FTARV = $AB90
0140 STNUM = $AFBC
0150 ;
0160 *= $AFFD
0170 ;
0180 JSR FTARV
0190 LDY #$00
0200 LDA (FR0),Y
0210 JMP STNUM
5.2.18. Funkcja VAL
Funkcja VAL zwraca wartość uzyskaną z zamiany na liczbę
cyfr ciągu ASCII podanego jako argument i jest funkcją odwrotną
dla STR$. Realizuje ją procedura XVAL, która najpierw odczytuje
informacje o ciągu przy pomocy GFILSP, a następnie zamienia
odnaleziony ciąg na liczbę zmiennoprzecinkową przez wywołanie
procedury AFP z pakietu FP. Wywołanie procedury PSTMAD jest tu
konieczne tylko dla skasowania znacznika MEOLFLG (Modified EOL
FLaG) ustawionego przez procedurę GFILSP. Poprawny wynik
otrzymuje odpowiedni format i jest zapisywany na stosie przez
skok do procedury NUMTYP, która znajduje się wewnątrz XLEN.
Jeśli podany ciąg nie daje się zamienić na liczbę, to
wykonywany jest skok do CHARER i sygnalizowany jest błąd
(CHARacter ERror).
0100 ;eXecute VAL function
0110 ;
0120 AFP = $D800
0130 CHARER = $B910
0140 CIX = $F2
0150 GFILSP = $BD7D
0160 NUMTYP = $AFC3
0170 PSTMAD = $BD9D
0180 ;
0190 *= $AFEB
0200 ;
0210 JSR GFILSP
0220 LDA #$00
0230 STA CIX
0240 JSR AFP
0250 JSR PSTMAD
0260 BCC NUMTYP
0270 JSR CHARER
Procedura GFILSP najpierw odszukuje podany ciąg przy pomocy
FTARV, a następnie wpisuje jego adres do wektora INBUFP.
Ponadto do rejestru CSTAD przepisywany jest starszy bajt adresu
ciągu i młodszy bajt jego długości. Przed opuszczeniem
procedury znak końca wiersza (RETURN) jest jeszcze umieszczany
na końcu ciągu, a jeśli jego długość jest większa od 256
bajtów, to jako 256 znak.
0100 ;Get FILe SPecification
0110 ;
0120 CSTAD = $97
0130 FR0 = $D4
0140 FTARV = $AB90
0150 INBUFP = $F3
0160 MEOLFLG = $92
0170 ;
0180 *= $BD7D
0190 ;
0200 JSR FTARV
0210 LDA FR0
0220 STA INBUFP
0230 LDA FR0+1
0240 STA INBUFP+1
0250 LDY FR0+2
0260 LDX FR0+3
0270 BEQ PUT
0280 LDY #$FF
0290 PUT LDA (INBUFP),Y
0300 STA CSTAD
0310 STY CSTAD+1
0320 LDA #$9B
0330 STA (INBUFP),Y
0340 STA MEOLFLG
0350 RTS
5.2.19. Funkcje manipulatorów
Atari Basic posiada cztery dodatkowe funkcje, które służą
do odczytu położenia manipulatorów. Funkcja STICK zwraca liczbę
określającą pozycję joysticka, a STRIG - stan jego przycisku.
Podobnie wynik funkcji PADDLE określa stan potencjometru, a
PTRIG - jego przycisku. Argumentem tych funkcji jest numer
manipulatora: 0 lub 1 dla joysticka oraz liczba od 0 do 3 dla
potencjometru. Działanie wszystkich tych funkcji polega na
odczycie zawartości odpowiedniego rejestru. Rejestr jest
wybierany przez zsumowanie jego numeru i odległości od rejestru
pierwszego manipulatora (PADDLE0). Przekształcenia i zapisania
wyniku dokonuje procedura STNUM.
0100 BVALER = $B92E
0110 FR0 = $D4
0120 GETNUM = $ABDA
0130 PADDL0 = $0270
0140 STNUM = $AFBC
0150 ;
0160 *= $B00D
0170 ;
0180 ;eXecute PADDLE function
0190 ;
0200 XPADDL LDA #$00
0210 BEQ GET
0220 ;
0230 ;eXecute STICK function
0240 ;
0250 XSTICK LDA #$08
0260 BNE GET
0270 ;
0280 ;eXecute PTRIG function
0290 ;
0300 XPTRIG LDA #$0C
0310 BNE GET
0320 ;
0330 ;eXecute STRIG function
0340 ;
0350 XSTRIG LDA #$14
0360 GET PHA
0370 JSR GETNUM
0380 LDA FR0+1
0390 BNE ERR
0400 LDA FR0
0410 PLA
0420 CLC
0430 ADC FR0
0440 TAX
0450 LDA PADDL0,X
0460 LDY #$00
0470 BEQ STNUM
0480 ERR JSR BVALER
5.3. Inne operatory
Poza opisanymi wyżej operatorami i funkcjami Atari Basic
posiada jeszcze dodatkowe operatory. Umożliwiają one wykonanie
porównań, operacji logicznych, zmianę priorytetu działań itd. W
wielu przypadkach operatory te są oznaczane jednakowymi
symbolami, na co była już wcześniej zwracana uwaga.
5.3.1. Operatory relacji i logiczne
Operatory logiczne i operatory relacji zwracają wartości
PRAWDA lub FAŁSZ. W Atari Basic wartości logicznej FAŁSZ
odpowiada wartość liczbowa zero, zaś wartości PRAWDA - jeden.
Interpreter Basica zawiera następujące operatory relacji:
mniejsze lub równe (<=), większe lub równe (>=), mniejsze (<),
większe (>), różne (<>) i równe (=). Operatorami logicznymi są
zaś AND (iloczyn logiczny), OR (alternatywa) i NOT (negacja).
Wszystkie procedury wykonawcze tych operatorów są ze sobą
ściśle powiązane, ponadto dołączona jest do nich procedura XSGN
realizująca funkcję SGN.
0100 ;Execute compare operators
0110 ;
0120 AF1 = $DA48
0130 CMPVAR = $AD11
0140 FR0 = $D4
0150 FR1 = $E0
0160 GETVAR = $ABE9
0170 GETVRS = $ABFD
0180 PUTVAR = $ABB2
0190 VART = $D2
0200 ;
0210 *= $ACA3
0220 ;
0230 ;eXecute Less or EQual
0240 ;
0250 XLEQ JSR CMPVAR
0260 BMI ONEP
0270 BEQ ONEP
0280 BPL ZERO
0290 ;
0300 ;eXecute Not EQual
0310 ;
0320 XNEQ JSR CMPVAR
0330 JMP RESLTZ
0340 ;
0350 ;eXecute LESs than
0360 ;
0370 XLES JSR CMPVAR
0380 BMI ONEP
0390 BPL ZERO
0400 ;
0410 ;eXecute GReaTer than
0420 ;
0430 XGRT JSR CMPVAR
0440 BMI ZERO
0450 BEQ ZERO
0460 BPL ONEP
0470 ;
0480 ;eXecute Greater or EQual
0490 ;
0500 XGEQ JSR CMPVAR
0510 BMI ZERO
0520 BPL ONEP
0530 ;
0540 ;eXecute EQUal
0550 ;
0560 XEQU JSR CMPVAR
0570 JMP RESLTO
0580 ;
0590 ;eXecute AND operator
0600 ;
0610 XAND JSR GETVRS
0620 LDA FR0
0630 AND FR1
0640 JMP RESLTZ
0650 ;
0660 ;eXecute OR operator
0670 ;
0680 XOR JSR GETVRS
0690 LDA FR0
0700 ORA FR1
0710 ;
0720 ;RESuLT Zero
0730 ;
0740 RESLTZ BEQ ZERO
0750 BNE ONEP
0760 ;
0770 ;eXecute NOT operator
0780 ;
0790 XNOT JSR GETVAR
0800 LDA FR0
0810 ;
0820 ;RESuLT One
0830 ;
0840 RESLTO BEQ ONEP
0850 ;
0860 ;result ZERO
0870 ;
0880 ZERO LDA #$00
0890 TAY
0900 BEQ STRES
0910 ;
0920 ;result ONE Positive
0930 ;
0940 ONEP LDA #$40
0950 ;
0960 ;result ONE Negative
0970 ;
0980 ONEN LDY #$01
0990 ;
1000 ;STore RESult
1010 ;
1020 STRES STA FR0
1030 STY FR0+1
1040 LDX #FR0+2
1050 LDY #$04
1060 JSR AF1
1070 STA VART
1080 END JMP PUTVAR
1090 ;
1100 ;eXecute SGN function
1110 ;
1120 XSGN JSR GETVAR
1130 LDA FR0
1140 BEQ END
1150 BPL ONEP
1160 LDA #$C0
1170 BMI ONEN
Określenie relacji między dwoma argumantami rozpoczyna się
zawsze od wywołania procedury CMPVAR, która dokonuje porównania
odpowiednich wartości, a wynik sygnalizuje w odpowiednich
bitach statusu procesora. Zależnie od rodzaju porównania i
stanu bitów Negative i Zero wykonywane są skoki do właściwych
fragmentów procedury, gdzie ustalane są wartości dwóch
pierwszych bajtów wyniku. Pozostałe bajty rejestru FR0 są
zerowane przez procedurę AF1 i rezultat przepisywany jest do
bufora przez procedurę PUTVAR.
Nieco inaczej przebiega obliczenie wyniku operacji
logicznych. Procedury GETVRS (dla AND i OR) lub GETVAR (dla
NOT) pobierają z bufora argumenty, na których pierwszym bajcie
wykonywana jest odpowiednia operacja logiczna. Oznacza to, że
operacje logiczne Atari Basic rozróżniają tylko dwa stany: zero
i niezero. Opracowanie wyniku i jego zapisanie są analogiczne
jak dla operatorów relacji.
W opisywanym zespole procedur znajduje się również
procedura XSGN, realizująca funkcję SGN, która zwraca znak
podanego argumentu. W zależności od wartości najstarszego bitu
liczby pobranej przez GETVAR następuje skok do odpowiedniej
etykiety i zapisanie wyniku: 0, 1 lub -1.
Samo porównanie wartości jest przeprowadzane przez
procedurę CMPVAR. Najpierw odczytuje ona token operatora. Jeśli
jest to token operacji na wartościach tekstowych, to wykonywany
jest skok do procedury CMPSTR. W przeciwnym przypadku
porównywane liczby są odczytywane z bufora i, przez wywołanie
BSUB, odejmowane od siebie. Znak wyniku tego działania jest
umieszczany w akumulatorze i procedura się kończy.
0100 ;CoMPare VARiables
0110 ;
0120 BSUB = $AD2C
0130 CMPSTR = $AF6C
0140 FR0 = $D4
0150 GETVRS = $ABFD
0160 LOMEM = $80
0170 STIX = $A9
0180 ;
0190 *= $AD11
0200 ;
0210 LDY STIX
0220 DEY
0230 LDA (LOMEM),Y
0240 CMP #'/
0250 BCC NUM
0260 JMP CMPSTR
0270 NUM JSR GETVRS
0280 ;
0290 ;GET SUBtract
0300 ;
0310 GETSUB JSR BSUB
0320 LDA FR0
0330 RTS
W przypadku porównania ciągów tekstowych, najpierw - przez
dwukrotne wywołanie procedury FTARV - odczytywane są informacje
o nich. Następnie rozpoczyna się pętla, w której kolejno
porównywany jest znak po znaku. Jeśli znaki są równe, to adresy
kontrolowanych znaków są zwiększane i pętla się powtarza. Gdy
znaki są różne, to wynik porównania jest zapisywany do
akumulatora (jak w przypadku porównania liczb) i procedura się
kończy. Pętla jest także przerywana, gdy wszystkie znaki są
jednakowe, a został osiągnięty koniec jednego lub obu ciągów.
Jeśli obu, to sygnalizowana jest ich równość. W przeciwnym
przypadku ciąg dłuższy zostaje uznany za większy. Licznikiem
jest tu długość ciągu zapisana w trzecim i czwartym bajcie
rejestrów FR0 i FR1.
0100 ;CoMPare STRings
0110 ;
0120 FMOV01 = $DDB6
0130 FR0 = $D4
0140 FR1 = $E0
0150 FTARV = $AB90
0160 ;
0170 *= $AF6C
0180 ;
0190 CMPSTR JSR FTARV
0200 JSR FMOV01
0210 JSR FTARV
0220 LOOP LDX #FR0+2
0230 JSR DECREG
0240 PHP
0250 LDX #FR1+2
0260 JSR DECREG
0270 BEQ CST
0280 PLP
0290 BEQ MNS
0300 LDY #$00
0310 LDA (FR0),Y
0320 CMP (FR1),Y
0330 BEQ IF0
0340 BCC MNS
0350 PLS LDA #$01
0360 RTS
0370 MNS LDA #$80
0380 RTS
0390 CST PLP
0400 BNE PLS
0410 RTS
0420 IF0 INC FR0
0430 BNE IF1
0440 INC FR0+1
0450 IF1 INC FR1
0460 BNE LOOP
0470 INC FR1+1
0480 BNE LOOP
0490 ;
0500 ;DECrease REGister
0510 ;
0520 DECREG LDA $00,X
0530 BNE SKIP
0540 LDA $01,X
0550 BEQ END
0560 DEC $01,X
0570 SKIP DEC $00,X
0580 TAY
0590 END RTS
5.3.2. Operatory przypisania
W interpreterze Atari Basic występują dwa operatory
przypisania (oba oznaczone znakiem "="): przypisanie liczbowe i
przypisanie tekstowe. Służą one odpowiednio do nadania wartości
zmiennym liczbowym i tekstowym. Pomimo jednakowego symbolu mają
one inne tokeny i są realizowane przez inne procedury.
Procedurą wykonawczą przypisania liczbowego jest XNLET.
Przypisanie liczby musi być ostatnią operacją w obliczaniu
wyrażenia. Dlatego najpierw sprawdzana jest wartość licznika
STIX. Jeśli jest ona różna od $FF, to oznacza zmienną
indeksowaną i konieczność obliczenia indeksów. W takim razie
rejestr BHLD2 otrzymuje wartość $80 i procedura jest
przerywana. W przeciwnym przypadku obie wartości są pobierane z
bufora przez GETVRS, a następnie obliczona wartość liczby jest
przepisywana do rejestru zmiennej liczbowej. Teraz XNLET kończy
się skokiem do procedury SAVVAL.
0100 ;eXecute Number LET mark
0110 ;
0120 BHLD2 = $B1
0130 FR0 = $D4
0140 FR1 = $E0
0150 GETVRS = $ABFD
0160 SAVVAL = $AC0C
0170 STIX = $A9
0180 ;
0190 *= $AD4A
0200 ;
0210 LDA STIX
0220 CMP #$FF
0230 BNE EXIT
0240 JSR GETVRS
0250 LDX #$05
0260 LOOP LDA FR1,X
0270 STA FR0,X
0280 DEX
0290 BPL LOOP
0300 JMP SAVVAL
0310 EXIT LDA #$80
0320 STA BHLD2
0330 RTS
W tej procedurze według numeru zmiennej z rejestru VARN
obliczany jest jej adres w tablicy wartości zmiennych (przez
wywołanie CALPC). Cała zmienna (rejestry VARN, VART i FR0 -
razem osiem bajtów) jest przepisywana w pętli do obliczonego
miejsca i procedura się kończy.
0100 ;SAVe VALue
0110 ;
0120 CALPC = $AC1E
0130 PCNTC = $9D
0140 VARN = $D3
0150 VART = $D2
0160 ;
0170 *= $AC0C
0180 ;
0190 LDA VARN
0200 JSR CALPC
0210 LDX #$00
0220 LOOP LDA VART,X
0230 STA (PCNTC),Y
0240 INY
0250 INX
0260 CPX #$08
0270 BCC LOOP
0280 RTS
Znacznie bardziej skomplikowana jest procedura XSLET
realizująca operację przypisania tekstowego. Najpierw
wywoływana jest procedura FTARV i odczytany przez nią adres
ciągu jest umieszczany w rejestrze OLDMHI, a jego długość w
rejestrze MRANGE. Jeśli jest to ostatni operator wyrażenia, to
przez ponowne wywołanie FTARV odczytywana jest maksymalna
długość ciągu. Jeśli nie, to wywołanie procedury PRCOP powoduje
obliczenie początkowego adresu przypisania. Przekroczenie
zadeklarowanej długości nie powoduje błędu, a nadmiar znaków
jest po prostu pomijany. Następnie obliczany jest adres w
tablicy STARP, pod którym powinny znaleźć się znaki ciągu, i
odpowiedni obszar pamięci jest przepisywany przez procedurę
MOVMEM. Jeżeli wszystkie czynności przebiegły poprawnie, to po
zapisaniu nowej długości ciągu wykonywany jest skok do
procedury SAVVAL. Próba przypisania wartości niezadeklarowanej
zmiennej tekstowej powoduje natomiast ustawienie bitu Carry
przed końcem procedury.
0100 ;eXecute String LET mark
0110 ;
0120 BHLD2 = $B1
0130 FR0 = $D4
0140 FTARV = $AB90
0150 MOVMEM = $A944
0160 MRANGE = $A2
0170 NEWMHI = $9B
0180 OLDMHI = $99
0190 PRCOP = $AB04
0200 SAVVAL = $AC0C
0210 STARP = $8C
0220 STIX = $A9
0230 VARBL = $AB81
0240 VARN = $D3
0250 ZTEMP3 = $F9
0260 ;
0270 *= $AE8E
0280 ;
0290 JSR FTARV
0300 LDA FR0
0310 STA OLDMHI
0320 LDA FR0+1
0330 STA OLDMHI+1
0340 LDA FR0+2
0350 STA MRANGE
0360 LDY FR0+3
0370 STY MRANGE+1
0380 LDY STIX
0390 CPY #$FF
0400 BEQ MAX
0410 LDA #$80
0420 STA BHLD2
0430 JSR PRCOP
0440 LDA FR0+3
0450 LDY FR0+2
0460 ROL BHLD2
0470 BCS BPS
0480 MAX JSR FTARV
0490 LDA FR0+5
0500 LDY FR0+4
0510 BPS CMP MRANGE+1
0520 BCC STR
0530 BNE ADD
0540 CPY MRANGE
0550 BCS ADD
0560 STR STA MRANGE+1
0570 STY MRANGE
0580 ADD CLC
0590 LDA FR0
0600 ADC MRANGE
0610 TAY
0620 LDA FR0+1
0630 ADC MRANGE+1
0640 TAX
0650 SEC
0660 TYA
0670 SBC STARP
0680 STA ZTEMP3
0690 TXA
0700 SBC STARP+1
0710 STA ZTEMP3+1
0720 SEC
0730 LDA #$00
0740 SBC MRANGE
0750 STA MRANGE
0760 SEC
0770 LDA OLDMHI
0780 SBC MRANGE
0790 STA OLDMHI
0800 LDA OLDMHI+1
0810 SBC $00
0820 STA OLDMHI+1
0830 SEC
0840 LDA FR0
0850 SBC MRANGE
0860 STA NEWMHI
0870 LDA FR0+1
0880 SBC #$00
0890 STA NEWMHI+1
0900 JSR MOVMEM
0910 LDA VARN
0920 JSR VARBL
0930 SEC
0940 LDA ZTEMP3
0950 SBC FR0
0960 TAY
0970 LDA ZTEMP3+1
0980 SBC FR0+1
0990 TAX
1000 LDA #$02
1010 AND BHLD2
1020 BEQ SAVE
1030 LDA #$00
1040 STA BHLD2
1050 CPX FR0+3
1060 BCC EXIT
1070 BNE SAVE
1080 CPY FR0+2
1090 BCS SAVE
1100 EXIT RTS
1110 SAVE STY FR0+2
1120 STX FR0+3
1130 JMP SAVVAL
5.3.3. Operatory nawiasów
Ważną rolę w wyrażeniach pełnią nawiasy. Służą one do
wydzielenia indeksów w instrukcjach deklaracji zmiennych
tablicowych (indeksowych i tekstowych) oraz przypisania
wartości tym zmiennym. Także w tym przypadku jednakowym
oznaczeniom odpowiadają różne tokeny i procedury wykonawcze.
Napotkanie tokena oznaczającego nawias zmiennej
indeksowanej powoduje wywołanie procedury XPIXV. Niemal
identyczny jest w początkowym fragmencie przebieg procedury
wywoływanej po rozpoznaniu tokena nawiasu w instrukcji
deklarującej zmienną tablicową - XPDIM.
W przypadku deklaracji na początku procedury XPDIM w
rejestrze BHLD2 umieszczana jest wartość $40, która służy
później do rozpoznania rodzaju nawiasu. Następnie z bufora
pobierane są przy pomocy GETNUM liczby określające wymiar lub
indeks zmiennej. Jeśli przy tym zawartość rejestru BHLD1 jest
różna od zera, to są to dwie liczby, a gdy jest równa zero, to
tylko jedna. Do rejestru CSTAD wpisywany jest pierwszy wymiar
(jeśli istnieje) lub zero. Drugi wymiar przepisywany jest
zawsze do rejestru ZTEMP1. Przekroczenie przez jeden z wymiarów
wartości $7FFF powoduje sygnalizację błędu przez skok do
procedury DIMER (DIMension ERror). Następnie odczytywana jest
nazwa zmiennej (przez wywołanie GETVAR). Teraz możliwe są trzy
sytuacje. Jeżeli jest to deklaracja, to procedura się kończy
przez RTS. Jeżeli jest to nawias indeksu zmiennej, która nie
była przedtem deklarowana (skasowany bit 0 rejestru VART), to
sygnalizowany jest błąd. Dalsze kontynuowanie procedury
następuje jedynie w przypadku nawiasu zmiennej indeksowanej,
która została wcześniej deklarowana.
0100 AUXBR = $AF
0110 BHLD1 = $B0
0120 BHLD2 = $B1
0130 CSTAD = $97
0140 DIMER = $B922
0150 EVZTMP = $AF48
0160 FR0 = $D4
0170 GETNUM = $ABDA
0180 GETVAR = $ABE9
0190 PUTVAR = $ABB2
0200 STARP = $8C
0210 STPTR = $AA
0220 VART = $D2
0230 ZT1ADD = $AF3D
0240 ZT1ML6 = $AF31
0250 ZTEMP1 = $F5
0260 ;
0270 *= $AD6D
0280 ;
0290 ;eXecute Parenthese of DIMension
0300 ;
0310 XPDIM LDA #$40
0320 STA BHLD2
0330 ;
0340 ;eXecute Parenthese of IndeX Variable
0350 ;
0360 XPIXV BIT BHLD2
0370 BPL GET1
0380 LDA STPTR
0390 STA AUXBR
0400 DEC STPTR
0410 GET1 LDA #$00
0420 TAY
0430 CMP BHLD1
0440 BEQ GET2
0450 DEC BHLD1
0460 JSR GETNUM
0470 LDA FR0+1
0480 BMI ERR
0490 LDY FR0
0500 GET2 STA CSTAD+1
0510 STY CSTAD
0520 JSR GETNUM
0530 LDA FR0
0540 STA ZTEMP1
0550 LDA FR0+1
0560 BMI ERR
0570 STA ZTEMP1+1
0580 JSR GETVAR
0590 BIT BHLD2
0600 BVC CVT
0610 LDA #$00
0620 STA BHLD2
0630 RTS
0640 CVT ROR VART
0650 BCS FDIM
0660 ERR JSR DIMER
0670 FDIM LDA ZTEMP1+1
0680 CMP FR0+3
0690 BCC SDIM
0700 BNE ERR
0710 LDA ZTEMP1
0720 CMP FR0+2
0730 BCS ERR
0740 SDIM LDA CSTAD+1
0750 CMP FR0+5
0760 BCC MEM
0770 BNE ERR
0780 LDA CSTAD
0790 CMP FR0+4
0800 BCS ERR
0810 MEM JSR EVZTMP
0820 LDA CSTAD
0830 LDY CSTAD+1
0840 JSR ZT1ADD
0850 JSR ZT1ML6
0860 LDA FR0
0870 LDY FR0+1
0880 JSR ZT1ADD
0890 LDA STARP
0900 LDY STARP+1
0910 JSR ZT1ADD
0920 BIT BHLD2
0930 BPL SAV
0940 LDA AUXBR
0950 STA STPTR
0960 JSR GETVAR
0970 LDY #$05
0980 NXT1 LDA FR0,Y
0990 STA (ZTEMP1),Y
1000 DEY
1010 BPL NXT1
1020 INY
1030 STY BHLD2
1040 RTS
1050 SAV LDY #$05
1060 NXT2 LDA (ZTEMP1),Y
1070 STA FR0,Y
1080 DEY
1090 BPL NXT2
1100 INY
1110 STY VART
1120 JMP PUTVAR
Odczytanie odpowiedniej wartości zmiennej indeksowanej
rozpoczyna się od porównania podanych wymiarów z wymiarami
zadeklarowanymi. Przekroczenie tych ostatnich powoduje także
skok do DIMER. Jeśli dotąd wszystko przebiegło poprawnie, to
wywoływana jest procedura EVZTMP, która przelicza zawartość
rejestru ZTEMP1, aby umożliwić odszukanie w pamięci właściwego
elementu.
0100 ;EValuate ZTEMP registers
0110 ;
0120 FRE = $DA
0130 ZTEMP1 = $F5
0140 ZTEMP2 = $F7
0150 ZTEMP3 = $F9
0160 ;
0170 *= $AF48
0180 ;
0190 LDA #$00
0200 STA ZTEMP2
0210 STA ZTEMP2+1
0220 LDY #$10
0230 LOOP LDA ZTEMP1
0240 LSR A
0250 BCC SKIP
0260 CLC
0270 LDX #$FE
0280 NXT1 LDA ZTEMP3,X
0290 ADC FRE,X
0300 STA ZTEMP3,X
0310 INX
0320 BNE NXT1
0330 SKIP LDX #$03
0340 NXT2 ROR ZTEMP1,X
0350 DEX
0360 BPL NXT2
0370 DEY
0380 BNE LOOP
0390 RTS
Następnie, przez kolejne wywołania procedur ZT1ML6 i ZT1ADD
z różnymi parametrami w akumulatorze i rejestrze Y, adres
elementu zmiennej jest obliczany jako suma adresu w tablicy
wartości tej zmiennej, indeksu tablicy od początku tablicy
STARP oraz adresu początkowego STARP.
0100 ZTEMP1 = $F5
0110 ;
0120 *= $AF31
0130 ;
0140 ;ZTEMP1 MuLtiple by 6
0150 ;
0160 ZT1ML6 ASL ZTEMP1
0170 ROL ZTEMP1+1
0180 LDY ZTEMP1+1
0190 LDA ZTEMP1
0200 ASL ZTEMP1
0210 ROL ZTEMP1+1
0220 ;
0230 ;ZTEMP1 ADDition
0240 ;
0250 ZT1ADD CLC
0260 ADC ZTEMP1
0270 STA ZTEMP1
0280 TYA
0290 ADC ZTEMP1+1
0300 STA ZTEMP1+1
0310 RTS
Gdy element został już odszukany, to dalsze czynności
zależą od zawartości rejestru BHLD2. Wartość ujemna oznacza
zapis elementu, a dodatnia jego odczyt. W pierwszym przypadku
liczba z rejestru FR0 jest przepisywana pod wskazany adres i
procedura kończy się przez RTS. Podobnie w drugim przypadku
liczba przepisywana jest z odnalezionego miejsca do rejestru
FR0, lecz na końcu następuje skok do procedury PUTVAR, która
zapisuje uzyskany wynik do bufora.
Token nawiasu wyrażenia tekstowego powoduje wywołanie
procedury XPSEX. Znaczna jej część jest przeznaczona na
sprawdzenie zgodności wymiarów użytych ciągów tekstowych.
Sposób porównania jest przy tym regulowany przez rejestry BHLD1
i BHLD2. Pierwszy z nich zawiera liczbę wartości użytych do
określenia fragmentu ciągu, a drugi rodzaj operacji - zapis czy
odczyt (jak w XPIXV). Przy zapisie podana długość ciągu jest
porównywana z zadeklarowaną (piąty i szósty bajt rejestru FR0),
a przy odczycie - z długością aktualną (trzeci i czwarty bajt
FR0). Jeśli długość ciągu okaże się niepoprawna, to błąd jest
sygnalizowany przez procedurę SLENER (String LENgth ERror).
Na zakończenie procedura FTARV odczytuje adres ciągu w
pamięci. Po dodaniu adresu elementu w ciągu otrzymany
bezwzględny adres jest zapisywany do FR0. Przy odczycie jest to
już koniec, a przy zapisie następuje skok do PUTVAR, gdzie
współrzędne elementu są przepisywane do bufora.
0100 ;eXecute Parenthesis of String Expression
0110 ;
0120 BHLD1 = $B0
0130 BHLD2 = $B1
0140 CSTAD = $97
0150 FR0 = $D4
0160 FTARV = $AB90
0170 GETSLN = $AE81
0180 GETVAR = $ABE9
0190 PUTVAR = $ABB2
0200 SLENER = $B92A
0210 ZTEMP1 = $F5
0220 ;
0230 *= $AE11
0240 ;
0250 LDA BHLD1
0260 BEQ GLN
0270 JSR GETSLN
0280 STY CSTAD+1
0290 STA CSTAD
0300 GLN JSR GETSLN
0310 SEC
0320 SBC #$01
0330 STA ZTEMP1
0340 TYA
0350 SBC #$00
0360 STA ZTEMP1+1
0370 JSR GETVAR
0380 LDA BHLD2
0390 BPL SCD
0400 ORA BHLD1
0410 STA BHLD2
0420 LDY FR0+5
0430 LDA FR0+4
0440 JMP CONT
0450 SCD LDA FR0+2
0460 LDY FR0+3
0470 COND LDX BHLD1
0480 BEQ SBZ
0490 DEC BHLD1
0500 CPY CSTAD+1
0510 BCC ERR
0520 BNE RAD
0530 CMP CSTAD
0540 BCC ERR
0550 RAD LDY CSTAD+1
0560 LDA CSTAD
0570 SBZ SEC
0580 SBC ZTEMP1
0590 STA FR0+2
0600 TAX
0610 TYA
0620 SBC ZTEMP1+1
0630 STA FR0+3
0640 BCC ERR
0650 TAY
0660 BNE ADZ
0670 TXA
0680 BEQ ERR
0690 ADZ JSR FTARV+3
0700 CLC
0710 LDA FR0
0720 ADC ZTEMP1
0730 STA FR0
0740 LDA FR0+1
0750 ADC ZTEMP1+1
0760 STA FR0+1
0770 BIT BHLD2
0780 BPL PUT
0790 RTS
0800 PUT JMP PUTVAR
0810 ERR JSR SLENER
W opisanej powyżej procedurze odczyt indeksu długości ciągu
jest wykonywany przez pomocniczą procedurę GETSLN. Przez
wywołanie GETNUM pobiera ona z bufora liczbę całkowitą i
sprawdza jej wartość. Jeśli liczba ta jest równa zero (oba
bajty zerowe), to przez skok do SLENER sygnalizowany jest błąd.
0100 ;GET String LeNgth
0110 ;
0120 FR0 = $D4
0130 GETNUM = $ABDA
0140 ;
0150 *= $AE81
0160 ;
0170 JSR GETNUM
0180 LDA FR0
0190 LDY FR0+1
0200 BNE END
0210 TAX
0220 BEQ $AE7E ;JSR SLENER
0230 END RTS
Ostatnia procedura jest wspólna dla przecinka
oddzielającego indeksy zmiennej tablicowej (XCOM) oraz dla
nawiasu zamykającego (XEPAR). Zdejmuje ona jedynie ze stosu
dwie wartości (adres powrotny) i kończy się skokiem do
procedury PRCOP wywołującej realizacyjną procedurę operatora
lub funkcji. W przypadku przecinka zwiększana jest jeszcze
zawartość rejestru BHLD1, który określa liczbę wartości użytych
dla wskazania indeksu zmiennej. Należy tu zwrócić uwagę, że
procedura XCOM jest wykonywana TYLKO dla przecinka w indeksie
zmiennej tablicowej, a nie dla przecinków zastosowanych w
innych miejscach.
0100 BHLD1 = $B0
0110 PRCOP = $AB04
0120 STIX = $A9
0130 ;
0140 *= $AD64
0150 ;
0160 ;eXecute index COMma
0170 ;
0180 XCOM INC BHLD1
0190 ;
0200 ;eXecute End PARenthesis
0210 ;
0220 XEPAR LDY STIX
0230 PLA
0240 PLA
0250 JMP PRCOP
|