V trojici článků o čipu POKEY, které na Rootu vyšly v předchozích týdnech [1] [2] [3] jsme se zaměřili na popis způsobu generování zvuků (a částečně i hudby) tímto integrovaným obvodem. Ovšem POKEY je víceúčelovým čipem, protože realizuje resp. přesněji řečeno umožňuje realizovat hned několik činností: generování zvuků a hudby (již známe), čtení stavů paddle (potenciometrů), čtení klávesnice (64 běžných kláves + 3 speciálně zpracovávané klávesy Control, Shift a Break), obsahuje generátor pseudonáhodných hodnot, dále čítače (využívají tři zvukové kanály, využití našly například při práci s magnetofonem), sériový I/O a řízení IRQ (přerušení). V dnešním článku se začneme zabývat „nezvukovými“ vlastnostmi tohoto integrovaného obvodu.
Co se dozvíte v článku
- Řídicí a stavové registry čipu POKEY
- Přístup k řídicím a stavovým registrům z assembleru
- Generátor pseudonáhodných hodnot
- První demonstrační příklad: výpis pseudonáhodných hodnot formou sekvence ATASCII znaků
- Grafická vizualizace pseudonáhodných hodnot
- Druhý demonstrační příklad: vizualizace pseudonáhodných hodnot v grafickém režimu 80×48 pixelů
- Třetí a čtvrtý příklad: vyplnění celé obrazovky pseudonáhodnými hodnotami
- Krátká odbočka do ještě vzdálenější historie
- Herní ovladače typu paddle
- Způsob připojení paddle a měření natočení ovladače
- Registry POKEY použité pro práci s paddle
- Čtení natočení paddle operačním systémem
- Využití paddle ve hrách a kreslicích programech
- Pátý demonstrační příklad: příprava na ovládání hráče
- Šestý demonstrační příklad: přímé ovládání hráče s využitím paddle
- Obsah navazujícího článku
- Příloha: Makefile pro překlad všech demonstračních příkladů
- Repositář s demonstračními příklady
- Odkazy na Internetu
Řídicí a stavové registry čipu POKEY
Ovládání či naopak čtení stavu čipu POKEY se provádí s využitím řídicích a stavových registrů. Adresový rozsah vyhrazený pro POKEY je roven šestnácti adresám, ovšem využívá se jich jen 14 pro čtení a 15 pro zápis. Jak je na osmibitových Atari zvykem, je při zápisu na určitou adresu použit (zcela) odlišný registr, než pro čtení. Konkrétně tedy při čtení z adresy $d200 získáme stav natočení prvního paddle, kdežto při zápisu na stejnou adresu nastavujeme děličku frekvence prvního zvukového kanálu. I z tohoto důvodu existují pro vybrané registry takzvané stínové registry, což jsou místa v operační paměti, kam operační systém zapisuje stejné informace, jaké zapisuje do řídicích registrů (odsud totiž není možné čtení provést):
| Jméno | Stínový registr | Čtení/zápis | Adresa | Stručný popis |
|---|---|---|---|---|
| POT0 | PADDL0 | čtení | $d200 | měření natočení prvního paddle |
| POT1 | PADDL1 | čtení | $d201 | měření natočení druhého paddle |
| POT2 | PADDL2 | čtení | $d202 | měření natočení třetího paddle |
| POT3 | PADDL3 | čtení | $d203 | měření natočení čtvrtého paddle |
| POT4 | PADDL4 | čtení | $d204 | měření natočení pátého paddle |
| POT5 | PADDL5 | čtení | $d205 | měření natočení šestého paddle |
| POT6 | PADDL6 | čtení | $d206 | měření natočení sedmého paddle |
| POT7 | PADDL7 | čtení | $d207 | měření natočení osmého paddle |
| ALLPOT | čtení | $d208 | stav měření všech osmi paddle (0 znamená validní informaci, 1 prováděné měření) | |
| KBCODE | CH | čtení | $d209 | kód stisknuté klávesy (což není zcela přesné, viz další text) |
| RANDOM | čtení | $d20a | hodnota generátoru pseudonáhodných hodnot | |
| SERIN | čtení | $d20d | buffer pro data posílaná po sériovém portu (čtení-příjem) | |
| IRQST | čtení | $d20e | stav jednotlivých přerušení (IRQ) | |
| SKSTAT | čtení | $d20f | stavový registr sériového portu | |
| AUDF1 | zápis | $d200 | nastavení děličky frekvence prvního zvukového kanálu | |
| AUDC1 | zápis | $d201 | hlasitost + zkreslení (poly čítače) prvního zvukového kanálu | |
| AUDF2 | zápis | $d202 | nastavení děličky frekvence druhého zvukového kanálu | |
| AUDC2 | zápis | $d203 | hlasitost + zkreslení (poly čítače) druhého zvukového kanálu | |
| AUDF3 | zápis | $d204 | nastavení děličky frekvence třetího zvukového kanálu | |
| AUDC3 | zápis | $d205 | hlasitost + zkreslení (poly čítače) třetího zvukového kanálu | |
| AUDF4 | zápis | $d206 | nastavení děličky frekvence čtvrtého zvukového kanálu | |
| AUDC4 | zápis | $d207 | hlasitost + zkreslení (poly čítače) čtvrtého zvukového kanálu | |
| AUDCTL | zápis | $d208 | globální nastavení celého zvukového subsystému (frekvence+spojení zvukových kanálů) | |
| STIMER | zápis | $d209 | zapnutí časovače | |
| SKRES | zápis | $d20a | zápis jakékoli hodnoty provede vynulování bitů 5 až 7 registru SKSTAT | |
| POTGO | zápis | $d20b | inicializace měření natočení paddle (vybití kondenzátorů) | |
| SEROUT | zápis | $d20d | buffer pro data posílaná po sériovém portu (zápis-vysílání) | |
| IRQEN | POKMSK | zápis | $d20e | povolení či zákaz jednotlivých přerušení |
| SKCTL | SSKCLT | zápis | $d20f | řízení sériového portu, režimu čtení klávesnice a rychlého čtení paddle |
Alternativně je možné registry rozdělit podle subsystému, který ovládají nebo o němž zjišťují stav.
Zvukový subsystém:
| Jméno | Stínový registr | Čtení/zápis | Adresa | Stručný popis |
|---|---|---|---|---|
| AUDF1 | zápis | $d200 | nastavení děličky frekvence prvního zvukového kanálu | |
| AUDC1 | zápis | $d201 | hlasitost + zkreslení (poly čítače) prvního zvukového kanálu | |
| AUDF2 | zápis | $d202 | nastavení děličky frekvence druhého zvukového kanálu | |
| AUDC2 | zápis | $d203 | hlasitost + zkreslení (poly čítače) druhého zvukového kanálu | |
| AUDF3 | zápis | $d204 | nastavení děličky frekvence třetího zvukového kanálu | |
| AUDC3 | zápis | $d205 | hlasitost + zkreslení (poly čítače) třetího zvukového kanálu | |
| AUDF4 | zápis | $d206 | nastavení děličky frekvence čtvrtého zvukového kanálu | |
| AUDC4 | zápis | $d207 | hlasitost + zkreslení (poly čítače) čtvrtého zvukového kanálu | |
| AUDCTL | zápis | $d208 | globální nastavení celého zvukového subsystému (frekvence+spojení zvukových kanálů) |
Práce s ovladači typu paddle:
| Jméno | Stínový registr | Čtení/zápis | Adresa | Stručný popis |
|---|---|---|---|---|
| POT0 | PADDL0 | čtení | $d200 | měření natočení prvního paddle |
| POT1 | PADDL1 | čtení | $d201 | měření natočení druhého paddle |
| POT2 | PADDL2 | čtení | $d202 | měření natočení třetího paddle |
| POT3 | PADDL3 | čtení | $d203 | měření natočení čtvrtého paddle |
| POT4 | PADDL4 | čtení | $d204 | měření natočení pátého paddle |
| POT5 | PADDL5 | čtení | $d205 | měření natočení šestého paddle |
| POT6 | PADDL6 | čtení | $d206 | měření natočení sedmého paddle |
| POT7 | PADDL7 | čtení | $d207 | měření natočení osmého paddle |
| ALLPOT | čtení | $d208 | stav měření všech osmi paddle (0 znamená validní informaci, 1 prováděné měření) | |
| POTGO | zápis | $d20b | inicializace měření natočení paddle (vybití kondenzátorů) |
Sériový I/O port:
| Jméno | Stínový registr | Čtení/zápis | Adresa | Stručný popis |
|---|---|---|---|---|
| SERIN | čtení | $d20d | buffer pro data posílaná po sériovém portu (čtení-příjem) | |
| SKSTAT | čtení | $d20f | stavový registr sériového portu | |
| SEROUT | zápis | $d20d | buffer pro data posílaná po sériovém portu (zápis-vysílání) | |
| SKCTL | SSKCLT | zápis | $d20f | řízení sériového portu, režimu čtení klávesnice a rychlého čtení paddle |
| SKRES | zápis | $d20a | zápis jakékoli hodnoty provede vynulování bitů 5 až 7 registru SKSTAT |
Klávesnice:
| Jméno | Stínový registr | Čtení/zápis | Adresa | Stručný popis |
|---|---|---|---|---|
| KBCODE | CH | čtení | $d209 | kód stisknuté klávesy (což není zcela přesné, viz další text) |
IRQ:
| Jméno | Stínový registr | Čtení/zápis | Adresa | Stručný popis |
|---|---|---|---|---|
| IRQST | čtení | $d20e | stav jednotlivých přerušení (IRQ) | |
| IRQEN | POKMSK | zápis | $d20e | povolení či zákaz jednotlivých přerušení |
Generátor pseudonáhodných hodnot a časovač:
| Jméno | Stínový registr | Čtení/zápis | Adresa | Stručný popis |
|---|---|---|---|---|
| RANDOM | čtení | $d20a | hodnota generátoru pseudonáhodných hodnot | |
| STIMER | zápis | $d209 | zapnutí časovače |
Přístup k řídicím a stavovým registrům z assembleru
Při programování v assembleru samozřejmě nemusíme pracovat přímo s číselnými adresami registrů POKEY, protože v souboru atari.inc máme k dispozici příslušné konstanty. Zejména počáteční adresu adresové prostoru čipu POKEY:
POKEY = $D200 ;POKEY area
Adresy jednotlivých registrů jsou pak od POKEY odvozeny:
; Read Addresses POT0 = POKEY + $00 ;potentiometer 0 POT1 = POKEY + $01 ;potentiometer 1 POT2 = POKEY + $02 ;potentiometer 2 POT3 = POKEY + $03 ;potentiometer 3 POT4 = POKEY + $04 ;potentiometer 4 POT5 = POKEY + $05 ;potentiometer 5 POT6 = POKEY + $06 ;potentiometer 6 POT7 = POKEY + $07 ;potentiometer 7 ALLPOT = POKEY + $08 ;potentiometer port status KBCODE = POKEY + $09 ;keyboard code RANDOM = POKEY + $0A ;random number generator SERIN = POKEY + $0D ;serial port input IRQST = POKEY + $0E ;IRQ interrupt status SKSTAT = POKEY + $0F ;serial port and keyboard status ; Write Addresses AUDF1 = POKEY + $00 ;channel 1 audio frequency AUDC1 = POKEY + $01 ;channel 1 audio control AUDF2 = POKEY + $02 ;channel 2 audio frequency AUDC2 = POKEY + $03 ;channel 2 audio control AUDF3 = POKEY + $04 ;channel 3 audio frequency AUDC3 = POKEY + $05 ;channel 3 audio control AUDF4 = POKEY + $06 ;channel 4 audio frequency AUDC4 = POKEY + $07 ;channel 4 audio control AUDCTL = POKEY + $08 ;audio control STIMER = POKEY + $09 ;start timers SKRES = POKEY + $0A ;reset SKSTAT status POTGO = POKEY + $0B ;start potentiometer scan sequence SEROUT = POKEY + $0D ;serial port output IRQEN = POKEY + $0E ;IRQ interrupt enable SKCTL = POKEY + $0F ;serial port and keyboard control
Generátor pseudonáhodných hodnot
Praktickou část dnešního článku začneme tou nejjednodušší vlastností čipu POKEY. Přes stavový registr RANDOM (určen je jen pro čtení) je totiž možné v libovolný okamžik přečíst osm bitů s pseudonáhodnými hodnotami. Interně je pro generování pseudonáhodných hodnot využit sedmnáctibitový posuvný registr se zpětnou vazbou (poly čítač, LFSR – Linear-feedback shift register), s nímž jsme se již setkali při popisu zvukového subsystému. Tento poly čítač tedy slouží jak ke generování šumu, tak i pseudonáhodných hodnot (ostatně proč nevyužít již tak drahé tranzistory pro více účelů?). Tento čítač mění svůj stav s frekvencí CPU (tedy cca 1,7 MHz) a registr RANDOM lze díky tomu, že v počítači vznikají a jsou zpracovávána různá přerušení, dochází k přerušení od ANTICu atd., považovat za dostatečně kvalitní generátor (pseudo)náhodných hodnot – pro hry je vhodný zcela jistě a pro kryptografické aplikace nejsou osmibitová Atari určena :-)
První demonstrační příklad: výpis pseudonáhodných hodnot formou sekvence ATASCII znaků
V dnešním prvním demonstračním příkladu se na prvních šest textových řádků standardního grafického režimu GR.0 vypíšou náhodné ATASCII znaky, jejichž kódy byly získány právě čtením registru RANDOM:
Obrázek 1: Pseudonáhodné znaky generované s využitím čipu POKEY.
Po každém stisku klávesy se provede překreslení obrazovky novou sadou pseudonáhodných znaků:
Obrázek 2: Odlišné pseudonáhodné znaky generované s využitím čipu POKEY.
ldy #0 ; počitadlo zápisů
fills:
lda RANDOM
sta (88), y ; tisk znaku na zvolené místo na obrazovce
; (adresa Video RAM je na adresách 88 a 89)
iny ; zvětšit hodnotu počitadla a offsetu
cpy #40*6 ; test na koncovou hodnotu počitadla
bne fills ; skok, pokud Y>40*6
Následuje výpis úplného zdrojového kódu tohoto demonstračního příkladu:
; ---------------------------------------------------------------------
; Vyplnění bloku náhodnými hodnotami.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
loop:
ldy #0 ; počitadlo zápisů
fills:
lda RANDOM
sta (88), y ; tisk znaku na zvolené místo na obrazovce
; (adresa Video RAM je na adresách 88 a 89)
iny ; zvětšit hodnotu počitadla a offsetu
cpy #40*6 ; test na koncovou hodnotu počitadla
bne fills ; skok, pokud Y>40*6
jsr get_key
jmp loop
.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
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
Grafická vizualizace pseudonáhodných hodnot
U generátorů pseudonáhodných hodnot vždy stojíme před otázkou: do jaké míry jsou čtené hodnoty „náhodné“ a zda mají dostatečně dlouhou periodu (ostatně samotná existence konečné periody znamená, že hodnoty nejsou skutečně náhodné). Pochopitelně bychom pro analýzu mohli použít různé předpřipravené nástroje, ovšem pro jednoduchost si vystačíme s lidským mozkem a jeho systémem rozpoznávání vzorů (v chaosu). Nejprve nastavíme monochromatický grafický režim a následně do obrazové paměti uložíme pseudonáhodné hodnoty získané čtením z registru RANDOM. Pokud vznikne ve čtených datech krátká perioda popř. se nějaké vzorky budou opakovat, lidský mozek by tuto skutečnost měl zaznamenat.
Monochromatický režim bude mít rozlišení 80×48 pixelů (není nutné řešit stránkování) a jeho display list vypadá takto:
dlist: .byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků .byte DL_LMS+DL_MAP80x4x2 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 9 (GR.4) .byte <screen, >screen ; počáteční adresa obrazové paměti .res 47, DL_MAP80x4x2 ; opakovat řádky grafického režimu 9 (GR.4) .byte DL_JVB, dlist ; skok na začátek display listu
Druhý demonstrační příklad: vizualizace pseudonáhodných hodnot v grafickém režimu 80×48 pixelů
V dnešním druhém demonstračním příkladu se nejdříve nastaví monochromatický grafický režim s rozlišením 80×48 pixelů, přičemž obrazovka bude inicializována statickými daty:
Obrázek 3: Na začátku má obrazová paměť tento obsah.
Po stisku libovolné klávesy se prvních 240 bajtů, tedy prvních 24 obrazových řádků přepíše pseudonáhodnými daty přečtenými z registru RANDOM:
Obrázek 4: Vyplnění prvních 240 bajtů obrazové paměti pseudonáhodnými daty.
Každý další stisk klávesy tuto první část obrazové paměti přepíše novými pseudonáhodnými hodnotami:
Obrázek 5: Vyplnění prvních 240 bajtů obrazové paměti odlišnými pseudonáhodnými daty.
Úplný zdrojový kód takto upraveného demonstračního příkladu vypadá následovně:
; ---------------------------------------------------------------------
; Vyplnění bloku náhodnými hodnotami.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #0 ; kod barvy
sta COLOR0 ; ulozit do registru COLOR2
lda #10 ; kod barvy
sta COLOR4 ; ulozit do registru COLOR4
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
jsr get_key
loop:
ldy #0 ; počitadlo zápisů
fills:
lda RANDOM
sta screen, y ; tisk znaku na zvolené místo na obrazovce
iny ; zvětšit hodnotu počitadla a offsetu
cpy #40*6 ; test na koncovou hodnotu počitadla
bne fills ; skok, pokud Y>40*6
jsr get_key
jmp loop
.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_MAP80x4x2 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 9 (GR.4)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 47, DL_MAP80x4x2 ; opakovat řádky grafického režimu 9 (GR.4)
.byte DL_JVB, dlist ; skok na začátek display listu
screen: ; video paměť
.include "image_80x48.asm"
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
Třetí a čtvrtý příklad: vyplnění celé obrazovky pseudonáhodnými hodnotami
Další, v pořadí již třetí demonstrační příklad, vykresluje pseudonáhodné pixely na celou obrazovku. Díky použitému grafickému režimu je nutné vyplnit pouze 480 bajtů, což znamená dvojici programových smyček (každá pro vyplnění resp. nastavení 240 bajtů). Poněkud naivní, ale na druhou stranu plně funkční, způsob implementace může vypadat takto:
fills1:
lda RANDOM
sta screen, y ; tisk osmice pixelu na zvolené místo na obrazovce
iny ; zvětšit hodnotu počitadla a offsetu
cpy #40*6 ; test na koncovou hodnotu počitadla
bne fills1 ; skok, pokud Y>40*6
ldy #0 ; počitadlo zápisů
fills2:
lda RANDOM
sta screen+40*6, y ; tisk osmice pixelu na zvolené místo na obrazovce
iny ; zvětšit hodnotu počitadla a offsetu
cpy #40*6 ; test na koncovou hodnotu počitadla
bne fills2 ; skok, pokud Y>40*6
Obrázek 6: Vyplnění celé obrazové paměti (480 bajtů) pseudonáhodnými daty.
Obrázek 7: Odlišné pseudonáhodné hodnoty vyplňující celou obrazovou paměť.
Úplný zdrojový kód takto rozšířeného demonstračního příkladu vypadá následovně:
; ---------------------------------------------------------------------
; Vyplnění bloku náhodnými hodnotami.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #0 ; kod barvy
sta COLOR0 ; ulozit do registru COLOR2
lda #10 ; kod barvy
sta COLOR4 ; ulozit do registru COLOR4
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
jsr get_key
loop:
ldy #0 ; počitadlo zápisů
fills1:
lda RANDOM
sta screen, y ; tisk osmice pixelu na zvolené místo na obrazovce
iny ; zvětšit hodnotu počitadla a offsetu
cpy #40*6 ; test na koncovou hodnotu počitadla
bne fills1 ; skok, pokud Y>40*6
ldy #0 ; počitadlo zápisů
fills2:
lda RANDOM
sta screen+40*6, y ; tisk osmice pixelu na zvolené místo na obrazovce
iny ; zvětšit hodnotu počitadla a offsetu
cpy #40*6 ; test na koncovou hodnotu počitadla
bne fills2 ; skok, pokud Y>40*6
jsr get_key
jmp loop
.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_MAP80x4x2 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 9 (GR.4)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 47, DL_MAP80x4x2 ; opakovat řádky grafického režimu 9 (GR.4)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
screen: ; video paměť
.include "image_80x48.asm"
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
Výhodnější však může být uložení „vyplňovací smyčky“ do makra, kterému se předá počáteční adresa vyplňovaného bloku a celkový počet bajtů (do 255):
.macro fill_random_pixels address, size
.local fills
ldy #0 ; počitadlo zápisů
fills:
lda RANDOM
sta address, y ; tisk znaku osmice pixelu na zvolené místo na obrazovce
iny ; zvětšit hodnotu počitadla a offsetu
cpy size ; test na koncovou hodnotu počitadla
bne fills ; skok, pokud Y>size
ldy #0 ; počitadlo zápisů
.endmacro
Toto makro bude v programu expandováno celkem dvakrát:
fill_random_pixels screen, 40*6
fill_random_pixels screen+40*6, 40*6
Výsledný programový kód bude po těchto úpravách vypadat takto:
; ---------------------------------------------------------------------
; Vyplnění bloku náhodnými hodnotami.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.macro fill_random_pixels address, size
.local fills
ldy #0 ; počitadlo zápisů
fills:
lda RANDOM
sta address, y ; tisk znaku osmice pixelu na zvolené místo na obrazovce
iny ; zvětšit hodnotu počitadla a offsetu
cpy size ; test na koncovou hodnotu počitadla
bne fills ; skok, pokud Y>size
ldy #0 ; počitadlo zápisů
.endmacro
.proc main
lda #0 ; kod barvy
sta COLOR0 ; ulozit do registru COLOR2
lda #10 ; kod barvy
sta COLOR4 ; ulozit do registru COLOR4
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
jsr get_key
loop:
fill_random_pixels screen, 40*6
fill_random_pixels screen+40*6, 40*6
jsr get_key
jmp loop
.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_MAP80x4x2 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 9 (GR.4)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 47, DL_MAP80x4x2 ; opakovat řádky grafického režimu 9 (GR.4)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
screen: ; video paměť
.include "image_80x48.asm"
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
Krátká odbočka do ještě vzdálenější historie
Vraťme se na chvíli do dávné IT minulosti, konkrétně do roku 1966. Právě tehdy Nolan Bushnell najal mladého inženýra Allana Alcorna, aby pro něj navrhl a zkonstruoval závodní hru (vývoj probíhal paralelně s vývojem Computer Space). Pokud by Alan skutečně toto zadání zdárně dokončil, jednalo by se s velkou pravděpodobností o první závodní hru tohoto typu na světě, po několika týdnech vývoje se však ukázalo, že se při tehdejší úrovni integrace elektronických obvodů jedná o příliš ambiciózní projekt, protože výsledný produkt by byl velký a především drahý. Allan Alcorn tedy místo implementačně komplikované závodní hry vytvořil vlastní kopii hry Pong, která se stala velmi úspěšným titulem a velkou měrou přispěla k dalšímu vývoji herních automatů a posléze i domácích herních konzolí (nejdůležitější byl fakt, že se i další firmy začaly o toto zcela nové průmyslové odvětví zajímat, což pozitivně přispělo k vývoji dalších technologií, na jejichž konci stojí moderní osobní počítače)

