Gruzińskie Noce, czyli Nerdy Nights po polsku [Tutorial o pisaniu gier]
Zjednoczeni bracia w Pegazus!
Według internetów to najlepszy poradnik tworzenia gier na NESa, autorstwa bunnyboya z Nintendo Age. Bierzcie i korzystajcie z tego wszyscy. Mam nadzieję, że komukolwiek się przyda.

Teksty wszystkich Gruzińskich Nocy powstawały przez ok rok na emu-nes. Główny materiał tłumaczyłem ja, ale merytoryczną wartość zawdzięczamy panu bunnyboy z nintendoage oraz kilku chłopa z rodzimych forów. Głównie byli to Dizzy9 i siudym, ale swoje ważne trzy grosze dorzucili także Berion, Daf, Helper, Koks i MWK (kolejność alfabetyczna).

Bunnyboy często opisuje trochę koślawo pewne rzeczy, które na naszym emu-nesie były zrobione na medal, dlatego poradnik nie jest zrobiony w skali 1:1. Są wstawki z forum, ominięte nie używane rzeczy i dodana literatura dla głodnych wiedzy. Wszystko za zgodą i poparciem autora.
Dobrych nocy!


Tydzień 1: Systemy liczbowe i podstawowe pojęcia w programowaniu

Systemy Liczbowe

Dziesiętny

Podstawą systemu dziesiętnego jest 10. Znaki w tym systemie przyjmują wartości od 0 do 9. Każde miejsce w liczbie jest 10 razy większe od sąsiada z prawej (i mniejsze 10x od sąsiedniej cyfry z lewej). 
Jeśli do 10 dostawisz zero z prawej wyjdzie 100, czyli 10 razy większa.
Jeśli od 10 usuniesz 0 z prawej to wyjdzie 1, czyli 10 razy mniejsza.
miejsce 100’ek    miejsce 10’ek    miejsce 1’ek
    0                        0                 1            = 001
    0                        1                 0            = 010
    1                        0                 0            = 100

Aby uzyskać wartość, jaką przedstawia liczba mnożymy znak (w dziesiętnym jest to cyfra 0-9) razy 10 do potęgi takiej, które miejsce zajmuje cyfra licząc od prawej. Kolejno:
1 (bo 10^0=1)
10 (bo 10^1=10)
100 (bo 10^2=100)
...

miejsce 100’ek    miejsce 10’ek    miejsce 1’ek
    3                        8                  0      = 3*100 + 8*10 + 0*1 = 380
    0                        4                  1      = 0*100 + 4*10 + 1*1 = 41


Binarny

W komputerach wszystko dzieje się na 2jkach, czyli binarnie, 2 w podstawie. To najprostsza rzecz jaką rozumie komputer; jak pstryczek od światła ON / OFF, 1/0.
System binarny (o podstawie 2) działa w ten sam sposób, co dziesiętny, tyle że jest mniej cyfr, bo 2 (a nie 10).
Jeśli do 10 (w dziesiętnym to 2) dostawisz zero z prawej wyjdzie 100 (w dziesiętnym to 4), czyli 2 razy większa.
Jeśli od 10 usuniesz 0 z prawej to wyjdzie 1, czyli 2 razy mniejsza..
 
Aby uzyskać wartość, jaką przedstawia liczba mnożymy znak (0 albo 1) razy 2 do potęgi takiej, które miejsce zajmuje cyfra licząc od prawej. Kolejno:
1(bo 2^0=1)
2(bo 2^1=1)
4(bo 2^2=4)
8(bo 2^3=8)

miejsce 8’ek    miejsce 4’ek    miejsce 2’ek    miejsce 1’ek
      0                  1                  0                    0          = 0*8 + 1*4 + 0*2 + 0*1 = 4
      1                  1                  1                    1          = 1*8 + 1*4 + 1*2 + 1*1 = 15

NES działa na systemie 8 bit, co oznacza, że liczby na których operuje mają 8 binarnych(dwójkowych) cyfr długości. Przykłady poniżej:

Binarny        Dziesiętny
00000000 =  0
00001111 =  15
00010000 =  16
10101010 =  170
11111111 =  255

Po czasie zaczniesz szybko odczytywać liczby binarne, a co najmniej rozpoznawać pewne schematy. Jak widzisz bajt może mieć tylko wartości 0 – 255. Dla większych liczb, musisz użyć dwóch, lub więcej bajtów. No i nie ma liczb ujemnych, ale o tym później.


Szesnastkowy

Heksadecymalny / Hex ma w podstawie 16, więc każda z cyfr przyjmuje wartości 0 – 15.
Problem we wstawieniu wartości 10 i więcej do jednego znaku rozwiązano literami, zamiast cyfr:

Dziesiętny   Hex
    0       =    0
    1       =    1
    9       =    9
   10      =    A
   11      =    B
   12      =    C
   14      =    D
   14      =    E
   15      =    F

Każde miejsce cyfry(jednego znaku) w liczbie to kolejna potęga 16tki. Kolejno od prawej:
1(bo 16^0=1)
16(bo 16^1=16)

miejsce 16'ek   miejsce 1'ek
     6                    A          = 6*16 + A(10)*1 = 106
     1                    0          = 1*16 +     0*1 = 16

Hex jest częściej używany, bo jest o wiele szybszy w zapisie, niż binarny. 8 cyfr liczby binarnej wymienia się na dwie cyfry szesnastkowej.
Binarny        01101010
podziel         |     |
w połowie      /       \
               0110     1010
na             |         |
hex            6         A
               |         |
i              \        /
złóż               6A


01101010 = 6A


Kilka przykładów:

Binarny       Hex   Dziesiętny
00000000 = 00  = 0
00001111 = 0F  = 15
00010000 = 10  = 16
10101010 = AA  = 170
11111111 = FF  = 255

Do łatwego przechodzenia między tymi systemami możesz użyć kalkulatora z Windowsa na widoku programisty przełączając między Hex, Dec i Bin.

Żeby umożliwić rozpoznanie w jakim systemie zapisana jest liczba dopisuje się dodatkowy znak. Binarny jest często z prefixem %, np. %00001111. Hex ma $, czyli $2A. Jest wiele konwencji, np. Dopisek na końcu do binarnego b, czyli 00001111b a do hex’a h, czyli 2Ah.

NES ma 16sto bitową szynę adresową (szegóły później), więc ma dostęp do 2^16 bajtów pamięci. 16 binarnych cyfr zmienia w 4 hex’owe znaki, więc typowy adres wygląda jak $8000, $FFFF i $4017.


Podstawowe pojęcia w programowaniu

Wszystkie języki programowania mają trzy podstawowe pojęcia. Są nimi instrukcje, zmienne I przepływ sterowania. Jeśli brakuje czegoś z tych trzech – nie jest to już prawdziwy język programowania. Takim przykładem jest HTML, który nie ma określonego przepływu sterowania, więc nie jest językiem.

Instrukcje
Instrukcje to najmniejsze komendy jakie wykonuje processor. Na raz występuje jedna tylko instrukcja i są wykonywane jedna po drugiej. Procesor NES’a ma tylko 56 instrukcji. Przeważnie około 10 z nich będzie używanyh ciągle i minimum 10 z nich kompletnie zignorujemy. Przykładami może być odawanie, wczytywanie liczby, czy porównywanie zmiennej do zera.

Zmienne
Zmienna to miejsce gdzie przechowywane są dane, które mogą być zmieniane (edytowane).Przykładem może być pionowa pozycja Mario na ekranie. Może być zmieniana w każdej chwili podczas gry. Zmienne w kodzie źródłowym mogą mieć takie nazwy jakie im nadasz, więc coś koło MarioPionPozycja

Przepływ sterowania
Normalnie twoje instrukcje lecą w kolejności jedna po drugiej. Czasem będziesz chciał użyć innej części kodu w zależności od zmiennej. Będzie to wyrażenie przepływu sterowania, które zmieni normalny przepływ twojego programu. Przykładowo, kiedy Mario spada, skocz do części kodu, która sprawdza, czy już dotknął podłoża.


Literatura uzupełniająca:
Oryginał [eng]:
http://www.nintendoage.com/forum/messageview.cfm?catid=22&threadid=4147
« Ostatnia zmiana: Maja 26, 2016, 01:39:17 wysłana przez 1990in1 »

Odp: Gruzińskie Noce, czyli Nerdy Nights po polsku [Tutorial o pisaniu gier]
« Odpowiedź #1 dnia: Maja 25, 2016, 13:13:12 »
Tydzień 2: Architektura NES i troszkę o grafice

Architektura NES

KB - Kilobajt to Kilo bajtów, czyli tysiąc. No ciut ponad: 1KB = 1024 B. Komputer liczy na dwójkach, stąd nierówna końcówka - 2^10 = 1024. Co innego znaczy Kb, a co innego KB. Kb to kilobity (mniejsze). Dzieląc Kb (bity, czyli mniejsze) przez 8 o trzymasz tyle samo w jednostce KB (bajty, większe). Tak jak w prędkości internetu: możesz mieć łącze 16Mb/s które jest równe 2MB/s.
 
ROM - Read Only Memory,  przetrzymuje dane, które nie mogą być zmieniane. Tu zapisany jest kod gry albo grafika.

RAM - Random Access Memory, przetrzymuje dane które mogą być odczytywane i zapisywane.  Po odłączeniu napięcia chip jest opróżniany, by podtrzymać jego treść można użyć baterii.

PRG - Program memory, pamięć na kod gry.

CHR - Character memory, pamięć na grafikę

CPU - Central Processing Unit, główny procesor

PPU - Picture Processing Unit, chip graficzny

APU - Audio Processing Unit, chip audio znajdujący się wewnątrz CPU


Opis systemu

