Hlavní navigace

Kouzlo minimalismu: vývoj her a demo programů pro herní konzoli NES

2. 6. 2022
Doba čtení: 30 minut

Sdílet

 Autor: Depositphotos
Na sérii článků o vývoji her (či spíše hříček) pro osmibitovou herní konzoli Atari 2600 s využitím Batari Basicu dnes nepřímo navážeme. Řekneme si totiž, jak vyvíjet hry a dema pro slavnou a přelomovou herní konzoli NES.

Obsah

1. Kouzlo minimalismu: vývoj her a grafických i zvukových dem pro herní konzoli NES

2. Slavná osmibitová herní konzole NES

3. Architektura herní konzole NES

4. Základní technické parametry herní konzole NES

5. Osmibitový mikroprocesor Ricoh 2A03

6. Další podpůrné moduly integrované do čipu 2A03/2A07

7. Zvukový subsystém herní konzole NES

8. Generátory obdélníkových signálů a trojúhelníkového signálu

9. Generátor šumu

10. Adresy všech řídicích registrů zvukového subsystému

11. Grafický subsystém NESu

12. Assemblery a překladače pro NES

13. Komplexní vývojová prostředí

14. Základní programové vybavení pro vývoj her pro NE

15. Instalace ca65 a cc65

16. Instalace emulátoru NESu

17. Kostra programu pro NES vytvořená v céčku

18. Kostra programu pro NES vytvořená v assembleru

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

20. Odkazy na Internetu

1. Kouzlo minimalismu: vývoj her a grafických i zvukových dem pro herní konzoli NES

Na sérii článků o vývoji her (či spíše hříček) pro osmibitovou herní konzoli Atari 2600 s využitím Batari Basicu [1] [2] [3] dnes nepřímo navážeme. Řekneme si totiž, jakým způsobem je možné vyvíjet hry a grafická či hudební dema pro slavnou a přelomovou herní konzoli NES (Nintendo Entertainment System). Tato konzole již nemá tak omezené HW prostředky jako Atari 2600, což programátorům umožňuje tvorbu složitějších her (Legend of Zelda, Castlevania či Metroid) a dostupné systémové prostředky (kapacita ROM i RAM) jsou vlastně tak velké, že se dá s úspěchem použít i programovací jazyk C. V tomto miniseriálu se zaměříme jak na použití C, tak i assembleru.

Poznámka: existoval, resp. existuje i NESBasic, což je kombinace Basicu a assembleru. Ovšem zdá se, že se již příliš nepoužívá – komunita se dnes zaměřuje právě na assembler a ve druhé řadě na C.

Existuje několik rozumných i nerozumných důvodů, proč se vlastně zabývat dnes již historickou herní konzolí:

  1. Jedná se o stroj, na kterém má programátor přímý přístup k hardware, a to bez jakýchkoli pomocných prostředků – žádná ROM s BIOSem či dokonce s operačním systémem. Vše je zapotřebí řešit, dokonce i správně nastavit přerušovací vektory atd.
  2. Lze získat reálné výsledky, což mi přijde jako více produktivní činnost, než se zúčastnit různých code golfů (ale to je pochopitelně čistě subjektivní názor), nebo používat různé jednodeskové minipočítače, kde lze většinou jen blikat LED.
  3. Komunita okolo NESu je stále aktivní a velmi přátelská. Vzniká i velké množství retroher.
  4. V případě NESu, a obecně osmibitových herních konzolí i osmibitových domácích mikropočítačů, se jedná o jednu z mála platforem, kde má význam ve větší míře používat assembler.
  5. Mikroprocesor MOS 6502 je navržený minimalisticky, má jen několik desítek instrukcí a práce s ním může být zábavná (samozřejmě opět čistě subjektivní popis).
  6. Pro vývoj jsou zapotřebí jen minimální prostředky – assembler, popř. překladač céčka, editor grafiky, textový editor a emulátor herní konzole. To je velmi osvěžující, zejména v době obrovských IDE i „nekonečně“ tranzitivně závislých knihoven v běžných moderních aplikacích.
Poznámka: pod pojmem „retrohra“ se může skrývat buď hra vyvinutá skutečně pro dobové konzole (Atari 2600, Atari 5200, NES, SNES, Sega Master System, GameBoy atd.) popř. pro osmibitové domácí mikropočítače (), nebo naopak hra běžící na moderním hardware a využívající všechny poslední výkřiky techniky z oblasti CPU+GPU (a typicky programovaná ve vyšším programovacím jazyku), která pouze simuluje starší vzhled grafiky, popř. zvuků. V tomto článku i v článcích navazujících nebudeme klamat ani hráče ani sami sebe a použijeme buď reálnou konzoli nebo alespoň její emulátor.

2. Slavná osmibitová herní konzole NES

V úvodní části článku se budeme zabývat popisem osmibitové herní konzole nazvané Nintendo Entertainment System, zkráceně NES, která byla navržena a následně vyráběna společností Nintendo Corporation. Tato firma se již před uvedením NESu na trh zabývala konstrukcí video automatů a taktéž jednoúčelových herních konzolí s jedním či dvěma LCD (v našich zemích jsou známější podobné konzole vyráběné v SSSR a mnohdy upravované pro místní podmínky – odlišné postavičky atd.). Stejně jako herní konzole firmy Sega, i konzole NES pochází z Japonska, kde se prodávala pod názvem Family Computer neboli Famicom (pod stejným názvem se ostatně tato konzole prodávala i v některých dalších asijských zemích).

Obrázek 1: Osmibitová herní konzole Nintendo Entertainment System (NES).

Jedná se o nejúspěšnější osmibitovou herní konzoli vůbec, a to jak z pohledu celkového počtu prodaných kusů, tak i při pohledu na časové období, kdy byla tato konzole vyráběna a prodávána, protože se odhaduje, že bylo prodáno téměř 62 milionů kusů tohoto zařízení a navíc vzniklo přibližně padesát více či méně kompatibilních klonů NESu. A navíc byly pro tuto herní konzoli vyvinuty herní série, které vlastně pokračují dodnes (například …). Mimochodem je 62 milionů v dobovém srovnání velmi vysoké číslo, a to například i v porovnání s další (již popsanou) úspěšnou herní konzolí Atari 2600, jíž bylo prodáno celkem 30 milionů (do obou čísel nejsou započítány klony konzole, které většinou nebyly korektně licencovány).

