Urządzenie Ultimate HardDoom™¶
Grafika w grze Ultimate Doom¶
Rzutowanie dowolnej geometrii trójwymiarowej na dwuwymiarową płaszczyznę jest dość skomplikowane obliczeniowo. Jednym z ważniejszych elementów jest obliczenie współczynnika perspektywy dla każdego rysowanego piksela — wymaga to obliczenia odwrotności pierwiastka kwadratowego. Jest to bardzo kosztowna operacja i wykonanie jej dla każdego piksela ekranu jest niemożliwe przy zachowaniu rozsądnej ilości ramek na sekundę, nawet dla najnowocześniejszych i napotężniejszych procesorów i486, nie mówiąc już o procesorach i386 używanych obecnie przez większość graczy.
Gra Ultimate Doom zalicza się do tzw. gier 2.5D — choć grafika wygląda na trójwymiarową (i jest bardzo realistyczna), użyta geometria oraz punkt widzenia są bardzo ograniczone. Użytkownik może patrzeć tylko przed siebie (a nie w górę czy w dół), a w świecie gry spotkamy tylko 3 typy obiektów:
pionowe ściany — po zrzutowaniu na ekran, każda pokryta kolumna pikseli składa się z pikseli o równym współczynniku perspektywy, pozwalając na wyliczenie go tylko raz na każdą kolumnę.
poziome płaszczyzny (podłogi i sufity) — po zrzutowaniu na ekran, każda pokryta linia pikseli składa się z pikseli o równym współczynniku perspektywy.
obiekty ruchome, rysowane przez skopiowanie prosto na ekran przeskalowanych tzw. sprite’ów, czyli przygotowanych przez autorów zdjęć, zrobionych z 8 różnych kątów.
Główna praca rysowania świata gry spada na dwie gorące funkcje:
R_DrawColumn
— rysuje kolumnę pikseli z podanej kolumny tekstury i z podanym współczynnikiem skalowania. Używane do rysowania ścian, obiektów oraz grafiki interfejsu.R_DrawSpan
— rysuje poziomy pasek pikseli, teksturując podaną teksturą płaszczyzny (wycinając z tej tekstury linię pod dowolnym kątem i nakładając ją na rysowany pasek).
Urządzenie Ultimate HardDoom™ dostarcza sprzętową implementację powyższych funkcji, przejmując większość obciążenia z głównego procesora i pozwalając na grę w wysokich rozdzielczościach (płynna rozgrywka na 640×480!) nawet na starszych procesorach. Oprócz tych funkcji, urządzenie dostarcza również kilka pomniejszych funkcji pomocniczych, pozwalając na rysowanie całej grafiki gry na urządzeniu.
Urządzenie¶
Urządzenie jest podłączane do komputera przez szynę PCI –
identyfikator producenta to 0x0666
, a identyfikator urządzenia to
0x1995
.
Urządzenie nie posiada własnej pamięci i operuje na buforach w głównej pamięci komputera przez bezpośredni dostęp do pamięci (DMA). Ponieważ główna pamięć komputera podlega fragmentacji, a urządzenie może używać całkiem dużo pamięci, urządzenie wykonuje dostęp do pamięci przez tablicę stron w swoim własnym formacie.
Urządzenie jest zaprojektowane tak, by można było je obsługiwać z minimalną ingerencją jądra w przesyłane polecenia — wszystkie polecenia są walidowane przez urządzenie i operują tylko tylko na adresach wirtualnych, tłumaczonych przez tabele stron — użytkownik przygotowujący bufor poleceń nie ma możliwości zawieszenia urządzenia ani dostępu do nie swojej pamięci. Co najwyżej, może spowodować zgłoszenie błędu przez urządzenie, który może być obsłużony przez jądro systemu.
Urządzeniem steruje się przez rejestry wejścia/wyjścia zmapowane do pamięci (MMIO). Ten obszar MMIO jest pierwszym i jedynym obszarem BAR używanym przez urządzenie (BAR0). Urządzenie wykorzystuje również jedną linię przerwania PCI.
Obszar MMIO ma wielkość 64kiB, ale tylko niektóre bajty z tego zakresu są używane na rejestry. Wszystkie udokumentowane rejestry są 32-bitowe w formacie little-endian i powinny być używane tylko przez wyrównane 32-bitowe odczyty i zapisy.
Przestrzeń adresowa i blok stronicowania¶
Wszystkie dane i polecenia używane przez urządzenie są obsługiwane przez adresy wirtualne, które są tłumaczone przez tabele stron urządzenia. Urządzenie obsługuje dwie przestrzenie adresowe, odpowiadające dwóm zestawom tabeli stron:
przestrzeń adresowa jądra: używana przez blok
BATCH
do wczytywania zadańprzestrzeń adresowa użytkownika: używana do wczytywania poleceń, czytania tekstur i map kolorów, czytania i pisania buforów ramki
Urządzenie pozwala na szybką zmianę przestrzeni adresowej użytkownika (przez wybranie nowego zestawu tabeli stron), pozwalając na bezpieczną obsługę wielu użytkowników przez jądro.
Urządzenie używa 40-bitowych adresów fizycznych (zarówno dla buforów jak i dla tabel stron), 32-bitowych adresów wirtualnych, i stron o rozmiarze 4kiB. Tabele stron używane przez urządzenie są dwupoziomowe:
jądro podaje urządzeniu adres fizyczny katalogu stron
bity 22-31 adresu wirtualnego wybierają wpis w katalogu stron (page directory), który zawiera adres fizyczny tablicy stron
bity 12-21 adresu wirtualnego wybierają wpis w tabeli stron (page table), który zawiera adres fizyczny strony i jej prawa dostępu (do odczytu i zapisu, bądź tylko do odczytu)
bity 0-11 adresu wirtualnego to offset wewnątrz strony
Zarówno katalogi stron jak i tabele stron mają rozmiar 4kiB (taki jak strona) i składają się z 1024 wpisów, gdzie każdy wpis jest 32-bitowym słowem w formacie little-endian. Wpis w katalogu stron ma następujący format:
bit 0:
PRESENT
— jeśli ustawiony, dany wpis jest obecny i może być użyty. Jeśli wyzerowany, wpis jest nieobecny i użycie go spowoduje błąd braku strony (PAGE_FAULT
).bity 4-31:
PA
— bity 12-39 adresu fizycznego tabeli stron (bity 0-11 adresu są zawsze równe 0 — tabele i katalogi stron muszą być wyrównane).
Wpis w tabeli stron ma następujący format:
bit 0:
PRESENT
— jeśli ustawiony, dany wpis jest obecny i może być użyty. Jeśli wyzerowany, wpis jest nieobecny i użycie go spowoduje błąd braku strony (PAGE_FAULT
).bit 1:
WRITABLE
— jeśli ustawiony, dana strona jest dostępna do odczytu i zapisu. Jeśli wyzerowany, strona jest dostępna tylko do odczytu, a próba zapisu spowoduje błąd strony (PAGE_FAULT
).bity 4-31:
PA
— bity 12-39 adresu fizycznego strony (bity 0-11 adresu są zawsze równe 0 — strony muszą być wyrównane).
Katalog stron wybiera się, podając bity 12-39 jego adresu fizycznego (czyli adres
przesunięty w prawo o 12 bitów). Aby wybrać katalog stron jądra, należy zapisać
tak przesunięty adres do rejestru BATCH_PDP
. Wybór katalogu stron użytkownika
następuje podczas wysyłania zadania do urządzenia.
Aby nie czytać ciągle tabeli stron, urządzenie może zapamiętywać
dane z tabel i katalogów stron w buforach TLB. Zmiana aktywnego katalogu
stron spowoduje automatyczne wyczyszczenie odpowiadających buforów TLB.
Sterownik może również w dowolnym momencie wyczyścić bufory TLB ręcznie,
używając bitu TLB_KERNEL
bądź TLB_USER
w rejestrze RESET
.
W szczególności, należy to zrobić po odmapowaniu strony z przestrzeni
adresowej, a przed jej zwolnieniem (aby użytkownik nie mógł użyć
sterego mapowania, by dotknąć nie swojej pamięci).
Jeśli urządzenie napotka niepoprawny adres wirtualny (bez flagi PRESENT
w katalogu stron czy tabeli stron, bądź bez flagi WRITABLE
przy próbie
zapisu), zgłaszane jest jedno z przerwań PAGE_FAULT_*
, a odpowiadający
blok urządzenia jest wyłączany w rejestrze ENABLE
. W takiej sytuacji
można naprawić brak strony (przez wstawienie nowych wartości w katalogi
i tabele stron) i wznowić zadanie, bądź też przerwać zadanie przez
wykonanie procedury resetu urządzenia.
Aby wznowić zadanie, należy poprawić odpowiednie wpisy w katalogach
czy tabelach stron, wyczyścić TLB, oznaczyć przerwanie jako obsłużone,
po czym włączyć z powrotem odpowiedni blok w ENABLE
— urządzenie
kontynuuje wtedy pracę od momentu w którym trafiło na błąd strony.
Urządzenie ma 8 różnych przerwań PAGE_FAULT
, odpowiadających 8 wewnętrznym
ścieżkom dostępu do pamięci. Są to:
PAGE_FAULT_BATCH
: blokBATCH
, wczytywanie zadań (jako jedyne używa przestrzeni adresowej jądra)PAGE_FAULT_CMD
: blokCMD
, wczytywanie poleceńPAGE_FAULT_SRD
: blokSRD
, wczytywanie mapy kolorów, bufora źródłowego do poleceniaBLIT
, bądź bufora docelowego do poleceniaDRAW_FUZZ
PAGE_FAULT_SWR_DST
: blokSWR
, dostęp do docelowego bufora ramki (jako jedyne wykonuje zapisy do pamięci)PAGE_FAULT_COL_CMAP_B
: blokCOL
, wczytywanie mapy kolorówPAGE_FAULT_COL_SRC
: blokCOL
, wczytywanie teksturyPAGE_FAULT_SPAN_SRC
: blokSPAN
, wczytywanie teksturyPAGE_FAULT_SWR_TRANSMAP
: blokSWR
, wczytywanie mapy przezroczystości
Aby dowiedzieć się, jaki adres wirtualny spowodował błąd strony, można przeczytać jeden z następujących rejestrów:
BAR0 + 0x0480: TLB_CLIENT_VA[TLB_CLIENT_BATCH]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_BATCH
.BAR0 + 0x0484: TLB_CLIENT_VA[TLB_CLIENT_CMD]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_CMD
.BAR0 + 0x0488: TLB_CLIENT_VA[TLB_CLIENT_SRD]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_SRD
.BAR0 + 0x048c: TLB_CLIENT_VA[TLB_CLIENT_SWR_DST]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_SWR_DST
.BAR0 + 0x0490: TLB_CLIENT_VA[TLB_CLIENT_COL_CMAP_B]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_COL_CMAP_B
.BAR0 + 0x0494: TLB_CLIENT_VA[TLB_CLIENT_COL_SRC]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_COL_SRC
.BAR0 + 0x0498: TLB_CLIENT_VA[TLB_CLIENT_SPAN_SRC]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_SPAN_SRC
.BAR0 + 0x049c: TLB_CLIENT_VA[TLB_CLIENT_SWR_TRANSMAP]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_SWR_TRANSMAP
.
Przydatne mogą być też następujące rejestry:
BAR0 + 0x0400: TLB_KERNEL_PDP
Adres aktywnego katalogu stron jądra, przesunięty w prawo o 12 bitów.
BAR0 + 0x0404: TLB_USER_PDP
Adres aktywnego katalogu stron użytkownika, przesunięty w prawo o 12 bitów.
Wysyłanie zadań¶
Urządzenia używa się przez zlecanie mu wykonania zadań. Są dwa sposoby
na zlecanie wykonania zadań — bezpośrednio przez rejestry, bądź przez
blok BATCH
, który wczytuje zadania do wykonania z pamięci. Zadanie
składa się z trzech słów:
PDP
: bity 12-39 adresu fizycznego katalogu stron użytkownika, który będzie używany przez to zadanie.CMD_PTR
: wskaźnik wirtualny (w przestrzeni użytkownika), pod którym znajdują się polecenia do wykonania przez urządzenie. Wskaźnik ten musi być wielokrotnością 4.CMD_SIZE
: rozmiar bufora poleceń do wykonania w bajtach. Musi być wielokrotnością 4.
Aby zlecić zadanie bezpośrednio, należy użyć nastęþujących rejestrów:
BAR0 + 0x0040: JOB_PDP
Bity 12-39 adresu fizycznego katalogu stron użytkownika.
BAR0 + 0x0044: JOB_CMD_PTR
Wskaźnik na polecenia.
BAR0 + 0x0048: JOB_CMD_SIZE
Rozmiar poleceń.
BAR0 + 0x004c: JOB_TRIGGER
Rejestr tylko do zapisu. Zapisanie dowolnej wartości spowoduje natychmiastowe rozpoczęcie wykonywania zadania zdefiniowanego przez powyższe rejestry. Nie należy używać tego rejestru, gdy jakieś zadanie jest już wykonywane przez urządzenie.
Poprawne zakończenie wykonywania zadania spowoduje zgłoszenie przerwania
JOB_DONE
. Zadanie może również spowodować błąd, zgłaszając inne
przerwanie — CMD_ERROR
, FE_ERROR
, PAGE_FAULT_*
. W takim
wypadku konieczne będzie wykonanie procedury resetu urządzenia przed
wysłaniem kolejnego polecenia.
Błąd FE_ERROR
¶
Błąd FE_ERROR
jest zgłaszany przez blok FE
w przypadku napotkania
problemu przez firmware — oznacza to wykryte błędne polecenie, bądź wewnętrzny
błąd firmware’u. Można się dowiedzieć więcej o źródle błędu, czytając następujące
rejestry:
BAR0 + 0x0120: FE_ERROR_DATA_A
Informacja o błędzie (zależna od kodu błędu).
BAR0 + 0x0124: FE_ERROR_DATA_B
Informacja o błędzie (zależna od kodu błędu).
BAR0 + 0x0128: FE_ERROR_CODE
Kod błędu. Jeden z:
0x00
:UNK_USER_COMMAND
— nieznany typ polecenia od użytkownika.DATA_A
to wskaźnik na błędne polecenie,DATA_B
to pierwsze słowo polecenia.0x01
:DST_PTR_UNALIGNED
— wskaźnik na bufor ramki nie jest wielokrotnością 64 bajtów.DATA_A
to wskaźnik na błędne polecenie,DATA_B
to błędny wskaźnik na bufor ramki.0x02
:DST_PITCH_UNALIGNED
— przeskok bufora ramki nie jest wielokrotnością 64 bajtów.DATA_A
to wskaźnik na błędne polecenie,DATA_B
to błędny przeskok.0x03
:COLORMAP_UNALIGNED
— wskaźnik na mapę kolorów nie jest wielokrotnością 64 bajtów.DATA_A
to wskaźnik na błędne polecenie,DATA_B
to błędny wskaźnik na mapę kolorów.0x04
:DRAW_COLUMNS_Y_REV
— początkowa współrzędna y w poleceniuDRAW_COLUMNS
bądźDRAW_FUZZ
jest większa niż końcowa wspołrzędna.DATA_A
to wskaźnik na błędne polecenie,DATA_B
to błędne słowo polecenia.0x05
:DRAW_SPANS_X_REV
— początkowa współrzędna x w poleceniuDRAW_SPANS
jest większa niż końcowa wspołrzędna.DATA_A
to wskaźnik na błędne polecenie,DATA_B
to błędne słowo polecenia.0x80
:ILLEGAL_INSTRUCTION
— wewnętrzny błąd firmware’u. Nigdy nie powinno się zdarzyć.0x81
:UNALIGNED_INSTRUCTION
— wewnętrzny błąd firmware’u.0x82
:BUS_ERROR_LOAD
— wewnętrzny błąd firmware’u.0x83
:BUS_ERROR_STORE
— wewnętrzny błąd firmware’u.0x84
:BUS_ERROR_EXEC
— wewnętrzny błąd firmware’u.
Blok BATCH
¶
Blok BATCH
pozwala na automatyczne wczytywanie zadań przez urządzenie —
sterownik zapisuje zadania do wykonania w cyklicznym buforze w przestrzeni adresowej
jądra, a urządzenie będzie automatycznie wczytywać i rozpoczynać kolejne
zadanie gdy tylko skończy obecne. Aby użyć tego bloku, należy stworzyć
bufor zadań, zmapować go do przestrzeni jądra, po czym użyć następujących
rejestrów:
BAR0 + 0x0020: BATCH_PDP
Adres fizyczny katalogu stron jądra, przesunięty w prawo o 12 bitów.
BAR0 + 0x0024: BATCH_GET
Adres wirtualny, z którego urządzenie ma wczytać następne zadanie do wykonania. Po udanym wykonaniu wczytanego zadania, ten wskaźnik zostanie automatycznie zwiększony o 16 przez urządzenie.
BAR0 + 0x0028: BATCH_PUT
Adres wirtualny, pod którym jądro ma zapisać następne zadanie do wykonania. Jeśli
BATCH_GET
będzie równyBATCH_PUT
, oznacza to, że urządzenie obecnie nie ma żadnych zadań do wczytania (i będzie czekać aż jądro zapisze kolejne zadanie i zwiększyBATCH_PUT
).BAR0 + 0x002c: BATCH_WAIT
Adres wirtualny, którego osiągnięcie przez
BATCH_GET
spowoduje wyzwolenie przerwaniaBATCH_WAIT
. Sterownik może użyć tego mechanizmu, by czekać na zakończenie wykonania określonego polecenia.BAR0 + 0x0030: BATCH_WRAP_FROM
Adres wirtualny końca bufora zadań. Jeśli
BATCH_GET
po zwiększeniu osiągnął by wartośćBATCH_WRAP_FROM
, zamiast tego zostanie ustawiony naBATCH_WRAP_TO
(by działać jak bufor cykliczny).BAR0 + 0x0034: BATCH_WRAP_TO
Adres wirtualny początku bufora zadań (patrz wyżej).
Opis zadania ma 16 bajtów i składa się z czterych słów 32-bitowych w formacie litle-endian:
bajty 0-3: wskaźnik fizyczny na katalog stron (zostanie zapisany do
JOB_PDP
)bajty 4-7: wskaźnik wirtualny na polecenia (
JOB_CMD_PTR
)bajty 8-11: rozmiar poleceń w bajtach (
JOB_CMD_SIZE
)bajty 12-15: (nieużywane)
Wszystkie adresy używane przez blok BATCH
muszą być wielokrotnością 16 bajtów
(czyli rozmiaru struktury opisującej zadanie).
Blok BATCH
można opisać następującym pseudokodem:
while True:
# Gdy nie ma aktywnego zadania, blok BATCH jest włączony, i bufor zadań nie jest pusty...
if ENABLE.BATCH and not STATUS.JOB and BATCH_GET != BATCH_PUT:
# Wczytaj 16-bajtowe zadanie z przestrzeni adresowej jądra
cur_batch = mem_read(BATCH_PD, BATCH_GET, 0x10)
# I uruchom je
JOB_PD = cur_batch[0:4]
JOB_CMD_PTR = cur_batch[4:8]
JOB_CMD_SIZE = cur_batch[8:12]
# Bajty 12:16 to padding
JOB.TRIGGER()
# czekaj na zakończenie zadania
while STATUS.JOB:
pass
# Podbij BATCH_GET, być może zastosuj wrap.
BATCH_GET += 0x10
if BATCH_GET == BATCH_WRAP_FROM:
BATCH_GET = BATCH_WRAP_TO
# Zgłoś przerwanie, jeśli osiągnięta została odpowiednia wartość wskaźnika.
if BATCH_GET == BATCH_WAIT:
INTR |= INTR_BATCH_WAIT
Rejestry sterujące¶
Blok sterujący zajmuje się nadzorowaniem pracy całego urządzenia. Jego rejestry to:
BAR0 + 0x0000: ENABLE
Rejestr kontrolujący pracę pozostałych bloków, dostępny do odczytu i zapisu. Ma wiele bitów, z których każdy kontroluje pracę jednego bloku urządzenia (1 — blok jest aktywny i może wykonywać pracę, 0 — blok jest nieaktywny). Wyłączenie bloku w tym rejestrze nie spowoduje jego resetu — po ponownym włączeniu, blok kontynuuje pracę od momentu, w którym skończył. Bity:
bit 0:
BATCH
— blok wczytywania zadań.bit 2:
CMD
— wczytywanie poleceń użytkownika.bit 3:
FE
— przetwarzanie poleceń przez firmware.bit 4:
SRD
— proste wczytywanie danychbit 5:
SPAN
— teksturowanie paskówbit 6:
COL
— teksturowanie kolumnbit 7:
FX
— efekty specjalne (FUZZ
, mapy kolorów)bit 8:
SWR
— mapy przezroczystości i zapis do bufora ramki.
Rejestr jest ustawiany na 0 przez reset maszyny, blokując urządzenie do momentu załadowania sterownika. W przypadku zgłoszenia błędu przez któryś z bloków urządzenia, urządzenie wyzeruje odpowiedni bit w tym rejestrze, zatrzymując blok do momentu naprawienia problemu przez sterownik (być może przez reset całego urządzenia).
Warning
Przed włączeniem urządzenia, należy pamiętać o:
załadowaniu firmware’u przez
FE_CODE_ADDR
iFE_CODE_WINDOW
zresetowaniu wszystkich bloków w
RESET
,zainicjowaniu rejestrów bloku
BATCH
(jeśli go używamy)wyzerowaniu przerwań (w
INTR
)
BAR0 + 0x0004: RESET
Rejestr resetowania urządzenia, dostępny tylko do zapisu. Ma wiele bitów, odpowiadających blokom urządzenia. Przy zapisie, wszystkie bloki, których bity są równe 1 w zapisanej wartości, są resetowane: wszystkie polecenia w trakcie wykonywania są przerywane i kasowane, kolejki poleceń i pamięci podręczne są czyszczone. Bity:
bit 1:
JOB
— kontrola zadańbit 2:
CMD
— wczytywanie poleceń użytkownika.bit 3:
FE
— przetwarzanie poleceń przez firmware.bit 4:
SRD
— proste wczytywanie danychbit 5:
SPAN
— teksturowanie paskówbit 6:
COL
— teksturowanie kolumnbit 7:
FX
— efekty specjalne (FUZZ
, mapy kolorów)bit 8:
SWR
— mapy przezroczystości i zapis do bufora ramki.bit 9:
STATS
— blok statystyk. Wszystkie liczniki są ustawiane na 0.bit 10:
TLB_KERNEL
— bufory TLB dla przestrzeni wirtualnej jądra. Wszystkie wpisy tabel i katalogów stron zapisane w TLB są kasowane i, w razie potrzeby, zostaną wczytane na nowo.bit 11:
TLB_USER
— bufory TLB dla przestrzeni wirtualnej użytkownika.bit 12:
CACHE_COL_CMAP_B
— pamięć podręczna na mapy kolorów.bit 13:
CACHE_COL_SRC
— pamięć podręczna na tekstury kolumnowe.bit 14:
CACHE_SPAN_SRC
— pamięć podręczna na tekstury płaskie.bit 15:
CACHE_SWR_TRANSMAP
— pamięć podręczna na mapy przezroczystości.bit 16:
FIFO_SRDCMD
— wewnętrzna kolejka urządzenia.bit 17:
FIFO_SPANCMD
— wewnętrzna kolejka urządzenia.bit 18:
FIFO_COLCMD
— wewnętrzna kolejka urządzenia.bit 19:
FIFO_FXCMD
— wewnętrzna kolejka urządzenia.bit 20:
FIFO_SWRCMD
— wewnętrzna kolejka urządzenia.bit 21:
FIFO_COLIN
— wewnętrzna kolejka urządzenia.bit 22:
FIFO_FXIN
— wewnętrzna kolejka urządzenia.bit 24:
FIFO_FESEM
— wewnętrzna kolejka urządzenia.bit 25:
FIFO_SRDSEM
— wewnętrzna kolejka urządzenia.bit 26:
FIFO_COLSEM
— wewnętrzna kolejka urządzenia.bit 27:
FIFO_SPANSEM
— wewnętrzna kolejka urządzenia.bit 28:
FIFO_SPANOUT
— wewnętrzna kolejka urządzenia.bit 29:
FIFO_COLOUT
— wewnętrzna kolejka urządzenia.bit 30:
FIFO_FXOUT
— wewnętrzna kolejka urządzenia.
Użycie bitów
TLB_*
,STATS
iCACHE_*
w dowolnym momencie nie wpłynie negatywnie na pracę urządzenia. Wszystkie pozostałe bity powinny być używane w zasadzie tylko wtedy, gdy resetujemy całe urządzenie (gdyż w przeciwnym przypadku stan poszczególnych bloków się rozsynchronizuje).Warning
Przed użyciem urządzenia (włączeniem bloku rysującego w
ENABLE
) należy zresetować całe urządzenie (zapisując0x7f7ffffe
do tego rejestru) – w przeciwnym wypadku, wewnętrzne rejestry stanu urządzenia mogą zawierać śmieci, powodując wykonanie nieokreślonych poleceń przez urządzenie (w tym zapisu do dowolnej pamięci) bądź zawieszenie urządzenia.Blok
BATCH
nie ma swojego bitu resetującego — jeśli sterownik chce go użyć, powinien zamiast tego zainicjować rejestryBATCH_*
.
Przerwania¶
Urządzenie wewnętrznie używa 12 przerwań (które są agregowane w jedno przerwanie PCI):
BATCH_WAIT
— przerwanie używane do powiadomienia sterownika o wykonaniu wybranego polecenia przez blokBATCH
. Wyzwalane, gdy wskaźnikBATCH_GET
osiągnie wartośćBATCH_WAIT
.JOB_DONE
— wyzwalane przy poprawnym wykonaniu każdego polecenia.FE_ERROR
— wyzwalane, gdy firmware zauważy błędne polecenie bądź napotka wewnętrzny błąd.CMD_ERROR
— wyzwalane, gdy blokCMD
wyjedzie poza bufor (czyli koniec bufora w środku polecenia).PAGE_FAULT_BATCH
— wyzwalane, gdy blokBATCH
spowoduje błąd stronyPAGE_FAULT_CMD
— wyzwalane, gdy blokCMD
spowoduje błąd stronyPAGE_FAULT_SRD
— wyzwalane, gdy blokSRD
spowoduje błąd stronyPAGE_FAULT_SWR_DST
— wyzwalane, gdy blokSWR
spowoduje błąd strony przy zapisie do bufora ramkiPAGE_FAULT_COL_CMAP_B
— wyzwalane, gdy blokCOL
spowoduje błąd strony przy czytaniu mapy kolorówPAGE_FAULT_COL_SRC
— wyzwalane, gdy blokCOL
spowoduje błąd strony przy czytaniu teksturyPAGE_FAULT_SPAN_SRC
— wyzwalane, gdy blokSPAN
spowoduje błąd stronyPAGE_FAULT_SWR_TRANSMAP
— wyzwalane, gdy blokSWR
spowoduje błąd strony przy czytaniu mapy przezroczystości
Każde z powyższych przerwań może być w danej chwili aktywne bądź nie.
Przerwanie staje się aktywne, gdy zajdzie odpowiednie zdarzenie. Przerwanie
staje się nieaktywne, gdy sterownik zapisze 1 do odpowiedniego bitu
w rejestrze INTR
. Aby sprawdzić, które przerwania są aktywne, należy
przeczytać rejestr INTR
.
Niezależnie, każde z powyższych przerwań może być w danej chwili włączone
bądź nie. Sterownik może ustawić włączony podzbiór przerwań przez zapis
odpowiedniej maski do rejestru INTR_ENABLE
. Urządzenie zgłasza przerwanie
na swojej linii przerwań PCI wtedy i tylko wtedy, gdy istnieje włączone
i aktywne przerwanie.
BAR0 + 0x0008: INTR
Rejestr statusu przerwań. Ma 12 bitów, każdy odpowiadający jednemu rodzajowi przerwań:
bit 0:
BATCH_WAIT
,bit 1:
JOB_DONE
,bit 4:
FE_ERROR
,bit 5:
CMD_ERROR
,bit 8:
PAGE_FAULT_BATCH
bit 9:
PAGE_FAULT_CMD
bit 10:
PAGE_FAULT_SRD
bit 11:
PAGE_FAULT_SWR_DST
bit 12:
PAGE_FAULT_COL_CMAP_A
bit 13:
PAGE_FAULT_COL_SRC
bit 14:
PAGE_FAULT_SPAN_SRC
bit 15:
PAGE_FAULT_SWR_TRANSMAP
Odczyt tego rejestru zwróci 1 dla aktywnych przerwań, 0 dla nieaktywnych. Zapis spowoduje wyzerowanie (ustawienie na nieaktywne) wszystkich przerwań, dla których został zapisany bit 1. Przykładowo, zapisanie 0x12 spowoduje wyzerowanie przerwań
FE_ERROR
orazJOB_DONE
bez zmiany stanu pozostałych przerwań.BAR0 + 0x000c: INTR_ENABLE
Rejestr włączania przerwań, dostępny do odczytu i zapisu. Ma takie same bity jak
INTR
. 1 oznacza przerwanie włączone, a 0 — przerwanie wyłączone. Przy resecie maszyny, rejestr zostaje ustawiony na 0, blokując możliwość zgłaszania przerwania PCI przez urządzenie do momentu załadowania sterownika.
Firmware¶
Blok rysujący opiera się na zaawansowanym procesorze odpowiadającym za
przetwarzanie poleceń. Przed włączeniem bloku FE
w ENABLE
,
sterownik musi załadować odpowiedni firmware na urządzenie — w przeciwnym
wypadku, zachowanie urządzenia jest kompletnie niezdefiniowane. Firmware jest
tablicą 32-bitowych słów, maksymalnie 16384-elementową. Jest dostarczany
w pliku https://github.com/mwkmwkmwk/uharddoom/blob/master/udoomfw.h , a jego format
i działanie jest tajemnicą handlową firmy DoomDevices® (i nie należy zadawać
niewygodnych pytań na jego temat).
Do ładowania firmware’u służą następujące rejestry:
BAR0 + 0x0100: FE_CODE_ADDR
Adres w pamięci kodu, który będziemy widoczny przez
FE_CODE_WINDOW
(dostępny do odczytu i zapisu). Musi być wielokrotnością 4.BAR0 + 0x0104: FE_CODE_WINDOW
Okno do pamięci kodu — odczytuje bądź zapisuje słowo wybrane przez
FE_CODE_ADDR
, po czym zwiększaFE_CODE_ADDR
o 4 (pozwalając na szybki sekwencyjny odczyt bądź zapis wielu słów kodu).
Uruchomienie urządzenia¶
Poprawna procedura uruchamiania urządzenia jest następująca (użycie dowolnej innej procedury spowoduje utratę gwarancji i brak możliwości zwrotu urządzenia):
zapisać 0 do
FE_CODE_ADDR
,kolejno zapisać wszystkie słowa tablicy
udoomfw[]
doFE_CODE_WINDOW
,zresetować wszystkie bloki urządzenia przez zapis
0x7f7ffffe
doRESET
,zainicjować
BATCH_PDP
,BATCH_GET
,BATCH_PUT
iBATCH_WRAP
, jeśli chcemy użyć bloku wczytywania pleceń,wyzerować
INTR
przez zapis0xff33
,włączyć używane przez nas przerwania w
INTR_ENABLE
,włączyć wszystkie bloki urządzenia w
ENABLE
(być może z wyjątkiemBATCH
).
Po wykonaniu tej procedury można rozpocząć wysyłanie poleceń do urządzenia
(przez JOB_*
lub BATCH_PUT
).
Żeby wyłączyć urządzenie, wystarczy zapisać 0 do ENABLE
oraz
INTR_ENABLE
, po czym przeczytać dowolny rejestr urządzenia.
Reset urządzenia¶
W przypadku zgłoszenia błędu przez urządzenie, należy je zresetować, wykonując następujące kroki:
jeśli używamy bloku
BATCH
:wyłączyć blok
BATCH
wENABLE
,usunąć zadanie, które spowodowało błąd z bufora
BATCH
, bądź ręcznie przesunąć wskaźnikBATCH_GET
by je pominąć (bez tego, urządzenie natychmiast po rsecie wykona je po raz drugi, co prawdopodobnie również skończy się błędem),dobrym pomysłem będzie również usunięcie wszystkich innych zadań wysłanych przez ten sam kontekst (zadanie można efektywnie usunąć przez zmianę jego pola
CMD_SIZE
na 0)
zatrzymać wszystkie bloki urządzenia przez zapis
0
doENABLE
zresetować wszystkie bloki urządzenia przez zapis
0x7f7ffffe
doRESET
,wyzerować
INTR
przez zapis0xff33
,włączyć wszystkie bloki urządzenia w
ENABLE
(być może z wyjątkiemBATCH
).
Inne rejestry¶
Dokumentacja sprzętowa nigdy nie mówi całej prawdy (i rzadko mówi tylko prawdę). Urządzenie może mieć inne rejestry ponad wyżej wymienione, lecz używanie ich w ostatecznym sterowniku jest złym pomysłem — są to po prostu szczegóły implementacyjne. Mogą jednak być przydatne przy debugowaniu…
Poniższe rejestry nie są potrzebne do napisania sterownika, ale mają szansę się przydać (oprócz nich, urządzenie zawiera wiele nieudokumentowanych rejestrów).
BAR0 + 0x004: STATUS
Rejestr statusu, tylko do odczytu. Ma wiele bitów, odpowiadających blokom urządzenia. Gdy bit jest równy 1, dany blok ma pracę do wykonania (i, jeśli odpowiedni bit w
ENABLE
jest równy 1, będzie próbował ją wykonać). Gdy bit jest równy 0, dany blok nie ma nic do zrobienia (ale może się to w każdej chwili zmienić, jeżeli inny blok zleci mu jakieś zadania). Bity:bit 0:
BATCH
— wczytywanie zadańbit 1:
JOB
— kontrola zadańbit 2:
CMD
— wczytywanie poleceń użytkownika.bit 3:
FE
— przetwarzanie poleceń przez firmware.bit 4:
SRD
— proste wczytywanie danychbit 5:
SPAN
— teksturowanie paskówbit 6:
COL
— teksturowanie kolumnbit 7:
FX
— efekty specjalne (FUZZ
, mapy kolorów)bit 8:
SWR
— mapy przezroczystości i zapis do bufora ramki.bit 16:
FIFO_SRDCMD
— wewnętrzna kolejka urządzenia.bit 17:
FIFO_SPANCMD
— wewnętrzna kolejka urządzenia.bit 18:
FIFO_COLCMD
— wewnętrzna kolejka urządzenia.bit 19:
FIFO_FXCMD
— wewnętrzna kolejka urządzenia.bit 20:
FIFO_SWRCMD
— wewnętrzna kolejka urządzenia.bit 21:
FIFO_COLIN
— wewnętrzna kolejka urządzenia.bit 22:
FIFO_FXIN
— wewnętrzna kolejka urządzenia.bit 24:
FIFO_FESEM
— wewnętrzna kolejka urządzenia.bit 25:
FIFO_SRDSEM
— wewnętrzna kolejka urządzenia.bit 26:
FIFO_COLSEM
— wewnętrzna kolejka urządzenia.bit 27:
FIFO_SPANSEM
— wewnętrzna kolejka urządzenia.bit 28:
FIFO_SPANOUT
— wewnętrzna kolejka urządzenia.bit 29:
FIFO_COLOUT
— wewnętrzna kolejka urządzenia.bit 30:
FIFO_FXOUT
— wewnętrzna kolejka urządzenia.
BAR0 + 0x800 * i, 0 <= i < 0x80: STATS[i]
Statystyki pracy urządzenia. Każdy indeks w tej tablicy jest osobnym licznikiem, zliczającym pewne zdarzenia na urządzeniu. Dostępne zdarzenia i ich indeksy są wymienione w pliku nagłówkowym.
Podręcznik użytkownika¶
Ten rozdział opisuje polecenia, których może użyć użytkownik urządzenia. Nie jest on potrzebny do napisania sterownika, ale może wyjaśnić działanie testów.
Bufory ramek¶
Bufor ramki to obszar w pamięci służący do rysowania. Jest to po prostu dwuwymiarowa tablicą pikseli. Ponieważ urządzenie było projektowane zanim wynaleziono kolor 24-bitowy (albo nawet 16-bitowy), każdy piksel jest po prostu bajtem — odpowiadającym jakiemuś kolorowi z palety (obsługą palety zajmuje się urządzenie wyświetlające i nie musimy się nią przejmować). Urządzenie obsługuje bufory ramki o dowolnej wielkości mieszczącej się w pamięci i wymiarach nie większych niż 65535×65535.
Bufor ramki jest definiowany dwoma parametrami: adresem wirtualnym w przestrzeni
użytkownika oraz “przeskokiem” (pitch), czyli liczbą bajtów między początkami
kolejnych wierszy. Obydwa te parametry muszą być wielokrotnością 64.
Adres piksela (x, y) wewnątrz bufora to po prostu addr + x + y * pitch
.
Urządzenie nie ma informacji o wysokości ani szerokości bufora ramki — użytkownik
powinien sam zapewnić, że używane współrzędne mieszczą się w zakresie.
Tekstury kolumnowe¶
Tekstury kolumnowe zawierają dane obrazu, który polecenie DRAW_COLUMNS
będzie rysowało do bufora ramki (po przeskalowaniu i przetworzeniu).
Mają dość skomplikowany format i składają z tekseli pogrupowanych
w kolumny (które nie muszą pokrywać całej powierzchni tekstury
— tekstury kolumnowe mogą mieć dziury). Urządzenie nie przejmuje się
dokładnym formatem tych tekstur — znalezienie w nim początku odpowiedniej
kolumny jest zadaniem silnika gry. Teksturę kolumnową i miejsce w niej
wybiera się podając urządzeniu adres wirtualny kolumny w teksturze i wysokość
tej tekstury (maksymalnie 65536) — współrzędne w teksturze wyjeżdżające poza
jej wysokość będą automatycznie zawijane modulo wysokość.
Note
Aby nie mylić pojęć z odpowiadającymi pojęciami w buforach ramki, w grafice komputerowej przyjęło się nazywać współrzędne w teksturze U oraz V (zamiast X i Y), a pojedynczy element — tekselem (zamiast piksela).
Tekstury płaskie¶
Tekstury płaskie są używane przez polecenie DRAW_SPANS
do rysowania
podłóg i sufitów. W oryginalnej grze, mają zawsze wymiary 64×64 tekseli,
i zajmują 2**12 bajtów. W urządzeniu Ultimate HardDoom™, są obsługiwane
nieco bardziej ogólnie i mogą mieć dowolne wymiary będące potęgami dwójki
z zakresu 1..65536. Są definiowane przez 4 parametry:
adres wirtualny w przestrzeni użytkownika
przeskok (jak przy buforach ramki)
log2 z szerokości (
ULOG
)log2 z wysokości (
VLOG
)
Tak jak przy teksturach kolumnowych, wszystkie współrzędne są zawijane modulo wymiary tekstury.
Mapy kolorów¶
Mapy kolorów są po prostu tablicami mapującymi kolory z palety na inne kolory — mają przez to zawsze dokładnie 256 bajtów. Ich adres musi być również wyrównany do 256 bajtów. Są używane do wielu efektów:
zamiana palety kolorów, aby umożliwić użycie tej samej tekstury do kilku wersji kolorystycznych (tzw. palette swap) — w końcu pamięć jest droga
ściemnianie kolorów (do symulacji słabego oświetlenia)
zmiana skali kolorów (np. przefarbowanie wszystkiego na niebiesko pod wodą)
Mapę kolorów definiuje się przez jej adres wirtualny.
Mapy przezroczystości¶
Aby uzyskać efekt przezroczystości w grafice komputerowej, potrzebna jest jakaś funkcja scalająca dwa kolory — kolor tła oraz kolor rysowanego obiektu. W przypadku grafiki używającej schematu True Color, wystarczyłoby zsumować składowe kolorów z odpowiednimi wagami. Jednak w przypadku grafiki używającej palety kolorów, trzeba to zrobić używając obliczonej wcześniej tabeli — mapy przezroczystości. Mapa przezroczystości jest dwuwymiarową tablicą bajtów o rozmiarze 256×256. Jest to po prostu tablica mapująca pary kolorów na scalony kolor i definiuje się przez jej adres wirtualny.
Polecenia¶
Użytkownik zleca urządzeniu pracę przez przygotowanie bufora poleceń i przekazanie jego adresu oraz rozmiaru do jądra, które poprosi urządzenie o wykonanie go. Bufor poleceń składa się z 32-bitowych słów w formacie little-endian. Polecenia składają się z wielu słów.
Urządzenie obsługuje 7 różnych poleceń:
0
:FILL_RECT
— wypełnia prostokątny obszar jednym kolorem.1
:DRAW_LINE
— rysuje prostą jednokolorową linię między dwoma punktami.2
:BLIT
— kopiuje prostokątny obszar między dwoma buforami ramki.3
:WIPE
— rysuje efekt przejścia między ekranami4
:DRAW_COLUMNS
— rysuje kolumny pikseli używając tekstury kolumnowej.5
:DRAW_FUZZ
— aplikuje efekt rozmytego cienia na kolumnie pikseli.6
:DRAW_SPANS
— rysuje paski pikseli używając tekstury płaskiej.
Typ polecenia jest określony przez niskie 8 bitów pierwszego słowa polecenia.
Wszystkie nieużywane pola muszą być ustawione na 0 — w przeciwnym wypadku, zachowanie urządzenia nie jest zdefiniowane.
Polecenie FILL_RECT
¶
Polega na wypełnieniu zadanego prostokąta w buforze ramki jednym kolorem. Składa się z 5 słów:
słowo 0: nagłówek
bity 0-7: typ polecenia (0)
bity 8-15: kolor do wypełnienia
słowo 1: wskaźnik na bufor ramki
słowo 2: przeskok bufora ramki
słowo 3: współrzędne lewego górnego rogu prostokąta
bity 0-15: X
bity 16-31: Y
słowo 4: wymiary prostokąta
bity 0-15: szerokość
bity 16-31: wysokość
Jest używane w grze do rysowania tła mapy (dostępnej pod klawiszem Tab).
Polecenie DRAW_LINE
¶
Rysuje jednokolorową linię prostą o szerokości 1 piksela między podanymi dwoma punktami w zadanym buforze ramki. Składa się z 5 słów:
słowo 0: nagłówek
bity 0-7: typ polecenia (1)
bity 8-15: kolor linii
słowo 1: wskaźnik na bufor ramki
słowo 2: przeskok bufora ramki
słowo 3: współrzędne jednego końca linii
bity 0-15: X
bity 16-31: Y
słowo 4: współrzędne drugiego końca linii
bity 0-15: X
bity 16-31: Y
Jest używane w grze do rysowania mapy (dostępnej pod klawiszem Tab).
Polecenie BLIT
¶
Polega na skopiowaniu zadanego prostokąta z jednego bufora ramki bądź tekstury płaskiej do drugiego bufora ramki, być może ze skalowaniem. Składa się z 9 słów:
słowo 0: nagłówek
bity 0-7: typ polecenia (2)
bity 16-20:
ULOG
(log2 z szerokości źródłowego bufora — wszystkie współrzędne U będą brane modulo2**ULOG
)bity 24-28:
VLOG
(log2 z wysokości źródłowego bufora — wszystkie współrzędne V będą brane modulo2**VLOG
)
słowo 1: wskaźnik na docelowy bufor ramki
słowo 2: przeskok docelowego bufora ramki
słowo 3: współrzędne lewego górnego rogu docelowego prostokąta
bity 0-15: X
bity 16-31: Y
słowo 4: wymiary docelowego prostokąta
bity 0-15: szerokość
bity 16-31: wysokość
słowo 5: wskaźnik na źródłowy bufor ramki
słowo 6: przeskok źródłowego bufora ramki
słowo 7: współrzędne lewego górnego rogu źródłowego prostokąta
bity 0-15: U (czyli X)
bity 16-31: V (czyli Y)
słowo 8: wymiary źródłowego prostokąta
bity 0-15: szerokość
bity 16-31: wysokość
Polecenie BLIT
nie powinno być wykonywane, gdy kopiujemy wewnątrz
jednego bufora, a prostokąt źródłowy ma część wspólną z prostokątem docelowym
– wynik jest wtedy niedeterministyczny.
Polecenie jest używane w grze do skopiowania przygotowanego wcześniej panelu dolnego, do rysowania tła do ekranu opcji, tła do tekstu “fabuły” wyświetlanego po ukończeniu niektórych poziomów, oraz jako obramowanie ekranu w przypadku wybrania pola widzenia mniejszego niż ekran (klawisz -).
Polecenie WIPE
¶
Rysuje efekt przejścia między dwoma ekranami (“wipe” albo “melt”). Składa się z 9 słów nagłówka + 1 słowa na każdą kolumnę przetwarzanego prostokąta. Nagłówek jest następujący:
słowo 0: typ polecenia (3)
słowo 1: wskaźnik na docelowy bufor ramki
słowo 2: przeskok docelowego bufora ramki
słowo 3: współrzędne lewego górnego rogu prostokąta
bity 0-15: X
bity 16-31: Y
słowo 4: wymiary prostokąta
bity 0-15: szerokość
bity 16-31: wysokość
słowo 5: wskaźnik na pierwszy źródłowy bufor ramki
słowo 6: przeskok pierwszego źródłowego bufora ramki
słowo 7: wskaźnik na drugi źródłowy bufor ramki
słowo 8: przeskok drugiego źródłowego bufora ramki
Dla każdej kolumny (czyli tyle razy, ile wynosi szerokość prostokąta w pikselach):
słowo 0: offset Y danej kolumny — pierwsze
offset
pikseli będzie brane z pierwszego źródłowego bufora, reszta z drugiego źródłowego bufora.
Jest używane w grze przy zmianie poziomów, rozpoczęciu/zakończeniu gry, itp.
Polecenie DRAW_COLUMNS
¶
Rysuje teksturowane kolumny do bufora ramki. Teksturowane piksele mogą być jednocześnie przetwarzane przez dwie mapy kolorów: mapę A, wspólną dla wszystkich kolumn w poleceniu, i mapę B, wybieraną osobno dla każdej kolumny. Składa się z nagłówka wybierającego opcje operacji i bufor docelowy oraz ze zmiennej liczby opisów kolumn. Nagłówek wygląda następująco:
słowo 0: nagłówek
bity 0-7: typ polecenia (4)
bit 8:
CMAP_A_EN
— jeśli ustawiony, włącza mapę kolorów Abit 9:
CMAP_B_EN
— jeśli ustawiony, włącza mapę kolorów Bbit 12:
TRANS_EN
— jeśli ustawiony, włącza mapę przezroczystościbity 16-31: liczba kolumn do narysowania
słowo 1: wskaźnik na docelowy bufor ramki
słowo 2: przeskok docelowego bufora ramki
słowo 3 (obecne tylko jeśli
CMAP_A_EN
): wskaźnik na mapę kolorów Asłowo 4 (obecne tylko jeśli
TRANS_EN
): wskaźnik na mapę przezroczystości
Po nagłówku następują opisy kolumn, każdy w następującym formacie:
słowo 0:
bity 0-15: współrzędna X kolumny
bity 16-31:
SRC_HEIGHT
— wysokość tekstury
słowo 1:
bity 0-15: początkowa (górna) współrzędna Y kolumny
bity 16-31: końcowa (dolna) współrzędna Y kolumny
słowo 2:
SRC_PTR
— wskaźnik na kolumnę w teksturzesłowo 3:
USTART
— początkowa współrzędna w teksturze (dla górnego piksela), zapisana jako zmiennoprzecinkowa liczba z 16 bitami całkowitymi i 16 bitami po przecinkusłowo 4:
USTEP
— przeskok współrzędnej w teksturze dla kolejnych pikselisłowo 5 (obecne tylko jeśli
CMAP_B_EN
): wskaźnik na mapę kolorów B
Rysowana kolumna jest teksturowana za pomocą następującego algorytmu:
# Obliczenie koloru dla piksela (x, y).
coord = (USTART + USTEP * (y - Y0)) >> 16
coord %= SRC_HEIGHT
color = *(SRC_PTR + coord)
if CMAP_A_EN:
color = *(CMAP_A_PTR + color)
if CMAP_B_EN:
color = *(CMAP_B_PTR + color)
if TRANS_EN:
color = *(TRANSMAP_PTR + (orig_color << 8) + color)
Polecenie DRAW_FUZZ
¶
Aplikuje efekt rozmytego cienia na kolumnach w buforze ramki. Składa się z nagłówka wybierającego opcje operacji i bufor docelowy oraz ze zmiennej liczby opisów kolumn. Nagłówek wygląda następująco:
słowo 0: nagłówek
bity 0-7: typ polecenia (5)
bity 16-31: liczba kolumn do narysowania
słowo 1: wskaźnik na docelowy bufor ramki
słowo 2: przeskok docelowego bufora ramki
słowo 3:
bity 0-15:
FUZZSTART
— najmniejsza współrzędna Y, którą wolno przeczytać algorytmowibity 16-31:
FUZZEND
— największa współrzędna Y, którą wolno przeczytać algorytmowi
słowo 4: wskaźnik na mapę kolorów
Po nagłówku następują opisy kolumn, każdy w następującym formacie:
słowo 0:
bity 0-15: współrzędna X kolumny
bity 16-21:
FUZZPOS
— źródło losowości dla algorytmu (z zakresu 0-49)
słowo 1:
bity 0-15: początkowa (górna) współrzędna Y kolumny
bity 16-31: końcowa (dolna) współrzędna Y kolumny
Polecenie DRAW_SPANS
¶
Rysuje teksturowane paski do bufora ramki. Opcjonalnie aplikuję mapy kolorów na paski. Składa się z nagłówka wybierającego opcje operacji, bufor docelowy, i teksturę płaską oraz ze zmiennej liczby opisów pasków. Nagłówek wygląda następująco:
słowo 0: nagłówek
bity 0-7: typ polecenia (6)
bit 8:
CMAP_EN
— jeśli ustawiony, włącza mapę kolorówbit 12:
TRANS_EN
— jeśli ustawiony, włącza mapę przezroczystościbity 16-20:
ULOG
(log2 z szerokości źródłowego bufora — wszystkie współrzędne U będą brane modulo2**ULOG
)bity 24-28:
VLOG
(log2 z wysokości źródłowego bufora — wszystkie współrzędne V będą brane modulo2**VLOG
)
słowo 1: wskaźnik na docelowy bufor ramki
słowo 2: przeskok docelowego bufora ramki
słowo 3: współrzędnie Y
bity 0-15:
Y0
— współrzędna Y pierwszego paskabity 16-31:
Y1
— współrzędna Y ostatniego paska
Współrzędne mogą być podane w dowolnej kolejności — jeśli
Y1 > Y0
, współrzędne kolejnych pasków będą rosnąć, a jeśliY1 < Y0
, będą maleć.słowo 4: wskaźnik na źródłowy bufor tekstury płaskiej
słowo 5: przeskok źródłowego bufora tekstury płaskiej
słowo 6 (obecne tylko jeśli
TRANS_EN
): wskaźnik na mapę przezroczystości
Po nagłówku następuje abs(Y0 - Y1) + 1
opisów pasków, każdy w następującym formacie:
słowo 0:
bity 0-15: początkowa (lewa) współrzędna X paska
bity 16-31: końcowa (prawa) współrzędna X paska
słowo 1:
USTART
— początkowa współrzędna U w teksturze (dla lewego piksela), zapisana jako zmiennoprzecinkowa liczba z 16 bitami całkowitymi i 16 bitami po przecinkusłowo 2:
VSTART
— początkowa współrzędna V w teksturzesłowo 3:
USTEP
— przeskok współrzędnej U w teksturze dla kolejnych pikselisłowo 4:
VSTEP
— przeskok współrzędnej V w teksturze dla kolejnych pikselisłowo 5 (jeśli
CMAP_EN
): wskaźnik na mapę kolorów
Rysowany pasek jest teksturowany za pomocą następującego algorytmu:
# Obliczenie koloru dla piksela (x, y).
u = (USTART + USTEP * (x - X0)) >> 16 % (1 << ULOG)
v = (VSTART + VSTEP * (x - X0)) >> 16 % (1 << VLOG)
color = *(SRC_PTR + u + SRC_PITCH * v)
if CMAP_EN:
color = *(CMAP_PTR + color)
if TRANS_EN:
color = *(TRANSMAP_PTR + (orig_color << 8) + color)