Grafické módy 9++ a 10++

Piotr Fusik (Fox/Taquart)

Překlad: Radek Štěrba (Raster/C.P.U.)

Tento text je nedoslovným překladem anglického překladu původního článku publikovaného v polském magazínu "Atarynka", číslo 2/2002. Fox mi ho osobně poslal, že mohu udělat český překlad pro Flop. Kladl mi na srdce, abych předtím, než budu text překládat, nejdříve princip jeho speciálního grafického módu pochopil. To se mi naštěstí podařilo, takže hurá na českou verzi.

LewiS/AiDS v článku v magazínu Atarynka 1/2002 píše:

Nejpoužívanějším grafickým módem je "Konopův mód", který poprvé použil Konop/Shadows v demu "Asskicker". Tento mód byl používán léta a pravděpodobně neexistuje jiný mód, který by byl tak rychlý s podobnými parametry (čtvercové body, malá obrazová paměť, 16 odstínů [nebo jiný GTIA mód]).

LewiS se mýlil, neboť existuje podobný mód s ještě lepšími vlastnostmi než výše uvedený "Konopův" (známý též jako 9+). Tento nový mód byl nazván 9++ a Fox ho použil například v super demu Numen.

Výhody nového módu 9++

  1. Je rychlejší než mód 9+, přitom však nepoužívá prázdné meziřádky.
  2. Display List je značně kratší (každý grafický řádek je tvořen pouze jedinou DL instrukcí).
  3. Display List obsahuje pouze jednu jedinou definici obrazové paměti na začátku, takže například pro potřeby metody "střídání obrazovek" není nutné mít dva DListy, ale stačí měnit jen tuto jednu definici v jednom DL.
  4. Je možné jednoduše vytvořil variace tohoto módu s jinou výškou řádku (např. 3,5 nebo 6 obrazových bodů místo 4), maximálně 16.
  5. Vypadá lépe - posuďte sami - hlavně například v GTIA módu 10 jsou barvy hezčí, protože nepoužitím prázdných meziřádků není snížen jas a sytost barev.

Nevýhody

  1. V tomto módu spotřebovává ANTIC podstatně méně taktů, ovšem 6502 zase musí udělat nějakou práci navíc. Není jí moc, ale musí být přesně synchronizována se zobrazováním a vyžaduje to psaní obslužných rutin.
  2. Je problém se zobrazením nejspodnějšího, 240. obrazového řádku. Proto se u módu 9++ musíte buď spokojit s maximálně 59 řádky (59*4=236 obrazových řádků), a nebo udělat nejvrchnější či nejspodnější řádek nižší, tj. 3 obrazové řádky místo 4.

Myšlenka

Hlavní myšlenka je jednoduchá - zařiďme, ať ANTIC zobrazuje stejný obrazový řádek vícekrát pod sebou a používá přitom jen svou interní paměť. To stejné se děje třeba v ANTIC módu 8 (GRAPHICS 3), kdy se data přenášejí z hlavní paměti pouze pro první obrazový řádek, a následujících 7 obrazových řádků se zobrazuje již z paměti ANTICu.

Teorie

ANTIC obsahuje interní 4-bitový registr DCTR, který počítá obrazové řádky. Normálně pro každý řádek grafického módu počítá od 0 do hodnoty příslušné pro daný grafický mód. Například v nejjemnějších módech je tato hodnota 0, u grafik s dvojnásobnou výškou bodu je to 1 atd.

Jiné je to při aktivovaném vertikálním rolování. V prvním rolujícím řádku DCTR počítá od VSCROL do 0, v následujícím normálně, a v posledním od 0 do VSCROL. Počítáním od A do B je myšleno, že nejdříve je DCTR nastaveno hodnotou A a poblíž konce každého obrazového řádku je DCTR porovnáno s B. V případě rovnosti se přejde na další DL instrukci, jinak je DCTR zvýšeno o 1.

