ActiveBasicでCPUIDをしてみる(機械語を実行してみる)
CPUIDとは、CPUに関する情報を取得する命令で、WindowsAPI等ではなく、CPUに直接命令を出して取得します。
しかし、CPUに直接命令を出す部分はアセンブラか機械語で書かなきゃいけませんが、ActiveBasicはインラインアセンブラには対応していません。
仕方ないので、CでCPUIDを実行する関数を作ってコンパイルし、出力された機械語をActiveBasicに組み込んで呼び出します。
本末転倒・・・?
趣味なのでおkw
今回はCPU名を取得してみたいと思います。
昨今のWindows機なら実行できるはずです。
Cのコード
//CPUID実行可能かチェック
unsigned char __stdcall isCpuid(void){ DWORD After_Eax, Before_Eax;
__asm{ PUSHFD POP EAX MOV Before_Eax,EAX XOR EAX, 00200000H PUSH EAX POPFD PUSHFD POP EAX MOV After_Eax,EAX }
if(After_Eax == Before_Eax){return FALSE;} else{return TRUE;}return -1;}
//CPUID取得関数
//引数: stEAX = CPUIDの引数 、 Regster=↓が代入される。あらかじめメモリは確保しておくこと。
//戻り値:EAX=Regster[0] , EBX=Regster[1] , ECX=Regster[2] , EDX=Regster[3]
unsigned char __stdcall GetCpuID(int stEAX , DWORD *Regster){
DWORD cpEAX, cpEBX, cpECX, cpEDX ;
DWORD After_Eax, Before_Eax;
__asm{
PUSHFD
POP EAX
MOV Before_Eax,EAX
XOR EAX, 00200000H
PUSH EAX
POPFD
PUSHFD
POP EAX
MOV After_Eax,EAX
}
if(After_Eax == Before_Eax){return FALSE;}
__asm{
MOV EAX,stEAX
CPUID
MOV cpEAX,EAX
MOV cpEBX,EBX
MOV cpECX,ECX
MOV cpEDX,EDX
}
Regster[0] = cpEAX;
Regster[1] = cpEBX;
Regster[2] = cpECX;
Regster[3] = cpEDX;
return TRUE;
}
コンパイル&機械語部分切り出し&配列化
ActiveBasicのコード
Function SetAsm(asm As *Byte, codesize As Long) As VoidPtr'機械語をロードして実行権限をつける関数(Wikiより)
SetAsm=VirtualAlloc(NULL, codesize, MEM_COMMIT, PAGE_EXECUTE_READWRITE) '追加
memcpy(SetAsm,asm,codesize)
End Function
Sub FreeAsm(func As VoidPtr)
VirtualFree(func, 0, MEM_DECOMMIT or MEM_RELEASE) '解放
End Sub
'機械語コード(_stdcall) CPUIDが使用可能かチェック (1&h4 + 6 = 46byte)
Dim isCPUID_Asm[ELM(46)]=[
&h55, &h8B, &hEC, &h83, &hC4, &hF8, &h9C, &h58, &h89, &h45,
&hF8, &h35, &h00, &h00, &h20, &h00, &h50, &h9D, &h9C, &h58,
&h89, &h45, &hFC, &h8B, &h45, &hFC, &h3B, &h45, &hF8, &h75,
&h04, &h33, &hC0, &hEB, &h06, &hB0, &h01, &hEB, &h02, &hB0,
&hFF, &h59, &h59, &h5D, &hC3, &h90
] As Byte
'機械語コード(_stdcall) CPUIDの結果を取得(レジスタ) (20*4 + 13 = 93byte)
Dim GetCpuID_Asm[ELM(93)]=[
&h55, &h8B, &hEC, &h83, &hC4, &hE8, &h53, &h56, &h8B, &h75, &h0C, &h9C, &h58, &h89, &h45, &hE8, &h35, &h00, &h00, &h20, '1
&h00, &h50, &h9D, &h9C, &h58, &h89, &h45, &hEC, &h8B, &h45, &hEC, &h8B, &h55, &hE8, &h3B, &hC2, &h75, &h04, &h33, &hC0, '2
&hEB, &h2A, &h8B, &h45, &h08, &h0F, &hA2, &h89, &h45, &hFC, &h89, &h5D, &hF8, &h89, &h4D, &hF4, &h89, &h55, &hF0, &h8B, '3
&h55, &hFC, &h89, &h16, &h8B, &h4D, &hF8, &h89, &h4E, &h04, &h8B, &h45, &hF4, &h89, &h46, &h08, &h8B, &h55, &hF0, &h89, '4
&h56, &h0C, &hB0, &h01, &h5E, &h5B, &h8B, &hE5, &h5D, &hC2, &h08, &h00, &h90
] As Byte
'-----------------プログラム本体----------------------
#console
'関数ポインタ宣言
Dim isCPUID As *Function() As Byte
Dim GetCpuID As *Function(Mode As Long,Vender As DWordPtr) As Byte
'いつもの変数
Dim Ret As Byte
Dim RetCpuName As BytePtr
'関数の中身をセット
isCPUID = SetAsm(isCPUID_Asm,ELM(46))
GetCpuID = SetAsm(GetCpuID_Asm,ELM(93))
'処理
Print "CPUID TEST PROGRAMM"
Ret = isCPUID()
if Ret = TRUE Then
Print "isCPUID = TRUE"
Else
Print ex"isCPUID = FALSE"
END
End If
RetCpuName=GetCpuName()
Print "CPU Name = "+MakeStr(RetCpuName)
free(RetCpuName)
'関数解放
FreeAsm(isCPUID)
FreeAsm(GetCpuID)
Sleep(-1)
Function GetCpuName() As BytePtr 'CPUIDを実行して取得したバイトの並びを分解して文字列に
Dim Reg1 As DWordPtr
Dim Reg2 As DWordPtr
Dim Reg3 As DWordPtr
Dim CpuName[49] As Byte
Dim buff[256] As Byte
Reg1 = malloc(17)
Reg2 = malloc(17)
Reg3 = malloc(17)
GetCpuID( &h80000002, Reg1 )
GetCpuID( &h80000003, Reg2 )
GetCpuID( &h80000004, Reg3 )
CpuName[0] = ((Reg1[0] >> 0) And &hFF)
--- 容量の影響で省略 ---
CpuName[47] = ((Reg3[3] >> 24) And &hFF)
CpuName[48] = 0
GetCpuName=malloc(48)
lstrcpy(GetCpuName,CpuName)
EndFunction
参考文献:
Windowsによるハードウェア制御 p54 CQ出版 北山 洋幸著
abwiki @ ウィキ (ActiveBasic非公式wiki) - メモリ上の機械語プログラム
http://www31.atwiki.jp/abwiki/pages/217.html
CPUID命令によるCPUの性能・機能の把握
http://codezine.jp/article/detail/168?p=2
[gcc]CPUID命令を使用して、CPUの情報を取得する
http://nanoappli.com/blog/archives/3963
以上です。