Obrázek 2: K herní konzoli NES postupně vzniklo velké množství různých přídavných zařízení a doplňků, například rozhraní pro MIDI či disketová jednotka používající speciální diskety o průměru dva palce. Zajímavý byl taktéž modem prodávaný zejména v Japonsku, který měl být používán především pro hraní loterií.

Zajímavá je i „morální životnost“ NESu, protože tato konzole byla představena již v roce 1983 v Japonsku a i když byl oficiálně prodej NESu ukončen „již“ v roce 1995 (tj. po dvanácti letech), ve skutečnosti výroba pokračovala až do roku 2003 a ještě do roku 2007 nabízela firma Nintendo možnost oprav NESu (posléze byla tato nabídka stažena, protože již nebyly k dispozici příslušné náhradní díly). Právě na příkladu NESu a taktéž herní konzole Atari 2600 je možné ukázat, že se vývoj herních konzolí poměrně podstatným způsobem odlišuje od vývoje osobních počítačů a je v ostrém kontrastu například s překotným „morálním zastaráváním“ mobilních telefonů (kde je rozdíl asi nejvíce patrný).

Poznámka: prodej konzole s osmibitovým mikroprocesorem a pouhými dvěma kilobajty operační paměti RAM v roce 2003 se totiž může zdát z technologického hlediska vlastně docela nesmyslný, z ekonomického i herního hlediska tomu však může být úplně jinak.

Obrázek 3: Rozhraní pro MIDI určené pro NES.

Není tedy divu, že i přes některé problematické rysy NESu (ochranné prvky a zpočátku i striktní politika prodeje her třetích stran) se dosti úspěšně rozvinula tvorba „moderních retroher“ určených právě pro tuto herní konzoli.

Poznámka: samotné Nintendo dnes nabízí zmenšenou podobu NESu s třiceti klasickými hrami uvnitř (včetně Zeldy, her s Mariem, slavným Metroidem atd.).

3. Architektura herní konzole NES

Ještě předtím, než vykreslíme první snímek hry nebo zahrajeme první tón, je nutné alespoň do určité míry porozumět interní architektuře herní konzole NES. Interně se sice jedná o poměrně jednoduché zařízení s minimálním počtem čipů (což je s ohledem na tržní segment logické), ovšem zejména grafický subsystém může zpočátku vypadat poměrně komplikovaně, protože se v NESu nepoužívá přímočará bitmapová grafika, na jakou jsme zvyklí dnes například při použití knihoven SDL či SDL2. Samozřejmě je výsledkem činnosti grafického subsystému rastrový obrázek vykreslený na televizoru (či dnes spíše na monitoru připojeného k počítači s emulátorem NESu), ovšem tento rastrový obrázek vzniká interpretací (či zpracováním) několika tabulek, či možná lépe řečeno polí. Nicméně grafický subsystém NESu byl ve skutečnosti navržen velmi pečlivě a s ohledem na to, že se jedná o herní konzoli a nikoli o obecný domácí mikropočítač, který je sice univerzální, ovšem tato univerzalita znamená, že se prakticky v každém ohledu musely udělat ústupky (což je ovšem téma na samostatný článek).

Obrázek 4: Zjednodušené schéma architektury herní konzole NES.

4. Základní technické parametry herní konzole NES

Konstruktéři herní konzole NES stáli před podobným problémem, jako konstruktéři všech dalších osmibitových konzolí: jakým způsobem dosáhnout uspokojivého výpočetního výkonu, dobrých grafických i zvukových schopností, a to za co nejnižší výrobní cenu, která je samozřejmě ve velmi konkurenčním prostředí herních konzolí jedním z nejdůležitějších parametrů. Na jednu stranu bylo snahou ušetřit co nejvíce systémových prostředků, na stranu druhou měla být NES minimálně o třídu lepší (z pohledu grafiky i zvuků) než konkurenční herní konzole, tedy především dosluhující Atari 2600, ale především konzole z třetí generace Sega Master System, Atari 7800 a SG-1000.

Výsledkem výše zmíněné snahy o dobré vybalancování výrobní ceny a schopností konzole byl systém se čtyřmi (pouhými čtyřmi!) hlavními čipy: osmibitovým mikroprocesorem Ricoh 2A03 (de facto upravený MOS 6502) obsahujícím i zvukový generátor, dále čipem RP2C02, resp. RP2C07 (PPU) zajišťujícím generování grafického obrazu (nazývá se PPU) a dvojicí paměťových čipů. Jeden z těchto paměťových čipů přitom tvořil programovou a datovou RAM, druhý čip sloužil grafickému procesoru pro uložení vzorků, z nichž se skládalo herní pole (playfield, ovšem nenechte se zmást i termínem background) a sprity, popř. i další důležité informace nutné pro vygenerování grafického obrazu.

Poznámka: povšimněte si, že na rozdíl o domácích osmibitových mikropočítačů nebyla zařazena například ROM s operačním systémem (vše si řešil paměťový modul se hrou), ani takové „maličkosti“ jako znaková sada apod. Na jednu stranu jsou tedy začátky programování NESu jednodušší, neboť mezi programátorem a hardwarem není umístěna žádná mezivrstva, ovšem minimálně grafický subsystém tvoří určitou vstupní bariéru – a tím pádem i výzvu!

Obrázek 5: Herní série The Legend of Zelda

Systém Nintendo Entertainment System samozřejmě podporoval (přesněji řečeno přímo vyžadoval) použití zásuvných modulů s hrami či jinými aplikacemi. Tyto moduly mohly obsahovat jak paměť ROM/EPROM s binárním kódem hry i se všemi statickými daty (maximální adresovatelná kapacita dosahovala 32 kB, zvýšit ji bylo možné klasicky přes paměťové banky), tak i paměť RAM, jejíž typická kapacita byla 8 kB. Pomocí zásuvného modulu bylo možné rozšířit i kapacitu video paměti. Schéma zapojení jedné z variant herní konzole Nintendo Entertainment System je dostupné například na této adrese: http://nesdev.parodius.com/Ntd_8bit­.jpg. Povšimněte si především způsobu vzájemného propojení mikroprocesoru (CPU) s grafickým procesorem (PPU) pomocí datové sběrnice a šesti bitů adresové sběrnice i toho, jak jsou od sebe odděleny oba čipy RAM (ve druhém případě je adresa zachycena do latche LS 373 s využitím signálu ALE).

Poznámka: toto rozdělení RAM nás bude při programování poněkud mást.

