Obsah
1. EndBASIC: moderní reinkarnace klasického BASICu
2. Specifické vlastnosti klasických BASICů
3. Přepis algoritmu do EndBASICu
4. Od špagetového kódu ke strukturovanému programování
5. GOTO a výpočet největšího společného dělitele
6. Strukturované příkazy pro programové smyčky
7. Počítaná programová smyčka FOR-NEXT
8. Smyčka typu WHILE-WEND s podmínkou na začátku
9. Výpočet konstanty π varianta s FOR-NEXT i WHILE-WEND
12. Data jako součást zdrojového kódu
13. Strukturovaný příkaz pro rozvětvení
14. Kombinace textové a grafické plochy
19. Repositář s demonstračními příklady
1. EndBASIC: moderní reinkarnace klasického BASICu
Na úterní článek, v němž jsme se mj. věnovali i moderním reinkarnacím klasického BASICu v podobě, v jaké si ho mnozí pamatujeme z dob kralování osmibitových domácích mikropočítačů, dnes částečně navážeme. Ukážeme si totiž některé možnosti, ale nutno dodat, že taktéž omezení EndBASICu, což je interpret BASICu naprogramovaný v Rustu. To je dosti neobvyklá kombinace technologií, protože původní BASICy byly psány většinou v čistém assembleru. EndBASIC se do jisté míry snaží napodobit chování klasických interpretrů BASICu, ovšem nabízí i některá vylepšení. Na druhou stranu však uvidíme, že některé jazykové konstrukce nejsou v EndBASICu povoleny nebo se chovají odlišně, což je škoda, protože se v mnoha ohledech jedná o poměrně zajímavý (nostalgický) projekt, který však není zpětně kompatibilní asi s žádným klasickým BASICem.
Obrázek 1: Online varianta EndBASICu.
EndBASIC si můžete stáhnout pro váš operační systém ze stránky https://www.endbasic.dev/ a spouštět ho lokálně. Aby vše pracovalo korektně, tedy i grafické příkazy, je nutné mít nainstalovánu knihovnu SDL2; bez ní sice bude EndBASIC funkční, ale bez grafického výstupu.
2. Specifické vlastnosti klasických BASICů
Ještě před vlastním popisem EndBASICu si krátce připomeňme, jak vlastně vypadaly programy psané pro interpretry BASICů běžících na osmibitových domácích mikropočítačích. Příkladem může být program pro seřazení prvků v poli pomalým a naivním algoritmem bublinkového řazení (bubble sort). Realizace tohoto programu v Atari BASICu může vypadat například následovně:
1 REM ***************************** 2 REM Bubble sort 3 REM 4 REM Uprava pro Atari BASIC 5 REM 6 REM ***************************** 7 REM 8 REM 9 REM 10 DIM A(20) 11 FOR I=0 TO 20 12 A(I)=INT(100*RND(0)) 13 NEXT I 14 GOSUB 100:REM TISK OBSAHU POLE 20 FOR I=19 TO 0 STEP -1 21 PRINT "."; 25 FOR J=0 TO I 30 IF A(J)<A(J+1) THEN X=A(J):A(J)=A(J+1):A(J+1)=X 35 NEXT J 40 NEXT I 49 PRINT "" 50 PRINT "SORTED:" 60 GOSUB 100:REM TISK OBSAHU POLE 99 STOP 100 REM TISK OBSAHU POLE 101 FOR I=0 TO 20 102 PRINT I,A(I) 103 NEXT I 104 RETURN 998 REM finito 999 STOP
Povšimněte si několika faktů:
- Čísla řádků jsou povinná
- Všechny proměnné jsou globální a automaticky definované při prvním použití
- Jedinými datovými typy jsou reálná čísla, řetězce (zde nepoužité) a 1D či 2D pole reálných čísel
- Pole jsou indexována od nuly
- Indexy prvků polí končí až stanoveného limitu (tedy v našem případě včetně 20)
- Podmínka IF je nestrukturovaným příkazem platným do konce řádku (a nelze rozdělit na více řádků)
- Jedinými strukturovanými příkazy je smyčka FOR-NEXT a dvojice příkazů GOSUB a RETURN
- Počítané smyčky mění počitadlo od dolní meze až do horní meze včetně (zde tedy při tisku pole od 0 do 20 včetně)
Výše uvedené chování při alokaci polí i při tvorbě počítané programové smyčky zamezuje mnoha typických chybám &plusm;1 (naproti tomu C a od něj odvozené jazyky se chovají odlišně a poněkud méně intuitivně).
Realizace téhož algoritmu, tentokrát v TurboBASICu XL (tedy pro stejnou platformu), je poněkud odlišná. Opět se nejdříve podívejme na zdrojový kód, který posléze krátce okomentujeme:
1 REM BUBBLE SORT 2 REM IMPLEMENTACE PRO TURBO-BASIC XL 3 REM 4 REM ---------------------------- 5 REM 9 SIZE=20 10 DIM A(SIZE) 11 FOR I=0 TO SIZE 12 A(I)=RAND(100) 13 NEXT I 14 EXEC PRINT_ARRAY 20 FOR I=SIZE-1 TO 0 STEP -1 21 PRINT "."; 25 FOR J=0 TO I 30 IF A(J)<A(J+1) 31 X=A(J):A(J)=A(J+1):A(J+1)=X 32 ENDIF 35 NEXT J 40 NEXT I 49 PRINT "" 50 PRINT "SORTED:" 60 EXEC PRINT_ARRAY 99 END 100 ------------------------------ 101 REM TISK OBSAHU POLE 102 PROC PRINT_ARRAY 103 FOR I=0 TO SIZE 104 PRINT I,A(I) 105 NEXT I 106 ENDPROC
Oproti původnímu kódu můžeme najít mnoho užitečných změn:
- Strukturovaný kód je odsazen
- Je podporován strukturovaný příkaz IF-(THEN)-ENDIF
- Existuje zde podpora pro pojmenované procedury (podprogramy): PROC a ENDPROC
- Je podporován „speciální komentář“ zobrazený jako řada ----------, ovšem interně reprezentovaný jediným tokenem
Další úprava byla provedena pro Microsoft BASIC. Jedná se o program, který je teoreticky nezávislý na HW (měl by běžet i na jiných strojích), ovšem kvůli použití HW časovače je zdrojový kód jako celek opět nepřenosný na jiné platformy. Důležitou změnou typickou právě pro Microsoft BASIC, je podpora celočíselného datového typu, který je použit tehdy, pokud se za jméno proměnné vloží znak procenta. Výsledný běh programu je díky tomu mnohonásobně rychlejší (někdy i řádově rychlejší) oproti použití reálných čísel (na HW bez matematického koprocesoru):
1 REM ******************************** 2 REM Bubble sort pro pole typu single 3 REM 4 REM Uprava pro Microsoft BASIC 5 REM 6 REM ******************************** 7 REM 8 REM 9 REM 10 REM VYMAZANI CASOVACE 11 POKE 18,0 12 POKE 19,0 13 POKE 20,0 20 REM VLASTNI BENCHMARK 21 MAX%=50 22 DIM A(MAX%) 23 FOR I%=0 TO MAX% 24 A(I%)=INT(100*RND(0)) 25 NEXT I% 26 GOSUB 100:REM TISK OBSAHU POLE 27 FOR I%=MAX%-1 TO 0 STEP -1 28 PRINT "."; 29 FOR J%=0 TO I% 30 IF A(J%)<A(J%+1) THEN X=A(J%):A(J%)=A(J%+1):A(J%+1)=X 31 NEXT J% 32 NEXT I% 33 PRINT "" 34 PRINT "SORTED:" 35 GOSUB 100:REM TISK OBSAHU POLE 40 REM PRECTENI CASOVACE 41 REM (PLATNE PRO PAL) 42 T=((PEEK(18)*65536+PEEK(19)*256+PEEK(20))/50) 50 PRINT "FINISHED IN ";T;" SECONDS" 99 END 100 REM TISK OBSAHU POLE 101 FOR I%=0 TO MAX% 102 PRINT I%,A(I%) 103 NEXT I% 104 RETURN
V případě přepisu na slavné ZX Spectrum bylo nutné provést několik úprav. Především se to týká polí, které jsou indexovány od jedničky (u některých BASICů existuje volba indexování od 0 nebo od 1). Taktéž se funkce RND volá bez závorek a je nutné používat příkaz LET pro přiřazení:
Obrázek 2: Inicializace pole a tisk pole; realizace v BASICu pro ZX Spectrum (používám emulaci 128k a nikoli původního 48k vlastně jen z toho důvodu, protože můj notebook nemá na klávesnici napsané původní Sinclairovské příkazy :-).
Obrázek 3: Hlavní část algoritmu bublinkového řazení. Prohození hodnot jsem zde realizoval na více řádků kvůli větší čitelnosti.
Přecházíme na platformu IBM PC a slavný GW-BASIC. Úprava pro tento ve své době oblíbený dialekt BASICu může vypadat takto:
1 REM ***************************** 2 REM Bubble sort 3 REM 4 REM Uprava pro GW-BASIC 5 REM 6 REM ***************************** 7 REM 8 REM 9 REM 10 DIM A(20) 11 FOR I=0 TO 20 12 A(I)=INT(100*RND(1)) 13 NEXT I 14 GOSUB 100:REM TISK OBSAHU POLE 20 FOR I=19 TO 0 STEP -1 21 PRINT "."; 25 FOR J=0 TO I 30 IF A(J)<A(J+1) THEN X=A(J):A(J)=A(J+1):A(J+1)=X 35 NEXT J 40 NEXT I 49 PRINT "" 50 PRINT "SORTED:" 60 GOSUB 100:REM TISK OBSAHU POLE 99 END 100 REM TISK OBSAHU POLE 101 FOR I=0 TO 20 102 PRINT I,A(I) 103 NEXT I 104 RETURN
Opět si povšimněte odsazení strukturovaných příkazů (programové smyčky). Jinak se toto řešení do značné míry podobá TurboBASICu či Microsoft BASICu.
3. Přepis algoritmu do EndBASICu
Přímý přepis algoritmu bublinkového řazení do EndBASICu je, ač je to s podivem, relativně složitý, protože se změnila především sémantika některých operací (syntaxe taktéž, ovšem to lze poměrně snadno překonat). První změnou je fakt, že pole se alokují podobně jako v jazycích odvozených od céčka: specifikuje se kapacita pole a nikoli index posledního prvku (kapacita tedy musí být o jedničku vyšší). Dále se používají strukturované podmínky if-then-end if a u počítaných programových smyček for se u klíčového slova next nesmí použít jméno proměnné (to je podle mého názoru škoda, byla to skvělá kontrola korektnosti zápisu). A navíc je možné namísto čísel řádků používat pojmenovaná návěští, podobně jako v mnoha dalších programovacích jazycích i v assemblerech:
rem ***************************** rem Algoritmus bubble sort rem rem Úprava pro EndBASIC rem rem ***************************** dim a(21) for i=0 to 20 a(i)=int(100*rnd(1)) next gosub @PRINT_ARRAY for i=19 to 0 step -1 print "."; for j=0 to i if a(j)<a(j+1) then x=a(j) a(j)=a(j+1) a(j+1)=x end if next next print "" print "sorted:" gosub @PRINT_ARRAY end @PRINT_ARRAY rem Tisk obsahu pole for i=0 to 20 print i,a(i) next return
Výsledky získané tímto programem:
0 90 1 55 2 54 3 17 4 90 5 16 6 10 7 55 8 62 9 25 10 46 11 29 12 10 13 45 14 78 15 91 16 32 17 86 18 73 19 14 20 41 .................... sorted: 0 91 1 90 2 90 3 86 4 78 5 73 6 62 7 55 8 55 9 54 10 46 11 45 12 41 13 32 14 29 15 25 16 17 17 16 18 14 19 10 20 10
4. Od špagetového kódu ke strukturovanému programování
V klasických interpretrech BASICu se striktně používala čísla řádků (jen tak se daly odlišit přímé příkazy od zápisu programu), nestrukturované jazykové konstrukce založené na příkazu GOTO a globální proměnné. Mnohdy ani nebylo možné definovat vlastní procedury nebo funkce. EndBASIC představuje poměrně masivní odklon od tohoto stylu programování. Nicméně si ukažme, jak vypadá použití příkazů GOTO a GOSUB, protože ty jsou esencemi klasického BASICu (první BASIC vůbec měl jen 15 příkazů a mezi nimi GOTO a GOSUB pochopitelně figurovalo). EndBASIC čísla řádků podporuje (ale nevyžaduje) a podporuje i původní sémantiku příkazu GOTO:
Použití čísel řádků a příkazu GOTO pro skok na konkrétní řádek:
rem ***************************** rem rem Klasický příkaz GOTO s čísly rem řádků rem rem Úprava pro EndBASIC rem rem ***************************** 10 print "hello" 20 goto 10
Namísto čísel řádků je možné použít pojmenované návěští, což jsme si již ostatně ukázali na příkladu algoritmu bublinkového řazení. Specialitou EndBASICu je, že návěští musí začínat znakem @:
rem ***************************** rem rem Příkaz GOTO a textové návěští rem rem Úprava pro EndBASIC rem rem ***************************** @opak print "hello" goto @opak
5. GOTO a výpočet největšího společného dělitele
Podobně lze realizovat výpočet největšího společného dělitele bez čísel řádků. Povšimněte si strukturované konstrukce if-then, kterou se EndBASIC odlišuje od starších variant klasických BASICů:
rem ***************************** rem rem Výpočet největšího společného rem dělitele. rem rem Úprava pro EndBASIC rem (využití GOTO) rem rem ***************************** print "x="; input x print "y="; input y rem "Vypocet" @loop if x=y then print "gcd:"; x end if if x>y then x=x-y goto @loop end if if x<y then y=y-x goto @loop end if end
6. Strukturované příkazy pro programové smyčky
EndBASIC podporuje dva formáty strukturovaných programových smyček. Prvním typem smyčky je smyčka FOR-NEXT, která je použita v prakticky všech BASICech – ovšem její chování není všude stejné! A druhým typem smyčky je smyčka WHILE-WEND. Posléze byla do QBasicu přidána i univerzální smyčka DO-LOOP, u níž lze zvolit test na začátku a/nebo i na konci. I tento typ smyčky EndBASICu najdeme. Popišme si ji nejdříve, a to příkladem z dokumentace:
DO PRINT "Infinite loop" LOOP DO a = a + 1 LOOP UNTIL a = 10 DO a = a + 1 LOOP WHILE a < 10 a = 0 DO UNTIL a = 10 a = a + 1 LOOP a = 0 DO WHILE a < 10 a = a + 1 LOOP
Tato smyčka tedy odpovídá jak strukturované konstrukci repeat-until, tak i while-end.
7. Počítaná programová smyčka FOR-NEXT
Nejtypičtější příklad použití počítané smyčky FOR-NEXT. Povšimněte si, jak se pracuje s počáteční a koncovou hodnotou počitadla:
rem ***************************** rem rem Smyčka typu FOR-NEXT rem v základní formě. rem rem Úprava pro EndBASIC rem rem ***************************** for i=0 to 10 print i next
Výsledky ukazují, že počitadlo dosáhlo obou mezních hodnot:
0 1 2 3 4 5 6 7 8 9 10
Specifikovat je v případě potřeby možné i krok určující změnu počitadla smyčky po každé iteraci:
rem ***************************** rem rem Smyčka typu FOR-NEXT rem se specifikací kroku. rem rem Úprava pro EndBASIC rem rem ***************************** for i=0 to 10 step 2 print i next
Výsledky:
0 2 4 6 8 10
Některé BASICy v dalším programu do smyčky vstoupí a vypíšou hodnotu 10, což je nekorektní chování (příkladem je například již zmiňovaný Atari BASIC). EndBASIC, ostatně podobně jako GW-BASIC, v tomto případě korektně nevypíše nic:
rem ***************************** rem rem Smyčka typu FOR-NEXT rem ve formě, kdy je horní limit rem menší než limit horní. rem rem Úprava pro EndBASIC rem rem ***************************** for i=10 to 0 print i next
EndBASIC ovšem do smyčky nevstoupí a tudíž ani nevypíše počáteční hodnotu počitadla.
Otestujme si ještě, kdy a jak se kontroluje mezní hodnota počitadla. Některé BASICy jsou v tomto ohledu velmi flexibilní a umožňují změnu mezí v každé iteraci. EndBASIC ovšem tuto flexibilitu postrádá (a těžko rozhodnout, zda je to dobře nebo špatně):
rem ***************************** rem rem Smyčka typu FOR-NEXT rem s průběžnou změnou horního rem limitu. rem rem Úprava pro EndBASIC rem rem ***************************** max=10 for i=0 to max print i,max max=max+1 next
Tato konstrukce není v EndBASICu korektní:
endbasic: 13:1: Cannot redefine max as a variable
Totéž platí pro pokus o změnu kroku:
rem ***************************** rem rem Smyčka typu FOR-NEXT rem s průběžnou změnou kroku rem změny počitadla smyčky. rem rem Úprava pro EndBASIC rem rem ***************************** s=1 for i=0 to 20 step s print i,s s=s+1 next i
Opět se nejedná o platný zápis:
endbasic: 14:20: STEP needs a literal number
8. Smyčka typu WHILE-WEND s podmínkou na začátku
V mnoha klasických BASICech byl podporován pouze jeden typ programové smyčky, a to konkrétně počítané smyčky typu FOR-NEXT; ostatní typy smyček bylo nutné simulovat kombinací příkazů IF a GOTO. Později byla přidána podpora pro další strukturované příkazy, což mj. znamenalo přidání syntaxe pro programovou smyčku WHILE, jejíž ukončující příkaz se jmenoval WEND. I tento typ smyčky je v EndBASICu podporován, takže si můžeme upravit program pro výpočet největšího společného dělitele takovým způsobem, aby tuto smyčku využíval:
rem ***************************** rem rem Výpočet největšího společného rem dělitele. rem rem Úprava pro EndBASIC rem (využití smyčky WHILE-WEND) rem rem ***************************** print "x="; input x print "y="; input y while x<>y if x>y then x=x-y if x<y then y=y-x wend print "gcd: ";x end
9. Výpočet konstanty π varianta s FOR-NEXT i WHILE-WEND
Následuje nepatrně složitější příklad s dvojicí nepřímo vnořených smyček pro výpočet konstanty π. Nejprve si uveďme variantu s počítanou programovou smyčkou FOR-NEXT:
rem ***************************** rem rem Výpočet konstanty Pi. rem rem Uprava pro EndBASIC rem rem ***************************** n=1 for i=1 to 10 gosub @calc_pi print i,n,pi_approx n=n*2 next end @calc_pi rem rem Subrutina pro výpočet Pi rem pi_approx=4.0 for j=3 to n+2 step 2 pi_approx=pi_approx*(j-1)/j*(j+1)/j next return
Postupně se zlepšující výsledek:
1 1 3.5555555555555554 2 2 3.5555555555555554 3 4 3.4133333333333327 4 8 3.3023935500125976 5 16 3.230036466411717 6 32 3.188127169447139 7 64 3.165482060034797 8 128 3.1536988490958007 9 256 3.147686899556424 10 512 3.1446501625172125
Podobným způsobem ovšem můžeme použít strukturovanou programovou smyčku WHILE-END. Tentokrát ovšem nebudeme muset používat pomocné počitadlo i; smyčka bude řízena přímo hodnotou uloženou v proměnné n:
REM ***************************** REM REM Výpočet hodnoty konstanty PI REM postavený na smyčce REM typu WHILE-WEND. REM rem Úprava pro EndBASIC REM REM ***************************** n=1 while n<=2000 gosub @calc_pi print n,pi_approx n=n*2 wend end rem rem Subrutina pro vypocet pi rem @calc_pi pi_approx=4.0 j=3 while j<=n+2 pi_approx=pi_approx*(j-1)/j*(j+1)/j j=j+2 wend return
Výsledky by měly odpovídat předchozímu řešení:
1 3.5555555555555554 2 3.5555555555555554 4 3.4133333333333327 8 3.3023935500125976 16 3.230036466411717 32 3.188127169447139 64 3.165482060034797 128 3.1536988490958007 256 3.147686899556424 512 3.1446501625172125 1024 3.143124017028201
10. Alokace polí
V EndBASICu se pole alokují příkazem DIM, podobně jako v klasických BASICech. Ovšem je nutné si dát pozor na to, že se zadává velikost pole v každém rozměru a nikoli index posledního prvku v dané dimenzi. To znamená, že je zde sémantický rozdíl v příkazu:
DIM A(10)
Tento zápis má tři možné sémantiky lišící se podle použitého BASICu:
- pole, jehož prvky mají indexy 0, 1, … 10 (jedenáct prvků)
- pole, jehož prvky mají indexy 1, 2, … 10 (deset prvků)
- pole, jehož prvky mají indexy 0, 1, … 9 (deset prvků) – případ EndBASICu
V EndBASICu je taktéž možné používat vícerozměrná pole; nejsme zde tedy omezeni na jednu až dvě dimenze tak, jako v mnoha klasických BASICech:
dim a(10) dim b(10, 10) dim c(10, 10, 20) dim d(10, 20, 30, 40) rem atd.
Příklad práce s jedenáctiprvkovým jednorozměrným polem:
rem ***************************** rem rem Práce s jednorozměrnými poli rem rem Úprava pro EndBASIC rem rem ***************************** rem Definice pole dim a(11) rem Naplnění pole for i=0 to 10 a(i)=10*i next rem Tisk pole for i=0 to 10 print a(i) next end
Výsledky:
0 10 20 30 40 50 60 70 80 90 100
Dtto, ale s dvourozměrným polem:
rem ***************************** rem rem Práce s dvourozměrnymi poli rem rem Úprava pro EndBASIC rem rem ***************************** rem Deklarace pole dim m(6,6) rem Naplnění pole for i=0 to 5 for j=0 to 5 m(i,j)=i*j next next rem Tisk pole for i=0 to 5 for j=0 to 5 print m(i,j), next print next end
Opět si ukažme výsledky:
0 0 0 0 0 0 0 1 2 3 4 5 0 2 4 6 8 10 0 3 6 9 12 15 0 4 8 12 16 20 0 5 10 15 20 25
Navíc je možné použít funkce lbound% a ubound% pro zjištění nejmenšího a největšího indexu v dané dimenzi (nebo v jediné dimenzi jednorozměrného pole):
dim A(10) rem vypíše 0 print lbound%(A) rem vypíše 9 print ubound%(A)
11. Řetězce
Po cca čtyřiceti letech, které uplynuly od doby kralování klasických BASICů, se poněkud pozměnily řetězce. Namísto ASCII znaků (či spíše podmnožiny ASCII znaků) nyní mohou řetězce obsahovat Unicode znaky a interně používají kódování UTF-8. Jinak ovšem v EndBASICu nalezneme běžnou sadu funkcí pro zpracování řetězců. Programátorům jsou k dispozici tři „klasické“ funkce určené pro převody mezi znaky, řetězci a číselnými hodnotami – ASC% (převod znaku na jeho Unicode hodnotu – sic), CHR$ (opak ASC%) a STR$ (opak VAL, převod čísla na řetězec).
Navíc jsou i v EndBASICu dostupné funkce LEFT$, RIGHT$ a MID$ přidané do BASICu původně Microsoftem, které v Microsoftích interpretrech přežily pravděpodobně až do současnosti.
rem ***************************** rem rem Práce s řetězci, manipulace rem s obsahem řetězců. rem rem Úprava pro EndBASIC rem rem ***************************** A$="HELLO WORLD!" print A$ print left$(A$, 5) print right$(A$, 6) print mid$(A$, 7, 5) end
HELLO WORLD! HELLO WORLD! ORLD!
Mimochodem – takto vytvořený řetězec není nutné alokovat pomocí DIM.
12. Data jako součást zdrojového kódu
Tradiční BASICy kromě polí neumožňovaly práci se strukturovanými datovými typy, takže se všechny problémy musely řešit právě jen s využitím polí (a to většinou s omezením na jednorozměrná a dvourozměrná pole). Tato pole bylo možné inicializovat s využitím hodnot (dat), která byla součástí vlastního programu a konkrétně byla uložena na řádcích začínajících příkazem DATA. Přečtení jedné hodnoty (a interní posun na další hodnotu) je realizován příkazem (nikoli funkcí) READ. To ovšem znamená, že tyto hodnoty nebyly pevně spojeny s poli a bylo je například možné ihned vypsat, vykreslit na obrazovku formou pixelu, přehrát jako zvu atd. Jednalo se tedy až o kupodivu velmi flexibilní systém.
Výpis tří hodnot uložených v sekci DATA může být realizován takto:
rem ***************************** rem rem Data jako součást zdrojového rem kódu rem rem Úprava pro EndBASIC rem rem ***************************** for i = 1 to 3 read a$ print a$ next data "FOO", "BAR", "BAZ"
Výsledky:
FOO BAR BAZ
Příkazem RESTORE je možné nastavit interní ukazatel na další prvek znovu na začátek (popř. na zadaný programový řádek), takže lze prvky načítat neustále dokola:
rem ***************************** rem rem Data jako součást zdrojového rem kódu rem rem Úprava pro EndBASIC rem rem ***************************** for j = 1 to 5 restore for i = 1 to 3 read a$ print a$ next next data "FOO", "BAR", "BAZ"
Nyní se budou hodnoty na výstupu opakovat:
FOO BAR BAZ FOO BAR BAZ FOO BAR BAZ FOO BAR BAZ FOO BAR BAZ
13. Strukturovaný příkaz pro rozvětvení
Kromě strukturovaného rozeskoku IF-THEN-END IF obsahuje programovací jazyk EndBASIC i strukturované rozvětvení, které se do jisté míry podobá řídicí struktuře switch známé z céčka. Ovšem realizace podobné konstrukce v EndBASICu nevyžaduje zápis klíčového slova break a navíc se jedná o nepatrně flexibilnější řešení, jak je to ostatně patrné z následujícího příkladu, který detekuje sudé a liché hodnoty v rozsahu 0 až 10 (povšimněte si větve typu „default“) zapisované pomocí case else:
for i = 0 to 10 select case i case 1, 3, 5, 7, 9 print i, "odd" case 2, 4, 6, 8, 10 print i, "even" case else print i, "something else" end select next
Výsledky:
0 something else 1 odd 2 even 3 odd 4 even 5 odd 6 even 7 odd 8 even 9 odd 10 even
Vždy je vybrána pouze jedna větev (resp. maximálně jedna větev), což znamená, že v další variantě se větev case 0 nikdy nevybere, protože hodnota 0 je testována i ve větvi předchozí:
for i = 0 to 10 select case i case 1, 3, 5, 7, 9 print i, "odd" case 0, 2, 4, 6, 8, 10 print i, "even" case 0: print i, "zero" case else print i, "something else" end select next
Výsledky odpovídají popisu:
0 even 1 odd 2 even 3 odd 4 even 5 odd 6 even 7 odd 8 even 9 odd 10 even
Oprava předchozího programu tak, aby se větev case 0 korektně vybrala pro vstupní nulovou hodnotu:
for i = 0 to 10 select case i case 1, 3, 5, 7, 9 print i, "odd" case 2, 4, 6, 8, 10 print i, "even" case 0: print i, "zero" case else print i, "something else" end select next
A takto vypadají výsledky po výše popsané úpravě:
0 zero 1 odd 2 even 3 odd 4 even 5 odd 6 even 7 odd 8 even 9 odd 10 even
14. Kombinace textové a grafické plochy
V EndBASICu je možné, podobně jako u některých dobových osmibitových mikropočítačů, na ploše obrazovky zkombinovat text (zdrojový kód atd.) s grafikou. To vede k tomu, že některé příkazy, například příkaz COLOR ovlivňují jak barvu textu, tak i vykreslovaných grafických entit. Navíc je nutné rozlišovat rozměry obrazovky v pixelech (dvě funkce) a ve znacích (jiné dvě funkce). A konečně, smazání plochy znamená smazání jak textu, tak i grafiky.
Obrázek 4: Podporováno je pouze šestnáct základních barev.
15. Ovládání textové konzole
Pro ovládání textové konzole (to zahrnuje i tvorbu her, menu atd.) postačují pouze čtyři příkazy:
Příkaz | Význam |
---|---|
CLS | vymazání celé obrazovky (i případné grafiky) |
COLOR | nastavení barvy popředí a pozadí |
LOCATE | přenastavení textového kurzoru na zadané souřadnice |
tisk znaku, číselné hodnoty nebo řetězce na zadaných souřadnicích |
Obrázek 5: Kombinace příkazů COLOR a PRINT.
16. Grafický výstup
Pro vykreslení grafických entit a pro ovládání „grafické části“ plochy se používá následujících sedm příkazů a dvojice funkcí:
Příkaz | Stručný popis |
---|---|
GFX_PIXEL | vykreslení pixelu |
GFX_LINE | vykreslení úsečky |
GFX_CIRCLE | vykreslení kružnice |
GFX_CIRCLEF | vykreslení kruhu |
GFX_RECT | vykreslení obrysu osově orientovaného obdélníka |
GFX_RECTF | vykreslení osově orientovaného obdélníka |
GFX_SYNC | zakáže nebo povolí obnovu obsahu plochy po každém grafickém příkazu |
GFX_WIDTH% | šířka grafické plochy v pixelech |
GFX_HEIGHT% | výška grafické plochy v pixelech |
Význam většiny těchto příkazů a funkcí je pravděpodobně zřejmý, ovšem zastavme se u GFX_SYNC. Ten umožňuje pozastavit vykreslování na obrazovku – vše se bude vykreslovat na pozadí pokud se zavolá GFX_SYNC FALSE (a to včetně vstupního textu!). Posléze se každým dalším voláním GFX_SYNC obrazovka překreslí. Tímto způsobem je možné do určité míry nahradit double buffering a mj. tím dosáhnout i urychlení vykreslování. Výchozí chování, tj. okamžité překreslení obrazovky, se dosáhne příkazem GFX_SYNC TRUE.
17. Ukázky grafického výstupu
Vykreslení několika úseček barvami s indexem 0 až 15 (více barev není podporováno!):
rem ***************************** rem rem Nastavení barvy a použití rem příkazu pro kresbu úsečky. rem rem Úprava pro EndBASIC rem rem ***************************** for i=0 to 80 color i mod 16 gfx_line i * 5, 0, 0, 400-i*5 gfx_line 640 - i * 5, 0, 639, 400-i*5 next
Obrázek 6: Kombinace textu a grafiky v grafické konzoli.
V dalším příkladu je realizováno vykreslení vzorku (nejedná se však o fraktál) na obrazovku. Stále jsme omezeni pouze na patnáct základních barev a postarší verze EndBASICu nepodporovaly operátor MOD, takže bylo nutné jeho chování zapsat smyčkou:
rem ***************************** rem rem Circle moire rem v grafickém režimu (16 barev) rem rem Úprava pro EndBASIC (bez MOD) rem rem ***************************** for y = 0 to 400 for x = 0 to 400 c = int(x*x/20+y*y/20) while c > 15 c = c - 16 wend color c gfx_pixel x, y next next
Výsledek:
Obrázek 7: Moiré vykreslené předchozím příkladem.
Nová verze EndBASICu již operátor MOD podporuje, takže lze vše zapsat v lidštější podobě:
rem ***************************** rem rem Circle moire rem v grafickém režimu (16 barev) rem rem Úprava pro EndBASIC (s MOD) rem rem ***************************** for y = 0 to 400 for x = 0 to 400 c = int(x*x/20+y*y/20) c = c MOD 16 color c gfx_pixel x, y next next
18. Závěrečné zhodnocení
I přes některé zajímavé vlastnosti EndBASICu v tomto jazyku nalezneme i mnohá inherentní omezení, která prakticky znemožňují jeho použití pro větší projekty. Samotný návrh jazyka znemožňuje jeho použití pro složitější programový kód, a to zejména z těchto důvodů:
- Nelze vytvářet uživatelsky definované datové typy
- Program nelze strukturovat do menších celků – procedur a funkcí
- Podobně neexistuje možnost specifikace modulů, ideálně s vlastním jmenným prostorem
- Není řešena viditelnost proměnných, takže i teoretické strukturování do podprogramů má své zásadní limity
- Velmi omezeny jsou taktéž možnosti rekurze – sice jsou řešitelné, ale programově, nikoli na úrovni jazyka
- V neposlední řadě je interpretace programů velmi pomalá, možná na úrovni BASICů běžících na HW s 1000× menším výkonem
Pokud si ovšem chcete vyzkoušet, jak se zhruba programovalo v dobách BASICů a osmibitových mikropočítačů, není EndBASIC špatnou volbou, i když podle mého názoru zajímavější a více „stylové“ je využití PC-BASICu, se kterým jsme se seznámili v tomto článku (tam si totiž můžete prakticky vyzkoušet i příkazy POKE a PEEK).
19. Repositář s demonstračními příklady
Všechny dnes popsané demonstrační příklady určené pro spuštění v EndBASICu byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/8bit-fame. Příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý (dnes již poměrně rozsáhlý) repositář:
20. Odkazy na Internetu
- BASIC oslavil 60 let (zprávička)
https://www.root.cz/zpravicky/basic-oslavil-60-let/ - Online IDE s interpretrem BASICu
https://8bitworkshop.com/v3.11.0/ - gotBASIC.com
https://gotbasic.com/atari.html - Informace o GW-Basicu
https://gw-basic.com/ - Stránka s živým EndBASICem
https://repl.endbasic.dev/ - EndBASIC: BASIC interpreter + DOS environment, reimagined
https://www.endbasic.dev/ - GWBASIC User's Manual
http://www.antonis.de/qbebooks/gwbasman/index2.html - GFA-BASIC
http://sites.google.com/site/gfabasic16/ - E-mail od tvůrce GW-Basicu
http://www.classiccmp.org/pipermail/cctech/2005-April/042999.html - General Electric GE-400
http://www.feb-patrimoine.com/PROJET/ge400/ge-400.htm - GE-400 Time-sharing information systems:
http://www.computerhistory.org/collections/accession/102646147 - A brief history of the development of BASIC (pravděpodobně již nefunkční odkaz)
http://www.phys.uu.nl/~bergmann/history.html - History of BASIC (PDF)
http://www.q7basic.org/History%20of%20BASIC.pdf - Dartmouth College Computation Center. 1964.-The original Dartmouth BASIC manual
http://www.bitsavers.org/pdf/dartmouth/BASIC_Oct64.pdf - The Original BASIC
http://www.truebasic.com/ - BASIC – Beginners All-purpose Symbolic Instruction Code
http://hopl.murdoch.edu.au/showlanguage.prx?exp=176 - The History of the Mainframe Computer
http://www.vikingwaters.com/htmlpages/MFHistory.htm - Dartmouth Time Sharing System
http://en.wikipedia.org/wiki/Dartmouth_Time_Sharing_System - General Electric (Wikipedia)
http://en.wikipedia.org/wiki/General_Electric - GE 225 vs. IBM 1401
http://ed-thelen.org/GE225-IBM1401.html - A GE-225 is found
http://ed-thelen.org/comp-hist/GE225.html - G.E. 200 Series Computers
http://www.smecc.org/g_e__200_series_computers.htm - DTSS – Dartmouth Time Sharing System
http://dtss.dartmouth.edu/index.php - John G. Kemeny: BASIC and DTSS: Everyone a Programmer
http://dtss.dartmouth.edu/everyoneaprogrammer.php - GE-200 series (Wikipedia)
http://en.wikipedia.org/wiki/GE-200_series - GE-400 series (Wikipedia)
http://en.wikipedia.org/wiki/GE-400_series - GE-600 series (Wikipedia)
http://en.wikipedia.org/wiki/GE-600_series - ZX Basic Manual
http://www.worldofspectrum.org/ZXBasicManual/ - ZX81 BASIC Programming
http://www.worldofspectrum.org/ZX81BasicProgramming/ - Sinclair BASIC History
http://scratchpad.wikia.com/wiki/Sinclair_BASIC_History - Sinclair BASIC (Wikipedia CZ)
http://cs.wikipedia.org/wiki/Sinclair_BASIC - Sinclair BASIC (Wikipedia EN)
http://en.wikipedia.org/wiki/Sinclair_BASIC - Beta BASIC (Wikipedia EN)
http://en.wikipedia.org/wiki/Beta_BASIC - Beta BASIC (Wikipedia CZ)
http://cs.wikipedia.org/wiki/Beta_BASIC - BETA BASIC NEWSLETTER No 8
http://spectrum128.ru/help/BetaBasicNewsletter8.pdf - R. T. RUSSELL: The home of BBC BASIC
http://www.rtrussell.co.uk/ - R. T. RUSSELL: A History of BBC BASIC
http://www.cix.co.uk/~rrussell/bbcbasic/history.html - SuperBASIC (Wikipedia EN)
http://en.wikipedia.org/wiki/SuperBASIC - SuperBASIC (Wikipedia CZ)
http://en.wikipedia.org/wiki/SuperBASIC - Laser Basic/Laser Compiler
http://www.sincuser.f9.co.uk/049/laser.htm - Laser BASIC (Wikipedia CZ)
http://cs.wikipedia.org/wiki/Laser_BASIC - BBC BASIC
http://www.bbcbasic.co.uk/bbcbasic.html - BBC BASIC
http://mdfs.net/Software/BBCBasic/ - BBC BASIC (Z80) for the ZX Spectrum
http://mdfs.net/Software/BBCBasic/Spectrum/ - BBC BASIC (Wikipedia CZ)
http://en.wikipedia.org/wiki/BBC_BASIC - BeebWiki – 8-bit Acorn Computer Wiky
http://beebwiki.jonripley.com/Main_Page - Porovnání osmibitů
http://porovnani8bitu.spaces.live.com/ - Rosetta Code – Main Page
http://rosettacode.org/wiki/Main_Page - Rosetta Code – Category Basic
http://rosettacode.org/wiki/Category:BASIC - QBasicJedi
http://www.freewebs.com/qbasicjedi/ - QBasic/QuickBasic Downloads
http://www.freewebs.com/qbasicjedi/qbdownloads.html - QuickBASIC (Wikipedia CZ)
http://cs.wikipedia.org/wiki/QuickBASIC - QBasic.com
http://www.qbasic.com/ - QBasic (Wikipedia)
http://cs.wikipedia.org/wiki/QBasic - Dialling with QBASIC
http://www.mysundial.ca/tsp/qbasic.html - BASIC (Wikipedia EN)
http://en.wikipedia.org/wiki/BASIC - BASIC (Wikipedia CZ)
http://cs.wikipedia.org/wiki/BASIC - Turbo BASIC (Wikipedia CZ)
http://cs.wikipedia.org/wiki/Turbo_BASIC - More BASIC Computer Games
http://www.atariarchives.org/morebasicgames/ - How to build an interpreter in Java, Part 1: The BASICs
http://www.javaworld.com/jw-05–1997/jw-05-indepth.html - Apple I
http://applemuseum.bott.org/sections/computers/a1.html - The Apple 1 history
http://apple2history.org/history/ah02.html - The Apple 2 history
http://apple2history.org/history/ah03.html - INTEGER BASIC Reference
http://www.landsnail.com/a2ref2.htm - APPLESOFT Reference
http://www.landsnail.com/a2ref.htm - Apple II Programming
http://home.swbell.net/rubywand/csa2pfaq.html - Applesoft Lite: Applesoft BASIC for the Replica-1
http://cowgod.org/replica1/applesoft/ - Simons' BASIC
http://en.wikipedia.org/wiki/Simons'_BASIC - Simon's Basic
http://www.lemon64.com/?mainurl=http%3A//www.lemon64.com/museum/list.php%3Flineoffset%3D54%26genre%3Dmanualmisc - BASIC
http://www.c64-wiki.com/index.php/BASIC - C64 Wiki: Simons Basic
http://www.c64-wiki.de/index.php/Simons_Basic - Simons' Basic (evaluation)
http://www.atarimagazines.com/creative/v9n11/60_Simons_Basic.php - Bill Gates' Personal Easter Eggs in 8 Bit BASIChttp://www.pagetable.com/?p=43
- Kladivo na programy (je tam třeba popsán způsob nahrávání a ochran programů protí kopírování a crackování)
http://www.grandjihlava.cz/tmp/kladivo.pdf - Didaktik Gama návod k obsluze
http://www.grandjihlava.cz/tmp/navod.pdf - Můj přítel Didaktik Gama
http://www.grandjihlava.cz/tmp/priteldidaktik.pdf - Tip & trip pro Didaktik
http://www.grandjihlava.cz/tmp/tiptrikdidaktik.pdf - Muzeum československých mikropočítačů
http://cs-pocitace.ic.cz/ - Popis modulů a aplikací pro IQ-151
http://cs.felk.cvut.cz/~bily/iq151/ - IQ 151
http://osmi.tarbik.com/cssr/iq151.html - „Domácí počítače“ nedávné minulosti
http://www.fi.muni.cz/usr/jkucera/pv109/xkrejcir.htm - ZPA: IQ-151
http://www.homecomputer.de/pages/easteurope_cz.html#iq151 - Zrození IQ-151
http://www.iq151.net/index.htm - Stručná historie počítače IQ150/IQ151
http://www.iq151.net/history.htm - Old Computers – IQ 151
http://www.old-computers.com/MUSEUM/computer.asp?st=1&c=1045 - Wikipedia EN: IQ 151
http://en.wikipedia.org/wiki/IQ151 - Wikipedia CZ: IQ 151
http://cs.wikipedia.org/wiki/IQ151 - JtyOne Online ZX81 Emulator
http://www.zx81stuff.org.uk/zx81/jtyone.html - OpenSE BASIC
https://zxdesign.itch.io/opense