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 CALLBACK16 DosContext
;
36 static DWORD DiskTransferArea
;
37 /*static*/ BYTE CurrentDrive
;
38 static CHAR LastDrive
= 'E';
39 static CHAR CurrentDirectories
[NUM_DRIVES
][DOS_DIR_LENGTH
];
40 static DOS_SFT_ENTRY DosSystemFileTable
[DOS_SFT_SIZE
];
41 static WORD DosErrorLevel
= 0x0000;
43 /* PUBLIC VARIABLES ***********************************************************/
45 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
46 BOOLEAN DoEcho
= FALSE
;
47 WORD CurrentPsp
= SYSTEM_PSP
;
48 WORD DosLastError
= 0;
50 /* PRIVATE FUNCTIONS **********************************************************/
52 static WORD
DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL
,
55 PCHAR Ptr
, DestBuffer
= NULL
;
59 /* If we have an environment strings list, compute its size */
62 /* Calculate the size of the environment block */
63 Ptr
= (PCHAR
)Environment
;
64 while (*Ptr
) Ptr
+= strlen(Ptr
) + 1;
65 TotalSize
= (ULONG_PTR
)Ptr
- (ULONG_PTR
)Environment
;
69 /* Empty environment string */
72 /* Add the final environment block NULL-terminator */
75 /* Add the two bytes for the program name tag */
78 /* Add the string buffer size */
79 TotalSize
+= strlen(ProgramName
) + 1;
81 /* Allocate the memory for the environment block */
82 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
83 if (!DestSegment
) return 0;
85 DestBuffer
= (PCHAR
)SEG_OFF_TO_PTR(DestSegment
, 0);
87 /* If we have an environment strings list, copy it */
90 Ptr
= (PCHAR
)Environment
;
93 /* Copy the string and NULL-terminate it */
94 strcpy(DestBuffer
, Ptr
);
95 DestBuffer
+= strlen(Ptr
);
96 *(DestBuffer
++) = '\0';
98 /* Move to the next string */
99 Ptr
+= strlen(Ptr
) + 1;
104 /* Empty environment string */
105 *(DestBuffer
++) = '\0';
107 /* NULL-terminate the environment block */
108 *(DestBuffer
++) = '\0';
110 /* Store the special program name tag */
111 *(DestBuffer
++) = LOBYTE(DOS_PROGRAM_NAME_TAG
);
112 *(DestBuffer
++) = HIBYTE(DOS_PROGRAM_NAME_TAG
);
114 /* Copy the program name after the environment block */
115 strcpy(DestBuffer
, ProgramName
);
120 /* Taken from base/shell/cmd/console.c */
121 static BOOL
IsConsoleHandle(HANDLE hHandle
)
125 /* Check whether the handle may be that of a console... */
126 if ((GetFileType(hHandle
) & FILE_TYPE_CHAR
) == 0) return FALSE
;
129 * It may be. Perform another test... The idea comes from the
130 * MSDN description of the WriteConsole API:
132 * "WriteConsole fails if it is used with a standard handle
133 * that is redirected to a file. If an application processes
134 * multilingual output that can be redirected, determine whether
135 * the output handle is a console handle (one method is to call
136 * the GetConsoleMode function and check whether it succeeds).
137 * If the handle is a console handle, call WriteConsole. If the
138 * handle is not a console handle, the output is redirected and
139 * you should call WriteFile to perform the I/O."
141 return GetConsoleMode(hHandle
, &dwMode
);
144 static inline PDOS_SFT_ENTRY
DosFindFreeSftEntry(VOID
)
148 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
150 if (DosSystemFileTable
[i
].Type
== DOS_SFT_ENTRY_NONE
)
152 return &DosSystemFileTable
[i
];
159 static inline PDOS_SFT_ENTRY
DosFindWin32SftEntry(HANDLE Handle
)
163 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
165 if (DosSystemFileTable
[i
].Type
== DOS_SFT_ENTRY_WIN32
166 && DosSystemFileTable
[i
].Handle
== Handle
)
168 return &DosSystemFileTable
[i
];
175 static inline PDOS_SFT_ENTRY
DosFindDeviceSftEntry(PDOS_DEVICE_NODE Device
)
179 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
181 if (DosSystemFileTable
[i
].Type
== DOS_SFT_ENTRY_DEVICE
182 && DosSystemFileTable
[i
].DeviceNode
== Device
)
184 return &DosSystemFileTable
[i
];
191 WORD
DosOpenHandle(HANDLE Handle
)
196 PDOS_SFT_ENTRY SftEntry
;
198 /* The system PSP has no handle table */
199 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
201 /* Get a pointer to the handle table */
202 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
203 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
205 /* Find a free entry in the JFT */
206 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
208 if (HandleTable
[DosHandle
] == 0xFF) break;
211 /* If there are no free entries, fail */
212 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
214 /* Check if the handle is already in the SFT */
215 SftEntry
= DosFindWin32SftEntry(Handle
);
216 if (SftEntry
!= NULL
)
218 /* Already in the table, reference it */
219 SftEntry
->RefCount
++;
223 /* Find a free SFT entry to use */
224 SftEntry
= DosFindFreeSftEntry();
225 if (SftEntry
== NULL
)
227 /* The SFT is full */
228 return INVALID_DOS_HANDLE
;
231 /* Initialize the empty table entry */
232 SftEntry
->Type
= DOS_SFT_ENTRY_WIN32
;
233 SftEntry
->Handle
= Handle
;
234 SftEntry
->RefCount
= 1;
238 /* Set the JFT entry to that SFT index */
239 HandleTable
[DosHandle
] = ARRAY_INDEX(SftEntry
, DosSystemFileTable
);
241 /* Return the new handle */
245 WORD
DosOpenDevice(PDOS_DEVICE_NODE Device
)
250 PDOS_SFT_ENTRY SftEntry
;
252 DPRINT("DosOpenDevice(\"%Z\")\n", &Device
->Name
);
254 /* The system PSP has no handle table */
255 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
257 /* Get a pointer to the handle table */
258 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
259 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
261 /* Find a free entry in the JFT */
262 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
264 if (HandleTable
[DosHandle
] == 0xFF) break;
267 /* If there are no free entries, fail */
268 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
270 /* Check if the device is already in the SFT */
271 SftEntry
= DosFindDeviceSftEntry(Device
);
272 if (SftEntry
!= NULL
)
274 /* Already in the table, reference it */
275 SftEntry
->RefCount
++;
279 /* Find a free SFT entry to use */
280 SftEntry
= DosFindFreeSftEntry();
281 if (SftEntry
== NULL
)
283 /* The SFT is full */
284 return INVALID_DOS_HANDLE
;
287 /* Initialize the empty table entry */
288 SftEntry
->Type
= DOS_SFT_ENTRY_DEVICE
;
289 SftEntry
->DeviceNode
= Device
;
290 SftEntry
->RefCount
= 1;
294 /* Call the open routine, if it exists */
295 if (Device
->OpenRoutine
) Device
->OpenRoutine(Device
);
297 /* Set the JFT entry to that SFT index */
298 HandleTable
[DosHandle
] = ARRAY_INDEX(SftEntry
, DosSystemFileTable
);
300 /* Return the new handle */
304 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
310 /* Clear the table first */
311 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
313 /* Check if this is the initial process */
314 if (CurrentPsp
== SYSTEM_PSP
)
316 PDOS_SFT_ENTRY SftEntry
;
317 HANDLE StandardHandles
[3];
318 PDOS_DEVICE_NODE Con
= DosGetDevice("CON");
321 /* Get the native standard handles */
322 StandardHandles
[0] = GetStdHandle(STD_INPUT_HANDLE
);
323 StandardHandles
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
324 StandardHandles
[2] = GetStdHandle(STD_ERROR_HANDLE
);
326 for (i
= 0; i
< 3; i
++)
328 /* Find the corresponding SFT entry */
329 if (IsConsoleHandle(StandardHandles
[i
]))
331 SftEntry
= DosFindDeviceSftEntry(Con
);
335 SftEntry
= DosFindWin32SftEntry(StandardHandles
[i
]);
338 if (SftEntry
== NULL
)
340 /* Create a new SFT entry for it */
341 SftEntry
= DosFindFreeSftEntry();
342 if (SftEntry
== NULL
)
344 DPRINT1("Cannot create standard handle %d, the SFT is full!\n", i
);
348 SftEntry
->RefCount
= 0;
350 if (IsConsoleHandle(StandardHandles
[i
]))
352 SftEntry
->Type
= DOS_SFT_ENTRY_DEVICE
;
353 SftEntry
->DeviceNode
= Con
;
355 /* Call the open routine */
356 if (Con
->OpenRoutine
) Con
->OpenRoutine(Con
);
360 SftEntry
->Type
= DOS_SFT_ENTRY_WIN32
;
361 SftEntry
->Handle
= StandardHandles
[i
];
365 SftEntry
->RefCount
++;
366 DestinationTable
[i
] = ARRAY_INDEX(SftEntry
, DosSystemFileTable
);
371 /* Get the parent PSP block and handle table */
372 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
373 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
375 /* Copy the first 20 handles into the new table */
376 for (i
= 0; i
< DEFAULT_JFT_SIZE
; i
++)
378 DestinationTable
[i
] = SourceTable
[i
];
380 /* Increase the reference count */
381 DosSystemFileTable
[SourceTable
[i
]].RefCount
++;
386 static BOOLEAN
DosResizeHandleTable(WORD NewSize
)
392 /* Get the PSP block */
393 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
395 if (NewSize
== PspBlock
->HandleTableSize
)
401 if (PspBlock
->HandleTableSize
> DEFAULT_JFT_SIZE
)
403 /* Get the segment of the current table */
404 Segment
= (LOWORD(PspBlock
->HandleTablePtr
) >> 4) + HIWORD(PspBlock
->HandleTablePtr
);
406 if (NewSize
<= DEFAULT_JFT_SIZE
)
408 /* Get the current handle table */
409 HandleTable
= FAR_POINTER(PspBlock
->HandleTablePtr
);
411 /* Copy it to the PSP */
412 RtlCopyMemory(PspBlock
->HandleTable
, HandleTable
, NewSize
);
414 /* Free the memory */
415 DosFreeMemory(Segment
);
417 /* Update the handle table pointer and size */
418 PspBlock
->HandleTableSize
= NewSize
;
419 PspBlock
->HandleTablePtr
= MAKELONG(0x18, CurrentPsp
);
423 /* Resize the memory */
424 if (!DosResizeMemory(Segment
, NewSize
, NULL
))
426 /* Unable to resize, try allocating it somewhere else */
427 Segment
= DosAllocateMemory(NewSize
, NULL
);
428 if (Segment
== 0) return FALSE
;
430 /* Get the new handle table */
431 HandleTable
= SEG_OFF_TO_PTR(Segment
, 0);
433 /* Copy the handles to the new table */
434 RtlCopyMemory(HandleTable
,
435 FAR_POINTER(PspBlock
->HandleTablePtr
),
436 PspBlock
->HandleTableSize
);
438 /* Update the handle table pointer */
439 PspBlock
->HandleTablePtr
= MAKELONG(0, Segment
);
442 /* Update the handle table size */
443 PspBlock
->HandleTableSize
= NewSize
;
446 else if (NewSize
> DEFAULT_JFT_SIZE
)
448 Segment
= DosAllocateMemory(NewSize
, NULL
);
449 if (Segment
== 0) return FALSE
;
451 /* Get the new handle table */
452 HandleTable
= SEG_OFF_TO_PTR(Segment
, 0);
454 /* Copy the handles from the PSP to the new table */
455 RtlCopyMemory(HandleTable
,
456 FAR_POINTER(PspBlock
->HandleTablePtr
),
457 PspBlock
->HandleTableSize
);
459 /* Update the handle table pointer and size */
460 PspBlock
->HandleTableSize
= NewSize
;
461 PspBlock
->HandleTablePtr
= MAKELONG(0, Segment
);
467 static BOOLEAN
DosCloseHandle(WORD DosHandle
)
471 PDOS_SFT_ENTRY SftEntry
;
473 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle
);
475 /* The system PSP has no handle table */
476 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
478 /* Get a pointer to the handle table */
479 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
480 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
482 /* Make sure the handle is open */
483 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
485 /* Make sure the SFT entry is valid */
486 SftEntry
= &DosSystemFileTable
[HandleTable
[DosHandle
]];
487 if (SftEntry
->Type
== DOS_SFT_ENTRY_NONE
) return FALSE
;
489 /* Decrement the reference count of the SFT entry */
490 SftEntry
->RefCount
--;
492 /* Check if the reference count fell to zero */
493 if (!SftEntry
->RefCount
)
495 switch (SftEntry
->Type
)
497 case DOS_SFT_ENTRY_WIN32
:
499 /* Close the win32 handle and clear it */
500 CloseHandle(SftEntry
->Handle
);
505 case DOS_SFT_ENTRY_DEVICE
:
507 PDOS_DEVICE_NODE Node
= SftEntry
->DeviceNode
;
509 /* Call the close routine, if it exists */
510 if (Node
->CloseRoutine
) SftEntry
->DeviceNode
->CloseRoutine(SftEntry
->DeviceNode
);
517 /* Shouldn't happen */
522 /* Invalidate the SFT entry */
523 SftEntry
->Type
= DOS_SFT_ENTRY_NONE
;
526 /* Clear the entry in the JFT */
527 HandleTable
[DosHandle
] = 0xFF;
532 static BOOLEAN
DosDuplicateHandle(WORD OldHandle
, WORD NewHandle
)
538 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
542 /* The system PSP has no handle table */
543 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
545 /* Get a pointer to the handle table */
546 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
547 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
549 /* Make sure the old handle is open */
550 if (HandleTable
[OldHandle
] == 0xFF) return FALSE
;
552 /* Check if the new handle is open */
553 if (HandleTable
[NewHandle
] != 0xFF)
556 DosCloseHandle(NewHandle
);
559 /* Increment the reference count of the SFT entry */
560 SftIndex
= HandleTable
[OldHandle
];
561 DosSystemFileTable
[SftIndex
].RefCount
++;
563 /* Make the new handle point to that SFT entry */
564 HandleTable
[NewHandle
] = SftIndex
;
570 static BOOLEAN
DosChangeDrive(BYTE Drive
)
572 WCHAR DirectoryPath
[DOS_CMDLINE_LENGTH
];
574 /* Make sure the drive exists */
575 if (Drive
> (LastDrive
- 'A')) return FALSE
;
577 /* Find the path to the new current directory */
578 swprintf(DirectoryPath
, L
"%c\\%S", Drive
+ 'A', CurrentDirectories
[Drive
]);
580 /* Change the current directory of the process */
581 if (!SetCurrentDirectory(DirectoryPath
)) return FALSE
;
583 /* Set the current drive */
584 CurrentDrive
= Drive
;
590 static BOOLEAN
DosChangeDirectory(LPSTR Directory
)
596 /* Make sure the directory path is not too long */
597 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
599 DosLastError
= ERROR_PATH_NOT_FOUND
;
603 /* Get the drive number */
604 DriveNumber
= Directory
[0] - 'A';
606 /* Make sure the drive exists */
607 if (DriveNumber
> (LastDrive
- 'A'))
609 DosLastError
= ERROR_PATH_NOT_FOUND
;
613 /* Get the file attributes */
614 Attributes
= GetFileAttributesA(Directory
);
616 /* Make sure the path exists and is a directory */
617 if ((Attributes
== INVALID_FILE_ATTRIBUTES
)
618 || !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
620 DosLastError
= ERROR_PATH_NOT_FOUND
;
624 /* Check if this is the current drive */
625 if (DriveNumber
== CurrentDrive
)
627 /* Change the directory */
628 if (!SetCurrentDirectoryA(Directory
))
630 DosLastError
= LOWORD(GetLastError());
635 /* Get the directory part of the path */
636 Path
= strchr(Directory
, '\\');
639 /* Skip the backslash */
643 /* Set the directory for the drive */
646 strncpy(CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
650 CurrentDirectories
[DriveNumber
][0] = '\0';
657 static BOOLEAN
DosControlBreak(VOID
)
661 /* Call interrupt 0x23 */
662 Int32Call(&DosContext
, 0x23);
666 DosTerminateProcess(CurrentPsp
, 0, 0);
673 /* PUBLIC FUNCTIONS ***********************************************************/
675 PDOS_SFT_ENTRY
DosGetSftEntry(WORD DosHandle
)
680 /* The system PSP has no handle table */
681 if (CurrentPsp
== SYSTEM_PSP
) return NULL
;
683 /* Get a pointer to the handle table */
684 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
685 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
687 /* Make sure the handle is open */
688 if (HandleTable
[DosHandle
] == 0xFF) return NULL
;
690 /* Return a pointer to the SFT entry */
691 return &DosSystemFileTable
[HandleTable
[DosHandle
]];
694 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
696 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
697 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
699 RtlZeroMemory(PspBlock
, sizeof(*PspBlock
));
701 /* Set the exit interrupt */
702 PspBlock
->Exit
[0] = 0xCD; // int 0x20
703 PspBlock
->Exit
[1] = 0x20;
705 /* Set the number of the last paragraph */
706 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
708 /* Save the interrupt vectors */
709 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
710 PspBlock
->BreakAddress
= IntVecTable
[0x23];
711 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
713 /* Set the parent PSP */
714 PspBlock
->ParentPsp
= CurrentPsp
;
716 /* Copy the parent handle table */
717 DosCopyHandleTable(PspBlock
->HandleTable
);
719 /* Set the environment block */
720 PspBlock
->EnvBlock
= Environment
;
722 /* Set the handle table pointers to the internal handle table */
723 PspBlock
->HandleTableSize
= 20;
724 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
726 /* Set the DOS version */
727 PspBlock
->DosVersion
= DOS_VERSION
;
729 /* Set the far call opcodes */
730 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
731 PspBlock
->FarCall
[1] = 0x21;
732 PspBlock
->FarCall
[2] = 0xCB; // retf
734 /* Set the command line */
735 PspBlock
->CommandLineSize
= (BYTE
)min(strlen(CommandLine
), DOS_CMDLINE_LENGTH
- 1);
736 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
737 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
740 DWORD
DosLoadExecutable(IN DOS_EXEC_TYPE LoadType
,
741 IN LPCSTR ExecutablePath
,
742 IN LPCSTR CommandLine
,
743 IN LPCSTR Environment OPTIONAL
,
744 OUT PDWORD StackLocation OPTIONAL
,
745 OUT PDWORD EntryPoint OPTIONAL
)
747 DWORD Result
= ERROR_SUCCESS
;
748 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
749 LPBYTE Address
= NULL
;
753 DWORD i
, FileSize
, ExeSize
;
754 PIMAGE_DOS_HEADER Header
;
755 PDWORD RelocationTable
;
757 LPSTR CmdLinePtr
= (LPSTR
)CommandLine
;
759 DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
763 Environment
? Environment
: "n/a",
767 if (LoadType
== DOS_LOAD_OVERLAY
)
769 DPRINT1("Overlay loading is not supported yet.\n");
770 return ERROR_NOT_SUPPORTED
;
773 /* NULL-terminate the command line by removing the return carriage character */
774 while (*CmdLinePtr
&& *CmdLinePtr
!= '\r') CmdLinePtr
++;
777 /* Open a handle to the executable */
778 FileHandle
= CreateFileA(ExecutablePath
,
783 FILE_ATTRIBUTE_NORMAL
,
785 if (FileHandle
== INVALID_HANDLE_VALUE
)
787 Result
= GetLastError();
791 /* Get the file size */
792 FileSize
= GetFileSize(FileHandle
, NULL
);
794 /* Create a mapping object for the file */
795 FileMapping
= CreateFileMapping(FileHandle
,
801 if (FileMapping
== NULL
)
803 Result
= GetLastError();
807 /* Map the file into memory */
808 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
811 Result
= GetLastError();
815 /* Copy the environment block to DOS memory */
816 EnvBlock
= DosCopyEnvironmentBlock(Environment
, ExecutablePath
);
819 Result
= ERROR_NOT_ENOUGH_MEMORY
;
823 /* Check if this is an EXE file or a COM file */
824 if (Address
[0] == 'M' && Address
[1] == 'Z')
828 /* Get the MZ header */
829 Header
= (PIMAGE_DOS_HEADER
)Address
;
831 /* Get the base size of the file, in paragraphs (rounded up) */
832 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
834 /* Add the PSP size, in paragraphs */
835 ExeSize
+= sizeof(DOS_PSP
) >> 4;
837 /* Add the maximum size that should be allocated */
838 ExeSize
+= Header
->e_maxalloc
;
840 /* Make sure it does not pass 0xFFFF */
841 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
843 /* Try to allocate that much memory */
844 Segment
= DosAllocateMemory((WORD
)ExeSize
, &MaxAllocSize
);
848 /* Check if there's at least enough memory for the minimum size */
849 if (MaxAllocSize
< (ExeSize
- Header
->e_maxalloc
+ Header
->e_minalloc
))
851 Result
= DosLastError
;
855 /* Allocate that minimum amount */
856 ExeSize
= MaxAllocSize
;
857 Segment
= DosAllocateMemory((WORD
)ExeSize
, NULL
);
858 ASSERT(Segment
!= 0);
861 /* Initialize the PSP */
862 DosInitializePsp(Segment
,
867 /* The process owns its own memory */
868 DosChangeMemoryOwner(Segment
, Segment
);
869 DosChangeMemoryOwner(EnvBlock
, Segment
);
871 /* Copy the program to Segment:0100 */
872 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
873 Address
+ (Header
->e_cparhdr
<< 4),
874 min(FileSize
- (Header
->e_cparhdr
<< 4),
875 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
877 /* Get the relocation table */
878 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
880 /* Perform relocations */
881 for (i
= 0; i
< Header
->e_crlc
; i
++)
883 /* Get a pointer to the word that needs to be patched */
884 RelocWord
= (PWORD
)SEG_OFF_TO_PTR(Segment
+ HIWORD(RelocationTable
[i
]),
885 0x100 + LOWORD(RelocationTable
[i
]));
887 /* Add the number of the EXE segment to it */
888 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
891 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
893 /* Set the initial segment registers */
897 /* Set the stack to the location from the header */
898 setSS(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
);
902 CurrentPsp
= Segment
;
903 DiskTransferArea
= MAKELONG(0x80, Segment
);
904 CpuExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
912 /* Find the maximum amount of memory that can be allocated */
913 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
915 /* Make sure it's enough for the whole program and the PSP */
916 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
)))
918 Result
= ERROR_NOT_ENOUGH_MEMORY
;
922 /* Allocate all of it */
923 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
926 Result
= DosLastError
;
930 /* The process owns its own memory */
931 DosChangeMemoryOwner(Segment
, Segment
);
932 DosChangeMemoryOwner(EnvBlock
, Segment
);
934 /* Copy the program to Segment:0100 */
935 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
939 /* Initialize the PSP */
940 DosInitializePsp(Segment
,
945 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
947 /* Set the initial segment registers */
951 /* Set the stack to the last word of the segment */
956 * Set the value on the stack to 0, so that a near return
957 * jumps to PSP:0000 which has the exit code.
959 *((LPWORD
)SEG_OFF_TO_PTR(Segment
, 0xFFFE)) = 0;
962 CurrentPsp
= Segment
;
963 DiskTransferArea
= MAKELONG(0x80, Segment
);
964 CpuExecute(Segment
, 0x100);
969 if (Result
!= ERROR_SUCCESS
)
971 /* It was not successful, cleanup the DOS memory */
972 if (EnvBlock
) DosFreeMemory(EnvBlock
);
973 if (Segment
) DosFreeMemory(Segment
);
977 if (Address
!= NULL
) UnmapViewOfFile(Address
);
979 /* Close the file mapping object */
980 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
982 /* Close the file handle */
983 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
988 DWORD
DosStartProcess(IN LPCSTR ExecutablePath
,
989 IN LPCSTR CommandLine
,
990 IN LPCSTR Environment OPTIONAL
)
994 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
1001 if (Result
!= ERROR_SUCCESS
) goto Quit
;
1003 /* Attach to the console */
1005 VidBiosAttachToConsole();
1007 // HACK: Simulate a ENTER key release scancode on the PS/2 port because
1008 // some apps expect to read a key release scancode (> 0x80) when they
1010 IOWriteB(PS2_CONTROL_PORT
, 0xD2); // Next write is for the first PS/2 port
1011 IOWriteB(PS2_DATA_PORT
, 0x80 | 0x1C); // ENTER key release
1013 /* Start simulation */
1014 SetEvent(VdmTaskEvent
);
1017 /* Detach from the console */
1018 VidBiosDetachFromConsole();
1026 WORD
DosCreateProcess(DOS_EXEC_TYPE LoadType
,
1028 PDOS_EXEC_PARAM_BLOCK Parameters
)
1032 LPVOID Environment
= NULL
;
1033 VDM_COMMAND_INFO CommandInfo
;
1034 CHAR CmdLine
[MAX_PATH
];
1035 CHAR AppName
[MAX_PATH
];
1036 CHAR PifFile
[MAX_PATH
];
1037 CHAR Desktop
[MAX_PATH
];
1038 CHAR Title
[MAX_PATH
];
1040 STARTUPINFOA StartupInfo
;
1041 PROCESS_INFORMATION ProcessInfo
;
1043 /* Get the binary type */
1044 if (!GetBinaryTypeA(ProgramName
, &BinaryType
)) return GetLastError();
1046 /* Did the caller specify an environment segment? */
1047 if (Parameters
->Environment
)
1049 /* Yes, use it instead of the parent one */
1050 Environment
= SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
1053 /* Set up the startup info structure */
1054 RtlZeroMemory(&StartupInfo
, sizeof(StartupInfo
));
1055 StartupInfo
.cb
= sizeof(StartupInfo
);
1057 /* Create the process */
1058 if (!CreateProcessA(ProgramName
,
1059 FAR_POINTER(Parameters
->CommandLine
),
1069 return GetLastError();
1072 /* Check the type of the program */
1075 /* These are handled by NTVDM */
1076 case SCS_DOS_BINARY
:
1077 case SCS_WOW_BINARY
:
1079 /* Clear the structure */
1080 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
1082 /* Initialize the structure members */
1083 CommandInfo
.TaskId
= SessionId
;
1084 CommandInfo
.VDMState
= VDM_FLAG_NESTED_TASK
| VDM_FLAG_DONT_WAIT
;
1085 CommandInfo
.CmdLine
= CmdLine
;
1086 CommandInfo
.CmdLen
= sizeof(CmdLine
);
1087 CommandInfo
.AppName
= AppName
;
1088 CommandInfo
.AppLen
= sizeof(AppName
);
1089 CommandInfo
.PifFile
= PifFile
;
1090 CommandInfo
.PifLen
= sizeof(PifFile
);
1091 CommandInfo
.Desktop
= Desktop
;
1092 CommandInfo
.DesktopLen
= sizeof(Desktop
);
1093 CommandInfo
.Title
= Title
;
1094 CommandInfo
.TitleLen
= sizeof(Title
);
1095 CommandInfo
.Env
= Env
;
1096 CommandInfo
.EnvLen
= sizeof(Env
);
1098 /* Get the VDM command information */
1099 if (!GetNextVDMCommand(&CommandInfo
))
1101 /* Shouldn't happen */
1105 /* Increment the re-entry count */
1106 CommandInfo
.VDMState
= VDM_INC_REENTER_COUNT
;
1107 GetNextVDMCommand(&CommandInfo
);
1109 /* Load the executable */
1110 Result
= DosLoadExecutable(LoadType
,
1114 &Parameters
->StackLocation
,
1115 &Parameters
->EntryPoint
);
1116 if (Result
!= ERROR_SUCCESS
)
1118 DisplayMessage(L
"Could not load '%S'. Error: %u", AppName
, Result
);
1119 // FIXME: Decrement the reenter count. Or, instead, just increment
1120 // the VDM reenter count *only* if this call succeeds...
1126 /* Not handled by NTVDM */
1129 /* Wait for the process to finish executing */
1130 WaitForSingleObject(ProcessInfo
.hProcess
, INFINITE
);
1134 /* Close the handles */
1135 CloseHandle(ProcessInfo
.hProcess
);
1136 CloseHandle(ProcessInfo
.hThread
);
1138 return ERROR_SUCCESS
;
1142 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
, WORD KeepResident
)
1145 WORD McbSegment
= FIRST_MCB_SEGMENT
;
1146 PDOS_MCB CurrentMcb
;
1147 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
1148 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
1150 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
1155 /* Check if this PSP is it's own parent */
1156 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
1158 if (KeepResident
== 0)
1160 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
1162 /* Close the handle */
1167 /* Free the memory used by the process */
1170 /* Get a pointer to the MCB */
1171 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
1173 /* Make sure the MCB is valid */
1174 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z') break;
1176 /* Check if this block was allocated by the process */
1177 if (CurrentMcb
->OwnerPsp
== Psp
)
1179 if (KeepResident
== 0)
1181 /* Free this entire block */
1182 DosFreeMemory(McbSegment
+ 1);
1184 else if (KeepResident
< CurrentMcb
->Size
)
1186 /* Reduce the size of the block */
1187 DosResizeMemory(McbSegment
+ 1, KeepResident
, NULL
);
1189 /* No further paragraphs need to stay resident */
1194 /* Just reduce the amount of paragraphs we need to keep resident */
1195 KeepResident
-= CurrentMcb
->Size
;
1199 /* If this was the last block, quit */
1200 if (CurrentMcb
->BlockType
== 'Z') break;
1202 /* Update the segment and continue */
1203 McbSegment
+= CurrentMcb
->Size
+ 1;
1207 /* Restore the interrupt vectors */
1208 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
1209 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
1210 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
1212 /* Update the current PSP */
1213 if (Psp
== CurrentPsp
)
1215 CurrentPsp
= PspBlock
->ParentPsp
;
1216 if (CurrentPsp
== SYSTEM_PSP
)
1218 ResetEvent(VdmTaskEvent
);
1224 // FIXME: This is probably not the best way to do it
1225 /* Check if this was a nested DOS task */
1226 if (CurrentPsp
!= SYSTEM_PSP
)
1228 VDM_COMMAND_INFO CommandInfo
;
1230 /* Decrement the re-entry count */
1231 CommandInfo
.TaskId
= SessionId
;
1232 CommandInfo
.VDMState
= VDM_DEC_REENTER_COUNT
;
1233 GetNextVDMCommand(&CommandInfo
);
1235 /* Clear the structure */
1236 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
1238 /* Update the VDM state of the task */
1239 CommandInfo
.TaskId
= SessionId
;
1240 CommandInfo
.VDMState
= VDM_FLAG_DONT_WAIT
;
1241 GetNextVDMCommand(&CommandInfo
);
1245 /* Save the return code - Normal termination */
1246 DosErrorLevel
= MAKEWORD(ReturnCode
, 0x00);
1248 /* Return control to the parent process */
1249 CpuExecute(HIWORD(PspBlock
->TerminateAddress
),
1250 LOWORD(PspBlock
->TerminateAddress
));
1253 BOOLEAN
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
1255 PDOS_SFT_ENTRY SftEntry
= DosGetSftEntry(FileHandle
);
1256 PDOS_DEVICE_NODE Node
= NULL
;
1258 /* Make sure it exists */
1261 DosLastError
= ERROR_FILE_NOT_FOUND
;
1265 if (SftEntry
->Type
== DOS_SFT_ENTRY_DEVICE
) Node
= SftEntry
->DeviceNode
;
1267 switch (ControlCode
)
1269 /* Get Device Information */
1275 * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
1276 * for a list of possible flags.
1281 /* Return the device attributes with bit 7 set */
1282 InfoWord
= Node
->DeviceAttributes
| (1 << 7);
1289 /* Set Device Information */
1292 // TODO: NOT IMPLEMENTED
1298 /* Read From Device I/O Control Channel */
1301 WORD Length
= getCX();
1303 if (Node
== NULL
|| !(Node
->DeviceAttributes
& DOS_DEVATTR_IOCTL
))
1305 DosLastError
= ERROR_INVALID_FUNCTION
;
1309 /* Do nothing if there is no IOCTL routine */
1310 if (!Node
->IoctlReadRoutine
)
1316 Node
->IoctlReadRoutine(Node
, MAKELONG(getDX(), getDS()), &Length
);
1322 /* Write To Device I/O Control Channel */
1325 WORD Length
= getCX();
1327 if (Node
== NULL
|| !(Node
->DeviceAttributes
& DOS_DEVATTR_IOCTL
))
1329 DosLastError
= ERROR_INVALID_FUNCTION
;
1333 /* Do nothing if there is no IOCTL routine */
1334 if (!Node
->IoctlWriteRoutine
)
1340 Node
->IoctlWriteRoutine(Node
, MAKELONG(getDX(), getDS()), &Length
);
1346 /* Unsupported control code */
1349 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
1351 DosLastError
= ERROR_INVALID_PARAMETER
;
1357 VOID WINAPI
DosInt20h(LPWORD Stack
)
1359 /* This is the exit interrupt */
1360 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
1363 VOID WINAPI
DosInt21h(LPWORD Stack
)
1366 SYSTEMTIME SystemTime
;
1368 PDOS_INPUT_BUFFER InputBuffer
;
1369 PDOS_COUNTRY_CODE_BUFFER CountryCodeBuffer
;
1372 /* Check the value in the AH register */
1375 /* Terminate Program */
1378 DosTerminateProcess(Stack
[STACK_CS
], 0, 0);
1382 /* Read Character from STDIN with Echo */
1385 DPRINT("INT 21h, AH = 01h\n");
1387 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
1389 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1392 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
1393 // Check also Ctrl-P and set echo-to-printer flag.
1394 // Ctrl-Z is not interpreted.
1400 /* Write Character to STDOUT */
1403 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1404 Character
= getDL();
1405 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1408 * We return the output character (DOS 2.1+).
1409 * Also, if we're going to output a TAB, then
1410 * don't return a TAB but a SPACE instead.
1411 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
1412 * for more information.
1414 setAL(Character
== '\t' ? ' ' : Character
);
1418 /* Read Character from STDAUX */
1421 // FIXME: Really read it from STDAUX!
1422 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
1423 // setAL(DosReadCharacter());
1427 /* Write Character to STDAUX */
1430 // FIXME: Really write it to STDAUX!
1431 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
1432 // DosPrintCharacter(getDL());
1436 /* Write Character to Printer */
1439 // FIXME: Really write it to printer!
1440 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
1441 DPRINT1("0x%p\n", getDL());
1442 DPRINT1("\n\n-----------\n\n");
1446 /* Direct Console I/O */
1449 Character
= getDL();
1451 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1453 if (Character
!= 0xFF)
1456 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1459 * We return the output character (DOS 2.1+).
1460 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
1461 * for more information.
1468 if (DosCheckInput())
1470 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1471 setAL(DosReadCharacter(DOS_INPUT_HANDLE
));
1475 /* No character available */
1476 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1484 /* Character Input without Echo */
1488 DPRINT("Char input without echo\n");
1490 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1492 // FIXME: For 0x07, do not check Ctrl-C/Break.
1493 // For 0x08, do check those control sequences and if needed,
1500 /* Write string to STDOUT */
1503 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1505 while (*String
!= '$')
1507 DosPrintCharacter(DOS_OUTPUT_HANDLE
, *String
);
1512 * We return the terminating character (DOS 2.1+).
1513 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
1514 * for more information.
1516 setAL('$'); // *String
1520 /* Read Buffered Input */
1524 InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
1526 DPRINT("Read Buffered Input\n");
1528 while (Count
< InputBuffer
->MaxLength
)
1530 /* Try to read a character (wait) */
1531 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1535 /* Extended character */
1538 /* Read the scancode */
1539 DosReadCharacter(DOS_INPUT_HANDLE
);
1546 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
1547 DosPrintCharacter(DOS_OUTPUT_HANDLE
, 'C');
1549 if (DosControlBreak()) return;
1560 /* Erase the character */
1561 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
1562 DosPrintCharacter(DOS_OUTPUT_HANDLE
, ' ');
1563 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '\b');
1571 /* Append it to the buffer */
1572 InputBuffer
->Buffer
[Count
] = Character
;
1573 Count
++; /* Carriage returns are also counted */
1575 /* Check if this is a special character */
1576 if (Character
< 0x20 && Character
!= 0x0A && Character
!= 0x0D)
1578 DosPrintCharacter(DOS_OUTPUT_HANDLE
, '^');
1579 Character
+= 'A' - 1;
1582 /* Echo the character */
1583 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1587 if (Character
== '\r') break;
1590 /* Update the length */
1591 InputBuffer
->Length
= Count
;
1596 /* Get STDIN Status */
1599 setAL(DosCheckInput() ? 0xFF : 0x00);
1603 /* Flush Buffer and Read STDIN */
1606 BYTE InputFunction
= getAL();
1608 /* Flush STDIN buffer */
1609 DosFlushFileBuffers(DOS_INPUT_HANDLE
);
1612 * If the input function number contained in AL is valid, i.e.
1613 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
1614 * recursively with AL == AH.
1616 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
1617 InputFunction
== 0x07 || InputFunction
== 0x08 ||
1618 InputFunction
== 0x0A)
1620 /* Call ourselves recursively */
1621 setAH(InputFunction
);
1630 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1632 // TODO: Flush what's needed.
1633 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
1635 /* Clear CF in DOS 6 only */
1636 if (PspBlock
->DosVersion
== 0x0006)
1637 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1642 /* Set Default Drive */
1645 DosChangeDrive(getDL());
1646 setAL(LastDrive
- 'A' + 1);
1650 /* NULL Function for CP/M Compatibility */
1654 * This function corresponds to the CP/M BDOS function
1655 * "get bit map of logged drives", which is meaningless
1658 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
1659 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
1660 * for more information.
1666 /* Get Default Drive */
1669 setAL(CurrentDrive
);
1673 /* Set Disk Transfer Area */
1676 DiskTransferArea
= MAKELONG(getDX(), getDS());
1680 /* NULL Function for CP/M Compatibility */
1685 * Function 0x1D corresponds to the CP/M BDOS function
1686 * "get bit map of read-only drives", which is meaningless
1688 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
1689 * for more information.
1691 * Function 0x1E corresponds to the CP/M BDOS function
1692 * "set file attributes", which was meaningless under MS-DOS 1.x.
1693 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
1694 * for more information.
1700 /* NULL Function for CP/M Compatibility */
1704 * This function corresponds to the CP/M BDOS function
1705 * "get/set default user (sublibrary) number", which is meaningless
1708 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
1709 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
1710 * for more information.
1716 /* Set Interrupt Vector */
1719 ULONG FarPointer
= MAKELONG(getDX(), getDS());
1720 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
1721 getAL(), HIWORD(FarPointer
), LOWORD(FarPointer
));
1723 /* Write the new far pointer to the IDT */
1724 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
1728 /* Create New PSP */
1731 DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
1735 /* Get System Date */
1738 GetLocalTime(&SystemTime
);
1739 setCX(SystemTime
.wYear
);
1740 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
1741 setAL(SystemTime
.wDayOfWeek
);
1745 /* Set System Date */
1748 GetLocalTime(&SystemTime
);
1749 SystemTime
.wYear
= getCX();
1750 SystemTime
.wMonth
= getDH();
1751 SystemTime
.wDay
= getDL();
1753 /* Return success or failure */
1754 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1758 /* Get System Time */
1761 GetLocalTime(&SystemTime
);
1762 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
1763 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
1767 /* Set System Time */
1770 GetLocalTime(&SystemTime
);
1771 SystemTime
.wHour
= getCH();
1772 SystemTime
.wMinute
= getCL();
1773 SystemTime
.wSecond
= getDH();
1774 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
1776 /* Return success or failure */
1777 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1781 /* Get Disk Transfer Area */
1784 setES(HIWORD(DiskTransferArea
));
1785 setBX(LOWORD(DiskTransferArea
));
1789 /* Get DOS Version */
1792 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1795 * DOS 2+ - GET DOS VERSION
1796 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
1797 * for more information.
1800 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
1803 * Return DOS OEM number:
1804 * 0x00 for IBM PC-DOS
1805 * 0x02 for packaged MS-DOS
1811 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
1814 * Return version flag:
1815 * 1 << 3 if DOS is in ROM,
1816 * 0 (reserved) if not.
1821 /* Return DOS 24-bit user serial number in BL:CX */
1826 * Return DOS version: Minor:Major in AH:AL
1827 * The Windows NT DOS box returns version 5.00, subject to SETVER.
1829 setAX(PspBlock
->DosVersion
);
1834 /* Terminate and Stay Resident */
1837 DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
1838 DosTerminateProcess(CurrentPsp
, getAL(), getDX());
1842 /* Extended functionalities */
1845 if (getAL() == 0x06)
1848 * DOS 5+ - GET TRUE VERSION NUMBER
1849 * This function always returns the true version number, unlike
1850 * AH=30h, whose return value may be changed with SETVER.
1851 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
1852 * for more information.
1856 * Return the true DOS version: Minor:Major in BH:BL
1857 * The Windows NT DOS box returns BX=3205h (version 5.50).
1859 setBX(NTDOS_VERSION
);
1861 /* DOS revision 0 */
1869 // /* Invalid subfunction */
1876 /* Get Interrupt Vector */
1879 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[getAL()];
1881 /* Read the address from the IDT into ES:BX */
1882 setES(HIWORD(FarPointer
));
1883 setBX(LOWORD(FarPointer
));
1887 /* SWITCH character - AVAILDEV */
1890 if (getAL() == 0x00)
1893 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
1894 * This setting is ignored by MS-DOS 4.0+.
1895 * MS-DOS 5+ always return AL=00h/DL=2Fh.
1896 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
1897 * for more information.
1902 else if (getAL() == 0x01)
1905 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
1906 * This setting is ignored by MS-DOS 5+.
1907 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
1908 * for more information.
1913 else if (getAL() == 0x02)
1916 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1917 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1918 * for more information.
1923 else if (getAL() == 0x03)
1926 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1927 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1928 * for more information.
1935 /* Invalid subfunction */
1942 /* Get/Set Country-dependent Information */
1945 CountryCodeBuffer
= (PDOS_COUNTRY_CODE_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
1947 if (getAL() == 0x00)
1950 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_IDATE
,
1951 &CountryCodeBuffer
->TimeFormat
,
1952 sizeof(CountryCodeBuffer
->TimeFormat
) / sizeof(TCHAR
));
1955 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1956 setAX(LOWORD(GetLastError()));
1960 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_SCURRENCY
,
1961 &CountryCodeBuffer
->CurrencySymbol
,
1962 sizeof(CountryCodeBuffer
->CurrencySymbol
) / sizeof(TCHAR
));
1965 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1966 setAX(LOWORD(GetLastError()));
1970 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
,
1971 &CountryCodeBuffer
->ThousandSep
,
1972 sizeof(CountryCodeBuffer
->ThousandSep
) / sizeof(TCHAR
));
1975 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1976 setAX(LOWORD(GetLastError()));
1980 Return
= GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
,
1981 &CountryCodeBuffer
->DecimalSep
,
1982 sizeof(CountryCodeBuffer
->DecimalSep
) / sizeof(TCHAR
));
1985 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1986 setAX(LOWORD(GetLastError()));
1990 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1994 // TODO: NOT IMPLEMENTED
2001 /* Create Directory */
2004 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2006 if (CreateDirectoryA(String
, NULL
))
2008 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2012 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2013 setAX(LOWORD(GetLastError()));
2019 /* Remove Directory */
2022 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2024 if (RemoveDirectoryA(String
))
2026 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2030 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2031 setAX(LOWORD(GetLastError()));
2037 /* Set Current Directory */
2040 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2042 if (DosChangeDirectory(String
))
2044 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2048 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2049 setAX(DosLastError
);
2055 /* Create or Truncate File */
2059 WORD ErrorCode
= DosCreateFile(&FileHandle
,
2060 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2064 if (ErrorCode
== ERROR_SUCCESS
)
2066 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2071 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2078 /* Open File or Device */
2083 LPCSTR FileName
= (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2084 PDOS_DEVICE_NODE Device
= DosGetDevice(FileName
);
2088 FileHandle
= DosOpenDevice(Device
);
2089 ErrorCode
= (FileHandle
!= INVALID_DOS_HANDLE
)
2090 ? ERROR_SUCCESS
: ERROR_TOO_MANY_OPEN_FILES
;
2094 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, getAL());
2097 if (ErrorCode
== ERROR_SUCCESS
)
2099 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2104 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2111 /* Close File or Device */
2114 if (DosCloseHandle(getBX()))
2116 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2120 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2121 setAX(ERROR_INVALID_HANDLE
);
2127 /* Read from File or Device */
2133 DPRINT("DosReadFile(0x%04X)\n", getBX());
2136 ErrorCode
= DosReadFile(getBX(),
2137 MAKELONG(getDX(), getDS()),
2142 if (ErrorCode
== ERROR_SUCCESS
)
2144 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2147 else if (ErrorCode
!= ERROR_NOT_READY
)
2149 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2156 /* Write to File or Device */
2159 WORD BytesWritten
= 0;
2160 WORD ErrorCode
= DosWriteFile(getBX(),
2161 MAKELONG(getDX(), getDS()),
2165 if (ErrorCode
== ERROR_SUCCESS
)
2167 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2168 setAX(BytesWritten
);
2172 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2182 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2184 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
2186 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2188 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
2189 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
2191 setAL(FileName
[0] - 'A');
2195 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2196 setAX(GetLastError());
2206 WORD ErrorCode
= DosSeekFile(getBX(),
2207 MAKELONG(getDX(), getCX()),
2211 if (ErrorCode
== ERROR_SUCCESS
)
2213 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2215 /* Return the new offset in DX:AX */
2216 setDX(HIWORD(NewLocation
));
2217 setAX(LOWORD(NewLocation
));
2221 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2228 /* Get/Set File Attributes */
2232 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2234 if (getAL() == 0x00)
2236 /* Get the attributes */
2237 Attributes
= GetFileAttributesA(FileName
);
2239 /* Check if it failed */
2240 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
2242 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2243 setAX(GetLastError());
2247 /* Return the attributes that DOS can understand */
2248 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2249 setCX(Attributes
& 0x00FF);
2252 else if (getAL() == 0x01)
2254 /* Try to set the attributes */
2255 if (SetFileAttributesA(FileName
, getCL()))
2257 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2261 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2262 setAX(GetLastError());
2267 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2268 setAX(ERROR_INVALID_FUNCTION
);
2277 if (DosHandleIoctl(getAL(), getBX()))
2279 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2283 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2284 setAX(DosLastError
);
2290 /* Duplicate Handle */
2294 PDOS_SFT_ENTRY SftEntry
= DosGetSftEntry(getBX());
2296 if (SftEntry
== NULL
|| SftEntry
->Type
== DOS_SFT_ENTRY_NONE
)
2298 /* The handle is invalid */
2299 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2300 setAX(ERROR_INVALID_HANDLE
);
2304 /* Open a new handle to the same entry */
2305 switch (SftEntry
->Type
)
2307 case DOS_SFT_ENTRY_WIN32
:
2309 NewHandle
= DosOpenHandle(SftEntry
->Handle
);
2313 case DOS_SFT_ENTRY_DEVICE
:
2315 NewHandle
= DosOpenDevice(SftEntry
->DeviceNode
);
2321 /* Shouldn't happen */
2326 if (NewHandle
== INVALID_DOS_HANDLE
)
2328 /* Too many files open */
2329 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2330 setAX(ERROR_TOO_MANY_OPEN_FILES
);
2334 /* Return the result */
2335 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2340 /* Force Duplicate Handle */
2343 if (DosDuplicateHandle(getBX(), getCX()))
2345 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2349 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2350 setAX(ERROR_INVALID_HANDLE
);
2356 /* Get Current Directory */
2359 BYTE DriveNumber
= getDL();
2360 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
2362 /* Get the real drive number */
2363 if (DriveNumber
== 0)
2365 DriveNumber
= CurrentDrive
;
2369 /* Decrement DriveNumber since it was 1-based */
2373 if (DriveNumber
<= LastDrive
- 'A')
2376 * Copy the current directory into the target buffer.
2377 * It doesn't contain the drive letter and the backslash.
2379 strncpy(String
, CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
2380 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2381 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
2385 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2386 setAX(ERROR_INVALID_DRIVE
);
2392 /* Allocate Memory */
2395 WORD MaxAvailable
= 0;
2396 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
2400 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2405 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2406 setAX(DosLastError
);
2407 setBX(MaxAvailable
);
2416 if (DosFreeMemory(getES()))
2418 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2422 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2423 setAX(ERROR_ARENA_TRASHED
);
2429 /* Resize Memory Block */
2434 if (DosResizeMemory(getES(), getBX(), &Size
))
2436 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2440 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2441 setAX(DosLastError
);
2452 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)getAL();
2453 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
2454 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
2455 WORD ErrorCode
= DosCreateProcess(LoadType
, ProgramName
, ParamBlock
);
2457 if (ErrorCode
== ERROR_SUCCESS
)
2459 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2463 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2471 /* Terminate With Return Code */
2474 DosTerminateProcess(CurrentPsp
, getAL(), 0);
2478 /* Get Return Code (ERRORLEVEL) */
2482 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
2483 * DosErrorLevel is cleared after being read by this function.
2485 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2486 setAX(DosErrorLevel
);
2487 DosErrorLevel
= 0x0000; // Clear it
2491 /* Find First File */
2494 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(DiskTransferArea
),
2495 SEG_OFF_TO_PTR(getDS(), getDX()),
2500 if (Result
== ERROR_SUCCESS
)
2501 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2503 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2508 /* Find Next File */
2511 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(DiskTransferArea
));
2515 if (Result
== ERROR_SUCCESS
)
2516 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2518 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2523 /* Internal - Set Current Process ID (Set PSP Address) */
2526 // FIXME: Is it really what it's done ??
2527 CurrentPsp
= getBX();
2531 /* Internal - Get Current Process ID (Get PSP Address) */
2533 /* Get Current PSP Address */
2537 * Undocumented AH=51h is identical to the documented AH=62h.
2538 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
2539 * and http://www.ctyme.com/intr/rb-3140.htm
2540 * for more information.
2546 /* Internal - Get "List of lists" (SYSVARS) */
2550 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
2551 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
2552 * for more information.
2555 /* Return the DOS "list of lists" in ES:BX */
2559 DPRINT1("INT 21h, AH=52h: This application requires the internal DOS List of lists (SYSVARS). UNIMPLEMENTED");
2566 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2567 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
2570 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
2571 * for more information.
2574 if (MoveFileA(ExistingFileName
, NewFileName
))
2576 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2580 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2581 setAX(GetLastError());
2587 /* Get/Set Memory Management Options */
2590 if (getAL() == 0x00)
2592 /* Get allocation strategy */
2593 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2594 setAX(DosAllocStrategy
);
2596 else if (getAL() == 0x01)
2598 /* Set allocation strategy */
2600 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2601 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2603 /* Can't set both */
2604 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2605 setAX(ERROR_INVALID_PARAMETER
);
2609 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT
)
2611 /* Invalid allocation strategy */
2612 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2613 setAX(ERROR_INVALID_PARAMETER
);
2617 DosAllocStrategy
= getBL();
2618 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2620 else if (getAL() == 0x02)
2622 /* Get UMB link state */
2623 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2624 setAL(DosUmbLinked
? 0x01 : 0x00);
2626 else if (getAL() == 0x03)
2628 /* Set UMB link state */
2629 if (getBX()) DosLinkUmb();
2630 else DosUnlinkUmb();
2631 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2635 /* Invalid or unsupported function */
2636 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2637 setAX(ERROR_INVALID_FUNCTION
);
2643 /* Get Extended Error Information */
2646 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
2651 /* Create Temporary File */
2654 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2655 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
2661 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
2662 * for more information.
2665 // FIXME: Check for buffer validity?
2666 // It should be a ASCIZ path ending with a '\' + 13 zero bytes
2667 // to receive the generated filename.
2669 /* First create the temporary file */
2670 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
2673 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2674 setAX(GetLastError());
2678 /* Now try to open it in read/write access */
2679 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
2680 if (ErrorCode
== ERROR_SUCCESS
)
2682 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2687 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2694 /* Create New File */
2698 WORD ErrorCode
= DosCreateFile(&FileHandle
,
2699 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2703 if (ErrorCode
== ERROR_SUCCESS
)
2705 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2710 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2717 /* Lock/Unlock Region of File */
2720 PDOS_SFT_ENTRY SftEntry
= DosGetSftEntry(getBX());
2722 if (SftEntry
== NULL
|| SftEntry
->Type
!= DOS_SFT_ENTRY_WIN32
)
2724 /* The handle is invalid */
2725 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2726 setAX(ERROR_INVALID_HANDLE
);
2730 if (getAL() == 0x00)
2732 /* Lock region of file */
2733 if (LockFile(SftEntry
->Handle
,
2734 MAKELONG(getCX(), getDX()), 0,
2735 MAKELONG(getSI(), getDI()), 0))
2737 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2741 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2742 setAX(GetLastError());
2745 else if (getAL() == 0x01)
2747 /* Unlock region of file */
2748 if (UnlockFile(SftEntry
->Handle
,
2749 MAKELONG(getCX(), getDX()), 0,
2750 MAKELONG(getSI(), getDI()), 0))
2752 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2756 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2757 setAX(GetLastError());
2762 /* Invalid subfunction */
2763 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2764 setAX(ERROR_INVALID_FUNCTION
);
2770 /* Canonicalize File Name or Path */
2774 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
2775 * for more information.
2779 * We suppose that the DOS app gave to us a valid
2780 * 128-byte long buffer for the canonicalized name.
2782 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
2784 SEG_OFF_TO_PTR(getES(), getDI()),
2788 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2789 setAX(GetLastError());
2793 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2797 // FIXME: Convert the full path name into short version.
2798 // We cannot reliably use GetShortPathName, because it fails
2799 // if the path name given doesn't exist. However this DOS
2800 // function AH=60h should be able to work even for non-existing
2801 // path and file names.
2806 /* Set Handle Count */
2809 if (!DosResizeHandleTable(getBX()))
2811 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2812 setAX(DosLastError
);
2814 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2824 * Function 6Ah is identical to function 68h,
2825 * and sets AH to 68h if success.
2826 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
2827 * for more information.
2831 if (DosFlushFileBuffers(getBX()))
2833 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2837 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2838 setAX(GetLastError());
2844 /* Extended Open/Create */
2848 WORD CreationStatus
;
2851 /* Check for AL == 00 */
2852 if (getAL() != 0x00)
2854 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2855 setAX(ERROR_INVALID_FUNCTION
);
2860 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
2861 * for the full detailed description.
2863 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
2866 ErrorCode
= DosCreateFileEx(&FileHandle
,
2868 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI()),
2873 if (ErrorCode
== ERROR_SUCCESS
)
2875 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2876 setCX(CreationStatus
);
2881 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2891 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2894 setAL(0); // Some functions expect AL to be 0 when it's not supported.
2895 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2900 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
2902 /* Set CF to terminate the running process */
2903 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2906 VOID WINAPI
DosFastConOut(LPWORD Stack
)
2909 * This is the DOS 2+ Fast Console Output Interrupt.
2910 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
2912 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2913 * for more information.
2916 /* Save AX and BX */
2917 USHORT AX
= getAX();
2918 USHORT BX
= getBX();
2921 * Set the parameters:
2922 * AL contains the character to print (already set),
2923 * BL contains the character attribute,
2924 * BH contains the video page to use.
2926 setBL(DOS_CHAR_ATTRIBUTE
);
2927 setBH(Bda
->VideoPage
);
2929 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2931 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
2933 /* Restore AX and BX */
2938 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
2942 /* Extended Memory Specification */
2946 if (!XmsGetDriverEntry(&DriverEntry
)) break;
2948 if (getAL() == 0x00)
2950 /* The driver is loaded */
2953 else if (getAL() == 0x10)
2955 setES(HIWORD(DriverEntry
));
2956 setBX(LOWORD(DriverEntry
));
2960 DPRINT1("Unknown DOS XMS Function: INT 0x2F, AH = 43h, AL = %xh\n", getAL());
2968 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2970 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2975 BOOLEAN
DosKRNLInitialize(VOID
)
2981 CHAR CurrentDirectory
[MAX_PATH
];
2982 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2988 /* Clear the current directory buffer */
2989 RtlZeroMemory(CurrentDirectories
, sizeof(CurrentDirectories
));
2991 /* Get the current directory */
2992 if (!GetCurrentDirectoryA(MAX_PATH
, CurrentDirectory
))
2994 // TODO: Use some kind of default path?
2998 /* Convert that to a DOS path */
2999 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, DOS_DIR_LENGTH
))
3001 // TODO: Use some kind of default path?
3006 CurrentDrive
= DosDirectory
[0] - 'A';
3008 /* Get the directory part of the path */
3009 Path
= strchr(DosDirectory
, '\\');
3012 /* Skip the backslash */
3016 /* Set the directory */
3019 strncpy(CurrentDirectories
[CurrentDrive
], Path
, DOS_DIR_LENGTH
);
3022 /* Read CONFIG.SYS */
3023 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
3026 while (fgetws(Buffer
, 256, Stream
))
3028 // TODO: Parse the line
3033 /* Initialize the SFT */
3034 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
3036 DosSystemFileTable
[i
].Type
= DOS_SFT_ENTRY_NONE
;
3037 DosSystemFileTable
[i
].RefCount
= 0;
3042 /* Initialize the callback context */
3043 InitializeContext(&DosContext
, 0x0070, 0x0000);
3045 /* Register the DOS 32-bit Interrupts */
3046 RegisterDosInt32(0x20, DosInt20h
);
3047 RegisterDosInt32(0x21, DosInt21h
);
3048 // RegisterDosInt32(0x22, DosInt22h ); // Termination
3049 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
3050 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
3051 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
3052 RegisterDosInt32(0x2F, DosInt2Fh
);
3054 /* Load the EMS driver */
3055 if (!EmsDrvInitialize(EMS_TOTAL_PAGES
))
3057 DPRINT1("Could not initialize EMS. EMS will not be available.\n"
3058 "Try reducing the number of EMS pages.\n");
3061 /* Load the XMS driver (HIMEM) */
3064 /* Load the CON driver */