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