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