[an error occurred while processing this directive]
Bip, bip...

Programarea plăcii de sunet

Adrian Monea
După ce în numărul trecut am învățat despre programarea chip-ului FM al plăcii Sound Blaster, în acest număr vom învăța despre programarea chip-ului DSP. De asemenea va fi prezentat și un unit Pascal pentru lucrul cu DSP-ul.

Porturile chip-ului DSP al plăcii SoundBlaster

Chip-ul DSP (Digital Sound Processor) este programat prin intermediul a patru porturi determinate de setarea adresei de bază a plăcii de sunet:

RESET: $2x6

READ DATA: $2xA

WRITE COMMAND/DATA output

WRITE BUFFER STATUS input $2xC

DATA AVAILABLE $2xE

Unde:

x = 1 Ț adresa de bază a plăcii este setată la $210

x = 2 Ț adresa de bază a plăcii este setată la $220

...

x = 6 Ț adresa de bază a plăcii este setată la $260

Resetarea DSP-ului

Înainte de a fi programat, DSP-ul trebuie resetat. Acest lucru se face conform următoarei proceduri:

1) Se scrie valoarea 1 în portul de RESET ($2x6)

2) Se așteaptă 3 microsecunde

3) Se scrie valoarea 0 în portul de RESET ($2x6)

4) Se citește octetul de la portul de date disponibile ($2xE) până când bitul 7 este 1

5) Se citește octetul de la portul de citire date ($2xA) până când valoarea acestuia este $AA.

DSP-ul are nevoie în mod normal de aproximativ 100 de microsecunde pentru a se reseta. Dacă nu se resetează într-un timp rezonabil (să zicem 200 de microsecunde) atunci a apărut o eroare (este foarte probabil să se fi folosit o adresă I/O incorectă).

Scrierea în DSP

O valoare poate fi scrisă în DSP astfel:

1) Se citește portul WRITE BUFFER STATUS ($2xC) până când bitul 7 este 0

2) Se scrie valoarea în portul WRITE COMMAND/DATA ($2xC)

Citirea din DSP:

O valoare poate fi citită din DSP astfel:

1) Se citește valoarea din portul DATA AVAILABLE ($2xE) până când bitul 7 este 1

2) Se citește valoarea din portul READ DATA ($2xA)

Activarea difuzoarelor și controlul DMA-ului

Difuzoarele și DMA-ul se controlează scriind unul din următorii octeți în DSP:

Valoare Descriere

$D0 Oprește DMA-ul

$D1 Activează difuzoarele

$D3 Dezactivează difuzoarele

$D4 Repornește DMA-ul

Despre DMA vom discuta în rândurile ce urmează. Comenzile DMA de mai jos pot fi folosite pentru a opri melodia în timpul redării acesteia.

Scrierea în DAC

DAC-ul (Digital to Analog Converter) este partea plăcii de sunet responsabilă cu convertirea unui număr (de la 0 la 255) într-un nivel de sunet. Pentru a genera o undă de sunet pătrată la volum maxim (de exemplu) se pot scrie alternativ valori de 0 și 255 în DAC.

Pentru programarea DAC-ului în mod direct programul principal trebuie să seteze valoarea dorită în DAC. În mod direct DAC-ul nu poate fi programat decât pe 8 biți. Pentru a seta nivelul DAC-ului trebuie scrisă valoarea $10 în DSP urmată de numărul dorit (0-255). Evident, dacă difuzoarele nu au fost activate, nu se va auzi nici un sunet. În programarea în mod direct programul este responsabil de sincronizarea eșantioanelor digitale reproduse de placă. În mod tipic un program care folosește DAC-ul în mod direct reprogramează întreruperea de timer a calculatorului pentru a sincroniza eșantioanele necesare redării unei melodii.

DAC-ul poate fi de asemenea programat să accepte valori trimise lui prin controlerul DMA (Direct Memory Access). Programarea DMA-ului este complexă și de aceea în acest articol nu poate fi tratată exhaustiv. Vom aborda totuși câteva aspecte legate de DMA. Cel mai important lucru care nu trebuie uitat în legătură cu controlerul DMA este că acesta nu poate transfera date care se întind pe mai multe pagini de memorie. Dacă datele ce se doresc a fi transferate depășesc în dimensiune o pagină de memorie ele vor trebui transferate în memorie pe bucăți, câte o pagină pe transfer.

Setarea frecvenței de redare pentru transferul DMA este făcută scriind valoarea $40 în DSP urmată de TIME_CONSTANT, unde TIME_CONSTANT=256-1000000/frecvență.

