Tytuł: Gruzińskie Noce, czyli Nerdy Nights #PL 07 Wiadomość wysłana przez: 1990in1 Października 16, 2015, 13:52:12 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) Kod: 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 Kod: 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: 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. Kod: ->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 (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 (http://nintendoage.com/forum/messageview.cfm?catid=22&threadid=8747) Podręcznik ASM 6502 dla Atari [PL]: http://atarionline.pl/biblioteka/materialy_ksiazkowe/Asembler%206502%20(v2).pdf Tytuł: Odp: Gruzińskie Noce, czyli Nerdy Nights #PL 07 Wiadomość wysłana przez: Koks Października 16, 2015, 15:54:12 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. To może być mylące - te instrukcje nie są zupełnie odwrotne, ogólnie ROL wrzuca do Carry Flag zawartość bitu 7 (chociaż w tym przypadku to rzeczywiście zero, ale co z tego?).Tytuł: Odp: Gruzińskie Noce, czyli Nerdy Nights #PL 07 Wiadomość wysłana przez: sdm Grudnia 07, 2015, 17:12:24 To moze teraz czas na "PL" : ?
http://nintendoage.com/auth/forum/messageview.cfm?catid=22&threadid=33378 http://nintendoage.com/forum/messageview.cfm?catid=22&threadid=38467 Sa inne tez czesci. Tytuł: Odp: Gruzińskie Noce, czyli Nerdy Nights #PL 07 Wiadomość wysłana przez: 1990in1 Grudnia 07, 2015, 20:03:04 Niestety, brak mi wiedzy i czasu na podjęcie się takiego wyzwania:) Może jak zacznę sam trochę to ogarniać to i do kolejnych tłumaczeń się wezmę, ale na razie samych nocy nie "przeszedłem".
|