C++ Builder 和彙編混合編程 |
|
conundrum
尊榮會員 發表:893 回覆:1272 積分:643 註冊:2004-01-06 發送簡訊給我 |
http://mental.mentsu.com/tech/bcb/bcb_asm.htm C Builder 和彙編混合編程 我在寫定時提醒 時碰到一個問題:怎l發聲? 我開始是用 32 位元 Windows 的 API 函數 MessageBeep( -1 ); 那聲音又小又難聽。原來在 16 位的 Windows API 中有的一套 PlaySound 的函數在 32 位元 Windows 中又取消了, DOS 下的 Sound 函數更是早就不能用了。 幸好我對硬體還算了解,知道 PC Speaker 的聲音是通過系統中的定時計數晶片 8253/8254 {生的, 只要通過硬體埠訪問晶片就可以{生想要的聲音了。 問題在於 Windows 是工作在保護模式下, 大多數硬體埠都要在特權級0(PL0, 這是搞硬體的人的說法,後來我才知道在搞 OS 和 Driver 的人中是叫 Ring 0 的, 這才比較正確,因d如果不是 Intel 的 CPU 可能就不叫 PL 了)中, 即作業系統核心態中,才可以訪問(比如硬碟口,訪問時是不會出錯,但結果不正 確), 這也就意味著要寫成驅動程式的形式,天啊! VxD 和 WDM 我都不會, 怎l辦? 事實上沒有這l困難,像 PC Speaker 這種無傷大體的埠, Windows 是不保護的, 即在用戶態下也可以正常訪問。 現在還有一個問題就是用什l語句訪問埠? DOS 中 C 語言裏的那幾個埠操 作函數在 Windows 中都取消了,只好用彙編。我開始是用 ASM 語句插入彙編代 碼,結果發現 BCB 在編譯時碰到 ASM 時會把 BCB 文件編譯成一個巨大的 ASM 文件, 再重新妍妦捰X語言程式彙編,速度太慢。最後採用了我在 DOS 編程時 常用的方法, 做一個單獨的 ASM 文件加入工程文件中。 下面是兩個用於發聲的函數,最前面聲明了兩個外部 C 調用形式的函數, 是兩個用彙編寫的位元組埠輸入/輸出函數,注意:在 C 中一定要注意外部函 數應d C 調用形式。程式中多處強制類型轉換是d了不出現警告,我對程式一 向要求 Error/Warning/Hint 全d 0。 extern "C" { Byte InPortB( int aPort ); void OutPortB( int aPort, Byte aValue ); } void __fastcall Sound( int aFreq ) { if ( ( aFreq >= 20 ) && ( aFreq <= 20000 ) ) { aFreq = 1193181 / aFreq; Byte b = InPortB( 0x61 ); if ( ( b & 3 ) == 0 ) { OutPortB( 0x61, Byte( b | 3 ) ); OutPortB( 0x43, 0xb6 ); } OutPortB( 0x42, ( Byte )aFreq ); OutPortB( 0x42, ( Byte )( aFreq >> 8 ) ); } } void __fastcall NoSound( void ) { Byte b = Byte( InPortB( 0x61 ) & 0xfc ); OutPortB( 0x61, b ); } 下面是兩個埠 I/O 的函數的彙編根源程式,即定時提醒(Alarm)中的 IOPortB.asm 文件的全部內容,是在 BCB {生的 ASM 文件基礎上作了一點點的 優化。 注意: 1 、最前面的 .386p 必不可少,指定用 32 位元保護模式,至於 modal flat 我也不太明白是 What , 跟 16 位時的 tiny, small... 不同,大概是指用 32 位元保護模式的平坦位址間模式吧; 2 、在 32 位元保護模式中, CS/IP d 32 位,參數在棧中的位置與 16 位時 不同; 3 、最後的 public 也不可少,字首的下劃線也是必須的,另外記得用大小寫敏 感方式彙編。 .386p model flat _TEXT segment dword public use32 'CODE' _InPortB proc near push ebp mov ebp, esp mov dx, word ptr [ebp 8] in al, dx pop ebp ret _InPortB endp _OutPortB proc near push ebp mov ebp, esp mov dx, word ptr [ebp 8] mov al, byte ptr [ebp 12] out dx, al pop ebp ret _OutPortB endp public _InPortB public _OutPortB _TEXT ends end 注意:此法在 Windows NT 上行不通,因d Windows NT 保護了所有的 埠, 必須用 WDM,連 VxD 也不行,它只用於 Windows 95 ,在 Windows 98 中 也可以用, 但 Windows NT 和 Windows 2000 都不支援。不過在 Windows NT/2000 中可以使用 Beep( int dwFreq, int dwDuration ); 來發聲, 如下面 的函數。 void __fastcall MyBeep( int aFreq, int aDuration ) { if ( GetVersion( ) & 0x80000000 ) { Sound( aFreq ); int n = GetTickCount( ) aDuration; while ( GetTickCount( ) < n ); NoSound( ); } else Beep( aFreq, aDuration ); }發表人 - conundrum 於 2004/02/14 12:13:36 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |