int67.c 13.5 KB
Newer Older
1 2
/*
 * Int67 (EMS) emulation
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * Copyright 2002 Jukka Heinonen
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22 23
 */

#include <assert.h>
#include "wine/winbase16.h"
#include "dosexe.h"
24
#include "wine/debug.h"
25

26
WINE_DEFAULT_DEBUG_CHANNEL(int);
27 28 29 30 31 32 33 34 35 36 37

/*
 * EMS page size == 16 kilobytes.
 */
#define EMS_PAGE_SIZE (16*1024)

/*
 * Linear address of EMS page.
 */
#define EMS_PAGE_ADDRESS(base,page) (((char*)base) + EMS_PAGE_SIZE * page)

38
/*
39 40 41 42
 * Maximum number of pages that can be allocated using EMS.
 */
#define EMS_MAX_PAGES 1024

43
/*
44 45 46 47 48 49 50 51 52 53 54 55 56
 * Maximum number of EMS handles (allocated blocks).
 */
#define EMS_MAX_HANDLES 256

/*
 * Global EMM Import Record.
 * Applications can get address of this record
 * and directly access allocated memory if they use
 * IOCTL interface.
 *
 * FIXME: Missing lots of fields, packing is not correct.
 */

57
static struct {
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
  struct {
    UCHAR hindex;  /* handle number */
    BYTE  flags;   /* bit 0: normal handle rather than system handle */
    char  name[8]; /* handle name */
    WORD  pages;   /* allocated pages */
    void *address; /* physical address*/
  } handle[EMS_MAX_HANDLES];

  /* Wine specific fields... */

  int   used_pages;     /* Number of allocated pages. */
  void *frame_address;  /* Address of 64k EMS page frame */
  WORD  frame_selector; /* Segment of 64k EMS page frame */

  struct {
    UCHAR hindex;       /* handle number */
    WORD  logical_page; /* logical page */
  } mapping[4];

77 78 79 80 81
  struct {
    UCHAR hindex;       /* handle number */
    WORD  logical_page; /* logical page */
  } mapping_save_area[EMS_MAX_HANDLES][4];

82
} *EMS_record;
83 84

/**********************************************************************
85
 *          EMS_init
86 87 88 89 90 91
 *
 * Allocates and initialized page frame and EMS global import record.
 */
static void EMS_init(void)
{
  /*
92
   * Start of 64k EMS frame.
93
   */
94
  ULONG base = 0xc0000;
95 96 97 98

  if(EMS_record)
    return;

99 100
  EMS_record = HeapAlloc(GetProcessHeap(),
                         HEAP_ZERO_MEMORY,
101
                         sizeof(*EMS_record));
102

103
  EMS_record->frame_address = (void *)base;
104 105 106 107 108 109 110 111
  EMS_record->frame_selector = base >> 4;
}

/**********************************************************************
 *          EMS_alloc
 *
 * Get handle and allocate memory.
 */
112
static void EMS_alloc( CONTEXT *context )
113 114 115 116 117 118 119
{
  int hindex = 1; /* handle zero is reserved for system */

  while(hindex < EMS_MAX_HANDLES && EMS_record->handle[hindex].address)
    hindex++;

  if(hindex == EMS_MAX_HANDLES) {
120
    SET_AH( context, 0x85 ); /* status: no more handles available */
121 122 123 124 125
  } else {
    int   pages = BX_reg(context);
    void *buffer = HeapAlloc( GetProcessHeap(), 0, pages * EMS_PAGE_SIZE );

    if(!buffer) {
126
      SET_AH( context, 0x88 ); /* status: insufficient pages available */
127 128 129 130
    } else {
      EMS_record->handle[hindex].address = buffer;
      EMS_record->handle[hindex].pages = pages;
      EMS_record->used_pages += pages;
131

132 133
      SET_DX( context, hindex ); /* handle to allocated memory*/
      SET_AH( context, 0 );      /* status: ok */
134 135 136 137 138 139 140 141 142
    }
  }
}

/**********************************************************************
 *          EMS_access_name
 *
 * Get/set handle name.
 */
