4 * Copyright 1995 Anand Kumria
11 #include "wine/winbase16.h"
12 #include "wine/winuser16.h"
15 #include "selectors.h"
23 #define VXD_BARF(context,name) \
24 DUMP( "vxd %s: unknown/not implemented parameters:\n" \
25 "vxd %s: AX %04x, BX %04x, CX %04x, DX %04x, " \
26 "SI %04x, DI %04x, DS %04x, ES %04x\n", \
27 (name), (name), AX_reg(context), BX_reg(context), \
28 CX_reg(context), DX_reg(context), SI_reg(context), \
29 DI_reg(context), (WORD)DS_reg(context), (WORD)ES_reg(context) )
32 static WORD
VXD_WinVersion(void)
34 WORD version
= LOWORD(GetVersion16());
35 return (version
>> 8) | (version
<< 8);
38 /***********************************************************************
41 void VXD_VMM ( CONTEXT
*context
)
43 unsigned service
= AX_reg(context
);
45 TRACE(vxd
,"[%04x] VMM \n", (UINT16
)service
);
49 case 0x0000: /* version */
50 AX_reg(context
) = VXD_WinVersion();
54 case 0x026d: /* Get_Debug_Flag '/m' */
55 case 0x026e: /* Get_Debug_Flag '/n' */
61 VXD_BARF( context
, "VMM" );
65 /***********************************************************************
68 void WINAPI
VXD_PageFile( CONTEXT
*context
)
70 unsigned service
= AX_reg(context
);
72 /* taken from Ralf Brown's Interrupt List */
74 TRACE(vxd
,"[%04x] PageFile\n", (UINT16
)service
);
78 case 0x00: /* get version, is this windows version? */
79 TRACE(vxd
,"returning version\n");
80 AX_reg(context
) = VXD_WinVersion();
84 case 0x01: /* get swap file info */
85 TRACE(vxd
,"VxD PageFile: returning swap file info\n");
86 AX_reg(context
) = 0x00; /* paging disabled */
87 ECX_reg(context
) = 0; /* maximum size of paging file */
88 /* FIXME: do I touch DS:SI or DS:DI? */
92 case 0x02: /* delete permanent swap on exit */
93 TRACE(vxd
,"VxD PageFile: supposed to delete swap\n");
97 case 0x03: /* current temporary swap file size */
98 TRACE(vxd
,"VxD PageFile: what is current temp. swap size\n");
102 case 0x04: /* read or write?? INTERRUP.D */
103 case 0x05: /* cancel?? INTERRUP.D */
104 case 0x06: /* test I/O valid INTERRUP.D */
106 VXD_BARF( context
, "pagefile" );
111 /***********************************************************************
114 void VXD_Reboot ( CONTEXT
*context
)
116 unsigned service
= AX_reg(context
);
118 TRACE(vxd
,"[%04x] VMM \n", (UINT16
)service
);
122 case 0x0000: /* version */
123 AX_reg(context
) = VXD_WinVersion();
124 RESET_CFLAG(context
);
128 VXD_BARF( context
, "REBOOT" );
132 /***********************************************************************
135 void VXD_VDD ( CONTEXT
*context
)
137 unsigned service
= AX_reg(context
);
139 TRACE(vxd
,"[%04x] VDD \n", (UINT16
)service
);
143 case 0x0000: /* version */
144 AX_reg(context
) = VXD_WinVersion();
145 RESET_CFLAG(context
);
149 VXD_BARF( context
, "VDD" );
153 /***********************************************************************
156 void VXD_VMD ( CONTEXT
*context
)
158 unsigned service
= AX_reg(context
);
160 TRACE(vxd
,"[%04x] VMD \n", (UINT16
)service
);
164 case 0x0000: /* version */
165 AX_reg(context
) = VXD_WinVersion();
166 RESET_CFLAG(context
);
170 VXD_BARF( context
, "VMD" );
174 /***********************************************************************
177 void WINAPI
VXD_Shell( CONTEXT
*context
)
179 unsigned service
= DX_reg(context
);
181 TRACE(vxd
,"[%04x] Shell\n", (UINT16
)service
);
183 switch (service
) /* Ralf Brown says EDX, but I use DX instead */
186 TRACE(vxd
,"returning version\n");
187 AX_reg(context
) = VXD_WinVersion();
188 EBX_reg(context
) = 1; /* system VM Handle */
196 VXD_BARF( context
, "shell" );
199 case 0x0006: /* SHELL_Get_VM_State */
200 TRACE(vxd
,"VxD Shell: returning VM state\n");
201 /* Actually we don't, not yet. We have to return a structure
202 * and I am not to sure how to set it up and return it yet,
203 * so for now let's do nothing. I can (hopefully) get this
204 * by the next release
206 /* RESET_CFLAG(context); */
225 VXD_BARF( context
, "SHELL" );
228 /* the new Win95 shell API */
229 case 0x0100: /* get version */
230 AX_reg(context
) = VXD_WinVersion();
233 case 0x0104: /* retrieve Hook_Properties list */
234 case 0x0105: /* call Hook_Properties callbacks */
235 VXD_BARF( context
, "SHELL" );
238 case 0x0106: /* install timeout callback */
239 TRACE( vxd
, "VxD Shell: ignoring shell callback (%ld sec.)\n",
240 EBX_reg( context
) );
244 case 0x0107: /* get version of any VxD */
246 VXD_BARF( context
, "SHELL" );
252 /***********************************************************************
255 void WINAPI
VXD_Comm( CONTEXT
*context
)
257 unsigned service
= AX_reg(context
);
259 TRACE(vxd
,"[%04x] Comm\n", (UINT16
)service
);
263 case 0x0000: /* get version */
264 TRACE(vxd
,"returning version\n");
265 AX_reg(context
) = VXD_WinVersion();
266 RESET_CFLAG(context
);
269 case 0x0001: /* set port global */
270 case 0x0002: /* get focus */
271 case 0x0003: /* virtualise port */
273 VXD_BARF( context
, "comm" );
277 /***********************************************************************
280 void VXD_Timer( CONTEXT
*context
)
282 unsigned service
= AX_reg(context
);
284 TRACE(vxd
,"[%04x] Virtual Timer\n", (UINT16
)service
);
288 case 0x0000: /* version */
289 AX_reg(context
) = VXD_WinVersion();
290 RESET_CFLAG(context
);
293 case 0x0100: /* clock tick time, in 840nsecs */
294 EAX_reg(context
) = GetTickCount();
296 EDX_reg(context
) = EAX_reg(context
) >> 22;
297 EAX_reg(context
) <<= 10; /* not very precise */
300 case 0x0101: /* current Windows time, msecs */
301 case 0x0102: /* current VM time, msecs */
302 EAX_reg(context
) = GetTickCount();
306 VXD_BARF( context
, "VTD" );
310 /***********************************************************************
313 static DWORD System_Time
= 0;
314 static WORD System_Time_Selector
= 0;
315 static void System_Time_Tick( WORD timer
) { System_Time
+= 55; }
316 void VXD_TimerAPI ( CONTEXT
*context
)
318 unsigned service
= AX_reg(context
);
320 TRACE(vxd
,"[%04x] TimerAPI \n", (UINT16
)service
);
324 case 0x0000: /* version */
325 AX_reg(context
) = VXD_WinVersion();
326 RESET_CFLAG(context
);
329 case 0x0009: /* get system time selector */
330 if ( !System_Time_Selector
)
332 System_Time_Selector
= SELECTOR_AllocBlock( &System_Time
, sizeof(DWORD
),
333 SEGMENT_DATA
, FALSE
, TRUE
);
334 CreateSystemTimer( 55, System_Time_Tick
);
337 AX_reg(context
) = System_Time_Selector
;
338 RESET_CFLAG(context
);
342 VXD_BARF( context
, "VTDAPI" );
346 /***********************************************************************
349 void VXD_ConfigMG ( CONTEXT
*context
)
351 unsigned service
= AX_reg(context
);
353 TRACE(vxd
,"[%04x] ConfigMG \n", (UINT16
)service
);
357 case 0x0000: /* version */
358 AX_reg(context
) = VXD_WinVersion();
359 RESET_CFLAG(context
);
363 VXD_BARF( context
, "CONFIGMG" );
367 /***********************************************************************
370 void VXD_Enable ( CONTEXT
*context
)
372 unsigned service
= AX_reg(context
);
374 TRACE(vxd
,"[%04x] Enable \n", (UINT16
)service
);
378 case 0x0000: /* version */
379 AX_reg(context
) = VXD_WinVersion();
380 RESET_CFLAG(context
);
384 VXD_BARF( context
, "ENABLE" );
388 /***********************************************************************
391 void VXD_APM ( CONTEXT
*context
)
393 unsigned service
= AX_reg(context
);
395 TRACE(vxd
,"[%04x] APM \n", (UINT16
)service
);
399 case 0x0000: /* version */
400 AX_reg(context
) = VXD_WinVersion();
401 RESET_CFLAG(context
);
405 VXD_BARF( context
, "APM" );
409 /***********************************************************************
412 * This is an implementation of the services of the Win32s VxD.
413 * Since official documentation of these does not seem to be available,
414 * certain arguments of some of the services remain unclear.
416 * FIXME: The following services are currently unimplemented:
417 * Exception handling (0x01, 0x1C)
418 * Debugger support (0x0C, 0x14, 0x17)
419 * Low-level memory access (0x02, 0x03, 0x0A, 0x0B)
420 * Memory Statistics (0x1B)
423 * We have a specific problem running Win32s on Linux (and probably also
424 * the other x86 unixes), since Win32s tries to allocate its main 'flat
425 * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB).
426 * The rationale for this seems to be that they want one the one hand to
427 * be able to leave the Win 3.1 memory (starting with the main DOS memory)
428 * at linear address 0, but want at other hand to have offset 0 of the
429 * flat data/code segment point to an unmapped page (to catch NULL pointer
430 * accesses). Hence they allocate the flat segments with a base of 0xffff0000
431 * so that the Win 3.1 memory area at linear address zero shows up in the
432 * flat segments at offset 0x10000 (since linear addresses wrap around at
433 * 4GB). To compensate for that discrepancy between flat segment offsets
434 * and plain linear addresses, all flat pointers passed between the 32-bit
435 * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate
436 * direction by the glue code (mainly) in W32SKRNL and WIN32S16.
438 * The problem for us is now that Linux does not allow a LDT selector with
439 * base 0xffff0000 to be created, since it would 'see' a part of the kernel
440 * address space. To address this problem we introduce *another* offset:
441 * We add 0x10000 to every linear address we get as an argument from Win32s.
442 * This means especially that the flat code/data selectors get actually
443 * allocated with base 0x0, so that flat offsets and (real) linear addresses
444 * do again agree! In fact, every call e.g. of a Win32s VxD service now
445 * has all pointer arguments (which are offsets in the flat data segement)
446 * first reduced by 0x10000 by the W32SKRNL glue code, and then again
447 * increased by 0x10000 by *our* code.
449 * Note that to keep everything consistent, this offset has to be applied by
450 * every Wine function that operates on 'linear addresses' passed to it by
451 * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit
452 * API routines, this affects only two locations: this VxD and the DPMI
453 * handler. (NOTE: Should any Win32s application pass a linear address to
454 * any routine apart from those, e.g. some other VxD handler, that code
455 * would have to take the offset into account as well!)
457 * The application of the offset is triggered by marking the current process
458 * as a Win32s process by setting the PDB32_WIN32S_PROC flag in the process
459 * database. This is done the first time any application calls the GetVersion()
460 * service of the Win32s VxD. (Note that the flag is never removed.)
464 void VXD_Win32s( CONTEXT
*context
)
466 switch (AX_reg(context
))
468 case 0x0000: /* Get Version */
472 * Output: EAX: LoWord: Win32s Version (1.30)
473 * HiWord: VxD Version (200)
479 * EDX: Debugging Flags
483 * 1 if VMCPD VxD not found
486 TRACE(vxd
, "GetVersion()\n");
488 EAX_reg(context
) = VXD_WinVersion() | (200 << 16);
489 EBX_reg(context
) = 0;
490 ECX_reg(context
) = 0;
491 EDX_reg(context
) = 0;
492 EDI_reg(context
) = 0;
495 * If this is the first time we are called for this process,
496 * hack the memory image of WIN32S16 so that it doesn't try
497 * to access the GDT directly ...
499 * The first code segment of WIN32S16 (version 1.30) contains
500 * an unexported function somewhere between the exported functions
501 * SetFS and StackLinearToSegmented that tries to find a selector
502 * in the LDT that maps to the memory image of the LDT itself.
503 * If it succeeds, it stores this selector into a global variable
504 * which will be used to speed up execution by using this selector
505 * to modify the LDT directly instead of using the DPMI calls.
507 * To perform this search of the LDT, this function uses the
508 * sgdt and sldt instructions to find the linear address of
509 * the (GDT and then) LDT. While those instructions themselves
510 * execute without problem, the linear address that sgdt returns
511 * points (at least under Linux) to the kernel address space, so
512 * that any subsequent access leads to a segfault.
514 * Fortunately, WIN32S16 still contains as a fallback option the
515 * mechanism of using DPMI calls to modify LDT selectors instead
516 * of direct writes to the LDT. Thus we can circumvent the problem
517 * by simply replacing the first byte of the offending function
518 * with an 'retf' instruction. This means that the global variable
519 * supposed to contain the LDT alias selector will remain zero,
520 * and hence WIN32S16 will fall back to using DPMI calls.
522 * The heuristic we employ to _find_ that function is as follows:
523 * We search between the addresses of the exported symbols SetFS
524 * and StackLinearToSegmented for the byte sequence '0F 01 04'
525 * (this is the opcode of 'sgdt [si]'). We then search backwards
526 * from this address for the last occurrance of 'CB' (retf) that marks
527 * the end of the preceeding function. The following byte (which
528 * should now be the first byte of the function we are looking for)
529 * will be replaced by 'CB' (retf).
531 * This heuristic works for the retail as well as the debug version
532 * of Win32s version 1.30. For versions earlier than that this
533 * hack should not be necessary at all, since the whole mechanism
534 * ('PERF130') was introduced only in 1.30 to improve the overall
535 * performance of Win32s.
538 if (!(PROCESS_Current()->flags
& PDB32_WIN32S_PROC
))
540 HMODULE16 hModule
= GetModuleHandle16("win32s16");
541 SEGPTR func1
= (SEGPTR
)WIN32_GetProcAddress16(hModule
, "SetFS");
542 SEGPTR func2
= (SEGPTR
)WIN32_GetProcAddress16(hModule
,
543 "StackLinearToSegmented");
545 if ( hModule
&& func1
&& func2
546 && SELECTOROF(func1
) == SELECTOROF(func2
))
548 BYTE
*start
= PTR_SEG_TO_LIN(func1
);
549 BYTE
*end
= PTR_SEG_TO_LIN(func2
);
550 BYTE
*p
, *retv
= NULL
;
553 for (p
= start
; p
< end
; p
++)
554 if (*p
== 0xCB) found
= 0, retv
= p
;
555 else if (*p
== 0x0F) found
= 1;
556 else if (*p
== 0x01 && found
== 1) found
= 2;
557 else if (*p
== 0x04 && found
== 2) { found
= 3; break; }
560 if (found
== 3 && retv
)
562 TRACE(vxd
, "PERF130 hack: "
563 "Replacing byte %02X at offset %04X:%04X\n",
564 *(retv
+1), SELECTOROF(func1
),
565 OFFSETOF(func1
) + retv
+1-start
);
567 *(retv
+1) = (BYTE
)0xCB;
573 * Mark process as Win32s, so that subsequent DPMI calls
574 * will perform the W32S_APP2WINE/W32S_WINE2APP address shift.
577 PROCESS_Current()->flags
|= PDB32_WIN32S_PROC
;
581 case 0x0001: /* Install Exception Handling */
583 * Input: EBX: Flat address of W32SKRNL Exception Data
585 * ECX: LoWord: Flat Code Selector
586 * HiWord: Flat Data Selector
588 * EDX: Flat address of W32SKRNL Exception Handler
589 * (this is equal to W32S_BackTo32 + 0x40)
591 * ESI: SEGPTR KERNEL.HASGPHANDLER
593 * EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
595 * Output: EAX: 0 if OK
598 TRACE(vxd
, "[0001] EBX=%lx ECX=%lx EDX=%lx ESI=%lx EDI=%lx\n",
599 EBX_reg(context
), ECX_reg(context
), EDX_reg(context
),
600 ESI_reg(context
), EDI_reg(context
));
604 EAX_reg(context
) = 0;
608 case 0x0002: /* Set Page Access Flags */
610 * Input: EBX: New access flags
611 * Bit 2: User Page if set, Supervisor Page if clear
612 * Bit 1: Read-Write if set, Read-Only if clear
614 * ECX: Size of memory area to change
616 * EDX: Flat start address of memory area
618 * Output: EAX: Size of area changed
621 TRACE(vxd
, "[0002] EBX=%lx ECX=%lx EDX=%lx\n",
622 EBX_reg(context
), ECX_reg(context
), EDX_reg(context
));
626 EAX_reg(context
) = ECX_reg(context
);
630 case 0x0003: /* Get Page Access Flags */
632 * Input: EDX: Flat address of page to query
634 * Output: EAX: Page access flags
635 * Bit 2: User Page if set, Supervisor Page if clear
636 * Bit 1: Read-Write if set, Read-Only if clear
639 TRACE(vxd
, "[0003] EDX=%lx\n", EDX_reg(context
));
643 EAX_reg(context
) = 6;
647 case 0x0004: /* Map Module */
649 * Input: ECX: IMTE (offset in Module Table) of new module
651 * EDX: Flat address of Win32s Module Table
653 * Output: EAX: 0 if OK
656 if (!EDX_reg(context
) || CX_reg(context
) == 0xFFFF)
658 TRACE(vxd
, "MapModule: Initialization call\n");
659 EAX_reg(context
) = 0;
664 * Structure of a Win32s Module Table Entry:
679 * Note: This function should set up a demand-paged memory image
680 * of the given module. Since mmap does not allow file offsets
681 * not aligned at 1024 bytes, we simply load the image fully
685 struct Win32sModule
*moduleTable
=
686 (struct Win32sModule
*)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
687 struct Win32sModule
*module
= moduleTable
+ ECX_reg(context
);
689 IMAGE_NT_HEADERS
*nt_header
= PE_HEADER(module
->baseAddr
);
690 IMAGE_SECTION_HEADER
*pe_seg
= PE_SECTIONS(module
->baseAddr
);
692 HFILE image
= _lopen(module
->pathName
, OF_READ
);
693 BOOL error
= (image
== INVALID_HANDLE_VALUE
);
696 TRACE(vxd
, "MapModule: Loading %s\n", module
->pathName
);
699 !error
&& i
< nt_header
->FileHeader
.NumberOfSections
;
701 if(!(pe_seg
->Characteristics
& IMAGE_SCN_CNT_UNINITIALIZED_DATA
))
703 DWORD off
= pe_seg
->PointerToRawData
;
704 DWORD len
= pe_seg
->SizeOfRawData
;
705 LPBYTE addr
= module
->baseAddr
+ pe_seg
->VirtualAddress
;
707 TRACE(vxd
, "MapModule: "
708 "Section %d at %08lx from %08lx len %08lx\n",
709 i
, (DWORD
)addr
, off
, len
);
711 if ( _llseek(image
, off
, SEEK_SET
) != off
712 || _lread(image
, addr
, len
) != len
)
719 ERR(vxd
, "MapModule: Unable to load %s\n", module
->pathName
);
721 else if (module
->relocDelta
!= 0)
723 IMAGE_DATA_DIRECTORY
*dir
= nt_header
->OptionalHeader
.DataDirectory
724 + IMAGE_DIRECTORY_ENTRY_BASERELOC
;
725 IMAGE_BASE_RELOCATION
*r
= (IMAGE_BASE_RELOCATION
*)
726 (dir
->Size
? module
->baseAddr
+ dir
->VirtualAddress
: 0);
728 TRACE(vxd
, "MapModule: Reloc delta %08lx\n", module
->relocDelta
);
730 while (r
&& r
->VirtualAddress
)
732 LPBYTE page
= module
->baseAddr
+ r
->VirtualAddress
;
733 int count
= (r
->SizeOfBlock
- 8) / 2;
735 TRACE(vxd
, "MapModule: %d relocations for page %08lx\n",
738 for(i
= 0; i
< count
; i
++)
740 int offset
= r
->TypeOffset
[i
] & 0xFFF;
741 int type
= r
->TypeOffset
[i
] >> 12;
744 case IMAGE_REL_BASED_ABSOLUTE
:
746 case IMAGE_REL_BASED_HIGH
:
747 *(WORD
*)(page
+offset
) += HIWORD(module
->relocDelta
);
749 case IMAGE_REL_BASED_LOW
:
750 *(WORD
*)(page
+offset
) += LOWORD(module
->relocDelta
);
752 case IMAGE_REL_BASED_HIGHLOW
:
753 *(DWORD
*)(page
+offset
) += module
->relocDelta
;
756 WARN(vxd
, "MapModule: Unsupported fixup type\n");
761 r
= (IMAGE_BASE_RELOCATION
*)((LPBYTE
)r
+ r
->SizeOfBlock
);
765 EAX_reg(context
) = 0;
766 RESET_CFLAG(context
);
771 case 0x0005: /* UnMap Module */
773 * Input: EDX: Flat address of module image
775 * Output: EAX: 1 if OK
778 TRACE(vxd
, "UnMapModule: %lx\n", (DWORD
)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
));
780 /* As we didn't map anything, there's nothing to unmap ... */
782 EAX_reg(context
) = 1;
786 case 0x0006: /* VirtualAlloc */
788 * Input: ECX: Current Process
790 * EDX: Flat address of arguments on stack
792 * DWORD *retv [out] Flat base address of allocated region
793 * LPVOID base [in] Flat address of region to reserve/commit
794 * DWORD size [in] Size of region
795 * DWORD type [in] Type of allocation
796 * DWORD prot [in] Type of access protection
798 * Output: EAX: NtStatus
801 DWORD
*stack
= (DWORD
*)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
802 DWORD
*retv
= (DWORD
*)W32S_APP2WINE(stack
[0], W32S_OFFSET
);
803 LPVOID base
= (LPVOID
) W32S_APP2WINE(stack
[1], W32S_OFFSET
);
804 DWORD size
= stack
[2];
805 DWORD type
= stack
[3];
806 DWORD prot
= stack
[4];
809 TRACE(vxd
, "VirtualAlloc(%lx, %lx, %lx, %lx, %lx)\n",
810 (DWORD
)retv
, (DWORD
)base
, size
, type
, prot
);
812 if (type
& 0x80000000)
814 WARN(vxd
, "VirtualAlloc: strange type %lx\n", type
);
818 if (!base
&& (type
& MEM_COMMIT
) && prot
== PAGE_READONLY
)
820 WARN(vxd
, "VirtualAlloc: NLS hack, allowing write access!\n");
821 prot
= PAGE_READWRITE
;
824 result
= (DWORD
)VirtualAlloc(base
, size
, type
, prot
);
826 if (W32S_WINE2APP(result
, W32S_OFFSET
))
827 *retv
= W32S_WINE2APP(result
, W32S_OFFSET
),
828 EAX_reg(context
) = STATUS_SUCCESS
;
831 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
836 case 0x0007: /* VirtualFree */
838 * Input: ECX: Current Process
840 * EDX: Flat address of arguments on stack
842 * DWORD *retv [out] TRUE if success, FALSE if failure
843 * LPVOID base [in] Flat address of region
844 * DWORD size [in] Size of region
845 * DWORD type [in] Type of operation
847 * Output: EAX: NtStatus
850 DWORD
*stack
= (DWORD
*)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
851 DWORD
*retv
= (DWORD
*)W32S_APP2WINE(stack
[0], W32S_OFFSET
);
852 LPVOID base
= (LPVOID
) W32S_APP2WINE(stack
[1], W32S_OFFSET
);
853 DWORD size
= stack
[2];
854 DWORD type
= stack
[3];
857 TRACE(vxd
, "VirtualFree(%lx, %lx, %lx, %lx)\n",
858 (DWORD
)retv
, (DWORD
)base
, size
, type
);
860 result
= VirtualFree(base
, size
, type
);
864 EAX_reg(context
) = STATUS_SUCCESS
;
867 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
872 case 0x0008: /* VirtualProtect */
874 * Input: ECX: Current Process
876 * EDX: Flat address of arguments on stack
878 * DWORD *retv [out] TRUE if success, FALSE if failure
879 * LPVOID base [in] Flat address of region
880 * DWORD size [in] Size of region
881 * DWORD new_prot [in] Desired access protection
882 * DWORD *old_prot [out] Previous access protection
884 * Output: EAX: NtStatus
887 DWORD
*stack
= (DWORD
*)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
888 DWORD
*retv
= (DWORD
*)W32S_APP2WINE(stack
[0], W32S_OFFSET
);
889 LPVOID base
= (LPVOID
) W32S_APP2WINE(stack
[1], W32S_OFFSET
);
890 DWORD size
= stack
[2];
891 DWORD new_prot
= stack
[3];
892 DWORD
*old_prot
= (DWORD
*)W32S_APP2WINE(stack
[4], W32S_OFFSET
);
895 TRACE(vxd
, "VirtualProtect(%lx, %lx, %lx, %lx, %lx)\n",
896 (DWORD
)retv
, (DWORD
)base
, size
, new_prot
, (DWORD
)old_prot
);
898 result
= VirtualProtect(base
, size
, new_prot
, old_prot
);
902 EAX_reg(context
) = STATUS_SUCCESS
;
905 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
910 case 0x0009: /* VirtualQuery */
912 * Input: ECX: Current Process
914 * EDX: Flat address of arguments on stack
916 * DWORD *retv [out] Nr. bytes returned
917 * LPVOID base [in] Flat address of region
918 * LPMEMORY_BASIC_INFORMATION info [out] Info buffer
919 * DWORD len [in] Size of buffer
921 * Output: EAX: NtStatus
924 DWORD
*stack
= (DWORD
*)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
925 DWORD
*retv
= (DWORD
*)W32S_APP2WINE(stack
[0], W32S_OFFSET
);
926 LPVOID base
= (LPVOID
) W32S_APP2WINE(stack
[1], W32S_OFFSET
);
927 LPMEMORY_BASIC_INFORMATION info
=
928 (LPMEMORY_BASIC_INFORMATION
)W32S_APP2WINE(stack
[2], W32S_OFFSET
);
929 DWORD len
= stack
[3];
932 TRACE(vxd
, "VirtualQuery(%lx, %lx, %lx, %lx)\n",
933 (DWORD
)retv
, (DWORD
)base
, (DWORD
)info
, len
);
935 result
= VirtualQuery(base
, info
, len
);
938 EAX_reg(context
) = STATUS_SUCCESS
;
943 case 0x000A: /* SetVirtMemProcess */
945 * Input: ECX: Process Handle
947 * EDX: Flat address of region
949 * Output: EAX: NtStatus
952 TRACE(vxd
, "[000a] ECX=%lx EDX=%lx\n",
953 ECX_reg(context
), EDX_reg(context
));
957 EAX_reg(context
) = STATUS_SUCCESS
;
961 case 0x000B: /* ??? some kind of cleanup */
963 * Input: ECX: Process Handle
965 * Output: EAX: NtStatus
968 TRACE(vxd
, "[000b] ECX=%lx\n", ECX_reg(context
));
972 EAX_reg(context
) = STATUS_SUCCESS
;
976 case 0x000C: /* Set Debug Flags */
978 * Input: EDX: Debug Flags
980 * Output: EDX: Previous Debug Flags
983 FIXME(vxd
, "[000c] EDX=%lx\n", EDX_reg(context
));
987 EDX_reg(context
) = 0;
991 case 0x000D: /* NtCreateSection */
993 * Input: EDX: Flat address of arguments on stack
995 * HANDLE32 *retv [out] Handle of Section created
996 * DWORD flags1 [in] (?? unknown ??)
997 * DWORD atom [in] Name of Section to create
998 * LARGE_INTEGER *size [in] Size of Section
999 * DWORD protect [in] Access protection
1000 * DWORD flags2 [in] (?? unknown ??)
1001 * HFILE32 hFile [in] Handle of file to map
1002 * DWORD psp [in] (Win32s: PSP that hFile belongs to)
1004 * Output: EAX: NtStatus
1007 DWORD
*stack
= (DWORD
*) W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
1008 HANDLE
*retv
= (HANDLE
*)W32S_APP2WINE(stack
[0], W32S_OFFSET
);
1009 DWORD flags1
= stack
[1];
1010 DWORD atom
= stack
[2];
1011 LARGE_INTEGER
*size
= (LARGE_INTEGER
*)W32S_APP2WINE(stack
[3], W32S_OFFSET
);
1012 DWORD protect
= stack
[4];
1013 DWORD flags2
= stack
[5];
1014 HFILE hFile
= FILE_GetHandle(stack
[6]);
1015 DWORD psp
= stack
[7];
1017 HANDLE result
= INVALID_HANDLE_VALUE
;
1020 TRACE(vxd
, "NtCreateSection(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1021 (DWORD
)retv
, flags1
, atom
, (DWORD
)size
, protect
, flags2
,
1024 if (!atom
|| GlobalGetAtomNameA(atom
, name
, sizeof(name
)))
1026 TRACE(vxd
, "NtCreateSection: name=%s\n", atom
? name
: NULL
);
1028 result
= CreateFileMappingA(hFile
, NULL
, protect
,
1029 size
? size
->HighPart
: 0,
1030 size
? size
->LowPart
: 0,
1034 if (result
== INVALID_HANDLE_VALUE
)
1035 WARN(vxd
, "NtCreateSection: failed!\n");
1037 TRACE(vxd
, "NtCreateSection: returned %lx\n", (DWORD
)result
);
1039 if (result
!= INVALID_HANDLE_VALUE
)
1041 EAX_reg(context
) = STATUS_SUCCESS
;
1044 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1049 case 0x000E: /* NtOpenSection */
1051 * Input: EDX: Flat address of arguments on stack
1053 * HANDLE32 *retv [out] Handle of Section opened
1054 * DWORD protect [in] Access protection
1055 * DWORD atom [in] Name of Section to create
1057 * Output: EAX: NtStatus
1060 DWORD
*stack
= (DWORD
*)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
1061 HANDLE
*retv
= (HANDLE
*)W32S_APP2WINE(stack
[0], W32S_OFFSET
);
1062 DWORD protect
= stack
[1];
1063 DWORD atom
= stack
[2];
1065 HANDLE result
= INVALID_HANDLE_VALUE
;
1068 TRACE(vxd
, "NtOpenSection(%lx, %lx, %lx)\n",
1069 (DWORD
)retv
, protect
, atom
);
1071 if (atom
&& GlobalGetAtomNameA(atom
, name
, sizeof(name
)))
1073 TRACE(vxd
, "NtOpenSection: name=%s\n", name
);
1075 result
= OpenFileMappingA(protect
, FALSE
, name
);
1078 if (result
== INVALID_HANDLE_VALUE
)
1079 WARN(vxd
, "NtOpenSection: failed!\n");
1081 TRACE(vxd
, "NtOpenSection: returned %lx\n", (DWORD
)result
);
1083 if (result
!= INVALID_HANDLE_VALUE
)
1085 EAX_reg(context
) = STATUS_SUCCESS
;
1088 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1093 case 0x000F: /* NtCloseSection */
1095 * Input: EDX: Flat address of arguments on stack
1097 * HANDLE32 handle [in] Handle of Section to close
1098 * DWORD *id [out] Unique ID (?? unclear ??)
1100 * Output: EAX: NtStatus
1103 DWORD
*stack
= (DWORD
*)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
1104 HANDLE handle
= stack
[0];
1105 DWORD
*id
= (DWORD
*)W32S_APP2WINE(stack
[1], W32S_OFFSET
);
1107 TRACE(vxd
, "NtCloseSection(%lx, %lx)\n", (DWORD
)handle
, (DWORD
)id
);
1109 CloseHandle(handle
);
1110 if (id
) *id
= 0; /* FIXME */
1112 EAX_reg(context
) = STATUS_SUCCESS
;
1117 case 0x0010: /* NtDupSection */
1119 * Input: EDX: Flat address of arguments on stack
1121 * HANDLE32 handle [in] Handle of Section to duplicate
1123 * Output: EAX: NtStatus
1126 DWORD
*stack
= (DWORD
*)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
1127 HANDLE handle
= stack
[0];
1130 TRACE(vxd
, "NtDupSection(%lx)\n", (DWORD
)handle
);
1132 DuplicateHandle( GetCurrentProcess(), handle
,
1133 GetCurrentProcess(), &new_handle
,
1134 0, FALSE
, DUPLICATE_SAME_ACCESS
);
1135 EAX_reg(context
) = STATUS_SUCCESS
;
1140 case 0x0011: /* NtMapViewOfSection */
1142 * Input: EDX: Flat address of arguments on stack
1144 * HANDLE32 SectionHandle [in] Section to be mapped
1145 * DWORD ProcessHandle [in] Process to be mapped into
1146 * DWORD * BaseAddress [in/out] Address to be mapped at
1147 * DWORD ZeroBits [in] (?? unclear ??)
1148 * DWORD CommitSize [in] (?? unclear ??)
1149 * LARGE_INTEGER *SectionOffset [in] Offset within section
1150 * DWORD * ViewSize [in] Size of view
1151 * DWORD InheritDisposition [in] (?? unclear ??)
1152 * DWORD AllocationType [in] (?? unclear ??)
1153 * DWORD Protect [in] Access protection
1155 * Output: EAX: NtStatus
1158 DWORD
* stack
= (DWORD
*)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
1159 HANDLE SectionHandle
= stack
[0];
1160 DWORD ProcessHandle
= stack
[1]; /* ignored */
1161 DWORD
* BaseAddress
= (DWORD
*)W32S_APP2WINE(stack
[2], W32S_OFFSET
);
1162 DWORD ZeroBits
= stack
[3];
1163 DWORD CommitSize
= stack
[4];
1164 LARGE_INTEGER
*SectionOffset
= (LARGE_INTEGER
*)W32S_APP2WINE(stack
[5], W32S_OFFSET
);
1165 DWORD
* ViewSize
= (DWORD
*)W32S_APP2WINE(stack
[6], W32S_OFFSET
);
1166 DWORD InheritDisposition
= stack
[7];
1167 DWORD AllocationType
= stack
[8];
1168 DWORD Protect
= stack
[9];
1170 LPBYTE address
= (LPBYTE
)(BaseAddress
?
1171 W32S_APP2WINE(*BaseAddress
, W32S_OFFSET
) : 0);
1172 DWORD access
= 0, result
;
1174 switch (Protect
& ~(PAGE_GUARD
|PAGE_NOCACHE
))
1176 case PAGE_READONLY
: access
= FILE_MAP_READ
; break;
1177 case PAGE_READWRITE
: access
= FILE_MAP_WRITE
; break;
1178 case PAGE_WRITECOPY
: access
= FILE_MAP_COPY
; break;
1180 case PAGE_EXECUTE_READ
: access
= FILE_MAP_READ
; break;
1181 case PAGE_EXECUTE_READWRITE
: access
= FILE_MAP_WRITE
; break;
1182 case PAGE_EXECUTE_WRITECOPY
: access
= FILE_MAP_COPY
; break;
1185 TRACE(vxd
, "NtMapViewOfSection"
1186 "(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1187 (DWORD
)SectionHandle
, ProcessHandle
, (DWORD
)BaseAddress
,
1188 ZeroBits
, CommitSize
, (DWORD
)SectionOffset
, (DWORD
)ViewSize
,
1189 InheritDisposition
, AllocationType
, Protect
);
1190 TRACE(vxd
, "NtMapViewOfSection: "
1191 "base=%lx, offset=%lx, size=%lx, access=%lx\n",
1192 (DWORD
)address
, SectionOffset
? SectionOffset
->LowPart
: 0,
1193 ViewSize
? *ViewSize
: 0, access
);
1195 result
= (DWORD
)MapViewOfFileEx(SectionHandle
, access
,
1196 SectionOffset
? SectionOffset
->HighPart
: 0,
1197 SectionOffset
? SectionOffset
->LowPart
: 0,
1198 ViewSize
? *ViewSize
: 0, address
);
1200 TRACE(vxd
, "NtMapViewOfSection: result=%lx\n", result
);
1202 if (W32S_WINE2APP(result
, W32S_OFFSET
))
1204 if (BaseAddress
) *BaseAddress
= W32S_WINE2APP(result
, W32S_OFFSET
);
1205 EAX_reg(context
) = STATUS_SUCCESS
;
1208 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1213 case 0x0012: /* NtUnmapViewOfSection */
1215 * Input: EDX: Flat address of arguments on stack
1217 * DWORD ProcessHandle [in] Process (defining address space)
1218 * LPBYTE BaseAddress [in] Base address of view to be unmapped
1220 * Output: EAX: NtStatus
1223 DWORD
*stack
= (DWORD
*)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
1224 DWORD ProcessHandle
= stack
[0]; /* ignored */
1225 LPBYTE BaseAddress
= (LPBYTE
)W32S_APP2WINE(stack
[1], W32S_OFFSET
);
1227 TRACE(vxd
, "NtUnmapViewOfSection(%lx, %lx)\n",
1228 ProcessHandle
, (DWORD
)BaseAddress
);
1230 UnmapViewOfFile(BaseAddress
);
1232 EAX_reg(context
) = STATUS_SUCCESS
;
1237 case 0x0013: /* NtFlushVirtualMemory */
1239 * Input: EDX: Flat address of arguments on stack
1241 * DWORD ProcessHandle [in] Process (defining address space)
1242 * LPBYTE *BaseAddress [in?] Base address of range to be flushed
1243 * DWORD *ViewSize [in?] Number of bytes to be flushed
1244 * DWORD *unknown [???] (?? unknown ??)
1246 * Output: EAX: NtStatus
1249 DWORD
*stack
= (DWORD
*)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
1250 DWORD ProcessHandle
= stack
[0]; /* ignored */
1251 DWORD
*BaseAddress
= (DWORD
*)W32S_APP2WINE(stack
[1], W32S_OFFSET
);
1252 DWORD
*ViewSize
= (DWORD
*)W32S_APP2WINE(stack
[2], W32S_OFFSET
);
1253 DWORD
*unknown
= (DWORD
*)W32S_APP2WINE(stack
[3], W32S_OFFSET
);
1255 LPBYTE address
= (LPBYTE
)(BaseAddress
? W32S_APP2WINE(*BaseAddress
, W32S_OFFSET
) : 0);
1256 DWORD size
= ViewSize
? *ViewSize
: 0;
1258 TRACE(vxd
, "NtFlushVirtualMemory(%lx, %lx, %lx, %lx)\n",
1259 ProcessHandle
, (DWORD
)BaseAddress
, (DWORD
)ViewSize
,
1261 TRACE(vxd
, "NtFlushVirtualMemory: base=%lx, size=%lx\n",
1262 (DWORD
)address
, size
);
1264 FlushViewOfFile(address
, size
);
1266 EAX_reg(context
) = STATUS_SUCCESS
;
1271 case 0x0014: /* Get/Set Debug Registers */
1273 * Input: ECX: 0 if Get, 1 if Set
1275 * EDX: Get: Flat address of buffer to receive values of
1276 * debug registers DR0 .. DR7
1277 * Set: Flat address of buffer containing values of
1278 * debug registers DR0 .. DR7 to be set
1282 FIXME(vxd
, "[0014] ECX=%lx EDX=%lx\n",
1283 ECX_reg(context
), EDX_reg(context
));
1289 case 0x0015: /* Set Coprocessor Emulation Flag */
1291 * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation
1296 TRACE(vxd
, "[0015] EDX=%lx\n", EDX_reg(context
));
1298 /* We don't care, as we always have a coprocessor anyway */
1302 case 0x0016: /* Init Win32S VxD PSP */
1304 * If called to query required PSP size:
1307 * Output: EDX: Required size of Win32s VxD PSP
1309 * If called to initialize allocated PSP:
1311 * Input: EBX: LoWord: Selector of Win32s VxD PSP
1312 * HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1316 if (EBX_reg(context
) == 0)
1317 EDX_reg(context
) = 0x80;
1320 PDB16
*psp
= PTR_SEG_OFF_TO_LIN(BX_reg(context
), 0);
1322 psp
->fileHandlesPtr
= MAKELONG(HIWORD(EBX_reg(context
)), 0x5c);
1323 memset((LPBYTE
)psp
+ 0x5c, '\xFF', 32);
1328 case 0x0017: /* Set Break Point */
1330 * Input: EBX: Offset of Break Point
1331 * CX: Selector of Break Point
1336 FIXME(vxd
, "[0017] EBX=%lx CX=%x\n",
1337 EBX_reg(context
), CX_reg(context
));
1343 case 0x0018: /* VirtualLock */
1345 * Input: ECX: Current Process
1347 * EDX: Flat address of arguments on stack
1349 * DWORD *retv [out] TRUE if success, FALSE if failure
1350 * LPVOID base [in] Flat address of range to lock
1351 * DWORD size [in] Size of range
1353 * Output: EAX: NtStatus
1356 DWORD
*stack
= (DWORD
*)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
1357 DWORD
*retv
= (DWORD
*)W32S_APP2WINE(stack
[0], W32S_OFFSET
);
1358 LPVOID base
= (LPVOID
) W32S_APP2WINE(stack
[1], W32S_OFFSET
);
1359 DWORD size
= stack
[2];
1362 TRACE(vxd
, "VirtualLock(%lx, %lx, %lx)\n",
1363 (DWORD
)retv
, (DWORD
)base
, size
);
1365 result
= VirtualLock(base
, size
);
1369 EAX_reg(context
) = STATUS_SUCCESS
;
1372 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1377 case 0x0019: /* VirtualUnlock */
1379 * Input: ECX: Current Process
1381 * EDX: Flat address of arguments on stack
1383 * DWORD *retv [out] TRUE if success, FALSE if failure
1384 * LPVOID base [in] Flat address of range to unlock
1385 * DWORD size [in] Size of range
1387 * Output: EAX: NtStatus
1390 DWORD
*stack
= (DWORD
*)W32S_APP2WINE(EDX_reg(context
), W32S_OFFSET
);
1391 DWORD
*retv
= (DWORD
*)W32S_APP2WINE(stack
[0], W32S_OFFSET
);
1392 LPVOID base
= (LPVOID
) W32S_APP2WINE(stack
[1], W32S_OFFSET
);
1393 DWORD size
= stack
[2];
1396 TRACE(vxd
, "VirtualUnlock(%lx, %lx, %lx)\n",
1397 (DWORD
)retv
, (DWORD
)base
, size
);
1399 result
= VirtualUnlock(base
, size
);
1403 EAX_reg(context
) = STATUS_SUCCESS
;
1406 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1411 case 0x001A: /* KGetSystemInfo */
1415 * Output: ECX: Start of sparse memory arena
1416 * EDX: End of sparse memory arena
1419 TRACE(vxd
, "KGetSystemInfo()\n");
1422 * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as
1423 * sparse memory arena. We do it the other way around, since
1424 * we have to reserve 3GB - 4GB for Linux, and thus use
1425 * 0GB - 3GB as sparse memory arena.
1427 * FIXME: What about other OSes ?
1430 ECX_reg(context
) = W32S_WINE2APP(0x00000000, W32S_OFFSET
);
1431 EDX_reg(context
) = W32S_WINE2APP(0xbfffffff, W32S_OFFSET
);
1435 case 0x001B: /* KGlobalMemStat */
1437 * Input: ESI: Flat address of buffer to receive memory info
1442 struct Win32sMemoryInfo
1444 DWORD DIPhys_Count
; /* Total physical pages */
1445 DWORD DIFree_Count
; /* Free physical pages */
1446 DWORD DILin_Total_Count
; /* Total virtual pages (private arena) */
1447 DWORD DILin_Total_Free
; /* Free virtual pages (private arena) */
1449 DWORD SparseTotal
; /* Total size of sparse arena (bytes ?) */
1450 DWORD SparseFree
; /* Free size of sparse arena (bytes ?) */
1453 struct Win32sMemoryInfo
*info
=
1454 (struct Win32sMemoryInfo
*)W32S_APP2WINE(ESI_reg(context
), W32S_OFFSET
);
1456 FIXME(vxd
, "KGlobalMemStat(%lx)\n", (DWORD
)info
);
1463 case 0x001C: /* Enable/Disable Exceptions */
1465 * Input: ECX: 0 to disable, 1 to enable exception handling
1470 TRACE(vxd
, "[001c] ECX=%lx\n", ECX_reg(context
));
1476 case 0x001D: /* VirtualAlloc called from 16-bit code */
1478 * Input: EDX: Segmented address of arguments on stack
1480 * LPVOID base [in] Flat address of region to reserve/commit
1481 * DWORD size [in] Size of region
1482 * DWORD type [in] Type of allocation
1483 * DWORD prot [in] Type of access protection
1485 * Output: EAX: NtStatus
1486 * EDX: Flat base address of allocated region
1489 DWORD
*stack
= PTR_SEG_OFF_TO_LIN(LOWORD(EDX_reg(context
)),
1490 HIWORD(EDX_reg(context
)));
1491 LPVOID base
= (LPVOID
)W32S_APP2WINE(stack
[0], W32S_OFFSET
);
1492 DWORD size
= stack
[1];
1493 DWORD type
= stack
[2];
1494 DWORD prot
= stack
[3];
1497 TRACE(vxd
, "VirtualAlloc16(%lx, %lx, %lx, %lx)\n",
1498 (DWORD
)base
, size
, type
, prot
);
1500 if (type
& 0x80000000)
1502 WARN(vxd
, "VirtualAlloc16: strange type %lx\n", type
);
1506 result
= (DWORD
)VirtualAlloc(base
, size
, type
, prot
);
1508 if (W32S_WINE2APP(result
, W32S_OFFSET
))
1509 EDX_reg(context
) = W32S_WINE2APP(result
, W32S_OFFSET
),
1510 EAX_reg(context
) = STATUS_SUCCESS
;
1512 EDX_reg(context
) = 0,
1513 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1514 TRACE(vxd
, "VirtualAlloc16: returning base %lx\n", EDX_reg(context
));
1519 case 0x001E: /* VirtualFree called from 16-bit code */
1521 * Input: EDX: Segmented address of arguments on stack
1523 * LPVOID base [in] Flat address of region
1524 * DWORD size [in] Size of region
1525 * DWORD type [in] Type of operation
1527 * Output: EAX: NtStatus
1528 * EDX: TRUE if success, FALSE if failure
1531 DWORD
*stack
= PTR_SEG_OFF_TO_LIN(LOWORD(EDX_reg(context
)),
1532 HIWORD(EDX_reg(context
)));
1533 LPVOID base
= (LPVOID
)W32S_APP2WINE(stack
[0], W32S_OFFSET
);
1534 DWORD size
= stack
[1];
1535 DWORD type
= stack
[2];
1538 TRACE(vxd
, "VirtualFree16(%lx, %lx, %lx)\n",
1539 (DWORD
)base
, size
, type
);
1541 result
= VirtualFree(base
, size
, type
);
1544 EDX_reg(context
) = TRUE
,
1545 EAX_reg(context
) = STATUS_SUCCESS
;
1547 EDX_reg(context
) = FALSE
,
1548 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1553 case 0x001F: /* FWorkingSetSize */
1555 * Input: EDX: 0 if Get, 1 if Set
1557 * ECX: Get: Buffer to receive Working Set Size
1558 * Set: Buffer containing Working Set Size
1563 DWORD
*ptr
= (DWORD
*)W32S_APP2WINE(ECX_reg(context
), W32S_OFFSET
);
1564 BOOL set
= EDX_reg(context
);
1566 TRACE(vxd
, "FWorkingSetSize(%lx, %lx)\n", (DWORD
)ptr
, (DWORD
)set
);
1569 /* We do it differently ... */;
1573 EAX_reg(context
) = STATUS_SUCCESS
;
1579 VXD_BARF( context
, "W32S" );