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