- Artykuły
- Turbo Basic XL
- Paweł "Sikor" Sikorski
- Tomasz "TDC" Cieślewicz
- Sławomir "Bluki" Woźniak
- Action!
- Tomasz "TDC" Cieślewicz
1. Wstęp
Każdy użytkownik Atari XL/XE ma do dyspozycji wbudowany język programowania – ATARI BASIC. Po wczesnej fascynacji grami lub programami demonstracyjnymi przychodzi u większości z nas, użytkowników maszyn retro, czas, w którym staramy się zaprogramować naszą maszynkę. Oczywisty wybór pada na wbudowany język programowania.
Zazwyczaj szybko okazuje się, że ten wybór nie spełnia naszych oczekiwań. Język ten oprócz tego, że jest stosunkowo wolny (o tym dalej), ma szereg ograniczeń. Szybkie wyszukiwanie zazwyczaj daje nam jeden cel: assembler. Ale tutaj jest pies pogrzebany – szybko okazuje się, że mimo swej prostoty, jest to język, który wymaga dokładnej znajomości nie tylko samej struktury programu, ale także tzw. „mapy pamięci” samego komputera nieomal że co do bajtu. A na poznanie tego nie każdy ma czas i chęci, niekiedy zaś po prostu brakuje nam samozaparcia.
Czy w związku z tym mamy się od razu zniechęcić? Nie, na Atari powstał cały szereg języków programowania, można wręcz twierdzić, że pod względem ich ilości (a często i jakości) – jest to jeden z najlepiej „oprogramowanych” komputerów ośmiobitowych. Piszę tu oczywiście o językach natywnych, czyli występujących bezpośrednio na naszym sprzęcie.
Mamy do wyboru zarówno tak zwane języki kompilowane (wikipedia: http://pl.wikipedia.org/wiki/Język_kompilowany), jak i interpretowane (wikipedia: http://pl.wikipedia.org/wiki/Język_interpretowany), do jakich należy Basic. Zaletą języków kompilowanych jest zazwyczaj ich prędkość, za to mają większą złożoność przy tworzeniu kodu – każdorazowe tworzenie kodu przed debugowaniem wymaga kompilacji, a potem często linkowania z biblioteką lub innymi częściami programu. Języki interpretowane są zazwyczaj prostsze, ale i wolniejsze. Co więcej – zazwyczaj wymuszają blokowe myślenie przy pisaniu kodu.
Czy w związku z tym należy się przerzucić na język kompilowany, aby uzyskać większą prędkość? Niekoniecznie, do wielu zadań w zupełności wystarczają języki interpretowane, do jakich zalicza się także Atari Basic. Pewnym rozszerzeniem jego możliwości jest Turbo Basic XL – który zostanie opisany na łamach niniejszej strony, zarówno w postaci serii artykułów (moje artykuły będę także umieszczał w formie *.pdf, do łatwiejszego wydruku – kolejne części będzie można dołączać do niniejszego tekstu, tworząc pewną całość, którą będzie można spiąć wspólnie w skoroszycie A5), jak i biblioteki gotowych procedur do wykorzystania (zamieszczanych w formie tekstu) – aby stworzyć pewną ich bazę.
Co przemawia za zastosowaniem Turbo Basica XL? Między innymi jego wsteczna kompatybilność z Atari Basicem, przez co nie tracimy wcześniejszych prac. Większość poprawnie napisanych programów z tego drugiego zadziała w naszym nowym środowisku, trzeba jednak wziąć poprawkę, że tu mogą działać nieco szybciej, co się wiąże ze specyfikacją nowszego podejścia do tematu. Co więcej, do Turbo Basica XL (nazywanego od tej pory w skrócie TB XL) został stworzony bardzo ciekawy kompilator (Turbo Basic XL Compiler) oraz wygodny runtime, uruchamiający skompilowane programy (za pomocą linkera możemy go połączyć bezpośrednio ze skompilowanym programem, tworząc poprawny plik wykonywalny dla DOSa).
W serii artykułów nie zamierzam dokładnie omawiać składni języka, ale pewnie pewne podstawy się mimochodem znajdą. Dla ciekawych polecam lekturę książki W. Zientary „Języki Atari XL/XE – część pierwsza (do znalezienia na www.atarionline.pl w biblioteczce atarowca) lub serii artykułów traktujących o TB XL w „Tajemnicach Atari” (do znalezienia na stronie http://tajemnice.atari8.info/).
Zapraszam do zapoznawania się z kolejnymi częściami artykułu.
- Do pobrania:
- W formacie *.pdf
- W formacie *.txt (kodowanie UTF-8)

2. Przydatne narzędzia i początki programowania.
W kolejnych częściach artykułu będę starał się przedstawiać proste programy i narzędzia, które są pomocne przy tworzeniu plikowych programów, zgodnych z DOSem. Oznacza to, że programy tu przedstawione będą poddawane dalszej obróbce, takiej jak kompilowanie, linkowanie, łączenie z danymi, itp.
Zacznijmy od przygotowania sobie środowiska pracy. Potrzebne nam będą: edytor (czyli właściwy Turbo Basic XL), kompilator do niego (Turbo Basic Compiler), linker i runtime (czyli biblioteka do uruchomienia skompilowanych programów). Z dodatkowych rzeczy osobiście używam Super Packera – którym nadaje nagłówki binarne danym – oraz programu Mossad do linkowania całości (często moje programy nie wchodzą do standardowej pamięci Super Packera). Do tego przydatne bywają dowolne programy graficzne, edytory fontów, muzyki (koniecznie z playerami), ale o tym później.
Aby zapoznać się z tworzeniem programu proponuję zacząć od standardowego w tym przypadku programu typu „hello World!”:
10 ? CHR$(125) 20 ? "PROGRAM TESTOWY"Zapisujemy go na przykład pod nazwą TEST.TB (SAVE”D:TEST.TB”). Oczywiście, ten programik nie korzysta z dobrodziejstw TB XL, ale pozwoli nam zapoznać się z kolejnymi etapami przygotowania programu plikowego uruchamianego pod DOSem.
Teraz przechodzimy do DOS-u i uruchamiamy kompilator (L => TBC.COM). Jego obsługa jest banalna: numer napedu wybieramy poprzez wciśnięcie cyfry odpowiadającej numerowi napędu, w jakim mamy nasz plik źródłowy (w przykładach zawsze będzie to napęd numer 1), a następnie wybieramy za pomocą strzałek nasz plik źródłowy. Jeśli plik nie jest prawidłowym zbiorem TB XL – zostaniemy o tym powiadomieni stosownym komunikatem, zaś jeśli wszystko jest o.k. - automatycznie zacznie się kompilacja programu. Jeśli wszystko przebiegnie poprawnie – zostaniemy poproszeni o wpisanie nazwy skompilowanego programu, przy czym automatycznie zostanie dodana końcówka pliku .CTB, mówiąca o tym, że jest to skompilowany plik TB XL. Potem zostaniemy spytani, czy chcemy wykonać kolejną kopię skompilowanego programu – dla nas jest ona zbędna.
Jeśli w programie wystąpi jakikolwiek błąd – proces kompilacji zostanie przerwany, a na ekranie pojawi się stosowny komunikat wraz z numerem linii w której wystąpił oraz numerem błędu. Warto go sprawdzić, bo często drobna sprawa niweczy godziny pracy. Zdarza się także, że program jest za długi i nie mieści się w buforze kompilatora.
Zakładając, że wszystko poszło dobrze – wracamy do naszego DOS-u (control-D) lub powtarzamy kompilację dla kolejnych programów.
Przyszła pora na zlinkowanie naszego dzieła z runtime-m (używamy poprawionego pliku RUNTIME2.EXE. Możemy tego dokonać funkcją append DOSu, skorzystać z dowolnego programu linkującego – lub najlepiej – skorzystać z dedykowanego linkera, co zazwyczaj czynię. Tu mała dygresja: linker może się znajdować w dowolnym napędzie, jednak plik RUNTIME2.EXE musi się znajdować w napędzie D1:. Po poprawnym wczytaniu linkera zostaniemy poproszeni o podanie pliku źródłowego – podajemy pełną ścieżkę (na przykład: D:TEST.CTB) oraz docelowy (D:TEST.XEX). Po poprawnym zlinkowaniu koniecznie należy wcisnąć klawisz ESC – błąd w kodzie linkera powoduje, że nie zamyka on w innym przypadku poprawnie plików! Po wciśnięciu ESC pojawia się standardowe wyjście z skompilowanego programu TB XL – wybieramy D (wyjście do DOS-a). Możemy teraz sprawdzić, czy nasz programik działa pod DOS-em: wybieramy L i ładujemy plik TEST.XEX. Jak widać można (tylko to paskudne zakończenie programu... Jak to można obejść – można będzie przeczytać w dziale TB XL lub Kruczki i Sztuczki już niedługo.
Przed kompilacją należy pamiętać, że niektóre dane programu są umieszczone w innych miejscach niż w programie źródłowym. Rodzi to czasem pewne problemy techniczne, gdy okazuje się, że programy źródłowe działają w zgoła inny sposób od skompilowanych (rzadki przypadek, ale się zdarza). Zazwyczaj pomaga poprzesuwanie danych binarnych używanych w naszym programie głównym (na przykład grafika, muzyka, itp.), choć trzeba się liczyć z tym, że wtedy nasz program może przestać działać należycie w wersji źródłowej. Jednak zdarza się to stosunkowo rzadko, a pewne nawyki i upodobania można nabyć wraz z nauką programowania, o czym będzie w dalszej części programu.
Należy się także wystrzegać dynamicznie wyliczanych linii w naszych dziełach – co jest jedną z przyczyn niemożności skompilowania niektórych programów (na przykład oryginalne KOLONY).
Z uwagi na Runtime należy też pamiętać o tym, aby zwrócić uwagę na obsługę niektórych wrażliwych miejsc pamięci – o czym będzie w dalszych częściach artykułu.
- Do pobrania:
- W formacie *.pdf
- W formacie *.txt (kodowanie UTF-8)
- Dyskietka w formacie *.atr z narzędziami opisanymi w artykule

3. Nieco o danych
Zakładam, że już z grubsza wiemy, jak przygotować program do uruchomienia pod DOSem (kompilacja, linkowanie, itp.). Teraz przyszła pora na kilka słów o obsłudze danych. A dokładnie o ich wgrywaniu, trzymaniu w programie, obsłudze po kompilacji.
Zacznijmy od czegoś prostego – idealnym przykładem jest tu zestaw fontów. Zajmuje on zawsze (w standardzie) 1 kilobajt pamięci, jego umiejscowienie też nie jest bez znaczenia. W TB XL powinien zawsze znajdować się na początku strony pamięci, a właściwie na początku takiej strony, której adres w pamięci jest podzielny przez cztery. Dlaczego właśnie tak? Przyjrzyjmy się nieco bliżej. Zestaw fontów to 1024 bajty, jedna strona pamięci to 256 bajtów, stąd jeden zestaw zajmuje cztery strony pamięci.
Kolejna rzecz – to zmiana zestawu znaków w programie. Wykonujemy ją jedną prostą instrukcją: POKE 756,adres_zestawu_znaków. Wiadomo, że instrukcja POKE pozwala nam na wprowadzanie jednej wartości ośmiobitowej, to jest liczby z zakresu 0-255. Wskazuje on nam początek strony, na której znajduje się nasz zestaw znaków.
Przyjrzyjmy się bliżej – standardowy zestaw znaków znajduje się na 224 stronie pamięci (POKE 756,224) – czyli pod adresem 57344. Trudny adres do zapamiętania, nieprawdaż? Ale wykonajmy jedną instrukcję pod TB XL:
?HEX$(57344)Otrzymamy wynik E000. Jest to nasz adres w postaci szesnastkowej. Jeśli teraz zamienicie dwie pierwsze cyfry z tego adresu na system dziesiętny to uzyskacie zaskakujący wynik:
?$E0Jest to bowiem 224, czyli to co wpisujemy pod POKE. Sprawa staje się prostsza do zrozumienia i zapamiętania. Ja w swoich programach ustaliłem sobie nieomalże stały adres na fonty – większość z nich mieści się pod adresem $9C00 (mała podpowiedź: POKE 756,$9C – tak, w TB XL dane możemy podawać szesnastkowo, poprzedzając je znakiem dolara). Przykłady w tym artykule dotyczące zestawu znaków postaram się podawać zawsze ustawione pod ten adres pamięci.
Dobrze, załóżmy, że przygotowaliśmy sobie zestaw znaków pod dowolnym edytorem fontów. Przydałoby się go użyć, czyli wprowadzić do pamięci komputera. Można to zrobić na kilka sposobów, podam trzy najczęściej spotykane (przynajmniej tak mi się wydaje). Wszelkie wczytywanie danych (za wyjątkiem danych poprzedzonych nagłówkiem binarnym) wymagają otwarcia odpowiedniego kanału do ich obsługi – a dobra praktyka programistyczna nakazuje na koniec nieużywany kanał zamknąć. Przyjrzyjmy się dwu poniższym przykładom (zakładamy, że plik z fontami jest nazwany FONT.FNT i jest w stacji D:):
Styl ATARI BASIC:
OPEN #1,4,0,"D:FONT.FNT":FOR I=0 TO 1023:GET #1,A: POKE $9C00+I,A:NEXT I:CLOSE #1UWAGA! Pamiętamy, że gdy chcemy to wczytać w Atari Basicu adres szesnastkowy zmieniamy na dziesiętny!
Styl Turbo Basic XL:
OPEN #1,4,0,"D2:*FNT":BGET #1,$9C00,1024:CLOSE #1Proponuję wypróbować oba. Wykonują dokładnie to samo, jak widać instrukcja BGET w TB XL nie dość, że jest wydajniejsza (szybsza) od zwykłego GET, to jeszcze nie wymaga użycia ani pętli (ilość bajtów podajemy wprost), ani zmiennej pośredniej (A).
Pokrótce, co się dzieje:
OPEN #1,4,0,"D:FONT.FNT" – otwiera nam kanał #1 na operacje wejścia/wyjścia (tu wejście)
FOR I=0 TO 1023:GET #1,A:POKE $9C00+I,A:NEXT I lub BGET #1,$9C00,1024 – pobiera z wejścia (stacja D:) 1024 bajty danych i wpisuje je pod adres $9C00
CLOSE #1 – zamyka pierwszy kanał wejścia/wyjścia
Po dokładny opis instrukcji zapraszam do literatury. Tak wczytany zestaw danych możemy już wykorzystać (POKE 756,$9C). Ale wcześniej wspomniałem o trzech metodach, a podałem dwie, o co chodzi?
Trzecia metoda jest pozornie trudniejsza, ale ja ją preferuję ze względu na późniejszą łatwość dołączania danych do kompilacji. Jeśli chcę mieć (finalnie) program w jednym bloku, skompilowany, wczytywany pod DOSem – wskazane by było, aby wiedział, skąd brać niezbędne dane (a takimi są fonty). A przykro by było, aby do prawidłowego działania program musiał je wczytywać po uruchomieniu (potrzebny DOS lub jego odpowiednik, wszystkie pojedyncze pliki należy trzymać razem, itp.).
Do czego zmierzam? Jest taka użyteczna instrukcja BLOAD, która wczytuje dane binarne, koniecznie poprzedzone nagłówkiem. Nasze fonty oczywiście takiego nie mają, ale w poprzednim artykule wspominałem o programach dodatkowych. Skorzystajmy z Super Packera, po jego załadowaniu wczytajmy nasze fonty do jego bufora (opcja F, ustalamy adres na 9C00, zapisujemy dane – dane binarne preferuję z rozszerzeniem *.DAT – więc ja podaję FONTY.DAT). Mamy w ten sposób przygotowany plik binarny do wgrania w programie.
Uwaga! Za pomocą Super Packera możemy w ten sposób przygotować sobie kilka bloków danych, na przykład grafikę, tekst, itp., a następnie zapisać to w jednym pliku.
Wróćmy do naszych fontów. W TB XL wpisujemy:
BLOAD"D:FONTY.DAT"Teraz spróbujmy: POKE 756,$9C. Hura, jest!
Jak widać, po odpowiednim przygotowaniu danych jest to bardzo proste. Oczywiście metoda nie ogranicza się do fontów, a zasadniczo do wszystkich danych zapisanych binarnie – ba, część programów od razu tworzy takie gotowe pliki (na przykład Chaos Music Composer – zarówno muzykę, jak i player wczytacie w ten sposób). Bardzo często w ten sposób przygotowuję sobie grafikę (bądź w postaci pełnoekranowej, bądź w postaci bloków pamięci). Przy odrobinie wprawy jest to dużo wygodniejsze (i szybsze) niż analogiczne wczytywanie danych instrukcjami TB XL (zapominamy o OPEN, CLOSE, ilości bajtów do wczytania podczas transmisji danych, itp., itd.). Dodatkowo – wszystkie potrzebne dane możemy zamknąć w jednym pliku (jedno BLOAD zamiast kilku/kilkunastu OPEN, BGET). A przy pisaniu większych programów jet to wielka oszczędność (czasu i miejsca).
Szykując kolejne dane należy pamiętać o jednej głównej zasadzie – nie mogą one nachodzić na siebie, bo będą się wzajemnie zamazywać (w zależności od położenia w pliku docelowym). Zdradliwym jest też fakt, że niekiedy przed kompilacją dane muszą być w innym miejscu niż po niej – ale to się zdarza zazwyczaj w większych programach.
Wracając do wczytywania i zapisu danych – proponuję zapoznać się z literaturą na ten temat, gdyż jest on zbyt obszerny na potrzeby tego artykułu. Zasadniczo tworząc programy plikowe trzymam się zasady, że wszystko wczytuję poza listingiem, bezpośrednio z edytora (pomijam numery linii), lub staram się takie rzeczy trzymać w oddzielnej procedurze, którą potem usuwam. O samych procedurach i więcej o danych będę jeszcze wspominał w kolejnych częściach tego opracowania.
Na zakończenie przypomnę tylko, jak w TB XL uruchamiać muzyki z CMC, skoro już o tym programie wspomniałem:
X=USR($adres_playera,nr_subsongu,$adres_muzyki)oraz jak wyciszyć muzykę:
X=USR($adres_playera)Oczywiście jest to odwołanie do procedury maszynowej, przez co mamy podany przykład jej wywołania. Jak pewnie spostrzegawczy zauważyli – adresy są tu podane szesnastkowo, jednak nic nie stoi na przeszkodzie, aby je podawać dziesiętnie. Tylko czy jest sens – wywołanie takie jak tutaj nie wymaga konwersji liczb, co jest zaletą TB XL, a jest dokładnie w taki sposób ustawiane przez program.
- Do pobrania:
- W formacie *.pdf
- W formacie *.txt (kodowanie UTF-8)

Dlaczego Turbo BASIC XL?

Wiele osób dobrze zna Atari Basic, który oznajmia uruchomienie Atari XL/XE komunikatem „Ready”. Język jest prosty, zrozumiały, ale niestety powolny, nadaje się jedynie do narysowania wykresu lub zrobienia programu operującego na tekście, o grafice i animacji trudno myśleć, gdy język nawet nie posiada odpowiednich poleceń.
Programowanie w Basicu, nie jest pozbawione wad (np. numerowanie linii), często jest wręcz odradzane, jednak za nim przemawia prostota, która rzesze ludzi na całym świecie nauczyła programować, mimo że byli w młodym wieku i wcześniej nie mieli o tym bladego pojęcia.
Doceniając walory Basica, niemiecki programista Frank Ostrowski w 1985 napisał nową implementację tego języka tzw. Turbo BASIC XL, była ona nie tylko poprawioną wersją, ale też mocno rozbudowaną (np. poprzez dodanie do języka namiastki procedur i wielu innych mniejszych lub większych udoskonaleń). Dziś nie ulega wątpliwości, praca jaką wykonał Frank, była bardzo dobra. Wiele osób uważa ten język za najlepszą, najwygodniejszą i najszybszą implementację BASICa dla małego Atari.
Osoba, która wcześniej programowała w Atari Basicu, będzie się w nim czuła jak w domu, ale szybko doceni nowe ciekawe możliwości, np. zupełnie nowe znacznie bardziej inteligentne instrukcje. Dodatkowo wraz z językiem dostępne są dodatkowe bardzo cenne narzędzia, gdzie warto wymienić Turbo BASIC XL Compiler, który umożliwia szybkie przygotowanie programu w wersji uruchamialnej z poziomu DOSa. Dodatkowo program skompilowany, czasami działa znacznie szybciej.
Ostatecznie powstał bardzo ciekawy pakiet, który warto polecić. Powstało w nim wiele gier (komercyjnych), różnego rodzaju programów użytkowych, a nawet pojedyncze dema. Warto wspomnieć o grze Kolony 2106, wydanej przez zespół Atarionline.pl w 2009 roku, a której premiera odbyła się na Poznań Game Arena 2009, największych w Polsce targach gier.
Turbo BASIC XL doskonale nadaje się do prostych programów, nieco do gier i animacji. Operując grafiką na podobnej zasadzie co w Atari Basicu możliwe jest osiągnięcie znacznie ciekawszych rezultatów, a przy odrobinie wprawy i znajomości sprzętu Atari, możliwe jest wykonanie najbardziej zaawansowanych animacji (chyba w całej historii) języka Basic.
Wydajność działania i wygoda zapewne nie może się równać z językiem Action!, w którym możliwe jest nawet edytowanie dwóch programów, w dwóch odrębnych okienkach. Jednak środowisko Basica jest (najczęściej) wszystkim znane i zapewne znacznie bardziej intuicyjne niż w Action! Dlatego temu komu wystarczy programowanie w Basicu, polecamy właśnie Turbo BASIC XL, jeśli jednak dla przykładu chciałbyś napisać grę porównywalną do tych najfajniejszych lub np. przetwarzać próbki dźwiękowe (ang. sample) w czasie rzeczywistym, to zdecydowanie polecamy język Action! bo to właśnie w tym języku powstało wiele epokowych utworów, gier i dem, napisanych przez prawdziwą legendę polskiej demosceny Jakuba Husaka.
- Do pobrania:
- W formacie *.txt (kodowanie UTF-8)

Przewodnik po TURBO-BASIC-u XL 1.5
Operacje dyskowe
Uwaga: jeśli używamy napędu nr 1 to w specyfikacji numer napędu (oznaczony jako „n”) można pominąć. Np. zamiast „D1:nazwa, piszemy D:nazwa.
BLOAD"Dn:nazwa" – wczytuje plik binarny z napędu dyskowego o numerze n i wskazanej nazwie, ale nie uruchamia go.
BRUN"Dn:nazwa" – wczytuje plik binarny z napędu dyskowego o numerze n i wskazanej nazwie, a następnie go uruchamia.
DELETE"Dn:nazwa" – kasuje plik o wskazanej nazwie w napędzie n.
DIR"Dn:nazwa" – odczytuje katalog dysku. W przypadku użycia tego polecenia bez parametrów (tylko DIR), TBXL przyjmuje domyślnie, że chodzi o napęd numer 1.
LOCK"Dn:nazwa" – zabezpiecza plik przed zapisem lub skasowaniem. Próba użycia tego polecenia na pliku już wcześniej zabezpieczonym nie powoduje błędu.
RENAME"Dn:stara_nazwa,nowa_nazwa" – zmienia nazwę pliku na dysku.
UNLOCK"Dn:nazwa" – odbezpiecza plik, wcześniej zabezpieczony poleceniem LOCK. Próba użycia tego polecenia na niezabezpieczonym pliku nie powoduje błędu.
Programowanie strukturalne – procedury i pętle
PROC nazwa ••• ENDPROCInstrukcje i linie programu zawarte pomiędzy PROC nazwa, a ENDPROC tworzą procedurę. Procedurę można przyrównać do podprogramu wywoływanego przez GOSUB i kończącego się poleceniem RETURN w Atari BASIC-u. Jednak w odróżnieniu od GOSUB procedura posiada nazwę, która może, a nawet powinna być znacząca, czyli określać co procedura robi.
EXEC nazwa – tak jak w Atari BASIC-u polecenie GOSUB nr_linii wywołuje podprogram, tak w TBXL wykonuje to EXEC nazwa, wywołując procedurę, która jest niczym innym jak podprogramem z nazwą.
ON warunek EXEC nazwa_procedury – warunkowe wywołanie procedury. Polecenie to funkcjonuje analogicznie do ON warunek GOSUB numer_linii w Atari BASIC-u.
DO ••• LOOP – pętla bez końca. Elementy programu zawarte pomiędzy DO a LOOP będą cały czas powtarzane. Jedynym dopuszczalnym w programowaniu strukturalnym sposobem „rozerwania” tej pętli jest polecenie EXIT.
IF warunek ••• ELSE ••• ENDIF – gdy warunek jest spełniony, wówczas wykonywana jest część programu przed ELSE i pomijana między ELSE i ENDIF. Natomiast gdy warunek nie jest spełniony, pomijana jest część programu przed ELSE, a wykonywana między ELSE i ENDIF. Słowo kluczowe ELSE może być pominięte, wówczas polecenie będzie działać podobnie jak znane z Atari BASIC-a IF THEN..., z tą różnicą, że wewnątrz takiej pętli IF-ENDIF może znajdować się dowolna ilość linii programu.
REPEAT ••• UNTIL warunek – ciąg instrukcji zawarty pomiędzy słowami kluczowymi REPEAT i UNTIL jest powtarzany tak długo, jak długo warunek pozostaje niespełniony, czyli ma wartość logiczną 0. Gdy warunek zostanie spełniony (czyli przyjmie wartość logiczną 1), nastąpi opuszczenie pętli. WHILE warunek ••• WEND – tak długo, jak warunek będzie spełniony (czyli posiadać będzie wartość logiczną 1) pętla będzie wykonywana.
Programowanie strukturalne – etykiety
# nazwa – etykieta; nadaje bieżącej linii programu nazwę. Etykieta musi być pierwszym (lub jedynym) poleceniem w linii.
GO# nazwa – bezwarunkowy skok do linii o wskazanej nazwie – odpowiednik polecenia GOTO nr_linii w Atari BASIC-u.
ON warunek GO# nazwa – polecenie skoku warunkowego do linii o wskazanej nazwie. Odpowiednikiem w Atari BASIC-u jest polecenie ON warunek GOTO numer_linii.
RESTORE# nazwa – odpowiednik polecenia RESTORE numer_linii w Atari BASIC-u.
TRAP# nazwa – odpowiednik polecenia TRAP numer_linii w Atari BASIC-u.
Operacje na pamięci
BGET #n,adres,ilość – pobiera z kanału (o numerze n) ciąg bajtów i zapisuje go w pamięci od wskazanego adresu. Ilość określa liczbę bajtów jaka ma być pobrana i zapisana. Wcześniej należy otworzyć do odczytu blok IOCB.
BPUT #n,adres,ilość – przesyła z pamięci komputera do wybranego kanału n ciąg bajtów. Adres wskazuje miejsce w pamięci od którego ma rozpocząć się przesyłanie, a ilość informuje ile bajtów ma być przesłanych. Wcześniej należy otworzyć do zapisu blok IOCB.
DPEEK(wyrażenie) – odczytuje wartość słowa spod adresu pamięci określonego przez wyrażenie. Słowo, czyli dwa kolejne bajty odczytywane są w kolejności młodszy bajt - starszy bajt. Efektem jest liczba całkowita z przedziału 0 ÷ 65535.
DPOKE wyrażenie1,wyrażenie2 – dwubajtowe POKE. Zapisuje w dwóch kolejnych komórkach pamięci, począwszy od adresu wskazanego przez wyrażenie1, wartość wyrażenia2. Wyrażenie1 musi mieścić się w zakresie liczb dodatnich od 0 do 65534, wyrażenie2 od 0 do 65535.
%GET #n,zmienna_liczbowa – pobiera z urządzenia zewnętrznego (stacja dysków, magnetofon) liczbę w sześciobajtowym formacie dziesiętnym (BCD) i umieszcza w zmiennej_liczbowej.
%PUT #n,wyrażenie_liczbowe – zapisuje na urządzeniu zewnętrznym wartość wyrażenia_liczbowego w sześciobajtowym formacie dziesiętnym (BCD).
MOVE adres_źródłowy,adres_docelowy,ile – kopiuje obszar pamięci zaczynający się od adresu_źródłowego w obszar zaczynający się od adresu_docelowego. Ile określa ilość przemieszczanych bajtów.
–MOVE adres_źródłowy,adres_docelowy,ile – to samo co MOVE, ale z jedną różnicą. Gdy adres_docelowy jest mniejszy niż adres_źródłowy + ile nastąpi nadpisanie danych. -MOVE przesuwa blok danych od końca, unikając w ten sposób nadpisania.
Grafika CIRCLE X,Y,R1,R2 – rysuje okrąg lub elipsę. Parametry X i Y wyznaczają punkt środkowy rysowanego obiektu. R1 to pozioma oś elipsy, a R2 – pionowa. Parametr R2 można pominąć, wówczas domyślnie R2=R1, czyli otrzymamy okrąg.
CLS – czyści ekran w trybie GRAPHICS 0 oraz okna tekstowe w trybach graficznych, po czym ustawia kursor w górnym lewym rogu.
CLS#n – czyści ekran obrazu utworzony przez blok IOCB o numerze „n” (1 ÷ 7). CLS#6 kasuje zawartość ekranu utworzonego w standardowych trybach graficznych.
FCOLOR wyrażenie_liczbowe – ustala jakiego rejestru koloru używać będzie polecenie FILLTO.
FILLTO X,Y – wypełnia „czysty” obszar ekranu, w prawo od rysowanego punktu. X to pozioma pozycja na ekranie, a Y pionowa. W Atari BASIC-u odpowiednikiem FILLTO jest: POSITION X,Y:XIO 18,#6,0,0,"S:".
PAINT X,Y – wypełnia pusty obszar wewnątrz figury (lub taki obszar na zewnątrz figury). X i Y ustala punkt ekranu, odpowiednio współrzędną poziomą i pionową, od którego rozpocznie się wypełnianie.
Dźwięk
DSOUND – użyte bez parametrów zamyka wszystkie kanały dźwiękowe. Taki sam efekt uzyskuje poleceniem SOUND bez parametrów.
DSOUND głos,częstotliwość,zniekształcenia,głośność – łączy dwa generatory dźwięku POKEY-a w jeden. Głos, to numer tak utworzonego generatora dźwięku. Dopuszczalne zakresy poszczególnych parametrów to:
- głos: 0 lub 1;
- częstotliwość: 0 ÷ 65535;
- zniekształcenia: 0 ÷ 14, tylko liczby parzyste (10 i 14 czysty dźwięk);
- głośność: 0 ÷ 15.
SOUND - patrz DSOUND.
Funkcje arytmetyczne i logiczne
%0, %1, %2, %3 – są to cztery stałe liczbowe odpowiadające odpowiednio liczbom 0, 1, 2 i 3. W przeciwieństwie do liczb zmiennoprzecinkowych, które zawsze zajmują 7 bajtów pamięci, stałe liczbowe zajmują tylko 1 bajt.
DEC(wyrażenie_tekstowe) – dokonuje konwersji liczb szesnastkowych na dziesiętne.
DIV: dzielna DIV dzielnik – dzielenie bez reszty; wynikiem będzie liczba całkowita powstała po odrzuceniu reszty z dzielenia.
EXOR: a EXOR b - alternatywa wykluczająca.
FRAC(wyrażenie_liczbowe) – zwraca część ułamkową wyrażenia_liczbowego poprzez odrzucenie wartości przed przecinkiem. Zostaje jednak zachowany znak liczby, np. wynikiem FRAC(-7.75) będzie liczba -0.75.
HEX$(wyrażenie liczbowe) – dokonuje konwersji całkowitej liczby dziesiętnej na szesnastkową.
MOD: a MOD b – polecenie to realizuje dzielenie modulo według wzoru: a-b*TRUNC(a/b).
RAND(n) – generuje całkowitą liczbę losową z przedziału
- 0 ÷ n–1, gdy n>0
- -n ÷ -1, gdy n<0.
RND – to samo co RND(0) w Atari BASIC-u, ale w krótszej formie.
TIME – podaje trzybajtową liczbę stanu zegara czasu rzeczywistego (RTCLOCK). Wartość ta obliczana jest według wzoru: 65536*PEEK(18)+256*PEEK(19)+PEEK(20).
TIME$ - pokazuje czas w formacie „hhmmss” (godziny-minuty-sekundy). Aby ustawić czas należy użyć sekwencji: TIME$="hhmmss", np. TIME$="163059".
TRUNC(wyrażenie_liczbowe) – zwraca część całkowitą wyrażenia_liczbowego poprzez odrzucenie części ułamkowej. Zachowuje znak liczby, np. TRUNC(-9.998) to -9.
!: a!b – alternatywa. Wynikiem jest 0 (fałsz) gdy te same bity w wyrażeniu a i b mają wartość 0. W innym przypadku = 1 (prawda). Wynik podawany jest w formacie dziesiętnym.
$aaaa – TBXL pozwala na umieszczanie w programie liczb szesnastkowych. Aby interpreter mógł je odróżnić od dziesiętnych, muszą być poprzedzone znakiem „$”, np. $AE12, $FF.
&: a&b – koniunkcja. Wynikiem jest 1 (prawda) tylko gdy te same bity w wyrażeniu a i b mają wartość 1, inaczej = 0 (fałsz). Wynik podawany jest w formacie dziesiętnym.
Operacje tekstowe
INKEY$ - gdy zostanie naciśnięty klawisz, INKEY$ zostanie przypisany odpowiedni znak ATASCII. Nie zatrzymuje programu.
INSTR(zmienna_tekstowa1,zmienna_tekstowa2,n) – poszukuje ciągu zawartego w zmiennej_tekstowej2 (krótszej) w zmiennej_tekstowej1 (dłuższej). Parametr n określa pozycję od jakiej ma się rozpocząć przeszukiwanie zmiennej_tekstowej1. Jeżeli przeszukiwanie ma rozpocząć się od początku (n=1) parametr ten można pominąć. Po znalezieniu ciągu INSTR zwraca pozycję pierwszego jego znaku w zmiennej_tekstowej_1. Jeśli ciąg nie zostanie odnaleziony wynikiem będzie zero.
UINSTR(zmienna_tekstowa1,zmienna_tekstowa2,n) – to samo co INSTR, z jedną różnicą: bit 5 i 7 każdego znaku jest ignorowany. Umożliwia to wyszukiwanie tekstu bez względu czy został napisany literami małymi, dużymi, czy w inwersie.
TEXT X,Y,ciąg – umieszcza tekst na ekranie graficznym. X i Y to współrzędne górnego lewego rogu pierwszego znaku parametru ciąg, który może być stałą lub zmienną, tekstową lub liczbową. Wyboru koloru tekstu dokonuje się poleceniem COLOR.
Sterowanie przebiegiem programu
*B + (lub *B) - naciśnięcie BREAK wywoła błąd, który może być przechwycony przez TRAP.
*B – - klawisz BREAK działa normalnie - naciśnięcie zatrzymuje program.
EXIT - natychmiastowe opuszczenie dowolnej pętli. Po napotkaniu wewnątrz pętli polecenia EXIT, jej wykonywanie zostaje natychmiast przerwane, bez względu na stan warunku pętli i zostaje wykonany skok do następnego polecenia po zamykającym pętlę.
*F + - warunek pętli FOR-NEXT sprawdzany jest na początku pętli (pętla może nie być wykonana ani razu).
*F – - warunek sprawdzany jest na końcu pętli, czyli jak w Atari BASIC-u (pętla zawsze zostanie wykonana co najmniej jeden raz).
PAUSE n – zatrzymuje wykonywanie programu na czas określony parametrem n. Jednostką jest tu 1/50 sekundy, czyli n = 50 zatrzyma program na jedną sekundę.Uwaga! Błąd w opisie - parametr n określa ramkę, 1/50s w PAL, 1/60s w NTSC - Sikor
Edycja programu
–– - specjalna instrukcja REM. Dwa myślniki użyte zaraz po numerze linii wyświetlane są w czasie listowania programu jako ciąg 30 znaków „-”.
DEL od_nr_linii,do_nr_linii – usuwa linie programu począwszy od_nr_linii, a skończywszy na do_nr_linii.
DUMP urządzenie – wysyła na wskazane urządzenie (np. drukarkę) listę zmiennych użytych w programie. Jeśli urządzenie zostanie pominięte, lista zostanie wyświetlona na ekranie.
*L + - podczas listowania programu (patrz polecenie LIST) włącza tabulację, w efekcie linie programu objęte pętlą oraz wewnątrz procedury będą „wcięte” o dwie pozycje poziome.
*L – - wyłącza tabulację (patrz *L +).
LIST – wyświetla listing programu. Możliwe są następujące konfiguracje:
- LIST – bez parametrów wyświetla na ekranie listing całego programu.
- LIST od_nr_linii,do_nr_linii – wyświetla wybrany fragment listingu.
- LIST od_nr_linii, – wyświetla listing od_nr_linii do końca programu.
- LIST nr_linii – wyświetla linię programu o podanym numerze.
Ponadto polecenie LIST działa tak samo jak w Atari BASIC-u.
ERL – podaje numer linii, w której nastąpiło przerwanie programu, np. z powodu wystąpienia błędu.
ERR – podaje kod błędu, który wystąpił w trakcie wykonywania programu.
RENUM stary_nr,nowy_nr,skok – zmienia numery linii programu. Linii o numerze stary_nr nadaje numer wpisany do nowy_nr. Następnej linii nadaje numer nowy_nr + skok i tak dalej do końca programu.
TRACE + - włącza śledzenie wykonywania programu. Interpreter będzie pokazywał na ekranie w nawiasach kwadratowych numery linii, które są właśnie wykonywane.
TRACE – - wyłącza śledzenie (patrz TRACE +).
Usprawnienia
CLOSE użyte bez parametrów zamyka wszystkie kanały IOCB o numerach 1 ÷ 7. To znacznie krócej niż w Atari BASIC-u: FOR X=1 TO 7:CLOSE #X:NEXT X.
DIM - przeciwieństwie do Atari BASIC-a automatycznie zeruje wszystkie elementy tablic liczbowych i zmiennych tekstowych, jednak długość tych ostatnich nie ulega zmianie (początkowo 0).
GET wyrażenie_liczbowe – oczekuje na naciśnięcie klawisza, po czym przypisuje wyrażeniu_liczbowemu kod ATASCII naciśniętego klawisza.
INPUT umożliwia teraz wyświetlenie tekstu przed wprowadzaniem danych np. INPUT"Podaj nazwisko i wiek ";A$,X. Ponadto zastąpienie średnika przecinkiem eliminuje znak zapytania: INPUT" ",A.
PUT wyrażenie_liczbowe – wysyła na ekran znak o kodzie ATASCII reprezentowany przez wyrażenie_liczbowe.
"" - podwójny cudzysłów umieszczony w ciągu tekstu wyświetlany jest jak zwykły cudzysłów, np. linia: »100 PRINT "To jest ""prawdziwy"" cytat."« zostanie wyświetlona: »To jest "prawdziwy" cytat.«.
_ - znak podkreślenia może być używany w nazwach procedur, etykiet i zmiennych, w tym tabel np. NOWA_ZMIENNA=100.
AUTORUN.BAS
Zaraz po starcie systemu TBXL poszukuje na dysku pliku o nazwie AUTORUN.BAS. Jeżeli taki odnajdzie, wczyta go i uruchomi. Ta cecha może być bardzo użyteczna np. do automatycznego uruchamiania nieskompilowanych gier.
- Do pobrania:
- W formacie *.rtf

Pierwsze kroki w Action!
Jeśli chcesz postawić swoje pierwsze kroki w Action! to specjalnie dla Ciebie przygotowałem ten krótki opis, program przykładowy, który jest mały, fajny i kształcący.
Zalecam wpisanie własnoręczne do edytora Action! tego kodu, gdyż warto poznać edytor tego języka, który kiedyś był ewenementem na Atari i innych komputerach. Dziś się wydaje, że takie funkcje jak zamiana tekstów, znaczniki, praca w kilku okienkach to zupełna norma, jednak w czasach gdy powstał Action! edytory były bardzo słabe (szczególnie na pecetach oraz w Uniksie itp.). Na Atari najczęściej korzystało się z edytora systemowego, który do wygodnych nie należy (choć pewnie go znasz jako posiadacz Atari, np. z Atari BASICa).
Nawet jeśli nie zamierzasz pisać dużo w tym edytorze lub ogólnie na Atari, to warto zapoznać się z możliwościami tego edytora, zapewne się to przyda (jest wygodny bo jest na cartridge'u więc nie trzeba czekać aż się załaduje). A już takim absolutnym minimum jest prosta umiejętność przełączania się pomiędzy edytorem, a monitorem, opcje wczytywania/zapisywania plików, czy też operowania na dwóch okienkach (krótki opis klawiatury znajduje się na końcu tego opisu). Jeśli jednak Atari to dla Ciebie jest wspaniały komputer oraz kawał wspaniałej historii to powinieneś ten edytor poznać znacznie lepiej (kiedyś pisałem w nim nawet artykuły do Bajtka). Po pierwsze może się on po prostu przydać jako dobry i łatwy w użyciu edytor tekstowy, po drugie warto wiedzieć, że na Atari był dostępny tak nowoczesny produkt (w końcu warto znać swój ulubiony komputer i jego oprogramowanie;)
Podkreślę jeszcze, że klawiszologia tego edytora może się na pierwszy rzut oka wydawać dziwna (bo nie przypomina nic co było wcześniej na Atari), jednak zapewniam że jest ona bardzo intuicyjna i wygodna w użyciu.
Gdy już zapoznasz się ze szczegółowym opisem opcji edytora Action! oraz własnoręcznie przećwiczysz jak się go używa to nie powinieneś mieć już problemów z pisaniem następnych programów (opisy edytora były w starych Bajtkach, można je znaleźć w internecie oraz na końcu tego tekstu znajduje się przegląd istotniejszych funkcji).
Wtedy już nie ma potrzeby wpisywania poniższego tekstu programu, a jedynie wystarczy wcisnąć kombinację klawiszy: Shift+Control+R (uprzednio instalując plik Action_przyklady.atr w pierwszej stacji tzw. "D1:") i wpisać w pasku na dole ekranu: przykl1.act. Po zatwierdzeniu klawiszem Return, ujrzymy wczytane źródło poniższego programu do edytora Action!
MODULE BYTE A, AN=$D40A, LI=$D40B, KOL=$D01A ,KOL1=$D018, K=764, CZAS=20 PROC TU() WHILE K<>28 DO KOL=LI+CZAS AN=1 OD K=42 RETURNAby uruchomić ten program, należy:
1. Przejść do monitora Action! (to taki tryb pracy języka związany z uruchamianiem programów)
W tym celu należy nacisnąć klawisze Shift+Control+M ("M" jak monitor).

Tym razem pasek jest na górze ekranu, gdzie miga kursor, teraz pisać można tylko w tej linii.
2. Skompilować program
Wykonujemy to naciskając klawisz "C" ("C" od ang. compile - kompilacja programu) i zatwierdzamy Return.
Proces ten może zająć więcej czasu jeśli kompilujemy dłuższy program lub kompilujemy dane z dyskietki (lub jeszcze gorzej z kasety ;) ), jednak w tym przypadku kompilacja jest natychmiastowa. Jest to jeszcze jedna zaleta języka Action! bardzo szybko kompiluje programy (wolno kompiluje jedynie wersja plikowa tego języka, której nie należy używać). Przypomnę, że w latach 80. kompilatory bardzo powoli kompilowały (np. kompilatory języka Pascal itp.) oraz generowały bardzo powolny i długi kod programu.
Kompilacja zakończona sukcesem w Action! nie powinna wyświetlić żadnego komunikatu, ekran powinien wyglądać tak jak na powyższym rysunku, czyli tak jak wyglądał zaraz po przejściu do monitora.
3. Uruchomić program
Aby uruchomić program wpisujemy literę "R" ("R" jak Run - działaj) i zatwierdzamy klawiszem Return.
Brawo ! Uruchomiłeś swój pierwszy program w Action!

Na ekranie powinniśmy zobaczyć animowane kolorowe rurki (tzw. bary).
Aby opuścić program, należy nacisnąć klawisz "ESC". Dzięki wpisaniu odpowiedniego polecenia (K=42), w monitorze Action! pojawia się już litera "E" (przejście do edytora Action!), którą wystarczy zatwierdzić klawiszem Return, aby ponownie edytować tzw. kod źródłowy programu (nazywany w skrócie "źródłem").
Opis źródła programu
Teraz opiszę części, z których się składa nasz program oraz to co i w którym miejscu jest wykonywane, aby osiągnąć ten efekt, który jest widoczny po uruchomieniu. Postaramy się też nieco ten program zmienić.
W pierwszej kolejności pewnie chciałbyś się dowiedzieć z jakich elementów składa się ten program, gdzie jest jego początek, gdzie się wykonuje itp. Dlatego postaram się fragment po fragmencie to wyjaśnić.
Pierwszą komendą, która zaczyna ten program jest
MODULEO dziwo tak naprawdę ta instrukcja nic nie robi, a jedynie pokazuje początek programu (modułu). Więc skoro nic nie robi, czy jest potrzebna? Nie jest, jak zapewne widać w innych przykładach z tej stronki, to słowo kluczowe możemy w wielu programach pominąć. Nie będę teraz się rozwodził o tym po co i kiedy jest ono potrzebne, gdyż w wielu Twoich programach nie będzie to miało znaczenia.
Umówmy się jedynie, że zgodnie z dobrym obyczajem programistycznym wpisuj MODULE na początku programu i już ;)
Następnym elementem są deklaracje zmiennych (globalnych):
BYTE A, AN=$D40A, LI=$D40B, KOL=$D01A ,KOL1=$D018, K=764, CZAS=20Zmienne muszą być deklarowane właśnie w tym miejscu programu. Dodatkowo w stosunku do BASICa, mamy tutaj bardzo ważną różnicę, w Action! wszystkie wykorzystane zmienne muszą być zadeklarowane.
A co to jest deklaracja zmiennej ? To jest poinformowanie kompilatora, że z danej zmiennej będziemy korzystać (choć nie ma co do tego przymusu). Dodatkowymi bardzo istotnymi informacjami są nazwy używanych zmiennych, ich typ (np. BYTE, CARD, INT itp.) oraz ew. wartość początkowa.
W BASICu jest taka zasada, że deklaracja zmiennej następuje w momencie jej pierwszego użycia, jest to dość wygodne (choć niestety nie mamy kontroli nad tym procesem). W Action! tak nie jest i może się to wydawać nieco niewygodne, jednak wiąże się z tym faktem kilka ciekawych rzeczy, przykładowo precyzyjnie określając typ zmiennej możemy zaoszczędzić miejsce w pamięci RAM (jest to nieosiągalne w większości języków BASIC).
Co kompilator robi z tymi zmiennymi? Powyższy kod źródłowy nie wykonuje właściwie nic podobnie jak MODULE, tzn. nie da się takimi operacjami deklaracji zmiennych zrobić gry w szachy czy warcaby;) Jednak jak już pisałem są to bardzo istotne informacje dla kompilatora. Sam uruchomiony program nic nie robi z tymi zmiennymi, ale wcześniej robi z nimi coś bardzo istotnego sam kompilator. W procesie kompilacji przydzielana jest pamięć RAM na każdą z tych zmiennych. Kompilator wie jakiego typu są zmienne więc proces ten nie stanowi dla niego żadnego problemu. Z drugiej strony oznacza to tyle, że jeśli zadeklarujemy zmienną, a jej nie użyjemy to zajmiemy pamięć na marne - i tak faktycznie będzie !;)
Dodatkowo jeśli w programie ustaliliśmy wartość początkową danej zmiennej to kompilator podczas kompilacji podstawi tę wartość we właściwe miejsce pamięci (tam gdzie znajduje się zmienna), może to wyglądać następująco:
CARD ROK=[2013]Jeśli zmienna nie zostanie zadeklarowana (często się to zdarza, że zapomnimy) to kompilator zgłosi nam, że nie zna danej zmiennej zgłaszając błąd numer 8 (czasami można daną zmienną nieprawidłowo zapisać itp. a kompilator też jej nie rozpozna).
Następną częścią naszego programu jest jego główna procedura, od której komputer rozpoczyna uruchomienie programu (jego wykonywanie):
PROC TU() (...) RETURNProcedura może się nazywać (niemal) dowolnie, w tym przykładzie jej nazwą jest "tu" (od TUtaj program się uruchamia). "RETURN" oznacza miejsce gdzie jest wyjście z procedury (jej koniec), czyli w tym przypadku oznacza to także wyjście z naszego programu (zakończenie go).
Następnym elementem jest pętla typu "WHILE":
WHILE K<>28 DO (...) ODPo WHILE znajduje się warunek wyjścia z pętli (może on być nigdy niespełniony, czyli tzw. pętla nieskończona). W tym wypadku jest to K różne od 28 (różne ma oznaczenie w Action! "<>" tak jak w BASICu oraz "#" czyli nierówne). Zmienna "K" jest specjalną zmienną w tym programie, która obsługuje (w prosty sposób) naciśnięte klawisze, a 28 to kod klawisza ESC.
Innymi słowy ten zapis odczytujemy: wykonuj pętlę dopóki zostanie naciśnięty klawisz ESC.
Słowa kluczowe DO i OD, są ramami określającymi początek oraz koniec pętli (np. w C/C++ mamy nawiasy "{" i "}"). Wszystkie instrukcje, które znajdują się między nimi stanowią treść pętli, czyli kod który będzie wykonywany tyle razy ile na to zezwolą warunki końca pętli.
Najistotniejszym elementem programu jest to co znajduje się we wnętrzu pętli, czyli to co on faktycznie wykonuje (w tym wypadku zmienia kolory ekranu oraz je animuje):
WHILE K<>28 DO KOL=LI+CZAS AN=1 ODZapis KOL=LI+CZAS, może się wydawać bardzo nieczytelny, o ile nigdy nie programowałeś na komputerze 8bitowym. Jeśli programowałeś w asemblerze 6502 to zapewne doskonale wiesz co się tutaj dzieje.
Działanie tego programu jest dość specyficzne i ściśle wiąże się z konstrukcją sprzętową komputerów Atari (która jest bardzo udana, nowoczesna i zaawansowana).
Jeśli jednak chcesz się dowiedzieć jak to działa to tu znajduje się skrócony opis: Zmienna KOL zostaje przypisana do rejestru odpowiadającego za kolor ramki obrazu. Wpisując do tej zmiennej jakąkolwiek wartość, spowoduje to bardzo szybki zapis do odpowiedniego rejestru koloru Atari (odbędzie się to tak szybko jak w asemblerze). Zmienna LI wskazuje na sprzętowy rejestr zliczający kolejne linie obrazu rysowane przez procesor graficzny (oznacza to tyle, że procesor graficzny ANTIC rysując nową linię, wartość tę przekaże do KOL czyli koloru ramki obrazu). Natomiast zmienna CZAS to bardzo ciekawe rozwiązanie, mianowicie system operacyjny Atari, samodzielnie (bez naszego udziału) odmierza upływ czasu (np. w pewnym momencie uruchomi "wygaszacz"), dlatego my ten zwiększający się czas używamy do przesuwania się kolorów. Oczywiście znak "+" dodaje obie wartości do siebie. Zapisanie dowolnej wartości do zmiennej AN, spowoduje zatrzymanie się programu do końca rysowania linii obrazu przez procesor graficzny. Dzięki temu może dziwnemu dla Ciebie rozwiązaniu jesteśmy pewni, że kolor jaki ustawiliśmy będzie widoczny przez całą linię obrazu (cały czas jej trwania). To spowolnienie faktycznie jest bardzo szybkie (odbywa się na chwilę z czasu trwania rysowania linii obrazu, a odbywa się to kilkanaście tysięcy razy na sekundę mimo spowolnienia), ale niezbędne, gdyż w przeciwnym wypadku zmiany kolorów odbywałyby się dużo, dużo za szybko (oraz nie byłyby zsynchronizowane z pracą procesora ANTIC, czyli z procesem rysowania obrazu). |
Jeśli jesteś zdziwiony tym co tutaj zobaczyłeś, to pragnę podkreślić, że to jest jedna z największych zalet Action! że da się to zrobić tak łatwo, szybko i wygodnie. Faktycznie w 16 znakach został zapisany cały główny kod programu, który powoduje narysowanie barów oraz ich animację ! (a da się to zrobić jeszcze krócej).
Na koniec przed RETURN znajduje się, komenda:
K=42Jak już pisałem wcześniej zmienna K jest przypisana do rejestru (tzw. cienia) o adresie 764, czyli odpowiedzialnego za odczyt klawiatury. To co tutaj robię to zapisuję do rejestru klawiatury, to dość dziwne bo jeśli przeczytacie jakąś książkę informatyczną to będą Was przekonywać, że klawiatura jest urządzeniem tylko do odczytu :P;)
Faktycznie to taki prosty trik, który symuluje naciśnięcie klawisza. W tym wypadku ma to na celu ułatwienie pracy z programem, gdyż po zakończeniu programu z dużym prawdopodobieństwem będziemy chcieli przejść do edytora Action! aby programować dalej (a jeśli często robimy coś innego to zmieniając kod tego klawisza można sobie to ustawić do innego celu). Dlatego teraz po zakończeniu programu w linii monitora zobaczymy literę "E" tak jakby faktycznie została naciśnięta na klawiaturze. Więc wystarczy ją jedynie zatwierdzić "Return" i już jesteśmy ponownie w edytorze.
Szczegóły programu
Warto opisać kilka szczegółów związanych z tym programem. Jeśli ciebie one teraz nie interesują możesz pominąć ten fragment opisu przeskakując do rozdziału pt. "Zrobimy coś innego ?".
Warto wyjaśnić co jest ustalane podczas deklaracji zmiennych:
BYTE A, AN=$D40A, LI=$D40B, KOL=$D01A ,KOL1=$D018, K=764, CZAS=20W tym miejscu informujemy kompilator Action! że chcemy w programie używać 7 zmiennych (faktycznie korzystamy tylko z 6). Wszystkie zmienne są typu BYTE, czyli zajmują w pamięci RAM najmniejszą możliwą wielkość (podstawową) jeden bajt. Wszystkie zmienne są bardzo specyficzne gdyż są przypisane do konkretnych adresów w pamięci Atari, z wyjątkiem pierwszej zmiennej o nazwie "A". Ta pierwsza zmienna jest zwykłą zmienną, czyli możemy ją w programie używać do dowolnych celów (oraz zmienna ta nie ma ustawionej wartości początkowej).
Pozostałe zmienne za pomocą znaku równości, przesuwają faktyczne miejsce, w którym się znajdują na wskazany adres. Oczywiście nie jest to dziełem przypadku, w tych miejscach w pamięci Atari znajdują się rejestry (lub tzw. rejestry cienie) odpowiadające za konkretne zachowania się komputera.
Od tej chwili odczyt z tych zmiennych będzie odczytywał wartość znajdującą się w danym rejestrze, przykładowo jeśli w miejsce
KOL=LI+CZAS AN=1Wpiszemy:
A=CZAS PRINTBE(A)lub krócej:
PRINTBE(CZAS)po uruchomieniu programu zobaczymy kolejne odczyty zawartości rejestru odpowiedzialnego za odmierzanie czasu przez system operacyjny Atari. Tak się składa akurat w tym momencie, że wartości te będą się co linijkę zwiększać (najczęściej) o jeden.
Skoro zapisaliśmy, że wszystkie zmienne są typu BYTE to oznacza to tyle, że z każdej zmiennej możemy odczytać wartość typu bajt oraz zapisać taką samą (wartości z przedziału 0-255). Dotyczy to również rejestrów większych od bajtu, jeśli jest taka potrzeba to oczywiście można odczytywać wartości ze zmiennych zadeklarowanych jako CARD (dwa bajty, wartości z przedziału 0-65535).
A co jeśli przekroczymy zakres zmiennej?
Nastąpi przepełnienie, które gracze zgodnie ze swoimi obserwacjami nazywają "przekręceniem licznika". Język Action! nic szczególnego nie robi jeśli zajdzie taka ewentualność. W skrócie odejmując jeden od zera otrzymamy 255 (bajt) lub 65535 (dwa bajty czyli CARD). To że Action! nie reaguje na takie sytuacje ukazuje jego jakość, czyli że jest to język który służy do tworzenia dobrych, szybkich programów (to jest podobna cecha jak w językach C/C++).
Powróćmy jednak do zmiennych i rejestrów sprzętowych:
AN=$D40A, LI=$D40B, KOL=$D01A ,KOL1=$D018, K=764, CZAS=20Zmienna AN znajduje się w adresie $D40A (to jest zapis wartości szesnastkowej, oznaczany też czasami np. 0xD40A, D40Ah lub inaczej - jeśli nie wiesz o co chodzi polecam przeczytanie i dobre zapoznanie się o co chodzi, np. wstęp na Wikipedii. Ten rejestr procesora graficznego ANTIC, powoduje uaktywnienie linii procesora centralnego MOS 6502, która go zatrzyma na pewien czas (zapis do tego rejestru powoduje wyzwolenie tego sygnału).
Zmienna LI bardzo podobnie, jednak zapisuje do niej wartości procesor graficzny podczas rysowania obrazu, więc my będziemy z tego rejestru odczytywać wartości, a nie zapisywać.
Zmienna KOL to rejestr koloru (to nie jest rejestr cienia) układu GTIA, który koloruje obraz tworzony przez procesor graficzny. Zapis do tej zmiennej spowoduje natychmiastową zmianę koloru obrazu. Odbywa się to bardzo szybko i jeśli coś nieprawidłowo zaprogramujemy to takie zapisy mogą wywoływać dziwne efekty migania obrazu (wiele razy na linię)
Druga zmienna KOL1 to kolejny rejestr koloru, przyda się nam zaraz do drugiego przykładu animacji.
Zmienna K jest wstawiona (podobno :D :D) pod adres cienia o numerze 764 (czyli tym razem zapis dziesiętny, a nie szesnastkowy), rejestr ten jest specyficznym rejestrem, gdyż określa się go mianem rejestru cienia. Oznacza to tyle, że do tego rejestru można zapisywać i odczytywać wartości bez żadnych problemów (ze zwykłych rejestrów sprzętowych czasami nie można zapisywać lub odczytywać itp.) oraz że operacja zapisu będzie wywierała efekt jedynie 50 razy na sekundę (60 dla komputerów działających w systemie NTSC).
Jeśli żaden klawisz nie został naciśnięty odczyt ze zmiennej K będzie zwracał wartość 255. Jeśli klawisz został naciśnięty to K zwróci nam kod naciśniętego klawisza. Następnie gdy już go odczytamy, należy ten rejestr zresetować wstawiając do niego kod 255 (czyli od tej pory komputer "sądzi", że żaden klawisz nie został naciśnięty).
Można to podglądać wstawiając podobnie jak wcześniej do wnętrza pętli (kasując wcześniejszą zawartość):
PRINTBE(K)Po uruchomieniu i sprawdzeniu widać, że (prawie) każdy klawisz ma swój kod, a naciśnięcie klawiszy w połączeniu z SHIFT i CONTROL dają inne kody, co można wykorzystać w swoich programach.
Przerwać taki program można również naciskając klawisz Break (działa jedynie jeśli w programie wykorzystujemy takie procedury jak PRINT() i inne znane np. z BASICa).
Zmienna CZAS działa podobnie jak K, z tą różnicą, że pod adresem 20 (zapis dziesiętny) system Atari odmierza czas. Odczyt z tego rejestru podaje nam aktualny czas jaki odmierzył system (zapis do tego rejestru jest możliwy).
Przykładowo jeśli zamiast 20 wpiszemy wartość 77 to będziemy mogli odliczać jaki czas upłynął od ostatniego naciśnięcia klawisza (służy do odmierzania czasu kiedy uruchomiony zostanie wygaszacz ekranu). Czas ten jednak jest odmierzany dość specyficznie, mianowicie wartość jest zwiększana, wtedy kiedy rejestr 20 doliczy do 255 (czyli nie jest to sekunda, ani minuta itp.).
Skoro już wiemy co i jak może być wykorzystywane za pośrednictwem tych zmiennych, to może jeszcze raz skupimy się na tym co program robi w głównej pętli:
KOL=LI+CZAS AN=1Mamy tutaj dwie oddzielne operacje podstawienia. W pierwszej odczytywane są wartości ze zmiennych (faktycznie rejestrów) LI oraz CZAS, sumowane, a następnie wynik jest zapisywany do rejestru koloru opisanego zmienną KOL.
Wartości zapisywane do tego rejestru mają niejako "aktywne" jedynie 7 bitów (z 8 dostępnych w tym bajcie) co oznacza, że widocznych kolorów jest maksymalnie 2^7=128. Konkretnie jest to 16 kolorów w 8 odcieniach - dość sporo jak na komputer 8bitowy (i to sporo młodszy od ZX Spectrum i Commodore 64).
Ostatnia operacja podstawienia, wstawia wartość 1 do rejestru AN, który właśnie jest "wrażliwy" na operacje zapisu. Jak tłumaczyłem powoduje to chwilowe zatrzymanie się głównego procesora w celach wizualnych;)
Dla tego rejestru nie ma znaczenia jaka wartość będzie zapisana. Istotne jest jedynie aby zaistniała stosowna reakcja, gdy zapis zostanie stwierdzony.
Następnie dzięki pętli WHILE cała operacja jest wykonywana na okrągło (bez końca), aż nie przerwiemy tego naciśnięciem klawisza ESC. Efekt jest taki, że gdy zmieni się wartość rejestrów LI+CZAS (czyli albo zmiana numeru linii albo zwiększenie czasu) to kolory zmienią się.
Jeśli zainteresowała Ciebie możliwość wykorzystywania cech rejestrów sprzętowych Atari w programowaniu to oczywiście pragnę wyjaśnić, że podobnych rejestrów, które można fajnie wykorzystać jest bardzo dużo - właściwie rekordowo ! Programowanie tego rodzaju jest kwintesencją programowania na komputerach takich jak Atari czy Commodore 64. Jednak sprzęt ten ma do zaoferowania jeszcze więcej, jeśli już jesteś zachwycony to zapewne ucieszysz się, że na operowaniu rejestrami zabawa się absolutnie nie kończy. Warto zapoznać się z wszystkimi trybami graficznymi Atari (procedura GRAPHICS(n)), sprzętowymi duszkami (wiele można z nimi w Action! zrobić!), a szczególnej uwagi wymaga program procesora graficznego ANTIC, tzw. display list, który jest niezwykłym cudem dla każdego kto się nim miał możliwość pobawić (warto wspomnieć o jego przerwaniach tzw. DLI).
Aby pogłębić wiedzę odnośnie np. rejestrów Atari, należy zapoznać się z jakimś opisem tzw. mapa pamięci Atari itp.
Kilka słów o procedurach PRINT()
Jak demonstrowałem wyżej, wstawiając do głównej pętli programu instrukcję tego rodzaju, można wyświetlać na ekranie jakieś zmiany w systemie lub w naszym programie podobnie jak ma to miejsce w BASICach (np. print lub ?). Wyświetlanie takie można zatrzymać na chwilę kombinacją Control+1 oraz przerwać (program) naciskając Break.
W Action! znajduje się spora ilość wariantów tej procedury. Jeśli przykładowo chcemy wyświetlać wartości typu BYTE, możemy napisać tak:
BYTE CZAS=20, K=764 PROC TU() WHILE K<>28 DO PRINTBE(CZAS) OD K=42 RETURNJeśli chcemy wyświetlać je wypełniając kolejne linie możemy to zrobić np. tak:
PRINTB(CZAS) PRINT(", ")Jeśli chcemy wyświetlić (poprzednim sposobem) wartości typu CARD, robimy to np. tak:
BYTE K=764 CARD ZLICZ=[0] PROC TU() WHILE K<>28 DO PRINTCE(ZLICZ) ZLICZ==+1 OD K=42 RETURNTeraz program będzie odliczał zaczynając od 0 a kończąc na końcu zakresu zmiennych typu CARD, czyli 65535.
Pewnie już zauważyłeś, że PRINTB() wyświetla zwartość zmiennej typu BYTE, a PRINTC() wyświetla wartości typu CARD. Natomiast PRINT() wyświetla tekst bez przejścia kursora do nowej linii, natomiast PRINTE() z przejściem do nowej linii. Odbywa się to nieco na wzór roli znaku ";" w BASICach. Można też łączyć wypisywanie wartości z przejściem do nowej linii: PRINTBE() lub PRINTCE().
Należy podkreślić, że w Action! nie istnieje zapis skrócony "?" znany z BASICów. Natomiast w to miejsce istnieje możliwość napisania własnych procedur piszących oraz rysujących, które będą działały bardzo szybko, mimo braku wykorzystania asemblera (szybkie procedury piszące są o tyle istotne, że wiele gier powstaje w trybach tekstowych, np. polskie gry Fred, Misja i wiele innych).
Zrobimy coś innego?
A teraz zademonstruję jak łatwo jest ten program (przykład1) zmodyfikować osiągając zupełnie inny rezultat. Zmieńmy jedynie zawartość pętli z:
KOL=LI+CZAS AN=1Na:
KOL1=LI+CZAS AN=1(po zmiennej KOL został dodany jedynie znak "1", który musi znajdować się zaraz za KOL bez żadnego odstępu)
Teraz uruchom program i sprawdź co się zmieniło ?
Zmieniło się coś? A jakże !;)
Jak widać posiadając taki szkielet programu w Action! zawierający główną pętlę programu oraz kilka zadeklarowanych zmiennych, mamy absolutne minimum aby programować dalej. Teraz nawet tak niewielkie zmiany (zmiana tylko jednego znaku!), mogą w rezultacie dawać zupełnie inne rezultaty ;)
Witam Ciebie w świecie Action! ;)
Kompilacja programu do pliku
Zapewne chciałbyś umieć ten program zapisać do pliku, aby np. posłać go znajomemu lub udostępnić w internecie (choć ten program może jest zbyt prosty aby od razu go zamieszczać ;)
Wykonuje się to bardzo prosto. Przechodzimy do monitora Action! naciskając klawisze Shift+Control+M, wybieramy opcję C (klawisz "C" + Return). Po zakończonej kompilacji wydajemy polecenie zapisania skompilowanego kodu do pliku wykonywalnego, pisząc np.
W "D:nazwa.xex"Od tej pory program został zapisany do pliku i można go uruchomić z poziomu np. DOSa. Najczęściej spotykanymi rozszerzeniami plików wykonywalnych są "com", "exe" oraz "xex", który przyjął się ze względu na powszechność emulatorów Atari.
Przed wykonaniem powyższych kroków warto usunąć z końca programu linię:
K=42Jest ona niepotrzebna jeśli program ma być uruchamiany nie z poziomu Action! ale np. DOSa. Kasowanie takiej linii można wykonać nie tylko poprzez fizyczne skasowanie zawartości linii, można to też wykonać poleceniem REM (z BASICa), czyli w Action! za pomocą średnika, a prezentuje się to następująco:
;K=42Tak rozpoczynająca się linia jest traktowana przez kompilator jakby nic nie zawierała (konkretnie od znaku średnika jest wszystko ignorowane do końca linii).
Jeszcze inną opcją jest zamiana na:
K=255Czyli kasowanie bufora klawiatury, choć to już zależy od tego co chcesz osiągnąć.
Tu muszę jednak wyjaśnić jedną kwestię. Język Action! od samego początku został tak zaprojektowany, że miał być dostępny tylko na cartridge'u (to rzadkość bo zwykle języki programowania się robiło i dziś robi również, aby się je wczytywało i uruchamiało z plików), dlatego program zapisany do pliku powyższą metodą, zwykle wymaga posiadania takiego cartridge. Jednak nie w tym przypadku - program z pierwszego przykładu nie wykorzystuje nic co się znajduje w pamięci stałej (ROM) tego języka, więc będzie można go uruchomić na każdym komputerze Atari (w najgorszym przypadku może jedynie nastąpić kolizja z jakimś innym DOSem, który będzie sprawiał jakieś problemy).
Jeśli wykorzystamy w naszym programie jakieś standardowe elementy języka Action! które znajdują się w pamięci ROM cartridge, to pojawi się pewien problem. Wtedy trzeba przejść nieco inną procedurę przygotowania pliku tak aby się uruchamiał u każdego (to będzie opisane w oddzielnym artykule).
Elementami, które powodują takie problemy są:
1. Używanie procedur bibliotecznych np. PRINT(), GRAPHICS(), POKE(), PEEK(), PLOT(), DRAWTO(), OPEN() itp.
2. Używanie operacji mnożenia i dzielenia (procesor 6502 nie posiada takich operacji)
3. Używanie większej ilości parametrów procedur i funkcji niż 3 bajty.
Widać wyraźnie, że nasz program nie wykorzystuje żadnego z wyżej wymienionych elementów.
Na koniec warto podkreślić, że nasz program z przykładu pierwszego po kompilacji zajmuje w pamięci jedynie 38 bajtów ! To pokazuje, że Action! generuje nie tylko kod szybki, ale też mały, a oszczędność na komputerach 8bitowych jest bardzo istotna.
Dla kontrastu podam, że nic nierobiący program (czyli nie posiadający żadnych operacji wykonywanych wewnątrz procedury wykonywanej po uruchomieniu programu) dla dzisiejszego peceta może zajmować 40-60 kb i więcej (czyli tyle ile ma całkowitej pamięci Atari 65 XE lub Commodore 64 :P).
Masz jakieś pomysły, chcesz coś jeszcze ciekawego zaprogramować w Action!?
Teraz już wiesz od czego zacząć, ja mogę już tylko powiedzieć: do dzieła!;)
Najczęściej używane funkcje Action! 1. monitor:
Wymienię kilka przydatnych operacji jakie są możliwe do wykonania w trybie monitora.
- komenda X umożliwia wykonanie dowolnej procedury bibliotecznej Action! (znajdującej się w ROMie cartridge'a z tym językiem), przykładowo:
X GRAPHICS(0) X POKE (712,255)operacje te muszą być napisane zgodnie ze składnią języka (nie posiadać żadnych błędów, np. muszą być zamknięte wszystkie nawiasy itp.)
Jest to odpowiednik natychmiastowego wykonania instrukcji w języku BASIC, czyli wpisanie polecenia bez numeru linii. Jednak w Action! ma to bardziej cel testujący czy debugujący, niż taki jak w BASICu. Wynika to z tego, że do tego celu w Action! służy drugie okienko tekstowe, w którym zawsze możemy napisać jakiś krótki kod i uruchamiać go przemiennie z tym podstawowym.
- SET - umożliwia wstawienie wartości do podanego adresu, np.
SET 712=255- ? - wyświetla wartości zapisane w danym adresie, np.
? 712

W pierwszej kolejności widzimy adres (tu 712), ten sam adres zapisany szesnastkowo, wartość zapisaną jako znak ATASCII, wartość zapisaną jako dwa bajty szesnastkowo oraz dwa bajty w zapisie dziesiętnym
- * - działa podobnie jak "?", ale wyświetla kolejne bajty.
Zatrzymać ten proces możemy na chwilę naciskając Control+1, a następnie zwalniamy tą samą kombinacją (to ważne bo inaczej nie będzie można pisać za pomocą klawiatury!). Przerywamy tryb monitorowania pamięci klawiszem spacji.
- B - bootowanie języka Action! (kasowanie wszystkich danych w pamięci)
Z racji swej roli wymaga potwierdzenia klawiszem "Y". Jeśli go naciśniemy widzimy Action! taki jakby dopiero co został uruchomiony.
- O - przejście do wyświetlania/zmieniania opcji kompilacji i edycji
Chcąc zmienić daną opcję wpisujemy odpowiednią wartość (tekstową lub liczbową), jeśli chcemy pominąć to naciskamy Return, a jeśli chcemy opuścić tryb zmian opcji naciskamy ESC.
2. Edytor:
Shift+Control+Góra - przesunięcie się o jeden ekran w górę (zależy od aktualnych ustawień wielkości okienka edycyjnego)
Shift+Control+Dół - przesunięcie się o jeden ekran w dół (--"--)
Shift+Control+< - przesuwanie podglądu edycji w lewo (nie działa pod emulatorem Altirra w wersji np. 2.20)
Shift+Control+> - przesunięcie podglądu edycji w prawo (--"--)
Shift+Control+E - przeskoczenie do końca tekstu
Shift+Control+Return - wstawienie pustej linii lub podział linii na dwie (w miejscu kursora)
Shift+Control+Backspace - połączenie bieżącej linii z linią znajdującą się wyżej (o ile kursor znajduje się na początku linii)
Shift+Control+T - ustawianie znaczników
Shift+Control+U - Undo - działa nieco inaczej niż dziś się do tego przyzwyczailiśmy. Działa tylko odnośnie aktualnej linii do czasu aż jej nie opuścimy (czyli przywraca zmianę tej jednej linii od czasu, gdy przesunięty został na nią kursor)
Shift+Delete - kasowanie kolejnych linii oraz wstawianie ich do "schowka" (odpowiednik Control+X itp.)
Shift+Control+P - wstawianie tekstu ze "schowka" do aktualnego okienka edycyjnego (odpowiednik Control+V itp.)
Shift+Control+I - przełączanie się pomiędzy dwoma trybami edycji INSERT i REPLACE. Pierwszy jest naturalny dla użytkowników edytorów dla małego Atari, drugi będzie bardziej intuicyjny dla osób przyzwyczajonych do współczesnych edytorów dla dużych komputerów Atari, Apple lub pecetów.
Shift+Control+R - wczytanie pliku tekstowego (również ponowne, czyli dołączenie pliku do tekstu)
Shift+Control+W - zapis pliku tekstowego z aktywnego okna edycyjnego (źródło programu)
Shift+Control+1 (lub 2) - przejście między oknami edycji
Shift+Control+D - zamknięcie aktywnego okienka (bez zapisu zawartości)
Clear - kasowanie zawartości aktywnego okienka bez zamykania samego okienka
Shift+Control+F - szukanie ciągu znaków w tekście programu (lub innego edytowanego pliku tekstowego)
Shift+Control+S - zamiana ciągów znaków
Ciągi tekstowe wpisywane w operacjach takich jak np. R, W, F, S itp. są zapamiętywane i proponowane po ponownym użyciu. Możemy je wykorzystać, skasować lub edytować naciskając Backspace.
W edytorze działa też wiele typowych dla Atari kombinacji klawiszy, jednak mogą one działać nieco inaczej, gdyż edytor Action! został przemyślany nieco inaczej niż systemowy edytor (czyli został zaprojektowany bardziej elastycznie, udostępniając wiele nowych możliwości).
Zachęcam do eksperymentowania, kiedyś Action! stanowił na Atari (i nie tylko) zupełnie nową jakość, dziś warto o tym wiedzieć oraz przekonać się o tym własnoręcznie;)