Obrázek 8: Al Alcorn byl v pořadí třetím zaměstnancem firmy Atari.
Automat s touto hrou sice pro vykreslování herní scény používal rastrový displej (obyčejnou černobílou televizní obrazovku), ovšem dnes zcela běžný framebuffer, tj. obrazová paměť, ve které je uložena předloha vykreslovaného rastrového obrázku, zde nebyl implementován – namísto toho se obraz generoval v sadě integrovaných obvodů řízených přesným hodinovým signálem, podobně jako ve výše zmíněné hře Computer Space. V souvislosti s videohrou Pong se setkáváme s další důležitou firmou. Jedná se o společnost Magnavox, která vytvořila herní konzoli Odyssey právě se hrou Pong. Vzhledem k tomu, že konzole Odyssey byla vytvořena dříve než automat Pong, byl Nolan Bushnell a jeho právě založená firma Atari zažalována (korunu tomu dodal fakt, že Bushnell byl zapsán v seznamu hostů pozvaných na představení konzole Odyssey ještě před jejím vydáním a samozřejmě též před vydáním videohry Pong). Výsledkem tohoto soudního sporu bylo, že firma Atari v roce 1976 zakoupila „licenci“ za 700 000 dolarů na hru Pong, a tuto licenci samozřejmě zaplatila z výnosů, které jí tento herní automat přinášel.

