clanky/basic-zx-spectra

BASIC ZX SPECTRA

Autor: Daniel Novotný

V tomto dokumentu bych rád popsal interní Basic počítače Sinclair ZX Spectrum.

Basic je programovací jazyk určený zejména pro začátečníky. Jeho jméno je údajně zkratkou z "Begginer's All-purpose Symbolic Instruction Code", ale toto je prý backronym, což znamená, že to, že je název zkratkou bylo vymyšleno až dodatečně. Původní význam jména jazyka je prostě anglické slovo "basic", znamenající "elementární, základní". Nyní se jazyk používá zejména v prostředí Windows, například jako makrojazyk kancelářského balíku MS Office.

Sinclair ZX Spectrum je osmibitový domácí počítač z osmdesátých let, řízený procesorem Z80, disponující 48kB RAM a 16kB ROM. Právě ROM tohoto počítače obsahuje interpret Basicu, o kterém budu hovořit.

Basic na Spectru plní dvě, částečně se prolínající, funkce. Jednak je programovacím jazykem a jednak funguje jako řídící jazyk "operačního systému". Pokud před příkaz napíšeme číslo řádku, bere se jako součást programu, v opačném případě se příkaz spustí ihned - proto Basic Spectra obsahuje příkazy jako "nahraj program z kazety", "spusť program", "zresetuj paměť" a podobně.

Aby se domácí počítač nemusel příliš zatěžovat lexikální analýzou, nejsou příkazy jazyka složeny z jednotlivých znaků, ale jsou kódovány zvlášť (v horních pozicích 128-255, které původní ASCII kód nedefinuje) a vypisují se stisknutím jediné klávesy, případně stiskem přeřaďovače (Symbol shift, Extended mode) a klávesy. Příkazy je nutno zapisovat tímto způsobem, příkaz vypsaný jednotlivými písmeny systém nepřijme. Například příkaz PRINT je vlastně znak s kódem 245 a vypíše se klávesou "P" stisknutou v kontextu zadávání příkazu.

Obecně je vždy řádek programu ve tvaru <číslo řádku> <příkaz> <parametry>. Číslo řádku je v tomto dialektu BASICu povinné, doporučuje se nečíslovat řádky 1,2,3... ale po pěti (5,10,15...) či po deseti - a to z toho důvodu, abyste mohli mezi dva programové řádky vložit řádek další bez nutnosti přečíslování celého programu. Parametry příkazu se většinou oddělují čárkami, ale ne vždy: některé příkazy mají složitější syntaxi, například PRINT nebo FOR.

Základní datové typy jsou pouze dva: číslo a řetězec.
Čísla jsou všechna v pohyblivé řádové čárce, proměnné se nerozlišují na celočíselné a reálné. Číselné konstanty se zapisují tak, jak je zvykem ve většině programovacích jazyků, je možno použít i vědeckou notaci - např. 2.5e3 je 2500. Na čísla se dají aplikovat běžné aritmetické operace +,-,*,/ , šipka nahoru slouží pro mocninu, další aritmetické funkce viz níže.
Řetězce se zapisují do uvozovek, jejich velikost je omezena pouze pamětí počítače. Je možno je spojovat (řetězit) operátorem +, další funkce včetně pro Spectrum unikátního adresování částí řetězce budou uvedeny níže.
Jedinou složitější datovou strukturou je pole - tím se budeme zabývat později.

Proměnné mohou být buď číselné nebo řetězcové. Nemusí (ani nedají) se deklarovat, systém je deklaruje automaticky při jejich prvním použití v přiřazovacím příkazu (LET, INPUT, READ). Pokud se v programu objeví použití proměnné bez této automaticky proběhlé deklarace (tedy např. v příkazu PRINT), vede to k chybě Variable not found.
Jména číselných proměnných se skládají z libovolného počtu čísel a písmen, první musí být písmeno. V tomto Spectrum "boduje" nad jinými "konkurenčními" mikropočítači - umožňuje jako identifikátory proměnných celá slova, zatímco ostatní soudobé srovnatelné systémy nabízely většinou pouze jedno písmeno a číslici.
Naproti tomu jména řetězcových proměnných se skládají pouze z jednoho písmena a znaku $. Tento znak se používá i k označení funkcí vracejících řetězce - viz níže. Z uvedeného vyplývá, že řetězcových proměnných může být v programu maximálně 26.
Na velikosti písmen ve jménech proměnných nezáleží.

