Replace calls to WaitForMultipleObjects with a 0 count by calls to
[wine] / dlls / winedos / int67.c
1 /*
2  * Int67 (EMS) emulation
3  *
4  * Copyright 2002 Jukka Heinonen
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 <assert.h>
22 #include "wine/winbase16.h"
23 #include "dosexe.h"
24 #include "miscemu.h"
25 #include "wine/debug.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(int);
28
29 /*
30  * EMS page size == 16 kilobytes.
31  */
32 #define EMS_PAGE_SIZE (16*1024)
33
34 /*
35  * Linear address of EMS page.
36  */
37 #define EMS_PAGE_ADDRESS(base,page) (((char*)base) + EMS_PAGE_SIZE * page)
38
39 /*
40  * Maximum number of pages that can be allocated using EMS.
41  */
42 #define EMS_MAX_PAGES 1024
43
44 /*
45  * Maximum number of EMS handles (allocated blocks).
46  */
47 #define EMS_MAX_HANDLES 256
48
49 /*
50  * Global EMM Import Record.
51  * Applications can get address of this record
52  * and directly access allocated memory if they use
53  * IOCTL interface.
54  *
55  * FIXME: Missing lots of fields, packing is not correct.
56  */
57
58 struct {
59   struct {
60     UCHAR hindex;  /* handle number */
61     BYTE  flags;   /* bit 0: normal handle rather than system handle */
62     char  name[8]; /* handle name */
63     WORD  pages;   /* allocated pages */
64     void *address; /* physical address*/
65   } handle[EMS_MAX_HANDLES];
66
67   /* Wine specific fields... */
68
69   int   used_pages;     /* Number of allocated pages. */
70   void *frame_address;  /* Address of 64k EMS page frame */
71   WORD  frame_selector; /* Segment of 64k EMS page frame */
72
73   struct {
74     UCHAR hindex;       /* handle number */
75     WORD  logical_page; /* logical page */
76   } mapping[4];
77
78   struct {
79     UCHAR hindex;       /* handle number */
80     WORD  logical_page; /* logical page */
81   } mapping_save_area[EMS_MAX_HANDLES][4];
82
83 } *EMS_record = 0;
84
85 /**********************************************************************
86  *          EMS_init
87  *
88  * Allocates and initialized page frame and EMS global import record.
89  */
90 static void EMS_init(void)
91 {
92   /*
93    * Start of 64k EMS frame.
94    */
95   ULONG base = 0xc0000;
96
97   if(EMS_record)
98     return;
99
100   EMS_record = HeapAlloc(GetProcessHeap(),
101                          HEAP_ZERO_MEMORY,
102                          sizeof(*EMS_record));
103
104   EMS_record->frame_address = (void *)base;
105   EMS_record->frame_selector = base >> 4;
106 }
107
108 /**********************************************************************
109  *          EMS_alloc
110  *
111  * Get handle and allocate memory.
112  */
113 static void EMS_alloc( CONTEXT86 *context )
114 {
115   int hindex = 1; /* handle zero is reserved for system */
116
117   while(hindex < EMS_MAX_HANDLES && EMS_record->handle[hindex].address)
118     hindex++;
119
120   if(hindex == EMS_MAX_HANDLES) {
121     SET_AH( context, 0x85 ); /* status: no more handles available */
122   } else {
123     int   pages = BX_reg(context);
124     void *buffer = HeapAlloc( GetProcessHeap(), 0, pages * EMS_PAGE_SIZE );
125
126     if(!buffer) {
127       SET_AH( context, 0x88 ); /* status: insufficient pages available */
128     } else {
129       EMS_record->handle[hindex].address = buffer;
130       EMS_record->handle[hindex].pages = pages;
131       EMS_record->used_pages += pages;
132
133       SET_DX( context, hindex ); /* handle to allocated memory*/
134       SET_AH( context, 0 );      /* status: ok */
135     }
136   }
137 }
138
139 /**********************************************************************
140  *          EMS_access_name
141  *
142  * Get/set handle name.
143  */
144 static void EMS_access_name( CONTEXT86 *context )
145 {
146   char *ptr;
147   int hindex = DX_reg(context);
148   if(hindex < 0 || hindex >= EMS_MAX_HANDLES) {
149     SET_AH( context, 0x83 ); /* invalid handle */
150     return;
151   }
152
153   switch AL_reg(context) {
154   case 0x00: /* get name */
155     ptr = PTR_REAL_TO_LIN(context->SegEs, DI_reg(context));
156     memcpy(ptr, EMS_record->handle[hindex].name, 8);
157     SET_AH( context, 0 );
158     break;
159
160   case 0x01: /* set name */
161     ptr = PTR_REAL_TO_LIN(context->SegDs, SI_reg(context));
162     memcpy(EMS_record->handle[hindex].name, ptr, 8);
163     SET_AH( context, 0 );
164     break;
165
166   default:
167     INT_BARF(context,0x67);
168     break;
169   }
170 }
171
172 /**********************************************************************
173  *          EMS_map
174  *
175  * Map logical page into physical page.
176  */
177 static BYTE EMS_map( WORD physical_page, WORD new_hindex, WORD new_logical_page )
178 {
179   int   old_hindex;
180   int   old_logical_page;
181   void *physical_address;
182
183   if(physical_page > 3)
184     return 0x8b; /* status: invalid physical page */
185
186   old_hindex = EMS_record->mapping[physical_page].hindex;
187   old_logical_page = EMS_record->mapping[physical_page].logical_page;
188   physical_address = EMS_PAGE_ADDRESS(EMS_record->frame_address, physical_page);
189
190   /* unmap old page */
191   if(old_hindex) {
192     void *ptr = EMS_PAGE_ADDRESS(EMS_record->handle[old_hindex].address,
193                                  old_logical_page);
194     memcpy(ptr, physical_address, EMS_PAGE_SIZE);
195   }
196
197   /* map new page */
198   if(new_hindex && new_logical_page != 0xffff) {
199     void *ptr = EMS_PAGE_ADDRESS(EMS_record->handle[new_hindex].address,
200                                  new_logical_page);
201
202     if(new_hindex >= EMS_MAX_HANDLES || !EMS_record->handle[new_hindex].address)
203       return 0x83; /* status: invalid handle */
204
205     if(new_logical_page >= EMS_record->handle[new_hindex].pages)
206       return 0x8a; /* status: invalid logical page */
207
208     memcpy(physical_address, ptr, EMS_PAGE_SIZE);
209     EMS_record->mapping[physical_page].hindex = new_hindex;
210     EMS_record->mapping[physical_page].logical_page = new_logical_page;
211   } else {
212     EMS_record->mapping[physical_page].hindex = 0;
213     EMS_record->mapping[physical_page].logical_page = 0;
214   }
215
216   return 0; /* status: ok */
217 }
218
219 /**********************************************************************
220  *          EMS_map_multiple
221  *
222  * Map multiple logical pages into physical pages.
223  */
224 static void EMS_map_multiple( CONTEXT86 *context )
225 {
226   WORD *ptr = PTR_REAL_TO_LIN(context->SegDs, SI_reg(context));
227   BYTE  status = 0;
228   int   i;
229
230   for(i=0; i<CX_reg(context) && !status; i++, ptr += 2)
231     switch(AL_reg(context)) {
232     case 0x00:
233       status = EMS_map( ptr[1],
234                        DX_reg(context), ptr[0] );
235       break;
236     case 0x01:
237       status = EMS_map( (ptr[1] - EMS_record->frame_selector) >> 10,
238                        DX_reg(context), ptr[0] );
239       break;
240     default:
241       status = 0x8f; /* status: undefined subfunction */
242     }
243
244   SET_AH( context, status );
245 }
246
247 /**********************************************************************
248  *          EMS_free
249  *
250  * Free memory and release handle.
251  */
252 static void EMS_free( CONTEXT86 *context )
253 {
254   int hindex = DX_reg(context);
255   int i;
256
257   if(hindex < 0 || hindex >= EMS_MAX_HANDLES) {
258     SET_AH( context, 0x83 ); /* status: invalid handle */
259     return;
260   }
261
262   if(!EMS_record->handle[hindex].address) {
263     SET_AH( context, 0 ); /* status: ok */
264     return;
265   }
266
267   EMS_record->used_pages -= EMS_record->handle[hindex].pages;
268
269   /* unmap pages */
270   for(i=0; i<4; i++)
271     if(EMS_record->mapping[i].hindex == hindex)
272       EMS_record->mapping[i].hindex = 0;
273
274   /* free block */
275   HeapFree( GetProcessHeap(), 0, EMS_record->handle[hindex].address );
276   EMS_record->handle[hindex].address = 0;
277
278   SET_AH( context, 0 );    /* status: ok */
279 }
280
281 /**********************************************************************
282  *          EMS_save_context
283  *
284  * Save physical page mappings into handle specific save area.
285  */
286 static void EMS_save_context( CONTEXT86 *context )
287 {
288   WORD h = DX_reg(context);
289   int  i;
290
291   for(i=0; i<4; i++) {
292     EMS_record->mapping_save_area[h][i].hindex = EMS_record->mapping[i].hindex;
293     EMS_record->mapping_save_area[h][i].logical_page = EMS_record->mapping[i].logical_page;
294   }
295
296   SET_AX( context, 0 ); /* status: ok */
297 }
298
299
300 /**********************************************************************
301  *          EMS_restore_context
302  *
303  * Restore physical page mappings from handle specific save area.
304  */
305 static void EMS_restore_context( CONTEXT86 *context )
306 {
307   WORD handle = DX_reg(context);
308   int  i;
309
310   for(i=0; i<4; i++) {
311     int hindex       = EMS_record->mapping_save_area[handle][i].hindex;
312     int logical_page = EMS_record->mapping_save_area[handle][i].logical_page;
313
314     if(EMS_map( i, hindex, logical_page )) {
315       SET_AX( context, 0x8e ); /* status: restore of mapping context failed */
316       return;
317     }
318   }
319
320   SET_AX( context, 0 ); /* status: ok */
321 }
322
323 /**********************************************************************
324  *          DOSVM_Int67Handler (WINEDOS16.203)
325  *
326  * Handler for interrupt 67h EMS routines.
327  */
328 void WINAPI DOSVM_Int67Handler( CONTEXT86 *context )
329 {
330   switch AH_reg(context) {
331
332   case 0x40: /* EMS - GET MANAGER STATUS */
333     SET_AH( context, 0 ); /* status: ok */
334     break;
335
336   case 0x41: /* EMS - GET PAGE FRAME SEGMENT */
337     EMS_init();
338     SET_BX( context, EMS_record->frame_selector ); /* segment of page frame */
339     SET_AH( context, 0 );                          /* status: ok */
340     break;
341
342   case 0x42: /* EMS - GET NUMBER OF PAGES */
343     EMS_init();
344     /* unallocated 16k pages */
345     SET_BX( context, EMS_MAX_PAGES - EMS_record->used_pages );
346     /* total number of 16k pages */
347     SET_DX( context, EMS_MAX_PAGES );
348     /* status: ok */
349     SET_AH( context, 0 );
350     break;
351
352   case 0x43: /* EMS - GET HANDLE AND ALLOCATE MEMORY */
353     EMS_init();
354     EMS_alloc(context);
355     break;
356
357   case 0x44: /* EMS - MAP MEMORY */
358     EMS_init();
359     SET_AH( context, EMS_map( AL_reg(context), DX_reg(context), BX_reg(context) ) );
360     break;
361
362   case 0x45: /* EMS - RELEASE HANDLE AND MEMORY */
363     EMS_init();
364     EMS_free(context);
365     break;
366
367   case 0x46: /* EMS - GET EMM VERSION */
368     SET_AL( context, 0x40 ); /* version 4.0 */
369     SET_AH( context, 0 );    /* status: ok */
370     break;
371
372   case 0x47: /* EMS - SAVE MAPPING CONTEXT */
373     EMS_init();
374     EMS_save_context(context);
375     break;
376
377   case 0x48: /* EMS - RESTORE MAPPING CONTEXT */
378     EMS_init();
379     EMS_restore_context(context);
380     break;
381
382   case 0x49: /* EMS - reserved - GET I/O PORT ADDRESSES */
383   case 0x4a: /* EMS - reserved - GET TRANSLATION ARRAY */
384     INT_BARF(context,0x67);
385     break;
386
387   case 0x4b: /* EMS - GET NUMBER OF EMM HANDLES */
388     SET_BX( context, EMS_MAX_HANDLES ); /* EMM handles */
389     SET_AH( context, 0 );               /* status: ok */
390     break;
391
392   case 0x4c: /* EMS - GET PAGES OWNED BY HANDLE */
393   case 0x4d: /* EMS - GET PAGES FOR ALL HANDLES */
394   case 0x4e: /* EMS - GET OR SET PAGE MAP */
395   case 0x4f: /* EMS 4.0 - GET/SET PARTIAL PAGE MAP */
396     INT_BARF(context,0x67);
397     break;
398
399   case 0x50: /* EMS 4.0 - MAP/UNMAP MULTIPLE HANDLE PAGES */
400     EMS_init();
401     EMS_map_multiple(context);
402     break;
403
404   case 0x51: /* EMS 4.0 - REALLOCATE PAGES */
405   case 0x52: /* EMS 4.0 - GET/SET HANDLE ATTRIBUTES */
406     INT_BARF(context,0x67);
407     break;
408
409   case 0x53: /* EMS 4.0 - GET/SET HANDLE NAME */
410     EMS_init();
411     EMS_access_name(context);
412     break;
413
414   case 0x54: /* EMS 4.0 - GET HANDLE DIRECTORY */
415   case 0x55: /* EMS 4.0 - ALTER PAGE MAP AND JUMP */
416   case 0x56: /* EMS 4.0 - ALTER PAGE MAP AND CALL */
417   case 0x57: /* EMS 4.0 - MOVE/EXCHANGE MEMORY REGION */
418   case 0x58: /* EMS 4.0 - GET MAPPABLE PHYSICAL ADDRESS ARRAY */
419     INT_BARF(context,0x67);
420     break;
421
422   case 0x59: /* EMS 4.0 - GET EXPANDED MEMORY HARDWARE INFORMATION */
423     if(AL_reg(context) == 0x01) {
424       EMS_init();
425       /* unallocated raw pages */
426       SET_BX( context, EMS_MAX_PAGES - EMS_record->used_pages );
427       /* total number raw pages */
428       SET_DX( context, EMS_MAX_PAGES );
429       /* status: ok */
430       SET_AH( context, 0 );
431     } else
432       INT_BARF(context,0x67);
433     break;
434
435   case 0x5a: /* EMS 4.0 - ALLOCATE STANDARD/RAW PAGES */
436   case 0x5b: /* EMS 4.0 - ALTERNATE MAP REGISTER SET */
437   case 0x5c: /* EMS 4.0 - PREPARE EXPANDED MEMORY HARDWARE FOR WARM BOOT */
438   case 0x5d: /* EMS 4.0 - ENABLE/DISABLE OS FUNCTION SET FUNCTIONS */
439     INT_BARF(context,0x67);
440     break;
441
442   case 0xde: /* Virtual Control Program Interface (VCPI) */
443     if(AL_reg(context) == 0x00) {
444       /*
445        * VCPI INSTALLATION CHECK
446        * (AH_reg() != 0) means VCPI is not present
447        */
448       TRACE("- VCPI installation check\n");
449       return;
450     } else
451       INT_BARF(context,0x67);
452     break;
453
454   default:
455     INT_BARF(context,0x67);
456   }
457 }
458
459
460 /**********************************************************************
461  *          EMS_Ioctl_Handler
462  *
463  * Handler for interrupt 21h IOCTL routine for device "EMMXXXX0".
464  */
465 void WINAPI EMS_Ioctl_Handler( CONTEXT86 *context )
466 {
467   assert(AH_reg(context) == 0x44);
468
469   switch AL_reg(context) {
470   case 0x00: /* IOCTL - GET DEVICE INFORMATION */
471       RESET_CFLAG(context); /* operation was successful */
472       SET_DX( context, 0x4080 ); /* bit 14 (support ioctl read) and
473                                  * bit 7 (is_device) */
474       break;
475
476   case 0x02: /* EMS - GET MEMORY MANAGER INFORMATION */
477       /*
478        * This is what is called "Windows Global EMM Import Specification".
479        * Undocumented of course! Supports three requests:
480        * GET API ENTRY POINT
481        * GET EMM IMPORT STRUCTURE ADDRESS
482        * GET MEMORY MANAGER VERSION
483        */
484       INT_BARF(context,0x21);
485       break;
486
487   case 0x07: /* IOCTL - GET OUTPUT STATUS */
488       RESET_CFLAG(context); /* operation was successful */
489       SET_AL( context, 0xff ); /* device is ready */
490       break;
491
492   default:
493       INT_BARF(context,0x21);
494       break;
495   }
496 }