143
static void EMS_access_name( CONTEXT *context )
144 145 146 147
{
  char *ptr;
  int hindex = DX_reg(context);
  if(hindex < 0 || hindex >= EMS_MAX_HANDLES) {
148
    SET_AH( context, 0x83 ); /* invalid handle */
149 150 151 152 153
    return;
  }

  switch AL_reg(context) {
  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 158 159
    break;

  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 166 167 168 169 170 171 172 173 174 175
  default:
    INT_BARF(context,0x67);
    break;
  }
}

/**********************************************************************
 *          EMS_map
 *
 * Map logical page into physical page.
 */
176
static BYTE EMS_map( WORD physical_page, WORD new_hindex, WORD new_logical_page )
177
{
178 179 180
  int   old_hindex;
  int   old_logical_page;
  void *physical_address;
181

182 183
  if(physical_page > 3)
    return 0x8b; /* status: invalid physical page */
184

185 186 187
  old_hindex = EMS_record->mapping[physical_page].hindex;
  old_logical_page = EMS_record->mapping[physical_page].logical_page;
  physical_address = EMS_PAGE_ADDRESS(EMS_record->frame_address, physical_page);
188 189 190 191 192 193 194 195 196 197 198 199

  /* unmap old page */
  if(old_hindex) {
    void *ptr = EMS_PAGE_ADDRESS(EMS_record->handle[old_hindex].address,
                                 old_logical_page);
    memcpy(ptr, physical_address, EMS_PAGE_SIZE);
  }

  /* map new page */
  if(new_hindex && new_logical_page != 0xffff) {
    void *ptr = EMS_PAGE_ADDRESS(EMS_record->handle[new_hindex].address,
                                 new_logical_page);
200 201 202 203 204 205 206

    if(new_hindex >= EMS_MAX_HANDLES || !EMS_record->handle[new_hindex].address)
      return 0x83; /* status: invalid handle */

    if(new_logical_page >= EMS_record->handle[new_hindex].pages)
      return 0x8a; /* status: invalid logical page */

207 208 209 210 211 212 213 214
    memcpy(physical_address, ptr, EMS_PAGE_SIZE);
    EMS_record->mapping[physical_page].hindex = new_hindex;
    EMS_record->mapping[physical_page].logical_page = new_logical_page;
  } else {
    EMS_record->mapping[physical_page].hindex = 0;
    EMS_record->mapping[physical_page].logical_page = 0;
  }

215 216 217 218 219 220 221 222
  return 0; /* status: ok */
}

/**********************************************************************
 *          EMS_map_multiple
 *
 * Map multiple logical pages into physical pages.
 */
223
static void EMS_map_multiple( CONTEXT *context )
224 225 226 227 228 229 230 231
{
  WORD *ptr = PTR_REAL_TO_LIN(context->SegDs, SI_reg(context));
  BYTE  status = 0;
  int   i;

  for(i=0; i<CX_reg(context) && !status; i++, ptr += 2)
    switch(AL_reg(context)) {
    case 0x00:
232
      status = EMS_map( ptr[1],
233 234 235 236 237 238 239
                       DX_reg(context), ptr[0] );
      break;
    case 0x01:
      status = EMS_map( (ptr[1] - EMS_record->frame_selector) >> 10,
                       DX_reg(context), ptr[0] );
      break;
    default:
240
      status = 0x8f; /* status: undefined subfunction */
241 242
    }

243
  SET_AH( context, status );
244 245 246 247 248 249 250
}

/**********************************************************************
 *          EMS_free
 *
 * Free memory and release handle.
 */
251
static void EMS_free( CONTEXT *context )
252 253 254 255 256
{
  int hindex = DX_reg(context);
  int i;

  if(hindex < 0 || hindex >= EMS_MAX_HANDLES) {
257
    SET_AH( context, 0x83 ); /* status: invalid handle */
258 259 260 261
    return;
  }

  if(!EMS_record->handle[hindex].address) {
262
    SET_AH( context, 0 ); /* status: ok */
263 264 265 266 267 268 269 270 271 272 273 274 275 276
    return;
  }

  EMS_record->used_pages -= EMS_record->handle[hindex].pages;

  /* unmap pages */
  for(i=0; i<4; i++)
    if(EMS_record->mapping[i].hindex == hindex)
      EMS_record->mapping[i].hindex = 0;

  /* free block */
  HeapFree( GetProcessHeap(), 0, EMS_record->handle[hindex].address );
  EMS_record->handle[hindex].address = 0;

277
  SET_AH( context, 0 );    /* status: ok */
278 279
}

