TOP MAP UP

AMD64 Software Visual Studio .NET 2005 Professional beta1 でアセンブラ

CPUID 編

 実際にアセンブラを使おうと思うと、プログラム側から見て問題になるのは、プログラムの動作環境だ。すなわちx86かamd64か、あるいは使用したいプロセッサの拡張機能がイネーブルかどうか(これに付随してIntelかAMDかの判定もいるだろう)
 しかし一般にあるようなCPU判定プログラムのように(例:WCPUID)特に詳しく判定する必要もないし(プロセッサにユニークなナンバーからのプロセッサ判定を行う為のデータ等を保持する必要がある)要するに一般的なC++のパーツとして見たCPUIDを考える必要がある。

 前提条件として、WindowsプログラムでWin32API以上の上で走ると仮定する(real mode/standard protect mode/extend protect mode判定はない)CPUIDに反応するプロセッサ上で走ると仮定する(486以下の判定はない。この仮定は可能性としては少々苦しいが、簡略化の為の条件設定なので勘弁願いたい:汗)



 まずx86かamd64かの判定はコンパイルの時点(early)で決定するので、ディレクティブによる判定を行うのがベストだろう。この時のディレクティブはx86=WIN32,amd64=WIN64というような物が、恐らく多分(汗)製品版でも採用されるような気がする。
#ifdef	WIN32
	Win32のコード
#else
	amd64のコード
#endif

 注意して欲しいのは、厳密にはWin32でない!=amd64、あるいは、amd64でない!=Win32という部分だ。通常状態ではWin32でない==amd64と仮定してもらってもなんら差し支えないが、将来的にビルドターゲットを増やす場合はこのような判定はよろしくない。まぁなんにせよソースの書き方次第なので、十分後からでもコントロール出来るので留意しておくレベルで十分だろう



 次に実際にcpuid命令を実行し情報を収集する。この時得られる情報はプログラム実行時(late)にしか得られない物でだが、一度実行すれば再度実行する必要はない。また汎用的に使われる事が考えられるので、クラスとして作成する事とする。
 クラスとして作成する場合実行のタイミングはcpuidの場合はコンストラクタ内が良いだろう。また何度も実行する物でもないので取得した情報は保存しておき、参照できるようにしておくと便利であろうと思われる。
 以上の事を踏まえて、作成したcpuidクラスは以下のようになる。

cpuid.h
#ifndef		_AZU_WINDOW_CPUID_
#define		_AZU_WINDOW_CPUID_

#include	

//	Standard Feature Support
#define	_CPUID_SFS_		0x00000001
#define	_CPUID_SFS_FPU_		0x00000001
#define	_CPUID_SFS_VME_		0x00000002
#define	_CPUID_SFS_DE_		0x00000004
#define	_CPUID_SFS_PSE_		0x00000008
#define	_CPUID_SFS_TSC_		0x00000010
#define	_CPUID_SFS_MSR_		0x00000020
#define	_CPUID_SFS_PAE_		0x00000040
#define	_CPUID_SFS_MCE_		0x00000080
#define	_CPUID_SFS_CX8_		0x00000100
#define	_CPUID_SFS_APIC_		0x00000200
#define	_CPUID_SFS_R10_		0x00000400
#define	_CPUID_SFS_SEP_		0x00000800
#define	_CPUID_SFS_MTRR_		0x00001000
#define	_CPUID_SFS_PGE_		0x00002000
#define	_CPUID_SFS_MCA_		0x00004000
#define	_CPUID_SFS_CMOV_		0x00008000
#define	_CPUID_SFS_PAT_		0x00010000
#define	_CPUID_SFS_PSE36_		0x00020000
#define	_CPUID_SFS_PN_		0x00040000
#define	_CPUID_SFS_CLF_		0x00080000
#define	_CPUID_SFS_R20_		0x00100000
#define	_CPUID_SFS_R21_		0x00200000
#define	_CPUID_SFS_R22_		0x00400000
#define	_CPUID_SFS_MMX_		0x00800000
#define	_CPUID_SFS_FXSR_		0x01000000
#define	_CPUID_SFS_XMM_		0x02000000
#define	_CPUID_SFS_SSE2_		0x04000000
#define	_CPUID_SFS_R27_		0x08000000
#define	_CPUID_SFS_R28_		0x10000000
#define	_CPUID_SFS_R29_		0x20000000
#define	_CPUID_SFS_R30_		0x40000000
#define	_CPUID_SFS_R31_		0x80000000

