Hangkártyák programozása retro 12.

6.2.1.4. Hangfelvétel és lejátszás 8 bites auto-init DMA segítségével

Ebben a fejezetben nem térek ki külön a megszakítás vezérlő és a DMA vezérlő ismertetésére, illetve programozására, mivel arról egy külön könyvet lehetne írni. Azonban a jobb megérthetőség érdekében a [4] alapján készült Pascal unitot teljes terjedelmében közlöm:

unit sb_aiDMA;

{$g+}

interface uses dos;

type

mymem  = array[0..65534] of byte;

memptr = ^mymem;

const

SBbase    : word = $220;{ Báziscím }

SB_IRQ    : byte = $05; { Megszakítási vonal száma }

SB_DMA    : byte = $01; { DMA csatorna száma }

SB_TYPE   : byte = $00; { Hangkártya típus }

{ A DMA controller chip I/O címei }

DMA_cmd  = $08; { 8 bites DMA parancsregiszter}

DMA_mask = $0a; { 8 bites DMA maszkregiszter }

DMA_bpt  = $0c; { 8 bites DMA bájtmutató }

DMA_mode = $0b; { 8 bites DMA üzemmód regiszter }

DMA_stat = $08; { 8 bites DMA állapotregiszter }

DMA_req  = $09; { 8 bites DMA szoftveres kérés regiszter }

DMA_init = $0d; { 8 bites DMA inicializáló regiszter }

{ A buffer feldolgozás jelzése a hívó programnak }

RfshRequest : boolean = false;

PlayMode   = $58; { MEM->I/O auto-init mód (lejátszás)}

RecordMode = $54; { I/O->MEM auto-init mód (felvétel)}

var

DMAbuffer   : memptr; { A teljes DMA buffer mutatója }

DMAptr      : memptr; { A feldolgozandó bufferrész mutatója}

BufferSize  : word;   { A teljes DMA buffer mérete bájtban }

HalfSize    : word;   { A DMA buffer méretének fele }

{Sound Blaster DSP specifikus eljárások és függvények}

function  GetBlasterENV(var Base:word; var IRQ,DMA,Typ:byte):boolean;

function  DSPreset:boolean;

procedure DSPwrite(DSPdata:byte);

function  DSPread:byte;

procedure SetSampleRate(SR:word);

procedure SetDMAblockSize(size:word);

{ Megszakítás vezérlő és DMA vezérlő specifikus eljárások }

procedure EnableIRQline(Line:byte);

procedure DisableIRQline(Line:byte);

procedure DMA_addr(a:pointer; var p,s,l:word);

procedure InitDMAcontroller(Transfermode:byte);

procedure DoneDMAcontroller;

procedure InitInterrupt(UserDefinedIRQ:pointer);

procedure DoneInterrupt;

{ DMA bufferkezelő eljárások és függvények }

function  AllocDMAbuffer(size:word):integer;

procedure FreeDMAbuffer;

procedure ClearDMAbuffer;

procedure NextBuffer;

{ DMA átvitel vezérlő eljárások }

procedure StartAutoInitDMAplay;

procedure StartAutoInitDMArecord;

procedure StopAutoInitDMA;

implementation

const

{ A 8 bites DMA vezérlő regisztereinek I/O címei }

DMApages   : array[0..3] of byte=($87,$83,$81,$82);

DMAoffsets : array[0..3] of byte=($00,$02,$04,$06);

DMAsizes   : array[0..3] of byte=($01,$03,$05,$07);

{ Az aktuális DMA csatorna regisztereinek címei }

DMApage   : byte = $83;

DMAoffset : word = $0002;

DMAsize   : word = $0003;

var

{ Az eredeti megszakításvektort tároló változó }

OldSBorgIRQ : procedure;

{ A DMA buffer lefoglalásához szükséges változók }

DMAp1,DMAp2 : pointer;

DMAs1,DMAs2 : word;

