Asembler procesora 6502     Obsługa konsoli NES (Famicom)           Kompilacja


Na początek podstawowe informacje o konsoli NES (Famicom):

Pierwszy raz została wyprodukowana w 1983 roku przez firmę Nintendo i była sprzedawana w Japonii
pod nazwą "Famicom" (Family computer). Następnie w 1985 roku pojawiła się w USA jako NES (Nin-
tendo Entertainment System). Obie te konsole wewnętrznie są prawie identyczne, różnią się jedynie
wyglądem oraz złączem kartridży (60/72 piny) i joypadów (15/7 pinów). Produkcję konsoli NES zakoń-
czono w 1995 roku w USA, a konsoli Famicom w 2003 roku w Japonii. Przez ten czas sprzedano ponad
60 milionów konsol i 500 milionów gier na całym świecie. W Polsce w latach 90-tych pojawiły się klony
konsoli Famicom (najpopularniejszy był Pegasus).

W konsoli NES znajduje się procesor 2A03 (NTSC) / 2A07 (PAL), taktowany zegarem 1.79/1.66 MHz,
wyprodukowany w technologii NMOS przez firmę Ricoh. Od zwykłego procesora 6502 różni się tym,
że nie może pracować w trybie dziesiętnym (BCD mode) oraz posiada 22 rejestry wejścia/wyjścia (I/O),
dostępne poprzez mechanizm adresowania pamięci. Służą one do obsługi wbudowanego programo-
walnego generatora dźwięku (pAPU - pseudo Audio Processing Unit), prostego kanału DMA oraz kon-
trolerów gier (joypadów). Poza tym jego budowa i lista instrukcji jest identyczna. Dodatkowo w NESie
znajduje się procesor graficzny PPU (Picture Processing Unit), o nazwie 2C02 (NTSC) / 2C07 (PAL),
taktowany zegarem 5.37/5.32 MHz, również firmy Ricoh. Jest to specjalizowany układ zajmujący się
tylko obliczaniem i generowaniem grafiki.

Przestrzeń adresowa głównego procesora (CPU) [64 kB]:
$0000-07FF - wewnętrzna pamięć 2kB RAM:
  $0000-00FF - strona zerowa (Zero Page)
  $0100-01FF - stos (maks. 128 rozgałęzień programu)
$2000-2007 - rejestry procesora graficznego PPU
$4000-4017 - rejestry procesora audio (pAPU) i joypadów
$5000-5FFF - rozszerzenie pamięci VRAM (MMC5)
$6000-7FFF - pamięć RAM kartridża (SRAM)
$8000-FFFF - pamięć ROM kartridża:
  $8000-BFFF - PRG-ROM bank 0
  $C000-FFFF - PRG-ROM bank 1:
    $FFFA-FFFF - wektory przerwań NMI/RESET/IRQ

Przestrzeń adresowa procesora graficznego (PPU) [16 kB VRAM]:
$0000-0FFF - tablica patternów 0 (CHR-ROM kartridża) [Pattern Table]
$1000-1FFF - tablica patternów 1 (CHR-ROM kartridża) [Pattern Table]
$2000-23BF - tablica indeksów 0 [Name Table]
$23C0-23FF - tablica właściwości 0 [Attribute Table]
$2400-27BF - tablica indeksów 1 [Name Table]
$27C0-27FF - tablica właściwości 1 [Attribute Table]
$2800-2BBF - tablica indeksów 2 [Name Table]
$2BC0-2BFF - tablica właściwości 2 [Attribute Table]
$2C00-2FBF - tablica indeksów 3 [Name Table]
$2FC0-2FFF - tablica właściwości 3 [Attribute Table]
$3000-3EFF - nie używany obszar
$3F00-3F0F - paleta tła [Background Palette]
$3F10-3F1F - paleta spriteów [Sprite Palette]
$3F20-3FFF - nie używany obszar

