[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 #define NDEBUG
12
13 #include "dos.h"
14 #include "bios.h"
15 #include "emulator.h"
16
17 /* PRIVATE VARIABLES **********************************************************/
18
19 static WORD CurrentPsp = SYSTEM_PSP;
20 static WORD DosLastError = 0;
21 static DWORD DiskTransferArea;
22 static BYTE CurrentDrive;
23 static CHAR LastDrive = 'E';
24 static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH];
25 static HANDLE DosSystemFileTable[DOS_SFT_SIZE];
26 static WORD DosSftRefCount[DOS_SFT_SIZE];
27 static BYTE DosAllocStrategy = DOS_ALLOC_BEST_FIT;
28 static BOOLEAN DosUmbLinked = FALSE;
29
30 /* PRIVATE FUNCTIONS **********************************************************/
31
32 static VOID DosCombineFreeBlocks(WORD StartBlock)
33 {
34 PDOS_MCB CurrentMcb = SEGMENT_TO_MCB(StartBlock), NextMcb;
35
36 /* If this is the last block or it's not free, quit */
37 if (CurrentMcb->BlockType == 'Z' || CurrentMcb->OwnerPsp != 0) return;
38
39 while (TRUE)
40 {
41 /* Get a pointer to the next MCB */
42 NextMcb = SEGMENT_TO_MCB(StartBlock + CurrentMcb->Size + 1);
43
44 /* Check if the next MCB is free */
45 if (NextMcb->OwnerPsp == 0)
46 {
47 /* Combine them */
48 CurrentMcb->Size += NextMcb->Size + 1;
49 CurrentMcb->BlockType = NextMcb->BlockType;
50 NextMcb->BlockType = 'I';
51 }
52 else
53 {
54 /* No more adjoining free blocks */
55 break;
56 }
57 }
58 }
59
60 static WORD DosCopyEnvironmentBlock(WORD SourceSegment, LPCSTR ProgramName)
61 {
62 PCHAR Ptr, SourceBuffer, DestBuffer = NULL;
63 ULONG TotalSize = 0;
64 WORD DestSegment;
65
66 Ptr = SourceBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(SourceSegment, 0));
67
68 /* Calculate the size of the environment block */
69 while (*Ptr)
70 {
71 TotalSize += strlen(Ptr) + 1;
72 Ptr += strlen(Ptr) + 1;
73 }
74 TotalSize++;
75
76 /* Add the string buffer size */
77 TotalSize += strlen(ProgramName) + 1;
78
79 /* Allocate the memory for the environment block */
80 DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
81 if (!DestSegment) return 0;
82
83 Ptr = SourceBuffer;
84
85 DestBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(DestSegment, 0));
86 while (*Ptr)
87 {
88 /* Copy the string */
89 strcpy(DestBuffer, Ptr);
90
91 /* Advance to the next string */
92 DestBuffer += strlen(Ptr);
93 Ptr += strlen(Ptr) + 1;
94
95 /* Put a zero after the string */
96 *(DestBuffer++) = 0;
97 }
98
99 /* Set the final zero */
100 *(DestBuffer++) = 0;
101
102 /* Copy the program name after the environment block */
103 strcpy(DestBuffer, ProgramName);
104
105 return DestSegment;
106 }
107
108 static VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner)
109 {
110 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1);
111
112 /* Just set the owner */
113 Mcb->OwnerPsp = NewOwner;
114 }
115
116 static WORD DosOpenHandle(HANDLE Handle)
117 {
118 BYTE i;
119 WORD DosHandle;
120 PDOS_PSP PspBlock;
121 LPBYTE HandleTable;
122
123 /* The system PSP has no handle table */
124 if (CurrentPsp == SYSTEM_PSP) return INVALID_DOS_HANDLE;
125
126 /* Get a pointer to the handle table */
127 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
128 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
129
130 /* Find a free entry in the JFT */
131 for (DosHandle = 0; DosHandle < PspBlock->HandleTableSize; DosHandle++)
132 {
133 if (HandleTable[DosHandle] == 0xFF) break;
134 }
135
136 /* If there are no free entries, fail */
137 if (DosHandle == PspBlock->HandleTableSize) return INVALID_DOS_HANDLE;
138
139 /* Check if the handle is already in the SFT */
140 for (i = 0; i < DOS_SFT_SIZE; i++)
141 {
142 /* Check if this is the same handle */
143 if (DosSystemFileTable[i] != Handle) continue;
144
145 /* Already in the table, reference it */
146 DosSftRefCount[i]++;
147
148 /* Set the JFT entry to that SFT index */
149 HandleTable[DosHandle] = i;
150
151 /* Return the new handle */
152 return DosHandle;
153 }
154
155 /* Add the handle to the SFT */
156 for (i = 0; i < DOS_SFT_SIZE; i++)
157 {
158 /* Make sure this is an empty table entry */
159 if (DosSystemFileTable[i] != INVALID_HANDLE_VALUE) continue;
160
161 /* Initialize the empty table entry */
162 DosSystemFileTable[i] = Handle;
163 DosSftRefCount[i] = 1;
164
165 /* Set the JFT entry to that SFT index */
166 HandleTable[DosHandle] = i;
167
168 /* Return the new handle */
169 return DosHandle;
170 }
171
172 /* The SFT is full */
173 return INVALID_DOS_HANDLE;
174 }
175
176 static HANDLE DosGetRealHandle(WORD DosHandle)
177 {
178 PDOS_PSP PspBlock;
179 LPBYTE HandleTable;
180
181 /* The system PSP has no handle table */
182 if (CurrentPsp == SYSTEM_PSP) return INVALID_HANDLE_VALUE;
183
184 /* Get a pointer to the handle table */
185 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
186 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
187
188 /* Make sure the handle is open */
189 if (HandleTable[DosHandle] == 0xFF) return INVALID_HANDLE_VALUE;
190
191 /* Return the Win32 handle */
192 return DosSystemFileTable[HandleTable[DosHandle]];
193 }
194
195 static VOID DosCopyHandleTable(LPBYTE DestinationTable)
196 {
197 INT i;
198 PDOS_PSP PspBlock;
199 LPBYTE SourceTable;
200
201 /* Clear the table first */
202 for (i = 0; i < 20; i++) DestinationTable[i] = 0xFF;
203
204 /* Check if this is the initial process */
205 if (CurrentPsp == SYSTEM_PSP)
206 {
207 /* Set up the standard I/O devices */
208 for (i = 0; i <= 2; i++)
209 {
210 /* Set the index in the SFT */
211 DestinationTable[i] = (BYTE)i;
212
213 /* Increase the reference count */
214 DosSftRefCount[i]++;
215 }
216
217 /* Done */
218 return;
219 }
220
221 /* Get the parent PSP block and handle table */
222 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
223 SourceTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
224
225 /* Copy the first 20 handles into the new table */
226 for (i = 0; i < 20; i++)
227 {
228 DestinationTable[i] = SourceTable[i];
229
230 /* Increase the reference count */
231 DosSftRefCount[SourceTable[i]]++;
232 }
233 }
234
235 /* PUBLIC FUNCTIONS ***********************************************************/
236
237 WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable)
238 {
239 WORD Result = 0, Segment = FIRST_MCB_SEGMENT, MaxSize = 0;
240 PDOS_MCB CurrentMcb, NextMcb;
241 BOOLEAN SearchUmb = FALSE;
242
243 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size);
244
245 if (DosUmbLinked && (DosAllocStrategy & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW)))
246 {
247 /* Search UMB first */
248 Segment = UMB_START_SEGMENT;
249 SearchUmb = TRUE;
250 }
251
252 while (TRUE)
253 {
254 /* Get a pointer to the MCB */
255 CurrentMcb = SEGMENT_TO_MCB(Segment);
256
257 /* Make sure it's valid */
258 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z')
259 {
260 DPRINT("The DOS memory arena is corrupted!\n");
261 DosLastError = ERROR_ARENA_TRASHED;
262 return 0;
263 }
264
265 /* Only check free blocks */
266 if (CurrentMcb->OwnerPsp != 0) goto Next;
267
268 /* Combine this free block with adjoining free blocks */
269 DosCombineFreeBlocks(Segment);
270
271 /* Update the maximum block size */
272 if (CurrentMcb->Size > MaxSize) MaxSize = CurrentMcb->Size;
273
274 /* Check if this block is big enough */
275 if (CurrentMcb->Size < Size) goto Next;
276
277 switch (DosAllocStrategy & 0x3F)
278 {
279 case DOS_ALLOC_FIRST_FIT:
280 {
281 /* For first fit, stop immediately */
282 Result = Segment;
283 goto Done;
284 }
285
286 case DOS_ALLOC_BEST_FIT:
287 {
288 /* For best fit, update the smallest block found so far */
289 if ((Result == 0) || (CurrentMcb->Size < SEGMENT_TO_MCB(Result)->Size))
290 {
291 Result = Segment;
292 }
293
294 break;
295 }
296
297 case DOS_ALLOC_LAST_FIT:
298 {
299 /* For last fit, make the current block the result, but keep searching */
300 Result = Segment;
301 break;
302 }
303 }
304
305 Next:
306 /* If this was the last MCB in the chain, quit */
307 if (CurrentMcb->BlockType == 'Z')
308 {
309 /* Check if nothing was found while searching through UMBs */
310 if ((Result == 0) && SearchUmb && (DosAllocStrategy & DOS_ALLOC_HIGH_LOW))
311 {
312 /* Search low memory */
313 Segment = FIRST_MCB_SEGMENT;
314 continue;
315 }
316
317 break;
318 }
319
320 /* Otherwise, update the segment and continue */
321 Segment += CurrentMcb->Size + 1;
322 }
323
324 Done:
325
326 /* If we didn't find a free block, return 0 */
327 if (Result == 0)
328 {
329 DosLastError = ERROR_NOT_ENOUGH_MEMORY;
330 if (MaxAvailable) *MaxAvailable = MaxSize;
331 return 0;
332 }
333
334 /* Get a pointer to the MCB */
335 CurrentMcb = SEGMENT_TO_MCB(Result);
336
337 /* Check if the block is larger than requested */
338 if (CurrentMcb->Size > Size)
339 {
340 /* It is, split it into two blocks */
341 NextMcb = SEGMENT_TO_MCB(Result + Size + 1);
342
343 /* Initialize the new MCB structure */
344 NextMcb->BlockType = CurrentMcb->BlockType;
345 NextMcb->Size = CurrentMcb->Size - Size - 1;
346 NextMcb->OwnerPsp = 0;
347
348 /* Update the current block */
349 CurrentMcb->BlockType = 'M';
350 CurrentMcb->Size = Size;
351 }
352
353 /* Take ownership of the block */
354 CurrentMcb->OwnerPsp = CurrentPsp;
355
356 /* Return the segment of the data portion of the block */
357 return Result + 1;
358 }
359
360 BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable)
361 {
362 BOOLEAN Success = TRUE;
363 WORD Segment = BlockData - 1, ReturnSize = 0, NextSegment;
364 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment), NextMcb;
365
366 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
367 BlockData,
368 NewSize);
369
370 /* Make sure this is a valid, allocated block */
371 if ((Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') || Mcb->OwnerPsp == 0)
372 {
373 Success = FALSE;
374 DosLastError = ERROR_INVALID_HANDLE;
375 goto Done;
376 }
377
378 ReturnSize = Mcb->Size;
379
380 /* Check if we need to expand or contract the block */
381 if (NewSize > Mcb->Size)
382 {
383 /* We can't expand the last block */
384 if (Mcb->BlockType != 'M')
385 {
386 Success = FALSE;
387 goto Done;
388 }
389
390 /* Get the pointer and segment of the next MCB */
391 NextSegment = Segment + Mcb->Size + 1;
392 NextMcb = SEGMENT_TO_MCB(NextSegment);
393
394 /* Make sure the next segment is free */
395 if (NextMcb->OwnerPsp != 0)
396 {
397 DPRINT("Cannot expand memory block: next segment is not free!\n");
398 DosLastError = ERROR_NOT_ENOUGH_MEMORY;
399 Success = FALSE;
400 goto Done;
401 }
402
403 /* Combine this free block with adjoining free blocks */
404 DosCombineFreeBlocks(NextSegment);
405
406 /* Set the maximum possible size of the block */
407 ReturnSize += NextMcb->Size + 1;
408
409 /* Maximize the current block */
410 Mcb->Size = ReturnSize;
411 Mcb->BlockType = NextMcb->BlockType;
412
413 /* Invalidate the next block */
414 NextMcb->BlockType = 'I';
415
416 /* Check if the block is larger than requested */
417 if (Mcb->Size > NewSize)
418 {
419 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
420 Mcb->Size,
421 NewSize);
422
423 /* It is, split it into two blocks */
424 NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
425
426 /* Initialize the new MCB structure */
427 NextMcb->BlockType = Mcb->BlockType;
428 NextMcb->Size = Mcb->Size - NewSize - 1;
429 NextMcb->OwnerPsp = 0;
430
431 /* Update the current block */
432 Mcb->BlockType = 'M';
433 Mcb->Size = NewSize;
434 }
435 }
436 else if (NewSize < Mcb->Size)
437 {
438 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
439 Mcb->Size,
440 NewSize);
441
442 /* Just split the block */
443 NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
444 NextMcb->BlockType = Mcb->BlockType;
445 NextMcb->Size = Mcb->Size - NewSize - 1;
446 NextMcb->OwnerPsp = 0;
447
448 /* Update the MCB */
449 Mcb->BlockType = 'M';
450 Mcb->Size = NewSize;
451 }
452
453 Done:
454 /* Check if the operation failed */
455 if (!Success)
456 {
457 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
458 ReturnSize);
459
460 /* Return the maximum possible size */
461 if (MaxAvailable) *MaxAvailable = ReturnSize;
462 }
463
464 return Success;
465 }
466
467 BOOLEAN DosFreeMemory(WORD BlockData)
468 {
469 PDOS_MCB Mcb = SEGMENT_TO_MCB(BlockData - 1);
470
471 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData);
472
473 /* Make sure the MCB is valid */
474 if (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z')
475 {
476 DPRINT("MCB block type '%c' not valid!\n", Mcb->BlockType);
477 return FALSE;
478 }
479
480 /* Mark the block as free */
481 Mcb->OwnerPsp = 0;
482
483 return TRUE;
484 }
485
486 BOOLEAN DosLinkUmb(VOID)
487 {
488 DWORD Segment = FIRST_MCB_SEGMENT;
489 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment);
490
491 DPRINT("Linking UMB\n");
492
493 /* Check if UMBs are already linked */
494 if (DosUmbLinked) return FALSE;
495
496 /* Find the last block */
497 while ((Mcb->BlockType == 'M') && (Segment <= 0xFFFF))
498 {
499 Segment += Mcb->Size + 1;
500 Mcb = SEGMENT_TO_MCB(Segment);
501 }
502
503 /* Make sure it's valid */
504 if (Mcb->BlockType != 'Z') return FALSE;
505
506 /* Connect the MCB with the UMB chain */
507 Mcb->BlockType = 'M';
508
509 DosUmbLinked = TRUE;
510 return TRUE;
511 }
512
513 BOOLEAN DosUnlinkUmb(VOID)
514 {
515 DWORD Segment = FIRST_MCB_SEGMENT;
516 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment);
517
518 DPRINT("Unlinking UMB\n");
519
520 /* Check if UMBs are already unlinked */
521 if (!DosUmbLinked) return FALSE;
522
523 /* Find the block preceding the MCB that links it with the UMB chain */
524 while (Segment <= 0xFFFF)
525 {
526 if ((Segment + Mcb->Size) == (FIRST_MCB_SEGMENT + USER_MEMORY_SIZE))
527 {
528 /* This is the last non-UMB segment */
529 break;
530 }
531
532 /* Advance to the next MCB */
533 Segment += Mcb->Size + 1;
534 Mcb = SEGMENT_TO_MCB(Segment);
535 }
536
537 /* Mark the MCB as the last MCB */
538 Mcb->BlockType = 'Z';
539
540 DosUmbLinked = FALSE;
541 return TRUE;
542 }
543
544 WORD DosCreateFile(LPWORD Handle, LPCSTR FilePath, WORD Attributes)
545 {
546 HANDLE FileHandle;
547 WORD DosHandle;
548
549 DPRINT("DosCreateFile: FilePath \"%s\", Attributes 0x%04X\n",
550 FilePath,
551 Attributes);
552
553 /* Create the file */
554 FileHandle = CreateFileA(FilePath,
555 GENERIC_READ | GENERIC_WRITE,
556 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
557 NULL,
558 CREATE_ALWAYS,
559 Attributes,
560 NULL);
561
562 if (FileHandle == INVALID_HANDLE_VALUE)
563 {
564 /* Return the error code */
565 return (WORD)GetLastError();
566 }
567
568 /* Open the DOS handle */
569 DosHandle = DosOpenHandle(FileHandle);
570
571 if (DosHandle == INVALID_DOS_HANDLE)
572 {
573 /* Close the handle */
574 CloseHandle(FileHandle);
575
576 /* Return the error code */
577 return ERROR_TOO_MANY_OPEN_FILES;
578 }
579
580 /* It was successful */
581 *Handle = DosHandle;
582 return ERROR_SUCCESS;
583 }
584
585 WORD DosOpenFile(LPWORD Handle, LPCSTR FilePath, BYTE AccessMode)
586 {
587 HANDLE FileHandle;
588 ACCESS_MASK Access = 0;
589 WORD DosHandle;
590
591 DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n",
592 FilePath,
593 AccessMode);
594
595 /* Parse the access mode */
596 switch (AccessMode & 3)
597 {
598 case 0:
599 {
600 /* Read-only */
601 Access = GENERIC_READ;
602 break;
603 }
604
605 case 1:
606 {
607 /* Write only */
608 Access = GENERIC_WRITE;
609 break;
610 }
611
612 case 2:
613 {
614 /* Read and write */
615 Access = GENERIC_READ | GENERIC_WRITE;
616 break;
617 }
618
619 default:
620 {
621 /* Invalid */
622 return ERROR_INVALID_PARAMETER;
623 }
624 }
625
626 /* Open the file */
627 FileHandle = CreateFileA(FilePath,
628 Access,
629 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
630 NULL,
631 OPEN_EXISTING,
632 FILE_ATTRIBUTE_NORMAL,
633 NULL);
634
635 if (FileHandle == INVALID_HANDLE_VALUE)
636 {
637 /* Return the error code */
638 return (WORD)GetLastError();
639 }
640
641 /* Open the DOS handle */
642 DosHandle = DosOpenHandle(FileHandle);
643
644 if (DosHandle == INVALID_DOS_HANDLE)
645 {
646 /* Close the handle */
647 CloseHandle(FileHandle);
648
649 /* Return the error code */
650 return ERROR_TOO_MANY_OPEN_FILES;
651 }
652
653 /* It was successful */
654 *Handle = DosHandle;
655 return ERROR_SUCCESS;
656 }
657
658 WORD DosReadFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesRead)
659 {
660 WORD Result = ERROR_SUCCESS;
661 DWORD BytesRead32 = 0;
662 HANDLE Handle = DosGetRealHandle(FileHandle);
663
664 DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle, Count);
665
666 /* Make sure the handle is valid */
667 if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
668
669 /* Read the file */
670 if (!ReadFile(Handle, Buffer, Count, &BytesRead32, NULL))
671 {
672 /* Store the error code */
673 Result = (WORD)GetLastError();
674 }
675
676 /* The number of bytes read is always 16-bit */
677 *BytesRead = LOWORD(BytesRead32);
678
679 /* Return the error code */
680 return Result;
681 }
682
683 WORD DosWriteFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesWritten)
684 {
685 WORD Result = ERROR_SUCCESS;
686 DWORD BytesWritten32 = 0;
687 HANDLE Handle = DosGetRealHandle(FileHandle);
688
689 DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n",
690 FileHandle,
691 Count);
692
693 /* Make sure the handle is valid */
694 if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
695
696 /* Write the file */
697 if (!WriteFile(Handle, Buffer, Count, &BytesWritten32, NULL))
698 {
699 /* Store the error code */
700 Result = (WORD)GetLastError();
701 }
702
703 /* The number of bytes written is always 16-bit */
704 *BytesWritten = LOWORD(BytesWritten32);
705
706 /* Return the error code */
707 return Result;
708 }
709
710 WORD DosSeekFile(WORD FileHandle, LONG Offset, BYTE Origin, LPDWORD NewOffset)
711 {
712 WORD Result = ERROR_SUCCESS;
713 DWORD FilePointer;
714 HANDLE Handle = DosGetRealHandle(FileHandle);
715
716 DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n",
717 FileHandle,
718 Offset,
719 Origin);
720
721 /* Make sure the handle is valid */
722 if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
723
724 /* Check if the origin is valid */
725 if (Origin != FILE_BEGIN && Origin != FILE_CURRENT && Origin != FILE_END)
726 {
727 return ERROR_INVALID_FUNCTION;
728 }
729
730 /* Move the file pointer */
731 FilePointer = SetFilePointer(Handle, Offset, NULL, Origin);
732
733 /* Check if there's a possibility the operation failed */
734 if (FilePointer == INVALID_SET_FILE_POINTER)
735 {
736 /* Get the real error code */
737 Result = (WORD)GetLastError();
738 }
739
740 if (Result != ERROR_SUCCESS)
741 {
742 /* The operation did fail */
743 return Result;
744 }
745
746 /* Return the file pointer, if requested */
747 if (NewOffset) *NewOffset = FilePointer;
748
749 /* Return success */
750 return ERROR_SUCCESS;
751 }
752
753 BOOLEAN DosDuplicateHandle(WORD OldHandle, WORD NewHandle)
754 {
755 BYTE SftIndex;
756 PDOS_PSP PspBlock;
757 LPBYTE HandleTable;
758
759 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
760 OldHandle,
761 NewHandle);
762
763 /* The system PSP has no handle table */
764 if (CurrentPsp == SYSTEM_PSP) return FALSE;
765
766 /* Get a pointer to the handle table */
767 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
768 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
769
770 /* Make sure the old handle is open */
771 if (HandleTable[OldHandle] == 0xFF) return FALSE;
772
773 /* Check if the new handle is open */
774 if (HandleTable[NewHandle] != 0xFF)
775 {
776 /* Close it */
777 DosCloseHandle(NewHandle);
778 }
779
780 /* Increment the reference count of the SFT entry */
781 SftIndex = HandleTable[OldHandle];
782 DosSftRefCount[SftIndex]++;
783
784 /* Make the new handle point to that SFT entry */
785 HandleTable[NewHandle] = SftIndex;
786
787 /* Return success */
788 return TRUE;
789 }
790
791 BOOLEAN DosCloseHandle(WORD DosHandle)
792 {
793 BYTE SftIndex;
794 PDOS_PSP PspBlock;
795 LPBYTE HandleTable;
796
797 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle);
798
799 /* The system PSP has no handle table */
800 if (CurrentPsp == SYSTEM_PSP) return FALSE;
801
802 /* Get a pointer to the handle table */
803 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
804 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
805
806 /* Make sure the handle is open */
807 if (HandleTable[DosHandle] == 0xFF) return FALSE;
808
809 /* Decrement the reference count of the SFT entry */
810 SftIndex = HandleTable[DosHandle];
811 DosSftRefCount[SftIndex]--;
812
813 /* Check if the reference count fell to zero */
814 if (!DosSftRefCount[SftIndex])
815 {
816 /* Close the file, it's no longer needed */
817 CloseHandle(DosSystemFileTable[SftIndex]);
818
819 /* Clear the handle */
820 DosSystemFileTable[SftIndex] = INVALID_HANDLE_VALUE;
821 }
822
823 /* Clear the entry in the JFT */
824 HandleTable[DosHandle] = 0xFF;
825
826 return TRUE;
827 }
828
829 BOOLEAN DosChangeDrive(BYTE Drive)
830 {
831 WCHAR DirectoryPath[DOS_CMDLINE_LENGTH];
832
833 /* Make sure the drive exists */
834 if (Drive > (LastDrive - 'A')) return FALSE;
835
836 /* Find the path to the new current directory */
837 swprintf(DirectoryPath, L"%c\\%S", Drive + 'A', CurrentDirectories[Drive]);
838
839 /* Change the current directory of the process */
840 if (!SetCurrentDirectory(DirectoryPath)) return FALSE;
841
842 /* Set the current drive */
843 CurrentDrive = Drive;
844
845 /* Return success */
846 return TRUE;
847 }
848
849 BOOLEAN DosChangeDirectory(LPSTR Directory)
850 {
851 BYTE DriveNumber;
852 DWORD Attributes;
853 LPSTR Path;
854
855 /* Make sure the directory path is not too long */
856 if (strlen(Directory) >= DOS_DIR_LENGTH)
857 {
858 DosLastError = ERROR_PATH_NOT_FOUND;
859 return FALSE;
860 }
861
862 /* Get the drive number */
863 DriveNumber = Directory[0] - 'A';
864
865 /* Make sure the drive exists */
866 if (DriveNumber > (LastDrive - 'A'))
867 {
868 DosLastError = ERROR_PATH_NOT_FOUND;
869 return FALSE;
870 }
871
872 /* Get the file attributes */
873 Attributes = GetFileAttributesA(Directory);
874
875 /* Make sure the path exists and is a directory */
876 if ((Attributes == INVALID_FILE_ATTRIBUTES)
877 || !(Attributes & FILE_ATTRIBUTE_DIRECTORY))
878 {
879 DosLastError = ERROR_PATH_NOT_FOUND;
880 return FALSE;
881 }
882
883 /* Check if this is the current drive */
884 if (DriveNumber == CurrentDrive)
885 {
886 /* Change the directory */
887 if (!SetCurrentDirectoryA(Directory))
888 {
889 DosLastError = LOWORD(GetLastError());
890 return FALSE;
891 }
892 }
893
894 /* Get the directory part of the path */
895 Path = strchr(Directory, '\\');
896 if (Path != NULL)
897 {
898 /* Skip the backslash */
899 Path++;
900 }
901
902 /* Set the directory for the drive */
903 if (Path != NULL) strcpy(CurrentDirectories[DriveNumber], Path);
904 else strcpy(CurrentDirectories[DriveNumber], "");
905
906 /* Return success */
907 return TRUE;
908 }
909
910 VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment)
911 {
912 PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment);
913 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
914
915 ZeroMemory(PspBlock, sizeof(DOS_PSP));
916
917 /* Set the exit interrupt */
918 PspBlock->Exit[0] = 0xCD; // int 0x20
919 PspBlock->Exit[1] = 0x20;
920
921 /* Set the number of the last paragraph */
922 PspBlock->LastParagraph = PspSegment + ProgramSize - 1;
923
924 /* Save the interrupt vectors */
925 PspBlock->TerminateAddress = IntVecTable[0x22];
926 PspBlock->BreakAddress = IntVecTable[0x23];
927 PspBlock->CriticalAddress = IntVecTable[0x24];
928
929 /* Set the parent PSP */
930 PspBlock->ParentPsp = CurrentPsp;
931
932 /* Copy the parent handle table */
933 DosCopyHandleTable(PspBlock->HandleTable);
934
935 /* Set the environment block */
936 PspBlock->EnvBlock = Environment;
937
938 /* Set the handle table pointers to the internal handle table */
939 PspBlock->HandleTableSize = 20;
940 PspBlock->HandleTablePtr = MAKELONG(0x18, PspSegment);
941
942 /* Set the DOS version */
943 PspBlock->DosVersion = DOS_VERSION;
944
945 /* Set the far call opcodes */
946 PspBlock->FarCall[0] = 0xCD; // int 0x21
947 PspBlock->FarCall[1] = 0x21;
948 PspBlock->FarCall[2] = 0xCB; // retf
949
950 /* Set the command line */
951 PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
952 RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
953 PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
954 }
955
956 BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
957 {
958 BOOLEAN Success = FALSE, AllocatedEnvBlock = FALSE;
959 HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
960 LPBYTE Address = NULL;
961 LPSTR ProgramFilePath, Parameters[256];
962 CHAR CommandLineCopy[DOS_CMDLINE_LENGTH];
963 INT ParamCount = 0;
964 WORD Segment = 0;
965 WORD MaxAllocSize;
966 DWORD i, FileSize, ExeSize;
967 PIMAGE_DOS_HEADER Header;
968 PDWORD RelocationTable;
969 PWORD RelocWord;
970
971 DPRINT("DosCreateProcess: CommandLine \"%s\", EnvBlock 0x%04X\n",
972 CommandLine,
973 EnvBlock);
974
975 /* Save a copy of the command line */
976 strcpy(CommandLineCopy, CommandLine);
977
978 // FIXME: Improve parsing (especially: "some_path\with spaces\program.exe" options)
979
980 /* Get the file name of the executable */
981 ProgramFilePath = strtok(CommandLineCopy, " \t");
982
983 /* Load the parameters in the local array */
984 while ((ParamCount < sizeof(Parameters)/sizeof(Parameters[0]))
985 && ((Parameters[ParamCount] = strtok(NULL, " \t")) != NULL))
986 {
987 ParamCount++;
988 }
989
990 /* Open a handle to the executable */
991 FileHandle = CreateFileA(ProgramFilePath,
992 GENERIC_READ,
993 0,
994 NULL,
995 OPEN_EXISTING,
996 FILE_ATTRIBUTE_NORMAL,
997 NULL);
998 if (FileHandle == INVALID_HANDLE_VALUE) goto Cleanup;
999
1000 /* Get the file size */
1001 FileSize = GetFileSize(FileHandle, NULL);
1002
1003 /* Create a mapping object for the file */
1004 FileMapping = CreateFileMapping(FileHandle,
1005 NULL,
1006 PAGE_READONLY,
1007 0,
1008 0,
1009 NULL);
1010 if (FileMapping == NULL) goto Cleanup;
1011
1012 /* Map the file into memory */
1013 Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
1014 if (Address == NULL) goto Cleanup;
1015
1016 /* Did we get an environment segment? */
1017 if (!EnvBlock)
1018 {
1019 /* Set a flag to know if the environment block was allocated here */
1020 AllocatedEnvBlock = TRUE;
1021
1022 /* No, copy the one from the parent */
1023 EnvBlock = DosCopyEnvironmentBlock((CurrentPsp != SYSTEM_PSP)
1024 ? SEGMENT_TO_PSP(CurrentPsp)->EnvBlock
1025 : SYSTEM_ENV_BLOCK,
1026 ProgramFilePath);
1027 }
1028
1029 /* Check if this is an EXE file or a COM file */
1030 if (Address[0] == 'M' && Address[1] == 'Z')
1031 {
1032 /* EXE file */
1033
1034 /* Get the MZ header */
1035 Header = (PIMAGE_DOS_HEADER)Address;
1036
1037 /* Get the base size of the file, in paragraphs (rounded up) */
1038 ExeSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
1039
1040 /* Add the PSP size, in paragraphs */
1041 ExeSize += sizeof(DOS_PSP) >> 4;
1042
1043 /* Add the maximum size that should be allocated */
1044 ExeSize += Header->e_maxalloc;
1045
1046 /* Make sure it does not pass 0xFFFF */
1047 if (ExeSize > 0xFFFF) ExeSize = 0xFFFF;
1048
1049 /* Reduce the size one by one until the allocation is successful */
1050 for (i = Header->e_maxalloc; i >= Header->e_minalloc; i--, ExeSize--)
1051 {
1052 /* Try to allocate that much memory */
1053 Segment = DosAllocateMemory((WORD)ExeSize, NULL);
1054 if (Segment != 0) break;
1055 }
1056
1057 /* Check if at least the lowest allocation was successful */
1058 if (Segment == 0) goto Cleanup;
1059
1060 /* Initialize the PSP */
1061 DosInitializePsp(Segment,
1062 CommandLine,
1063 (WORD)ExeSize,
1064 EnvBlock);
1065
1066 /* The process owns its own memory */
1067 DosChangeMemoryOwner(Segment, Segment);
1068 DosChangeMemoryOwner(EnvBlock, Segment);
1069
1070 /* Copy the program to Segment:0100 */
1071 RtlCopyMemory((PVOID)((ULONG_PTR)BaseAddress
1072 + TO_LINEAR(Segment, 0x100)),
1073 Address + (Header->e_cparhdr << 4),
1074 min(FileSize - (Header->e_cparhdr << 4),
1075 (ExeSize << 4) - sizeof(DOS_PSP)));
1076
1077 /* Get the relocation table */
1078 RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
1079
1080 /* Perform relocations */
1081 for (i = 0; i < Header->e_crlc; i++)
1082 {
1083 /* Get a pointer to the word that needs to be patched */
1084 RelocWord = (PWORD)((ULONG_PTR)BaseAddress
1085 + TO_LINEAR(Segment + HIWORD(RelocationTable[i]),
1086 0x100 + LOWORD(RelocationTable[i])));
1087
1088 /* Add the number of the EXE segment to it */
1089 *RelocWord += Segment + (sizeof(DOS_PSP) >> 4);
1090 }
1091
1092 /* Set the initial segment registers */
1093 EmulatorSetRegister(EMULATOR_REG_DS, Segment);
1094 EmulatorSetRegister(EMULATOR_REG_ES, Segment);
1095
1096 /* Set the stack to the location from the header */
1097 EmulatorSetStack(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss,
1098 Header->e_sp);
1099
1100 /* Execute */
1101 CurrentPsp = Segment;
1102 DiskTransferArea = MAKELONG(0x80, Segment);
1103 EmulatorExecute(Segment + Header->e_cs + (sizeof(DOS_PSP) >> 4),
1104 Header->e_ip);
1105
1106 Success = TRUE;
1107 }
1108 else
1109 {
1110 /* COM file */
1111
1112 /* Find the maximum amount of memory that can be allocated */
1113 DosAllocateMemory(0xFFFF, &MaxAllocSize);
1114
1115 /* Make sure it's enough for the whole program and the PSP */
1116 if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP))) goto Cleanup;
1117
1118 /* Allocate all of it */
1119 Segment = DosAllocateMemory(MaxAllocSize, NULL);
1120 if (Segment == 0) goto Cleanup;
1121
1122 /* The process owns its own memory */
1123 DosChangeMemoryOwner(Segment, Segment);
1124 DosChangeMemoryOwner(EnvBlock, Segment);
1125
1126 /* Copy the program to Segment:0100 */
1127 RtlCopyMemory((PVOID)((ULONG_PTR)BaseAddress
1128 + TO_LINEAR(Segment, 0x100)),
1129 Address,
1130 FileSize);
1131
1132 /* Initialize the PSP */
1133 DosInitializePsp(Segment,
1134 CommandLine,
1135 (WORD)((FileSize + sizeof(DOS_PSP)) >> 4),
1136 EnvBlock);
1137
1138 /* Set the initial segment registers */
1139 EmulatorSetRegister(EMULATOR_REG_DS, Segment);
1140 EmulatorSetRegister(EMULATOR_REG_ES, Segment);
1141
1142 /* Set the stack to the last word of the segment */
1143 EmulatorSetStack(Segment, 0xFFFE);
1144
1145 /* Execute */
1146 CurrentPsp = Segment;
1147 DiskTransferArea = MAKELONG(0x80, Segment);
1148 EmulatorExecute(Segment, 0x100);
1149
1150 Success = TRUE;
1151 }
1152
1153 Cleanup:
1154 if (!Success)
1155 {
1156 /* It was not successful, cleanup the DOS memory */
1157 if (AllocatedEnvBlock) DosFreeMemory(EnvBlock);
1158 if (Segment) DosFreeMemory(Segment);
1159 }
1160
1161 /* Unmap the file*/
1162 if (Address != NULL) UnmapViewOfFile(Address);
1163
1164 /* Close the file mapping object */
1165 if (FileMapping != NULL) CloseHandle(FileMapping);
1166
1167 /* Close the file handle */
1168 if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
1169
1170 return Success;
1171 }
1172
1173 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
1174 {
1175 WORD i;
1176 WORD McbSegment = FIRST_MCB_SEGMENT;
1177 PDOS_MCB CurrentMcb;
1178 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
1179 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
1180
1181 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
1182 Psp,
1183 ReturnCode);
1184
1185 /* Check if this PSP is it's own parent */
1186 if (PspBlock->ParentPsp == Psp) goto Done;
1187
1188 for (i = 0; i < PspBlock->HandleTableSize; i++)
1189 {
1190 /* Close the handle */
1191 DosCloseHandle(i);
1192 }
1193
1194 /* Free the memory used by the process */
1195 while (TRUE)
1196 {
1197 /* Get a pointer to the MCB */
1198 CurrentMcb = SEGMENT_TO_MCB(McbSegment);
1199
1200 /* Make sure the MCB is valid */
1201 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType !='Z') break;
1202
1203 /* If this block was allocated by the process, free it */
1204 if (CurrentMcb->OwnerPsp == Psp) DosFreeMemory(McbSegment + 1);
1205
1206 /* If this was the last block, quit */
1207 if (CurrentMcb->BlockType == 'Z') break;
1208
1209 /* Update the segment and continue */
1210 McbSegment += CurrentMcb->Size + 1;
1211 }
1212
1213 Done:
1214 /* Restore the interrupt vectors */
1215 IntVecTable[0x22] = PspBlock->TerminateAddress;
1216 IntVecTable[0x23] = PspBlock->BreakAddress;
1217 IntVecTable[0x24] = PspBlock->CriticalAddress;
1218
1219 /* Update the current PSP */
1220 if (Psp == CurrentPsp)
1221 {
1222 CurrentPsp = PspBlock->ParentPsp;
1223 if (CurrentPsp == SYSTEM_PSP) VdmRunning = FALSE;
1224 }
1225
1226 /* Return control to the parent process */
1227 EmulatorExecute(HIWORD(PspBlock->TerminateAddress),
1228 LOWORD(PspBlock->TerminateAddress));
1229 }
1230
1231 CHAR DosReadCharacter(VOID)
1232 {
1233 CHAR Character = '\0';
1234 WORD BytesRead;
1235
1236 /* Use the file reading function */
1237 DosReadFile(DOS_INPUT_HANDLE, &Character, sizeof(CHAR), &BytesRead);
1238
1239 return Character;
1240 }
1241
1242 VOID DosPrintCharacter(CHAR Character)
1243 {
1244 WORD BytesWritten;
1245
1246 /* Use the file writing function */
1247 DosWriteFile(DOS_OUTPUT_HANDLE, &Character, sizeof(CHAR), &BytesWritten);
1248 }
1249
1250 BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
1251 {
1252 HANDLE Handle = DosGetRealHandle(FileHandle);
1253
1254 if (Handle == INVALID_HANDLE_VALUE)
1255 {
1256 /* Doesn't exist */
1257 DosLastError = ERROR_FILE_NOT_FOUND;
1258 return FALSE;
1259 }
1260
1261 switch (ControlCode)
1262 {
1263 /* Get Device Information */
1264 case 0x00:
1265 {
1266 WORD InfoWord = 0;
1267
1268 if (Handle == DosSystemFileTable[0])
1269 {
1270 /* Console input */
1271 InfoWord |= 1 << 0;
1272 }
1273 else if (Handle == DosSystemFileTable[1])
1274 {
1275 /* Console output */
1276 InfoWord |= 1 << 1;
1277 }
1278
1279 /* It is a character device */
1280 InfoWord |= 1 << 7;
1281
1282 /* Return the device information word */
1283 EmulatorSetRegister(EMULATOR_REG_DX, InfoWord);
1284
1285 return TRUE;
1286 }
1287
1288 /* Unsupported control code */
1289 default:
1290 {
1291 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode);
1292
1293 DosLastError = ERROR_INVALID_PARAMETER;
1294 return FALSE;
1295 }
1296 }
1297 }
1298
1299 VOID DosInt20h(LPWORD Stack)
1300 {
1301 /* This is the exit interrupt */
1302 DosTerminateProcess(Stack[STACK_CS], 0);
1303 }
1304
1305 VOID DosInt21h(LPWORD Stack)
1306 {
1307 INT i;
1308 CHAR Character;
1309 SYSTEMTIME SystemTime;
1310 PCHAR String;
1311 PDOS_INPUT_BUFFER InputBuffer;
1312 DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
1313 DWORD Ecx = EmulatorGetRegister(EMULATOR_REG_CX);
1314 DWORD Edx = EmulatorGetRegister(EMULATOR_REG_DX);
1315 DWORD Ebx = EmulatorGetRegister(EMULATOR_REG_BX);
1316 WORD DataSegment = (WORD)EmulatorGetRegister(EMULATOR_REG_DS);
1317 WORD ExtSegment = (WORD)EmulatorGetRegister(EMULATOR_REG_ES);
1318
1319 /* Check the value in the AH register */
1320 switch (HIBYTE(Eax))
1321 {
1322 /* Terminate Program */
1323 case 0x00:
1324 {
1325 DosTerminateProcess(Stack[STACK_CS], 0);
1326 break;
1327 }
1328
1329 /* Read Character And Echo */
1330 case 0x01:
1331 {
1332 Character = DosReadCharacter();
1333 DosPrintCharacter(Character);
1334 EmulatorSetRegister(EMULATOR_REG_AX, (Eax & 0xFFFFFF00) | Character);
1335 break;
1336 }
1337
1338 /* Print Character */
1339 case 0x02:
1340 {
1341 DosPrintCharacter(LOBYTE(Edx));
1342 break;
1343 }
1344
1345 /* Read Character Without Echo */
1346 case 0x07:
1347 case 0x08:
1348 {
1349 EmulatorSetRegister(EMULATOR_REG_AX,
1350 (Eax & 0xFFFFFF00) | DosReadCharacter());
1351 break;
1352 }
1353
1354 /* Print String */
1355 case 0x09:
1356 {
1357 String = (PCHAR)((ULONG_PTR)BaseAddress
1358 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1359
1360 while ((*String) != '$')
1361 {
1362 DosPrintCharacter(*String);
1363 String++;
1364 }
1365
1366 break;
1367 }
1368
1369 /* Read Buffered Input */
1370 case 0x0A:
1371 {
1372 InputBuffer = (PDOS_INPUT_BUFFER)((ULONG_PTR)BaseAddress
1373 + TO_LINEAR(DataSegment,
1374 LOWORD(Edx)));
1375
1376 InputBuffer->Length = 0;
1377 for (i = 0; i < InputBuffer->MaxLength; i ++)
1378 {
1379 Character = DosReadCharacter();
1380 DosPrintCharacter(Character);
1381 InputBuffer->Buffer[InputBuffer->Length] = Character;
1382 if (Character == '\r') break;
1383 InputBuffer->Length++;
1384 }
1385
1386 break;
1387 }
1388
1389 /* Set Default Drive */
1390 case 0x0E:
1391 {
1392 DosChangeDrive(LOBYTE(Edx));
1393 EmulatorSetRegister(EMULATOR_REG_AX,
1394 (Eax & 0xFFFFFF00) | (LastDrive - 'A' + 1));
1395
1396 break;
1397 }
1398
1399 /* Set Disk Transfer Area */
1400 case 0x1A:
1401 {
1402 DiskTransferArea = MAKELONG(LOWORD(Edx), DataSegment);
1403 break;
1404 }
1405
1406 /* Set Interrupt Vector */
1407 case 0x25:
1408 {
1409 DWORD FarPointer = MAKELONG(LOWORD(Edx), DataSegment);
1410
1411 /* Write the new far pointer to the IDT */
1412 ((PDWORD)BaseAddress)[LOBYTE(Eax)] = FarPointer;
1413
1414 break;
1415 }
1416
1417 /* Get system date */
1418 case 0x2A:
1419 {
1420 GetLocalTime(&SystemTime);
1421 EmulatorSetRegister(EMULATOR_REG_CX,
1422 (Ecx & 0xFFFF0000) | SystemTime.wYear);
1423 EmulatorSetRegister(EMULATOR_REG_DX,
1424 (Edx & 0xFFFF0000)
1425 | (SystemTime.wMonth << 8)
1426 | SystemTime.wDay);
1427 EmulatorSetRegister(EMULATOR_REG_AX,
1428 (Eax & 0xFFFFFF00) | SystemTime.wDayOfWeek);
1429 break;
1430 }
1431
1432 /* Set system date */
1433 case 0x2B:
1434 {
1435 GetLocalTime(&SystemTime);
1436 SystemTime.wYear = LOWORD(Ecx);
1437 SystemTime.wMonth = HIBYTE(Edx);
1438 SystemTime.wDay = LOBYTE(Edx);
1439
1440 if (SetLocalTime(&SystemTime))
1441 {
1442 /* Return success */
1443 EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
1444 }
1445 else
1446 {
1447 /* Return failure */
1448 EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
1449 }
1450
1451 break;
1452 }
1453
1454 /* Get system time */
1455 case 0x2C:
1456 {
1457 GetLocalTime(&SystemTime);
1458 EmulatorSetRegister(EMULATOR_REG_CX,
1459 (Ecx & 0xFFFF0000)
1460 | (SystemTime.wHour << 8)
1461 | SystemTime.wMinute);
1462 EmulatorSetRegister(EMULATOR_REG_DX,
1463 (Edx & 0xFFFF0000)
1464 | (SystemTime.wSecond << 8)
1465 | (SystemTime.wMilliseconds / 10));
1466 break;
1467 }
1468
1469 /* Set system time */
1470 case 0x2D:
1471 {
1472 GetLocalTime(&SystemTime);
1473 SystemTime.wHour = HIBYTE(Ecx);
1474 SystemTime.wMinute = LOBYTE(Ecx);
1475 SystemTime.wSecond = HIBYTE(Edx);
1476 SystemTime.wMilliseconds = LOBYTE(Edx) * 10;
1477
1478 if (SetLocalTime(&SystemTime))
1479 {
1480 /* Return success */
1481 EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
1482 }
1483 else
1484 {
1485 /* Return failure */
1486 EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
1487 }
1488
1489 break;
1490 }
1491
1492 /* Get Disk Transfer Area */
1493 case 0x2F:
1494 {
1495 EmulatorSetRegister(EMULATOR_REG_ES, HIWORD(DiskTransferArea));
1496 EmulatorSetRegister(EMULATOR_REG_BX, LOWORD(DiskTransferArea));
1497
1498 break;
1499 }
1500
1501 /* Get DOS Version */
1502 case 0x30:
1503 {
1504 PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
1505
1506 EmulatorSetRegister(EMULATOR_REG_AX, PspBlock->DosVersion);
1507 break;
1508 }
1509
1510 /* Get Interrupt Vector */
1511 case 0x35:
1512 {
1513 DWORD FarPointer = ((PDWORD)BaseAddress)[LOBYTE(Eax)];
1514
1515 /* Read the address from the IDT into ES:BX */
1516 EmulatorSetRegister(EMULATOR_REG_ES, HIWORD(FarPointer));
1517 EmulatorSetRegister(EMULATOR_REG_BX, LOWORD(FarPointer));
1518
1519 break;
1520 }
1521
1522 /* Create Directory */
1523 case 0x39:
1524 {
1525 String = (PCHAR)((ULONG_PTR)BaseAddress
1526 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1527
1528 if (CreateDirectoryA(String, NULL))
1529 {
1530 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1531 }
1532 else
1533 {
1534 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1535 EmulatorSetRegister(EMULATOR_REG_AX,
1536 (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
1537 }
1538
1539 break;
1540 }
1541
1542 /* Remove Directory */
1543 case 0x3A:
1544 {
1545 String = (PCHAR)((ULONG_PTR)BaseAddress
1546 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1547
1548 if (RemoveDirectoryA(String))
1549 {
1550 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1551 }
1552 else
1553 {
1554 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1555 EmulatorSetRegister(EMULATOR_REG_AX,
1556 (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
1557 }
1558
1559
1560 break;
1561 }
1562
1563 /* Set Current Directory */
1564 case 0x3B:
1565 {
1566 String = (PCHAR)((ULONG_PTR)BaseAddress
1567 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1568
1569 if (DosChangeDirectory(String))
1570 {
1571 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1572 }
1573 else
1574 {
1575 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1576 EmulatorSetRegister(EMULATOR_REG_AX,
1577 (Eax & 0xFFFF0000) | DosLastError);
1578 }
1579
1580 break;
1581 }
1582
1583 /* Create File */
1584 case 0x3C:
1585 {
1586 WORD FileHandle;
1587 WORD ErrorCode = DosCreateFile(&FileHandle,
1588 (LPCSTR)(ULONG_PTR)BaseAddress
1589 + TO_LINEAR(DataSegment, LOWORD(Edx)),
1590 LOWORD(Ecx));
1591
1592 if (ErrorCode == 0)
1593 {
1594 /* Clear CF */
1595 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1596
1597 /* Return the handle in AX */
1598 EmulatorSetRegister(EMULATOR_REG_AX,
1599 (Eax & 0xFFFF0000) | FileHandle);
1600 }
1601 else
1602 {
1603 /* Set CF */
1604 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1605
1606 /* Return the error code in AX */
1607 EmulatorSetRegister(EMULATOR_REG_AX,
1608 (Eax & 0xFFFF0000) | ErrorCode);
1609 }
1610
1611 break;
1612 }
1613
1614 /* Open File */
1615 case 0x3D:
1616 {
1617 WORD FileHandle;
1618 WORD ErrorCode = DosCreateFile(&FileHandle,
1619 (LPCSTR)(ULONG_PTR)BaseAddress
1620 + TO_LINEAR(DataSegment, LOWORD(Edx)),
1621 LOBYTE(Eax));
1622
1623 if (ErrorCode == 0)
1624 {
1625 /* Clear CF */
1626 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1627
1628 /* Return the handle in AX */
1629 EmulatorSetRegister(EMULATOR_REG_AX,
1630 (Eax & 0xFFFF0000) | FileHandle);
1631 }
1632 else
1633 {
1634 /* Set CF */
1635 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1636
1637 /* Return the error code in AX */
1638 EmulatorSetRegister(EMULATOR_REG_AX,
1639 (Eax & 0xFFFF0000) | ErrorCode);
1640 }
1641
1642 break;
1643 }
1644
1645 /* Close File */
1646 case 0x3E:
1647 {
1648 if (DosCloseHandle(LOWORD(Ebx)))
1649 {
1650 /* Clear CF */
1651 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1652 }
1653 else
1654 {
1655 /* Set CF */
1656 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1657
1658 /* Return the error code in AX */
1659 EmulatorSetRegister(EMULATOR_REG_AX,
1660 (Eax & 0xFFFF0000) | ERROR_INVALID_HANDLE);
1661 }
1662
1663 break;
1664 }
1665
1666 /* Read File */
1667 case 0x3F:
1668 {
1669 WORD BytesRead = 0;
1670 WORD ErrorCode = DosReadFile(LOWORD(Ebx),
1671 (LPVOID)((ULONG_PTR)BaseAddress
1672 + TO_LINEAR(DataSegment, LOWORD(Edx))),
1673 LOWORD(Ecx),
1674 &BytesRead);
1675
1676 if (ErrorCode == 0)
1677 {
1678 /* Clear CF */
1679 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1680
1681 /* Return the number of bytes read in AX */
1682 EmulatorSetRegister(EMULATOR_REG_AX,
1683 (Eax & 0xFFFF0000) | BytesRead);
1684 }
1685 else
1686 {
1687 /* Set CF */
1688 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1689
1690 /* Return the error code in AX */
1691 EmulatorSetRegister(EMULATOR_REG_AX,
1692 (Eax & 0xFFFF0000) | ErrorCode);
1693 }
1694 break;
1695 }
1696
1697 /* Write File */
1698 case 0x40:
1699 {
1700 WORD BytesWritten = 0;
1701 WORD ErrorCode = DosWriteFile(LOWORD(Ebx),
1702 (LPVOID)((ULONG_PTR)BaseAddress
1703 + TO_LINEAR(DataSegment, LOWORD(Edx))),
1704 LOWORD(Ecx),
1705 &BytesWritten);
1706
1707 if (ErrorCode == 0)
1708 {
1709 /* Clear CF */
1710 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1711
1712 /* Return the number of bytes written in AX */
1713 EmulatorSetRegister(EMULATOR_REG_AX,
1714 (Eax & 0xFFFF0000) | BytesWritten);
1715 }
1716 else
1717 {
1718 /* Set CF */
1719 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1720
1721 /* Return the error code in AX */
1722 EmulatorSetRegister(EMULATOR_REG_AX,
1723 (Eax & 0xFFFF0000) | ErrorCode);
1724 }
1725
1726 break;
1727 }
1728
1729 /* Delete File */
1730 case 0x41:
1731 {
1732 LPSTR FileName = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(DataSegment, Edx));
1733
1734 /* Call the API function */
1735 if (DeleteFileA(FileName)) Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1736 else
1737 {
1738 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1739 EmulatorSetRegister(EMULATOR_REG_AX, GetLastError());
1740 }
1741
1742 break;
1743 }
1744
1745 /* Seek File */
1746 case 0x42:
1747 {
1748 DWORD NewLocation;
1749 WORD ErrorCode = DosSeekFile(LOWORD(Ebx),
1750 MAKELONG(LOWORD(Edx), LOWORD(Ecx)),
1751 LOBYTE(Eax),
1752 &NewLocation);
1753
1754 if (ErrorCode == 0)
1755 {
1756 /* Clear CF */
1757 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1758
1759 /* Return the new offset in DX:AX */
1760 EmulatorSetRegister(EMULATOR_REG_DX,
1761 (Edx & 0xFFFF0000) | HIWORD(NewLocation));
1762 EmulatorSetRegister(EMULATOR_REG_AX,
1763 (Eax & 0xFFFF0000) | LOWORD(NewLocation));
1764 }
1765 else
1766 {
1767 /* Set CF */
1768 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1769
1770 /* Return the error code in AX */
1771 EmulatorSetRegister(EMULATOR_REG_AX,
1772 (Eax & 0xFFFF0000) | ErrorCode);
1773 }
1774
1775 break;
1776 }
1777
1778 /* Get/Set File Attributes */
1779 case 0x43:
1780 {
1781 DWORD Attributes;
1782 LPSTR FileName = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(DataSegment, Edx));
1783
1784 if (LOBYTE(Eax) == 0x00)
1785 {
1786 /* Get the attributes */
1787 Attributes = GetFileAttributesA(FileName);
1788
1789 /* Check if it failed */
1790 if (Attributes == INVALID_FILE_ATTRIBUTES)
1791 {
1792 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1793 EmulatorSetRegister(EMULATOR_REG_AX, GetLastError());
1794
1795 break;
1796 }
1797
1798 /* Return the attributes that DOS can understand */
1799 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1800 EmulatorSetRegister(EMULATOR_REG_CX,
1801 (Ecx & 0xFFFFFF00) | LOBYTE(Attributes));
1802 }
1803 else if (LOBYTE(Eax) == 0x01)
1804 {
1805 /* Try to set the attributes */
1806 if (SetFileAttributesA(FileName, LOBYTE(Ecx)))
1807 {
1808 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1809 }
1810 else
1811 {
1812 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1813 EmulatorSetRegister(EMULATOR_REG_AX, GetLastError());
1814 }
1815 }
1816 else
1817 {
1818 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1819 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_FUNCTION);
1820 }
1821
1822 break;
1823 }
1824
1825 /* IOCTL */
1826 case 0x44:
1827 {
1828 if (DosHandleIoctl(LOBYTE(Eax), LOWORD(Ebx)))
1829 {
1830 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1831 }
1832 else
1833 {
1834 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1835 EmulatorSetRegister(EMULATOR_REG_AX, DosLastError);
1836 }
1837
1838 break;
1839 }
1840
1841 /* Duplicate Handle */
1842 case 0x45:
1843 {
1844 WORD NewHandle;
1845 HANDLE Handle = DosGetRealHandle(LOWORD(Ebx));
1846
1847 if (Handle != INVALID_HANDLE_VALUE)
1848 {
1849 /* The handle is invalid */
1850 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1851 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_HANDLE);
1852
1853 break;
1854 }
1855
1856 /* Open a new handle to the same entry */
1857 NewHandle = DosOpenHandle(Handle);
1858
1859 if (NewHandle == INVALID_DOS_HANDLE)
1860 {
1861 /* Too many files open */
1862 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1863 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_TOO_MANY_OPEN_FILES);
1864
1865 break;
1866 }
1867
1868 /* Return the result */
1869 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1870 EmulatorSetRegister(EMULATOR_REG_AX, NewHandle);
1871
1872 break;
1873 }
1874
1875 /* Force Duplicate Handle */
1876 case 0x46:
1877 {
1878 if (DosDuplicateHandle(LOWORD(Ebx), LOWORD(Ecx)))
1879 {
1880 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1881 }
1882 else
1883 {
1884 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1885 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_HANDLE);
1886 }
1887
1888 break;
1889 }
1890
1891 /* Allocate Memory */
1892 case 0x48:
1893 {
1894 WORD MaxAvailable = 0;
1895 WORD Segment = DosAllocateMemory(LOWORD(Ebx), &MaxAvailable);
1896
1897 if (Segment != 0)
1898 {
1899 EmulatorSetRegister(EMULATOR_REG_AX, Segment);
1900 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1901 }
1902 else
1903 {
1904 EmulatorSetRegister(EMULATOR_REG_AX, DosLastError);
1905 EmulatorSetRegister(EMULATOR_REG_BX, MaxAvailable);
1906 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1907 }
1908
1909 break;
1910 }
1911
1912 /* Free Memory */
1913 case 0x49:
1914 {
1915 if (DosFreeMemory(ExtSegment))
1916 {
1917 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1918 }
1919 else
1920 {
1921 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_ARENA_TRASHED);
1922 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1923 }
1924
1925 break;
1926 }
1927
1928 /* Resize Memory Block */
1929 case 0x4A:
1930 {
1931 WORD Size;
1932
1933 if (DosResizeMemory(ExtSegment, LOWORD(Ebx), &Size))
1934 {
1935 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1936 }
1937 else
1938 {
1939 EmulatorSetRegister(EMULATOR_REG_AX, DosLastError);
1940 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1941 EmulatorSetRegister(EMULATOR_REG_BX, Size);
1942 }
1943
1944 break;
1945 }
1946
1947 /* Terminate With Return Code */
1948 case 0x4C:
1949 {
1950 DosTerminateProcess(CurrentPsp, LOBYTE(Eax));
1951 break;
1952 }
1953
1954 /* Get Current Process */
1955 case 0x51:
1956 {
1957 EmulatorSetRegister(EMULATOR_REG_BX, CurrentPsp);
1958
1959 break;
1960 }
1961
1962 /* Get/Set Memory Management Options */
1963 case 0x58:
1964 {
1965 if (LOBYTE(Eax) == 0x00)
1966 {
1967 /* Get allocation strategy */
1968
1969 EmulatorSetRegister(EMULATOR_REG_AX, DosAllocStrategy);
1970 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1971 }
1972 else if (LOBYTE(Eax) == 0x01)
1973 {
1974 /* Set allocation strategy */
1975
1976 if ((LOBYTE(Ebx) & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
1977 == (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
1978 {
1979 /* Can't set both */
1980 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_PARAMETER);
1981 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1982 break;
1983 }
1984
1985 if ((LOBYTE(Ebx) & 0x3F) > DOS_ALLOC_LAST_FIT)
1986 {
1987 /* Invalid allocation strategy */
1988 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_PARAMETER);
1989 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1990 break;
1991 }
1992
1993 DosAllocStrategy = LOBYTE(Ebx);
1994 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1995 }
1996 else if (LOBYTE(Eax) == 0x02)
1997 {
1998 /* Get UMB link state */
1999
2000 Eax &= 0xFFFFFF00;
2001 if (DosUmbLinked) Eax |= 1;
2002 EmulatorSetRegister(EMULATOR_REG_AX, Eax);
2003 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2004 }
2005 else if (LOBYTE(Eax) == 0x03)
2006 {
2007 /* Set UMB link state */
2008
2009 if (Ebx) DosLinkUmb();
2010 else DosUnlinkUmb();
2011 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2012 }
2013 else
2014 {
2015 /* Invalid or unsupported function */
2016
2017 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2018 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_FUNCTION);
2019 }
2020
2021 break;
2022 }
2023
2024 /* Unsupported */
2025 default:
2026 {
2027 DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax));
2028 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2029 }
2030 }
2031 }
2032
2033 VOID DosBreakInterrupt(LPWORD Stack)
2034 {
2035 UNREFERENCED_PARAMETER(Stack);
2036
2037 VdmRunning = FALSE;
2038 }
2039
2040 BOOLEAN DosInitialize(VOID)
2041 {
2042 BYTE i;
2043 PDOS_MCB Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT);
2044 FILE *Stream;
2045 WCHAR Buffer[256];
2046 LPWSTR SourcePtr, Environment;
2047 LPSTR AsciiString;
2048 LPSTR DestPtr = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(SYSTEM_ENV_BLOCK, 0));
2049 DWORD AsciiSize;
2050 CHAR CurrentDirectory[MAX_PATH];
2051 CHAR DosDirectory[DOS_DIR_LENGTH];
2052 LPSTR Path;
2053
2054 /* Initialize the MCB */
2055 Mcb->BlockType = 'Z';
2056 Mcb->Size = USER_MEMORY_SIZE;
2057 Mcb->OwnerPsp = 0;
2058
2059 /* Initialize the link MCB to the UMB area */
2060 Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT + USER_MEMORY_SIZE + 1);
2061 Mcb->BlockType = 'M';
2062 Mcb->Size = UMB_START_SEGMENT - FIRST_MCB_SEGMENT - USER_MEMORY_SIZE - 2;
2063 Mcb->OwnerPsp = SYSTEM_PSP;
2064
2065 /* Initialize the UMB area */
2066 Mcb = SEGMENT_TO_MCB(UMB_START_SEGMENT);
2067 Mcb->BlockType = 'Z';
2068 Mcb->Size = UMB_END_SEGMENT - UMB_START_SEGMENT;
2069 Mcb->OwnerPsp = 0;
2070
2071 /* Get the environment strings */
2072 SourcePtr = Environment = GetEnvironmentStringsW();
2073 if (Environment == NULL) return FALSE;
2074
2075 /* Fill the DOS system environment block */
2076 while (*SourcePtr)
2077 {
2078 /* Get the size of the ASCII string */
2079 AsciiSize = WideCharToMultiByte(CP_ACP,
2080 0,
2081 SourcePtr,
2082 -1,
2083 NULL,
2084 0,
2085 NULL,
2086 NULL);
2087
2088 /* Allocate memory for the ASCII string */
2089 AsciiString = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, AsciiSize);
2090 if (AsciiString == NULL)
2091 {
2092 FreeEnvironmentStringsW(Environment);
2093 return FALSE;
2094 }
2095
2096 /* Convert to ASCII */
2097 WideCharToMultiByte(CP_ACP,
2098 0,
2099 SourcePtr,
2100 -1,
2101 AsciiString,
2102 AsciiSize,
2103 NULL,
2104 NULL);
2105
2106 /* Copy the string into DOS memory */
2107 strcpy(DestPtr, AsciiString);
2108
2109 /* Move to the next string */
2110 SourcePtr += wcslen(SourcePtr) + 1;
2111 DestPtr += strlen(AsciiString);
2112 *(DestPtr++) = 0;
2113
2114 /* Free the memory */
2115 HeapFree(GetProcessHeap(), 0, AsciiString);
2116 }
2117 *DestPtr = 0;
2118
2119 /* Free the memory allocated for environment strings */
2120 FreeEnvironmentStringsW(Environment);
2121
2122 /* Clear the current directory buffer */
2123 ZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
2124
2125 /* Get the current directory */
2126 if (!GetCurrentDirectoryA(MAX_PATH, CurrentDirectory))
2127 {
2128 // TODO: Use some kind of default path?
2129 return FALSE;
2130 }
2131
2132 /* Convert that to a DOS path */
2133 if (!GetShortPathNameA(CurrentDirectory, DosDirectory, DOS_DIR_LENGTH))
2134 {
2135 // TODO: Use some kind of default path?
2136 return FALSE;
2137 }
2138
2139 /* Set the drive */
2140 CurrentDrive = DosDirectory[0] - 'A';
2141
2142 /* Get the path */
2143 Path = strchr(DosDirectory, '\\');
2144 if (Path != NULL)
2145 {
2146 /* Skip the backslash */
2147 Path++;
2148 }
2149
2150 /* Set the directory */
2151 if (Path != NULL) strcpy(CurrentDirectories[CurrentDrive], Path);
2152
2153 /* Read CONFIG.SYS */
2154 Stream = _wfopen(DOS_CONFIG_PATH, L"r");
2155 if (Stream != NULL)
2156 {
2157 while (fgetws(Buffer, 256, Stream))
2158 {
2159 // TODO: Parse the line
2160 }
2161 fclose(Stream);
2162 }
2163
2164 /* Initialize the SFT */
2165 for (i = 0; i < DOS_SFT_SIZE; i++)
2166 {
2167 DosSystemFileTable[i] = INVALID_HANDLE_VALUE;
2168 DosSftRefCount[i] = 0;
2169 }
2170
2171 /* Get handles to standard I/O devices */
2172 DosSystemFileTable[0] = GetStdHandle(STD_INPUT_HANDLE);
2173 DosSystemFileTable[1] = GetStdHandle(STD_OUTPUT_HANDLE);
2174 DosSystemFileTable[2] = GetStdHandle(STD_ERROR_HANDLE);
2175
2176 return TRUE;
2177 }
2178
2179 /* EOF */