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