Zadanie 2: sterownik urządzenia HardDoom™¶
Data ogłoszenia: 10.04.2018
Termin oddania: 15.05.2018 (ostateczny 29.05.2018)
Contents
Materiały dodatkowe¶
- Urządzenie HardDoom™
- Symulator urządzenia: https://github.com/koriakin/qemu (branch
harddoom
) - Program testowy: https://github.com/koriakin/prboom-plus (branch
doomdev
) - Plik nagłówkowy z definicjami rejestrów sprzętowych (do skopiowania do rozwiązania): https://github.com/koriakin/qemu/blob/harddoom/hw/misc/harddoom.h
- Plik nagłówkowy z definicjami interfejsu urządzenia znakowego (do skopiowania do rozwiązania): https://github.com/koriakin/prboom-plus/blob/doomdev/src/doomdev.h
- Mikrokod potrzebny do uruchomienia urządzenia: https://github.com/koriakin/harddoom/blob/master/doomcode.bin lub https://github.com/koriakin/harddoom/blob/master/doomcode.h
Wprowadzenie¶
Zadanie polega na napisaniu sterownika do urządzenia HardDoom™, będącego akceleratorem grafiki dedykowanym dla gry Doom. Urządzenie dostarczane jest w postaci zmodyfikowanej wersji qemu.
Urządzenie powinno być dostępne dla użytkownika w formie urządzenia
znakowego. Dla każdego urządzenia HardDoom™ obecnego w systemie należy
utworzyć urządzenie znakowe /dev/doomX
, gdzie X to numer kolejny
urządzenia HardDoom™, zaczynając od 0.
Interfejs urządzenia znakowego¶
Urządzenie /dev/doom*
służy tylko i wyłącznie do tworzenia zasobów
HardDoom™ – wszystkie właściwe operacje będą wykonywane na utworzonych
zasobach. Powinny one obsługiwać następujące operacje:
open
: w oczywisty sposób.close
: w oczywisty sposób.ioctl(DOOMDEV_IOCTL_CREATE_SURFACE)
: tworzy nowy bufor ramki na urządzeniu. Jako parametr tego wywołania przekazywane są wymiary bufora (szerokość i wysokość). Szerokość musi być wielokrotnością 64 z zakresu 64 .. 2048, a wysokość musi być w zakresie 1 .. 2048. Wynikiem tego wywołania jest nowy deskryptor pliku odnoszący się do utworzonego bufora. Utworzony bufor ma nieokreśloną zawartość.ioctl(DOOMDEV_IOCTL_CREATE_TEXTURE)
: tworzy nową teksturę kolumnową na urządzeniu. Parametrami tego wywołania są rozmiar tekstury w bajtach (maksymalnie 4MiB), wysokość tekstury w tekselach (maksymalnie 1023, lub 0, jeśli tekstura nie ma być powtarzana w pionie), oraz wskaźnik na dane tekstury. Wynikiem jest deskryptor pliku odnoszący się do utworzonej tekstury.ioctl(DOOMDEV_IOCTL_CREATE_FLAT)
: tworzy nową teksturę płaską na urządzeniu. Parametrem tego wywołania jest wskaźnik na dane (0x1000
bajtów). Wynikiem jest deskryptor pliku odnoszący się do utworzonej tekstury.ioctl(DOOMDEV_IOCTL_CREATE_COLORMAPS)
: tworzy nową tablicę map kolorów na urządzeniu. Parametrami tego wywołania są rozmiar tablicy (liczba map kolorów) oraz wskaźnik na dane (każda mapa kolorów to0x100
bajtów). Wynikiem jest deskryptor pliku odnoszący się do utworzonej tablicy. Maksymalny dopuszczalny rozmiar to tablica0x100
map.
Tekstury i tablice map kolorów nie obsługują żadnych
standardowych operacji poza close
(która, o ile nie zostały już
żadne inne referencje, zwalnia ich pamięć) – można ich użyć jedynie
jako parametrów do wywołań rysowania. Nie da się również w żaden sposób zmienić
ich zawartości po utworzeniu.
Wszystkie wskaźniki przekazywane są jako uint64_t
, aby struktury miały taki
sam układ w trybie 64-bitowym jak w 32-bitowym, pozwalając na uniknięcie konieczności
definiowania odpowiedających struktur _compat
. Z tego samego powodu wiele
struktur ma nieużywane pola _pad
.
Na buforach ramki można wywołać następujące operacje:
ioctl(DOOMDEV_SURF_IOCTL_COPY_RECT)
: wykonuje serię operacjiCOPY_RECT
do danego bufora. Parametry to:surf_src_fd
: deskryptor pliku wskazujący na bufor ramki, z którego ma być wykonana kopia.rects_ptr
: wskaźnik na tablicę strukturdoomdev_copy_rect
.rects_num
: liczba prostokątów do skopiowania.- w każdej strukturze
doomdev_copy_rect
:pos_dst_x
,pos_dst_y
– współrzędne prostokąta docelowego w danym buforze (lewy górny róg).pos_src_x
,pos_src_y
– współrzędne prostokąta źródłowego w buforze źródłowym.width
,height
– rozmiar kopiowanego prostokąta.
Semantyka tego wywołania jest dość podobna do
write
: sterownik stara się wykonać jak najwięcej operacji z zadanej listy, zatrzymując się w razie błędu bądź przyjścia sygnału. Jeśli nie udało się wykonać pierwszej operacji, zwracany jest kod błędu. W przeciwnym przypadku, zwracana jest liczba wykonanych operacji. Kod użytkownika jest odpowiedzialny za ponowienie próby w przypadku niepełnego wykonania.Użytkownik jest odpowiedzialny, za zapewnienie, że w ramach jednego wywołania
ioctl
żaden piksel nie jest jednocześnie pisany i czytany (tzn. nie będzie wymagane polecenieINTERLOCK
między prostokątami). Sterownik nie musi (choć jak bardzo chce to może) wykrywać takich sytuacji – wysłanie poleceń na urządzenie i uzyskanie błędnego wyniku rysowania jest dopuszczalne w takiej sytuacji.ioctl(DOOMDEV_SURF_IOCTL_FILL_RECT)
: wykonuje serię operacjiFILL_RECT
. Parametry:rects_ptr
: wskaźnik na tablicę strukturdoomdev_fill_rect
.rects_num
: liczba prostokątów do wypełnienia.- w każdej strukturze
doomdev_fill_rect
:pos_dst_x
,pos_dst_y
– współrzędne prostokąta docelowego w danym buforze.width
,height
– rozmiar wypełnianego prostokąta.color
– kolor, którym będzie wypełniany prostokąt.
Wartość zwracana jak przy
DOOMDEV_SURF_IOCTL_COPY_RECT
.ioctl(DOOMDEV_SURF_IOCTL_DRAW_LINE)
: wykonuje serię operacjiDRAW_LINE
. Parametry:lines_ptr
: wskaźnik na tablicę strukturdoomdev_line
.lines_num
: liczba linii do narysowania.- w każdej strukturze
doomdev_line
:pos_a_x
,pos_a_y
: współrzędne jednego z końców linii.pos_b_x
,pos_b_y
: współrzędne drugiego z końców linii.color
– kolor, którym będzie rysowana linia.
Wartość zwracana jak przy
DOOMDEV_SURF_IOCTL_COPY_RECT
.ioctl(DOOMDEV_SURF_IOCTL_DRAW_BACKGROUND)
: wykonuje operacjęDRAW_BACKGROUND
. Parametry:flat_fd
: deskryptor pliku wskazujący na teksturę płaską, która będzie służyć za tło.
W przypadku udanego wywołania zwraca 0.
ioctl(DOOMDEV_SURF_IOCTL_DRAW_COLUMNS)
: wykonuje serię operacjiDRAW_COLUMN
z podobnymi parametrami. Parametry wspólne dla wszystkich kolumn:draw_flags
: ustawienia rysowania, kombinacja następujących flag:DOOMDEV_DRAW_FLAGS_FUZZ
– jeśli włączona, rysowany będzie efekt cienia – większość parametrów jest ignorowana (w tym pozostałe flagi).DOOMDEV_DRAW_FLAGS_TRANSLATE
– jeśli włączona, paleta będzie przemapowana zgodnie z mapą kolorów.DOOMDEV_DRAW_FLAGS_COLORMAP
– jeśli włączona, kolory będą przyciemnane zgodnie z mapą kolorów.
texture_fd
: deksryptor tekstury kolumnowej. Ignorowany, jeśli użyto flagiFUZZ
.translation_fd
: deskryptor tablicy map kolorów użytej do opcjiTRANSLATE
. Ignorowany, jeśli nie użyto flagiTRANSLATE
.colormap_fd
: deskryptor tablicy map kolorów użytej do opcjiCOLORMAP
orazFUZZ
. Ignorowany, jeśli nie użyto żadnej z tych flag.translation_idx
: indeks mapy kolorów użytej do zmiany palety (używany tylko, jeśli flagaTRANSLATE
jest włączona).columns_num
: liczba kolumn do rysowaniacolumns_ptr
wskaźnik (w przestrzeni użytkownika) na parametry ustawiane osobno dla każdej kolumny, zapisane jako tablica strukturdoomdev_column
:column_offset
: offset początku danych kolumny w teksturze w bajtach.ustart
: liczba stałoprzecinkowa 16.16 bez znaku, musi być w zakresie obsługiwanym przez sprzęt. Ignorowane, jeśli użyto flagiFUZZ
.ustep
: liczba stałoprzecinkowa 16.16 bez znaku, musi być w zakresie obsługiwanym przez sprzęt.x
: współrzędnax
kolumny.y1
,y2
: współrzędney
początku i końca kolumny.colormap_idx
: indeks mapy kolorów użytej do przyciemniania kolorów. Używany tylko, jeśli flagaFUZZ
lubCOLORMAP
jest włączona.
Wartość zwracana jak przy
DOOMDEV_SURF_IOCTL_COPY_RECT
.ioctl(DOOMDEV_SURF_IOCTL_DRAW_SPANS)
: wykonuje serię operacjiDRAW_SPAN
z podobnymi parametrami. Parametry wspólne dla wszystkich pasków:flat_fd
: deksryptor tekstury płaskiej.translation_fd
: jak wyżej.colormap_fd
: jak wyżej.draw_flags
: jak wyżej, ale bez flagiFUZZ
.translation_idx
: jak wyżej.spans_num
: liczba pasków do rysowaniaspans_ptr
wskaźnik (w przestrzeni użytkownika) na dane pasków do rysowania, zapisane jako tablica strukturdoomdev_span
:ustart
,vstart
: początkowe współrzędne w teksturze (dla lewego końca). Liczby stałoprzecinkowe 16.16 (wysokie 10 bitów powinno być zignorowane przez sterownik).ustep
,vstep
: krok współrzędnych x, y. Format taki jak przy*start
.x1
,x2
: współrzędnex
początku i końca paska.y
: współrzędnay
paska.colormap_idx
: jak wyżej.
Wartość zwracana jak przy
DOOMDEV_SURF_IOCTL_COPY_RECT
.lseek
: ustawia pozycję w buforze dla następnych wywołańread
.read
,pread
,readv
, itp: czeka na zakończenie wszystkich wcześniej wysłanych operacji rysujących do danego bufora, po czym czyta gotowe dane z bufora do przestrzeni użytkownika. W razie próby czytania poza zakresem bufora, należy poinformować o końcu pliku.
Sterownik powinien wykrywać polecenia z niepoprawnymi parametrami
(zły typ pliku przekazany jako *_fd
, współrzędne wystające poza
bufor ramki, itp.) i zwrócić błąd EINVAL
. W przypadku próby stworzenia
tekstur czy buforów ramki większych niż obsługiwane przez sprzęt, należy
zwrócić EOVERFLOW
.
Sterownik powinien rejestrować swoje urządzenia w sysfs, aby udev
automatycznie utworzył pliki urzadzeń o odpowiednich nazwach w /dev
.
Numery major i minor dla tych urządzeń są dowolne (majory powinny być
alokowane dynamicznie).
Plik nagłówkowy z odpowiednimi definicjami można znaleźć tutaj: https://github.com/koriakin/prboom-plus/blob/doomdev/src/doomdev.h
Sterownik może przyjąć ograniczenie do 256 urządzeń w systemie.
Założenia interakcji ze sprzętem¶
Można założyć, że przed załadowaniem sterownika, urządzenie ma stan jak po resecie sprzętowym. Urządzenie należy też w takim stanie zostawić przy odładowaniu sterownika.
Pełnowartościowe rozwiązanie powinno działać asynchronicznie – rysujące
operacje ioctl
powinny wysyłać polecenia do urządzenia i wracać do
przestrzeni użytkownika bez oczekiwania na zakończenie działania (ale jeśli
bufory poleceń są już pełne, dopuszczalne jest oczekiwanie na zwolnienie
miejsca w nich). Oczekiwanie na zakończenie poleceń powinno być wykonane
dopiero przy wywołaniu read
, które będzie rzeczywiście potrzebowało
wyników rysowania.
Zasady oceniania¶
Za zadanie można uzyskać do 10 punktów. Na ocenę zadania składają się trzy części:
- pełne wykorzystanie urządzenia (od 0 do 2 punktów):
- operacja w pełni asynchroniczna (
ioctl
nie czeka na zakończenie wysłanych poleceń, rozpoczęcie wysyłania poleceń przezioctl
nie czeka na zakończenie poleceń wysłanych wcześniej,read
nie wymaga zatrzymania całego urządzenia): 1p - użycie bloku wczytywania poleceń: 1p
- operacja w pełni asynchroniczna (
- wynik testów (od 0 do 8 punktów)
- ocena kodu rozwiązania (od 0 do -10 punktów)
Punktacja¶
Punkty ujemne za kod można było dostać za:
- -0.5 Brak weryfikacji minor w callbacku open()
- -0.5 Niepoprawne używanie błędów ERESTARTSYS/EINTR, EFAULT i/lub wariantów *_interruptible mutexów/semaforów.
- -1.0 Aktywne oczekiwanie przy synchronizacji procesów (zamiast wait_event* i podobnych).
- -0.3 Brak/błędna implementacja lseek.
- -0.3 Nie sprawdzanie czy zasoby (bufory, tekstury itp) należą do właściwego urządzenia.
- -0.5 close() na poszczególnych zasobach zwalnia je nawet jak są jeszcze używane.
- -0.5 Niepoprawna weryfikacja argumentów poleceń.
- -0.8 Brak synchronizacji równoległych dostępów do urządzania (w najprostszej wersji synchronicznej: mutex w ioctl, per urządzenie).
- -0.3 Zwracanie 0 lub nieokreśloną wartość zamiast błędu jeśli pierwsze polecenie w batchu się nie powiedzie.
- -0.3 Wycieki zasobów/pamięci (np. brakujące fdput, device_destroy).
- -0.5 Nie zwracanie IRQ_NONE jeśli HARDDOOM_INTR == 0.
- -0.8 Dostęp do userspace bezpośrednio (bez copy_from_user etc).
- -0.3 Nie sprawdzanie czy zasoby (źródłowe bufory, tekstury, mapy kolorów) są właściwego typu.
- -0.5 Używanie _istotnie_ za dużo pamięci (np. alokowanie zawsze maksymalnego bufora ramki zamiast faktycznego rozmiaru)
Testy:
- DRAW_BACKGROUND
- COPY_RECT niewymagający INTERLOCK
- FILL_RECT
- DRAW_LINE
- DRAW_COLUMN z COLORMAP, TRANSLATE
- DRAW_COLUMN bez flag
- DRAW_SPAN
- DRAW_COLUMN na zmianę z COPY_RECT pomiędzy kilkoma buforami, wymagający INTERLOCK
- wszystkie powyższe, na wielu buforach, jeden wątek
- wszystkie powyższe, na wielu buforach, wiele równoległych wątków (każdy ze swoim zestawm buforów)
Forma rozwiązania¶
Sterownik powinien zostać zrealizowany jako moduł jądra Linux w wersji
4.9.13. Moduł zawierający sterownik powinien nazywać się harddoom.ko
.
Jako rozwiązanie należy dostarczyć paczkę zawierającą:
- źródła modułu
- pliki Makefile i Kbuild pozwalające na zbudowanie modułu
- krótki opis rozwiązania
Rozwiązania prosimy nadsyłać na adres marmarek@mimuw.edu.pl
z kopią
do mwk@mimuw.edu.pl
.
QEMU¶
Do użycia urządzenia HardDoom™ wymagana jest zmodyfikowana wersja qemu, dostępna w wersji źródłowej.
Aby skompilować zmodyfikowaną wersję qemu, należy:
Sklonować repozytorium https://github.com/koriakin/qemu
git checkout harddoom
Upewnić się, że są zainstalowane zależności:
ncurses
,libsdl
,curl
, a w niektórych dystrybucjach takżencurses-dev
,libsdl-dev
,curl-dev
(nazwy pakietów mogą się nieco różnić w zależności od dystrybucji)Uruchomić
./configure
z opcjami wedle uznania (patrz./configure --help
). Oficjalna binarka była kompilowana z:--target-list=i386-softmmu,x86_64-softmmu --python=$(which python2) --audio-drv-list=alsa,pa
Wykonać
make
Zainstalować wykonując
make install
, lub uruchomić bezpośrednio (binarka tox86_64-softmmu/qemu-system-x86_64
).
Aby zmodyfikowane qemu emulowało urządzenie HardDoom™, należy przekazać mu
opcję -device harddoom
. Przekazanie tej opcji kilka razy spowoduje emulację
kilku instancji urządzenia.
Aby dodać na żywo (do działającego qemu) urządzenie HardDoom™, należy:
- przejść do trybu monitora w qemu (Ctrl+Alt+2 w oknie qemu)
- wpisać
device_add harddoom
- przejść z powrotem do zwykłego ekranu przez Ctrl-Alt-1
- wpisać
echo 1 > /sys/bus/pci/rescan
, aby linux zauważył
Aby udać usunięcie urządzenia:
echo 1 > /sys/bus/pci/devices/0000:<idurządzenia>/remove
Testy¶
Do testowania sterownika przygotowaliśmy zmodyfikowaną wersję prboom-plus
,
który jest uwspółcześnioną wersją silnika gry Doom. Aby go uruchomić, należy:
- zainstalować w obrazie paczki:
libsdl2-dev
libsdl2-mixer-dev
libsdl2-image-dev
libsdl2-net-dev
xfce4
[albo inne środowisko graficzne]xserver-xorg
autoconf
- pobrać źródła z repozytorium https://github.com/koriakin/prboom-plus
- wybrać branch
doomdev
- skompilować źródła (bez instalacji, program nie będzie w stanie znaleźć swojego pliku z danymi,
prboom-plus.wad
):./bootstrap
./configure --prefix=$HOME
make
make install
- pobrać plik z danymi gry. Można użyć dowolnego z następujących plików:
freedoom1.wad
lubfreedoom2.wad
z projektu Freedoom (https://freedoom.github.io/) – klon gry Doom dostępny na wolnej licencji.doom.wad
lubdoom2.wad
z pełnej wersji oryginalnej gry, jeśli zakupiliśmy taką.doom1.wad
z wersji shareware oryginalnej gry.
- załadować nasz sterownik i upewnić się, że mamy dostęp do
/dev/doom0
- uruchomić X11, a w nim grę:
$HOME/bin/prboom-plus -iwad <dane_gry.wad>
- w menu Options -> General -> Video mode wybrać opcję “doomdev” (domyślne ustawienie “8bit” wybiera renderowanie programowe w trybie bardzo podobnym do naszego urządzenia i można go użyć do porównania wyników).
Aby w grze działał dźwięk, należy przekazać do qemu opcję -soundhw hda
i przy kompilacji
jądra włączyć stosowny sterownik (Device Drivers -> Sound card support -> HD-Audio).
Wskazówki¶
Do plików dla buforów ramek, tekstur, itp. polecamy użyć funkcji
anon_inode_getfile
. Niestety, tak utworzone pliki nie pozwalają domyślnie
na lseek
, pread
, itp – żeby to naprawić, należy ustawić flagi
FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE
w polu f_mode
.
Aby dostać strukturę file
z deskryptora pliku, możemy użyć fdget
i fdput
.
Aby sprawdzić, czy przekazana nam struktura
jest odpowiedniego typu, wystarczy porównać jej wskaźnik na strukturę
file_operations
z naszą.
Polecamy zacząć implementację od operacji FILL_COLOR
i DRAW_LINE
(wymagają tylko bufora ramki i pozwalają zobaczyć mapę). Następnie
polecamy DRAW_COLUMN
(można na początku pominąć flagi i mapy kolorów)
– jest odpowiedzialna za rysowanie większości grafiki w grze i bez niej
niewiele zobaczymy.
Urządzenie wymaga, by rozmiar tekstury był wielokrotnością 256 bajtów. W przypadku stworzenia tekstury o rozmiarze niewspieranym bezpośrednio przez sprzęt, należy wyrównać rozmiar w górę i dopełnić dane od użytkownika zerami.
Rozmiar tekstury czy bufora ramki rzadko kiedy jest dokładnie wielokrotnością strony – możemy to wykorzystać, umieszczając tabelę stron w nieużywanej części ostatniej strony. Pozwoli to uniknąć osobnej alokacji na (zazwyczaj bardzo małą) tabelę stron.
Do rozwiązania w niżej punktowanej wersji synchronicznej nie jest konieczne
użycie polecenia FENCE
i powiązanych rejestrów – wystarczy samo
PING_SYNC
. W rozwiązaniu w pełnej wersji asynchronicznej
konieczne będzie użycie FENCE
(w połączeniu z rejestrem FENCE_WAIT
lub poleceniem PING_SYNC
do oczekiwania w read
).
Może się zdarzyć, że nie będziemy w stanie wysłać polecenia ze względu na
brak miejsca w kolejce poleceń (tej wbudowanej w urządzenie bądź naszej
własnej w pamięci wskazywanej przez CMD_*_PTR
). Żeby efektywnie
zaimplementować oczekiwanie na wolne miejsce, polecamy:
- wysyłać z jakąś minimalną częstotliwością (np. co 1/8 .. 1/2 wielkości
naszego bufora poleceń bądź sprzętowej kolejki) polecenie
PING_ASYNC
- domyślnie ustawić przerwanie
PONG_ASYNC
na wyłączone - w razie zauważenia braku miejsca w kolejce:
- wyzerować przerwanie
PONG_ASYNC
wINTR
- sprawdzić, czy dalej nie ma miejsca w kolejce (zabezpieczenie przed wyścigiem) – jeśli jest, od razu wrócić do wysyłania
- włączyć przerwanie
PONG_ASYNC
wINTR_ENABLE
- czekać na przerwanie
- z powrotem wyłączyć przerwanie
PONG_ASYNC
wINTR_ENABLE
- wyzerować przerwanie
Aby urządzenie działało wydajnie, należy unikać niepotrzebnego wysyłania
poleceń wymuszających serializację (przede wszystkim między poleceniami
z jednego wywołania ioctl
):
*_PT
,*_ADDR
: czyszczą pamięć podręczną i blokują paczkowanie sąsiednich kolumn przez mikrokod (w szczególności, jeśli dwie sąsiednie kolumny mają taki samcolormap_idx
, nie należy wysyłać między nimi poleceniaCOLORMAP_ADDR
).SURF_DIMS
,TEXTURE_DIMS
,DRAW_PARAMS
,FENCE
,PING_SYNC
: blokują paczkowanie kolumn (ale są w zasadzie darmowe pomiędzy paczkami).INTERLOCK
: blokuje równoległe przetwarzanie poleceńCOPY_RECT
(jeśli chcemy czytać z bufora, do którego ostatnio rysowaliśmy przed wysłaniem ostatniego poleceniaINTERLOCK
, nie ma potrzeby wysyłać kolejnego – w szczególności, w przypadku serii wywołańCOPY_RECT
między różnymi buforami ramki, nie należy wysyłaćINTERLOCK
między wywołaniami).
Nie trzeba się za to przejmować nadmiarowymi poleceniami FILL_COLOR
,
XY_*
, *START
, *STEP
, PING_ASYNC
– przetworzenie
ich przez urządzenie nie zajmie więcej niż sprawdzenie nadmiarowości przez
sterownik.
Jeżeli chcemy tymczasowo (do testów) pozmieniać coś w grze (np. wykomentować
operacje, których jeszcze nie wspieramy), kod odpowiedzialny za obsługę
urządzenia możemy znaleźć w src/i_doomdev.c
.
Jeżeli popsujemy konfigurację prboom-plus
tak, że przestanie się
uruchamiać w stopniu wystarczającym do zmiany ustawień, możemy znaleźć
jego plik konfiguracyjny w $HOME/.prboom-plus/prboom-plus.cfg
.
Usunięcie go spowoduje przywrócenie ustawień domyślnych.
Aby zobaczyć różne operacje w akcji:
DRAW_COLUMN
bez żadnych flag: używane do rysowania grafik interfejsu (menu, itp).DRAW_COLUMN
zCOLORMAP
: używane do rysowania wszystkich ścian i większości obiektów.DRAW_COLUMN
zTRANSLATE
: używane do rysowania nowego HUD (dostępnego pod przyciskiem F5 – być może po naciśnięciu kilka razy). Jeśli zmiana palety działa, część cyfr powinna nie być czerwona.DRAW_COLUMN
zFUZZ
: wpisujemy kodidbeholdi
, żeby uczynić się niewidzialnym (po prostu naciskając kolejne litery w trakcie rozgrywki).DRAW_SPAN
: używane do rysowania podłogi / sufitu.DRAW_LINE
iFILL_COLOR
: używane do rysowania mapy (przycisk Tab).COPY_RECT
: używane do efektu przejścia między stanami gry (choćby rozpoczęcie nowej gry czy ukończenie poziomu).DRAW_BACKGROUND
: zmniejszamy rozmiar ekranu z pełnego (naciskając przycisk-
kilka razy) – ramka ekranu powinna być wypełniona.