]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/hw/dos/doswin.c
added a bunch of things~ and midi stuff~
[16.git] / src / lib / doslib / hw / dos / doswin.c
1
2 #ifdef TARGET_WINDOWS
3 # include <windows.h>
4 #endif
5
6 #include <stdio.h>
7 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stddef.h>
11 #include <unistd.h>
12 #include <malloc.h>
13 #include <assert.h>
14 #include <fcntl.h>
15 #include <dos.h>
16
17 #include <hw/cpu/cpu.h>
18 #include <hw/dos/dos.h>
19 #include <hw/dos/doswin.h>
20 #include <hw/dos/dosntvdm.h>
21
22 /* DEBUG: Flush out calls that aren't there */
23 #ifdef TARGET_OS2
24 # define int86 ___EVIL___
25 # define int386 ___EVIL___
26 # define ntvdm_RegisterModule ___EVIL___
27 # define ntvdm_UnregisterModule ___EVIL___
28 # define _dos_getvect ___EVIL___
29 # define _dos_setvect ___EVIL___
30 #endif
31
32 const char *windows_version_method = NULL;
33
34 /* return value:
35  *   0 = not running under Windows
36  *   1 = running under Windows */
37 const char *windows_emulation_comment_str = NULL;
38 uint8_t windows_emulation = 0;
39 uint16_t windows_version = 0;           /* NOTE: 0 for Windows NT */
40 uint8_t windows_mode = 0;
41 uint8_t windows_init = 0;
42
43 const char *windows_mode_strs[WINDOWS_MAX] = {
44         "None",
45         "Real",
46         "Standard",
47         "Enhanced",
48         "NT",
49         "OS/2"
50 };
51
52 /* TESTING (whether or not it correctly detects the presence of Windows):
53  *    Note that in some columns the API returns insufficient information and the
54  *    API has to use it's best guess on the correct value, which might be
55  *    inaccurate or wrong (marked: GUESSES).
56  *
57  *    For Windows NT/2000/XP/Vista/7 tests, the code does not have any way of
58  *    knowing (yet) which version of the NT kernel is involved, so the best
59  *    it can offer is "I am running under NT" (marked as ONLY NT)
60  *
61  *    OS, shell, configuration & mode         Detects   Correct mode    Correct version
62  *    Microsoft Windows 3.0 (DOSBox 0.74)
63  *       386 Enhanced Mode                    YES       YES             YES
64  *       286 Standard Mode                    YES       GUESSES         YES
65  *       8086 Real Mode                       YES       GUESSES         YES
66  *    Microsoft Windows 3.1 (DOSBox 0.74)
67  *       386 Enhanced Mode                    YES       YES             YES
68  *       286 Standard Mode                    YES       YES             YES
69  *    Microsoft Windows 3.11 (DOSBox 0.74)
70  *       386 Enhanced Mode                    YES       YES             YES*
71  *       286 Standard Mode                    YES       YES             YES*
72  *         * = Despite being v3.11 it still reports itself as v3.1
73  *    Microsoft Windows 95 (4.00.950) (DOS 7.00) (Qemu)
74  *       Normal                               YES       YES*            YES (4.0)
75  *       Safe mode                            YES       YES*            YES (4.0)
76  *       Prevent DOS apps detecting Windows   NO        -               -
77  *         * = Reports self as "enhanced mode" which is really the only mode supported
78  *    Microsoft Windows 95 OSR2.5 (4.00.950 C) (DOS 7.10) (Qemu)
79  *       Normal                               YES       YES             YES (4.0)
80  *       Safe mode                            YES       YES             YES (4.0)
81  *    Microsoft Windows 95 SP1 (4.00.950a) (DOS 7.00) (Qemu)
82  *       Normal                               YES       YES             YES (4.0)
83  *       Safe mode                            YES       YES             YES (4.0)
84  *    Microsoft Windows 98 (4.10.1998) (DOS 7.10) (Qemu)
85  *       Normal                               YES       YES             YES (4.10)
86  *       Safe mode                            YES       YES             YES (4.10)
87  *    Microsoft Windows 98 SE (4.10.2222 A) (DOS 7.10) (Qemu)
88  *       Normal                               YES       YES             YES (4.10)
89  *       Safe mode                            YES       YES             YES (4.10)
90  *    Microsoft Windows ME (4.90.3000) (DOS 8.00) (Qemu)
91  *       Normal                               YES       YES             YES (4.90)
92  *       Safe mode                            YES       YES             YES (4.90)
93  *    Microsoft Windows 2000 Professional (5.00.2195) (VirtualBox)
94  *       Normal                               YES       N/A             ONLY NT
95  *    Microsoft Windows XP Professional (5.1.2600) (VirtualBox)
96  *       Normal                               YES       N/A             ONLY NT
97  *    Microsoft Windows XP Professional SP1 (5.1.2600) (VirtualBox)
98  *       Normal                               YES       N/A             ONLY NT
99  *    Microsoft Windows XP Professional SP2 (5.1.2600) (VirtualBox)
100  *       Normal                               YES       N/A             ONLY NT
101  *    Microsoft Windows XP Professional SP3 (5.1.2600) (VirtualBox)
102  *       Normal                               YES       N/A             ONLY NT
103 */
104
105 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 32
106 /* it's nice to know if we're running under WINE (and therefore possibly Linux)
107  * as opposed to real Windows, so that we can adjust our techniques accordingly.
108  * I doubt for example that WINE would support Windows NT's NTVDM.EXE BOP codes,
109  * or that our Win9x compatible VxD enumeration would know not to try enumerating
110  * drivers. */
111 void win32_probe_for_wine() { /* Probing for WINE from the Win32 layer */
112         HMODULE ntdll;
113
114         ntdll = LoadLibrary("NTDLL.DLL");
115         if (ntdll) {
116                 const char *(__stdcall *p)() = (const char *(__stdcall *)())GetProcAddress(ntdll,"wine_get_version");
117                 if (p != NULL) {
118                         windows_emulation = WINEMU_WINE;
119                         windows_emulation_comment_str = p(); /* and the function apparently returns a string */
120                 }
121                 FreeLibrary(ntdll);
122         }
123 }
124 #elif defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
125 void win16_probe_for_wine() { /* Probing for WINE from the Win16 layer */
126         DWORD callw,retv;
127
128         if (!genthunk32_init()) return;
129         if (genthunk32w_ntdll == 0) return;
130
131         callw = __GetProcAddress32W(genthunk32w_ntdll,"wine_get_version");
132         if (callw == 0) return;
133
134         retv = __CallProcEx32W(CPEX_DEST_STDCALL/*nothing to convert*/,0/*0 param*/,callw);
135         if (retv == 0) return;
136
137         windows_emulation = WINEMU_WINE;
138         {
139                 /* NTS: We assume that WINE, just like real Windows, will not move or relocate
140                  *      NTDLL.DLL and will not move the string it just returned. */
141                 /* TODO: You need a function the host program can call to free the selector
142                  *       you allocated here, in case it wants to reclaim resources */
143                 uint16_t sel;
144                 uint16_t myds=0;
145                 __asm mov myds,ds
146                 sel = AllocSelector(myds);
147                 if (sel != 0) {
148                         /* the selector is directed at the string, then retained in this
149                          * code as a direct FAR pointer to WINE's version string */
150                         SetSelectorBase(sel,retv);
151                         SetSelectorLimit(sel,0xFFF);    /* WINE's version string is rarely longer than 14 chars */
152                         windows_emulation_comment_str = MK_FP(sel,0);
153                 }
154         }
155 }
156 #endif
157
158 int detect_windows() {
159 #if defined(TARGET_WINDOWS)
160 # if TARGET_MSDOS == 32
161 #  ifdef WIN386
162         /* Windows 3.0/3.1 with Win386 */
163         if (!windows_init) {
164                 DWORD raw;
165
166                 windows_emulation = 0;
167                 windows_init = 1;
168                 windows_mode = WINDOWS_ENHANCED; /* most likely scenario is Windows 3.1 386 enhanced mode */
169
170                 raw = GetVersion();
171                 windows_version_method = "GetVersion";
172                 windows_version = (LOBYTE(LOWORD(raw)) << 8) | HIBYTE(LOWORD(raw));
173                 /* NTS: Microsoft documents GetVersion() as leaving bit 31 unset for NT, bit 31 set for Win32s and Win 9x/ME.
174                  *      But that's not what the Win16 version of the function does! */
175
176                 if (dos_version == 0) probe_dos();
177
178                 /* Windows 3.1/9x/ME */
179                 raw = GetWinFlags();
180                 if (raw & WF_PMODE) {
181                         if (raw & WF_ENHANCED)
182                                 windows_mode = WINDOWS_ENHANCED;
183                         else/* if (raw & WF_STANDARD)*/
184                                 windows_mode = WINDOWS_STANDARD;
185                 }
186                 else {
187                         windows_mode = WINDOWS_REAL;
188                 }
189
190                 /* NTS: All 32-bit Windows systems since Windows NT 3.51 and Windows 95 return
191                  *      major=3 minor=95 when Win16 applications query the version number. The only
192                  *      exception to that rule is Windows NT 3.1, which returns major=3 minor=10,
193                  *      the same version number returned by Windows 3.1. */
194                 if (windows_mode == WINDOWS_ENHANCED &&
195                         (dos_version >= 0x510 && dos_version <= 0x57f)/* MS-DOS v5.50 */ &&
196                         (windows_version == 0x035F /* Windows NT 4/2000/XP/Vista/7/8 */ ||
197                          windows_version == 0x030A /* Windows NT 3.1/3.5x */)) {
198                         /* if the real DOS version is 5.50 then we're under NT */
199                         windows_mode = WINDOWS_NT;
200                 }
201
202                 switch (dos_version>>8) {
203                         case 10:        /* OS/2 1.x */
204                         case 20:        /* OS/2 2.x (low=0), and OS/2 Warp 3 (low=30), and OS/2 Warp 4 (low=40) */
205                                 windows_version_method = "Deduce from DOS version";
206                                 windows_version = dos_version;
207                                 windows_mode = WINDOWS_OS2;
208                                 break;
209                 };
210         }
211 #  elif TARGET_WINDOWS >= 40 || defined(WINNT)
212         /* Windows 95/98/ME/XP/2000/NT/etc. and Windows NT builds: We don't need to do anything.
213          * The fact we're running means Windows is present */
214         /* TODO: Clarify which Windows: Are we running under NT? or 9X/ME? What version? */
215         if (!windows_init) {
216                 OSVERSIONINFO ovi;
217
218                 windows_emulation = 0;
219                 windows_init = 1;
220                 memset(&ovi,0,sizeof(ovi));
221                 ovi.dwOSVersionInfoSize = sizeof(ovi);
222                 GetVersionEx(&ovi);
223
224                 windows_version_method = "GetVersionEx";
225                 windows_version = (ovi.dwMajorVersion << 8) | ovi.dwMinorVersion;
226                 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT)
227                         windows_mode = WINDOWS_NT;
228                 else
229                         windows_mode = WINDOWS_ENHANCED; /* Windows 3.1 Win32s, or Windows 95/98/ME */
230
231                 win32_probe_for_wine();
232         }
233 #  elif TARGET_WINDOWS == 31
234         /* Windows 3.1 with Win32s target build. Note that such programs run in the Win32 layer on Win95/98/ME/NT/2000/etc. */
235         /* TODO: Clarify which Windows, using the limited set of functions available under Win32s, or perhaps using GetProcAddress
236          *       to use the later GetVersionEx() functions offered by Win95/98/NT/etc. */
237         if (!windows_init) {
238                 DWORD raw;
239
240                 windows_emulation = 0;
241                 windows_init = 1;
242                 windows_mode = WINDOWS_ENHANCED; /* Assume Windows 3.1 386 Enhanced Mode. This 32-bit app couldn't run otherwise */
243
244                 raw = GetVersion();
245                 windows_version_method = "GetVersion";
246                 windows_version = (LOBYTE(LOWORD(raw)) << 8) | HIBYTE(LOWORD(raw));
247                 if (!(raw & 0x80000000UL)) { /* FIXME: Does this actually work? */
248                         /* Windows NT/2000/XP/etc */
249                         windows_mode = WINDOWS_NT;
250                 }
251
252                 /* TODO: GetProcAddress() GetVersionEx() and get the REAL version number from Windows */
253
254                 win32_probe_for_wine();
255         }
256 #  else
257 #   error Unknown 32-bit Windows variant
258 #  endif
259 # elif TARGET_MSDOS == 16
260 #  if TARGET_WINDOWS >= 30
261         /* Windows 3.0/3.1, what we then want to know is what mode we're running under: Real? Standard? Enhanced?
262          * The API function GetWinFlags() only exists in 3.0 and higher, it doesn't exist under 2.x */
263         /* TODO */
264         if (!windows_init) {
265                 DWORD raw;
266
267                 windows_emulation = 0;
268                 windows_init = 1;
269                 windows_mode = WINDOWS_ENHANCED; /* most likely scenario is Windows 3.1 386 enhanced mode */
270
271                 raw = GetVersion();
272                 windows_version_method = "GetVersion";
273                 windows_version = (LOBYTE(LOWORD(raw)) << 8) | HIBYTE(LOWORD(raw));
274                 /* NTS: Microsoft documents GetVersion() as leaving bit 31 unset for NT, bit 31 set for Win32s and Win 9x/ME.
275                  *      But that's not what the Win16 version of the function does! */
276
277                 if (dos_version == 0) probe_dos();
278
279                 /* Windows 3.1/9x/ME */
280                 raw = GetWinFlags();
281                 if (raw & WF_PMODE) {
282                         if (raw & WF_ENHANCED)
283                                 windows_mode = WINDOWS_ENHANCED;
284                         else/* if (raw & WF_STANDARD)*/
285                                 windows_mode = WINDOWS_STANDARD;
286                 }
287                 else {
288                         windows_mode = WINDOWS_REAL;
289                 }
290
291                 /* NTS: All 32-bit Windows systems since Windows NT 3.51 and Windows 95 return
292                  *      major=3 minor=95 when Win16 applications query the version number. The only
293                  *      exception to that rule is Windows NT 3.1, which returns major=3 minor=10,
294                  *      the same version number returned by Windows 3.1. */
295                 if (windows_mode == WINDOWS_ENHANCED &&
296                         (dos_version >= 0x510 && dos_version <= 0x57f)/* MS-DOS v5.50 */ &&
297                         (windows_version == 0x035F /* Windows NT 4/2000/XP/Vista/7/8 */ ||
298                          windows_version == 0x030A /* Windows NT 3.1/3.5x */)) {
299                         /* if the real DOS version is 5.50 then we're under NT */
300                         windows_mode = WINDOWS_NT;
301                 }
302
303                 switch (dos_version>>8) {
304                         case 10:        /* OS/2 1.x */
305                         case 20:        /* OS/2 2.x (low=0), and OS/2 Warp 3 (low=30), and OS/2 Warp 4 (low=40) */
306                                 windows_version_method = "Deduce from DOS version";
307                                 windows_version = dos_version;
308                                 windows_mode = WINDOWS_OS2;
309                                 break;
310                 };
311
312                 /* If we're running under Windows 9x/ME or Windows NT/2000 we can thunk our way into
313                  * the Win32 world and call various functions to get a more accurate picture of the
314                  * Windows system we are running on */
315                 /* NTS: Under Windows NT 3.51 or later this technique is the only way to get the
316                  *      true system version number. The Win16 GetVersion() will always return
317                  *      some backwards compatible version number except for NT 3.1:
318                  *
319                  *                   Win16             Win32
320                  *               +==========================
321                  *      NT 3.1   |   3.1               3.1
322                  *      NT 3.51  |   3.1               3.51
323                  *      NT 4.0   |   3.95              4.0
324                  *      2000     |   3.95              5.0
325                  *      XP       |   3.95              5.1
326                  *      Vista    |   3.95              6.0
327                  *      7        |   3.95              6.1
328                  *      8        |   3.95              6.2
329                  *
330                  */
331                 if (genthunk32_init() && genthunk32w_kernel32_GetVersionEx != 0) {
332                         OSVERSIONINFO osv;
333
334                         memset(&osv,0,sizeof(osv));
335                         osv.dwOSVersionInfoSize = sizeof(osv);
336                         if (__CallProcEx32W(CPEX_DEST_STDCALL | 1/* convert param 1*/,
337                                 1/*1 param*/,genthunk32w_kernel32_GetVersionEx,
338                                 (DWORD)((void far*)(&osv))) != 0UL) {
339                                 windows_version_method = "GetVersionEx [16->32 CallProcEx32W]";
340                                 windows_version = (osv.dwMajorVersion << 8) | osv.dwMinorVersion;
341                                 if (osv.dwPlatformId == 2/*VER_PLATFORM_WIN32_NT*/)
342                                         windows_mode = WINDOWS_NT;
343                                 else
344                                         windows_mode = WINDOWS_ENHANCED;
345                         }
346                 }
347
348                 win16_probe_for_wine();
349         }
350 #  elif TARGET_WINDOWS >= 20
351         /* Windows 2.x or higher. Use GetProcAddress() to locate GetWinFlags() if possible, else assume real mode
352          * and find some other way to detect if we're running under the 286 or 386 enhanced versions of Windows 2.11 */
353         /* TODO */
354         if (!windows_init) {
355                 windows_init = 1;
356                 windows_version = 0x200;
357                 windows_mode = WINDOWS_REAL;
358                 windows_version_method = "Assuming";
359         }
360 #  else
361         /* Windows 1.x. No GetProcAddress, no GetWinFlags. Assume Real mode. */
362         /* TODO: How exactly DO you get the Windows version in 1.1? */
363         if (!windows_init) {
364                 windows_init = 1;
365                 windows_version = 0x101; /* Assume 1.1 */
366                 windows_mode = WINDOWS_REAL;
367                 windows_version_method = "Assuming";
368         }
369 #  endif
370 # else
371 #  error Unknown Windows bit target
372 # endif
373 #elif defined(TARGET_OS2)
374         /* OS/2 16-bit or 32-bit. Obviously as something compiled for OS/2, we're running under OS/2 */
375         if (!windows_init) {
376                 windows_version_method = "I'm an OS/2 program, therefore the environment is OS/2";
377                 windows_version = dos_version;
378                 windows_mode = WINDOWS_OS2;
379                 windows_init = 1;
380         }
381 #else
382         /* MS-DOS 16-bit or 32-bit. MS-DOS applications must try various obscure interrupts to detect whether Windows is running */
383         /* TODO: How can we detect whether we're running under OS/2? */
384         if (!windows_init) {
385                 union REGS regs;
386
387                 windows_version = 0;
388                 windows_mode = 0;
389                 windows_init = 1;
390
391                 switch (dos_version>>8) {
392                         case 10:        /* OS/2 1.x */
393                         case 20:        /* OS/2 2.x (low=0), and OS/2 Warp 3 (low=30), and OS/2 Warp 4 (low=40) */
394                                 windows_version_method = "Deduce from DOS version";
395                                 windows_version = dos_version;
396                                 windows_mode = WINDOWS_OS2;
397                                 break;
398                 };
399
400                 if (windows_version == 0) {
401                         regs.w.ax = 0x160A;
402 #if TARGET_MSDOS == 32
403                         int386(0x2F,&regs,&regs);
404 #else
405                         int86(0x2F,&regs,&regs);
406 #endif
407                         if (regs.w.ax == 0x0000 && regs.w.bx >= 0x300 && regs.w.bx <= 0x700) { /* Windows 3.1 */
408                                 windows_version = regs.w.bx;
409                                 switch (regs.w.cx) {
410                                         case 0x0002: windows_mode = WINDOWS_STANDARD; break;
411                                         case 0x0003: windows_mode = WINDOWS_ENHANCED; break;
412                                         default:     windows_version = 0; break;
413                                 }
414
415                                 if (windows_mode != 0)
416                                         windows_version_method = "INT 2Fh AX=160Ah";
417                         }
418                 }
419
420                 if (windows_version == 0) {
421                         regs.w.ax = 0x4680;
422 #if TARGET_MSDOS == 32
423                         int386(0x2F,&regs,&regs);
424 #else
425                         int86(0x2F,&regs,&regs);
426 #endif
427                         if (regs.w.ax == 0x0000) { /* Windows 3.0 or DOSSHELL in real or standard mode */
428                         /* FIXME: Okay... if DOSSHELL triggers this test how do I tell between Windows and DOSSHELL? */
429                         /* Also, this call does NOT work when Windows 3.0 is in enhanced mode, and for Real and Standard modes
430                          * does not tell us which mode is active.
431                          *
432                          * As far as I can tell there really is no way to differentiate whether it is running in Real or
433                          * Standard mode, because on a 286 there is no "virtual 8086" mode. The only way Windows can run
434                          * DOS level code is to thunk back into real mode. So for all purposes whatsoever, we might as well
435                          * say that we're running in Windows Real mode because during that time slice we have complete control
436                          * of the CPU. */
437                                 windows_version = 0x300;
438                                 windows_mode = WINDOWS_REAL;
439                                 windows_version_method = "INT 2Fh AX=4680h";
440                         }
441                 }
442
443                 if (windows_version == 0) {
444                         regs.w.ax = 0x1600;
445 #if TARGET_MSDOS == 32
446                         int386(0x2F,&regs,&regs);
447 #else
448                         int86(0x2F,&regs,&regs);
449 #endif
450                         if (regs.h.al == 1 || regs.h.al == 0xFF) { /* Windows 2.x/386 */
451                                 windows_version = 0x200;
452                                 windows_mode = WINDOWS_ENHANCED;
453                         }
454                         else if (regs.h.al == 3 || regs.h.al == 4) {
455                                 windows_version = (regs.h.al << 8) + regs.h.ah;
456                                 windows_mode = WINDOWS_ENHANCED; /* Windows 3.0 */
457                         }
458
459                         if (windows_mode != 0)
460                                 windows_version_method = "INT 2Fh AX=1600h";
461                 }
462
463                 if (windows_version == 0 && windows_mode == WINDOWS_NONE) {
464                 /* well... if the above fails, but the "true" DOS version is 5.50, then we're running under Windows NT */
465                 /* NOTE: Every copy of NT/2000/XP/Vista I have reports 5.50, but assuming that will continue is stupid.
466                  *       Microsoft is free to change that someday. */
467                         if (dos_version == 0) probe_dos();
468                         if (dos_version >= 0x510 && dos_version <= 0x57f) { /* FIXME: If I recall Windows NT really does stick to v5.50, so this range check should be changed into == 5.50 */
469                                 windows_mode = WINDOWS_NT;
470                                 windows_version = 0;
471                                 windows_version_method = "Assuming from DOS version number";
472                         }
473                 }
474
475                 /* now... if this is Windows NT, the next thing we can do is use NTVDM.EXE's
476                  * BOP opcodes to load a "helper" DLL that allows us to call into Win32 */
477 # if defined(NTVDM_CLIENT) && !defined(TARGET_WINDOWS)
478                 if (windows_mode == WINDOWS_NT && ntvdm_dosntast_init()) {
479                         /* OK. Ask for the version number */
480                         OSVERSIONINFO ovi;
481
482                         memset(&ovi,0,sizeof(ovi));
483                         ovi.dwOSVersionInfoSize = sizeof(ovi);
484                         if (ntvdm_dosntast_getversionex(&ovi)) {
485                                 windows_version_method = "GetVersionEx [NTVDM.EXE + DOSNTAST.VDD]";
486                                 windows_version = (ovi.dwMajorVersion << 8) | ovi.dwMinorVersion;
487                                 if (ovi.dwPlatformId == 2/*VER_PLATFORM_WIN32_NT*/)
488                                         windows_mode = WINDOWS_NT;
489                                 else
490                                         windows_mode = WINDOWS_ENHANCED;
491                         }
492                 }
493 # endif
494         }
495 #endif
496
497         return (windows_mode != WINDOWS_NONE);
498 }
499
500 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 32 && !defined(WIN386)
501 /* API for exploiting the QT_Thunk Win32 -> Win16 thunking offered by Windows 9x/ME */
502 unsigned char           win9x_qt_thunk_probed = 0;
503 unsigned char           win9x_qt_thunk_available = 0;
504
505 void                    (__stdcall *QT_Thunk)() = NULL;
506 DWORD                   (__stdcall *LoadLibrary16)(LPSTR lpszLibFileName) = NULL;
507 VOID                    (__stdcall *FreeLibrary16)(DWORD dwInstance) = NULL;
508 HGLOBAL16               (__stdcall *GlobalAlloc16)(UINT flags,DWORD size) = NULL;
509 HGLOBAL16               (__stdcall *GlobalFree16)(HGLOBAL16 handle) = NULL;
510 DWORD                   (__stdcall *GlobalLock16)(HGLOBAL16 handle) = NULL;
511 BOOL                    (__stdcall *GlobalUnlock16)(HGLOBAL16 handle) = NULL;
512 VOID                    (__stdcall *GlobalUnfix16)(HGLOBAL16 handle) = NULL;
513 VOID                    (__stdcall *GlobalFix16)(HGLOBAL16 handle) = NULL;
514 DWORD                   (__stdcall *GetProcAddress16)(DWORD dwInstance, LPSTR lpszProcName) = NULL;
515 DWORD                   win9x_kernel_win16 = 0;
516 DWORD                   win9x_user_win16 = 0;
517
518 int Win9xQT_ThunkInit() {
519         if (!win9x_qt_thunk_probed) {
520                 Win32OrdinalLookupInfo nfo;
521                 HMODULE kern32;
522
523                 win9x_qt_thunk_probed = 1;
524                 win9x_qt_thunk_available = 0;
525
526                 if (dos_version == 0) probe_dos();
527                 if (windows_mode == 0) detect_windows();
528                 if (windows_mode != WINDOWS_ENHANCED) return 0; /* This does not work under Windows NT */
529                 if (windows_version < 0x400) return 0; /* This does not work on Windows 3.1 Win32s (FIXME: Are you sure?) */
530
531                 /* This hack relies on undocumented Win16 support routines hidden in KERNEL32.DLL.
532                  * They're so super seekret, Microsoft won't even let us get to them through GetProcAddress() */
533                 kern32 = GetModuleHandle("KERNEL32.DLL");
534                 if (windows_emulation == WINEMU_WINE) {
535                         /* FIXME: Direct ordinal lookup doesn't work. Returned
536                          *        addresses point to invalid regions of KERNEL32.DLL.
537                          *        I doubt WINE is even putting a PE-compatible image
538                          *        of it out there.
539                          *
540                          *        WINE does allow us to GetProcAddress ordinals
541                          *        (unlike Windows 9x which blocks it) but I'm not
542                          *        really sure the returned functions are anything
543                          *        like the Windows 9x equivalent. If we assume they
544                          *        are, this code seems unable to get the address of
545                          *        KRNL386.EXE's "GETVERSION" function.
546                          *
547                          *        So basically WINE's Windows 9x emulation is more
548                          *        like Windows XP's Application Compatability modes
549                          *        than any serious attempt at pretending to be
550                          *        Windows 9x. And the entry points may well be
551                          *        stubs or other random functions in the same way
552                          *        that ordinal 35 is unrelated under Windows XP. */
553                         return 0;
554                 }
555                 else if (Win32GetOrdinalLookupInfo(kern32,&nfo)) {
556                         GlobalFix16 = (void*)Win32GetOrdinalAddress(&nfo,27);
557                         GlobalLock16 = (void*)Win32GetOrdinalAddress(&nfo,25);
558                         GlobalFree16 = (void*)Win32GetOrdinalAddress(&nfo,31);
559                         LoadLibrary16 = (void*)Win32GetOrdinalAddress(&nfo,35);
560                         FreeLibrary16 = (void*)Win32GetOrdinalAddress(&nfo,36);
561                         GlobalAlloc16 = (void*)Win32GetOrdinalAddress(&nfo,24);
562                         GlobalUnfix16 = (void*)Win32GetOrdinalAddress(&nfo,28);
563                         GlobalUnlock16 = (void*)Win32GetOrdinalAddress(&nfo,26);
564                         GetProcAddress16 = (void*)Win32GetOrdinalAddress(&nfo,37);
565                         QT_Thunk = (void*)GetProcAddress(kern32,"QT_Thunk");
566                 }
567                 else {
568                         GlobalFix16 = NULL;
569                         GlobalLock16 = NULL;
570                         GlobalFree16 = NULL;
571                         GlobalUnfix16 = NULL;
572                         LoadLibrary16 = NULL;
573                         FreeLibrary16 = NULL;
574                         GlobalAlloc16 = NULL;
575                         GlobalUnlock16 = NULL;
576                         GetProcAddress16 = NULL;
577                         QT_Thunk = NULL;
578                 }
579
580                 if (LoadLibrary16 && FreeLibrary16 && GetProcAddress16 && QT_Thunk) {
581                         /* Prove the API works by loading a reference to KERNEL */
582                         win9x_kernel_win16 = LoadLibrary16("KERNEL");
583                         if (win9x_kernel_win16 != 0) {
584                                 win9x_qt_thunk_available = 1;
585                         }
586
587                         win9x_user_win16 = LoadLibrary16("USER");
588                 }
589         }
590
591         return win9x_qt_thunk_available;
592 }
593
594 void Win9xQT_ThunkFree() {
595         if (win9x_kernel_win16) {
596                 FreeLibrary16(win9x_kernel_win16);
597                 win9x_kernel_win16 = 0;
598         }
599 }
600 #endif
601
602 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
603 unsigned char   ToolHelpProbed = 0;
604 HMODULE         ToolHelpDLL = 0;
605 BOOL            (PASCAL FAR *__TimerCount)(TIMERINFO FAR *t) = NULL;
606 BOOL            (PASCAL FAR *__InterruptUnRegister)(HTASK htask) = NULL;
607 BOOL            (PASCAL FAR *__InterruptRegister)(HTASK htask,FARPROC callback) = NULL;
608
609 int ToolHelpInit() {
610         if (!ToolHelpProbed) {
611                 UINT oldMode;
612
613                 ToolHelpProbed = 1;
614
615                 /* BUGFIX: In case TOOLHELP.DLL is missing (such as when running under Windows 3.0)
616                  *         this prevents sudden interruption by a "Cannot find TOOLHELP.DLL" error
617                  *         dialog box */
618                 oldMode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
619                 ToolHelpDLL = LoadLibrary("TOOLHELP.DLL");
620                 SetErrorMode(oldMode);
621                 if (ToolHelpDLL != 0) {
622                         __TimerCount = (void far*)GetProcAddress(ToolHelpDLL,"TIMERCOUNT");
623                         __InterruptRegister = (void far*)GetProcAddress(ToolHelpDLL,"INTERRUPTREGISTER");
624                         __InterruptUnRegister = (void far*)GetProcAddress(ToolHelpDLL,"INTERRUPTUNREGISTER");
625                 }
626         }
627
628         return (ToolHelpDLL != 0) && (__TimerCount != NULL) && (__InterruptRegister != NULL) &&
629                 (__InterruptUnRegister != NULL);
630 }
631
632 void ToolHelpFree() {
633         if (ToolHelpDLL) {
634                 FreeLibrary(ToolHelpDLL);
635                 ToolHelpDLL = 0;
636         }
637         __InterruptUnRegister = NULL;
638         __InterruptRegister = NULL;
639         __TimerCount = NULL;
640 }
641 #endif
642