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