Don't leak cache imagelists on exit.
[wine/hacks.git] / dlls / winedos / int67.c
blob6112ff41d3549cd0ef7838ebc7da28e376b9d258
1 /*
2 * Int67 (EMS) emulation
4 * Copyright 2002 Jukka Heinonen
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.
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.
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
21 #include <assert.h>
22 #include "wine/winbase16.h"
23 #include "dosexe.h"
24 #include "miscemu.h"
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(int);
30 * EMS page size == 16 kilobytes.
32 #define EMS_PAGE_SIZE (16*1024)
35 * Linear address of EMS page.
37 #define EMS_PAGE_ADDRESS(base,page) (((char*)base) + EMS_PAGE_SIZE * page)
40 * Maximum number of pages that can be allocated using EMS.
42 #define EMS_MAX_PAGES 1024
45 * Maximum number of EMS handles (allocated blocks).
47 #define EMS_MAX_HANDLES 256
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.
55 * FIXME: Missing lots of fields, packing is not correct.
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];
67 /* Wine specific fields... */
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 */
73 struct {
74 UCHAR hindex; /* handle number */
75 WORD logical_page; /* logical page */
76 } mapping[4];
78 struct {
79 UCHAR hindex; /* handle number */
80 WORD logical_page; /* logical page */
81 } mapping_save_area[EMS_MAX_HANDLES][4];
83 } *EMS_record = 0;
85 /**********************************************************************
86 * EMS_init
88 * Allocates and initialized page frame and EMS global import record.
90 static void EMS_init(void)
93 * Start of 64k EMS frame.
95 ULONG base = 0xc0000;
97 if(EMS_record)
98 return;
100 EMS_record = HeapAlloc(GetProcessHeap(),
101 HEAP_ZERO_MEMORY,
102 sizeof(*EMS_record));
104 EMS_record->frame_address = (void *)base;
105 EMS_record->frame_selector = base >> 4;
108 /**********************************************************************
109 * EMS_alloc
111 * Get handle and allocate memory.
113 static void EMS_alloc( CONTEXT86 *context )
115 int hindex = 1; /* handle zero is reserved for system */
117 while(hindex < EMS_MAX_HANDLES && EMS_record->handle[hindex].address)
118 hindex++;
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 );
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;
133 SET_DX( context, hindex ); /* handle to allocated memory*/
134 SET_AH( context, 0 ); /* status: ok */
139 /**********************************************************************
140 * EMS_access_name
142 * Get/set handle name.
144 static void EMS_access_name( CONTEXT86 *context )
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;
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;
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;
166 default:
167 INT_BARF(context,0x67);
168 break;
172 /**********************************************************************
173 * EMS_map
175 * Map logical page into physical page.
177 static BYTE EMS_map( WORD physical_page, WORD new_hindex, WORD new_logical_page )
179 int old_hindex;
180 int old_logical_page;
181 void *physical_address;
183 if(physical_page > 3)
184 return 0x8b; /* status: invalid physical page */
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);
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);
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);
202 if(new_hindex >= EMS_MAX_HANDLES || !EMS_record->handle[new_hindex].address)
203 return 0x83; /* status: invalid handle */
205 if(new_logical_page >= EMS_record->handle[new_hindex].pages)
206 return 0x8a; /* status: invalid logical page */
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;
216 return 0; /* status: ok */
219 /**********************************************************************
220 * EMS_map_multiple
222 * Map multiple logical pages into physical pages.
224 static void EMS_map_multiple( CONTEXT86 *context )
226 WORD *ptr = PTR_REAL_TO_LIN(context->SegDs, SI_reg(context));
227 BYTE status = 0;
228 int i;
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 */
244 SET_AH( context, status );
247 /**********************************************************************
248 * EMS_free
250 * Free memory and release handle.
252 static void EMS_free( CONTEXT86 *context )
254 int hindex = DX_reg(context);
255 int i;
257 if(hindex < 0 || hindex >= EMS_MAX_HANDLES) {
258 SET_AH( context, 0x83 ); /* status: invalid handle */
259 return;
262 if(!EMS_record->handle[hindex].address) {
263 SET_AH( context, 0 ); /* status: ok */
264 return;
267 EMS_record->used_pages -= EMS_record->handle[hindex].pages;
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;
274 /* free block */
275 HeapFree( GetProcessHeap(), 0, EMS_record->handle[hindex].address );
276 EMS_record->handle[hindex].address = 0;
278 SET_AH( context, 0 ); /* status: ok */
281 /**********************************************************************
282 * EMS_save_context
284 * Save physical page mappings into handle specific save area.
286 static void EMS_save_context( CONTEXT86 *context )
288 WORD h = DX_reg(context);
289 int i;
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;
296 SET_AX( context, 0 ); /* status: ok */
300 /**********************************************************************
301 * EMS_restore_context
303 * Restore physical page mappings from handle specific save area.
305 static void EMS_restore_context( CONTEXT86 *context )
307 WORD handle = DX_reg(context);
308 int i;
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;
314 if(EMS_map( i, hindex, logical_page )) {
315 SET_AX( context, 0x8e ); /* status: restore of mapping context failed */
316 return;
320 SET_AX( context, 0 ); /* status: ok */
323 /**********************************************************************
324 * DOSVM_Int67Handler (WINEDOS16.203)
326 * Handler for interrupt 67h EMS routines.
328 void WINAPI DOSVM_Int67Handler( CONTEXT86 *context )
330 switch AH_reg(context) {
332 case 0x40: /* EMS - GET MANAGER STATUS */
333 SET_AH( context, 0 ); /* status: ok */
334 break;
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;
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;
352 case 0x43: /* EMS - GET HANDLE AND ALLOCATE MEMORY */
353 EMS_init();
354 EMS_alloc(context);
355 break;
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;
362 case 0x45: /* EMS - RELEASE HANDLE AND MEMORY */
363 EMS_init();
364 EMS_free(context);
365 break;
367 case 0x46: /* EMS - GET EMM VERSION */
368 SET_AL( context, 0x40 ); /* version 4.0 */
369 SET_AH( context, 0 ); /* status: ok */
370 break;
372 case 0x47: /* EMS - SAVE MAPPING CONTEXT */
373 EMS_init();
374 EMS_save_context(context);
375 break;
377 case 0x48: /* EMS - RESTORE MAPPING CONTEXT */
378 EMS_init();
379 EMS_restore_context(context);
380 break;
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;
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;
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;
399 case 0x50: /* EMS 4.0 - MAP/UNMAP MULTIPLE HANDLE PAGES */
400 EMS_init();
401 EMS_map_multiple(context);
402 break;
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;
409 case 0x53: /* EMS 4.0 - GET/SET HANDLE NAME */
410 EMS_init();
411 EMS_access_name(context);
412 break;
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;
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;
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;
442 case 0xde: /* Virtual Control Program Interface (VCPI) */
443 if(AL_reg(context) == 0x00) {
445 * VCPI INSTALLATION CHECK
446 * (AH_reg() != 0) means VCPI is not present
448 TRACE("- VCPI installation check\n");
449 return;
450 } else
451 INT_BARF(context,0x67);
452 break;
454 default:
455 INT_BARF(context,0x67);
460 /**********************************************************************
461 * EMS_Ioctl_Handler
463 * Handler for interrupt 21h IOCTL routine for device "EMMXXXX0".
465 void WINAPI EMS_Ioctl_Handler( CONTEXT86 *context )
467 assert(AH_reg(context) == 0x44);
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;
476 case 0x02: /* EMS - GET MEMORY MANAGER INFORMATION */
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
484 INT_BARF(context,0x21);
485 break;
487 case 0x07: /* IOCTL - GET OUTPUT STATUS */
488 RESET_CFLAG(context); /* operation was successful */
489 SET_AL( context, 0xff ); /* device is ready */
490 break;
492 default:
493 INT_BARF(context,0x21);
494 break;