a78c63c3bcf89c735a9d4fa6385a077fc747238e
[reactos.git] / subsystems / ntvdm / dos.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: dos.c
5 * PURPOSE: VDM DOS Kernel
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "dos.h"
12 #include "bios.h"
13 #include "emulator.h"
14
15 /* PRIVATE VARIABLES **********************************************************/
16
17 static WORD CurrentPsp = SYSTEM_PSP;
18 static DWORD DiskTransferArea;
19 static HANDLE DosSystemFileTable[DOS_SFT_SIZE];
20 static WORD DosSftRefCount[DOS_SFT_SIZE];
21
22 /* PRIVATE FUNCTIONS **********************************************************/
23
24 static VOID DosCombineFreeBlocks(WORD StartBlock)
25 {
26 PDOS_MCB CurrentMcb = SEGMENT_TO_MCB(StartBlock), NextMcb;
27
28 /* If this is the last block or it's not free, quit */
29 if (CurrentMcb->BlockType == 'Z' || CurrentMcb->OwnerPsp != 0) return;
30
31 while (TRUE)
32 {
33 /* Get a pointer to the next MCB */
34 NextMcb = SEGMENT_TO_MCB(StartBlock + CurrentMcb->Size + 1);
35
36 /* Check if the next MCB is free */
37 if (NextMcb->OwnerPsp == 0)
38 {
39 /* Combine them */
40 CurrentMcb->Size += NextMcb->Size + 1;
41 CurrentMcb->BlockType = NextMcb->BlockType;
42 NextMcb->BlockType = 'I';
43 }
44 else
45 {
46 /* No more adjoining free blocks */
47 break;
48 }
49 }
50 }
51
52 static WORD DosCopyEnvironmentBlock(WORD SourceSegment)
53 {
54 PCHAR Ptr, SourceBuffer, DestBuffer = NULL;
55 ULONG TotalSize = 0;
56 WORD DestSegment;
57
58 Ptr = SourceBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(SourceSegment, 0));
59
60 /* Calculate the size of the environment block */
61 while (*Ptr)
62 {
63 TotalSize += strlen(Ptr) + 1;
64 Ptr += strlen(Ptr) + 1;
65 }
66 TotalSize++;
67
68 /* Allocate the memory for the environment block */
69 DestSegment = DosAllocateMemory((TotalSize + 0x0F) >> 4, NULL);
70 if (!DestSegment) return 0;
71
72 Ptr = SourceBuffer;
73
74 DestBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(DestSegment, 0));
75 while (*Ptr)
76 {
77 /* Copy the string */
78 strcpy(DestBuffer, Ptr);
79
80 /* Advance to the next string */
81 Ptr += strlen(Ptr) + 1;
82 DestBuffer += strlen(Ptr);
83
84 /* Put a zero after the string */
85 *(DestBuffer++) = 0;
86 }
87
88 /* Set the final zero */
89 *DestBuffer = 0;
90
91 return DestSegment;
92 }
93
94 static VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner)
95 {
96 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1);
97
98 /* Just set the owner */
99 Mcb->OwnerPsp = NewOwner;
100 }
101
102 static WORD DosOpenHandle(HANDLE Handle)
103 {
104 BYTE i;
105 WORD DosHandle;
106 PDOS_PSP PspBlock;
107 LPBYTE HandleTable;
108
109 /* The system PSP has no handle table */
110 if (CurrentPsp == SYSTEM_PSP) return INVALID_DOS_HANDLE;
111
112 /* Get a pointer to the handle table */
113 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
114 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
115
116 /* Find a free entry in the JFT */
117 for (DosHandle = 0; DosHandle < PspBlock->HandleTableSize; DosHandle++)
118 {
119 if (HandleTable[DosHandle] == 0xFF) break;
120 }
121
122 /* If there are no free entries, fail */
123 if (DosHandle == PspBlock->HandleTableSize) return INVALID_DOS_HANDLE;
124
125 /* Check if the handle is already in the SFT */
126 for (i = 0; i < DOS_SFT_SIZE; i++)
127 {
128 /* Check if this is the same handle */
129 if (DosSystemFileTable[i] != Handle) continue;
130
131 /* Already in the table, reference it */
132 DosSftRefCount[i]++;
133
134 /* Set the JFT entry to that SFT index */
135 HandleTable[DosHandle] = i;
136
137 /* Return the new handle */
138 return DosHandle;
139 }
140
141 /* Add the handle to the SFT */
142 for (i = 0; i < DOS_SFT_SIZE; i++)
143 {
144 /* Make sure this is an empty table entry */
145 if (DosSystemFileTable[i] != INVALID_HANDLE_VALUE) continue;
146
147 /* Initialize the empty table entry */
148 DosSystemFileTable[i] = Handle;
149 DosSftRefCount[i] = 1;
150
151 /* Set the JFT entry to that SFT index */
152 HandleTable[DosHandle] = i;
153
154 /* Return the new handle */
155 return DosHandle;
156 }
157
158 /* The SFT is full */
159 return INVALID_DOS_HANDLE;
160 }
161
162 static HANDLE DosGetRealHandle(WORD DosHandle)
163 {
164 PDOS_PSP PspBlock;
165 LPBYTE HandleTable;
166
167 /* The system PSP has no handle table */
168 if (CurrentPsp == SYSTEM_PSP) return INVALID_HANDLE_VALUE;
169
170 /* Get a pointer to the handle table */
171 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
172 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
173
174 /* Make sure the handle is open */
175 if (HandleTable[DosHandle] == 0xFF) return INVALID_HANDLE_VALUE;
176
177 /* Return the Win32 handle */
178 return DosSystemFileTable[HandleTable[DosHandle]];
179 }
180
181 static VOID DosCopyHandleTable(LPBYTE DestinationTable)
182 {
183 INT i;
184 PDOS_PSP PspBlock;
185 LPBYTE SourceTable;
186
187 /* Clear the table first */
188 for (i = 0; i < 20; i++) DestinationTable[i] = 0xFF;
189
190 /* Check if this is the initial process */
191 if (CurrentPsp == SYSTEM_PSP)
192 {
193 /* Set up the standard I/O devices */
194 for (i = 0; i <= 2; i++)
195 {
196 /* Set the index in the SFT */
197 DestinationTable[i] = i;
198
199 /* Increase the reference count */
200 DosSftRefCount[i]++;
201 }
202
203 /* Done */
204 return;
205 }
206
207 /* Get the parent PSP block and handle table */
208 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
209 SourceTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
210
211 /* Copy the first 20 handles into the new table */
212 for (i = 0; i < 20; i++)
213 {
214 DestinationTable[i] = SourceTable[i];
215
216 /* Increase the reference count */
217 DosSftRefCount[SourceTable[i]]++;
218 }
219 }
220
221 /* PUBLIC FUNCTIONS ***********************************************************/
222
223 WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable)
224 {
225 WORD Result = 0, Segment = FIRST_MCB_SEGMENT, MaxSize = 0;
226 PDOS_MCB CurrentMcb, NextMcb;
227
228 while (TRUE)
229 {
230 /* Get a pointer to the MCB */
231 CurrentMcb = SEGMENT_TO_MCB(Segment);
232
233 /* Make sure it's valid */
234 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z')
235 {
236 return 0;
237 }
238
239 /* Only check free blocks */
240 if (CurrentMcb->OwnerPsp != 0) goto Next;
241
242 /* Combine this free block with adjoining free blocks */
243 DosCombineFreeBlocks(Segment);
244
245 /* Update the maximum block size */
246 if (CurrentMcb->Size > MaxSize) MaxSize = CurrentMcb->Size;
247
248 /* Check if this block is big enough */
249 if (CurrentMcb->Size < Size) goto Next;
250
251 /* It is, update the smallest found so far */
252 if ((Result == 0) || (CurrentMcb->Size < SEGMENT_TO_MCB(Result)->Size))
253 {
254 Result = Segment;
255 }
256
257 Next:
258 /* If this was the last MCB in the chain, quit */
259 if (CurrentMcb->BlockType == 'Z') break;
260
261 /* Otherwise, update the segment and continue */
262 Segment += CurrentMcb->Size + 1;
263 }
264
265 /* If we didn't find a free block, return 0 */
266 if (Result == 0)
267 {
268 if (MaxAvailable) *MaxAvailable = MaxSize;
269 return 0;
270 }
271
272 /* Get a pointer to the MCB */
273 CurrentMcb = SEGMENT_TO_MCB(Result);
274
275 /* Check if the block is larger than requested */
276 if (CurrentMcb->Size > Size)
277 {
278 /* It is, split it into two blocks */
279 NextMcb = SEGMENT_TO_MCB(Result + Size + 1);
280
281 /* Initialize the new MCB structure */
282 NextMcb->BlockType = CurrentMcb->BlockType;
283 NextMcb->Size = CurrentMcb->Size - Size - 1;
284 NextMcb->OwnerPsp = 0;
285
286 /* Update the current block */
287 CurrentMcb->BlockType = 'M';
288 CurrentMcb->Size = Size;
289 }
290
291 /* Take ownership of the block */
292 CurrentMcb->OwnerPsp = CurrentPsp;
293
294 /* Return the segment of the data portion of the block */
295 return Result + 1;
296 }
297
298 BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable)
299 {
300 BOOLEAN Success = TRUE;
301 WORD Segment = BlockData - 1, ReturnSize = 0, NextSegment;
302 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment), NextMcb;
303
304 /* Make sure this is a valid, allocated block */
305 if ((Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') || Mcb->OwnerPsp == 0)
306 {
307 Success = FALSE;
308 goto Done;
309 }
310
311 ReturnSize = Mcb->Size;
312
313 /* Check if we need to expand or contract the block */
314 if (NewSize > Mcb->Size)
315 {
316 /* We can't expand the last block */
317 if (Mcb->BlockType != 'M')
318 {
319 Success = FALSE;
320 goto Done;
321 }
322
323 /* Get the pointer and segment of the next MCB */
324 NextSegment = Segment + Mcb->Size + 1;
325 NextMcb = SEGMENT_TO_MCB(NextSegment);
326
327 /* Make sure the next segment is free */
328 if (NextMcb->OwnerPsp != 0)
329 {
330 Success = FALSE;
331 goto Done;
332 }
333
334 /* Combine this free block with adjoining free blocks */
335 DosCombineFreeBlocks(NextSegment);
336
337 /* Set the maximum possible size of the block */
338 ReturnSize += NextMcb->Size + 1;
339
340 /* Maximize the current block */
341 Mcb->Size = ReturnSize;
342 Mcb->BlockType = NextMcb->BlockType;
343
344 /* Invalidate the next block */
345 NextMcb->BlockType = 'I';
346
347 /* Check if the block is larger than requested */
348 if (Mcb->Size > NewSize)
349 {
350 /* It is, split it into two blocks */
351 NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
352
353 /* Initialize the new MCB structure */
354 NextMcb->BlockType = Mcb->BlockType;
355 NextMcb->Size = Mcb->Size - NewSize - 1;
356 NextMcb->OwnerPsp = 0;
357
358 /* Update the current block */
359 Mcb->BlockType = 'M';
360 Mcb->Size = NewSize;
361 }
362 }
363 else if (NewSize < Mcb->Size)
364 {
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;
370
371 /* Update the MCB */
372 Mcb->BlockType = 'M';
373 Mcb->Size = NewSize;
374 }
375
376 Done:
377 /* Check if the operation failed */
378 if (!Success)
379 {
380 /* Return the maximum possible size */
381 if (MaxAvailable) *MaxAvailable = ReturnSize;
382 }
383
384 return Success;
385 }
386
387 BOOLEAN DosFreeMemory(WORD BlockData)
388 {
389 PDOS_MCB Mcb = SEGMENT_TO_MCB(BlockData - 1);
390
391 /* Make sure the MCB is valid */
392 if (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') return FALSE;
393
394 /* Mark the block as free */
395 Mcb->OwnerPsp = 0;
396
397 return TRUE;
398 }
399
400 WORD DosCreateFile(LPWORD Handle, LPCSTR FilePath, WORD Attributes)
401 {
402 HANDLE FileHandle;
403 WORD DosHandle;
404
405 /* Create the file */
406 FileHandle = CreateFileA(FilePath,
407 GENERIC_READ | GENERIC_WRITE,
408 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
409 NULL,
410 CREATE_ALWAYS,
411 Attributes,
412 NULL);
413
414 if (FileHandle == INVALID_HANDLE_VALUE)
415 {
416 /* Return the error code */
417 return GetLastError();
418 }
419
420 /* Open the DOS handle */
421 DosHandle = DosOpenHandle(FileHandle);
422
423 if (DosHandle == INVALID_DOS_HANDLE)
424 {
425 /* Close the handle */
426 CloseHandle(FileHandle);
427
428 /* Return the error code */
429 return ERROR_TOO_MANY_OPEN_FILES;
430 }
431
432 /* It was successful */
433 *Handle = DosHandle;
434 return ERROR_SUCCESS;
435 }
436
437 WORD DosOpenFile(LPWORD Handle, LPCSTR FilePath, BYTE AccessMode)
438 {
439 HANDLE FileHandle;
440 ACCESS_MASK Access = 0;
441 WORD DosHandle;
442
443 /* Parse the access mode */
444 switch (AccessMode & 3)
445 {
446 case 0:
447 {
448 /* Read-only */
449 Access = GENERIC_READ;
450 break;
451 }
452
453 case 1:
454 {
455 /* Write only */
456 Access = GENERIC_WRITE;
457 break;
458 }
459
460 case 2:
461 {
462 /* Read and write */
463 Access = GENERIC_READ | GENERIC_WRITE;
464 break;
465 }
466
467 default:
468 {
469 /* Invalid */
470 return ERROR_INVALID_PARAMETER;
471 }
472 }
473
474 /* Open the file */
475 FileHandle = CreateFileA(FilePath,
476 Access,
477 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
478 NULL,
479 OPEN_EXISTING,
480 FILE_ATTRIBUTE_NORMAL,
481 NULL);
482
483 if (FileHandle == INVALID_HANDLE_VALUE)
484 {
485 /* Return the error code */
486 return GetLastError();
487 }
488
489 /* Open the DOS handle */
490 DosHandle = DosOpenHandle(FileHandle);
491
492 if (DosHandle == INVALID_DOS_HANDLE)
493 {
494 /* Close the handle */
495 CloseHandle(FileHandle);
496
497 /* Return the error code */
498 return ERROR_TOO_MANY_OPEN_FILES;
499 }
500
501 /* It was successful */
502 *Handle = DosHandle;
503 return ERROR_SUCCESS;
504 }
505
506 WORD DosReadFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesRead)
507 {
508 WORD Result = ERROR_SUCCESS;
509 DWORD BytesRead32 = 0;
510 HANDLE Handle = DosGetRealHandle(FileHandle);
511
512 /* Make sure the handle is valid */
513 if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_PARAMETER;
514
515 /* Read the file */
516 if (!ReadFile(Handle, Buffer, Count, &BytesRead32, NULL))
517 {
518 /* Store the error code */
519 Result = GetLastError();
520 }
521
522 /* The number of bytes read is always 16-bit */
523 *BytesRead = LOWORD(BytesRead32);
524
525 /* Return the error code */
526 return Result;
527 }
528
529 WORD DosWriteFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesWritten)
530 {
531 WORD Result = ERROR_SUCCESS;
532 DWORD BytesWritten32 = 0;
533 HANDLE Handle = DosGetRealHandle(FileHandle);
534
535 /* Make sure the handle is valid */
536 if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_PARAMETER;
537
538 /* Write the file */
539 if (!WriteFile(Handle, Buffer, Count, &BytesWritten32, NULL))
540 {
541 /* Store the error code */
542 Result = GetLastError();
543 }
544
545 /* The number of bytes written is always 16-bit */
546 *BytesWritten = LOWORD(BytesWritten32);
547
548 /* Return the error code */
549 return Result;
550 }
551
552 BOOLEAN DosCloseHandle(WORD DosHandle)
553 {
554 BYTE SftIndex;
555 PDOS_PSP PspBlock;
556 LPBYTE HandleTable;
557
558 /* The system PSP has no handle table */
559 if (CurrentPsp == SYSTEM_PSP) return FALSE;
560
561 /* Get a pointer to the handle table */
562 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
563 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
564
565 /* Make sure the handle is open */
566 if (HandleTable[DosHandle] == 0xFF) return FALSE;
567
568 /* Decrement the reference count of the SFT entry */
569 SftIndex = HandleTable[DosHandle];
570 DosSftRefCount[SftIndex]--;
571
572 /* Check if the reference count fell to zero */
573 if (!DosSftRefCount[SftIndex])
574 {
575 /* Close the file, it's no longer needed */
576 CloseHandle(DosSystemFileTable[SftIndex]);
577
578 /* Clear the handle */
579 DosSystemFileTable[SftIndex] = INVALID_HANDLE_VALUE;
580 }
581
582 return TRUE;
583 }
584
585 VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment)
586 {
587 PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment);
588 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
589
590 ZeroMemory(PspBlock, sizeof(DOS_PSP));
591
592 /* Set the exit interrupt */
593 PspBlock->Exit[0] = 0xCD; // int 0x20
594 PspBlock->Exit[1] = 0x20;
595
596 /* Set the number of the last paragraph */
597 PspBlock->LastParagraph = PspSegment + ProgramSize - 1;
598
599 /* Save the interrupt vectors */
600 PspBlock->TerminateAddress = IntVecTable[0x22];
601 PspBlock->BreakAddress = IntVecTable[0x23];
602 PspBlock->CriticalAddress = IntVecTable[0x24];
603
604 /* Set the parent PSP */
605 PspBlock->ParentPsp = CurrentPsp;
606
607 /* Copy the parent handle table */
608 DosCopyHandleTable(PspBlock->HandleTable);
609
610 /* Set the environment block */
611 PspBlock->EnvBlock = Environment;
612
613 /* Set the handle table pointers to the internal handle table */
614 PspBlock->HandleTableSize = 20;
615 PspBlock->HandleTablePtr = MAKELONG(0x18, PspSegment);
616
617 /* Set the DOS version */
618 PspBlock->DosVersion = DOS_VERSION;
619
620 /* Set the far call opcodes */
621 PspBlock->FarCall[0] = 0xCD; // int 0x21
622 PspBlock->FarCall[1] = 0x21;
623 PspBlock->FarCall[2] = 0xCB; // retf
624
625 /* Set the command line */
626 PspBlock->CommandLineSize = strlen(CommandLine);
627 RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
628 PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
629 }
630
631 BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
632 {
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];
638 INT ParamCount = 0;
639 DWORD Segment = 0;
640 DWORD i, FileSize, ExeSize;
641 PIMAGE_DOS_HEADER Header;
642 PDWORD RelocationTable;
643 PWORD RelocWord;
644
645 /* Save a copy of the command line */
646 strcpy(CommandLineCopy, CommandLine);
647
648 /* Get the file name of the executable */
649 ProgramFilePath = strtok(CommandLineCopy, " \t");
650
651 /* Load the parameters in the local array */
652 while ((ParamCount < 256)
653 && ((Parameters[ParamCount] = strtok(NULL, " \t")) != NULL))
654 {
655 ParamCount++;
656 }
657
658 /* Open a handle to the executable */
659 FileHandle = CreateFileA(ProgramFilePath,
660 GENERIC_READ,
661 0,
662 NULL,
663 OPEN_EXISTING,
664 FILE_ATTRIBUTE_NORMAL,
665 NULL);
666 if (FileHandle == INVALID_HANDLE_VALUE) goto Cleanup;
667
668 /* Get the file size */
669 FileSize = GetFileSize(FileHandle, NULL);
670
671 /* Create a mapping object for the file */
672 FileMapping = CreateFileMapping(FileHandle,
673 NULL,
674 PAGE_READONLY,
675 0,
676 0,
677 NULL);
678 if (FileMapping == NULL) goto Cleanup;
679
680 /* Map the file into memory */
681 Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
682 if (Address == NULL) goto Cleanup;
683
684 /* Did we get an environment segment? */
685 if (!EnvBlock)
686 {
687 /* Set a flag to know if the environment block was allocated here */
688 AllocatedEnvBlock = TRUE;
689
690 /* No, copy the one from the parent */
691 EnvBlock = DosCopyEnvironmentBlock((CurrentPsp != SYSTEM_PSP)
692 ? SEGMENT_TO_PSP(CurrentPsp)->EnvBlock
693 : SYSTEM_ENV_BLOCK);
694 }
695
696 /* Check if this is an EXE file or a COM file */
697 if (Address[0] == 'M' && Address[1] == 'Z')
698 {
699 /* EXE file */
700
701 /* Get the MZ header */
702 Header = (PIMAGE_DOS_HEADER)Address;
703
704 // TODO: Verify checksum and executable!
705
706 /* Get the base size of the file, in paragraphs (rounded up) */
707 ExeSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
708
709 /* Add the PSP size, in paragraphs */
710 ExeSize += sizeof(DOS_PSP) >> 4;
711
712 /* Add the maximum size that should be allocated */
713 ExeSize += Header->e_maxalloc;
714
715 /* Make sure it does not pass 0xFFFF */
716 if (ExeSize > 0xFFFF) ExeSize = 0xFFFF;
717
718 /* Reduce the size one by one until the allocation is successful */
719 for (i = Header->e_maxalloc; i >= Header->e_minalloc; i--, ExeSize--)
720 {
721 /* Try to allocate that much memory */
722 Segment = DosAllocateMemory(ExeSize, NULL);
723 if (Segment != 0) break;
724 }
725
726 /* Check if at least the lowest allocation was successful */
727 if (Segment == 0) goto Cleanup;
728
729 /* Initialize the PSP */
730 DosInitializePsp(Segment,
731 CommandLine,
732 ExeSize,
733 EnvBlock);
734
735 /* The process owns its own memory */
736 DosChangeMemoryOwner(Segment, Segment);
737 DosChangeMemoryOwner(EnvBlock, Segment);
738
739 /* Copy the program to Segment:0100 */
740 RtlCopyMemory((PVOID)((ULONG_PTR)BaseAddress
741 + TO_LINEAR(Segment, 0x100)),
742 Address + (Header->e_cparhdr << 4),
743 FileSize - (Header->e_cparhdr << 4));
744
745 /* Get the relocation table */
746 RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
747
748 /* Perform relocations */
749 for (i = 0; i < Header->e_crlc; i++)
750 {
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])));
755
756 /* Add the number of the EXE segment to it */
757 *RelocWord += Segment + (sizeof(DOS_PSP) >> 4);
758 }
759
760 /* Set the initial segment registers */
761 EmulatorSetRegister(EMULATOR_REG_DS, Segment);
762 EmulatorSetRegister(EMULATOR_REG_ES, Segment);
763
764 /* Set the stack to the location from the header */
765 EmulatorSetStack(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss,
766 Header->e_sp);
767
768 /* Execute */
769 CurrentPsp = Segment;
770 DiskTransferArea = MAKELONG(0x80, Segment);
771 EmulatorExecute(Segment + Header->e_cs + (sizeof(DOS_PSP) >> 4),
772 Header->e_ip);
773
774 Success = TRUE;
775 }
776 else
777 {
778 /* COM file */
779
780 /* Allocate memory for the whole program and the PSP */
781 Segment = DosAllocateMemory((FileSize + sizeof(DOS_PSP)) >> 4, NULL);
782 if (Segment == 0) goto Cleanup;
783
784 /* Copy the program to Segment:0100 */
785 RtlCopyMemory((PVOID)((ULONG_PTR)BaseAddress
786 + TO_LINEAR(Segment, 0x100)),
787 Address,
788 FileSize);
789
790 /* Initialize the PSP */
791 DosInitializePsp(Segment,
792 CommandLine,
793 (FileSize + sizeof(DOS_PSP)) >> 4,
794 EnvBlock);
795
796 /* Set the initial segment registers */
797 EmulatorSetRegister(EMULATOR_REG_DS, Segment);
798 EmulatorSetRegister(EMULATOR_REG_ES, Segment);
799
800 /* Set the stack to the last word of the segment */
801 EmulatorSetStack(Segment, 0xFFFE);
802
803 /* Execute */
804 CurrentPsp = Segment;
805 DiskTransferArea = MAKELONG(0x80, Segment);
806 EmulatorExecute(Segment, 0x100);
807
808 Success = TRUE;
809 }
810
811 Cleanup:
812 if (!Success)
813 {
814 /* It was not successful, cleanup the DOS memory */
815 if (AllocatedEnvBlock) DosFreeMemory(EnvBlock);
816 if (Segment) DosFreeMemory(Segment);
817 }
818
819 /* Unmap the file*/
820 if (Address != NULL) UnmapViewOfFile(Address);
821
822 /* Close the file mapping object */
823 if (FileMapping != NULL) CloseHandle(FileMapping);
824
825 /* Close the file handle */
826 if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
827
828 return Success;
829 }
830
831 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
832 {
833 WORD McbSegment = FIRST_MCB_SEGMENT;
834 PDOS_MCB CurrentMcb;
835 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
836 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
837
838 /* Check if this PSP is it's own parent */
839 if (PspBlock->ParentPsp == Psp) goto Done;
840
841 // TODO: Close all handles opened by the process
842
843 /* Free the memory used by the process */
844 while (TRUE)
845 {
846 /* Get a pointer to the MCB */
847 CurrentMcb = SEGMENT_TO_MCB(McbSegment);
848
849 /* Make sure the MCB is valid */
850 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType !='Z') break;
851
852 /* If this block was allocated by the process, free it */
853 if (CurrentMcb->OwnerPsp == Psp) DosFreeMemory(McbSegment);
854
855 /* If this was the last block, quit */
856 if (CurrentMcb->BlockType == 'Z') break;
857
858 /* Update the segment and continue */
859 McbSegment += CurrentMcb->Size + 1;
860 }
861
862 Done:
863 /* Restore the interrupt vectors */
864 IntVecTable[0x22] = PspBlock->TerminateAddress;
865 IntVecTable[0x23] = PspBlock->BreakAddress;
866 IntVecTable[0x24] = PspBlock->CriticalAddress;
867
868 /* Update the current PSP */
869 if (Psp == CurrentPsp)
870 {
871 CurrentPsp = PspBlock->ParentPsp;
872 if (CurrentPsp == SYSTEM_PSP) VdmRunning = FALSE;
873 }
874
875 /* Return control to the parent process */
876 EmulatorExecute(HIWORD(PspBlock->TerminateAddress),
877 LOWORD(PspBlock->TerminateAddress));
878 }
879
880 CHAR DosReadCharacter()
881 {
882 CHAR Character = '\0';
883 WORD BytesRead;
884
885 /* Use the file reading function */
886 DosReadFile(DOS_INPUT_HANDLE, &Character, sizeof(CHAR), &BytesRead);
887
888 return Character;
889 }
890
891 VOID DosPrintCharacter(CHAR Character)
892 {
893 WORD BytesWritten;
894
895 /* Use the file writing function */
896 DosWriteFile(DOS_OUTPUT_HANDLE, &Character, sizeof(CHAR), &BytesWritten);
897 }
898
899 VOID DosInt20h(WORD CodeSegment)
900 {
901 /* This is the exit interrupt */
902 DosTerminateProcess(CodeSegment, 0);
903 }
904
905 VOID DosInt21h(WORD CodeSegment)
906 {
907 INT i;
908 CHAR Character;
909 SYSTEMTIME SystemTime;
910 PCHAR String;
911 PDOS_INPUT_BUFFER InputBuffer;
912 DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
913 DWORD Ecx = EmulatorGetRegister(EMULATOR_REG_CX);
914 DWORD Edx = EmulatorGetRegister(EMULATOR_REG_DX);
915 DWORD Ebx = EmulatorGetRegister(EMULATOR_REG_BX);
916 WORD DataSegment = EmulatorGetRegister(EMULATOR_REG_DS);
917 WORD ExtSegment = EmulatorGetRegister(EMULATOR_REG_ES);
918
919 /* Check the value in the AH register */
920 switch (HIBYTE(Eax))
921 {
922 /* Terminate Program */
923 case 0x00:
924 {
925 DosTerminateProcess(CodeSegment, 0);
926 break;
927 }
928
929 /* Read Character And Echo */
930 case 0x01:
931 {
932 Character = DosReadCharacter();
933 DosPrintCharacter(Character);
934 EmulatorSetRegister(EMULATOR_REG_AX, (Eax & 0xFFFFFF00) | Character);
935 break;
936 }
937
938 /* Print Character */
939 case 0x02:
940 {
941 DosPrintCharacter(LOBYTE(Edx));
942 break;
943 }
944
945 /* Read Character Without Echo */
946 case 0x07:
947 case 0x08:
948 {
949 EmulatorSetRegister(EMULATOR_REG_AX,
950 (Eax & 0xFFFFFF00) | DosReadCharacter());
951 break;
952 }
953
954 /* Print String */
955 case 0x09:
956 {
957 String = (PCHAR)((ULONG_PTR)BaseAddress
958 + TO_LINEAR(DataSegment, LOWORD(Edx)));
959
960 while ((*String) != '$')
961 {
962 DosPrintCharacter(*String);
963 String++;
964 }
965
966 break;
967 }
968
969 /* Read Buffered Input */
970 case 0x0A:
971 {
972 InputBuffer = (PDOS_INPUT_BUFFER)((ULONG_PTR)BaseAddress
973 + TO_LINEAR(DataSegment,
974 LOWORD(Edx)));
975
976 InputBuffer->Length = 0;
977 for (i = 0; i < InputBuffer->MaxLength; i ++)
978 {
979 Character = DosReadCharacter();
980 DosPrintCharacter(Character);
981 InputBuffer->Buffer[InputBuffer->Length] = Character;
982 if (Character == '\r') break;
983 InputBuffer->Length++;
984 }
985
986 break;
987 }
988
989 /* Set Disk Transfer Area */
990 case 0x1A:
991 {
992 DiskTransferArea = MAKELONG(LOWORD(Edx), DataSegment);
993 break;
994 }
995
996 /* Set Interrupt Vector */
997 case 0x25:
998 {
999 DWORD FarPointer = MAKELONG(LOWORD(Edx), DataSegment);
1000
1001 /* Write the new far pointer to the IDT */
1002 ((PDWORD)BaseAddress)[LOBYTE(Eax)] = FarPointer;
1003
1004 break;
1005 }
1006
1007 /* Get system date */
1008 case 0x2A:
1009 {
1010 GetLocalTime(&SystemTime);
1011 EmulatorSetRegister(EMULATOR_REG_CX,
1012 (Ecx & 0xFFFF0000) | SystemTime.wYear);
1013 EmulatorSetRegister(EMULATOR_REG_DX,
1014 (Edx & 0xFFFF0000)
1015 | (SystemTime.wMonth << 8)
1016 | SystemTime.wDay);
1017 EmulatorSetRegister(EMULATOR_REG_AX,
1018 (Eax & 0xFFFFFF00) | SystemTime.wDayOfWeek);
1019 break;
1020 }
1021
1022 /* Set system date */
1023 case 0x2B:
1024 {
1025 GetLocalTime(&SystemTime);
1026 SystemTime.wYear = LOWORD(Ecx);
1027 SystemTime.wMonth = HIBYTE(Edx);
1028 SystemTime.wDay = LOBYTE(Edx);
1029
1030 if (SetLocalTime(&SystemTime))
1031 {
1032 /* Return success */
1033 EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
1034 }
1035 else
1036 {
1037 /* Return failure */
1038 EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
1039 }
1040
1041 break;
1042 }
1043
1044 /* Get system time */
1045 case 0x2C:
1046 {
1047 GetLocalTime(&SystemTime);
1048 EmulatorSetRegister(EMULATOR_REG_CX,
1049 (Ecx & 0xFFFF0000)
1050 | (SystemTime.wHour << 8)
1051 | SystemTime.wMinute);
1052 EmulatorSetRegister(EMULATOR_REG_DX,
1053 (Edx & 0xFFFF0000)
1054 | (SystemTime.wSecond << 8)
1055 | (SystemTime.wMilliseconds / 10));
1056 break;
1057 }
1058
1059 /* Set system time */
1060 case 0x2D:
1061 {
1062 GetLocalTime(&SystemTime);
1063 SystemTime.wHour = HIBYTE(Ecx);
1064 SystemTime.wMinute = LOBYTE(Ecx);
1065 SystemTime.wSecond = HIBYTE(Edx);
1066 SystemTime.wMilliseconds = LOBYTE(Edx) * 10;
1067
1068 if (SetLocalTime(&SystemTime))
1069 {
1070 /* Return success */
1071 EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
1072 }
1073 else
1074 {
1075 /* Return failure */
1076 EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
1077 }
1078
1079 break;
1080 }
1081
1082 /* Get Disk Transfer Area */
1083 case 0x2F:
1084 {
1085 EmulatorSetRegister(EMULATOR_REG_ES, HIWORD(DiskTransferArea));
1086 EmulatorSetRegister(EMULATOR_REG_BX, LOWORD(DiskTransferArea));
1087
1088 break;
1089 }
1090
1091 /* Get DOS Version */
1092 case 0x30:
1093 {
1094 PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
1095
1096 EmulatorSetRegister(EMULATOR_REG_AX, PspBlock->DosVersion);
1097 break;
1098 }
1099
1100 /* Get Interrupt Vector */
1101 case 0x35:
1102 {
1103 DWORD FarPointer = ((PDWORD)BaseAddress)[LOBYTE(Eax)];
1104
1105 /* Read the address from the IDT into ES:BX */
1106 EmulatorSetRegister(EMULATOR_REG_ES, HIWORD(FarPointer));
1107 EmulatorSetRegister(EMULATOR_REG_BX, LOWORD(FarPointer));
1108
1109 break;
1110 }
1111
1112 /* Create Directory */
1113 case 0x39:
1114 {
1115 String = (PCHAR)((ULONG_PTR)BaseAddress
1116 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1117
1118 if (CreateDirectoryA(String, NULL))
1119 {
1120 EmulatorClearFlag(EMULATOR_FLAG_CF);
1121 }
1122 else
1123 {
1124 EmulatorSetFlag(EMULATOR_FLAG_CF);
1125 EmulatorSetRegister(EMULATOR_REG_AX,
1126 (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
1127 }
1128
1129 break;
1130 }
1131
1132 /* Remove Directory */
1133 case 0x3A:
1134 {
1135 String = (PCHAR)((ULONG_PTR)BaseAddress
1136 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1137
1138 if (RemoveDirectoryA(String))
1139 {
1140 EmulatorClearFlag(EMULATOR_FLAG_CF);
1141 }
1142 else
1143 {
1144 EmulatorSetFlag(EMULATOR_FLAG_CF);
1145 EmulatorSetRegister(EMULATOR_REG_AX,
1146 (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
1147 }
1148
1149
1150 break;
1151 }
1152
1153 /* Set Current Directory */
1154 case 0x3B:
1155 {
1156 String = (PCHAR)((ULONG_PTR)BaseAddress
1157 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1158
1159 if (SetCurrentDirectoryA(String))
1160 {
1161 EmulatorClearFlag(EMULATOR_FLAG_CF);
1162 }
1163 else
1164 {
1165 EmulatorSetFlag(EMULATOR_FLAG_CF);
1166 EmulatorSetRegister(EMULATOR_REG_AX,
1167 (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
1168 }
1169
1170 break;
1171 }
1172
1173 /* Create File */
1174 case 0x3C:
1175 {
1176 WORD FileHandle;
1177 WORD ErrorCode = DosCreateFile(&FileHandle,
1178 (LPCSTR)(ULONG_PTR)BaseAddress
1179 + TO_LINEAR(DataSegment, LOWORD(Edx)),
1180 LOWORD(Ecx));
1181
1182 if (ErrorCode == 0)
1183 {
1184 /* Clear CF */
1185 EmulatorClearFlag(EMULATOR_FLAG_CF);
1186
1187 /* Return the handle in AX */
1188 EmulatorSetRegister(EMULATOR_REG_AX,
1189 (Eax & 0xFFFF0000) | FileHandle);
1190 }
1191 else
1192 {
1193 /* Set CF */
1194 EmulatorSetFlag(EMULATOR_FLAG_CF);
1195
1196 /* Return the error code in AX */
1197 EmulatorSetRegister(EMULATOR_REG_AX,
1198 (Eax & 0xFFFF0000) | ErrorCode);
1199 }
1200
1201 break;
1202 }
1203
1204 /* Open File */
1205 case 0x3D:
1206 {
1207 WORD FileHandle;
1208 WORD ErrorCode = DosCreateFile(&FileHandle,
1209 (LPCSTR)(ULONG_PTR)BaseAddress
1210 + TO_LINEAR(DataSegment, LOWORD(Edx)),
1211 LOBYTE(Eax));
1212
1213 if (ErrorCode == 0)
1214 {
1215 /* Clear CF */
1216 EmulatorClearFlag(EMULATOR_FLAG_CF);
1217
1218 /* Return the handle in AX */
1219 EmulatorSetRegister(EMULATOR_REG_AX,
1220 (Eax & 0xFFFF0000) | FileHandle);
1221 }
1222 else
1223 {
1224 /* Set CF */
1225 EmulatorSetFlag(EMULATOR_FLAG_CF);
1226
1227 /* Return the error code in AX */
1228 EmulatorSetRegister(EMULATOR_REG_AX,
1229 (Eax & 0xFFFF0000) | ErrorCode);
1230 }
1231
1232 break;
1233 }
1234
1235 /* Close File */
1236 case 0x3E:
1237 {
1238 if (DosCloseHandle(LOWORD(Ebx)))
1239 {
1240 /* Clear CF */
1241 EmulatorClearFlag(EMULATOR_FLAG_CF);
1242 }
1243 else
1244 {
1245 /* Set CF */
1246 EmulatorSetFlag(EMULATOR_FLAG_CF);
1247
1248 /* Return the error code in AX */
1249 EmulatorSetRegister(EMULATOR_REG_AX,
1250 (Eax & 0xFFFF0000) | ERROR_INVALID_PARAMETER);
1251 }
1252
1253 break;
1254 }
1255
1256 /* Read File */
1257 case 0x3F:
1258 {
1259 WORD BytesRead = 0;
1260 WORD ErrorCode = DosReadFile(LOWORD(Ebx),
1261 (LPVOID)((ULONG_PTR)BaseAddress
1262 + TO_LINEAR(DataSegment, LOWORD(Edx))),
1263 LOWORD(Ecx),
1264 &BytesRead);
1265
1266 if (ErrorCode == 0)
1267 {
1268 /* Clear CF */
1269 EmulatorClearFlag(EMULATOR_FLAG_CF);
1270
1271 /* Return the number of bytes read in AX */
1272 EmulatorSetRegister(EMULATOR_REG_AX,
1273 (Eax & 0xFFFF0000) | BytesRead);
1274 }
1275 else
1276 {
1277 /* Set CF */
1278 EmulatorSetFlag(EMULATOR_FLAG_CF);
1279
1280 /* Return the error code in AX */
1281 EmulatorSetRegister(EMULATOR_REG_AX,
1282 (Eax & 0xFFFF0000) | ErrorCode);
1283 }
1284 break;
1285 }
1286
1287 /* Write File */
1288 case 0x40:
1289 {
1290 WORD BytesWritten = 0;
1291 WORD ErrorCode = DosWriteFile(LOWORD(Ebx),
1292 (LPVOID)((ULONG_PTR)BaseAddress
1293 + TO_LINEAR(DataSegment, LOWORD(Edx))),
1294 LOWORD(Ecx),
1295 &BytesWritten);
1296
1297 if (ErrorCode == 0)
1298 {
1299 /* Clear CF */
1300 EmulatorClearFlag(EMULATOR_FLAG_CF);
1301
1302 /* Return the number of bytes written in AX */
1303 EmulatorSetRegister(EMULATOR_REG_AX,
1304 (Eax & 0xFFFF0000) | BytesWritten);
1305 }
1306 else
1307 {
1308 /* Set CF */
1309 EmulatorSetFlag(EMULATOR_FLAG_CF);
1310
1311 /* Return the error code in AX */
1312 EmulatorSetRegister(EMULATOR_REG_AX,
1313 (Eax & 0xFFFF0000) | ErrorCode);
1314 }
1315
1316 break;
1317 }
1318
1319 /* Allocate Memory */
1320 case 0x48:
1321 {
1322 WORD MaxAvailable = 0;
1323 WORD Segment = DosAllocateMemory(LOWORD(Ebx), &MaxAvailable);
1324
1325 if (Segment != 0)
1326 {
1327 EmulatorSetRegister(EMULATOR_REG_AX, Segment);
1328 EmulatorSetRegister(EMULATOR_REG_BX, MaxAvailable);
1329 EmulatorClearFlag(EMULATOR_FLAG_CF);
1330 }
1331 else EmulatorSetFlag(EMULATOR_FLAG_CF);
1332
1333 break;
1334 }
1335
1336 /* Free Memory */
1337 case 0x49:
1338 {
1339 if (DosFreeMemory(ExtSegment))
1340 {
1341 EmulatorClearFlag(EMULATOR_FLAG_CF);
1342 }
1343 else EmulatorSetFlag(EMULATOR_FLAG_CF);
1344
1345 break;
1346 }
1347
1348 /* Resize Memory Block */
1349 case 0x4A:
1350 {
1351 WORD Size;
1352
1353 if (DosResizeMemory(ExtSegment, LOWORD(Ebx), &Size))
1354 {
1355 EmulatorClearFlag(EMULATOR_FLAG_CF);
1356 }
1357 else
1358 {
1359 EmulatorSetFlag(EMULATOR_FLAG_CF);
1360 EmulatorSetRegister(EMULATOR_REG_BX, Size);
1361 }
1362
1363 break;
1364 }
1365
1366 /* Terminate With Return Code */
1367 case 0x4C:
1368 {
1369 DosTerminateProcess(CurrentPsp, LOBYTE(Eax));
1370 break;
1371 }
1372
1373 /* Unsupported */
1374 default:
1375 {
1376 DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax));
1377 EmulatorSetFlag(EMULATOR_FLAG_CF);
1378 }
1379 }
1380 }
1381
1382 VOID DosBreakInterrupt()
1383 {
1384 VdmRunning = FALSE;
1385 }
1386
1387 BOOLEAN DosInitialize()
1388 {
1389 BYTE i;
1390 PDOS_MCB Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT);
1391 FILE *Stream;
1392 WCHAR Buffer[256];
1393 LPWSTR SourcePtr, Environment;
1394 LPSTR AsciiString;
1395 LPSTR DestPtr = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(SYSTEM_ENV_BLOCK, 0));
1396 DWORD AsciiSize;
1397
1398 /* Initialize the MCB */
1399 Mcb->BlockType = 'Z';
1400 Mcb->Size = USER_MEMORY_SIZE;
1401 Mcb->OwnerPsp = 0;
1402
1403 /* Get the environment strings */
1404 SourcePtr = Environment = GetEnvironmentStringsW();
1405 if (Environment == NULL) return FALSE;
1406
1407 /* Fill the DOS system environment block */
1408 while (*SourcePtr)
1409 {
1410 /* Get the size of the ASCII string */
1411 AsciiSize = WideCharToMultiByte(CP_ACP,
1412 0,
1413 SourcePtr,
1414 -1,
1415 NULL,
1416 0,
1417 NULL,
1418 NULL);
1419
1420 /* Allocate memory for the ASCII string */
1421 AsciiString = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, AsciiSize);
1422 if (AsciiString == NULL)
1423 {
1424 FreeEnvironmentStringsW(Environment);
1425 return FALSE;
1426 }
1427
1428 /* Convert to ASCII */
1429 WideCharToMultiByte(CP_ACP,
1430 0,
1431 SourcePtr,
1432 -1,
1433 AsciiString,
1434 AsciiSize,
1435 NULL,
1436 NULL);
1437
1438 /* Copy the string into DOS memory */
1439 strcpy(DestPtr, AsciiString);
1440
1441 /* Free the memory */
1442 HeapFree(GetProcessHeap(), 0, AsciiString);
1443
1444 /* Move to the next string */
1445 SourcePtr += wcslen(SourcePtr) + 1;
1446 DestPtr += strlen(AsciiString);
1447 *(DestPtr++) = 0;
1448 }
1449 *DestPtr = 0;
1450
1451 /* Free the memory allocated for environment strings */
1452 FreeEnvironmentStringsW(Environment);
1453
1454 /* Read CONFIG.SYS */
1455 Stream = _wfopen(DOS_CONFIG_PATH, L"r");
1456 if (Stream != NULL)
1457 {
1458 while (fgetws(Buffer, 256, Stream))
1459 {
1460 // TODO: Parse the line
1461 }
1462 fclose(Stream);
1463 }
1464
1465 /* Initialize the SFT */
1466 for (i = 0; i < DOS_SFT_SIZE; i++)
1467 {
1468 DosSystemFileTable[i] = INVALID_HANDLE_VALUE;
1469 DosSftRefCount[i] = 0;
1470 }
1471
1472 /* Get handles to standard I/O devices */
1473 DosSystemFileTable[0] = GetStdHandle(STD_INPUT_HANDLE);
1474 DosSystemFileTable[1] = GetStdHandle(STD_OUTPUT_HANDLE);
1475 DosSystemFileTable[2] = GetStdHandle(STD_ERROR_HANDLE);
1476
1477 return TRUE;
1478 }
1479
1480 /* EOF */