asm and c backend for x64/x86
Code: Select all
DeclareModule CPUInfo
;cpu info utility module x86/x64 asm c backends
;idle
;v1.1 added GetCPUSerialNumber - jassing
Structure CPUInfoString
val.i
desc.s
EndStructure
Global NewMap cpudata.CPUInfoString()
cpudata("CPU_FPU")\val =0 : cpudata()\desc ="Onboard x87 FPU"
cpudata("CPU_VME")\val = 1 :cpudata()\desc = "Virtual 8086 mode extensions (such As VIF, VIP, PIV)"
cpudata("CPU_DE")\val =2 : cpudata()\desc= "Debugging extensions (CR4 bit 3)"
cpudata("CPU_PSE")\val =3 : cpudata()\desc= "Page Size Extension"
cpudata("CPU_TSC")\val =4 : cpudata()\desc= "Time Stamp Counter"
cpudata("CPU_MSR")\val=5 : cpudata()\desc= "Model-specific registers"
cpudata("CPU_PAE")\val=6 : cpudata()\desc= "Physical Address Extension"
cpudata("CPU_MCE")\val=7 : cpudata()\desc= "Machine Check Exception"
cpudata("CPU_CX8")\val =8 : cpudata()\desc= "CMPXCHG8 (compare-And-Swap) instruction"
cpudata("CPU_APIC")\val=9 : cpudata()\desc= "Onboard Advanced Programmable Interrupt Controller"
cpudata("CPU_SEP")\val=11 : cpudata()\desc= "SYSENTER And SYSEXIT instructions"
cpudata("CPU_MTRR")\val=12 : cpudata()\desc= "Memory Type Range Registers"
cpudata("CPU_PGE")\val =13 : cpudata()\desc= "Page Global Enable bit in CR4"
cpudata("CPU_MCA")\val =14 : cpudata()\desc= "Machine check architecture"
cpudata("CPU_CMOV")\val=15 : cpudata()\desc= "Conditional move And FCMOV instructions"
cpudata("CPU_PAT")\val=16 : cpudata()\desc= "Page Attribute Table (reserved)"
cpudata("CPU_PSE36")\val=17 : cpudata()\desc= "36-bit page size extension"
cpudata("CPU_PSN")\val=18 : cpudata()\desc= "Processor Serial Number"
cpudata("CPU_CLFSH")\val=19 : cpudata()\desc= "CLFLUSH instruction (SSE2)"
cpudata("CPU_DS")\val=21 : cpudata()\desc= "Debug store: save trace of executed jumps"
cpudata("CPU_ACPI")\val=22 : cpudata()\desc= "Onboard thermal control MSRs For ACPI"
cpudata("CPU_MMX")\val=23 : cpudata()\desc= "MMX instructions"
cpudata("CPU_FXSR")\val=24 : cpudata()\desc= "FXSAVE, FXRESTOR instructions, CR4 bit 9"
cpudata("CPU_SSE")\val=25 : cpudata()\desc= "SSE instructions (a.k.a. Katmai New Instructions)"
cpudata("CPU_SSE2")\val=26 : cpudata()\desc= "SSE2 instructions"
cpudata("CPU_SS")\val=27 : cpudata()\desc= "CPU cache supports self-snoop"
cpudata("CPU_HTT")\val=28 : cpudata()\desc= "Hyper-threading"
cpudata("CPU_TM")\val=29 : cpudata()\desc= "Thermal monitor automatically limits temperature"
cpudata("CPU_IA64")\val=30 : cpudata()\desc= "IA64 processor emulating x86"
cpudata("CPU_PBE")\val=31 : cpudata()\desc= "Pending Break Enable (PBE#PB_CPU_ pin) wakeup support"
cpudata("CPU_SSE3")\val=32 : cpudata()\desc= "Prescott New Instructions-SSE3 (PNI)"
cpudata("CPU_PCMULQDQ")\val =33 : cpudata()\desc= "PCLMULQDQ support"
cpudata("CPU_DTES64")\val=34 : cpudata()\desc= "64-bit Debug store (edx bit 21)"
cpudata("CPU_MONITOR")\val=35 : cpudata()\desc= "MONITOR And MWAIT instructions (SSE3)"
cpudata("CPU_DSCPL")\val=36 : cpudata()\desc= "CPL qualified Debug store"
cpudata("CPU_VMX")\val=37 : cpudata()\desc= "Virtual Machine eXtensions"
cpudata("CPU_SMX")\val=38 : cpudata()\desc= "Safer Mode Extensions (LaGrande)"
cpudata("CPU_EST")\val=39 : cpudata()\desc= "Enhanced SpeedStep"
cpudata("CPU_TM2")\val=40 : cpudata()\desc= "Thermal Monitor 2"
cpudata("CPU_SSSE3")\val=41 : cpudata()\desc= "Supplemental SSE3 instructions"
cpudata("CPU_CNXTID")\val=42 : cpudata()\desc= "L1 Context ID"
cpudata("CPU_FMA ")\val=44 : cpudata()\desc= "Fused multiply-add (FMA3)"
cpudata("CPU_CX16")\val=45 : cpudata()\desc= "CMPXCHG16B instruction"
cpudata("CPU_XTPR")\val=46 : cpudata()\desc= "Can disable sending task priority messages"
cpudata("CPU_PDCM")\val=47 : cpudata()\desc= "Perfmon & Debug capability"
cpudata("CPU_PCID")\val=49 : cpudata()\desc= "Process context identifiers (CR4 bit 17)"
cpudata("CPU_DCA")\val=50 : cpudata()\desc= "Direct cache access For DMA writes[10][11]"
cpudata("CPU_SSE41")\val=51 : cpudata()\desc= "SSE4.1 instructions"
cpudata("CPU_SSE42")\val=52 : cpudata()\desc= "SSE4.2 instructions"
cpudata("CPU_X2APIC")\val=53 : cpudata()\desc= "x2APIC support"
cpudata("CPU_MOVBE")\val=54 : cpudata()\desc= "MOVBE instruction (big-endian)"
cpudata("CPU_POPCNT")\val=55 : cpudata()\desc= "POPCNT instruction"
cpudata("CPU_TSCDEADLINE")\val=56 : cpudata()\desc= "APIC supports one-shot operation using a TSC deadline value"
cpudata("CPU_AES")\val=57 : cpudata()\desc= "AES instruction set"
cpudata("CPU_XSAVE")\val=58 : cpudata()\desc= "XSAVE, XRESTOR, XSETBV, XGETBV"
cpudata("CPU_OSXSAVE")\val=59 : cpudata()\desc= "XSAVE enabled by OS"
cpudata("CPU_AVX")\val=60 : cpudata()\desc= "Advanced Vector Extensions"
cpudata("CPU_F16C")\val=61 : cpudata()\desc= "F16C (half-precision) FP support"
cpudata("CPU_RDRND")\val=62 : cpudata()\desc= "RDRAND (on-chip random number generator) support"
cpudata("CPU_HYPERVISOR")\val=63 : cpudata()\desc= "Running on a hypervisor (always 0 on a real CPU, but also With some hypervisors)"
Declare.s GetVendor()
Declare.s GetName()
Declare.s GetVendorVM()
Declare.s GetCPUSerialNumber()
Declare IsCPU(strOp.s)
Declare CountCores()
Declare GetActiveCore()
Declare GetFrequency()
Declare.s GetAllFeatures()
EndDeclareModule
Module CPUInfo
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
Macro AsmInput(var)
!:[var] "r" (v_#var)
EndMacro
Macro AsmInput2(var0,var1)
!:[var0] "r" (v_#var0),[var1] "r" (v_#var1)
EndMacro
Macro AsmInput3(var0,var1,var2)
!:[var0] "r" (v_#var0),[var1] "r" (v_#var1),[var2] "r" (v_#var2)
EndMacro
Macro AsmOutput(var)
!".att_syntax;"
!:[var] "=r" (v_#var)
EndMacro
Macro AsmOutput2(var0,var1)
!".att_syntax;"
!:[var0] "=r" (v_#var0), [var1] "=r" (v_#var1)
EndMacro
Macro AsmOutput3(var0,var1,var2)
!".att_syntax;"
!:[var0] "=r" (v_#var0) , [var1] "=r" (v_#var1) , [var2] "=r" (v_#var2)
EndMacro
Macro AsmOutput4(var0,var1,var2,var3)
!".att_syntax;"
!:[var0] "=r" (v_#var0) , [var1] "=r" (v_#var1) , [var2] "=r" (v_#var2) , [var3] "=r" (v_#var3)
EndMacro
Macro BeginAsm()
! __asm__ __volatile__ (".intel_syntax noprefix;"
EndMacro
Macro AsmClobbers() ; "eax","edx" ...
!:
EndMacro
Macro EndAsm()
!);
EndMacro
Macro __cpuid(level,va, vb, vc, vd)
!unsigned int reg_a, reg_b, reg_c, reg_d;
!asm volatile ("cpuid;"
!: "=a" (reg_a), "=b" (reg_b), "=c" (reg_c), "=d" (reg_d)
!: "0" (v_level)
!);
! v_#va = reg_a;
! v_#vb = reg_b;
! v_#vc = reg_c;
! v_#vd = reg_d;
EndMacro
CompilerEndIf
Procedure.s GetVendor()
Protected a.l,b.l,c.l,d.l,level.l=0
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
__cpuid(level,a,b,c,d)
ProcedureReturn PeekS(@b,4,#PB_Ascii) + PeekS(@d,4,#PB_Ascii) + PeekS(@c,4,#PB_Ascii)
CompilerElse
!mov eax,0
!cpuid
!mov [p.v_a],ebx
!mov [p.v_b],edx
!mov [p.v_c],ecx
ProcedureReturn PeekS(@a,4,#PB_Ascii) + PeekS(@b,4,#PB_Ascii) + PeekS(@c,4,#PB_Ascii)
CompilerEndIf
EndProcedure
Procedure.s GetVendorVM()
Protected a.l,b.l,c.l,d.l,level.l
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
level = $40000000
__cpuid(level,a,b,c,d)
ProcedureReturn PeekS(@b,4,#PB_Ascii) + PeekS(@c,4,#PB_Ascii) + PeekS(@d,4,#PB_Ascii)
CompilerElse
!mov eax,$40000000
!cpuid
!mov [p.v_a],ebx
!mov [p.v_b],ecx
!mov [p.v_c],edx
CompilerEndIf
ProcedureReturn PeekS(@a,4,#PB_Ascii) + PeekS(@b,4,#PB_Ascii) + PeekS(@c,4,#PB_Ascii)
EndProcedure
Procedure IsCPU(strOp.s)
Protected op.l,ret.l
op = cpudata(strOp)\val
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
BeginAsm()
!"mov eax, 1;"
!"cpuid;"
!"xchg eax, ecx;"
!"mov ecx, %[op];"
!"shr edx, cl;"
!"shr eax, cl;"
!"sub ecx, 32;"
!"shr ecx, 31;"
!"and edx, ecx;"
!"xor ecx, 1;"
!"and eax, ecx;"
!"or eax, edx;"
!"mov %[ret],eax;"
AsmOutput(ret)
AsmInput(op)
AsmClobbers() "eax","ecx","edx"
EndAsm()
CompilerElse
!mov eax, 1
!cpuid
!xchg eax, ecx
!mov ecx, [p.v_op]
!shr edx, cl
!shr eax, cl
!sub ecx, 32
!shr ecx, 31
!and edx, ecx
!xor ecx, 1
!and eax, ecx
!or eax, edx
!mov [p.v_ret],eax
CompilerEndIf
ProcedureReturn ret
EndProcedure
Procedure.s GetAllFeatures()
Protected output.s
ForEach cpudata()
If IsCPU(MapKey(cpudata()))
output + MapKey(cpudata()) + " :: " + cpudata()\desc + #LF$
EndIf
Next
ProcedureReturn output
EndProcedure
Procedure.s GetName()
ProcedureReturn CPUName()
EndProcedure
Procedure CountCores()
ProcedureReturn CountCPUs(#PB_System_ProcessCPUs)
EndProcedure
Procedure.i GetActiveCore()
Protected ret.l
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
BeginAsm()
!"xor ebx, ebx;"
!"mov eax, 0x1;"
!"cpuid;"
!"mov eax,ebx;"
!"shr eax,24;"
!"mov %[ret],eax;"
AsmOutput(ret)
!:
AsmClobbers() "eax","ebx"
EndAsm()
CompilerElse
!xor ebx, ebx
!mov eax, 0x1
!cpuid
!mov eax,ebx
!shr eax,24
!mov [p.v_ret],eax
CompilerEndIf
ProcedureReturn ret
EndProcedure
Procedure GetFrequency()
Protected a.l,b.l,t1.l,t2.l,result
Repeat
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
BeginAsm()
!"xor ebx, ebx;"
!"mov eax, 0x1;"
!"cpuid;"
!"shr ebx, 24;"
!"mov %[a], ebx;"
!"rdtsc;"
!"mov %[t1], eax;"
AsmOutput2(a,t1)
!:
AsmClobbers() "eax","ebx","edx"
EndAsm()
Delay(100)
BeginAsm()
!"xor ebx, ebx;"
!"mov eax, 0x1;"
!"cpuid;"
!"shr ebx, 24;"
!"mov %[b],ebx;"
!"rdtsc;"
!"mov %[t2],eax;"
AsmOutput2(b,t2)
!:
AsmClobbers() "eax","ebx","edx"
EndAsm()
CompilerElse
!xor ebx, ebx
!mov eax, 0x1
!cpuid
!shr ebx, 24
!mov [p.v_a],ebx
!rdtsc
!mov [p.v_t1],eax
Delay(100)
!xor ebx, ebx
!mov eax, 0x1
!cpuid
!shr ebx, 24
!mov [p.v_b],ebx
!rdtsc
!mov [p.v_t2],eax
CompilerEndIf
If a = b
result = ((t2-t1) / (100000))
result = ((result+5) / 5) * 5
If t2 - t1 > 0
Break
EndIf
EndIf
ForEver
ProcedureReturn result
EndProcedure
Procedure.s GetCPUSerialNumber()
Define.l highbits, lowbits
Define.q serial
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
BeginAsm()
!"mov eax, 0x80000003;"
!"cpuid;"
!"mov %[lowbits],ecx;"
!"mov %[highbits],edx;"
AsmOutput2(lowbits,highbits)
!:
AsmClobbers() "eax","ecx","edx"
EndAsm()
CompilerElse
!MOV eax, $80000003
!CPUID
!MOV dword [p.v_lowbits], ecx
!MOV dword [p.v_highbits], edx
CompilerEndIf
serial = lowbits | highbits << 32
ProcedureReturn Hex(serial, #PB_Quad)
EndProcedure
EndModule
;Test code
CompilerIf #PB_Compiler_IsMainFile
Prototype.s MyFunction()
Global MyFunction.MyFunction
Procedure.s _MyFunction()
ProcedureReturn "fall back mode"
EndProcedure
Procedure.s _MyFunction_SSE2()
ProcedureReturn "SSE2 mode"
EndProcedure
Procedure InitDynamic()
UseModule CPUInfo
If IsCPU("CPU_SSE2")
MyFunction = @_MyFunction_SSE2()
Else
MyFunction =@_MyFunction()
EndIf
UnuseModule CPUInfo
EndProcedure
Debug CPUInfo::GetVendor()
Debug CPUInfo::GetVendorVM()
Debug CPUInfo::GetName()
Debug CPUInfo::GetCPUSerialNumber()
Debug Str(CPUInfo::CountCores()) + " cores"
Debug "Running on core number " + Str(CPUInfo::GetActiveCore())
Debug "Running at " + Str(CPUInfo::GetFrequency()) + " mhz"
Debug cpuinfo::GetAllFeatures()
InitDynamic()
Debug MyFunction()
CompilerEndIf