0cae2ca039696ed289eb5bba7f8b83881062d884
[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 = NULL;
1211
1212 /* Make sure it exists */
1213 if (!SftEntry)
1214 {
1215 DosLastError = ERROR_FILE_NOT_FOUND;
1216 return FALSE;
1217 }
1218
1219 if (SftEntry->Type == DOS_SFT_ENTRY_DEVICE) Node = SftEntry->DeviceNode;
1220
1221 switch (ControlCode)
1222 {
1223 /* Get Device Information */
1224 case 0x00:
1225 {
1226 WORD InfoWord = 0;
1227
1228 /*
1229 * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
1230 * for a list of possible flags.
1231 */
1232
1233 if (Node)
1234 {
1235 /* Return the device attributes with bit 7 set */
1236 InfoWord = Node->DeviceAttributes | (1 << 7);
1237 }
1238
1239 setDX(InfoWord);
1240 return TRUE;
1241 }
1242
1243 /* Set Device Information */
1244 case 0x01:
1245 {
1246 // TODO: NOT IMPLEMENTED
1247 UNIMPLEMENTED;
1248
1249 return FALSE;
1250 }
1251
1252 /* Read From Device I/O Control Channel */
1253 case 0x02:
1254 {
1255 WORD Length = getCX();
1256
1257 if (Node == NULL || !(Node->DeviceAttributes & DOS_DEVATTR_IOCTL))
1258 {
1259 DosLastError = ERROR_INVALID_FUNCTION;
1260 return FALSE;
1261 }
1262
1263 /* Do nothing if there is no IOCTL routine */
1264 if (!Node->IoctlReadRoutine)
1265 {
1266 setAX(0);
1267 return TRUE;
1268 }
1269
1270 Node->IoctlReadRoutine(Node, MAKELONG(getDX(), getDS()), &Length);
1271
1272 setAX(Length);
1273 return TRUE;
1274 }
1275
1276 /* Write To Device I/O Control Channel */
1277 case 0x03:
1278 {
1279 WORD Length = getCX();
1280
1281 if (Node == NULL || !(Node->DeviceAttributes & DOS_DEVATTR_IOCTL))
1282 {
1283 DosLastError = ERROR_INVALID_FUNCTION;
1284 return FALSE;
1285 }
1286
1287 /* Do nothing if there is no IOCTL routine */
1288 if (!Node->IoctlWriteRoutine)
1289 {
1290 setAX(0);
1291 return TRUE;
1292 }
1293
1294 Node->IoctlWriteRoutine(Node, MAKELONG(getDX(), getDS()), &Length);
1295
1296 setAX(Length);
1297 return TRUE;
1298 }
1299
1300 /* Unsupported control code */
1301 default:
1302 {
1303 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode);
1304
1305 DosLastError = ERROR_INVALID_PARAMETER;
1306 return FALSE;
1307 }
1308 }
1309 }
1310
1311 VOID WINAPI DosInt20h(LPWORD Stack)
1312 {
1313 /* This is the exit interrupt */
1314 DosTerminateProcess(Stack[STACK_CS], 0);
1315 }
1316
1317 VOID WINAPI DosInt21h(LPWORD Stack)
1318 {
1319 BYTE Character;
1320 SYSTEMTIME SystemTime;
1321 PCHAR String;
1322 PDOS_INPUT_BUFFER InputBuffer;
1323 PDOS_COUNTRY_CODE_BUFFER CountryCodeBuffer;
1324 INT Return;
1325
1326 /* Check the value in the AH register */
1327 switch (getAH())
1328 {
1329 /* Terminate Program */
1330 case 0x00:
1331 {
1332 DosTerminateProcess(Stack[STACK_CS], 0);
1333 break;
1334 }
1335
1336 /* Read Character from STDIN with Echo */
1337 case 0x01:
1338 {
1339 DPRINT("INT 21h, AH = 01h\n");
1340
1341 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
1342 DoEcho = TRUE;
1343 Character = DosReadCharacter(DOS_INPUT_HANDLE);
1344 DoEcho = FALSE;
1345
1346 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
1347 // Check also Ctrl-P and set echo-to-printer flag.
1348 // Ctrl-Z is not interpreted.
1349
1350 setAL(Character);
1351 break;
1352 }
1353
1354 /* Write Character to STDOUT */
1355 case 0x02:
1356 {
1357 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1358 Character = getDL();
1359 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
1360
1361 /*
1362 * We return the output character (DOS 2.1+).
1363 * Also, if we're going to output a TAB, then
1364 * don't return a TAB but a SPACE instead.
1365 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
1366 * for more information.
1367 */
1368 setAL(Character == '\t' ? ' ' : Character);
1369 break;
1370 }
1371
1372 /* Read Character from STDAUX */
1373 case 0x03:
1374 {
1375 // FIXME: Really read it from STDAUX!
1376 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
1377 // setAL(DosReadCharacter());
1378 break;
1379 }
1380
1381 /* Write Character to STDAUX */
1382 case 0x04:
1383 {
1384 // FIXME: Really write it to STDAUX!
1385 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
1386 // DosPrintCharacter(getDL());
1387 break;
1388 }
1389
1390 /* Write Character to Printer */
1391 case 0x05:
1392 {
1393 // FIXME: Really write it to printer!
1394 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
1395 DPRINT1("0x%p\n", getDL());
1396 DPRINT1("\n\n-----------\n\n");
1397 break;
1398 }
1399
1400 /* Direct Console I/O */
1401 case 0x06:
1402 {
1403 Character = getDL();
1404
1405 // FIXME: Under DOS 2+, output handle may be redirected!!!!
1406
1407 if (Character != 0xFF)
1408 {
1409 /* Output */
1410 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
1411
1412 /*
1413 * We return the output character (DOS 2.1+).
1414 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
1415 * for more information.
1416 */
1417 setAL(Character);
1418 }
1419 else
1420 {
1421 /* Input */
1422 if (DosCheckInput())
1423 {
1424 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
1425 setAL(DosReadCharacter(DOS_INPUT_HANDLE));
1426 }
1427 else
1428 {
1429 /* No character available */
1430 Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF;
1431 setAL(0x00);
1432 }
1433 }
1434
1435 break;
1436 }
1437
1438 /* Character Input without Echo */
1439 case 0x07:
1440 case 0x08:
1441 {
1442 DPRINT("Char input without echo\n");
1443
1444 // FIXME: Under DOS 2+, input handle may be redirected!!!!
1445 Character = DosReadCharacter(DOS_INPUT_HANDLE);
1446
1447 // FIXME: For 0x07, do not check Ctrl-C/Break.
1448 // For 0x08, do check those control sequences and if needed,
1449 // call INT 0x23.
1450
1451 // /* Let the BOP repeat if needed */
1452 // if (getCF()) break;
1453
1454 setAL(Character);
1455 break;
1456 }
1457
1458 /* Write string to STDOUT */
1459 case 0x09:
1460 {
1461 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
1462
1463 while (*String != '$')
1464 {
1465 DosPrintCharacter(DOS_OUTPUT_HANDLE, *String);
1466 String++;
1467 }
1468
1469 /*
1470 * We return the terminating character (DOS 2.1+).
1471 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
1472 * for more information.
1473 */
1474 setAL('$'); // *String
1475 break;
1476 }
1477
1478 /* Read Buffered Input */
1479 case 0x0A:
1480 {
1481 WORD Count = 0;
1482 InputBuffer = (PDOS_INPUT_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
1483
1484 DPRINT("Read Buffered Input\n");
1485
1486 while (Count < InputBuffer->MaxLength)
1487 {
1488 // FIXME!! This function should interpret backspaces etc...
1489
1490 /* Try to read a character (wait) */
1491 Character = DosReadCharacter(DOS_INPUT_HANDLE);
1492
1493 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
1494
1495 /* Echo the character and append it to the buffer */
1496 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
1497 InputBuffer->Buffer[Count] = Character;
1498
1499 Count++; /* Carriage returns are also counted */
1500
1501 if (Character == '\r') break;
1502 }
1503
1504 /* Update the length */
1505 InputBuffer->Length = Count;
1506
1507 break;
1508 }
1509
1510 /* Get STDIN Status */
1511 case 0x0B:
1512 {
1513 setAL(DosCheckInput() ? 0xFF : 0x00);
1514 break;
1515 }
1516
1517 /* Flush Buffer and Read STDIN */
1518 case 0x0C:
1519 {
1520 BYTE InputFunction = getAL();
1521
1522 /* Flush STDIN buffer */
1523 DosFlushFileBuffers(DOS_INPUT_HANDLE);
1524
1525 /*
1526 * If the input function number contained in AL is valid, i.e.
1527 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
1528 * recursively with AL == AH.
1529 */
1530 if (InputFunction == 0x01 || InputFunction == 0x06 ||
1531 InputFunction == 0x07 || InputFunction == 0x08 ||
1532 InputFunction == 0x0A)
1533 {
1534 /* Call ourselves recursively */
1535 setAH(InputFunction);
1536 DosInt21h(Stack);
1537 }
1538 break;
1539 }
1540
1541 /* Disk Reset */
1542 case 0x0D:
1543 {
1544 PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
1545
1546 // TODO: Flush what's needed.
1547 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
1548
1549 /* Clear CF in DOS 6 only */
1550 if (PspBlock->DosVersion == 0x0006)
1551 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1552
1553 break;
1554 }
1555
1556 /* Set Default Drive */
1557 case 0x0E:
1558 {
1559 DosChangeDrive(getDL());
1560 setAL(LastDrive - 'A' + 1);
1561 break;
1562 }
1563
1564 /* NULL Function for CP/M Compatibility */
1565 case 0x18:
1566 {
1567 /*
1568 * This function corresponds to the CP/M BDOS function
1569 * "get bit map of logged drives", which is meaningless
1570 * under MS-DOS.
1571 *
1572 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
1573 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
1574 * for more information.
1575 */
1576 setAL(0x00);
1577 break;
1578 }
1579
1580 /* Get Default Drive */
1581 case 0x19:
1582 {
1583 setAL(CurrentDrive);
1584 break;
1585 }
1586
1587 /* Set Disk Transfer Area */
1588 case 0x1A:
1589 {
1590 DiskTransferArea = MAKELONG(getDX(), getDS());
1591 break;
1592 }
1593
1594 /* NULL Function for CP/M Compatibility */
1595 case 0x1D:
1596 case 0x1E:
1597 {
1598 /*
1599 * Function 0x1D corresponds to the CP/M BDOS function
1600 * "get bit map of read-only drives", which is meaningless
1601 * under MS-DOS.
1602 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
1603 * for more information.
1604 *
1605 * Function 0x1E corresponds to the CP/M BDOS function
1606 * "set file attributes", which was meaningless under MS-DOS 1.x.
1607 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
1608 * for more information.
1609 */
1610 setAL(0x00);
1611 break;
1612 }
1613
1614 /* NULL Function for CP/M Compatibility */
1615 case 0x20:
1616 {
1617 /*
1618 * This function corresponds to the CP/M BDOS function
1619 * "get/set default user (sublibrary) number", which is meaningless
1620 * under MS-DOS.
1621 *
1622 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
1623 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
1624 * for more information.
1625 */
1626 setAL(0x00);
1627 break;
1628 }
1629
1630 /* Set Interrupt Vector */
1631 case 0x25:
1632 {
1633 ULONG FarPointer = MAKELONG(getDX(), getDS());
1634 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
1635 getAL(), HIWORD(FarPointer), LOWORD(FarPointer));
1636
1637 /* Write the new far pointer to the IDT */
1638 ((PULONG)BaseAddress)[getAL()] = FarPointer;
1639 break;
1640 }
1641
1642 /* Create New PSP */
1643 case 0x26:
1644 {
1645 DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
1646 break;
1647 }
1648
1649 /* Get System Date */
1650 case 0x2A:
1651 {
1652 GetLocalTime(&SystemTime);
1653 setCX(SystemTime.wYear);
1654 setDX(MAKEWORD(SystemTime.wDay, SystemTime.wMonth));
1655 setAL(SystemTime.wDayOfWeek);
1656 break;
1657 }
1658
1659 /* Set System Date */
1660 case 0x2B:
1661 {
1662 GetLocalTime(&SystemTime);
1663 SystemTime.wYear = getCX();
1664 SystemTime.wMonth = getDH();
1665 SystemTime.wDay = getDL();
1666
1667 /* Return success or failure */
1668 setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF);
1669 break;
1670 }
1671
1672 /* Get System Time */
1673 case 0x2C:
1674 {
1675 GetLocalTime(&SystemTime);
1676 setCX(MAKEWORD(SystemTime.wMinute, SystemTime.wHour));
1677 setDX(MAKEWORD(SystemTime.wMilliseconds / 10, SystemTime.wSecond));
1678 break;
1679 }
1680
1681 /* Set System Time */
1682 case 0x2D:
1683 {
1684 GetLocalTime(&SystemTime);
1685 SystemTime.wHour = getCH();
1686 SystemTime.wMinute = getCL();
1687 SystemTime.wSecond = getDH();
1688 SystemTime.wMilliseconds = getDL() * 10; // In hundredths of seconds
1689
1690 /* Return success or failure */
1691 setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF);
1692 break;
1693 }
1694
1695 /* Get Disk Transfer Area */
1696 case 0x2F:
1697 {
1698 setES(HIWORD(DiskTransferArea));
1699 setBX(LOWORD(DiskTransferArea));
1700 break;
1701 }
1702
1703 /* Get DOS Version */
1704 case 0x30:
1705 {
1706 PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
1707
1708 /*
1709 * DOS 2+ - GET DOS VERSION
1710 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
1711 * for more information.
1712 */
1713
1714 if (LOBYTE(PspBlock->DosVersion) < 5 || getAL() == 0x00)
1715 {
1716 /*
1717 * Return DOS OEM number:
1718 * 0x00 for IBM PC-DOS
1719 * 0x02 for packaged MS-DOS
1720 * 0xFF for NT DOS
1721 */
1722 setBH(0xFF);
1723 }
1724
1725 if (LOBYTE(PspBlock->DosVersion) >= 5 && getAL() == 0x01)
1726 {
1727 /*
1728 * Return version flag:
1729 * 1 << 3 if DOS is in ROM,
1730 * 0 (reserved) if not.
1731 */
1732 setBH(0x00);
1733 }
1734
1735 /* Return DOS 24-bit user serial number in BL:CX */
1736 setBL(0x00);
1737 setCX(0x0000);
1738
1739 /*
1740 * Return DOS version: Minor:Major in AH:AL
1741 * The Windows NT DOS box returns version 5.00, subject to SETVER.
1742 */
1743 setAX(PspBlock->DosVersion);
1744
1745 break;
1746 }
1747
1748 /* Extended functionalities */
1749 case 0x33:
1750 {
1751 if (getAL() == 0x06)
1752 {
1753 /*
1754 * DOS 5+ - GET TRUE VERSION NUMBER
1755 * This function always returns the true version number, unlike
1756 * AH=30h, whose return value may be changed with SETVER.
1757 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
1758 * for more information.
1759 */
1760
1761 /*
1762 * Return the true DOS version: Minor:Major in BH:BL
1763 * The Windows NT DOS box returns BX=3205h (version 5.50).
1764 */
1765 setBX(NTDOS_VERSION);
1766
1767 /* DOS revision 0 */
1768 setDL(0x00);
1769
1770 /* Unpatched DOS */
1771 setDH(0x00);
1772 }
1773 // else
1774 // {
1775 // /* Invalid subfunction */
1776 // setAL(0xFF);
1777 // }
1778
1779 break;
1780 }
1781
1782 /* Get Interrupt Vector */
1783 case 0x35:
1784 {
1785 DWORD FarPointer = ((PDWORD)BaseAddress)[getAL()];
1786
1787 /* Read the address from the IDT into ES:BX */
1788 setES(HIWORD(FarPointer));
1789 setBX(LOWORD(FarPointer));
1790 break;
1791 }
1792
1793 /* SWITCH character - AVAILDEV */
1794 case 0x37:
1795 {
1796 if (getAL() == 0x00)
1797 {
1798 /*
1799 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
1800 * This setting is ignored by MS-DOS 4.0+.
1801 * MS-DOS 5+ always return AL=00h/DL=2Fh.
1802 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
1803 * for more information.
1804 */
1805 setDL('/');
1806 setAL(0x00);
1807 }
1808 else if (getAL() == 0x01)
1809 {
1810 /*
1811 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
1812 * This setting is ignored by MS-DOS 5+.
1813 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
1814 * for more information.
1815 */
1816 // getDL();
1817 setAL(0xFF);
1818 }
1819 else if (getAL() == 0x02)
1820 {
1821 /*
1822 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1823 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1824 * for more information.
1825 */
1826 // setDL();
1827 setAL(0xFF);
1828 }
1829 else if (getAL() == 0x03)
1830 {
1831 /*
1832 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
1833 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
1834 * for more information.
1835 */
1836 // getDL();
1837 setAL(0xFF);
1838 }
1839 else
1840 {
1841 /* Invalid subfunction */
1842 setAL(0xFF);
1843 }
1844
1845 break;
1846 }
1847
1848 /* Get/Set Country-dependent Information */
1849 case 0x38:
1850 {
1851 CountryCodeBuffer = (PDOS_COUNTRY_CODE_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
1852
1853 if (getAL() == 0x00)
1854 {
1855 /* Get */
1856 Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDATE,
1857 &CountryCodeBuffer->TimeFormat,
1858 sizeof(CountryCodeBuffer->TimeFormat) / sizeof(TCHAR));
1859 if (Return == 0)
1860 {
1861 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1862 setAX(LOWORD(GetLastError()));
1863 break;
1864 }
1865
1866 Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SCURRENCY,
1867 &CountryCodeBuffer->CurrencySymbol,
1868 sizeof(CountryCodeBuffer->CurrencySymbol) / sizeof(TCHAR));
1869 if (Return == 0)
1870 {
1871 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1872 setAX(LOWORD(GetLastError()));
1873 break;
1874 }
1875
1876 Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND,
1877 &CountryCodeBuffer->ThousandSep,
1878 sizeof(CountryCodeBuffer->ThousandSep) / sizeof(TCHAR));
1879 if (Return == 0)
1880 {
1881 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1882 setAX(LOWORD(GetLastError()));
1883 break;
1884 }
1885
1886 Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
1887 &CountryCodeBuffer->DecimalSep,
1888 sizeof(CountryCodeBuffer->DecimalSep) / sizeof(TCHAR));
1889 if (Return == 0)
1890 {
1891 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1892 setAX(LOWORD(GetLastError()));
1893 break;
1894 }
1895
1896 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1897 }
1898 else
1899 {
1900 // TODO: NOT IMPLEMENTED
1901 UNIMPLEMENTED;
1902 }
1903
1904 break;
1905 }
1906
1907 /* Create Directory */
1908 case 0x39:
1909 {
1910 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
1911
1912 if (CreateDirectoryA(String, NULL))
1913 {
1914 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1915 }
1916 else
1917 {
1918 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1919 setAX(LOWORD(GetLastError()));
1920 }
1921
1922 break;
1923 }
1924
1925 /* Remove Directory */
1926 case 0x3A:
1927 {
1928 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
1929
1930 if (RemoveDirectoryA(String))
1931 {
1932 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1933 }
1934 else
1935 {
1936 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1937 setAX(LOWORD(GetLastError()));
1938 }
1939
1940 break;
1941 }
1942
1943 /* Set Current Directory */
1944 case 0x3B:
1945 {
1946 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
1947
1948 if (DosChangeDirectory(String))
1949 {
1950 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1951 }
1952 else
1953 {
1954 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1955 setAX(DosLastError);
1956 }
1957
1958 break;
1959 }
1960
1961 /* Create or Truncate File */
1962 case 0x3C:
1963 {
1964 WORD FileHandle;
1965 WORD ErrorCode = DosCreateFile(&FileHandle,
1966 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
1967 CREATE_ALWAYS,
1968 getCX());
1969
1970 if (ErrorCode == ERROR_SUCCESS)
1971 {
1972 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1973 setAX(FileHandle);
1974 }
1975 else
1976 {
1977 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1978 setAX(ErrorCode);
1979 }
1980
1981 break;
1982 }
1983
1984 /* Open File or Device */
1985 case 0x3D:
1986 {
1987 WORD FileHandle;
1988 WORD ErrorCode;
1989 LPCSTR FileName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1990 PDOS_DEVICE_NODE Device = DosGetDevice(FileName);
1991
1992 if (Device)
1993 {
1994 FileHandle = DosOpenDevice(Device);
1995 ErrorCode = (FileHandle != INVALID_DOS_HANDLE)
1996 ? ERROR_SUCCESS : ERROR_TOO_MANY_OPEN_FILES;
1997 }
1998 else
1999 {
2000 ErrorCode = DosOpenFile(&FileHandle, FileName, getAL());
2001 }
2002
2003 if (ErrorCode == ERROR_SUCCESS)
2004 {
2005 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2006 setAX(FileHandle);
2007 }
2008 else
2009 {
2010 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2011 setAX(ErrorCode);
2012 }
2013
2014 break;
2015 }
2016
2017 /* Close File or Device */
2018 case 0x3E:
2019 {
2020 if (DosCloseHandle(getBX()))
2021 {
2022 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2023 }
2024 else
2025 {
2026 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2027 setAX(ERROR_INVALID_HANDLE);
2028 }
2029
2030 break;
2031 }
2032
2033 /* Read from File or Device */
2034 case 0x3F:
2035 {
2036 WORD BytesRead = 0;
2037 WORD ErrorCode;
2038
2039 DPRINT("DosReadFile(0x%04X)\n", getBX());
2040
2041 DoEcho = TRUE;
2042 ErrorCode = DosReadFile(getBX(),
2043 MAKELONG(getDX(), getDS()),
2044 getCX(),
2045 &BytesRead);
2046 DoEcho = FALSE;
2047
2048 if (ErrorCode == ERROR_SUCCESS)
2049 {
2050 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2051 setAX(BytesRead);
2052 }
2053 else if (ErrorCode != ERROR_NOT_READY)
2054 {
2055 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2056 setAX(ErrorCode);
2057 }
2058
2059 break;
2060 }
2061
2062 /* Write to File or Device */
2063 case 0x40:
2064 {
2065 WORD BytesWritten = 0;
2066 WORD ErrorCode = DosWriteFile(getBX(),
2067 MAKELONG(getDX(), getDS()),
2068 getCX(),
2069 &BytesWritten);
2070
2071 if (ErrorCode == ERROR_SUCCESS)
2072 {
2073 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2074 setAX(BytesWritten);
2075 }
2076 else
2077 {
2078 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2079 setAX(ErrorCode);
2080 }
2081
2082 break;
2083 }
2084
2085 /* Delete File */
2086 case 0x41:
2087 {
2088 LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
2089
2090 if (demFileDelete(FileName) == ERROR_SUCCESS)
2091 {
2092 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2093 /*
2094 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
2095 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
2096 */
2097 setAL(FileName[0] - 'A');
2098 }
2099 else
2100 {
2101 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2102 setAX(GetLastError());
2103 }
2104
2105 break;
2106 }
2107
2108 /* Seek File */
2109 case 0x42:
2110 {
2111 DWORD NewLocation;
2112 WORD ErrorCode = DosSeekFile(getBX(),
2113 MAKELONG(getDX(), getCX()),
2114 getAL(),
2115 &NewLocation);
2116
2117 if (ErrorCode == ERROR_SUCCESS)
2118 {
2119 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2120
2121 /* Return the new offset in DX:AX */
2122 setDX(HIWORD(NewLocation));
2123 setAX(LOWORD(NewLocation));
2124 }
2125 else
2126 {
2127 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2128 setAX(ErrorCode);
2129 }
2130
2131 break;
2132 }
2133
2134 /* Get/Set File Attributes */
2135 case 0x43:
2136 {
2137 DWORD Attributes;
2138 LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
2139
2140 if (getAL() == 0x00)
2141 {
2142 /* Get the attributes */
2143 Attributes = GetFileAttributesA(FileName);
2144
2145 /* Check if it failed */
2146 if (Attributes == INVALID_FILE_ATTRIBUTES)
2147 {
2148 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2149 setAX(GetLastError());
2150 }
2151 else
2152 {
2153 /* Return the attributes that DOS can understand */
2154 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2155 setCX(Attributes & 0x00FF);
2156 }
2157 }
2158 else if (getAL() == 0x01)
2159 {
2160 /* Try to set the attributes */
2161 if (SetFileAttributesA(FileName, getCL()))
2162 {
2163 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2164 }
2165 else
2166 {
2167 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2168 setAX(GetLastError());
2169 }
2170 }
2171 else
2172 {
2173 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2174 setAX(ERROR_INVALID_FUNCTION);
2175 }
2176
2177 break;
2178 }
2179
2180 /* IOCTL */
2181 case 0x44:
2182 {
2183 if (DosHandleIoctl(getAL(), getBX()))
2184 {
2185 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2186 }
2187 else
2188 {
2189 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2190 setAX(DosLastError);
2191 }
2192
2193 break;
2194 }
2195
2196 /* Duplicate Handle */
2197 case 0x45:
2198 {
2199 WORD NewHandle;
2200 PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(getBX());
2201
2202 if (SftEntry == NULL || SftEntry->Type == DOS_SFT_ENTRY_NONE)
2203 {
2204 /* The handle is invalid */
2205 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2206 setAX(ERROR_INVALID_HANDLE);
2207 break;
2208 }
2209
2210 /* Open a new handle to the same entry */
2211 switch (SftEntry->Type)
2212 {
2213 case DOS_SFT_ENTRY_WIN32:
2214 {
2215 NewHandle = DosOpenHandle(SftEntry->Handle);
2216 break;
2217 }
2218
2219 case DOS_SFT_ENTRY_DEVICE:
2220 {
2221 NewHandle = DosOpenDevice(SftEntry->DeviceNode);
2222 break;
2223 }
2224
2225 default:
2226 {
2227 /* Shouldn't happen */
2228 ASSERT(FALSE);
2229 }
2230 }
2231
2232 if (NewHandle == INVALID_DOS_HANDLE)
2233 {
2234 /* Too many files open */
2235 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2236 setAX(ERROR_TOO_MANY_OPEN_FILES);
2237 break;
2238 }
2239
2240 /* Return the result */
2241 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2242 setAX(NewHandle);
2243 break;
2244 }
2245
2246 /* Force Duplicate Handle */
2247 case 0x46:
2248 {
2249 if (DosDuplicateHandle(getBX(), getCX()))
2250 {
2251 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2252 }
2253 else
2254 {
2255 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2256 setAX(ERROR_INVALID_HANDLE);
2257 }
2258
2259 break;
2260 }
2261
2262 /* Get Current Directory */
2263 case 0x47:
2264 {
2265 BYTE DriveNumber = getDL();
2266 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI());
2267
2268 /* Get the real drive number */
2269 if (DriveNumber == 0)
2270 {
2271 DriveNumber = CurrentDrive;
2272 }
2273 else
2274 {
2275 /* Decrement DriveNumber since it was 1-based */
2276 DriveNumber--;
2277 }
2278
2279 if (DriveNumber <= LastDrive - 'A')
2280 {
2281 /*
2282 * Copy the current directory into the target buffer.
2283 * It doesn't contain the drive letter and the backslash.
2284 */
2285 strncpy(String, CurrentDirectories[DriveNumber], DOS_DIR_LENGTH);
2286 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2287 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
2288 }
2289 else
2290 {
2291 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2292 setAX(ERROR_INVALID_DRIVE);
2293 }
2294
2295 break;
2296 }
2297
2298 /* Allocate Memory */
2299 case 0x48:
2300 {
2301 WORD MaxAvailable = 0;
2302 WORD Segment = DosAllocateMemory(getBX(), &MaxAvailable);
2303
2304 if (Segment != 0)
2305 {
2306 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2307 setAX(Segment);
2308 }
2309 else
2310 {
2311 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2312 setAX(DosLastError);
2313 setBX(MaxAvailable);
2314 }
2315
2316 break;
2317 }
2318
2319 /* Free Memory */
2320 case 0x49:
2321 {
2322 if (DosFreeMemory(getES()))
2323 {
2324 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2325 }
2326 else
2327 {
2328 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2329 setAX(ERROR_ARENA_TRASHED);
2330 }
2331
2332 break;
2333 }
2334
2335 /* Resize Memory Block */
2336 case 0x4A:
2337 {
2338 WORD Size;
2339
2340 if (DosResizeMemory(getES(), getBX(), &Size))
2341 {
2342 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2343 }
2344 else
2345 {
2346 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2347 setAX(DosLastError);
2348 setBX(Size);
2349 }
2350
2351 break;
2352 }
2353
2354 #ifndef STANDALONE
2355 /* Execute */
2356 case 0x4B:
2357 {
2358 DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)getAL();
2359 LPSTR ProgramName = SEG_OFF_TO_PTR(getDS(), getDX());
2360 PDOS_EXEC_PARAM_BLOCK ParamBlock = SEG_OFF_TO_PTR(getES(), getBX());
2361 WORD ErrorCode = DosCreateProcess(LoadType, ProgramName, ParamBlock);
2362
2363 if (ErrorCode == ERROR_SUCCESS)
2364 {
2365 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2366 }
2367 else
2368 {
2369 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2370 setAX(ErrorCode);
2371 }
2372
2373 break;
2374 }
2375 #endif
2376
2377 /* Terminate With Return Code */
2378 case 0x4C:
2379 {
2380 DosTerminateProcess(CurrentPsp, getAL());
2381 break;
2382 }
2383
2384 /* Get Return Code (ERRORLEVEL) */
2385 case 0x4D:
2386 {
2387 /*
2388 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
2389 * DosErrorLevel is cleared after being read by this function.
2390 */
2391 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2392 setAX(DosErrorLevel);
2393 DosErrorLevel = 0x0000; // Clear it
2394 break;
2395 }
2396
2397 /* Find First File */
2398 case 0x4E:
2399 {
2400 WORD Result = (WORD)demFileFindFirst(FAR_POINTER(DiskTransferArea),
2401 SEG_OFF_TO_PTR(getDS(), getDX()),
2402 getCX());
2403
2404 setAX(Result);
2405
2406 if (Result == ERROR_SUCCESS)
2407 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2408 else
2409 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2410
2411 break;
2412 }
2413
2414 /* Find Next File */
2415 case 0x4F:
2416 {
2417 WORD Result = (WORD)demFileFindNext(FAR_POINTER(DiskTransferArea));
2418
2419 setAX(Result);
2420
2421 if (Result == ERROR_SUCCESS)
2422 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2423 else
2424 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2425
2426 break;
2427 }
2428
2429 /* Internal - Set Current Process ID (Set PSP Address) */
2430 case 0x50:
2431 {
2432 // FIXME: Is it really what it's done ??
2433 CurrentPsp = getBX();
2434 break;
2435 }
2436
2437 /* Internal - Get Current Process ID (Get PSP Address) */
2438 case 0x51:
2439 /* Get Current PSP Address */
2440 case 0x62:
2441 {
2442 /*
2443 * Undocumented AH=51h is identical to the documented AH=62h.
2444 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
2445 * and http://www.ctyme.com/intr/rb-3140.htm
2446 * for more information.
2447 */
2448 setBX(CurrentPsp);
2449 break;
2450 }
2451
2452 /* Internal - Get "List of lists" (SYSVARS) */
2453 case 0x52:
2454 {
2455 /*
2456 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
2457 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
2458 * for more information.
2459 */
2460
2461 /* Return the DOS "list of lists" in ES:BX */
2462 setES(0x0000);
2463 setBX(0x0000);
2464
2465 DisplayMessage(L"Required for AARD code, do you remember? :P");
2466 break;
2467 }
2468
2469 /* Rename File */
2470 case 0x56:
2471 {
2472 LPSTR ExistingFileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
2473 LPSTR NewFileName = (LPSTR)SEG_OFF_TO_PTR(getES(), getDI());
2474
2475 /*
2476 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
2477 * for more information.
2478 */
2479
2480 if (MoveFileA(ExistingFileName, NewFileName))
2481 {
2482 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2483 }
2484 else
2485 {
2486 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2487 setAX(GetLastError());
2488 }
2489
2490 break;
2491 }
2492
2493 /* Get/Set Memory Management Options */
2494 case 0x58:
2495 {
2496 if (getAL() == 0x00)
2497 {
2498 /* Get allocation strategy */
2499 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2500 setAX(DosAllocStrategy);
2501 }
2502 else if (getAL() == 0x01)
2503 {
2504 /* Set allocation strategy */
2505
2506 if ((getBL() & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
2507 == (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
2508 {
2509 /* Can't set both */
2510 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2511 setAX(ERROR_INVALID_PARAMETER);
2512 break;
2513 }
2514
2515 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT)
2516 {
2517 /* Invalid allocation strategy */
2518 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2519 setAX(ERROR_INVALID_PARAMETER);
2520 break;
2521 }
2522
2523 DosAllocStrategy = getBL();
2524 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2525 }
2526 else if (getAL() == 0x02)
2527 {
2528 /* Get UMB link state */
2529 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2530 setAL(DosUmbLinked ? 0x01 : 0x00);
2531 }
2532 else if (getAL() == 0x03)
2533 {
2534 /* Set UMB link state */
2535 if (getBX()) DosLinkUmb();
2536 else DosUnlinkUmb();
2537 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2538 }
2539 else
2540 {
2541 /* Invalid or unsupported function */
2542 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2543 setAX(ERROR_INVALID_FUNCTION);
2544 }
2545
2546 break;
2547 }
2548
2549 /* Get Extended Error Information */
2550 case 0x59:
2551 {
2552 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
2553 getBX());
2554 break;
2555 }
2556
2557 /* Create Temporary File */
2558 case 0x5A:
2559 {
2560 LPSTR PathName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
2561 LPSTR FileName = PathName; // The buffer for the path and the full file name is the same.
2562 UINT uRetVal;
2563 WORD FileHandle;
2564 WORD ErrorCode;
2565
2566 /*
2567 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
2568 * for more information.
2569 */
2570
2571 // FIXME: Check for buffer validity?
2572 // It should be a ASCIZ path ending with a '\' + 13 zero bytes
2573 // to receive the generated filename.
2574
2575 /* First create the temporary file */
2576 uRetVal = GetTempFileNameA(PathName, NULL, 0, FileName);
2577 if (uRetVal == 0)
2578 {
2579 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2580 setAX(GetLastError());
2581 break;
2582 }
2583
2584 /* Now try to open it in read/write access */
2585 ErrorCode = DosOpenFile(&FileHandle, FileName, 2);
2586 if (ErrorCode == ERROR_SUCCESS)
2587 {
2588 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2589 setAX(FileHandle);
2590 }
2591 else
2592 {
2593 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2594 setAX(ErrorCode);
2595 }
2596
2597 break;
2598 }
2599
2600 /* Create New File */
2601 case 0x5B:
2602 {
2603 WORD FileHandle;
2604 WORD ErrorCode = DosCreateFile(&FileHandle,
2605 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
2606 CREATE_NEW,
2607 getCX());
2608
2609 if (ErrorCode == ERROR_SUCCESS)
2610 {
2611 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2612 setAX(FileHandle);
2613 }
2614 else
2615 {
2616 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2617 setAX(ErrorCode);
2618 }
2619
2620 break;
2621 }
2622
2623 /* Lock/Unlock Region of File */
2624 case 0x5C:
2625 {
2626 PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(getBX());
2627
2628 if (SftEntry == NULL || SftEntry->Type != DOS_SFT_ENTRY_WIN32)
2629 {
2630 /* The handle is invalid */
2631 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2632 setAX(ERROR_INVALID_HANDLE);
2633 break;
2634 }
2635
2636 if (getAL() == 0x00)
2637 {
2638 /* Lock region of file */
2639 if (LockFile(SftEntry->Handle,
2640 MAKELONG(getCX(), getDX()), 0,
2641 MAKELONG(getSI(), getDI()), 0))
2642 {
2643 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2644 }
2645 else
2646 {
2647 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2648 setAX(GetLastError());
2649 }
2650 }
2651 else if (getAL() == 0x01)
2652 {
2653 /* Unlock region of file */
2654 if (UnlockFile(SftEntry->Handle,
2655 MAKELONG(getCX(), getDX()), 0,
2656 MAKELONG(getSI(), getDI()), 0))
2657 {
2658 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2659 }
2660 else
2661 {
2662 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2663 setAX(GetLastError());
2664 }
2665 }
2666 else
2667 {
2668 /* Invalid subfunction */
2669 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2670 setAX(ERROR_INVALID_FUNCTION);
2671 }
2672
2673 break;
2674 }
2675
2676 /* Canonicalize File Name or Path */
2677 case 0x60:
2678 {
2679 /*
2680 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
2681 * for more information.
2682 */
2683
2684 /*
2685 * We suppose that the DOS app gave to us a valid
2686 * 128-byte long buffer for the canonicalized name.
2687 */
2688 DWORD dwRetVal = GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
2689 128,
2690 SEG_OFF_TO_PTR(getES(), getDI()),
2691 NULL);
2692 if (dwRetVal == 0)
2693 {
2694 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2695 setAX(GetLastError());
2696 }
2697 else
2698 {
2699 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2700 setAX(0x0000);
2701 }
2702
2703 // FIXME: Convert the full path name into short version.
2704 // We cannot reliably use GetShortPathName, because it fails
2705 // if the path name given doesn't exist. However this DOS
2706 // function AH=60h should be able to work even for non-existing
2707 // path and file names.
2708
2709 break;
2710 }
2711
2712 /* Set Handle Count */
2713 case 0x67:
2714 {
2715 if (!DosResizeHandleTable(getBX()))
2716 {
2717 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2718 setAX(DosLastError);
2719 }
2720 else Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2721
2722 break;
2723 }
2724
2725 /* Commit File */
2726 case 0x68:
2727 case 0x6A:
2728 {
2729 /*
2730 * Function 6Ah is identical to function 68h,
2731 * and sets AH to 68h if success.
2732 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
2733 * for more information.
2734 */
2735 setAH(0x68);
2736
2737 if (DosFlushFileBuffers(getBX()))
2738 {
2739 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2740 }
2741 else
2742 {
2743 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2744 setAX(GetLastError());
2745 }
2746
2747 break;
2748 }
2749
2750 /* Extended Open/Create */
2751 case 0x6C:
2752 {
2753 WORD FileHandle;
2754 WORD CreationStatus;
2755 WORD ErrorCode;
2756
2757 /* Check for AL == 00 */
2758 if (getAL() != 0x00)
2759 {
2760 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2761 setAX(ERROR_INVALID_FUNCTION);
2762 break;
2763 }
2764
2765 /*
2766 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
2767 * for the full detailed description.
2768 *
2769 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
2770 */
2771
2772 ErrorCode = DosCreateFileEx(&FileHandle,
2773 &CreationStatus,
2774 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getSI()),
2775 getBL(),
2776 getDL(),
2777 getCX());
2778
2779 if (ErrorCode == ERROR_SUCCESS)
2780 {
2781 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2782 setCX(CreationStatus);
2783 setAX(FileHandle);
2784 }
2785 else
2786 {
2787 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2788 setAX(ErrorCode);
2789 }
2790
2791 break;
2792 }
2793
2794 /* Unsupported */
2795 default:
2796 {
2797 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2798 getAH(), getAL());
2799
2800 setAL(0); // Some functions expect AL to be 0 when it's not supported.
2801 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2802 }
2803 }
2804 }
2805
2806 VOID WINAPI DosBreakInterrupt(LPWORD Stack)
2807 {
2808 UNREFERENCED_PARAMETER(Stack);
2809
2810 /* Stop the VDM task */
2811 ResetEvent(VdmTaskEvent);
2812 CpuUnsimulate();
2813 }
2814
2815 VOID WINAPI DosFastConOut(LPWORD Stack)
2816 {
2817 /*
2818 * This is the DOS 2+ Fast Console Output Interrupt.
2819 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
2820 *
2821 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2822 * for more information.
2823 */
2824
2825 /* Save AX and BX */
2826 USHORT AX = getAX();
2827 USHORT BX = getBX();
2828
2829 /*
2830 * Set the parameters:
2831 * AL contains the character to print (already set),
2832 * BL contains the character attribute,
2833 * BH contains the video page to use.
2834 */
2835 setBL(DOS_CHAR_ATTRIBUTE);
2836 setBH(Bda->VideoPage);
2837
2838 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2839 setAH(0x0E);
2840 Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT);
2841
2842 /* Restore AX and BX */
2843 setBX(BX);
2844 setAX(AX);
2845 }
2846
2847 VOID WINAPI DosInt2Fh(LPWORD Stack)
2848 {
2849 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
2850 getAH(), getAL());
2851 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2852 }
2853
2854 BOOLEAN DosKRNLInitialize(VOID)
2855 {
2856
2857 #if 1
2858
2859 UCHAR i;
2860 CHAR CurrentDirectory[MAX_PATH];
2861 CHAR DosDirectory[DOS_DIR_LENGTH];
2862 LPSTR Path;
2863
2864 FILE *Stream;
2865 WCHAR Buffer[256];
2866
2867 /* Clear the current directory buffer */
2868 RtlZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
2869
2870 /* Get the current directory */
2871 if (!GetCurrentDirectoryA(MAX_PATH, CurrentDirectory))
2872 {
2873 // TODO: Use some kind of default path?
2874 return FALSE;
2875 }
2876
2877 /* Convert that to a DOS path */
2878 if (!GetShortPathNameA(CurrentDirectory, DosDirectory, DOS_DIR_LENGTH))
2879 {
2880 // TODO: Use some kind of default path?
2881 return FALSE;
2882 }
2883
2884 /* Set the drive */
2885 CurrentDrive = DosDirectory[0] - 'A';
2886
2887 /* Get the directory part of the path */
2888 Path = strchr(DosDirectory, '\\');
2889 if (Path != NULL)
2890 {
2891 /* Skip the backslash */
2892 Path++;
2893 }
2894
2895 /* Set the directory */
2896 if (Path != NULL)
2897 {
2898 strncpy(CurrentDirectories[CurrentDrive], Path, DOS_DIR_LENGTH);
2899 }
2900
2901 /* Read CONFIG.SYS */
2902 Stream = _wfopen(DOS_CONFIG_PATH, L"r");
2903 if (Stream != NULL)
2904 {
2905 while (fgetws(Buffer, 256, Stream))
2906 {
2907 // TODO: Parse the line
2908 }
2909 fclose(Stream);
2910 }
2911
2912 /* Initialize the SFT */
2913 for (i = 0; i < DOS_SFT_SIZE; i++)
2914 {
2915 DosSystemFileTable[i].Type = DOS_SFT_ENTRY_NONE;
2916 DosSystemFileTable[i].RefCount = 0;
2917 }
2918
2919 /* Load the EMS driver */
2920 EmsDrvInitialize();
2921
2922 /* Load the CON driver */
2923 ConDrvInitialize();
2924
2925 #endif
2926
2927 /* Initialize the callback context */
2928 InitializeContext(&DosContext, 0x0070, 0x0000);
2929
2930 /* Register the DOS 32-bit Interrupts */
2931 RegisterDosInt32(0x20, DosInt20h );
2932 RegisterDosInt32(0x21, DosInt21h );
2933 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2934 RegisterDosInt32(0x23, DosBreakInterrupt); // Ctrl-C / Ctrl-Break
2935 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2936 RegisterDosInt32(0x29, DosFastConOut ); // DOS 2+ Fast Console Output
2937 RegisterDosInt32(0x2F, DosInt2Fh );
2938
2939 return TRUE;
2940 }
2941
2942 /* EOF */