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 *******************************************************************/
21 #include "bios/bios.h"
22 #include "registers.h"
24 /* PRIVATE VARIABLES **********************************************************/
26 CALLBACK16 DosContext
;
28 static WORD CurrentPsp
= SYSTEM_PSP
;
29 static WORD DosLastError
= 0;
30 static DWORD DiskTransferArea
;
31 /*static*/ BYTE CurrentDrive
;
32 static CHAR LastDrive
= 'E';
33 static CHAR CurrentDirectories
[NUM_DRIVES
][DOS_DIR_LENGTH
];
39 } DosSystemFileTable
[DOS_SFT_SIZE
];
41 static BYTE DosAllocStrategy
= DOS_ALLOC_BEST_FIT
;
42 static BOOLEAN DosUmbLinked
= FALSE
;
43 static WORD DosErrorLevel
= 0x0000;
45 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
46 BOOLEAN DoEcho
= FALSE
;
48 /* PRIVATE FUNCTIONS **********************************************************/
51 * Memory management functions
53 static VOID
DosCombineFreeBlocks(WORD StartBlock
)
55 PDOS_MCB CurrentMcb
= SEGMENT_TO_MCB(StartBlock
), NextMcb
;
57 /* If this is the last block or it's not free, quit */
58 if (CurrentMcb
->BlockType
== 'Z' || CurrentMcb
->OwnerPsp
!= 0) return;
62 /* Get a pointer to the next MCB */
63 NextMcb
= SEGMENT_TO_MCB(StartBlock
+ CurrentMcb
->Size
+ 1);
65 /* Check if the next MCB is free */
66 if (NextMcb
->OwnerPsp
== 0)
69 CurrentMcb
->Size
+= NextMcb
->Size
+ 1;
70 CurrentMcb
->BlockType
= NextMcb
->BlockType
;
71 NextMcb
->BlockType
= 'I';
75 /* No more adjoining free blocks */
81 static WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
83 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
84 PDOS_MCB CurrentMcb
, NextMcb
;
85 BOOLEAN SearchUmb
= FALSE
;
87 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size
);
89 if (DosUmbLinked
&& (DosAllocStrategy
& (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
)))
91 /* Search UMB first */
92 Segment
= UMB_START_SEGMENT
;
98 /* Get a pointer to the MCB */
99 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
101 /* Make sure it's valid */
102 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
104 DPRINT("The DOS memory arena is corrupted!\n");
105 DosLastError
= ERROR_ARENA_TRASHED
;
109 /* Only check free blocks */
110 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
112 /* Combine this free block with adjoining free blocks */
113 DosCombineFreeBlocks(Segment
);
115 /* Update the maximum block size */
116 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
118 /* Check if this block is big enough */
119 if (CurrentMcb
->Size
< Size
) goto Next
;
121 switch (DosAllocStrategy
& 0x3F)
123 case DOS_ALLOC_FIRST_FIT
:
125 /* For first fit, stop immediately */
130 case DOS_ALLOC_BEST_FIT
:
132 /* For best fit, update the smallest block found so far */
133 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
141 case DOS_ALLOC_LAST_FIT
:
143 /* For last fit, make the current block the result, but keep searching */
150 /* If this was the last MCB in the chain, quit */
151 if (CurrentMcb
->BlockType
== 'Z')
153 /* Check if nothing was found while searching through UMBs */
154 if ((Result
== 0) && SearchUmb
&& (DosAllocStrategy
& DOS_ALLOC_HIGH_LOW
))
156 /* Search low memory */
157 Segment
= FIRST_MCB_SEGMENT
;
164 /* Otherwise, update the segment and continue */
165 Segment
+= CurrentMcb
->Size
+ 1;
170 /* If we didn't find a free block, return 0 */
173 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
174 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
178 /* Get a pointer to the MCB */
179 CurrentMcb
= SEGMENT_TO_MCB(Result
);
181 /* Check if the block is larger than requested */
182 if (CurrentMcb
->Size
> Size
)
184 /* It is, split it into two blocks */
185 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
187 /* Initialize the new MCB structure */
188 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
189 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
190 NextMcb
->OwnerPsp
= 0;
192 /* Update the current block */
193 CurrentMcb
->BlockType
= 'M';
194 CurrentMcb
->Size
= Size
;
197 /* Take ownership of the block */
198 CurrentMcb
->OwnerPsp
= CurrentPsp
;
200 /* Return the segment of the data portion of the block */
204 static BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
206 BOOLEAN Success
= TRUE
;
207 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
208 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
210 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
214 /* Make sure this is a valid, allocated block */
215 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
218 DosLastError
= ERROR_INVALID_HANDLE
;
222 ReturnSize
= Mcb
->Size
;
224 /* Check if we need to expand or contract the block */
225 if (NewSize
> Mcb
->Size
)
227 /* We can't expand the last block */
228 if (Mcb
->BlockType
!= 'M')
234 /* Get the pointer and segment of the next MCB */
235 NextSegment
= Segment
+ Mcb
->Size
+ 1;
236 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
238 /* Make sure the next segment is free */
239 if (NextMcb
->OwnerPsp
!= 0)
241 DPRINT("Cannot expand memory block: next segment is not free!\n");
242 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
247 /* Combine this free block with adjoining free blocks */
248 DosCombineFreeBlocks(NextSegment
);
250 /* Set the maximum possible size of the block */
251 ReturnSize
+= NextMcb
->Size
+ 1;
253 if (ReturnSize
< NewSize
)
255 DPRINT("Cannot expand memory block: insufficient free segments available!\n");
256 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
261 /* Maximize the current block */
262 Mcb
->Size
= ReturnSize
;
263 Mcb
->BlockType
= NextMcb
->BlockType
;
265 /* Invalidate the next block */
266 NextMcb
->BlockType
= 'I';
268 /* Check if the block is larger than requested */
269 if (Mcb
->Size
> NewSize
)
271 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
275 /* It is, split it into two blocks */
276 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
278 /* Initialize the new MCB structure */
279 NextMcb
->BlockType
= Mcb
->BlockType
;
280 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
281 NextMcb
->OwnerPsp
= 0;
283 /* Update the current block */
284 Mcb
->BlockType
= 'M';
288 else if (NewSize
< Mcb
->Size
)
290 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
294 /* Just split the block */
295 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
296 NextMcb
->BlockType
= Mcb
->BlockType
;
297 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
298 NextMcb
->OwnerPsp
= 0;
301 Mcb
->BlockType
= 'M';
306 /* Check if the operation failed */
309 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
312 /* Return the maximum possible size */
313 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
319 static BOOLEAN
DosFreeMemory(WORD BlockData
)
321 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
323 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData
);
325 /* Make sure the MCB is valid */
326 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z')
328 DPRINT("MCB block type '%c' not valid!\n", Mcb
->BlockType
);
332 /* Mark the block as free */
338 static BOOLEAN
DosLinkUmb(VOID
)
340 DWORD Segment
= FIRST_MCB_SEGMENT
;
341 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
343 DPRINT("Linking UMB\n");
345 /* Check if UMBs are already linked */
346 if (DosUmbLinked
) return FALSE
;
348 /* Find the last block */
349 while ((Mcb
->BlockType
== 'M') && (Segment
<= 0xFFFF))
351 Segment
+= Mcb
->Size
+ 1;
352 Mcb
= SEGMENT_TO_MCB(Segment
);
355 /* Make sure it's valid */
356 if (Mcb
->BlockType
!= 'Z') return FALSE
;
358 /* Connect the MCB with the UMB chain */
359 Mcb
->BlockType
= 'M';
365 static BOOLEAN
DosUnlinkUmb(VOID
)
367 DWORD Segment
= FIRST_MCB_SEGMENT
;
368 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
370 DPRINT("Unlinking UMB\n");
372 /* Check if UMBs are already unlinked */
373 if (!DosUmbLinked
) return FALSE
;
375 /* Find the block preceding the MCB that links it with the UMB chain */
376 while (Segment
<= 0xFFFF)
378 if ((Segment
+ Mcb
->Size
) == (FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
))
380 /* This is the last non-UMB segment */
384 /* Advance to the next MCB */
385 Segment
+= Mcb
->Size
+ 1;
386 Mcb
= SEGMENT_TO_MCB(Segment
);
389 /* Mark the MCB as the last MCB */
390 Mcb
->BlockType
= 'Z';
392 DosUmbLinked
= FALSE
;
396 static VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
398 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
400 /* Just set the owner */
401 Mcb
->OwnerPsp
= NewOwner
;
404 static WORD
DosCopyEnvironmentBlock(LPCVOID Environment
, LPCSTR ProgramName
)
406 PCHAR Ptr
, DestBuffer
= NULL
;
410 Ptr
= (PCHAR
)Environment
;
412 /* Calculate the size of the environment block */
415 TotalSize
+= strlen(Ptr
) + 1;
416 Ptr
+= strlen(Ptr
) + 1;
420 /* Add the string buffer size */
421 TotalSize
+= strlen(ProgramName
) + 1;
423 /* Add the two extra bytes */
426 /* Allocate the memory for the environment block */
427 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
428 if (!DestSegment
) return 0;
430 Ptr
= (PCHAR
)Environment
;
432 DestBuffer
= (PCHAR
)SEG_OFF_TO_PTR(DestSegment
, 0);
435 /* Copy the string */
436 strcpy(DestBuffer
, Ptr
);
438 /* Advance to the next string */
439 DestBuffer
+= strlen(Ptr
);
440 Ptr
+= strlen(Ptr
) + 1;
442 /* Put a zero after the string */
446 /* Set the final zero */
449 /* Store the special program name tag */
450 *(DestBuffer
++) = LOBYTE(DOS_PROGRAM_NAME_TAG
);
451 *(DestBuffer
++) = HIBYTE(DOS_PROGRAM_NAME_TAG
);
453 /* Copy the program name after the environment block */
454 strcpy(DestBuffer
, ProgramName
);
464 /* Taken from base/shell/cmd/console.c */
465 BOOL
IsConsoleHandle(HANDLE hHandle
)
469 /* Check whether the handle may be that of a console... */
470 if ((GetFileType(hHandle
) & FILE_TYPE_CHAR
) == 0) return FALSE
;
473 * It may be. Perform another test... The idea comes from the
474 * MSDN description of the WriteConsole API:
476 * "WriteConsole fails if it is used with a standard handle
477 * that is redirected to a file. If an application processes
478 * multilingual output that can be redirected, determine whether
479 * the output handle is a console handle (one method is to call
480 * the GetConsoleMode function and check whether it succeeds).
481 * If the handle is a console handle, call WriteConsole. If the
482 * handle is not a console handle, the output is redirected and
483 * you should call WriteFile to perform the I/O."
485 return GetConsoleMode(hHandle
, &dwMode
);
488 WORD
DosOpenHandle(HANDLE Handle
)
495 /* The system PSP has no handle table */
496 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
498 /* Get a pointer to the handle table */
499 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
500 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
502 /* Find a free entry in the JFT */
503 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
505 if (HandleTable
[DosHandle
] == 0xFF) break;
508 /* If there are no free entries, fail */
509 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
511 /* Check if the handle is already in the SFT */
512 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
514 /* Check if this is the same handle */
515 if (DosSystemFileTable
[i
].Handle
!= Handle
) continue;
517 /* Already in the table, reference it */
518 DosSystemFileTable
[i
].RefCount
++;
520 /* Set the JFT entry to that SFT index */
521 HandleTable
[DosHandle
] = i
;
523 /* Return the new handle */
527 /* Add the handle to the SFT */
528 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
530 /* Make sure this is an empty table entry */
531 if (DosSystemFileTable
[i
].Handle
!= INVALID_HANDLE_VALUE
) continue;
533 /* Initialize the empty table entry */
534 DosSystemFileTable
[i
].Handle
= Handle
;
535 DosSystemFileTable
[i
].RefCount
= 1;
537 /* Set the JFT entry to that SFT index */
538 HandleTable
[DosHandle
] = i
;
540 /* Return the new handle */
544 /* The SFT is full */
545 return INVALID_DOS_HANDLE
;
548 HANDLE
DosGetRealHandle(WORD DosHandle
)
553 /* The system PSP has no handle table */
554 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_HANDLE_VALUE
;
556 /* Get a pointer to the handle table */
557 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
558 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
560 /* Make sure the handle is open */
561 if (HandleTable
[DosHandle
] == 0xFF) return INVALID_HANDLE_VALUE
;
563 /* Return the Win32 handle */
564 return DosSystemFileTable
[HandleTable
[DosHandle
]].Handle
;
567 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
573 /* Clear the table first */
574 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
576 /* Check if this is the initial process */
577 if (CurrentPsp
== SYSTEM_PSP
)
579 /* Set up the standard I/O devices */
580 for (i
= 0; i
<= 2; i
++)
582 /* Set the index in the SFT */
583 DestinationTable
[i
] = (BYTE
)i
;
585 /* Increase the reference count */
586 DosSystemFileTable
[i
].RefCount
++;
593 /* Get the parent PSP block and handle table */
594 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
595 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
597 /* Copy the first 20 handles into the new table */
598 for (i
= 0; i
< 20; i
++)
600 DestinationTable
[i
] = SourceTable
[i
];
602 /* Increase the reference count */
603 DosSystemFileTable
[SourceTable
[i
]].RefCount
++;
607 static BOOLEAN
DosResizeHandleTable(WORD NewSize
)
613 /* Get the PSP block */
614 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
616 if (NewSize
== PspBlock
->HandleTableSize
)
622 if (PspBlock
->HandleTableSize
> 20)
624 /* Get the segment of the current table */
625 Segment
= (LOWORD(PspBlock
->HandleTablePtr
) >> 4) + HIWORD(PspBlock
->HandleTablePtr
);
629 /* Get the current handle table */
630 HandleTable
= FAR_POINTER(PspBlock
->HandleTablePtr
);
632 /* Copy it to the PSP */
633 RtlCopyMemory(PspBlock
->HandleTable
, HandleTable
, NewSize
);
635 /* Free the memory */
636 DosFreeMemory(Segment
);
638 /* Update the handle table pointer and size */
639 PspBlock
->HandleTableSize
= NewSize
;
640 PspBlock
->HandleTablePtr
= MAKELONG(0x18, CurrentPsp
);
644 /* Resize the memory */
645 if (!DosResizeMemory(Segment
, NewSize
, NULL
))
647 /* Unable to resize, try allocating it somewhere else */
648 Segment
= DosAllocateMemory(NewSize
, NULL
);
649 if (Segment
== 0) return FALSE
;
651 /* Get the new handle table */
652 HandleTable
= SEG_OFF_TO_PTR(Segment
, 0);
654 /* Copy the handles to the new table */
655 RtlCopyMemory(HandleTable
,
656 FAR_POINTER(PspBlock
->HandleTablePtr
),
657 PspBlock
->HandleTableSize
);
659 /* Update the handle table pointer */
660 PspBlock
->HandleTablePtr
= MAKELONG(0, Segment
);
663 /* Update the handle table size */
664 PspBlock
->HandleTableSize
= NewSize
;
667 else if (NewSize
> 20)
669 Segment
= DosAllocateMemory(NewSize
, NULL
);
670 if (Segment
== 0) return FALSE
;
672 /* Get the new handle table */
673 HandleTable
= SEG_OFF_TO_PTR(Segment
, 0);
675 /* Copy the handles from the PSP to the new table */
676 RtlCopyMemory(HandleTable
,
677 FAR_POINTER(PspBlock
->HandleTablePtr
),
678 PspBlock
->HandleTableSize
);
680 /* Update the handle table pointer and size */
681 PspBlock
->HandleTableSize
= NewSize
;
682 PspBlock
->HandleTablePtr
= MAKELONG(0, Segment
);
688 static BOOLEAN
DosCloseHandle(WORD DosHandle
)
694 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle
);
696 /* The system PSP has no handle table */
697 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
699 /* Get a pointer to the handle table */
700 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
701 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
703 /* Make sure the handle is open */
704 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
706 /* Decrement the reference count of the SFT entry */
707 SftIndex
= HandleTable
[DosHandle
];
708 DosSystemFileTable
[SftIndex
].RefCount
--;
710 /* Check if the reference count fell to zero */
711 if (!DosSystemFileTable
[SftIndex
].RefCount
)
713 /* Close the file, it's no longer needed */
714 CloseHandle(DosSystemFileTable
[SftIndex
].Handle
);
716 /* Clear the handle */
717 DosSystemFileTable
[SftIndex
].Handle
= INVALID_HANDLE_VALUE
;
720 /* Clear the entry in the JFT */
721 HandleTable
[DosHandle
] = 0xFF;
726 static BOOLEAN
DosDuplicateHandle(WORD OldHandle
, WORD NewHandle
)
732 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
736 /* The system PSP has no handle table */
737 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
739 /* Get a pointer to the handle table */
740 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
741 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
743 /* Make sure the old handle is open */
744 if (HandleTable
[OldHandle
] == 0xFF) return FALSE
;
746 /* Check if the new handle is open */
747 if (HandleTable
[NewHandle
] != 0xFF)
750 DosCloseHandle(NewHandle
);
753 /* Increment the reference count of the SFT entry */
754 SftIndex
= HandleTable
[OldHandle
];
755 DosSystemFileTable
[SftIndex
].RefCount
++;
757 /* Make the new handle point to that SFT entry */
758 HandleTable
[NewHandle
] = SftIndex
;
770 static BOOLEAN
DosChangeDrive(BYTE Drive
)
772 WCHAR DirectoryPath
[DOS_CMDLINE_LENGTH
];
774 /* Make sure the drive exists */
775 if (Drive
> (LastDrive
- 'A')) return FALSE
;
777 /* Find the path to the new current directory */
778 swprintf(DirectoryPath
, L
"%c\\%S", Drive
+ 'A', CurrentDirectories
[Drive
]);
780 /* Change the current directory of the process */
781 if (!SetCurrentDirectory(DirectoryPath
)) return FALSE
;
783 /* Set the current drive */
784 CurrentDrive
= Drive
;
790 static BOOLEAN
DosChangeDirectory(LPSTR Directory
)
796 /* Make sure the directory path is not too long */
797 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
799 DosLastError
= ERROR_PATH_NOT_FOUND
;
803 /* Get the drive number */
804 DriveNumber
= Directory
[0] - 'A';
806 /* Make sure the drive exists */
807 if (DriveNumber
> (LastDrive
- 'A'))
809 DosLastError
= ERROR_PATH_NOT_FOUND
;
813 /* Get the file attributes */
814 Attributes
= GetFileAttributesA(Directory
);
816 /* Make sure the path exists and is a directory */
817 if ((Attributes
== INVALID_FILE_ATTRIBUTES
)
818 || !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
820 DosLastError
= ERROR_PATH_NOT_FOUND
;
824 /* Check if this is the current drive */
825 if (DriveNumber
== CurrentDrive
)
827 /* Change the directory */
828 if (!SetCurrentDirectoryA(Directory
))
830 DosLastError
= LOWORD(GetLastError());
835 /* Get the directory part of the path */
836 Path
= strchr(Directory
, '\\');
839 /* Skip the backslash */
843 /* Set the directory for the drive */
846 strncpy(CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
850 CurrentDirectories
[DriveNumber
][0] = '\0';
857 /* PUBLIC FUNCTIONS ***********************************************************/
859 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
861 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
862 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
864 ZeroMemory(PspBlock
, sizeof(DOS_PSP
));
866 /* Set the exit interrupt */
867 PspBlock
->Exit
[0] = 0xCD; // int 0x20
868 PspBlock
->Exit
[1] = 0x20;
870 /* Set the number of the last paragraph */
871 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
873 /* Save the interrupt vectors */
874 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
875 PspBlock
->BreakAddress
= IntVecTable
[0x23];
876 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
878 /* Set the parent PSP */
879 PspBlock
->ParentPsp
= CurrentPsp
;
881 /* Copy the parent handle table */
882 DosCopyHandleTable(PspBlock
->HandleTable
);
884 /* Set the environment block */
885 PspBlock
->EnvBlock
= Environment
;
887 /* Set the handle table pointers to the internal handle table */
888 PspBlock
->HandleTableSize
= 20;
889 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
891 /* Set the DOS version */
892 PspBlock
->DosVersion
= DOS_VERSION
;
894 /* Set the far call opcodes */
895 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
896 PspBlock
->FarCall
[1] = 0x21;
897 PspBlock
->FarCall
[2] = 0xCB; // retf
899 /* Set the command line */
900 PspBlock
->CommandLineSize
= (BYTE
)min(strlen(CommandLine
), DOS_CMDLINE_LENGTH
- 1);
901 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
902 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
905 DWORD
DosLoadExecutable(IN DOS_EXEC_TYPE LoadType
,
906 IN LPCSTR ExecutablePath
,
907 IN LPCSTR CommandLine
,
908 IN PVOID Environment
,
909 OUT PDWORD StackLocation OPTIONAL
,
910 OUT PDWORD EntryPoint OPTIONAL
)
912 DWORD Result
= ERROR_SUCCESS
;
913 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
914 LPBYTE Address
= NULL
;
918 DWORD i
, FileSize
, ExeSize
;
919 PIMAGE_DOS_HEADER Header
;
920 PDWORD RelocationTable
;
922 LPSTR CmdLinePtr
= (LPSTR
)CommandLine
;
924 DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
932 if (LoadType
== DOS_LOAD_OVERLAY
)
934 DPRINT1("Overlay loading is not supported yet.\n");
935 return ERROR_NOT_SUPPORTED
;
938 /* NULL-terminate the command line by removing the return carriage character */
939 while (*CmdLinePtr
&& *CmdLinePtr
!= '\r') CmdLinePtr
++;
942 /* Open a handle to the executable */
943 FileHandle
= CreateFileA(ExecutablePath
,
948 FILE_ATTRIBUTE_NORMAL
,
950 if (FileHandle
== INVALID_HANDLE_VALUE
)
952 Result
= GetLastError();
956 /* Get the file size */
957 FileSize
= GetFileSize(FileHandle
, NULL
);
959 /* Create a mapping object for the file */
960 FileMapping
= CreateFileMapping(FileHandle
,
966 if (FileMapping
== NULL
)
968 Result
= GetLastError();
972 /* Map the file into memory */
973 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
976 Result
= GetLastError();
980 /* Copy the environment block to DOS memory */
981 EnvBlock
= DosCopyEnvironmentBlock(Environment
, ExecutablePath
);
984 Result
= ERROR_NOT_ENOUGH_MEMORY
;
988 /* Check if this is an EXE file or a COM file */
989 if (Address
[0] == 'M' && Address
[1] == 'Z')
993 /* Get the MZ header */
994 Header
= (PIMAGE_DOS_HEADER
)Address
;
996 /* Get the base size of the file, in paragraphs (rounded up) */
997 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
999 /* Add the PSP size, in paragraphs */
1000 ExeSize
+= sizeof(DOS_PSP
) >> 4;
1002 /* Add the maximum size that should be allocated */
1003 ExeSize
+= Header
->e_maxalloc
;
1005 /* Make sure it does not pass 0xFFFF */
1006 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
1008 /* Reduce the size one by one until the allocation is successful */
1009 for (i
= Header
->e_maxalloc
; i
>= Header
->e_minalloc
; i
--, ExeSize
--)
1011 /* Try to allocate that much memory */
1012 Segment
= DosAllocateMemory((WORD
)ExeSize
, NULL
);
1013 if (Segment
!= 0) break;
1016 /* Check if at least the lowest allocation was successful */
1019 Result
= DosLastError
;
1023 /* Initialize the PSP */
1024 DosInitializePsp(Segment
,
1029 /* The process owns its own memory */
1030 DosChangeMemoryOwner(Segment
, Segment
);
1031 DosChangeMemoryOwner(EnvBlock
, Segment
);
1033 /* Copy the program to Segment:0100 */
1034 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
1035 Address
+ (Header
->e_cparhdr
<< 4),
1036 min(FileSize
- (Header
->e_cparhdr
<< 4),
1037 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
1039 /* Get the relocation table */
1040 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
1042 /* Perform relocations */
1043 for (i
= 0; i
< Header
->e_crlc
; i
++)
1045 /* Get a pointer to the word that needs to be patched */
1046 RelocWord
= (PWORD
)SEG_OFF_TO_PTR(Segment
+ HIWORD(RelocationTable
[i
]),
1047 0x100 + LOWORD(RelocationTable
[i
]));
1049 /* Add the number of the EXE segment to it */
1050 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
1053 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1055 /* Set the initial segment registers */
1059 /* Set the stack to the location from the header */
1060 EmulatorSetStack(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
,
1064 CurrentPsp
= Segment
;
1065 DiskTransferArea
= MAKELONG(0x80, Segment
);
1066 CpuExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
1074 /* Find the maximum amount of memory that can be allocated */
1075 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
1077 /* Make sure it's enough for the whole program and the PSP */
1078 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
)))
1080 Result
= ERROR_NOT_ENOUGH_MEMORY
;
1084 /* Allocate all of it */
1085 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
1088 Result
= DosLastError
;
1092 /* The process owns its own memory */
1093 DosChangeMemoryOwner(Segment
, Segment
);
1094 DosChangeMemoryOwner(EnvBlock
, Segment
);
1096 /* Copy the program to Segment:0100 */
1097 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
1101 /* Initialize the PSP */
1102 DosInitializePsp(Segment
,
1107 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
1109 /* Set the initial segment registers */
1113 /* Set the stack to the last word of the segment */
1114 EmulatorSetStack(Segment
, 0xFFFE);
1117 * Set the value on the stack to 0, so that a near return
1118 * jumps to PSP:0000 which has the exit code.
1120 *((LPWORD
)SEG_OFF_TO_PTR(Segment
, 0xFFFE)) = 0;
1123 CurrentPsp
= Segment
;
1124 DiskTransferArea
= MAKELONG(0x80, Segment
);
1125 CpuExecute(Segment
, 0x100);
1130 if (Result
!= ERROR_SUCCESS
)
1132 /* It was not successful, cleanup the DOS memory */
1133 if (EnvBlock
) DosFreeMemory(EnvBlock
);
1134 if (Segment
) DosFreeMemory(Segment
);
1138 if (Address
!= NULL
) UnmapViewOfFile(Address
);
1140 /* Close the file mapping object */
1141 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
1143 /* Close the file handle */
1144 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
1149 DWORD
DosStartProcess(IN LPCSTR ExecutablePath
,
1150 IN LPCSTR CommandLine
,
1151 IN PVOID Environment
)
1155 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
1162 if (Result
!= ERROR_SUCCESS
) goto Quit
;
1164 /* Attach to the console */
1165 VidBiosAttachToConsole(); // FIXME: And in fact, attach the full NTVDM UI to the console
1167 /* Start simulation */
1168 SetEvent(VdmTaskEvent
);
1171 /* Detach from the console */
1172 VidBiosDetachFromConsole(); // FIXME: And in fact, detach the full NTVDM UI from the console
1179 WORD
DosCreateProcess(DOS_EXEC_TYPE LoadType
,
1181 PDOS_EXEC_PARAM_BLOCK Parameters
)
1185 LPVOID Environment
= NULL
;
1186 VDM_COMMAND_INFO CommandInfo
;
1187 CHAR CmdLine
[MAX_PATH
];
1188 CHAR AppName
[MAX_PATH
];
1189 CHAR PifFile
[MAX_PATH
];
1190 CHAR Desktop
[MAX_PATH
];
1191 CHAR Title
[MAX_PATH
];
1193 STARTUPINFOA StartupInfo
;
1194 PROCESS_INFORMATION ProcessInfo
;
1196 /* Get the binary type */
1197 if (!GetBinaryTypeA(ProgramName
, &BinaryType
)) return GetLastError();
1199 /* Did the caller specify an environment segment? */
1200 if (Parameters
->Environment
)
1202 /* Yes, use it instead of the parent one */
1203 Environment
= SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
1206 /* Set up the startup info structure */
1207 ZeroMemory(&StartupInfo
, sizeof(STARTUPINFOA
));
1208 StartupInfo
.cb
= sizeof(STARTUPINFOA
);
1210 /* Create the process */
1211 if (!CreateProcessA(ProgramName
,
1212 FAR_POINTER(Parameters
->CommandLine
),
1222 return GetLastError();
1225 /* Check the type of the program */
1228 /* These are handled by NTVDM */
1229 case SCS_DOS_BINARY
:
1230 case SCS_WOW_BINARY
:
1232 /* Clear the structure */
1233 ZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
1235 /* Initialize the structure members */
1236 CommandInfo
.TaskId
= SessionId
;
1237 CommandInfo
.VDMState
= VDM_FLAG_NESTED_TASK
| VDM_FLAG_DONT_WAIT
;
1238 CommandInfo
.CmdLine
= CmdLine
;
1239 CommandInfo
.CmdLen
= sizeof(CmdLine
);
1240 CommandInfo
.AppName
= AppName
;
1241 CommandInfo
.AppLen
= sizeof(AppName
);
1242 CommandInfo
.PifFile
= PifFile
;
1243 CommandInfo
.PifLen
= sizeof(PifFile
);
1244 CommandInfo
.Desktop
= Desktop
;
1245 CommandInfo
.DesktopLen
= sizeof(Desktop
);
1246 CommandInfo
.Title
= Title
;
1247 CommandInfo
.TitleLen
= sizeof(Title
);
1248 CommandInfo
.Env
= Env
;
1249 CommandInfo
.EnvLen
= sizeof(Env
);
1251 /* Get the VDM command information */
1252 if (!GetNextVDMCommand(&CommandInfo
))
1254 /* Shouldn't happen */
1258 /* Increment the re-entry count */
1259 CommandInfo
.VDMState
= VDM_INC_REENTER_COUNT
;
1260 GetNextVDMCommand(&CommandInfo
);
1262 /* Load the executable */
1263 Result
= DosLoadExecutable(LoadType
,
1267 &Parameters
->StackLocation
,
1268 &Parameters
->EntryPoint
);
1269 if (Result
!= ERROR_SUCCESS
)
1271 DisplayMessage(L
"Could not load '%S'. Error: %u", AppName
, Result
);
1272 // FIXME: Decrement the reenter count. Or, instead, just increment
1273 // the VDM reenter count *only* if this call succeeds...
1279 /* Not handled by NTVDM */
1282 /* Wait for the process to finish executing */
1283 WaitForSingleObject(ProcessInfo
.hProcess
, INFINITE
);
1287 /* Close the handles */
1288 CloseHandle(ProcessInfo
.hProcess
);
1289 CloseHandle(ProcessInfo
.hThread
);
1291 return ERROR_SUCCESS
;
1295 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
)
1298 WORD McbSegment
= FIRST_MCB_SEGMENT
;
1299 PDOS_MCB CurrentMcb
;
1300 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
1301 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
1303 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
1307 /* Check if this PSP is it's own parent */
1308 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
1310 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
1312 /* Close the handle */
1316 /* Free the memory used by the process */
1319 /* Get a pointer to the MCB */
1320 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
1322 /* Make sure the MCB is valid */
1323 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!='Z') break;
1325 /* If this block was allocated by the process, free it */
1326 if (CurrentMcb
->OwnerPsp
== Psp
) DosFreeMemory(McbSegment
+ 1);
1328 /* If this was the last block, quit */
1329 if (CurrentMcb
->BlockType
== 'Z') break;
1331 /* Update the segment and continue */
1332 McbSegment
+= CurrentMcb
->Size
+ 1;
1336 /* Restore the interrupt vectors */
1337 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
1338 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
1339 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
1341 /* Update the current PSP */
1342 if (Psp
== CurrentPsp
)
1344 CurrentPsp
= PspBlock
->ParentPsp
;
1345 if (CurrentPsp
== SYSTEM_PSP
)
1347 ResetEvent(VdmTaskEvent
);
1353 // FIXME: This is probably not the best way to do it
1354 /* Check if this was a nested DOS task */
1355 if (CurrentPsp
!= SYSTEM_PSP
)
1357 VDM_COMMAND_INFO CommandInfo
;
1359 /* Decrement the re-entry count */
1360 CommandInfo
.TaskId
= SessionId
;
1361 CommandInfo
.VDMState
= VDM_DEC_REENTER_COUNT
;
1362 GetNextVDMCommand(&CommandInfo
);
1364 /* Clear the structure */
1365 ZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
1367 /* Update the VDM state of the task */
1368 CommandInfo
.TaskId
= SessionId
;
1369 CommandInfo
.VDMState
= VDM_FLAG_DONT_WAIT
;
1370 GetNextVDMCommand(&CommandInfo
);
1374 /* Save the return code - Normal termination */
1375 DosErrorLevel
= MAKEWORD(ReturnCode
, 0x00);
1377 /* Return control to the parent process */
1378 CpuExecute(HIWORD(PspBlock
->TerminateAddress
),
1379 LOWORD(PspBlock
->TerminateAddress
));
1382 BOOLEAN
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
1384 HANDLE Handle
= DosGetRealHandle(FileHandle
);
1386 if (Handle
== INVALID_HANDLE_VALUE
)
1389 DosLastError
= ERROR_FILE_NOT_FOUND
;
1393 switch (ControlCode
)
1395 /* Get Device Information */
1401 * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
1402 * for a list of possible flags.
1405 if (Handle
== DosSystemFileTable
[DOS_INPUT_HANDLE
].Handle
)
1410 /* It is a device */
1413 else if (Handle
== DosSystemFileTable
[DOS_OUTPUT_HANDLE
].Handle
)
1415 /* Console output */
1418 /* It is a device */
1422 /* Return the device information word */
1427 /* Unsupported control code */
1430 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
1432 DosLastError
= ERROR_INVALID_PARAMETER
;
1438 VOID WINAPI
DosInt20h(LPWORD Stack
)
1440 /* This is the exit interrupt */
1441 DosTerminateProcess(Stack
[STACK_CS
], 0);
1444 VOID WINAPI
DosInt21h(LPWORD Stack
)
1447 SYSTEMTIME SystemTime
;
1449 PDOS_INPUT_BUFFER InputBuffer
;
1451 /* Check the value in the AH register */
1454 /* Terminate Program */
1457 DosTerminateProcess(Stack
[STACK_CS
], 0);
1461 /* Read Character from STDIN with Echo */
1464 DPRINT("INT 21h, AH = 01h\n");
1466 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
1468 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1471 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
1472 // Check also Ctrl-P and set echo-to-printer flag.
1473 // Ctrl-Z is not interpreted.
1479 /* Write Character to STDOUT */
1482 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1483 Character
= getDL();
1484 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1487 * We return the output character (DOS 2.1+).
1488 * Also, if we're going to output a TAB, then
1489 * don't return a TAB but a SPACE instead.
1490 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
1491 * for more information.
1493 setAL(Character
== '\t' ? ' ' : Character
);
1497 /* Read Character from STDAUX */
1500 // FIXME: Really read it from STDAUX!
1501 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
1502 // setAL(DosReadCharacter());
1506 /* Write Character to STDAUX */
1509 // FIXME: Really write it to STDAUX!
1510 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
1511 // DosPrintCharacter(getDL());
1515 /* Write Character to Printer */
1518 // FIXME: Really write it to printer!
1519 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
1520 DPRINT1("0x%p\n", getDL());
1521 DPRINT1("\n\n-----------\n\n");
1525 /* Direct Console I/O */
1528 Character
= getDL();
1530 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1532 if (Character
!= 0xFF)
1535 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1538 * We return the output character (DOS 2.1+).
1539 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
1540 * for more information.
1547 if (DosCheckInput())
1549 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1550 setAL(DosReadCharacter(DOS_INPUT_HANDLE
));
1554 /* No character available */
1555 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1563 /* Character Input without Echo */
1567 DPRINT("Char input without echo\n");
1569 // FIXME: Under DOS 2+, input handle may be redirected!!!!
1570 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1572 // FIXME: For 0x07, do not check Ctrl-C/Break.
1573 // For 0x08, do check those control sequences and if needed,
1576 // /* Let the BOP repeat if needed */
1577 // if (getCF()) break;
1583 /* Write string to STDOUT */
1586 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1588 while (*String
!= '$')
1590 DosPrintCharacter(DOS_OUTPUT_HANDLE
, *String
);
1595 * We return the terminating character (DOS 2.1+).
1596 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
1597 * for more information.
1599 setAL('$'); // *String
1603 /* Read Buffered Input */
1607 InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
1609 DPRINT("Read Buffered Input\n");
1611 while (Count
< InputBuffer
->MaxLength
)
1613 // FIXME!! This function should interpret backspaces etc...
1615 /* Try to read a character (wait) */
1616 Character
= DosReadCharacter(DOS_INPUT_HANDLE
);
1618 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
1620 /* Echo the character and append it to the buffer */
1621 DosPrintCharacter(DOS_OUTPUT_HANDLE
, Character
);
1622 InputBuffer
->Buffer
[Count
] = Character
;
1624 Count
++; /* Carriage returns are also counted */
1626 if (Character
== '\r') break;
1629 /* Update the length */
1630 InputBuffer
->Length
= Count
;
1635 /* Get STDIN Status */
1638 setAL(DosCheckInput() ? 0xFF : 0x00);
1642 /* Flush Buffer and Read STDIN */
1645 BYTE InputFunction
= getAL();
1647 /* Flush STDIN buffer */
1648 DosFlushFileBuffers(DOS_INPUT_HANDLE
);
1651 * If the input function number contained in AL is valid, i.e.
1652 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
1653 * recursively with AL == AH.
1655 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
1656 InputFunction
== 0x07 || InputFunction
== 0x08 ||
1657 InputFunction
== 0x0A)
1659 /* Call ourselves recursively */
1660 setAH(InputFunction
);
1669 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1671 // TODO: Flush what's needed.
1672 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
1674 /* Clear CF in DOS 6 only */
1675 if (PspBlock
->DosVersion
== 0x0006)
1676 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1681 /* Set Default Drive */
1684 DosChangeDrive(getDL());
1685 setAL(LastDrive
- 'A' + 1);
1689 /* NULL Function for CP/M Compatibility */
1693 * This function corresponds to the CP/M BDOS function
1694 * "get bit map of logged drives", which is meaningless
1697 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
1698 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
1699 * for more information.
1705 /* Get Default Drive */
1708 setAL(CurrentDrive
);
1712 /* Set Disk Transfer Area */
1715 DiskTransferArea
= MAKELONG(getDX(), getDS());
1719 /* NULL Function for CP/M Compatibility */
1724 * Function 0x1D corresponds to the CP/M BDOS function
1725 * "get bit map of read-only drives", which is meaningless
1727 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
1728 * for more information.
1730 * Function 0x1E corresponds to the CP/M BDOS function
1731 * "set file attributes", which was meaningless under MS-DOS 1.x.
1732 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
1733 * for more information.
1739 /* NULL Function for CP/M Compatibility */
1743 * This function corresponds to the CP/M BDOS function
1744 * "get/set default user (sublibrary) number", which is meaningless
1747 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
1748 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
1749 * for more information.
1755 /* Set Interrupt Vector */
1758 ULONG FarPointer
= MAKELONG(getDX(), getDS());
1759 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
1760 getAL(), HIWORD(FarPointer
), LOWORD(FarPointer
));
1762 /* Write the new far pointer to the IDT */
1763 ((PULONG
)BaseAddress
)[getAL()] = FarPointer
;
1767 /* Create New PSP */
1770 DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
1774 /* Get System Date */
1777 GetLocalTime(&SystemTime
);
1778 setCX(SystemTime
.wYear
);
1779 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
1780 setAL(SystemTime
.wDayOfWeek
);
1784 /* Set System Date */
1787 GetLocalTime(&SystemTime
);
1788 SystemTime
.wYear
= getCX();
1789 SystemTime
.wMonth
= getDH();
1790 SystemTime
.wDay
= getDL();
1792 /* Return success or failure */
1793 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1797 /* Get System Time */
1800 GetLocalTime(&SystemTime
);
1801 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
1802 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
1806 /* Set System Time */
1809 GetLocalTime(&SystemTime
);
1810 SystemTime
.wHour
= getCH();
1811 SystemTime
.wMinute
= getCL();
1812 SystemTime
.wSecond
= getDH();
1813 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
1815 /* Return success or failure */
1816 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1820 /* Get Disk Transfer Area */
1823 setES(HIWORD(DiskTransferArea
));
1824 setBX(LOWORD(DiskTransferArea
));
1828 /* Get DOS Version */
1831 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1834 * DOS 2+ - GET DOS VERSION
1835 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
1836 * for more information.
1839 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
1842 * Return DOS OEM number:
1843 * 0x00 for IBM PC-DOS
1844 * 0x02 for packaged MS-DOS
1850 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
1853 * Return version flag:
1854 * 1 << 3 if DOS is in ROM,
1855 * 0 (reserved) if not.
1860 /* Return DOS 24-bit user serial number in BL:CX */
1865 * Return DOS version: Minor:Major in AH:AL
1866 * The Windows NT DOS box returns version 5.00, subject to SETVER.
1868 setAX(PspBlock
->DosVersion
);
1873 /* Extended functionalities */
1876 if (getAL() == 0x06)
1879 * DOS 5+ - GET TRUE VERSION NUMBER
1880 * This function always returns the true version number, unlike
1881 * AH=30h, whose return value may be changed with SETVER.
1882 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
1883 * for more information.
1887 * Return the true DOS version: Minor:Major in BH:BL
1888 * The Windows NT DOS box returns BX=3205h (version 5.50).
1890 setBX(NTDOS_VERSION
);
1892 /* DOS revision 0 */
1900 // /* Invalid subfunction */
1907 /* Get Interrupt Vector */
1910 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[getAL()];
1912 /* Read the address from the IDT into ES:BX */
1913 setES(HIWORD(FarPointer
));
1914 setBX(LOWORD(FarPointer
));
1918 /* SWITCH character - AVAILDEV */
1921 if (getAL() == 0x00)
1924 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
1925 * This setting is ignored by MS-DOS 4.0+.
1926 * MS-DOS 5+ always return AL=00h/DL=2Fh.
1927 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
1928 * for more information.
1933 else if (getAL() == 0x01)
1936 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
1937 * This setting is ignored by MS-DOS 5+.
1938 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
1939 * for more information.
1944 else if (getAL() == 0x02)
1947 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1948 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1949 * for more information.
1954 else if (getAL() == 0x03)
1957 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1958 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1959 * for more information.
1966 /* Invalid subfunction */
1973 /* Create Directory */
1976 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1978 if (CreateDirectoryA(String
, NULL
))
1980 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1984 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1985 setAX(LOWORD(GetLastError()));
1991 /* Remove Directory */
1994 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1996 if (RemoveDirectoryA(String
))
1998 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2002 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2003 setAX(LOWORD(GetLastError()));
2009 /* Set Current Directory */
2012 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2014 if (DosChangeDirectory(String
))
2016 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2020 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2021 setAX(DosLastError
);
2027 /* Create or Truncate File */
2031 WORD ErrorCode
= DosCreateFile(&FileHandle
,
2032 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2036 if (ErrorCode
== ERROR_SUCCESS
)
2038 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2043 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2054 WORD ErrorCode
= DosOpenFile(&FileHandle
,
2055 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2058 if (ErrorCode
== ERROR_SUCCESS
)
2060 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2065 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2075 if (DosCloseHandle(getBX()))
2077 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2081 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2082 setAX(ERROR_INVALID_HANDLE
);
2088 /* Read from File or Device */
2094 DPRINT("INT 21h, AH = 3Fh\n");
2097 ErrorCode
= DosReadFile(getBX(),
2098 SEG_OFF_TO_PTR(getDS(), getDX()),
2103 if (ErrorCode
== ERROR_SUCCESS
)
2105 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2108 else if (ErrorCode
!= ERROR_NOT_READY
)
2110 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2117 /* Write to File or Device */
2120 WORD BytesWritten
= 0;
2121 WORD ErrorCode
= DosWriteFile(getBX(),
2122 SEG_OFF_TO_PTR(getDS(), getDX()),
2126 if (ErrorCode
== ERROR_SUCCESS
)
2128 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2129 setAX(BytesWritten
);
2133 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2143 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2145 if (demFileDelete(FileName
) == ERROR_SUCCESS
)
2147 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2149 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
2150 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
2152 setAL(FileName
[0] - 'A');
2156 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2157 setAX(GetLastError());
2167 WORD ErrorCode
= DosSeekFile(getBX(),
2168 MAKELONG(getDX(), getCX()),
2172 if (ErrorCode
== ERROR_SUCCESS
)
2174 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2176 /* Return the new offset in DX:AX */
2177 setDX(HIWORD(NewLocation
));
2178 setAX(LOWORD(NewLocation
));
2182 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2189 /* Get/Set File Attributes */
2193 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2195 if (getAL() == 0x00)
2197 /* Get the attributes */
2198 Attributes
= GetFileAttributesA(FileName
);
2200 /* Check if it failed */
2201 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
2203 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2204 setAX(GetLastError());
2208 /* Return the attributes that DOS can understand */
2209 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2210 setCX(Attributes
& 0x00FF);
2213 else if (getAL() == 0x01)
2215 /* Try to set the attributes */
2216 if (SetFileAttributesA(FileName
, getCL()))
2218 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2222 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2223 setAX(GetLastError());
2228 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2229 setAX(ERROR_INVALID_FUNCTION
);
2238 if (DosHandleIoctl(getAL(), getBX()))
2240 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2244 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2245 setAX(DosLastError
);
2251 /* Duplicate Handle */
2255 HANDLE Handle
= DosGetRealHandle(getBX());
2257 if (Handle
== INVALID_HANDLE_VALUE
)
2259 /* The handle is invalid */
2260 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2261 setAX(ERROR_INVALID_HANDLE
);
2265 /* Open a new handle to the same entry */
2266 NewHandle
= DosOpenHandle(Handle
);
2268 if (NewHandle
== INVALID_DOS_HANDLE
)
2270 /* Too many files open */
2271 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2272 setAX(ERROR_TOO_MANY_OPEN_FILES
);
2276 /* Return the result */
2277 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2282 /* Force Duplicate Handle */
2285 if (DosDuplicateHandle(getBX(), getCX()))
2287 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2291 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2292 setAX(ERROR_INVALID_HANDLE
);
2298 /* Get Current Directory */
2301 BYTE DriveNumber
= getDL();
2302 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
2304 /* Get the real drive number */
2305 if (DriveNumber
== 0)
2307 DriveNumber
= CurrentDrive
;
2311 /* Decrement DriveNumber since it was 1-based */
2315 if (DriveNumber
<= LastDrive
- 'A')
2318 * Copy the current directory into the target buffer.
2319 * It doesn't contain the drive letter and the backslash.
2321 strncpy(String
, CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
2322 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2323 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
2327 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2328 setAX(ERROR_INVALID_DRIVE
);
2334 /* Allocate Memory */
2337 WORD MaxAvailable
= 0;
2338 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
2342 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2347 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2348 setAX(DosLastError
);
2349 setBX(MaxAvailable
);
2358 if (DosFreeMemory(getES()))
2360 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2364 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2365 setAX(ERROR_ARENA_TRASHED
);
2371 /* Resize Memory Block */
2376 if (DosResizeMemory(getES(), getBX(), &Size
))
2378 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2382 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2383 setAX(DosLastError
);
2394 DOS_EXEC_TYPE LoadType
= (DOS_EXEC_TYPE
)getAL();
2395 LPSTR ProgramName
= SEG_OFF_TO_PTR(getDS(), getDX());
2396 PDOS_EXEC_PARAM_BLOCK ParamBlock
= SEG_OFF_TO_PTR(getES(), getBX());
2397 WORD ErrorCode
= DosCreateProcess(LoadType
, ProgramName
, ParamBlock
);
2399 if (ErrorCode
== ERROR_SUCCESS
)
2401 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2405 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2413 /* Terminate With Return Code */
2416 DosTerminateProcess(CurrentPsp
, getAL());
2420 /* Get Return Code (ERRORLEVEL) */
2424 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
2425 * DosErrorLevel is cleared after being read by this function.
2427 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2428 setAX(DosErrorLevel
);
2429 DosErrorLevel
= 0x0000; // Clear it
2433 /* Find First File */
2436 WORD Result
= (WORD
)demFileFindFirst(FAR_POINTER(DiskTransferArea
),
2437 SEG_OFF_TO_PTR(getDS(), getDX()),
2442 if (Result
== ERROR_SUCCESS
)
2443 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2445 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2450 /* Find Next File */
2453 WORD Result
= (WORD
)demFileFindNext(FAR_POINTER(DiskTransferArea
));
2457 if (Result
== ERROR_SUCCESS
)
2458 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2460 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2465 /* Internal - Set Current Process ID (Set PSP Address) */
2468 // FIXME: Is it really what it's done ??
2469 CurrentPsp
= getBX();
2473 /* Internal - Get Current Process ID (Get PSP Address) */
2475 /* Get Current PSP Address */
2479 * Undocumented AH=51h is identical to the documented AH=62h.
2480 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
2481 * and http://www.ctyme.com/intr/rb-3140.htm
2482 * for more information.
2488 /* Internal - Get "List of lists" (SYSVARS) */
2492 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
2493 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
2494 * for more information.
2497 /* Return the DOS "list of lists" in ES:BX */
2501 DisplayMessage(L
"Required for AARD code, do you remember? :P");
2508 LPSTR ExistingFileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2509 LPSTR NewFileName
= (LPSTR
)SEG_OFF_TO_PTR(getES(), getDI());
2512 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
2513 * for more information.
2516 if (MoveFileA(ExistingFileName
, NewFileName
))
2518 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2522 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2523 setAX(GetLastError());
2529 /* Get/Set Memory Management Options */
2532 if (getAL() == 0x00)
2534 /* Get allocation strategy */
2535 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2536 setAX(DosAllocStrategy
);
2538 else if (getAL() == 0x01)
2540 /* Set allocation strategy */
2542 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2543 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2545 /* Can't set both */
2546 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2547 setAX(ERROR_INVALID_PARAMETER
);
2551 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT
)
2553 /* Invalid allocation strategy */
2554 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2555 setAX(ERROR_INVALID_PARAMETER
);
2559 DosAllocStrategy
= getBL();
2560 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2562 else if (getAL() == 0x02)
2564 /* Get UMB link state */
2565 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2566 setAL(DosUmbLinked
? 0x01 : 0x00);
2568 else if (getAL() == 0x03)
2570 /* Set UMB link state */
2571 if (getBX()) DosLinkUmb();
2572 else DosUnlinkUmb();
2573 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2577 /* Invalid or unsupported function */
2578 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2579 setAX(ERROR_INVALID_FUNCTION
);
2585 /* Get Extended Error Information */
2588 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
2593 /* Create Temporary File */
2596 LPSTR PathName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2597 LPSTR FileName
= PathName
; // The buffer for the path and the full file name is the same.
2603 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
2604 * for more information.
2607 // FIXME: Check for buffer validity?
2608 // It should be a ASCIZ path ending with a '\' + 13 zero bytes
2609 // to receive the generated filename.
2611 /* First create the temporary file */
2612 uRetVal
= GetTempFileNameA(PathName
, NULL
, 0, FileName
);
2615 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2616 setAX(GetLastError());
2620 /* Now try to open it in read/write access */
2621 ErrorCode
= DosOpenFile(&FileHandle
, FileName
, 2);
2622 if (ErrorCode
== ERROR_SUCCESS
)
2624 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2629 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2636 /* Create New File */
2640 WORD ErrorCode
= DosCreateFile(&FileHandle
,
2641 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2645 if (ErrorCode
== ERROR_SUCCESS
)
2647 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2652 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2659 /* Lock/Unlock Region of File */
2662 HANDLE Handle
= DosGetRealHandle(getBX());
2664 if (Handle
== INVALID_HANDLE_VALUE
)
2666 /* The handle is invalid */
2667 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2668 setAX(ERROR_INVALID_HANDLE
);
2672 if (getAL() == 0x00)
2674 /* Lock region of file */
2675 if (LockFile(Handle
,
2676 MAKELONG(getCX(), getDX()), 0,
2677 MAKELONG(getSI(), getDI()), 0))
2679 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2683 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2684 setAX(GetLastError());
2687 else if (getAL() == 0x01)
2689 /* Unlock region of file */
2690 if (UnlockFile(Handle
,
2691 MAKELONG(getCX(), getDX()), 0,
2692 MAKELONG(getSI(), getDI()), 0))
2694 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2698 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2699 setAX(GetLastError());
2704 /* Invalid subfunction */
2705 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2706 setAX(ERROR_INVALID_FUNCTION
);
2712 /* Canonicalize File Name or Path */
2716 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
2717 * for more information.
2721 * We suppose that the DOS app gave to us a valid
2722 * 128-byte long buffer for the canonicalized name.
2724 DWORD dwRetVal
= GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
2726 SEG_OFF_TO_PTR(getES(), getDI()),
2730 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2731 setAX(GetLastError());
2735 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2739 // FIXME: Convert the full path name into short version.
2740 // We cannot reliably use GetShortPathName, because it fails
2741 // if the path name given doesn't exist. However this DOS
2742 // function AH=60h should be able to work even for non-existing
2743 // path and file names.
2748 /* Set Handle Count */
2751 if (!DosResizeHandleTable(getBX()))
2753 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2754 setAX(DosLastError
);
2756 else Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2766 * Function 6Ah is identical to function 68h,
2767 * and sets AH to 68h if success.
2768 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
2769 * for more information.
2773 if (DosFlushFileBuffers(getBX()))
2775 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2779 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2780 setAX(GetLastError());
2786 /* Extended Open/Create */
2790 WORD CreationStatus
;
2793 /* Check for AL == 00 */
2794 if (getAL() != 0x00)
2796 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2797 setAX(ERROR_INVALID_FUNCTION
);
2802 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
2803 * for the full detailed description.
2805 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
2808 ErrorCode
= DosCreateFileEx(&FileHandle
,
2810 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getSI()),
2815 if (ErrorCode
== ERROR_SUCCESS
)
2817 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2818 setCX(CreationStatus
);
2823 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2833 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2836 setAL(0); // Some functions expect AL to be 0 when it's not supported.
2837 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2842 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
2844 UNREFERENCED_PARAMETER(Stack
);
2846 /* Stop the VDM task */
2847 ResetEvent(VdmTaskEvent
);
2851 VOID WINAPI
DosFastConOut(LPWORD Stack
)
2854 * This is the DOS 2+ Fast Console Output Interrupt.
2855 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
2857 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2858 * for more information.
2861 /* Save AX and BX */
2862 USHORT AX
= getAX();
2863 USHORT BX
= getBX();
2866 * Set the parameters:
2867 * AL contains the character to print (already set),
2868 * BL contains the character attribute,
2869 * BH contains the video page to use.
2871 setBL(DOS_CHAR_ATTRIBUTE
);
2872 setBH(Bda
->VideoPage
);
2874 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2876 Int32Call(&DosContext
, BIOS_VIDEO_INTERRUPT
);
2878 /* Restore AX and BX */
2883 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
2885 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2887 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2890 BOOLEAN
DosKRNLInitialize(VOID
)
2896 CHAR CurrentDirectory
[MAX_PATH
];
2897 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2903 /* Clear the current directory buffer */
2904 ZeroMemory(CurrentDirectories
, sizeof(CurrentDirectories
));
2906 /* Get the current directory */
2907 if (!GetCurrentDirectoryA(MAX_PATH
, CurrentDirectory
))
2909 // TODO: Use some kind of default path?
2913 /* Convert that to a DOS path */
2914 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, DOS_DIR_LENGTH
))
2916 // TODO: Use some kind of default path?
2921 CurrentDrive
= DosDirectory
[0] - 'A';
2923 /* Get the directory part of the path */
2924 Path
= strchr(DosDirectory
, '\\');
2927 /* Skip the backslash */
2931 /* Set the directory */
2934 strncpy(CurrentDirectories
[CurrentDrive
], Path
, DOS_DIR_LENGTH
);
2937 /* Read CONFIG.SYS */
2938 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
2941 while (fgetws(Buffer
, 256, Stream
))
2943 // TODO: Parse the line
2948 /* Initialize the SFT */
2949 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
2951 DosSystemFileTable
[i
].Handle
= INVALID_HANDLE_VALUE
;
2952 DosSystemFileTable
[i
].RefCount
= 0;
2955 /* Get handles to standard I/O devices */
2956 DosSystemFileTable
[0].Handle
= GetStdHandle(STD_INPUT_HANDLE
);
2957 DosSystemFileTable
[1].Handle
= GetStdHandle(STD_OUTPUT_HANDLE
);
2958 DosSystemFileTable
[2].Handle
= GetStdHandle(STD_ERROR_HANDLE
);
2960 /* Initialize the reference counts */
2961 DosSystemFileTable
[0].RefCount
=
2962 DosSystemFileTable
[1].RefCount
=
2963 DosSystemFileTable
[2].RefCount
= 1;
2967 /* Initialize the callback context */
2968 InitializeContext(&DosContext
, 0x0070, 0x0000);
2970 /* Register the DOS 32-bit Interrupts */
2971 RegisterDosInt32(0x20, DosInt20h
);
2972 RegisterDosInt32(0x21, DosInt21h
);
2973 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2974 RegisterDosInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
2975 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2976 RegisterDosInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
2977 RegisterDosInt32(0x2F, DosInt2Fh
);