function GetBlasterENV(var Base:word; var IRQ,DMA,Typ:byte):boolean;

var

s : string[128];

i : integer;

begin

GetBlasterENV:=false;

s:=getenv(‘BLASTER’); { Lekérdezzük a BLASTER környezeti változót }

if s[0]=#0 then exit; { Kilépünk, ha nincs ilyen változó }

{ Megállapítjuk az I/O báziscímet }

i:=pos(‘A2’,s);

if i>0 then val(‘$’+copy(s,i+1,3),Base,i);

if i<>0 then exit;

{ Megállapítjuk a megszakítási vonal számát }

i:=pos(‘ I’,s);

if i>0 then val(copy(s,i+2,1),IRQ,i);

if i<>0 then exit;

{ Megállapítjuk a DMA csatorna számát }

i:=pos(‘ D’,s);

if i>0 then val(copy(s,i+2,1),DMA,i);

if i<>0 then exit;

{ Megállapítjuk a kártya típusszámát }

i:=pos(‘ T’,s);

if i>0 then val(copy(s,i+2,1),Typ,i);

if i<>0 then exit;

GetBlasterENV:=true;

end;

A GetBlasterEnv függvénysegítségével kérdezzük le a BLASTER környezeti változót, amelyből megtudhatjuk a kártya báziscímét, az általa használt megszakítási vonal sorszámát és DMA csatorna számát.

function DSPreset:boolean; assembler;

asm

…{ Ezt a rutint már ismertettem a DSP programozása kapcsán}

end;

procedure DSPwrite(DSPdata:byte); assembler;

asm

… { Ezt a rutint már ismertettem a DSP programozása kapcsán}

end;

function DSPread:byte; assembler;

asm

… { Ezt a rutint már ismertettem a DSP programozása kapcsán}

end;

A fenti három szubrutinnal már találkozhattunk a DSP programozása kapcsán.

procedure SetSampleRate(SR:word);

var

TC : byte;

begin

TC:=256-round(1000000/SR);     { Kiszámítjuk az időállandót, }

DSPwrite($40);                 { majd kiírjuk a DSP-re }

DSPwrite(TC);

end;

A SetSampleRate eljárás a mintavételezési frekvencia beállítására szolgál.

procedure SetDMAblocksize(size:word);

begin

DSPwrite($48);                 { Set DMA blocksize parancs }

dec(size);                     { A méretet csökkenteni kell!}

DSPwrite(LO(size));            { Kiírjuk az alsó bájtot }

DSPwrite(HI(size));            { Kiírjuk a felső bájtot }

end;

A SetDMABlocksize arra szolgál, hogy általa beállítsuk a DMA blokk méretét a DSP számára. Auto-init DMA használatakor a Set DMA blocksize DSP paranccsal kell megadni a DSP számára az átvitt bájtok számát.

procedure SB_AutoInitDMA_IRQ; assembler;

asm

push  DS                 { Tároljuk a használt regisztereket }

push  ax

push  dx

mov   ax,Seg @DATA       { A DS szegmensregisztert az        }

mov   DS,ax              { eredeti adatszegmensre állítjuk   }

mov   dx,SBbase

add   dx,$0e             { Olvassuk DSP data avail portot,   }

in    al,dx              { ez jelzi a hangkártyának, hogy a  }

{ megszakítást fogadtuk              }

mov   RfshRequest,TRUE   { A buffer frissítése szükséges jel }

mov   al,020h            { EOI jelet küldünk a               }

out   020h,al            { megszakítás vezérlőnek            }

pop   dx

pop   ax

pop   DS                 { Visszaolvassuk a regiszterek értékét  }

iret                     { Visszatérünk a megszakításból         }

end;