Fizycznie PPU posiada 2 kB pamięci VRAM (Video-RAM), przeznaczonej do przechowywania 2 tablic
indeksów i 2 tablic właściwości, 256 bajtów pamięci SPR-RAM (Sprite-RAM) dla właściwości 64 sprite-
ów oraz 32 bajty pamięci dla palety kolorów tła i spriteów. Dostęp do tej pamięci jest możliwy poprzez
rejestry DMA/PPU.

Niżej znajduje się opis kolejnych fragmentów kodu programu, który służy do inicjalizacji i obsługi
konsoli NES (Famicom). Do kompilacji użyto kompilatora "NES Assembler 3.01". Tworzy on ROMy
(pliki ".NES"), które można uruchamiać na dowolnym emulatorze konsoli NES (Famicom).
 .inesprg 1 ;liczba banków kodu PRG-ROM (po 16 kB)
 .ineschr 1 ;liczba banków grafiki CHR-ROM (po 8 kB)
 .inesmap 0 ;typ mappera (0=NROM, czyli brak przełączania banków)
 .inesmir 0 ;powielanie tła (0=poziome, 1=pionowe)
Wartości tych parametrów znajdą się w 16-bajtowym nagłówku (iNES) pliku wynikowego ".NES".
 .zp ;zmienne strony zerowej (zero page)

joy1=$00   ;stan przycisków joypada 1
joy2=$01   ;stan przycisków joypada 2
ptr.lo=$02 ;wskaźnik 16-bitowy (młodszy bajt)
ptr.hi=$03 ;wskaźnik 16-bitowy (starszy bajt)
var0=$04
var1=$05
Definiowanie zmiennych, które będą umieszczone na stronie zerowej pamięci RAM (w obszarze
adresowym $0000-00FF). Po nazwie zmiennej podaje się jej 8-bitowy adres.
 .bss ;zmienne poza stroną zerową

;Zmienne z właściwościami spriteu nr 0
spr0y=$700 ;pozycja Y
spr0n=$701 ;numer patternu z tablicy [Pattern Table] dla spriteów
spr0a=$702 ;atrybuty (bity 76543210):
           ; 7 - Flip sprite vertically
           ; 6 - Flip sprite horizontally
           ; 5 - Priority (0: in front of background, 1: behind background)
           ;10 - Color Palette of sprite (choose which set of 4 from the 16 colors to use)
spr0x=$703 ;pozycja X
Definiowanie zmiennych, które będą umieszczone poza stroną zerową w pamięci RAM (obszar
adresowy $0200-07FF). Po nazwie zmiennej podaje się jej pełny (16-bitowy) adres.
 .code
Dyrektywa oznaczająca początek kodu programu.
 .bank  0
 .org   $8000
Utworzenie banku kodu nr 0 (PRG-ROM) o pojemności 8 kB i ustawienie jego adresu na $8000.

Po włączeniu/zresetowaniu konsoli NES trzeba odczekać ok. 29658 (NTSC) / 33132 (PAL) cykli
zegarowych procesora, zanim będzie można odczytywać/zapisywać rejestry kontrolne $2003-2007
(ustabilizowanie się procesora graficznego PPU).
RESET:
 sei          ;wyłączenie obsługi przerwań IRQ procesora
 cld          ;wyłączenie trybu dziesiętnego (procesor w NES go nie obsługuje)
 ldx    #$40
 stx    $4017 ;wyłączenie obsługi przerwań IRQ licznika ramek pAPU
 ldx    #$FF
 txs          ;resetowanie wskaźnika stosu
 inx
 stx    $2000 ;wyłączenie generowania przerwania NMI podczas odświeżania ekranu (VBlank)
 stx    $2001 ;wyłączenie wyświetlania grafiki na ekranie
 stx    $4010 ;wyłączenie obsługi przerwań IRQ kanału DMC pAPU