5. Osmibitový mikroprocesor Ricoh 2A03

Ústředním čipem, na němž je postavena herní konzole NES, je mikroprocesor Ricoh 2A03, popř. Ricoh 2A07 (tyto čipy se od sebe liší především odlišným časováním a jsou určeny pro různé televizní normy). Konstruktéři v případě mikroprocesoru 2A03/2A07 vsadili na osvědčenou jistotu, protože tento čip je založen na jádru oblíbeného mikroprocesoru MOS 6502, jenž byl v různých variantách použit v několika dalších osmibitových herních konzolích (Atari 2600, Atari 5200, Atari 7800) a taktéž v mnoha domácích osmibitových mikropočítačích (Atari 400/800/800XL/130XE a varianty, Apple I, Apple II, BBC Micro, Commodore C64…) – je to jeden ze tří nejúspěšnějších dobových mikroprocesorů. V případě čipů 2A03/2A07 však došlo k určitému zjednodušení jádra původního MOS 6502; chyběl například nepříliš často používaný režim pro práci s BCD čísly, kdy se v jednom bajtu ukládaly dvě číslice 0–9, tj. rozsah ukládaných hodnot byl 00 až 99 a nikoli 0 a 255. Původní mikroprocesor MOS 6502 mohl být do tohoto režimu přepnut pomocí instrukce SED, bližší informace o tomto režimu lze najít na adrese http://www.6502.org/tutori­als/decimal_mode.html.

Poznámka: vynechání režimu BCD je poněkud zvláštní, protože se hodí například pro zobrazení skóre či počtu životů ve hře. Možná i z tohoto důvodu mnoho her pro NES zobrazuje počet životů ikonami (ovšem skóre a statistiku zobrazuje například Tetris).

Obrázek 6: Mikroprocesor Ricoh 2A03.

6. Další podpůrné moduly integrované do čipu 2A03/2A07

Zatímco jádro mikroprocesoru bylo nepatrně zjednodušeno, byly na čip Ricoh 2A03/2A07 přidány další velmi důležité podpůrné moduly, díky jejichž existenci mohlo být redukováno celkové množství integrovaných obvodů, z nichž se herní konzole NES skládala (což významně snížilo cenu i zmenšilo množství reklamací). Jednalo se o následující moduly, z nichž některé budou popsány v navazujících kapitolách:

  • Programovatelný „pomalý“ časovač se základní frekvencí 240 Hz, z níž mohly být odvozeny další frekvence (48, 60, 96, 120 a 192 Hz).
  • Modul pro přímý přístup do paměti a přenosy dat (DMA).
  • Dva programovatelné generátory obdélníkového signálu používané pro tvorbu zvuků.
  • Jeden generátor trojúhelníkového signálu, taktéž používaný pro tvorbu zvuků.
  • Konfigurovatelný generátor šumu tvořený posuvným registrem se zpětnou vazbou.
  • Modul pro přehrávání samplů (D/A převodník).

Jen pro zajímavost se podívejme na zapojení vývodů:

              ___  ___
             |o  \/   |
