很早以前就想写个这样的东西,不过自己是音痴,不会读乐谱.
上次在看雪上看到 玩命大牛写的用BEEP做的《送别》之后,摘抄了曲子,自己实现了直接IO版本.
有几个小细节需要解释:
驱动喇叭的端口:0x61,0x40-0x43.
0x40-0x42是3个计数器,0x40控制系统时钟,0x41控制DRAM刷新的,0x42就是留给一般应用,比如扬声器,我们要用到的就是0x42.
0x43是这3个计数器的控制端口,下面详细解释.
这些端口都是8位操作的,所以每次读写只能操作8位数据.
我在程序中给0x43 OUT 的是0xB6,即10110100.
第0位是0,表示写入的是二进制数,
第1-3位代表计数器的运行方式,010代表以方式2运行,即分频器方式.
第4-5位代表给0x40-0x42端口读写数据的方式,11代表先读低8位,再读高8位.
第6-7位代表选择的计数器,10代表选择选择计数器2,即0x42端口.
再来解释0x61端口.
计数器2的时钟信号一个1193180Hz的输入,计数器2的另外一个输入是0x61端口的第0位,当第0位为1时,计数器2启动.
另外,计数器2的输出端和0x61端口的第1位经过与门之后输出到喇叭,以此来驱动喇叭.
所以要让喇叭启动,就把0x61端口的低两位置1就可以了,要停止就置0.
详细的看代码.
解释完了,希望不是废话.
下面看代码.
代码:
#define ONE_MyBeep 600
#define HALF_MyBeep 300
#define NOTE_1 440
#define NOTE_2 495
#define NOTE_3 550
#define NOTE_4 587
#define NOTE_5 660
#define NOTE_6 733
#define NOTE_7 825
UCHAR In_8(PUCHAR Port)
{
UCHAR Value;
__asm
{
mov edx,Port
in al,dx
mov Value,al
nop
nop
}
return Value;
}
void Out_8(PUCHAR Port,UCHAR Value)
{
__asm
{
mov edx,Port
mov al,Value
out dx,al
nop
nop
}
}
void MyBeep(USHORT freq,ULONG duration)
{
LARGE_INTEGER timeout;
UCHAR stop;
USHORT timer=1193180/freq;
UCHAR start;
start=In_8((PUCHAR)0x61);
start=start|3;
timeout=RtlConvertLongToLargeInteger(-10000*duration);
/*0x61端口低两位置1,启动喇叭*/
Out_8((PUCHAR)0x61,start);
/*计数器控制端口0x43,置位为10110100*/
Out_8((PUCHAR)0x43,0xB4);
/*先写低八位*/
Out_8((PUCHAR)0x42,timer&0xF);
/*再写高八位*/
Out_8((PUCHAR)0x42,(timer>>8)&0xF);
/*喇叭持续时间*/
KeDelayExecutionThread(KernelMode,FALSE,&timeout);
/*0x61端口低两位置0,停止喇叭*/
stop=In_8((PUCHAR)0x61);
stop=stop&0xFC;
Out_8((PUCHAR)0x61,stop);
}
void PlayMusic(void)
{
MyBeep(NOTE_5, ONE_MyBeep);
MyBeep(NOTE_3, HALF_MyBeep);
MyBeep(NOTE_5, HALF_MyBeep);
MyBeep(NOTE_1*2, ONE_MyBeep*2);
MyBeep(NOTE_6, ONE_MyBeep);
MyBeep(NOTE_1*2, ONE_MyBeep);
MyBeep(NOTE_5, ONE_MyBeep*2);
MyBeep(NOTE_5, ONE_MyBeep);
MyBeep(NOTE_1, HALF_MyBeep);
MyBeep(NOTE_2, HALF_MyBeep);
MyBeep(NOTE_3, ONE_MyBeep);
MyBeep(NOTE_2, HALF_MyBeep);
MyBeep(NOTE_1, HALF_MyBeep);
MyBeep(NOTE_2, ONE_MyBeep*4);
MyBeep(NOTE_5, ONE_MyBeep);
MyBeep(NOTE_3, HALF_MyBeep);
MyBeep(NOTE_5, HALF_MyBeep);
MyBeep(NOTE_1*2, HALF_MyBeep*3);
MyBeep(NOTE_7, HALF_MyBeep);
MyBeep(NOTE_6, ONE_MyBeep);
MyBeep(NOTE_1*2, ONE_MyBeep);
MyBeep(NOTE_5, ONE_MyBeep*2);
MyBeep(NOTE_5, ONE_MyBeep);
MyBeep(NOTE_2, HALF_MyBeep);
MyBeep(NOTE_3, HALF_MyBeep);
MyBeep(NOTE_4, HALF_MyBeep*3);
MyBeep(NOTE_7/2, HALF_MyBeep);
MyBeep(NOTE_1, ONE_MyBeep*4);
MyBeep(NOTE_6, ONE_MyBeep);
MyBeep(NOTE_1*2, ONE_MyBeep);
MyBeep(NOTE_1*2, ONE_MyBeep*2);
MyBeep(NOTE_7, ONE_MyBeep);
MyBeep(NOTE_6, HALF_MyBeep);
MyBeep(NOTE_7, HALF_MyBeep);
MyBeep(NOTE_1*2, ONE_MyBeep*2);
MyBeep(NOTE_6, HALF_MyBeep);
MyBeep(NOTE_7, HALF_MyBeep);
MyBeep(NOTE_1*2, HALF_MyBeep);
MyBeep(NOTE_6, HALF_MyBeep);
MyBeep(NOTE_6, HALF_MyBeep);
MyBeep(NOTE_5, HALF_MyBeep);
MyBeep(NOTE_3, HALF_MyBeep);
MyBeep(NOTE_1, HALF_MyBeep);
MyBeep(NOTE_2, ONE_MyBeep*4);
MyBeep(NOTE_5, ONE_MyBeep);
MyBeep(NOTE_3, HALF_MyBeep);
MyBeep(NOTE_5, HALF_MyBeep);
MyBeep(NOTE_1*2, HALF_MyBeep*3);
MyBeep(NOTE_7, HALF_MyBeep);
MyBeep(NOTE_6, ONE_MyBeep);
MyBeep(NOTE_1*2, ONE_MyBeep);
MyBeep(NOTE_5, ONE_MyBeep*2);
MyBeep(NOTE_5, ONE_MyBeep);
MyBeep(NOTE_2, HALF_MyBeep);
MyBeep(NOTE_3, HALF_MyBeep);
MyBeep(NOTE_4, HALF_MyBeep*3);
MyBeep(NOTE_7/2, HALF_MyBeep);
MyBeep(NOTE_1, ONE_MyBeep*3);
}