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ň