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