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
31 #include "wine/winbase16.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
);
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 */
93 CHAR file_extension
[3];
94 WORD current_block_number
;
95 WORD logical_record_size
;
97 WORD date_of_last_write
;
98 WORD time_of_last_write
;
101 WORD starting_cluster
;
102 WORD sequence_number
;
103 BYTE file_attributes
;
105 BYTE record_within_current_block
;
106 BYTE random_access_record_number
[4];
113 BYTE xfcb_file_attribute
;
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 /***********************************************************************
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
)
151 WCHAR drivespec
[3] = {'A', ':', 0};
154 drivespec
[0] += drive
- 1;
155 drivetype
= GetDriveTypeW( drivespec
);
157 if (drivetype
== DRIVE_UNKNOWN
|| drivetype
== DRIVE_NO_ROOT_DIR
)
158 return MAX_DOS_DRIVES
;
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 /***********************************************************************
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;
196 *input
= pending_scan
;
205 if (!DOSVM_Int16ReadChar( &ascii
, &scan
, waitctx
))
210 if (waitctx
&& !ascii
)
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
245 *(WORD
*)(buffer
+ 0) = 0; /* FIXME: Get from locale */
247 /* 02 - BYTE[5]: ASCIIZ currency symbol string */
248 buffer
[2] = '$'; /* FIXME: Get from locale */
251 /* 07 - BYTE[2]: ASCIIZ thousands separator */
252 buffer
[7] = 0; /* FIXME: Get from locale */
255 /* 09 - BYTE[2]: ASCIIZ decimal separator */
256 buffer
[9] = '.'; /* FIXME: Get from locale */
259 /* 11 - BYTE[2]: ASCIIZ date separator */
260 buffer
[11] = '/'; /* FIXME: Get from locale */
263 /* 13 - BYTE[2]: ASCIIZ time separator */
264 buffer
[13] = ':'; /* FIXME: Get from locale */
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
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 */
291 /* 24 - BYTE[10]: Reserved */
292 memset( buffer
+ 24, 0, 10 );
296 /***********************************************************************
299 * Initialize DOS heap.
301 static void INT21_FillHeap( INT21_HEAP
*heap
)
303 static const char terminators
[] = "\"\\./[]:|<>+=;,";
309 heap
->uppercase_size
= 128;
310 for (i
= 0; i
< 128; i
++)
311 heap
->uppercase_table
[i
] = toupper( 128 + i
);
316 heap
->lowercase_size
= 256;
317 for (i
= 0; i
< 256; i
++)
318 heap
->lowercase_table
[i
] = tolower( i
);
323 heap
->collating_size
= 256;
324 for (i
= 0; i
< 256; i
++)
325 heap
->collating_table
[i
] = i
;
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.
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
),
372 INT21_FillHeap( ptr
);
373 heap_initialized
= TRUE
;
376 if (!ISV86(context
) && DOSVM_IsWin16())
377 return heap_selector
;
383 /***********************************************************************
384 * INT21_GetCurrentDirectory
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
];
399 TRACE( "drive %d\n", DL_reg(context
) );
401 if (new_drive
== MAX_DOS_DRIVES
)
403 SetLastError(ERROR_INVALID_DRIVE
);
408 * Grab current directory.
411 INT21_SetCurrentDrive( new_drive
);
412 if (!GetCurrentDirectoryW( MAX_PATH
, pathW
))
414 INT21_SetCurrentDrive( old_drive
);
417 INT21_SetCurrentDrive( old_drive
);
420 * Convert into short format.
425 DWORD result
= GetShortPathNameW( pathW
, pathW
, MAX_PATH
);
428 if (result
> MAX_PATH
)
430 WARN( "Short path too long!\n" );
431 SetLastError(ERROR_NETWORK_BUSY
); /* Internal Wine error. */
437 * The returned pathname does not include
438 * the drive letter, colon or leading backslash.
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. */
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. */
459 /* Remove drive letter, colon and leading backslash. */
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. */
481 /* Undocumented success code. */
482 SET_AX( context
, 0x0100 );
484 /* Truncate buffer to 64 bytes. */
488 TRACE( "%c:=%s\n", 'A' + new_drive
, pathA
);
490 strcpy( buffer
, pathA
);
495 /***********************************************************************
496 * INT21_SetCurrentDirectory
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();
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
);
521 /***********************************************************************
529 * - subfunction 0x6c of function 0x71
531 static BOOL
INT21_CreateFile( CONTEXT86
*context
,
538 char *pathA
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, pathSegOff
);
539 WCHAR pathW
[MAX_PATH
];
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
);
563 * Convert DOS action flags into Win32 creation disposition parameter.
568 winMode
= OPEN_EXISTING
;
571 winMode
= TRUNCATE_EXISTING
;
574 winMode
= CREATE_NEW
;
577 winMode
= OPEN_ALWAYS
;
580 winMode
= CREATE_ALWAYS
;
583 SetLastError( ERROR_INVALID_PARAMETER
);
588 * Convert DOS access/share flags into Win32 desired access parameter.
590 switch(dosAccessShare
& 0x07)
593 winAccess
= GENERIC_READ
;
596 winAccess
= GENERIC_WRITE
;
599 winAccess
= GENERIC_READ
| GENERIC_WRITE
;
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
;
614 * Convert DOS access/share flags into Win32 share mode parameter.
616 switch(dosAccessShare
& 0x70)
618 case OF_SHARE_EXCLUSIVE
:
621 case OF_SHARE_DENY_WRITE
:
622 winSharing
= FILE_SHARE_READ
;
624 case OF_SHARE_DENY_READ
:
625 winSharing
= FILE_SHARE_WRITE
;
627 case OF_SHARE_DENY_NONE
:
628 case OF_SHARE_COMPAT
:
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
)
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
);
665 winAttributes
= dosAttributes
&
666 (FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_HIDDEN
|
667 FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_ARCHIVE
);
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
)
682 * Determine DOS file status.
693 case TRUNCATE_EXISTING
:
700 dosStatus
= (GetLastError() == ERROR_ALREADY_EXISTS
) ? 1 : 2;
703 dosStatus
= (GetLastError() == ERROR_ALREADY_EXISTS
) ? 3 : 2;
710 * Return DOS file handle and DOS status.
712 SET_AX( context
, Win32HandleToDosFileHandle(winHandle
) );
715 SET_CX( context
, dosStatus
);
717 TRACE( "CreateFile finished: handle=%d, status=%d.\n",
718 AX_reg(context
), dosStatus
);
724 /***********************************************************************
725 * INT21_BufferedInput
727 * Handler for function 0x0a and reading from console using
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
)
740 * Return immediately if capacity is zero.
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' );
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
);
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.
802 * DX:DX [I/O] File control block (FCB or XFCB) of unopened file
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
)
821 BY_HANDLE_FILE_INFORMATION info
;
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
;
831 file_path
[0] = 'A' + INT21_MapDrive( fcb
->drive_number
);
833 if (AL_result
== 0) {
836 memcpy(pos
, fcb
->file_name
, 8);
839 pos
= strchr(pos
, ' ');
842 memcpy(pos
, fcb
->file_extension
, 3);
845 pos
= strchr(pos
, ' ');
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 */
853 hfile16
= Win32HandleToDosFileHandle(handle
);
854 if (hfile16
== HFILE_ERROR16
) {
855 TRACE("Win32HandleToDosFileHandle(%p) failed: HFILE_ERROR\n", handle
);
857 AL_result
= 0xff; /* failed */
858 } else if (hfile16
> 255) {
859 TRACE("hfile16 (=%d) larger than 255 for \"%s\"\n", hfile16
, file_path
);
861 AL_result
= 0xff; /* failed */
863 if (!GetFileInformationByHandle(handle
, &info
)) {
864 TRACE("GetFileInformationByHandle(%d, %p) for \"%s\" failed\n",
865 hfile16
, handle
, file_path
);
867 AL_result
= 0xff; /* failed */
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: */
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 */
893 SET_AL(context
, AL_result
);
897 /***********************************************************************
898 * INT21_CloseFileUsingFCB
900 * Handler for function 0x10.
903 * DX:DX [I/O] File control block (FCB or XFCB) of open file
912 static void INT21_CloseFileUsingFCB( CONTEXT86
*context
)
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
;
924 if (_lclose16((HFILE16
) fcb
->file_number
) != 0) {
925 TRACE("_lclose16(%d) failed\n", fcb
->file_number
);
926 AL_result
= 0xff; /* failed */
928 TRACE("successful closed file %d\n", fcb
->file_number
);
929 AL_result
= 0x00; /* successful */
931 SET_AL(context
, AL_result
);
935 /***********************************************************************
936 * INT21_SequentialReadFromFCB
938 * Handler for function 0x14.
941 * DX:DX [I/O] File control block (FCB or XFCB) of open file
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
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
)
964 BYTE
*disk_transfer_area
;
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
;
974 handle
= DosFileHandleToWin32Handle((HFILE16
) fcb
->file_number
);
975 if (handle
== INVALID_HANDLE_VALUE
) {
976 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
978 AL_result
= 0x01; /* end of file, no data read */
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 */
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 */
995 memset(&disk_transfer_area
[bytes_read
], 0, fcb
->logical_record_size
- bytes_read
);
996 AL_result
= 0x03; /* end of file, partial record read */
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 */
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
++;
1010 fcb
->record_within_current_block
++;
1013 SET_AL(context
, AL_result
);
1017 /***********************************************************************
1018 * INT21_SequentialWriteToFCB
1020 * Handler for function 0x15.
1023 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1028 * 2: segment wrap in DTA (not returned now)
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
)
1042 DWORD record_number
;
1044 BYTE
*disk_transfer_area
;
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
;
1054 handle
= DosFileHandleToWin32Handle((HFILE16
) fcb
->file_number
);
1055 if (handle
== INVALID_HANDLE_VALUE
) {
1056 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1058 AL_result
= 0x01; /* disk full */
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 */
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 */
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 */
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
++;
1085 fcb
->record_within_current_block
++;
1088 SET_AL(context
, AL_result
);
1092 /***********************************************************************
1093 * INT21_ReadRandomRecordFromFCB
1095 * Handler for function 0x21.
1098 * DX:DX [I/O] File control block (FCB or XFCB) of open file
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
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
)
1118 DWORD record_number
;
1120 BYTE
*disk_transfer_area
;
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
;
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",
1135 AL_result
= 0x01; /* end of file, no data read */
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 */
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 */
1151 memset(&disk_transfer_area
[bytes_read
], 0, fcb
->logical_record_size
- bytes_read
);
1152 AL_result
= 0x03; /* end of file, partial record read */
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 */
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.
1173 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1178 * 2: segment wrap in DTA (not returned now)
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
)
1191 DWORD record_number
;
1193 BYTE
*disk_transfer_area
;
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
;
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",
1208 AL_result
= 0x01; /* disk full */
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 */
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 */
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 */
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.
1241 * CX [I/O] Number of records to read
1242 * DX:DX [I/O] File control block (FCB or XFCB) of open file
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
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
)
1266 DWORD record_number
;
1268 BYTE
*disk_transfer_area
;
1269 UINT records_requested
;
1270 UINT bytes_requested
;
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
;
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",
1287 AL_result
= 0x01; /* end of file, no data read */
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
);
1294 AL_result
= 0x01; /* end of file, no data read */
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 */
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 */
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 */
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.
1334 * CX [I/O] Number of records to write
1335 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1340 * 2: segment wrap in DTA (not returned now)
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
)
1357 DWORD record_number
;
1359 BYTE
*disk_transfer_area
;
1360 UINT records_requested
;
1361 UINT bytes_requested
;
1363 UINT records_written
;
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
;
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",
1377 records_written
= 0;
1378 AL_result
= 0x01; /* disk full */
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 */
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 */
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 */
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
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
,
1428 TRACE( "CREATE DIRECTORY %s\n", dirA
);
1430 MultiByteToWideChar(CP_OEMCP
, 0, dirA
, -1, dirW
, MAX_PATH
);
1432 if (CreateDirectoryW(dirW
, NULL
))
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
);
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",
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
);
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 */
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 */
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 */
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 */
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 */
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 */
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
)) );
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
,
1553 WORD len
= CX_reg(context
);
1554 while (len
--) { *ptr
= toupper(*ptr
); ptr
++; }
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
) );
1564 case 0x23: /* DETERMINE IF CHARACTER REPRESENTS YES/NO RESPONSE */
1565 INT_BARF( context
, 0x21 );
1566 SET_CFLAG( context
);
1570 INT_BARF( context
, 0x21 );
1577 /***********************************************************************
1578 * INT21_FileAttributes
1582 * - subfunction 0x43 of function 0x71
1584 static BOOL
INT21_FileAttributes( CONTEXT86
*context
,
1588 WCHAR fileW
[MAX_PATH
];
1589 char *fileA
= CTX_SEG_OFF_TO_LIN(context
,
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
);
1609 SET_CX( context
, (WORD
)result
);
1611 SET_AX( context
, (WORD
)result
); /* DR DOS */
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
) ))
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
)
1633 SET_AX( context
, LOWORD(result
) );
1634 SET_DX( context
, HIWORD(result
) );
1638 case 0x03: /* SET FILE LAST-WRITTEN DATE AND TIME */
1640 INT_BARF( context
, 0x21 );
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
)
1652 DosDateTimeToFileTime( DI_reg(context
),
1655 status
= SetFileTime( handle
, NULL
, NULL
, &filetime
);
1657 CloseHandle( handle
);
1662 case 0x04: /* GET FILE LAST-WRITTEN DATE AND TIME */
1664 INT_BARF( context
, 0x21 );
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
)
1676 status
= GetFileTime( handle
, NULL
, NULL
, &filetime
);
1679 FileTimeToDosDateTime( &filetime
, &date
, &time
);
1680 SET_DI( context
, date
);
1681 SET_CX( context
, time
);
1684 CloseHandle( handle
);
1689 case 0x05: /* SET FILE LAST ACCESS DATE */
1691 INT_BARF( context
, 0x21 );
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
)
1703 DosDateTimeToFileTime( DI_reg(context
),
1706 status
= SetFileTime( handle
, NULL
, &filetime
, NULL
);
1708 CloseHandle( handle
);
1713 case 0x06: /* GET FILE LAST ACCESS DATE */
1715 INT_BARF( context
, 0x21 );
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
)
1727 status
= GetFileTime( handle
, NULL
, &filetime
, NULL
);
1730 FileTimeToDosDateTime( &filetime
, &date
, NULL
);
1731 SET_DI( context
, date
);
1734 CloseHandle( handle
);
1739 case 0x07: /* SET FILE CREATION DATE AND TIME */
1741 INT_BARF( context
, 0x21 );
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
)
1753 * FIXME: SI has number of 10-millisecond units past time in CX.
1755 DosDateTimeToFileTime( DI_reg(context
),
1758 status
= SetFileTime( handle
, &filetime
, NULL
, NULL
);
1760 CloseHandle( handle
);
1765 case 0x08: /* GET FILE CREATION DATE AND TIME */
1767 INT_BARF( context
, 0x21 );
1770 TRACE( "GET FILE CREATION DATE AND TIME, handle %d\n",
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
)
1779 status
= GetFileTime( handle
, &filetime
, NULL
, NULL
);
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
1789 SET_SI( context
, 0 );
1792 CloseHandle(handle
);
1797 case 0xff: /* EXTENDED-LENGTH FILENAME OPERATIONS */
1798 if (islong
|| context
->Ebp
!= 0x5053)
1799 INT_BARF( context
, 0x21 );
1802 switch(CL_reg(context
))
1805 if (!INT21_CreateDirectory( context
))
1810 if (!INT21_RenameFile( context
))
1815 INT_BARF( context
, 0x21 );
1821 INT_BARF( context
, 0x21 );
1828 /***********************************************************************
1829 * INT21_FileDateTime
1831 * Handler for function 0x57.
1833 static BOOL
INT21_FileDateTime( CONTEXT86
*context
)
1835 HANDLE handle
= DosFileHandleToWin32Handle(BX_reg(context
));
1839 switch (AL_reg(context
)) {
1840 case 0x00: /* Get last-written stamp */
1841 TRACE( "GET FILE LAST-WRITTEN DATE AND TIME, handle %d\n",
1844 if (!GetFileTime( handle
, NULL
, NULL
, &filetime
))
1846 FileTimeToDosDateTime( &filetime
, &date
, &time
);
1847 SET_DX( context
, date
);
1848 SET_CX( context
, time
);
1852 case 0x01: /* Set last-written stamp */
1853 TRACE( "SET FILE LAST-WRITTEN DATE AND TIME, handle %d\n",
1856 DosDateTimeToFileTime( DX_reg(context
),
1859 if (!SetFileTime( handle
, NULL
, NULL
, &filetime
))
1864 case 0x04: /* Get last access stamp, DOS 7 */
1865 TRACE( "GET FILE LAST ACCESS DATE AND TIME, handle %d\n",
1868 if (!GetFileTime( handle
, NULL
, &filetime
, NULL
))
1870 FileTimeToDosDateTime( &filetime
, &date
, &time
);
1871 SET_DX( context
, date
);
1872 SET_CX( context
, time
);
1876 case 0x05: /* Set last access stamp, DOS 7 */
1877 TRACE( "SET FILE LAST ACCESS DATE AND TIME, handle %d\n",
1880 DosDateTimeToFileTime( DX_reg(context
),
1883 if (!SetFileTime( handle
, NULL
, &filetime
, NULL
))
1888 case 0x06: /* Get creation stamp, DOS 7 */
1889 TRACE( "GET FILE CREATION DATE AND TIME, handle %d\n",
1892 if (!GetFileTime( handle
, &filetime
, NULL
, NULL
))
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 );
1904 case 0x07: /* Set creation stamp, DOS 7 */
1905 TRACE( "SET FILE CREATION DATE AND TIME, handle %d\n",
1909 * FIXME: SI has number of 10-millisecond units past time in CX.
1911 DosDateTimeToFileTime( DX_reg(context
),
1914 if (!SetFileTime( handle
, &filetime
, NULL
, NULL
))
1920 INT_BARF( context
, 0x21 );
1928 /***********************************************************************
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
1941 if (!ISV86(context
) && DOSVM_IsWin16())
1942 SET_BX( context
, LOWORD(GetCurrentPDB16()) );
1944 SET_BX( context
, DOSVM_psp
);
1948 /***********************************************************************
1951 * Handler for block device IOCTLs.
1953 static void INT21_Ioctl_Block( CONTEXT86
*context
)
1956 BYTE drive
= INT21_MapDrive( BL_reg(context
) );
1957 WCHAR drivespec
[4] = {'A', ':', '\\', 0};
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
);
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 );
1980 case 0x08: /* CHECK IF BLOCK DEVICE REMOVABLE */
1981 TRACE( "IOCTL - CHECK IF BLOCK DEVICE REMOVABLE - %c:\n",
1984 if (drivetype
== DRIVE_REMOVABLE
)
1985 SET_AX( context
, 0 ); /* removable */
1987 SET_AX( context
, 1 ); /* not removable */
1990 case 0x09: /* CHECK IF BLOCK DEVICE REMOTE */
1991 TRACE( "IOCTL - CHECK IF BLOCK DEVICE REMOTE - %c:\n",
1994 if (drivetype
== DRIVE_REMOTE
)
1995 SET_DX( context
, (1<<9) | (1<<12) ); /* remote + no direct IO */
1997 SET_DX( context
, 0 ); /* FIXME: use driver attr here */
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",
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
);
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
);
2029 case 0x0860: /* get device parameters */
2030 INT_Int21Handler( context
);
2033 case 0x0861: /* read logical device track */
2034 TRACE( "GENERIC IOCTL - Read logical device track - %c:\n",
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
);
2051 case 0x0866: /* get volume serial number */
2052 INT_Int21Handler( context
);
2055 case 0x086a: /* unlock logical volume */
2056 TRACE( "GENERIC IOCTL - Logical volume unlocked - %c:\n",
2060 case 0x086f: /* get drive map information */
2061 INT_Int21Handler( context
);
2065 INT_Int21Handler( context
);
2069 INT_BARF( context
, 0x21 );
2073 case 0x0e: /* GET LOGICAL DRIVE MAP */
2074 TRACE( "IOCTL - GET LOGICAL DRIVE MAP - %c:\n",
2076 /* FIXME: this is not correct if drive has mappings */
2077 SET_AL( context
, 0 ); /* drive has no mapping */
2080 case 0x0f: /* SET LOGICAL DRIVE MAP */
2081 INT_Int21Handler( context
);
2084 case 0x11: /* QUERY GENERIC IOCTL CAPABILITY */
2086 INT_BARF( context
, 0x21 );
2091 /***********************************************************************
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
);
2110 if (dev
&& !strcmpiW( dev
->name
, scsimgrW
) && AL_reg(context
) == 2)
2112 DOSVM_ASPIHandler(context
);
2116 switch (AL_reg(context
))
2118 case 0x00: /* GET DEVICE INFORMATION */
2119 TRACE( "IOCTL - GET DEVICE INFORMATION - %d\n", BX_reg(context
) );
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.
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
);
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() );
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 );
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 );
2179 case 0x0c: /* GENERIC CHARACTER DEVICE REQUEST */
2180 case 0x10: /* QUERY GENERIC IOCTL CAPABILITY */
2182 INT_BARF( context
, 0x21 );
2187 /***********************************************************************
2190 * Handler for function 0x44.
2192 static void INT21_Ioctl( CONTEXT86
*context
)
2194 switch (AL_reg(context
))
2200 INT21_Ioctl_Char( context
);
2205 INT21_Ioctl_Block( context
);
2210 INT21_Ioctl_Char( context
);
2215 INT21_Ioctl_Block( context
);
2219 INT21_Ioctl_Char( context
);
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
);
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
);
2240 INT21_Ioctl_Char( context
);
2246 INT21_Ioctl_Block( context
);
2250 INT21_Ioctl_Char( context
);
2254 INT21_Ioctl_Block( context
);
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 */
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 */
2269 case 0xe0: /* Sun PC-NFS API */
2270 TRACE( "Sun PC-NFS API\n" );
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 );
2297 switch (AL_reg(context
))
2299 case 0x0d: /* RESET DRIVE */
2300 INT_BARF( context
, 0x21 );
2303 case 0x39: /* LONG FILENAME - MAKE DIRECTORY */
2304 if (!INT21_CreateDirectory( context
))
2305 bSetDOSExtendedError
= TRUE
;
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
;
2322 case 0x3b: /* LONG FILENAME - CHANGE DIRECTORY */
2323 if (!INT21_SetCurrentDirectory( context
))
2324 bSetDOSExtendedError
= TRUE
;
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
;
2341 case 0x43: /* LONG FILENAME - EXTENDED GET/SET FILE ATTRIBUTES */
2342 if (!INT21_FileAttributes( context
, BL_reg(context
), TRUE
))
2343 bSetDOSExtendedError
= TRUE
;
2346 case 0x47: /* LONG FILENAME - GET CURRENT DIRECTORY */
2347 if (!INT21_GetCurrentDirectory( context
, TRUE
))
2348 bSetDOSExtendedError
= TRUE
;
2351 case 0x4e: /* LONG FILENAME - FIND FIRST MATCHING FILE */
2352 case 0x4f: /* LONG FILENAME - FIND NEXT MATCHING FILE */
2353 INT_Int21Handler( context
);
2356 case 0x56: /* LONG FILENAME - RENAME FILE */
2357 if (!INT21_RenameFile(context
))
2358 bSetDOSExtendedError
= TRUE
;
2361 case 0x60: /* LONG FILENAME - CONVERT PATH */
2362 INT_Int21Handler( context
);
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
;
2371 case 0xa0: /* LONG FILENAME - GET VOLUME INFORMATION */
2372 case 0xa1: /* LONG FILENAME - "FindClose" - TERMINATE DIRECTORY SEARCH */
2373 INT_Int21Handler( context
);
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
;
2389 case 0xa7: /* LONG FILENAME - CONVERT TIME */
2390 switch (BL_reg(context
))
2392 case 0x00: /* FILE TIME TO DOS TIME */
2395 FILETIME
*filetime
= CTX_SEG_OFF_TO_LIN(context
,
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
2410 SET_BH( context
, 0 );
2414 case 0x01: /* DOS TIME TO FILE TIME */
2416 FILETIME
*filetime
= CTX_SEG_OFF_TO_LIN(context
,
2420 TRACE( "LONG FILENAME - DOS TIME TO FILE TIME\n" );
2423 * FIXME: BH has number of 10-millisecond units
2426 DosDateTimeToFileTime( DX_reg(context
), CX_reg(context
),
2432 INT_BARF( context
, 0x21 );
2437 case 0xa8: /* LONG FILENAME - GENERATE SHORT FILENAME */
2438 case 0xa9: /* LONG FILENAME - SERVER CREATE OR OPEN FILE */
2439 case 0xaa: /* LONG FILENAME - SUBST */
2441 INT_BARF( context
, 0x21 );
2444 if (bSetDOSExtendedError
)
2446 SET_AX( context
, GetLastError() );
2447 SET_CFLAG( context
);
2452 /***********************************************************************
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();
2488 class = action
= locus
= 0;
2490 case ERROR_DIR_NOT_EMPTY
:
2495 case ERROR_ACCESS_DENIED
:
2496 class = EC_AccessDenied
;
2500 case ERROR_CANNOT_MAKE
:
2501 class = EC_AccessDenied
;
2505 case ERROR_DISK_FULL
:
2506 case ERROR_HANDLE_DISK_FULL
:
2507 class = EC_MediaError
;
2511 case ERROR_FILE_EXISTS
:
2512 case ERROR_ALREADY_EXISTS
:
2517 case ERROR_FILE_NOT_FOUND
:
2518 class = EC_NotFound
;
2522 case ER_GeneralFailure
:
2523 class = EC_SystemFailure
;
2527 case ERROR_INVALID_DRIVE
:
2528 class = EC_MediaError
;
2532 case ERROR_INVALID_HANDLE
:
2533 class = EC_ProgramError
;
2537 case ERROR_LOCK_VIOLATION
:
2538 class = EC_AccessDenied
;
2542 case ERROR_NO_MORE_FILES
:
2543 class = EC_MediaError
;
2548 class = EC_NotFound
;
2552 case ERROR_NOT_ENOUGH_MEMORY
:
2553 class = EC_OutOfResource
;
2557 case ERROR_PATH_NOT_FOUND
:
2558 class = EC_NotFound
;
2563 class = EC_NotFound
;
2567 case ERROR_SHARING_VIOLATION
:
2568 class = EC_Temporary
;
2572 case ERROR_TOO_MANY_OPEN_FILES
:
2573 class = EC_ProgramError
;
2578 FIXME("Unknown error %d\n", error
);
2579 class = EC_SystemFailure
;
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
,
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)
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())
2627 MZ_Exit( context
, FALSE
, 0 );
2630 case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
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
));
2643 case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
2644 TRACE("Write Character to Standard Output\n");
2645 DOSVM_PutChar(DL_reg(context
));
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 );
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
))
2662 INT21_ReadChar( &ascii
, context
);
2663 SET_AL( context
, ascii
);
2664 RESET_ZFLAG( context
);
2668 /* no character available */
2669 SET_AL( context
, 0 );
2670 SET_ZFLAG( context
);
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
) );
2685 case 0x07: /* DIRECT CHARACTER INPUT WITHOUT ECHO */
2688 TRACE("DIRECT CHARACTER INPUT WITHOUT ECHO\n");
2689 INT21_ReadChar( &ascii
, context
);
2690 SET_AL( context
, ascii
);
2694 case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
2697 TRACE("CHARACTER INPUT WITHOUT ECHO\n");
2698 INT21_ReadChar( &ascii
, context
);
2699 SET_AL( context
, ascii
);
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
);
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 );
2722 for(; data
!= p
; data
++)
2723 DOSVM_PutChar( *data
);
2725 SET_AL( context
, '$' ); /* yes, '$' (0x24) gets returned in AL */
2729 case 0x0a: /* BUFFERED INPUT */
2731 BYTE
*ptr
= CTX_SEG_OFF_TO_LIN(context
,
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
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] );
2753 ptr
[1] = (BYTE
)result
- 1;
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 */
2765 SET_AL( context
, 0 ); /* no character available */
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
);
2789 case 0x0d: /* DISK BUFFER FLUSH */
2790 TRACE("DISK BUFFER FLUSH ignored\n");
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
);
2799 case 0x0f: /* OPEN FILE USING FCB */
2800 INT21_OpenFileUsingFCB( context
);
2803 case 0x10: /* CLOSE FILE USING FCB */
2804 INT21_CloseFileUsingFCB( context
);
2807 case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
2808 case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
2809 INT_Int21Handler( context
);
2812 case 0x13: /* DELETE FILE USING FCB */
2813 INT_BARF( context
, 0x21 );
2816 case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
2817 INT21_SequentialReadFromFCB( context
);
2820 case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
2821 INT21_SequentialWriteToFCB( context
);
2824 case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
2825 case 0x17: /* RENAME FILE USING FCB */
2826 INT_BARF( context
, 0x21 );
2829 case 0x18: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
2830 SET_AL( context
, 0 );
2833 case 0x19: /* GET CURRENT DEFAULT DRIVE */
2834 SET_AL( context
, INT21_GetCurrentDrive() );
2835 TRACE( "GET CURRENT DRIVE -> %c:\n", 'A' + AL_reg( context
) );
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
) );
2847 case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
2848 case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
2849 INT_Int21Handler( context
);
2852 case 0x1d: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
2853 case 0x1e: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
2854 SET_AL( context
, 0 );
2857 case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
2858 INT_Int21Handler( context
);
2861 case 0x20: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
2862 SET_AL( context
, 0 );
2865 case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
2866 INT21_ReadRandomRecordFromFCB( context
);
2869 case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
2870 INT21_WriteRandomRecordToFCB( context
);
2873 case 0x23: /* GET FILE SIZE FOR FCB */
2874 case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
2875 INT_BARF( context
, 0x21 );
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
);
2885 DOSVM_SetRMHandler( AL_reg(context
), ptr
);
2889 case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
2890 INT_BARF( context
, 0x21 );
2893 case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
2894 INT21_RandomBlockReadFromFCB( context
);
2897 case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
2898 INT21_RandomBlockWriteToFCB( context
);
2901 case 0x29: /* PARSE FILENAME INTO FCB */
2902 INT_Int21Handler( context
);
2905 case 0x2a: /* GET SYSTEM DATE */
2906 TRACE( "GET SYSTEM DATE\n" );
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
);
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",
2930 SET_AL( context
, 0 ); /* Let's pretend we succeeded */
2934 SET_AL( context
, 0xff ); /* invalid date */
2939 case 0x2c: /* GET SYSTEM TIME */
2940 TRACE( "GET SYSTEM TIME\n" );
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 );
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 */
2958 case 0x2e: /* SET VERIFY FLAG */
2959 TRACE("SET VERIFY FLAG ignored\n");
2960 /* we cannot change the behaviour anyway, so just ignore it */
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
) );
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 */
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 );
2988 case 0x31: /* TERMINATE AND STAY RESIDENT */
2989 FIXME("TERMINATE AND STAY RESIDENT stub\n");
2992 case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
2993 INT_Int21Handler( context
);
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
);
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;
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;
3019 SET_DL( context
, DOSCONF_GetConfig()->brk_flag
);
3020 DOSCONF_GetConfig()->brk_flag
= 0;
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)*/
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 */
3039 INT_BARF( context
, 0x21 );
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
) );
3050 case 0x35: /* GET INTERRUPT VECTOR */
3051 TRACE("GET INTERRUPT VECTOR 0x%02x\n",AL_reg(context
));
3054 if (!ISV86(context
) && DOSVM_IsWin16())
3055 addr
= DOSVM_GetPMHandler16( AL_reg(context
) );
3057 addr
= DOSVM_GetRMHandler( AL_reg(context
) );
3058 context
->SegEs
= SELECTOROF(addr
);
3059 SET_BX( context
, OFFSETOF(addr
) );
3063 case 0x36: /* GET FREE DISK SPACE */
3064 INT_Int21Handler( context
);
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
, '/' );
3076 case 0x01: /*"SWITCHAR" - SET SWITCH CHARACTER*/
3077 FIXME( "SWITCHAR - SET SWITCH CHARACTER: %c\n",
3079 SET_AL( context
, 0x00 ); /* success*/
3082 INT_BARF( context
, 0x21 );
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
,
3101 SET_AX( context
, INT21_GetSystemCountryCode() );
3102 SET_BX( context
, INT21_GetSystemCountryCode() );
3105 case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
3106 if (!INT21_CreateDirectory( context
))
3107 bSetDOSExtendedError
= TRUE
;
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
;
3125 case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
3126 if (!INT21_SetCurrentDirectory( context
))
3127 bSetDOSExtendedError
= TRUE
;
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
;
3136 case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
3137 if (!INT21_CreateFile( context
, context
->Edx
, FALSE
,
3138 AL_reg(context
), 0x01 ))
3139 bSetDOSExtendedError
= TRUE
;
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
;
3148 case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
3149 TRACE( "READ from %d to %04lX:%04X for %d bytes\n",
3156 WORD count
= CX_reg(context
);
3157 BYTE
*buffer
= CTX_SEG_OFF_TO_LIN( context
,
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
)
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
);
3184 bSetDOSExtendedError
= TRUE
;
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))
3199 for(i
=0; i
<CX_reg(context
); i
++)
3200 DOSVM_PutChar(ptr
[i
]);
3201 SET_AX(context
, CX_reg(context
));
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
;
3210 SET_AX( context
, (WORD
)result
);
3215 case 0x41: /* "UNLINK" - DELETE FILE */
3217 WCHAR fileW
[MAX_PATH
];
3218 char *fileA
= CTX_SEG_OFF_TO_LIN(context
,
3222 TRACE( "UNLINK %s\n", fileA
);
3223 MultiByteToWideChar(CP_OEMCP
, 0, fileA
, -1, fileW
, MAX_PATH
);
3225 if (!DeleteFileW( fileW
))
3226 bSetDOSExtendedError
= TRUE
;
3230 case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
3231 TRACE( "LSEEK handle %d offset %ld from %s\n",
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
;
3246 SET_AX( context
, LOWORD(status
) );
3247 SET_DX( context
, HIWORD(status
) );
3252 case 0x43: /* FILE ATTRIBUTES */
3253 if (!INT21_FileAttributes( context
, AL_reg(context
), FALSE
))
3254 bSetDOSExtendedError
= TRUE
;
3257 case 0x44: /* IOCTL */
3258 INT21_Ioctl( context
);
3261 case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
3262 TRACE( "DUPLICATE FILE HANDLE %d\n", BX_reg(context
) );
3265 HFILE handle16
= HFILE_ERROR
;
3267 if (DuplicateHandle( GetCurrentProcess(),
3268 DosFileHandleToWin32Handle(BX_reg(context
)),
3269 GetCurrentProcess(),
3271 0, TRUE
, DUPLICATE_SAME_ACCESS
))
3272 handle16
= Win32HandleToDosFileHandle(handle32
);
3274 if (handle16
== HFILE_ERROR
)
3275 bSetDOSExtendedError
= TRUE
;
3277 SET_AX( context
, handle16
);
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
;
3288 case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
3289 if (!INT21_GetCurrentDirectory( context
, FALSE
))
3290 bSetDOSExtendedError
= TRUE
;
3293 case 0x48: /* ALLOCATE MEMORY */
3294 TRACE( "ALLOCATE MEMORY for %d paragraphs\n", BX_reg(context
) );
3297 DWORD bytes
= (DWORD
)BX_reg(context
) << 4;
3299 if (!ISV86(context
) && DOSVM_IsWin16())
3301 DWORD rv
= GlobalDOSAlloc16( bytes
);
3302 selector
= LOWORD( rv
);
3305 DOSMEM_GetBlock( bytes
, &selector
);
3308 SET_AX( context
, selector
);
3312 SET_AX( context
, 0x0008 ); /* insufficient memory */
3313 SET_BX( context
, DOSMEM_Available() >> 4 );
3318 case 0x49: /* FREE MEMORY */
3319 TRACE( "FREE MEMORY segment %04lX\n", context
->SegEs
);
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 */
3332 ok
= DOSMEM_FreeBlock( (void*)((DWORD
)context
->SegEs
<< 4) );
3336 TRACE("FREE MEMORY failed\n");
3338 SET_AX( context
, 0x0009 ); /* memory block address invalid */
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" );
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 */
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
);
3385 SET_CFLAG( context
);
3386 SET_AX( context
, instance
);
3391 if (!MZ_Exec( context
, program
, AL_reg(context
), paramblk
))
3392 bSetDOSExtendedError
= TRUE
;
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
) );
3402 MZ_Exit( context
, FALSE
, AL_reg(context
) );
3405 case 0x4d: /* GET RETURN CODE */
3406 TRACE("GET RETURN CODE (ERRORLEVEL)\n");
3407 SET_AX( context
, DOSVM_retval
);
3411 case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
3412 case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
3413 INT_Int21Handler( context
);
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
);
3421 case 0x51: /* GET PSP ADDRESS */
3422 INT21_GetPSP( context
);
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
) );
3434 SEGPTR ptr
= DOSMEM_LOL()->wine_rm_lol
;
3435 context
->SegEs
= SELECTOROF(ptr
);
3436 SET_BX( context
, OFFSETOF(ptr
) );
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 */
3445 case 0x56: /* "RENAME" - RENAME FILE */
3446 if (!INT21_RenameFile( context
))
3447 bSetDOSExtendedError
= TRUE
;
3450 case 0x57: /* FILE DATE AND TIME */
3451 if (!INT21_FileDateTime( context
))
3452 bSetDOSExtendedError
= TRUE
;
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 */
3463 case 0x01: /* SET ALLOCATION STRATEGY */
3464 TRACE( "SET MEMORY ALLOCATION STRATEGY to %d - ignored\n",
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 */
3473 case 0x03: /* SET UMB LINK STATE */
3474 TRACE( "SET UMB LINK STATE to %d - ignored\n",
3479 INT_BARF( context
, 0x21 );
3483 case 0x59: /* GET EXTENDED ERROR INFO */
3484 INT21_GetExtendedError( context
);
3487 case 0x5a: /* CREATE TEMPORARY FILE */
3488 INT_Int21Handler( context
);
3491 case 0x5b: /* CREATE NEW FILE */
3492 if (!INT21_CreateFile( context
, context
->Edx
, FALSE
,
3493 OF_READWRITE
| OF_SHARE_COMPAT
, 0x10 ))
3494 bSetDOSExtendedError
= TRUE
;
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
;
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
;
3520 INT_BARF( context
, 0x21 );
3525 case 0x5d: /* NETWORK 5D */
3526 FIXME( "Network function 5D not implemented.\n" );
3527 SetLastError( ER_NoNetwork
);
3528 bSetDOSExtendedError
= TRUE
;
3531 case 0x5e: /* NETWORK 5E */
3532 case 0x5f: /* NETWORK 5F */
3533 case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
3534 INT_Int21Handler( context
);
3537 case 0x61: /* UNUSED */
3538 SET_AL( context
, 0 );
3541 case 0x62: /* GET PSP ADDRESS */
3542 INT21_GetPSP( context
);
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 */
3556 case 0x64: /* OS/2 DOS BOX */
3557 INT_BARF( context
, 0x21 );
3561 case 0x65: /* EXTENDED COUNTRY INFORMATION */
3562 INT21_ExtendedCountryInformation( context
);
3565 case 0x66: /* GLOBAL CODE PAGE TABLE */
3566 switch (AL_reg(context
))
3569 TRACE( "GET GLOBAL CODE PAGE TABLE\n" );
3570 SET_BX( context
, GetOEMCP() );
3571 SET_DX( context
, GetOEMCP() );
3574 FIXME( "SET GLOBAL CODE PAGE TABLE, active %d, system %d - ignored\n",
3575 BX_reg(context
), DX_reg(context
) );
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
;
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
;
3592 case 0x69: /* DISK SERIAL NUMBER */
3593 INT_Int21Handler( context
);
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
;
3602 case 0x6b: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
3603 SET_AL( context
, 0 );
3606 case 0x6c: /* EXTENDED OPEN/CREATE */
3607 if (!INT21_CreateFile( context
, context
->Esi
, TRUE
,
3608 BX_reg(context
), DL_reg(context
) ))
3609 bSetDOSExtendedError
= TRUE
;
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 );
3618 case 0x71: /* MSDOS 7 - LONG FILENAME FUNCTIONS */
3619 INT21_LongFilename( context
);
3622 case 0x73: /* MSDOS7 - FAT32 */
3623 INT_Int21Handler( context
);
3626 case 0xdc: /* CONNECTION SERVICES - GET CONNECTION NUMBER */
3627 TRACE( "CONNECTION SERVICES - GET CONNECTION NUMBER - ignored\n" );
3630 case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
3631 TRACE( "NOVELL NETWARE - RETURN SHELL VERSION - ignored\n" );
3635 INT_BARF( context
, 0x21 );
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
,