Az SB_AutoInitDMA_IRQ egy megszakítási rutin. Az auto-init DMA adatátvitel folyamán a hangkártya minden blokk lejátszásakor, illetve felvételekor megszakítást generál. A megszakítási rutin nyugtázza a megszakításkérést a hangkártya felé, és igaz értékűre állítja RfshRequest változót, majd nyugtázza a megszakítást a megszakításvezérlőnek is. A DMA átvitel ez alatt is zavartalanul folyik tovább.

procedure NextBuffer; assembler;

asm

mov   ax,DMAbuffer.word  { Az aktuális mutató a buffer elejére }

cmp   ax,DMAptr.word     { mutat? }

jnz   @1sthalf           { Ugrás tovább, ha nem }

@2ndfalf:

add   ax,HalfSize        { Ha igen, a második fele következik }

@1sthalf:

mov   DMAptr.word,ax     { Egyébként az első }

end;

A buffer felének feldolgozásakor a NextBuffer eljárás segítségével válthatunk aktív buffert.

procedure EnableIRQline(Line:byte); assembler;

asm

mov  cl,Line       { A vonal száma a CL regiszterbe kerül  }

and  cl,00000111b  { Csak az alsó három bitet hagyjuk meg  }

mov  ah,11111110b  { Maszk: ez a bit nulla lesz            }

rol  ah,cl         { A 0 bitet a megfelelő helyre toljuk   }

in   al,021h

and  al,ah         { Engedélyezzük az adott vonalat        }

out  021h,al

end;

procedure DisableIRQline(Line:byte); assembler;

asm

mov  cl,Line       { A vonal száma a CL regiszterbe kerül  }

and  cl,00000111b  { Csak az alsó három bitet hagyjuk meg  }

mov  ah,00000001b  { Maszk: ez a bit egy lesz              }

rol  ah,cl         { Az 1 bitet a megfelelő helyre toljuk  }

in   al,021h

or   al,ah         { Tiltjuk az adott vonalat  }

out  021h,al

end;

Az EnableIRQline, illetve a DisableIRQline engedélyezi, illetve tiltja az adott megszakítási vonalat.

procedure DMA_addr(a:pointer; var p,s,l:word);

var

la : longint;

begin

{ la=20 bites fizikai cím, azaz 16*szegmens+offszet }

la:=(longint(seg(a^)) shl 4)+ofs(a^);

p:=la shr 16;            { Lapszám: 0..15 közé fog esni }

s:=la and 65535;         { Start Address a page-en belül }

l:=65536-s;              { A page-ből még ennyi bájt van hátra }

end;

A DMA_addr segítségével az a pointer által mutatott címből előállítja a hozzá tartozó 64K-s DMA lapszámot és a lapon belüli eltolást. A lapszám: p, az eltolás: s, a lapon rendelkezésre álló bájtok száma pedig: l.

procedure InitDMAcontroller(Transfermode:byte);

var

p,s,l : word;

begin

{A táblázatokból kiolvassuk az adott csatornához tartozó I/O címeket}

DMApage   := DMApages[SB_DMA];       { Lapregiszter }

DMAoffset := DMAoffsets[SB_DMA];     { DMA címregiszter }

DMAsize   := DMAsizes[SB_DMA];       { Számláló regiszter }

port[DMA_mask]:=SB_DMA or 4;         { Tiltjuk a DMA-t a csatornán  }

port[DMA_bpt]:=$00;                  { Töröljük a bájtmutatót       }

port[DMA_mode]:=Transfermode or SB_DMA;   { auto-init mód a csatornára }

DMA_addr(DMAbuffer,p,s,l);           { Kiszámítjuk a cím komponenseit  }

port[DMApage]:=p;                    { Beállítjuk a lapregiszter,      }

port[DMAoffset]:=LO(s);              { majd a csatorna címregiszterét  }

port[DMAoffset]:=HI(s);

port[DMAsize]:=LO(BufferSize-1);     { Beállítjuk a számláló regisztert}

port[DMAsize]:=HI(BufferSize-1);     { blokk mérete -1 !               }

