• 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ň