Basic pro Tesla Ondra SPO-186: (3) Pokračování

Pokračoval jsem v tvorbě Basicu pro Ondru.

Štval mne CAPS LOCK, který byl potřeba při editaci programu.
Ondra nemá službu, která by jej nějak aktivovala, a sahat nesystémově do systémových proměnných se mi nechtělo, musel se tedy po startu Basicu zapnout ručně.
Myslím, že stejný problém má i Editor/Assembler od Microsoftu a snad i HiSoft Pascal.
Tak jsem nutnost CAPS LOCKu dostranil nejdřív z příkazů a funkcí (PRINT, print a PrInT jsou jeden příkaz), z názvů proměnných a nakonec z povelu MUSIC (který tehdy už existoval).

K CAPS LOCKu jsem se, ač jsem s tím původně nepočítal, musel později vrátit.
Objevila se totiž chyba ve zpracování IF.

Přišel jsem na ni poté, co jsem dodělal operátory AND, OR, XOR a chtěl jsem je otestovat.
Myslel jsem si tedy, že jsem ji tam zavlekl s těmito operátory.

V poznámkách jsem měl zapsáno:
“V příkazu IF chybu hodí, když je podmínka splněna – sice se správně vyhodnotí, ale skok na zpracování zbytku řádu selže a doskáče do pryč (příkaz SAVE taky – asi zůstává nějaký bordel na zásobníku).
Když podmínka v IF není splněna, není problém a program běží (konec řádku se bez problému vynechá).”

A později:
“Chtěl jsem dodat operátory AND, OR a XOR (hlavně aby bylo možné psát IF (A>1)AND(B<5)), ale bohužel při tom úplně přestalo fungovat porovnávání (<, >, <=, …). Zatím netuším, proč. Všechno vypadá v pořádku.
 (Už mne napadlo, v čem to zase vězí – jako obvykle tam žádná není a počítač dělá přesně to, co po něm chci, debil mezi klávesnicí a židlí, zvláštní je, že to musí nefungovat už hodně dlouho, co jsem tam dal tu možnost používat i malá písmena, a všiml jsem si toho až teď.)

Chyby v IF a SAVE byly, jak se ukázalo, spolu nesouvisející, a se zásobníkem rovněž nesouvisející.
Opravdu obě (či všechny tři, protože chybky v IF byly možná dvě, ale to už si nepamatuji, ta jedna spočívala tuším ve smazaném skoku při kladném vyhodnocení podmínky jako pravdivé) spočívaly v tom, že programátor je debil.

Inu, znaky příkazu se konvertovaly na velká písmena pomocí AND 0DFH.
Fungovalo to dobře, až na tu drobnost, že operátory pro porovnání (<, =, >=, …) se dekódují stejně jako příkazy, z podobné tabulky a stejným podprogramem.
Takže, při pohledu na ASCII tabulku vidíme, že se najednou pohybujeme v oblasti netisknutelných znaků a počítač najednou povel porovnání nezná.

Po opravě (znaky menší než zavináč se na CAPS LOCK neupravují) začalo porovnávání chodit.

Díky tomu, že pole se shodou okolností jmenuje zavináč a zbytek proměnných je označen písmeny, nebylo potřeba korekci CAPS LOCKu ve zpracování jmen proměnných (kam jsem ho předtím taky doplnil) měnit, zatímco v příkazu MUSIC bylo potřeba pohlídat znak #.

Povely se dají zkracovat, například PR. jako PRINT.
No a když se zkrátí na úplné maximum? Ještě méně než jedno písmeno a tečka?
Nápad dát PRINT na čelní místo v obou (přímý prováděcí i program) režimech byl můj nápad, když jsem chtěl něco jako povel ? v Microsoft Basicu, ale otazník se mi líbil míň, než zápis .” TEXT” ve Forthu – a ta tečka se tedy sama nabídla.
Tím vznikl extra krátký (a svý dekódováním rychlý) a užitečný příkaz.

Když už jsem PRINT upravoval, přidal jsem možnost vypisovat netisknutelné znaky zadáním jejich kódu (a přidal opačnou, i když trochu zbytečnou, funkci vracející ASCII kód znaku).
Upravil jsem trochu formát výpisu podle ZX Spectra a zavedl povel AT pro nastavení pozice kurzoru.

