'=========================================================================== ' Subject: GET CPU & FPU INFORMATION Date: 08-05-97 (15:10) ' Author: Hans Lunsing Code: QB, QBasic, PDS ' Origin: FidoNet QUIK_BAS Echo Packet: DOS.ABC '=========================================================================== 'Well, I decided to try to translate my assembler program for CPU and FPU 'recognition for QBASIC. You will find the result in this and the 'following 4 messages. For QuickBasic the machine code part is available 'as an object file as well. You will find it in PostIt! format in the 'last message of this series. 'Information about the CPU and the FPU can be obtained by a call to the 'SUB GetProcInfo, declared as follows: 'DECLARE SUB GetProcInfo (ProcInfo AS ProcInfoType) 'ProcInfoType has the following structure: 'TYPE ProcInfoType ' CpuFamily AS INTEGER ' CpuVendor AS INTEGER ' CpuModel AS INTEGER ' CpuStepping AS INTEGER ' CpuType AS INTEGER ' CpuFeatures AS LONG ' CPUIDFlag AS INTEGER ' CxDirFlag AS INTEGER ' Fpu AS INTEGER 'END TYPE 'There is one extra element: CxDirFlag. It's one for Cyrix processors 'supporting the DIR registers (containing information about that CPU), 'and zero otherwise. If CPUIDFlag is zero and CxDirFlag = 1 the element 'CpuFeatures is zero, being undefined, but the CpuFamily, CpuModel, 'CpuStepping and CpuType are derived from the DIRs. 'The most important elements of this structure are CpuFamily and Fpu. 'They get one of the following values: 'CpuFamily: Fpu identification: '0 - 8086/8088, NEC V20/V30 0 - no coprocessor '1 - 80186 1 - 8087 '2 - 80286 2 - 80287 '3 - 80386 3 - 80387 '4 - 80486, AMD 5x86, Cyrix 486 4 - built in or 80387 '5 - Pentium, AMD K5 and K6, Cyrix 5x86 5 - built in '6 - Pentium Pro, Cyrix 6x86 'NEC V20/V30 can be distinguished from 8086/8088 by the CpuVendor 'element. It's 6 for NEC, and 0 (unspecified) for 8086/8088. 'Distinction between the 80386DX and the 80386SX can be made with the 'CpuModel element. It's 0 for the DX, and 1 for the SX. Furthermore, the '80486DX has a built-in FPU, the 80486SX has not, but can have a 80387 'alongside. 'All other information stems from the CPUID (CPU identification) 'instruction available on all modern processors. If the instruction is 'fully supported the CPUIDFlag is 2, if it gives the vendor only the flag 'is 1, and otherwise it's 0. Except in the case of 80386DX or SX the 'elements CpuModel, CpuStepping, CpuType and CpuFeatures will have no 'meaning if CPUIDFlag <> 2. The first three will be 255 then, and the 'features 0. 'I hope the code is sufficiently self explanatory. Here is is: '-------------------------------------------------------- ' ProcId ' QBASIC procedure to get info about the CPU and the FPU ' By Hans Lunsing, 1996, ' using Intel's, AMD's and Cyrix's documentation about the processors ' supporting the CPUID instruction, Cyrix's documentation about its ' device identification registers (DIR), source code given by Intel, AMD ' and Cyrix, and code snippets from several other sources. ' Donated to the public domain ' ---------------------------------------------------------------------- DEFINT A-Z CONST FALSE = 0, TRUE = NOT FALSE TYPE ProcInfoType CpuFamily AS INTEGER CpuVendor AS INTEGER CpuModel AS INTEGER CpuStepping AS INTEGER CpuType AS INTEGER CpuFeatures AS LONG CPUIDFlag AS INTEGER CxDirFlag AS INTEGER Fpu AS INTEGER END TYPE DECLARE SUB GetProcInfo (ProcInfo AS ProcInfoType) DECLARE FUNCTION GetCPUName$ (ProcInfo AS ANY) DECLARE FUNCTION GetFPUName$ (ProcInfo AS ANY) DECLARE FUNCTION GetVendorName$ (Vendor AS INTEGER) '----------------------------------------------------------------------- ' The machine code of the assembler procedure for GetProcInfo '----------------------------------------------------------------------- GetProcInfoASM: ' Number of bytes DATA 1237 ' Hexadecimal representation of machine code DATA 55,8B,EC,9C,1E,06,56,57,8B,5E,06,8B,3F,81,EF,0A DATA 00,8C,C8,8E,D8,8E,C0,80,BD,C7,00,FF,75,72,C6,85 DATA C9,00,00,E8,FA,00,72,11,C6,85,C7,00,00,E8,FD,00 DATA 72,5B,C6,85,C9,00,06,EB,54,E8,F8,00,72,07,C6,85 DATA C7,00,01,EB,48,E8,02,01,72,07,C6,85,C7,00,02,EB DATA 3C,E8,06,01,72,0E,C6,85,C7,00,03,E8,24,01,88,85 DATA CB,00,EB,29,C6,85,C7,00,04,E8,9A,02,72,12,C6,85 DATA C9,00,03,E8,A3,02,80,BD,D7,00,01,75,10,E8,42,03 DATA E8,90,01,80,BD,C9,00,03,75,03,E8,5C,03,E8,91,03 DATA 8B,46,FC,8E,C0,BE,C7,00,03,F7,8B,7E,08,B9,14,00 DATA F3,A4,5F,5E,07,1F,9D,5D,CA,04,00,48,61,6E,73,20 DATA 4C,75,6E,73,69,6E,67,2C,20,31,39,39,37,FF,00,FF DATA 00,FF,00,FF,00,FF,00,00,00,00,00,00,00,00,00,FF DATA 00,00,00,00,00,00,00,00,00,00,00,00,00,24,00,00 DATA 00,00,00,00,47,65,6E,75,69,6E,65,49,6E,74,65,6C DATA 41,75,74,68,65,6E,74,69,63,41,4D,44,41,4D,44,20 DATA 49,53,42,45,54,54,45,52,43,79,72,69,78,49,6E,73 DATA 74,65,61,64,55,4D,43,20,55,4D,43,20,55,4D,43,20 DATA B0,FF,B1,20,D2,E0,0A,C0,F9,75,01,F8,C3,F9,60,F8 DATA 72,01,61,C3,9C,58,8B,C8,25,FF,0F,50,9D,9C,58,25 DATA 00,F0,3D,00,F0,F9,75,01,F8,C3,81,C9,00,F0,51,9D DATA 9C,58,25,00,F0,F9,75,01,F8,C3,8B,DC,83,E4,FC,66 DATA 9C,66,58,66,8B,C8,66,35,00,00,04,00,66,50,66,9D DATA 66,9C,66,58,66,51,66,9D,66,33,C1,8B,E3,F9,75,01 DATA F8,C3,57,FC,B9,00,04,33,F6,F3,66,AD,B9,00,04,33 DATA F6,8B,FE,FA,B0,B0,E6,43,EB,00,32,C0,E6,42,EB,00 DATA E6,42,EB,00,E4,61,0C,01,E6,61,F3,A5,B0,80,E6,43 DATA EB,00,FB,E4,42,EB,00,8A,D0,E4,42,EB,00,8A,F0,F7 DATA DA,E4,61,24,FE,E6,61,52,B9,00,04,33,F6,8B,FE,FA DATA B0,B0,E6,43,EB,00,32,C0,E6,42,EB,00,E6,42,EB,00 DATA E4,61,0C,01,E6,61,F3,66,A5,B0,80,E6,43,EB,00,FB DATA E4,42,EB,00,8A,D0,E4,42,EB,00,8A,F0,F7,DA,E4,61 DATA 24,FE,E6,61,58,8B,D8,D1,EB,03,C3,3B,C2,B0,00,14 DATA 00,5F,C3,C6,85,D5,00,00,66,8B,C1,66,35,00,00,20 DATA 00,66,50,66,9D,66,9C,66,58,66,33,C1,0F,84,D5,00 DATA FE,85,D5,00,66,33,C0,0F,A2,66,89,9D,DB,00,66,89 DATA 95,DF,00,66,89,8D,E3,00,BE,DB,00,03,F7,56,57,81 DATA C7,EE,00,B9,0C,00,F3,A6,5F,5E,0B,C9,75,07,C6,85 DATA C9,00,01,EB,65,56,57,81,C7,FA,00,B9,0C,00,F3,A6 DATA 5F,5E,0B,C9,75,07,C6,85,C9,00,02,EB,4D,56,57,81 DATA C7,06,01,B9,0C,00,F3,A6,5F,5E,0B,C9,75,07,C6,85 DATA C9,00,02,EB,35,56,57,81,C7,12,01,B9,0C,00,F3,A6 DATA 5F,5E,0B,C9,75,07,C6,85,C9,00,03,EB,1D,56,57,81 DATA C7,1E,01,B9,0C,00,F3,A6,5F,5E,0B,C9,75,07,C6,85 DATA C9,00,04,EB,05,C6,85,C9,00,09,66,83,F8,01,7C,35 DATA FE,85,D5,00,66,33,C0,66,40,0F,A2,66,89,95,D1,00 DATA 8A,D0,80,E2,0F,88,95,CD,00,24,F0,C0,E8,04,88,85 DATA CB,00,8A,F4,80,E6,0F,88,B5,C7,00,80,E4,30,C0,EC DATA 04,88,A5,CF,00,C3,33,C0,9E,B8,05,00,B3,02,F6,F3 DATA 9F,80,FC,02,F9,75,01,F8,C3,C6,85,E8,00,FF,C6,85 DATA E9,00,FF,B4,C3,E8,E3,00,8A,D0,34,80,E8,E6,00,B4 DATA C0,E8,D7,00,B4,C3,E8,D2,00,32,ED,3A,C2,74,02,FE DATA C5,8A,C2,E8,CF,00,C6,85,D7,00,00,80,FE,01,75,18 DATA FE,85,D7,00,B4,FE,E8,B2,00,88,85,E8,00,B4,FF,E8 DATA A9,00,88,85,E9,00,EB,34,B4,C2,E8,9E,00,8A,D0,34 DATA 04,E8,A1,00,B4,C0,E8,92,00,B4,C2,E8,8D,00,32,C9 DATA 3A,C2,74,02,FE,C1,8A,C2,E8,8A,00,80,FA,01,75,07 DATA C6,85,E8,00,FE,EB,05,C6,85,E8,00,FD,8A,85,E8,00 DATA 8A,E0,24,0F,C0,EC,04,C6,85,C7,00,04,88,A5,CB,00 DATA 88,85,CD,00,3C,03,72,09,3C,0F,74,05,C6,85,C7,00 DATA 05,C3,B4,C3,E8,44,00,88,85,EA,00,24,0F,0C,10,E8 DATA 43,00,B4,E8,E8,34,00,88,85,EB,00,0C,80,E8,35,00 DATA B4,C3,8A,85,EA,00,E8,2C,00,B4,C3,E8,1D,00,88,85 DATA EA,00,24,0F,0C,10,E8,1C,00,B4,E8,8A,85,EB,00,E8 DATA 13,00,B4,C3,8A,85,EA,00,E8,0A,00,9C,FA,8A,C4,E6 DATA 22,E4,23,9D,C3,9C,FA,86,C4,E6,22,86,C4,E6,23,9D DATA C3,9C,B2,00,80,BD,D5,00,02,75,0C,8B,85,D1,00,A8 DATA 01,74,04,B2,05,EB,7F,DB,E3,C7,85,EC,00,5A,5A,DD DATA BD,EC,00,8B,85,EC,00,3C,00,75,6B,D9,BD,EC,00,8B DATA 85,EC,00,25,3F,10,83,F8,3F,75,5B,B2,01,80,BD,C7 DATA 00,02,72,52,B2,02,74,4E,80,BD,C7,00,03,75,24,9B DATA D9,E8,9B,D9,EE,9B,DE,F9,9B,D9,C0,9B,D9,E0,9B,DE DATA D9,9B,DD,BD,EC,00,8B,85,EC,00,9E,B2,02,74,27,B2 DATA 03,EB,23,B2,05,80,BD,C7,00,04,75,1A,80,BD,D5,00 DATA 01,75,0C,F7,85,D1,00,01,00,75,0B,B2,03,EB,07,E8 DATA 0A,00,73,02,B2,03,88,95,D9,00,9D,C3,0F,20,C0,8A DATA D8,34,10,0F,22,C0,0F,20,C0,86,C3,0F,22,C0,3A,C3 DATA F9,75,01,F8,C3 ' Pretty impressive, isn't it? For me it was, when I saw the result :) '----------------------------------------------------------------------- ' Example: get CPU and FPU info '----------------------------------------------------------------------- DIM ProcInfo AS ProcInfoType GetProcInfo ProcInfo PRINT "CPU: "; GetCPUName$(ProcInfo) PRINT "FPU: "; GetFPUName$(ProcInfo) PRINT PRINT "CPU Vendor: "; GetVendorName$(ProcInfo.CpuVendor) IF ProcInfo.CPUIDFlag = 2 THEN PRINT "CPU version: "; PRINT USING "#_.##_.##(#)"; ProcInfo.CpuFamily; ProcInfo.CpuModel; ProcInfo.CpuStepping; ProcInfo.CpuType PRINT "CPU features: "; RIGHT$("0000000" + HEX$(ProcInfo.CpuFeatures), 8) ELSE PRINT "CPU family: "; ProcInfo.CpuFamily END IF END ' Vendor codes CONST UNKNOWN = 0, INTEL = 1, AMD = 2, CYRIX = 3 CONST UMC = 4, NEC = 6, OTHER = 9 ' CPU family codes CONST I8086 = 0, I80186 = 1, I80286 = 2, I80386 = 3, I80486 = 4 CONST FAMILY5 = 5, FAMILY6 = 6 ' FPU identifications: CONST NONE = 0, I8087 = 1, I80287 = 2, I80387 = 3 CONST BUILT.IN.OR.387 = 4, BUILT.IN = 5 FUNCTION GetCPUName$ (ProcInfo AS ProcInfoType) DIM Cpu AS STRING IF ProcInfo.CPUIDFlag = 2 OR ProcInfo.CxDirFlag = 1 THEN 'CPU supports CPUID instruction fully, or is a Cyrix processor 'and supports the DIR registers SELECT CASE ProcInfo.CpuVendor CASE INTEL SELECT CASE ProcInfo.CpuFamily CASE I80486 SELECT CASE ProcInfo.CpuModel CASE 3: Cpu = "Intel486 DX2 or DX2 Overdrive" CASE 5: Cpu = "Intel486 SX2" CASE 7: Cpu = "Intel486 DX2 (write back enhanced)" CASE 8: IF ProcInfo.CpuType = 0 THEN Cpu = "Intel486 DX4 or DX4 Overdrive" ELSE Cpu = "Intel486 DX4 Overdrive" END IF CASE ELSE Cpu = "Intel486" END SELECT CASE FAMILY5 SELECT CASE ProcInfo.CpuModel CASE 1: Cpu = "Pentium (510\60, 567\66)" CASE 2: IF ProcInfo.CpuType = 0 THEN Cpu = "Pentium (680\75, 735\90, 815\100, 1000\120, 1110\133)" ELSE Cpu = "Pentium Overdrive for Pentium CPU" END IF CASE 3: Cpu = "Pentium Overdrive for Intel486 CPU" CASE 4: Cpu = "Overdrive for Pentium (680\75, 735\90, 815\100, 1000\120, 1110\133)" CASE 5: Cpu = "Pentium Overdrive for Intel486 DX4 CPU" CASE ELSE Cpu = "Intel Pentium" END SELECT CASE FAMILY6 SELECT CASE ProcInfo.CpuModel CASE 1: Cpu = "Pentium Pro" CASE 3: Cpu = "Overdrive for Pentium Pro" CASE ELSE Cpu = "Intel Pentium Pro" END SELECT CASE ELSE Cpu = "Intel Pentium Pro Plus" END SELECT CASE AMD SELECT CASE ProcInfo.CpuFamily CASE I80486 SELECT CASE ProcInfo.CpuModel CASE 3: Cpu = "Am486DX2 (Write Trough)" CASE 7: Cpu = "Am486DX2 (Write Back)" CASE 8: Cpu = "Am486DX4 or Am5x86-150Mhz (Write Trough)" CASE 9: Cpu = "Am486DX4 or Am5x86-150Mhz (Write Back)" CASE 14: Cpu = "Am5x86 133 or 160Mhz (Write Trough)" CASE 15: Cpu = "Am5x86 133 or 160Mhz (Write Back)" CASE ELSE Cpu = "AMD Am486 or Am5x86" END SELECT CASE FAMILY5 SELECT CASE ProcInfo.CpuModel CASE 0: Cpu = "AMD-K5 (model 0)" CASE 1: Cpu = "AMD-K5 (model 1)" CASE 6: Cpu = "AMD-K6" CASE ELSE Cpu = "AMD-K5 or AMD-K6" END SELECT CASE ELSE Cpu = "AMD-K6 Plus" CASE ELSE END SELECT CASE CYRIX SELECT CASE ProcInfo.CpuModel CASE 0 SELECT CASE ProcInfo.CpuStepping CASE 0: Cpu = "Cx486 SLC" CASE 1: Cpu = "Cx486 DLC" CASE 2: Cpu = "Cx486 SLC2" CASE 3: Cpu = "Cx486 DLC2" CASE 4: Cpu = "Cx486 SRx, Retail Upgrade Cx486SLC" CASE 5: Cpu = "Cx486 DRx, Retail Upgrade Cx486DLC" CASE 6: Cpu = "Cx486 SRx2, Retail Upgrade 2x Cx486SLC" CASE 7: Cpu = "Cx486 DRx2, Retail Upgrade 2x Cx486DLC" CASE ELSE: Cpu = "Cx486 model 0 stepping" + STR$(ProcInfo.CpuStepping) END SELECT CASE 1 SELECT CASE ProcInfo.CpuStepping CASE 0: Cpu = "Cx486S B step" CASE 1: Cpu = "Cx486S2 B step" CASE 2: Cpu = "Cx486Se B step" CASE 3: Cpu = "Cx486S2e B step" CASE 10: Cpu = "Cx486DX" CASE 11: Cpu = "Cx486DX2" CASE 15: Cpu = "Cx486DX4" CASE ELSE: Cpu = "Cx486 model 1 stepping" + STR$(ProcInfo.CpuStepping) END SELECT CASE 2 SELECT CASE ProcInfo.CpuStepping CASE 8, 10: Cpu = "Cyrix 5x86 1x Core/Bus Clock" CASE 9, 11: Cpu = "Cyrix 5x86 2x Core/Bus Clock" CASE 13, 15: Cpu = "Cyrix 5x86 3x Core/Bus Clock" CASE 12, 14: Cpu = "Cyrix 5x86 4x Core/Bus Clock" CASE ELSE: Cpu = "Cyrix 5x86 stepping" + STR$(ProcInfo.CpuStepping) END SELECT CASE 3 SELECT CASE ProcInfo.CpuStepping CASE 0, 2: Cpu = "Cyrix 6x86 1x Core/Bus Clock" CASE 1, 3: Cpu = "Cyrix 6x86 2x Core/Bus Clock" CASE 5, 7: Cpu = "Cyrix 6x86 3x Core/Bus Clock" CASE 4, 6: Cpu = "Cyrix 6x86 4x Core/Bus Clock" CASE ELSE: Cpu = "Cyrix 6x86 stepping" + STR$(ProcInfo.CpuStepping) END SELECT CASE 15 SELECT CASE ProcInfo.CpuStepping CASE 13: Cpu = "Cx486SLC or DLC, old model" CASE 14: Cpu = "Cx486Sa Step=1" CASE ELSE: Cpu = "Cyrix unknown model" END SELECT CASE ELSE Cpu = "Cyrix unknown model" END SELECT CASE UMC SELECT CASE ProcInfo.CpuFamily CASE I80486: Cpu = "UMC 80486-like" CASE FAMILY5: Cpu = "UMC Pentium-like" CASE FAMILY6: Cpu = "UMC Pentium-Pro-like" CASE ELSE: Cpu = "UMC Pentium-Pro-like Plus" END SELECT CASE ELSE SELECT CASE ProcInfo.CpuFamily CASE I80486: Cpu = "80486-like" CASE FAMILY5: Cpu = "Pentium-like" CASE FAMILY6: Cpu = "Pentium-Pro-like" CASE ELSE: Cpu = "Pentium-Pro-like Plus" END SELECT END SELECT ELSE 'CPU doesn't support CPUID instruction or supports it for vendor id only SELECT CASE ProcInfo.CpuFamily CASE I8086 IF ProcInfo.CpuVendor = NEC THEN Cpu = "NEC V20/V30" ELSE Cpu = "8086/8088" END IF CASE I80186 Cpu = "80186/80188" CASE I80286 Cpu = "80286" CASE I80386 SELECT CASE ProcInfo.CpuModel CASE 0: Cpu = "80386 DX" CASE 1: Cpu = "80386 SX" CASE ELSE: Cpu = "80386" END SELECT CASE I80486 IF ProcInfo.CpuVendor = CYRIX THEN Cpu = "Cx486 SLC/DLC" ELSEIF ProcInfo.Fpu = BUILT.IN THEN Cpu = "80486 DX (or newer)" ELSE Cpu = "80486 SX" END IF END SELECT END IF GetCPUName$ = Cpu END FUNCTION FUNCTION GetFPUName$ (ProcInfo AS ProcInfoType) DIM Fpu AS STRING SELECT CASE ProcInfo.Fpu CASE BUILT.IN: Fpu = "built-in" CASE I80387: Fpu = "80387" CASE I80287: Fpu = "80287" CASE I8087: Fpu = "8087" CASE ELSE: Fpu = "not present" END SELECT GetFPUName$ = Fpu END FUNCTION SUB GetProcInfo (ProcInfo AS ProcInfoType) STATIC '----------------------------------------------------------------------- ' Procedure to get info about the CPU and the FPU. ' Uses the ProcInfoType structure to return the info in. '----------------------------------------------------------------------- IF NOT MachineCode% THEN ' First time dimension string array to hold machine code RESTORE GetProcInfoASM READ nASMBYTES% REDIM ASMBuffer(0 TO nASMBYTES% - 1) AS STRING * 1 END IF ' Get address of machine code DEF SEG = VARSEG(ASMBuffer(0)) Offset% = VARPTR(ASMBuffer(0)) IF NOT MachineCode% THEN ' First time load string array with machine code FOR i% = 0 TO nASMBYTES% - 1 READ Code$ POKE Offset% + i%, VAL("&H" + Code$) NEXT i% ' Indicate availability of machine code MachineCode% = TRUE END IF ' Get info. ' The second offset% is necessary!!! CALL ABSOLUTE(ProcInfo, Offset%, Offset%) DEF SEG END SUB FUNCTION GetVendorName$ (Vendor AS INTEGER) DIM Ven AS STRING SELECT CASE Vendor CASE INTEL: Ven = "Intel" CASE AMD: Ven = "AMD" CASE CYRIX: Ven = "Cyrix" CASE UMC: Ven = "UMC" CASE NEC: Ven = "NEC" CASE ELSE: Ven = "not specified" END SELECT GetVendorName$ = Ven END FUNCTION