Hangkártyák programozása retro 13.

6.2.2. Gravis Ultrasound lehetőségei és programozása

A Gravis hangkártyák általános felépítése helyett nézzük meg, milyen lehetőségekkel bír az általam használt GUS ACE:

  • Erősített vonali sztereó kimenet (Line Out),
  • Erősített vonali sztereó bemenet más audio eszközöktől (Line In),
  • 512 K, illetve 1 MB saját memória hullámformák tárolására (GUS DRAM),
  • 32 digitális csatorna,
  • GF1 hangprocesszor (Voice Sound Synthetizer),
  • hullámforma táblákkal megvalósított FM hangkeltés,
  • minden csatornán különálló hangerő és balansz,
  • állítható mintavételi frekvencia 44,1 kHz-ig,
  • 8- vagy 16 bites sztereó, illetve mono lejátszás,
  • szoftverből állítható IRQ vonal és DMA csatorna,
  • jumperrel állítható I/O báziscím,
  • a hangkártya minden egysége elérhető processzorral, ugyanakkor képes függetlenül dolgozni,
  • több pontos burkológörbe előállítása hardveres úton.

Itt most nem térünk ki részletesen a regiszterek ismertetésére, mivel elég sok van belőlük és véleményem szerint azok felépítése sokkal bonyolultabb a Sound Blaster esetén tapasztaltnál. Akit részletesebben érdekel a téma, bőven talál leírásokat az ezzel foglalkozó szakirodalomban. Inkább [4] alapján a konkrét programozást bemutató, a feladat szempontjából fontosabb Pascal unitot közlök:

{$G+}

unit gus;

interface uses DOS,CRT;

type

memptr = ^memory;

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

const

Hdg : array[0..15] of char=’0123456789ABCDEF’;

{ Hangkártya hardverbeállítások }

Base    : word = $250; { I/O báziscím }

GF1irq  : byte = 11;   { Megszakítási vonal }

MIDIirq : byte =  7;   { MIDI megszakítási vonal }

DMA_1   : byte =  3;   { GUS DRAM DMA csatorna }

DMA_2   : byte =  3;   { Felvétel DMA csatorna }

{ Frekvenciatáblázat 5 oktáv számára }

FreqTAB : Array[0..59] of word=(

$0038,$003B,$003E,$0042,$0046,$004A,$004F,$0053,$0058,$005E,$0063,$0069,

$0070,$0076,$007D,$0085,$008D,$0095,$009E,$00A7,$00B1,$00BC,$00C7,$00D3,

$00E0,$00ED,$00FB,$010A,$011A,$012B,$013D,$014F,$0163,$0179,$018F,$01A7,

$01C0,$01DB,$01F7,$0214,$0234,$0256,$027A,$029F,$02C7,$02F2,$031E,$034E,

$0380,$03B6,$03EE,$0429,$0469,$04AD,$04F4,$053F,$058F,$05E4,$063D,$069C);

{ Hangerőtáblázat a 128 lépéses lineáris szabályozáshoz }

VolTAB: Array[0..127] of word=(

$0000,$8ff0,$9ff0,$a800,$aff0,$b400,$b800,$bc00,

$bff0,$c200,$c400,$c600,$c800,$ca00,$cc00,$ce00,

$cff0,$d100,$d200,$d300,$d400,$d500,$d600,$d700,

$d800,$d900,$da00,$db00,$dc00,$dd00,$de00,$df00,

$dff0,$e080,$e100,$e170,$e200,$e280,$e300,$e370,

$e400,$e480,$e500,$e580,$e600,$e680,$e700,$e780,

$e800,$e880,$e900,$e990,$ea00,$ea80,$eb00,$eb80,

$ec00,$ec80,$ed00,$ed90,$ee00,$ee80,$ef00,$ef90,

$eff0,$f030,$f070,$f0c0,$f100,$f140,$f170,$f1c0,

$f200,$f240,$f280,$f2b0,$f300,$f340,$f380,$f3b0,

$f400,$f440,$f470,$f4c0,$f500,$f540,$f590,$f5c0,

$f600,$f650,$f690,$f6c0,$f700,$f740,$f790,$f7c0,

$f800,$f840,$f880,$f8b0,$f900,$f940,$f980,$f9b0,

$fa00,$fa50,$fa80,$fac0,$fb00,$fb40,$fb80,$fbc0,

$fc00,$fc40,$fc80,$fcd0,$fd00,$fd40,$fd80,$fdb0,

$fe00,$fe40,$fe80,$fec0,$ff00,$ff40,$ff80,$ffd0);

function  GetByte(Address:longint):byte;

procedure PutByte(Address:longint; value:byte);

