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