Příkazů je velká řada. Postupně si probereme nejdůležitější z nich.

Příkaz PRINT slouží k výstupu na obrazovku. Ukážeme vám v klasickém programu "Hello, world" jeho základní podobu:

10 PRINT "Hello, world!"

Další základní příkazy jsou INPUT pro vstup řetězce/čísla, přiřazovací příkaz LET a příkaz REM, který slouží pro poznámky - počítač jej ignoruje:

10 REM zakladni aritmetika
20 INPUT "Zadej prvni cislo: ";a
30 INPUT "Zadej druhe cislo: ";b
40 LET c=a+b
50 PRINT "Vysledny soucet: ";c

Vidíme, že u příkazu INPUT můžeme použít i jakýsi "prompt" - legendu pro uživatele, aby věděl, co má vlastně zadávat. Položky INPUTu a PRINTu je možno oddělovat středníkem (pak budou na sebe těsně "nalepené"), čárkou (vzdálenost půl řádku) či apostrofem (každá na nový řádek).

Řídící struktury jsou v BASICu následující: podmínkový příkaz IF, cyklus FOR, příkaz GOTO a "podprogramové" příkazy GO SUB a RETURN.
Příkaz GOTO má jako parametr číselný výraz určující číslo řádku, na které počítač skočí. Narozdíl od "konkurenčních" mikropočítačů této doby, které uznávaly pouze tvar GOTO číslo, na Spectru bylo možno použít i GOTO proměnná, nebo třeba GOTO proměnná*100... Díky absenci některých struktur (WHILE cyklu, ELSE...) je tolik diskutovaný a haněný příkaz GOTO nutno používat. To může vést k nepřehlednému programu, "špagetovému kódu".
Příkaz IF má syntaxi IF podmínka THEN příkaz. Podmínka se může skládat z relačních operátorů (větší než, rovno...), případně spojených logickými operátory AND, OR, NOT. Řetězce lze nejen testovat na rovnost, ale i lexikograficky porovnávat. Dvě připomínky: THEN nemůžeme vypsat z klávesnice, ale musíme jej napsat "naráz" jako Symbol shift + G, toto se týká i ostatních "vícesložkových" příkazů, stejně jako operátorů AND, OR, >= atd. A za druhé: v příkazu po THEN můžeme využít znaku :, který slouží k napsání více příkazů za sebou - pro všechny pak bude platit podmínka IF. Příkaz ELSE zde není, ale dá se nahradit právě skládáním příkazů s využitím příkazu GOTO:

100 IF vek>=18 THEN PRINT "Muzu nalit alkohol." : GOTO 120
110 PRINT "Smula, mladej."
120 REM zde program pokracuje

Cyklus FOR je ve tvaru FOR i=číslo1 TO číslo2 STEP krok . Čítač příkazu FOR je číselná proměnná, postupně probíhá hodnoty od čísla1 k číslu2, při každé iteraci se proměnná zvýší o krok. STEP je nepovinný, při neuvedení je rovný jedné. Konec iterace se označuje příkazem NEXT i, kde i je proměnná daného cyklu FOR. Příkládek:

10 FOR i=1 TO 9
20 FOR j=1 TO i
30 PRINT j;
40 NEXT j
50 PRINT
60 NEXT i

Zde vidíme nové vlastnosti příkazu PRINT - pokud končíme středníkem, znamená to "neukončuj řádek", naopak samotný PRINT bez parametrů nám pouze odřádkuje. Basicový způsob "cyklení" je poněkud nerobustní: stačí splést NEXT i a NEXT j a máme sémantickou chybu...

