3 * Runtime CPU detection library.
4 * (C) 2009-2012 Jonathan Campbell.
5 * Hackipedia DOS library.
7 * This code is licensed under the LGPL.
8 * <insert LGPL legal text here>
10 * Compiles for intended target environments:
12 * - Windows 3.0/3.1/95/98/ME
13 * - Windows NT 3.1/3.51/4.0/2000/XP/Vista/7
17 * A common library to autodetect the CPU at runtime. If the program calling us
18 * is interested, we can also provide Pentium CPUID and extended CPUID information.
19 * Also includes code to autodetect at runtime 1) if SSE is present and 2) if SSE
20 * is enabled by the OS and 3) if we can enable SSE. */
22 /* FIXME: The 16-bit real mode DOS builds of this program are unable to detect CPUID under OS/2 2.x and OS/2 Warp 3. Why? */
24 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
25 /* Win16: We're probably on a 386, but we could be on a 286 if Windows 3.1 is in standard mode.
26 * If the user manages to run us under Windows 3.0, we could also run in 8086 real mode.
27 * We still do the tests so the Windows API cannot deceive us, but we still need GetWinFlags
28 * to tell between 8086 real mode + virtual8086 mode and protected mode. */
30 # include <toolhelp.h>
34 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
42 #include "src/lib/doslib/cpu.h"
44 /* DEBUG: Flush out calls that aren't there */
46 # define int86 ___EVIL___
47 # define ntvdm_RegisterModule ___EVIL___
48 # define ntvdm_UnregisterModule ___EVIL___
49 # define _dos_getvect ___EVIL___
50 # define _dos_setvect ___EVIL___
53 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
54 # include <hw/dos/winfcon.h>
57 char cpu_cpuid_vendor[13]={0};
58 struct cpu_cpuid_features cpu_cpuid_features = {0};
59 signed char cpu_basic_level = -1;
60 uint32_t cpu_cpuid_max = 0;
61 unsigned char cpu_flags = 0;
62 uint16_t cpu_tmp1 = 0;
65 #if TARGET_MSDOS == 32
66 /* we're obviously in 32-bit protected mode, or else this code would not be running at all */
67 /* Applies to: 32-bit DOS, Win32, Win95/98/ME/NT/2000/XP/etc. */
68 cpu_flags = CPU_FLAG_PROTECTED_MODE | CPU_FLAG_PROTECTED_MODE_32;
73 cpu_basic_level = cpu_basic_probe();
75 #if defined(TARGET_OS2)
76 /* OS/2 wouldn't let a program like myself touch control registers. Are you crazy?!? */
77 cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
78 #elif defined(TARGET_WINDOWS) && TARGET_MSDOS == 32
79 /* Under Windows 3.1 Win32s and Win 9x/ME/NT it's pretty much a given any attempt to work with
80 * control registers will fail. Win 9x/ME will silently ignore, and NT will fault it */
81 cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
82 #elif !defined(TARGET_WINDOWS) && TARGET_MSDOS == 32
83 /* 32-bit DOS: Generally yes we can, but only if we're Ring 0 */
94 if (s != 0) cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
98 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
99 /* Windows 3.0/3.1 specific: are we in 32-bit protected mode? 16-bit protected mode? real mode?
100 * real mode with v86 mode (does Windows even work that way?). Note that GetWinFlags only appeared
101 * in Windows 3.0. If we're under Windows 2.x we have to use alternative detection methods, or
102 * else assume Real Mode since Windows 2.x usually does run that way. But: There are 286 and 386
103 * enhanced versions of Windows 2.x, so how do we detect those?
105 * NTS: This code doesn't even run under Windows 2.x. If we patch the binary to report itself as
106 * a 2.x compatible and try to run it under Windows 2.11, the Windows kernel says it's
107 * "out of memory" and then everything freezes (?!?). */
109 # if TARGET_WINDOWS >= 30 /* If targeting Windows 3.0 or higher at compile time, then assume GetWinFlags() exists */
110 DWORD flags = GetWinFlags();
112 # elif TARGET_WINDOWS >= 20 /* If targeting Windows 2.0 or higher, then check the system first in case we're run under 3.0 or higher */
113 /* FIXME: If locating the function by name fails, what ordinal do we search by? */
114 DWORD (PASCAL FAR *__GetWinFlags)() = (LPVOID)GetProcAddress(GetModuleHandle("KERNEL"),"GETWINFLAGS");
115 if (__GetWinFlags != NULL) {
116 DWORD flags = __GetWinFlags();
117 MessageBox(NULL,"Found it","",MB_OK);
118 # else /* don't try. Windows 1.0 does not have GetWinFlags() and does not have any dynamic library functions either. There is
119 no GetModuleHandle, GetProcAddress, etc. */
123 cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
124 if (flags & WF_ENHANCED)
125 cpu_flags |= CPU_FLAG_PROTECTED_MODE | CPU_FLAG_PROTECTED_MODE_32;
126 else if (flags & WF_STANDARD)
127 cpu_flags |= CPU_FLAG_PROTECTED_MODE;
129 /* I highly doubt Windows 3.0 "real mode" every involves virtual 8086 mode, but
130 * just in case, check the machine status register. Since Windows 3.0 could run
131 * on an 8086, we must be cautious to do this test only on a 286 or higher */
132 else if (cpu_basic_level >= 2) {
141 /* if the PE bit is set, we're under Protected Mode
142 * that must mean that all of windows is in Real Mode, but overall
143 * the whole show is in virtual 8086 mode.
144 * We're assuming here that Windows would not lie to us about what mode is active.
146 * THEORY: Could this happen if Windows 3.0 were started in Real Mode
147 * while EMM386.EXE is resident and active? */
148 cpu_flags |= CPU_FLAG_V86_ACTIVE;
149 cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
157 int cpu_basic_probe()
167 // BUGFIX: Calling near a function that returns far is like taking a long walk off a short pier
170 // call cpu_basic_probe_f_