2 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
3 /* Win16: We're probably on a 386, but we could be on a 286 if Windows 3.1 is in standard mode.
4 * If the user manages to run us under Windows 3.0, we could also run in 8086 real mode.
5 * We still do the tests so the Windows API cannot deceive us, but we still need GetWinFlags
6 * to tell between 8086 real mode + virtual8086 mode and protected mode. */
12 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
20 #include <hw/cpu/cpu.h>
21 #include <hw/dos/dos.h>
22 #include <hw/dos/doswin.h>
23 #include <hw/cpu/cpusse.h>
25 /* DEBUG: Flush out calls that aren't there */
27 # define int86 ___EVIL___
28 # define ntvdm_RegisterModule ___EVIL___
29 # define ntvdm_UnregisterModule ___EVIL___
30 # define _dos_getvect ___EVIL___
31 # define _dos_setvect ___EVIL___
34 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
35 # include <hw/dos/winfcon.h>
38 unsigned char cpu_sse_usable = 0;
39 unsigned char cpu_sse_usable_probed = 0;
40 unsigned char cpu_sse_usable_can_turn_on = 0;
41 unsigned char cpu_sse_usable_can_turn_off = 0;
42 const char* cpu_sse_unusable_reason = NULL;
43 const char* cpu_sse_usable_detection_method = NULL;
45 #if !defined(TARGET_WINDOWS) && !defined(TARGET_OS2)
46 static unsigned char faulted = 0;
48 static void __declspec(naked) fault_int6_vec() {
49 /* the test routine executes a XORPS xmm0,xmm0 (3 bytes long).
50 * if we just IRET the CPU will go right back and execute it again */
51 # if TARGET_MSDOS == 32
79 # if TARGET_MSDOS == 16 && !defined(TARGET_WINDOWS) && !defined(TARGET_OS2)
80 unsigned int _cdecl cpu_dpmi_win9x_sse_test();
83 # if TARGET_MSDOS == 32 && !defined(TARGET_OS2)
84 static void __declspec(naked) fault_int6() { /* DPMI exception handler */
92 add dword ptr [esp+8+12],3 /* +3 bytes for 'xorps xmm0,xmm0' */
99 #elif defined(TARGET_WINDOWS) && TARGET_MSDOS == 16 && !defined(TARGET_OS2)
100 static unsigned char faulted = 0;
102 /* SS:SP + 12h = SS (fault)
103 * SS:SS + 10h = SP (fault)
104 * SS:SP + 0Eh = FLAGS (fault)
105 * SS:SP + 0Ch = CS (fault)
106 * SS:SP + 0Ah = IP (fault)
107 * SS:SP + 08h = handle (internal)
108 * SS:SP + 06h = interrupt number
109 * SS:SP + 04h = AX (to load into DS)
110 * SS:SP + 02h = CS (toolhelp.dll)
111 * SS:SP + 00h = IP (toolhelp.dll)
113 * to pass exception on: RETF
114 * to restart instruction: clear first 10 bytes of the stack, and IRET (??) */
115 static void __declspec(naked) fault_int6_toolhelp() {
123 /* is this for INT 6h? */
124 cmp word ptr [bp+6+6],6 /* SS:SP + 06h = interrupt number */
127 /* set the faulted flag, change the return address, then IRET directly back */
131 add word ptr [bp+6+10],3
135 add sp,0Ah /* throw away handle, int number, and CS:IP return */
138 /* tell ToolHelp we didn't handle the interrupt */
143 static void __declspec(naked) fault_int6() { /* DPMI exception handler */
153 add word ptr [bp+6+6],3
162 /* check if SSE is usable. Just because CPUID says it's there
163 * doesn't mean the OS enabled it.
165 * if do_enable is nonzero and the function detects that it's
166 * possible, the function will enable SSE and return success */
167 int cpu_check_sse_is_usable() {
168 if (!cpu_sse_usable_probed) {
169 cpu_sse_usable_probed = 1;
171 cpu_sse_unusable_reason = "";
172 cpu_sse_usable_detection_method = "None";
173 cpu_sse_usable_can_turn_off = 0;
174 cpu_sse_usable_can_turn_on = 0;
176 if (cpu_basic_level < 0) cpu_probe();
178 if (!(cpu_flags & CPU_FLAG_CPUID)) {
179 cpu_sse_unusable_reason = "No CPUID available";
182 if (!(cpu_cpuid_features.a.raw[2] & (1UL << 25UL))) {
183 cpu_sse_unusable_reason = "CPUID indicates SSE is not present";
187 #ifdef TARGET_WINDOWS
188 /* ==================== RUNNING WITHIN WINDOWS =================== */
189 /* Within Windows, we have no way to enable SSE if the OS kernel doesn't support it.
190 * So we first must learn whether or not the OS supports it.
191 * Guide: Windows NT/2000/XP/Vista/etc... you can use IsProcessorFeaturePresent()
192 * Windows 95/98/ME... there is no way other than attempting the instruction to see if it causes a fault.
193 * Windows 3.1/3.0... NO. And you cannot enable it either! Buuuuut.... if the Windows 3.1 binary is run
194 * under Windows XP/ME/98 and the kernel has SSE enabled, then it is possible to use SSE instructions.
196 * NOTE: Any Windows 9x kernel prior to 98SE does not support SSE, but if something happens to enable it in CR4
197 * prior to Windows taking control, then it will stay enabled while Windows is running. Those versions
198 * treat the SSE enable bit as unknown and they don't change it. BUT also realize that the SSE registers
199 * are not saved and restored by the kernel scheduler! If you are the only process that will be using SSE
200 * that is fine, but if two or more tasks try to use SSE there will be serious conflicts and possibly
203 * Reading CR4 to determine availability has the same effects as it does for the MS-DOS code, either
204 * nonsense values (Windows 9x/ME) or it causes a fault (Windows NT). */
206 # if TARGET_MSDOS == 32
208 /* Windows 3.0/3.1 Win386: the underlying system is 16-bit and we're 32-bit through an extender */
209 cpu_sse_unusable_reason = "SSE support for Windows 3.x Win386 is not implemented";
213 BOOL (WINAPI *__IsProcFeaturePresent)(DWORD f) = NULL;
215 if (windows_mode == WINDOWS_NT)
216 __IsProcFeaturePresent = (BOOL (WINAPI *)(DWORD))GetProcAddress(GetModuleHandle("KERNEL32.DLL"),"IsProcessorFeaturePresent");
218 if (__IsProcFeaturePresent != NULL) {
219 cpu_sse_usable_detection_method = "IsProcessorFeaturePresent [WinNT]";
220 printf("Using IsProcessorFeaturePresent\n");
221 if (!__IsProcFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE))
222 cpu_sse_unusable_reason = "Windows NT HAL says SSE not enabled";
229 cpu_sse_usable_detection_method = "Executing SSE to see if it causes a fault [Win31s/9x/ME/NT]";
231 /* we just have to try */
239 __except (EXCEPTION_EXECUTE_HANDLER) {
244 cpu_sse_unusable_reason = "Windows 3.1/9x/ME kernel does not have SSE enabled";
253 # else /* TARGET_MSDOS == 16 */
254 if ((windows_mode == WINDOWS_STANDARD || windows_mode == WINDOWS_ENHANCED) && windows_version < 0x35F) {
255 /* Windows 3.0/3.1: We can abuse the DPMI server to hook INT 6h exceptions.
256 * Very clean, and it does not require TOOLHELP.DLL. But it doesn't work under Windows 9x/ME.
257 * The DPMI call silently fails and KERNEL32.DLL catches the fault without passing it on to us. */
260 op = win16_getexhandler(6);
261 cpu_sse_usable_detection_method = "Hooking INT 6, executing SSE to see if it causes a fault [Win16]";
262 if (win16_setexhandler(6,fault_int6)) {
269 win16_setexhandler(6,op);
272 win16_setexhandler(6,op);
273 cpu_sse_unusable_reason = "Unable to hook INT 6 by DPMI";
278 cpu_sse_unusable_reason = "Windows 3.x kernel does not have SSE enabled";
285 /* Windows 9x/ME. Abusing DPMI as in Windows 3.1 doesn't work, the calls silently fail. But Microsoft
286 * apparently made sure the TOOLHELP API functions do their job, so we use that technique. It even works
287 * under NTVDM.EXE in Windows NT/2000/XP */
288 else if ((windows_mode == WINDOWS_STANDARD || windows_mode == WINDOWS_ENHANCED || windows_mode == WINDOWS_NT) && ToolHelpInit()) {
289 cpu_sse_usable_detection_method = "InterruptRegister/TOOLHELP.DLL, executing SSE to see if it causes a fault [Win16]";
290 if (__InterruptRegister(NULL,MakeProcInstance((FARPROC)fault_int6_toolhelp,_win_hInstance))) {
298 if (!__InterruptUnRegister(NULL))
299 MessageBox(NULL,"WARNING: Unable to unregister interrupt","",MB_OK);
302 cpu_sse_unusable_reason = "Windows 9x/ME/NT kernel does not have SSE enabled";
310 MessageBox(NULL,"WARNING: Unable to register interrupt","",MB_OK);
314 cpu_sse_unusable_reason = "This library does not have support for detecting SSE from Win16 under Windows 3.0 Real Mode";
318 #elif defined(TARGET_OS2)
319 /* ==================== RUNNING AS OS/2 APP ====================== */
321 cpu_sse_unusable_reason = "Assuming not present";
323 cpu_sse_usable_detection_method = "None";
324 cpu_sse_usable_can_turn_on = 0;
325 cpu_sse_usable_can_turn_off = 0;
327 /* ==================== RUNNING AS MS-DOS APP ==================== */
330 if ((windows_mode == WINDOWS_STANDARD || windows_mode == WINDOWS_ENHANCED) && TARGET_MSDOS == 16) {
331 #if TARGET_MSDOS == 16
332 /* Windows 9x/ME DOS box.
333 * we can't read the control registers, and hooking INT 6 doesn't work. If the SSE test causes
334 * an Invalid Opcode exception Windows 9x will NOT forward the exception down to us!
335 * Same with Windows 3.0/3.1 Standard/Enhanced mode!
337 * But, Windows 9x does offer DPMI. So to carry out the test, we use DPMI to enter protected mode,
338 * set up DPMI exception handlers, perform the test, and then use DPMI to exit back to real mode
341 * Note that this code is not necessary for Windows NT/2000/XP/Vista/etc. as NTVDM.EXE contains
342 * code to forward the Invalid Opcode exception to us if it sees that we hooked it */
343 cpu_sse_usable_detection_method = "Hook INT 6h, execute SSE to see if it causes a fault [DPMI under Win 3.1/9x/ME DOS box]";
346 /* to carry out this test, we must enter protected mode through DPMI. we must initialize
347 * DPMI if not already done, or if the caller has already gone through DPMI, we must
348 * enter using whatever bit width he chose.
350 * NOTE: This means then, that if the calling program needs DPMI the program must have
351 * set it's own preferences up prior to calling this function, because otherwise
352 * you're going to be stuck with our preferences. Now maybe if DPMI had thought
353 * about something like... I dunno... a way to un-initialize DPMI for itself,
354 * maybe we wouldn't have this problem, would we? */
355 if (dpmi_entered == 0)
356 dpmi_enter(DPMI_ENTER_AUTO);
358 if (dpmi_entered != 0) {
359 unsigned int reason = cpu_dpmi_win9x_sse_test();
364 else if (reason == 1) {
365 /* test OK, sse not available */
366 cpu_sse_unusable_reason = "SSE is currently disabled, nobody has enabled it yet";
370 cpu_sse_unusable_reason = "Unable to enter DPMI protected mode";
374 cpu_sse_unusable_reason = "As an MS-DOS application I have no way to test for SSE from within Win 3.1/9x/ME DOS box, DPMI is not available";
378 else if (cpu_v86_active || windows_mode == WINDOWS_NT ||
379 ((windows_mode == WINDOWS_STANDARD || windows_mode == WINDOWS_ENHANCED) && TARGET_MSDOS == 32)) {
380 /* pure DOS mode with virtual 8086 mode, or Windows NT DOS box.
381 * we can't read the control registers (or under EMM386.EXE we can, but within v86 mode it's
382 * not wise to assume that we can). Note that DOS32a is able to catch Invalid Opcode exceptions,
383 * even from within Windows 9x/ME/NT, so when compiled as a 32-bit DOS app we also need to use DPMI functions
384 * "set exception handler" to ensure that we catch the fault. */
386 #if TARGET_MSDOS == 32
390 cpu_sse_usable_detection_method = "Hook INT 6h, execute SSE to see if it causes a fault [MS-DOS]";
392 #if TARGET_MSDOS == 32
393 oh_ex = dpmi_getexhandler(6);
394 dpmi_setexhandler(6,(void far*)fault_int6);
397 oh = _dos_getvect(6);
398 _dos_setvect(6,(void far*)fault_int6_vec);
407 #if TARGET_MSDOS == 32
408 dpmi_setexhandler(6,oh_ex);
411 /* TODO: If we're in pure DOS mode, and virtual 8086 mode is active, and we know VCPI is present,
412 * then it is possible for us to enable/disable SSE by switching into protected mode via VCPI */
415 cpu_sse_unusable_reason = "SSE is currently disabled, nobody has enabled it yet";
422 /* pure DOS mode. we can read the control registers without crashing */
425 cpu_sse_usable_detection_method = "80386 MOV CR4 [MS-DOS]";
437 cpu_sse_unusable_reason = "SSE is currently disabled";
440 cpu_sse_usable_can_turn_on = 1;
441 cpu_sse_usable_can_turn_off = 1;
443 #endif /* TARGET_WINDOWS */
446 return cpu_sse_usable;
449 int cpu_sse_disable() {
450 #if !defined(TARGET_WINDOWS) && !defined(TARGET_OS2)
451 uint32_t confidence=0;
454 if (!(cpu_flags & CPU_FLAG_CPUID))
456 if (!(cpu_cpuid_features.a.raw[2] & (1UL << 25UL)))
458 if (!cpu_sse_usable_can_turn_off)
461 #if defined(TARGET_WINDOWS) || defined(TARGET_OS2)
462 return 0; /* it's very unlikely we could ever touch the control registers from within Windows */
463 /* FIXME: Maybe as a Win32 app under Windows 9x we could try? */
468 and eax,0xFFFFFDFF /* bit 9 */
470 mov ebx,eax /* remember what we wrote */
476 mov confidence,eax /* EAX = nonzero if write didn't work */
480 cpu_sse_unusable_reason = "Attempting to write CR4 failed";
489 int cpu_sse_enable() {
490 #if !defined(TARGET_WINDOWS) && !defined(TARGET_OS2)
491 uint32_t confidence=0;
494 if (!(cpu_flags & CPU_FLAG_CPUID))
496 if (!(cpu_cpuid_features.a.raw[2] & (1UL << 25UL)))
498 if (!cpu_sse_usable_can_turn_on)
501 #if defined(TARGET_WINDOWS) || defined(TARGET_OS2)
502 return 0; /* it's very unlikely we could ever touch the control registers from within Windows */
503 /* FIXME: Maybe as a Win32 app under Windows 9x we could try? */
508 or eax,0x200 /* bit 9 */
510 mov ebx,eax /* remember what we wrote */
516 mov confidence,eax /* EAX = nonzero if write didn't work */
520 cpu_sse_unusable_reason = "Attempting to write CR4 failed";