port[DMA_mask]:=SB_DMA and 3;        { Engedélyezzük a DMA-t           }

end;

InitDMAcontroller: a 8 bites DMA vezérlő beállítása az auto-init DMA módhoz. Az eljárás beállítja a DMA vezérlő üzemmódját, a lapregiszter, a csatorna cím- és számlálóregiszterét.

procedure DoneDMAcontroller;

begin

port[DMA_mask]:=SB_DMA or 4;         { Tiltjuk a DMA-t a csatornán }

end;

procedure InitInterrupt(UserDefinedIRQ:pointer);

begin

GetIntVec($08+SB_IRQ,@OldSBorgIRQ);  { Tároljuk az eredeti vektort }

if UserDefinedIRQ=nil then

SetIntVec($08+SB_IRQ,@SB_AutoInitDMA_IRQ) { Beállítjuk az újat    }

else

SetIntVec($08+SB_IRQ,UserDefinedIRQ);

EnableIRQline(SB_IRQ);               { Engedélyezzük az adott vonalat}

end;

Az InitInterrupt eljárással állítjuk be a megszakítási vektort. Az eredeti megszakítási vektort elmenti, majd beállítja az általunk definiáltat.

procedure DoneInterrupt;

begin

DisableIRQline(SB_IRQ);            { Tiltjuk az adott vonalat }

SetIntVec($08+SB_IRQ,@OldSBorgIRQ);{ Visszaállítjuk az eredeti vektort }

end;

procedure StartAutoInitDMAplay;

begin

DSPwrite($d1);               { Parancs: DSP speaker on       }

DSPwrite($1c);               { Parancs: DSP auto-init DMA DAC}

end;

StartAutoInitDMAplay: az auto-init DMA lejátszás megkezdése.

procedure StartAutoInitDMArecord;

begin

DSPwrite($d3);                    { Parancs: DSP speaker off      }

DSPwrite($2c);                    { Parancs: DSP auto-init DMA ADC}

end;

StartAutoinitDMArecord: az auto-init DMA felvétel elindítása.

procedure StopAutoInitDMA;

begin

DSPwrite($d3);                     { Parancs: DSP speaker off      }

DSPwrite($d0);                     { Parancs: Stop DMA transfer    }

DSPwrite($da);                     { Parancs: Stop auto-init DMA   }

DSPwrite($d0);                     { Parancs: Stop DMA transfer    }

end;

StopAutoInitDMA: az auto-init DMA átvitel leállítása.

procedure ClearDMAbuffer;

begin

fillchar(DMAbuffer^,BufferSize,$80);{ Minden minta nulla jelszintű  }

end;

ClearDMAbuffer: a DMA buffer törlése.

function AllocDMAbuffer(size:word):integer;

var

p,s,l,i : word;

begin

DMAs1:=size;                { Első menet }

DMAs2:=0;

AllocDMAbuffer:=-1;

if MaxAvail<size then exit;{ Azonnal kilépünk, ha nincs elég hely} getmem(DMAp1,DMAs1);        { Lefoglaljuk a megfelelő blokkot }

DMA_addr(DMAp1,p,s,l);     { Meghatározzuk a cím komponenseit }

if l>=size then            { Van elég hely a lapon? }

begin                     { Ha igen…             }

DMAbuffer:=DMAp1;        { Ez lesz a DMAbuffer    }

DMAptr:=DMAp1;           { A DMAptr is ide mutat  }

BufferSize:=size;        { Beállítjuk a buffer méretét }

HalfSize:=BufferSize shr 1;{ Ennek fele a HalfSize     }

AllocDMAbuffer:=1;         { A lefoglal s sikeres volt (1. menet)}

ClearDMAbuffer;            { Töröljük a buffert (csend)  }

exit;                      { Visszatérünk a függvényből  }

end;

inc(p);                      { A következő lap lesz a jó }

