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