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 */
95 CHAR file_extension[3];
96 WORD current_block_number;
97 WORD logical_record_size;
99 WORD date_of_last_write;
100 WORD time_of_last_write;
103 WORD starting_cluster;
104 WORD sequence_number;
105 BYTE file_attributes;
107 BYTE record_within_current_block;
108 BYTE random_access_record_number[4];
115 BYTE xfcb_file_attribute;
120 /***********************************************************************
123 * Reads a character from the standard input.
124 * Extended keycodes will be returned as two separate characters.
126 static BOOL INT21_ReadChar( BYTE *input, CONTEXT86 *waitctx )
128 static BYTE pending_scan = 0;
133 *input = pending_scan;
142 if (!DOSVM_Int16ReadChar( &ascii, &scan, waitctx ))
147 if (waitctx && !ascii)
154 /***********************************************************************
155 * INT21_GetSystemCountryCode
157 * Return DOS country code for default system locale.
159 static WORD INT21_GetSystemCountryCode()
162 * FIXME: Determine country code. We should probably use
163 * DOSCONF structure for that.
165 return GetSystemDefaultLangID();
169 /***********************************************************************
170 * INT21_FillCountryInformation
172 * Fill 34-byte buffer with country information data using
173 * default system locale.
175 static void INT21_FillCountryInformation( BYTE *buffer )
177 /* 00 - WORD: date format
182 *(WORD*)(buffer + 0) = 0; /* FIXME: Get from locale */
184 /* 02 - BYTE[5]: ASCIIZ currency symbol string */
185 buffer[2] = '$'; /* FIXME: Get from locale */
188 /* 07 - BYTE[2]: ASCIIZ thousands separator */
189 buffer[7] = 0; /* FIXME: Get from locale */
192 /* 09 - BYTE[2]: ASCIIZ decimal separator */
193 buffer[9] = '.'; /* FIXME: Get from locale */
196 /* 11 - BYTE[2]: ASCIIZ date separator */
197 buffer[11] = '/'; /* FIXME: Get from locale */
200 /* 13 - BYTE[2]: ASCIIZ time separator */
201 buffer[13] = ':'; /* FIXME: Get from locale */
204 /* 15 - BYTE: Currency format
205 * bit 2 = set if currency symbol replaces decimal point
206 * bit 1 = number of spaces between value and currency symbol
207 * bit 0 = 0 if currency symbol precedes value
208 * 1 if currency symbol follows value
210 buffer[15] = 0; /* FIXME: Get from locale */
212 /* 16 - BYTE: Number of digits after decimal in currency */
213 buffer[16] = 0; /* FIXME: Get from locale */
215 /* 17 - BYTE: Time format
216 * bit 0 = 0 if 12-hour clock
219 buffer[17] = 1; /* FIXME: Get from locale */
221 /* 18 - DWORD: Address of case map routine */
222 *(DWORD*)(buffer + 18) = 0; /* FIXME: ptr to case map routine */
224 /* 22 - BYTE[2]: ASCIIZ data-list separator */
225 buffer[22] = ','; /* FIXME: Get from locale */
228 /* 24 - BYTE[10]: Reserved */
229 memset( buffer + 24, 0, 10 );
233 /***********************************************************************
236 * Initialize DOS heap.
238 static void INT21_FillHeap( INT21_HEAP *heap )
240 static const char terminators[] = "\"\\./[]:|<>+=;,";
246 heap->uppercase_size = 128;
247 for (i = 0; i < 128; i++)
248 heap->uppercase_table[i] = toupper( 128 + i );
253 heap->lowercase_size = 256;
254 for (i = 0; i < 256; i++)
255 heap->lowercase_table[i] = tolower( i );
260 heap->collating_size = 256;
261 for (i = 0; i < 256; i++)
262 heap->collating_table[i] = i;
267 heap->filename_size = 8 + strlen(terminators);
268 heap->filename_illegal_size = strlen(terminators);
269 strcpy( heap->filename_illegal_table, terminators );
271 heap->filename_reserved1 = 0x01;
272 heap->filename_lowest = 0; /* FIXME: correct value? */
273 heap->filename_highest = 0xff; /* FIXME: correct value? */
274 heap->filename_reserved2 = 0x00;
275 heap->filename_exclude_first = 0x00; /* FIXME: correct value? */
276 heap->filename_exclude_last = 0x00; /* FIXME: correct value? */
277 heap->filename_reserved3 = 0x02;
280 * DBCS lead byte table. This table is empty.
283 memset( heap->dbcs_table, 0, sizeof(heap->dbcs_table) );
286 * Initialize InDos flag.
288 heap->misc_indos = 0;
292 /***********************************************************************
293 * INT21_GetHeapSelector
295 * Get segment/selector for DOS heap (INT21_HEAP).
296 * Creates and initializes heap on first call.
298 static WORD INT21_GetHeapSelector( CONTEXT86 *context )
300 static WORD heap_segment = 0;
301 static WORD heap_selector = 0;
302 static BOOL heap_initialized = FALSE;
304 if (!heap_initialized)
306 INT21_HEAP *ptr = DOSVM_AllocDataUMB( sizeof(INT21_HEAP),
309 INT21_FillHeap( ptr );
310 heap_initialized = TRUE;
313 if (!ISV86(context) && DOSVM_IsWin16())
314 return heap_selector;
320 /***********************************************************************
321 * INT21_BufferedInput
323 * Handler for function 0x0a and reading from console using
326 * Reads a string of characters from standard input until
327 * enter key is pressed. Returns either number of characters
328 * read from console including terminating CR or
329 * zero if capacity was zero.
331 static WORD INT21_BufferedInput( CONTEXT86 *context, BYTE *ptr, WORD capacity )
336 * Return immediately if capacity is zero.
346 DOSVM_Int16ReadChar( &ascii, &scan, context );
348 if (ascii == '\r' || ascii == '\n')
351 * FIXME: What should be echoed here?
353 DOSVM_PutChar( '\r' );
354 DOSVM_PutChar( '\n' );
360 * FIXME: This function is supposed to support
361 * DOS editing keys...
365 * If the buffer becomes filled to within one byte of
366 * capacity, DOS rejects all further characters up to,
367 * but not including, the terminating carriage return.
369 if (ascii != 0 && length < capacity-1)
371 DOSVM_PutChar( ascii );
379 static BYTE *GetCurrentDTA( CONTEXT86 *context )
381 TDB *pTask = GlobalLock16(GetCurrentTask());
383 /* FIXME: This assumes DTA was set correctly! */
384 return (BYTE *)CTX_SEG_OFF_TO_LIN( context, SELECTOROF(pTask->dta),
385 (DWORD)OFFSETOF(pTask->dta) );
389 /***********************************************************************
390 * INT21_OpenFileUsingFCB
392 * Handler for function 0x0f.
395 * DX:DX [I/O] File control block (FCB or XFCB) of unopened file
402 * Opens a FCB file for read/write in compatibility mode. Upon calling
403 * the FCB must have the drive_number, file_name, and file_extension
404 * fields filled and all other bytes cleared.
406 static void INT21_OpenFileUsingFCB( CONTEXT86 *context )
410 char current_directory[MAX_PATH];
415 BY_HANDLE_FILE_INFORMATION info;
418 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
419 if (fcb->drive_number == 0xff) {
420 xfcb = (struct XFCB *) fcb;
421 fcb = (struct FCB *) xfcb->fcb;
425 if (fcb->drive_number == 0) {
426 if (!GetCurrentDirectoryA(sizeof(current_directory), current_directory) ||
427 current_directory[1] != ':') {
428 TRACE("GetCurrentDirectoryA failed\n");
429 AL_result = 0xff; /* failed */
431 file_path[0] = toupper(current_directory[0]);
433 file_path[0] = 'A' + fcb->drive_number - 1;
436 if (AL_result == 0) {
439 memcpy(pos, fcb->file_name, 8);
442 pos = strchr(pos, ' ');
445 memcpy(pos, fcb->file_extension, 3);
448 pos = strchr(pos, ' ');
451 handle = (HANDLE) _lopen(file_path, OF_READWRITE);
452 if (handle == INVALID_HANDLE_VALUE) {
453 TRACE("_lopen(\"%s\") failed: INVALID_HANDLE_VALUE\n", file_path);
454 AL_result = 0xff; /* failed */
456 hfile16 = Win32HandleToDosFileHandle(handle);
457 if (hfile16 == HFILE_ERROR16) {
458 TRACE("Win32HandleToDosFileHandle(%p) failed: HFILE_ERROR\n", handle);
460 AL_result = 0xff; /* failed */
461 } else if (hfile16 > 255) {
462 TRACE("hfile16 (=%d) larger than 255 for \"%s\"\n", hfile16, file_path);
464 AL_result = 0xff; /* failed */
466 if (!GetFileInformationByHandle(handle, &info)) {
467 TRACE("GetFileInformationByHandle(%d, %p) for \"%s\" failed\n",
468 hfile16, handle, file_path);
470 AL_result = 0xff; /* failed */
472 fcb->drive_number = file_path[0] - 'A' + 1;
473 fcb->current_block_number = 0;
474 fcb->logical_record_size = 128;
475 fcb->file_size = info.nFileSizeLow;
476 FileTimeToDosDateTime(&info.ftLastWriteTime,
477 &fcb->date_of_last_write, &fcb->time_of_last_write);
478 fcb->file_number = hfile16;
479 fcb->attributes = 0xc2;
480 fcb->starting_cluster = 0; /* don't know correct init value */
481 fcb->sequence_number = 0; /* don't know correct init value */
482 fcb->file_attributes = info.dwFileAttributes;
483 /* The following fields are not initialized */
484 /* by the native function: */
486 /* record_within_current_block */
487 /* random_access_record_number */
489 TRACE("successful opened file \"%s\" as %d (handle %p)\n",
490 file_path, hfile16, handle);
491 AL_result = 0x00; /* successful */
496 SET_AL(context, AL_result);
500 /***********************************************************************
501 * INT21_CloseFileUsingFCB
503 * Handler for function 0x10.
506 * DX:DX [I/O] File control block (FCB or XFCB) of open file
515 static void INT21_CloseFileUsingFCB( CONTEXT86 *context )
521 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
522 if (fcb->drive_number == 0xff) {
523 xfcb = (struct XFCB *) fcb;
524 fcb = (struct FCB *) xfcb->fcb;
527 if (_lclose16((HFILE16) fcb->file_number) != 0) {
528 TRACE("_llclose16(%d) failed\n", fcb->file_number);
529 AL_result = 0xff; /* failed */
531 TRACE("successful closed file %d\n", fcb->file_number);
532 AL_result = 0x00; /* successful */
534 SET_AL(context, AL_result);
538 /***********************************************************************
539 * INT21_SequenialReadFromFCB
541 * Handler for function 0x14.
544 * DX:DX [I/O] File control block (FCB or XFCB) of open file
548 * 1: end of file, no data read
549 * 2: segment wrap in DTA, no data read (not returned now)
550 * 3: end of file, partial record read
553 * Reads a record with the size FCB->logical_record_size from the FCB
554 * to the disk transfer area. The position of the record is specified
555 * with FCB->current_block_number and FCB->record_within_current_block.
556 * Then FCB->current_block_number and FCB->record_within_current_block
557 * are updated to point to the next record. If a partial record is
558 * read, it is filled with zeros up to the FCB->logical_record_size.
560 static void INT21_SequenialReadFromFCB( CONTEXT86 *context )
567 BYTE *disk_transfer_area;
571 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
572 if (fcb->drive_number == 0xff) {
573 xfcb = (struct XFCB *) fcb;
574 fcb = (struct FCB *) xfcb->fcb;
577 handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
578 if (handle == INVALID_HANDLE_VALUE) {
579 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
581 AL_result = 0x01; /* end of file, no data read */
583 record_number = 128 * fcb->current_block_number + fcb->record_within_current_block;
584 position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
585 if (position != record_number * fcb->logical_record_size) {
586 TRACE("seek(%d, %ld, 0) failed with %ld\n",
587 fcb->file_number, record_number * fcb->logical_record_size, position);
588 AL_result = 0x01; /* end of file, no data read */
590 disk_transfer_area = GetCurrentDTA(context);
591 bytes_read = _lread((HFILE) handle, disk_transfer_area, fcb->logical_record_size);
592 if (bytes_read != fcb->logical_record_size) {
593 TRACE("_lread(%d, %p, %d) failed with %d\n",
594 fcb->file_number, disk_transfer_area, fcb->logical_record_size, bytes_read);
595 if (bytes_read == 0) {
596 AL_result = 0x01; /* end of file, no data read */
598 memset(&disk_transfer_area[bytes_read], 0, fcb->logical_record_size - bytes_read);
599 AL_result = 0x03; /* end of file, partial record read */
602 TRACE("successful read %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
603 bytes_read, record_number, position, fcb->file_number, handle);
604 AL_result = 0x00; /* successful */
608 if (AL_result == 0x00 || AL_result == 0x03) {
609 if (fcb->record_within_current_block == 127) {
610 fcb->record_within_current_block = 0;
611 fcb->current_block_number++;
613 fcb->record_within_current_block++;
616 SET_AL(context, AL_result);
620 /***********************************************************************
621 * INT21_SequenialWriteToFCB
623 * Handler for function 0x15.
626 * DX:DX [I/O] File control block (FCB or XFCB) of open file
631 * 2: segment wrap in DTA (not returned now)
634 * Writes a record with the size FCB->logical_record_size from the disk
635 * transfer area to the FCB. The position of the record is specified
636 * with FCB->current_block_number and FCB->record_within_current_block.
637 * Then FCB->current_block_number and FCB->record_within_current_block
638 * are updated to point to the next record.
640 static void INT21_SequenialWriteToFCB( CONTEXT86 *context )
647 BYTE *disk_transfer_area;
651 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
652 if (fcb->drive_number == 0xff) {
653 xfcb = (struct XFCB *) fcb;
654 fcb = (struct FCB *) xfcb->fcb;
657 handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
658 if (handle == INVALID_HANDLE_VALUE) {
659 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
661 AL_result = 0x01; /* disk full */
663 record_number = 128 * fcb->current_block_number + fcb->record_within_current_block;
664 position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
665 if (position != record_number * fcb->logical_record_size) {
666 TRACE("seek(%d, %ld, 0) failed with %ld\n",
667 fcb->file_number, record_number * fcb->logical_record_size, position);
668 AL_result = 0x01; /* disk full */
670 disk_transfer_area = GetCurrentDTA(context);
671 bytes_written = _lwrite((HFILE) handle, disk_transfer_area, fcb->logical_record_size);
672 if (bytes_written != fcb->logical_record_size) {
673 TRACE("_lwrite(%d, %p, %d) failed with %d\n",
674 fcb->file_number, disk_transfer_area, fcb->logical_record_size, bytes_written);
675 AL_result = 0x01; /* disk full */
677 TRACE("successful written %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
678 bytes_written, record_number, position, fcb->file_number, handle);
679 AL_result = 0x00; /* successful */
683 if (AL_result == 0x00) {
684 if (fcb->record_within_current_block == 127) {
685 fcb->record_within_current_block = 0;
686 fcb->current_block_number++;
688 fcb->record_within_current_block++;
691 SET_AL(context, AL_result);
695 /***********************************************************************
696 * INT21_ReadRandomRecordFromFCB
698 * Handler for function 0x21.
701 * DX:DX [I/O] File control block (FCB or XFCB) of open file
705 * 1: end of file, no data read
706 * 2: segment wrap in DTA, no data read (not returned now)
707 * 3: end of file, partial record read
710 * Reads a record with the size FCB->logical_record_size from
711 * the FCB to the disk transfer area. The position of the record
712 * is specified with FCB->random_access_record_number. The
713 * FCB->random_access_record_number is not updated. If a partial record
714 * is read, it is filled with zeros up to the FCB->logical_record_size.
716 static void INT21_ReadRandomRecordFromFCB( CONTEXT86 *context )
723 BYTE *disk_transfer_area;
727 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
728 if (fcb->drive_number == 0xff) {
729 xfcb = (struct XFCB *) fcb;
730 fcb = (struct FCB *) xfcb->fcb;
733 memcpy(&record_number, fcb->random_access_record_number, 4);
734 handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
735 if (handle == INVALID_HANDLE_VALUE) {
736 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
738 AL_result = 0x01; /* end of file, no data read */
740 position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
741 if (position != record_number * fcb->logical_record_size) {
742 TRACE("seek(%d, %ld, 0) failed with %ld\n",
743 fcb->file_number, record_number * fcb->logical_record_size, position);
744 AL_result = 0x01; /* end of file, no data read */
746 disk_transfer_area = GetCurrentDTA(context);
747 bytes_read = _lread((HFILE) handle, disk_transfer_area, fcb->logical_record_size);
748 if (bytes_read != fcb->logical_record_size) {
749 TRACE("_lread(%d, %p, %d) failed with %d\n",
750 fcb->file_number, disk_transfer_area, fcb->logical_record_size, bytes_read);
751 if (bytes_read == 0) {
752 AL_result = 0x01; /* end of file, no data read */
754 memset(&disk_transfer_area[bytes_read], 0, fcb->logical_record_size - bytes_read);
755 AL_result = 0x03; /* end of file, partial record read */
758 TRACE("successful read %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
759 bytes_read, record_number, position, fcb->file_number, handle);
760 AL_result = 0x00; /* successful */
764 fcb->current_block_number = record_number / 128;
765 fcb->record_within_current_block = record_number % 128;
766 SET_AL(context, AL_result);
770 /***********************************************************************
771 * INT21_WriteRandomRecordToFCB
773 * Handler for function 0x22.
776 * DX:DX [I/O] File control block (FCB or XFCB) of open file
781 * 2: segment wrap in DTA (not returned now)
784 * Writes a record with the size FCB->logical_record_size from
785 * the disk transfer area to the FCB. The position of the record
786 * is specified with FCB->random_access_record_number. The
787 * FCB->random_access_record_number is not updated.
789 static void INT21_WriteRandomRecordToFCB( CONTEXT86 *context )
796 BYTE *disk_transfer_area;
800 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
801 if (fcb->drive_number == 0xff) {
802 xfcb = (struct XFCB *) fcb;
803 fcb = (struct FCB *) xfcb->fcb;
806 memcpy(&record_number, fcb->random_access_record_number, 4);
807 handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
808 if (handle == INVALID_HANDLE_VALUE) {
809 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
811 AL_result = 0x01; /* disk full */
813 position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
814 if (position != record_number * fcb->logical_record_size) {
815 TRACE("seek(%d, %ld, 0) failed with %ld\n",
816 fcb->file_number, record_number * fcb->logical_record_size, position);
817 AL_result = 0x01; /* disk full */
819 disk_transfer_area = GetCurrentDTA(context);
820 bytes_written = _lwrite((HFILE) handle, disk_transfer_area, fcb->logical_record_size);
821 if (bytes_written != fcb->logical_record_size) {
822 TRACE("_lwrite(%d, %p, %d) failed with %d\n",
823 fcb->file_number, disk_transfer_area, fcb->logical_record_size, bytes_written);
824 AL_result = 0x01; /* disk full */
826 TRACE("successful written %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
827 bytes_written, record_number, position, fcb->file_number, handle);
828 AL_result = 0x00; /* successful */
832 fcb->current_block_number = record_number / 128;
833 fcb->record_within_current_block = record_number % 128;
834 SET_AL(context, AL_result);
838 /***********************************************************************
839 * INT21_RandomBlockReadFromFCB
841 * Handler for function 0x27.
844 * CX [I/O] Number of records to read
845 * DX:DX [I/O] File control block (FCB or XFCB) of open file
849 * 1: end of file, no data read
850 * 2: segment wrap in DTA, no data read (not returned now)
851 * 3: end of file, partial record read
854 * Reads several records with the size FCB->logical_record_size from
855 * the FCB to the disk transfer area. The number of records to be
856 * read is specified in the CX register. The position of the first
857 * record is specified with FCB->random_access_record_number. The
858 * FCB->random_access_record_number, the FCB->current_block_number
859 * and FCB->record_within_current_block are updated to point to the
860 * next record after the records read. If a partial record is read,
861 * it is filled with zeros up to the FCB->logical_record_size. The
862 * CX register is set to the number of successfully read records.
864 static void INT21_RandomBlockReadFromFCB( CONTEXT86 *context )
871 BYTE *disk_transfer_area;
872 UINT records_requested;
873 UINT bytes_requested;
878 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
879 if (fcb->drive_number == 0xff) {
880 xfcb = (struct XFCB *) fcb;
881 fcb = (struct FCB *) xfcb->fcb;
884 memcpy(&record_number, fcb->random_access_record_number, 4);
885 handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
886 if (handle == INVALID_HANDLE_VALUE) {
887 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
890 AL_result = 0x01; /* end of file, no data read */
892 position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
893 if (position != record_number * fcb->logical_record_size) {
894 TRACE("seek(%d, %ld, 0) failed with %ld\n",
895 fcb->file_number, record_number * fcb->logical_record_size, position);
897 AL_result = 0x01; /* end of file, no data read */
899 disk_transfer_area = GetCurrentDTA(context);
900 records_requested = CX_reg(context);
901 bytes_requested = (UINT) records_requested * fcb->logical_record_size;
902 bytes_read = _lread((HFILE) handle, disk_transfer_area, bytes_requested);
903 if (bytes_read != bytes_requested) {
904 TRACE("_lread(%d, %p, %d) failed with %d\n",
905 fcb->file_number, disk_transfer_area, bytes_requested, bytes_read);
906 records_read = bytes_read / fcb->logical_record_size;
907 if (bytes_read % fcb->logical_record_size == 0) {
908 AL_result = 0x01; /* end of file, no data read */
911 memset(&disk_transfer_area[bytes_read], 0, records_read * fcb->logical_record_size - bytes_read);
912 AL_result = 0x03; /* end of file, partial record read */
915 TRACE("successful read %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
916 bytes_read, record_number, position, fcb->file_number, handle);
917 records_read = records_requested;
918 AL_result = 0x00; /* successful */
922 record_number += records_read;
923 memcpy(fcb->random_access_record_number, &record_number, 4);
924 fcb->current_block_number = record_number / 128;
925 fcb->record_within_current_block = record_number % 128;
926 SET_CX(context, records_read);
927 SET_AL(context, AL_result);
931 /***********************************************************************
932 * INT21_RandomBlockWriteToFCB
934 * Handler for function 0x28.
937 * CX [I/O] Number of records to write
938 * DX:DX [I/O] File control block (FCB or XFCB) of open file
943 * 2: segment wrap in DTA (not returned now)
946 * Writes several records with the size FCB->logical_record_size from
947 * the disk transfer area to the FCB. The number of records to be
948 * written is specified in the CX register. The position of the first
949 * record is specified with FCB->random_access_record_number. The
950 * FCB->random_access_record_number, the FCB->current_block_number
951 * and FCB->record_within_current_block are updated to point to the
952 * next record after the records written. The CX register is set to
953 * the number of successfully written records.
955 static void INT21_RandomBlockWriteToFCB( CONTEXT86 *context )
962 BYTE *disk_transfer_area;
963 UINT records_requested;
964 UINT bytes_requested;
966 UINT records_written;
969 fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
970 if (fcb->drive_number == 0xff) {
971 xfcb = (struct XFCB *) fcb;
972 fcb = (struct FCB *) xfcb->fcb;
975 memcpy(&record_number, fcb->random_access_record_number, 4);
976 handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
977 if (handle == INVALID_HANDLE_VALUE) {
978 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
981 AL_result = 0x01; /* disk full */
983 position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
984 if (position != record_number * fcb->logical_record_size) {
985 TRACE("seek(%d, %ld, 0) failed with %ld\n",
986 fcb->file_number, record_number * fcb->logical_record_size, position);
988 AL_result = 0x01; /* disk full */
990 disk_transfer_area = GetCurrentDTA(context);
991 records_requested = CX_reg(context);
992 bytes_requested = (UINT) records_requested * fcb->logical_record_size;
993 bytes_written = _lwrite((HFILE) handle, disk_transfer_area, bytes_requested);
994 if (bytes_written != bytes_requested) {
995 TRACE("_lwrite(%d, %p, %d) failed with %d\n",
996 fcb->file_number, disk_transfer_area, bytes_requested, bytes_written);
997 records_written = bytes_written / fcb->logical_record_size;
998 AL_result = 0x01; /* disk full */
1000 TRACE("successful write %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
1001 bytes_written, record_number, position, fcb->file_number, handle);
1002 records_written = records_requested;
1003 AL_result = 0x00; /* successful */
1007 record_number += records_written;
1008 memcpy(fcb->random_access_record_number, &record_number, 4);
1009 fcb->current_block_number = record_number / 128;
1010 fcb->record_within_current_block = record_number % 128;
1011 SET_CX(context, records_written);
1012 SET_AL(context, AL_result);
1016 /***********************************************************************
1017 * INT21_CreateDirectory
1021 * - subfunction 0x39 of function 0x71
1022 * - subfunction 0xff of function 0x43 (CL == 0x39)
1024 static BOOL INT21_CreateDirectory( CONTEXT86 *context )
1026 WCHAR dirW[MAX_PATH];
1027 char *dirA = CTX_SEG_OFF_TO_LIN(context,
1031 TRACE( "CREATE DIRECTORY %s\n", dirA );
1033 MultiByteToWideChar(CP_OEMCP, 0, dirA, -1, dirW, MAX_PATH);
1035 if (CreateDirectoryW(dirW, NULL))
1039 * FIXME: CreateDirectory's LastErrors will clash with the ones
1040 * used by DOS. AH=39 only returns 3 (path not found) and
1041 * 5 (access denied), while CreateDirectory return several
1042 * ones. Remap some of them. -Marcus
1044 switch (GetLastError())
1046 case ERROR_ALREADY_EXISTS:
1047 case ERROR_FILENAME_EXCED_RANGE:
1048 case ERROR_DISK_FULL:
1049 SetLastError(ERROR_ACCESS_DENIED);
1059 /***********************************************************************
1060 * INT21_ExtendedCountryInformation
1062 * Handler for function 0x65.
1064 static void INT21_ExtendedCountryInformation( CONTEXT86 *context )
1066 BYTE *dataptr = CTX_SEG_OFF_TO_LIN( context, context->SegEs, context->Edi );
1068 TRACE( "GET EXTENDED COUNTRY INFORMATION, subfunction %02x\n",
1072 * Check subfunctions that are passed country and code page.
1074 if (AL_reg(context) >= 0x01 && AL_reg(context) <= 0x07)
1076 WORD country = DX_reg(context);
1077 WORD codepage = BX_reg(context);
1079 if (country != 0xffff && country != INT21_GetSystemCountryCode())
1080 FIXME( "Requested info on non-default country %04x\n", country );
1082 if (codepage != 0xffff && codepage != GetOEMCP())
1083 FIXME( "Requested info on non-default code page %04x\n", codepage );
1086 switch (AL_reg(context)) {
1087 case 0x00: /* SET GENERAL INTERNATIONALIZATION INFO */
1088 INT_BARF( context, 0x21 );
1089 SET_CFLAG( context );
1092 case 0x01: /* GET GENERAL INTERNATIONALIZATION INFO */
1093 TRACE( "Get general internationalization info\n" );
1094 dataptr[0] = 0x01; /* Info ID */
1095 *(WORD*)(dataptr+1) = 38; /* Size of the following info */
1096 *(WORD*)(dataptr+3) = INT21_GetSystemCountryCode(); /* Country ID */
1097 *(WORD*)(dataptr+5) = GetOEMCP(); /* Code page */
1098 INT21_FillCountryInformation( dataptr + 7 );
1099 SET_CX( context, 41 ); /* Size of returned info */
1102 case 0x02: /* GET POINTER TO UPPERCASE TABLE */
1103 case 0x04: /* GET POINTER TO FILENAME UPPERCASE TABLE */
1104 TRACE( "Get pointer to uppercase table\n" );
1105 dataptr[0] = AL_reg(context); /* Info ID */
1106 *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1107 offsetof(INT21_HEAP, uppercase_size) );
1108 SET_CX( context, 5 ); /* Size of returned info */
1111 case 0x03: /* GET POINTER TO LOWERCASE TABLE */
1112 TRACE( "Get pointer to lowercase table\n" );
1113 dataptr[0] = 0x03; /* Info ID */
1114 *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1115 offsetof(INT21_HEAP, lowercase_size) );
1116 SET_CX( context, 5 ); /* Size of returned info */
1119 case 0x05: /* GET POINTER TO FILENAME TERMINATOR TABLE */
1120 TRACE("Get pointer to filename terminator table\n");
1121 dataptr[0] = 0x05; /* Info ID */
1122 *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1123 offsetof(INT21_HEAP, filename_size) );
1124 SET_CX( context, 5 ); /* Size of returned info */
1127 case 0x06: /* GET POINTER TO COLLATING SEQUENCE TABLE */
1128 TRACE("Get pointer to collating sequence table\n");
1129 dataptr[0] = 0x06; /* Info ID */
1130 *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1131 offsetof(INT21_HEAP, collating_size) );
1132 SET_CX( context, 5 ); /* Size of returned info */
1135 case 0x07: /* GET POINTER TO DBCS LEAD BYTE TABLE */
1136 TRACE("Get pointer to DBCS lead byte table\n");
1137 dataptr[0] = 0x07; /* Info ID */
1138 *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1139 offsetof(INT21_HEAP, dbcs_size) );
1140 SET_CX( context, 5 ); /* Size of returned info */
1143 case 0x20: /* CAPITALIZE CHARACTER */
1144 case 0xa0: /* CAPITALIZE FILENAME CHARACTER */
1145 TRACE("Convert char to uppercase\n");
1146 SET_DL( context, toupper(DL_reg(context)) );
1149 case 0x21: /* CAPITALIZE STRING */
1150 case 0xa1: /* CAPITALIZE COUNTED FILENAME STRING */
1151 TRACE("Convert string to uppercase with length\n");
1153 char *ptr = (char *)CTX_SEG_OFF_TO_LIN( context,
1156 WORD len = CX_reg(context);
1157 while (len--) { *ptr = toupper(*ptr); ptr++; }
1161 case 0x22: /* CAPITALIZE ASCIIZ STRING */
1162 case 0xa2: /* CAPITALIZE ASCIIZ FILENAME */
1163 TRACE("Convert ASCIIZ string to uppercase\n");
1164 _strupr( (LPSTR)CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx) );
1167 case 0x23: /* DETERMINE IF CHARACTER REPRESENTS YES/NO RESPONSE */
1168 INT_BARF( context, 0x21 );
1169 SET_CFLAG( context );
1173 INT_BARF( context, 0x21 );
1180 /***********************************************************************
1181 * INT21_FileAttributes
1185 * - subfunction 0x43 of function 0x71
1187 static BOOL INT21_FileAttributes( CONTEXT86 *context,
1191 WCHAR fileW[MAX_PATH];
1192 char *fileA = CTX_SEG_OFF_TO_LIN(context,
1201 switch (subfunction)
1203 case 0x00: /* GET FILE ATTRIBUTES */
1204 TRACE( "GET FILE ATTRIBUTES for %s\n", fileA );
1205 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1207 result = GetFileAttributesW( fileW );
1212 SET_CX( context, (WORD)result );
1214 SET_AX( context, (WORD)result ); /* DR DOS */
1218 case 0x01: /* SET FILE ATTRIBUTES */
1219 TRACE( "SET FILE ATTRIBUTES 0x%02x for %s\n",
1220 CX_reg(context), fileA );
1221 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1223 if (!SetFileAttributesW( fileW, CX_reg(context) ))
1227 case 0x02: /* GET COMPRESSED FILE SIZE */
1228 TRACE( "GET COMPRESSED FILE SIZE for %s\n", fileA );
1229 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1231 result = GetCompressedFileSizeW( fileW, NULL );
1232 if (result == INVALID_FILE_SIZE)
1236 SET_AX( context, LOWORD(result) );
1237 SET_DX( context, HIWORD(result) );
1241 case 0x03: /* SET FILE LAST-WRITTEN DATE AND TIME */
1243 INT_BARF( context, 0x21 );
1246 TRACE( "SET FILE LAST-WRITTEN DATE AND TIME, file %s\n", fileA );
1247 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1249 handle = CreateFileW( fileW, GENERIC_WRITE,
1250 FILE_SHARE_READ | FILE_SHARE_WRITE,
1251 NULL, OPEN_EXISTING, 0, 0 );
1252 if (handle == INVALID_HANDLE_VALUE)
1255 DosDateTimeToFileTime( DI_reg(context),
1258 status = SetFileTime( handle, NULL, NULL, &filetime );
1260 CloseHandle( handle );
1265 case 0x04: /* GET FILE LAST-WRITTEN DATE AND TIME */
1267 INT_BARF( context, 0x21 );
1270 TRACE( "GET FILE LAST-WRITTEN DATE AND TIME, file %s\n", fileA );
1271 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1273 handle = CreateFileW( fileW, GENERIC_READ,
1274 FILE_SHARE_READ | FILE_SHARE_WRITE,
1275 NULL, OPEN_EXISTING, 0, 0 );
1276 if (handle == INVALID_HANDLE_VALUE)
1279 status = GetFileTime( handle, NULL, NULL, &filetime );
1282 FileTimeToDosDateTime( &filetime, &date, &time );
1283 SET_DI( context, date );
1284 SET_CX( context, time );
1287 CloseHandle( handle );
1292 case 0x05: /* SET FILE LAST ACCESS DATE */
1294 INT_BARF( context, 0x21 );
1297 TRACE( "SET FILE LAST ACCESS DATE, file %s\n", fileA );
1298 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1300 handle = CreateFileW( fileW, GENERIC_WRITE,
1301 FILE_SHARE_READ | FILE_SHARE_WRITE,
1302 NULL, OPEN_EXISTING, 0, 0 );
1303 if (handle == INVALID_HANDLE_VALUE)
1306 DosDateTimeToFileTime( DI_reg(context),
1309 status = SetFileTime( handle, NULL, &filetime, NULL );
1311 CloseHandle( handle );
1316 case 0x06: /* GET FILE LAST ACCESS DATE */
1318 INT_BARF( context, 0x21 );
1321 TRACE( "GET FILE LAST ACCESS DATE, file %s\n", fileA );
1322 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1324 handle = CreateFileW( fileW, GENERIC_READ,
1325 FILE_SHARE_READ | FILE_SHARE_WRITE,
1326 NULL, OPEN_EXISTING, 0, 0 );
1327 if (handle == INVALID_HANDLE_VALUE)
1330 status = GetFileTime( handle, NULL, &filetime, NULL );
1333 FileTimeToDosDateTime( &filetime, &date, NULL );
1334 SET_DI( context, date );
1337 CloseHandle( handle );
1342 case 0x07: /* SET FILE CREATION DATE AND TIME */
1344 INT_BARF( context, 0x21 );
1347 TRACE( "SET FILE CREATION DATE AND TIME, file %s\n", fileA );
1349 handle = CreateFileW( fileW, GENERIC_WRITE,
1350 FILE_SHARE_READ | FILE_SHARE_WRITE,
1351 NULL, OPEN_EXISTING, 0, 0 );
1352 if (handle == INVALID_HANDLE_VALUE)
1356 * FIXME: SI has number of 10-millisecond units past time in CX.
1358 DosDateTimeToFileTime( DI_reg(context),
1361 status = SetFileTime( handle, &filetime, NULL, NULL );
1363 CloseHandle( handle );
1368 case 0x08: /* GET FILE CREATION DATE AND TIME */
1370 INT_BARF( context, 0x21 );
1373 TRACE( "GET FILE CREATION DATE AND TIME, handle %d\n",
1376 handle = CreateFileW( fileW, GENERIC_READ,
1377 FILE_SHARE_READ | FILE_SHARE_WRITE,
1378 NULL, OPEN_EXISTING, 0, 0 );
1379 if (handle == INVALID_HANDLE_VALUE)
1382 status = GetFileTime( handle, &filetime, NULL, NULL );
1385 FileTimeToDosDateTime( &filetime, &date, &time );
1386 SET_DI( context, date );
1387 SET_CX( context, time );
1389 * FIXME: SI has number of 10-millisecond units past
1392 SET_SI( context, 0 );
1395 CloseHandle(handle);
1400 case 0xff: /* EXTENDED-LENGTH FILENAME OPERATIONS */
1401 if (islong || context->Ebp != 0x5053)
1402 INT_BARF( context, 0x21 );
1405 switch(CL_reg(context))
1408 if (!INT21_CreateDirectory( context ))
1413 if (!INT21_RenameFile( context ))
1418 INT_BARF( context, 0x21 );
1424 INT_BARF( context, 0x21 );
1431 /***********************************************************************
1432 * INT21_FileDateTime
1434 * Handler for function 0x57.
1436 static BOOL INT21_FileDateTime( CONTEXT86 *context )
1438 HANDLE handle = DosFileHandleToWin32Handle(BX_reg(context));
1442 switch (AL_reg(context)) {
1443 case 0x00: /* Get last-written stamp */
1444 TRACE( "GET FILE LAST-WRITTEN DATE AND TIME, handle %d\n",
1447 if (!GetFileTime( handle, NULL, NULL, &filetime ))
1449 FileTimeToDosDateTime( &filetime, &date, &time );
1450 SET_DX( context, date );
1451 SET_CX( context, time );
1455 case 0x01: /* Set last-written stamp */
1456 TRACE( "SET FILE LAST-WRITTEN DATE AND TIME, handle %d\n",
1459 DosDateTimeToFileTime( DX_reg(context),
1462 if (!SetFileTime( handle, NULL, NULL, &filetime ))
1467 case 0x04: /* Get last access stamp, DOS 7 */
1468 TRACE( "GET FILE LAST ACCESS DATE AND TIME, handle %d\n",
1471 if (!GetFileTime( handle, NULL, &filetime, NULL ))
1473 FileTimeToDosDateTime( &filetime, &date, &time );
1474 SET_DX( context, date );
1475 SET_CX( context, time );
1479 case 0x05: /* Set last access stamp, DOS 7 */
1480 TRACE( "SET FILE LAST ACCESS DATE AND TIME, handle %d\n",
1483 DosDateTimeToFileTime( DX_reg(context),
1486 if (!SetFileTime( handle, NULL, &filetime, NULL ))
1491 case 0x06: /* Get creation stamp, DOS 7 */
1492 TRACE( "GET FILE CREATION DATE AND TIME, handle %d\n",
1495 if (!GetFileTime( handle, &filetime, NULL, NULL ))
1497 FileTimeToDosDateTime( &filetime, &date, &time );
1498 SET_DX( context, date );
1499 SET_CX( context, time );
1501 * FIXME: SI has number of 10-millisecond units past time in CX.
1503 SET_SI( context, 0 );
1507 case 0x07: /* Set creation stamp, DOS 7 */
1508 TRACE( "SET FILE CREATION DATE AND TIME, handle %d\n",
1512 * FIXME: SI has number of 10-millisecond units past time in CX.
1514 DosDateTimeToFileTime( DX_reg(context),
1517 if (!SetFileTime( handle, &filetime, NULL, NULL ))
1523 INT_BARF( context, 0x21 );
1531 /***********************************************************************
1534 * Handler for functions 0x51 and 0x62.
1536 static void INT21_GetPSP( CONTEXT86 *context )
1538 TRACE( "GET CURRENT PSP ADDRESS (%02x)\n", AH_reg(context) );
1541 * FIXME: should we return the original DOS PSP upon
1544 if (!ISV86(context) && DOSVM_IsWin16())
1545 SET_BX( context, LOWORD(GetCurrentPDB16()) );
1547 SET_BX( context, DOSVM_psp );
1551 /***********************************************************************
1554 * Handler for block device IOCTLs.
1556 static void INT21_Ioctl_Block( CONTEXT86 *context )
1558 INT_Int21Handler( context );
1562 /***********************************************************************
1565 * Handler for character device IOCTLs.
1567 static void INT21_Ioctl_Char( CONTEXT86 *context )
1569 static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0};
1570 static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0};
1572 HANDLE handle = DosFileHandleToWin32Handle(BX_reg(context));
1573 const DOS_DEVICE *dev = DOSFS_GetDeviceByHandle(handle);
1575 if (dev && !strcmpiW( dev->name, emmxxxx0W ))
1577 EMS_Ioctl_Handler(context);
1581 if (dev && !strcmpiW( dev->name, scsimgrW ) && AL_reg(context) == 2)
1583 DOSVM_ASPIHandler(context);
1587 INT_Int21Handler( context );
1591 /***********************************************************************
1594 * Handler for function 0x44.
1596 static void INT21_Ioctl( CONTEXT86 *context )
1598 switch (AL_reg(context))
1604 INT21_Ioctl_Char( context );
1609 INT21_Ioctl_Block( context );
1614 INT21_Ioctl_Char( context );
1619 INT21_Ioctl_Block( context );
1623 INT21_Ioctl_Char( context );
1626 case 0x0b: /* SET SHARING RETRY COUNT */
1627 TRACE( "SET SHARING RETRY COUNT: Pause %d, retries %d.\n",
1628 CX_reg(context), DX_reg(context) );
1629 if (!CX_reg(context))
1631 SET_AX( context, 1 );
1632 SET_CFLAG( context );
1636 DOSMEM_LOL()->sharing_retry_delay = CX_reg(context);
1637 if (DX_reg(context))
1638 DOSMEM_LOL()->sharing_retry_count = DX_reg(context);
1639 RESET_CFLAG( context );
1644 INT21_Ioctl_Char( context );
1650 INT21_Ioctl_Block( context );
1654 INT21_Ioctl_Char( context );
1658 INT21_Ioctl_Block( context );
1661 case 0x12: /* DR DOS - DETERMINE DOS TYPE (OBSOLETE FUNCTION) */
1662 TRACE( "DR DOS - DETERMINE DOS TYPE (OBSOLETE FUNCTION)\n" );
1663 SET_CFLAG(context); /* Error / This is not DR DOS. */
1664 SET_AX( context, 0x0001 ); /* Invalid function */
1667 case 0x52: /* DR DOS - DETERMINE DOS TYPE */
1668 TRACE( "DR DOS - DETERMINE DOS TYPE\n" );
1669 SET_CFLAG(context); /* Error / This is not DR DOS. */
1670 SET_AX( context, 0x0001 ); /* Invalid function */
1673 case 0xe0: /* Sun PC-NFS API */
1674 TRACE( "Sun PC-NFS API\n" );
1679 INT_BARF( context, 0x21 );
1684 /***********************************************************************
1685 * INT21_LongFilename
1687 * Handler for function 0x71.
1689 static void INT21_LongFilename( CONTEXT86 *context )
1691 BOOL bSetDOSExtendedError = FALSE;
1693 if (HIBYTE(HIWORD(GetVersion16())) < 0x07)
1695 TRACE( "LONG FILENAME - functions supported only under DOS7\n" );
1696 SET_CFLAG( context );
1697 SET_AL( context, 0 );
1701 switch (AL_reg(context))
1703 case 0x0d: /* RESET DRIVE */
1704 INT_Int21Handler( context );
1707 case 0x39: /* LONG FILENAME - MAKE DIRECTORY */
1708 if (!INT21_CreateDirectory( context ))
1709 bSetDOSExtendedError = TRUE;
1712 case 0x3a: /* LONG FILENAME - REMOVE DIRECTORY */
1714 WCHAR dirW[MAX_PATH];
1715 char *dirA = CTX_SEG_OFF_TO_LIN(context,
1716 context->SegDs, context->Edx);
1718 TRACE( "LONG FILENAME - REMOVE DIRECTORY %s\n", dirA );
1719 MultiByteToWideChar(CP_OEMCP, 0, dirA, -1, dirW, MAX_PATH);
1721 if (!RemoveDirectoryW( dirW ))
1722 bSetDOSExtendedError = TRUE;
1726 case 0x3b: /* LONG FILENAME - CHANGE DIRECTORY */
1727 INT_Int21Handler( context );
1730 case 0x41: /* LONG FILENAME - DELETE FILE */
1732 WCHAR fileW[MAX_PATH];
1733 char *fileA = CTX_SEG_OFF_TO_LIN(context,
1734 context->SegDs, context->Edx);
1736 TRACE( "LONG FILENAME - DELETE FILE %s\n", fileA );
1737 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
1739 if (!DeleteFileW( fileW ))
1740 bSetDOSExtendedError = TRUE;
1744 case 0x43: /* LONG FILENAME - EXTENDED GET/SET FILE ATTRIBUTES */
1745 if (!INT21_FileAttributes( context, BL_reg(context), TRUE ))
1746 bSetDOSExtendedError = TRUE;
1749 case 0x47: /* LONG FILENAME - GET CURRENT DIRECTORY */
1750 case 0x4e: /* LONG FILENAME - FIND FIRST MATCHING FILE */
1751 case 0x4f: /* LONG FILENAME - FIND NEXT MATCHING FILE */
1752 INT_Int21Handler( context );
1755 case 0x56: /* LONG FILENAME - RENAME FILE */
1756 if (!INT21_RenameFile(context))
1757 bSetDOSExtendedError = TRUE;
1760 case 0x60: /* LONG FILENAME - CONVERT PATH */
1761 case 0x6c: /* LONG FILENAME - CREATE OR OPEN FILE */
1762 case 0xa0: /* LONG FILENAME - GET VOLUME INFORMATION */
1763 case 0xa1: /* LONG FILENAME - "FindClose" - TERMINATE DIRECTORY SEARCH */
1764 case 0xa6: /* LONG FILENAME - GET FILE INFO BY HANDLE */
1765 case 0xa7: /* LONG FILENAME - CONVERT TIME */
1766 case 0xa8: /* LONG FILENAME - GENERATE SHORT FILENAME */
1767 case 0xa9: /* LONG FILENAME - SERVER CREATE OR OPEN FILE */
1768 case 0xaa: /* LONG FILENAME - SUBST */
1769 INT_Int21Handler( context );
1773 INT_BARF( context, 0x21 );
1776 if (bSetDOSExtendedError)
1778 SET_AX( context, GetLastError() );
1779 SET_CFLAG( context );
1784 /***********************************************************************
1789 * - subfunction 0x56 of function 0x71
1790 * - subfunction 0xff of function 0x43 (CL == 0x56)
1792 static BOOL INT21_RenameFile( CONTEXT86 *context )
1794 WCHAR fromW[MAX_PATH];
1795 WCHAR toW[MAX_PATH];
1796 char *fromA = CTX_SEG_OFF_TO_LIN(context,
1797 context->SegDs,context->Edx);
1798 char *toA = CTX_SEG_OFF_TO_LIN(context,
1799 context->SegEs,context->Edi);
1801 TRACE( "RENAME FILE %s to %s\n", fromA, toA );
1802 MultiByteToWideChar(CP_OEMCP, 0, fromA, -1, fromW, MAX_PATH);
1803 MultiByteToWideChar(CP_OEMCP, 0, toA, -1, toW, MAX_PATH);
1805 return MoveFileW( fromW, toW );
1809 /***********************************************************************
1810 * INT21_GetExtendedError
1812 static void INT21_GetExtendedError( CONTEXT86 *context )
1814 BYTE class, action, locus;
1815 WORD error = GetLastError();
1820 class = action = locus = 0;
1822 case ERROR_DIR_NOT_EMPTY:
1827 case ERROR_ACCESS_DENIED:
1828 class = EC_AccessDenied;
1832 case ERROR_CANNOT_MAKE:
1833 class = EC_AccessDenied;
1837 case ERROR_DISK_FULL:
1838 case ERROR_HANDLE_DISK_FULL:
1839 class = EC_MediaError;
1843 case ERROR_FILE_EXISTS:
1844 case ERROR_ALREADY_EXISTS:
1849 case ERROR_FILE_NOT_FOUND:
1850 class = EC_NotFound;
1854 case ER_GeneralFailure:
1855 class = EC_SystemFailure;
1859 case ERROR_INVALID_DRIVE:
1860 class = EC_MediaError;
1864 case ERROR_INVALID_HANDLE:
1865 class = EC_ProgramError;
1869 case ERROR_LOCK_VIOLATION:
1870 class = EC_AccessDenied;
1874 case ERROR_NO_MORE_FILES:
1875 class = EC_MediaError;
1880 class = EC_NotFound;
1884 case ERROR_NOT_ENOUGH_MEMORY:
1885 class = EC_OutOfResource;
1889 case ERROR_PATH_NOT_FOUND:
1890 class = EC_NotFound;
1895 class = EC_NotFound;
1899 case ERROR_SHARING_VIOLATION:
1900 class = EC_Temporary;
1904 case ERROR_TOO_MANY_OPEN_FILES:
1905 class = EC_ProgramError;
1910 FIXME("Unknown error %d\n", error );
1911 class = EC_SystemFailure;
1916 TRACE("GET EXTENDED ERROR code 0x%02x class 0x%02x action 0x%02x locus %02x\n",
1917 error, class, action, locus );
1918 SET_AX( context, error );
1919 SET_BH( context, class );
1920 SET_BL( context, action );
1921 SET_CH( context, locus );
1925 /***********************************************************************
1926 * DOSVM_Int21Handler
1928 * Interrupt 0x21 handler.
1930 void WINAPI DOSVM_Int21Handler( CONTEXT86 *context )
1932 BOOL bSetDOSExtendedError = FALSE;
1934 TRACE( "AX=%04x BX=%04x CX=%04x DX=%04x "
1935 "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
1936 AX_reg(context), BX_reg(context),
1937 CX_reg(context), DX_reg(context),
1938 SI_reg(context), DI_reg(context),
1939 (WORD)context->SegDs, (WORD)context->SegEs,
1943 * Extended error is used by (at least) functions 0x2f to 0x62.
1944 * Function 0x59 returns extended error and error should not
1945 * be cleared before handling the function.
1947 if (AH_reg(context) >= 0x2f && AH_reg(context) != 0x59)
1950 RESET_CFLAG(context); /* Not sure if this is a good idea. */
1952 switch(AH_reg(context))
1954 case 0x00: /* TERMINATE PROGRAM */
1955 TRACE("TERMINATE PROGRAM\n");
1956 if (DOSVM_IsWin16())
1959 MZ_Exit( context, FALSE, 0 );
1962 case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
1965 TRACE("DIRECT CHARACTER INPUT WITH ECHO\n");
1966 INT21_ReadChar( &ascii, context );
1967 SET_AL( context, ascii );
1969 * FIXME: What to echo when extended keycodes are read?
1971 DOSVM_PutChar(AL_reg(context));
1975 case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
1976 TRACE("Write Character to Standard Output\n");
1977 DOSVM_PutChar(DL_reg(context));
1980 case 0x03: /* READ CHARACTER FROM STDAUX */
1981 case 0x04: /* WRITE CHARACTER TO STDAUX */
1982 case 0x05: /* WRITE CHARACTER TO PRINTER */
1983 INT_BARF( context, 0x21 );
1986 case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
1987 if (DL_reg(context) == 0xff)
1989 TRACE("Direct Console Input\n");
1991 if (INT21_ReadChar( NULL, NULL ))
1994 INT21_ReadChar( &ascii, context );
1995 SET_AL( context, ascii );
1996 RESET_ZFLAG( context );
2000 /* no character available */
2001 SET_AL( context, 0 );
2002 SET_ZFLAG( context );
2007 TRACE("Direct Console Output\n");
2008 DOSVM_PutChar(DL_reg(context));
2010 * At least DOS versions 2.1-7.0 return character
2011 * that was written in AL register.
2013 SET_AL( context, DL_reg(context) );
2017 case 0x07: /* DIRECT CHARACTER INPUT WITHOUT ECHO */
2020 TRACE("DIRECT CHARACTER INPUT WITHOUT ECHO\n");
2021 INT21_ReadChar( &ascii, context );
2022 SET_AL( context, ascii );
2026 case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
2029 TRACE("CHARACTER INPUT WITHOUT ECHO\n");
2030 INT21_ReadChar( &ascii, context );
2031 SET_AL( context, ascii );
2035 case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
2036 TRACE("WRITE '$'-terminated string from %04lX:%04X to stdout\n",
2037 context->SegDs, DX_reg(context) );
2039 LPSTR data = CTX_SEG_OFF_TO_LIN( context,
2040 context->SegDs, context->Edx );
2044 * Do NOT use strchr() to calculate the string length,
2045 * as '\0' is valid string content, too!
2046 * Maybe we should check for non-'$' strings, but DOS doesn't.
2048 while (*p != '$') p++;
2050 if (DOSVM_IsWin16())
2051 WriteFile( DosFileHandleToWin32Handle(1),
2052 data, p - data, 0, 0 );
2054 for(; data != p; data++)
2055 DOSVM_PutChar( *data );
2057 SET_AL( context, '$' ); /* yes, '$' (0x24) gets returned in AL */
2061 case 0x0a: /* BUFFERED INPUT */
2063 BYTE *ptr = CTX_SEG_OFF_TO_LIN(context,
2068 TRACE( "BUFFERED INPUT (size=%d)\n", ptr[0] );
2071 * FIXME: Some documents state that
2072 * ptr[1] holds number of chars from last input which
2073 * may be recalled on entry, other documents do not mention
2077 TRACE( "Handle old chars in buffer!\n" );
2080 * ptr[0] - capacity (includes terminating CR)
2081 * ptr[1] - characters read (excludes terminating CR)
2083 result = INT21_BufferedInput( context, ptr + 2, ptr[0] );
2085 ptr[1] = (BYTE)result - 1;
2091 case 0x0b: /* GET STDIN STATUS */
2092 TRACE( "GET STDIN STATUS\n" );
2094 if (INT21_ReadChar( NULL, NULL ))
2095 SET_AL( context, 0xff ); /* character available */
2097 SET_AL( context, 0 ); /* no character available */
2101 case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
2103 BYTE al = AL_reg(context); /* Input function to execute after flush. */
2105 TRACE( "FLUSH BUFFER AND READ STANDARD INPUT - 0x%02x\n", al );
2107 /* FIXME: buffers are not flushed */
2110 * If AL is one of 0x01, 0x06, 0x07, 0x08, or 0x0a,
2111 * int21 function identified by AL will be called.
2113 if(al == 0x01 || al == 0x06 || al == 0x07 || al == 0x08 || al == 0x0a)
2115 SET_AH( context, al );
2116 DOSVM_Int21Handler( context );
2121 case 0x0d: /* DISK BUFFER FLUSH */
2122 TRACE("DISK BUFFER FLUSH ignored\n");
2125 case 0x0e: /* SELECT DEFAULT DRIVE */
2126 INT_Int21Handler( context );
2129 case 0x0f: /* OPEN FILE USING FCB */
2130 INT21_OpenFileUsingFCB( context );
2133 case 0x10: /* CLOSE FILE USING FCB */
2134 INT21_CloseFileUsingFCB( context );
2137 case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
2138 case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
2139 INT_Int21Handler( context );
2142 case 0x13: /* DELETE FILE USING FCB */
2143 INT_BARF( context, 0x21 );
2146 case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
2147 INT21_SequenialReadFromFCB( context );
2150 case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
2151 INT21_SequenialWriteToFCB( context );
2154 case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
2155 case 0x17: /* RENAME FILE USING FCB */
2156 INT_BARF( context, 0x21 );
2159 case 0x18: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
2160 SET_AL( context, 0 );
2163 case 0x19: /* GET CURRENT DEFAULT DRIVE */
2164 INT_Int21Handler( context );
2167 case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
2168 TRACE( "SET DISK TRANSFER AREA ADDRESS %04lX:%04X\n",
2169 context->SegDs, DX_reg(context) );
2171 TDB *task = GlobalLock16( GetCurrentTask() );
2172 task->dta = MAKESEGPTR( context->SegDs, DX_reg(context) );
2176 case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
2177 case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
2178 INT_Int21Handler( context );
2181 case 0x1d: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
2182 case 0x1e: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
2183 SET_AL( context, 0 );
2186 case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
2187 INT_Int21Handler( context );
2190 case 0x20: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
2191 SET_AL( context, 0 );
2194 case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
2195 INT21_ReadRandomRecordFromFCB( context );
2198 case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
2199 INT21_WriteRandomRecordToFCB( context );
2202 case 0x23: /* GET FILE SIZE FOR FCB */
2203 case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
2204 INT_BARF( context, 0x21 );
2207 case 0x25: /* SET INTERRUPT VECTOR */
2208 TRACE("SET INTERRUPT VECTOR 0x%02x\n",AL_reg(context));
2210 FARPROC16 ptr = (FARPROC16)MAKESEGPTR( context->SegDs, DX_reg(context) );
2211 if (!ISV86(context) && DOSVM_IsWin16())
2212 DOSVM_SetPMHandler16( AL_reg(context), ptr );
2214 DOSVM_SetRMHandler( AL_reg(context), ptr );
2218 case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
2219 INT_BARF( context, 0x21 );
2222 case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
2223 INT21_RandomBlockReadFromFCB( context );
2226 case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
2227 INT21_RandomBlockWriteToFCB( context );
2230 case 0x29: /* PARSE FILENAME INTO FCB */
2231 INT_Int21Handler( context );
2234 case 0x2a: /* GET SYSTEM DATE */
2235 TRACE( "GET SYSTEM DATE\n" );
2238 GetLocalTime( &systime );
2239 SET_CX( context, systime.wYear );
2240 SET_DH( context, systime.wMonth );
2241 SET_DL( context, systime.wDay );
2242 SET_AL( context, systime.wDayOfWeek );
2246 case 0x2b: /* SET SYSTEM DATE */
2247 TRACE( "SET SYSTEM DATE\n" );
2249 WORD year = CX_reg(context);
2250 BYTE month = DH_reg(context);
2251 BYTE day = DL_reg(context);
2253 if (year >= 1980 && year <= 2099 &&
2254 month >= 1 && month <= 12 &&
2255 day >= 1 && day <= 31)
2257 FIXME( "SetSystemDate(%02d/%02d/%04d): not allowed\n",
2259 SET_AL( context, 0 ); /* Let's pretend we succeeded */
2263 SET_AL( context, 0xff ); /* invalid date */
2268 case 0x2c: /* GET SYSTEM TIME */
2269 TRACE( "GET SYSTEM TIME\n" );
2272 GetLocalTime( &systime );
2273 SET_CH( context, systime.wHour );
2274 SET_CL( context, systime.wMinute );
2275 SET_DH( context, systime.wSecond );
2276 SET_DL( context, systime.wMilliseconds / 10 );
2280 case 0x2d: /* SET SYSTEM TIME */
2281 FIXME("SetSystemTime(%02d:%02d:%02d.%02d): not allowed\n",
2282 CH_reg(context), CL_reg(context),
2283 DH_reg(context), DL_reg(context) );
2284 SET_AL( context, 0 ); /* Let's pretend we succeeded */
2287 case 0x2e: /* SET VERIFY FLAG */
2288 TRACE("SET VERIFY FLAG ignored\n");
2289 /* we cannot change the behaviour anyway, so just ignore it */
2292 case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
2293 TRACE( "GET DISK TRANSFER AREA ADDRESS\n" );
2295 TDB *task = GlobalLock16( GetCurrentTask() );
2296 context->SegEs = SELECTOROF( task->dta );
2297 SET_BX( context, OFFSETOF( task->dta ) );
2301 case 0x30: /* GET DOS VERSION */
2302 TRACE( "GET DOS VERSION - %s requested\n",
2303 (AL_reg(context) == 0x00) ? "OEM number" : "version flag" );
2305 if (AL_reg(context) == 0x00)
2306 SET_BH( context, 0xff ); /* OEM number => undefined */
2308 SET_BH( context, 0x08 ); /* version flag => DOS is in ROM */
2310 SET_AL( context, HIBYTE(HIWORD(GetVersion16())) ); /* major version */
2311 SET_AH( context, LOBYTE(HIWORD(GetVersion16())) ); /* minor version */
2313 SET_BL( context, 0x12 ); /* 0x123456 is 24-bit Wine's serial # */
2314 SET_CX( context, 0x3456 );
2317 case 0x31: /* TERMINATE AND STAY RESIDENT */
2318 FIXME("TERMINATE AND STAY RESIDENT stub\n");
2321 case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
2322 INT_Int21Handler( context );
2325 case 0x33: /* MULTIPLEXED */
2326 switch (AL_reg(context))
2328 case 0x00: /* GET CURRENT EXTENDED BREAK STATE */
2329 TRACE("GET CURRENT EXTENDED BREAK STATE\n");
2330 SET_DL( context, DOSCONF_GetConfig()->brk_flag );
2333 case 0x01: /* SET EXTENDED BREAK STATE */
2334 TRACE("SET CURRENT EXTENDED BREAK STATE\n");
2335 DOSCONF_GetConfig()->brk_flag = (DL_reg(context) > 0) ? 1 : 0;
2338 case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/
2339 TRACE("GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE\n");
2340 /* ugly coding in order to stay reentrant */
2341 if (DL_reg(context))
2343 SET_DL( context, DOSCONF_GetConfig()->brk_flag );
2344 DOSCONF_GetConfig()->brk_flag = 1;
2348 SET_DL( context, DOSCONF_GetConfig()->brk_flag );
2349 DOSCONF_GetConfig()->brk_flag = 0;
2353 case 0x05: /* GET BOOT DRIVE */
2354 TRACE("GET BOOT DRIVE\n");
2355 SET_DL( context, 3 );
2356 /* c: is Wine's bootdrive (a: is 1)*/
2359 case 0x06: /* GET TRUE VERSION NUMBER */
2360 TRACE("GET TRUE VERSION NUMBER\n");
2361 SET_BL( context, HIBYTE(HIWORD(GetVersion16())) ); /* major */
2362 SET_BH( context, LOBYTE(HIWORD(GetVersion16())) ); /* minor */
2363 SET_DL( context, 0x00 ); /* revision */
2364 SET_DH( context, 0x08 ); /* DOS is in ROM */
2368 INT_BARF( context, 0x21 );
2373 case 0x34: /* GET ADDRESS OF INDOS FLAG */
2374 TRACE( "GET ADDRESS OF INDOS FLAG\n" );
2375 context->SegEs = INT21_GetHeapSelector( context );
2376 SET_BX( context, offsetof(INT21_HEAP, misc_indos) );
2379 case 0x35: /* GET INTERRUPT VECTOR */
2380 TRACE("GET INTERRUPT VECTOR 0x%02x\n",AL_reg(context));
2383 if (!ISV86(context) && DOSVM_IsWin16())
2384 addr = DOSVM_GetPMHandler16( AL_reg(context) );
2386 addr = DOSVM_GetRMHandler( AL_reg(context) );
2387 context->SegEs = SELECTOROF(addr);
2388 SET_BX( context, OFFSETOF(addr) );
2392 case 0x36: /* GET FREE DISK SPACE */
2393 INT_Int21Handler( context );
2396 case 0x37: /* SWITCHAR */
2398 switch (AL_reg(context))
2400 case 0x00: /* "SWITCHAR" - GET SWITCH CHARACTER */
2401 TRACE( "SWITCHAR - GET SWITCH CHARACTER\n" );
2402 SET_AL( context, 0x00 ); /* success*/
2403 SET_DL( context, '/' );
2405 case 0x01: /*"SWITCHAR" - SET SWITCH CHARACTER*/
2406 FIXME( "SWITCHAR - SET SWITCH CHARACTER: %c\n",
2408 SET_AL( context, 0x00 ); /* success*/
2411 INT_BARF( context, 0x21 );
2417 case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
2418 TRACE( "GET COUNTRY-SPECIFIC INFORMATION\n" );
2419 if (AL_reg(context))
2421 WORD country = AL_reg(context);
2422 if (country == 0xff)
2423 country = BX_reg(context);
2424 if (country != INT21_GetSystemCountryCode())
2425 FIXME( "Requested info on non-default country %04x\n", country );
2427 INT21_FillCountryInformation( CTX_SEG_OFF_TO_LIN(context,
2430 SET_AX( context, INT21_GetSystemCountryCode() );
2431 SET_BX( context, INT21_GetSystemCountryCode() );
2434 case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
2435 if (!INT21_CreateDirectory( context ))
2436 bSetDOSExtendedError = TRUE;
2439 case 0x3a: /* "RMDIR" - REMOVE DIRECTORY */
2441 WCHAR dirW[MAX_PATH];
2442 char *dirA = CTX_SEG_OFF_TO_LIN(context,
2443 context->SegDs, context->Edx);
2445 TRACE( "REMOVE DIRECTORY %s\n", dirA );
2447 MultiByteToWideChar(CP_OEMCP, 0, dirA, -1, dirW, MAX_PATH);
2449 if (!RemoveDirectoryW( dirW ))
2450 bSetDOSExtendedError = TRUE;
2454 case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
2455 case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
2456 case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
2457 INT_Int21Handler( context );
2460 case 0x3e: /* "CLOSE" - CLOSE FILE */
2461 TRACE( "CLOSE handle %d\n", BX_reg(context) );
2462 if (_lclose16( BX_reg(context) ) == HFILE_ERROR16)
2463 bSetDOSExtendedError = TRUE;
2466 case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
2467 TRACE( "READ from %d to %04lX:%04X for %d bytes\n",
2474 WORD count = CX_reg(context);
2475 BYTE *buffer = CTX_SEG_OFF_TO_LIN( context,
2479 /* Some programs pass a count larger than the allocated buffer */
2480 if (DOSVM_IsWin16())
2482 WORD maxcount = GetSelectorLimit16( context->SegDs )
2483 - DX_reg(context) + 1;
2484 if (count > maxcount)
2489 * FIXME: Reading from console (BX=1) in DOS mode
2490 * does not work as it is supposed to work.
2493 if (!DOSVM_IsWin16() && BX_reg(context) == 0)
2495 result = INT21_BufferedInput( context, buffer, count );
2496 SET_AX( context, (WORD)result );
2498 else if (ReadFile( DosFileHandleToWin32Handle(BX_reg(context)),
2499 buffer, count, &result, NULL ))
2500 SET_AX( context, (WORD)result );
2502 bSetDOSExtendedError = TRUE;
2506 case 0x40: /* "WRITE" - WRITE TO FILE OR DEVICE */
2507 TRACE( "WRITE from %04lX:%04X to handle %d for %d byte\n",
2508 context->SegDs, DX_reg(context),
2509 BX_reg(context), CX_reg(context) );
2511 BYTE *ptr = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
2513 if (!DOSVM_IsWin16() &&
2514 (BX_reg(context) == 1 || BX_reg(context) == 2))
2517 for(i=0; i<CX_reg(context); i++)
2518 DOSVM_PutChar(ptr[i]);
2519 SET_AX(context, CX_reg(context));
2523 HFILE handle = (HFILE)DosFileHandleToWin32Handle(BX_reg(context));
2524 LONG result = _hwrite( handle, ptr, CX_reg(context) );
2525 if (result == HFILE_ERROR)
2526 bSetDOSExtendedError = TRUE;
2528 SET_AX( context, (WORD)result );
2533 case 0x41: /* "UNLINK" - DELETE FILE */
2535 WCHAR fileW[MAX_PATH];
2536 char *fileA = CTX_SEG_OFF_TO_LIN(context,
2540 TRACE( "UNLINK %s\n", fileA );
2541 MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
2543 if (!DeleteFileW( fileW ))
2544 bSetDOSExtendedError = TRUE;
2548 case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
2549 TRACE( "LSEEK handle %d offset %ld from %s\n",
2551 MAKELONG( DX_reg(context), CX_reg(context) ),
2552 (AL_reg(context) == 0) ?
2553 "start of file" : ((AL_reg(context) == 1) ?
2554 "current file position" : "end of file") );
2556 HANDLE handle = DosFileHandleToWin32Handle(BX_reg(context));
2557 LONG offset = MAKELONG( DX_reg(context), CX_reg(context) );
2558 DWORD status = SetFilePointer( handle, offset,
2559 NULL, AL_reg(context) );
2560 if (status == INVALID_SET_FILE_POINTER)
2561 bSetDOSExtendedError = TRUE;
2564 SET_AX( context, LOWORD(status) );
2565 SET_DX( context, HIWORD(status) );
2570 case 0x43: /* FILE ATTRIBUTES */
2571 if (!INT21_FileAttributes( context, AL_reg(context), FALSE ))
2572 bSetDOSExtendedError = TRUE;
2575 case 0x44: /* IOCTL */
2576 INT21_Ioctl( context );
2579 case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
2580 TRACE( "DUPLICATE FILE HANDLE %d\n", BX_reg(context) );
2583 HFILE handle16 = HFILE_ERROR;
2585 if (DuplicateHandle( GetCurrentProcess(),
2586 DosFileHandleToWin32Handle(BX_reg(context)),
2587 GetCurrentProcess(),
2589 0, TRUE, DUPLICATE_SAME_ACCESS ))
2590 handle16 = Win32HandleToDosFileHandle(handle32);
2592 if (handle16 == HFILE_ERROR)
2593 bSetDOSExtendedError = TRUE;
2595 SET_AX( context, handle16 );
2599 case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
2600 TRACE( "FORCEDUP - FORCE DUPLICATE FILE HANDLE %d to %d\n",
2601 BX_reg(context), CX_reg(context) );
2602 if (FILE_Dup2( BX_reg(context), CX_reg(context) ) == HFILE_ERROR16)
2603 bSetDOSExtendedError = TRUE;
2606 case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
2607 INT_Int21Handler( context );
2610 case 0x48: /* ALLOCATE MEMORY */
2611 TRACE( "ALLOCATE MEMORY for %d paragraphs\n", BX_reg(context) );
2614 DWORD bytes = (DWORD)BX_reg(context) << 4;
2616 if (!ISV86(context) && DOSVM_IsWin16())
2618 DWORD rv = GlobalDOSAlloc16( bytes );
2619 selector = LOWORD( rv );
2622 DOSMEM_GetBlock( bytes, &selector );
2625 SET_AX( context, selector );
2629 SET_AX( context, 0x0008 ); /* insufficient memory */
2630 SET_BX( context, DOSMEM_Available() >> 4 );
2635 case 0x49: /* FREE MEMORY */
2636 TRACE( "FREE MEMORY segment %04lX\n", context->SegEs );
2640 if (!ISV86(context) && DOSVM_IsWin16())
2642 ok = !GlobalDOSFree16( context->SegEs );
2644 /* If we don't reset ES_reg, we will fail in the relay code */
2649 ok = DOSMEM_FreeBlock( (void*)((DWORD)context->SegEs << 4) );
2653 TRACE("FREE MEMORY failed\n");
2655 SET_AX( context, 0x0009 ); /* memory block address invalid */
2660 case 0x4a: /* RESIZE MEMORY BLOCK */
2661 TRACE( "RESIZE MEMORY segment %04lX to %d paragraphs\n",
2662 context->SegEs, BX_reg(context) );
2664 DWORD newsize = (DWORD)BX_reg(context) << 4;
2666 if (!ISV86(context) && DOSVM_IsWin16())
2668 FIXME( "Resize memory block - unsupported under Win16\n" );
2672 LPVOID address = (void*)((DWORD)context->SegEs << 4);
2673 UINT blocksize = DOSMEM_ResizeBlock( address, newsize, FALSE );
2675 if (blocksize == (UINT)-1)
2677 SET_CFLAG( context );
2678 SET_AX( context, 0x0009 ); /* illegal address */
2680 else if(blocksize != newsize)
2682 SET_CFLAG( context );
2683 SET_AX( context, 0x0008 ); /* insufficient memory */
2684 SET_BX( context, blocksize >> 4 ); /* new block size */
2690 case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
2692 BYTE *program = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
2693 BYTE *paramblk = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx);
2695 TRACE( "EXEC %s\n", program );
2697 if (DOSVM_IsWin16())
2699 HINSTANCE16 instance = WinExec16( program, SW_NORMAL );
2702 SET_CFLAG( context );
2703 SET_AX( context, instance );
2708 if (!MZ_Exec( context, program, AL_reg(context), paramblk))
2709 bSetDOSExtendedError = TRUE;
2714 case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
2715 TRACE( "EXIT with return code %d\n", AL_reg(context) );
2716 if (DOSVM_IsWin16())
2717 ExitThread( AL_reg(context) );
2719 MZ_Exit( context, FALSE, AL_reg(context) );
2722 case 0x4d: /* GET RETURN CODE */
2723 TRACE("GET RETURN CODE (ERRORLEVEL)\n");
2724 SET_AX( context, DOSVM_retval );
2728 case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
2729 case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
2730 INT_Int21Handler( context );
2733 case 0x50: /* SET CURRENT PROCESS ID (SET PSP ADDRESS) */
2734 TRACE("SET CURRENT PROCESS ID (SET PSP ADDRESS)\n");
2735 DOSVM_psp = BX_reg(context);
2738 case 0x51: /* GET PSP ADDRESS */
2739 INT21_GetPSP( context );
2742 case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
2743 if (!ISV86(context) && DOSVM_IsWin16())
2745 SEGPTR ptr = DOSMEM_LOL()->wine_pm_lol;
2746 context->SegEs = SELECTOROF(ptr);
2747 SET_BX( context, OFFSETOF(ptr) );
2751 SEGPTR ptr = DOSMEM_LOL()->wine_rm_lol;
2752 context->SegEs = SELECTOROF(ptr);
2753 SET_BX( context, OFFSETOF(ptr) );
2757 case 0x54: /* Get Verify Flag */
2758 TRACE("Get Verify Flag - Not Supported\n");
2759 SET_AL( context, 0x00 ); /* pretend we can tell. 00h = off 01h = on */
2762 case 0x56: /* "RENAME" - RENAME FILE */
2763 if (!INT21_RenameFile( context ))
2764 bSetDOSExtendedError = TRUE;
2767 case 0x57: /* FILE DATE AND TIME */
2768 if (!INT21_FileDateTime( context ))
2769 bSetDOSExtendedError = TRUE;
2772 case 0x58: /* GET OR SET MEMORY ALLOCATION STRATEGY */
2773 switch (AL_reg(context))
2775 case 0x00: /* GET MEMORY ALLOCATION STRATEGY */
2776 TRACE( "GET MEMORY ALLOCATION STRATEGY\n" );
2777 SET_AX( context, 0 ); /* low memory first fit */
2780 case 0x01: /* SET ALLOCATION STRATEGY */
2781 TRACE( "SET MEMORY ALLOCATION STRATEGY to %d - ignored\n",
2785 case 0x02: /* GET UMB LINK STATE */
2786 TRACE( "GET UMB LINK STATE\n" );
2787 SET_AL( context, 0 ); /* UMBs not part of DOS memory chain */
2790 case 0x03: /* SET UMB LINK STATE */
2791 TRACE( "SET UMB LINK STATE to %d - ignored\n",
2796 INT_BARF( context, 0x21 );
2800 case 0x59: /* GET EXTENDED ERROR INFO */
2801 INT21_GetExtendedError( context );
2804 case 0x5a: /* CREATE TEMPORARY FILE */
2805 case 0x5b: /* CREATE NEW FILE */
2806 INT_Int21Handler( context );
2809 case 0x5c: /* "FLOCK" - RECORD LOCKING */
2811 DWORD offset = MAKELONG(DX_reg(context), CX_reg(context));
2812 DWORD length = MAKELONG(DI_reg(context), SI_reg(context));
2813 HANDLE handle = DosFileHandleToWin32Handle(BX_reg(context));
2815 switch (AL_reg(context))
2817 case 0x00: /* LOCK */
2818 TRACE( "lock handle %d offset %ld length %ld\n",
2819 BX_reg(context), offset, length );
2820 if (!LockFile( handle, offset, 0, length, 0 ))
2821 bSetDOSExtendedError = TRUE;
2824 case 0x01: /* UNLOCK */
2825 TRACE( "unlock handle %d offset %ld length %ld\n",
2826 BX_reg(context), offset, length );
2827 if (!UnlockFile( handle, offset, 0, length, 0 ))
2828 bSetDOSExtendedError = TRUE;
2832 INT_BARF( context, 0x21 );
2837 case 0x5d: /* NETWORK 5D */
2838 FIXME( "Network function 5D not implemented.\n" );
2839 SetLastError( ER_NoNetwork );
2840 bSetDOSExtendedError = TRUE;
2843 case 0x5e: /* NETWORK 5E */
2844 case 0x5f: /* NETWORK 5F */
2845 case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
2846 INT_Int21Handler( context );
2849 case 0x61: /* UNUSED */
2850 SET_AL( context, 0 );
2853 case 0x62: /* GET PSP ADDRESS */
2854 INT21_GetPSP( context );
2857 case 0x63: /* MISC. LANGUAGE SUPPORT */
2858 switch (AL_reg(context)) {
2859 case 0x00: /* GET DOUBLE BYTE CHARACTER SET LEAD-BYTE TABLE */
2860 TRACE( "GET DOUBLE BYTE CHARACTER SET LEAD-BYTE TABLE\n" );
2861 context->SegDs = INT21_GetHeapSelector( context );
2862 SET_SI( context, offsetof(INT21_HEAP, dbcs_table) );
2863 SET_AL( context, 0 ); /* success */
2868 case 0x64: /* OS/2 DOS BOX */
2869 INT_BARF( context, 0x21 );
2873 case 0x65: /* EXTENDED COUNTRY INFORMATION */
2874 INT21_ExtendedCountryInformation( context );
2877 case 0x66: /* GLOBAL CODE PAGE TABLE */
2878 switch (AL_reg(context))
2881 TRACE( "GET GLOBAL CODE PAGE TABLE\n" );
2882 SET_BX( context, GetOEMCP() );
2883 SET_DX( context, GetOEMCP() );
2886 FIXME( "SET GLOBAL CODE PAGE TABLE, active %d, system %d - ignored\n",
2887 BX_reg(context), DX_reg(context) );
2892 case 0x67: /* SET HANDLE COUNT */
2893 TRACE( "SET HANDLE COUNT to %d\n", BX_reg(context) );
2894 if (SetHandleCount( BX_reg(context) ) < BX_reg(context) )
2895 bSetDOSExtendedError = TRUE;
2898 case 0x68: /* "FFLUSH" - COMMIT FILE */
2899 TRACE( "FFLUSH - handle %d\n", BX_reg(context) );
2900 if (!FlushFileBuffers( DosFileHandleToWin32Handle(BX_reg(context)) ))
2901 bSetDOSExtendedError = TRUE;
2904 case 0x69: /* DISK SERIAL NUMBER */
2905 INT_Int21Handler( context );
2908 case 0x6a: /* COMMIT FILE */
2909 TRACE( "COMMIT FILE - handle %d\n", BX_reg(context) );
2910 if (!FlushFileBuffers( DosFileHandleToWin32Handle(BX_reg(context)) ))
2911 bSetDOSExtendedError = TRUE;
2914 case 0x6b: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
2915 SET_AL( context, 0 );
2918 case 0x6c: /* EXTENDED OPEN/CREATE */
2919 INT_Int21Handler( context );
2922 case 0x70: /* MSDOS 7 - GET/SET INTERNATIONALIZATION INFORMATION */
2923 FIXME( "MS-DOS 7 - GET/SET INTERNATIONALIZATION INFORMATION\n" );
2924 SET_CFLAG( context );
2925 SET_AL( context, 0 );
2928 case 0x71: /* MSDOS 7 - LONG FILENAME FUNCTIONS */
2929 INT21_LongFilename( context );
2932 case 0x73: /* MSDOS7 - FAT32 */
2933 INT_Int21Handler( context );
2936 case 0xdc: /* CONNECTION SERVICES - GET CONNECTION NUMBER */
2937 TRACE( "CONNECTION SERVICES - GET CONNECTION NUMBER - ignored\n" );
2940 case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
2941 TRACE( "NOVELL NETWARE - RETURN SHELL VERSION - ignored\n" );
2945 INT_BARF( context, 0x21 );
2948 } /* END OF SWITCH */
2950 /* Set general error condition. */
2951 if (bSetDOSExtendedError)
2953 SET_AX( context, GetLastError() );
2954 SET_CFLAG( context );
2957 /* Print error code if carry flag is set. */
2958 if (context->EFlags & 0x0001)
2959 TRACE("failed, error %ld\n", GetLastError() );
2961 TRACE( "returning: AX=%04x BX=%04x CX=%04x DX=%04x "
2962 "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
2963 AX_reg(context), BX_reg(context),
2964 CX_reg(context), DX_reg(context),
2965 SI_reg(context), DI_reg(context),
2966 (WORD)context->SegDs, (WORD)context->SegEs,