;Opcjonalnie w tym miejscu można wykonać dodatkowy kod inicjujący (np. ustawić mapper)

 bit    $2002  ;zerowanie flagi VBlank (bit 7), jeśli została ustawiona podczas włączania/resetu NESa
 jsr    VBlank ;czekanie aż PPU będzie gotowy (upłynęło ok. 27384 cykli)

;Rejestr stanu $2002 (bity 76543210):
; 7 - VBlank Occurance (1=In VBlank)
; 6 - Sprite #0 Occurance (1=PPU has hit Sprite #0)
; 5 - Scanline Sprite Count (0=8-sprites or less on current scanline,
;                            1=More than 8 sprites on current scanline)
; 4 - VRAM Write Flag (0=Writes to VRAM are respected, 1=Writes to VRAM are ignored)
;NOTE: bit7 is set to 0 after read occurs.
;NOTE: After a read occurs, $2005 is reset, hence the next write to $2005 will be Horizontal.
;NOTE: After a read occurs, $2006 is reset, hence the next write to $2006 will be high byte portion.
Pozostało ok. 30000 cykli zegarowych procesora, zanim ustabilizuje się PPU. W tym czasie można
zresetować pamięć RAM, ustawić audio lub inne rejestry mappera.
ClrMem:
 lda    #$00
 sta    $0000,X
 sta    $0100,X
 sta    $0200,X
 sta    $0300,X
 sta    $0400,X
 sta    $0500,X
 sta    $0600,X
 lda    #$FF    ;ta wartość powoduje, że zbędne spritey nie będą wyświetlane
 sta    $0700,X ;adres strony pamięci przeznaczonej dla właściwości spriteów
 inx
 bne    ClrMem
Czyszczenie pamięci RAM konsoli (w tym właściwości spriteów).
 jsr    VBlank  ;czekanie aż PPU będzie gotowy (upłynęło ok. 57165 cykli)
Upłynęło wystarczająco dużo cykli zegarowych procesora i PPU jest gotowy do prawidłowej pracy.
Teraz można załadować palety kolorów, tło, właściwości tła, spritey i wyświetlać obraz.
;Czyszczenie spriteów (nie wymagane przez emulatory)
 lda    #$07    ;numer strony pamięci z właściwościami spriteów
 sta    $4014   ;wyświetlenie (odświeżenie) spriteów
Podczas moich testów tego programu na konsoli Pegasus, losowo i w różnych miejscach ekranu
pojawiał się sprite nr 0. Ten kod zapobiega temu. Nie jest wymagany przez emulatory, które nie
wyświetlają żadnych niepotrzebnych spriteów.
;Czyszczenie tła całego ekranu (nie wymagane przez emulatory)
 lda    $2002   ;resetowanie PPU
 lda    #$20    ;starszy bajt adresu tablicy indeksów nr 0 [Name Table]
 sta    $2006   ;zapis do rejestru adresowego PPU
 lda    #$00    ;młodszy bajt adresu tablicy indeksów nr 0 [Name Table]
 sta    $2006   ;zapis do rejestru adresowego PPU
 ldy    #$04
ClrBg:
 ldx    #$F0
ClrBg0:
 sta    $2007
 dex
 bne    ClrBg0
 dey
 bne    ClrBg
Czyszczenie tła całego ekranu. Nie jest wymagane tylko przez emulatory, które same zerują pamięć
tła. Prawdziwa konsola tego nie robi i dlatego, jeśli brakuje tej procedury, to na ekranie pojawiają się
przypadkowe znaki graficzne (artefakty).
 lda    $2002   ;resetowanie PPU
 lda    #$3F    ;starszy bajt adresu palety tła [Background Palette]
 sta    $2006   ;zapis do rejestru adresowego PPU
 lda    #$00    ;młodszy bajt adresu palety tła [Background Palette]
 sta    $2006   ;zapis do rejestru adresowego PPU
 ldx    #$00
