Dnešním článkem uzavřeme tu část seriálu o osmibitových domácích mikropočítačích Atari, která byla věnována jeho grafickým schopnostem. Připomeňme si, že jsme si popsali jak možnosti nabízené čipem GTIA (primárně se jedná o podporu pro sprity a taktéž pro speciální GTIA režimy), tak i ANTIC (kombinace barvových režimů, DLI a VBI). Dnešní článek je věnován problematice takzvaného grafického kernelu. Jedná se o podprogram (subrutinu), který je nějakým způsobem synchronizovaný s generováním obrazu a jenž tudíž běží souběžně s generováním obrazu.
Co se dozvíte v článku
- Notoricky známý příklad: duha (zobrazení všech barev)
- Úplný zdrojový kód prvního demonstračního příkladu
- Plynule scrollující duha
- Úplný zdrojový kód druhého demonstračního příkladu
- Dvě nezávisle scrollující duhové pruhy: herní plocha a pozadí
- Úplný zdrojový kód třetího demonstračního příkladu
- Kernel modifikující barvy na aktuálním televizním řádku v reálném čase
- Úplný zdrojový kód čtvrtého demonstračního příkladu
- Zákaz DMA ANTICu a přesná synchronizace programu s paprskem
- Úplný zdrojový kód pátého demonstračního příkladu
- Větší množství barev v režimech GTIA
- Úprava display listu a nová podoba grafických kernelů
- Úplný zdrojový kód šestého demonstračního příkladu: více barev v režimu GTIA 9
- Úplný zdrojový kód sedmého demonstračního příkladu: více barev v režimu GTIA 11
- Skládáme vše dohromady: grafický kernel, DLI a zobrazení spritů
- Úplný zdrojový kód posledního demonstračního příkladu
- Příloha: Makefile pro překlad všech demonstračních příkladů
- Repositář s demonstračními příklady
- Odkazy na Internetu
Díky tomu je možné (pochopitelně pouze při použití korektních technik) zvýšit počet současně použitelných barev, zvětšit počet zobrazených spritů na obrazovce atd. Grafický kernel si tedy (při použití moderních termínů) můžeme představit jako vlákno spouštěné v určitých přesně definovaných okamžicích.
Otázkou zůstává, jakým způsobem se grafický kernel synchronizuje s generováním obrazu. Teoreticky je nutné mít k dispozici tři možnosti synchronizace:
- Synchronizace se začátkem či naopak s koncem vykreslování celého snímku. To lze na osmibitových Atari zajistit relativně snadno, konkrétně s využitím VBI resp. subrutiny vyvolané při VBI (připomeňme si možnost naprogramování jak přímé reakce na VBI, tak i takzvané odložené VBI).
- Synchronizace se začátkem vykreslování obrazového (televizního) řádku. Zde mají vývojáři k dispozici dva mechanismy. Buď nakonfigurování a vyvolání DLI (tedy úprava display listu) nebo aktivní čekání zápisem do registru WSYNC. K oběma těmto způsobům se dnes dostaneme.
- Synchronizace s vykreslením jednotlivých pixelů. Tato technika nemá (minimálně na osmibitových Atari) podporu v hardware, takže se musíme uchýlit k počítání strojových cyklů, ovšem se všemi nevýhodami, které to přináší. Navíc se musí počítat s dalšími přerušeními (několik cyklů si například „ukradne“ ANTIC) a aby toho nebylo málo, není mikroprocesor MOS 6502 dostatečně rychlý na to, aby například dokázal měnit barvy individuálních pixelů. K této problematice se dnes opět dostaneme.
Ještě si připomeňme (z předchozího článku), jak vlastně souvisí časování generování televizního obrazu s frekvencí mikroprocesoru MOS 6502. Pro jednoduchost se zaměříme na Atari upravené pro televizní normu PAL (NTSC je odlišná monstrozita, časování snímků a řádků je zde odlišné). Mikroprocesor MOS 6502 má na Atari pro PAL nastavenu frekvenci přibližně 1,77 MHz. Z této frekvence lze (převrácenou hodnotou) vypočítat dobu trvání jednoho cyklu CPU. Z této doby je dalším dělením dvěma odvozena hodnota nazývaná color cycle (důvod pro toto označení si hned řekneme).
Zobrazení snímků je do značné míry svázáno s frekvencí CPU a tím pádem i s výše uvedeným barvovým cyklem. Zobrazení jednoho obrazového řádku na televizoru trvá 228 barvových cyklů (a tím pádem 114 cyklů procesoru). Označení color cycle vychází z toho, že barvy na obrazovce lze modifikovat právě v jednotlivých barvových cyklech (nikoli CPU cyklech). Výjimkou jsou monochromatické režimy GR.0 a GR.8, které zobrazují pixely s poloviční šířkou (pixel=1/2 color cycle) a proto se v těchto režimech barvy pixelů nemohou měnit (modifikují se jen jejich intenzity).
Notoricky známý příklad: duha (zobrazení všech barev)
Začneme poměrně jednoduchým grafickým kernelem, který i přes svoji triviálnost ukazuje možnosti a schopnosti této programátorské techniky. Tento konkrétní kernel je vyvolán při DLI (a tedy vyžaduje úpravu display listu), ovšem interně je značně jednoduchý. Můžeme zde vidět implementaci počítané programové smyčky, která se bude opakovat 192×, což je (nikoli náhodou) celkový počet obrazových řádků v režimu GR.8 (24 textových řádků × 8 obrazových řádků/řádek textový = 192):
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
ldx #192 ; počitadlo řádků
next_line:
...
...
...
dex
bne next_line ; opakovat 192x
rti ; návrat z DLI
A co konkrétně se v této smyčce provádí? Nejprve se realizuje zápis do HW registru WSYNC. Připomeňme si, že tato operace počká na horizontální synchronizaci obrazu a tedy vlastně čekáme na začátek zobrazení dalšího obrazového řádku. A po zápisu do WSYNC uložíme do barvového registru COLPF2 (barva pozadí v GR.8) aktuální hodnotu index registru X (počitadlo řádků), čímž se pro celý obrazový řádek, který se právě začal vykreslovat, změnila barva kreslení:
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
ldx #192 ; počitadlo řádků
next_line:
stx COLPF2 ; změna barvy
sta WSYNC ; čekání na další řádek
dex
bne next_line ; opakovat 192x
rti ; návrat z DLI
Výsledek znají všichni uživatelé počítačů Atari – jedná se o klasickou duhu použitou v nepřeberném množství her a dem:
Obrázek 1: Klasická duha je jedním z typických triků na Atari.
Aby se vůbec subrutina s obsluhou DLI vyvolala, musíme DLI povolit, a to jedenkrát pro celý display list (ne vícekrát, už jen kvůli době trvání subrutiny). Pro zajímavost jsem display list upravil takovým způsobem, aby se DLI vyvolalo ještě na horním (černém) okraji obrazovky, protože příznak DL_DLI je použit u instrukce DL_BLK8:
dlist: .byte DL_BLK8, DL_BLK8, DL_DLI+DL_BLK8 ; 3x8=24 prázdných obrazových řádků .byte DL_LMS+DL_CHR40x8x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 2 (GR.0) .byte <screen, >screen ; počáteční adresa obrazové paměti .res 23, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Úplný zdrojový kód prvního demonstračního příkladu
Následuje výpis zdrojového kódu dnešního prvního demonstračního příkladu, který posloužil pro vygenerování obrázku ukázaného v předchozí kapitole. Povšimněte si, jak krátký tento program ve skutečnosti je:
.include "atari.inc"
.CODE
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
; vektor DLI
lda #<dli ; nastavení DLI
sta VDSLST
lda #>dli
sta VDSLST+1
; povolení DLI
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
loop: jmp loop
.endproc
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
ldx #192 ; počitadlo řádků
next_line:
stx COLPF2 ; změna barvy
sta WSYNC ; čekání na další řádek
dex
bne next_line ; opakovat 192x
rti ; návrat z DLI
dlist:
.byte DL_BLK8, DL_BLK8, DL_DLI+DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_CHR40x8x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 2 (GR.0)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 23, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
end:
.BSS
screen: .res 40*24
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Plynule scrollující duha
Pokusme se nyní o nepatrnou modifikaci našeho grafického kernelu, a to konkrétně takovým způsobem, aby se zobrazil další známý grafický efekt. Jedná se o plynule scrollující duhu, tj. o obdobu předchozího efektu, ovšem vhodně upraveného takovým způsobem, aby se duha plynule pohybovala nahoru či dolů. Pohyb je skutečně plynulý, protože je navázán na synchronizaci kernelu s DLI.
Obrázek 2: Animovaná duha.
Nejprve v programovém kódu rezervujeme bajt, který bude obsahovat hodnotu představující posun duhy. Tato hodnota se při každém snímku zvýší nebo naopak sníží o jedničku (popř. můžete posun navázat na náklon joysticku atd.). Ideální by bylo tento bajt uložit do nulté stránky paměti, ovšem prozatím si vystačíme s bajtem umístěným „náhodně“ (adresa bude zvolena assemblerem):
color: .byte 0
V subrutině volané při každém DLI (v našem případě tedy na začátku snímku) se opět používá kombinace instrukcí pro synchronizaci sta WSYNC s instrukcí pro změnu barvy v registru COLPF2. A opět použijeme počitadlo 192 řádků realizované index registrem X, ovšem s jedním rozdílem – počáteční hodnota zapisovaná do COLPF2 není přímo odvozena z obsahu registru X (počitadla), ale naopak registru Y, který je inicializován na hodnotu přečtenou z adresy color:
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
ldx #192 ; počitadlo řádků
ldy color ; počáteční barva
next_line:
sty COLPF2 ; změna barvy
sta WSYNC ; čekání na další řádek
iny ; další barva v paletě
dex
bne next_line ; opakovat 192x
inc color ; počáteční barva pro další snímek
rti ; návrat z DLI
ltx color
inx
stx color
Mimochodem: akumulátor je (poněkud paradoxně) pro tento účel zcela nevhodný kvůli tomu, že je nutné nastavit nebo naopak vynulovat příznak carry a provádět součet s konstantou, nikoli přímo realizovat inkrementaci.
Úplný zdrojový kód druhého demonstračního příkladu
Zdrojový kód dnešního druhého demonstračního příkladu vypadá následovně:
.include "atari.inc"
.CODE
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
; vektor DLI
lda #<dli ; nastavení DLI
sta VDSLST
lda #>dli
sta VDSLST+1
; povolení DLI
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
loop: jmp loop
.endproc
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
ldx #192 ; počitadlo řádků
ldy color ; počáteční barva
next_line:
sty COLPF2 ; změna barvy
sta WSYNC ; čekání na další řádek
iny ; další barva v paletě
dex
bne next_line ; opakovat 192x
inc color ; počáteční barva pro další snímek
rti ; návrat z DLI
dlist:
.byte DL_BLK8, DL_BLK8, DL_DLI+DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_CHR40x8x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 2 (GR.0)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 23, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
end:
color: .byte 0
.BSS
screen: .res 40*24
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Dvě nezávisle scrollující duhové pruhy: herní plocha a pozadí
V další verzi se pokusíme efekt scrollující duhy ještě nepatrně vylepšit. Namísto jedné duhy zobrazíme duhy dvě (obě pohyblivé). Jedna duha bude zobrazena v herním poli (playfield), druhá na okraji (border). A každá duha se bude pohybovat odlišnou rychlostí. Na začátku subrutiny pro obsluhu DLI se počitadlo color uloží do index registru Y, zatímco doplněk této hodnoty do akumulátoru A:
ldy color ; počáteční barva
lda #$ff
clc
sbc color
V programové smyčce obsahující samotný grafický kernel se opět provádí synchronizace s televizními řádky. A nastavují se dva barvové registry COLPF2 a COLBK. Za smyčkou se ještě barva pozadí nastaví na černou barvu, protože v opačném případě by spodní část obrazovky poblikávala (v herním poli jsme to nemuseli řešit, ale okraj je viditelný):
next_line:
sty COLPF2 ; změna barvy
sta WSYNC ; čekání na další řádek
iny ; další barva v paletě
sta COLBK ; změna barvy okraje
clc
sbc #1
dex
bne next_line ; opakovat 192x
inc color ; počáteční barva pro další snímek
lda #0 ; černá barva
sta COLBK ; změna barvy spodního okraje
rti ; návrat z DLI
Výsledek:
Obrázek 3: Dvojice animovaných duh.
Pro zajímavost si ještě ukažme modifikaci tohoto příkladu do podoby s úzkým herním polem (narrow playfield):
Obrázek 4: Animované duhy a úzké herní pole.
Úplný zdrojový kód třetího demonstračního příkladu
Opět následuje výpis úplného zdrojového kódu dnešního třetího demonstračního příkladu, který vypadá takto:
.include "atari.inc"
.CODE
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
; vektor DLI
lda #<dli ; nastavení DLI
sta VDSLST
lda #>dli
sta VDSLST+1
; povolení DLI
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
loop: jmp loop
.endproc
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
ldx #192 ; počitadlo řádků
ldy color ; počáteční barva
lda #$ff
clc
sbc color
next_line:
sty COLPF2 ; změna barvy
sta WSYNC ; čekání na další řádek
iny ; další barva v paletě
sta COLBK ; změna barvy okraje
clc
sbc #1
dex
bne next_line ; opakovat 192x
inc color ; počáteční barva pro další snímek
lda #0 ; černá barva
sta COLBK ; změna barvy spodního okraje
rti ; návrat z DLI
dlist:
.byte DL_BLK8, DL_BLK8, DL_DLI+DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_CHR40x8x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 2 (GR.0)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 23, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
end:
color: .byte 0
.BSS
screen: .res 40*24
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Pro nastavení úzkého herního pole je použita tato modifikace:
; ---------------------------------------------------------------------
; nastavení spodních dvou bitů stínového registru SDMCTL
; ---------------------------------------------------------------------
.macro set_playfield_width bitfield
lda SDMCTL ; načíst stínový registr
and #%11111100 ; vynulovat spodní dva bity
ora bitfield ; nastavit spodní dva bity na zvolenou masku
sta SDMCTL ; a uložit zpět
.endmacro
a:
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
set_playfield_width #%01 ; šířka herního pole
; vektor DLI
lda #<dli ; nastavení DLI
sta VDSLST
lda #>dli
sta VDSLST+1
; povolení DLI
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
Kernel modifikující barvy na aktuálním televizním řádku v reálném čase
Pokusme se nyní o návrh komplikovanějšího kernelu, konkrétně kernelu, který modifikuje barvu v průběhu zobrazení televizního řádku. Tímto trikem lze zobrazit mnohem větší počet barev na jednom řádku, ovšem chybí nám zde jedna klíčová vlastnost: přesná synchronizace na úrovni jednotlivých pixelů. Tuto možnost čipy ANTIC ani GTIA nenabízí, takže nám nezbývá než počítat jednotlivé cykly. To je ovšem komplikované (jak ostatně uvidíme), protože mikroprocesor je v průběhu zobrazování pozastavován čipem ANTIC, který načítá data z paměti přes DMA. Z tohoto důvodu není počítání cyklů snadné, protože se časování na jednotlivých řádcích postupně mění.
Grafický kernel, který je ukázaný pod tímto odstavcem, nejprve nastaví barvu herního pole na hodnotu uloženou v registru Y (ta postupně roste po jednotlivých řádcích). Poté se vykoná deset instrukcí NOP, z nichž každá trvá dva cykly. Celkem tedy CPU vykoná minimálně dvacet cyklů, což by teoreticky mělo odpovídat deseti color cyklům a tedy dvěma znakům. Následně se barva herního pole změní na černou a počká se na dokončení vykreslení televizního řádku:
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
ldx #192 ; počitadlo řádků
ldy color ; počáteční barva
next_line:
sty COLPF2 ; změna barvy
iny ; další barva v paletě
.repeat 10
nop
.endrepeat
lda #0
sta COLPF2 ; změna barvy
sta WSYNC ; čekání na další řádek
dex
bne next_line ; opakovat 192x
inc color ; počáteční barva pro další snímek
rti ; návrat z DLI
Očekávali bychom, že se stále zobrazí duha, ovšem zkrácená na šířku přibližně dvou či tří znaků. Ovšem skutečnost je odlišná:
Obrázek 5: Vliv DMA na grafický kernel, který mění barvy v rámci jednoho řádku.
Můžeme zde vidět, že CPU je na některých řádcích „zpožděn“ čipem ANTIC. Jedná se o začátky textových řádků, tj. každý osmý obrazový řádek je zobrazen odlišně – změna barvy na černou je opožděna tak, že zasahuje až do řádku dalšího (ANTIC načítá instrukci display listu, ale též tvary znaků atd.). Z této jednoduché ukázky je patrné, že práce na úrovni jednotlivých řádků není jednoduchá (a navíc se nevyplatí používat textové režimy, ve kterých neplatí vztah logický řádek==televizní řádek).
Úplný zdrojový kód čtvrtého demonstračního příkladu
Následuje výpis úplného zdrojového kódu dnešního čtvrtého demonstračního příkladu, který vypadá takto:
.include "atari.inc"
.CODE
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
; vektor DLI
lda #<dli ; nastavení DLI
sta VDSLST
lda #>dli
sta VDSLST+1
; povolení DLI
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
loop: jmp loop
.endproc
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
ldx #192 ; počitadlo řádků
ldy color ; počáteční barva
next_line:
sty COLPF2 ; změna barvy
iny ; další barva v paletě
.repeat 10
nop
.endrepeat
lda #0
sta COLPF2 ; změna barvy
sta WSYNC ; čekání na další řádek
dex
bne next_line ; opakovat 192x
inc color ; počáteční barva pro další snímek
rti ; návrat z DLI
dlist:
.byte DL_BLK8, DL_BLK8, DL_DLI+DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_CHR40x8x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 2 (GR.0)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 23, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
end:
color: .byte 0
.BSS
screen: .res 40*24
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Zákaz DMA ANTICu a přesná synchronizace programu s paprskem
Otázkou zůstává, jestli je vůbec možné na osmibitových mikropočítačích Atari dosáhnout toho, aby bylo v grafickém kernelu možné počítat strojové cykly bez toho, aby do nich zasahoval ANTIC se svým DMA. Řešení existuje a je jednoduché – ANTIC „odstřihneme“ od DMA. Sice se nebude zobrazovat běžný textový či grafický režim, ale to do určité míry můžeme vyřešit v grafickém kernelu (alespoň pro účely dema).
Nejprve upravíme display list takovým způsobem, aby obsahoval jen instrukce DL_BLK8. Teoreticky je možné display list zkrátit pouze na dvě instrukce (konkrétně na DL_DLI+DL_BLK8 a DL_VJB, ale ponechme ho v podobě, která odpovídá textovému režimu GR.0, ovšem s tím rozdílem, že se zobrazují jen prázdné řádky:
dlist: .byte DL_BLK8, DL_BLK8, DL_DLI+DL_BLK8 ; 3x8=24 prázdných obrazových řádků .res 24, DL_BLK8 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
A následně nesmíme zapomenout na zákaz DMA:
lda #0
sta DMACTL
Samotný grafický kernel je upraven do takové podoby, že na jednom řádku přepíná barvu několikrát, a to vždy po deseti instrukcích NOP (dvacet cyklů). Na obrazovce tak bude jasně patrné, jaká je „šířka dvaceti cyklů“ (resp. spíše 25 cyklů kvůli zápisu nové barvy):
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
lda #0
sta DMACTL
ldx #192 ; počitadlo řádků
ldy color ; počáteční barva
sta WSYNC ; čekání na další řádek
next_line:
sty COLBK ; změna barvy
iny ; další barva v paletě
.repeat 10
nop
.endrepeat
lda #0
sta COLBK ; změna barvy
.repeat 10
nop
.endrepeat
sty COLBK
.repeat 10
nop
.endrepeat
sta COLBK ; změna barvy
sta WSYNC ; čekání na další řádek
lda #0
sta COLBK ; změna barvy
dex
bne next_line ; opakovat 192x
inc color ; počáteční barva pro další snímek
rti ; návrat z DLI
Výsledkem je naprosto čistá animace (až na první řádek – ten ovšem není synchronizován přes WSYNC):
Obrázek 6: Korektní "přerušovaná duha" (vypnutí DMA).
Úplný zdrojový kód pátého demonstračního příkladu
Dnešní v pořadí již pátý demonstrační příklad má následující zdrojový kód:
.include "atari.inc"
.CODE
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
; vektor DLI
lda #<dli ; nastavení DLI
sta VDSLST
lda #>dli
sta VDSLST+1
; povolení DLI
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
loop: jmp loop
.endproc
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
lda #0
sta DMACTL
ldx #192 ; počitadlo řádků
ldy color ; počáteční barva
sta WSYNC ; čekání na další řádek
next_line:
sty COLBK ; změna barvy
iny ; další barva v paletě
.repeat 10
nop
.endrepeat
lda #0
sta COLBK ; změna barvy
.repeat 10
nop
.endrepeat
sty COLBK
.repeat 10
nop
.endrepeat
sta COLBK ; změna barvy
sta WSYNC ; čekání na další řádek
lda #0
sta COLBK ; změna barvy
dex
bne next_line ; opakovat 192x
inc color ; počáteční barva pro další snímek
rti ; návrat z DLI
dlist:
.byte DL_BLK8, DL_BLK8, DL_DLI+DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.res 24, DL_BLK8 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
end:
color: .byte 0
.BSS
screen: .res 40*24
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Větší množství barev v režimech GTIA
Po (alespoň rámcovém) popisu možností grafických kernelů se ještě jednou vrátíme k režimům GTIA. S využitím DLI (nebo i celých grafických kernelů) je totiž možné relativně snadno zobrazit celou paletu 128 nebo dokonce 256 barev, kterými osmibitové mikropočítače Atari disponují. Dále uvedené úpravy jsou založeny na následujícím programu, který napřed provede přepnutí do běžného grafického režimu GR.8 (320×192 pixelů monochromaticky) a vykreslí všech 16 kombinací čtyř sousedních pixelů, počká na stisk klávesy a následně se přepne do GTIA režimu číslo 9:
Obrázek 7: Všechny kombinace čtyř sousedních pixelů v grafickém režimu GR.8.
Obrázek 8: Přepnutí do GTIA režimu číslo 9.
Tento program je založen na demonstračních příkladech, které jsme si ukázali již minule, takže nevyžaduje podrobnější popis:
; ---------------------------------------------------------------------
; Grafický režim 320x192 se dvěma barvami.
; Korektní display list.
; Vykreslení šestnácti různých kombinací pixelů.
; Přepnutí do GTIA režimu 9.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
; dva bloky RAM využité jako obrazová paměť
screen1 = $7000
screen2 = $8000
; ---------------------------------------------------------------------
; makro pro nastavení display listu
; ---------------------------------------------------------------------
.macro set_display_list label
lda #<label ; nižší byte adresy display listu
sta SDLSTL
lda #>label ; vyšší byte adresy display listu
sta SDLSTH
.endmacro
; ---------------------------------------------------------------------
; vyplnění obrazovky
; ---------------------------------------------------------------------
.macro fill_screen screen
.local next_line ; definice lokálního symbolu
.local draw_on_line ; definice lokálního symbolu
screen_addr = 128
lda #<screen1 ; uložit adresu začátku obrazovky na nultou stránku
sta screen_addr
lda #>screen1
sta screen_addr+1
ldx #96 ; počitadlo řádků
next_line:
lda #0 ; zapisovaná hodnota + počitadlo smyčky
tay ; offset do obrazové paměti
clc ; nechceme přičítat přenos
; ---------------------------------------------------------------------
; vykreslení pixelů na jednom řádku
; ---------------------------------------------------------------------
draw_on_line:
sta (screen_addr), y ; zápis na první řádek
iny ; další zápis bude proveden o jeden bajt dále
adc #1 ; zvýšení hodnoty v akumulátoru
cmp #40 ; konec smyčky?
bne draw_on_line ; dokud není nula, pokračovat
lda screen_addr ; 16bitové zvýšení adresy
clc
adc #40
sta screen_addr
lda screen_addr+1
adc #0
sta screen_addr+1
dex ; snížení počitadla
bne next_line ; jsme na posledním řádku? ne->skok
.endmacro
; ---------------------------------------------------------------------
; hlavní herní smyčka
; ---------------------------------------------------------------------
.macro game_loop
.local loop ; definice lokálního symbolu
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endmacro
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
set_display_list dlist ; nastavení display listu
fill_screen screen1 ; vyplnění obrazovky
jsr get_key ; čekání na stisk klávesy
jsr gtia_mode_9 ; nastavení GTIA režimu číslo 9
game_loop ; hlavní herní smyčka
.endproc
; ---------------------------------------------------------------------
; nastavení GTIA režimu číslo 9
; ---------------------------------------------------------------------
.proc gtia_mode_9
lda GPRIOR ; modifikace stínového registru GPRIOR
ora #$40 ; výběr GTIA režimu GR.9
sta GPRIOR
rts ; vyber adresy ze zasobniku + skok
.endproc
; ---------------------------------------------------------------------
; čekání na stisk klávesy
; ---------------------------------------------------------------------
.proc get_key
KBHANDLER = $e424 ; rutina pro cteni klavesy
lda KBHANDLER+1 ; cteni horni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
lda KBHANDLER ; cteni dolni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
rts ; vyber adresy ze zasobniku + skok
; zde neni nutne mit RTS
.endproc
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8)
.byte <screen1, >screen1 ; počáteční adresa obrazové paměti (první blok)
.res 95, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8)
.byte <screen2, >screen2 ; počáteční adresa obrazové paměti (druhý blok)
.res 95, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
end:
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Úprava display listu a nová podoba grafických kernelů
Aby se zobrazilo 128 nebo 256 barev, tedy celá dostupná paleta (a přitom se nejednalo o triviální duhu), budeme přepínat barvy v DLI subrutině, tedy v podprogramu, který je zavolán v případě, že instrukce display listu obsahuje příznak DL_DLI. V GTIA režimu číslo 9 postačuje pouze změnit barevný odstín v HW registru COLBK, čímž se vlastně změní odstín všech šestnácti zobrazitelných intenzit pixelů. Jediný zápis tedy změní všech šestnáct barev:
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda color ; barva pozadí
clc
adc #16
sta COLBK ; přímo nastavit zápisem do HW registru
sta color
pla ; obnovit akumulátor
rti ; návrat z DLI
Pokud je naopak nastavený GTIA režim číslo 11, mění se intenzita barvy a nikoli její odstín. Ovšem stále platí, že postačuje jediný zápis do HW registru COLBK. Intenzita je nyní definována sudým číslem 0 až 14 a proto subrutina obsahuje instrukci AND pro maskování:
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda color ; barva pozadí
clc
adc #1
sta color
and #%00001110
sta COLBK ; přímo nastavit zápisem do HW registru
pla ; obnovit akumulátor
rti ; návrat z DLI
To ovšem nestačí. Navíc je nutné změnit i display list. Vždy si necháme vyvolat DLI a poté se zobrazí (stejnými barvami) dalších pět řádků. Celkem tedy vždy šest obrazových řádků bude mít stejný obsah. A proč jsem vybral hodnotu 6? Platí 6×16=96, což je přesně polovina výšky obrazovky. Právě na tomto místě je nutné přepínat na odlišný blok video RAM, protože maximální velikost tohoto bloku je rovna čtyřem kilobajtům. To by nebyl nepřekonatelný problém, ovšem poněkud by se zkomplikoval kód vykreslující vzorek pixelů (cílem těchto článků je co nejčitelnější kód):
dlist: .byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků .byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8) .byte <screen1, >screen1 ; počáteční adresa obrazové paměti (první blok) .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_MAP320x1x1+DL_DLI ; povolení DLI .byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8) .byte <screen2, >screen2 ; počáteční adresa obrazové paměti (druhý blok) .res 95, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Úplný zdrojový kód šestého demonstračního příkladu: více barev v režimu GTIA 9
V režimu 9 vypadá cesta k barvové paletě na obrazovce takto:
Obrázek 9: Všechny kombinace čtyř sousedních pixelů v grafickém režimu GR.8.
Obrázek 10: Zapnutí DLI pro změnu barvy okraje (viz poněkud neviditelné tmavé barvy na okraji).
Obrázek 11: GTIA režim číslo 9 + DLI měnící odstíny barev.
Zdrojový kód demonstračního příkladu, který zobrazí barvovou paletu s využitím GTIA režimu číslo 9, vypadá následovně:
; ---------------------------------------------------------------------
; Grafický režim 320x192 se dvěma barvami.
; Korektní display list.
; Vykreslení šestnácti různých kombinací pixelů.
; Přepnutí do GTIA režimu 9.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
; dva bloky RAM využité jako obrazová paměť
screen1 = $7000
screen2 = $8000
; ---------------------------------------------------------------------
; makro pro nastavení display listu
; ---------------------------------------------------------------------
.macro set_display_list label
lda #<label ; nižší byte adresy display listu
sta SDLSTL
lda #>label ; vyšší byte adresy display listu
sta SDLSTH
.endmacro
; ---------------------------------------------------------------------
; makro pro nastavení DLI
; ---------------------------------------------------------------------
.macro set_dli_handler handler
; vektor DLI
lda #<handler ; nastavení DLI
sta VDSLST
lda #>handler
sta VDSLST+1
.endmacro
; ---------------------------------------------------------------------
; vyplnění obrazovky
; ---------------------------------------------------------------------
.macro fill_screen screen
.local next_line ; definice lokálního symbolu
.local draw_on_line ; definice lokálního symbolu
screen_addr = 128
lda #<screen1 ; uložit adresu začátku obrazovky na nultou stránku
sta screen_addr
lda #>screen1
sta screen_addr+1
ldx #96 ; počitadlo řádků
next_line:
lda #0 ; zapisovaná hodnota + počitadlo smyčky
tay ; offset do obrazové paměti
clc ; nechceme přičítat přenos
; ---------------------------------------------------------------------
; vykreslení pixelů na jednom řádku
; ---------------------------------------------------------------------
draw_on_line:
sta (screen_addr), y ; zápis na první řádek
iny ; další zápis bude proveden o jeden bajt dále
adc #1 ; zvýšení hodnoty v akumulátoru
cmp #40 ; konec smyčky?
bne draw_on_line ; dokud není nula, pokračovat
lda screen_addr ; 16bitové zvýšení adresy
clc
adc #40
sta screen_addr
lda screen_addr+1
adc #0
sta screen_addr+1
dex ; snížení počitadla
bne next_line ; jsme na posledním řádku? ne->skok
.endmacro
; ---------------------------------------------------------------------
; hlavní herní smyčka
; ---------------------------------------------------------------------
.macro game_loop
.local loop ; definice lokálního symbolu
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endmacro
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
set_display_list dlist ; nastavení display listu
set_dli_handler dli ; nastavení obsluhy DLI
fill_screen screen1 ; vyplnění obrazovky
jsr get_key ; čekání na stisk klávesy
jsr enable_dli ; povolení DLI (a VBI)
jsr get_key ; čekání na stisk klávesy
jsr gtia_mode_9 ; nastavení GTIA režimu číslo 9
game_loop ; hlavní herní smyčka
.endproc
; ---------------------------------------------------------------------
; povolení DLI (a VBI)
; ---------------------------------------------------------------------
.proc enable_dli
; povolení DLI
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
rts
.endproc
; ---------------------------------------------------------------------
; nastavení GTIA režimu číslo 9
; ---------------------------------------------------------------------
.proc gtia_mode_9
lda GPRIOR ; modifikace stínového registru GPRIOR
ora #$40 ; výběr GTIA režimu GR.9
sta GPRIOR
rts ; vyber adresy ze zasobniku + skok
.endproc
; ---------------------------------------------------------------------
; čekání na stisk klávesy
; ---------------------------------------------------------------------
.proc get_key
KBHANDLER = $e424 ; rutina pro cteni klavesy
lda KBHANDLER+1 ; cteni horni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
lda KBHANDLER ; cteni dolni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
rts ; vyber adresy ze zasobniku + skok
; zde neni nutne mit RTS
.endproc
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda color ; barva pozadí
clc
adc #16
sta COLBK ; přímo nastavit zápisem do HW registru
sta color
pla ; obnovit akumulátor
rti ; návrat z DLI
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8)
.byte <screen1, >screen1 ; počáteční adresa obrazové paměti (první blok)
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8)
.byte <screen2, >screen2 ; počáteční adresa obrazové paměti (druhý blok)
.res 95, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
; barva v GTIA režimu 9
color: .byte 1
end:
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Úplný zdrojový kód sedmého demonstračního příkladu: více barev v režimu GTIA 11
V režimu 11 vypadá cesta k barvové paletě na obrazovce následovně:
Obrázek 12: Všechny kombinace čtyř sousedních pixelů v grafickém režimu GR.8.
Obrázek 13: Zapnutí DLI pro změnu intenzity barev okraje.
Obrázek 14: Režim GTIA 11 + DLI pro postupnou změnu intenzity barev.
Opět se podívejme na úplný zdrojový kód příkladu, tentokrát příkladu, který zobrazí barvovou paletu v režimu GTIA 11 a nikoli GTIA 9:
; ---------------------------------------------------------------------
; Grafický režim 320x192 se dvěma barvami.
; Korektní display list.
; Vykreslení šestnácti různých kombinací pixelů.
; Přepnutí do GTIA režimu 11.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
; dva bloky RAM využité jako obrazová paměť
screen1 = $7000
screen2 = $8000
; ---------------------------------------------------------------------
; makro pro nastavení display listu
; ---------------------------------------------------------------------
.macro set_display_list label
lda #<label ; nižší byte adresy display listu
sta SDLSTL
lda #>label ; vyšší byte adresy display listu
sta SDLSTH
.endmacro
; ---------------------------------------------------------------------
; makro pro nastavení DLI
; ---------------------------------------------------------------------
.macro set_dli_handler handler
; vektor DLI
lda #<handler ; nastavení DLI
sta VDSLST
lda #>handler
sta VDSLST+1
.endmacro
; ---------------------------------------------------------------------
; vyplnění obrazovky
; ---------------------------------------------------------------------
.macro fill_screen screen
.local next_line ; definice lokálního symbolu
.local draw_on_line ; definice lokálního symbolu
screen_addr = 128
lda #<screen1 ; uložit adresu začátku obrazovky na nultou stránku
sta screen_addr
lda #>screen1
sta screen_addr+1
ldx #96 ; počitadlo řádků
next_line:
lda #0 ; zapisovaná hodnota + počitadlo smyčky
tay ; offset do obrazové paměti
clc ; nechceme přičítat přenos
; ---------------------------------------------------------------------
; vykreslení pixelů na jednom řádku
; ---------------------------------------------------------------------
draw_on_line:
sta (screen_addr), y ; zápis na první řádek
iny ; další zápis bude proveden o jeden bajt dále
adc #1 ; zvýšení hodnoty v akumulátoru
cmp #40 ; konec smyčky?
bne draw_on_line ; dokud není nula, pokračovat
lda screen_addr ; 16bitové zvýšení adresy
clc
adc #40
sta screen_addr
lda screen_addr+1
adc #0
sta screen_addr+1
dex ; snížení počitadla
bne next_line ; jsme na posledním řádku? ne->skok
.endmacro
; ---------------------------------------------------------------------
; hlavní herní smyčka
; ---------------------------------------------------------------------
.macro game_loop
.local loop ; definice lokálního symbolu
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endmacro
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
set_display_list dlist ; nastavení display listu
set_dli_handler dli ; nastavení obsluhy DLI
fill_screen screen1 ; vyplnění obrazovky
jsr get_key ; čekání na stisk klávesy
jsr enable_dli ; povolení DLI (a VBI)
jsr get_key ; čekání na stisk klávesy
jsr gtia_mode_11 ; nastavení GTIA režimu číslo 11
game_loop ; hlavní herní smyčka
.endproc
; ---------------------------------------------------------------------
; povolení DLI (a VBI)
; ---------------------------------------------------------------------
.proc enable_dli
; povolení DLI
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
rts
.endproc
; ---------------------------------------------------------------------
; nastavení GTIA režimu číslo 11
; ---------------------------------------------------------------------
.proc gtia_mode_11
lda GPRIOR ; modifikace stínového registru GPRIOR
ora #$c0 ; výběr GTIA režimu GR.11
sta GPRIOR
rts ; vyber adresy ze zasobniku + skok
.endproc
; ---------------------------------------------------------------------
; čekání na stisk klávesy
; ---------------------------------------------------------------------
.proc get_key
KBHANDLER = $e424 ; rutina pro cteni klavesy
lda KBHANDLER+1 ; cteni horni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
lda KBHANDLER ; cteni dolni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
rts ; vyber adresy ze zasobniku + skok
; zde neni nutne mit RTS
.endproc
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda color ; barva pozadí
clc
adc #1
sta color
and #%00001110
sta COLBK ; přímo nastavit zápisem do HW registru
pla ; obnovit akumulátor
rti ; návrat z DLI
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8)
.byte <screen1, >screen1 ; počáteční adresa obrazové paměti (první blok)
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8)
.byte <screen2, >screen2 ; počáteční adresa obrazové paměti (druhý blok)
.res 95, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
; barva v GTIA režimu 9
color: .byte 0
end:
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Skládáme vše dohromady: grafický kernel, DLI a zobrazení spritů
V samotném závěru popisu grafických schopností osmibitových domácích mikropočítačů Atari zkombinujeme některé postupy do jediného programu. V něm se zobrazí větší množství spritů, každý se zvlášť nastavenou barvou nezávislou na grafickém režimu ani na obsahu herního pole. Sprity se pohybují synchronizovaně s obrazem (VBI) a herní pole obsahuje paletu 256 barev (DLI). Výsledek by měl vypadat následovně:
Obrázek 15: GTIA režim s DLI (256 barev) a čtyřmi nezávislými sprity.
Úplný zdrojový kód posledního demonstračního příkladu
Dnešní poslední demonstrační příklad je již poměrně dlouhý, protože po překladu získáme .xex soubor s délkou 491 bajtů (to jsou skoro dvě paměťové stránky!):
; ---------------------------------------------------------------------
; Grafický režim 320x192 se dvěma barvami.
; Korektní display list.
; Vykreslení šestnácti různých kombinací pixelů.
; Přepnutí do GTIA režimu 9.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
; dva bloky RAM využité jako obrazová paměť
screen1 = $7000
screen2 = $8000
; adresy PMG
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
; ---------------------------------------------------------------------
; makro pro nastavení display listu
; ---------------------------------------------------------------------
.macro set_display_list label
lda #<label ; nižší byte adresy display listu
sta SDLSTL
lda #>label ; vyšší byte adresy display listu
sta SDLSTH
.endmacro
; ---------------------------------------------------------------------
; makro pro nastavení DLI
; ---------------------------------------------------------------------
.macro set_dli_handler handler
; vektor DLI
lda #<handler ; nastavení DLI
sta VDSLST
lda #>handler
sta VDSLST+1
.endmacro
; ---------------------------------------------------------------------
; makro pro nastavení VBI
; ---------------------------------------------------------------------
.macro set_vbi_handler handler
; nastavit vektor pro odloženou VBI
lda #>handler
sta VVBLKD+1
lda #<handler
sta VVBLKD
.endmacro
; ---------------------------------------------------------------------
; vyplnění obrazovky
; ---------------------------------------------------------------------
.macro fill_screen screen
.local next_line ; definice lokálního symbolu
.local draw_on_line ; definice lokálního symbolu
screen_addr = 128
lda #<screen1 ; uložit adresu začátku obrazovky na nultou stránku
sta screen_addr
lda #>screen1
sta screen_addr+1
ldx #96 ; počitadlo řádků
next_line:
lda #0 ; zapisovaná hodnota + počitadlo smyčky
tay ; offset do obrazové paměti
clc ; nechceme přičítat přenos
; ---------------------------------------------------------------------
; vykreslení pixelů na jednom řádku
; ---------------------------------------------------------------------
draw_on_line:
sta (screen_addr), y ; zápis na první řádek
iny ; další zápis bude proveden o jeden bajt dále
adc #1 ; zvýšení hodnoty v akumulátoru
cmp #40 ; konec smyčky?
bne draw_on_line ; dokud není nula, pokračovat
lda screen_addr ; 16bitové zvýšení adresy
clc
adc #40
sta screen_addr
lda screen_addr+1
adc #0
sta screen_addr+1
dex ; snížení počitadla
bne next_line ; jsme na posledním řádku? ne->skok
.endmacro
; ---------------------------------------------------------------------
; set PMG
; ---------------------------------------------------------------------
.macro set_pmg
.local next_line ; definice lokálního symbolu
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #100 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #120 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_BLUE<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #0 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+20, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+30, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+40, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+50, x ; uložit byte - čtvrtý hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
.endmacro
; ---------------------------------------------------------------------
; hlavní herní smyčka
; ---------------------------------------------------------------------
.macro game_loop
.local loop ; definice lokálního symbolu
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endmacro
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
set_display_list dlist ; nastavení display listu
set_pmg ; nastavení PMG
set_dli_handler dli ; nastavení obsluhy DLI
set_vbi_handler horizontal_movement
fill_screen screen1 ; vyplnění obrazovky
jsr get_key ; čekání na stisk klávesy
jsr enable_dli ; povolení DLI (a VBI)
jsr get_key ; čekání na stisk klávesy
jsr gtia_mode_9 ; nastavení GTIA režimu číslo 9
game_loop ; hlavní herní smyčka
.endproc
; ---------------------------------------------------------------------
; povolení DLI (a VBI)
; ---------------------------------------------------------------------
.proc enable_dli
; povolení DLI
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
rts
.endproc
; ---------------------------------------------------------------------
; nastavení GTIA režimu číslo 9
; ---------------------------------------------------------------------
.proc gtia_mode_9
lda GPRIOR ; modifikace stínového registru GPRIOR
ora #$40 ; výběr GTIA režimu GR.9
sta GPRIOR
rts ; vyber adresy ze zasobniku + skok
.endproc
; ---------------------------------------------------------------------
; čekání na stisk klávesy
; ---------------------------------------------------------------------
.proc get_key
KBHANDLER = $e424 ; rutina pro cteni klavesy
lda KBHANDLER+1 ; cteni horni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
lda KBHANDLER ; cteni dolni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
rts ; vyber adresy ze zasobniku + skok
; zde neni nutne mit RTS
.endproc
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda color ; barva pozadí
clc
adc #16
sta COLBK ; přímo nastavit zápisem do HW registru
sta color
pla ; obnovit akumulátor
rti ; návrat z DLI
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos_0 ; původní pozice prvního hráče
dex ; posun hráče doleva
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos_0 ; zapamatovat si pozici prvního hráče
ldx x_pos_1 ; původní pozice prvního hráče
inx ; posun hráče doprava
stx HPOSP1 ; změna pozice prvního hráče
stx x_pos_1 ; zapamatovat si pozici prvního hráče
ldx x_pos_2 ; původní pozice prvního hráče
dex ; posun hráče doleva
dex ; posun hráče doleva
stx HPOSP2 ; změna pozice prvního hráče
stx x_pos_2 ; zapamatovat si pozici prvního hráče
ldx x_pos_3 ; původní pozice prvního hráče
inx ; posun hráče doprava
inx ; posun hráče doprava
stx HPOSP3 ; změna pozice prvního hráče
stx x_pos_3 ; zapamatovat si pozici prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8)
.byte <screen1, >screen1 ; počáteční adresa obrazové paměti (první blok)
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.res 5, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_MAP320x1x1+DL_DLI ; povolení DLI
.byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8)
.byte <screen2, >screen2 ; počáteční adresa obrazové paměti (druhý blok)
.res 95, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
; barva v GTIA režimu 9
color: .byte 1
; data spritů
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
x_pos_0: .byte 80
x_pos_1: .byte 100
x_pos_2: .byte 120
x_pos_3: .byte 140
end:
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Příloha: Makefile pro překlad všech demonstračních příkladů
Všechny minule i dnes popsané demonstrační příklady, pro jejichž překlad je zapotřebí použít assembler ca65 a linker ld65, je možné přeložit s využitím souboru Makefile, jehož obsah je vypsán pod tímto odstavcem:
execs := dummy.xex print_a.xex \
background_color_1.xex background_color_2.xex \
color_computation_1.xex color_computation_2.xex \
subroutine_1.xex subroutine_2.xex \
hex_number_1.xex hex_number_2.xex \
hex_number_3.xex hex_number_4.xex \
hex_number_5.xex hex_number_6.xex \
hex_number_7.xex hex_number_8.xex \
hex_number_9.xex \
fill_block_1.xex fill_block_2.xex \
fill_block_3.xex fill_block_4.xex \
fill_block_5.xex \
pmg_01.xex pmg_02.xex \
pmg_03.xex pmg_04.xex \
pmg_05.xex pmg_06.xex \
pmg_07.xex pmg_08.xex \
pmg_09.xex pmg_10.xex \
pmg_11.xex pmg_12.xex \
pmg_13.xex pmg_14.xex \
pmg_15.xex pong.xex \
pmg_stick_1.xex pmg_stick_2.xex \
pmg_stick_3.xex pmg_stick_4.xex \
pmg_stick_5.xex pmg_stick_6.xex \
pmg_collisions_1.xex pmg_collisions_2.xex \
pmg_collisions_3.xex pmg_collisions_4.xex \
antic_1.xex antic_2.xex \
antic_3.xex antic_4.xex \
antic_5.xex antic_6.xex \
antic_7.xex antic_8.xex \
antic_9.xex antic_A.xex \
antic_B.xex \
antic_bitmap_1.xex antic_bitmap_2.xex \
antic_bitmap_3.xex antic_bitmap_4.xex \
antic_bitmap_5.xex antic_bitmap_6.xex \
antic_bitmap_7.xex antic_bitmap_8.xex \
antic_dli_1.asm antic_dli_2.asm \
antic_dli_3.asm antic_dli_4.asm \
antic_dli_5.asm antic_dli_pmg.asm
all: $(execs)
clean:
rm -f *.o
rm -f *.xex
.PHONY: all clean
%.o: %.asm
ca65 $< -t atari -o $@ -l $(basename $<)_list.asm --list-bytes 100
%.xex: %.o
ld65 -C linker.cfg $< -o $@ -m $(basename $<).map
antic_bitmap_6.xex: antic_bitmap_6.o
ld65 -C linker_image.cfg $< -o $@ -m $(basename $<).map
antic_bitmap_7.xex: antic_bitmap_7.o
ld65 -C linker_image_2.cfg $< -o $@ -m $(basename $<).map
antic: \
antic_1.xex \
antic_2.xex \
antic_3.xex \
antic_4.xex \
antic_5.xex \
antic_6.xex \
antic_7.xex \
antic_8.xex \
antic_9.xex \
antic_A.xex \
antic_B.xex
antic_bitmap: \
antic_bitmap_1.xex \
antic_bitmap_2.xex \
antic_bitmap_3.xex \
antic_bitmap_4.xex \
antic_bitmap_5.xex \
antic_bitmap_6.xex \
antic_bitmap_7.xex \
antic_bitmap_8.xex
antic_dli: \
antic_dli_1.xex \
antic_dli_2.xex \
antic_dli_3.xex \
antic_dli_4.xex \
antic_dli_5.xex \
antic_dli_pmg.xex
antic_vbi: \
antic_vbi_1.xex \
antic_vbi_2.xex \
antic_vbi_3.xex \
antic_vbi_dli_1.xex \
antic_vbi_dli_2.xex \
antic_vbi_dli_3.xex \
antic_vbi_dli_4.xex
antic_scrolling: \
antic_scrolling_1.xex \
antic_scrolling_2.xex \
antic_scrolling_3.xex \
antic_scrolling_4.xex
gtia: \
gtia_mode_1.xex \
gtia_mode_2.xex \
gtia_mode_3.xex \
gtia_mode_4.xex \
gtia_mode_5.xex \
gtia_mode_6.xex \
gtia_mode_7.xex \
gtia_mode_8.xex \
gtia_mode_9.xex \
gtia_mode_A.xex \
gtia_mode_B.xex
Výsledkem překladu jsou soubory s koncovkou .xex, které je možné přímo spustit v emulátoru osmibitových počítačů Atari.
Repositář s demonstračními příklady
Všechny demonstrační příklady, s nimiž jsme se v předchozích článcích i v článku dnešním seznámili a které jsou určeny pro překlad s využitím assembleru ca65, jsou dostupné, jak je zvykem, na GitHubu. V tabulce níže jsou uvedeny odkazy na jednotlivé zdrojové kódy příkladů psané v assembleru i „listingy“ vygenerované samotným assemblerem, ze kterých je patrné, jakým způsobem se jednotlivé příklady přeložily do výsledného XEX souboru:
Odkazy na Internetu
- MOS 6502 instruction set
http://www.6502.org/users/obelisk/6502/instructions.html - EXE File Format Description
https://gury.atari8.info/refs/file_formats_exe.php - XEX Filter – A toolkit to analyze and manipulate Atari binary files
https://www.vitoco.cl/atari/xex-filter/index.html - chkxex.py
https://raw.githubusercontent.com/seban-slt/tcx_tools/refs/heads/master/chkxex.py - ca65 Users Guide
https://cc65.github.io/doc/ca65.html - cc65 Users Guide
https://cc65.github.io/doc/cc65.html - ld65 Users Guide
https://cc65.github.io/doc/ld65.html - da65 Users Guide
https://cc65.github.io/doc/da65.html - Překladače jazyka C pro historické osmibitové mikroprocesory
https://www.root.cz/clanky/prekladace-jazyka-c-pro-historicke-osmibitove-mikroprocesory/ - Překladače programovacího jazyka C pro historické osmibitové mikroprocesory (2)
https://www.root.cz/clanky/prekladace-programovaciho-jazyka-c-pro-historicke-osmibitove-mikroprocesory-2/ - Getting Started Programming in C: Coding a Retro Game with C Part 2
https://retrogamecoders.com/getting-started-with-c-cc65/ - NES game development in 6502 assembly – Part 1
https://kibrit.tech/en/blog/nes-game-development-part-1 - NES 6502 Programming Tutorial – Part 1: Getting Started
https://dev.xenforo.relay.cool/index.php?threads/nes-6502-programming-tutorial-part-1-getting-started.858389/ - Minimal NES example using ca65
https://github.com/bbbradsmith/NES-ca65-example - List of 6502-based Computers and Consoles
https://www.retrocompute.co.uk/list-of-6502-based-computers-and-consoles/ - 6502 – the first RISC µP
http://ericclever.com/6500/ - 3 Generations of Game Machine Architecture
http://www.atariarchives.org/dev/CGEXPO99.html - “Hello, world” from scratch on a 6502 — Part 1
https://www.youtube.com/watch?v=LnzuMJLZRdU - A Tour of 6502 Cross-Assemblers
https://bumbershootsoft.wordpress.com/2016/01/31/a-tour-of-6502-cross-assemblers/ - Adventures with ca65
https://atariage.com/forums/topic/312451-adventures-with-ca65/ - example ca65 startup code
https://atariage.com/forums/topic/209776-example-ca65-startup-code/ - 6502 PRIMER: Building your own 6502 computer
http://wilsonminesco.com/6502primer/ - 6502 Instruction Set
https://www.masswerk.at/6502/6502_instruction_set.html - Chip Hall of Fame: MOS Technology 6502 Microprocessor
https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-mos-technology-6502-microprocessor - Single-board computer
https://en.wikipedia.org/wiki/Single-board_computer - www.6502.org
http://www.6502.org/ - 6502 PRIMER: Building your own 6502 computer – clock generator
http://wilsonminesco.com/6502primer/ClkGen.html - Great Microprocessors of the Past and Present (V 13.4.0)
http://www.cpushack.com/CPU/cpu.html - Jak se zrodil procesor?
https://www.root.cz/clanky/jak-se-zrodil-procesor/ - Osmibitové mikroprocesory a mikrořadiče firmy Motorola (1)
https://www.root.cz/clanky/osmibitove-mikroprocesory-a-mikroradice-firmy-motorola-1/ - Mikrořadiče a jejich použití v jednoduchých mikropočítačích
https://www.root.cz/clanky/mikroradice-a-jejich-pouziti-v-jednoduchych-mikropocitacich/ - Mikrořadiče a jejich aplikace v jednoduchých mikropočítačích (2)
https://www.root.cz/clanky/mikroradice-a-jejich-aplikace-v-jednoduchych-mikropocitacich-2/ - 25 Microchips That Shook the World
https://spectrum.ieee.org/tech-history/silicon-revolution/25-microchips-that-shook-the-world - Comparison of instruction set architectures
https://en.wikipedia.org/wiki/Comparison_of_instruction_set_architectures - How To Start Learning Atari 8 Bit Assembly For Free
https://forums.atariage.com/topic/300732-how-to-start-learning-atari-8-bit-assembly-for-free/ - WUDSN (Demo Group)
https://www.wudsn.com/ - Machine Language For Beginners
https://www.atariarchives.org/mlb/ - Assembly language: all about I/O
https://www.atarimagazines.com/v3n8/AllAbout_IO.html - Sedmdesátiny assemblerů: lidsky čitelný strojový kód
https://www.root.cz/clanky/sedmdesatiny-assembleru-lidsky-citelny-strojovy-kod/ - Color names
https://atariwiki.org/wiki/Wiki.jsp?page=Color%20names - ATASCII
https://en.wikipedia.org/wiki/ATASCII - Put characters in display ram isn't ATASCII?
https://forums.atariage.com/topic/359973-put-characters-in-display-ram-isnt-atascii/ - ATASCII And Internal Character Code Values
https://www.atariarchives.org/mapping/appendix10.php - Reading ATASCII from the keyboard in assembly
https://forums.atariage.com/topic/361733-reading-atascii-from-the-keyboard-in-assembly/ - Why does the 6502 JSR instruction only increment the return address by 2 bytes?
https://retrocomputing.stackexchange.com/questions/19543/why-does-the-6502-jsr-instruction-only-increment-the-return-address-by-2-bytes - Pushing return address to stack off by 1 byte
https://forums.atariage.com/topic/378206-pushing-return-address-to-stack-off-by-1-byte/ - Intel x86 documentation has more pages than the 6502 has transistors
https://www.righto.com/2013/09/intel-x86-documentation-has-more-pages.html - Clearing a Section of Memory
http://www.6502.org/source/general/clearmem.htm - Practical Memory Move Routines by Bruce Clark
http://www.6502.org/source/general/memory_move.html - 6502 Assembly Programming Guide
https://neumont-gamedev.github.io/posts/retrogamedev-6502-guide/ - Off-by-one error
https://en.wikipedia.org/wiki/Off-by-one_error - 6502 cycle times
https://www.nesdev.org/wiki/6502_cycle_times - Atari TIA
http://www.atarihq.com/danb/tia.shtml - TIA Playfield
http://www.atarihq.com/danb/TIA/Playfield.shtml - Atari Inc.:
ANTIC C012296 (NTSC) Revision D
Atari Incorporated, Sunnyvale CA, 1982 - Atari Inc.:
GTIA C014805 (NTSC) Revision A
Atari Incorporated, Sunnyvale CA, 1982 - Atari 5200
http://www.atariage.com/software_search.html?SystemID=5200 - Atari 5200 Hardware and Accessories
http://www.atariage.com/5200/archives/hardware.html - Atari 5200 Screenshots
http://www.atariage.com/system_items.html?SystemID=5200&ItemTypeID=SCREENSHOT - History of video game consoles (second generation): Wikipedia
http://en.wikipedia.org/wiki/History_of_video_game_consoles_(second_generation) - Atari 5200: Wikipedia
http://en.wikipedia.org/wiki/Atari_5200 - Player-Missile Graphics
https://www.atariarchives.org/agagd/chapter5.php - Sprite (computer graphics)
https://en.wikipedia.org/wiki/Sprite_(computer_graphics) - Atari Graphics Demonstrations by Underground Software, 1985 | Atari 8 bit Demo
https://www.youtube.com/watch?v=h7N9EYSyCkw - Atari 8-bit Display List Interrupts: A Complete(ish) Tutorial
https://playermissile.com/dli_tutorial/ - Atari Assembler Editor manual
https://atariwiki.org/wiki/attach/Atari%20Assembler%20Editor/ATARI%20Assembler%20Editor%20User-s%20Manual-OCR.pdf - Atari 8-bit Fine Scrolling: A Complete(ish) Tutorial
https://playermissile.com/scrolling_tutorial/index.html - Atari Fine Scrolling
https://www.atarimagazines.com/compute/issue67/338_1_Atari_Fine_Scrolling.php - CTIA / GTIA Pinout Diagram
https://user.xmission.com/~trevin/atari/gtia_pinout.html - GTIA Modes
https://page6.org/archive/issue02/page10.htm - 56 graphic modes
https://www.atari800×l.eu/docs/kb/kb-hardware-0005-atari-8bit-56-graphic-modes.html - UNLOCKING THE 56 GRAPHIC MODES
https://www.atarimagazines.com/v3n5/allmodes.html - GTIA Modes 9, 10 & 11 + ANTIC data and color clocks
https://forums.atariage.com/topic/366256-gtia-modes-9–10–11-antic-data-and-color-clocks/ - ANTIC, GTIA and timing info
https://atarimax.com/jindroush.atari.org/atanttim.html - 6.10 Cycle timing
https://github.com/AnimaInCorpore/A8E/blob/main/AHRM/6.%20CTIA-GTIA/10.%20Cycle%20timing.md
