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 *******************************************************************/
15 /* PRIVATE VARIABLES **********************************************************/
17 static WORD CurrentPsp
= SYSTEM_PSP
;
18 static DWORD DiskTransferArea
;
19 static HANDLE DosSystemFileTable
[DOS_SFT_SIZE
];
20 static WORD DosSftRefCount
[DOS_SFT_SIZE
];
22 /* PRIVATE FUNCTIONS **********************************************************/
24 static VOID
DosCombineFreeBlocks(WORD StartBlock
)
26 PDOS_MCB CurrentMcb
= SEGMENT_TO_MCB(StartBlock
), NextMcb
;
28 /* If this is the last block or it's not free, quit */
29 if (CurrentMcb
->BlockType
== 'Z' || CurrentMcb
->OwnerPsp
!= 0) return;
33 /* Get a pointer to the next MCB */
34 NextMcb
= SEGMENT_TO_MCB(StartBlock
+ CurrentMcb
->Size
+ 1);
36 /* Check if the next MCB is free */
37 if (NextMcb
->OwnerPsp
== 0)
40 CurrentMcb
->Size
+= NextMcb
->Size
+ 1;
41 CurrentMcb
->BlockType
= NextMcb
->BlockType
;
42 NextMcb
->BlockType
= 'I';
46 /* No more adjoining free blocks */
52 static WORD
DosCopyEnvironmentBlock(WORD SourceSegment
)
54 PCHAR Ptr
, SourceBuffer
, DestBuffer
= NULL
;
58 Ptr
= SourceBuffer
= (PCHAR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(SourceSegment
, 0));
60 /* Calculate the size of the environment block */
63 TotalSize
+= strlen(Ptr
) + 1;
64 Ptr
+= strlen(Ptr
) + 1;
68 /* Allocate the memory for the environment block */
69 DestSegment
= DosAllocateMemory((TotalSize
+ 0x0F) >> 4, NULL
);
70 if (!DestSegment
) return 0;
74 DestBuffer
= (PCHAR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(DestSegment
, 0));
78 strcpy(DestBuffer
, Ptr
);
80 /* Advance to the next string */
81 Ptr
+= strlen(Ptr
) + 1;
82 DestBuffer
+= strlen(Ptr
);
84 /* Put a zero after the string */
88 /* Set the final zero */
94 static VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
96 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
98 /* Just set the owner */
99 Mcb
->OwnerPsp
= NewOwner
;
102 static WORD
DosOpenHandle(HANDLE Handle
)
109 /* The system PSP has no handle table */
110 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
112 /* Get a pointer to the handle table */
113 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
114 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
116 /* Find a free entry in the JFT */
117 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
119 if (HandleTable
[DosHandle
] == 0xFF) break;
122 /* If there are no free entries, fail */
123 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
125 /* Check if the handle is already in the SFT */
126 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
128 /* Check if this is the same handle */
129 if (DosSystemFileTable
[i
] != Handle
) continue;
131 /* Already in the table, reference it */
134 /* Set the JFT entry to that SFT index */
135 HandleTable
[DosHandle
] = i
;
137 /* Return the new handle */
141 /* Add the handle to the SFT */
142 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
144 /* Make sure this is an empty table entry */
145 if (DosSystemFileTable
[i
] != INVALID_HANDLE_VALUE
) continue;
147 /* Initialize the empty table entry */
148 DosSystemFileTable
[i
] = Handle
;
149 DosSftRefCount
[i
] = 1;
151 /* Set the JFT entry to that SFT index */
152 HandleTable
[DosHandle
] = i
;
154 /* Return the new handle */
158 /* The SFT is full */
159 return INVALID_DOS_HANDLE
;
162 static HANDLE
DosGetRealHandle(WORD DosHandle
)
167 /* The system PSP has no handle table */
168 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_HANDLE_VALUE
;
170 /* Get a pointer to the handle table */
171 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
172 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
174 /* Make sure the handle is open */
175 if (HandleTable
[DosHandle
] == 0xFF) return INVALID_HANDLE_VALUE
;
177 /* Return the Win32 handle */
178 return DosSystemFileTable
[HandleTable
[DosHandle
]];
181 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
187 /* Clear the table first */
188 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
190 /* Check if this is the initial process */
191 if (CurrentPsp
== SYSTEM_PSP
)
193 /* Set up the standard I/O devices */
194 for (i
= 0; i
<= 2; i
++)
196 /* Set the index in the SFT */
197 DestinationTable
[i
] = i
;
199 /* Increase the reference count */
207 /* Get the parent PSP block and handle table */
208 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
209 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
211 /* Copy the first 20 handles into the new table */
212 for (i
= 0; i
< 20; i
++)
214 DestinationTable
[i
] = SourceTable
[i
];
216 /* Increase the reference count */
217 DosSftRefCount
[SourceTable
[i
]]++;
221 /* PUBLIC FUNCTIONS ***********************************************************/
223 WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
225 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
226 PDOS_MCB CurrentMcb
, NextMcb
;
230 /* Get a pointer to the MCB */
231 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
233 /* Make sure it's valid */
234 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
239 /* Only check free blocks */
240 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
242 /* Combine this free block with adjoining free blocks */
243 DosCombineFreeBlocks(Segment
);
245 /* Update the maximum block size */
246 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
248 /* Check if this block is big enough */
249 if (CurrentMcb
->Size
< Size
) goto Next
;
251 /* It is, update the smallest found so far */
252 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
258 /* If this was the last MCB in the chain, quit */
259 if (CurrentMcb
->BlockType
== 'Z') break;
261 /* Otherwise, update the segment and continue */
262 Segment
+= CurrentMcb
->Size
+ 1;
265 /* If we didn't find a free block, return 0 */
268 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
272 /* Get a pointer to the MCB */
273 CurrentMcb
= SEGMENT_TO_MCB(Result
);
275 /* Check if the block is larger than requested */
276 if (CurrentMcb
->Size
> Size
)
278 /* It is, split it into two blocks */
279 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
281 /* Initialize the new MCB structure */
282 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
283 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
284 NextMcb
->OwnerPsp
= 0;
286 /* Update the current block */
287 CurrentMcb
->BlockType
= 'M';
288 CurrentMcb
->Size
= Size
;
291 /* Take ownership of the block */
292 CurrentMcb
->OwnerPsp
= CurrentPsp
;
294 /* Return the segment of the data portion of the block */
298 BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
300 BOOLEAN Success
= TRUE
;
301 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
302 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
304 /* Make sure this is a valid, allocated block */
305 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
311 ReturnSize
= Mcb
->Size
;
313 /* Check if we need to expand or contract the block */
314 if (NewSize
> Mcb
->Size
)
316 /* We can't expand the last block */
317 if (Mcb
->BlockType
!= 'M')
323 /* Get the pointer and segment of the next MCB */
324 NextSegment
= Segment
+ Mcb
->Size
+ 1;
325 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
327 /* Make sure the next segment is free */
328 if (NextMcb
->OwnerPsp
!= 0)
334 /* Combine this free block with adjoining free blocks */
335 DosCombineFreeBlocks(NextSegment
);
337 /* Set the maximum possible size of the block */
338 ReturnSize
+= NextMcb
->Size
+ 1;
340 /* Maximize the current block */
341 Mcb
->Size
= ReturnSize
;
342 Mcb
->BlockType
= NextMcb
->BlockType
;
344 /* Invalidate the next block */
345 NextMcb
->BlockType
= 'I';
347 /* Check if the block is larger than requested */
348 if (Mcb
->Size
> NewSize
)
350 /* It is, split it into two blocks */
351 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
353 /* Initialize the new MCB structure */
354 NextMcb
->BlockType
= Mcb
->BlockType
;
355 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
356 NextMcb
->OwnerPsp
= 0;
358 /* Update the current block */
359 Mcb
->BlockType
= 'M';
363 else if (NewSize
< Mcb
->Size
)
365 /* Just split the block */
366 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
367 NextMcb
->BlockType
= Mcb
->BlockType
;
368 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
369 NextMcb
->OwnerPsp
= 0;
372 Mcb
->BlockType
= 'M';
377 /* Check if the operation failed */
380 /* Return the maximum possible size */
381 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
387 BOOLEAN
DosFreeMemory(WORD BlockData
)
389 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
391 /* Make sure the MCB is valid */
392 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') return FALSE
;
394 /* Mark the block as free */
400 WORD
DosCreateFile(LPWORD Handle
, LPCSTR FilePath
, WORD Attributes
)
405 /* Create the file */
406 FileHandle
= CreateFileA(FilePath
,
407 GENERIC_READ
| GENERIC_WRITE
,
408 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
414 if (FileHandle
== INVALID_HANDLE_VALUE
)
416 /* Return the error code */
417 return GetLastError();
420 /* Open the DOS handle */
421 DosHandle
= DosOpenHandle(FileHandle
);
423 if (DosHandle
== INVALID_DOS_HANDLE
)
425 /* Close the handle */
426 CloseHandle(FileHandle
);
428 /* Return the error code */
429 return ERROR_TOO_MANY_OPEN_FILES
;
432 /* It was successful */
434 return ERROR_SUCCESS
;
437 WORD
DosOpenFile(LPWORD Handle
, LPCSTR FilePath
, BYTE AccessMode
)
440 ACCESS_MASK Access
= 0;
443 /* Parse the access mode */
444 switch (AccessMode
& 3)
449 Access
= GENERIC_READ
;
456 Access
= GENERIC_WRITE
;
463 Access
= GENERIC_READ
| GENERIC_WRITE
;
470 return ERROR_INVALID_PARAMETER
;
475 FileHandle
= CreateFileA(FilePath
,
477 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
480 FILE_ATTRIBUTE_NORMAL
,
483 if (FileHandle
== INVALID_HANDLE_VALUE
)
485 /* Return the error code */
486 return GetLastError();
489 /* Open the DOS handle */
490 DosHandle
= DosOpenHandle(FileHandle
);
492 if (DosHandle
== INVALID_DOS_HANDLE
)
494 /* Close the handle */
495 CloseHandle(FileHandle
);
497 /* Return the error code */
498 return ERROR_TOO_MANY_OPEN_FILES
;
501 /* It was successful */
503 return ERROR_SUCCESS
;
506 WORD
DosReadFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesRead
)
508 WORD Result
= ERROR_SUCCESS
;
509 DWORD BytesRead32
= 0;
510 HANDLE Handle
= DosGetRealHandle(FileHandle
);
512 /* Make sure the handle is valid */
513 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_PARAMETER
;
516 if (!ReadFile(Handle
, Buffer
, Count
, &BytesRead32
, NULL
))
518 /* Store the error code */
519 Result
= GetLastError();
522 /* The number of bytes read is always 16-bit */
523 *BytesRead
= LOWORD(BytesRead32
);
525 /* Return the error code */
529 WORD
DosWriteFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesWritten
)
531 WORD Result
= ERROR_SUCCESS
;
532 DWORD BytesWritten32
= 0;
533 HANDLE Handle
= DosGetRealHandle(FileHandle
);
535 /* Make sure the handle is valid */
536 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_PARAMETER
;
539 if (!WriteFile(Handle
, Buffer
, Count
, &BytesWritten32
, NULL
))
541 /* Store the error code */
542 Result
= GetLastError();
545 /* The number of bytes written is always 16-bit */
546 *BytesWritten
= LOWORD(BytesWritten32
);
548 /* Return the error code */
552 BOOLEAN
DosCloseHandle(WORD DosHandle
)
558 /* The system PSP has no handle table */
559 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
561 /* Get a pointer to the handle table */
562 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
563 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
565 /* Make sure the handle is open */
566 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
568 /* Decrement the reference count of the SFT entry */
569 SftIndex
= HandleTable
[DosHandle
];
570 DosSftRefCount
[SftIndex
]--;
572 /* Check if the reference count fell to zero */
573 if (!DosSftRefCount
[SftIndex
])
575 /* Close the file, it's no longer needed */
576 CloseHandle(DosSystemFileTable
[SftIndex
]);
578 /* Clear the handle */
579 DosSystemFileTable
[SftIndex
] = INVALID_HANDLE_VALUE
;
585 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
587 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
588 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
590 ZeroMemory(PspBlock
, sizeof(DOS_PSP
));
592 /* Set the exit interrupt */
593 PspBlock
->Exit
[0] = 0xCD; // int 0x20
594 PspBlock
->Exit
[1] = 0x20;
596 /* Set the number of the last paragraph */
597 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
599 /* Save the interrupt vectors */
600 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
601 PspBlock
->BreakAddress
= IntVecTable
[0x23];
602 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
604 /* Set the parent PSP */
605 PspBlock
->ParentPsp
= CurrentPsp
;
607 /* Copy the parent handle table */
608 DosCopyHandleTable(PspBlock
->HandleTable
);
610 /* Set the environment block */
611 PspBlock
->EnvBlock
= Environment
;
613 /* Set the handle table pointers to the internal handle table */
614 PspBlock
->HandleTableSize
= 20;
615 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
617 /* Set the DOS version */
618 PspBlock
->DosVersion
= DOS_VERSION
;
620 /* Set the far call opcodes */
621 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
622 PspBlock
->FarCall
[1] = 0x21;
623 PspBlock
->FarCall
[2] = 0xCB; // retf
625 /* Set the command line */
626 PspBlock
->CommandLineSize
= strlen(CommandLine
);
627 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
628 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
631 BOOLEAN
DosCreateProcess(LPCSTR CommandLine
, WORD EnvBlock
)
633 BOOLEAN Success
= FALSE
, AllocatedEnvBlock
= FALSE
;
634 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
635 LPBYTE Address
= NULL
;
636 LPSTR ProgramFilePath
, Parameters
[128];
637 CHAR CommandLineCopy
[128];
641 DWORD i
, FileSize
, ExeSize
;
642 PIMAGE_DOS_HEADER Header
;
643 PDWORD RelocationTable
;
646 /* Save a copy of the command line */
647 strcpy(CommandLineCopy
, CommandLine
);
649 /* Get the file name of the executable */
650 ProgramFilePath
= strtok(CommandLineCopy
, " \t");
652 /* Load the parameters in the local array */
653 while ((ParamCount
< 256)
654 && ((Parameters
[ParamCount
] = strtok(NULL
, " \t")) != NULL
))
659 /* Open a handle to the executable */
660 FileHandle
= CreateFileA(ProgramFilePath
,
665 FILE_ATTRIBUTE_NORMAL
,
667 if (FileHandle
== INVALID_HANDLE_VALUE
) goto Cleanup
;
669 /* Get the file size */
670 FileSize
= GetFileSize(FileHandle
, NULL
);
672 /* Create a mapping object for the file */
673 FileMapping
= CreateFileMapping(FileHandle
,
679 if (FileMapping
== NULL
) goto Cleanup
;
681 /* Map the file into memory */
682 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
683 if (Address
== NULL
) goto Cleanup
;
685 /* Did we get an environment segment? */
688 /* Set a flag to know if the environment block was allocated here */
689 AllocatedEnvBlock
= TRUE
;
691 /* No, copy the one from the parent */
692 EnvBlock
= DosCopyEnvironmentBlock((CurrentPsp
!= SYSTEM_PSP
)
693 ? SEGMENT_TO_PSP(CurrentPsp
)->EnvBlock
697 /* Check if this is an EXE file or a COM file */
698 if (Address
[0] == 'M' && Address
[1] == 'Z')
702 /* Get the MZ header */
703 Header
= (PIMAGE_DOS_HEADER
)Address
;
705 /* Get the base size of the file, in paragraphs (rounded up) */
706 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
708 /* Add the PSP size, in paragraphs */
709 ExeSize
+= sizeof(DOS_PSP
) >> 4;
711 /* Add the maximum size that should be allocated */
712 ExeSize
+= Header
->e_maxalloc
;
714 /* Make sure it does not pass 0xFFFF */
715 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
717 /* Reduce the size one by one until the allocation is successful */
718 for (i
= Header
->e_maxalloc
; i
>= Header
->e_minalloc
; i
--, ExeSize
--)
720 /* Try to allocate that much memory */
721 Segment
= DosAllocateMemory(ExeSize
, NULL
);
722 if (Segment
!= 0) break;
725 /* Check if at least the lowest allocation was successful */
726 if (Segment
== 0) goto Cleanup
;
728 /* Initialize the PSP */
729 DosInitializePsp(Segment
,
734 /* The process owns its own memory */
735 DosChangeMemoryOwner(Segment
, Segment
);
736 DosChangeMemoryOwner(EnvBlock
, Segment
);
738 /* Copy the program to Segment:0100 */
739 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
740 + TO_LINEAR(Segment
, 0x100)),
741 Address
+ (Header
->e_cparhdr
<< 4),
742 min(FileSize
- (Header
->e_cparhdr
<< 4),
743 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
745 /* Get the relocation table */
746 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
748 /* Perform relocations */
749 for (i
= 0; i
< Header
->e_crlc
; i
++)
751 /* Get a pointer to the word that needs to be patched */
752 RelocWord
= (PWORD
)((ULONG_PTR
)BaseAddress
753 + TO_LINEAR(Segment
+ HIWORD(RelocationTable
[i
]),
754 0x100 + LOWORD(RelocationTable
[i
])));
756 /* Add the number of the EXE segment to it */
757 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
760 /* Set the initial segment registers */
761 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
762 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
764 /* Set the stack to the location from the header */
765 EmulatorSetStack(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
,
769 CurrentPsp
= Segment
;
770 DiskTransferArea
= MAKELONG(0x80, Segment
);
771 EmulatorExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
780 /* Find the maximum amount of memory that can be allocated */
781 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
783 /* Make sure it's enough for the whole program and the PSP */
784 if ((MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
))) goto Cleanup
;
786 /* Allocate all of it */
787 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
788 if (Segment
== 0) goto Cleanup
;
790 /* The process owns its own memory */
791 DosChangeMemoryOwner(Segment
, Segment
);
792 DosChangeMemoryOwner(EnvBlock
, Segment
);
794 /* Copy the program to Segment:0100 */
795 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
796 + TO_LINEAR(Segment
, 0x100)),
800 /* Initialize the PSP */
801 DosInitializePsp(Segment
,
803 (FileSize
+ sizeof(DOS_PSP
)) >> 4,
806 /* Set the initial segment registers */
807 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
808 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
810 /* Set the stack to the last word of the segment */
811 EmulatorSetStack(Segment
, 0xFFFE);
814 CurrentPsp
= Segment
;
815 DiskTransferArea
= MAKELONG(0x80, Segment
);
816 EmulatorExecute(Segment
, 0x100);
824 /* It was not successful, cleanup the DOS memory */
825 if (AllocatedEnvBlock
) DosFreeMemory(EnvBlock
);
826 if (Segment
) DosFreeMemory(Segment
);
830 if (Address
!= NULL
) UnmapViewOfFile(Address
);
832 /* Close the file mapping object */
833 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
835 /* Close the file handle */
836 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
841 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
)
843 WORD McbSegment
= FIRST_MCB_SEGMENT
;
845 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
846 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
848 /* Check if this PSP is it's own parent */
849 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
851 // TODO: Close all handles opened by the process
853 /* Free the memory used by the process */
856 /* Get a pointer to the MCB */
857 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
859 /* Make sure the MCB is valid */
860 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!='Z') break;
862 /* If this block was allocated by the process, free it */
863 if (CurrentMcb
->OwnerPsp
== Psp
) DosFreeMemory(McbSegment
);
865 /* If this was the last block, quit */
866 if (CurrentMcb
->BlockType
== 'Z') break;
868 /* Update the segment and continue */
869 McbSegment
+= CurrentMcb
->Size
+ 1;
873 /* Restore the interrupt vectors */
874 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
875 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
876 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
878 /* Update the current PSP */
879 if (Psp
== CurrentPsp
)
881 CurrentPsp
= PspBlock
->ParentPsp
;
882 if (CurrentPsp
== SYSTEM_PSP
) VdmRunning
= FALSE
;
885 /* Return control to the parent process */
886 EmulatorExecute(HIWORD(PspBlock
->TerminateAddress
),
887 LOWORD(PspBlock
->TerminateAddress
));
890 CHAR
DosReadCharacter()
892 CHAR Character
= '\0';
895 /* Use the file reading function */
896 DosReadFile(DOS_INPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesRead
);
901 VOID
DosPrintCharacter(CHAR Character
)
905 /* Use the file writing function */
906 DosWriteFile(DOS_OUTPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesWritten
);
909 VOID
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
911 HANDLE Handle
= DosGetRealHandle(FileHandle
);
913 if (Handle
== INVALID_HANDLE_VALUE
)
916 EmulatorSetFlag(EMULATOR_FLAG_CF
);
917 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_FILE_NOT_FOUND
);
922 /* Get Device Information */
927 if (Handle
== DosSystemFileTable
[0])
932 else if (Handle
== DosSystemFileTable
[1])
938 /* It is a character device */
941 /* Return the device information word */
942 EmulatorClearFlag(EMULATOR_FLAG_CF
);
943 EmulatorSetRegister(EMULATOR_REG_DX
, InfoWord
);
948 /* Unsupported control code */
951 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
953 EmulatorSetFlag(EMULATOR_FLAG_CF
);
954 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_PARAMETER
);
959 VOID
DosInt20h(WORD CodeSegment
)
961 /* This is the exit interrupt */
962 DosTerminateProcess(CodeSegment
, 0);
965 VOID
DosInt21h(WORD CodeSegment
)
969 SYSTEMTIME SystemTime
;
971 PDOS_INPUT_BUFFER InputBuffer
;
972 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
973 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
974 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
975 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
976 WORD DataSegment
= EmulatorGetRegister(EMULATOR_REG_DS
);
977 WORD ExtSegment
= EmulatorGetRegister(EMULATOR_REG_ES
);
979 /* Check the value in the AH register */
982 /* Terminate Program */
985 DosTerminateProcess(CodeSegment
, 0);
989 /* Read Character And Echo */
992 Character
= DosReadCharacter();
993 DosPrintCharacter(Character
);
994 EmulatorSetRegister(EMULATOR_REG_AX
, (Eax
& 0xFFFFFF00) | Character
);
998 /* Print Character */
1001 DosPrintCharacter(LOBYTE(Edx
));
1005 /* Read Character Without Echo */
1009 EmulatorSetRegister(EMULATOR_REG_AX
,
1010 (Eax
& 0xFFFFFF00) | DosReadCharacter());
1017 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1018 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1020 while ((*String
) != '$')
1022 DosPrintCharacter(*String
);
1029 /* Read Buffered Input */
1032 InputBuffer
= (PDOS_INPUT_BUFFER
)((ULONG_PTR
)BaseAddress
1033 + TO_LINEAR(DataSegment
,
1036 InputBuffer
->Length
= 0;
1037 for (i
= 0; i
< InputBuffer
->MaxLength
; i
++)
1039 Character
= DosReadCharacter();
1040 DosPrintCharacter(Character
);
1041 InputBuffer
->Buffer
[InputBuffer
->Length
] = Character
;
1042 if (Character
== '\r') break;
1043 InputBuffer
->Length
++;
1049 /* Set Disk Transfer Area */
1052 DiskTransferArea
= MAKELONG(LOWORD(Edx
), DataSegment
);
1056 /* Set Interrupt Vector */
1059 DWORD FarPointer
= MAKELONG(LOWORD(Edx
), DataSegment
);
1061 /* Write the new far pointer to the IDT */
1062 ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)] = FarPointer
;
1067 /* Get system date */
1070 GetLocalTime(&SystemTime
);
1071 EmulatorSetRegister(EMULATOR_REG_CX
,
1072 (Ecx
& 0xFFFF0000) | SystemTime
.wYear
);
1073 EmulatorSetRegister(EMULATOR_REG_DX
,
1075 | (SystemTime
.wMonth
<< 8)
1077 EmulatorSetRegister(EMULATOR_REG_AX
,
1078 (Eax
& 0xFFFFFF00) | SystemTime
.wDayOfWeek
);
1082 /* Set system date */
1085 GetLocalTime(&SystemTime
);
1086 SystemTime
.wYear
= LOWORD(Ecx
);
1087 SystemTime
.wMonth
= HIBYTE(Edx
);
1088 SystemTime
.wDay
= LOBYTE(Edx
);
1090 if (SetLocalTime(&SystemTime
))
1092 /* Return success */
1093 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
1097 /* Return failure */
1098 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
1104 /* Get system time */
1107 GetLocalTime(&SystemTime
);
1108 EmulatorSetRegister(EMULATOR_REG_CX
,
1110 | (SystemTime
.wHour
<< 8)
1111 | SystemTime
.wMinute
);
1112 EmulatorSetRegister(EMULATOR_REG_DX
,
1114 | (SystemTime
.wSecond
<< 8)
1115 | (SystemTime
.wMilliseconds
/ 10));
1119 /* Set system time */
1122 GetLocalTime(&SystemTime
);
1123 SystemTime
.wHour
= HIBYTE(Ecx
);
1124 SystemTime
.wMinute
= LOBYTE(Ecx
);
1125 SystemTime
.wSecond
= HIBYTE(Edx
);
1126 SystemTime
.wMilliseconds
= LOBYTE(Edx
) * 10;
1128 if (SetLocalTime(&SystemTime
))
1130 /* Return success */
1131 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
1135 /* Return failure */
1136 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
1142 /* Get Disk Transfer Area */
1145 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(DiskTransferArea
));
1146 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(DiskTransferArea
));
1151 /* Get DOS Version */
1154 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1156 EmulatorSetRegister(EMULATOR_REG_AX
, PspBlock
->DosVersion
);
1160 /* Get Interrupt Vector */
1163 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)];
1165 /* Read the address from the IDT into ES:BX */
1166 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(FarPointer
));
1167 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(FarPointer
));
1172 /* Create Directory */
1175 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1176 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1178 if (CreateDirectoryA(String
, NULL
))
1180 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1184 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1185 EmulatorSetRegister(EMULATOR_REG_AX
,
1186 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1192 /* Remove Directory */
1195 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1196 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1198 if (RemoveDirectoryA(String
))
1200 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1204 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1205 EmulatorSetRegister(EMULATOR_REG_AX
,
1206 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1213 /* Set Current Directory */
1216 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1217 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1219 if (SetCurrentDirectoryA(String
))
1221 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1225 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1226 EmulatorSetRegister(EMULATOR_REG_AX
,
1227 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1237 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1238 (LPCSTR
)(ULONG_PTR
)BaseAddress
1239 + TO_LINEAR(DataSegment
, LOWORD(Edx
)),
1245 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1247 /* Return the handle in AX */
1248 EmulatorSetRegister(EMULATOR_REG_AX
,
1249 (Eax
& 0xFFFF0000) | FileHandle
);
1254 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1256 /* Return the error code in AX */
1257 EmulatorSetRegister(EMULATOR_REG_AX
,
1258 (Eax
& 0xFFFF0000) | ErrorCode
);
1268 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1269 (LPCSTR
)(ULONG_PTR
)BaseAddress
1270 + TO_LINEAR(DataSegment
, LOWORD(Edx
)),
1276 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1278 /* Return the handle in AX */
1279 EmulatorSetRegister(EMULATOR_REG_AX
,
1280 (Eax
& 0xFFFF0000) | FileHandle
);
1285 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1287 /* Return the error code in AX */
1288 EmulatorSetRegister(EMULATOR_REG_AX
,
1289 (Eax
& 0xFFFF0000) | ErrorCode
);
1298 if (DosCloseHandle(LOWORD(Ebx
)))
1301 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1306 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1308 /* Return the error code in AX */
1309 EmulatorSetRegister(EMULATOR_REG_AX
,
1310 (Eax
& 0xFFFF0000) | ERROR_INVALID_PARAMETER
);
1320 WORD ErrorCode
= DosReadFile(LOWORD(Ebx
),
1321 (LPVOID
)((ULONG_PTR
)BaseAddress
1322 + TO_LINEAR(DataSegment
, LOWORD(Edx
))),
1329 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1331 /* Return the number of bytes read in AX */
1332 EmulatorSetRegister(EMULATOR_REG_AX
,
1333 (Eax
& 0xFFFF0000) | BytesRead
);
1338 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1340 /* Return the error code in AX */
1341 EmulatorSetRegister(EMULATOR_REG_AX
,
1342 (Eax
& 0xFFFF0000) | ErrorCode
);
1350 WORD BytesWritten
= 0;
1351 WORD ErrorCode
= DosWriteFile(LOWORD(Ebx
),
1352 (LPVOID
)((ULONG_PTR
)BaseAddress
1353 + TO_LINEAR(DataSegment
, LOWORD(Edx
))),
1360 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1362 /* Return the number of bytes written in AX */
1363 EmulatorSetRegister(EMULATOR_REG_AX
,
1364 (Eax
& 0xFFFF0000) | BytesWritten
);
1369 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1371 /* Return the error code in AX */
1372 EmulatorSetRegister(EMULATOR_REG_AX
,
1373 (Eax
& 0xFFFF0000) | ErrorCode
);
1382 DosHandleIoctl(LOBYTE(Eax
), LOWORD(Ebx
));
1387 /* Allocate Memory */
1390 WORD MaxAvailable
= 0;
1391 WORD Segment
= DosAllocateMemory(LOWORD(Ebx
), &MaxAvailable
);
1395 EmulatorSetRegister(EMULATOR_REG_AX
, Segment
);
1396 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1400 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_NOT_ENOUGH_MEMORY
);
1401 EmulatorSetRegister(EMULATOR_REG_BX
, MaxAvailable
);
1402 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1411 if (DosFreeMemory(ExtSegment
))
1413 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1415 else EmulatorSetFlag(EMULATOR_FLAG_CF
);
1420 /* Resize Memory Block */
1425 if (DosResizeMemory(ExtSegment
, LOWORD(Ebx
), &Size
))
1427 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1431 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1432 EmulatorSetRegister(EMULATOR_REG_BX
, Size
);
1438 /* Terminate With Return Code */
1441 DosTerminateProcess(CurrentPsp
, LOBYTE(Eax
));
1448 DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax
));
1449 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1454 VOID
DosBreakInterrupt()
1459 BOOLEAN
DosInitialize()
1462 PDOS_MCB Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
);
1465 LPWSTR SourcePtr
, Environment
;
1467 LPSTR DestPtr
= (LPSTR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(SYSTEM_ENV_BLOCK
, 0));
1470 /* Initialize the MCB */
1471 Mcb
->BlockType
= 'Z';
1472 Mcb
->Size
= USER_MEMORY_SIZE
;
1475 /* Get the environment strings */
1476 SourcePtr
= Environment
= GetEnvironmentStringsW();
1477 if (Environment
== NULL
) return FALSE
;
1479 /* Fill the DOS system environment block */
1482 /* Get the size of the ASCII string */
1483 AsciiSize
= WideCharToMultiByte(CP_ACP
,
1492 /* Allocate memory for the ASCII string */
1493 AsciiString
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, AsciiSize
);
1494 if (AsciiString
== NULL
)
1496 FreeEnvironmentStringsW(Environment
);
1500 /* Convert to ASCII */
1501 WideCharToMultiByte(CP_ACP
,
1510 /* Copy the string into DOS memory */
1511 strcpy(DestPtr
, AsciiString
);
1513 /* Free the memory */
1514 HeapFree(GetProcessHeap(), 0, AsciiString
);
1516 /* Move to the next string */
1517 SourcePtr
+= wcslen(SourcePtr
) + 1;
1518 DestPtr
+= strlen(AsciiString
);
1523 /* Free the memory allocated for environment strings */
1524 FreeEnvironmentStringsW(Environment
);
1526 /* Read CONFIG.SYS */
1527 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
1530 while (fgetws(Buffer
, 256, Stream
))
1532 // TODO: Parse the line
1537 /* Initialize the SFT */
1538 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
1540 DosSystemFileTable
[i
] = INVALID_HANDLE_VALUE
;
1541 DosSftRefCount
[i
] = 0;
1544 /* Get handles to standard I/O devices */
1545 DosSystemFileTable
[0] = GetStdHandle(STD_INPUT_HANDLE
);
1546 DosSystemFileTable
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
1547 DosSystemFileTable
[2] = GetStdHandle(STD_ERROR_HANDLE
);