procedure MoveSample(Adr:longint; var Src; count:word);

procedure GravisWait;

procedure GravisRESET;

function  GravisTEST:boolean;

procedure SetGF1Byte(Ch,Reg,Value:byte);

procedure SetGF1Word(Ch,Reg:byte; Value:word);

function  GetGF1Byte(Ch,Reg:byte):byte;

function  GetGF1Word(Ch,Reg:byte):word;

function  GravisFindBase:word;

function  GravisMemory:word;

procedure GravisVolume(ch:byte; volume:word);

procedure GravisBalance(ch:byte; balance:byte);

procedure GravisFreq(ch:byte; freq:word);

procedure PlayVoice(ch,mode:byte; b,s,e:longint);

procedure StopVoice(ch:byte);

function  VoicePos(ch:byte):longint;

function  Hex(b:byte):string;

function  HexW(w:word):string;

procedure Note(ch,n:byte);

procedure Volume(ch,n:byte);

procedure SetIRQservice(Irqr:pointer);

procedure ResetIRQservice;

implementation

function  GetByte(Address:longint):byte; assembler;

asm

mov   dx,Base

add   dx,$103 { Globálisregiszter-kiválasztás }

mov   al,$43

out   dx,al   { A DRAM alsó szó regiszter }

inc   dx

mov   ax,Address.word

out   dx,ax   { Kiírjuk a cím alsó 16 bitjét }

dec   dx

mov   al,$44  { A DRAM felső bájt regiszter }

out   dx,al

add   dx,$0002

mov   al,[Address+2].byte

and   al,00001111b

out   dx,al       { Kiírjuk a cím felső 4 bitjét }

add   dx,$0002    { DX: $3×7, DRAM read/write}

in    al,dx       { Beolvassuk az adatot az AL-be }

end;

GetByte: a GUS DRAM tetszőleges bájtjának lekérdezése.

procedure PutByte(Address:longint; value:byte); assembler;

asm

mov   dx,Base

add   dx,$103    { Globálisregiszter-kiválasztás }

mov   al,$43

out   dx,al       { A DRAM alsó szó regiszter }

inc   dx

mov   ax,Address.word

out   dx,ax       { Kiírjuk a cím alsó 16 bitjét }

dec   dx

mov   al,$44       { A DRAM felső bájt regiszter }

out   dx,al

add   dx,$0002

mov   al,[Address+2].byte

and   al,00001111b

out   dx,al        { Kiírjuk a cím felső 4 bitjét }

add   dx,$0002     { DX: $3×7, DRAM read/write}

mov   al,value     { AL: a bájt új értéke }

out   dx,al        { Beírjuk az értéket a DRAM-ba }

end;

PutByte: egy GUS DRAM bájt beírása.

procedure MoveSample(Adr:longint; var Src; count:word); assembler;

asm

mov   di,Adr.word;

mov   bl,[Adr+2].byte

and   bl,00001111b       { BL:DI=DRAM cím (első bájt)}

cld

les   si,Src

mov   ax,ES:[si]

mov   ES,ES:[si+2]

mov   si,ax              { ES:SI a forrásadatra mutat }

mov   cx,count           { CX a bájtok számát tartalmazza }

@putbytes:                 { Kiírás: PutByte-hoz hasonlóan }

mov   dx,Base

add   dx,$103

mov   al,$43

out   dx,al

inc   dx

mov   ax,di

out   dx,ax

dec   dx

mov   al,$44

out   dx,al

add   dx,$0002

mov   al,bl

out   dx,al

add   dx,$0002

segES lodsb           { Beolvasás a forrásadatokból }

out   dx,al           { Kiírás a célhelyre (GUS DRAM)}

add   di,$01

adc   bl,$00          { Növeljük a DRAM címet (BL:DI)}

loop  @putbytes       { CX darab bájtot írunk ki }

end;

MoveSample: egy legfeljebb 64K hosszú memóriaterület betöltése a GUS DRAM-ba.

procedure GravisWait;           assembler;

asm

push  dx       { Tároljuk a DX, AX regisztereket }

push  ax

mov   dx,$0000  { Egy semleges I/O báziscím }

in    al,dx

in    al,dx

in    al,dx

in    al,dx

in    al,dx

in    al,dx     { Várakozási ciklusok }

pop   ax        { AX, DX visszahozása }

pop   dx

end;

GravisWait: a Gravis csővezetékes rendszere miatt néhol várakozásra van szükség.

procedure GravisRESET;      assembler;

asm

mov   dx,Base

add   dx,$103      { Globálisregiszter-kiválasztás }

mov   al,$4C

out   dx,al        { $4C: Reset regiszter}

add   dx,$0002

