potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++ EFLAGSの値とCPUID命令の使用可否を確認する(32ビット限定)

VC++インラインアセンブリを用いてEFLAGSレジスタの値とCPUID命令の使用可否を確認するコードです。EFLAGSレジスタの独特な扱い、詳細についてはネットで調べるとすぐ出てくるかと思います。__asmを使用しているため、32ビット限定です。

class eflags
{
private:
    eflags(unsigned int value) : value(value) {}

public:
    union {
        unsigned int value;
        struct {
            unsigned cf : 1;
            unsigned r1 : 1;
            unsigned pf : 1;
            unsigned r2 : 1;
            unsigned af : 1;
            unsigned r3 : 1;
            unsigned zf : 1;
            unsigned sf : 1;
            unsigned tf : 1;
            unsigned if_ : 1;
            unsigned df : 1;
            unsigned of : 1;
            unsigned iopl : 2;
            unsigned nt : 1;
            unsigned r4 : 1;
            unsigned rf : 1;
            unsigned vm : 1;
            unsigned ac : 1;
            unsigned vif : 1;
            unsigned vip : 1;
            unsigned id : 1;
            unsigned r5 : 10;
        } bits;
    };

    // EFLAGSを読み込む。
    static eflags pop()
    {
        unsigned int i;
        __asm
        {
            pushfd
            pop i
        }
        return eflags(i);
    }

    // EFLAGSを書き込む。
    void push()
    {
        unsigned int i = value;
        __asm
        {
            push i
            popfd
        }
    }

    // CPUID命令が有効かどうかを確認する。
    static bool is_cpuid_valid()
    {
        eflags eflags1 = eflags::pop();
        eflags1.bits.id = ~eflags1.bits.id;
        eflags1.push();

        eflags eflags2 = eflags::pop();
        eflags2.bits.id = ~eflags2.bits.id; // 元に戻しておく
        eflags2.push();

        return eflags1.value != eflags2.value;
    }
};

#include <iostream>
#include <bitset>

int main()
{
    std::cout << "CPUID命令は" << (eflags::is_cpuid_valid() ? "有効" : "無効") << "です。" << std::endl;
    std::cout << "EFLAGS: " << std::bitset<32>(eflags::pop().value) << std::endl;

    std::cin.get();

    return 0;
}