Pro domácí osmibitové mikropočítače Atari bylo v době jejich největší slávy nabízeno relativně velké množství různých interpretrů programovacího jazyka BASIC (které navíc nebyly mezi sebou zcela kompatibilní). Tímto tématem jsme se již zabývali, takže si dnes uvedeme jen krátké shrnutí.
Co se dozvíte v článku
- Základní vlastnosti Fast Basicu
- Překladač Fast Basicu provozovatelný na PC
- Fast Basic prakticky: překlad a spuštění programu typu „Hello, world!“
- Program končící v nekonečné smyčce
- Fáze překladu
- Realizace výpočtu faktoriálu
- Dvě varianty výpočtu faktoriálu ve Fast BASICu: celočíselné nebo FP operace?
- Podpora pro práci s grafikou
- Operace fillto
- Podpora pro práci se sprity (PMG)
- Zobrazení hráče (PMG)
- Realizace horizontálního i vertikálního pohybu hráče
- Jak by stejný program napsal znalec BASICu?
- Strukturované programování ve Fast Basicu
- Peephole optimalizace prováděné překladačem na PC
- Ukázky peephole optimalizací
- Zhodnocení
- Repositář s demonstračními příklady
- Odkazy na Internetu
Obrázek 1: Asi nejznámější obrazovka osmibitových mikropočítačů Atari: inicializovaný interpretr programovacího jazyka Atari BASIC.
V první řadě se pochopitelně jedná o originální Atari BASIC, který byl součástí standardní ROM (8kB BASIC+16kB operační systém). V roce 1983 byl společností OSS (tu již dobře známe, vlastně na ni v oblasti vývojových nástrojů narážíme neustále) vydán dialekt BASICu pojmenovaný BASIC A+.
O další rok později vydala společnost OSS interpret programovacího jazyka BASIC, který byl pojmenován BASIC XL, a to podle toho, že běžel na systémech Atari XL (s plnou RAM) a pochopitelně taktéž na Atari XE. Opět se jednalo o poměrně významné vylepšení a rozšíření možností původního BASICu, k dispozici byly i metody k urychlení výsledných (interpretovaných) programů.
Obrázek 2: Úvodní obrazovka interpretru BASIC XE.
V roce 1985 společnost OSS vydala BASIC XE, a to z toho důvodu, že byl podporován přístup do rozšířené paměti počítačů Atari 130 XE (celkem neuvěřitelných 128 kB RAM) – program bylo možné oddělit do dat, přičemž program byl umístěn ve druhé bance RAM. BASIC XE nabízel i rychlejší matematické operace a oproti BASICu XL nabízel i několik dalších vylepšení (ovšem ne mnoho).
Obrázek 3: Nabídka nástrojů nabízených na disketě s Turbo BASICem XL.
Dalším interpretrem programovacího jazyka BASIC je Atari Microsoft BASIC. Z několika pohledů se však jedná o dosti rozdílný interpret, než je tomu v (ideové) řadě Atari BASIC → BASIC A+ → BASIC XL → BASIC XE → Turbo BASIC. O rok později po vydání původního Atari Microsoft BASICu (1982) byla vydána jeho druhá verze s několika opravami a taktéž s několika novými příkazy..
Obrázek 4: Inicializace interpretru Turbo BASICu XL.
Zapomenout nesmíme na slavný Turbo BASIC XL (či jen Turbo BASIC), jehož autorem je známý Frank Ostrowski (tento programátor později vytvořil GFA BASIC pro osobní počítače Atari ST). Turbo BASIC téměř splňoval všechny sny BASICových programátorů – byl rychlejší než původní Atari BASIC, byl s Atari BASICem plně kompatibilní, díky programovému „odklápění“ ROM nabízel vývojářům prakticky stejné nebo i větší množství volné operační paměti (což působilo téměř magicky) a navíc Turbo BASIC obsahoval poměrně velké množství nových příkazů, ať již určených pro strukturované programování (nové typy programových smyček), tak i příkazy zjednodušující programátorovu každodenní činnost.
Obrázek 5: Inicializace interpretru Turbo BASICu XL 2.11 doplněná o ovladač Turbo 2000.
Nejnovějším BASICem navrženým a implementovaným pro domácí osmibitové mikropočítače Atari je Fast BASIC, jehož popisem se budeme zabývat dnes. Jedná se projekt, který je v mnoha ohledech odlišný od klasických BASICů. V první řadě namísto překladu zdrojového kódu do sekvence tokenů používá překladač do bajtkódu. To je koncept, který známe například z Pythonu, programovacího jazyka Lua, Javy atd. (ale i z původního Pascalu). Při běhu programu (tedy v runtime) je bajtkód konvertován na instrukce mikroprocesoru (což si ukážeme). Výsledek je ovšem v každém případě rychlejší, než interpretace tokenizovaného kódu a většinou je bajtkód i kratší, než kód tokenizovaný.
Obrázek 6: Dnes popisovaný Fast Basic.
Základní vlastnosti Fast Basicu
Fast Basic se v několika ohledech odlišuje od většiny klasických interpretrů jazyka BASIC. Jedna změna je patrná na první pohled – zdrojové kódy programů nevyžadují zápis čísel řádků, což znamená, že programy psané ve Fast Basicu se spíše podobají skriptům psaným v jazyku Lua či Pythonu. To vede k nutnosti náhrady skoků realizovaných v klasických interpretrech BASICu příkazem GOTO za strukturované jazykové konstrukce – což znamená podmínky (rozvětvení) a různé varianty programových smyček (což je ostatně jen dobře a do značné míry to zamezuje vzniku „špagetového kódu“, kterým je BASIC nechvalně proslulý).
Obrázek 7: Editor Fast Basicu (vidíme zde určitou inspiraci Emacsem).
Druhá změna sice není na první pohled patrná, ale ovlivňuje způsob interpretace programů. Klasické BASICy prováděly tokenizaci zdrojového kódu na sekvenci tokenů ve stylu:
10 LET A=42
na (zhruba):
číslo_řádku token_příkazu_let token_proměnná index_proměnné token_přiřazení token_čísla číslo
Ovšem interpretace takto reprezentovaného programu je velmi pomalá. Fast Basic je z tohoto důvodu postaven na bajtkódu. Sice se stále (při překladu) emituje sekvence tokenů, ovšem ty mají význam instrukcí bajtkódu: uložení proměnné na zásobník, provedení určité aritmetické operace, porovnání hodnoty s nulou atd. Navíc verze Fast Basicu pro PC obsahuje i optimalizaci bajtkódu – náhradu sekvence instrukcí bajtkódu za kratší či rychlejší variantu. Výsledek je (většinou) kratší a současně je interpretace rychlejší. Fast Basic tak může být pro stejné algoritmy zhruba 1,5× až 2× rychlejší než Turbo Basic XL (a ten je cca 3× až 5× rychlejší, než Atari Basic).
Mezi další vylepšení patří například podpora pro pole s osmibitovými a 16bitovými hodnotami, což je velmi praktické a pochopitelně to vede i k úsporám operační paměti. Taktéž je možné používat proměnné obsahující celá čísla a tím pádem obejít pomalé FP operace. Změnilo se i vývojové prostředí (pokud se využívá varianta Fast Basicu běžící přímo na Atari), protože se již nepoužívá klasický obrazovkový editor s čísly řádků, ale editor, který připomíná Vim – podporován je scrolling atd. (ostatně problematický či nepodporovaný scrolling popř. neexistence celoobrazovkového editoru je jedním z důvodů, proč byla čísla řádků vůbec do klasického BASICu původně zavedena).
Obrázek 8: Takto vypadá editor Fast Basicu po inicializaci.
Překladač Fast Basicu provozovatelný na PC
Fast Basic existuje ve dvou variantách. První varianta je určena přímo pro provozování na osmibitových mikropočítačích Atari, což s sebou pochopitelně přináší určitá omezení. Této variantě se budeme věnovat později. Druhou variantu tvoří překladač, který je provozovatelný přímo na PC. To znamená, že vývoj (včetně editace a překladu) je prováděn na PC a výsledkem překladu jsou binární soubory ve formátu XEX popř. obrazy disket, které se spouští v emulátoru počítačů Atari popř. se ukládají na paměťová média používaná v reálných Atari. Obě varianty Fast Basicu lze získat z GitHub repositáře dostupného na adrese https://github.com/dmsc/fastbasic. Mimochodem: v tomto repositáři se nachází i zdrojové kódy assembleru ca65 a linkeru ld65. Nejedná se o náhodu, protože tyto dva nástroje jsou použity ve fázi překladu – i tu si pochopitelně popíšeme v navazujících kapitolách.
Fast Basic prakticky: překlad a spuštění programu typu „Hello, world!“
Ukažme si nyní praktické použití překladače Fast Basicu. Překládat budeme klasický program typu „Hello, world!“, který ve variantě pro Fast Basic vypadá následovně:
print "Hello, world!"
Překlad tohoto zdrojového kódu se provede následovně:
$ fastbasic hello_1.bas BAS compile 'hello_1.bas' to 'hello_1.asm' ASM assemble 'hello_1.asm' to 'hello_1.o' LINK hello_1.xex
Výsledkem je soubor nazvaný hello_1.xex, jehož velikost je pouze 443 bajtů! Ano, to není žádná chyba: celý program i se svým runtime nedosahuje ani velikosti půl kilobajtu:
$ ls -la hello_1.xex -rw-r--r--. 1 ptisnovs ptisnovs 443 Oct 11 20:50 hello_1.xex
Výsledný program hello_1.xex je možné spustit v emulátoru osmibitových mikropočítačů Atari. Korektně se vypíše zpráva „Hello, world!“, ovšem poté program buď začne vypisovat BOOT ERROR nebo se vrátí do DOSu (nebo dokonce do Self Testu):
Obrázek 9: První demonstrační příklad sice vypíše zprávu, ale potom skončí katastrofou.
Program končící v nekonečné smyčce
Abychom zabránili návratu do DOSu (který nemusí být načten do paměti) nebo „pádu“ do Self Testu, ukončíme zdrojový kód programu nekonečnou smyčkou DO-LOOP (takže ho vlastně neukončíme – program z této smyčky pochopitelně nikdy nevyskočí):
print "Hello, world!" do loop
Výsledek je již lepší (řízení se nevrátí do DOSu) a výsledný program je přitom prodloužen pouze o dva bajty!:
Obrázek 10: Druhý demonstrační příklad s nekonečnou smyčkou.
Fáze překladu
Překlad zdrojových kódů Fast Basicu probíhá v několika fázích:
- Překlad zdrojových kódů do bajtkódu
- Vygenerování kódu v assembleru kompatibilního s CA65, který obsahuje jednotlivé instrukce bajtkódu
- Překlad assemblerem do objektového kódu
- Slinkování do výsledného souboru ve formátu XEX
- Pokud vše proběhne v pořádku, jsou odstraněny kódy v assembleru i soubor s objektovým kódem
Ukažme si celý postup na jednoduchém demonstračním příkladu, v němž se nejprve přiřadí hodnoty do dvou proměnných x a y, provede se součet těchto proměnných s uložením výsledku součtu do proměnné z a konečně se obsah proměnné z vypíše na obrazovku. Fast Basic nevyžaduje deklaraci proměnných, takže celý příklad může vypadat následovně:
x = 1 y = 2 z = x + y print z
V případě, že při překladu použijeme přepínač -c, provede se pouze první fáze překladu z Fast Basicu do assembleru, což nám umožňuje prozkoumat, jak vlastně tento kód vypadá:
; Imported symbols
; Exported symbols
.export bytecode_start
.include "target.inc"
; TOKENS:
.importzp TOK_1
.importzp TOK_ADD_VAR
.importzp TOK_BYTE
.importzp TOK_BYTE_PUT
.importzp TOK_END
.importzp TOK_INT_STR
.importzp TOK_PRINT_STR
.importzp TOK_VAR_LOAD
.importzp TOK_VAR_STORE
;-----------------------------
; Macro to get variable ID from name
.import __HEAP_RUN__
.macro makevar name
.byte <((.ident (.concat ("fb_var_", name)) - __HEAP_RUN__)/2)
.endmacro
; Variables
.segment "HEAP"
.export fb_var_X
fb_var_X: .res 2 ; word variable
.export fb_var_Y
fb_var_Y: .res 2 ; word variable
.export fb_var_Z
fb_var_Z: .res 2 ; word variable
;-----------------------------
; Bytecode
.segment "BYTECODE"
bytecode_start:
@FastBasic_LINE_1: ; LINE 1
.byte TOK_1
.byte TOK_VAR_STORE
makevar "X"
@FastBasic_LINE_2: ; LINE 2
.byte TOK_BYTE
.byte 2
.byte TOK_VAR_STORE
makevar "Y"
@FastBasic_LINE_3: ; LINE 3
.byte TOK_VAR_LOAD
makevar "X"
.byte TOK_ADD_VAR
makevar "Y"
.byte TOK_VAR_STORE
makevar "Z"
@FastBasic_LINE_4: ; LINE 4
.byte TOK_VAR_LOAD
makevar "Z"
.byte TOK_INT_STR
.byte TOK_PRINT_STR
.byte TOK_BYTE_PUT
.byte 155
.byte TOK_END
V další fázi překladu se může vytvořit i soubor s listingem vytvořený assemblerem ca65. Ten bude v našem konkrétním případě vypadat takto:
000000r 1 ; TOKENS:
000000r 1 .importzp TOK_1
000000r 1 .importzp TOK_ADD_VAR
000000r 1 .importzp TOK_BYTE
000000r 1 .importzp TOK_BYTE_PUT
000000r 1 .importzp TOK_END
000000r 1 .importzp TOK_INT_STR
000000r 1 .importzp TOK_PRINT_STR
000000r 1 .importzp TOK_VAR_LOAD
000000r 1 .importzp TOK_VAR_STORE
000000r 1 ;-----------------------------
000000r 1 ; Macro to get variable ID from name
000000r 1 .import __HEAP_RUN__
000000r 1 .macro makevar name
000000r 1 .byte <((.ident (.concat ("fb_var_", name)) - __HEAP_RUN__)/2)
000000r 1 .endmacro
000000r 1 ; Variables
000000r 1 .segment "HEAP"
000000r 1 .export fb_var_X
000000r 1 xx xx fb_var_X: .res 2 ; word variable
000002r 1 .export fb_var_Y
000002r 1 xx xx fb_var_Y: .res 2 ; word variable
000004r 1 .export fb_var_Z
000004r 1 xx xx fb_var_Z: .res 2 ; word variable
000006r 1 ;-----------------------------
000006r 1 ; Bytecode
000006r 1 .segment "BYTECODE"
000000r 1 bytecode_start:
000000r 1 @FastBasic_LINE_1: ; LINE 1
000000r 1 rr .byte TOK_1
000001r 1 rr .byte TOK_VAR_STORE
000002r 1 rr makevar "X"
000003r 1 @FastBasic_LINE_2: ; LINE 2
000003r 1 rr .byte TOK_BYTE
000004r 1 02 .byte 2
000005r 1 rr .byte TOK_VAR_STORE
000006r 1 rr makevar "Y"
000007r 1 @FastBasic_LINE_3: ; LINE 3
000007r 1 rr .byte TOK_VAR_LOAD
000008r 1 rr makevar "X"
000009r 1 rr .byte TOK_ADD_VAR
00000Ar 1 rr makevar "Y"
00000Br 1 rr .byte TOK_VAR_STORE
00000Cr 1 rr makevar "Z"
00000Dr 1 @FastBasic_LINE_4: ; LINE 4
00000Dr 1 rr .byte TOK_VAR_LOAD
00000Er 1 rr makevar "Z"
00000Fr 1 rr .byte TOK_INT_STR
000010r 1 rr .byte TOK_PRINT_STR
000011r 1 rr .byte TOK_BYTE_PUT
000012r 1 9B .byte 155
000013r 1 rr .byte TOK_END
000013r 1
Realizace výpočtu faktoriálu
Podívejme se nyní na realizaci výpočtu faktoriálu. Tento výpočet jsme si již kdysi ukazovali ve variantě určené pro Atari BASIC:
1 REM VYPOCET FAKTORIALU 2 REM UPRAVA PRO ATARI BASIC 3 REM 10 FOR N=0 TO 20 11 GOSUB 100 12 PRINT N,FACT 13 NEXT N 99 END 100 REM VYPOCET FAKTORIALU 101 FACT=1 102 FOR I=N TO 1 STEP -1 103 FACT=FACT*I 104 NEXT I 105 RETURN
Přepis do Turbo BASICu (ze kterého Fast Basic částečně vychází) využívajícího pojmenované procedury – klíčová slova PROC a ENDPROC:
1 REM VYPOCET FAKTORIALU 2 REM UPRAVA PRO TURBO-BASIC XL 3 REM POUZITI KONSTANT %0 a %1 4 REM ---------------------------- 5 REM 10 FOR N=%0 TO 20 11 EXEC FACTORIAL 12 PRINT N,FACT 13 NEXT N 99 END 100 ------------------------------ 101 REM VYPOCET FAKTORIALU 102 PROC FACTORIAL 103 FACT=%1 104 FOR I=N TO %1 STEP -1 105 FACT=FACT*I 106 NEXT I 107 ENDPROC
Dvě varianty výpočtu faktoriálu ve Fast BASICu: celočíselné nebo FP operace?
Úprava výše uvedeného programu pro Fast Basic je přitom relativně snadná. V prví fázi postačuje odstranit čísla řádků:
' VYPOCET FAKTORIALU
' UPRAVA PRO FAST BASIC
' ----------------------------
fact%=-1
for n=0 to 20
exec factorial
print n,fact%
next n
do
loop
' vypocet faktorialu
proc factorial
fact%=1.0
for i=n to 1 step -1
fact%=fact%*i
next i
endproc
Dále můžeme volání procedury factorial provést přes znak zavináče (a nikoli příkazem exec – i to je ovšem možné). Povšimněte si, že parametry se do procedury factorial předávají přes globální proměnnou – to je prostě BASIC:
n=0
proc factorial
fact=1
for i=n to 1 step -1
fact=fact*i
next i
print fact
endproc
for n=1 to 20
@factorial
next n
do
loop
Otestujme si výsledky:
Obrázek 11: Výpočet faktoriálu se šestnáctibitovými hodnotami.
Ze zobrazených výsledků je patrné, že výpočty probíhají se šestnáctibitovými hodnotami se znaménkem, což konkrétně při výpočtu faktoriálu znamená velmi rychlé přetečení. Samozřejmě můžeme zapsat program ve Fast Basicu, který bude provádět výpočty s hodnotami s plovoucí řádovou čárkou, což je sice pomalejší (ve skutečnosti mnohem pomalejší), ale výsledky již budou korektní. Úprava programu spočívá v tom, že za jméno proměnné fact přidáme znak procenta, což vlastně znamená určení typu této proměnné:
n=0
proc factorial
fact%=1.0
for i=n to 1 step -1
fact%=fact%*i
next i
print fact%
endproc
for n=1 to 20
@factorial
next n
do
loop
Nyní budou výsledky vypočteny sice mnohem (mnohem!) pomaleji, ale jsou alespoň korektní:
Obrázek 12: Výpočet faktoriálu s FP hodnotami.
Podpora pro práci s grafikou
Ve Fast Basicu jsou dostupné základní příkazy pro práci s grafikou. Tyto příkazy jsou odvozeny od standardního Atari Basicu, takže si jen v krátkosti ukažme použití příkazů graphics, color, plot a drawto:
graphics 7 max_x = 159 max_y = 79 color 1 plot 0,0 drawto max_x, max_y color 2 plot max_x, 0 drawto 0, max_y do loop
Po překladu a spuštění tohoto příklady by se měl na obrazovce objevit barevný kříž:
Obrázek 13: Základní grafické příkazy GRAPHICS, COLOR, PLOT a DRAWTO.
Operace fillto
Navíc je dostupný i příkaz fillto. Ten odpovídá příkazu drawto, ovšem navíc vyplňuje plochu napravo od vykreslované úsečky až po fcolor. Příkaz fillto je odvozen od operace FILL implementované pro zařízení S: (viz nedávné články o SIO, v nichž jsme se touto operací zabývali). S určitými chybami můžeme tento příkaz použít pro vykreslení vlajky:
graphics 7+16 ' bila setcolor 0, 0, 14 ' tmave cervena setcolor 1, 3, 8 ' tmave modra setcolor 2, 7, 4 max_x = 159 mid_x = 80 max_y = 79 ' horní část vlajky color 1 fcolor 1 ' pravý okraj plot max_x, 0 drawto max_x, max_y/2 ' bílý segment plot 0,0 fillto mid_x, max_y/2 ' spodní část vlajky color 2 fcolor 2 ' pravý okraj plot max_x, max_y/2+1 drawto max_x, max_y ' červený segment plot 0, max_y fillto mid_x, max_y/2 ' modrý klín color 3 fcolor 3 plot 0,1 fillto 0, max_y-1 do loop
Jak je při pohledu na výsledek patrné, program by ještě potřeboval vylepšit v oblasti horního a dolního okraje:
Obrázek 14: Vlajka vykreslená příkazy FILLTO.
Podpora pro práci se sprity (PMG)
Jako jeden z mála interpretrů jazyka BASIC pro osmibitové domácí mikropočítače Atari podporuje Fast Basic i práci se sprity (PMG – Player-Missile Graphics). To je užitečné, protože není nutné volat příkazy POKE s „magickými“ hodnotami. Podpora je sice poměrně minimalistická, ovšem pro některé účely může být dostačující.
V první řadě byl přidán příkaz PMGRAPHICS pro povolení a nastavení režimu zobrazení spritů. Existují tři možnosti: sprity nejsou zobrazeny, sprity jsou zobrazeny v 128řádkovém režimu a sprity jsou zobrazeny v režimu 256řádkovém. Dalším příkazem je PMHPOS, kterým se nastavuje horizontální pozice hráčů a střel na obrazovce. Barvy hráčů a střel se nastavují standardním příkazem SETCOLOR s prvním parametrem nastaveným na záporné hodnoty –4 až –1. A nesmíme zapomenout na funkci PMADR, která vrací adresu paměťového bloku pro zvoleného hráče nebo všechny střely. A to je vlastně vše – ostatní operace zajistí buď POKE nebo příkaz pro přesun bloku paměti MOVE.
Zobrazení hráče (PMG)
Již několikrát jsme se pokusili zobrazit hráče číslo 0 s bitmapou reprezentující „ufona“ ze slavné hry Space Invaders. Varianta pro Fast Basicu je (možná až na blok s daty) velmi čitelná a snadno pochopitelná:
' textovy rezim graphics 0 ' povoleni zobrazeni spritu pmgraphics 2 ' adresa bloku pro hrace 1 P0Mem = pmadr(0) ' vymazani pameti hrace mset P0Mem, 128, 0 ' nastavení barvy hrace setcolor -4, 1, 15 ' bitmapa hrace DATA PMdata() byte = 24, 60, 126, 219, 255, 36, 90, 165 ' horizontalni pozice hrace na obrazovce pmhpos 0, 120 ' kopie bitmapy hrace move adr(PMdata), P0Mem+60, 8 do loop
Výsledek vypadá takto:
Obrázek 15: Hráč zobrazený programem napsaným ve Fast Basicu.
Realizace horizontálního i vertikálního pohybu hráče
Rozšíření programu o horizontální i vertikální pohyb hráče s využitím joysticku je relativně snadné. Postačuje program doplnit o proměnné x a y, logiku pro úpravu hodnot těchto proměnných v závislosti na náklonu joysticku, realizovat horizontální posun zavoláním příkazu PHPOS a vertikální posun přesunem bloku paměti příkazem MOVE. Ovšem program je tak rychlý, že ho musíme zpomalit příkazem PAUSE, který ve své základní variantě počká 1/50 nebo 1/60 sekundy (už asi tušíte, že se zde pracuje se snímkovou frekvencí):
' textovy rezim
graphics 0
' povoleni zobrazeni spritu
pmgraphics 2
' adresa bloku pro hrace 1
P0Mem = pmadr(0)
' vymazani pameti hrace
mset P0Mem, 128, 0
' nastavení barvy hrace
setcolor -4, 1, 15
' bitmapa hrace i se dvema "mazacimi" radky
DATA PMdata() byte = 0, 24, 60, 126, 219, 255, 36, 90, 165, 0
y = 60
x = 120
do
' horizontalni pozice hrace na obrazovce
pmhpos 0, x
' kopie bitmapy hrace
move adr(PMdata), P0Mem+y, 10
if stick(0) = 7 then x=x+1
if stick(0) = 11 then x=x-1
if stick(0) = 14 then y=y-1
if stick(0) = 13 then y=y+1
' poseckejme
pause
loop
Jak by stejný program napsal znalec BASICu?
Ovšem znalci (Atari) BASICu by se nezdržovali zápisem podmínek if-else. Namísto toho by využili faktu, že porovnání (jedním znakem =) vrací na základě výsledku porovnání hodnotu 1 nebo 0, se kterou lze ihned provádět výpočty. Posun hráče po obrazovce by se tedy mohl zkrátit na:
s = stick(0) x = x + (s=7) - (s=11) y = y + (s=13) - (s=14)
Upravený program by mohl vypadat následovně:
' textovy rezim
graphics 0
' povoleni zobrazeni spritu
pmgraphics 2
' adresa bloku pro hrace 1
P0Mem = pmadr(0)
' vymazani pameti hrace
mset P0Mem, 128, 0
' nastavení barvy hrace
setcolor -4, 1, 15
' bitmapa hrace i se dvema "mazacimi" radky
DATA PMdata() byte = 0, 24, 60, 126, 219, 255, 36, 90, 165, 0
y = 60
x = 120
do
' horizontalni pozice hrace na obrazovce
pmhpos 0, x
' kopie bitmapy hrace
move adr(PMdata), P0Mem+y, 10
s = stick(0)
x = x + (s=7) - (s=11)
y = y + (s=13) - (s=14)
' poseckejme
pause
loop
Strukturované programování ve Fast Basicu
Fast Basic obsahuje některé konstrukce, které se přibližují klasickému strukturovanému programování. Konkrétně to znamená to, že jsou podporovány procedury (pojmenované podprogramy), strukturovaná konstrukce IF-ELSE-ENDIF a programové smyčky FOR-NEXT, DO-LOOP, WHILE-WEND a REPEAT-UNTIL. Použití těchto konstrukcí si ukážeme na několika demonstračních příkladech:
Součet prvků uložených v poli:
PROC SUM P N
SUM% = 0
WHILE N>0
DEC N
SUM% = SUM% + %(P)
P = P + 1
WEND
NDPROC
Čtení hodin reálného času:
PROC GetTime
REPEAT
QT = PEEK(18)
ETIME% = TIME
UNTIL QT = PEEK(18)
IF ETIME%<0
QT = QT + 1
ENDIF
ETIME% = 65536.0 * QT + ETIME%
ENDPROC
Z programových smyček je možné vyskočit příkazem EXIT:
b=1 repeat a=1 while a < max c= a*a+b*b a=a+1 if c > 50 ? 1 exit endif if a + b > threshold ? 0 exit endif ? c, wend b=b+1 until b > finish
Peephole optimalizace prováděné překladačem na PC
PC varianta překladače Fast Basicu obsahuje podporu pro různé optimalizace prováděné na úrovni bajtkódu. Tyto optimalizace pracují zhruba následovně: detekuje se určitá sekvence instrukcí bajtkódu a ta je následně nahrazena za jinou sekvencí instrukcí, nebo se původní instrukce dokonce smažou, pokud nemají vliv na výsledný program. Ve velké většině případů se tedy nejedná o žádné pokročilé globální optimalizace typu rozbalení smyček atd. (to mimochodem nemá na MOS 6502 žádný větší význam), ovšem každá vynechaná instrukce (popř. jednodušší instrukce) pochopitelně výsledný program dokáže zrychlit, a to mnohdy poměrně významným způsobem.
Ukázky peephole optimalizací
Abychom si udělali malou představu, jak jsou vlastně peephole optimalizace na úrovni bajtkódu realizovány, podívejme se na příklady. První příklad maže sekvenci instrukcí pro přičtení nuly:
// TOK_PUSH / TOK_NUM / 0 / TOK_ADD -> -
if(mtok(0, "TOK_PUSH") && mtok(1, "TOK_NUM") && mword(2) && val(2) == 0 &&
mtok(3, "TOK_ADD"))
{
del(3);
del(2);
del(1);
del(0);
i--;
continue;
}
dtto pro násobení jedničkou:
// TOK_PUSH / TOK_NUM / 1 / TOK_MUL -> -
if(mtok(0, "TOK_PUSH") && mtok(1, "TOK_NUM") && mword(2) && val(2) == 1 &&
mtok(3, "TOK_MUL"))
{
del(3);
del(2);
del(1);
del(0);
i--;
continue;
}
Náhrada sekvence instrukcí, které zvyšují hodnotu proměnné o jedničku, za specializovanou instrukci INCVAR (jak asi tušíte, je realizován instrukcí kombinací ADC+INC):
// VAR = VAR + 1 ==> INC VAR
// TOK_VAR / x / TOK_PUSH / TOK_NUM / 1 / TOK_ADD / TOK_VAR_STORE / x
// -> TOK_INCVAR / x
if(mtok(0, "TOK_VAR_LOAD") && mtok(2, "TOK_PUSH") && mtok(3, "TOK_NUM") &&
mword(4) && val(4) == 1 && mtok(5, "TOK_ADD") &&
mtok(6, "TOK_VAR_STORE") && varn(1) == varn(7))
{
set_tok(0, "TOK_INCVAR");
del(7);
del(6);
del(5);
del(4);
del(3);
del(2);
i--;
continue;
}
A konečně náhrada instrukcí pro vynásobení hodnoty dvěma za bitový posun doleva:
// TOK_PUSH / TOK_NUM / 2 / TOK_MUL -> TOK_USHL
if(mtok(0, "TOK_PUSH") && mtok(1, "TOK_NUM") && mword(2) && val(2) == 2 &&
mtok(3, "TOK_MUL"))
{
del(3);
del(2);
del(1);
set_tok(0, "TOK_USHL");
i--;
continue;
}
Podobných optimalizací existuje několik desítek.
Zhodnocení
Již několikrát jsme se v tomto seriálu setkali s různými programovacími jazyky, ať již interpretry či překladači, určenými pro osmibitové domácí mikropočítače Atari. A (alespoň prozatím) je výsledek stále stejný – pro tvorbu nejefektivnějších programů je (pochopitelně) nejlepší assembler a v relativně těsném závěsu za ním se pohybuje původní překladač programovacího jazyka Action! Jedinou vážnější nevýhodou Actionu je fakt, že původní verze nepodporuje operace s hodnotami s plovoucí řádovou čárkou. Teoreticky by měly být minimálně stejně kvalitní i překladače programovacího jazyka C, ovšem v praxi tomu tak není. V oblasti interpretrů je nejlepší dnes popisovaný Fast Basic, těsně za ním pak Turbo Basic XL (verze NEzaložená na překladači). Ostatní interprety BASICů jsou buď mnohem pomalejší, nebo jim chybí některé důležité vlastnosti (podpora pro multimediální příkazy atd.).
Repositář s demonstračními příklady
Všechny demonstrační příklady, s nimiž jsme se v předchozích článcích i v článku dnešním seznámili a které jsou určeny pro překlad s využitím assembleru ca65, jsou dostupné, jak je zvykem, na GitHubu. V tabulce níže jsou uvedeny odkazy na jednotlivé zdrojové kódy příkladů psané v assembleru i „listingy“ vygenerované samotným assemblerem, ze kterých je patrné, jakým způsobem se jednotlivé příklady přeložily do výsledného XEX souboru:
Odkazy na Internetu
- MOS 6502 instruction set
http://www.6502.org/users/obelisk/6502/instructions.html - EXE File Format Description
https://gury.atari8.info/refs/file_formats_exe.php - XEX Filter – A toolkit to analyze and manipulate Atari binary files
https://www.vitoco.cl/atari/xex-filter/index.html - chkxex.py
https://raw.githubusercontent.com/seban-slt/tcx_tools/refs/heads/master/chkxex.py - ca65 Users Guide
https://cc65.github.io/doc/ca65.html - cc65 Users Guide
https://cc65.github.io/doc/cc65.html - ld65 Users Guide
https://cc65.github.io/doc/ld65.html - da65 Users Guide
https://cc65.github.io/doc/da65.html - Překladače jazyka C pro historické osmibitové mikroprocesory
https://www.root.cz/clanky/prekladace-jazyka-c-pro-historicke-osmibitove-mikroprocesory/ - Překladače programovacího jazyka C pro historické osmibitové mikroprocesory (2)
https://www.root.cz/clanky/prekladace-programovaciho-jazyka-c-pro-historicke-osmibitove-mikroprocesory-2/ - Getting Started Programming in C: Coding a Retro Game with C Part 2
https://retrogamecoders.com/getting-started-with-c-cc65/ - NES game development in 6502 assembly – Part 1
https://kibrit.tech/en/blog/nes-game-development-part-1 - NES 6502 Programming Tutorial – Part 1: Getting Started
https://dev.xenforo.relay.cool/index.php?threads/nes-6502-programming-tutorial-part-1-getting-started.858389/ - Minimal NES example using ca65
https://github.com/bbbradsmith/NES-ca65-example - List of 6502-based Computers and Consoles
https://www.retrocompute.co.uk/list-of-6502-based-computers-and-consoles/ - 6502 – the first RISC µP
http://ericclever.com/6500/ - 3 Generations of Game Machine Architecture
http://www.atariarchives.org/dev/CGEXPO99.html - “Hello, world” from scratch on a 6502 — Part 1
https://www.youtube.com/watch?v=LnzuMJLZRdU - A Tour of 6502 Cross-Assemblers
https://bumbershootsoft.wordpress.com/2016/01/31/a-tour-of-6502-cross-assemblers/ - Adventures with ca65
https://atariage.com/forums/topic/312451-adventures-with-ca65/ - example ca65 startup code
https://atariage.com/forums/topic/209776-example-ca65-startup-code/ - 6502 PRIMER: Building your own 6502 computer
http://wilsonminesco.com/6502primer/ - 6502 Instruction Set
https://www.masswerk.at/6502/6502_instruction_set.html - Chip Hall of Fame: MOS Technology 6502 Microprocessor
https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-mos-technology-6502-microprocessor - Single-board computer
https://en.wikipedia.org/wiki/Single-board_computer - www.6502.org
http://www.6502.org/ - 6502 PRIMER: Building your own 6502 computer – clock generator
http://wilsonminesco.com/6502primer/ClkGen.html - Great Microprocessors of the Past and Present (V 13.4.0)
http://www.cpushack.com/CPU/cpu.html - Jak se zrodil procesor?
https://www.root.cz/clanky/jak-se-zrodil-procesor/ - Osmibitové mikroprocesory a mikrořadiče firmy Motorola (1)
https://www.root.cz/clanky/osmibitove-mikroprocesory-a-mikroradice-firmy-motorola-1/ - Mikrořadiče a jejich použití v jednoduchých mikropočítačích
https://www.root.cz/clanky/mikroradice-a-jejich-pouziti-v-jednoduchych-mikropocitacich/ - Mikrořadiče a jejich aplikace v jednoduchých mikropočítačích (2)
https://www.root.cz/clanky/mikroradice-a-jejich-aplikace-v-jednoduchych-mikropocitacich-2/ - 25 Microchips That Shook the World
https://spectrum.ieee.org/tech-history/silicon-revolution/25-microchips-that-shook-the-world - Comparison of instruction set architectures
https://en.wikipedia.org/wiki/Comparison_of_instruction_set_architectures - How To Start Learning Atari 8 Bit Assembly For Free
https://forums.atariage.com/topic/300732-how-to-start-learning-atari-8-bit-assembly-for-free/ - WUDSN (Demo Group)
https://www.wudsn.com/ - Machine Language For Beginners
https://www.atariarchives.org/mlb/ - Assembly language: all about I/O
https://www.atarimagazines.com/v3n8/AllAbout_IO.html - Sedmdesátiny assemblerů: lidsky čitelný strojový kód
https://www.root.cz/clanky/sedmdesatiny-assembleru-lidsky-citelny-strojovy-kod/ - Color names
https://atariwiki.org/wiki/Wiki.jsp?page=Color%20names - ATASCII
https://en.wikipedia.org/wiki/ATASCII - Put characters in display ram isn't ATASCII?
https://forums.atariage.com/topic/359973-put-characters-in-display-ram-isnt-atascii/ - ATASCII And Internal Character Code Values
https://www.atariarchives.org/mapping/appendix10.php - Reading ATASCII from the keyboard in assembly
https://forums.atariage.com/topic/361733-reading-atascii-from-the-keyboard-in-assembly/ - Why does the 6502 JSR instruction only increment the return address by 2 bytes?
https://retrocomputing.stackexchange.com/questions/19543/why-does-the-6502-jsr-instruction-only-increment-the-return-address-by-2-bytes - Pushing return address to stack off by 1 byte
https://forums.atariage.com/topic/378206-pushing-return-address-to-stack-off-by-1-byte/ - Intel x86 documentation has more pages than the 6502 has transistors
https://www.righto.com/2013/09/intel-x86-documentation-has-more-pages.html - Clearing a Section of Memory
http://www.6502.org/source/general/clearmem.htm - Practical Memory Move Routines by Bruce Clark
http://www.6502.org/source/general/memory_move.html - 6502 Assembly Programming Guide
https://neumont-gamedev.github.io/posts/retrogamedev-6502-guide/ - Off-by-one error
https://en.wikipedia.org/wiki/Off-by-one_error - 6502 cycle times
https://www.nesdev.org/wiki/6502_cycle_times - Atari TIA
http://www.atarihq.com/danb/tia.shtml - TIA Playfield
http://www.atarihq.com/danb/TIA/Playfield.shtml - Atari Inc.:
ANTIC C012296 (NTSC) Revision D
Atari Incorporated, Sunnyvale CA, 1982 - Atari Inc.:
GTIA C014805 (NTSC) Revision A
Atari Incorporated, Sunnyvale CA, 1982 - Atari 5200
http://www.atariage.com/software_search.html?SystemID=5200 - Atari 5200 Hardware and Accessories
http://www.atariage.com/5200/archives/hardware.html - Atari 5200 Screenshots
http://www.atariage.com/system_items.html?SystemID=5200&ItemTypeID=SCREENSHOT - History of video game consoles (second generation): Wikipedia
http://en.wikipedia.org/wiki/History_of_video_game_consoles_(second_generation) - Atari 5200: Wikipedia
http://en.wikipedia.org/wiki/Atari_5200 - Player-Missile Graphics
https://www.atariarchives.org/agagd/chapter5.php - Sprite (computer graphics)
https://en.wikipedia.org/wiki/Sprite_(computer_graphics) - Atari Graphics Demonstrations by Underground Software, 1985 | Atari 8 bit Demo
https://www.youtube.com/watch?v=h7N9EYSyCkw - Atari 8-bit Display List Interrupts: A Complete(ish) Tutorial
https://playermissile.com/dli_tutorial/ - Atari Assembler Editor manual
https://atariwiki.org/wiki/attach/Atari%20Assembler%20Editor/ATARI%20Assembler%20Editor%20User-s%20Manual-OCR.pdf - Atari 8-bit Fine Scrolling: A Complete(ish) Tutorial
https://playermissile.com/scrolling_tutorial/index.html - Atari Fine Scrolling
https://www.atarimagazines.com/compute/issue67/338_1_Atari_Fine_Scrolling.php - CTIA / GTIA Pinout Diagram
https://user.xmission.com/~trevin/atari/gtia_pinout.html - GTIA Modes
https://page6.org/archive/issue_02/page_10.htm - 56 graphic modes
https://www.atari800xl.eu/docs/kb/kb-hardware-0005-atari-8bit-56-graphic-modes.html - UNLOCKING THE 56 GRAPHIC MODES
https://www.atarimagazines.com/v3n5/allmodes.html - GTIA Modes 9, 10 & 11 + ANTIC data and color clocks
https://forums.atariage.com/topic/366256-gtia-modes-9–10–11-antic-data-and-color-clocks/ - ANTIC, GTIA and timing info
https://atarimax.com/jindroush.atari.org/atanttim.html - 6.10 Cycle timing
https://github.com/AnimaInCorpore/A8E/blob/main/AHRM/6.%20CTIA-GTIA/10.%20Cycle%20timing.md - Doug Neubauer
https://en.wikipedia.org/wiki/Doug_Neubauer - POKEY
https://en.wikipedia.org/wiki/POKEY - Inside the Atari 800XL
https://www.goto10retro.com/p/800xl-inside - Grayscale Project – „Jam Session“ – Chiptune Visualization / Atari SAP
https://www.youtube.com/watch?v=Qx-AHgvwrHo&list=PL92E73FD91764173B - POKEY MUSIC ( ATARI XL / XE ) =+ MUSIC FROM POLAND += DEMO
https://www.youtube.com/watch?v=5PswfMjMop4&list=RD5PswfMjMop4&start_radio=1 - Atari.org
http://www.atari.org/ - Atari POKEY
http://www.absoluteastronomy.com/topics/Atari_POKEY - Chiptune
http://www.absoluteastronomy.com/topics/Chiptune - ASAP – Another Slight Atari Player
http://asap.sourceforge.net/ - Atari SAP music archive
http://asma.atari.org/ - RASTER Music Tracker (RMT)
http://raster.infos.cz/atari/rmt/rmt.htm - POKEY explorer
https://github.com/ivop/pokey-explorer - POKEY C012294 Documentation
https://7800.8bitdev.org/index.php/POKEY_C012294_Documentation - Pokey Registers
https://user.xmission.com/~trevin/atari/pokey_regs.html - POKEY CO12294
http://krap.pl/mirrorz/atari/homepage.ntlworld.com/kryten_droid/Atari/800XL/atari_hw/pokey.htm - De Re Atari: SOUND
https://www.atariarchives.org/dere/chapt07.php#H7_3_5 - Paddle (game controller)
https://en.wikipedia.org/wiki/Paddle_(game_controller) - Interfacing Your Atari
https://www.atariarchives.org/creativeatari/Interfacing_Your_Atari.php - Atari SIO
https://en.wikipedia.org/wiki/Atari_SIO - Atari Chips
https://atarimax.com/jindroush.atari.org/achip.html - Table 4: Keyboard Code Index, KEYCODE: ATASCII
https://www.atariarchives.org/c3ba/kcindex.php - SIO2PC
http://raster.atariportal.cz/hw/sio2pc.htm - Using Serial Input/Output
https://www.atarimagazines.com/compute/issue64/insight_atari.php - SIO ROUTINES
https://www.atariarchives.org/mmm/sio.php - ATARI 8 bit Serial Input/Output (SIO)
https://allpinouts.org/pinouts/connectors/serial/atari-8-bit-serial-input-output-sio/ - SIOspecs
https://archive.org/details/SIOspecs - Interview with Joe Decuir
http://www.atarimuseum.com/articles/joedecuir.html - USB AND OS/2
http://www.os2voice.org/VNL/past_issues/VNL1199H/vnewsf5.htm - Standards and specs: The ins and outs of USB
http://www.ibm.com/developerworks/power/library/pa-spec7.html - USB Class Codes
http://www.usb.org/developers/defined_class - Universal Serial Bus
http://en.wikipedia.org/wiki/Usb - SMBus.org
http://www.smbus.org - SMBus FAQ
http://www.smbus.org/faq/faq.htm - System Management Bus
http://en.wikipedia.org/wiki/System_Management_Bus - SMBus Specifications
http://www.smbus.org/specs - SMBus Control Method Interface Specification Version 1.0
http://www.smbus.org/specs/smbus_cmi10.pdf - System Management Bus (SMBus) Specification Version 2.0
http://www.smbus.org/specs/smbus20.pdf - Atari Connection Machine
http://acm.atariportal.cz/s/con8bit/8bits - Atari Program Recorder
https://en.wikipedia.org/wiki/Atari_Program_Recorder - THE ATARI CASSETTE
https://www.atariarchives.org/dere/chaptC.php - Turbo-2000
https://www.atari-800.cz/turbo-2000 - Atari XC12 FSK Decoder
https://zeninstruments.blogspot.com/2024/08/atari-xc12-fsk-decoder.html - Clock recovery
https://en.wikipedia.org/wiki/Clock_recovery - Jiří Richter (programátor)
https://cs.wikipedia.org/wiki/Ji%C5%99%C3%AD_Richter_(program%C3%A1tor) - Spectrum tape interface
https://sinclair.wiki.zxnet.co.uk/wiki/Spectrum_tape_interface - How Tape Loading Works
https://lemmings.info/how-tape-loading-works/ - CIO Reference
https://atarifoundry.com/docs/forge/advanced-topics/cio-reference - The central input-output system in Atari computers
https://www.atariarchives.org/alp/chapter_9.php - ATR: chpt.1: Central Input/Output (CIO)
https://www.atariarchives.org/cfn/12/02/0010.php - NOTE and POINT
https://atarifoundry.com/docs/forge/advanced-topics/note-point - Customizing the Atari Operating System Device Handlers: Part 1
https://www.atarimagazines.com/v8n2/customhandlers.html - Customizing the Atari Operating System Device Handlers: Part II
https://www.atarimagazines.com/v8n3/OperatingSystemHandlers.html - Spectrum tape interface
https://sinclair.wiki.zxnet.co.uk/wiki/Spectrum_tape_interface - Turbo 2000
http://wiki.oldcomp.cz/index.php?title=Turbo_2000 - Původní manuál k systému Turbo 2000
http://wiki.oldcomp.cz/images/6/65/Zak_praha_1987_p2_turbo2000.pdf - Mark and space
https://en.wikipedia.org/wiki/Mark_and_space - Obdélníkový průběh
https://cs.wikipedia.org/wiki/Obd%C3%A9ln%C3%ADkov%C3%BD_pr%C5%AFb%C4%9Bh - All you ever wanted to know about: Atari Cartridges
https://atariwiki.org/wiki/Wiki.jsp?page=Cartridges - Manuál k MAC/65
https://data.atariwiki.org/DOC/MAC-65_and_BUG-65_Rev._1.1-OSS-OCR.pdf - Manuál k DDT
https://atariwiki.org/wiki/attach/DDT/DDT_1.0_Manual.pdf - MAC/65 na Atari wiki
https://www.atariwiki.org/wiki/Wiki.jsp?page=Mac65 - Madass – home page
https://mads.atari8.info/ - Mad-Assembler (GitHub)
https://github.com/tebe6502/Mad-Assembler - The 6502/65C02/65C816 Instruction Set Decoded
https://llx.com/Neil/a2/opcodes.html - Action
https://atariwiki.org/wiki/Wiki.jsp?page=Action#top - Action: manuál
https://www.atarimania.com/documents/atari-action-reference-manual.pdf - Getting in on the Action 1
https://atariwiki.org/wiki/Wiki.jsp?page=Getting%20in%20on%20the%20Action%201 - Getting in on the Action 2
https://atariwiki.org/wiki/Wiki.jsp?page=Getting%20in%20on%20the%20Action%202 - Category:Action! na RosettaCode
https://rosettacode.org/wiki/Category:Action! - Deep Blue C Compiler
https://www.atarimania.com/games/atari-400–800-xl-xe-utilities-deep-blue-c-compiler-12478 - DEEP BLUE C Manual
https://www.atariarchives.org/APX/showdocs.php?cat=20166 - C/65
https://www.atarimania.com/games/atari-400–800-xl-xe-utilities-c-65–19204 - Getting Started Programming in C: Coding a Retro Game with C Part 2
https://retrogamecoders.com/getting-started-with-c-cc65/ - CC65 Advanced optimizations
https://github.com/ilmenit/CC65-Advanced-Optimizations/tree/master/04-get%20rid%20of%20C%20stack