Mějme například tento DList:
$2f
$0f
a VSCROL=13 (decimálně):
Pak se bude nejprve 4x opakovat první obrazový řádek (DCTR bude nabývat hodnot 13,14,15,0 - hodnota 0 následuje po 15, protože DCTR je 4-bitový registr). Potom následuje druhý obrazový řádek 14x (DCTR bude nabývat hodnot od 0 do 13 včetně). Obdobně pro VSCROL=3:
Nejprve první obrazový řádek 14x.
Poté druhý obrazový řádek 4x.
Co je důležité, obrazová data pro několik stejných řádků se přenášejí z hlavní paměti pouze jednou.

Konstrukce módu

Jak lze snadno vyvodit z předešlých informací, pro grafický mód 9++ s každým řádkem o výšce 4 obrazové řádky, budeme potřebovat následující DList:

$6f,$xxyy(adresa obrazové paměti)
$0f
$2f
$0f
...

a budeme ve správných okamžicích měnit obsah VSCROL registru.

Následující obrázek vám pomůže pochopit co se kdy děje:

DCTR
 13  @--------------------------
 14  ---------------------------
 15  ---------------------------
  0  ---------------------------
  0  ===========================
  1  ===========================
  2  ===========================
  3  ====!===================*##
        \--viditelný obraz--/

'-' - první řádek grafického módu 9++
      (bit 5 v DListu je nastaven)
'=' - druhý řádek grafického módu 9++
      (bit 5 v DListu je nulový)
'@' - nyní VSCROL musí obsahovat 13
'!' - nyní se vyvolává DLI rutina
'*' - nyní VSCROL musí obsahovat 3
'#' - nyní nastavujeme 13 do VSCROL

Obrázek ukazoval dva řádky grafického módu 9++, tj. 8 obrazových řádků. Také znázorňoval časování - jednotlivé obrazové řádky jsou vykreslovány zleva doprava. Nejprve musí VSCROL obsahovat hodnotu 13 (decimálně). Jakmile si ANTIC přebere tuto hodnotu do DCTR, vykreslí 4x stejný obrazový řádek - tj. první řádek grafického módu 9++. Následně se budou vykreslovat jednotlivé obrazové řádky druhého řádku grafického módu 9++. Na konci čtvrtého obrazového řádku musíme zajistit, aby VSCROL obsahoval 3. Je dost času na nastavení této hodnoty do VSCROLu. Můžeme to provést třeba v DLI na prvním řádku. Mnohem kritičtější je to s přípravou pro třetí grafický řádek. Jak je vidět na obrázku ('#'), je jen krátký okamžik před koncem řádku, kdy musíme uložit hodnotu 13 do VSCROLu (jen pár strojových taktů).

Varianty implementace

Registr VSCROL můžeme nastavovat:
  1. použitím DLI přerušení,
  2. uvnitř kódu nějakého našeho efektu,
  3. použitím IRQ POKEY časovačů.
Varianta a) je bezpochyby nejjednodušší, avšak nejpomalejší. Optimální je varianta b), použitelná však bude jen u jednoduchých efektů typu bump mapping. Varianta c) je kvalitnější než a), protože POKEY časovače můžeme nastavovat s přesností na jednotlivé takty, takže neplýtváme časem v rutině přerušení. Ovšem pro potřeby hry či dema je c) nepraktická, protože POKEY časovače se používají také pro generování zvuků a nechceme obětovat úroveň hudebního doprovodu (méně zvukových generátorů) jen kvůli novému grafickému módu.

DLI rutina přerušení

Potřebujeme, aby spotřebovávala co nejméně času. Bude se vyvolávat na každém druhém grafickém řádku (tj. jednou na 8 obrazových řádků) - to řídíme bitem 5 v DL instrukci.

Přerušení se vyvolává krátce po začátku vykreslování obrazového řádku (okamžik '!'). Normálně NMI obsluha zjistí typ přerušení (DLI nebo VLBKI) a uschová obsah registrů (přinejmenším A registru). Přesto máme stále dost času před zápisem hodnoty 13 do VSCROLu. Nejjednodušším způsobem jak spotřebovat tento čas (a zajistit správnou synchronizaci) je použití WSYNC. Pak zapíšeme 13 do VSCROLu a ihned poté 3.

