]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/cpu.c
8e111690b70a8ce1a4ff79799ae8756467efa3b0
[16.git] / src / lib / doslib / cpu.c
1 /* cpu.c
2  *
3  * Runtime CPU detection library.
4  * (C) 2009-2012 Jonathan Campbell.
5  * Hackipedia DOS library.
6  *
7  * This code is licensed under the LGPL.
8  * <insert LGPL legal text here>
9  *
10  * Compiles for intended target environments:
11  *   - MS-DOS
12  *   - Windows 3.0/3.1/95/98/ME
13  *   - Windows NT 3.1/3.51/4.0/2000/XP/Vista/7
14  *   - OS/2 16-bit
15  *   - OS/2 32-bit
16  *
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. */
21
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? */
23
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. */
29 # include <windows.h>
30 # include <toolhelp.h>
31 #endif
32
33 #include <stdio.h>
34 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <assert.h>
39 #include <fcntl.h>
40 #include <dos.h>
41
42 #include "src/lib/doslib/cpu.h"
43
44 /* DEBUG: Flush out calls that aren't there */
45 #ifdef TARGET_OS2
46 # define int86 ___EVIL___
47 # define ntvdm_RegisterModule ___EVIL___
48 # define ntvdm_UnregisterModule ___EVIL___
49 # define _dos_getvect ___EVIL___
50 # define _dos_setvect ___EVIL___
51 #endif
52
53 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
54 # include <hw/dos/winfcon.h>
55 #endif
56
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;
63
64 void cpu_probe() {
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;
69 #else
70         cpu_flags = 0;
71 #endif
72
73         cpu_basic_level = cpu_basic_probe();
74
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 */
84         {
85                 unsigned int s=0;
86
87                 __asm {
88                         xor     eax,eax
89                         mov     ax,cs
90                         and     ax,3
91                         mov     s,eax
92                 }
93
94                 if (s != 0) cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
95         }
96 #endif
97
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?
104          *
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 (?!?). */
108         {
109 # if TARGET_WINDOWS >= 30 /* If targeting Windows 3.0 or higher at compile time, then assume GetWinFlags() exists */
110                 DWORD flags = GetWinFlags();
111                 if (1) {
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. */
120                 if (0) {
121 # endif
122                         if (WF_PMODE) {
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;
128                         }
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) {
133                                 unsigned int tmp=0;
134
135                                 __asm {
136                                         .286
137                                         smsw tmp
138                                 }
139
140                                 if (tmp & 1) {
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.
145                                          *
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;
150                                 }
151                         }
152                 }
153         }
154 #endif
155 }
156
157 int cpu_basic_probe()
158 {
159         __asm
160         {
161         push            bx
162         push            cx
163         push            dx
164         push            si
165         push            di
166         push            bp
167 // BUGFIX: Calling near a function that returns far is like taking a long walk off a short pier
168 //      push            cs
169
170 //      call            cpu_basic_probe_f_
171
172         pop             bp
173         pop             di
174         pop             si
175         pop             dx
176         pop             cx
177         pop             bx
178         //retnative
179         }
180         return 0;
181 }