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