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).