|
|
// Copyright (c) 1996-1999 Microsoft Corporation
// Mmx.cpp
// MMX Mix engines for Microsoft synth
/*
Variable useage.
Variable register pfSamplePos eax pfPitch ebx dwI ecx dwIncDelta edx (edx is sometimes a temporary register) dwPosition1 esi dwPostiion2 edi
vfRvolume and vfLvolume mm0 vfRVolume, vfLVolume mm2
mm4 - mm7 are temporary mmx registers. */
// Notes about calculation.
// Loop is unrolled once.
// *1 shifting volumne to 15 bit values to get rid of shifts and simplify code.
// This make the packed mulitply work better later since I keep the sound interpolated
// wave value at 16 bit signed value. For a PMULHW, this results in 15 bit results
// which is the same as the original code.
// *2 linear interpolation can be done very quickly with MMX by re-arranging the
// way that the interpolation is done. Here is code in C that shows the difference.
// Original C code
//lM1 = ((pcWave[dwPosition1 + 1] - pcWave[dwPosition1]) * dwFract1) >> 12;
//lM2 = ((pcWave[dwPosition2 + 1] - pcWave[dwPosition2]) * dwFract2) >> 12;
//lM1 += pcWave[dwPosition1];
//lM2 += pcWave[dwPosition2];
// Equivalent C Code that can be done with a pmadd
//lM1 = (pcWave[dwPosition1 + 1] * dwFract1 + pcWave[dwPosition1]*(0x1000-dwFract1)) >> 12;
//lM2 = (pcWave[dwPosition2 + 1] * dwFract2 + pcWave[dwPosition2]*(0x1000-dwFract2)) >> 12;
#ifdef DMSYNTH_MINIPORT
#include "common.h"
#else
#include "simple.h"
#include <mmsystem.h>
#include "synth.h"
#include "debug.h"
#endif
typedef unsigned __int64 QWORD;
#ifdef ORG_MONO_MIXER
DWORD CDigitalAudio::MixMono8X(short * pBuffer, DWORD dwLength, DWORD dwDeltaPeriod, VFRACT vfDeltaVolume, VFRACT vfLastVolume[], PFRACT pfDeltaPitch, PFRACT pfSampleLength, PFRACT pfLoopLength) { DWORD dwI; DWORD dwIncDelta = dwDeltaPeriod; char * pcWave = (char *) m_pnWave; PFRACT pfSamplePos = m_pfLastSample; VFRACT vfVolume = vfLastVolume[0]; PFRACT pfPitch = m_pfLastPitch; PFRACT pfPFract = pfPitch << 8; VFRACT vfVFract = vfVolume << 8; // Keep high res version around.
QWORD dwFractMASK = 0x000000000FFF0FFF; QWORD dwFractOne = 0x0000000010001000; QWORD wordmask = 0x0000FFFF0000FFFF; QWORD vfDeltaLandRVolume;
_asm{ // vfLVFract and vfRVFract are in mm0
//VFRACT vfLVFract = vfLVolume1 << 8; // Keep high res version around.
//VFRACT vfRVFract = vfRVolume1 << 8;
movd mm0, vfVolume movd mm7, vfVolume
// vfDeltaLVolume and vfDeltaRVolume are put in mm1 so that they can be stored in vfDeltaLandRVolume
movd mm1, vfDeltaVolume movd mm6, vfDeltaVolume
punpckldq mm1, mm6 // dwI = 0
mov ecx, 0 movq vfDeltaLandRVolume, mm1
movq mm1, dwFractOne movq mm4, dwFractMASK mov eax, pfSamplePos
punpckldq mm0, mm7 mov ebx, pfPitch
pslld mm0, 8 mov edx, dwIncDelta
movq mm2, mm0 // vfLVolume and vfRVolume in mm2
// need to be set before first pass.
// *1 I shift by 5 so that volume is a 15 bit value instead of a 12 bit value
psrld mm2, 5 //for (dwI = 0; dwI < dwLength; )
//{
mainloop: cmp ecx, dwLength jae done
cmp eax, pfSampleLength //if (pfSamplePos >= pfSampleLength)
jb NotPastEndOfSample1 //{
cmp pfLoopLength, 0 //if (!pfLoopLength)
je done // break;
sub eax, pfLoopLength // else pfSamplePos -= pfLoopLength;
NotPastEndOfSample1: //}
mov esi, eax // dwPosition1 = pfSamplePos;
add eax, ebx // pfSamplePos += pfPitch;
sub edx, 2 // dwIncDelta-=2;
jnz DontIncreaseValues1 //if (!dwIncDelta) {
// Since edx was use for dwIncDelta and now its zero, we can use if for a temporary
// for a bit. All code that TestLVol and TestRVol is doing is zeroing out the volume
// if it goes below zero.
paddd mm0, vfDeltaLandRVolume // vfVFract += vfDeltaVolume;
// vfVFract += vfDeltaVolume;
pxor mm5, mm5 // TestLVol = 0; TestRVol = 0;
mov edx, pfPFract // Temp = pfPFract;
pcmpgtd mm5, mm0 // if (TestLVol > vfLVFract) TestLVol = 0xffffffff;
// if (TestRVol > vfRVFract) TestRVol = 0xffffffff;
add edx, pfDeltaPitch // Temp += pfDeltaPitch;
pandn mm5, mm0 // TestLVol = vfLVFract & (~TestLVol);
// TestRVol = vfRVFract & (~TestRVol);
mov pfPFract, edx // pfPFract = Temp;
movq mm2, mm5 // vfLVolume = TestLVol;
// vfRVolume = TestRVol;
shr edx, 8 // Temp = Temp >> 8;
psrld mm2, 5 // vfLVolume = vfLVolume >> 5;
// vfRVolume = vfRVolume >> 5;
mov ebx, edx // pfPitch = Temp;
mov edx, dwDeltaPeriod //dwIncDelta = dwDeltaPeriod;
//}
DontIncreaseValues1:
movd mm6, esi // dwFract1 = dwPosition1;
movq mm5, mm1 // words in mm5 = 0, 0, 0x1000, 0x1000
shr esi, 12 // dwPosition1 = dwPosition1 >> 12;
inc ecx //dwI++;
// if ( dwI < dwLength) break;
cmp ecx, dwLength jae StoreOne //if (pfSamplePos >= pfSampleLength)
//{
cmp eax, pfSampleLength jb NotPastEndOfSample2
// Original if in C was not negated
//if (!pfLoopLength)
cmp pfLoopLength, 0 //break;
je StoreOne //else
//pfSamplePos -= pfLoopLength;
sub eax, pfLoopLength //}
NotPastEndOfSample2:
//shl esi, 1 // do not shift left since pcWave is array of chars
mov edi, eax // dwPosition2 = pfSamplePos;
add esi, pcWave // Put address of pcWave[dwPosition1] in esi
movd mm7, eax // dwFract2 = pfSamplePos;
shr edi, 12 // dwPosition2 = dwPosition2 >> 12;
punpcklwd mm6, mm7 // combine dwFract Values. Words in mm6 after unpack are
// 0, 0, dwFract2, dwFract1
pand mm6, mm4 // dwFract2 &= 0xfff; dwFract1 &= 0xfff;
movzx esi, word ptr[esi] //lLM1 = pcWave[dwPosition1];
movd mm3, esi
psubw mm5, mm6 // 0, 0, 0x1000 - dwFract2, 0x1000 - dwFract1
//shl edi, 1 //do not shift left since pcWave is array of chars
punpcklwd mm5, mm6 // dwFract2, 0x1000 - dwFract2, dwFract1, 0x1000 - dwFract1
add edi, pcWave // Put address of pcWave[dwPosition2] in edi
mov esi, ecx // Temp = dWI;
shl esi, 1 // Temp = Temp << 1;
movzx edi, word ptr[edi] //lLM2 = pcWave[dwPoisition2];
movd mm6, edi
pxor mm7, mm7 // zero out mm7 to make 8 bit into 16 bit
// low 4 bytes in mm3
punpcklwd mm3, mm6 // pcWave[dwPos2+1], pcWave[dwPos2], pcWave[dwPos1+1], pcWave[dwPos1]
add esi, pBuffer //
punpcklbw mm7, mm3 // low four bytes bytes in
// pcWave[dwPos2+1], pcWave[dwPos2], pcWave[dwPos1+1], pcWave[dwPos1]
pmaddwd mm7, mm5 // high dword = lM2 =
//(pcWave[dwPosition2 + 1] * dwFract2 + pcWave[dwPosition2]*(0x1000-dwFract2))
// low dword = lM1 =
//(pcWave[dwPosition1 + 1] * dwFract1 + pcWave[dwPosition1]*(0x1000-dwFract1))
movq mm3, mm2 // put left and right volume levels in mm3
add eax, ebx //pfSamplePos += pfPitch;
packssdw mm3, mm2 // words in mm7
// vfVolume, vfVolume, vfVolume, vfVolume
movd mm5, dword ptr[esi-2] // Load values from buffer
inc ecx // dwI++;
psrad mm7, 12 // shift back down to 16 bits.
packssdw mm7, mm4 // only need one word in mono case.
// low word are lm2 and lm1
// above multiplies and shifts are all done with this one pmul. Low two word are only
// interest in mono case
pmulhw mm3, mm7 // lLM1 *= vfVolume;
// lLM2 *= vfVolume;
paddsw mm5, mm3 // Add values to buffer with saturation
movd dword ptr[esi-2], mm5 // Store values back into buffer.
// }
jmp mainloop
// Need to write only one.
//if (dwI < dwLength)
//{
StoreOne: #if 1
// Linearly interpolate between points and store only one value.
// combine dwFract Values.
// Make mm7 zero for unpacking
//shl esi, 1 // do not shift left since pcWave is array of chars
add esi, pcWave // Put address of pcWave[dwPosition1] in esi
pxor mm7, mm7 //lLM1 = pcWave[dwPosition1];
movzx esi, word ptr[esi] // Doing AND that was not done for dwFract1 and dwFract2
pand mm6, mm4
// words in MMX register after operation is complete.
psubw mm5, mm6 // 0, 0, 0x1000 - 0, 0x1000 - dwFract1
punpcklwd mm5, mm6 // 0 , 0x1000 - 0, dwFract1, 0x1000 - dwFract1
// put values of pcWave into MMX registers. They are read into a regular register so
// that the routine does not read past the end of the buffer otherwise, it could read
// directly into the MMX registers.
// words in MMX registers
pxor mm7, mm7 // low four bytes
movd mm4, esi // 0, 0, pcWave[dwPos1+1], pcWave[dwPos1]
// 8 bytes after unpakc
punpcklbw mm7, mm4 // 0, 0, 0, 0, pcWave[dwPos1+1], 0, pcWave[dwPos1], 0
// *2 pmadd efficent code.
//lM2 = (pcWave[dwPosition2 + 1] * dwFract2 + pcWave[dwPosition2]*(0x1000-dwFract2)) >> 12;
//lM1 = (pcWave[dwPosition1 + 1] * dwFract1 + pcWave[dwPosition1]*(0x1000-dwFract1)) >> 12;
pmaddwd mm7, mm5// low dword = lM1 =
//(pcWave[dwPosition1 + 1] * dwFract1 + pcWave[dwPosition1]*(0x1000-dwFract1))
psrad mm7, 12 // shift back down to 16 bits
movq mm5, mm2 // move volume into mm5
/*
// Set lLM to be same as lM
lLM1 = lM1;
lLM1 *= vfLVolume1; lLM1 >>= 5; // Signal bumps up to 15 bits.
lM1 *= vfRVolume1; lM1 >>= 5;
// Set lLM to be same as lM
lLM2 = lM2;
lLM2 *= vfLVolume2; lLM2 >>= 5; // Signal bumps up to 15 bits.
lM2 *= vfRVolume2; lM2 >>= 5; */ // above multiplies and shifts are all done with this one pmul
pmulhw mm5, mm7 // calculate buffer location.
mov edi, ecx shl edi, 1 add edi, pBuffer
movd edx, mm5
//pBuffer[dwI+1] += (short) lM1;
add word ptr[edi-2], dx jno no_oflowr1 //pBuffer[dwI+1] = 0x7fff;
mov word ptr[edi-2], 0x7fff js no_oflowr1 //pBuffer[dwI+1] = (short) 0x8000;
mov word ptr[edi-2], 0x8000 no_oflowr1: //}
#endif
done:
mov edx, this // get address of class object
//vfLastVolume[0] = vfVolume;
//vfLastVolume[1] = vfVolume;
// need to shift volume back down to 12 bits before storing
psrld mm2, 3 #if 0
movd [edx]this.m_vfLastVolume[0], mm2 movd [edx]this.m_vfLastVolume[1], mm2 #endif
movd vfLastVolume[0], mm2 movd vfLastVolume[1], mm2 //m_pfLastPitch = pfPitch;
mov [edx]this.m_pfLastPitch, ebx //m_pfLastSample = pfSamplePos;
mov [edx]this.m_pfLastSample, eax // put value back into dwI to be returned. This could just be passed back in eax I think.
mov dwI, ecx emms } // ASM block
return (dwI); } #endif
DWORD CDigitalAudio::Mix8X(short * pBuffer, DWORD dwLength, DWORD dwDeltaPeriod, VFRACT vfDeltaLVolume, VFRACT vfDeltaRVolume, VFRACT vfLastVolume[], PFRACT pfDeltaPitch, PFRACT pfSampleLength, PFRACT pfLoopLength)
{ DWORD dwI; //DWORD dwPosition1, dwPosition2;
//long lM1, lLM1;
//long lM2, lLM2;
DWORD dwIncDelta = dwDeltaPeriod; //VFRACT dwFract1, dwFract2;
char * pcWave = (char *) m_pnWave; PFRACT pfSamplePos = m_pfLastSample; VFRACT vfLVolume = vfLastVolume[0]; VFRACT vfRVolume = vfLastVolume[1];
VFRACT vfLVolume2 = vfLastVolume[0]; VFRACT vfRVolume2 = vfLastVolume[1];
PFRACT pfPitch = m_pfLastPitch; PFRACT pfPFract = pfPitch << 8; dwLength <<= 1;
QWORD dwFractMASK = 0x000000000FFF0FFF; QWORD dwFractOne = 0x0000000010001000; QWORD wordmask = 0x0000FFFF0000FFFF; QWORD vfDeltaLandRVolume;
_asm{ // vfLVFract and vfRVFract are in mm0
//VFRACT vfLVFract = vfLVolume1 << 8; // Keep high res version around.
//VFRACT vfRVFract = vfRVolume1 << 8;
movd mm0, vfLVolume movd mm7, vfRVolume
// vfDeltaLVolume and vfDeltaRVolume are put in mm1 so that they can be stored in vfDeltaLandRVolume
movd mm1, vfDeltaLVolume movd mm6, vfDeltaRVolume
punpckldq mm1, mm6 // dwI = 0
mov ecx, 0 movq vfDeltaLandRVolume, mm1
movq mm1, dwFractOne movq mm4, dwFractMASK mov eax, pfSamplePos
punpckldq mm0, mm7 mov ebx, pfPitch
pslld mm0, 8 mov edx, dwIncDelta
movq mm2, mm0 // vfLVolume and vfRVolume in mm2
// need to be set before first pass.
// *1 I shift by 5 so that volume is a 15 bit value instead of a 12 bit value
psrld mm2, 5 //for (dwI = 0; dwI < dwLength; )
//{
mainloop: cmp ecx, dwLength jae done
cmp eax, pfSampleLength //if (pfSamplePos >= pfSampleLength)
jb NotPastEndOfSample1 //{
cmp pfLoopLength, 0 //if (!pfLoopLength)
je done // break;
sub eax, pfLoopLength // else pfSamplePos -= pfLoopLength;
NotPastEndOfSample1: //}
mov esi, eax // dwPosition1 = pfSamplePos;
add eax, ebx // pfSamplePos += pfPitch;
sub edx, 2 // dwIncDelta-=2;
jnz DontIncreaseValues1 //if (!dwIncDelta) {
// Since edx was use for dwIncDelta and now its zero, we can use if for a temporary
// for a bit. All code that TestLVol and TestRVol is doing is zeroing out the volume
// if it goes below zero.
paddd mm0, vfDeltaLandRVolume // vfLVFract += vfDeltaLVolume;
// vfRVFract += vfDeltaRVolume;
pxor mm5, mm5 // TestLVol = 0; TestRVol = 0;
mov edx, pfPFract // Temp = pfPFract;
pcmpgtd mm5, mm0 // if (TestLVol > vfLVFract) TestLVol = 0xffffffff;
// if (TestRVol > vfRVFract) TestRVol = 0xffffffff;
add edx, pfDeltaPitch // Temp += pfDeltaPitch;
pandn mm5, mm0 // TestLVol = vfLVFract & (~TestLVol);
// TestRVol = vfRVFract & (~TestRVol);
mov pfPFract, edx // pfPFract = Temp;
movq mm2, mm5 // vfLVolume = TestLVol;
// vfRVolume = TestRVol;
shr edx, 8 // Temp = Temp >> 8;
psrld mm2, 5 // vfLVolume = vfLVolume >> 5;
// vfRVolume = vfRVolume >> 5;
mov ebx, edx // pfPitch = Temp;
mov edx, dwDeltaPeriod //dwIncDelta = dwDeltaPeriod;
//}
DontIncreaseValues1:
movd mm6, esi // dwFract1 = dwPosition1;
movq mm5, mm1 // words in mm5 = 0, 0, 0x1000, 0x1000
shr esi, 12 // dwPosition1 = dwPosition1 >> 12;
add ecx, 2 //dwI += 2;
// if ( dwI < dwLength) break;
cmp ecx, dwLength jae StoreOne //if (pfSamplePos >= pfSampleLength)
//{
cmp eax, pfSampleLength jb NotPastEndOfSample2
// Original if in C was not negated
//if (!pfLoopLength)
cmp pfLoopLength, 0 //break;
je StoreOne //else
//pfSamplePos -= pfLoopLength;
sub eax, pfLoopLength //}
NotPastEndOfSample2:
//shl esi, 1 // do not shift left since pcWave is array of chars
mov edi, eax // dwPosition2 = pfSamplePos;
add esi, pcWave // Put address of pcWave[dwPosition1] in esi
movd mm7, eax // dwFract2 = pfSamplePos;
shr edi, 12 // dwPosition2 = dwPosition2 >> 12;
punpcklwd mm6, mm7 // combine dwFract Values. Words in mm6 after unpack are
// 0, 0, dwFract2, dwFract1
pand mm6, mm4 // dwFract2 &= 0xfff; dwFract1 &= 0xfff;
movzx esi, word ptr[esi] //lLM1 = pcWave[dwPosition1];
movd mm3, esi
psubw mm5, mm6 // 0, 0, 0x1000 - dwFract2, 0x1000 - dwFract1
//shl edi, 1 // do not shift left since pcWave is array of chars
punpcklwd mm5, mm6 // dwFract2, 0x1000 - dwFract2, dwFract1, 0x1000 - dwFract1
add edi, pcWave // Put address of pcWave[dwPosition2] in edi
mov esi, ecx // Temp = dWI;
shl esi, 1 // Temp = Temp << 1;
movzx edi, word ptr[edi] //lLM2 = pcWave[dwPosition2];
movd mm6, edi pxor mm7, mm7 // zero out mm7 to make 8 bit into 16 bit
// low 4 bytes bytes in mm3
punpcklwd mm3, mm6 // pcWave[dwPos2+1], pcWave[dwPos2], pcWave[dwPos1+1], pcWave[dwPos1]
add esi, pBuffer //
punpcklbw mm7, mm3 // bytes in mm7
// pcWave[dwPos2+1], 0, pcWave[dwPos2], 0, pcWave[dwPos1+1], pcWave[dwPos1], 0
pmaddwd mm7, mm5 // high dword = lM2 =
//(pcWave[dwPosition2 + 1] * dwFract2 + pcWave[dwPosition2]*(0x1000-dwFract2))
// low dword = lM1 =
//(pcWave[dwPosition1 + 1] * dwFract1 + pcWave[dwPosition1]*(0x1000-dwFract1))
movq mm3, mm2 // put left and right volume levels in mm3
add eax, ebx //pfSamplePos += pfPitch;
packssdw mm3, mm2 // words in mm3
// vfRVolume2, vfLVolume2, vfRVolume1, vfLVolume1
movq mm5, qword ptr[esi-4] // Load values from buffer
add ecx, 2 // dwI += 2;
psrad mm7, 12 // shift back down to 16 bits.
pand mm7, wordmask // combine results to get ready to multiply by left and right
movq mm6, mm7 // volume levels.
pslld mm6, 16 //
por mm7, mm6 // words in mm7
// lM2, lM2, lM1, lM1
// above multiplies and shifts are all done with this one pmul
pmulhw mm3, mm7 // lLM1 *= vfLVolume;
// lM1 *= vfRVolume;
// lLM2 *= vfLVolume;
// lM2 *= vfRVolume;
paddsw mm5, mm3 // Add values to buffer with saturation
movq qword ptr[esi-4], mm5 // Store values back into buffer.
// }
jmp mainloop
// Need to write only one.
//if (dwI < dwLength)
//{
StoreOne: #if 1
// Linearly interpolate between points and store only one value.
// combine dwFract Values.
// Make mm7 zero for unpacking
//shl esi, 1 // do not shift left since pcWave is array of chars
add esi, pcWave // Put address of pcWave[dwPosition1] in esi
pxor mm7, mm7 //lLM1 = pcWave[dwPosition1];
movzx esi, word ptr[esi] // Doing AND that was not done for dwFract1 and dwFract2
pand mm6, mm4
// words in MMX register after operation is complete.
psubw mm5, mm6 // 0, 0, 0x1000 - 0, 0x1000 - dwFract1
punpcklwd mm5, mm6 // 0 , 0x1000 - 0, dwFract1, 0x1000 - dwFract1
// put values of pcWave into MMX registers. They are read into a regular register so
// that the routine does not read past the end of the buffer otherwise, it could read
// directly into the MMX registers.
pxor mm7, mm7 // byte in MMX registers
movd mm4, esi // 0, 0, pcWave[dwPos1+1], pcWave[dwPos1]
punpcklbw mm7, mm4 // 0, 0, 0, 0, pcWave[dwPos1+1], 0, pcWave[dwPos1], 0
// *2 pmadd efficent code.
//lM2 = (pcWave[dwPosition2 + 1] * dwFract2 + pcWave[dwPosition2]*(0x1000-dwFract2)) >> 12;
//lM1 = (pcWave[dwPosition1 + 1] * dwFract1 + pcWave[dwPosition1]*(0x1000-dwFract1)) >> 12;
pmaddwd mm7, mm5// low dword = lM1 =
//(pcWave[dwPosition1 + 1] * dwFract1 + pcWave[dwPosition1]*(0x1000-dwFract1))
psrad mm7, 12 // shift back down to 16 bits
pand mm7, wordmask // combine results to get ready to multiply by left and right
movq mm6, mm7 // volume levels.
pslld mm6, 16 //
por mm7, mm6 // words in mm7
// lM2, lM2, lM1, lM1
pxor mm6, mm6
movq mm5, mm2 // move volume1 into mm5
// use pack to get 4 volume values together for multiplication.
packssdw mm5, mm6 // words in mm7
// 0, 0, vfRVolume1, vfLVolume1
/*
// Set lLM to be same as lM
lLM1 = lM1;
lLM1 *= vfLVolume1; lLM1 >>= 5; // Signal bumps up to 15 bits.
lM1 *= vfRVolume1; lM1 >>= 5;
// Set lLM to be same as lM
lLM2 = lM2;
lLM2 *= vfLVolume2; lLM2 >>= 5; // Signal bumps up to 15 bits.
lM2 *= vfRVolume2; lM2 >>= 5; */ // above multiplies and shifts are all done with this one pmul
pmulhw mm5, mm7 // calculate buffer location.
mov edi, ecx shl edi, 1 add edi, pBuffer
/*
add word ptr[edi-4], si jno no_oflowl1 // pBuffer[dwI] = 0x7fff;
mov word ptr[edi-4], 0x7fff js no_oflowl1 //pBuffer[dwI] = (short) 0x8000;
mov word ptr[edi-4], 0x8000 no_oflowl1: //pBuffer[dwI+1] += (short) lM1;
add word ptr[edi-2], dx jno no_oflowr1 //pBuffer[dwI+1] = 0x7fff;
mov word ptr[edi-2], 0x7fff js no_oflowr1 //pBuffer[dwI+1] = (short) 0x8000;
mov word ptr[edi-2], 0x8000 no_oflowr1: */ movd mm7, dword ptr[edi-4] paddsw mm7, mm5 movd dword ptr[edi-4], mm7 //}
#endif
done:
mov edx, this // get address of class object
//vfLastVolume[0] = vfLVolume;
//vfLastVolume[1] = vfRVolume;
// need to shift volume back down to 12 bits before storing
#if 0
psrld mm2, 3 movd [edx]this.m_vfLastVolume[0], mm2 psrlq mm2, 32 movd [edx]this.m_vfLastVolume[1], mm2 #endif
psrld mm2, 3 movd vfLastVolume[0], mm2 psrlq mm2, 32 movd vfLastVolume[1], mm2 //m_pfLastPitch = pfPitch;
mov [edx]this.m_pfLastPitch, ebx //m_pfLastSample = pfSamplePos;
mov [edx]this.m_pfLastSample, eax // put value back into dwI to be returned. This could just be passed back in eax I think.
mov dwI, ecx emms } // ASM block
return (dwI >> 1); }
#ifdef ORG_MONO_MIXER
DWORD CDigitalAudio::MixMono16X(short * pBuffer, DWORD dwLength, DWORD dwDeltaPeriod, VFRACT vfDeltaVolume, VFRACT vfLastVolume[], PFRACT pfDeltaPitch, PFRACT pfSampleLength, PFRACT pfLoopLength)
{ DWORD dwI; DWORD dwIncDelta = dwDeltaPeriod; short * pcWave = (short*) m_pnWave; PFRACT pfSamplePos = m_pfLastSample; VFRACT vfVolume = vfLastVolume[0]; PFRACT pfPitch = m_pfLastPitch; PFRACT pfPFract = pfPitch << 8; VFRACT vfVFract = vfVolume << 8; // Keep high res version around.
QWORD dwFractMASK = 0x000000000FFF0FFF; QWORD dwFractOne = 0x0000000010001000; QWORD wordmask = 0x0000FFFF0000FFFF; QWORD vfDeltaLandRVolume;
_asm{ // vfLVFract and vfRVFract are in mm0
//VFRACT vfLVFract = vfLVolume1 << 8; // Keep high res version around.
//VFRACT vfRVFract = vfRVolume1 << 8;
movd mm0, vfVolume movd mm7, vfVolume
// vfDeltaLVolume and vfDeltaRVolume are put in mm1 so that they can be stored in vfDeltaLandRVolume
movd mm1, vfDeltaVolume movd mm6, vfDeltaVolume
punpckldq mm1, mm6 // dwI = 0
mov ecx, 0 movq vfDeltaLandRVolume, mm1
movq mm1, dwFractOne movq mm4, dwFractMASK mov eax, pfSamplePos
punpckldq mm0, mm7 mov ebx, pfPitch
pslld mm0, 8 mov edx, dwIncDelta
movq mm2, mm0 // vfLVolume and vfRVolume in mm2
// need to be set before first pass.
// *1 I shift by 5 so that volume is a 15 bit value instead of a 12 bit value
psrld mm2, 5 //for (dwI = 0; dwI < dwLength; )
//{
mainloop: cmp ecx, dwLength jae done
cmp eax, pfSampleLength //if (pfSamplePos >= pfSampleLength)
jb NotPastEndOfSample1 //{
cmp pfLoopLength, 0 //if (!pfLoopLength)
je done // break;
sub eax, pfLoopLength // else pfSamplePos -= pfLoopLength;
NotPastEndOfSample1: //}
mov esi, eax // dwPosition1 = pfSamplePos;
add eax, ebx // pfSamplePos += pfPitch;
sub edx, 2 // dwIncDelta-=2;
jnz DontIncreaseValues1 //if (!dwIncDelta) {
// Since edx was use for dwIncDelta and now its zero, we can use if for a temporary
// for a bit. All code that TestLVol and TestRVol is doing is zeroing out the volume
// if it goes below zero.
paddd mm0, vfDeltaLandRVolume // vfVFract += vfDeltaVolume;
// vfVFract += vfDeltaVolume;
pxor mm5, mm5 // TestLVol = 0; TestRVol = 0;
mov edx, pfPFract // Temp = pfPFract;
pcmpgtd mm5, mm0 // if (TestLVol > vfLVFract) TestLVol = 0xffffffff;
// if (TestRVol > vfRVFract) TestRVol = 0xffffffff;
add edx, pfDeltaPitch // Temp += pfDeltaPitch;
pandn mm5, mm0 // TestLVol = vfLVFract & (~TestLVol);
// TestRVol = vfRVFract & (~TestRVol);
mov pfPFract, edx // pfPFract = Temp;
movq mm2, mm5 // vfLVolume = TestLVol;
// vfRVolume = TestRVol;
shr edx, 8 // Temp = Temp >> 8;
psrld mm2, 5 // vfLVolume = vfLVolume >> 5;
// vfRVolume = vfRVolume >> 5;
mov ebx, edx // pfPitch = Temp;
mov edx, dwDeltaPeriod //dwIncDelta = dwDeltaPeriod;
//}
DontIncreaseValues1:
movd mm6, esi // dwFract1 = dwPosition1;
movq mm5, mm1 // words in mm5 = 0, 0, 0x1000, 0x1000
shr esi, 12 // dwPosition1 = dwPosition1 >> 12;
inc ecx //dwI++;
// if ( dwI < dwLength) break;
cmp ecx, dwLength jae StoreOne //if (pfSamplePos >= pfSampleLength)
//{
cmp eax, pfSampleLength jb NotPastEndOfSample2
// Original if in C was not negated
//if (!pfLoopLength)
cmp pfLoopLength, 0 //break;
je StoreOne //else
//pfSamplePos -= pfLoopLength;
sub eax, pfLoopLength //}
NotPastEndOfSample2:
shl esi, 1 // shift left since pcWave is array of shorts
mov edi, eax // dwPosition2 = pfSamplePos;
add esi, pcWave // Put address of pcWave[dwPosition1] in esi
movd mm7, eax // dwFract2 = pfSamplePos;
shr edi, 12 // dwPosition2 = dwPosition2 >> 12;
punpcklwd mm6, mm7 // combine dwFract Values. Words in mm6 after unpack are
// 0, 0, dwFract2, dwFract1
pand mm6, mm4 // dwFract2 &= 0xfff; dwFract1 &= 0xfff;
movd mm7, dword ptr[esi] //lLM1 = pcWave[dwPosition1];
psubw mm5, mm6 // 0, 0, 0x1000 - dwFract2, 0x1000 - dwFract1
shl edi, 1 // shift left since pcWave is array of shorts
punpcklwd mm5, mm6 // dwFract2, 0x1000 - dwFract2, dwFract1, 0x1000 - dwFract1
add edi, pcWave // Put address of pcWave[dwPosition2] in edi
mov esi, ecx // Temp = dWI;
shl esi, 1 // Temp = Temp << 1;
movq mm3, mm2 // put left and right volume levels in mm3
movd mm6, dword ptr[edi] //lLM2 = pcWave[dwPosition2];
packssdw mm3, mm2 // words in mm7
// vfRVolume2, vfLVolume2, vfRVolume1, vfLVolume1
add esi, pBuffer //
punpckldq mm7, mm6 // low four bytes bytes in
// pcWave[dwPos2+1], pcWave[dwPos2], pcWave[dwPos1+1], pcWave[dwPos1]
pmaddwd mm7, mm5 // high dword = lM2 =
//(pcWave[dwPosition2 + 1] * dwFract2 + pcWave[dwPosition2]*(0x1000-dwFract2))
// low dword = lM1 =
//(pcWave[dwPosition1 + 1] * dwFract1 + pcWave[dwPosition1]*(0x1000-dwFract1))
add eax, ebx //pfSamplePos += pfPitch;
movd mm5, dword ptr[esi-2] // Load values from buffer
inc ecx // dwI++;
psrad mm7, 12 // shift back down to 16 bits.
packssdw mm7, mm4 // only need one word in mono case.
// low word are lm2 and lm1
// above multiplies and shifts are all done with this one pmul. Low two word are only
// interest in mono case
pmulhw mm3, mm7 // lLM1 *= vfVolume;
// lLM2 *= vfVolume;
paddsw mm5, mm3 // Add values to buffer with saturation
movd dword ptr[esi-2], mm5 // Store values back into buffer.
// }
jmp mainloop
// Need to write only one.
//if (dwI < dwLength)
//{
StoreOne: #if 1
// Linearly interpolate between points and store only one value.
// combine dwFract Values.
// Make mm7 zero for unpacking
shl esi, 1 // shift left since pcWave is array of shorts
add esi, pcWave // Put address of pcWave[dwPosition1] in esi
pxor mm7, mm7 //lLM1 = pcWave[dwPosition1];
mov esi, dword ptr[esi] // Doing AND that was not done for dwFract1 and dwFract2
pand mm6, mm4
// words in MMX register after operation is complete.
psubw mm5, mm6 // 0, 0, 0x1000 - 0, 0x1000 - dwFract1
punpcklwd mm5, mm6 // 0 , 0x1000 - 0, dwFract1, 0x1000 - dwFract1
// put values of pcWave into MMX registers. They are read into a regular register so
// that the routine does not read past the end of the buffer otherwise, it could read
// directly into the MMX registers.
// words in MMX registers
movd mm7, esi // 0, 0, pcWave[dwPos1+1], pcWave[dwPos1]
// *2 pmadd efficent code.
//lM2 = (pcWave[dwPosition2 + 1] * dwFract2 + pcWave[dwPosition2]*(0x1000-dwFract2)) >> 12;
//lM1 = (pcWave[dwPosition1 + 1] * dwFract1 + pcWave[dwPosition1]*(0x1000-dwFract1)) >> 12;
pmaddwd mm7, mm5// low dword = lM1 =
//(pcWave[dwPosition1 + 1] * dwFract1 + pcWave[dwPosition1]*(0x1000-dwFract1))
psrad mm7, 12 // shift back down to 16 bits
movq mm5, mm2 // move volume into mm5
/*
// Set lLM to be same as lM
lLM1 = lM1;
lLM1 *= vfLVolume1; lLM1 >>= 5; // Signal bumps up to 15 bits.
lM1 *= vfRVolume1; lM1 >>= 5;
// Set lLM to be same as lM
lLM2 = lM2;
lLM2 *= vfLVolume2; lLM2 >>= 5; // Signal bumps up to 15 bits.
lM2 *= vfRVolume2; lM2 >>= 5; */ // above multiplies and shifts are all done with this one pmul
pmulhw mm5, mm7 // calculate buffer location.
mov edi, ecx shl edi, 1 add edi, pBuffer
movd edx, mm5
//pBuffer[dwI+1] += (short) lM1;
add word ptr[edi-2], dx jno no_oflowr1 //pBuffer[dwI+1] = 0x7fff;
mov word ptr[edi-2], 0x7fff js no_oflowr1 //pBuffer[dwI+1] = (short) 0x8000;
mov word ptr[edi-2], 0x8000 no_oflowr1: //}
#endif
done:
mov edx, this // get address of class object
//vfLastVolume[0] = vfVolume;
//vfLastVolume[1] = vfVolume;
// need to shift volume back down to 12 bits before storing
psrld mm2, 3 #if 0
movd [edx]this.m_vfLastVolume[0], mm2 movd [edx]this.m_vfLastVolume[1], mm2 #endif
movd vfLastVolume[0], mm2 movd vfLastVolume[1], mm2 //m_pfLastPitch = pfPitch;
mov [edx]this.m_pfLastPitch, ebx //m_pfLastSample = pfSamplePos;
mov [edx]this.m_pfLastSample, eax // put value back into dwI to be returned. This could just be passed back in eax I think.
mov dwI, ecx emms } // ASM block
return (dwI); } #endif
DWORD CDigitalAudio::Mix16X(short * pBuffer, DWORD dwLength, DWORD dwDeltaPeriod, VFRACT vfDeltaLVolume, VFRACT vfDeltaRVolume, VFRACT vfLastVolume[], PFRACT pfDeltaPitch, PFRACT pfSampleLength, PFRACT pfLoopLength) { DWORD dwI; //DWORD dwPosition1, dwPosition2;
//long lM1, lLM1;
//long lM2, lLM2;
DWORD dwIncDelta = dwDeltaPeriod; //VFRACT dwFract1, dwFract2;
short * pcWave = (short *) m_pnWave; PFRACT pfSamplePos = m_pfLastSample; VFRACT vfLVolume = vfLastVolume[0]; VFRACT vfRVolume = vfLastVolume[1];
VFRACT vfLVolume2 = vfLastVolume[0]; VFRACT vfRVolume2 = vfLastVolume[1];
PFRACT pfPitch = m_pfLastPitch; PFRACT pfPFract = pfPitch << 8; dwLength <<= 1;
QWORD dwFractMASK = 0x000000000FFF0FFF; QWORD dwFractOne = 0x0000000010001000; QWORD wordmask = 0x0000FFFF0000FFFF; QWORD vfDeltaLandRVolume;
_asm{ // vfLVFract and vfRVFract are in mm0
//VFRACT vfLVFract = vfLVolume1 << 8; // Keep high res version around.
//VFRACT vfRVFract = vfRVolume1 << 8;
movd mm0, vfLVolume movd mm7, vfRVolume
// vfDeltaLVolume and vfDeltaRVolume are put in mm1 so that they can be stored in vfDeltaLandRVolume
movd mm1, vfDeltaLVolume movd mm6, vfDeltaRVolume
punpckldq mm1, mm6 // dwI = 0
mov ecx, 0 movq vfDeltaLandRVolume, mm1
movq mm1, dwFractOne movq mm4, dwFractMASK mov eax, pfSamplePos
punpckldq mm0, mm7 mov ebx, pfPitch
pslld mm0, 8 mov edx, dwIncDelta
movq mm2, mm0 // vfLVolume and vfRVolume in mm2
// need to be set before first pass.
// *1 I shift by 5 so that volume is a 15 bit value instead of a 12 bit value
psrld mm2, 5 //for (dwI = 0; dwI < dwLength; )
//{
mainloop: cmp ecx, dwLength jae done
cmp eax, pfSampleLength //if (pfSamplePos >= pfSampleLength)
jb NotPastEndOfSample1 //{
cmp pfLoopLength, 0 //if (!pfLoopLength)
je done // break;
sub eax, pfLoopLength // else pfSamplePos -= pfLoopLength;
NotPastEndOfSample1: //}
mov esi, eax // dwPosition1 = pfSamplePos;
add eax, ebx // pfSamplePos += pfPitch;
sub edx, 2 // dwIncDelta-=2;
jnz DontIncreaseValues1 //if (!dwIncDelta) {
// Since edx was use for dwIncDelta and now its zero, we can use if for a temporary
// for a bit. All code that TestLVol and TestRVol is doing is zeroing out the volume
// if it goes below zero.
paddd mm0, vfDeltaLandRVolume // vfLVFract += vfDeltaLVolume;
// vfRVFract += vfDeltaRVolume;
pxor mm5, mm5 // TestLVol = 0; TestRVol = 0;
mov edx, pfPFract // Temp = pfPFract;
pcmpgtd mm5, mm0 // if (TestLVol > vfLVFract) TestLVol = 0xffffffff;
// if (TestRVol > vfRVFract) TestRVol = 0xffffffff;
add edx, pfDeltaPitch // Temp += pfDeltaPitch;
pandn mm5, mm0 // TestLVol = vfLVFract & (~TestLVol);
// TestRVol = vfRVFract & (~TestRVol);
mov pfPFract, edx // pfPFract = Temp;
movq mm2, mm5 // vfLVolume = TestLVol;
// vfRVolume = TestRVol;
shr edx, 8 // Temp = Temp >> 8;
psrld mm2, 5 // vfLVolume = vfLVolume >> 5;
// vfRVolume = vfRVolume >> 5;
mov ebx, edx // pfPitch = Temp;
mov edx, dwDeltaPeriod //dwIncDelta = dwDeltaPeriod;
//}
DontIncreaseValues1:
movd mm6, esi // dwFract1 = dwPosition1;
movq mm5, mm1 // words in mm5 = 0, 0, 0x1000, 0x1000
shr esi, 12 // dwPosition1 = dwPosition1 >> 12;
add ecx, 2 //dwI += 2;
// if ( dwI < dwLength) break;
cmp ecx, dwLength jae StoreOne //if (pfSamplePos >= pfSampleLength)
//{
cmp eax, pfSampleLength jb NotPastEndOfSample2
// Original if in C was not negated
//if (!pfLoopLength)
cmp pfLoopLength, 0 //break;
je StoreOne //else
//pfSamplePos -= pfLoopLength;
sub eax, pfLoopLength //}
NotPastEndOfSample2:
shl esi, 1 // shift left since pcWave is array of shorts
mov edi, eax // dwPosition2 = pfSamplePos;
add esi, pcWave // Put address of pcWave[dwPosition1] in esi
movd mm7, eax // dwFract2 = pfSamplePos;
shr edi, 12 // dwPosition2 = dwPosition2 >> 12;
punpcklwd mm6, mm7 // combine dwFract Values. Words in mm6 after unpack are
// 0, 0, dwFract2, dwFract1
pand mm6, mm4 // dwFract2 &= 0xfff; dwFract1 &= 0xfff;
movd mm7, dword ptr[esi] //lLM1 = pcWave[dwPosition1];
psubw mm5, mm6 // 0, 0, 0x1000 - dwFract2, 0x1000 - dwFract1
shl edi, 1 // shift left since pcWave is array of shorts
punpcklwd mm5, mm6 // dwFract2, 0x1000 - dwFract2, dwFract1, 0x1000 - dwFract1
add edi, pcWave // Put address of pcWave[dwPosition2] in edi
mov esi, ecx // Temp = dWI;
shl esi, 1 // Temp = Temp << 1;
movq mm3, mm2 // put left and right volume levels in mm3
movd mm6, dword ptr[edi] //lLM2 = pcWave[dwPosition2];
packssdw mm3, mm2 // words in mm7
// vfRVolume2, vfLVolume2, vfRVolume1, vfLVolume1
add esi, pBuffer //
punpckldq mm7, mm6 // low four bytes bytes in
// pcWave[dwPos2+1], pcWave[dwPos2], pcWave[dwPos1+1], pcWave[dwPos1]
pmaddwd mm7, mm5 // high dword = lM2 =
//(pcWave[dwPosition2 + 1] * dwFract2 + pcWave[dwPosition2]*(0x1000-dwFract2))
// low dword = lM1 =
//(pcWave[dwPosition1 + 1] * dwFract1 + pcWave[dwPosition1]*(0x1000-dwFract1))
add eax, ebx //pfSamplePos += pfPitch;
movq mm5, qword ptr[esi-4] // Load values from buffer
add ecx, 2 // dwI += 2;
psrad mm7, 12 // shift back down to 16 bits.
pand mm7, wordmask // combine results to get ready to multiply by left and right
movq mm6, mm7 // volume levels.
pslld mm6, 16 //
por mm7, mm6 // words in mm7
// lM2, lM2, lM1, lM1
// above multiplies and shifts are all done with this one pmul
pmulhw mm3, mm7 // lLM1 *= vfLVolume;
// lM1 *= vfRVolume;
// lLM2 *= vfLVolume;
// lM2 *= vfRVolume;
paddsw mm5, mm3 // Add values to buffer with saturation
movq qword ptr[esi-4], mm5 // Store values back into buffer.
// }
jmp mainloop
// Need to write only one.
//if (dwI < dwLength)
//{
StoreOne: #if 1
// Linearly interpolate between points and store only one value.
// combine dwFract Values.
// Make mm7 zero for unpacking
shl esi, 1 // shift left since pcWave is array of shorts
add esi, pcWave // Put address of pcWave[dwPosition1] in esi
pxor mm7, mm7 //lLM1 = pcWave[dwPosition1];
mov esi, dword ptr[esi] // Doing AND that was not done for dwFract1 and dwFract2
pand mm6, mm4
// words in MMX register after operation is complete.
psubw mm5, mm6 // 0, 0, 0x1000 - 0, 0x1000 - dwFract1
punpcklwd mm5, mm6 // 0 , 0x1000 - 0, dwFract1, 0x1000 - dwFract1
// put values of pcWave into MMX registers. They are read into a regular register so
// that the routine does not read past the end of the buffer otherwise, it could read
// directly into the MMX registers.
// words in MMX registers
movd mm7, esi // 0, 0, pcWave[dwPos1+1], pcWave[dwPos1]
// *2 pmadd efficent code.
//lM2 = (pcWave[dwPosition2 + 1] * dwFract2 + pcWave[dwPosition2]*(0x1000-dwFract2)) >> 12;
//lM1 = (pcWave[dwPosition1 + 1] * dwFract1 + pcWave[dwPosition1]*(0x1000-dwFract1)) >> 12;
pmaddwd mm7, mm5// low dword = lM1 =
//(pcWave[dwPosition1 + 1] * dwFract1 + pcWave[dwPosition1]*(0x1000-dwFract1))
psrad mm7, 12 // shift back down to 16 bits
pand mm7, wordmask // combine results to get ready to multiply by left and right
movq mm6, mm7 // volume levels.
pslld mm6, 16 //
por mm7, mm6 // words in mm7
// lM2, lM2, lM1, lM1
pxor mm6, mm6
movq mm5, mm2 // move volume1 into mm5
// use pack to get 4 volume values together for multiplication.
packssdw mm5, mm6 // words in mm7
// 0, 0, vfRVolume1, vfLVolume1
/*
// Set lLM to be same as lM
lLM1 = lM1;
lLM1 *= vfLVolume1; lLM1 >>= 5; // Signal bumps up to 15 bits.
lM1 *= vfRVolume1; lM1 >>= 5;
// Set lLM to be same as lM
lLM2 = lM2;
lLM2 *= vfLVolume2; lLM2 >>= 5; // Signal bumps up to 15 bits.
lM2 *= vfRVolume2; lM2 >>= 5; */ // above multiplies and shifts are all done with this one pmul
pmulhw mm5, mm7 // calculate buffer location.
mov edi, ecx shl edi, 1 add edi, pBuffer
/*
add word ptr[edi-4], si jno no_oflowl1 // pBuffer[dwI] = 0x7fff;
mov word ptr[edi-4], 0x7fff js no_oflowl1 //pBuffer[dwI] = (short) 0x8000;
mov word ptr[edi-4], 0x8000 no_oflowl1: //pBuffer[dwI+1] += (short) lM1;
add word ptr[edi-2], dx jno no_oflowr1 //pBuffer[dwI+1] = 0x7fff;
mov word ptr[edi-2], 0x7fff js no_oflowr1 //pBuffer[dwI+1] = (short) 0x8000;
mov word ptr[edi-2], 0x8000 no_oflowr1: */ movd mm7, dword ptr[edi-4] paddsw mm7, mm5 movd dword ptr[edi-4], mm7 //}
#endif
done:
mov edx, this // get address of class object
//vfLastVolume[0] = vfLVolume;
//vfLastVolume[1] = vfRVolume;
// need to shift volume back down to 12 bits before storing
#if 0
psrld mm2, 3 movd [edx]this.vfLastVolume[0], mm2 psrlq mm2, 32 movd [edx]this.vfLastVolume[1], mm2 #endif
psrld mm2, 3 movd vfLastVolume[0], mm2 psrlq mm2, 32 movd vfLastVolume[1], mm2 //m_pfLastPitch = pfPitch;
mov [edx]this.m_pfLastPitch, ebx //m_pfLastSample = pfSamplePos;
mov [edx]this.m_pfLastSample, eax // put value back into dwI to be returned. This could just be passed back in eax I think.
mov dwI, ecx emms } // ASM block
return (dwI >> 1); }
static BOOL MMXDisabled() { ULONG ulValue = FALSE;
if (!GetRegValueDword( TEXT("Software\\Microsoft\\DirectMusic"), TEXT("MMXDisabled"), &ulValue)) { return FALSE; }
return (BOOL)ulValue; }
#define CPU_ID _asm _emit 0x0f _asm _emit 0xa2
BOOL MultiMediaInstructionsSupported() { static BOOL bMultiMediaInstructionsSupported = FALSE; static BOOL bFlagNotSetYet = TRUE; // No need to keep interogating the CPU after it has been checked the first time
if (bFlagNotSetYet) { bFlagNotSetYet = FALSE; // Don't repeat the check for each call
if (!MMXDisabled()) { _asm { pushfd // Store original EFLAGS on stack
pop eax // Get original EFLAGS in EAX
mov ecx, eax // Duplicate original EFLAGS in ECX for toggle check
xor eax, 0x00200000L // Flip ID bit in EFLAGS
push eax // Save new EFLAGS value on stack
popfd // Replace current EFLAGS value
pushfd // Store new EFLAGS on stack
pop eax // Get new EFLAGS in EAX
xor eax, ecx // Can we toggle ID bit?
jz Done // Jump if no, Processor is older than a Pentium so CPU_ID is not supported
mov eax, 1 // Set EAX to tell the CPUID instruction what to return
push ebx CPU_ID // Get family/model/stepping/features
pop ebx test edx, 0x00800000L // Check if mmx technology available
jz Done // Jump if no
} // Tests have passed, this machine supports the Intel MultiMedia Instruction Set!
bMultiMediaInstructionsSupported = TRUE; Done: NULL; } }
#if DBG
if ( bMultiMediaInstructionsSupported ) { Trace(1,"MMX - Detected, Enabling MMX mixing\n\r"); } else { Trace(1,"MMX - Not Detected\n\r"); } #endif
return (bMultiMediaInstructionsSupported); }
|