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