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 *******************************************************************/
20 #include "bios/bios.h"
21 #include "registers.h"
23 /* PRIVATE VARIABLES **********************************************************/
25 CALLBACK16 DosContext
;
27 static WORD CurrentPsp
= SYSTEM_PSP
;
28 static WORD DosLastError
= 0;
29 static DWORD DiskTransferArea
;
30 /*static*/ BYTE CurrentDrive
;
31 static CHAR LastDrive
= 'E';
32 static CHAR CurrentDirectories
[NUM_DRIVES
][DOS_DIR_LENGTH
];
38 } DosSystemFileTable
[DOS_SFT_SIZE
];
40 static BYTE DosAllocStrategy
= DOS_ALLOC_BEST_FIT
;
41 static BOOLEAN DosUmbLinked
= FALSE
;
42 static WORD DosErrorLevel
= 0x0000;
44 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
45 BOOLEAN DoEcho
= FALSE
;
47 /* PRIVATE FUNCTIONS **********************************************************/
50 * Memory management functions
52 static VOID
DosCombineFreeBlocks(WORD StartBlock
)
54 PDOS_MCB CurrentMcb
= SEGMENT_TO_MCB(StartBlock
), NextMcb
;
56 /* If this is the last block or it's not free, quit */
57 if (CurrentMcb
->BlockType
== 'Z' || CurrentMcb
->OwnerPsp
!= 0) return;
61 /* Get a pointer to the next MCB */
62 NextMcb
= SEGMENT_TO_MCB(StartBlock
+ CurrentMcb
->Size
+ 1);
64 /* Check if the next MCB is free */
65 if (NextMcb
->OwnerPsp
== 0)
68 CurrentMcb
->Size
+= NextMcb
->Size
+ 1;
69 CurrentMcb
->BlockType
= NextMcb
->BlockType
;
70 NextMcb
->BlockType
= 'I';
74 /* No more adjoining free blocks */
80 static WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
82 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
83 PDOS_MCB CurrentMcb
, NextMcb
;
84 BOOLEAN SearchUmb
= FALSE
;
86 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size
);
88 if (DosUmbLinked
&& (DosAllocStrategy
& (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
)))
90 /* Search UMB first */
91 Segment
= UMB_START_SEGMENT
;
97 /* Get a pointer to the MCB */
98 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
100 /* Make sure it's valid */
101 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
103 DPRINT("The DOS memory arena is corrupted!\n");
104 DosLastError
= ERROR_ARENA_TRASHED
;
108 /* Only check free blocks */
109 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
111 /* Combine this free block with adjoining free blocks */
112 DosCombineFreeBlocks(Segment
);
114 /* Update the maximum block size */
115 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
117 /* Check if this block is big enough */
118 if (CurrentMcb
->Size
< Size
) goto Next
;
120 switch (DosAllocStrategy
& 0x3F)
122 case DOS_ALLOC_FIRST_FIT
:
124 /* For first fit, stop immediately */
129 case DOS_ALLOC_BEST_FIT
:
131 /* For best fit, update the smallest block found so far */
132 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
140 case DOS_ALLOC_LAST_FIT
:
142 /* For last fit, make the current block the result, but keep searching */
149 /* If this was the last MCB in the chain, quit */
150 if (CurrentMcb
->BlockType
== 'Z')
152 /* Check if nothing was found while searching through UMBs */
153 if ((Result
== 0) && SearchUmb
&& (DosAllocStrategy
& DOS_ALLOC_HIGH_LOW
))
155 /* Search low memory */
156 Segment
= FIRST_MCB_SEGMENT
;
163 /* Otherwise, update the segment and continue */
164 Segment
+= CurrentMcb
->Size
+ 1;
169 /* If we didn't find a free block, return 0 */
172 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
173 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
177 /* Get a pointer to the MCB */
178 CurrentMcb
= SEGMENT_TO_MCB(Result
);
180 /* Check if the block is larger than requested */
181 if (CurrentMcb
->Size
> Size
)
183 /* It is, split it into two blocks */
184 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
186 /* Initialize the new MCB structure */
187 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
188 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
189 NextMcb
->OwnerPsp
= 0;
191 /* Update the current block */
192 CurrentMcb
->BlockType
= 'M';
193 CurrentMcb
->Size
= Size
;
196 /* Take ownership of the block */
197 CurrentMcb
->OwnerPsp
= CurrentPsp
;
199 /* Return the segment of the data portion of the block */
203 static BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
205 BOOLEAN Success
= TRUE
;
206 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
207 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
209 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
213 /* Make sure this is a valid, allocated block */
214 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
217 DosLastError
= ERROR_INVALID_HANDLE
;
221 ReturnSize
= Mcb
->Size
;
223 /* Check if we need to expand or contract the block */
224 if (NewSize
> Mcb
->Size
)
226 /* We can't expand the last block */
227 if (Mcb
->BlockType
!= 'M')
233 /* Get the pointer and segment of the next MCB */
234 NextSegment
= Segment
+ Mcb
->Size
+ 1;
235 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
237 /* Make sure the next segment is free */
238 if (NextMcb
->OwnerPsp
!= 0)
240 DPRINT("Cannot expand memory block: next segment is not free!\n");
241 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
246 /* Combine this free block with adjoining free blocks */
247 DosCombineFreeBlocks(NextSegment
);
249 /* Set the maximum possible size of the block */
250 ReturnSize
+= NextMcb
->Size
+ 1;
252 if (ReturnSize
< NewSize
)
254 DPRINT("Cannot expand memory block: insufficient free segments available!\n");
255 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
260 /* Maximize the current block */
261 Mcb
->Size
= ReturnSize
;
262 Mcb
->BlockType
= NextMcb
->BlockType
;
264 /* Invalidate the next block */
265 NextMcb
->BlockType
= 'I';
267 /* Check if the block is larger than requested */
268 if (Mcb
->Size
> NewSize
)
270 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
274 /* It is, split it into two blocks */
275 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
277 /* Initialize the new MCB structure */
278 NextMcb
->BlockType
= Mcb
->BlockType
;
279 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
280 NextMcb
->OwnerPsp
= 0;
282 /* Update the current block */
283 Mcb
->BlockType
= 'M';
287 else if (NewSize
< Mcb
->Size
)
289 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
293 /* Just split the block */
294 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
295 NextMcb
->BlockType
= Mcb
->BlockType
;
296 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
297 NextMcb
->OwnerPsp
= 0;
300 Mcb
->BlockType
= 'M';
305 /* Check if the operation failed */
308 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
311 /* Return the maximum possible size */
312 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
318 static BOOLEAN
DosFreeMemory(WORD BlockData
)
320 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
322 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData
);
324 /* Make sure the MCB is valid */
325 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z')
327 DPRINT("MCB block type '%c' not valid!\n", Mcb
->BlockType
);
331 /* Mark the block as free */
337 static BOOLEAN
DosLinkUmb(VOID
)
339 DWORD Segment
= FIRST_MCB_SEGMENT
;
340 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
342 DPRINT("Linking UMB\n");
344 /* Check if UMBs are already linked */
345 if (DosUmbLinked
) return FALSE
;
347 /* Find the last block */
348 while ((Mcb
->BlockType
== 'M') && (Segment
<= 0xFFFF))
350 Segment
+= Mcb
->Size
+ 1;
351 Mcb
= SEGMENT_TO_MCB(Segment
);
354 /* Make sure it's valid */
355 if (Mcb
->BlockType
!= 'Z') return FALSE
;
357 /* Connect the MCB with the UMB chain */
358 Mcb
->BlockType
= 'M';
364 static BOOLEAN
DosUnlinkUmb(VOID
)
366 DWORD Segment
= FIRST_MCB_SEGMENT
;
367 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
369 DPRINT("Unlinking UMB\n");
371 /* Check if UMBs are already unlinked */
372 if (!DosUmbLinked
) return FALSE
;
374 /* Find the block preceding the MCB that links it with the UMB chain */
375 while (Segment
<= 0xFFFF)
377 if ((Segment
+ Mcb
->Size
) == (FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
))
379 /* This is the last non-UMB segment */
383 /* Advance to the next MCB */
384 Segment
+= Mcb
->Size
+ 1;
385 Mcb
= SEGMENT_TO_MCB(Segment
);
388 /* Mark the MCB as the last MCB */
389 Mcb
->BlockType
= 'Z';
391 DosUmbLinked
= FALSE
;
395 static VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
397 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
399 /* Just set the owner */
400 Mcb
->OwnerPsp
= NewOwner
;
403 static WORD
DosCopyEnvironmentBlock(LPCVOID Environment
, LPCSTR ProgramName
)
405 PCHAR Ptr
, DestBuffer
= NULL
;
409 Ptr
= (PCHAR
)Environment
;
411 /* Calculate the size of the environment block */
414 TotalSize
+= strlen(Ptr
) + 1;
415 Ptr
+= strlen(Ptr
) + 1;
419 /* Add the string buffer size */
420 TotalSize
+= strlen(ProgramName
) + 1;
422 /* Add the two extra bytes */
425 /* Allocate the memory for the environment block */
426 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
427 if (!DestSegment
) return 0;
429 Ptr
= (PCHAR
)Environment
;
431 DestBuffer
= (PCHAR
)SEG_OFF_TO_PTR(DestSegment
, 0);
434 /* Copy the string */
435 strcpy(DestBuffer
, Ptr
);
437 /* Advance to the next string */
438 DestBuffer
+= strlen(Ptr
);
439 Ptr
+= strlen(Ptr
) + 1;
441 /* Put a zero after the string */
445 /* Set the final zero */
448 /* Store the special program name tag */
449 *(DestBuffer
++) = LOBYTE(DOS_PROGRAM_NAME_TAG
);
450 *(DestBuffer
++) = HIBYTE(DOS_PROGRAM_NAME_TAG
);
452 /* Copy the program name after the environment block */
453 strcpy(DestBuffer
, ProgramName
);
463 /* Taken from base/shell/cmd/console.c */
464 BOOL
IsConsoleHandle(HANDLE hHandle
)
468 /* Check whether the handle may be that of a console... */
469 if ((GetFileType(hHandle
) & FILE_TYPE_CHAR
) == 0) return FALSE
;
472 * It may be. Perform another test... The idea comes from the
473 * MSDN description of the WriteConsole API:
475 * "WriteConsole fails if it is used with a standard handle
476 * that is redirected to a file. If an application processes
477 * multilingual output that can be redirected, determine whether
478 * the output handle is a console handle (one method is to call
479 * the GetConsoleMode function and check whether it succeeds).
480 * If the handle is a console handle, call WriteConsole. If the
481 * handle is not a console handle, the output is redirected and
482 * you should call WriteFile to perform the I/O."
484 return GetConsoleMode(hHandle
, &dwMode
);
487 WORD
DosOpenHandle(HANDLE Handle
)
494 /* The system PSP has no handle table */
495 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
497 /* Get a pointer to the handle table */
498 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
499 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
501 /* Find a free entry in the JFT */
502 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
504 if (HandleTable
[DosHandle
] == 0xFF) break;
507 /* If there are no free entries, fail */
508 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
510 /* Check if the handle is already in the SFT */
511 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
513 /* Check if this is the same handle */
514 if (DosSystemFileTable
[i
].Handle
!= Handle
) continue;
516 /* Already in the table, reference it */
517 DosSystemFileTable
[i
].RefCount
++;
519 /* Set the JFT entry to that SFT index */
520 HandleTable
[DosHandle
] = i
;
522 /* Return the new handle */
526 /* Add the handle to the SFT */
527 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
529 /* Make sure this is an empty table entry */
530 if (DosSystemFileTable
[i
].Handle
!= INVALID_HANDLE_VALUE
) continue;
532 /* Initialize the empty table entry */
533 DosSystemFileTable
[i
].Handle
= Handle
;
534 DosSystemFileTable
[i
].RefCount
= 1;
536 /* Set the JFT entry to that SFT index */
537 HandleTable
[DosHandle
] = i
;
539 /* Return the new handle */
543 /* The SFT is full */
544 return INVALID_DOS_HANDLE
;
547 HANDLE
DosGetRealHandle(WORD DosHandle
)
552 /* The system PSP has no handle table */
553 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_HANDLE_VALUE
;
555 /* Get a pointer to the handle table */
556 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
557 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
559 /* Make sure the handle is open */
560 if (HandleTable
[DosHandle
] == 0xFF) return INVALID_HANDLE_VALUE
;
562 /* Return the Win32 handle */
563 return DosSystemFileTable
[HandleTable
[DosHandle
]].Handle
;
566 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
572 /* Clear the table first */
573 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
575 /* Check if this is the initial process */
576 if (CurrentPsp
== SYSTEM_PSP
)
578 /* Set up the standard I/O devices */
579 for (i
= 0; i
<= 2; i
++)
581 /* Set the index in the SFT */
582 DestinationTable
[i
] = (BYTE
)i
;
584 /* Increase the reference count */
585 DosSystemFileTable
[i
].RefCount
++;
592 /* Get the parent PSP block and handle table */
593 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
594 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
596 /* Copy the first 20 handles into the new table */
597 for (i
= 0; i
< 20; i
++)
599 DestinationTable
[i
] = SourceTable
[i
];
601 /* Increase the reference count */
602 DosSystemFileTable
[SourceTable
[i
]].RefCount
++;
606 static BOOLEAN
DosResizeHandleTable(WORD NewSize
)
612 /* Get the PSP block */
613 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
615 if (NewSize
== PspBlock
->HandleTableSize
)
621 if (PspBlock
->HandleTableSize
> 20)
623 /* Get the segment of the current table */
624 Segment
= (LOWORD(PspBlock
->HandleTablePtr
) >> 4) + HIWORD(PspBlock
->HandleTablePtr
);
628 /* Get the current handle table */
629 HandleTable
= FAR_POINTER(PspBlock
->HandleTablePtr
);
631 /* Copy it to the PSP */
632 RtlCopyMemory(PspBlock
->HandleTable
, HandleTable
, NewSize
);
634 /* Free the memory */
635 DosFreeMemory(Segment
);
637 /* Update the handle table pointer and size */
638 PspBlock
->HandleTableSize
= NewSize
;
639 PspBlock
->HandleTablePtr
= MAKELONG(0x18, CurrentPsp
);
643 /* Resize the memory */
644 if (!DosResizeMemory(Segment
, NewSize
, NULL
))
646 /* Unable to resize, try allocating it somewhere else */
647 Segment
= DosAllocateMemory(NewSize
, NULL
);
648 if (Segment
== 0) return FALSE
;
650 /* Get the new handle table */
651 HandleTable
= SEG_OFF_TO_PTR(Segment
, 0);
653 /* Copy the handles to the new table */
654 RtlCopyMemory(HandleTable
,
655 FAR_POINTER(PspBlock
->HandleTablePtr
),
656 PspBlock
->HandleTableSize
);
658 /* Update the handle table pointer */
659 PspBlock
->HandleTablePtr
= MAKELONG(0, Segment
);
662 /* Update the handle table size */
663 PspBlock
->HandleTableSize
= NewSize
;
666 else if (NewSize
> 20)
668 Segment
= DosAllocateMemory(NewSize
, NULL
);
669 if (Segment
== 0) return FALSE
;
671 /* Get the new handle table */
672 HandleTable
= SEG_OFF_TO_PTR(Segment
, 0);
674 /* Copy the handles from the PSP to the new table */
675 RtlCopyMemory(HandleTable
,
676 FAR_POINTER(PspBlock
->HandleTablePtr
),
677 PspBlock
->HandleTableSize
);
679 /* Update the handle table pointer and size */
680 PspBlock
->HandleTableSize
= NewSize
;
681 PspBlock
->HandleTablePtr
= MAKELONG(0, Segment
);
687 static BOOLEAN
DosCloseHandle(WORD DosHandle
)
693 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle
);
695 /* The system PSP has no handle table */
696 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
698 /* Get a pointer to the handle table */
699 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
700 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
702 /* Make sure the handle is open */
703 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
705 /* Decrement the reference count of the SFT entry */
706 SftIndex
= HandleTable
[DosHandle
];
707 DosSystemFileTable
[SftIndex
].RefCount
--;
709 /* Check if the reference count fell to zero */
710 if (!DosSystemFileTable
[SftIndex
].RefCount
)
712 /* Close the file, it's no longer needed */
713 CloseHandle(DosSystemFileTable
[SftIndex
].Handle
);
715 /* Clear the handle */
716 DosSystemFileTable
[SftIndex
].Handle
= INVALID_HANDLE_VALUE
;
719 /* Clear the entry in the JFT */
720 HandleTable
[DosHandle
] = 0xFF;
725 static BOOLEAN
DosDuplicateHandle(WORD OldHandle
, WORD NewHandle
)
731 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
735 /* The system PSP has no handle table */
736 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
738 /* Get a pointer to the handle table */
739 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
740 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
742 /* Make sure the old handle is open */
743 if (HandleTable
[OldHandle
] == 0xFF) return FALSE
;
745 /* Check if the new handle is open */
746 if (HandleTable
[NewHandle
] != 0xFF)
749 DosCloseHandle(NewHandle
);
752 /* Increment the reference count of the SFT entry */
753 SftIndex
= HandleTable
[OldHandle
];
754 DosSystemFileTable
[SftIndex
].RefCount
++;
756 /* Make the new handle point to that SFT entry */
757 HandleTable
[NewHandle
] = SftIndex
;
769 static BOOLEAN
DosChangeDrive(BYTE Drive
)
771 WCHAR DirectoryPath
[DOS_CMDLINE_LENGTH
];
773 /* Make sure the drive exists */
774 if (Drive
> (LastDrive
- 'A')) return FALSE
;
776 /* Find the path to the new current directory */
777 swprintf(DirectoryPath
, L
"%c\\%S", Drive
+ 'A', CurrentDirectories
[Drive
]);
779 /* Change the current directory of the process */
780 if (!SetCurrentDirectory(DirectoryPath
)) return FALSE
;
782 /* Set the current drive */
783 CurrentDrive
= Drive
;
789 static BOOLEAN
DosChangeDirectory(LPSTR Directory
)
795 /* Make sure the directory path is not too long */
796 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
798 DosLastError
= ERROR_PATH_NOT_FOUND
;
802 /* Get the drive number */
803 DriveNumber
= Directory
[0] - 'A';
805 /* Make sure the drive exists */
806 if (DriveNumber
> (LastDrive
- 'A'))
808 DosLastError
= ERROR_PATH_NOT_FOUND
;
812 /* Get the file attributes */
813 Attributes
= GetFileAttributesA(Directory
);
815 /* Make sure the path exists and is a directory */
816 if ((Attributes
== INVALID_FILE_ATTRIBUTES
)
817 || !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
819 DosLastError
= ERROR_PATH_NOT_FOUND
;
823 /* Check if this is the current drive */
824 if (DriveNumber
== CurrentDrive
)
826 /* Change the directory */
827 if (!SetCurrentDirectoryA(Directory
))
829 DosLastError
= LOWORD(GetLastError());
834 /* Get the directory part of the path */
835 Path
= strchr(Directory
, '\\');
838 /* Skip the backslash */
842 /* Set the directory for the drive */
845 strncpy(CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
849 CurrentDirectories
[DriveNumber
][0] = '\0';
856 /* PUBLIC FUNCTIONS ***********************************************************/
858 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
860 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
861 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
863 ZeroMemory(PspBlock
, sizeof(DOS_PSP
));
865 /* Set the exit interrupt */
866 PspBlock
->Exit
[0] = 0xCD; // int 0x20
867 PspBlock
->Exit
[1] = 0x20;
869 /* Set the number of the last paragraph */
870 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
872 /* Save the interrupt vectors */
873 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
874 PspBlock
->BreakAddress
= IntVecTable
[0x23];
875 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
877 /* Set the parent PSP */
878 PspBlock
->ParentPsp
= CurrentPsp
;
880 /* Copy the parent handle table */
881 DosCopyHandleTable(PspBlock
->HandleTable
);
883 /* Set the environment block */
884 PspBlock
->EnvBlock
= Environment
;
886 /* Set the handle table pointers to the internal handle table */
887 PspBlock
->HandleTableSize
= 20;
888 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
890 /* Set the DOS version */
891 PspBlock
->DosVersion
= DOS_VERSION
;
893 /* Set the far call opcodes */
894 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
895 PspBlock
->FarCall
[1] = 0x21;
896 PspBlock
->FarCall
[2] = 0xCB; // retf
898 /* Set the command line */
899 PspBlock
->CommandLineSize
= (BYTE
)min(strlen(CommandLine
), DOS_CMDLINE_LENGTH
- 1);
900 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
901 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
904 DWORD
DosLoadExecutable(IN DOS_EXEC_TYPE LoadType
,
905 IN LPCSTR ExecutablePath
,
906 IN LPCSTR CommandLine
,
907 IN PVOID Environment
,
908 OUT PDWORD StackLocation OPTIONAL
,
909 OUT PDWORD EntryPoint OPTIONAL
)
911 DWORD Result
= ERROR_SUCCESS
;
912 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
913 LPBYTE Address
= NULL
;
917 DWORD i
, FileSize
, ExeSize
;
918 PIMAGE_DOS_HEADER Header
;
919 PDWORD RelocationTable
;
921 LPSTR CmdLinePtr
= (LPSTR
)CommandLine
;
923 DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
931 if (LoadType
== DOS_LOAD_OVERLAY
)
933 DPRINT1("Overlay loading is not supported yet.\n");
934 return ERROR_NOT_SUPPORTED
;
937 /* NULL-terminate the command line by removing the return carriage character */
938 while (*CmdLinePtr
&& *CmdLinePtr
!= '\r') CmdLinePtr
++;
941 /* Open a handle to the executable */
942 FileHandle
= CreateFileA(ExecutablePath
,
947 FILE_ATTRIBUTE_NORMAL
,
949 if (FileHandle
== INVALID_HANDLE_VALUE
)
951 Result
= GetLastError();
955 /* Get the file size */
956 FileSize
= GetFileSize(FileHandle
, NULL
);
958 /* Create a mapping object for the file */
959 FileMapping
= CreateFileMapping(FileHandle
,
965 if (FileMapping
== NULL
)
967 Result
= GetLastError();
971 /* Map the file into memory */
972 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
975 Result
= GetLastError();
979 /* Copy the environment block to DOS memory */
980 EnvBlock
= DosCopyEnvironmentBlock(Environment
, ExecutablePath
);
983 Result
= ERROR_NOT_ENOUGH_MEMORY
;
987 /* Check if this is an EXE file or a COM file */
988 if (Address
[0] == 'M' && Address
[1] == 'Z')
992 /* Get the MZ header */
993 Header
= (PIMAGE_DOS_HEADER
)Address
;
995 /* Get the base size of the file, in paragraphs (rounded up) */
996 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
998 /* Add the PSP size, in paragraphs */
999 ExeSize
+= sizeof(DOS_PSP
) >> 4;
1001 /* Add the maximum size that should be allocated */
1002 ExeSize
+= Header
->e_maxalloc
;
1004 /* Make sure it does not pass 0xFFFF */
1005 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
1007 /* Reduce the size one by one until the allocation is successful */
1008 for (i
= Header
->e_maxalloc
; i
>= Header
->e_minalloc
; i
--, ExeSize
--)
1010 /* Try to allocate that much memory */
1011 Segment
= DosAllocateMemory((WORD
)ExeSize
, NULL
);
1012 if (Segment
!= 0) break;
1015 /* Check if at least the lowest allocation was successful */
1018 Result
= DosLastError
;
1022 /* Initialize the PSP */
1023 DosInitializePsp(Segment
,
1028 /* The process owns its own memory */
1029 DosChangeMemoryOwner(Segment
, Segment
);
1030 DosChangeMemoryOwner(EnvBlock
, Segment
);
1032 /* Copy the program to Segment:0100 */
1033 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
1034 Address
+ (Header
->e_cparhdr
<< 4),
1035 min(FileSize
- (Header
->e_cparhdr
<< 4),
1036 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
1038 /* Get the relocation table */
1039 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
1041 /* Perform relocations */
1042 for (i
= 0; i
< Header
->e_crlc
; i
++)
1044 /* Get a pointer to the word that needs to be patched */
1045 RelocWord
= (PWORD
)SEG_OFF_TO_PTR(Segment
+ HIWORD(RelocationTable
[i
]),
1046 0x100 + LOWORD(RelocationTable
[i
]));
1048 /* Add the number of the EXE segment to it */
1049 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
1052 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1054 /* Set the initial segment registers */
1058 /* Set the stack to the location from the header */
1059 EmulatorSetStack(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
,
1063 CurrentPsp
= Segment
;
1064 DiskTransferArea
= MAKELONG(0x80, Segment
);
1065 EmulatorExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
1073 /* Find the maximum amount of memory that can be allocated */
1074 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
1076 /* Make sure it's enough for the whole program and the PSP */
1077 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
)))
1079 Result
= ERROR_NOT_ENOUGH_MEMORY
;
1083 /* Allocate all of it */
1084 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
1087 Result
= DosLastError
;
1091 /* The process owns its own memory */
1092 DosChangeMemoryOwner(Segment
, Segment
);
1093 DosChangeMemoryOwner(EnvBlock
, Segment
);
1095 /* Copy the program to Segment:0100 */
1096 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
1100 /* Initialize the PSP */
1101 DosInitializePsp(Segment
,
1106 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1108 /* Set the initial segment registers */
1112 /* Set the stack to the last word of the segment */
1113 EmulatorSetStack(Segment
, 0xFFFE);
1116 * Set the value on the stack to 0, so that a near return
1117 * jumps to PSP:0000 which has the exit code.
1119 *((LPWORD
)SEG_OFF_TO_PTR(Segment
, 0xFFFE)) = 0;
1122 CurrentPsp
= Segment
;
1123 DiskTransferArea
= MAKELONG(0x80, Segment
);
1124 EmulatorExecute(Segment
, 0x100);
1129 if (Result
!= ERROR_SUCCESS
)
1131 /* It was not successful, cleanup the DOS memory */
1132 if (EnvBlock
) DosFreeMemory(EnvBlock
);
1133 if (Segment
) DosFreeMemory(Segment
);
1137 if (Address
!= NULL
) UnmapViewOfFile(Address
);
1139 /* Close the file mapping object */
1140 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
1142 /* Close the file handle */
1143 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
1148 DWORD
DosStartProcess(IN LPCSTR ExecutablePath
,
1149 IN LPCSTR CommandLine
,
1150 IN PVOID Environment
)
1154 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
1161 if (Result
!= ERROR_SUCCESS
) goto Quit
;
1163 /* Attach to the console */
1164 VidBiosAttachToConsole(); // FIXME: And in fact, attach the full NTVDM UI to the console
1166 /* Start simulation */
1167 SetEvent(VdmTaskEvent
);
1170 /* Detach from the console */
1171 VidBiosDetachFromConsole(); // FIXME: And in fact, detach the full NTVDM UI from the console
1178 WORD
DosCreateProcess(DOS_EXEC_TYPE LoadType
,
1180 PDOS_EXEC_PARAM_BLOCK Parameters
)
1184 LPVOID Environment
= NULL
;
1185 VDM_COMMAND_INFO CommandInfo
;
1186 CHAR CmdLine
[MAX_PATH
];
1187 CHAR AppName
[MAX_PATH
];
1188 CHAR PifFile
[MAX_PATH
];
1189 CHAR Desktop
[MAX_PATH
];
1190 CHAR Title
[MAX_PATH
];
1192 STARTUPINFOA StartupInfo
;
1193 PROCESS_INFORMATION ProcessInfo
;
1195 /* Get the binary type */
1196 if (!GetBinaryTypeA(ProgramName
, &BinaryType
)) return GetLastError();
1198 /* Did the caller specify an environment segment? */
1199 if (Parameters
->Environment
)
1201 /* Yes, use it instead of the parent one */
1202 Environment
= SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
1205 /* Set up the startup info structure */
1206 ZeroMemory(&StartupInfo
, sizeof(STARTUPINFOA
));
1207 StartupInfo
.cb
= sizeof(STARTUPINFOA
);
1209 /* Create the process */
1210 if (!CreateProcessA(ProgramName
,
1211 FAR_POINTER(Parameters
->CommandLine
),
1221 return GetLastError();
1224 /* Check the type of the program */
1227 /* These are handled by NTVDM */
1228 case SCS_DOS_BINARY
:
1229 case SCS_WOW_BINARY
:
1231 /* Clear the structure */
1232 ZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
1234 /* Initialize the structure members */
1235 CommandInfo
.TaskId
= SessionId
;
1236 CommandInfo
.VDMState
= VDM_FLAG_NESTED_TASK
| VDM_FLAG_DONT_WAIT
;
1237 CommandInfo
.CmdLine
= CmdLine
;
1238 CommandInfo
.CmdLen
= sizeof(CmdLine
);
1239 CommandInfo
.AppName
= AppName
;
1240 CommandInfo
.AppLen
= sizeof(AppName
);
1241 CommandInfo
.PifFile
= PifFile
;
1242 CommandInfo
.PifLen
= sizeof(PifFile
);
1243 CommandInfo
.Desktop
= Desktop
;
1244 CommandInfo
.DesktopLen
= sizeof(Desktop
);
1245 CommandInfo
.Title
= Title
;
1246 CommandInfo
.TitleLen
= sizeof(Title
);
1247 CommandInfo
.Env
= Env
;
1248 CommandInfo
.EnvLen
= sizeof(Env
);
1250 /* Get the VDM command information */
1251 if (!GetNextVDMCommand(&CommandInfo
))
1253 /* Shouldn't happen */
1257 /* Increment the re-entry count */
1258 CommandInfo
.VDMState
= VDM_INC_REENTER_COUNT
;
1259 GetNextVDMCommand(&CommandInfo
);
1261 /* Load the executable */
1262 Result
= DosLoadExecutable(LoadType
,
1266 &Parameters
->StackLocation
,
1267 &Parameters
->EntryPoint
);
1268 if (Result
!= ERROR_SUCCESS
)
1270 DisplayMessage(L
"Could not load '%S'. Error: %u", AppName
, Result
);
1271 // FIXME: Decrement the reenter count. Or, instead, just increment
1272 // the VDM reenter count *only* if this call succeeds...
1278 /* Not handled by NTVDM */
1281 /* Wait for the process to finish executing */
1282 WaitForSingleObject(ProcessInfo
.hProcess
, INFINITE
);
1286 /* Close the handles */
1287 CloseHandle(ProcessInfo
.hProcess
);
1288 CloseHandle(ProcessInfo
.hThread
);
1290 return ERROR_SUCCESS
;
1294 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
)
1297 WORD McbSegment
= FIRST_MCB_SEGMENT
;
1298 PDOS_MCB CurrentMcb
;
1299 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
1300 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
1302 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
1306 /* Check if this PSP is it's own parent */
1307 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
1309 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
1311 /* Close the handle */
1315 /* Free the memory used by the process */
1318 /* Get a pointer to the MCB */
1319 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
1321 /* Make sure the MCB is valid */
1322 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!='Z') break;
1324 /* If this block was allocated by the process, free it */
1325 if (CurrentMcb
->OwnerPsp
== Psp
) DosFreeMemory(McbSegment
+ 1);
1327 /* If this was the last block, quit */
1328 if (CurrentMcb
->BlockType
== 'Z') break;
1330 /* Update the segment and continue */
1331 McbSegment
+= CurrentMcb
->Size
+ 1;
1335 /* Restore the interrupt vectors */
1336 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
1337 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
1338 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
1340 /* Update the current PSP */
1341 if (Psp
== CurrentPsp
)
1343 CurrentPsp
= PspBlock
->ParentPsp
;
1344 if (CurrentPsp
== SYSTEM_PSP
)
1346 ResetEvent(VdmTaskEvent
);
1347 EmulatorUnsimulate();
1352 // FIXME: This is probably not the best way to do it
1353 /* Check if this was a nested DOS task */
1354 if (CurrentPsp
!= SYSTEM_PSP
)
1356 VDM_COMMAND_INFO CommandInfo
;
1358 /* Decrement the re-entry count */
1359 CommandInfo
.TaskId
= SessionId
;
1360 CommandInfo
.VDMState
= VDM_DEC_REENTER_COUNT
;
1361 GetNextVDMCommand(&CommandInfo
);
1363 /* Clear the structure */
1364 ZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
1366 /* Update the VDM state of the task */
1367 CommandInfo
.TaskId
= SessionId
;
1368 CommandInfo
.VDMState
= VDM_FLAG_DONT_WAIT
;
1369 GetNextVDMCommand(&CommandInfo
);
1373 /* Save the return code - Normal termination */
1374 DosErrorLevel
= MAKEWORD(ReturnCode
, 0x00);
1376 /* Return control to the parent process */
1377 EmulatorExecute(HIWORD(PspBlock
->TerminateAddress
),
1378 LOWORD(PspBlock
->TerminateAddress
));
1381 BOOLEAN
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
1383 HANDLE Handle
= DosGetRealHandle(FileHandle
);
1385 if (Handle
== INVALID_HANDLE_VALUE
)
1388 DosLastError
= ERROR_FILE_NOT_FOUND
;
1392 switch (ControlCode
)
1394 /* Get Device Information */
1400 * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
1401 * for a list of possible flags.
1404 if (Handle
== DosSystemFileTable
[DOS_INPUT_HANDLE
].Handle
)
1409 else if (Handle
== DosSystemFileTable
[DOS_OUTPUT_HANDLE
].Handle
)
1411 /* Console output */
1415 /* It is a device */
1418 /* Return the device information word */
1423 /* Unsupported control code */
1426 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
1428 DosLastError
= ERROR_INVALID_PARAMETER
;
1434 VOID WINAPI
DosInt20h(LPWORD Stack
)
1436 /* This is the exit interrupt */
1437 DosTerminateProcess(Stack
[STACK_CS
], 0);
1440 VOID WINAPI
DosInt21h(LPWORD Stack
)
1443 SYSTEMTIME SystemTime
;
1445 PDOS_INPUT_BUFFER InputBuffer
;
1447 /* Check the value in the AH register */
1450 /* Terminate Program */
1453 DosTerminateProcess(Stack
[STACK_CS
], 0);
1457 /* Read Character from STDIN with Echo */
1460 DPRINT("INT 21h, AH = 01h\n");
1462 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
1464 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1467 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
1468 // Check also Ctrl-P and set echo-to-printer flag.
1469 // Ctrl-Z is not interpreted.
1475 /* Write Character to STDOUT */
1478 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1479 Character
= getDL();
1480 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1483 * We return the output character (DOS 2.1+).
1484 * Also, if we're going to output a TAB, then
1485 * don't return a TAB but a SPACE instead.
1486 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
1487 * for more information.
1489 setAL(Character
== '\t' ? ' ' : Character
);
1493 /* Read Character from STDAUX */
1496 // FIXME: Really read it from STDAUX!
1497 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
1498 // setAL(DosReadCharacter());
1502 /* Write Character to STDAUX */
1505 // FIXME: Really write it to STDAUX!
1506 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
1507 // DosPrintCharacter(getDL());
1511 /* Write Character to Printer */
1514 // FIXME: Really write it to printer!
1515 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
1516 DPRINT1("0x%p\n", getDL());
1517 DPRINT1("\n\n-----------\n\n");
1521 /* Direct Console I/O */
1524 Character
= getDL();
1526 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1528 if (Character
!= 0xFF)
1531 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1534 * We return the output character (DOS 2.1+).
1535 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
1536 * for more information.
1543 if (DosCheckInput())
1545 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1546 setAL(DosReadCharacter(DOS_INPUT_HANDLE
));
1550 /* No character available */
1551 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1559 /* Character Input without Echo */
1563 DPRINT("Char input without echo\n");
1565 // FIXME: Under DOS 2+, input handle may be redirected!!!!
1566 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1568 // FIXME: For 0x07, do not check Ctrl-C/Break.
1569 // For 0x08, do check those control sequences and if needed,
1572 // /* Let the BOP repeat if needed */
1573 // if (getCF()) break;
1579 /* Write string to STDOUT */
1582 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1584 while (*String
!= '$')
1586 DosPrintCharacter(DOS_OUTPUT_HANDLE
, *String
);
1591 * We return the terminating character (DOS 2.1+).
1592 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
1593 * for more information.
1595 setAL('$'); // *String
1599 /* Read Buffered Input */
1603 InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
1605 DPRINT("Read Buffered Input\n");
1607 while (Count
< InputBuffer
->MaxLength
)
1609 // FIXME!! This function should interpret backspaces etc...
1611 /* Try to read a character (wait) */
1612 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1614 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
1616 /* Echo the character and append it to the buffer */
1617 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1618 InputBuffer
->Buffer
[Count
] = Character
;
1620 Count
++; /* Carriage returns are also counted */
1622 if (Character
== '\r') break;
1625 /* Update the length */
1626 InputBuffer
->Length
= Count
;
1631 /* Get STDIN Status */
1634 setAL(DosCheckInput() ? 0xFF : 0x00);
1638 /* Flush Buffer and Read STDIN */
1641 BYTE InputFunction
= getAL();
1643 /* Flush STDIN buffer */
1644 DosFlushFileBuffers(DOS_INPUT_HANDLE
);
1647 * If the input function number contained in AL is valid, i.e.
1648 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
1649 * recursively with AL == AH.
1651 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
1652 InputFunction
== 0x07 || InputFunction
== 0x08 ||
1653 InputFunction
== 0x0A)
1655 /* Call ourselves recursively */
1656 setAH(InputFunction
);
1665 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1667 // TODO: Flush what's needed.
1668 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
1670 /* Clear CF in DOS 6 only */
1671 if (PspBlock
->DosVersion
== 0x0006)
1672 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1677 /* Set Default Drive */
1680 DosChangeDrive(getDL());
1681 setAL(LastDrive
- 'A' + 1);
1685 /* NULL Function for CP/M Compatibility */
1689 * This function corresponds to the CP/M BDOS function
1690 * "get bit map of logged drives", which is meaningless
1693 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
1694 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
1695 * for more information.
1701 /* Get Default Drive */
1704 setAL(CurrentDrive
);
1708 /* Set Disk Transfer Area */
1711 DiskTransferArea
= MAKELONG(getDX(), getDS());
1715 /* NULL Function for CP/M Compatibility */
1720 * Function 0x1D corresponds to the CP/M BDOS function
1721 * "get bit map of read-only drives", which is meaningless
1723 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
1724 * for more information.
1726 * Function 0x1E corresponds to the CP/M BDOS function
1727 * "set file attributes", which was meaningless under MS-DOS 1.x.
1728 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
1729 * for more information.
1735 /* NULL Function for CP/M Compatibility */
1739 * This function corresponds to the CP/M BDOS function
1740 * "get/set default user (sublibrary) number", which is meaningless
1743 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
1744 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
1745 * for more information.
1751 /* Set Interrupt Vector */
1754 ULONG FarPointer
= MAKELONG(getDX(), getDS());
1755 DPRINT1("Setting interrupt 0x%x ...\n", getAL());
1757 /* Write the new far pointer to the IDT */
1758 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
1762 /* Create New PSP */
1765 DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
1769 /* Get System Date */
1772 GetLocalTime(&SystemTime
);
1773 setCX(SystemTime
.wYear
);
1774 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
1775 setAL(SystemTime
.wDayOfWeek
);
1779 /* Set System Date */
1782 GetLocalTime(&SystemTime
);
1783 SystemTime
.wYear
= getCX();
1784 SystemTime
.wMonth
= getDH();
1785 SystemTime
.wDay
= getDL();
1787 /* Return success or failure */
1788 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1792 /* Get System Time */
1795 GetLocalTime(&SystemTime
);
1796 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
1797 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
1801 /* Set System Time */
1804 GetLocalTime(&SystemTime
);
1805 SystemTime
.wHour
= getCH();
1806 SystemTime
.wMinute
= getCL();
1807 SystemTime
.wSecond
= getDH();
1808 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
1810 /* Return success or failure */
1811 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1815 /* Get Disk Transfer Area */
1818 setES(HIWORD(DiskTransferArea
));
1819 setBX(LOWORD(DiskTransferArea
));
1823 /* Get DOS Version */
1826 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1829 * DOS 2+ - GET DOS VERSION
1830 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
1831 * for more information.
1834 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
1837 * Return DOS OEM number:
1838 * 0x00 for IBM PC-DOS
1839 * 0x02 for packaged MS-DOS
1844 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
1847 * Return version flag:
1848 * 1 << 3 if DOS is in ROM,
1849 * 0 (reserved) if not.
1854 /* Return DOS 24-bit user serial number in BL:CX */
1859 * Return DOS version: Minor:Major in AH:AL
1860 * The Windows NT DOS box returns version 5.00, subject to SETVER.
1862 setAX(PspBlock
->DosVersion
);
1867 /* Extended functionalities */
1870 if (getAL() == 0x06)
1873 * DOS 5+ - GET TRUE VERSION NUMBER
1874 * This function always returns the true version number, unlike
1875 * AH=30h, whose return value may be changed with SETVER.
1876 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
1877 * for more information.
1881 * Return the true DOS version: Minor:Major in BH:BL
1882 * The Windows NT DOS box returns BX=3205h (version 5.50).
1884 setBX(NTDOS_VERSION
);
1886 /* DOS revision 0 */
1894 // /* Invalid subfunction */
1901 /* Get Interrupt Vector */
1904 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[getAL()];
1906 /* Read the address from the IDT into ES:BX */
1907 setES(HIWORD(FarPointer
));
1908 setBX(LOWORD(FarPointer
));
1912 /* SWITCH character - AVAILDEV */
1915 if (getAL() == 0x00)
1918 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
1919 * This setting is ignored by MS-DOS 4.0+.
1920 * MS-DOS 5+ always return AL=00h/DL=2Fh.
1921 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
1922 * for more information.
1927 else if (getAL() == 0x01)
1930 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
1931 * This setting is ignored by MS-DOS 5+.
1932 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
1933 * for more information.
1938 else if (getAL() == 0x02)
1941 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1942 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1943 * for more information.
1948 else if (getAL() == 0x03)
1951 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1952 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1953 * for more information.
1960 /* Invalid subfunction */
1967 /* Create Directory */
1970 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1972 if (CreateDirectoryA(String
, NULL
))
1974 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1978 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1979 setAX(LOWORD(GetLastError()));
1985 /* Remove Directory */
1988 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1990 if (RemoveDirectoryA(String
))
1992 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1996 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1997 setAX(LOWORD(GetLastError()));
2003 /* Set Current Directory */
2006 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2008 if (DosChangeDirectory(String
))
2010 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2014 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2015 setAX(DosLastError
);
2021 /* Create or Truncate File */
2025 WORD ErrorCode
= DosCreateFile(&FileHandle
,
2026 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2030 if (ErrorCode
== ERROR_SUCCESS
)
2032 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2037 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2048 WORD ErrorCode
= DosOpenFile(&FileHandle
,
2049 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2052 if (ErrorCode
== ERROR_SUCCESS
)
2054 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2059 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2069 if (DosCloseHandle(getBX()))
2071 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2075 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2076 setAX(ERROR_INVALID_HANDLE
);
2082 /* Read from File or Device */
2088 DPRINT("INT 21h, AH = 3Fh\n");
2091 ErrorCode
= DosReadFile(getBX(),
2092 SEG_OFF_TO_PTR(getDS(), getDX()),
2097 if (ErrorCode
== ERROR_SUCCESS
)
2099 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2102 else if (ErrorCode
!= ERROR_NOT_READY
)
2104 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2111 /* Write to File or Device */
2114 WORD BytesWritten
= 0;
2115 WORD ErrorCode
= DosWriteFile(getBX(),
2116 SEG_OFF_TO_PTR(getDS(), getDX()),
2120 if (ErrorCode
== ERROR_SUCCESS
)
2122 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2123 setAX(BytesWritten
);
2127 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2137 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2139 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
2141 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2143 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
2144 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
2146 setAL(FileName
[0] - 'A');
2150 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2151 setAX(GetLastError());
2161 WORD ErrorCode
= DosSeekFile(getBX(),
2162 MAKELONG(getDX(), getCX()),
2166 if (ErrorCode
== ERROR_SUCCESS
)
2168 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2170 /* Return the new offset in DX:AX */
2171 setDX(HIWORD(NewLocation
));
2172 setAX(LOWORD(NewLocation
));
2176 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2183 /* Get/Set File Attributes */
2187 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2189 if (getAL() == 0x00)
2191 /* Get the attributes */
2192 Attributes
= GetFileAttributesA(FileName
);
2194 /* Check if it failed */
2195 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
2197 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2198 setAX(GetLastError());
2202 /* Return the attributes that DOS can understand */
2203 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2204 setCX(Attributes
& 0x00FF);
2207 else if (getAL() == 0x01)
2209 /* Try to set the attributes */
2210 if (SetFileAttributesA(FileName
, getCL()))
2212 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2216 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2217 setAX(GetLastError());
2222 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2223 setAX(ERROR_INVALID_FUNCTION
);
2232 if (DosHandleIoctl(getAL(), getBX()))
2234 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2238 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2239 setAX(DosLastError
);
2245 /* Duplicate Handle */
2249 HANDLE Handle
= DosGetRealHandle(getBX());
2251 if (Handle
== INVALID_HANDLE_VALUE
)
2253 /* The handle is invalid */
2254 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2255 setAX(ERROR_INVALID_HANDLE
);
2259 /* Open a new handle to the same entry */
2260 NewHandle
= DosOpenHandle(Handle
);
2262 if (NewHandle
== INVALID_DOS_HANDLE
)
2264 /* Too many files open */
2265 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2266 setAX(ERROR_TOO_MANY_OPEN_FILES
);
2270 /* Return the result */
2271 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2276 /* Force Duplicate Handle */
2279 if (DosDuplicateHandle(getBX(), getCX()))
2281 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2285 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2286 setAX(ERROR_INVALID_HANDLE
);
2292 /* Get Current Directory */
2295 BYTE DriveNumber
= getDL();
2296 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
2298 /* Get the real drive number */
2299 if (DriveNumber
== 0)
2301 DriveNumber
= CurrentDrive
;
2305 /* Decrement DriveNumber since it was 1-based */
2309 if (DriveNumber
<= LastDrive
- 'A')
2312 * Copy the current directory into the target buffer.
2313 * It doesn't contain the drive letter and the backslash.
2315 strncpy(String
, CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
2316 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2317 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
2321 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2322 setAX(ERROR_INVALID_DRIVE
);
2328 /* Allocate Memory */
2331 WORD MaxAvailable
= 0;
2332 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
2336 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2341 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2342 setAX(DosLastError
);
2343 setBX(MaxAvailable
);
2352 if (DosFreeMemory(getES()))
2354 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2358 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2359 setAX(ERROR_ARENA_TRASHED
);
2365 /* Resize Memory Block */
2370 if (DosResizeMemory(getES(), getBX(), &Size
))
2372 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2376 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2377 setAX(DosLastError
);
2388 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)getAL();
2389 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
2390 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
2391 WORD ErrorCode
= DosCreateProcess(LoadType
, ProgramName
, ParamBlock
);
2393 if (ErrorCode
== ERROR_SUCCESS
)
2395 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2399 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2407 /* Terminate With Return Code */
2410 DosTerminateProcess(CurrentPsp
, getAL());
2414 /* Get Return Code (ERRORLEVEL) */
2418 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
2419 * DosErrorLevel is cleared after being read by this function.
2421 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2422 setAX(DosErrorLevel
);
2423 DosErrorLevel
= 0x0000; // Clear it
2427 /* Find First File */
2430 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(DiskTransferArea
),
2431 SEG_OFF_TO_PTR(getDS(), getDX()),
2436 if (Result
== ERROR_SUCCESS
)
2437 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2439 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2444 /* Find Next File */
2447 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(DiskTransferArea
));
2451 if (Result
== ERROR_SUCCESS
)
2452 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2454 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2459 /* Internal - Set Current Process ID (Set PSP Address) */
2462 // FIXME: Is it really what it's done ??
2463 CurrentPsp
= getBX();
2467 /* Internal - Get Current Process ID (Get PSP Address) */
2469 /* Get Current PSP Address */
2473 * Undocumented AH=51h is identical to the documented AH=62h.
2474 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
2475 * and http://www.ctyme.com/intr/rb-3140.htm
2476 * for more information.
2482 /* Internal - Get "List of lists" (SYSVARS) */
2486 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
2487 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
2488 * for more information.
2491 /* Return the DOS "list of lists" in ES:BX */
2495 DisplayMessage(L
"Required for AARD code, do you remember? :P");
2502 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2503 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
2506 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
2507 * for more information.
2510 if (MoveFileA(ExistingFileName
, NewFileName
))
2512 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2516 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2517 setAX(GetLastError());
2523 /* Get/Set Memory Management Options */
2526 if (getAL() == 0x00)
2528 /* Get allocation strategy */
2529 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2530 setAX(DosAllocStrategy
);
2532 else if (getAL() == 0x01)
2534 /* Set allocation strategy */
2536 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2537 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2539 /* Can't set both */
2540 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2541 setAX(ERROR_INVALID_PARAMETER
);
2545 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT
)
2547 /* Invalid allocation strategy */
2548 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2549 setAX(ERROR_INVALID_PARAMETER
);
2553 DosAllocStrategy
= getBL();
2554 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2556 else if (getAL() == 0x02)
2558 /* Get UMB link state */
2559 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2560 setAL(DosUmbLinked
? 0x01 : 0x00);
2562 else if (getAL() == 0x03)
2564 /* Set UMB link state */
2565 if (getBX()) DosLinkUmb();
2566 else DosUnlinkUmb();
2567 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2571 /* Invalid or unsupported function */
2572 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2573 setAX(ERROR_INVALID_FUNCTION
);
2579 /* Create Temporary File */
2582 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2583 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
2589 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
2590 * for more information.
2593 // FIXME: Check for buffer validity?
2594 // It should be a ASCIZ path ending with a '\' + 13 zero bytes
2595 // to receive the generated filename.
2597 /* First create the temporary file */
2598 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
2601 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2602 setAX(GetLastError());
2606 /* Now try to open it in read/write access */
2607 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
2608 if (ErrorCode
== ERROR_SUCCESS
)
2610 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2615 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2622 /* Create New File */
2626 WORD ErrorCode
= DosCreateFile(&FileHandle
,
2627 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2631 if (ErrorCode
== ERROR_SUCCESS
)
2633 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2638 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2645 /* Lock/Unlock Region of File */
2648 HANDLE Handle
= DosGetRealHandle(getBX());
2650 if (Handle
== INVALID_HANDLE_VALUE
)
2652 /* The handle is invalid */
2653 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2654 setAX(ERROR_INVALID_HANDLE
);
2658 if (getAL() == 0x00)
2660 /* Lock region of file */
2661 if (LockFile(Handle
,
2662 MAKELONG(getCX(), getDX()), 0,
2663 MAKELONG(getSI(), getDI()), 0))
2665 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2669 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2670 setAX(GetLastError());
2673 else if (getAL() == 0x01)
2675 /* Unlock region of file */
2676 if (UnlockFile(Handle
,
2677 MAKELONG(getCX(), getDX()), 0,
2678 MAKELONG(getSI(), getDI()), 0))
2680 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2684 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2685 setAX(GetLastError());
2690 /* Invalid subfunction */
2691 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2692 setAX(ERROR_INVALID_FUNCTION
);
2698 /* Canonicalize File Name or Path */
2702 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
2703 * for more information.
2707 * We suppose that the DOS app gave to us a valid
2708 * 128-byte long buffer for the canonicalized name.
2710 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
2712 SEG_OFF_TO_PTR(getES(), getDI()),
2716 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2717 setAX(GetLastError());
2721 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2725 // FIXME: Convert the full path name into short version.
2726 // We cannot reliably use GetShortPathName, because it fails
2727 // if the path name given doesn't exist. However this DOS
2728 // function AH=60h should be able to work even for non-existing
2729 // path and file names.
2734 /* Set Handle Count */
2737 if (!DosResizeHandleTable(getBX()))
2739 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2740 setAX(DosLastError
);
2742 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2752 * Function 6Ah is identical to function 68h,
2753 * and sets AH to 68h if success.
2754 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
2755 * for more information.
2759 if (DosFlushFileBuffers(getBX()))
2761 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2765 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2766 setAX(GetLastError());
2772 /* Extended Open/Create */
2775 /* Check for AL == 00 */
2776 if (getAL() != 0x00)
2778 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2779 setAX(ERROR_INVALID_FUNCTION
);
2783 // TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2784 // FIXME: Extend and merge DosOpenFile and DosCreateFile into
2785 // a single wrapper around CreateFileA, which acts as:
2786 // http://www.ctyme.com/intr/rb-3179.htm
2794 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2797 setAL(0); // Some functions expect AL to be 0 when it's not supported.
2798 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2803 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
2805 UNREFERENCED_PARAMETER(Stack
);
2807 /* Stop the VDM task */
2808 ResetEvent(VdmTaskEvent
);
2809 EmulatorUnsimulate();
2812 VOID WINAPI
DosFastConOut(LPWORD Stack
)
2815 * This is the DOS 2+ Fast Console Output Interrupt.
2816 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
2818 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2819 * for more information.
2822 /* Save AX and BX */
2823 USHORT AX
= getAX();
2824 USHORT BX
= getBX();
2827 * Set the parameters:
2828 * AL contains the character to print (already set),
2829 * BL contains the character attribute,
2830 * BH contains the video page to use.
2832 setBL(DOS_CHAR_ATTRIBUTE
);
2833 setBH(Bda
->VideoPage
);
2835 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2837 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
2839 /* Restore AX and BX */
2844 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
2846 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2848 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2851 BOOLEAN
DosKRNLInitialize(VOID
)
2857 CHAR CurrentDirectory
[MAX_PATH
];
2858 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2864 /* Clear the current directory buffer */
2865 ZeroMemory(CurrentDirectories
, sizeof(CurrentDirectories
));
2867 /* Get the current directory */
2868 if (!GetCurrentDirectoryA(MAX_PATH
, CurrentDirectory
))
2870 // TODO: Use some kind of default path?
2874 /* Convert that to a DOS path */
2875 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, DOS_DIR_LENGTH
))
2877 // TODO: Use some kind of default path?
2882 CurrentDrive
= DosDirectory
[0] - 'A';
2884 /* Get the directory part of the path */
2885 Path
= strchr(DosDirectory
, '\\');
2888 /* Skip the backslash */
2892 /* Set the directory */
2895 strncpy(CurrentDirectories
[CurrentDrive
], Path
, DOS_DIR_LENGTH
);
2898 /* Read CONFIG.SYS */
2899 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
2902 while (fgetws(Buffer
, 256, Stream
))
2904 // TODO: Parse the line
2909 /* Initialize the SFT */
2910 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
2912 DosSystemFileTable
[i
].Handle
= INVALID_HANDLE_VALUE
;
2913 DosSystemFileTable
[i
].RefCount
= 0;
2916 /* Get handles to standard I/O devices */
2917 DosSystemFileTable
[0].Handle
= GetStdHandle(STD_INPUT_HANDLE
);
2918 DosSystemFileTable
[1].Handle
= GetStdHandle(STD_OUTPUT_HANDLE
);
2919 DosSystemFileTable
[2].Handle
= GetStdHandle(STD_ERROR_HANDLE
);
2921 /* Initialize the reference counts */
2922 DosSystemFileTable
[0].RefCount
=
2923 DosSystemFileTable
[1].RefCount
=
2924 DosSystemFileTable
[2].RefCount
= 1;
2928 /* Initialize the callback context */
2929 InitializeContext(&DosContext
, 0x0070, 0x0000);
2931 /* Register the DOS 32-bit Interrupts */
2932 RegisterDosInt32(0x20, DosInt20h
);
2933 RegisterDosInt32(0x21, DosInt21h
);
2934 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2935 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
2936 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2937 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
2938 RegisterDosInt32(0x2F, DosInt2Fh
);