mov   al,$00       { A GF1 inicializálása }

out   dx,al

call  GravisWait   { Várakozás }

sub   dx,$0002

mov   al,$4C

out   dx,al        { Ismét Reset regiszter}

add   dx,$0002

mov   al,$07       { Az eredeti normális működés visszaállítása}

out   dx,al

sub   dx,$0002

mov   al,$0E

out   dx,al        { 14 aktív csatorna }

mov   al,$CE

add   dx,$0002

out   dx,al

mov   dx,Base

mov   al,$0C

out   dx,al        { Bemenetek engedélyezése }

end;

GravisReset: a hangkártya alapállapotba állítása.

function  GravisTEST:boolean;

begin

asm

mov   dx,Base

add   dx,$103

mov   al,$4C       { A hangkártya inicializálásának megkísérlése }

out   dx,al

add   dx,$0002

xor   al,al

out   dx,al

call  GravisWait

call  GravisWait

sub   dx,$0002

mov   al,$4C

out   dx,al

add   dx,$0002

mov   al,$01

out   dx,al

end;

PutByte(256,$55);       { Néhány bájt kiírása a memóriába }

PutByte(512,$aa);

PutByte(000,$0f);

{ Ha ugyanazt olvassuk vissza amit kiírtunk, akkor van Gravis }

GravisTest:=(GetByte(256)=$55)and(GetByte(512)=$aa);

end;

GravisTest: a függvény igaz értékkel tér vissza, ha az adott báziscímen található Gravis hangkártya.

procedure SetGF1Byte(Ch,Reg,Value:byte);                assembler;

asm

mov   dx,Base

add   dx,$102           { Csatorna lapregiszter ($3×2)}

mov   al,&Ch            { Kiírjuk a csatornaszámot többszőr}

out   dx,al

out   dx,al

out   dx,al

inc   dx                { Regiszter-kiválasztás ($3×3)}

mov   al,Reg

out   dx,al             { Kiírjuk a regiszter számát }

add   dx,$0002          { Regiszter adatai I/O cím ($3×5)}

mov   al,Value

out   dx,al             { Kiírjuk a regiszter új értékét }

end;

procedure SetGF1Word(Ch,Reg:byte; Value:word);  assembler;

asm

mov   dx,Base

add   dx,$102       { Csatorna lapregiszter ($3×2)}

mov   al,&Ch        { Kiírjuk a csatornaszámot többszőr}

out   dx,al

out   dx,al

out   dx,al

inc   dx            { Regiszter-kiválasztás ($3×3)}

mov   al,Reg

out   dx,al         { Kiírjuk a regiszter számát }

inc   dx            { Regiszter word adat cím ($3×4)}

mov   ax,Value

out   dx,ax         { Kiírjuk a word adatot }

end;

SetGF1Byte és SetGF1Word: a GF1 processzor globális és csatorna specifikus regisztereinek közvetlen módosítása.

function  GetGF1Byte(Ch,Reg:byte):byte;                 assembler;

asm

mov   dx,Base

add   dx,$102      { Csatorna lapregiszter ($3×2)}

mov   al,&Ch       { Kiírjuk a csatornaszámot többszőr}

out   dx,al

out   dx,al

out   dx,al

inc   dx           { Regiszter-kiválasztás ($3×3)}

mov   al,Reg

out   dx,al        { Kiírjuk a regiszter számát }

add   dx,$0002     { Regiszter adata I/O cím ($3×5)}

in    al,dx        { Beolvassuk a regiszter értékeket }

end;

function  GetGF1Word(Ch,Reg:byte):word;                 assembler;

asm

mov   dx,Base

add   dx,$102

mov   al,&Ch

out   dx,al

out   dx,al

out   dx,al

inc   dx

mov   al,Reg

out   dx,al

inc   dx

in    ax,dx

end;

GetGF1byte és GetGF1word: a GF1 regisztereinek kiolvasása.

function  GravisFindBase:word;

var i:byte;

w:word;

begin

w:=0;

for i:=1 to 6 do

begin

Base:=$200 or i*16;

if GravisTEST then w:=Base;

end;

GravisFindBase:=w;

Base:=w;

end;

GravisFindBase: az I/O báziscím meghatározása.

function  GravisMemory:word;

var b:word;

begin

PutByte($3ffff,$01); {  256*1024-1 }

if GetByte($3ffff)=$01 then b:=256;

PutByte($7ffff,$02); {  512*1024-1 }

if GetByte($7ffff)=$02 then b:=512;

PutByte($bffff,$04); {  768*1024-1 }

if GetByte($bffff)=$04 then b:=768;

PutByte($fffff,$08); { 1024*1024-1 }

if GetByte($fffff)=$08 then b:=1024;

