widl: Fix typelib lcid fields to match those produced by midl.
[wine] / dlls / winedos / vxd.c
1 /*
2  * VxD emulation
3  *
4  * Copyright 1995 Anand Kumria
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <fcntl.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
35 #include "ntstatus.h"
36 #define WIN32_NO_STATUS
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winreg.h"
40 #include "winternl.h"
41 #include "wingdi.h"
42 #include "winuser.h"
43 #include "wine/winbase16.h"
44 #include "dosexe.h"
45 #include "wine/debug.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(vxd);
48
49 #define W32S_APP2WINE(addr) ((addr)? (DWORD)(addr) + W32S_offset : 0)
50 #define W32S_WINE2APP(addr) ((addr)? (DWORD)(addr) - W32S_offset : 0)
51
52 #define VXD_BARF(context,name) \
53     TRACE( "vxd %s: unknown/not implemented parameters:\n" \
54                      "vxd %s: AX %04x, BX %04x, CX %04x, DX %04x, " \
55                      "SI %04x, DI %04x, DS %04x, ES %04x\n", \
56              (name), (name), AX_reg(context), BX_reg(context), \
57              CX_reg(context), DX_reg(context), SI_reg(context), \
58              DI_reg(context), (WORD)context->SegDs, (WORD)context->SegEs )
59
60 static UINT W32S_offset;
61
62 static WORD VXD_WinVersion(void)
63 {
64     WORD version = LOWORD(GetVersion16());
65     return (version >> 8) | (version << 8);
66 }
67
68 /***********************************************************************
69  *           VXD_VMM (WPROCS.401)
70  */
71 void WINAPI VXD_VMM ( CONTEXT86 *context )
72 {
73     unsigned service = AX_reg(context);
74
75     TRACE("[%04x] VMM\n", (UINT16)service);
76
77     switch(service)
78     {
79     case 0x0000: /* version */
80         SET_AX( context, VXD_WinVersion() );
81         RESET_CFLAG(context);
82         break;
83
84     case 0x026d: /* Get_Debug_Flag '/m' */
85     case 0x026e: /* Get_Debug_Flag '/n' */
86         SET_AL( context, 0 );
87         RESET_CFLAG(context);
88         break;
89
90     default:
91         VXD_BARF( context, "VMM" );
92     }
93 }
94
95 /***********************************************************************
96  *           VXD_PageFile (WPROCS.433)
97  */
98 void WINAPI VXD_PageFile( CONTEXT86 *context )
99 {
100     unsigned    service = AX_reg(context);
101
102     /* taken from Ralf Brown's Interrupt List */
103
104     TRACE("[%04x] PageFile\n", (UINT16)service );
105
106     switch(service)
107     {
108     case 0x00: /* get version, is this windows version? */
109         TRACE("returning version\n");
110         SET_AX( context, VXD_WinVersion() );
111         RESET_CFLAG(context);
112         break;
113
114     case 0x01: /* get swap file info */
115         TRACE("VxD PageFile: returning swap file info\n");
116         SET_AX( context, 0x00 ); /* paging disabled */
117         context->Ecx = 0;   /* maximum size of paging file */
118         /* FIXME: do I touch DS:SI or DS:DI? */
119         RESET_CFLAG(context);
120         break;
121
122     case 0x02: /* delete permanent swap on exit */
123         TRACE("VxD PageFile: supposed to delete swap\n");
124         RESET_CFLAG(context);
125         break;
126
127     case 0x03: /* current temporary swap file size */
128         TRACE("VxD PageFile: what is current temp. swap size\n");
129         RESET_CFLAG(context);
130         break;
131
132     case 0x04: /* read or write?? INTERRUP.D */
133     case 0x05: /* cancel?? INTERRUP.D */
134     case 0x06: /* test I/O valid INTERRUP.D */
135     default:
136         VXD_BARF( context, "pagefile" );
137         break;
138     }
139 }
140
141 /***********************************************************************
142  *           VXD_Reboot (WPROCS.409)
143  */
144 void WINAPI VXD_Reboot ( CONTEXT86 *context )
145 {
146     unsigned service = AX_reg(context);
147
148     TRACE("[%04x] Reboot\n", (UINT16)service);
149
150     switch(service)
151     {
152     case 0x0000: /* version */
153         SET_AX( context, VXD_WinVersion() );
154         RESET_CFLAG(context);
155         break;
156
157     default:
158         VXD_BARF( context, "REBOOT" );
159     }
160 }
161
162 /***********************************************************************
163  *           VXD_VDD (WPROCS.410)
164  */
165 void WINAPI VXD_VDD ( CONTEXT86 *context )
166 {
167     unsigned service = AX_reg(context);
168
169     TRACE("[%04x] VDD\n", (UINT16)service);
170
171     switch(service)
172     {
173     case 0x0000: /* version */
174         SET_AX( context, VXD_WinVersion() );
175         RESET_CFLAG(context);
176         break;
177
178     default:
179         VXD_BARF( context, "VDD" );
180     }
181 }
182
183 /***********************************************************************
184  *           VXD_VMD (WPROCS.412)
185  */
186 void WINAPI VXD_VMD ( CONTEXT86 *context )
187 {
188     unsigned service = AX_reg(context);
189
190     TRACE("[%04x] VMD\n", (UINT16)service);
191
192     switch(service)
193     {
194     case 0x0000: /* version */
195         SET_AX( context, VXD_WinVersion() );
196         RESET_CFLAG(context);
197         break;
198
199     default:
200         VXD_BARF( context, "VMD" );
201     }
202 }
203
204 /***********************************************************************
205  *           VXD_VXDLoader (WPROCS.439)
206  */
207 void WINAPI VXD_VXDLoader( CONTEXT86 *context )
208 {
209     unsigned service = AX_reg(context);
210
211     TRACE("[%04x] VXDLoader\n", (UINT16)service);
212
213     switch (service)
214     {
215     case 0x0000: /* get version */
216         TRACE("returning version\n");
217         SET_AX( context, 0x0000 );
218         SET_DX( context, VXD_WinVersion() );
219         RESET_CFLAG(context);
220         break;
221
222     case 0x0001: /* load device */
223         FIXME("load device %04x:%04x (%s)\n",
224               context->SegDs, DX_reg(context),
225               debugstr_a(MapSL(MAKESEGPTR(context->SegDs, DX_reg(context)))));
226         SET_AX( context, 0x0000 );
227         context->SegEs = 0x0000;
228         SET_DI( context, 0x0000 );
229         RESET_CFLAG(context);
230         break;
231
232     case 0x0002: /* unload device */
233         FIXME("unload device (%08x)\n", context->Ebx);
234         SET_AX( context, 0x0000 );
235         RESET_CFLAG(context);
236         break;
237
238     default:
239         VXD_BARF( context, "VXDLDR" );
240         SET_AX( context, 0x000B ); /* invalid function number */
241         SET_CFLAG(context);
242         break;
243     }
244 }
245
246 /***********************************************************************
247  *           VXD_Shell (WPROCS.423)
248  */
249 void WINAPI VXD_Shell( CONTEXT86 *context )
250 {
251     unsigned    service = DX_reg(context);
252
253     TRACE("[%04x] Shell\n", (UINT16)service);
254
255     switch (service) /* Ralf Brown says EDX, but I use DX instead */
256     {
257     case 0x0000:
258         TRACE("returning version\n");
259         SET_AX( context, VXD_WinVersion() );
260         context->Ebx = 1; /* system VM Handle */
261         break;
262
263     case 0x0001:
264     case 0x0002:
265     case 0x0003:
266         /* SHELL_SYSMODAL_Message
267         ebx virtual machine handle
268         eax message box flags
269         ecx address of message
270         edi address of caption
271         return response in eax
272         */
273     case 0x0004:
274         /* SHELL_Message
275         ebx virtual machine handle
276         eax message box flags
277         ecx address of message
278         edi address of caption
279         esi address callback
280         edx reference data for callback
281         return response in eax
282         */
283     case 0x0005:
284         VXD_BARF( context, "shell" );
285         break;
286
287     case 0x0006: /* SHELL_Get_VM_State */
288         TRACE("VxD Shell: returning VM state\n");
289         /* Actually we don't, not yet. We have to return a structure
290          * and I am not to sure how to set it up and return it yet,
291          * so for now let's do nothing. I can (hopefully) get this
292          * by the next release
293          */
294         /* RESET_CFLAG(context); */
295         break;
296
297     case 0x0007:
298     case 0x0008:
299     case 0x0009:
300     case 0x000A:
301     case 0x000B:
302     case 0x000C:
303     case 0x000D:
304     case 0x000E:
305     case 0x000F:
306     case 0x0010:
307     case 0x0011:
308     case 0x0012:
309     case 0x0013:
310     case 0x0014:
311     case 0x0015:
312     case 0x0016:
313         VXD_BARF( context, "SHELL" );
314         break;
315
316     /* the new Win95 shell API */
317     case 0x0100:     /* get version */
318         SET_AX( context, VXD_WinVersion() );
319         break;
320
321     case 0x0104:   /* retrieve Hook_Properties list */
322     case 0x0105:   /* call Hook_Properties callbacks */
323         VXD_BARF( context, "SHELL" );
324         break;
325
326     case 0x0106:   /* install timeout callback */
327         TRACE("VxD Shell: ignoring shell callback (%d sec.)\n", context->Ebx);
328         SET_CFLAG(context);
329         break;
330
331     case 0x0107:   /* get version of any VxD */
332     default:
333         VXD_BARF( context, "SHELL" );
334         break;
335     }
336 }
337
338
339 /***********************************************************************
340  *           VXD_Comm (WPROCS.414)
341  */
342 void WINAPI VXD_Comm( CONTEXT86 *context )
343 {
344     unsigned    service = AX_reg(context);
345
346     TRACE("[%04x] Comm\n", (UINT16)service);
347
348     switch (service)
349     {
350     case 0x0000: /* get version */
351         TRACE("returning version\n");
352         SET_AX( context, VXD_WinVersion() );
353         RESET_CFLAG(context);
354         break;
355
356     case 0x0001: /* set port global */
357     case 0x0002: /* get focus */
358     case 0x0003: /* virtualise port */
359     default:
360         VXD_BARF( context, "comm" );
361     }
362 }
363
364 /***********************************************************************
365  *           VXD_Timer (WPROCS.405)
366  */
367 void WINAPI VXD_Timer( CONTEXT86 *context )
368 {
369     unsigned service = AX_reg(context);
370
371     TRACE("[%04x] Virtual Timer\n", (UINT16)service);
372
373     switch(service)
374     {
375     case 0x0000: /* version */
376         SET_AX( context, VXD_WinVersion() );
377         RESET_CFLAG(context);
378         break;
379
380     case 0x0100: /* clock tick time, in 840nsecs */
381         context->Eax = GetTickCount();
382
383         context->Edx = context->Eax >> 22;
384         context->Eax <<= 10; /* not very precise */
385         break;
386
387     case 0x0101: /* current Windows time, msecs */
388     case 0x0102: /* current VM time, msecs */
389         context->Eax = GetTickCount();
390         break;
391
392     default:
393         VXD_BARF( context, "VTD" );
394     }
395 }
396
397
398 /***********************************************************************
399  *           timer_thread
400  */
401 static DWORD CALLBACK timer_thread( void *arg )
402 {
403     DWORD *system_time = arg;
404
405     for (;;)
406     {
407         *system_time = GetTickCount();
408         Sleep( 55 );
409     }
410
411     return 0;
412 }
413
414
415 /***********************************************************************
416  *           VXD_TimerAPI (WPROCS.1490)
417  */
418 void WINAPI VXD_TimerAPI ( CONTEXT86 *context )
419 {
420     static WORD System_Time_Selector;
421
422     unsigned service = AX_reg(context);
423
424     TRACE("[%04x] TimerAPI\n", (UINT16)service);
425
426     switch(service)
427     {
428     case 0x0000: /* version */
429         SET_AX( context, VXD_WinVersion() );
430         RESET_CFLAG(context);
431         break;
432
433     case 0x0009: /* get system time selector */
434         if ( !System_Time_Selector )
435         {
436             HANDLE16 handle = GlobalAlloc16( GMEM_FIXED, sizeof(DWORD) );
437             System_Time_Selector = handle | 7;
438             CloseHandle( CreateThread( NULL, 0, timer_thread, GlobalLock16(handle), 0, NULL ) );
439         }
440         SET_AX( context, System_Time_Selector );
441         RESET_CFLAG(context);
442         break;
443
444     default:
445         VXD_BARF( context, "VTDAPI" );
446     }
447 }
448
449 /***********************************************************************
450  *           VXD_ConfigMG (WPROCS.451)
451  */
452 void WINAPI VXD_ConfigMG ( CONTEXT86 *context )
453 {
454     unsigned service = AX_reg(context);
455
456     TRACE("[%04x] ConfigMG\n", (UINT16)service);
457
458     switch(service)
459     {
460     case 0x0000: /* version */
461         SET_AX( context, VXD_WinVersion() );
462         RESET_CFLAG(context);
463         break;
464
465     default:
466         VXD_BARF( context, "CONFIGMG" );
467     }
468 }
469
470 /***********************************************************************
471  *           VXD_Enable (WPROCS.455)
472  */
473 void WINAPI VXD_Enable ( CONTEXT86 *context )
474 {
475     unsigned service = AX_reg(context);
476
477     TRACE("[%04x] Enable\n", (UINT16)service);
478
479     switch(service)
480     {
481     case 0x0000: /* version */
482         SET_AX( context, VXD_WinVersion() );
483         RESET_CFLAG(context);
484         break;
485
486     default:
487         VXD_BARF( context, "ENABLE" );
488     }
489 }
490
491 /***********************************************************************
492  *           VXD_APM (WPROCS.438)
493  */
494 void WINAPI VXD_APM ( CONTEXT86 *context )
495 {
496     unsigned service = AX_reg(context);
497
498     TRACE("[%04x] APM\n", (UINT16)service);
499
500     switch(service)
501     {
502     case 0x0000: /* version */
503         SET_AX( context, VXD_WinVersion() );
504         RESET_CFLAG(context);
505         break;
506
507     default:
508         VXD_BARF( context, "APM" );
509     }
510 }
511
512 /***********************************************************************
513  *           VXD_Win32s (WPROCS.445)
514  *
515  * This is an implementation of the services of the Win32s VxD.
516  * Since official documentation of these does not seem to be available,
517  * certain arguments of some of the services remain unclear.
518  *
519  * FIXME: The following services are currently unimplemented:
520  *        Exception handling      (0x01, 0x1C)
521  *        Debugger support        (0x0C, 0x14, 0x17)
522  *        Low-level memory access (0x02, 0x03, 0x0A, 0x0B)
523  *        Memory Statistics       (0x1B)
524  *
525  *
526  * We have a specific problem running Win32s on Linux (and probably also
527  * the other x86 unixes), since Win32s tries to allocate its main 'flat
528  * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB).
529  * The rationale for this seems to be that they want one the one hand to
530  * be able to leave the Win 3.1 memory (starting with the main DOS memory)
531  * at linear address 0, but want at other hand to have offset 0 of the
532  * flat data/code segment point to an unmapped page (to catch NULL pointer
533  * accesses). Hence they allocate the flat segments with a base of 0xffff0000
534  * so that the Win 3.1 memory area at linear address zero shows up in the
535  * flat segments at offset 0x10000 (since linear addresses wrap around at
536  * 4GB). To compensate for that discrepancy between flat segment offsets
537  * and plain linear addresses, all flat pointers passed between the 32-bit
538  * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate
539  * direction by the glue code (mainly) in W32SKRNL and WIN32S16.
540  *
541  * The problem for us is now that Linux does not allow a LDT selector with
542  * base 0xffff0000 to be created, since it would 'see' a part of the kernel
543  * address space. To address this problem we introduce *another* offset:
544  * We add 0x10000 to every linear address we get as an argument from Win32s.
545  * This means especially that the flat code/data selectors get actually
546  * allocated with base 0x0, so that flat offsets and (real) linear addresses
547  * do again agree!  In fact, every call e.g. of a Win32s VxD service now
548  * has all pointer arguments (which are offsets in the flat data segment)
549  * first reduced by 0x10000 by the W32SKRNL glue code, and then again
550  * increased by 0x10000 by *our* code.
551  *
552  * Note that to keep everything consistent, this offset has to be applied by
553  * every Wine function that operates on 'linear addresses' passed to it by
554  * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit
555  * API routines, this affects only two locations: this VxD and the DPMI
556  * handler. (NOTE: Should any Win32s application pass a linear address to
557  * any routine apart from those, e.g. some other VxD handler, that code
558  * would have to take the offset into account as well!)
559  *
560  * The offset is set the first time any application calls the GetVersion()
561  * service of the Win32s VxD. (Note that the offset is never reset.)
562  *
563  */
564
565 void WINAPI VXD_Win32s( CONTEXT86 *context )
566 {
567     switch (AX_reg(context))
568     {
569     case 0x0000: /* Get Version */
570         /*
571          * Input:   None
572          *
573          * Output:  EAX: LoWord: Win32s Version (1.30)
574          *               HiWord: VxD Version (200)
575          *
576          *          EBX: Build (172)
577          *
578          *          ECX: ???   (1)
579          *
580          *          EDX: Debugging Flags
581          *
582          *          EDI: Error Flag
583          *               0 if OK,
584          *               1 if VMCPD VxD not found
585          */
586
587         TRACE("GetVersion()\n");
588
589         context->Eax = VXD_WinVersion() | (200 << 16);
590         context->Ebx = 0;
591         context->Ecx = 0;
592         context->Edx = 0;
593         context->Edi = 0;
594
595         /*
596          * If this is the first time we are called for this process,
597          * hack the memory image of WIN32S16 so that it doesn't try
598          * to access the GDT directly ...
599          *
600          * The first code segment of WIN32S16 (version 1.30) contains
601          * an unexported function somewhere between the exported functions
602          * SetFS and StackLinearToSegmented that tries to find a selector
603          * in the LDT that maps to the memory image of the LDT itself.
604          * If it succeeds, it stores this selector into a global variable
605          * which will be used to speed up execution by using this selector
606          * to modify the LDT directly instead of using the DPMI calls.
607          *
608          * To perform this search of the LDT, this function uses the
609          * sgdt and sldt instructions to find the linear address of
610          * the (GDT and then) LDT. While those instructions themselves
611          * execute without problem, the linear address that sgdt returns
612          * points (at least under Linux) to the kernel address space, so
613          * that any subsequent access leads to a segfault.
614          *
615          * Fortunately, WIN32S16 still contains as a fallback option the
616          * mechanism of using DPMI calls to modify LDT selectors instead
617          * of direct writes to the LDT. Thus we can circumvent the problem
618          * by simply replacing the first byte of the offending function
619          * with an 'retf' instruction. This means that the global variable
620          * supposed to contain the LDT alias selector will remain zero,
621          * and hence WIN32S16 will fall back to using DPMI calls.
622          *
623          * The heuristic we employ to _find_ that function is as follows:
624          * We search between the addresses of the exported symbols SetFS
625          * and StackLinearToSegmented for the byte sequence '0F 01 04'
626          * (this is the opcode of 'sgdt [si]'). We then search backwards
627          * from this address for the last occurrence of 'CB' (retf) that marks
628          * the end of the preceding function. The following byte (which
629          * should now be the first byte of the function we are looking for)
630          * will be replaced by 'CB' (retf).
631          *
632          * This heuristic works for the retail as well as the debug version
633          * of Win32s version 1.30. For versions earlier than that this
634          * hack should not be necessary at all, since the whole mechanism
635          * ('PERF130') was introduced only in 1.30 to improve the overall
636          * performance of Win32s.
637          */
638
639         if (!W32S_offset)
640         {
641             HMODULE16 hModule = GetModuleHandle16("win32s16");
642             SEGPTR func1 = (SEGPTR)GetProcAddress16(hModule, "SetFS");
643             SEGPTR func2 = (SEGPTR)GetProcAddress16(hModule, "StackLinearToSegmented");
644
645             if (   hModule && func1 && func2
646                 && SELECTOROF(func1) == SELECTOROF(func2))
647             {
648                 BYTE *start = MapSL(func1);
649                 BYTE *end   = MapSL(func2);
650                 BYTE *p, *retv = NULL;
651                 int found = 0;
652
653                 for (p = start; p < end; p++)
654                     if (*p == 0xCB) found = 0, retv = p;
655                     else if (*p == 0x0F) found = 1;
656                     else if (*p == 0x01 && found == 1) found = 2;
657                     else if (*p == 0x04 && found == 2) { found = 3; break; }
658                     else found = 0;
659
660                 if (found == 3 && retv)
661                 {
662                     TRACE("PERF130 hack: "
663                                "Replacing byte %02X at offset %04X:%04X\n",
664                                *(retv+1), SELECTOROF(func1),
665                                           OFFSETOF(func1) + retv+1-start);
666
667                     *(retv+1) = (BYTE)0xCB;
668                 }
669             }
670         }
671
672         /*
673          * Mark process as Win32s, so that subsequent DPMI calls
674          * will perform the W32S_APP2WINE/W32S_WINE2APP address shift.
675          */
676         W32S_offset = 0x10000;
677         break;
678
679
680     case 0x0001: /* Install Exception Handling */
681         /*
682          * Input:   EBX: Flat address of W32SKRNL Exception Data
683          *
684          *          ECX: LoWord: Flat Code Selector
685          *               HiWord: Flat Data Selector
686          *
687          *          EDX: Flat address of W32SKRNL Exception Handler
688          *               (this is equal to W32S_BackTo32 + 0x40)
689          *
690          *          ESI: SEGPTR KERNEL.HASGPHANDLER
691          *
692          *          EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
693          *
694          * Output:  EAX: 0 if OK
695          */
696
697         TRACE("[0001] EBX=%x ECX=%x EDX=%x ESI=%x EDI=%x\n",
698                    context->Ebx, context->Ecx, context->Edx,
699                    context->Esi, context->Edi);
700
701         /* FIXME */
702
703         context->Eax = 0;
704         break;
705
706
707     case 0x0002: /* Set Page Access Flags */
708         /*
709          * Input:   EBX: New access flags
710          *               Bit 2: User Page if set, Supervisor Page if clear
711          *               Bit 1: Read-Write if set, Read-Only if clear
712          *
713          *          ECX: Size of memory area to change
714          *
715          *          EDX: Flat start address of memory area
716          *
717          * Output:  EAX: Size of area changed
718          */
719
720         TRACE("[0002] EBX=%x ECX=%x EDX=%x\n",
721                    context->Ebx, context->Ecx, context->Edx);
722
723         /* FIXME */
724
725         context->Eax = context->Ecx;
726         break;
727
728
729     case 0x0003: /* Get Page Access Flags */
730         /*
731          * Input:   EDX: Flat address of page to query
732          *
733          * Output:  EAX: Page access flags
734          *               Bit 2: User Page if set, Supervisor Page if clear
735          *               Bit 1: Read-Write if set, Read-Only if clear
736          */
737
738         TRACE("[0003] EDX=%x\n", context->Edx);
739
740         /* FIXME */
741
742         context->Eax = 6;
743         break;
744
745
746     case 0x0004: /* Map Module */
747         /*
748          * Input:   ECX: IMTE (offset in Module Table) of new module
749          *
750          *          EDX: Flat address of Win32s Module Table
751          *
752          * Output:  EAX: 0 if OK
753          */
754
755     if (!context->Edx || CX_reg(context) == 0xFFFF)
756     {
757         TRACE("MapModule: Initialization call\n");
758         context->Eax = 0;
759     }
760     else
761     {
762         /*
763          * Structure of a Win32s Module Table Entry:
764          */
765         struct Win32sModule
766         {
767             DWORD  flags;
768             DWORD  flatBaseAddr;
769             LPCSTR moduleName;
770             LPCSTR pathName;
771             LPCSTR unknown;
772             LPBYTE baseAddr;
773             DWORD  hModule;
774             DWORD  relocDelta;
775         };
776
777         /*
778          * Note: This function should set up a demand-paged memory image
779          *       of the given module. Since mmap does not allow file offsets
780          *       not aligned at 1024 bytes, we simply load the image fully
781          *       into memory.
782          */
783
784         struct Win32sModule *moduleTable =
785                             (struct Win32sModule *)W32S_APP2WINE(context->Edx);
786         struct Win32sModule *module = moduleTable + context->Ecx;
787
788         IMAGE_NT_HEADERS *nt_header = RtlImageNtHeader( (HMODULE)module->baseAddr );
789         IMAGE_SECTION_HEADER *pe_seg = (IMAGE_SECTION_HEADER*)((char *)&nt_header->OptionalHeader +
790                                                          nt_header->FileHeader.SizeOfOptionalHeader);
791
792
793         HFILE image = _lopen(module->pathName, OF_READ);
794         BOOL error = (image == HFILE_ERROR);
795         UINT i;
796
797         TRACE("MapModule: Loading %s\n", module->pathName);
798
799         for (i = 0;
800              !error && i < nt_header->FileHeader.NumberOfSections;
801              i++, pe_seg++)
802             if(!(pe_seg->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
803             {
804                 DWORD  off  = pe_seg->PointerToRawData;
805                 DWORD  len  = pe_seg->SizeOfRawData;
806                 LPBYTE addr = module->baseAddr + pe_seg->VirtualAddress;
807
808                 TRACE("MapModule: "
809                            "Section %d at %08x from %08x len %08x\n",
810                            i, (DWORD)addr, off, len);
811
812                 if (   _llseek(image, off, SEEK_SET) != off
813                     || _lread(image, addr, len) != len)
814                     error = TRUE;
815             }
816
817         _lclose(image);
818
819         if (error)
820             ERR("MapModule: Unable to load %s\n", module->pathName);
821
822         else if (module->relocDelta != 0)
823         {
824             IMAGE_DATA_DIRECTORY *dir = nt_header->OptionalHeader.DataDirectory
825                                       + IMAGE_DIRECTORY_ENTRY_BASERELOC;
826             IMAGE_BASE_RELOCATION *r = (IMAGE_BASE_RELOCATION *)
827                 (dir->Size? module->baseAddr + dir->VirtualAddress : 0);
828
829             TRACE("MapModule: Reloc delta %08x\n", module->relocDelta);
830
831             while (r && r->VirtualAddress)
832             {
833                 LPBYTE page  = module->baseAddr + r->VirtualAddress;
834                 WORD *TypeOffset = (WORD *)(r + 1);
835                 unsigned int count = (r->SizeOfBlock - sizeof(*r)) / sizeof(*TypeOffset);
836
837                 TRACE("MapModule: %d relocations for page %08x\n",
838                            count, (DWORD)page);
839
840                 for(i = 0; i < count; i++)
841                 {
842                     int offset = TypeOffset[i] & 0xFFF;
843                     int type   = TypeOffset[i] >> 12;
844                     switch(type)
845                     {
846                     case IMAGE_REL_BASED_ABSOLUTE:
847                         break;
848                     case IMAGE_REL_BASED_HIGH:
849                         *(WORD *)(page+offset) += HIWORD(module->relocDelta);
850                         break;
851                     case IMAGE_REL_BASED_LOW:
852                         *(WORD *)(page+offset) += LOWORD(module->relocDelta);
853                         break;
854                     case IMAGE_REL_BASED_HIGHLOW:
855                         *(DWORD*)(page+offset) += module->relocDelta;
856                         break;
857                     default:
858                         WARN("MapModule: Unsupported fixup type\n");
859                         break;
860                     }
861                 }
862
863                 r = (IMAGE_BASE_RELOCATION *)((LPBYTE)r + r->SizeOfBlock);
864             }
865         }
866
867         context->Eax = 0;
868         RESET_CFLAG(context);
869     }
870     break;
871
872
873     case 0x0005: /* UnMap Module */
874         /*
875          * Input:   EDX: Flat address of module image
876          *
877          * Output:  EAX: 1 if OK
878          */
879
880         TRACE("UnMapModule: %x\n", W32S_APP2WINE(context->Edx));
881
882         /* As we didn't map anything, there's nothing to unmap ... */
883
884         context->Eax = 1;
885         break;
886
887
888     case 0x0006: /* VirtualAlloc */
889         /*
890          * Input:   ECX: Current Process
891          *
892          *          EDX: Flat address of arguments on stack
893          *
894          *   DWORD *retv     [out] Flat base address of allocated region
895          *   LPVOID base     [in]  Flat address of region to reserve/commit
896          *   DWORD  size     [in]  Size of region
897          *   DWORD  type     [in]  Type of allocation
898          *   DWORD  prot     [in]  Type of access protection
899          *
900          * Output:  EAX: NtStatus
901          */
902     {
903         DWORD *stack  = (DWORD *)W32S_APP2WINE(context->Edx);
904         DWORD *retv   = (DWORD *)W32S_APP2WINE(stack[0]);
905         LPVOID base   = (LPVOID) W32S_APP2WINE(stack[1]);
906         DWORD  size   = stack[2];
907         DWORD  type   = stack[3];
908         DWORD  prot   = stack[4];
909         DWORD  result;
910
911         TRACE("VirtualAlloc(%x, %x, %x, %x, %x)\n",
912                    (DWORD)retv, (DWORD)base, size, type, prot);
913
914         if (type & 0x80000000)
915         {
916             WARN("VirtualAlloc: strange type %x\n", type);
917             type &= 0x7fffffff;
918         }
919
920         if (!base && (type & MEM_COMMIT) && prot == PAGE_READONLY)
921         {
922             WARN("VirtualAlloc: NLS hack, allowing write access!\n");
923             prot = PAGE_READWRITE;
924         }
925
926         result = (DWORD)VirtualAlloc(base, size, type, prot);
927
928         if (W32S_WINE2APP(result))
929             *retv            = W32S_WINE2APP(result),
930             context->Eax = STATUS_SUCCESS;
931         else
932             *retv            = 0,
933             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
934     }
935     break;
936
937
938     case 0x0007: /* VirtualFree */
939         /*
940          * Input:   ECX: Current Process
941          *
942          *          EDX: Flat address of arguments on stack
943          *
944          *   DWORD *retv     [out] TRUE if success, FALSE if failure
945          *   LPVOID base     [in]  Flat address of region
946          *   DWORD  size     [in]  Size of region
947          *   DWORD  type     [in]  Type of operation
948          *
949          * Output:  EAX: NtStatus
950          */
951     {
952         DWORD *stack  = (DWORD *)W32S_APP2WINE(context->Edx);
953         DWORD *retv   = (DWORD *)W32S_APP2WINE(stack[0]);
954         LPVOID base   = (LPVOID) W32S_APP2WINE(stack[1]);
955         DWORD  size   = stack[2];
956         DWORD  type   = stack[3];
957         DWORD  result;
958
959         TRACE("VirtualFree(%x, %x, %x, %x)\n",
960                    (DWORD)retv, (DWORD)base, size, type);
961
962         result = VirtualFree(base, size, type);
963
964         if (result)
965             *retv            = TRUE,
966             context->Eax = STATUS_SUCCESS;
967         else
968             *retv            = FALSE,
969             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
970     }
971     break;
972
973
974     case 0x0008: /* VirtualProtect */
975         /*
976          * Input:   ECX: Current Process
977          *
978          *          EDX: Flat address of arguments on stack
979          *
980          *   DWORD *retv     [out] TRUE if success, FALSE if failure
981          *   LPVOID base     [in]  Flat address of region
982          *   DWORD  size     [in]  Size of region
983          *   DWORD  new_prot [in]  Desired access protection
984          *   DWORD *old_prot [out] Previous access protection
985          *
986          * Output:  EAX: NtStatus
987          */
988     {
989         DWORD *stack    = (DWORD *)W32S_APP2WINE(context->Edx);
990         DWORD *retv     = (DWORD *)W32S_APP2WINE(stack[0]);
991         LPVOID base     = (LPVOID) W32S_APP2WINE(stack[1]);
992         DWORD  size     = stack[2];
993         DWORD  new_prot = stack[3];
994         DWORD *old_prot = (DWORD *)W32S_APP2WINE(stack[4]);
995         DWORD  result;
996
997         TRACE("VirtualProtect(%x, %x, %x, %x, %x)\n",
998                    (DWORD)retv, (DWORD)base, size, new_prot, (DWORD)old_prot);
999
1000         result = VirtualProtect(base, size, new_prot, old_prot);
1001
1002         if (result)
1003             *retv            = TRUE,
1004             context->Eax = STATUS_SUCCESS;
1005         else
1006             *retv            = FALSE,
1007             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
1008     }
1009     break;
1010
1011
1012     case 0x0009: /* VirtualQuery */
1013         /*
1014          * Input:   ECX: Current Process
1015          *
1016          *          EDX: Flat address of arguments on stack
1017          *
1018          *   DWORD *retv                     [out] Nr. bytes returned
1019          *   LPVOID base                     [in]  Flat address of region
1020          *   LPMEMORY_BASIC_INFORMATION info [out] Info buffer
1021          *   DWORD  len                      [in]  Size of buffer
1022          *
1023          * Output:  EAX: NtStatus
1024          */
1025     {
1026         DWORD *stack  = (DWORD *)W32S_APP2WINE(context->Edx);
1027         DWORD *retv   = (DWORD *)W32S_APP2WINE(stack[0]);
1028         LPVOID base   = (LPVOID) W32S_APP2WINE(stack[1]);
1029         PMEMORY_BASIC_INFORMATION info =
1030                         (PMEMORY_BASIC_INFORMATION)W32S_APP2WINE(stack[2]);
1031         DWORD  len    = stack[3];
1032         DWORD  result;
1033
1034         TRACE("VirtualQuery(%x, %x, %x, %x)\n",
1035                    (DWORD)retv, (DWORD)base, (DWORD)info, len);
1036
1037         result = VirtualQuery(base, info, len);
1038
1039         *retv            = result;
1040         context->Eax = STATUS_SUCCESS;
1041     }
1042     break;
1043
1044
1045     case 0x000A: /* SetVirtMemProcess */
1046         /*
1047          * Input:   ECX: Process Handle
1048          *
1049          *          EDX: Flat address of region
1050          *
1051          * Output:  EAX: NtStatus
1052          */
1053
1054         TRACE("[000a] ECX=%x EDX=%x\n",
1055                    context->Ecx, context->Edx);
1056
1057         /* FIXME */
1058
1059         context->Eax = STATUS_SUCCESS;
1060         break;
1061
1062
1063     case 0x000B: /* ??? some kind of cleanup */
1064         /*
1065          * Input:   ECX: Process Handle
1066          *
1067          * Output:  EAX: NtStatus
1068          */
1069
1070         TRACE("[000b] ECX=%x\n", context->Ecx);
1071
1072         /* FIXME */
1073
1074         context->Eax = STATUS_SUCCESS;
1075         break;
1076
1077
1078     case 0x000C: /* Set Debug Flags */
1079         /*
1080          * Input:   EDX: Debug Flags
1081          *
1082          * Output:  EDX: Previous Debug Flags
1083          */
1084
1085         FIXME("[000c] EDX=%x\n", context->Edx);
1086
1087         /* FIXME */
1088
1089         context->Edx = 0;
1090         break;
1091
1092
1093     case 0x000D: /* NtCreateSection */
1094         /*
1095          * Input:   EDX: Flat address of arguments on stack
1096          *
1097          *   HANDLE32 *retv      [out] Handle of Section created
1098          *   DWORD  flags1       [in]  (?? unknown ??)
1099          *   DWORD  atom         [in]  Name of Section to create
1100          *   LARGE_INTEGER *size [in]  Size of Section
1101          *   DWORD  protect      [in]  Access protection
1102          *   DWORD  flags2       [in]  (?? unknown ??)
1103          *   HFILE32 hFile       [in]  Handle of file to map
1104          *   DWORD  psp          [in]  (Win32s: PSP that hFile belongs to)
1105          *
1106          * Output:  EAX: NtStatus
1107          */
1108     {
1109         DWORD *stack    = (DWORD *)   W32S_APP2WINE(context->Edx);
1110         HANDLE *retv  = (HANDLE *)W32S_APP2WINE(stack[0]);
1111         DWORD  flags1   = stack[1];
1112         DWORD  atom     = stack[2];
1113         LARGE_INTEGER *size = (LARGE_INTEGER *)W32S_APP2WINE(stack[3]);
1114         DWORD  protect  = stack[4];
1115         DWORD  flags2   = stack[5];
1116         HANDLE hFile    = DosFileHandleToWin32Handle(stack[6]);
1117         DWORD  psp      = stack[7];
1118
1119         HANDLE result = INVALID_HANDLE_VALUE;
1120         char name[128];
1121
1122         TRACE("NtCreateSection(%x, %x, %x, %x, %x, %x, %x, %x)\n",
1123                    (DWORD)retv, flags1, atom, (DWORD)size, protect, flags2,
1124                    (DWORD)hFile, psp);
1125
1126         if (!atom || GlobalGetAtomNameA(atom, name, sizeof(name)))
1127         {
1128             TRACE("NtCreateSection: name=%s\n", atom? name : NULL);
1129
1130             result = CreateFileMappingA(hFile, NULL, protect,
1131                                           size? size->u.HighPart : 0,
1132                                           size? size->u.LowPart  : 0,
1133                                           atom? name : NULL);
1134         }
1135
1136         if (result == INVALID_HANDLE_VALUE)
1137             WARN("NtCreateSection: failed!\n");
1138         else
1139             TRACE("NtCreateSection: returned %x\n", (DWORD)result);
1140
1141         if (result != INVALID_HANDLE_VALUE)
1142             *retv            = result,
1143             context->Eax = STATUS_SUCCESS;
1144         else
1145             *retv            = result,
1146             context->Eax = STATUS_NO_MEMORY;   /* FIXME */
1147     }
1148     break;
1149
1150
1151     case 0x000E: /* NtOpenSection */
1152         /*
1153          * Input:   EDX: Flat address of arguments on stack
1154          *
1155          *   HANDLE32 *retv  [out] Handle of Section opened
1156          *   DWORD  protect  [in]  Access protection
1157          *   DWORD  atom     [in]  Name of Section to create
1158          *
1159          * Output:  EAX: NtStatus
1160          */
1161     {
1162         DWORD *stack    = (DWORD *)W32S_APP2WINE(context->Edx);
1163         HANDLE *retv  = (HANDLE *)W32S_APP2WINE(stack[0]);
1164         DWORD  protect  = stack[1];
1165         DWORD  atom     = stack[2];
1166
1167         HANDLE result = INVALID_HANDLE_VALUE;
1168         char name[128];
1169
1170         TRACE("NtOpenSection(%x, %x, %x)\n",
1171                    (DWORD)retv, protect, atom);
1172
1173         if (atom && GlobalGetAtomNameA(atom, name, sizeof(name)))
1174         {
1175             TRACE("NtOpenSection: name=%s\n", name);
1176
1177             result = OpenFileMappingA(protect, FALSE, name);
1178         }
1179
1180         if (result == INVALID_HANDLE_VALUE)
1181             WARN("NtOpenSection: failed!\n");
1182         else
1183             TRACE("NtOpenSection: returned %x\n", (DWORD)result);
1184
1185         if (result != INVALID_HANDLE_VALUE)
1186             *retv            = result,
1187             context->Eax = STATUS_SUCCESS;
1188         else
1189             *retv            = result,
1190             context->Eax = STATUS_NO_MEMORY;   /* FIXME */
1191     }
1192     break;
1193
1194
1195     case 0x000F: /* NtCloseSection */
1196         /*
1197          * Input:   EDX: Flat address of arguments on stack
1198          *
1199          *   HANDLE32 handle  [in]  Handle of Section to close
1200          *   DWORD *id        [out] Unique ID  (?? unclear ??)
1201          *
1202          * Output:  EAX: NtStatus
1203          */
1204     {
1205         DWORD *stack    = (DWORD *)W32S_APP2WINE(context->Edx);
1206         HANDLE handle   = (HANDLE)stack[0];
1207         DWORD *id       = (DWORD *)W32S_APP2WINE(stack[1]);
1208
1209         TRACE("NtCloseSection(%x, %x)\n", (DWORD)handle, (DWORD)id);
1210
1211         CloseHandle(handle);
1212         if (id) *id = 0; /* FIXME */
1213
1214         context->Eax = STATUS_SUCCESS;
1215     }
1216     break;
1217
1218
1219     case 0x0010: /* NtDupSection */
1220         /*
1221          * Input:   EDX: Flat address of arguments on stack
1222          *
1223          *   HANDLE32 handle  [in]  Handle of Section to duplicate
1224          *
1225          * Output:  EAX: NtStatus
1226          */
1227     {
1228         DWORD *stack    = (DWORD *)W32S_APP2WINE(context->Edx);
1229         HANDLE handle   = (HANDLE)stack[0];
1230         HANDLE new_handle;
1231
1232         TRACE("NtDupSection(%x)\n", (DWORD)handle);
1233
1234         DuplicateHandle( GetCurrentProcess(), handle,
1235                          GetCurrentProcess(), &new_handle,
1236                          0, FALSE, DUPLICATE_SAME_ACCESS );
1237         context->Eax = STATUS_SUCCESS;
1238     }
1239     break;
1240
1241
1242     case 0x0011: /* NtMapViewOfSection */
1243         /*
1244          * Input:   EDX: Flat address of arguments on stack
1245          *
1246          *   HANDLE32 SectionHandle       [in]     Section to be mapped
1247          *   DWORD    ProcessHandle       [in]     Process to be mapped into
1248          *   DWORD *  BaseAddress         [in/out] Address to be mapped at
1249          *   DWORD    ZeroBits            [in]     (?? unclear ??)
1250          *   DWORD    CommitSize          [in]     (?? unclear ??)
1251          *   LARGE_INTEGER *SectionOffset [in]     Offset within section
1252          *   DWORD *  ViewSize            [in]     Size of view
1253          *   DWORD    InheritDisposition  [in]     (?? unclear ??)
1254          *   DWORD    AllocationType      [in]     (?? unclear ??)
1255          *   DWORD    Protect             [in]     Access protection
1256          *
1257          * Output:  EAX: NtStatus
1258          */
1259     {
1260         DWORD *  stack          = (DWORD *)W32S_APP2WINE(context->Edx);
1261         HANDLE   SectionHandle  = (HANDLE)stack[0];
1262         DWORD    ProcessHandle  = stack[1]; /* ignored */
1263         DWORD *  BaseAddress    = (DWORD *)W32S_APP2WINE(stack[2]);
1264         DWORD    ZeroBits       = stack[3];
1265         DWORD    CommitSize     = stack[4];
1266         LARGE_INTEGER *SectionOffset = (LARGE_INTEGER *)W32S_APP2WINE(stack[5]);
1267         DWORD *  ViewSize       = (DWORD *)W32S_APP2WINE(stack[6]);
1268         DWORD    InheritDisposition = stack[7];
1269         DWORD    AllocationType = stack[8];
1270         DWORD    Protect        = stack[9];
1271
1272         LPBYTE address = (LPBYTE)(BaseAddress?
1273                         W32S_APP2WINE(*BaseAddress) : 0);
1274         DWORD  access = 0, result;
1275
1276         switch (Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
1277         {
1278             case PAGE_READONLY:           access = FILE_MAP_READ;  break;
1279             case PAGE_READWRITE:          access = FILE_MAP_WRITE; break;
1280             case PAGE_WRITECOPY:          access = FILE_MAP_COPY;  break;
1281
1282             case PAGE_EXECUTE_READ:       access = FILE_MAP_READ;  break;
1283             case PAGE_EXECUTE_READWRITE:  access = FILE_MAP_WRITE; break;
1284             case PAGE_EXECUTE_WRITECOPY:  access = FILE_MAP_COPY;  break;
1285         }
1286
1287         TRACE("NtMapViewOfSection"
1288                    "(%x, %x, %x, %x, %x, %x, %x, %x, %x, %x)\n",
1289                    (DWORD)SectionHandle, ProcessHandle, (DWORD)BaseAddress,
1290                    ZeroBits, CommitSize, (DWORD)SectionOffset, (DWORD)ViewSize,
1291                    InheritDisposition, AllocationType, Protect);
1292         TRACE("NtMapViewOfSection: "
1293                    "base=%x, offset=%x, size=%x, access=%x\n",
1294                    (DWORD)address, SectionOffset? SectionOffset->u.LowPart : 0,
1295                    ViewSize? *ViewSize : 0, access);
1296
1297         result = (DWORD)MapViewOfFileEx(SectionHandle, access,
1298                             SectionOffset? SectionOffset->u.HighPart : 0,
1299                             SectionOffset? SectionOffset->u.LowPart  : 0,
1300                             ViewSize? *ViewSize : 0, address);
1301
1302         TRACE("NtMapViewOfSection: result=%x\n", result);
1303
1304         if (W32S_WINE2APP(result))
1305         {
1306             if (BaseAddress) *BaseAddress = W32S_WINE2APP(result);
1307             context->Eax = STATUS_SUCCESS;
1308         }
1309         else
1310             context->Eax = STATUS_NO_MEMORY; /* FIXME */
1311     }
1312     break;
1313
1314
1315     case 0x0012: /* NtUnmapViewOfSection */
1316         /*
1317          * Input:   EDX: Flat address of arguments on stack
1318          *
1319          *   DWORD  ProcessHandle  [in]  Process (defining address space)
1320          *   LPBYTE BaseAddress    [in]  Base address of view to be unmapped
1321          *
1322          * Output:  EAX: NtStatus
1323          */
1324     {
1325         DWORD *stack          = (DWORD *)W32S_APP2WINE(context->Edx);
1326         DWORD  ProcessHandle  = stack[0]; /* ignored */
1327         LPBYTE BaseAddress    = (LPBYTE)W32S_APP2WINE(stack[1]);
1328
1329         TRACE("NtUnmapViewOfSection(%x, %x)\n",
1330                    ProcessHandle, (DWORD)BaseAddress);
1331
1332         UnmapViewOfFile(BaseAddress);
1333
1334         context->Eax = STATUS_SUCCESS;
1335     }
1336     break;
1337
1338
1339     case 0x0013: /* NtFlushVirtualMemory */
1340         /*
1341          * Input:   EDX: Flat address of arguments on stack
1342          *
1343          *   DWORD   ProcessHandle  [in]  Process (defining address space)
1344          *   LPBYTE *BaseAddress    [in?] Base address of range to be flushed
1345          *   DWORD  *ViewSize       [in?] Number of bytes to be flushed
1346          *   DWORD  *unknown        [???] (?? unknown ??)
1347          *
1348          * Output:  EAX: NtStatus
1349          */
1350     {
1351         DWORD *stack          = (DWORD *)W32S_APP2WINE(context->Edx);
1352         DWORD  ProcessHandle  = stack[0]; /* ignored */
1353         DWORD *BaseAddress    = (DWORD *)W32S_APP2WINE(stack[1]);
1354         DWORD *ViewSize       = (DWORD *)W32S_APP2WINE(stack[2]);
1355         DWORD *unknown        = (DWORD *)W32S_APP2WINE(stack[3]);
1356
1357         LPBYTE address = (LPBYTE)(BaseAddress? W32S_APP2WINE(*BaseAddress) : 0);
1358         DWORD  size    = ViewSize? *ViewSize : 0;
1359
1360         TRACE("NtFlushVirtualMemory(%x, %x, %x, %x)\n",
1361                    ProcessHandle, (DWORD)BaseAddress, (DWORD)ViewSize,
1362                    (DWORD)unknown);
1363         TRACE("NtFlushVirtualMemory: base=%x, size=%x\n",
1364                    (DWORD)address, size);
1365
1366         FlushViewOfFile(address, size);
1367
1368         context->Eax = STATUS_SUCCESS;
1369     }
1370     break;
1371
1372
1373     case 0x0014: /* Get/Set Debug Registers */
1374         /*
1375          * Input:   ECX: 0 if Get, 1 if Set
1376          *
1377          *          EDX: Get: Flat address of buffer to receive values of
1378          *                    debug registers DR0 .. DR7
1379          *               Set: Flat address of buffer containing values of
1380          *                    debug registers DR0 .. DR7 to be set
1381          * Output:  None
1382          */
1383
1384         FIXME("[0014] ECX=%x EDX=%x\n",
1385                    context->Ecx, context->Edx);
1386
1387         /* FIXME */
1388         break;
1389
1390
1391     case 0x0015: /* Set Coprocessor Emulation Flag */
1392         /*
1393          * Input:   EDX: 0 to deactivate, 1 to activate coprocessor emulation
1394          *
1395          * Output:  None
1396          */
1397
1398         TRACE("[0015] EDX=%x\n", context->Edx);
1399
1400         /* We don't care, as we always have a coprocessor anyway */
1401         break;
1402
1403
1404     case 0x0016: /* Init Win32S VxD PSP */
1405         /*
1406          * If called to query required PSP size:
1407          *
1408          *     Input:  EBX: 0
1409          *     Output: EDX: Required size of Win32s VxD PSP
1410          *
1411          * If called to initialize allocated PSP:
1412          *
1413          *     Input:  EBX: LoWord: Selector of Win32s VxD PSP
1414          *                  HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1415          *     Output: None
1416          */
1417
1418         if (context->Ebx == 0)
1419             context->Edx = 0x80;
1420         else
1421         {
1422             PDB16 *psp = MapSL( MAKESEGPTR( BX_reg(context), 0 ));
1423             psp->nbFiles = 32;
1424             psp->fileHandlesPtr = MAKELONG(HIWORD(context->Ebx), 0x5c);
1425             memset((LPBYTE)psp + 0x5c, '\xFF', 32);
1426         }
1427         break;
1428
1429
1430     case 0x0017: /* Set Break Point */
1431         /*
1432          * Input:   EBX: Offset of Break Point
1433          *          CX:  Selector of Break Point
1434          *
1435          * Output:  None
1436          */
1437
1438         FIXME("[0017] EBX=%x CX=%x\n",
1439                    context->Ebx, CX_reg(context));
1440
1441         /* FIXME */
1442         break;
1443
1444
1445     case 0x0018: /* VirtualLock */
1446         /*
1447          * Input:   ECX: Current Process
1448          *
1449          *          EDX: Flat address of arguments on stack
1450          *
1451          *   DWORD *retv     [out] TRUE if success, FALSE if failure
1452          *   LPVOID base     [in]  Flat address of range to lock
1453          *   DWORD  size     [in]  Size of range
1454          *
1455          * Output:  EAX: NtStatus
1456          */
1457     {
1458         DWORD *stack  = (DWORD *)W32S_APP2WINE(context->Edx);
1459         DWORD *retv   = (DWORD *)W32S_APP2WINE(stack[0]);
1460         LPVOID base   = (LPVOID) W32S_APP2WINE(stack[1]);
1461         DWORD  size   = stack[2];
1462         DWORD  result;
1463
1464         TRACE("VirtualLock(%x, %x, %x)\n",
1465                    (DWORD)retv, (DWORD)base, size);
1466
1467         result = VirtualLock(base, size);
1468
1469         if (result)
1470             *retv            = TRUE,
1471             context->Eax = STATUS_SUCCESS;
1472         else
1473             *retv            = FALSE,
1474             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
1475     }
1476     break;
1477
1478
1479     case 0x0019: /* VirtualUnlock */
1480         /*
1481          * Input:   ECX: Current Process
1482          *
1483          *          EDX: Flat address of arguments on stack
1484          *
1485          *   DWORD *retv     [out] TRUE if success, FALSE if failure
1486          *   LPVOID base     [in]  Flat address of range to unlock
1487          *   DWORD  size     [in]  Size of range
1488          *
1489          * Output:  EAX: NtStatus
1490          */
1491     {
1492         DWORD *stack  = (DWORD *)W32S_APP2WINE(context->Edx);
1493         DWORD *retv   = (DWORD *)W32S_APP2WINE(stack[0]);
1494         LPVOID base   = (LPVOID) W32S_APP2WINE(stack[1]);
1495         DWORD  size   = stack[2];
1496         DWORD  result;
1497
1498         TRACE("VirtualUnlock(%x, %x, %x)\n",
1499                    (DWORD)retv, (DWORD)base, size);
1500
1501         result = VirtualUnlock(base, size);
1502
1503         if (result)
1504             *retv            = TRUE,
1505             context->Eax = STATUS_SUCCESS;
1506         else
1507             *retv            = FALSE,
1508             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
1509     }
1510     break;
1511
1512
1513     case 0x001A: /* KGetSystemInfo */
1514         /*
1515          * Input:   None
1516          *
1517          * Output:  ECX:  Start of sparse memory arena
1518          *          EDX:  End of sparse memory arena
1519          */
1520
1521         TRACE("KGetSystemInfo()\n");
1522
1523         /*
1524          * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as
1525          *       sparse memory arena. We do it the other way around, since
1526          *       we have to reserve 3GB - 4GB for Linux, and thus use
1527          *       0GB - 3GB as sparse memory arena.
1528          *
1529          *       FIXME: What about other OSes ?
1530          */
1531
1532         context->Ecx = W32S_WINE2APP(0x00000000);
1533         context->Edx = W32S_WINE2APP(0xbfffffff);
1534         break;
1535
1536
1537     case 0x001B: /* KGlobalMemStat */
1538         /*
1539          * Input:   ESI: Flat address of buffer to receive memory info
1540          *
1541          * Output:  None
1542          */
1543     {
1544         struct Win32sMemoryInfo
1545         {
1546             DWORD DIPhys_Count;       /* Total physical pages */
1547             DWORD DIFree_Count;       /* Free physical pages */
1548             DWORD DILin_Total_Count;  /* Total virtual pages (private arena) */
1549             DWORD DILin_Total_Free;   /* Free virtual pages (private arena) */
1550
1551             DWORD SparseTotal;        /* Total size of sparse arena (bytes ?) */
1552             DWORD SparseFree;         /* Free size of sparse arena (bytes ?) */
1553         };
1554
1555         struct Win32sMemoryInfo *info =
1556                        (struct Win32sMemoryInfo *)W32S_APP2WINE(context->Esi);
1557
1558         FIXME("KGlobalMemStat(%x)\n", (DWORD)info);
1559
1560         /* FIXME */
1561     }
1562     break;
1563
1564
1565     case 0x001C: /* Enable/Disable Exceptions */
1566         /*
1567          * Input:   ECX: 0 to disable, 1 to enable exception handling
1568          *
1569          * Output:  None
1570          */
1571
1572         TRACE("[001c] ECX=%x\n", context->Ecx);
1573
1574         /* FIXME */
1575         break;
1576
1577
1578     case 0x001D: /* VirtualAlloc called from 16-bit code */
1579         /*
1580          * Input:   EDX: Segmented address of arguments on stack
1581          *
1582          *   LPVOID base     [in]  Flat address of region to reserve/commit
1583          *   DWORD  size     [in]  Size of region
1584          *   DWORD  type     [in]  Type of allocation
1585          *   DWORD  prot     [in]  Type of access protection
1586          *
1587          * Output:  EAX: NtStatus
1588          *          EDX: Flat base address of allocated region
1589          */
1590     {
1591         DWORD *stack  = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1592         LPVOID base   = (LPVOID)W32S_APP2WINE(stack[0]);
1593         DWORD  size   = stack[1];
1594         DWORD  type   = stack[2];
1595         DWORD  prot   = stack[3];
1596         DWORD  result;
1597
1598         TRACE("VirtualAlloc16(%x, %x, %x, %x)\n",
1599                    (DWORD)base, size, type, prot);
1600
1601         if (type & 0x80000000)
1602         {
1603             WARN("VirtualAlloc16: strange type %x\n", type);
1604             type &= 0x7fffffff;
1605         }
1606
1607         result = (DWORD)VirtualAlloc(base, size, type, prot);
1608
1609         if (W32S_WINE2APP(result))
1610             context->Edx = W32S_WINE2APP(result),
1611             context->Eax = STATUS_SUCCESS;
1612         else
1613             context->Edx = 0,
1614             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
1615         TRACE("VirtualAlloc16: returning base %x\n", context->Edx);
1616     }
1617     break;
1618
1619
1620     case 0x001E: /* VirtualFree called from 16-bit code */
1621         /*
1622          * Input:   EDX: Segmented address of arguments on stack
1623          *
1624          *   LPVOID base     [in]  Flat address of region
1625          *   DWORD  size     [in]  Size of region
1626          *   DWORD  type     [in]  Type of operation
1627          *
1628          * Output:  EAX: NtStatus
1629          *          EDX: TRUE if success, FALSE if failure
1630          */
1631     {
1632         DWORD *stack  = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1633         LPVOID base   = (LPVOID)W32S_APP2WINE(stack[0]);
1634         DWORD  size   = stack[1];
1635         DWORD  type   = stack[2];
1636         DWORD  result;
1637
1638         TRACE("VirtualFree16(%x, %x, %x)\n",
1639                    (DWORD)base, size, type);
1640
1641         result = VirtualFree(base, size, type);
1642
1643         if (result)
1644             context->Edx = TRUE,
1645             context->Eax = STATUS_SUCCESS;
1646         else
1647             context->Edx = FALSE,
1648             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
1649     }
1650     break;
1651
1652
1653     case 0x001F: /* FWorkingSetSize */
1654         /*
1655          * Input:   EDX: 0 if Get, 1 if Set
1656          *
1657          *          ECX: Get: Buffer to receive Working Set Size
1658          *               Set: Buffer containing Working Set Size
1659          *
1660          * Output:  NtStatus
1661          */
1662     {
1663         DWORD *ptr = (DWORD *)W32S_APP2WINE(context->Ecx);
1664         BOOL set = context->Edx;
1665
1666         TRACE("FWorkingSetSize(%x, %x)\n", (DWORD)ptr, (DWORD)set);
1667
1668         if (set)
1669             /* We do it differently ... */;
1670         else
1671             *ptr = 0x100;
1672
1673         context->Eax = STATUS_SUCCESS;
1674     }
1675     break;
1676
1677
1678     default:
1679         VXD_BARF( context, "W32S" );
1680     }
1681
1682 }