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