2a48c6f2b008ba2b7987760345e9a7c9706d9b41
[reactos.git] / reactos / subsystems / ntvdm / dos / dos32krnl / dos.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: dos/dos32krnl/dos.c
5 * PURPOSE: DOS32 Kernel
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #define NDEBUG
13
14 #include "emulator.h"
15 #include "cpu/cpu.h"
16 #include "int32.h"
17
18 #include "dos.h"
19 #include "dos/dem.h"
20
21 #include "bios/bios.h"
22
23 #include "io.h"
24 #include "hardware/ps2.h"
25
26 /* PRIVATE VARIABLES **********************************************************/
27
28 CALLBACK16 DosContext;
29
30 static WORD CurrentPsp = SYSTEM_PSP;
31 static WORD DosLastError = 0;
32 static DWORD DiskTransferArea;
33 /*static*/ BYTE CurrentDrive;
34 static CHAR LastDrive = 'E';
35 static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH];
36
37 static struct
38 {
39 HANDLE Handle;
40 WORD RefCount;
41 } DosSystemFileTable[DOS_SFT_SIZE];
42
43 static BYTE DosAllocStrategy = DOS_ALLOC_BEST_FIT;
44 static BOOLEAN DosUmbLinked = FALSE;
45 static WORD DosErrorLevel = 0x0000;
46
47 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
48 BOOLEAN DoEcho = FALSE;
49
50 /* PRIVATE FUNCTIONS **********************************************************/
51
52 /*
53 * Memory management functions
54 */
55 static VOID DosCombineFreeBlocks(WORD StartBlock)
56 {
57 PDOS_MCB CurrentMcb = SEGMENT_TO_MCB(StartBlock), NextMcb;
58
59 /* If this is the last block or it's not free, quit */
60 if (CurrentMcb->BlockType == 'Z' || CurrentMcb->OwnerPsp != 0) return;
61
62 while (TRUE)
63 {
64 /* Get a pointer to the next MCB */
65 NextMcb = SEGMENT_TO_MCB(StartBlock + CurrentMcb->Size + 1);
66
67 /* Check if the next MCB is free */
68 if (NextMcb->OwnerPsp == 0)
69 {
70 /* Combine them */
71 CurrentMcb->Size += NextMcb->Size + 1;
72 CurrentMcb->BlockType = NextMcb->BlockType;
73 NextMcb->BlockType = 'I';
74 }
75 else
76 {
77 /* No more adjoining free blocks */
78 break;
79 }
80 }
81 }
82
83 static WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable)
84 {
85 WORD Result = 0, Segment = FIRST_MCB_SEGMENT, MaxSize = 0;
86 PDOS_MCB CurrentMcb, NextMcb;
87 BOOLEAN SearchUmb = FALSE;
88
89 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size);
90
91 if (DosUmbLinked && (DosAllocStrategy & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW)))
92 {
93 /* Search UMB first */
94 Segment = UMB_START_SEGMENT;
95 SearchUmb = TRUE;
96 }
97
98 while (TRUE)
99 {
100 /* Get a pointer to the MCB */
101 CurrentMcb = SEGMENT_TO_MCB(Segment);
102
103 /* Make sure it's valid */
104 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z')
105 {
106 DPRINT("The DOS memory arena is corrupted!\n");
107 DosLastError = ERROR_ARENA_TRASHED;
108 return 0;
109 }
110
111 /* Only check free blocks */
112 if (CurrentMcb->OwnerPsp != 0) goto Next;
113
114 /* Combine this free block with adjoining free blocks */
115 DosCombineFreeBlocks(Segment);
116
117 /* Update the maximum block size */
118 if (CurrentMcb->Size > MaxSize) MaxSize = CurrentMcb->Size;
119
120 /* Check if this block is big enough */
121 if (CurrentMcb->Size < Size) goto Next;
122
123 switch (DosAllocStrategy & 0x3F)
124 {
125 case DOS_ALLOC_FIRST_FIT:
126 {
127 /* For first fit, stop immediately */
128 Result = Segment;
129 goto Done;
130 }
131
132 case DOS_ALLOC_BEST_FIT:
133 {
134 /* For best fit, update the smallest block found so far */
135 if ((Result == 0) || (CurrentMcb->Size < SEGMENT_TO_MCB(Result)->Size))
136 {
137 Result = Segment;
138 }
139
140 break;
141 }
142
143 case DOS_ALLOC_LAST_FIT:
144 {
145 /* For last fit, make the current block the result, but keep searching */
146 Result = Segment;
147 break;
148 }
149 }
150
151 Next:
152 /* If this was the last MCB in the chain, quit */
153 if (CurrentMcb->BlockType == 'Z')
154 {
155 /* Check if nothing was found while searching through UMBs */
156 if ((Result == 0) && SearchUmb && (DosAllocStrategy & DOS_ALLOC_HIGH_LOW))
157 {
158 /* Search low memory */
159 Segment = FIRST_MCB_SEGMENT;
160 continue;
161 }
162
163 break;
164 }
165
166 /* Otherwise, update the segment and continue */
167 Segment += CurrentMcb->Size + 1;
168 }
169
170 Done:
171
172 /* If we didn't find a free block, return 0 */
173 if (Result == 0)
174 {
175 DosLastError = ERROR_NOT_ENOUGH_MEMORY;
176 if (MaxAvailable) *MaxAvailable = MaxSize;
177 return 0;
178 }
179
180 /* Get a pointer to the MCB */
181 CurrentMcb = SEGMENT_TO_MCB(Result);
182
183 /* Check if the block is larger than requested */
184 if (CurrentMcb->Size > Size)
185 {
186 /* It is, split it into two blocks */
187 NextMcb = SEGMENT_TO_MCB(Result + Size + 1);
188
189 /* Initialize the new MCB structure */
190 NextMcb->BlockType = CurrentMcb->BlockType;
191 NextMcb->Size = CurrentMcb->Size - Size - 1;
192 NextMcb->OwnerPsp = 0;
193
194 /* Update the current block */
195 CurrentMcb->BlockType = 'M';
196 CurrentMcb->Size = Size;
197 }
198
199 /* Take ownership of the block */
200 CurrentMcb->OwnerPsp = CurrentPsp;
201
202 /* Return the segment of the data portion of the block */
203 return Result + 1;
204 }
205
206 static BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable)
207 {
208 BOOLEAN Success = TRUE;
209 WORD Segment = BlockData - 1, ReturnSize = 0, NextSegment;
210 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment), NextMcb;
211
212 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
213 BlockData,
214 NewSize);
215
216 /* Make sure this is a valid, allocated block */
217 if ((Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') || Mcb->OwnerPsp == 0)
218 {
219 Success = FALSE;
220 DosLastError = ERROR_INVALID_HANDLE;
221 goto Done;
222 }
223
224 ReturnSize = Mcb->Size;
225
226 /* Check if we need to expand or contract the block */
227 if (NewSize > Mcb->Size)
228 {
229 /* We can't expand the last block */
230 if (Mcb->BlockType != 'M')
231 {
232 Success = FALSE;
233 goto Done;
234 }
235
236 /* Get the pointer and segment of the next MCB */
237 NextSegment = Segment + Mcb->Size + 1;
238 NextMcb = SEGMENT_TO_MCB(NextSegment);
239
240 /* Make sure the next segment is free */
241 if (NextMcb->OwnerPsp != 0)
242 {
243 DPRINT("Cannot expand memory block: next segment is not free!\n");
244 DosLastError = ERROR_NOT_ENOUGH_MEMORY;
245 Success = FALSE;
246 goto Done;
247 }
248
249 /* Combine this free block with adjoining free blocks */
250 DosCombineFreeBlocks(NextSegment);
251
252 /* Set the maximum possible size of the block */
253 ReturnSize += NextMcb->Size + 1;
254
255 if (ReturnSize < NewSize)
256 {
257 DPRINT("Cannot expand memory block: insufficient free segments available!\n");
258 DosLastError = ERROR_NOT_ENOUGH_MEMORY;
259 Success = FALSE;
260 goto Done;
261 }
262
263 /* Maximize the current block */
264 Mcb->Size = ReturnSize;
265 Mcb->BlockType = NextMcb->BlockType;
266
267 /* Invalidate the next block */
268 NextMcb->BlockType = 'I';
269
270 /* Check if the block is larger than requested */
271 if (Mcb->Size > NewSize)
272 {
273 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
274 Mcb->Size,
275 NewSize);
276
277 /* It is, split it into two blocks */
278 NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
279
280 /* Initialize the new MCB structure */
281 NextMcb->BlockType = Mcb->BlockType;
282 NextMcb->Size = Mcb->Size - NewSize - 1;
283 NextMcb->OwnerPsp = 0;
284
285 /* Update the current block */
286 Mcb->BlockType = 'M';
287 Mcb->Size = NewSize;
288 }
289 }
290 else if (NewSize < Mcb->Size)
291 {
292 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
293 Mcb->Size,
294 NewSize);
295
296 /* Just split the block */
297 NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
298 NextMcb->BlockType = Mcb->BlockType;
299 NextMcb->Size = Mcb->Size - NewSize - 1;
300 NextMcb->OwnerPsp = 0;
301
302 /* Update the MCB */
303 Mcb->BlockType = 'M';
304 Mcb->Size = NewSize;
305 }
306
307 Done:
308 /* Check if the operation failed */
309 if (!Success)
310 {
311 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
312 ReturnSize);
313
314 /* Return the maximum possible size */
315 if (MaxAvailable) *MaxAvailable = ReturnSize;
316 }
317
318 return Success;
319 }
320
321 static BOOLEAN DosFreeMemory(WORD BlockData)
322 {
323 PDOS_MCB Mcb = SEGMENT_TO_MCB(BlockData - 1);
324
325 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData);
326
327 /* Make sure the MCB is valid */
328 if (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z')
329 {
330 DPRINT("MCB block type '%c' not valid!\n", Mcb->BlockType);
331 return FALSE;
332 }
333
334 /* Mark the block as free */
335 Mcb->OwnerPsp = 0;
336
337 return TRUE;
338 }
339
340 static BOOLEAN DosLinkUmb(VOID)
341 {
342 DWORD Segment = FIRST_MCB_SEGMENT;
343 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment);
344
345 DPRINT("Linking UMB\n");
346
347 /* Check if UMBs are already linked */
348 if (DosUmbLinked) return FALSE;
349
350 /* Find the last block */
351 while ((Mcb->BlockType == 'M') && (Segment <= 0xFFFF))
352 {
353 Segment += Mcb->Size + 1;
354 Mcb = SEGMENT_TO_MCB(Segment);
355 }
356
357 /* Make sure it's valid */
358 if (Mcb->BlockType != 'Z') return FALSE;
359
360 /* Connect the MCB with the UMB chain */
361 Mcb->BlockType = 'M';
362
363 DosUmbLinked = TRUE;
364 return TRUE;
365 }
366
367 static BOOLEAN DosUnlinkUmb(VOID)
368 {
369 DWORD Segment = FIRST_MCB_SEGMENT;
370 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment);
371
372 DPRINT("Unlinking UMB\n");
373
374 /* Check if UMBs are already unlinked */
375 if (!DosUmbLinked) return FALSE;
376
377 /* Find the block preceding the MCB that links it with the UMB chain */
378 while (Segment <= 0xFFFF)
379 {
380 if ((Segment + Mcb->Size) == (FIRST_MCB_SEGMENT + USER_MEMORY_SIZE))
381 {
382 /* This is the last non-UMB segment */
383 break;
384 }
385
386 /* Advance to the next MCB */
387 Segment += Mcb->Size + 1;
388 Mcb = SEGMENT_TO_MCB(Segment);
389 }
390
391 /* Mark the MCB as the last MCB */
392 Mcb->BlockType = 'Z';
393
394 DosUmbLinked = FALSE;
395 return TRUE;
396 }
397
398 static VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner)
399 {
400 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1);
401
402 /* Just set the owner */
403 Mcb->OwnerPsp = NewOwner;
404 }
405
406 static WORD DosCopyEnvironmentBlock(LPCSTR Environment, LPCSTR ProgramName)
407 {
408 PCHAR Ptr, DestBuffer = NULL;
409 ULONG TotalSize = 0;
410 WORD DestSegment;
411
412 Ptr = (PCHAR)Environment;
413
414 /* Calculate the size of the environment block */
415 while (*Ptr) Ptr += strlen(Ptr) + 1;
416 TotalSize = (ULONG_PTR)Ptr - (ULONG_PTR)Environment + 1; // Add final NULL-terminator
417
418 /* Add the string buffer size */
419 TotalSize += strlen(ProgramName) + 1;
420
421 /* Add the two extra bytes */
422 TotalSize += 2;
423
424 /* Allocate the memory for the environment block */
425 DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
426 if (!DestSegment) return 0;
427
428 Ptr = (PCHAR)Environment;
429
430 DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
431 while (*Ptr)
432 {
433 /* Copy the string and NULL-terminate it */
434 strcpy(DestBuffer, Ptr);
435 DestBuffer += strlen(Ptr);
436 *(DestBuffer++) = '\0';
437
438 /* Move to the next string */
439 Ptr += strlen(Ptr) + 1;
440 }
441 /* NULL-terminate the environment block */
442 *(DestBuffer++) = '\0';
443
444 /* Store the special program name tag */
445 *(DestBuffer++) = LOBYTE(DOS_PROGRAM_NAME_TAG);
446 *(DestBuffer++) = HIBYTE(DOS_PROGRAM_NAME_TAG);
447
448 /* Copy the program name after the environment block */
449 strcpy(DestBuffer, ProgramName);
450
451 return DestSegment;
452 }
453
454
455
456
457
458
459 /* Taken from base/shell/cmd/console.c */
460 BOOL IsConsoleHandle(HANDLE hHandle)
461 {
462 DWORD dwMode;
463
464 /* Check whether the handle may be that of a console... */
465 if ((GetFileType(hHandle) & FILE_TYPE_CHAR) == 0) return FALSE;
466
467 /*
468 * It may be. Perform another test... The idea comes from the
469 * MSDN description of the WriteConsole API:
470 *
471 * "WriteConsole fails if it is used with a standard handle
472 * that is redirected to a file. If an application processes
473 * multilingual output that can be redirected, determine whether
474 * the output handle is a console handle (one method is to call
475 * the GetConsoleMode function and check whether it succeeds).
476 * If the handle is a console handle, call WriteConsole. If the
477 * handle is not a console handle, the output is redirected and
478 * you should call WriteFile to perform the I/O."
479 */
480 return GetConsoleMode(hHandle, &dwMode);
481 }
482
483 WORD DosOpenHandle(HANDLE Handle)
484 {
485 BYTE i;
486 WORD DosHandle;
487 PDOS_PSP PspBlock;
488 LPBYTE HandleTable;
489
490 /* The system PSP has no handle table */
491 if (CurrentPsp == SYSTEM_PSP) return INVALID_DOS_HANDLE;
492
493 /* Get a pointer to the handle table */
494 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
495 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
496
497 /* Find a free entry in the JFT */
498 for (DosHandle = 0; DosHandle < PspBlock->HandleTableSize; DosHandle++)
499 {
500 if (HandleTable[DosHandle] == 0xFF) break;
501 }
502
503 /* If there are no free entries, fail */
504 if (DosHandle == PspBlock->HandleTableSize) return INVALID_DOS_HANDLE;
505
506 /* Check if the handle is already in the SFT */
507 for (i = 0; i < DOS_SFT_SIZE; i++)
508 {
509 /* Check if this is the same handle */
510 if (DosSystemFileTable[i].Handle != Handle) continue;
511
512 /* Already in the table, reference it */
513 DosSystemFileTable[i].RefCount++;
514
515 /* Set the JFT entry to that SFT index */
516 HandleTable[DosHandle] = i;
517
518 /* Return the new handle */
519 return DosHandle;
520 }
521
522 /* Add the handle to the SFT */
523 for (i = 0; i < DOS_SFT_SIZE; i++)
524 {
525 /* Make sure this is an empty table entry */
526 if (DosSystemFileTable[i].Handle != INVALID_HANDLE_VALUE) continue;
527
528 /* Initialize the empty table entry */
529 DosSystemFileTable[i].Handle = Handle;
530 DosSystemFileTable[i].RefCount = 1;
531
532 /* Set the JFT entry to that SFT index */
533 HandleTable[DosHandle] = i;
534
535 /* Return the new handle */
536 return DosHandle;
537 }
538
539 /* The SFT is full */
540 return INVALID_DOS_HANDLE;
541 }
542
543 HANDLE DosGetRealHandle(WORD DosHandle)
544 {
545 PDOS_PSP PspBlock;
546 LPBYTE HandleTable;
547
548 /* The system PSP has no handle table */
549 if (CurrentPsp == SYSTEM_PSP) return INVALID_HANDLE_VALUE;
550
551 /* Get a pointer to the handle table */
552 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
553 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
554
555 /* Make sure the handle is open */
556 if (HandleTable[DosHandle] == 0xFF) return INVALID_HANDLE_VALUE;
557
558 /* Return the Win32 handle */
559 return DosSystemFileTable[HandleTable[DosHandle]].Handle;
560 }
561
562 static VOID DosCopyHandleTable(LPBYTE DestinationTable)
563 {
564 INT i;
565 PDOS_PSP PspBlock;
566 LPBYTE SourceTable;
567
568 /* Clear the table first */
569 for (i = 0; i < 20; i++) DestinationTable[i] = 0xFF;
570
571 /* Check if this is the initial process */
572 if (CurrentPsp == SYSTEM_PSP)
573 {
574 /* Set up the standard I/O devices */
575 for (i = 0; i <= 2; i++)
576 {
577 /* Set the index in the SFT */
578 DestinationTable[i] = (BYTE)i;
579
580 /* Increase the reference count */
581 DosSystemFileTable[i].RefCount++;
582 }
583
584 /* Done */
585 return;
586 }
587
588 /* Get the parent PSP block and handle table */
589 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
590 SourceTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
591
592 /* Copy the first 20 handles into the new table */
593 for (i = 0; i < 20; i++)
594 {
595 DestinationTable[i] = SourceTable[i];
596
597 /* Increase the reference count */
598 DosSystemFileTable[SourceTable[i]].RefCount++;
599 }
600 }
601
602 static BOOLEAN DosResizeHandleTable(WORD NewSize)
603 {
604 PDOS_PSP PspBlock;
605 LPBYTE HandleTable;
606 WORD Segment;
607
608 /* Get the PSP block */
609 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
610
611 if (NewSize == PspBlock->HandleTableSize)
612 {
613 /* No change */
614 return TRUE;
615 }
616
617 if (PspBlock->HandleTableSize > 20)
618 {
619 /* Get the segment of the current table */
620 Segment = (LOWORD(PspBlock->HandleTablePtr) >> 4) + HIWORD(PspBlock->HandleTablePtr);
621
622 if (NewSize <= 20)
623 {
624 /* Get the current handle table */
625 HandleTable = FAR_POINTER(PspBlock->HandleTablePtr);
626
627 /* Copy it to the PSP */
628 RtlCopyMemory(PspBlock->HandleTable, HandleTable, NewSize);
629
630 /* Free the memory */
631 DosFreeMemory(Segment);
632
633 /* Update the handle table pointer and size */
634 PspBlock->HandleTableSize = NewSize;
635 PspBlock->HandleTablePtr = MAKELONG(0x18, CurrentPsp);
636 }
637 else
638 {
639 /* Resize the memory */
640 if (!DosResizeMemory(Segment, NewSize, NULL))
641 {
642 /* Unable to resize, try allocating it somewhere else */
643 Segment = DosAllocateMemory(NewSize, NULL);
644 if (Segment == 0) return FALSE;
645
646 /* Get the new handle table */
647 HandleTable = SEG_OFF_TO_PTR(Segment, 0);
648
649 /* Copy the handles to the new table */
650 RtlCopyMemory(HandleTable,
651 FAR_POINTER(PspBlock->HandleTablePtr),
652 PspBlock->HandleTableSize);
653
654 /* Update the handle table pointer */
655 PspBlock->HandleTablePtr = MAKELONG(0, Segment);
656 }
657
658 /* Update the handle table size */
659 PspBlock->HandleTableSize = NewSize;
660 }
661 }
662 else if (NewSize > 20)
663 {
664 Segment = DosAllocateMemory(NewSize, NULL);
665 if (Segment == 0) return FALSE;
666
667 /* Get the new handle table */
668 HandleTable = SEG_OFF_TO_PTR(Segment, 0);
669
670 /* Copy the handles from the PSP to the new table */
671 RtlCopyMemory(HandleTable,
672 FAR_POINTER(PspBlock->HandleTablePtr),
673 PspBlock->HandleTableSize);
674
675 /* Update the handle table pointer and size */
676 PspBlock->HandleTableSize = NewSize;
677 PspBlock->HandleTablePtr = MAKELONG(0, Segment);
678 }
679
680 return TRUE;
681 }
682
683 static BOOLEAN DosCloseHandle(WORD DosHandle)
684 {
685 BYTE SftIndex;
686 PDOS_PSP PspBlock;
687 LPBYTE HandleTable;
688
689 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle);
690
691 /* The system PSP has no handle table */
692 if (CurrentPsp == SYSTEM_PSP) return FALSE;
693
694 /* Get a pointer to the handle table */
695 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
696 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
697
698 /* Make sure the handle is open */
699 if (HandleTable[DosHandle] == 0xFF) return FALSE;
700
701 /* Decrement the reference count of the SFT entry */
702 SftIndex = HandleTable[DosHandle];
703 DosSystemFileTable[SftIndex].RefCount--;
704
705 /* Check if the reference count fell to zero */
706 if (!DosSystemFileTable[SftIndex].RefCount)
707 {
708 /* Close the file, it's no longer needed */
709 CloseHandle(DosSystemFileTable[SftIndex].Handle);
710
711 /* Clear the handle */
712 DosSystemFileTable[SftIndex].Handle = INVALID_HANDLE_VALUE;
713 }
714
715 /* Clear the entry in the JFT */
716 HandleTable[DosHandle] = 0xFF;
717
718 return TRUE;
719 }
720
721 static BOOLEAN DosDuplicateHandle(WORD OldHandle, WORD NewHandle)
722 {
723 BYTE SftIndex;
724 PDOS_PSP PspBlock;
725 LPBYTE HandleTable;
726
727 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
728 OldHandle,
729 NewHandle);
730
731 /* The system PSP has no handle table */
732 if (CurrentPsp == SYSTEM_PSP) return FALSE;
733
734 /* Get a pointer to the handle table */
735 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
736 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
737
738 /* Make sure the old handle is open */
739 if (HandleTable[OldHandle] == 0xFF) return FALSE;
740
741 /* Check if the new handle is open */
742 if (HandleTable[NewHandle] != 0xFF)
743 {
744 /* Close it */
745 DosCloseHandle(NewHandle);
746 }
747
748 /* Increment the reference count of the SFT entry */
749 SftIndex = HandleTable[OldHandle];
750 DosSystemFileTable[SftIndex].RefCount++;
751
752 /* Make the new handle point to that SFT entry */
753 HandleTable[NewHandle] = SftIndex;
754
755 /* Return success */
756 return TRUE;
757 }
758
759
760
761
762
763
764
765 static BOOLEAN DosChangeDrive(BYTE Drive)
766 {
767 WCHAR DirectoryPath[DOS_CMDLINE_LENGTH];
768
769 /* Make sure the drive exists */
770 if (Drive > (LastDrive - 'A')) return FALSE;
771
772 /* Find the path to the new current directory */
773 swprintf(DirectoryPath, L"%c\\%S", Drive + 'A', CurrentDirectories[Drive]);
774
775 /* Change the current directory of the process */
776 if (!SetCurrentDirectory(DirectoryPath)) return FALSE;
777
778 /* Set the current drive */
779 CurrentDrive = Drive;
780
781 /* Return success */
782 return TRUE;
783 }
784
785 static BOOLEAN DosChangeDirectory(LPSTR Directory)
786 {
787 BYTE DriveNumber;
788 DWORD Attributes;
789 LPSTR Path;
790
791 /* Make sure the directory path is not too long */
792 if (strlen(Directory) >= DOS_DIR_LENGTH)
793 {
794 DosLastError = ERROR_PATH_NOT_FOUND;
795 return FALSE;
796 }
797
798 /* Get the drive number */
799 DriveNumber = Directory[0] - 'A';
800
801 /* Make sure the drive exists */
802 if (DriveNumber > (LastDrive - 'A'))
803 {
804 DosLastError = ERROR_PATH_NOT_FOUND;
805 return FALSE;
806 }
807
808 /* Get the file attributes */
809 Attributes = GetFileAttributesA(Directory);
810
811 /* Make sure the path exists and is a directory */
812 if ((Attributes == INVALID_FILE_ATTRIBUTES)
813 || !(Attributes & FILE_ATTRIBUTE_DIRECTORY))
814 {
815 DosLastError = ERROR_PATH_NOT_FOUND;
816 return FALSE;
817 }
818
819 /* Check if this is the current drive */
820 if (DriveNumber == CurrentDrive)
821 {
822 /* Change the directory */
823 if (!SetCurrentDirectoryA(Directory))
824 {
825 DosLastError = LOWORD(GetLastError());
826 return FALSE;
827 }
828 }
829
830 /* Get the directory part of the path */
831 Path = strchr(Directory, '\\');
832 if (Path != NULL)
833 {
834 /* Skip the backslash */
835 Path++;
836 }
837
838 /* Set the directory for the drive */
839 if (Path != NULL)
840 {
841 strncpy(CurrentDirectories[DriveNumber], Path, DOS_DIR_LENGTH);
842 }
843 else
844 {
845 CurrentDirectories[DriveNumber][0] = '\0';
846 }
847
848 /* Return success */
849 return TRUE;
850 }
851
852 /* PUBLIC FUNCTIONS ***********************************************************/
853
854 VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment)
855 {
856 PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment);
857 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
858
859 RtlZeroMemory(PspBlock, sizeof(*PspBlock));
860
861 /* Set the exit interrupt */
862 PspBlock->Exit[0] = 0xCD; // int 0x20
863 PspBlock->Exit[1] = 0x20;
864
865 /* Set the number of the last paragraph */
866 PspBlock->LastParagraph = PspSegment + ProgramSize - 1;
867
868 /* Save the interrupt vectors */
869 PspBlock->TerminateAddress = IntVecTable[0x22];
870 PspBlock->BreakAddress = IntVecTable[0x23];
871 PspBlock->CriticalAddress = IntVecTable[0x24];
872
873 /* Set the parent PSP */
874 PspBlock->ParentPsp = CurrentPsp;
875
876 /* Copy the parent handle table */
877 DosCopyHandleTable(PspBlock->HandleTable);
878
879 /* Set the environment block */
880 PspBlock->EnvBlock = Environment;
881
882 /* Set the handle table pointers to the internal handle table */
883 PspBlock->HandleTableSize = 20;
884 PspBlock->HandleTablePtr = MAKELONG(0x18, PspSegment);
885
886 /* Set the DOS version */
887 PspBlock->DosVersion = DOS_VERSION;
888
889 /* Set the far call opcodes */
890 PspBlock->FarCall[0] = 0xCD; // int 0x21
891 PspBlock->FarCall[1] = 0x21;
892 PspBlock->FarCall[2] = 0xCB; // retf
893
894 /* Set the command line */
895 PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
896 RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
897 PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
898 }
899
900 DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
901 IN LPCSTR ExecutablePath,
902 IN LPCSTR CommandLine,
903 IN LPCSTR Environment,
904 OUT PDWORD StackLocation OPTIONAL,
905 OUT PDWORD EntryPoint OPTIONAL)
906 {
907 DWORD Result = ERROR_SUCCESS;
908 HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
909 LPBYTE Address = NULL;
910 WORD Segment = 0;
911 WORD EnvBlock = 0;
912 WORD MaxAllocSize;
913 DWORD i, FileSize, ExeSize;
914 PIMAGE_DOS_HEADER Header;
915 PDWORD RelocationTable;
916 PWORD RelocWord;
917 LPSTR CmdLinePtr = (LPSTR)CommandLine;
918
919 DPRINT("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
920 LoadType,
921 ExecutablePath,
922 CommandLine,
923 Environment,
924 StackLocation,
925 EntryPoint);
926
927 if (LoadType == DOS_LOAD_OVERLAY)
928 {
929 DPRINT1("Overlay loading is not supported yet.\n");
930 return ERROR_NOT_SUPPORTED;
931 }
932
933 /* NULL-terminate the command line by removing the return carriage character */
934 while (*CmdLinePtr && *CmdLinePtr != '\r') CmdLinePtr++;
935 *CmdLinePtr = '\0';
936
937 /* Open a handle to the executable */
938 FileHandle = CreateFileA(ExecutablePath,
939 GENERIC_READ,
940 FILE_SHARE_READ,
941 NULL,
942 OPEN_EXISTING,
943 FILE_ATTRIBUTE_NORMAL,
944 NULL);
945 if (FileHandle == INVALID_HANDLE_VALUE)
946 {
947 Result = GetLastError();
948 goto Cleanup;
949 }
950
951 /* Get the file size */
952 FileSize = GetFileSize(FileHandle, NULL);
953
954 /* Create a mapping object for the file */
955 FileMapping = CreateFileMapping(FileHandle,
956 NULL,
957 PAGE_READONLY,
958 0,
959 0,
960 NULL);
961 if (FileMapping == NULL)
962 {
963 Result = GetLastError();
964 goto Cleanup;
965 }
966
967 /* Map the file into memory */
968 Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
969 if (Address == NULL)
970 {
971 Result = GetLastError();
972 goto Cleanup;
973 }
974
975 /* Copy the environment block to DOS memory */
976 EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath);
977 if (EnvBlock == 0)
978 {
979 Result = ERROR_NOT_ENOUGH_MEMORY;
980 goto Cleanup;
981 }
982
983 /* Check if this is an EXE file or a COM file */
984 if (Address[0] == 'M' && Address[1] == 'Z')
985 {
986 /* EXE file */
987
988 /* Get the MZ header */
989 Header = (PIMAGE_DOS_HEADER)Address;
990
991 /* Get the base size of the file, in paragraphs (rounded up) */
992 ExeSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
993
994 /* Add the PSP size, in paragraphs */
995 ExeSize += sizeof(DOS_PSP) >> 4;
996
997 /* Add the maximum size that should be allocated */
998 ExeSize += Header->e_maxalloc;
999
1000 /* Make sure it does not pass 0xFFFF */
1001 if (ExeSize > 0xFFFF) ExeSize = 0xFFFF;
1002
1003 /* Reduce the size one by one until the allocation is successful */
1004 for (i = Header->e_maxalloc; i >= Header->e_minalloc; i--, ExeSize--)
1005 {
1006 /* Try to allocate that much memory */
1007 Segment = DosAllocateMemory((WORD)ExeSize, NULL);
1008 if (Segment != 0) break;
1009 }
1010
1011 /* Check if at least the lowest allocation was successful */
1012 if (Segment == 0)
1013 {
1014 Result = DosLastError;
1015 goto Cleanup;
1016 }
1017
1018 /* Initialize the PSP */
1019 DosInitializePsp(Segment,
1020 CommandLine,
1021 (WORD)ExeSize,
1022 EnvBlock);
1023
1024 /* The process owns its own memory */
1025 DosChangeMemoryOwner(Segment, Segment);
1026 DosChangeMemoryOwner(EnvBlock, Segment);
1027
1028 /* Copy the program to Segment:0100 */
1029 RtlCopyMemory(SEG_OFF_TO_PTR(Segment, 0x100),
1030 Address + (Header->e_cparhdr << 4),
1031 min(FileSize - (Header->e_cparhdr << 4),
1032 (ExeSize << 4) - sizeof(DOS_PSP)));
1033
1034 /* Get the relocation table */
1035 RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
1036
1037 /* Perform relocations */
1038 for (i = 0; i < Header->e_crlc; i++)
1039 {
1040 /* Get a pointer to the word that needs to be patched */
1041 RelocWord = (PWORD)SEG_OFF_TO_PTR(Segment + HIWORD(RelocationTable[i]),
1042 0x100 + LOWORD(RelocationTable[i]));
1043
1044 /* Add the number of the EXE segment to it */
1045 *RelocWord += Segment + (sizeof(DOS_PSP) >> 4);
1046 }
1047
1048 if (LoadType == DOS_LOAD_AND_EXECUTE)
1049 {
1050 /* Set the initial segment registers */
1051 setDS(Segment);
1052 setES(Segment);
1053
1054 /* Set the stack to the location from the header */
1055 setSS(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss);
1056 setSP(Header->e_sp);
1057
1058 /* Execute */
1059 CurrentPsp = Segment;
1060 DiskTransferArea = MAKELONG(0x80, Segment);
1061 CpuExecute(Segment + Header->e_cs + (sizeof(DOS_PSP) >> 4),
1062 Header->e_ip);
1063 }
1064 }
1065 else
1066 {
1067 /* COM file */
1068
1069 /* Find the maximum amount of memory that can be allocated */
1070 DosAllocateMemory(0xFFFF, &MaxAllocSize);
1071
1072 /* Make sure it's enough for the whole program and the PSP */
1073 if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP)))
1074 {
1075 Result = ERROR_NOT_ENOUGH_MEMORY;
1076 goto Cleanup;
1077 }
1078
1079 /* Allocate all of it */
1080 Segment = DosAllocateMemory(MaxAllocSize, NULL);
1081 if (Segment == 0)
1082 {
1083 Result = DosLastError;
1084 goto Cleanup;
1085 }
1086
1087 /* The process owns its own memory */
1088 DosChangeMemoryOwner(Segment, Segment);
1089 DosChangeMemoryOwner(EnvBlock, Segment);
1090
1091 /* Copy the program to Segment:0100 */
1092 RtlCopyMemory(SEG_OFF_TO_PTR(Segment, 0x100),
1093 Address,
1094 FileSize);
1095
1096 /* Initialize the PSP */
1097 DosInitializePsp(Segment,
1098 CommandLine,
1099 MaxAllocSize,
1100 EnvBlock);
1101
1102 if (LoadType == DOS_LOAD_AND_EXECUTE)
1103 {
1104 /* Set the initial segment registers */
1105 setDS(Segment);
1106 setES(Segment);
1107
1108 /* Set the stack to the last word of the segment */
1109 setSS(Segment);
1110 setSP(0xFFFE);
1111
1112 /*
1113 * Set the value on the stack to 0, so that a near return
1114 * jumps to PSP:0000 which has the exit code.
1115 */
1116 *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0;
1117
1118 /* Execute */
1119 CurrentPsp = Segment;
1120 DiskTransferArea = MAKELONG(0x80, Segment);
1121 CpuExecute(Segment, 0x100);
1122 }
1123 }
1124
1125 Cleanup:
1126 if (Result != ERROR_SUCCESS)
1127 {
1128 /* It was not successful, cleanup the DOS memory */
1129 if (EnvBlock) DosFreeMemory(EnvBlock);
1130 if (Segment) DosFreeMemory(Segment);
1131 }
1132
1133 /* Unmap the file*/
1134 if (Address != NULL) UnmapViewOfFile(Address);
1135
1136 /* Close the file mapping object */
1137 if (FileMapping != NULL) CloseHandle(FileMapping);
1138
1139 /* Close the file handle */
1140 if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
1141
1142 return Result;
1143 }
1144
1145 DWORD DosStartProcess(IN LPCSTR ExecutablePath,
1146 IN LPCSTR CommandLine,
1147 IN LPCSTR Environment)
1148 {
1149 DWORD Result;
1150
1151 Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
1152 ExecutablePath,
1153 CommandLine,
1154 Environment,
1155 NULL,
1156 NULL);
1157
1158 if (Result != ERROR_SUCCESS) goto Quit;
1159
1160 /* Attach to the console */
1161 VidBiosAttachToConsole(); // FIXME: And in fact, attach the full NTVDM UI to the console
1162
1163 // HACK: Simulate a ENTER key release scancode on the PS/2 port because
1164 // some apps expect to read a key release scancode (> 0x80) when they
1165 // are started.
1166 IOWriteB(PS2_CONTROL_PORT, 0xD2); // Next write is for the first PS/2 port
1167 IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
1168
1169 /* Start simulation */
1170 SetEvent(VdmTaskEvent);
1171 CpuSimulate();
1172
1173 /* Detach from the console */
1174 VidBiosDetachFromConsole(); // FIXME: And in fact, detach the full NTVDM UI from the console
1175
1176 Quit:
1177 return Result;
1178 }
1179
1180 #ifndef STANDALONE
1181 WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
1182 LPCSTR ProgramName,
1183 PDOS_EXEC_PARAM_BLOCK Parameters)
1184 {
1185 DWORD Result;
1186 DWORD BinaryType;
1187 LPVOID Environment = NULL;
1188 VDM_COMMAND_INFO CommandInfo;
1189 CHAR CmdLine[MAX_PATH];
1190 CHAR AppName[MAX_PATH];
1191 CHAR PifFile[MAX_PATH];
1192 CHAR Desktop[MAX_PATH];
1193 CHAR Title[MAX_PATH];
1194 CHAR Env[MAX_PATH];
1195 STARTUPINFOA StartupInfo;
1196 PROCESS_INFORMATION ProcessInfo;
1197
1198 /* Get the binary type */
1199 if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
1200
1201 /* Did the caller specify an environment segment? */
1202 if (Parameters->Environment)
1203 {
1204 /* Yes, use it instead of the parent one */
1205 Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0);
1206 }
1207
1208 /* Set up the startup info structure */
1209 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
1210 StartupInfo.cb = sizeof(StartupInfo);
1211
1212 /* Create the process */
1213 if (!CreateProcessA(ProgramName,
1214 FAR_POINTER(Parameters->CommandLine),
1215 NULL,
1216 NULL,
1217 FALSE,
1218 0,
1219 Environment,
1220 NULL,
1221 &StartupInfo,
1222 &ProcessInfo))
1223 {
1224 return GetLastError();
1225 }
1226
1227 /* Check the type of the program */
1228 switch (BinaryType)
1229 {
1230 /* These are handled by NTVDM */
1231 case SCS_DOS_BINARY:
1232 case SCS_WOW_BINARY:
1233 {
1234 /* Clear the structure */
1235 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
1236
1237 /* Initialize the structure members */
1238 CommandInfo.TaskId = SessionId;
1239 CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
1240 CommandInfo.CmdLine = CmdLine;
1241 CommandInfo.CmdLen = sizeof(CmdLine);
1242 CommandInfo.AppName = AppName;
1243 CommandInfo.AppLen = sizeof(AppName);
1244 CommandInfo.PifFile = PifFile;
1245 CommandInfo.PifLen = sizeof(PifFile);
1246 CommandInfo.Desktop = Desktop;
1247 CommandInfo.DesktopLen = sizeof(Desktop);
1248 CommandInfo.Title = Title;
1249 CommandInfo.TitleLen = sizeof(Title);
1250 CommandInfo.Env = Env;
1251 CommandInfo.EnvLen = sizeof(Env);
1252
1253 /* Get the VDM command information */
1254 if (!GetNextVDMCommand(&CommandInfo))
1255 {
1256 /* Shouldn't happen */
1257 ASSERT(FALSE);
1258 }
1259
1260 /* Increment the re-entry count */
1261 CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
1262 GetNextVDMCommand(&CommandInfo);
1263
1264 /* Load the executable */
1265 Result = DosLoadExecutable(LoadType,
1266 AppName,
1267 CmdLine,
1268 Env,
1269 &Parameters->StackLocation,
1270 &Parameters->EntryPoint);
1271 if (Result != ERROR_SUCCESS)
1272 {
1273 DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
1274 // FIXME: Decrement the reenter count. Or, instead, just increment
1275 // the VDM reenter count *only* if this call succeeds...
1276 }
1277
1278 break;
1279 }
1280
1281 /* Not handled by NTVDM */
1282 default:
1283 {
1284 /* Wait for the process to finish executing */
1285 WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
1286 }
1287 }
1288
1289 /* Close the handles */
1290 CloseHandle(ProcessInfo.hProcess);
1291 CloseHandle(ProcessInfo.hThread);
1292
1293 return ERROR_SUCCESS;
1294 }
1295 #endif
1296
1297 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
1298 {
1299 WORD i;
1300 WORD McbSegment = FIRST_MCB_SEGMENT;
1301 PDOS_MCB CurrentMcb;
1302 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
1303 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
1304
1305 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
1306 Psp,
1307 ReturnCode);
1308
1309 /* Check if this PSP is it's own parent */
1310 if (PspBlock->ParentPsp == Psp) goto Done;
1311
1312 for (i = 0; i < PspBlock->HandleTableSize; i++)
1313 {
1314 /* Close the handle */
1315 DosCloseHandle(i);
1316 }
1317
1318 /* Free the memory used by the process */
1319 while (TRUE)
1320 {
1321 /* Get a pointer to the MCB */
1322 CurrentMcb = SEGMENT_TO_MCB(McbSegment);
1323
1324 /* Make sure the MCB is valid */
1325 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType !='Z') break;
1326
1327 /* If this block was allocated by the process, free it */
1328 if (CurrentMcb->OwnerPsp == Psp) DosFreeMemory(McbSegment + 1);
1329
1330 /* If this was the last block, quit */
1331 if (CurrentMcb->BlockType == 'Z') break;
1332
1333 /* Update the segment and continue */
1334 McbSegment += CurrentMcb->Size + 1;
1335 }
1336
1337 Done:
1338 /* Restore the interrupt vectors */
1339 IntVecTable[0x22] = PspBlock->TerminateAddress;
1340 IntVecTable[0x23] = PspBlock->BreakAddress;
1341 IntVecTable[0x24] = PspBlock->CriticalAddress;
1342
1343 /* Update the current PSP */
1344 if (Psp == CurrentPsp)
1345 {
1346 CurrentPsp = PspBlock->ParentPsp;
1347 if (CurrentPsp == SYSTEM_PSP)
1348 {
1349 ResetEvent(VdmTaskEvent);
1350 CpuUnsimulate();
1351 }
1352 }
1353
1354 #ifndef STANDALONE
1355 // FIXME: This is probably not the best way to do it
1356 /* Check if this was a nested DOS task */
1357 if (CurrentPsp != SYSTEM_PSP)
1358 {
1359 VDM_COMMAND_INFO CommandInfo;
1360
1361 /* Decrement the re-entry count */
1362 CommandInfo.TaskId = SessionId;
1363 CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
1364 GetNextVDMCommand(&CommandInfo);
1365
1366 /* Clear the structure */
1367 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
1368
1369 /* Update the VDM state of the task */
1370 CommandInfo.TaskId = SessionId;
1371 CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
1372 GetNextVDMCommand(&CommandInfo);
1373 }
1374 #endif
1375
1376 /* Save the return code - Normal termination */
1377 DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
1378
1379 /* Return control to the parent process */
1380 CpuExecute(HIWORD(PspBlock->TerminateAddress),
1381 LOWORD(PspBlock->TerminateAddress));
1382 }
1383
1384 BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
1385 {
1386 HANDLE Handle = DosGetRealHandle(FileHandle);
1387
1388 if (Handle == INVALID_HANDLE_VALUE)
1389 {
1390 /* Doesn't exist */
1391 DosLastError = ERROR_FILE_NOT_FOUND;
1392 return FALSE;
1393 }
1394
1395 switch (ControlCode)
1396 {
1397 /* Get Device Information */
1398 case 0x00:
1399 {
1400 WORD InfoWord = 0;
1401
1402 /*
1403 * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
1404 * for a list of possible flags.
1405 */
1406
1407 if (Handle == DosSystemFileTable[DOS_INPUT_HANDLE].Handle)
1408 {
1409 /* Console input */
1410 InfoWord |= 1 << 0;
1411
1412 /* It is a device */
1413 InfoWord |= 1 << 7;
1414 }
1415 else if (Handle == DosSystemFileTable[DOS_OUTPUT_HANDLE].Handle)
1416 {
1417 /* Console output */
1418 InfoWord |= 1 << 1;
1419
1420 /* It is a device */
1421 InfoWord |= 1 << 7;
1422 }
1423
1424 /* Return the device information word */
1425 setDX(InfoWord);
1426 return TRUE;
1427 }
1428
1429 /* Unsupported control code */
1430 default:
1431 {
1432 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode);
1433
1434 DosLastError = ERROR_INVALID_PARAMETER;
1435 return FALSE;
1436 }
1437 }
1438 }
1439
1440 VOID WINAPI DosInt20h(LPWORD Stack)
1441 {
1442 /* This is the exit interrupt */
1443 DosTerminateProcess(Stack[STACK_CS], 0);
1444 }
1445
1446 VOID WINAPI DosInt21h(LPWORD Stack)
1447 {
1448 BYTE Character;
1449 SYSTEMTIME SystemTime;
1450 PCHAR String;
1451 PDOS_INPUT_BUFFER InputBuffer;
1452
1453 /* Check the value in the AH register */
1454 switch (getAH())
1455 {
1456 /* Terminate Program */
1457 case 0x00:
1458 {
1459 DosTerminateProcess(Stack[STACK_CS], 0);
1460 break;
1461 }
1462
1463 /* Read Character from STDIN with Echo */
1464 case 0x01:
1465 {
1466 DPRINT("INT 21h, AH = 01h\n");
1467
1468 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
1469 DoEcho = TRUE;
1470 Character = DosReadCharacter(DOS_INPUT_HANDLE);
1471 DoEcho = FALSE;
1472
1473 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
1474 // Check also Ctrl-P and set echo-to-printer flag.
1475 // Ctrl-Z is not interpreted.
1476
1477 setAL(Character);
1478 break;
1479 }
1480
1481 /* Write Character to STDOUT */
1482 case 0x02:
1483 {
1484 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1485 Character = getDL();
1486 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
1487
1488 /*
1489 * We return the output character (DOS 2.1+).
1490 * Also, if we're going to output a TAB, then
1491 * don't return a TAB but a SPACE instead.
1492 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
1493 * for more information.
1494 */
1495 setAL(Character == '\t' ? ' ' : Character);
1496 break;
1497 }
1498
1499 /* Read Character from STDAUX */
1500 case 0x03:
1501 {
1502 // FIXME: Really read it from STDAUX!
1503 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
1504 // setAL(DosReadCharacter());
1505 break;
1506 }
1507
1508 /* Write Character to STDAUX */
1509 case 0x04:
1510 {
1511 // FIXME: Really write it to STDAUX!
1512 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
1513 // DosPrintCharacter(getDL());
1514 break;
1515 }
1516
1517 /* Write Character to Printer */
1518 case 0x05:
1519 {
1520 // FIXME: Really write it to printer!
1521 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
1522 DPRINT1("0x%p\n", getDL());
1523 DPRINT1("\n\n-----------\n\n");
1524 break;
1525 }
1526
1527 /* Direct Console I/O */
1528 case 0x06:
1529 {
1530 Character = getDL();
1531
1532 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1533
1534 if (Character != 0xFF)
1535 {
1536 /* Output */
1537 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
1538
1539 /*
1540 * We return the output character (DOS 2.1+).
1541 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
1542 * for more information.
1543 */
1544 setAL(Character);
1545 }
1546 else
1547 {
1548 /* Input */
1549 if (DosCheckInput())
1550 {
1551 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
1552 setAL(DosReadCharacter(DOS_INPUT_HANDLE));
1553 }
1554 else
1555 {
1556 /* No character available */
1557 Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF;
1558 setAL(0x00);
1559 }
1560 }
1561
1562 break;
1563 }
1564
1565 /* Character Input without Echo */
1566 case 0x07:
1567 case 0x08:
1568 {
1569 DPRINT("Char input without echo\n");
1570
1571 // FIXME: Under DOS 2+, input handle may be redirected!!!!
1572 Character = DosReadCharacter(DOS_INPUT_HANDLE);
1573
1574 // FIXME: For 0x07, do not check Ctrl-C/Break.
1575 // For 0x08, do check those control sequences and if needed,
1576 // call INT 0x23.
1577
1578 // /* Let the BOP repeat if needed */
1579 // if (getCF()) break;
1580
1581 setAL(Character);
1582 break;
1583 }
1584
1585 /* Write string to STDOUT */
1586 case 0x09:
1587 {
1588 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
1589
1590 while (*String != '$')
1591 {
1592 DosPrintCharacter(DOS_OUTPUT_HANDLE, *String);
1593 String++;
1594 }
1595
1596 /*
1597 * We return the terminating character (DOS 2.1+).
1598 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
1599 * for more information.
1600 */
1601 setAL('$'); // *String
1602 break;
1603 }
1604
1605 /* Read Buffered Input */
1606 case 0x0A:
1607 {
1608 WORD Count = 0;
1609 InputBuffer = (PDOS_INPUT_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
1610
1611 DPRINT("Read Buffered Input\n");
1612
1613 while (Count < InputBuffer->MaxLength)
1614 {
1615 // FIXME!! This function should interpret backspaces etc...
1616
1617 /* Try to read a character (wait) */
1618 Character = DosReadCharacter(DOS_INPUT_HANDLE);
1619
1620 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
1621
1622 /* Echo the character and append it to the buffer */
1623 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
1624 InputBuffer->Buffer[Count] = Character;
1625
1626 Count++; /* Carriage returns are also counted */
1627
1628 if (Character == '\r') break;
1629 }
1630
1631 /* Update the length */
1632 InputBuffer->Length = Count;
1633
1634 break;
1635 }
1636
1637 /* Get STDIN Status */
1638 case 0x0B:
1639 {
1640 setAL(DosCheckInput() ? 0xFF : 0x00);
1641 break;
1642 }
1643
1644 /* Flush Buffer and Read STDIN */
1645 case 0x0C:
1646 {
1647 BYTE InputFunction = getAL();
1648
1649 /* Flush STDIN buffer */
1650 DosFlushFileBuffers(DOS_INPUT_HANDLE);
1651
1652 /*
1653 * If the input function number contained in AL is valid, i.e.
1654 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
1655 * recursively with AL == AH.
1656 */
1657 if (InputFunction == 0x01 || InputFunction == 0x06 ||
1658 InputFunction == 0x07 || InputFunction == 0x08 ||
1659 InputFunction == 0x0A)
1660 {
1661 /* Call ourselves recursively */
1662 setAH(InputFunction);
1663 DosInt21h(Stack);
1664 }
1665 break;
1666 }
1667
1668 /* Disk Reset */
1669 case 0x0D:
1670 {
1671 PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
1672
1673 // TODO: Flush what's needed.
1674 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
1675
1676 /* Clear CF in DOS 6 only */
1677 if (PspBlock->DosVersion == 0x0006)
1678 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1679
1680 break;
1681 }
1682
1683 /* Set Default Drive */
1684 case 0x0E:
1685 {
1686 DosChangeDrive(getDL());
1687 setAL(LastDrive - 'A' + 1);
1688 break;
1689 }
1690
1691 /* NULL Function for CP/M Compatibility */
1692 case 0x18:
1693 {
1694 /*
1695 * This function corresponds to the CP/M BDOS function
1696 * "get bit map of logged drives", which is meaningless
1697 * under MS-DOS.
1698 *
1699 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
1700 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
1701 * for more information.
1702 */
1703 setAL(0x00);
1704 break;
1705 }
1706
1707 /* Get Default Drive */
1708 case 0x19:
1709 {
1710 setAL(CurrentDrive);
1711 break;
1712 }
1713
1714 /* Set Disk Transfer Area */
1715 case 0x1A:
1716 {
1717 DiskTransferArea = MAKELONG(getDX(), getDS());
1718 break;
1719 }
1720
1721 /* NULL Function for CP/M Compatibility */
1722 case 0x1D:
1723 case 0x1E:
1724 {
1725 /*
1726 * Function 0x1D corresponds to the CP/M BDOS function
1727 * "get bit map of read-only drives", which is meaningless
1728 * under MS-DOS.
1729 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
1730 * for more information.
1731 *
1732 * Function 0x1E corresponds to the CP/M BDOS function
1733 * "set file attributes", which was meaningless under MS-DOS 1.x.
1734 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
1735 * for more information.
1736 */
1737 setAL(0x00);
1738 break;
1739 }
1740
1741 /* NULL Function for CP/M Compatibility */
1742 case 0x20:
1743 {
1744 /*
1745 * This function corresponds to the CP/M BDOS function
1746 * "get/set default user (sublibrary) number", which is meaningless
1747 * under MS-DOS.
1748 *
1749 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
1750 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
1751 * for more information.
1752 */
1753 setAL(0x00);
1754 break;
1755 }
1756
1757 /* Set Interrupt Vector */
1758 case 0x25:
1759 {
1760 ULONG FarPointer = MAKELONG(getDX(), getDS());
1761 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
1762 getAL(), HIWORD(FarPointer), LOWORD(FarPointer));
1763
1764 /* Write the new far pointer to the IDT */
1765 ((PULONG)BaseAddress)[getAL()] = FarPointer;
1766 break;
1767 }
1768
1769 /* Create New PSP */
1770 case 0x26:
1771 {
1772 DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
1773 break;
1774 }
1775
1776 /* Get System Date */
1777 case 0x2A:
1778 {
1779 GetLocalTime(&SystemTime);
1780 setCX(SystemTime.wYear);
1781 setDX(MAKEWORD(SystemTime.wDay, SystemTime.wMonth));
1782 setAL(SystemTime.wDayOfWeek);
1783 break;
1784 }
1785
1786 /* Set System Date */
1787 case 0x2B:
1788 {
1789 GetLocalTime(&SystemTime);
1790 SystemTime.wYear = getCX();
1791 SystemTime.wMonth = getDH();
1792 SystemTime.wDay = getDL();
1793
1794 /* Return success or failure */
1795 setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF);
1796 break;
1797 }
1798
1799 /* Get System Time */
1800 case 0x2C:
1801 {
1802 GetLocalTime(&SystemTime);
1803 setCX(MAKEWORD(SystemTime.wMinute, SystemTime.wHour));
1804 setDX(MAKEWORD(SystemTime.wMilliseconds / 10, SystemTime.wSecond));
1805 break;
1806 }
1807
1808 /* Set System Time */
1809 case 0x2D:
1810 {
1811 GetLocalTime(&SystemTime);
1812 SystemTime.wHour = getCH();
1813 SystemTime.wMinute = getCL();
1814 SystemTime.wSecond = getDH();
1815 SystemTime.wMilliseconds = getDL() * 10; // In hundredths of seconds
1816
1817 /* Return success or failure */
1818 setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF);
1819 break;
1820 }
1821
1822 /* Get Disk Transfer Area */
1823 case 0x2F:
1824 {
1825 setES(HIWORD(DiskTransferArea));
1826 setBX(LOWORD(DiskTransferArea));
1827 break;
1828 }
1829
1830 /* Get DOS Version */
1831 case 0x30:
1832 {
1833 PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
1834
1835 /*
1836 * DOS 2+ - GET DOS VERSION
1837 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
1838 * for more information.
1839 */
1840
1841 if (LOBYTE(PspBlock->DosVersion) < 5 || getAL() == 0x00)
1842 {
1843 /*
1844 * Return DOS OEM number:
1845 * 0x00 for IBM PC-DOS
1846 * 0x02 for packaged MS-DOS
1847 * 0xFF for NT DOS
1848 */
1849 setBH(0xFF);
1850 }
1851
1852 if (LOBYTE(PspBlock->DosVersion) >= 5 && getAL() == 0x01)
1853 {
1854 /*
1855 * Return version flag:
1856 * 1 << 3 if DOS is in ROM,
1857 * 0 (reserved) if not.
1858 */
1859 setBH(0x00);
1860 }
1861
1862 /* Return DOS 24-bit user serial number in BL:CX */
1863 setBL(0x00);
1864 setCX(0x0000);
1865
1866 /*
1867 * Return DOS version: Minor:Major in AH:AL
1868 * The Windows NT DOS box returns version 5.00, subject to SETVER.
1869 */
1870 setAX(PspBlock->DosVersion);
1871
1872 break;
1873 }
1874
1875 /* Extended functionalities */
1876 case 0x33:
1877 {
1878 if (getAL() == 0x06)
1879 {
1880 /*
1881 * DOS 5+ - GET TRUE VERSION NUMBER
1882 * This function always returns the true version number, unlike
1883 * AH=30h, whose return value may be changed with SETVER.
1884 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
1885 * for more information.
1886 */
1887
1888 /*
1889 * Return the true DOS version: Minor:Major in BH:BL
1890 * The Windows NT DOS box returns BX=3205h (version 5.50).
1891 */
1892 setBX(NTDOS_VERSION);
1893
1894 /* DOS revision 0 */
1895 setDL(0x00);
1896
1897 /* Unpatched DOS */
1898 setDH(0x00);
1899 }
1900 // else
1901 // {
1902 // /* Invalid subfunction */
1903 // setAL(0xFF);
1904 // }
1905
1906 break;
1907 }
1908
1909 /* Get Interrupt Vector */
1910 case 0x35:
1911 {
1912 DWORD FarPointer = ((PDWORD)BaseAddress)[getAL()];
1913
1914 /* Read the address from the IDT into ES:BX */
1915 setES(HIWORD(FarPointer));
1916 setBX(LOWORD(FarPointer));
1917 break;
1918 }
1919
1920 /* SWITCH character - AVAILDEV */
1921 case 0x37:
1922 {
1923 if (getAL() == 0x00)
1924 {
1925 /*
1926 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
1927 * This setting is ignored by MS-DOS 4.0+.
1928 * MS-DOS 5+ always return AL=00h/DL=2Fh.
1929 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
1930 * for more information.
1931 */
1932 setDL('/');
1933 setAL(0x00);
1934 }
1935 else if (getAL() == 0x01)
1936 {
1937 /*
1938 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
1939 * This setting is ignored by MS-DOS 5+.
1940 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
1941 * for more information.
1942 */
1943 // getDL();
1944 setAL(0xFF);
1945 }
1946 else if (getAL() == 0x02)
1947 {
1948 /*
1949 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1950 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1951 * for more information.
1952 */
1953 // setDL();
1954 setAL(0xFF);
1955 }
1956 else if (getAL() == 0x03)
1957 {
1958 /*
1959 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1960 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1961 * for more information.
1962 */
1963 // getDL();
1964 setAL(0xFF);
1965 }
1966 else
1967 {
1968 /* Invalid subfunction */
1969 setAL(0xFF);
1970 }
1971
1972 break;
1973 }
1974
1975 /* Create Directory */
1976 case 0x39:
1977 {
1978 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
1979
1980 if (CreateDirectoryA(String, NULL))
1981 {
1982 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1983 }
1984 else
1985 {
1986 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1987 setAX(LOWORD(GetLastError()));
1988 }
1989
1990 break;
1991 }
1992
1993 /* Remove Directory */
1994 case 0x3A:
1995 {
1996 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
1997
1998 if (RemoveDirectoryA(String))
1999 {
2000 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2001 }
2002 else
2003 {
2004 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2005 setAX(LOWORD(GetLastError()));
2006 }
2007
2008 break;
2009 }
2010
2011 /* Set Current Directory */
2012 case 0x3B:
2013 {
2014 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
2015
2016 if (DosChangeDirectory(String))
2017 {
2018 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2019 }
2020 else
2021 {
2022 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2023 setAX(DosLastError);
2024 }
2025
2026 break;
2027 }
2028
2029 /* Create or Truncate File */
2030 case 0x3C:
2031 {
2032 WORD FileHandle;
2033 WORD ErrorCode = DosCreateFile(&FileHandle,
2034 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
2035 CREATE_ALWAYS,
2036 getCX());
2037
2038 if (ErrorCode == ERROR_SUCCESS)
2039 {
2040 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2041 setAX(FileHandle);
2042 }
2043 else
2044 {
2045 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2046 setAX(ErrorCode);
2047 }
2048
2049 break;
2050 }
2051
2052 /* Open File */
2053 case 0x3D:
2054 {
2055 WORD FileHandle;
2056 WORD ErrorCode = DosOpenFile(&FileHandle,
2057 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
2058 getAL());
2059
2060 if (ErrorCode == ERROR_SUCCESS)
2061 {
2062 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2063 setAX(FileHandle);
2064 }
2065 else
2066 {
2067 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2068 setAX(ErrorCode);
2069 }
2070
2071 break;
2072 }
2073
2074 /* Close File */
2075 case 0x3E:
2076 {
2077 if (DosCloseHandle(getBX()))
2078 {
2079 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2080 }
2081 else
2082 {
2083 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2084 setAX(ERROR_INVALID_HANDLE);
2085 }
2086
2087 break;
2088 }
2089
2090 /* Read from File or Device */
2091 case 0x3F:
2092 {
2093 WORD BytesRead = 0;
2094 WORD ErrorCode;
2095
2096 DPRINT("INT 21h, AH = 3Fh\n");
2097
2098 DoEcho = TRUE;
2099 ErrorCode = DosReadFile(getBX(),
2100 SEG_OFF_TO_PTR(getDS(), getDX()),
2101 getCX(),
2102 &BytesRead);
2103 DoEcho = FALSE;
2104
2105 if (ErrorCode == ERROR_SUCCESS)
2106 {
2107 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2108 setAX(BytesRead);
2109 }
2110 else if (ErrorCode != ERROR_NOT_READY)
2111 {
2112 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2113 setAX(ErrorCode);
2114 }
2115
2116 break;
2117 }
2118
2119 /* Write to File or Device */
2120 case 0x40:
2121 {
2122 WORD BytesWritten = 0;
2123 WORD ErrorCode = DosWriteFile(getBX(),
2124 SEG_OFF_TO_PTR(getDS(), getDX()),
2125 getCX(),
2126 &BytesWritten);
2127
2128 if (ErrorCode == ERROR_SUCCESS)
2129 {
2130 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2131 setAX(BytesWritten);
2132 }
2133 else
2134 {
2135 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2136 setAX(ErrorCode);
2137 }
2138
2139 break;
2140 }
2141
2142 /* Delete File */
2143 case 0x41:
2144 {
2145 LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
2146
2147 if (demFileDelete(FileName) == ERROR_SUCCESS)
2148 {
2149 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2150 /*
2151 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
2152 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
2153 */
2154 setAL(FileName[0] - 'A');
2155 }
2156 else
2157 {
2158 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2159 setAX(GetLastError());
2160 }
2161
2162 break;
2163 }
2164
2165 /* Seek File */
2166 case 0x42:
2167 {
2168 DWORD NewLocation;
2169 WORD ErrorCode = DosSeekFile(getBX(),
2170 MAKELONG(getDX(), getCX()),
2171 getAL(),
2172 &NewLocation);
2173
2174 if (ErrorCode == ERROR_SUCCESS)
2175 {
2176 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2177
2178 /* Return the new offset in DX:AX */
2179 setDX(HIWORD(NewLocation));
2180 setAX(LOWORD(NewLocation));
2181 }
2182 else
2183 {
2184 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2185 setAX(ErrorCode);
2186 }
2187
2188 break;
2189 }
2190
2191 /* Get/Set File Attributes */
2192 case 0x43:
2193 {
2194 DWORD Attributes;
2195 LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
2196
2197 if (getAL() == 0x00)
2198 {
2199 /* Get the attributes */
2200 Attributes = GetFileAttributesA(FileName);
2201
2202 /* Check if it failed */
2203 if (Attributes == INVALID_FILE_ATTRIBUTES)
2204 {
2205 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2206 setAX(GetLastError());
2207 }
2208 else
2209 {
2210 /* Return the attributes that DOS can understand */
2211 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2212 setCX(Attributes & 0x00FF);
2213 }
2214 }
2215 else if (getAL() == 0x01)
2216 {
2217 /* Try to set the attributes */
2218 if (SetFileAttributesA(FileName, getCL()))
2219 {
2220 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2221 }
2222 else
2223 {
2224 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2225 setAX(GetLastError());
2226 }
2227 }
2228 else
2229 {
2230 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2231 setAX(ERROR_INVALID_FUNCTION);
2232 }
2233
2234 break;
2235 }
2236
2237 /* IOCTL */
2238 case 0x44:
2239 {
2240 if (DosHandleIoctl(getAL(), getBX()))
2241 {
2242 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2243 }
2244 else
2245 {
2246 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2247 setAX(DosLastError);
2248 }
2249
2250 break;
2251 }
2252
2253 /* Duplicate Handle */
2254 case 0x45:
2255 {
2256 WORD NewHandle;
2257 HANDLE Handle = DosGetRealHandle(getBX());
2258
2259 if (Handle == INVALID_HANDLE_VALUE)
2260 {
2261 /* The handle is invalid */
2262 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2263 setAX(ERROR_INVALID_HANDLE);
2264 break;
2265 }
2266
2267 /* Open a new handle to the same entry */
2268 NewHandle = DosOpenHandle(Handle);
2269
2270 if (NewHandle == INVALID_DOS_HANDLE)
2271 {
2272 /* Too many files open */
2273 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2274 setAX(ERROR_TOO_MANY_OPEN_FILES);
2275 break;
2276 }
2277
2278 /* Return the result */
2279 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2280 setAX(NewHandle);
2281 break;
2282 }
2283
2284 /* Force Duplicate Handle */
2285 case 0x46:
2286 {
2287 if (DosDuplicateHandle(getBX(), getCX()))
2288 {
2289 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2290 }
2291 else
2292 {
2293 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2294 setAX(ERROR_INVALID_HANDLE);
2295 }
2296
2297 break;
2298 }
2299
2300 /* Get Current Directory */
2301 case 0x47:
2302 {
2303 BYTE DriveNumber = getDL();
2304 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI());
2305
2306 /* Get the real drive number */
2307 if (DriveNumber == 0)
2308 {
2309 DriveNumber = CurrentDrive;
2310 }
2311 else
2312 {
2313 /* Decrement DriveNumber since it was 1-based */
2314 DriveNumber--;
2315 }
2316
2317 if (DriveNumber <= LastDrive - 'A')
2318 {
2319 /*
2320 * Copy the current directory into the target buffer.
2321 * It doesn't contain the drive letter and the backslash.
2322 */
2323 strncpy(String, CurrentDirectories[DriveNumber], DOS_DIR_LENGTH);
2324 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2325 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
2326 }
2327 else
2328 {
2329 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2330 setAX(ERROR_INVALID_DRIVE);
2331 }
2332
2333 break;
2334 }
2335
2336 /* Allocate Memory */
2337 case 0x48:
2338 {
2339 WORD MaxAvailable = 0;
2340 WORD Segment = DosAllocateMemory(getBX(), &MaxAvailable);
2341
2342 if (Segment != 0)
2343 {
2344 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2345 setAX(Segment);
2346 }
2347 else
2348 {
2349 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2350 setAX(DosLastError);
2351 setBX(MaxAvailable);
2352 }
2353
2354 break;
2355 }
2356
2357 /* Free Memory */
2358 case 0x49:
2359 {
2360 if (DosFreeMemory(getES()))
2361 {
2362 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2363 }
2364 else
2365 {
2366 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2367 setAX(ERROR_ARENA_TRASHED);
2368 }
2369
2370 break;
2371 }
2372
2373 /* Resize Memory Block */
2374 case 0x4A:
2375 {
2376 WORD Size;
2377
2378 if (DosResizeMemory(getES(), getBX(), &Size))
2379 {
2380 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2381 }
2382 else
2383 {
2384 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2385 setAX(DosLastError);
2386 setBX(Size);
2387 }
2388
2389 break;
2390 }
2391
2392 #ifndef STANDALONE
2393 /* Execute */
2394 case 0x4B:
2395 {
2396 DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)getAL();
2397 LPSTR ProgramName = SEG_OFF_TO_PTR(getDS(), getDX());
2398 PDOS_EXEC_PARAM_BLOCK ParamBlock = SEG_OFF_TO_PTR(getES(), getBX());
2399 WORD ErrorCode = DosCreateProcess(LoadType, ProgramName, ParamBlock);
2400
2401 if (ErrorCode == ERROR_SUCCESS)
2402 {
2403 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2404 }
2405 else
2406 {
2407 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2408 setAX(ErrorCode);
2409 }
2410
2411 break;
2412 }
2413 #endif
2414
2415 /* Terminate With Return Code */
2416 case 0x4C:
2417 {
2418 DosTerminateProcess(CurrentPsp, getAL());
2419 break;
2420 }
2421
2422 /* Get Return Code (ERRORLEVEL) */
2423 case 0x4D:
2424 {
2425 /*
2426 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
2427 * DosErrorLevel is cleared after being read by this function.
2428 */
2429 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2430 setAX(DosErrorLevel);
2431 DosErrorLevel = 0x0000; // Clear it
2432 break;
2433 }
2434
2435 /* Find First File */
2436 case 0x4E:
2437 {
2438 WORD Result = (WORD)demFileFindFirst(FAR_POINTER(DiskTransferArea),
2439 SEG_OFF_TO_PTR(getDS(), getDX()),
2440 getCX());
2441
2442 setAX(Result);
2443
2444 if (Result == ERROR_SUCCESS)
2445 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2446 else
2447 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2448
2449 break;
2450 }
2451
2452 /* Find Next File */
2453 case 0x4F:
2454 {
2455 WORD Result = (WORD)demFileFindNext(FAR_POINTER(DiskTransferArea));
2456
2457 setAX(Result);
2458
2459 if (Result == ERROR_SUCCESS)
2460 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2461 else
2462 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2463
2464 break;
2465 }
2466
2467 /* Internal - Set Current Process ID (Set PSP Address) */
2468 case 0x50:
2469 {
2470 // FIXME: Is it really what it's done ??
2471 CurrentPsp = getBX();
2472 break;
2473 }
2474
2475 /* Internal - Get Current Process ID (Get PSP Address) */
2476 case 0x51:
2477 /* Get Current PSP Address */
2478 case 0x62:
2479 {
2480 /*
2481 * Undocumented AH=51h is identical to the documented AH=62h.
2482 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
2483 * and http://www.ctyme.com/intr/rb-3140.htm
2484 * for more information.
2485 */
2486 setBX(CurrentPsp);
2487 break;
2488 }
2489
2490 /* Internal - Get "List of lists" (SYSVARS) */
2491 case 0x52:
2492 {
2493 /*
2494 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
2495 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
2496 * for more information.
2497 */
2498
2499 /* Return the DOS "list of lists" in ES:BX */
2500 setES(0x0000);
2501 setBX(0x0000);
2502
2503 DisplayMessage(L"Required for AARD code, do you remember? :P");
2504 break;
2505 }
2506
2507 /* Rename File */
2508 case 0x56:
2509 {
2510 LPSTR ExistingFileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
2511 LPSTR NewFileName = (LPSTR)SEG_OFF_TO_PTR(getES(), getDI());
2512
2513 /*
2514 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
2515 * for more information.
2516 */
2517
2518 if (MoveFileA(ExistingFileName, NewFileName))
2519 {
2520 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2521 }
2522 else
2523 {
2524 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2525 setAX(GetLastError());
2526 }
2527
2528 break;
2529 }
2530
2531 /* Get/Set Memory Management Options */
2532 case 0x58:
2533 {
2534 if (getAL() == 0x00)
2535 {
2536 /* Get allocation strategy */
2537 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2538 setAX(DosAllocStrategy);
2539 }
2540 else if (getAL() == 0x01)
2541 {
2542 /* Set allocation strategy */
2543
2544 if ((getBL() & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
2545 == (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
2546 {
2547 /* Can't set both */
2548 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2549 setAX(ERROR_INVALID_PARAMETER);
2550 break;
2551 }
2552
2553 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT)
2554 {
2555 /* Invalid allocation strategy */
2556 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2557 setAX(ERROR_INVALID_PARAMETER);
2558 break;
2559 }
2560
2561 DosAllocStrategy = getBL();
2562 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2563 }
2564 else if (getAL() == 0x02)
2565 {
2566 /* Get UMB link state */
2567 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2568 setAL(DosUmbLinked ? 0x01 : 0x00);
2569 }
2570 else if (getAL() == 0x03)
2571 {
2572 /* Set UMB link state */
2573 if (getBX()) DosLinkUmb();
2574 else DosUnlinkUmb();
2575 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2576 }
2577 else
2578 {
2579 /* Invalid or unsupported function */
2580 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2581 setAX(ERROR_INVALID_FUNCTION);
2582 }
2583
2584 break;
2585 }
2586
2587 /* Get Extended Error Information */
2588 case 0x59:
2589 {
2590 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
2591 getBX());
2592 break;
2593 }
2594
2595 /* Create Temporary File */
2596 case 0x5A:
2597 {
2598 LPSTR PathName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
2599 LPSTR FileName = PathName; // The buffer for the path and the full file name is the same.
2600 UINT uRetVal;
2601 WORD FileHandle;
2602 WORD ErrorCode;
2603
2604 /*
2605 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
2606 * for more information.
2607 */
2608
2609 // FIXME: Check for buffer validity?
2610 // It should be a ASCIZ path ending with a '\' + 13 zero bytes
2611 // to receive the generated filename.
2612
2613 /* First create the temporary file */
2614 uRetVal = GetTempFileNameA(PathName, NULL, 0, FileName);
2615 if (uRetVal == 0)
2616 {
2617 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2618 setAX(GetLastError());
2619 break;
2620 }
2621
2622 /* Now try to open it in read/write access */
2623 ErrorCode = DosOpenFile(&FileHandle, FileName, 2);
2624 if (ErrorCode == ERROR_SUCCESS)
2625 {
2626 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2627 setAX(FileHandle);
2628 }
2629 else
2630 {
2631 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2632 setAX(ErrorCode);
2633 }
2634
2635 break;
2636 }
2637
2638 /* Create New File */
2639 case 0x5B:
2640 {
2641 WORD FileHandle;
2642 WORD ErrorCode = DosCreateFile(&FileHandle,
2643 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
2644 CREATE_NEW,
2645 getCX());
2646
2647 if (ErrorCode == ERROR_SUCCESS)
2648 {
2649 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2650 setAX(FileHandle);
2651 }
2652 else
2653 {
2654 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2655 setAX(ErrorCode);
2656 }
2657
2658 break;
2659 }
2660
2661 /* Lock/Unlock Region of File */
2662 case 0x5C:
2663 {
2664 HANDLE Handle = DosGetRealHandle(getBX());
2665
2666 if (Handle == INVALID_HANDLE_VALUE)
2667 {
2668 /* The handle is invalid */
2669 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2670 setAX(ERROR_INVALID_HANDLE);
2671 break;
2672 }
2673
2674 if (getAL() == 0x00)
2675 {
2676 /* Lock region of file */
2677 if (LockFile(Handle,
2678 MAKELONG(getCX(), getDX()), 0,
2679 MAKELONG(getSI(), getDI()), 0))
2680 {
2681 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2682 }
2683 else
2684 {
2685 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2686 setAX(GetLastError());
2687 }
2688 }
2689 else if (getAL() == 0x01)
2690 {
2691 /* Unlock region of file */
2692 if (UnlockFile(Handle,
2693 MAKELONG(getCX(), getDX()), 0,
2694 MAKELONG(getSI(), getDI()), 0))
2695 {
2696 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2697 }
2698 else
2699 {
2700 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2701 setAX(GetLastError());
2702 }
2703 }
2704 else
2705 {
2706 /* Invalid subfunction */
2707 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2708 setAX(ERROR_INVALID_FUNCTION);
2709 }
2710
2711 break;
2712 }
2713
2714 /* Canonicalize File Name or Path */
2715 case 0x60:
2716 {
2717 /*
2718 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
2719 * for more information.
2720 */
2721
2722 /*
2723 * We suppose that the DOS app gave to us a valid
2724 * 128-byte long buffer for the canonicalized name.
2725 */
2726 DWORD dwRetVal = GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
2727 128,
2728 SEG_OFF_TO_PTR(getES(), getDI()),
2729 NULL);
2730 if (dwRetVal == 0)
2731 {
2732 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2733 setAX(GetLastError());
2734 }
2735 else
2736 {
2737 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2738 setAX(0x0000);
2739 }
2740
2741 // FIXME: Convert the full path name into short version.
2742 // We cannot reliably use GetShortPathName, because it fails
2743 // if the path name given doesn't exist. However this DOS
2744 // function AH=60h should be able to work even for non-existing
2745 // path and file names.
2746
2747 break;
2748 }
2749
2750 /* Set Handle Count */
2751 case 0x67:
2752 {
2753 if (!DosResizeHandleTable(getBX()))
2754 {
2755 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2756 setAX(DosLastError);
2757 }
2758 else Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2759
2760 break;
2761 }
2762
2763 /* Commit File */
2764 case 0x68:
2765 case 0x6A:
2766 {
2767 /*
2768 * Function 6Ah is identical to function 68h,
2769 * and sets AH to 68h if success.
2770 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
2771 * for more information.
2772 */
2773 setAH(0x68);
2774
2775 if (DosFlushFileBuffers(getBX()))
2776 {
2777 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2778 }
2779 else
2780 {
2781 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2782 setAX(GetLastError());
2783 }
2784
2785 break;
2786 }
2787
2788 /* Extended Open/Create */
2789 case 0x6C:
2790 {
2791 WORD FileHandle;
2792 WORD CreationStatus;
2793 WORD ErrorCode;
2794
2795 /* Check for AL == 00 */
2796 if (getAL() != 0x00)
2797 {
2798 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2799 setAX(ERROR_INVALID_FUNCTION);
2800 break;
2801 }
2802
2803 /*
2804 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
2805 * for the full detailed description.
2806 *
2807 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
2808 */
2809
2810 ErrorCode = DosCreateFileEx(&FileHandle,
2811 &CreationStatus,
2812 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getSI()),
2813 getBL(),
2814 getDL(),
2815 getCX());
2816
2817 if (ErrorCode == ERROR_SUCCESS)
2818 {
2819 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2820 setCX(CreationStatus);
2821 setAX(FileHandle);
2822 }
2823 else
2824 {
2825 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2826 setAX(ErrorCode);
2827 }
2828
2829 break;
2830 }
2831
2832 /* Unsupported */
2833 default:
2834 {
2835 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2836 getAH(), getAL());
2837
2838 setAL(0); // Some functions expect AL to be 0 when it's not supported.
2839 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2840 }
2841 }
2842 }
2843
2844 VOID WINAPI DosBreakInterrupt(LPWORD Stack)
2845 {
2846 UNREFERENCED_PARAMETER(Stack);
2847
2848 /* Stop the VDM task */
2849 ResetEvent(VdmTaskEvent);
2850 CpuUnsimulate();
2851 }
2852
2853 VOID WINAPI DosFastConOut(LPWORD Stack)
2854 {
2855 /*
2856 * This is the DOS 2+ Fast Console Output Interrupt.
2857 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
2858 *
2859 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2860 * for more information.
2861 */
2862
2863 /* Save AX and BX */
2864 USHORT AX = getAX();
2865 USHORT BX = getBX();
2866
2867 /*
2868 * Set the parameters:
2869 * AL contains the character to print (already set),
2870 * BL contains the character attribute,
2871 * BH contains the video page to use.
2872 */
2873 setBL(DOS_CHAR_ATTRIBUTE);
2874 setBH(Bda->VideoPage);
2875
2876 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2877 setAH(0x0E);
2878 Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT);
2879
2880 /* Restore AX and BX */
2881 setBX(BX);
2882 setAX(AX);
2883 }
2884
2885 VOID WINAPI DosInt2Fh(LPWORD Stack)
2886 {
2887 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2888 getAH(), getAL());
2889 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2890 }
2891
2892 BOOLEAN DosKRNLInitialize(VOID)
2893 {
2894
2895 #if 1
2896
2897 UCHAR i;
2898 CHAR CurrentDirectory[MAX_PATH];
2899 CHAR DosDirectory[DOS_DIR_LENGTH];
2900 LPSTR Path;
2901
2902 FILE *Stream;
2903 WCHAR Buffer[256];
2904
2905 /* Clear the current directory buffer */
2906 RtlZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
2907
2908 /* Get the current directory */
2909 if (!GetCurrentDirectoryA(MAX_PATH, CurrentDirectory))
2910 {
2911 // TODO: Use some kind of default path?
2912 return FALSE;
2913 }
2914
2915 /* Convert that to a DOS path */
2916 if (!GetShortPathNameA(CurrentDirectory, DosDirectory, DOS_DIR_LENGTH))
2917 {
2918 // TODO: Use some kind of default path?
2919 return FALSE;
2920 }
2921
2922 /* Set the drive */
2923 CurrentDrive = DosDirectory[0] - 'A';
2924
2925 /* Get the directory part of the path */
2926 Path = strchr(DosDirectory, '\\');
2927 if (Path != NULL)
2928 {
2929 /* Skip the backslash */
2930 Path++;
2931 }
2932
2933 /* Set the directory */
2934 if (Path != NULL)
2935 {
2936 strncpy(CurrentDirectories[CurrentDrive], Path, DOS_DIR_LENGTH);
2937 }
2938
2939 /* Read CONFIG.SYS */
2940 Stream = _wfopen(DOS_CONFIG_PATH, L"r");
2941 if (Stream != NULL)
2942 {
2943 while (fgetws(Buffer, 256, Stream))
2944 {
2945 // TODO: Parse the line
2946 }
2947 fclose(Stream);
2948 }
2949
2950 /* Initialize the SFT */
2951 for (i = 0; i < DOS_SFT_SIZE; i++)
2952 {
2953 DosSystemFileTable[i].Handle = INVALID_HANDLE_VALUE;
2954 DosSystemFileTable[i].RefCount = 0;
2955 }
2956
2957 /* Get handles to standard I/O devices */
2958 DosSystemFileTable[0].Handle = GetStdHandle(STD_INPUT_HANDLE);
2959 DosSystemFileTable[1].Handle = GetStdHandle(STD_OUTPUT_HANDLE);
2960 DosSystemFileTable[2].Handle = GetStdHandle(STD_ERROR_HANDLE);
2961
2962 /* Initialize the reference counts */
2963 DosSystemFileTable[0].RefCount =
2964 DosSystemFileTable[1].RefCount =
2965 DosSystemFileTable[2].RefCount = 1;
2966
2967 #endif
2968
2969 /* Initialize the callback context */
2970 InitializeContext(&DosContext, 0x0070, 0x0000);
2971
2972 /* Register the DOS 32-bit Interrupts */
2973 RegisterDosInt32(0x20, DosInt20h );
2974 RegisterDosInt32(0x21, DosInt21h );
2975 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2976 RegisterDosInt32(0x23, DosBreakInterrupt); // Ctrl-C / Ctrl-Break
2977 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2978 RegisterDosInt32(0x29, DosFastConOut ); // DOS 2+ Fast Console Output
2979 RegisterDosInt32(0x2F, DosInt2Fh );
2980
2981 return TRUE;
2982 }
2983
2984 /* EOF */