Witam
Jestem uczniem 2 klasy liceum, z elektroniką łączy mnie tylko hobby. Chciałbym przestawić mój projekt wykonany na konkurs Gra Retro. Jest to remake gry Tetris. Jako wyświetlacz użyłem monitora VGA (w tym wypadku LCD) oraz pada od Pegasusa jako kontroler. Całe oprogramowanie zostało napisane przeze mnie.
Trochę danych:
Procesor: Atmega8 (8kB flash, 1kB SRAM)
Obraz: 104x120px, 16kolorów
Wejście: Pad od Pegasusa (8 przycisków)
Dźwięk: Mono, generowany przez PWM (niestety nie ma, brakło czasu)
Sprzęt
Procesorem jest znana wszystkim Atmega8 taktowana częstotliwością 20MHz. Wybór tego procesora był złym pomysłem, ale nie miałem już czasu aby to zmienić. Procesor na tej częstotliwości działa bardzo stabilnie. 90% czasu procesora poświęcone jest na generowanie obrazu VGA. Generowanie linii jest bardzo wrażliwe na opóźnienia. Dlatego każda linia generowana jest w przerwaniu timera 16bitowego wywoływanego co dokładnie 636 cykli zegarowych. Tam są ustawiane sygnały synchronizacji oraz kolorów. Z tego powodu nie można korzystać z innych przerwań w konsoli. Obraz jest 16kolorowy, generowany jest za pomocą dzielników rezystorowych. W 10% czasu procesora generowana jest grafika, liczone są kolizje i cała logia gry.
Urządzenie zasilane jest przez stabilizator 7805 w standardowej konfiguracji.
Dźwięk
Niestety brak. Limit czasowy konkursu okazał się zabójczy. Co prawda złącze jest wyprowadzone, razem z filtrem i dzielnikiem, ale konsola nie generuje żadnego dźwięku.
Oprogramowanie
Jak wspomniałem całość napisana jest w assemblerze. Dlaczego? Atmega8 ma tylko 1kB ramu, to za mało, aby generować ładny obraz. Dodatkowo pisząc w C nie mamy nad nią wielkiej kontroli i potrzebujemy dużo stosu. Pisząc w assemblerze mam nad tym kontrolę.
Warto wziąć lepszy procesor i nie męczyć się w assemblerze, po przygodach z nim nie chcę mieć więcej nic wspólnego (mimo, że pisze się całkiem przyjemnie w porównaniu do x86).
Pisząc grę opierałem się na Tengen Tetris (w którego grałem na Pegasusie lata temu) oraz Tetrisie na Gameboya. Zasady w mojej wersji są praktycznie takie same.
Pamięć EEPROM została wykorzystana do trzymania najlepszych wyników uzyskanych w grze. Przetwornik ADC został wykorzystany do losowania klocków. Czytam szum z jednego z pinów.
Źródła załączam w załączniku, jednak nie polecam wzorowania się na nich (wiele rzeczy się powtarza, dużo jest bezsensownego kodu, pierwszy raz pisałem w assemblerze i się spieszyłem dodatkowo
Generowanie obrazu
Generując obraz VGA (640x480) potrzebujemy częstotliwości 25.175MHz - jeden cykl zegara to jeden piksel. Ale dostanie takiego kwarcu nie jest łatwym zadaniem i może być to częstotliwość za duża do prawidłowego działania procesora. Ja użyłem kwarcu 20.000MHz, który jest łatwo dostępny, ale zmniejszyło to rozdzielczość do 512x480 (a w rzeczywistości jeszcze więcej). Generowanie każdej linii wygląda w ten sposób - przez 512 cykli (dla kwarcu 25.175MHz jest to 640 cykli) wyrzucamy co cykl zegara następny kolor. Niestety w takim mikrokontrolerze jest to niemożliwe, gdyż samo wczytanie danych z pamięci SRAM zajmuje 2 cykle zegara, a instrukcja OUT (wyrzucenie danych na port) 1 cykl. Czyli w najlepszym przypadku dostajemy 512/3~170px. Następnie przez 124 cykle musimy wygenerować sygnały synchronizacji poziomej. Ja w tym miejscu wchodzę w przerwanie, zrzucam rejestry na stos, ustawiam synchronizację sprawdzam, którą linię aktualnie rendeuję - jeżeli większą niż 480 to wychodzę z przerwania i oddaje czas procesorowi na resztę programu. Przez 480 linii obraz jest generowany, potem przez 11 linii wyświetlany jest kolor czarny, potem w ciągu 2 linii ustawiam synchronizację pionową i przez kolejne 31 linii dalej jest ciemność. Daje to 44 linie, przez które nic się nie robi. Daje nam to ( w przybliżeniu) 512*44=22528 wolnych cykli procesora. To właśnie w tym momencie wykonywana jest reszta programu.
Jednak nie jest tak fajnie, gdyż nie dysponujemy taką ilością ramu. Ze względu na ograniczenia RAMu musiałem wymyślić sposób na generację ładnego obrazu. Najlepszym pomysłem okazało się podzelenie pamięci na bloki (tiles) o wielkości 4x4 piksele. W pamięci mieszczę 28x30 takich bloczków. Jeden bloczek to jedna z 256 grafik zapisanych we flashu. W ten sposób można łatwo rysować klocki oraz czcionki.
W przerwaniu video wartości poszczególnych bloków zamieniane są na wartości znajdujące się we flashu. Niestety te obliczenia zajmują dużo (aż 19 cykli) co drastycznie zmniejsza rozdzielczość. W wyniku otrzymujemy rozdzielczość 104x120 pikseli.
Obliczone piksele są wyrzucane na PORTC, gdzie 3 najmłodsze bity to kolory RGB, a 4 bit to bit jasności. Jest on podłączony przez diody i rezystor do każdego z bitów RGB. Gdy nie jest on zaświecony - na każdym bicie jest 0 v, a gdy włączony - 0.35v (czyli szary). Zwiększa to ilość kolorów z 8 do 16.
Płytkę robiłem termotransferem, miedź nie wygląda najlepiej, jest to wina zużytego tonera w drukarce. Złącze od pada jest podłączone przez goldpiny z głupiego powodu - w sklepie nie mieli złącza do druku, musiałem użyć takiego na kabel.
Opis wyjść pada, na którym się wzorowałem: http://nesdev.parodius.com/ffpa.txt
Fajne? Ranking DIY