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