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