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 *******************************************************************/
17 #include "registers.h"
19 /* PRIVATE VARIABLES **********************************************************/
21 static WORD CurrentPsp
= SYSTEM_PSP
;
22 static WORD DosLastError
= 0;
23 static DWORD DiskTransferArea
;
24 static BYTE CurrentDrive
;
25 static CHAR LastDrive
= 'E';
26 static CHAR CurrentDirectories
[NUM_DRIVES
][DOS_DIR_LENGTH
];
27 static HANDLE DosSystemFileTable
[DOS_SFT_SIZE
];
28 static WORD DosSftRefCount
[DOS_SFT_SIZE
];
29 static BYTE DosAllocStrategy
= DOS_ALLOC_BEST_FIT
;
30 static BOOLEAN DosUmbLinked
= FALSE
;
32 /* PRIVATE FUNCTIONS **********************************************************/
34 /* Taken from base/shell/cmd/console.c */
35 static BOOL
IsConsoleHandle(HANDLE hHandle
)
39 /* Check whether the handle may be that of a console... */
40 if ((GetFileType(hHandle
) & FILE_TYPE_CHAR
) == 0) return FALSE
;
43 * It may be. Perform another test... The idea comes from the
44 * MSDN description of the WriteConsole API:
46 * "WriteConsole fails if it is used with a standard handle
47 * that is redirected to a file. If an application processes
48 * multilingual output that can be redirected, determine whether
49 * the output handle is a console handle (one method is to call
50 * the GetConsoleMode function and check whether it succeeds).
51 * If the handle is a console handle, call WriteConsole. If the
52 * handle is not a console handle, the output is redirected and
53 * you should call WriteFile to perform the I/O."
55 return GetConsoleMode(hHandle
, &dwMode
);
58 static VOID
DosCombineFreeBlocks(WORD StartBlock
)
60 PDOS_MCB CurrentMcb
= SEGMENT_TO_MCB(StartBlock
), NextMcb
;
62 /* If this is the last block or it's not free, quit */
63 if (CurrentMcb
->BlockType
== 'Z' || CurrentMcb
->OwnerPsp
!= 0) return;
67 /* Get a pointer to the next MCB */
68 NextMcb
= SEGMENT_TO_MCB(StartBlock
+ CurrentMcb
->Size
+ 1);
70 /* Check if the next MCB is free */
71 if (NextMcb
->OwnerPsp
== 0)
74 CurrentMcb
->Size
+= NextMcb
->Size
+ 1;
75 CurrentMcb
->BlockType
= NextMcb
->BlockType
;
76 NextMcb
->BlockType
= 'I';
80 /* No more adjoining free blocks */
86 static WORD
DosCopyEnvironmentBlock(WORD SourceSegment
, LPCSTR ProgramName
)
88 PCHAR Ptr
, SourceBuffer
, DestBuffer
= NULL
;
92 Ptr
= SourceBuffer
= (PCHAR
)SEG_OFF_TO_PTR(SourceSegment
, 0);
94 /* Calculate the size of the environment block */
97 TotalSize
+= strlen(Ptr
) + 1;
98 Ptr
+= strlen(Ptr
) + 1;
102 /* Add the string buffer size */
103 TotalSize
+= strlen(ProgramName
) + 1;
105 /* Allocate the memory for the environment block */
106 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
107 if (!DestSegment
) return 0;
111 DestBuffer
= (PCHAR
)SEG_OFF_TO_PTR(DestSegment
, 0);
114 /* Copy the string */
115 strcpy(DestBuffer
, Ptr
);
117 /* Advance to the next string */
118 DestBuffer
+= strlen(Ptr
);
119 Ptr
+= strlen(Ptr
) + 1;
121 /* Put a zero after the string */
125 /* Set the final zero */
128 /* Copy the program name after the environment block */
129 strcpy(DestBuffer
, ProgramName
);
134 static VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
136 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
138 /* Just set the owner */
139 Mcb
->OwnerPsp
= NewOwner
;
142 static WORD
DosOpenHandle(HANDLE Handle
)
149 /* The system PSP has no handle table */
150 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
152 /* Get a pointer to the handle table */
153 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
154 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
156 /* Find a free entry in the JFT */
157 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
159 if (HandleTable
[DosHandle
] == 0xFF) break;
162 /* If there are no free entries, fail */
163 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
165 /* Check if the handle is already in the SFT */
166 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
168 /* Check if this is the same handle */
169 if (DosSystemFileTable
[i
] != Handle
) continue;
171 /* Already in the table, reference it */
174 /* Set the JFT entry to that SFT index */
175 HandleTable
[DosHandle
] = i
;
177 /* Return the new handle */
181 /* Add the handle to the SFT */
182 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
184 /* Make sure this is an empty table entry */
185 if (DosSystemFileTable
[i
] != INVALID_HANDLE_VALUE
) continue;
187 /* Initialize the empty table entry */
188 DosSystemFileTable
[i
] = Handle
;
189 DosSftRefCount
[i
] = 1;
191 /* Set the JFT entry to that SFT index */
192 HandleTable
[DosHandle
] = i
;
194 /* Return the new handle */
198 /* The SFT is full */
199 return INVALID_DOS_HANDLE
;
202 static HANDLE
DosGetRealHandle(WORD DosHandle
)
207 /* The system PSP has no handle table */
208 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_HANDLE_VALUE
;
210 /* Get a pointer to the handle table */
211 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
212 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
214 /* Make sure the handle is open */
215 if (HandleTable
[DosHandle
] == 0xFF) return INVALID_HANDLE_VALUE
;
217 /* Return the Win32 handle */
218 return DosSystemFileTable
[HandleTable
[DosHandle
]];
221 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
227 /* Clear the table first */
228 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
230 /* Check if this is the initial process */
231 if (CurrentPsp
== SYSTEM_PSP
)
233 /* Set up the standard I/O devices */
234 for (i
= 0; i
<= 2; i
++)
236 /* Set the index in the SFT */
237 DestinationTable
[i
] = (BYTE
)i
;
239 /* Increase the reference count */
247 /* Get the parent PSP block and handle table */
248 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
249 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
251 /* Copy the first 20 handles into the new table */
252 for (i
= 0; i
< 20; i
++)
254 DestinationTable
[i
] = SourceTable
[i
];
256 /* Increase the reference count */
257 DosSftRefCount
[SourceTable
[i
]]++;
261 /* PUBLIC FUNCTIONS ***********************************************************/
263 WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
265 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
266 PDOS_MCB CurrentMcb
, NextMcb
;
267 BOOLEAN SearchUmb
= FALSE
;
269 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size
);
271 if (DosUmbLinked
&& (DosAllocStrategy
& (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
)))
273 /* Search UMB first */
274 Segment
= UMB_START_SEGMENT
;
280 /* Get a pointer to the MCB */
281 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
283 /* Make sure it's valid */
284 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
286 DPRINT("The DOS memory arena is corrupted!\n");
287 DosLastError
= ERROR_ARENA_TRASHED
;
291 /* Only check free blocks */
292 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
294 /* Combine this free block with adjoining free blocks */
295 DosCombineFreeBlocks(Segment
);
297 /* Update the maximum block size */
298 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
300 /* Check if this block is big enough */
301 if (CurrentMcb
->Size
< Size
) goto Next
;
303 switch (DosAllocStrategy
& 0x3F)
305 case DOS_ALLOC_FIRST_FIT
:
307 /* For first fit, stop immediately */
312 case DOS_ALLOC_BEST_FIT
:
314 /* For best fit, update the smallest block found so far */
315 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
323 case DOS_ALLOC_LAST_FIT
:
325 /* For last fit, make the current block the result, but keep searching */
332 /* If this was the last MCB in the chain, quit */
333 if (CurrentMcb
->BlockType
== 'Z')
335 /* Check if nothing was found while searching through UMBs */
336 if ((Result
== 0) && SearchUmb
&& (DosAllocStrategy
& DOS_ALLOC_HIGH_LOW
))
338 /* Search low memory */
339 Segment
= FIRST_MCB_SEGMENT
;
346 /* Otherwise, update the segment and continue */
347 Segment
+= CurrentMcb
->Size
+ 1;
352 /* If we didn't find a free block, return 0 */
355 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
356 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
360 /* Get a pointer to the MCB */
361 CurrentMcb
= SEGMENT_TO_MCB(Result
);
363 /* Check if the block is larger than requested */
364 if (CurrentMcb
->Size
> Size
)
366 /* It is, split it into two blocks */
367 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
369 /* Initialize the new MCB structure */
370 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
371 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
372 NextMcb
->OwnerPsp
= 0;
374 /* Update the current block */
375 CurrentMcb
->BlockType
= 'M';
376 CurrentMcb
->Size
= Size
;
379 /* Take ownership of the block */
380 CurrentMcb
->OwnerPsp
= CurrentPsp
;
382 /* Return the segment of the data portion of the block */
386 BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
388 BOOLEAN Success
= TRUE
;
389 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
390 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
392 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
396 /* Make sure this is a valid, allocated block */
397 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
400 DosLastError
= ERROR_INVALID_HANDLE
;
404 ReturnSize
= Mcb
->Size
;
406 /* Check if we need to expand or contract the block */
407 if (NewSize
> Mcb
->Size
)
409 /* We can't expand the last block */
410 if (Mcb
->BlockType
!= 'M')
416 /* Get the pointer and segment of the next MCB */
417 NextSegment
= Segment
+ Mcb
->Size
+ 1;
418 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
420 /* Make sure the next segment is free */
421 if (NextMcb
->OwnerPsp
!= 0)
423 DPRINT("Cannot expand memory block: next segment is not free!\n");
424 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
429 /* Combine this free block with adjoining free blocks */
430 DosCombineFreeBlocks(NextSegment
);
432 /* Set the maximum possible size of the block */
433 ReturnSize
+= NextMcb
->Size
+ 1;
435 /* Maximize the current block */
436 Mcb
->Size
= ReturnSize
;
437 Mcb
->BlockType
= NextMcb
->BlockType
;
439 /* Invalidate the next block */
440 NextMcb
->BlockType
= 'I';
442 /* Check if the block is larger than requested */
443 if (Mcb
->Size
> NewSize
)
445 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
449 /* It is, split it into two blocks */
450 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
452 /* Initialize the new MCB structure */
453 NextMcb
->BlockType
= Mcb
->BlockType
;
454 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
455 NextMcb
->OwnerPsp
= 0;
457 /* Update the current block */
458 Mcb
->BlockType
= 'M';
462 else if (NewSize
< Mcb
->Size
)
464 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
468 /* Just split the block */
469 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
470 NextMcb
->BlockType
= Mcb
->BlockType
;
471 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
472 NextMcb
->OwnerPsp
= 0;
475 Mcb
->BlockType
= 'M';
480 /* Check if the operation failed */
483 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
486 /* Return the maximum possible size */
487 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
493 BOOLEAN
DosFreeMemory(WORD BlockData
)
495 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
497 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData
);
499 /* Make sure the MCB is valid */
500 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z')
502 DPRINT("MCB block type '%c' not valid!\n", Mcb
->BlockType
);
506 /* Mark the block as free */
512 BOOLEAN
DosLinkUmb(VOID
)
514 DWORD Segment
= FIRST_MCB_SEGMENT
;
515 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
517 DPRINT("Linking UMB\n");
519 /* Check if UMBs are already linked */
520 if (DosUmbLinked
) return FALSE
;
522 /* Find the last block */
523 while ((Mcb
->BlockType
== 'M') && (Segment
<= 0xFFFF))
525 Segment
+= Mcb
->Size
+ 1;
526 Mcb
= SEGMENT_TO_MCB(Segment
);
529 /* Make sure it's valid */
530 if (Mcb
->BlockType
!= 'Z') return FALSE
;
532 /* Connect the MCB with the UMB chain */
533 Mcb
->BlockType
= 'M';
539 BOOLEAN
DosUnlinkUmb(VOID
)
541 DWORD Segment
= FIRST_MCB_SEGMENT
;
542 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
544 DPRINT("Unlinking UMB\n");
546 /* Check if UMBs are already unlinked */
547 if (!DosUmbLinked
) return FALSE
;
549 /* Find the block preceding the MCB that links it with the UMB chain */
550 while (Segment
<= 0xFFFF)
552 if ((Segment
+ Mcb
->Size
) == (FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
))
554 /* This is the last non-UMB segment */
558 /* Advance to the next MCB */
559 Segment
+= Mcb
->Size
+ 1;
560 Mcb
= SEGMENT_TO_MCB(Segment
);
563 /* Mark the MCB as the last MCB */
564 Mcb
->BlockType
= 'Z';
566 DosUmbLinked
= FALSE
;
570 WORD
DosCreateFile(LPWORD Handle
, LPCSTR FilePath
, WORD Attributes
)
575 DPRINT("DosCreateFile: FilePath \"%s\", Attributes 0x%04X\n",
579 /* Create the file */
580 FileHandle
= CreateFileA(FilePath
,
581 GENERIC_READ
| GENERIC_WRITE
,
582 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
588 if (FileHandle
== INVALID_HANDLE_VALUE
)
590 /* Return the error code */
591 return (WORD
)GetLastError();
594 /* Open the DOS handle */
595 DosHandle
= DosOpenHandle(FileHandle
);
597 if (DosHandle
== INVALID_DOS_HANDLE
)
599 /* Close the handle */
600 CloseHandle(FileHandle
);
602 /* Return the error code */
603 return ERROR_TOO_MANY_OPEN_FILES
;
606 /* It was successful */
608 return ERROR_SUCCESS
;
611 WORD
DosOpenFile(LPWORD Handle
, LPCSTR FilePath
, BYTE AccessMode
)
614 ACCESS_MASK Access
= 0;
617 DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n",
621 /* Parse the access mode */
622 switch (AccessMode
& 3)
627 Access
= GENERIC_READ
;
634 Access
= GENERIC_WRITE
;
641 Access
= GENERIC_READ
| GENERIC_WRITE
;
648 return ERROR_INVALID_PARAMETER
;
653 FileHandle
= CreateFileA(FilePath
,
655 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
658 FILE_ATTRIBUTE_NORMAL
,
661 if (FileHandle
== INVALID_HANDLE_VALUE
)
663 /* Return the error code */
664 return (WORD
)GetLastError();
667 /* Open the DOS handle */
668 DosHandle
= DosOpenHandle(FileHandle
);
670 if (DosHandle
== INVALID_DOS_HANDLE
)
672 /* Close the handle */
673 CloseHandle(FileHandle
);
675 /* Return the error code */
676 return ERROR_TOO_MANY_OPEN_FILES
;
679 /* It was successful */
681 return ERROR_SUCCESS
;
684 WORD
DosReadFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesRead
)
686 WORD Result
= ERROR_SUCCESS
;
687 DWORD BytesRead32
= 0;
688 HANDLE Handle
= DosGetRealHandle(FileHandle
);
690 DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle
, Count
);
692 /* Make sure the handle is valid */
693 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
696 if (!ReadFile(Handle
, Buffer
, Count
, &BytesRead32
, NULL
))
698 /* Store the error code */
699 Result
= (WORD
)GetLastError();
702 /* The number of bytes read is always 16-bit */
703 *BytesRead
= LOWORD(BytesRead32
);
705 /* Return the error code */
709 WORD
DosWriteFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesWritten
)
711 WORD Result
= ERROR_SUCCESS
;
712 DWORD BytesWritten32
= 0;
713 HANDLE Handle
= DosGetRealHandle(FileHandle
);
716 DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n",
720 /* Make sure the handle is valid */
721 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
723 if (IsConsoleHandle(Handle
))
725 for (i
= 0; i
< Count
; i
++)
727 /* Call the BIOS to print the character */
728 BiosPrintCharacter(((LPBYTE
)Buffer
)[i
], DOS_CHAR_ATTRIBUTE
, Bda
->VideoPage
);
735 if (!WriteFile(Handle
, Buffer
, Count
, &BytesWritten32
, NULL
))
737 /* Store the error code */
738 Result
= (WORD
)GetLastError();
742 /* The number of bytes written is always 16-bit */
743 *BytesWritten
= LOWORD(BytesWritten32
);
745 /* Return the error code */
749 WORD
DosSeekFile(WORD FileHandle
, LONG Offset
, BYTE Origin
, LPDWORD NewOffset
)
751 WORD Result
= ERROR_SUCCESS
;
753 HANDLE Handle
= DosGetRealHandle(FileHandle
);
755 DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n",
760 /* Make sure the handle is valid */
761 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
763 /* Check if the origin is valid */
764 if (Origin
!= FILE_BEGIN
&& Origin
!= FILE_CURRENT
&& Origin
!= FILE_END
)
766 return ERROR_INVALID_FUNCTION
;
769 /* Move the file pointer */
770 FilePointer
= SetFilePointer(Handle
, Offset
, NULL
, Origin
);
772 /* Check if there's a possibility the operation failed */
773 if (FilePointer
== INVALID_SET_FILE_POINTER
)
775 /* Get the real error code */
776 Result
= (WORD
)GetLastError();
779 if (Result
!= ERROR_SUCCESS
)
781 /* The operation did fail */
785 /* Return the file pointer, if requested */
786 if (NewOffset
) *NewOffset
= FilePointer
;
789 return ERROR_SUCCESS
;
792 BOOLEAN
DosDuplicateHandle(WORD OldHandle
, WORD NewHandle
)
798 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
802 /* The system PSP has no handle table */
803 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
805 /* Get a pointer to the handle table */
806 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
807 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
809 /* Make sure the old handle is open */
810 if (HandleTable
[OldHandle
] == 0xFF) return FALSE
;
812 /* Check if the new handle is open */
813 if (HandleTable
[NewHandle
] != 0xFF)
816 DosCloseHandle(NewHandle
);
819 /* Increment the reference count of the SFT entry */
820 SftIndex
= HandleTable
[OldHandle
];
821 DosSftRefCount
[SftIndex
]++;
823 /* Make the new handle point to that SFT entry */
824 HandleTable
[NewHandle
] = SftIndex
;
830 BOOLEAN
DosCloseHandle(WORD DosHandle
)
836 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle
);
838 /* The system PSP has no handle table */
839 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
841 /* Get a pointer to the handle table */
842 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
843 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
845 /* Make sure the handle is open */
846 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
848 /* Decrement the reference count of the SFT entry */
849 SftIndex
= HandleTable
[DosHandle
];
850 DosSftRefCount
[SftIndex
]--;
852 /* Check if the reference count fell to zero */
853 if (!DosSftRefCount
[SftIndex
])
855 /* Close the file, it's no longer needed */
856 CloseHandle(DosSystemFileTable
[SftIndex
]);
858 /* Clear the handle */
859 DosSystemFileTable
[SftIndex
] = INVALID_HANDLE_VALUE
;
862 /* Clear the entry in the JFT */
863 HandleTable
[DosHandle
] = 0xFF;
868 BOOLEAN
DosChangeDrive(BYTE Drive
)
870 WCHAR DirectoryPath
[DOS_CMDLINE_LENGTH
];
872 /* Make sure the drive exists */
873 if (Drive
> (LastDrive
- 'A')) return FALSE
;
875 /* Find the path to the new current directory */
876 swprintf(DirectoryPath
, L
"%c\\%S", Drive
+ 'A', CurrentDirectories
[Drive
]);
878 /* Change the current directory of the process */
879 if (!SetCurrentDirectory(DirectoryPath
)) return FALSE
;
881 /* Set the current drive */
882 CurrentDrive
= Drive
;
888 BOOLEAN
DosChangeDirectory(LPSTR Directory
)
894 /* Make sure the directory path is not too long */
895 if (strlen(Directory
) >= DOS_DIR_LENGTH
)
897 DosLastError
= ERROR_PATH_NOT_FOUND
;
901 /* Get the drive number */
902 DriveNumber
= Directory
[0] - 'A';
904 /* Make sure the drive exists */
905 if (DriveNumber
> (LastDrive
- 'A'))
907 DosLastError
= ERROR_PATH_NOT_FOUND
;
911 /* Get the file attributes */
912 Attributes
= GetFileAttributesA(Directory
);
914 /* Make sure the path exists and is a directory */
915 if ((Attributes
== INVALID_FILE_ATTRIBUTES
)
916 || !(Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
918 DosLastError
= ERROR_PATH_NOT_FOUND
;
922 /* Check if this is the current drive */
923 if (DriveNumber
== CurrentDrive
)
925 /* Change the directory */
926 if (!SetCurrentDirectoryA(Directory
))
928 DosLastError
= LOWORD(GetLastError());
933 /* Get the directory part of the path */
934 Path
= strchr(Directory
, '\\');
937 /* Skip the backslash */
941 /* Set the directory for the drive */
942 if (Path
!= NULL
) strcpy(CurrentDirectories
[DriveNumber
], Path
);
943 else strcpy(CurrentDirectories
[DriveNumber
], "");
949 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
951 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
952 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
954 ZeroMemory(PspBlock
, sizeof(DOS_PSP
));
956 /* Set the exit interrupt */
957 PspBlock
->Exit
[0] = 0xCD; // int 0x20
958 PspBlock
->Exit
[1] = 0x20;
960 /* Set the number of the last paragraph */
961 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
963 /* Save the interrupt vectors */
964 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
965 PspBlock
->BreakAddress
= IntVecTable
[0x23];
966 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
968 /* Set the parent PSP */
969 PspBlock
->ParentPsp
= CurrentPsp
;
971 /* Copy the parent handle table */
972 DosCopyHandleTable(PspBlock
->HandleTable
);
974 /* Set the environment block */
975 PspBlock
->EnvBlock
= Environment
;
977 /* Set the handle table pointers to the internal handle table */
978 PspBlock
->HandleTableSize
= 20;
979 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
981 /* Set the DOS version */
982 PspBlock
->DosVersion
= DOS_VERSION
;
984 /* Set the far call opcodes */
985 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
986 PspBlock
->FarCall
[1] = 0x21;
987 PspBlock
->FarCall
[2] = 0xCB; // retf
989 /* Set the command line */
990 PspBlock
->CommandLineSize
= (BYTE
)min(strlen(CommandLine
), DOS_CMDLINE_LENGTH
- 1);
991 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
992 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
995 BOOLEAN
DosCreateProcess(LPCSTR CommandLine
, WORD EnvBlock
)
997 BOOLEAN Success
= FALSE
, AllocatedEnvBlock
= FALSE
;
998 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
999 LPBYTE Address
= NULL
;
1000 LPSTR ProgramFilePath
, Parameters
[256];
1001 CHAR CommandLineCopy
[DOS_CMDLINE_LENGTH
];
1005 DWORD i
, FileSize
, ExeSize
;
1006 PIMAGE_DOS_HEADER Header
;
1007 PDWORD RelocationTable
;
1010 DPRINT("DosCreateProcess: CommandLine \"%s\", EnvBlock 0x%04X\n",
1014 /* Save a copy of the command line */
1015 strcpy(CommandLineCopy
, CommandLine
);
1017 // FIXME: Improve parsing (especially: "some_path\with spaces\program.exe" options)
1019 /* Get the file name of the executable */
1020 ProgramFilePath
= strtok(CommandLineCopy
, " \t");
1022 /* Load the parameters in the local array */
1023 while ((ParamCount
< sizeof(Parameters
)/sizeof(Parameters
[0]))
1024 && ((Parameters
[ParamCount
] = strtok(NULL
, " \t")) != NULL
))
1029 /* Open a handle to the executable */
1030 FileHandle
= CreateFileA(ProgramFilePath
,
1035 FILE_ATTRIBUTE_NORMAL
,
1037 if (FileHandle
== INVALID_HANDLE_VALUE
) goto Cleanup
;
1039 /* Get the file size */
1040 FileSize
= GetFileSize(FileHandle
, NULL
);
1042 /* Create a mapping object for the file */
1043 FileMapping
= CreateFileMapping(FileHandle
,
1049 if (FileMapping
== NULL
) goto Cleanup
;
1051 /* Map the file into memory */
1052 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
1053 if (Address
== NULL
) goto Cleanup
;
1055 /* Did we get an environment segment? */
1058 /* Set a flag to know if the environment block was allocated here */
1059 AllocatedEnvBlock
= TRUE
;
1061 /* No, copy the one from the parent */
1062 EnvBlock
= DosCopyEnvironmentBlock((CurrentPsp
!= SYSTEM_PSP
)
1063 ? SEGMENT_TO_PSP(CurrentPsp
)->EnvBlock
1068 /* Check if this is an EXE file or a COM file */
1069 if (Address
[0] == 'M' && Address
[1] == 'Z')
1073 /* Get the MZ header */
1074 Header
= (PIMAGE_DOS_HEADER
)Address
;
1076 /* Get the base size of the file, in paragraphs (rounded up) */
1077 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
1079 /* Add the PSP size, in paragraphs */
1080 ExeSize
+= sizeof(DOS_PSP
) >> 4;
1082 /* Add the maximum size that should be allocated */
1083 ExeSize
+= Header
->e_maxalloc
;
1085 /* Make sure it does not pass 0xFFFF */
1086 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
1088 /* Reduce the size one by one until the allocation is successful */
1089 for (i
= Header
->e_maxalloc
; i
>= Header
->e_minalloc
; i
--, ExeSize
--)
1091 /* Try to allocate that much memory */
1092 Segment
= DosAllocateMemory((WORD
)ExeSize
, NULL
);
1093 if (Segment
!= 0) break;
1096 /* Check if at least the lowest allocation was successful */
1097 if (Segment
== 0) goto Cleanup
;
1099 /* Initialize the PSP */
1100 DosInitializePsp(Segment
,
1105 /* The process owns its own memory */
1106 DosChangeMemoryOwner(Segment
, Segment
);
1107 DosChangeMemoryOwner(EnvBlock
, Segment
);
1109 /* Copy the program to Segment:0100 */
1110 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
1111 Address
+ (Header
->e_cparhdr
<< 4),
1112 min(FileSize
- (Header
->e_cparhdr
<< 4),
1113 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
1115 /* Get the relocation table */
1116 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
1118 /* Perform relocations */
1119 for (i
= 0; i
< Header
->e_crlc
; i
++)
1121 /* Get a pointer to the word that needs to be patched */
1122 RelocWord
= (PWORD
)SEG_OFF_TO_PTR(Segment
+ HIWORD(RelocationTable
[i
]),
1123 0x100 + LOWORD(RelocationTable
[i
]));
1125 /* Add the number of the EXE segment to it */
1126 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
1129 /* Set the initial segment registers */
1133 /* Set the stack to the location from the header */
1134 EmulatorSetStack(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
,
1138 CurrentPsp
= Segment
;
1139 DiskTransferArea
= MAKELONG(0x80, Segment
);
1140 EmulatorExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
1149 /* Find the maximum amount of memory that can be allocated */
1150 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
1152 /* Make sure it's enough for the whole program and the PSP */
1153 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
))) goto Cleanup
;
1155 /* Allocate all of it */
1156 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
1157 if (Segment
== 0) goto Cleanup
;
1159 /* The process owns its own memory */
1160 DosChangeMemoryOwner(Segment
, Segment
);
1161 DosChangeMemoryOwner(EnvBlock
, Segment
);
1163 /* Copy the program to Segment:0100 */
1164 RtlCopyMemory(SEG_OFF_TO_PTR(Segment
, 0x100),
1168 /* Initialize the PSP */
1169 DosInitializePsp(Segment
,
1171 (WORD
)((FileSize
+ sizeof(DOS_PSP
)) >> 4),
1174 /* Set the initial segment registers */
1178 /* Set the stack to the last word of the segment */
1179 EmulatorSetStack(Segment
, 0xFFFE);
1182 CurrentPsp
= Segment
;
1183 DiskTransferArea
= MAKELONG(0x80, Segment
);
1184 EmulatorExecute(Segment
, 0x100);
1192 /* It was not successful, cleanup the DOS memory */
1193 if (AllocatedEnvBlock
) DosFreeMemory(EnvBlock
);
1194 if (Segment
) DosFreeMemory(Segment
);
1198 if (Address
!= NULL
) UnmapViewOfFile(Address
);
1200 /* Close the file mapping object */
1201 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
1203 /* Close the file handle */
1204 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
1209 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
)
1212 WORD McbSegment
= FIRST_MCB_SEGMENT
;
1213 PDOS_MCB CurrentMcb
;
1214 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
1215 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
1217 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
1221 /* Check if this PSP is it's own parent */
1222 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
1224 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
1226 /* Close the handle */
1230 /* Free the memory used by the process */
1233 /* Get a pointer to the MCB */
1234 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
1236 /* Make sure the MCB is valid */
1237 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!='Z') break;
1239 /* If this block was allocated by the process, free it */
1240 if (CurrentMcb
->OwnerPsp
== Psp
) DosFreeMemory(McbSegment
+ 1);
1242 /* If this was the last block, quit */
1243 if (CurrentMcb
->BlockType
== 'Z') break;
1245 /* Update the segment and continue */
1246 McbSegment
+= CurrentMcb
->Size
+ 1;
1250 /* Restore the interrupt vectors */
1251 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
1252 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
1253 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
1255 /* Update the current PSP */
1256 if (Psp
== CurrentPsp
)
1258 CurrentPsp
= PspBlock
->ParentPsp
;
1259 if (CurrentPsp
== SYSTEM_PSP
) VdmRunning
= FALSE
;
1262 /* Return control to the parent process */
1263 EmulatorExecute(HIWORD(PspBlock
->TerminateAddress
),
1264 LOWORD(PspBlock
->TerminateAddress
));
1267 CHAR
DosReadCharacter(VOID
)
1269 CHAR Character
= '\0';
1272 if (IsConsoleHandle(DosGetRealHandle(DOS_INPUT_HANDLE
)))
1275 Character
= LOBYTE(BiosGetCharacter());
1279 /* Use the file reading function */
1280 DosReadFile(DOS_INPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesRead
);
1286 BOOLEAN
DosCheckInput(VOID
)
1288 HANDLE Handle
= DosGetRealHandle(DOS_INPUT_HANDLE
);
1290 if (IsConsoleHandle(Handle
))
1293 return (BiosPeekCharacter() != 0xFFFF);
1298 DWORD FileSize
= GetFileSize(Handle
, &FileSizeHigh
);
1299 LONG LocationHigh
= 0;
1300 DWORD Location
= SetFilePointer(Handle
, 0, &LocationHigh
, FILE_CURRENT
);
1302 return ((Location
!= FileSize
) || (LocationHigh
!= FileSizeHigh
));
1306 VOID
DosPrintCharacter(CHAR Character
)
1310 /* Use the file writing function */
1311 DosWriteFile(DOS_OUTPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesWritten
);
1314 BOOLEAN
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
1316 HANDLE Handle
= DosGetRealHandle(FileHandle
);
1318 if (Handle
== INVALID_HANDLE_VALUE
)
1321 DosLastError
= ERROR_FILE_NOT_FOUND
;
1325 switch (ControlCode
)
1327 /* Get Device Information */
1332 if (Handle
== DosSystemFileTable
[0])
1337 else if (Handle
== DosSystemFileTable
[1])
1339 /* Console output */
1343 /* It is a character device */
1346 /* Return the device information word */
1351 /* Unsupported control code */
1354 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
1356 DosLastError
= ERROR_INVALID_PARAMETER
;
1362 VOID
DosInt20h(LPWORD Stack
)
1364 /* This is the exit interrupt */
1365 DosTerminateProcess(Stack
[STACK_CS
], 0);
1368 VOID
DosInt21h(LPWORD Stack
)
1371 SYSTEMTIME SystemTime
;
1373 PDOS_INPUT_BUFFER InputBuffer
;
1375 /* Check the value in the AH register */
1378 /* Terminate Program */
1381 DosTerminateProcess(Stack
[STACK_CS
], 0);
1385 /* Read Character And Echo */
1388 Character
= DosReadCharacter();
1389 DosPrintCharacter(Character
);
1391 /* Let the BOP repeat if needed */
1392 if (EmulatorGetFlag(EMULATOR_FLAG_CF
)) break;
1398 /* Print Character */
1401 BYTE Character
= getDL();
1402 DosPrintCharacter(Character
);
1405 * We return the output character (DOS 2.1+), see:
1406 * http://www.delorie.com/djgpp/doc/rbinter/id/65/25.html
1407 * for more information.
1413 /* Direct Console I/O */
1416 BYTE Character
= getDL();
1418 if (Character
!= 0xFF)
1421 DosPrintCharacter(Character
);
1424 * We return the output character (DOS 2.1+), see:
1425 * http://www.delorie.com/djgpp/doc/rbinter/id/69/25.html
1426 * for more information.
1433 if (DosCheckInput())
1435 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
1436 setAL(DosReadCharacter());
1440 /* No character available */
1441 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
1449 /* Read Character Without Echo */
1453 Character
= DosReadCharacter();
1455 /* Let the BOP repeat if needed */
1456 if (EmulatorGetFlag(EMULATOR_FLAG_CF
)) break;
1465 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1467 while (*String
!= '$')
1469 DosPrintCharacter(*String
);
1474 * We return the output character (DOS 2.1+), see:
1475 * http://www.delorie.com/djgpp/doc/rbinter/id/73/25.html
1476 * for more information.
1482 /* Read Buffered Input */
1485 InputBuffer
= (PDOS_INPUT_BUFFER
)SEG_OFF_TO_PTR(getDS(), getDX());
1487 while (Stack
[STACK_COUNTER
] < InputBuffer
->MaxLength
)
1489 /* Try to read a character */
1490 Character
= DosReadCharacter();
1492 /* If it's not ready yet, let the BOP repeat */
1493 if (EmulatorGetFlag(EMULATOR_FLAG_CF
)) break;
1495 /* Echo the character and append it to the buffer */
1496 DosPrintCharacter(Character
);
1497 InputBuffer
->Buffer
[Stack
[STACK_COUNTER
]] = Character
;
1499 if (Character
== '\r') break;
1500 Stack
[STACK_COUNTER
]++;
1503 /* Update the length */
1504 InputBuffer
->Length
= Stack
[STACK_COUNTER
];
1508 /* Get STDIN Status */
1511 setAL(DosCheckInput() ? 0xFF : 0x00);
1515 /* Set Default Drive */
1518 DosChangeDrive(getDL());
1519 setAL(LastDrive
- 'A' + 1);
1523 /* Get Default Drive */
1526 setAL(CurrentDrive
);
1530 /* Set Disk Transfer Area */
1533 DiskTransferArea
= MAKELONG(getDX(), getDS());
1537 /* Set Interrupt Vector */
1540 DWORD FarPointer
= MAKELONG(getDX(), getDS());
1542 /* Write the new far pointer to the IDT */
1543 ((PDWORD
)BaseAddress
)[getAL()] = FarPointer
;
1548 /* Get system date */
1551 GetLocalTime(&SystemTime
);
1552 setCX(SystemTime
.wYear
);
1553 setDX(MAKEWORD(SystemTime
.wDay
, SystemTime
.wMonth
));
1554 setAL(SystemTime
.wDayOfWeek
);
1558 /* Set system date */
1561 GetLocalTime(&SystemTime
);
1562 SystemTime
.wYear
= getCX();
1563 SystemTime
.wMonth
= getDH();
1564 SystemTime
.wDay
= getDL();
1566 /* Return success or failure */
1567 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1571 /* Get system time */
1574 GetLocalTime(&SystemTime
);
1575 setCX(MAKEWORD(SystemTime
.wMinute
, SystemTime
.wHour
));
1576 setDX(MAKEWORD(SystemTime
.wMilliseconds
/ 10, SystemTime
.wSecond
));
1580 /* Set system time */
1583 GetLocalTime(&SystemTime
);
1584 SystemTime
.wHour
= getCH();
1585 SystemTime
.wMinute
= getCL();
1586 SystemTime
.wSecond
= getDH();
1587 SystemTime
.wMilliseconds
= getDL() * 10; // In hundredths of seconds
1589 /* Return success or failure */
1590 setAL(SetLocalTime(&SystemTime
) ? 0x00 : 0xFF);
1594 /* Get Disk Transfer Area */
1597 setES(HIWORD(DiskTransferArea
));
1598 setBX(LOWORD(DiskTransferArea
));
1602 /* Get DOS Version */
1605 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1607 if (LOBYTE(PspBlock
->DosVersion
) < 5 || getAL() == 0)
1609 /* Return DOS 24-bit user serial number in BL:CX */
1614 if (LOBYTE(PspBlock
->DosVersion
) >= 5 && getAL() == 1)
1617 * Return DOS OEM number:
1618 * 0x00 for IBM PC-DOS
1624 /* Return DOS version: Minor:Major in AH:AL */
1625 setAX(PspBlock
->DosVersion
);
1630 /* Get Interrupt Vector */
1633 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[getAL()];
1635 /* Read the address from the IDT into ES:BX */
1636 setES(HIWORD(FarPointer
));
1637 setBX(LOWORD(FarPointer
));
1641 /* Create Directory */
1644 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1646 if (CreateDirectoryA(String
, NULL
))
1648 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1652 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1653 setAX(LOWORD(GetLastError()));
1659 /* Remove Directory */
1662 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1664 if (RemoveDirectoryA(String
))
1666 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1670 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1671 setAX(LOWORD(GetLastError()));
1677 /* Set Current Directory */
1680 String
= (PCHAR
)SEG_OFF_TO_PTR(getDS(), getDX());
1682 if (DosChangeDirectory(String
))
1684 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1688 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1689 setAX(DosLastError
);
1699 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1700 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1705 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1710 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1721 WORD ErrorCode
= DosOpenFile(&FileHandle
,
1722 (LPCSTR
)SEG_OFF_TO_PTR(getDS(), getDX()),
1727 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1732 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1742 if (DosCloseHandle(getBX()))
1744 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1748 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1749 setAX(ERROR_INVALID_HANDLE
);
1758 WORD Handle
= getBX();
1759 LPBYTE Buffer
= (LPBYTE
)SEG_OFF_TO_PTR(getDS(), getDX());
1760 WORD Count
= getCX();
1762 WORD ErrorCode
= ERROR_SUCCESS
;
1764 if (IsConsoleHandle(DosGetRealHandle(Handle
)))
1766 while (Stack
[STACK_COUNTER
] < Count
)
1768 /* Read a character from the BIOS */
1769 Buffer
[Stack
[STACK_COUNTER
]] = LOBYTE(BiosGetCharacter()); // FIXME: Security checks!
1771 /* Stop if the BOP needs to be repeated */
1772 if (EmulatorGetFlag(EMULATOR_FLAG_CF
)) break;
1774 /* Increment the counter */
1775 Stack
[STACK_COUNTER
]++;
1778 if (Stack
[STACK_COUNTER
] < Count
) ErrorCode
= ERROR_NOT_READY
;
1779 else BytesRead
= Count
;
1783 /* Use the file reading function */
1784 ErrorCode
= DosReadFile(Handle
, Buffer
, Count
, &BytesRead
);
1789 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1792 else if (ErrorCode
!= ERROR_NOT_READY
)
1794 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1803 WORD BytesWritten
= 0;
1804 WORD ErrorCode
= DosWriteFile(getBX(),
1805 SEG_OFF_TO_PTR(getDS(), getDX()),
1811 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1812 setAX(BytesWritten
);
1816 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1826 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1828 /* Call the API function */
1829 if (DeleteFileA(FileName
))
1831 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1835 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1836 setAX(GetLastError());
1846 WORD ErrorCode
= DosSeekFile(getBX(),
1847 MAKELONG(getDX(), getCX()),
1853 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1855 /* Return the new offset in DX:AX */
1856 setDX(HIWORD(NewLocation
));
1857 setAX(LOWORD(NewLocation
));
1861 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1868 /* Get/Set File Attributes */
1872 LPSTR FileName
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getDX());
1874 if (getAL() == 0x00)
1876 /* Get the attributes */
1877 Attributes
= GetFileAttributesA(FileName
);
1879 /* Check if it failed */
1880 if (Attributes
== INVALID_FILE_ATTRIBUTES
)
1882 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1883 setAX(GetLastError());
1887 /* Return the attributes that DOS can understand */
1888 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1889 setCL(LOBYTE(Attributes
));
1891 else if (getAL() == 0x01)
1893 /* Try to set the attributes */
1894 if (SetFileAttributesA(FileName
, getCL()))
1896 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1900 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1901 setAX(GetLastError());
1906 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1907 setAX(ERROR_INVALID_FUNCTION
);
1916 if (DosHandleIoctl(getAL(), getBX()))
1918 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1922 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1923 setAX(DosLastError
);
1929 /* Duplicate Handle */
1933 HANDLE Handle
= DosGetRealHandle(getBX());
1935 if (Handle
!= INVALID_HANDLE_VALUE
)
1937 /* The handle is invalid */
1938 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1939 setAX(ERROR_INVALID_HANDLE
);
1943 /* Open a new handle to the same entry */
1944 NewHandle
= DosOpenHandle(Handle
);
1946 if (NewHandle
== INVALID_DOS_HANDLE
)
1948 /* Too many files open */
1949 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1950 setAX(ERROR_TOO_MANY_OPEN_FILES
);
1954 /* Return the result */
1955 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1960 /* Force Duplicate Handle */
1963 if (DosDuplicateHandle(getBX(), getCX()))
1965 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1969 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1970 setAX(ERROR_INVALID_HANDLE
);
1976 /* Allocate Memory */
1979 WORD MaxAvailable
= 0;
1980 WORD Segment
= DosAllocateMemory(getBX(), &MaxAvailable
);
1984 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
1989 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
1990 setAX(DosLastError
);
1991 setBX(MaxAvailable
);
2000 if (DosFreeMemory(getES()))
2002 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2006 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2007 setAX(ERROR_ARENA_TRASHED
);
2013 /* Resize Memory Block */
2018 if (DosResizeMemory(getES(), getBX(), &Size
))
2020 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2024 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2025 setAX(DosLastError
);
2032 /* Terminate With Return Code */
2035 DosTerminateProcess(CurrentPsp
, getAL());
2039 /* Get Current Process */
2046 /* Get/Set Memory Management Options */
2049 if (getAL() == 0x00)
2051 /* Get allocation strategy */
2052 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2053 setAX(DosAllocStrategy
);
2055 else if (getAL() == 0x01)
2057 /* Set allocation strategy */
2059 if ((getBL() & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2060 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
2062 /* Can't set both */
2063 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2064 setAX(ERROR_INVALID_PARAMETER
);
2068 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT
)
2070 /* Invalid allocation strategy */
2071 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2072 setAX(ERROR_INVALID_PARAMETER
);
2076 DosAllocStrategy
= getBL();
2077 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2079 else if (getAL() == 0x02)
2081 /* Get UMB link state */
2082 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2083 setAL(DosUmbLinked
? 0x01 : 0x00);
2085 else if (getAL() == 0x03)
2087 /* Set UMB link state */
2088 if (getBX()) DosLinkUmb();
2089 else DosUnlinkUmb();
2090 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
2094 /* Invalid or unsupported function */
2095 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2096 setAX(ERROR_INVALID_FUNCTION
);
2105 DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", getAH());
2106 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
2111 VOID
DosBreakInterrupt(LPWORD Stack
)
2113 UNREFERENCED_PARAMETER(Stack
);
2118 BOOLEAN
DosInitialize(VOID
)
2121 PDOS_MCB Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
);
2124 LPWSTR SourcePtr
, Environment
;
2126 LPSTR DestPtr
= (LPSTR
)SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK
, 0);
2128 CHAR CurrentDirectory
[MAX_PATH
];
2129 CHAR DosDirectory
[DOS_DIR_LENGTH
];
2132 /* Initialize the MCB */
2133 Mcb
->BlockType
= 'Z';
2134 Mcb
->Size
= USER_MEMORY_SIZE
;
2137 /* Initialize the link MCB to the UMB area */
2138 Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
+ 1);
2139 Mcb
->BlockType
= 'M';
2140 Mcb
->Size
= UMB_START_SEGMENT
- FIRST_MCB_SEGMENT
- USER_MEMORY_SIZE
- 2;
2141 Mcb
->OwnerPsp
= SYSTEM_PSP
;
2143 /* Initialize the UMB area */
2144 Mcb
= SEGMENT_TO_MCB(UMB_START_SEGMENT
);
2145 Mcb
->BlockType
= 'Z';
2146 Mcb
->Size
= UMB_END_SEGMENT
- UMB_START_SEGMENT
;
2149 /* Get the environment strings */
2150 SourcePtr
= Environment
= GetEnvironmentStringsW();
2151 if (Environment
== NULL
) return FALSE
;
2153 /* Fill the DOS system environment block */
2156 /* Get the size of the ASCII string */
2157 AsciiSize
= WideCharToMultiByte(CP_ACP
,
2166 /* Allocate memory for the ASCII string */
2167 AsciiString
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, AsciiSize
);
2168 if (AsciiString
== NULL
)
2170 FreeEnvironmentStringsW(Environment
);
2174 /* Convert to ASCII */
2175 WideCharToMultiByte(CP_ACP
,
2184 /* Copy the string into DOS memory */
2185 strcpy(DestPtr
, AsciiString
);
2187 /* Move to the next string */
2188 SourcePtr
+= wcslen(SourcePtr
) + 1;
2189 DestPtr
+= strlen(AsciiString
);
2192 /* Free the memory */
2193 HeapFree(GetProcessHeap(), 0, AsciiString
);
2197 /* Free the memory allocated for environment strings */
2198 FreeEnvironmentStringsW(Environment
);
2200 /* Clear the current directory buffer */
2201 ZeroMemory(CurrentDirectories
, sizeof(CurrentDirectories
));
2203 /* Get the current directory */
2204 if (!GetCurrentDirectoryA(MAX_PATH
, CurrentDirectory
))
2206 // TODO: Use some kind of default path?
2210 /* Convert that to a DOS path */
2211 if (!GetShortPathNameA(CurrentDirectory
, DosDirectory
, DOS_DIR_LENGTH
))
2213 // TODO: Use some kind of default path?
2218 CurrentDrive
= DosDirectory
[0] - 'A';
2221 Path
= strchr(DosDirectory
, '\\');
2224 /* Skip the backslash */
2228 /* Set the directory */
2229 if (Path
!= NULL
) strcpy(CurrentDirectories
[CurrentDrive
], Path
);
2231 /* Read CONFIG.SYS */
2232 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
2235 while (fgetws(Buffer
, 256, Stream
))
2237 // TODO: Parse the line
2242 /* Initialize the SFT */
2243 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
2245 DosSystemFileTable
[i
] = INVALID_HANDLE_VALUE
;
2246 DosSftRefCount
[i
] = 0;
2249 /* Get handles to standard I/O devices */
2250 DosSystemFileTable
[0] = GetStdHandle(STD_INPUT_HANDLE
);
2251 DosSystemFileTable
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
2252 DosSystemFileTable
[2] = GetStdHandle(STD_ERROR_HANDLE
);