ROUT  <--- 01]        [40 <---  VCC
COUT  <--- 02]        [39 --->  $4016W.0
/RES  ---> 03]        [38 --->  $4016W.1
A0    <--- 04]        [37 --->  $4016W.2
A1    <--- 05]        [36 --->  /$4016R
A2    <--- 06]        [35 --->  /$4017R
A3    <--- 07]        [34 --->  R/W
A4    <--- 08]        [33 <---  /NMI
A5    <--- 09]        [32 <---  /IRQ
A6    <--- 10]  2A03  [31 --->  PHI2
A7    <--- 11]        [30 <---  ---
A8    <--- 12]        [29 <---  CLK
A9    <--- 13]        [28 <--->  D0
A10   <--- 14]        [27 <--->  D1
A11   <--- 15]        [26 <--->  D2
A12   <--- 16]        [25 <--->  D3
A13   <--- 17]        [24 <--->  D4
A14   <--- 18]        [23 <--->  D5
A15   <--- 19]        [22 <--->  D6
VEE   ---> 20]        [21 <--->  D7
             |________|

Obrázek 7: Schéma pinů mikroprocesoru Ricoh 2A03.

7. Zvukový subsystém herní konzole NES

V této kapitole si řekneme základní informace o zvukovém subsystému herní konzole NES. Z předchozího textu již víme, že je tento subsystém přímo součástí integrovaného obvodu s mikroprocesorem 2A03/2A07. Zvuk či melodie mohly být tvořeny v pěti na sobě nezávislých kanálech (což je s ohledem na dobu vzniku hodně). Jedná se o dva generátory obdélníkového signálu s volitelnou amplitudou, frekvencí a střídou, dále o generátor trojúhelníkového signálu s volitelnou frekvencí a automaticky měněnou amplitudou, generátor šumu, u nějž bylo možné zvolit režim činnosti, amplitudu a frekvenci posunu a konečně o D/A převodník, který mohl být buď ovládán přímo programově, nebo bylo umožněno načítat zvukové vzorky (samply) uložené v paměti ROM či RAM. Zajímavé je, že zvukový subsystém neobsahoval žádné filtry, na rozdíl od (jednoduchých) filtrů nabízených čipem POKEY (Atari) či komplexnějších filtrů použitých v čipu SID (Commodore C64).

Obrázek 8: Super Mario Bros ve verzi pro NES.

Taktéž podpora pro tvarování obálky generovaných signálů byla pouze dosti jednoduchá (tuto funkcionalitu však neměl ani POKEY, kde se musela řešit programovými cestami). Čip 2A03/2A07 obsahoval dva piny, na které byl přiváděn výstup ze všech pěti zvukových kanálů. Na pin číslo 1 (ROUT) byl přiváděn výstup z obou generátorů obdélníkových signálů, zatímco výstup z ostatních třech kanálů byl přiváděn na pin číslo 2 (COUT). V herní konzoli NES byl výstup z obou kanálů sloučen a analogově sečten s mikrofonním vstupem (ten se příliš často nepoužíval) a výsledný analogový signál byl zesílen a dále zpracován.

Obrázek 9: Další screenshot hry Super Mario Bros.

8. Generátory obdélníkových signálů a trojúhelníkového signálu

Na ovládání nejjednodušší byly oba generátory obdélníkových signálů, u nichž bylo možné měnit zejména tři základní parametry: amplitudu (4 bity = 16 úrovní), frekvenci (přibližně 54,6 Hz až 12,4 kHz podle použité televizní normy, frekvenci bylo možné měnit plynule) a střídu (1:8, 1:4, 1:2, 3:4). U generátoru trojúhelníkového signálu se měnila jeho úroveň (4 bity = 16 úrovní) automaticky, ale programově bylo možné nastavit frekvenci změn signálu v rozsahu 27,3 Hz až 55,9 kHz (opět v závislosti na použité televizní normě). Generátor šumu si popíšeme v následující kapitole, takže nám zbývá už jen pátý kanál, jímž je D/A převodník. Ten bylo možné ovládat programově změnou jednoho řídicího registru (sedmibitová hodnota), popř. byla podporována funkce automatického přehrávání vzorků (samplů) z ROM, přičemž při načítání samplu došlo k pozastavení mikroprocesoru. Při přehrávání vzorků byla frekvence nastavitelná v rozsahu 4,2 kHz až 33,5 kHz (ovšem zvolit bylo možné jen šestnáct přednastavených frekvencí).

42

Obrázek 10: Poslední screenshot ze hry The Legend of Zelda ve verzi pro NES.

9. Generátor šumu

Podobně jako zvukové čipy POKEY či AY-3–8910, obsahoval i zvukový subsystém herní konzole NES generátor šumu. Ten byl vytvořen pomocí patnáctibitového posuvného registru se zpětnou vazbou: vybrané dva bity posuvného registru byly přes hradlo XOR přesunuty na jeho začátek, přičemž se obsah registru mezitím posunul o jeden bit doleva. Zvukový výstup byl generován na základě aktuálního obsahu bitu s indexem 14. Posuvný registr mohl pracovat ve dvou režimech činnosti: „krátkém“ a „dlouhém“. Ve dlouhém režimu se generovala sekvence pseudonáhodných bitů s periodou 32767 vzorků (zbývající 32768 stav nemohl být použit, protože se jednalo o nulovou hodnotu spadající mimo generovanou sekvenci). V krátkém režimu byla perioda pseudonáhodného signálu kratší: 93 bitů/vzorků. Samozřejmě bylo taktéž možné měnit frekvenci posunu, a to přibližně v rozsahu 29,3 Hz až 447 kHz (opět v závislosti na tom, jaká televizní norma byla použita).

                 <=======================

+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
| 14 | 13 | 12 | 11 | 10 |  9 |  8 |  7 |  6 |  5 |  4 |  3 |  2 |  1 |  0 |
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
   v    v        _____                                                   ^
   v    +-------| xor |--------------------------------------------------+
   +------------|_____|

Obrázek 11: Konfigurace generátoru šumu v „dlouhém“ režimu.

                 <=======================

+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
| 14 | 13 | 12 | 11 | 10 |  9 |  8 |  7 |  6 |  5 |  4 |  3 |  2 |  1 |  0 |
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
   v                             v        _____                          ^
   v                             +-------| xor |-------------------------+
   +-------------------------------------|_____|

Obrázek 12: Konfigurace generátoru šumu v „krátkém“ režimu.

10. Adresy všech řídicích registrů zvukového subsystému

Na závěr si ještě uvedeme mapu všech 22 řídicích registrů ovlivňujících funkci všech zvukových generátorů popsaných výše:

Adresa Název řídicího registru Význam
$4000 SQ1_VOL Duty and volume for square wave 1
$4001 SQ1_SWEEP Sweep control register for square wave 1
$4002 SQ1_LO Low byte of period for square wave 1
$4003 SQ1_HI High byte of period and length counter value for square wave 1
$4004 SQ2_VOL Duty and volume for square wave 2
$4005 SQ2_SWEEP Sweep control register for square wave 2
$4006 SQ2_LO Low byte of period for square wave 2
$4007 SQ2_HI High byte of period and length counter value for square wave 2
$4008 TRI_LINEAR Triangle wave linear counter
$4009   Not used
$400A TRI_LO Low byte of period for triangle wave
$400B TRI_HI High byte of period and length counter value for triangle wave
$400C NOISE_VOL Volume for noise generator
$400D   Not used
$400E NOISE_LO Period and waveform shape for noise generator
$400F NOISE_HI Length counter value for noise generator
$4010 DMC_FREQ Play mode and frequency for DMC samples
$4011 DMC_RAW 7-bit DAC
$4012 DMC_START Start of DMC waveform is at address $C000 + $40*$xx
$4013 DMC_LEN Length of DMC waveform is $10*$xx + 1 bytes (128*$xx + 8 samples)
$4014 OAM_DMA Writing $xx copies 256 bytes by reading from $xx00-$xxFF and writing to $2004 (OAM data)
$4015 SND_CHN Sound channels enable and status
Poznámka: generování zvuků si ukážeme příště.

Obrázek 13: Jeden z trackerů pro herní konzoli NES: Nerd Tracker.

11. Grafický subsystém NESu

Grafický subsystém použitý v herní konzoli Nintendo Entertainment System byl v mnoha ohledech ještě zajímavější než její zvukový subsystém. Víme již, že herní konzole NES existovala ve variantě pro televizní normu NTSC i pro normu PAL. Konzole určené pro televizní normu PAL obsahovaly mikroprocesor 2A07 s hodinovou frekvencí 1,66 MHz, zatímco pro normu NTSC byly určeny konzole s mikroprocesorem 2A03 používající hodinovou frekvenci 1,79 MHz. I grafické čipy se lišily podle toho, pro jakou televizní normu byly určeny. Pro normu NTSC se používal čip RP2C02 se vstupní hodinovou frekvencí 5,37 MHz, zatímco pro normu PAL byl použit čip RP2C07 s frekvencí 5,32 MHz. Tyto čipy, označované taktéž zkratkou PPU, obsahovaly 256 interní paměti využívané systémem pro zobrazení spritů. Kromě toho přistupoval PPU k samostatnému čipu RAM o kapacitě dva kilobajty. V této paměti bylo uloženo větší množství datových struktur nesoucích informace o pozadí scény, o barvové paletě i o tvarech spritů. Ve skutečnosti však mohl PPU přistupovat i k paměti ROM umístěné na paměťovém modulu se hrou.

Poznámka: podrobnosti o grafickém subsystému, které je nezbytně nutné znát pro tvorbu her, si popíšeme příště.

Obrázek 14: Barvová paleta používaná herní konzolí NES.

12. Assemblery a překladače pro NES

Vzhledem k tomu, že se mikroprocesor herní konzole NES lišil od původního čipu MOS 6502 jen v několika málo instrukcích, je možné pro vývoj her pro NES používat assembler nazvaný DASM, o němž jsme se zmínili v článcích o Batari BASICu. Tento assembler (přesněji řečeno cross assembler) je v současnosti pravděpodobně nejpoužívanější, a to i v oblasti dalších zařízení založených na procesorech řady MOS 6502. Existují či v nedávné minulosti existovaly však i další assemblery, například starší FASM, v minulosti poměrně populární X816 či P65, který je zajímavý především tím, že je naprogramován v Perlu a nikoli v jazyku C, podobně jako mnoho dalších assemblerů. Musím též doplnit informaci o disassemblerech: 6502 Simulator, TRaCER či NESrev – ten je psaný v Javě (v souvislosti s osmibitovými konzolemi jde o poměrně bizarní technologii :-) a jeho autorem je Kent Hansen.

Obrázek 15: Arkanoid pro herní konzoli NES – třetí obtížnost.

Ovšem díky poměrně velké kapacitě paměti ROM umístěné na cartridgích s hrami a taktéž díky tomu, že při programování grafického subsystému NESu není nutné provádět optimalizace na úrovni jednotlivých strojových cyklů (viz Atari 2600) se dnes mnoho vývojářů uchyluje k použití programovacího jazyka C. V této oblasti se velmi často používá cross compiler CC65 doplněný o hlavičkové soubory obsahující jména a adresy řídicích registrů NESu atd.

Obrázek 16: První místnost ve hře Flappy (verze pro NES).

13. Komplexní vývojová prostředí

Pro vývoj lze alternativně použít i nbasic (doplněný o nesasm) což je určitý hybrid mezi jazykem BASIC a assemblerem. Dialekt BASICu, který je zde použitý, například nepodporuje dynamickou alokaci paměti, proměnné jsou pouze lokální, pole se umisťují na zadanou absolutní adresu a je podporován inline assembler. Díky těmto omezením je převod programu z BASICu do assembleru MOS 6502 poměrně přímočarý, takže programátor může mít velmi dobrou představu, jakým způsobem se jednotlivé jazykové konstrukce přeloží a využijí. Mimochodem: nbasic a nesasm byl v roce 2004 dokonce využit i v univerzitním kurzu pro výuku programování počítačových her s využitím emulátoru NESu. Bližší informace a především informacemi (i vtipem) nabité slajdy najdete na adrese http://bobrost.com/nes/.

Obrázek 17: Scrollující prostředí hry Final Fantasy.

14. Základní programové vybavení pro vývoj her pro NES

Pro herní konzoli NES dokonce vzniklo i moderně pojaté integrované vývojové prostředí nazvané NESICIDE. Autory tohoto vývojového prostředí jsou Christopher Pow, Tim Sarbin a Benjamin Eriksson. V tomto IDE nalezneme některé nástroje známé i z jiných současných integrovaných vývojových prostředí, například správce projektů, programátorský editor apod. Ovšem navíc jsou přidány nástroje, které úzce souvisejí s možnostmi herní konzole NES. Příkladem může být integrovaný debugger, disassembler (+code browser) a emulátorNESu, jenž je možné spustit přímo v okně IDE. Vzhledem k poměrně komplikované struktuře grafické paměti popsané v navazujícím článku je integrované vývojové prostředí NESICIDE vybaveno i editorem a prohlížečem barvové palety NESu a taktéž možností „vizualizace“ obrazové paměti – je možné pracovat jak s uživatelsky definovanými znaky (součást pozadí), tak i se sprity. Díky tomuto nástroji je možné NESICIDE relativně snadno použít i pro modifikaci existujících her.

Obrázek 18: Screenshot vývojového prostředí NESICIDE.

15. Instalace ca65 a cc65

Instalace assembleru ca65 i céčkového překladače cc65 je na většině distribucí Linuxu snadná, neboť se jedná o balíčky umístěné přímo v repositářích dané distribuce. Příkladem může být Linux Mint:

$ sudo apt-get install cc65
 
Reading package lists... Done
Building dependency tree
Reading state information... Done
Suggested packages:
  cc65-doc
The following NEW packages will be installed:
  cc65
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 2 162 kB of archives.
After this operation, 31,8 MB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu focal/universe amd64 cc65 amd64 2.18-1 [2 162 kB]
Fetched 2 162 kB in 5s (423 kB/s)
Selecting previously unselected package cc65.
(Reading database ... 291820 files and directories currently installed.)
Preparing to unpack .../archives/cc65_2.18-1_amd64.deb ...
Unpacking cc65 (2.18-1) ...
Setting up cc65 (2.18-1) ...

Po dokončení instalace budou k dispozici tři nové nástroje (a několik podpůrných nástrojů).

V první řadě se jedná o assembler:

$ cc65 --version
cc65 V2.18 - Ubuntu 2.18-1

Dále o překladač céčka:

$ ca65 --version
ca65 V2.18 - Ubuntu 2.18-1

A využijeme i samostatný linker:

$ ld65 --version
ld65 V2.18 - Ubuntu 2.18-1
Poznámka: nainstalován je ale například i disassembler atd.

16. Instalace emulátoru NESu

V současnosti existuje několik (řádově desítka) podporovaných a stále vyvíjených emulátorů konzole NES. Prozatím se spokojíme s jedním z nejjednodušších emulátorů, kterým je Nestopia. Opět se jedná o nástroj typicky dostupný ve standardních repositářích Linuxových distribucí:

$ sudo apt-get install nestopia
 
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
  nestopia
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 885 kB of archives.
After this operation, 3 933 kB of additional disk space will be used.
0% [Working]y
Get:1 http://archive.ubuntu.com/ubuntu focal/universe amd64 nestopia amd64 1.50-1build1 [885 kB]
Fetched 885 kB in 7s (119 kB/s)
Selecting previously unselected package nestopia.
(Reading database ... 292411 files and directories currently installed.)
Preparing to unpack .../nestopia_1.50-1build1_amd64.deb ...
Unpacking nestopia (1.50-1build1) ...
Setting up nestopia (1.50-1build1) ...
update-alternatives: using /usr/games/nestopia to provide /usr/bin/nes (nes) in auto mode
Processing triggers for desktop-file-utils (0.24+linuxmint1) ...
Processing triggers for mime-support (3.64ubuntu1) ...
Processing triggers for hicolor-icon-theme (0.17-2) ...
Processing triggers for doc-base (0.10.9) ...
Processing 1 added doc-base file...
Processing triggers for gnome-menus (3.36.0-1ubuntu1) ...
Processing triggers for man-db (2.9.1-1) ...

17. Kostra programu pro NES vytvořená v céčku

Nyní, když již máme nainstalován jak assembler, tak i překladač programovacího jazyka C pro mikroprocesor MOS 6502 (a tím pádem i pro 2A03/2A07), si můžeme ukázat, jak by mohla vypadat kostra (základ) hry naprogramované pro NES. Prozatím si bez dalšího podrobnějšího popisu ukažme příklad, který byl vytvořen Dougem Frakerem a je dostupný na adrese https://github.com/nesdou­g/01_Hello/blob/master/he­llo.c. Jedná se o „čisté céčko“, ovšem celá funkcionalita je postavena na již předpřipravených knihovnách, z×nichž některé části jsou naprogramovány v assembleru (jedinou výjimkou je klauzule #pragma):

/*      simple Hello World, for cc65, for NES
 *  writing to the screen with rendering disabled
 *      using neslib
 *      Doug Fraker 2018
 */     
 
 
  
#include "LIB/neslib.h"
#include "LIB/nesdoug.h"
 
#define BLACK 0x0f
#define DK_GY 0x00
#define LT_GY 0x10
#define WHITE 0x30
// there's some oddities in the palette code, black must be 0x0f, white must be 0x30
 
 
 
#pragma bss-name(push, "ZEROPAGE")
 
// GLOBAL VARIABLES
// all variables should be global for speed
// zeropage global is even faster
 
unsigned char i;
 
 
 
const unsigned char text[]="Hello World!"; // zero terminated c string
 
const unsigned char palette[]={
BLACK, DK_GY, LT_GY, WHITE,
0,0,0,0,
0,0,0,0,
0,0,0,0
};
 
 
 
 
 
void main (void) {
 
        ppu_off(); // screen off
 
        pal_bg(palette); //load the BG palette
 
        // set a starting point on the screen
        // vram_adr(NTADR_A(x,y));
        vram_adr(NTADR_A(10,14)); // screen is 32 x 30 tiles
 
        ppu_on_all(); // turn on screen
 
 
        while (1){
                // infinite loop
                // game code can go here later.

        }
}
Poznámka: příště si ukážeme, jakým způsobem se vlastně tento kód přeloží a jak (či zda vůbec) se bude lišit od programu napsaného v assembleru.

18. Kostra programu pro NES vytvořená v assembleru

V kontextu tohoto miniseriálu bude zajímavější kostra programu vytvořená přímo v assembleru mikroprocesoru MOS 6502. Následující program byl získán úpravou kódu popsaného na stránce https://www.pagetable.com/?p=410. Prozatím si uvedeme jeho prakticky nejjednodušší možnou podobu, ovšem v dalším článku budou uvedeny všechny možné úpravy a pochopitelně i rozšíření. K samotnému zdrojovému kódu je dodáván i soubor link.cfg (jméno může být libovolné a použijeme ho při volání linkeru ld65), který obsahuje informaci o struktuře paměti NESu i názvy jednotlivých segmentů. Právě tyto názvy následně použijeme ve zdrojovém kódu assembleru, abychom nemuseli všude psát hexadecimální adresy:

MEMORY {
    ZP:     start = $0000, size = $0100, type = rw;
    RAM:    start = $0300, size = $0400, type = rw;
    HEADER: start = $0000, size = $0010, type = rw,
            file = %O, fill = yes;
    PRG0:   start = $8000, size = $4000, type = ro,
            file = %O, fill = yes;
    CHR0a:  start = $0000, size = $1000, type = ro,
            file = %O, fill = yes;
    CHR0b:  start = $1000, size = $1000, type = ro,
            file = %O, fill = yes;
}
 
SEGMENTS {
    ZEROPAGE: load = ZP, type = zp;
    BSS:    load = RAM, type = bss;
    INES:   load = HEADER, type = ro, align = $10;
    CODE:   load = PRG0, type = ro;
    VECTOR: load = PRG0, type = ro, start = $BFFA;
    CHR0a:  load = CHR0a, type = ro;
    CHR0b:  load = CHR0b, type = ro;
}
Poznámka: podrobnější popis jednotlivých segmentů bude opět uveden příště. Důležitý je zejména segment INES, což je vlastně pseudosegment popisující strukturu vygenerovaného souboru pro NES (nejedná se totiž o jednoduchý „otisk paměti“ 1:1, ale o soubor s interní strukturou a hlavičkou definovanou právě v INES).
; ---------------------------------------------------------------------
; Definice hlavičky obrazu ROM
; ---------------------------------------------------------------------
 
; Size of PRG in units of 16 KiB.
prg_npage = 1
 
; Size of CHR in units of 8 KiB.
chr_npage = 1
 
; INES mapper number.
mapper = 0
 
; Mirroring (0 = horizontal, 1 = vertical)
mirroring = 1
 
.segment "INES"
        .byte $4e, $45, $53, $1a
        .byte prg_npage
        .byte chr_npage
        .byte ((mapper & $0f) << 4) | (mirroring & 1)
        .byte mapper & $f0
.code
 
 
 
; ---------------------------------------------------------------------
; Blok paměti s definicí dlaždic 8x8 pixelů
; ---------------------------------------------------------------------
 
.segment "CHR0a"
.segment "CHR0b"
 
 
 
; ---------------------------------------------------------------------
; 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
        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
        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)
 
        ; nastavení řídicích registrů
        ldx #$00
        stx $2000               ; nastavení PPUCTRL = 0
        stx $2001               ; nastavení PPUMASK = 0
        stx $4015               ; nastavení APUSTATUS = 0
 
        ; čekání na vnitřní inicializaci PPU (dva snímky)