LoadPal:
 lda    Palette,X
 sta    $2007   ;zapis koloru palety do pamięci VRAM
                ;(adres w rejestrze adresowym PPU zwiększa się automatycznie)
 inx
 cpx    #$20    ;liczba bajtów do skopiowania
 bne    LoadPal
Ładowanie palety kolorów tła i spriteów do pamięci VRAM (adresy $3F00 i $3F10). Pod adresem
$3F00 znajduje się wartość koloru przeźroczystości tła i spriteów, która jest powielana co 4 bajty.
Każda z tych palet zawiera 16 kolorów, które są podzielone na 4 zestawy (sety).
 lda    $2002   ;resetowanie PPU
 lda    #$20    ;starszy bajt adresu tablicy indeksów nr 0 [Name Table]
 sta    $2006   ;zapis do rejestru adresowego PPU
 lda    #$00    ;młodszy bajt adresu tablicy indeksów nr 0 [Name Table]
 sta    $2006   ;zapis do rejestru adresowego PPU
 ldx    #$00
LoadBg:
 lda    Background,X
 sta    $2007   ;zapis indeksu tła do pamięci VRAM
                ;(adres w rejestrze adresowym PPU zwiększa się automatycznie)
 inx
 cpx    #$05    ;liczba bajtów do skopiowania
 bne    LoadBg
Ładowanie indeksów tła do pamięci VRAM. Każdy raster ekranu tła o wymiarach 8x8 pikseli jest
opisany za pomocą 1 bajtu. Indeksy tła to numery patternów z tablicy [Pattern Table] dla tła, które
mają być wyświetlone w kolejnych rastrach ekranu jako tło.
 lda    $2002   ;resetowanie PPU
 lda    #$23    ;starszy bajt adresu tablicy właściwości nr 0 [Attribute Table]
 sta    $2006   ;zapis do rejestru adresowego PPU
 lda    #$C0    ;młodszy bajt adresu tablicy właściwości nr 0 [Attribute Table]
 sta    $2006   ;zapis do rejestru adresowego PPU
 ldx    #$00
LoadBgAttr:
 lda    BgAttr,X
 sta    $2007   ;zapis właściwości tła do pamięci VRAM
                ;(adres w rejestrze adresowym PPU zwiększa się automatycznie)
 inx
 cpx    #$01    ;liczba bajtów do skopiowania
 bne    LoadBgAttr
Ładowanie właściwości tła do pamięci VRAM. Każdy blok ekranu tła o wymiarach 32x32 piksele jest
opisany za pomocą 1 bajtu; po 2 bity na każdy raster 16x16 pikseli. Wartość tych 2 bitów decyduje,
który z 4 zestawów kolorów palety tła, ma być użyty do wyświetlenia danego rastra z tego bloku.
 ldx    #$00
LoadSpr:
 lda    Sprites,X
 sta    $0700,X ;adres strony pamięci dla właściwości spriteów
 inx
 cpx    #$0C    ;liczba bajtów do skopiowania
 bne    LoadSpr
Ładowanie właściwości spriteów do pamięci RAM. Każdy sprite jest opisany za pomocą 4 bajtów:
1/4 - współrzędne Y/X położenia spriteu na ekranie; 2 - numer patternu z tablicy [Pattern Table]
dla spriteów, który ma być wyświetlony jako sprite; 3 - atrybuty; wartość 2-najmłodszych bitów tego
bajtu decyduje, który z 4 zestawów kolorów palety spriteów, ma być użyty do wyświetlenia spriteu.
 lda    #%00010000 ;generowanie przerwania NMI wyłączone, PPU działa w trybie Master, rozmiar spriteów
 sta    $2000      ;8x8 pikseli, wybranie tablicy patternów nr 1 (spod adresu $1000) dla tła, wybranie
                   ;tablicy patternów nr 0 (spod adresu $0000) dla spriteów, automatyczne zwiększanie
                   ;adresu przez PPU o 1, wybranie tablicy indeksów nr 0 (spod adresu $2000)

;Jedną tablicę patternów ($0000 lub $1000) można wybrać jednocześnie dla tła i dla spriteów.

