Fixed header dependencies to be fully compatible with the Windows
[wine] / dlls / winedos / int31.c
1 /*
2  * DPMI 0.9 emulation
3  *
4  * Copyright 1995 Alexandre Julliard
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 <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wine/winbase16.h"
29 #include "wownt32.h"
30 #include "miscemu.h"
31 #include "task.h"
32 #include "msdos.h"
33 #include "dosexe.h"
34
35 #include "excpt.h"
36 #include "wine/debug.h"
37 #include "wine/exception.h"
38 #include "thread.h"
39 #include "toolhelp.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(int31);
42
43 /* Structure for real-mode callbacks */
44 typedef struct
45 {
46     DWORD edi;
47     DWORD esi;
48     DWORD ebp;
49     DWORD reserved;
50     DWORD ebx;
51     DWORD edx;
52     DWORD ecx;
53     DWORD eax;
54     WORD  fl;
55     WORD  es;
56     WORD  ds;
57     WORD  fs;
58     WORD  gs;
59     WORD  ip;
60     WORD  cs;
61     WORD  sp;
62     WORD  ss;
63 } REALMODECALL;
64
65 typedef struct tagRMCB {
66     DWORD address;
67     DWORD proc_ofs,proc_sel;
68     DWORD regs_ofs,regs_sel;
69     struct tagRMCB *next;
70 } RMCB;
71
72 static RMCB *FirstRMCB = NULL;
73 static WORD dpmi_flag;
74 static void* lastvalloced = NULL;
75 static BYTE DPMI_retval;
76
77 /**********************************************************************
78  *          DOSVM_IsDos32
79  * 
80  * Return TRUE if we are in 32-bit protected mode DOS process.
81  */
82 BOOL DOSVM_IsDos32(void)
83 {
84   return (dpmi_flag & 1) ? TRUE : FALSE;
85 }
86
87
88 /**********************************************************************
89  *          dpmi_exception_handler
90  *
91  * Handle EXCEPTION_VM86_STI exceptions generated
92  * when there are pending asynchronous events.
93  */
94 static WINE_EXCEPTION_FILTER(dpmi_exception_handler)
95 {
96 #ifdef __i386__
97     EXCEPTION_RECORD *rec = GetExceptionInformation()->ExceptionRecord;
98     CONTEXT *context = GetExceptionInformation()->ContextRecord;
99
100     if (rec->ExceptionCode == EXCEPTION_VM86_STI)
101     {
102         if (ISV86(context))
103             ERR( "Real mode STI caught by protected mode handler!\n" );
104         DOSVM_SendQueuedEvents(context);
105         return EXCEPTION_CONTINUE_EXECUTION;
106     }
107     else if (rec->ExceptionCode == EXCEPTION_VM86_INTx)
108     {
109         if (ISV86(context))
110             ERR( "Real mode INTx caught by protected mode handler!\n" );
111         DPMI_retval = (BYTE)rec->ExceptionInformation[0];
112         return EXCEPTION_EXECUTE_HANDLER;
113     }
114
115 #endif
116     return EXCEPTION_CONTINUE_SEARCH;
117 }
118
119
120 /**********************************************************************
121  *          INT_GetRealModeContext
122  */
123 static void INT_GetRealModeContext( REALMODECALL *call, CONTEXT86 *context )
124 {
125     context->Eax    = call->eax;
126     context->Ebx    = call->ebx;
127     context->Ecx    = call->ecx;
128     context->Edx    = call->edx;
129     context->Esi    = call->esi;
130     context->Edi    = call->edi;
131     context->Ebp    = call->ebp;
132     context->EFlags = call->fl | V86_FLAG;
133     context->Eip    = call->ip;
134     context->Esp    = call->sp;
135     context->SegCs  = call->cs;
136     context->SegDs  = call->ds;
137     context->SegEs  = call->es;
138     context->SegFs  = call->fs;
139     context->SegGs  = call->gs;
140     context->SegSs  = call->ss;
141 }
142
143
144 /**********************************************************************
145  *          INT_SetRealModeContext
146  */
147 static void INT_SetRealModeContext( REALMODECALL *call, CONTEXT86 *context )
148 {
149     call->eax = context->Eax;
150     call->ebx = context->Ebx;
151     call->ecx = context->Ecx;
152     call->edx = context->Edx;
153     call->esi = context->Esi;
154     call->edi = context->Edi;
155     call->ebp = context->Ebp;
156     call->fl  = LOWORD(context->EFlags);
157     call->ip  = LOWORD(context->Eip);
158     call->sp  = LOWORD(context->Esp);
159     call->cs  = context->SegCs;
160     call->ds  = context->SegDs;
161     call->es  = context->SegEs;
162     call->fs  = context->SegFs;
163     call->gs  = context->SegGs;
164     call->ss  = context->SegSs;
165 }
166
167 /**********************************************************************
168  *          DPMI_xalloc
169  * special virtualalloc, allocates lineary monoton growing memory.
170  * (the usual VirtualAlloc does not satisfy that restriction)
171  */
172 static LPVOID DPMI_xalloc( DWORD len ) 
173 {
174     LPVOID  ret;
175     LPVOID  oldlastv = lastvalloced;
176
177     if (lastvalloced) 
178     {
179         int xflag = 0;
180
181         ret = NULL;
182         while (!ret) 
183         {
184             ret = VirtualAlloc( lastvalloced, len,
185                                 MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE );
186             if (!ret)
187                 lastvalloced = (char *) lastvalloced + 0x10000;
188
189             /* we failed to allocate one in the first round.
190              * try non-linear
191              */
192             if (!xflag && (lastvalloced<oldlastv)) 
193             { 
194                 /* wrapped */
195                 FIXME( "failed to allocate linearly growing memory (%ld bytes), "
196                        "using non-linear growing...\n", len );
197                 xflag++;
198             }
199
200             /* if we even fail to allocate something in the next
201              * round, return NULL
202              */
203             if ((xflag==1) && (lastvalloced >= oldlastv))
204                 xflag++;
205
206             if ((xflag==2) && (lastvalloced < oldlastv)) {
207                 FIXME( "failed to allocate any memory of %ld bytes!\n", len );
208                 return NULL;
209             }
210         }
211     } 
212     else
213     {
214         ret = VirtualAlloc( NULL, len, 
215                             MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE );
216     }
217
218     lastvalloced = (LPVOID)(((DWORD)ret+len+0xffff)&~0xffff);
219     return ret;
220 }
221
222 /**********************************************************************
223  *          DPMI_xfree
224  */
225 static void DPMI_xfree( LPVOID ptr ) 
226 {
227     VirtualFree( ptr, 0, MEM_RELEASE );
228 }
229
230 /**********************************************************************
231  *          DPMI_xrealloc
232  *
233  * FIXME: perhaps we could grow this mapped area... 
234  */
235 static LPVOID DPMI_xrealloc( LPVOID ptr, DWORD newsize ) 
236 {
237     MEMORY_BASIC_INFORMATION        mbi;
238     LPVOID                          newptr;
239
240     newptr = DPMI_xalloc( newsize );
241     if (ptr) 
242     {
243         if (!VirtualQuery(ptr,&mbi,sizeof(mbi))) 
244         {
245             FIXME( "realloc of DPMI_xallocd region %p?\n", ptr );
246             return NULL;
247         }
248
249         if (mbi.State == MEM_FREE) 
250         {
251             FIXME( "realloc of DPMI_xallocd region %p?\n", ptr );
252             return NULL;
253         }
254
255         /* We do not shrink allocated memory. most reallocs
256          * only do grows anyway
257          */
258         if (newsize <= mbi.RegionSize)
259             return ptr;
260
261         memcpy( newptr, ptr, mbi.RegionSize );
262         DPMI_xfree( ptr );
263     }
264
265     return newptr;
266 }
267
268
269 #ifdef __i386__
270
271 void DPMI_CallRMCB32(RMCB *rmcb, UINT16 ss, DWORD esp, UINT16*es, DWORD*edi)
272 #if 0 /* original code, which early gccs puke on */
273 {
274     int _clobber;
275     __asm__ __volatile__(
276         "pushl %%ebp\n"
277         "pushl %%ebx\n"
278         "pushl %%es\n"
279         "pushl %%ds\n"
280         "pushfl\n"
281         "mov %7,%%es\n"
282         "mov %5,%%ds\n"
283         ".byte 0x36, 0xff, 0x18\n" /* lcall *%ss:(%eax) */
284         "popl %%ds\n"
285         "mov %%es,%0\n"
286         "popl %%es\n"
287         "popl %%ebx\n"
288         "popl %%ebp\n"
289     : "=d" (*es), "=D" (*edi), "=S" (_clobber), "=a" (_clobber), "=c" (_clobber)
290     : "0" (ss), "2" (esp),
291       "4" (rmcb->regs_sel), "1" (rmcb->regs_ofs),
292       "3" (&rmcb->proc_ofs) );
293 }
294 #else /* code generated by a gcc new enough */
295 ;
296 __ASM_GLOBAL_FUNC(DPMI_CallRMCB32,
297     "pushl %ebp\n\t"
298     "movl %esp,%ebp\n\t"
299     "pushl %edi\n\t"
300     "pushl %esi\n\t"
301     "movl 0x8(%ebp),%eax\n\t"
302     "movl 0x10(%ebp),%esi\n\t"
303     "movl 0xc(%ebp),%edx\n\t"
304     "movl 0x10(%eax),%ecx\n\t"
305     "movl 0xc(%eax),%edi\n\t"
306     "addl $0x4,%eax\n\t"
307     "pushl %ebp\n\t"
308     "pushl %ebx\n\t"
309     "pushl %es\n\t"
310     "pushl %ds\n\t"
311     "pushfl\n\t"
312     "mov %cx,%es\n\t"
313     "mov %dx,%ds\n\t"
314     ".byte 0x36, 0xff, 0x18\n\t" /* lcall *%ss:(%eax) */
315     "popl %ds\n\t"
316     "mov %es,%dx\n\t"
317     "popl %es\n\t"
318     "popl %ebx\n\t"
319     "popl %ebp\n\t"
320     "movl 0x14(%ebp),%eax\n\t"
321     "movw %dx,(%eax)\n\t"
322     "movl 0x18(%ebp),%edx\n\t"
323     "movl %edi,(%edx)\n\t"
324     "popl %esi\n\t"
325     "popl %edi\n\t"
326     "leave\n\t"
327     "ret")
328 #endif
329
330 #endif /* __i386__ */
331
332 /**********************************************************************
333  *          DPMI_CallRMCBProc
334  *
335  * This routine does the hard work of calling a callback procedure.
336  */
337 static void DPMI_CallRMCBProc( CONTEXT86 *context, RMCB *rmcb, WORD flag )
338 {
339     DWORD old_vif = NtCurrentTeb()->dpmi_vif;
340
341     /* Disable virtual interrupts. */
342     NtCurrentTeb()->dpmi_vif = 0;
343
344     if (IS_SELECTOR_SYSTEM( rmcb->proc_sel )) {
345         /* Wine-internal RMCB, call directly */
346         ((RMCBPROC)rmcb->proc_ofs)(context);
347     } else __TRY {
348 #ifdef __i386__
349         UINT16 ss,es;
350         DWORD esp,edi;
351
352         INT_SetRealModeContext(MapSL(MAKESEGPTR( rmcb->regs_sel, rmcb->regs_ofs )), context);
353         ss = SELECTOR_AllocBlock( (void *)(context->SegSs<<4), 0x10000, WINE_LDT_FLAGS_DATA );
354         esp = context->Esp;
355
356         FIXME("untested!\n");
357
358         /* The called proc ends with an IRET, and takes these parameters:
359          * DS:ESI = pointer to real-mode SS:SP
360          * ES:EDI = pointer to real-mode call structure
361          * It returns:
362          * ES:EDI = pointer to real-mode call structure (may be a copy)
363          * It is the proc's responsibility to change the return CS:IP in the
364          * real-mode call structure. */
365         if (flag & 1) {
366             /* 32-bit DPMI client */
367             DPMI_CallRMCB32(rmcb, ss, esp, &es, &edi);
368         } else {
369             /* 16-bit DPMI client */
370             CONTEXT86 ctx = *context;
371             ctx.SegCs = rmcb->proc_sel;
372             ctx.Eip   = rmcb->proc_ofs;
373             ctx.SegDs = ss;
374             ctx.Esi   = esp;
375             ctx.SegEs = rmcb->regs_sel;
376             ctx.Edi   = rmcb->regs_ofs;
377             /* FIXME: I'm pretty sure this isn't right - should push flags first */
378             WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&ctx );
379             es = ctx.SegEs;
380             edi = ctx.Edi;
381         }
382         FreeSelector16(ss);
383         INT_GetRealModeContext( MapSL( MAKESEGPTR( es, edi )), context);
384 #else
385         ERR("RMCBs only implemented for i386\n");
386 #endif
387     } __EXCEPT(dpmi_exception_handler) { } __ENDTRY
388         
389     /* Restore virtual interrupt flag. */
390     NtCurrentTeb()->dpmi_vif = old_vif;                                 
391 }
392
393
394 /**********************************************************************
395  *          DPMI_CallRMProc
396  *
397  * This routine does the hard work of calling a real mode procedure.
398  */
399 int DPMI_CallRMProc( CONTEXT86 *context, LPWORD stack, int args, int iret )
400 {
401     LPWORD stack16;
402     LPVOID addr = NULL; /* avoid gcc warning */
403     RMCB *CurrRMCB;
404     int alloc = 0, already = 0;
405     BYTE *code;
406
407     TRACE("EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n",
408                  context->Eax, context->Ebx, context->Ecx, context->Edx );
409     TRACE("ESI=%08lx EDI=%08lx ES=%04lx DS=%04lx CS:IP=%04lx:%04x, %d WORD arguments, %s\n",
410                  context->Esi, context->Edi, context->SegEs, context->SegDs,
411                  context->SegCs, LOWORD(context->Eip), args, iret?"IRET":"FAR" );
412
413 callrmproc_again:
414
415 /* there might be some code that just jumps to RMCBs or the like,
416    in which case following the jumps here might get us to a shortcut */
417     code = CTX_SEG_OFF_TO_LIN(context, context->SegCs, context->Eip);
418     switch (*code) {
419     case 0xe9: /* JMP NEAR */
420       context->Eip += 3 + *(WORD *)(code+1);
421       /* yeah, I know these gotos don't look good... */
422       goto callrmproc_again;
423     case 0xea: /* JMP FAR */
424       context->Eip = *(WORD *)(code+1);
425       context->SegCs = *(WORD *)(code+3);
426       /* ...but since the label is there anyway... */
427       goto callrmproc_again;
428     case 0xeb: /* JMP SHORT */
429       context->Eip += 2 + *(signed char *)(code+1);
430       /* ...because of other gotos below, so... */
431       goto callrmproc_again;
432     }
433
434 /* shortcut for chaining to internal interrupt handlers */
435     if ((context->SegCs == 0xF000) && iret)
436     {
437         DOSVM_CallBuiltinHandler( context, LOWORD(context->Eip)/4 );
438         return 0;
439     }
440
441 /* shortcut for RMCBs */
442     CurrRMCB = FirstRMCB;
443
444     while (CurrRMCB && (HIWORD(CurrRMCB->address) != context->SegCs))
445         CurrRMCB = CurrRMCB->next;
446
447     if (!CurrRMCB && !MZ_Current())
448     {
449         FIXME("DPMI real-mode call using DOS VM task system, not fully tested!\n");
450         TRACE("creating VM86 task\n");
451         MZ_AllocDPMITask();
452     }
453     if (!already) {
454         if (!context->SegSs) {
455             alloc = 1; /* allocate default stack */
456             stack16 = addr = DOSMEM_GetBlock( 64, (UINT16 *)&(context->SegSs) );
457             context->Esp = 64-2;
458             stack16 += 32-1;
459             if (!addr) {
460                 ERR("could not allocate default stack\n");
461                 return 1;
462             }
463         } else {
464             stack16 = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
465         }
466         context->Esp -= (args + (iret?1:0)) * sizeof(WORD);
467         stack16 -= args;
468         if (args) memcpy(stack16, stack, args*sizeof(WORD) );
469         /* push flags if iret */
470         if (iret) {
471             stack16--; args++;
472             *stack16 = LOWORD(context->EFlags);
473         }
474         /* push return address (return to interrupt wrapper) */
475         *(--stack16) = DOSVM_dpmi_segments->wrap_seg;
476         *(--stack16) = 0;
477         /* adjust stack */
478         context->Esp -= 2*sizeof(WORD);
479         already = 1;
480     }
481
482     if (CurrRMCB) {
483         /* RMCB call, invoke protected-mode handler directly */
484         DPMI_CallRMCBProc(context, CurrRMCB, dpmi_flag);
485         /* check if we returned to where we thought we would */
486         if ((context->SegCs != DOSVM_dpmi_segments->wrap_seg) ||
487             (LOWORD(context->Eip) != 0)) {
488             /* we need to continue at different address in real-mode space,
489                so we need to set it all up for real mode again */
490             goto callrmproc_again;
491         }
492     } else {
493         TRACE("entering real mode...\n");
494         DOSVM_Enter( context );
495         TRACE("returned from real-mode call\n");
496     }
497     if (alloc) DOSMEM_FreeBlock( addr );
498     return 0;
499 }
500
501
502 /**********************************************************************
503  *          CallRMInt   (WINEDOS.@)
504  */
505 void WINAPI DOSVM_CallRMInt( CONTEXT86 *context )
506 {
507     CONTEXT86 realmode_ctx;
508     FARPROC16 rm_int = DOSVM_GetRMHandler( BL_reg(context) );
509     REALMODECALL *call = CTX_SEG_OFF_TO_LIN( context, 
510                                              context->SegEs, 
511                                              context->Edi );
512     INT_GetRealModeContext( call, &realmode_ctx );
513
514     /* we need to check if a real-mode program has hooked the interrupt */
515     if (HIWORD(rm_int)!=0xF000) {
516         /* yup, which means we need to switch to real mode... */
517         realmode_ctx.SegCs = HIWORD(rm_int);
518         realmode_ctx.Eip   = LOWORD(rm_int);
519         if (DPMI_CallRMProc( &realmode_ctx, NULL, 0, TRUE))
520           SET_CFLAG(context);
521     } else {
522         RESET_CFLAG(context);
523         /* use the IP we have instead of BL_reg, in case some apps
524            decide to move interrupts around for whatever reason... */
525         DOSVM_CallBuiltinHandler( &realmode_ctx, LOWORD(rm_int)/4 );
526     }
527     INT_SetRealModeContext( call, &realmode_ctx );
528 }
529
530
531 /**********************************************************************
532  *          CallRMProc   (WINEDOS.@)
533  */
534 void WINAPI DOSVM_CallRMProc( CONTEXT86 *context, int iret )
535 {
536     REALMODECALL *p = CTX_SEG_OFF_TO_LIN( context, 
537                                           context->SegEs, 
538                                           context->Edi );
539     CONTEXT86 context16;
540
541     TRACE("RealModeCall: EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n",
542           p->eax, p->ebx, p->ecx, p->edx);
543     TRACE("              ESI=%08lx EDI=%08lx ES=%04x DS=%04x CS:IP=%04x:%04x, %d WORD arguments, %s\n",
544           p->esi, p->edi, p->es, p->ds, p->cs, p->ip, CX_reg(context), iret?"IRET":"FAR" );
545
546     if (!(p->cs) && !(p->ip)) { /* remove this check
547                                    if Int21/6501 case map function
548                                    has been implemented */
549         SET_CFLAG(context);
550         return;
551      }
552     INT_GetRealModeContext(p, &context16);
553     DPMI_CallRMProc( &context16, ((LPWORD)MapSL(MAKESEGPTR(context->SegSs, LOWORD(context->Esp))))+3,
554                      CX_reg(context), iret );
555     INT_SetRealModeContext(p, &context16);
556 }
557
558
559 /* (see dosmem.c, function DOSMEM_InitDPMI) */
560 static void StartPM( CONTEXT86 *context )
561 {
562     UINT16 cs, ss, ds, es;
563     CONTEXT86 pm_ctx;
564     DWORD psp_ofs = (DWORD)(DOSVM_psp<<4);
565     PDB16 *psp = (PDB16 *)psp_ofs;
566     HANDLE16 env_seg = psp->environment;
567     unsigned char selflags = WINE_LDT_FLAGS_DATA;
568
569     RESET_CFLAG(context);
570     dpmi_flag = AX_reg(context);
571 /* our mode switch wrapper have placed the desired CS into DX */
572     cs = SELECTOR_AllocBlock( (void *)(DX_reg(context)<<4), 0x10000, WINE_LDT_FLAGS_CODE );
573 /* due to a flaw in some CPUs (at least mine), it is best to mark stack segments as 32-bit if they
574    can be used in 32-bit code. Otherwise, these CPUs may not set the high word of esp during a
575    ring transition (from kernel code) to the 16-bit stack, and this causes trouble if executing
576    32-bit code using this stack. */
577     if (dpmi_flag & 1) selflags |= WINE_LDT_FLAGS_32BIT;
578     ss = SELECTOR_AllocBlock( (void *)(context->SegSs<<4), 0x10000, selflags );
579 /* do the same for the data segments, just in case */
580     if (context->SegDs == context->SegSs) ds = ss;
581     else ds = SELECTOR_AllocBlock( (void *)(context->SegDs<<4), 0x10000, selflags );
582     es = SELECTOR_AllocBlock( psp, 0x100, selflags );
583 /* convert environment pointer, as the spec says, but we're a bit lazy about the size here... */
584     psp->environment = SELECTOR_AllocBlock( (void *)(env_seg<<4), 0x10000, WINE_LDT_FLAGS_DATA );
585
586     pm_ctx = *context;
587     pm_ctx.SegCs = DOSVM_dpmi_segments->dpmi_sel;
588 /* our mode switch wrapper expects the new CS in DX, and the new SS in AX */
589     pm_ctx.Eax   = ss;
590     pm_ctx.Edx   = cs;
591     pm_ctx.SegDs = ds;
592     pm_ctx.SegEs = es;
593     pm_ctx.SegFs = 0;
594     pm_ctx.SegGs = 0;
595
596     TRACE("DOS program is now entering %d-bit protected mode\n", 
597           DOSVM_IsDos32() ? 32 : 16);
598
599     __TRY 
600     {
601         WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&pm_ctx );
602     } 
603     __EXCEPT(dpmi_exception_handler) 
604     { 
605     } 
606     __ENDTRY
607
608     TRACE( "Protected mode DOS program is terminating\n" );
609
610     /*
611      * FIXME: Instead of calling ExitThread, we should release all
612      *        allocated protected mode resources and call MZ_Exit
613      *        using real mode context. See DPMI specification.
614      */
615     ExitThread( DPMI_retval );
616
617 #if 0
618     FreeSelector16(psp->environment);
619     psp->environment = env_seg;
620     FreeSelector16(es);
621     if (ds != ss) FreeSelector16(ds);
622     FreeSelector16(ss);
623     FreeSelector16(cs);
624 #endif
625 }
626
627 static RMCB *DPMI_AllocRMCB( void )
628 {
629     RMCB *NewRMCB = HeapAlloc(GetProcessHeap(), 0, sizeof(RMCB));
630     UINT16 uParagraph;
631
632     if (NewRMCB)
633     {
634         LPVOID RMCBmem = DOSMEM_GetBlock(4, &uParagraph);
635         LPBYTE p = RMCBmem;
636
637         *p++ = 0xcd; /* RMCB: */
638         *p++ = 0x31; /* int $0x31 */
639 /* it is the called procedure's task to change the return CS:EIP
640    the DPMI 0.9 spec states that if it doesn't, it will be called again */
641         *p++ = 0xeb;
642         *p++ = 0xfc; /* jmp RMCB */
643         NewRMCB->address = MAKELONG(0, uParagraph);
644         NewRMCB->next = FirstRMCB;
645         FirstRMCB = NewRMCB;
646     }
647     return NewRMCB;
648 }
649
650
651 FARPROC16 WINAPI DPMI_AllocInternalRMCB( RMCBPROC proc )
652 {
653     RMCB *NewRMCB = DPMI_AllocRMCB();
654
655     if (NewRMCB) {
656         NewRMCB->proc_ofs = (DWORD)proc;
657         NewRMCB->proc_sel = 0;
658         NewRMCB->regs_ofs = 0;
659         NewRMCB->regs_sel = 0;
660         return (FARPROC16)(NewRMCB->address);
661     }
662     return NULL;
663 }
664
665
666 static int DPMI_FreeRMCB( DWORD address )
667 {
668     RMCB *CurrRMCB = FirstRMCB;
669     RMCB *PrevRMCB = NULL;
670
671     while (CurrRMCB && (CurrRMCB->address != address))
672     {
673         PrevRMCB = CurrRMCB;
674         CurrRMCB = CurrRMCB->next;
675     }
676     if (CurrRMCB)
677     {
678         if (PrevRMCB)
679         PrevRMCB->next = CurrRMCB->next;
680             else
681         FirstRMCB = CurrRMCB->next;
682         DOSMEM_FreeBlock(PTR_REAL_TO_LIN(SELECTOROF(CurrRMCB->address),OFFSETOF(CurrRMCB->address)));
683         HeapFree(GetProcessHeap(), 0, CurrRMCB);
684         return 0;
685     }
686     return 1;
687 }
688
689
690 void WINAPI DPMI_FreeInternalRMCB( FARPROC16 proc )
691 {
692     DPMI_FreeRMCB( (DWORD)proc );
693 }
694
695
696 /**********************************************************************
697  *          DOSVM_RawModeSwitchHandler
698  *
699  * DPMI Raw Mode Switch handler
700  */
701 void WINAPI DOSVM_RawModeSwitchHandler( CONTEXT86 *context )
702 {
703   CONTEXT86 rm_ctx;
704   int ret;
705
706   /* initialize real-mode context as per spec */
707   memset(&rm_ctx, 0, sizeof(rm_ctx));
708   rm_ctx.SegDs  = AX_reg(context);
709   rm_ctx.SegEs  = CX_reg(context);
710   rm_ctx.SegSs  = DX_reg(context);
711   rm_ctx.Esp    = context->Ebx;
712   rm_ctx.SegCs  = SI_reg(context);
713   rm_ctx.Eip    = context->Edi;
714   rm_ctx.Ebp    = context->Ebp;
715   rm_ctx.SegFs  = 0;
716   rm_ctx.SegGs  = 0;
717
718   /* Copy interrupt state. */
719   if (NtCurrentTeb()->dpmi_vif)
720       rm_ctx.EFlags = V86_FLAG | VIF_MASK;
721   else
722       rm_ctx.EFlags = V86_FLAG;
723
724   /* enter real mode again */
725   TRACE("re-entering real mode at %04lx:%04lx\n",rm_ctx.SegCs,rm_ctx.Eip);
726   ret = DOSVM_Enter( &rm_ctx );
727   /* when the real-mode stuff call its mode switch address,
728      DOSVM_Enter will return and we will continue here */
729
730   if (ret<0) {
731     ERR("Sync lost!\n");
732     /* if the sync was lost, there's no way to recover */
733     ExitProcess(1);
734   }
735
736   /* alter protected-mode context as per spec */
737   context->SegDs   = LOWORD(rm_ctx.Eax);
738   context->SegEs   = LOWORD(rm_ctx.Ecx);
739   context->SegSs   = LOWORD(rm_ctx.Edx);
740   context->Esp     = rm_ctx.Ebx;
741   context->SegCs   = LOWORD(rm_ctx.Esi);
742   context->Eip     = rm_ctx.Edi;
743   context->Ebp     = rm_ctx.Ebp;
744   context->SegFs   = 0;
745   context->SegGs   = 0;
746
747   /* Copy interrupt state. */
748   if (rm_ctx.EFlags & VIF_MASK)
749       NtCurrentTeb()->dpmi_vif = 1;
750   else
751       NtCurrentTeb()->dpmi_vif = 0;
752
753   /* Return to new address and hope that we didn't mess up */
754   TRACE("re-entering protected mode at %04lx:%08lx\n",
755       context->SegCs, context->Eip);
756 }
757
758
759 /**********************************************************************
760  *          AllocRMCB   (WINEDOS.@)
761  */
762 void WINAPI DOSVM_AllocRMCB( CONTEXT86 *context )
763 {
764     RMCB *NewRMCB = DPMI_AllocRMCB();
765
766     TRACE("Function to call: %04x:%04x\n", (WORD)context->SegDs, SI_reg(context) );
767
768     if (NewRMCB)
769     {
770        NewRMCB->proc_ofs = DOSVM_IsDos32() ? context->Esi : LOWORD(context->Esi);
771         NewRMCB->proc_sel = context->SegDs;
772        NewRMCB->regs_ofs = DOSVM_IsDos32() ? context->Edi : LOWORD(context->Edi);
773         NewRMCB->regs_sel = context->SegEs;
774         SET_CX( context, HIWORD(NewRMCB->address) );
775         SET_DX( context, LOWORD(NewRMCB->address) );
776     }
777     else
778     {
779         SET_AX( context, 0x8015 ); /* callback unavailable */
780         SET_CFLAG(context);
781     }
782 }
783
784
785 /**********************************************************************
786  *          FreeRMCB   (WINEDOS.@)
787  */
788 void WINAPI DOSVM_FreeRMCB( CONTEXT86 *context )
789 {
790     FIXME("callback address: %04x:%04x\n",
791           CX_reg(context), DX_reg(context));
792
793     if (DPMI_FreeRMCB(MAKELONG(DX_reg(context), CX_reg(context)))) {
794         SET_AX( context, 0x8024 ); /* invalid callback address */
795         SET_CFLAG(context);
796     }
797 }
798
799
800 /**********************************************************************
801  *         DOSVM_CheckWrappers
802  *
803  * Check if this was really a wrapper call instead of an interrupt.
804  */
805 BOOL DOSVM_CheckWrappers( CONTEXT86 *context )
806 {
807     if (context->SegCs==DOSVM_dpmi_segments->dpmi_seg) {
808         /* This is the protected mode switch */
809         StartPM(context);
810         return TRUE;
811     }
812     else if (context->SegCs==DOSVM_dpmi_segments->xms_seg)
813     {
814         /* This is the XMS driver entry point */
815         XMS_Handler(context);
816         return TRUE;
817     }
818     else
819     {
820         /* Check for RMCB */
821         RMCB *CurrRMCB = FirstRMCB;
822
823         while (CurrRMCB && (HIWORD(CurrRMCB->address) != context->SegCs))
824             CurrRMCB = CurrRMCB->next;
825
826         if (CurrRMCB) {
827             /* RMCB call, propagate to protected-mode handler */
828             DPMI_CallRMCBProc(context, CurrRMCB, dpmi_flag);
829             return TRUE;
830         }
831     }
832
833     return FALSE;
834 }
835
836 /**********************************************************************
837  *         DOSVM_Int31Handler (WINEDOS16.149)
838  *
839  * Handler for int 31h (DPMI).
840  */
841 void WINAPI DOSVM_Int31Handler( CONTEXT86 *context )
842 {
843     RESET_CFLAG(context);
844     switch(AX_reg(context))
845     {
846     case 0x0000:  /* Allocate LDT descriptors */
847         TRACE( "allocate LDT descriptors (%d)\n", CX_reg(context) );
848         {
849             WORD sel =  AllocSelectorArray16( CX_reg(context) );
850             if(!sel) 
851             {
852                TRACE( "failed\n" );
853                SET_AX( context, 0x8011 ); /* descriptor unavailable */
854                SET_CFLAG( context );
855             } 
856             else 
857             { 
858                 TRACE( "success, array starts at 0x%04x\n", sel );
859                 SET_AX( context, sel );      
860             }
861         }
862         break;
863
864     case 0x0001:  /* Free LDT descriptor */
865         TRACE( "free LDT descriptor (0x%04x)\n", BX_reg(context) );
866         if (FreeSelector16( BX_reg(context) ))
867         {
868             SET_AX( context, 0x8022 );  /* invalid selector */
869             SET_CFLAG( context );
870         }
871         else
872         {
873             /* If a segment register contains the selector being freed, */
874             /* set it to zero. */
875             if (!((context->SegDs^BX_reg(context)) & ~3)) context->SegDs = 0;
876             if (!((context->SegEs^BX_reg(context)) & ~3)) context->SegEs = 0;
877             if (!((context->SegFs^BX_reg(context)) & ~3)) context->SegFs = 0;
878             if (!((context->SegGs^BX_reg(context)) & ~3)) context->SegGs = 0;
879         }
880         break;
881
882     case 0x0002:  /* Real mode segment to descriptor */
883         TRACE( "real mode segment to descriptor (0x%04x)\n", BX_reg(context) );
884         {
885             WORD entryPoint = 0;  /* KERNEL entry point for descriptor */
886             switch(BX_reg(context))
887             {
888             case 0x0000: entryPoint = 183; break;  /* __0000H */
889             case 0x0040: entryPoint = 193; break;  /* __0040H */
890             case 0xa000: entryPoint = 174; break;  /* __A000H */
891             case 0xb000: entryPoint = 181; break;  /* __B000H */
892             case 0xb800: entryPoint = 182; break;  /* __B800H */
893             case 0xc000: entryPoint = 195; break;  /* __C000H */
894             case 0xd000: entryPoint = 179; break;  /* __D000H */
895             case 0xe000: entryPoint = 190; break;  /* __E000H */
896             case 0xf000: entryPoint = 194; break;  /* __F000H */
897             default:
898                 SET_AX( context, DOSMEM_AllocSelector(BX_reg(context)) );
899                 break;
900             }
901             if (entryPoint)
902             {
903                 FARPROC16 proc = GetProcAddress16( GetModuleHandle16( "KERNEL" ),
904                                                    (LPCSTR)(ULONG_PTR)entryPoint );
905                 SET_AX( context, LOWORD(proc) );
906             }
907         }
908         break;
909
910     case 0x0003:  /* Get next selector increment */
911         TRACE("get selector increment (__AHINCR)\n");
912         context->Eax = __AHINCR;
913         break;
914
915     case 0x0004:  /* Lock selector (not supported) */
916         FIXME("lock selector not supported\n");
917         context->Eax = 0;  /* FIXME: is this a correct return value? */
918         break;
919
920     case 0x0005:  /* Unlock selector (not supported) */
921         FIXME("unlock selector not supported\n");
922         context->Eax = 0;  /* FIXME: is this a correct return value? */
923         break;
924
925     case 0x0006:  /* Get selector base address */
926         TRACE( "get selector base address (0x%04x)\n", BX_reg(context) );
927         {
928             LDT_ENTRY entry;
929             WORD sel = BX_reg(context);
930             wine_ldt_get_entry( sel, &entry );
931             if (wine_ldt_is_empty(&entry))
932             {
933                 context->Eax = 0x8022;  /* invalid selector */
934                 SET_CFLAG(context);
935             }
936             else
937             {
938                 void *base = wine_ldt_get_base(&entry);
939                 SET_CX( context, HIWORD(base) );
940                 SET_DX( context, LOWORD(base) );
941             }
942         }
943         break;
944
945     case 0x0007:  /* Set selector base address */
946         {
947             DWORD base = MAKELONG( DX_reg(context), CX_reg(context) );
948             WORD  sel = BX_reg(context);
949             TRACE( "set selector base address (0x%04x,0x%08lx)\n", sel, base );
950
951             /* check if Win16 app wants to access lower 64K of DOS memory */
952             if (base < 0x10000 && DOSVM_IsWin16())
953                 DOSMEM_Init(TRUE);
954
955             SetSelectorBase( sel, base );
956         }
957         break;
958
959     case 0x0008:  /* Set selector limit */
960         {
961             DWORD limit = MAKELONG( DX_reg(context), CX_reg(context) );
962             TRACE( "set selector limit (0x%04x,0x%08lx)\n",
963                    BX_reg(context), limit );
964             SetSelectorLimit16( BX_reg(context), limit );
965         }
966         break;
967
968     case 0x0009:  /* Set selector access rights */
969         TRACE( "set selector access rights(0x%04x,0x%04x)\n",
970                BX_reg(context), CX_reg(context) );
971         SelectorAccessRights16( BX_reg(context), 1, CX_reg(context) );
972         break;
973
974     case 0x000a:  /* Allocate selector alias */
975         TRACE( "allocate selector alias (0x%04x)\n", BX_reg(context) );
976         SET_AX( context, AllocCStoDSAlias16( BX_reg(context) ) );
977         if (!AX_reg(context))
978         {
979             SET_AX( context, 0x8011 );  /* descriptor unavailable */
980             SET_CFLAG(context);
981         }
982         break;
983
984     case 0x000b:  /* Get descriptor */
985         TRACE( "get descriptor (0x%04x)\n", BX_reg(context) );
986         {
987             LDT_ENTRY *entry = (LDT_ENTRY*)CTX_SEG_OFF_TO_LIN( context,
988                                                                context->SegEs, 
989                                                                context->Edi );
990             wine_ldt_get_entry( BX_reg(context), entry );
991         }
992         break;
993
994     case 0x000c:  /* Set descriptor */
995         TRACE( "set descriptor (0x%04x)\n", BX_reg(context) );
996         {
997             LDT_ENTRY *entry = (LDT_ENTRY*)CTX_SEG_OFF_TO_LIN( context,
998                                                                context->SegEs, 
999                                                                context->Edi );
1000             wine_ldt_set_entry( BX_reg(context), entry );
1001         }
1002         break;
1003
1004     case 0x000d:  /* Allocate specific LDT descriptor */
1005         FIXME( "allocate descriptor (0x%04x), stub!\n", BX_reg(context) );
1006         SET_AX( context, 0x8011 ); /* descriptor unavailable */
1007         SET_CFLAG( context );
1008         break;
1009
1010     case 0x000e:  /* Get Multiple Descriptors (1.0) */
1011         FIXME( "get multiple descriptors - unimplemented\n" );
1012         break;
1013
1014     case 0x000f:  /* Set Multiple Descriptors (1.0) */
1015         FIXME( "set multiple descriptors - unimplemented\n" );
1016         break;
1017
1018     case 0x0100:  /* Allocate DOS memory block */
1019         TRACE( "allocate DOS memory block (0x%x paragraphs)\n", BX_reg(context) );
1020         {
1021             DWORD dw = GlobalDOSAlloc16( (DWORD)BX_reg(context) << 4 );
1022             if (dw) {
1023                 SET_AX( context, HIWORD(dw) );
1024                 SET_DX( context, LOWORD(dw) );
1025             } else {
1026                 SET_AX( context, 0x0008 ); /* insufficient memory */
1027                 SET_BX( context, DOSMEM_Available() >> 4 );
1028                 SET_CFLAG(context);
1029             }
1030             break;
1031         }
1032
1033     case 0x0101:  /* Free DOS memory block */
1034         TRACE( "free DOS memory block (0x%04x)\n", DX_reg(context) );
1035         {
1036             WORD error = GlobalDOSFree16( DX_reg(context) );
1037             if (error) {
1038                 SET_AX( context, 0x0009 ); /* memory block address invalid */
1039                 SET_CFLAG( context );
1040             }
1041         }
1042         break;
1043
1044     case 0x0102: /* Resize DOS Memory Block */
1045         FIXME( "resize DOS memory block (0x%04x, 0x%x paragraphs) - unimplemented\n", 
1046                DX_reg(context), BX_reg(context) );
1047         break;
1048
1049     case 0x0200: /* get real mode interrupt vector */
1050         TRACE( "get realmode interupt vector (0x%02x)\n",
1051                BL_reg(context) );
1052         {
1053             FARPROC16 proc = DOSVM_GetRMHandler( BL_reg(context) );
1054             SET_CX( context, SELECTOROF(proc) );
1055             SET_DX( context, OFFSETOF(proc) );
1056         }
1057         break;
1058
1059     case 0x0201: /* set real mode interrupt vector */
1060         TRACE( "set realmode interrupt vector (0x%02x, 0x%04x:0x%04x)\n", 
1061                BL_reg(context), CX_reg(context), DX_reg(context) );
1062         DOSVM_SetRMHandler( BL_reg(context), 
1063                             (FARPROC16)MAKESEGPTR(CX_reg(context), DX_reg(context)) );
1064         break;
1065
1066     case 0x0202:  /* Get Processor Exception Handler Vector */
1067         FIXME( "Get Processor Exception Handler Vector (0x%02x)\n",
1068                BL_reg(context) );
1069         if (DOSVM_IsDos32()) 
1070         {
1071             SET_CX( context, 0 );
1072             context->Edx = 0;
1073         } 
1074         else 
1075         {
1076             SET_CX( context, 0 );
1077             SET_DX( context, 0 );
1078         }
1079         break;
1080
1081     case 0x0203:  /* Set Processor Exception Handler Vector */
1082          FIXME( "Set Processor Exception Handler Vector (0x%02x)\n",
1083                 BL_reg(context) );
1084          break;
1085
1086     case 0x0204:  /* Get protected mode interrupt vector */
1087         TRACE("get protected mode interrupt handler (0x%02x)\n",
1088               BL_reg(context));
1089         if (DOSVM_IsDos32()) 
1090         {
1091             FARPROC48 handler = DOSVM_GetPMHandler48( BL_reg(context) );
1092             SET_CX( context, handler.selector );
1093             context->Edx = handler.offset;
1094         } 
1095         else 
1096         {
1097             FARPROC16 handler = DOSVM_GetPMHandler16( BL_reg(context) );
1098             SET_CX( context, SELECTOROF(handler) );
1099             SET_DX( context, OFFSETOF(handler) );
1100         }
1101         break;
1102
1103     case 0x0205:  /* Set protected mode interrupt vector */
1104         TRACE("set protected mode interrupt handler (0x%02x,0x%04x:0x%08lx)\n",
1105               BL_reg(context), CX_reg(context), context->Edx);
1106         if (DOSVM_IsDos32()) 
1107         {
1108             FARPROC48 handler;
1109             handler.selector = CX_reg(context);
1110             handler.offset = context->Edx;
1111             DOSVM_SetPMHandler48( BL_reg(context), handler );
1112         } 
1113         else 
1114         {
1115             FARPROC16 handler;
1116             handler = (FARPROC16)MAKESEGPTR( CX_reg(context), DX_reg(context)); 
1117             DOSVM_SetPMHandler16( BL_reg(context), handler );
1118         }
1119         break;
1120
1121     case 0x0300:  /* Simulate real mode interrupt */
1122         TRACE( "Simulate real mode interrupt %02x.\n", BL_reg(context));
1123         DOSVM_CallRMInt( context );
1124         break;
1125
1126     case 0x0301:  /* Call real mode procedure with far return */
1127         TRACE( "Call real mode procedure with far return.\n" );
1128         DOSVM_CallRMProc( context, FALSE );
1129         break;
1130
1131     case 0x0302:  /* Call real mode procedure with interrupt return */
1132         TRACE( "Call real mode procedure with interrupt return.\n" );
1133         DOSVM_CallRMProc( context, TRUE );
1134         break;
1135
1136     case 0x0303:  /* Allocate Real Mode Callback Address */
1137         TRACE( "Allocate real mode callback address.\n" );
1138         DOSVM_AllocRMCB( context );
1139         break;
1140
1141     case 0x0304:  /* Free Real Mode Callback Address */
1142         TRACE( "Free real mode callback address.\n" );
1143         DOSVM_FreeRMCB( context );
1144         break;
1145
1146     case 0x0305:  /* Get State Save/Restore Addresses */
1147         TRACE("get state save/restore addresses\n");
1148         /* we probably won't need this kind of state saving */
1149         SET_AX( context, 0 );
1150
1151         /* real mode: just point to the lret */
1152         SET_BX( context, DOSVM_dpmi_segments->wrap_seg );
1153         SET_CX( context, 2 );
1154
1155         /* protected mode: don't have any handler yet... */
1156         /* FIXME: Use DI in 16-bit DPMI and EDI in 32-bit DPMI */
1157         FIXME("no protected-mode dummy state save/restore handler yet\n");
1158         SET_SI( context, 0 );
1159         context->Edi = 0;
1160         break;
1161
1162     case 0x0306:  /* Get Raw Mode Switch Addresses */
1163         TRACE("get raw mode switch addresses\n");
1164
1165         /* real mode, point to standard DPMI return wrapper */
1166         SET_BX( context, DOSVM_dpmi_segments->wrap_seg );
1167         SET_CX( context, 0 );
1168
1169         /* protected mode, point to DPMI call wrapper */
1170         /* FIXME: Use DI in 16-bit DPMI and EDI in 32-bit DPMI */
1171         /* FIXME: Doesn't work in DPMI32... */
1172         SET_SI( context, DOSVM_dpmi_segments->dpmi_sel );
1173         context->Edi = 8; /* offset of the INT 0x31 call */
1174         break;
1175
1176     case 0x0400:  /* Get DPMI version */
1177         TRACE("get DPMI version\n");
1178         {
1179             SYSTEM_INFO si;
1180
1181             GetSystemInfo(&si);
1182             SET_AX( context, 0x005a );  /* DPMI version 0.90 */
1183             SET_BX( context, 0x0005 );  /* Flags: 32-bit, virtual memory */
1184             SET_CL( context, si.wProcessorLevel );
1185             SET_DX( context, 0x0870 );  /* Master/slave interrupt controller base */
1186         }
1187         break;
1188
1189     case 0x0401:  /* Get DPMI Capabilities (1.0) */
1190         FIXME( "get dpmi capabilities - unimplemented\n");
1191         break;
1192
1193     case 0x0500:  /* Get free memory information */
1194         TRACE("get free memory information\n");
1195         {
1196             MEMMANINFO mmi;
1197             void *ptr = CTX_SEG_OFF_TO_LIN( context,
1198                                             context->SegEs, 
1199                                             context->Edi );
1200
1201             mmi.dwSize = sizeof( mmi );
1202             MemManInfo16( &mmi );
1203
1204             /* the layout is just the same as MEMMANINFO, but without
1205              * the dwSize entry.
1206              */
1207             memcpy( ptr, ((char*)&mmi)+4, sizeof(mmi)-4 );
1208             break;
1209         }
1210
1211     case 0x0501:  /* Allocate memory block */
1212         {
1213             DWORD size = MAKELONG( CX_reg(context), BX_reg(context) );
1214             BYTE *ptr;
1215
1216             TRACE( "allocate memory block (%ld bytes)\n", size );
1217
1218             ptr = (BYTE *)DPMI_xalloc( size );
1219             if (!ptr)
1220             {
1221                 SET_AX( context, 0x8012 );  /* linear memory not available */
1222                 SET_CFLAG(context);
1223             } 
1224             else 
1225             {
1226                 SET_BX( context, HIWORD(ptr) );
1227                 SET_CX( context, LOWORD(ptr) );
1228                 SET_SI( context, HIWORD(ptr) );
1229                 SET_DI( context, LOWORD(ptr) );
1230             }
1231             break;
1232         }
1233
1234     case 0x0502:  /* Free memory block */
1235         {
1236             DWORD handle = MAKELONG( DI_reg(context), SI_reg(context) );
1237             TRACE( "free memory block (0x%08lx)\n", handle );
1238             DPMI_xfree( (void *)handle );
1239         }
1240         break;
1241
1242     case 0x0503:  /* Resize memory block */
1243         {
1244             DWORD size = MAKELONG( CX_reg(context), BX_reg(context) );
1245             DWORD handle = MAKELONG( DI_reg(context), SI_reg(context) );
1246             BYTE *ptr;
1247
1248             TRACE( "resize memory block (0x%08lx, %ld bytes)\n", handle, size );
1249
1250             ptr = (BYTE *)DPMI_xrealloc( (void *)handle, size );
1251             if (!ptr)
1252             {
1253                 SET_AX( context, 0x8012 );  /* linear memory not available */
1254                 SET_CFLAG(context);
1255             } else {
1256                 SET_BX( context, HIWORD(ptr) );
1257                 SET_CX( context, LOWORD(ptr) );
1258                 SET_SI( context, HIWORD(ptr) );
1259                 SET_DI( context, LOWORD(ptr) );
1260             }
1261         }
1262         break;
1263
1264     case 0x0507:  /* Set page attributes (1.0) */
1265         FIXME( "set page attributes - unimplemented\n" );
1266         break;  /* Just ignore it */
1267
1268     case 0x0600:  /* Lock linear region */
1269         TRACE( "lock linear region - ignored (no paging)\n" );
1270         break;
1271
1272     case 0x0601:  /* Unlock linear region */
1273         TRACE( "unlock linear region - ignored (no paging)\n" );
1274         break;
1275
1276     case 0x0602:  /* Mark real mode region as pageable */
1277         TRACE( "mark real mode region as pageable - ignored (no paging)\n" );
1278         break;
1279
1280     case 0x0603:  /* Relock real mode region */
1281         TRACE( "relock real mode region - ignored (no paging)\n" );
1282         break;
1283
1284     case 0x0604:  /* Get page size */
1285         TRACE("get pagesize\n");
1286         SET_BX( context, HIWORD(getpagesize()) );
1287         SET_CX( context, LOWORD(getpagesize()) );
1288         break;
1289
1290     case 0x0700: /* Mark pages as paging candidates */
1291         TRACE( "mark pages as paging candidates - ignored (no paging)\n" );
1292         break;
1293
1294     case 0x0701: /* Discard pages */
1295         TRACE( "discard pages - ignored (no paging)\n" );
1296         break;
1297
1298     case 0x0702:  /* Mark page as demand-paging candidate */
1299         TRACE( "mark page as demand-paging candidate - ignored (no paging)\n" );
1300         break;
1301
1302     case 0x0703:  /* Discard page contents */
1303         TRACE( "discard page contents - ignored (no paging)\n" );
1304         break;
1305
1306     case 0x0800:  /* Physical address mapping */
1307         FIXME( "physical address mapping (0x%08lx) - unimplemented\n", 
1308                MAKELONG(CX_reg(context),BX_reg(context)) );
1309         break;
1310
1311     case 0x0900:  /* Get and Disable Virtual Interrupt State */
1312         TRACE( "Get and Disable Virtual Interrupt State: %ld\n", 
1313                NtCurrentTeb()->dpmi_vif );
1314         SET_AL( context, NtCurrentTeb()->dpmi_vif ? 1 : 0 );
1315         NtCurrentTeb()->dpmi_vif = 0;
1316         break;
1317
1318     case 0x0901:  /* Get and Enable Virtual Interrupt State */
1319         TRACE( "Get and Enable Virtual Interrupt State: %ld\n", 
1320                NtCurrentTeb()->dpmi_vif );
1321         SET_AL( context, NtCurrentTeb()->dpmi_vif ? 1 : 0 );
1322         NtCurrentTeb()->dpmi_vif = 1;
1323         break;
1324
1325     case 0x0902:  /* Get Virtual Interrupt State */
1326         TRACE( "Get Virtual Interrupt State: %ld\n", 
1327                NtCurrentTeb()->dpmi_vif );
1328         SET_AL( context, NtCurrentTeb()->dpmi_vif ? 1 : 0 );
1329         break;
1330
1331     case 0x0e00:  /* Get Coprocessor Status (1.0) */
1332         /*
1333          * Return status in AX bits:
1334          * B0    - MPv (MP bit in the virtual MSW/CR0)
1335          *         0 = numeric coprocessor is disabled for this client
1336          *         1 = numeric coprocessor is enabled for this client
1337          * B1    - EMv (EM bit in the virtual MSW/CR0)
1338          *         0 = client is not emulating coprocessor instructions
1339          *         1 = client is emulating coprocessor instructions
1340          * B2    - MPr (MP bit from the actual MSW/CR0)
1341          *         0 = numeric coprocessor is not present
1342          *         1 = numeric coprocessor is present
1343          * B3    - EMr (EM bit from the actual MSW/CR0)
1344          *         0 = host is not emulating coprocessor instructions
1345          *         1 = host is emulating coprocessor instructions
1346          * B4-B7 - coprocessor type
1347          *         00H = no coprocessor
1348          *         02H = 80287
1349          *         03H = 80387
1350          *         04H = 80486 with numeric coprocessor
1351          *         05H-0FH = reserved for future numeric processors
1352          */
1353         TRACE( "Get Coprocessor Status\n" );
1354         SET_AX( context, 69 ); /* 486, coprocessor present and enabled */ 
1355         break;
1356
1357     case 0x0e01: /* Set Coprocessor Emulation (1.0) */
1358         /*
1359          * See function 0x0e00.
1360          * BX bit B0 is new value for MPv.
1361          * BX bit B1 is new value for EMv.
1362          */
1363         if (BX_reg(context) != 1)
1364             FIXME( "Set Coprocessor Emulation to %d - unimplemented\n", 
1365                    BX_reg(context) );
1366         else
1367             TRACE( "Set Coprocessor Emulation - ignored\n" );
1368         break;
1369
1370     default:
1371         INT_BARF( context, 0x31 );
1372         SET_AX( context, 0x8001 );  /* unsupported function */
1373         SET_CFLAG(context);
1374         break;
1375     }  
1376 }