Tvorba her pro Atari 2600 v Batari BASICu: ovládání čipu TIA

3. 5. 2022
Doba čtení: 27 minut

Sdílet

 Autor: Depositphotos
Ve druhém článku o programování her a dem pro herní konzoli Atari 2600 (VCS) si popíšeme možnosti čipu TIA v oblasti zobrazování grafiky. Zaměříme se zejména na popis ovládání čipu TIA z Batari BASICu.

Obsah

1. Grafický subsystém herní konzole Atari 2600

2. Skládání obrazu herního světa ze šesti typů objektů

3. Podrobnější informace o grafických objektech

4. Jak jsou tedy vykresleny skutečné herní scény?

5. Využití možností emulátoru Stella pro „debugging“ grafiky

6. Práce s jednotlivými grafickými objekty v Batari BASICu

7. Herní pole (playfield)

8. Střely (missiles)

9. Šířka vykreslování střel

10. Priorita zobrazení střel vůči pozadí i střel vůči sobě

11. Detekce kolize střel

12. Míč (ball), šířka vykreslení míče

13. Shrnutí a ukázka z třetího článku – relativně složité demo využívajících necelé 2kB ROM

14. Repositář s demonstračními příklady

15. Odkazy na Internetu

1. Grafický subsystém herní konzole Atari 2600

V úvodní kapitole si ve stručnosti popíšeme princip generování grafického obrazu na herní konzoli Atari 2600, protože se jedná o vlastnost, která pravděpodobně nejvíc ovlivnila její úspěch (a na druhou stranu zapříčinila velké bolesti hlavy programátorům, kteří museli tento systém do nejmenších detailů pochopit – to ovšem není při použití Batari BASICu přesně náš příklad). Vzhledem k tomu, že kapacita operační paměti konzole Atari 2600 byla rovna pouhým 128 bajtům (byl zde mimochodem umístěn i zásobník), bylo zřejmé, že v tak malé paměti není možné vytvořit plnohodnotný framebuffer (obrazovou paměť), a to ani v případě, že by byl černobílý a měl nepatrné rozlišení. Steve Mayer a Ron Milner, kteří navrhovali prototyp této herní konzole, se tedy rozhodli, že obraz bude generován přímo v průběhu zobrazování herní scény na obrazovce monitoru.

Obrázek 1: Obraz generovaný čipem TIA s uvedením přibližného časování.

O generování obrazu se staral čip nazvaný TIA (Television Interface Adaptor), který využíval data dodávaná programem (většinou nějakou hrou) a ukládaná do řídicích režimů čipu TIA. Teoreticky je sice možné přímé řízení intenzity elektronového paprsku (či trojice paprsků u barevné obrazovky), to by však bylo realizovatelné pouze v případě velmi přesného časování – i malé časové odchylky při změně barvy paprsku by totiž znamenaly, že se jednotlivé obrazové řádky navzájem posunou, nehledě na to, že výpočetní výkon MOS 6507 nebyl pro tento účel ani zdaleka dostatečný).

Obrázek 2: Barvová paleta herní konzole Atari 2600 při použití televizního systému NTSC. Emulátor Stella podporuje jak NTSC, tak i PAL, NTSC přitom nabízí větší paletu barev.

Obvod TIA vytvářel všechny potřebné signály pro televizor, který zobrazoval 60 půlsnímků (v tomto odstavci popisuji původní verzi určenou pro televizní normu NTSC; PAL má odlišné časování i frekvence). Každý půlsnímek obsahoval 262 obrazových řádků, přičemž 192 řádků bylo viditelných, tři řádky byly použity pro vyslání signálu vertikální synchronizace, 37 řádků bylo využito pro vertikální zatemnění (vertical blank) a zbylých 30 řádků bylo většinou taktéž zatemněných, protože u některých televizorů již tyto řádky ležely mimo zobrazitelnou plochu (jedná se o takzvaný overscan, který ovšem bylo možné využít). Každý obrazový řádek (scanline) se skládal z 228 pixelů vykreslených v 76 strojových cyklech (frekvence krystalu byla dělena třemi). Z oněch 228 pixelů jich bylo viditelných pouze 160, ve zbývající době totiž probíhal návrat elektronového paprsku a taktéž zobrazení okrajů (horizontal blank).

Obrázek 3: Hra Outlaw se dočkala konverze snad na všechny počítačové platformy. Její princip je jednoduchý a přitom se jedná o zábavnou hru. Velikost ROM s touto hrou je pouze 2048 bajtů.

Úkolem každého programátora tedy bylo vhodně měnit obsah řídicích registrů čipu TIA takovým způsobem, aby tento čip zobrazil smysluplnou obrazovou informaci v imaginární bitmapě o rozměrech 160×192 pixelů. Pro každý snímek ovšem bylo k dispozici přibližně pouze 15000 strojových cyklů, v nichž se musela stihnout provést jak vykreslovací rutina, tak i vlastní algoritmus hry (posun hráče, posun střel, výpočet skóre atd. atd.). Tedy – úkol nikoli pro pojídače koláčů, ale pro skutečné programátory.

Obrázek 4: Snímek ze hry Combat při souboji dvou tanků na volném území (opět se jedná o hru uloženou v ROM s pouhými 2048 bajty).

2. Skládání obrazu herního světa ze šesti typů objektů

Každý obrazový řádek složený, jak jsme si již ostatně řekli v předchozí kapitole, ze 160 viditelných pixelů, byl pomocí čipu TIA generován na základě pouhé šestice údajů – horizontální pozice takzvaného hráče 0, pozice hráče 1 (jednalo se o klasické osmipixelové sprity, jejichž horizontální šířka mohla být vynásobena dvěma či čtyřmi, a které se mohly 2× či 3×na jediném řádku opakovat), pozice střely 0, pozice střely 1 (jednopixelové sprity, opět s volitelnou šířkou a barvou příslušného hráče), pozice míče/ball (jednopixelový sprite s možností vertikálního posunu o jeden řádek) a konečně herního pole/playfield.

