C++与汇编的混合编程
一个人不应该用猜的方式,或是等待某大师的宣判,才确定"何时提供一个copy construtor而何时不需要"。--- Stanley B.Lippman
之所以用到汇编,一是关键程序需要极高的效率,需要用汇编来优化;二是有些功能高级语言是做不到的。
混合汇编一般有两种形式:内置汇编和外置汇编。
内置汇编比较简单,主要是因为
(1)一般不需要考虑寄存器的保存与恢复
(2)不需要考虑函数的命名问题和调用规则
(3)可以在程序中直接访问C/C++的局部变量和全局变量,访问C++中的成员变量也是比较容易的
其不足是失去了模块性
外置汇编刚好反过来了。
下面的程序是我最近作的一个小东西里的一个模块。采用外置汇编,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:
运行结果:
在自己机器上:
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的函数命名是不同的,考虑到可移植性,这里仍然用C函数命名)和函数的调用规则是要注意的。例如stdcall是要自己来进行参数退栈的,用ret XXX 来进行,这也是_setCPUInfo_via_asm@4为什么@符号带上一个数字的原因。二是变量访问的问题,牵扯到c++的内存模型以及内存对齐等一些问题,因而不太确定的情况下需要试验,而且一般尽量保证4字节对齐会使问题简化很多。这也是在cpu.cpp把本应该是bool类型的变量设置成int类型,字符数组的大小设置成4×的原因。
^_^,就这么多。