NES ma w sobie CPU oparty na procesorze 6502 z APU i obsługą kontrolera na jednym chipie oraz PPU odpowiedzialnym za grafikę na osobnym chipie. Twój kod odczytywany jest przez CPU i wysyła rozkazy do APU i PPU. Famiklony NOAC (NES On A Chip) jak większość słabych pegazusów i wszystkie "nowe" mają wszystko to w jednym zamknięte (nazywany glutem, ptasim mleczkiem itp.)




Są tylko 2KB RAMu dla CPU do przechowywania zmiennych oraz drugie 2KB dla PPU do utrzymywania dwóch ekranów grafiki tła. Niektóre carty rozszerzają pamięć CPU Working RAMem. Jeśli trzeba zapisać stan gry odbywa się to właśnie na WRAMie, którego stan utrzymuje bateria w cartridge'u. Mało które carty rozszerzają pamięć PPU do utrzymania czterech ekranów tła na raz - nie jest to stosowane często. Przez resztę tutoriala nie będziemy używać ani WRAMu ani czteroekranowego RAMu.

Każdy cart NESowy ma co najmniej trzy chipy. Jeden przechowuje kod programu (PRG), drugi przechowuje grafikę (CHR) a ostatni to blokada. Układ scalony od grafiki może być pamięcią RAM, a nie ROM. Oznacza to, że kod programu/gry może kopiować z PRG ROM do CHR RAM. Nie mniej jednak sam PRG jest zawsze pamięcią tylko do odczytu - ROM. Jest też opcja umieszczania CHR wewnątrz ROMu PRG, więc fizycznie jedna kostka mniej.

Kilka słów o CPU

CPU to 8-bitowy, zmodyfikowany procesor 6502. Podobny do stosowanych w Apple 2, Atari  2600, C64 i wielu innych systemach. W czasie, gdy tworzono Famicoma procesor ten był za słaby na komputer, ale do konsoli nadawał się doskonale.

CPU ma 16-bitową szynę adresową, mającą dostęp do 64KB pamięci. 2^16 = 65536, czyli 64KB. Składa się na to pamięć 2KB RAMu CPU, porty do PPU/APU/kontrolerów, WRAMu (jeśli jest w na cartridge'u) oraz 32KB na PRG ROM. Szesnastobitowe adresy są zapisane w hexie (systemie szesnastkowym), więc składają się z czterech znaków, które poprzedza symbol $. Przykładowo: wewnętrzny RAM jest pomiędzy $0000 - $0800. $0800 = 2048 czyli 2KB. 32KB szybko stały się niewystarczające dla gier, dlatego zaczęto stosować mappery. Mappery mogą podmieniać banki pamięci kodu PRG albo grafiki CHR. Na przykład mapper MMC3 pozwala na 512KB dla PRG i 256KB dla CHR. Nie ma limitów teoretycznych jeśli chodzi o rozmiary pamięci w mapperach. Najczęściej występujące to 128KB dla PRG i 64KB dla CHR.


Kilka słów o PPU

PPU w NESie to chip odpowiedzialny za wyświetlanie grafiki. Obsługuje wewnętrzny RAM na sprite'y i paletę kolorów. Jeden RAM znajdujący się na płycie głównej konsoli utrzymuje tło, a wszystkie grafiki wyświetlane na bieżąco są brane z carta (pamięci CHR).

Twój program nie jest odtwarzany przez PPU. PPU zawsze wykonuje tę samą sekwencję wyświetlania. Możesz jedynie ustawić takie opcje jak kolory i przewijanie obrazu (scrolling). PPU przetwarza jedną linię obrazu na raz. Najpierw pobierane są sprite'y z pamięci CHR (tej na cartcie). Jeśli jest ich więcej niż 8 na jednej linii pozostałe są ignorowane. Przez to właśnie w niektórych grach jak np Super Dodge Ball obraz mruga, kiedy dużo dzieje się na ekranie. Po sprite'ach wyświetlane jest tło. Kiedy przelecą wszystkie linie obrazu nastaje moment, kiedy nie są wysyłana żadna grafika. Ta chwila nazywana jest VBlank'iem i jest to jedyny moment, kiedy mogą się dokonać jakieś zmiany w grafice. W systemie PAL czas powrotu działka elektronowego z powrotem na górę ekranu (czyli czas VBlank'a) trwa dłużej, niż w NTSC przez co istnieje więcej czasu na aktualizację grafiki. Niektóre gry i demka nie działają poprawnie w systemie NTSC poprzez różnice w czasie VBlank'a. Oba systemy NTSC i PAL mają rozdzielczość 256x240 pikseli, ale 8 górnych i 8 dolnych linii jest ucinane podczas wyświetlania NTSC przez co obraz ogranicza się do 256x224 pikseli. Telewizor może odciąć kolejne 0-8 linii, więc lepiej zachować drobny odstęp przed wyświetleniem ważnych rzeczy.

NTSC chodzi na 60Hz, PAL na 50Hz. Przez to gry pisane pod NTSC są spowolnione ruchowo i mają wolniej odtwarzane dźwięki.



Kilka słów o Grafice

Tiles'y
Cała grafika składa się z tilesów o wymiarach 8x8 pikseli. System tile'i oznacza mniejsze zapotrzebowanie na pamięć (która była droga w tamtych czasach) i zarazem czyni niemożliwymi takie rzeczy jak obrazki bitmapowe, czy grafika 3d - praktycznie nieosiągalne. By przejrzeć wszystkie tilesy z gry, pobierz Tile Molester i otwórz swój plik .nes. Przewiń w dół, aż graficzki zaczną wyglądać jak graficzki, nie śnieg. Tak, te małe tilesy są układane w grze tak, by tworzyły większe obrazki.

Sprite'y
PPU może pomieścić 64 sprite'y, czyli "duszki" czyli to co się porusza na ekranie jak np. Mario. Na jednej linii obrazu może być wyświetlane maksymalnie 8 sprite'ów, ew. kolejne są pomijane. Stąd bierze się mruganie obrazu, kiedy jest wyświetlanych za dużo sprite'ów na raz.

Tło
To widoczki przewijające się naraz w całości. Sprite'y mogą być wyświetlane przed tłem lub za nim. Ekran mieści 32x30 tilesów tła. Wewnętrznego RAMu starcza na utrzymanie dwóch takich ekranów. Podczas przewijania obrazu grafika jest aktualizowana poza obrazem, zanim zostanie wyświetlona na ekranie.

Tabele Szablonów (Pattern Tables)
Miejsca gdzie są przechowywane dane aktualnych tiles'ów. Znajduje się na ROMie lub RAMie na cartcie. Każda Tabela mieści 256 tilesów. Jedna jest odpowiedzialna za tła, druga za sprite'y. Wszystkie grafiki wyświetlane w danym momencie na ekranie muszą się w nich zawierać. Pattern Table zawiera połowę informacji potrzebną do wyświetlenia tile'a.

Tabele Atrybutów (Attribute Tables)
Te tabele zawierają informację o kolorach w kwadratach 2x2 tilesy. Oznacza to, że na obszarze 16x16pikseli mogą występować tylko cztery kolory wybrane z jednej palety. Nie da się użyć więcej, niż 4 kolory. Jeśli znajdziesz fragment na środku ekranu, gdzie np. tło ma więcej niż 4 kolorki w obszarze ramki 16x16 pixeli to znaczy, że patrzysz na złączenie kilku bloczków 16x16, albo występuje tam jakiś sprite, a nie tło.

Palety
Są dwie osobne palety kolorów: dla tła i dla spriteów. Każda z nich ma po 16 kolorów.

Wyświetlając tile na ekranie brany jest indeks koloru danego piksela z Pattern Table i Atribute Table.  Potem by określić już sam kolor sprawdzana jest Paleta kolorów.



O samej grafice jeszcze będzie, także nie przejmuj się jeśli nie rozumiesz wszystkiego od razu.

Literatura uzupełniająca:
Oryginał [ENG]:
http://www.nintendoage.com/forum/messageview.cfm?catid=22&threadid=4291

Dobry opis grafiki znajdziesz w wątku Siudyma [PL]:
http://www.forum.emunes.pl/index.php?topic=1590.0

Podręcznik ASM 6502 dla Atari [PL]:
http://atarionline.pl/biblioteka/materialy_ksiazkowe/Asembler%206502%20(v2).pdf

Trochę rozszerzony opis grafiki, rejestry PPU [ENG]:
http://web.textfiles.com/games/nesgfx.txt

Programowanie na NES'a w pigułce z NES DEV [eng]:
http://nesdev.com/NESprgmn.txt

Dorzut linków od siudyma:

http://badderhacksnet.ipage.com/badderhacks/index.php?option=com_content&view=article&id=270:the-nes-picture-processing-unit-ppu&catid=14:dr-floppy&Itemid=7

I dwa ciekawe linki, przejrzyscie opisane:

http://ludumdare.com/compo/2014/06/23/creating-my-first-nes-game/

http://www.tummaigames.com/blog/

Odp: Gruzińskie Noce, czyli Nerdy Nights po polsku [Tutorial o pisaniu gier]
« Odpowiedź #2 dnia: Maja 25, 2016, 13:13:21 »
Tydzień 3: Assembler i pierwszy program


Assembler 6502

bit - najmniejsza jednostka danych. Ma dwie wartości: 1(on) albo 0(off), jak pstryczek od oświetlenia.

Bajt - 8 bitów złożonych razem daje Bajt. Można na nim zapisać liczbę od 0 do 255. Dwa Bajty to 16 bitów i mogą przedstawiać liczbę od 0 do 65535. W Bajcie bity są numerowane od prawej strony do lewej.

instrukcja - pojedyncza komenda wykonywana przez procesor. Instrukcje są wykonywane po kolei.


Konstrukcja Kodu

W assemblerze jest 5 głównych elementów. Niektóre z nich muszą mieć odpowiednie wcięcia, żeby assembler rozumiał je poprawnie.

