Changed default paper size to letter.
[wine] / msdos / dpmi.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 <unistd.h>
25 #include <string.h>
26
27 #include "windef.h"
28 #include "wine/winbase16.h"
29 #include "miscemu.h"
30 #include "msdos.h"
31 #include "task.h"
32 #include "toolhelp.h"
33 #include "selectors.h"
34 #include "callback.h"
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(int31);
38
39 #define DOS_GET_DRIVE(reg) ((reg) ? (reg) - 1 : DRIVE_GetCurrentDrive())
40
41 void CreateBPB(int drive, BYTE *data, BOOL16 limited);  /* defined in int21.c */
42
43 static void* lastvalloced = NULL;
44
45
46 DOSVM_TABLE Dosvm = { NULL, };
47
48 static HMODULE DosModule;
49
50 /**********************************************************************
51  *          DPMI_LoadDosSystem
52  */
53 BOOL DPMI_LoadDosSystem(void)
54 {
55     if (DosModule) return TRUE;
56     DosModule = LoadLibraryA( "winedos.dll" );
57     if (!DosModule) {
58         ERR("could not load winedos.dll, DOS subsystem unavailable\n");
59         return FALSE;
60     }
61 #define GET_ADDR(func)  Dosvm.func = (void *)GetProcAddress(DosModule, #func);
62
63     GET_ADDR(LoadDosExe);
64     GET_ADDR(CallRMInt);
65     GET_ADDR(CallRMProc);
66     GET_ADDR(AllocRMCB);
67     GET_ADDR(FreeRMCB);
68     GET_ADDR(SetTimer);
69     GET_ADDR(GetTimer);
70     GET_ADDR(inport);
71     GET_ADDR(outport);
72     GET_ADDR(ASPIHandler);
73 #undef GET_ADDR
74     return TRUE;
75 }
76
77 /**********************************************************************
78  *          DPMI_xalloc
79  * special virtualalloc, allocates lineary monoton growing memory.
80  * (the usual VirtualAlloc does not satisfy that restriction)
81  */
82 static LPVOID
83 DPMI_xalloc(int len) {
84         LPVOID  ret;
85         LPVOID  oldlastv = lastvalloced;
86
87         if (lastvalloced) {
88                 int     xflag = 0;
89                 ret = NULL;
90                 while (!ret) {
91                         ret=VirtualAlloc(lastvalloced,len,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);
92                         if (!ret)
93                                 lastvalloced = (char *) lastvalloced + 0x10000;
94                         /* we failed to allocate one in the first round. 
95                          * try non-linear
96                          */
97                         if (!xflag && (lastvalloced<oldlastv)) { /* wrapped */
98                                 FIXME("failed to allocate lineary growing memory (%d bytes), using non-linear growing...\n",len);
99                                 xflag++;
100                         }
101                         /* if we even fail to allocate something in the next 
102                          * round, return NULL
103                          */
104                         if ((xflag==1) && (lastvalloced >= oldlastv))
105                                 xflag++;
106                         if ((xflag==2) && (lastvalloced < oldlastv)) {
107                                 FIXME("failed to allocate any memory of %d bytes!\n",len);
108                                 return NULL;
109                         }
110                 }
111         } else
112                  ret=VirtualAlloc(NULL,len,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);
113         lastvalloced = (LPVOID)(((DWORD)ret+len+0xffff)&~0xffff);
114         return ret;
115 }
116
117 static void
118 DPMI_xfree(LPVOID ptr) {
119         VirtualFree(ptr,0,MEM_RELEASE);
120 }
121
122 /* FIXME: perhaps we could grow this mapped area... */
123 static LPVOID
124 DPMI_xrealloc(LPVOID ptr,DWORD newsize) {
125         MEMORY_BASIC_INFORMATION        mbi;
126         LPVOID                          newptr;
127
128         newptr = DPMI_xalloc(newsize);
129         if (ptr) {
130                 if (!VirtualQuery(ptr,&mbi,sizeof(mbi))) {
131                         FIXME("realloc of DPMI_xallocd region %p?\n",ptr);
132                         return NULL;
133                 }
134                 if (mbi.State == MEM_FREE) {
135                         FIXME("realloc of DPMI_xallocd region %p?\n",ptr);
136                         return NULL;
137                 }
138                 /* We do not shrink allocated memory. most reallocs
139                  * only do grows anyway
140                  */
141                 if (newsize<=mbi.RegionSize)
142                         return ptr;
143                 memcpy(newptr,ptr,mbi.RegionSize);
144                 DPMI_xfree(ptr);
145         }
146         return newptr;
147 }
148
149 /**********************************************************************
150  *          CallRMInt
151  */
152 static void CallRMInt( CONTEXT86 *context )
153 {
154     if (!Dosvm.CallRMInt && !DPMI_LoadDosSystem())
155     {
156         ERR("could not setup real-mode calls\n");
157         return;
158     }
159     Dosvm.CallRMInt( context );
160 }
161
162
163 static void CallRMProc( CONTEXT86 *context, int iret )
164 {
165     if (!Dosvm.CallRMProc && !DPMI_LoadDosSystem())
166     {
167         ERR("could not setup real-mode calls\n");
168         return;
169     }
170     Dosvm.CallRMProc( context, iret );
171 }
172
173 static void AllocRMCB( CONTEXT86 *context )
174 {
175     if (!Dosvm.AllocRMCB && !DPMI_LoadDosSystem())
176     {
177         ERR("could not setup real-mode calls\n");
178         return;
179     }
180     Dosvm.AllocRMCB( context );
181 }
182
183 static void FreeRMCB( CONTEXT86 *context )
184 {
185     if (!Dosvm.FreeRMCB)  /* cannot have allocated one if dosvm not loaded */
186     {
187         SET_LOWORD( context->Eax, 0x8024 ); /* invalid callback address */
188         SET_CFLAG( context );
189     }
190     else Dosvm.FreeRMCB( context );
191 }
192
193
194 /**********************************************************************
195  *          INT_Int31Handler (WPROCS.149)
196  *
197  * Handler for int 31h (DPMI).
198  */
199
200 void WINAPI INT_Int31Handler( CONTEXT86 *context )
201 {
202     /*
203      * Note: For Win32s processes, the whole linear address space is
204      *       shifted by 0x10000 relative to the OS linear address space.
205      *       See the comment in msdos/vxd.c.
206      */
207     DWORD dw;
208     BYTE *ptr;
209
210     RESET_CFLAG(context);
211     switch(AX_reg(context))
212     {
213     case 0x0000:  /* Allocate LDT descriptors */
214         TRACE("allocate LDT descriptors (%d)\n",CX_reg(context));
215         if (!(context->Eax = AllocSelectorArray16( CX_reg(context) )))
216         {
217             TRACE("failed\n");
218             context->Eax = 0x8011;  /* descriptor unavailable */
219             SET_CFLAG(context);
220         }
221         TRACE("success, array starts at 0x%04x\n",AX_reg(context));
222         break;
223
224     case 0x0001:  /* Free LDT descriptor */
225         TRACE("free LDT descriptor (0x%04x)\n",BX_reg(context));
226         if (FreeSelector16( BX_reg(context) ))
227         {
228             context->Eax = 0x8022;  /* invalid selector */
229             SET_CFLAG(context);
230         }
231         else
232         {
233             /* If a segment register contains the selector being freed, */
234             /* set it to zero. */
235             if (!((context->SegDs^BX_reg(context)) & ~3)) context->SegDs = 0;
236             if (!((context->SegEs^BX_reg(context)) & ~3)) context->SegEs = 0;
237             if (!((context->SegFs^BX_reg(context)) & ~3)) context->SegFs = 0;
238             if (!((context->SegGs^BX_reg(context)) & ~3)) context->SegGs = 0;
239         }
240         break;
241
242     case 0x0002:  /* Real mode segment to descriptor */
243         TRACE("real mode segment to descriptor (0x%04x)\n",BX_reg(context));
244         {
245             WORD entryPoint = 0;  /* KERNEL entry point for descriptor */
246             switch(BX_reg(context))
247             {
248             case 0x0000: entryPoint = 183; break;  /* __0000H */
249             case 0x0040: entryPoint = 193; break;  /* __0040H */
250             case 0xa000: entryPoint = 174; break;  /* __A000H */
251             case 0xb000: entryPoint = 181; break;  /* __B000H */
252             case 0xb800: entryPoint = 182; break;  /* __B800H */
253             case 0xc000: entryPoint = 195; break;  /* __C000H */
254             case 0xd000: entryPoint = 179; break;  /* __D000H */
255             case 0xe000: entryPoint = 190; break;  /* __E000H */
256             case 0xf000: entryPoint = 194; break;  /* __F000H */
257             default:
258                 context->Eax = DOSMEM_AllocSelector(BX_reg(context));
259                 break;
260             }
261             if (entryPoint) 
262                 context->Eax = LOWORD(GetProcAddress16( GetModuleHandle16( "KERNEL" ),
263                                                         (LPCSTR)(ULONG_PTR)entryPoint ));
264         }
265         break;
266
267     case 0x0003:  /* Get next selector increment */
268         TRACE("get selector increment (__AHINCR)\n");
269         context->Eax = __AHINCR;
270         break;
271
272     case 0x0004:  /* Lock selector (not supported) */
273         FIXME("lock selector not supported\n");
274         context->Eax = 0;  /* FIXME: is this a correct return value? */
275         break;
276
277     case 0x0005:  /* Unlock selector (not supported) */
278         FIXME("unlock selector not supported\n");
279         context->Eax = 0;  /* FIXME: is this a correct return value? */
280         break;
281
282     case 0x0006:  /* Get selector base address */
283         TRACE("get selector base address (0x%04x)\n",BX_reg(context));
284         if (!(dw = GetSelectorBase( BX_reg(context) )))
285         {
286             context->Eax = 0x8022;  /* invalid selector */
287             SET_CFLAG(context);
288         }
289         else
290         {
291             CX_reg(context) = HIWORD(W32S_WINE2APP(dw));
292             DX_reg(context) = LOWORD(W32S_WINE2APP(dw));
293         }
294         break;
295
296     case 0x0007:  /* Set selector base address */
297         TRACE("set selector base address (0x%04x,0x%08lx)\n",
298                      BX_reg(context),
299                      W32S_APP2WINE(MAKELONG(DX_reg(context),CX_reg(context))));
300         dw = W32S_APP2WINE(MAKELONG(DX_reg(context), CX_reg(context)));
301         if (dw < 0x10000)
302             /* app wants to access lower 64K of DOS memory, load DOS subsystem */
303             DPMI_LoadDosSystem();
304         SetSelectorBase(BX_reg(context), dw);
305         break;
306
307     case 0x0008:  /* Set selector limit */
308         TRACE("set selector limit (0x%04x,0x%08lx)\n",BX_reg(context),MAKELONG(DX_reg(context),CX_reg(context)));
309         dw = MAKELONG( DX_reg(context), CX_reg(context) );
310         SetSelectorLimit16( BX_reg(context), dw );
311         break;
312
313     case 0x0009:  /* Set selector access rights */
314         TRACE("set selector access rights(0x%04x,0x%04x)\n",BX_reg(context),CX_reg(context));
315         SelectorAccessRights16( BX_reg(context), 1, CX_reg(context) );
316         break;
317
318     case 0x000a:  /* Allocate selector alias */
319         TRACE("allocate selector alias (0x%04x)\n",BX_reg(context));
320         if (!(AX_reg(context) = AllocCStoDSAlias16( BX_reg(context) )))
321         {
322             AX_reg(context) = 0x8011;  /* descriptor unavailable */
323             SET_CFLAG(context);
324         }
325         break;
326
327     case 0x000b:  /* Get descriptor */
328         TRACE("get descriptor (0x%04x)\n",BX_reg(context));
329         {
330             LDT_ENTRY entry;
331             wine_ldt_set_base( &entry, (void*)W32S_WINE2APP(wine_ldt_get_base(&entry)) );
332             /* FIXME: should use ES:EDI for 32-bit clients */
333             *(LDT_ENTRY *)MapSL( MAKESEGPTR( context->SegEs, LOWORD(context->Edi) )) = entry;
334         }
335         break;
336
337     case 0x000c:  /* Set descriptor */
338         TRACE("set descriptor (0x%04x)\n",BX_reg(context));
339         {
340             LDT_ENTRY entry = *(LDT_ENTRY *)MapSL(MAKESEGPTR( context->SegEs, LOWORD(context->Edi)));
341             wine_ldt_set_base( &entry, (void*)W32S_APP2WINE(wine_ldt_get_base(&entry)) );
342             wine_ldt_set_entry( LOWORD(context->Ebx), &entry );
343         }
344         break;
345
346     case 0x000d:  /* Allocate specific LDT descriptor */
347         FIXME("allocate descriptor (0x%04x), stub!\n",BX_reg(context));
348         AX_reg(context) = 0x8011; /* descriptor unavailable */
349         SET_CFLAG(context);
350         break;
351     case 0x0100:  /* Allocate DOS memory block */
352         TRACE("allocate DOS memory block (0x%x paragraphs)\n",BX_reg(context));
353         dw = GlobalDOSAlloc16((DWORD)BX_reg(context)<<4);
354         if (dw) {
355             AX_reg(context) = HIWORD(dw);
356             DX_reg(context) = LOWORD(dw);
357         } else {
358             AX_reg(context) = 0x0008; /* insufficient memory */
359             BX_reg(context) = DOSMEM_Available()>>4;
360             SET_CFLAG(context);
361         }
362         break;
363     case 0x0101:  /* Free DOS memory block */
364         TRACE("free DOS memory block (0x%04x)\n",DX_reg(context));
365         dw = GlobalDOSFree16(DX_reg(context));
366         if (!dw) {
367             AX_reg(context) = 0x0009; /* memory block address invalid */
368             SET_CFLAG(context);
369         }
370         break;
371     case 0x0200: /* get real mode interrupt vector */
372         FIXME("get realmode interupt vector(0x%02x) unimplemented.\n",
373               BL_reg(context));
374         SET_CFLAG(context);
375         break;
376     case 0x0201: /* set real mode interrupt vector */
377         FIXME("set realmode interrupt vector(0x%02x,0x%04x:0x%04x) unimplemented\n", BL_reg(context),CX_reg(context),DX_reg(context));
378         SET_CFLAG(context);
379         break;
380     case 0x0204:  /* Get protected mode interrupt vector */
381         TRACE("get protected mode interrupt handler (0x%02x), stub!\n",BL_reg(context));
382         dw = (DWORD)INT_GetPMHandler( BL_reg(context) );
383         CX_reg(context) = HIWORD(dw);
384         DX_reg(context) = LOWORD(dw);
385         break;
386
387     case 0x0205:  /* Set protected mode interrupt vector */
388         TRACE("set protected mode interrupt handler (0x%02x,%p), stub!\n",
389             BL_reg(context),MapSL(MAKESEGPTR(CX_reg(context),DX_reg(context))));
390         INT_SetPMHandler( BL_reg(context),
391                           (FARPROC16)MAKESEGPTR( CX_reg(context), DX_reg(context) ));
392         break;
393
394     case 0x0300:  /* Simulate real mode interrupt */
395         CallRMInt( context );
396         break;
397
398     case 0x0301:  /* Call real mode procedure with far return */
399         CallRMProc( context, FALSE );
400         break;
401
402     case 0x0302:  /* Call real mode procedure with interrupt return */
403         CallRMProc( context, TRUE );
404         break;
405
406     case 0x0303:  /* Allocate Real Mode Callback Address */
407         AllocRMCB( context );
408         break;
409
410     case 0x0304:  /* Free Real Mode Callback Address */
411         FreeRMCB( context );
412         break;
413
414     case 0x0305:  /* Get State Save/Restore Addresses */
415         TRACE("get state save/restore addresses\n");
416         /* we probably won't need this kind of state saving */
417         AX_reg(context) = 0;
418         /* real mode: just point to the lret */
419         BX_reg(context) = DOSMEM_wrap_seg;
420         context->Ecx = 2;
421         /* protected mode: don't have any handler yet... */
422         FIXME("no protected-mode dummy state save/restore handler yet\n");
423         SI_reg(context) = 0;
424         context->Edi = 0;
425         break;
426
427     case 0x0306:  /* Get Raw Mode Switch Addresses */
428         TRACE("get raw mode switch addresses\n");
429         /* real mode, point to standard DPMI return wrapper */
430         BX_reg(context) = DOSMEM_wrap_seg;
431         context->Ecx = 0;
432         /* protected mode, point to DPMI call wrapper */
433         SI_reg(context) = DOSMEM_dpmi_sel;
434         context->Edi = 8; /* offset of the INT 0x31 call */
435         break;
436     case 0x0400:  /* Get DPMI version */
437         TRACE("get DPMI version\n");
438         {
439             SYSTEM_INFO si;
440
441             GetSystemInfo(&si);
442             AX_reg(context) = 0x005a;  /* DPMI version 0.90 */
443             BX_reg(context) = 0x0005;  /* Flags: 32-bit, virtual memory */
444             CL_reg(context) = si.wProcessorLevel;
445             DX_reg(context) = 0x0102;  /* Master/slave interrupt controller base*/
446             break;
447         }
448     case 0x0500:  /* Get free memory information */
449         TRACE("get free memory information\n");
450         {
451             MEMMANINFO mmi;
452
453             mmi.dwSize = sizeof(mmi);
454             MemManInfo16(&mmi);
455             ptr = MapSL(MAKESEGPTR(context->SegEs,DI_reg(context)));
456             /* the layout is just the same as MEMMANINFO, but without
457              * the dwSize entry.
458              */
459             memcpy(ptr,((char*)&mmi)+4,sizeof(mmi)-4);
460             break;
461         }
462     case 0x0501:  /* Allocate memory block */
463         TRACE("allocate memory block (%ld)\n",MAKELONG(CX_reg(context),BX_reg(context)));
464         if (!(ptr = (BYTE *)DPMI_xalloc(MAKELONG(CX_reg(context), BX_reg(context)))))
465         {
466             AX_reg(context) = 0x8012;  /* linear memory not available */
467             SET_CFLAG(context);
468         } else {
469             BX_reg(context) = SI_reg(context) = HIWORD(W32S_WINE2APP(ptr));
470             CX_reg(context) = DI_reg(context) = LOWORD(W32S_WINE2APP(ptr));
471         }
472         break;
473
474     case 0x0502:  /* Free memory block */
475         TRACE("free memory block (0x%08lx)\n",
476                      W32S_APP2WINE(MAKELONG(DI_reg(context),SI_reg(context))));
477         DPMI_xfree( (void *)W32S_APP2WINE(MAKELONG(DI_reg(context), SI_reg(context))) );
478         break;
479
480     case 0x0503:  /* Resize memory block */
481         TRACE("resize memory block (0x%08lx,%ld)\n",
482                      W32S_APP2WINE(MAKELONG(DI_reg(context),SI_reg(context))),
483                      MAKELONG(CX_reg(context),BX_reg(context)));
484         if (!(ptr = (BYTE *)DPMI_xrealloc(
485                 (void *)W32S_APP2WINE(MAKELONG(DI_reg(context),SI_reg(context))),
486                 MAKELONG(CX_reg(context),BX_reg(context)))))
487         {
488             AX_reg(context) = 0x8012;  /* linear memory not available */
489             SET_CFLAG(context);
490         } else {
491             BX_reg(context) = SI_reg(context) = HIWORD(W32S_WINE2APP(ptr));
492             CX_reg(context) = DI_reg(context) = LOWORD(W32S_WINE2APP(ptr));
493         }
494         break;
495
496     case 0x0507:  /* Modify page attributes */
497         FIXME("modify page attributes unimplemented\n");
498         break;  /* Just ignore it */
499
500     case 0x0600:  /* Lock linear region */
501         FIXME("lock linear region unimplemented\n");
502         break;  /* Just ignore it */
503
504     case 0x0601:  /* Unlock linear region */
505         FIXME("unlock linear region unimplemented\n");
506         break;  /* Just ignore it */
507
508     case 0x0602:  /* Unlock real-mode region */
509         FIXME("unlock realmode region unimplemented\n");
510         break;  /* Just ignore it */
511
512     case 0x0603:  /* Lock real-mode region */
513         FIXME("lock realmode region unimplemented\n");
514         break;  /* Just ignore it */
515
516     case 0x0604:  /* Get page size */
517         TRACE("get pagesize\n");
518         BX_reg(context) = 0;
519         CX_reg(context) = getpagesize();
520         break;
521
522     case 0x0702:  /* Mark page as demand-paging candidate */
523         FIXME("mark page as demand-paging candidate\n");
524         break;  /* Just ignore it */
525
526     case 0x0703:  /* Discard page contents */
527         FIXME("discard page contents\n");
528         break;  /* Just ignore it */
529
530      case 0x0800:  /* Physical address mapping */
531         FIXME("map real to linear (0x%08lx)\n",MAKELONG(CX_reg(context),BX_reg(context)));
532          if(!(ptr=DOSMEM_MapRealToLinear(MAKELONG(CX_reg(context),BX_reg(context)))))
533          {
534              AX_reg(context) = 0x8021; 
535              SET_CFLAG(context);
536          }
537          else
538          {
539              BX_reg(context) = HIWORD(W32S_WINE2APP(ptr));
540              CX_reg(context) = LOWORD(W32S_WINE2APP(ptr));
541              RESET_CFLAG(context);
542          }
543          break;
544
545     default:
546         INT_BARF( context, 0x31 );
547         AX_reg(context) = 0x8001;  /* unsupported function */
548         SET_CFLAG(context);
549         break;
550     }
551
552 }