pc6614

Obrázek 5: Hra Battlezone ve variantě určené pro herní konzoli Atari 2600 patřila mezi první hry, které se snažily navodit iluzi trojrozměrného prostoru. Zajímavé je, že původní „automatová“ verze této hry používala vektorový displej a traduje se, že existovala i speciální verze pro armádu USA (jednalo by se tak o jeden z prvních digitálních trenažérů).

Herní pole bylo reprezentováno největším objemem dat – 20bitovým vzorkem, který mohl být v pravé polovině obrazovky horizontálně zrcadlen nebo pouze zopakován – horizontální rozlišení pozadí je tedy rovno pouze 40 pixelům, tj. jeho pixely mají oproti hráčům čtyřnásobnou šířku. Programátor přitom mohl před vykreslením každého obrazového řádku změnit horizontální pozice hráčů, střel i míče, modifikovat jejich barvu, popř. i bitový vzorek hráčů a herního pole; navíc bylo možné určit priority zobrazení. Všechny změny byly prováděny zápisem do řídicích registrů čipu TIA, například herní pole bylo uloženo ve třech osmibitových registrech.

pc6615

Obrázek 6: Nechvalně proslulá „nehratelná“ hra E.T. určená pro herní konzoli Atari 2600.

Výše naznačený způsob generování rastrové grafiky sice na první pohled vypadá složitě (minimálně kvůli nárokům na přesnost časování při zápisu do jednořádkového bufferu pozadí), na druhou stranu však programátorům, kteří překonali prvotní potíže, přinášel poměrně velkou flexibilitu (osobně si myslím, že mnohem větší flexibilitu, než dnešní rigidní framebuffery :-). Zatímco jakákoli změna ve framebufferu (obrazové paměti) většinou znamenala nutnost přesunu relativně velkých bloků paměti, byla tvorba podobného efektu na systému bez framebufferu velmi jednoduchá a neznamenala větší zátěž pro mikroprocesor, než při generování statického obrázku. Proto jsou například hry určené pro herní konzoli Atari 2600 (která tímto systémem tvorby grafiky disponovala jako jeden z mála široce rozšířených systémů s mikroprocesorem) mnohdy plné pohybujících se objektů, protože mikroprocesor byl zatížen prakticky stejně jak při neustálém vytváření jedné neměnné scény, tak i při tvorbě scény, ve které se všechny objekty pohybují.

Obrázek 7: Další režim hry Combat – soubor dvou dvouplošníků.

Poznámka: možná by bylo zajímavé a poučné podobný grafický subsystém vytvořit s využitím soudobých komponent (stačilo by pravděpodobně jednoduché FPGA), protože dnešní mikroprocesory mají více než dostatečný výpočetní výkon pro programovou tvorbu grafiky a dobře navržený systém přerušení (popř. jednoduchý jednořádkový framebuffer doplněný o sprity) by mohl zajistit potřebnou kooperaci mezi podprogramem určeným pro vykreslování a zbylými aplikacemi.

Obrázek 8: Úvodní obrazovka hry Breakout je současně i jedinou obrazovkou ve hře. Větší počet barev evidentně pro čip TIA nepředstavuje problém.

3. Podrobnější informace o grafických objektech

V následující tabulce jsou pro přehled vypsány základní informace o všech šesti grafických objektech použitých pro postupné vykreslování obrazu na televizoru. K těmto objektům jsem ještě přidal sedmý objekt – pozadí. Tento objekt je definován pouze vybranou barvou a nikoli bitovým vzorkem (a jak jeho název napovídá, vždy se nachází až za ostatními objekty):

# Typ objektu Orig.název Objem paměti Šířka reprezentovaná jedním bitem
1 Pozadí Background 0 bitů ×
2 Hrací plocha Playground 20 bitů 4× základní šířka pixelu
3 Míč Ball 1 bit 1×, 2×, 4×, 8× šířka pixelu
4 Hráč 0 Player 0 8 bitů 1×, 2×, 4× šířka pixelu
5 Střela 0 Missile 0 1 bit 1×, 2×, 4×, 8× šířka pixelu
6 Hráč 1 Player 1 8 bitů 1×, 2×, 4× šířka pixelu
7 Střela 1 Missile 1 1 bit 1×, 2×, 4×, 8× šířka pixelu

Obrázek 9: Jedna z variant herní konzole Atari 2600.

Ve druhé tabulce jsou vypsány vlastnosti jednotlivých grafických objektů, které bylo možné měnit zápisem vhodných hodnot do řídicích registrů čipu TIA:

# Typ objektu Orig.název Volba barvy Další možnosti
1 Pozadí Background ano ×
2 Hrací plocha Playground pro jedničkové bity opakování či zrcadlení v pravé polovině obrazu
3 Míč Ball = barva hrací plochy posun, relativní posun, vertikální posun o jeden řádek, změna velikosti
4 Hráč 0 Player 0 pro jedničkové bity posun, změna šířky, opakování 2×, 3×
5 Střela 0 Missile 0 = barva hráče 0 -//-
6 Hráč 1 Player 1 pro jedničkové bity posun, změna šířky, opakování 2×, 3×
7 Střela 1 Missile 1 = barva hráče 1 -//-

Obrázek 10: Další varianta herní konzole Atari 2600.

4. Jak jsou tedy vykresleny skutečné herní scény?

