Rozdział 6
PROCEDURY WYKONAWCZE INSTRUKCJI
Opisywana w rozdziale 2 procedura EXSTM w celu wykonania
instrukcji Basica pobiera z tablicy wektorów STVTAB adres
odpowiedniej procedury realizacyjnej, a następnie wywołuje ją.
Po opisie wszystkich pozostałych elementów programu nadeszła
więc pora na przedstawienie procedur wykonawczych
poszczególnych instrukcji zaimplementowanych w interpreterze
Atari Basic. Procedury te zostały podzielone na grupy w
zależności od funkcji spełnianych w programie przez te
instrukcje.
6.1. Instrukcje kontroli programu
Instrukcjami kontroli programu nazywamy takie, które
sterują bezpośrednio pracą interpretera, a także realizacją
programu. Należą do nich instrukcje: NEW, BYE, DOS, END, STOP,
CONT, RUN i CLR.
6.1.1. Instrukcja NEW
Instrukcja NEW kasuje program w Basicu zawarty w pamięci
komputera oraz wszystkie wykorzystywane przez niego zmienne i
ich wartości. Jest ona częścią składową procedury zimnego
startu CDST i została opisana razem z nią. Analiza tej
procedury ujawnia, że wszystkie dane programu po jej wykonaniu
nadal pozostają w pamięci. Skasowane zostają tylko wskazujące
je wektory. Odzyskanie w całości skasowanego programu jest
jednak niemożliwe, gdyż wpisanie kolejnej instrukcji zniszczy
jego część. Pewne fragmenty można wszakże odtworzyć.
6.1.2. Instrukcja BYE
Instrukcja BYE powoduje zakończenie pracy interpretera i
przejście do wbudowanego programu testującego. Jej procedura
wykonawcza - XBYE - składa się tylko z wywołania procedury
CLALL, która zamyka wszystkie kanały IOCB (oprócz edytora) i
wyłącza dźwięk oraz skoku do programu testującego poprzez
tablicę skoków JMPTAB.
0100 ;eXecute BYE command
0110 ;
0120 CLALL = $BD45
0130 JTSTROM = $E480
0140 ;
0150 *= $A9E6
0160 ;
0170 JSR CLALL
0180 JMP JTSTROM
6.1.3. Instrukcja DOS
Także instrukcja DOS przerywa pracę interpretera, lecz
sterowanie komputerem przekazuje do programu, którego adres
zapisany jest w rejestrze DOSVEC. Przy pracy ze stacją dysków
znajduje się tam adres DOS-u (zob. "Mapa pamięci Atari XL/XE.
Dyskowe systemy operacyjne"). Jeśli nie ma stacji, to rejestr
DOSVEC zawiera adres programu testującego, więc działanie
instrukcji DOS jest analogiczne jak BYE. Procedura wykonawcza -
XDOS - jest również zbliżona do XBYE, a różni się tylko
końcowym skokiem.
0100 ;eXecute DOS command
0110 ;
0120 CLALL = $BD45
0130 DOSVEC = $0A
0140 ;
0150 *= $A9EC
0160 ;
0170 JSR CLALL
0180 JMP (DOSVEC)
6.1.4. Instrukcja END
Instrukcja END przerywa działanie programu, a następnie
zamyka wszystkie kanały dźwięku i komunikacji z urządzeniami
zewnętrznymi. Jest to realizowane w procedurze XEND przez skok
do gorącego startu interpretera. Przedtem jednak przez
wywołanie procedury SAVCLN w rejestrze STOPLN (STOP LiNe
number) zapamiętywany jest numer aktualnego wiersza programu.
0100 ;eXecute END statement
0110 ;
0120 SAVCLN = $B7A6
0130 WRMST = $A050
0140 ;
0150 *= $B78C
0160 ;
0170 JSR SAVCLN
0180 JMP WRMST
6.1.5. Instrukcja STOP
Instrukcja STOP także przerywa działanie programu, jednak
nie wyłącza dźwięku i nie zamyka kanałów I/O. Efekt jej
działania jest niemal identyczny z efektem wystąpienia błędu w
programie. Procedura wykonawcza tej instrukcji - XSTOP - jest
także wywoływana po naciśnięciu klawisza BREAK.
Realizacja instrukcji STOP rozpoczyna się od zapamiętania w
rejestrze STOPLN aktualnego numeru wiersza (przez SAVCLN) oraz
wyświetlenia na ekranie znaku końca wiersza RETURN (przez
PRTRET). Następnie w rejestrze POKADR umieszczany jest adres
komunikatu "STOPPED", który jest wyświetlany po wywołaniu
procedury PUTTXT. Dalsza część komunikatu (numer wiersza w
trybie programowym lub tylko RETURN w trybie bezpośrednim) jest
wyświetlana po przekazaniu sterowania do wnętrza procedury
obsługi błędu poprzez skok do etykiety DSTMSG.
0100 ;eXecute STOP statement
0110 ;
0120 DSTMSG = $B968
0130 POKADR = $95
0140 PRTRET = $BD79
0150 PUTTXT = $B567
0160 SAVCLN = $B7A6
0170 STMSG = $A5FD
0180 ;
0190 *= $B792
0200 ;
0210 JSR SAVCLN
0220 JSR PRTRET
0230 LDA # <STMSG
0240 STA POKADR
0250 LDA # >STMSG
0260 STA POKADR+1
0270 JSR PUTTXT
0280 JMP DSTMSG
Tekst komunikatu o przerwaniu pracy programu jest
umieszczony w pamięci za tablicą nazw instrukcji STNAME i
oznaczony etykietą STMSG.
0100 ;STopped MeSsaGe
0110 ;
0120 *= $A5FD
0130 ;
0140 .CBYTE "STOPPED "
6.1.6. Instrukcja CONT
Wznowienie realizacji przerwanego programu jest wykonywane
przez instrukcję CONT, a właściwie jej procedurę - XCONT.
Instrukcja CONT podana w trybie programowym, co jest sprawdzane
przez pierwsze wywołanie GHISTM, powoduje jedynie ustawienie
rejestrów kanału I/O na kanał edytora (poprzez RSTCHN). W
trybie bezpośrednim z rejestru STOPLN numer wiersza jest
przepisywany do CLNN (Current LiNe Number) i procedura FNDCST
odszukuje ten wiersz. Jeśli jest on w trybie bezpośrednim, to
procedura jest przerywana skokiem do gorącego startu
interpretera.
0100 ;eXecute CONT statement
0110 ;
0120 CLNN = $A0
0130 FNDCST = $A9A2
0140 GHISTM = $A9E1
0150 GLNLEN = $A9DC
0160 GSTMLN = $B819
0170 NXTSTM = $A9D0
0180 STOPLN = $BA
0190 ;
0200 *= $B7B5
0210 ;
0220 JSR GHISTM
0230 BPL $B7B2 ;JMP RSTCHN
0240 LDA STOPLN
0250 STA CLNN
0260 LDA STOPLN+1
0270 STA CLNN+1
0280 JSR FNDCST
0290 JSR GHISTM
0300 BMI $B775 ;JMP WRMST
0310 JSR GLNLEN
0320 JSR NXTSTM
0330 JSR GHISTM
0340 BMI $B775 ;JMP WRMST
0350 JMP GSTMLN
Po odnalezieniu aktualnego wiersza programu odczytywana
jest przy pomocy GLNLEN jego długość i procedura NXTSTM
znajduje następny wiersz. Jeżeli jest on w trybie bezpośrednim,
to także wykonywany jest skok do WRMST. W przeciwnym przypadku
następuje skok do GSTMLN i realizacja programu jest
kontynuowana. Jeśli zatrzymanie programu nastąpiło w środku
wiersza, to dalsze instrukcje w tym wierszu NIE będą wykonane.
6.1.7. Instrukcje CLR i RUN
Instrukcja CLR kasuje deklaracje zmiennych tablicowych oraz
zeruje pozostałe zmienne wykorzystywane w programie. Ponadto
usuwa ze stosu bieżącego wszelkie informacje o procedurach i
pętlach FOR/NEXT. Instrukcja RUN powoduje natomiast
uruchomienie programu znajdującego się w pamięci komputera lub
odczytanie programu z urządzenia zewnętrznego i jego
uruchomienie. Procedura wykonawcza instrukcji CLR (oznaczona
etykietą XCLR) jest częścią składową procedury XRUN, która
realizuje instrukcję RUN. Z tego powodu zostały one opisane
łącznie.
0100 ;eXecute RUN statement
0110 ;
0120 CLNN = $A0
0130 CLRVV = $B8B9
0140 DATAD = $B6
0150 DATALN = $B7
0160 FSTGLN = $B816
0170 GETIX = $B904
0180 GHISTM = $A9E1
0190 LDPRGM = $BAF7
0200 RSMEMT = $B8A8
0210 RSTBRG = $B8F1
0220 WRMST = $A050
0230 ;
0240 *= $B74C
0250 ;
0260 XRUN JSR GETIX
0270 BCS RUN
0280 JSR LDPRGM
0290 RUN NOP
0300 LDA #$00
0310 STA CLNN
0320 STA CLNN+1
0330 JSR FSTGLN
0340 JSR GHISTM
0350 BMI END
0360 JSR RSTBRG
0370 ;
0380 ;eXecute CLR statement
0390 ;
0400 XCLR JSR CLRVV
0410 JSR RSMEMT
0420 LDA #$00
0430 STA DATALN
0440 STA DATALN+1
0450 STA DATAD
0460 RTS
0470 END JMP WRMST
Najpierw rozpoznawany jest rodzaj instrukcji RUN, to
znaczy, czy program do uruchomienia znajduje się już w pamięci,
czy musi być najpierw odczytany. W tym celu porównywane są
przez procedurę GETIX indeksy wejściowy (INIX) i wyjściowy
(OUTIX). Jeśli indeks wejściowy jest większy, to wywoływana
jest procedura LDPRGM (jest ona fragmentem XLOAD), która
odczytuje program ze wskazanego urządzenia.
0100 ;GET IndeX
0110 ;
0120 INIX = $A8
0130 OUTIX = $A7
0140 ;
0150 *= $B904
0160 ;
0170 LDX INIX
0180 INX
0190 CPX OUTIX
0200 RTS
Sposób uruchomienia programu jest bardzo prosty. Najpierw
zerowany jest rejestr CLNN, a następnie procedura FSTGLN
odszukuje wiersz o najniższym numerze. Jeśli jest to wiersz w
trybie bezpośrednim (numer $8000), to procedura jest przerywana
skokiem do WRMST. Jeżeli nie, to kanały I/O są zamykane, a
rejestry Basica zerowane przez procedurę RSTBRG.
Teraz rozpoczyna się procedura XCLR. Na początku zerowane
są wszystkie zmienne liczbowe oraz informacje o zmiennych
tekstowych i tablicowych. Wykonuje to procedura CLRVV, która
wypełnia zerami prawie całą tablicę wartości zmiennych.
Nienaruszone pozostają tylko bajty określające numery i typy
zmiennych.
0100 ;CLeaR Variable Value
0110 ;
0120 STMTAB = $88
0130 VVTP = $86
0140 ZTEMP1 = $F5
0150 ;
0160 *= $B8B9
0170 ;
0180 LDX VVTP
0190 STX ZTEMP1
0200 LDY VVTP+1
0210 STY ZTEMP1+1
0220 LOOP LDX ZTEMP1+1
0230 CPX STMTAB+1
0240 BCC EXEC
0250 LDX ZTEMP1
0260 CPX STMTAB
0270 BCC EXEC
0280 RTS
0290 EXEC LDY #$00
0300 LDA (ZTEMP1),Y
0310 AND #$FE
0320 STA (ZTEMP1),Y
0330 LDY #$02
0340 LDX #$06
0350 LDA #$00
0360 NEXT STA (ZTEMP1),Y
0370 INY
0380 DEX
0390 BNE NEXT
0400 LDA ZTEMP1
0410 CLC
0420 ADC #$08
0430 STA ZTEMP1
0440 LDA ZTEMP1+1
0450 ADC #$00
0460 STA ZTEMP1+1
0470 BNE LOOP
Kolejnym krokiem jest skasowanie tablicy zmiennych
tablicowych i tekstowych oraz stosu bieżącego. Ta operacja jest
wykonywana przez procedurę RSMEMT. Polega ona na przepisaniu
wektora STARP (STring and ARray Pointer) do wektorów RUNSTK
(RUNtime STacK), BMEMHI (Basic MEMory HIgh) oraz APPMHI
(APPlication Memory HIgh - wykorzystywany przez system
operacyjny).
0100 ;ReStore MEMory Top
0110 ;
0120 APPMHI = $0E
0130 BMEMHI = $90
0140 RUNSTK = $8E
0150 STARP = $8C
0160 ;
0170 *= $B8A8
0180 ;
0190 LDA STARP
0200 STA RUNSTK
0210 STA BMEMHI
0220 STA APPMHI
0230 LDA STARP+1
0240 STA RUNSTK+1
0250 STA BMEMHI+1
0260 STA APPMHI+1
0270 RTS
Ostatnim etapem jest odtworzenie stanu początkowego
liczników danych zapisanych w instrukcjach DATA. Uzyskuje się
to przez wpisanie zer do rejestrów DATAD (DATa ADdress) i
DATALN (DATA LiNe number).
Uruchomienie programu następuje zawsze od wiersza o
najmniejszym numerze. Jest to jednak możliwe do ominięcia przy
pomocy krótkiej procedury maszynowej. Musi ona umieścić w
rejestrze CLNN numer wiersza, od którego będzie rozpoczęte
wykonywanie programu, a następnie wykonać skok do wnętrza XRUN
do adresu $B75B.
6.2. Instrukcje pomocnicze
Kolejna grupa instrukcji pełni rolę pomocniczą, lecz
prawidłowe działanie programu jest bez nich niemożliwe lub
przynajmniej utrudnione. Znajdują się tu także instrukcje,
których nie można zaliczyć do żadnej z pozostałych grup. Są to:
REM, DATA, DEG, RAD, RESTORE, DIM i POKE.
6.2.1. Instrukcje REM i DATA
Instrukcja REM zawiera komentarz wpisywany przez
programistę w celu zwiększenia przejrzystości programu lub
podania istotnych informacji. Natomiast w instrukcji DATA są
umieszczane dane dla instrukcji READ. Obie te instrukcje są w
trakcie wykonywania programu pomijane przez interpreter.
Procedura wykonawcza tych instrukcji ogranicza się więc tylko
do rozkazu RTS. Rozkaz ten znajduje się w procedurze GHISTM,
która została opisana w rozdziale 2.
6.2.2. Instrukcje DEG i RAD
Instrukcje DEG i RAD ustalają podstawę do obliczeń funkcji
trygonometrycznych. DEG powoduje traktowanie wszystkich
argumentów kątowych, jako podanych w stopniach, zaś RAD - jako
podanych w radianach. Odpowiednio procedury wykonawcze tych
instrukcji umieszczają w rejestrze RADFLG (RADians FLaG)
wartości sześć lub zero.
0100 RADFLG = $FB
0110 ;
0120 *= $B28D
0130 ;
0140 ;eXecute DEG statement
0150 ;
0160 XDEG LDA #$06
0170 BNE EXE
0180 ;
0190 ;eXecute RAD statement
0200 ;
0210 XRAD LDA #$00
0220 EXE STA RADFLG
0230 RTS
6.2.3. Instrukcja RESTORE
Instrukcja RESTORE ustawia znacznik odczytu danych z
instrukcji DATA na początek wiersza, którego numer został
podany jako argument. Jeśli instrukcja nie ma argumentu, to
znacznik jest ustawiany na pierwszy wiersz programu. Operacja
ta jest przeprowadzana przez procedurę XRSTR. W każdym
przypadku najpierw zerowany jest rejestr DATAD, co ustawia
znacznik na pierwszej danej w odpowiedniej instrukcji DATA.
0100 ;eXecute RESTORE statement
0110 ;
0120 DATAD = $B6
0130 DATALN = $B7
0140 FR0 = $D4
0150 GETIX = $B904
0160 GLNNUM = $ABCD
0170 ;
0180 *= $B296
0190 ;
0200 LDA #$00
0210 STA DATAD
0220 JSR GETIX
0230 BCC GLN
0240 TAY
0250 BEQ SLN
0260 GLN JSR GLNNUM
0270 LDA FR0+1
0280 LDY FR0
0290 SLN STA DATALN+1
0300 STY DATALN
0310 RTS
Następnie przez wywołanie procedury GETIX sprawdzane są
indeksy (podobnie jak w XRUN). Jeśli instrukcja posiada
argument, to zostaje on odczytany przez procedurę GLNNUM i
przepisany do rejestru DATALN. W przeciwnym przypadku rejestr
DATALN jest zerowany.
0100 ;Get LiNe NUMber
0110 ;
0120 FR0 = $D4
0130 LETNUM = $ABD7
0140 LINNER = $B926
0150 ;
0160 *= $ABCD
0170 ;
0180 JSR LETNUM
0190 LDA FR0+1
0200 BPL $ABC9 ;RTS
0210 JMP LINNER
Procedura GLNNUM wykorzystuje do odczytu dwubajtowej
wartości procedurę LETNUM. Po sprawdzeniu, czy uzyskany wynik
nie przekracza dopuszczalnej wartości ($7FFF), GLNNUM się
kończy. Jeśli jednak wartość nie jest poprawna, to przez skok
do procedury LINNER sygnalizowany jest błąd (LINe Number
ERror).
6.2.4. Instrukcja DIM
Instrukcja DIM służy do deklarowania wymiarów zmiennych
tablicowych i tekstowych. Atari Basic wymaga zadeklarowania
wszystkich takich zmiennych przed ich użyciem. Do realizacji
tej instrukcji przeznaczona jest procedura XDIM.
Ponieważ instrukcja DIM może zawierać deklaracje kilku
zmiennych, to procedura XDIM przebiega w pętli, która jest
sterowana licznikami INIX i OUTIX. Każde przejście pętli
rozpoczyna się od odczytania typu zmiennej. Jeśli była ona
wcześniej deklarowana (ustawiony bit 0 rejestru VART), to błąd
sygnalizowany jest przez skok do DIMER.
0100 ;eXecute DIM statement
0110 ;
0120 CSTAD = $97
0130 DIMER = $B922
0140 EVZTMP = $AF48
0150 FR0 = $D4
0160 INIX = $A8
0170 INSEL = $A87A
0180 OUTIX = $A7
0190 RUNSTK = $8E
0200 SAVVAL = $AC0C
0210 STARP = $8C
0220 VART = $D2
0230 XLET = $AADA
0240 ZFR0 = $DA44
0250 ZT1ML6 = $AF31
0260 ZTEMP1 = $F5
0270 ;
0280 *= $B206
0290 ;
0300 XDIM LDY INIX
0310 CPY OUTIX
0320 BCC EXE
0330 RTS
0340 EXE JSR XLET
0350 LDA VART
0360 ROR A
0370 BCC NUM
0380 ERR JSR DIMER
0390 NDM SEC
0400 ROL A
0410 STA VART
0420 BMI STRNG
0430 LDY ZTEMP1
0440 LDX ZTEMP1+1
0450 INY
0460 BNE COL
0470 INX
0480 BMI ERR
0490 COL STY FR0+2
0500 STX FR0+3
0510 STY ZTEMP1
0520 STX ZTEMP1+1
0530 LDY CSTAD
0540 LDX CSTAD+1
0550 INY
0560 BNE ROW
0570 INX
0580 BMI ERR
0590 ROW STY FR0+4
0600 STX FR0+5
0610 JSR EVZTMP
0620 JST ZT1ML6
0630 LDY ZTEMP1
0640 LDA ZTEMP1+1
0650 BMI ERR
0660 BPL INS
0670 STRNG LDA #$00
0680 STA FR0+2
0690 STA FR0+3
0700 LDY ZTEMP1
0710 STY FR0+4
0720 LDA ZTEMP1+1
0730 STA FR0+5
0740 BNE INS
0750 CPY #$00
0760 BEQ ERR
0770 INS LDX #RUNSTK
0780 JSR INSEL+2
0790 SEC
0800 LDA CSTAD
0810 SBC STARP
0820 STA FR0
0830 LDA CSTAD+1
0840 SBC STARP+1
0850 STA FR0+1
0860 JSR SAVVAL
0870 JMP XDIM
Po ustawieniu najmłodszego bitu rejestru VART sprawdzany
jest jego najstarszy bit, który wskazuje typ zmiennej. Zależnie
od jego stanu wybierany jest odpowiedni wariant procedury
(skasowany - zmienna liczbowa indeksowana czyli zmienna
tablicowa, ustawiony - zmienna tekstowa).
W przypadku zmiennej liczbowej indeksowanej jej wymiary są
odczytywane z rejestrów ZTEMP1 oraz CSTAD i po zwiększeniu o
jeden umieszczane w odpowiednich bajtach rejestru FR0.
Zwiększenie jest konieczne, gdyż elementy zmiennej są
numerowane od zera. Na tym etapie ponownie (uczyniła to już
procedura XPDIM) sprawdzane jest, czy wartości wymiarów są
mniejsze od $8000. Etap ten kończy się obliczeniem obszaru
pamięci niezbędnego dla zapisania wszystkich elementów
zmiennej.
Dla zmiennej tekstowej procedura jest nieco prostsza, gdyż
może ona mieć tylko jeden wymiar. Jako druga dana wpisywana
jest aktualna długość ciągu, a więc zero. Ponadto nie trzeba
obliczać przestrzeni zajmowanej przez zmienną.
Ostatnia faza pętli jest również wspólna dla zmiennych obu
typów. Przez wywołanie procedury INSEL rezerwowany jest w
tablicy STARP obszar pamięci dla zmiennej. Aktualne parametry
zmiennej są zapisywane w tablicy wartości (VVT) przez procedurę
SAVVAL. Ponieważ procedura INSEL (zob. rozdział 1) nie zeruje
rezerwowanego obszaru, to odczyt elementu zmiennej liczbowej da
zupełnie przypadkowy wynik. Próba odczytu elementu zmiennej
tekstowej spowoduje natomiast błąd długości ciągu (SLENER).
6.2.5. Instrukcja POKE
Instrukcja POKE umieszcza liczbę podaną jako drugi argument
w komórce pamięci, której adres jest pierwszym argumentem.
Realizuje ją procedura XPOKE. Najpierw procedura LETNUM
odczytuje dwubajtową liczbę całkowitą, która stanowi pierwszy
argument. Jest ona przepisywana do rejestru POKADR (POKe
ADdRess). Jednobajtowa wartość drugiego argumentu jest
odczytywana z bufora przez procedurę GETBYT. Zostaje ona
następnie umieszczona we wskazanym miejscu pamięci.
0100 ;eXecute POKE statement
0110 ;
0120 FR0 = $D4
0130 GETBYT = $ABE0
0140 LETNUM = $ABD7
0150 POKADR = $95
0160 ;
0170 *= $B278
0180 ;
0190 JSR LETNUM
0200 LDA FR0
0210 STA POKADR
0220 LDA FR0+1
0230 STA POKADR+1
0240 JSR GETBYT
0250 LDA FR0
0260 LDY #$00
0270 STA (POKADR),Y
0280 RTS
Procedura GETBYT działa dwustopniowo. Przez wywołanie
GLNNUM odczytuje liczbę dwubajtową mniejszą od $8000 (dla liczb
większych GLNNUM wskazuje błąd), a jeśli starszy bajt jest
niezerowy, to sygnalizuje błąd przez skok do BVALER (Bad VALue
ERror).
0100 ;GET BYTe
0110 ;
0120 BVALER = $B92E
0130 GLNNUM = $ABCD
0140 ;
0150 *= $ABE0
0160 ;
0170 JSR GLNNUM
0180 BNE ERR
0190 RTS
0200 ERR JSR BVALER
6.3. Instrukcje strukturalne
Podstawowy kształt programu jest tworzony przez instrukcje
strukturalne. Umożliwiają one realizację skoków, procedur i
pętli oraz warunkowe wykonanie innych instrukcji. Do instrukcji
strukturalnych w Atari Basic należą: POP, TRAP, GOTO, GOSUB,
RETURN, FOR, NEXT, ON i IF.
6.3.1. Instrukcja POP
Instrukcja POP zdejmuje ze stosu bieżącego informacje o
ostatniej procedurze GOSUB/RETURN lub pętli FOR/NEXT. Jest to
konieczne przy opuszczaniu procedury lub pętli przez skok.
Instrukcja ta jest realizowana przez procedurę XPOP.
0100 ;eXecute POP statement
0110 ;
0120 BMEMHI = $90
0130 CLNN = $A0
0140 DELEL = $A8F7
0150 RUNSTK = $8E
0160 STMNUM = $B2
0170 ;
0180 *= $B83E
0190 ;
0200 LDA RUNSTK+1
0210 CMP BMEMHI+1
0220 BCC POP
0230 LDA RUNSTK
0240 CMP BMEMHI
0250 BCS $B83D ;RTS
0260 POP LDA #$04
0270 LDX #BMEMHI
0280 JSR DELEL
0290 LDY #$03
0300 LDA (BMEMHI),Y
0310 STA STMNUM
0320 DEY
0330 LDA (BMEMHI),Y
0340 STA CLNN+1
0350 DEY
0360 LDA (BMEMHI),Y
0370 STA CLNN
0380 DEY
0390 LDA (BMEMHI),Y
0400 BEQ END
0410 PHA
0420 LDA #$0C
0430 LDX #BMEMHI
0440 JSR DELEL
0450 PLA
0460 END CLC
0470 RTS
Procedura XPOP musi najpierw sprawdzić, czy na stosie jest
coś zapisane, w przeciwnym bowiem przypadku uległaby
zniszczeniu część tablicy zmiennych indeksowanych. Następnie
przez wywołanie procedury DELEL wektor BMEMHI jest obniżany o
cztery miejsca. Odczytane stamtąd wartości są przepisywane
kolejno do rejestru numeru instrukcji STMNUM (STateMent NUMber)
oraz numeru wiersza CLNN (Current LiNe Number). Ostatnia
wartość pozwala na rozpoznanie rodzaju informacji zapisanej na
stosie. Jeśli jest to zero, a więc informacja dotyczy procedury
GOSUB, to procedura XPOP się kończy. W przeciwnym przypadku są
to dane pętli FOR/NEXT (teraz już zbędne) i przez ponowne
wywołanie DELEL wektor BMEMHI jest zmniejszany jeszcze o
dwanaście miejsc.
6.3.2. Instrukcja TRAP
Instrukcja TRAP ustala numer wiersza programu, od którego
będzie kontynuowana praca po wystąpieniu błędu. Jej procedura
wykonawcza - XTRAP - jest bardzo prosta. Numer wiersza
odczytany przy pomocy LETNUM umieszczany jest w rejestrze
TRAPLN (TRAP LiNe number) i to wszystko.
0100 ;eXecute TRAP statement
0110 ;
0120 FR0 = $D4
0130 LETNUM = $ABD7
0140 TRAPLN = $BC
0150 ;
0160 *= $B7D8
0170 ;
0180 JSR LETNUM
0190 LDA FR0
0200 STA TRAPLN
0210 LDA FR0+1
0220 STA TRAPLN+1
0230 RTS
6.3.3. Instrukcje GOTO i GOSUB
Instrukcje GOTO (lub GO TO) i GOSUB służą do wykonywania
skoków do innych miejsc programu. Dodatkowo GOSUB powoduje
zapamiętanie adresu aktualnej instrukcji, co umożliwia potem
powrót. W związku z tym ich procedury realizacyjne - XGOTO i
XGOSUB - różnią się tylko jednym rozkazem, który wywołuje
procedurę zapisu parametrów bieżącej instrukcji.
0100 CLNN = $A0
0110 FNDCST = $A9A2
0120 FR0 = $D4
0130 GLNNUM = $ABCD
0140 LNFDER = $B91C
0150 PRCSTM = $A95E
0160 RSCSTM = $B6F0
0170 SAVSTM = $B6F9
0180 ;
0190 *= $B6D2
0200 ;
0210 ;eXecute GOSUB statement
0220 ;
0230 ;
0240 XGOSUB JSR SAVSTM
0250 ;
0260 ;eXecute GOTO statement
0270 ;
0280 XGOTO JSR GLNNUM
0290 ;
0300 ;GO to LINE
0310 ;
0320 GOLINE LDA FR0+1
0330 STA CLNN+1
0340 LDA FR0
0350 STA CLNN
0360 ;
0370 ;FinD & EXecute STatement
0380 ;
0390 FDEXST JSR FNDCST
0400 BCS ERR
0410 PLA
0420 PLA
0430 JMP PRCSTM
0440 ERR JSR RSCSTM
0450 JSR LNFDER
Odczytanie numeru wiersza, do którego będzie wykonany skok
realizuje procedura GLNNUM. Numer ten jest przepisywany do
rejestru CLNN i procedura FNDCST odszukuje go w pamięci.
Następnie ze stosu procesora zdejmowany jest adres powrotny dla
XGOTO lub XGOSUB i po wykonaniu skoku do PRCSTM realizowany
jest wskazany wiersz programu. Jeżeli nie ma w programie
wiersza o podanym numerze, to przez wywołanie procedury RSCSTM
odtwarzany jest aktualny numer wiersza i przez skok do LNFDER
sygnalizowany jest błąd (Line Not FounD ERror).
0100 ;ReStore Current STateMent
0110 ;
0120 SAVCUR = $BE
0130 STMCUR = $8A
0140 ;
0150 *= $B6F0
0160 ;
0170 LDA SAVCUR
0180 STA STMCUR
0190 LDA SAVCUR+1
0200 STA STMCUR+1
0210 RTS
Podczas realizacji instrukcji GOSUB dodatkowo jest
wywoływana procedura SAVSTM, która zapisuje parametry
aktualnego wiersza na stosie bieżącym Basica. Składa się ona z
wywołań dwóch innych procedur, przy czym PHSTK jest fragmentem
procedury XFOR (rozdział 6.3.5).
0100 ;SAVe current STateMent
0110 ;
0120 PHSTK = $B6B5
0130 SAVIX = $B883
0140 ;
0150 *= $B6F9
0160 ;
0170 JSR SAVIX
0180 LDA #$00
0190 BEQ PHSTK
Druga procedura - SAVIX - przepisuje tylko aktualny stan
licznika wejściowego INIX (INput IndeX) do pomocniczego
rejestru TMPIX (TeMPorary IndeX).
0100 ;SAVe IndeX
0110 ;
0120 INIX = $A8
0130 TMPIX = $B3
0140 ;
0150 *= $B883
0160 ;
0170 LDY INIX
0180 STY TMPIX
0190 RTS
6.3.4. Instrukcja RETURN
Instrukcja RETURN jest odwrotnością instrukcji GOSUB i
powoduje powrót do niej, a dalszy przebieg programu jest
wykonywany od instrukcji następującej po GOSUB lub ON/GOSUB.
Jej procedura realizacyjna - XRTRN - jest znacznie bardziej
uniwersalna i stosowana jest także dla odtworzenia aktualnego
wiersza programu po wykonaniu instrukcji READ i LIST.
Rozpoczyna się ona od wywoływania opisanej wyżej procedury
XPOP, aż do natrafienia w ostatnim zdejmowanym ze stosu bajcie
na wartość równą zero. Jeśli zaś na stosie brak takich danych,
to przez skok do RETER sygnalizowany jest błąd (RETurn ERror).
0100 ;eXecute RETURN statement
0110 ;
0120 FSTGLN = $B816
0130 GOSLER = $B916
0140 OUTIX = $A7
0150 RETER = $B914
0160 RSCSTM = $B6F0
0170 STMCUR = $8A
0180 STMNUM = $B2
0190 XPOP = $B83E
0200 ;
0210 *= $BDA8
0220 ;
0230 XRTRN JSR XPOP
0240 BCS ERR
0250 BNE XRTRN
0260 JSR RETURN
0270 CMP #$0C
0280 BEQ END
0290 CMP #$1E
0300 BEQ END
0310 CMP #$04
0320 BEQ END
0330 CMP #$22
0340 BEQ END
0350 ;
0360 ;Bad RETurn LiNe
0370 ;
0380 BRETLN JSR RSCSTM
0390 JSR GOSLER
0400 ERR JSR RETER
0410 ;
0420 ;RETURN
0430 ;
0440 RETURN JSR FSTGLN
0450 BCS BRETLN
0460 LDY STMNUM
0470 DEY
0480 LDA (STMCUR),Y
0490 STA OUTIX
0500 INY
0510 LDA (STMCUR),Y
0520 END RTS
Następnie przez wywołanie RETURN odtwarzany jest stan
licznika wyjściowego OUTIX oraz adres ostatniego wykonanego
tokena, który jest ponadto odczytywany. Jeśli jest to token
jednej z dozwolonych tu instrukcji - GOSUB, ON, LIST lub READ,
to procedura się kończy. W przeciwnym przypadku procedura
RSCSTM odtwarza adres instrukcji RETURN i skokiem do GOSLER
wskazywany jest błąd (GOSub Line ERror).
6.3.5. Instrukcja FOR
Instrukcja FOR rozpoczyna pętlę, w której wielokrotnie
wykonywany jest zestaw innych instrukcji. Do realizacji tej
instrukcji służy procedura XFOR.
0100 ;eXecute FOR statement
0110 ;
0120 BMTUP = $B871
0130 FR0 = $D4
0140 GETIX = $B904
0150 LETVAR = $AC06
0160 PHREG = $B888
0170 PLSTK = $B823
0180 SAVIX = $B883
0190 SAVMHI = $C4
0200 STMCUR = $8A
0210 TMPIX = $B3
0220 VARN = $D3
0230 XLET = $AADA
0240 ZFR0 = $DA44
0250 ;
0260 *= $B67D
0270 ;
0280 JSR SAVIX
0290 JSR XLET
0300 LDA VARN
0310 ORA #$80
0320 PHA
0330 JSR PLSTK
0340 LDA #$0C
0350 JSR BMTUP
0360 JSR LETVAR
0370 LDX #FR0
0380 LDY #$00
0390 JSR PHREG
0400 JSR ZFR0
0410 LDA #$01
0420 STA FR0+1
0430 LDA #$40
0440 STA FR0
0450 JSR GETIX
0460 BCS PHN
0470 JSR LETVAR
0480 PHN LDX #FR0
0490 LDY #$06
0500 JSR PHREG
0510 PLA
0520 ;
0530 ;PusH on STacK
0540 ;
0550 PHSTK PHA
0560 LDA #$04
0570 JSR BMTUP
0580 PLA
0590 LDY #$00
0600 STA (SAVMHI),Y
0610 LDA (STMCUR),Y
0620 INY
0630 STA (SAVMHI),Y
0640 LDA (STMCUR),Y
0650 INY
0660 STA (SAVMHI),Y
0670 LDX TMPIX
0680 DEX
0690 TXA
0700 INY
0710 STA (SAVMHI),Y
0720 RTS
Rozpoczyna się ona od zapisania aktualnego stanu licznika
INIX przez procedurę SAVIX oraz ustalenia przy pomocy procedury
XLET stanu początkowego zmiennej sterującej (licznika pętli).
Numer tej zmiennej jest odkładany na stos i wywoływana jest
procedura PLSTK.
0100 ;PulL from STacK
0110 ;
0120 ACNT2 = $C7
0130 BMEMHI = $90
0140 SAVMHI = $C4
0150 SAVTST = $B87A
0160 XPOP = $B83E
0170 ;
0180 *= $B823
0190 ;
0200 STA ACNT2
0210 JSR SAVTST
0220 POP JSR XPOP
0230 BCS MEM
0240 BEQ MEM
0250 CMP ACNT2
0260 BEQ END
0270 BNE POP
0280 MEM LDA SAVMHI
0290 STA BMEMHI
0300 LDA SAVMHI+1
0310 STA BMEMHI+1
0320 END RTS
Zapisuje ona numer zmiennej do pomocniczego rejestru ACNT2
(Auxiliary CouNTer) oraz przepisuje aktualny wektor górnej
granicy pamięci do rejestru SAVMHI (przy pomocy procedury
SAVTST). Następnie przez kolejne wywołania XPOP przeszukiwany
jest stos bieżący. Znalezienie danych dla procedury GOSUB lub
osiągnięcie końca stosu przerywa to przeszukiwanie i powoduje
odtworzenie wektora BMEMHI. Natomiast bez odtwarzania tego
wektora procedura PLSTK kończy się w przypadku znalezienia na
stosie zmiennej o tym samym numerze, co znajdujący się w
rejestrze ACNT2.
0100 ;SAVe Top of STack
0110 ;
0120 BMEMHI = $90
0130 SAVMHI = $C4
0140 ;
0150 *= $B87A
0160 ;
0170 LDX BMEMHI
0180 STX SAVMHI
0190 LDX BMEMHI+1
0200 STX SAVMHI+1
0210 RTS
Teraz w akumulatorze umieszczona zostaje wartość $0C i
wywoływana jest procedura BMTUP. Najpierw zapisuje ona aktualny
stan BMEMHI, a potem podnosi go przy pomocy procedury INSEL o
wartość z akumulatora, czyli o 12 bajtów, przez co uzyskuje się
miejsce na umieszczenie parametrów zmiennej sterującej pętli
(wartość początkowa, wartość końcowa i krok).
0100 ;Basic Memory Top UP
0110 ;
0120 BMEMHI = $90
0130 INSEL = $A87A
0140 SAVTST = $B87A
0150 ;
0160 *= $B871
0170 ;
0180 JSR SAVTST
0190 TAY
0200 LDX #BMEMHI
0210 JMP INSEL
Graniczna wartość licznika pętli jest ustalana przy pomocy
procedury LETVAR. Przez kolejne wywołania XLET i GETVAR oblicza
ona wartość tej granicy i przepisuje uzyskany wynik z bufora
wejściowego do rejestru FR0.
0100 ;LET VARiable
0110 ;
0120 GETVAR = $ABE9
0130 XLET = $AADA
0140 ;
0150 *= $AC06
0160 ;
0170 JSR XLET
0180 JMP GETVAR
Obliczona wartość graniczna zmiennej sterującej jest
przepisywana przez procedurę PHREG z rejestru FR0 do
odpowiedniego miejsca stosu bieżącego. Używana jest w tym celu
zawartość rejestru SAVMHI, gdyż w rejestrze BMEMHI znajduje się
już nowy adres szczytu zajętej pamięci.
0100 ;PusH REGister
0110 ;
0120 ACNT1 = $C6
0130 SAVMHI = $C4
0140 ;
0150 *= $B888
0160 ;
0170 LDA #$06
0180 STA ACNT1
0190 LOOP LDA $00,X
0200 STA (SAVMHI),Y
0210 INX
0220 INY
0230 DEC ACNT1
0240 BNE LOOP
0250 RTS
Następnie w rejestrze FR0 umieszczana jest standardowa
wartość kroku pętli (1). Jeśli procedura GETIX wskaże, że
instrukcja FOR ma jeszcze dalszy ciąg, to liczba ta jest
zastępowana przez wartość kroku odczytaną przez procedurę
LETVAR. Kolejne wywołanie PHREG umieszcza odpowiednią wartość
kroku pętli na stosie bieżącym.
Na zakończenie przepisywane są na stos parametry instrukcji
FOR. Są to kolejno: numer zmiennej użytej do sterowania pętli,
numer wiersza zawierającego instrukcję FOR oraz indeks
wskazujący początek instrukcji następującej po FOR. Ponieważ
szczyt stosu jest przy tym ponownie podnoszony o cztery bajty,
więc razem dla zapisania wszystkich informacji zostało
wykorzystane 16 bajtów.
6.3.6. Instrukcja NEXT
Instrukcja NEXT stanowi taką parę dla instrukcji FOR, jak
RETURN dla GOSUB. Poza odtworzeniem adresu początkowego pętli
trzeba tu jeszcze dokonać zmiany stanu licznika pętli i
sprawdzić jego wartość. Do wykonania tych czynności służy
procedura XNEXT. Jej początek jest podobny jak w procedurze
XFOR. Tu jednak nieodnalezienie na stosie zmiennej o podanym
numerze powoduje skok do procedury NFORER i zasygnalizowanie
przez nią błędu (No matching FOR ERror).
0100 ;eXecute NEXT statement
0110 ;
0120 ACNT2 = $C7
0130 BADD = $AD26
0140 BMTUP = $B871
0150 BRETLN = $BDC2
0160 FR1 = $E0
0170 GETSUB = $AD20
0180 INIX = $A8
0190 NFORER = $B91A
0200 PLREG = $B897
0210 RETURN = $BDCB
0220 SAVVAL = $AC0C
0230 STMCUR = $8A
0240 VARBL = $AB81
0250 XPOP = $B83E
0260 ;
0270 *= $B700
0280 ;
0290 LDY INIX
0300 LDA (STMCUR),Y
0310 STA ACNT2
0320 POP JSR XPOP
0330 BCS ERR
0340 BEQ ERR
0350 CMP ACNT2
0360 BNE POP
0370 LDY #$06
0380 JSR PLREG
0390 LDA FR1
0400 PHA
0410 LDA ACNT2
0420 JSR VARBL
0430 JSR BADD
0440 JSR SAVVAL
0450 LDY #$00
0460 JSR PLREG
0470 PLA
0480 BPL BPS
0490 JSR GETSUB
0500 BPL EXIT
0510 RTS
0520 BPS JSR GETSUB
0530 BEQ EXIT
0540 BMI EXIT
0550 END RTS
0560 EXIT LDA #$10
0570 JSR BMTUP
0580 JSR RETURN
0590 CMP #$08
0600 BEQ END
0610 JMP BRETLN
0620 ERR JSR NFORER
Po odnalezieniu parametrów właściwej instrukcji FOR
wywoływana jest procedura PLREG (odwrotna do PHREG). Pobiera
ona ze stosu wartość kroku i dodaje go do aktualnej wartości
zmiennej sterującej, która została odczytana przez procedurę
VARBL. Uzyskany rezultat jest ponownie zapisywany przy pomocy
procedury SAVVAL.
0100 ;PulL REGister
0110 ;
0120 ACNT1 = $C6
0130 FR1 = $E0
0140 BMEMHI = $90
0150 ;
0160 *= $B897
0170 ;
0180 LDA #$06
0190 STA ACNT1
0200 LDX #FR1
0210 LOOP LDA (BMEMHI),Y
0220 STA $00,X
0230 INX
0240 INY
0250 DEC ACNT1
0260 BNE LOOP
0270 RTS
Następne wywołanie PLREG powoduje odczytanie ze stosu
granicznej wartości zmiennej sterującej. Warto przy tym
zauważyć, że dla uproszczenia wartość odczytana przez PLREG
jest zawsze umieszczana w rejestrze FR1. Po wykonaniu
odejmowania przez procedurę GETSUB dalsze postępowanie zależy
od znaku kroku pętli i znaku uzyskanego rezultatu. Jeśli
została osiągnięta lub przekroczona graniczna wartość zmiennej
sterującej, to procedura XNEXT kończy się rozkazem RTS i dalej
wykonywana jest instrukcja następująca po NEXT. W przeciwnym
wypadku przy użyciu procedury RETURN odczytywany jest adres
pierwszej instrukcji pętli i tam przekazywane jest sterowanie.
6.3.7. Instrukcja IF
Instrukcja IF umożliwia warunkowe wykonanie innych
instrukcji, ewentualnie skoku, jeśli zamiast instrukcji
umieszczony w niej będzie numer wiersza. Jej procedura
realizacyjna - XIF - jest bardzo prosta.
0100 ;eXecute IF statement
0110 ;
0120 BUFIX = $9F
0130 FR0 = $D4
0140 GETIX = $B904
0150 LETVAR = $AC06
0160 OUTIX = $A7
0170 XGOTO = $B6D5
0180 ;
0190 *= $B778
0200 ;
0210 JSR LETVAR
0220 LDA FR0+1
0230 BEQ MIX
0240 JSR GETIX
0250 BCS END
0260 JMP XGOTO
0270 MIX LDA BUFIX
0280 STA OUTIX
0290 END RTS
Przede wszystkim przez wywołanie procedury LETVAR obliczane
jest wyrażenie warunkowe. Jeśli wynikiem jest zero, to po
zrównaniu liczników BUFIX i OUTIX procedura się kończy. W takim
przypadku wykonywanie programu jest kontynuowane od początku
następnego wiersza. Jeśli wyrażenie warunkowe jest prawdziwe
(wynik różny od zera), to procedura GETIX porównuje stany
liczników bufora. Napotkanie końca wiersza powoduje skok do
procedury XGOTO, a w przeciwnym wypadku wykonywane są dalsze
instrukcje znajdujące się w tym samym wierszu.
6.3.8. Instrukcja ON
Instrukcja ON jest wielokrotną warunkową instrukcją skoku i
posiada dwie odmiany - ON/GOTO i ON/GOSUB. W zależności od
wartości wyrażenia wykonywany jest skok do wiersza, którego
numer jest umieszczony na odpowiedniej pozycji w instrukcji.
Procedurą realizacyjną tej instrukcji jest XON.
Rozpoczyna się ona odczytaniem wartości wyrażenia przy
pomocy procedury GETBYT. Wynika z tego, że wyrażenie musi dawać
wynik całkowity, mniejszy od 256, gdyż w innym przypadku
wystąpi błąd. Zerowa wartość tego wyrażenia powoduje
natychmiastowe opuszczenie procedury XON. Następnie sprawdzany
jest rodzaj instrukcji ON. Jeśli jest to ON/GOSUB, to adres
powrotny zapamiętywany jest przez wywołanie procedury SAVSTM.
Teraz w pętli kolejno odczytywane są numery wierszy i
zmniejszana jest obliczona wartość wyrażenia (przepisana do
rejestru TMPIX). Wyzerowanie tego rejestru oznacza napotkanie
właściwego numeru wiersza i powoduje wykonanie skoku przez
przejście do środka procedury XGOSUB w miejsce oznaczone
etykietą GOLINE. Jeśli wartość wyrażenia jest większa od liczby
numerów wierszy zawartych w instrukcji ON, to procedura XON
jest opuszczana bez wykonania skoku. W takim przypadku adres
powrotny dla ON/GOSUB jest jeszcze zdejmowany ze stosu przez
procedurę XPOP.
0100 ;eXecute ON statement
0110 ;
0120 FR0 = $D4
0130 GETBYT = $ABE0
0140 GETIX = $B904
0150 GLNNUM = $ABCD
0160 GOLINE = $B6D8
0170 INIX = $A8
0180 SAVIX = $B883
0190 SAVSTM = $B6F9
0200 STMCUR = $8A
0210 TMPIX = $B3
0220 XPOP = $B83E
0230 ;
0240 *= $B7E4
0250 ;
0260 JSR SAVIX
0270 JSR GETBYT
0280 LDA FR0
0290 BEQ END
0300 LDY INIX
0310 DEY
0320 LDA (STMCUR),Y
0330 CMP #$17
0340 PHP
0350 BEQ SKIP
0360 JSR SAVSTM+3
0370 SKIP LDA FR0
0380 STA TMPIX
0390 NEXT JSR GLNNUM
0400 DEC TMPIX
0410 BEQ JUMP
0420 JSR GETIX
0430 BCC NEXT
0440 PLP
0450 BEQ END
0460 JSR XPOP
0470 END RTS
0480 JUMP PLP
0490 JMP GOLINE
6.4. Obsługa komunikacji
Wiele procedur interpretera komunikuje się z urządzeniami
zewnętrznymi. Przeważnie są to procedury wykonawcze instrukcji
wejścia/wyjścia, lecz nie tylko. Do realizacji tego celu
przeznaczony jest zestaw procedur obsługujących komunikację z
urządzeniami zewnętrznymi. Ze względu na wykorzystywanie przez
liczne procedury realizujące instrukcje Basica zestaw ten
został wydzielony w osobnym rozdziale.
Podstawową procedurą komunikacji z urządzeniami
zewnętrznymi jest PRPCHN. Przygotowuje i przeprowadza ona
operacje wejścia/wyjścia, korzystając przy tym z innych
procedur pomocniczych. Przed jej wywołaniem w rejestrze IOCHN
(I/O CHannel Number) trzeba umieścić numer wykorzystywanego
kanału IOCB, a w rejestrze IOCMD (I/O CoMmanD) kod rozkazu
wykonywanej operacji. Ponadto, jeśli jest to operacja zapisu, w
akumulatorze powinien znajdować się przesyłany znak. Przy
wywoływaniu tej procedury od etykiety PRPDVC trzeba dodatkowo
umieścić w rejestrze X numer kanału IOCB.
0100 ENDIO = $BCBB
0110 ICAX1 = $034A
0120 ICAX1Z = $2A
0130 ICAX2 = $034B
0140 ICAX2Z = $2B
0150 IOCAL = $BAB2
0160 IOCHN = $B5
0170 SAVDVC = $BAC0
0180 ;
0190 *= $BA99
0200 ;
0210 ;PRePare CHaNnel
0220 ;
0230 PRPCHN LDX IOCHN
0240 ;
0250 ;PRePare DeViCe
0260 ;
0270 PRPDVC PHA
0280 JSR SAVDVC
0290 LDA ICAX1,X
0300 STA ICAX1Z
0310 LDA ICAX2,X
0320 STA ICAX2Z
0330 PLA
0340 TAY
0350 JSR IOCAL
0360 TYA
0370 JMP ENDIO+3
Na początku, po odczytaniu numeru kanału z rejestru IOCHN i
odłożeniu przesyłanego znaku na stosie, wywoływana jest
procedura SAVDVC. Zapisuje ona numer kanału i przelicza go na
postać wymaganą przez system operacyjny (zob. "Mapa pamięci
Atari XL/XE. Procedury wejścia/wyjścia"), a wynik zwraca w
rejestrze X. Następnie parametry transmisji z odpowiedniego
bloku IOCB przepisywane są na stronę zerową. Teraz przesyłany
znak jest zdejmowany ze stosu do rejestru Y i wywoływana jest
procedura IOCAL.
0100 ;Input/Output routine CAL1
0110 ;
0120 ICPUTB = $0346
0130 ;
0140 *= $BAB2
0150 ;
0160 LDA ICPUTB+1,X
0170 PHA
0180 LDA ICPUTB,X
0190 PHA
0200 TYA
0210 LDY #$92
0220 RTS
Jej zadaniem jest uruchomienie odpowiedniej procedury
wykonującej operację I/O. W tym celu adres tej procedury jest
przepisywany na stos, a przesyłany znak do akumulatora, zaś do
rejestru Y zapisywany jest kod nieistniejącej operacji
(Function Not Implemented). Wykonanie teraz rozkazu RTS
powoduje zdjęcie ze stosu adresu procedury transmisji i tym
samym wykonanie operacji. Po jej zakończeniu status operacji
przepisywany jest z rejestru Y do akumulatora. Procedura PRPCHN
kończy się skokiem do specjalnej procedury kończącej operacje
wejścia/wyjścia - ENDIO.
Wspomniana wcześniej procedura SAVDVC jest fragmentem nieco
większej procedury SAVCMD. Umieszcza ona jedynie zawartość
akumulatora w rejestrze IOCMD (tylko przy wywołaniu od SAVCMD)
oraz zawartość rejestru X w IODVC (I/O DeViCe). Potem następuje
bezpośredni skok do procedury MLTCHN.
0100 IOCMD = $C0
0110 IODVC = $C1
0120 MLTCHN = $BCAF
0130 ;
0140 *= $BABE
0150 ;
0160 ;SAVe CoMmanD
0170 ;
0180 SAVCMD STA IOCMD
0190 ;
0200 ;SAVe DeViCe number
0210 ;
0220 SAVDVC STX IODVC
0230 JMP MLTCHN
Także i ta procedura jest częścią większej - SETDVC. System
operacyjny Atari wymaga, aby przy wywołaniu procedur I/O w
rejestrze X znajdował się numer bloku IOCB pomnożony przez 16.
Numer ten jest więc odczytywany z rejestru IODVC i przez
czterokrotne przesunięcie w lewo doprowadzany do właściwej
postaci. Zbyt duży numer kanału powoduje skok do DVCNER i
sygnalizację błędu (DeViCe Number ERror).
0100 DVCNER = $B90C
0110 IODVC = $C1
0120 IXGDAT = $BD07
0130 ;
0140 *= $BCA8
0150 ;
0160 ;SET DeViCe
0170 ;
0180 SETDVC JSR IXGDAT
0190 STA IODVC
0200 BEQ ERR
0210 ;
0220 ;MuLTiple CHannel Number
0230 ;
0240 MLTCHN LDA IODVC
0250 ASL A
0260 ASL A
0270 ASL A
0280 ASL A
0290 TAX
0300 BPL $BD06 ;RTS
0310 ERR JSR DVCNER
Przy wywołaniu tej procedury od etykiety SETDVC ustalany
jest najpierw numer kanału. W tym celu wywoływana jest
procedura IXGDAT, która przy pomocy GLNNUM pobiera z bufora
wejściowego liczbę. Liczba ta zostaje następnie umieszczona w
rejestrze IODVC. Także tu niepoprawny (zerowy) numer kanału
powoduje zasygnalizowanie błędu przez skok do DVCNER.
0100 FR0 = $D4
0110 GLNNUM = $ABCD
0120 INIX = $A8
0130 ;
0140 *= $BD07
0150 ;
0160 ;Increase indeX & Get DATa
0170 ;
0180 IXGDAT INC INIX
0190 ;
0200 ;GET DATa
0210 ;
0220 GETDAT JSR GLNNUM
0230 LDA FR0
0240 RTS
Procedura kończąca operacje I/O służy do kontroli ich
poprawnego wykonania i wstępnej obsługi ewentualnych błędów. W
tym celu najpierw wywoływana jest procedura GETST, która
odczytuje z odpowiedniego bloku IOCB status wykonanej operacji.
Jeśli jest on mniejszy od $80, czyli przebieg operacji był
poprawny, to procedura ENDIO kończy się rozkazem RTS (w tym
przypadku znajduje się on w innej procedurze).
0100 ;END I/O operation
0110 ;
0120 CDST = $A000
0130 CLCHN = $BCF7
0140 DSPFLG = $02FE
0150 ERRCOD = $B9
0160 GETERR = $B934
0170 GETST = $BD00
0180 IODVC = $C1
0190 IRQSTAT = $11
0200 LOADFLG = $CA
0210 PROMPT = $C2
0220 RSTCHN = $BD5B
0230 WRMST2 = $A053
0240 ;
0250 *= $BCBB
0260 ;
0270 JSR GETST
0280 BPL $BD06 ;RTS
0290 LDY #$00
0300 STY DSPFLG
0310 CMP #$80
0320 BNE GST
0330 STY IRQSTAT
0340 LDA LOADFLG
0350 BEQ $BD06 ;RTS
0360 JMP CDST
0370 GST LDY IODVC
0380 CMP #$88
0390 BEQ EXIT
0400 PST STA ERRCOD
0410 CPX #$07
0420 BNE RST
0430 JSR CLCHN
0440 RST JSR RSTCHN
0450 JMP GETERR
0460 EXIT CPX #$07
0470 BNE PST
0480 LDX #$5D
0490 CPX PROMPT
0500 BNE PST
0510 JSR CLCHN
0520 JMP WRMST2
0100 ;GET STatus
0110 ;
0120 ICSTAT = $0343
0130 MLTCHN = $BCAF
0140 ;
0150 *= $BD00
0160 ;
0170 JSR MLTCHN
0180 LDA ICSTAT,X
0190 RTS
Jeśli status wskazuje niepoprawny przebieg operacji, to po
wyzerowaniu znacznika DSPFLG (DiSPlay FLaG) jest on porównywany
z kodem przerwania przez naciśnięcie klawisza BREAK ($80). Taka
przyczyna błędu jest zapisywana w rejestrze IRQSTAT (IRQ STATus
shadow register). Jeżeli operacja I/O dotyczyła odczytu
programu z pamięci masowej, co wskazuje niezerowa zawartość
rejestru LOADFLG (LOADing FLaG), to następuje ponowne
zainicjowanie interpretera przez skok do CDST. W przeciwnym
przypadku procedura ENDIO kończy się rozkazem RTS.
Następnie sprawdzane jest, czy status sygnalizuje
napotkanie końca pliku ($88). Jeśli tak, to w przypadku odczytu
programu zamykany jest kanał 7 (poprzez CLCHN) i wykonywany
jest skok do gorącego startu, a więc interpreter przechodzi do
trybu bezpośredniego bez sygnalizowania błędu. Każdy inny
status błędu powoduje zamknięcie kanału (z wyjątkiem kanału
IOCB 0), ustawienie wyjścia na kanał 0 (przez procedurę RSTCHN)
i sygnalizowanie błędu przez skok do procedury GETERR (zob.
rozdział 3).
Zamknięcie kanału IOCB jest wykonywane przez procedurę
CLCHN. Ponieważ numer kanału jest ustalany przez procedurę
MLTCHN, to zawsze zamykany jest kanał o numerze zapisanym w
rejestrze IODVC. Kanał IOCB 0 jest wykorzystywany przez edytor
ekranowy i jego zamknięcie jest niemożliwe. Zerowy numer kanału
powoduje więc przerwanie procedury rozkazem RTS. W przeciwnym
przypadku do akumulatora wpisywany jest kod operacji CLOSE
($0C) i następuje skok do procedury CIOEXE.
0100 ;CLose CHaNnel
0110 ;
0120 CIOEXE = $BD2B
0130 MLTCHN = $BCAF
0140 ;
0150 *= $BCF7
0160 ;
0170 JSR MLTCHN
0180 BEQ $BD06 ;RTS
0190 LDA #$0C
0200 BNE CIOEXE
Etykieta CIOEXE oznacza końcowy fragment procedury BFLN.
Rozpoczyna się ona od ustalenia długości bufora
wykorzystywanego podczas operacji I/O. Długość tego bufora jest
zależna od miejsca rozpoczęcia procedury. Przy wywołaniu od
BFLN1 długość bufora wynosi $FF, od BFLN2 - zero (znak
przesyłany jest więc w akumulatorze), od BFLN3 - długość
przepisywana jest z rejestru Y (mniejsza od $0100), zaś przy
wywołaniu od BFLN4 długość ustalana jest według zawartości
akumulatora (starszy bajt) i rejestru Y (młodszy bajt).
Teraz (od etykiety SIBUFA) jako adres bufora jest
przepisywany wektor zawarty w rejestrze INBUFP (INput BUFfer
Pointer). Następnie z rejestru IOCMD odczytywany jest do
akumulatora kod rozkazu operacji, która ma być wykonana
(etykieta CMDEXE). Kod ten jest umieszczany w rejestrze ICCMND
(IOCB CoMmaND register) odpowiedniego bloku IOCB. To miejsce
jest właśnie oznaczone etykietą CIOEXE. Cała procedura kończy
się bezpośrednim skokiem do centralnej procedury
wejścia/wyjścia systemu operacyjnego - CIOMAIN (zob. "Mapa
pamięci Atari XL/XE. Procedury wejścia/wyjścia.").
0100 ICBUFA = $0344
0110 ICBUFL = $0348
0120 ICCMND = $0342
0130 INBUFP = $F3
0140 IOCMD = $C0
0150 JCIOMAIN = $E456
0160 ;
0170 *= $BD0F
0180 ;
0190 ;set BuFfer LeNgth
0200 ;
0210 BFLN1 LDY #$FF
0220 BNE BFLN3
0230 BFLN2 LDY #$00
0240 BFLN3 LDA #$00
0250 BFLN4 STA ICBUFL+1,X
0260 TYA
0270 STA ICBUFL,X
0280 ;
0290 ;Set Input BUFfer Address
0300 ;
0310 SIBUFA LDA INBUFP+1
0320 LDY INBUFP
0330 STA INBUFA+1,X
0340 TYA
0350 STA ICBUFA,X
0360 ;
0370 ;CoMmanD EXEcute
0380 ;
0390 CMDEXE LDA IOCMD
0400 ;
0410 ;Central I/O EXEcute
0420 ;
0430 CIOEXE STA ICCMND,X
0440 JMP JCIOMAIN
6.5. Instrukcje dźwiękowe i graficzne
W grupie instrukcji wejścia/wyjścia najczęściej używane są
instrukcje dźwiękowe i graficzne. Dlatego też zostały one
wydzielone w osobnym rozdziale. Są to instrukcje: SOUND,
SETCOLOR, COLOR, POSITION, PLOT, DRAWTO i GRAPHICS. Istnieją
jeszcze inne instrukcje działające na ekranie, lecz ze względu
na ich wykorzystanie przy współpracy z urządzeniami
zewnętrznymi znajdują się one w następnym rozdziale.
6.5.1. Instrukcja SOUND
Instrukcja SOUND powoduje uruchomienie wskazanego
generatora dźwięku. Praca generatora trwa do następnej
przeznaczonej dla niego instrukcji SOUND albo do instrukcji
NEW, END lub RUN. Instrukcja SOUND wymaga czterech parametrów,
które oznaczają kolejno: numer generatora, okres generatora,
rodzaj zniekształceń i głośność dźwięku. Do realizacji tej
instrukcji służy procedura XSOUND.
0100 ;eXecute SOUND statement
0110 ;
0120 AUDC1 = $D201
0130 AUDCTL = $D208
0140 AUDF1 = $D200
0150 FR0 = $D4
0160 GETBYT = $ABE0
0170 LETNUM = $ABD7
0180 SKCTL = $D20F
0190 ;
0200 *= $B9D3
0210 ;
0220 JSR GETBYT
0230 LDA FR0
0240 CMP #$04
0250 BCS $B9D0 ;JMP BVALER
0260 ASL A
0270 PHA
0280 LDA #$00
0290 STA AUDCTL
0300 LDA #$03
0310 STA SKCTL
0320 JSR LETNUM
0330 PLA
0340 PHA
0350 TAX
0360 LDA FR0
0370 STA AUDF1,X
0380 JSR LETNUM
0390 LDA FR0
0400 ASL A
0410 ASL A
0420 ASL A
0430 ASL A
0440 PHA
0450 JSR LETNUM
0460 PLA
0470 TAY
0480 PLA
0490 TAX
0500 TYA
0510 CLC
0520 ADC FR0
0530 STA AUDC1,X
0540 RTS
Jako pierwszy pobierany jest przez procedurę GETNUM numer
generatora. Jeśli jest on większy od 3, to procedura jest
przerywana skokiem do BVALER, gdzie sygnalizowany jest błąd.
Poprawna wartość jest mnożona przez dwa i odkładana na stosie.
Następnie ustawiane są początkowe stany rejestrów AUDCTL (AUDio
ConTroL) oraz SKCTL (Serial/Keyboard ConTroL). Wywołana teraz
procedura LETNUM odczytuje wartość okresu generatora, która
jest wpisywana przy użyciu odtworzonego ze stosu numeru do
rejestru AUDF (AUDio Frequerency) odpowiedniego generatora.
Kolejne wywołanie LETNUM pobiera kod zniekształceń dźwięku.
Jest on mnożony przez 16 i odkładany na stos. Trzecie i
ostatnie wywołanie LETNUM daje wartość głośności dźwięku. Do
tej wartości dodawany jest zdjęty ze stosu kod zniekształceń -
cztery młodsze bity określają więc głośność, a cztery starsze
zniekształcenie. Wynik umieszczany jest w rejestrze AUDC (AUDio
Control) odpowiedniego generatora.
6.5.2. Instrukcja SETCOLOR
Zadaniem instrukcji SETCOLOR jest ustawienie barwy i
jasności koloru w jednym z rejestrów koloru obrazu. Do tego
celu potrzebne są trzy parametry: numer rejestru koloru oraz
barwa i jasność koloru. Procedura realizacyjna tej instrukcji -
XSETC - jest bardzo podobna do procedury XSOUND.
0100 ;eXecute SETCOLOR statement
0110 ;
0120 BVALER = $B92E
0130 COLFP0S = $02C4
0140 FR0 = $D4
0150 GETBYT = $ABE0
0160 LETNUM = $ABD7
0170 ;
0180 *= $B9AD
0190 ;
0200 JSR GETBYT
0210 LDA FR0
0220 CMP #$05
0230 BCS ERR
0240 PHA
0250 JSR LETNUM
0260 LDA FR0
0270 ASL A
0280 ASL A
0290 ASL A
0300 ASL A
0310 PHA
0320 JSR LETNUM
0330 PLA
0340 CLC
0350 ADC FR0
0360 TAY
0370 PLA
0380 TAX
0390 TYA
0400 STA COLPF0S,X
0410 RTS
0420 ERR JSR BVALER
Rozpoczyna się ona również od pobrania przez GETBYT numeru
rejestru, sprawdzenia jego wartości i odłożenia jej na stos.
Następnie przez dwukrotne wywołanie LETNUM odczytywane są dwa
pozostałe parametry. Są one sumowane podobnie jak w procedurze
XSOUND - kod barwy zajmuje cztery starsze bity, a kod jasności
cztery młodsze. Uzyskany wynik jest zapisywany według numeru
pobranego ze stosu do odpowiedniego rejestru koloru obrazu
COLPF (COLor of Play Field - rejestry 0-3) lub tła COLBAK
(COLor of BAcKground - rejestr 4).
6.5.3. Instrukcja COLOR
Instrukcja COLOR ustala numer rejestru koloru lub kod znaku
dla następnych instrukcji graficznych. Numer koloru jest jednak
inny niż użyty w instrukcji SETCOLOR. Rejestrom 0-3 odpowiadają
tu bowiem numery 1-4, zaś rejestrowi 4 (tło) numer 0 (poza
trybami graficznymi 9, 10 i 11, które są sterowane przez GTIA).
Instrukcja COLOR jest realizowana przez procedurę XCOLOR.
Pobiera ona przy pomocy procedury LETNUM liczbę i zapisuje ją
do rejestru COLOR, z którego korzystają inne procedury
graficzne. Wartość tej liczby nie jest tu sprawdzana i może
mieć dowolną wartość mniejszą od $FFFF (większe wartości
spowodują błąd w procedurze LETNUM). Zapisywany jest jednak
tylko młodszy bajt kodu, więc w rezultacie maksymalny numer
koloru lub kod znaku wynosi $FF.
0100 ;eXecute COLOR statement
0110 ;
0120 COLOR = $C8
0130 FR0 = $D4
0140 LETNUM = $ABD7
0150 ;
0160 *= $BA1F
0170 ;
0180 JSR LETNUM
0190 LDA FR0
0200 STA COLOR
0210 RTS
6.5.4. Instrukcja POSITION
Instrukcja POSITION umieszcza kursor na podanej pozycji.
Jest ona realizowana przez procedurę XPOS. Najpierw pozioma
pozycja kursora odczytywana jest przy pomocy LETNUM i jej oba
bajty przepisywane są do rejestru COLCRS (COLumn of CuRSor).
Następnie wywoływana jest procedura GETBYT odczytująca pozycję
pionową, która przepisywana jest do rejestru ROWCRS (ROW of
CuRSor). Odczyt współrzędnych jest wykonywany przez różne
procedury, gdyż maksymalna pozycja pozioma wynosi 319 (liczba
dwubajtowa), zaś maksymalna pozycja pionowa 191, a więc jeden
bajt.
0100 ;eXecute POSITION statement
0110 ;
0120 COLCRS = $55
0130 FR0 = $D4
0140 GETBYT = $ABE0
0150 LETNUM = $ABD7
0160 ROWCRS = $54
0170 ;
0180 *= $BA0C
0190 ;
0200 JSR LETNUM
0210 LDA FR0
0220 STA COLCRS
0230 LDA FR0+1
0240 STA COLCRS+1
0250 JSR GETBYT
0260 LDA FR0
0270 STA ROWCRS
0280 RTS
6.5.5. Instrukcja PLOT
Instrukcja PLOT umieszcza znak (w trybie tekstowym) lub
punkt (w trybie bitowym) we wskazanym miejscu ekranu. Numer
koloru lub kod znaku pobierany jest przy tym z rejestru COLOR.
Operację tą wykonuje procedura XPLOT. Najpierw przez wywołanie
procedury XPOS kursor ustawiany jest na odpowiedniej pozycji.
Następnie do akumulatora pobierany jest numer koloru lub kod
znaku, a do rejestru X numer IOCB 6, który służy do
przeprowadzania operacji graficznych. Potem wykonywany jest
skok do procedury PRPDVC, która przygotowuje i przeprowadza
(przy pomocy procedur systemu operacyjnego) operację zapisu
punktu lub znaku na ekranie.
0100 ;eXecute PLOT statement
0110 ;
0120 COLOR = $C8
0130 PRPDVC = $BA9B
0140 XPOS = $BA0C
0150 ;
0160 *= $BA6C
0170 ;
0180 JSR XPOS
0190 LDA COLOR
0200 LDX #$06
0210 JMP PRPDVC
6.5.6. Instrukcja DRAWTO
Instrukcja DRAWTO rysuje linię od aktualnej pozycji kursora
do pozycji o podanych współrzędnych. Procedura realizacyjna
XDRAW rozpoczyna się, podobnie jak XPLOT, od wywołania XPOS i
odczytania kodu znaku lub numeru koloru. Ta ostatnia wartość
jest teraz przepisywana do rejestru ATACHR (ATASCII CHaRacter).
Następnie w akumulatorze umieszczany jest kod operacji
rysowania linii ($11), a w rejestrze X numer kanału IOCB ($06).
Wartości te są doprowadzane do właściwej postaci i zapisywane
do odpowiednich rejestrów RAM przez wywołanie procedury SAVCMD.
Po ustawieniu parametrów pomocniczych w rejestrach ICAX1 i
ICAX2 (IOCB AuXiliary register) operacja jest wykonywana przez
wywołanie procedury CMDEXE. Po jej przeprowadzeniu XDRAW kończy
się skokiem do ENDIO, gdzie kontrolowany jest jeszcze status
wykonanej operacji.
0100 ;eXecute DRAWTO statement
0110 ;
0120 ATACHR = $02FB
0130 CMDEXE = $BD29
0140 COLOR = $C8
0150 ENDIO = $BCBB
0160 ICAX1 = $034A
0170 ICAX2 = $034B
0180 SAVCMD = $BABE
0190 XPOS = $BA0C
0200 ;
0210 *= $BA27
0220 ;
0230 JSR XPOS
0240 LDA COLOR
0250 STA ATACHR
0260 LDA #$11
0270 LDX #$06
0280 JSR SAVCMD
0290 LDA #$0C
0300 STA ICAX1,X
0310 LDA #$00
0320 STA ICAX2,X
0330 JSR CMDEXE
0340 JMP ENDIO
6.5.7. Instrukcja GRAPHICS
Instrukcja GRAPHICS służy do wyboru trybu graficznego.
Polega to na otwarciu kanału IOCB dla ekranu (Screen) i jest
wykonywane przez instrukcję XGRAPH. Wszystkie operacje na
ekranie są standardowo wykonywane poprzez kanał szósty, więc
najpierw do rejestru IODVC wpisywany jest numer tego kanału i
wywoływana jest procedura CLCHN, która go zamyka. Następnie
wymagany numer trybu graficznego jest odczytywany przez
procedurę LETNUM, a adres nazwy urządzenia SCRNAM jest
przepisywany do wektora bufora INBUFP. Teraz numer trybu jest
pobierany z rejestru FR0 i umieszczany w akumulatorze, a jego
bity 4-7 w rejestrze Y (określają one rodzaj otwarcia
urządzenia). Sama operacja otwarcia przeprowadzana jest przez
procedurę PRPOPN, a kontrola statusu tej operacji przez ENDIO.
0100 ;eXecute GRAPHICS statement
0110 ;
0120 CLCHN = $BCF7
0130 ENDIO = $BCBB
0140 FR0 = $D4
0150 INBUFP = $F3
0160 IODVC = $C1
0170 LETNUM = $ABD7
0180 PRPOPN = $BBD8
0190 ;
0200 *= $BA46
0210 ;
0220 LDX #$06
0230 STX IODVC
0240 JSR CLCHN
0250 JSR LETNUM
0260 LDX # <SCRNAM
0270 LDY # >SCRNAM
0280 STX INBUFP
0290 STY INBUFP+1
0300 LDX #$06
0310 LDA FR0
0320 AND #$F0
0330 EOR #$1C
0340 TAY
0350 LDA FR0
0360 JSR PRPOPN
0370 JMP ENDIO
0380 ;
0390 SCRNAM .BYTE "S:",$9B
Procedura PRPOPN ustala kod wykonywanej operacji na $03
(OPEN) i zapisuje go do odpowiednich rejestrów przy pomocy
SAVCMD. Następnie umieszcza przekazane parametry otwarcia
ekranu w rejestrach ICAX1 i ICAX2. Wywołanie systemowej
procedury I/O, która otwiera ekran, odbywa się za pośrednictwem
SIBUFA (dodatkowo ustala ona adres bufora). Procedura PRPOPN
kończy się bezpośrednim skokiem do STBV, gdzie odtwarzana jest
poprzednia wartość wektora INBUFP.
0100 ;PRePare for OPeN
0110 ;
0120 ICAX1 = $034A
0130 ICAX2 = $034B
0140 SAVCMD = $BABE
0150 SIBUFA = $BD1E
0160 STBV = $DA51
0170 ;
0180 *= $BBD8
0190 ;
0200 PHA
0210 LDA #$03
0220 JSR SAVCMD
0230 PLA
0240 STA ICAX2,X
0250 TYA
0260 STA ICAX1,X
0270 JSR SIBUFA
0280 JMP STBV
6.6. Instrukcje wejścia/wyjścia
Ostatnią, najliczniejszą grupę instrukcji Atari Basic
stanowią instrukcje wejścia/wyjścia. Służą one do komunikacji
komputera z urządzeniami zewnętrznymi, w tym także z edytorem i
ekranem. Są to następujące instrukcje: CLOSE, OPEN, XIO,
STATUS, PUT, GET, LOCATE, PRINT, LPRINT, POINT, NOTE, INPUT,
READ, SAVE, CSAVE, LOAD, CLOAD, ENTER i LIST.
6.6.1. Instrukcja CLOSE
Instrukcja CLOSE zamyka podany kanał wejścia/wyjścia, a
więc służy do zakończenia komunikacji przez odpowiedni blok
IOCB (zob. "Mapa pamięci Atari XL/XE. Procedury
wejścia/wyjścia."). Jest ona realizowana przez procedurę
wykonawczą XCLOSE.
0100 CMDEXE = $BD2B
0110 ENDIO = $BCBB
0120 IOCMD = $C0
0130 SETDVC = $BCA8
0140 ;
0150 *= $BC22
0160 ;
0170 ;eXecute CLOSE statement
0180 ;
0190 XCLOSE LDA #$0C
0200 ;
0210 ;PRoCeed I/O
0220 ;
0230 PRCIO STA IOCMD
0240 JSR SETDVC
0250 ;
0260 ;I/O OPERation
0270 ;
0280 IOOPER JSR CMDEXE
0290 JMP ENDIO
Procedura ta umieszcza w rejestrze IOCMD (I/O CoMmanD) kod
operacji zamknięcia kanału, a następnie przez wywołanie SETDVC
ustala numer kanału według wartości odczytanej z bufora
wejściowego. Operacja jest realizowana przez wywołanie
procedury CMDEXE, a sprawdzenie statusu i obsługę ewentualnego
błędu wykonuje procedura ENDIO.
Dwie dodatkowe etykiety - PRCIO i IOOPER - pozwalają na
wykorzystanie tej procedury do innych operacji I/O. Po
wywołaniu od PRCIO na kanale, którego numer zawiera rejestr X,
wykonywana jest operacja, której kod umieszczony jest w
akumulatorze. Realizacja operacji I/O, której parametry zostały
już wcześniej ustalone, następuje po wywołaniu procedury od
etykiety IOOPER.
6.6.2. Instrukcje XIO i OPEN
Instrukcja XIO jest uniwersalną instrukcją wejścia/wyjścia
i służy do wykonania dowolnej operacji, której kod zostanie
podany jako pierwszy parametr. Kolejnymi parametrami są: numer
kanału, dwa parametry pomocnicze i nazwa pliku lub urządzenia.
Instrukcja OPEN dokonuje otwarcia kanału IOCB do komunikacji z
urządzeniem zewnętrznym. Jej składnia jest taka sama jak
instrukcji XIO, z wyjątkiem pierwszego parametru (numer kanału,
dwa parametry pomocnicze i nazwa pliku lub urządzenia).
Procedury wykonawcze tych instrukcji - XXIO i XOPEN - mają
wspólny przebieg, a różnią się jedynie etapem początkowym. XXIO
rozpoczyna się od wywołania procedury GETDAT, która odczytuje
kod operacji do wykonania. Na początku procedury XOPEN kod ten
jest ustalany na $03 (OPEN). Teraz - od zapisania kodu w
rejestrze IOCMD - obie procedury przebiegają wspólnie.
Następnie procedura SETDVC odczytuje z bufora numer
wykorzystywanego kanału IOCB. Dwie dalsze wartości (parametry
pomocnicze) są odczytywane przez kolejne wywołania procedury
GETDAT i umieszczane w akumulatorze (pierwszy parametr) i
rejestrze Y (drugi parametr). Warto przy tym zwrócić uwagę, że
zarówno procedura SETDVC, jak i GETDAT przyjmują bez
sygnalizowania błędu dane dwubajtowe, lecz uwzględniany jest
tylko młodszy bajt danej.
0100 BFLN1 = $BD0F
0110 ENDIO = $BCBB
0120 GETDAT = $BD09
0130 GFILSP = $BD7D
0140 ICAX1 = $034A
0150 ICAX2 = $034B
0160 IOCMD = $C0
0170 MLTCHN = $BCAF
0180 PSTMAD = $BD9D
0190 SETDVC = $BCA8
0200 STBV = $DA51
0210 CLET = $AADA
0220 ;
0230 *= $BBEC
0240 ;
0250 ;eXecute XIO statement
0260 ;
0270 XXIO JSR GETDAT
0280 JMP EXE
0290 ;
0300 ;eXecute OPEN statement
0310 ;
0320 XOPEN LDA #$03
0330 EXE STA IOCMD
0340 JSR SETDVC
0350 JSR GETDAT
0360 PHA
0370 JSR GETDAT
0380 TAY
0390 PLA
0400 ;
0410 ;OPeN CHaNnel
0420 ;
0430 OPNCHN PHA
0440 TYA
0450 PHA
0460 JSR XLET
0470 JSR GFILSP
0480 JSR MLTCHN
0490 PLA
0500 STA ICAX2,X
0510 PLA
0520 STA ICAX1,X
0530 JSR BFLN1
0540 JSR PSTMAD
0550 JSR STBV
0560 JMP ENDIO
Realizacja operacji I/O rozpoczyna się od etykiety OPNCHN.
Najpierw wartości przekazane w akumulatorze i rejestrze Y są
odkładane na stosie i wywoływana jest procedura XLET. Według
obliczonego przez nią adresu procedura GFILSP odczytuje
specyfikację pliku lub urządzenia. Następnie poprzez MLTCHN
obliczany jest poprawny numer bloku IOCB i zdjęte ze stosu
parametry dodatkowe przepisywane są do rejestrów ICAX1 i ICAX2.
Ustalenie pozostałych parametrów i wykonanie operacji jest
przeprowadzane bezpośrednio przez procedurę BFLN. Przed
kończącym procedurę skokiem do ENDIO odtwarzany jest jeszcze
przy pomocy STBV wektor bufora wejściowego.
6.6.3. Instrukcja STATUS
Instrukcja STATUS odczytuje status podanego urządzenia lub
pliku, a jego wartość przypisuje wskazanej zmiennej. Jej
procedurą wykonawczą jest XSTAT.
0100 ;eXecute STATUS statement
0110 ;
0120 CIOEXE = $BD2B
0130 GETST = $BD00
0140 SAVBYT = $BD31
0150 SETDVC = $BCA8
0160 ;
0170 *= $BC2F
0180 ;
0190 JSR SETDVC
0200 LDA #$0D
0210 JSR CIOEXE
0220 JSR GETST
0230 JMP SAVBYT
Po odczytaniu przez procedurę SETDVC numeru kanału, do
akumulatora wpisywany jest kod operacji STATUS ($0D). Operacja
jest realizowana przez wywołanie procedury CIOEXE, a jej status
odczytuje procedura GETST. Uzyskany wynik jest zapisywany przy
pomocy procedury SAVBYT.
Najpierw odkłada ona na stosie przekazaną w akumulatorze
wartość (a przy wywołaniu od etykiety SAVWRD także wartość z
rejestu Y). Następnie przez wywołanie procedury LETVAR
odczytywana jest wskazana zmienna. Zdjęte ze stosu wartości
wpisywane są do jej dwóch pierwszych bajtów i przez wywołanie
procedury IFP zamieniane na liczbę zmiennoprzecinkową.
Procedura XSTAT kończy się skokiem do SAVVAL, gdzie uzyskana
liczba jest przepisywana do tablicy wartości zmiennych.
0100 FR0 = $D4
0110 IFP = $D9AA
0120 LETVAR = $AC06
0130 SAVVAL = $AC0C
0140 ;
0150 *= $BD31
0160 ;
0170 ;SAVe BYTe
0180 ;
0190 SAVBYT LDY #$00
0200 ;
0210 ;SAVe WoRD
0220 ;
0230 SAVWRD PHA
0240 TYA
0250 PHA
0260 JSR LETVAR
0270 PLA
0280 STA FR0+1
0290 PLA
0300 STA FR0
0310 JSR IFP
0320 JMP SAVVAL
6.6.4. Instrukcja PUT
Zadaniem instrukcji PUT jest zapisanie we wskazanym kanale
IOCB podanej wartości. Czynność tą wykonuje procedura XPUT,
która rozpoczyna się od ustalenia przy pomocy SETDVC
odpowiedniego numeru kanału.
0100 ;eXecute PUT statement
0110 ;
0120 FR0 = $D4
0130 IODVC = $C1
0140 LETNUM = $ABD7
0150 PRPDVC = $BA9B
0160 SETDVC = $BCA8
0170 ;
0180 *= $BC78
0190 ;
0200 JSR SETDVC
0210 JSR LETNUM
0220 LDA FR0
0230 LDX IODVC
0240 JMP PRPDVC
Następnie w akumulatorze umieszczany jest odczytany przez
procedurę LETNUM bajt do zapisu, zaś w rejestrze X numer kanału
pobrany z IODVC (I/O DeViCe). Procedura XPUT kończy się
bezpośrednim skokiem do PRPDVC, gdzie dokonuje się realizacja
zapisu i kontrola statusu tej operacji.
6.6.5. Instrukcje GET i LOCATE
Zadaniem instrukcji GET jest odczytanie ze wskazanego
kanału IOCB wartości i przypisanie jej podanej zmiennej.
Instrukcja LOCATE wykonuje podobne zadanie, lecz odczyt
następuje zawsze z ekranu i z podanej pozycji kursora.
Procedury wykonawcze tych instrukcji - XGET i XLOCAT - są więc
również podobne.
0100 BFLN3 = $BD15
0110 ENDIO = $BCBB
0120 INBUFP = $F3
0130 IOCMD = $C0
0140 SAVBYT = $BD31
0150 SAVDVC = $BCA8
0160 STBV = $DA51
0170 XPOS = $BA0C
0180 ;
0190 *= $BC85
0200 ;
0210 ;eXecute GET statement
0220 ;
0230 XGET JSR STBV
0240 JSR SETDVC
0250 EXE LDA #$07
0260 STA IOCMD
0270 LDY #$01
0280 JSR BFLN3
0290 JSR ENDIO
0300 LDY #$00
0310 LDA (INBUFP),Y
0320 JMP SAVBYT
0330 ;
0340 ;eXecute LOCATE statement
0350 ;
0360 XLOCAT JSR XPOS
0370 LDX #$06
0380 JSR SAVDVC
0390 BNE EXE
Procedura XGET na początku ustawia przy pomocy STBV adres
bufora dla operacji I/O oraz numer kanału IOCB (przez wywołanie
SETDVC). Natomiast procedura XLOCAT najpierw przez wywołanie
XPOS umieszcza kursor na odpowiedniej pozycji ekranu, a
następnie przy pomocy SAVDVC ustala numer kanału na 6. Dalej
obie procedury przebiegają identycznie. Do rejestru IOCMD
wpisywany jest kod operacji GET BYTE ($07), a procedura BFLN
ustala długość bufora na jeden znak. Po sprawdzeniu poprawności
operacji przy pomocy ENDIO odczytana liczba jest umieszczana w
akumulatorze. Procedury XGET i XLOCAT są opuszczane skokiem do
SAVBYT, która przypisuje otrzymaną wartość wskazanej zmiennej.
6.6.6. Instrukcja PRINT
Instrukcja PRINT służy do zapisywania dowolnych wartości na
urządzeniu zewnętrznym. Numer tego urządzenia powinien być
podany jako pierwszy parametr i oznaczony znakiem "#". W
przypadku braku numeru urządzenia interpreter przyjmuje, że
zapis ma być dokonany do kanału IOCB o numerze 0 czyli do
edytora. W instrukcji PRINT poza wartościami mogą znajdować się
separatory, które służą do nadania odpowiedniej formy
zapisywanej informacji. Separatorami typi są: średnik (;),
przecinek (,), dwukropek (:) i znak końca wiersza (EOL - End Of
Line). Procedura wykonawcza XPRINT, która realizuje tą
instrukcję, rozpoznaje kolejno w pętli elementy (wartości i
separatory) oraz realizuje ich zapis. Przed rozpoczęciem pętli
skok tabulacji (liczba znaków między sąsiednimi pozycjami
tabulacji) jest przepisywany z rejestru PTABW (Position
TABulate Width) do AUXBR, a licznik wyjściowy COX (Current
Output indeX) jest zerowany.
Pierwszą czynnością w każdym przejściu pętli jest odczyt
tokena do zapisu i jego rozpoznanie. Jeśli nie jest to token
żadnego separatora, to wywoływane są procedury XLET i GETVAR,
które kolejno obliczają wartość do zapisu i pobierają ją z
bufora wejściowego. Rodzaj wartości (liczbowa czy tekstowa)
jest rozpoznawany według najstarszego bitu rejestru VART.
Wartości liczbowe są najpierw doprowadzane do standardowego
formatu, w którym przedstawiane jest dziewięć cyfr znaczących.
Po zamianie tej liczby na ciąg znaków ASCII (przez procedurę
FASC) kolejne znaki są odczytywane z bufora i zapisywane przy
pomocy procedury RSTHIB (zob. niżej). Ostatni znak liczby jest
sygnalizowany przez ustawienie w nim najstarszego bitu. Po jego
zapisaniu następuje skok do początku głównej pętli (PRNT).
Przed zapisem wartości tekstowej wywoływana jest procedura
FTARV, która odczytuje parametry ciągu. Następnie kolejne znaki
ciągu są zapisywane przez procedurę INOUTX. Jako licznik znaków
służą w tym przypadku trzeci i czwarty bajt informacji o ciągu,
które zawierają jego aktualną długość. Po zapisaniu całego
żądanego tekstu następuje powrót do pętli głównej.
Rozpoznanie tokena numeru kanału ($1C) powoduje wywołanie
procedury IXGDAT. Odczytany przez nią numer IOCB jest
przepisywany do rejestru IOCHN i procedura XPRINT przechodzi do
rozpoznawania następnego tokena.
Średnik (;) powoduje zapisanie następnej wartości
bezpośrednio po ostatnio zapisanej. Po odczytaniu jego tokena
($15) sprawdzany jest następny token. Jeżeli jest to znak inny
niż dwukropek (:) lub koniec wiersza (znak RETURN), to
powtarzana jest główna pętla procedury. W przeciwnym przypadku
zerowany jest rejestr IOCHN (odtwarza to standardowe urządzenie
wyjścia - edytor) i procedura XPRINT się kończy.
W każdym innym - niż opisany powyżej - przypadku
rozpoznanie tokena kończącego instrukcję PRINT (dwukropek - $14
lub koniec wiersza - $16) powoduje wywołanie procedury INOUTX
(wewnątrz RSTHIB), która zapisuje znak RETURN ($9B). Po
wyzerowaniu rejestru IOCHN procedura XPRINT także się kończy.
0100 ;eXecute PRINT statement
0110 ;
0120 AUXBR = $AF
0130 CIX = $F2
0140 COX = $94
0150 FASC = $D8E6
0160 FR0 = $D4
0170 FTARV = $AB90
0180 GETVAR = $ABE9
0190 INBUFP = $F3
0200 INIX = $A8
0210 INOUTX = $B491
0220 IOCHN = $B5
0230 IXGDAT = $BD07
0240 PTABW = $C9
0250 RSTHIB = $B48F
0260 STMCUR = $8A
0270 VART = $D2
0280 XLET = $AADA
0290 ;
0300 *= $B3DA
0310 ;
0320 LDA PTABW
0330 STA AUXBR
0340 LDA #$00
0350 STA COX
0360 PRNT LDY INIX
0370 LDA (STMCUR),Y
0380 CMP #$12 ,
0390 BEQ COM
0400 CMP #$16 EOL
0410 BEQ EOL
0420 CMP #$14 :
0430 BEQ EOL
0440 CMP #$15 ;
0450 BEQ SCL
0460 CMP #$1C #
0470 BEQ DVC
0480 JSR XLET
0490 JSR GETVAR
0500 DEC INIX
0510 BIT VART
0520 BMI ARR
0530 LDA FR0+1
0540 CMP #$10
0550 BCC ASC
0560 LDA FR0+5
0570 AND #$F0
0580 STA FR0+5
0590 ASC JSR FASC
0600 LDA #$00
0610 STA CIX
0620 DIG LDY CIX
0630 LDA (INBUFP),Y
0640 PHA
0650 INC CIX
0660 JSR RSTHIB
0670 PLA
0680 BPL DIG
0690 BMI PRNT
0700 ARR JSR FTARV+3
0710 LDA #$00
0720 STA CIX
0730 LOOP LDA FR0+2
0740 BNE DCN
0750 DEC FR0+3
0760 BMI PRNT
0770 DCN DEC FR0+2
0780 LDY CIX
0790 LDA (FR0),Y
0800 INC CIX
0810 BNE NXT
0820 INC FR0+1
0830 NXT JSR INOUTX
0840 JMP LOOP
0850 COM LDY COX
0860 INY
0870 CPY AUXBR
0880 BCC TAB
0890 CLC
0900 LDA PTABW
0910 ADC AUXBR
0920 STA AUXBR
0930 BCC COM
0940 TAB LDY COX
0950 CPY AUXBR
0960 BCS SCL
0970 LDA #$20
0980 JSR RSTHIB
0990 JMP TAB
1000 EOL JMP RET EOL & :
1010 DVC JSR IXGDAT #
1020 STA IOCHN
1030 DEC INIX
1040 JMP PRNT
1050 SCL INC INIX ;
1060 LDY INIX
1070 LDA (STMCUR),Y
1080 CMP #$16 EOL
1090 BEQ END
1100 CMP #$14 :
1110 BEQ END
1120 JMP PRNT
1130 RET LDA #$9B
1140 JSR INOUTX
1150 END LDA #$00
1160 STA IOCHN
1170 RTS
Token przecinka ($12) nakazuje przejście do następnej
pozycji tabulacji. Jest to realizowane przy pomocy pętli, w
której procedura RSTHIB zapisuje spacje (po jednej w każdym
przejściu pętli). Licznikiem tej pętli jest rejestr AUXBR,
który zawiera wielokrotność kroku tabulacji (przepisanego z
rejestru PTABW) zmniejszoną o aktualną pozycję kursora w
zapisywanym wierszu. Gdy wszystkie wymagane spacje zostaną już
zapisane, następuje przeskok do fragmentu procedury XPRNT
realizującego token średnika (;).
Procedura RSTHIB (i jej fragment oznaczony etykietą INOUTX)
zwiększa stan licznika wyjściowego COX i przekazuje znak podany
w akumulatorze do procedury PRPCHN. W ten sposób znak z
akumulatora zostaje wysłany do urządzenia zewnętrznego. Przy
wywołaniu od RSTHIB dodatkowo w zapisywanym znaku kasowany jest
najstarszy bit.
0100 COX = $94
0110 PRPCHN = $BA99
0120 ;
0130 *= $B48F
0140 ;
0150 ;ReSeT HIgh Bit
0160 ;
0170 RSTHIB AND #$7F
0180 ;
0190 ;INcrease OUTput indeX
0200 ;
0210 INOUTX INC COX
0220 JMP PRPCHN
6.6.7. Instrukcja LPRINT
Instrukcja LPRINT ma podobne zadanie jak PRINT, lecz w tym
przypadku zapis wykonywany jest zawsze na drukarkę i przez
kanał IOCB 7. Realizuje to procedura XLPRNT. Rozpoczyna ją
ustawienie wektora INBUFP na nazwę urządzenia PRTNAM (drukarka
- P:) i wpisanie do rejestru IOCHN numeru kanału ($07).
Następnie urządzenie jest otwierane do zapisu przez wywołanie
procedury PRPOPN z wartością $08 (zapis) w rejestrze Y i $00 w
akumulatorze. Po sprawdzeniu poprawności otwarcia przez
procedurę ENDIO właściwy zapis realizowany jest przez XPRINT.
Po wykonaniu operacji wykorzystywany kanał IOCB jest zamykany
przez procedurę CLCHN.
0100 ;eXecute LPRINT statement
0110 ;
0120 CLCHN = $BCF7
0130 ENDIO = $BCBB
0140 INBUFP = $F3
0150 IOCHN = $B5
0160 PRPOPN = $BBD8
0170 XPRINT = $B3DA
0180 ;
0190 *= $B496
0200 ;
0210 LDA # <PRTNAM
0220 STA INBUFP
0230 LDA # >PRTNAM
0240 STA INBUFP+1
0250 LDX #$07
0260 STX IOCHN
0270 LDA #$00
0280 LDY #$08
0290 JSR PRPOPN
0300 JSR ENDIO
0310 JSR XPRINT
0320 JMP CLCHN
0330 ;
0340 PRTNAM .BYTE "P:",$9B
6.6.8. Instrukcja POINT
Instrukcja POINT ustawia głowicę zapisująco-odczytującą
stacji dysków na podanym bajcie we wskazanym sektorze. Jej
procedurą wykonawczą jest XPOINT, która po ustaleniu
potrzebnych parametrów wywołuje systemową procedurę CIOMAIN.
0100 ;eXecute POINT statement
0110 ;
0120 FR0 = $D4
0130 GLNNUM = $ABCD
0140 ICAX3 = $034C
0150 ICAX4 = $034D
0160 ICAX5 = $034E
0170 IOCMD = $C0
0180 IOOPER = $BC29
0190 MLTCHN = $BCAF
0200 SETDVC = $BCA8
0210 ;
0220 *= $BC54
0230 ;
0240 JSR SETDVC
0250 JSR GLNNUM
0260 JSR MLTCHN
0270 LDA FR0
0280 STA ICAX3,X
0290 LDA FR0+1
0300 STA ICAX4,X
0310 JSR GLNNUM
0320 JSR MLTCHN
0330 LDA FR0
0340 STA ICAX5,X
0350 LDA #$25
0360 STA IOCMD
0370 BNE IOOPER
Pierwszym parametrem jest numer kanału IOCB, który jest
pobierany z bufora wejściowego przez procedurę SETDVC.
Pozostałe parametry odczytywane są przez dwukrotne wywołanie
procedury GLNNUM, przy czym procedura MLTCHN służy każdorazowo
do obliczenia poprawnego adresu IOCB. Odczytane wartości są
umieszczane w rejestrach ICAX3 i ICAX4 (numer sektora) oraz
ICAX5 (numer bajtu w sektorze). Po tych przygotowaniach do
rejestru IOCMD wpisywany jest kod operacji POINT ($25) i
wykonywany jest skok do procedury IOOPER, która wywołuje
systemową procedurę I/O.
6.6.9. Instrukcja NOTE
Odwrotnością POINT jest instrukcja NOTE. Odczytuje ona
pozycję głowicy stacji dysków i uzyskane rezultaty przypisuje
dwóm wskazanym zmiennym. Procedura wykonawcza tej instrukcji -
XNOTE - jest także odwróceniem procedury XPOINT.
0100 ;eXecute NOTE statement
0110 ;
0120 ICAX3 = $034C
0130 ICAX4 = $034D
0140 ICAX5 = $034E
0150 MLTCHN = $BCAF
0160 PRCIO = $BC24
0170 SAVBYT = $BD31
0180 SAVWRD = $BD33
0190 ;
0200 *= $BC3D
0210 ;
0220 LDA #$26
0230 JSR PRCIO
0240 LDA ICAX3,X
0250 LDY ICAX4,X
0260 JSR SAVWRD
0270 JSR MLTCHN
0280 LDA ICAX5,X
0290 JMP SAVBYT
Rozpoczyna ją wywołanie procedury PRCIO z umieszczonym w
akumulatorze kodem operacji NOTE ($26). Numer sektora, w którym
aktualnie znajduje się głowica stacji dysków, odczytywany jest
z rejestrów ICAX3 i ICAX4, a następnie przekazywany do
odpowiedniej zmiennej przez procedurę SAVWRD. Analogicznie,
procedura SAVBYT przypisuje zmiennej numer bajtu odczytany z
rejestru ICAX5.
6.6.10. Instrukcje INPUT i READ
Instrukcje INPUT i READ służą do odczytu informacji, przy
czym INPUT dokonuje odczytu z urządzenia zewnętrznego (w tym
także z edytora), zaś READ odczytuje dane zawarte w
instrukcjach DATA. Odczytane dane są przypisywane umieszczonym
w instrukcji zmiennym. W obu przypadkach dozwolony jest
jednoczesny (w jednej instrukcji) odczyt dowolnej liczby danych
dowolnego typu. Procedury wykonawcze tych instrukcji są ze sobą
mocno powiązane i dlatego też muszą być opisywane razem.
Procedura wykonawcza instrukcji READ - XREAD - rozpoczyna
się od wpisania adresu wiersza z instrukcją DATA jako wektora
bufora wejściowego. W tym celu aktualny numer wiersza jest
zapamiętywany przy pomocy procedury SAVSTM, a zawartość
rejestru DATALN (DATA LiNe Number) jest przepisywana do CLNN
(Current LiNe Number). Następnie procedura FNDCST odszukuje ten
wiersz w pamięci, a jego adres z rejestru STMCUR (StaTeMent
CURrent address) jest przepisywany do wektora INBUFP (INput
BUFfer Pointer). Teraz wywołanie procedury XRTRN odtwarza adres
aktualnie wykonywanego wiersza z instrukcją READ. Podczas tych
operacji stan licznika wejściowego INIX jest przechowywany na
stosie. Trzeba tu zwrócić uwagę, że rejestr DATALN w momencie
uruchomienia programu zawiera wartość $00, a później może być
zmieniony tylko przez instrukcję READ (zob. dalej) i RESTORE
(zob. rozdział 6.2.3).
Teraz przez kilkakrotne wywoływanie procedury NXTDAT i
stanowiącej jej fragment SENDDT odczytywane są parametry
znalezionego wiersza. Są to kolejno: dwa bajty numeru wiersza
przepisywane do DATALN, długość wiersza przepisywana do
pierwszego bajtu rejestru ZTEMP1 i długość instrukcji
przepisywana do drugiego bajtu ZTEMP1. Procedura NXTDAT nie
jest tu jedank wykorzystywana zgodnie ze swoim przeznaczeniem.
Zasadniczo służy ona do poszukiwania końca odczytywanej danej,
bowiem w rezultacie jej wywołania bit Zero statusu procesora
jest ustawiany, gdy odczytanym znakiem jest przecinek (,) lub
koniec wiersza (EOL). W każdym innym przypadku bit Zero
pozostaje skasowany.
0100 CIX = $F2
0110 INBUFP = $F3
0120 ;
0130 *= $B32D
0140 ;
0150 ;NeXT DATa
0160 ;
0170 NXTDAT INC CIX
0180 ;
0190 ;Search for END of DaTa
0200 ;
0210 SENDDT LDY CIX
0220 LDA (INBUFP),Y
0230 CMP #',
0240 CLC
0250 BEQ END
0260 CMP #$9B
0270 END RTS
Ostatnie w tej fazie wywołanie NXTDAT zwraca token
pierwszej instrukcji w wierszu. Jeżeli nie jest to instrukcja
DATA (token $01), poszukiwany jest token następnej instrukcji w
tym samym wierszu. Po dotarciu do końca wiersza zerowany jest
znacznik DATAD i przeszukiwany jest następny wiersz programu,
aż do napotkania wiersza o numerze przekraczającym $7FFF. W
takim przypadku przez skok do procedury ODATER sygnalizowany
jest błąd (Out of DATa ERror). Następny wiersz poszukiwany jest
także wtedy, gdy stan licznika DATAD przekracza wartość
pierwszego bajtu rejestru ZTEMP1. Po znalezieniu danej, która
ma zostać odczytana, w rejestrze SXFLG (SyntaX FLaG)
umieszczana jest wartość $40, która pozwoli później rozpoznać
rodzaj wykonywanej instrukcji.
0100 ACHNN = $B4 0610 STA INIX
0110 AFP = $D800 0620 LOOP LDY #$00
0120 BINPER = $B924 0630 STY CIX
0130 CIX = $F2 0640 JSR SENDDT
0140 CLNN = $A0 0650 STA DATALN
0150 DATAD = $B6 0660 JSR NXTDAT
0160 DATALN = $B7 0670 STA DATALN+1
0170 FNDCST = $A9A2 0680 JSR NXTDAT
0180 GETIX = $B904 0690 STA ZTEMP1
0190 GIRQST = $A9F2 0700 NXT1 JSR NXTDAT
0200 INBUFP = $F3 0710 STA ZTEMP1+1
0210 INIX = $A8 0720 JSR NXTDAT
0220 INPREC = $BDE4 0730 EOR #$01
0230 IXGDAT = $BD07 0740 BEQ SDT
0240 NXTDAT = $B32D 0750 LDY ZTEMP1+1
0250 ODATER = $B928 0760 CPY ZTEMP1
0260 PROMPT = $C2 0770 BCS BPS1
0270 PUTVAR = $ABB2 0780 DEY
0280 RECVAL = $AB36 0790 STY CIX
0290 RSTPTR = $AB26 0800 BCC NXT1
0300 SAVSTM = $B6F9 0810 BPS1 STY CIX
0310 SAVVAL = $AC0C 0820 DEC CIX
0320 SENDDT = $B32F 0830 GET LDY #$01
0330 STBV = $DA51 0840 LDA (INBUFP),Y
0340 STMCUR = $8A 0850 BMI RERR
0350 STTXT = $AB5C 0860 SEC
0360 SXFLG = $A6 0870 LDA CIX
0370 VART = $D2 0880 ADC INBUFP
0380 XRTRN = $BDA8 0890 STA INBUFP
0390 XSLET = $AE8E 0900 LDA #$00
0400 XSTOP = $B792 0910 STA DATAD
0410 ZTEMP1 = $F5 0920 ADC INBUFP+1
0420 ; 0930 STA INBUFP+1
0430 *= $B2AE 0940 BCC LOOP
0440 ; 0950 SDT STA ZTEMP1
0450 ;eXecute READ statement 0960 NDT LDA ZTEMP1
0460 ; 0970 CMP DATAD
0470 XREAD LDA INIX 0980 BCS NLN
0480 PHA 0990 NXT2 JSR NXTDAT
0490 JSR SAVSTM 1000 BNE NXT2
0500 LDA DATALN 1010 BCS GET
0510 STA CLNN 1020 INC ZTEMP1
0520 LDA DATALN+1 1030 BNE NDT
0530 STA CLNN+1 1040 NLN LDA #$40
0540 JSR FNDCST 1050 STA SXFLG
0550 LDA STMCUR 1060 INC CIX
0560 STA INBUFP 1070 BCS RTV
0570 LDA STMCUR+1 1080 ;
0580 STA INBUFP+1 1090 *= $B33B
0590 JSR XRTRN 1100 ;
0600 PLA 1110 RERR JSR ODATER
1120 ; 1480 LDX #$FF
1130 ;eXecute INPUT statement 1490 NST INX
1140 ; 1500 JSR NXTDAT
1150 XINPUT LDA #'? 1510 BNE NST
1160 STA PROMPT 1520 BCS FND
1170 JSR RECVAL 1530 BIT SXFLG
1180 DEC INIX 1540 BVC NST
1190 BCC INP 1550 FND LDY ZTEMP1
1200 JSR IXGDAT 1560 LDA INIX
1210 STA ACHNN 1570 PHA
1220 INP JSR STBV 1580 TXA
1230 JSR INPREC 1590 LDX #INBUFP
1240 JSR GIRQST 1600 JSR STTXT
1250 BEQ BREAK 1610 PLA
1260 LDY #$00 1620 STA INIX
1270 STY SXFLG 1630 JSR XSLET+3
1280 STY CIX 1640 CKE BIT SXFLG
1290 RTV JSR RECVAL 1650 BVC CKI
1300 INC INIX 1660 INC DATAD
1310 LDA VART 1670 JSR GETIX
1320 BMI TEXT 1680 BCS END
1330 JSR AFP 1690 JSR SENDDT
1340 BCS IERR 1700 BCC BPS2
1350 JSR SENDDT 1710 JMP GET
1360 BNE IERR 1720 CKI JSR GETIX
1370 JSR SAVVAL 1730 BCC NXGT
1380 JMP CKE 1740 END JSR STBV
1390 BREAK JMP XSTOP 1750 LDA #$00
1400 IERR LDA #$00 1760 STA ACHNN
1410 STA ACHNN 1770 RTS
1420 JSR BINPER 1780 NXGT JSR SENDDT
1430 TEXT JSR RSTPTR 1790 BCC BPS2
1440 JSR PUTVAR 1800 JMP INP
1450 DEC CIX 1810 BPS2 INC CIX
1460 LDA CIX 1820 JMP RTV
1470 STA ZTEMP1
Odmienny jest początek procedury wykonawczej instrukcji
INPUT - XINPUT. Najpierw w rejestrze PROMPT (PROMPT character)
umieszczany jest kod znaku zapytania (?) i wywoływana jest
procedura RECVAL. Rozpoznaje ona wartość stanowiącą pierwszy
parametr instrukcji. Jeśli jest to znak "#", to następująca po
nim liczba jest odczytywana przez procedurę IXGDAT i
przepisywana do rejestru ACHNN (Auxiliary CHaNnell Number).
Następnie odtwarzany jest przy pomocy STBV wektor bufora
wejściowego i wywoływana jest procedura INPREC. Pobiera ona
rekord (ciąg znaków zakończony przez RETURN) z urządzenia
zewnętrznego. Jeśli urządzeniem tym jest edytor (IOCB 0), to
najpierw na ekranie jest umieszczany znak z rejestru PROMPT.
Po zakończeniu INPREC wywoływana jest procedura GIRQST,
która sprawdza, czy wykonywana operacja nie została przerwana
przez naciśnięcie klawisza BREAK. W takim przypadku wykonywany
jest skok do procedury XSTOP, która przerywa działanie
programu. Po poprawnym odczytaniu rekordu do rejestru SXFLG
wpisywana jest wartość $00, która oznacza instrukcję INPUT
(zob. wyżej).
Niektóre źródła podają, że przez zmianę wartości w
rejestrze PROMPT można zmienić znak wyświetlany przez
instrukcję INPUT. W świetle tego, co zostało wyżej napisane,
jest to niemożliwe, gdyż przed wykonaniem INPUT do rejestru
PROMPT wpisywany jest kod znaku zapytania. Taka zmiana wymaga
więc zmiany w samym interpreterze Atari Basic.
0100 ACHNN = $B4
0110 BFLN1 = $BD0F
0120 ENDIO = $BCBB
0130 PROMPT = $C2
0140 PRPCHN = $BA99
0150 SAVCMD = $BABE
0160 ;
0170 *= $BDDB
0180 ;
0190 ;PUT RETurn
0200 ;
0210 PUTRET LDX ACHNN
0220 BNE GETREC
0230 LDA #$9B
0240 JSR PRPCHN
0250 ;
0260 ;INPut RECord
0270 ;
0280 INPREC LDX ACHNN
0290 BNE GETREC
0300 LDA PROMPT
0310 JSR PRPCHN
0320 ;
0330 ;GET RECord
0340 ;
0350 GETREC LDX ACHNN
0360 LDA #$05
0370 JSR SAVCMD
0380 JSR BFLN1
0390 JMP ENDIO
Od tego miejsca przebieg obu procedur jest prawie
jednakowy. Najpierw przez wywołanie procedury RECVAL
rozpoznawany jest typ zmiennej. Zmienna liczbowa jest
zamieniana przy pomocy procedury AFP z ciągu cyfr w kodzie
ASCII na postać zmiennoprzecinkową. Gdy podczas tej zamiany
wystąpi błąd lub następny znak nie oznacza końca danej, to
procedura jest przerywana skokiem do BINPER, gdzie
sygnalizowany jest błąd (Bad INPut ERror). Poprawna wartość
jest natomiast przypisywana odpowiedniej zmiennej przez
wywołanie procedury SAVVAL.
W przypadku zmiennej tekstowej jej parametry są umieszczane
przy pomocy procedury PUTVAR w buforze wyjściowym odtworzonym
przez wywołanie RSTPTR. Następnie jest odszukiwany koniec
zmiennej przez kolejne wywołania procedury NXTDAT, przy czym
dla instrukcji INPUT znakiem końca danej jest tylko znak RETURN
(EOL), zaś dla READ także przecinek. Teraz cały ciąg jest
przepisywany do bufora wejściowego przez procedurę STTXT, a
potem przypisywany odpowiedniej zmiennej przez wywołanie XSLET.
Teraz procedura GETIX sprawdza, czy jest to koniec
instrukcji, w przypadku instrukcji READ jest ponadto zwiększany
licznik DATAD (DATa ADdress). Jeżeli cała instrukcja została
wykonana, to procedura STBV odtwarza wektor bufora, zerowany
jest rejestr ACHNN, a procedury XREAD i XINPUT kończą się
rozkazem RTS. Jeśli konieczne są następne dane, to dla
instrukcji INPUT są one ponownie pobierane z podanego
urządzenia (skok do etykiety INP). Kolejna dana dla instrukcji
READ jest odczytywana, gdy koniec poprzedniej był oznaczony
przecinkiem (skok do etykiety RTV). W innym przypadku
poszukiwana jest następna instrukcja DATA (skok do GET).
6.6.11. Instrukcje SAVE i CSAVE
Instrukcja SAVE służy do zapisu stokenizowanego programu w
Atari Basic na dowolnym urządzeniu zewnętrznym. Podobna do niej
instrukcja CSAVE dokonuje tego zapisu zawsze na magnetofon.
Procedury wykonawcze tych instrukcji - XSAVE i XCSAVE - różnią
się jedynie sposobem otwarcia kanału 7, a ich dalszy przebieg
(procedura SAVE) jest identyczny. Procedury te (a także
procedury wykorzystywane przez inne instrukcje) korzystają z
kanału IOCB 7. Należy więc unikać używania tego kanału we
własnych programach.
0100 ;OPeN CHannel #7
0110 ;
0120 CIOEXE = $BD2B
0130 IOCMD = $C0
0140 IODVC = $C1
0150 MLTCHN = $BCAF
0160 OPNCHN = $BC02
0170 ;
0180 *= $BAD7
0190 ;
0200 PHA
0210 LDY #$07
0220 STY IODVC
0230 JSR MLTCHN
0240 LDA #$0C
0250 JSR CIOEXE
0260 LDY #$03
0270 STY IOCMD
0280 PLA
0290 LDY #$00
0300 JSR OPNCHN
0310 LDA #$07
0320 RTS
Procedura XSAVE po umieszczeniu w akumulatorze wartości
$08, która wskazuje zapis, wywołuje procedurę OPNCH7. Tu
najpierw kanał IOCB 7 jest zamykany, a dopiero potem ponownie
otwierany do zapisu. Ponieważ jako drugi parametr pomocniczy
(do rejestru ICAX2) jest przesyłana do procedury OPNCHN wartość
$00, to w przypadku magnetofonu zapis będzie się odbywał z
długimi przerwami. Wykonywane tu zamknięcie kanału przed jego
otwarciem pozwala ustrzec się przed ewentualnymi błędami,
których wystąpienie jest możliwe przy stosowaniu instrukcji
CSAVE (zob. niżej).
0100 ;OPeN CASsette
0110 ;
0120 ENDIO = $BCBB
0130 INBUFP = $F3
0140 PRPOPN = $BBD8
0150 ;
0160 *= $BBB4
0170 ;
0180 NOP
0190 NOP
0200 PHA
0210 LDX # <CASNAM
0220 STX INBUFP
0230 LDX # >CASNAM
0240 STX INBUFP+1
0250 LDX #$07
0260 PLA
0270 TAY
0280 LDA #$80
0290 JSR PRPOPN
0300 JSR ENDIO
0310 LDA #$07
0320 RTS
0330 ;
0340 CASNAM .BYTE "C:",$9B
Procedura XCSAVE także umieszcza w akumulatorze wartość
$08, lecz następnie wywołuje procedurę OPNCAS. Nie zamyka ona
kanału 7 przed jego otwarciem, co może być przyczyną błędów,
jeśli kanał ten jest wykorzystywany w programie. Sama operacja
otwarcia jest przeprowadzona podobnie jak w opisanej wcześniej
procedurze XLPRNT (instrukcja LPRINT). Trzeba tu jednak zwrócić
uwagę na wartość $80 przekazywaną do PRPOPN. Jest ona tam
następnie przepisywana do rejestru ICAX2 i powoduje zapis z
krótkimi przerwami pomiędzy rekordami na kasecie.
0100 BFLN3 = $BD15
0110 BFLN4 = $BD17
0120 CLCHN = $BCF7
0130 ENDIO = $BCBB
0140 INBUFP = $F3
0150 IOCMD = $C0
0160 LBUFF = $0580
0170 LOMEM = $80
0180 MLTCHN = $BCAF
0190 OPNCAS = $BBB4
0200 OPNCH7 = $BAD7
0210 RUNSTK = $8E
0220 VNTP = $82
0230 ;
0240 *= $BB6D
0250 ;
0260 ;eXecute SAVE statement
0270 ;
0280 XSAVE LDA #$08
0290 JSR OPNCH7
0300 ;
0310 ;SAVE routine
0320 ;
0330 SAVE LDA #$0B
0340 STA IOCMD
0350 LDX #LOMEM
0360 LOOP SEC
0370 LDA $00,X
0380 SBC LOMEM
0390 STA [LBUFF-LOMEM],X
0400 INX
0410 LDA $00,X
0420 SBC LOMEM+1
0430 STA [LBUFF-LOMEM],X
0440 INX
0450 CPX #RUNSTK
0460 BCC LOOP
0470 JSR MLTCHN
0480 LDY #$0E
0490 JSR BFLN3
0500 JSR ENDIO
0510 ;
0520 ;PRoGraM area TRansmit
0530 ;
0540 PRGMTR JSR MLTCHN
0550 LDA VNTP
0560 STA INBUFP
0570 LDA VNTP+1
0580 STA INBUFP+1
0590 LDY LBUFF+$0D
0600 DEY
0610 TYA
0620 LDY LBUFF+$0C
0630 JSR BFLN4
0640 JSR ENDIO
0650 JMP CLCHN
0660 ;
0670 *= $BBD1
0680 ;
0690 ;eXecute CSAVE statement
0700 ;
0710 XCSAVE LDA #$08
0720 JSR OPNCAS
0730 BNE SAVE
Procedura SAVE po zapisaniu do rejestru IOCMD kodu operacji
PUT BYTE ($0B), realizuje zapisanie programu w dwóch etapach. W
pierwszym etapie zapisywane są względne wartości wektorów
stosowanych przez interpreter (LOMEM, VNTP, VNTD, VVTP, STMTAB,
STMCUR i STARP). Wartości względne są uzyskiwane przez odjęcie
od każdego wektora wielkości LOMEM. Dzięki temu program będzie
działał poprawnie po odczycie, niezależnie od aktualnej
wartości dolnej granicy dostępnej pamięci RAM. Czyni to program
całkowicie relokowalnym i pozwala na zainstalowanie przed
odczytem programu DOS-u lub dodatkowych procedur pomocniczych w
języku maszynowym (tzw. akcesoriów). Po przepisaniu tych
wektorów do bufora LBUFF (Line BUFFer) są one zapisywane na
urządzenie przez wywołanie procedury BFLN. Poprawność tego
zapisu jest sprawdzana przy pomocy procedury ENDIO.
W drugim etapie zapisywana jest cała treść programu wraz ze
zmiennymi (tablica nazw zmiennych, tablica wartości zmiennych i
tablica instrukcji). W tym celu jako adres bufora do zapisu
podawana jest zawartość wektora VNTP, a długość bufora
określana jest według zmniejszonej o jeden względnej wartości
wektora STARP. Tak określona zawartość bufora jest zapisywana
przez ponowne wywołanie procedury BFLN. Po sprawdzeniu
poprawności wykonanej operacji przez ENDIO procedura SAVE
kończy się skokiem do CLCHN, gdzie wykorzystywany kanał IOCB
jest zamykany.
6.6.12. Instrukcje LOAD i CLOAD
Instrukcje LOAD i CLOAD służą do odczytu stokenizowanego
programu i stanowią odpowiedniki instrukcji SAVE i CSAVE. Poza
punktami wywołań XLOAD i XCLOAD ich procedura wykonawcza
posiada jeszcze etykietę LDPRGM. Służy ona do realizacji
odczytu przez instrukcję RUN w przypadku uruchamiania programu
z urządzenia zewnętrznego. Rodzaj instrukcji jest rozpoznawany
według wartości odkładanej na stosie na początku procedury ($00
dla LOAD i CLOAD, $FF dla RUN).
0100 BFLN3 = $BD15
0110 ENDIO = $BCBB
0120 IOCMD = $C0
0130 LBUFF = $0580
0140 LOADER = $B90A
0150 LOADFLG = $CA
0160 LOMEM = $80
0170 MEMTOP = $02E5
0180 MLTCHN = $BCAF
0190 OPNCAS = $BBB4
0200 OPNCH7 = $BAD7
0210 PRGMTR = $BB98
0220 PRLGER = $B90E
0230 STARP = $8C
0240 VNTP = $82
0250 WRMST = $A050
0260 XCLR = $B766
0270 ;
0280 *= $BAF7
0290 ;
0300 ;LoaD PRoGraM
0310 ;
0320 LDPRGM LDA #$FF
0330 BNE EXE
0340 ;
0350 ;eXecute LOAD statement
0360 ;
0370 XLOAD LDA #$00
0380 EXE PHA
0390 LDA #$04
0400 JSR OPNCH7
0410 PLA
0420 ;
0430 ;LOAD routine
0440 ;
0450 LOAD PHA
0460 LDA #$07
0470 STA IOCMD
0480 STA LOADFLG
0490 JSR MLTCHN
0500 LDY #$0E
0510 JSR BFLN3
0520 JSR ENDIO
0530 LDA LBUFF
0540 ORA LBUFF+1
0550 BNE ERR
0560 LDX #STARP
0570 LOOP CLC
0580 LDA LOMEM
0590 ADC [LBUFF-LOMEM],X
0600 PHP
0610 CLC
0620 ADC #$00
0630 TAY
0640 LDA LOMEM+1
0650 ADC [LBUFF-LOMEM+1],X
0660 PLP
0670 ADC #$00
0680 CMP MEMTOP+1
0690 BCC WRT
0700 BNE PLG
0710 CPY MEMTOP
0720 BCC WRT
0730 PLG JMP PRLGER
0740 WRT STA $01,X
0750 STY $00,X
0760 DEX
0770 DEX
0780 CPX #VNTP
0790 BCS LOOP
0800 JSR PRGMTR
0810 JSR XCLR
0820 LDA #$00
0830 STA LOADFLG
0840 PLA
0850 BEQ WRM
0860 RTS
0870 WRM JMP WRMST
0880 ERR LDA #$00
0890 STA LOADFLG
0900 JSR LOADER
0910 ;
0920 ;eXecute CLOAD statement
0930 ;
0940 XCLOAD LDA #$04
0950 JSR OPNCAS
0960 LDA #$00
0970 BEQ LOAD
Podobnie jak przy zapisie wykonywanie instrukcji rozpoczyna
się od otwarcia kanału IOCB 7 przez odpowiednie procedury
(OPNCH7 lub OPNCAS), lecz w tym przypadku do odczytu - wartość
$04 w akumulatorze. Następnie do rejestru IOCMD wpisywany jest
kod operacji GET BYTE ($07), a znacznik LOADFLG (LOADin FLaG)
otrzymuje wartość niezerową. Znacznik ten pozwala po
wystąpieniu błędu na rozpoznanie, czy sam odczyt był poprawny.
Teraz przez wywołanie BFLN odczytywany jest pierwszy blok
informacji, czyli względne wartości wektorów. Jeśli dwa
pierwsze bajty nie są zerami (LOMEM-LOMEM=0), to procedura jest
przerywana skokiem do LOADER i sygnalizowany jest błąd (LOAD
file ERror). Po dodaniu aktualnej zawartości LOMEM odczytane
wektory są przepisywane do odpowiednich rejestrów. Wartość
każdego z nich jest przy tym porównywana z wektorem MEMTOP, a
jeśli go przekracza, to przez skok do procedury PRLGER
sygnalizowany jest błąd (PRogram too LonG ERror).
Druga faza (odczyt treści programu) jest realizowana przez
wywołanie procedury PRGMTR, która jest fragmentem SAVE. Dla
zapewnienia poprawnej pracy programu jest teraz wywoływana
procedura XCLR kasująca wartości i deklaracje wszystkich
zmiennych. Po zakończeniu odczytu zerowany jest znacznik
LOADFLG. Sposób opuszczenia procedury LOAD zależy od wartości
zdjętej ze stosu. Zero (instrukcja LOAD lub CLOAD) powoduje
skok do WRMST i gorący start interpretera. Inne wartości
wskazują na wywołanie z procedury XRUN i powrót do niej jest
wykonywany przez rozkaz RTS.
6.6.13. Instrukcja ENTER
Instrukcja ENTER pozwala na odczyt z urządzenia
zewnętrznego programu zapisanego w postaci pliku znaków ASCII,
to znaczy niestokenizowanego. Ponieważ jej procedura wykonawcza
- XENTER - nie zmienia wektorów interpretera, to odczytywany
program jest dołączany do znajdującego się już w pamięci.
0100 ;eXecute ENTER statement
0110 ;
0120 ACHNN = $B4
0130 OPNCH7 = $BAD7
0140 SYNTAX = $A060
0150 ;
0160 *= $BAC5
0170 ;
0180 LDA #$04
0190 JSR OPNCH7
0200 STA ACHNN
0210 JMP SYNTAX
Sam przebieg procedury jest bardzo prosty. Najpierw kanał 7
jest otwierany do odczytu przez wywołanie procedury OPNCH7, a
jego numer umieszczany jest w rejestrze ACHNN (Auxiliary
CHaNnel Number). Po tym procedura kończy się skokiem do SYNTAX.
W ten sposób następuje wprowadzanie kolejnych wierszy programu
na normalnych zasadach, lecz z wybranego urządzenia
zewnętrznego zamiast z klawiatury.
6.6.14. Instrukcja LIST
Instrukcja LIST służy do zapisu programu w postaci
niestokenizowanej (plik znaków ASCII) na dowolnym urządzeniu
zewnętrznym. Zapisywany może być cały program lub fragment
określony podanymi numerami wierszy. Jeśli nie zostanie podane
urządzenie do zapisu, to jest on wykonywany do edytora (na
ekran w trybie zero). Instrukcja LIST jest realizowana przez
procedurę XLIST.
Rozpoczyna się ona od przygotowania standardowych
parametrów instrukcji. Numer pierwszego zapisywanego wiersza w
rejestrze CLNN (Current LiNe Number) jest ustalany na zero, zaś
ostatniego - w rejestrze MAXLN (MAXimal LiNe number) - na
$7FFF. Ponadto w rejestrze DSPFLG umieszczana jest wartość
różna od zera, co pozwala na wyświetlanie na ekranie znaków
specjalnych. Na zakończenie tej fazy przy pomocy procedury
PRPCHN wyświetlany jest znak końca wiersza (RETURN) i
wywoływana jest procedura SAVSTM, która zapisuje parametry
aktualnie wykonywanego wiersza.
Teraz rozpoznawane są kolejne parametry instrukcji LIST
(jeśli istnieją). Jeżeli pierwsza wartość - odczytana przy
pomocy procedury LETVAR - jest tekstowa, to oznacza ona
urządzenie, na którym ma być wykonany zapis. W takim przypadku
wywoływana jest procedura OPNWRT, która otwiera do zapisu kanał
IOCB 7. Jej przebieg jest tak prosty, że nie wymaga żadnego
komentarza.
0100 ;OPeN for WRiTe
0110 ;
0120 IOCHN = $B5
0130 OPNCH7 = $BAD7
0140 ;
0150 *= $BACF
0160 ;
0170 LDA #$08
0180 JSR OPNCH7
0190 STA IOCHN
0200 RTS
Kolejna liczba (koniecznie) odczytana przez procedurę
GLNNUM stanowi numer pierwszego z zapisywanych wierszy programu
i jest przepisywana do rejestru CLNN. Ta sama wartość jest
umieszczana w rejestrze MAXLN, gdy w instrukcji LIST nie ma
więcej parametrów. W przeciwnym przypadku numer ostatniego
zapisywanego wiersza (MAXLN) jest odczytywany przez ponowne
wywołanie GLNNUM. Teraz według zawartości CLNN procedura FNDCST
odszukuje w pamięci wiersz o takim numerze lub - jeśli go nie
ma - o najmniejszym wyższym numerze i rozpoczyna się główna
pętla procedury.
0100 ;eXecute LIST statement
0110 ;
0120 CLCHN = $BCF7
0130 CLNN = $A0
0140 DSPFLG = $02FE
0150 FNDCST = $A9A2
0160 FR0 = $D4
0170 GHISTM = $A9E1
0180 GIRQST = $A9F2
0190 GLNLEN = $A9DC
0200 GLNNUM = $ABCD
0210 INIX = $A8
0220 IOCHN = $B5
0230 LETVAR = $AC06
0240 LSTPLN = $B58E
0250 MAXLN = $AD
0260 NXTSTM = $A9D0
0270 OPNWRT = $BACF
0280 OUTIX = $A7
0290 PRPCHN = $BA99
0300 SAVSTM = $B6F9
0310 STMCUR = $8A
0320 VART = $D2
0330 XRTRN = $BDA8
0340 ;
0350 *= $B4B5
0360 ;
0370 LDY #$00
0380 STY CLNN
0390 STY CLNN+1
0400 DEY
0410 STY MAXLN
0420 LDA #$7F
0430 STA MAXLN+1
0440 STA DSPFLG
0450 LDA #$9B
0460 JSR PRPCHN
0470 JSR SAVSTM
0480 EXE LDY INIX
0490 INY
0500 CPY OUTIX
0510 BCS CST
0520 LDA INIX
0530 PHA
0540 JSR LETVAR
0550 PLA
0560 STA INIX
0570 LDA VART
0580 BPL LIN
0590 JSR OPNWRT
0600 JMP EXE
0610 LIN JSR GLNNUM
0620 STA CLNN+1
0630 LDA FR0
0640 STA CLNN
0650 LDY INIX
0660 CPY OUTIX
0670 BEQ MAX
0680 JSR GLNNUM
0690 MAX LDA FR0
0700 STA MAXLN
0710 LDA FR0+1
0720 STA MAXLN+1
0730 CST JSR FNDCST
0740 NXTLN JSR GHISTM
0750 BMI END
0760 LDY #$01
0770 LDA (STMCUR),Y
0780 CMP MAXLN+1
0790 BCC LST
0800 BNE END
0810 DEY
0820 LDA (STMCUR),Y
0830 CMP MAXLN
0840 BCC LST
0850 BNE END
0860 LST JSR LSTPLN
0870 JSR GIRQST
0880 BEQ END
0890 JSR GLNLEN
0900 JSR NXTSTM
0910 JMP NXTLN
0920 END LDA IOCHN
0930 BEQ EXIT
0940 JSR CLCHN
0950 LDA #$00
0960 STA IOCHN
0970 EXIT STA DSPFLG
0980 JMP XRTRN
Przede wszystkim sprawdzane jest ewentualne osiągnięcie
wyznaczonego zakresu wierszy programu. Pętla jest przerywana,
jeśli wiersz do zapisu ma numer większy od zawartego w
rejestrze MAXLN lub większy od $7FFF (czyli jest w trybie
bezpośrednim). W takim przypadku następuje przejście do
końcowej fazy procedury XLIST. Jeżeli zapis był wykonywany na
kanał inny niż IOCB 0, to kanał ten jest zamykany przez
wywołanie CLCHN, a rejestr IOCHN jest zerowany. Zerowany jest
również - niezależnie od użytego IOCB - rejestr DSPFLG. Potem
procedura XLIST jest opuszczana skokiem do XRTRN, gdzie
odtwarzany jest numer wiersza zawierającego instrukcję LIST i
jej adres w tym wierszu.
Jeśli wiersz do zapisu mieści się w wyznaczonym zakresie,
to jego zapis wykonuje procedura LSTPLN (zob. niżej). Następnie
przy pomocy GIRQST sprawdzany jest stan klawisza BREAK. Gdy
został on naciśnięty, procedura XLIST także jest przerywana.
Jeśli nie, to przez wywołanie GLNLEN i NXTSTM znajdowany jest
kolejny wiersz programu i pętla się powtarza.
Poszczególne wiersze programu są zapisywane na wskazanym
urządzeniu zewnętrznym przez procedurę LSTPLN. Najpierw
odczytuje ona numer zapisywanego wiersza i zamienia go na ciąg
znaków ASCII. Adres tego ciągu jest umieszczany w rejestrze
POKADR i wywoływana jest procedura PTMSG2, która dokonuje
zapisu numeru.
0100 ;LiST Program LiNe
0110 ;
0120 BUFIX = $9F
0130 FASC = $D8E6
0140 FR0 = $D4
0150 IFP = $D9AA
0160 INBUFP = $F3
0170 INIX = $A8
0180 OUTIX = $A7
0190 POKADR = $95
0200 PRTLCN = $B5C2
0210 PTMSG2 = $B586
0220 STMCUR = $8A
0230 ;
0240 *= $B58E
0250 ;
0260 LSTPLN LDY #$00
0270 LDA (STMCUR),Y
0280 STA FR0
0290 INY
0300 LDA (STMCUR),Y
0310 STA FR0+1
0320 JSR IFP
0330 JSR FASC
0340 LDA INBUFP
0350 STA POKADR
0360 LDA INBUFP+1
0370 STA POKADR+1
0380 JSR PTMSG2
0390 ;
0400 ;PRinT Program LiNe
0410 ;
0420 PRTPLN LDY #$02
0430 LDA (STMCUR),Y
0440 STA BUFIX
0450 INY
0460 LOOP LDA (STMCUR),Y
0470 STA OUTIX
0480 INY
0490 STY INIX
0500 JSR PRTLCN
0510 LDY OUTIX
0520 CPY BUFIX
0530 BCC LOOP
0540 RTS
Teraz długość wiersza jest przepisywana do rejestru BUFIX
(BUFfer IndeX) - będzie ona stanowić licznik tokenów wiersza.
Następny token, który określa długość instrukcji umieszczany
jest w liczniku OUTIX (OUTput IndeX), zaś numer kolejnego
tokena liczony od początku wiersza w liczniku INIX (INput
IndeX). Zawartość wiersza jest zapisywana kolejno instrukcja po
instrukcji przez procedurę PRTLCN w pętli sterowanej
wymienionymi wyżej licznikami.
Procedura zapisu zawartości wiersza PRTLCN jest sterowana
przez pomocniczą procedurę INCIX oraz będącą jej fragmentem
CMPIX. Procedura ta porównuje stany liczników INIX i OUTIX.
Jeżeli rezultat sygnalizuje zakończenie zapisywanej instrukcji,
to, po zdjęciu ze stosu adresu powrotnego, procedura kończy się
rozkazem RTS. Powoduje to powrót bezpośrednio do procedury
LSTPLN z pominięciem miejsca wywołania w PRTLCN. W przeciwnym
razie kolejny token jest odczytywany z wiersza i przekazywany w
akumulatorze do procedury PRTLCN.
0100 INIX = $A8
0110 OUTIX = $A7
0120 STMCUR = $8A
0130 ;
0140 *= $B661
0150 ;
0160 ;INCrease IndeX
0170 ;
0180 INCIX INC INIX
0190 ;
0200 ;CoMPare IndeX
0210 ;
0220 CMPIX LDY INIX
0230 CPY OUTIX
0240 BCS EXIT
0250 LDA (STMCUR),Y
0260 RTS
0270 EXIT PLA
0280 PLA
0290 RTS
Przy zapisie instrukcji pierwszy odczytany token
porównywany jest najpierw z kodem opuszczonej instrukcji LET
($36). Jego rozpoznanie powoduje od razu przejście do następnej
fazy procedury. Każdy inny token powoduje wywołanie procedury
PUTSTM, która zapisuje słowo kluczowe tej instrukcji. Jeśli
była to instrukcja REM ($00) lub DATA ($01) albo token oznaczał
błąd składni ($37), to pozostała zawartość wiersza jest
zapisywana bez żadnych zmian.
Dla pozostałych instrukcji odczytywany jest następny token.
Jego wartość większa od $7F oznacza numer zmiennej. Nazwa
zmiennej o takim numerze jest odszukiwana w tablicy nazw
zmiennych przez procedurę FNDPEL i zapisywana przy pomocy
PUTTXT. Jeżeli koniec instrukcji nie został jeszcze osiągnięty,
to rozpoznawany jest następny token.
Wartość mniejsza od $0F (możliwa jest tylko $0E)
sygnalizuje stałą liczbową. Stała ta jest odczytywana przez
procedurę NUMBER i zamieniana na ciąg znaków ASCII. Zapis tego
ciągu jest wykonywany przy użyciu procedury PUTTXT, identycznie
jak numer wiersza w procedurze LSTPLN.
Wartość tokena równa $0F wskazuje stałą tekstową. Najpierw
długość tej stałej jest przepisywana do rejestru AUXBR i
procedura PRPCHN zapisuje znak cudzysłowu ("). Teraz przy
wykorzystaniu rejestru AUXBR jako licznika kolejne znaki stałej
są zapisywane przez procedurę PRPCHN. Na końcu PRPCHN zapisuje
jeszcze jeden znak cudzysłowu.
0100 ;PRinT Line CoNtens
0110 ;
0120 AUXBR = $AF
0130 CHKLTR = $A3EC
0140 CMPIX = $B663
0150 FASC = $D8E6
0160 FNDPEL = $B53E
0170 INBUFP = $F3
0180 INCIX = $B661
0190 INIX = $A8
0200 NUMBER = $AB45
0210 OPFN = $A7DE
0220 POKADR = $95
0230 PRPCHN = $BA99
0240 PTMSG1 = $B581
0250 PUTSTM = $B66F
0260 PUTTXT = $B567
0270 VNTP = $82
0280 ;
0290 *= $B5C2
0300 ;
0310 JSR CMPIX
0320 CMP #$36
0330 BEQ CHCK
0340 JSR PUTSTM
0350 JSR CMPIX
0360 CMP #$37
0370 BEQ PUT
0380 CMP #$02
0390 BCS CHCK
0400 PUT JSR INCIX
0410 JSR PRPCHN
0420 JMP PUT
0430 CHCK JSR INCIX
0440 BPL NUM
0450 AND #$7F
0460 STA AUXBR
0470 LDX #$00
0480 LDA VNTP+1
0490 LDY VNTP
0500 JSR FNDPEL
0510 JSR PUTTXT
0520 CMP #['(+$80]
0530 BNE CHCK
0540 JSR INCIX
0550 JMP CHCK
0560 NUM CMP #$0F
0570 BEQ STR
0580 BCS OPER
0590 JSR NUMBER
0600 DEC INIX
0610 JSR FASC
0620 LDA INBUFP
0630 STA POKADR
0640 LDA INBUFP+1
0650 STA POKADR+1
0660 PRNT JSR PUTTXT
0670 JMP CHCK
0680 STR JSR INCIX
0690 STA AUXBR
0700 LDA #'"
0710 JSR PRPCHN
0720 LDA AUXBR
0730 BEQ END
0740 CHR JSR INCIX
0750 JSR PRPCHN
0760 DEC AUXBR
0770 BNE CHR
0780 END LDA #'"
0790 JSR PRPCHN
0800 JMP CHCK
0810 OPER SEC
0820 SBC #$10
0830 STA AUXBR
0840 LDX #$00
0850 LDA # >OPFN
0860 LDY # <OPFN
0870 JSR FNDPEL
0880 JSR CMPIX
0890 CMP #$3D
0900 BCS PRNT
0910 LDY #$00
0920 LDA (POKADR),Y
0930 AND #$7F
0940 JSR CHKLTR
0950 BCS PRNT
0960 JSR PTMSG1
0970 JMP CHCK
Każdy token, inny niż dotychczas opisane, oznacza operator
lub funkcję. Jest on zmniejszany o $10 i procedura FNDPEL
odszukuje odpowiednią nazwę w tablicy OPFN. Znalezione
operatory (oprócz logicznych) są zapisywane przez procedurę
PTMSG1, a nazwy funkcji i operatory logiczne przez PUTTXT.
Po zapisaniu każdego elementu instrukcji następuje
przejście do początku głównej pętli (oznaczonego etykietą CHCK)
procedury PRTCLN i rozpoznanie kolejnego tokena. Opuszczenie
procedury jest realizowane wyłącznie w opisany wcześniej sposób
poprzez procedurę INCIX.
|