Na pożegnanie z Inverse’m postanowiłem przygotować nieco lepszy artykuł z tej serii. Mimo, iż poprzednie były dosyć ciekawe (chociaż proste), to jednak nie za bardzo wciągały czytelnika, przynajmniej tego, co ma BASIC gdzieś. Wiemy, że BASIC, jako najprostszy język programowania, nie nadaje się do robienia demek, jednak nadal jest popularny, czego dowodem jest ciągłe zainteresowanie jego nawet wśród pecetowych koderów. Niektórzy nawet wyśmiewają Assembler będąc w przekonaniu, że to właśnie Basic jest lepszy. Trochę nawet jest w tym prawdy, gdyż studiując jeden kod pecetowski natknąłem się na stos NOP-ów, które w BASIC-u można by było zastąpić jedną pętlą.
No, ale nie o tym chciałem tu rozmawiać, ale o BASIC-u z komody. Otóż każdy zapewne zna komplet instrukcji READ, DATA, RESTORE. Co one oznaczają? READ czyta dane z linii DATA, a RESTORE umożliwia ponowny odczyt danych w liniach DATA przez READ od samego początku. Jednak, gdy piszemy długi program z wieloma procedurami zawartymi w liniach DATA, które nie zawsze są jedna pod drugą, tylko rozrzucone po całym listingu i chcemy odczytać dane z n-tej linii, to powstaje problem. Nasz komputer zawsze zaczyna odczytywać dane od początku lub od miejsca, w którym odczytywanie zostało przerwane. Kiedy dane się skończą, a my dalej próbujemy coś odczytać, to wyskakuje nam błąd OUT OF DATA ERROR. Na to jest tylko jedna rada: albo używać komparacji, albo pętli. Ale co robić, gdy chcemy koniecznie odczytać żądaną informację z którejś tam linii? Restore nam nie pomoże, gdyż procesor, gdy napotka tą instrukcję, ustawia pewien wektor na $0801 (chyba, że program w BASIC-u startuje od innego miejsca pamięci). Czas Wam przybliżyć ten wektor. Fachowo nazywa się on DATPTR i jest on wskaźnikiem ADRESU (nie linii) aktualnej linii DATA. Więc sprawa wygląda bajecznie prosto: wystarczy przed wykonaniem instrukcji READ ustawić ten wektor, czyli komórki $41-$42 (#65-#66) na odpowiedni adres z linią DATA. Ponieważ nie warto sobie utrudniać życia, więc warto skorzystać z innego wektora (OLDTXT), czyli $3d-$3e (#61-#62). Ten wektor wskazuje adres (nie nr linii!) aktualnie wykonywanej linii w programie. Możemy więc wykorzystać go w naszym programie:
10 poke65,peek(61):poke66,peek(62) 20 read a$:printa$ 30 data"dane","tralala"
Ta metoda jest skuteczna, gdy mamy kilka partii z liniami DATA i w różnych odstępach czasu je wczytujemy. Jednak, gdy mamy np. taki fragment listingu:
10 data 1,5,2,4,5,3 20 data 12,34,24,23 30 data 0700,0800,0900
i chcemy odczytać konkretnie dane z linii 20, to polecam użyć zmienne, a linie DATA umieścić na początku programu:
10 data 1,4,2,4,4 15 hi=peek(61):lo=peek(62) 20 data 233,4652,32 30 data "traaa" 40 poke 65,hi:poke66,lo 50 for a = 1 to 3:read b(a): next 60 end
I o to chodziło…
Na koniec inna ciekawostka, czyli opis instrukcji USR. Jest to jedna z najrzadziej używanych instrukcji przez programistów. Programujący w BASIC-u rzadko używają wspólnie z BASIC-iem Assembler, więc dlatego instrukcja USR staje się im nieprzydatna. Ja postaram się przedstawić korzyści z korzystania z tej instrukcji. Jest to odmiana instrukcji SYS, tyle że możemy wykorzystać program w Assemblerze po to, aby coś tam nam obliczył i jednocześnie możemy przesłać mu dane i uzyskać obliczone. Jak to robić? Po pierwsze, należy ustawić wektor $0311-$0312 (#785-#786) na adres początkowy naszego programiku w Assemblerze. Wiadomo – jak ten programik startuje od $5000, to do $0314 wrzucamy #00 a do $0315 – $50. Ponieważ BASIC bezpośrednio nie rozpoznaje liczb szesnastkowych, to radzę sobie te cyferki zamienić na dziesiętne, np. za pomocą kalkulatora, albo monitora języka maszynowego. Po ustawieniu tego wektora wykonujemy np.:
10 a=usr(10)
wtedy nasz program wstawi do akumulatora #10 i wykona procedurę od $5000 (jeżeli tak ustawiliśmy ten wektor).Potem w zmiennej a znajdziemy wynik operacji przeprowadzonej na liczbie #10 w naszym assemblerowskim programie. Więc jak już widać gołym okiem, znajdujemy dużo korzyści w wyniku korzystania z tej instrukcji. Gdy chcemy koniecznie korzystać z SYS-a, to warto wiedzieć, że można ustawić komórki, które przechowują zawartości rejestrów. #780 zawiera akumulator, #781 rejestr X, #782 rejestr Y a #783 rejestr P (czyli rejestr znaczników). Powiedzmy, że wpisujemy przez poke do #780 wartość #10 i po tym wykonujemy SYS. Procedura w Assie, jeżeli na początku nie ustawia akumulatora, wykorzysta tą dziesiątkę. Potem po RTS-ie możemy przez peek odczytać te cztery komórki i sprawdzić, czy np. dobrze nam policzyło.
Wydaje mi się, że jednak USR jest wygodniejsze.
No i to na tyle, jeżeli chodzi o tajniki BASIC-a. Jeżeli kiedykolwiek jeszcze napiszę taki artykuł, to niestety na łamach innego magazynu. Przy pisaniu tego arta korzystałem z książki „Programować może każdy” R. Zielińskiego.
H.M.MURDOCK
Jest to czwarta i zarazem ostatnia część cyklu artykułów o tajnikach BASIC-a. Poniższą publikację stworzyłem krótko po tym, jak dowiedziałem się, że Inverse #8 będzie ostatnim numerem magazynu.
Artykuł napisałem w 2000 r. (prawdopodobnie po wakacjach) przy użyciu Commodore 64 oraz edytora tekstowego do magazynu dyskowego Inverse. Ukazał się on 31 października 2000 r. w magazynie Inverse #8/Oxygen64 w dziale Inne. Oryginalną treść poddałem niewielkiej korekcie, mając na uwadze przede wszystkim poprawną interpunkcję.