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