280 281 282 283 284
/**********************************************************************
 *          EMS_save_context
 *
 * Save physical page mappings into handle specific save area.
 */
285
static void EMS_save_context( CONTEXT *context )
286 287 288 289 290 291 292 293
{
  WORD h = DX_reg(context);
  int  i;

  for(i=0; i<4; i++) {
    EMS_record->mapping_save_area[h][i].hindex = EMS_record->mapping[i].hindex;
    EMS_record->mapping_save_area[h][i].logical_page = EMS_record->mapping[i].logical_page;
  }
294

295
  SET_AX( context, 0 ); /* status: ok */
296 297 298 299 300 301 302 303
}


/**********************************************************************
 *          EMS_restore_context
 *
 * Restore physical page mappings from handle specific save area.
 */
304
static void EMS_restore_context( CONTEXT *context )
305 306 307 308 309 310 311 312 313
{
  WORD handle = DX_reg(context);
  int  i;

  for(i=0; i<4; i++) {
    int hindex       = EMS_record->mapping_save_area[handle][i].hindex;
    int logical_page = EMS_record->mapping_save_area[handle][i].logical_page;

    if(EMS_map( i, hindex, logical_page )) {
314
      SET_AX( context, 0x8e ); /* status: restore of mapping context failed */
315 316 317
      return;
    }
  }
318

319
  SET_AX( context, 0 ); /* status: ok */
320 321
}

322
/**********************************************************************
323
 *          DOSVM_Int67Handler
324 325 326
 *
 * Handler for interrupt 67h EMS routines.
 */
