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