我在寫定時提醒 時碰到一個問題:怎么發(fā)聲?我開始是用 32 位 Windows 的 API 函數(shù) MessageBeep( -1 ); 那聲音又小又難聽。原來在 16 位的 Windows API 中有的一套 PlaySound 的函數(shù)在 32 位 Windows 中又取消了, DOS 下的 Sound 函數(shù)更是早就不能用了。
幸好我對硬件還算了解,知道 PC Speaker 的聲音是通過系統(tǒng)中的定時計數(shù)芯片 8253/8254 產(chǎn)生的,只要通過硬件端口訪問芯片就可以產(chǎn)生想要的聲音了。 問題在于 Windows 是工作在保護(hù)模式下,大多數(shù)硬件端口都要在特權(quán)級0(PL0, 這是搞硬件的人的說法,后來我才知道在搞 OS 和 Driver 的人中是叫 Ring 0 的, 這才比較正確,因為假如不是 Intel 的 CPU 可能就不叫 PL 了)中, 即操作系統(tǒng)核心態(tài)中,才可以訪問(比如硬盤口,訪問時是不會出錯,但結(jié)果不正確), 這也就意味著要寫成驅(qū)動程序的形式,天??! VxD 和 WDM 我都不會,怎么辦? 事實上沒有這么困難,像 PC Speaker 這種無傷大體的端口, Windows 是不保護(hù)的, 即在用戶態(tài)下也可以正常訪問。
現(xiàn)在還有一個問題就是用什么語句訪問端口? DOS 中 C 語言里的那幾個端口操作函數(shù)在 Windows 中都取消了,只好用匯編。我開始是用 ASM 語句插入?yún)R編代碼,結(jié)果發(fā)現(xiàn) BCB 在編譯時碰到 ASM 時會把 BCB 文件編譯成一個巨大的 ASM 文件, 再重新啟動匯編程序匯編,速度太慢。最后采用了我在 DOS 編程時常用的方法, 做一個單獨的 ASM 文件加入工程文件中。
下面是兩個用于發(fā)聲的函數(shù),最前面聲明了兩個外部 C 調(diào)用形式的函數(shù), 是兩個用匯編寫的字節(jié)端口輸入/輸出函數(shù),注重:在 C++ 中一定要注重外部函數(shù)應(yīng)為 C 調(diào)用形式。程序中多處強制類型轉(zhuǎn)換是為了不出現(xiàn)警告,我對程序一向要求 Error/Warning/Hint 全為 0。
extern "C" { Byte InPortB( int aPort ); void OutPortB( int aPort, Byte aValue ); }