C++与汇编的混合编程

C++与汇编的混合编程 

一个人不应该用猜的方式,或是等待某大师的宣判,才确定"何时提供一个copy construtor而何时不需要"。--- Stanley B.Lippman

之所以用到汇编,一是关键程序需要极高的效率,需要用汇编来优化;二是有些功能高级语言是做不到的。

混合汇编一般有两种形式:内置汇编和外置汇编。

内置汇编比较简单,主要是因为:
(1)一般不需要考虑寄存器的保存与恢复;
(2)不需要考虑函数的命名问题和调用规则;
(3)可以在程序中直接访问C/C++的局部变量和全局变量,访问C++中的成员变量也是比较容易的;
其不足是失去了模块性。

外置汇编刚好反过来了,有较好的模块性,便于重用和移植,但是相对于内置汇编要复杂一些,具体体现在上述的几点相反。

下面的程序是我最近在做的一个小东西里的一个小模块,主要作用是获取CPU的一些信息,如是否支持MMX指令。

汇编采用外置汇编,nasm格式;C++采用VC6作为编译器。

为了简单起见,我去掉了头文件。所以一共两个文件:(1)cpu.cpp;(2)cpuAsm.asm

(1)cpu.cpp:

#include  < iostream >

class  cpu;

extern 
" C "   void  _stdcall setCPUInfo_via_asm(cpu *  cpu);

class  cpu  
{
    
//friend std::ostream& operator<<(std::ostream& out, const cpu& cpu);
public:
    cpu();
    
int getCUPID() const;           // support cupid directive ? 1 : 0;
    int getFPU() const;             // support FPU ? 1 : 0;
    int getMMX() const;             // support MMX ? 1 : 0;
    int get3DNow() const;           // support 3DNow ? 1 : 0;
    const char* getOEM() const;     // OEM
    const char* getSpec() const;    // SPEC
private:    
    
int m_iCPUID;               
    
int m_iFPU;                 
    
int m_iMMX;                 
    
int m_i3DNow;                
    
char m_szOEM[16];
    
char m_szSpecification[52];
}
;

cpu::cpu(): m_iCPUID(
0 ), m_iFPU( 0 ), m_iMMX( 0 ), m_i3DNow( 0 )
{
    
/* set string null, in fact this will be done in function setCPUInfo_via_asm(),
     for more detail, see .nocpuid in function setCPUInfo_via_asm() 
*/

    
/*m_szOEM[0] = 0;
    m_szSpecification[0] = 0;
*/

    
    setCPUInfo_via_asm(
this);
}


int  cpu::getCUPID()  const
{
    
return m_iCPUID;
}


int  cpu::getFPU()  const
{
    
return m_iFPU;
}


int  cpu::getMMX()  const
{
    
return m_iMMX;
}


int  cpu::get3DNow()  const
{
    
return m_i3DNow;
}


const   char *  cpu::getOEM()  const
{
    
return m_szOEM;
}


const   char *  cpu::getSpec()  const
{
    
return m_szSpecification;
}


std::ostream
&  operator << (std::ostream &  out,  const  cpu &  cpu)
{
    out 
<< "Support CPUID : " << cpu.getCUPID()  << std::endl;
    out 
<< "Support FPU   : " << cpu.getFPU()    << std::endl;
    out 
<< "Support MMX   : " << cpu.getMMX()    << std::endl;
    out 
<< "Support 3DNow : " << cpu.get3DNow()  << std::endl;
    out 
<< "O E M         : " << cpu.getOEM()    << std::endl;
    out 
<< "Specification : " << cpu.getSpec()   << std::endl;

    
return out;
}


int  main()
{
    cpu currentcpu;
    std::cout 
<< currentcpu;
    
return 0;
}
             

(2)cpuAsm.asm:

[bits  32 ]
[section .text 
class = code use32]

void  _stdcall setCPUInfo_via_asm(BMCPU *  bmcpu);
global _setCPUInfo_via_asm@
4
_setCPUInfo_via_asm@
4 :
;
-------------------------------------------------
    push ebp                  ; save ebp
    mov ebp, esp              ; esp
-> ebp
    
    push ebx                  ; save ebx, esi, edi
    push esi
    push edi
;
-------------------------------------------------
    
    mov ebx, [ebp
+ 8 ]          ; R[ebx]  =  bmcpu, then
    ; M[ebx] 