Základem každé hry nebo dema určeného pro herní konzoli Atari 2600 je takzvaný (vykreslovací) kernel. Jedná se o podprogram, který pro každý vykreslovaný řádek naplní obsah řídicích registrů čipu TIA takovým způsobem, aby se celý řádek vykreslil podle potřeb programátora. Ve skutečnosti však nebývá snadné provést všechny výpočty pro každý programový řádek zvlášť, protože pro každý řádek je k dispozici pouhých 76 strojových cyklů (228/3) a instrukce mnohdy trvají 2 až 3 cykly, některé instrukce však i šest cyklů. Proto mnohé kernely modifikují registry každý druhý obrazový řádek a tudíž mají delší čas „na rozmyšlenou“. Tento princip je podporován i čipem TIA, který některé objekty dokáže vykreslit se zpožděním jednoho obrazového řádku. V extrémním případě – pokud nedojde ke změně žádného řídicího registru v průběhu vykreslení snímku – se každý objekt zobrazí na všech obrazových řádcích stejně – stejný tvar, stejné sloupce.

Poznámka: na konci každého snímku je sedmdesát řádků, které se v typických případech nevykreslují (3 řádky pro VSYNC, 37 řádků pro VBLANK a 30 řádků pro overscan). Máme tedy k dispozici 70×76=5320 dalších strojových cyklů, během nichž se typicky přepočítá celý herní svět, nastaví se řídicí registry zvukového subsystému atd.

Podívejme se na příklad kernelu použitého ve hře Pitfall:

Obrázek 11: Herní scéna ze hry Pitfall.

Nyní se podívejme, jak vypadá herní scéna ve chvíli, kdy budeme ignorovat modifikace barvových registrů a každému grafickému objektu přiřadíme konstantní barvu. Hráč v herní scéně je vykreslen hráčem číslo 1, ostatní protivníci hráčem číslo 0. Zajímavé je, že hráč číslo 0 i 1 je navíc využit i pro dokreslení stromů (rozvětvení pod listy), aby se odstranilo omezené horizontální rozlišení herního pole. Zajímavé je, že se zde tvůrci konkrétně na této obrazovce obešli bez použití střel. Povšimněte si taktéž liány. Ta je vykreslena jako sekvence míčů (ball), přičemž se postupně mění horizontální poloha těchto grafických objektů (což je vlastně mnohem rychlejší, než kdyby se měl neustále překreslovat framebuffer):

Obrázek 12: A takto jsou využity jednotlivé grafické objekty.

Podobně můžeme prozkoumat herní scénu ze hry River Raid:

Obrázek 13: Herní scéna ze hry River Raid.

V „ladicím“ zobrazení můžeme vidět, že všechny objekty ve scéně (kromě samotného hráče) jsou zobrazeny jako jediný sprite. Objekty jsou z tohoto důvodu postaveny takovým způsobem, že se nikdy vertikálně nepřekrývají – na každém obrazovém řádku je jen jediný objekt. Míč je použit pro zobrazení ukazatele paliva, střely pak pro skutečné střely (zde nejsou vidět, protože nedokážu udělat screenshot a současně držet „trigger“ joysticku):

Obrázek 14: A takto jsou využity jednotlivé grafické objekty.

5. Využití možností emulátoru Stella pro „debugging“ grafiky

V předchozí kapitole jsme mohli vidět, že herní scénu lze v emulátoru Stella zobrazit dvěma různými způsoby – buď tak, jako ji vidí běžný hráč (uživatel) herní konzole a poté způsobem, který jednotlivé objekty, z nichž se herní svět skládá, zobrazí každý odlišnou barvou. Navíc je i herní pole rozděleno na tři části, protože je definováno trojicí registrů. Tento způsob zobrazení tedy zcela ignoruje zápisy do registrů COLUPF, COLUP0 a COLUP1 a dovoluje nám nahlédnout do tajností tvorby herních scén, které mnohdy překvapují počtem barev a/nebo počtem pohyblivých objektů ve scéně. Podívejme se nyní na některé typické příklady.

Obrázek 15: Scéna ze hry H.E.R.O. jak ji vidí hráč.

„Ladicí“ způsob zobrazení stejné herní scény:

Obrázek 16: A takto je scéna rozdělena na sprity a herní pole.

Zajímavé bude zjistit, jak jsou zobrazeni protivníci ve slavné hře Space Invaders:

Obrázek 17: Slavní Space Invaders.

Tajemství je jednoduché – každý sprite je vždy zobrazen třikrát, což je ostatně podporováno přímo čipem TIA:

Obrázek 18: Jednotliví útočníci jsou tvořeni dvojicí spritů. Zajímavé je, že skóre je zobrazeno změnou herního pole.

Vraťme se ještě jednou ke hře Pitfall, tentokrát k jiné obrazovce:

Obrázek 19: Další slavná hra: Pitfall.

Jak klády, tak i zeď je tvořena stejným spritem, pokaždé ovšem s odlišnou barvou:

Obrázek 20: Ve scéně se používá pozadí, dvojice spritů a žebřík je kupodivu zobrazen s využitím míče.

Ve hře Ms. Pacman se pohybuje pět na sobě nezávislých objektů, které se dokonce mohou překrývat:

Obrázek 21: Ms.Pacman s pěti pohybujícími se objekty, každým v odlišné barvě.

„Ladicí“ způsob zobrazení stejné herní scény:

Obrázek 22: Ve skutečnosti se používají pouze dva sprity, u nichž se vhodně mění tvar a barva. Body, po jejichž konzumaci je možné duchy honit, jsou zobrazeny s využitím míčů.

A konečně jeden z prvním leteckých simulátorů viditelný z pozice pilota:

Obrázek 23: Air Raiders jsou pravděpodobně první hrou s naklonitelným horizontem.

„Ladicí“ způsob zobrazení stejné herní scény ukazuje tajemství nakloněného horizontu:

Obrázek 24: Stella odhalí, jak je tento horizont vytvořen – vhodným nastavením herního pole.

6. Práce s jednotlivými grafickými objekty v Batari BASICu