Dyrektywy
Dyrektywy to polecenia, które wysyłasz assemblerowi, żeby na przykład umieszczał kod w pamięci. Zaczynają się od . czyli kropki i muszą być poprzedzone wcięciem. Tabulator, cztery spacje, dwie spacje... Przykładowa dyrektywa każe assemblerowi rozpocząć kod w miejscu pamięci $8000, która to jest w sekcji ROM'u gry:
    .org $8000

Etykiety
Etykieta wyrównana jest do lewej krawędzi i kończy się dwukropkiem : na końcu. Etykietą organizujesz sobie kod i czynisz go czytelniejszym. Assembler przetłumacza sobie etykietę na konkretny adres.
    .org $8000
MaFunkcja
Podczas gdy assembler znajdzie taki zapis przypisze MaFunkcja do $8000. Czyli jeśli np. w kodzie będzie zapis:
    STA MaFunkcja
Zmieni ją na:
    STA &8000

Mnemoniki/Opcode'y
JMP z ang. jump, czyli "skocz" do kodu, LDA jest od Load itd. i tak samo jak dyrektywy są poprzedzone wcięciem (tab, spacje). Dokładamy do przykładu mnemonik (opcode) który każe mu przeskoczyć do etykiety o nazywie MaFunkcja.
  .org $8000
MaFunkcja:
  JMP MaFunkcja

Operandy
Operandy to dodatkowe informacje dla mnemonik (opcode'ów). Mnemoniki (opcode'y) mają od jednej do trzech operand. W przykładzie operandem jest #$FF:
   .org $8000
MaFunkcja:
   LDA #$FF
   JMP MaFunkcja

Komentarze
Komentarze mają ci pomóc zrozumieć co się dzieje w kodzie. Jeśli wrócisz do fragmentu kodu pisanego dawno temu - uratują ci życie, a co najmniej wiele jego godzin. Nie wstawiaj ich wszędzie (no chyba, że na samym początku zabawy z programowaniem) bo zanieczyszczą ci notatki zamiast pomóc. Komentarze zaczynają się średnikiem ; i są zupełnie ignorowane przez assembler.
  .org $8000
MaFunkcja:        ;ładuje FF do akumulatora
  LDA #$FF
  JMP MaFunkcja

Powyższy kod tworzy nieskończoną pętlę, która będzie ładowała wartość $FF do akumulatora, wracała do MaFunkcja i ładowała $FF do akumulatora, wracała...


Opis Procesora 6502

6502 to 8-bitowy, procesor 6502 z 16-bitową szyną adresową. Ma dostęp do 64KB pamięci bez stosowania przerzucania pamięci. W NESie ta pamięć podzielona jest na RAM, PPU/Audio/Dostęp do kontrolera oraz ROM gry.
$0000-0800 - wewnętrzny RAM, 2KB w NESie
$2000-2007 - porty dostępu do PPU
$4000-4017 - porty dostępu do Audio i kontrolera
$6000-7FFF - opcjonalnie WRAM wewnątrz cardridge'a
$8000-FFFF - ROM gry na cartridge'u.


Opis Assemblera 6502

Język assembler dla 6502 zaczyna się 3 znakową instrukcją "opcodem". Jest 56 instrukcji z czego często używać będziesz ok 10 z nich. Wiele instrukcji będzie miała jakąś wartość za opcode'm, którą możesz zapisać w dziesiętnym lub heksie. Jeśli tej wartości nie poprzedza # to jest to adres. Czyli:
LDA #$05 to załaduj wartość 5, a
LDA $0005 to załaduj wartość, która przechowywana jest pod adresem $0005.


Rejestry 6502

Rejestr to miejsce wewnątrz procesora, które przechowuje wartość jakąś. 6502 ma trzy 8 bitowe rejestry i rejestr statusu, którego będziesz używał. Dodatkowe rejestry nie są opisane w tym poradniku.

Akumulator
Akumulator to główny 8-titowy rejestr służący to ładowania, przechowywania, porównywania i liczenia danych. Poniżej kilka najczęściej używanych operacji:
LDA #$FF  ;ładuje wartość hexową $FF (dziesiętnie to 256) do A
STA $0000 ;przechowuje kumulator w, internal RAM

Rejestr indeksu X
Rejestr indeksu X (X) to kolejny 8 bitowy rejestr, zazwyczaj używany do liczenia lub dostępu pamięci. W pętlach będziesz go używał do śledzenia ile razy już pętla była wykonywana, podczas używania A do przetwarzania danych. Niektóre częste operacje to:
LDX $0000 ;wczytaj wartość z miejsca pamięci $0000 do X
INX          ;zwiększ(INkrementuj) X   X = X + 1

Rejestr indeksu Y
Rejestr indeksu Y (Y) działa praktycznie tak samo jak X. Niektóre instrukcje (nie są opisane w tym poradniku) działają tylko z X, a nie z Y. Przykładowe zwykłe operacje:
STY $00BA ;przechowaj(STore) Y do miejsca pamięci $00BA
TYA          ;przenieś (transfer) Y do Akumulatora.

Rejestr Statusu
Rejestr Statusu zawiera flagę z informacją o ostatniej istrukcji. Na przykład podczas odejmowania możesz sprawdzić, czy wynikiem jest zero.


Zestaw instrukcji 6502