DMAs2:=l;                    { Itt még ennyi hely kell   }

getmem(DMAp2,DMAs2);         { Lefoglaljuk               }

DMAbuffer:=ptr(p shl 12,$0000);{ Előállítjuk a buffer mutatóját }

DMAptr:=DMAbuffer;           { A DMAptr is ide mutat            }

BufferSize:=size;            { Beállítjuk a buffer méretét      }

HalfSize:=BufferSize shr 1;  { Ennek fele a HalfSize            }

AllocDMAbuffer:=2;           { A lefoglal s sikeres volt (2. menet)}

ClearDMAbuffer;              { Töröljük a buffert (csend)          }

end;

AllocDMAbuffer: helyfoglalás a DMA buffer számára.

procedure FreeDMAbuffer;

begin

if DMAs2<>0 then freemem(DMAp2,DMAs2); { A terület felszabadítása }

if DMAs1<>0 then freemem(DMAp1,DMAs1); { A terület felszabadítása }

DMAs2:=0;

DMAs1:=0;

end;

FreeDMAbuffer: az AllocDMAbuffer eljárás által lefoglalt területek felszabadítása.

END.

A felvételre természetesen használhattunk volna 16 bites auto-init DMA átviteli módot is, de a tapasztalat az, hogy a Windows nem igazán örül neki, ha a 16 bites DMA csatornát szeretnénk használni, ugyanis azt előszeretettel lefoglalja saját erőforrásai számára. Emiatt már a kártya telepítésekor érhetnek minket meglepetések, hiszen csakis a 8 bites DMA csatornákat ajánlja fel az említett operációs rendszer a hangkártya számára. Erről egyébként több hardveres kézikönyv is említést tesz és azzal vígasztal minket, hogy ilyen esetben a 16 bites DMA átvitel is a 8 bites DMA csatornán valósul meg.

Esetünkben nem követelmény, hogy az általunk rögzítendő hanganyag CD, esetleg HI-FI minőségű legyen, de 44,1 kHz-es mintavételi frekvencia és 8 bites kvantálási hossz mellett is megfelelő minőséget lehet elérni, még akkor is, ha mono felvételt készítünk. Így nyugodtak lehetünk, hogy nem lesz túl nagy az információveszteség, legalább is nem akkora, hogy érdemes lenne a hangállomány méretét többszörösére növelni, nem mintha olyan óriási méretekről lenne szó.

[4] László József: Hangkártya programozása Pascal és Assembly nyelven, Computer Books, Budapest, 1999

Hangkártyák programozása retro 11.

6.2.1. Sound Blaster 16 felépítése és programozása

6.2.1.1. Lehetőségek

1)      FM chip:

  • Bővített OPL–3 FM chip, amely négyoperátoros hangkeltésre is alkalmas,
  • Teljes kompatibilitás az előző ADLIB és SB (kivéve SB Pro) változatokkal,
  • 18 kétoperátoros, vagy 6 négyoperátoros hangcsatorna,

2)      Továbbfejlesztett DSP egység:

  • 8- és 16 bites felvétel és lejátszás,
  • Sztereó és mono felvétel és 8- és 16 bites módban,
  • Dinamikus szűrők a felvételhez és a visszajátszáshoz,
  • Két DMA csatorna a DMA vezérelt felvételhez és lejátszáshoz,
  • Kibővített DSP parancskészlet,
  • Lineáris lépésekben programozható mintavételi frekvencia 5-től 48 kHz-ig,

3)      Bővített keverő (mixer):

  • A bemeneti eszközök lehetséges keverése,
  • Magas és mély állítási lehetőségek,

4)      Szoftverből állítható hangerő minden kimenethez

5)      Beépített sztereó erősítő:

  • 4 W/csatorna, 4 ohmos hangszórókhoz,
  • Automatikus, vagy fix erősítés mikrofonhoz,
  • Külső, vagy belső erősítő a kimenetekhez