Batari BASIC obsahuje několik grafických kernelů. Prozatím budeme používat základní kernel, v němž se přímo i nepřímo pracuje se všemi šesti grafickými objekty čipu TIA. Vlastnosti těchto objektů se nastavují buď přístupem do řídicích registrů a/nebo nastavením speciálních proměnných a v případě pozadí i hráčů použitím specializovaných konstrukcí. Prakticky vše, co potřebujeme znát pro zobrazení grafických scén, je shrnuto v následující tabulce:

Typ grafického objektu Barva Další řídicí registry Speciální proměnné/konstrukce
pozadí COLUBK × ×
herní pole COLUPF PF0, CTRLPF Playground
hráč 0 COLUP0 NUSIZ0, REFP0, CTRLPF player0, player0×, player0y
hráč 1 COLUP1 NUSIZ1, REFP1, CTRLPF player1, player1×, player1y
střela 0 COLUP0 NUSIZ0, CTRLPF missile0×, missile0y, missile0height
střela 1 COLUP1 NUSIZ1, CTRLPF missile1×, missile1y, missile1height
míč COLUPF CTRLPF ballx, bally, ballheight
Poznámka: vidíme, že některé registry ovlivňují prakticky všechny typy grafických objektů. Zejména se to týká registru CTRLPF, v němž se kromě dalších věcí nastavují i priority zobrazení objektů.

7. Herní pole (playfield)

S konceptem herního pole jsme se již seznámili v úvodním článku o Batari BASICu. Připomeňme si, že při použití standardního kernelu Batari BASICu je možné pole definovat bitmapou o rozměrech 32×11 „pixelů“, přičemž šířka pixelů herního pole je čtyřikrát větší, než šířka hráčů nebo střel (při použití základního zvětšení); toto rozlišení ve směru horizontální osy není možné nijak měnit. V předchozím textu jsme si však řekli, že herní pole je definováno 20bitovým vzorkem, který se může buď 2× opakovat nebo být zrcadlen okolo vertikální osy, což znamená, že horizontální rozlišení by mělo být rovno 40 pixelům a nikoli jen 32 pixelům. Ovšem kvůli omezení vykreslovacího kernelu (časování atd.) je využito jen 80% herního pole, což přesně odpovídá 32 pixelům. Výška resp. přesněji řečeno vertikální rozlišení herního pole, je taktéž velmi malé kvůli časování v kernelu (čistě teoreticky by zde bylo možné dosáhnout 160 řádků).

Poznámka: herní pole je vykresleno jen tam, kde bitový vzorek obsahuje jedničku. V případě nuly „prosvítá“ herním polem buď pozadí nebo střela/hráč (pokud má herní pole vyšší prioritu než pohyblivé objekty).

Víme již, že bitmapa herního pole se specifikuje přes deklaraci playfield; barva herního pole se pak nastavuje řídicím registrem COLUPF:

  playfield:
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 ................................
 ................................
 ...XXXX....XXX.....XXX...XXXXX..
 ...X...X..X...X...X...X....X....
 ...XXXX...X...X...X...X....X....
 ...X.X....X...X...X...X....X....
 ...X..X...X...X...X...X....X....
 ...X...X...XXX.....XXX.....X....
 ................................
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
end
 
mainloop
    COLUPF = $14
    drawscreen
    goto mainloop

Výsledná scéna po vykreslení v emulátoru by měla vypadat následovně:

Obrázek 25: Scéna, v níž je použito pouze herní pole.

8. Střely (missiles)

Střely neboli missiles jsou dalším grafickým objektem, přičemž na rozdíl od herního pole jsou střely na obrazovce reprezentovány čtvercem či obdélníkem (v limitním případě jediným pixelem) s měnitelnou polohou na řádku. Šířka střel může být nastavena na jeden pixel, dva pixely, čtyři pixely či dokonce na osm pixelů (připomeňme, že na obrazovém řádku se 160 viditelnými pixely). Tuto šířku lze specifikovat v řídicím registru NUSIZ0 pro první střelu a NUSIZ1 pro druhou střelu. Barva střel je řízena obsahem řídicích registrů COLUP0 a COLUP1, což jinými slovy znamená, že střely mají stejnou barvu jako hráči (první střela má barvu totožnou jako první hráč, druhá střela jako hráč druhý). Dále lze ve specializovaných proměnných nazvaných missileNx a missileNy nastavit pozici střely na obrazovce (N=0 nebo N=1) a proměnná missileNheight obsahuje výšku střely N.

V následujícím demonstračním příkladu je nad herním polem zobrazena první střela, jejíž rozměry jsou nastaveny na 8×8 pixelů (má tedy šířku osmi pixelů a opakuje se na osmi po sobě jdoucích obrazových řádcích). Povšimněte si, že obsah řídicích registrů je zapotřebí v herní smyčce neustále obnovovat, což programu ubírá několik strojových cyklů z dostupných přibližně 5000 cyklů na snímek:

  playfield:
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 ................................
 ................................
 ...XXXX....XXX.....XXX...XXXXX..
 ...X...X..X...X...X...X....X....
 ...XXXX...X...X...X...X....X....
 ...X.X....X...X...X...X....X....
 ...X..X...X...X...X...X....X....
 ...X...X...XXX.....XXX.....X....
 ................................
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
end
 
  missile0x = 64
  missile0y = 64
  missile0height = 8
 
mainloop
    NUSIZ0 = $30
    COLUPF = 14
    COLUP0 = $1E
 
    drawscreen
    goto mainloop

Výsledek by měl vypadat následovně:

Obrázek 26: Herní scéna s pozadím, herní plochou a jedinou střelou.

Nic nám však nebrání zobrazit si obě střely, každou odlišnou barvou. Povšimněte si, že šířka střel je nastavena na jeden pixel a výška na osm pixelů, takže výsledkem by měly být svislé čárky:

  playfield:
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 ................................
 ................................
 ...XXXX....XXX.....XXX...XXXXX..
 ...X...X..X...X...X...X....X....
 ...XXXX...X...X...X...X....X....
 ...X.X....X...X...X...X....X....
 ...X..X...X...X...X...X....X....
 ...X...X...XXX.....XXX.....X....
 ................................
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
end
 
 
  missile0x = 80-20
  missile0y = 20
  missile0height = 8
 
  missile1x = 80+20
  missile1y = 20
  missile1height = 8
 
 
