[NTVDM]
[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 0x08:
947 {
948 EmulatorSetRegister(EMULATOR_REG_AX,
949 (Eax & 0xFFFFFF00) | DosReadCharacter());
950 break;
951 }
952
953 /* Print String */
954 case 0x09:
955 {
956 String = (PCHAR)((ULONG_PTR)BaseAddress
957 + TO_LINEAR(DataSegment, LOWORD(Edx)));
958
959 while ((*String) != '$')
960 {
961 DosPrintCharacter(*String);
962 String++;
963 }
964
965 break;
966 }
967
968 /* Read Buffered Input */
969 case 0x0A:
970 {
971 InputBuffer = (PDOS_INPUT_BUFFER)((ULONG_PTR)BaseAddress
972 + TO_LINEAR(DataSegment,
973 LOWORD(Edx)));
974
975 InputBuffer->Length = 0;
976 for (i = 0; i < InputBuffer->MaxLength; i ++)
977 {
978 Character = DosReadCharacter();
979 DosPrintCharacter(Character);
980 InputBuffer->Buffer[InputBuffer->Length] = Character;
981 if (Character == '\r') break;
982 InputBuffer->Length++;
983 }
984
985 break;
986 }
987
988 /* Set Disk Transfer Area */
989 case 0x1A:
990 {
991 DiskTransferArea = MAKELONG(LOWORD(Edx), DataSegment);
992 break;
993 }
994
995 /* Set Interrupt Vector */
996 case 0x25:
997 {
998 DWORD FarPointer = MAKELONG(LOWORD(Edx), DataSegment);
999
1000 /* Write the new far pointer to the IDT */
1001 ((PDWORD)BaseAddress)[LOBYTE(Eax)] = FarPointer;
1002
1003 break;
1004 }
1005
1006 /* Get system date */
1007 case 0x2A:
1008 {
1009 GetLocalTime(&SystemTime);
1010 EmulatorSetRegister(EMULATOR_REG_CX,
1011 (Ecx & 0xFFFF0000) | SystemTime.wYear);
1012 EmulatorSetRegister(EMULATOR_REG_DX,
1013 (Edx & 0xFFFF0000)
1014 | (SystemTime.wMonth << 8)
1015 | SystemTime.wDay);
1016 EmulatorSetRegister(EMULATOR_REG_AX,
1017 (Eax & 0xFFFFFF00) | SystemTime.wDayOfWeek);
1018 break;
1019 }
1020
1021 /* Set system date */
1022 case 0x2B:
1023 {
1024 GetLocalTime(&SystemTime);
1025 SystemTime.wYear = LOWORD(Ecx);
1026 SystemTime.wMonth = HIBYTE(Edx);
1027 SystemTime.wDay = LOBYTE(Edx);
1028
1029 if (SetLocalTime(&SystemTime))
1030 {
1031 /* Return success */
1032 EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
1033 }
1034 else
1035 {
1036 /* Return failure */
1037 EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
1038 }
1039
1040 break;
1041 }
1042
1043 /* Get system time */
1044 case 0x2C:
1045 {
1046 GetLocalTime(&SystemTime);
1047 EmulatorSetRegister(EMULATOR_REG_CX,
1048 (Ecx & 0xFFFF0000)
1049 | (SystemTime.wHour << 8)
1050 | SystemTime.wMinute);
1051 EmulatorSetRegister(EMULATOR_REG_DX,
1052 (Edx & 0xFFFF0000)
1053 | (SystemTime.wSecond << 8)
1054 | (SystemTime.wMilliseconds / 10));
1055 break;
1056 }
1057
1058 /* Set system time */
1059 case 0x2D:
1060 {
1061 GetLocalTime(&SystemTime);
1062 SystemTime.wHour = HIBYTE(Ecx);
1063 SystemTime.wMinute = LOBYTE(Ecx);
1064 SystemTime.wSecond = HIBYTE(Edx);
1065 SystemTime.wMilliseconds = LOBYTE(Edx) * 10;
1066
1067 if (SetLocalTime(&SystemTime))
1068 {
1069 /* Return success */
1070 EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
1071 }
1072 else
1073 {
1074 /* Return failure */
1075 EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
1076 }
1077
1078 break;
1079 }
1080
1081 /* Get Disk Transfer Area */
1082 case 0x2F:
1083 {
1084 EmulatorSetRegister(EMULATOR_REG_ES, HIWORD(DiskTransferArea));
1085 EmulatorSetRegister(EMULATOR_REG_BX, LOWORD(DiskTransferArea));
1086
1087 break;
1088 }
1089
1090 /* Get DOS Version */
1091 case 0x30:
1092 {
1093 PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
1094
1095 EmulatorSetRegister(EMULATOR_REG_AX, PspBlock->DosVersion);
1096 break;
1097 }
1098
1099 /* Get Interrupt Vector */
1100 case 0x35:
1101 {
1102 DWORD FarPointer = ((PDWORD)BaseAddress)[LOBYTE(Eax)];
1103
1104 /* Read the address from the IDT into ES:BX */
1105 EmulatorSetRegister(EMULATOR_REG_ES, HIWORD(FarPointer));
1106 EmulatorSetRegister(EMULATOR_REG_BX, LOWORD(FarPointer));
1107
1108 break;
1109 }
1110
1111 /* Create Directory */
1112 case 0x39:
1113 {
1114 String = (PCHAR)((ULONG_PTR)BaseAddress
1115 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1116
1117 if (CreateDirectoryA(String, NULL))
1118 {
1119 EmulatorClearFlag(EMULATOR_FLAG_CF);
1120 }
1121 else
1122 {
1123 EmulatorSetFlag(EMULATOR_FLAG_CF);
1124 EmulatorSetRegister(EMULATOR_REG_AX,
1125 (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
1126 }
1127
1128 break;
1129 }
1130
1131 /* Remove Directory */
1132 case 0x3A:
1133 {
1134 String = (PCHAR)((ULONG_PTR)BaseAddress
1135 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1136
1137 if (RemoveDirectoryA(String))
1138 {
1139 EmulatorClearFlag(EMULATOR_FLAG_CF);
1140 }
1141 else
1142 {
1143 EmulatorSetFlag(EMULATOR_FLAG_CF);
1144 EmulatorSetRegister(EMULATOR_REG_AX,
1145 (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
1146 }
1147
1148
1149 break;
1150 }
1151
1152 /* Set Current Directory */
1153 case 0x3B:
1154 {
1155 String = (PCHAR)((ULONG_PTR)BaseAddress
1156 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1157
1158 if (SetCurrentDirectoryA(String))
1159 {
1160 EmulatorClearFlag(EMULATOR_FLAG_CF);
1161 }
1162 else
1163 {
1164 EmulatorSetFlag(EMULATOR_FLAG_CF);
1165 EmulatorSetRegister(EMULATOR_REG_AX,
1166 (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
1167 }
1168
1169 break;
1170 }
1171
1172 /* Create File */
1173 case 0x3C:
1174 {
1175 WORD FileHandle;
1176 WORD ErrorCode = DosCreateFile(&FileHandle,
1177 (LPCSTR)(ULONG_PTR)BaseAddress
1178 + TO_LINEAR(DataSegment, LOWORD(Edx)),
1179 LOWORD(Ecx));
1180
1181 if (ErrorCode == 0)
1182 {
1183 /* Clear CF */
1184 EmulatorClearFlag(EMULATOR_FLAG_CF);
1185
1186 /* Return the handle in AX */
1187 EmulatorSetRegister(EMULATOR_REG_AX,
1188 (Eax & 0xFFFF0000) | FileHandle);
1189 }
1190 else
1191 {
1192 /* Set CF */
1193 EmulatorSetFlag(EMULATOR_FLAG_CF);
1194
1195 /* Return the error code in AX */
1196 EmulatorSetRegister(EMULATOR_REG_AX,
1197 (Eax & 0xFFFF0000) | ErrorCode);
1198 }
1199
1200 break;
1201 }
1202
1203 /* Open File */
1204 case 0x3D:
1205 {
1206 WORD FileHandle;
1207 WORD ErrorCode = DosCreateFile(&FileHandle,
1208 (LPCSTR)(ULONG_PTR)BaseAddress
1209 + TO_LINEAR(DataSegment, LOWORD(Edx)),
1210 LOBYTE(Eax));
1211
1212 if (ErrorCode == 0)
1213 {
1214 /* Clear CF */
1215 EmulatorClearFlag(EMULATOR_FLAG_CF);
1216
1217 /* Return the handle in AX */
1218 EmulatorSetRegister(EMULATOR_REG_AX,
1219 (Eax & 0xFFFF0000) | FileHandle);
1220 }
1221 else
1222 {
1223 /* Set CF */
1224 EmulatorSetFlag(EMULATOR_FLAG_CF);
1225
1226 /* Return the error code in AX */
1227 EmulatorSetRegister(EMULATOR_REG_AX,
1228 (Eax & 0xFFFF0000) | ErrorCode);
1229 }
1230
1231 break;
1232 }
1233
1234 /* Close File */
1235 case 0x3E:
1236 {
1237 if (DosCloseHandle(LOWORD(Ebx)))
1238 {
1239 /* Clear CF */
1240 EmulatorClearFlag(EMULATOR_FLAG_CF);
1241 }
1242 else
1243 {
1244 /* Set CF */
1245 EmulatorSetFlag(EMULATOR_FLAG_CF);
1246
1247 /* Return the error code in AX */
1248 EmulatorSetRegister(EMULATOR_REG_AX,
1249 (Eax & 0xFFFF0000) | ERROR_INVALID_PARAMETER);
1250 }
1251
1252 break;
1253 }
1254
1255 /* Read File */
1256 case 0x3F:
1257 {
1258 WORD BytesRead = 0;
1259 WORD ErrorCode = DosReadFile(LOWORD(Ebx),
1260 (LPVOID)((ULONG_PTR)BaseAddress
1261 + TO_LINEAR(DataSegment, LOWORD(Edx))),
1262 LOWORD(Ecx),
1263 &BytesRead);
1264
1265 if (ErrorCode == 0)
1266 {
1267 /* Clear CF */
1268 EmulatorClearFlag(EMULATOR_FLAG_CF);
1269
1270 /* Return the number of bytes read in AX */
1271 EmulatorSetRegister(EMULATOR_REG_AX,
1272 (Eax & 0xFFFF0000) | BytesRead);
1273 }
1274 else
1275 {
1276 /* Set CF */
1277 EmulatorSetFlag(EMULATOR_FLAG_CF);
1278
1279 /* Return the error code in AX */
1280 EmulatorSetRegister(EMULATOR_REG_AX,
1281 (Eax & 0xFFFF0000) | ErrorCode);
1282 }
1283 break;
1284 }
1285
1286 /* Write File */
1287 case 0x40:
1288 {
1289 WORD BytesWritten = 0;
1290 WORD ErrorCode = DosWriteFile(LOWORD(Ebx),
1291 (LPVOID)((ULONG_PTR)BaseAddress
1292 + TO_LINEAR(DataSegment, LOWORD(Edx))),
1293 LOWORD(Ecx),
1294 &BytesWritten);
1295
1296 if (ErrorCode == 0)
1297 {
1298 /* Clear CF */
1299 EmulatorClearFlag(EMULATOR_FLAG_CF);
1300
1301 /* Return the number of bytes written in AX */
1302 EmulatorSetRegister(EMULATOR_REG_AX,
1303 (Eax & 0xFFFF0000) | BytesWritten);
1304 }
1305 else
1306 {
1307 /* Set CF */
1308 EmulatorSetFlag(EMULATOR_FLAG_CF);
1309
1310 /* Return the error code in AX */
1311 EmulatorSetRegister(EMULATOR_REG_AX,
1312 (Eax & 0xFFFF0000) | ErrorCode);
1313 }
1314
1315 break;
1316 }
1317
1318 /* Allocate Memory */
1319 case 0x48:
1320 {
1321 WORD MaxAvailable = 0;
1322 WORD Segment = DosAllocateMemory(LOWORD(Ebx), &MaxAvailable);
1323
1324 if (Segment != 0)
1325 {
1326 EmulatorSetRegister(EMULATOR_REG_AX, Segment);
1327 EmulatorSetRegister(EMULATOR_REG_BX, MaxAvailable);
1328 EmulatorClearFlag(EMULATOR_FLAG_CF);
1329 }
1330 else EmulatorSetFlag(EMULATOR_FLAG_CF);
1331
1332 break;
1333 }
1334
1335 /* Free Memory */
1336 case 0x49:
1337 {
1338 if (DosFreeMemory(ExtSegment))
1339 {
1340 EmulatorClearFlag(EMULATOR_FLAG_CF);
1341 }
1342 else EmulatorSetFlag(EMULATOR_FLAG_CF);
1343
1344 break;
1345 }
1346
1347 /* Resize Memory Block */
1348 case 0x4A:
1349 {
1350 WORD Size;
1351
1352 if (DosResizeMemory(ExtSegment, LOWORD(Ebx), &Size))
1353 {
1354 EmulatorClearFlag(EMULATOR_FLAG_CF);
1355 }
1356 else
1357 {
1358 EmulatorSetFlag(EMULATOR_FLAG_CF);
1359 EmulatorSetRegister(EMULATOR_REG_BX, Size);
1360 }
1361
1362 break;
1363 }
1364
1365 /* Terminate With Return Code */
1366 case 0x4C:
1367 {
1368 DosTerminateProcess(CurrentPsp, LOBYTE(Eax));
1369 break;
1370 }
1371
1372 /* Unsupported */
1373 default:
1374 {
1375 DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax));
1376 EmulatorSetFlag(EMULATOR_FLAG_CF);
1377 }
1378 }
1379 }
1380
1381 VOID DosBreakInterrupt()
1382 {
1383 VdmRunning = FALSE;
1384 }
1385
1386 BOOLEAN DosInitialize()
1387 {
1388 BYTE i;
1389 PDOS_MCB Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT);
1390 FILE *Stream;
1391 WCHAR Buffer[256];
1392 LPWSTR SourcePtr, Environment;
1393 LPSTR AsciiString;
1394 LPSTR DestPtr = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(SYSTEM_ENV_BLOCK, 0));
1395 DWORD AsciiSize;
1396
1397 /* Initialize the MCB */
1398 Mcb->BlockType = 'Z';
1399 Mcb->Size = USER_MEMORY_SIZE;
1400 Mcb->OwnerPsp = 0;
1401
1402 /* Get the environment strings */
1403 SourcePtr = Environment = GetEnvironmentStringsW();
1404 if (Environment == NULL) return FALSE;
1405
1406 /* Fill the DOS system environment block */
1407 while (*SourcePtr)
1408 {
1409 /* Get the size of the ASCII string */
1410 AsciiSize = WideCharToMultiByte(CP_ACP,
1411 0,
1412 SourcePtr,
1413 -1,
1414 NULL,
1415 0,
1416 NULL,
1417 NULL);
1418
1419 /* Allocate memory for the ASCII string */
1420 AsciiString = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, AsciiSize);
1421 if (AsciiString == NULL)
1422 {
1423 FreeEnvironmentStringsW(Environment);
1424 return FALSE;
1425 }
1426
1427 /* Convert to ASCII */
1428 WideCharToMultiByte(CP_ACP,
1429 0,
1430 SourcePtr,
1431 -1,
1432 AsciiString,
1433 AsciiSize,
1434 NULL,
1435 NULL);
1436
1437 /* Copy the string into DOS memory */
1438 strcpy(DestPtr, AsciiString);
1439
1440 /* Free the memory */
1441 HeapFree(GetProcessHeap(), 0, AsciiString);
1442
1443 /* Move to the next string */
1444 SourcePtr += wcslen(SourcePtr) + 1;
1445 DestPtr += strlen(AsciiString);
1446 *(DestPtr++) = 0;
1447 }
1448 *DestPtr = 0;
1449
1450 /* Free the memory allocated for environment strings */
1451 FreeEnvironmentStringsW(Environment);
1452
1453 /* Read CONFIG.SYS */
1454 Stream = _wfopen(DOS_CONFIG_PATH, L"r");
1455 if (Stream != NULL)
1456 {
1457 while (fgetws(Buffer, 256, Stream))
1458 {
1459 // TODO: Parse the line
1460 }
1461 fclose(Stream);
1462 }
1463
1464 /* Initialize the SFT */
1465 for (i = 0; i < DOS_SFT_SIZE; i++)
1466 {
1467 DosSystemFileTable[i] = INVALID_HANDLE_VALUE;
1468 DosSftRefCount[i] = 0;
1469 }
1470
1471 /* Get handles to standard I/O devices */
1472 DosSystemFileTable[0] = GetStdHandle(STD_INPUT_HANDLE);
1473 DosSystemFileTable[1] = GetStdHandle(STD_OUTPUT_HANDLE);
1474 DosSystemFileTable[2] = GetStdHandle(STD_ERROR_HANDLE);
1475
1476 return TRUE;
1477 }
1478
1479 /* EOF */