6)      MIDI csatlakozó:

  • SB, vagy MPU401–UART kompatibilis mód,

7)      CD-ROM csatlakozó

8)      Hardverbeállítások szoftverből:

  • Szoftverből állítható megszakítási szint,
  • Szoftverből állítható 8- és 16 bites DMA csatorna szám.

6.2.1.2. A DSP programozása

Az SB16 DSP-je a következő I/O címeken keresztül érhető el:

I/O relatív cím I/O alapbeállítású cím DSP funkció
Base+$06 $226 (write only) A DSP alapállapotba hozása (DSP Reset)
Base+$0A $22A (read only) Adat olvasása az ADC-ről (Read Data)
Base+$0C $22C (read/write) Parancs/adat írása a DAC-ra (Write command/data)Bufferállapot olvasása (Read Buffer Status)
Base+$0E $22E (read only) Adatérvényesség olvasása, a 8 bites megszakítás nyugtázása (Data Available, 8 bit IRQ Acknowledge)
Base+$0F $22F (read only) A 16 bites megszakítás nyugtázása (16 bit IRQ Acknowledge)

A fontosabb SB16 DSP parancsokat a T.2. foglalja össze.

T.2. Fontosabb SB16 DSP parancsok

Kód Parancs Parancsfunkció
$1C 8 bit auto-init DMA play 8-bites auto-init DMA lejátszás
$D0 Pause 8 bit DMA 8-bites DMA átvitel felfüggesztése
$D1 Speaker On A DSP kimenetének bekapcsolása
$D3 Speaker Off A DSP kimenetének kikapcsolása
$D4 Continue 8 bit DMA 8-bites DMA átvitel folytatása
$D5 Pause 16 bit DMA 16-bites DMA átvitel felfüggesztése
$D6 Continue 16 bit DMA 16-bites DMA átvitel folytatása
$D9 Exit 16 bit auto-init DMA 16 bites auto-init DMA leállítása
$DA Exit 8 bit auto-init DMA 8 bites auto-init DMA leállítása
$E1 Get DSP version DSP verziószámának lekérdezése
$2C 8 bit auto-init DMA record 8bites auto-init DMA felvétel
$40 Set Time Constant A mintavételezési időállandó beállítása
$41 Set play sample rate Mintavételi frekvencia beállítása lejátszáshoz
$42 Set record sample rate Mintavételi frekvencia beállítása felvételhez
$48 Set DMA block size DMA blokkméret beállítása
$B6 16 bit auto-init DMA play 16 bites auto-init DMA lejátszás
$BE 16 bit auto-init DMA record 16 bites auto-init DMA felvétel
$B0 16 bit normal DMA play 16 bites normál DMA lejátszás
$B8 16 bit normal DMA record 16 bites normál DMA felvétel
$C6 8 bit auto-init DMA play 8 bites auto-init DMA lejátszás
$CE 8 bit auto-init DMA record 8 bites auto-init DMA felvétel
$C0 8 bit normal DMA play 8 bites normál DMA lejátszás
$C8 8 bit normal DMA record 8 bites normál DMA felvétel
$F2 IRQ request (8 bit) DSP szoftveres megszakításkérés 8 bites átvitelhez
$F3 IRQ request (16 bit) DSP szoftveres megszakításkérés 16 bites átvitelhez
$FB Read DSP status DSP állapotának lekérdezése
$FC Get DSP information További információ a DSP állapotáról
$FD Get last DSP command Az utolsó DSP parancs lekérdezése

A következő Pascal-ban használt Assembly betéteknek a DSP programozása során vehetjük hasznát. A DSP programozására készített teljes unit forrását F.25-ben találhatjuk.

function DSPreset:boolean; assembler;

asm

mov   dx,BaseAddr { A báziscím:$220 a DX regiszterbe kerül. }

add   dl,$06      { DX=$226, DSP Reset. }

mov   al,$01

