Snake na oscyloskop
(jeśli uważasz, że ten tekst jest chaotyczny, to dobrze uważasz; pisałem go na biegu i troche mi się nie chciało...)
(na dole strony jest galeria. i filmik. :))
Wstęp.
Pewnie zastanawiasz się - jak to możliwe? Ano możliwe - sam zobacz. Pomysł na zrobienie gry na oscyloskop lub inną lampę jest stary jak świat (no może bez przesady). Pierwszy był (chyba) Pong - poczytasz o nim i innych grach tutaj. No to zaczynamy.
Konstrukcja
Współczesne gry tego typu składają się zwykle z mikrokontrolera oraz przetworników D/A (cyfrowo/analogowych), których zdaniem jest zamiana wartości podanej na wejście (binarnie) na napięcie, które może posłużyć do odchylenia plamki na ekranie.
Do swojego projektu wybrałem mikrokontoler firmy Maxim/Dallas, model DS89C450. Należy on do rodziny MCS51 (czyli po prostu 8051). Na pokładzie znajdziemy:
- Zmodyfikowany rdzeń '51, pracujący z prędkością 12x szybszą niż oryginalny 8051 (przy tej samej częstotliwości zegara),
- 64KB pamięci programu typu FLASH, programowanej przez port RS232,
- 256+128 bajtów pamięci dostępu bezpośredniego (na zmienne),
- 1KB dodatkowej pamięci RAM adresowanej jako pamięc zewnętrzna (to tam trzymam dane dotyczące węża),
- 11-poziomowy system przerwań,
- dwa porty RS232,
- 3 timery,
- komparator analogowy.
O wyborze tego mikrokontrolera zdecydowało kilka aspektów - między innymi jego bogate możliwości, duża szybkość działania (przy maksymalnym zegarze wydajność obliczeniowa wynosi 33MIPS) no i fakt, że akurat miałem go pod ręką. :D
Procek rezyduje na płytce testowej, którą projektowałem przez pół roku. Płytka ta umożliwia programowanie procka przy użyciu jakiegokolwiek programu terminala, np. minicoma, poprzez RS232 (dla linuksiarzy ttySn, dla windziarzy COMn). Taktowany jest zegarem 11.0592MHz, czyli ok. 1/3 częstotliwości maksymalnej. Czemu akurat taka? Bo na niej najlepiej działa komunikacja przez RS232, a kwarc do zegara o takiej wartości kosztuje 1.40zł, a nie np. 5zł, jak te o wyższej częstotliwości.
Jako przetworniki D/A zastosowałem standardowe przetworniki DAC08 firmy Analog Devices. Mają one rozdzielczość 8 bitów, czyli mogą podać na wyjście 256 różnych poziomów napięcia. To raczej proste. Zostały one podłączone w bardzo typowy sposób (w zasadzie to przerysowałem żywcem schemat z karty katalogowej).
Przetworniki są dwa - do odchylania osi X oraz Y.
Jest jeszcze "joypad" zmontowany naprędce z walających się po pokoju części...
Działanie
Tu raczej nie ma wiele filozofii - każdy szanujący się programista chociaż raz napisał (albo próbował napisać) grę typu snake. Implementacja jest moja, pomysł też, chociaż sądzę, że wszystkie snake'i na świecie są napisane właśnie w ten sposób. Silnik działa w następujący sposób:
- Ruch węża jest sterowany za pomocą timera - nie ma mowy o zwalnianiu przy wydłużaniu węża.
- Pętla programu na zmianę odświeża ekran i skanuje klawiaturę, zapisując w przypadku wciśniecia klawisza, w którą stronę użyszkodnik/gracz chce węża poruszyć.
- Przy każdym nadchodzącym przerwaniu timera używany jest kierunek wybrany przez gracza jako ostatni.
- Dodatkowe zabezpieczenie zapewnia, że procedura przesuwania węża nie zostanie uruchomiona przez timer w nieodpowiednim momencie (np. w połowie zmieniania danych przez obsługę klawiatury).
Odświeżanie ekranu po prostu przemiata węża od głowy do ogona, rysując kolejne kwadraciki. Kwadracik jest rysowany przez bardzo sophisticated procedurę:
#define BOK 12
#define SQR 16
#define OFF 2
void vp_kwadracik(u8 x, u8 y)
{
X= x*SQR + OFF;
Y= y*SQR + OFF;
for (kw_c=BOK; kw_c; kw_c--) { X++; }
for (kw_c=BOK; kw_c; kw_c--) { Y++; }
for (kw_c=BOK; kw_c; kw_c--) { X--; }
for (kw_c=BOK; kw_c; kw_c--) { Y--; }
}
Generator liczb losowych to po prostu kręcący się niezależnie od innych timer, z którego w razie potrzeby odczytuję wartość (8-bitową) i rozbijam ją na dwie 4-bitowe oznaczające współrzędne X i Y punktu, XORując przy okazji z jakimiś innymi przypadkowymi wartościami z programu. Ponieważ odświeżanie minimalnie zależy od wciskania klawiszy, gra jest niepowtarzalna.
Program i kompilator
Co ja się będę rozpisywał... kompilowałem program przy użyciu opensource'owego SDCC. Kompilator jest beznadziejny (na jednym kawałku kodu mi segfaultował...), no ale lepszego nie mam, a za Keila płacić nie zamierzam.
Program powstawał w bólach, a tak poza tym to najpierw miał to być tetris. Lol...
Poza samą grą na początku jest rysowany kwadrat celem kalibracji viewportu. Potem jest jeszcze wybór poziomu trudności... osobiście lubię grać na siódemce. :] Na dziewiątce się nie da, bo timer odświeżający generuje przerwanie nieustannie i wąż porusza się z prędkością naddźwiękową... :D Na koniec pokazuje się liczba punktów. To znaczy powinna, ale kompilator ma problemy z operacjami na liczbach 16-bitowych. Zwłaszcza przesunięciach i and/or logicznych. Mówi się trudno...
Schematy!
- Płytka testowa [57K]
- Przetwornik (x2) [19K]
Źródełka!
- Wszystko [11K]
Galeria i filmik!
- Całość [93K]
- Wersja z joystickiem od Commodore 64 [45K] - dał się podłaczyć bez żadnego problemu
- Elektronika (w wysokiej rozdzielczości) [433K]
- Oscyloskop [40K]
- FILMIK - gra w akcji! [2.9MB!]