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
) + 1;
85 /* Set the final zero */
91 static VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
93 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
95 /* Just set the owner */
96 Mcb
->OwnerPsp
= NewOwner
;
99 static WORD
DosOpenHandle(HANDLE Handle
)
106 /* The system PSP has no handle table */
107 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
109 /* Get a pointer to the handle table */
110 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
111 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
113 /* Find a free entry in the JFT */
114 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
116 if (HandleTable
[DosHandle
] == 0xFF) break;
119 /* If there are no free entries, fail */
120 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
122 /* Check if the handle is already in the SFT */
123 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
125 /* Check if this is the same handle */
126 if (DosSystemFileTable
[i
] != Handle
) continue;
128 /* Already in the table, reference it */
131 /* Set the JFT entry to that SFT index */
132 HandleTable
[DosHandle
] = i
;
134 /* Return the new handle */
138 /* Add the handle to the SFT */
139 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
141 /* Make sure this is an empty table entry */
142 if (DosSystemFileTable
[i
] != INVALID_HANDLE_VALUE
) continue;
144 /* Initialize the empty table entry */
145 DosSystemFileTable
[i
] = Handle
;
146 DosSftRefCount
[i
] = 1;
148 /* Set the JFT entry to that SFT index */
149 HandleTable
[DosHandle
] = i
;
151 /* Return the new handle */
155 /* The SFT is full */
156 return INVALID_DOS_HANDLE
;
159 static HANDLE
DosGetRealHandle(WORD DosHandle
)
164 /* The system PSP has no handle table */
165 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_HANDLE_VALUE
;
167 /* Get a pointer to the handle table */
168 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
169 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
171 /* Make sure the handle is open */
172 if (HandleTable
[DosHandle
] == 0xFF) return INVALID_HANDLE_VALUE
;
174 /* Return the Win32 handle */
175 return DosSystemFileTable
[HandleTable
[DosHandle
]];
178 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
184 /* Clear the table first */
185 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
187 /* Check if this is the initial process */
188 if (CurrentPsp
== SYSTEM_PSP
)
190 /* Set up the standard I/O devices */
191 for (i
= 0; i
<= 2; i
++)
193 /* Set the index in the SFT */
194 DestinationTable
[i
] = i
;
196 /* Increase the reference count */
204 /* Get the parent PSP block and handle table */
205 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
206 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
208 /* Copy the first 20 handles into the new table */
209 for (i
= 0; i
< 20; i
++)
211 DestinationTable
[i
] = SourceTable
[i
];
213 /* Increase the reference count */
214 DosSftRefCount
[SourceTable
[i
]]++;
218 /* PUBLIC FUNCTIONS ***********************************************************/
220 WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
222 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
223 PDOS_MCB CurrentMcb
, NextMcb
;
227 /* Get a pointer to the MCB */
228 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
230 /* Make sure it's valid */
231 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
236 /* Only check free blocks */
237 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
239 /* Combine this free block with adjoining free blocks */
240 DosCombineFreeBlocks(Segment
);
242 /* Update the maximum block size */
243 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
245 /* Check if this block is big enough */
246 if (CurrentMcb
->Size
< Size
) goto Next
;
248 /* It is, update the smallest found so far */
249 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
255 /* If this was the last MCB in the chain, quit */
256 if (CurrentMcb
->BlockType
== 'Z') break;
258 /* Otherwise, update the segment and continue */
259 Segment
+= CurrentMcb
->Size
+ 1;
262 /* If we didn't find a free block, return 0 */
265 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
269 /* Get a pointer to the MCB */
270 CurrentMcb
= SEGMENT_TO_MCB(Result
);
272 /* Check if the block is larger than requested */
273 if (CurrentMcb
->Size
> Size
)
275 /* It is, split it into two blocks */
276 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
278 /* Initialize the new MCB structure */
279 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
280 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
281 NextMcb
->OwnerPsp
= 0;
283 /* Update the current block */
284 CurrentMcb
->BlockType
= 'M';
285 CurrentMcb
->Size
= Size
;
288 /* Take ownership of the block */
289 CurrentMcb
->OwnerPsp
= CurrentPsp
;
291 /* Return the segment of the data portion of the block */
295 BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
297 BOOLEAN Success
= TRUE
;
298 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
299 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
301 /* Make sure this is a valid, allocated block */
302 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
308 ReturnSize
= Mcb
->Size
;
310 /* Check if we need to expand or contract the block */
311 if (NewSize
> Mcb
->Size
)
313 /* We can't expand the last block */
314 if (Mcb
->BlockType
!= 'M')
320 /* Get the pointer and segment of the next MCB */
321 NextSegment
= Segment
+ Mcb
->Size
+ 1;
322 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
324 /* Make sure the next segment is free */
325 if (NextMcb
->OwnerPsp
!= 0)
331 /* Combine this free block with adjoining free blocks */
332 DosCombineFreeBlocks(NextSegment
);
334 /* Set the maximum possible size of the block */
335 ReturnSize
+= NextMcb
->Size
+ 1;
337 /* Maximize the current block */
338 Mcb
->Size
= ReturnSize
;
339 Mcb
->BlockType
= NextMcb
->BlockType
;
341 /* Invalidate the next block */
342 NextMcb
->BlockType
= 'I';
344 /* Check if the block is larger than requested */
345 if (Mcb
->Size
> NewSize
)
347 /* It is, split it into two blocks */
348 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
350 /* Initialize the new MCB structure */
351 NextMcb
->BlockType
= Mcb
->BlockType
;
352 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
353 NextMcb
->OwnerPsp
= 0;
355 /* Update the current block */
356 Mcb
->BlockType
= 'M';
360 else if (NewSize
< Mcb
->Size
)
362 /* Just split the block */
363 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
364 NextMcb
->BlockType
= Mcb
->BlockType
;
365 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
366 NextMcb
->OwnerPsp
= 0;
369 Mcb
->BlockType
= 'M';
374 /* Check if the operation failed */
377 /* Return the maximum possible size */
378 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
384 BOOLEAN
DosFreeMemory(WORD BlockData
)
386 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
388 /* Make sure the MCB is valid */
389 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') return FALSE
;
391 /* Mark the block as free */
397 WORD
DosCreateFile(LPWORD Handle
, LPCSTR FilePath
, WORD Attributes
)
402 /* Create the file */
403 FileHandle
= CreateFileA(FilePath
,
404 GENERIC_READ
| GENERIC_WRITE
,
405 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
411 if (FileHandle
== INVALID_HANDLE_VALUE
)
413 /* Return the error code */
414 return GetLastError();
417 /* Open the DOS handle */
418 DosHandle
= DosOpenHandle(FileHandle
);
420 if (DosHandle
== INVALID_DOS_HANDLE
)
422 /* Close the handle */
423 CloseHandle(FileHandle
);
425 /* Return the error code */
426 return ERROR_TOO_MANY_OPEN_FILES
;
429 /* It was successful */
431 return ERROR_SUCCESS
;
434 WORD
DosOpenFile(LPWORD Handle
, LPCSTR FilePath
, BYTE AccessMode
)
437 ACCESS_MASK Access
= 0;
440 /* Parse the access mode */
441 switch (AccessMode
& 3)
446 Access
= GENERIC_READ
;
453 Access
= GENERIC_WRITE
;
460 Access
= GENERIC_READ
| GENERIC_WRITE
;
467 return ERROR_INVALID_PARAMETER
;
472 FileHandle
= CreateFileA(FilePath
,
474 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
477 FILE_ATTRIBUTE_NORMAL
,
480 if (FileHandle
== INVALID_HANDLE_VALUE
)
482 /* Return the error code */
483 return GetLastError();
486 /* Open the DOS handle */
487 DosHandle
= DosOpenHandle(FileHandle
);
489 if (DosHandle
== INVALID_DOS_HANDLE
)
491 /* Close the handle */
492 CloseHandle(FileHandle
);
494 /* Return the error code */
495 return ERROR_TOO_MANY_OPEN_FILES
;
498 /* It was successful */
500 return ERROR_SUCCESS
;
503 WORD
DosReadFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesRead
)
505 WORD Result
= ERROR_SUCCESS
;
506 DWORD BytesRead32
= 0;
507 HANDLE Handle
= DosGetRealHandle(FileHandle
);
509 /* Make sure the handle is valid */
510 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_PARAMETER
;
513 if (!ReadFile(Handle
, Buffer
, Count
, &BytesRead32
, NULL
))
515 /* Store the error code */
516 Result
= GetLastError();
519 /* The number of bytes read is always 16-bit */
520 *BytesRead
= LOWORD(BytesRead32
);
522 /* Return the error code */
526 WORD
DosWriteFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesWritten
)
528 WORD Result
= ERROR_SUCCESS
;
529 DWORD BytesWritten32
= 0;
530 HANDLE Handle
= DosGetRealHandle(FileHandle
);
532 /* Make sure the handle is valid */
533 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_PARAMETER
;
536 if (!WriteFile(Handle
, Buffer
, Count
, &BytesWritten32
, NULL
))
538 /* Store the error code */
539 Result
= GetLastError();
542 /* The number of bytes written is always 16-bit */
543 *BytesWritten
= LOWORD(BytesWritten32
);
545 /* Return the error code */
549 BOOLEAN
DosCloseHandle(WORD DosHandle
)
555 /* The system PSP has no handle table */
556 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
558 /* Get a pointer to the handle table */
559 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
560 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
562 /* Make sure the handle is open */
563 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
565 /* Decrement the reference count of the SFT entry */
566 SftIndex
= HandleTable
[DosHandle
];
567 DosSftRefCount
[SftIndex
]--;
569 /* Check if the reference count fell to zero */
570 if (!DosSftRefCount
[SftIndex
])
572 /* Close the file, it's no longer needed */
573 CloseHandle(DosSystemFileTable
[SftIndex
]);
575 /* Clear the handle */
576 DosSystemFileTable
[SftIndex
] = INVALID_HANDLE_VALUE
;
582 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
584 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
585 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
587 ZeroMemory(PspBlock
, sizeof(DOS_PSP
));
589 /* Set the exit interrupt */
590 PspBlock
->Exit
[0] = 0xCD; // int 0x20
591 PspBlock
->Exit
[1] = 0x20;
593 /* Set the program size */
594 PspBlock
->MemSize
= ProgramSize
;
596 /* Save the interrupt vectors */
597 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
598 PspBlock
->BreakAddress
= IntVecTable
[0x23];
599 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
601 /* Set the parent PSP */
602 PspBlock
->ParentPsp
= CurrentPsp
;
604 /* Copy the parent handle table */
605 DosCopyHandleTable(PspBlock
->HandleTable
);
607 /* Set the environment block */
608 PspBlock
->EnvBlock
= Environment
;
610 /* Set the handle table pointers to the internal handle table */
611 PspBlock
->HandleTableSize
= 20;
612 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
614 /* Set the DOS version */
615 PspBlock
->DosVersion
= DOS_VERSION
;
617 /* Set the far call opcodes */
618 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
619 PspBlock
->FarCall
[1] = 0x21;
620 PspBlock
->FarCall
[2] = 0xCB; // retf
622 /* Set the command line */
623 PspBlock
->CommandLineSize
= strlen(CommandLine
);
624 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
625 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
628 BOOLEAN
DosCreateProcess(LPCSTR CommandLine
, WORD EnvBlock
)
630 BOOLEAN Success
= FALSE
, AllocatedEnvBlock
= FALSE
;
631 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
632 LPBYTE Address
= NULL
;
633 LPSTR ProgramFilePath
, Parameters
[128];
634 CHAR CommandLineCopy
[128];
636 WORD i
, Segment
= 0, FileSize
, ExeSize
;
637 PIMAGE_DOS_HEADER Header
;
638 PDWORD RelocationTable
;
641 /* Save a copy of the command line */
642 strcpy(CommandLineCopy
, CommandLine
);
644 /* Get the file name of the executable */
645 ProgramFilePath
= strtok(CommandLineCopy
, " \t");
647 /* Load the parameters in the local array */
648 while ((ParamCount
< 256)
649 && ((Parameters
[ParamCount
] = strtok(NULL
, " \t")) != NULL
))
654 /* Open a handle to the executable */
655 FileHandle
= CreateFileA(ProgramFilePath
,
660 FILE_ATTRIBUTE_NORMAL
,
662 if (FileHandle
== INVALID_HANDLE_VALUE
) goto Cleanup
;
664 /* Get the file size */
665 FileSize
= GetFileSize(FileHandle
, NULL
);
667 /* Create a mapping object for the file */
668 FileMapping
= CreateFileMapping(FileHandle
,
674 if (FileMapping
== NULL
) goto Cleanup
;
676 /* Map the file into memory */
677 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
678 if (Address
== NULL
) goto Cleanup
;
680 /* Did we get an environment segment? */
683 /* Set a flag to know if the environment block was allocated here */
684 AllocatedEnvBlock
= TRUE
;
686 /* No, copy the one from the parent */
687 EnvBlock
= DosCopyEnvironmentBlock((CurrentPsp
!= SYSTEM_PSP
)
688 ? SEGMENT_TO_PSP(CurrentPsp
)->EnvBlock
692 /* Check if this is an EXE file or a COM file */
693 if (Address
[0] == 'M' && Address
[1] == 'Z')
697 /* Get the MZ header */
698 Header
= (PIMAGE_DOS_HEADER
)Address
;
700 // TODO: Verify checksum and executable!
702 /* Get the base size of the file, in paragraphs (rounded up) */
703 ExeSize
= (((Header
->e_cp
- 1) << 8) + Header
->e_cblp
+ 0x0F) >> 4;
705 /* Loop from the maximum to the minimum number of extra paragraphs */
706 for (i
= Header
->e_maxalloc
; i
>= Header
->e_minalloc
; i
--)
708 /* Try to allocate that much memory */
709 Segment
= DosAllocateMemory(ExeSize
+ (sizeof(DOS_PSP
) >> 4) + i
, NULL
);
710 if (Segment
!= 0) break;
713 /* Check if at least the lowest allocation was successful */
714 if (Segment
== 0) goto Cleanup
;
716 /* Initialize the PSP */
717 DosInitializePsp(Segment
,
718 CommandLine
, ExeSize
+ (sizeof(DOS_PSP
) >> 4) + i
,
721 /* The process owns its own memory */
722 DosChangeMemoryOwner(Segment
, Segment
);
723 DosChangeMemoryOwner(EnvBlock
, Segment
);
725 /* Copy the program to Segment:0100 */
726 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
727 + TO_LINEAR(Segment
, 0x100)),
728 Address
+ (Header
->e_cparhdr
<< 4),
729 FileSize
- (Header
->e_cparhdr
<< 4));
731 /* Get the relocation table */
732 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
734 /* Perform relocations */
735 for (i
= 0; i
< Header
->e_crlc
; i
++)
737 /* Get a pointer to the word that needs to be patched */
738 RelocWord
= (PWORD
)((ULONG_PTR
)BaseAddress
739 + TO_LINEAR(Segment
+ HIWORD(RelocationTable
[i
]),
740 0x100 + LOWORD(RelocationTable
[i
])));
742 /* Add the number of the EXE segment to it */
743 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
746 /* Set the initial segment registers */
747 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
748 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
750 /* Set the stack to the location from the header */
751 EmulatorSetStack(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
,
755 CurrentPsp
= Segment
;
756 DiskTransferArea
= MAKELONG(0x80, Segment
);
757 EmulatorExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
766 /* Allocate memory for the whole program and the PSP */
767 Segment
= DosAllocateMemory((FileSize
+ sizeof(DOS_PSP
)) >> 4, NULL
);
768 if (Segment
== 0) goto Cleanup
;
770 /* Copy the program to Segment:0100 */
771 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
772 + TO_LINEAR(Segment
, 0x100)),
776 /* Initialize the PSP */
777 DosInitializePsp(Segment
,
779 (FileSize
+ sizeof(DOS_PSP
)) >> 4,
782 /* Set the initial segment registers */
783 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
784 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
786 /* Set the stack to the last word of the segment */
787 EmulatorSetStack(Segment
, 0xFFFE);
790 CurrentPsp
= Segment
;
791 DiskTransferArea
= MAKELONG(0x80, Segment
);
792 EmulatorExecute(Segment
, 0x100);
800 /* It was not successful, cleanup the DOS memory */
801 if (AllocatedEnvBlock
) DosFreeMemory(EnvBlock
);
802 if (Segment
) DosFreeMemory(Segment
);
806 if (Address
!= NULL
) UnmapViewOfFile(Address
);
808 /* Close the file mapping object */
809 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
811 /* Close the file handle */
812 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
817 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
)
819 WORD McbSegment
= FIRST_MCB_SEGMENT
;
821 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
822 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
824 /* Check if this PSP is it's own parent */
825 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
827 // TODO: Close all handles opened by the process
829 /* Free the memory used by the process */
832 /* Get a pointer to the MCB */
833 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
835 /* Make sure the MCB is valid */
836 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!='Z') break;
838 /* If this block was allocated by the process, free it */
839 if (CurrentMcb
->OwnerPsp
== Psp
) DosFreeMemory(McbSegment
);
841 /* If this was the last block, quit */
842 if (CurrentMcb
->BlockType
== 'Z') break;
844 /* Update the segment and continue */
845 McbSegment
+= CurrentMcb
->Size
+ 1;
849 /* Restore the interrupt vectors */
850 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
851 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
852 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
854 /* Update the current PSP */
855 if (Psp
== CurrentPsp
)
857 CurrentPsp
= PspBlock
->ParentPsp
;
858 if (CurrentPsp
== SYSTEM_PSP
) VdmRunning
= FALSE
;
861 /* Return control to the parent process */
862 EmulatorExecute(HIWORD(PspBlock
->TerminateAddress
),
863 LOWORD(PspBlock
->TerminateAddress
));
866 CHAR
DosReadCharacter()
868 // TODO: STDIN can be redirected under DOS 2.0+
871 /* A zero value for the character indicates a special key */
872 do Character
= BiosGetCharacter();
878 VOID
DosPrintCharacter(CHAR Character
)
880 // TODO: STDOUT can be redirected under DOS 2.0+
881 if (Character
== '\r') Character
= '\n';
885 VOID
DosInt20h(WORD CodeSegment
)
887 /* This is the exit interrupt */
888 DosTerminateProcess(CodeSegment
, 0);
891 VOID
DosInt21h(WORD CodeSegment
)
895 SYSTEMTIME SystemTime
;
897 PDOS_INPUT_BUFFER InputBuffer
;
898 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
899 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
900 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
901 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
902 WORD DataSegment
= EmulatorGetRegister(EMULATOR_REG_DS
);
903 WORD ExtSegment
= EmulatorGetRegister(EMULATOR_REG_ES
);
905 /* Check the value in the AH register */
908 /* Terminate Program */
911 DosTerminateProcess(CodeSegment
, 0);
915 /* Read Character And Echo */
918 Character
= DosReadCharacter();
919 DosPrintCharacter(Character
);
920 EmulatorSetRegister(EMULATOR_REG_AX
, (Eax
& 0xFFFFFF00) | Character
);
924 /* Print Character */
927 DosPrintCharacter(LOBYTE(Edx
));
931 /* Read Character Without Echo */
934 EmulatorSetRegister(EMULATOR_REG_AX
,
935 (Eax
& 0xFFFFFF00) | DosReadCharacter());
942 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
943 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
945 while ((*String
) != '$')
947 DosPrintCharacter(*String
);
954 /* Read Buffered Input */
957 InputBuffer
= (PDOS_INPUT_BUFFER
)((ULONG_PTR
)BaseAddress
958 + TO_LINEAR(DataSegment
,
961 InputBuffer
->Length
= 0;
962 for (i
= 0; i
< InputBuffer
->MaxLength
; i
++)
964 Character
= DosReadCharacter();
965 DosPrintCharacter(Character
);
966 InputBuffer
->Buffer
[InputBuffer
->Length
] = Character
;
967 if (Character
== '\r') break;
968 InputBuffer
->Length
++;
974 /* Set Disk Transfer Area */
977 DiskTransferArea
= MAKELONG(LOWORD(Edx
), DataSegment
);
981 /* Set Interrupt Vector */
984 DWORD FarPointer
= MAKELONG(LOWORD(Edx
), DataSegment
);
986 /* Write the new far pointer to the IDT */
987 ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)] = FarPointer
;
992 /* Get system date */
995 GetLocalTime(&SystemTime
);
996 EmulatorSetRegister(EMULATOR_REG_CX
,
997 (Ecx
& 0xFFFF0000) | SystemTime
.wYear
);
998 EmulatorSetRegister(EMULATOR_REG_DX
,
1000 | (SystemTime
.wMonth
<< 8)
1002 EmulatorSetRegister(EMULATOR_REG_AX
,
1003 (Eax
& 0xFFFFFF00) | SystemTime
.wDayOfWeek
);
1007 /* Set system date */
1010 GetLocalTime(&SystemTime
);
1011 SystemTime
.wYear
= LOWORD(Ecx
);
1012 SystemTime
.wMonth
= HIBYTE(Edx
);
1013 SystemTime
.wDay
= LOBYTE(Edx
);
1015 if (SetLocalTime(&SystemTime
))
1017 /* Return success */
1018 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
1022 /* Return failure */
1023 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
1029 /* Get system time */
1032 GetLocalTime(&SystemTime
);
1033 EmulatorSetRegister(EMULATOR_REG_CX
,
1035 | (SystemTime
.wHour
<< 8)
1036 | SystemTime
.wMinute
);
1037 EmulatorSetRegister(EMULATOR_REG_DX
,
1039 | (SystemTime
.wSecond
<< 8)
1040 | (SystemTime
.wMilliseconds
/ 10));
1044 /* Set system time */
1047 GetLocalTime(&SystemTime
);
1048 SystemTime
.wHour
= HIBYTE(Ecx
);
1049 SystemTime
.wMinute
= LOBYTE(Ecx
);
1050 SystemTime
.wSecond
= HIBYTE(Edx
);
1051 SystemTime
.wMilliseconds
= LOBYTE(Edx
) * 10;
1053 if (SetLocalTime(&SystemTime
))
1055 /* Return success */
1056 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
1060 /* Return failure */
1061 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
1067 /* Get Disk Transfer Area */
1070 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(DiskTransferArea
));
1071 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(DiskTransferArea
));
1076 /* Get DOS Version */
1079 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1081 EmulatorSetRegister(EMULATOR_REG_AX
, PspBlock
->DosVersion
);
1085 /* Get Interrupt Vector */
1088 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)];
1090 /* Read the address from the IDT into ES:BX */
1091 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(FarPointer
));
1092 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(FarPointer
));
1097 /* Create Directory */
1100 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1101 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1103 if (CreateDirectoryA(String
, NULL
))
1105 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1109 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1110 EmulatorSetRegister(EMULATOR_REG_AX
,
1111 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1117 /* Remove Directory */
1120 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1121 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1123 if (RemoveDirectoryA(String
))
1125 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1129 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1130 EmulatorSetRegister(EMULATOR_REG_AX
,
1131 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1138 /* Set Current Directory */
1141 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1142 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1144 if (SetCurrentDirectoryA(String
))
1146 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1150 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1151 EmulatorSetRegister(EMULATOR_REG_AX
,
1152 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1162 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1163 (LPCSTR
)(ULONG_PTR
)BaseAddress
1164 + TO_LINEAR(DataSegment
, LOWORD(Edx
)),
1170 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1172 /* Return the handle in AX */
1173 EmulatorSetRegister(EMULATOR_REG_AX
,
1174 (Eax
& 0xFFFF0000) | FileHandle
);
1179 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1181 /* Return the error code in AX */
1182 EmulatorSetRegister(EMULATOR_REG_AX
,
1183 (Eax
& 0xFFFF0000) | ErrorCode
);
1193 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1194 (LPCSTR
)(ULONG_PTR
)BaseAddress
1195 + TO_LINEAR(DataSegment
, LOWORD(Edx
)),
1201 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1203 /* Return the handle in AX */
1204 EmulatorSetRegister(EMULATOR_REG_AX
,
1205 (Eax
& 0xFFFF0000) | FileHandle
);
1210 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1212 /* Return the error code in AX */
1213 EmulatorSetRegister(EMULATOR_REG_AX
,
1214 (Eax
& 0xFFFF0000) | ErrorCode
);
1223 if (DosCloseHandle(LOWORD(Ebx
)))
1226 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1231 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1233 /* Return the error code in AX */
1234 EmulatorSetRegister(EMULATOR_REG_AX
,
1235 (Eax
& 0xFFFF0000) | ERROR_INVALID_PARAMETER
);
1245 WORD ErrorCode
= DosReadFile(LOWORD(Ebx
),
1246 (LPVOID
)((ULONG_PTR
)BaseAddress
1247 + TO_LINEAR(DataSegment
, LOWORD(Edx
))),
1254 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1256 /* Return the number of bytes read in AX */
1257 EmulatorSetRegister(EMULATOR_REG_AX
,
1258 (Eax
& 0xFFFF0000) | BytesRead
);
1263 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1265 /* Return the error code in AX */
1266 EmulatorSetRegister(EMULATOR_REG_AX
,
1267 (Eax
& 0xFFFF0000) | ErrorCode
);
1275 WORD BytesWritten
= 0;
1276 WORD ErrorCode
= DosWriteFile(LOWORD(Ebx
),
1277 (LPVOID
)((ULONG_PTR
)BaseAddress
1278 + TO_LINEAR(DataSegment
, LOWORD(Edx
))),
1285 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1287 /* Return the number of bytes written in AX */
1288 EmulatorSetRegister(EMULATOR_REG_AX
,
1289 (Eax
& 0xFFFF0000) | BytesWritten
);
1294 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1296 /* Return the error code in AX */
1297 EmulatorSetRegister(EMULATOR_REG_AX
,
1298 (Eax
& 0xFFFF0000) | ErrorCode
);
1304 /* Allocate Memory */
1307 WORD MaxAvailable
= 0;
1308 WORD Segment
= DosAllocateMemory(LOWORD(Ebx
), &MaxAvailable
);
1312 EmulatorSetRegister(EMULATOR_REG_AX
, Segment
);
1313 EmulatorSetRegister(EMULATOR_REG_BX
, MaxAvailable
);
1314 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1316 else EmulatorSetFlag(EMULATOR_FLAG_CF
);
1324 if (DosFreeMemory(ExtSegment
))
1326 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1328 else EmulatorSetFlag(EMULATOR_FLAG_CF
);
1333 /* Resize Memory Block */
1338 if (DosResizeMemory(ExtSegment
, LOWORD(Ebx
), &Size
))
1340 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1344 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1345 EmulatorSetRegister(EMULATOR_REG_BX
, Size
);
1351 /* Terminate With Return Code */
1354 DosTerminateProcess(CurrentPsp
, LOBYTE(Eax
));
1361 DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax
));
1362 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1367 VOID
DosBreakInterrupt()
1372 BOOLEAN
DosInitialize()
1375 PDOS_MCB Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
);
1378 LPWSTR SourcePtr
, Environment
;
1380 LPSTR DestPtr
= (LPSTR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(SYSTEM_ENV_BLOCK
, 0));
1383 /* Initialize the MCB */
1384 Mcb
->BlockType
= 'Z';
1385 Mcb
->Size
= (WORD
)USER_MEMORY_SIZE
;
1388 /* Get the environment strings */
1389 SourcePtr
= Environment
= GetEnvironmentStringsW();
1390 if (Environment
== NULL
) return FALSE
;
1392 /* Fill the DOS system environment block */
1395 /* Get the size of the ASCII string */
1396 AsciiSize
= WideCharToMultiByte(CP_ACP
,
1405 /* Allocate memory for the ASCII string */
1406 AsciiString
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, AsciiSize
);
1407 if (AsciiString
== NULL
)
1409 FreeEnvironmentStringsW(Environment
);
1413 /* Convert to ASCII */
1414 WideCharToMultiByte(CP_ACP
,
1423 /* Copy the string into DOS memory */
1424 strcpy(DestPtr
, AsciiString
);
1426 /* Free the memory */
1427 HeapFree(GetProcessHeap(), 0, AsciiString
);
1429 /* Move to the next string */
1430 SourcePtr
+= wcslen(SourcePtr
) + 1;
1431 DestPtr
+= strlen(AsciiString
) + 1;
1434 /* Free the memory allocated for environment strings */
1435 FreeEnvironmentStringsW(Environment
);
1437 /* Read CONFIG.SYS */
1438 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
1441 while (fgetws(Buffer
, 256, Stream
))
1443 // TODO: Parse the line
1448 /* Initialize the SFT */
1449 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
1451 DosSystemFileTable
[i
] = INVALID_HANDLE_VALUE
;
1452 DosSftRefCount
[i
] = 0;
1455 /* Get handles to standard I/O devices */
1456 DosSystemFileTable
[0] = GetStdHandle(STD_INPUT_HANDLE
);
1457 DosSystemFileTable
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
1458 DosSystemFileTable
[2] = GetStdHandle(STD_ERROR_HANDLE
);