Příkazy GO SUB číslo řádku a RETURN nám umožňují vytvářet podprogramy - GO SUB nám skočí na řádek s daným číslem (které může být rovněž vypočítáno výrazem) a uloží do zásobníku, kam se má program nazpět vrátit. RETURN naopak vybere ze zásobníku číslo řádku, které tam uložilo předchozí GO SUB a skočí na něj. Příklad (nic lepšího mě nenapadlo...):

5 REM promennou "a" pouzijeme jako parametr
10 LET a=3
20 GO SUB 1000
30 LET a=6
40 GO SUB 1000
45 INPUT "Zadej pocet:"; a
50 GO SUB 1000
60 GOTO 2000
900 REM konec hlavniho programu, zacatek podprogramu
1000 FOR i=1 TO a
1010 PRINT "ha ";
1020 NEXT i
1030 PRINT
1040 RETURN
2000 REM konec programu

Dalším přiřazovacím příkazem je READ, používá se spolu s příkazy DATA a RESTORE. Řádek označený DATA obsahuje skladiště dat - čárkou oddělená čísla a řetězce, která tvoří jakýsi "datový soubor", z něhož READ postupně čte a přiřazuje. Příkaz RESTORE obnoví již přečtená DATA tak, že následující READ je čte znovu od začátku. Následující příklad jsem převzal z knížky "Jak se domluvíme s počítačem" - zkuste bez spuštění vyčíst, co vám vypíše:

10 REM albatros
15 FOR p=1 TO 8
20 FOR q=1 TO p
25 READ a$
30 PRINT a$;
35 NEXT q
40 PRINT
42 RESTORE
45 NEXT p
50 FOR m=1 TO 8
55 FOR n=1 TO m
60 READ x$
62 PRINT " ";
65 NEXT n
70 FOR o=m+1 TO 8
75 READ a$
80 PRINT a$;
85 NEXT o
90 PRINT
92 RESTORE
95 NEXT m
100 DATA "A","L","B","A","T","R","O","S"

Pole se alokují příkazem DIM. Mohou být vícerozměrná, název pole je jednoznakový. Indexy se píší v kulatých závorkách, začínají od jedničky - narozdíl např. od jazyka C. Pole řetězců se alokují speciálním způsobem: poslední "rozměr" není rozměr, ale délka jednoho řetězce daného pole. Prvek pole řetězců je pak vždy takhle dlouhý, doplňuje se mezerami nebo ořezává. Toto zacházení s poli řetězců je specialita ZX Spectra. Příklady:

5 REM alokace:
10 DIM a(100)
15 REM prirazeni:
20 LET a(1)=42
25 REM ok, vypise nulu:
30 PRINT a(100)
35 REM hodi chybu - mimo meze:
40 PRINT a(101)
45 REM dtto - nulty prvek neexistuje:
50 PRINT a(0)
55 REM trojrozmerne pole:
60 DIM b(10,10,10)
65 REM pole 6x6 desetiznakovych stringu:
70 DIM x$(6,6,10)
75 REM alokuje retezec fixni delky 7 znaku:
80 DIM f$(7)

Nyní bych zmínil funkce pro práci s čísly a řetězci. Kvůli snaze o relativní stručnost tohoto článku je uvedu tabulkou. Uvádím i počet a typ parametrů. Návratový typ funkce se pozná podle toho, že funkcím vracející řetězec vždy končí název písmenem $.
funkce význam
SIN atrigonometrická funkce (i COS, TAN atd.)
ABS aabsolutní hodnota.
LN a přirozený logaritmus
SQR adruhá odmocnina (pozor! V Pascalu je sqr druhá mocnina a druhá odmocnina je sqrt)
INT acelá část reálného čísla (zaokrouhluje vždy dolů)
SGN a-1 pro záporná čísla, 1 pro kladná, 0 pro nulu
PI číslo pí
RND pseudonáhodné reálné číslo mezi 0 a 1
LEN a$délka řetězce
STR$ akonverze čísla na řetězec obsahující toto číslo
VAL a$konverze čísla/výrazu zapsaného v řetězci na číslo (opak STR$)
CHR$ aznak s ASCII kódem a (jednoznakový řetězec)
CODE a$ASCII kód prvního znaku a$ (opak CHR$)

