Storing an IP address in a signed int results in bugs if it starts
[wine] / dlls / winedos / int67.c
1 /*
2  * Int67 (EMS) emulation
3  */
4
5 #include <assert.h>
6 #include "wine/winbase16.h"
7 #include "dosexe.h"
8 #include "miscemu.h"
9 #include "debugtools.h"
10
11 DEFAULT_DEBUG_CHANNEL(int);
12
13 /*
14  * EMS page size == 16 kilobytes.
15  */
16 #define EMS_PAGE_SIZE (16*1024)
17
18 /*
19  * Linear address of EMS page.
20  */
21 #define EMS_PAGE_ADDRESS(base,page) (((char*)base) + EMS_PAGE_SIZE * page)
22
23 /* 
24  * Maximum number of pages that can be allocated using EMS.
25  */
26 #define EMS_MAX_PAGES 1024
27
28 /* 
29  * Maximum number of EMS handles (allocated blocks).
30  */
31 #define EMS_MAX_HANDLES 256
32
33 /*
34  * Global EMM Import Record.
35  * Applications can get address of this record
36  * and directly access allocated memory if they use
37  * IOCTL interface.
38  *
39  * FIXME: Missing lots of fields, packing is not correct.
40  */
41
42 struct {
43   struct {
44     UCHAR hindex;  /* handle number */
45     BYTE  flags;   /* bit 0: normal handle rather than system handle */
46     char  name[8]; /* handle name */
47     WORD  pages;   /* allocated pages */
48     void *address; /* physical address*/
49   } handle[EMS_MAX_HANDLES];
50
51   /* Wine specific fields... */
52
53   int   used_pages;     /* Number of allocated pages. */
54   void *frame_address;  /* Address of 64k EMS page frame */
55   WORD  frame_selector; /* Segment of 64k EMS page frame */
56
57   struct {
58     UCHAR hindex;       /* handle number */
59     WORD  logical_page; /* logical page */
60   } mapping[4];
61
62 } *EMS_record = 0;
63
64 /**********************************************************************
65  *          EMS_init 
66  *
67  * Allocates and initialized page frame and EMS global import record.
68  */
69 static void EMS_init(void)
70 {
71   /*
72    * FIXME: Should dynamically allocate upper memory block for EMS frame. 
73    */
74   ULONG base = 0xd0000;
75
76   if(EMS_record)
77     return;
78
79   EMS_record = HeapAlloc(GetProcessHeap(), 
80                          HEAP_ZERO_MEMORY, 
81                          sizeof(*EMS_record));
82   
83   EMS_record->frame_address = DOSMEM_MapDosToLinear(base);
84   EMS_record->frame_selector = base >> 4;
85 }
86
87 /**********************************************************************
88  *          EMS_alloc
89  *
90  * Get handle and allocate memory.
91  */
92 static void EMS_alloc( CONTEXT86 *context )
93 {
94   int hindex = 1; /* handle zero is reserved for system */
95
96   while(hindex < EMS_MAX_HANDLES && EMS_record->handle[hindex].address)
97     hindex++;
98
99   if(hindex == EMS_MAX_HANDLES) {
100     AH_reg(context) = 0x85; /* status: no more handles available */
101   } else {
102     int   pages = BX_reg(context);
103     void *buffer = HeapAlloc( GetProcessHeap(), 0, pages * EMS_PAGE_SIZE );
104
105     if(!buffer) {
106       AH_reg(context) = 0x88; /* status: insufficient pages available */
107     } else {
108       EMS_record->handle[hindex].address = buffer;
109       EMS_record->handle[hindex].pages = pages;
110       EMS_record->used_pages += pages;
111       
112       DX_reg(context) = hindex; /* handle to allocated memory*/  
113       AH_reg(context) = 0;      /* status: ok */
114     }
115   }
116 }
117
118 /**********************************************************************
119  *          EMS_access_name
120  *
121  * Get/set handle name.
122  */
123 static void EMS_access_name( CONTEXT86 *context )
124 {
125   char *ptr;
126   int hindex = DX_reg(context);
127   if(hindex < 0 || hindex >= EMS_MAX_HANDLES) {
128     AH_reg(context) = 0x83; /* invalid handle */
129     return;
130   }
131
132   switch AL_reg(context) {
133   case 0x00: /* get name */
134     ptr = MapSL(MAKESEGPTR(context->SegEs, DI_reg(context)));
135     memcpy(ptr, EMS_record->handle[hindex].name, 8);
136     AH_reg(context) = 0;
137     break;
138
139   case 0x01: /* set name */
140     ptr = MapSL(MAKESEGPTR(context->SegDs, SI_reg(context)));
141     memcpy(EMS_record->handle[hindex].name, ptr, 8);
142     AH_reg(context) = 0;
143     break;
144     
145   default:
146     INT_BARF(context,0x67);
147     break;
148   }
149 }
150
151 /**********************************************************************
152  *          EMS_map
153  *
154  * Map logical page into physical page.
155  */
156 static void EMS_map( CONTEXT86 *context )
157 {
158   int   physical_page = AL_reg(context);
159   int   new_hindex = DX_reg(context);
160   int   new_logical_page = BX_reg(context);
161
162   int   old_hindex = EMS_record->mapping[physical_page].hindex;
163   int   old_logical_page = EMS_record->mapping[physical_page].logical_page;
164
165   void *physical_address = EMS_PAGE_ADDRESS(EMS_record->frame_address,
166                                             physical_page);
167
168   /* unmap old page */
169   if(old_hindex) {
170     void *ptr = EMS_PAGE_ADDRESS(EMS_record->handle[old_hindex].address,
171                                  old_logical_page);
172     memcpy(ptr, physical_address, EMS_PAGE_SIZE);
173   }
174
175   /* map new page */
176   if(new_hindex && new_logical_page != 0xffff) {
177     void *ptr = EMS_PAGE_ADDRESS(EMS_record->handle[new_hindex].address,
178                                  new_logical_page);
179     memcpy(physical_address, ptr, EMS_PAGE_SIZE);
180     EMS_record->mapping[physical_page].hindex = new_hindex;
181     EMS_record->mapping[physical_page].logical_page = new_logical_page;
182   } else {
183     EMS_record->mapping[physical_page].hindex = 0;
184     EMS_record->mapping[physical_page].logical_page = 0;
185   }
186
187   AH_reg(context) = 0; /* status: ok */
188 }
189
190 /**********************************************************************
191  *          EMS_free
192  *
193  * Free memory and release handle.
194  */
195 static void EMS_free( CONTEXT86 *context )
196 {
197   int hindex = DX_reg(context);
198   int i;
199
200   if(hindex < 0 || hindex >= EMS_MAX_HANDLES) {
201     AH_reg(context) = 0x83; /* status: invalid handle */
202     return;
203   }
204
205   if(!EMS_record->handle[hindex].address) {
206     AH_reg(context) = 0; /* status: ok */
207     return;
208   }
209
210   EMS_record->used_pages -= EMS_record->handle[hindex].pages;
211
212   /* unmap pages */
213   for(i=0; i<4; i++)
214     if(EMS_record->mapping[i].hindex == hindex)
215       EMS_record->mapping[i].hindex = 0;
216
217   /* free block */
218   HeapFree( GetProcessHeap(), 0, EMS_record->handle[hindex].address );
219   EMS_record->handle[hindex].address = 0;
220
221   AH_reg(context) = 0;    /* status: ok */
222 }
223
224 /**********************************************************************
225  *          DOSVM_Int67Handler
226  *
227  * Handler for interrupt 67h EMS routines.
228  */
229 void WINAPI DOSVM_Int67Handler( CONTEXT86 *context )
230 {
231   switch AH_reg(context) {
232
233   case 0x40: /* EMS - GET MANAGER STATUS */
234     AH_reg(context) = 0; /* status: ok */
235     break;
236
237   case 0x41: /* EMS - GET PAGE FRAME SEGMENT */
238     EMS_init();
239     BX_reg(context) = EMS_record->frame_selector; /* segment of page frame */
240     AH_reg(context) = 0;                          /* status: ok */
241     break;
242
243   case 0x42: /* EMS - GET NUMBER OF PAGES */
244     EMS_init();
245     /* unallocated 16k pages */
246     BX_reg(context) = EMS_MAX_PAGES - EMS_record->used_pages; 
247     /* total number of 16k pages */
248     DX_reg(context) = EMS_MAX_PAGES;
249     /* status: ok */
250     AH_reg(context) = 0;
251     break;
252
253   case 0x43: /* EMS - GET HANDLE AND ALLOCATE MEMORY */
254     EMS_init();
255     EMS_alloc(context);
256     break;
257
258   case 0x44: /* EMS - MAP MEMORY */
259     EMS_init();
260     EMS_map(context);
261     break;
262
263   case 0x45: /* EMS - RELEASE HANDLE AND MEMORY */
264     EMS_init();
265     EMS_free(context);
266     break;
267
268   case 0x46: /* EMS - GET EMM VERSION */
269     AL_reg(context) = 0x40; /* version 4.0 */
270     AH_reg(context) = 0;    /* status: ok */
271     break;
272
273   case 0x47: /* EMS - SAVE MAPPING CONTEXT */
274   case 0x48: /* EMS - RESTORE MAPPING CONTEXT */
275     INT_BARF(context,0x67);
276     break;
277
278   case 0x49: /* EMS - reserved - GET I/O PORT ADDRESSES */
279   case 0x4a: /* EMS - reserved - GET TRANSLATION ARRAY */
280     INT_BARF(context,0x67);
281     break;
282
283   case 0x4b: /* EMS - GET NUMBER OF EMM HANDLES */
284     BX_reg(context) = EMS_MAX_HANDLES; /* EMM handles */
285     AH_reg(context) = 0;               /* status: ok */
286     break;
287
288   case 0x4c: /* EMS - GET PAGES OWNED BY HANDLE */
289   case 0x4d: /* EMS - GET PAGES FOR ALL HANDLES */
290   case 0x4e: /* EMS - GET OR SET PAGE MAP */
291   case 0x4f: /* EMS 4.0 - GET/SET PARTIAL PAGE MAP */
292   case 0x50: /* EMS 4.0 - MAP/UNMAP MULTIPLE HANDLE PAGES */
293   case 0x51: /* EMS 4.0 - REALLOCATE PAGES */
294   case 0x52: /* EMS 4.0 - GET/SET HANDLE ATTRIBUTES */
295     INT_BARF(context,0x67);
296     break;
297
298   case 0x53: /* EMS 4.0 - GET/SET HANDLE NAME */
299     EMS_init();
300     EMS_access_name(context);
301     break;
302
303   case 0x54: /* EMS 4.0 - GET HANDLE DIRECTORY */
304   case 0x55: /* EMS 4.0 - ALTER PAGE MAP AND JUMP */
305   case 0x56: /* EMS 4.0 - ALTER PAGE MAP AND CALL */
306   case 0x57: /* EMS 4.0 - MOVE/EXCHANGE MEMORY REGION */
307   case 0x58: /* EMS 4.0 - GET MAPPABLE PHYSICAL ADDRESS ARRAY */
308   case 0x59: /* EMS 4.0 - GET EXPANDED MEMORY HARDWARE INFORMATION */
309   case 0x5a: /* EMS 4.0 - ALLOCATE STANDARD/RAW PAGES */
310   case 0x5b: /* EMS 4.0 - ALTERNATE MAP REGISTER SET */
311   case 0x5c: /* EMS 4.0 - PREPARE EXPANDED MEMORY HARDWARE FOR WARM BOOT */
312   case 0x5d: /* EMS 4.0 - ENABLE/DISABLE OS FUNCTION SET FUNCTIONS */
313   default:
314     INT_BARF(context,0x67);
315   }
316 }
317
318
319 /**********************************************************************
320  *          EMS_Ioctl_Handler
321  *
322  * Handler for interrupt 21h IOCTL routine for device "EMMXXXX0".
323  */
324 void WINAPI EMS_Ioctl_Handler( CONTEXT86 *context )
325 {
326   assert(AH_reg(context) == 0x44);
327
328   switch AL_reg(context) {
329   case 0x00: /* IOCTL - GET DEVICE INFORMATION */
330       RESET_CFLAG(context); /* operation was successful */
331       DX_reg(context) = 0x4080; /* bit 14 (support ioctl read) and
332                                  * bit 7 (is_device) */
333       break;
334
335   case 0x02: /* EMS - GET MEMORY MANAGER INFORMATION */
336       /*
337        * This is what is called "Windows Global EMM Import Specification".
338        * Undocumented of course! Supports three requests:
339        * GET API ENTRY POINT
340        * GET EMM IMPORT STRUCTURE ADDRESS
341        * GET MEMORY MANAGER VERSION
342        */
343       INT_BARF(context,0x21);
344       break;
345
346   case 0x07: /* IOCTL - GET OUTPUT STATUS */
347       RESET_CFLAG(context); /* operation was successful */
348       AL_reg(context) = 0xff; /* device is ready */
349       break;
350
351   default:
352       INT_BARF(context,0x21);
353       break;
354   }
355 }