//	AMD Feature Support
#define	_CPUID_AFS_		0x80000001
#define	_CPUID_AFS_FPU_		0x00000001
#define	_CPUID_AFS_VME_		0x00000002
#define	_CPUID_AFS_DE_		0x00000004
#define	_CPUID_AFS_PSE_		0x00000008
#define	_CPUID_AFS_TSC_		0x00000010
#define	_CPUID_AFS_MSR_		0x00000020
#define	_CPUID_AFS_PAE_		0x00000040
#define	_CPUID_AFS_MCE_		0x00000080
#define	_CPUID_AFS_CX8_		0x00000100
#define	_CPUID_AFS_APIC_		0x00000200
#define	_CPUID_AFS_R10_		0x00000400
#define	_CPUID_AFS_SEP_		0x00000800
#define	_CPUID_AFS_MTRR_		0x00001000
#define	_CPUID_AFS_PGE_		0x00002000
#define	_CPUID_AFS_MCA_		0x00004000
#define	_CPUID_AFS_CMOV_		0x00008000
#define	_CPUID_AFS_PAT_		0x00010000
#define	_CPUID_AFS_PSE36_		0x00020000	//
#define	_CPUID_AFS_R18_		0x00040000
#define	_CPUID_AFS_R19_		0x00080000
#define	_CPUID_AFS_NX_		0x00100000
#define	_CPUID_AFS_R21_		0x00200000
#define	_CPUID_AFS_EMMX_		0x00400000
#define	_CPUID_AFS_MMX_		0x00800000
#define	_CPUID_AFS_FXSR_		0x01000000
#define	_CPUID_AFS_FFXSR_		0x02000000
#define	_CPUID_AFS_R26_		0x04000000
#define	_CPUID_AFS_R27_		0x08000000
#define	_CPUID_AFS_R28_		0x10000000
#define	_CPUID_AFS_LM_		0x20000000
#define	_CPUID_AFS_E3DN_		0x40000000
#define	_CPUID_AFS_3DN_		0x80000000

//	Advanced Power Management Features
#define	_CPUID_APMF_		0x80000007
#define	_CPUID_APMF_TS_		0x00000001
#define	_CPUID_APMF_FID_		0x00000002
#define	_CPUID_APMF_VID_		0x00000004
#define	_CPUID_APMF_TTP_		0x00000008
#define	_CPUID_APMF_TM_		0x00000010
#define	_CPUID_APMF_STC_		0x00000020
#define	_CPUID_APMF_R06_		0x00000040
#define	_CPUID_APMF_R07_		0x00000080
#define	_CPUID_APMF_R08_		0x00000100
#define	_CPUID_APMF_R09_		0x00000200
#define	_CPUID_APMF_R10_		0x00000400
#define	_CPUID_APMF_R11_		0x00000800
#define	_CPUID_APMF_R12_		0x00001000
#define	_CPUID_APMF_R13_		0x00002000
#define	_CPUID_APMF_R14_		0x00004000
#define	_CPUID_APMF_R15_		0x00008000
#define	_CPUID_APMF_R16_		0x00010000
#define	_CPUID_APMF_R17_		0x00020000
#define	_CPUID_APMF_R18_		0x00040000
#define	_CPUID_APMF_R19_		0x00080000
#define	_CPUID_APMF_R20_		0x00100000
#define	_CPUID_APMF_R21_		0x00200000
#define	_CPUID_APMF_R22_		0x00400000
#define	_CPUID_APMF_R23_		0x00800000
#define	_CPUID_APMF_R24_		0x01000000
#define	_CPUID_APMF_R25_		0x02000000
#define	_CPUID_APMF_R26_		0x04000000
#define	_CPUID_APMF_R27_		0x08000000
#define	_CPUID_APMF_R28_		0x10000000
#define	_CPUID_APMF_R29_		0x20000000
#define	_CPUID_APMF_R30_		0x40000000
#define	_CPUID_APMF_R31_		0x80000000



