[BASESRV][NTVDM][TESTVDD] Improve the FILE header section. Brought to you by Adam...
[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: subsystems/mvdm/ntvdm/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 "country.h"
22 #include "device.h"
23 #include "handle.h"
24 #include "dosfiles.h"
25 #include "memory.h"
26 #include "process.h"
27 #include "himem.h"
28
29 #include "bios/bios.h"
30
31 #include "io.h"
32 #include "hardware/ps2.h"
33
34 #include "emsdrv.h"
35
36 /* PRIVATE VARIABLES **********************************************************/
37
38 CALLBACK16 DosContext;
39
40 /* PUBLIC VARIABLES ***********************************************************/
41
42 /* Global DOS data area contained in guest memory */
43 PDOS_DATA DosData;
44 /* Easy accessors to useful DOS data area parts */
45 PDOS_SYSVARS SysVars;
46 PDOS_SDA Sda;
47
48 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
49 BOOLEAN DoEcho = FALSE;
50
51 /* PRIVATE FUNCTIONS **********************************************************/
52
53 static BOOLEAN DosChangeDrive(BYTE Drive)
54 {
55 CHAR DirectoryPath[DOS_CMDLINE_LENGTH + 1];
56
57 /* Make sure the drive exists */
58 if (Drive >= SysVars->NumLocalDrives) return FALSE;
59
60 RtlZeroMemory(DirectoryPath, sizeof(DirectoryPath));
61
62 /* Find the path to the new current directory */
63 snprintf(DirectoryPath,
64 DOS_CMDLINE_LENGTH,
65 "%c:\\%s",
66 'A' + Drive,
67 DosData->CurrentDirectories[Drive]);
68
69 /* Change the current directory of the process */
70 if (!SetCurrentDirectoryA(DirectoryPath)) return FALSE;
71
72 /* Set the current drive */
73 Sda->CurrentDrive = Drive;
74
75 /* Return success */
76 return TRUE;
77 }
78
79 static BOOLEAN DosChangeDirectory(LPSTR Directory)
80 {
81 BYTE DriveNumber;
82 DWORD Attributes;
83 LPSTR Path;
84 CHAR CurrentDirectory[MAX_PATH];
85 CHAR DosDirectory[DOS_DIR_LENGTH];
86
87 /* Make sure the directory path is not too long */
88 if (strlen(Directory) >= DOS_DIR_LENGTH)
89 {
90 Sda->LastErrorCode = ERROR_PATH_NOT_FOUND;
91 return FALSE;
92 }
93
94 /* Check whether the directory string is of format "X:..." */
95 if (strlen(Directory) >= 2 && Directory[1] == ':')
96 {
97 /* Get the drive number */
98 DriveNumber = RtlUpperChar(Directory[0]) - 'A';
99
100 /* Make sure the drive exists */
101 if (DriveNumber >= SysVars->NumLocalDrives)
102 {
103 Sda->LastErrorCode = ERROR_PATH_NOT_FOUND;
104 return FALSE;
105 }
106 }
107 else
108 {
109 /* Keep the current drive number */
110 DriveNumber = Sda->CurrentDrive;
111 }
112
113 /* Get the file attributes */
114 Attributes = GetFileAttributesA(Directory);
115
116 /* Make sure the path exists and is a directory */
117 if ((Attributes == INVALID_FILE_ATTRIBUTES) ||
118 !(Attributes & FILE_ATTRIBUTE_DIRECTORY))
119 {
120 Sda->LastErrorCode = ERROR_PATH_NOT_FOUND;
121 return FALSE;
122 }
123
124 /* Check if this is the current drive */
125 if (DriveNumber == Sda->CurrentDrive)
126 {
127 /* Change the directory */
128 if (!SetCurrentDirectoryA(Directory))
129 {
130 Sda->LastErrorCode = LOWORD(GetLastError());
131 return FALSE;
132 }
133 }
134
135 /* Get the (possibly new) current directory (needed if we specified a relative directory) */
136 if (!GetCurrentDirectoryA(sizeof(CurrentDirectory), CurrentDirectory))
137 {
138 // TODO: Use some kind of default path?
139 return FALSE;
140 }
141
142 /* Convert it to a DOS path */
143 if (!GetShortPathNameA(CurrentDirectory, DosDirectory, sizeof(DosDirectory)))
144 {
145 // TODO: Use some kind of default path?
146 return FALSE;
147 }
148
149 /* Get the directory part of the path and set the current directory for the drive */
150 Path = strchr(DosDirectory, '\\');
151 if (Path != NULL)
152 {
153 Path++; // Skip the backslash
154 strncpy(DosData->CurrentDirectories[DriveNumber], Path, DOS_DIR_LENGTH);
155 }
156 else
157 {
158 DosData->CurrentDirectories[DriveNumber][0] = '\0';
159 }
160
161 /* Return success */
162 return TRUE;
163 }
164
165 static BOOLEAN DosControlBreak(VOID)
166 {
167 setCF(0);
168
169 /* Call interrupt 0x23 */
170 Int32Call(&DosContext, 0x23);
171
172 if (getCF())
173 {
174 DosTerminateProcess(Sda->CurrentPsp, 0, 0);
175 return TRUE;
176 }
177
178 return FALSE;
179 }
180
181 /* PUBLIC FUNCTIONS ***********************************************************/
182
183 VOID WINAPI DosInt20h(LPWORD Stack)
184 {
185 /*
186 * This is the exit interrupt (alias to INT 21h, AH=00h).
187 * CS must be the PSP segment.
188 */
189 DosTerminateProcess(Stack[STACK_CS], 0, 0);
190 }
191
192 VOID WINAPI DosInt21h(LPWORD Stack)
193 {
194 BYTE Character;
195 SYSTEMTIME SystemTime;
196 PCHAR String;
197
198 Sda->InDos++;
199
200 /* Save the value of SS:SP on entry in the PSP */
201 SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack =
202 MAKELONG(getSP() + (STACK_FLAGS + 1) * 2, getSS());
203
204 /* Check the value in the AH register */
205 switch (getAH())
206 {
207 /* Terminate Program */
208 case 0x00:
209 {
210 /* CS must be the PSP segment */
211 DosTerminateProcess(Stack[STACK_CS], 0, 0);
212 break;
213 }
214
215 /* Read Character from STDIN with Echo */
216 case 0x01:
217 {
218 DPRINT("INT 21h, AH = 01h\n");
219
220 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
221 DoEcho = TRUE;
222 Character = DosReadCharacter(DOS_INPUT_HANDLE);
223 DoEcho = FALSE;
224
225 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
226 // Check also Ctrl-P and set echo-to-printer flag.
227 // Ctrl-Z is not interpreted.
228
229 setAL(Character);
230 break;
231 }
232
233 /* Write Character to STDOUT */
234 case 0x02:
235 {
236 // FIXME: Under DOS 2+, output handle may be redirected!!!!
237 Character = getDL();
238 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
239
240 /*
241 * We return the output character (DOS 2.1+).
242 * Also, if we're going to output a TAB, then
243 * don't return a TAB but a SPACE instead.
244 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
245 * for more information.
246 */
247 setAL(Character == '\t' ? ' ' : Character);
248 break;
249 }
250
251 /* Read Character from STDAUX */
252 case 0x03:
253 {
254 // FIXME: Really read it from STDAUX!
255 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
256 // setAL(DosReadCharacter());
257 break;
258 }
259
260 /* Write Character to STDAUX */
261 case 0x04:
262 {
263 // FIXME: Really write it to STDAUX!
264 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
265 // DosPrintCharacter(getDL());
266 break;
267 }
268
269 /* Write Character to Printer */
270 case 0x05:
271 {
272 // FIXME: Really write it to printer!
273 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
274 DPRINT1("0x%p\n", getDL());
275 DPRINT1("\n\n-----------\n\n");
276 break;
277 }
278
279 /* Direct Console I/O */
280 case 0x06:
281 {
282 Character = getDL();
283
284 // FIXME: Under DOS 2+, output handle may be redirected!!!!
285
286 if (Character != 0xFF)
287 {
288 /* Output */
289 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
290
291 /*
292 * We return the output character (DOS 2.1+).
293 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
294 * for more information.
295 */
296 setAL(Character);
297 }
298 else
299 {
300 /* Input */
301 if (DosCheckInput())
302 {
303 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
304 setAL(DosReadCharacter(DOS_INPUT_HANDLE));
305 }
306 else
307 {
308 /* No character available */
309 Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF;
310 setAL(0x00);
311 }
312 }
313
314 break;
315 }
316
317 /* Character Input without Echo */
318 case 0x07:
319 case 0x08:
320 {
321 DPRINT("Char input without echo\n");
322
323 Character = DosReadCharacter(DOS_INPUT_HANDLE);
324
325 // FIXME: For 0x07, do not check Ctrl-C/Break.
326 // For 0x08, do check those control sequences and if needed,
327 // call INT 0x23.
328
329 setAL(Character);
330 break;
331 }
332
333 /* Write String to STDOUT */
334 case 0x09:
335 {
336 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
337
338 while (*String != '$')
339 {
340 DosPrintCharacter(DOS_OUTPUT_HANDLE, *String);
341 String++;
342 }
343
344 /*
345 * We return the terminating character (DOS 2.1+).
346 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
347 * for more information.
348 */
349 setAL('$'); // *String
350 break;
351 }
352
353 /* Read Buffered Input */
354 case 0x0A:
355 {
356 WORD Count = 0;
357 PDOS_INPUT_BUFFER InputBuffer = (PDOS_INPUT_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
358
359 DPRINT("Read Buffered Input\n");
360
361 while (Count < InputBuffer->MaxLength)
362 {
363 /* Try to read a character (wait) */
364 Character = DosReadCharacter(DOS_INPUT_HANDLE);
365
366 switch (Character)
367 {
368 /* Extended character */
369 case '\0':
370 {
371 /* Read the scancode */
372 DosReadCharacter(DOS_INPUT_HANDLE);
373 break;
374 }
375
376 /* Ctrl-C */
377 case 0x03:
378 {
379 DosPrintCharacter(DOS_OUTPUT_HANDLE, '^');
380 DosPrintCharacter(DOS_OUTPUT_HANDLE, 'C');
381
382 if (DosControlBreak())
383 {
384 /* Set the character to a newline to exit the loop */
385 Character = '\r';
386 }
387
388 break;
389 }
390
391 /* Backspace */
392 case '\b':
393 {
394 if (Count > 0)
395 {
396 Count--;
397
398 /* Erase the character */
399 DosPrintCharacter(DOS_OUTPUT_HANDLE, '\b');
400 DosPrintCharacter(DOS_OUTPUT_HANDLE, ' ');
401 DosPrintCharacter(DOS_OUTPUT_HANDLE, '\b');
402 }
403
404 break;
405 }
406
407 default:
408 {
409 /* Append it to the buffer */
410 InputBuffer->Buffer[Count] = Character;
411
412 /* Check if this is a special character */
413 if (Character < 0x20 && Character != 0x0A && Character != 0x0D)
414 {
415 DosPrintCharacter(DOS_OUTPUT_HANDLE, '^');
416 Character += 'A' - 1;
417 }
418
419 /* Echo the character */
420 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
421 }
422 }
423
424 if (Character == '\r') break;
425 if (Character == '\b') continue;
426 Count++; /* Carriage returns are NOT counted */
427 }
428
429 /* Update the length */
430 InputBuffer->Length = Count;
431
432 break;
433 }
434
435 /* Get STDIN Status */
436 case 0x0B:
437 {
438 setAL(DosCheckInput() ? 0xFF : 0x00);
439 break;
440 }
441
442 /* Flush Buffer and Read STDIN */
443 case 0x0C:
444 {
445 BYTE InputFunction = getAL();
446
447 /* Flush STDIN buffer */
448 DosFlushFileBuffers(DOS_INPUT_HANDLE);
449
450 /*
451 * If the input function number contained in AL is valid, i.e.
452 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
453 * recursively with AL == AH.
454 */
455 if (InputFunction == 0x01 || InputFunction == 0x06 ||
456 InputFunction == 0x07 || InputFunction == 0x08 ||
457 InputFunction == 0x0A)
458 {
459 /* Call ourselves recursively */
460 setAH(InputFunction);
461 DosInt21h(Stack);
462 }
463 break;
464 }
465
466 /* Disk Reset */
467 case 0x0D:
468 {
469 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Sda->CurrentPsp);
470
471 // TODO: Flush what's needed.
472 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
473
474 /* Clear CF in DOS 6 only */
475 if (PspBlock->DosVersion == 0x0006)
476 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
477
478 break;
479 }
480
481 /* Set Default Drive */
482 case 0x0E:
483 {
484 DosChangeDrive(getDL());
485 setAL(SysVars->NumLocalDrives);
486 break;
487 }
488
489 /* NULL Function for CP/M Compatibility */
490 case 0x18:
491 {
492 /*
493 * This function corresponds to the CP/M BDOS function
494 * "get bit map of logged drives", which is meaningless
495 * under MS-DOS.
496 *
497 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
498 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
499 * for more information.
500 */
501 setAL(0x00);
502 break;
503 }
504
505 /* Get Default Drive */
506 case 0x19:
507 {
508 setAL(Sda->CurrentDrive);
509 break;
510 }
511
512 /* Set Disk Transfer Area */
513 case 0x1A:
514 {
515 Sda->DiskTransferArea = MAKELONG(getDX(), getDS());
516 break;
517 }
518
519 /* NULL Function for CP/M Compatibility */
520 case 0x1D:
521 case 0x1E:
522 {
523 /*
524 * Function 0x1D corresponds to the CP/M BDOS function
525 * "get bit map of read-only drives", which is meaningless
526 * under MS-DOS.
527 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
528 * for more information.
529 *
530 * Function 0x1E corresponds to the CP/M BDOS function
531 * "set file attributes", which was meaningless under MS-DOS 1.x.
532 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
533 * for more information.
534 */
535 setAL(0x00);
536 break;
537 }
538
539 /* NULL Function for CP/M Compatibility */
540 case 0x20:
541 {
542 /*
543 * This function corresponds to the CP/M BDOS function
544 * "get/set default user (sublibrary) number", which is meaningless
545 * under MS-DOS.
546 *
547 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
548 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
549 * for more information.
550 */
551 setAL(0x00);
552 break;
553 }
554
555 /* Set Interrupt Vector */
556 case 0x25:
557 {
558 ULONG FarPointer = MAKELONG(getDX(), getDS());
559 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
560 getAL(), HIWORD(FarPointer), LOWORD(FarPointer));
561
562 /* Write the new far pointer to the IDT */
563 ((PULONG)BaseAddress)[getAL()] = FarPointer;
564 break;
565 }
566
567 /* Create New PSP */
568 case 0x26:
569 {
570 /* DOS 2+ assumes that the caller's CS is the segment of the PSP to copy */
571 DosClonePsp(getDX(), Stack[STACK_CS]);
572 break;
573 }
574
575 /* Parse Filename into FCB */
576 case 0x29:
577 {
578 PCHAR FileName = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI());
579 PDOS_FCB Fcb = (PDOS_FCB)SEG_OFF_TO_PTR(getES(), getDI());
580 BYTE Options = getAL();
581 CHAR FillChar = ' ';
582 UINT i;
583
584 if (FileName[1] == ':')
585 {
586 /* Set the drive number */
587 Fcb->DriveNumber = RtlUpperChar(FileName[0]) - 'A' + 1;
588
589 /* Skip to the file name part */
590 FileName += 2;
591 }
592 else
593 {
594 /* No drive number specified */
595 if (Options & (1 << 1)) Fcb->DriveNumber = Sda->CurrentDrive + 1;
596 else Fcb->DriveNumber = 0;
597 }
598
599 /* Parse the file name */
600 i = 0;
601 while ((*FileName > 0x20) && (i < 8))
602 {
603 if (*FileName == '.') break;
604 else if (*FileName == '*')
605 {
606 FillChar = '?';
607 break;
608 }
609
610 Fcb->FileName[i++] = RtlUpperChar(*FileName++);
611 }
612
613 /* Fill the whole field with blanks only if bit 2 is not set */
614 if ((FillChar != ' ') || (i != 0) || !(Options & (1 << 2)))
615 {
616 for (; i < 8; i++) Fcb->FileName[i] = FillChar;
617 }
618
619 /* Skip to the extension part */
620 while (*FileName > 0x20 && *FileName != '.') FileName++;
621 if (*FileName == '.') FileName++;
622
623 /* Now parse the extension */
624 i = 0;
625 FillChar = ' ';
626
627 while ((*FileName > 0x20) && (i < 3))
628 {
629 if (*FileName == '*')
630 {
631 FillChar = '?';
632 break;
633 }
634
635 Fcb->FileExt[i++] = RtlUpperChar(*FileName++);
636 }
637
638 /* Fill the whole field with blanks only if bit 3 is not set */
639 if ((FillChar != ' ') || (i != 0) || !(Options & (1 << 3)))
640 {
641 for (; i < 3; i++) Fcb->FileExt[i] = FillChar;
642 }
643
644 break;
645 }
646
647 /* Get System Date */
648 case 0x2A:
649 {
650 GetLocalTime(&SystemTime);
651 setCX(SystemTime.wYear);
652 setDX(MAKEWORD(SystemTime.wDay, SystemTime.wMonth));
653 setAL(SystemTime.wDayOfWeek);
654 break;
655 }
656
657 /* Set System Date */
658 case 0x2B:
659 {
660 GetLocalTime(&SystemTime);
661 SystemTime.wYear = getCX();
662 SystemTime.wMonth = getDH();
663 SystemTime.wDay = getDL();
664
665 /* Return success or failure */
666 setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF);
667 break;
668 }
669
670 /* Get System Time */
671 case 0x2C:
672 {
673 GetLocalTime(&SystemTime);
674 setCX(MAKEWORD(SystemTime.wMinute, SystemTime.wHour));
675 setDX(MAKEWORD(SystemTime.wMilliseconds / 10, SystemTime.wSecond));
676 break;
677 }
678
679 /* Set System Time */
680 case 0x2D:
681 {
682 GetLocalTime(&SystemTime);
683 SystemTime.wHour = getCH();
684 SystemTime.wMinute = getCL();
685 SystemTime.wSecond = getDH();
686 SystemTime.wMilliseconds = getDL() * 10; // In hundredths of seconds
687
688 /* Return success or failure */
689 setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF);
690 break;
691 }
692
693 /* Get Disk Transfer Area */
694 case 0x2F:
695 {
696 setES(HIWORD(Sda->DiskTransferArea));
697 setBX(LOWORD(Sda->DiskTransferArea));
698 break;
699 }
700
701 /* Get DOS Version */
702 case 0x30:
703 {
704 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Sda->CurrentPsp);
705
706 /*
707 * DOS 2+ - GET DOS VERSION
708 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
709 * for more information.
710 */
711
712 if (LOBYTE(PspBlock->DosVersion) < 5 || getAL() == 0x00)
713 {
714 /*
715 * Return DOS OEM number:
716 * 0x00 for IBM PC-DOS
717 * 0x02 for packaged MS-DOS
718 * 0xFF for NT DOS
719 */
720 setBH(0xFF);
721 }
722
723 if (LOBYTE(PspBlock->DosVersion) >= 5 && getAL() == 0x01)
724 {
725 /*
726 * Return version flag:
727 * 1 << 3 if DOS is in ROM,
728 * 0 (reserved) if not.
729 */
730 setBH(0x00);
731 }
732
733 /* Return DOS 24-bit user serial number in BL:CX */
734 setBL(0x00);
735 setCX(0x0000);
736
737 /*
738 * Return DOS version: Minor:Major in AH:AL
739 * The Windows NT DOS box returns version 5.00, subject to SETVER.
740 */
741 setAX(PspBlock->DosVersion);
742
743 break;
744 }
745
746 /* Terminate and Stay Resident */
747 case 0x31:
748 {
749 DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
750 DosTerminateProcess(Sda->CurrentPsp, getAL(), getDX());
751 break;
752 }
753
754 /* Extended functionalities */
755 case 0x33:
756 {
757 switch (getAL())
758 {
759 /*
760 * DOS 4+ - GET BOOT DRIVE
761 */
762 case 0x05:
763 {
764 setDL(SysVars->BootDrive);
765 break;
766 }
767
768 /*
769 * DOS 5+ - GET TRUE VERSION NUMBER
770 * This function always returns the true version number, unlike
771 * AH=30h, whose return value may be changed with SETVER.
772 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
773 * for more information.
774 */
775 case 0x06:
776 {
777 /*
778 * Return the true DOS version: Minor:Major in BH:BL
779 * The Windows NT DOS box returns BX=3205h (version 5.50).
780 */
781 setBX(NTDOS_VERSION);
782
783 /* DOS revision 0 */
784 setDL(0x00);
785
786 /* Unpatched DOS */
787 setDH(0x00);
788
789 break;
790 }
791
792 default: // goto Default;
793 {
794 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
795 getAH(), getAL());
796 }
797 }
798
799 break;
800 }
801
802 /* Get Address of InDOS flag */
803 case 0x34:
804 {
805 setES(DOS_DATA_SEGMENT);
806 setBX(DOS_DATA_OFFSET(Sda.InDos));
807 break;
808 }
809
810 /* Get Interrupt Vector */
811 case 0x35:
812 {
813 ULONG FarPointer = ((PULONG)BaseAddress)[getAL()];
814
815 /* Read the address from the IDT into ES:BX */
816 setES(HIWORD(FarPointer));
817 setBX(LOWORD(FarPointer));
818 break;
819 }
820
821 /* Get Free Disk Space */
822 case 0x36:
823 {
824 CHAR RootPath[] = "?:\\";
825 DWORD SectorsPerCluster;
826 DWORD BytesPerSector;
827 DWORD NumberOfFreeClusters;
828 DWORD TotalNumberOfClusters;
829
830 if (getDL() == 0x00)
831 RootPath[0] = 'A' + Sda->CurrentDrive;
832 else
833 RootPath[0] = 'A' + getDL() - 1;
834
835 if (GetDiskFreeSpaceA(RootPath,
836 &SectorsPerCluster,
837 &BytesPerSector,
838 &NumberOfFreeClusters,
839 &TotalNumberOfClusters))
840 {
841 setAX(LOWORD(SectorsPerCluster));
842 setCX(LOWORD(BytesPerSector));
843 setBX(min(NumberOfFreeClusters, 0xFFFF));
844 setDX(min(TotalNumberOfClusters, 0xFFFF));
845 }
846 else
847 {
848 /* Error */
849 setAX(0xFFFF);
850 }
851
852 break;
853 }
854
855 /* SWITCH character - AVAILDEV */
856 case 0x37:
857 {
858 switch (getAL())
859 {
860 /*
861 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
862 * This setting is ignored by MS-DOS 4.0+.
863 * MS-DOS 5+ always return AL=00h/DL=2Fh.
864 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
865 * for more information.
866 */
867 case 0x00:
868 setDL('/');
869 setAL(0x00);
870 break;
871
872 /*
873 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
874 * This setting is ignored by MS-DOS 5+.
875 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
876 * for more information.
877 */
878 case 0x01:
879 // getDL();
880 setAL(0xFF);
881 break;
882
883 /*
884 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
885 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
886 * for more information.
887 */
888 case 0x02:
889 // setDL();
890 setAL(0xFF);
891 break;
892
893 /*
894 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
895 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
896 * for more information.
897 */
898 case 0x03:
899 // getDL();
900 setAL(0xFF);
901 break;
902
903 /* Invalid subfunction */
904 default:
905 setAL(0xFF);
906 break;
907 }
908
909 break;
910 }
911
912 /* Get/Set Country-dependent Information */
913 case 0x38:
914 {
915 WORD CountryId = getAL() < 0xFF ? getAL() : getBX();
916 WORD ErrorCode;
917
918 ErrorCode = DosGetCountryInfo(&CountryId,
919 (PDOS_COUNTRY_INFO)SEG_OFF_TO_PTR(getDS(), getDX()));
920
921 if (ErrorCode == ERROR_SUCCESS)
922 {
923 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
924 setBX(CountryId);
925 }
926 else
927 {
928 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
929 setAX(ErrorCode);
930 }
931
932 break;
933 }
934
935 /* Create Directory */
936 case 0x39:
937 {
938 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
939
940 if (CreateDirectoryA(String, NULL))
941 {
942 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
943 }
944 else
945 {
946 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
947 setAX(LOWORD(GetLastError()));
948 }
949
950 break;
951 }
952
953 /* Remove Directory */
954 case 0x3A:
955 {
956 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
957
958 if (RemoveDirectoryA(String))
959 {
960 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
961 }
962 else
963 {
964 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
965 setAX(LOWORD(GetLastError()));
966 }
967
968 break;
969 }
970
971 /* Set Current Directory */
972 case 0x3B:
973 {
974 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
975
976 if (DosChangeDirectory(String))
977 {
978 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
979 }
980 else
981 {
982 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
983 setAX(Sda->LastErrorCode);
984 }
985
986 break;
987 }
988
989 /* Create or Truncate File */
990 case 0x3C:
991 {
992 WORD FileHandle;
993 WORD ErrorCode = DosCreateFile(&FileHandle,
994 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
995 CREATE_ALWAYS,
996 getCX());
997
998 if (ErrorCode == ERROR_SUCCESS)
999 {
1000 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1001 setAX(FileHandle);
1002 }
1003 else
1004 {
1005 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1006 setAX(ErrorCode);
1007 }
1008
1009 break;
1010 }
1011
1012 /* Open File or Device */
1013 case 0x3D:
1014 {
1015 WORD FileHandle;
1016 LPCSTR FileName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1017 WORD ErrorCode = DosOpenFile(&FileHandle, FileName, getAL());
1018
1019 if (ErrorCode == ERROR_SUCCESS)
1020 {
1021 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1022 setAX(FileHandle);
1023 }
1024 else
1025 {
1026 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1027 setAX(ErrorCode);
1028 }
1029
1030 break;
1031 }
1032
1033 /* Close File or Device */
1034 case 0x3E:
1035 {
1036 if (DosCloseHandle(getBX()))
1037 {
1038 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1039 }
1040 else
1041 {
1042 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1043 setAX(ERROR_INVALID_HANDLE);
1044 }
1045
1046 break;
1047 }
1048
1049 /* Read from File or Device */
1050 case 0x3F:
1051 {
1052 WORD BytesRead = 0;
1053 WORD ErrorCode;
1054
1055 DPRINT("DosReadFile(0x%04X)\n", getBX());
1056
1057 DoEcho = TRUE;
1058 ErrorCode = DosReadFile(getBX(),
1059 MAKELONG(getDX(), getDS()),
1060 getCX(),
1061 &BytesRead);
1062 DoEcho = FALSE;
1063
1064 if (ErrorCode == ERROR_SUCCESS)
1065 {
1066 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1067 setAX(BytesRead);
1068 }
1069 else if (ErrorCode != ERROR_NOT_READY)
1070 {
1071 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1072 setAX(ErrorCode);
1073 }
1074
1075 break;
1076 }
1077
1078 /* Write to File or Device */
1079 case 0x40:
1080 {
1081 WORD BytesWritten = 0;
1082 WORD ErrorCode = DosWriteFile(getBX(),
1083 MAKELONG(getDX(), getDS()),
1084 getCX(),
1085 &BytesWritten);
1086
1087 if (ErrorCode == ERROR_SUCCESS)
1088 {
1089 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1090 setAX(BytesWritten);
1091 }
1092 else
1093 {
1094 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1095 setAX(ErrorCode);
1096 }
1097
1098 break;
1099 }
1100
1101 /* Delete File */
1102 case 0x41:
1103 {
1104 LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1105
1106 if (demFileDelete(FileName) == ERROR_SUCCESS)
1107 {
1108 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1109 /*
1110 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
1111 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
1112 */
1113 setAL(RtlUpperChar(FileName[0]) - 'A');
1114 }
1115 else
1116 {
1117 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1118 setAX(GetLastError());
1119 }
1120
1121 break;
1122 }
1123
1124 /* Seek File */
1125 case 0x42:
1126 {
1127 DWORD NewLocation;
1128 WORD ErrorCode = DosSeekFile(getBX(),
1129 MAKELONG(getDX(), getCX()),
1130 getAL(),
1131 &NewLocation);
1132
1133 if (ErrorCode == ERROR_SUCCESS)
1134 {
1135 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1136
1137 /* Return the new offset in DX:AX */
1138 setDX(HIWORD(NewLocation));
1139 setAX(LOWORD(NewLocation));
1140 }
1141 else
1142 {
1143 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1144 setAX(ErrorCode);
1145 }
1146
1147 break;
1148 }
1149
1150 /* Get/Set File Attributes */
1151 case 0x43:
1152 {
1153 DWORD Attributes;
1154 LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1155
1156 if (getAL() == 0x00)
1157 {
1158 /* Get the attributes */
1159 Attributes = GetFileAttributesA(FileName);
1160
1161 /* Check if it failed */
1162 if (Attributes == INVALID_FILE_ATTRIBUTES)
1163 {
1164 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1165 setAX(GetLastError());
1166 }
1167 else
1168 {
1169 /* Return the attributes that DOS can understand */
1170 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1171 setCX(Attributes & 0x00FF);
1172 }
1173 }
1174 else if (getAL() == 0x01)
1175 {
1176 /* Try to set the attributes */
1177 if (SetFileAttributesA(FileName, getCL()))
1178 {
1179 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1180 }
1181 else
1182 {
1183 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1184 setAX(GetLastError());
1185 }
1186 }
1187 else
1188 {
1189 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1190 setAX(ERROR_INVALID_FUNCTION);
1191 }
1192
1193 break;
1194 }
1195
1196 /* IOCTL */
1197 case 0x44:
1198 {
1199 WORD Length = getCX();
1200
1201 if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length))
1202 {
1203 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1204 setAX(Length);
1205 }
1206 else
1207 {
1208 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1209 setAX(Sda->LastErrorCode);
1210 }
1211
1212 break;
1213 }
1214
1215 /* Duplicate Handle */
1216 case 0x45:
1217 {
1218 WORD NewHandle = DosDuplicateHandle(getBX());
1219
1220 if (NewHandle != INVALID_DOS_HANDLE)
1221 {
1222 setAX(NewHandle);
1223 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1224 }
1225 else
1226 {
1227 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1228 setAX(Sda->LastErrorCode);
1229 }
1230
1231 break;
1232 }
1233
1234 /* Force Duplicate Handle */
1235 case 0x46:
1236 {
1237 if (DosForceDuplicateHandle(getBX(), getCX()))
1238 {
1239 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1240 }
1241 else
1242 {
1243 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1244 setAX(ERROR_INVALID_HANDLE);
1245 }
1246
1247 break;
1248 }
1249
1250 /* Get Current Directory */
1251 case 0x47:
1252 {
1253 BYTE DriveNumber = getDL();
1254 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI());
1255
1256 /* Get the real drive number */
1257 if (DriveNumber == 0)
1258 {
1259 DriveNumber = Sda->CurrentDrive;
1260 }
1261 else
1262 {
1263 /* Decrement DriveNumber since it was 1-based */
1264 DriveNumber--;
1265 }
1266
1267 if (DriveNumber < SysVars->NumLocalDrives)
1268 {
1269 /*
1270 * Copy the current directory into the target buffer.
1271 * It doesn't contain the drive letter and the backslash.
1272 */
1273 strncpy(String, DosData->CurrentDirectories[DriveNumber], DOS_DIR_LENGTH);
1274 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1275 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
1276 }
1277 else
1278 {
1279 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1280 setAX(ERROR_INVALID_DRIVE);
1281 }
1282
1283 break;
1284 }
1285
1286 /* Allocate Memory */
1287 case 0x48:
1288 {
1289 WORD MaxAvailable = 0;
1290 WORD Segment = DosAllocateMemory(getBX(), &MaxAvailable);
1291
1292 if (Segment != 0)
1293 {
1294 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1295 setAX(Segment);
1296 }
1297 else
1298 {
1299 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1300 setAX(Sda->LastErrorCode);
1301 setBX(MaxAvailable);
1302 }
1303
1304 break;
1305 }
1306
1307 /* Free Memory */
1308 case 0x49:
1309 {
1310 if (DosFreeMemory(getES()))
1311 {
1312 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1313 }
1314 else
1315 {
1316 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1317 setAX(Sda->LastErrorCode);
1318 }
1319
1320 break;
1321 }
1322
1323 /* Resize Memory Block */
1324 case 0x4A:
1325 {
1326 WORD Size;
1327
1328 if (DosResizeMemory(getES(), getBX(), &Size))
1329 {
1330 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1331 }
1332 else
1333 {
1334 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1335 setAX(Sda->LastErrorCode);
1336 setBX(Size);
1337 }
1338
1339 break;
1340 }
1341
1342 /* Execute */
1343 case 0x4B:
1344 {
1345 BYTE OrgAL = getAL();
1346 LPSTR ProgramName = SEG_OFF_TO_PTR(getDS(), getDX());
1347 PDOS_EXEC_PARAM_BLOCK ParamBlock = SEG_OFF_TO_PTR(getES(), getBX());
1348 WORD ErrorCode;
1349
1350 if (OrgAL <= DOS_LOAD_OVERLAY)
1351 {
1352 DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)OrgAL;
1353
1354 if (LoadType == DOS_LOAD_AND_EXECUTE)
1355 {
1356 /* Create a new process */
1357 ErrorCode = DosCreateProcess(ProgramName,
1358 ParamBlock,
1359 MAKELONG(Stack[STACK_IP], Stack[STACK_CS]));
1360 }
1361 else
1362 {
1363 /* Just load an executable */
1364 ErrorCode = DosLoadExecutable(LoadType,
1365 ProgramName,
1366 ParamBlock,
1367 NULL,
1368 NULL,
1369 MAKELONG(Stack[STACK_IP], Stack[STACK_CS]));
1370 }
1371 }
1372 else if (OrgAL == 0x05)
1373 {
1374 // http://www.ctyme.com/intr/rb-2942.htm
1375 DPRINT1("Set execution state is UNIMPLEMENTED\n");
1376 ErrorCode = ERROR_CALL_NOT_IMPLEMENTED;
1377 }
1378 else
1379 {
1380 ErrorCode = ERROR_INVALID_FUNCTION;
1381 }
1382
1383 if (ErrorCode == ERROR_SUCCESS)
1384 {
1385 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1386 }
1387 else
1388 {
1389 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1390 setAX(ErrorCode);
1391 }
1392
1393 break;
1394 }
1395
1396 /* Terminate with Return Code */
1397 case 0x4C:
1398 {
1399 DosTerminateProcess(Sda->CurrentPsp, getAL(), 0);
1400 break;
1401 }
1402
1403 /* Get Return Code (ERRORLEVEL) */
1404 case 0x4D:
1405 {
1406 /*
1407 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
1408 * DosErrorLevel is cleared after being read by this function.
1409 */
1410 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1411 setAX(Sda->ErrorLevel);
1412 Sda->ErrorLevel = 0x0000; // Clear it
1413 break;
1414 }
1415
1416 /* Find First File */
1417 case 0x4E:
1418 {
1419 WORD Result = (WORD)demFileFindFirst(FAR_POINTER(Sda->DiskTransferArea),
1420 SEG_OFF_TO_PTR(getDS(), getDX()),
1421 getCX());
1422
1423 setAX(Result);
1424
1425 if (Result == ERROR_SUCCESS)
1426 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1427 else
1428 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1429
1430 break;
1431 }
1432
1433 /* Find Next File */
1434 case 0x4F:
1435 {
1436 WORD Result = (WORD)demFileFindNext(FAR_POINTER(Sda->DiskTransferArea));
1437
1438 setAX(Result);
1439
1440 if (Result == ERROR_SUCCESS)
1441 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1442 else
1443 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1444
1445 break;
1446 }
1447
1448 /* Internal - Set Current Process ID (Set PSP Address) */
1449 case 0x50:
1450 {
1451 DosSetProcessContext(getBX());
1452 break;
1453 }
1454
1455 /* Internal - Get Current Process ID (Get PSP Address) */
1456 case 0x51:
1457 /* Get Current PSP Address */
1458 case 0x62:
1459 {
1460 /*
1461 * Undocumented AH=51h is identical to the documented AH=62h.
1462 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
1463 * and http://www.ctyme.com/intr/rb-3140.htm
1464 * for more information.
1465 */
1466 setBX(Sda->CurrentPsp);
1467 break;
1468 }
1469
1470 /* Internal - Get "List of lists" (SYSVARS) */
1471 case 0x52:
1472 {
1473 /*
1474 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
1475 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
1476 * for more information.
1477 */
1478
1479 /* Return the DOS "list of lists" in ES:BX */
1480 setES(DOS_DATA_SEGMENT);
1481 setBX(DOS_DATA_OFFSET(SysVars.FirstDpb));
1482 break;
1483 }
1484
1485 /* Create Child PSP */
1486 case 0x55:
1487 {
1488 DosCreatePsp(getDX(), getSI());
1489 DosSetProcessContext(getDX());
1490 break;
1491 }
1492
1493 /* Rename File */
1494 case 0x56:
1495 {
1496 LPSTR ExistingFileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1497 LPSTR NewFileName = (LPSTR)SEG_OFF_TO_PTR(getES(), getDI());
1498
1499 /*
1500 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
1501 * for more information.
1502 */
1503
1504 if (MoveFileA(ExistingFileName, NewFileName))
1505 {
1506 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1507 }
1508 else
1509 {
1510 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1511 setAX(GetLastError());
1512 }
1513
1514 break;
1515 }
1516
1517 /* File Attributes */
1518 case 0x57:
1519 {
1520 switch (getAL())
1521 {
1522 /* Get File's last-written Date and Time */
1523 case 0x00:
1524 {
1525 PDOS_FILE_DESCRIPTOR Descriptor = DosGetHandleFileDescriptor(getBX());
1526 FILETIME LastWriteTime;
1527 WORD FileDate, FileTime;
1528
1529 if (Descriptor == NULL)
1530 {
1531 /* Invalid handle */
1532 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1533 // Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1534 setAX(ERROR_INVALID_HANDLE);
1535 break;
1536 }
1537
1538 if (Descriptor->DeviceInfo & FILE_INFO_DEVICE)
1539 {
1540 /* Invalid for devices */
1541 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1542 // setAX(ERROR_INVALID_FUNCTION);
1543 setAX(ERROR_INVALID_HANDLE);
1544 break;
1545 }
1546
1547 /*
1548 * Retrieve the last-written Win32 date and time,
1549 * and convert it to DOS format.
1550 */
1551 if (!GetFileTime(Descriptor->Win32Handle,
1552 NULL, NULL, &LastWriteTime) ||
1553 !FileTimeToDosDateTime(&LastWriteTime,
1554 &FileDate, &FileTime))
1555 {
1556 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1557 setAX(GetLastError());
1558 break;
1559 }
1560
1561 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1562 setCX(FileTime);
1563 setDX(FileDate);
1564 break;
1565 }
1566
1567 /* Set File's last-written Date and Time */
1568 case 0x01:
1569 {
1570 PDOS_FILE_DESCRIPTOR Descriptor = DosGetHandleFileDescriptor(getBX());
1571 FILETIME LastWriteTime;
1572 WORD FileDate = getDX();
1573 WORD FileTime = getCX();
1574
1575 if (Descriptor == NULL)
1576 {
1577 /* Invalid handle */
1578 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1579 // Sda->LastErrorCode = ERROR_INVALID_HANDLE;
1580 setAX(ERROR_INVALID_HANDLE);
1581 break;
1582 }
1583
1584 if (Descriptor->DeviceInfo & FILE_INFO_DEVICE)
1585 {
1586 /* Invalid for devices */
1587 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1588 // setAX(ERROR_INVALID_FUNCTION);
1589 setAX(ERROR_INVALID_HANDLE);
1590 break;
1591 }
1592
1593 /*
1594 * Convert the new last-written DOS date and time
1595 * to Win32 format and set it.
1596 */
1597 if (!DosDateTimeToFileTime(FileDate, FileTime,
1598 &LastWriteTime) ||
1599 !SetFileTime(Descriptor->Win32Handle,
1600 NULL, NULL, &LastWriteTime))
1601 {
1602 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1603 setAX(GetLastError());
1604 break;
1605 }
1606
1607 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1608 break;
1609 }
1610
1611 default: // goto Default;
1612 {
1613 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1614 getAH(), getAL());
1615 }
1616 }
1617
1618 break;
1619 }
1620
1621 /* Get/Set Memory Management Options */
1622 case 0x58:
1623 {
1624 switch (getAL())
1625 {
1626 /* Get allocation strategy */
1627 case 0x00:
1628 {
1629 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1630 setAX(Sda->AllocStrategy);
1631 break;
1632 }
1633
1634 /* Set allocation strategy */
1635 case 0x01:
1636 {
1637 if ((getBL() & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
1638 == (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
1639 {
1640 /* Can't set both */
1641 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1642 setAX(ERROR_INVALID_PARAMETER);
1643 break;
1644 }
1645
1646 if ((getBL() & ~(DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
1647 > DOS_ALLOC_LAST_FIT)
1648 {
1649 /* Invalid allocation strategy */
1650 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1651 setAX(ERROR_INVALID_PARAMETER);
1652 break;
1653 }
1654
1655 Sda->AllocStrategy = getBL();
1656 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1657 break;
1658 }
1659
1660 /* Get UMB link state */
1661 case 0x02:
1662 {
1663 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1664 setAL(SysVars->UmbLinked ? 0x01 : 0x00);
1665 break;
1666 }
1667
1668 /* Set UMB link state */
1669 case 0x03:
1670 {
1671 BOOLEAN Success;
1672
1673 if (getBX())
1674 Success = DosLinkUmb();
1675 else
1676 Success = DosUnlinkUmb();
1677
1678 if (Success)
1679 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1680 else
1681 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1682
1683 break;
1684 }
1685
1686 /* Invalid or unsupported function */
1687 default:
1688 {
1689 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1690 setAX(ERROR_INVALID_FUNCTION);
1691 }
1692 }
1693
1694 break;
1695 }
1696
1697 /* Get Extended Error Information */
1698 case 0x59:
1699 {
1700 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
1701 getBX());
1702 break;
1703 }
1704
1705 /* Create Temporary File */
1706 case 0x5A:
1707 {
1708 LPSTR PathName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1709 LPSTR FileName = PathName; // The buffer for the path and the full file name is the same.
1710 UINT uRetVal;
1711 WORD FileHandle;
1712 WORD ErrorCode;
1713
1714 /*
1715 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
1716 * for more information.
1717 */
1718
1719 // FIXME: Check for buffer validity?
1720 // It should be a ASCIIZ path ending with a '\' + 13 zero bytes
1721 // to receive the generated filename.
1722
1723 /* First create the temporary file */
1724 uRetVal = GetTempFileNameA(PathName, NULL, 0, FileName);
1725 if (uRetVal == 0)
1726 {
1727 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1728 setAX(GetLastError());
1729 break;
1730 }
1731
1732 /* Now try to open it in read/write access */
1733 ErrorCode = DosOpenFile(&FileHandle, FileName, 2);
1734 if (ErrorCode == ERROR_SUCCESS)
1735 {
1736 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1737 setAX(FileHandle);
1738 }
1739 else
1740 {
1741 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1742 setAX(ErrorCode);
1743 }
1744
1745 break;
1746 }
1747
1748 /* Create New File */
1749 case 0x5B:
1750 {
1751 WORD FileHandle;
1752 WORD ErrorCode = DosCreateFile(&FileHandle,
1753 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
1754 CREATE_NEW,
1755 getCX());
1756
1757 if (ErrorCode == ERROR_SUCCESS)
1758 {
1759 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1760 setAX(FileHandle);
1761 }
1762 else
1763 {
1764 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1765 setAX(ErrorCode);
1766 }
1767
1768 break;
1769 }
1770
1771 /* Lock/Unlock Region of File */
1772 case 0x5C:
1773 {
1774 if (getAL() == 0x00)
1775 {
1776 /* Lock region of file */
1777 if (DosLockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1778 {
1779 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1780 }
1781 else
1782 {
1783 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1784 setAX(Sda->LastErrorCode);
1785 }
1786 }
1787 else if (getAL() == 0x01)
1788 {
1789 /* Unlock region of file */
1790 if (DosUnlockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
1791 {
1792 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1793 }
1794 else
1795 {
1796 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1797 setAX(Sda->LastErrorCode);
1798 }
1799 }
1800 else
1801 {
1802 /* Invalid subfunction */
1803 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1804 setAX(ERROR_INVALID_FUNCTION);
1805 }
1806
1807 break;
1808 }
1809
1810 /* Canonicalize File Name or Path */
1811 case 0x60:
1812 {
1813 /*
1814 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
1815 * for more information.
1816 */
1817
1818 /*
1819 * We suppose that the DOS app gave to us a valid
1820 * 128-byte long buffer for the canonicalized name.
1821 */
1822 DWORD dwRetVal = GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
1823 128,
1824 SEG_OFF_TO_PTR(getES(), getDI()),
1825 NULL);
1826 if (dwRetVal == 0)
1827 {
1828 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1829 setAX(GetLastError());
1830 }
1831 else
1832 {
1833 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1834 setAX(0x0000);
1835 }
1836
1837 // FIXME: Convert the full path name into short version.
1838 // We cannot reliably use GetShortPathName, because it fails
1839 // if the path name given doesn't exist. However this DOS
1840 // function AH=60h should be able to work even for non-existing
1841 // path and file names.
1842
1843 break;
1844 }
1845
1846 /* Miscellaneous Internal Functions */
1847 case 0x5D:
1848 {
1849 switch (getAL())
1850 {
1851 /* Get Swappable Data Area */
1852 case 0x06:
1853 {
1854 setDS(DOS_DATA_SEGMENT);
1855 setSI(DOS_DATA_OFFSET(Sda.ErrorMode));
1856 setCX(sizeof(DOS_SDA));
1857 setDX(FIELD_OFFSET(DOS_SDA, LastAX));
1858
1859 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1860 break;
1861 }
1862
1863 default: // goto Default;
1864 {
1865 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1866 getAH(), getAL());
1867 }
1868 }
1869
1870 break;
1871 }
1872
1873 /* Extended Country Information */
1874 case 0x65:
1875 {
1876 switch (getAL())
1877 {
1878 case 0x01: case 0x02: case 0x03:
1879 case 0x04: case 0x05: case 0x06:
1880 case 0x07:
1881 {
1882 WORD BufferSize = getCX();
1883 WORD ErrorCode;
1884 ErrorCode = DosGetCountryInfoEx(getAL(),
1885 getBX(),
1886 getDX(),
1887 (PDOS_COUNTRY_INFO_2)SEG_OFF_TO_PTR(getES(), getDI()),
1888 &BufferSize);
1889 if (ErrorCode == ERROR_SUCCESS)
1890 {
1891 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1892 setCX(BufferSize);
1893 }
1894 else
1895 {
1896 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1897 setAX(ErrorCode);
1898 }
1899
1900 break;
1901 }
1902
1903 /* Country-dependent Character Capitalization -- Character */
1904 case 0x20:
1905 /* Country-dependent Filename Capitalization -- Character */
1906 case 0xA0:
1907 {
1908 setDL(DosToUpper(getDL()));
1909 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1910 // setAX(ERROR_SUCCESS);
1911 break;
1912 }
1913
1914 /* Country-dependent Character Capitalization -- Counted ASCII String */
1915 case 0x21:
1916 /* Country-dependent Filename Capitalization -- Counted ASCII String */
1917 case 0xA1:
1918 {
1919 PCHAR Str = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
1920 // FIXME: Check for NULL ptr!!
1921 DosToUpperStrN(Str, Str, getCX());
1922 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1923 // setAX(ERROR_SUCCESS);
1924 break;
1925 }
1926
1927 /* Country-dependent Character Capitalization -- ASCIIZ String */
1928 case 0x22:
1929 /* Country-dependent Filename Capitalization -- ASCIIZ String */
1930 case 0xA2:
1931 {
1932 PSTR Str = (PSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1933 // FIXME: Check for NULL ptr!!
1934 DosToUpperStrZ(Str, Str);
1935 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1936 // setAX(ERROR_SUCCESS);
1937 break;
1938 }
1939
1940 /* Determine if Character represents YES/NO Response */
1941 case 0x23:
1942 {
1943 setAX(DosIfCharYesNo(MAKEWORD(getDL(), getDH())));
1944 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1945 break;
1946 }
1947
1948 default: // goto Default;
1949 {
1950 DPRINT1("INT 21h, AH = %02Xh, subfunction AL = %02Xh NOT IMPLEMENTED\n",
1951 getAH(), getAL());
1952 }
1953 }
1954
1955 break;
1956 }
1957
1958 /* Set Handle Count */
1959 case 0x67:
1960 {
1961 if (!DosResizeHandleTable(getBX()))
1962 {
1963 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1964 setAX(Sda->LastErrorCode);
1965 }
1966 else Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1967
1968 break;
1969 }
1970
1971 /* Commit File */
1972 case 0x68:
1973 case 0x6A:
1974 {
1975 /*
1976 * Function 6Ah is identical to function 68h,
1977 * and sets AH to 68h if success.
1978 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
1979 * for more information.
1980 */
1981 setAH(0x68);
1982
1983 if (DosFlushFileBuffers(getBX()))
1984 {
1985 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1986 }
1987 else
1988 {
1989 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1990 setAX(GetLastError());
1991 }
1992
1993 break;
1994 }
1995
1996 /* Extended Open/Create */
1997 case 0x6C:
1998 {
1999 WORD FileHandle;
2000 WORD CreationStatus;
2001 WORD ErrorCode;
2002
2003 /* Check for AL == 00 */
2004 if (getAL() != 0x00)
2005 {
2006 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2007 setAX(ERROR_INVALID_FUNCTION);
2008 break;
2009 }
2010
2011 /*
2012 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
2013 * for the full detailed description.
2014 *
2015 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
2016 */
2017
2018 ErrorCode = DosCreateFileEx(&FileHandle,
2019 &CreationStatus,
2020 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getSI()),
2021 getBL(),
2022 getDL(),
2023 getCX());
2024
2025 if (ErrorCode == ERROR_SUCCESS)
2026 {
2027 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2028 setCX(CreationStatus);
2029 setAX(FileHandle);
2030 }
2031 else
2032 {
2033 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2034 setAX(ErrorCode);
2035 }
2036
2037 break;
2038 }
2039
2040 /* Unsupported */
2041 default: // Default:
2042 {
2043 DPRINT1("DOS Function INT 21h, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n",
2044 getAH(), getAL());
2045
2046 setAL(0); // Some functions expect AL to be 0 when it's not supported.
2047 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2048 }
2049 }
2050
2051 Sda->InDos--;
2052 }
2053
2054 VOID WINAPI DosBreakInterrupt(LPWORD Stack)
2055 {
2056 /* Set CF to terminate the running process */
2057 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2058 }
2059
2060 VOID WINAPI DosAbsoluteRead(LPWORD Stack)
2061 {
2062 /*
2063 * This call should leave the flags on the stack for some reason,
2064 * so move the stack by one word.
2065 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
2066 */
2067 Stack[STACK_INT_NUM] = Stack[STACK_IP];
2068 Stack[STACK_IP] = Stack[STACK_CS];
2069 Stack[STACK_CS] = Stack[STACK_FLAGS];
2070 setSP(LOWORD(getSP() - 2));
2071
2072 // TODO: NOT IMPLEMENTED;
2073 UNIMPLEMENTED;
2074
2075 /* General failure */
2076 setAX(0x800C);
2077 Stack[STACK_FLAGS - 1] |= EMULATOR_FLAG_CF;
2078 }
2079
2080 VOID WINAPI DosAbsoluteWrite(LPWORD Stack)
2081 {
2082 /*
2083 * This call should leave the flags on the stack for some reason,
2084 * so move the stack by one word.
2085 * See: http://www.techhelpmanual.com/565-int_25h_26h__absolute_disk_read_write.html
2086 */
2087 Stack[STACK_INT_NUM] = Stack[STACK_IP];
2088 Stack[STACK_IP] = Stack[STACK_CS];
2089 Stack[STACK_CS] = Stack[STACK_FLAGS];
2090 setSP(LOWORD(getSP() - 2));
2091
2092 // TODO: NOT IMPLEMENTED;
2093 UNIMPLEMENTED;
2094
2095 /* General failure */
2096 setAX(0x800C);
2097 Stack[STACK_FLAGS - 1] |= EMULATOR_FLAG_CF;
2098 }
2099
2100 VOID WINAPI DosInt27h(LPWORD Stack)
2101 {
2102 WORD KeepResident = (getDX() + 0x0F) >> 4;
2103
2104 /* Terminate and Stay Resident. CS must be the PSP segment. */
2105 DPRINT1("Process going resident: %u paragraphs kept\n", KeepResident);
2106 DosTerminateProcess(Stack[STACK_CS], 0, KeepResident);
2107 }
2108
2109 VOID WINAPI DosIdle(LPWORD Stack)
2110 {
2111 /*
2112 * This will set the carry flag on the first call (to repeat the BOP),
2113 * and clear it in the next, so that exactly one HLT occurs.
2114 */
2115 setCF(!getCF());
2116 }
2117
2118 VOID WINAPI DosFastConOut(LPWORD Stack)
2119 {
2120 /*
2121 * This is the DOS 2+ Fast Console Output Interrupt.
2122 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
2123 *
2124 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
2125 * for more information.
2126 */
2127
2128 /* Save AX and BX */
2129 USHORT AX = getAX();
2130 USHORT BX = getBX();
2131
2132 /*
2133 * Set the parameters:
2134 * AL contains the character to print (already set),
2135 * BL contains the character attribute,
2136 * BH contains the video page to use.
2137 */
2138 setBL(DOS_CHAR_ATTRIBUTE);
2139 setBH(Bda->VideoPage);
2140
2141 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
2142 setAH(0x0E);
2143 Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT);
2144
2145 /* Restore AX and BX */
2146 setBX(BX);
2147 setAX(AX);
2148 }
2149
2150 VOID WINAPI DosInt2Ah(LPWORD Stack)
2151 {
2152 DPRINT1("INT 2Ah, AX=%4xh called\n", getAX());
2153 }
2154
2155 VOID WINAPI DosInt2Fh(LPWORD Stack)
2156 {
2157 switch (getAH())
2158 {
2159 /* DOS 3+ Internal Utility Functions */
2160 case 0x12:
2161 {
2162 DPRINT1("INT 2Fh, AX=%4xh DOS Internal Utility Function called\n", getAX());
2163
2164 switch (getAL())
2165 {
2166 /* Installation Check */
2167 case 0x00:
2168 {
2169 setAL(0xFF);
2170 break;
2171 }
2172
2173 /* Get DOS Data Segment */
2174 case 0x03:
2175 {
2176 setDS(DOS_DATA_SEGMENT);
2177 break;
2178 }
2179
2180 /* Compare FAR Pointers */
2181 case 0x14:
2182 {
2183 PVOID PointerFromFarPointer1 = SEG_OFF_TO_PTR(getDS(), getSI());
2184 PVOID PointerFromFarPointer2 = SEG_OFF_TO_PTR(getES(), getDI());
2185 BOOLEAN AreEqual = (PointerFromFarPointer1 == PointerFromFarPointer2);
2186
2187 setZF(AreEqual);
2188 setCF(!AreEqual);
2189 break;
2190 }
2191
2192 /* Set DOS Version Number to return */
2193 case 0x2F:
2194 {
2195 WORD DosVersion = getDX();
2196
2197 // Special case: return the true DOS version when DX=00h
2198 if (DosVersion == 0x0000)
2199 DosData->DosVersion = DOS_VERSION;
2200 else
2201 DosData->DosVersion = DosVersion;
2202
2203 break;
2204 }
2205 }
2206
2207 break;
2208 }
2209
2210 /* Set Disk Interrupt Handler */
2211 case 0x13:
2212 {
2213 /* Save the old values of PrevInt13 and RomBiosInt13 */
2214 ULONG OldInt13 = BiosData->PrevInt13;
2215 ULONG OldBiosInt13 = BiosData->RomBiosInt13;
2216
2217 /* Set PrevInt13 and RomBiosInt13 to their new values */
2218 BiosData->PrevInt13 = MAKELONG(getDX(), getDS());
2219 BiosData->RomBiosInt13 = MAKELONG(getBX(), getES());
2220
2221 /* Return in DS:DX the old value of PrevInt13 */
2222 setDS(HIWORD(OldInt13));
2223 setDX(LOWORD(OldInt13));
2224
2225 /* Return in DS:DX the old value of RomBiosInt13 */
2226 setES(HIWORD(OldBiosInt13));
2227 setBX(LOWORD(OldBiosInt13));
2228
2229 break;
2230 }
2231
2232 /* Mostly Windows 2.x/3.x/9x support */
2233 case 0x16:
2234 {
2235 /*
2236 * AL=80h is DOS/Windows/DPMI "Release Current Virtual Machine Time-slice"
2237 * Just do nothing in this case.
2238 */
2239 if (getAL() != 0x80) goto Default;
2240 break;
2241 }
2242
2243 /* Extended Memory Specification */
2244 case 0x43:
2245 {
2246 DWORD DriverEntry;
2247 if (!XmsGetDriverEntry(&DriverEntry)) break;
2248
2249 switch (getAL())
2250 {
2251 /* Installation Check */
2252 case 0x00:
2253 {
2254 /* The driver is loaded */
2255 setAL(0x80);
2256 break;
2257 }
2258
2259 /* Get Driver Address */
2260 case 0x10:
2261 {
2262 setES(HIWORD(DriverEntry));
2263 setBX(LOWORD(DriverEntry));
2264 break;
2265 }
2266
2267 default:
2268 DPRINT1("Unknown DOS XMS Function: INT 2Fh, AH = 43h, AL = %02Xh\n", getAL());
2269 break;
2270 }
2271
2272 break;
2273 }
2274
2275 default: Default:
2276 {
2277 DPRINT1("DOS Internal System Function INT 2Fh, AH = %02Xh, AL = %02Xh NOT IMPLEMENTED!\n",
2278 getAH(), getAL());
2279 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2280 }
2281 }
2282 }
2283
2284 BOOLEAN DosKRNLInitialize(VOID)
2285 {
2286 UCHAR i;
2287 PDOS_SFT Sft;
2288 LPSTR Path;
2289 BOOLEAN Success = TRUE;
2290 DWORD dwRet;
2291 CHAR CurrentDirectory[MAX_PATH];
2292 CHAR DosDirectory[DOS_DIR_LENGTH];
2293
2294 static const BYTE NullDriverRoutine[] =
2295 {
2296 /* Strategy routine entry */
2297 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
2298 0xC7,
2299 0x47,
2300 FIELD_OFFSET(DOS_REQUEST_HEADER, Status),
2301 LOBYTE(DOS_DEVSTAT_DONE),
2302 HIBYTE(DOS_DEVSTAT_DONE),
2303
2304 /* Interrupt routine entry */
2305 0xCB, // retf
2306 };
2307
2308 /* Set the data segment */
2309 setDS(DOS_DATA_SEGMENT);
2310
2311 /* Initialize the global DOS data area */
2312 DosData = (PDOS_DATA)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT, 0x0000);
2313 RtlZeroMemory(DosData, sizeof(*DosData));
2314
2315 /* Initialize the DOS stack */
2316 setSS(DOS_DATA_SEGMENT);
2317 setSP(DOS_DATA_OFFSET(DosStack) + sizeof(DosData->DosStack) - sizeof(WORD));
2318
2319 /* Initialize the list of lists */
2320 SysVars = &DosData->SysVars;
2321 RtlZeroMemory(SysVars, sizeof(*SysVars));
2322 SysVars->FirstSft = MAKELONG(DOS_DATA_OFFSET(Sft), DOS_DATA_SEGMENT);
2323 SysVars->CurrentDirs = MAKELONG(DOS_DATA_OFFSET(CurrentDirectories),
2324 DOS_DATA_SEGMENT);
2325 /*
2326 * The last drive can be redefined with the LASTDRIVE command.
2327 * At the moment, set the real maximum possible, 'Z'.
2328 */
2329 SysVars->NumLocalDrives = 'Z' - 'A' + 1; // See #define NUM_DRIVES in dos.h
2330
2331 /* The boot drive is initialized to the %SYSTEMDRIVE% value */
2332 // NOTE: Using the NtSystemRoot system variable might be OS-specific...
2333 SysVars->BootDrive = RtlUpcaseUnicodeChar(SharedUserData->NtSystemRoot[0]) - 'A' + 1;
2334
2335 /* Initialize the NUL device driver */
2336 SysVars->NullDevice.Link = 0xFFFFFFFF;
2337 SysVars->NullDevice.DeviceAttributes = DOS_DEVATTR_NUL | DOS_DEVATTR_CHARACTER;
2338 // Offset from within the DOS data segment
2339 SysVars->NullDevice.StrategyRoutine = DOS_DATA_OFFSET(NullDriverRoutine);
2340 // Hardcoded to the RETF inside StrategyRoutine
2341 SysVars->NullDevice.InterruptRoutine = SysVars->NullDevice.StrategyRoutine + 6;
2342 RtlFillMemory(SysVars->NullDevice.DeviceName,
2343 sizeof(SysVars->NullDevice.DeviceName),
2344 ' ');
2345 RtlCopyMemory(SysVars->NullDevice.DeviceName, "NUL", strlen("NUL"));
2346 RtlCopyMemory(DosData->NullDriverRoutine,
2347 NullDriverRoutine,
2348 sizeof(NullDriverRoutine));
2349
2350 /* Default DOS version to report */
2351 DosData->DosVersion = DOS_VERSION;
2352
2353 /* Initialize the swappable data area */
2354 Sda = &DosData->Sda;
2355 RtlZeroMemory(Sda, sizeof(*Sda));
2356
2357 /* Get the current directory and convert it to a DOS path */
2358 dwRet = GetCurrentDirectoryA(sizeof(CurrentDirectory), CurrentDirectory);
2359 if (dwRet == 0)
2360 {
2361 Success = FALSE;
2362 DPRINT1("GetCurrentDirectoryA failed (Error: %u)\n", GetLastError());
2363 }
2364 else if (dwRet > sizeof(CurrentDirectory))
2365 {
2366 Success = FALSE;
2367 DPRINT1("Current directory too long (%d > MAX_PATH) for GetCurrentDirectoryA\n", dwRet);
2368 }
2369
2370 if (Success)
2371 {
2372 dwRet = GetShortPathNameA(CurrentDirectory, DosDirectory, sizeof(DosDirectory));
2373 if (dwRet == 0)
2374 {
2375 Success = FALSE;
2376 DPRINT1("GetShortPathNameA failed (Error: %u)\n", GetLastError());
2377 }
2378 else if (dwRet > sizeof(DosDirectory))
2379 {
2380 Success = FALSE;
2381 DPRINT1("Short path too long (%d > DOS_DIR_LENGTH) for GetShortPathNameA\n", dwRet);
2382 }
2383 }
2384
2385 if (!Success)
2386 {
2387 /* We failed, use the boot drive instead */
2388 DosDirectory[0] = SysVars->BootDrive + 'A' - 1;
2389 DosDirectory[1] = ':';
2390 DosDirectory[2] = '\\';
2391 DosDirectory[3] = '\0';
2392 }
2393
2394 /* Set the current drive */
2395 Sda->CurrentDrive = RtlUpperChar(DosDirectory[0]) - 'A';
2396
2397 /* Get the directory part of the path and set the current directory */
2398 Path = strchr(DosDirectory, '\\');
2399 if (Path != NULL)
2400 {
2401 Path++; // Skip the backslash
2402 strncpy(DosData->CurrentDirectories[Sda->CurrentDrive], Path, DOS_DIR_LENGTH);
2403 }
2404 else
2405 {
2406 DosData->CurrentDirectories[Sda->CurrentDrive][0] = '\0';
2407 }
2408
2409 /* Set the current PSP to the system PSP */
2410 Sda->CurrentPsp = SYSTEM_PSP;
2411
2412 /* Initialize the SFT */
2413 Sft = (PDOS_SFT)FAR_POINTER(SysVars->FirstSft);
2414 Sft->Link = 0xFFFFFFFF;
2415 Sft->NumDescriptors = DOS_SFT_SIZE;
2416
2417 for (i = 0; i < Sft->NumDescriptors; i++)
2418 {
2419 /* Clear the file descriptor entry */
2420 RtlZeroMemory(&Sft->FileDescriptors[i], sizeof(DOS_FILE_DESCRIPTOR));
2421 }
2422
2423 /* Initialize memory management */
2424 DosInitializeMemory();
2425
2426 /* Initialize the callback context */
2427 InitializeContext(&DosContext, DOS_CODE_SEGMENT, 0x0000);
2428
2429 /* Register the DOS 32-bit Interrupts */
2430 RegisterDosInt32(0x20, DosInt20h );
2431 RegisterDosInt32(0x21, DosInt21h );
2432 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2433 RegisterDosInt32(0x23, DosBreakInterrupt); // Ctrl-C / Ctrl-Break
2434 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2435 RegisterDosInt32(0x25, DosAbsoluteRead ); // Absolute Disk Read
2436 RegisterDosInt32(0x26, DosAbsoluteWrite ); // Absolute Disk Write
2437 RegisterDosInt32(0x27, DosInt27h ); // Terminate and Stay Resident
2438 RegisterDosInt32(0x28, DosIdle ); // DOS Idle Interrupt
2439 RegisterDosInt32(0x29, DosFastConOut ); // DOS 2+ Fast Console Output
2440 RegisterDosInt32(0x2F, DosInt2Fh ); // Multiplex Interrupt
2441
2442 /* Unimplemented DOS interrupts */
2443 RegisterDosInt32(0x2A, DosInt2Ah); // DOS Critical Sections / Network
2444 // RegisterDosInt32(0x2E, NULL); // COMMAND.COM "Reload Transient"
2445 // COMMAND.COM adds support for INT 2Fh, AX=AE00h and AE01h "Installable Command - Installation Check & Execute"
2446 // COMMAND.COM adds support for INT 2Fh, AX=5500h "COMMAND.COM Interface"
2447
2448 /* Reserved DOS interrupts */
2449 RegisterDosInt32(0x2B, NULL);
2450 RegisterDosInt32(0x2C, NULL);
2451 RegisterDosInt32(0x2D, NULL);
2452
2453 /* Initialize country data */
2454 DosCountryInitialize();
2455
2456 /* Load the CON driver */
2457 ConDrvInitialize();
2458
2459 /* Load the XMS driver (HIMEM) */
2460 XmsInitialize();
2461
2462 /* Load the EMS driver */
2463 if (!EmsDrvInitialize(EMS_SEGMENT, EMS_TOTAL_PAGES))
2464 {
2465 DosDisplayMessage("Could not initialize EMS. EMS will not be available.\n"
2466 "Page frame segment or number of EMS pages invalid.\n");
2467 }
2468
2469 /* Finally initialize the UMBs */
2470 DosInitializeUmb();
2471
2472 return TRUE;
2473 }
2474
2475 /* EOF */