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