Zavedl jsem nepovinné THEN (předtím nebylo vůbec, ale já ho občas automaticky napsal a pak mi to házelo error, to mne štvalo).
LET je nepovinné, ale zápis s LET se dekóduje rychleji (přiřazení bez zapsaného LET musí projet nejdříve dekódování všech klíčových slov příkazů).
Původně jsem se bál, že to nebude jednoduché, ale když se to udělá dostatečně blbě, tak to jednoduché je.

Klasické REM jsem doplnil možností zápisu poznámky mezi hranaté závorky (přesněji – stačí ta levá).

Dále je na řadě grafika a sprity – to jsou příkazy, které jsou asi nejvíc “vidět” a na kterých se dá něco předvádět.
Nejprve přišel PLOT a UNPLOT, ale nezůstalo to tak.
Jak opět uvádí zápisky:
“Uvažuju o formách DRAW X1,Y1,X2,Y2 (od, do) a RDRAW X,Y (“Relative” DRAW), kreslící z posledního použitého bodu (případně LINE a RLINE, případně LINE a LINE_R jako na QL). Snad půjde předělat i kreslení kružnice a vyplňovaného kruhu (zatím nemám vymyšlené názvy příkazů, aby nebyly zavádějící, ale intuitivní).
Možná ještě zruším PLOT a UNPLOT, udělám jen jeden PLOT a sadu příkazů, kterými by se nastavovalo, zda se bude (všemi grafickými příkazy – PLOT, LINE) kreslit nebo mazat (PENUP a PENDOWN znějí dobře, ale zrovna PENUP není intuitivní – znělo by to, že se “nebude kreslit”, a nevyplynulo by dost jasně, že se bude mazat. Další možnosti jsou PEN a ERASER nebo PEN 0, PEN 1).”

Jak to dopadlo, dnes už víme – vznikly LINE a RLINE a mazání bylo vyřešeno povelem PEN, který umí nejen kreslit, mazat a xorovat, ale i ditherovat jednoduchým vzorkem (a tím nahradit nedostatek barev na Ondrovi).
Dithering (PEN 3) se v současné verzi s původním obsahem obrazovky ANDuje (jako PEN 1), ale možná to ještě změním (že se “nenakreslený bod” bude opravdu mazat).

Kružnice jsem, vzhledem k tomu, že dostupné rutiny pro ZX Spectrum počítají s jedním 8bitovým registrem pro každou osu, ale Ondra má 320 (tedy více než 256) bodů horizontálně, z implementace vynechal.

S grafickými a hudebními rutinami jsem starost neměl, protože už existuje knihovna OndraGDK, kterou stačilo jen mírně upravit a obalit příslušnými příkazy Basicu.

Podívejme se na další poznámky:
“Otázka je, jak naložit s daty spritů – původní nápad byl, že se sprite nakreslí pomocí PLOT a DRAW, “otiskne se” do pole, odkud se pak bude vykreslovat kreslicí rutinou.
Druhý nápad byl použít nějaký příkaz DATA, za kterým by byla grafická data spritu zadána a rovnou by se odtud čerpala (a konvertovala) pro kreslicí rutinu, nebo se “nějak” z numerické formy zkonvertovala do binární buď na místě, nebo do pole (a kreslila se pak bez konverze).
Upřímně, kreslicí rutiny a příkazy samotné budou jednoduché, ale vymyslet, jak a kam sprity ukládat, je věc, kterou jsem ještě nějak nerozebíral.”

“Spritová grafika.
To je vlastně jen další OndraGDK.
Fór je jen v tom, jak spritovou grafiku definovat.
Měla by být možnost nakreslit postavičku pomocí PRINT, PLOT a DRAW a sejmout ji z obrazovky, druhá věc by měl být povel pro “POKE hexadecimálního řetězce do paměti” (CODE adresa,”FF5A17…”), aby se nemusela dohrávat binárka grafických dat do paměti z kazeťáku.
(Otázka je, zda bude potřeba i opak – převod kusu paměti na hexa řetězec.)

Výsledkem je, jak již řečeno, povel CODE:
“Grafická data jsem nakonec vymyslel (aby se nemusela dohrávat nějaká binárka s grafickými daty, což na OndraSD stejně nejde) jako příkaz, který do paměti napoukuje hexadecimální kód (CODE adresa,”FFA0154A … “).”
A příkaz CODE tak opravdu vypadá a funguje.

A když už jsem v tom vrtání se s daty v paměti byl, doplnil jsem příkazy WPEEK a WPOKE.

