V předchozím článku jsem popsal režim LoRes neboli Radastan: 128 krát 96 bodů, šestnáct barev a příjemně lineární framebuffer. Při psaní dema pro Forever 2027 jsem ale několikrát potřeboval spočítat ještě méně bodů. A tak jsem sešel o patro níž. Ne do tajného registru ZX Spectrum Next, ale k malé softwarové lsti.
ZX Spectrum Next / renderovací techniky
Super LoRes: když jsou i malé pixely příliš velká práce
Jak nad Radastanem postavit virtuální obraz 64 × 48, proměnit jediný výpočet v blok 4 × 4 bodů a získaný čas věnovat plasmě, SDF objektu nebo jiné grafické nepleše.
Nejdřív malé varování: není to nový režim Nextu
Název Super LoRes používám jako pracovní označení pro virtuální plochu
64 krát 48 bodů. Hardware o ní vůbec neví. Next stále zobrazuje obyčejný LoRes 128 krát 96
a stále čte 6144 bajtů z adresy $4000 nebo
$6000. Jen renderer počítá obraz v poloviční šířce
i výšce a každý výsledek zapíše do čtveřice LoRes pixelů.
Normální LoRes pixel se hardwarově zobrazí jako čtverec 2 krát 2 bodů aktivní plochy. Když softwarově spojíme dva LoRes pixely vedle sebe a dva pod sebou, dostaneme výsledný blok 4 krát 4. Rozměr 64 krát 48 tedy není nastavením registru, ale souřadnicovým systémem našeho efektu. Klidně bychom si mohli vytvořit 32 krát 24 s bloky 8 krát 8, ale to už by obraz připomínal barevnou mozaiku viděnou přes zamlžené potápěčské brýle.
Běžný LoRes obraz má 12 288 pixelů. Super LoRes počítá jen 3072 vzorků, tedy přesně čtvrtinu. U drahé matematiky — vzdálenostních polí, deformací, bump mappingu nebo tunelu — je to velmi příjemná sleva. Framebuffer ani počet nutných výstupních bajtů se nezmenší; šetří se hlavně počet výpočtů efektu.
; Zapnuti je naprosto stejne jako u bezneho LoRes
nextreg $15,$80 ; povolit LoRes vrstvu
nextreg $6A,$20 ; Radastan 128x96, obrazovka $4000
nextreg $26,0 ; horizontalni posun
nextreg $27,0 ; vertikalni posun
Jak z jedné barvy vznikne blok 4 × 4
LoRes ukládá dva vodorovné pixely do jednoho bajtu. Horní nibble je barva levého bodu,
dolní nibble barva pravého. Chceme-li oba body stejně barevné, stačí index barvy
n zkopírovat do obou nibblů. Pro barvu
$A tedy zapíšeme bajt $AA.
Tím máme vodorovnou polovinu práce hotovou.
Souřadnice efektu: x, y
2 × 2 softwarově, každý bod potom 2 × 2 hardwarově
Svislé zdvojení je ještě přímočařejší: stejný bajt zapíšeme do aktuálního LoRes řádku
a znovu o 64 bajtů níž. Jeden řádek LoRes má právě 64 bajtů, takže druhý zápis míří
na stejnou vodorovnou pozici v následujícím řádku. Dvě stejné hodnoty
$AA pod sebou tvoří 2 krát 2 LoRes pixely a hardware
z nich udělá šestnáct stejně barevných bodů monitoru.
Adresa bez dělení, násobení a černé magie
Logické souřadnice Super LoRes jsou x = 0..63 a
y = 0..47. Každý logický řádek zabere dva fyzické
LoRes řádky po 64 bajtech. Adresa horního bajtu bloku je proto velmi jednoduchá:
Při kreslení celé obrazovky není potřeba adresu pro každý bod znovu počítat. Ukazatel postupuje
lineárně přes 64 vzorků. Na konci řádku už stojí na začátku jeho svislé kopie, takže k němu
přičteme dalších 64 a ocitneme se na začátku příštího logického řádku. Právě tento detail vysvětluje
instrukci add ix,bc v renderovací smyčce níže.
Oba buffery neleží těsně za sebou. Nemažte jedním velkým blokem celé
$4000–$77FF. Oblast
$5800–$5FFF může obsahovat atributy, systémové proměnné
nebo jiná data. V demu čistím zvlášť 6144 bajtů od $4000
a zvlášť 6144 bajtů od $6000.
Renderovací smyčka, která dělá tu důležitou práci
Nejdříve si připravíme tabulku zarovnanou na hranici 256 bajtů. Pro indexy 0 až 15 vrací hodnoty
$00, $11, $22 ... $FF. Díky zarovnání zůstává adresa
tabulky v registru H a barevný index stačí vložit do
L. Místo několika posunů a logického OR pak máme jediný
lookup. Z80 má tabulky rád. Kdyby mohl, pravděpodobně by si do tabulky předpočítal i přestávku na kávu.
ALIGN 256
lores_expand_nibble:
db $00,$11,$22,$33,$44,$55,$66,$77
db $88,$99,$AA,$BB,$CC,$DD,$EE,$FF
ASSERT low lores_expand_nibble == 0
Následující smyčka je zjednodušenou podobou kódu, který používám. Zdroj obsahuje 3072 bajtů — jeden paletový
index pro každý bod 64 krát 48. DE ukazuje na zdroj,
IX na začátek skrytého LoRes framebufferu.
; DE = 64x48 zdroj, jeden index 0..15 na bajt
; IX = $4000 nebo $6000, podle zvoleneho zadniho bufferu
ld bc,$0040 ; 64 bajtu na LoRes radek
ld iy,$3040 ; IYH=48 radku, IYL=64 bodu
ld h,high lores_expand_nibble
.row:
ld iyl,64
.pixel:
ld a,(de) ; barva 0..15
inc de
ld l,a
ld a,(hl) ; n se zmeni na nn
ld (ix+0),a ; dva LoRes pixely vedle sebe
ld (ix+64),a ; a stejna dvojice o radek niz
inc ix
dec iyl
jr nz,.pixel
add ix,bc ; preskocit prave vyplnenou kopii
dec iyh
jr nz,.row
ret
U procedurálního efektu nemusí existovat žádný 3KB zdroj. Smyčka vypočítá barvu a okamžitě ji rozbalí přes stejnou tabulku. Přesně tak pracuje část SDF Metamorph: pro každý bod načte předpočítaný poloměr, úhel a světlo, vyhodnotí povrch a výsledný čtyřbitový materiál rovnou zapíše do dvou řádků zadního bufferu. Mezikrok v paměti by tam jen zdržoval.
Co předpočítat a co nechat na Z80N
Super LoRes neurčuje, odkud se barvy vezmou. Lze je počítat živě, načítat z hotových snímků, skládat z map a tabulek nebo všechny přístupy promíchat. Volba je hlavně obchod mezi pamětí, časem procesoru a tím, zda se efekt musí měnit podle vstupu či hudby.
Minimum snímkových dat, maximum volnosti. Hodí se pro SDF, tunely, částice a zpětnou vazbu.
Nejrychlejší čtení a jednoduchý lookup. Dva 64×48 snímky se vejdou do jednoho 8KB MMU page.
Poloviční data, ale před kreslením je nutné rozdělit horní a dolní nibble.
| Podoba jednoho snímku | Velikost | Výhoda | Cena |
|---|---|---|---|
| 64 × 48, 8 bitů na index | 3072 B | Jednoduchá a rychlá expanze | Polovina bajtu zůstává nevyužitá |
| 64 × 48, 4 bity na index | 1536 B | Velmi úsporné animace | Rozbalování obou nibblů |
| Hotový LoRes 128 × 96 | 6144 B | Přímé kopírování do framebufferu | Žádná úspora dat |
| Procedurální snímek | 0 B snímků | Animace reaguje za běhu | Výpočet musí stihnout frame |
V demu často používám variantu 3072 bajtů. Dva snímky zaberou 6144 bajtů, takže sdílejí jeden
8KB MMU page a po namapování do $E000 se vybírají adresou
$E000 nebo $EC00.
U hotové animace je to příjemný kompromis: změna stránky je levná, zdroj se čte lineárně a vnitřní
smyčka nemusí rozbalovat dva různé pixely z jednoho bajtu.
Kreslíme dozadu, ukazujeme až hotový obraz
Vím, že je to možná zbytečné, protože každý kdo programoval a využíval 2. VRAM na ZX Spectru 128k, mu je práce se stínovou VRAM známa... ale pro jistotu. :) Super LoRes používá stejné dvě obrazovky jako Radastan. Bit 4 registru
$6A vybírá viditelný framebuffer. Pokud je na monitoru
$4000, renderer zapisuje do $6000
a naopak. Po dokončení celého snímku počká na přerušení, přepne bit a role obou stránek prohodí.
; visible_page obsahuje 0 nebo $10
call render_superlores_frame ; kresli do opacne stranky
halt ; pockej na hranici snimku
ld a,(visible_page)
xor $10 ; prohod $4000 / $6000
ld (visible_page),a
or $20 ; zachovej zapnuty Radastan
nextreg $6A,a
Je důležité rozlišovat stránku, která je vidět, a stránku, do které se kreslí.
Proměnná se může jmenovat jakkoli, ale její význam musí zůstat konzistentní. V mých efektech
visible_page popisuje právě zobrazenou stránku a výběr
cílové adresy proto záměrně volí tu druhou. Tohle je drobnost, která po půlhodině ladění začne
působit jako velmi osobní spor mezi programátorem a registrem $6A.