;Jeśli rozmiar spriteów wynosi 8x16 pikseli, to sprite składa się z 2 patternów: górnego i dolnego.
;Jeśli spritey korzystają z tablicy patternów nr 0 ($0000), to we właściwościach spriteu podaje się
;parzysty numer patternu (0, 2, 4, 6,...), który będzie górną częścią spriteu; dolną jego częścią
;będzie pattern o kolejnym numerze (1, 3, 5, 7,...).
;Jeśli spritey korzystają z tablicy patternów nr 1 ($1000), to we właściwościach spriteu podaje się
;nieparzysty numer patternu (1, 3, 5, 7,...), który będzie dolną częścią spriteu; górną jego częścią
;będzie pattern o wcześniejszym numerze (0, 2, 4, 6,...).

;Rejestr kontrolny $2000 (bity 76543210):
; 7 - Execute NMI on VBlank (1=ON)
; 6 - PPU Master/Slave Selection (0=MASTER)
; 5 - Sprite Size (0=8x8, 1=8x16 pixels)
; 4 - Background Pattern Table Address (0=$0000, 1=$1000)
; 3 - Sprite Pattern Table Address (0=$0000, 1=$1000)
; 2 - PPU Address Increment (0=by 1, 1=by 32)
;10 - Name Table Address (00=$2000, 01=$2400, 10=$2800, 11=$2C00)
Ustawianie parametrów rejestru kontrolnego PPU $2000.
 lda    #%00011110 ;kolory bez zmian, spritey widoczne, tło widoczne, spritey widoczne na całym
 sta    $2001      ;ekranie, tło widoczne na całym ekranie, normalne wyświetlanie obrazu.

;Rejestr kontrolny $2001 (bity 76543210):
;765 - when bit0=1: Full Background Colour (000=None, 001=Green, 010=Blue, 100=Red)
;      when bit0=0: Color Intensity (000=None, 001=Green, 010=Blue, 100=Red)
; 4 - Sprite Visibility (1=ON)
; 3 - Background Visibility (1=ON)
; 2 - Sprite Clipping (0=Sprites invisible in left 8-pixel column)
; 1 - Background Clipping (0=BG invisible in left 8-pixel column)
; 0 - Display Type (0=Normal, 1=AND 0x30 all palette entries, effectively producing a monochrome
;                   display, note that colour emphasis STILL works when this is on!)
Ustawianie parametrów rejestru kontrolnego PPU $2001.
 jsr    HVScroll     ;wyłączenie przewijania tła
Rozgałęzienie do procedury, dzięki której obraz jest poprawnie wyświetlany.
 ldx    #LOW(Text0)  ;młodszy bajt adresu tekstu ASCIIZ do wyświetlenia
 ldy    #HIGH(Text0) ;starszy bajt adresu tekstu ASCIIZ do wyświetlenia
 lda    #$07         ;numer kolumny, w której będzie wyświetlony pierwszy znak (0-31)
 sta    var0
 lda    #$0C         ;numer wiersza, w którym będzie wyświetlony pierwszy znak (0-29)
 jsr    DrawBgText   ;wyświetlanie tekstu ASCIIZ jako tła
Rozgałęzienie do procedury, która wyświetla dowolny tekst ASCIIZ jako tło.
Loop:
 jsr    ReadJoy1   ;zapis stanu przycisków joypada 1 do zmiennej
 lda    joy1
 tay
 and    #%00010000 ;START
 beq    NoStr
 ldx    #LOW(Text1)  ;obsługa naciśnięcia przycisku START
 ldy    #HIGH(Text1)
 lda    #$07
 sta    var0
 lda    #$0C
 jsr    DrawBgText ;wyświetlanie tekstu ASCIIZ jako tła
 jmp    Draw
NoStr:
 tya
 and    #%00100000 ;SELECT
 beq    NoSel

