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