Często używane mnemoniki (opcode'y) Ładuj/Zapisz
LDA #$0A   ; Ładuj wartość 0A do akumulatora A
                ; liczbowa część mniemoniki (opcode'u) może być wartością lub też adresem
                ; jeśli wartość jest zerem, zostanie ustawiona flaga zera.

LDX $0000  ; Ładuj wartość spod adresu $0000 do rejestru indeksu X
                ; jeśli wartość jest zerem, zostanie ustawiona flaga zera.

LDY #$FF   ; Ładuj wartość $FF do rejestru indeksu Y
                ; jeśli wartość jest zerem, zostanie ustawiona flaga zera.

STA $2000  ; Zapisz wartość z akumulatora A do adresu $2000
                ; liczbowa część mniemoniki (opcode'u) musi być adresem

STX $4016  ; Zapisz wartość z X w $4016
                ;  liczbowa część mniemoniki (opcode'u) musi być adresem

STY $0101  ; Zapisz Y w $0101
                ;  liczbowa część mniemoniki (opcode'u) musi być adresem

TAX        ; Przenieś wartość z A do X
              ; jeśli wartość jest zerem, zostanie ustawiona flaga zera.

TAY        ; Przenieś A do Y
              ; jeśli wartość jest zerem, zostanie ustawiona flaga zera.
TXA        ; Przenieś X do A
             ; jeśli wartość jest zerem, zostanie ustawiona flaga zera.

TYA        ; Przenieś Y do A
             ; jeśli wartość jest zerem, zostanie ustawiona flaga zera.


Często używane mnemoniki (opcode'y) matematyczne
ADC #$01   ; Dodaj z przenoszeniem
                ; A = A + $01 + przenoszenie
                ;  jeśli wartość jest zerem, zostanie ustawiona flaga zera.

SBC #$80   ; Obejmij z przenoszeniem
                ; A = A - $80 - (1 - przenoszenie)
                ;  jeśli wartość jest zerem, zostanie ustawiona flaga zera.

CLC        ; Wyczyść flagę przeniesienia w rejestrze statusu
             ; zazwyczaj powinno być to wykonane przed dodawaniem (ADC)

SEC        ; Ustaw flagę przeniesienia w rejestrze statusu
             ; zazwyczaj powinno być to wykonane przed odejmowaniem (SBC)

INC $0100  ; Inkrementuj(zwiększ) wartość w adresie $0100
                ; jeśli wartość jest zerem, zostanie ustawiona flaga zera.

DEC $0001  ; Dekrementuj(zmniejsz) $0001
                ; jeśli wartość jest zerem, zostanie ustawiona flaga zera.

INY        ; INkremenruj rejestr Y
             ; jeśli wartość jest zerem, zostanie ustawiona flaga zera.

INX        ; INkremenruj rejestr X
             ; jeśli wartość jest zerem, zostanie ustawiona flaga zera.

DEY        ; DEkrementuj Y
             ; jeśli wartość jest zerem, zostanie ustawiona flaga zera.

DEX        ; DEkrementuj X
             ; jeśli wartość jest zerem, zostanie ustawiona flaga zera.

ASL A      ; Przesunięcie bitowe w lewo
              ; Przesuń wszystkie bity o jedno miejsce w lewo
              ; jest to mnożenie razy 2
              ;  jeśli wartość jest zerem, zostanie ustawiona flaga zera.

LSR $6000  ; Przesunięcie bitowe w prawo
                ; Przesuń wszystkie bity o jedno miejsce w lewo
                ; jest to dzielenie przez 2
                ;  jeśli wartość jest zerem, zostanie ustawiona flaga zera.

Często używane opcode'y porównujące
CMP #$01   ; Porównaj A do wartości $01
                ; wykonuje dzielenie, ale nie podaje wyniku
                ; w zamian możesz sprawdzić rejestr statusu:
                ; równy, mniejszy, czy większy

CPX $0050  ; Porównaj X do wartości spod adresu $0050

CPY #$FF   ; Porównaj Y do wartości $FF


Często używane opcode'y przepływu wiadomości
JMP $8000  ; skocz do $8000, kontynuuj odtwarzanie kodu w tym miejscu

BEQ $FF00  ; Odgałęzienie gdy równe, kontynuuj odtwarzanie kodu w tym miejscu
           ; najpierw powinieneś wykonać CMP, które wyczyści lub ustawi flagę zero
           ; następnie BEQ sprawdzi flagę zero
           ; jeśli zero jest ustawione (wartości są równe) kod skoczy do $FF00 i będzie kontynuowany od tamtego miejsca
           ; jeśli zero jest wyczyszczone (wartości nie są równe) nie ma skoku, kod jest kontynuowany poprostu w kolejnej linijce

BNE $FF00  ; Odgałęzienie jeśli nie równe - odwrotność powyższego, opposite above, skok wykonuje się, gdy flaga zero jest wyczyszczona


Struktura kodu

Wstęp
Ta część będzie zawierać sporo informacji, bo od razu ustawimy wszystko, żeby uruchomić pierwszy program na NES'a. Część kodu możesz po prostu wkleić, a co od czego jest dokładnie opisane będzie później. Głównym celem jest odpalenie w ogóle pierwszego programu.

Nagłówek iNES
16 bajtowy nagłówek przedstawia emulatorowi / konsoli podstawowe informacje o mapperze, pionowym/poziomym mirroringiem obrazu i rozmiarach PGR i CHR. Możesz te informacje wstawić na samym początku swojego pliku asm.
  .inesprg 1   ; 1x 16KB bank kodu PRG
  .ineschr 1   ; 1x 8KB bank danych CHR
  .inesmap 0   ; mapper 0 = NROM, bez zmian banków
  .inesmir 1   ; mirroring tła (pionowy)

Bankowanie
NESASM grupuje wszystko w 8KB banki kodu i 8KB banki grafiki. By wypełnić 16KB miejsca w PRG potrzebne są 2 banki. Numeracja zaczyna się od zera (czyli 0 jest pierwszy, a 1 to drugi). Musisz wskazać assemblerowi w którym miejscu w pamięci ma się zaczynać każdy z poszczególnych banków.
  .bank 0
  .org $C000
; i dalej trochę kodu

  .bank 1
  .org $E000
; tu też dalej kod

  .bank 2
  .org $0000
; a tu grafiki

Dodawanie plików
Pliki zewnętrzne są często używane przy grafikach i poziomach. Stosuje się do tego dyrektywę incbin. Dane nie będą od razu używane, ale jest to potrzebne do dopasowania pliku .NES z nagłówkiem iNES.
  .bank 2
  .org $0000
  .incbin "mario.chr"   ;includes 8KB graphics file from SMB1

Wektory
Są trzy specyficzne sytuacje, kiedy procesor NESa przerywa zwykły ciąg kodu i skacze do nowego miejsca. Wektory zapisane w pamięci ROM PGR wskazują te miejsca procesorowi. Dwie z trzech wektorów będą opisane w tym poradniku.
Wektor NMI - wykonywany raz na klatkę (wideo) jeśli jest włączony. PPU informuje procesor, że zaczyna się VBlank i jest gotowe do aktualizacji grafiki.
Wektor RESET - wykonywany zawsze podczas włączania i resetowania konsoli.
Wektor IRQ - wykonywany dla pewnych chipów w mapperach lub przerwań audio, nie będziemy o tym mówić.
Te trzy zawsze muszą pojawić się w kodzie we właściwej kolejności. Dyrektywa .dw określa słowo maszynowe (Data Word) (1 słowo = 2 bajty):
  .bank 1
  .org $FFFA     ;tu się zaczyna pierwszy z trzech wektorów
  .dw NMI        ;kiedy wystąpi NMI (raz na klatkę, jeśli jest włączony) procesor skoczy do etykiety NMI
  .dw RESET      ;podczas uruchomienia konsoli i resetowania procesor skacze do etykiety REDET
  .dw 0          ;zewnętrzne przerwania IRQ sobie darujemy w tym poradniku

Uzupełniając Program
Twoje pierwsze, fascynujące dzieło będzie wyświetlać ekran pełen... jednego koloru! Aby tego dokonać trzeba wprowadzić ustawienia PPU. Wpisuje się je w adres $2001. Ciąg 76543210 to kolejne numery bitów, od 7 do 0. Te 8 bitów bajta zapiszemy pod $2001.
PPUMASK ($2001)
76543210
||||||||
|||||||+- Skala Szarości (0: zwykłe kolory; 1: WSZYSTKIE palety
|||||||      z 0x30, wyświetlać będą wszystko na czarno - biało)
|||||||      zwróć uwagę, że podkreślenie kolorów wciąż działa, nawet jeśli szarość jest włączona!
||||||+-- Wyłącz ucinanie 8 pixeli tła z lewej strony ekranu
|||||+--- Wyłącz ucinanie 8 pixeli sprite'ów z lewej strony ekranu
||||+---- Włącz renderowanie tła
|||+----- Włącz renderowanie sprite'ów
||+------ Podkreśl czerwony (i przygaś pozostałe kolory)
|+------- Podkreśl zielony (i przygaś pozostałe kolory)
+-------- Podkreśl niebieski (i przygaś pozostałe kolory)
W tym programie bity 7, 6, 5 zostaną użyte do ustawienia koloru ekranu:
  LDA %10000000   ;podkreśl niebieski
  STA $2001
Forever:
  JMP Forever     ;nieskończona pętla

Podsumowując
http://www.nespowerpak.com/nesasm/background.zip
http://www.nespowerpak.com/nesasm/NESASM3.zip
http://www.the-interweb.com/serendipity/exit.php?url_id=627_id=90
http://www.the-interweb.com/serendipity/exit.php?url_id=627&entry_id=90
Pobierz i wypakuj background.zip. Cały ten powyższy kod jest w pliku background.asm. Upewnij się, że plik mario.chr i background.bat są w tym samym folderze co NESASM3, potem dwuklik w background.bat. Włączy się NESASM3 i powinien utworzyć background.nes. Otwórz plik NES w FCEUXD SP by zobaczyć swoje kolorowe tło! Zedytuj background.asm żeby zmienić intensywność bitów 7-5 w celu zmiany tła na zielony lub czerwony.

Możesz zacząć Dubug... (z menu Tools w FCEUXD SP) żeby zobaczyć tok swojego kodu. Wciśnij Step Into, wybierz Reset z NES menu, później klikaj Step Into by odtwarzać instrukcje po kolei. Z lewej są adresy pamięci, następny jest opcode w hex, który aktualnie odtwarza 6502. Zajmuje to 1 - 3 bajty. Dalej jest kod, który pisałeś z pominiętymi komentarzami i etykietami przetłumaczonymi na adresy. Górna linijka to instrukcja, która będzie wykonywana jako następna. W sumie jak dotąd nie ma za dużo kodu, ale później debugger przyda się dużo bardziej.

Literatura uzupełniająca:
Oryginał [ENG]:
http://www.nintendoage.com/forum/messageview.cfm?catid=22&threadid=4440

Dodatek od siudyma:

http://wyslijto.pl/plik/rpldzkbm8c

Odp: Gruzińskie Noce, czyli Nerdy Nights po polsku [Tutorial o pisaniu gier]
« Odpowiedź #3 dnia: Maja 25, 2016, 13:13:38 »
Tej nocy: umiesz napisać program na Pegasusa? To coś chociaż wyświetl!

Palety
Zanim wyświetlimy coś na ekranie, najpierw trzeba ustawić kolory palet. Istnieją dwie osobne palety, każda po 16 bajtów. Jedna używana do tła, druga do sprite'ów. Bajt w palecie odpowiada jednemu z 64 podstawowych kolorów, które NES jest w stanie wyświetlić. $0D jest zły i nie powinien być używany, bo telewizor może mieć problemy z pokazaniem 'czarniejszego, niż czarny', glitche itp.



Palety zaczynają się pod adresem PPU $3F00 oraz $3F10. By ustawić te adresy PPU używa portu $2006. Port musi być zapisywany "na raty" - dwukrotnie najpierw wyższy, później niższy bajt.

  LDA $2002    ; wczytaj status PPU by zresetować przerzutnik stanów wysoki / niski na wysoki
  LDA #$3F
  STA $2006    ; wpisz wysoki Bajt $3F10 w tamten adres
  LDA #$10
  STA $2006    ; wpisz niski Bajt $3F10 w tamten adres

Powyższy kod nakazuje PPU ustawić się w adresie $3F10. Teraz port danych PPU pod $2007 jest gotów do odbierania danych. Pierwszy zapis trafi do adresu, który ustawiliśmy ($3F10) a kolejne będą zwiększane już przez PPU automatycznie ($3F11, $3F12, $3F13) po każdym wczytaniu lub zapisaniu. Możesz dalej wprowadzać dane, a adresy będą wzrastać. Poniżej: jak ustawić pierwsze czery kolory w palecie:

  LDA #$32   ;kod dla jasnego niebieskawego
  STA $2007  ;wpisz do PPU $3F10
  LDA #$14   ;kod dla różowego
  STA $2007  ;wpisz do PPU $3F11
  LDA #$2A   ;kod dla zielonego
  STA $2007  ;wpisz do PPU $3F12
  LDA #$16   ;kod dla czerwonego
  STA $2007  ;wpisz do PPU $3F13

Możesz w ten sposób wypełnić resztę palety. Ale na szczęście jest szybsza metoda. Najpierw możesz użyć dyrektywy .db do przechowania danych:
PaletteData:

  .db $0F,$31,$32,$33,$0F,$35,$36,$37,$0F,$39,$3A,$3B,$0F,$3D,$3E,$0F  ;dane do palety tła
  .db $0F,$1C,$15,$14,$0F,$02,$38,$3C,$0F,$1C,$15,$14,$0F,$02,$38,$3C  ;dane do palety sprite'ów

Potem używa się pętli, żeby skopiować te bajty do palety w PPU. Rejestr X jest używany jako indels do palety i zliczania ile razy pętla już się powtórzyła. Chcesz skopiować na raz obie palety, łącznie 32 bajty. Indeksując standardowo od zera byłyby to wartości 0-31, ale na następnym pętla ma się zakończyć, więc będą to wartości od 0 do 32 (łącznie 33).

   LDX #$00                      ; zaczynamy od 0
LoadPalettesLoop:
   LDA PaletteData, x          ; wczytaj dane spod adresu (PaletteData + wartość x)
                                       ; 1. raz w pętli załaduje PaletteData+0
                                       ; 2. raz w pętli załaduje PaletteData+1
                                       ; 3. raz w pętli załaduje PaletteData+2
                                       ; i tak dalej
   STA $2007                     ; wpisz do PPU
   INX                               ; X = X + 1
   CPX #$20                      ; Porównaj X do hexa $20, dziesiętnie 32
   BNE LoadPalettesLoop     ; Odgałąź (z powrotem) do LoadPalettesLoop jeśli porównanie jest różne od zera
                                       ; jeśli porównanie było równe 32, kontynuuj kod poniżej

Kiedy ten fragment kodu się wykona i przeleci już cała pętla, mamy gotową paletę kolorów. Jeden bajt, albo też i całość palety można zmieniać podczas trwania programu.
Sprite'y
Wszystko co się porusza osobno od tła jest stworzone ze sprite'ów. Sprite to tile złożony 8x8 pikseli, który PPU może wrzycić w dowolnym miejscu na ekranie. Zazwyczaj obiekty składają się z kilku sprite'ów obok siebie. Przykładowo duży Mario albo Bowser. PPU starcza wewnętrznej pamięci na 64 sprite'y. To osobna pamięć, oddzielna od całej pozostałej pamięci wideo i nie może być rozszerzona.
DMA Sprite'ów
Najszybszą i najłatwiejszą metodą na przeniesienie twoich sprite'ów do pamięci sprite'ów jest DMA(ang. direct memory access). Oznacza to skopiowanie bloku RAMu z CPU do pamięci sprite'ów PPU. Do tego celu zazwyczaj używa się pamięci RAM z płyty głównej $0200-02FF. By rozpocząć transfer muszą zostać wpisane dwa bajty do portów PPU:

  LDA #$00
  STA $2003  ; ustaw niski bajt (00) adresu RAM
  LDA #$02
  STA $4014  ; ustaw wysoki bajt (02) adresu RAM, rozpocznij transfer.

W momencie drugiego zapisu DMA rozpoczyna się automatycznie. Zostaną skopiowane dane wszystkich 64 sprite'ów. Tak jak wszystkie aktualizacje grafiki, tę operację też trzeba wykonać podczas VBlank'a, dlatego też trafi on do sekcji kodu NMI.


Dane Sprite'ów
sprite potrzebuje 4 bajtów danych na swoją pozycję i informację o tile'u w tej kolejności:
1 - Miejsce Y - pionowa pozycja sprite'a na ekranie. $00 to szczyt ekranu. Cokolwiek ponad $EF ($F0, $F1...) jest poza dolną granicą ekranu.

2 - numer tile'a - numer (0 - 255) grafiki, która ma być wzięta z Pattern Table (Tabeli Szablonów)

3 - Atrybuty - bajt przedstawia kolor i informację o wyświetlaniu:
  7 6 5 432 1 0
  | | |         | |
  | | |        + +--- Paleta kolorów dla sprite'a. Wybierz zestaw czterech spośród 16 kolorów.
  | | |
  | | +------------- Priorytet (0: przed tłem; 1: za tłem)
  | +--------------- obróć sprite'a poziomo
  +----------------- obróć sprite'a pionowo

4 - Miejsce X - pozioma pozycja sprite'a na ekranie. $00 jest z lewej, cokolwiek ponad $F9 jest poza ekranem.

Te 4 bajty są powtarzane 64 razy (po jednym zestawie na jednego sprite'a) by wypełnić 256 bajtów pamięci dla sprite'ów. Jeśli chcesz edytować sprite'a 0, możesz zmienić bajty $0200-0203. Sprite 1 to $0204-0207, sprite 2 to $0208-020B, itd.


Włączanie NMI/Sprite'ów
Port PPU $2001 jest znów używany do włączania sprite'ów. Ustawiając bit 4 na 1 sprawimy, że się pojawią. NMI też musi być włączone, więc DMA Sprite'ów będzie odpalone i sprite'y będą kopiowane co każdą klatkę. Dzieje się to w porcie $2000 PPU. Pattern Table 0 też jest zaznaczone do wybierania sprite'ów z niego. Tło będzie pochodziło z Pattern Table 1 kiedy dodamy je później.

  PPUCTRL ($2000)
  76543210
  | ||||||
  | ||||++- Adresy nametable'i
  | ||||      (0 = $2000; 1 = $2400; 2 = $2800; 3 = $2C00)
  | |||+--- zwiększanie adresu VRAM co wczytanie/zapis PPUDATA przez CPU
  | |||       (0: zwiększ o 1, patterny będą wypisywane jeden po drugim w liniach poziomych)
  | |||       (1: zwiększ o 32, wypisywane "jeden pod drugim", czyli z góry na dół.)
  | ||+---- adres pattern table(tabeli szablonów) Sprite'ów 8x8 (0: $0000; 1: $1000)
  | |+----- adres pattern table Tła (0: $0000; 1: $1000)
  | +------ rozmiar Sprite'a(0: 8x8; 1: 8x16)
  |
  +-------- Wygeneruj NMI na początku
               vblank'a (0: off; 1: on)

Dodajmy trochę kodu, żeby ustawić sprite'a:
  LDA #$80
  STA $0200        ;ustaw sprite 0 w środku ($80) ekranu pionowo
  STA $0203        ;ustaw sprite 0 w środku ($80) ekranu poziomo.
  LDA #$00
  STA $0201        ;numer tile'a = 0
  STA $0202        ;paleta kolorów = 0, bez obracania

  LDA #%10000000   ; włącz NMI, sprite'y z Pattern Table(Tabeli Szablonów) 0
  STA $2000

  LDA #%00010000   ; bez podkreśleń kolorów (tło czarne), włączone sprite'y
  STA $2001


Podsumowując
http://www.nespowerpak.com/nesasm/sprites.zip
Pobierz i rozpakuj sprites.zip. Cały powyższy kod jest w pliku sprites.asm. Upewnij się, że sprites.asm, mario.chr i sprites.bat są w tym samym folderze co NESASM3, potem dwuklik w sprites.bat. Uruchomi to NESASM3 i powinno utworzyć sprites.nes. Otwórz .nes w FCEUXD SP żeby zobaczyć swoje dzieło! Tile o numerze 0 jest za głową i czapką Mario, widzisz? Edytuj -sprites.asm by zmienić położenie sprite'a (0 - 255), albo zmienić paletę kolorów (0 - 3).

Literatura uzupełniająca:
Oryginał [ENG]:
http://www.nintendoage.com/forum/messageview.cfm?catid=22&threadid=6082

Odp: Gruzińskie Noce, czyli Nerdy Nights po polsku [Tutorial o pisaniu gier]
« Odpowiedź #4 dnia: Maja 25, 2016, 13:13:53 »
Tej nocy: Pojedyncze sprite'y są dla leszczy, dorzućmy więcej! No i poruszajmy tym trochę używając pada.

Wiele Sprite'ów

Ostatnio był tylko jeden sprite, więc używaliśmy zaledwie kilku par LDA/STA żeby załadować jego dane. Tym razem będą 4 sprite'y na ekranie. Wykonywanie tylu wczyań/zapisów zajmuje sporo czasu i miejsca, więc zamiast tego użyjemy pętli podobnej do tej, która ustawiała wcześniej palety. Najpierw ustawiamy bajty używając dyrektywy .db:

sprite'y:

       ;pion tile    atr   poziom

  .db $80, $32, $00, $80   ;sprite 0

  .db $80, $33, $00, $88   ;sprite 1

  .db $88, $34, $00, $80   ;sprite 2

  .db $88, $35, $00, $88   ;sprite 3

Mamy 4 bajty na sprite w jednym wierszu. Bajty są we właściwej kolejności i łatwo je zmieniać. Są to tylko dane początkowe, kiedy program jest uruchomiony kopia w pamięci RAM może być zmieniana, żeby poruszać sprite'm.

Dalej potrzebujesz pętli, żeby skopiować dane do RAM'u. Ta pętla działa tak samo jak ta od ładowania palet z rejestrem X jako licznikiem pętli.

LoadSprites:
  LDX #$00                  ; zaczynamy od 0

LoadSpritesLoop:
  LDA sprites, x             ; załaduj dane z adresu (sprites + x)
  STA $0200, x             ; zapisz pod adresem RAM ($0200 + x)
  INX                          ; X = X + 1
  CPX #$10                  ; Porównaj X z hex'em $10, dziesiętnie 16
  BNE LoadSpritesLoop   ; Odgałąź do LoadSpritesLoop jeśli porównanie Nie było równe 0
                                 ; Jeśli porównanie było równe 16, kontynuuj kod dalej (nie wracaj do pętli)


Jeśli chcesz dodać więcej sprite'ów, możesz dostawić linijkę do sekcji srpite .db i zwiększyć wartość porównywania CPX. Odtworzy to pętlę więcej razy, przekopiuje więcej bajtów.

Kiedy sprite został załadowany do RAM'u możesz zmieniać tam jego dane.


Porty kontrolerów

Kontrolery są dostępne pod adresami pamięci $4016 i $4017. Najpierw musisz wpisać wartość $01 a potem $00 do portu $416. To rozkaże kontrolerowi, by załapał aktualne pozycje przycisków. Potem je wczytujesz spod $4016 dla I pada, $4017 dla II. Przyciski są wysyłane na raz w bicie 0. Jeśli bit zerowy ma wartość 0 - przycisk nie jest wciśnięty; jeśli bit zerowy ma stan 1 - przycisk jest wciśnięty.

Stan przycisków dla każdego z padów jest wczytywany w tej kolejności: A, B, Select, Start, Góra, Dół, Lewa, Prawa.

  LDA #$01
  STA $4016
  LDA #$00

  STA $4016     ; rozkaż obu kontrolerom by załapały stany przycisków
  LDA $4016     ; player 1 - A
  LDA $4016     ; player 1 - B
  LDA $4016     ; player 1 - Select
  LDA $4016     ; player 1 - Start
  LDA $4016     ; player 1 - Góra
  LDA $4016     ; player 1 - Dół
  LDA $4016     ; player 1 - Lewo
  LDA $4016     ; player 1 - Prawo

  LDA $4017     ; player 2 - A
  LDA $4017     ; player 2 - B
  LDA $4017     ; player 2 - Select
  LDA $4017     ; player 2 - Start
  LDA $4017     ; player 2 - Góra
  LDA $4017     ; player 2 - Dół
  LDA $4017     ; player 2 - Lewo
  LDA $4017     ; player 2 - Prawo


Instrukcja AND

Informacja o stanie przycisku jest wysyłana tylko w bicie zerowym, więc wygodnie by było wykasować pozostałe, zbędne bity. Każdy z ośmiu bitów jest ANDowany z bitem spod następnej wartości. Jeśli oba mają wartość 1, wynikiem instrukcji AND jest też jedynka. W każdym innym przypadku wynikiem jest 0.

0 AND 0 = 0

0 AND 1 = 0

1 AND 0 = 0

1 AND 1 = 1

Dla jakichś dwóch przypadkowych bajtów wygląda to tak:

       0 1 0 1 1 0 1 1
AND  1 0 1 0 1 1 0 1
---------------------
        0 0 0 0 1 0 0 1


Interesuje nas wyłącznie bit zerowy, więc czyścimy wszystkie pozostałe:

        01011011    dane z kontrolera
AND   00000001    ANDujemy z tym
----------------
        00000001    wynik (tylko pierwszy z prawej, bit 0 o wartości 1 ) pozostałe usunięta


Więc, aby usunąć zbędne bity podczas wczytywania kontrolerów trzeba dodać funkcję AND po każdym wczytaniu spod $4016 albo $4017:

  LDA $4016       ; player 1 - A
  AND #%00000001

  LDA $4016       ; player 1 - B
  AND #%00000001

  LDA $4016       ; player 1 - Select
  AND #%00000001


instrukcja BEQ

Instrukcja BNE (Branch if Not Equal) używana wcześniej  w pętlach do Odgałęzienia, jeśli Nie Równe ustawionej wartości. Tutaj w BEQ (Branch if Equal) wykonuje się odgałęzienie, jeśli wartość wynosi zero.

ReadA:

  LDA $4016            ; player 1 - A
  AND #%00000001  ; wykasuj wszystko prócz bitu 0
  BEQ ReadADone     ; odgałąź do ReadADone jeśli przycisk NIE jest wciśnięty (0)
                            ; tu dodajemy instrukcje jakie mają się wykonywać, kiedy przycisk JEST wciśnięty (1)
ReadADone:            ; Zakończona obsługa przycisku.


instrukcje CLC/ADC

Do naszego dema użyjemy kontrolera I gracza, by poruszyć sprite'm Mario. Żeby to zrobić musimy umieć dodawać wartości. ADC to dodawanie z przenoszeniem. Przed dodawaniem warto się upewnić, że przeniesienie jest wyczyszczone używając CLC. W przykładzie załadujemy pozycję sprite'a do A, wyczyścimy przeniesienie, dodamy jeden do wartości i wpiszemy wartość z powrotem jako pozycjię sprite'a":

  LDA $0203   ; wczytaj poziomą pozycję sprite'a (X)
  CLC         ; czyścimy flagę przeniesienia
  ADC #$01    ; A = A + 1
  STA $0203   ; zapisz poziomą pozycję sprite'a (X)


instrukcje SEC/SBC

By poruszyć sprite w przeciwnym kierunku potrzebne jest odejmowanie. SCB to odejmowanie z przeniesieniem. Tu dla odmiany ustawiamy flagę przeniesienia (SEC).

  LDA $0203   ; wczytaj pozycję sprite'a
  SEC         ; ustawiamy flagę przeniesienia
  SBC #$01    ; A = A - 1
  STA $0203   ; zapisz pozycję sprite'a


Podsumowując

http://www.nespowerpak.com/nesasm/controller.zip

Pobierz i rozpakuj controller.zip.  Powyższy kod znajuje się w pliku controller.asm.  Upewnij się, że pliki mario.chr oraz controller.bat są w tym samym folderze NESASM, potem dwuklik w controller.bat.  Włączy to NESASM i powinno utworzyć plik controller.nes.  Otwórz go przez FCEUXD SP żeby zobaczyć małego Mario.  Wciśnij przyciski A i B, żeby poruszyć sprite'm. Ruch będzie wykonywany raz na ramkę, lub 60 pikseli na sekundę w trybie NTSC.  Jeśli Mario się nie rusza, upewnij się, że kontrolery są normalnie ustawione w menu Config pod Input...  Jeśli wciśniesz oba przyciski na raz nic się nie powinno ruszać..

Spróbuj zedytować wartości ADC i SBC, żeby hydraulik ruszał się żwawiej.  Ekran ma tylko 256 pikseli szerokości, więc jeśli przesadzisz będzie skakał losowo! poza tym spróbuj edytować kod tak, żeby poruszać czterema sprite'ami na raz.

Na koniec spróbuj użyć d-pada zamiast przycisków. Lewo/prawo niech porusza sprite'ami pozioma, a góra/dół pionowo.


Literatura uzupełniająca:
Oryginał [ENG]:
http://www.nintendoage.com/forum/messageview.cfm?catid=22&threadid=7974

Podręcznik ASM 6502 dla Atari [PL]:
http://atarionline.pl/biblioteka/materialy_ksiazkowe/Asembler%206502%20(v2).pdf

Odp: Gruzińskie Noce, czyli Nerdy Nights po polsku [Tutorial o pisaniu gier]
« Odpowiedź #5 dnia: Maja 25, 2016, 13:13:59 »
Tej nocy: nauczmy się teraz wyświetlać nieruchome tło.

Tło
trzy cztery składniki potrzebne do generowania tła w NES'ie. Pierwszy to paleta kolorów tła, z której wybiera się kolory dla elementów tła na ekranie. Drugi to nametable mówiący o samym układzie kolorów układzie Tilesów na ekranie i układzie atrybutów, które zawierają tylko numery palet, nie "kolory", trzeci - attribute table - przypisuje kolory z palety do miejsc na ekranie. Ostatnim elementem są same grafiki, które chcemy wyświetlić.

Paleta Tła
Tak jak dla sprite'ów tak i dla tła jest osobna, 16-kolorowa paleta. W naszym poprzednim programie ładowaliśmy paletę, ale jeszcze nie była używana. Żeby zobaczyć te kolory można użyć PPU Viewer w FCEUXD SP. 

Nametable
Tak jak i sprite'y, tak i obrazy tła składają się z tile'ów 8x8 pikseli. Rozdzielczość obrazu to 32 x 30 tile'ów, czyli 256 x 240 pikseli. W systemie PAL wyświetlany jest pełen obraz. W NTSC ucinanych jest 8 górnych i 8 dolnych poziomych linii co daje po okrojeniu 256x224. Dodatkowo same telewizory mogą nie wyświetlać kilku górnych lub dolnych wierszy.
 
Jeden ekran pełen tile'ów tła nazywany jest nametable'm. NES posiada wewnętrzny RAM połączony z PPU, który starcza na dwa nametable. Będziemy używać tylko jednego obrazu w tym poradniku. Nametable będzie się zaczynał u nas w PPU od adresu $2000 i zajmować będzie 960 bajtów (32x30). Nametable można podejrzeć przez FCEUX SP w Nametable viewerze.

Tabele atrybutów (Attribute Tables)
Tabele atrybutów są prawdopodobnie najtrudniejszą rzeczą do zrozumienia, a poza tym ustanawiają wiele ograniczeń grafiki. Każdy z Nametable'i ma tabelę atrybutów, która ustala które kolory z palety będą określane w danej sekcji/części ekranu. Attribute Table jest przechowywana w tym samym wewnętrznym RAMie co Nametable. Będziemy używać tego, który zaczyna się w PPU pod adresem $23C0 ($2000+960). 

Po pierwsze ekran podzielon jestże na siatkę o kratkach 32x32 pixele, czyli 4x4 tile. Każdy bajt w Atribute Table ustala grupę kolorów (0-3) w palecie tła, która będzie używana w tym obszarze. Obszar ten jest dalej podzielony na cztery mniejsze kratki o wymiarach 2x2 tile. Dwa bity wchodzące w skład jednego bajta tabeli atrybutów są przypisane do każdej kratki 2x2.
To rozmiar jednego bloczku cegieł w SMB. To ograniczenie oznacza, że tylko 4 kolory (z jednej grupy kolorów) mogą być użyte w każdym z pikseli w kwadracie/obszarze tła o rozmiarach 16x16 pikseli. Sekcja z zieloną rurą w SMB nie może używać koloru czerwonego, bo już są wykorzystuje 4 kolory.

Rzut okiem na przykładowy obraz z SMB. Nałożona siatka 4x4 tile, na dole pokazane palety kolorów


Siatka ma 8 kwadratów w poziomie, więc będzie 8 bajtów atrybutów poziomo. Potem każdy z tych pól siatki jest podzielony na sekcje po 2x2 tile, żeby stworzyć bajt atrybutowy:


Żaden obszar z 16x16 pixeli nie może używać więcej niż 4 kolory. Żodyn! Dla czepliwych cwaniaków: chyba, że spojrzymy na obszar zahaczający o sąsiednią 'kratkę', albo pojawią się w nim sprite'y.



Wrzucanie danych
By ustawić grafikę tła, twoje dane muszą być określone w ROMie twym przy użyciu dyrektywy .db Tego nie ma w ROMie, tak się załącza ciąg danych w pliku źródłowym dla kompilatora. Co więcej, różne kompilatory używają różnych dyrektyw, np. ASM6 używa ".byte" zamiast ".db". Niektóre edytory graficzne tworzą te dane, ale tu zrobimy je ręcznie. Dla przykładu przerobimy tylko kilka linii.Używamy tego samego CHRa z SMB. Wpierwej definiujemy dane nametable'a, każdy wiersz grafiki dzielimy dla naszej wygody na dwa 16 bajtowe fragmenty:

nametable:

  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;wiersz 1
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;samo niebo ($24 = niebo)


  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;wiersz 2
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;samo niebo


  .db $24,$24,$24,$24,$45,$45,$24,$24,$45,$45,$45,$45,$45,$45,$24,$24  ;;wiersz 3
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$53,$54,$24,$24  ;;kilka górnych części cegieł


  .db $24,$24,$24,$24,$47,$47,$24,$24,$47,$47,$47,$47,$47,$47,$24,$24  ;;wiersz 4
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$55,$56,$24,$24  ;;kilka dolnych części cegieł


Potem określane są dane z attribute table. Każdy bajt obejmuje obszar 4x4 tile zatem potrzebnych nam będzie tylko 8 bajtów. W kodzie binarnym łatwiej jest edytować 2 bity na obszar 2x2 tile'i.
 
attribute:

.db %00000000, %00010000, %0010000, %00010000, %00000000, %00000000, %00000000, %00110000
 
 
I na koniec, używna jest ta sama paleta kolorów co w SMB:
palette:
.db $22,$29,$1A,$0F, $22,$36,$17,$0F, $22,$30,$21,$0F, $22,$27,$17,$0F
 
Tak jak przy poprzednim wczytywaniu palet, stosowana jest pętla do skopiowania określonej liczby bajtów z pamięci do PPU. Najpierw adres PPU jest ustawiany jako początek nametable w miejscu $2000. Potem kopiowane jest nasze 128 Bajtów danych tła. Następnie adres PPU jest ustawiany na początek tabeli atrybutów w miejscu #23C0 i kopiowanych jest 8 Bajtów.

LoadBackground:
LDA $ 2002             ; wczytaj stan PPU żeby zresetować wysoki/niski zatrzask
LDA #$20
STA $ 2006             ; zapisz wysoki bajt adresu $2000
LDA #$00
STA $ 2006             ; zapisz niski bajt adresu $2000
LDX #$00 ; zacznij w 0
LoadBackgroundLoop:
LDA background, x ; wczytaj dane z adresu (background + wartość x)
STA $ 2007 ; wpisz do PPU
INX ; X = X + 1
CPX #$80 ; Porównaj X do hex'a $80, dziesiętnie 128 - kopiowanie 128 Bajtów
BNE LoadBackgroundLoop ; Odgałąź do LoadBackgroundLoop jeśli porównanie wyszło różne od zera
                        ; jeśli porównanie było równe 128, kontynuuj niżej
 
 
 LoadAttribute:
LDA $ 2002             ; wczytaj stan PPU żeby zresetować wysoki/niski zatrzask
LDA #$23
STA $ 2006             ; zapisz wysoki bajt adresu $23C0
LDA #$C0
STA $ 2006             ; zapisz niski bajt adresu $23C0
LDX #$00 ; zacznij w 0

LoadAttributeLoop:
LDA attribute, x ; wczytaj dane z adresu (attribute + wartość w x)
STA $ 2007 ; zapisz do PPU
INX ; X = X + 1
CPX #$08 ; Porównaj X do hex'a $08, dziesiętnie 8 - kopiowanie 8 Bajtów
BNE LoadAttributeLoop
 
Ostanie zmiany to rozkazanie PPU by używał grafik Pattern Table 0 dla sprite'ów, a Pattern Table 1 dla tła.

LDA #%10010000 ;aktywuj NMI, sprite'y z Pattern 0, tłao z Pattern 1
STA $2000

Aktywuj renderowanie tła:
LDA #%00011110 ; aktywuj spritey, aktywuj tło
STA $2001
 
Wyzeruj pozycję przesunięcia ekranu:
 
LDA #$00
STA $2005
STA $2005
(zapis $2005 tylko zmienia pozycję przewinięcia ekranu (pierwszy zapis zmienia przesunięcie poziome, drugi pionowe)
 
Podsumowując:
http://www.nespowerpak.com/nesasm/background2.zip
Pobierz i rozpakuj background2.zip. Powyższy kod znajuje się w pliku background.asm.  Upewnij się, że pliki mario.chr oraz background.bat są w tym samym folderze NESASM, potem dwuklik w background.bat.  Włączy się NESASM i powinien utworzyć plik background.nes.  Otwórz go przez FCEUXD SP żeby zobaczyć tło. Zmień region w emulatorze na PAL, żeby ujrzeć cały ekran.
 
Wszystkie fragmenty tła jakich nie zapisałeś będą wciąż używać tile'a 0, który to jest numerem 0 w grafikach SMB. Spróbuj dodać więcej nametable'i i danych atrybutów do sekcji .db, potem zmień pętle tak, by kopiowały więcej Bajtów do pamięci RAM PPU. Możesz też spróbować zmieniać adres startowy w PPU dla nametable'i tabel atrybutów, by przesunąć je kilka wierszy niżej.



Literatura uzupełniająca:
Oryginał [ENG]:
Nerdy Nights week 6

Podręcznik ASM 6502 dla Atari [PL]:
http://atarionline.pl/biblioteka/materialy_ksiazkowe/Asembler%206502%20(v2).pdf

Odp: Gruzińskie Noce, czyli Nerdy Nights po polsku [Tutorial o pisaniu gier]
« Odpowiedź #6 dnia: Maja 25, 2016, 13:14:11 »
Tej nocy: Większość to organizacja kodu i struktura Twojej gry.
 
Zmienne
 Jak już mówiliśmy podczas Gruzińskiej nocy #1, zmienne są przechowywane w pamięci RAM i można je sobie zmieniać w dowolnym momencie. Dane Sprite'ów są w całości zmiennymi. Będziesz potrzebował kolejnych zmiennych do śledzenia wielu rzeczy, np. aktualnie zdobytych punktów itp. Żeby coś takiego zrobić, należy najpierw powiedzieć NESASM'owi w jakim miejscu w pamięci RAM ma przechowywać te zmienne. Robi się to dyrektywami .rsset oraz .rs.
 .rsset służy do określenia początkowego adresu dla zmiennych. Potem .rs rezerwuje miejsce dla nich. Zazwyczaj rezerwowany jest 1B(bajt) ale można to zmieniać. Za każdym kolejnym użyciem .rs adres rezerwacji rośnie kolejno, więc nie trzeba ustawiać kolejnego początku poprzez .rsset.
 
.rsset $0000 ;zacznij obstawiać zmienne od miejsca w pamięci 0
score1 .rs 1 ;ustaw punkty dla gracza 1 w miejscu $0000
score2 .rs 1 ;ustaw punkty dla gracza 2 w miejscu $0001
buttons1 .rs 1 ;ustaw dane pada gracza 1 w miejscu $0002
buttons2 .rs 1 ;ustaw dane pada gracza 2 w miejscu $0003


Jak już ustawisz adres dla konkretnej zmiennej, nie musisz znać jej adresu w pamięci. Możesz po prostu odnosić się do niego używając nazwy zmiennej. Możesz oczywiście dopisać kolejne zmienne do tych powyżej a assembler automatycznie nada im kolejne adresy w pamięci.


Stałe
Stałe to liczby, których nie zmieniasz. Ich zadaniem jest zwiększenie przejrzystości kodu. Przykładową stałą w Pong'u są zewnętrzne ściany. Musisz porównać pozycję piłeczki z miejscem ściany, żeby ją odbić. A że ściany się nie ruszają, więc całkiem dobrze by było opisać je jako stałe. Łatwiej jest porównać coś z LEWĄŚCIANĄ niż z $F6
Żeby zadeklarować stałą używamy znaku równości:

RIGHTWALL = $ 02   ; zrób coś, jeśli piłeczka zetknie się z którąś z tych ścian
TOPWALL = $ 20
BOTTOMWALL = $D8
LEFTWALL = $F6

 
Assembler znajdzie/podmieni tekst na odpowiednie wartości podczas budowania twojego kodu.
 

Podprogramy

W miarę zwiększania się twojego kodu dużo łatwiej będzie ci się poruszać po podprogramach (coś a la akapity z nazwą) niż używać kodu ciągnącego się z góry do dołu. Podprogramów możemy używać ile i jak nam się podoba, mogą być wywołane w dowolnym momencie. A tak to wygląda:

RESET:
  SEI ; wyłącz IRQs
  CLD ; wyłącz tryb dziesiętny

vblankwait1: ; pierwsze czekanie na vblank, by upewnić się, że PPU jest gotów
  BIT $2002
  BPL vblankwait1

clrmem:
  LDA #$FE
  STA $0200, x
  INX
  BNE clrmem

vblankwait2: ; drugie czekanie na vblank, by upewnić się, że PPU jest gotów
  BIT $2002
  BPL vblankwait2


Patrzaj pan, vblankwait jest użyty dwa razy, więc możnaby go określić przez podprogram. Najpierw kod vblankwait'a wyciągamy poza ciąg kodu:

vblankwait: ; wait for vblank
  BIT $2002
  BPL vblankwait

RESET:
  SEI ; wyłącz IRQs
  CLD ; wyłącz tryb dziesiętny

clrmem:
  LDA #$FE
  STA $0200, x
  INX
  BNE clrmem

A teraz jeszcze trzeba wywołać to, co wyciągneliśmy. Robimy to instrukcją JSR (Jump to SubRoutine).

RESET:
  SEI ; wyłącz IRQs
  CLD ; wyłącz tryb dziesiętny

  JSR vblankwait ;; skocz do pierwszego czekania na vblanka

clrmem:
  LDA #$FE
  STA $0200, x
  INX
  BNE clrmem

  JSR vblankwait ;; skocz do drugiego czekania na vblanka


Po zakończeniu się podprogramu, powinien wrócić do miejsca w którym został wywołany. A wraca przez instrukcję RTS(ReTurn from Subroutine).
 
     vblankwait: ; czekaj na vblank <--------
       BIT $2002
       BPL vblankwait
   ----- RTS
 /
|  RESET:
|    SEI ; disable IRQs
|    CLD ; disable decimal mode
|
|   JSR vblankwait ;;skocz do vblankwait #1 --/
|
 \--> clrmem:
         LDA #$FE
         STA $0200, x
         INX
         BNE clrmem

         JSR vblankwait ;; skacze do vblankwait jeszcze raz, potem wraca tu

 
Lepszy odczyt kontrolera
Potrafiąc już używać podprogramów możesz usprawnić sprawdzanie stanu kontrolera (pada). Poprzednio kontroler był odczytywany podczas przetwarzania. Podczas różnych stanów gry trzeba by wielokrotnie używać tego samego kodu odczytu stanu kontrolera. Upraszczamy to przez jeden podprogram. Zapisuje on dane konkretnego przycisku pod zmienną. A zmienna ta może być odczytywana później już bez potrzeby odczytywania całego kontrolera.


ReadController:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016
  LDX #$08
ReadControllerLoop:
  LDA $4016
  LSR A ; bit0 -> Przenieś
  ROL buttons ; bit0 <- Przenieś
  DEX
  BNE ReadControllerLoop
  RTS


Ten kod używa dwóch nowych instrukcji. LSR (Logical Shift Right) - bierze każdy jeden bit z A i przesuwa go o jedną pozycję w prawo. Bit7 (pierwszy z lewej) jest uzupełniany zerem, a bit0 jest przesunięty do Carry Flag (czyli Flagi Przenoszenia)


bit number          7 6 5 4 3 2 1 0 carry
originalne dane     1 0 0 1 1 0 1 1 0
                     \ \ \ \ \ \ \ \
przesunięte dane     0 1 0 0 1 1 0 1 1


Każda pozycja bitu jest potęgą dwójki, więc wykonanie LSR to to samo co dzielenie przez 2

I nowa instrukcja#2 to ROL (ROtate Left) będące odwrotnością LSR. Każdy bit przesuwany jest w lewo o jedno miejsce. Carry Flag przybiera wartość 0. Wykonanie ROL to to samoco mnożenie przez 2.


Instrukcje te są sprytnie używane razem do odczytywania kontrolera:
-Kiedy przycisk jest wczytywany, jego dane są w bicie 0.
-LSR wrzuca te dane do Carry
-ROL z powrotem przesuwa dane przycisku i ustawia Carry z powrotem na 0

                      Accumulator                               buttons data

bit:             7  6  5  4  3  2  1  0  Carry           7  6  5  4  3  2  1  0  Carry
read button A    0  0  0  0  0  0  0  A  0               0  0  0  0  0  0  0  0  0
LSR A            0  0  0  0  0  0  0  0  A               0  0  0  0  0  0  0  0  A
ROL buttons      0  0  0  0  0  0  0  0  A               0  0  0  0  0  0  0  A  0
 
read button B    0  0  0  0  0  0  0  B  0               0  0  0  0  0  0  0  A  0
LSR A            0  0  0  0  0  0  0  0  B               0  0  0  0  0  0  0  A  B
ROL buttons      0  0  0  0  0  0  0  0  0               0  0  0  0  0  0  A  B  0
 
read button SEL  0  0  0  0  0  0  0 SEL 0               0  0  0  0  0  0  0  A  0
LSR A            0  0  0  0  0  0  0  0 SEL              0  0  0  0  0  0  0  A SEL
ROL buttons      0  0  0  0  0  0  0  0  0               0  0  0  0  0  A  B SEL 0

read button STA  0  0  0  0  0  0  0 STA 0               0  0  0  0  0  0  0  A  0
LSR A            0  0  0  0  0  0  0  0 STA              0  0  0  0  0  0  0  A STA
ROL buttons      0  0  0  0  0  0  0  0  0               0  0  0  0  A  B SEL STA 0

I dalej pętla wykonuje się aż do ośmiu razy, żwby wczytać wszystkie przyciski z kontrolera. Na koniec w każdym z bitów jest status jednego z przycisków:

bit: 7 6 5 4 3 2 1 0

button: A B select start up down left right


Układ Gry
Silnik Ponga będzie używał zwykłego, prostego schematu gier na NES'a. Najpierw wykonywana jest cała inicjalizacja. Czyli  czyszczenie RAMu, ustawianie PPU, ładowanie grafik ekranu tytułowego. Potem wpada w nieskończoną pętlę czekającą na NMI. Kiedy wskakuje NMI, PPU jest gotowe do aktualizacji wszyskich grafik. Jest na to dość mało czasu, więc jako pierwszy ładowane jest kod sprite'ów DMA. Kiedy wszystkie grafiki mamy za sobą, zaczyna się ten właściwy silnik gry. Wczytywane są kontrolery, potem przetwarzanie gry. Pozycje sprite'ów są aktualizowane do pamięci RAM ale nie są aktualizowane aż do następnego NMI. Kiedy silnik gry zakończy swoje działania wracamy do nieskończonej pętli.

Kod inicjalizacji -> Nieskończona Pętla -> NMI -> Auktualizacjia Grafiki -> Wczytanie Przycisków -> Silnik Gry --\
                   ^                                                                                             |
                    \--------------------------------------------------------------------------------------------/


Stan Gry
Jeśli jesteśmy w "stanie" ekranu tytułowego nie ma potrzeby przetwarzania kodu odpowiadającego na ruch piłeczki. Dla Ponga będą trzy stany gry. Na ilustracji jest przedstawione co każdy ze stanów robi i co powinno być ustawione, żeby przejść do kolejnego stanu.

->Stan Tytułowy               /--> Stan Grania               /-->  Stan Game Over
/  czekaj na przycisk start --/     rusz kulę                /      czekaj na przycisk start -\
|                                   rusz paletkę            |                                  \
|                                   sprawdź kolizje         /                                  |
|                                   spr. czy punkcty = 15 -/                                   |
 \                                                                                             /
  \-------------------------------------------------------------------------------------------/


Następnym krokiem jest dodanie szczegółów to tego, co już sobie rozrysowaliśmy. Dziel i rządź, te sprawy. Część z elementów gry takich jak punkty dla drugiego gracza będą dodane później. Bez punktów jednak nie będziemy mogli przejść do stanu Game Over, ale na wszystko przyjdzie czas!

 Stan Tytułowy:
   jeśli wciśnięty start
     wyłącz ekran
     ładuj ekran gry
     ustaw pozycję piłeczki/paletek
     idź do Stanu Grania
     włącz ekran

 Stan Grania:
   ruszaj piłeczką
     jeśli piłeczka rusza się w prawo
     dodaj prędkość x do pozycji x piłeczki
     jeśli wpółrzędna x piłeczki > x ściany prawej
       odbij, piłeczka rusza się teraz w lewo

   jeśli piłeczka rusza się w lewo
     odejmij prędkość x do pozycji x piłeczki
     jeśli wpółrzędna x piłeczki < x ściany lewej
       odbij, piłeczka rusza się teraz w prawo

   jeśli piłeczka rusza się w górę
     odejmij prędkość y od pozycji y piłeczki
     jeśli wpółrzędna y piłeczki < y ściany górnej
       odbij, piłeczka rusza się teraz w dół
 
   jeśli piłeczka rusza się w dół
     dodaj prędkość y do pozycji y piłeczki
     jeśli wpółrzędna y piłeczki > y ściany dolnej
       odbij, piłeczka rusza się teraz w górę

   jeśli wciśnięta strzałka w górę
     jeśli góra paletki > górnej paletki
       rusz górę i dół paletki w górę

   jeśli wciśnięta strzałka w dół
     jeśli dół paletki < dolna ściana
       rusz górę i dół paletki w dół

   jeśli x kulki < x paletki
     jeśli y kulki < y góry paletki
       jeśli y kulki < y dołu paletki
         odbij, kulka porusza się w lewo

 Stan Game Over:
   jeśli wciśnięty start
     wyłącz ekran
     wczytaj ekran tytułowy
     idź do Stanu Tytułowego
     włącz ekran


Podsumowując
http://www.nespowerpak.com/nesasm/pong1.zip

Pobierz i rozpakuj pong1.zip. Kod Stanu Grania i poruszania piłeczką znajduje się pong1.asm. Upewnij się, że pliki mario.chr oraz pong1.bat są w tym samym folderze NESASM3, potem dwuklik w pong1.bat. Włączy się NESASM3 i powinien utworzyć pong1.nes. Otwórz go w FCEUXD SP by zobaczyć poruszaną piłeczkę!

Pozostałe fragmenty kodu nie są dokończone. Spróbuje je napisać samodzielnie. Główne braki to poruszanie paletki i kolizje piłeczki z paletkami. Możesz też dodać Stan Intro, ekran tytułowy itp. przy pomocy porad co do tła z poprzedniej nocy.

Literatura uzupełniająca:
Oryginał [ENG]:
Nerdy Nights week 7

Podręcznik ASM 6502 dla Atari [PL]:
http://atarionline.pl/biblioteka/materialy_ksiazkowe/Asembler%206502%20(v2).pdf

*

Offline MWK

  • Asystent Administratora
  • *****
  • 1412
    • www.mwkpegasus.prv.pl
Gruzińskie Noce - suplement diety
« Odpowiedź #7 dnia: Kwietnia 02, 2020, 00:23:05 »
Chciałem tylko wrzucić dwie linki do całości tego działu jako taki podręczny dodatek ale również bogate źródło wiedzy o całości konsoli:

NES Tech FAQ >> http://nesdev.com/NESTechFAQ.htm

Z Archiwum X >> http://nesdev.com/archive.html

*

Offline sdm

  • **
  • 182
Odp: Gruzińskie Noce - suplement diety
« Odpowiedź #8 dnia: Czerwca 07, 2020, 22:55:02 »
Jako dodatek:

http://romanworkshop.blutu.pl/asm65/nes.htm

Made by RomanWorkshop - osoba z forum Amigi. Wydaje mi się, że chyba nie bywa na pegasusowych forach.