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"
43 //#include <hw/dos/dos.h>
45 /* DEBUG: Flush out calls that aren't there */
47 # define int86 ___EVIL___
48 # define ntvdm_RegisterModule ___EVIL___
49 # define ntvdm_UnregisterModule ___EVIL___
50 # define _dos_getvect ___EVIL___
51 # define _dos_setvect ___EVIL___
54 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
55 # include <hw/dos/winfcon.h>
58 char cpu_cpuid_vendor[13]={0};
59 struct cpu_cpuid_features cpu_cpuid_features = {0};
60 signed char cpu_basic_level = -1;
61 uint32_t cpu_cpuid_max = 0;
62 unsigned char cpu_flags = 0;
63 uint16_t cpu_tmp1 = 0;
66 #if TARGET_MSDOS == 32
67 /* we're obviously in 32-bit protected mode, or else this code would not be running at all */
68 /* Applies to: 32-bit DOS, Win32, Win95/98/ME/NT/2000/XP/etc. */
69 cpu_flags = CPU_FLAG_PROTECTED_MODE | CPU_FLAG_PROTECTED_MODE_32;
74 cpu_basic_level = cpu_basic_probe();
76 #if defined(TARGET_OS2)
77 /* OS/2 wouldn't let a program like myself touch control registers. Are you crazy?!? */
78 cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
79 #elif defined(TARGET_WINDOWS) && TARGET_MSDOS == 32
80 /* Under Windows 3.1 Win32s and Win 9x/ME/NT it's pretty much a given any attempt to work with
81 * control registers will fail. Win 9x/ME will silently ignore, and NT will fault it */
82 cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
83 #elif !defined(TARGET_WINDOWS) && TARGET_MSDOS == 32
84 /* 32-bit DOS: Generally yes we can, but only if we're Ring 0 */
95 if (s != 0) cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
99 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
100 /* Windows 3.0/3.1 specific: are we in 32-bit protected mode? 16-bit protected mode? real mode?
101 * real mode with v86 mode (does Windows even work that way?). Note that GetWinFlags only appeared
102 * in Windows 3.0. If we're under Windows 2.x we have to use alternative detection methods, or
103 * else assume Real Mode since Windows 2.x usually does run that way. But: There are 286 and 386
104 * enhanced versions of Windows 2.x, so how do we detect those?
106 * NTS: This code doesn't even run under Windows 2.x. If we patch the binary to report itself as
107 * a 2.x compatible and try to run it under Windows 2.11, the Windows kernel says it's
108 * "out of memory" and then everything freezes (?!?). */
110 # if TARGET_WINDOWS >= 30 /* If targeting Windows 3.0 or higher at compile time, then assume GetWinFlags() exists */
111 DWORD flags = GetWinFlags();
113 # 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 */
114 /* FIXME: If locating the function by name fails, what ordinal do we search by? */
115 DWORD (PASCAL FAR *__GetWinFlags)() = (LPVOID)GetProcAddress(GetModuleHandle("KERNEL"),"GETWINFLAGS");
116 if (__GetWinFlags != NULL) {
117 DWORD flags = __GetWinFlags();
118 MessageBox(NULL,"Found it","",MB_OK);
119 # else /* don't try. Windows 1.0 does not have GetWinFlags() and does not have any dynamic library functions either. There is
120 no GetModuleHandle, GetProcAddress, etc. */
124 cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
125 if (flags & WF_ENHANCED)
126 cpu_flags |= CPU_FLAG_PROTECTED_MODE | CPU_FLAG_PROTECTED_MODE_32;
127 else if (flags & WF_STANDARD)
128 cpu_flags |= CPU_FLAG_PROTECTED_MODE;
130 /* I highly doubt Windows 3.0 "real mode" every involves virtual 8086 mode, but
131 * just in case, check the machine status register. Since Windows 3.0 could run
132 * on an 8086, we must be cautious to do this test only on a 286 or higher */
133 else if (cpu_basic_level >= 2) {
142 /* if the PE bit is set, we're under Protected Mode
143 * that must mean that all of windows is in Real Mode, but overall
144 * the whole show is in virtual 8086 mode.
145 * We're assuming here that Windows would not lie to us about what mode is active.
147 * THEORY: Could this happen if Windows 3.0 were started in Real Mode
148 * while EMM386.EXE is resident and active? */
149 cpu_flags |= CPU_FLAG_V86_ACTIVE;
150 cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;