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