Powrót do spisu treści

Rozdział 4

INSTRUKCJA PRZYPISANIA

    W każdym programie najczęściej wykonywana jest instrukcja przypisania (LET). Od niej zaczniemy więc opis działania poszczególnych procedur wykonawczych Atari Basic. Ponieważ jest ona często wykorzystywana przez procedury pozostałych instrukcji, a sama wykorzystuje procedury operatorów i funkcji, to jej opis został umieszczony w oddzielnym rozdziale.
            0100 ;eXecute LET statement
            0110 ;
            0120 EXEOP = $AB18
            0130 LOMEM = $80
            0140 OPCT =  $AC35
            0150 PUTVAR = $ABB2
            0160 RECVAL = $AB36
            0170 RSTPTR = $AB26
            0180 STIX =  $A9
            0190 TIX =   $AC
            0200 TOX =   $AB
            0210 ;
            0220     *=  $AADA
            0230 ;
            0240 XLET JSR RSTPTR
            0250 LOOP JSR RECVAL
            0260     BCS OPER
            0270     JSR PUTVAR
            0280     BMI LOOP
            0290 OPER STA TOX
            0300     TAX
            0310     LDA OPCT-$10,X
            0320     LSR A
            0330     LSR A
            0340     LSR A
            0350     LSR A
            0360     STA TIX
            0370 GETT LDY STIX
            0380     LDA (LOMEM),Y
            0390     TAX
            0400     LDA OPCT-$10,X
            0410     AND #$0F
            0420     CMP TIX
            0430     BCC SAVT
            0440     TAX
            0450     BEQ $AB35   ;RTS
            0460 ;
            0470 ;PRoCeed OPerator
            0480 ;
            0490 PRCOP LDA (LOMEM),Y
            0500     INC STIX
            0510     JSR EXEOP
            0520     JMP GETT
            0530 SAVT LDA TOX
            0540     DEY
            0550     STA (LOMEM),Y
            0560     STY STIX
            0570     JMP LOOP
    Pierwszym krokiem jest odpowiednie ustawienie liczników i innych rejestrów. W tym celu wywoływana jest procedura RSTPTR. Zeruje ona rejestry STPTR (STack PoinTeR), BHLD1 i BHLD2 (Basic HoLD register) oraz wpisuje wartość $FF do licznika STIX (STack IndeX). Ponadto na koniec bufora tokenizacji wpisywana jest wartość $11.
            0100 ;ReSeT PoinTeRs
            0110 ;
            0120 BHLD1 = $B0
            0130 BHLD2 = $B1
            0140 LOMEM = $80
            0150 STIX =  $A9
            0160 STPTR = $AA
            0170 ;
            0180     *=  $AB26
            0190 ;
            0200     LDY #$FF
            0210     LDA #$11
            0220     STA (LOMEM),Y
            0230     STY STIX
            0240     INY
            0250     STY BHLD1
            0260     STY STPTR
            0270     STY BHLD2
            0280 ;
            0290 ;eXecute PLUS sign
            0300 ;
            0310 XPLS RTS
    Teraz rozpoczyna się główna pętla procedury przypisania XLET. Wywoływana jest tu procedura RECVAL, której zadaniem jest rozpoznanie wartości do przypisania. Najpierw odczytywany jest token do rozpoznania. Wartość tego tokena większa od $7F oznacza zmienną i powoduje skok do etykiety VARBL umieszczonej na końcu procedury. Wartość $0F oznacza stałą tekstową, a $0E - stałą liczbową. W tych przypadkach wykonywane są odpowiednie fragmenty procedury. Każdy inny token powoduje opuszczenie RECVAL z ustawionym bitem Carry, co sygnalizuje, że badany token nie oznacza wartości (stałej lub zmiennej).
            0100 ;RECognize VALue
            0110 ;
            0120 CALPC = $AC1E
            0130 FR0 =   $D4
            0140 INIX =  $A8
            0150 PCNTC = $9D
            0160 STMCUR = $8A
            0170 VARN =  $D3
            0180 VART =  $D2
            0190 ;
            0200     *=  $AB36
            0210 ;
            0220 RECVAL LDY INIX
            0230     INC INIX
            0240     LDA (STMCUR),Y
            0250     BMI VARBL
            0260     CMP #$0F
            0270     BCC NUMBER
            0280     BEQ TEXT
            0290     RTS
            0300 ;
            0310 ;NUMBER
            0320 ;
            0330 NUMBER LDX #$00
            0340 NXT1 INY
            0350     LDA (STMCUR),Y
            0360     STA FR0,X
            0370     INX
            0380     CPX #$06
            0390     BCC NXT1
            0400     INY
            0410     LDA #$00
            0420     TAX
            0430     BEQ STORE
            0440 TEXT INY
            0450     LDA (STMCUR),Y
            0460     LDX #STMCUR
            0470 ;
            0480 ;STore TeXT
            0490 ;
            0500 STTXT STA FR0+2
            0510     STA FR0+4
            0520     INY
            0530     TYA
            0540     CLC
            0550     ADC $00,X
            0560     STA FR0
            0570     LDA #$00
            0580     STA FR0+3
            0590     STA FR0+5
            0600     ADC $01,X
            0610     STA FR0+1
            0620     TYA
            0630     ADC FR0+2
            0640     TAY
            0650     LDX #$00
            0660     LDA #$83
            0670 STORE STA VART
            0680     STX VARN
            0690     STY INIX
            0700     CLC
            0710     RTS
            0720 ;
            0730 ;VARiaBLe
            0740 ;
            0750 VARBL JSR CALPC
            0760 NXT2 LDA (PCNTC),Y
            0770     STA VART,Y
            0780     INY
            0790     CPY #$08
            0800     BCC NXT2
            0810     CLC
            0820     RTS
    Jeśli rozpoznana została stała liczbowa, to jej sześć bajtów jest przepisywane z tablicy instrukcji do rejestru operacji zmiennoprzecinkowych FR0. Po rozpoznaniu stałej tekstowej jej długość jest zapisywana do trzeciego i piątego bajtu rejestru FR0, a w dwóch pierwszych bajtach zapisywany jest adres pierwszego znaku stałej. Taki format zapisu odpowiada dokładnie zapisowi zmiennej tekstowej w tablicy wartości zmiennych. Na zakończenie do rejestru VARN (VARiable Number) wpisywane jest zero, co oznacza stałą, a do rejestru VART (VARiable Type) - $00 dla stałej liczbowej lub $83 dla stałej tekstowej.

    W przypadku zmiennej najpierw wywoływana jest procedura CALPC, która oblicza adres zmiennej w tablicy wartości. W tym celu numer zmiennej (bez najstarszego bitu) jest mnożony przez osiem (długość wpisu w tablicy wartości zmiennych) i dodawany do adresu tej tablicy. Uzyskany wynik umieszczany jest w liczniku PCNTC.
            0100 ;CALculate Program Counter
            0110 ;
            0120 PCNTC = $9D
            0130 VVTP =  $86
            0140 ;
            0150     *=  $AC1E
            0160 ;
            0170     LDY #$00
            0180     STY PCNTC+1
            0190     ASL A
            0200     ASL A
            0210     ROL PCNTC+1
            0220     ASL A
            0230     ROL PCNTC+1
            0240     CLC
            0250     ADC VVTP
            0260     STA PCNTC
            0270     LDA VVTP+1
            0280     ADC PCNTC+1
            0290     STA PCNTC+1
            0300     RTS
    Następnie według obliczonego adresu wartość zmiennej jest odczytywana z tablicy i przepisywana do rejestru operacji zmiennoprzecinkowych. Po wykonaniu tej czynności w rejestrze VART znajduje się kod typu zmiennej, a w rejestrze VARN - jej numer. Rejestr FR0 zawiera wartość zmiennej w przypadku prostej zmiennej liczbowej lub parametry zmiennej w przypadku zmiennej tablicowej.

    W przypadku napotkania wartości w instrukcji przypisania wywoływana jest procedura PUTVAR. Przepisuje ona znalezioną wartość z rejestrów VART, VARN i FR0 (razem osiem bajtów) do bufora tokenizacji. Odpowiednio zwiększany jest przy tym licznik STPTR. Jeśli nastąpi przepełnienie bufora, co jest sprawdzane na początku procedury, to następuje skok do STOVER i sygnalizowany jest błąd (STack OVerflow ERror). Wynika z tego, że jednocześnie można zapisać w buforze 256/8=32 wartości. Nie jest to jednak prawda, gdyż część bufora zajmują tokeny operatorów i funkcji, a ponadto podczas obliczania wyrażenia niektóre wartości i tokeny mogą być usuwane z bufora jeszcze przed zapisaniem następnych.
            0100 ;PUT VARiable
            0110 ;
            0120 LOMEM = $80
            0130 STIX =  $A9
            0140 STOVER = $B920
            0150 STPTR = $AA
            0160 VART =  $D2
            0170 ;
            0180     *=  $ABB2
            0190 ;
            0200     INC STPTR
            0210     LDA STPTR
            0220     ASL A
            0230     ASL A
            0240     ASL A
            0250     CMP STIX
            0260     BCS ERR
            0270     TAY
            0280     DEY
            0290     LDX #$07
            0300 LOOP LDA VART,X
            0310     STA (LOMEM),Y
            0320     DEY
            0330     DEX
            0340     BPL LOOP
            0350     RTS
            0360 ERR JMP STOVER
    Po przepisaniu wartości przez procedurę PUTVAR pętla XLET jest powtarzana od wywołania RECVAL. Ponieważ składnia instrukcji jest sprawdzana podczas wprowadzania wiersza, to niemożliwe jest rozpoznanie bezpośrednio po sobie dwóch wartości.

    Gdy badany token nie oznacza wartości, to jest zapisywany do rejestru TOX (Temporary Output indeX). Według niego jest następnie odczytywany z tablicy OPCT kod, którego starsza połowa jest umieszczana w rejestrze TIX (Temporary Input indeX). Teraz według licznika STIX odczytywany jest token z bufora (w pierwszym przejściu jest to zawsze $11). Według tego tokena ponownie pobierany jest kod z OPCT, a jego młodsza połowa jest porównywana z zawartością rejestru TIX. Jeżeli zawartość TIX jest większa, to token operacji jest zapisywany na końcu bufora (licznik STIX jest przy tym zmniejszany). W przeciwnym przypadku procedura XLET kończy się, jeśli w akumulatorze jest wartość zero. Trzeba tu zwrócić uwagę na sposób wykorzystania bufora tokenizacji. Od jego początku kolejno są umieszczane wartości, a od końca tokeny operacji. Licznik STPTR wskazuje przy tym liczbę zapisanych wartości, a licznik STIX - token pierwszej operacji do wykonania. Tablica OPCT jest skonstruowana w taki sposób, że w odpowiednim momencie następuje przejście do części oznaczonej etykietą PRCOP. Wartości w tej tablicy wyznaczają jednocześnie priorytet operatorów i funkcji.
            0100 ;OPerator's Coefficients Table
            0110 ;
            0120     *=  $AC35
            0130 ;
            0140     .BYTE $00   ;niewykorzystany
            0150     .BYTE $00   ;niewykorzystany
            0160     .BYTE $00   ;,
            0170     .BYTE $00   ;$
            0180     .BYTE $00   ;: -koniec instrukcji
            0190     .BYTE $00   ;;
            0200     .BYTE $00   ;EOL -koniec wiersza
            0210     .BYTE $00   ;GOTO
            0220     .BYTE $00   ;GOSUB
            0230     .BYTE $00   ;TO
            0240     .BYTE $00   ;STEP
            0250     .BYTE $00   ;THEN
            0260     .BYTE $00   ;#
            0270     .BYTE $88   ;<=  |
            0280     .BYTE $88   ;<>  |
            0290     .BYTE $88   ;>=  |operatory
            0300     .BYTE $88   ;<   |liczbowe
            0310     .BYTE $88   ;>   |
            0320     .BYTE $88   ;=   |
            0330     .BYTE $CC   ;^
            0340     .BYTE $AA   ;*
            0350     .BYTE $99   ;+
            0360     .BYTE $99   ;-
            0370     .BYTE $AA   ;/
            0380     .BYTE $DD   ;NOT
            0390     .BYTE $55   ;OR
            0400     .BYTE $66   ;AND
            0410     .BYTE $F2   ;(
            0420     .BYTE $4E   ;)
            0430     .BYTE $F1   ;= -LET liczbowe
            0440     .BYTE $F1   ;= -LET tekstowe
            0450     .BYTE $EE   ;<=  |
            0460     .BYTE $EE   ;<>  |
            0470     .BYTE $EE   ;>=  |operatory
            0480     .BYTE $EE   ;<   |tekstowe
            0490     .BYTE $EE   ;>   |
            0500     .BYTE $EE   ;=   |
            0510     .BYTE $DD   ;+ -znak
            0520     .BYTE $DD   ;- -znak
            0530     .BYTE $F2   ;( -wyr. tekstowe
            0540     .BYTE $F2   ;( -zm. indeksowana
            0550     .BYTE $F2   ;( -wym. zm. ind.
            0560     .BYTE $F2   ;( -wyr. funkcyjne
            0570     .BYTE $F2   ;( -wym. zm. tekst.
            0580     .BYTE $43   ;, -w indeksie
            0590     .BYTE $F2   ;STR$
            0600     .BYTE $F2   ;CHR$
            0610     .BYTE $F2   ;USR
            0620     .BYTE $F2   ;ASC
            0630     .BYTE $F2   ;VAL
            0640     .BYTE $F2   ;LEN
            0650     .BYTE $F2   ;ADR
            0660     .BYTE $F2   ;ATN
            0670     .BYTE $F2   ;COS
            0680     .BYTE $F2   ;PEEK
            0690     .BYTE $F2   ;SIN
            0700     .BYTE $F2   ;RND
            0710     .BYTE $F2   ;FRE
            0720     .BYTE $F2   ;EXP
            0730     .BYTE $F2   ;LOG
            0740     .BYTE $F2   ;CLOG
            0750     .BYTE $F2   ;SQR
            0760     .BYTE $F2   ;SGN
            0770     .BYTE $F2   ;ABS
            0780     .BYTE $F2   ;INT
            0790     .BYTE $F2   ;PADDLE
            0800     .BYTE $F2   ;STICK
            0810     .BYTE $F2   ;PTRIG
            0820     .BYTE $F2   ;STRIG
    Po rozpoznaniu operatora o priorytecie wyższym niż poprzednie, jego token jest odczytywany z bufora tokenizacji, a licznik STIX jest zwiększany. Teraz w celu wykonania operacji lub funkcji wywoływana jest procedura EXEOP. Działanie jej jest identyczne, jak wcześniej opisanej procedury EXESTM. Adres procedury wykonawczej jest jednak w tym przypadku pobierany z tabeli wektorów OPVTAB. Zmniejszenie wartości tokena o $1D jest konieczne dla pominięcia operatorów, które są integralnymi częściami instrukcji.
            0100 ;EXEcute OPerator
            0110 ;
            0120 OPVTAV = $AA6A
            0130 ;
            0140     *=  $AB18
            0150 ;
            0160     SEC
            0170     SBC #$1D
            0180     ASL A
            0190     TAX
            0200     LDA OPVTAB,X
            0210     PHA
            0220     LDA OPVTAB+1,X
            0230     PHA
            0240     RTS
    Także tablica OPVTAB jest zbudowana identycznie jak STVTAB. Z podanego wyżej powodu zawiera ona jedynie wektory operacji i funkcji, których tokeny mają wartości od $1D do $54.
            0100 ;OPerator Vectors TABle
            0110 ;
            0120 XABS =  $B099
            0130 XADD =  $BDFA
            0140 XADR =  $B007
            0150 XAND =  $ACCF
            0160 XASC =  $AFFD
            0170 XATN =  $B118
            0180 XCHR =  $B052
            0190 XCLOG = $B13D
            0200 XCOM =  $AD64
            0210 XCOS =  $B10F
            0220 XDIV =  $AC8C
            0230 XEPAR = $AD66
            0240 XEQU =  $ACC9
            0250 XEXP =  $B14A
            0260 XFRE =  $AFD6
            0270 XGEQ =  $ACC2
            0280 XGRT =  $ACB9
            0290 XINT =  $B0C8
            0300 XLEN =  $AFB5
            0310 XLEQ =  $ACA3
            0320 XLES =  $ACB2
            0330 XLOG =  $B121
            0340 XMIN =  $AC95
            0350 XMUL =  $AC83
            0360 XNEQ =  $ACAC
            0370 XNLET = $AD4A
            0380 XNOT =  $ACE4
            0390 XOR =   $ACD9
            0400 XPADDL = $B00D
            0410 XPDIM = $AD6D
            0420 XPEEK = $AFCC
            0430 XPIXV = $AD71
            0440 XPLS =  $AB35
            0450 XPSEX = $AE11
            0460 XPTRIG = $B015
            0470 XRND =  $B076
            0480 XRPW =  $B15E
            0490 XSGN =  $AD04
            0500 XSIN =  $B106
            0510 XSLET = $AE8E
            0520 XSQR =  $B153
            0530 XSTICK = $B011
            0540 XSTR =  $B034
            0550 XSTRIG = $B019
            0560 XSUB =  $AC7A
            0570 XUSR =  $B0A5
            0580 XVAL =  $AFEB
            0590 ;
            0600     *=  $AA6A
            0610 ;
            0620     .DBYTE XLEQ-1   ;<= |
            0630     .DBYTE XNEQ-1   ;<> |
            0640     .DBYTE XGEQ-1   ;>= |operatory
            0650     .DBYTE XLES-1   ;<  |liczbowe
            0660     .DBYTE XGRT-1   ;>  |
            0670     .DBYTE XEQU-1   ;=  |
            0680     .DBYTE XRPW-1   ;^
            0690     .DBYTE XMUL-1   ;*
            0700     .DBYTE XADD-1   ;+
            0710     .DBYTE XSUB-1   ;-
            0720     .DBYTE XDIV-1   ;/
            0730     .DBYTE XNOT-1   ;NOT
            0740     .DBYTE XOR-1    ;OR
            0750     .DBYTE XAND-1   ;AND
            0760     .DBYTE XPLS-1   ;(
            0770     .DBYTE XEPAR-1  ;)
            0780     .DBYTE XNLET-1  ;= -liczbowy
            0790     .DBYTE XSLET-1  ;= -tekstowy
            0800     .DBYTE XLEQ-1   ;<= |
            0810     .DBYTE XNEQ-1   ;<> |
            0820     .DBYTE XGEQ-1   ;>= |operatory
            0830     .DBYTE XLES-1   ;<  |tekstowe
            0840     .DBYTE XGRT-1   ;>  |
            0850     .DBYTE XEQU-1   ;=  |
            0860     .DBYTE XPLS-1   ;+ znak
            0870     .DBYTE XMIN-1   ;- znak
            0880     .DBYTE XPSEX-1  ;( -wyr. tekst.
            0890     .DBYTE XPIXV-1  ;( -zm. indeks.
            0900     .DBYTE XPDIM-1  ;( -wym. zm. ind.
            0910     .DBYTE XEPAR-1  ;( -wyr. funkc.
            0920     .DBYTE XPDIM-1  ;( -wym. zm. tekst.
            0930     .DBYTE XCOM-1   ;, -indeks
            0940     .DBYTE XSTR-1   ;STR$
            0950     .DBYTE XCHR-1   ;CHR$
            0960     .DBYTE XUSR-1   ;USR
            0970     .DBYTE XASC-1   ;ASC
            0980     .DBYTE XVAL-1   ;VAL
            0990     .DBYTE XLEN-1   ;LEN
            1000     .DBYTE XADR-1   ;ADR
            1010     .DBYTE XATN-1   ;ATN
            1020     .DBYTE XCOS-1   ;COS
            1030     .DBYTE XPEEK-1  ;PEEK
            1040     .DBYTE XSIN-1   ;SIN
            1050     .DBYTE XRND-1   ;RND
            1060     .DBYTE XFRE-1   ;FRE
            1070     .DBYTE XEXP-1   ;EXP
            1080     .DBYTE XLOG-1   ;LOG
            1090     .DBYTE XCLOG-1  ;CLOG
            1100     .DBYTE XSQR-1   ;SQR
            1110     .DBYTE XSGN-1   ;SGN
            1120     .DBYTE XABS-1   ;ABS
            1130     .DBYTE XINT-1   ;INT
            1140     .DBYTE XPADDL-1 ;PADDLE
            1150     .DBYTE XSTICK-1 ;STICK
            1160     .DBYTE XPTRIG-1 ;PTRIG
            1170     .DBYTE XSTRIG-1 ;STRIG
Zientara Wojciech: Mapa pamięci Atari XL/XE. Procedury interpretera Basica, SOETO, Warszawa, 1988.