Na Spectru se nepoužívají ke "krájení" řetězců řetězcové funkce LEFT$, RIGHT$ a MID$ jako v jiných dialektech Basicu. Místo toho se používá indexování: a$(3 TO 5) je třetí až pátý znak a$, a$(8 TO) osmý až poslední, a$(7) pouze sedmý znak atd. Do těchto výrazů lze i přiřazovat. Řetězec se nebude zkracovat ani natahovat - v případě neshodné délky se doplní mezerami respektive se konec přiřazovaného usekne.

Generátor náhodných čísel RND se inicializuje příkazem RANDOMIZE, který vezme "seed" ze systémového časovače. Je také možnost inicializovat sekvenci pevným číslem příkazem RANDOMIZE číslo

Příkladů na funkce jistě spoustu vymyslíte sami - uvedu třeba program realizující jednoduchou kalkulačku, ve které můžete používat právě i funkce Spectrum Basicu - nesmíte je však vypsat po písmenech, ale "vcelku" stiskem příslušného tlačítka. Za řádkem 20 lze implementovat další interní příkazy kalkulačky kromě "quit", zbytek výrazů se prostě předá interpretu Basicu.

5 REM klauzule LINE umozni lepsi vstup retezce - neobjevuji se uvozovky
6 REM obvykle se objevujici po INPUT a$
10 INPUT "kalkulacka>"; LINE a$
20 IF a$ = "quit" THEN GOTO 50
30 PRINT VAL a$
40 GOTO 10
50 REM konec programu

Pomocí příkazu DEF FN lze definovat vlastní uživatelské funkce, s jednopísmenným názvem a libovolným rozumným počtem parametrů. Funkce vracející řetězec končí název znakem $. Například:

10 REM zaokrouhleni na cele
20 DEF FN z(x) = INT (x+0.5)
30 REM maximum dvou cisel
40 DEF FN m(a,b) = ( a+b+ABS(a-b) )/2
50 REM nahodne cislo od nuly do n-1
60 DEF FN r(n) = INT (RND * n)
50 REM funkce "LEFT$" jiných dialektu
60 DEF FN l$(a$,n) = a$( TO n)
70 REM priklad volani takovychto fci
80 LET a = FN r(100)
90 LET a$ = FN l$("foobar",3)

Pro práci s kazetovým magnetofonem slouží příkazy LOAD "název programu", SAVE "název programu". Příkaz LOAD "" načte první nalezený program na kazetě. Existuje též "vychytávka" SAVE "program" LINE . Takto uložený program se po nahrání z kazety automaticky spustí - není třeba zadávat RUN.

Nyní něco napíšu o "multimediálních" možnostech Spectráckých programů, tedy schopnosti dělat něco více než řádkový vstup a výstup.

Za prvé - příkaz PRINT lze doplnit o klauzuli AT označující, na kterou pozici obrazovky se bude tisknout. Opak dělá funkce SCREEN$(x,y), která vrátí znak na souřadnicích (x,y) - a to pouze jestli tam skutečně je znak a ne například jemná grafika - pak vrátí prázdný řetězec. Pěkný příklad na AT, CHR$ a RND:

3 RANDOMIZE
5  REM nahodny zobrazitelny znak
10 LET a$ = CHR$ (32 + INT ( RND * (128-32) ) )
15 REM nahoda v rozmezich obrazovky
20 LET x = INT ( RND * 22 )
30 LET y = INT ( RND * 32 )
35 REM vypis to
40 PRINT AT x,y ; a$
50 GOTO 10