mainloop
    NUSIZ0 = $0
    NUSIZ1 = $0
    COLUPF = 14
    COLUP0 = $1E
    COLUP1 = $4E
 
    drawscreen
    goto mainloop

Výsledek by měl v tomto případě vypadat takto:

Obrázek 27: Herní scéna s pozadím, herní plochou a oběma střelami.

9. Šířka vykreslování střel

Šířka střel se nastavuje v horních bitech řídicích registrů nazvaných NUSIZ0 (pro první střelu) a NUSIZ1 (pro střelu druhou). Existují čtyři podporované šířky střel, jak je to ostatně patrné z následující tabulky:

NUSIZ0/NUSIZ1 Stručný popis
$0× střela s šířkou jednoho pixelu
$1× střela s šířkou dvou pixelů
$2× střela s šířkou čtyř pixelů
$3× střela s šířkou osmi pixelů

Úprava předchozího příkladu pro střely s šířkou dvou pixelů by tedy mohla vypadat následovně:

mainloop
    NUSIZ0 = $10
    NUSIZ1 = $10
    COLUPF = 14
    COLUP0 = $1E
    COLUP1 = $4E
 
    drawscreen
    goto mainloop

S výsledkem:

Obrázek 28: Herní scéna s pozadím, herní plochou a oběma střelami.

Úprava předchozího příkladu pro střely s šířkou čtyř pixelů:

mainloop
    NUSIZ0 = $20
    NUSIZ1 = $20
    COLUPF = 14
    COLUP0 = $1E
    COLUP1 = $4E
 
    drawscreen
    goto mainloop

Výsledek je tentokrát odlišný:

Obrázek 29: Herní scéna s pozadím, herní plochou a oběma střelami.

Úprava předchozího příkladu pro střely s šířkou osmi pixelů:

mainloop
    NUSIZ0 = $30
    NUSIZ1 = $30
    COLUPF = 14
    COLUP0 = $1E
    COLUP1 = $4E
 
    drawscreen
    goto mainloop

Výsledek získaný po spuštění tohoto demonstračního příkladu:

Obrázek 30: Herní scéna s pozadím, herní plochou a oběma střelami.

10. Priorita zobrazení střel vůči pozadí i střel vůči sobě

Nastavením jednoho bitu řídicího registru nazvaného CTRLPF (s ním jsme se již setkali) lze řídit, zda se mají střely a současně i hráči zobrazit před herním polem či za ním. Toto chování se ovlivňuje třetím bitem registru CTRLPF. Ukažme si vliv tohoto bitu na způsob vykreslení herní scény. Nejdříve zobrazíme herní pole a dvojici střel, které částečně herní pole překrývají:

  playfield:
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 ................................
 ................................
 ...XXXX....XXX.....XXX...XXXXX..
 ...X...X..X...X...X...X....X....
 ...XXXX...X...X...X...X....X....
 ...X.X....X...X...X...X....X....
 ...X..X...X...X...X...X....X....
 ...X...X...XXX.....XXX.....X....
 ................................
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
end
 
 
  missile0x = 80-20
  missile0y = 28
  missile0height = 8
 
  missile1x = 80+20
  missile1y = 28
  missile1height = 8
 
 
mainloop
    NUSIZ0 = $30
    NUSIZ1 = $30
    COLUPF = 14
    COLUP0 = $1E
    COLUP1 = $4E
 
    drawscreen
    goto mainloop

Ze zobrazeného výsledku je patrné, že střely jsou zobrazeny před herním polem, tedy mají vyšší prioritu:

Obrázek 31: Herní pole, před nímž jsou zobrazeny střely.

Chování grafických objektů při vykreslování můžeme jednoduše modifikovat, a to konkrétně nastavením třetího bitu řídicího registru CTRLPF na jedničku (tedy namísto hodnoty 1 zde bude uložena hodnota 5):

mainloop
    CTRLPF = $05
 
    NUSIZ0 = $30
    NUSIZ1 = $30
    COLUPF = 14
    COLUP0 = $1E
    COLUP1 = $4E
 
    drawscreen
    goto mainloop

Nyní bude scéna s naprosto stejnými grafickými objekty zobrazena odlišně:

Obrázek 32: Herní pole, za nímž jsou zobrazeny střely.

A konečně se podívejme na to, jak jsou zobrazeny překrývající se střely. Prioritu zobrazení těchto dvou typů objektů nelze změnit:

  playfield:
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 ................................
 ................................
 ...XXXX....XXX.....XXX...XXXXX..
 ...X...X..X...X...X...X....X....
 ...XXXX...X...X...X...X....X....
 ...X.X....X...X...X...X....X....
 ...X..X...X...X...X...X....X....
 ...X...X...XXX.....XXX.....X....
 ................................
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
end
 
 
  missile0x = 80-2
  missile0y = 28-2
  missile0height = 8
 
  missile1x = 80+2
  missile1y = 28+2
  missile1height = 8
 
 
mainloop
    CTRLPF = $05
 
    NUSIZ0 = $30
    NUSIZ1 = $30
    COLUPF = 14
    COLUP0 = $1E
    COLUP1 = $4E
 
    drawscreen
    goto mainloop

Výsledek:

Obrázek 33: Dvojice překrývajících se střel.

11. Detekce kolize střel

