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