kernel32: Add parentheses to clarify the precedence between '&' and '||'.
[wine/multimedia.git] / dlls / krnl386.exe16 / int67.c
blob3137bc929e394418a68727f14cf165ed58fea375
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <assert.h>
22 #include "wine/winbase16.h"
23 #include "dosexe.h"
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(int);
29 * EMS page size == 16 kilobytes.
31 #define EMS_PAGE_SIZE (16*1024)
34 * Linear address of EMS page.
36 #define EMS_PAGE_ADDRESS(base,page) (((char*)base) + EMS_PAGE_SIZE * page)
39 * Maximum number of pages that can be allocated using EMS.
41 #define EMS_MAX_PAGES 1024
44 * Maximum number of EMS handles (allocated blocks).
46 #define EMS_MAX_HANDLES 256
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.
54 * FIXME: Missing lots of fields, packing is not correct.
57 static 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];
66 /* Wine specific fields... */
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 */
72 struct {
73 UCHAR hindex; /* handle number */
74 WORD logical_page; /* logical page */
75 } mapping[4];
77 struct {
78 UCHAR hindex; /* handle number */
79 WORD logical_page; /* logical page */
80 } mapping_save_area[EMS_MAX_HANDLES][4];
82 } *EMS_record;
84 /**********************************************************************
85 * EMS_init
87 * Allocates and initialized page frame and EMS global import record.
89 static void EMS_init(void)
92 * Start of 64k EMS frame.
94 ULONG base = 0xc0000;
96 if(EMS_record)
97 return;
99 EMS_record = HeapAlloc(GetProcessHeap(),
100 HEAP_ZERO_MEMORY,
101 sizeof(*EMS_record));
103 EMS_record->frame_address = (void *)base;
104 EMS_record->frame_selector = base >> 4;
107 /**********************************************************************
108 * EMS_alloc
110 * Get handle and allocate memory.
112 static void EMS_alloc( CONTEXT *context )
114 int hindex = 1; /* handle zero is reserved for system */
116 while(hindex < EMS_MAX_HANDLES && EMS_record->handle[hindex].address)
117 hindex++;
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 );
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;
132 SET_DX( context, hindex ); /* handle to allocated memory*/
133 SET_AH( context, 0 ); /* status: ok */
138 /**********************************************************************
139 * EMS_access_name
141 * Get/set handle name.
143 static void EMS_access_name( CONTEXT *context )
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;
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;
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;
165 default:
166 INT_BARF(context,0x67);
167 break;
171 /**********************************************************************
172 * EMS_map
174 * Map logical page into physical page.
176 static BYTE EMS_map( WORD physical_page, WORD new_hindex, WORD new_logical_page )
178 int old_hindex;
179 int old_logical_page;
180 void *physical_address;
182 if(physical_page > 3)
183 return 0x8b; /* status: invalid physical page */
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);
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);
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);
201 if(new_hindex >= EMS_MAX_HANDLES || !EMS_record->handle[new_hindex].address)
202 return 0x83; /* status: invalid handle */
204 if(new_logical_page >= EMS_record->handle[new_hindex].pages)
205 return 0x8a; /* status: invalid logical page */
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;
215 return 0; /* status: ok */
218 /**********************************************************************
219 * EMS_map_multiple
221 * Map multiple logical pages into physical pages.
223 static void EMS_map_multiple( CONTEXT *context )
225 WORD *ptr = PTR_REAL_TO_LIN(context->SegDs, SI_reg(context));
226 BYTE status = 0;
227 int i;
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 */
243 SET_AH( context, status );
246 /**********************************************************************
247 * EMS_free
249 * Free memory and release handle.
251 static void EMS_free( CONTEXT *context )
253 int hindex = DX_reg(context);
254 int i;
256 if(hindex < 0 || hindex >= EMS_MAX_HANDLES) {
257 SET_AH( context, 0x83 ); /* status: invalid handle */
258 return;
261 if(!EMS_record->handle[hindex].address) {
262 SET_AH( context, 0 ); /* status: ok */
263 return;
266 EMS_record->used_pages -= EMS_record->handle[hindex].pages;
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;
273 /* free block */
274 HeapFree( GetProcessHeap(), 0, EMS_record->handle[hindex].address );
275 EMS_record->handle[hindex].address = 0;
277 SET_AH( context, 0 ); /* status: ok */
280 /**********************************************************************
281 * EMS_save_context
283 * Save physical page mappings into handle specific save area.
285 static void EMS_save_context( CONTEXT *context )
287 WORD h = DX_reg(context);
288 int i;
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;
295 SET_AX( context, 0 ); /* status: ok */
299 /**********************************************************************
300 * EMS_restore_context
302 * Restore physical page mappings from handle specific save area.
304 static void EMS_restore_context( CONTEXT *context )
306 WORD handle = DX_reg(context);
307 int i;
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;
313 if(EMS_map( i, hindex, logical_page )) {
314 SET_AX( context, 0x8e ); /* status: restore of mapping context failed */
315 return;
319 SET_AX( context, 0 ); /* status: ok */
322 /**********************************************************************
323 * DOSVM_Int67Handler
325 * Handler for interrupt 67h EMS routines.
327 void WINAPI DOSVM_Int67Handler( CONTEXT *context )
329 switch AH_reg(context) {
331 case 0x40: /* EMS - GET MANAGER STATUS */
332 SET_AH( context, 0 ); /* status: ok */
333 break;
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;
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;
351 case 0x43: /* EMS - GET HANDLE AND ALLOCATE MEMORY */
352 EMS_init();
353 EMS_alloc(context);
354 break;
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;
361 case 0x45: /* EMS - RELEASE HANDLE AND MEMORY */
362 EMS_init();
363 EMS_free(context);
364 break;
366 case 0x46: /* EMS - GET EMM VERSION */
367 SET_AL( context, 0x40 ); /* version 4.0 */
368 SET_AH( context, 0 ); /* status: ok */
369 break;
371 case 0x47: /* EMS - SAVE MAPPING CONTEXT */
372 EMS_init();
373 EMS_save_context(context);
374 break;
376 case 0x48: /* EMS - RESTORE MAPPING CONTEXT */
377 EMS_init();
378 EMS_restore_context(context);
379 break;
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;
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;
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;
398 case 0x50: /* EMS 4.0 - MAP/UNMAP MULTIPLE HANDLE PAGES */
399 EMS_init();
400 EMS_map_multiple(context);
401 break;
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;
408 case 0x53: /* EMS 4.0 - GET/SET HANDLE NAME */
409 EMS_init();
410 EMS_access_name(context);
411 break;
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;
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;
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;
441 case 0xde: /* Virtual Control Program Interface (VCPI) */
442 if(AL_reg(context) == 0x00) {
444 * VCPI INSTALLATION CHECK
445 * (AH_reg() != 0) means VCPI is not present
447 TRACE("- VCPI installation check\n");
448 return;
449 } else
450 INT_BARF(context,0x67);
451 break;
453 default:
454 INT_BARF(context,0x67);
459 /**********************************************************************
460 * EMS_Ioctl_Handler
462 * Handler for interrupt 21h IOCTL routine for device "EMMXXXX0".
464 void EMS_Ioctl_Handler( CONTEXT *context )
466 assert(AH_reg(context) == 0x44);
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;
475 case 0x02: /* EMS - GET MEMORY MANAGER INFORMATION */
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
483 INT_BARF(context,0x21);
484 break;
486 case 0x07: /* IOCTL - GET OUTPUT STATUS */
487 RESET_CFLAG(context); /* operation was successful */
488 SET_AL( context, 0xff ); /* device is ready */
489 break;
491 default:
492 INT_BARF(context,0x21);
493 break;