Čip POKEY v osmibitových Atari (3. část)
Co se dozvíte v článku
- Čip POKEY v osmibitových Atari (3. část)
- Horní propust pro první zvukový kanál tvořená kanálem číslo 3
- Výsledky získané pro různé frekvence kanálů číslo 1 a 3
- Výsledky získané po ztlumení kanálu číslo 3
- První demonstrační příklad: nastavení horní propusti pro první zvukový kanál
- Řízení frekvence zvukového kanálu i frekvence jeho horní propusti
- Druhý demonstrační příklad: syntetizér využívající horní propust řízený joystickem
- Kombinace horní propusti a zvukového kanálu se vstupem z poly čítače
- Třetí demonstrační příklad: syntetizér s konfigurovatelným vstupem z poly čítačů
- Neobvyklá kombinace: horní propust založená na poly čítači
- Čtvrtý demonstrační příklad: horní propust založená na poly čítači
- Přímé ovládání hlasitosti zvukového kanálu
- Programová smyčka generující tón s konfigurovatelným průběhem
- Pátý demonstrační příklad: přímé ovládání výstupu zvukového kanálu mikroprocesorem
- Proč je zvuk generovaný předchozím příkladem poškozen?
- Zákaz všech přerušení, včetně přerušení od ANTICu
- Šestý demonstrační příklad: korektní přehrání tónu při zákazu všech přerušení
- Příloha: Makefile pro překlad všech demonstračních příkladů
- Repositář s demonstračními příklady
- Odkazy na Internetu
Na předchozí dvojici článků o čipu POKEY [1] [2] dnes navážeme a dokončíme tak téma tvorby zvuků na osmibitových mikropočítačích Atari. Nejprve si ukážeme způsob odlišného zapojení zvukových kanálů, který se označuje termínem horní propust (high pass), i když se interně jedná o pouhou kombinaci dvou binárních hradel.
Poté si řekneme, jak lze tvořit signály se složitějším průběhem, než jsou více či méně modifikované obdélníkové průběhy. Při té příležitosti si ukážeme způsob zákazu všech zdrojů přerušení, takže bude mít CPU prosto pro generování zvukového signálu v reálném čase.
Již naposledy se podívejme na bity řídicího registru AUDCTL. Význam většiny bitů jsme si již vysvětlili, ovšem zbývají nám bity 1 a 2, kterými lze vždy dvojici kanálů spojit tak, aby jeden z kanálů tvořil takzvanou horní propust:
| Bit | Stručný popis |
|---|---|
| 7 | záměna 17bitového poly čítače za 9bitový čítač |
| 6 | vstupní hodinový signál pro kanál 1 bude mít frekvenci CPU (1,77 nebo 1,79 MHz) |
| 5 | vstupní hodinový signál pro kanál 3 bude mít frekvenci CPU (1,77 nebo 1,79 MHz) |
| 4 | vstupem kanálu číslo 2 bude kanál číslo 1 (spojení kanálů do 16bitového děliče) |
| 3 | vstupem kanálu číslo 4 bude kanál číslo 3 (spojení kanálů do 16bitového děliče) |
| 2 | konfigurace primitivní horní propusti pro kanál 1 tvořené kanálem 3 |
| 1 | konfigurace primitivní horní propusti pro kanál 2 tvořené kanálem 4 |
| 0 | vstupní hodinový signál bude mít frekvenci cca 15kHz a ne 64kHz |
Horní propust pro první zvukový kanál tvořená kanálem číslo 3
Čip POKEY dokáže spojit dvojici kanálů 1+2 a/nebo 3+4 do jediného šestnáctibitového kanálu. Ovšem kanály lze zkombinovat i jiným způsobem. Konkrétně je možné propojit kanály 1+3 a/nebo 2+4 (pozor – to jsou odlišné dvojice) tak, že vyšší kanál bude zdrojem hodinových pulsů přiváděných na bránu (gate). A signál z kanálu 1 nebo 2 je přiveden na vstup této brány. Za ní je umístěno hradlo XOR kombinující přímý signál z kanálů 1 nebo 2 se signálem z brány (tedy pokud je brána otevřena, je výsledkem nula). Toto zapojení je z nějakého důvodu označováno termínem horní propust, i když se od klasické analogové horní propusti (RC článek) snad ve všech ohledech liší :-) Nicméně se jedná o techniku umožňující tvorbu zajímavých zvuků.
Prakticky je konfigurace horní propusti triviální, protože se jedná o nastavení jediného bitu v řídicím registru AUDCTL. Pokud budeme chtít nastavit horní propust pro kanál číslo 1, bude tato horní propust realizována výstupem kanálu číslo 3 (před aplikací hlasitosti – jedná se totiž o čistě binární operaci). Nastavení je snadné:
lda #PURE_TONE_MASK+10 ; hlasitost (0-15)
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 2 čipu POKEY
lda #71 ; dělič základní frekvence (komorní A: 440 Hz)
sta AUDF1 ; zápis do registru děliče frekvence pro zvukový kanál číslo 1 čipu POKEY
lda #31 ; dělič základní frekvence (cca 1kHz)
sta AUDF3 ; zápis do registru děliče frekvence pro zvukový kanál číslo 2 čipu POKEY
lda #HIGH_PASS_1_3_MASK ; nastavení horní propusti
sta AUDCTL ; zápis do řídicího registru čipu POKEY
Ve výše uvedeném kódu má propust frekvenci přibližně 1 kHz a tvoří bránu pro frekvenci 440 Hz. Ovšem nic nám nebrání v modifikaci děličů a tím pádem k definici propusti na 440 Hz pro signál o frekvenci 1 kHz (což v analogovém světě vede k naprosto odlišným výsledkům):
lda #PURE_TONE_MASK+10 ; hlasitost (0-15)
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
; otočení frekvencí
lda #31 ; dělič základní frekvence (cca 1kHz)
sta AUDF1 ; zápis do registru děliče frekvence pro zvukový kanál číslo 1 čipu POKEY
lda #71 ; dělič základní frekvence (komorní A: 440 Hz)
sta AUDF3 ; zápis do registru děliče frekvence pro zvukový kanál číslo 3 čipu POKEY
lda #HIGH_PASS_1_3_MASK ; nastavení horní propusti
sta AUDCTL ; zápis do řídicího registru čipu POKEY
lda #PURE_TONE_MASK+10 ; hlasitost (0-15)
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY
lda #0 ; hlasitost (0-15)
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
Výsledky získané pro různé frekvence kanálů číslo 1 a 3
Vyzkoušejme si nyní, jak bude vypadat signál posílaný (samozřejmě po jeho zesílení) na reproduktor v případě, že zkombinujeme následující dva průběhy (kanál 1 a 3) nastavené na generování čistého tónu o frekvencích 440 Hz (komorní A) a 1 kHz:
Obrázek 1: Průběh signálu pro čistý tón o frekvenci 440 Hz.
Obrázek 2: Průběh signálu pro čistý tón o frekvenci 1 kHz.
Po nastavení kanálu číslo 3 ve funkci horní propusti pro kanál číslo 1 získáme tyto (divné) průběhy, které vůbec neodpovídají představě funkce reálné horní propusti:
Obrázek 3: Signál o frekvenci 1 kHz je použit jako horní propust pro signál o frekvenci 440 Hz.
Obrázek 4: Signál o frekvenci 440 Hz je použit jako horní propust pro signál o frekvenci 1 kHz.
Výsledky získané po ztlumení kanálu číslo 3
Jeden z problémů předchozích dvou průběhů je fakt, že jsme sice kanál číslo 3 použili ve funkci horní propusti (resp. jejího řízení), ovšem i výstup z tohoto kanálu má nenulovou hlasitost a proto se „propisuje“ na výstup. To ovšem můžeme snadno opravit tak, že hlasitost tohoto kanálu bude nastavena na nulu. Ovšem obdélníkový průběh získaný děličkou tohoto kanálu bude stále možné využít pro implementaci horní propusti (hlasitost se totiž aplikuje až na konci celého řetězce zpracování zvuku):
lda #PURE_TONE_MASK+10 ; hlasitost (0-15)
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
Výsledky sice stále neodpovídají představě horní propusti, ale minimálně se jedná o jediný signál, který není sčítaný s dalším signálem:
Obrázek 5: Signál o frekvenci 1 kHz je použit jako horní propust pro signál o frekvenci 440 Hz.
Obrázek 6: Signál o frekvenci 440 Hz je použit jako horní propust pro signál o frekvenci 1 kHz.
První demonstrační příklad: nastavení horní propusti pro první zvukový kanál
Úplný zdrojový kód dnešního prvního demonstračního příkladu, ve kterém je nastavena horní propust pro první kanál a následně se postupně mění frekvence kanálu 1 a 3, vypadá následovně:
; ---------------------------------------------------------------------
; Generování zvuků čipem POKEY.
;
; Nastavení horní propusti pro zvukový kanál číslo 1
; Postupná změna frekvence kanálů 1 a 3.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PURE_TONE_MASK = %10100000
HIGH_PASS_1_3_MASK = %00000100
.proc main
jsr get_key ; čekání na stisk klávesy
lda #PURE_TONE_MASK+10 ; hlasitost (0-15)
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
lda #71 ; dělič základní frekvence (komorní A: 440 Hz)
sta AUDF1 ; zápis do registru děliče frekvence pro zvukový kanál číslo 1 čipu POKEY
lda #31 ; dělič základní frekvence (cca 1kHz)
sta AUDF3 ; zápis do registru děliče frekvence pro zvukový kanál číslo 3 čipu POKEY
lda #00 ; základní tón odvozený od 64kHz
sta AUDCTL ; zápis do řídicího registru čipu POKEY
jsr get_key ; čekání na stisk klávesy
lda #HIGH_PASS_1_3_MASK ; nastavení horní propusti
sta AUDCTL ; zápis do řídicího registru čipu POKEY
jsr get_key ; čekání na stisk klávesy
; otočení frekvencí
lda #31 ; dělič základní frekvence (cca 1kHz)
sta AUDF1 ; zápis do registru děliče frekvence pro zvukový kanál číslo 1 čipu POKEY
lda #71 ; dělič základní frekvence (komorní A: 440 Hz)
sta AUDF3 ; zápis do registru děliče frekvence pro zvukový kanál číslo 3 čipu POKEY
loop: 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
Řízení frekvence zvukového kanálu i frekvence jeho horní propusti
Již minule jsme si ukázali způsob řízení frekvence zvukového kanálu (či dokonce dvojice zvukových kanálů) s využitím joysticku. Naprosto stejnou techniku pochopitelně můžeme použít i v případě, kdy jsou dva zvukové kanály zkombinovány takovým způsobem, že jeden z nich tvoří horní propust druhému kanálu. Tím lze v doslova několika desítkách bajtů (konkrétně se jedná o 85 bajtů – a to jsem se nesnažil o optimalizace) realizovat zvukový syntetizér. Jeho základní nastavení je jednoduché:
lda #PURE_TONE_MASK+10 ; hlasitost (0-15) sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY lda #HIGH_PASS_1_3_MASK ; nastavení horní propusti sta AUDCTL ; zápis do řídicího registru čipu POKEY
Řízení dalších registrů čipu POKEY je realizováno v subrutině VBI, která je volána pravidelně 50× nebo 60× za sekundu:
lda #>horizontal_vertical_movement sta VVBLKD+1 lda #<horizontal_vertical_movement sta VVBLKD
Hodnoty děliček zvukových kanálů 1 a 3 jsou uloženy v paměti (bylo by lepší použít nultou stránku paměti):
; děliče frekvence divider1: .byte 10 divider2: .byte 10
Subrutina VBI čte náklon joysticku a nastavuje podle něj děličky kanálů 1 a 3. Díky tomu, že jsou tyto kanály spojeny tak, aby se realizovala horní propust, je možné si otestovat všech 65000 možných kombinací (nulové hodnoty děliček nepočítám):
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_vertical_movement
ldx divider1
ldy divider2
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; změna děliče
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; změna děliče
not_right:
cmp #14 ; je nakloněn nahoru?
bne not_up
dey ; změna Y-ové souřadnice hráče
not_up:
cmp #13 ; je nakloněn dolů?
bne not_down
iny ; změna Y-ové souřadnice hráče
not_down:
stx AUDF1 ; zápis do registru děliče frekvence pro zvukový kanál číslo 1 čipu POKEY
sty AUDF3 ; zápis do registru děliče frekvence pro zvukový kanál číslo 3 čipu POKEY
stx divider1 ; zapamatovat pro další volání VBI
sty divider2
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
Obrázek 7: Realizace horní propusti pro signály s různými frekvencemi.
Obrázek 8: Realizace horní propusti pro signály s různými frekvencemi.
Obrázek 9: Realizace horní propusti pro signály s různými frekvencemi.
Druhý demonstrační příklad: syntetizér využívající horní propust řízený joystickem
Úplný zdrojový kód dnešního druhého demonstračního příkladu vypadá následovně:
; ---------------------------------------------------------------------
; Generování zvuků čipem POKEY.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PURE_TONE_MASK = %10100000
HIGH_PASS_1_3_MASK = %00000100
; Bit Stručný popis
; 7 záměna 17bitového poly čítače za 9bitový čítač
; 6 vstupní hodinový signál pro kanál 1 bude mít frekvenci CPU (1,77 nebo 1,79 MHz)
; 5 vstupní hodinový signál pro kanál 3 bude mít frekvenci CPU (1,77 nebo 1,79 MHz)
; 4 vstupem kanálu číslo 2 bude kanál číslo 1 (spojení kanálů do 16bitového děliče)
; 3 vstupem kanálu číslo 4 bude kanál číslo 3 (spojení kanálů do 16bitového děliče)
; 2 konfigurace primitivní horní propusti pro kanál 1 tvořené kanálem 3
; 1 konfigurace primitivní horní propusti pro kanál 2 tvořené kanálem 4
; 0 vstupní hodinový signál bude mít frekvenci cca 15kHz a ne 64kHz
.proc main
lda #PURE_TONE_MASK+10 ; hlasitost (0-15)
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
lda #HIGH_PASS_1_3_MASK ; nastavení horní propusti
sta AUDCTL ; zápis do řídicího registru čipu POKEY
; nastavit vektor pro odloženou VBI
lda #>horizontal_vertical_movement
sta VVBLKD+1
lda #<horizontal_vertical_movement
sta VVBLKD
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_vertical_movement
ldx divider1
ldy divider2
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; změna děliče
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; změna děliče
not_right:
cmp #14 ; je nakloněn nahoru?
bne not_up
dey ; změna Y-ové souřadnice hráče
not_up:
cmp #13 ; je nakloněn dolů?
bne not_down
iny ; změna Y-ové souřadnice hráče
not_down:
stx AUDF1 ; zápis do registru děliče frekvence pro zvukový kanál číslo 1 čipu POKEY
sty AUDF3 ; zápis do registru děliče frekvence pro zvukový kanál číslo 3 čipu POKEY
stx divider1
sty divider2
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
; děliče frekvence
divider1: .byte 10
divider2: .byte 10
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
Kombinace horní propusti a zvukového kanálu se vstupem z poly čítače
Primární důvod, proč vlastně bylo zapojení horní propusti v čipu POKEY implementováno, s velkou pravděpodobností spočívá v možnosti kombinace vstupu šumu (získaného výstupem z poly čítače) s periodickým signálem z běžného zvukového kanálu. Tím se doplňuje možnost „samplování“ výstupu poly čítače periodickým signálem. Ukažme si to na příkladu propojení zvukových kanálů 1 a 3. Příklad upravíme takovým způsobem, že se po stisku libovolné klávesy změní způsob zapojení prvního zvukového kanálu – namísto pravidelného obdélníkového tónu se použije různá kombinace poly čítačů. A joystickem se mění hodnota děliček obou zvukových kanálů, tedy jak prvního, tak i třetího. Hodnota uložená v bajtu noise1 je ořezána na rozsah 0..7 a posunuta do nejvyšších tří bitů registru AUDC1 (spodní čtyři bity obsahují hlasitost, pátý bit si ukážeme v dalším textu):
.proc main
lda #PURE_TONE_MASK+10 ; hlasitost (0-15)
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
lda #HIGH_PASS_1_3_MASK ; nastavení horní propusti
sta AUDCTL ; zápis do řídicího registru čipu POKEY
; nastavit vektor pro odloženou VBI
lda #>horizontal_vertical_movement
sta VVBLKD+1
lda #<horizontal_vertical_movement
sta VVBLKD
loop:
lda noise1
and #%111 ; jen spodní tři bity
asl ; posunout do nejvyšších tří pozic
asl
asl
asl
asl
clc
adc #10 ; přidat hlasitost
sta AUDC1
inc noise1
jsr get_key ; čekání na stisk klávesy
jmp loop
.endproc
Vizualizované průběhy signálu vstupujícího do reproduktoru vypadají následovně:
Obrázek 10: Vstupním signálem do horní propusti je šum.
Obrázek 11: Vstupním signálem do horní propusti je šum.
Třetí demonstrační příklad: syntetizér s konfigurovatelným vstupem z poly čítačů
Úplný zdrojový kód dnešního třetího demonstračního příkladu vypadá následovně:
; ---------------------------------------------------------------------
; Generování zvuků čipem POKEY.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PURE_TONE_MASK = %10100000
POLY_4_BIT_MASK = %11000000
POLY_5_BIT_MASK = %01100000
POLY_17_BIT_MASK = %10000000
POLY_4_5_BIT_MASK = %01000000
POLY_17_5_BIT_MASK = %00000000
HIGH_PASS_1_3_MASK = %00000100
.proc main
jsr get_key ; čekání na stisk klávesy
lda #PURE_TONE_MASK+10 ; hlasitost (0-15)
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
lda #71 ; dělič základní frekvence (komorní A: 440 Hz)
sta AUDF1 ; zápis do registru děliče frekvence pro zvukový kanál číslo 1 čipu POKEY
lda #31 ; dělič základní frekvence (cca 1kHz)
sta AUDF3 ; zápis do registru děliče frekvence pro zvukový kanál číslo 3 čipu POKEY
lda #00 ; základní tón odvozený od 64kHz
sta AUDCTL ; zápis do řídicího registru čipu POKEY
jsr get_key ; čekání na stisk klávesy
lda #HIGH_PASS_1_3_MASK ; nastavení horní propusti
sta AUDCTL ; zápis do řídicího registru čipu POKEY
jsr get_key ; čekání na stisk klávesy
lda #POLY_4_BIT_MASK+10 ; hlasitost (0-15)
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
jsr get_key ; čekání na stisk klávesy
lda #POLY_5_BIT_MASK+10 ; hlasitost (0-15)
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
jsr get_key ; čekání na stisk klávesy
lda #POLY_17_BIT_MASK+10 ; hlasitost (0-15)
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
loop: 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
Neobvyklá kombinace: horní propust založená na poly čítači
V předchozím demonstračním příkladu jsme si ukázali „běžný“ způsob zapojení zvukových kanálů. Konkrétně první kanál obsahoval šum získaný z poly čítače nebo čítačů a třetí kanál frekvenci pro horní propust (záchytný registr+XOR). Ovšem nic nám nebrání v otočení nastavení zvukových kanálů. To znamená, že vstupem bude periodický signál se zvolenou frekvencí a horní propust bude řízena zvukovým kanálem s nakonfigurovaným zdrojem šumu:
lda #PURE_TONE_MASK+10 ; hlasitost (0-15) sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY lda #71 ; dělič základní frekvence (komorní A: 440 Hz) sta AUDF1 ; zápis do registru děliče frekvence pro zvukový kanál číslo 1 čipu POKEY lda #31 ; dělič základní frekvence (cca 1kHz) sta AUDF3 ; zápis do registru děliče frekvence pro zvukový kanál číslo 3 čipu POKEY lda #HIGH_PASS_1_3_MASK ; nastavení horní propusti sta AUDCTL ; zápis do řídicího registru čipu POKEY lda #POLY_17_BIT_MASK+10 ; hlasitost (0-15) sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
Výsledek je … zajímavý … a otevírá prostor pro další zkoušky:
Obrázek 12: Šum tvořící horní propust.
Čtvrtý demonstrační příklad: horní propust založená na poly čítači
Postup uvedený v předchozí kapitole byl použit v dnešním čtvrtém demonstračním příkladu, jehož úplný zdrojový kód vypadá následovně:
; ---------------------------------------------------------------------
; Generování zvuků čipem POKEY.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PURE_TONE_MASK = %10100000
POLY_4_BIT_MASK = %11000000
POLY_5_BIT_MASK = %01100000
POLY_17_BIT_MASK = %10000000
POLY_4_5_BIT_MASK = %01000000
POLY_17_5_BIT_MASK = %00000000
HIGH_PASS_1_3_MASK = %00000100
.proc main
jsr get_key ; čekání na stisk klávesy
lda #PURE_TONE_MASK+10 ; hlasitost (0-15)
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
lda #71 ; dělič základní frekvence (komorní A: 440 Hz)
sta AUDF1 ; zápis do registru děliče frekvence pro zvukový kanál číslo 1 čipu POKEY
lda #31 ; dělič základní frekvence (cca 1kHz)
sta AUDF3 ; zápis do registru děliče frekvence pro zvukový kanál číslo 3 čipu POKEY
lda #00 ; základní tón odvozený od 64kHz
sta AUDCTL ; zápis do řídicího registru čipu POKEY
jsr get_key ; čekání na stisk klávesy
lda #HIGH_PASS_1_3_MASK ; nastavení horní propusti
sta AUDCTL ; zápis do řídicího registru čipu POKEY
jsr get_key ; čekání na stisk klávesy
lda #POLY_4_BIT_MASK+10 ; hlasitost (0-15)
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
jsr get_key ; čekání na stisk klávesy
lda #POLY_5_BIT_MASK+10 ; hlasitost (0-15)
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
jsr get_key ; čekání na stisk klávesy
lda #POLY_17_BIT_MASK+10 ; hlasitost (0-15)
sta AUDC3 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 3 čipu POKEY
loop: 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
Přímé ovládání hlasitosti zvukového kanálu
V některých případech možnosti nepřímého ovládání reproduktoru s využitím děličů frekvence, poly čítačů a primitivní formy horní propusti nemusí být dostačující. Příkladem je syntéza řeči či snaha o dobré napodobení reálných hudebních nástrojů. V takových případech (a také pokud je k dispozici dostatečný výkon mikroprocesoru) je možné generování obdélníkového signálu zcela vypnout a řídit pouze amplitudu na výstupu každého zvukového kanálu. Tímto způsobem lze, jak jsme si již ostatně řekli minule, přehrávat i nasamplované zvuky, ovšem kvůli výše uvedeným vlastnostem čipu POKEY je možné použít pouze šestnáct úrovní hlasitosti, tj. jedná se o čtyřbitový sampling s dynamickým rozsahem pouze 24 dB (naproti tomu CD-Audio využívá šestnáctibitový sampling s dynamickým rozsahem 96 dB a zvuková karta Sound Blaster 1 používá osmibitový sampling s dynamickým rozsahem cca 48 dB, což zhruba odpovídá magnetofonovému záznamu). V případě potřeby je však možné digitalizovaný zvuk přehrávat současně na všech čtyřech zvukových kanálech, čímž se počet úrovní – a tím i dynamický rozsah – nepatrně zvyšuje (součet intenzit ovšem není lineární ale logaritmický).
Ukažme si, jakým způsobem se zvukový kanál nastaví, aby bylo možné výstup ze zvukového kanálu ovládat přímo. Nejprve pro jistotu nastavíme POKEY do výchozího stavu (tedy tak, v jaké podobě se nachází po resetu):
lda #00 ; základní tón odvozený od 64kHz + základní nastavení čtyř zvukových kanálů sta AUDCTL ; zápis do řídicího registru čipu POKEY
Obsah registru AUDFx bude ignorován, takže se o něj nemusíme starat. Potřebujeme pouze nastavit pátý bit registru AUDCx a do dolních čtyř bitů vložit hodnotu 0..15, která přímo odpovídá úrovni poslané přes D/A převodník na reproduktor. Příkladem, jak toho dosáhnout, je vytvoření tabulky (o maximálním počtu 256 samplů) s hodnotami 16 až 31. Všechny tyto hodnoty mají nastaven pátý bit:
; tabulka amplitud volumes: .byte 24, 25, 26, 27, 28, 30, 31 .byte 30, 29, 28, 27, 26, 25, 24 .byte 23, 22, 21, 20, 19, 18, 17 .byte 18, 19, 20, 21, 22, 23, 24
Tyto hodnoty můžeme nějakým způsobem načítat, například s využitím index registru X (obsahuje offset) a přímo posílat do řídicího registru AUDCx:
lda volumes, x ; načtení amplitudy sta AUDC1 ; přímý zápis amplitudy (včetně bitu číslo 5)
Programová smyčka generující tón s konfigurovatelným průběhem
Podívejme se nyní na to, jak by mohla vypadat programová smyčka, která bude postupně načítat vzorky z operační paměti a posílat je na reproduktor, resp. přesněji řečeno na D/A převodník. Základní podoba by mohla vypadat takto:
ldx #0 ; vynulovat počitadlo
loop:
lda volumes, x ; načtení amplitudy
sta AUDC1 ; přímý zápis amplitudy (včetně bitu číslo 5)
ldy tempo
l1:
dey ; krátká zpožďovací smyčka
bne l1
inx ; nový sampl
cpx nc ; jsme na konci dat?
bne loop ; zatím ne - pokračujeme v přehrávání
ldx #0 ; začneme od začátku
beq loop ; když už je příznak nastavený, proč ho nevyužít
V praxi může být výhodné mít uloženu dobu trvání jednotlivých samplů a program tedy bude nepatrně komplikovanější (získáno úpravou z Do Re Atari):
ldx #0 ; vynulovat počitadlo
loop:
lda delays, x ; načtení doby trvání
sta msc
l0:
lda volumes, x ; načtení amplitudy
sta AUDC1 ; přímý zápis amplitudy (včetně bitu číslo 5)
ldy tempo
l1:
dey ; krátká zpožďovací smyčka
bne l1
dec msc
bne l0 ; vnější zpožďovací smyčka
inx ; nový sampl
cpx nc ; jsme na konci dat?
bne loop ; zatím ne - pokračujeme v přehrávání
ldx #0 ; začneme od začátku
beq loop ; když už je příznak nastavený, proč ho nevyužít
Výše uvedená počítaná programová smyčka využívá několik datových struktur uložených v operační paměti. Samotný čítač vzorků (samplů) je uložen v index registru X a nabývá hodnot od nuly do hodnoty uložené na návěští nc:
; čítač samplů nc: .byte 28
Tabulka s amplitudami, přesněji řečeno s hodnotami, které se postupně zapisují do řídicího registru AUDC1, obsahuje hodnoty v rozsahu 16 až 31 (protože je nastavený pátý bit):
; tabulka amplitud volumes: .byte 24, 25, 26, 27, 28, 30, 31 .byte 30, 29, 28, 27, 26, 25, 24 .byte 23, 22, 21, 20, 19, 18, 17 .byte 18, 19, 20, 21, 22, 23, 24
Další tabulka obsahuje relativní dobu přehrávání (trvání) jednotlivých vzorků. Jednička znamená nejrychlejší přehrání, šestka (či vyšší hodnota) pomalejší přehrání:
; tabulka doby trvání amplitud delays: .byte 1, 1, 1, 2, 2, 2, 3 .byte 6, 3, 2, 2, 2, 1, 1 .byte 1, 1, 1, 2, 2, 2, 3 .byte 6, 3, 2, 2, 2, 1, 1
Celková rychlost přehrávání je ovlivněna další hodnotou, která se využívá ve vnitřní programové smyčce. Zvýšením hodnoty lze dosáhnout pomalejšího přehrávání (více než cca 10 kHz v případě našeho signálu):
; vyšší hodnota - nižší tón tempo: .byte 1
Poslední paměťové místo je pomocné a obsahuje čítač vnější programové smyčky:
; čítač vnější zpožďovací smyčky msc: .byte 0
Pátý demonstrační příklad: přímé ovládání výstupu zvukového kanálu mikroprocesorem
V dnešním pátém demonstračním příkladu je realizováno přímé ovládání výstupu zvoleného zvukového kanálu (zde konkrétně kanálu číslo 1) s využitím mikroprocesoru. Úplný zdrojový kód tohoto příkladu vypadá následovně:
; ---------------------------------------------------------------------
; Generování zvuků čipem POKEY.
;
; Přímé přehrávání samplů.
; Použit je jen zvukový kanál číslo 1.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
jsr get_key ; čekání na stisk klávesy
lda #00 ; základní tón odvozený od 64kHz + základní nastavení čtyř zvukových kanálů
sta AUDCTL ; zápis do řídicího registru čipu POKEY
ldx #0 ; vynulovat počitadlo
loop:
lda delays, x ; načtení doby trvání
sta msc
l0:
lda volumes, x ; načtení amplitudy
sta AUDC1 ; přímý zápis amplitudy (včetně bitu číslo 5)
ldy tempo
l1:
dey ; krátká zpožďovací smyčka
bne l1
dec msc
bne l0 ; vnější zpožďovací smyčka
inx ; nový sampl
cpx nc ; jsme na konci dat?
bne loop ; zatím ne - pokračujeme v přehrávání
ldx #0 ; začneme od začátku
beq loop ; když už je příznak nastavený, proč ho nevyužít
.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
; čítač samplů
nc:
.byte 28
; tabulka amplitud
volumes:
.byte 24, 25, 26, 27, 28, 30, 31
.byte 30, 29, 28, 27, 26, 25, 24
.byte 23, 22, 21, 20, 19, 18, 17
.byte 18, 19, 20, 21, 22, 23, 24
; tabulka doby trvání amplitud
delays:
.byte 1, 1, 1, 2, 2, 2, 3
.byte 6, 3, 2, 2, 2, 1, 1
.byte 1, 1, 1, 2, 2, 2, 3
.byte 6, 3, 2, 2, 2, 1, 1
; vyšší hodnota - nižší tón
tempo:
.byte 1
; čítač vnější zpožďovací smyčky
msc:
.byte 0
end:
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
Proč je zvuk generovaný předchozím příkladem poškozen?
Tón generovaný předchozím demonstračním příkladem ve skutečnosti zní „divně“, protože je v něm například jasně slyšitelná frekvence 50 Hz (nebo 60 Hz v případě NTSC). To je ostatně patrné i na grafickém znázornění průběhu generovaného signálu:
Obrázek 13: Průběh signálu generovaného pomocí CPU.
Z průběhu vyplývá, že trojúhelníky nemají stejnou délku a navíc je ve druhé třetině patrná prodleva – reproduktor si „držel“ nastavenou amplitudu (resp. výchylku), protože po tento okamžik program neměnil hodnotu v řídicím registru AUDC1 (byl pozastaven).
Důvodem jsou přerušení naší programové smyčky způsobené dalšími čipy. Největším „viníkem“ je ANTIC, který potřebuje přes DMA načítat obrazová data i display list a proto zdržuje CPU. A dalším „viníkem“ je obslužná subrutina VBI používaná operačním systémem (proto slyšíme oněch 50 či 60 Hz).
Zákaz všech přerušení, včetně přerušení od ANTICu
Aby byl generovaný zvuk bez kazů, budeme muset zakázat všechna přerušení. To provádí následující podprogram, který zakazuje sériové I/O (to jsme si ještě v tomto seriálu nepopsali), dále zakazuje obsluhu DLI i VBI a pro jistotu zcela zakáže zobrazování (a tedy požadavky na DMA od ANTICu):
; ---------------------------------------------------------------------
; přehrávací smyčka nesmí být rušena žádnými dalšími podprogramy
; ---------------------------------------------------------------------
.proc disable_all_interrupts
lda #3 ; povolení práce s klávesnicí, zákaz serial I/O
sta SKCTL ; zápis do řídicího registru čipu POKEY
lda #00
sta NMIEN ; zákaz DLI i VBI
sta DMACTL ; zákaz zobrazování
sta IRQEN ; zákaz nemaskovatelného přerušení
rts ; návrat z podprogramu
.endproc
Výsledkem je čistý signál s trojúhelníkovým průběhem (případné nesrovnalosti lze napravit změnou konstant v programu):
Obrázek 14: Průběh signálu generovaného pomocí CPU při zákazu všech přerušení.
Šestý demonstrační příklad: korektní přehrání tónu při zákazu všech přerušení
Následuje výpis zdrojového kódu šestého a dnes již posledního demonstračního příkladu, který dokáže přehrát tón se zvoleným průběhem bez toho, aby docházelo k nepříjemnému rušení:
; ---------------------------------------------------------------------
; Generování zvuků čipem POKEY.
;
; Přímé přehrávání samplů.
; Použit je jen zvukový kanál číslo 1.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
jsr get_key ; čekání na stisk klávesy
lda #00 ; základní tón odvozený od 64kHz + základní nastavení čtyř zvukových kanálů
sta AUDCTL ; zápis do řídicího registru čipu POKEY
jsr disable_all_interrupts
ldx #0 ; vynulovat počitadlo
loop:
lda delays, x ; načtení doby trvání
sta msc
l0:
lda volumes, x ; načtení amplitudy
sta AUDC1 ; přímý zápis amplitudy (včetně bitu číslo 5)
ldy tempo
l1:
dey ; krátká zpožďovací smyčka
bne l1
dec msc
bne l0 ; vnější zpožďovací smyčka
inx ; nový sampl
cpx nc ; jsme na konci dat?
bne loop ; zatím ne - pokračujeme v přehrávání
ldx #0 ; začneme od začátku
beq loop ; když už je příznak nastavený, proč ho nevyužít
.endproc
; ---------------------------------------------------------------------
; přehrávací smyčka nesmí být rušena žádnými dalšími podprogramy
; ---------------------------------------------------------------------
.proc disable_all_interrupts
lda #3 ; povolení práce s klávesnicí, zákaz serial I/O
sta SKCTL ; zápis do řídicího registru čipu POKEY
lda #00
sta NMIEN ; zákaz DLI i VBI
sta DMACTL ; zákaz zobrazování
sta IRQEN ; zákaz nemaskovatelného přerušení
rts ; návrat z podprogramu
.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
; čítač samplů
nc:
.byte 28
; tabulka amplitud
volumes:
.byte 24, 25, 26, 27, 28, 30, 31
.byte 30, 29, 28, 27, 26, 25, 24
.byte 23, 22, 21, 20, 19, 18, 17
.byte 18, 19, 20, 21, 22, 23, 24
; tabulka doby trvání amplitud
delays:
.byte 1, 1, 1, 2, 2, 2, 3
.byte 6, 3, 2, 2, 2, 1, 1
.byte 1, 1, 1, 2, 2, 2, 3
.byte 6, 3, 2, 2, 2, 1, 1
; vyšší hodnota - nižší tón
tempo:
.byte 1
; čítač vnější zpožďovací smyčky
msc:
.byte 0
end:
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
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
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_F.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
