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