out   dx,al       { Egy $01 érték kiküldése. }

mov   cx,100

rep lodsb         { Várakozás. }

xor   al,al

out   dx,al       { Egy $00 érték kiküldése. }

add   dl,$08      { DX=$22E, Data Available. }

mov   cx,128

@wait1:             { Várakozás, amíg az adat érvényes nem lesz. }

in    al,dx

and   al,$80

jnz   @test2      { Kiugrás, ha érvényes. }

loop  @wait1

jmp   @bad        { Hiba, ha lejárt a ciklus. }

@test2:

mov   cx,10240

sub   dl,$04      { DX=$22A, DSP Read Data. }

@wait2:

in    al,dx

cmp   al,$0aa     { A $AA értéket kell adnia. }

jz    @good       { Ugrás, ha a Reset sikeres. }

loop  @wait2

@bad:

xor   al,al       { Hiba, ha Al=false. }

jmp   @exit

@good:

mov   al,$01      { Nincs hiba, ha AL=true. }

@exit:

end;

DSPreset: a Sound Blaster DSP egységének alapállapotba hozása. A függvény megpróbálja inicializálni a DSP egységet. Ha ez sikerül, vagyis a beállított időintervallumon belül a megfelelő értéket észleli, akkor igaz értékkel tér vissza. Ha az időzítési ciklus lejártával sem érzékeli a megfelelő eredményeket, akkor a DSP nem működik helyesen és hamis értékkel tér vissza.

procedure DSPwrite(DSPdata:byte);  assembler;

asm

mov   dx,BaseAddr { Báziscím:$220 a DX regiszterbe. }

add   dl,0ch      { DX=$22C, Read Buffer Status. }

@c1:

in    al,dx

and   al,$80      { Szabad már a regiszter? }

jnz   @c1         { Várakozás, ha még nem. }

mov   al,DSPdata  { AL=adat/parancs. }

out   dx,al       { Write Command/Data. }

end;

DSPwrite: egy adat, illetve parancs kiküldése a DSP-nek.

function DSPread:byte;  assembler;

asm

mov   dx,BaseAddr { Báziscím:$220 a DX regiszterbe. }

add   dl,$0e      { DX=$22E, Data Available. }

@wait:

in    al,dx       { Várakozás, amíg az adat érvényes nem lesz. }

and   al,$80

jz    @wait

sub   dl,$04      { DX=$22A, DSP Read Data. }

in    al,dx       { Az adat beolvasása AL-be. }

end;

DSPread: adat beolvasása a DSP-ről.

F.31-ben további példákat láthatunk a DSP programozására.

6.2.1.3. A mixer programozása

Az SB16 mixer a következő I/O címeken keresztül érhető el:

Regiszter I/O cím Alapbeállítású I/O cím Funkció
Base+$04 $224 (w) Mixer címregiszter (address)
Base+$05 $225 (r/w) Mixer adatregiszter (data)

Az SB16 mixerének regisztereit a T.3. ismerteti részletesebben.

T.3. Az SB mixerének fontosabb regiszterei

