Velmi zajímavým vyšším programovacím jazykem, který byl optimalizován pro použití na osmibitových mikropočítačích, byl jazyk nazvaný Action!. Tento jazyk, o kterém kdysi vznikaly celé legendy (zejména v zemích s nedostatkem literatury), byl určen pro osmibitové domácí mikropočítače Atari a od ostatních (obecnějších) jazyků se odlišoval zejména tím, že jeho možnosti do značné míry odrážely schopnosti těchto mikropočítačů (datové typy omezeny na bajt, 16bitový integer se znaménkem a bez znaménka atd.).
Co se dozvíte v článku
- Rezervovaná klíčová slova a speciální znaky
- Datové typy v jazyku Action!
- Programové konstrukce v jazyku Action!
- Praktická část
- Program typu „Hello world!“
- Překlad a spuštění programu
- Program pro nastavení barvy pozadí
- Uložení přeloženého programu s jeho následnou analýzou
- Zobrazení spritu s nastavením jeho horizontální pozice a barvy
- Horizontální pohyb spritu s čekáním na dokončení zobrazení snímku
- Analýza strojového kódu získaného překladem
- Horizontální pohyb spritu řízený joystickem
- Výsledek překladu demonstračního příkladu do strojového kódu
- Překlad operace inkrementace hodnoty pro proměnné různých typů
- Překlad operace pro posun doprava pro proměnné různých typů
- Překlad operace pro posun doleva o větší počet bitů
- Překlad součinu obsahu proměnné s konstantou
- Repositář s demonstračními příklady
- Odkazy na Internetu
Syntaxe jazyka Action! je odvozena od Algolu (resp. od Algolské větve jazyků, kam spadá i Pascal), což se týká zejména způsobu zápisu programových smyček a podmínek (IF-FI, DO-OD). Action! podporoval strukturované programování (což je dnes zcela základní vlastnost, ovšem v době kralování BASICu tomu tak nebylo), uživatelé mohli vytvářet lokální proměnné, ovšem ne na zásobníku, jehož velikost i možnosti adresování byly na MOS 6502 omezeny.
Obrázek 1: Úvodní obrazovka vývojového prostředí jazyka Action!
Zajímavě bylo vyřešeno uživatelské rozhraní jazyka Action!, protože uživatelé měli k dispozici plnohodnotný textový editor (už bez čísel řádků), jednoprůchodový (a tedy dostatečně rychlý) překladač a monitor, v němž bylo možné provádět překlad, uložení strojového kódu a častečně i ladění vyvíjené aplikace. Zdrojový text mohl být neustále uložen v operační paměti současně s přeloženou (nativní) aplikací, což bylo umožněno tím, že samotný Action! byl umístěn na přídavném paměťovém modulu (cartridge). Mimochodem: editor, překladač i monitor se na paměťovém modulu vešly do pouhých šestnácti kilobajtů. To, současně s faktem, že výsledkem překladu byl relativně rychlý strojový kód, vedlo k tomu, že jazyk Action! získal téměř kultovní status (i když je pravda, že mnohem více programů vznikalo přímo v assembleru).
Obrázek 2: Obrazovka prostředí jazyka Action! po přepnutí do monitoru.
Rezervovaná klíčová slova a speciální znaky
V jazyku Action! je definováno několik rezervovaných klíčových slov a taktéž symbolů se speciálním významem. V následující tabulce jsou vypsána všechna klíčová slova jazyka Action!, která mají přesně specifikovanou sémantiku:
| AND | FI | OR | UNTIL |
| ARRAY | FOR | POINTER | WHILE |
| BYTE | FUNC | PROC | XOR |
| CARD | IF | RETURN | |
| CHAR | INCLUDE | RSH | |
| DEFINE | INT | SET | |
| DO | LSH | STEP | |
| ELSE | MOD | THEN | |
| ELSEIF | MODULE | TO | |
| EXIT | OD | TYPE |
Překladač jazyka Action! taktéž rozpoznává různé operátory a symboly (pole, indexování atd.), které jsou vypsány v další tabulce:
| Aritmetické | Relační | Binární | Ostatní symboly |
|---|---|---|---|
| + | = | & | ( |
| – | > | % | ) |
| * | >= | ! | [ |
| / | < | @ | ] |
| <= | " | ||
| <> | ' | ||
| # | $ | ||
| ; |
Datové typy v jazyku Action!
Programovací jazyk Action! podporuje tři základní (primitivní) datové typy, které jsou pojmenované BYTE, INT a CARD. Označení BYTE představuje osmibitovou hodnotu typu bajt (v tomto případě bez znaménka, protože operace s hodnotami se znaménky je v případě mikroprocesoru MOS 6502 složitější), INT je šestnáctibitová hodnota se znaménkem a CARD je taktéž šestnáctibitová hodnota, ovšem bez znaménka (a opět platí – manipulace s CARD je jednodušší, než s hodnotami typu INT).
Následuje příklad deklarace a popř. i inicializace proměnných těchto typů:
BYTE x,y,z INT num=[0] <- inicializace na zadanou hodnotu BYTE x=$8000 <- uložení na adresu
Povšimněte si toho, že se ve třetím případě nejedná o inicializaci proměnné na určitou hodnotu, ale o určení, na které adrese bude proměnná uložena.
Kromě toho lze používat i šestnáctibitové ukazatele, podobně, jako je tomu například v programovacím jazyku C:
BYTE POINTER ptr CARD POINTER ptr2
Podporována jsou ovšem i pole, přičemž inicializaci prvků polí (libovolného typu) lze provést přes řetězcový literál:
BYTE ARRAY pole INT ARRAY x(10) BYTE ARRAY str = "This is a string constant"
Aby toho nebylo málo, je možné deklarovat i vlastní struktury (záznamy), opět podobně jako v programovacím jazyku C:
TYPE r=[BYTE b1, b2
INT i1]
Programové konstrukce v jazyku Action!
Programovací jazyk Action! podporuje strukturované programování a má v tomto ohledu zhruba stejné vyjadřovací schopnosti, jako již zmíněný jazyk C či Pascal. Ostatně v praktické části dnešního článku si ukážeme několik způsobů využití dostupných programových konstrukcí (při zápisu použijeme odsazení, které je taktéž podporováno, resp. přesněji řečeno je překladač toleruje). V této kapitole si v krátkosti ukážeme způsob zápisu programů. Konkrétně napíšeme program pro výpočet největšího společného dělitele dvou celých čísel bez znaménka (tedy typu CARD):
CARD FUNC Gcd(CARD a,b)
CARD tmp
IF a<b THEN
tmp=a a=b b=tmp
FI
WHILE b<>0
DO
tmp=a MOD b
a=b
b=tmp
OD
RETURN(a)
PROC Test(CARD a,b)
CARD res
res=Gcd(a,b)
PrintF("GCD of %I and %I is %I%E",a,b,res)
RETURN
PROC Main()
Test(48,18)
Test(9360,12240)
Test(17,19)
Test(123,1)
Test(0,0)
RETURN
Podrobnosti si postupně vysvětlíme, ovšem samotný zápis programu by měl být velmi dobře čitelný.
Praktická část
Studium jakéhokoli programovacího jazyka pochopitelně vyžaduje i praktické otestování všech (nebo většiny) možností, které tento programovací jazyk a jeho vývojové prostředí programátorům nabízí. Z tohoto důvodu si ve druhé části dnešního článku ukážeme několik příkladů naprogramovaných právě v jazyku Action! Bude nás přitom zajímat nejenom to, zda bude výsledný program funkční, ale i způsob jeho překladu do strojového kódu (tedy korespondence mezi vysokoúrovňovými konstrukcemi na straně jedné a nízkoúrovňovým strojovým kódem na straně druhé). Musíme si totiž ověřit, do jaké míry byly pravdivé dobové chvály na kvalitu a rychlost přeložených programů. Nejdříve si ukážeme jednoduché příklady bez programových smyček a dalších složitějších programových konstrukcí, ovšem později nezapomeneme ani na tyto důležité části jazyka Action!
Program typu „Hello world!“
První program, který vytvoříme, bude již tradičně vypisovat na obrazovku řetězec „Hello, world!“. V jazyku Action! bude tento program vypadat následovně:
PROC HELLO()
PRINTE("Hello, world!")
RETURN
Název procedury může být jakýkoli a nesmíme zapomenout na ukončení těla procedury klíčovým slovem RETURN (proto, že se jedná o ukončení definice, není toto klíčové slovo odsazeno). A PRINTE je knihovní funkce jazyka Action! Pokud se taková funkce v programu používá, je generování výsledného strojového kódu složitější, protože musíme provést fázi linkingu. S podrobnostmi se seznámíme příště.
Obrázek 3: První reálný program, který vypíše řetězec na obrazovku.
Překlad a spuštění programu
Překlad a spuštění programu se provede následující sekvencí operací:
- Stiskem Ctrl+Shift+M se přejde do Monitoru. Obrazovka se změní, protože na horním řádku bude zobrazena příkazová řádka
- Zapíšeme příkaz COMPILE nebo jen C. Nesmí se zobrazit žádné hlášení o chybě!
- Zapíšeme příkaz RUN nebo jen R. Tím se program spustí
Výsledek by měl vypadat takto:
Obrázek 4: Překlad a spuštění programu v monitoru.
Po ukončení programu se vrátíme zpět do editoru příkazem EXIT nebo jen E.
Uložení zdrojového kódu na disketu, kazetu či RAMdisk se provede pomocí Ctrl+Shift+W a zadáním jména souboru:
Obrázek 5: Zápis zdrojového kódu na záznamové zařízení.
Program pro nastavení barvy pozadí
Druhý program psaný v jazyku Action!, který si v dnešním článku ukážeme, bude zaměřen na nízkoúrovňové operace. Konkrétně v něm provedeme modifikaci pozadí obrazovky zápisem do stínového barvového registru čipu GTIA uloženého na adrese $02C6 neboli 710. Využijeme zde faktu, že při deklaraci proměnné lze určit i její adresu, takže se vlastně zcela vynechá manipulace s registry, funkce typu POKE atd. To je skutečně mocná zbraň jazyka Action!:
PROC COLOR() BYTE PLAYFIELD=710 PLAYFIELD=42 RETURN
Obrázek 6: Program, který změní barvu pozadí herního pole.
Alternativně je pochopitelně možné adresu stínového registru zapsat hexadecimální hodnotou (tato možnost citelně chybí v Atari BASICu):
PROC COLOR() BYTE PLAYFIELD=$02C6 PLAYFIELD=42 RETURN
Obrázek 7: Alternativní zápis programu s využitím hexadecimální adresy.
Obrázek 8: Po spuštění programu v monitoru se skutečně změní barva pozadí herního pole.
Uložení přeloženého programu s jeho následnou analýzou
V monitoru (Ctrl+Shift M) je možné příkazem W zařízení:soubor uložit strojový kód, který vznikl překladem. To nám umožní analýzu toho, jakým způsobem překladač jazyka Action! dokáže přeložit zapsané programové konstrukce. Po vstupu do monitoru provedeme překlad příkazem C (již známe) a příkazem W: H:OBJ (v emulátoru) bude strojový kód zapsán do souboru nazvaného jednoduše OBJ (prozatím je dobré používat v názvech souborů velké znaky). Na reálném Atari je pochopitelně možný zápis na další typy zařízení, včetně diskety či kazety.
Vzniklý soubor obsahuje hlavičku o velikosti deseti bajtů, kterou musíme přeskočit. Odstraníme ji (už v Linuxu, ne na Atari) například takto:
$ tail -c +10 OBJ > OBJ2
Výsledkem činnosti předchozího příkazu bude soubor OBJ2, ve kterém bude chybět prvních deset bajtů z původního souboru. Nyní již můžeme použít disassembler ca65, který byl nainstalován společně s assemblerem ca65 a překladačem céčka cc65 v rámci úvodního článku tohoto seriálu:
$ da65 OBJ2
Výsledek bude vypadat následovně:
; da65 V2.18 - Fedora 2.19-12.fc42
; Created: 2026-09-04 11:37:39
; Input file: OBJ2
; Page: 1
.setcpu "6502"
L000E := $000E
lda #$2A
sta $02C6
rts
rts
.byte $E2
.byte $02
.byte $E3
.byte $02
bvs L000E
Povšimněte si, že samotný program (tři instrukce začínající na návěští L000E) je zapsán přesně tak, jako bychom to udělali ručně v assembleru. V praxi bychom pravděpodobně namísto instrukce RTS použili instrukci BRK, ale to má v tomto případě podobný význam.
Zobrazení spritu s nastavením jeho horizontální pozice a barvy
Předchozí demonstrační příklad byl dosti triviální, takže se nyní pokusme o realizaci poněkud složitějšího programu. Konkrétně se bude jednat o program, po jehož spuštění se na obrazovce vykreslí sprite – první hráč. Prozatím pro jednoduchost pouze nastavíme globální masku hráče a nikoli jeho bitmapu. To znamená, že hráč bude vykreslen formou vertikálního sloupce o šířce šestnácti pixelů (šířka pixelu spritu je totiž dvojnásobná oproti šířce pixelu v nejjemnějším grafickém režimu GR.8).
V assembleru by celý program mohl vypadat například následovně. Jedná se vlastně o zápis do několika řídicích registrů popř. po stínových registrů:
.proc main
lda #128 ; horizontální pozice hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #HUE_MAGENTA<<4 + 8 ; barva hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #$ff ; bitová maska hráče
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
lda #0 ; bitové pole: vše nulové
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
Přepis z assembleru do programovacího jazyka Action! je v tomto případě poměrně jednoduchý. Postačuje nám provést deklaraci proměnných typu BYTE (každý řídicí nebo stínový registr má šířku jednoho bajtu) se specifikací adresy těchto proměnných v paměti. Následně se pouze provede zápis konstant (půjde ovšem skutečně o konstanty) do těchto proměnných:
PROC SPRITE()
CARD GTIA=$D000
BYTE HPOSP0=$D000
BYTE PCOLR0=$02C0
BYTE GRAFP0=$D00D
BYTE GRACTL=$D010
HPOSP0=128
PCOLR0=5 LSH 4+8
GRAFP0=$FF
GRACTL=0
RETURN
Tento program můžeme běžným způsobem (v monitoru) přeložit a spustit. Obrazovka by po spuštění našeho programu měla mít následující obsah:
Obrázek 9: Spuštění programu, který vykreslí sprite (prvního hráče).
Opět se pokusme postupem popsaným v předchozí kapitole získat zpětný překlad ze strojového kódu do čitelnějšího assembleru:
; da65 V2.18 - Fedora 2.19-12.fc42
; Created: 2026-09-05 11:29:45
; Input file: SPRITES1
; Page: 1
.setcpu "6502"
lda #$80
sta $D000
lda #$05
asl a
asl a
asl a
asl a
sta $AE
clc
lda $AE
adc #$08
sta $02C0
lda #$FF
sta $D00D
ldy #$00
sty $D010
rts
rts
.byte $E2
.byte $02
.byte $E3
.byte $02
rol $0F
Tento kód již není tak pěkný, jako tomu bylo v předchozím příkladu. Je tomu tak z toho důvodu, že jazyk Action! počítá kód barvy v runtime a nikoli už při překladu (ale opět – nelze asi očekávat zázraky). Kód lze rozdělit na čtyři části:
Nastavení horizontální pozice hráče:
lda #$80
sta $D000
Nastavení barvy hráče:
lda #$05
asl a
asl a
asl a
asl a
sta $AE
clc
lda $AE
adc #$08
sta $02C0
Nastavení bitové masky hráče:
lda #$FF
sta $D00D
Nastavení registru GRACTL:
ldy #$00
sty $D010
Horizontální pohyb spritu s čekáním na dokončení zobrazení snímku
V dalším demonstračním příkladu hráče (sprite) „rozpohybujeme“. V nekonečné programové smyčce budeme postupně zvyšovat hodnotu bajtu obsahujícího horizontální pozici hráče. Tuto hodnotu v každé iteraci smyčky zapíšeme do registru HPOSP0 čipu GTIA:
BYTE HPOSP0=$D000
BYTE X
X=128
DO
X=X+1
HPOSP0=X
OD
Pohyb hráče je však příliš rychlý (ano – Atari může být někdy až moc rychlé) a proto do smyčky zařadíme volání procedury WSYNC čekající na začátek vykreslení nového snímku:
BYTE HPOSP0=$D000
BYTE X
X=128
DO
X=X+1
HPOSP0=X
WSYNC()
OD
Samotná procedura WSYNC detekuje změnu hodnoty reprezentující hodiny „tikající“ na základě zobrazených snímků:
PROC WSYNC()
BYTE CLK=$14
BYTE OLD
OLD=CLK
WHILE OLD=CLK
DO
OD
RETURN
Následuje výpis celého zdrojového kódu tohoto demonstračního příkladu:
PROC WSYNC()
BYTE CLK=$14
BYTE OLD
OLD=CLK
WHILE OLD=CLK
DO
OD
RETURN
PROC SPRITE()
BYTE X
CARD GTIA=$D000
BYTE HPOSP0=$D000
BYTE PCOLR0=$02C0
BYTE GRAFP0=$D00D
BYTE GRACTL=$D010
HPOSP0=128
PCOLR0=5 LSH 4+8
GRAFP0=$FF
GRACTL=0
X=128
DO
X=X+1
HPOSP0=X
WSYNC()
OD
RETURN
Obrázek 10: Program pro realizaci automatického postupného posunu spritu po obrazovce.
Analýza strojového kódu získaného překladem
Opět se podíváme na to, jakým způsobem byl demonstrační příklad z předchozí kapitoly přeložen do strojového kódu:
; da65 V2.18 - Fedora 2.19-12.fc42
; Created: 2026-09-05 11:29:49
; Input file: S222
; Page: 1
.setcpu "6502"
L105E := $105E
L1066 := $1066
L1073 := $1073
L1078 := $1078
L109C := $109C
lda $14
sta $105D
lda $105D
eor $14
beq LFFBD
jmp L1073
LFFBD: jmp L1066
rts
brk
jmp L1078
lda #$80
sta $D000
lda #$05
asl a
asl a
asl a
asl a
sta $AE
clc
lda $AE
adc #$08
sta $02C0
lda #$FF
sta $D00D
ldy #$00
sty $D010
lda #$80
sta $1074
inc $1074
lda $1074
sta $D000
jsr L105E
jmp L109C
rts
rts
.byte $E2
.byte $02
.byte $E3
.byte $02
adc $10,x
Detekce změny hodin (tedy vykreslení celého snímku) je realizována velmi pěkně:
lda $14
sta $105D
lda $105D
eor $14
beq LFFBD
jmp L1073 ; skok na instrukci RTS
LFFBD: jmp L1066 ; skok na začátek procedury
rts
Konfiguraci spritu již známe:
Nastavení horizontální pozice hráče:
lda #$80
sta $D000
Nastavení barvy hráče:
lda #$05
asl a
asl a
asl a
asl a
sta $AE
clc
lda $AE
adc #$08
sta $02C0
Nastavení bitové masky hráče:
lda #$FF
sta $D00D
Nastavení registru GRACTL:
ldy #$00
sty $D010
Následuje nastavení proměnné X:
lda #$80
sta $1074
A konečně můžeme ve výpisu provedeném disassemblerem vidět i vlastní programovou smyčku, ve které se hráč posunuje doprava (INC) a volá se subrutina WSYNC (to je ona instrukce JSR):
inc $1074
lda $1074
sta $D000
jsr L105E
jmp L109C
Horizontální pohyb spritu řízený joystickem
V posledním rozsáhlejším příkladu, který si dnes ukážeme, je pohyb spritu (pro jednoduchost jen v horizontálním směru) řízen joystickem. To je téma, které již známe, takže si jen v krátkosti připomeňme, že na adrese $0278 je uložen kód reprezentující náklon prvního joysticku:
Decimálně Binárně
14 1110
| |
10 | 6 1010 | 0110
\ |/ \ |/
11-- 15 ---7 1011-- 1111 --0111
/ |\ / |\
9 | 5 1001 | 0101
| |
13 1101
My tedy budeme rozpoznávat pouze kódy 11 a 7. Realizace posunu hráče je vlastně velmi jednoduchá a omezuje se na programovou konstrukci IF-THEN-FI:
IF STICK0=11 THEN
X=X-1
FI
IF STICK0=7 THEN
X=X+1
FI
Samotná programová smyčka, ve které se neustále kontroluje stav joysticku a při jeho náklonu doprava nebo doleva se hráč posune (s čekáním na další snímek), vypadá následovně:
BYTE HPOSP0=$D000
BYTE STICK0=$0278
X=128
DO
IF STICK0=11 THEN
X=X-1
FI
IF STICK0=7 THEN
X=X+1
FI
HPOSP0=X
WSYNC()
OD
Zařazení upravené smyčky do zdrojového kódu demonstračního příkladu je již snadné:
PROC WSYNC()
BYTE CLK=$14
BYTE OLD
OLD=CLK
WHILE OLD=CLK
DO
OD
RETURN
PROC SPRITE()
BYTE X
CARD GTIA=$D000
BYTE HPOSP0=$D000
BYTE PCOLR0=$02C0
BYTE GRAFP0=$D00D
BYTE GRACTL=$D010
BYTE STICK0=$0278
HPOSP0=128
PCOLR0=5 LSH 4+8
GRAFP0=$FF
GRACTL=0
X=128
DO
IF STICK0=11 THEN
X=X-1
FI
IF STICK0=7 THEN
X=X+1
FI
HPOSP0=X
WSYNC()
OD
RETURN
Po překladu a spuštění (opět v monitoru) je možné sprite prvního hráče programově ovládat:
Obrázek 11: Pohyb spritu po obrazovce.
Obrázek 12: Pohyb spritu po obrazovce po spuštění programu.
Výsledek překladu demonstračního příkladu do strojového kódu
Nejprve se podívejme, jak byl příklad z předchozí kapitoly přeložen a posléze si vysvětlíme jeho jednotlivé části:
; da65 V2.18 - Fedora 2.19-12.fc42
; Created: 2026-09-04 19:31:00
; Input file: YYY
; Page: 1
.setcpu "6502"
L10F0 := $10F0
L10F8 := $10F8
L1105 := $1105
L110A := $110A
L112E := $112E
L1141 := $1141
L114E := $114E
LFF37 := $FF37
lda $14
sta $10EF
lda $10EF
eor $14
beq LFFA0
jmp L1105
LFFA0: jmp L10F8
rts
brk
jmp L110A
lda #$80
sta $D000
lda #$05
asl a
asl a
asl a
asl a
sta $AE
clc
lda $AE
adc #$08
sta $02C0
lda #$FF
sta $D00D
ldy #$00
sty $D010
lda #$80
sta $1106
lda $0278
eor #$0B
beq LFFD6
jmp L1141
LFFD6: sec
lda $1106
sbc #$01
sta $1106
lda $0278
eor #$07
beq LFFE9
jmp L114E
LFFE9: inc $1106
lda $1106
sta $D000
jsr L10F0
jmp L112E
rts
rts
.byte $E2
.byte $02
.byte $E3
.byte $02
.byte $07
.byte $11
První část instrukcí již dobře známe – jedná se o implementaci čekání na „tik“ hodin:
lda $14
sta $10EF
lda $10EF
eor $14
beq LFFA0
jmp L1105
LFFA0: jmp L10F8
rts
I další část známe – nastavení čtyř bajtů s barvou, tvarem a horizontální pozicí hráče a povolení PMG:
; horizontální pozice hráče
lda #$80
sta $D000
; barva hráče
lda #$05
asl a
asl a
asl a
asl a
sta $AE
clc
lda $AE
adc #$08
sta $02C0
; bitová maska hráče
lda #$FF
sta $D00D
; nastavení registru GRACTL
ldy #$00
sty $D010
Následuje inicializace proměnné X na hodnotu $80:
lda #$80
sta $1106
Nové je čtení registru s kódem náklonu joysticku a přičtení či odečtení jedničky od proměnné X:
lda $0278
eor #$0B ; 11=doleva
beq LFFD6
jmp L1141
LFFD6: sec
lda $1106
sbc #$01 ; X=X-1
sta $1106
lda $0278
eor #$07 ; 7=doprava
beq LFFE9
jmp L114E
LFFE9: inc $1106 ; X=X+1
Posun spritu s čekáním na vykreslení snímku:
lda $1106
sta $D000
jsr L10F0
jmp L112E
Překlad operace inkrementace hodnoty pro proměnné různých typů
V dalším ukázkovém programu si ověříme způsob realizace zdánlivě triviální operace přičtení jedničky k obsahu proměnných typu BYTE, CARD a INT. Ve skutečnosti není tato inkrementace pro šestnáctibitové hodnoty zcela jednoduchá, takže otestujeme, jak bude vypadat sekvence instrukcí vytvořených překladačem jazyka Action! pro všechny tři varianty inkrementace:
PROC MAIN()
BYTE X
CARD Y
INT Z
X=1
Y=60000
Z=30000
X=X+1
Y=Y+1
Z=Z+1
RETURN
Nejprve si uvedeme výpis disassemblovaného kódu tak, jak ho provedl nástroj da65:
; da65 V2.18 - Fedora 2.19-12.fc42
; Created: 2026-09-04 20:06:07
; Input file: INC
; Page: 1
.setcpu "6502"
L0ED8 := $0ED8
brk
brk
jmp L0ED8
ldy #$01
sty $0ED0
lda #$EA
sta $0ED2
lda #$60
sta $0ED1
lda #$75
sta $0ED4
lda #$30
sta $0ED3
inc $0ED0
inc $0ED1
bne LFFF0
inc $0ED2
LFFF0: inc $0ED3
bne LFFF8
inc $0ED4
LFFF8: rts
rts
.byte $E2
.byte $02
.byte $E3
.byte $02
cmp $0E,x
Sekvenci instrukcí, které jsme disassemblerem získali, je možné rozdělit do bloků podle toho, jaký příkaz realizují (pochopitelně se neprovádí žádné složité optimalizace, takže si to můžeme dovolit):
X=1
ldy #$01
sty $0ED0
Y=60000
; 60000=$EA60
lda #$EA
sta $0ED2
lda #$60
sta $0ED1
Z=30000
; 3000=$7530
lda #$75
sta $0ED4
lda #$30
sta $0ED3
X=X+1
inc $0ED0
Y=Y+1
inc $0ED1
bne LFFF0
inc $0ED2
LFFF0:
Z=Z+1
inc $0ED3
bne LFFF8
inc $0ED4
LFFF8:
Překlad operace pro posun doprava pro proměnné různých typů
Podobným postupem, jaký byl zvolen v předchozí kapitole, můžeme zjistit způsob překladu operace RSH, tj. bitového posunu doprava. Posun bajtu lze realizovat jedinou instrukcí, ovšem v případě dvoubajtových hodnot to již není tak jednoduché. A situace je ještě komplikovanější pro šestnáctibitové hodnoty se znaménkem:
PROC MAIN()
BYTE X
CARD Y
INT Z
X = 10
Y = 10
Z = 10
X = X RSH 1
Y = Y RSH 1
Z = Z RSH 1
RETURN
Výsledkem je následující sekvence instrukcí:
; da65 V2.18 - Fedora 2.19-12.fc42
; Created: 2026-09-04 22:12:17
; Input file: YYY
; Page: 1
.setcpu "6502"
L0EFC := $0EFC
ora $00
jmp L0EFC
lda #$0A
sta $0EF4
ldy #$00
sty $0EF6
lda #$0A
sta $0EF5
sty $0EF8
lda #$0A
sta $0EF7
lsr $0EF4
lsr $0EF6
ror $0EF5
lsr $0EF8
ror $0EF7
rts
rts
.byte $E2
.byte $02
.byte $E3
.byte $02
.byte $F9
.byte $0E
Instrukce si opět rozdělíme podle toho, jaký příkaz v jazyku Action! implementují:
X = 10
lda #$0A
sta $0EF4
Y = 10
ldy #$00
sty $0EF6
lda #$0A
sta $0EF5
Z = 10
ldy #$00
sty $0EF8
lda #$0A
sta $0EF7
X = X RSH 1
lsr $0EF4
Y = Y RSH 1
; dolní bajt je nutné rotovat - z levé strany se totiž nasunuje příznak Carry
lsr $0EF6
ror $0EF5
Z = Z RSH 1
; tato operace ve skutečnosti nezachová znaménko a vlastně postrádá smysl
; protože původní znaménko ovlivní výslednou hodnotu
; dolní bajt je nutné rotovat - z levé strany se totiž nasunuje příznak Carry
lsr $0EF8
ror $0EF7
Překlad operace pro posun doleva o větší počet bitů
Mikroprocesor MOS 6502 sice podporuje instrukce pro bitové posuny a rotace, ovšem neobsahuje plnohodnotný barrel shifter a tudíž se posuny mohou provádět o jediný bit. Bude tedy zajímavé zjistit, jakým způsobem se vlastně realizuje operace LSH, která umožňuje provést posun doleva o n bitů. A jak je již v tomto článku zvykem, budeme posouvat hodnoty proměnných typu BYTE, CARD i INT, tedy jak hodnoty osmibitové, tak i šestnáctibitové:
PROC MAIN()
BYTE X
CARD Y
INT Z
X=1
Y=1
Z=1
X=X LSH 5
Y=Y LSH 5
Z=Z LSH 5
RETURN
Výsledek analýzy strojového kódu vygenerovaného překladačem jazyka Action!:
; da65 V2.18 - Fedora 2.19-12.fc42
; Created: 2026-09-04 20:15:26
; Input file: YYY
; Page: 1
.setcpu "6502"
L0EEC := $0EEC
LB5C0 := $B5C0
brk
brk
jmp L0EEC
ldy #$01
sty $0EE4
dey
sty $0EE6
iny
sty $0EE5
dey
sty $0EE8
iny
sty $0EE7
lda $0EE4
asl a
asl a
asl a
asl a
asl a
sta $0EE4
lda #$05
sta $84
lda $0EE6
tax
lda $0EE5
jsr LB5C0
sta $0EE5
txa
sta $0EE6
lda #$05
sta $84
lda $0EE8
tax
lda $0EE7
jsr LB5C0
sta $0EE7
txa
sta $0EE8
rts
rts
.byte $E2
.byte $02
.byte $E3
.byte $02
sbc #$0E
Opět se pokusme tuto sekvenci instrukcí rozdělit tak, aby to odpovídalo původním příkazům:
X=1
ldy #$01
sty $0EE4
Y=1
dey
sty $0EE6
iny
sty $0EE5
Z=1
dey
sty $0EE8
iny
sty $0EE7
X=X LSH 5
lda $0EE4
asl a
asl a
asl a
asl a
asl a
sta $0EE4
Y=Y LSH 5
lda #$05
sta $84
lda $0EE6
tax
lda $0EE5
jsr LB5C0
sta $0EE5
txa
sta $0EE6
Z=Z LSH 5
lda #$05
sta $84
lda $0EE8
tax
lda $0EE7
jsr LB5C0
sta $0EE7
txa
sta $0EE8
Překlad součinu obsahu proměnné s konstantou
Mikroprocesor MOS 6502 nepodporuje instrukci pro výpočet součinu. Tuto operaci je tedy nutné rozložit na jednodušší operace, konkrétně na bitové posuny a součty. Ověřme si, jak vypadá sekvence instrukcí vygenerovaná překladačem jazyka Action! pro příkazy, které vynásobí obsahy proměnných typu BYTE, CARD a INT konstantou pět:
PROC MAIN()
BYTE X
CARD Y
INT Z
X=42
Y=42
Z=42
X=X*5
Y=Y*5
Z=Z*5
RETURN
Překlad do strojového kódu dopadne následovně:
; da65 V2.18 - Fedora 2.19-12.fc42
; Created: 2026-09-04 20:06:03
; Input file: YYY2
; Page: 1
.setcpu "6502"
L0ED8 := $0ED8
LA000 := $A000
brk
brk
jmp L0ED8
lda #$2A
sta $0ED0
ldy #$00
sty $0ED2
lda #$2A
sta $0ED1
sty $0ED4
lda #$2A
sta $0ED3
lda #$00
sta $85
lda #$05
sta $84
lda $0ED0
ldx #$00
jsr LA000
sta $0ED0
lda #$00
sta $85
lda #$05
sta $84
lda $0ED2
tax
lda $0ED1
jsr LA000
sta $0ED1
txa
sta $0ED2
lda #$00
sta $85
lda #$05
sta $84
lda $0ED4
tax
lda $0ED3
jsr LA000
sta $0ED3
txa
sta $0ED4
rts
rts
.byte $E2
.byte $02
.byte $E3
.byte $02
cmp $0E,x
A opět – dnes již naposledy – si tuto sekvenci instrukcí rozložíme tak, aby jednotlivé bloky odpovídaly původním příkazům:
X=42
lda #$2A
sta $0ED0
Y=42
ldy #$00
sty $0ED2
lda #$2A
sta $0ED1
Z=42
sty $0ED4
lda #$2A
sta $0ED3
X=X*5
lda #$00
sta $85
lda #$05
sta $84
lda $0ED0
ldx #$00
jsr LA000
sta $0ED0
Y=Y*5
lda #$00
sta $85
lda #$05
sta $84
lda $0ED2
tax
lda $0ED1
jsr LA000
sta $0ED1
txa
sta $0ED2
Z=Z*5
lda #$00
sta $85
lda #$05
sta $84
lda $0ED4
tax
lda $0ED3
jsr LA000
sta $0ED3
txa
sta $0ED4
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.atari800×l.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/800×l-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
file:///home/ptisnovs/Downloads/action-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!
