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 *******************************************************************/
23 #include "bios/bios.h"
26 #include "hardware/ps2.h"
28 /* PRIVATE VARIABLES **********************************************************/
30 CALLBACK16 DosContext
;
32 static DWORD DiskTransferArea
;
33 /*static*/ BYTE CurrentDrive
;
34 static CHAR LastDrive
= 'E';
35 static CHAR CurrentDirectories
[NUM_DRIVES
][DOS_DIR_LENGTH
];
36 static DOS_SFT_ENTRY DosSystemFileTable
[DOS_SFT_SIZE
];
37 static WORD DosErrorLevel
= 0x0000;
39 /* PUBLIC VARIABLES ***********************************************************/
41 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
42 BOOLEAN DoEcho
= FALSE
;
43 WORD CurrentPsp
= SYSTEM_PSP
;
44 WORD DosLastError
= 0;
46 /* PRIVATE FUNCTIONS **********************************************************/
48 static WORD
DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL
,
51 PCHAR Ptr
, DestBuffer
= NULL
;
55 /* If we have an environment strings list, compute its size */
58 /* Calculate the size of the environment block */
59 Ptr
= (PCHAR
)Environment
;
60 while (*Ptr
) Ptr
+= strlen(Ptr
) + 1;
61 TotalSize
= (ULONG_PTR
)Ptr
- (ULONG_PTR
)Environment
;
65 /* Empty environment string */
68 /* Add the final environment block NULL-terminator */
71 /* Add the two bytes for the program name tag */
74 /* Add the string buffer size */
75 TotalSize
+= strlen(ProgramName
) + 1;
77 /* Allocate the memory for the environment block */
78 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
79 if (!DestSegment
) return 0;
81 DestBuffer
= (PCHAR
)SEG_OFF_TO_PTR(DestSegment
, 0);
83 /* If we have an environment strings list, copy it */
86 Ptr
= (PCHAR
)Environment
;
89 /* Copy the string and NULL-terminate it */
90 strcpy(DestBuffer
, Ptr
);
91 DestBuffer
+= strlen(Ptr
);
92 *(DestBuffer
++) = '\0';
94 /* Move to the next string */
95 Ptr
+= strlen(Ptr
) + 1;
100 /* Empty environment string */
101 *(DestBuffer
++) = '\0';
103 /* NULL-terminate the environment block */
104 *(DestBuffer
++) = '\0';
106 /* Store the special program name tag */
107 *(DestBuffer
++) = LOBYTE(DOS_PROGRAM_NAME_TAG
);
108 *(DestBuffer
++) = HIBYTE(DOS_PROGRAM_NAME_TAG
);
110 /* Copy the program name after the environment block */
111 strcpy(DestBuffer
, ProgramName
);
116 /* Taken from base/shell/cmd/console.c */
117 static BOOL
IsConsoleHandle(HANDLE hHandle
)
121 /* Check whether the handle may be that of a console... */
122 if ((GetFileType(hHandle
) & FILE_TYPE_CHAR
) == 0) return FALSE
;
125 * It may be. Perform another test... The idea comes from the
126 * MSDN description of the WriteConsole API:
128 * "WriteConsole fails if it is used with a standard handle
129 * that is redirected to a file. If an application processes
130 * multilingual output that can be redirected, determine whether
131 * the output handle is a console handle (one method is to call
132 * the GetConsoleMode function and check whether it succeeds).
133 * If the handle is a console handle, call WriteConsole. If the
134 * handle is not a console handle, the output is redirected and
135 * you should call WriteFile to perform the I/O."
137 return GetConsoleMode(hHandle
, &dwMode
);
140 static inline PDOS_SFT_ENTRY
DosFindFreeSftEntry(VOID
)
144 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
146 if (DosSystemFileTable
[i
].Type
== DOS_SFT_ENTRY_NONE
)
148 return &DosSystemFileTable
[i
];
155 static inline PDOS_SFT_ENTRY
DosFindWin32SftEntry(HANDLE Handle
)
159 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
161 if (DosSystemFileTable
[i
].Type
== DOS_SFT_ENTRY_WIN32
162 && DosSystemFileTable
[i
].Handle
== Handle
)
164 return &DosSystemFileTable
[i
];
171 static inline PDOS_SFT_ENTRY
DosFindDeviceSftEntry(PDOS_DEVICE_NODE Device
)
175 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
177 if (DosSystemFileTable
[i
].Type
== DOS_SFT_ENTRY_DEVICE
178 && DosSystemFileTable
[i
].DeviceNode
== Device
)
180 return &DosSystemFileTable
[i
];
187 WORD
DosOpenHandle(HANDLE Handle
)
192 PDOS_SFT_ENTRY SftEntry
;
194 /* The system PSP has no handle table */
195 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
197 /* Get a pointer to the handle table */
198 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
199 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
201 /* Find a free entry in the JFT */
202 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
204 if (HandleTable
[DosHandle
] == 0xFF) break;
207 /* If there are no free entries, fail */
208 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
210 /* Check if the handle is already in the SFT */
211 SftEntry
= DosFindWin32SftEntry(Handle
);
212 if (SftEntry
!= NULL
)
214 /* Already in the table, reference it */
215 SftEntry
->RefCount
++;
219 /* Find a free SFT entry to use */
220 SftEntry
= DosFindFreeSftEntry();
221 if (SftEntry
== NULL
)
223 /* The SFT is full */
224 return INVALID_DOS_HANDLE
;
227 /* Initialize the empty table entry */
228 SftEntry
->Type
= DOS_SFT_ENTRY_WIN32
;
229 SftEntry
->Handle
= Handle
;
230 SftEntry
->RefCount
= 1;
234 /* Set the JFT entry to that SFT index */
235 HandleTable
[DosHandle
] = ARRAY_INDEX(SftEntry
, DosSystemFileTable
);
237 /* Return the new handle */
241 WORD
DosOpenDevice(PDOS_DEVICE_NODE Device
)
246 PDOS_SFT_ENTRY SftEntry
;
248 DPRINT("DosOpenDevice(\"%Z\")\n", &Device
->Name
);
250 /* The system PSP has no handle table */
251 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
253 /* Get a pointer to the handle table */
254 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
255 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
257 /* Find a free entry in the JFT */
258 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
260 if (HandleTable
[DosHandle
] == 0xFF) break;
263 /* If there are no free entries, fail */
264 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
266 /* Check if the device is already in the SFT */
267 SftEntry
= DosFindDeviceSftEntry(Device
);
268 if (SftEntry
!= NULL
)
270 /* Already in the table, reference it */
271 SftEntry
->RefCount
++;
275 /* Find a free SFT entry to use */
276 SftEntry
= DosFindFreeSftEntry();
277 if (SftEntry
== NULL
)
279 /* The SFT is full */
280 return INVALID_DOS_HANDLE
;
283 /* Initialize the empty table entry */
284 SftEntry
->Type
= DOS_SFT_ENTRY_DEVICE
;
285 SftEntry
->DeviceNode
= Device
;
286 SftEntry
->RefCount
= 1;
290 /* Call the open routine, if it exists */
291 if (Device
->OpenRoutine
) Device
->OpenRoutine(Device
);
293 /* Set the JFT entry to that SFT index */
294 HandleTable
[DosHandle
] = ARRAY_INDEX(SftEntry
, DosSystemFileTable
);
296 /* Return the new handle */
300 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
306 /* Clear the table first */
307 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
309 /* Check if this is the initial process */
310 if (CurrentPsp
== SYSTEM_PSP
)
312 PDOS_SFT_ENTRY SftEntry
;
313 HANDLE StandardHandles
[3];
314 PDOS_DEVICE_NODE Con
= DosGetDevice("CON");
317 /* Get the native standard handles */
318 StandardHandles
[0] = GetStdHandle(STD_INPUT_HANDLE
);
319 StandardHandles
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
320 StandardHandles
[2] = GetStdHandle(STD_ERROR_HANDLE
);
322 for (i
= 0; i
< 3; i
++)
324 /* Find the corresponding SFT entry */
325 if (IsConsoleHandle(StandardHandles
[i
]))
327 SftEntry
= DosFindDeviceSftEntry(Con
);
331 SftEntry
= DosFindWin32SftEntry(StandardHandles
[i
]);
334 if (SftEntry
== NULL
)
336 /* Create a new SFT entry for it */
337 SftEntry
= DosFindFreeSftEntry();
338 if (SftEntry
== NULL
)
340 DPRINT1("Cannot create standard handle %d, the SFT is full!\n", i
);
344 SftEntry
->RefCount
= 0;
346 if (IsConsoleHandle(StandardHandles
[i
]))
348 SftEntry
->Type
= DOS_SFT_ENTRY_DEVICE
;
349 SftEntry
->DeviceNode
= Con
;
351 /* Call the open routine */
352 if (Con
->OpenRoutine
) Con
->OpenRoutine(Con
);
356 SftEntry
->Type
= DOS_SFT_ENTRY_WIN32
;
357 SftEntry
->Handle
= StandardHandles
[i
];
361 SftEntry
->RefCount
++;
362 DestinationTable
[i
] = ARRAY_INDEX(SftEntry
, DosSystemFileTable
);
367 /* Get the parent PSP block and handle table */
368 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
369 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
371 /* Copy the first 20 handles into the new table */
372 for (i
= 0; i
< DEFAULT_JFT_SIZE
; i
++)
374 DestinationTable
[i
] = SourceTable
[i
];
376 /* Increase the reference count */
377 DosSystemFileTable
[SourceTable
[i
]].RefCount
++;
382 static BOOLEAN
DosResizeHandleTable(WORD NewSize
)
388 /* Get the PSP block */
389 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
391 if (NewSize
== PspBlock
->HandleTableSize
)
397 if (PspBlock
->HandleTableSize
> DEFAULT_JFT_SIZE
)
399 /* Get the segment of the current table */
400 Segment
= (LOWORD(PspBlock
->HandleTablePtr
) >> 4) + HIWORD(PspBlock
->HandleTablePtr
);
402 if (NewSize
<= DEFAULT_JFT_SIZE
)
404 /* Get the current handle table */
405 HandleTable
= FAR_POINTER(PspBlock
->HandleTablePtr
);
407 /* Copy it to the PSP */
408 RtlCopyMemory(PspBlock
->HandleTable
, HandleTable
, NewSize
);
410 /* Free the memory */
411 DosFreeMemory(Segment
);
413 /* Update the handle table pointer and size */
414 PspBlock
->HandleTableSize
= NewSize
;
415 PspBlock
->HandleTablePtr
= MAKELONG(0x18, CurrentPsp
);
419 /* Resize the memory */
420 if (!DosResizeMemory(Segment
, NewSize
, NULL
))
422 /* Unable to resize, try allocating it somewhere else */
423 Segment
= DosAllocateMemory(NewSize
, NULL
);
424 if (Segment
== 0) return FALSE
;
426 /* Get the new handle table */
427 HandleTable
= SEG_OFF_TO_PTR(Segment
, 0);
429 /* Copy the handles to the new table */
430 RtlCopyMemory(HandleTable
,
431 FAR_POINTER(PspBlock
->HandleTablePtr
),
432 PspBlock
->HandleTableSize
);
434 /* Update the handle table pointer */
435 PspBlock
->HandleTablePtr
= MAKELONG(0, Segment
);
438 /* Update the handle table size */
439 PspBlock
->HandleTableSize
= NewSize
;
442 else if (NewSize
> DEFAULT_JFT_SIZE
)
444 Segment
= DosAllocateMemory(NewSize
, NULL
);
445 if (Segment
== 0) return FALSE
;
447 /* Get the new handle table */
448 HandleTable
= SEG_OFF_TO_PTR(Segment
, 0);
450 /* Copy the handles from the PSP to the new table */
451 RtlCopyMemory(HandleTable
,
452 FAR_POINTER(PspBlock
->HandleTablePtr
),
453 PspBlock
->HandleTableSize
);
455 /* Update the handle table pointer and size */
456 PspBlock
->HandleTableSize
= NewSize
;
457 PspBlock
->HandleTablePtr
= MAKELONG(0, Segment
);
463 static BOOLEAN
DosCloseHandle(WORD DosHandle
)
467 PDOS_SFT_ENTRY SftEntry
;
469 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle
);
471 /* The system PSP has no handle table */
472 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
474 /* Get a pointer to the handle table */
475 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
476 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
478 /* Make sure the handle is open */
479 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
481 /* Make sure the SFT entry is valid */
482 SftEntry
= &DosSystemFileTable
[HandleTable
[DosHandle
]];
483 if (SftEntry
->Type
== DOS_SFT_ENTRY_NONE
) return FALSE
;
485 /* Decrement the reference count of the SFT entry */
486 SftEntry
->RefCount
--;
488 /* Check if the reference count fell to zero */
489 if (!SftEntry
->RefCount
)
491 switch (SftEntry
->Type
)
493 case DOS_SFT_ENTRY_WIN32
:
495 /* Close the win32 handle and clear it */
496 CloseHandle(SftEntry
->Handle
);
501 case DOS_SFT_ENTRY_DEVICE
:
503 PDOS_DEVICE_NODE Node
= SftEntry
->DeviceNode
;
505 /* Call the close routine, if it exists */
506 if (Node
->CloseRoutine
) SftEntry
->DeviceNode
->CloseRoutine(SftEntry
->DeviceNode
);
513 /* Shouldn't happen */
518 /* Invalidate the SFT entry */
519 SftEntry
->Type
= DOS_SFT_ENTRY_NONE
;
522 /* Clear the entry in the JFT */
523 HandleTable
[DosHandle
] = 0xFF;
528 static BOOLEAN
DosDuplicateHandle(WORD OldHandle
, WORD NewHandle
)
534 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
538 /* The system PSP has no handle table */
539 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
541 /* Get a pointer to the handle table */
542 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
543 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
545 /* Make sure the old handle is open */
546 if (HandleTable
[OldHandle
] == 0xFF) return FALSE
;
548 /* Check if the new handle is open */
549 if (HandleTable
[NewHandle
] != 0xFF)
552 DosCloseHandle(NewHandle
);
555 /* Increment the reference count of the SFT entry */
556 SftIndex
= HandleTable
[OldHandle
];
557 DosSystemFileTable
[SftIndex
].RefCount
++;
559 /* Make the new handle point to that SFT entry */
560 HandleTable
[NewHandle
] = SftIndex
;
566 static BOOLEAN
DosChangeDrive(BYTE Drive
)
568 WCHAR DirectoryPath
[DOS_CMDLINE_LENGTH
];
570 /* Make sure the drive exists */
571 if (Drive
> (LastDrive
- 'A')) return FALSE
;
573 /* Find the path to the new current directory */
574 swprintf(DirectoryPath
, L
"%c\\%S", Drive
+ 'A', CurrentDirectories
[Drive
]);
576 /* Change the current directory of the process */
577 if (!SetCurrentDirectory(DirectoryPath
)) return FALSE
;
579 /* Set the current drive */
580 CurrentDrive
= Drive
;
586 static BOOLEAN
DosChangeDirectory(LPSTR Directory
)
592 /* Make sure the directory path is not too long */
593 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
595 DosLastError
= ERROR_PATH_NOT_FOUND
;
599 /* Get the drive number */
600 DriveNumber
= Directory
[0] - 'A';
602 /* Make sure the drive exists */
603 if (DriveNumber
> (LastDrive
- 'A'))
605 DosLastError
= ERROR_PATH_NOT_FOUND
;
609 /* Get the file attributes */
610 Attributes
= GetFileAttributesA(Directory
);
612 /* Make sure the path exists and is a directory */
613 if ((Attributes
== INVALID_FILE_ATTRIBUTES
)
614 || !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
616 DosLastError
= ERROR_PATH_NOT_FOUND
;
620 /* Check if this is the current drive */
621 if (DriveNumber
== CurrentDrive
)
623 /* Change the directory */
624 if (!SetCurrentDirectoryA(Directory
))
626 DosLastError
= LOWORD(GetLastError());
631 /* Get the directory part of the path */
632 Path
= strchr(Directory
, '\\');
635 /* Skip the backslash */
639 /* Set the directory for the drive */
642 strncpy(CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
646 CurrentDirectories
[DriveNumber
][0] = '\0';
653 /* PUBLIC FUNCTIONS ***********************************************************/
655 PDOS_SFT_ENTRY
DosGetSftEntry(WORD DosHandle
)
660 /* The system PSP has no handle table */
661 if (CurrentPsp
== SYSTEM_PSP
) return NULL
;
663 /* Get a pointer to the handle table */
664 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
665 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
667 /* Make sure the handle is open */
668 if (HandleTable
[DosHandle
] == 0xFF) return NULL
;
670 /* Return a pointer to the SFT entry */
671 return &DosSystemFileTable
[HandleTable
[DosHandle
]];
674 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
676 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
677 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
679 RtlZeroMemory(PspBlock
, sizeof(*PspBlock
));
681 /* Set the exit interrupt */
682 PspBlock
->Exit
[0] = 0xCD; // int 0x20
683 PspBlock
->Exit
[1] = 0x20;
685 /* Set the number of the last paragraph */
686 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
688 /* Save the interrupt vectors */
689 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
690 PspBlock
->BreakAddress
= IntVecTable
[0x23];
691 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
693 /* Set the parent PSP */
694 PspBlock
->ParentPsp
= CurrentPsp
;
696 /* Copy the parent handle table */
697 DosCopyHandleTable(PspBlock
->HandleTable
);
699 /* Set the environment block */
700 PspBlock
->EnvBlock
= Environment
;
702 /* Set the handle table pointers to the internal handle table */
703 PspBlock
->HandleTableSize
= 20;
704 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
706 /* Set the DOS version */
707 PspBlock
->DosVersion
= DOS_VERSION
;
709 /* Set the far call opcodes */
710 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
711 PspBlock
->FarCall
[1] = 0x21;
712 PspBlock
->FarCall
[2] = 0xCB; // retf
714 /* Set the command line */
715 PspBlock
->CommandLineSize
= (BYTE
)min(strlen(CommandLine
), DOS_CMDLINE_LENGTH
- 1);
716 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
717 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
720 DWORD
DosLoadExecutable(IN DOS_EXEC_TYPE LoadType
,
721 IN LPCSTR ExecutablePath
,
722 IN LPCSTR CommandLine
,
723 IN LPCSTR Environment OPTIONAL
,
724 OUT PDWORD StackLocation OPTIONAL
,
725 OUT PDWORD EntryPoint OPTIONAL
)
727 DWORD Result
= ERROR_SUCCESS
;
728 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
729 LPBYTE Address
= NULL
;
733 DWORD i
, FileSize
, ExeSize
;
734 PIMAGE_DOS_HEADER Header
;
735 PDWORD RelocationTable
;
737 LPSTR CmdLinePtr
= (LPSTR
)CommandLine
;
739 DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
743 Environment
? Environment
: "n/a",
747 if (LoadType
== DOS_LOAD_OVERLAY
)
749 DPRINT1("Overlay loading is not supported yet.\n");
750 return ERROR_NOT_SUPPORTED
;
753 /* NULL-terminate the command line by removing the return carriage character */
754 while (*CmdLinePtr
&& *CmdLinePtr
!= '\r') CmdLinePtr
++;
757 /* Open a handle to the executable */
758 FileHandle
= CreateFileA(ExecutablePath
,
763 FILE_ATTRIBUTE_NORMAL
,
765 if (FileHandle
== INVALID_HANDLE_VALUE
)
767 Result
= GetLastError();
771 /* Get the file size */
772 FileSize
= GetFileSize(FileHandle
, NULL
);
774 /* Create a mapping object for the file */
775 FileMapping
= CreateFileMapping(FileHandle
,
781 if (FileMapping
== NULL
)
783 Result
= GetLastError();
787 /* Map the file into memory */
788 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
791 Result
= GetLastError();
795 /* Copy the environment block to DOS memory */
796 EnvBlock
= DosCopyEnvironmentBlock(Environment
, ExecutablePath
);
799 Result
= ERROR_NOT_ENOUGH_MEMORY
;
803 /* Check if this is an EXE file or a COM file */
804 if (Address
[0] == 'M' && Address
[1] == 'Z')
808 /* Get the MZ header */
809 Header
= (PIMAGE_DOS_HEADER
)Address
;
811 /* Get the base size of the file, in paragraphs (rounded up) */
812 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
814 /* Add the PSP size, in paragraphs */
815 ExeSize
+= sizeof(DOS_PSP
) >> 4;
817 /* Add the maximum size that should be allocated */
818 ExeSize
+= Header
->e_maxalloc
;
820 /* Make sure it does not pass 0xFFFF */
821 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
823 /* Try to allocate that much memory */
824 Segment
= DosAllocateMemory((WORD
)ExeSize
, &MaxAllocSize
);
828 /* Check if there's at least enough memory for the minimum size */
829 if (MaxAllocSize
< (ExeSize
- Header
->e_maxalloc
+ Header
->e_minalloc
))
831 Result
= DosLastError
;
835 /* Allocate that minimum amount */
836 ExeSize
= MaxAllocSize
;
837 Segment
= DosAllocateMemory((WORD
)ExeSize
, NULL
);
838 ASSERT(Segment
!= 0);
841 /* Initialize the PSP */
842 DosInitializePsp(Segment
,
847 /* The process owns its own memory */
848 DosChangeMemoryOwner(Segment
, Segment
);
849 DosChangeMemoryOwner(EnvBlock
, Segment
);
851 /* Copy the program to Segment:0100 */
852 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
853 Address
+ (Header
->e_cparhdr
<< 4),
854 min(FileSize
- (Header
->e_cparhdr
<< 4),
855 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
857 /* Get the relocation table */
858 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
860 /* Perform relocations */
861 for (i
= 0; i
< Header
->e_crlc
; i
++)
863 /* Get a pointer to the word that needs to be patched */
864 RelocWord
= (PWORD
)SEG_OFF_TO_PTR(Segment
+ HIWORD(RelocationTable
[i
]),
865 0x100 + LOWORD(RelocationTable
[i
]));
867 /* Add the number of the EXE segment to it */
868 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
871 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
873 /* Set the initial segment registers */
877 /* Set the stack to the location from the header */
878 setSS(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
);
882 CurrentPsp
= Segment
;
883 DiskTransferArea
= MAKELONG(0x80, Segment
);
884 CpuExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
892 /* Find the maximum amount of memory that can be allocated */
893 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
895 /* Make sure it's enough for the whole program and the PSP */
896 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
)))
898 Result
= ERROR_NOT_ENOUGH_MEMORY
;
902 /* Allocate all of it */
903 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
906 Result
= DosLastError
;
910 /* The process owns its own memory */
911 DosChangeMemoryOwner(Segment
, Segment
);
912 DosChangeMemoryOwner(EnvBlock
, Segment
);
914 /* Copy the program to Segment:0100 */
915 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
919 /* Initialize the PSP */
920 DosInitializePsp(Segment
,
925 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
927 /* Set the initial segment registers */
931 /* Set the stack to the last word of the segment */
936 * Set the value on the stack to 0, so that a near return
937 * jumps to PSP:0000 which has the exit code.
939 *((LPWORD
)SEG_OFF_TO_PTR(Segment
, 0xFFFE)) = 0;
942 CurrentPsp
= Segment
;
943 DiskTransferArea
= MAKELONG(0x80, Segment
);
944 CpuExecute(Segment
, 0x100);
949 if (Result
!= ERROR_SUCCESS
)
951 /* It was not successful, cleanup the DOS memory */
952 if (EnvBlock
) DosFreeMemory(EnvBlock
);
953 if (Segment
) DosFreeMemory(Segment
);
957 if (Address
!= NULL
) UnmapViewOfFile(Address
);
959 /* Close the file mapping object */
960 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
962 /* Close the file handle */
963 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
968 DWORD
DosStartProcess(IN LPCSTR ExecutablePath
,
969 IN LPCSTR CommandLine
,
970 IN LPCSTR Environment OPTIONAL
)
974 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
981 if (Result
!= ERROR_SUCCESS
) goto Quit
;
983 /* Attach to the console */
984 VidBiosAttachToConsole(); // FIXME: And in fact, attach the full NTVDM UI to the console
986 // HACK: Simulate a ENTER key release scancode on the PS/2 port because
987 // some apps expect to read a key release scancode (> 0x80) when they
989 IOWriteB(PS2_CONTROL_PORT
, 0xD2); // Next write is for the first PS/2 port
990 IOWriteB(PS2_DATA_PORT
, 0x80 | 0x1C); // ENTER key release
992 /* Start simulation */
993 SetEvent(VdmTaskEvent
);
996 /* Detach from the console */
997 VidBiosDetachFromConsole(); // FIXME: And in fact, detach the full NTVDM UI from the console
1004 WORD
DosCreateProcess(DOS_EXEC_TYPE LoadType
,
1006 PDOS_EXEC_PARAM_BLOCK Parameters
)
1010 LPVOID Environment
= NULL
;
1011 VDM_COMMAND_INFO CommandInfo
;
1012 CHAR CmdLine
[MAX_PATH
];
1013 CHAR AppName
[MAX_PATH
];
1014 CHAR PifFile
[MAX_PATH
];
1015 CHAR Desktop
[MAX_PATH
];
1016 CHAR Title
[MAX_PATH
];
1018 STARTUPINFOA StartupInfo
;
1019 PROCESS_INFORMATION ProcessInfo
;
1021 /* Get the binary type */
1022 if (!GetBinaryTypeA(ProgramName
, &BinaryType
)) return GetLastError();
1024 /* Did the caller specify an environment segment? */
1025 if (Parameters
->Environment
)
1027 /* Yes, use it instead of the parent one */
1028 Environment
= SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
1031 /* Set up the startup info structure */
1032 RtlZeroMemory(&StartupInfo
, sizeof(StartupInfo
));
1033 StartupInfo
.cb
= sizeof(StartupInfo
);
1035 /* Create the process */
1036 if (!CreateProcessA(ProgramName
,
1037 FAR_POINTER(Parameters
->CommandLine
),
1047 return GetLastError();
1050 /* Check the type of the program */
1053 /* These are handled by NTVDM */
1054 case SCS_DOS_BINARY
:
1055 case SCS_WOW_BINARY
:
1057 /* Clear the structure */
1058 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
1060 /* Initialize the structure members */
1061 CommandInfo
.TaskId
= SessionId
;
1062 CommandInfo
.VDMState
= VDM_FLAG_NESTED_TASK
| VDM_FLAG_DONT_WAIT
;
1063 CommandInfo
.CmdLine
= CmdLine
;
1064 CommandInfo
.CmdLen
= sizeof(CmdLine
);
1065 CommandInfo
.AppName
= AppName
;
1066 CommandInfo
.AppLen
= sizeof(AppName
);
1067 CommandInfo
.PifFile
= PifFile
;
1068 CommandInfo
.PifLen
= sizeof(PifFile
);
1069 CommandInfo
.Desktop
= Desktop
;
1070 CommandInfo
.DesktopLen
= sizeof(Desktop
);
1071 CommandInfo
.Title
= Title
;
1072 CommandInfo
.TitleLen
= sizeof(Title
);
1073 CommandInfo
.Env
= Env
;
1074 CommandInfo
.EnvLen
= sizeof(Env
);
1076 /* Get the VDM command information */
1077 if (!GetNextVDMCommand(&CommandInfo
))
1079 /* Shouldn't happen */
1083 /* Increment the re-entry count */
1084 CommandInfo
.VDMState
= VDM_INC_REENTER_COUNT
;
1085 GetNextVDMCommand(&CommandInfo
);
1087 /* Load the executable */
1088 Result
= DosLoadExecutable(LoadType
,
1092 &Parameters
->StackLocation
,
1093 &Parameters
->EntryPoint
);
1094 if (Result
!= ERROR_SUCCESS
)
1096 DisplayMessage(L
"Could not load '%S'. Error: %u", AppName
, Result
);
1097 // FIXME: Decrement the reenter count. Or, instead, just increment
1098 // the VDM reenter count *only* if this call succeeds...
1104 /* Not handled by NTVDM */
1107 /* Wait for the process to finish executing */
1108 WaitForSingleObject(ProcessInfo
.hProcess
, INFINITE
);
1112 /* Close the handles */
1113 CloseHandle(ProcessInfo
.hProcess
);
1114 CloseHandle(ProcessInfo
.hThread
);
1116 return ERROR_SUCCESS
;
1120 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
)
1123 WORD McbSegment
= FIRST_MCB_SEGMENT
;
1124 PDOS_MCB CurrentMcb
;
1125 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
1126 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
1128 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
1132 /* Check if this PSP is it's own parent */
1133 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
1135 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
1137 /* Close the handle */
1141 /* Free the memory used by the process */
1144 /* Get a pointer to the MCB */
1145 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
1147 /* Make sure the MCB is valid */
1148 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!='Z') break;
1150 /* If this block was allocated by the process, free it */
1151 if (CurrentMcb
->OwnerPsp
== Psp
) DosFreeMemory(McbSegment
+ 1);
1153 /* If this was the last block, quit */
1154 if (CurrentMcb
->BlockType
== 'Z') break;
1156 /* Update the segment and continue */
1157 McbSegment
+= CurrentMcb
->Size
+ 1;
1161 /* Restore the interrupt vectors */
1162 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
1163 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
1164 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
1166 /* Update the current PSP */
1167 if (Psp
== CurrentPsp
)
1169 CurrentPsp
= PspBlock
->ParentPsp
;
1170 if (CurrentPsp
== SYSTEM_PSP
)
1172 ResetEvent(VdmTaskEvent
);
1178 // FIXME: This is probably not the best way to do it
1179 /* Check if this was a nested DOS task */
1180 if (CurrentPsp
!= SYSTEM_PSP
)
1182 VDM_COMMAND_INFO CommandInfo
;
1184 /* Decrement the re-entry count */
1185 CommandInfo
.TaskId
= SessionId
;
1186 CommandInfo
.VDMState
= VDM_DEC_REENTER_COUNT
;
1187 GetNextVDMCommand(&CommandInfo
);
1189 /* Clear the structure */
1190 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
1192 /* Update the VDM state of the task */
1193 CommandInfo
.TaskId
= SessionId
;
1194 CommandInfo
.VDMState
= VDM_FLAG_DONT_WAIT
;
1195 GetNextVDMCommand(&CommandInfo
);
1199 /* Save the return code - Normal termination */
1200 DosErrorLevel
= MAKEWORD(ReturnCode
, 0x00);
1202 /* Return control to the parent process */
1203 CpuExecute(HIWORD(PspBlock
->TerminateAddress
),
1204 LOWORD(PspBlock
->TerminateAddress
));
1207 BOOLEAN
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
1209 PDOS_SFT_ENTRY SftEntry
= DosGetSftEntry(FileHandle
);
1210 PDOS_DEVICE_NODE Node
= NULL
;
1212 /* Make sure it exists */
1215 DosLastError
= ERROR_FILE_NOT_FOUND
;
1219 if (SftEntry
->Type
== DOS_SFT_ENTRY_DEVICE
) Node
= SftEntry
->DeviceNode
;
1221 switch (ControlCode
)
1223 /* Get Device Information */
1229 * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
1230 * for a list of possible flags.
1235 /* Return the device attributes with bit 7 set */
1236 InfoWord
= Node
->DeviceAttributes
| (1 << 7);
1243 /* Set Device Information */
1246 // TODO: NOT IMPLEMENTED
1252 /* Read From Device I/O Control Channel */
1255 WORD Length
= getCX();
1257 if (Node
== NULL
|| !(Node
->DeviceAttributes
& DOS_DEVATTR_IOCTL
))
1259 DosLastError
= ERROR_INVALID_FUNCTION
;
1263 /* Do nothing if there is no IOCTL routine */
1264 if (!Node
->IoctlReadRoutine
)
1270 Node
->IoctlReadRoutine(Node
, MAKELONG(getDX(), getDS()), &Length
);
1276 /* Write To Device I/O Control Channel */
1279 WORD Length
= getCX();
1281 if (Node
== NULL
|| !(Node
->DeviceAttributes
& DOS_DEVATTR_IOCTL
))
1283 DosLastError
= ERROR_INVALID_FUNCTION
;
1287 /* Do nothing if there is no IOCTL routine */
1288 if (!Node
->IoctlWriteRoutine
)
1294 Node
->IoctlWriteRoutine(Node
, MAKELONG(getDX(), getDS()), &Length
);
1300 /* Unsupported control code */
1303 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
1305 DosLastError
= ERROR_INVALID_PARAMETER
;
1311 VOID WINAPI
DosInt20h(LPWORD Stack
)
1313 /* This is the exit interrupt */
1314 DosTerminateProcess(Stack
[STACK_CS
], 0);
1317 VOID WINAPI
DosInt21h(LPWORD Stack
)
1320 SYSTEMTIME SystemTime
;
1322 PDOS_INPUT_BUFFER InputBuffer
;
1323 PDOS_COUNTRY_CODE_BUFFER CountryCodeBuffer
;
1326 /* Check the value in the AH register */
1329 /* Terminate Program */
1332 DosTerminateProcess(Stack
[STACK_CS
], 0);
1336 /* Read Character from STDIN with Echo */
1339 DPRINT("INT 21h, AH = 01h\n");
1341 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
1343 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1346 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
1347 // Check also Ctrl-P and set echo-to-printer flag.
1348 // Ctrl-Z is not interpreted.
1354 /* Write Character to STDOUT */
1357 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1358 Character
= getDL();
1359 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1362 * We return the output character (DOS 2.1+).
1363 * Also, if we're going to output a TAB, then
1364 * don't return a TAB but a SPACE instead.
1365 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
1366 * for more information.
1368 setAL(Character
== '\t' ? ' ' : Character
);
1372 /* Read Character from STDAUX */
1375 // FIXME: Really read it from STDAUX!
1376 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
1377 // setAL(DosReadCharacter());
1381 /* Write Character to STDAUX */
1384 // FIXME: Really write it to STDAUX!
1385 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
1386 // DosPrintCharacter(getDL());
1390 /* Write Character to Printer */
1393 // FIXME: Really write it to printer!
1394 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
1395 DPRINT1("0x%p\n", getDL());
1396 DPRINT1("\n\n-----------\n\n");
1400 /* Direct Console I/O */
1403 Character
= getDL();
1405 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1407 if (Character
!= 0xFF)
1410 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1413 * We return the output character (DOS 2.1+).
1414 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
1415 * for more information.
1422 if (DosCheckInput())
1424 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1425 setAL(DosReadCharacter(DOS_INPUT_HANDLE
));
1429 /* No character available */
1430 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1438 /* Character Input without Echo */
1442 DPRINT("Char input without echo\n");
1444 // FIXME: Under DOS 2+, input handle may be redirected!!!!
1445 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1447 // FIXME: For 0x07, do not check Ctrl-C/Break.
1448 // For 0x08, do check those control sequences and if needed,
1451 // /* Let the BOP repeat if needed */
1452 // if (getCF()) break;
1458 /* Write string to STDOUT */
1461 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1463 while (*String
!= '$')
1465 DosPrintCharacter(DOS_OUTPUT_HANDLE
, *String
);
1470 * We return the terminating character (DOS 2.1+).
1471 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
1472 * for more information.
1474 setAL('$'); // *String
1478 /* Read Buffered Input */
1482 InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
1484 DPRINT("Read Buffered Input\n");
1486 while (Count
< InputBuffer
->MaxLength
)
1488 // FIXME!! This function should interpret backspaces etc...
1490 /* Try to read a character (wait) */
1491 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1493 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
1495 /* Echo the character and append it to the buffer */
1496 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1497 InputBuffer
->Buffer
[Count
] = Character
;
1499 Count
++; /* Carriage returns are also counted */
1501 if (Character
== '\r') break;
1504 /* Update the length */
1505 InputBuffer
->Length
= Count
;
1510 /* Get STDIN Status */
1513 setAL(DosCheckInput() ? 0xFF : 0x00);
1517 /* Flush Buffer and Read STDIN */
1520 BYTE InputFunction
= getAL();
1522 /* Flush STDIN buffer */
1523 DosFlushFileBuffers(DOS_INPUT_HANDLE
);
1526 * If the input function number contained in AL is valid, i.e.
1527 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
1528 * recursively with AL == AH.
1530 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
1531 InputFunction
== 0x07 || InputFunction
== 0x08 ||
1532 InputFunction
== 0x0A)
1534 /* Call ourselves recursively */
1535 setAH(InputFunction
);
1544 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1546 // TODO: Flush what's needed.
1547 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
1549 /* Clear CF in DOS 6 only */
1550 if (PspBlock
->DosVersion
== 0x0006)
1551 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1556 /* Set Default Drive */
1559 DosChangeDrive(getDL());
1560 setAL(LastDrive
- 'A' + 1);
1564 /* NULL Function for CP/M Compatibility */
1568 * This function corresponds to the CP/M BDOS function
1569 * "get bit map of logged drives", which is meaningless
1572 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
1573 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
1574 * for more information.
1580 /* Get Default Drive */
1583 setAL(CurrentDrive
);
1587 /* Set Disk Transfer Area */
1590 DiskTransferArea
= MAKELONG(getDX(), getDS());
1594 /* NULL Function for CP/M Compatibility */
1599 * Function 0x1D corresponds to the CP/M BDOS function
1600 * "get bit map of read-only drives", which is meaningless
1602 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
1603 * for more information.
1605 * Function 0x1E corresponds to the CP/M BDOS function
1606 * "set file attributes", which was meaningless under MS-DOS 1.x.
1607 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
1608 * for more information.
1614 /* NULL Function for CP/M Compatibility */
1618 * This function corresponds to the CP/M BDOS function
1619 * "get/set default user (sublibrary) number", which is meaningless
1622 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
1623 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
1624 * for more information.
1630 /* Set Interrupt Vector */
1633 ULONG FarPointer
= MAKELONG(getDX(), getDS());
1634 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
1635 getAL(), HIWORD(FarPointer
), LOWORD(FarPointer
));
1637 /* Write the new far pointer to the IDT */
1638 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
1642 /* Create New PSP */
1645 DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
1649 /* Get System Date */
1652 GetLocalTime(&SystemTime
);
1653 setCX(SystemTime
.wYear
);
1654 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
1655 setAL(SystemTime
.wDayOfWeek
);
1659 /* Set System Date */
1662 GetLocalTime(&SystemTime
);
1663 SystemTime
.wYear
= getCX();
1664 SystemTime
.wMonth
= getDH();
1665 SystemTime
.wDay
= getDL();
1667 /* Return success or failure */
1668 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1672 /* Get System Time */
1675 GetLocalTime(&SystemTime
);
1676 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
1677 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
1681 /* Set System Time */
1684 GetLocalTime(&SystemTime
);
1685 SystemTime
.wHour
= getCH();
1686 SystemTime
.wMinute
= getCL();
1687 SystemTime
.wSecond
= getDH();
1688 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
1690 /* Return success or failure */
1691 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1695 /* Get Disk Transfer Area */
1698 setES(HIWORD(DiskTransferArea
));
1699 setBX(LOWORD(DiskTransferArea
));
1703 /* Get DOS Version */
1706 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1709 * DOS 2+ - GET DOS VERSION
1710 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
1711 * for more information.
1714 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
1717 * Return DOS OEM number:
1718 * 0x00 for IBM PC-DOS
1719 * 0x02 for packaged MS-DOS
1725 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
1728 * Return version flag:
1729 * 1 << 3 if DOS is in ROM,
1730 * 0 (reserved) if not.
1735 /* Return DOS 24-bit user serial number in BL:CX */
1740 * Return DOS version: Minor:Major in AH:AL
1741 * The Windows NT DOS box returns version 5.00, subject to SETVER.
1743 setAX(PspBlock
->DosVersion
);
1748 /* Extended functionalities */
1751 if (getAL() == 0x06)
1754 * DOS 5+ - GET TRUE VERSION NUMBER
1755 * This function always returns the true version number, unlike
1756 * AH=30h, whose return value may be changed with SETVER.
1757 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
1758 * for more information.
1762 * Return the true DOS version: Minor:Major in BH:BL
1763 * The Windows NT DOS box returns BX=3205h (version 5.50).
1765 setBX(NTDOS_VERSION
);
1767 /* DOS revision 0 */
1775 // /* Invalid subfunction */
1782 /* Get Interrupt Vector */
1785 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[getAL()];
1787 /* Read the address from the IDT into ES:BX */
1788 setES(HIWORD(FarPointer
));
1789 setBX(LOWORD(FarPointer
));
1793 /* SWITCH character - AVAILDEV */
1796 if (getAL() == 0x00)
1799 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
1800 * This setting is ignored by MS-DOS 4.0+.
1801 * MS-DOS 5+ always return AL=00h/DL=2Fh.
1802 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
1803 * for more information.
1808 else if (getAL() == 0x01)
1811 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
1812 * This setting is ignored by MS-DOS 5+.
1813 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
1814 * for more information.
1819 else if (getAL() == 0x02)
1822 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1823 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1824 * for more information.
1829 else if (getAL() == 0x03)
1832 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1833 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1834 * for more information.
1841 /* Invalid subfunction */
1848 /* Get/Set Country-dependent Information */
1851 CountryCodeBuffer
= (PDOS_COUNTRY_CODE_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
1853 if (getAL() == 0x00)
1856 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_IDATE
,
1857 &CountryCodeBuffer
->TimeFormat
,
1858 sizeof(CountryCodeBuffer
->TimeFormat
) / sizeof(TCHAR
));
1861 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1862 setAX(LOWORD(GetLastError()));
1866 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_SCURRENCY
,
1867 &CountryCodeBuffer
->CurrencySymbol
,
1868 sizeof(CountryCodeBuffer
->CurrencySymbol
) / sizeof(TCHAR
));
1871 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1872 setAX(LOWORD(GetLastError()));
1876 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
,
1877 &CountryCodeBuffer
->ThousandSep
,
1878 sizeof(CountryCodeBuffer
->ThousandSep
) / sizeof(TCHAR
));
1881 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1882 setAX(LOWORD(GetLastError()));
1886 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
,
1887 &CountryCodeBuffer
->DecimalSep
,
1888 sizeof(CountryCodeBuffer
->DecimalSep
) / sizeof(TCHAR
));
1891 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1892 setAX(LOWORD(GetLastError()));
1896 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1900 // TODO: NOT IMPLEMENTED
1907 /* Create Directory */
1910 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1912 if (CreateDirectoryA(String
, NULL
))
1914 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1918 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1919 setAX(LOWORD(GetLastError()));
1925 /* Remove Directory */
1928 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1930 if (RemoveDirectoryA(String
))
1932 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1936 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1937 setAX(LOWORD(GetLastError()));
1943 /* Set Current Directory */
1946 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1948 if (DosChangeDirectory(String
))
1950 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1954 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1955 setAX(DosLastError
);
1961 /* Create or Truncate File */
1965 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1966 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1970 if (ErrorCode
== ERROR_SUCCESS
)
1972 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1977 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1984 /* Open File or Device */
1989 LPCSTR FileName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1990 PDOS_DEVICE_NODE Device
= DosGetDevice(FileName
);
1994 FileHandle
= DosOpenDevice(Device
);
1995 ErrorCode
= (FileHandle
!= INVALID_DOS_HANDLE
)
1996 ? ERROR_SUCCESS
: ERROR_TOO_MANY_OPEN_FILES
;
2000 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, getAL());
2003 if (ErrorCode
== ERROR_SUCCESS
)
2005 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2010 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2017 /* Close File or Device */
2020 if (DosCloseHandle(getBX()))
2022 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2026 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2027 setAX(ERROR_INVALID_HANDLE
);
2033 /* Read from File or Device */
2039 DPRINT("DosReadFile(0x%04X)\n", getBX());
2042 ErrorCode
= DosReadFile(getBX(),
2043 MAKELONG(getDX(), getDS()),
2048 if (ErrorCode
== ERROR_SUCCESS
)
2050 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2053 else if (ErrorCode
!= ERROR_NOT_READY
)
2055 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2062 /* Write to File or Device */
2065 WORD BytesWritten
= 0;
2066 WORD ErrorCode
= DosWriteFile(getBX(),
2067 MAKELONG(getDX(), getDS()),
2071 if (ErrorCode
== ERROR_SUCCESS
)
2073 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2074 setAX(BytesWritten
);
2078 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2088 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2090 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
2092 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2094 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
2095 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
2097 setAL(FileName
[0] - 'A');
2101 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2102 setAX(GetLastError());
2112 WORD ErrorCode
= DosSeekFile(getBX(),
2113 MAKELONG(getDX(), getCX()),
2117 if (ErrorCode
== ERROR_SUCCESS
)
2119 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2121 /* Return the new offset in DX:AX */
2122 setDX(HIWORD(NewLocation
));
2123 setAX(LOWORD(NewLocation
));
2127 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2134 /* Get/Set File Attributes */
2138 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2140 if (getAL() == 0x00)
2142 /* Get the attributes */
2143 Attributes
= GetFileAttributesA(FileName
);
2145 /* Check if it failed */
2146 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
2148 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2149 setAX(GetLastError());
2153 /* Return the attributes that DOS can understand */
2154 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2155 setCX(Attributes
& 0x00FF);
2158 else if (getAL() == 0x01)
2160 /* Try to set the attributes */
2161 if (SetFileAttributesA(FileName
, getCL()))
2163 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2167 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2168 setAX(GetLastError());
2173 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2174 setAX(ERROR_INVALID_FUNCTION
);
2183 if (DosHandleIoctl(getAL(), getBX()))
2185 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2189 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2190 setAX(DosLastError
);
2196 /* Duplicate Handle */
2200 PDOS_SFT_ENTRY SftEntry
= DosGetSftEntry(getBX());
2202 if (SftEntry
== NULL
|| SftEntry
->Type
== DOS_SFT_ENTRY_NONE
)
2204 /* The handle is invalid */
2205 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2206 setAX(ERROR_INVALID_HANDLE
);
2210 /* Open a new handle to the same entry */
2211 switch (SftEntry
->Type
)
2213 case DOS_SFT_ENTRY_WIN32
:
2215 NewHandle
= DosOpenHandle(SftEntry
->Handle
);
2219 case DOS_SFT_ENTRY_DEVICE
:
2221 NewHandle
= DosOpenDevice(SftEntry
->DeviceNode
);
2227 /* Shouldn't happen */
2232 if (NewHandle
== INVALID_DOS_HANDLE
)
2234 /* Too many files open */
2235 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2236 setAX(ERROR_TOO_MANY_OPEN_FILES
);
2240 /* Return the result */
2241 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2246 /* Force Duplicate Handle */
2249 if (DosDuplicateHandle(getBX(), getCX()))
2251 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2255 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2256 setAX(ERROR_INVALID_HANDLE
);
2262 /* Get Current Directory */
2265 BYTE DriveNumber
= getDL();
2266 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
2268 /* Get the real drive number */
2269 if (DriveNumber
== 0)
2271 DriveNumber
= CurrentDrive
;
2275 /* Decrement DriveNumber since it was 1-based */
2279 if (DriveNumber
<= LastDrive
- 'A')
2282 * Copy the current directory into the target buffer.
2283 * It doesn't contain the drive letter and the backslash.
2285 strncpy(String
, CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
2286 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2287 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
2291 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2292 setAX(ERROR_INVALID_DRIVE
);
2298 /* Allocate Memory */
2301 WORD MaxAvailable
= 0;
2302 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
2306 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2311 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2312 setAX(DosLastError
);
2313 setBX(MaxAvailable
);
2322 if (DosFreeMemory(getES()))
2324 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2328 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2329 setAX(ERROR_ARENA_TRASHED
);
2335 /* Resize Memory Block */
2340 if (DosResizeMemory(getES(), getBX(), &Size
))
2342 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2346 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2347 setAX(DosLastError
);
2358 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)getAL();
2359 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
2360 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
2361 WORD ErrorCode
= DosCreateProcess(LoadType
, ProgramName
, ParamBlock
);
2363 if (ErrorCode
== ERROR_SUCCESS
)
2365 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2369 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2377 /* Terminate With Return Code */
2380 DosTerminateProcess(CurrentPsp
, getAL());
2384 /* Get Return Code (ERRORLEVEL) */
2388 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
2389 * DosErrorLevel is cleared after being read by this function.
2391 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2392 setAX(DosErrorLevel
);
2393 DosErrorLevel
= 0x0000; // Clear it
2397 /* Find First File */
2400 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(DiskTransferArea
),
2401 SEG_OFF_TO_PTR(getDS(), getDX()),
2406 if (Result
== ERROR_SUCCESS
)
2407 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2409 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2414 /* Find Next File */
2417 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(DiskTransferArea
));
2421 if (Result
== ERROR_SUCCESS
)
2422 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2424 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2429 /* Internal - Set Current Process ID (Set PSP Address) */
2432 // FIXME: Is it really what it's done ??
2433 CurrentPsp
= getBX();
2437 /* Internal - Get Current Process ID (Get PSP Address) */
2439 /* Get Current PSP Address */
2443 * Undocumented AH=51h is identical to the documented AH=62h.
2444 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
2445 * and http://www.ctyme.com/intr/rb-3140.htm
2446 * for more information.
2452 /* Internal - Get "List of lists" (SYSVARS) */
2456 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
2457 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
2458 * for more information.
2461 /* Return the DOS "list of lists" in ES:BX */
2465 DisplayMessage(L
"Required for AARD code, do you remember? :P");
2472 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2473 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
2476 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
2477 * for more information.
2480 if (MoveFileA(ExistingFileName
, NewFileName
))
2482 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2486 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2487 setAX(GetLastError());
2493 /* Get/Set Memory Management Options */
2496 if (getAL() == 0x00)
2498 /* Get allocation strategy */
2499 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2500 setAX(DosAllocStrategy
);
2502 else if (getAL() == 0x01)
2504 /* Set allocation strategy */
2506 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2507 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2509 /* Can't set both */
2510 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2511 setAX(ERROR_INVALID_PARAMETER
);
2515 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT
)
2517 /* Invalid allocation strategy */
2518 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2519 setAX(ERROR_INVALID_PARAMETER
);
2523 DosAllocStrategy
= getBL();
2524 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2526 else if (getAL() == 0x02)
2528 /* Get UMB link state */
2529 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2530 setAL(DosUmbLinked
? 0x01 : 0x00);
2532 else if (getAL() == 0x03)
2534 /* Set UMB link state */
2535 if (getBX()) DosLinkUmb();
2536 else DosUnlinkUmb();
2537 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2541 /* Invalid or unsupported function */
2542 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2543 setAX(ERROR_INVALID_FUNCTION
);
2549 /* Get Extended Error Information */
2552 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
2557 /* Create Temporary File */
2560 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2561 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
2567 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
2568 * for more information.
2571 // FIXME: Check for buffer validity?
2572 // It should be a ASCIZ path ending with a '\' + 13 zero bytes
2573 // to receive the generated filename.
2575 /* First create the temporary file */
2576 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
2579 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2580 setAX(GetLastError());
2584 /* Now try to open it in read/write access */
2585 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
2586 if (ErrorCode
== ERROR_SUCCESS
)
2588 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2593 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2600 /* Create New File */
2604 WORD ErrorCode
= DosCreateFile(&FileHandle
,
2605 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2609 if (ErrorCode
== ERROR_SUCCESS
)
2611 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2616 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2623 /* Lock/Unlock Region of File */
2626 PDOS_SFT_ENTRY SftEntry
= DosGetSftEntry(getBX());
2628 if (SftEntry
== NULL
|| SftEntry
->Type
!= DOS_SFT_ENTRY_WIN32
)
2630 /* The handle is invalid */
2631 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2632 setAX(ERROR_INVALID_HANDLE
);
2636 if (getAL() == 0x00)
2638 /* Lock region of file */
2639 if (LockFile(SftEntry
->Handle
,
2640 MAKELONG(getCX(), getDX()), 0,
2641 MAKELONG(getSI(), getDI()), 0))
2643 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2647 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2648 setAX(GetLastError());
2651 else if (getAL() == 0x01)
2653 /* Unlock region of file */
2654 if (UnlockFile(SftEntry
->Handle
,
2655 MAKELONG(getCX(), getDX()), 0,
2656 MAKELONG(getSI(), getDI()), 0))
2658 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2662 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2663 setAX(GetLastError());
2668 /* Invalid subfunction */
2669 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2670 setAX(ERROR_INVALID_FUNCTION
);
2676 /* Canonicalize File Name or Path */
2680 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
2681 * for more information.
2685 * We suppose that the DOS app gave to us a valid
2686 * 128-byte long buffer for the canonicalized name.
2688 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
2690 SEG_OFF_TO_PTR(getES(), getDI()),
2694 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2695 setAX(GetLastError());
2699 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2703 // FIXME: Convert the full path name into short version.
2704 // We cannot reliably use GetShortPathName, because it fails
2705 // if the path name given doesn't exist. However this DOS
2706 // function AH=60h should be able to work even for non-existing
2707 // path and file names.
2712 /* Set Handle Count */
2715 if (!DosResizeHandleTable(getBX()))
2717 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2718 setAX(DosLastError
);
2720 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2730 * Function 6Ah is identical to function 68h,
2731 * and sets AH to 68h if success.
2732 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
2733 * for more information.
2737 if (DosFlushFileBuffers(getBX()))
2739 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2743 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2744 setAX(GetLastError());
2750 /* Extended Open/Create */
2754 WORD CreationStatus
;
2757 /* Check for AL == 00 */
2758 if (getAL() != 0x00)
2760 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2761 setAX(ERROR_INVALID_FUNCTION
);
2766 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
2767 * for the full detailed description.
2769 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
2772 ErrorCode
= DosCreateFileEx(&FileHandle
,
2774 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI()),
2779 if (ErrorCode
== ERROR_SUCCESS
)
2781 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2782 setCX(CreationStatus
);
2787 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2797 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2800 setAL(0); // Some functions expect AL to be 0 when it's not supported.
2801 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2806 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
2808 UNREFERENCED_PARAMETER(Stack
);
2810 /* Stop the VDM task */
2811 ResetEvent(VdmTaskEvent
);
2815 VOID WINAPI
DosFastConOut(LPWORD Stack
)
2818 * This is the DOS 2+ Fast Console Output Interrupt.
2819 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
2821 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2822 * for more information.
2825 /* Save AX and BX */
2826 USHORT AX
= getAX();
2827 USHORT BX
= getBX();
2830 * Set the parameters:
2831 * AL contains the character to print (already set),
2832 * BL contains the character attribute,
2833 * BH contains the video page to use.
2835 setBL(DOS_CHAR_ATTRIBUTE
);
2836 setBH(Bda
->VideoPage
);
2838 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2840 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
2842 /* Restore AX and BX */
2847 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
2849 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2851 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2854 BOOLEAN
DosKRNLInitialize(VOID
)
2860 CHAR CurrentDirectory
[MAX_PATH
];
2861 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2867 /* Clear the current directory buffer */
2868 RtlZeroMemory(CurrentDirectories
, sizeof(CurrentDirectories
));
2870 /* Get the current directory */
2871 if (!GetCurrentDirectoryA(MAX_PATH
, CurrentDirectory
))
2873 // TODO: Use some kind of default path?
2877 /* Convert that to a DOS path */
2878 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, DOS_DIR_LENGTH
))
2880 // TODO: Use some kind of default path?
2885 CurrentDrive
= DosDirectory
[0] - 'A';
2887 /* Get the directory part of the path */
2888 Path
= strchr(DosDirectory
, '\\');
2891 /* Skip the backslash */
2895 /* Set the directory */
2898 strncpy(CurrentDirectories
[CurrentDrive
], Path
, DOS_DIR_LENGTH
);
2901 /* Read CONFIG.SYS */
2902 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
2905 while (fgetws(Buffer
, 256, Stream
))
2907 // TODO: Parse the line
2912 /* Initialize the SFT */
2913 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
2915 DosSystemFileTable
[i
].Type
= DOS_SFT_ENTRY_NONE
;
2916 DosSystemFileTable
[i
].RefCount
= 0;
2919 /* Load the EMS driver */
2922 /* Load the CON driver */
2927 /* Initialize the callback context */
2928 InitializeContext(&DosContext
, 0x0070, 0x0000);
2930 /* Register the DOS 32-bit Interrupts */
2931 RegisterDosInt32(0x20, DosInt20h
);
2932 RegisterDosInt32(0x21, DosInt21h
);
2933 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2934 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
2935 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2936 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
2937 RegisterDosInt32(0x2F, DosInt2Fh
);