1 /* 2 * COPYRIGHT: GPL - See COPYING in the top level directory 3 * PROJECT: ReactOS Virtual DOS Machine 4 * FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c 5 * PURPOSE: DOS32 Kernel 6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org> 7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr) 8 */ 9 10 /* INCLUDES *******************************************************************/ 11 12 #include "ntvdm.h" 13 14 #define NDEBUG 15 #include <debug.h> 16 17 #include "emulator.h" 18 #include "cpu/cpu.h" 19 #include "int32.h" 20 21 #include "dos.h" 22 #include "dos/dem.h" 23 #include "country.h" 24 #include "device.h" 25 #include "handle.h" 26 #include "dosfiles.h" 27 #include "memory.h" 28 #include "process.h" 29 #include "himem.h" 30 31 #include "bios/bios.h" 32 33 #include "io.h" 34 #include "hardware/ps2.h" 35 36 #include "emsdrv.h" 37 38 /* PRIVATE VARIABLES **********************************************************/ 39 40 CALLBACK16 DosContext; 41 42 /* PUBLIC VARIABLES ***********************************************************/ 43 44 /* Global DOS data area contained in guest memory */ 45 PDOS_DATA DosData; 46 /* Easy accessors to useful DOS data area parts */ 47 PDOS_SYSVARS SysVars; 48 PDOS_SDA Sda; 49 50 /* PRIVATE FUNCTIONS **********************************************************/ 51 52 static BOOLEAN DosChangeDrive(BYTE Drive) 53 { 54 CHAR DirectoryPath[DOS_CMDLINE_LENGTH + 1]; 55 56 /* Make sure the drive exists */ 57 if (Drive >= SysVars->NumLocalDrives) return FALSE; 58 59 RtlZeroMemory(DirectoryPath, sizeof(DirectoryPath)); 60 61 /* Find the path to the new current directory */ 62 snprintf(DirectoryPath, 63 DOS_CMDLINE_LENGTH, 64 "%c:\\%s", 65 'A' + Drive, 66 DosData->CurrentDirectories[Drive]); 67 68 /* Change the current directory of the process */ 69 if (!SetCurrentDirectoryA(DirectoryPath)) return FALSE; 70 71 /* Set the current drive */ 72 Sda->CurrentDrive = Drive; 73 74 /* Return success */ 75 return TRUE; 76 } 77 78 static BOOLEAN DosChangeDirectory(LPSTR Directory) 79 { 80 BYTE DriveNumber; 81 DWORD Attributes; 82 LPSTR Path; 83 CHAR CurrentDirectory[MAX_PATH]; 84 CHAR DosDirectory[DOS_DIR_LENGTH]; 85 86 /* Make sure the directory path is not too long */ 87 if (strlen(Directory) >= DOS_DIR_LENGTH) 88 { 89 Sda->LastErrorCode = ERROR_PATH_NOT_FOUND; 90 return FALSE; 91 } 92 93 /* Check whether the directory string is of format "X:..." */ 94 if (strlen(Directory) >= 2 && Directory[1] == ':') 95 { 96 /* Get the drive number */ 97 DriveNumber = RtlUpperChar(Directory[0]) - 'A'; 98 99 /* Make sure the drive exists */ 100 if (DriveNumber >= SysVars->NumLocalDrives) 101 { 102 Sda->LastErrorCode = ERROR_PATH_NOT_FOUND; 103 return FALSE; 104 } 105 } 106 else 107 { 108 /* Keep the current drive number */ 109 DriveNumber = Sda->CurrentDrive; 110 } 111 112 /* Get the file attributes */ 113 Attributes = GetFileAttributesA(Directory); 114 115 /* Make sure the path exists and is a directory */ 116 if ((Attributes == INVALID_FILE_ATTRIBUTES) || 117 !(Attributes & FILE_ATTRIBUTE_DIRECTORY)) 118 { 119 Sda->LastErrorCode = ERROR_PATH_NOT_FOUND; 120 return FALSE; 121 } 122 123 /* Check if this is the current drive */ 124 if (DriveNumber == Sda->CurrentDrive) 125 { 126 /* Change the directory */ 127 if (!SetCurrentDirectoryA(Directory)) 128 { 129 Sda->LastErrorCode = LOWORD(GetLastError()); 130 return FALSE; 131 } 132 } 133 134 /* Get the (possibly new) current directory (needed if we specified a relative directory) */ 135 if (!GetCurrentDirectoryA(sizeof(CurrentDirectory), CurrentDirectory)) 136 { 137 // TODO: Use some kind of default path? 138 return FALSE; 139 } 140 141 /* Convert it to a DOS path */ 142 if (!GetShortPathNameA(CurrentDirectory, DosDirectory, sizeof(DosDirectory))) 143 { 144 // TODO: Use some kind of default path? 145 return FALSE; 146 } 147 148 /* Get the directory part of the path and set the current directory for the drive */ 149 Path = strchr(DosDirectory, '\\'); 150 if (Path != NULL) 151 { 152 Path++; // Skip the backslash 153 strncpy(DosData->CurrentDirectories[DriveNumber], Path, DOS_DIR_LENGTH); 154 } 155 else 156 { 157 DosData->CurrentDirectories[DriveNumber][0] = '\0'; 158 } 159 160 /* Return success */ 161 return TRUE; 162 } 163 164 static BOOLEAN DosIsFileOnCdRom(VOID) 165 { 166 UINT DriveType; 167 CHAR RootPathName[4]; 168 169 /* Construct a simple <letter>:\ string to get drive type */ 170 RootPathName[0] = Sda->CurrentDrive + 'A'; 171 RootPathName[1] = ':'; 172 RootPathName[2] = '\\'; 173 RootPathName[3] = ANSI_NULL; 174 175 DriveType = GetDriveTypeA(RootPathName); 176 return (DriveType == DRIVE_CDROM); 177 } 178 179 /* PUBLIC FUNCTIONS ***********************************************************/ 180 181 BOOLEAN DosControlBreak(VOID) 182 { 183 setCF(0); 184 185 /* Print an extra newline */ 186 DosPrintCharacter(DOS_OUTPUT_HANDLE, '\r'); 187 DosPrintCharacter(DOS_OUTPUT_HANDLE, '\n'); 188 189 /* Call interrupt 0x23 */ 190 Int32Call(&DosContext, 0x23); 191 192 if (getCF()) 193 { 194 DosTerminateProcess(Sda->CurrentPsp, 0, 0); 195 return TRUE; 196 } 197 198 return FALSE; 199 } 200 201 VOID WINAPI DosInt20h(LPWORD Stack) 202 { 203 /* 204 * This is the exit interrupt (alias to INT 21h, AH=00h). 205 * CS must be the PSP segment. 206 */ 207 DosTerminateProcess(Stack[STACK_CS], 0, 0); 208 } 209 210 VOID WINAPI DosInt21h(LPWORD Stack) 211 { 212 BYTE Character; 213 SYSTEMTIME SystemTime; 214 PCHAR String; 215 216 Sda->InDos++; 217 218 /* Save the value of SS:SP on entry in the PSP */ 219 SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack = 220 MAKELONG(getSP() + (STACK_FLAGS + 1) * 2, getSS()); 221 222 /* Check the value in the AH register */ 223 switch (getAH()) 224 { 225 /* Terminate Program */ 226 case 0x00: 227 { 228 /* CS must be the PSP segment */ 229 DosTerminateProcess(Stack[STACK_CS], 0, 0); 230 break; 231 } 232 233 /* Read Character from STDIN with Echo */ 234 case 0x01: 235 { 236 DPRINT("INT 21h, AH = 01h\n"); 237 238 Character = DosReadCharacter(DOS_INPUT_HANDLE, TRUE); 239 if (Character == 0x03 && DosControlBreak()) break; 240 241 setAL(Character); 242 break; 243 } 244 245 /* Write Character to STDOUT */ 246 case 0x02: 247 { 248 // FIXME: Under DOS 2+, output handle may be redirected!!!! 249 Character = getDL(); 250 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character); 251 252 /* 253 * We return the output character (DOS 2.1+). 254 * Also, if we're going to output a TAB, then 255 * don't return a TAB but a SPACE instead. 256 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm 257 * for more information. 258 */ 259 setAL(Character == '\t' ? ' ' : Character); 260 break; 261 } 262 263 /* Read Character from STDAUX */ 264 case 0x03: 265 { 266 // FIXME: Really read it from STDAUX! 267 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n"); 268 // setAL(DosReadCharacter()); 269 break; 270 } 271 272 /* Write Character to STDAUX */ 273 case 0x04: 274 { 275 // FIXME: Really write it to STDAUX! 276 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n"); 277 // DosPrintCharacter(getDL()); 278 break; 279 } 280 281 /* Write Character to Printer */ 282 case 0x05: 283 { 284 // FIXME: Really write it to printer! 285 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n"); 286 DPRINT1("0x%p\n", getDL()); 287 DPRINT1("\n\n-----------\n\n"); 288 break; 289 } 290 291 /* Direct Console I/O */ 292 case 0x06: 293 { 294 Character = getDL(); 295 296 // FIXME: Under DOS 2+, output handle may be redirected!!!! 297 298 if (Character != 0xFF) 299 { 300 /* Output */ 301 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character); 302 303 /* 304 * We return the output character (DOS 2.1+). 305 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm 306 * for more information. 307 */ 308 setAL(Character); 309 } 310 else 311 { 312 /* Input */ 313 if (DosCheckInput()) 314 { 315 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF; 316 setAL(DosReadCharacter(DOS_INPUT_HANDLE, FALSE)); 317 } 318 else 319 { 320 /* No character available */ 321 Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF; 322 setAL(0x00); 323 } 324 } 325 326 break; 327 } 328 329 /* Direct Character Input without Echo */ 330 case 0x07: 331 { 332 DPRINT("Direct char input without echo\n"); 333 setAL(DosReadCharacter(DOS_INPUT_HANDLE, FALSE)); 334 break; 335 } 336 337 /* Character Input without Echo */ 338 case 0x08: 339 { 340 DPRINT("Char input without echo\n"); 341 342 Character = DosReadCharacter(DOS_INPUT_HANDLE, FALSE); 343 if (Character == 0x03 && DosControlBreak()) break; 344 345 setAL(Character); 346 break; 347 } 348 349 /* Write String to STDOUT */ 350 case 0x09: 351 { 352 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX()); 353 354 while (*String != '$') 355 { 356 DosPrintCharacter(DOS_OUTPUT_HANDLE, *String); 357 String++; 358 } 359 360 /* 361 * We return the terminating character (DOS 2.1+). 362 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm 363 * for more information. 364 */ 365 setAL('$'); // *String 366 break; 367 } 368 369 /* Read Buffered Input */ 370 case 0x0A: 371 { 372 PDOS_INPUT_BUFFER InputBuffer = (PDOS_INPUT_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX()); 373 374 DPRINT("Read Buffered Input\n"); 375 if (InputBuffer->MaxLength == 0) break; 376 377 /* Read from standard input */ 378 InputBuffer->Length = DosReadLineBuffered( 379 DOS_INPUT_HANDLE, 380 MAKELONG(getDX() + FIELD_OFFSET(DOS_INPUT_BUFFER, Buffer), getDS()), 381 InputBuffer->MaxLength 382 ); 383 384 break; 385 } 386 387 /* Get STDIN Status */ 388 case 0x0B: 389 { 390 setAL(DosCheckInput() ? 0xFF : 0x00); 391 break; 392 } 393 394 /* Flush Buffer and Read STDIN */ 395 case 0x0C: 396 { 397 BYTE InputFunction = getAL(); 398 399 /* Flush STDIN buffer */ 400 DosFlushFileBuffers(DOS_INPUT_HANDLE); 401 402 /* 403 * If the input function number contained in AL is valid, i.e. 404 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves 405 * recursively with AL == AH. 406 */ 407 if (InputFunction == 0x01 || InputFunction == 0x06 || 408 InputFunction == 0x07 || InputFunction == 0x08 || 409 InputFunction == 0x0A) 410 { 411 /* Call ourselves recursively */ 412 setAH(InputFunction); 413 DosInt21h(Stack); 414 } 415 break; 416 } 417 418 /* Disk Reset */ 419 case 0x0D: 420 { 421 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Sda->CurrentPsp); 422 423 // TODO: Flush what's needed. 424 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n"); 425 426 /* Clear CF in DOS 6 only */ 427 if (PspBlock->DosVersion == 0x0006) 428 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 429 430 break; 431 } 432 433 /* Set Default Drive */ 434 case 0x0E: 435 { 436 DosChangeDrive(getDL()); 437 setAL(SysVars->NumLocalDrives); 438 break; 439 } 440 441 /* NULL Function for CP/M Compatibility */ 442 case 0x18: 443 { 444 /* 445 * This function corresponds to the CP/M BDOS function 446 * "get bit map of logged drives", which is meaningless 447 * under MS-DOS. 448 * 449 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB 450 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm 451 * for more information. 452 */ 453 setAL(0x00); 454 break; 455 } 456 457 /* Get Default Drive */ 458 case 0x19: 459 { 460 setAL(Sda->CurrentDrive); 461 break; 462 } 463 464 /* Set Disk Transfer Area */ 465 case 0x1A: 466 { 467 Sda->DiskTransferArea = MAKELONG(getDX(), getDS()); 468 break; 469 } 470 471 /* NULL Function for CP/M Compatibility */ 472 case 0x1D: 473 case 0x1E: 474 { 475 /* 476 * Function 0x1D corresponds to the CP/M BDOS function 477 * "get bit map of read-only drives", which is meaningless 478 * under MS-DOS. 479 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm 480 * for more information. 481 * 482 * Function 0x1E corresponds to the CP/M BDOS function 483 * "set file attributes", which was meaningless under MS-DOS 1.x. 484 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm 485 * for more information. 486 */ 487 setAL(0x00); 488 break; 489 } 490 491 /* NULL Function for CP/M Compatibility */ 492 case 0x20: 493 { 494 /* 495 * This function corresponds to the CP/M BDOS function 496 * "get/set default user (sublibrary) number", which is meaningless 497 * under MS-DOS. 498 * 499 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION 500 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm 501 * for more information. 502 */ 503 setAL(0x00); 504 break; 505 } 506 507 /* Set Interrupt Vector */ 508 case 0x25: 509 { 510 ULONG FarPointer = MAKELONG(getDX(), getDS()); 511 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n", 512 getAL(), HIWORD(FarPointer), LOWORD(FarPointer)); 513 514 /* Write the new far pointer to the IDT */ 515 ((PULONG)BaseAddress)[getAL()] = FarPointer; 516 break; 517 } 518 519 /* Create New PSP */ 520 case 0x26: 521 { 522 /* DOS 2+ assumes that the caller's CS is the segment of the PSP to copy */ 523 DosClonePsp(getDX(), Stack[STACK_CS]); 524 break; 525 } 526 527 /* Parse Filename into FCB */ 528 case 0x29: 529 { 530 PCHAR FileName = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI()); 531 PDOS_FCB Fcb = (PDOS_FCB)SEG_OFF_TO_PTR(getES(), getDI()); 532 BYTE Options = getAL(); 533 CHAR FillChar = ' '; 534 UINT i; 535 536 if (FileName[1] == ':') 537 { 538 /* Set the drive number */ 539 Fcb->DriveNumber = RtlUpperChar(FileName[0]) - 'A' + 1; 540 541 /* Skip to the file name part */ 542 FileName += 2; 543 } 544 else 545 { 546 /* No drive number specified */ 547 if (Options & (1 << 1)) Fcb->DriveNumber = Sda->CurrentDrive + 1; 548 else Fcb->DriveNumber = 0; 549 } 550 551 /* Parse the file name */ 552 i = 0; 553 while ((*FileName > 0x20) && (i < 8)) 554 { 555 if (*FileName == '.') break; 556 else if (*FileName == '*') 557 { 558 FillChar = '?'; 559 break; 560 } 561 562 Fcb->FileName[i++] = RtlUpperChar(*FileName++); 563 } 564 565 /* Fill the whole field with blanks only if bit 2 is not set */ 566 if ((FillChar != ' ') || (i != 0) || !(Options & (1 << 2))) 567 { 568 for (; i < 8; i++) Fcb->FileName[i] = FillChar; 569 } 570 571 /* Skip to the extension part */ 572 while (*FileName > 0x20 && *FileName != '.') FileName++; 573 if (*FileName == '.') FileName++; 574 575 /* Now parse the extension */ 576 i = 0; 577 FillChar = ' '; 578 579 while ((*FileName > 0x20) && (i < 3)) 580 { 581 if (*FileName == '*') 582 { 583 FillChar = '?'; 584 break; 585 } 586 587 Fcb->FileExt[i++] = RtlUpperChar(*FileName++); 588 } 589 590 /* Fill the whole field with blanks only if bit 3 is not set */ 591 if ((FillChar != ' ') || (i != 0) || !(Options & (1 << 3))) 592 { 593 for (; i < 3; i++) Fcb->FileExt[i] = FillChar; 594 } 595 596 break; 597 } 598 599 /* Get System Date */ 600 case 0x2A: 601 { 602 GetLocalTime(&SystemTime); 603 setCX(SystemTime.wYear); 604 setDX(MAKEWORD(SystemTime.wDay, SystemTime.wMonth)); 605 setAL(SystemTime.wDayOfWeek); 606 break; 607 } 608 609 /* Set System Date */ 610 case 0x2B: 611 { 612 GetLocalTime(&SystemTime); 613 SystemTime.wYear = getCX(); 614 SystemTime.wMonth = getDH(); 615 SystemTime.wDay = getDL(); 616 617 /* Return success or failure */ 618 setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF); 619 break; 620 } 621 622 /* Get System Time */ 623 case 0x2C: 624 { 625 GetLocalTime(&SystemTime); 626 setCX(MAKEWORD(SystemTime.wMinute, SystemTime.wHour)); 627 setDX(MAKEWORD(SystemTime.wMilliseconds / 10, SystemTime.wSecond)); 628 break; 629 } 630 631 /* Set System Time */ 632 case 0x2D: 633 { 634 GetLocalTime(&SystemTime); 635 SystemTime.wHour = getCH(); 636 SystemTime.wMinute = getCL(); 637 SystemTime.wSecond = getDH(); 638 SystemTime.wMilliseconds = getDL() * 10; // In hundredths of seconds 639 640 /* Return success or failure */ 641 setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF); 642 break; 643 } 644 645 /* Get Disk Transfer Area */ 646 case 0x2F: 647 { 648 setES(HIWORD(Sda->DiskTransferArea)); 649 setBX(LOWORD(Sda->DiskTransferArea)); 650 break; 651 } 652 653 /* Get DOS Version */ 654 case 0x30: 655 { 656 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Sda->CurrentPsp); 657 658 /* 659 * DOS 2+ - GET DOS VERSION 660 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm 661 * for more information. 662 */ 663 664 if (LOBYTE(PspBlock->DosVersion) < 5 || getAL() == 0x00) 665 { 666 /* 667 * Return DOS OEM number: 668 * 0x00 for IBM PC-DOS 669 * 0x02 for packaged MS-DOS 670 * 0xFF for NT DOS 671 */ 672 setBH(0xFF); 673 } 674 675 if (LOBYTE(PspBlock->DosVersion) >= 5 && getAL() == 0x01) 676 { 677 /* 678 * Return version flag: 679 * 1 << 3 if DOS is in ROM, 680 * 0 (reserved) if not. 681 */ 682 setBH(0x00); 683 } 684 685 /* Return DOS 24-bit user serial number in BL:CX */ 686 setBL(0x00); 687 setCX(0x0000); 688 689 /* 690 * Return DOS version: Minor:Major in AH:AL 691 * The Windows NT DOS box returns version 5.00, subject to SETVER. 692 */ 693 setAX(PspBlock->DosVersion); 694 695 break; 696 } 697 698 /* Terminate and Stay Resident */ 699 case 0x31: 700 { 701 DPRINT1("Process going resident: %u paragraphs kept\n", getDX()); 702 DosTerminateProcess(Sda->CurrentPsp, getAL(), getDX()); 703 break; 704 } 705 706 /* Extended functionalities */ 707 case 0x33: 708 { 709 switch (getAL()) 710 { 711 /* 712 * DOS 4+ - GET BOOT DRIVE 713 */ 714 case 0x05: 715 { 716 setDL(SysVars->BootDrive); 717 break; 718 } 719 720 /* 721 * DOS 5+ - GET TRUE VERSION NUMBER 722 * This function always returns the true version number, unlike 723 * AH=30h, whose return value may be changed with SETVER. 724 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm 725 * for more information. 726 */ 727 case 0x06: 728 { 729 /* 730 * Return the true DOS version: Minor:Major in BH:BL 731 * The Windows NT DOS box returns BX=3205h (version 5.50). 732 */ 733 setBX(NTDOS_VERSION); 734 735 /* DOS revision 0 */ 736 setDL(0x00); 737 738 /* Unpatched DOS */ 739 setDH(0x00); 740 741 break; 742 } 743 744 default: // goto Default; 745 { 746 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n", 747 getAH(), getAL()); 748 } 749 } 750 751 break; 752 } 753 754 /* Get Address of InDOS flag */ 755 case 0x34: 756 { 757 setES(DOS_DATA_SEGMENT); 758 setBX(DOS_DATA_OFFSET(Sda.InDos)); 759 break; 760 } 761 762 /* Get Interrupt Vector */ 763 case 0x35: 764 { 765 ULONG FarPointer = ((PULONG)BaseAddress)[getAL()]; 766 767 /* Read the address from the IDT into ES:BX */ 768 setES(HIWORD(FarPointer)); 769 setBX(LOWORD(FarPointer)); 770 break; 771 } 772 773 /* Get Free Disk Space */ 774 case 0x36: 775 { 776 CHAR RootPath[] = "?:\\"; 777 DWORD SectorsPerCluster; 778 DWORD BytesPerSector; 779 DWORD NumberOfFreeClusters; 780 DWORD TotalNumberOfClusters; 781 782 if (getDL() == 0x00) 783 RootPath[0] = 'A' + Sda->CurrentDrive; 784 else 785 RootPath[0] = 'A' + getDL() - 1; 786 787 if (GetDiskFreeSpaceA(RootPath, 788 &SectorsPerCluster, 789 &BytesPerSector, 790 &NumberOfFreeClusters, 791 &TotalNumberOfClusters)) 792 { 793 setAX(LOWORD(SectorsPerCluster)); 794 setCX(LOWORD(BytesPerSector)); 795 setBX(min(NumberOfFreeClusters, 0xFFFF)); 796 setDX(min(TotalNumberOfClusters, 0xFFFF)); 797 } 798 else 799 { 800 /* Error */ 801 setAX(0xFFFF); 802 } 803 804 break; 805 } 806 807 /* SWITCH character - AVAILDEV */ 808 case 0x37: 809 { 810 switch (getAL()) 811 { 812 /* 813 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER 814 * This setting is ignored by MS-DOS 4.0+. 815 * MS-DOS 5+ always return AL=00h/DL=2Fh. 816 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm 817 * for more information. 818 */ 819 case 0x00: 820 setDL('/'); 821 setAL(0x00); 822 break; 823 824 /* 825 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER 826 * This setting is ignored by MS-DOS 5+. 827 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm 828 * for more information. 829 */ 830 case 0x01: 831 // getDL(); 832 setAL(0xFF); 833 break; 834 835 /* 836 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE 837 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm 838 * for more information. 839 */ 840 case 0x02: 841 // setDL(); 842 setAL(0xFF); 843 break; 844 845 /* 846 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE 847 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm 848 * for more information. 849 */ 850 case 0x03: 851 // getDL(); 852 setAL(0xFF); 853 break; 854 855 /* Invalid subfunction */ 856 default: 857 setAL(0xFF); 858 break; 859 } 860 861 break; 862 } 863 864 /* Get/Set Country-dependent Information */ 865 case 0x38: 866 { 867 WORD CountryId = getAL() < 0xFF ? getAL() : getBX(); 868 WORD ErrorCode; 869 870 ErrorCode = DosGetCountryInfo(&CountryId, 871 (PDOS_COUNTRY_INFO)SEG_OFF_TO_PTR(getDS(), getDX())); 872 873 if (ErrorCode == ERROR_SUCCESS) 874 { 875 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 876 setBX(CountryId); 877 } 878 else 879 { 880 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 881 setAX(ErrorCode); 882 } 883 884 break; 885 } 886 887 /* Create Directory */ 888 case 0x39: 889 { 890 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX()); 891 892 if (CreateDirectoryA(String, NULL)) 893 { 894 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 895 } 896 else 897 { 898 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 899 setAX(LOWORD(GetLastError())); 900 } 901 902 break; 903 } 904 905 /* Remove Directory */ 906 case 0x3A: 907 { 908 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX()); 909 910 if (RemoveDirectoryA(String)) 911 { 912 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 913 } 914 else 915 { 916 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 917 setAX(LOWORD(GetLastError())); 918 } 919 920 break; 921 } 922 923 /* Set Current Directory */ 924 case 0x3B: 925 { 926 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX()); 927 928 if (DosChangeDirectory(String)) 929 { 930 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 931 } 932 else 933 { 934 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 935 setAX(Sda->LastErrorCode); 936 } 937 938 break; 939 } 940 941 /* Create or Truncate File */ 942 case 0x3C: 943 { 944 WORD FileHandle; 945 WORD ErrorCode = DosCreateFile(&FileHandle, 946 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()), 947 CREATE_ALWAYS, 948 getCX()); 949 950 if (ErrorCode == ERROR_SUCCESS) 951 { 952 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 953 setAX(FileHandle); 954 } 955 else 956 { 957 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 958 setAX(ErrorCode); 959 } 960 961 break; 962 } 963 964 /* Open File or Device */ 965 case 0x3D: 966 { 967 WORD FileHandle; 968 BYTE AccessShareModes = getAL(); 969 LPCSTR FileName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()); 970 WORD ErrorCode = DosOpenFile(&FileHandle, FileName, AccessShareModes); 971 972 /* 973 * Check if we failed because we attempted to open a file for write 974 * on a CDROM drive. In that situation, attempt to reopen for read 975 */ 976 if (ErrorCode == ERROR_ACCESS_DENIED && 977 (AccessShareModes & 0x03) != 0 && DosIsFileOnCdRom()) 978 { 979 ErrorCode = DosOpenFile(&FileHandle, FileName, 0); 980 } 981 982 if (ErrorCode == ERROR_SUCCESS) 983 { 984 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 985 setAX(FileHandle); 986 } 987 else 988 { 989 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 990 setAX(ErrorCode); 991 } 992 993 break; 994 } 995 996 /* Close File or Device */ 997 case 0x3E: 998 { 999 if (DosCloseHandle(getBX())) 1000 { 1001 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1002 } 1003 else 1004 { 1005 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1006 setAX(ERROR_INVALID_HANDLE); 1007 } 1008 1009 break; 1010 } 1011 1012 /* Read from File or Device */ 1013 case 0x3F: 1014 { 1015 WORD BytesRead = 0; 1016 WORD ErrorCode; 1017 1018 DPRINT("DosReadFile(0x%04X)\n", getBX()); 1019 1020 ErrorCode = DosReadFile(getBX(), 1021 MAKELONG(getDX(), getDS()), 1022 getCX(), 1023 &BytesRead); 1024 1025 if (ErrorCode == ERROR_SUCCESS) 1026 { 1027 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1028 setAX(BytesRead); 1029 } 1030 else if (ErrorCode != ERROR_NOT_READY) 1031 { 1032 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1033 setAX(ErrorCode); 1034 } 1035 1036 break; 1037 } 1038 1039 /* Write to File or Device */ 1040 case 0x40: 1041 { 1042 WORD BytesWritten = 0; 1043 WORD ErrorCode = DosWriteFile(getBX(), 1044 MAKELONG(getDX(), getDS()), 1045 getCX(), 1046 &BytesWritten); 1047 1048 if (ErrorCode == ERROR_SUCCESS) 1049 { 1050 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1051 setAX(BytesWritten); 1052 } 1053 else 1054 { 1055 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1056 setAX(ErrorCode); 1057 } 1058 1059 break; 1060 } 1061 1062 /* Delete File */ 1063 case 0x41: 1064 { 1065 LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX()); 1066 1067 if (demFileDelete(FileName) == ERROR_SUCCESS) 1068 { 1069 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1070 /* 1071 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm 1072 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file." 1073 */ 1074 setAL(RtlUpperChar(FileName[0]) - 'A'); 1075 } 1076 else 1077 { 1078 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1079 setAX(GetLastError()); 1080 } 1081 1082 break; 1083 } 1084 1085 /* Seek File */ 1086 case 0x42: 1087 { 1088 DWORD NewLocation; 1089 WORD ErrorCode = DosSeekFile(getBX(), 1090 MAKELONG(getDX(), getCX()), 1091 getAL(), 1092 &NewLocation); 1093 1094 if (ErrorCode == ERROR_SUCCESS) 1095 { 1096 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1097 1098 /* Return the new offset in DX:AX */ 1099 setDX(HIWORD(NewLocation)); 1100 setAX(LOWORD(NewLocation)); 1101 } 1102 else 1103 { 1104 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1105 setAX(ErrorCode); 1106 } 1107 1108 break; 1109 } 1110 1111 /* Get/Set File Attributes */ 1112 case 0x43: 1113 { 1114 DWORD Attributes; 1115 LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX()); 1116 1117 if (getAL() == 0x00) 1118 { 1119 /* Get the attributes */ 1120 Attributes = GetFileAttributesA(FileName); 1121 1122 /* Check if it failed */ 1123 if (Attributes == INVALID_FILE_ATTRIBUTES) 1124 { 1125 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1126 setAX(GetLastError()); 1127 } 1128 else 1129 { 1130 /* Return the attributes that DOS can understand */ 1131 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1132 setCX(Attributes & 0x00FF); 1133 } 1134 } 1135 else if (getAL() == 0x01) 1136 { 1137 /* Try to set the attributes */ 1138 if (SetFileAttributesA(FileName, getCL())) 1139 { 1140 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1141 } 1142 else 1143 { 1144 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1145 setAX(GetLastError()); 1146 } 1147 } 1148 else 1149 { 1150 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1151 setAX(ERROR_INVALID_FUNCTION); 1152 } 1153 1154 break; 1155 } 1156 1157 /* IOCTL */ 1158 case 0x44: 1159 { 1160 WORD Length = getCX(); 1161 1162 if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length)) 1163 { 1164 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1165 setAX(Length); 1166 } 1167 else 1168 { 1169 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1170 setAX(Sda->LastErrorCode); 1171 } 1172 1173 break; 1174 } 1175 1176 /* Duplicate Handle */ 1177 case 0x45: 1178 { 1179 WORD NewHandle = DosDuplicateHandle(getBX()); 1180 1181 if (NewHandle != INVALID_DOS_HANDLE) 1182 { 1183 setAX(NewHandle); 1184 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1185 } 1186 else 1187 { 1188 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1189 setAX(Sda->LastErrorCode); 1190 } 1191 1192 break; 1193 } 1194 1195 /* Force Duplicate Handle */ 1196 case 0x46: 1197 { 1198 if (DosForceDuplicateHandle(getBX(), getCX())) 1199 { 1200 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1201 } 1202 else 1203 { 1204 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1205 setAX(ERROR_INVALID_HANDLE); 1206 } 1207 1208 break; 1209 } 1210 1211 /* Get Current Directory */ 1212 case 0x47: 1213 { 1214 BYTE DriveNumber = getDL(); 1215 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI()); 1216 1217 /* Get the real drive number */ 1218 if (DriveNumber == 0) 1219 { 1220 DriveNumber = Sda->CurrentDrive; 1221 } 1222 else 1223 { 1224 /* Decrement DriveNumber since it was 1-based */ 1225 DriveNumber--; 1226 } 1227 1228 if (DriveNumber < SysVars->NumLocalDrives) 1229 { 1230 /* 1231 * Copy the current directory into the target buffer. 1232 * It doesn't contain the drive letter and the backslash. 1233 */ 1234 strncpy(String, DosData->CurrentDirectories[DriveNumber], DOS_DIR_LENGTH); 1235 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1236 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm 1237 } 1238 else 1239 { 1240 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1241 setAX(ERROR_INVALID_DRIVE); 1242 } 1243 1244 break; 1245 } 1246 1247 /* Allocate Memory */ 1248 case 0x48: 1249 { 1250 WORD MaxAvailable = 0; 1251 WORD Segment = DosAllocateMemory(getBX(), &MaxAvailable); 1252 1253 if (Segment != 0) 1254 { 1255 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1256 setAX(Segment); 1257 } 1258 else 1259 { 1260 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1261 setAX(Sda->LastErrorCode); 1262 setBX(MaxAvailable); 1263 } 1264 1265 break; 1266 } 1267 1268 /* Free Memory */ 1269 case 0x49: 1270 { 1271 if (DosFreeMemory(getES())) 1272 { 1273 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1274 } 1275 else 1276 { 1277 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1278 setAX(Sda->LastErrorCode); 1279 } 1280 1281 break; 1282 } 1283 1284 /* Resize Memory Block */ 1285 case 0x4A: 1286 { 1287 WORD Size; 1288 1289 if (DosResizeMemory(getES(), getBX(), &Size)) 1290 { 1291 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1292 } 1293 else 1294 { 1295 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1296 setAX(Sda->LastErrorCode); 1297 setBX(Size); 1298 } 1299 1300 break; 1301 } 1302 1303 /* Execute */ 1304 case 0x4B: 1305 { 1306 BYTE OrgAL = getAL(); 1307 LPSTR ProgramName = SEG_OFF_TO_PTR(getDS(), getDX()); 1308 PDOS_EXEC_PARAM_BLOCK ParamBlock = SEG_OFF_TO_PTR(getES(), getBX()); 1309 WORD ErrorCode; 1310 1311 if (OrgAL <= DOS_LOAD_OVERLAY) 1312 { 1313 DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)OrgAL; 1314 1315 if (LoadType == DOS_LOAD_AND_EXECUTE) 1316 { 1317 /* Create a new process */ 1318 ErrorCode = DosCreateProcess(ProgramName, 1319 ParamBlock, 1320 MAKELONG(Stack[STACK_IP], Stack[STACK_CS])); 1321 } 1322 else 1323 { 1324 /* Just load an executable */ 1325 ErrorCode = DosLoadExecutable(LoadType, 1326 ProgramName, 1327 ParamBlock, 1328 NULL, 1329 NULL, 1330 MAKELONG(Stack[STACK_IP], Stack[STACK_CS])); 1331 } 1332 } 1333 else if (OrgAL == 0x05) 1334 { 1335 // http://www.ctyme.com/intr/rb-2942.htm 1336 DPRINT1("Set execution state is UNIMPLEMENTED\n"); 1337 ErrorCode = ERROR_CALL_NOT_IMPLEMENTED; 1338 } 1339 else 1340 { 1341 ErrorCode = ERROR_INVALID_FUNCTION; 1342 } 1343 1344 if (ErrorCode == ERROR_SUCCESS) 1345 { 1346 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1347 } 1348 else 1349 { 1350 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1351 setAX(ErrorCode); 1352 } 1353 1354 break; 1355 } 1356 1357 /* Terminate with Return Code */ 1358 case 0x4C: 1359 { 1360 DosTerminateProcess(Sda->CurrentPsp, getAL(), 0); 1361 break; 1362 } 1363 1364 /* Get Return Code (ERRORLEVEL) */ 1365 case 0x4D: 1366 { 1367 /* 1368 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm 1369 * DosErrorLevel is cleared after being read by this function. 1370 */ 1371 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1372 setAX(Sda->ErrorLevel); 1373 Sda->ErrorLevel = 0x0000; // Clear it 1374 break; 1375 } 1376 1377 /* Find First File */ 1378 case 0x4E: 1379 { 1380 WORD Result = (WORD)demFileFindFirst(FAR_POINTER(Sda->DiskTransferArea), 1381 SEG_OFF_TO_PTR(getDS(), getDX()), 1382 getCX()); 1383 1384 setAX(Result); 1385 1386 if (Result == ERROR_SUCCESS) 1387 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1388 else 1389 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1390 1391 break; 1392 } 1393 1394 /* Find Next File */ 1395 case 0x4F: 1396 { 1397 WORD Result = (WORD)demFileFindNext(FAR_POINTER(Sda->DiskTransferArea)); 1398 1399 setAX(Result); 1400 1401 if (Result == ERROR_SUCCESS) 1402 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1403 else 1404 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1405 1406 break; 1407 } 1408 1409 /* Internal - Set Current Process ID (Set PSP Address) */ 1410 case 0x50: 1411 { 1412 DosSetProcessContext(getBX()); 1413 break; 1414 } 1415 1416 /* Internal - Get Current Process ID (Get PSP Address) */ 1417 case 0x51: 1418 /* Get Current PSP Address */ 1419 case 0x62: 1420 { 1421 /* 1422 * Undocumented AH=51h is identical to the documented AH=62h. 1423 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm 1424 * and http://www.ctyme.com/intr/rb-3140.htm 1425 * for more information. 1426 */ 1427 setBX(Sda->CurrentPsp); 1428 break; 1429 } 1430 1431 /* Internal - Get "List of lists" (SYSVARS) */ 1432 case 0x52: 1433 { 1434 /* 1435 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h). 1436 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm 1437 * for more information. 1438 */ 1439 1440 /* Return the DOS "list of lists" in ES:BX */ 1441 setES(DOS_DATA_SEGMENT); 1442 setBX(DOS_DATA_OFFSET(SysVars.FirstDpb)); 1443 break; 1444 } 1445 1446 /* Create Child PSP */ 1447 case 0x55: 1448 { 1449 DosCreatePsp(getDX(), getSI()); 1450 DosSetProcessContext(getDX()); 1451 break; 1452 } 1453 1454 /* Rename File */ 1455 case 0x56: 1456 { 1457 LPSTR ExistingFileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX()); 1458 LPSTR NewFileName = (LPSTR)SEG_OFF_TO_PTR(getES(), getDI()); 1459 1460 /* 1461 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm 1462 * for more information. 1463 */ 1464 1465 if (MoveFileA(ExistingFileName, NewFileName)) 1466 { 1467 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1468 } 1469 else 1470 { 1471 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1472 setAX(GetLastError()); 1473 } 1474 1475 break; 1476 } 1477 1478 /* File Attributes */ 1479 case 0x57: 1480 { 1481 switch (getAL()) 1482 { 1483 /* Get File's last-written Date and Time */ 1484 case 0x00: 1485 { 1486 PDOS_FILE_DESCRIPTOR Descriptor = DosGetHandleFileDescriptor(getBX()); 1487 FILETIME LastWriteTime; 1488 WORD FileDate, FileTime; 1489 1490 if (Descriptor == NULL) 1491 { 1492 /* Invalid handle */ 1493 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1494 // Sda->LastErrorCode = ERROR_INVALID_HANDLE; 1495 setAX(ERROR_INVALID_HANDLE); 1496 break; 1497 } 1498 1499 if (Descriptor->DeviceInfo & FILE_INFO_DEVICE) 1500 { 1501 /* Invalid for devices */ 1502 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1503 // setAX(ERROR_INVALID_FUNCTION); 1504 setAX(ERROR_INVALID_HANDLE); 1505 break; 1506 } 1507 1508 /* 1509 * Retrieve the last-written Win32 date and time, 1510 * and convert it to DOS format. 1511 */ 1512 if (!GetFileTime(Descriptor->Win32Handle, 1513 NULL, NULL, &LastWriteTime) || 1514 !FileTimeToDosDateTime(&LastWriteTime, 1515 &FileDate, &FileTime)) 1516 { 1517 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1518 setAX(GetLastError()); 1519 break; 1520 } 1521 1522 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1523 setCX(FileTime); 1524 setDX(FileDate); 1525 break; 1526 } 1527 1528 /* Set File's last-written Date and Time */ 1529 case 0x01: 1530 { 1531 PDOS_FILE_DESCRIPTOR Descriptor = DosGetHandleFileDescriptor(getBX()); 1532 FILETIME LastWriteTime; 1533 WORD FileDate = getDX(); 1534 WORD FileTime = getCX(); 1535 1536 if (Descriptor == NULL) 1537 { 1538 /* Invalid handle */ 1539 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1540 // Sda->LastErrorCode = ERROR_INVALID_HANDLE; 1541 setAX(ERROR_INVALID_HANDLE); 1542 break; 1543 } 1544 1545 if (Descriptor->DeviceInfo & FILE_INFO_DEVICE) 1546 { 1547 /* Invalid for devices */ 1548 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1549 // setAX(ERROR_INVALID_FUNCTION); 1550 setAX(ERROR_INVALID_HANDLE); 1551 break; 1552 } 1553 1554 /* 1555 * Convert the new last-written DOS date and time 1556 * to Win32 format and set it. 1557 */ 1558 if (!DosDateTimeToFileTime(FileDate, FileTime, 1559 &LastWriteTime) || 1560 !SetFileTime(Descriptor->Win32Handle, 1561 NULL, NULL, &LastWriteTime)) 1562 { 1563 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1564 setAX(GetLastError()); 1565 break; 1566 } 1567 1568 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1569 break; 1570 } 1571 1572 default: // goto Default; 1573 { 1574 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n", 1575 getAH(), getAL()); 1576 } 1577 } 1578 1579 break; 1580 } 1581 1582 /* Get/Set Memory Management Options */ 1583 case 0x58: 1584 { 1585 switch (getAL()) 1586 { 1587 /* Get allocation strategy */ 1588 case 0x00: 1589 { 1590 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1591 setAX(Sda->AllocStrategy); 1592 break; 1593 } 1594 1595 /* Set allocation strategy */ 1596 case 0x01: 1597 { 1598 if ((getBL() & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW)) 1599 == (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW)) 1600 { 1601 /* Can't set both */ 1602 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1603 setAX(ERROR_INVALID_PARAMETER); 1604 break; 1605 } 1606 1607 if ((getBL() & ~(DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW)) 1608 > DOS_ALLOC_LAST_FIT) 1609 { 1610 /* Invalid allocation strategy */ 1611 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1612 setAX(ERROR_INVALID_PARAMETER); 1613 break; 1614 } 1615 1616 Sda->AllocStrategy = getBL(); 1617 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1618 break; 1619 } 1620 1621 /* Get UMB link state */ 1622 case 0x02: 1623 { 1624 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1625 setAL(SysVars->UmbLinked ? 0x01 : 0x00); 1626 break; 1627 } 1628 1629 /* Set UMB link state */ 1630 case 0x03: 1631 { 1632 BOOLEAN Success; 1633 1634 if (getBX()) 1635 Success = DosLinkUmb(); 1636 else 1637 Success = DosUnlinkUmb(); 1638 1639 if (Success) 1640 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1641 else 1642 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1643 1644 break; 1645 } 1646 1647 /* Invalid or unsupported function */ 1648 default: 1649 { 1650 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1651 setAX(ERROR_INVALID_FUNCTION); 1652 } 1653 } 1654 1655 break; 1656 } 1657 1658 /* Get Extended Error Information */ 1659 case 0x59: 1660 { 1661 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n", 1662 getBX()); 1663 break; 1664 } 1665 1666 /* Create Temporary File */ 1667 case 0x5A: 1668 { 1669 LPSTR PathName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX()); 1670 LPSTR FileName = PathName; // The buffer for the path and the full file name is the same. 1671 UINT uRetVal; 1672 WORD FileHandle; 1673 WORD ErrorCode; 1674 1675 /* 1676 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm 1677 * for more information. 1678 */ 1679 1680 // FIXME: Check for buffer validity? 1681 // It should be a ASCIIZ path ending with a '\' + 13 zero bytes 1682 // to receive the generated filename. 1683 1684 /* First create the temporary file */ 1685 uRetVal = GetTempFileNameA(PathName, NULL, 0, FileName); 1686 if (uRetVal == 0) 1687 { 1688 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1689 setAX(GetLastError()); 1690 break; 1691 } 1692 1693 /* Now try to open it in read/write access */ 1694 ErrorCode = DosOpenFile(&FileHandle, FileName, 2); 1695 if (ErrorCode == ERROR_SUCCESS) 1696 { 1697 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1698 setAX(FileHandle); 1699 } 1700 else 1701 { 1702 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1703 setAX(ErrorCode); 1704 } 1705 1706 break; 1707 } 1708 1709 /* Create New File */ 1710 case 0x5B: 1711 { 1712 WORD FileHandle; 1713 WORD ErrorCode = DosCreateFile(&FileHandle, 1714 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()), 1715 CREATE_NEW, 1716 getCX()); 1717 1718 if (ErrorCode == ERROR_SUCCESS) 1719 { 1720 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1721 setAX(FileHandle); 1722 } 1723 else 1724 { 1725 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1726 setAX(ErrorCode); 1727 } 1728 1729 break; 1730 } 1731 1732 /* Lock/Unlock Region of File */ 1733 case 0x5C: 1734 { 1735 if (getAL() == 0x00) 1736 { 1737 /* Lock region of file */ 1738 if (DosLockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI()))) 1739 { 1740 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1741 } 1742 else 1743 { 1744 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1745 setAX(Sda->LastErrorCode); 1746 } 1747 } 1748 else if (getAL() == 0x01) 1749 { 1750 /* Unlock region of file */ 1751 if (DosUnlockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI()))) 1752 { 1753 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1754 } 1755 else 1756 { 1757 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1758 setAX(Sda->LastErrorCode); 1759 } 1760 } 1761 else 1762 { 1763 /* Invalid subfunction */ 1764 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1765 setAX(ERROR_INVALID_FUNCTION); 1766 } 1767 1768 break; 1769 } 1770 1771 /* Canonicalize File Name or Path */ 1772 case 0x60: 1773 { 1774 /* 1775 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm 1776 * for more information. 1777 */ 1778 1779 /* 1780 * We suppose that the DOS app gave to us a valid 1781 * 128-byte long buffer for the canonicalized name. 1782 */ 1783 DWORD dwRetVal = GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()), 1784 128, 1785 SEG_OFF_TO_PTR(getES(), getDI()), 1786 NULL); 1787 if (dwRetVal == 0) 1788 { 1789 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1790 setAX(GetLastError()); 1791 } 1792 else 1793 { 1794 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1795 setAX(0x0000); 1796 } 1797 1798 // FIXME: Convert the full path name into short version. 1799 // We cannot reliably use GetShortPathName, because it fails 1800 // if the path name given doesn't exist. However this DOS 1801 // function AH=60h should be able to work even for non-existing 1802 // path and file names. 1803 1804 break; 1805 } 1806 1807 /* Miscellaneous Internal Functions */ 1808 case 0x5D: 1809 { 1810 switch (getAL()) 1811 { 1812 /* Get Swappable Data Area */ 1813 case 0x06: 1814 { 1815 setDS(DOS_DATA_SEGMENT); 1816 setSI(DOS_DATA_OFFSET(Sda.ErrorMode)); 1817 setCX(sizeof(DOS_SDA)); 1818 setDX(FIELD_OFFSET(DOS_SDA, LastAX)); 1819 1820 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1821 break; 1822 } 1823 1824 default: // goto Default; 1825 { 1826 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n", 1827 getAH(), getAL()); 1828 } 1829 } 1830 1831 break; 1832 } 1833 1834 /* Extended Country Information */ 1835 case 0x65: 1836 { 1837 switch (getAL()) 1838 { 1839 case 0x01: case 0x02: case 0x03: 1840 case 0x04: case 0x05: case 0x06: 1841 case 0x07: 1842 { 1843 WORD BufferSize = getCX(); 1844 WORD ErrorCode; 1845 ErrorCode = DosGetCountryInfoEx(getAL(), 1846 getBX(), 1847 getDX(), 1848 (PDOS_COUNTRY_INFO_2)SEG_OFF_TO_PTR(getES(), getDI()), 1849 &BufferSize); 1850 if (ErrorCode == ERROR_SUCCESS) 1851 { 1852 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1853 setCX(BufferSize); 1854 } 1855 else 1856 { 1857 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1858 setAX(ErrorCode); 1859 } 1860 1861 break; 1862 } 1863 1864 /* Country-dependent Character Capitalization -- Character */ 1865 case 0x20: 1866 /* Country-dependent Filename Capitalization -- Character */ 1867 case 0xA0: 1868 { 1869 setDL(DosToUpper(getDL())); 1870 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1871 // setAX(ERROR_SUCCESS); 1872 break; 1873 } 1874 1875 /* Country-dependent Character Capitalization -- Counted ASCII String */ 1876 case 0x21: 1877 /* Country-dependent Filename Capitalization -- Counted ASCII String */ 1878 case 0xA1: 1879 { 1880 PCHAR Str = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX()); 1881 // FIXME: Check for NULL ptr!! 1882 DosToUpperStrN(Str, Str, getCX()); 1883 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1884 // setAX(ERROR_SUCCESS); 1885 break; 1886 } 1887 1888 /* Country-dependent Character Capitalization -- ASCIIZ String */ 1889 case 0x22: 1890 /* Country-dependent Filename Capitalization -- ASCIIZ String */ 1891 case 0xA2: 1892 { 1893 PSTR Str = (PSTR)SEG_OFF_TO_PTR(getDS(), getDX()); 1894 // FIXME: Check for NULL ptr!! 1895 DosToUpperStrZ(Str, Str); 1896 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1897 // setAX(ERROR_SUCCESS); 1898 break; 1899 } 1900 1901 /* Determine if Character represents YES/NO Response */ 1902 case 0x23: 1903 { 1904 setAX(DosIfCharYesNo(MAKEWORD(getDL(), getDH()))); 1905 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1906 break; 1907 } 1908 1909 default: // goto Default; 1910 { 1911 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n", 1912 getAH(), getAL()); 1913 } 1914 } 1915 1916 break; 1917 } 1918 1919 /* Set Handle Count */ 1920 case 0x67: 1921 { 1922 if (!DosResizeHandleTable(getBX())) 1923 { 1924 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1925 setAX(Sda->LastErrorCode); 1926 } 1927 else Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1928 1929 break; 1930 } 1931 1932 /* Commit File */ 1933 case 0x68: 1934 case 0x6A: 1935 { 1936 /* 1937 * Function 6Ah is identical to function 68h, 1938 * and sets AH to 68h if success. 1939 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm 1940 * for more information. 1941 */ 1942 setAH(0x68); 1943 1944 if (DosFlushFileBuffers(getBX())) 1945 { 1946 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1947 } 1948 else 1949 { 1950 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1951 setAX(GetLastError()); 1952 } 1953 1954 break; 1955 } 1956 1957 /* Extended Open/Create */ 1958 case 0x6C: 1959 { 1960 WORD FileHandle; 1961 WORD CreationStatus; 1962 WORD ErrorCode; 1963 1964 /* Check for AL == 00 */ 1965 if (getAL() != 0x00) 1966 { 1967 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1968 setAX(ERROR_INVALID_FUNCTION); 1969 break; 1970 } 1971 1972 /* 1973 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm 1974 * for the full detailed description. 1975 * 1976 * WARNING: BH contains some extended flags that are NOT SUPPORTED. 1977 */ 1978 1979 ErrorCode = DosCreateFileEx(&FileHandle, 1980 &CreationStatus, 1981 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getSI()), 1982 getBL(), 1983 getDL(), 1984 getCX()); 1985 1986 if (ErrorCode == ERROR_SUCCESS) 1987 { 1988 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 1989 setCX(CreationStatus); 1990 setAX(FileHandle); 1991 } 1992 else 1993 { 1994 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 1995 setAX(ErrorCode); 1996 } 1997 1998 break; 1999 } 2000 2001 /* Unsupported */ 2002 default: // Default: 2003 { 2004 DPRINT1("DOS Function INT 21h, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n", 2005 getAH(), getAL()); 2006 2007 setAL(0); // Some functions expect AL to be 0 when it's not supported. 2008 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 2009 } 2010 } 2011 2012 Sda->InDos--; 2013 } 2014 2015 VOID WINAPI DosBreakInterrupt(LPWORD Stack) 2016 { 2017 /* Set CF to terminate the running process */ 2018 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 2019 } 2020 2021 VOID WINAPI DosAbsoluteRead(LPWORD Stack) 2022 { 2023 /* 2024 * This call should leave the flags on the stack for some reason, 2025 * so move the stack by one word. 2026 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html 2027 */ 2028 Stack[STACK_INT_NUM] = Stack[STACK_IP]; 2029 Stack[STACK_IP] = Stack[STACK_CS]; 2030 Stack[STACK_CS] = Stack[STACK_FLAGS]; 2031 setSP(LOWORD(getSP() - 2)); 2032 2033 // TODO: NOT IMPLEMENTED; 2034 UNIMPLEMENTED; 2035 2036 /* General failure */ 2037 setAX(0x800C); 2038 Stack[STACK_FLAGS - 1] |= EMULATOR_FLAG_CF; 2039 } 2040 2041 VOID WINAPI DosAbsoluteWrite(LPWORD Stack) 2042 { 2043 /* 2044 * This call should leave the flags on the stack for some reason, 2045 * so move the stack by one word. 2046 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html 2047 */ 2048 Stack[STACK_INT_NUM] = Stack[STACK_IP]; 2049 Stack[STACK_IP] = Stack[STACK_CS]; 2050 Stack[STACK_CS] = Stack[STACK_FLAGS]; 2051 setSP(LOWORD(getSP() - 2)); 2052 2053 // TODO: NOT IMPLEMENTED; 2054 UNIMPLEMENTED; 2055 2056 /* General failure */ 2057 setAX(0x800C); 2058 Stack[STACK_FLAGS - 1] |= EMULATOR_FLAG_CF; 2059 } 2060 2061 VOID WINAPI DosInt27h(LPWORD Stack) 2062 { 2063 WORD KeepResident = (getDX() + 0x0F) >> 4; 2064 2065 /* Terminate and Stay Resident. CS must be the PSP segment. */ 2066 DPRINT1("Process going resident: %u paragraphs kept\n", KeepResident); 2067 DosTerminateProcess(Stack[STACK_CS], 0, KeepResident); 2068 } 2069 2070 VOID WINAPI DosIdle(LPWORD Stack) 2071 { 2072 /* 2073 * This will set the carry flag on the first call (to repeat the BOP), 2074 * and clear it in the next, so that exactly one HLT occurs. 2075 */ 2076 setCF(!getCF()); 2077 } 2078 2079 VOID WINAPI DosFastConOut(LPWORD Stack) 2080 { 2081 /* 2082 * This is the DOS 2+ Fast Console Output Interrupt. 2083 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh. 2084 * 2085 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm 2086 * for more information. 2087 */ 2088 2089 /* Save AX and BX */ 2090 USHORT AX = getAX(); 2091 USHORT BX = getBX(); 2092 2093 /* 2094 * Set the parameters: 2095 * AL contains the character to print (already set), 2096 * BL contains the character attribute, 2097 * BH contains the video page to use. 2098 */ 2099 setBL(DOS_CHAR_ATTRIBUTE); 2100 setBH(Bda->VideoPage); 2101 2102 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */ 2103 setAH(0x0E); 2104 Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT); 2105 2106 /* Restore AX and BX */ 2107 setBX(BX); 2108 setAX(AX); 2109 } 2110 2111 VOID WINAPI DosInt2Ah(LPWORD Stack) 2112 { 2113 DPRINT1("INT 2Ah, AX=%4xh called\n", getAX()); 2114 } 2115 2116 VOID WINAPI DosInt2Fh(LPWORD Stack) 2117 { 2118 switch (getAH()) 2119 { 2120 /* DOS 3+ Internal Utility Functions */ 2121 case 0x12: 2122 { 2123 DPRINT1("INT 2Fh, AX=%4xh DOS Internal Utility Function called\n", getAX()); 2124 2125 switch (getAL()) 2126 { 2127 /* Installation Check */ 2128 case 0x00: 2129 { 2130 setAL(0xFF); 2131 break; 2132 } 2133 2134 /* Get DOS Data Segment */ 2135 case 0x03: 2136 { 2137 setDS(DOS_DATA_SEGMENT); 2138 break; 2139 } 2140 2141 /* Compare FAR Pointers */ 2142 case 0x14: 2143 { 2144 PVOID PointerFromFarPointer1 = SEG_OFF_TO_PTR(getDS(), getSI()); 2145 PVOID PointerFromFarPointer2 = SEG_OFF_TO_PTR(getES(), getDI()); 2146 BOOLEAN AreEqual = (PointerFromFarPointer1 == PointerFromFarPointer2); 2147 2148 if (AreEqual) 2149 { 2150 Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF; 2151 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; 2152 } 2153 else 2154 { 2155 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF; 2156 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 2157 } 2158 break; 2159 } 2160 2161 /* Set DOS Version Number to return */ 2162 case 0x2F: 2163 { 2164 WORD DosVersion = getDX(); 2165 2166 // Special case: return the true DOS version when DX=00h 2167 if (DosVersion == 0x0000) 2168 DosData->DosVersion = DOS_VERSION; 2169 else 2170 DosData->DosVersion = DosVersion; 2171 2172 break; 2173 } 2174 } 2175 2176 break; 2177 } 2178 2179 /* Set Disk Interrupt Handler */ 2180 case 0x13: 2181 { 2182 /* Save the old values of PrevInt13 and RomBiosInt13 */ 2183 ULONG OldInt13 = BiosData->PrevInt13; 2184 ULONG OldBiosInt13 = BiosData->RomBiosInt13; 2185 2186 /* Set PrevInt13 and RomBiosInt13 to their new values */ 2187 BiosData->PrevInt13 = MAKELONG(getDX(), getDS()); 2188 BiosData->RomBiosInt13 = MAKELONG(getBX(), getES()); 2189 2190 /* Return in DS:DX the old value of PrevInt13 */ 2191 setDS(HIWORD(OldInt13)); 2192 setDX(LOWORD(OldInt13)); 2193 2194 /* Return in DS:DX the old value of RomBiosInt13 */ 2195 setES(HIWORD(OldBiosInt13)); 2196 setBX(LOWORD(OldBiosInt13)); 2197 2198 break; 2199 } 2200 2201 /* Mostly Windows 2.x/3.x/9x support */ 2202 case 0x16: 2203 { 2204 /* 2205 * AL=80h is DOS/Windows/DPMI "Release Current Virtual Machine Time-slice" 2206 * Just do nothing in this case. 2207 */ 2208 if (getAL() != 0x80) goto Default; 2209 break; 2210 } 2211 2212 /* Extended Memory Specification */ 2213 case 0x43: 2214 { 2215 DWORD DriverEntry; 2216 if (!XmsGetDriverEntry(&DriverEntry)) break; 2217 2218 switch (getAL()) 2219 { 2220 /* Installation Check */ 2221 case 0x00: 2222 { 2223 /* The driver is loaded */ 2224 setAL(0x80); 2225 break; 2226 } 2227 2228 /* Get Driver Address */ 2229 case 0x10: 2230 { 2231 setES(HIWORD(DriverEntry)); 2232 setBX(LOWORD(DriverEntry)); 2233 break; 2234 } 2235 2236 default: 2237 DPRINT1("Unknown DOS XMS Function: INT 2Fh, AH = 43h, AL = %02Xh\n", getAL()); 2238 break; 2239 } 2240 2241 break; 2242 } 2243 2244 default: Default: 2245 { 2246 DPRINT1("DOS Internal System Function INT 2Fh, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n", 2247 getAH(), getAL()); 2248 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; 2249 } 2250 } 2251 } 2252 2253 BOOLEAN DosKRNLInitialize(VOID) 2254 { 2255 UCHAR i; 2256 PDOS_SFT Sft; 2257 LPSTR Path; 2258 BOOLEAN Success = TRUE; 2259 DWORD dwRet; 2260 CHAR CurrentDirectory[MAX_PATH]; 2261 CHAR DosDirectory[DOS_DIR_LENGTH]; 2262 2263 static const BYTE NullDriverRoutine[] = 2264 { 2265 /* Strategy routine entry */ 2266 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE 2267 0xC7, 2268 0x47, 2269 FIELD_OFFSET(DOS_REQUEST_HEADER, Status), 2270 LOBYTE(DOS_DEVSTAT_DONE), 2271 HIBYTE(DOS_DEVSTAT_DONE), 2272 2273 /* Interrupt routine entry */ 2274 0xCB, // retf 2275 }; 2276 2277 /* Set the data segment */ 2278 setDS(DOS_DATA_SEGMENT); 2279 2280 /* Initialize the global DOS data area */ 2281 DosData = (PDOS_DATA)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT, 0x0000); 2282 RtlZeroMemory(DosData, sizeof(*DosData)); 2283 2284 /* Initialize the DOS stack */ 2285 setSS(DOS_DATA_SEGMENT); 2286 setSP(DOS_DATA_OFFSET(DosStack) + sizeof(DosData->DosStack) - sizeof(WORD)); 2287 2288 /* Initialize the list of lists */ 2289 SysVars = &DosData->SysVars; 2290 RtlZeroMemory(SysVars, sizeof(*SysVars)); 2291 SysVars->FirstSft = MAKELONG(DOS_DATA_OFFSET(Sft), DOS_DATA_SEGMENT); 2292 SysVars->CurrentDirs = MAKELONG(DOS_DATA_OFFSET(CurrentDirectories), 2293 DOS_DATA_SEGMENT); 2294 /* 2295 * The last drive can be redefined with the LASTDRIVE command. 2296 * At the moment, set the real maximum possible, 'Z'. 2297 */ 2298 SysVars->NumLocalDrives = 'Z' - 'A' + 1; // See #define NUM_DRIVES in dos.h 2299 2300 /* The boot drive is initialized to the %SYSTEMDRIVE% value */ 2301 // NOTE: Using the NtSystemRoot system variable might be OS-specific... 2302 SysVars->BootDrive = RtlUpcaseUnicodeChar(SharedUserData->NtSystemRoot[0]) - 'A' + 1; 2303 2304 /* Initialize the NUL device driver */ 2305 SysVars->NullDevice.Link = 0xFFFFFFFF; 2306 SysVars->NullDevice.DeviceAttributes = DOS_DEVATTR_NUL | DOS_DEVATTR_CHARACTER; 2307 // Offset from within the DOS data segment 2308 SysVars->NullDevice.StrategyRoutine = DOS_DATA_OFFSET(NullDriverRoutine); 2309 // Hardcoded to the RETF inside StrategyRoutine 2310 SysVars->NullDevice.InterruptRoutine = SysVars->NullDevice.StrategyRoutine + 6; 2311 RtlFillMemory(SysVars->NullDevice.DeviceName, 2312 sizeof(SysVars->NullDevice.DeviceName), 2313 ' '); 2314 RtlCopyMemory(SysVars->NullDevice.DeviceName, "NUL", strlen("NUL")); 2315 RtlCopyMemory(DosData->NullDriverRoutine, 2316 NullDriverRoutine, 2317 sizeof(NullDriverRoutine)); 2318 2319 /* Default DOS version to report */ 2320 DosData->DosVersion = DOS_VERSION; 2321 2322 /* Initialize the swappable data area */ 2323 Sda = &DosData->Sda; 2324 RtlZeroMemory(Sda, sizeof(*Sda)); 2325 2326 /* Get the current directory and convert it to a DOS path */ 2327 dwRet = GetCurrentDirectoryA(sizeof(CurrentDirectory), CurrentDirectory); 2328 if (dwRet == 0) 2329 { 2330 Success = FALSE; 2331 DPRINT1("GetCurrentDirectoryA failed (Error: %u)\n", GetLastError()); 2332 } 2333 else if (dwRet > sizeof(CurrentDirectory)) 2334 { 2335 Success = FALSE; 2336 DPRINT1("Current directory too long (%d > MAX_PATH) for GetCurrentDirectoryA\n", dwRet); 2337 } 2338 2339 if (Success) 2340 { 2341 dwRet = GetShortPathNameA(CurrentDirectory, DosDirectory, sizeof(DosDirectory)); 2342 if (dwRet == 0) 2343 { 2344 Success = FALSE; 2345 DPRINT1("GetShortPathNameA failed (Error: %u)\n", GetLastError()); 2346 } 2347 else if (dwRet > sizeof(DosDirectory)) 2348 { 2349 Success = FALSE; 2350 DPRINT1("Short path too long (%d > DOS_DIR_LENGTH) for GetShortPathNameA\n", dwRet); 2351 } 2352 } 2353 2354 if (!Success) 2355 { 2356 /* We failed, use the boot drive instead */ 2357 DosDirectory[0] = SysVars->BootDrive + 'A' - 1; 2358 DosDirectory[1] = ':'; 2359 DosDirectory[2] = '\\'; 2360 DosDirectory[3] = '\0'; 2361 } 2362 2363 /* Set the current drive */ 2364 Sda->CurrentDrive = RtlUpperChar(DosDirectory[0]) - 'A'; 2365 2366 /* Get the directory part of the path and set the current directory */ 2367 Path = strchr(DosDirectory, '\\'); 2368 if (Path != NULL) 2369 { 2370 Path++; // Skip the backslash 2371 strncpy(DosData->CurrentDirectories[Sda->CurrentDrive], Path, DOS_DIR_LENGTH); 2372 } 2373 else 2374 { 2375 DosData->CurrentDirectories[Sda->CurrentDrive][0] = '\0'; 2376 } 2377 2378 /* Set the current PSP to the system PSP */ 2379 Sda->CurrentPsp = SYSTEM_PSP; 2380 2381 /* Initialize the SFT */ 2382 Sft = (PDOS_SFT)FAR_POINTER(SysVars->FirstSft); 2383 Sft->Link = 0xFFFFFFFF; 2384 Sft->NumDescriptors = DOS_SFT_SIZE; 2385 2386 for (i = 0; i < Sft->NumDescriptors; i++) 2387 { 2388 /* Clear the file descriptor entry */ 2389 RtlZeroMemory(&Sft->FileDescriptors[i], sizeof(DOS_FILE_DESCRIPTOR)); 2390 } 2391 2392 /* Initialize memory management */ 2393 DosInitializeMemory(); 2394 2395 /* Initialize the callback context */ 2396 InitializeContext(&DosContext, DOS_CODE_SEGMENT, 0x0000); 2397 2398 /* Register the DOS 32-bit Interrupts */ 2399 RegisterDosInt32(0x20, DosInt20h ); 2400 RegisterDosInt32(0x21, DosInt21h ); 2401 // RegisterDosInt32(0x22, DosInt22h ); // Termination 2402 RegisterDosInt32(0x23, DosBreakInterrupt); // Ctrl-C / Ctrl-Break 2403 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error 2404 RegisterDosInt32(0x25, DosAbsoluteRead ); // Absolute Disk Read 2405 RegisterDosInt32(0x26, DosAbsoluteWrite ); // Absolute Disk Write 2406 RegisterDosInt32(0x27, DosInt27h ); // Terminate and Stay Resident 2407 RegisterDosInt32(0x28, DosIdle ); // DOS Idle Interrupt 2408 RegisterDosInt32(0x29, DosFastConOut ); // DOS 2+ Fast Console Output 2409 RegisterDosInt32(0x2F, DosInt2Fh ); // Multiplex Interrupt 2410 2411 /* Unimplemented DOS interrupts */ 2412 RegisterDosInt32(0x2A, DosInt2Ah); // DOS Critical Sections / Network 2413 // RegisterDosInt32(0x2E, NULL); // COMMAND.COM "Reload Transient" 2414 // COMMAND.COM adds support for INT 2Fh, AX=AE00h and AE01h "Installable Command - Installation Check & Execute" 2415 // COMMAND.COM adds support for INT 2Fh, AX=5500h "COMMAND.COM Interface" 2416 2417 /* Reserved DOS interrupts */ 2418 RegisterDosInt32(0x2B, NULL); 2419 RegisterDosInt32(0x2C, NULL); 2420 RegisterDosInt32(0x2D, NULL); 2421 2422 /* Initialize country data */ 2423 DosCountryInitialize(); 2424 2425 /* Load the CON driver */ 2426 ConDrvInitialize(); 2427 2428 /* Load the XMS driver (HIMEM) */ 2429 XmsInitialize(); 2430 2431 /* Load the EMS driver */ 2432 if (!EmsDrvInitialize(EMS_SEGMENT, EMS_TOTAL_PAGES)) 2433 { 2434 DosDisplayMessage("Could not initialize EMS. EMS will not be available.\n" 2435 "Page frame segment or number of EMS pages invalid.\n"); 2436 } 2437 2438 /* Finally initialize the UMBs */ 2439 DosInitializeUmb(); 2440 2441 return TRUE; 2442 } 2443 2444 /* EOF */ 2445