Ukázka:

    lda #dli
    sta $201
    lda #$22
    sta $22f
    lda #
dl sta $231 lda #$40 sta $26f lda #$c0 sta $d40e jmp * dli pha sta $d40a lda #13 sta $d405 lda #3 sta $d405 pla rti ; 2 prázdné řádky, 1 řádek grafiky 9++ dl db $90,$6f dw $f000 ; 29krát $8f,$2f => 58 řádků grafiky 9++ db $8f,$2f,$8f,$2f,$8f,$2f,$8f,$2f db $8f,$2f,$8f,$2f,$8f,$2f,$8f,$2f db $8f,$2f,$8f,$2f,$8f,$2f,$8f,$2f db $8f,$2f,$8f,$2f,$8f,$2f,$8f,$2f db $8f,$2f,$8f,$2f,$8f,$2f,$8f,$2f db $8f,$2f,$8f,$2f,$8f,$2f,$8f,$2f db $8f,$2f,$8f,$2f,$8f,$2f,$8f,$2f db $8f,$2f db $41 dw dl

Možná není na první pohled zřejmé, proč jsme zapsali 3 do VSCROLu na konci DLI, a ne před zápisem do $d40a (WSYNC)?

Jak bylo znázorněno na obrázku, VSCROL je ve druhém řádku kontrolován blízko konci obrazového řádku. Přesněji, je to v okamžiku, když je dokončeno zpracování aktuálního DL kódu. Avšak když použijeme DLI (které se vyvolává v posledním obrazovém řádku DL kódu), provádí se další porovnání DCTR s VSCROLem. Tudíž VSCROL musí již obsahovat 3, když přerušení začíná.

Kolik taktů získáme?

To je velmi důležitá otázka, protože rychlost jsme zmínili hned v prvním bodě mezi výhodami tohoto nového módu.

Jeden řádek módu 9+ může v DListu vypadat takto:

    db $0f,$00,$4f
    dw screen
    db $00

Celkově: 6 taktů pro DL
+ (64 nebo 80) taktů pro obraz (v závislosti na šířce obrazu).
V módu 9++ je to pouze jeden DL kód, takže 1 takt pro DL
+ (32 nebo 40) taktů pro obraz. Navíc však 6502 musí nastavovat VSCROL, na což spotřebuje 6 taktů. Spočtěme nyní celou obrazovku (2 prázdné obrazové řádky, 59 řádků gr.módu 9++, zadání počátku videopaměti v prvním řádku, JVB):
Úzký obraz:
9+: 1+59*(6+64)+2+3=4136
9++: 1+59*(1+32+6)+2+3=2307 Normální šířka obrazu:
9+: 1+59*(6+80)+2+3=5080
9++: 1+59*(1+40+6)+2+3=2779

Zhruba lze říci, že zisk představuje cca 6-7% CPU strojového času. To však platí jen v ideálním případě, kdy provádíme nastavování VSCROLu přímo uvnitř kódu. Použijeme-li na to DLI, jako v uvedeném příkladu, vyjde nám nový mód 9++ nepatrně pomalejší než starý mód 9+, ale stále mnohem rychlejší než mód bez prázdných řádků, vytvořený pouze DListem). Přitom nejvíc času je promrháno použitím STA $d40a (synchronizace přes WSYNC). Elegantním (avšak obtížně realizovatelným) řešením může být využití tohoto času pro nějaké výpočty.

Hlavně však nesmíme zapomínat na další výhody módu 9++, díky kterým se jeho použití stává výhodnější než klasických módů konstruovaných pouze přes DList. Přitom, pokud již nastavování VSCROLu děláme přes DLI, je tu další výhoda: Můžeme například měnit barvu pozadí pro jednotlivé řádky a nestojí nás to téměř již žádnou práci navíc.

P.S. Popsanou metodu s VSCROL registrem můžete obecně použít i pro vytvoření dalších zajímavých módů, třeba hardwarem podporovaného textového módu 40x40 znaků.