327
void WINAPI DOSVM_Int67Handler( CONTEXT *context )
328 329 330 331
{
  switch AH_reg(context) {

  case 0x40: /* EMS - GET MANAGER STATUS */
332
    SET_AH( context, 0 ); /* status: ok */
333 334 335 336
    break;

  case 0x41: /* EMS - GET PAGE FRAME SEGMENT */
    EMS_init();
337 338
    SET_BX( context, EMS_record->frame_selector ); /* segment of page frame */
    SET_AH( context, 0 );                          /* status: ok */
339 340 341 342 343
    break;

  case 0x42: /* EMS - GET NUMBER OF PAGES */
    EMS_init();
    /* 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 350 351 352 353 354 355 356 357
    break;

  case 0x43: /* EMS - GET HANDLE AND ALLOCATE MEMORY */
    EMS_init();
    EMS_alloc(context);
    break;

  case 0x44: /* EMS - MAP MEMORY */
    EMS_init();
358
    SET_AH( context, EMS_map( AL_reg(context), DX_reg(context), BX_reg(context) ) );
359 360 361 362 363 364 365 366
    break;

  case 0x45: /* EMS - RELEASE HANDLE AND MEMORY */
    EMS_init();
    EMS_free(context);
    break;

  case 0x46: /* EMS - GET EMM VERSION */
367 368
    SET_AL( context, 0x40 ); /* version 4.0 */
    SET_AH( context, 0 );    /* status: ok */
369 370 371
    break;

  case 0x47: /* EMS - SAVE MAPPING CONTEXT */
372
    EMS_init();
373 374 375
    EMS_save_context(context);
    break;

376
  case 0x48: /* EMS - RESTORE MAPPING CONTEXT */
377 378
    EMS_init();
    EMS_restore_context(context);
379 380 381 382 383 384 385 386
    break;

  case 0x49: /* EMS - reserved - GET I/O PORT ADDRESSES */
  case 0x4a: /* EMS - reserved - GET TRANSLATION ARRAY */
    INT_BARF(context,0x67);
    break;

  case 0x4b: /* EMS - GET NUMBER OF EMM HANDLES */
387 388
    SET_BX( context, EMS_MAX_HANDLES ); /* EMM handles */
    SET_AH( context, 0 );               /* status: ok */
389 390 391 392 393 394
    break;

  case 0x4c: /* EMS - GET PAGES OWNED BY HANDLE */
  case 0x4d: /* EMS - GET PAGES FOR ALL HANDLES */
  case 0x4e: /* EMS - GET OR SET PAGE MAP */
  case 0x4f: /* EMS 4.0 - GET/SET PARTIAL PAGE MAP */
395 396 397
    INT_BARF(context,0x67);
    break;

398
  case 0x50: /* EMS 4.0 - MAP/UNMAP MULTIPLE HANDLE PAGES */
399 400 401 402
    EMS_init();
    EMS_map_multiple(context);
    break;

403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
  case 0x51: /* EMS 4.0 - REALLOCATE PAGES */
  case 0x52: /* EMS 4.0 - GET/SET HANDLE ATTRIBUTES */
    INT_BARF(context,0x67);
    break;

  case 0x53: /* EMS 4.0 - GET/SET HANDLE NAME */
    EMS_init();
    EMS_access_name(context);
    break;

  case 0x54: /* EMS 4.0 - GET HANDLE DIRECTORY */
  case 0x55: /* EMS 4.0 - ALTER PAGE MAP AND JUMP */
  case 0x56: /* EMS 4.0 - ALTER PAGE MAP AND CALL */
  case 0x57: /* EMS 4.0 - MOVE/EXCHANGE MEMORY REGION */
  case 0x58: /* EMS 4.0 - GET MAPPABLE PHYSICAL ADDRESS ARRAY */
418 419 420
    INT_BARF(context,0x67);
    break;

421
  case 0x59: /* EMS 4.0 - GET EXPANDED MEMORY HARDWARE INFORMATION */
422 423 424
    if(AL_reg(context) == 0x01) {
      EMS_init();
      /* 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 431 432 433
    } else
      INT_BARF(context,0x67);
    break;

434 435 436 437
  case 0x5a: /* EMS 4.0 - ALLOCATE STANDARD/RAW PAGES */
  case 0x5b: /* EMS 4.0 - ALTERNATE MAP REGISTER SET */
  case 0x5c: /* EMS 4.0 - PREPARE EXPANDED MEMORY HARDWARE FOR WARM BOOT */
  case 0x5d: /* EMS 4.0 - ENABLE/DISABLE OS FUNCTION SET FUNCTIONS */
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
    INT_BARF(context,0x67);
    break;

  case 0xde: /* Virtual Control Program Interface (VCPI) */
    if(AL_reg(context) == 0x00) {
      /*
       * VCPI INSTALLATION CHECK
       * (AH_reg() != 0) means VCPI is not present
       */
      TRACE("- VCPI installation check\n");
      return;
    } else
      INT_BARF(context,0x67);
    break;

453 454 455 456 457 458 459 460 461 462 463
  default:
    INT_BARF(context,0x67);
  }
}


/**********************************************************************
 *          EMS_Ioctl_Handler
 *
 * Handler for interrupt 21h IOCTL routine for device "EMMXXXX0".
 */
464
void EMS_Ioctl_Handler( CONTEXT *context )
465 466 467 468 469 470
{
  assert(AH_reg(context) == 0x44);

  switch AL_reg(context) {
  case 0x00: /* IOCTL - GET DEVICE INFORMATION */
      RESET_CFLAG(context); /* operation was successful */
471
      SET_DX( context, 0x4080 ); /* bit 14 (support ioctl read) and
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
                                 * bit 7 (is_device) */
      break;

  case 0x02: /* EMS - GET MEMORY MANAGER INFORMATION */
      /*
       * This is what is called "Windows Global EMM Import Specification".
       * Undocumented of course! Supports three requests:
       * GET API ENTRY POINT
       * GET EMM IMPORT STRUCTURE ADDRESS
       * GET MEMORY MANAGER VERSION
       */
      INT_BARF(context,0x21);
      break;

  case 0x07: /* IOCTL - GET OUTPUT STATUS */
      RESET_CFLAG(context); /* operation was successful */
488
      SET_AL( context, 0xff ); /* device is ready */
489 490 491 492 493 494 495
      break;

  default:
      INT_BARF(context,0x21);
      break;
  }
}