Obsah
1. První seznámení s možnostmi PPU
4. Zvýšení intenzity zvolené barvové složky
5. Úplný zdrojový kód sedmého demonstračního příkladu
6. Vylepšený zápis adresy barvové palety
7. Úplný zdrojový kód osmého demonstračního příkladu
8. Vymazání barvové palety ve formě makra
9. Úplný zdrojový kód devátého demonstračního příkladu
10. Vymazání barvové palety ve formě podprogramu
11. Úplný zdrojový kód desátého demonstračního příkladu
12. Nastavení barev v barvové paletě z tabulkových dat
13. Úplný zdrojový kód jedenáctého demonstračního příkladu
14. Refaktoring – specifikace adresy pro zápis barev s využitím makra
15. Úplný zdrojový kód dvanáctého demonstračního příkladu
16. Příprava pro zobrazení spritů
17. Odkazy na předchozí části seriálu
18. Repositář s demonstračními příklady
1. První seznámení s možnostmi PPU
Ve čtvrté části seriálu o vývoji her a multimediálních dem určených pro slavnou a v mnoha ohledech přelomovou osmibitovou herní konzoli Nintendo Entertainment System (NES) se již (konečně) začneme zabývat jejími grafickými schopnostmi. Ukážeme si zejména práci s barvovými paletami, ale taktéž si postupně připravíme kód napsaný v assembleru, jenž bude určený pro vykreslení spritů – protože právě pohyblivé sprity tvoří společně s pozadím (background) celou grafickou herní scénu. Herní konzole NES totiž neobsahuje podporu pro klasickou bitmapovou grafiku ani podporu textových režimů – vše je nutné vykreslit přes sprity a pozadí, což jsou (velmi zjednodušeně řečeno) indexy bitmapových „dlaždic“ (tiles) uložených v k tomu určených blocích paměti. Vše se navíc poněkud komplikuje technologií mapování hodnot uložených v dlaždicích přes barvové palety na výsledné barvy zobrazené na obrazovce.
2. Řídicí registry PPU
Při práci s grafikou na herní konzoli NES je nutné nejprve provést korektní inicializaci čipu PPU. Zejména je nutné nastavit barvovou paletu, což nám umožní korektně navázat dalšími operacemi, například zobrazením pozadí, zobrazením spritů atd. Čip PPU se ovládá přes několik řídicích registrů, které jsou mapovány do adresového prostoru mikroprocesoru 6502. Všechny tyto řídicí registry jsou pro přehlednost vypsány v následující tabulce:
Registr | Adresa (CPU) | Stručný popis |
---|---|---|
PPUCTRL | $2000 | povolení NMI, nastavení výšky spritů, nastavení dlaždic atd. |
PPUMASK | $2001 | zvýraznění vybrané barvy, povolení spritů, povolení pozadí atd. |
PPUSTATUS | $2002 | stav snímku (VBLANK), stav spritů (kolize) |
OAMADDR | $2003 | adresa pro čtení/zápis do OAM (Object Attribute Memory) |
OAMDATA | $2004 | zapisovaná/čtená data do/z OAM (Object Attribute Memory) |
PPUSCROLL | $2005 | použito pro horizontální i vertikální scrolling |
PPUADDR | $2006 | adresa pro čtení/zápis do paměti PPU |
PPUDATA | $2007 | zapisovaná/čtená data do/z paměti PPU |
3. Nastavení barvové palety
Jednou z prvních operací s PPU, kterou je nutné provést, je nastavení barvové palety. Ve skutečnosti grafický subsystém NESu pracuje s dvojicí palet, z nichž každá obsahuje šestnáct barev. První paleta je určena pro pozadí, druhá pro sprity. Vzhledem k tomu, že každá barva je zapsána v jednom bajtu, je celková velikost paměti pro uložení obou palet rovna třiceti dvěma bajtům, které tvoří kontinuální blok, ovšem blok v paměti dostupné PPU a nikoli mikroprocesoru.
Obrázek 1: Zjednodušené schéma architektury herní konzole NES.
Barvové palety jsou v paměti dostupné čipu PPU typicky uloženy na adresách $3f00 až $3f1f. Ukažme si nyní, jakým způsobem se palety vynulují, tj. jak se do nich uloží sekvence 32 kódů šedé barvy (kupodivu nula neodpovídá barvě černé, ale právě šedé). Nejprve je nutné čipu PPU sdělit, na jaké adresy se mají (v následujícím kroku) posílaná data uložit – tato adresa se uloží do interního záchytného registru (latche). Před vlastním posláním adresy je vhodné pro jistotu záchytný registr inicializovat (aby PPU věděl, jak má zpracovat zápisy). Tato operace se provede přečtením bajtu z řídicího registru PPUSTATUS, což lze v assembleru realizovat snadno:
; vynulování barvové palety lda PPUSTATUS ; reset záchytného registru
Dále do čipu PPU pošleme adresu začátku barvových palet (tedy adresu bloku, kde budou kódy barev uloženy). Adresu je nutné poslat jako sekvenci dvou bajtů do řídicího registru PPUADDR, což vede k zápisu adresy do již zmíněného interního záchytného registru (latche). Budeme požadovat, aby palety začínaly na adrese $3f00, což znamená, že adresu pošleme systémem vyšší bajt, nižší bajt:
lda #$3f ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #$00 ; nižší bajt adresy sta PPUADDR
V posledním kroku pošleme do řídicího registru PPUDATA sekvenci třiceti dvou bajtů. Ty budou čipem PPU korektně zpracovány, protože se využije interní latch, jehož hodnota se po každém zápisu do PPUDATA zvýší o jedničku. V kódu používáme nám již známé instrukce mikroprocesoru MOS 6502 i anonymních návěští (label) podporovaných assemblerem ca65, takže zde pravděpodobně není zapotřebí dalšího podrobnějšího vysvětlování:
ldx #$20 ; počitadlo barev v paletě: 16+16 lda #$00 ; vynulování každé barvy
Vlastní programová smyčka:
: sta PPUDATA ; zápis barvy dex ; snížení hodnoty počitadla bne :-
4. Zvýšení intenzity zvolené barvové složky
Zobrazování grafiky čipem PPU je řízeno přes registr nazvaný PPUMASK. Jednotlivé bity tohoto registru mají následující význam:
Bit | Stručný popis |
---|---|
7 | zvýraznění modré barvové složky |
6 | zvýraznění zelené barvové složky |
5 | zvýraznění červené barvové složky |
4 | povolení zobrazení spritů |
3 | povolení zobrazení pozadí |
2 | povolení/zákaz zobrazení spritů v levých osmi pixelech obrazovky |
1 | povolení/zákaz zobrazení pozadí v levých osmi pixelech obrazovky |
0 | barevný nebo monochromatický výstup |
Zajímavé jsou pro nás prozatím nejvyšší tři bity tohoto registru, protože ty nám umožňují vykreslit na obrazovku „alespoň něco“ kromě šedé barvy. Tyto bity totiž určují, že jedna z vybraných barvových složek RGB se má v rámci celého obrazu zvýraznit na úkor obou zbývajících barvových složek – to vlastně znamená, že má smysl nastavit pouze jediný z těchto bitů.
Můžeme se například pokusit o zvýraznění modré barvové složky, a to takto:
lda #%10000000 ; vysoká intenzita modré barvy sta PPUMASK
S výsledkem:
Obrázek 2: Zvýraznění modré barvové složky.
Dtto pro zelenou barvovou složku:
lda #%01000000 ; vysoká intenzita zelené barvy sta PPUMASK
S výsledkem:
Obrázek 3: Zvýraznění zelené barvové složky.
A konečně zvýraznění červené barvové složky celého obrazu:
lda #%00100000 ; vysoká intenzita červené barvy sta PPUMASK
S výsledkem:
Obrázek 4: Zvýraznění červené barvové složky.
5. Úplný zdrojový kód sedmého demonstračního příkladu
Zdrojový kód v pořadí již sedmého demonstračního příkladu popsaného v předchozích kapitolách je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example07.asm (pro překlad a slinkování tohoto demonstračního příkladu použijte tento Makefile):
; --------------------------------------------------------------------- ; Kostra programu pro herní konzoli NES ; Nastavení barvové palety, zvýšení intenzity barvy ; ; Založeno na příkladu https://github.com/depp/ctnes/tree/master/nesdev/01 ; 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 ; --------------------------------------------------------------------- ; 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 ; --------------------------------------------------------------------- ; 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 "HEADER" .byte $4e, $45, $53, $1a .byte prg_npage .byte chr_npage .byte ((mapper & $0f) << 4) | (mirroring & 1) .byte mapper & $f0 ; --------------------------------------------------------------------- ; 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 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 stx PPUMASK ; nastavení PPUMASK = 0 ; č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 ; vynulování barvové palety lda PPUSTATUS ; reset záchytného registru lda #$3f ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #$00 ; nižší bajt adresy sta PPUADDR 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 :- lda #%10000000 ; vysoká intenzita modré barvy sta PPUMASK ; 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 "VECTORS" .addr nmi .addr reset .addr irq ; --------------------------------------------------------------------- ; Finito ; ---------------------------------------------------------------------
6. Vylepšený zápis adresy barvové palety
Podívejme se ještě jednou na tu část programového kódu, v níž do řídicích registrů čipu PPU zapisujeme adresu začátku barvových palet:
lda #$3f ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #$00 ; nižší bajt adresy sta PPUADDR
Tento kód má několik nectností. Zejména je v něm adresa „zadrátována“, přičemž by bylo vhodnější použít pojmenovanou konstantu (assembler je totiž – poněkud překvapivě – v některých ohledech vyšším programovacím jazykem, než BASIC). A navíc by bylo vhodné, aby se rozdělení 16bitové adresy na vyšší a nižší bajt provedlo automaticky, ideálně s využitím možností assembleru – protože rozdělení 16bitové hodnoty na dvě hodnoty osmibitové je v oblasti, v níž se mikroprocesor MOS 6502 používá, velmi časté.
Nejprve tedy vytvořme symbolickou konstantu obsahující začátek paměťového bloku s paletami. To je triviální operace:
; Další důležité adresy PALETTE = $3f00
Následně jen musíme zajistit, aby se z této konstanty získal vyšší a nižší bajt. Assembler ca65 pro tyto účely obsahuje dvojici operátorů zapisovaných znaky < a > Tyto operátory se vyhodnocují v čase překladu, takže za jejich použití neplatíme žádným strojovým časem (při běhu programu):
lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR
7. Úplný zdrojový kód osmého demonstračního příkladu
Zdrojový kód v pořadí osmého demonstračního příkladu popsaného v předchozích kapitole je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example08.asm (pro překlad a slinkování tohoto demonstračního příkladu použijte tento Makefile):
; --------------------------------------------------------------------- ; Kostra programu pro herní konzoli NES ; Nastavení barvové palety, zvýšení intenzity barvy ; ; Založeno na příkladu https://github.com/depp/ctnes/tree/master/nesdev/01 ; 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 ; 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 ; --------------------------------------------------------------------- ; 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 "HEADER" .byte $4e, $45, $53, $1a .byte prg_npage .byte chr_npage .byte ((mapper & $0f) << 4) | (mirroring & 1) .byte mapper & $f0 ; --------------------------------------------------------------------- ; 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 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 stx PPUMASK ; nastavení PPUMASK = 0 ; č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 ; vynulování barvové palety lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR 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 :- lda #%10000000 ; vysoká intenzita modré barvy sta PPUMASK ; 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 "VECTORS" .addr nmi .addr reset .addr irq ; --------------------------------------------------------------------- ; Finito ; ---------------------------------------------------------------------
8. Vymazání barvové palety ve formě makra
V předchozí dvojici demonstračních příkladů bylo nastavení (resp. přesněji řečeno vynulování) barvové palety řešeno přímo v hlavní proceduře reset. Pro větší přehlednost je však vhodnější přenést tento kód buď do makra nebo do samostatného podprogramu (subrutiny). Nejdříve se podívejme na řešení založené na použití makra. Kód pro vynulování barvové palety překopírujeme do pojmenovaného makra:
; vynulování barvové palety .macro clear_palette lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR 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 :- .endmacro
Toto makro se velmi jednoduše použije (expanduje) následujícím způsobem:
; vymazání obsahu RAM clear_ram ; čekání na další snímek wait_for_frame ; vynulování barvové palety clear_palette ; expanze makra
Makro je do kódu přímo vloženo, což je jasně patrné i při pohledu na listing, což je assemblerem generovaný soubor, jenž pořizujeme v době překladu:
00003Ar 1 00003Ar 1 ; vynulování barvové palety 00003Ar 1 AD 02 20 A9 clear_palette ; expanze makra 00003Er 1 3F 8D 06 20 000042r 1 A9 00 8D 06 000046r 1 20 A2 20 A9 00004Ar 1 00 8D 07 20 00004Er 1 CA D0 FA 000051r 1
9. Úplný zdrojový kód devátého demonstračního příkladu
Zdrojový kód v pořadí devátého demonstračního příkladu popsaného v předchozích kapitole je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example09.asm (pro překlad a slinkování tohoto demonstračního příkladu použijte tento Makefile):
; --------------------------------------------------------------------- ; Kostra programu pro herní konzoli NES ; Nastavení barvové palety, zvýšení intenzity barvy ; Paleta je nastavena přes makro ; ; Založeno na příkladu https://github.com/depp/ctnes/tree/master/nesdev/01 ; 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 ; 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 ; vynulování barvové palety .macro clear_palette lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR 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 :- .endmacro ; --------------------------------------------------------------------- ; 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 "HEADER" .byte $4e, $45, $53, $1a .byte prg_npage .byte chr_npage .byte ((mapper & $0f) << 4) | (mirroring & 1) .byte mapper & $f0 ; --------------------------------------------------------------------- ; 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 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 stx PPUMASK ; nastavení PPUMASK = 0 ; č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 ; vynulování barvové palety clear_palette ; expanze makra lda #%10000000 ; vysoká intenzita modré barvy sta PPUMASK ; 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 "VECTORS" .addr nmi .addr reset .addr irq ; --------------------------------------------------------------------- ; Finito ; ---------------------------------------------------------------------
10. Vymazání barvové palety ve formě podprogramu
V některých případech nám však nevyhovuje, že kód makra je vkládán (expandován) při každém volání makra v programovém kódu. Mnohdy budeme chtít, aby byl kód realizován formou podprogramu (subrutiny), která je skutečně volána. Využijeme zde přitom trojici technik:
- Instrukci jsr neboli Jump to SubRoutine pro volání podprogramu
- Instrukci rts neboli ReTurn from Subroutine pro návrat z podprogrammu
- Syntaktickou konstrukci a assembleru ca65, jenž nám umožní subrutiny definovat ve zdrojovém kódu
Volání subrutiny vypadá následovně:
; vynulování barvové palety jsr clear_palette ; zavolání subrutiny
Což je rozdílné od expanze makra, které se zapisuje odlišně:
; vynulování barvové palety clear_palette ; expanze makra
Kód samotné subrutiny se nepatrně odlišuje od makra, protože nesmíme zapomenout na instrukci zajišťující návrat zpět do volaného kódu:
; vynulování barvové palety .proc clear_palette lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR 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
Volaná subrutina není, na rozdíl od makra, přímo expandována, ale je skutečně zavolána instrukcí jsr:
00003Ar 1 00003Ar 1 ; vynulování barvové palety 00003Ar 1 20 rr rr jsr clear_palette ; zavolání subrutiny 00003Dr 1
Jedná se prozatím o velmi jednoduchý podprogram, kterému se nepředávají žádné parametry a tudíž se ani nemusíme starat o to, jak vůbec tyto parametry předávat – zda přes registry, zásobník apod.
11. Úplný zdrojový kód desátého demonstračního příkladu
Zdrojový kód v pořadí desátého demonstračního příkladu popsaného v předchozích kapitole je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example10.asm (pro překlad a slinkování tohoto demonstračního příkladu použijte tento Makefile):
; --------------------------------------------------------------------- ; Kostra programu pro herní konzoli NES ; Nastavení barvové palety, zvýšení intenzity barvy ; ; Založeno na příkladu https://github.com/depp/ctnes/tree/master/nesdev/01 ; 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 ; 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 ; --------------------------------------------------------------------- ; 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 "HEADER" .byte $4e, $45, $53, $1a .byte prg_npage .byte chr_npage .byte ((mapper & $0f) << 4) | (mirroring & 1) .byte mapper & $f0 ; --------------------------------------------------------------------- ; 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 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 stx PPUMASK ; nastavení PPUMASK = 0 ; č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 ; vynulování barvové palety jsr clear_palette ; zavolání subrutiny lda #%10000000 ; vysoká intenzita modré barvy sta PPUMASK ; 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 lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR 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 ; --------------------------------------------------------------------- ; Tabulka vektorů CPU ; --------------------------------------------------------------------- .segment "VECTORS" .addr nmi .addr reset .addr irq ; --------------------------------------------------------------------- ; Finito ; ---------------------------------------------------------------------
12. Nastavení barev v barvové paletě z tabulkových dat
Podívejme se nyní, jakým způsobem lze nastavit barvovou paletu na barvy zvolené programátorem, nikoli tedy na nudnou šedou barvu, jak tomu bylo v předchozích demonstračních příkladech. Připomeňme si, že se vlastně nastavuje dvojice palet – šestnáct barev pozadí a šestnáct barev spritů. Každá barva v paletě je reprezentována jediným bajtem, tj. obě palety tvoří v paměti PPU blok o délce třiceti dvou bajtů. Barvy jsou vybírány z této palety:
Obrázek 5: Barvová paleta používaná herní konzolí NES.
Budeme chtít nastavit paletu, která odpovídá slavné hře Super Mario Bros. Tuto paletu lze reprezentovat sekvencí 32 bajtů uvozených návěštím (label) palette. Jednotlivé bajty jsou v assembleru ca65 definovány přes .byte (zatímco NESASM používá .db – ovšem význam je totožný):
; 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ů
Obrázek 6: Super Mario Bros ve verzi pro NES.
Podprogram (subrutina) pro nastavení barvové palety se do značné míry podobá subrutině pro nastavení všech barev na totožnou (nulovou) hodnotu – provede se přesun 32 bajtů z RAM CPU do RAM PPU přes registr PPUDATA. Pouze se namísto zápisu nulové hodnoty uložené v akumulátoru hodnota načte z palety instrukcí lda adresa,x, kde registr x obsahuje offset, který se postupně zvyšuje od nuly do 31. Současně tento registr slouží i jako počitadlo, jehož hodnota je testována instrukcí cpx následované podmíněným skokem bne:
; nastavení barvové palety .proc load_palette lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR ; $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
Podprogram pro nastavení barvové palety zavoláme nám již známou instrukcí jsr:
; nastavení barvové palety jsr load_palette ; zavolání subrutiny
Výsledkem bude tato „herní scéna“:
Obrázek 7: První barva palety pozadí.
13. Úplný zdrojový kód jedenáctého demonstračního příkladu
Zdrojový kód v pořadí jedenáctého demonstračního příkladu popsaného v předchozích kapitole je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example11.asm (pro překlad a slinkování tohoto demonstračního příkladu použijte tento Makefile):
; --------------------------------------------------------------------- ; Kostra programu pro herní konzoli NES ; Nastavení barvové palety, zvýšení intenzity barvy ; ; Založeno na příkladu https://github.com/depp/ctnes/tree/master/nesdev/01 ; 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 ; 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 ; --------------------------------------------------------------------- ; 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 "HEADER" .byte $4e, $45, $53, $1a .byte prg_npage .byte chr_npage .byte ((mapper & $0f) << 4) | (mirroring & 1) .byte mapper & $f0 ; --------------------------------------------------------------------- ; 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 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 stx PPUMASK ; nastavení PPUMASK = 0 ; č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 ; 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 lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR 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 lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR ; $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 ; 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ů ; --------------------------------------------------------------------- ; Tabulka vektorů CPU ; --------------------------------------------------------------------- .segment "VECTORS" .addr nmi .addr reset .addr irq ; --------------------------------------------------------------------- ; Finito ; ---------------------------------------------------------------------
14. Refaktoring – specifikace adresy pro zápis barev s využitím makra
Pokud se podíváme na instrukce v podprogramech (subrutinách) clear_palette a load_palette, zjistíme, že prvních pět instrukcí je totožných, protože provádí inicializaci PPU a nastavení interního záchytného registru (latche) pro přenos dat do RAM PPU (která jinak není pro CPU dostupná). Těchto pět instrukcí je v následujících dvou výpisech zvýrazněno:
.proc clear_palette lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR 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
Totožných pět instrukcí v subrutině load_palette:
.proc load_palette lda PPUSTATUS ; reset záchytného registru lda #>PALETTE ; nastavení adresy pro barvovou paletu $3f00 sta PPUADDR lda #<PALETTE ; nižší bajt adresy sta PPUADDR ; $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
Program by bylo vhodné nějakým způsobem refaktorovat, aby se totožný kód ve zdrojovém textu neopakoval. Máme dvě možnosti – použít makro nebo ze subrutiny volat další subrutinu. Oněch pět shodných instrukcí má délku 13 bajtů, což je (alespoň podle mého názoru) na hranici mezi použitím makra (více volání – větší kód) a subrutiny (více volání – déletrvající kód). Zkusme využít makro, do něhož instrukce prostě zkopírujeme:
.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
Úprava zdrojového kódu obou subrutin je nyní triviální:
.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
a:
.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
15. Úplný zdrojový kód dvanáctého demonstračního příkladu
Zdrojový kód v pořadí již dvanáctého demonstračního příkladu popsaného v předchozích kapitole je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example12.asm (pro překlad a slinkování tohoto demonstračního příkladu opět použijte tento Makefile):
; --------------------------------------------------------------------- ; Kostra programu pro herní konzoli NES ; Nastavení barvové palety, zvýšení intenzity barvy ; Setup PPU přes makro ; ; Založeno na příkladu https://github.com/depp/ctnes/tree/master/nesdev/01 ; 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 ; 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 = 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 "HEADER" .byte $4e, $45, $53, $1a .byte prg_npage .byte chr_npage .byte ((mapper & $0f) << 4) | (mirroring & 1) .byte mapper & $f0 ; --------------------------------------------------------------------- ; 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 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 stx PPUMASK ; nastavení PPUMASK = 0 ; č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 ; 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 ; 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ů ; --------------------------------------------------------------------- ; Tabulka vektorů CPU ; --------------------------------------------------------------------- .segment "VECTORS" .addr nmi .addr reset .addr irq ; --------------------------------------------------------------------- ; Finito ; ---------------------------------------------------------------------
16. Příprava pro zobrazení spritů
V rámci dalšího kroku zobrazíme na ploše obrazovky sprite, tedy pohyblivý rastrový obrázek. Zatímco například na C64 je práce se sprity triviální a na osmibitových počítačích Atari jen nepatrně složitější, vyžaduje práce se sprity na herní konzoli NES poněkud komplexnější přípravy (o to jednodušší je ovšem následně zobrazení či pohyb spritů):
- V obrazu cartridge (který se nahrává do emulátoru nebo reálné herní konzole NES) musí být i bitmapa s definicí tvarů, z nichž se sprity skládají.
- Data spritů je nutné přenést do paměti PPU, a to ideálně s využitím DMA (přímého přístupu do paměti). Tato operace se provede jen jedenkrát, pokud se spokojíme s jednou sadou spritů (někdy se pro různé levely používají odlišné sady).
- Je nutné správně nastavit konfiguraci spritů – jejich případné otočení okolo souřadných os, prioritu vůči pozadí a výběr části barvové palety (lze měnit jak v rámci snímku, tak i mezi snímky).
- Přes PPU se musí povolit zobrazení spritů.
- Taktéž se musí povolit NMI, aby se sprity automaticky zobrazily v každém snímku.
17. Odkazy na předchozí části seriálu
- Kouzlo minimalismu: vývoj her a demo programů pro herní konzoli NES
https://www.root.cz/clanky/kouzlo-minimalismu-vyvoj-her-a-demo-programu-pro-herni-konzoli-nes/ - Základy tvorby her pro herní konzoli NES: mikroprocesor 6502 a assembler ca65
https://www.root.cz/clanky/zaklady-tvorby-her-pro-herni-konzoli-nes-mikroprocesor-6502-a-assembler-ca65/ - Základy tvorby her pro herní konzoli NES: triky nabízené assemblerem, tvorba zvuků a grafiky
https://www.root.cz/clanky/zaklady-tvorby-her-pro-herni-konzoli-nes-triky-nabizene-assemblerem-tvorba-zvuku-a-grafiky/
18. 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. 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ář:
19. Odkazy na Internetu
- NesDev.org
https://www.nesdev.org/ - 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 - NES – Init Code
https://nesdev-wiki.nes.science/wikipages/Init_code.xhtml - 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