]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/cpu.c
GPL library! for sound!! ^o^
[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 //#include <hw/dos/dos.h>
44
45 /* DEBUG: Flush out calls that aren't there */
46 #ifdef TARGET_OS2
47 # define int86 ___EVIL___
48 # define ntvdm_RegisterModule ___EVIL___
49 # define ntvdm_UnregisterModule ___EVIL___
50 # define _dos_getvect ___EVIL___
51 # define _dos_setvect ___EVIL___
52 #endif
53
54 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
55 # include <hw/dos/winfcon.h>
56 #endif
57
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;
64
65 void cpu_probe() {
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;
70 #else
71         cpu_flags = 0;
72 #endif
73
74         cpu_basic_level = cpu_basic_probe();
75
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 */
85         {
86                 unsigned int s=0;
87
88                 __asm {
89                         xor     eax,eax
90                         mov     ax,cs
91                         and     ax,3
92                         mov     s,eax
93                 }
94
95                 if (s != 0) cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
96         }
97 #endif
98
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?
105          *
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 (?!?). */
109         {
110 # if TARGET_WINDOWS >= 30 /* If targeting Windows 3.0 or higher at compile time, then assume GetWinFlags() exists */
111                 DWORD flags = GetWinFlags();
112                 if (1) {
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. */
121                 if (0) {
122 # endif
123                         if (WF_PMODE) {
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;
129                         }
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) {
134                                 unsigned int tmp=0;
135
136                                 __asm {
137                                         .286
138                                         smsw tmp
139                                 }
140
141                                 if (tmp & 1) {
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.
146                                          *
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;
151                                 }
152                         }
153                 }
154         }
155 #endif
156 }
157