wait1:  bit $2002               ; test obsahu registru PPUSTATUS 
        bpl wait1               ; skok, pokud je příznak N nulový
wait2:  bit $2002               ; test obsahu registru PPUSTATUS 
        bpl wait2               ; skok, pokud je příznak N nulový
 
        ; vymazání obsahu RAM
        lda #$00                ; vynulování registru A
loop:   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 loop                ; po přetečení 0xff -> 0x00 konec smyčky
 
        ; čekání na dokončení dalšího snímku, potom může začít herní smyčka
wait3:  bit $2002               ; test obsahu registru PPUSTATUS 
        bpl wait3               ; skok, pokud je příznak N nulový

        ; vlastní herní smyčka je prozatím prázdná
game_loop:
        jmp game_loop           ; nekonečná smyčka (později rozšíříme)
.endproc
 
 
 
; ---------------------------------------------------------------------
; Tabulka vektorů CPU
; ---------------------------------------------------------------------
 
.segment "VECTOR"
.addr nmi
.addr reset
.addr irq
 
 
 
; ---------------------------------------------------------------------
; Finito
; ---------------------------------------------------------------------
Poznámka: nenechte se vystrašit tím, jak je kód dlouhý. Je to způsobeno tím, že mezi programem a reálným hardwarem není žádná další mezivrstva typu ROM s BIOSem a už vůbec ne operační systém. Všechny operace, včetně inicializace hardware, tedy musíme provádět explicitně.