=   int  m_iCPUID
    ; M[ebx
+ 4 =   int  m_iFPU
    ; M[ebx
+ 8 =   int  m_iMMX
    ; M[ebx
+ 12 =   int  m_i3DNow
    ; M[ebx
+ 16 =   char  m_szOEM[ 16 ]
    ; M[ebx
+ 32 =   char  m_szSpecification[ 52 ];
    
    ; set 
int  m_iFPU
.setfpu_start:
    fninit
    mov eax, 
0x5a5a
    fnstsw ax
    cmp eax, 
0
    jne .nofpu
    mov [ebx
+ 4 ], dword  1       ;  int  m_iFPU  =   1 ;
    jmp .setfpu_end
.nofpu:
    mov [ebx
+ 4 ], dword  0       ;  int  m_iFPU  =   0 ;
.setfpu_end:
    
    ; set 
int  m_iCPUID
.setcpuid_start:
    pushfd                    ; get extended flags 
    pop eax;
    mov ecx, eax              ; save current flags
    xor eax, 
0x200000          ; toggle bit  21
    push eax                  ; put 
new  flags on stack
    popfd                     ; flags updated now in flags
    pushfd                    ; get extended flags
    pop eax
    xor eax, ecx              ; 
if  bit  21  r / w then supports cpuid
    jz .nocpuid
    mov [ebx], dword 
1         ;  int  m_iCPUID  =   1 ;
    jmp .setcpuid_end
.nocpuid:
    mov [ebx], dword 
0         ;  int  m_iCPUID  =   0 ;
    
    mov [ebx
+ 8 ], dword  0       ;  int  m_iMMX  =   0 ;
    mov [ebx
+ 12 ], dword  0      ;  int  m_i3DNow  =   0 ;
    mov [ebx
+ 16 ],  byte   0       ; set  char  m_szOEM[ 16 null  string
    mov [ebx
+ 32 ],  byte   0       ; set  char  m_szSpecification[ 52 null  string
    
    jmp .returnAsm            ; cupid not supported, then 
return
.setcpuid_end:

    ; set 
int  m_iMMX
.setmmx_start:
    mov esi, ebx              ; save ebx to esi
    
    mov eax, 
1
    cpuid
    and edx, 
0x800000
    jz .nommx
    mov [esi
+ 8 ], dword  1       ;  int  m_iMMX  =   1 ;
    jmp .setmmx_end
.nommx:
    mov [esi
+ 8 ], dword  0       ;  int  m_iMMX  =   0 ;
.setmmx_end:
    mov ebx, esi              ; restore ebx from esi
    
    ; set 
int  m_i3DNow
.set3dnow_start:
    mov esi, ebx              ; save ebx to esi
    
    mov eax, 
0x80000001
    cpuid
    and edx, 
0x80000000
    jz .no3dnow
    mov [esi
+ 12 ], dword  1      ;  int  m_i3DNow  =   1 ;
    jmp .set3dnow_end
.no3dnow:
    mov [esi
+ 12 ], dword  0      ;  int  m_i3DNow  =   0 ;
.set3dnow_end:
    mov ebx, esi              ; restore ebx from esi
    
    ; set 
char  m_szOEM[ 16 ]
.setoem_start:
    mov esi, ebx              ; save ebx to esi
     
    mov eax, 
0
    cpuid
    mov [esi
+ 16 ], ebx         ;  char  m_szOEM[ 0 - 3 =  ebx;
    mov [esi
+ 20 ], edx         ;  char  m_szOEM[ 4 - 7 =  edx;
    mov [esi
+ 24 ], ecx         ;  char  m_szOEM[ 8 - 11 =  edx;
    mov [esi
+ 28 ],  byte   0       ;  char  m_szOEM[ 12 =   0 ;
    
    mov ebx, esi              ; restore ebx from esi
.setoem_end:

    ; set 
char   char  m_szSpecification[ 52 ]
.setspec_start:
    mov esi, ebx              ; save ebx to esi
    
    mov eax, 
0x80000002
    cpuid
    mov [esi
+ 32 ], eax         ;  char  m_szSpecification[ 0 - 3 =  eax;
    mov [esi
+ 36 ], ebx         ;  char  m_szSpecification[ 4 - 7 =  ebx;
    mov [esi
+ 40 ], ecx         ;  char  m_szSpecification[ 8 - 11 =  ecx;
    mov [esi
+ 44 ], edx         ;  char  m_szSpecification[ 12 - 15 =  edx;
    
    mov eax, 
0x80000003
    cpuid
    mov [esi
+ 48 ], eax
    mov [esi
+ 52 ], ebx
    mov [esi
+ 56 ], ecx
    mov [esi
+ 60 ], edx
    
    mov eax, 
0x80000004
    cpuid
    mov [esi
+ 64 ], eax
    mov [esi
+ 68 ], ebx
    mov [esi
+ 72 ], ecx
    mov [esi
+ 76 ], edx
    
    mov [esi
+ 77 ],  byte   0       ;  char  m_szSpecification[ 48 =   0 ;
    
    mov ebx, esi              ; restore ebx from esi
.setspec_end:

;
-------------------------------------------------
.returnAsm:    
    pop edi                   ; restore ebx, esi, edi
    pop esi
    pop ebx
    
    mov esp, ebp              ; restore ebp
    pop ebp
    ret 
4

运行结果:

在自己机器上:

Support CPUID : 1
Support FPU   : 1
Support MMX   : 1
Support 3DNow : 0
O E M         : GenuineIntel
Specification :               Intel(R) Pentium(R) 4 CPU 2.80G

在一台CPU为AMD的机器上:

Support CPUID : 1
Support FPU   : 1
Support MMX   : 1
Support 3DNow : 1
O E M         : AuthenticAMD
Specification : AMD Athlon(tm) XP 1700+

 

几点小结:

(1)环境:把nasmw拷贝到VC的bin下,建好工程文件后,右键单击cpuAsm.asm,Settings->Custom Build中Commands填入nasmw $(InputName).asm  -f win32 -o $(IntDir)/$(InputName).obj,Outputs一栏填入$(IntDir)/$(InputName).obj。如果工程的文件分布复杂一点,Commands是要根据需要作一点变动的。^_^。

(2)函数的命名(C++和C的函数命名是不同的,考虑到可移植性,这里仍然用C函数命名)和函数的调用规则是要注意的。例如stdcall是要自己来进行参数退栈的,用ret XXX 来进行,这也是_setCPUInfo_via_asm@4为什么@符号带上一个数字的原因。

(3)变量访问的问题。这个涉及到C++的对象模型以及内存对齐等一些细节,因而不太确定的情况下需要试验,而且一般尽量保证4字节对齐会使问题简化很多。这也是在cpu.cpp把本应该是bool类型的变量设置成int类型,字符数组的大小设置成4×的原因。

^_^,就这么多。

 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值