;Tu można umieścić kod obsługujący naciśnięcie przycisku SELECT

 jmp    Loop
NoSel:
 tya
 and    #%10000000 ;A
 beq    NoA

;Tu można umieścić kod obsługujący naciśnięcie przycisku A

 jmp    Loop
NoA:
 tya
 and    #%01000000 ;B
 beq    Loop

;Tu można umieścić kod obsługujący naciśnięcie przycisku B

 jmp    Loop
Start:
 jsr    ReadJoy1   ;zapis stanu przycisków joypada 1 do zmiennej
 lda    joy1
 tay
 and    #%00001000 ;UP
 beq    NoUp
 lda    spr0y      ;obsługa naciśnięcia przycisku UP
 sec
 sbc    #$01
 sta    spr0y      ;nowa pozycja Y spriteu nr 0
 jmp    Draw
NoUp:
 tya
 and    #%00000100 ;DOWN
 beq    NoDown
 lda    spr0y      ;obsługa naciśnięcia przycisku DOWN
 clc
 adc    #$01
 sta    spr0y      ;nowa pozycja Y spriteu nr 0
 jmp    Draw
NoDown:
 tya
 and    #%00000010 ;LEFT
 beq    NoLeft
 lda    spr0x      ;obsługa naciśnięcia przycisku LEFT
 sec
 sbc    #$01
 sta    spr0x      ;nowa pozycja X spriteu nr 0
 jmp    Draw
NoLeft:
 tya
 and    #%00000001 ;RIGHT
 beq    Start
 lda    spr0x      ;obsługa naciśnięcia przycisku RIGHT
 clc
 adc    #$01
 sta    spr0x      ;nowa pozycja X spriteu nr 0
Draw:
 jsr    VBlank     ;czekanie aż PPU będzie gotowy
 lda    #$07       ;numer strony pamięci z właściwościami spriteów
 sta    $4014      ;wyświetlenie (odświeżenie) spriteów
 jmp    Start
Główna pętla programu z obsługą przycisków joypada 1. Po naciśnięciu przycisku START na ekranie
zostaną wyświetlone 3 spritey. Spritem nr 0 można poruszać za pomocą przycisków kierunkowych
(góra, dół, lewo, prawo). Za główną pętlą programu umieszcza się m.in. różne procedury.
VBlank:
 bit    $2002
 bpl    VBlank
 rts
Procedura czeka, aż PPU zacznie odświeżać ekran (VBlank). Tylko wtedy można bezpiecznie
wykonywać operacje na pamięci graficznej VRAM. Zapobiega to pojawianiu się błędów (artefaktów)
w wyświetlanym obrazie.
HVScroll:
 lda    #$00
 sta    $2005 ;poziome przewijanie (Horizontal scroll)
 sta    $2005 ;pionowe przewijanie (Vertical scroll)
 rts
Procedura wyłącza przewijanie tła. Zapewnia to poprawne wyświetlanie górnej linii obrazu.
ReadJoy1:
 ldx    #$01
 stx    $4016
 dex
 stx    $4016
 ldx    #$08
ReadJoy1a:
 lda    $4016 ;odczyt stanu kolejnego przycisku (kolejność: A, B, SELECT, START, UP, DOWN, LEFT, RIGHT)
 ror    A
 rol    joy1  ;zapis stanu przycisku jako wartości bitu (1 = przycisk naciśnięty)
 dex
 bne    ReadJoy1a
 rts

ReadJoy2:
 ldx    #$01
 stx    $4016
 dex
 stx    $4016
 ldx    #$08
ReadJoy2a:
 lda    $4017 ;odczyt stanu kolejnego przycisku (kolejność: A, B, SELECT, START, UP, DOWN, LEFT, RIGHT)
 ror    A
 rol    joy2  ;zapis stanu przycisku jako wartości bitu (1 = przycisk naciśnięty)
 dex
 bne    ReadJoy2a
 rts
