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