Tytuł: Gruzińskie Noce, czyli Nerdy Nights #PL 03 Wiadomość wysłana przez: 1990in1 Grudnia 02, 2014, 22:31:30 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/background.zip)http://www.nespowerpak.com/nesasm/NESASM3.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_id=90)http://www.the-interweb.com/serendipity/exit.php?url_id=627&entry_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 Dzięki Dizziemu za wytłumaczanie niektórych rzeczy. Tytuł: Odp: Gruzińskie Noce, czyli Nerdy Nights #PL 03 Wiadomość wysłana przez: siudym Grudnia 16, 2014, 21:54:36 Dodatkowo fajny, polski art. na temat przesuwania bitow:
http://s16.postimg.org/mlp2kcd91/przesuwanie.png (http://s16.postimg.org/mlp2kcd91/przesuwanie.png) (http://s16.postimg.org/ufl2rzvo5/przes_sml.jpg) http://wyslijto.pl/plik/rpldzkbm8c |