V další poznámce se píše:
“Děti byly nemocné, navíc měly svátek a narozeniny, a otravovaly, tak jsem prakticky nic neudělal.
Jenom jsem doplnil WPOKE a WPEEK (W jako word, kombinovaná inspirace z QL a SAMa, POKE_W mi na Ondru nesedělo a u DPOKE mi zas nesedělo to D), když už jsem dělal ten povel CODE.

CODE na zadanou adresu uloží data zadaná jako hexadecimální řetězec. Aby bylo možné často opakovaná data vkládat do substringů a zadat je v nepřetržité řadě (pro vkládání dat grafiky pro sprity to bude asi užitečné), dá se zadat
10 A="FF00":B="AA"
20 CODE 16384,$A,$A,$A,$A,$A,$B,"154A5B"

S tím souvisí úprava povelů SAY a MUSIC, kde to teď jde taky:
10 A="CDE"
20 MUSIC "T4",$A,$A,"F",$A

“.

Další poznámka je starší, ještě z doby, než vznikly operátory AND, OR, XOR:
“Teď promýšlím příkazy pro čtení joysticku. Chci, aby to bylo v Basicu dobře zpracovatelné, takže můj nápad je následující:
Povel JOY načte aktuální stav joysticku a klávesnice (jen šipky a SPACE). Narozdíl od OndraGDK, která vrací úplné šílenosti (v případě klávesnice jsou směry uspořádané jinak, než na joysticku, a neumí vracet stav celé klávesnice) načtu všechny 4 řady (joystick, první část šipek, druhou část šipek, mezeru) a přechroupu tak, aby směry odpovídaly směrům na joysticku.
Příkaz nic nevrací, hodnoty si uloží.

5 funkcí bez parametru (podobně, jako je funkce SIZE): UP, DOWN, LEFT, RIGHT, FIRE. Bude TRUE, pokud v okamžiku čtení povelem JOY byl daný směr stištěn.
Takže v praxi:
10 JOY
20 IF UP: REM nahoru (UP=1)
30 IF DOWN: REM dolu
40 IF FIRE: REM strelba

Chtěl bych dodělat ještě logické operátory (AND, OR, možná XOR), aby šlo i
IF UP AND RIGHT: REM šikmo vpravo nahoru".
Další poznámka o joysticku je z pozdější doby, kdy už AND, OR a XOR existovalo:
“Čtení klávesnice a joysticku.
Mělo by to fungovat takto – povel JOY načte stav klávesnice a joysticku v daném okamžiku (bude-li se stav měnit během vyhodnocování, je to už jedno).
Jednotlivé směry (ať na klávesnici, nebo joysticku) načíst pomocí funkcí UP, DOWN, LEFT, RIGHT, FIRE
10 JOY
20 IF UP THEN GOTO 250: REM nahoru
30 IF (UP)AND(RIGHT) THEN GOTO 300: REM šikmo
“.
Jak je vidět, je zapracování aritmetických operátorů jednodušší, než hrátky s joystickem, protože ten na realizaci zatím čeká.

Pokračovalo ale i vychytávání chyb.

Čím byla způsobena chyba v příkazech LOAD, SAVE a INPUT?
Zapomněl jsem, že pro vstup řádky používám kazeťákový buffer. A data pro interpretaci vloženého řádku tam nechávám.
Takže když se v režimu přímého provádění zadalo SAVE, buffer se přemázl daty ukládaných sektorů a po návratu už nebylo možné zjistit, jestli za SAVE následuje další příkaz nebo konec řádku, protože tam byly nesmysly.
Stejně tak při přímém provádění INPUT A,B,C se při vložení proměnné A buffer s příkazem smazal a nebylo možné zjistit, že se má vkládat ještě proměnná B a C (respektive podle délky se to občas přepsalo, občas ne).
Při vykonávání INPUT v programu bylo vše v pořádku, protože vykonávaný řádek není v bufferu a nepřemaže se.

Tak jsem měl dvě možnosti – buď prováděný příkaz vykopírovat z bufferu někam ven (zabrat další paměť a tak dále), nebo testovat, zda jsem v přímém provádění, nebo mám spuštěný program pomocí RUN, a v prvním případě provést INPUT jen jedné proměnné (a zbytek řádku zahodit, takže v přímém provádění nebude fungovat ani INPUT A: PRINT A, protože PRINT se vůbec neprovede).

Zatím jsem udělal tu druhou.
A časem to možná předělám na tu první.
Možná.

Pokračování příště…