DLI – největší zbraň osmibitových Atari?
Co se dozvíte v článku
- DLI – největší zbraň osmibitových Atari?
- Základ pro všechny další demonstrační příklady využívající DLI
- Instrukce DLI v display listu
- Display list, který v polovině obrazovky vyvolá DLI
- Realizace obsluhy přerušení DLI
- Změna barvy pozadí pro dolní polovinu obrazovky
- Nastavení přerušovacího vektoru pro DLI
- Úplný zdrojový kód druhého demonstračního příkladu
- WSYNC – čekání na dokončení vykreslení celého obrazového řádku
- Nejjednodušší realizace změny barvy pozadí na dvou odlišným místech na obrazovce
- Úplný zdrojový kód třetího demonstračního příkladu
- Opakovaná změna barvy pozadí: vylepšená verze, ve které není CPU zdržován čekáním na HSYNC
- Úplný zdrojový kód třetího demonstračního příkladu
- Zjednodušená varianta DLI: přepínání mezi dvěma barvami pozadí
- Úplný zdrojový kód čtvrtého demonstračního příkladu
- Realizace zobrazení osmi spritů namísto čtyř spritů
- Úplný zdrojový kód dnešního 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
Ústředním prvkem osmibitových domácích mikropočítačů Atari je mikroprocesor MOS 6502. Z pohledu výpočetního výkonu se nejedná (a ani nejednalo) o nejrychlejší dostupný osmibitový CPU, což však konkrétně v případě Atari nevadilo, protože MOS 6502 byl doplněn o další čipy: ANTIC, GTIA a POKEY. Pravděpodobně nejdůležitější je ANTIC s vlastní instrukční sadou a přímým přístupem do operační paměti.
Připomeňme si, že display list slouží pro nastavení textových a grafických režimů pro každý logický řádek na obrazovce zvlášť. Ovšem navíc je možné u každého zobrazovaného řádku povolit přerušení DLI (Display-List Interrupt), které se provede při vykreslování řádku popř. je možné počkat až na horizontální zpětný běh elektronového paprsku na televizní obrazovce.
Obrázek 1: Atari ve skutečnosti dokáže zobrazit 128 barev a ne 256. To vše většinou díky DLI a VBI.
To je sice zajímavá technologie, ale proč by se vůbec mělo jednat o největší zbraň osmibitových Atari? Jen díky DLI je možné zobrazit větší počet barev (klidně i všech 128 dostupných barev, popř. scrollující barvy), současně zobrazit více než 128 různých znaků, zobrazit více než 4/5 spritů (což si dnes ukážeme), zobrazit sprity naprosto libovolnými barvami atd. Záleží jen na schopnostech programátora, jakým způsobem DLI využije.
Obrázek 2: Kombinace grafických režimů GR.9 a GR.11 na jediné obrazovce, opět s využitím DLI.
Obrázek 3: I v této slavné hře se využívá DLI, dokonce na několika místech. Mimochodem si povšimněte kombinace různých grafických režimů.
Základ pro všechny další demonstrační příklady využívající DLI
V rámci navazujících kapitol si ukážeme několik demonstračních příkladů, ve kterých se bude DLI využívat. Všechny tyto příklady budou postaveny na stejném základu, jenž je vypsán pod tímto odstavcem. Příklad obsahuje čtyři hlavní části: nastavení display listu, vyplnění obrazovky vzorem (všemi znaky v přímé i inverzní podobě), vlastní display list a paměťový prostor rezervovaný pro video RAM (960 bajtů). Mělo by se jednat o známé techniky podrobně popsané v předchozích třech článcích:
; ---------------------------------------------------------------------
; Výpis všech 128 znaků v přímé i inverzní podobě v režimu GR.0.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
ldy #0 ; počitadlo zápisů
fill_screen:
tya ; kód vypisovaného znaku
sta screen, y ; tisk znaku na zvolené místo na obrazovce
sta screen+40*12, y
iny ; zvětšit hodnotu počitadla a offsetu
bne fill_screen ; skok
loop: jmp loop
.endproc
dlist:
.byte DL_BLK8, DL_BLK8, 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
Po překladu a spuštění tohoto demonstračního příkladu by měla obrazovka Atari vypadat následovně:
Obrázek 4: Zobrazení dvou bloků s 256 znaky (128 znaků v přímé i inverzní podobě).
Instrukce DLI v display listu
Připomeňme si, že vykreslování herního pole (playfield) čipem ANTIC probíhá (zjednodušeně řečeno) následujícím způsobem:
- Přečte se instrukce display listu, což je jedna hodnota typu bajt nebo hodnota následovaná dvoubajtovou adresou
- Instrukce se provede, což znamená buď vykreslení řádku (text či grafika) nebo skok na jinou instrukci display listu
Existuje několik typů instrukcí, které lze do display listu zapsat, zejména:
- Vykreslení prázdných řádků vybarvených barvou okraje (border)
- Vykreslení textového řádku (písma) nebo grafického řádku (40, 80, 160 či 320 pixelů)
- Skok na jinou instrukci display listu
- Nastavení začátku obrazové paměti + vykreslení textového nebo grafického řádku (kupodivu se jedná o jedinou instrukci)
To však není vše, protože u každé instrukce display listu je možné jejím nejvyšším bitem nastavit, že se má provést DLI, tj. vyvolat přerušovací rutina. Můžeme si tedy nastavit pomocnou konstantu (je již definována v include souborech):
DL_DLI = 128
Funkce čipu ANTIC je ve skutečnosti taktéž odlišná:
- Přečte se instrukce display listu, což je jedna hodnota typu bajt nebo hodnota následovaná dvoubajtovou adresou
- Instrukce se provede, což znamená buď vykreslení řádku (text či grafika) nebo skok na jinou instrukci display listu
- Pokud je nastaven nejvyšší bit instrukce (DL_DLI), automaticky se zavolá subrutina pro obsluhu DLI. Co se v této subrutině děje, je plnou zodpovědností programátora.
Display list, který v polovině obrazovky vyvolá DLI
Podívejme se nyní, jak by mohl vypadat zápis display listu v případě, že při každém vykreslení obrazovky budeme chtít, aby se zhruba v její polovině vyvolala subrutina DLI. Je to snadné, protože původní display list již známe a postačuje nám k jedné instrukci představované konstantou DL_CHR40×8×1 přičíst konstantu DL_DLI:
dlist: .byte DL_BLK8, DL_BLK8, 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 .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Samozřejmě je možné použít zkrácený zápis založený na instrukci assembleru .res, který dokáže bajt (či jinou hodnotu) n-krát zopakovat. Alternativní způsob zápisu display listu tedy může vypadat následovně:
dlist: .byte DL_BLK8, DL_BLK8, 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 10, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI .res 12, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Realizace obsluhy přerušení DLI
Dále si musíme ukázat, jak vlastně bude vypadat vlastní obsluha přerušení DLI. Jedná se o běžnou subrutinu, která musí končit instrukcí RTI (Return from Interrupt):
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
...
...
...
rti ; návrat z DLI
Subrutina nesmí navenek měnit žádné pracovní registry, tudíž ani akumulátor. V praxi tedy bude základ subrutiny DLI vypadat takto:
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
...
...
...
pla ; obnovit akumulátor
rti ; návrat z DLI
Změna barvy pozadí pro dolní polovinu obrazovky
Podívejme se na konkrétní subrutinu DLI, která modifikuje HW registr COLPF2, což je registr čipu GTIA. Zápisem nějaké hodnoty do tohoto registru se ihned změní barva vykreslování pozadí (výchozí barva je modrá – typicky Atarácká barva):
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda #$c4 ; barva pozadí
sta COLPF2 ; přímo nastavit zápisem do HW registru
pla ; obnovit akumulátor
rti ; návrat z DLI
DLI je vyvoláno zhruba v polovině obrazovky, takže její druhá polovina bude mít barvu 0×c4. Ovšem jak je možné, že horní polovina dalšího snímku bude zase modrá? Obsah registru COLPF2 je totiž po vykreslení celého snímku automaticky obnoven ze svého stínového registru COLOR2 (o to se stará jiná subrutina – VBI).
Nastavení přerušovacího vektoru pro DLI
V posledním kroku musíme systému oznámit, na kterých adresách se vlastně subrutina DLI nachází. Adresu naší subrutiny nazvané příznačně dli uložíme do „vektoru“ umístěného na adresách VDSLST a VDSLST+1:
VDSLST = $0200 ;DISPLAY LIST NMI VECTOR
Pro rozdělení adresy na horní a dolní bajt slouží operátory < a > (adresu totiž zná již assembler, takže horní a spodní bajt dokáže automaticky vypočítat):
; vektor DLI
lda #<dli ; nastavení DLI
sta VDSLST
lda #>dli
sta VDSLST+1
Úplný zdrojový kód druhého demonstračního příkladu
Obsluha DLI v podobě, v jaké byla ukázána v předchozích kapitolách, byla přidána do dnešního druhého demonstračního příkladu. Po jeho spuštění se sice stále zobrazí znaky v textovém režimu GR.0, ovšem spodní polovina obrazovky bude mít odlišnou barvu pozadí:
Obrázek 5: Díky DLI bylo umožněno přepnout spodní polovinu pozadí na zelenou barvu.
Následuje výpis úplného zdrojového kódu dnešního druhého demonstračního příkladu:
; ---------------------------------------------------------------------
; Výpis všech 128 znaků v přímé i inverzní podobě v režimu GR.0.
;
; Dočasná modifikace barvy pozadí provedená v DLI (dvanáctý textový řádek)
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
ldy #0 ; počitadlo zápisů
fill_screen:
tya ; kód vypisovaného znaku
sta screen, y ; tisk znaku na zvolené místo na obrazovce
sta screen+40*12, y
iny ; zvětšit hodnotu počitadla a offsetu
bne fill_screen ; skok
; 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:
pha ; uschovat akumulátor
lda #$c4 ; barva pozadí
sta COLPF2 ; 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_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 10, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 12, 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
WSYNC – čekání na dokončení vykreslení celého obrazového řádku
DLI se sice vyvolá ve chvíli, kdy ANTIC přečte instrukci s příznakem DL_DLI, ovšem v případě, že se mají provádět viditelné změny na obrazovce, potřebujeme mít k dispozici poněkud přesnější mechanismus. Ideální by bylo mít možnost počkat, až se vykreslí celý obrazový řádek a paprsek (CRT) se bude vracet na levý okraj obrazovky (pochopitelně bez kreslení). V tomto okamžiku totiž můžeme provést například změnu barvy, pozic spritů atd. – vše se projeví až na dalším obrazovém řádku, bez viditelného poblikávání a dalších nežádoucích efektů.
Ve skutečnosti je tato technika na osmibitových Atari k dispozici. Čip ANTIC totiž obsahuje HW registr nazvaný příhodně WSYNC (neboli Wait for Sync). Zápis libovolné hodnoty do tohoto registru pozastaví CPU (MOS 6502) až do chvíle, kdy se začne paprsek vracet na začátek dalšího řádku (ANTIC k tomu používá pin RDY). To znamená, že čekání na HSYNC (Horizontal sync) je na počítačích Atari až triviálně … triviální:
sta WSYNC ; čekat na WSYNC
Nejjednodušší realizace změny barvy pozadí na dvou odlišným místech na obrazovce
Výše uvedeného triku se zápisem do HW registru WSYNC je možné relativně snadno využít. Pokud například budeme chtít změnit barvu pozadí obrazovky dvakrát, můžeme do rutiny DLI přidat několik zápisů do WSYNC, přičemž každý zápis znamená čekání na dokončení vykreslení dalšího obrazového řádku (a osm obrazových řádků nám dá jeden celý textový řádek). Tato technika vlastně zastaví činnost CPU na poměrně dlouhou dobu (pro jednoduchost počítejme 100 strojových cyklů na každý řádek), ovšem v některých případech můžete tento strojový čas oželet:
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda #$c4 ; barva pozadí
sta COLPF2 ; přímo nastavit zápisem do HW registru
sta WSYNC ; čekat na WSYNC
sta WSYNC
sta WSYNC
sta WSYNC
sta WSYNC
sta WSYNC
sta WSYNC
sta WSYNC
sta WSYNC
lda #$44 ; barva pozadí
sta COLPF2 ; přímo nastavit zápisem do HW registru
pla ; obnovit akumulátor
rti ; návrat z DLI
Výsledek by měl vypadat následovně:
Obrázek 6: Modifikace barvy pozadí a po osmi dalších obrazových řádcích nová modifikace.
Úplný zdrojový kód třetího demonstračního příkladu
Úplný zdrojový kód dnešního třetího demonstračního příkladu, který dokáže změnit barvu pozadí na dvou místech na obrazovce, vypadá následovně:
; ---------------------------------------------------------------------
; Výpis všech 128 znaků v přímé i inverzní podobě v režimu GR.0.
; Opakované čekání na synchronizační pulz v DLI, výběr odlišné barvy pozadí
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
ldy #0 ; počitadlo zápisů
clear:
tya ; kód vypisovaného znaku
sta screen, y ; tisk znaku na zvolené místo na obrazovce
sta screen+40*12, y
iny ; zvětšit hodnotu počitadla a offsetu
bne clear ; skok
; 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:
pha ; uschovat akumulátor
lda #$c4 ; barva pozadí
sta COLPF2 ; přímo nastavit zápisem do HW registru
sta WSYNC ; čekat na WSYNC
sta WSYNC
sta WSYNC
sta WSYNC
sta WSYNC
sta WSYNC
sta WSYNC
sta WSYNC
sta WSYNC
lda #$44 ; barva pozadí
sta COLPF2 ; 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_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 10, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 12, 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
Opakovaná změna barvy pozadí: vylepšená verze, ve které není CPU zdržován čekáním na HSYNC
V předchozím kódu jsme dvojí změnu barvy pozadí realizovali tak, že CPU čekal na několik signálů HSYNC. To je sice funkční řešení, ovšem vlastně se tak připravujeme o strojový čas. Výhodnější bude, když si subrutina volaná při DLI bude pamatovat, kolikrát byla zavolána, popř. jestli se jedná o „sudé“ nebo „liché“ volání atd. Musíme tedy realizovat nějakou formu paměti (a nesmí se jednat o pracovní registr). Z tohoto důvodu si zarezervujeme jeden bajt v paměti, který bude obsahovat barvu pozadí, která se má při vyvolání DLI nastavit. Například:
color: .byte $c4 ; původní barva
V samotné subrutině nejprve nastavíme (změníme) barvu pozadí a následně na základě jednoduché podmínky zapíšeme do paměti na adresu color novou barvu pozadí (ta se použije při dalším volání DLI). Naivní a zcela neoptimalizovaná varianta subrutiny bude vypadat následovně:
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda color ; barva pozadí
sta COLPF2 ; přímo nastavit zápisem do HW registru
cmp #$c4 ; prohazujeme barvy $c4 a $44
bne skip_set
lda #$44 ; přechod $c4 -> $44
sta color
pla ; obnovit akumulátor
rti ; návrat z DLI
skip_set:
lda #$c4 ; přechod $44 -> $44
sta color
pla ; obnovit akumulátor
rti ; návrat z DLI
Display list může vypadat následovně – povšimněte si, že hned dvě instrukce display listu obsahují příznak DL_DLI:
dlist: .byte DL_BLK8, DL_BLK8, 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 6, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI .res 7, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI .res 8, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Výsledek při dvojím volání DLI bude vypadat takto:
Obrázek 7: Tři přesně načasované modifikace barvy pozadí díky subrutině pro obsluhu DLI.
Úplný zdrojový kód třetího demonstračního příkladu
Opět si pro úplnost ukážeme celý zdrojový kód demonstračního příkladu, jehož subrutina volaná při DLI byla ukázána v předchozí kapitole:
; ---------------------------------------------------------------------
; Výpis všech 128 znaků v přímé i inverzní podobě v režimu GR.0.
; Display list obsahující několik instrukcí DLI, opakovaná změna barvy pozadí
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
ldy #0 ; počitadlo zápisů
clear:
tya ; kód vypisovaného znaku
sta screen, y ; tisk znaku na zvolené místo na obrazovce
sta screen+40*8, y
sta screen+40*16, y
iny ; zvětšit hodnotu počitadla a offsetu
bne clear ; skok
; 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:
pha ; uschovat akumulátor
lda color ; barva pozadí
sta COLPF2 ; přímo nastavit zápisem do HW registru
cmp #$c4 ; prohazujeme barvy $c4 a $44
bne skip_set
lda #$44 ; přechod $c4 -> $44
sta color
pla ; obnovit akumulátor
rti ; návrat z DLI
skip_set:
lda #$c4 ; přechod $44 -> $44
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_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 6, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 7, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 8, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
color:
.byte $c4 ; původní barva
end:
.BSS
screen: .res 40*2
.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
Zjednodušená varianta DLI: přepínání mezi dvěma barvami pozadí
Celou subrutinu volanou při DLI ovšem můžeme v některých případech zjednodušit, například tehdy, pokud se přepínáme mezi dvěma barvami, mezi nimiž existuje nějaký vztah vyjádřitelný aritmetickou či logickou operací. Příkladem může být přepínání dvou barev, jejichž kódy se od sebe odlišují pouze nejvyšším bitem. Přepnutí barev lze v takovém případě realizovat bez podmínky a podmíněného skoku:
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda color ; barva pozadí
sta COLPF2 ; přímo nastavit zápisem do HW registru
eor #%10000000 ; negovat nejvyšší bit
sta color
pla ; obnovit akumulátor
rti ; návrat z DLI
Výsledná obrazovka může vypadat takto:
Obrázek 8: Tři přesně načasované modifikace barvy pozadí díky subrutině pro obsluhu DLI.
Úplný zdrojový kód čtvrtého demonstračního příkladu
Opět se podívejme na úplný zdrojový kód čtvrtého demonstračního příkladu, který obsahuje zjednodušenou subrutinu pro DLI:
; ---------------------------------------------------------------------
; Výpis všech 128 znaků v přímé i inverzní podobě v režimu GR.0.
;
; Display list obsahující několik instrukcí DLI, opakovaná změna barvy pozadí
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
ldy #0 ; počitadlo zápisů
clear:
tya ; kód vypisovaného znaku
sta screen, y ; tisk znaku na zvolené místo na obrazovce
sta screen+40*8, y
sta screen+40*16, y
iny ; zvětšit hodnotu počitadla a offsetu
bne clear ; skok
; 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:
pha ; uschovat akumulátor
lda color ; barva pozadí
sta COLPF2 ; přímo nastavit zápisem do HW registru
eor #%10000000 ; negovat nejvyšší bit
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_CHR40x8x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 2 (GR.0)
.byte screen ; počáteční adresa obrazové paměti
.res 6, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 7, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 8, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
color:
.byte $c4 ; původní barva
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
Realizace zobrazení osmi spritů namísto čtyř spritů
V perexu dnešního článku jsme si mj. řekli, že DLI je možné využít pro zobrazení většího množství spritů, než čip GTIA oficiálně umožňuje. Předpokládejme pro jednoduchost, že na jednom obrazovém řádku budeme chtít zobrazit maximálně čtyři základní sprity (či pět spritů, pokud spojíme všechny střely do pátého hráče). V takovém případě je možné do bitmapy spritů (8×128 či 8×256 pixelů) nakreslit větší počet postaviček atd. a v rámci DLI přepínat horizontální pozice spritů, měnit jejich barvy či dokonce měnit samotné bitmapy spritů. Ukažme si to na jednoduchém příkladu, ve kterém namísto čtyř základních spritů zobrazíme spritů osm (ovšem nikoli na tom samém obrazovém řádku).
V rámci přípravy nastavíme horizontální pozice všech čtyř spritů (což není nic nového):
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
Stejným způsobem nastavíme jejich barvy:
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhé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
Poněkud odlišné je vyplnění bitmap spritů, protože každý sprite bude obsahovat dvě postavičky (s vertikálním offsetem 30 a 80 řádků):
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+30, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+30, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+30, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+30, x ; uložit byte - čtvrtý hráč
sta addr+PLAYER_0_OFFSET+80, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+80, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+80, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+80, 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
V display listu uvedeme dva řádky s povoleným DLI. Subrutina pro DLI se tedy zavolá na jedenáctém a třináctém textovém řádku:
dlist: .byte DL_BLK8, DL_BLK8, 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 10, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI .res 11, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
V operační paměti navíc budeme rezervovat bajt, do kterého je uložena horizontální pozice jednoho hráče:
offset: .byte 80
Nyní nastává onen trik, který na určitém místě na obrazovce posune všechny sprity a změní jejich barvy. V subrutině volané pro obsluhu DLI dočasně změníme barvy hráčů přímým zápisem do HW registrů čipu GTIA. A navíc i posuneme hráče o offset, který je postupně modifikován (proto se DLI volá dvakrát):
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda #$2f ; dočasné barvy hráčů
sta COLPM0
lda #$4f ; dočasné barvy hráčů
sta COLPM1
lda #$6f ; dočasné barvy hráčů
sta COLPM2
lda #$2f ; dočasné barvy hráčů
sta COLPM3
lda offset ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
cmp #80 ; prohazovat souřadnice 80 a 160
bne next
lda #160
sta offset
pla ; obnovit akumulátor
rti ; návrat z DLI
next:
lda #80
sta offset
pla ; obnovit akumulátor
rti ; návrat z DLI
Pochopitelně nás bude zajímat výsledek. Vězte tedy, že Atari dokáže zobrazit desítky spritů, nikoli pouze sprity čtyři:
Obrázek 9: Zobrazení celkem osmi spritů v osmi různých barvách.
Úplný zdrojový kód dnešního posledního demonstračního příkladu
Úplný zdrojový kód dnešního šestého a současně i posledního demonstračního příkladu vypadá následovně:
; ---------------------------------------------------------------------
; Zobrazení osmi spritů namísto čtyř spritů s využitím DLI.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
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
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
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_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhé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+30, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+30, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+30, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+30, x ; uložit byte - čtvrtý hráč
sta addr+PLAYER_0_OFFSET+80, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+80, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+80, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+80, 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
; 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
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda #$2f ; dočasné barvy hráčů
sta COLPM0
lda #$4f ; dočasné barvy hráčů
sta COLPM1
lda #$6f ; dočasné barvy hráčů
sta COLPM2
lda #$2f ; dočasné barvy hráčů
sta COLPM3
lda offset ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
cmp #80 ; prohazovat souřadnice 80 a 160
bne next
lda #160
sta offset
pla ; obnovit akumulátor
rti ; návrat z DLI
next:
lda #80
sta offset
pla ; obnovit akumulátor
rti ; návrat z DLI
.endproc
dlist:
.byte DL_BLK8, DL_BLK8, 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 10, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 11, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
offset: .byte 80
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
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
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
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
