Obsah
1. Sprity na osmibitových domácích mikropočítačích a herních konzolích
2. Osmibitová herní konzole Atari 2600
3. Osmibitové domácí počítače Atari
6. Modifikovatelné informace o spritech
7. Načtení spritů do operační paměti
8. Kód v obsluze nemaskovatelného přerušení
9. Úplný zdrojový kód dnešního prvního demonstračního příkladu
11. Modifikace barvových atributů spritů
12. Úplný zdrojový kód dnešního druhého demonstračního příkladu
13. Větší množství spritů na obrazovce rozdělených do řádků
14. Úplný zdrojový kód dnešního třetího demonstračního příkladu
15. Větší množství spritů na jediném řádku
16. Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu
17. Repositář s demonstračními příklady
1. Sprity na osmibitových domácích mikropočítačích a herních konzolích
Na některých osmibitových domácích počítačích a taktéž osmibitových herních konzolích se ve hrách, ale i v dalších aplikacích zaměřených na počítačovou grafiku, často používala možnost zrychleného vykreslování jednoduchých rastrových obrázků nazývaných sprity. Generování obrazů spritů na televizi či obrazovce monitoru přitom probíhalo nezávisle na vykreslování ostatní scény (v tomto případě nazývané pozadí – background, popř. hrací plocha – playground), přičemž bylo možné definovat prioritu vykreslování, tj. zda bude ve výsledném obrázku upřednostněn pixel z pozadí nebo ze spritu. Grafické subsystémy těchto počítačů, které podporovaly práci se sprity, také většinou obsahovaly takzvané kolizní registry, do kterých se ukládaly příznaky překryvu pozadí se sprity (kolize spritu s barvou pozadí) nebo mezi několika sprity navzájem (kolize spritů). Tím se při práci s pohyblivými obrazy předmětů nemusely provádět zdlouhavé testy mezi všemi zobrazovanými pixely, postačilo pouze přečíst stav z kolizních registrů.
V dnešním článku o vývoji pro osmibitovou herní konzoli NES se budeme zabývat „klasickými“ sprity tak, jak byly implementovány u osmibitových strojů (včetně mnoha dobových herních automatů). Po krátkém úvodu se zaměříme na vývoj pro NES v assembleru.
Obrázek 1: Screenshot ze hry Starquake ve verzi pro osmibitové počítače Atari. Tyto počítače podporovaly vykreslování čtyř jednobarevných spritů (každý sprite samozřejmě mohl mít jinou barvu), jejichž rozlišení bylo 8×128 resp. 8×256 pixelů a dále čtyř takzvaných střel, což byly sprity zúžené na 2×128 nebo 2×256 pixelů (všechny střely se navíc mohly spojit do pátého spritu). Hrací plocha hry Starquake je vytvořena v monochromatickém textově-grafickém režimu, ovšem hráč je vykreslen pomocí spritu, tj. může mít barvu odlišnou od okolní scény a může se pohybovat v horizontálním i vertikálním směru bez nutnosti přesunů dat v obrazové paměti (pouze při pohybu ve vertikálním směru je nutné přesunout několik bajtů definujících tvar hráče).
U většiny osmibitových počítačů, které podporovaly vykreslování spritů, byly video paměti určené pro uložení pozadí (hrací plochy) a paměti pro uložení rastrových dat spritů od sebe odděleny a teprve až v průběhu vykreslování na obrazovku se provedla kombinace pixelů pozadí a pixelů uložených ve spritech – viz druhý obrázek. Sprity byly poprvé použity v dnes již zapomenutém počítači Elektor TV Games Computer z roku 1979. Později byly podporovány například na osmibitových počítačích Atari, na počítači Commodore C64 (viz další kapitoly) a v pozdější době i na herních konzolích, a to včetně NESu.
Obrázek 2: Tok dat mezi operační pamětí, mikroprocesorem a čipy ANTIC i GTIA při generování video signálu na osmibitových počítačích Atari.
2. Osmibitová herní konzole Atari 2600
Na herní konzoli Atari 2600 jsou sprity hlavními objekty tvořícími (vedle primitivního pozadí) prakticky veškerou grafickou informaci zobrazenou na monitoru. Každý obrazový řádek byl s využitím č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 (což je jediný grafický objekt, který není spritem).
Obrázek 3: 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ů).
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 |
3. Osmibitové domácí počítače Atari
Prvním čipem typickým pro mnoho elektronických zařízení firmy Atari byl čip nazvaný GTIA, neboli Graphics Television Interface Adaptor, popř. též George's Television Interface Adaptor podle jména svého tvůrce George McLeoda. Tento čip, jenž vznikl rozšířením možností původního čipu TIA z Atari 2600 (viz předchozí kapitolu), zajišťoval několik funkcí: generování signálů nesoucích informaci o barvách a světlosti (luminanci) pixelů, řízení a zobrazování spritů, řízení priority spritů a pozadí a taktéž detekce kolizí mezi sprity navzájem, popř. kolizí mezi sprity a pozadím. Navíc tento čip dokázal na pozadí zobrazovat bitmapu generovanou spolupracujícím čipem ANTIC, popř. tuto bitmapu mohl reinterpretovat takovým způsobem, že vznikly tři nové grafické režimy s horizontálním rozlišením sníženým na 80 pouhých pixelů, protože každý pixel byl představován čtveřicí bitů (jeden obrazový řádek má při použití standardních režimů ve framebufferu velikost 20 nebo 40 bajtů, v rozšířeném hracím poli pak maximálně 48 bajtů), ovšem s možností zobrazení až šestnácti barev či šestnácti úrovní jedné barvy.
„Good hardware-software tradeoffs make the product economically viable.“
Ve skutečnosti byly všechny tři nové grafické režimy podporované čipem GTIA založené na monochromatickém režimu čipu ANTIC číslo 8, který umožňoval na jednom řádku standardně zobrazit 320 pixelů (teoreticky bylo sice možné tyto tři režimy založit například i na textovém režimu, ovšem v praxi se tento způsob práce s grafikou příliš často nepoužíval). Čip GTIA vždy čtveřici sousedních pixelů sloučil a výsledné čtyři bity mu sloužily buď pro výběr barvy z barvové palety (k dispozici bylo devět barev z možných šestnácti – jedná se o celkem zbytečné omezení dané počtem barvových registrů), určení odstínu barvy (úroveň, tj. světlost byla v tomto případě konstantní), popř. k určení úrovně barvy, zatímco odstín byl konstantní (tímto způsobem bylo možné například pracovat s černobílými fotografiemi). Čip GTIA tedy ponechával značnou část práce, především časování, provádění takzvaného display-listu atd., přístup do paměti, na obvodu ANTIC, což se vlastně ani příliš neliší od principu práce čipu TIA.
Obrázek 4: Snímek ze známé hry Gyrrus.
Čip GTIA byl řízen pomocí 32 registrů, z nichž všechny byly určeny pro zápis a některé taktéž pro čtení, tj. například pro zjišťování kolizí atd. Některé z těchto registrů jsou vypsány v následující tabulce (povšimněte si, kolik registrů je vlastně určeno pro práci se sprity):
Registr | Režim | Význam |
---|---|---|
COLPM0 | W | barva hráče číslo 0 a střely číslo 0 |
COLPM1 | W | barva hráče číslo 1 a střely číslo 1 |
COLPM2 | W | barva hráče číslo 2 a střely číslo 2 |
COLPM3 | W | barva hráče číslo 3 a střely číslo 3 |
COLPF0 | W | barva pro herní pole číslo 0 |
COLPF1 | W | barva pro herní pole číslo 1 |
COLPF2 | W | barva pro herní pole číslo 2 |
COLPF3 | W | barva pro herní pole číslo 3 |
COLBK | W | barva pozadí |
PRIOR | W | řízení priority objektů a taktéž výběr grafického režimu |
HPOSP0 | W | horizontální pozice hráče číslo 0 |
HPOSP1 | W | horizontální pozice hráče číslo 1 |
HPOSP2 | W | horizontální pozice hráče číslo 2 |
HPOSP3 | W | horizontální pozice hráče číslo 3 |
HPOSM0 | W | horizontální pozice střely číslo 0 |
HPOSM1 | W | horizontální pozice střely číslo 1 |
HPOSM2 | W | horizontální pozice střely číslo 2 |
HPOSM3 | W | horizontální pozice střely číslo 3 |
SIZEP0 | W | horizontální zvětšení hráče číslo 0 (1×, 2×, 4×) |
SIZEP1 | W | horizontální zvětšení hráče číslo 1 (1×, 2×, 4×) |
SIZEP2 | W | horizontální zvětšení hráče číslo 2 (1×, 2×, 4×) |
SIZEP3 | W | horizontální zvětšení hráče číslo 3 (1×, 2×, 4×) |
SIZEM | W | horizontální zvětšení všech střel (1×, 2×, 4×) |
GRAFP0 | W | bitová data pro hráče číslo 0 |
GRAFP1 | W | bitová data pro hráče číslo 1 |
GRAFP2 | W | bitová data pro hráče číslo 2 |
GRAFP3 | W | bitová data pro hráče číslo 3 |
GRAFM | W | bitová data pro všechny střely |
M0PF | R | kolizní registr mezi střelou číslo 0 a herním polem |
M1PF | R | kolizní registr mezi střelou číslo 1 a herním polem |
M2PF | R | kolizní registr mezi střelou číslo 2 a herním polem |
M3PF | R | kolizní registr mezi střelou číslo 3 a herním polem |
P0PF | R | kolizní registr mezi hráčem číslo 0 a herním polem |
P1PF | R | kolizní registr mezi hráčem číslo 1 a herním polem |
P2PF | R | kolizní registr mezi hráčem číslo 2 a herním polem |
P3PF | R | kolizní registr mezi hráčem číslo 3 a herním polem |
M0PL | R | kolizní registr mezi střelou číslo 0 a hráčem |
M1PL | R | kolizní registr mezi střelou číslo 1 a hráčem |
M2PL | R | kolizní registr mezi střelou číslo 2 a hráčem |
M3PL | R | kolizní registr mezi střelou číslo 3 a hráčem |
P0PL | R | kolizní registr mezi hráčem číslo 0 a dalším hráčem |
P1PL | R | kolizní registr mezi hráčem číslo 1 a dalším hráčem |
P2PL | R | kolizní registr mezi hráčem číslo 2 a dalším hráčem |
P3PL | R | kolizní registr mezi hráčem číslo 3 a dalším hráčem |
Obrázek 5: Další snímek ze hry Gyrrus.
Čip GTIA kromě zavedení tří nových grafických režimů umožňoval vykreslit čtyři sprity s rozlišením maximálně 8×256 pixelů (popř. 8×128 pixelů) a další čtyři sprity s rozlišením 2×256 pixelů, které bylo možno spojit do jednoho (pátého) většího spritu s rozlišením 8×256 pixelů. Sprity široké 8 pixelů se v literatuře nazývají hráči (players), úzké dvoupixelové sprity se pak jmenují střely (missiles). Sprity byly jednobarevné, více barev bylo možno dosáhnout logickými operacemi nad překrývajícími se sprity (počítače Commodore C64 naproti tomu nabízely i sprity v režimu multicolor). Každý sprite mohl pomocí jedné instrukce měnit svoji horizontální velikost i horizontální pozici, přičemž polohy spritů byly navzájem nezávislé (horizontální pozice se interně zjišťovala pomocí čítače a komparátoru, což bylo řešení odlišné od technologie použité v čipu TIA, kde byl namísto čítače využíván linear feedback shift register). Vertikální pozice spritů se měnila blokovým přesunem bitmapy spritu v operační paměti. Bylo také možné definovat priority vykreslování spritů vůči sobě navzájem i vůči pozadí, tj. zda se má sprite vykreslovat nad herním polem (popř. jiným spritem) či se naopak pod některými barvami skrývat.
Obrázek 6: Hra Adventure 2 pro počítače Atari 5200. Jedná se o hry z 21. století vytvořenou v domácích podmínkách, která se snaží zachovat prvky z původní hry Adventure pro Atari 2600 (viz tvar hráče – čtverečku).
Kromě toho, že se dala měnit priorita jednotlivých spritů, bylo také možné detekovat kolizi spritu s jiným spritem popř. s nějakou barvou hracího pole. To stejné samozřejmě platí i pro střely, u nichž byla možná detekce kolize s hráčem či kolize s hracím polem. Při kolizi (do úvahy se samozřejmě braly pouze viditelné pixely spritu, tj. pixely nastavené na logickou jedničku) se nastavil příslušný bit ve stavových registrech, odkud bylo možné kdykoli poté zjistit, zda ke kolizi došlo či nikoli. Díky této funkcionalitě bylo možné velmi snadno otestovat například náraz hráče do stěny, zásah hráče střelou atd. Vzhledem k tomu, že sprity byly pouze jednobarevné, museli se vícebarevní hráči sestavovat z několika spritů. Omezení počtu spritů naproti tomu nebylo kritické, neboť jeden sprite mohl být ve skutečnosti použitý pro zobrazení většího množství objektů ve scéně – jediným omezením bylo to, že tyto objekty nesměly ležet na stejném obrazovém řádku (podobné omezení zavádí i NES).
Obrázek 7: Další screenshot ze hry Adventure 2.
Pro porovnání shodných vlastností a rozdílů mezi čipy TIA a GTIA se podívejme na následující dvojici tabulek. V první tabulce jsou vypsány grafické objekty, s nimiž dokázal pracovat čip TIA použitý v herní konzoli Atari 2600:
# | 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 8: A další screenshot ze hry Adventure 2.
Ve druhé tabulce jsou vypsány grafické objekty, s nimiž dokázal pracovat čip GTIA použitý v herní konzoli Atari 5200 i v prakticky všech osmibitových domácích počítačích Atari (pokud tedy nepočítáme prvních zhruba 100 000 počítačů Atari 400 a Atari 800 s čipy CTIA:
# | Typ objektu | Orig.název | Objem paměti | Šířka reprezentovaná jedním bitem |
---|---|---|---|---|
1 | Pozadí | Background | 0 bitů | barva v COLBK, přes celou šířku řádku |
2 | Hrací plocha | Playground | x bitů | generováno v ANTIC |
3 | Hráč 0 | Player 0 | 8 bitů×128/256 | 1×, 2×, 4× šířka pixelu |
4 | Střela 0 | Missile 0 | 2 bity×128/256 | 1×, 2×, 4×, 8× šířka pixelu |
5 | Hráč 1 | Player 1 | 8 bitů×128/256 | 1×, 2×, 4× šířka pixelu |
6 | Střela 1 | Missile 1 | 2 bity×128/256 | 1×, 2×, 4×, 8× šířka pixelu |
7 | Hráč 2 | Player 2 | 8 bitů×128/256 | 1×, 2×, 4× šířka pixelu |
8 | Střela 2 | Missile 2 | 2 bity×128/256 | 1×, 2×, 4×, 8× šířka pixelu |
9 | Hráč 3 | Player 3 | 8 bitů×128/256 | 1×, 2×, 4× šířka pixelu |
10 | Střela 3 | Missile 3 | 2 bity×128/256 | 1×, 2×, 4×, 8× šířka pixelu |
Obrázek 9: Kombinace textových a pseudografických režimů ve hře International Karate.
4. Počítač Commodore C64
Grafický čip VIC-II použitý v počítači Commodore C64 podporuje, podobně jako obvod GTIA u osmibitových Atari, práci se sprity. Spritů může být (pokud není použita některá z technik popsaných v dalším textu) současně zobrazeno osm, přičemž každý sprite má velikost 24×21 pixelů v monochromatickém (hi-res) režimu nebo 12×21 pixelů v režimu multi-color. Pomocí vhodně naprogramované přerušovací rutiny je však možné zobrazit i více spritů – využívá se změna vertikální polohy některého již vykresleného spritu, což znamená, že omezení na osm spritů ve skutečnosti platí pouze pro jeden obrazový řádek. Ze čtyřiceti sedmi řídicích osmibitových registrů čipu VIC-II je jich plných 34 určeno pro ovládání spritů. U spritů je možné nastavovat jejich horizontální i vertikální polohu, barvu, velikost (zvětšení ve směru horizontální a/nebo vertikální osy), grafický režim (hi-res, multi-color) a kolize typu sprite-sprite a sprite-bitmapa. Také je možné měnit prioritu spritů vůči pozadí, vzájemná priorita spritů je však neměnná a je určena číslem spritu.
Obrázek 10: Osm čtyřbarevných spritů zobrazených v režimu multi-color.
Způsob zobrazení spritů nezávisle na pozadí je v čipu VIC-II řešen podobným způsobem, jako výše zmíněného čipu GTIA používaného u osmibitových počítačů Atari. Hlavní rozdíl (spíše rozšíření) spočívá v možnosti vertikálního posunu spritů a taktéž v možnosti jejich zobrazení ve více barvách (což je v praxi velmi důležité).
V následující tabulce jsou vypsány ty řídicí registry čipu VIC-II, pomocí nichž je možné zvolit způsob zobrazení spritů na obrazovce, měnit souřadnice spritů (registry 0 až 16) a popř. přečíst příznaky kolize spritů (registr 30 a 31). Povšimněte si, že vzhledem k horizontálnímu rozlišení čipu VIC-II, tj. 320 pixelům, je nutné horizontální souřadnice spritů uložit do devíti bitů, přičemž hodnoty nejvyšších bitů jsou pro všechny sprity sdruženy do řídicího registru číslo 16 (u Atari je tomu jinak). Taktéž je možné pro každý sprite zvolit barvu nezávisle na barvách ostatních spritů (a samozřejmě nezávisle na barvě pozadí) či zvětšit sprite v horizontálním či vertikálním směru (registr 23 a 29). Změnou hodnoty uložené v registru číslo 21 lze libovolný sprite povolit či zakázat:
#| Adr. |Bit7|Bit6|Bit5|Bit4|Bit3|Bit2|Bit1|Bit0| Function --+-------+----+----+----+----+----+----+----+----+------------------------ 0| $d000 | M0X | X coordinate sprite 0 --+-------+---------------------------------------+------------------------ 1| $d001 | M0Y | Y coordinate sprite 0 --+-------+---------------------------------------+------------------------ 2| $d002 | M1X | X coordinate sprite 1 --+-------+---------------------------------------+------------------------ 3| $d003 | M1Y | Y coordinate sprite 1 --+-------+---------------------------------------+------------------------ 4| $d004 | M2X | X coordinate sprite 2 --+-------+---------------------------------------+------------------------ 5| $d005 | M2Y | Y coordinate sprite 2 --+-------+---------------------------------------+------------------------ 6| $d006 | M3X | X coordinate sprite 3 --+-------+---------------------------------------+------------------------ 7| $d007 | M3Y | Y coordinate sprite 3 --+-------+---------------------------------------+------------------------ 8| $d008 | M4X | X coordinate sprite 4 --+-------+---------------------------------------+------------------------ 9| $d009 | M4Y | Y coordinate sprite 4 --+-------+---------------------------------------+------------------------ 10| $d00a | M5X | X coordinate sprite 5 --+-------+---------------------------------------+------------------------ 11| $d00b | M5Y | Y coordinate sprite 5 --+-------+---------------------------------------+------------------------ 12| $d00c | M6X | X coordinate sprite 6 --+-------+---------------------------------------+------------------------ 13| $d00d | M6Y | Y coordinate sprite 6 --+-------+---------------------------------------+------------------------ 14| $d00e | M7X | X coordinate sprite 7 --+-------+---------------------------------------+------------------------ 15| $d00f | M7Y | Y coordinate sprite 7 --+-------+----+----+----+----+----+----+----+----+------------------------ 16| $d010 |M7X8|M6X8|M5X8|M4X8|M3X8|M2X8|M1X8|M0X8| MSBs of X coordinates --+-------+----+----+----+----+----+----+----+----+------------------------ 21| $d015 | M7E| M6E| M5E| M4E| M3E| M2E| M1E| M0E| Sprite enabled --+-------+----+----+----+----+----+----+----+----+------------------------ 23| $d017 |M7YE|M6YE|M5YE|M4YE|M3YE|M2YE|M1YE|M0YE| Sprite Y expansion --+-------+----+----+----+----+----+----+----+----+------------------------ 27| $d01b |M7DP|M6DP|M5DP|M4DP|M3DP|M2DP|M1DP|M0DP| Sprite data priority --+-------+----+----+----+----+----+----+----+----+------------------------ 28| $d01c |M7MC|M6MC|M5MC|M4MC|M3MC|M2MC|M1MC|M0MC| Sprite multicolor --+-------+----+----+----+----+----+----+----+----+------------------------ 29| $d01d |M7XE|M6XE|M5XE|M4XE|M3XE|M2XE|M1XE|M0XE| Sprite X expansion --+-------+----+----+----+----+----+----+----+----+------------------------ 30| $d01e | M7M| M6M| M5M| M4M| M3M| M2M| M1M| M0M| Sprite-sprite collision --+-------+----+----+----+----+----+----+----+----+------------------------ 31| $d01f | M7D| M6D| M5D| M4D| M3D| M2D| M1D| M0D| Sprite-data collision --+-------+----+----+----+----+----+----+----+----+------------------------ 37| $d025 | - | - | - | - | MM0 | Sprite multicolor 0 --+-------+----+----+----+----+-------------------+------------------------ 38| $d026 | - | - | - | - | MM1 | Sprite multicolor 1 --+-------+----+----+----+----+-------------------+------------------------ 39| $d027 | - | - | - | - | M0C | Color sprite 0 --+-------+----+----+----+----+-------------------+------------------------ 40| $d028 | - | - | - | - | M1C | Color sprite 1 --+-------+----+----+----+----+-------------------+------------------------ 41| $d029 | - | - | - | - | M2C | Color sprite 2 --+-------+----+----+----+----+-------------------+------------------------ 42| $d02a | - | - | - | - | M3C | Color sprite 3 --+-------+----+----+----+----+-------------------+------------------------ 43| $d02b | - | - | - | - | M4C | Color sprite 4 --+-------+----+----+----+----+-------------------+------------------------ 44| $d02c | - | - | - | - | M5C | Color sprite 5 --+-------+----+----+----+----+-------------------+------------------------ 45| $d02d | - | - | - | - | M6C | Color sprite 6 --+-------+----+----+----+----+-------------------+------------------------ 46| $d02e | - | - | - | - | M7C | Color sprite 7 --+-------+----+----+----+----+-------------------+------------------------
Obrázek 11: S využitím jednoduchého triku je možné zobrazit i více spritů než osm, jak je ukázáno na tomto screenshotu. Jediné omezení spočívá v tom, že se na jednom obrazovém řádku nesmí vyskytovat více než osm spritů, jejich celkový počet je limitován pouze výpočetním výkonem mikroprocesoru.
Sprity podporované čipem VIC-II mohou být zobrazeny, podobně jako pozadí, buď v režimu hi-res (vyšší horizontální rozlišení, jeden bit na pixel) nebo multi-color (poloviční horizontální rozlišení, dva bity na pixel). Režim zobrazení každého spritu lze nastavit příslušným bitem v registru 28 (sprite multicolor). Nejprve si popíšeme způsob zobrazení spritu v režimu hi-res. V tomto režimu má sprite rozlišení 24×21 pixelů, přičemž každý pixel je reprezentován pouze jediným bitem. Z toho vyplývá, že bitmapa se spritem má velikost 63 bytů, protože 24×21/8=63. V případě, že má bit odpovídající pixelu ve spritu hodnotu 0, jedná se o průhledný pixel, tj. příslušným pixelem může prosvítat buď jiný sprite nebo pozadí (v závislosti na tom, o který sprite se jedná). Pokud je bit odpovídající danému pixelu nastavený na hodnotu 1, je pixel vybarven barvou přečtenou z řídicího registru 39–46, opět v závislosti na tom, o který sprite 0–7 se jedná. Pomocí řídicího registru 23 a 29 je možné zvolit zvětšení spritu v horizontální či vertikální ose. Pokud je bit příslušný danému spritu nastaven na jedničku, je sprite 2× zvětšen buď v horizontální (registr 29) či vertikální (registr 23) ose, tj. nezmění se rozlišení spritu ale velikost jeho pixelů.
V případě, že je mód zobrazení spritu nastavený do režimu multi-color, jsou pro každý pixel ve spritu vyhrazeny dva bity. Velikost obsazené paměti zůstává zachována – 63 bajtů, ovšem horizontální rozlišení spritu se v tomto režimu snižuje na 12 pixelů a pixely mají dvojnásobnou šířku oproti režimu hi-res. Vzhledem k tomu, že je pomocí dvou bitů možné zakódovat celkem čtyři stavy, je určení barvy pixelu zajímavější než v režimu hi-res. Všechny sprity spolu sdílí dvě společné barvy z barvové palety, přičemž indexy těchto barev jsou uloženy v řídicích registrech 37 a 38. Třetí barva je pro každý sprite individuální – viz řídicí registry 39–46. Poslední bitová kombinace odpovídá pozadí, tj. v tomto případě je pixel spritu průhledný. I v režimu multi-color je možné sprity zvětšit v horizontální i vertikální ose, ovšem s tím rozdílem, že zvětšení v ose horizontální je buď dvojnásobné nebo čtyřnásobné oproti základní velikosti pixelu (odpovídající grafickému režimu hi-res).
V předchozích třech odstavcích jsme si řekli, že bitmapa každého spritu je vždy uložena v 63 bajtech, bez ohledu na to, zda se jedná o sprite zobrazovaný v režimu hi-res či multi-color. Těchto 63 bajtů je umístěno v adresovém prostoru o velikosti 16 kB, do kterého má v daném okamžiku čip VIC-II přístup. Mikroprocesor může změnit počáteční adresu bitmapy reprezentující sprite v krocích po 64 bajtech, což mj. znamená, že změna zobrazovaného tvaru je velmi rychlá – jedná se o přepis jediného bajtu. Pokud programátor dokáže ve chvíli, kdy elektronový paprsek neprovádí vykreslování, změnit adresu, na které se nachází bitmapa spritu, souřadnice spritu a popř. i jeho barvu (barvy), je možné zvýšit celkový počet zobrazitelných spritů z původních osmi až na řádově stovky.
Platí zde jediné omezení – na jednom obrazovém řádku se vykreslí pouze osm pixelů, jejichž podobu není možné měnit. Důvodem je to, že VIC-II si na začátku každého obrazového řádku přečte aktuální řádek každého spritu (tj. tři bajty) do svých interních bufferů. Právě díky tomu, že je obsah těchto bufferů obnovován v každém obrazovém řádku, je umožněno zvýšení celkového počtu spritů i postupná změna jejich souřadnic.
Obrázek 12: Demo, ve kterém se současně (na jedné obrazovce) zobrazuje 144 spritů.
5. Herní konzole NES
Základem při vykreslování spritů na herní konzoli NES je opět tabulka vzorků, ta je ovšem doplněna pomocnou pamětí o kapacitě 256 bajtů, která je umístěna přímo na čipu PPU. Programátor měl k této paměti přístup buď přes řídicí registry PPU, alternativně pak přes DMA. Ve zmíněných 256 bajtech (ty použijeme v konfiguračních příkladech) jsou umístěny informace o 64 spritech, tj. pro každý sprite jsou vyhrazeny čtyři bajty. V těchto bajtech se nachází horizontální pozice spritu, vertikální pozice spritu, horní dva bity barvy (spodní bity jsou přímo v tabulce vzorků), index do tabulky vzorků (ukazuje na tvar spritu) a konečně taktéž bitové příznaky: horizontální zrcadlení, vertikální zrcadlení a priorita spritu (před/za pozadím).
Obrázek 13: Screenshot ze hry Donkey Kong.
Kvůli dalším technologickým omezením čipu PPU mohlo být na jednom obrazovém řádku (tj. vedle sebe) zobrazeno pouze omezené množství spritů, tj. nebylo například možné všechny sprity umístit vedle sebe. Taktéž počet celkově zobrazovaných barev nedosáhl hodnoty 32 (16 pro pozadí, 16 pro sprity), ale pouze 25, přičemž barvová paleta obsahovala 48 barev a pět odstínů šedi (konkrétní způsob zobrazení barev byl na obou televizních normách poněkud odlišný).
Obrázek 14: Další screenshot ze hry Donkey Kong.
6. Modifikovatelné informace o spritech
Každý sprite je vykreslen, jak již ostatně velmi dobře víme, jako rastrový obrázek o rozměrech 8×8 pixelů nebo 8×16 pixelů. Zatímco samotný rastrový obrázek je uložen v paměti ROM na cartridge se hrou, musí mít NES k dispozici ještě informace o pozici spritů na obrazovce (x,y), index obrázku se spritem (tedy vlastně nepřímý „krátký“ ukazatel do paměti ROM) a navíc i speciální bajt, jehož bity určují část barvové palety, zrcadlení spritu atd.
V dnešním prvním demonstračním příkladu vykreslíme na obrazovku postavičku Maria:
Obrázek 15: Postavička Maria vykreslená dnešním prvním demonstračním příkladem.
Tato postavička je složena z osmi spritů, z nichž každý má rozměry 8×8 pixelů:
Obrázek 16: Postavička Maria složená z osmi spritů.
V paměti tedy musíme mít k dispozici data osmi spritů – souřadnice [x, y] číslo spritu (tile number) a atributový bajt, a to v pořadí y, tile number, attributes, x. Celkem se jedná o 32 bajtů (8×4 bajty). V assembleru je deklarace těchto 32 bajtů následující:
; data pro jeden "velký" sprite - Maria spritedata: .byte $10, $00, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $10, $01, $00, $10 .byte $18, $02, $00, $08 .byte $18, $03, $00, $10 .byte $20, $04, $00, $08 .byte $20, $05, $00, $10 .byte $28, $06, $00, $08 .byte $28, $07, $00, $10
Samotné bitmapy spritů (tj. jednotlivé dlaždice), jsou uloženy v souboru mario.chr, jenž lze získat následujícím způsobem (velikost je přesně 8 kB):
$ wget https://github.com/ericdigioia/6502-Assembly-NES-Game-Development-Playground/raw/main/mario.chr
Nesmíme zapomenout přilinkovat tento soubor do výsledného binárního obrazu cartridge:
.segment "CHARS" .incbin "mario.chr"
7. Načtení spritů do operační paměti
Výše uvedených 32 bajtů je nutné přenést do operační paměti, konkrétně na paměťovou stránku číslo 2 (tj. od adresy 0×200). Tím je umožněna například změna souřadnice spritu, změna jeho indexu (animace) atd.:
: lda spritedata,X ; budeme přesouvat data z této oblasti sta $0200,X ; uložení do paměti spritů inx ; zvýšení hodnoty počitadla cpx #32 ; každý sprite má 4 bajty: y-coord, tile, attributy, y-coord * 8 spritů = 32 bne :-
Dále povolíme NMI, tj. nemaskovatelné přerušení, které se automaticky vyvolává při každém zatemnění snímku (VBLANK):
cli ; vynulování bitu I - povolení přerušení lda #%10000000 sta PPUCTRL ; při každém VBLANK se vyvolá NMI (důležité!)
A konečně povolíme zobrazování spritů nastavením pátého bitu řídicího registru PPUMASK (s nímž jsme se již setkali minule):
lda #%00010000 ; povolení zobrazení spritů sta PPUMASK
Všechny výše popsané operace jsou realizovány v rámci podprogramu (subrutiny) load_sprites, kterou lze zavolat jedenkrát po resetu herní konzole NES:
; načtení spritů .proc load_sprites : lda spritedata,X ; budeme přesouvat data z této oblasti sta $0200,X ; uložení do paměti spritů inx ; zvýšení hodnoty počitadla cpx #32 ; každý sprite má 4 bajty: y-coord, tile, attributy, y-coord * 8 spritů = 32 bne :- cli ; vynulování bitu I - povolení přerušení lda #%10000000 sta PPUCTRL ; při každém VBLANK se vyvolá NMI (důležité!) lda #%00010000 ; povolení zobrazení spritů sta PPUMASK rts ; návrat ze subrutiny .endproc
8. Kód v obsluze nemaskovatelného přerušení
Pro zobrazení spritů nám zbývá jediná maličkost – redefinovat obslužnou rutinu zavolanou při každém vyvolání nemaskovatelného přerušení (NMI – Non-Maskable Interrupt). Toto přerušení je zavoláno při každém vertikálním zatemnění, kdy je možné měnit parametry dalšího snímku bez „blikání“. Nám prozatím postačuje pouze spustit DMA, které zajistí přenos rastru spritů na obrazovku (a to implementační detaily se vlastně programátor nemusí starat):
; Obslužná rutina pro NMI (nemaskovatelné přerušení, vertical blank) .proc nmi lda #$02 ; horní bajt adresy pro přenos + zahájení přenosu sta OAM_DMA rti ; návrat z přerušení .endproc
9. Úplný zdrojový kód dnešního prvního demonstračního příkladu
Úplný zdrojový kód dnešního prvního demonstračního příkladu (v pořadí již třináctého příkladu pro NES) je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example13.asm. Pro překlad a slinkování tohoto příkladu je zapotřebí i Makefile a příkaz make example15.nes:
; --------------------------------------------------------------------- ; Kostra programu pro herní konzoli NES ; Nastavení barvové palety, zvýšení intenzity barvy ; Setup PPU přes makro ; Definice spritu a zobrazení spritů s Mariem ; ; Založeno na příkladu https://github.com/depp/ctnes/tree/master/nesdev/01 ; Taktéž založeno na https://nerdy-nights.nes.science/#main_tutorial-3 ; Viz též článek na https://www.moria.us/blog/2018/03/nes-development ; Audio https://raw.githubusercontent.com/iliak/nes/master/doc/apu_ref.txt ; --------------------------------------------------------------------- ; Jména řídicích registrů použitých v kódu PPUCTRL = $2000 PPUMASK = $2001 PPUSTATUS = $2002 PPUADDR = $2006 PPUDATA = $2007 DMC_FREQ = $4010 OAM_DMA = $4014 ; Další důležité adresy PALETTE = $3f00 ; --------------------------------------------------------------------- ; Definice maker ; --------------------------------------------------------------------- .macro setup_cpu ; nastavení stavu CPU sei ; zákaz přerušení cld ; vypnutí dekadického režimu (není podporován) ldx #$ff txs ; vrchol zásobníku nastaven na 0xff (první stránka) .endmacro .macro wait_for_frame : bit PPUSTATUS ; test obsahu registru PPUSTATUS bpl :- ; skok, pokud je příznak N nulový .endmacro .macro clear_ram lda #$00 ; vynulování registru A : sta $000, x ; vynulování X-tého bajtu v nulté stránce sta $100, x sta $200, x sta $300, x sta $400, x sta $500, x sta $600, x sta $700, x ; vynulování X-tého bajtu v sedmé stránce inx ; přechod na další bajt bne :- ; po přetečení 0xff -> 0x00 konec smyčky .endmacro .macro ppu_data_palette_address lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR .endmacro ; --------------------------------------------------------------------- ; Definice hlavičky obrazu ROM ; --------------------------------------------------------------------- ; Size of PRG in units of 16 KiB. prg_npage = 2 ; Size of CHR in units of 8 KiB. chr_npage = 1 ; INES mapper number. mapper = 0 ; Mirroring (0 = horizontal, 1 = vertical) mirroring = 1 .segment "HEADER" .byte $4e, $45, $53, $1a .byte prg_npage .byte chr_npage .byte ((mapper & $0f) << 4) | (mirroring & 1) .byte mapper & $f0 .segment "ZEROPAGE" .segment "STARTUP" .segment "CODE" ; --------------------------------------------------------------------- ; Blok paměti s definicí dlaždic 8x8 pixelů ; --------------------------------------------------------------------- .segment "CHR0a" .segment "CHR0b" .code ; --------------------------------------------------------------------- ; Programový kód rutin pro NMI, RESET a IRQ volaných automaticky CPU ; ; viz též https://www.pagetable.com/?p=410 ; --------------------------------------------------------------------- ; Obslužná rutina pro NMI (nemaskovatelné přerušení, vertical blank) .proc nmi lda #$02 ; horní bajt adresy pro přenos + zahájení přenosu sta OAM_DMA rti ; návrat z přerušení .endproc ; Obslužná rutina pro IRQ (maskovatelné přerušení) .proc irq rti ; návrat z přerušení .endproc ; Obslužná rutina pro RESET .proc reset ; nastavení stavu CPU setup_cpu ; nastavení řídicích registrů ldx #$00 stx PPUCTRL ; nastavení PPUCTRL = 0 (NMI) stx PPUMASK ; nastavení PPUMASK = 0 stx DMC_FREQ ; zákaz DMC IRQ ldx #$40 stx $4017 ; interrupt inhibit bit ; čekání na vnitřní inicializaci PPU (dva snímky) wait_for_frame wait_for_frame ; vymazání obsahu RAM clear_ram ; čekání na další snímek wait_for_frame ; nastavení barvové palety jsr load_palette ; zavolání subrutiny ; nastavení spritů jsr load_sprites ; zavolání subrutiny ; vlastní herní smyčka je prozatím prázdná game_loop: jmp game_loop ; nekonečná smyčka (později rozšíříme) .endproc ; vynulování barvové palety .proc clear_palette ppu_data_palette_address ldx #$20 ; počitadlo barev v paletě: 16+16 lda #$00 ; vynulování každé barvy : sta PPUDATA ; zápis barvy dex ; snížení hodnoty počitadla bne :- rts ; návrat ze subrutiny .endproc ; nastavení barvové palety .proc load_palette ppu_data_palette_address ; $3f00-$3f0f - paleta pozadí ; $3f10-$3f1f - paleta spritů ldx #$00 ; vynulovat počitadlo a offset : lda palette, x ; načíst bajt s offsetem sta PPUDATA ; zápis barvy do PPU inx ; zvýšit počitadlo/offset cpx #32 ; limit počtu barev bne :- ; opakovat smyčku 32x rts ; návrat ze subrutiny .endproc ; načtení spritů .proc load_sprites ldx #0 ; vynulování počitadla : lda spritedata,X ; budeme přesouvat data z této oblasti sta $0200,X ; uložení do paměti spritů inx ; zvýšení hodnoty počitadla cpx #32 ; každý sprite má 4 bajty: y-coord, tile, attributy, y-coord * 8 spritů = 32 bne :- cli ; vynulování bitu I - povolení přerušení lda #%10000000 sta PPUCTRL ; při každém VBLANK se vyvolá NMI (důležité!) lda #%00010000 ; povolení zobrazení spritů sta PPUMASK rts ; návrat ze subrutiny .endproc ; samotná barvová paleta palette: .byte $22, $29, $1a, $0F, $22, $36, $17, $0F, $22, $30, $21, $0F, $22, $27, $17, $0F ; barvy pozadí .byte $22, $16, $27, $18, $22, $1A, $30, $27, $22, $16, $30, $27, $22, $0F, $36, $17 ; barvy spritů ; data pro osm spritů spritedata: .byte $10, $00, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $10, $01, $00, $10 .byte $18, $02, $00, $08 .byte $18, $03, $00, $10 .byte $20, $04, $00, $08 .byte $20, $05, $00, $10 .byte $28, $06, $00, $08 .byte $28, $07, $00, $10 ; --------------------------------------------------------------------- ; Tabulka vektorů CPU ; --------------------------------------------------------------------- .segment "VECTORS" .addr nmi .addr reset .addr irq .segment "CHARS" .incbin "mario.chr" ; --------------------------------------------------------------------- ; Finito ; ---------------------------------------------------------------------
10. Změna souřadnic spritů
Víme již, že všechny „dynamické“ údaje o spritech (kromě jejich bitmapy) jsou uloženy v operační paměti, konkrétně od adresy $0200 (což dává smysl, protože od adresy $0000 je uložena nultá stránka a od adresy $0100 zásobník). Velmi snadno je tedy možné měnit pozice spritů, a to je tak rychlá operace, že i na dnešní dobu až neskutečně pomalý mikroprocesor MOS 6502 dokáže změnit pozice spritů mezi jednotlivými snímky a tak zajistit animaci herního světa. My sice prozatím nevíme, jak animace tvořit, ovšem změna pozice spritů je snadná – postačuje změnit blok dat, který je kopírovaný z ROM právě na druhou stránku, tedy od adresy $0200:
; data pro osm spritů spritedata: .byte $10, $00, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $10, $01, $00, $12 .byte $1a, $02, $00, $08 .byte $1a, $03, $00, $12 .byte $24, $04, $00, $08 .byte $24, $05, $00, $12 .byte $2e, $06, $00, $08 .byte $2e, $07, $00, $12
Obrázek 17: Postavička Maria složená z osmi spritů, které jsou od sebe vzdáleny o čtyři pixely.
11. Modifikace barvových atributů spritů
Spodní dva bity třetího bajtu (ze čtyř), které popisují dynamické parametry spritů, obsahují dva bity z indexu (ukazatele) do barvové palety. Změnou těchto dvou bitů je tedy možné vybrat jednu ze čtyř barvových palet (každá se čtyřmi resp. třemi barvami + průhledností) vyhrazených pro sprity. Jednoduchou úpravou kódu můžeme dosáhnout toho, že každé dva sprity z celkových osmi zobrazených spritů budou používat odlišnou barvovou paletu:
spritedata: .byte $10, $00, $01, $08 ; y-coord, tile number, attributes, x-coord .byte $10, $01, $01, $12 .byte $1a, $02, $02, $08 .byte $1a, $03, $02, $12 .byte $24, $04, $03, $08 .byte $24, $05, $03, $12 .byte $2e, $06, $00, $08 .byte $2e, $07, $00, $12
Obrázek 18: Vždy dva sprity vedle sebe mají nastaven stejný barvový atribut.
12. Úplný zdrojový kód dnešního druhého demonstračního příkladu
Úplný zdrojový kód dnešního druhého demonstračního příkladu (v pořadí již čtrnáctého příkladu pro NES) je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example14.asm. Pro překlad a slinkování tohoto příkladu je zapotřebí i Makefile a příkaz make example15.nes:
; --------------------------------------------------------------------- ; Kostra programu pro herní konzoli NES ; Nastavení barvové palety, zvýšení intenzity barvy ; Setup PPU přes makro ; Definice spritu a zobrazení spritů s rozloženým Mariem ; ; Založeno na příkladu https://github.com/depp/ctnes/tree/master/nesdev/01 ; Taktéž založeno na https://nerdy-nights.nes.science/#main_tutorial-3 ; Viz též článek na https://www.moria.us/blog/2018/03/nes-development ; Audio https://raw.githubusercontent.com/iliak/nes/master/doc/apu_ref.txt ; --------------------------------------------------------------------- ; Jména řídicích registrů použitých v kódu PPUCTRL = $2000 PPUMASK = $2001 PPUSTATUS = $2002 PPUADDR = $2006 PPUDATA = $2007 DMC_FREQ = $4010 OAM_DMA = $4014 ; Další důležité adresy PALETTE = $3f00 ; --------------------------------------------------------------------- ; Definice maker ; --------------------------------------------------------------------- .macro setup_cpu ; nastavení stavu CPU sei ; zákaz přerušení cld ; vypnutí dekadického režimu (není podporován) ldx #$ff txs ; vrchol zásobníku nastaven na 0xff (první stránka) .endmacro .macro wait_for_frame : bit PPUSTATUS ; test obsahu registru PPUSTATUS bpl :- ; skok, pokud je příznak N nulový .endmacro .macro clear_ram lda #$00 ; vynulování registru A : sta $000, x ; vynulování X-tého bajtu v nulté stránce sta $100, x sta $200, x sta $300, x sta $400, x sta $500, x sta $600, x sta $700, x ; vynulování X-tého bajtu v sedmé stránce inx ; přechod na další bajt bne :- ; po přetečení 0xff -> 0x00 konec smyčky .endmacro .macro ppu_data_palette_address lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR .endmacro ; --------------------------------------------------------------------- ; Definice hlavičky obrazu ROM ; --------------------------------------------------------------------- ; Size of PRG in units of 16 KiB. prg_npage = 2 ; Size of CHR in units of 8 KiB. chr_npage = 1 ; INES mapper number. mapper = 0 ; Mirroring (0 = horizontal, 1 = vertical) mirroring = 1 .segment "HEADER" .byte $4e, $45, $53, $1a .byte prg_npage .byte chr_npage .byte ((mapper & $0f) << 4) | (mirroring & 1) .byte mapper & $f0 .segment "ZEROPAGE" .segment "STARTUP" .segment "CODE" ; --------------------------------------------------------------------- ; Blok paměti s definicí dlaždic 8x8 pixelů ; --------------------------------------------------------------------- .segment "CHR0a" .segment "CHR0b" .code ; --------------------------------------------------------------------- ; Programový kód rutin pro NMI, RESET a IRQ volaných automaticky CPU ; ; viz též https://www.pagetable.com/?p=410 ; --------------------------------------------------------------------- ; Obslužná rutina pro NMI (nemaskovatelné přerušení, vertical blank) .proc nmi lda #$02 ; horní bajt adresy pro přenos + zahájení přenosu sta OAM_DMA rti ; návrat z přerušení .endproc ; Obslužná rutina pro IRQ (maskovatelné přerušení) .proc irq rti ; návrat z přerušení .endproc ; Obslužná rutina pro RESET .proc reset ; nastavení stavu CPU setup_cpu ; nastavení řídicích registrů ldx #$00 stx PPUCTRL ; nastavení PPUCTRL = 0 (NMI) stx PPUMASK ; nastavení PPUMASK = 0 stx DMC_FREQ ; zákaz DMC IRQ ldx #$40 stx $4017 ; interrupt inhibit bit ; čekání na vnitřní inicializaci PPU (dva snímky) wait_for_frame wait_for_frame ; vymazání obsahu RAM clear_ram ; čekání na další snímek wait_for_frame ; nastavení barvové palety jsr load_palette ; zavolání subrutiny ; nastavení spritů jsr load_sprites ; zavolání subrutiny ; vlastní herní smyčka je prozatím prázdná game_loop: jmp game_loop ; nekonečná smyčka (později rozšíříme) .endproc ; vynulování barvové palety .proc clear_palette ppu_data_palette_address ldx #$20 ; počitadlo barev v paletě: 16+16 lda #$00 ; vynulování každé barvy : sta PPUDATA ; zápis barvy dex ; snížení hodnoty počitadla bne :- rts ; návrat ze subrutiny .endproc ; nastavení barvové palety .proc load_palette ppu_data_palette_address ; $3f00-$3f0f - paleta pozadí ; $3f10-$3f1f - paleta spritů ldx #$00 ; vynulovat počitadlo a offset : lda palette, x ; načíst bajt s offsetem sta PPUDATA ; zápis barvy do PPU inx ; zvýšit počitadlo/offset cpx #32 ; limit počtu barev bne :- ; opakovat smyčku 32x rts ; návrat ze subrutiny .endproc ; načtení spritů .proc load_sprites ldx #0 ; vynulování počitadla : lda spritedata,X ; budeme přesouvat data z této oblasti sta $0200,X ; uložení do paměti spritů inx ; zvýšení hodnoty počitadla cpx #32 ; každý sprite má 4 bajty: y-coord, tile, attributy, y-coord * 8 spritů = 32 bne :- cli ; vynulování bitu I - povolení přerušení lda #%10000000 sta PPUCTRL ; při každém VBLANK se vyvolá NMI (důležité!) lda #%00010000 ; povolení zobrazení spritů sta PPUMASK rts ; návrat ze subrutiny .endproc ; samotná barvová paleta palette: .byte $22, $29, $1a, $0F, $22, $36, $17, $0F, $22, $30, $21, $0F, $22, $27, $17, $0F ; barvy pozadí .byte $22, $16, $27, $18, $22, $1A, $30, $27, $22, $16, $30, $27, $22, $0F, $36, $17 ; barvy spritů ; data pro osm spritů spritedata: .byte $10, $00, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $10, $01, $00, $12 .byte $1a, $02, $00, $08 .byte $1a, $03, $00, $12 .byte $24, $04, $00, $08 .byte $24, $05, $00, $12 .byte $2e, $06, $00, $08 .byte $2e, $07, $00, $12 ; --------------------------------------------------------------------- ; Tabulka vektorů CPU ; --------------------------------------------------------------------- .segment "VECTORS" .addr nmi .addr reset .addr irq .segment "CHARS" .incbin "mario.chr" ; --------------------------------------------------------------------- ; Finito ; ---------------------------------------------------------------------
13. Větší počet spritů na obrazovce rozdělených do řádků
Celkový počet spritů zobrazitelných na obrazovce herní konzole NES je 64, ovšem současně platí omezení pro osm spritů na každém řádku. Podívejme se nyní na způsob zobrazení celkem 2×4×4=32 spritů resp. 2×5×4=40 spritů, přičemž na každém obrazovém řádku jsou zobrazeny maximálně dva sprity (a tudíž pro nás druhé omezení nehraje roli):
Obrázek 19: Čtyři postavičky, každá sestavená z osmi spritů = 32 spritů celkem (a 128 bajtů RAM).
Data pro čtyři postavičky:
; data pro větší množství spritů spritedata: .byte $10, $00, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $10, $01, $00, $10 .byte $18, $02, $00, $08 .byte $18, $03, $00, $10 .byte $20, $04, $00, $08 .byte $20, $05, $00, $10 .byte $28, $06, $00, $08 .byte $28, $07, $00, $10 .byte $40, $08, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $40, $09, $00, $10 .byte $48, $0a, $00, $08 .byte $48, $0b, $00, $10 .byte $50, $0c, $00, $08 .byte $50, $0d, $00, $10 .byte $58, $0d, $00, $08 .byte $58, $0f, $00, $10 .byte $70, $10, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $70, $11, $00, $10 .byte $78, $12, $00, $08 .byte $78, $13, $00, $10 .byte $80, $14, $00, $08 .byte $80, $15, $00, $10 .byte $88, $16, $00, $08 .byte $88, $17, $00, $10 .byte $a0, $18, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $a0, $19, $00, $10 .byte $a8, $1a, $00, $08 .byte $a8, $1b, $00, $10 .byte $b0, $1c, $00, $08 .byte $b0, $1d, $00, $10 .byte $b8, $1d, $00, $08 .byte $b8, $1f, $00, $10
Obrázek 20: Pět postaviček, každá sestavená z osmi spritů = 40 spritů celkem (a 160 bajtů RAM).
Data pro pět postaviček:
; data pro větší množství spritů spritedata: .byte $10, $00, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $10, $01, $00, $10 .byte $18, $02, $00, $08 .byte $18, $03, $00, $10 .byte $20, $04, $00, $08 .byte $20, $05, $00, $10 .byte $28, $06, $00, $08 .byte $28, $07, $00, $10 .byte $30, $08, $00, $18 ; y-coord, tile number, attributes, x-coord .byte $30, $09, $00, $20 .byte $38, $0a, $00, $18 .byte $38, $0b, $00, $20 .byte $40, $0c, $00, $18 .byte $40, $0d, $00, $20 .byte $48, $0d, $00, $18 .byte $48, $0f, $00, $20 .byte $50, $10, $00, $28 ; y-coord, tile number, attributes, x-coord .byte $50, $11, $00, $30 .byte $58, $12, $00, $28 .byte $58, $13, $00, $30 .byte $60, $14, $00, $28 .byte $60, $15, $00, $30 .byte $68, $16, $00, $28 .byte $68, $17, $00, $30 .byte $70, $18, $00, $38 ; y-coord, tile number, attributes, x-coord .byte $70, $19, $00, $40 .byte $78, $1a, $00, $38 .byte $78, $1b, $00, $40 .byte $80, $1c, $00, $38 .byte $80, $1d, $00, $40 .byte $88, $1d, $00, $38 .byte $88, $1f, $00, $40 .byte $90, $20, $00, $48 ; y-coord, tile number, attributes, x-coord .byte $90, $21, $00, $50 .byte $98, $22, $00, $48 .byte $98, $23, $00, $50 .byte $a0, $24, $00, $48 .byte $a0, $25, $00, $50 .byte $a8, $26, $00, $48 .byte $a8, $27, $00, $50
14. Úplný zdrojový kód dnešního třetího demonstračního příkladu
Úplný zdrojový kód dnešního třetího demonstračního příkladu (v pořadí již patnáctého příkladu pro NES) je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example15.asm. Pro překlad a slinkování tohoto příkladu je zapotřebí i Makefile a příkaz make example15.nes:
; --------------------------------------------------------------------- ; Kostra programu pro herní konzoli NES ; Nastavení barvové palety, zvýšení intenzity barvy ; Setup PPU přes makro ; Definice spritu a zobrazení spritů s rozloženým Mariem. Současné ; zobrazení většího množství spritů. ; ; Založeno na příkladu https://github.com/depp/ctnes/tree/master/nesdev/01 ; Taktéž založeno na https://nerdy-nights.nes.science/#main_tutorial-3 ; Viz též článek na https://www.moria.us/blog/2018/03/nes-development ; Audio https://raw.githubusercontent.com/iliak/nes/master/doc/apu_ref.txt ; --------------------------------------------------------------------- ; Jména řídicích registrů použitých v kódu PPUCTRL = $2000 PPUMASK = $2001 PPUSTATUS = $2002 PPUADDR = $2006 PPUDATA = $2007 DMC_FREQ = $4010 OAM_DMA = $4014 ; Další důležité adresy PALETTE = $3f00 ; --------------------------------------------------------------------- ; Definice maker ; --------------------------------------------------------------------- .macro setup_cpu ; nastavení stavu CPU sei ; zákaz přerušení cld ; vypnutí dekadického režimu (není podporován) ldx #$ff txs ; vrchol zásobníku nastaven na 0xff (první stránka) .endmacro .macro wait_for_frame : bit PPUSTATUS ; test obsahu registru PPUSTATUS bpl :- ; skok, pokud je příznak N nulový .endmacro .macro clear_ram lda #$00 ; vynulování registru A : sta $000, x ; vynulování X-tého bajtu v nulté stránce sta $100, x sta $200, x sta $300, x sta $400, x sta $500, x sta $600, x sta $700, x ; vynulování X-tého bajtu v sedmé stránce inx ; přechod na další bajt bne :- ; po přetečení 0xff -> 0x00 konec smyčky .endmacro .macro ppu_data_palette_address lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR .endmacro ; --------------------------------------------------------------------- ; Definice hlavičky obrazu ROM ; --------------------------------------------------------------------- ; Size of PRG in units of 16 KiB. prg_npage = 2 ; Size of CHR in units of 8 KiB. chr_npage = 1 ; INES mapper number. mapper = 0 ; Mirroring (0 = horizontal, 1 = vertical) mirroring = 1 .segment "HEADER" .byte $4e, $45, $53, $1a .byte prg_npage .byte chr_npage .byte ((mapper & $0f) << 4) | (mirroring & 1) .byte mapper & $f0 .segment "ZEROPAGE" .segment "STARTUP" .segment "CODE" ; --------------------------------------------------------------------- ; Blok paměti s definicí dlaždic 8x8 pixelů ; --------------------------------------------------------------------- .segment "CHR0a" .segment "CHR0b" .code ; --------------------------------------------------------------------- ; Programový kód rutin pro NMI, RESET a IRQ volaných automaticky CPU ; ; viz též https://www.pagetable.com/?p=410 ; --------------------------------------------------------------------- ; Obslužná rutina pro NMI (nemaskovatelné přerušení, vertical blank) .proc nmi lda #$02 ; horní bajt adresy pro přenos + zahájení přenosu sta OAM_DMA rti ; návrat z přerušení .endproc ; Obslužná rutina pro IRQ (maskovatelné přerušení) .proc irq rti ; návrat z přerušení .endproc ; Obslužná rutina pro RESET .proc reset ; nastavení stavu CPU setup_cpu ; nastavení řídicích registrů ldx #$00 stx PPUCTRL ; nastavení PPUCTRL = 0 (NMI) stx PPUMASK ; nastavení PPUMASK = 0 stx DMC_FREQ ; zákaz DMC IRQ ldx #$40 stx $4017 ; interrupt inhibit bit ; čekání na vnitřní inicializaci PPU (dva snímky) wait_for_frame wait_for_frame ; vymazání obsahu RAM clear_ram ; čekání na další snímek wait_for_frame ; nastavení barvové palety jsr load_palette ; zavolání subrutiny ; nastavení spritů jsr load_sprites ; zavolání subrutiny ; vlastní herní smyčka je prozatím prázdná game_loop: jmp game_loop ; nekonečná smyčka (později rozšíříme) .endproc ; vynulování barvové palety .proc clear_palette ppu_data_palette_address ldx #$20 ; počitadlo barev v paletě: 16+16 lda #$00 ; vynulování každé barvy : sta PPUDATA ; zápis barvy dex ; snížení hodnoty počitadla bne :- rts ; návrat ze subrutiny .endproc ; nastavení barvové palety .proc load_palette ppu_data_palette_address ; $3f00-$3f0f - paleta pozadí ; $3f10-$3f1f - paleta spritů ldx #$00 ; vynulovat počitadlo a offset : lda palette, x ; načíst bajt s offsetem sta PPUDATA ; zápis barvy do PPU inx ; zvýšit počitadlo/offset cpx #32 ; limit počtu barev bne :- ; opakovat smyčku 32x rts ; návrat ze subrutiny .endproc ; načtení spritů .proc load_sprites ldx #0 : lda spritedata,X ; budeme přesouvat data z této oblasti sta $0200,X ; uložení do paměti spritů inx ; zvýšení hodnoty počitadla cpx #128 ; každý sprite má 4 bajty: y-coord, tile, attributy, y-coord * 8 spritů = 32 ; * 4 postavičky = 128 bne :- cli ; vynulování bitu I - povolení přerušení lda #%10000000 sta PPUCTRL ; při každém VBLANK se vyvolá NMI (důležité!) lda #%00010000 ; povolení zobrazení spritů sta PPUMASK rts ; návrat ze subrutiny .endproc ; samotná barvová paleta palette: .byte $22, $29, $1a, $0F, $22, $36, $17, $0F, $22, $30, $21, $0F, $22, $27, $17, $0F ; barvy pozadí .byte $22, $16, $27, $18, $22, $1A, $30, $27, $22, $16, $30, $27, $22, $0F, $36, $17 ; barvy spritů ; data pro větší množství spritů spritedata: .byte $10, $00, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $10, $01, $00, $10 .byte $18, $02, $00, $08 .byte $18, $03, $00, $10 .byte $20, $04, $00, $08 .byte $20, $05, $00, $10 .byte $28, $06, $00, $08 .byte $28, $07, $00, $10 .byte $40, $08, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $40, $09, $00, $10 .byte $48, $0a, $00, $08 .byte $48, $0b, $00, $10 .byte $50, $0c, $00, $08 .byte $50, $0d, $00, $10 .byte $58, $0d, $00, $08 .byte $58, $0f, $00, $10 .byte $70, $10, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $70, $11, $00, $10 .byte $78, $12, $00, $08 .byte $78, $13, $00, $10 .byte $80, $14, $00, $08 .byte $80, $15, $00, $10 .byte $88, $16, $00, $08 .byte $88, $17, $00, $10 .byte $a0, $18, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $a0, $19, $00, $10 .byte $a8, $1a, $00, $08 .byte $a8, $1b, $00, $10 .byte $b0, $1c, $00, $08 .byte $b0, $1d, $00, $10 .byte $b8, $1d, $00, $08 .byte $b8, $1f, $00, $10 ; --------------------------------------------------------------------- ; Tabulka vektorů CPU ; --------------------------------------------------------------------- .segment "VECTORS" .addr nmi .addr reset .addr irq .segment "CHARS" .incbin "mario.chr" ; --------------------------------------------------------------------- ; Finito ; ---------------------------------------------------------------------
15. Větší množství spritů na jediném řádku
Omezení celkového počtu maximálně osmi spritů na obrazovém řádku je velmi dobře viditelné v případě, že se pokusíme zobrazit pět postaviček vedle sebe, tudíž deset spritů na řádku. V tomto případě sprity napravo od prvních osmi jednoduše nejsou zobrazeny:
Obrázek 21: Pět postaviček vedle sebe = 10 spritů na řádku. Pravá postava není viditelná.
Obrázek 22: Pět postaviček vedle sebe = 10 spritů na řádku. Vertikální posun jedné postavy ukazuje, jak omezení na osm spritů na řádku reálně funguje.
Obrázek 23: Další posun čtvrté postavičky zajistí, že se již zobrazí všechny sprity.
Data pro pět postaviček zobrazených vedle sebe:
; data pro větší množství spritů spritedata: .byte $10, $00, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $10, $01, $00, $10 .byte $18, $02, $00, $08 .byte $18, $03, $00, $10 .byte $20, $04, $00, $08 .byte $20, $05, $00, $10 .byte $28, $06, $00, $08 .byte $28, $07, $00, $10 .byte $10, $08, $00, $28 ; y-coord, tile number, attributes, x-coord .byte $10, $09, $00, $30 .byte $18, $0a, $00, $28 .byte $18, $0b, $00, $30 .byte $20, $0c, $00, $28 .byte $20, $0d, $00, $30 .byte $28, $0d, $00, $28 .byte $28, $0f, $00, $30 .byte $10, $10, $00, $48 ; y-coord, tile number, attributes, x-coord .byte $10, $11, $00, $50 .byte $18, $12, $00, $48 .byte $18, $13, $00, $50 .byte $20, $14, $00, $48 .byte $20, $15, $00, $50 .byte $28, $16, $00, $48 .byte $28, $17, $00, $50 .byte $30, $18, $00, $68 ; y-coord, tile number, attributes, x-coord .byte $30, $19, $00, $70 .byte $38, $1a, $00, $68 .byte $38, $1b, $00, $70 .byte $40, $1c, $00, $68 .byte $40, $1d, $00, $70 .byte $48, $1e, $00, $68 .byte $48, $1f, $00, $70 .byte $10, $10, $00, $88 ; y-coord, tile number, attributes, x-coord .byte $10, $11, $00, $90 .byte $18, $12, $00, $88 .byte $18, $13, $00, $90 .byte $20, $14, $00, $88 .byte $20, $15, $00, $90 .byte $28, $16, $00, $88 .byte $28, $17, $00, $90
16. Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu
Úplný zdrojový kód dnešního posledního demonstračního příkladu (v pořadí již šestnáctého příkladu pro NES) je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example16.asm. Pro překlad a slinkování tohoto příkladu je zapotřebí i Makefile a příkaz make example15.nes:
; --------------------------------------------------------------------- ; Kostra programu pro herní konzoli NES ; Nastavení barvové palety, zvýšení intenzity barvy ; Setup PPU přes makro ; Definice spritu a zobrazení spritů s rozloženým Mariem. Současné ; zobrazení většího množství spritů. ; ; Založeno na příkladu https://github.com/depp/ctnes/tree/master/nesdev/01 ; Taktéž založeno na https://nerdy-nights.nes.science/#main_tutorial-3 ; Viz též článek na https://www.moria.us/blog/2018/03/nes-development ; Audio https://raw.githubusercontent.com/iliak/nes/master/doc/apu_ref.txt ; --------------------------------------------------------------------- ; Jména řídicích registrů použitých v kódu PPUCTRL = $2000 PPUMASK = $2001 PPUSTATUS = $2002 PPUADDR = $2006 PPUDATA = $2007 DMC_FREQ = $4010 OAM_DMA = $4014 ; Další důležité adresy PALETTE = $3f00 ; --------------------------------------------------------------------- ; Definice maker ; --------------------------------------------------------------------- .macro setup_cpu ; nastavení stavu CPU sei ; zákaz přerušení cld ; vypnutí dekadického režimu (není podporován) ldx #$ff txs ; vrchol zásobníku nastaven na 0xff (první stránka) .endmacro .macro wait_for_frame : bit PPUSTATUS ; test obsahu registru PPUSTATUS bpl :- ; skok, pokud je příznak N nulový .endmacro .macro clear_ram lda #$00 ; vynulování registru A : sta $000, x ; vynulování X-tého bajtu v nulté stránce sta $100, x sta $200, x sta $300, x sta $400, x sta $500, x sta $600, x sta $700, x ; vynulování X-tého bajtu v sedmé stránce inx ; přechod na další bajt bne :- ; po přetečení 0xff -> 0x00 konec smyčky .endmacro .macro ppu_data_palette_address lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR .endmacro ; --------------------------------------------------------------------- ; Definice hlavičky obrazu ROM ; --------------------------------------------------------------------- ; Size of PRG in units of 16 KiB. prg_npage = 2 ; Size of CHR in units of 8 KiB. chr_npage = 1 ; INES mapper number. mapper = 0 ; Mirroring (0 = horizontal, 1 = vertical) mirroring = 1 .segment "HEADER" .byte $4e, $45, $53, $1a .byte prg_npage .byte chr_npage .byte ((mapper & $0f) << 4) | (mirroring & 1) .byte mapper & $f0 .segment "ZEROPAGE" .segment "STARTUP" .segment "CODE" ; --------------------------------------------------------------------- ; Blok paměti s definicí dlaždic 8x8 pixelů ; --------------------------------------------------------------------- .segment "CHR0a" .segment "CHR0b" .code ; --------------------------------------------------------------------- ; Programový kód rutin pro NMI, RESET a IRQ volaných automaticky CPU ; ; viz též https://www.pagetable.com/?p=410 ; --------------------------------------------------------------------- ; Obslužná rutina pro NMI (nemaskovatelné přerušení, vertical blank) .proc nmi lda #$02 ; horní bajt adresy pro přenos + zahájení přenosu sta OAM_DMA rti ; návrat z přerušení .endproc ; Obslužná rutina pro IRQ (maskovatelné přerušení) .proc irq rti ; návrat z přerušení .endproc ; Obslužná rutina pro RESET .proc reset ; nastavení stavu CPU setup_cpu ; nastavení řídicích registrů ldx #$00 stx PPUCTRL ; nastavení PPUCTRL = 0 (NMI) stx PPUMASK ; nastavení PPUMASK = 0 stx DMC_FREQ ; zákaz DMC IRQ ldx #$40 stx $4017 ; interrupt inhibit bit ; čekání na vnitřní inicializaci PPU (dva snímky) wait_for_frame wait_for_frame ; vymazání obsahu RAM clear_ram ; čekání na další snímek wait_for_frame ; nastavení barvové palety jsr load_palette ; zavolání subrutiny ; nastavení spritů jsr load_sprites ; zavolání subrutiny ; vlastní herní smyčka je prozatím prázdná game_loop: jmp game_loop ; nekonečná smyčka (později rozšíříme) .endproc ; vynulování barvové palety .proc clear_palette ppu_data_palette_address ldx #$20 ; počitadlo barev v paletě: 16+16 lda #$00 ; vynulování každé barvy : sta PPUDATA ; zápis barvy dex ; snížení hodnoty počitadla bne :- rts ; návrat ze subrutiny .endproc ; nastavení barvové palety .proc load_palette ppu_data_palette_address ; $3f00-$3f0f - paleta pozadí ; $3f10-$3f1f - paleta spritů ldx #$00 ; vynulovat počitadlo a offset : lda palette, x ; načíst bajt s offsetem sta PPUDATA ; zápis barvy do PPU inx ; zvýšit počitadlo/offset cpx #32 ; limit počtu barev bne :- ; opakovat smyčku 32x rts ; návrat ze subrutiny .endproc ; načtení spritů .proc load_sprites ldx #0 : lda spritedata,X ; budeme přesouvat data z této oblasti sta $0200,X ; uložení do paměti spritů inx ; zvýšení hodnoty počitadla cpx #160 ; každý sprite má 4 bajty: y-coord, tile, attributy, y-coord * 8 spritů = 32 ; * 5 postaviček = 160 bajtů bne :- cli ; vynulování bitu I - povolení přerušení lda #%10000000 sta PPUCTRL ; při každém VBLANK se vyvolá NMI (důležité!) lda #%00010000 ; povolení zobrazení spritů sta PPUMASK rts ; návrat ze subrutiny .endproc ; samotná barvová paleta palette: .byte $22, $29, $1a, $0F, $22, $36, $17, $0F, $22, $30, $21, $0F, $22, $27, $17, $0F ; barvy pozadí .byte $22, $16, $27, $18, $22, $1A, $30, $27, $22, $16, $30, $27, $22, $0F, $36, $17 ; barvy spritů ; data pro větší množství spritů spritedata: .byte $10, $00, $00, $08 ; y-coord, tile number, attributes, x-coord .byte $10, $01, $00, $10 .byte $18, $02, $00, $08 .byte $18, $03, $00, $10 .byte $20, $04, $00, $08 .byte $20, $05, $00, $10 .byte $28, $06, $00, $08 .byte $28, $07, $00, $10 .byte $10, $08, $00, $28 ; y-coord, tile number, attributes, x-coord .byte $10, $09, $00, $30 .byte $18, $0a, $00, $28 .byte $18, $0b, $00, $30 .byte $20, $0c, $00, $28 .byte $20, $0d, $00, $30 .byte $28, $0d, $00, $28 .byte $28, $0f, $00, $30 .byte $10, $10, $00, $48 ; y-coord, tile number, attributes, x-coord .byte $10, $11, $00, $50 .byte $18, $12, $00, $48 .byte $18, $13, $00, $50 .byte $20, $14, $00, $48 .byte $20, $15, $00, $50 .byte $28, $16, $00, $48 .byte $28, $17, $00, $50 .byte $30, $18, $00, $68 ; y-coord, tile number, attributes, x-coord .byte $30, $19, $00, $70 .byte $38, $1a, $00, $68 .byte $38, $1b, $00, $70 .byte $40, $1c, $00, $68 .byte $40, $1d, $00, $70 .byte $48, $1e, $00, $68 .byte $48, $1f, $00, $70 .byte $10, $10, $00, $88 ; y-coord, tile number, attributes, x-coord .byte $10, $11, $00, $90 .byte $18, $12, $00, $88 .byte $18, $13, $00, $90 .byte $20, $14, $00, $88 .byte $20, $15, $00, $90 .byte $28, $16, $00, $88 .byte $28, $17, $00, $90 ; --------------------------------------------------------------------- ; Tabulka vektorů CPU ; --------------------------------------------------------------------- .segment "VECTORS" .addr nmi .addr reset .addr irq .segment "CHARS" .incbin "mario.chr" ; --------------------------------------------------------------------- ; Finito ; ---------------------------------------------------------------------
17. Repositář s demonstračními příklady
Demonstrační příklady napsané v assembleru, které jsou určené pro překlad pomocí assembleru ca65 (jenž je součástí cc65), byly uložen do Git repositáře, který je dostupný na adrese https://github.com/tisnik/8bit-fame. Jednotlivé demonstrační 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ář:
18. Odkazy na Internetu
- NesDev.org
https://www.nesdev.org/ - The Sprite Attribute Byte
https://www.patater.com/nes-asm-tutorials/day-17/ - How to Program an NES game in C
https://nesdoug.com/ - 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 - „Game Development in Eight Bits“ by Kevin Zurawel
https://www.youtube.com/watch?v=TPbroUDHG0s&list=PLcGKfGEEONaBjSfQaSiU9yQsjPxxDQyV8&index=4 - Game Development for the 8-bit NES: A class by Bob Rost
http://bobrost.com/nes/ - Game Development for the 8-bit NES: Lecture Notes
http://bobrost.com/nes/lectures.php - NES Graphics Explained
https://www.youtube.com/watch?v=7Co_8dC2zb8 - NES GAME PROGRAMMING PART 1
https://rpgmaker.net/tutorials/227/?post=240020 - 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/ - History of video game consoles (second generation): Wikipedia
http://en.wikipedia.org/wiki/History_of_video_game_consoles_(second_generation) - 6502 – the first RISC µP
http://ericclever.com/6500/ - 3 Generations of Game Machine Architecture
http://www.atariarchives.org/dev/CGEXPO99.html - bee – The Multi-Console Emulator
http://www.thebeehive.ws/ - Nerdy Nights Mirror
https://nerdy-nights.nes.science/ - The Nerdy Nights ca65 Remix
https://github.com/ddribin/nerdy-nights - NES Development Day 1: Creating a ROM
https://www.moria.us/blog/2018/03/nes-development - How to Start Making NES Games
https://www.matthughson.com/2021/11/17/how-to-start-making-nes-games/ - 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 - Nocash NES Specs
http://nocash.emubase.de/everynes.htm - Nintendo Entertainment System
http://cs.wikipedia.org/wiki/NES - Nintendo Entertainment System Architecture
http://nesdev.icequake.net/nes.txt - NesDev
http://nesdev.parodius.com/ - 2A03 technical reference
http://nesdev.parodius.com/2A03%20technical%20reference.txt - NES Dev wiki: 2A03
http://wiki.nesdev.com/w/index.php/2A03 - Ricoh 2A03
http://en.wikipedia.org/wiki/Ricoh_2A03 - 2A03 pinouts
http://nesdev.parodius.com/2A03_pinout.txt - 27c3: Reverse Engineering the MOS 6502 CPU (en)
https://www.youtube.com/watch?v=fWqBmmPQP40 - “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/ - Nintendo Entertainment System (NES)
https://8bitworkshop.com/docs/platforms/nes/ - Question about NES vectors and PPU
https://archive.nes.science/nesdev-forums/f10/t4154.xhtml - How do mapper chips actually work?
https://archive.nes.science/nesdev-forums/f9/t13125.xhtml - INES
https://www.nesdev.org/wiki/INES - NES Basics and Our First Game
http://thevirtualmountain.com/nes/2017/03/08/nes-basics-and-our-first-game.html - Where is the reset vector in a .nes file?
https://archive.nes.science/nesdev-forums/f10/t17413.xhtml - CPU memory map
https://www.nesdev.org/wiki/CPU_memory_map - How to make NES music
http://blog.snugsound.com/2008/08/how-to-make-nes-music.html - Nintendo Entertainment System Architecture
http://nesdev.icequake.net/nes.txt - MIDINES
http://www.wayfar.net/0×f00000_overview.php - FamiTracker
http://famitracker.com/ - nerdTracker II
http://nesdev.parodius.com/nt2/ - How NES Graphics work
http://nesdev.parodius.com/nesgfx.txt - NES Technical/Emulation/Development FAQ
http://nesdev.parodius.com/NESTechFAQ.htm - 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 - Day 1 – Beginning NES Assembly
https://www.patater.com/nes-asm-tutorials/day-1/ - Day 2 – A Source Code File's Structure
https://www.patater.com/nes-asm-tutorials/day-2/ - Assembly Language Misconceptions
https://www.youtube.com/watch?v=8_0tbkbSGRE - How Machine Language Works
https://www.youtube.com/watch?v=HWpi9n2H3kE