2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: dos/dos32krnl/dos.c
5 * PURPOSE: DOS32 Kernel
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 /* INCLUDES *******************************************************************/
25 #include "bios/bios.h"
28 #include "hardware/ps2.h"
32 /* PRIVATE VARIABLES **********************************************************/
34 #define INDOS_POINTER MAKELONG(0x00FE, 0x0070)
36 CALLBACK16 DosContext
;
38 static DWORD DiskTransferArea
;
39 /*static*/ BYTE CurrentDrive
;
40 static CHAR LastDrive
= 'E';
41 static CHAR CurrentDirectories
[NUM_DRIVES
][DOS_DIR_LENGTH
];
42 static DOS_SFT_ENTRY DosSystemFileTable
[DOS_SFT_SIZE
];
43 static WORD DosErrorLevel
= 0x0000;
46 /* PUBLIC VARIABLES ***********************************************************/
48 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
49 BOOLEAN DoEcho
= FALSE
;
50 WORD CurrentPsp
= SYSTEM_PSP
;
51 WORD DosLastError
= 0;
53 /* PRIVATE FUNCTIONS **********************************************************/
55 static WORD
DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL
,
58 PCHAR Ptr
, DestBuffer
= NULL
;
62 /* If we have an environment strings list, compute its size */
65 /* Calculate the size of the environment block */
66 Ptr
= (PCHAR
)Environment
;
67 while (*Ptr
) Ptr
+= strlen(Ptr
) + 1;
68 TotalSize
= (ULONG_PTR
)Ptr
- (ULONG_PTR
)Environment
;
72 /* Empty environment string */
75 /* Add the final environment block NULL-terminator */
78 /* Add the two bytes for the program name tag */
81 /* Add the string buffer size */
82 TotalSize
+= strlen(ProgramName
) + 1;
84 /* Allocate the memory for the environment block */
85 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
86 if (!DestSegment
) return 0;
88 DestBuffer
= (PCHAR
)SEG_OFF_TO_PTR(DestSegment
, 0);
90 /* If we have an environment strings list, copy it */
93 Ptr
= (PCHAR
)Environment
;
96 /* Copy the string and NULL-terminate it */
97 strcpy(DestBuffer
, Ptr
);
98 DestBuffer
+= strlen(Ptr
);
99 *(DestBuffer
++) = '\0';
101 /* Move to the next string */
102 Ptr
+= strlen(Ptr
) + 1;
107 /* Empty environment string */
108 *(DestBuffer
++) = '\0';
110 /* NULL-terminate the environment block */
111 *(DestBuffer
++) = '\0';
113 /* Store the special program name tag */
114 *(DestBuffer
++) = LOBYTE(DOS_PROGRAM_NAME_TAG
);
115 *(DestBuffer
++) = HIBYTE(DOS_PROGRAM_NAME_TAG
);
117 /* Copy the program name after the environment block */
118 strcpy(DestBuffer
, ProgramName
);
123 /* Taken from base/shell/cmd/console.c */
124 static BOOL
IsConsoleHandle(HANDLE hHandle
)
128 /* Check whether the handle may be that of a console... */
129 if ((GetFileType(hHandle
) & FILE_TYPE_CHAR
) == 0) return FALSE
;
132 * It may be. Perform another test... The idea comes from the
133 * MSDN description of the WriteConsole API:
135 * "WriteConsole fails if it is used with a standard handle
136 * that is redirected to a file. If an application processes
137 * multilingual output that can be redirected, determine whether
138 * the output handle is a console handle (one method is to call
139 * the GetConsoleMode function and check whether it succeeds).
140 * If the handle is a console handle, call WriteConsole. If the
141 * handle is not a console handle, the output is redirected and
142 * you should call WriteFile to perform the I/O."
144 return GetConsoleMode(hHandle
, &dwMode
);
147 static inline PDOS_SFT_ENTRY
DosFindFreeSftEntry(VOID
)
151 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
153 if (DosSystemFileTable
[i
].Type
== DOS_SFT_ENTRY_NONE
)
155 return &DosSystemFileTable
[i
];
162 static inline PDOS_SFT_ENTRY
DosFindWin32SftEntry(HANDLE Handle
)
166 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
168 if (DosSystemFileTable
[i
].Type
== DOS_SFT_ENTRY_WIN32
169 && DosSystemFileTable
[i
].Handle
== Handle
)
171 return &DosSystemFileTable
[i
];
178 static inline PDOS_SFT_ENTRY
DosFindDeviceSftEntry(PDOS_DEVICE_NODE Device
)
182 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
184 if (DosSystemFileTable
[i
].Type
== DOS_SFT_ENTRY_DEVICE
185 && DosSystemFileTable
[i
].DeviceNode
== Device
)
187 return &DosSystemFileTable
[i
];
194 WORD
DosOpenHandle(HANDLE Handle
)
199 PDOS_SFT_ENTRY SftEntry
;
201 /* The system PSP has no handle table */
202 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
204 /* Get a pointer to the handle table */
205 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
206 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
208 /* Find a free entry in the JFT */
209 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
211 if (HandleTable
[DosHandle
] == 0xFF) break;
214 /* If there are no free entries, fail */
215 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
217 /* Check if the handle is already in the SFT */
218 SftEntry
= DosFindWin32SftEntry(Handle
);
219 if (SftEntry
!= NULL
)
221 /* Already in the table, reference it */
222 SftEntry
->RefCount
++;
226 /* Find a free SFT entry to use */
227 SftEntry
= DosFindFreeSftEntry();
228 if (SftEntry
== NULL
)
230 /* The SFT is full */
231 return INVALID_DOS_HANDLE
;
234 /* Initialize the empty table entry */
235 SftEntry
->Type
= DOS_SFT_ENTRY_WIN32
;
236 SftEntry
->Handle
= Handle
;
237 SftEntry
->RefCount
= 1;
241 /* Set the JFT entry to that SFT index */
242 HandleTable
[DosHandle
] = ARRAY_INDEX(SftEntry
, DosSystemFileTable
);
244 /* Return the new handle */
248 WORD
DosOpenDevice(PDOS_DEVICE_NODE Device
)
253 PDOS_SFT_ENTRY SftEntry
;
255 DPRINT("DosOpenDevice(\"%Z\")\n", &Device
->Name
);
257 /* The system PSP has no handle table */
258 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
260 /* Get a pointer to the handle table */
261 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
262 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
264 /* Find a free entry in the JFT */
265 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
267 if (HandleTable
[DosHandle
] == 0xFF) break;
270 /* If there are no free entries, fail */
271 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
273 /* Check if the device is already in the SFT */
274 SftEntry
= DosFindDeviceSftEntry(Device
);
275 if (SftEntry
!= NULL
)
277 /* Already in the table, reference it */
278 SftEntry
->RefCount
++;
282 /* Find a free SFT entry to use */
283 SftEntry
= DosFindFreeSftEntry();
284 if (SftEntry
== NULL
)
286 /* The SFT is full */
287 return INVALID_DOS_HANDLE
;
290 /* Initialize the empty table entry */
291 SftEntry
->Type
= DOS_SFT_ENTRY_DEVICE
;
292 SftEntry
->DeviceNode
= Device
;
293 SftEntry
->RefCount
= 1;
297 /* Call the open routine, if it exists */
298 if (Device
->OpenRoutine
) Device
->OpenRoutine(Device
);
300 /* Set the JFT entry to that SFT index */
301 HandleTable
[DosHandle
] = ARRAY_INDEX(SftEntry
, DosSystemFileTable
);
303 /* Return the new handle */
307 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
313 /* Clear the table first */
314 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
316 /* Check if this is the initial process */
317 if (CurrentPsp
== SYSTEM_PSP
)
319 PDOS_SFT_ENTRY SftEntry
;
320 HANDLE StandardHandles
[3];
321 PDOS_DEVICE_NODE Con
= DosGetDevice("CON");
324 /* Get the native standard handles */
325 StandardHandles
[0] = GetStdHandle(STD_INPUT_HANDLE
);
326 StandardHandles
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
327 StandardHandles
[2] = GetStdHandle(STD_ERROR_HANDLE
);
329 for (i
= 0; i
< 3; i
++)
331 /* Find the corresponding SFT entry */
332 if (IsConsoleHandle(StandardHandles
[i
]))
334 SftEntry
= DosFindDeviceSftEntry(Con
);
338 SftEntry
= DosFindWin32SftEntry(StandardHandles
[i
]);
341 if (SftEntry
== NULL
)
343 /* Create a new SFT entry for it */
344 SftEntry
= DosFindFreeSftEntry();
345 if (SftEntry
== NULL
)
347 DPRINT1("Cannot create standard handle %d, the SFT is full!\n", i
);
351 SftEntry
->RefCount
= 0;
353 if (IsConsoleHandle(StandardHandles
[i
]))
355 SftEntry
->Type
= DOS_SFT_ENTRY_DEVICE
;
356 SftEntry
->DeviceNode
= Con
;
358 /* Call the open routine */
359 if (Con
->OpenRoutine
) Con
->OpenRoutine(Con
);
363 SftEntry
->Type
= DOS_SFT_ENTRY_WIN32
;
364 SftEntry
->Handle
= StandardHandles
[i
];
368 SftEntry
->RefCount
++;
369 DestinationTable
[i
] = ARRAY_INDEX(SftEntry
, DosSystemFileTable
);
374 /* Get the parent PSP block and handle table */
375 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
376 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
378 /* Copy the first 20 handles into the new table */
379 for (i
= 0; i
< DEFAULT_JFT_SIZE
; i
++)
381 DestinationTable
[i
] = SourceTable
[i
];
383 /* Increase the reference count */
384 DosSystemFileTable
[SourceTable
[i
]].RefCount
++;
389 static BOOLEAN
DosResizeHandleTable(WORD NewSize
)
395 /* Get the PSP block */
396 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
398 if (NewSize
== PspBlock
->HandleTableSize
)
404 if (PspBlock
->HandleTableSize
> DEFAULT_JFT_SIZE
)
406 /* Get the segment of the current table */
407 Segment
= (LOWORD(PspBlock
->HandleTablePtr
) >> 4) + HIWORD(PspBlock
->HandleTablePtr
);
409 if (NewSize
<= DEFAULT_JFT_SIZE
)
411 /* Get the current handle table */
412 HandleTable
= FAR_POINTER(PspBlock
->HandleTablePtr
);
414 /* Copy it to the PSP */
415 RtlCopyMemory(PspBlock
->HandleTable
, HandleTable
, NewSize
);
417 /* Free the memory */
418 DosFreeMemory(Segment
);
420 /* Update the handle table pointer and size */
421 PspBlock
->HandleTableSize
= NewSize
;
422 PspBlock
->HandleTablePtr
= MAKELONG(0x18, CurrentPsp
);
426 /* Resize the memory */
427 if (!DosResizeMemory(Segment
, NewSize
, NULL
))
429 /* Unable to resize, try allocating it somewhere else */
430 Segment
= DosAllocateMemory(NewSize
, NULL
);
431 if (Segment
== 0) return FALSE
;
433 /* Get the new handle table */
434 HandleTable
= SEG_OFF_TO_PTR(Segment
, 0);
436 /* Copy the handles to the new table */
437 RtlCopyMemory(HandleTable
,
438 FAR_POINTER(PspBlock
->HandleTablePtr
),
439 PspBlock
->HandleTableSize
);
441 /* Update the handle table pointer */
442 PspBlock
->HandleTablePtr
= MAKELONG(0, Segment
);
445 /* Update the handle table size */
446 PspBlock
->HandleTableSize
= NewSize
;
449 else if (NewSize
> DEFAULT_JFT_SIZE
)
451 Segment
= DosAllocateMemory(NewSize
, NULL
);
452 if (Segment
== 0) return FALSE
;
454 /* Get the new handle table */
455 HandleTable
= SEG_OFF_TO_PTR(Segment
, 0);
457 /* Copy the handles from the PSP to the new table */
458 RtlCopyMemory(HandleTable
,
459 FAR_POINTER(PspBlock
->HandleTablePtr
),
460 PspBlock
->HandleTableSize
);
462 /* Update the handle table pointer and size */
463 PspBlock
->HandleTableSize
= NewSize
;
464 PspBlock
->HandleTablePtr
= MAKELONG(0, Segment
);
470 static BOOLEAN
DosCloseHandle(WORD DosHandle
)
474 PDOS_SFT_ENTRY SftEntry
;
476 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle
);
478 /* The system PSP has no handle table */
479 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
481 /* Get a pointer to the handle table */
482 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
483 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
485 /* Make sure the handle is open */
486 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
488 /* Make sure the SFT entry is valid */
489 SftEntry
= &DosSystemFileTable
[HandleTable
[DosHandle
]];
490 if (SftEntry
->Type
== DOS_SFT_ENTRY_NONE
) return FALSE
;
492 /* Decrement the reference count of the SFT entry */
493 SftEntry
->RefCount
--;
495 /* Check if the reference count fell to zero */
496 if (!SftEntry
->RefCount
)
498 switch (SftEntry
->Type
)
500 case DOS_SFT_ENTRY_WIN32
:
502 /* Close the win32 handle and clear it */
503 CloseHandle(SftEntry
->Handle
);
508 case DOS_SFT_ENTRY_DEVICE
:
510 PDOS_DEVICE_NODE Node
= SftEntry
->DeviceNode
;
512 /* Call the close routine, if it exists */
513 if (Node
->CloseRoutine
) SftEntry
->DeviceNode
->CloseRoutine(SftEntry
->DeviceNode
);
520 /* Shouldn't happen */
525 /* Invalidate the SFT entry */
526 SftEntry
->Type
= DOS_SFT_ENTRY_NONE
;
529 /* Clear the entry in the JFT */
530 HandleTable
[DosHandle
] = 0xFF;
535 static BOOLEAN
DosDuplicateHandle(WORD OldHandle
, WORD NewHandle
)
541 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
545 /* The system PSP has no handle table */
546 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
548 /* Get a pointer to the handle table */
549 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
550 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
552 /* Make sure the old handle is open */
553 if (HandleTable
[OldHandle
] == 0xFF) return FALSE
;
555 /* Check if the new handle is open */
556 if (HandleTable
[NewHandle
] != 0xFF)
559 DosCloseHandle(NewHandle
);
562 /* Increment the reference count of the SFT entry */
563 SftIndex
= HandleTable
[OldHandle
];
564 DosSystemFileTable
[SftIndex
].RefCount
++;
566 /* Make the new handle point to that SFT entry */
567 HandleTable
[NewHandle
] = SftIndex
;
573 static BOOLEAN
DosChangeDrive(BYTE Drive
)
575 WCHAR DirectoryPath
[DOS_CMDLINE_LENGTH
];
577 /* Make sure the drive exists */
578 if (Drive
> (LastDrive
- 'A')) return FALSE
;
580 /* Find the path to the new current directory */
581 swprintf(DirectoryPath
, L
"%c\\%S", Drive
+ 'A', CurrentDirectories
[Drive
]);
583 /* Change the current directory of the process */
584 if (!SetCurrentDirectory(DirectoryPath
)) return FALSE
;
586 /* Set the current drive */
587 CurrentDrive
= Drive
;
593 static BOOLEAN
DosChangeDirectory(LPSTR Directory
)
599 /* Make sure the directory path is not too long */
600 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
602 DosLastError
= ERROR_PATH_NOT_FOUND
;
606 /* Get the drive number */
607 DriveNumber
= Directory
[0] - 'A';
609 /* Make sure the drive exists */
610 if (DriveNumber
> (LastDrive
- 'A'))
612 DosLastError
= ERROR_PATH_NOT_FOUND
;
616 /* Get the file attributes */
617 Attributes
= GetFileAttributesA(Directory
);
619 /* Make sure the path exists and is a directory */
620 if ((Attributes
== INVALID_FILE_ATTRIBUTES
)
621 || !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
623 DosLastError
= ERROR_PATH_NOT_FOUND
;
627 /* Check if this is the current drive */
628 if (DriveNumber
== CurrentDrive
)
630 /* Change the directory */
631 if (!SetCurrentDirectoryA(Directory
))
633 DosLastError
= LOWORD(GetLastError());
638 /* Get the directory part of the path */
639 Path
= strchr(Directory
, '\\');
642 /* Skip the backslash */
646 /* Set the directory for the drive */
649 strncpy(CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
653 CurrentDirectories
[DriveNumber
][0] = '\0';
660 static BOOLEAN
DosControlBreak(VOID
)
664 /* Call interrupt 0x23 */
665 Int32Call(&DosContext
, 0x23);
669 DosTerminateProcess(CurrentPsp
, 0, 0);
676 /* PUBLIC FUNCTIONS ***********************************************************/
678 PDOS_SFT_ENTRY
DosGetSftEntry(WORD DosHandle
)
683 /* The system PSP has no handle table */
684 if (CurrentPsp
== SYSTEM_PSP
) return NULL
;
686 /* Get a pointer to the handle table */
687 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
688 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
690 /* Make sure the handle is open */
691 if (HandleTable
[DosHandle
] == 0xFF) return NULL
;
693 /* Return a pointer to the SFT entry */
694 return &DosSystemFileTable
[HandleTable
[DosHandle
]];
697 VOID
DosInitializePsp(WORD PspSegment
,
703 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
704 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
706 RtlZeroMemory(PspBlock
, sizeof(*PspBlock
));
708 /* Set the exit interrupt */
709 PspBlock
->Exit
[0] = 0xCD; // int 0x20
710 PspBlock
->Exit
[1] = 0x20;
712 /* Set the number of the last paragraph */
713 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
715 /* Save the interrupt vectors */
716 PspBlock
->TerminateAddress
= ReturnAddress
;
717 PspBlock
->BreakAddress
= IntVecTable
[0x23];
718 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
720 /* Set the parent PSP */
721 PspBlock
->ParentPsp
= CurrentPsp
;
723 /* Copy the parent handle table */
724 DosCopyHandleTable(PspBlock
->HandleTable
);
726 /* Set the environment block */
727 PspBlock
->EnvBlock
= Environment
;
729 /* Set the handle table pointers to the internal handle table */
730 PspBlock
->HandleTableSize
= 20;
731 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
733 /* Set the DOS version */
734 PspBlock
->DosVersion
= DOS_VERSION
;
736 /* Set the far call opcodes */
737 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
738 PspBlock
->FarCall
[1] = 0x21;
739 PspBlock
->FarCall
[2] = 0xCB; // retf
741 /* Set the command line */
742 PspBlock
->CommandLineSize
= (BYTE
)min(strlen(CommandLine
), DOS_CMDLINE_LENGTH
- 1);
743 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
744 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
747 DWORD
DosLoadExecutable(IN DOS_EXEC_TYPE LoadType
,
748 IN LPCSTR ExecutablePath
,
749 IN LPCSTR CommandLine
,
750 IN LPCSTR Environment OPTIONAL
,
751 IN DWORD ReturnAddress OPTIONAL
,
752 OUT PDWORD StackLocation OPTIONAL
,
753 OUT PDWORD EntryPoint OPTIONAL
)
755 DWORD Result
= ERROR_SUCCESS
;
756 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
757 LPBYTE Address
= NULL
;
761 DWORD i
, FileSize
, ExeSize
;
762 PIMAGE_DOS_HEADER Header
;
763 PDWORD RelocationTable
;
765 LPSTR CmdLinePtr
= (LPSTR
)CommandLine
;
767 DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
771 Environment
? Environment
: "n/a",
775 if (LoadType
== DOS_LOAD_OVERLAY
)
777 DPRINT1("Overlay loading is not supported yet.\n");
778 return ERROR_NOT_SUPPORTED
;
781 /* NULL-terminate the command line by removing the return carriage character */
782 while (*CmdLinePtr
&& *CmdLinePtr
!= '\r') CmdLinePtr
++;
785 /* Open a handle to the executable */
786 FileHandle
= CreateFileA(ExecutablePath
,
791 FILE_ATTRIBUTE_NORMAL
,
793 if (FileHandle
== INVALID_HANDLE_VALUE
)
795 Result
= GetLastError();
799 /* Get the file size */
800 FileSize
= GetFileSize(FileHandle
, NULL
);
802 /* Create a mapping object for the file */
803 FileMapping
= CreateFileMapping(FileHandle
,
809 if (FileMapping
== NULL
)
811 Result
= GetLastError();
815 /* Map the file into memory */
816 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
819 Result
= GetLastError();
823 /* Copy the environment block to DOS memory */
824 EnvBlock
= DosCopyEnvironmentBlock(Environment
, ExecutablePath
);
827 Result
= ERROR_NOT_ENOUGH_MEMORY
;
831 /* Check if this is an EXE file or a COM file */
832 if (Address
[0] == 'M' && Address
[1] == 'Z')
836 /* Get the MZ header */
837 Header
= (PIMAGE_DOS_HEADER
)Address
;
839 /* Get the base size of the file, in paragraphs (rounded up) */
840 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
842 /* Add the PSP size, in paragraphs */
843 ExeSize
+= sizeof(DOS_PSP
) >> 4;
845 /* Add the maximum size that should be allocated */
846 ExeSize
+= Header
->e_maxalloc
;
848 /* Make sure it does not pass 0xFFFF */
849 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
851 /* Try to allocate that much memory */
852 Segment
= DosAllocateMemory((WORD
)ExeSize
, &MaxAllocSize
);
856 /* Check if there's at least enough memory for the minimum size */
857 if (MaxAllocSize
< (ExeSize
- Header
->e_maxalloc
+ Header
->e_minalloc
))
859 Result
= DosLastError
;
863 /* Allocate that minimum amount */
864 ExeSize
= MaxAllocSize
;
865 Segment
= DosAllocateMemory((WORD
)ExeSize
, NULL
);
866 ASSERT(Segment
!= 0);
869 /* Initialize the PSP */
870 DosInitializePsp(Segment
,
876 /* The process owns its own memory */
877 DosChangeMemoryOwner(Segment
, Segment
);
878 DosChangeMemoryOwner(EnvBlock
, Segment
);
880 /* Copy the program to Segment:0100 */
881 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
882 Address
+ (Header
->e_cparhdr
<< 4),
883 min(FileSize
- (Header
->e_cparhdr
<< 4),
884 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
886 /* Get the relocation table */
887 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
889 /* Perform relocations */
890 for (i
= 0; i
< Header
->e_crlc
; i
++)
892 /* Get a pointer to the word that needs to be patched */
893 RelocWord
= (PWORD
)SEG_OFF_TO_PTR(Segment
+ HIWORD(RelocationTable
[i
]),
894 0x100 + LOWORD(RelocationTable
[i
]));
896 /* Add the number of the EXE segment to it */
897 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
900 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
902 /* Set the initial segment registers */
906 /* Set the stack to the location from the header */
907 setSS(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
);
911 CurrentPsp
= Segment
;
912 DiskTransferArea
= MAKELONG(0x80, Segment
);
913 CpuExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
921 /* Find the maximum amount of memory that can be allocated */
922 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
924 /* Make sure it's enough for the whole program and the PSP */
925 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
)))
927 Result
= ERROR_NOT_ENOUGH_MEMORY
;
931 /* Allocate all of it */
932 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
935 Result
= DosLastError
;
939 /* The process owns its own memory */
940 DosChangeMemoryOwner(Segment
, Segment
);
941 DosChangeMemoryOwner(EnvBlock
, Segment
);
943 /* Copy the program to Segment:0100 */
944 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
948 /* Initialize the PSP */
949 DosInitializePsp(Segment
,
955 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
957 /* Set the initial segment registers */
961 /* Set the stack to the last word of the segment */
966 * Set the value on the stack to 0, so that a near return
967 * jumps to PSP:0000 which has the exit code.
969 *((LPWORD
)SEG_OFF_TO_PTR(Segment
, 0xFFFE)) = 0;
972 CurrentPsp
= Segment
;
973 DiskTransferArea
= MAKELONG(0x80, Segment
);
974 CpuExecute(Segment
, 0x100);
979 if (Result
!= ERROR_SUCCESS
)
981 /* It was not successful, cleanup the DOS memory */
982 if (EnvBlock
) DosFreeMemory(EnvBlock
);
983 if (Segment
) DosFreeMemory(Segment
);
987 if (Address
!= NULL
) UnmapViewOfFile(Address
);
989 /* Close the file mapping object */
990 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
992 /* Close the file handle */
993 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
998 DWORD
DosStartProcess(IN LPCSTR ExecutablePath
,
999 IN LPCSTR CommandLine
,
1000 IN LPCSTR Environment OPTIONAL
)
1003 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
1005 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
1009 IntVecTable
[0x20], // Use INT 20h
1013 if (Result
!= ERROR_SUCCESS
) goto Quit
;
1015 /* Attach to the console */
1017 VidBiosAttachToConsole();
1019 // HACK: Simulate a ENTER key release scancode on the PS/2 port because
1020 // some apps expect to read a key release scancode (> 0x80) when they
1022 IOWriteB(PS2_CONTROL_PORT
, 0xD2); // Next write is for the first PS/2 port
1023 IOWriteB(PS2_DATA_PORT
, 0x80 | 0x1C); // ENTER key release
1025 /* Start simulation */
1026 SetEvent(VdmTaskEvent
);
1029 /* Detach from the console */
1030 VidBiosDetachFromConsole();
1038 WORD
DosCreateProcess(DOS_EXEC_TYPE LoadType
,
1040 PDOS_EXEC_PARAM_BLOCK Parameters
,
1041 DWORD ReturnAddress
)
1045 LPVOID Environment
= NULL
;
1046 VDM_COMMAND_INFO CommandInfo
;
1047 CHAR CmdLine
[MAX_PATH
];
1048 CHAR AppName
[MAX_PATH
];
1049 CHAR PifFile
[MAX_PATH
];
1050 CHAR Desktop
[MAX_PATH
];
1051 CHAR Title
[MAX_PATH
];
1052 ULONG EnvSize
= 256;
1053 PVOID Env
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, EnvSize
);
1054 STARTUPINFOA StartupInfo
;
1055 PROCESS_INFORMATION ProcessInfo
;
1057 /* Get the binary type */
1058 if (!GetBinaryTypeA(ProgramName
, &BinaryType
)) return GetLastError();
1060 /* Did the caller specify an environment segment? */
1061 if (Parameters
->Environment
)
1063 /* Yes, use it instead of the parent one */
1064 Environment
= SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
1067 /* Set up the startup info structure */
1068 RtlZeroMemory(&StartupInfo
, sizeof(StartupInfo
));
1069 StartupInfo
.cb
= sizeof(StartupInfo
);
1071 /* Create the process */
1072 if (!CreateProcessA(ProgramName
,
1073 FAR_POINTER(Parameters
->CommandLine
),
1083 return GetLastError();
1086 /* Check the type of the program */
1089 /* These are handled by NTVDM */
1090 case SCS_DOS_BINARY
:
1091 case SCS_WOW_BINARY
:
1093 /* Clear the structure */
1094 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
1096 /* Initialize the structure members */
1097 CommandInfo
.TaskId
= SessionId
;
1098 CommandInfo
.VDMState
= VDM_FLAG_NESTED_TASK
| VDM_FLAG_DONT_WAIT
;
1099 CommandInfo
.CmdLine
= CmdLine
;
1100 CommandInfo
.CmdLen
= sizeof(CmdLine
);
1101 CommandInfo
.AppName
= AppName
;
1102 CommandInfo
.AppLen
= sizeof(AppName
);
1103 CommandInfo
.PifFile
= PifFile
;
1104 CommandInfo
.PifLen
= sizeof(PifFile
);
1105 CommandInfo
.Desktop
= Desktop
;
1106 CommandInfo
.DesktopLen
= sizeof(Desktop
);
1107 CommandInfo
.Title
= Title
;
1108 CommandInfo
.TitleLen
= sizeof(Title
);
1109 CommandInfo
.Env
= Env
;
1110 CommandInfo
.EnvLen
= EnvSize
;
1113 /* Get the VDM command information */
1114 if (!GetNextVDMCommand(&CommandInfo
))
1116 if (CommandInfo
.EnvLen
> EnvSize
)
1118 /* Expand the environment size */
1119 EnvSize
= CommandInfo
.EnvLen
;
1120 CommandInfo
.Env
= Env
= RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, Env
, EnvSize
);
1122 /* Repeat the request */
1123 CommandInfo
.VDMState
|= VDM_FLAG_RETRY
;
1127 /* Shouldn't happen */
1131 /* Load the executable */
1132 Result
= DosLoadExecutable(LoadType
,
1137 &Parameters
->StackLocation
,
1138 &Parameters
->EntryPoint
);
1139 if (Result
== ERROR_SUCCESS
)
1141 /* Increment the re-entry count */
1142 CommandInfo
.VDMState
= VDM_INC_REENTER_COUNT
;
1143 GetNextVDMCommand(&CommandInfo
);
1147 DisplayMessage(L
"Could not load '%S'. Error: %u", AppName
, Result
);
1153 /* Not handled by NTVDM */
1156 /* Wait for the process to finish executing */
1157 WaitForSingleObject(ProcessInfo
.hProcess
, INFINITE
);
1161 RtlFreeHeap(RtlGetProcessHeap(), 0, Env
);
1163 /* Close the handles */
1164 CloseHandle(ProcessInfo
.hProcess
);
1165 CloseHandle(ProcessInfo
.hThread
);
1167 return ERROR_SUCCESS
;
1171 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
, WORD KeepResident
)
1174 WORD McbSegment
= FIRST_MCB_SEGMENT
;
1175 PDOS_MCB CurrentMcb
;
1176 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
1177 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
1179 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
1184 /* Check if this PSP is it's own parent */
1185 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
1187 if (KeepResident
== 0)
1189 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
1191 /* Close the handle */
1196 /* Free the memory used by the process */
1199 /* Get a pointer to the MCB */
1200 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
1202 /* Make sure the MCB is valid */
1203 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z') break;
1205 /* Check if this block was allocated by the process */
1206 if (CurrentMcb
->OwnerPsp
== Psp
)
1208 if (KeepResident
== 0)
1210 /* Free this entire block */
1211 DosFreeMemory(McbSegment
+ 1);
1213 else if (KeepResident
< CurrentMcb
->Size
)
1215 /* Reduce the size of the block */
1216 DosResizeMemory(McbSegment
+ 1, KeepResident
, NULL
);
1218 /* No further paragraphs need to stay resident */
1223 /* Just reduce the amount of paragraphs we need to keep resident */
1224 KeepResident
-= CurrentMcb
->Size
;
1228 /* If this was the last block, quit */
1229 if (CurrentMcb
->BlockType
== 'Z') break;
1231 /* Update the segment and continue */
1232 McbSegment
+= CurrentMcb
->Size
+ 1;
1236 /* Restore the interrupt vectors */
1237 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
1238 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
1239 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
1241 /* Update the current PSP */
1242 if (Psp
== CurrentPsp
)
1244 CurrentPsp
= PspBlock
->ParentPsp
;
1245 if (CurrentPsp
== SYSTEM_PSP
)
1247 ResetEvent(VdmTaskEvent
);
1253 // FIXME: This is probably not the best way to do it
1254 /* Check if this was a nested DOS task */
1255 if (CurrentPsp
!= SYSTEM_PSP
)
1257 VDM_COMMAND_INFO CommandInfo
;
1259 /* Decrement the re-entry count */
1260 CommandInfo
.TaskId
= SessionId
;
1261 CommandInfo
.VDMState
= VDM_DEC_REENTER_COUNT
;
1262 GetNextVDMCommand(&CommandInfo
);
1264 /* Clear the structure */
1265 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
1267 /* Update the VDM state of the task */
1268 CommandInfo
.TaskId
= SessionId
;
1269 CommandInfo
.VDMState
= VDM_FLAG_DONT_WAIT
;
1270 GetNextVDMCommand(&CommandInfo
);
1274 /* Save the return code - Normal termination */
1275 DosErrorLevel
= MAKEWORD(ReturnCode
, 0x00);
1277 /* Return control to the parent process */
1278 CpuExecute(HIWORD(PspBlock
->TerminateAddress
),
1279 LOWORD(PspBlock
->TerminateAddress
));
1282 BOOLEAN
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
1284 PDOS_SFT_ENTRY SftEntry
= DosGetSftEntry(FileHandle
);
1285 PDOS_DEVICE_NODE Node
= NULL
;
1287 /* Make sure it exists */
1290 DosLastError
= ERROR_FILE_NOT_FOUND
;
1294 if (SftEntry
->Type
== DOS_SFT_ENTRY_DEVICE
) Node
= SftEntry
->DeviceNode
;
1296 switch (ControlCode
)
1298 /* Get Device Information */
1304 * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
1305 * for a list of possible flags.
1310 /* Return the device attributes with bit 7 set */
1311 InfoWord
= Node
->DeviceAttributes
| (1 << 7);
1318 /* Set Device Information */
1321 // TODO: NOT IMPLEMENTED
1327 /* Read From Device I/O Control Channel */
1330 WORD Length
= getCX();
1332 if (Node
== NULL
|| !(Node
->DeviceAttributes
& DOS_DEVATTR_IOCTL
))
1334 DosLastError
= ERROR_INVALID_FUNCTION
;
1338 /* Do nothing if there is no IOCTL routine */
1339 if (!Node
->IoctlReadRoutine
)
1345 Node
->IoctlReadRoutine(Node
, MAKELONG(getDX(), getDS()), &Length
);
1351 /* Write To Device I/O Control Channel */
1354 WORD Length
= getCX();
1356 if (Node
== NULL
|| !(Node
->DeviceAttributes
& DOS_DEVATTR_IOCTL
))
1358 DosLastError
= ERROR_INVALID_FUNCTION
;
1362 /* Do nothing if there is no IOCTL routine */
1363 if (!Node
->IoctlWriteRoutine
)
1369 Node
->IoctlWriteRoutine(Node
, MAKELONG(getDX(), getDS()), &Length
);
1375 /* Unsupported control code */
1378 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
1380 DosLastError
= ERROR_INVALID_PARAMETER
;
1386 VOID WINAPI
DosInt20h(LPWORD Stack
)
1388 /* This is the exit interrupt */
1389 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
1392 VOID WINAPI
DosInt21h(LPWORD Stack
)
1395 SYSTEMTIME SystemTime
;
1397 PDOS_INPUT_BUFFER InputBuffer
;
1398 PDOS_COUNTRY_CODE_BUFFER CountryCodeBuffer
;
1403 /* Check the value in the AH register */
1406 /* Terminate Program */
1409 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
1413 /* Read Character from STDIN with Echo */
1416 DPRINT("INT 21h, AH = 01h\n");
1418 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
1420 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1423 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
1424 // Check also Ctrl-P and set echo-to-printer flag.
1425 // Ctrl-Z is not interpreted.
1431 /* Write Character to STDOUT */
1434 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1435 Character
= getDL();
1436 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1439 * We return the output character (DOS 2.1+).
1440 * Also, if we're going to output a TAB, then
1441 * don't return a TAB but a SPACE instead.
1442 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
1443 * for more information.
1445 setAL(Character
== '\t' ? ' ' : Character
);
1449 /* Read Character from STDAUX */
1452 // FIXME: Really read it from STDAUX!
1453 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
1454 // setAL(DosReadCharacter());
1458 /* Write Character to STDAUX */
1461 // FIXME: Really write it to STDAUX!
1462 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
1463 // DosPrintCharacter(getDL());
1467 /* Write Character to Printer */
1470 // FIXME: Really write it to printer!
1471 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
1472 DPRINT1("0x%p\n", getDL());
1473 DPRINT1("\n\n-----------\n\n");
1477 /* Direct Console I/O */
1480 Character
= getDL();
1482 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1484 if (Character
!= 0xFF)
1487 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1490 * We return the output character (DOS 2.1+).
1491 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
1492 * for more information.
1499 if (DosCheckInput())
1501 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1502 setAL(DosReadCharacter(DOS_INPUT_HANDLE
));
1506 /* No character available */
1507 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1515 /* Character Input without Echo */
1519 DPRINT("Char input without echo\n");
1521 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1523 // FIXME: For 0x07, do not check Ctrl-C/Break.
1524 // For 0x08, do check those control sequences and if needed,
1531 /* Write string to STDOUT */
1534 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1536 while (*String
!= '$')
1538 DosPrintCharacter(DOS_OUTPUT_HANDLE
, *String
);
1543 * We return the terminating character (DOS 2.1+).
1544 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
1545 * for more information.
1547 setAL('$'); // *String
1551 /* Read Buffered Input */
1555 InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
1557 DPRINT("Read Buffered Input\n");
1559 while (Count
< InputBuffer
->MaxLength
)
1561 /* Try to read a character (wait) */
1562 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1566 /* Extended character */
1569 /* Read the scancode */
1570 DosReadCharacter(DOS_INPUT_HANDLE
);
1577 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
1578 DosPrintCharacter(DOS_OUTPUT_HANDLE
, 'C');
1580 if (DosControlBreak())
1582 /* Set the character to a newline to exit the loop */
1596 /* Erase the character */
1597 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
1598 DosPrintCharacter(DOS_OUTPUT_HANDLE
, ' ');
1599 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
1607 /* Append it to the buffer */
1608 InputBuffer
->Buffer
[Count
] = Character
;
1609 Count
++; /* Carriage returns are also counted */
1611 /* Check if this is a special character */
1612 if (Character
< 0x20 && Character
!= 0x0A && Character
!= 0x0D)
1614 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
1615 Character
+= 'A' - 1;
1618 /* Echo the character */
1619 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1623 if (Character
== '\r') break;
1626 /* Update the length */
1627 InputBuffer
->Length
= Count
;
1632 /* Get STDIN Status */
1635 setAL(DosCheckInput() ? 0xFF : 0x00);
1639 /* Flush Buffer and Read STDIN */
1642 BYTE InputFunction
= getAL();
1644 /* Flush STDIN buffer */
1645 DosFlushFileBuffers(DOS_INPUT_HANDLE
);
1648 * If the input function number contained in AL is valid, i.e.
1649 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
1650 * recursively with AL == AH.
1652 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
1653 InputFunction
== 0x07 || InputFunction
== 0x08 ||
1654 InputFunction
== 0x0A)
1656 /* Call ourselves recursively */
1657 setAH(InputFunction
);
1666 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1668 // TODO: Flush what's needed.
1669 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
1671 /* Clear CF in DOS 6 only */
1672 if (PspBlock
->DosVersion
== 0x0006)
1673 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1678 /* Set Default Drive */
1681 DosChangeDrive(getDL());
1682 setAL(LastDrive
- 'A' + 1);
1686 /* NULL Function for CP/M Compatibility */
1690 * This function corresponds to the CP/M BDOS function
1691 * "get bit map of logged drives", which is meaningless
1694 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
1695 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
1696 * for more information.
1702 /* Get Default Drive */
1705 setAL(CurrentDrive
);
1709 /* Set Disk Transfer Area */
1712 DiskTransferArea
= MAKELONG(getDX(), getDS());
1716 /* NULL Function for CP/M Compatibility */
1721 * Function 0x1D corresponds to the CP/M BDOS function
1722 * "get bit map of read-only drives", which is meaningless
1724 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
1725 * for more information.
1727 * Function 0x1E corresponds to the CP/M BDOS function
1728 * "set file attributes", which was meaningless under MS-DOS 1.x.
1729 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
1730 * for more information.
1736 /* NULL Function for CP/M Compatibility */
1740 * This function corresponds to the CP/M BDOS function
1741 * "get/set default user (sublibrary) number", which is meaningless
1744 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
1745 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
1746 * for more information.
1752 /* Set Interrupt Vector */
1755 ULONG FarPointer
= MAKELONG(getDX(), getDS());
1756 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
1757 getAL(), HIWORD(FarPointer
), LOWORD(FarPointer
));
1759 /* Write the new far pointer to the IDT */
1760 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
1764 /* Create New PSP */
1767 DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
1771 /* Parse Filename into FCB */
1774 PCHAR FileName
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
1775 PDOS_FCB Fcb
= (PDOS_FCB
)SEG_OFF_TO_PTR(getES(), getDI());
1776 BYTE Options
= getAL();
1778 CHAR FillChar
= ' ';
1780 if (FileName
[1] == ':')
1782 /* Set the drive number */
1783 Fcb
->DriveNumber
= RtlUpperChar(FileName
[0]) - 'A' + 1;
1785 /* Skip to the file name part */
1790 /* No drive number specified */
1791 if (Options
& (1 << 1)) Fcb
->DriveNumber
= CurrentDrive
+ 1;
1792 else Fcb
->DriveNumber
= 0;
1795 /* Parse the file name */
1797 while (*FileName
&& (i
< 8))
1799 if (*FileName
== '.') break;
1800 else if (*FileName
== '*')
1806 Fcb
->FileName
[i
++] = *FileName
++;
1809 /* Fill the whole field with blanks only if bit 2 is not set */
1810 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 2)))
1812 for (; i
< 8; i
++) Fcb
->FileName
[i
] = FillChar
;
1815 /* Skip to the extension part */
1816 while (*FileName
&& *FileName
!= '.') FileName
++;
1817 if (*FileName
== '.') FileName
++;
1819 /* Now parse the extension */
1823 while (*FileName
&& (i
< 3))
1825 if (*FileName
== '*')
1831 Fcb
->FileExt
[i
++] = *FileName
++;
1834 /* Fill the whole field with blanks only if bit 3 is not set */
1835 if ((FillChar
!= ' ') || (i
!= 0) || !(Options
& (1 << 3)))
1837 for (; i
< 3; i
++) Fcb
->FileExt
[i
] = FillChar
;
1843 /* Get System Date */
1846 GetLocalTime(&SystemTime
);
1847 setCX(SystemTime
.wYear
);
1848 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
1849 setAL(SystemTime
.wDayOfWeek
);
1853 /* Set System Date */
1856 GetLocalTime(&SystemTime
);
1857 SystemTime
.wYear
= getCX();
1858 SystemTime
.wMonth
= getDH();
1859 SystemTime
.wDay
= getDL();
1861 /* Return success or failure */
1862 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1866 /* Get System Time */
1869 GetLocalTime(&SystemTime
);
1870 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
1871 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
1875 /* Set System Time */
1878 GetLocalTime(&SystemTime
);
1879 SystemTime
.wHour
= getCH();
1880 SystemTime
.wMinute
= getCL();
1881 SystemTime
.wSecond
= getDH();
1882 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
1884 /* Return success or failure */
1885 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1889 /* Get Disk Transfer Area */
1892 setES(HIWORD(DiskTransferArea
));
1893 setBX(LOWORD(DiskTransferArea
));
1897 /* Get DOS Version */
1900 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1903 * DOS 2+ - GET DOS VERSION
1904 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
1905 * for more information.
1908 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
1911 * Return DOS OEM number:
1912 * 0x00 for IBM PC-DOS
1913 * 0x02 for packaged MS-DOS
1919 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
1922 * Return version flag:
1923 * 1 << 3 if DOS is in ROM,
1924 * 0 (reserved) if not.
1929 /* Return DOS 24-bit user serial number in BL:CX */
1934 * Return DOS version: Minor:Major in AH:AL
1935 * The Windows NT DOS box returns version 5.00, subject to SETVER.
1937 setAX(PspBlock
->DosVersion
);
1942 /* Terminate and Stay Resident */
1945 DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
1946 DosTerminateProcess(CurrentPsp
, getAL(), getDX());
1950 /* Extended functionalities */
1953 if (getAL() == 0x06)
1956 * DOS 5+ - GET TRUE VERSION NUMBER
1957 * This function always returns the true version number, unlike
1958 * AH=30h, whose return value may be changed with SETVER.
1959 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
1960 * for more information.
1964 * Return the true DOS version: Minor:Major in BH:BL
1965 * The Windows NT DOS box returns BX=3205h (version 5.50).
1967 setBX(NTDOS_VERSION
);
1969 /* DOS revision 0 */
1977 // /* Invalid subfunction */
1984 /* Get Address of InDOS flag */
1987 setES(HIWORD(INDOS_POINTER
));
1988 setBX(LOWORD(INDOS_POINTER
));
1993 /* Get Interrupt Vector */
1996 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[getAL()];
1998 /* Read the address from the IDT into ES:BX */
1999 setES(HIWORD(FarPointer
));
2000 setBX(LOWORD(FarPointer
));
2004 /* SWITCH character - AVAILDEV */
2007 if (getAL() == 0x00)
2010 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
2011 * This setting is ignored by MS-DOS 4.0+.
2012 * MS-DOS 5+ always return AL=00h/DL=2Fh.
2013 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
2014 * for more information.
2019 else if (getAL() == 0x01)
2022 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
2023 * This setting is ignored by MS-DOS 5+.
2024 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
2025 * for more information.
2030 else if (getAL() == 0x02)
2033 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
2034 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
2035 * for more information.
2040 else if (getAL() == 0x03)
2043 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
2044 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
2045 * for more information.
2052 /* Invalid subfunction */
2059 /* Get/Set Country-dependent Information */
2062 CountryCodeBuffer
= (PDOS_COUNTRY_CODE_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
2064 if (getAL() == 0x00)
2067 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_IDATE
,
2068 &CountryCodeBuffer
->TimeFormat
,
2069 sizeof(CountryCodeBuffer
->TimeFormat
) / sizeof(TCHAR
));
2072 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2073 setAX(LOWORD(GetLastError()));
2077 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_SCURRENCY
,
2078 &CountryCodeBuffer
->CurrencySymbol
,
2079 sizeof(CountryCodeBuffer
->CurrencySymbol
) / sizeof(TCHAR
));
2082 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2083 setAX(LOWORD(GetLastError()));
2087 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
,
2088 &CountryCodeBuffer
->ThousandSep
,
2089 sizeof(CountryCodeBuffer
->ThousandSep
) / sizeof(TCHAR
));
2092 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2093 setAX(LOWORD(GetLastError()));
2097 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
,
2098 &CountryCodeBuffer
->DecimalSep
,
2099 sizeof(CountryCodeBuffer
->DecimalSep
) / sizeof(TCHAR
));
2102 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2103 setAX(LOWORD(GetLastError()));
2107 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2111 // TODO: NOT IMPLEMENTED
2118 /* Create Directory */
2121 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2123 if (CreateDirectoryA(String
, NULL
))
2125 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2129 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2130 setAX(LOWORD(GetLastError()));
2136 /* Remove Directory */
2139 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2141 if (RemoveDirectoryA(String
))
2143 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2147 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2148 setAX(LOWORD(GetLastError()));
2154 /* Set Current Directory */
2157 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2159 if (DosChangeDirectory(String
))
2161 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2165 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2166 setAX(DosLastError
);
2172 /* Create or Truncate File */
2176 WORD ErrorCode
= DosCreateFile(&FileHandle
,
2177 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2181 if (ErrorCode
== ERROR_SUCCESS
)
2183 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2188 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2195 /* Open File or Device */
2200 LPCSTR FileName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2201 PDOS_DEVICE_NODE Device
= DosGetDevice(FileName
);
2205 FileHandle
= DosOpenDevice(Device
);
2206 ErrorCode
= (FileHandle
!= INVALID_DOS_HANDLE
)
2207 ? ERROR_SUCCESS
: ERROR_TOO_MANY_OPEN_FILES
;
2211 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, getAL());
2214 if (ErrorCode
== ERROR_SUCCESS
)
2216 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2221 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2228 /* Close File or Device */
2231 if (DosCloseHandle(getBX()))
2233 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2237 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2238 setAX(ERROR_INVALID_HANDLE
);
2244 /* Read from File or Device */
2250 DPRINT("DosReadFile(0x%04X)\n", getBX());
2253 ErrorCode
= DosReadFile(getBX(),
2254 MAKELONG(getDX(), getDS()),
2259 if (ErrorCode
== ERROR_SUCCESS
)
2261 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2264 else if (ErrorCode
!= ERROR_NOT_READY
)
2266 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2273 /* Write to File or Device */
2276 WORD BytesWritten
= 0;
2277 WORD ErrorCode
= DosWriteFile(getBX(),
2278 MAKELONG(getDX(), getDS()),
2282 if (ErrorCode
== ERROR_SUCCESS
)
2284 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2285 setAX(BytesWritten
);
2289 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2299 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2301 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
2303 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2305 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
2306 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
2308 setAL(FileName
[0] - 'A');
2312 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2313 setAX(GetLastError());
2323 WORD ErrorCode
= DosSeekFile(getBX(),
2324 MAKELONG(getDX(), getCX()),
2328 if (ErrorCode
== ERROR_SUCCESS
)
2330 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2332 /* Return the new offset in DX:AX */
2333 setDX(HIWORD(NewLocation
));
2334 setAX(LOWORD(NewLocation
));
2338 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2345 /* Get/Set File Attributes */
2349 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2351 if (getAL() == 0x00)
2353 /* Get the attributes */
2354 Attributes
= GetFileAttributesA(FileName
);
2356 /* Check if it failed */
2357 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
2359 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2360 setAX(GetLastError());
2364 /* Return the attributes that DOS can understand */
2365 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2366 setCX(Attributes
& 0x00FF);
2369 else if (getAL() == 0x01)
2371 /* Try to set the attributes */
2372 if (SetFileAttributesA(FileName
, getCL()))
2374 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2378 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2379 setAX(GetLastError());
2384 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2385 setAX(ERROR_INVALID_FUNCTION
);
2394 if (DosHandleIoctl(getAL(), getBX()))
2396 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2400 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2401 setAX(DosLastError
);
2407 /* Duplicate Handle */
2411 PDOS_SFT_ENTRY SftEntry
= DosGetSftEntry(getBX());
2413 if (SftEntry
== NULL
|| SftEntry
->Type
== DOS_SFT_ENTRY_NONE
)
2415 /* The handle is invalid */
2416 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2417 setAX(ERROR_INVALID_HANDLE
);
2421 /* Open a new handle to the same entry */
2422 switch (SftEntry
->Type
)
2424 case DOS_SFT_ENTRY_WIN32
:
2426 NewHandle
= DosOpenHandle(SftEntry
->Handle
);
2430 case DOS_SFT_ENTRY_DEVICE
:
2432 NewHandle
= DosOpenDevice(SftEntry
->DeviceNode
);
2438 /* Shouldn't happen */
2443 if (NewHandle
== INVALID_DOS_HANDLE
)
2445 /* Too many files open */
2446 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2447 setAX(ERROR_TOO_MANY_OPEN_FILES
);
2451 /* Return the result */
2452 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2457 /* Force Duplicate Handle */
2460 if (DosDuplicateHandle(getBX(), getCX()))
2462 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2466 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2467 setAX(ERROR_INVALID_HANDLE
);
2473 /* Get Current Directory */
2476 BYTE DriveNumber
= getDL();
2477 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
2479 /* Get the real drive number */
2480 if (DriveNumber
== 0)
2482 DriveNumber
= CurrentDrive
;
2486 /* Decrement DriveNumber since it was 1-based */
2490 if (DriveNumber
<= LastDrive
- 'A')
2493 * Copy the current directory into the target buffer.
2494 * It doesn't contain the drive letter and the backslash.
2496 strncpy(String
, CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
2497 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2498 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
2502 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2503 setAX(ERROR_INVALID_DRIVE
);
2509 /* Allocate Memory */
2512 WORD MaxAvailable
= 0;
2513 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
2517 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2522 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2523 setAX(DosLastError
);
2524 setBX(MaxAvailable
);
2533 if (DosFreeMemory(getES()))
2535 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2539 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2540 setAX(ERROR_ARENA_TRASHED
);
2546 /* Resize Memory Block */
2551 if (DosResizeMemory(getES(), getBX(), &Size
))
2553 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2557 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2558 setAX(DosLastError
);
2569 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)getAL();
2570 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
2571 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
2572 DWORD ReturnAddress
= MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]);
2575 if (LoadType
!= DOS_LOAD_OVERLAY
)
2577 ErrorCode
= DosCreateProcess(LoadType
, ProgramName
, ParamBlock
, ReturnAddress
);
2581 ErrorCode
= DosLoadExecutable(DOS_LOAD_OVERLAY
,
2583 FAR_POINTER(ParamBlock
->CommandLine
),
2584 SEG_OFF_TO_PTR(ParamBlock
->Environment
, 0),
2590 if (ErrorCode
== ERROR_SUCCESS
)
2592 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2596 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2604 /* Terminate With Return Code */
2607 DosTerminateProcess(CurrentPsp
, getAL(), 0);
2611 /* Get Return Code (ERRORLEVEL) */
2615 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
2616 * DosErrorLevel is cleared after being read by this function.
2618 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2619 setAX(DosErrorLevel
);
2620 DosErrorLevel
= 0x0000; // Clear it
2624 /* Find First File */
2627 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(DiskTransferArea
),
2628 SEG_OFF_TO_PTR(getDS(), getDX()),
2633 if (Result
== ERROR_SUCCESS
)
2634 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2636 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2641 /* Find Next File */
2644 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(DiskTransferArea
));
2648 if (Result
== ERROR_SUCCESS
)
2649 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2651 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2656 /* Internal - Set Current Process ID (Set PSP Address) */
2659 // FIXME: Is it really what it's done ??
2660 CurrentPsp
= getBX();
2664 /* Internal - Get Current Process ID (Get PSP Address) */
2666 /* Get Current PSP Address */
2670 * Undocumented AH=51h is identical to the documented AH=62h.
2671 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
2672 * and http://www.ctyme.com/intr/rb-3140.htm
2673 * for more information.
2679 /* Internal - Get "List of lists" (SYSVARS) */
2683 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
2684 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
2685 * for more information.
2688 /* Return the DOS "list of lists" in ES:BX */
2692 DPRINT1("INT 21h, AH=52h: This application requires the internal DOS List of lists (SYSVARS). UNIMPLEMENTED\n");
2699 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2700 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
2703 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
2704 * for more information.
2707 if (MoveFileA(ExistingFileName
, NewFileName
))
2709 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2713 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2714 setAX(GetLastError());
2720 /* Get/Set Memory Management Options */
2723 if (getAL() == 0x00)
2725 /* Get allocation strategy */
2726 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2727 setAX(DosAllocStrategy
);
2729 else if (getAL() == 0x01)
2731 /* Set allocation strategy */
2733 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2734 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2736 /* Can't set both */
2737 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2738 setAX(ERROR_INVALID_PARAMETER
);
2742 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT
)
2744 /* Invalid allocation strategy */
2745 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2746 setAX(ERROR_INVALID_PARAMETER
);
2750 DosAllocStrategy
= getBL();
2751 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2753 else if (getAL() == 0x02)
2755 /* Get UMB link state */
2756 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2757 setAL(DosUmbLinked
? 0x01 : 0x00);
2759 else if (getAL() == 0x03)
2761 /* Set UMB link state */
2762 if (getBX()) DosLinkUmb();
2763 else DosUnlinkUmb();
2764 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2768 /* Invalid or unsupported function */
2769 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2770 setAX(ERROR_INVALID_FUNCTION
);
2776 /* Get Extended Error Information */
2779 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
2784 /* Create Temporary File */
2787 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2788 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
2794 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
2795 * for more information.
2798 // FIXME: Check for buffer validity?
2799 // It should be a ASCIZ path ending with a '\' + 13 zero bytes
2800 // to receive the generated filename.
2802 /* First create the temporary file */
2803 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
2806 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2807 setAX(GetLastError());
2811 /* Now try to open it in read/write access */
2812 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
2813 if (ErrorCode
== ERROR_SUCCESS
)
2815 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2820 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2827 /* Create New File */
2831 WORD ErrorCode
= DosCreateFile(&FileHandle
,
2832 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2836 if (ErrorCode
== ERROR_SUCCESS
)
2838 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2843 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2850 /* Lock/Unlock Region of File */
2853 PDOS_SFT_ENTRY SftEntry
= DosGetSftEntry(getBX());
2855 if (SftEntry
== NULL
|| SftEntry
->Type
!= DOS_SFT_ENTRY_WIN32
)
2857 /* The handle is invalid */
2858 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2859 setAX(ERROR_INVALID_HANDLE
);
2863 if (getAL() == 0x00)
2865 /* Lock region of file */
2866 if (LockFile(SftEntry
->Handle
,
2867 MAKELONG(getCX(), getDX()), 0,
2868 MAKELONG(getSI(), getDI()), 0))
2870 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2874 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2875 setAX(GetLastError());
2878 else if (getAL() == 0x01)
2880 /* Unlock region of file */
2881 if (UnlockFile(SftEntry
->Handle
,
2882 MAKELONG(getCX(), getDX()), 0,
2883 MAKELONG(getSI(), getDI()), 0))
2885 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2889 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2890 setAX(GetLastError());
2895 /* Invalid subfunction */
2896 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2897 setAX(ERROR_INVALID_FUNCTION
);
2903 /* Canonicalize File Name or Path */
2907 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
2908 * for more information.
2912 * We suppose that the DOS app gave to us a valid
2913 * 128-byte long buffer for the canonicalized name.
2915 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
2917 SEG_OFF_TO_PTR(getES(), getDI()),
2921 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2922 setAX(GetLastError());
2926 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2930 // FIXME: Convert the full path name into short version.
2931 // We cannot reliably use GetShortPathName, because it fails
2932 // if the path name given doesn't exist. However this DOS
2933 // function AH=60h should be able to work even for non-existing
2934 // path and file names.
2939 /* Set Handle Count */
2942 if (!DosResizeHandleTable(getBX()))
2944 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2945 setAX(DosLastError
);
2947 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2957 * Function 6Ah is identical to function 68h,
2958 * and sets AH to 68h if success.
2959 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
2960 * for more information.
2964 if (DosFlushFileBuffers(getBX()))
2966 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2970 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2971 setAX(GetLastError());
2977 /* Extended Open/Create */
2981 WORD CreationStatus
;
2984 /* Check for AL == 00 */
2985 if (getAL() != 0x00)
2987 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2988 setAX(ERROR_INVALID_FUNCTION
);
2993 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
2994 * for the full detailed description.
2996 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
2999 ErrorCode
= DosCreateFileEx(&FileHandle
,
3001 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI()),
3006 if (ErrorCode
== ERROR_SUCCESS
)
3008 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
3009 setCX(CreationStatus
);
3014 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
3024 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
3027 setAL(0); // Some functions expect AL to be 0 when it's not supported.
3028 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
3035 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
3037 /* Set CF to terminate the running process */
3038 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
3041 VOID WINAPI
DosFastConOut(LPWORD Stack
)
3044 * This is the DOS 2+ Fast Console Output Interrupt.
3045 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
3047 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
3048 * for more information.
3051 /* Save AX and BX */
3052 USHORT AX
= getAX();
3053 USHORT BX
= getBX();
3056 * Set the parameters:
3057 * AL contains the character to print (already set),
3058 * BL contains the character attribute,
3059 * BH contains the video page to use.
3061 setBL(DOS_CHAR_ATTRIBUTE
);
3062 setBH(Bda
->VideoPage
);
3064 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
3066 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
3068 /* Restore AX and BX */
3073 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
3077 /* Extended Memory Specification */
3081 if (!XmsGetDriverEntry(&DriverEntry
)) break;
3083 if (getAL() == 0x00)
3085 /* The driver is loaded */
3088 else if (getAL() == 0x10)
3090 setES(HIWORD(DriverEntry
));
3091 setBX(LOWORD(DriverEntry
));
3095 DPRINT1("Unknown DOS XMS Function: INT 0x2F, AH = 43h, AL = %xh\n", getAL());
3103 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
3105 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
3110 BOOLEAN
DosKRNLInitialize(VOID
)
3116 CHAR CurrentDirectory
[MAX_PATH
];
3117 CHAR DosDirectory
[DOS_DIR_LENGTH
];
3123 /* Setup the InDOS flag */
3124 InDos
= (PBYTE
)FAR_POINTER(INDOS_POINTER
);
3127 /* Clear the current directory buffer */
3128 RtlZeroMemory(CurrentDirectories
, sizeof(CurrentDirectories
));
3130 /* Get the current directory */
3131 if (!GetCurrentDirectoryA(MAX_PATH
, CurrentDirectory
))
3133 // TODO: Use some kind of default path?
3137 /* Convert that to a DOS path */
3138 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, DOS_DIR_LENGTH
))
3140 // TODO: Use some kind of default path?
3145 CurrentDrive
= DosDirectory
[0] - 'A';
3147 /* Get the directory part of the path */
3148 Path
= strchr(DosDirectory
, '\\');
3151 /* Skip the backslash */
3155 /* Set the directory */
3158 strncpy(CurrentDirectories
[CurrentDrive
], Path
, DOS_DIR_LENGTH
);
3161 /* Read CONFIG.SYS */
3162 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
3165 while (fgetws(Buffer
, 256, Stream
))
3167 // TODO: Parse the line
3172 /* Initialize the SFT */
3173 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
3175 DosSystemFileTable
[i
].Type
= DOS_SFT_ENTRY_NONE
;
3176 DosSystemFileTable
[i
].RefCount
= 0;
3181 /* Initialize the callback context */
3182 InitializeContext(&DosContext
, 0x0070, 0x0000);
3184 /* Register the DOS 32-bit Interrupts */
3185 RegisterDosInt32(0x20, DosInt20h
);
3186 RegisterDosInt32(0x21, DosInt21h
);
3187 // RegisterDosInt32(0x22, DosInt22h ); // Termination
3188 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
3189 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
3190 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
3191 RegisterDosInt32(0x2F, DosInt2Fh
);
3193 /* Load the EMS driver */
3194 if (!EmsDrvInitialize(EMS_TOTAL_PAGES
))
3196 DPRINT1("Could not initialize EMS. EMS will not be available.\n"
3197 "Try reducing the number of EMS pages.\n");
3200 /* Load the XMS driver (HIMEM) */
3203 /* Load the CON driver */