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