Urządzenie Final HardDoom™¶
Grafika w grze Final 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 Final 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 Final 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
0x1996
.
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.
Bufory i blok stronicowania¶
Wszystkie dane i polecenia używane przez urządzenie są zawarte w stronicowanych
buforach. Urządzenie może jednocześnie używać do 64 buforów — blok stronicowania
ma 64 “sloty” (identyfikowane przez liczby 0..63), w które można wpiąć bufor
za pomocą polecenia BIND_SLOT
.
Każdy slot ma następujące atrybuty, wybierane przy wpięciu bufora:
PRESENT
: jeśli ustawiony, do slota wpięty jest bufor, ma tabelę stron, i może być użyty przez urządzenie; jeśli nie jest ustawiony, każda próba użycia tego slotu spowoduje błądWRITABLE
: jeśli ustawiony, bufor podpięty przez ten slot jest dostępny do odczytu i zapisu; jeśli nie jest ustawiony, odczyty są dozwolone, ale zapisy będą powodować błądUSER
: jeśli ustawiony, ten slot może być wykorzystywany przez wszystkie polecenia; jeśli nie jest ustawiony, dozwolne jest wyłącznie użycie go jako głównego bufora poleceń bądź jako parametr poleceniaCALL
, a użycie go przez polecenia użytkownika spowoduje błądPT_ADDR
: adres fizyczny tabeli stronPITCH
: przeskok pomiędzy kolejnymi liniami (pitch): istotne tylko, gdy dany slot zostanie użyty jako bufor ramki bądź bufor tekstur płaskich; musi być wielokrotnością 64 bajtów i być mniejszy niż 4 MiB
Urządzenie używa 40-bitowych adresów fizycznych (zarówno dla buforów jak i dla tabel stron), 22-bitowych adresów wirtualnych w ramach bufora, i stron o rozmiarze 4kiB. Tabele stron używane przez urządzenie są jednopoziomowe:
jądro podaje urządzeniu adres fizyczny katalogu stron przy podpinaniu bufora
bity 12-21 adresu wirtualnego wybierają wpis w tabeli stron (page table), który zawiera adres fizyczny strony
bity 0-11 adresu wirtualnego to offset wewnątrz strony
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 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
).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 nie czytać ciągle tabeli stron, urządzenie może zapamiętywać
dane z tabel i katalogów stron w buforach TLB. Wykonanie polecenia BIND_SLOT
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
w rejestrze RESET
.
Jeśli urządzenie napotka niepoprawny adres wirtualny (bez flagi PRESENT
w slocie bądź w tabeli stron), 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 tabelę
stron) i wznowić zadanie, bądź też przerwać zadanie przez wykonanie
procedury resetu urządzenia.
Aby wznowić zadanie, należy poprawić odpowiednie wpisy w tabeli 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_CMD_MAIN
: blokCMD
, wczytywanie poleceń z głównego buforaPAGE_FAULT_CMD_SUB
: blokCMD
, wczytywanie poleceń z bufora wywołanego przezCALL
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 + 0x0540: MMU_CLIENT_VA[MMU_CLIENT_CMD_MAIN]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_CMD_MAIN
. Bity 0-21 to właściwy adres wirtualny, bity 24-29 to numer slotu.BAR0 + 0x0544: MMU_CLIENT_VA[MMU_CLIENT_CMD_SUB]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_CMD_SUB
.BAR0 + 0x0548: MMU_CLIENT_VA[MMU_CLIENT_SRD]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_SRD
.BAR0 + 0x054c: MMU_CLIENT_VA[MMU_CLIENT_SWR_DST]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_SWR_DST
.BAR0 + 0x0550: MMU_CLIENT_VA[MMU_CLIENT_COL_CMAP_B]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_COL_CMAP_B
.BAR0 + 0x0554: MMU_CLIENT_VA[MMU_CLIENT_COL_SRC]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_COL_SRC
.BAR0 + 0x0558: MMU_CLIENT_VA[MMU_CLIENT_SPAN_SRC]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_SPAN_SRC
.BAR0 + 0x055c: MMU_CLIENT_VA[MMU_CLIENT_SWR_TRANSMAP]
Adres wirtualny odpowiadający błędowi
PAGE_FAULT_SWR_TRANSMAP
.
Wysyłanie poleceń¶
Urządzenia używa się przez wysyłanie mu poleceń. Polecenia składają się ze zmiennej liczby 32-bitowych słów w formacie little-endian. Istnieje 12 różnych typów poleceń, rozróżnianych po niskich 4 bitach pierwszego słowa.
Wszystkie nieużywane pola muszą być ustawione na 0 — w przeciwnym wypadku, zachowanie urządzenia nie jest zdefiniowane.
Istnieją trzy sposoby na wysłanie poleceń do urządzenia:
CMD_MANUAL
— ręczne wysyłanie, przez zapis kolejnych słów polecenia do rejestru MMIO urządzenia.CMD_MAIN
— urządzenie automatycznie wczytuje polecenia z głównego bufora poleceń, który jest cyklicznym buforem 32-bitowych słów z dwoma rejestrami wskaźnikowymi opisującymi obecne zajęcie bufora. Nie może być używany jednocześnie zCMD_MANUAL
.CMD_SUB
— urządzenie automatycznie wczytuje polecenia z bufora poleceń użytkownika, który jest prostą tablicą słów opisaną przez początek i koniec. Aby użyć tego sposobu, należy wysłać polecenieCALL
opisujące bufor używającCMD_MANUAL
bądźCMD_MAIN
— urządzenie wtedy natychmiast zawiesi przetwarzanie kolejnych poleceń poCALL
i zacznie zamiast tego przetwarzać polecenia z wywołanego bufora, po czym (po dojściu do końca) wróci do przetwarzania poprzedniego strumienia poleceń. W tym buforze dozwolone jest używanie wyłącznie poleceń użytkownika — próba wysłania przezCMD_SUB
poleceńBIND_SLOT
,CLEAR_SLOTS
,CALL
, bądźFENCE
spowoduje błąd.
Ręczne wysyłanie poleceń¶
Polecenia można wysyłać do urządzenia bezpośrednio przez MMIO, używając następujących rejestrów:
BAR0 + 0x008c: CMD_MANUAL_FREE
Rejestr tylko do odczytu. Odczytana wartość jest liczbą wolnych miejsc w kolejce poleceń (tyle słów można natychmiast zapisać do
CMD_MANUAL_FEED
bez obaw oFEED_OVERFLOW
). Kolejka ta ma maksymalną pojemność 255 słów. Ten rejestr będzie zawsze równy 0, jeśli włączony jest buforCMD_MAIN
. Można założyć, że ten rejestr będzie równy 255 bezpośrednio po resecie urządzenia bądź gdy zostaną przetworzone wszystkie wcześniej wysłane polecenia.BAR0 + 0x008c: CMD_MANUAL_FEED
Rejestr tylko do zapisu. Każdy zapis do tego rejestru wysyła jedno słowo do kolejki poleceń. Jeśli kolejka jest już pełna (
CMD_MANUAL_FREE == 0
), zamiast tego wyzwalane jest przerwanieFEED_ERROR
.
Automatyczne wczytywanie poleceń¶
Blok CMD_MAIN
pozwala na automatyczne wczytywanie polecań przez urządzenie —
sterownik zapisuje polecenia do wykonania w cyklicznym buforze,
a urządzenie będzie je automatycznie wczytywać do swojej kolejki.
Aby użyć tego bloku, należy stworzyć bufor zadań, wpiąć go do jednego ze slotów
(przez wysłanie polecenia BIND_SLOT
za pomocą CMD_MANUAL_FEED
i oczekiwania
na zakończenie go), po czym użyć następujących rejestrów:
BAR0 + 0x0080: CMD_MAIN_SETUP
Ustawienia głównego bufora poleceń:
bity 0-21:
WRAP
: adres końca bufora poleceń; gdy urządzenie dojdzie do tego adresu, automatycznie wróci do adresu 0.bity 24-29:
SLOT
: numer slotu zawierającego główny bufor poleceń.bit 31:
ENABLE
: włączenie automatycznego wczytywania poleceń. Gdy ten bit jest ustawiony, automatyczne wczytywanie poleceń jest aktywne i urządzenie będzie wczytywało polecenia zawsze, gdyCMD_MAIN_GET != CMD_MAIN_PUT
. Ustawienie tego bitu jednocześnie wyłącza możliwość użyciaCMD_MANUAL_FEED
.
BAR0 + 0x0084: CMD_MAIN_GET
Adres wirtualny, z którego urządzenie ma wczytać następne słowo do kolejki poleceń. Po wczytaniu tego słowa, ten wskaźnik zostanie automatycznie zwiększony o 4 przez urządzenie. Gdyby to zwiększenie miało spowodować, że
CMD_MAIN_GET == CMD_MAIN_SETUP.WRAP
, wskaźnik jest zamiast tego ustawiany z powrotem na 0.BAR0 + 0x0088: CMD_MAIN_PUT
Adres wirtualny, pod którym jądro ma zapisać następne słowo do wykonania. Jeśli
CMD_MAIN_GET
będzie równyCMD_MAIN_PUT
, oznacza to, że urządzenie obecnie nie ma żadnych poleceń do wczytania (i będzie czekać aż jądro zapisze kolejne słowo i zwiększyCMD_MAIN_PUT
).
Wszystkie adresy używane przez blok CMD_MAIN
muszą być wielokrotnością 4 bajtów
(czyli rozmiaru słowa).
Blok CMD_MAIN
można opisać następującym pseudokodem:
while True:
# Gdy jest miejsce w kolejce, automatyczne wczytywanie jest włączone, a bufor
# nie jest pusty...
if internal_fifo.free != 0 and CMD_MAIN_GET != CMD_MAIN_PUT and CMD_MAIN_SETUP.ENABLE:
# Wczytaj słowo z adresu GET.
cmd = mem_read_le32(CMD_MAIN_SETUP.SLOT, CMD_MAIN_GET)
# Zapisz do kolejki.
internal_fifo.push(cmd)
# Podbij GET, być może zastosuj wrap.
CMD_MAIN_GET += 4
if CMD_MAIN_GET == CMD_MAIN_SETUP.WRAP:
CMD_MAIN_GET = 0
Polecenie NOP
¶
Nic nie robi. Składa się z 1 słowa. Może być użyte do zapchania nieużywanych słów w buforze poleceń, jeśli czujemy taką potrzebę.
słowo 0: nagłówek
bity 0-3: typ polecenia (
0x0
)
Polecenie BIND_SLOT
¶
Służy do wpięcia bufora do slotu. Składa się z 2 słów:
słowo 0: nagłówek
bity 0-3: typ polecenia (
0x8
)bity 4-9:
SLOT
— numer slotu, który przepinamybity 10-25:
PITCH
— przeskok bufora, przesunięty w prawo o 6 bitów (jeśli nie zamierzamy używać tego bufora jako bufora ramki ani tekstury płaskiej, możemy tu wpisać 0).
słowo 1:
bit 0:
PRESENT
bit 1:
WRITABLE
bit 2:
USER
bity 4-31:
PA
: adres fizyczny tabeli stron, przsunięty w prawo o 12 bitów
To polecenie może być wysłane tylko przez CMD_MANUAL
bądź CMD_MAIN
.
Jeśli flaga PRESENT
jest ustawiona na 0, wartość reszty pól nie ma znaczenia.
Polecenie CLEAR_SLOTS
¶
Służy do wyczysczenia slotów. Składa się z 3 słów:
słowo 0: nagłowek
bity 0-3: typ polecenia (
0x9
)
słowo 1: bity 0-31 maski slotów do wyczyszczenia
słowo 2: bity 32-63 maski
Polecenie czyści wpięcia wszystkich slotów, których odpowiadający bit w masce
jest zapalony. Jest równoważne wysłaniu odpowiedniej liczby poleceń BIND_SLOT
z wyłączoną flagą PRESENT
.
To polecenie może być wysłane tylko przez CMD_MANUAL
bądź CMD_MAIN
.
Polecenie CALL
¶
Zawiesza przetwarzanie poleceń z głównej kolejki poleceń, rozpoczyna wczytywanie i przetwarzanie poleceń z podanego bufora użytkownika. Składa się z 2 słów.
słowo 0: nagłówek
bity 0-3: typ polecenia (
0xa
)bity 4-9:
SLOT
— wybiera slot zawierający polecenia do wykonaniabity 10-29:
ADDR
— adres wirtualny początku poleceń w buforze, przesunięty w prawo o 2 bity
słowo 1: długość poleceń, w bajtach; musi być wielokrotnością 4 i być nie większa niż 4MiB.
Wskazane polecenia zostaną wykonane bezpośrednio po poleceniu CALL
i przed kolejnym
poleceniem wysłanym po CALL
przez CMD_MAIN
bądź CMD_MANUAL
.
To polecenie może być wysłane tylko przez CMD_MANUAL
bądź CMD_MAIN
.
Polecenie FENCE
¶
Służy do powiadamiania sterownika o zakończeniu wykonywania wszystkich poprzednich poleceń. Składa się z 1 słowa.
słowo 0: nagłówek
bity 0-3: typ polecenia (
0xb
)bity 4-31:
VAL
— parametr; nie ma zdefinowanej semantyki przez urządzenie, zalecamy użycie go jako numeru kolejnej serii poleceń (modulo2**28
).
To polecenie może być wysłane tylko przez CMD_MANUAL
bądź CMD_MAIN
.
To polecenie ma następujące efekty:
Oczekuje na pełne zakończenie wykonania wszystkich wcześniejszych poleceń.
Ustawia wartość rejestru
CMD_FENCE_LAST
na parametrVAL
Jeśli parametr
VAL
jest równy rejestrowiCMD_FENCE_WAIT
, wyzwala przerwanieFENCE_WAIT
.
BAR0 + 0x0090: CMD_FENCE_LAST
28-bitowy rejestr do odczytu i zapisu, ustawiany na parametr
VAL
przy wykonaniu poleceniaFENCE
. Może być użyty do ustalenia, które polecenia już zostały wykonane. Zalecamy zapisanie go ze sterownika dokładnie raz, przed uruchomieniem urządzenia, aby nadać mu wartość początkową.BAR0 + 0x0094: CMD_FENCE_WAIT
Rejestr do odczytu i zapisu, służy do wyzwolenia przerwania gdy podane polecenie
FENCE
zostanie wykonane.bity 0-27:
VAL
, wartość na którą sterownik czeka. Gdy zostanie wykonane polecenieFENCE
z równą wartością, a bitDISABLE
nie jest ustawiony, zostje wyzwolone przerwanieFENCE_WAIT
.bit 31:
DISABLE
— jeśli ustawiony, powyższa wartość jest ignorowana, a przerwanieFENCE_WAIT
nie jest wyzwalane.
Błąd CMD_ERROR
¶
Błąd CMD_ERROR
jest zgłaszany przez blok CMD
w przypadku napotkania
problemu przez firmware — oznacza to wykryte błędne polecenie.
Można się dowiedzieć więcej o źródle błędu, czytając następujące rejestry:
BAR0 + 0x0098: CMD_ERROR_CODE
Kod błędu. Jeden z:
0x0
:SUB_INCOMPLETE
— polecenie wysłane przez bufor użytkownika (CMD_SUB
) zostało ucięte przez koniec listy poleceń.CMD_ERROR_DATA
to adres wirtualny końca listy poleceń.0x1
:UNK_COMMAND
— nieznany typ polecenia.CMD_ERROR_DATA
nie jest ustawiany.0x2
:PRIV_COMMAND
— uprzywilejowane polecenie wysłane przez bufor użytkoenika.CMD_ERROR_DATA
nie jest ustawiany.0x3
:INVALID_SLOT
— użycie niepodpiętego slotu.CMD_ERROR_DATA
zawiera numer slotu.0x4
:KERNEL_SLOT
— użycie slotu bez atrybutuUSER
w poleceniu użytkownika.CMD_ERROR_DATA
zawiera numer slotu.0x5
:RO_SLOT
— użycie slotu bez atrybutuWRITABLE
do zapisu.CMD_ERROR_DATA
zawiera numer slotu.0x6
: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.CMD_ERROR_DATA
zawiera błędne słowo polecenia.0x7
:DRAW_SPANS_X_REV
— początkowa współrzędna x w poleceniuDRAW_SPANS
jest większa niż końcowa wspołrzędna.CMD_ERROR_DATA
zawiera błędne słowo polecenia.
BAR0 + 0x009c: CMD_ERROR_DATA
Informacja o błędzie (zależna od kodu błędu).
BAR0 + 0x00a0: CMD_INFO
Informacja o źródle błędnego polecenia:
bity 0-21:
PTR
, adres wirtualny nagłówka poleceniabity 24-29:
SLOT
, z którego polecenie było wczytanebit 30:
SUB
— jeśli ustawione, polecenie zostało wczytane z bufora użytkownika (wywołanego przezCALL
).bit 31:
MANUAL
— jeśli ustawione, polecenie było wysłane przezCMD_MANUAL_FEED
i polaSLOT
orazPTR
są nieważne.
BAR0 + 0x00a4: CMD_HEADER
Kopia nagłówka (słowa 0) błędnego polecenia.
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:
CMD
— wczytywanie poleceńbit 1:
FE
— przetwarzanie poleceń przez firmwarebit 2:
SRD
— proste wczytywanie danychbit 3:
SPAN
— teksturowanie paskówbit 4:
COL
— teksturowanie kolumnbit 5:
FX
— efekty specjalne (FUZZ
, mapy kolorów)bit 6:
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
,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 0:
CMD
— wczytywanie poleceńbit 1:
FE
— przetwarzanie poleceń przez firmwarebit 2:
SRD
— proste wczytywanie danychbit 3:
SPAN
— teksturowanie paskówbit 4:
COL
— teksturowanie kolumnbit 5:
FX
— efekty specjalne (FUZZ
, mapy kolorów)bit 6:
SWR
— mapy przezroczystości i zapis do bufora ramki.bit 7:
MMU
— blok stronicowaniabit 8:
STATS
— blok statystyk. Wszystkie liczniki są ustawiane na 0.bit 9:
TLB
— wpisy TLB. Wszystkie wpisy tabel i katalogów stron zapisane w TLB są kasowane i, w razie potrzeby, zostaną wczytane na nowo.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ąc0x7f7ff3ff
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.
Przerwania¶
Urządzenie wewnętrznie używa 12 przerwań (które są agregowane w jedno przerwanie PCI):
FENCE_WAIT
— przerwanie używane do powiadomienia sterownika o wykonaniu wybranego poleceniaFENCE
przez blokCMD
.FEED_ERROR
— użycie rejestruCMD_MANUAL_FEED
, gdy nie ma miejsca w kolejce, bądź gdy aktywne jest automatyczne wczytywanie poleceń.CMD_ERROR
— wyzwalane, gdy firmware zauważy błędne polecenie.FE_ERROR
— wyzwalane, gdy firmware napotka wewnętrzny błąd.PAGE_FAULT_CMD_MAIN
— wyzwalane, gdy blokCMD
spowoduje błąd strony przy wczytywaniu głównego bufora poleceńPAGE_FAULT_CMD_SUB
— wyzwalane, gdy blokCMD
spowoduje błąd strony przy wczytywaniu poleceń użytkownikaPAGE_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:
FENCE_WAIT
,bit 1:
FEED_ERROR
,bit 2:
CMD_ERROR
,bit 3:
FE_ERROR
,bit 8:
PAGE_FAULT_CMD_MAIN
bit 9:
PAGE_FAULT_CMD_SUB
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 0xa spowoduje wyzerowanie przerwań
FE_ERROR
orazFEED_ERROR
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/fharddoom/blob/main/fdoomfw.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
fdoomfw[]
doFE_CODE_WINDOW
,zresetować wszystkie bloki urządzenia przez zapis
0x7f7ff3ff
doRESET
,wyzerować
INTR
przez zapis0xff0f
,włączyć używane przez nas przerwania w
INTR_ENABLE
,włączyć wszystkie bloki urządzenia w
ENABLE
.zainicjować
CMD_FENCE_LAST
iCMD_FENCE_WAIT
, jeśli czujemy taką potrzebę.
Jeśli do tego chcemy użyć automatycznego wczytywania poleceń:
skonstruować tabelę stron głównego bufora poleceń
wysłać ręcznie (przez
CMD_MANUAL_FEED
) polecenieBIND_SLOT
wpinające ten bufor w któryś slot (w zadaniu sloty 60-63 są zarezerwowane dla jądra)poczekać na zakończenie tego polecenia (przez czekanie na wyzerowanie rejestru
STATUS
bądź wysłanie poleceniaFENCE
po nim)zainicjować
CMD_MAIN_GET
iCMD_MAIN_PUT
zainicjować
CMD_MAIN_SETUP
Ż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:
zatrzymać wszystkie bloki urządzenia przez zapis
0
doENABLE
zresetować wszystkie bloki urządzenia przez zapis
0x7f7ff3ff
doRESET
,wyzerować
INTR
przez zapis0xff0f
,włączyć wszystkie bloki urządzenia w
ENABLE
zainicjować
CMD_FENCE_LAST
iCMD_FENCE_WAIT
, jeśli czujemy taką potrzebę.
Jeśli do tego używamy automatycznego wczytywania poleceń:
przewinąć wskaźnik
CMD_MAIN_GET
tak, by wskazywał na następne zadanie po tym nieudanymdobrym pomysłem będzie również usunięcie wszystkich innych zadań wysłanych przez ten sam kontekst (zadanie można efektywnie usunąć przez nadpisanie jego polecenia
CALL
przezNOP
)wysłać ręcznie (przez
CMD_MANUAL_FEED
) polecenieBIND_SLOT
wpinające ten bufor w któryś slot (w zadaniu sloty 60-63 są zarezerwowane dla jądra)poczekać na zakończenie tego polecenia (przez czekanie na wyzerowanie rejestru
STATUS
bądź wysłanie poleceniaFENCE
po nim)zainicjować
CMD_MAIN_GET
iCMD_MAIN_PUT
zainicjować
CMD_MAIN_SETUP
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:
CMD
— wczytywanie poleceń użytkownika.bit 1:
FE
— przetwarzanie poleceń przez firmware.bit 2:
SRD
— proste wczytywanie danychbit 3:
SPAN
— teksturowanie paskówbit 4:
COL
— teksturowanie kolumnbit 5:
FX
— efekty specjalne (FUZZ
, mapy kolorów)bit 6:
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: tabelą stron
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 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 indeks slotu bufora z teksturą, adres
wirtualny kolumny tekstury w tym buforze, 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 Final 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:
indeks slotu bufora z teksturą
przeskok (jak przy buforach ramki)
log2 z szerokości (
ULOG
) — 6 w oryginalnej grzelog2 z wysokości (
VLOG
) — 6 w oryginalnej grze
Tak jak przy teksturach kolumnowych, współrzędne są zawijane modulo
wymiary tekstury gdy wyjadą poza ramy zdefiniowane przez ULOG
i VLOG
. Dokładny sposób zawijania jest jednak inny niż przy teksturach
kolumnowych: o ile przy teksturach kolumnowych współrzędna jest po prostu
brana modulo wysokość, o tyle przy teksturach płaskich oryginalne współrzędne
są brane bez zmian, za to blokowana jest modyfikacja bitów powyżej
ULOG
/VLOG
-tego bitu współrzędnej przy przechodzeniu do kolejnych
tekseli (co efektywnie zawija teksturowanie w ramach jednego prostokąta
2**ULOG×2**VLOG
w ramach większego prostokąta, pozwalająć na załadowanie
całego atlasu tekstur do jednego bufora i wybranie konkretnej tekstury
przez wysokie bity współrzędnych).
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. 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:
indeks slotu bufora z mapami kolorów
indeks konkretnej mapy kolorów w tym buforze (jest mnożony przez 256 aby otrzymać adres wirtualny tej mapy)
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. Definiuje się ją przez:
indeks slotu bufora z mapami przezroczystości
indeks konkretnej mapy przezroczystości w tym buforze (jest mnożony przez 65536 aby otrzymać adres wirtualny tej mapy)
Polecenie FILL_RECT
¶
Polega na wypełnieniu zadanego prostokąta w buforze ramki jednym kolorem. Składa się z 3 słów:
słowo 0: nagłówek
bity 0-3: typ polecenia (
0x1
)bity 4-9:
SLOT_DST
— numer slotu z docelowym buforem ramkibity 24-31: kolor do wypełnienia
słowo 1: współrzędne lewego górnego rogu prostokąta
bity 0-15: X
bity 16-31: Y
słowo 2: 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 3 słów:
słowo 0: nagłówek
bity 0-3: typ polecenia (
0x2
)bity 4-9:
SLOT_DST
— numer slotu z docelowym buforem ramkibity 24-31: kolor linii
słowo 1: współrzędne jednego końca linii
bity 0-15: X
bity 16-31: Y
słowo 2: 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 5 słów:
słowo 0: nagłówek
bity 0-3: typ polecenia (
0x3
)bity 4-9:
SLOT_DST
— numer slotu z docelowym buforem ramkibity 16-21:
SLOT_SRC
— numer slotu ze źródłowym buforem ramki bądź teksturą płaskąbity 22-26:
ULOG
(log2 z szerokości źródłowego bufora — wszystkie współrzędne U będą brane modulo2**ULOG
)bity 27-31:
VLOG
(log2 z wysokości źródłowego bufora — wszystkie współrzędne V będą brane modulo2**VLOG
)
słowo 1: współrzędne lewego górnego rogu docelowego prostokąta
bity 0-15: X
bity 16-31: Y
słowo 2: wymiary docelowego prostokąta
bity 0-15: szerokość
bity 16-31: wysokość
słowo 3: 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 4: 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 3 słów nagłówka + 1 słowa na każdą kolumnę przetwarzanego prostokąta. Nagłówek jest następujący:
słowo 0:
bity 0-3: typ polecenia (
0x4
)bity 4-9:
SLOT_DST
— numer slotu z docelowym buforem ramkibity 16-21:
SLOT_SRC_A
— numer slotu z pierwszym źródłowym buforem ramkibity 24-29:
SLOT_SRC_B
— numer slotu z drugim źródłowym buforem ramki
słowo 1: współrzędne lewego górnego rogu prostokąta
bity 0-15: X
bity 16-31: Y
słowo 2: wymiary prostokąta
bity 0-15: szerokość
bity 16-31: wysokość
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-3: typ polecenia (
0x5
)bity 4-9:
SLOT_DST
— numer slotu z docelowym buforem ramkibit 12:
CMAP_A_EN
— jeśli ustawiony, włącza mapę kolorów Abit 13:
CMAP_B_EN
— jeśli ustawiony, włącza mapę kolorów Bbit 14:
TRANS_EN
— jeśli ustawiony, włącza mapę przezroczystościbity 16-31: liczba kolumn do narysowania
słowo 1 (obecne tylko jeśli
CMAP_A_EN
lubTRANS_EN
):bity 0-5:
SLOT_CMAP_A
— numer slotu z mapą kolorów Abity 6-19:
CMAP_A_IDX
— indeks mapy kolorów A w buforzebity 20-25:
SLOT_TRANSMAP
— numer slotu z mapą przezroczystościbity 26-31:
TRANSMAP_IDX
— indeks mapy przezroczystości w buforze
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:
bity 0-21:
TEX_PTR
— wskaźnik na kolumnę w teksturzebity 24-29:
SLOT_TEX
— numer slotu z teksturą
sł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
):bity 0-5:
SLOT_CMAP_B
— numer slotu z mapą kolorów Bbity 6-19:
CMAP_B_IDX
— indeks mapy kolorów B w buforze
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 = read_byte(SLOT_TEX, TEX_PTR + coord)
if CMAP_A_EN:
color = read_byte(SLOT_CMAP_A, CMAP_A_IDX << 8 | color)
if CMAP_B_EN:
color = read_byte(SLOT_CMAP_B, CMAP_B_IDX << 8 | color)
if TRANS_EN:
color = read_byte(SLOT_TRANSMAP, TRANSMAP_IDX << 16 | 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-3: typ polecenia (
0x6
)bity 4-9:
SLOT_DST
— numer slotu z docelowym buforem ramkibity 16-31: liczba kolumn do narysowania
słowo 1:
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 2:
bity 0-5:
SLOT_CMAP_A
— numer slotu z mapą kolorówbity 6-19:
CMAP_A_IDX
— indeks mapy kolorów w buforze
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. 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, 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-3: typ polecenia (
0x7
)bity 4-9:
SLOT_DST
— numer slotu z docelowym buforem ramkibit 12:
CMAP_A_EN
— jeśli ustawiony, włącza mapę kolorów Abit 13:
CMAP_B_EN
— jeśli ustawiony, włącza mapę kolorów Bbit 14:
TRANS_EN
— jeśli ustawiony, włącza mapę przezroczystościbity 16-21:
SLOT_SRC
— numer slotu ze źródłowym buforem ramki bądź teksturą płaskąbity 22-26:
ULOG
(log2 z szerokości źródłowego bufora — wszystkie współrzędne U będą brane modulo2**ULOG
)bity 27-31:
VLOG
(log2 z wysokości źródłowego bufora — wszystkie współrzędne V będą brane modulo2**VLOG
)
słowo 1 (obecne tylko jeśli
CMAP_A_EN
lubTRANS_EN
):bity 0-5:
SLOT_CMAP_A
— numer slotu z mapą kolorów Abity 6-19:
CMAP_A_IDX
— indeks mapy kolorów A w buforzebity 20-25:
SLOT_TRANSMAP
— numer slotu z mapą przezroczystościbity 26-31:
TRANSMAP_IDX
— indeks mapy przezroczystości w buforze
słowo 2: 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ć.
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 (obecne tylko jeśli
CMAP_B_EN
):bity 0-5:
SLOT_CMAP_B
— numer slotu z mapą kolorów Bbity 6-19:
CMAP_B_IDX
— indeks mapy kolorów B w buforze
Rysowany pasek jest teksturowany za pomocą następującego algorytmu:
# Obliczenie koloru dla piksela (x, y).
mask_u = (1 << ULOG << 16) - 1
mask_v = (1 << VLOG << 16) - 1
# Wysokie bity współrzędnych są stałe i brane z *START, niskie bity zmieniają się o *STEP.
ufract = (USTART & ~mask_u) | ((USTART + USTEP * (x - X0)) & mask_u)
vfract = (VSTART & ~mask_v) | ((VSTART + VSTEP * (x - X0)) & mask_v)
u = ufract >> 16
v = vfract >> 16
color = read_byte(SLOT_SRC, u + slot_pitch(SLOT_SRC) * v)
if CMAP_A_EN:
color = read_byte(SLOT_CMAP_A, CMAP_A_IDX << 8 | color)
if CMAP_B_EN:
color = read_byte(SLOT_CMAP_B, CMAP_B_IDX << 8 | color)
if TRANS_EN:
color = read_byte(SLOT_TRANSMAP, TRANSMAP_IDX << 16 | orig_color << 8 | color)