Čip TIA umožňuje detekovat kolizi mezi jednotlivými objekty, což je výhodné, protože to ušetří velké množství nutných výpočtů (zásah hráče, náraz hráče do zdi atd.). Pro test kolize v Batari BASICu postačuje použít funkci nazvanou collision, které se předá dvojice grafických objektů. V případě, že tyto objekty kolidují, vrátí se logická hodnota pravda, v opačném případě se pochopitelně vrátí nepravda. V dalším demonstračním příkladu je kolize střel zvýrazněna změnou barvy herního pole:

  playfield:
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 ................................
 ................................
 ...XXXX....XXX.....XXX...XXXXX..
 ...X...X..X...X...X...X....X....
 ...XXXX...X...X...X...X....X....
 ...X.X....X...X...X...X....X....
 ...X..X...X...X...X...X....X....
 ...X...X...XXX.....XXX.....X....
 ................................
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
end
 
 
  missile0x = 80-20
  missile0y = 28-2
  missile0height = 8
 
  missile1x = 80+20
  missile1y = 28+2
  missile1height = 8
 
 
mainloop
    NUSIZ0 = $30
    NUSIZ1 = $30
    COLUPF = $7F
    COLUP0 = $1E
    COLUP1 = $4E
 
    if joy0up then missile0y = missile0y - 1
    if joy0down then missile0y = missile0y + 1
    if joy0left then missile0x = missile0x - 1
    if joy0right then missile0x = missile0x + 1
    if collision(missile0, missile1) then COLUPF = $48
 
    drawscreen
    goto mainloop

Obrázek 34: Střely, které nekolidují.

Obrázek 35: Střely, které kolidují.

12. Míč (ball), šířka vykreslení míče

Dalším grafickým objektem, který může tvořit součást herní scény, je takzvaný míč neboli ball. Pokud celou situaci poněkud zjednodušíme, můžeme si pod pojmem ball představit objekt, který se svými základními vlastnostmi podobá střelám – má šířku jednoho pixelu, ovšem lze ho v případě potřeby „roztáhnout“ na šířku dvou, čtyř či osmi pixelů, a to konkrétně úpravou obsahu řídicího registru CTRLPF:

CTRLPF Stručný popis
$0× míč s šířkou jednoho pixelu
$1× míč s šířkou dvou pixelů
$2× míč s šířkou čtyř pixelů
$3× míč s šířkou osmi pixelů

Od střel se míč liší především tím, že má stejnou barvu jako herní pole (playfield). Jeho barva je tedy nastavena obsahem řídicího registru COLUPF. Pozice míče na obrazovce se v Batari BASICu nastavuje proměnnými ballx a bally, jeho výška (specifikovaná v obrazových řádcích) pak proměnnou ballheight.

V dalším demonstračním příkladu je zobrazeno jednoduché herní pole a míč, který se pohybuje pod úhlem 45° a odráží se od okrajů obrazovky – jedná se tedy o míč ze hry Pong. Vše je řízeno výše uvedenými řídicími registry a trojicí speciálních proměnných:

  playfield:
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 ................................
 ................................
 ................................
 ................................
 ................................
 ................................
 ................................
 ................................
 ................................
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
end
 
  ballx = 80
  bally = 50
  ballheight = 8
 
  rem Posun mice 0 v horizontalnim i vertikalnim smeru
  let c = 1
  let d = 1
 
mainloop
    rem Sirka mice
    CTRLPF = $31
 
    rem Barva pozadi a soucasne i mice
    COLUPF = 14
 
    ballx = ballx + c
    if ballx >= 152 then ballx = 152: c =- 1
    if ballx <= 1 then ballx = 1: c = 1
    bally = bally + d
    if bally >= 80 then bally = 80: d =- 1
    if bally <= 10 then bally = 10: d = 1
 
    drawscreen
    goto mainloop

Obrázek 36: Míč pohybující se v herní scéně.

Poznámka: konstanta 152 použitá v demonstračním příkladu je odvozena od šířky obrazovky 160 pixelů a šířky míče 8 pixelů.

13. Shrnutí a ukázka z třetího článku – relativně složité demo využívajících necelé 2kB ROM

Prozatím jsme si popsali jen některé možnosti grafického čipu TIA. Zbývá nám ovšem ještě popis těch nejdůležitějších grafických objektů – hráčů (players), z nichž se na dalších platformách (jak na herních konzolích, tak i na mnoha herních automatech i na některých počítačích) vyvinuly plnohodnotné sprity. TIA umožňuje různé manipulace s hráči, například jejich několikanásobné zobrazení na jediném řádku (viz Space Invaders) atd. Podívejme se nyní jen v krátkosti (a bez dalších podrobností) na následující demo, jehož jednotlivé prvky budou zřejmé po pročtení navazujícího článku:

Obrázek 37: Snímek získaný z dema.

Obrázek 38: Další snímek získaný z dema.

I přes relativně velké množství pohybujících se objektů je kód dema po překladu menší než 2kB:

ict ve školství 24

16:43 $ 2600basic.sh collisions_2.bas
Found dasm version: DASM 2.20.14.1
Starting build of collisions_2.bas
batari Basic v1.6-SNAPSHOT (c)2021
2600 Basic compilation complete.
      2368 bytes of ROM space left
 
Complete. (0)
Build complete.

Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:

  playfield:
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 ................................
 ................................
 ................................
 ................................
 ................................
 ................................
 ................................
 ................................
 ................................
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
end
 
   player0:
   %01000010
   %10000001
   %01011010
   %11111111
   %11011011
   %01111110
   %00111100
   %00011000
end
 
   player1:
   %10100101
   %01011010
   %00100100
   %11111111
   %11011011
   %01111110
   %00111100
   %00011000
end
 
  player0x = 20
  player0y = 20
 
  player1x = 140
  player1y = 30
 
  missile0x = 80
  missile0y = 40
  missile0height = 4
 
  missile1x = 80
  missile1y = 60
  missile1height = 4
 
  ballx = 80
  bally = 50
  ballheight = 8
 
  scorecolor = $98
 
  rem Posun hrace #1 v horizontalnim smeru
  let a = 1
 
  rem Posun hrace #2 v horizontalnim smeru
  let b = 1
 
  rem Posun strely 0 v horizontalnim i vertikalnim smeru
  let c = 1
  let d = 1
 
  rem Posun strely 1 v horizontalnim i vertikalnim smeru
  let e = 1
  let f = -1
 
  rem Pocatecni hodnota score
  score = 0
 
