• PROGRAMOVÁNÍ KARTY SOUND BLASTER 16 •
Úvod
Tento dokument byl napsán podle anglického originálu Programming the
SoundBlaster 16 DSP, jehož autorem je Ethan Brodsky. Autor tohoto
dokumentu ani Ethan Brodsky nenesou žádnou zodpovědnost za případné
škody, jenž si použitím tohoto dokumentu způsobíte a předem vás
upozorňují, že veškeré postupy zde popsané si zkoušíte na vlastní
nebezpečí.
Porty karty Sound Blaster 16
Karta Sound Blaster 16 (dále jen SB 16 či SB) v sobě obsahuje DSP čip
(CT1341), který je možno programovat použitím různých I/O portů,
které jsou určeny bázovou I/O adresou, nastavenou buď pomocí jumperů
na kartě, nebo pomocí příslušného programu u karty bez jumperů. SB16
obsahuje celkem 16 I/O portů, používaných buď k programování čipu
Yamaha YM 262, který se stará o část syntetizovaných nástroju, nebo
k programování mixéru, či k programování přístupu k jednotce CD-ROM.
Jen 5 z nich je použito k programování DSP čipu. Jsou to:
2x6 - DSP Reset port
2xA - DSP Read port
2xC - DSP Write port (data, příkazy), DSP write buffer status
2xE - DSP DSP read buffer status, DSP interrupt acknowledge
2xF - DSP 16-bit interrupt acknowledge
Jejich funkce je následující:
2x6 - pomocí zápisu na tento port se SB přivede do stavu, kdy je
možno s ní začít pracovat. Vymažou se registry, které byly nastaveny
při předchozím hraní a pokud karta momentálně hraje, tak se přenos
zastaví.
2xA - tento port slouží k přečtení dat ze SB.
2xC - tento port je určeen k zápisu dat a příkazů do SB, a jeho
nejvyšší bit slouží též jako kontrola, zda DSP už přijal údaje, které
jsme do něj poslali
2xE - nejvyšší bit tohoto portu má obdobnou funkci, jako
u předchozího portu, je však používám pro kontrolu, zda jsou data,
o která jsme DSP požádali už dostupná na čtecím portu. Též slouží na
potvrzení proběnutí přerušení při vlastním přenosu.
2xF - potvrzení proběhnutí přerušení při 16 bitovém zvuku.
Reset DSP
Před tím, než začnete DSP programovat, musíte jej resetovat. DSP se
resetuje touto procedurou:
1) Zápis 1 do reset portu (2xF)
2) Počkání 3 mikrosekundy
3) Zapsat 0 do reset portu (2x6)
4) Číst Read buffer status port (2xE) dokud není 7. bit nastaven na 1
5) Číst Read port (2xA) dokud neobdržíme AAh
Doba, jak dlouho se DSP resetuje se pohybuje kolem 100 mikrosekund.
Pokud po této době nedostaneme signál AAh, pak je nejspíše SB špatně
instalován nebo jsme zvolili špatnou bázovou adresu.
TIP: Tímto způsobem můžeme napsat program, který detekuje bázovou
adresu SB - SB se může nacházet na adresách 220h, 240h, 260h nebo
280h.
Psaní do DSP
K zapsaní byte do DSP se používá následující procedura:
1) Číst Write buffer status port (2xC) dokud není vymazám 7. bit
2) Zapsat hodnotu na Write port (2xC)
Čtení z DSP
Pokud hodláte přečíst nějaká data z DSP, měli byste použít tuto
sekvenci:
1) Číst Read buffer status port (2xE) dokud není nastaven 7. bit
2) Číst hodnotu z Read portu (2xA)
Postup při přehrávání
Vytvoříme si v paměti buffer, do něhož nahrajeme první část souboru,
který chceme přehrát.
type TBuffer=Array [0..2047] of byte;
var Buffer:TBuffer;
f:file;
count:Word;
begin
Assign (f, 'PISNICKA.WAV');
Reset (f, 1);
Seek (f, 2Ch); {Přeskočíme hlavičku souboru WAV}
New (Buffer);
BlockRead (f, Buffer^, SizeOf (TBuffer), count);
end;
Nejdříve spočítáme lineární adresu toho bufferu.
Adresa:= Seg(Buffer^)*16+Ofs(Buffer^);
Pak do maskovacího portu DMA (pokud používáme 16bit přenos, tak je to
D4h, pokud 8bit přenos, tak 0Ah) pošleme 1+(číslo_kanálu_DMA MOD 4)
čímž zastavíme přenos dat do karty přes DMA.
Pak vymažeme flip-flop počítadlo tak, že do mazacího flip-flop portu
(při 16bit je to D8h, při 8bit 0Ch) posles cokoliv.
Pak na port módu přenosu (16bit D6h, 8bit 0Bh) pošleme něco podle
téhle tabulky:
Bity 6 a 7 musí byt 0
Bit 5 1 zmenšovat adresu v paměti, 0 zvětšovat adresu v paměti.
Bit 4 1 Autoinit DMA, 0 Jeden cyklus DMA (při autoinit DMA přenos,
když dojede na konec bloku dat automaticky skočí na začátek, což je
velmi výhodné - viz níže, při Jednom cyklu se to na konci zastaví)
Bity 3 a 2 00 - verifikace (?), 01 - Psaní (do paměti), 10 - čtení
(z paměti)
Bity 1 a 0 00 - Kanál 0 (a 4), 01 - 1 (a 5), 10 - 2 (6), 11 - 3 (7)
Tohle si složíme a pošleme to na port módu přenosu, pokud chceme
hrát, bude ten byte vypadat asi takto:
adresu v paměti zvětšovat
v
00 0 1 10 xx <- to si rozhodneme podle kanálu, na kterém je SB
^ ^
autoinit přenos čtení z paměti
Pak pošleme do řadiče DMA začátek stránky, který vypočítáme
z lineární adresy. (Adresa MOD 65536) a nebo při 16bit přenosu
((Adresa DIV 2) MOD 65536). Z tohoto výsledku se pošle nejdříve nižší
a pak vyšší byte na adresu portu báze adresy (pro kanál 0 je to port
00h, pro kanál 1 = 02h, pro 2 = 04h, pro 3 = 06h, při 16bit přenosu
je to pro kanál 4 = C0, pro 5 = C4, pro 6 = C8, pro 7 = CC).
Pak pošleme do počítadla délku bufferu-1 a to opět nejdříve nižší
a pak vyšší byte. Port počítadla DMA je : pro 8bit přenos je to pro
každý kanál o 1 větší, než port báze adresy, pro 16bit přenos je to
vždy o 2 vyšší (např.: pro kanál 5 je to C6, pro kanál 2 je to 05).
Poté do stránkovacího portu pošleme pro 8bit přenos (Adresa div
65536), při 16bit přenosu ((Adresa div 2) div 65536). Čísla
stránkovacího portu jsou tyto :
Pro 8bit přenos a kanál 2 je to 81, kanál 3 je to 82, kanál 1 je to
83, kanál 0 je to 87, pro 16bit přenos a kanál 6 je to 89, pro kanál
7 je to 8A, pro kanál 5 je to 8B.
Pak povolíme DMA přenos tím, ze vymažeme maskovací bit pro určitý
kanál tak, že do maskovacího portu DMA (pokud používáme 16bit přenos,
tak je to D4h, pokud 8bit přenos, tak 0Ah) pošleme jenom
číslo_kanálu MOD 4.
Pak nastavíme samplovací frekvenci pro SB, a to tak, že pošleme do
DSP příkaz 41h a pak vyšší a pot nižší byte té frekvence, kterou
chceme přehrávat.
A teďka přichází to, čím se to vše rozhýbe (rozhlučí) :
Do DSP pošle tato sekvence :
Příkaz, který vypadá takhle:
pro 8 bit 0 pro D/A převodník 0 vypnuto
1100 1 pro A/D převodník 1 zapnuto
v v v
xxxx A/D A/I FIFO 0
^ ^
1011 0 pro jeden cyklus
Pro 16 bit 1 pro autoinit
Mód, který vypadá takhle :
0 Mono
1 Stereo
v
00 Stereo Signed 0000
^
0 unsigned data
1 signed data
Pak nižší byte délky bufferu-1
Pak vyšší byte délky bufferu-1
Pokud nechceme, aby to při přehrávání lupalo a praskalo, tak uděláme
takový fígl, že si buffer rozdělíme na dvě poloviny, a do DMA řadiče
pošleme délku celého bufferu, ale do SB dálku jenom poloviny. To
způsobí, že SB vyvolá IRQ, že nemá data už v polovině bufferu, ale
přesto pokračuje v hraní dat, která přicházejí z DMA kanálu.
Nainstalujeme si obsluhu toho IRQ a jakmile přenos dojede do
poloviny, vyvolá to příslušné IRQ a skočí to do té naší obsluhy, tak
se z disku, nebo z jiného místa natáhnou další data do poloviny,
která právě proběhla, pak se do SB pošle signál, že už je IRQ
obslouženo, což se udělá tak, že při 8bit přenosu přečteme něco
z portu [2xE] a nebo při 16bit přenosu z portu [2xF]. Pak se ještě
řadiči přerušení pošle 20h (při IRQ 0-7 je to port 20h, při IRQ 8-15
je to port A0h).
Pak při každém dalším vyvolání IRQ dotáhneme dlaší kousek dat do
bufferu, který právě proběh a to až do doby, kdy se dostaneme na
konec souboru, který právě přehráváme.
Vše zastavíme tak, že při 8bit zvuku pošleme do DSP příkaz DAh a při
16bit zvuku D9h.
Poděkování
Hlavně bych rád poděkoval Ethanu Brodsky, který tohle všech sepsal
a dal programátorům k používání.
Bernie
výheň