Překlad z assembleru do objektového kódu se provede následovně:

$ ca65 example01.asm -o example01.o -l example01.list
Poznámka: přepínač -l example01.list ve skutečnosti není nutný, ovšem umožní nám podívat se na to, jak je vlastně náš kód pochopen assemblerem (makra atd.).

Objektový kód se přeloží do „obrazu“ pro NES příkazem, v němž definujeme cestu k výše zmíněnému konfiguračnímu souboru linkeru:

$ ld65 -C link.cfg example01.o -o example01.nes

Výsledný soubor je již možné otevřít v emulátoru, který by ho měl načíst bez nahlášení chyby (ovšem obrazovka zůstane černá).

skolení ELK

Ještě se pro zajímavost podívejme na soubor vygenerovaný assemblerem nazvaný example01.list. Jedná se o kontrolní výpis/opis zpracovávaného assemblerovského zdrojového kódu:

ca65 V2.18 - Ubuntu 2.18-1
Main file   : example01.asm
Current file: example01.asm
 
000000r 1               ; ---------------------------------------------------------------------
000000r 1               ; Definice hlavičky obrazu ROM
000000r 1               ; ---------------------------------------------------------------------
000000r 1
000000r 1               ; Size of PRG in units of 16 KiB.
000000r 1               prg_npage = 1
000000r 1
000000r 1               ; Size of CHR in units of 8 KiB.
000000r 1               chr_npage = 1
000000r 1
000000r 1               ; INES mapper number.
000000r 1               mapper = 0
000000r 1
000000r 1               ; Mirroring (0 = horizontal, 1 = vertical)
000000r 1               mirroring = 1
000000r 1
000000r 1               .segment "INES"
000000r 1  4E 45 53 1A          .byte $4e, $45, $53, $1a
000004r 1  01                   .byte prg_npage
000005r 1  01                   .byte chr_npage
000006r 1  01                   .byte ((mapper & $0f) << 4) | (mirroring & 1)
000007r 1  00                   .byte mapper & $f0
000008r 1               .code
000000r 1
000000r 1
000000r 1
000000r 1               ; ---------------------------------------------------------------------
000000r 1               ; Blok paměti s definicí dlaždic 8x8 pixelů
000000r 1               ; ---------------------------------------------------------------------
000000r 1
000000r 1               .segment "CHR0a"
000000r 1               .segment "CHR0b"
000000r 1
000000r 1
000000r 1
000000r 1               ; ---------------------------------------------------------------------
000000r 1               ; Programový kód rutin pro NMI, RESET a IRQ volaných automaticky CPU
000000r 1               ;
000000r 1               ; viz též https://www.pagetable.com/?p=410
000000r 1               ; ---------------------------------------------------------------------
000000r 1
000000r 1               ; Obslužná rutina pro NMI (nemaskovatelné přerušení, vertical blank)
000000r 1
000000r 1               .proc nmi
000000r 1  40                   rti                     ; návrat z přerušení
000001r 1               .endproc
000001r 1
000001r 1
000001r 1
000001r 1               ; Obslužná rutina pro IRQ (maskovatelné přerušení)
000001r 1
000001r 1               .proc irq
000001r 1  40                   rti                     ; návrat z přerušení
000002r 1               .endproc
000002r 1
000002r 1
000002r 1
000002r 1               ; Obslužná rutina pro RESET
000002r 1
000002r 1               .proc reset
000002r 1                       ; nastavení stavu CPU
000002r 1  78                   sei                     ; zákaz přerušení
000003r 1  D8                   cld                     ; vypnutí dekadického režimu (není podporován)
000004r 1
000004r 1  A2 FF                ldx #$ff
000006r 1  9A                   txs                     ; vrchol zásobníku nastaven na 0xff (první stránka)
000007r 1
000007r 1                       ; nastavení řídicích registrů
000007r 1  A2 00                ldx #$00
000009r 1  8E 00 20             stx $2000               ; nastavení PPUCTRL = 0
00000Cr 1  8E 01 20             stx $2001               ; nastavení PPUMASK = 0
00000Fr 1  8E 15 40             stx $4015               ; nastavení APUSTATUS = 0
000012r 1
000012r 1                       ; čekání na vnitřní inicializaci PPU (dva snímky)
000012r 1  2C 02 20     wait1:  bit $2002               ; test obsahu registru PPUSTATUS
000015r 1  10 FB                bpl wait1               ; skok, pokud je příznak N nulový
000017r 1  2C 02 20     wait2:  bit $2002               ; test obsahu registru PPUSTATUS
00001Ar 1  10 FB                bpl wait2               ; skok, pokud je příznak N nulový
00001Cr 1
00001Cr 1                       ; vymazání obsahu RAM
00001Cr 1  A9 00                lda #$00                ; vynulování registru A
00001Er 1  95 00        loop:   sta $000, x             ; vynulování X-tého bajtu v nulté stránce
000020r 1  9D 00 01             sta $100, x
000023r 1  9D 00 02             sta $200, x
000026r 1  9D 00 03             sta $300, x
000029r 1  9D 00 04             sta $400, x
00002Cr 1  9D 00 05             sta $500, x
00002Fr 1  9D 00 06             sta $600, x
000032r 1  9D 00 07             sta $700, x             ; vynulování X-tého bajtu v sedmé stránce
000035r 1  E8                   inx                     ; přechod na další bajt
000036r 1  D0 E6                bne loop                ; po přetečení 0xff -> 0x00 konec smyčky
000038r 1
000038r 1                       ; čekání na dokončení dalšího snímku, potom může začít herní smyčka
000038r 1  2C 02 20     wait3:  bit $2002               ; test obsahu registru PPUSTATUS
00003Br 1  10 FB                bpl wait3               ; skok, pokud je příznak N nulový
00003Dr 1
00003Dr 1                       ; vlastní herní smyčka je prozatím prázdná
00003Dr 1               game_loop:
00003Dr 1  4C rr rr             jmp game_loop           ; nekonečná smyčka (později rozšíříme)
000040r 1               .endproc
000040r 1
000040r 1
000040r 1
000040r 1               ; ---------------------------------------------------------------------
000040r 1               ; Tabulka vektorů CPU
000040r 1               ; ---------------------------------------------------------------------
000040r 1
000040r 1               .segment "VECTOR"
000000r 1  rr rr        .addr nmi
000002r 1  rr rr        .addr reset
000004r 1  rr rr        .addr irq
000006r 1
000006r 1
000006r 1
000006r 1               ; ---------------------------------------------------------------------
000006r 1               ; Finito
000006r 1               ; ---------------------------------------------------------------------
000006r 1

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

