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