線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:1529
推到 Plurk!
推到 Facebook!

C++ Builder 和彙編混合編程

 
conundrum
尊榮會員


發表:893
回覆:1272
積分:643
註冊:2004-01-06

發送簡訊給我
#1 引用回覆 回覆 發表時間:2004-02-14 12:00:46 IP:61.221.xxx.xxx 未訂閱
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
系統時間:2024-03-29 23:47:16
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!