Procedury zapisują stan przycisków joypada 1 i joypada 2 do zmiennej.
DrawBgText:
 stx    ptr.lo   ;młodszy bajt adresu tekstu ASCIIZ
 sty    ptr.hi   ;starszy bajt adresu tekstu ASCIIZ
 sta    var1     ;numer wiersza
 lsr    A
 lsr    A
 lsr    A
 tax
 beq    DrawBgText1
 tay
 lda    var1
DrawBgText0:
 sec
 sbc    #$08
 dey
 bne    DrawBgText0
 sta    var1
 tya
DrawBgText1:
 ldy    var1
 beq    DrawBgText3
DrawBgText2:
 clc
 adc    #$20
 dey
 bne    DrawBgText2
DrawBgText3:
 clc
 adc    var0     ;numer kolumny
 tay
 txa
 clc
 adc    #$20
 tax
;Ładowanie tekstu ASCIIZ
 jsr    VBlank   ;czekanie aż PPU będzie gotowy
 lda    $2002    ;resetowanie PPU
 txa             ;starszy bajt adresu tablicy indeksów nr 0 [Name Table]
 sta    $2006
 tya             ;młodszy bajt adresu tablicy indeksów nr 0 [Name Table]
 sta    $2006
 ldy    #$00
DrawBgText4:
 lda    [ptr.lo],Y  ;odczyt kolejnego znaku z tekstu ASCIIZ
 beq    DrawBgText5
 sec
 sbc    #$20
 sta    $2007    ;zapis indeksu tła (numeru patternu) do pamięci VRAM
 iny
 jmp    DrawBgText4
DrawBgText5:
;Czyszczenie właściwości tła
 lda    $2002    ;resetowanie PPU
 lda    #$23     ;starszy bajt adresu tablicy właściwości nr 0 [Attribute Table]
 sta    $2006
 lda    #$C0     ;młodszy bajt adresu tablicy właściwości nr 0 [Attribute Table]
 sta    $2006
 jsr    HVScroll ;wyłączenie przewijania tła
 ldx    #$3F     ;liczba bajtów do wyczyszczenia
DrawBgText6:
 sta    $2007    ;zapis zerowych ($00) właściwości tła do pamięci VRAM
 dex
 bpl    DrawBgText6
 rts
Procedura wyświetla dowolny tekst ASCIIZ (zakończony bajtem $00) jako tło (maksymalnie 255 znaków
+ bajt $00). Rozdzielczość obrazu konsoli NES w trybie PAL, wynosi 256x240 pikseli. Obraz tła jest po-
dzielony na rastry o wymiarach 8x8 pikseli. Każdy pattern z tablicy [Pattern Table] dla tła, zajmuje 1 taki
raster i reprezentuje 1 znak ASCII. Stąd na ekranie zmieści się maksymalnie 960 znaków (30 wierszy
po 32 kolumny).
NMI:
IRQ: rti
Procedury obsługujące przerwania konsoli NES. Przerwanie NMI jest generowane przez PPU przy
odświeżaniu ekranu (VBlank); 50x/s w systemie PAL lub 60x/s w systemie NTSC. Przerwanie IRQ
może być wywołane instrukcją BRK lub sygnałem na linii IRQ procesora. Każda z tych procedur
musi być zakończona instrukcją RTI (powrót z przerwania).
Palette:
 .db    $0F,$06,$15,$26,$0F,$35,$36,$37,$0F,$39,$3A,$3B,$0F,$15,$3E,$3F ;paleta tła
 .db    $0F,$1C,$15,$02,$0F,$1C,$15,$02,$0F,$1C,$15,$02,$0F,$1C,$15,$02 ;paleta spriteów
Wartości kolorów palety tła i spriteów.
Background:
 .db    $80,$1F,$82,$20,$84 ;indeksy tła
Wartości indeksów tła (numery patternów z tablicy [Pattern Table] dla tła).
BgAttr:
 .db    %00000000 ;właściwości tła
