CPUID命令を使って、CPUの情報をプログラムで探ってみましょう。
もちろん、アセンブラ命令はCPUIDを使います。ちなみにここで扱うのはIntelCPUです。
CPUIDって命令がたくさん使える上に、動作の種類が大量にあるので、ファンクション0(ベンダー文字列の取得)と、ファンクション1(CPU情報の取得)をやってみます。
CPUIDは、EAXに命令を入れて、cpuidを実行することで、eax, ebx, ecx, edxレジスタにそれぞれ情報が返ってきます。
まず、ファンクション0。
mov eax, 0
cpuid
上記の命令を実行すると、ASCII文字列で、GenuireIntelという文字が返ってきます。(ソース参照)
次にファンクション1です。
mov eax, 1
cpuid
この命令を実行することで、EAXにプロセッサシグネチャ、EBXにファンクションフラグ、ECXにFeatureFlag2, EDXにFeatureFlag1が返ってきます。
プロセッサシグネチャの内部構成は次の通りです。
プロセッサシグネチャ
Bit 0~3: Stepping ID
Bit 4~7: Model Number
Bit 8~11: Family Code
Bit 12~13: Processor Type
Bit 14~15: Reserved
Bit 16~19: Extended Model
Bit 20~27: Extended Family
Bit 28~31: Reserved
このうち、Model NumderとFamily Codeの組み合わせでCPUを判断することができます。
(厳密にはProcessor TypeでOverdriveがあったり、Extended Modelでさらに分岐があったりしますが)
ファンクションフラグは、通信用語の基礎知識を見ると以下の通りのようです。
Bit 31~24: APIC ID
Bit 23~16: 論理CPU数
Bit 15~8: CLFLUSH
Bit 7~0: ブランドID
Feature Flag1および2については、CPUにSSEやHTTなどの機能が存在するかかどうかをCPUのフラグで判別できます。
ここでは長くなるので、割愛さしてもらいますが・・・。
詳しくは、こちらから。http://download.intel.com/design/processor/applnots/24161832.pdf
ということで、ここではプロセッサシグネチャについて、実際にプログラムで確認してみます。
コンパイラーはVisual C++ 9.0 Express Editionです。
001 #include "stdafx.h"
002 #include <iostream>
003
004 int _tmain(int argc, _TCHAR* argv[])
005 {
006 typedef unsigned int DWORD;
007
008 struct _cpumatrix {
009 unsigned char cpu_code;
010 std::string desc;
011 } cpumatrix[] =
012 {
013 // CPUのマトリックステーブル。左側にFamilyCode, ModelNumberと右側にCPUの文字列を対応づけます。
014 { 0x40, "Intel486 DX processor"},
015 { 0x41, "Intel486 DX processor"},
016 { 0x43, "IntelDX2 processor"},
017 { 0x44, "Intel486 SL processor"},
018 { 0x45, "IntelSX2 processors"},
019 { 0x47, "Write-Back Enhanced IntelDX2 processors"},
020 { 0x48, "IntelDX4 processor"},
021 { 0x51, "Pentium processors(60,66)"},
022 { 0x52, "Pentium Processors(75,90,100,120,133,150,166,200)"},
023 { 0x54, "Pentium processor with MMX technology(166,200)"},
024 { 0x61, "Pentium Pro processor"},
025 { 0x63, "Pentium II processor, model03"},
026 { 0x65, "Pentium II processor, model05, PentiumII Xeon processor, Intel Celeron processor"},
027 { 0x66, "Celeron processor, model06"},
028 { 0x67, "Pentium III processor, model07, and Pentium III Xeon processor, model07"},
029 { 0x68, "Pentium III processor, model 08,Pentium III Xeon processor, model 08,and Celeron processor, model 08"},
030 { 0x69, "Intel Pentium M processor, Intel Celeron M processor model 09"},
031 { 0x6A, "Pentium III Xeon processor, model0Ah"},
032 { 0x6B, "Pentium III processor, model 0Bh"},
033 { 0x6D, "Intel Pentium M processor,90 nm process."},
034 { 0x6E, "Intel Core Duo processor, 65 nm process."},
035 { 0x6F, "Intel Core2 Duo processor, 65 nm process."},
036 { 0xF0, "Pentium 4 processor."},
037 { 0xF1, "Pentium 4 Processor 0.18um process."},
038 { 0xF2, "Pentium 4 Processor 0.13um process."},
039 { 0xF3, "Pentium 4 Processor, Celeron D Processor"},
040 { 0xF4, "Pentium 4 Processor, Pentium D Processor 90nm process"},
041 { 0xF6, "Pentium 4 Processor, Pentium D Processor 65nm process"},
042 { 0x00, "unknown processor"}
043 };
044
045
046 // ベンダーIDのチェック
047 char vender_sig[13];
048 __asm {
049 mov eax, 0; /* EAXに0を入れる */
050 cpuid; /* CPUID実行 */
051 mov dword ptr [vender_sig + 0], ebx; /* 最初の4文字 */
052 mov dword ptr [vender_sig + 4], edx; /* 次の4文字 */
053 mov dword ptr [vender_sig + 8], ecx; /* 最後の4文字 */
054 mov byte ptr [vender_sig + 12], 0; /* ラストに \0 */
055 }
056
057 std::cout << "Vender String = " << vender_sig << std::endl;
058
059
060 // プロセッサシグネチャ, ファクションフラグ、Featureフラグのチェック
061 DWORD prosessor_sig, function_flag, feature_flag2, feature_flag1;
062 __asm {
063 mov eax, 1; /* EAXに1を入れる */
064 cpuid; /* CPUID実行 */
065 mov dword ptr [prosessor_sig], eax; /* プロセッサシグネチャ */
066 mov dword ptr [function_flag], ebx; /* ファンクションフラグ */
067 mov dword ptr [feature_flag2], ecx; /* Featureフラグ */
068 mov dword ptr [feature_flag1], edx; /* Featureフラグ */
069 }
070
071 // ステッピングID, モデルナンバー, ファミリーコード, プロセッサタイプ
072 DWORD SteppingID, ModelNumder, FamilyCode, ProcessorType;
073 SteppingID = prosessor_sig & 0x0000000F;
074 ModelNumder = ( prosessor_sig & 0x000000F0 ) >> 4;
075 FamilyCode = ( prosessor_sig & 0x00000F00 ) >> 8;
076 ProcessorType = ( prosessor_sig & 0x000003000 ) >> 12;
077
078 // マトリックステーブルから一致する文字列を探します。
079 std::string cpustring = "unknown processor";
080 for ( int i = 0 ; ( cpumatrix[i].cpu_code != 0x00 ) ; i++ ) {
081 if ( ( FamilyCode << 4 ) + ModelNumder == cpumatrix[i].cpu_code ) {
082 cpustring = cpumatrix[i].desc;
083 }
084 }
085
086 std::cout << "ProcessorType = " << ProcessorType << std::endl;
087 std::cout << "SteppingID = " << SteppingID << std::endl;
088 std::cout << "ModelNumder = " << ModelNumder << std::endl;
089 std::cout << "FamilyCode = " << FamilyCode << std::endl;
090 std::cout << "Processor Desciption = [" << cpustring.c_str() << "]" << std::endl;
091
092 return 0;
093 }
このコードを実行すると、
Vender String = GenuineIntel
ProcessorType = 0
SteppingID = 7
ModelNumder = 4
FamilyCode = 15
Processor Desciption = [Pentium 4 Processor, Pentium D Processor 90um process.]
のように、CPUの情報が出力されることを確認することができます。
アセンブラ言語は、コンピュータの命令を直接(と言っても機械語でもありませんが、ほぼ1対1で対応するってことで)記述する言語です。
ですが、この言語を使ってプログラミングを行うと、メモリの値を直接のデータアドレスで指定しないといけません。
もともとアセンブラには変数っていう概念はないのでアドレスを計算してあげなければいけないんですけど。
そこで、Visual C++のインラインアセンブラを使うと、C++コードの途中にアセンブラ言語を混ぜることができます。
こうすることで、必要最低限の作業量でMMXやSSEなど、通常のコンパイラが吐いてくれないアセンブラ命令を使うことができたり、CPUIDなどの特殊命令も実行することができます。
インラインアセンブラは次のような記述で使えます。
001 __asm {
002 // インラインアセンブラコードをここに記述
003 }
004
こんな感じ。
まずは、C++で変数を定義して、そこにアセンブラでデータを突っ込んでみましょう。
データのロードにはmov命令を使います。たぶんアセンブラで最も使う命令と思われます。
001 #include <iostream>
002 void main()
003 {
004 int a;
005 __asm {
006 mov a, 256;
007 }
008 std::cout << "a = " << a << std::endl; // a = 256 と出力される
009 }
MOV命令は
mov dest, src
dest レジスタ,メモリ
src レジスタ,メモリ,即値
となっています。ですので、
001 {
002 int a,b;
003 b = 256;
004 __asm {
005 mov a, b;
006 }
007 }
008
というのは動きません。
次に、足し算命令を使ってみましょう。
足し算命令はaddを使います。
add dest, src
flag OF, SF, ZF, AF, CF, PF
dest レジスタ,メモリ
src レジスタ,メモリ,即値
これをインラインアセンブラで記述してみます。
001#include <iostream>
002void main()
003{
004 int a;
005 __asm {
006 mov a, 256;
007 add a, 128;
008 }
009 std::cout << "a = " << a << std::endl; // a = 384 と出力される
010}
このときに、MOV命令と違って演算を行うとフラグが立ちます。これは、算術演算を行った時の結果に対して出てくるものです。
CF(キャリーフラグ)
算術命令を行った際に、最上位ビットが繰り上げもしくは繰り下げを行ったときにセットされます。
PF(パリティーフラグ)
演算結果のパリティビット。二進数で1の数が偶数のときに1になる。使ったことがないw
AF(補助フラグ)
あまり使わない…かも。調べて分かったら書く。
ZF(ゼロフラグ)
演算結果が0になった場合に1になる。とても使う。
SF(サインフラグ)
演算結果がマイナスになったときに1になる。signedコードなので基本的には最上位ビットと同じ。
OF(オーバーフローフラグ)
ビットが越えたときに1になる。たとえば、
mov AX, 65530;
add AX, 100;
とすると、AXレジスタは16ビットなのでオーバーフローとなり、ビットが立ちます。
符号付き演算で、符号が変化したときに1になります。
この値をチェックすることもあります。たとえば、INT 21Hなんかは異常時にCFが1になってくるので、関数の戻り値みたいに使う人もいたり。
といことで、次回はCPUID命令で。
わんくま同盟に参加して、一年になりました。
そもそも、わんくま同盟に申請出したときには、存在が何をしているのかすら知りませんでした。
某SNSで、「Web記事でも書けるようにならんかなー。」とぼやいていたところ、
とあるかたから、
「や ら な い か ?」
といわれまして、とりあえず、
「よくわからないけれど、やるお!」と答えたのがきっかけでした。
それにしても、ここ一年ですごい密度の高いメンバーに出会えてとても充実しました。
最近はチラ裏で遊んでばかりで、さぼってばかりで申し訳ないです。
もしかしたら、しょーもないインラインアセンブラなんかも乗せると役に立つのかもしれません。
めざせ、月1エントリ… orz
半年くらい前のエントリ:http://blogs.wankuma.com/nagase/archive/2007/08/31/92916.aspx#120872
ですが、
>この回路図を基に0-10V入力に対して4-20mA出力になるような回路にするにはどうしたら宜しいでしょうか?
ということで、返信をテキストで記述しようとしたが、それを書くには余白が狭すぎるので新しく起こしました。
前段の加算回路で、2.5Vのオフセットをつけて、0Vから10Vを2.5Vから12.5Vに変換してます。
これを、1V=1.6mAの演算式で、R11をセットします。これで、(計算上は)出力に、4mA - 20mAを流せます。
ただし、上の回路はそもそもuAオーダーの出力を出すためものなので、オペアンプが飽和します。通常利用の場合は、電圧検出抵抗で検出した電流を電圧に変換して、増幅器の反対側に入れるのをオススメしておきます。
C#の練習用にDelegateを利用したFizzBuzz問題をやってみた。
なんかメソッドがことごとくstaticになってるんで、見た目はあまりよくはないですが…。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DelegateFizzBuzz
{
class Program
{ public static int[] FizzBuzzMap =
{3, 0, 0, 1, 0, 2, 1, 0, 0, 1, 2, 0, 1, 0, 0 };
protected delegate void doFuzzBuzz(int n);
public static void Other(int n)
{
Console.WriteLine(n);
}
public static void Fuzz(int n)
{
Console.WriteLine("Fuzz");
}
public static void Buzz(int n)
{
Console.WriteLine("Buzz");
}
public static void FuzzBuzz(int n)
{
Console.WriteLine("FuzzBuzz");
}
static void Main(string[] args)
{
doFuzzBuzz[] FizzBuzzFunc =
{
new doFuzzBuzz( Other ),
new doFuzzBuzz( Fuzz ),
new doFuzzBuzz( Buzz ),
new doFuzzBuzz( FuzzBuzz )
};
for (int i = 0; i < 100; i++)
{
FizzBuzzFunc[FizzBuzzMap[i % 15]](i);
}
}
}
}
1月の東京勉強会に参加されたみなさま、おつかれさまでした。
いろいろと思うことととかあるんですが、そのあたりはチラ裏でどどんと1日10エントリするとして。
特に2次会では、ひさびさにえムナウさんとえぴさんとの席で濃いい話も出来たのでなかなか楽しい会話でした。あ、あと、いろんな面で大変スミマセンでした。次回にでも^-^
最近は、何年ぶりかにASP.NETを触りつつC#もやっていたんですが、やっぱりプログラミング言語って面白いですよね。
なんというか、久しぶりにプログラミングに燃えてきました。
私のはてなダイヤリーからの転載です^-^;
えーと、株価っていうかここでは日経平均株価とかTOPIX、ニューヨークダウなどの株価指数です。
株価が下がっても、株式を持っていない自分には関係ねーよ!なんつっている人も結構多いですが、んなこたーありません。
株価が下がるっていうことは、世の中に出回るお金の流通量が減ります。すると、物が売れなくなります。
物が売れないと、会社が売り上げが減るか、もしくはなくなります。
特に、大企業から仕事を請け負っているような、業務請負なんかの場合には、大企業は他社に仕事回すような余裕はないものですから真っ先に仕事がなくなります。
するってーと、当然会社は小さいほうからつぶれていきますね。
ちなみに、現在ガンガン株価が下がっている理由としては、もちろん発端としては米国サブプライムローンによる金融の損失っていうものもあるのですが、いわゆる金が株式から先物に流れているせいもあります。
ここの先物っていうのは原油とか小麦とかのこと。
株価が下がって原油・小麦とかの価格があがる → 会社はつぶれるわ、物価は大暴騰するわ、失業率があがりまくるわ
ということですね。ただですねー。今回の下げがこれだけ止まらないっていうのは、福田超先生が明確な改革路線を見出さないからいかんのです。
小泉内閣時代にも問題は多々ありましたが、改革路線だけは絶対に貫くっていうメッセージがあったものだから、ここまでひどくはならんかったのですが・・・。