Regiszter Funkció
$30 Master Volume Left Kimeneti hangerő, bal oldal
$31 Master Volume Right Kimeneti hangerő, jobb oldal
$32 DSP Voice Volume Left DSP hangerő, bal oldal
$33 DSP Voice Volume Right DSP hangerő, jobb oldal
$34 MIDI/FM Volume Left MIDI/FM egység hangerő, bal oldal
$35 MIDI/FM Volume Right MIDI/FM egység hangerő, jobb oldal
$36 CD Volume Left Audio CD hangerő, bal oldal
$37 CD Volume Right Audio CD hangerő, jobb oldal
$38 Line In Volume Left Vonali bemeneti hangerő, bal oldal
$39 Line In Volume Right Vonali bemeneti hangerő, jobb oldal
$3A Microphone Volume Mikrofon hangerő
$3B PC-Speaker Volume PC-Speaker hangerő
$3C Output Select Kimeneti eszközök kiválasztása
$3D Left Input Device Select Bal oldali bemeneti eszközök kiválasztása
$3E Right Input Device Select Jobb oldali bemeneti eszközök kiválasztása
$3F Left Input Gain Level Bal oldali bemeneti erősítési szint
$40 Right Input Gain Level Jobb oldali bemeneti erősítési szint
$41 Left Output Gain Level Bal oldali kimeneti erősítési szint
$42 Right Output Gain Level Jobb oldali kimeneti erősítési szint
$43 Microphone AGC Control Mikrofon AGC engedélyezés/tiltás
$44 Left Treble Level Bal oldali magas szint szabályozás
$45 Right Treble Level Jobb oldali magas szint szabályozás
$46 Left Bass Level Bal oldali mély szint szabályozás
$47 Right Bass Level Jobb oldali mély szint szabályozás
$80 Interrupt Level Select Megszakítási vonal kiválasztása
$81 DMA Channels Select DMA csatornák kiválasztása (8, 16 bit)
$82 Interrupt Request Status Megszakításkérés állapot

A mixer néhány fontosabb regiszterének részletezése

Reg. 7. bit 6. bit 5. bit 4. bit 3. bit 2. bit 1. bit 0. bit Megjegyzés
$3C Line Lf Line Rg CD Lf CD Rg Mic 0: tiltás,1: engedélyezés
$3D 0 (fix) MIDI Lf MIDI Rg Line Lf Line Rg CD Lf CD Rg Mic 0: tiltás,1: engedélyezés
$3E 0 (fix) MIDI Lf MIDI Rg Line Lf Line Rg CD Lf CD Rg Mic 0: tiltás,1: engedélyezés
$42 Gain Gain 00: egyszeres erősítés,01: kétszeres erősítés,10 négyszeres erősítés,

11: nyolcszoros erősítés

$43 FGC/AGC $00: fix erősítés,$01: automatikus erősítésszabályozás
$80 IRQ 10 IRQ 7 IRQ 5 IRQ 2 Mindig csak egy bit legyen 1
$81 DMA 7 DMA 6 DMA 5 DMA 3 DMA 1 DMA 0 Egyszerre csak egy bit lehet 1 mindkét részben
High DMA Low DMA
$82 1 (fix) 0 (fix) 0 (fix) 0 (fix) ? UART DSP 16 DSP 8 A regiszter alsó három bitje azt adja meg, hogy melyik eszköz okozta a megszakítást.

A következő programrészleteknek a mixer programozása során vehetjük hasznát:

procedure SetMixer(RegNum,Value:byte);     assembler;

asm

mov  dx,Base      { Báziscím:$220 }

add  dx,$0004     { Mixer címregiszter:$224 }

mov  al,RegNum

out  dx,al        { Cím kiküldése }

inc  dx           { Mixer adatregiszter:$225 }

mov  al,Value

out  dx,al        { Adat kiküldése }

end;

A SetMixer eljárás segítségével a mixer egy adott regiszterét lehet beállítani.

function  GetMixer(Regnum:byte):byte;      assembler;

asm

mov  dx,Base      { Báziscím:$220 }

add  dx,$0004     { Mixer címregiszter:$224 }

mov  al,Regnum

out  dx,al        { Cím kiküldése }

inc  dx           { Mixer adatregiszter:$225 }

in   al,dx        { Adat beolvasása }

end;

A GetMixer függvény egy adott regiszter értékét kérdezi le.

A fenti rutinokat tartalmazó Pascal unit részletes listája a F.26-ban található [4] alapján. F.32-ben pedig egy olyan program tanulmányozható, amely kiírja az SB mixerének regisztereit, módosítható és fix bitjeikkel.

[4] László József: Hangkártya programozása Pascal és Assembly nyelven, Computer Books, Budapest, 1999