ADPCM este un acronim pentru Adaptive Pulse Code Modulation, o tehnică pentru compresia de sunet în care se stochează diferența dintre eșantioanele consecutive și nu valorile propriu-zise ale acestora. În modurile cu octeți de referință primul octet este de fapt valoarea de start. Existența modurilor cu și fară octeți de referință înseamnă că putem reda blocuri consecutive fără a avea nevoie de un octet de referință la începutul fiecăruia.

Modul de efectuare al unui transfer DMA este următorul:

1) Se încarcă datele de sunet în memorie

2) Se pregătește controlerul DMA pentru transfer

3) Se setează constanta DSP TIME_CONSTANT la rata de eșantionare

4) Se scrie valoarea pentru DMA_TYPE_VALUE în DSP

5) Se scrie valoarea DATA_LENGTH în DSP (2 octeți, cel mai nesemnificativ fiind primul), unde:

DATA_LENGTH = număr de octeți de trimis - 1

Trebuie reținut că chip-ul DMA trebuie programat înaintea DSP-ului.

Citirea din ADC

Citirea de eșantioane din ADC (Analog to Digital Converter) poate fi făcută de asemenea în mod direct sau prin DMA.

Pentru a citi un eșantion în mod direct scrieți valoarea $20 în DSP și apoi citiți valoarea din DSP.

Pentru a pregăti DSP-ul pentru un transfer DMA trebuie urmați următorii pași:

1) Alocați memorie pentru eșantion

2) Pregătiți controlerul DMA pentru transfer

3) Setați constanta DSP TIME_CONSTANT la frecvența de eșantionare

4) Scrieți valoarea $24 în DSP

5) Scrieți DATA_LENGTH în DSP (2 octeți, cel mai nesemnificativ primul), unde DATA_LENGTH= numărul de octeți de citit - 1

Citirile DMA pot fi făcute doar pe 8 biți, modurile cu compresie sunt făcute prin soft și stocate în fișiere cu extensia VOC.

Programarea controlerului DMA

În cele de mai jos vor fi prezentați pașii necesari pentru programarea canalului 1 DMA pentru DSP în modul real:

1) Se calculează adresa de 20 de biți a tamponului de memorie folosit, unde

Adresa_de_bază = Segment*16 + Offset,

de exemplu $1234:$5678 = $179B8

2) Se scrie valoarea $05 în portul $0A

3) Se scrie valoarea $00 în portul $0C

4) Se scrie valoarea $49 în portul $0B (pentru redare) sau $45 în portul $0B (pentru înregistrare)

5) Se scrie octetul cel mai nesemnificativ (biții 0 - 7) al adresei de memorie de 20 de biți în portul $02

6) Se scrie octetul cel mai semnificativ (biții 8 - 15) al adresei de memorie de 20 de biți în portul $02

7) Se scrie pagina (biții 16 - 19) adresei de memorie de 20 de biți în portul $83

8) Se scrie octetul cel mai nesemnificativ al variabilei DATA_LENGTH în portul $03

9) Se scrie octetul cel mai semnificativ al variabilei DATA_LENGTH în portul $03

10) Se scrie valoarea $01 în portul $0A

Terminarea transferului DMA

Când un transfer DMA este complet, este generată o întrerupere. Numărul propriu zis al întreruperii depinde de setarea numărului IRQ pentru placa de sunet:

Număr IRQ Întrerupere

2 $0A

3 $0B

5 $0D

7 $0F

Pentru a servi una din aceste întreruperi trebuie făcute următoarele:

1) Se recunoaște întreruperea DSP citind portul DATA AVAILABLE ($2xE) o dată

2) Dacă există mai multe blocuri de transfer atunci ele sunt pregătite

3) Se scrie valoarea $20 (EOI) în portul controlerului de întreruperi $20

La fel ca și cu orice altă întrerupere hardware, trebuie refăcută starea sistemului (regiștrii etc) exact la fel cum era înainte de apelarea întreruperii.

Un unit Pascal pentru DSP

În continuare vom prezenta un unit Pascal pentru lucrul cu DSP-ul în modul real, unit scris de Mark Feldman, autorul PCGPE.

Listing DSP.PAS

Bibliografie:

1. Mark Feldman - PCGPE (PC Games Programming Encyclopedia, format electronic)

2. Creative Labs Corporation - Sound Blaster Resource Kit

Adrian Monea este student în anul I la Facultatea de Automatică și Calculatoare, Universitatea Tehnică Cluj.

[cuprins]