2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: VDM DOS Kernel
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
19 #include "registers.h"
21 /* PRIVATE VARIABLES **********************************************************/
23 static WORD CurrentPsp
= SYSTEM_PSP
;
24 static WORD DosLastError
= 0;
25 static DWORD DiskTransferArea
;
26 static BYTE CurrentDrive
;
27 static CHAR LastDrive
= 'E';
28 static CHAR CurrentDirectories
[NUM_DRIVES
][DOS_DIR_LENGTH
];
29 static HANDLE DosSystemFileTable
[DOS_SFT_SIZE
];
30 static WORD DosSftRefCount
[DOS_SFT_SIZE
];
31 static BYTE DosAllocStrategy
= DOS_ALLOC_BEST_FIT
;
32 static BOOLEAN DosUmbLinked
= FALSE
;
33 static WORD DosErrorLevel
= 0x0000;
36 #define BOP_DOS 0x50 // DOS System BOP (for NTIO.SYS and NTDOS.SYS)
37 #define BOP_CMD 0x54 // DOS Command Interpreter BOP (for COMMAND.COM)
39 /* PRIVATE FUNCTIONS **********************************************************/
41 /* Taken from base/shell/cmd/console.c */
42 static BOOL
IsConsoleHandle(HANDLE hHandle
)
46 /* Check whether the handle may be that of a console... */
47 if ((GetFileType(hHandle
) & FILE_TYPE_CHAR
) == 0) return FALSE
;
50 * It may be. Perform another test... The idea comes from the
51 * MSDN description of the WriteConsole API:
53 * "WriteConsole fails if it is used with a standard handle
54 * that is redirected to a file. If an application processes
55 * multilingual output that can be redirected, determine whether
56 * the output handle is a console handle (one method is to call
57 * the GetConsoleMode function and check whether it succeeds).
58 * If the handle is a console handle, call WriteConsole. If the
59 * handle is not a console handle, the output is redirected and
60 * you should call WriteFile to perform the I/O."
62 return GetConsoleMode(hHandle
, &dwMode
);
65 static VOID
DosCombineFreeBlocks(WORD StartBlock
)
67 PDOS_MCB CurrentMcb
= SEGMENT_TO_MCB(StartBlock
), NextMcb
;
69 /* If this is the last block or it's not free, quit */
70 if (CurrentMcb
->BlockType
== 'Z' || CurrentMcb
->OwnerPsp
!= 0) return;
74 /* Get a pointer to the next MCB */
75 NextMcb
= SEGMENT_TO_MCB(StartBlock
+ CurrentMcb
->Size
+ 1);
77 /* Check if the next MCB is free */
78 if (NextMcb
->OwnerPsp
== 0)
81 CurrentMcb
->Size
+= NextMcb
->Size
+ 1;
82 CurrentMcb
->BlockType
= NextMcb
->BlockType
;
83 NextMcb
->BlockType
= 'I';
87 /* No more adjoining free blocks */
93 static WORD
DosCopyEnvironmentBlock(WORD SourceSegment
, LPCSTR ProgramName
)
95 PCHAR Ptr
, SourceBuffer
, DestBuffer
= NULL
;
99 Ptr
= SourceBuffer
= (PCHAR
)SEG_OFF_TO_PTR(SourceSegment
, 0);
101 /* Calculate the size of the environment block */
104 TotalSize
+= strlen(Ptr
) + 1;
105 Ptr
+= strlen(Ptr
) + 1;
109 /* Add the string buffer size */
110 TotalSize
+= strlen(ProgramName
) + 1;
112 /* Allocate the memory for the environment block */
113 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
114 if (!DestSegment
) return 0;
118 DestBuffer
= (PCHAR
)SEG_OFF_TO_PTR(DestSegment
, 0);
121 /* Copy the string */
122 strcpy(DestBuffer
, Ptr
);
124 /* Advance to the next string */
125 DestBuffer
+= strlen(Ptr
);
126 Ptr
+= strlen(Ptr
) + 1;
128 /* Put a zero after the string */
132 /* Set the final zero */
135 /* Copy the program name after the environment block */
136 strcpy(DestBuffer
, ProgramName
);
141 static VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
143 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
145 /* Just set the owner */
146 Mcb
->OwnerPsp
= NewOwner
;
149 static WORD
DosOpenHandle(HANDLE Handle
)
156 /* The system PSP has no handle table */
157 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
159 /* Get a pointer to the handle table */
160 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
161 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
163 /* Find a free entry in the JFT */
164 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
166 if (HandleTable
[DosHandle
] == 0xFF) break;
169 /* If there are no free entries, fail */
170 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
172 /* Check if the handle is already in the SFT */
173 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
175 /* Check if this is the same handle */
176 if (DosSystemFileTable
[i
] != Handle
) continue;
178 /* Already in the table, reference it */
181 /* Set the JFT entry to that SFT index */
182 HandleTable
[DosHandle
] = i
;
184 /* Return the new handle */
188 /* Add the handle to the SFT */
189 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
191 /* Make sure this is an empty table entry */
192 if (DosSystemFileTable
[i
] != INVALID_HANDLE_VALUE
) continue;
194 /* Initialize the empty table entry */
195 DosSystemFileTable
[i
] = Handle
;
196 DosSftRefCount
[i
] = 1;
198 /* Set the JFT entry to that SFT index */
199 HandleTable
[DosHandle
] = i
;
201 /* Return the new handle */
205 /* The SFT is full */
206 return INVALID_DOS_HANDLE
;
209 static HANDLE
DosGetRealHandle(WORD DosHandle
)
214 /* The system PSP has no handle table */
215 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_HANDLE_VALUE
;
217 /* Get a pointer to the handle table */
218 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
219 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
221 /* Make sure the handle is open */
222 if (HandleTable
[DosHandle
] == 0xFF) return INVALID_HANDLE_VALUE
;
224 /* Return the Win32 handle */
225 return DosSystemFileTable
[HandleTable
[DosHandle
]];
228 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
234 /* Clear the table first */
235 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
237 /* Check if this is the initial process */
238 if (CurrentPsp
== SYSTEM_PSP
)
240 /* Set up the standard I/O devices */
241 for (i
= 0; i
<= 2; i
++)
243 /* Set the index in the SFT */
244 DestinationTable
[i
] = (BYTE
)i
;
246 /* Increase the reference count */
254 /* Get the parent PSP block and handle table */
255 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
256 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
258 /* Copy the first 20 handles into the new table */
259 for (i
= 0; i
< 20; i
++)
261 DestinationTable
[i
] = SourceTable
[i
];
263 /* Increase the reference count */
264 DosSftRefCount
[SourceTable
[i
]]++;
268 /* PUBLIC FUNCTIONS ***********************************************************/
270 WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
272 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
273 PDOS_MCB CurrentMcb
, NextMcb
;
274 BOOLEAN SearchUmb
= FALSE
;
276 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size
);
278 if (DosUmbLinked
&& (DosAllocStrategy
& (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
)))
280 /* Search UMB first */
281 Segment
= UMB_START_SEGMENT
;
287 /* Get a pointer to the MCB */
288 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
290 /* Make sure it's valid */
291 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
293 DPRINT("The DOS memory arena is corrupted!\n");
294 DosLastError
= ERROR_ARENA_TRASHED
;
298 /* Only check free blocks */
299 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
301 /* Combine this free block with adjoining free blocks */
302 DosCombineFreeBlocks(Segment
);
304 /* Update the maximum block size */
305 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
307 /* Check if this block is big enough */
308 if (CurrentMcb
->Size
< Size
) goto Next
;
310 switch (DosAllocStrategy
& 0x3F)
312 case DOS_ALLOC_FIRST_FIT
:
314 /* For first fit, stop immediately */
319 case DOS_ALLOC_BEST_FIT
:
321 /* For best fit, update the smallest block found so far */
322 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
330 case DOS_ALLOC_LAST_FIT
:
332 /* For last fit, make the current block the result, but keep searching */
339 /* If this was the last MCB in the chain, quit */
340 if (CurrentMcb
->BlockType
== 'Z')
342 /* Check if nothing was found while searching through UMBs */
343 if ((Result
== 0) && SearchUmb
&& (DosAllocStrategy
& DOS_ALLOC_HIGH_LOW
))
345 /* Search low memory */
346 Segment
= FIRST_MCB_SEGMENT
;
353 /* Otherwise, update the segment and continue */
354 Segment
+= CurrentMcb
->Size
+ 1;
359 /* If we didn't find a free block, return 0 */
362 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
363 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
367 /* Get a pointer to the MCB */
368 CurrentMcb
= SEGMENT_TO_MCB(Result
);
370 /* Check if the block is larger than requested */
371 if (CurrentMcb
->Size
> Size
)
373 /* It is, split it into two blocks */
374 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
376 /* Initialize the new MCB structure */
377 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
378 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
379 NextMcb
->OwnerPsp
= 0;
381 /* Update the current block */
382 CurrentMcb
->BlockType
= 'M';
383 CurrentMcb
->Size
= Size
;
386 /* Take ownership of the block */
387 CurrentMcb
->OwnerPsp
= CurrentPsp
;
389 /* Return the segment of the data portion of the block */
393 BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
395 BOOLEAN Success
= TRUE
;
396 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
397 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
399 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
403 /* Make sure this is a valid, allocated block */
404 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
407 DosLastError
= ERROR_INVALID_HANDLE
;
411 ReturnSize
= Mcb
->Size
;
413 /* Check if we need to expand or contract the block */
414 if (NewSize
> Mcb
->Size
)
416 /* We can't expand the last block */
417 if (Mcb
->BlockType
!= 'M')
423 /* Get the pointer and segment of the next MCB */
424 NextSegment
= Segment
+ Mcb
->Size
+ 1;
425 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
427 /* Make sure the next segment is free */
428 if (NextMcb
->OwnerPsp
!= 0)
430 DPRINT("Cannot expand memory block: next segment is not free!\n");
431 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
436 /* Combine this free block with adjoining free blocks */
437 DosCombineFreeBlocks(NextSegment
);
439 /* Set the maximum possible size of the block */
440 ReturnSize
+= NextMcb
->Size
+ 1;
442 /* Maximize the current block */
443 Mcb
->Size
= ReturnSize
;
444 Mcb
->BlockType
= NextMcb
->BlockType
;
446 /* Invalidate the next block */
447 NextMcb
->BlockType
= 'I';
449 /* Check if the block is larger than requested */
450 if (Mcb
->Size
> NewSize
)
452 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
456 /* It is, split it into two blocks */
457 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
459 /* Initialize the new MCB structure */
460 NextMcb
->BlockType
= Mcb
->BlockType
;
461 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
462 NextMcb
->OwnerPsp
= 0;
464 /* Update the current block */
465 Mcb
->BlockType
= 'M';
469 else if (NewSize
< Mcb
->Size
)
471 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
475 /* Just split the block */
476 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
477 NextMcb
->BlockType
= Mcb
->BlockType
;
478 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
479 NextMcb
->OwnerPsp
= 0;
482 Mcb
->BlockType
= 'M';
487 /* Check if the operation failed */
490 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
493 /* Return the maximum possible size */
494 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
500 BOOLEAN
DosFreeMemory(WORD BlockData
)
502 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
504 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData
);
506 /* Make sure the MCB is valid */
507 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z')
509 DPRINT("MCB block type '%c' not valid!\n", Mcb
->BlockType
);
513 /* Mark the block as free */
519 BOOLEAN
DosLinkUmb(VOID
)
521 DWORD Segment
= FIRST_MCB_SEGMENT
;
522 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
524 DPRINT("Linking UMB\n");
526 /* Check if UMBs are already linked */
527 if (DosUmbLinked
) return FALSE
;
529 /* Find the last block */
530 while ((Mcb
->BlockType
== 'M') && (Segment
<= 0xFFFF))
532 Segment
+= Mcb
->Size
+ 1;
533 Mcb
= SEGMENT_TO_MCB(Segment
);
536 /* Make sure it's valid */
537 if (Mcb
->BlockType
!= 'Z') return FALSE
;
539 /* Connect the MCB with the UMB chain */
540 Mcb
->BlockType
= 'M';
546 BOOLEAN
DosUnlinkUmb(VOID
)
548 DWORD Segment
= FIRST_MCB_SEGMENT
;
549 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
551 DPRINT("Unlinking UMB\n");
553 /* Check if UMBs are already unlinked */
554 if (!DosUmbLinked
) return FALSE
;
556 /* Find the block preceding the MCB that links it with the UMB chain */
557 while (Segment
<= 0xFFFF)
559 if ((Segment
+ Mcb
->Size
) == (FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
))
561 /* This is the last non-UMB segment */
565 /* Advance to the next MCB */
566 Segment
+= Mcb
->Size
+ 1;
567 Mcb
= SEGMENT_TO_MCB(Segment
);
570 /* Mark the MCB as the last MCB */
571 Mcb
->BlockType
= 'Z';
573 DosUmbLinked
= FALSE
;
577 WORD
DosCreateFile(LPWORD Handle
, LPCSTR FilePath
, WORD Attributes
)
582 DPRINT("DosCreateFile: FilePath \"%s\", Attributes 0x%04X\n",
586 /* Create the file */
587 FileHandle
= CreateFileA(FilePath
,
588 GENERIC_READ
| GENERIC_WRITE
,
589 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
595 if (FileHandle
== INVALID_HANDLE_VALUE
)
597 /* Return the error code */
598 return (WORD
)GetLastError();
601 /* Open the DOS handle */
602 DosHandle
= DosOpenHandle(FileHandle
);
604 if (DosHandle
== INVALID_DOS_HANDLE
)
606 /* Close the handle */
607 CloseHandle(FileHandle
);
609 /* Return the error code */
610 return ERROR_TOO_MANY_OPEN_FILES
;
613 /* It was successful */
615 return ERROR_SUCCESS
;
618 WORD
DosOpenFile(LPWORD Handle
, LPCSTR FilePath
, BYTE AccessMode
)
621 ACCESS_MASK Access
= 0;
624 DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n",
628 /* Parse the access mode */
629 switch (AccessMode
& 3)
634 Access
= GENERIC_READ
;
641 Access
= GENERIC_WRITE
;
648 Access
= GENERIC_READ
| GENERIC_WRITE
;
655 return ERROR_INVALID_PARAMETER
;
660 FileHandle
= CreateFileA(FilePath
,
662 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
665 FILE_ATTRIBUTE_NORMAL
,
668 if (FileHandle
== INVALID_HANDLE_VALUE
)
670 /* Return the error code */
671 return (WORD
)GetLastError();
674 /* Open the DOS handle */
675 DosHandle
= DosOpenHandle(FileHandle
);
677 if (DosHandle
== INVALID_DOS_HANDLE
)
679 /* Close the handle */
680 CloseHandle(FileHandle
);
682 /* Return the error code */
683 return ERROR_TOO_MANY_OPEN_FILES
;
686 /* It was successful */
688 return ERROR_SUCCESS
;
691 WORD
DosReadFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesRead
)
693 WORD Result
= ERROR_SUCCESS
;
694 DWORD BytesRead32
= 0;
695 HANDLE Handle
= DosGetRealHandle(FileHandle
);
697 DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle
, Count
);
699 /* Make sure the handle is valid */
700 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
703 if (!ReadFile(Handle
, Buffer
, Count
, &BytesRead32
, NULL
))
705 /* Store the error code */
706 Result
= (WORD
)GetLastError();
709 /* The number of bytes read is always 16-bit */
710 *BytesRead
= LOWORD(BytesRead32
);
712 /* Return the error code */
716 WORD
DosWriteFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesWritten
)
718 WORD Result
= ERROR_SUCCESS
;
719 DWORD BytesWritten32
= 0;
720 HANDLE Handle
= DosGetRealHandle(FileHandle
);
723 DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n",
727 /* Make sure the handle is valid */
728 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
730 if (IsConsoleHandle(Handle
))
732 for (i
= 0; i
< Count
; i
++)
734 /* Call the BIOS to print the character */
735 BiosPrintCharacter(((LPBYTE
)Buffer
)[i
], DOS_CHAR_ATTRIBUTE
, Bda
->VideoPage
);
742 if (!WriteFile(Handle
, Buffer
, Count
, &BytesWritten32
, NULL
))
744 /* Store the error code */
745 Result
= (WORD
)GetLastError();
749 /* The number of bytes written is always 16-bit */
750 *BytesWritten
= LOWORD(BytesWritten32
);
752 /* Return the error code */
756 WORD
DosSeekFile(WORD FileHandle
, LONG Offset
, BYTE Origin
, LPDWORD NewOffset
)
758 WORD Result
= ERROR_SUCCESS
;
760 HANDLE Handle
= DosGetRealHandle(FileHandle
);
762 DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n",
767 /* Make sure the handle is valid */
768 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
770 /* Check if the origin is valid */
771 if (Origin
!= FILE_BEGIN
&& Origin
!= FILE_CURRENT
&& Origin
!= FILE_END
)
773 return ERROR_INVALID_FUNCTION
;
776 /* Move the file pointer */
777 FilePointer
= SetFilePointer(Handle
, Offset
, NULL
, Origin
);
779 /* Check if there's a possibility the operation failed */
780 if (FilePointer
== INVALID_SET_FILE_POINTER
)
782 /* Get the real error code */
783 Result
= (WORD
)GetLastError();
786 if (Result
!= ERROR_SUCCESS
)
788 /* The operation did fail */
792 /* Return the file pointer, if requested */
793 if (NewOffset
) *NewOffset
= FilePointer
;
796 return ERROR_SUCCESS
;
799 BOOLEAN
DosFlushFileBuffers(WORD FileHandle
)
801 HANDLE Handle
= DosGetRealHandle(FileHandle
);
803 /* Make sure the handle is valid */
804 if (Handle
== INVALID_HANDLE_VALUE
) return FALSE
;
807 * No need to check whether the handle is a console handle since
808 * FlushFileBuffers() automatically does this check and calls
809 * FlushConsoleInputBuffer() for us.
811 // if (IsConsoleHandle(Handle))
812 // return (BOOLEAN)FlushConsoleInputBuffer(Handle);
814 return (BOOLEAN
)FlushFileBuffers(Handle
);
817 BOOLEAN
DosDuplicateHandle(WORD OldHandle
, WORD NewHandle
)
823 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
827 /* The system PSP has no handle table */
828 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
830 /* Get a pointer to the handle table */
831 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
832 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
834 /* Make sure the old handle is open */
835 if (HandleTable
[OldHandle
] == 0xFF) return FALSE
;
837 /* Check if the new handle is open */
838 if (HandleTable
[NewHandle
] != 0xFF)
841 DosCloseHandle(NewHandle
);
844 /* Increment the reference count of the SFT entry */
845 SftIndex
= HandleTable
[OldHandle
];
846 DosSftRefCount
[SftIndex
]++;
848 /* Make the new handle point to that SFT entry */
849 HandleTable
[NewHandle
] = SftIndex
;
855 BOOLEAN
DosCloseHandle(WORD DosHandle
)
861 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle
);
863 /* The system PSP has no handle table */
864 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
866 /* Get a pointer to the handle table */
867 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
868 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
870 /* Make sure the handle is open */
871 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
873 /* Decrement the reference count of the SFT entry */
874 SftIndex
= HandleTable
[DosHandle
];
875 DosSftRefCount
[SftIndex
]--;
877 /* Check if the reference count fell to zero */
878 if (!DosSftRefCount
[SftIndex
])
880 /* Close the file, it's no longer needed */
881 CloseHandle(DosSystemFileTable
[SftIndex
]);
883 /* Clear the handle */
884 DosSystemFileTable
[SftIndex
] = INVALID_HANDLE_VALUE
;
887 /* Clear the entry in the JFT */
888 HandleTable
[DosHandle
] = 0xFF;
893 BOOLEAN
DosChangeDrive(BYTE Drive
)
895 WCHAR DirectoryPath
[DOS_CMDLINE_LENGTH
];
897 /* Make sure the drive exists */
898 if (Drive
> (LastDrive
- 'A')) return FALSE
;
900 /* Find the path to the new current directory */
901 swprintf(DirectoryPath
, L
"%c\\%S", Drive
+ 'A', CurrentDirectories
[Drive
]);
903 /* Change the current directory of the process */
904 if (!SetCurrentDirectory(DirectoryPath
)) return FALSE
;
906 /* Set the current drive */
907 CurrentDrive
= Drive
;
913 BOOLEAN
DosChangeDirectory(LPSTR Directory
)
919 /* Make sure the directory path is not too long */
920 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
922 DosLastError
= ERROR_PATH_NOT_FOUND
;
926 /* Get the drive number */
927 DriveNumber
= Directory
[0] - 'A';
929 /* Make sure the drive exists */
930 if (DriveNumber
> (LastDrive
- 'A'))
932 DosLastError
= ERROR_PATH_NOT_FOUND
;
936 /* Get the file attributes */
937 Attributes
= GetFileAttributesA(Directory
);
939 /* Make sure the path exists and is a directory */
940 if ((Attributes
== INVALID_FILE_ATTRIBUTES
)
941 || !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
943 DosLastError
= ERROR_PATH_NOT_FOUND
;
947 /* Check if this is the current drive */
948 if (DriveNumber
== CurrentDrive
)
950 /* Change the directory */
951 if (!SetCurrentDirectoryA(Directory
))
953 DosLastError
= LOWORD(GetLastError());
958 /* Get the directory part of the path */
959 Path
= strchr(Directory
, '\\');
962 /* Skip the backslash */
966 /* Set the directory for the drive */
969 strncpy(CurrentDirectories
[DriveNumber
], Path
, DOS_DIR_LENGTH
);
973 CurrentDirectories
[DriveNumber
][0] = '\0';
980 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
982 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
983 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
985 ZeroMemory(PspBlock
, sizeof(DOS_PSP
));
987 /* Set the exit interrupt */
988 PspBlock
->Exit
[0] = 0xCD; // int 0x20
989 PspBlock
->Exit
[1] = 0x20;
991 /* Set the number of the last paragraph */
992 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
994 /* Save the interrupt vectors */
995 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
996 PspBlock
->BreakAddress
= IntVecTable
[0x23];
997 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
999 /* Set the parent PSP */
1000 PspBlock
->ParentPsp
= CurrentPsp
;
1002 /* Copy the parent handle table */
1003 DosCopyHandleTable(PspBlock
->HandleTable
);
1005 /* Set the environment block */
1006 PspBlock
->EnvBlock
= Environment
;
1008 /* Set the handle table pointers to the internal handle table */
1009 PspBlock
->HandleTableSize
= 20;
1010 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
1012 /* Set the DOS version */
1013 PspBlock
->DosVersion
= DOS_VERSION
;
1015 /* Set the far call opcodes */
1016 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
1017 PspBlock
->FarCall
[1] = 0x21;
1018 PspBlock
->FarCall
[2] = 0xCB; // retf
1020 /* Set the command line */
1021 PspBlock
->CommandLineSize
= (BYTE
)min(strlen(CommandLine
), DOS_CMDLINE_LENGTH
- 1);
1022 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
1023 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
1026 BOOLEAN
DosCreateProcess(LPCSTR CommandLine
, WORD EnvBlock
)
1028 BOOLEAN Success
= FALSE
, AllocatedEnvBlock
= FALSE
;
1029 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
1030 LPBYTE Address
= NULL
;
1031 LPSTR ProgramFilePath
, Parameters
[256];
1032 CHAR CommandLineCopy
[DOS_CMDLINE_LENGTH
];
1033 CHAR ParamString
[DOS_CMDLINE_LENGTH
];
1037 DWORD i
, FileSize
, ExeSize
;
1038 PIMAGE_DOS_HEADER Header
;
1039 PDWORD RelocationTable
;
1042 DPRINT("DosCreateProcess: CommandLine \"%s\", EnvBlock 0x%04X\n",
1046 /* Save a copy of the command line */
1047 strcpy(CommandLineCopy
, CommandLine
);
1049 /* Get the file name of the executable */
1050 ProgramFilePath
= strtok(CommandLineCopy
, " \t");
1052 /* Load the parameters in the local array */
1053 while ((ParamCount
< sizeof(Parameters
)/sizeof(Parameters
[0]))
1054 && ((Parameters
[ParamCount
] = strtok(NULL
, " \t")) != NULL
))
1059 ZeroMemory(ParamString
, sizeof(ParamString
));
1061 /* Store the parameters in a string */
1062 for (i
= 0; i
< ParamCount
; i
++)
1064 strncat(ParamString
, Parameters
[i
], DOS_CMDLINE_LENGTH
- strlen(ParamString
) - 1);
1065 strncat(ParamString
, " ", DOS_CMDLINE_LENGTH
- strlen(ParamString
) - 1);
1068 /* Open a handle to the executable */
1069 FileHandle
= CreateFileA(ProgramFilePath
,
1074 FILE_ATTRIBUTE_NORMAL
,
1076 if (FileHandle
== INVALID_HANDLE_VALUE
) goto Cleanup
;
1078 /* Get the file size */
1079 FileSize
= GetFileSize(FileHandle
, NULL
);
1081 /* Create a mapping object for the file */
1082 FileMapping
= CreateFileMapping(FileHandle
,
1088 if (FileMapping
== NULL
) goto Cleanup
;
1090 /* Map the file into memory */
1091 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
1092 if (Address
== NULL
) goto Cleanup
;
1094 /* Did we get an environment segment? */
1097 /* Set a flag to know if the environment block was allocated here */
1098 AllocatedEnvBlock
= TRUE
;
1100 /* No, copy the one from the parent */
1101 EnvBlock
= DosCopyEnvironmentBlock((CurrentPsp
!= SYSTEM_PSP
)
1102 ? SEGMENT_TO_PSP(CurrentPsp
)->EnvBlock
1107 /* Check if this is an EXE file or a COM file */
1108 if (Address
[0] == 'M' && Address
[1] == 'Z')
1112 /* Get the MZ header */
1113 Header
= (PIMAGE_DOS_HEADER
)Address
;
1115 /* Get the base size of the file, in paragraphs (rounded up) */
1116 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
1118 /* Add the PSP size, in paragraphs */
1119 ExeSize
+= sizeof(DOS_PSP
) >> 4;
1121 /* Add the maximum size that should be allocated */
1122 ExeSize
+= Header
->e_maxalloc
;
1124 /* Make sure it does not pass 0xFFFF */
1125 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
1127 /* Reduce the size one by one until the allocation is successful */
1128 for (i
= Header
->e_maxalloc
; i
>= Header
->e_minalloc
; i
--, ExeSize
--)
1130 /* Try to allocate that much memory */
1131 Segment
= DosAllocateMemory((WORD
)ExeSize
, NULL
);
1132 if (Segment
!= 0) break;
1135 /* Check if at least the lowest allocation was successful */
1136 if (Segment
== 0) goto Cleanup
;
1138 /* Initialize the PSP */
1139 DosInitializePsp(Segment
,
1144 /* The process owns its own memory */
1145 DosChangeMemoryOwner(Segment
, Segment
);
1146 DosChangeMemoryOwner(EnvBlock
, Segment
);
1148 /* Copy the program to Segment:0100 */
1149 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
1150 Address
+ (Header
->e_cparhdr
<< 4),
1151 min(FileSize
- (Header
->e_cparhdr
<< 4),
1152 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
1154 /* Get the relocation table */
1155 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
1157 /* Perform relocations */
1158 for (i
= 0; i
< Header
->e_crlc
; i
++)
1160 /* Get a pointer to the word that needs to be patched */
1161 RelocWord
= (PWORD
)SEG_OFF_TO_PTR(Segment
+ HIWORD(RelocationTable
[i
]),
1162 0x100 + LOWORD(RelocationTable
[i
]));
1164 /* Add the number of the EXE segment to it */
1165 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
1168 /* Set the initial segment registers */
1172 /* Set the stack to the location from the header */
1173 EmulatorSetStack(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
,
1177 CurrentPsp
= Segment
;
1178 DiskTransferArea
= MAKELONG(0x80, Segment
);
1179 EmulatorExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
1188 /* Find the maximum amount of memory that can be allocated */
1189 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
1191 /* Make sure it's enough for the whole program and the PSP */
1192 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
))) goto Cleanup
;
1194 /* Allocate all of it */
1195 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
1196 if (Segment
== 0) goto Cleanup
;
1198 /* The process owns its own memory */
1199 DosChangeMemoryOwner(Segment
, Segment
);
1200 DosChangeMemoryOwner(EnvBlock
, Segment
);
1202 /* Copy the program to Segment:0100 */
1203 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
1207 /* Initialize the PSP */
1208 DosInitializePsp(Segment
,
1213 /* Set the initial segment registers */
1217 /* Set the stack to the last word of the segment */
1218 EmulatorSetStack(Segment
, 0xFFFE);
1221 * Set the value on the stack to 0, so that a near return
1222 * jumps to PSP:0000 which has the exit code.
1224 *((LPWORD
)SEG_OFF_TO_PTR(Segment
, 0xFFFE)) = 0;
1227 CurrentPsp
= Segment
;
1228 DiskTransferArea
= MAKELONG(0x80, Segment
);
1229 EmulatorExecute(Segment
, 0x100);
1237 /* It was not successful, cleanup the DOS memory */
1238 if (AllocatedEnvBlock
) DosFreeMemory(EnvBlock
);
1239 if (Segment
) DosFreeMemory(Segment
);
1243 if (Address
!= NULL
) UnmapViewOfFile(Address
);
1245 /* Close the file mapping object */
1246 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
1248 /* Close the file handle */
1249 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
1254 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
)
1257 WORD McbSegment
= FIRST_MCB_SEGMENT
;
1258 PDOS_MCB CurrentMcb
;
1259 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
1260 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
1262 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
1266 /* Check if this PSP is it's own parent */
1267 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
1269 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
1271 /* Close the handle */
1275 /* Free the memory used by the process */
1278 /* Get a pointer to the MCB */
1279 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
1281 /* Make sure the MCB is valid */
1282 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!='Z') break;
1284 /* If this block was allocated by the process, free it */
1285 if (CurrentMcb
->OwnerPsp
== Psp
) DosFreeMemory(McbSegment
+ 1);
1287 /* If this was the last block, quit */
1288 if (CurrentMcb
->BlockType
== 'Z') break;
1290 /* Update the segment and continue */
1291 McbSegment
+= CurrentMcb
->Size
+ 1;
1295 /* Restore the interrupt vectors */
1296 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
1297 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
1298 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
1300 /* Update the current PSP */
1301 if (Psp
== CurrentPsp
)
1303 CurrentPsp
= PspBlock
->ParentPsp
;
1304 if (CurrentPsp
== SYSTEM_PSP
) VdmRunning
= FALSE
;
1307 /* Save the return code - Normal termination */
1308 DosErrorLevel
= MAKEWORD(ReturnCode
, 0x00);
1310 /* Return control to the parent process */
1311 EmulatorExecute(HIWORD(PspBlock
->TerminateAddress
),
1312 LOWORD(PspBlock
->TerminateAddress
));
1315 CHAR
DosReadCharacter(VOID
)
1317 CHAR Character
= '\0';
1320 if (IsConsoleHandle(DosGetRealHandle(DOS_INPUT_HANDLE
)))
1323 Character
= LOBYTE(BiosGetCharacter());
1327 /* Use the file reading function */
1328 DosReadFile(DOS_INPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesRead
);
1334 BOOLEAN
DosCheckInput(VOID
)
1336 HANDLE Handle
= DosGetRealHandle(DOS_INPUT_HANDLE
);
1338 if (IsConsoleHandle(Handle
))
1341 return (BiosPeekCharacter() != 0xFFFF);
1346 DWORD FileSize
= GetFileSize(Handle
, &FileSizeHigh
);
1347 LONG LocationHigh
= 0;
1348 DWORD Location
= SetFilePointer(Handle
, 0, &LocationHigh
, FILE_CURRENT
);
1350 return ((Location
!= FileSize
) || (LocationHigh
!= FileSizeHigh
));
1354 VOID
DosPrintCharacter(CHAR Character
)
1358 /* Use the file writing function */
1359 DosWriteFile(DOS_OUTPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesWritten
);
1362 BOOLEAN
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
1364 HANDLE Handle
= DosGetRealHandle(FileHandle
);
1366 if (Handle
== INVALID_HANDLE_VALUE
)
1369 DosLastError
= ERROR_FILE_NOT_FOUND
;
1373 switch (ControlCode
)
1375 /* Get Device Information */
1381 * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
1382 * for a list of possible flags.
1385 if (Handle
== DosSystemFileTable
[0])
1390 else if (Handle
== DosSystemFileTable
[1])
1392 /* Console output */
1396 /* It is a device */
1399 /* Return the device information word */
1404 /* Unsupported control code */
1407 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
1409 DosLastError
= ERROR_INVALID_PARAMETER
;
1415 VOID WINAPI
DosSystemBop(LPWORD Stack
)
1417 /* Get the Function Number and skip it */
1418 BYTE FuncNum
= *(PBYTE
)SEG_OFF_TO_PTR(getCS(), getIP());
1421 DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum
);
1424 VOID WINAPI
DosCmdInterpreterBop(LPWORD Stack
)
1426 /* Get the Function Number and skip it */
1427 BYTE FuncNum
= *(PBYTE
)SEG_OFF_TO_PTR(getCS(), getIP());
1432 case 0x08: // Launch external command
1434 #define CMDLINE_LENGTH 1024
1439 LPSTR Command
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getSI());
1440 CHAR CommandLine
[CMDLINE_LENGTH
] = "";
1441 STARTUPINFOA StartupInfo
;
1442 PROCESS_INFORMATION ProcessInformation
;
1443 DPRINT1("CMD Run Command '%s'\n", Command
);
1445 Command
[strlen(Command
)-1] = 0;
1447 strcpy(CommandLine
, "cmd.exe /c ");
1448 strcat(CommandLine
, Command
);
1450 ZeroMemory(&StartupInfo
, sizeof(StartupInfo
));
1451 ZeroMemory(&ProcessInformation
, sizeof(ProcessInformation
));
1453 StartupInfo
.cb
= sizeof(StartupInfo
);
1455 DosPrintCharacter('\n');
1457 Result
= CreateProcessA(NULL
,
1466 &ProcessInformation
);
1469 DPRINT1("Command '%s' launched successfully\n");
1471 /* Wait for process termination */
1472 WaitForSingleObject(ProcessInformation
.hProcess
, INFINITE
);
1474 /* Get the exit code */
1475 GetExitCodeProcess(ProcessInformation
.hProcess
, &dwExitCode
);
1478 CloseHandle(ProcessInformation
.hThread
);
1479 CloseHandle(ProcessInformation
.hProcess
);
1483 DPRINT1("Failed when launched command '%s'\n");
1484 dwExitCode
= GetLastError();
1487 DosPrintCharacter('\n');
1489 setAL((UCHAR
)dwExitCode
);
1496 DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum
);
1497 // setCF(1); // Disable, otherwise we enter an infinite loop
1503 VOID WINAPI
DosInt20h(LPWORD Stack
)
1505 /* This is the exit interrupt */
1506 DosTerminateProcess(Stack
[STACK_CS
], 0);
1509 VOID WINAPI
DosInt21h(LPWORD Stack
)
1512 SYSTEMTIME SystemTime
;
1514 PDOS_INPUT_BUFFER InputBuffer
;
1516 /* Check the value in the AH register */
1519 /* Terminate Program */
1522 DosTerminateProcess(Stack
[STACK_CS
], 0);
1526 /* Read Character from STDIN with Echo */
1529 Character
= DosReadCharacter();
1530 DosPrintCharacter(Character
);
1532 /* Let the BOP repeat if needed */
1539 /* Write Character to STDOUT */
1542 Character
= getDL();
1543 DosPrintCharacter(Character
);
1546 * We return the output character (DOS 2.1+).
1547 * Also, if we're going to output a TAB, then
1548 * don't return a TAB but a SPACE instead.
1549 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
1550 * for more information.
1552 setAL(Character
== '\t' ? ' ' : Character
);
1556 /* Read Character from STDAUX */
1559 // FIXME: Really read it from STDAUX!
1560 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
1561 setAL(DosReadCharacter());
1565 /* Write Character to STDAUX */
1568 // FIXME: Really write it to STDAUX!
1569 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
1570 DosPrintCharacter(getDL());
1574 /* Write Character to Printer */
1577 // FIXME: Really write it to printer!
1578 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
1579 DPRINT1("0x%p\n", getDL());
1580 DPRINT1("\n\n-----------\n\n");
1584 /* Direct Console I/O */
1587 Character
= getDL();
1589 if (Character
!= 0xFF)
1592 DosPrintCharacter(Character
);
1595 * We return the output character (DOS 2.1+).
1596 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
1597 * for more information.
1604 if (DosCheckInput())
1606 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1607 setAL(DosReadCharacter());
1611 /* No character available */
1612 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1620 /* Character Input without Echo */
1624 Character
= DosReadCharacter();
1626 /* Let the BOP repeat if needed */
1633 /* Write string to STDOUT */
1636 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1638 while (*String
!= '$')
1640 DosPrintCharacter(*String
);
1645 * We return the terminating character (DOS 2.1+).
1646 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
1647 * for more information.
1653 /* Read Buffered Input */
1656 InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
1658 while (Stack
[STACK_COUNTER
] < InputBuffer
->MaxLength
)
1660 /* Try to read a character */
1661 Character
= DosReadCharacter();
1663 /* If it's not ready yet, let the BOP repeat */
1666 /* Echo the character and append it to the buffer */
1667 DosPrintCharacter(Character
);
1668 InputBuffer
->Buffer
[Stack
[STACK_COUNTER
]] = Character
;
1670 if (Character
== '\r') break;
1671 Stack
[STACK_COUNTER
]++;
1674 /* Update the length */
1675 InputBuffer
->Length
= Stack
[STACK_COUNTER
];
1679 /* Get STDIN Status */
1682 setAL(DosCheckInput() ? 0xFF : 0x00);
1686 /* Flush Buffer and Read STDIN */
1689 BYTE InputFunction
= getAL();
1691 /* Flush STDIN buffer */
1692 DosFlushFileBuffers(DOS_INPUT_HANDLE
); // Maybe just create a DosFlushInputBuffer...
1695 * If the input function number contained in AL is valid, i.e.
1696 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
1697 * recursively with AL == AH.
1699 if (InputFunction
== 0x01 || InputFunction
== 0x06 ||
1700 InputFunction
== 0x07 || InputFunction
== 0x08 ||
1701 InputFunction
== 0x0A)
1703 setAH(InputFunction
);
1705 * Instead of calling ourselves really recursively as in:
1707 * prefer resetting the CF flag to let the BOP repeat.
1717 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1719 // TODO: Flush what's needed.
1720 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
1722 /* Clear CF in DOS 6 only */
1723 if (PspBlock
->DosVersion
== 0x0006)
1724 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1729 /* Set Default Drive */
1732 DosChangeDrive(getDL());
1733 setAL(LastDrive
- 'A' + 1);
1737 /* NULL Function for CP/M Compatibility */
1741 * This function corresponds to the CP/M BDOS function
1742 * "get bit map of logged drives", which is meaningless
1745 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
1746 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
1747 * for more information.
1753 /* Get Default Drive */
1756 setAL(CurrentDrive
);
1760 /* Set Disk Transfer Area */
1763 DiskTransferArea
= MAKELONG(getDX(), getDS());
1767 /* NULL Function for CP/M Compatibility */
1772 * Function 0x1D corresponds to the CP/M BDOS function
1773 * "get bit map of read-only drives", which is meaningless
1775 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
1776 * for more information.
1778 * Function 0x1E corresponds to the CP/M BDOS function
1779 * "set file attributes", which was meaningless under MS-DOS 1.x.
1780 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
1781 * for more information.
1787 /* NULL Function for CP/M Compatibility */
1791 * This function corresponds to the CP/M BDOS function
1792 * "get/set default user (sublibrary) number", which is meaningless
1795 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
1796 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
1797 * for more information.
1803 /* Set Interrupt Vector */
1806 DWORD FarPointer
= MAKELONG(getDX(), getDS());
1807 DPRINT1("Setting interrupt 0x%x ...\n", getAL());
1809 /* Write the new far pointer to the IDT */
1810 ((PDWORD
)BaseAddress
)[getAL()] = FarPointer
;
1814 /* Create New PSP */
1817 DPRINT1("INT 21h, 26h - Create New PSP is UNIMPLEMENTED\n");
1821 /* Get System Date */
1824 GetLocalTime(&SystemTime
);
1825 setCX(SystemTime
.wYear
);
1826 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
1827 setAL(SystemTime
.wDayOfWeek
);
1831 /* Set System Date */
1834 GetLocalTime(&SystemTime
);
1835 SystemTime
.wYear
= getCX();
1836 SystemTime
.wMonth
= getDH();
1837 SystemTime
.wDay
= getDL();
1839 /* Return success or failure */
1840 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1844 /* Get System Time */
1847 GetLocalTime(&SystemTime
);
1848 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
1849 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
1853 /* Set System Time */
1856 GetLocalTime(&SystemTime
);
1857 SystemTime
.wHour
= getCH();
1858 SystemTime
.wMinute
= getCL();
1859 SystemTime
.wSecond
= getDH();
1860 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
1862 /* Return success or failure */
1863 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1867 /* Get Disk Transfer Area */
1870 setES(HIWORD(DiskTransferArea
));
1871 setBX(LOWORD(DiskTransferArea
));
1875 /* Get DOS Version */
1878 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1881 * DOS 2+ - GET DOS VERSION
1882 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
1883 * for more information.
1886 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0x00)
1889 * Return DOS OEM number:
1890 * 0x00 for IBM PC-DOS
1891 * 0x02 for packaged MS-DOS
1896 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 0x01)
1899 * Return version flag:
1900 * 1 << 3 if DOS is in ROM,
1901 * 0 (reserved) if not.
1906 /* Return DOS 24-bit user serial number in BL:CX */
1911 * Return DOS version: Minor:Major in AH:AL
1912 * The Windows NT DOS box returns version 5.00, subject to SETVER.
1914 setAX(PspBlock
->DosVersion
);
1919 /* Extended functionalities */
1922 if (getAL() == 0x06)
1925 * DOS 5+ - GET TRUE VERSION NUMBER
1926 * This function always returns the true version number, unlike
1927 * AH=30h, whose return value may be changed with SETVER.
1928 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
1929 * for more information.
1933 * Return the true DOS version: Minor:Major in BH:BL
1934 * The Windows NT DOS box returns BX=3205h (version 5.50).
1936 setBX(NTDOS_VERSION
);
1938 /* DOS revision 0 */
1946 // /* Invalid subfunction */
1953 /* Get Interrupt Vector */
1956 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[getAL()];
1958 /* Read the address from the IDT into ES:BX */
1959 setES(HIWORD(FarPointer
));
1960 setBX(LOWORD(FarPointer
));
1964 /* SWITCH character - AVAILDEV */
1967 if (getAL() == 0x00)
1970 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
1971 * This setting is ignored by MS-DOS 4.0+.
1972 * MS-DOS 5+ always return AL=00h/DL=2Fh.
1973 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
1974 * for more information.
1979 else if (getAL() == 0x01)
1982 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
1983 * This setting is ignored by MS-DOS 5+.
1984 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
1985 * for more information.
1990 else if (getAL() == 0x02)
1993 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1994 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1995 * for more information.
2000 else if (getAL() == 0x03)
2003 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
2004 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
2005 * for more information.
2012 /* Invalid subfunction */
2019 /* Create Directory */
2022 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2024 if (CreateDirectoryA(String
, NULL
))
2026 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2030 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2031 setAX(LOWORD(GetLastError()));
2037 /* Remove Directory */
2040 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2042 if (RemoveDirectoryA(String
))
2044 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2048 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2049 setAX(LOWORD(GetLastError()));
2055 /* Set Current Directory */
2058 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
2060 if (DosChangeDirectory(String
))
2062 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2066 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2067 setAX(DosLastError
);
2077 WORD ErrorCode
= DosCreateFile(&FileHandle
,
2078 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2083 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2088 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2099 WORD ErrorCode
= DosOpenFile(&FileHandle
,
2100 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
2105 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2110 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2120 if (DosCloseHandle(getBX()))
2122 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2126 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2127 setAX(ERROR_INVALID_HANDLE
);
2133 /* Read from File or Device */
2136 WORD Handle
= getBX();
2137 LPBYTE Buffer
= (LPBYTE
)SEG_OFF_TO_PTR(getDS(), getDX());
2138 WORD Count
= getCX();
2140 WORD ErrorCode
= ERROR_SUCCESS
;
2143 if (IsConsoleHandle(DosGetRealHandle(Handle
)))
2145 while (Stack
[STACK_COUNTER
] < Count
)
2147 /* Read a character from the BIOS */
2148 Character
= LOBYTE(BiosGetCharacter());
2150 /* Stop if the BOP needs to be repeated */
2153 // FIXME: Security checks!
2154 DosPrintCharacter(Character
);
2155 Buffer
[Stack
[STACK_COUNTER
]++] = Character
;
2157 if (Character
== '\r')
2159 /* Stop on first carriage return */
2160 DosPrintCharacter('\n');
2165 if (Character
!= '\r')
2167 if (Stack
[STACK_COUNTER
] < Count
) ErrorCode
= ERROR_NOT_READY
;
2168 else BytesRead
= Count
;
2170 else BytesRead
= Stack
[STACK_COUNTER
];
2174 /* Use the file reading function */
2175 ErrorCode
= DosReadFile(Handle
, Buffer
, Count
, &BytesRead
);
2178 if (ErrorCode
== ERROR_SUCCESS
)
2180 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2183 else if (ErrorCode
!= ERROR_NOT_READY
)
2185 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2191 /* Write to File or Device */
2194 WORD BytesWritten
= 0;
2195 WORD ErrorCode
= DosWriteFile(getBX(),
2196 SEG_OFF_TO_PTR(getDS(), getDX()),
2200 if (ErrorCode
== ERROR_SUCCESS
)
2202 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2203 setAX(BytesWritten
);
2207 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2217 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2219 /* Call the API function */
2220 if (DeleteFileA(FileName
))
2222 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2224 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
2225 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
2227 setAL(FileName
[0] - 'A');
2231 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2232 setAX(GetLastError());
2242 WORD ErrorCode
= DosSeekFile(getBX(),
2243 MAKELONG(getDX(), getCX()),
2247 if (ErrorCode
== ERROR_SUCCESS
)
2249 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2251 /* Return the new offset in DX:AX */
2252 setDX(HIWORD(NewLocation
));
2253 setAX(LOWORD(NewLocation
));
2257 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2264 /* Get/Set File Attributes */
2268 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
2270 if (getAL() == 0x00)
2272 /* Get the attributes */
2273 Attributes
= GetFileAttributesA(FileName
);
2275 /* Check if it failed */
2276 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
2278 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2279 setAX(GetLastError());
2283 /* Return the attributes that DOS can understand */
2284 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2285 setCX(Attributes
& 0x00FF);
2288 else if (getAL() == 0x01)
2290 /* Try to set the attributes */
2291 if (SetFileAttributesA(FileName
, getCL()))
2293 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2297 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2298 setAX(GetLastError());
2303 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2304 setAX(ERROR_INVALID_FUNCTION
);
2313 if (DosHandleIoctl(getAL(), getBX()))
2315 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2319 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2320 setAX(DosLastError
);
2326 /* Duplicate Handle */
2330 HANDLE Handle
= DosGetRealHandle(getBX());
2332 if (Handle
!= INVALID_HANDLE_VALUE
)
2334 /* The handle is invalid */
2335 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2336 setAX(ERROR_INVALID_HANDLE
);
2340 /* Open a new handle to the same entry */
2341 NewHandle
= DosOpenHandle(Handle
);
2343 if (NewHandle
== INVALID_DOS_HANDLE
)
2345 /* Too many files open */
2346 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2347 setAX(ERROR_TOO_MANY_OPEN_FILES
);
2351 /* Return the result */
2352 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2357 /* Force Duplicate Handle */
2360 if (DosDuplicateHandle(getBX(), getCX()))
2362 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2366 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2367 setAX(ERROR_INVALID_HANDLE
);
2373 /* Get Current Directory */
2376 BYTE DriveNumber
= getDL();
2377 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getSI());
2379 /* Get the real drive number */
2380 if (DriveNumber
== 0)
2382 DriveNumber
= CurrentDrive
;
2386 /* Decrement DriveNumber since it was 1-based */
2390 if (DriveNumber
<= LastDrive
- 'A')
2393 * Copy the current directory into the target buffer.
2394 * It doesn't contain the drive letter and the backslash.
2396 strncpy(String
, CurrentDirectories
[DriveNumber
], DOS_DIR_LENGTH
);
2397 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2398 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
2402 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2403 setAX(ERROR_INVALID_DRIVE
);
2409 /* Allocate Memory */
2412 WORD MaxAvailable
= 0;
2413 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
2417 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2422 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2423 setAX(DosLastError
);
2424 setBX(MaxAvailable
);
2433 if (DosFreeMemory(getES()))
2435 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2439 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2440 setAX(ERROR_ARENA_TRASHED
);
2446 /* Resize Memory Block */
2451 if (DosResizeMemory(getES(), getBX(), &Size
))
2453 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2457 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2458 setAX(DosLastError
);
2465 /* Terminate With Return Code */
2468 DosTerminateProcess(CurrentPsp
, getAL());
2472 /* Get Return Code (ERRORLEVEL) */
2476 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
2477 * DosErrorLevel is cleared after being read by this function.
2479 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2480 setAX(DosErrorLevel
);
2481 DosErrorLevel
= 0x0000; // Clear it
2485 /* Internal - Set Current Process ID (Set PSP Address) */
2488 // FIXME: Is it really what it's done ??
2489 CurrentPsp
= getBX();
2493 /* Internal - Get Current Process ID (Get PSP Address) */
2495 /* Get Current PSP Address */
2499 * Undocumented AH=51h is identical to the documented AH=62h.
2500 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
2501 * and http://www.ctyme.com/intr/rb-3140.htm
2502 * for more information.
2508 /* Get/Set Memory Management Options */
2511 if (getAL() == 0x00)
2513 /* Get allocation strategy */
2514 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2515 setAX(DosAllocStrategy
);
2517 else if (getAL() == 0x01)
2519 /* Set allocation strategy */
2521 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2522 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2524 /* Can't set both */
2525 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2526 setAX(ERROR_INVALID_PARAMETER
);
2530 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT
)
2532 /* Invalid allocation strategy */
2533 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2534 setAX(ERROR_INVALID_PARAMETER
);
2538 DosAllocStrategy
= getBL();
2539 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2541 else if (getAL() == 0x02)
2543 /* Get UMB link state */
2544 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2545 setAL(DosUmbLinked
? 0x01 : 0x00);
2547 else if (getAL() == 0x03)
2549 /* Set UMB link state */
2550 if (getBX()) DosLinkUmb();
2551 else DosUnlinkUmb();
2552 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2556 /* Invalid or unsupported function */
2557 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2558 setAX(ERROR_INVALID_FUNCTION
);
2567 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2570 setAL(0); // Some functions expect AL to be 0 when it's not supported.
2571 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2576 VOID WINAPI
DosBreakInterrupt(LPWORD Stack
)
2578 UNREFERENCED_PARAMETER(Stack
);
2584 VOID WINAPI
DosFastConOut(LPWORD Stack
)
2587 * This is the DOS 2+ Fast Console Output Interrupt.
2588 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2589 * for more information.
2591 UNREFERENCED_PARAMETER(Stack
);
2594 * The default handler under DOS 2.x and 3.x simply calls INT 10/AH=0Eh.
2595 * Do better and call directly BiosPrintCharacter: it's what INT 10/AH=0Eh
2596 * does. Otherwise we would have to set BL to DOS_CHAR_ATTRIBUTE and
2597 * BH to Bda->VideoPage.
2599 BiosPrintCharacter(getAL(), DOS_CHAR_ATTRIBUTE
, Bda
->VideoPage
);
2602 VOID WINAPI
DosInt2Fh(LPWORD Stack
)
2604 DPRINT1("DOS System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2606 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2609 BOOLEAN
DosInitialize(VOID
)
2612 PDOS_MCB Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
);
2615 LPWSTR SourcePtr
, Environment
;
2617 LPSTR DestPtr
= (LPSTR
)SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK
, 0);
2619 CHAR CurrentDirectory
[MAX_PATH
];
2620 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2623 /* Initialize the MCB */
2624 Mcb
->BlockType
= 'Z';
2625 Mcb
->Size
= USER_MEMORY_SIZE
;
2628 /* Initialize the link MCB to the UMB area */
2629 Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
+ 1);
2630 Mcb
->BlockType
= 'M';
2631 Mcb
->Size
= UMB_START_SEGMENT
- FIRST_MCB_SEGMENT
- USER_MEMORY_SIZE
- 2;
2632 Mcb
->OwnerPsp
= SYSTEM_PSP
;
2634 /* Initialize the UMB area */
2635 Mcb
= SEGMENT_TO_MCB(UMB_START_SEGMENT
);
2636 Mcb
->BlockType
= 'Z';
2637 Mcb
->Size
= UMB_END_SEGMENT
- UMB_START_SEGMENT
;
2640 /* Get the environment strings */
2641 SourcePtr
= Environment
= GetEnvironmentStringsW();
2642 if (Environment
== NULL
) return FALSE
;
2644 /* Fill the DOS system environment block */
2647 /* Get the size of the ASCII string */
2648 AsciiSize
= WideCharToMultiByte(CP_ACP
,
2657 /* Allocate memory for the ASCII string */
2658 AsciiString
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, AsciiSize
);
2659 if (AsciiString
== NULL
)
2661 FreeEnvironmentStringsW(Environment
);
2665 /* Convert to ASCII */
2666 WideCharToMultiByte(CP_ACP
,
2675 /* Copy the string into DOS memory */
2676 strcpy(DestPtr
, AsciiString
);
2678 /* Move to the next string */
2679 SourcePtr
+= wcslen(SourcePtr
) + 1;
2680 DestPtr
+= strlen(AsciiString
);
2683 /* Free the memory */
2684 HeapFree(GetProcessHeap(), 0, AsciiString
);
2688 /* Free the memory allocated for environment strings */
2689 FreeEnvironmentStringsW(Environment
);
2691 /* Clear the current directory buffer */
2692 ZeroMemory(CurrentDirectories
, sizeof(CurrentDirectories
));
2694 /* Get the current directory */
2695 if (!GetCurrentDirectoryA(MAX_PATH
, CurrentDirectory
))
2697 // TODO: Use some kind of default path?
2701 /* Convert that to a DOS path */
2702 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, DOS_DIR_LENGTH
))
2704 // TODO: Use some kind of default path?
2709 CurrentDrive
= DosDirectory
[0] - 'A';
2711 /* Get the directory part of the path */
2712 Path
= strchr(DosDirectory
, '\\');
2715 /* Skip the backslash */
2719 /* Set the directory */
2722 strncpy(CurrentDirectories
[CurrentDrive
], Path
, DOS_DIR_LENGTH
);
2725 /* Read CONFIG.SYS */
2726 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
2729 while (fgetws(Buffer
, 256, Stream
))
2731 // TODO: Parse the line
2736 /* Initialize the SFT */
2737 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
2739 DosSystemFileTable
[i
] = INVALID_HANDLE_VALUE
;
2740 DosSftRefCount
[i
] = 0;
2743 /* Get handles to standard I/O devices */
2744 DosSystemFileTable
[0] = GetStdHandle(STD_INPUT_HANDLE
);
2745 DosSystemFileTable
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
2746 DosSystemFileTable
[2] = GetStdHandle(STD_ERROR_HANDLE
);
2748 /* Register the DOS BOPs */
2749 RegisterBop(BOP_DOS
, DosSystemBop
);
2750 RegisterBop(BOP_CMD
, DosCmdInterpreterBop
);
2752 /* Register the DOS 32-bit Interrupts */
2753 RegisterInt32(0x20, DosInt20h
);
2754 RegisterInt32(0x21, DosInt21h
);
2755 // RegisterInt32(0x22, DosInt22h ); // Termination
2756 RegisterInt32(0x23, DosBreakInterrupt
); // Ctrl-C / Ctrl-Break
2757 // RegisterInt32(0x24, DosInt24h ); // Critical Error
2758 RegisterInt32(0x29, DosFastConOut
); // DOS 2+ Fast Console Output
2759 RegisterInt32(0x2F, DosInt2Fh
);