Za druhé - jemná grafika se kreslí příkazy PLOT, DRAW a CIRCLE. PLOT x,y nakreslí bod o souřadnicích (x,y) . DRAW x,y nakreslí úsečku od minulého PLOTu či DRAWu na nové souřadnice, posunuté o x a y - souřadnice DRAW se totiž nepočítají absolutně vzhledem k bodu (0,0) obrazovky, ale relativně vzhledem k bodu kam se naposledy kreslilo - mohou tedy být i záporné. Následující příklad (takový "šetřič obrazovky") proto musí absolutní souřadnice přepočítávat na relativní. Borland Pascal toto řeší o něco lépe - má dvě funkce line a lineRel, takže se každý může rozhodnout zda kreslit čáru "absolutně" či "relativně".

5 RANDOMIZE
6 REM pocatecni bod
10 LET x1=INT(RND*256)
20 LET y1=INT(RND*176)
25 REM konecny bod
30 LET x2=INT(RND*256)
40 LET y2=INT(RND*176)
45 REM uprava absolutnich souradnic na relativni
50 LET x2=x2-x1
60 LET y2=y2-y1
70 PLOT x1,y1
80 DRAW x2,y2
90 GOTO 10
Nyní vám doplním znalosti k tomu, abych tu mohl uvést jeden z vůbec prvních programů, které jsem na Spectru kdysi coby dítě školou povinné napsal. CLS maže obrazovku, PAPER a INK určují barvičky "podkladu" a písma/kresby, CIRCLE je kružnice. V programu obratně využívám zaokrouhlovacích chyb při kreslení kružnice. "Animace" znázorňuje hvězdnou noc a přílet UFO.

10 PAPER 0 : INK 7 : CLS
15 REM hvezdy:
20 FOR i=1 TO 50
30 PLOT INT (RND*256), INT (RND*176)
40 NEXT i
45 REM ufo:
50 INK 3
60 FOR i=1 TO 40
70 CIRCLE 128,88,i
80 NEXT i

Bohužel barvy se neváží k jednotlivým pixelům, ale k oblasti 8x8 pixelů (velikost jednoho znaku), což si můžeme ověřit následujícím jednoduchým prográmkem:

10 INK INT (RND*8)
20 PLOT INT (RND*256), INT (RND*176)
30 GOTO 10

Další příkazy a funkce využitelné např. v počítačových hrách jsou tyto:

  • Příkaz PAUSE čeká stanovený počet snímků obrazovky (PAUSE 50 ... jedna sekunda). PAUSE 0 čeká na stisk libovolné klávesy
  • Funkce INKEY$ vrací právě stištěnou klávesu. Pokud žádná stištěná není, nečeká a vrací prázdný řetězec.
  • Příkaz BEEP x,y pípá. x je výška tónu, y jeho délka.

Na závěr: pokud vám nestačí možnosti Basicu, můžete využít funkci PEEK adresa pro čtení z paměti, příkaz POKE adresa,bajt pro zápis do paměti a analogickou funkci IN a příkaz OUT pro práci s porty. Pokud se vám povede do paměti "nacpat" program ve strojovém kódu, spustíte ho GOTO USR (adresa). Ale tyto věci jsou již nad rámec tohoto článku a Basicu vůbec.

To je ode mě všechno, doufám, že si nyní se svými emulátory užijete nejenom hraní her, ale i trochu toho spectráckého programování...

Použitá literatura:

  1. Sinclair Research Ltd. : Sinclair ZX Spectrum - BASIC programming
  2. Jiří Franěk: Jak se domluvíme s počítačem

Stránka byla už zobrazena: 165 ×
  Aktualizováno: 2. 5. 2018, 18:13



     psp-ikona.gif, 2 kB    Text to HTML converter and formatter  builder_ikonka.gif, 3 kB
           
  Best Old Games      kabelmanie.gif, 2 kB  root_ico.gif, 2 kB  český diskmag pro počítače ZX Spectrum a kompatibilní
synapse.gif, 4 kB   TuningPC.cz - Vše pro tuning a casemodding vašeho PC        pspad88x31.gif  delphix.gif, 1 kB
sgatlantis_88x31_3.jpg, 4 kB  atlan.gif, 22 kB  arkon.jpg, 12 kB  ico_abeceda.gif, 888B  blog.idnes.cz   
           
       
 
  torry_logo.gif, 4 kB  

Stránka načtena za 0.00185 sekund.