mainloop
    rem Sirka strel
    NUSIZ0 = $20
    NUSIZ1 = $20
 
    rem Sirka mice
    CTRLPF = $31
 
    rem Barvy pozadi i hracu
    COLUPF = 14
    COLUP0 = $1E
    COLUP1 = $4E
 
    player0x = player0x + a
    if player0x >= 152 then player0x = 152: a =- 1
    if player0x <= 0 then player0x = 0: a = 1
 
    player1x = player1x + b
    if player1x >= 152 then player1x = 152: b =- 1
    if player1x <= 1 then player1x = 1: b = 1
 
    missile0x = missile0x + c
    if missile0x >= 152 then missile0x = 152: c =- 1
    if missile0x <= 1 then missile0x = 1: c = 1
    missile0y = missile0y + d
    if missile0y >= 80 then missile0y = 80: d =- 1
    if missile0y <= 10 then missile0y = 10: d = 1
 
    missile1x = missile1x + e
    if missile1x >= 152 then missile1x = 152: e =- 1
    if missile1x <= 1 then missile1x = 1: e = 1
    missile1y = missile1y + f
    if missile1y >= 80 then missile1y = 80: f =- 1
    if missile1y <= 10 then missile1y = 10: f = 1
 
    if joy0up then bally = bally - 1
    if joy0down then bally = bally + 1
    if joy0left then ballx = ballx - 1
    if joy0right then ballx = ballx + 1
 
    if collision(missile0, player0) then COLUPF = $38:score = score + 1
    if collision(missile0, player1) then COLUPF = $48:score = score + 10
    if collision(missile1, player0) then COLUPF = $58:score = score + 100
    if collision(missile1, player1) then COLUPF = $68:score = score + 1000
    if collision(missile0, missile1) then COLUPF = $78:score = score + 10000
    if collision(ball, player0) then COLUP0 = $48:score = score + 100000
    if collision(ball, player1) then COLUP1 = $48:score = score + 100000
    if collision(ball, missile0) then COLUP0 = $48:score = score + 100000
    if collision(ball, missile1) then COLUP1 = $48
 
    drawscreen
    goto mainloop

14. Repositář s demonstračními příklady

Všechny minule i dnes popisované demonstrační příklady určené pro překlad Batari-BASICem byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/8bit-fame. Příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý (dnes již poměrně rozsáhlý) repositář:

# Příklad Stručný popis Adresa
1 playfield1.bas zobrazení herního pole, bez smyčky kernelu https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/playfield1.bas
2 playfield2.bas ukázka smyčky kernelu https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/playfield2.bas
3 playfield3.bas změna barvy herního pole mimo smyčku https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/playfield3.bas
4 playfield4.bas změna barvy herního pole uvnitř smyčky https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/playfield4.bas
5 playfield5.bas postupná změna barvy herního pole https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/playfield5.bas
6 missile1.bas zobrazení střely nad herním polem https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/missile1.bas
7 missile2.bas pohyb střelou pomocí joysticku https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/missile2.bas
8 missile3.bas změna barvy střely tlačítkem joysticku https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/missile3.bas
9 missile4.bas předchozí demonstrační příklad, ale s čísly řádků https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/missile4.bas
     
10 missile5.bas zobrazení dvojice střel https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/missile5.bas
11 missile6.bas střely s dvojnásobnou šířkou https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/missile6.bas
12 missile7.bas střely se čtyřnásobnou šířkou https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/missile7.bas
13 missile8.bas střely s osminásobnou šířkou https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/missile8.bas
14 missile9.bas střely překrývající herní pole https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/missile9.bas
15 missile_A.bas herní pole překrývající střely https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/missile_A.bas
16 missile_B.bas vzájemný překryv střel https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/missile_B.bas
17 players1.bas zobrazení hráče https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/players1.bas
18 players2.bas posun hráče joystickem https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/players2.bas
19 players3.bas hráč s dvojnásobnou šířkou https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/players3.bas
20 players4.bas hráč se čtyřnásobnou šířkou https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/players4.bas
21 players5.bas oba hráči, obě střely i míč v jediné scéně https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/players5.bas
22 collisions1.bas kolize mezi různými typy grafických objektů https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/collisions1.bas
23 collisions2.bas kolize mezi různými typy grafických objektů https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/collisions2.bas
24 score.bas zobrazení skóre s ladicími informacemi https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/score.bas
25 players_repeat1.bas zobrazení hráče v několika kopiích https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/players_repeat1.bas
26 ball.bas míč pohybující se ve scéně https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/ball.bas
27 summary2.bas shrnutí – relativně složité demo využívajících necelé 2kB ROM https://github.com/tisnik/8bit-fame/blob/master/batari-Basic/summary2.bas