Wartości właściwości tła.
Sprites:
;pozycja Y, numer patternu z tablicy [Pattern Table] dla spriteów, atrybuty, pozycja X
 .db    $50,$00,$00,$50 ;sprite 0
 .db    $13,$01,$00,$23 ;sprite 1
 .db    $30,$02,$00,$35 ;sprite 2
Wartości właściwości spriteów.
Text0:
 .db    "   Press START  ",0
Text1:
 .db    "NES is the best!",0
Dowolne teksty ASCIIZ do wyświetlenia jako tło.
 .bank  1
 .org   $FFFA ;adres 1-szego wektora
 .dw    NMI   ;procedura obsługująca przerwanie NMI
 .dw    RESET ;procedura obsługująca przerwanie RESET
 .dw    IRQ   ;procedura obsługująca przerwanie IRQ
Utworzenie banku kodu nr 1 (PRG-ROM) o pojemności 8 kB i ustawienie jego adresu na $FFFA.
Pod tym adresem znajduje się tablica wektorów przerwań. Każdy wektor to pełny (16-bitowy)
adres procedury, która obsługuje dane przerwanie.

Banki pamięci tworzone dyrektywą ".bank" mają pojemność 8 kB. Cały kod programu mieści się w 2 tak-
ich bankach, czyli zajmuje 16 kB pamięci PRG-ROM. W konsoli NES pamięć PRG-ROM jest podzielona
na 2 banki: 0 ($8000) i 1 ($C000), każdy po 16 kB. Gdy program zajmuje do 16 kB, to linia adresowa
A14 procesora nie jest podłączona do układu pamięci PRG-ROM. Dlatego nawet, jeśli procesor próbuje
odczytać dane z banku 1, to w rzeczywistości zawsze dokonuje odczytu z banku 0 (jest to tzw. mirroring).
Dzieje się tak, ponieważ adres danych do odczytu ustalają tylko linie adresowe A0-A13, które dla obu
banków mają jednakowe wartości, a o wyborze banku decyduje jedynie wartość odłączonej linii A14.
Dzięki temu tablica wektorów przerwań ($FFFA), zawsze znajduje się we właściwym miejscu i pod tym
samym adresem, niezależnie od liczby banków. Gdy program zajmuje do 32 kB, to linia adresowa A14
procesora musi być podłączona do układu pamięci PRG-ROM i wtedy jest dostępna cała przestrzeń
adresowa tej pamięci. Jeśli program zajmuje powyżej 32 kB, to dodatkowo stosuje się różnego rodzaju
mappery do przełączania większej niż 2, liczby banków pamięci PRG-ROM.
 .bank   2
 .org    $0000
 .incbin "main.chr"
Ostatni fragment kodu tworzy bank nr 2 i umieszcza w nim plik binarny z grafiką (CHR-ROM).
Zawiera on tablice patternów nr: 0 ($0000) i 1 ($1000) z grafiką dla tła i/lub dla spriteów.
Plik ten można bardzo łatwo utworzyć i edytować za pomocą programu "YY-CHR 0.97".

Dodatkowe materiały objaśniające sposób wyświetlania grafiki przez konsolę NES (Famicom),
znajdują się na tej stronie.

Po kompilacji całości powyższego kodu, otrzyma się plik wynikowy ".NES" o rozmiarze 24 kB (16 bajtów
nagłówka iNES, 16 kB PRG-ROM, 8 kB CHR-ROM). Po uruchomieniu tego pliku na emulatorze, w le-
wym-górnym rogu ekranu powinno pojawić się 5 patternów tła, a na środku ekranu powinien znajdować
się napis "Press START". Po naciśnięciu przycisku START joypada 1, napis powinien zmienić się na
"NES is the best!", a na ekranie powinny pojawić się 3 spritey (ponumerowane 0, 1 i 2). Spritem nr 0
powinno dać się poruszać za pomocą przycisków kierunkowych joypada 1 (góra, dół, lewo, prawo).