📄 ac97.cpp
字号:
call si_DelayMilliSec ; delays cuz codecs are slow
pop edx
pop eax
}
}
///////////////////////////////////////////////////////////////////////////////////
//
// Set PCM Master Volume
//
///////////////////////////////////////////////////////////////////////////////////
VOID ac97_SetMasterVolume( USHORT dwVolume )
{
__asm
{
push eax
push edx
mov ax, dwVolume
mov dx, ds:[NAMBAR]
add dx, CODEC_MASTER_VOL_REG
out dx, ax
mov eax, 4
call si_DelayMilliSec ; delays cuz codecs are slow
pop edx
pop eax
}
}
///////////////////////////////////////////////////////////////////////////////////
//
// Set PCM Out Volume
//
///////////////////////////////////////////////////////////////////////////////////
VOID ac97_SetPcmOutVolume( USHORT dwVolume )
{
__asm
{
push eax
push edx
mov ax, dwVolume
mov dx, ds:[NAMBAR]
add dx, CODEC_PCM_OUT_REG
out dx, ax
mov eax, 4
call si_DelayMilliSec ; delays cuz codecs are slow
pop edx
pop eax
}
}
///////////////////////////////////////////////////////////////////////////////////
//
// register reset the DMA engine. This may cause a pop noise on the output
// lines when the device is reset. Prolly a better idea to mute output, then
// reset.
//
///////////////////////////////////////////////////////////////////////////////////
VOID ac97_ResetDMA( VOID )
{
__asm
{
push eax
push edx
mov dx, ds:[NABMBAR]
add dx, PO_CR_REG ; set pointer to Cntl reg
mov al, RR ; set reset
out dx, al ; self clearing bit
pop edx
pop eax
}
}
///////////////////////////////////////////////////////////////////////////////////
//
// The Last Valid Index register tells the DMA engine when to stop playing.
// input AL = index # to stop on
//
///////////////////////////////////////////////////////////////////////////////////
VOID ac97_SetLastValidIndex( UCHAR dwLastIndex )
{
__asm
{
push eax
push edx
mov al, dwLastIndex
mov dx, ds:[NABMBAR]
add dx, PO_LVI_REG
out dx, al
pop edx
pop eax
}
}
///////////////////////////////////////////////////////////////////////////////////
//
// Returns current index value
//
///////////////////////////////////////////////////////////////////////////////////
ULONG ac97_GetCurrentIndex( VOID )
{
ULONG val;
__asm
{
push edx
xor eax, eax
mov dx, ds:[NABMBAR]
add dx, PO_CIV_REG
in al, dx
pop edx
mov val, eax
}
return val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// set the last valid index to something other than what we're currently
// playing so that we never end.
//
// this routine just sets the last valid index to 1 less than the index
// that we're currently playing, thus keeping it in and endless loop
//
///////////////////////////////////////////////////////////////////////////////////
VOID ac97_SetNewIndex( VOID )
{
__asm
{
push eax
call ac97_GetCurrentIndex ; get CIV
dec al ; make new index <> current
and al, INDEX_MASK ; make sure new value is 0-31
push eax
call ac97_SetLastValidIndex ; write new value
pop eax
}
}
///////////////////////////////////////////////////////////////////////////////////
//
// examines the CIV and the LVI. When they're the same, we set LVI <> CIV
// that way, we never run out of buffers to play.
//
///////////////////////////////////////////////////////////////////////////////////
VOID ac97_UpdateLVI( VOID )
{
__asm
{
push eax
push edx
mov dx, ds:[NABMBAR]
add dx, PO_CIV_REG ; read Current Index Value
in ax, dx ; and Last Valid Index value together.
cmp al, ah ; are we playing the last index?
jnz not_last ; no, never mind
call ac97_SetNewIndex ; set it to something else.
not_last:
pop edx
pop eax
}
}
///////////////////////////////////////////////////////////////////////////////////
//
// create Buffer Descriptor List
//
// A buffer descriptor list is a list of pointers and control bits that the
// DMA engine uses to know where to get the .wav data and how to play it.
//
// I set it up to use only 2 buffers of .wav data, and whenever 1 buffer is
// playing, I refresh the other one with good data.
//
//
// For the control bits, you can specify that the DMA engine fire an interrupt
// after a buffer has been processed, but I poll the current index register
// to know when it's safe to update the other buffer.
//
// I set the BUP bit, which tells the DMA engine to just play 0's (silence)
// if it ever runs out of data to play. Good for safety.
//
///////////////////////////////////////////////////////////////////////////////////
VOID ac97_CreateBDL( VOID )
{
ULONG dwWavBuf1PhysAddr = (ULONG)MmGetPhysicalAddress(WAV_BUFFER1).QuadPart;
ULONG dwWavBuf2PhysAddr = (ULONG)MmGetPhysicalAddress(WAV_BUFFER2).QuadPart;
// setup 32 entries in BDL
for (int i = 0; i < 16; i++)
{
BDL_BUFFER[i*4+0] = dwWavBuf1PhysAddr;
BDL_BUFFER[i*4+1] = (WAV_BUFFER_SIZE / sizeof(USHORT)) | BUP | IOC; // set length to 32k samples. 1 sample is 16bits or 2bytes.
BDL_BUFFER[i*4+2] = dwWavBuf2PhysAddr;
BDL_BUFFER[i*4+3] = (WAV_BUFFER_SIZE / sizeof(USHORT)) | BUP | IOC; // set length to 32k samples. 1 sample is 16bits or 2bytes.
}
}
///////////////////////////////////////////////////////////////////////////////////
//
// tell the DMA engine where to find our list of Buffer Descriptors.
// this 32bit value is a flat mode memory offset (ie no segment:offset)
//
// write NABMBAR+10h with offset of buffer descriptor list
//
///////////////////////////////////////////////////////////////////////////////////
VOID ac97_SetBDLAddress( VOID )
{
ULONG dwPhysAddr = (ULONG)MmGetPhysicalAddress(BDL_BUFFER).QuadPart;
__asm
{
push eax
push edx
mov eax, dwPhysAddr
mov dx, ds:[NABMBAR]
add dx, PO_BDBAR_REG ; set pointer to BDL
out dx, eax ; write to AC97 controller
pop edx
pop eax
}
}
///////////////////////////////////////////////////////////////////////////////////
//
// Let's play some music.
//
///////////////////////////////////////////////////////////////////////////////////
VOID ac97_Play( VOID )
{
__asm
{
push eax
push edx
mov dx, ds:[NABMBAR]
add dx, PO_CR_REG ; DCM out control register
mov al, RPBM | LVBIE
out dx, al ; set start!
pop edx
pop eax
}
}
///////////////////////////////////////////////////////////////////////////////////
//
// Stop player
//
///////////////////////////////////////////////////////////////////////////////////
VOID ac97_Stop( VOID )
{
__asm
{
push eax
push edx
mov dx, ds:[NABMBAR] ; finshed with song, stop everything
add dx, PO_CR_REG ; DCM out control register
mov al, 0
out dx, al ; stop player
pop edx
pop eax
}
}
///////////////////////////////////////////////////////////////////////////////////
//
// while DMA engine is running, examine current index and wait until it hits 1
// as soon as it's 1, we need to refresh the data in wavbuffer1 with another
// 64k. Likewise when it's playing buffer 2, refresh buffer 1 and repeat.
//
///////////////////////////////////////////////////////////////////////////////////
VOID ac97_Loop( VOID )
{
while (1)
{
// wait while buffer1 is played than load it with new PCM data
KeStallExecutionProcessor(10);
while( (ac97_GetCurrentIndex() & BIT0) == 1 )
{
// ac97_UpdateLVI(); // set LVI != CIV
if (si_ReadFromKbdBuffer_char() == KBD_ESC) return;
}
if (mp3_GetData(WAV_BUFFER1, WAV_BUFFER_SIZE) == -1) mp3_RestartStream();
// wait while buffer2 is played than load it with new PCM data
KeStallExecutionProcessor(10);
while( (ac97_GetCurrentIndex() & BIT0) == 0 )
{
// ac97_UpdateLVI(); // set LVI != CIV
if (si_ReadFromKbdBuffer_char() == KBD_ESC) return;
}
if (mp3_GetData(WAV_BUFFER2, WAV_BUFFER_SIZE) == -1) mp3_RestartStream();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -