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 /* Get System Date */
1774 GetLocalTime(&SystemTime
);
1775 setCX(SystemTime
.wYear
);
1776 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
1777 setAL(SystemTime
.wDayOfWeek
);
1781 /* Set System Date */
1784 GetLocalTime(&SystemTime
);
1785 SystemTime
.wYear
= getCX();
1786 SystemTime
.wMonth
= getDH();
1787 SystemTime
.wDay
= getDL();
1789 /* Return success or failure */
1790 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1794 /* Get System Time */
1797 GetLocalTime(&SystemTime
);
1798 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
1799 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
1803 /* Set System Time */
1806 GetLocalTime(&SystemTime
);
1807 SystemTime
.wHour
= getCH();
1808 SystemTime
.wMinute
= getCL();
1809 SystemTime
.wSecond
= getDH();
1810 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
1812 /* Return success or failure */
1813 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1817 /* Get Disk Transfer Area */
1820 setES(HIWORD(DiskTransferArea
));
1821 setBX(LOWORD(DiskTransferArea
));
1825 /* Get DOS Version */
1828 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1831 * DOS 2+ - GET DOS VERSION
1832 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
1833 * for more information.
1836 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
1839 * Return DOS OEM number:
1840 * 0x00 for IBM PC-DOS
1841 * 0x02 for packaged MS-DOS
1847 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
1850 * Return version flag:
1851 * 1 << 3 if DOS is in ROM,
1852 * 0 (reserved) if not.
1857 /* Return DOS 24-bit user serial number in BL:CX */
1862 * Return DOS version: Minor:Major in AH:AL
1863 * The Windows NT DOS box returns version 5.00, subject to SETVER.
1865 setAX(PspBlock
->DosVersion
);
1870 /* Terminate and Stay Resident */
1873 DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
1874 DosTerminateProcess(CurrentPsp
, getAL(), getDX());
1878 /* Extended functionalities */
1881 if (getAL() == 0x06)
1884 * DOS 5+ - GET TRUE VERSION NUMBER
1885 * This function always returns the true version number, unlike
1886 * AH=30h, whose return value may be changed with SETVER.
1887 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
1888 * for more information.
1892 * Return the true DOS version: Minor:Major in BH:BL
1893 * The Windows NT DOS box returns BX=3205h (version 5.50).
1895 setBX(NTDOS_VERSION
);
1897 /* DOS revision 0 */
1905 // /* Invalid subfunction */
1912 /* Get Address of InDOS flag */
1915 setES(HIWORD(INDOS_POINTER
));
1916 setBX(LOWORD(INDOS_POINTER
));
1921 /* Get Interrupt Vector */
1924 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[getAL()];
1926 /* Read the address from the IDT into ES:BX */
1927 setES(HIWORD(FarPointer
));
1928 setBX(LOWORD(FarPointer
));
1932 /* SWITCH character - AVAILDEV */
1935 if (getAL() == 0x00)
1938 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
1939 * This setting is ignored by MS-DOS 4.0+.
1940 * MS-DOS 5+ always return AL=00h/DL=2Fh.
1941 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
1942 * for more information.
1947 else if (getAL() == 0x01)
1950 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
1951 * This setting is ignored by MS-DOS 5+.
1952 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
1953 * for more information.
1958 else if (getAL() == 0x02)
1961 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1962 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1963 * for more information.
1968 else if (getAL() == 0x03)
1971 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1972 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1973 * for more information.
1980 /* Invalid subfunction */
1987 /* Get/Set Country-dependent Information */
1990 CountryCodeBuffer
= (PDOS_COUNTRY_CODE_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
1992 if (getAL() == 0x00)
1995 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_IDATE
,
1996 &CountryCodeBuffer
->TimeFormat
,
1997 sizeof(CountryCodeBuffer
->TimeFormat
) / sizeof(TCHAR
));
2000 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2001 setAX(LOWORD(GetLastError()));
2005 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_SCURRENCY
,
2006 &CountryCodeBuffer
->CurrencySymbol
,
2007 sizeof(CountryCodeBuffer
->CurrencySymbol
) / sizeof(TCHAR
));
2010 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2011 setAX(LOWORD(GetLastError()));
2015 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
,
2016 &CountryCodeBuffer
->ThousandSep
,
2017 sizeof(CountryCodeBuffer
->ThousandSep
) / sizeof(TCHAR
));
2020 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2021 setAX(LOWORD(GetLastError()));
2025 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
,
2026 &CountryCodeBuffer
->DecimalSep
,
2027 sizeof(CountryCodeBuffer
->DecimalSep
) / sizeof(TCHAR
));
2030 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2031 setAX(LOWORD(GetLastError()));
2035 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2039 // TODO: NOT IMPLEMENTED
2046 /* Create Directory */
2049 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2051 if (CreateDirectoryA(String
, NULL
))
2053 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2057 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2058 setAX(LOWORD(GetLastError()));
2064 /* Remove Directory */
2067 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2069 if (RemoveDirectoryA(String
))
2071 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2075 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2076 setAX(LOWORD(GetLastError()));
2082 /* Set Current Directory */
2085 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2087 if (DosChangeDirectory(String
))
2089 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2093 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2094 setAX(DosLastError
);
2100 /* Create or Truncate File */
2104 WORD ErrorCode
= DosCreateFile(&FileHandle
,
2105 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2109 if (ErrorCode
== ERROR_SUCCESS
)
2111 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2116 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2123 /* Open File or Device */
2128 LPCSTR FileName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2129 PDOS_DEVICE_NODE Device
= DosGetDevice(FileName
);
2133 FileHandle
= DosOpenDevice(Device
);
2134 ErrorCode
= (FileHandle
!= INVALID_DOS_HANDLE
)
2135 ? ERROR_SUCCESS
: ERROR_TOO_MANY_OPEN_FILES
;
2139 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, getAL());
2142 if (ErrorCode
== ERROR_SUCCESS
)
2144 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2149 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2156 /* Close File or Device */
2159 if (DosCloseHandle(getBX()))
2161 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2165 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2166 setAX(ERROR_INVALID_HANDLE
);
2172 /* Read from File or Device */
2178 DPRINT("DosReadFile(0x%04X)\n", getBX());
2181 ErrorCode
= DosReadFile(getBX(),
2182 MAKELONG(getDX(), getDS()),
2187 if (ErrorCode
== ERROR_SUCCESS
)
2189 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2192 else if (ErrorCode
!= ERROR_NOT_READY
)
2194 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2201 /* Write to File or Device */
2204 WORD BytesWritten
= 0;
2205 WORD ErrorCode
= DosWriteFile(getBX(),
2206 MAKELONG(getDX(), getDS()),
2210 if (ErrorCode
== ERROR_SUCCESS
)
2212 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2213 setAX(BytesWritten
);
2217 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2227 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2229 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
2231 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2233 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
2234 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
2236 setAL(FileName
[0] - 'A');
2240 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2241 setAX(GetLastError());
2251 WORD ErrorCode
= DosSeekFile(getBX(),
2252 MAKELONG(getDX(), getCX()),
2256 if (ErrorCode
== ERROR_SUCCESS
)
2258 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2260 /* Return the new offset in DX:AX */
2261 setDX(HIWORD(NewLocation
));
2262 setAX(LOWORD(NewLocation
));
2266 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2273 /* Get/Set File Attributes */
2277 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2279 if (getAL() == 0x00)
2281 /* Get the attributes */
2282 Attributes
= GetFileAttributesA(FileName
);
2284 /* Check if it failed */
2285 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
2287 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2288 setAX(GetLastError());
2292 /* Return the attributes that DOS can understand */
2293 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2294 setCX(Attributes
& 0x00FF);
2297 else if (getAL() == 0x01)
2299 /* Try to set the attributes */
2300 if (SetFileAttributesA(FileName
, getCL()))
2302 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2306 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2307 setAX(GetLastError());
2312 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2313 setAX(ERROR_INVALID_FUNCTION
);
2322 if (DosHandleIoctl(getAL(), getBX()))
2324 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2328 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2329 setAX(DosLastError
);
2335 /* Duplicate Handle */
2339 PDOS_SFT_ENTRY SftEntry
= DosGetSftEntry(getBX());
2341 if (SftEntry
== NULL
|| SftEntry
->Type
== DOS_SFT_ENTRY_NONE
)
2343 /* The handle is invalid */
2344 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2345 setAX(ERROR_INVALID_HANDLE
);
2349 /* Open a new handle to the same entry */
2350 switch (SftEntry
->Type
)
2352 case DOS_SFT_ENTRY_WIN32
:
2354 NewHandle
= DosOpenHandle(SftEntry
->Handle
);
2358 case DOS_SFT_ENTRY_DEVICE
:
2360 NewHandle
= DosOpenDevice(SftEntry
->DeviceNode
);
2366 /* Shouldn't happen */
2371 if (NewHandle
== INVALID_DOS_HANDLE
)
2373 /* Too many files open */
2374 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2375 setAX(ERROR_TOO_MANY_OPEN_FILES
);
2379 /* Return the result */
2380 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2385 /* Force Duplicate Handle */
2388 if (DosDuplicateHandle(getBX(), getCX()))
2390 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2394 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2395 setAX(ERROR_INVALID_HANDLE
);
2401 /* Get Current Directory */
2404 BYTE DriveNumber
= getDL();
2405 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
2407 /* Get the real drive number */
2408 if (DriveNumber
== 0)
2410 DriveNumber
= CurrentDrive
;
2414 /* Decrement DriveNumber since it was 1-based */
2418 if (DriveNumber
<= LastDrive
- 'A')
2421 * Copy the current directory into the target buffer.
2422 * It doesn't contain the drive letter and the backslash.
2424 strncpy(String
, CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
2425 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2426 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
2430 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2431 setAX(ERROR_INVALID_DRIVE
);
2437 /* Allocate Memory */
2440 WORD MaxAvailable
= 0;
2441 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
2445 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2450 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2451 setAX(DosLastError
);
2452 setBX(MaxAvailable
);
2461 if (DosFreeMemory(getES()))
2463 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2467 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2468 setAX(ERROR_ARENA_TRASHED
);
2474 /* Resize Memory Block */
2479 if (DosResizeMemory(getES(), getBX(), &Size
))
2481 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2485 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2486 setAX(DosLastError
);
2497 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)getAL();
2498 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
2499 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
2500 DWORD ReturnAddress
= MAKELONG(Stack
[STACK_IP
], Stack
[STACK_CS
]);
2501 WORD ErrorCode
= DosCreateProcess(LoadType
, ProgramName
, ParamBlock
, ReturnAddress
);
2503 if (ErrorCode
== ERROR_SUCCESS
)
2505 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2509 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2517 /* Terminate With Return Code */
2520 DosTerminateProcess(CurrentPsp
, getAL(), 0);
2524 /* Get Return Code (ERRORLEVEL) */
2528 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
2529 * DosErrorLevel is cleared after being read by this function.
2531 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2532 setAX(DosErrorLevel
);
2533 DosErrorLevel
= 0x0000; // Clear it
2537 /* Find First File */
2540 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(DiskTransferArea
),
2541 SEG_OFF_TO_PTR(getDS(), getDX()),
2546 if (Result
== ERROR_SUCCESS
)
2547 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2549 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2554 /* Find Next File */
2557 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(DiskTransferArea
));
2561 if (Result
== ERROR_SUCCESS
)
2562 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2564 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2569 /* Internal - Set Current Process ID (Set PSP Address) */
2572 // FIXME: Is it really what it's done ??
2573 CurrentPsp
= getBX();
2577 /* Internal - Get Current Process ID (Get PSP Address) */
2579 /* Get Current PSP Address */
2583 * Undocumented AH=51h is identical to the documented AH=62h.
2584 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
2585 * and http://www.ctyme.com/intr/rb-3140.htm
2586 * for more information.
2592 /* Internal - Get "List of lists" (SYSVARS) */
2596 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
2597 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
2598 * for more information.
2601 /* Return the DOS "list of lists" in ES:BX */
2605 DPRINT1("INT 21h, AH=52h: This application requires the internal DOS List of lists (SYSVARS). UNIMPLEMENTED\n");
2612 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2613 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
2616 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
2617 * for more information.
2620 if (MoveFileA(ExistingFileName
, NewFileName
))
2622 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2626 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2627 setAX(GetLastError());
2633 /* Get/Set Memory Management Options */
2636 if (getAL() == 0x00)
2638 /* Get allocation strategy */
2639 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2640 setAX(DosAllocStrategy
);
2642 else if (getAL() == 0x01)
2644 /* Set allocation strategy */
2646 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2647 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2649 /* Can't set both */
2650 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2651 setAX(ERROR_INVALID_PARAMETER
);
2655 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT
)
2657 /* Invalid allocation strategy */
2658 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2659 setAX(ERROR_INVALID_PARAMETER
);
2663 DosAllocStrategy
= getBL();
2664 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2666 else if (getAL() == 0x02)
2668 /* Get UMB link state */
2669 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2670 setAL(DosUmbLinked
? 0x01 : 0x00);
2672 else if (getAL() == 0x03)
2674 /* Set UMB link state */
2675 if (getBX()) DosLinkUmb();
2676 else DosUnlinkUmb();
2677 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2681 /* Invalid or unsupported function */
2682 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2683 setAX(ERROR_INVALID_FUNCTION
);
2689 /* Get Extended Error Information */
2692 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
2697 /* Create Temporary File */
2700 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2701 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
2707 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
2708 * for more information.
2711 // FIXME: Check for buffer validity?
2712 // It should be a ASCIZ path ending with a '\' + 13 zero bytes
2713 // to receive the generated filename.
2715 /* First create the temporary file */
2716 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
2719 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2720 setAX(GetLastError());
2724 /* Now try to open it in read/write access */
2725 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
2726 if (ErrorCode
== ERROR_SUCCESS
)
2728 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2733 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2740 /* Create New File */
2744 WORD ErrorCode
= DosCreateFile(&FileHandle
,
2745 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2749 if (ErrorCode
== ERROR_SUCCESS
)
2751 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2756 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2763 /* Lock/Unlock Region of File */
2766 PDOS_SFT_ENTRY SftEntry
= DosGetSftEntry(getBX());
2768 if (SftEntry
== NULL
|| SftEntry
->Type
!= DOS_SFT_ENTRY_WIN32
)
2770 /* The handle is invalid */
2771 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2772 setAX(ERROR_INVALID_HANDLE
);
2776 if (getAL() == 0x00)
2778 /* Lock region of file */
2779 if (LockFile(SftEntry
->Handle
,
2780 MAKELONG(getCX(), getDX()), 0,
2781 MAKELONG(getSI(), getDI()), 0))
2783 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2787 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2788 setAX(GetLastError());
2791 else if (getAL() == 0x01)
2793 /* Unlock region of file */
2794 if (UnlockFile(SftEntry
->Handle
,
2795 MAKELONG(getCX(), getDX()), 0,
2796 MAKELONG(getSI(), getDI()), 0))
2798 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2802 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2803 setAX(GetLastError());
2808 /* Invalid subfunction */
2809 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2810 setAX(ERROR_INVALID_FUNCTION
);
2816 /* Canonicalize File Name or Path */
2820 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
2821 * for more information.
2825 * We suppose that the DOS app gave to us a valid
2826 * 128-byte long buffer for the canonicalized name.
2828 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
2830 SEG_OFF_TO_PTR(getES(), getDI()),
2834 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2835 setAX(GetLastError());
2839 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2843 // FIXME: Convert the full path name into short version.
2844 // We cannot reliably use GetShortPathName, because it fails
2845 // if the path name given doesn't exist. However this DOS
2846 // function AH=60h should be able to work even for non-existing
2847 // path and file names.
2852 /* Set Handle Count */
2855 if (!DosResizeHandleTable(getBX()))
2857 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2858 setAX(DosLastError
);
2860 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2870 * Function 6Ah is identical to function 68h,
2871 * and sets AH to 68h if success.
2872 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
2873 * for more information.
2877 if (DosFlushFileBuffers(getBX()))
2879 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2883 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2884 setAX(GetLastError());
2890 /* Extended Open/Create */
2894 WORD CreationStatus
;
2897 /* Check for AL == 00 */
2898 if (getAL() != 0x00)
2900 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2901 setAX(ERROR_INVALID_FUNCTION
);
2906 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
2907 * for the full detailed description.
2909 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
2912 ErrorCode
= DosCreateFileEx(&FileHandle
,
2914 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI()),
2919 if (ErrorCode
== ERROR_SUCCESS
)
2921 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2922 setCX(CreationStatus
);
2927 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2937 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2940 setAL(0); // Some functions expect AL to be 0 when it's not supported.
2941 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2948 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
2950 /* Set CF to terminate the running process */
2951 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2954 VOID WINAPI
DosFastConOut(LPWORD Stack
)
2957 * This is the DOS 2+ Fast Console Output Interrupt.
2958 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
2960 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2961 * for more information.
2964 /* Save AX and BX */
2965 USHORT AX
= getAX();
2966 USHORT BX
= getBX();
2969 * Set the parameters:
2970 * AL contains the character to print (already set),
2971 * BL contains the character attribute,
2972 * BH contains the video page to use.
2974 setBL(DOS_CHAR_ATTRIBUTE
);
2975 setBH(Bda
->VideoPage
);
2977 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2979 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
2981 /* Restore AX and BX */
2986 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
2990 /* Extended Memory Specification */
2994 if (!XmsGetDriverEntry(&DriverEntry
)) break;
2996 if (getAL() == 0x00)
2998 /* The driver is loaded */
3001 else if (getAL() == 0x10)
3003 setES(HIWORD(DriverEntry
));
3004 setBX(LOWORD(DriverEntry
));
3008 DPRINT1("Unknown DOS XMS Function: INT 0x2F, AH = 43h, AL = %xh\n", getAL());
3016 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
3018 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
3023 BOOLEAN
DosKRNLInitialize(VOID
)
3029 CHAR CurrentDirectory
[MAX_PATH
];
3030 CHAR DosDirectory
[DOS_DIR_LENGTH
];
3036 /* Setup the InDOS flag */
3037 InDos
= (PBYTE
)FAR_POINTER(INDOS_POINTER
);
3040 /* Clear the current directory buffer */
3041 RtlZeroMemory(CurrentDirectories
, sizeof(CurrentDirectories
));
3043 /* Get the current directory */
3044 if (!GetCurrentDirectoryA(MAX_PATH
, CurrentDirectory
))
3046 // TODO: Use some kind of default path?
3050 /* Convert that to a DOS path */
3051 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, DOS_DIR_LENGTH
))
3053 // TODO: Use some kind of default path?
3058 CurrentDrive
= DosDirectory
[0] - 'A';
3060 /* Get the directory part of the path */
3061 Path
= strchr(DosDirectory
, '\\');
3064 /* Skip the backslash */
3068 /* Set the directory */
3071 strncpy(CurrentDirectories
[CurrentDrive
], Path
, DOS_DIR_LENGTH
);
3074 /* Read CONFIG.SYS */
3075 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
3078 while (fgetws(Buffer
, 256, Stream
))
3080 // TODO: Parse the line
3085 /* Initialize the SFT */
3086 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
3088 DosSystemFileTable
[i
].Type
= DOS_SFT_ENTRY_NONE
;
3089 DosSystemFileTable
[i
].RefCount
= 0;
3094 /* Initialize the callback context */
3095 InitializeContext(&DosContext
, 0x0070, 0x0000);
3097 /* Register the DOS 32-bit Interrupts */
3098 RegisterDosInt32(0x20, DosInt20h
);
3099 RegisterDosInt32(0x21, DosInt21h
);
3100 // RegisterDosInt32(0x22, DosInt22h ); // Termination
3101 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
3102 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
3103 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
3104 RegisterDosInt32(0x2F, DosInt2Fh
);
3106 /* Load the EMS driver */
3107 if (!EmsDrvInitialize(EMS_TOTAL_PAGES
))
3109 DPRINT1("Could not initialize EMS. EMS will not be available.\n"
3110 "Try reducing the number of EMS pages.\n");
3113 /* Load the XMS driver (HIMEM) */
3116 /* Load the CON driver */