class	CPUID{
		void		_cpuid(unsigned long *,unsigned long);
public:
		unsigned long	standard_max;
		unsigned long	amd_max;
		unsigned long	*standard;
		unsigned long	*amd;
		CPUID();
};

#endif		_AZU_WINDOW_CPUID_


cpuid.cpp
#include	"cpuid.h"

CPUID::CPUID()
{
	unsigned long	tmp[4];
	unsigned long	x;

		_cpuid(tmp,0);
		standard_max=tmp[0];
		standard=new unsigned long[(standard_max+1)<<2];
		for(x=0;x!=(standard_max+1);x++)
			_cpuid(&standard[x<<2],x);

	if(	(standard[1]==0x68747541)	&&
		(standard[2]==0x444d4163)	&&
		(standard[3]==0x69746e65)		){
		_cpuid(tmp,0|0x80000000);
		amd_max=tmp[0]&0x7fffffff;
		amd=new unsigned long[(amd_max+1)<<2];
		for(x=0;x!=(amd_max+1);x++)
			_cpuid(&amd[x<<2],x|0x80000000);
	}
}


x86.asm
;x86
;	Class	CPUID

	.686P
	.XMM
	.model	flat
	.code

PUBLIC	?_cpuid@CPUID@@AAEXPAKK@Z
?_cpuid@CPUID@@AAEXPAKK@Z	PROC
	push	eax
	push	ebx
	push	ecx
	push	edx
	push	esi

	mov	esi,dword ptr [esp+04h+14h]
	mov	eax,dword ptr [esp+08h+14h]
	cpuid

	mov	dword ptr[esi+00h],eax
	mov	dword ptr[esi+04h],ebx
	mov	dword ptr[esi+08h],ecx
	mov	dword ptr[esi+0ch],edx

	pop	esi
	pop	edx
	pop	ecx
	pop	ebx
	pop	eax

	ret	8
?_cpuid@CPUID@@AAEXPAKK@Z	ENDP

END


a64.asm
;x86
;	Class	CPUID

_TEXT	SEGMENT

PUBLIC	?_cpuid@CPUID@@AEAAXPEAKK@Z
?_cpuid@CPUID@@AEAAXPEAKK@Z	PROC
	push	rax
	push	rbx
	push	rsi

	mov	rsi,rdx
	mov	rax,r8
	cpuid

	mov	dword ptr [rsi+00h],eax
	mov	dword ptr [rsi+04h],ebx
	mov	dword ptr [rsi+08h],ecx
	mov	dword ptr [rsi+0ch],edx

	pop	rsi
	pop	rbx
	pop	rax

	ret	0
?_cpuid@CPUID@@AEAAXPEAKK@Z	ENDP

_TEXT	ENDS

END


 cpuid.cpp内でAMDシグネチャを数値で判定している(ストリング比較はnullがいるので、ソース的に見にくいが数値で判定)
 その判定によってAMD拡張部分を調査するかを決めている。

 各.asmだが、cpuidは各レジスタに値を返すので、レジスタの内容が破壊されてしまうためpushしている。また同じ理由で破壊前提レジスタを一気に使用するため、返値のアドレスを入れるのにsiを使用する。
 a64.asmは使うのはe?xだが、AMD64モードではe?xサイズでの演算も上位32bitがゼロクリアされるので、自動的に破壊され、実質64bitの変動があるので注意。

 プログラム中からのこのクラスの使用法だが、まずは返値の最大数(unsigned long単位)をチェックする。これがゼロである場合は、目的の返り値は入っていない。後はcpuid.h前半で規定されているディレクティブを使って、拡張機能の有無をチェックすると良い。