GravisMemory:=b;

end;

GravisMemory: a memória meghatározása.

procedure GravisVolume(ch:byte; volume:word);

begin

SetGF1Word(ch,$09,volume);

end;

GravisVolume: direkt hangerő szabályozás.

procedure GravisBalance(ch:byte; balance:byte);

begin

SetGF1Byte(ch,$0C,balance);

end;

GravisBalance: egy csatorna balanszértékének beállítása.

procedure GravisFreq(ch:byte; freq:word);

begin

SetGF1Word(ch,$01,freq);

end;

GravisFreq: a lejátszási frekvencia beállítása.

procedure PlayVoice(ch,mode:byte; b,s,e:longint);

begin

SetGF1Word(Ch,$03,(s and $7f) shl 9);

SetGF1Word(Ch,$02,(s shr 7));

SetGF1Word(Ch,$05,(e and $7f) shl 9);

SetGF1Word(Ch,$04,(e shr 7));

SetGF1Word(Ch,$0B,(b and $7f) shl 9);

SetGF1Word(Ch,$0A,(b shr 7));

SetGF1Byte(Ch,$00,mode);

{SetGF1Byte(Ch,$4C,$03);}

end;

PlayVoice: egy GUS DRAM-ba betöltött hangminta lejátszása, ch: a lejátszáshoz használt csatorna száma, mode: a lejátszás módja, b: a hangminta aktuális címe, s: a hangminta kezdőcíme, e: a hangminta végcíme.

procedure StopVoice(ch:byte);                   assembler;

asm

push  &ch.word

push  $0080        { Lekérdezzük a voice control regiszter értékét}

call  GetGF1Byte

and   al,$DF

or    al,$03       { Voice Stop bit=1 }

xor   ah,ah

push  ax

push  &ch.word

push  $0000

push  ax

call  SetGF1Byte     { Beállítjuk a Voice controlt }

call  GravisWait

pop   ax

push  &ch.word

push  $0000

push  ax

call  SetGF1Byte

end;

StopVoice: a hangminta lejátszásának leállítása.

function  Hex(b:byte):string;

begin

Hex:=Hdg[b shr 4]+Hdg[b and 15];

end;

function  HexW(w:word):string;

begin

HexW:=Hdg[w shr 12]+Hdg[(w shr 8  ) and 15]+

Hdg[(w shr 4) and 15]+Hdg[w and 15];

end;

Hex és Hexw: bájt és word hexadecimális érétkének meghatározása.

procedure Note(ch,n:byte);

begin

GravisFreq(ch,FreqTAB[n]);

end;

Note: egy adott hangjegyhez tartozó frekvencia beállítása.

procedure Volume(ch,n:byte);

begin

GravisVolume(ch,VolTAB[n and 127]);

end;

Volume: egy csatorna hangerejének lineáris beállítása.

procedure FirstIRQ; interrupt;

var a:byte;

begin

a:=port[Base+$0006];

a:=$00;

while a and $c0<>$c0 do

a:=getgf1byte($00,$8f);

port[$20]:=$20;

port[$a0]:=$20;

end;

FirstIRQ: a unit belső megszakítási rutinja, amely kívülről nem érhető el.

procedure SetIRQservice(Irqr:pointer);

var i:byte;

begin

SetGF1Byte($00,$4C,$03);      { Minden GF1 IRQ-t tiltunk       }

SetIntVec($73,@FirstIRQ);     { Beállítjuk az init IRQ-t       }

port[Base]:=$4c;              { Kiválasztjuk az IRQ regisztert }

port[Base+$000b]:=$05;        { IRQ11 , int 73h  00001000      }

port[$a1]:=port[$a1] and $F7; { Engedélyezzük a vonalat        }

for i:=0 to 31 do

begin                        { Minden csatorna letiltva       }

setgf1byte(i,$0d,$02);

setgf1byte(i,$00,$02);

end;

SetGF1byte($00,$41,$00);      { DRAM DMA IRQ tiltva      }

SetGF1byte($00,$49,$00);      { Felvétel IRQ tiltva      }

SetGF1Byte($00,$4C,$07);      { GF1 IRQ engedélyezve     }

delay(400);                   { várakozás                }

SetIntVec($73,IRQR);          { Az új megszakítási rutin }

end;

SetIRQsercvice: a megszakítás inicializálása és a megszakítási rutin címének beállítása.

procedure ResetIRQservice;

begin

port[$a1]:=port[$a1] or $08;  { Tiltjuk a vonalat }

SetGF1Byte($00,$4C,$03);      { GF1 IRQ letiltva  }

end;

ResetIRQservice: a GF1 megszakítás tiltása.

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

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s