Obrázek 9: Hra Pong ve variantě z roku 1972.
Herní ovladače typu paddle
Proč jsme se však o hře Pong vůbec zmiňovali? Není to proto, že by se jednalo o výrobek společnosti Atari, ale z toho důvodu, že se hra Pong ovládala dvojicí otočných ovladačů. Natočením se měnil odpor, který byl periodicky měřen a hra reagovala na vypočtenou hodnotu. Později byl podobný způsob ovládání použit i v domácí (přenosné) variantě hry Pong. A protože se ve stolním tenisu (ping pong) rakety označují slovem paddle, vžilo se toto označení i pro otočné herní ovladače, které interně využívaly buď rotační enkodér nebo (častěji) běžný potenciometr. A právě druhý typ ovladačů založených na potenciometru je podporován i osmibitovými domácími mikropočítači Atari. Ovladače typu paddle se připojovaly do stejných konektorů, jako joysticky. A vzhledem k tomu, že do jednoho joystickového konektoru mohly být připojeny dva ovladače typu paddle (piny paddle A a paddle B), bylo možné podle typu počítače pracovat buď se čtyřmi (série XL a XE) nebo dokonce osmi (původní Atari 400 a Atari 800) otočnými ovladači.

Obrázek 10: Kabinet se hrou Pong. Z panelu pod obrazovkou je patrné minimalistické pojetí ovládání této hry s využitím dvojice paddle.
Způsob připojení paddle a měření natočení ovladače
Každý ovladač paddle je většinou představovaný běžným potenciometrem, který je zapojen mezi pin s pěti volty a vstupní pin číslo 5 nebo 9 na joystickovém konektoru. Interně je každý z pinů (5, 9) interně zapojen ke kondenzátoru, který má svou druhou elektrodu uzemněnou. A navíc je paralelně ke kondenzátoru zapojen tranzistor ovládaný softwarově. Počítač při zjišťování, jak je paddle (a tedy vlastně potenciometr) natočen, provádí tyto operace:
- Tranzistor se sepne, čímž se kondenzátor vybije (pravděpodobně je do série zapojen i ochranný rezistor, to je však interní věc Atari).
- Tranzistor se otevře a zaznamená se čas t1
- Nyní se kondenzátor začne přes potenciometr nabíjet. Rychlost nabíjení závisí na aktuálním odporu potenciometru a tedy i na jeho natočení.
- Jakmile nabití dosáhne určité úrovně, dojde k zaznamenání času t2. Rozdíl t2-t1 je tím větší, čím větší je odpor potenciometru. PADDLE nepoužívá reálný čas; namísto toho počítá obrazové řádky.
Konkrétní způsob měření je prováděn operačním systémem a je odvozen – jako ostatně i mnoho dalších činností – od časování grafického subsystému. Konkrétně se čas měří v délce trvání obrazových řádků, ať již viditelných či neviditelných. Hodnoty, které jsou naměřeny, jsou tak v rozsahu 0 až 228, což je (jaká náhoda) rozsah horizontálních pozic spritů. Některé hry tedy mohly načtené údaje o natočení přímo použít pro horizontální pozicování hráče.
Registry POKEY použité pro práci s paddle
Pro čtení hodnoty ovladačů paddle (je jich teoreticky celkem osm, prakticky ovšem spíše maximálně čtyři) se používá deset registrů čipu POKEY. Devět z těchto registrů je použito pro čtení, desátý registr naopak slouží pro zápis:
| Jméno | Stínový registr | Čtení/zápis | Adresa | Stručný popis |
|---|---|---|---|---|
| POT0 | PADDL0 | čtení | $d200 | měření natočení prvního paddle |
| POT1 | PADDL1 | čtení | $d201 | měření natočení druhého paddle |
| POT2 | PADDL2 | čtení | $d202 | měření natočení třetího paddle |
| POT3 | PADDL3 | čtení | $d203 | měření natočení čtvrtého paddle |
| POT4 | PADDL4 | čtení | $d204 | měření natočení pátého paddle |
| POT5 | PADDL5 | čtení | $d205 | měření natočení šestého paddle |
| POT6 | PADDL6 | čtení | $d206 | měření natočení sedmého paddle |
| POT7 | PADDL7 | čtení | $d207 | měření natočení osmého paddle |
| ALLPOT | čtení | $d208 | stav měření všech osmi paddle (0 znamená validní informaci, 1 prováděné měření) | |
| POTGO | zápis | $d20b | inicializace měření natočení paddle (vybití kondenzátorů) |
Jak čtení probíhá? Typicky se na konci VBI rutiny provede zápis libovolné hodnoty do registru POTGO, čímž se zapne měření odporu (resp. času nabíjení kondenzátoru připojeného do série s potenciometrem):
LDA #cokoli STA POTGO
Teoreticky je poté možné čekat na nastavení nulových bitů v registru ALLPOT. Jakmile je příslušný bit nulový, je hodnota uložená v odpovídajícím registru POTx korektní (kondenzátor se nabil). Ovšem prakticky víme, že hodnoty POTx budou korektní až v následujícím snímku, takže je opět můžeme vyčíst v subrutině VBI:
LDA POT0 ... ... ...
Čtení natočení paddle operačním systémem
Ve skutečnosti je veškerá obsluha paddle řešena již na úrovni operačního systému, který automaticky nastavuje stínové registry PADDLx. Původní ROM počítačů Atari obsahuje mj. i následující sekvenci instrukcí, která je součástí subrutiny VBI:
; Source: original Atari ROMs 1508 E883 A2 03 LDX #3 1509 EBBS BD 10 D0 STRL: LDA TRIGO,X ;MOVE JOYSTICK TRIGGERS 1510 EBBS 90 84 02 STA STRIGO,X 1511 E8BB BD 00 D2 LDA POTO,X ;MOVE POT VALUES 1512 EBBE 9D 70 02 STA PADDLO,X 1513 E8C1 BD 04 D2 LDA POT4,X 1514 EBC4 9D 74 02 STA PADDL4,X 1515 E8C7 CA DEX 1516 E8C8 10 EB BPL STRL 1517 E8CA 8D OB D2 STA POTGO ;START POTS FOR NEXT TIME 1518 ; 1519 E8CD A2 06 LDX #6 1520 E8CF A0 03 LDY #3 1521 EBD1 89 78 02 PTRLP: LDA STICKO,Y ;TRANSFER BITS FROM JOYSTICKS 1522 E8D4 4A LSR A ;TO PADDLE TRIGGERS 1523 E8D5 4A LSR A 1524 E8D6 4A LSR A 1525 EBD7 90 7D 02 STA PTRIG1,X 1526 E8DA A9 00 LDA #0 1527 E8DC 2A ROL A 1528 E8DD 9D 7C 02 STA PTRIGO,X 1529 E8E0 CA DEX 1530 E8E1 CA DEX 1531 E8E2 88 DEY 1532 E8E3 10 EC BPL PTRLP
Čitelnější kód nalezneme například ve známé Altiře:
; Zdroj:
; https://github.com/ilmenit/AltirraSDL/blob/main/src/Kernel/source/Shared/vbi.s
pot_loop:
lda pot0,x
sta paddl0,x
lda #0
sta ptrig0,x
dex
bpl pot_loop
ldx #3
trig_loop:
lda trig0,x
sta strig0,x
dex
bpl trig_loop
lda porta
ldx #0
ldy #0
jsr do_stick_ptrigs
lda portb
ldx #4
ldy #2
jsr do_stick_ptrigs
.endif
;restart pots (required for SysInfo)
sta potgo
Využití paddle ve hrách a kreslicích programech
Ovladače typu paddle byly poprvé vydány pro herní konzoli Atari 2600 (VCS) a byly využity v několika známých hrách. Mezi ně patří Breakout (tedy předchůdce Arkanoidu), Warlords, Canyon Bomber či Kaboom! Na osmibitových domácích mikropočítačích Atari (400, 800, řada XL, řada XE atd.) se sice používal naprosto stejný ovladač, ovšem podpora pro paddle ve hrách byla relativně slabší. Pravděpodobně nejznámější hrou jsou Warlords (stejný princip jako na Atari 2600 či na herním automatu) a paddle podporuje (vedle joysticku) například i Arkanoid či Planetary Defense.
Obrázek 11: Hra Warlords je ovládaná pomocí paddle.
Obrázek 12: Úvodní obrazovka Arkanoidu nabízí ovládání pomocí paddle.
Obrázek 13: První úroveň hry Arkanoid.
Obrázek 14: Planetary Defense se dá ovládat i dvojicí paddle, což může být v tomto případě lepší, než s využitím joysticku (což je taktéž možné).
Zajímavý je tablet (v původním smyslu tohoto slova) nazvaný Koala Pad, který se připojoval k analogovému vstupu, konkrétně byl připojen jako dvojice ovladačů typu paddle. Koala Pad byl podporován kreslicím programem Koala Painter, jehož uživatelské rozhraní vypadá následovně (je použit grafický režim 160×192 pixelů pro obrázek, uživatelské rozhraní je však založeno na textovém režimu s pěti barvami):
Obrázek 15: Menu kreslicího programu Koala Painter využívá textový režim s pěti barvami.
Pátý demonstrační příklad: příprava na ovládání hráče
Vraťme se nyní na chvíli k článkům o PMG, tedy o spritech, které se u osmibitových mikropočítačů Atari nazývají souslovím Player-Missile Graphics. V těchto článcích jsme si kromě dalších věcí ukázali, jak snadné je měnit horizontální pozici hráče zápisem osmibitové hodnoty do jednoho z řídicích registrů HPOSx integrovaného obvodu GTIA. Takto jsme například implementovali posun hráče s využitím joysticku:
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos ; původní pozice prvního hráče
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
Tato subrutina je volána 50× nebo 60× za sekundu a navíc využívá paměťovou buňku s předchozí pozicí hráče:
; horizontální pozice hráče x_pos: .byte 80
Výsledek na obrazovce Atari může vypadat následovně:
Obrázek 16: Posun hráče s využitím joysticku.
Následuje úplný zdrojový kód, který bude v rámci navazující kapitoly modifikován tak, aby bylo možné hráče posunovat nikoli joystickem, ale pomocí ovladače paddle:
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 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 #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+50, x ; uložit byte - první hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
; nastavit vektor pro odloženou VBI
lda #>horizontal_movement
sta VVBLKD+1
lda #<horizontal_movement
sta VVBLKD
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos ; původní pozice prvního hráče
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
; horizontální pozice hráče
x_pos: .byte 80
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
Šestý demonstrační příklad: přímé ovládání hráče s využitím paddle
Přímé ovládání hráče, resp. přesněji řečeno horizontální polohy hráče s využitím ovladače paddle, je realizováno v dnešním šestém a současně i posledním demonstračním příkladu. Změna pozice hráče je realizována ve VBI a je až triviálně jednoduchá, protože většinu práce za nás udělá operační systém:
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
lda PADDL0 ; čtení hodnoty paddle
sta HPOSP0 ; změna pozice prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
Výsledek:
Obrázek 17: Posun hráče s využitím paddle.
Úplný zdrojový kód dnešního šestého demonstračního příkladu vypadá následovně:
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 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 #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+50, x ; uložit byte - první hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
; nastavit vektor pro odloženou VBI
lda #>horizontal_movement
sta VVBLKD+1
lda #<horizontal_movement
sta VVBLKD
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
lda PADDL0 ; čtení hodnoty paddle
sta HPOSP0 ; změna pozice prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
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
Obsah navazujícího článku
V navazujícím článku budeme pokračovat v popisu možností nabízených čipem POKEY. Nejdříve si ukážeme způsob čtení kódu stlačených kláves, s čímž nepřímo souvisí i problematika hardwarových přerušení (IRQ), které POKEY taktéž dokáže ovládat (maskovat atd.). A poté si řekneme, jakým způsobem byl integrovaný čip POKEY využit pro řízení sériového portu. Ten byl velmi důležitý, protože sloužil k připojování dalších periferií k osmibitovým mikropočítačům Atari. Ostatně sériový port Atari nazývaný SIO se stal předchůdcem USB, tedy sériové sběrnice používané dodnes.
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 \
sound_1.xex sound_2.xex \
sound_3.xex sound_4.xex \
sound_5.xex sound_6.xex \
sound_7.xex sound_8.xex \
sound_9.xex sound_A.xex \
sound_B.xex sound_C.xex \
sound_D.xex sound_E.xex \
sound_F.xex \
rnd_1.xex rnd_2.xex \
rnd_3.xex rnd_4.xex \
paddle_1.xex paddle_2.xex
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
sound: \
sound_1.xex \
sound_2.xex \
sound_3.xex \
sound_4.xex \
sound_5.xex \
sound_6.xex \
sound_7.xex \
sound_8.xex \
sound_9.xex \
sound_A.xex \
sound_B.xex \
sound_C.xex \
sound_D.xex \
sound_E.xex \
sound_F.xex
rnd: \
rnd_1.xex \
rnd_2.xex \
rnd_3.xex \
rnd_4.xex
paddle: \
paddle_1.xex \
paddle_2.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 - Doug Neubauer
https://en.wikipedia.org/wiki/Doug_Neubauer - POKEY
https://en.wikipedia.org/wiki/POKEY - Inside the Atari 800XL
https://www.goto10retro.com/p/800×l-inside - Grayscale Project – „Jam Session“ – Chiptune Visualization / Atari SAP
https://www.youtube.com/watch?v=Qx-AHgvwrHo&list=PL92E73FD91764173B - POKEY MUSIC ( ATARI XL / XE ) =+ MUSIC FROM POLAND += DEMO
https://www.youtube.com/watch?v=5PswfMjMop4&list=RD5PswfMjMop4&start_radio=1 - Atari.org
http://www.atari.org/ - Atari POKEY
http://www.absoluteastronomy.com/topics/Atari_POKEY - Chiptune
http://www.absoluteastronomy.com/topics/Chiptune - ASAP – Another Slight Atari Player
http://asap.sourceforge.net/ - Atari SAP music archive
http://asma.atari.org/ - RASTER Music Tracker (RMT)
http://raster.infos.cz/atari/rmt/rmt.htm - POKEY explorer
https://github.com/ivop/pokey-explorer - POKEY C012294 Documentation
https://7800.8bitdev.org/index.php/POKEY_C012294_Documentation - Pokey Registers
https://user.xmission.com/~trevin/atari/pokey_regs.html - POKEY CO12294
http://krap.pl/mirrorz/atari/homepage.ntlworld.com/kryten_droid/Atari/800XL/atari_hw/pokey.htm - De Re Atari: SOUND
https://www.atariarchives.org/dere/chapt07.php#H7_3_5 - Paddle (game controller)
https://en.wikipedia.org/wiki/Paddle_(game_controller) - Interfacing Your Atari
https://www.atariarchives.org/creativeatari/Interfacing_Your_Atari.php - Atari SIO
https://en.wikipedia.org/wiki/Atari_SIO