Demonstrační příklad napsaný v assembleru, jenž je určený pro překlad pomocí ca65, byl uložen do Git repositáře, který je dostupný na adrese https://github.com/tisnik/8bit-fame. Příklad 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 example01.asm zdrojový kód příkladu https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example01.asm
2 link.cfg konfigurace segmentů pro linker ld65 https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/link.cfg
3 Makefile Makefile pro překlad prvního příkladu https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/Makefile

20. Odkazy na Internetu

  1. NesDev.org
    https://www.nesdev.org/
  2. How to Program an NES game in C
    https://nesdoug.com/
  3. Getting Started Programming in C: Coding a Retro Game with C Part 2
    https://retrogamecoders.com/getting-started-with-c-cc65/
  4. „Game Development in Eight Bits“ by Kevin Zurawel
    https://www.youtube.com/wat­ch?v=TPbroUDHG0s&list=PLcGKfGE­EONaBjSfQaSiU9yQsjPxxDQyV8&in­dex=4
  5. Game Development for the 8-bit NES: A class by Bob Rost
    http://bobrost.com/nes/
  6. Game Development for the 8-bit NES: Lecture Notes
    http://bobrost.com/nes/lectures.php
  7. NES Graphics Explained
    https://www.youtube.com/wat­ch?v=7Co_8dC2zb8
  8. NES GAME PROGRAMMING PART 1
    https://rpgmaker.net/tuto­rials/227/?post=240020
  9. 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/
  10. Minimal NES example using ca65
    https://github.com/bbbradsmith/NES-ca65-example
  11. List of 6502-based Computers and Consoles
    https://www.retrocompute.co.uk/list-of-6502-based-computers-and-consoles/
  12. History of video game consoles (second generation): Wikipedia
    http://en.wikipedia.org/wi­ki/History_of_video_game_con­soles_(second_generation)
  13. 6502 – the first RISC µP
    http://ericclever.com/6500/
  14. 3 Generations of Game Machine Architecture
    http://www.atariarchives.or­g/dev/CGEXPO99.html
  15. bee – The Multi-Console Emulator
    http://www.thebeehive.ws/
  16. Nerdy Nights Mirror
    https://nerdy-nights.nes.science/
  17. NES Development Day 1: Creating a ROM
    https://www.moria.us/blog/2018/03/nes-development
  18. How to Start Making NES Games
    https://www.matthughson.com/2021/11/17/how-to-start-making-nes-games/
  19. ca65 Users Guide
    https://cc65.github.io/doc/ca65.html
  20. cc65 Users Guide
    https://cc65.github.io/doc/cc65.html
  21. ld65 Users Guide
    https://cc65.github.io/doc/ld65.html
  22. da65 Users Guide
    https://cc65.github.io/doc/da65.html
  23. Nocash NES Specs
    http://nocash.emubase.de/everynes.htm
  24. Nintendo Entertainment System
    http://cs.wikipedia.org/wiki/NES
  25. Nintendo Entertainment System Architecture
    http://nesdev.icequake.net/nes.txt
  26. NesDev
    http://nesdev.parodius.com/
  27. 2A03 technical reference
    http://nesdev.parodius.com/2A03%20techni­cal%20reference.txt
  28. NES Dev wiki: 2A03
    http://wiki.nesdev.com/w/in­dex.php/2A03
  29. Ricoh 2A03
    http://en.wikipedia.org/wi­ki/Ricoh_2A03
  30. 2A03 pinouts
    http://nesdev.parodius.com/2A03_pi­nout.txt
  31. 27c3: Reverse Engineering the MOS 6502 CPU (en)
    https://www.youtube.com/wat­ch?v=fWqBmmPQP40
  32. “Hello, world” from scratch on a 6502 — Part 1
    https://www.youtube.com/wat­ch?v=LnzuMJLZRdU
  33. A Tour of 6502 Cross-Assemblers
    https://bumbershootsoft.wor­dpress.com/2016/01/31/a-tour-of-6502-cross-assemblers/
  34. Nintendo Entertainment System (NES)
    https://8bitworkshop.com/doc­s/platforms/nes/
  35. Question about NES vectors and PPU
    https://archive.nes.science/nesdev-forums/f10/t4154.xhtml
  36. How do mapper chips actually work?
    https://archive.nes.science/nesdev-forums/f9/t13125.xhtml
  37. INES
    https://www.nesdev.org/wiki/INES
  38. NES Basics and Our First Game
    http://thevirtualmountain­.com/nes/2017/03/08/nes-basics-and-our-first-game.html
  39. Where is the reset vector in a .nes file?
    https://archive.nes.science/nesdev-forums/f10/t17413.xhtml
  40. CPU memory map
    https://www.nesdev.org/wi­ki/CPU_memory_map
  41. How to make NES music
    http://blog.snugsound.com/2008/08/how-to-make-nes-music.html
  42. Nintendo Entertainment System Architecture
    http://nesdev.icequake.net/nes.txt
  43. MIDINES
    http://www.wayfar.net/0×f00000_o­verview.php
  44. FamiTracker
    http://famitracker.com/
  45. nerdTracker II
    http://nesdev.parodius.com/nt2/
  46. How NES Graphics work
    http://nesdev.parodius.com/nesgfx.txt
  47. NES Technical/Emulation/Development FAQ
    http://nesdev.parodius.com/NES­TechFAQ.htm