Better implementation of GetCalendarInfo{A,W}, not perfect.
[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 } *EMS_record = 0;
79
80 /**********************************************************************
81  *          EMS_init 
82  *
83  * Allocates and initialized page frame and EMS global import record.
84  */
85 static void EMS_init(void)
86 {
87   /*
88    * FIXME: Should dynamically allocate upper memory block for EMS frame. 
89    */
90   ULONG base = 0xd0000;
91
92   if(EMS_record)
93     return;
94
95   EMS_record = HeapAlloc(GetProcessHeap(), 
96                          HEAP_ZERO_MEMORY, 
97                          sizeof(*EMS_record));
98   
99   EMS_record->frame_address = DOSMEM_MapDosToLinear(base);
100   EMS_record->frame_selector = base >> 4;
101 }
102
103 /**********************************************************************
104  *          EMS_alloc
105  *
106  * Get handle and allocate memory.
107  */
108 static void EMS_alloc( CONTEXT86 *context )
109 {
110   int hindex = 1; /* handle zero is reserved for system */
111
112   while(hindex < EMS_MAX_HANDLES && EMS_record->handle[hindex].address)
113     hindex++;
114
115   if(hindex == EMS_MAX_HANDLES) {
116     AH_reg(context) = 0x85; /* status: no more handles available */
117   } else {
118     int   pages = BX_reg(context);
119     void *buffer = HeapAlloc( GetProcessHeap(), 0, pages * EMS_PAGE_SIZE );
120
121     if(!buffer) {
122       AH_reg(context) = 0x88; /* status: insufficient pages available */
123     } else {
124       EMS_record->handle[hindex].address = buffer;
125       EMS_record->handle[hindex].pages = pages;
126       EMS_record->used_pages += pages;
127       
128       DX_reg(context) = hindex; /* handle to allocated memory*/  
129       AH_reg(context) = 0;      /* status: ok */
130     }
131   }
132 }
133
134 /**********************************************************************
135  *          EMS_access_name
136  *
137  * Get/set handle name.
138  */
139 static void EMS_access_name( CONTEXT86 *context )
140 {
141   char *ptr;
142   int hindex = DX_reg(context);
143   if(hindex < 0 || hindex >= EMS_MAX_HANDLES) {
144     AH_reg(context) = 0x83; /* invalid handle */
145     return;
146   }
147
148   switch AL_reg(context) {
149   case 0x00: /* get name */
150     ptr = MapSL(MAKESEGPTR(context->SegEs, DI_reg(context)));
151     memcpy(ptr, EMS_record->handle[hindex].name, 8);
152     AH_reg(context) = 0;
153     break;
154
155   case 0x01: /* set name */
156     ptr = MapSL(MAKESEGPTR(context->SegDs, SI_reg(context)));
157     memcpy(EMS_record->handle[hindex].name, ptr, 8);
158     AH_reg(context) = 0;
159     break;
160     
161   default:
162     INT_BARF(context,0x67);
163     break;
164   }
165 }
166
167 /**********************************************************************
168  *          EMS_map
169  *
170  * Map logical page into physical page.
171  */
172 static void EMS_map( CONTEXT86 *context )
173 {
174   int   physical_page = AL_reg(context);
175   int   new_hindex = DX_reg(context);
176   int   new_logical_page = BX_reg(context);
177
178   int   old_hindex = EMS_record->mapping[physical_page].hindex;
179   int   old_logical_page = EMS_record->mapping[physical_page].logical_page;
180
181   void *physical_address = EMS_PAGE_ADDRESS(EMS_record->frame_address,
182                                             physical_page);
183
184   /* unmap old page */
185   if(old_hindex) {
186     void *ptr = EMS_PAGE_ADDRESS(EMS_record->handle[old_hindex].address,
187                                  old_logical_page);
188     memcpy(ptr, physical_address, EMS_PAGE_SIZE);
189   }
190
191   /* map new page */
192   if(new_hindex && new_logical_page != 0xffff) {
193     void *ptr = EMS_PAGE_ADDRESS(EMS_record->handle[new_hindex].address,
194                                  new_logical_page);
195     memcpy(physical_address, ptr, EMS_PAGE_SIZE);
196     EMS_record->mapping[physical_page].hindex = new_hindex;
197     EMS_record->mapping[physical_page].logical_page = new_logical_page;
198   } else {
199     EMS_record->mapping[physical_page].hindex = 0;
200     EMS_record->mapping[physical_page].logical_page = 0;
201   }
202
203   AH_reg(context) = 0; /* status: ok */
204 }
205
206 /**********************************************************************
207  *          EMS_free
208  *
209  * Free memory and release handle.
210  */
211 static void EMS_free( CONTEXT86 *context )
212 {
213   int hindex = DX_reg(context);
214   int i;
215
216   if(hindex < 0 || hindex >= EMS_MAX_HANDLES) {
217     AH_reg(context) = 0x83; /* status: invalid handle */
218     return;
219   }
220
221   if(!EMS_record->handle[hindex].address) {
222     AH_reg(context) = 0; /* status: ok */
223     return;
224   }
225
226   EMS_record->used_pages -= EMS_record->handle[hindex].pages;
227
228   /* unmap pages */
229   for(i=0; i<4; i++)
230     if(EMS_record->mapping[i].hindex == hindex)
231       EMS_record->mapping[i].hindex = 0;
232
233   /* free block */
234   HeapFree( GetProcessHeap(), 0, EMS_record->handle[hindex].address );
235   EMS_record->handle[hindex].address = 0;
236
237   AH_reg(context) = 0;    /* status: ok */
238 }
239
240 /**********************************************************************
241  *          DOSVM_Int67Handler
242  *
243  * Handler for interrupt 67h EMS routines.
244  */
245 void WINAPI DOSVM_Int67Handler( CONTEXT86 *context )
246 {
247   switch AH_reg(context) {
248
249   case 0x40: /* EMS - GET MANAGER STATUS */
250     AH_reg(context) = 0; /* status: ok */
251     break;
252
253   case 0x41: /* EMS - GET PAGE FRAME SEGMENT */
254     EMS_init();
255     BX_reg(context) = EMS_record->frame_selector; /* segment of page frame */
256     AH_reg(context) = 0;                          /* status: ok */
257     break;
258
259   case 0x42: /* EMS - GET NUMBER OF PAGES */
260     EMS_init();
261     /* unallocated 16k pages */
262     BX_reg(context) = EMS_MAX_PAGES - EMS_record->used_pages; 
263     /* total number of 16k pages */
264     DX_reg(context) = EMS_MAX_PAGES;
265     /* status: ok */
266     AH_reg(context) = 0;
267     break;
268
269   case 0x43: /* EMS - GET HANDLE AND ALLOCATE MEMORY */
270     EMS_init();
271     EMS_alloc(context);
272     break;
273
274   case 0x44: /* EMS - MAP MEMORY */
275     EMS_init();
276     EMS_map(context);
277     break;
278
279   case 0x45: /* EMS - RELEASE HANDLE AND MEMORY */
280     EMS_init();
281     EMS_free(context);
282     break;
283
284   case 0x46: /* EMS - GET EMM VERSION */
285     AL_reg(context) = 0x40; /* version 4.0 */
286     AH_reg(context) = 0;    /* status: ok */
287     break;
288
289   case 0x47: /* EMS - SAVE MAPPING CONTEXT */
290   case 0x48: /* EMS - RESTORE MAPPING CONTEXT */
291     INT_BARF(context,0x67);
292     break;
293
294   case 0x49: /* EMS - reserved - GET I/O PORT ADDRESSES */
295   case 0x4a: /* EMS - reserved - GET TRANSLATION ARRAY */
296     INT_BARF(context,0x67);
297     break;
298
299   case 0x4b: /* EMS - GET NUMBER OF EMM HANDLES */
300     BX_reg(context) = EMS_MAX_HANDLES; /* EMM handles */
301     AH_reg(context) = 0;               /* status: ok */
302     break;
303
304   case 0x4c: /* EMS - GET PAGES OWNED BY HANDLE */
305   case 0x4d: /* EMS - GET PAGES FOR ALL HANDLES */
306   case 0x4e: /* EMS - GET OR SET PAGE MAP */
307   case 0x4f: /* EMS 4.0 - GET/SET PARTIAL PAGE MAP */
308   case 0x50: /* EMS 4.0 - MAP/UNMAP MULTIPLE HANDLE PAGES */
309   case 0x51: /* EMS 4.0 - REALLOCATE PAGES */
310   case 0x52: /* EMS 4.0 - GET/SET HANDLE ATTRIBUTES */
311     INT_BARF(context,0x67);
312     break;
313
314   case 0x53: /* EMS 4.0 - GET/SET HANDLE NAME */
315     EMS_init();
316     EMS_access_name(context);
317     break;
318
319   case 0x54: /* EMS 4.0 - GET HANDLE DIRECTORY */
320   case 0x55: /* EMS 4.0 - ALTER PAGE MAP AND JUMP */
321   case 0x56: /* EMS 4.0 - ALTER PAGE MAP AND CALL */
322   case 0x57: /* EMS 4.0 - MOVE/EXCHANGE MEMORY REGION */
323   case 0x58: /* EMS 4.0 - GET MAPPABLE PHYSICAL ADDRESS ARRAY */
324   case 0x59: /* EMS 4.0 - GET EXPANDED MEMORY HARDWARE INFORMATION */
325   case 0x5a: /* EMS 4.0 - ALLOCATE STANDARD/RAW PAGES */
326   case 0x5b: /* EMS 4.0 - ALTERNATE MAP REGISTER SET */
327   case 0x5c: /* EMS 4.0 - PREPARE EXPANDED MEMORY HARDWARE FOR WARM BOOT */
328   case 0x5d: /* EMS 4.0 - ENABLE/DISABLE OS FUNCTION SET FUNCTIONS */
329   default:
330     INT_BARF(context,0x67);
331   }
332 }
333
334
335 /**********************************************************************
336  *          EMS_Ioctl_Handler
337  *
338  * Handler for interrupt 21h IOCTL routine for device "EMMXXXX0".
339  */
340 void WINAPI EMS_Ioctl_Handler( CONTEXT86 *context )
341 {
342   assert(AH_reg(context) == 0x44);
343
344   switch AL_reg(context) {
345   case 0x00: /* IOCTL - GET DEVICE INFORMATION */
346       RESET_CFLAG(context); /* operation was successful */
347       DX_reg(context) = 0x4080; /* bit 14 (support ioctl read) and
348                                  * bit 7 (is_device) */
349       break;
350
351   case 0x02: /* EMS - GET MEMORY MANAGER INFORMATION */
352       /*
353        * This is what is called "Windows Global EMM Import Specification".
354        * Undocumented of course! Supports three requests:
355        * GET API ENTRY POINT
356        * GET EMM IMPORT STRUCTURE ADDRESS
357        * GET MEMORY MANAGER VERSION
358        */
359       INT_BARF(context,0x21);
360       break;
361
362   case 0x07: /* IOCTL - GET OUTPUT STATUS */
363       RESET_CFLAG(context); /* operation was successful */
364       AL_reg(context) = 0xff; /* device is ready */
365       break;
366
367   default:
368       INT_BARF(context,0x21);
369       break;
370   }
371 }