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