Integrovaný obvod POKEY tvoří společně s čipy MOS 6502, ANTIC a GTIA základ osmibitových domácích mikropočítačů Atari. V předchozích částech tohoto seriálu jsme se primárně věnovali popisu grafických schopností Atari a tím pádem i vlastnostem integrovaných obvodů ANTIC a GTIA. Připomeňme si, že každý z těchto dvou obvodů byl určen pro jedinou operaci (ANTIC pro generování video signálu s barvami pixelů herního pole, GTIA pro zobrazení spritů nezávislých na herním poli). Určitou výjimkou bylo přidání čtyř bitových vstupů do čipu GTIA; ty sloužily pro čtení stavu tlačítek čtyř joysticků*.
Co se dozvíte v článku
- Obecný úvod: zvukové subsystémy počítačů
- Zpět na stromy: generování zvuků na domácích a první generaci osobních počítačů
- Integrovaný obvod POKEY v roli generátoru zvuků a hudby
- Ovládání základní frekvence zvukových kanálů
- Role poly čítačů (posuvných registrů se zpětnou vazbou)
- Malá odbočka: simulace činnosti poly čítače
- Řídicí registry určené pro ovládání zvuků a hudby
- Nastavení zapojení zvukových kanálů registrem AUDCTL
- Volba frekvence v režimu čtyř zvukových kanálů
- Nastavení hlasitosti a zkreslení
- Nastavení registrů čipu POKEY pro vygenerování čistého tónu
- Způsob výpočtu dělitele frekvence pro vygenerování tónu komorního A
- Minimální kód potřebný pro vygenerování tónu komorního A
- Úplný zdrojový kód prvního demonstračního příkladu
- Čistý tón ovlivněný šumem
- Úplný zdrojový kód druhého demonstračního příkladu
- Příloha: Makefile pro překlad všech demonstračních příkladů
- Repositář s demonstračními příklady
- Odkazy na Internetu
Integrovaný obvod POKEY je odlišný, protože realizuje hned několik činností: generování zvuků a hudby, č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í).
Jedná se tedy o čip, jehož vlastnosti je při tvorbě pro Atari nutné poměrně dobře znát. Právě sloučení mnoha různých funkcí do jednoho čtyřicetipinového integrovaného obvodu umožnilo snížit celkový počet obvodů ve všech osmibitových domácích mikropočítačích Atari, což samozřejmě – v mnohem větší míře než dnes – souvisí i s celkovou relativně nízkou výrobní cenou počítače či konzole, jeho nižší poruchovostí apod.
Dnes se budeme věnovat první operaci, kterou čip POKEY realizuje (a pravděpodobně se jedná o operaci, která POKEY nejvíce proslavila, ať již v dobrém nebo ve špatném). Jedná se o generování zvuků a hudby.
Obecný úvod: zvukové subsystémy počítačů
Prakticky každý moderní osobní počítač je vybaven zvukovým výstupem. Elektronický obvod, který se stará o vytváření zvuků a hudby je v současnosti buď přímo součástí čipové sady základní desky nebo je v počítači nainstalována zvuková karta určená pro interní či (dnes možná častěji) externí sběrnici. Cesta, která vedla od prvních programem či jednoduchým multivibrátorem řízených reproduktorů (ZX Spectrum, IBM PC XT) k dnešním výkonným zvukovým čipům umožňujícím tvorbu prostorových několikakanálových zvuků a hudby založené na wavetable syntéze, byla poměrně dlouhá a především zajímavá.
Tak jako v případě grafických subsystémů počítačů, i pro tvorbu zvuků a hudby zkoušeli návrháři čipů používat různé postupy a mnohdy tak vznikly zcela unikátní integrované obvody, které se staly legendami. Mezi tyto čipy patří například SID (Sound Interface Device) firmy Commodore, AY-3–8910/8912/8913 firmy General Instruments (později jej vyráběla i firma Yamaha pod označením YM2149), POKEY firmy Atari, PAULA firmy Commodore či čipy OPL2 (YM 3812) a OPL3 (YMF 262) vyráběné firmou Yamaha. Každý z těchto čipů je založen na odlišném způsobu vytváření hudby a jimi vytvářený zvuk je (snad s výjimkou čipu PAULA) velmi charakteristický a originální.
První úspěšné pokusy s vytvářením různých zvuků s pomocí počítačů sahají až do počátku šedesátých let minulého století. Již v roce 1962 zkoušel tehdy devětadvacetiletý John Chowning ze Stanfordu některé způsoby syntézy hudby – napodobování tónů skutečných hudebních nástrojů. Vzhledem k tomu, že tehdy dostupný hardware neměl dostatečnou výkonnost ani paměťovou kapacitu pro záznam zvuků skutečných hudebních nástrojů (to jsme blízko možnostem osmibitových mikropočítačů), spočívala většina návrhů v různě konstruovaných multivibrátorech a oscilátorech (jednalo se jak o analogové obvody, tak i o programem řízený digitálně-analogový převodník), na něž byly zapojeny integrační a derivační články, pásmové propusti či zádrže (těmi se modifikoval tvar původního obdélníkového či sinusového periodického signálu) a jejichž parametry se různě v čase měnily na základě dat posílaných počítačem. Digitalizace v takové kvalitě, jak ji známe a můžeme provádět i na tom nejlevnějším počítači dnes, byla před šedesáti lety spíše námětem pro sci-fi, protože paměťové (a vlastně i výpočetní) nároky na přehrání digitalizovaného zvuku byly na tehdejší dobu obrovské.
Mezitím se John Chowning stále pokoušel o napodobení zvuků hudebních nástrojů, protože základ – přehrání skladby zapsané sekvencí not – měl již vyřešen. Velmi významný byl však až objev, který učinil teprve v roce 1967. Chowning zkoušel vytvořit zvukový efekt známý pod pojmem vibrato. V podstatě se jedná o dvojici za sebe zapojených oscilátorů, přičemž první oscilátor (s frekvencí cca 20 Hz) ovlivňuje frekvenci druhého oscilátoru, který generuje výsledný signál. Pomocí takto zapojené dvojice oscilátorů se podařilo vygenerovat harmonický tón s celým spektrem vyšších frekvencí, přičemž změnou frekvence a amplitudy prvního oscilátoru se výška tónu neměnila, ale jeho zabarvení ano. Tak byl učiněn objev frekvenční syntézy (FM syntézy), na jehož principu se po více než dvacet let stavěly syntetizéry a posléze také čipy pro zvukové karty. Zajímavé je, že ve Stanfordu ani v celých USA tento objev (a především jeho důsledky pro tehdy již dlouho existující průmysl elektronických a elektromechanických hudebních nástrojů) nikoho nezajímal.
Z tohoto důvodu se John Chowning spojil s panem Ishimurou z firmy Yamaha, jenž při osobní návštěvě na Stanfordu po necelých deseti minutách princip, způsob využití FM i důsledky pro další vývoj v celém oboru pochopil a doporučil svým nadřízeným, aby se Yamaha v této oblasti více angažovala. Firma Yamaha si posléze tento způsob tvorby hudby nechala licencovat, zahájila dokonce vývoj a výrobu vlastních čipů pro syntézu hudby (původně měla tyto čipy pro Yamahu vyrábět firma NEC) a v roce 1983 prodala více než 200 000 kusů legendárního syntetizéru Yamaha DX-7, což bylo na tehdejší dobu (menší trh než dnes) považováno za velký úspěch následovaný po několika letech krachem firem, jenž vyráběly nyní již zastaralé elektromechanické syntetizéry (samotné obchodní značky těchto výrobců sice přežily, ale patří jiným vlastníkům). V roce 1994 dokonce přesahoval obrat trhu se zvukovými kartami s FM syntézou (přesněji syntézou založenou na fázové modulaci) jednu miliardu dolarů.
Zpět na stromy: generování zvuků na domácích a první generaci osobních počítačů
Některé osmibitové domácí počítače, například slavné ZX-Spectrum 48k, byly vybaveny pouze velmi jednoduchým zvukovým rozhraním – reproduktorem zapojeným přes zesilovač na logický (dvoustavový) výstup z mikroprocesoru či jiného podpůrného obvodu (například multivibrátoru). Na reproduktor byl posílán obdélníkový signál u nějž bylo možné měnit jeho střídu (poměr mezi logickým stavem jedna a nula), ovšem nikoli amplitudu, což samozřejmě znamenalo, že výsledné zvuky získaly charakteristické a snadno rozpoznatelné zabarvení. Někteří zkušení autoři byly kupodivu schopni i za těchto velmi skromných podmínek vyprodukovat poměrně zajímavou hudbu, dokonce bylo možné rychlou změnou přehrávaných frekvencí simulovat polyfonii či přehrávat samplované zvuky (ve své podstatě simulovali jednobitový digitálně-analogový převodník). Kromě tvůrců her se například jednalo o autory Buzzcocks či Freshies, kteří použili hudbu generovanou ZX-Spectrem při tvorbě svých alb. Stejný princip tvorby zvuků byl později použit i u osobního počítače IBM PC pod názvem PC-Speaker (též známého jako squeezer, beeper atd.).
V předchozí kapitole jsme si stručně popsali zrod FM syntézy. Čipy založené na principu FM syntézy se staly velmi populární především u hudebních syntetizérů; dodnes je ostatně můžeme slyšet v hudbě vzniklé především v osmdesátých letech minulého století. Z tohoto pohledu by se zdálo logické, aby se stejné čipy objevily i na domácích osmibitových počítačích (a herních automatech, které byly většinou založeny na stejných integrovaných obvodech), ovšem ve skutečnosti k tomu nedošlo. Jednou z příčin byl poměrně pozdní nástup čipů s FM syntézou a také jejich poměrně vysoká cena (a možná i problémy se získáním licence). Z tohoto důvodu se prakticky každý významnější výrobce domácích počítačů (které byly mj. určeny i pro hraní her) s větším či menším úspěchem pokusil buď o výrobu vlastního zvukového čipu či o nákup čipu (nezaložených ovšem na FM syntéze) od třetích výrobců. Tímto způsobem vznikly a později značně získaly na popularitě především tři principiálně odlišné čipy zmíněné v perexu tohoto článku: SID (Sound Interface Device) firmy Commodore, AY-3–8910/8912/8913 firmy General Instruments (později jej vyráběla i firma Yamaha pod označením YM2149) a POKEY firmy Atari.
Dalším krokem ve vývoji hudebních schopností počítačů bylo uvedení čipu PAULA použitého v počítačích Amiga. Tento čip již umožňoval současné přehrání čtyř osmibitových zvukových vzorků (samplů) se stereo výstupem. Důležité přitom bylo, že o přehrávání zvuků se nemusel starat samotný mikroprocesor, což se mnohdy významným způsobem projevilo na výkonu celého počítače (vlastníci počítačů IBM PC si podobný komfort mohli dovolit až s uvedením zvukové karty Gravis Ultrasound). Zvukové možnosti čipu PAULA se odrazily i na formátu hudebních souborů MOD (module). Bližší informace o tomto čipu si řekneme v navazujících částech tohoto seriálu.
Integrovaný obvod POKEY v roli generátoru zvuků a hudby
V úvodní kapitole jsme si řekli, že integrovaný obvod POKEY slouží mj. i k syntéze zvuků a hudby. Zvuk je možné generovat s využitím čtyř zvukových kanálů (lze tedy vytvářet až čtyřhlasou polyfonii), přičemž vždy dva sousední kanály lze v případě potřeby spojit do kanálu jednoho, u něhož je možné přesněji řídit frekvenci zvuku (v podstatě to znamená, že se dva osmibitové čítače/děliče spojí do jednoho čítače šestnáctibitového a změní se zdroj vstupních hodinových signálů – viz další text). Každý zvukový kanál produkuje obdélníkový signál s amplitudou, kterou je možné nastavit do jedné ze šestnácti úrovní (pro specifikaci amplitudy každého kanálu jsou vyhrazeny v ovládacích registrech pouze čtyři bity). Nulová logická hodnota obdélníkového signálu na vstupu vždy vede k nulovému napětí na výstupu zvukového kanálu; logická jednička je převedena na jednu ze šestnácti úrovní napětí. Závislost mezi zvolenou úrovní a napětím není přesně lineární; taktéž generovaný signál není (například po připojení na osciloskop) zcela obdélníkový, čehož se v pozdější době využívalo při tvorbě trojúhelníkových průběhů (ovšem nutno říci, že za značné pomoci mikroprocesoru a přesného časování).
POKEY má pro výstup zvuku rezervován jen jediný pin, z čehož plyne, že výsledkem je monofonní zvuk/hudba. Ovšem některé herní automaty (nikoli ovšem osmibitové počítače Atari) měly nainstalováno větší množství čipů POKEY a v takovém případě bylo pochopitelně možné dosáhnout i stereofonního výstupu.
Ovládání základní frekvence zvukových kanálů
Frekvence zvuku v každém zvukovém kanálu je řízena děličem 1:N, který je interně implementovaný jako čítač (ostatně právě na základě čítačů obvod POKEY detekuje stlačené klávesy, generuje pseudonáhodná čísla, provádí A/D převod z ovladačů paddle a komunikuje se SIO). V případě, že zvukové kanály nejsou spojeny do dvojic, je vstupní frekvence dělena hodnotou 2×+1, kde x je číslo v rozsahu 0–255 zapsané do řídicího registru obvodu POKEY. Vždy dva zvukové kanály je ovšem možné spojit, čímž se sice sníží počet současně přehrávaných zvuků, ale zvýší se přesnost čítače, protože vstupní frekvence může být dělena hodnotou 1 až 2×216+1=131073 (v tomto případě se však volí vyšší frekvence hodinových signálů na vstupu obvodu – jinak by po vydělení příliš velkou hodnotou byl na výstupu zvukových kanálů infrazvuk, který se v případě obdélníkového průběhu projevuje pouze „lupáním“ membrány reproduktoru při skokové změně amplitudy). Změnou obsahu řídicího registru obvodu POKEY je možné nakonfigurovat jednu ze tří kombinací zvukových kanálů:
- Čtyři zvukové kanály, frekvence každého z nich je vytvořena pomocí osmibitového děliče. Tato konfigurace čipu POKEY je použita při práci se zvukem z Atari BASICu (známý příkaz SOUND a,b,c,d). V tomto režimu lze generovat tóny v rozsahu zhruba čtyř oktáv (a navíc se značnou nepřesností – POKEY je „rozladěný“).
- Dva zvukové kanály, frekvence každého z nich je vytvořena pomocí šestnáctibitového děliče, který vznikl spojením dvou děličů osmibitových. Tónový rozsah z obou stran v tomto případě přesahuje limity lidského sluchu (infrazvuk, ultrazvuk).
- Jeden zvukový kanál řízený šestnáctibitovým děličem a dva kanály řízené děličem osmibitovým (tuto konfiguraci využívalo mnoho hudebníků, kteří přesněji řízený kanál použili pro basový hudební nástroj a další dva kanály pro perkusní nástroj – někdy samplovaný – a hlavní melodii hranou většinou na vyšších frekvencích).
Role poly čítačů (posuvných registrů se zpětnou vazbou)
Obdélníkový signál na výstupu každého zvukového kanálu může být buď periodický (a tedy pravidelný), nebo může být řízen takzvaným poly čítačem, který produkuje pseudonáhodné binární hodnoty. Obvod POKEY obsahuje celkem tři poly čítače (čtyřbitový, pětibitový a sedmnáctibitový, který lze zkrátit na devítibitový), jejichž výstup může řídit vybraný zvukový kanál (zvukové kanály se dělí o stejné poly čítače, ovšem každý zvukový kanál může mít nastavenou jinou frekvenci, tj. i výsledný zvuk bude odlišný). U sedmnáctibitového poly čítače (viz další odstavce) je vzdálenost mezi stejnými vzorky (perioda) tak dlouhá, že ho lze považovat za generátor náhodných impulsů, který vytváří bílý šum.
Princip řízení zvukového kanálu poly čítačem je ve skutečnosti velmi jednoduchý – řízení je prováděno obyčejným logickým hradlem a klopným obvodem typu D. Poly čítače sice mění hodnotu na svém výstupu velmi rychle (jsou řízeny přímo hodinovým signálem mikroprocesoru, tj. cca 1,79 MHz pro počítače pracující v normě NTSC a 1,77 MHz pro počítače s televizní normou PAL), ale maximální frekvence na výstupu zvukového kanálu je kvůli zapojenému logickému hradlu omezena frekvencí získanou pomocí děliče 1:N (resp. přesněji 1:(N+1)).
Samotný poly čítač, tj. generátor pseudonáhodného šumu, je tvořen posuvným registrem řízeným externím hodinovým signálem. S každým taktem hodin dojde k posunu obsahu registru (tj. čtyř, pěti, sedmnácti a ve speciálním případě devíti bitů) o jednu pozici. Na vstup posuvného registru je zpětnovazební smyčkou přivedena binární hodnota získaná pomocí hradla typu XOR připojeného svými vstupy na třetí a poslední bit posuvného registru – hodnota, na výstupu hradla je tedy zapsána do první pozice:
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+---> | 1| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| 0| -> Output
| +--+--+--+--+--+--+--+--+--+--+--+--+-++--+--+-++
| ______ 0 | |
| 0 / //---+ |
+-------------------------------( XOR (( 0 |
\_____\\------------+
Pokud vezmeme do úvahy logickou funkci, kterou hradlo typu XOR reprezentuje, dojdeme k závěru, že po inicializaci čipu POKEY může mít posuvný registr prakticky jakoukoli nenulovou hodnotu, protože i za této situace rychle dojde k jeho naplnění pseudonáhodnými daty, které se poté periodicky opakují s periodou, jejíž délka závisí na bitové délce samotného posuvného registru. Pokud má registr délku n bitů, je perioda rovna 2n-1 taktům, protože jeden zbývající stav – samé nulové bity – tvoří samostatný (nezajímavý) cyklus. Poslední bit posuvného registru představuje i jeho finální výstup, tj. sekvenci pseudonáhodných binárních hodnot, kterou po zpracování obvodem POKEY (zejména po nastavení amplitudy) slyšíme.
Obrázek 1: Přechody mezi stavy LFSR (animace).
Zatímco čtyřbitové a pětibitové poly čítače produkují poměrně rychle se opakující pseudonáhodné sekvence (lze s nimi napodobit například zvuk leteckých motorů), sedmnáctibitový poly čítač již má délku sekvence tak dlouhou, že s ním lze generovat náhodný zvuk. Tento čítač lze také překonfigurovat tak, že se jeho délka sníží na devět bitů (viz další text).
Obrázek 2: Zvukový subsystém čipu TIA (Atari 2600) se v mnoha ohledech podobá zvukovému subsystému čipu POKEY.
Malá odbočka: simulace činnosti poly čítače
Funkci polyčítače si můžeme odsimulovat pomocí jednoduchého céčkového programu, který po svém spuštění vypíše na obrazovku všech n stavů polyčítače o zadané délce (viz symbolická konstanta POLYCOUNTER_LENGTH). Pomocí konstant BIT_A_INDEX a BIT_B_INDEX lze určit pozice bitů, které vstupují do hradla XOR, jehož výstup tvoří zpětnovazebnou smyčku:
/* vypis stavu polycitace */
#include <stdio.h>
#include <stdlib.h>
#define POLYCOUNTER_LENGTH 5
#define BIT_A_INDEX 3
#define BIT_B_INDEX 5
/* vypis vsech bitu polycitace */
void printPoly( int polycounter )
{
int i;
/* pro vsechny bity posuvneho registru */
for ( i=0; i<POLYCOUNTER_LENGTH; i++ )
{
/* vypis binarni hodnoty jednoho bitu */
putchar('0'+ (polycounter & 1) );
/* posun na dalsi bit */
polycounter = polycounter >> 1;
}
putchar('\n');
}
int main(void)
{
/* pocatecni hodnota polycitace */
int polycounter = 1;
int i;
for ( i=0; i < (1<<POLYCOUNTER_LENGTH); i++ )
{
int bitA, bitB, inputBit;
/* vypis hodnoty pocitadla i hexadecimalni hodnoty polycitace */
/* hodnota polycitace musi byt maskovana na svou max. bitovou delku */
printf("%02d\t%02x\t", i+1, polycounter & ((1<<POLYCOUNTER_LENGTH)-1));
/* vypis vsech bitu polycitace */
printPoly(polycounter);
/* ziskani hodnoty prvniho bitu, ktery jde do zpetnovazebni smycky */
bitA = (polycounter >> (BIT_A_INDEX-1) ) & 1;
/* ziskani hodnoty druheho bitu, ktery jde do zpetnovazebni smycky */
bitB = (polycounter >> (BIT_B_INDEX-1) ) & 1;
/* simulace funkce hradla typu XOR */
inputBit = bitA ^ bitB;
/* posun o jeden bit doleva a nastaveni */
/* nove hodnoty prvniho bitu (LSB) */
polycounter = (polycounter << 1) | inputBit;
}
return 0;
}
Výstup z programu pro pětibitový polyčítač (všech 31 nenulových stavů čítače):
01 01 10000 02 02 01000 03 04 00100 04 09 10010 05 12 01001 06 05 10100 07 0b 11010 08 16 01101 09 0c 00110 10 19 10011 11 13 11001 12 07 11100 13 0f 11110 14 1f 11111 15 1e 01111 16 1c 00111 17 18 00011 18 11 10001 19 03 11000 20 06 01100 21 0d 10110 22 1b 11011 23 17 11101 24 0e 01110 25 1d 10111 26 1a 01011 27 15 10101 28 0a 01010 29 14 00101 30 08 00010 31 10 00001 32 01 10000
Druhý program vypisuje hodnoty posledního bitu polyčítače, který se v čipu POKEY používá pro generování pseudonáhodných zvuků. Aby bylo patrné, že se bitová sekvence na výstupu několikrát opakuje, je vypsáno několik period, každá na zvláštním řádku:
/* simulace funkce polycitace (polycounteru) */
#include <stdio.h>
#include <stdlib.h>
#define POLYCOUNTER_LENGTH 5
#define BIT_A_INDEX 3
#define BIT_B_INDEX 5
int main(void)
{
/* pocatecni hodnota polycitace */
int polycounter = 1;
int i,j;
/* projdeme pres nekolik period, aby bylo patrne, ze se hodnoty opakuji */
for ( j=0; j < 10; j++ )
{
/* vypis cele jedne periody polycitace */
for ( i=0; i < (1<<POLYCOUNTER_LENGTH)-1; i++ )
{
int bitA, bitB, inputBit, outputBit;
/* ziskani hodnoty prvniho bitu, ktery jde do zpetnovazebni smycky */
bitA = (polycounter >> (BIT_A_INDEX-1) ) & 1;
/* ziskani hodnoty druheho bitu, ktery jde do zpetnovazebni smycky */
bitB = (polycounter >> (BIT_B_INDEX-1) ) & 1;
/* nejvyssi bit polycitace tvori i jeho vystup */
outputBit = (polycounter >> (POLYCOUNTER_LENGTH-1) ) & 1;
/* vypis vystupni hodnoty */
putchar('0' + outputBit);
/* simulace funkce hradla typu XOR */
inputBit = bitA ^ bitB;
/* posun o jeden bit doleva a nastaveni */
/* nove hodnoty prvniho bitu (LSB) */
polycounter = (polycounter << 1) | inputBit;
}
putchar('\n');
}
return 0;
}
Výstup získaný po spuštění druhého programu:
0000100101100111110001101110101 0000100101100111110001101110101 0000100101100111110001101110101 0000100101100111110001101110101 0000100101100111110001101110101 0000100101100111110001101110101 0000100101100111110001101110101 0000100101100111110001101110101 0000100101100111110001101110101 0000100101100111110001101110101
Řídicí registry určené pro ovládání zvuků a hudby
Čip POKEY se ovládá přes 29 osmibitových řídicích a stavových registrů, ovšem pouze devět těchto registrů je určeno pro ovládání zvukových kanálů. Jedná se o registry pojmenované AUDF1 až AUDF4, AUDC1 až AUDC4 a konečně o registr AUDCTL (mimochodem: zvukové kanály se číslují od jedničky). Stručný popis těchto registrů je uveden v tabulce; jejich konkrétní význam (obsazení bitů atd.) si postupně vysvětlíme v navazujících kapitolách:
| Registr/registry | Stručný popis |
|---|---|
| AUDF1–4 | děliče frekvence (uložen dělitel-1) |
| AUDC1–4 | nastavení hlasitosti (4 bity), povolení přímého řízení hlasitosti, volba generátoru šumu |
| AUDCTL | globální nastavení generátoru zvuků (spojení kanálů, filtry, vstupní frekvence) |
Definice adres těchto registrů pro assembler:
POKEY = $D200 ;POKEY area 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
Nastavení zapojení zvukových kanálů registrem AUDCTL
Způsob vzájemné konfigurace zvukových kanálů i volba vstupní frekvence je nastavována bity v řídicím registru AUDCTL. Tímto registrem lze volit především:
- Frekvenci signálu přiváděného na vstup kanálů (frekvence CPU, 15kHz či 64kHz)
- Spojení dvou kanálů s osmibitovým děličem frekvence na jeden kanál s děličem 16bitovým (počet kanálu se tak snižuje)
- Konfigurace horní propusti (podrobnosti si ukážeme příště)
- Nejvyšším bitem lze 17bitový poly čítač (ten s „nekonečnou“ periodou) na čítač devítibitový
Konkrétně vypadá obsah registru AUDCTL následovně:
| 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 |
Za zmínku stojí vstupní frekvence, které se v literatuře běžně zapisují jako 15kHz a 64kHz. To jsou podezřele „pěkná“ čísla na to, aby byla realizovatelná na počítačích s jediným oscilátorem. Realita je pochopitelně odlišná. Vstupem je vždy frekvence CPU, což je 1,7734470 MHz pro verzi PAL a 1,7897725 MHz pro verzi NTSC. Většina dokumentace obsahuje hodnoty pro NTSC, takže půjdeme proti davu a uvedeme si, jak vypadá časování v normě PAL.
Obě frekvence označované jako „15kHz“ a „64kHz“ vzniknou dělením frekvence CPU takto:
f_15kHz = 1773,447 / 114 = 15,556 kHz f_64kHz = 1773,447 / 28 = 63,337 kHz
Volba frekvence v režimu čtyř zvukových kanálů
Registry AUDF1 až AUDF4 obsahují hodnotu dělitele frekvence. V případě, že jsou zvukové kanály nakonfigurovány do režimu osmibitového dělitele, je význam hodnot v těchto registrech následující:
| Bity | Hodnota N |
|---|---|
| 00000000 | 1 |
| 00000001 | 2 |
| 00000010 | 3 |
| … | … |
| 11111111 | 256 |
Pokud je nakonfigurováno spojení dvou kanálů (2+1 a/nebo 4+3) bude dělitel N vypočten spojením obsahu dvou registrů AUDF2 @ AUDF1 a/nebo AUDF4 @ AUDF3 (znak @ znamená spojení dvojice osmibitových hodnot do jedné hodnoty šestnáctibitové).
Nastavení hlasitosti a zkreslení
Poslední čtveřice řídicích registrů AUDC1 až AUDC4 obsahuje tři sekce se šířkou tří bitů, jednoho bitu a čtyř bitů:
| Bity | Stručný popis |
|---|---|
| 7,6,5 | volba zapojení kanálu (viz obrázek) |
| 4 | přímé řízení hlasitosti (bity 7,6,5 budou ignorovány) |
| 3..0 | hlasitost v rozsahu 0 až 15 |
Volba hlasitosti je zřejmá – čtyři bity mohou reprezentovat hodnotu 0 až 15 odpovídající hlasitosti (v přibližně logaritmickém měřítku). Nastavením bitu číslo 4 se zajistí, že se budou ignorovat děliče frekvence i generátory šumu (poly čítače). Výstup z příslušného kanálu bude přímo určen bity 3..0, což znamená, že lze realizovat sampling v reálném čase (což je ovšem časově náročné). A konečně nejvyšší tři bity slouží k ovládání přepínačů naznačených na dalším schématu:
Obrázek 3: Volba způsobu zapojení zvukového kanálu 1-4 přes registr AUDCx.
Konkrétně to znamená, že lze realizovat tyto způsoby zapojení:
| Bit 7 | Bit 6 | Bit 5 | Výsledek |
|---|---|---|---|
| 0 | 0 | 0 | 17 bit poly → 5 bit poly |
| 0 | 0 | 1 | 5 bit poly |
| 0 | 1 | 0 | 4 bit poly → 5 bit poly |
| 0 | 1 | 1 | 5 bit poly |
| 1 | 0 | 0 | 17 bit poly |
| 1 | 0 | 1 | čistý tón |
| 1 | 1 | 0 | 4 bit poly |
| 1 | 1 | 1 | čistý tón |
Nastavení registrů čipu POKEY pro vygenerování čistého tónu
V předchozích kapitolách jsme si sice popsali všech devět registrů, které se používají pro nastavení zvukových kanálů, ovšem důležitě je vědět, jak bude vypadat nastavení v praxi. Možností konfigurace je totiž celá řada. V dnešním článku si ukážeme jen dva příklady (a to prakticky ty nejjednodušší). Pokročilejším nastavením čipu POKEY se budeme věnovat příště. První demonstrační příklad po svém spuštění nastaví první zvukový kanál takovým způsobem, že bude přehrávat tón odpovídající komornímu A, což by mělo přesně odpovídat frekvenci 440 Hz.
Konfigurace kanálů bude vypadat následovně:
- Vstupní frekvenci pro všechny kanály zvolíme 64kHz (což je ve skutečnosti 63,337 kHz)
- Kanály nebudou spojeny, horní propusti nebudou nastaveny
- Dělič frekvence prvního kanálu nastavíme na takovou hodnotu, aby po vydělení 63,337 kHz vznikl obdélníkový signál s frekvencí 440 Hz
- Hlasitost prvního kanálu nastavíme na hodnotu 10 (z rozsahu 0..15)
- Zakážeme použití poly čítačů, takže výsledkem bude čistý (obdélníkový) signál
Co lze z předchozích bodů odvodit:
- Registr AUDCTL bude nastaven na samé nuly
- Registr AUDF1 nastavíme na hodnotu 71 (výpočet bude uveden v další kapitole)
- Registr AUDC1 nastavíme na hodnotu %10100000 (čistý tón) + 10 (hlasitost)
- Obsah registrů AUDF2, AUDF3, AUDF4 a AUDC1, AUDC2, AUDC3 ponecháme na výchozí nulové hodnotě
Způsob výpočtu dělitele frekvence pro vygenerování tónu komorního A
Ještě nám zbývá výpočet hodnoty, která se musí uložit do řídicího registru AUDF1. Tato hodnota bude zvolena takovým způsobem, aby se přehrála frekvence 440 Hz nebo nejbližší možná frekvence. Vstupem pro výpočet je frekvence CPU pro Atari určené pro televizní normu PAL, tj. 1.7734470 MHz (převedeme na Hz):
fclk = 1,7734470 Mhz = 1773447 Hz
Nulový bit registru AUDCTL jsme nastavili na nulu, takže frekvence na vstupu zvukových kanálů bude rovna:
fin = fclk / 28 = 1773447 / 28 = 63337 Hz
Dále jsme nastavili pátý a sedmý bit registru AUDC1 na jedničku a šestý bit na nulu. To znamená, že se bude přehrávat čistý obdélníkový tón a navíc se vstupní frekvence dále dělí dvěma:
fin2 = fin / 2 = 63337 / 2 = 31688 Hz
Tato frekvence se přímo dělí obsahem registru AUDF1 (+1), takže pro 440 Hz vypočteme potřebný dělitel:
N = 31688 Hz / 440 Hz – 1 = 72 – 1 = 71
Skutečná frekvence, která bude slyšel z reproduktoru, bude rovna:
fout = fin2 / (71+1) = 31688 / 72 = 439 Hz
S přimhouřením obou uší tedy skutečně dostaneme poněkud rozladěné komorní A (440 Hz).
Minimální kód potřebný pro vygenerování tónu komorního A
Na rozdíl od práce s grafikou je tvorba zvuků na osmibitových Atari velmi jednoduchá; nevyžaduje žádnou alokaci paměti (display list) a vlastně ani přesné časování. Pro přehrání komorního A postačují tři zápisy do třech řídicích registrů č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 #71 ; dělič základní frekvence
sta AUDF1 ; zápis do registru děliče frekvence pro zvukový kanál číslo 1 čipu POKEY
lda #00 ; základní tón odvozený od 64kHz
sta AUDCTL ; zápis do řídicího registru čipu POKEY
Vypnutí zvuku lze realizovat nastavením nulové hlasitosti prvního kanálu:
lda #0 ; vypnutí zvuku (nulová hlasitost)
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY
Úplný zdrojový kód prvního demonstračního příkladu
Úplný zdrojový kód dnešního prvního demonstračního příkladu vypadá následovně:
; ---------------------------------------------------------------------
; Generování zvuků čipem POKEY.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PURE_TONE_MASK = %10100000
.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
lda #71 ; dělič základní frekvence
sta AUDF1 ; zápis do registru děliče frekvence pro zvukový kanál číslo 1 č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 #0 ; vypnutí zvuku (nulová hlasitost)
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 č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
Čistý tón ovlivněný šumem
V předchozím demonstračním příkladu generoval POKEY čistý obdélníkový signál, ovšem modifikací obsahu registru AUDC1 je možné změnit způsob zapojení prvního zvukového kanálu takovým způsobem, aby se tento obdélníkový signál smíchal s výstupem vybraného poly čítače či dvojice poly čítačů. Z nabízených způsobů zapojení zvolíme tento:
Obrázek 4: Konfigurace umožňující kombinaci signálu z pětibitového poly čítače a vstupního pravidelného obdélníkového signálu.
To se provede jednoduše:
PURE_TONE_MASK = %10100000
POLY_5_BIT_MASK = %01100000
lda #POLY_5_BIT_MASK+10 ; hlasitost (0-15) + noise
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY
Úplný zdrojový kód druhého demonstračního příkladu
Následuje výpis úplného zdrojového kódu dnešního druhého demonstračního příkladu:
; ---------------------------------------------------------------------
; Generování zvuků čipem POKEY.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PURE_TONE_MASK = %10100000
POLY_5_BIT_MASK = %01100000
.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
lda #71 ; dělič základní frekvence
sta AUDF1 ; zápis do registru děliče frekvence pro zvukový kanál číslo 1 č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 #POLY_5_BIT_MASK+10 ; hlasitost (0-15) + noise
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 čipu POKEY
jsr get_key ; čekání na stisk klávesy
lda #0 ; vypnutí zvuku (nulová hlasitost)
sta AUDC1 ; zápis do registru řízení hlasitosti pro zvukový kanál číslo 1 č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ří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
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
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