15. Odkazy na Internetu

  1. STELLA PROGRAMMER'S GUIDE
    https://alienbill.com/2600/101/doc­s/stella.html
  2. Batari BASIC GitHub repositář
    https://github.com/batari-Basic/batari-Basic
  3. Programming Tutorial
    https://atariage.com/forum­s/topic/111938-programming-tutorial/
  4. batari Basic Commands
    https://www.randomterrain.com/atari-2600-memories-batari-basic-commands.html
  5. About batari Basic
    https://bataribasic.com/
  6. Rationale
    https://bataribasic.com/ra­tionale.html
  7. Games That Push The Limits of the Atari 2600
    https://www.youtube.com/wat­ch?v=zM0IsWdIc_g
  8. Vývojové nástroje používané v dobách osmibitových mikropočítačů
    https://www.root.cz/clanky/vyvojove-nastroje-pouzivane-v-dobach-osmibitovych-mikropocitacu/
  9. Programovací jazyky používané na platformě osmibitových domácích mikropočítačů Atari
    https://www.root.cz/clanky/pro­gramovaci-jazyky-pouzivane-na-platforme-osmibitovych-domacich-mikropocitacu-atari/
  10. Programovací jazyky používané na platformě osmibitových domácích mikropočítačů Atari (2)
    https://www.root.cz/clanky/pro­gramovaci-jazyky-pouzivane-na-platforme-osmibitovych-domacich-mikropocitacu-atari-2/
  11. Barvové palety čipu TIA
    http://www.qotile.net/mini­dig/docs/tia_color.html
  12. Crazy Limit Pushing Games From the Last Years of the Atari 2600!
    https://www.youtube.com/wat­ch?v=ADy1F8v59YU
  13. Atari 2600 VCS Top 100 Games Hits (past week)
    http://www.atarimania.com/top-atari-atari-2600-vcs-_G2_7.html
  14. Tobikomi – The Sound Of Thunder [Atari TIA Chip]
    https://www.youtube.com/watch?v=j0w-IZ6nAMQ
  15. TIA Visual Objects
    https://github.com/jigo2600/ji­go2600/blob/master/doc/TI­A_Visual_Objects.md
  16. TIA Sound
    https://github.com/jigo2600/ji­go2600/blob/master/doc/TI­A_Sound.md
  17. How To Make An Atari Game
    https://www.youtube.com/wat­ch?v=Ww3her2zk_I
  18. Let's Make an Atari 2600 Game! – Part 1
    https://www.youtube.com/wat­ch?v=Iqo_oARxjEg
  19. Let's Make an Atari 2600 Game! – Part 2
    https://www.youtube.com/wat­ch?v=hFFQjwFbzV8
  20. Let's Make an Atari 2600 Game! – Part 3
    https://www.youtube.com/wat­ch?v=lZ0AL6jCBXY
  21. Let's Make an Atari 2600 Game! Part 4 – Title screens and challenges
    https://www.youtube.com/watch?v=-G2kmsmqk-E
  22. Let's Make an Atari 2600 Game! Part 5 – Sound
    https://www.youtube.com/wat­ch?v=9rX2eo20×q8
  23. Let's Make an Atari 2600 game! 6 – Realtime RPG combat
    https://www.youtube.com/wat­ch?v=alRGuQ9gjRA
  24. Let's Make an Atari 2600 Game! Part 7 – Monsters
    https://www.youtube.com/wat­ch?v=vaAlYC_8YSA
  25. Let's Make an Atari 2600 Game! Part 8 – 3D Engine
    https://www.youtube.com/wat­ch?v=c1dPY1ROZe4
  26. Let's Make an Atari 2600 Game – Part 9 – Homemade cartridge
    https://www.youtube.com/wat­ch?v=xKlMohF_9Cc
  27. Bird Poop! – Atari 2600 Homebrew – batari Basic
    https://www.youtube.com/watch?v=-m4gKis0vBg
  28. DP Interviews: Bob Whitehead (By Scott Stilphen)
    http://www.digitpress.com/li­brary/interviews/interview_bob_whi­tehead.html
  29. The dasm macro assembler
    http://dasm-dillon.sourceforge.net/
  30. Official home of dasm, a versatile macro assembler
    https://dasm-assembler.github.io/
  31. Dokumentace k DASMu
    https://raw.githubusercontent.com/dasm-assembler/dasm/master/docs/dasm.pdf
  32. Atari Programming Workshop Chapter links
    http://atariage.com/forum­s/viewtopic.php?t=47479
  33. Various Development Kits
    http://devkits.handheldmuseum.com/
  34. Classic Console Development
    http://sebastianmihai.com/ccd/
  35. Atari 2600 development – Snappy (batari basic)
    http://sebastianmihai.com/ma­in.php?t=47
  36. Atari VCS (Atari 2600) – fotografie
    http://oldcomputers.net/atari-vcs.html
  37. History of Consoles: Atari VCS/2600 (1977)
    http://gamester81.com/history-of-consoles-atari-vcs2600–1977/
  38. Iag Bogost: Racing the Beam
    http://www.bogost.com/book­s/video_computer_system.shtml
  39. Atari 2600 Programming Tutorial
    http://www.randomterrain.com/atari-2600-memories-tutorial-andrew-davie-01.html
  40. Atari 2600 Development Cartridge *Super Deluxe*~!
    http://jazz-disassemblies.blogspot.cz/2013/09/atari-2600-development-cartridge-super.html
  41. Atari „Alpine“ Devkit (pro Atari Jaguar)
    http://justclaws.atari.or­g/devcats/hardware/ataridev­.htm
  42. 6502 compatible assembler and emulator in javascript
    http://www.6502asm.com/
  43. Atari 2600 Programming
    http://atariage.com/2600/programming/
  44. Retrozone – Brand new original homebrew games by current programmers
    http://www.retrousb.com/in­dex.php?cPath=30
  45. ATARI VCS/2600 TIA CHIPS
    http://www.ataricompendium­.com/faq/vcs_tia/vcs_tia.html
  46. Elena, New programming language for the ZX Spectrum Next
    https://www.vintageisthene­wold.com/elena-new-programming-language-for-the-zx-spectrum-next
  47. ZX Spectrum development with modern tools
    http://www.breakintoprogram­.co.uk/software_developmen­t/zx-spectrum-development-with-modern-tools
  48. Z80 Development Toolchain
    http://www.breakintoprogram­.co.uk/computers/zx-spectrum/assembly-language/z80-development-toolchain
  49. Space Invaders Sprite Sheet
    https://www.deviantart.com/go­operblooper22/art/Space-Invaders-Sprite-Sheet-135338373

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.