Migrate create/open file functions to winedos.
[wine/multimedia.git] / dlls / winedos / int21.c
blobf050be04bf006e346722a18e8c85badc743a7018
1 /*
2 * DOS interrupt 21h handler
4 * Copyright 1993, 1994 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 * Copyright 1997 Andreas Mohr
7 * Copyright 1998 Uwe Bonnes
8 * Copyright 1998, 1999 Ove Kaaven
9 * Copyright 2003 Thomas Mertes
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "config.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winternl.h"
31 #include "wine/winbase16.h"
32 #include "dosexe.h"
33 #include "miscemu.h"
34 #include "msdos.h"
35 #include "file.h"
36 #include "task.h"
37 #include "winerror.h"
38 #include "winuser.h"
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
43 * FIXME: Delete this reference when all int21 code has been moved to winedos.
45 extern void WINAPI INT_Int21Handler( CONTEXT86 *context );
48 * Forward declarations.
50 static BOOL INT21_RenameFile( CONTEXT86 *context );
52 WINE_DEFAULT_DEBUG_CHANNEL(int21);
55 #include "pshpack1.h"
58 * Structure for DOS data that can be accessed directly from applications.
59 * Real and protected mode pointers will be returned to this structure so
60 * the structure must be correctly packed.
62 typedef struct _INT21_HEAP {
63 WORD uppercase_size; /* Size of the following table in bytes */
64 BYTE uppercase_table[128]; /* Uppercase equivalents of chars from 0x80 to 0xff. */
66 WORD lowercase_size; /* Size of the following table in bytes */
67 BYTE lowercase_table[256]; /* Lowercase equivalents of chars from 0x00 to 0xff. */
69 WORD collating_size; /* Size of the following table in bytes */
70 BYTE collating_table[256]; /* Values used to sort characters from 0x00 to 0xff. */
72 WORD filename_size; /* Size of the following filename data in bytes */
73 BYTE filename_reserved1; /* 0x01 for MS-DOS 3.30-6.00 */
74 BYTE filename_lowest; /* Lowest permissible character value for filename */
75 BYTE filename_highest; /* Highest permissible character value for filename */
76 BYTE filename_reserved2; /* 0x00 for MS-DOS 3.30-6.00 */
77 BYTE filename_exclude_first; /* First illegal character in permissible range */
78 BYTE filename_exclude_last; /* Last illegal character in permissible range */
79 BYTE filename_reserved3; /* 0x02 for MS-DOS 3.30-6.00 */
80 BYTE filename_illegal_size; /* Number of terminators in the following table */
81 BYTE filename_illegal_table[16]; /* Characters which terminate a filename */
83 WORD dbcs_size; /* Number of valid ranges in the following table */
84 BYTE dbcs_table[16]; /* Start/end bytes for N ranges and 00/00 as terminator */
86 BYTE misc_indos; /* Interrupt 21 nesting flag */
87 } INT21_HEAP;
90 struct FCB {
91 BYTE drive_number;
92 CHAR file_name[8];
93 CHAR file_extension[3];
94 WORD current_block_number;
95 WORD logical_record_size;
96 DWORD file_size;
97 WORD date_of_last_write;
98 WORD time_of_last_write;
99 BYTE file_number;
100 BYTE attributes;
101 WORD starting_cluster;
102 WORD sequence_number;
103 BYTE file_attributes;
104 BYTE unused;
105 BYTE record_within_current_block;
106 BYTE random_access_record_number[4];
110 struct XFCB {
111 BYTE xfcb_signature;
112 BYTE reserved[5];
113 BYTE xfcb_file_attribute;
114 BYTE fcb[37];
117 #include "poppack.h"
120 /***********************************************************************
121 * INT21_GetCurrentDrive
123 * Return current drive using scheme (0=A:, 1=B:, 2=C:, ...) or
124 * MAX_DOS_DRIVES on error.
126 static BYTE INT21_GetCurrentDrive()
128 WCHAR current_directory[MAX_PATH];
130 if (!GetCurrentDirectoryW( MAX_PATH, current_directory ) ||
131 current_directory[1] != ':')
133 TRACE( "Failed to get current drive.\n" );
134 return MAX_DOS_DRIVES;
137 return toupperW( current_directory[0] ) - 'A';
141 /***********************************************************************
142 * INT21_MapDrive
144 * Convert drive number from scheme (0=default, 1=A:, 2=B:, ...) into
145 * scheme (0=A:, 1=B:, 2=C:, ...) or MAX_DOS_DRIVES on error.
147 static BYTE INT21_MapDrive( BYTE drive )
149 if (drive)
151 WCHAR drivespec[3] = {'A', ':', 0};
152 UINT drivetype;
154 drivespec[0] += drive - 1;
155 drivetype = GetDriveTypeW( drivespec );
157 if (drivetype == DRIVE_UNKNOWN || drivetype == DRIVE_NO_ROOT_DIR)
158 return MAX_DOS_DRIVES;
160 return drive - 1;
163 return INT21_GetCurrentDrive();
167 /***********************************************************************
168 * INT21_SetCurrentDrive
170 * Set current drive. Uses scheme (0=A:, 1=B:, 2=C:, ...).
172 static void INT21_SetCurrentDrive( BYTE drive )
174 WCHAR drivespec[3] = {'A', ':', 0};
176 drivespec[0] += drive;
178 if (!SetCurrentDirectoryW( drivespec ))
179 TRACE( "Failed to set current drive.\n" );
183 /***********************************************************************
184 * INT21_ReadChar
186 * Reads a character from the standard input.
187 * Extended keycodes will be returned as two separate characters.
189 static BOOL INT21_ReadChar( BYTE *input, CONTEXT86 *waitctx )
191 static BYTE pending_scan = 0;
193 if (pending_scan)
195 if (input)
196 *input = pending_scan;
197 if (waitctx)
198 pending_scan = 0;
199 return TRUE;
201 else
203 BYTE ascii;
204 BYTE scan;
205 if (!DOSVM_Int16ReadChar( &ascii, &scan, waitctx ))
206 return FALSE;
208 if (input)
209 *input = ascii;
210 if (waitctx && !ascii)
211 pending_scan = scan;
212 return TRUE;
217 /***********************************************************************
218 * INT21_GetSystemCountryCode
220 * Return DOS country code for default system locale.
222 static WORD INT21_GetSystemCountryCode( void )
225 * FIXME: Determine country code. We should probably use
226 * DOSCONF structure for that.
228 return GetSystemDefaultLangID();
232 /***********************************************************************
233 * INT21_FillCountryInformation
235 * Fill 34-byte buffer with country information data using
236 * default system locale.
238 static void INT21_FillCountryInformation( BYTE *buffer )
240 /* 00 - WORD: date format
241 * 00 = mm/dd/yy
242 * 01 = dd/mm/yy
243 * 02 = yy/mm/dd
245 *(WORD*)(buffer + 0) = 0; /* FIXME: Get from locale */
247 /* 02 - BYTE[5]: ASCIIZ currency symbol string */
248 buffer[2] = '$'; /* FIXME: Get from locale */
249 buffer[3] = 0;
251 /* 07 - BYTE[2]: ASCIIZ thousands separator */
252 buffer[7] = 0; /* FIXME: Get from locale */
253 buffer[8] = 0;
255 /* 09 - BYTE[2]: ASCIIZ decimal separator */
256 buffer[9] = '.'; /* FIXME: Get from locale */
257 buffer[10] = 0;
259 /* 11 - BYTE[2]: ASCIIZ date separator */
260 buffer[11] = '/'; /* FIXME: Get from locale */
261 buffer[12] = 0;
263 /* 13 - BYTE[2]: ASCIIZ time separator */
264 buffer[13] = ':'; /* FIXME: Get from locale */
265 buffer[14] = 0;
267 /* 15 - BYTE: Currency format
268 * bit 2 = set if currency symbol replaces decimal point
269 * bit 1 = number of spaces between value and currency symbol
270 * bit 0 = 0 if currency symbol precedes value
271 * 1 if currency symbol follows value
273 buffer[15] = 0; /* FIXME: Get from locale */
275 /* 16 - BYTE: Number of digits after decimal in currency */
276 buffer[16] = 0; /* FIXME: Get from locale */
278 /* 17 - BYTE: Time format
279 * bit 0 = 0 if 12-hour clock
280 * 1 if 24-hour clock
282 buffer[17] = 1; /* FIXME: Get from locale */
284 /* 18 - DWORD: Address of case map routine */
285 *(DWORD*)(buffer + 18) = 0; /* FIXME: ptr to case map routine */
287 /* 22 - BYTE[2]: ASCIIZ data-list separator */
288 buffer[22] = ','; /* FIXME: Get from locale */
289 buffer[23] = 0;
291 /* 24 - BYTE[10]: Reserved */
292 memset( buffer + 24, 0, 10 );
296 /***********************************************************************
297 * INT21_FillHeap
299 * Initialize DOS heap.
301 static void INT21_FillHeap( INT21_HEAP *heap )
303 static const char terminators[] = "\"\\./[]:|<>+=;,";
304 int i;
307 * Uppercase table.
309 heap->uppercase_size = 128;
310 for (i = 0; i < 128; i++)
311 heap->uppercase_table[i] = toupper( 128 + i );
314 * Lowercase table.
316 heap->lowercase_size = 256;
317 for (i = 0; i < 256; i++)
318 heap->lowercase_table[i] = tolower( i );
321 * Collating table.
323 heap->collating_size = 256;
324 for (i = 0; i < 256; i++)
325 heap->collating_table[i] = i;
328 * Filename table.
330 heap->filename_size = 8 + strlen(terminators);
331 heap->filename_illegal_size = strlen(terminators);
332 strcpy( heap->filename_illegal_table, terminators );
334 heap->filename_reserved1 = 0x01;
335 heap->filename_lowest = 0; /* FIXME: correct value? */
336 heap->filename_highest = 0xff; /* FIXME: correct value? */
337 heap->filename_reserved2 = 0x00;
338 heap->filename_exclude_first = 0x00; /* FIXME: correct value? */
339 heap->filename_exclude_last = 0x00; /* FIXME: correct value? */
340 heap->filename_reserved3 = 0x02;
343 * DBCS lead byte table. This table is empty.
345 heap->dbcs_size = 0;
346 memset( heap->dbcs_table, 0, sizeof(heap->dbcs_table) );
349 * Initialize InDos flag.
351 heap->misc_indos = 0;
355 /***********************************************************************
356 * INT21_GetHeapSelector
358 * Get segment/selector for DOS heap (INT21_HEAP).
359 * Creates and initializes heap on first call.
361 static WORD INT21_GetHeapSelector( CONTEXT86 *context )
363 static WORD heap_segment = 0;
364 static WORD heap_selector = 0;
365 static BOOL heap_initialized = FALSE;
367 if (!heap_initialized)
369 INT21_HEAP *ptr = DOSVM_AllocDataUMB( sizeof(INT21_HEAP),
370 &heap_segment,
371 &heap_selector );
372 INT21_FillHeap( ptr );
373 heap_initialized = TRUE;
376 if (!ISV86(context) && DOSVM_IsWin16())
377 return heap_selector;
378 else
379 return heap_segment;
383 /***********************************************************************
384 * INT21_GetCurrentDirectory
386 * Handler for:
387 * - function 0x47
388 * - subfunction 0x47 of function 0x71
390 static BOOL INT21_GetCurrentDirectory( CONTEXT86 *context, BOOL islong )
392 char *buffer = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Esi);
393 BYTE new_drive = INT21_MapDrive( DL_reg(context) );
394 BYTE old_drive = INT21_GetCurrentDrive();
395 WCHAR pathW[MAX_PATH];
396 char pathA[MAX_PATH];
397 WCHAR *ptr = pathW;
399 TRACE( "drive %d\n", DL_reg(context) );
401 if (new_drive == MAX_DOS_DRIVES)
403 SetLastError(ERROR_INVALID_DRIVE);
404 return FALSE;
408 * Grab current directory.
411 INT21_SetCurrentDrive( new_drive );
412 if (!GetCurrentDirectoryW( MAX_PATH, pathW ))
414 INT21_SetCurrentDrive( old_drive );
415 return FALSE;
417 INT21_SetCurrentDrive( old_drive );
420 * Convert into short format.
423 if (!islong)
425 DWORD result = GetShortPathNameW( pathW, pathW, MAX_PATH );
426 if (!result)
427 return FALSE;
428 if (result > MAX_PATH)
430 WARN( "Short path too long!\n" );
431 SetLastError(ERROR_NETWORK_BUSY); /* Internal Wine error. */
432 return FALSE;
437 * The returned pathname does not include
438 * the drive letter, colon or leading backslash.
441 if (ptr[0] == '\\')
444 * FIXME: We should probably just strip host part from name...
446 FIXME( "UNC names are not supported.\n" );
447 SetLastError(ERROR_NETWORK_BUSY); /* Internal Wine error. */
448 return FALSE;
450 else if (!ptr[0] || ptr[1] != ':' || ptr[2] != '\\')
452 WARN( "Path is neither UNC nor DOS path: %s\n",
453 wine_dbgstr_w(ptr) );
454 SetLastError(ERROR_NETWORK_BUSY); /* Internal Wine error. */
455 return FALSE;
457 else
459 /* Remove drive letter, colon and leading backslash. */
460 ptr += 3;
464 * Convert into OEM string.
467 if (!WideCharToMultiByte(CP_OEMCP, 0, ptr, -1, pathA,
468 MAX_PATH, NULL, NULL))
470 WARN( "Cannot convert path!\n" );
471 SetLastError(ERROR_NETWORK_BUSY); /* Internal Wine error. */
472 return FALSE;
476 * Success.
479 if (!islong)
481 /* Undocumented success code. */
482 SET_AX( context, 0x0100 );
484 /* Truncate buffer to 64 bytes. */
485 pathA[63] = 0;
488 TRACE( "%c:=%s\n", 'A' + new_drive, pathA );
490 strcpy( buffer, pathA );
491 return TRUE;
495 /***********************************************************************
496 * INT21_SetCurrentDirectory
498 * Handler for:
499 * - function 0x3b
500 * - subfunction 0x3b of function 0x71
502 static BOOL INT21_SetCurrentDirectory( CONTEXT86 *context )
504 WCHAR dirW[MAX_PATH];
505 char *dirA = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
506 BYTE drive = INT21_GetCurrentDrive();
507 BOOL result;
509 TRACE( "SET CURRENT DIRECTORY %s\n", dirA );
511 MultiByteToWideChar(CP_OEMCP, 0, dirA, -1, dirW, MAX_PATH);
512 result = SetCurrentDirectoryW( dirW );
514 /* This function must not change current drive. */
515 INT21_SetCurrentDrive( drive );
517 return result;
521 /***********************************************************************
522 * INT21_CreateFile
524 * Handler for:
525 * - function 0x3c
526 * - function 0x3d
527 * - function 0x5b
528 * - function 0x6c
529 * - subfunction 0x6c of function 0x71
531 static BOOL INT21_CreateFile( CONTEXT86 *context,
532 DWORD pathSegOff,
533 BOOL returnStatus,
534 WORD dosAccessShare,
535 BYTE dosAction )
537 WORD dosStatus;
538 char *pathA = CTX_SEG_OFF_TO_LIN(context, context->SegDs, pathSegOff);
539 WCHAR pathW[MAX_PATH];
540 DWORD winAccess;
541 DWORD winAttributes;
542 HANDLE winHandle;
543 DWORD winMode;
544 DWORD winSharing;
546 TRACE( "CreateFile called: function=%02x, action=%02x, access/share=%04x, "
547 "create flags=%04x, file=%s.\n",
548 AH_reg(context), dosAction, dosAccessShare, CX_reg(context), pathA );
551 * Application tried to create/open a file whose name
552 * ends with a backslash. This is not allowed.
554 * FIXME: This needs to be validated, especially the return value.
556 if (pathA[strlen(pathA) - 1] == '/')
558 SetLastError( ERROR_FILE_NOT_FOUND );
559 return FALSE;
563 * Convert DOS action flags into Win32 creation disposition parameter.
565 switch(dosAction)
567 case 0x01:
568 winMode = OPEN_EXISTING;
569 break;
570 case 0x02:
571 winMode = TRUNCATE_EXISTING;
572 break;
573 case 0x10:
574 winMode = CREATE_NEW;
575 break;
576 case 0x11:
577 winMode = OPEN_ALWAYS;
578 break;
579 case 0x12:
580 winMode = CREATE_ALWAYS;
581 break;
582 default:
583 SetLastError( ERROR_INVALID_PARAMETER );
584 return FALSE;
588 * Convert DOS access/share flags into Win32 desired access parameter.
590 switch(dosAccessShare & 0x07)
592 case OF_READ:
593 winAccess = GENERIC_READ;
594 break;
595 case OF_WRITE:
596 winAccess = GENERIC_WRITE;
597 break;
598 case OF_READWRITE:
599 winAccess = GENERIC_READ | GENERIC_WRITE;
600 break;
601 case 0x04:
603 * Read-only, do not modify file's last-access time (DOS7).
605 * FIXME: How to prevent modification of last-access time?
607 winAccess = GENERIC_READ;
608 break;
609 default:
610 winAccess = 0;
614 * Convert DOS access/share flags into Win32 share mode parameter.
616 switch(dosAccessShare & 0x70)
618 case OF_SHARE_EXCLUSIVE:
619 winSharing = 0;
620 break;
621 case OF_SHARE_DENY_WRITE:
622 winSharing = FILE_SHARE_READ;
623 break;
624 case OF_SHARE_DENY_READ:
625 winSharing = FILE_SHARE_WRITE;
626 break;
627 case OF_SHARE_DENY_NONE:
628 case OF_SHARE_COMPAT:
629 default:
630 winSharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
634 * FIXME: Bit (dosAccessShare & 0x80) represents inheritance.
635 * What to do with this bit?
636 * FIXME: Bits in the high byte of dosAccessShare are not supported.
637 * See both function 0x6c and subfunction 0x6c of function 0x71 for
638 * definition of these bits.
642 * Convert DOS create attributes into Win32 flags and attributes parameter.
644 if (winMode == OPEN_EXISTING || winMode == TRUNCATE_EXISTING)
646 winAttributes = 0;
648 else
650 WORD dosAttributes = CX_reg(context);
652 if (dosAttributes & FILE_ATTRIBUTE_LABEL)
655 * Application tried to create volume label entry.
656 * This is difficult to support so we do not allow it.
658 * FIXME: If volume does not already have a label,
659 * this function is supposed to succeed.
661 SetLastError( ERROR_ACCESS_DENIED );
662 return TRUE;
665 winAttributes = dosAttributes &
666 (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN |
667 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE);
671 * Open the file.
673 MultiByteToWideChar(CP_OEMCP, 0, pathA, -1, pathW, MAX_PATH);
675 winHandle = CreateFileW( pathW, winAccess, winSharing, NULL,
676 winMode, winAttributes, 0 );
678 if (winHandle == INVALID_HANDLE_VALUE)
679 return FALSE;
682 * Determine DOS file status.
684 * 1 = file opened
685 * 2 = file created
686 * 3 = file replaced
688 switch(winMode)
690 case OPEN_EXISTING:
691 dosStatus = 1;
692 break;
693 case TRUNCATE_EXISTING:
694 dosStatus = 3;
695 break;
696 case CREATE_NEW:
697 dosStatus = 2;
698 break;
699 case OPEN_ALWAYS:
700 dosStatus = (GetLastError() == ERROR_ALREADY_EXISTS) ? 1 : 2;
701 break;
702 case CREATE_ALWAYS:
703 dosStatus = (GetLastError() == ERROR_ALREADY_EXISTS) ? 3 : 2;
704 break;
705 default:
706 dosStatus = 0;
710 * Return DOS file handle and DOS status.
712 SET_AX( context, Win32HandleToDosFileHandle(winHandle) );
714 if (returnStatus)
715 SET_CX( context, dosStatus );
717 TRACE( "CreateFile finished: handle=%d, status=%d.\n",
718 AX_reg(context), dosStatus );
720 return TRUE;
724 /***********************************************************************
725 * INT21_BufferedInput
727 * Handler for function 0x0a and reading from console using
728 * function 0x3f.
730 * Reads a string of characters from standard input until
731 * enter key is pressed. Returns either number of characters
732 * read from console including terminating CR or
733 * zero if capacity was zero.
735 static WORD INT21_BufferedInput( CONTEXT86 *context, BYTE *ptr, WORD capacity )
737 BYTE length = 0;
740 * Return immediately if capacity is zero.
742 if (capacity == 0)
743 return 0;
745 while(TRUE)
747 BYTE ascii;
748 BYTE scan;
750 DOSVM_Int16ReadChar( &ascii, &scan, context );
752 if (ascii == '\r' || ascii == '\n')
755 * FIXME: What should be echoed here?
757 DOSVM_PutChar( '\r' );
758 DOSVM_PutChar( '\n' );
759 ptr[length] = '\r';
760 return length + 1;
764 * FIXME: This function is supposed to support
765 * DOS editing keys...
769 * If the buffer becomes filled to within one byte of
770 * capacity, DOS rejects all further characters up to,
771 * but not including, the terminating carriage return.
773 if (ascii != 0 && length < capacity-1)
775 DOSVM_PutChar( ascii );
776 ptr[length] = ascii;
777 length++;
783 /***********************************************************************
784 * INT21_GetCurrentDTA
786 static BYTE *INT21_GetCurrentDTA( CONTEXT86 *context )
788 TDB *pTask = GlobalLock16(GetCurrentTask());
790 /* FIXME: This assumes DTA was set correctly! */
791 return (BYTE *)CTX_SEG_OFF_TO_LIN( context, SELECTOROF(pTask->dta),
792 (DWORD)OFFSETOF(pTask->dta) );
796 /***********************************************************************
797 * INT21_OpenFileUsingFCB
799 * Handler for function 0x0f.
801 * PARAMS
802 * DX:DX [I/O] File control block (FCB or XFCB) of unopened file
804 * RETURNS (in AL)
805 * 0x00: successful
806 * 0xff: failed
808 * NOTES
809 * Opens a FCB file for read/write in compatibility mode. Upon calling
810 * the FCB must have the drive_number, file_name, and file_extension
811 * fields filled and all other bytes cleared.
813 static void INT21_OpenFileUsingFCB( CONTEXT86 *context )
815 struct FCB *fcb;
816 struct XFCB *xfcb;
817 char file_path[16];
818 char *pos;
819 HANDLE handle;
820 HFILE16 hfile16;
821 BY_HANDLE_FILE_INFORMATION info;
822 BYTE AL_result;
824 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
825 if (fcb->drive_number == 0xff) {
826 xfcb = (struct XFCB *) fcb;
827 fcb = (struct FCB *) xfcb->fcb;
828 } /* if */
830 AL_result = 0;
831 file_path[0] = 'A' + INT21_MapDrive( fcb->drive_number );
833 if (AL_result == 0) {
834 file_path[1] = ':';
835 pos = &file_path[2];
836 memcpy(pos, fcb->file_name, 8);
837 pos[8] = ' ';
838 pos[9] = '\0';
839 pos = strchr(pos, ' ');
840 *pos = '.';
841 pos++;
842 memcpy(pos, fcb->file_extension, 3);
843 pos[3] = ' ';
844 pos[4] = '\0';
845 pos = strchr(pos, ' ');
846 *pos = '\0';
848 handle = (HANDLE) _lopen(file_path, OF_READWRITE);
849 if (handle == INVALID_HANDLE_VALUE) {
850 TRACE("_lopen(\"%s\") failed: INVALID_HANDLE_VALUE\n", file_path);
851 AL_result = 0xff; /* failed */
852 } else {
853 hfile16 = Win32HandleToDosFileHandle(handle);
854 if (hfile16 == HFILE_ERROR16) {
855 TRACE("Win32HandleToDosFileHandle(%p) failed: HFILE_ERROR\n", handle);
856 CloseHandle(handle);
857 AL_result = 0xff; /* failed */
858 } else if (hfile16 > 255) {
859 TRACE("hfile16 (=%d) larger than 255 for \"%s\"\n", hfile16, file_path);
860 _lclose16(hfile16);
861 AL_result = 0xff; /* failed */
862 } else {
863 if (!GetFileInformationByHandle(handle, &info)) {
864 TRACE("GetFileInformationByHandle(%d, %p) for \"%s\" failed\n",
865 hfile16, handle, file_path);
866 _lclose16(hfile16);
867 AL_result = 0xff; /* failed */
868 } else {
869 fcb->drive_number = file_path[0] - 'A' + 1;
870 fcb->current_block_number = 0;
871 fcb->logical_record_size = 128;
872 fcb->file_size = info.nFileSizeLow;
873 FileTimeToDosDateTime(&info.ftLastWriteTime,
874 &fcb->date_of_last_write, &fcb->time_of_last_write);
875 fcb->file_number = hfile16;
876 fcb->attributes = 0xc2;
877 fcb->starting_cluster = 0; /* don't know correct init value */
878 fcb->sequence_number = 0; /* don't know correct init value */
879 fcb->file_attributes = info.dwFileAttributes;
880 /* The following fields are not initialized */
881 /* by the native function: */
882 /* unused */
883 /* record_within_current_block */
884 /* random_access_record_number */
886 TRACE("successful opened file \"%s\" as %d (handle %p)\n",
887 file_path, hfile16, handle);
888 AL_result = 0x00; /* successful */
889 } /* if */
890 } /* if */
891 } /* if */
892 } /* if */
893 SET_AL(context, AL_result);
897 /***********************************************************************
898 * INT21_CloseFileUsingFCB
900 * Handler for function 0x10.
902 * PARAMS
903 * DX:DX [I/O] File control block (FCB or XFCB) of open file
905 * RETURNS (in AL)
906 * 0x00: successful
907 * 0xff: failed
909 * NOTES
910 * Closes a FCB file.
912 static void INT21_CloseFileUsingFCB( CONTEXT86 *context )
914 struct FCB *fcb;
915 struct XFCB *xfcb;
916 BYTE AL_result;
918 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
919 if (fcb->drive_number == 0xff) {
920 xfcb = (struct XFCB *) fcb;
921 fcb = (struct FCB *) xfcb->fcb;
922 } /* if */
924 if (_lclose16((HFILE16) fcb->file_number) != 0) {
925 TRACE("_lclose16(%d) failed\n", fcb->file_number);
926 AL_result = 0xff; /* failed */
927 } else {
928 TRACE("successful closed file %d\n", fcb->file_number);
929 AL_result = 0x00; /* successful */
930 } /* if */
931 SET_AL(context, AL_result);
935 /***********************************************************************
936 * INT21_SequentialReadFromFCB
938 * Handler for function 0x14.
940 * PARAMS
941 * DX:DX [I/O] File control block (FCB or XFCB) of open file
943 * RETURNS (in AL)
944 * 0: successful
945 * 1: end of file, no data read
946 * 2: segment wrap in DTA, no data read (not returned now)
947 * 3: end of file, partial record read
949 * NOTES
950 * Reads a record with the size FCB->logical_record_size from the FCB
951 * to the disk transfer area. The position of the record is specified
952 * with FCB->current_block_number and FCB->record_within_current_block.
953 * Then FCB->current_block_number and FCB->record_within_current_block
954 * are updated to point to the next record. If a partial record is
955 * read, it is filled with zeros up to the FCB->logical_record_size.
957 static void INT21_SequentialReadFromFCB( CONTEXT86 *context )
959 struct FCB *fcb;
960 struct XFCB *xfcb;
961 HANDLE handle;
962 DWORD record_number;
963 long position;
964 BYTE *disk_transfer_area;
965 UINT bytes_read;
966 BYTE AL_result;
968 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
969 if (fcb->drive_number == 0xff) {
970 xfcb = (struct XFCB *) fcb;
971 fcb = (struct FCB *) xfcb->fcb;
972 } /* if */
974 handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
975 if (handle == INVALID_HANDLE_VALUE) {
976 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
977 fcb->file_number);
978 AL_result = 0x01; /* end of file, no data read */
979 } else {
980 record_number = 128 * fcb->current_block_number + fcb->record_within_current_block;
981 position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
982 if (position != record_number * fcb->logical_record_size) {
983 TRACE("seek(%d, %ld, 0) failed with %ld\n",
984 fcb->file_number, record_number * fcb->logical_record_size, position);
985 AL_result = 0x01; /* end of file, no data read */
986 } else {
987 disk_transfer_area = INT21_GetCurrentDTA(context);
988 bytes_read = _lread((HFILE) handle, disk_transfer_area, fcb->logical_record_size);
989 if (bytes_read != fcb->logical_record_size) {
990 TRACE("_lread(%d, %p, %d) failed with %d\n",
991 fcb->file_number, disk_transfer_area, fcb->logical_record_size, bytes_read);
992 if (bytes_read == 0) {
993 AL_result = 0x01; /* end of file, no data read */
994 } else {
995 memset(&disk_transfer_area[bytes_read], 0, fcb->logical_record_size - bytes_read);
996 AL_result = 0x03; /* end of file, partial record read */
997 } /* if */
998 } else {
999 TRACE("successful read %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
1000 bytes_read, record_number, position, fcb->file_number, handle);
1001 AL_result = 0x00; /* successful */
1002 } /* if */
1003 } /* if */
1004 } /* if */
1005 if (AL_result == 0x00 || AL_result == 0x03) {
1006 if (fcb->record_within_current_block == 127) {
1007 fcb->record_within_current_block = 0;
1008 fcb->current_block_number++;
1009 } else {
1010 fcb->record_within_current_block++;
1011 } /* if */
1012 } /* if */
1013 SET_AL(context, AL_result);
1017 /***********************************************************************
1018 * INT21_SequentialWriteToFCB
1020 * Handler for function 0x15.
1022 * PARAMS
1023 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1025 * RETURNS (in AL)
1026 * 0: successful
1027 * 1: disk full
1028 * 2: segment wrap in DTA (not returned now)
1030 * NOTES
1031 * Writes a record with the size FCB->logical_record_size from the disk
1032 * transfer area to the FCB. The position of the record is specified
1033 * with FCB->current_block_number and FCB->record_within_current_block.
1034 * Then FCB->current_block_number and FCB->record_within_current_block
1035 * are updated to point to the next record.
1037 static void INT21_SequentialWriteToFCB( CONTEXT86 *context )
1039 struct FCB *fcb;
1040 struct XFCB *xfcb;
1041 HANDLE handle;
1042 DWORD record_number;
1043 long position;
1044 BYTE *disk_transfer_area;
1045 UINT bytes_written;
1046 BYTE AL_result;
1048 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
1049 if (fcb->drive_number == 0xff) {
1050 xfcb = (struct XFCB *) fcb;
1051 fcb = (struct FCB *) xfcb->fcb;
1052 } /* if */
1054 handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
1055 if (handle == INVALID_HANDLE_VALUE) {
1056 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1057 fcb->file_number);
1058 AL_result = 0x01; /* disk full */
1059 } else {
1060 record_number = 128 * fcb->current_block_number + fcb->record_within_current_block;
1061 position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
1062 if (position != record_number * fcb->logical_record_size) {
1063 TRACE("seek(%d, %ld, 0) failed with %ld\n",
1064 fcb->file_number, record_number * fcb->logical_record_size, position);
1065 AL_result = 0x01; /* disk full */
1066 } else {
1067 disk_transfer_area = INT21_GetCurrentDTA(context);
1068 bytes_written = _lwrite((HFILE) handle, disk_transfer_area, fcb->logical_record_size);
1069 if (bytes_written != fcb->logical_record_size) {
1070 TRACE("_lwrite(%d, %p, %d) failed with %d\n",
1071 fcb->file_number, disk_transfer_area, fcb->logical_record_size, bytes_written);
1072 AL_result = 0x01; /* disk full */
1073 } else {
1074 TRACE("successful written %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
1075 bytes_written, record_number, position, fcb->file_number, handle);
1076 AL_result = 0x00; /* successful */
1077 } /* if */
1078 } /* if */
1079 } /* if */
1080 if (AL_result == 0x00) {
1081 if (fcb->record_within_current_block == 127) {
1082 fcb->record_within_current_block = 0;
1083 fcb->current_block_number++;
1084 } else {
1085 fcb->record_within_current_block++;
1086 } /* if */
1087 } /* if */
1088 SET_AL(context, AL_result);
1092 /***********************************************************************
1093 * INT21_ReadRandomRecordFromFCB
1095 * Handler for function 0x21.
1097 * PARAMS
1098 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1100 * RETURNS (in AL)
1101 * 0: successful
1102 * 1: end of file, no data read
1103 * 2: segment wrap in DTA, no data read (not returned now)
1104 * 3: end of file, partial record read
1106 * NOTES
1107 * Reads a record with the size FCB->logical_record_size from
1108 * the FCB to the disk transfer area. The position of the record
1109 * is specified with FCB->random_access_record_number. The
1110 * FCB->random_access_record_number is not updated. If a partial record
1111 * is read, it is filled with zeros up to the FCB->logical_record_size.
1113 static void INT21_ReadRandomRecordFromFCB( CONTEXT86 *context )
1115 struct FCB *fcb;
1116 struct XFCB *xfcb;
1117 HANDLE handle;
1118 DWORD record_number;
1119 long position;
1120 BYTE *disk_transfer_area;
1121 UINT bytes_read;
1122 BYTE AL_result;
1124 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
1125 if (fcb->drive_number == 0xff) {
1126 xfcb = (struct XFCB *) fcb;
1127 fcb = (struct FCB *) xfcb->fcb;
1128 } /* if */
1130 memcpy(&record_number, fcb->random_access_record_number, 4);
1131 handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
1132 if (handle == INVALID_HANDLE_VALUE) {
1133 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1134 fcb->file_number);
1135 AL_result = 0x01; /* end of file, no data read */
1136 } else {
1137 position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
1138 if (position != record_number * fcb->logical_record_size) {
1139 TRACE("seek(%d, %ld, 0) failed with %ld\n",
1140 fcb->file_number, record_number * fcb->logical_record_size, position);
1141 AL_result = 0x01; /* end of file, no data read */
1142 } else {
1143 disk_transfer_area = INT21_GetCurrentDTA(context);
1144 bytes_read = _lread((HFILE) handle, disk_transfer_area, fcb->logical_record_size);
1145 if (bytes_read != fcb->logical_record_size) {
1146 TRACE("_lread(%d, %p, %d) failed with %d\n",
1147 fcb->file_number, disk_transfer_area, fcb->logical_record_size, bytes_read);
1148 if (bytes_read == 0) {
1149 AL_result = 0x01; /* end of file, no data read */
1150 } else {
1151 memset(&disk_transfer_area[bytes_read], 0, fcb->logical_record_size - bytes_read);
1152 AL_result = 0x03; /* end of file, partial record read */
1153 } /* if */
1154 } else {
1155 TRACE("successful read %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
1156 bytes_read, record_number, position, fcb->file_number, handle);
1157 AL_result = 0x00; /* successful */
1158 } /* if */
1159 } /* if */
1160 } /* if */
1161 fcb->current_block_number = record_number / 128;
1162 fcb->record_within_current_block = record_number % 128;
1163 SET_AL(context, AL_result);
1167 /***********************************************************************
1168 * INT21_WriteRandomRecordToFCB
1170 * Handler for function 0x22.
1172 * PARAMS
1173 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1175 * RETURNS (in AL)
1176 * 0: successful
1177 * 1: disk full
1178 * 2: segment wrap in DTA (not returned now)
1180 * NOTES
1181 * Writes a record with the size FCB->logical_record_size from
1182 * the disk transfer area to the FCB. The position of the record
1183 * is specified with FCB->random_access_record_number. The
1184 * FCB->random_access_record_number is not updated.
1186 static void INT21_WriteRandomRecordToFCB( CONTEXT86 *context )
1188 struct FCB *fcb;
1189 struct XFCB *xfcb;
1190 HANDLE handle;
1191 DWORD record_number;
1192 long position;
1193 BYTE *disk_transfer_area;
1194 UINT bytes_written;
1195 BYTE AL_result;
1197 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
1198 if (fcb->drive_number == 0xff) {
1199 xfcb = (struct XFCB *) fcb;
1200 fcb = (struct FCB *) xfcb->fcb;
1201 } /* if */
1203 memcpy(&record_number, fcb->random_access_record_number, 4);
1204 handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
1205 if (handle == INVALID_HANDLE_VALUE) {
1206 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1207 fcb->file_number);
1208 AL_result = 0x01; /* disk full */
1209 } else {
1210 position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
1211 if (position != record_number * fcb->logical_record_size) {
1212 TRACE("seek(%d, %ld, 0) failed with %ld\n",
1213 fcb->file_number, record_number * fcb->logical_record_size, position);
1214 AL_result = 0x01; /* disk full */
1215 } else {
1216 disk_transfer_area = INT21_GetCurrentDTA(context);
1217 bytes_written = _lwrite((HFILE) handle, disk_transfer_area, fcb->logical_record_size);
1218 if (bytes_written != fcb->logical_record_size) {
1219 TRACE("_lwrite(%d, %p, %d) failed with %d\n",
1220 fcb->file_number, disk_transfer_area, fcb->logical_record_size, bytes_written);
1221 AL_result = 0x01; /* disk full */
1222 } else {
1223 TRACE("successful written %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
1224 bytes_written, record_number, position, fcb->file_number, handle);
1225 AL_result = 0x00; /* successful */
1226 } /* if */
1227 } /* if */
1228 } /* if */
1229 fcb->current_block_number = record_number / 128;
1230 fcb->record_within_current_block = record_number % 128;
1231 SET_AL(context, AL_result);
1235 /***********************************************************************
1236 * INT21_RandomBlockReadFromFCB
1238 * Handler for function 0x27.
1240 * PARAMS
1241 * CX [I/O] Number of records to read
1242 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1244 * RETURNS (in AL)
1245 * 0: successful
1246 * 1: end of file, no data read
1247 * 2: segment wrap in DTA, no data read (not returned now)
1248 * 3: end of file, partial record read
1250 * NOTES
1251 * Reads several records with the size FCB->logical_record_size from
1252 * the FCB to the disk transfer area. The number of records to be
1253 * read is specified in the CX register. The position of the first
1254 * record is specified with FCB->random_access_record_number. The
1255 * FCB->random_access_record_number, the FCB->current_block_number
1256 * and FCB->record_within_current_block are updated to point to the
1257 * next record after the records read. If a partial record is read,
1258 * it is filled with zeros up to the FCB->logical_record_size. The
1259 * CX register is set to the number of successfully read records.
1261 static void INT21_RandomBlockReadFromFCB( CONTEXT86 *context )
1263 struct FCB *fcb;
1264 struct XFCB *xfcb;
1265 HANDLE handle;
1266 DWORD record_number;
1267 long position;
1268 BYTE *disk_transfer_area;
1269 UINT records_requested;
1270 UINT bytes_requested;
1271 UINT bytes_read;
1272 UINT records_read;
1273 BYTE AL_result;
1275 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
1276 if (fcb->drive_number == 0xff) {
1277 xfcb = (struct XFCB *) fcb;
1278 fcb = (struct FCB *) xfcb->fcb;
1279 } /* if */
1281 memcpy(&record_number, fcb->random_access_record_number, 4);
1282 handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
1283 if (handle == INVALID_HANDLE_VALUE) {
1284 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1285 fcb->file_number);
1286 records_read = 0;
1287 AL_result = 0x01; /* end of file, no data read */
1288 } else {
1289 position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
1290 if (position != record_number * fcb->logical_record_size) {
1291 TRACE("seek(%d, %ld, 0) failed with %ld\n",
1292 fcb->file_number, record_number * fcb->logical_record_size, position);
1293 records_read = 0;
1294 AL_result = 0x01; /* end of file, no data read */
1295 } else {
1296 disk_transfer_area = INT21_GetCurrentDTA(context);
1297 records_requested = CX_reg(context);
1298 bytes_requested = (UINT) records_requested * fcb->logical_record_size;
1299 bytes_read = _lread((HFILE) handle, disk_transfer_area, bytes_requested);
1300 if (bytes_read != bytes_requested) {
1301 TRACE("_lread(%d, %p, %d) failed with %d\n",
1302 fcb->file_number, disk_transfer_area, bytes_requested, bytes_read);
1303 records_read = bytes_read / fcb->logical_record_size;
1304 if (bytes_read % fcb->logical_record_size == 0) {
1305 AL_result = 0x01; /* end of file, no data read */
1306 } else {
1307 records_read++;
1308 memset(&disk_transfer_area[bytes_read], 0, records_read * fcb->logical_record_size - bytes_read);
1309 AL_result = 0x03; /* end of file, partial record read */
1310 } /* if */
1311 } else {
1312 TRACE("successful read %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
1313 bytes_read, record_number, position, fcb->file_number, handle);
1314 records_read = records_requested;
1315 AL_result = 0x00; /* successful */
1316 } /* if */
1317 } /* if */
1318 } /* if */
1319 record_number += records_read;
1320 memcpy(fcb->random_access_record_number, &record_number, 4);
1321 fcb->current_block_number = record_number / 128;
1322 fcb->record_within_current_block = record_number % 128;
1323 SET_CX(context, records_read);
1324 SET_AL(context, AL_result);
1328 /***********************************************************************
1329 * INT21_RandomBlockWriteToFCB
1331 * Handler for function 0x28.
1333 * PARAMS
1334 * CX [I/O] Number of records to write
1335 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1337 * RETURNS (in AL)
1338 * 0: successful
1339 * 1: disk full
1340 * 2: segment wrap in DTA (not returned now)
1342 * NOTES
1343 * Writes several records with the size FCB->logical_record_size from
1344 * the disk transfer area to the FCB. The number of records to be
1345 * written is specified in the CX register. The position of the first
1346 * record is specified with FCB->random_access_record_number. The
1347 * FCB->random_access_record_number, the FCB->current_block_number
1348 * and FCB->record_within_current_block are updated to point to the
1349 * next record after the records written. The CX register is set to
1350 * the number of successfully written records.
1352 static void INT21_RandomBlockWriteToFCB( CONTEXT86 *context )
1354 struct FCB *fcb;
1355 struct XFCB *xfcb;
1356 HANDLE handle;
1357 DWORD record_number;
1358 long position;
1359 BYTE *disk_transfer_area;
1360 UINT records_requested;
1361 UINT bytes_requested;
1362 UINT bytes_written;
1363 UINT records_written;
1364 BYTE AL_result;
1366 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
1367 if (fcb->drive_number == 0xff) {
1368 xfcb = (struct XFCB *) fcb;
1369 fcb = (struct FCB *) xfcb->fcb;
1370 } /* if */
1372 memcpy(&record_number, fcb->random_access_record_number, 4);
1373 handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
1374 if (handle == INVALID_HANDLE_VALUE) {
1375 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1376 fcb->file_number);
1377 records_written = 0;
1378 AL_result = 0x01; /* disk full */
1379 } else {
1380 position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
1381 if (position != record_number * fcb->logical_record_size) {
1382 TRACE("seek(%d, %ld, 0) failed with %ld\n",
1383 fcb->file_number, record_number * fcb->logical_record_size, position);
1384 records_written = 0;
1385 AL_result = 0x01; /* disk full */
1386 } else {
1387 disk_transfer_area = INT21_GetCurrentDTA(context);
1388 records_requested = CX_reg(context);
1389 bytes_requested = (UINT) records_requested * fcb->logical_record_size;
1390 bytes_written = _lwrite((HFILE) handle, disk_transfer_area, bytes_requested);
1391 if (bytes_written != bytes_requested) {
1392 TRACE("_lwrite(%d, %p, %d) failed with %d\n",
1393 fcb->file_number, disk_transfer_area, bytes_requested, bytes_written);
1394 records_written = bytes_written / fcb->logical_record_size;
1395 AL_result = 0x01; /* disk full */
1396 } else {
1397 TRACE("successful write %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
1398 bytes_written, record_number, position, fcb->file_number, handle);
1399 records_written = records_requested;
1400 AL_result = 0x00; /* successful */
1401 } /* if */
1402 } /* if */
1403 } /* if */
1404 record_number += records_written;
1405 memcpy(fcb->random_access_record_number, &record_number, 4);
1406 fcb->current_block_number = record_number / 128;
1407 fcb->record_within_current_block = record_number % 128;
1408 SET_CX(context, records_written);
1409 SET_AL(context, AL_result);
1413 /***********************************************************************
1414 * INT21_CreateDirectory
1416 * Handler for:
1417 * - function 0x39
1418 * - subfunction 0x39 of function 0x71
1419 * - subfunction 0xff of function 0x43 (CL == 0x39)
1421 static BOOL INT21_CreateDirectory( CONTEXT86 *context )
1423 WCHAR dirW[MAX_PATH];
1424 char *dirA = CTX_SEG_OFF_TO_LIN(context,
1425 context->SegDs,
1426 context->Edx);
1428 TRACE( "CREATE DIRECTORY %s\n", dirA );
1430 MultiByteToWideChar(CP_OEMCP, 0, dirA, -1, dirW, MAX_PATH);
1432 if (CreateDirectoryW(dirW, NULL))
1433 return TRUE;
1436 * FIXME: CreateDirectory's LastErrors will clash with the ones
1437 * used by DOS. AH=39 only returns 3 (path not found) and
1438 * 5 (access denied), while CreateDirectory return several
1439 * ones. Remap some of them. -Marcus
1441 switch (GetLastError())
1443 case ERROR_ALREADY_EXISTS:
1444 case ERROR_FILENAME_EXCED_RANGE:
1445 case ERROR_DISK_FULL:
1446 SetLastError(ERROR_ACCESS_DENIED);
1447 break;
1448 default:
1449 break;
1452 return FALSE;
1456 /***********************************************************************
1457 * INT21_ExtendedCountryInformation
1459 * Handler for function 0x65.
1461 static void INT21_ExtendedCountryInformation( CONTEXT86 *context )
1463 BYTE *dataptr = CTX_SEG_OFF_TO_LIN( context, context->SegEs, context->Edi );
1465 TRACE( "GET EXTENDED COUNTRY INFORMATION, subfunction %02x\n",
1466 AL_reg(context) );
1469 * Check subfunctions that are passed country and code page.
1471 if (AL_reg(context) >= 0x01 && AL_reg(context) <= 0x07)
1473 WORD country = DX_reg(context);
1474 WORD codepage = BX_reg(context);
1476 if (country != 0xffff && country != INT21_GetSystemCountryCode())
1477 FIXME( "Requested info on non-default country %04x\n", country );
1479 if (codepage != 0xffff && codepage != GetOEMCP())
1480 FIXME( "Requested info on non-default code page %04x\n", codepage );
1483 switch (AL_reg(context)) {
1484 case 0x00: /* SET GENERAL INTERNATIONALIZATION INFO */
1485 INT_BARF( context, 0x21 );
1486 SET_CFLAG( context );
1487 break;
1489 case 0x01: /* GET GENERAL INTERNATIONALIZATION INFO */
1490 TRACE( "Get general internationalization info\n" );
1491 dataptr[0] = 0x01; /* Info ID */
1492 *(WORD*)(dataptr+1) = 38; /* Size of the following info */
1493 *(WORD*)(dataptr+3) = INT21_GetSystemCountryCode(); /* Country ID */
1494 *(WORD*)(dataptr+5) = GetOEMCP(); /* Code page */
1495 INT21_FillCountryInformation( dataptr + 7 );
1496 SET_CX( context, 41 ); /* Size of returned info */
1497 break;
1499 case 0x02: /* GET POINTER TO UPPERCASE TABLE */
1500 case 0x04: /* GET POINTER TO FILENAME UPPERCASE TABLE */
1501 TRACE( "Get pointer to uppercase table\n" );
1502 dataptr[0] = AL_reg(context); /* Info ID */
1503 *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1504 offsetof(INT21_HEAP, uppercase_size) );
1505 SET_CX( context, 5 ); /* Size of returned info */
1506 break;
1508 case 0x03: /* GET POINTER TO LOWERCASE TABLE */
1509 TRACE( "Get pointer to lowercase table\n" );
1510 dataptr[0] = 0x03; /* Info ID */
1511 *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1512 offsetof(INT21_HEAP, lowercase_size) );
1513 SET_CX( context, 5 ); /* Size of returned info */
1514 break;
1516 case 0x05: /* GET POINTER TO FILENAME TERMINATOR TABLE */
1517 TRACE("Get pointer to filename terminator table\n");
1518 dataptr[0] = 0x05; /* Info ID */
1519 *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1520 offsetof(INT21_HEAP, filename_size) );
1521 SET_CX( context, 5 ); /* Size of returned info */
1522 break;
1524 case 0x06: /* GET POINTER TO COLLATING SEQUENCE TABLE */
1525 TRACE("Get pointer to collating sequence table\n");
1526 dataptr[0] = 0x06; /* Info ID */
1527 *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1528 offsetof(INT21_HEAP, collating_size) );
1529 SET_CX( context, 5 ); /* Size of returned info */
1530 break;
1532 case 0x07: /* GET POINTER TO DBCS LEAD BYTE TABLE */
1533 TRACE("Get pointer to DBCS lead byte table\n");
1534 dataptr[0] = 0x07; /* Info ID */
1535 *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1536 offsetof(INT21_HEAP, dbcs_size) );
1537 SET_CX( context, 5 ); /* Size of returned info */
1538 break;
1540 case 0x20: /* CAPITALIZE CHARACTER */
1541 case 0xa0: /* CAPITALIZE FILENAME CHARACTER */
1542 TRACE("Convert char to uppercase\n");
1543 SET_DL( context, toupper(DL_reg(context)) );
1544 break;
1546 case 0x21: /* CAPITALIZE STRING */
1547 case 0xa1: /* CAPITALIZE COUNTED FILENAME STRING */
1548 TRACE("Convert string to uppercase with length\n");
1550 char *ptr = (char *)CTX_SEG_OFF_TO_LIN( context,
1551 context->SegDs,
1552 context->Edx );
1553 WORD len = CX_reg(context);
1554 while (len--) { *ptr = toupper(*ptr); ptr++; }
1556 break;
1558 case 0x22: /* CAPITALIZE ASCIIZ STRING */
1559 case 0xa2: /* CAPITALIZE ASCIIZ FILENAME */
1560 TRACE("Convert ASCIIZ string to uppercase\n");
1561 _strupr( (LPSTR)CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx) );
1562 break;
1564 case 0x23: /* DETERMINE IF CHARACTER REPRESENTS YES/NO RESPONSE */
1565 INT_BARF( context, 0x21 );
1566 SET_CFLAG( context );
1567 break;
1569 default:
1570 INT_BARF( context, 0x21 );
1571 SET_CFLAG(context);
1572 break;
1577 /***********************************************************************
1578 * INT21_FileAttributes
1580 * Handler for:
1581 * - function 0x43
1582 * - subfunction 0x43 of function 0x71
1584 static BOOL INT21_FileAttributes( CONTEXT86 *context,
1585 BYTE subfunction,
1586 BOOL islong )
1588 WCHAR fileW[MAX_PATH];
1589 char *fileA = CTX_SEG_OFF_TO_LIN(context,
1590 context->SegDs,
1591 context->Edx);
1592 HANDLE handle;
1593 BOOL status;
1594 FILETIME filetime;
1595 DWORD result;
1596 WORD date, time;
1598 switch (subfunction)
1600 case 0x00: /* GET FILE ATTRIBUTES */
1601 TRACE( "GET FILE ATTRIBUTES for %s\n", fileA );
1602 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1604 result = GetFileAttributesW( fileW );
1605 if (result == -1)
1606 return FALSE;
1607 else
1609 SET_CX( context, (WORD)result );
1610 if (!islong)
1611 SET_AX( context, (WORD)result ); /* DR DOS */
1613 break;
1615 case 0x01: /* SET FILE ATTRIBUTES */
1616 TRACE( "SET FILE ATTRIBUTES 0x%02x for %s\n",
1617 CX_reg(context), fileA );
1618 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1620 if (!SetFileAttributesW( fileW, CX_reg(context) ))
1621 return FALSE;
1622 break;
1624 case 0x02: /* GET COMPRESSED FILE SIZE */
1625 TRACE( "GET COMPRESSED FILE SIZE for %s\n", fileA );
1626 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1628 result = GetCompressedFileSizeW( fileW, NULL );
1629 if (result == INVALID_FILE_SIZE)
1630 return FALSE;
1631 else
1633 SET_AX( context, LOWORD(result) );
1634 SET_DX( context, HIWORD(result) );
1636 break;
1638 case 0x03: /* SET FILE LAST-WRITTEN DATE AND TIME */
1639 if (!islong)
1640 INT_BARF( context, 0x21 );
1641 else
1643 TRACE( "SET FILE LAST-WRITTEN DATE AND TIME, file %s\n", fileA );
1644 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1646 handle = CreateFileW( fileW, GENERIC_WRITE,
1647 FILE_SHARE_READ | FILE_SHARE_WRITE,
1648 NULL, OPEN_EXISTING, 0, 0 );
1649 if (handle == INVALID_HANDLE_VALUE)
1650 return FALSE;
1652 DosDateTimeToFileTime( DI_reg(context),
1653 CX_reg(context),
1654 &filetime );
1655 status = SetFileTime( handle, NULL, NULL, &filetime );
1657 CloseHandle( handle );
1658 return status;
1660 break;
1662 case 0x04: /* GET FILE LAST-WRITTEN DATE AND TIME */
1663 if (!islong)
1664 INT_BARF( context, 0x21 );
1665 else
1667 TRACE( "GET FILE LAST-WRITTEN DATE AND TIME, file %s\n", fileA );
1668 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1670 handle = CreateFileW( fileW, GENERIC_READ,
1671 FILE_SHARE_READ | FILE_SHARE_WRITE,
1672 NULL, OPEN_EXISTING, 0, 0 );
1673 if (handle == INVALID_HANDLE_VALUE)
1674 return FALSE;
1676 status = GetFileTime( handle, NULL, NULL, &filetime );
1677 if (status)
1679 FileTimeToDosDateTime( &filetime, &date, &time );
1680 SET_DI( context, date );
1681 SET_CX( context, time );
1684 CloseHandle( handle );
1685 return status;
1687 break;
1689 case 0x05: /* SET FILE LAST ACCESS DATE */
1690 if (!islong)
1691 INT_BARF( context, 0x21 );
1692 else
1694 TRACE( "SET FILE LAST ACCESS DATE, file %s\n", fileA );
1695 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1697 handle = CreateFileW( fileW, GENERIC_WRITE,
1698 FILE_SHARE_READ | FILE_SHARE_WRITE,
1699 NULL, OPEN_EXISTING, 0, 0 );
1700 if (handle == INVALID_HANDLE_VALUE)
1701 return FALSE;
1703 DosDateTimeToFileTime( DI_reg(context),
1705 &filetime );
1706 status = SetFileTime( handle, NULL, &filetime, NULL );
1708 CloseHandle( handle );
1709 return status;
1711 break;
1713 case 0x06: /* GET FILE LAST ACCESS DATE */
1714 if (!islong)
1715 INT_BARF( context, 0x21 );
1716 else
1718 TRACE( "GET FILE LAST ACCESS DATE, file %s\n", fileA );
1719 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1721 handle = CreateFileW( fileW, GENERIC_READ,
1722 FILE_SHARE_READ | FILE_SHARE_WRITE,
1723 NULL, OPEN_EXISTING, 0, 0 );
1724 if (handle == INVALID_HANDLE_VALUE)
1725 return FALSE;
1727 status = GetFileTime( handle, NULL, &filetime, NULL );
1728 if (status)
1730 FileTimeToDosDateTime( &filetime, &date, NULL );
1731 SET_DI( context, date );
1734 CloseHandle( handle );
1735 return status;
1737 break;
1739 case 0x07: /* SET FILE CREATION DATE AND TIME */
1740 if (!islong)
1741 INT_BARF( context, 0x21 );
1742 else
1744 TRACE( "SET FILE CREATION DATE AND TIME, file %s\n", fileA );
1746 handle = CreateFileW( fileW, GENERIC_WRITE,
1747 FILE_SHARE_READ | FILE_SHARE_WRITE,
1748 NULL, OPEN_EXISTING, 0, 0 );
1749 if (handle == INVALID_HANDLE_VALUE)
1750 return FALSE;
1753 * FIXME: SI has number of 10-millisecond units past time in CX.
1755 DosDateTimeToFileTime( DI_reg(context),
1756 CX_reg(context),
1757 &filetime );
1758 status = SetFileTime( handle, &filetime, NULL, NULL );
1760 CloseHandle( handle );
1761 return status;
1763 break;
1765 case 0x08: /* GET FILE CREATION DATE AND TIME */
1766 if (!islong)
1767 INT_BARF( context, 0x21 );
1768 else
1770 TRACE( "GET FILE CREATION DATE AND TIME, handle %d\n",
1771 BX_reg(context) );
1773 handle = CreateFileW( fileW, GENERIC_READ,
1774 FILE_SHARE_READ | FILE_SHARE_WRITE,
1775 NULL, OPEN_EXISTING, 0, 0 );
1776 if (handle == INVALID_HANDLE_VALUE)
1777 return FALSE;
1779 status = GetFileTime( handle, &filetime, NULL, NULL );
1780 if (status)
1782 FileTimeToDosDateTime( &filetime, &date, &time );
1783 SET_DI( context, date );
1784 SET_CX( context, time );
1786 * FIXME: SI has number of 10-millisecond units past
1787 * time in CX.
1789 SET_SI( context, 0 );
1792 CloseHandle(handle);
1793 return status;
1795 break;
1797 case 0xff: /* EXTENDED-LENGTH FILENAME OPERATIONS */
1798 if (islong || context->Ebp != 0x5053)
1799 INT_BARF( context, 0x21 );
1800 else
1802 switch(CL_reg(context))
1804 case 0x39:
1805 if (!INT21_CreateDirectory( context ))
1806 return FALSE;
1807 break;
1809 case 0x56:
1810 if (!INT21_RenameFile( context ))
1811 return FALSE;
1812 break;
1814 default:
1815 INT_BARF( context, 0x21 );
1818 break;
1820 default:
1821 INT_BARF( context, 0x21 );
1824 return TRUE;
1828 /***********************************************************************
1829 * INT21_FileDateTime
1831 * Handler for function 0x57.
1833 static BOOL INT21_FileDateTime( CONTEXT86 *context )
1835 HANDLE handle = DosFileHandleToWin32Handle(BX_reg(context));
1836 FILETIME filetime;
1837 WORD date, time;
1839 switch (AL_reg(context)) {
1840 case 0x00: /* Get last-written stamp */
1841 TRACE( "GET FILE LAST-WRITTEN DATE AND TIME, handle %d\n",
1842 BX_reg(context) );
1844 if (!GetFileTime( handle, NULL, NULL, &filetime ))
1845 return FALSE;
1846 FileTimeToDosDateTime( &filetime, &date, &time );
1847 SET_DX( context, date );
1848 SET_CX( context, time );
1849 break;
1852 case 0x01: /* Set last-written stamp */
1853 TRACE( "SET FILE LAST-WRITTEN DATE AND TIME, handle %d\n",
1854 BX_reg(context) );
1856 DosDateTimeToFileTime( DX_reg(context),
1857 CX_reg(context),
1858 &filetime );
1859 if (!SetFileTime( handle, NULL, NULL, &filetime ))
1860 return FALSE;
1861 break;
1864 case 0x04: /* Get last access stamp, DOS 7 */
1865 TRACE( "GET FILE LAST ACCESS DATE AND TIME, handle %d\n",
1866 BX_reg(context) );
1868 if (!GetFileTime( handle, NULL, &filetime, NULL ))
1869 return FALSE;
1870 FileTimeToDosDateTime( &filetime, &date, &time );
1871 SET_DX( context, date );
1872 SET_CX( context, time );
1873 break;
1876 case 0x05: /* Set last access stamp, DOS 7 */
1877 TRACE( "SET FILE LAST ACCESS DATE AND TIME, handle %d\n",
1878 BX_reg(context) );
1880 DosDateTimeToFileTime( DX_reg(context),
1881 CX_reg(context),
1882 &filetime );
1883 if (!SetFileTime( handle, NULL, &filetime, NULL ))
1884 return FALSE;
1885 break;
1888 case 0x06: /* Get creation stamp, DOS 7 */
1889 TRACE( "GET FILE CREATION DATE AND TIME, handle %d\n",
1890 BX_reg(context) );
1892 if (!GetFileTime( handle, &filetime, NULL, NULL ))
1893 return FALSE;
1894 FileTimeToDosDateTime( &filetime, &date, &time );
1895 SET_DX( context, date );
1896 SET_CX( context, time );
1898 * FIXME: SI has number of 10-millisecond units past time in CX.
1900 SET_SI( context, 0 );
1901 break;
1904 case 0x07: /* Set creation stamp, DOS 7 */
1905 TRACE( "SET FILE CREATION DATE AND TIME, handle %d\n",
1906 BX_reg(context) );
1909 * FIXME: SI has number of 10-millisecond units past time in CX.
1911 DosDateTimeToFileTime( DX_reg(context),
1912 CX_reg(context),
1913 &filetime );
1914 if (!SetFileTime( handle, &filetime, NULL, NULL ))
1915 return FALSE;
1916 break;
1919 default:
1920 INT_BARF( context, 0x21 );
1921 break;
1924 return TRUE;
1928 /***********************************************************************
1929 * INT21_GetPSP
1931 * Handler for functions 0x51 and 0x62.
1933 static void INT21_GetPSP( CONTEXT86 *context )
1935 TRACE( "GET CURRENT PSP ADDRESS (%02x)\n", AH_reg(context) );
1938 * FIXME: should we return the original DOS PSP upon
1939 * Windows startup ?
1941 if (!ISV86(context) && DOSVM_IsWin16())
1942 SET_BX( context, LOWORD(GetCurrentPDB16()) );
1943 else
1944 SET_BX( context, DOSVM_psp );
1948 /***********************************************************************
1949 * INT21_Ioctl_Block
1951 * Handler for block device IOCTLs.
1953 static void INT21_Ioctl_Block( CONTEXT86 *context )
1955 BYTE *dataptr;
1956 BYTE drive = INT21_MapDrive( BL_reg(context) );
1957 WCHAR drivespec[4] = {'A', ':', '\\', 0};
1958 UINT drivetype;
1960 drivespec[0] += drive;
1961 drivetype = GetDriveTypeW( drivespec );
1963 if (drivetype == DRIVE_UNKNOWN || drivetype == DRIVE_NO_ROOT_DIR)
1965 TRACE( "IOCTL - SUBFUNCTION %d - INVALID DRIVE %c:\n",
1966 AL_reg(context), 'A' + drive );
1967 SetLastError( ERROR_INVALID_DRIVE );
1968 SET_AX( context, ERROR_INVALID_DRIVE );
1969 SET_CFLAG( context );
1970 return;
1973 switch (AL_reg(context))
1975 case 0x04: /* READ FROM BLOCK DEVICE CONTROL CHANNEL */
1976 case 0x05: /* WRITE TO BLOCK DEVICE CONTROL CHANNEL */
1977 INT_BARF( context, 0x21 );
1978 break;
1980 case 0x08: /* CHECK IF BLOCK DEVICE REMOVABLE */
1981 TRACE( "IOCTL - CHECK IF BLOCK DEVICE REMOVABLE - %c:\n",
1982 'A' + drive );
1984 if (drivetype == DRIVE_REMOVABLE)
1985 SET_AX( context, 0 ); /* removable */
1986 else
1987 SET_AX( context, 1 ); /* not removable */
1988 break;
1990 case 0x09: /* CHECK IF BLOCK DEVICE REMOTE */
1991 TRACE( "IOCTL - CHECK IF BLOCK DEVICE REMOTE - %c:\n",
1992 'A' + drive );
1994 if (drivetype == DRIVE_REMOTE)
1995 SET_DX( context, (1<<9) | (1<<12) ); /* remote + no direct IO */
1996 else
1997 SET_DX( context, 0 ); /* FIXME: use driver attr here */
1998 break;
2000 case 0x0d: /* GENERIC BLOCK DEVICE REQUEST */
2001 /* Get pointer to IOCTL parameter block. */
2002 dataptr = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
2004 switch (CX_reg(context))
2006 case 0x0841: /* write logical device track */
2007 TRACE( "GENERIC IOCTL - Write logical device track - %c:\n",
2008 'A' + drive);
2010 WORD head = *(WORD *)dataptr+1;
2011 WORD cyl = *(WORD *)dataptr+3;
2012 WORD sect = *(WORD *)dataptr+5;
2013 WORD nrsect = *(WORD *)dataptr+7;
2014 BYTE *data = (BYTE *)dataptr+9; /* FIXME: is this correct? */
2016 if (!DOSVM_RawWrite(drive, head*cyl*sect, nrsect, data, FALSE))
2018 SET_AX( context, ERROR_WRITE_FAULT );
2019 SET_CFLAG(context);
2022 break;
2024 case 0x084a: /* lock logical volume */
2025 TRACE( "GENERIC IOCTL - Lock logical volume, level %d mode %d - %c:\n",
2026 BH_reg(context), DX_reg(context), 'A' + drive );
2027 break;
2029 case 0x0860: /* get device parameters */
2030 INT_Int21Handler( context );
2031 break;
2033 case 0x0861: /* read logical device track */
2034 TRACE( "GENERIC IOCTL - Read logical device track - %c:\n",
2035 'A' + drive);
2037 WORD head = *(WORD *)dataptr+1;
2038 WORD cyl = *(WORD *)dataptr+3;
2039 WORD sect = *(WORD *)dataptr+5;
2040 WORD nrsect = *(WORD *)dataptr+7;
2041 BYTE *data = (BYTE *)dataptr+9; /* FIXME: is this correct? */
2043 if (!DOSVM_RawRead(drive, head*cyl*sect, nrsect, data, FALSE))
2045 SET_AX( context, ERROR_READ_FAULT );
2046 SET_CFLAG(context);
2049 break;
2051 case 0x0866: /* get volume serial number */
2052 INT_Int21Handler( context );
2053 break;
2055 case 0x086a: /* unlock logical volume */
2056 TRACE( "GENERIC IOCTL - Logical volume unlocked - %c:\n",
2057 'A' + drive );
2058 break;
2060 case 0x086f: /* get drive map information */
2061 INT_Int21Handler( context );
2062 break;
2064 case 0x0872:
2065 INT_Int21Handler( context );
2066 break;
2068 default:
2069 INT_BARF( context, 0x21 );
2071 break;
2073 case 0x0e: /* GET LOGICAL DRIVE MAP */
2074 TRACE( "IOCTL - GET LOGICAL DRIVE MAP - %c:\n",
2075 'A' + drive );
2076 /* FIXME: this is not correct if drive has mappings */
2077 SET_AL( context, 0 ); /* drive has no mapping */
2078 break;
2080 case 0x0f: /* SET LOGICAL DRIVE MAP */
2081 INT_Int21Handler( context );
2082 break;
2084 case 0x11: /* QUERY GENERIC IOCTL CAPABILITY */
2085 default:
2086 INT_BARF( context, 0x21 );
2091 /***********************************************************************
2092 * INT21_Ioctl_Char
2094 * Handler for character device IOCTLs.
2096 static void INT21_Ioctl_Char( CONTEXT86 *context )
2098 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
2099 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
2101 HANDLE handle = DosFileHandleToWin32Handle(BX_reg(context));
2102 const DOS_DEVICE *dev = DOSFS_GetDeviceByHandle(handle);
2104 if (dev && !strcmpiW( dev->name, emmxxxx0W ))
2106 EMS_Ioctl_Handler(context);
2107 return;
2110 if (dev && !strcmpiW( dev->name, scsimgrW ) && AL_reg(context) == 2)
2112 DOSVM_ASPIHandler(context);
2113 return;
2116 switch (AL_reg(context))
2118 case 0x00: /* GET DEVICE INFORMATION */
2119 TRACE( "IOCTL - GET DEVICE INFORMATION - %d\n", BX_reg(context) );
2120 if (dev)
2123 * Returns attribute word in DX:
2124 * Bit 14 - Device driver can process IOCTL requests.
2125 * Bit 13 - Output until busy supported.
2126 * Bit 11 - Driver supports OPEN/CLOSE calls.
2127 * Bit 8 - Unknown.
2128 * Bit 7 - Set (indicates device).
2129 * Bit 6 - EOF on input.
2130 * Bit 5 - Raw (binary) mode.
2131 * Bit 4 - Device is special (uses int29).
2132 * Bit 3 - Clock device.
2133 * Bit 2 - NUL device.
2134 * Bit 1 - Standard output.
2135 * Bit 0 - Standard input.
2137 SET_DX( context, dev->flags );
2139 else
2142 * Returns attribute word in DX:
2143 * Bit 15 - File is remote.
2144 * Bit 14 - Don't set file date/time on closing.
2145 * Bit 11 - Media not removable.
2146 * Bit 8 - Generate int24 if no disk space on write
2147 * or read past end of file
2148 * Bit 7 - Clear (indicates file).
2149 * Bit 6 - File has not been written.
2150 * Bit 5..0 - Drive number (0=A:,...)
2152 * FIXME: Should check if file is on remote or removable drive.
2153 * FIXME: Should use drive file is located on (and not current).
2155 SET_DX( context, 0x0140 + INT21_GetCurrentDrive() );
2157 break;
2159 case 0x01: /* SET DEVICE INFORMATION */
2160 case 0x02: /* READ FROM CHARACTER DEVICE CONTROL CHANNEL */
2161 case 0x03: /* WRITE TO CHARACTER DEVICE CONTROL CHANNEL */
2162 case 0x06: /* GET INPUT STATUS */
2163 case 0x07: /* GET OUTPUT STATUS */
2164 INT_BARF( context, 0x21 );
2165 break;
2167 case 0x0a: /* CHECK IF HANDLE IS REMOTE */
2168 TRACE( "IOCTL - CHECK IF HANDLE IS REMOTE - %d\n", BX_reg(context) );
2170 * Returns attribute word in DX:
2171 * Bit 15 - Set if remote.
2172 * Bit 14 - Set if date/time not set on close.
2174 * FIXME: Should check if file is on remote drive.
2176 SET_DX( context, 0 );
2177 break;
2179 case 0x0c: /* GENERIC CHARACTER DEVICE REQUEST */
2180 case 0x10: /* QUERY GENERIC IOCTL CAPABILITY */
2181 default:
2182 INT_BARF( context, 0x21 );
2187 /***********************************************************************
2188 * INT21_Ioctl
2190 * Handler for function 0x44.
2192 static void INT21_Ioctl( CONTEXT86 *context )
2194 switch (AL_reg(context))
2196 case 0x00:
2197 case 0x01:
2198 case 0x02:
2199 case 0x03:
2200 INT21_Ioctl_Char( context );
2201 break;
2203 case 0x04:
2204 case 0x05:
2205 INT21_Ioctl_Block( context );
2206 break;
2208 case 0x06:
2209 case 0x07:
2210 INT21_Ioctl_Char( context );
2211 break;
2213 case 0x08:
2214 case 0x09:
2215 INT21_Ioctl_Block( context );
2216 break;
2218 case 0x0a:
2219 INT21_Ioctl_Char( context );
2220 break;
2222 case 0x0b: /* SET SHARING RETRY COUNT */
2223 TRACE( "SET SHARING RETRY COUNT: Pause %d, retries %d.\n",
2224 CX_reg(context), DX_reg(context) );
2225 if (!CX_reg(context))
2227 SET_AX( context, 1 );
2228 SET_CFLAG( context );
2230 else
2232 DOSMEM_LOL()->sharing_retry_delay = CX_reg(context);
2233 if (DX_reg(context))
2234 DOSMEM_LOL()->sharing_retry_count = DX_reg(context);
2235 RESET_CFLAG( context );
2237 break;
2239 case 0x0c:
2240 INT21_Ioctl_Char( context );
2241 break;
2243 case 0x0d:
2244 case 0x0e:
2245 case 0x0f:
2246 INT21_Ioctl_Block( context );
2247 break;
2249 case 0x10:
2250 INT21_Ioctl_Char( context );
2251 break;
2253 case 0x11:
2254 INT21_Ioctl_Block( context );
2255 break;
2257 case 0x12: /* DR DOS - DETERMINE DOS TYPE (OBSOLETE FUNCTION) */
2258 TRACE( "DR DOS - DETERMINE DOS TYPE (OBSOLETE FUNCTION)\n" );
2259 SET_CFLAG(context); /* Error / This is not DR DOS. */
2260 SET_AX( context, 0x0001 ); /* Invalid function */
2261 break;
2263 case 0x52: /* DR DOS - DETERMINE DOS TYPE */
2264 TRACE( "DR DOS - DETERMINE DOS TYPE\n" );
2265 SET_CFLAG(context); /* Error / This is not DR DOS. */
2266 SET_AX( context, 0x0001 ); /* Invalid function */
2267 break;
2269 case 0xe0: /* Sun PC-NFS API */
2270 TRACE( "Sun PC-NFS API\n" );
2271 /* not installed */
2272 break;
2274 default:
2275 INT_BARF( context, 0x21 );
2280 /***********************************************************************
2281 * INT21_LongFilename
2283 * Handler for function 0x71.
2285 static void INT21_LongFilename( CONTEXT86 *context )
2287 BOOL bSetDOSExtendedError = FALSE;
2289 if (HIBYTE(HIWORD(GetVersion16())) < 0x07)
2291 TRACE( "LONG FILENAME - functions supported only under DOS7\n" );
2292 SET_CFLAG( context );
2293 SET_AL( context, 0 );
2294 return;
2297 switch (AL_reg(context))
2299 case 0x0d: /* RESET DRIVE */
2300 INT_BARF( context, 0x21 );
2301 break;
2303 case 0x39: /* LONG FILENAME - MAKE DIRECTORY */
2304 if (!INT21_CreateDirectory( context ))
2305 bSetDOSExtendedError = TRUE;
2306 break;
2308 case 0x3a: /* LONG FILENAME - REMOVE DIRECTORY */
2310 WCHAR dirW[MAX_PATH];
2311 char *dirA = CTX_SEG_OFF_TO_LIN(context,
2312 context->SegDs, context->Edx);
2314 TRACE( "LONG FILENAME - REMOVE DIRECTORY %s\n", dirA );
2315 MultiByteToWideChar(CP_OEMCP, 0, dirA, -1, dirW, MAX_PATH);
2317 if (!RemoveDirectoryW( dirW ))
2318 bSetDOSExtendedError = TRUE;
2320 break;
2322 case 0x3b: /* LONG FILENAME - CHANGE DIRECTORY */
2323 if (!INT21_SetCurrentDirectory( context ))
2324 bSetDOSExtendedError = TRUE;
2325 break;
2327 case 0x41: /* LONG FILENAME - DELETE FILE */
2329 WCHAR fileW[MAX_PATH];
2330 char *fileA = CTX_SEG_OFF_TO_LIN(context,
2331 context->SegDs, context->Edx);
2333 TRACE( "LONG FILENAME - DELETE FILE %s\n", fileA );
2334 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
2336 if (!DeleteFileW( fileW ))
2337 bSetDOSExtendedError = TRUE;
2339 break;
2341 case 0x43: /* LONG FILENAME - EXTENDED GET/SET FILE ATTRIBUTES */
2342 if (!INT21_FileAttributes( context, BL_reg(context), TRUE ))
2343 bSetDOSExtendedError = TRUE;
2344 break;
2346 case 0x47: /* LONG FILENAME - GET CURRENT DIRECTORY */
2347 if (!INT21_GetCurrentDirectory( context, TRUE ))
2348 bSetDOSExtendedError = TRUE;
2349 break;
2351 case 0x4e: /* LONG FILENAME - FIND FIRST MATCHING FILE */
2352 case 0x4f: /* LONG FILENAME - FIND NEXT MATCHING FILE */
2353 INT_Int21Handler( context );
2354 break;
2356 case 0x56: /* LONG FILENAME - RENAME FILE */
2357 if (!INT21_RenameFile(context))
2358 bSetDOSExtendedError = TRUE;
2359 break;
2361 case 0x60: /* LONG FILENAME - CONVERT PATH */
2362 INT_Int21Handler( context );
2363 break;
2365 case 0x6c: /* LONG FILENAME - CREATE OR OPEN FILE */
2366 if (!INT21_CreateFile( context, context->Esi, TRUE,
2367 BX_reg(context), DL_reg(context) ))
2368 bSetDOSExtendedError = TRUE;
2369 break;
2371 case 0xa0: /* LONG FILENAME - GET VOLUME INFORMATION */
2372 case 0xa1: /* LONG FILENAME - "FindClose" - TERMINATE DIRECTORY SEARCH */
2373 INT_Int21Handler( context );
2374 break;
2376 case 0xa6: /* LONG FILENAME - GET FILE INFO BY HANDLE */
2378 HANDLE handle = DosFileHandleToWin32Handle(BX_reg(context));
2379 BY_HANDLE_FILE_INFORMATION *info =
2380 CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
2382 TRACE( "LONG FILENAME - GET FILE INFO BY HANDLE\n" );
2384 if (!GetFileInformationByHandle(handle, info))
2385 bSetDOSExtendedError = TRUE;
2387 break;
2389 case 0xa7: /* LONG FILENAME - CONVERT TIME */
2390 switch (BL_reg(context))
2392 case 0x00: /* FILE TIME TO DOS TIME */
2394 WORD date, time;
2395 FILETIME *filetime = CTX_SEG_OFF_TO_LIN(context,
2396 context->SegDs,
2397 context->Esi);
2399 TRACE( "LONG FILENAME - FILE TIME TO DOS TIME\n" );
2401 FileTimeToDosDateTime( filetime, &date, &time );
2403 SET_DX( context, date );
2404 SET_CX( context, time );
2407 * FIXME: BH has number of 10-millisecond units
2408 * past time in CX.
2410 SET_BH( context, 0 );
2412 break;
2414 case 0x01: /* DOS TIME TO FILE TIME */
2416 FILETIME *filetime = CTX_SEG_OFF_TO_LIN(context,
2417 context->SegEs,
2418 context->Edi);
2420 TRACE( "LONG FILENAME - DOS TIME TO FILE TIME\n" );
2423 * FIXME: BH has number of 10-millisecond units
2424 * past time in CX.
2426 DosDateTimeToFileTime( DX_reg(context), CX_reg(context),
2427 filetime );
2429 break;
2431 default:
2432 INT_BARF( context, 0x21 );
2433 break;
2435 break;
2437 case 0xa8: /* LONG FILENAME - GENERATE SHORT FILENAME */
2438 case 0xa9: /* LONG FILENAME - SERVER CREATE OR OPEN FILE */
2439 case 0xaa: /* LONG FILENAME - SUBST */
2440 default:
2441 INT_BARF( context, 0x21 );
2444 if (bSetDOSExtendedError)
2446 SET_AX( context, GetLastError() );
2447 SET_CFLAG( context );
2452 /***********************************************************************
2453 * INT21_RenameFile
2455 * Handler for:
2456 * - function 0x56
2457 * - subfunction 0x56 of function 0x71
2458 * - subfunction 0xff of function 0x43 (CL == 0x56)
2460 static BOOL INT21_RenameFile( CONTEXT86 *context )
2462 WCHAR fromW[MAX_PATH];
2463 WCHAR toW[MAX_PATH];
2464 char *fromA = CTX_SEG_OFF_TO_LIN(context,
2465 context->SegDs,context->Edx);
2466 char *toA = CTX_SEG_OFF_TO_LIN(context,
2467 context->SegEs,context->Edi);
2469 TRACE( "RENAME FILE %s to %s\n", fromA, toA );
2470 MultiByteToWideChar(CP_OEMCP, 0, fromA, -1, fromW, MAX_PATH);
2471 MultiByteToWideChar(CP_OEMCP, 0, toA, -1, toW, MAX_PATH);
2473 return MoveFileW( fromW, toW );
2477 /***********************************************************************
2478 * INT21_GetExtendedError
2480 static void INT21_GetExtendedError( CONTEXT86 *context )
2482 BYTE class, action, locus;
2483 WORD error = GetLastError();
2485 switch(error)
2487 case ERROR_SUCCESS:
2488 class = action = locus = 0;
2489 break;
2490 case ERROR_DIR_NOT_EMPTY:
2491 class = EC_Exists;
2492 action = SA_Ignore;
2493 locus = EL_Disk;
2494 break;
2495 case ERROR_ACCESS_DENIED:
2496 class = EC_AccessDenied;
2497 action = SA_Abort;
2498 locus = EL_Disk;
2499 break;
2500 case ERROR_CANNOT_MAKE:
2501 class = EC_AccessDenied;
2502 action = SA_Abort;
2503 locus = EL_Unknown;
2504 break;
2505 case ERROR_DISK_FULL:
2506 case ERROR_HANDLE_DISK_FULL:
2507 class = EC_MediaError;
2508 action = SA_Abort;
2509 locus = EL_Disk;
2510 break;
2511 case ERROR_FILE_EXISTS:
2512 case ERROR_ALREADY_EXISTS:
2513 class = EC_Exists;
2514 action = SA_Abort;
2515 locus = EL_Disk;
2516 break;
2517 case ERROR_FILE_NOT_FOUND:
2518 class = EC_NotFound;
2519 action = SA_Abort;
2520 locus = EL_Disk;
2521 break;
2522 case ER_GeneralFailure:
2523 class = EC_SystemFailure;
2524 action = SA_Abort;
2525 locus = EL_Unknown;
2526 break;
2527 case ERROR_INVALID_DRIVE:
2528 class = EC_MediaError;
2529 action = SA_Abort;
2530 locus = EL_Disk;
2531 break;
2532 case ERROR_INVALID_HANDLE:
2533 class = EC_ProgramError;
2534 action = SA_Abort;
2535 locus = EL_Disk;
2536 break;
2537 case ERROR_LOCK_VIOLATION:
2538 class = EC_AccessDenied;
2539 action = SA_Abort;
2540 locus = EL_Disk;
2541 break;
2542 case ERROR_NO_MORE_FILES:
2543 class = EC_MediaError;
2544 action = SA_Abort;
2545 locus = EL_Disk;
2546 break;
2547 case ER_NoNetwork:
2548 class = EC_NotFound;
2549 action = SA_Abort;
2550 locus = EL_Network;
2551 break;
2552 case ERROR_NOT_ENOUGH_MEMORY:
2553 class = EC_OutOfResource;
2554 action = SA_Abort;
2555 locus = EL_Memory;
2556 break;
2557 case ERROR_PATH_NOT_FOUND:
2558 class = EC_NotFound;
2559 action = SA_Abort;
2560 locus = EL_Disk;
2561 break;
2562 case ERROR_SEEK:
2563 class = EC_NotFound;
2564 action = SA_Ignore;
2565 locus = EL_Disk;
2566 break;
2567 case ERROR_SHARING_VIOLATION:
2568 class = EC_Temporary;
2569 action = SA_Retry;
2570 locus = EL_Disk;
2571 break;
2572 case ERROR_TOO_MANY_OPEN_FILES:
2573 class = EC_ProgramError;
2574 action = SA_Abort;
2575 locus = EL_Disk;
2576 break;
2577 default:
2578 FIXME("Unknown error %d\n", error );
2579 class = EC_SystemFailure;
2580 action = SA_Abort;
2581 locus = EL_Unknown;
2582 break;
2584 TRACE("GET EXTENDED ERROR code 0x%02x class 0x%02x action 0x%02x locus %02x\n",
2585 error, class, action, locus );
2586 SET_AX( context, error );
2587 SET_BH( context, class );
2588 SET_BL( context, action );
2589 SET_CH( context, locus );
2593 /***********************************************************************
2594 * DOSVM_Int21Handler
2596 * Interrupt 0x21 handler.
2598 void WINAPI DOSVM_Int21Handler( CONTEXT86 *context )
2600 BOOL bSetDOSExtendedError = FALSE;
2602 TRACE( "AX=%04x BX=%04x CX=%04x DX=%04x "
2603 "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
2604 AX_reg(context), BX_reg(context),
2605 CX_reg(context), DX_reg(context),
2606 SI_reg(context), DI_reg(context),
2607 (WORD)context->SegDs, (WORD)context->SegEs,
2608 context->EFlags );
2611 * Extended error is used by (at least) functions 0x2f to 0x62.
2612 * Function 0x59 returns extended error and error should not
2613 * be cleared before handling the function.
2615 if (AH_reg(context) >= 0x2f && AH_reg(context) != 0x59)
2616 SetLastError(0);
2618 RESET_CFLAG(context); /* Not sure if this is a good idea. */
2620 switch(AH_reg(context))
2622 case 0x00: /* TERMINATE PROGRAM */
2623 TRACE("TERMINATE PROGRAM\n");
2624 if (DOSVM_IsWin16())
2625 ExitThread( 0 );
2626 else
2627 MZ_Exit( context, FALSE, 0 );
2628 break;
2630 case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
2632 BYTE ascii;
2633 TRACE("DIRECT CHARACTER INPUT WITH ECHO\n");
2634 INT21_ReadChar( &ascii, context );
2635 SET_AL( context, ascii );
2637 * FIXME: What to echo when extended keycodes are read?
2639 DOSVM_PutChar(AL_reg(context));
2641 break;
2643 case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
2644 TRACE("Write Character to Standard Output\n");
2645 DOSVM_PutChar(DL_reg(context));
2646 break;
2648 case 0x03: /* READ CHARACTER FROM STDAUX */
2649 case 0x04: /* WRITE CHARACTER TO STDAUX */
2650 case 0x05: /* WRITE CHARACTER TO PRINTER */
2651 INT_BARF( context, 0x21 );
2652 break;
2654 case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
2655 if (DL_reg(context) == 0xff)
2657 TRACE("Direct Console Input\n");
2659 if (INT21_ReadChar( NULL, NULL ))
2661 BYTE ascii;
2662 INT21_ReadChar( &ascii, context );
2663 SET_AL( context, ascii );
2664 RESET_ZFLAG( context );
2666 else
2668 /* no character available */
2669 SET_AL( context, 0 );
2670 SET_ZFLAG( context );
2673 else
2675 TRACE("Direct Console Output\n");
2676 DOSVM_PutChar(DL_reg(context));
2678 * At least DOS versions 2.1-7.0 return character
2679 * that was written in AL register.
2681 SET_AL( context, DL_reg(context) );
2683 break;
2685 case 0x07: /* DIRECT CHARACTER INPUT WITHOUT ECHO */
2687 BYTE ascii;
2688 TRACE("DIRECT CHARACTER INPUT WITHOUT ECHO\n");
2689 INT21_ReadChar( &ascii, context );
2690 SET_AL( context, ascii );
2692 break;
2694 case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
2696 BYTE ascii;
2697 TRACE("CHARACTER INPUT WITHOUT ECHO\n");
2698 INT21_ReadChar( &ascii, context );
2699 SET_AL( context, ascii );
2701 break;
2703 case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
2704 TRACE("WRITE '$'-terminated string from %04lX:%04X to stdout\n",
2705 context->SegDs, DX_reg(context) );
2707 LPSTR data = CTX_SEG_OFF_TO_LIN( context,
2708 context->SegDs, context->Edx );
2709 LPSTR p = data;
2712 * Do NOT use strchr() to calculate the string length,
2713 * as '\0' is valid string content, too!
2714 * Maybe we should check for non-'$' strings, but DOS doesn't.
2716 while (*p != '$') p++;
2718 if (DOSVM_IsWin16())
2719 WriteFile( DosFileHandleToWin32Handle(1),
2720 data, p - data, 0, 0 );
2721 else
2722 for(; data != p; data++)
2723 DOSVM_PutChar( *data );
2725 SET_AL( context, '$' ); /* yes, '$' (0x24) gets returned in AL */
2727 break;
2729 case 0x0a: /* BUFFERED INPUT */
2731 BYTE *ptr = CTX_SEG_OFF_TO_LIN(context,
2732 context->SegDs,
2733 context->Edx);
2734 WORD result;
2736 TRACE( "BUFFERED INPUT (size=%d)\n", ptr[0] );
2739 * FIXME: Some documents state that
2740 * ptr[1] holds number of chars from last input which
2741 * may be recalled on entry, other documents do not mention
2742 * this at all.
2744 if (ptr[1])
2745 TRACE( "Handle old chars in buffer!\n" );
2748 * ptr[0] - capacity (includes terminating CR)
2749 * ptr[1] - characters read (excludes terminating CR)
2751 result = INT21_BufferedInput( context, ptr + 2, ptr[0] );
2752 if (result > 0)
2753 ptr[1] = (BYTE)result - 1;
2754 else
2755 ptr[1] = 0;
2757 break;
2759 case 0x0b: /* GET STDIN STATUS */
2760 TRACE( "GET STDIN STATUS\n" );
2762 if (INT21_ReadChar( NULL, NULL ))
2763 SET_AL( context, 0xff ); /* character available */
2764 else
2765 SET_AL( context, 0 ); /* no character available */
2767 break;
2769 case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
2771 BYTE al = AL_reg(context); /* Input function to execute after flush. */
2773 TRACE( "FLUSH BUFFER AND READ STANDARD INPUT - 0x%02x\n", al );
2775 /* FIXME: buffers are not flushed */
2778 * If AL is one of 0x01, 0x06, 0x07, 0x08, or 0x0a,
2779 * int21 function identified by AL will be called.
2781 if(al == 0x01 || al == 0x06 || al == 0x07 || al == 0x08 || al == 0x0a)
2783 SET_AH( context, al );
2784 DOSVM_Int21Handler( context );
2787 break;
2789 case 0x0d: /* DISK BUFFER FLUSH */
2790 TRACE("DISK BUFFER FLUSH ignored\n");
2791 break;
2793 case 0x0e: /* SELECT DEFAULT DRIVE */
2794 TRACE( "SELECT DEFAULT DRIVE - %c:\n", 'A' + DL_reg(context) );
2795 INT21_SetCurrentDrive( DL_reg(context) );
2796 SET_AL( context, MAX_DOS_DRIVES );
2797 break;
2799 case 0x0f: /* OPEN FILE USING FCB */
2800 INT21_OpenFileUsingFCB( context );
2801 break;
2803 case 0x10: /* CLOSE FILE USING FCB */
2804 INT21_CloseFileUsingFCB( context );
2805 break;
2807 case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
2808 case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
2809 INT_Int21Handler( context );
2810 break;
2812 case 0x13: /* DELETE FILE USING FCB */
2813 INT_BARF( context, 0x21 );
2814 break;
2816 case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
2817 INT21_SequentialReadFromFCB( context );
2818 break;
2820 case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
2821 INT21_SequentialWriteToFCB( context );
2822 break;
2824 case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
2825 case 0x17: /* RENAME FILE USING FCB */
2826 INT_BARF( context, 0x21 );
2827 break;
2829 case 0x18: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
2830 SET_AL( context, 0 );
2831 break;
2833 case 0x19: /* GET CURRENT DEFAULT DRIVE */
2834 SET_AL( context, INT21_GetCurrentDrive() );
2835 TRACE( "GET CURRENT DRIVE -> %c:\n", 'A' + AL_reg( context ) );
2836 break;
2838 case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
2839 TRACE( "SET DISK TRANSFER AREA ADDRESS %04lX:%04X\n",
2840 context->SegDs, DX_reg(context) );
2842 TDB *task = GlobalLock16( GetCurrentTask() );
2843 task->dta = MAKESEGPTR( context->SegDs, DX_reg(context) );
2845 break;
2847 case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
2848 case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
2849 INT_Int21Handler( context );
2850 break;
2852 case 0x1d: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
2853 case 0x1e: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
2854 SET_AL( context, 0 );
2855 break;
2857 case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
2858 INT_Int21Handler( context );
2859 break;
2861 case 0x20: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
2862 SET_AL( context, 0 );
2863 break;
2865 case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
2866 INT21_ReadRandomRecordFromFCB( context );
2867 break;
2869 case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
2870 INT21_WriteRandomRecordToFCB( context );
2871 break;
2873 case 0x23: /* GET FILE SIZE FOR FCB */
2874 case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
2875 INT_BARF( context, 0x21 );
2876 break;
2878 case 0x25: /* SET INTERRUPT VECTOR */
2879 TRACE("SET INTERRUPT VECTOR 0x%02x\n",AL_reg(context));
2881 FARPROC16 ptr = (FARPROC16)MAKESEGPTR( context->SegDs, DX_reg(context) );
2882 if (!ISV86(context) && DOSVM_IsWin16())
2883 DOSVM_SetPMHandler16( AL_reg(context), ptr );
2884 else
2885 DOSVM_SetRMHandler( AL_reg(context), ptr );
2887 break;
2889 case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
2890 INT_BARF( context, 0x21 );
2891 break;
2893 case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
2894 INT21_RandomBlockReadFromFCB( context );
2895 break;
2897 case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
2898 INT21_RandomBlockWriteToFCB( context );
2899 break;
2901 case 0x29: /* PARSE FILENAME INTO FCB */
2902 INT_Int21Handler( context );
2903 break;
2905 case 0x2a: /* GET SYSTEM DATE */
2906 TRACE( "GET SYSTEM DATE\n" );
2908 SYSTEMTIME systime;
2909 GetLocalTime( &systime );
2910 SET_CX( context, systime.wYear );
2911 SET_DH( context, systime.wMonth );
2912 SET_DL( context, systime.wDay );
2913 SET_AL( context, systime.wDayOfWeek );
2915 break;
2917 case 0x2b: /* SET SYSTEM DATE */
2918 TRACE( "SET SYSTEM DATE\n" );
2920 WORD year = CX_reg(context);
2921 BYTE month = DH_reg(context);
2922 BYTE day = DL_reg(context);
2924 if (year >= 1980 && year <= 2099 &&
2925 month >= 1 && month <= 12 &&
2926 day >= 1 && day <= 31)
2928 FIXME( "SetSystemDate(%02d/%02d/%04d): not allowed\n",
2929 day, month, year );
2930 SET_AL( context, 0 ); /* Let's pretend we succeeded */
2932 else
2934 SET_AL( context, 0xff ); /* invalid date */
2937 break;
2939 case 0x2c: /* GET SYSTEM TIME */
2940 TRACE( "GET SYSTEM TIME\n" );
2942 SYSTEMTIME systime;
2943 GetLocalTime( &systime );
2944 SET_CH( context, systime.wHour );
2945 SET_CL( context, systime.wMinute );
2946 SET_DH( context, systime.wSecond );
2947 SET_DL( context, systime.wMilliseconds / 10 );
2949 break;
2951 case 0x2d: /* SET SYSTEM TIME */
2952 FIXME("SetSystemTime(%02d:%02d:%02d.%02d): not allowed\n",
2953 CH_reg(context), CL_reg(context),
2954 DH_reg(context), DL_reg(context) );
2955 SET_AL( context, 0 ); /* Let's pretend we succeeded */
2956 break;
2958 case 0x2e: /* SET VERIFY FLAG */
2959 TRACE("SET VERIFY FLAG ignored\n");
2960 /* we cannot change the behaviour anyway, so just ignore it */
2961 break;
2963 case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
2964 TRACE( "GET DISK TRANSFER AREA ADDRESS\n" );
2966 TDB *task = GlobalLock16( GetCurrentTask() );
2967 context->SegEs = SELECTOROF( task->dta );
2968 SET_BX( context, OFFSETOF( task->dta ) );
2970 break;
2972 case 0x30: /* GET DOS VERSION */
2973 TRACE( "GET DOS VERSION - %s requested\n",
2974 (AL_reg(context) == 0x00) ? "OEM number" : "version flag" );
2976 if (AL_reg(context) == 0x00)
2977 SET_BH( context, 0xff ); /* OEM number => undefined */
2978 else
2979 SET_BH( context, 0x08 ); /* version flag => DOS is in ROM */
2981 SET_AL( context, HIBYTE(HIWORD(GetVersion16())) ); /* major version */
2982 SET_AH( context, LOBYTE(HIWORD(GetVersion16())) ); /* minor version */
2984 SET_BL( context, 0x12 ); /* 0x123456 is 24-bit Wine's serial # */
2985 SET_CX( context, 0x3456 );
2986 break;
2988 case 0x31: /* TERMINATE AND STAY RESIDENT */
2989 FIXME("TERMINATE AND STAY RESIDENT stub\n");
2990 break;
2992 case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
2993 INT_Int21Handler( context );
2994 break;
2996 case 0x33: /* MULTIPLEXED */
2997 switch (AL_reg(context))
2999 case 0x00: /* GET CURRENT EXTENDED BREAK STATE */
3000 TRACE("GET CURRENT EXTENDED BREAK STATE\n");
3001 SET_DL( context, DOSCONF_GetConfig()->brk_flag );
3002 break;
3004 case 0x01: /* SET EXTENDED BREAK STATE */
3005 TRACE("SET CURRENT EXTENDED BREAK STATE\n");
3006 DOSCONF_GetConfig()->brk_flag = (DL_reg(context) > 0) ? 1 : 0;
3007 break;
3009 case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/
3010 TRACE("GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE\n");
3011 /* ugly coding in order to stay reentrant */
3012 if (DL_reg(context))
3014 SET_DL( context, DOSCONF_GetConfig()->brk_flag );
3015 DOSCONF_GetConfig()->brk_flag = 1;
3017 else
3019 SET_DL( context, DOSCONF_GetConfig()->brk_flag );
3020 DOSCONF_GetConfig()->brk_flag = 0;
3022 break;
3024 case 0x05: /* GET BOOT DRIVE */
3025 TRACE("GET BOOT DRIVE\n");
3026 SET_DL( context, 3 );
3027 /* c: is Wine's bootdrive (a: is 1)*/
3028 break;
3030 case 0x06: /* GET TRUE VERSION NUMBER */
3031 TRACE("GET TRUE VERSION NUMBER\n");
3032 SET_BL( context, HIBYTE(HIWORD(GetVersion16())) ); /* major */
3033 SET_BH( context, LOBYTE(HIWORD(GetVersion16())) ); /* minor */
3034 SET_DL( context, 0x00 ); /* revision */
3035 SET_DH( context, 0x08 ); /* DOS is in ROM */
3036 break;
3038 default:
3039 INT_BARF( context, 0x21 );
3040 break;
3042 break;
3044 case 0x34: /* GET ADDRESS OF INDOS FLAG */
3045 TRACE( "GET ADDRESS OF INDOS FLAG\n" );
3046 context->SegEs = INT21_GetHeapSelector( context );
3047 SET_BX( context, offsetof(INT21_HEAP, misc_indos) );
3048 break;
3050 case 0x35: /* GET INTERRUPT VECTOR */
3051 TRACE("GET INTERRUPT VECTOR 0x%02x\n",AL_reg(context));
3053 FARPROC16 addr;
3054 if (!ISV86(context) && DOSVM_IsWin16())
3055 addr = DOSVM_GetPMHandler16( AL_reg(context) );
3056 else
3057 addr = DOSVM_GetRMHandler( AL_reg(context) );
3058 context->SegEs = SELECTOROF(addr);
3059 SET_BX( context, OFFSETOF(addr) );
3061 break;
3063 case 0x36: /* GET FREE DISK SPACE */
3064 INT_Int21Handler( context );
3065 break;
3067 case 0x37: /* SWITCHAR */
3069 switch (AL_reg(context))
3071 case 0x00: /* "SWITCHAR" - GET SWITCH CHARACTER */
3072 TRACE( "SWITCHAR - GET SWITCH CHARACTER\n" );
3073 SET_AL( context, 0x00 ); /* success*/
3074 SET_DL( context, '/' );
3075 break;
3076 case 0x01: /*"SWITCHAR" - SET SWITCH CHARACTER*/
3077 FIXME( "SWITCHAR - SET SWITCH CHARACTER: %c\n",
3078 DL_reg( context ));
3079 SET_AL( context, 0x00 ); /* success*/
3080 break;
3081 default:
3082 INT_BARF( context, 0x21 );
3083 break;
3086 break;
3088 case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
3089 TRACE( "GET COUNTRY-SPECIFIC INFORMATION\n" );
3090 if (AL_reg(context))
3092 WORD country = AL_reg(context);
3093 if (country == 0xff)
3094 country = BX_reg(context);
3095 if (country != INT21_GetSystemCountryCode())
3096 FIXME( "Requested info on non-default country %04x\n", country );
3098 INT21_FillCountryInformation( CTX_SEG_OFF_TO_LIN(context,
3099 context->SegDs,
3100 context->Edx) );
3101 SET_AX( context, INT21_GetSystemCountryCode() );
3102 SET_BX( context, INT21_GetSystemCountryCode() );
3103 break;
3105 case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
3106 if (!INT21_CreateDirectory( context ))
3107 bSetDOSExtendedError = TRUE;
3108 break;
3110 case 0x3a: /* "RMDIR" - REMOVE DIRECTORY */
3112 WCHAR dirW[MAX_PATH];
3113 char *dirA = CTX_SEG_OFF_TO_LIN(context,
3114 context->SegDs, context->Edx);
3116 TRACE( "REMOVE DIRECTORY %s\n", dirA );
3118 MultiByteToWideChar(CP_OEMCP, 0, dirA, -1, dirW, MAX_PATH);
3120 if (!RemoveDirectoryW( dirW ))
3121 bSetDOSExtendedError = TRUE;
3123 break;
3125 case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
3126 if (!INT21_SetCurrentDirectory( context ))
3127 bSetDOSExtendedError = TRUE;
3128 break;
3130 case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
3131 if (!INT21_CreateFile( context, context->Edx, FALSE,
3132 OF_READWRITE | OF_SHARE_COMPAT, 0x12 ))
3133 bSetDOSExtendedError = TRUE;
3134 break;
3136 case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
3137 if (!INT21_CreateFile( context, context->Edx, FALSE,
3138 AL_reg(context), 0x01 ))
3139 bSetDOSExtendedError = TRUE;
3140 break;
3142 case 0x3e: /* "CLOSE" - CLOSE FILE */
3143 TRACE( "CLOSE handle %d\n", BX_reg(context) );
3144 if (_lclose16( BX_reg(context) ) == HFILE_ERROR16)
3145 bSetDOSExtendedError = TRUE;
3146 break;
3148 case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
3149 TRACE( "READ from %d to %04lX:%04X for %d bytes\n",
3150 BX_reg(context),
3151 context->SegDs,
3152 DX_reg(context),
3153 CX_reg(context) );
3155 DWORD result;
3156 WORD count = CX_reg(context);
3157 BYTE *buffer = CTX_SEG_OFF_TO_LIN( context,
3158 context->SegDs,
3159 context->Edx );
3161 /* Some programs pass a count larger than the allocated buffer */
3162 if (DOSVM_IsWin16())
3164 WORD maxcount = GetSelectorLimit16( context->SegDs )
3165 - DX_reg(context) + 1;
3166 if (count > maxcount)
3167 count = maxcount;
3171 * FIXME: Reading from console (BX=1) in DOS mode
3172 * does not work as it is supposed to work.
3175 if (!DOSVM_IsWin16() && BX_reg(context) == 0)
3177 result = INT21_BufferedInput( context, buffer, count );
3178 SET_AX( context, (WORD)result );
3180 else if (ReadFile( DosFileHandleToWin32Handle(BX_reg(context)),
3181 buffer, count, &result, NULL ))
3182 SET_AX( context, (WORD)result );
3183 else
3184 bSetDOSExtendedError = TRUE;
3186 break;
3188 case 0x40: /* "WRITE" - WRITE TO FILE OR DEVICE */
3189 TRACE( "WRITE from %04lX:%04X to handle %d for %d byte\n",
3190 context->SegDs, DX_reg(context),
3191 BX_reg(context), CX_reg(context) );
3193 BYTE *ptr = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
3195 if (!DOSVM_IsWin16() &&
3196 (BX_reg(context) == 1 || BX_reg(context) == 2))
3198 int i;
3199 for(i=0; i<CX_reg(context); i++)
3200 DOSVM_PutChar(ptr[i]);
3201 SET_AX(context, CX_reg(context));
3203 else
3205 HFILE handle = (HFILE)DosFileHandleToWin32Handle(BX_reg(context));
3206 LONG result = _hwrite( handle, ptr, CX_reg(context) );
3207 if (result == HFILE_ERROR)
3208 bSetDOSExtendedError = TRUE;
3209 else
3210 SET_AX( context, (WORD)result );
3213 break;
3215 case 0x41: /* "UNLINK" - DELETE FILE */
3217 WCHAR fileW[MAX_PATH];
3218 char *fileA = CTX_SEG_OFF_TO_LIN(context,
3219 context->SegDs,
3220 context->Edx);
3222 TRACE( "UNLINK %s\n", fileA );
3223 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
3225 if (!DeleteFileW( fileW ))
3226 bSetDOSExtendedError = TRUE;
3228 break;
3230 case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
3231 TRACE( "LSEEK handle %d offset %ld from %s\n",
3232 BX_reg(context),
3233 MAKELONG( DX_reg(context), CX_reg(context) ),
3234 (AL_reg(context) == 0) ?
3235 "start of file" : ((AL_reg(context) == 1) ?
3236 "current file position" : "end of file") );
3238 HANDLE handle = DosFileHandleToWin32Handle(BX_reg(context));
3239 LONG offset = MAKELONG( DX_reg(context), CX_reg(context) );
3240 DWORD status = SetFilePointer( handle, offset,
3241 NULL, AL_reg(context) );
3242 if (status == INVALID_SET_FILE_POINTER)
3243 bSetDOSExtendedError = TRUE;
3244 else
3246 SET_AX( context, LOWORD(status) );
3247 SET_DX( context, HIWORD(status) );
3250 break;
3252 case 0x43: /* FILE ATTRIBUTES */
3253 if (!INT21_FileAttributes( context, AL_reg(context), FALSE ))
3254 bSetDOSExtendedError = TRUE;
3255 break;
3257 case 0x44: /* IOCTL */
3258 INT21_Ioctl( context );
3259 break;
3261 case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
3262 TRACE( "DUPLICATE FILE HANDLE %d\n", BX_reg(context) );
3264 HANDLE handle32;
3265 HFILE handle16 = HFILE_ERROR;
3267 if (DuplicateHandle( GetCurrentProcess(),
3268 DosFileHandleToWin32Handle(BX_reg(context)),
3269 GetCurrentProcess(),
3270 &handle32,
3271 0, TRUE, DUPLICATE_SAME_ACCESS ))
3272 handle16 = Win32HandleToDosFileHandle(handle32);
3274 if (handle16 == HFILE_ERROR)
3275 bSetDOSExtendedError = TRUE;
3276 else
3277 SET_AX( context, handle16 );
3279 break;
3281 case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
3282 TRACE( "FORCEDUP - FORCE DUPLICATE FILE HANDLE %d to %d\n",
3283 BX_reg(context), CX_reg(context) );
3284 if (FILE_Dup2( BX_reg(context), CX_reg(context) ) == HFILE_ERROR16)
3285 bSetDOSExtendedError = TRUE;
3286 break;
3288 case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
3289 if (!INT21_GetCurrentDirectory( context, FALSE ))
3290 bSetDOSExtendedError = TRUE;
3291 break;
3293 case 0x48: /* ALLOCATE MEMORY */
3294 TRACE( "ALLOCATE MEMORY for %d paragraphs\n", BX_reg(context) );
3296 WORD selector = 0;
3297 DWORD bytes = (DWORD)BX_reg(context) << 4;
3299 if (!ISV86(context) && DOSVM_IsWin16())
3301 DWORD rv = GlobalDOSAlloc16( bytes );
3302 selector = LOWORD( rv );
3304 else
3305 DOSMEM_GetBlock( bytes, &selector );
3307 if (selector)
3308 SET_AX( context, selector );
3309 else
3311 SET_CFLAG(context);
3312 SET_AX( context, 0x0008 ); /* insufficient memory */
3313 SET_BX( context, DOSMEM_Available() >> 4 );
3316 break;
3318 case 0x49: /* FREE MEMORY */
3319 TRACE( "FREE MEMORY segment %04lX\n", context->SegEs );
3321 BOOL ok;
3323 if (!ISV86(context) && DOSVM_IsWin16())
3325 ok = !GlobalDOSFree16( context->SegEs );
3327 /* If we don't reset ES_reg, we will fail in the relay code */
3328 if (ok)
3329 context->SegEs = 0;
3331 else
3332 ok = DOSMEM_FreeBlock( (void*)((DWORD)context->SegEs << 4) );
3334 if (!ok)
3336 TRACE("FREE MEMORY failed\n");
3337 SET_CFLAG(context);
3338 SET_AX( context, 0x0009 ); /* memory block address invalid */
3341 break;
3343 case 0x4a: /* RESIZE MEMORY BLOCK */
3344 TRACE( "RESIZE MEMORY segment %04lX to %d paragraphs\n",
3345 context->SegEs, BX_reg(context) );
3347 DWORD newsize = (DWORD)BX_reg(context) << 4;
3349 if (!ISV86(context) && DOSVM_IsWin16())
3351 FIXME( "Resize memory block - unsupported under Win16\n" );
3353 else
3355 LPVOID address = (void*)((DWORD)context->SegEs << 4);
3356 UINT blocksize = DOSMEM_ResizeBlock( address, newsize, FALSE );
3358 if (blocksize == (UINT)-1)
3360 SET_CFLAG( context );
3361 SET_AX( context, 0x0009 ); /* illegal address */
3363 else if(blocksize != newsize)
3365 SET_CFLAG( context );
3366 SET_AX( context, 0x0008 ); /* insufficient memory */
3367 SET_BX( context, blocksize >> 4 ); /* new block size */
3371 break;
3373 case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
3375 BYTE *program = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
3376 BYTE *paramblk = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx);
3378 TRACE( "EXEC %s\n", program );
3380 if (DOSVM_IsWin16())
3382 HINSTANCE16 instance = WinExec16( program, SW_NORMAL );
3383 if (instance < 32)
3385 SET_CFLAG( context );
3386 SET_AX( context, instance );
3389 else
3391 if (!MZ_Exec( context, program, AL_reg(context), paramblk))
3392 bSetDOSExtendedError = TRUE;
3395 break;
3397 case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
3398 TRACE( "EXIT with return code %d\n", AL_reg(context) );
3399 if (DOSVM_IsWin16())
3400 ExitThread( AL_reg(context) );
3401 else
3402 MZ_Exit( context, FALSE, AL_reg(context) );
3403 break;
3405 case 0x4d: /* GET RETURN CODE */
3406 TRACE("GET RETURN CODE (ERRORLEVEL)\n");
3407 SET_AX( context, DOSVM_retval );
3408 DOSVM_retval = 0;
3409 break;
3411 case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
3412 case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
3413 INT_Int21Handler( context );
3414 break;
3416 case 0x50: /* SET CURRENT PROCESS ID (SET PSP ADDRESS) */
3417 TRACE("SET CURRENT PROCESS ID (SET PSP ADDRESS)\n");
3418 DOSVM_psp = BX_reg(context);
3419 break;
3421 case 0x51: /* GET PSP ADDRESS */
3422 INT21_GetPSP( context );
3423 break;
3425 case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
3426 if (!ISV86(context) && DOSVM_IsWin16())
3428 SEGPTR ptr = DOSMEM_LOL()->wine_pm_lol;
3429 context->SegEs = SELECTOROF(ptr);
3430 SET_BX( context, OFFSETOF(ptr) );
3432 else
3434 SEGPTR ptr = DOSMEM_LOL()->wine_rm_lol;
3435 context->SegEs = SELECTOROF(ptr);
3436 SET_BX( context, OFFSETOF(ptr) );
3438 break;
3440 case 0x54: /* Get Verify Flag */
3441 TRACE("Get Verify Flag - Not Supported\n");
3442 SET_AL( context, 0x00 ); /* pretend we can tell. 00h = off 01h = on */
3443 break;
3445 case 0x56: /* "RENAME" - RENAME FILE */
3446 if (!INT21_RenameFile( context ))
3447 bSetDOSExtendedError = TRUE;
3448 break;
3450 case 0x57: /* FILE DATE AND TIME */
3451 if (!INT21_FileDateTime( context ))
3452 bSetDOSExtendedError = TRUE;
3453 break;
3455 case 0x58: /* GET OR SET MEMORY ALLOCATION STRATEGY */
3456 switch (AL_reg(context))
3458 case 0x00: /* GET MEMORY ALLOCATION STRATEGY */
3459 TRACE( "GET MEMORY ALLOCATION STRATEGY\n" );
3460 SET_AX( context, 0 ); /* low memory first fit */
3461 break;
3463 case 0x01: /* SET ALLOCATION STRATEGY */
3464 TRACE( "SET MEMORY ALLOCATION STRATEGY to %d - ignored\n",
3465 BL_reg(context) );
3466 break;
3468 case 0x02: /* GET UMB LINK STATE */
3469 TRACE( "GET UMB LINK STATE\n" );
3470 SET_AL( context, 0 ); /* UMBs not part of DOS memory chain */
3471 break;
3473 case 0x03: /* SET UMB LINK STATE */
3474 TRACE( "SET UMB LINK STATE to %d - ignored\n",
3475 BX_reg(context) );
3476 break;
3478 default:
3479 INT_BARF( context, 0x21 );
3481 break;
3483 case 0x59: /* GET EXTENDED ERROR INFO */
3484 INT21_GetExtendedError( context );
3485 break;
3487 case 0x5a: /* CREATE TEMPORARY FILE */
3488 INT_Int21Handler( context );
3489 break;
3491 case 0x5b: /* CREATE NEW FILE */
3492 if (!INT21_CreateFile( context, context->Edx, FALSE,
3493 OF_READWRITE | OF_SHARE_COMPAT, 0x10 ))
3494 bSetDOSExtendedError = TRUE;
3495 break;
3497 case 0x5c: /* "FLOCK" - RECORD LOCKING */
3499 DWORD offset = MAKELONG(DX_reg(context), CX_reg(context));
3500 DWORD length = MAKELONG(DI_reg(context), SI_reg(context));
3501 HANDLE handle = DosFileHandleToWin32Handle(BX_reg(context));
3503 switch (AL_reg(context))
3505 case 0x00: /* LOCK */
3506 TRACE( "lock handle %d offset %ld length %ld\n",
3507 BX_reg(context), offset, length );
3508 if (!LockFile( handle, offset, 0, length, 0 ))
3509 bSetDOSExtendedError = TRUE;
3510 break;
3512 case 0x01: /* UNLOCK */
3513 TRACE( "unlock handle %d offset %ld length %ld\n",
3514 BX_reg(context), offset, length );
3515 if (!UnlockFile( handle, offset, 0, length, 0 ))
3516 bSetDOSExtendedError = TRUE;
3517 break;
3519 default:
3520 INT_BARF( context, 0x21 );
3523 break;
3525 case 0x5d: /* NETWORK 5D */
3526 FIXME( "Network function 5D not implemented.\n" );
3527 SetLastError( ER_NoNetwork );
3528 bSetDOSExtendedError = TRUE;
3529 break;
3531 case 0x5e: /* NETWORK 5E */
3532 case 0x5f: /* NETWORK 5F */
3533 case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
3534 INT_Int21Handler( context );
3535 break;
3537 case 0x61: /* UNUSED */
3538 SET_AL( context, 0 );
3539 break;
3541 case 0x62: /* GET PSP ADDRESS */
3542 INT21_GetPSP( context );
3543 break;
3545 case 0x63: /* MISC. LANGUAGE SUPPORT */
3546 switch (AL_reg(context)) {
3547 case 0x00: /* GET DOUBLE BYTE CHARACTER SET LEAD-BYTE TABLE */
3548 TRACE( "GET DOUBLE BYTE CHARACTER SET LEAD-BYTE TABLE\n" );
3549 context->SegDs = INT21_GetHeapSelector( context );
3550 SET_SI( context, offsetof(INT21_HEAP, dbcs_table) );
3551 SET_AL( context, 0 ); /* success */
3552 break;
3554 break;
3556 case 0x64: /* OS/2 DOS BOX */
3557 INT_BARF( context, 0x21 );
3558 SET_CFLAG(context);
3559 break;
3561 case 0x65: /* EXTENDED COUNTRY INFORMATION */
3562 INT21_ExtendedCountryInformation( context );
3563 break;
3565 case 0x66: /* GLOBAL CODE PAGE TABLE */
3566 switch (AL_reg(context))
3568 case 0x01:
3569 TRACE( "GET GLOBAL CODE PAGE TABLE\n" );
3570 SET_BX( context, GetOEMCP() );
3571 SET_DX( context, GetOEMCP() );
3572 break;
3573 case 0x02:
3574 FIXME( "SET GLOBAL CODE PAGE TABLE, active %d, system %d - ignored\n",
3575 BX_reg(context), DX_reg(context) );
3576 break;
3578 break;
3580 case 0x67: /* SET HANDLE COUNT */
3581 TRACE( "SET HANDLE COUNT to %d\n", BX_reg(context) );
3582 if (SetHandleCount( BX_reg(context) ) < BX_reg(context) )
3583 bSetDOSExtendedError = TRUE;
3584 break;
3586 case 0x68: /* "FFLUSH" - COMMIT FILE */
3587 TRACE( "FFLUSH - handle %d\n", BX_reg(context) );
3588 if (!FlushFileBuffers( DosFileHandleToWin32Handle(BX_reg(context)) ))
3589 bSetDOSExtendedError = TRUE;
3590 break;
3592 case 0x69: /* DISK SERIAL NUMBER */
3593 INT_Int21Handler( context );
3594 break;
3596 case 0x6a: /* COMMIT FILE */
3597 TRACE( "COMMIT FILE - handle %d\n", BX_reg(context) );
3598 if (!FlushFileBuffers( DosFileHandleToWin32Handle(BX_reg(context)) ))
3599 bSetDOSExtendedError = TRUE;
3600 break;
3602 case 0x6b: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
3603 SET_AL( context, 0 );
3604 break;
3606 case 0x6c: /* EXTENDED OPEN/CREATE */
3607 if (!INT21_CreateFile( context, context->Esi, TRUE,
3608 BX_reg(context), DL_reg(context) ))
3609 bSetDOSExtendedError = TRUE;
3610 break;
3612 case 0x70: /* MSDOS 7 - GET/SET INTERNATIONALIZATION INFORMATION */
3613 FIXME( "MS-DOS 7 - GET/SET INTERNATIONALIZATION INFORMATION\n" );
3614 SET_CFLAG( context );
3615 SET_AL( context, 0 );
3616 break;
3618 case 0x71: /* MSDOS 7 - LONG FILENAME FUNCTIONS */
3619 INT21_LongFilename( context );
3620 break;
3622 case 0x73: /* MSDOS7 - FAT32 */
3623 INT_Int21Handler( context );
3624 break;
3626 case 0xdc: /* CONNECTION SERVICES - GET CONNECTION NUMBER */
3627 TRACE( "CONNECTION SERVICES - GET CONNECTION NUMBER - ignored\n" );
3628 break;
3630 case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
3631 TRACE( "NOVELL NETWARE - RETURN SHELL VERSION - ignored\n" );
3632 break;
3634 default:
3635 INT_BARF( context, 0x21 );
3636 break;
3638 } /* END OF SWITCH */
3640 /* Set general error condition. */
3641 if (bSetDOSExtendedError)
3643 SET_AX( context, GetLastError() );
3644 SET_CFLAG( context );
3647 /* Print error code if carry flag is set. */
3648 if (context->EFlags & 0x0001)
3649 TRACE("failed, error %ld\n", GetLastError() );
3651 TRACE( "returning: AX=%04x BX=%04x CX=%04x DX=%04x "
3652 "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
3653 AX_reg(context), BX_reg(context),
3654 CX_reg(context), DX_reg(context),
3655 SI_reg(context), DI_reg(context),
3656 (WORD)context->SegDs, (WORD)context->SegEs,
3657 context->EFlags );