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 /* PRIVATE VARIABLES **********************************************************/
19 static WORD CurrentPsp
= SYSTEM_PSP
;
20 static WORD DosLastError
= 0;
21 static DWORD DiskTransferArea
;
22 static HANDLE DosSystemFileTable
[DOS_SFT_SIZE
];
23 static WORD DosSftRefCount
[DOS_SFT_SIZE
];
24 static BYTE DosAllocStrategy
= DOS_ALLOC_BEST_FIT
;
25 static BOOLEAN DosUmbLinked
= FALSE
;
27 /* PRIVATE FUNCTIONS **********************************************************/
29 static VOID
DosCombineFreeBlocks(WORD StartBlock
)
31 PDOS_MCB CurrentMcb
= SEGMENT_TO_MCB(StartBlock
), NextMcb
;
33 /* If this is the last block or it's not free, quit */
34 if (CurrentMcb
->BlockType
== 'Z' || CurrentMcb
->OwnerPsp
!= 0) return;
38 /* Get a pointer to the next MCB */
39 NextMcb
= SEGMENT_TO_MCB(StartBlock
+ CurrentMcb
->Size
+ 1);
41 /* Check if the next MCB is free */
42 if (NextMcb
->OwnerPsp
== 0)
45 CurrentMcb
->Size
+= NextMcb
->Size
+ 1;
46 CurrentMcb
->BlockType
= NextMcb
->BlockType
;
47 NextMcb
->BlockType
= 'I';
51 /* No more adjoining free blocks */
57 static WORD
DosCopyEnvironmentBlock(WORD SourceSegment
)
59 PCHAR Ptr
, SourceBuffer
, DestBuffer
= NULL
;
63 Ptr
= SourceBuffer
= (PCHAR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(SourceSegment
, 0));
65 /* Calculate the size of the environment block */
68 TotalSize
+= strlen(Ptr
) + 1;
69 Ptr
+= strlen(Ptr
) + 1;
73 /* Allocate the memory for the environment block */
74 DestSegment
= DosAllocateMemory((TotalSize
+ 0x0F) >> 4, NULL
);
75 if (!DestSegment
) return 0;
79 DestBuffer
= (PCHAR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(DestSegment
, 0));
83 strcpy(DestBuffer
, Ptr
);
85 /* Advance to the next string */
86 Ptr
+= strlen(Ptr
) + 1;
87 DestBuffer
+= strlen(Ptr
);
89 /* Put a zero after the string */
93 /* Set the final zero */
99 static VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
101 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
103 /* Just set the owner */
104 Mcb
->OwnerPsp
= NewOwner
;
107 static WORD
DosOpenHandle(HANDLE Handle
)
114 /* The system PSP has no handle table */
115 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_DOS_HANDLE
;
117 /* Get a pointer to the handle table */
118 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
119 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
121 /* Find a free entry in the JFT */
122 for (DosHandle
= 0; DosHandle
< PspBlock
->HandleTableSize
; DosHandle
++)
124 if (HandleTable
[DosHandle
] == 0xFF) break;
127 /* If there are no free entries, fail */
128 if (DosHandle
== PspBlock
->HandleTableSize
) return INVALID_DOS_HANDLE
;
130 /* Check if the handle is already in the SFT */
131 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
133 /* Check if this is the same handle */
134 if (DosSystemFileTable
[i
] != Handle
) continue;
136 /* Already in the table, reference it */
139 /* Set the JFT entry to that SFT index */
140 HandleTable
[DosHandle
] = i
;
142 /* Return the new handle */
146 /* Add the handle to the SFT */
147 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
149 /* Make sure this is an empty table entry */
150 if (DosSystemFileTable
[i
] != INVALID_HANDLE_VALUE
) continue;
152 /* Initialize the empty table entry */
153 DosSystemFileTable
[i
] = Handle
;
154 DosSftRefCount
[i
] = 1;
156 /* Set the JFT entry to that SFT index */
157 HandleTable
[DosHandle
] = i
;
159 /* Return the new handle */
163 /* The SFT is full */
164 return INVALID_DOS_HANDLE
;
167 static HANDLE
DosGetRealHandle(WORD DosHandle
)
172 /* The system PSP has no handle table */
173 if (CurrentPsp
== SYSTEM_PSP
) return INVALID_HANDLE_VALUE
;
175 /* Get a pointer to the handle table */
176 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
177 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
179 /* Make sure the handle is open */
180 if (HandleTable
[DosHandle
] == 0xFF) return INVALID_HANDLE_VALUE
;
182 /* Return the Win32 handle */
183 return DosSystemFileTable
[HandleTable
[DosHandle
]];
186 static VOID
DosCopyHandleTable(LPBYTE DestinationTable
)
192 /* Clear the table first */
193 for (i
= 0; i
< 20; i
++) DestinationTable
[i
] = 0xFF;
195 /* Check if this is the initial process */
196 if (CurrentPsp
== SYSTEM_PSP
)
198 /* Set up the standard I/O devices */
199 for (i
= 0; i
<= 2; i
++)
201 /* Set the index in the SFT */
202 DestinationTable
[i
] = i
;
204 /* Increase the reference count */
212 /* Get the parent PSP block and handle table */
213 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
214 SourceTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
216 /* Copy the first 20 handles into the new table */
217 for (i
= 0; i
< 20; i
++)
219 DestinationTable
[i
] = SourceTable
[i
];
221 /* Increase the reference count */
222 DosSftRefCount
[SourceTable
[i
]]++;
226 /* PUBLIC FUNCTIONS ***********************************************************/
228 WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
230 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
231 PDOS_MCB CurrentMcb
, NextMcb
;
232 BOOLEAN SearchUmb
= FALSE
;
234 if (DosUmbLinked
&& (DosAllocStrategy
& (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
)))
236 /* Search UMB first */
237 Segment
= UMB_START_SEGMENT
;
243 /* Get a pointer to the MCB */
244 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
246 /* Make sure it's valid */
247 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
249 DosLastError
= ERROR_ARENA_TRASHED
;
253 /* Only check free blocks */
254 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
256 /* Combine this free block with adjoining free blocks */
257 DosCombineFreeBlocks(Segment
);
259 /* Update the maximum block size */
260 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
262 /* Check if this block is big enough */
263 if (CurrentMcb
->Size
< Size
) goto Next
;
265 switch (DosAllocStrategy
& 0x3F)
267 case DOS_ALLOC_FIRST_FIT
:
269 /* For first fit, stop immediately */
274 case DOS_ALLOC_BEST_FIT
:
276 /* For best fit, update the smallest block found so far */
277 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
285 case DOS_ALLOC_LAST_FIT
:
287 /* For last fit, make the current block the result, but keep searching */
294 /* If this was the last MCB in the chain, quit */
295 if (CurrentMcb
->BlockType
== 'Z')
297 /* Check if nothing was found while searching through UMBs */
298 if ((Result
== 0) && SearchUmb
&& (DosAllocStrategy
& DOS_ALLOC_HIGH_LOW
))
300 /* Search low memory */
301 Segment
= FIRST_MCB_SEGMENT
;
308 /* Otherwise, update the segment and continue */
309 Segment
+= CurrentMcb
->Size
+ 1;
314 /* If we didn't find a free block, return 0 */
317 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
318 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
322 /* Get a pointer to the MCB */
323 CurrentMcb
= SEGMENT_TO_MCB(Result
);
325 /* Check if the block is larger than requested */
326 if (CurrentMcb
->Size
> Size
)
328 /* It is, split it into two blocks */
329 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
331 /* Initialize the new MCB structure */
332 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
333 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
334 NextMcb
->OwnerPsp
= 0;
336 /* Update the current block */
337 CurrentMcb
->BlockType
= 'M';
338 CurrentMcb
->Size
= Size
;
341 /* Take ownership of the block */
342 CurrentMcb
->OwnerPsp
= CurrentPsp
;
344 /* Return the segment of the data portion of the block */
348 BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
350 BOOLEAN Success
= TRUE
;
351 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
352 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
354 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
358 /* Make sure this is a valid, allocated block */
359 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
362 DosLastError
= ERROR_INVALID_HANDLE
;
366 ReturnSize
= Mcb
->Size
;
368 /* Check if we need to expand or contract the block */
369 if (NewSize
> Mcb
->Size
)
371 /* We can't expand the last block */
372 if (Mcb
->BlockType
!= 'M')
378 /* Get the pointer and segment of the next MCB */
379 NextSegment
= Segment
+ Mcb
->Size
+ 1;
380 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
382 /* Make sure the next segment is free */
383 if (NextMcb
->OwnerPsp
!= 0)
385 DPRINT("Cannot expand memory block: next segment is not free!\n");
386 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
391 /* Combine this free block with adjoining free blocks */
392 DosCombineFreeBlocks(NextSegment
);
394 /* Set the maximum possible size of the block */
395 ReturnSize
+= NextMcb
->Size
+ 1;
397 /* Maximize the current block */
398 Mcb
->Size
= ReturnSize
;
399 Mcb
->BlockType
= NextMcb
->BlockType
;
401 /* Invalidate the next block */
402 NextMcb
->BlockType
= 'I';
404 /* Check if the block is larger than requested */
405 if (Mcb
->Size
> NewSize
)
407 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
411 /* It is, split it into two blocks */
412 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
414 /* Initialize the new MCB structure */
415 NextMcb
->BlockType
= Mcb
->BlockType
;
416 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
417 NextMcb
->OwnerPsp
= 0;
419 /* Update the current block */
420 Mcb
->BlockType
= 'M';
424 else if (NewSize
< Mcb
->Size
)
426 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
430 /* Just split the block */
431 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
432 NextMcb
->BlockType
= Mcb
->BlockType
;
433 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
434 NextMcb
->OwnerPsp
= 0;
437 Mcb
->BlockType
= 'M';
442 /* Check if the operation failed */
445 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
448 /* Return the maximum possible size */
449 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
455 BOOLEAN
DosFreeMemory(WORD BlockData
)
457 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
459 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData
);
461 /* Make sure the MCB is valid */
462 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z')
464 DPRINT("MCB block type '%c' not valid!\n", Mcb
->BlockType
);
468 /* Mark the block as free */
474 BOOLEAN
DosLinkUmb(VOID
)
476 DWORD Segment
= FIRST_MCB_SEGMENT
;
477 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
479 DPRINT("Linking UMB\n");
481 /* Check if UMBs are already linked */
482 if (DosUmbLinked
) return FALSE
;
484 /* Find the last block */
485 while ((Mcb
->BlockType
== 'M') && (Segment
<= 0xFFFF))
487 Segment
+= Mcb
->Size
+ 1;
488 Mcb
= SEGMENT_TO_MCB(Segment
);
491 /* Make sure it's valid */
492 if (Mcb
->BlockType
!= 'Z') return FALSE
;
494 /* Connect the MCB with the UMB chain */
495 Mcb
->BlockType
= 'M';
501 BOOLEAN
DosUnlinkUmb(VOID
)
503 DWORD Segment
= FIRST_MCB_SEGMENT
;
504 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
506 DPRINT("Unlinking UMB\n");
508 /* Check if UMBs are already unlinked */
509 if (!DosUmbLinked
) return FALSE
;
511 /* Find the block preceding the MCB that links it with the UMB chain */
512 while (Segment
<= 0xFFFF)
514 if ((Segment
+ Mcb
->Size
) == (FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
))
516 /* This is the last non-UMB segment */
520 /* Advance to the next MCB */
521 Segment
+= Mcb
->Size
+ 1;
522 Mcb
= SEGMENT_TO_MCB(Segment
);
525 /* Mark the MCB as the last MCB */
526 Mcb
->BlockType
= 'Z';
528 DosUmbLinked
= FALSE
;
532 WORD
DosCreateFile(LPWORD Handle
, LPCSTR FilePath
, WORD Attributes
)
537 DPRINT("DosCreateFile: FilePath \"%s\", Attributes 0x%04X\n",
541 /* Create the file */
542 FileHandle
= CreateFileA(FilePath
,
543 GENERIC_READ
| GENERIC_WRITE
,
544 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
550 if (FileHandle
== INVALID_HANDLE_VALUE
)
552 /* Return the error code */
553 return GetLastError();
556 /* Open the DOS handle */
557 DosHandle
= DosOpenHandle(FileHandle
);
559 if (DosHandle
== INVALID_DOS_HANDLE
)
561 /* Close the handle */
562 CloseHandle(FileHandle
);
564 /* Return the error code */
565 return ERROR_TOO_MANY_OPEN_FILES
;
568 /* It was successful */
570 return ERROR_SUCCESS
;
573 WORD
DosOpenFile(LPWORD Handle
, LPCSTR FilePath
, BYTE AccessMode
)
576 ACCESS_MASK Access
= 0;
579 DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n",
583 /* Parse the access mode */
584 switch (AccessMode
& 3)
589 Access
= GENERIC_READ
;
596 Access
= GENERIC_WRITE
;
603 Access
= GENERIC_READ
| GENERIC_WRITE
;
610 return ERROR_INVALID_PARAMETER
;
615 FileHandle
= CreateFileA(FilePath
,
617 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
620 FILE_ATTRIBUTE_NORMAL
,
623 if (FileHandle
== INVALID_HANDLE_VALUE
)
625 /* Return the error code */
626 return GetLastError();
629 /* Open the DOS handle */
630 DosHandle
= DosOpenHandle(FileHandle
);
632 if (DosHandle
== INVALID_DOS_HANDLE
)
634 /* Close the handle */
635 CloseHandle(FileHandle
);
637 /* Return the error code */
638 return ERROR_TOO_MANY_OPEN_FILES
;
641 /* It was successful */
643 return ERROR_SUCCESS
;
646 WORD
DosReadFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesRead
)
648 WORD Result
= ERROR_SUCCESS
;
649 DWORD BytesRead32
= 0;
650 HANDLE Handle
= DosGetRealHandle(FileHandle
);
652 DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle
, Count
);
654 /* Make sure the handle is valid */
655 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
658 if (!ReadFile(Handle
, Buffer
, Count
, &BytesRead32
, NULL
))
660 /* Store the error code */
661 Result
= GetLastError();
664 /* The number of bytes read is always 16-bit */
665 *BytesRead
= LOWORD(BytesRead32
);
667 /* Return the error code */
671 WORD
DosWriteFile(WORD FileHandle
, LPVOID Buffer
, WORD Count
, LPWORD BytesWritten
)
673 WORD Result
= ERROR_SUCCESS
;
674 DWORD BytesWritten32
= 0;
675 HANDLE Handle
= DosGetRealHandle(FileHandle
);
677 DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n",
681 /* Make sure the handle is valid */
682 if (Handle
== INVALID_HANDLE_VALUE
) return ERROR_INVALID_HANDLE
;
685 if (!WriteFile(Handle
, Buffer
, Count
, &BytesWritten32
, NULL
))
687 /* Store the error code */
688 Result
= GetLastError();
691 /* The number of bytes written is always 16-bit */
692 *BytesWritten
= LOWORD(BytesWritten32
);
694 /* Return the error code */
698 BOOLEAN
DosCloseHandle(WORD DosHandle
)
704 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle
);
706 /* The system PSP has no handle table */
707 if (CurrentPsp
== SYSTEM_PSP
) return FALSE
;
709 /* Get a pointer to the handle table */
710 PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
711 HandleTable
= (LPBYTE
)FAR_POINTER(PspBlock
->HandleTablePtr
);
713 /* Make sure the handle is open */
714 if (HandleTable
[DosHandle
] == 0xFF) return FALSE
;
716 /* Decrement the reference count of the SFT entry */
717 SftIndex
= HandleTable
[DosHandle
];
718 DosSftRefCount
[SftIndex
]--;
720 /* Check if the reference count fell to zero */
721 if (!DosSftRefCount
[SftIndex
])
723 /* Close the file, it's no longer needed */
724 CloseHandle(DosSystemFileTable
[SftIndex
]);
726 /* Clear the handle */
727 DosSystemFileTable
[SftIndex
] = INVALID_HANDLE_VALUE
;
733 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
735 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
736 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
738 ZeroMemory(PspBlock
, sizeof(DOS_PSP
));
740 /* Set the exit interrupt */
741 PspBlock
->Exit
[0] = 0xCD; // int 0x20
742 PspBlock
->Exit
[1] = 0x20;
744 /* Set the number of the last paragraph */
745 PspBlock
->LastParagraph
= PspSegment
+ ProgramSize
- 1;
747 /* Save the interrupt vectors */
748 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
749 PspBlock
->BreakAddress
= IntVecTable
[0x23];
750 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
752 /* Set the parent PSP */
753 PspBlock
->ParentPsp
= CurrentPsp
;
755 /* Copy the parent handle table */
756 DosCopyHandleTable(PspBlock
->HandleTable
);
758 /* Set the environment block */
759 PspBlock
->EnvBlock
= Environment
;
761 /* Set the handle table pointers to the internal handle table */
762 PspBlock
->HandleTableSize
= 20;
763 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
765 /* Set the DOS version */
766 PspBlock
->DosVersion
= DOS_VERSION
;
768 /* Set the far call opcodes */
769 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
770 PspBlock
->FarCall
[1] = 0x21;
771 PspBlock
->FarCall
[2] = 0xCB; // retf
773 /* Set the command line */
774 PspBlock
->CommandLineSize
= strlen(CommandLine
);
775 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
776 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
779 BOOLEAN
DosCreateProcess(LPCSTR CommandLine
, WORD EnvBlock
)
781 BOOLEAN Success
= FALSE
, AllocatedEnvBlock
= FALSE
;
782 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
783 LPBYTE Address
= NULL
;
784 LPSTR ProgramFilePath
, Parameters
[128];
785 CHAR CommandLineCopy
[128];
789 DWORD i
, FileSize
, ExeSize
;
790 PIMAGE_DOS_HEADER Header
;
791 PDWORD RelocationTable
;
794 DPRINT("DosCreateProcess: CommandLine \"%s\", EnvBlock 0x%04X\n",
798 /* Save a copy of the command line */
799 strcpy(CommandLineCopy
, CommandLine
);
801 /* Get the file name of the executable */
802 ProgramFilePath
= strtok(CommandLineCopy
, " \t");
804 /* Load the parameters in the local array */
805 while ((ParamCount
< 256)
806 && ((Parameters
[ParamCount
] = strtok(NULL
, " \t")) != NULL
))
811 /* Open a handle to the executable */
812 FileHandle
= CreateFileA(ProgramFilePath
,
817 FILE_ATTRIBUTE_NORMAL
,
819 if (FileHandle
== INVALID_HANDLE_VALUE
) goto Cleanup
;
821 /* Get the file size */
822 FileSize
= GetFileSize(FileHandle
, NULL
);
824 /* Create a mapping object for the file */
825 FileMapping
= CreateFileMapping(FileHandle
,
831 if (FileMapping
== NULL
) goto Cleanup
;
833 /* Map the file into memory */
834 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
835 if (Address
== NULL
) goto Cleanup
;
837 /* Did we get an environment segment? */
840 /* Set a flag to know if the environment block was allocated here */
841 AllocatedEnvBlock
= TRUE
;
843 /* No, copy the one from the parent */
844 EnvBlock
= DosCopyEnvironmentBlock((CurrentPsp
!= SYSTEM_PSP
)
845 ? SEGMENT_TO_PSP(CurrentPsp
)->EnvBlock
849 /* Check if this is an EXE file or a COM file */
850 if (Address
[0] == 'M' && Address
[1] == 'Z')
854 /* Get the MZ header */
855 Header
= (PIMAGE_DOS_HEADER
)Address
;
857 /* Get the base size of the file, in paragraphs (rounded up) */
858 ExeSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
860 /* Add the PSP size, in paragraphs */
861 ExeSize
+= sizeof(DOS_PSP
) >> 4;
863 /* Add the maximum size that should be allocated */
864 ExeSize
+= Header
->e_maxalloc
;
866 /* Make sure it does not pass 0xFFFF */
867 if (ExeSize
> 0xFFFF) ExeSize
= 0xFFFF;
869 /* Reduce the size one by one until the allocation is successful */
870 for (i
= Header
->e_maxalloc
; i
>= Header
->e_minalloc
; i
--, ExeSize
--)
872 /* Try to allocate that much memory */
873 Segment
= DosAllocateMemory(ExeSize
, NULL
);
874 if (Segment
!= 0) break;
877 /* Check if at least the lowest allocation was successful */
878 if (Segment
== 0) goto Cleanup
;
880 /* Initialize the PSP */
881 DosInitializePsp(Segment
,
886 /* The process owns its own memory */
887 DosChangeMemoryOwner(Segment
, Segment
);
888 DosChangeMemoryOwner(EnvBlock
, Segment
);
890 /* Copy the program to Segment:0100 */
891 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
892 + TO_LINEAR(Segment
, 0x100)),
893 Address
+ (Header
->e_cparhdr
<< 4),
894 min(FileSize
- (Header
->e_cparhdr
<< 4),
895 (ExeSize
<< 4) - sizeof(DOS_PSP
)));
897 /* Get the relocation table */
898 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
900 /* Perform relocations */
901 for (i
= 0; i
< Header
->e_crlc
; i
++)
903 /* Get a pointer to the word that needs to be patched */
904 RelocWord
= (PWORD
)((ULONG_PTR
)BaseAddress
905 + TO_LINEAR(Segment
+ HIWORD(RelocationTable
[i
]),
906 0x100 + LOWORD(RelocationTable
[i
])));
908 /* Add the number of the EXE segment to it */
909 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
912 /* Set the initial segment registers */
913 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
914 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
916 /* Set the stack to the location from the header */
917 EmulatorSetStack(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
,
921 CurrentPsp
= Segment
;
922 DiskTransferArea
= MAKELONG(0x80, Segment
);
923 EmulatorExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
932 /* Find the maximum amount of memory that can be allocated */
933 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
935 /* Make sure it's enough for the whole program and the PSP */
936 if ((MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
))) goto Cleanup
;
938 /* Allocate all of it */
939 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
940 if (Segment
== 0) goto Cleanup
;
942 /* The process owns its own memory */
943 DosChangeMemoryOwner(Segment
, Segment
);
944 DosChangeMemoryOwner(EnvBlock
, Segment
);
946 /* Copy the program to Segment:0100 */
947 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
948 + TO_LINEAR(Segment
, 0x100)),
952 /* Initialize the PSP */
953 DosInitializePsp(Segment
,
955 (FileSize
+ sizeof(DOS_PSP
)) >> 4,
958 /* Set the initial segment registers */
959 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
960 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
962 /* Set the stack to the last word of the segment */
963 EmulatorSetStack(Segment
, 0xFFFE);
966 CurrentPsp
= Segment
;
967 DiskTransferArea
= MAKELONG(0x80, Segment
);
968 EmulatorExecute(Segment
, 0x100);
976 /* It was not successful, cleanup the DOS memory */
977 if (AllocatedEnvBlock
) DosFreeMemory(EnvBlock
);
978 if (Segment
) DosFreeMemory(Segment
);
982 if (Address
!= NULL
) UnmapViewOfFile(Address
);
984 /* Close the file mapping object */
985 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
987 /* Close the file handle */
988 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
993 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
)
996 WORD McbSegment
= FIRST_MCB_SEGMENT
;
998 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
999 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
1001 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
1005 /* Check if this PSP is it's own parent */
1006 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
1008 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
1010 /* Close the handle */
1014 /* Free the memory used by the process */
1017 /* Get a pointer to the MCB */
1018 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
1020 /* Make sure the MCB is valid */
1021 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!='Z') break;
1023 /* If this block was allocated by the process, free it */
1024 if (CurrentMcb
->OwnerPsp
== Psp
) DosFreeMemory(McbSegment
);
1026 /* If this was the last block, quit */
1027 if (CurrentMcb
->BlockType
== 'Z') break;
1029 /* Update the segment and continue */
1030 McbSegment
+= CurrentMcb
->Size
+ 1;
1034 /* Restore the interrupt vectors */
1035 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
1036 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
1037 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
1039 /* Update the current PSP */
1040 if (Psp
== CurrentPsp
)
1042 CurrentPsp
= PspBlock
->ParentPsp
;
1043 if (CurrentPsp
== SYSTEM_PSP
) VdmRunning
= FALSE
;
1046 /* Return control to the parent process */
1047 EmulatorExecute(HIWORD(PspBlock
->TerminateAddress
),
1048 LOWORD(PspBlock
->TerminateAddress
));
1051 CHAR
DosReadCharacter(VOID
)
1053 CHAR Character
= '\0';
1056 /* Use the file reading function */
1057 DosReadFile(DOS_INPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesRead
);
1062 VOID
DosPrintCharacter(CHAR Character
)
1066 /* Use the file writing function */
1067 DosWriteFile(DOS_OUTPUT_HANDLE
, &Character
, sizeof(CHAR
), &BytesWritten
);
1070 VOID
DosHandleIoctl(BYTE ControlCode
, WORD FileHandle
)
1072 HANDLE Handle
= DosGetRealHandle(FileHandle
);
1074 if (Handle
== INVALID_HANDLE_VALUE
)
1077 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1078 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_FILE_NOT_FOUND
);
1081 switch (ControlCode
)
1083 /* Get Device Information */
1088 if (Handle
== DosSystemFileTable
[0])
1093 else if (Handle
== DosSystemFileTable
[1])
1095 /* Console output */
1099 /* It is a character device */
1102 /* Return the device information word */
1103 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1104 EmulatorSetRegister(EMULATOR_REG_DX
, InfoWord
);
1109 /* Unsupported control code */
1112 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode
);
1114 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1115 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_PARAMETER
);
1120 VOID
DosInt20h(WORD CodeSegment
)
1122 /* This is the exit interrupt */
1123 DosTerminateProcess(CodeSegment
, 0);
1126 VOID
DosInt21h(WORD CodeSegment
)
1130 SYSTEMTIME SystemTime
;
1132 PDOS_INPUT_BUFFER InputBuffer
;
1133 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
1134 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
1135 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
1136 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
1137 WORD DataSegment
= EmulatorGetRegister(EMULATOR_REG_DS
);
1138 WORD ExtSegment
= EmulatorGetRegister(EMULATOR_REG_ES
);
1140 /* Check the value in the AH register */
1141 switch (HIBYTE(Eax
))
1143 /* Terminate Program */
1146 DosTerminateProcess(CodeSegment
, 0);
1150 /* Read Character And Echo */
1153 Character
= DosReadCharacter();
1154 DosPrintCharacter(Character
);
1155 EmulatorSetRegister(EMULATOR_REG_AX
, (Eax
& 0xFFFFFF00) | Character
);
1159 /* Print Character */
1162 DosPrintCharacter(LOBYTE(Edx
));
1166 /* Read Character Without Echo */
1170 EmulatorSetRegister(EMULATOR_REG_AX
,
1171 (Eax
& 0xFFFFFF00) | DosReadCharacter());
1178 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1179 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1181 while ((*String
) != '$')
1183 DosPrintCharacter(*String
);
1190 /* Read Buffered Input */
1193 InputBuffer
= (PDOS_INPUT_BUFFER
)((ULONG_PTR
)BaseAddress
1194 + TO_LINEAR(DataSegment
,
1197 InputBuffer
->Length
= 0;
1198 for (i
= 0; i
< InputBuffer
->MaxLength
; i
++)
1200 Character
= DosReadCharacter();
1201 DosPrintCharacter(Character
);
1202 InputBuffer
->Buffer
[InputBuffer
->Length
] = Character
;
1203 if (Character
== '\r') break;
1204 InputBuffer
->Length
++;
1210 /* Set Disk Transfer Area */
1213 DiskTransferArea
= MAKELONG(LOWORD(Edx
), DataSegment
);
1217 /* Set Interrupt Vector */
1220 DWORD FarPointer
= MAKELONG(LOWORD(Edx
), DataSegment
);
1222 /* Write the new far pointer to the IDT */
1223 ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)] = FarPointer
;
1228 /* Get system date */
1231 GetLocalTime(&SystemTime
);
1232 EmulatorSetRegister(EMULATOR_REG_CX
,
1233 (Ecx
& 0xFFFF0000) | SystemTime
.wYear
);
1234 EmulatorSetRegister(EMULATOR_REG_DX
,
1236 | (SystemTime
.wMonth
<< 8)
1238 EmulatorSetRegister(EMULATOR_REG_AX
,
1239 (Eax
& 0xFFFFFF00) | SystemTime
.wDayOfWeek
);
1243 /* Set system date */
1246 GetLocalTime(&SystemTime
);
1247 SystemTime
.wYear
= LOWORD(Ecx
);
1248 SystemTime
.wMonth
= HIBYTE(Edx
);
1249 SystemTime
.wDay
= LOBYTE(Edx
);
1251 if (SetLocalTime(&SystemTime
))
1253 /* Return success */
1254 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
1258 /* Return failure */
1259 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
1265 /* Get system time */
1268 GetLocalTime(&SystemTime
);
1269 EmulatorSetRegister(EMULATOR_REG_CX
,
1271 | (SystemTime
.wHour
<< 8)
1272 | SystemTime
.wMinute
);
1273 EmulatorSetRegister(EMULATOR_REG_DX
,
1275 | (SystemTime
.wSecond
<< 8)
1276 | (SystemTime
.wMilliseconds
/ 10));
1280 /* Set system time */
1283 GetLocalTime(&SystemTime
);
1284 SystemTime
.wHour
= HIBYTE(Ecx
);
1285 SystemTime
.wMinute
= LOBYTE(Ecx
);
1286 SystemTime
.wSecond
= HIBYTE(Edx
);
1287 SystemTime
.wMilliseconds
= LOBYTE(Edx
) * 10;
1289 if (SetLocalTime(&SystemTime
))
1291 /* Return success */
1292 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
1296 /* Return failure */
1297 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
1303 /* Get Disk Transfer Area */
1306 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(DiskTransferArea
));
1307 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(DiskTransferArea
));
1312 /* Get DOS Version */
1315 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
1317 EmulatorSetRegister(EMULATOR_REG_AX
, PspBlock
->DosVersion
);
1321 /* Get Interrupt Vector */
1324 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)];
1326 /* Read the address from the IDT into ES:BX */
1327 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(FarPointer
));
1328 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(FarPointer
));
1333 /* Create Directory */
1336 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1337 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1339 if (CreateDirectoryA(String
, NULL
))
1341 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1345 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1346 EmulatorSetRegister(EMULATOR_REG_AX
,
1347 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1353 /* Remove Directory */
1356 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1357 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1359 if (RemoveDirectoryA(String
))
1361 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1365 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1366 EmulatorSetRegister(EMULATOR_REG_AX
,
1367 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1374 /* Set Current Directory */
1377 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
1378 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
1380 if (SetCurrentDirectoryA(String
))
1382 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1386 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1387 EmulatorSetRegister(EMULATOR_REG_AX
,
1388 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
1398 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1399 (LPCSTR
)(ULONG_PTR
)BaseAddress
1400 + TO_LINEAR(DataSegment
, LOWORD(Edx
)),
1406 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1408 /* Return the handle in AX */
1409 EmulatorSetRegister(EMULATOR_REG_AX
,
1410 (Eax
& 0xFFFF0000) | FileHandle
);
1415 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1417 /* Return the error code in AX */
1418 EmulatorSetRegister(EMULATOR_REG_AX
,
1419 (Eax
& 0xFFFF0000) | ErrorCode
);
1429 WORD ErrorCode
= DosCreateFile(&FileHandle
,
1430 (LPCSTR
)(ULONG_PTR
)BaseAddress
1431 + TO_LINEAR(DataSegment
, LOWORD(Edx
)),
1437 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1439 /* Return the handle in AX */
1440 EmulatorSetRegister(EMULATOR_REG_AX
,
1441 (Eax
& 0xFFFF0000) | FileHandle
);
1446 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1448 /* Return the error code in AX */
1449 EmulatorSetRegister(EMULATOR_REG_AX
,
1450 (Eax
& 0xFFFF0000) | ErrorCode
);
1459 if (DosCloseHandle(LOWORD(Ebx
)))
1462 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1467 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1469 /* Return the error code in AX */
1470 EmulatorSetRegister(EMULATOR_REG_AX
,
1471 (Eax
& 0xFFFF0000) | ERROR_INVALID_HANDLE
);
1481 WORD ErrorCode
= DosReadFile(LOWORD(Ebx
),
1482 (LPVOID
)((ULONG_PTR
)BaseAddress
1483 + TO_LINEAR(DataSegment
, LOWORD(Edx
))),
1490 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1492 /* Return the number of bytes read in AX */
1493 EmulatorSetRegister(EMULATOR_REG_AX
,
1494 (Eax
& 0xFFFF0000) | BytesRead
);
1499 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1501 /* Return the error code in AX */
1502 EmulatorSetRegister(EMULATOR_REG_AX
,
1503 (Eax
& 0xFFFF0000) | ErrorCode
);
1511 WORD BytesWritten
= 0;
1512 WORD ErrorCode
= DosWriteFile(LOWORD(Ebx
),
1513 (LPVOID
)((ULONG_PTR
)BaseAddress
1514 + TO_LINEAR(DataSegment
, LOWORD(Edx
))),
1521 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1523 /* Return the number of bytes written in AX */
1524 EmulatorSetRegister(EMULATOR_REG_AX
,
1525 (Eax
& 0xFFFF0000) | BytesWritten
);
1530 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1532 /* Return the error code in AX */
1533 EmulatorSetRegister(EMULATOR_REG_AX
,
1534 (Eax
& 0xFFFF0000) | ErrorCode
);
1543 DosHandleIoctl(LOBYTE(Eax
), LOWORD(Ebx
));
1548 /* Allocate Memory */
1551 WORD MaxAvailable
= 0;
1552 WORD Segment
= DosAllocateMemory(LOWORD(Ebx
), &MaxAvailable
);
1556 EmulatorSetRegister(EMULATOR_REG_AX
, Segment
);
1557 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1561 EmulatorSetRegister(EMULATOR_REG_AX
, DosLastError
);
1562 EmulatorSetRegister(EMULATOR_REG_BX
, MaxAvailable
);
1563 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1572 if (DosFreeMemory(ExtSegment
))
1574 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1578 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_ARENA_TRASHED
);
1579 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1585 /* Resize Memory Block */
1590 if (DosResizeMemory(ExtSegment
, LOWORD(Ebx
), &Size
))
1592 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1596 EmulatorSetRegister(EMULATOR_REG_AX
, DosLastError
);
1597 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1598 EmulatorSetRegister(EMULATOR_REG_BX
, Size
);
1604 /* Terminate With Return Code */
1607 DosTerminateProcess(CurrentPsp
, LOBYTE(Eax
));
1611 /* Get/Set Memory Management Options */
1614 if (LOBYTE(Eax
) == 0x00)
1616 /* Get allocation strategy */
1618 EmulatorSetRegister(EMULATOR_REG_AX
, DosAllocStrategy
);
1619 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1621 else if (LOBYTE(Eax
) == 0x01)
1623 /* Set allocation strategy */
1625 if ((LOBYTE(Ebx
) & (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1626 == (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
))
1628 /* Can't set both */
1629 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_PARAMETER
);
1630 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1634 if ((LOBYTE(Ebx
) & 0x3F) > DOS_ALLOC_LAST_FIT
)
1636 /* Invalid allocation strategy */
1637 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_PARAMETER
);
1638 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1642 DosAllocStrategy
= LOBYTE(Ebx
);
1643 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1645 else if (LOBYTE(Eax
) == 0x02)
1647 /* Get UMB link state */
1650 if (DosUmbLinked
) Eax
|= 1;
1651 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
);
1652 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1654 else if (LOBYTE(Eax
) == 0x03)
1656 /* Set UMB link state */
1658 if (Ebx
) DosLinkUmb();
1659 else DosUnlinkUmb();
1660 EmulatorClearFlag(EMULATOR_FLAG_CF
);
1664 /* Invalid or unsupported function */
1666 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1667 EmulatorSetRegister(EMULATOR_REG_AX
, ERROR_INVALID_FUNCTION
);
1676 DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax
));
1677 EmulatorSetFlag(EMULATOR_FLAG_CF
);
1682 VOID
DosBreakInterrupt(VOID
)
1687 BOOLEAN
DosInitialize(VOID
)
1690 PDOS_MCB Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
);
1693 LPWSTR SourcePtr
, Environment
;
1695 LPSTR DestPtr
= (LPSTR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(SYSTEM_ENV_BLOCK
, 0));
1698 /* Initialize the MCB */
1699 Mcb
->BlockType
= 'Z';
1700 Mcb
->Size
= USER_MEMORY_SIZE
;
1703 /* Initialize the link MCB to the UMB area */
1704 Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
+ 1);
1705 Mcb
->BlockType
= 'M';
1706 Mcb
->Size
= UMB_START_SEGMENT
- FIRST_MCB_SEGMENT
- USER_MEMORY_SIZE
- 2;
1707 Mcb
->OwnerPsp
= SYSTEM_PSP
;
1709 /* Initialize the UMB area */
1710 Mcb
= SEGMENT_TO_MCB(UMB_START_SEGMENT
);
1711 Mcb
->BlockType
= 'Z';
1712 Mcb
->Size
= UMB_END_SEGMENT
- UMB_START_SEGMENT
;
1715 /* Get the environment strings */
1716 SourcePtr
= Environment
= GetEnvironmentStringsW();
1717 if (Environment
== NULL
) return FALSE
;
1719 /* Fill the DOS system environment block */
1722 /* Get the size of the ASCII string */
1723 AsciiSize
= WideCharToMultiByte(CP_ACP
,
1732 /* Allocate memory for the ASCII string */
1733 AsciiString
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, AsciiSize
);
1734 if (AsciiString
== NULL
)
1736 FreeEnvironmentStringsW(Environment
);
1740 /* Convert to ASCII */
1741 WideCharToMultiByte(CP_ACP
,
1750 /* Copy the string into DOS memory */
1751 strcpy(DestPtr
, AsciiString
);
1753 /* Free the memory */
1754 HeapFree(GetProcessHeap(), 0, AsciiString
);
1756 /* Move to the next string */
1757 SourcePtr
+= wcslen(SourcePtr
) + 1;
1758 DestPtr
+= strlen(AsciiString
);
1763 /* Free the memory allocated for environment strings */
1764 FreeEnvironmentStringsW(Environment
);
1766 /* Read CONFIG.SYS */
1767 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
1770 while (fgetws(Buffer
, 256, Stream
))
1772 // TODO: Parse the line
1777 /* Initialize the SFT */
1778 for (i
= 0; i
< DOS_SFT_SIZE
; i
++)
1780 DosSystemFileTable
[i
] = INVALID_HANDLE_VALUE
;
1781 DosSftRefCount
[i
] = 0;
1784 /* Get handles to standard I/O devices */
1785 DosSystemFileTable
[0] = GetStdHandle(STD_INPUT_HANDLE
);
1786 DosSystemFileTable
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
1787 DosSystemFileTable
[2] = GetStdHandle(STD_ERROR_HANDLE
);