4c2586a557df1895a7077bc663314f3693eaaa4d
[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 /* Save the value of SS:SP on entry in the PSP */
182 SEGMENT_TO_PSP(CurrentPsp)->LastStack =
183 MAKELONG(getSP() + (STACK_FLAGS + 1) * 2, getSS());
184
185 /* Check the value in the AH register */
186 switch (getAH())
187 {
188 /* Terminate Program */
189 case 0x00:
190 {
191 DosTerminateProcess(Stack[STACK_CS], 0, 0);
192 break;
193 }
194
195 /* Read Character from STDIN with Echo */
196 case 0x01:
197 {
198 DPRINT("INT 21h, AH = 01h\n");
199
200 // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
201 DoEcho = TRUE;
202 Character = DosReadCharacter(DOS_INPUT_HANDLE);
203 DoEcho = FALSE;
204
205 // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
206 // Check also Ctrl-P and set echo-to-printer flag.
207 // Ctrl-Z is not interpreted.
208
209 setAL(Character);
210 break;
211 }
212
213 /* Write Character to STDOUT */
214 case 0x02:
215 {
216 // FIXME: Under DOS 2+, output handle may be redirected!!!!
217 Character = getDL();
218 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
219
220 /*
221 * We return the output character (DOS 2.1+).
222 * Also, if we're going to output a TAB, then
223 * don't return a TAB but a SPACE instead.
224 * See Ralf Brown: http://www.ctyme.com/intr/rb-2554.htm
225 * for more information.
226 */
227 setAL(Character == '\t' ? ' ' : Character);
228 break;
229 }
230
231 /* Read Character from STDAUX */
232 case 0x03:
233 {
234 // FIXME: Really read it from STDAUX!
235 DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
236 // setAL(DosReadCharacter());
237 break;
238 }
239
240 /* Write Character to STDAUX */
241 case 0x04:
242 {
243 // FIXME: Really write it to STDAUX!
244 DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
245 // DosPrintCharacter(getDL());
246 break;
247 }
248
249 /* Write Character to Printer */
250 case 0x05:
251 {
252 // FIXME: Really write it to printer!
253 DPRINT1("INT 16h, 05h: Write character to printer is HALFPLEMENTED -\n\n");
254 DPRINT1("0x%p\n", getDL());
255 DPRINT1("\n\n-----------\n\n");
256 break;
257 }
258
259 /* Direct Console I/O */
260 case 0x06:
261 {
262 Character = getDL();
263
264 // FIXME: Under DOS 2+, output handle may be redirected!!!!
265
266 if (Character != 0xFF)
267 {
268 /* Output */
269 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
270
271 /*
272 * We return the output character (DOS 2.1+).
273 * See Ralf Brown: http://www.ctyme.com/intr/rb-2558.htm
274 * for more information.
275 */
276 setAL(Character);
277 }
278 else
279 {
280 /* Input */
281 if (DosCheckInput())
282 {
283 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
284 setAL(DosReadCharacter(DOS_INPUT_HANDLE));
285 }
286 else
287 {
288 /* No character available */
289 Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF;
290 setAL(0x00);
291 }
292 }
293
294 break;
295 }
296
297 /* Character Input without Echo */
298 case 0x07:
299 case 0x08:
300 {
301 DPRINT("Char input without echo\n");
302
303 Character = DosReadCharacter(DOS_INPUT_HANDLE);
304
305 // FIXME: For 0x07, do not check Ctrl-C/Break.
306 // For 0x08, do check those control sequences and if needed,
307 // call INT 0x23.
308
309 setAL(Character);
310 break;
311 }
312
313 /* Write string to STDOUT */
314 case 0x09:
315 {
316 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
317
318 while (*String != '$')
319 {
320 DosPrintCharacter(DOS_OUTPUT_HANDLE, *String);
321 String++;
322 }
323
324 /*
325 * We return the terminating character (DOS 2.1+).
326 * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
327 * for more information.
328 */
329 setAL('$'); // *String
330 break;
331 }
332
333 /* Read Buffered Input */
334 case 0x0A:
335 {
336 WORD Count = 0;
337 InputBuffer = (PDOS_INPUT_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
338
339 DPRINT("Read Buffered Input\n");
340
341 while (Count < InputBuffer->MaxLength)
342 {
343 /* Try to read a character (wait) */
344 Character = DosReadCharacter(DOS_INPUT_HANDLE);
345
346 switch (Character)
347 {
348 /* Extended character */
349 case '\0':
350 {
351 /* Read the scancode */
352 DosReadCharacter(DOS_INPUT_HANDLE);
353 break;
354 }
355
356 /* Ctrl-C */
357 case 0x03:
358 {
359 DosPrintCharacter(DOS_OUTPUT_HANDLE, '^');
360 DosPrintCharacter(DOS_OUTPUT_HANDLE, 'C');
361
362 if (DosControlBreak())
363 {
364 /* Set the character to a newline to exit the loop */
365 Character = '\r';
366 }
367
368 break;
369 }
370
371 /* Backspace */
372 case '\b':
373 {
374 if (Count > 0)
375 {
376 Count--;
377
378 /* Erase the character */
379 DosPrintCharacter(DOS_OUTPUT_HANDLE, '\b');
380 DosPrintCharacter(DOS_OUTPUT_HANDLE, ' ');
381 DosPrintCharacter(DOS_OUTPUT_HANDLE, '\b');
382 }
383
384 break;
385 }
386
387 default:
388 {
389 /* Append it to the buffer */
390 InputBuffer->Buffer[Count] = Character;
391
392 /* Check if this is a special character */
393 if (Character < 0x20 && Character != 0x0A && Character != 0x0D)
394 {
395 DosPrintCharacter(DOS_OUTPUT_HANDLE, '^');
396 Character += 'A' - 1;
397 }
398
399 /* Echo the character */
400 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
401 }
402 }
403
404 if (Character == '\r') break;
405 if (Character == '\b') continue;
406 Count++; /* Carriage returns are NOT counted */
407 }
408
409 /* Update the length */
410 InputBuffer->Length = Count;
411
412 break;
413 }
414
415 /* Get STDIN Status */
416 case 0x0B:
417 {
418 setAL(DosCheckInput() ? 0xFF : 0x00);
419 break;
420 }
421
422 /* Flush Buffer and Read STDIN */
423 case 0x0C:
424 {
425 BYTE InputFunction = getAL();
426
427 /* Flush STDIN buffer */
428 DosFlushFileBuffers(DOS_INPUT_HANDLE);
429
430 /*
431 * If the input function number contained in AL is valid, i.e.
432 * AL == 0x01 or 0x06 or 0x07 or 0x08 or 0x0A, call ourselves
433 * recursively with AL == AH.
434 */
435 if (InputFunction == 0x01 || InputFunction == 0x06 ||
436 InputFunction == 0x07 || InputFunction == 0x08 ||
437 InputFunction == 0x0A)
438 {
439 /* Call ourselves recursively */
440 setAH(InputFunction);
441 DosInt21h(Stack);
442 }
443 break;
444 }
445
446 /* Disk Reset */
447 case 0x0D:
448 {
449 PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
450
451 // TODO: Flush what's needed.
452 DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
453
454 /* Clear CF in DOS 6 only */
455 if (PspBlock->DosVersion == 0x0006)
456 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
457
458 break;
459 }
460
461 /* Set Default Drive */
462 case 0x0E:
463 {
464 DosChangeDrive(getDL());
465 setAL(LastDrive - 'A' + 1);
466 break;
467 }
468
469 /* NULL Function for CP/M Compatibility */
470 case 0x18:
471 {
472 /*
473 * This function corresponds to the CP/M BDOS function
474 * "get bit map of logged drives", which is meaningless
475 * under MS-DOS.
476 *
477 * For: PTS-DOS 6.51 & S/DOS 1.0 - EXTENDED RENAME FILE USING FCB
478 * See Ralf Brown: http://www.ctyme.com/intr/rb-2584.htm
479 * for more information.
480 */
481 setAL(0x00);
482 break;
483 }
484
485 /* Get Default Drive */
486 case 0x19:
487 {
488 setAL(CurrentDrive);
489 break;
490 }
491
492 /* Set Disk Transfer Area */
493 case 0x1A:
494 {
495 DiskTransferArea = MAKELONG(getDX(), getDS());
496 break;
497 }
498
499 /* NULL Function for CP/M Compatibility */
500 case 0x1D:
501 case 0x1E:
502 {
503 /*
504 * Function 0x1D corresponds to the CP/M BDOS function
505 * "get bit map of read-only drives", which is meaningless
506 * under MS-DOS.
507 * See Ralf Brown: http://www.ctyme.com/intr/rb-2592.htm
508 * for more information.
509 *
510 * Function 0x1E corresponds to the CP/M BDOS function
511 * "set file attributes", which was meaningless under MS-DOS 1.x.
512 * See Ralf Brown: http://www.ctyme.com/intr/rb-2593.htm
513 * for more information.
514 */
515 setAL(0x00);
516 break;
517 }
518
519 /* NULL Function for CP/M Compatibility */
520 case 0x20:
521 {
522 /*
523 * This function corresponds to the CP/M BDOS function
524 * "get/set default user (sublibrary) number", which is meaningless
525 * under MS-DOS.
526 *
527 * For: S/DOS 1.0+ & PTS-DOS 6.51+ - GET OEM REVISION
528 * See Ralf Brown: http://www.ctyme.com/intr/rb-2596.htm
529 * for more information.
530 */
531 setAL(0x00);
532 break;
533 }
534
535 /* Set Interrupt Vector */
536 case 0x25:
537 {
538 ULONG FarPointer = MAKELONG(getDX(), getDS());
539 DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
540 getAL(), HIWORD(FarPointer), LOWORD(FarPointer));
541
542 /* Write the new far pointer to the IDT */
543 ((PULONG)BaseAddress)[getAL()] = FarPointer;
544 break;
545 }
546
547 /* Create New PSP */
548 case 0x26:
549 {
550 DosClonePsp(getDX(), getCS());
551 break;
552 }
553
554 /* Parse Filename into FCB */
555 case 0x29:
556 {
557 PCHAR FileName = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI());
558 PDOS_FCB Fcb = (PDOS_FCB)SEG_OFF_TO_PTR(getES(), getDI());
559 BYTE Options = getAL();
560 INT i;
561 CHAR FillChar = ' ';
562
563 if (FileName[1] == ':')
564 {
565 /* Set the drive number */
566 Fcb->DriveNumber = RtlUpperChar(FileName[0]) - 'A' + 1;
567
568 /* Skip to the file name part */
569 FileName += 2;
570 }
571 else
572 {
573 /* No drive number specified */
574 if (Options & (1 << 1)) Fcb->DriveNumber = CurrentDrive + 1;
575 else Fcb->DriveNumber = 0;
576 }
577
578 /* Parse the file name */
579 i = 0;
580 while ((*FileName > 0x20) && (i < 8))
581 {
582 if (*FileName == '.') break;
583 else if (*FileName == '*')
584 {
585 FillChar = '?';
586 break;
587 }
588
589 Fcb->FileName[i++] = RtlUpperChar(*FileName++);
590 }
591
592 /* Fill the whole field with blanks only if bit 2 is not set */
593 if ((FillChar != ' ') || (i != 0) || !(Options & (1 << 2)))
594 {
595 for (; i < 8; i++) Fcb->FileName[i] = FillChar;
596 }
597
598 /* Skip to the extension part */
599 while (*FileName > 0x20 && *FileName != '.') FileName++;
600 if (*FileName == '.') FileName++;
601
602 /* Now parse the extension */
603 i = 0;
604 FillChar = ' ';
605
606 while ((*FileName > 0x20) && (i < 3))
607 {
608 if (*FileName == '*')
609 {
610 FillChar = '?';
611 break;
612 }
613
614 Fcb->FileExt[i++] = RtlUpperChar(*FileName++);
615 }
616
617 /* Fill the whole field with blanks only if bit 3 is not set */
618 if ((FillChar != ' ') || (i != 0) || !(Options & (1 << 3)))
619 {
620 for (; i < 3; i++) Fcb->FileExt[i] = FillChar;
621 }
622
623 break;
624 }
625
626 /* Get System Date */
627 case 0x2A:
628 {
629 GetLocalTime(&SystemTime);
630 setCX(SystemTime.wYear);
631 setDX(MAKEWORD(SystemTime.wDay, SystemTime.wMonth));
632 setAL(SystemTime.wDayOfWeek);
633 break;
634 }
635
636 /* Set System Date */
637 case 0x2B:
638 {
639 GetLocalTime(&SystemTime);
640 SystemTime.wYear = getCX();
641 SystemTime.wMonth = getDH();
642 SystemTime.wDay = getDL();
643
644 /* Return success or failure */
645 setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF);
646 break;
647 }
648
649 /* Get System Time */
650 case 0x2C:
651 {
652 GetLocalTime(&SystemTime);
653 setCX(MAKEWORD(SystemTime.wMinute, SystemTime.wHour));
654 setDX(MAKEWORD(SystemTime.wMilliseconds / 10, SystemTime.wSecond));
655 break;
656 }
657
658 /* Set System Time */
659 case 0x2D:
660 {
661 GetLocalTime(&SystemTime);
662 SystemTime.wHour = getCH();
663 SystemTime.wMinute = getCL();
664 SystemTime.wSecond = getDH();
665 SystemTime.wMilliseconds = getDL() * 10; // In hundredths of seconds
666
667 /* Return success or failure */
668 setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF);
669 break;
670 }
671
672 /* Get Disk Transfer Area */
673 case 0x2F:
674 {
675 setES(HIWORD(DiskTransferArea));
676 setBX(LOWORD(DiskTransferArea));
677 break;
678 }
679
680 /* Get DOS Version */
681 case 0x30:
682 {
683 PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
684
685 /*
686 * DOS 2+ - GET DOS VERSION
687 * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
688 * for more information.
689 */
690
691 if (LOBYTE(PspBlock->DosVersion) < 5 || getAL() == 0x00)
692 {
693 /*
694 * Return DOS OEM number:
695 * 0x00 for IBM PC-DOS
696 * 0x02 for packaged MS-DOS
697 * 0xFF for NT DOS
698 */
699 setBH(0xFF);
700 }
701
702 if (LOBYTE(PspBlock->DosVersion) >= 5 && getAL() == 0x01)
703 {
704 /*
705 * Return version flag:
706 * 1 << 3 if DOS is in ROM,
707 * 0 (reserved) if not.
708 */
709 setBH(0x00);
710 }
711
712 /* Return DOS 24-bit user serial number in BL:CX */
713 setBL(0x00);
714 setCX(0x0000);
715
716 /*
717 * Return DOS version: Minor:Major in AH:AL
718 * The Windows NT DOS box returns version 5.00, subject to SETVER.
719 */
720 setAX(PspBlock->DosVersion);
721
722 break;
723 }
724
725 /* Terminate and Stay Resident */
726 case 0x31:
727 {
728 DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
729 DosTerminateProcess(CurrentPsp, getAL(), getDX());
730 break;
731 }
732
733 /* Extended functionalities */
734 case 0x33:
735 {
736 if (getAL() == 0x06)
737 {
738 /*
739 * DOS 5+ - GET TRUE VERSION NUMBER
740 * This function always returns the true version number, unlike
741 * AH=30h, whose return value may be changed with SETVER.
742 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
743 * for more information.
744 */
745
746 /*
747 * Return the true DOS version: Minor:Major in BH:BL
748 * The Windows NT DOS box returns BX=3205h (version 5.50).
749 */
750 setBX(NTDOS_VERSION);
751
752 /* DOS revision 0 */
753 setDL(0x00);
754
755 /* Unpatched DOS */
756 setDH(0x00);
757 }
758 // else
759 // {
760 // /* Invalid subfunction */
761 // setAL(0xFF);
762 // }
763
764 break;
765 }
766
767 /* Get Address of InDOS flag */
768 case 0x34:
769 {
770 setES(HIWORD(INDOS_POINTER));
771 setBX(LOWORD(INDOS_POINTER));
772 break;
773 }
774
775 /* Get Interrupt Vector */
776 case 0x35:
777 {
778 ULONG FarPointer = ((PULONG)BaseAddress)[getAL()];
779
780 /* Read the address from the IDT into ES:BX */
781 setES(HIWORD(FarPointer));
782 setBX(LOWORD(FarPointer));
783 break;
784 }
785
786 /* Get Free Disk Space */
787 case 0x36:
788 {
789 CHAR RootPath[3] = "X:\\";
790 DWORD SectorsPerCluster;
791 DWORD BytesPerSector;
792 DWORD NumberOfFreeClusters;
793 DWORD TotalNumberOfClusters;
794
795 if (getDL() == 0) RootPath[0] = 'A' + CurrentDrive;
796 else RootPath[0] = 'A' + getDL() - 1;
797
798 if (GetDiskFreeSpaceA(RootPath,
799 &SectorsPerCluster,
800 &BytesPerSector,
801 &NumberOfFreeClusters,
802 &TotalNumberOfClusters))
803 {
804 setAX(LOWORD(SectorsPerCluster));
805 setCX(LOWORD(BytesPerSector));
806 setBX(LOWORD(NumberOfFreeClusters));
807 setDX(LOWORD(TotalNumberOfClusters));
808 }
809 else
810 {
811 /* Error */
812 setAX(0xFFFF);
813 }
814
815 break;
816 }
817
818 /* SWITCH character - AVAILDEV */
819 case 0x37:
820 {
821 if (getAL() == 0x00)
822 {
823 /*
824 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
825 * This setting is ignored by MS-DOS 4.0+.
826 * MS-DOS 5+ always return AL=00h/DL=2Fh.
827 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
828 * for more information.
829 */
830 setDL('/');
831 setAL(0x00);
832 }
833 else if (getAL() == 0x01)
834 {
835 /*
836 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
837 * This setting is ignored by MS-DOS 5+.
838 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
839 * for more information.
840 */
841 // getDL();
842 setAL(0xFF);
843 }
844 else if (getAL() == 0x02)
845 {
846 /*
847 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
848 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
849 * for more information.
850 */
851 // setDL();
852 setAL(0xFF);
853 }
854 else if (getAL() == 0x03)
855 {
856 /*
857 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
858 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
859 * for more information.
860 */
861 // getDL();
862 setAL(0xFF);
863 }
864 else
865 {
866 /* Invalid subfunction */
867 setAL(0xFF);
868 }
869
870 break;
871 }
872
873 /* Get/Set Country-dependent Information */
874 case 0x38:
875 {
876 CountryCodeBuffer = (PDOS_COUNTRY_CODE_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
877
878 if (getAL() == 0x00)
879 {
880 /* Get */
881 Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDATE,
882 &CountryCodeBuffer->TimeFormat,
883 sizeof(CountryCodeBuffer->TimeFormat) / sizeof(TCHAR));
884 if (Return == 0)
885 {
886 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
887 setAX(LOWORD(GetLastError()));
888 break;
889 }
890
891 Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SCURRENCY,
892 &CountryCodeBuffer->CurrencySymbol,
893 sizeof(CountryCodeBuffer->CurrencySymbol) / sizeof(TCHAR));
894 if (Return == 0)
895 {
896 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
897 setAX(LOWORD(GetLastError()));
898 break;
899 }
900
901 Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND,
902 &CountryCodeBuffer->ThousandSep,
903 sizeof(CountryCodeBuffer->ThousandSep) / sizeof(TCHAR));
904 if (Return == 0)
905 {
906 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
907 setAX(LOWORD(GetLastError()));
908 break;
909 }
910
911 Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
912 &CountryCodeBuffer->DecimalSep,
913 sizeof(CountryCodeBuffer->DecimalSep) / sizeof(TCHAR));
914 if (Return == 0)
915 {
916 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
917 setAX(LOWORD(GetLastError()));
918 break;
919 }
920
921 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
922 }
923 else
924 {
925 // TODO: NOT IMPLEMENTED
926 UNIMPLEMENTED;
927 }
928
929 break;
930 }
931
932 /* Create Directory */
933 case 0x39:
934 {
935 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
936
937 if (CreateDirectoryA(String, NULL))
938 {
939 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
940 }
941 else
942 {
943 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
944 setAX(LOWORD(GetLastError()));
945 }
946
947 break;
948 }
949
950 /* Remove Directory */
951 case 0x3A:
952 {
953 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
954
955 if (RemoveDirectoryA(String))
956 {
957 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
958 }
959 else
960 {
961 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
962 setAX(LOWORD(GetLastError()));
963 }
964
965 break;
966 }
967
968 /* Set Current Directory */
969 case 0x3B:
970 {
971 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
972
973 if (DosChangeDirectory(String))
974 {
975 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
976 }
977 else
978 {
979 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
980 setAX(DosLastError);
981 }
982
983 break;
984 }
985
986 /* Create or Truncate File */
987 case 0x3C:
988 {
989 WORD FileHandle;
990 WORD ErrorCode = DosCreateFile(&FileHandle,
991 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
992 CREATE_ALWAYS,
993 getCX());
994
995 if (ErrorCode == ERROR_SUCCESS)
996 {
997 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
998 setAX(FileHandle);
999 }
1000 else
1001 {
1002 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1003 setAX(ErrorCode);
1004 }
1005
1006 break;
1007 }
1008
1009 /* Open File or Device */
1010 case 0x3D:
1011 {
1012 WORD FileHandle;
1013 LPCSTR FileName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1014 WORD ErrorCode = DosOpenFile(&FileHandle, FileName, getAL());
1015
1016 if (ErrorCode == ERROR_SUCCESS)
1017 {
1018 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1019 setAX(FileHandle);
1020 }
1021 else
1022 {
1023 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1024 setAX(ErrorCode);
1025 }
1026
1027 break;
1028 }
1029
1030 /* Close File or Device */
1031 case 0x3E:
1032 {
1033 if (DosCloseHandle(getBX()))
1034 {
1035 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1036 }
1037 else
1038 {
1039 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1040 setAX(ERROR_INVALID_HANDLE);
1041 }
1042
1043 break;
1044 }
1045
1046 /* Read from File or Device */
1047 case 0x3F:
1048 {
1049 WORD BytesRead = 0;
1050 WORD ErrorCode;
1051
1052 DPRINT("DosReadFile(0x%04X)\n", getBX());
1053
1054 DoEcho = TRUE;
1055 ErrorCode = DosReadFile(getBX(),
1056 MAKELONG(getDX(), getDS()),
1057 getCX(),
1058 &BytesRead);
1059 DoEcho = FALSE;
1060
1061 if (ErrorCode == ERROR_SUCCESS)
1062 {
1063 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1064 setAX(BytesRead);
1065 }
1066 else if (ErrorCode != ERROR_NOT_READY)
1067 {
1068 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1069 setAX(ErrorCode);
1070 }
1071
1072 break;
1073 }
1074
1075 /* Write to File or Device */
1076 case 0x40:
1077 {
1078 WORD BytesWritten = 0;
1079 WORD ErrorCode = DosWriteFile(getBX(),
1080 MAKELONG(getDX(), getDS()),
1081 getCX(),
1082 &BytesWritten);
1083
1084 if (ErrorCode == ERROR_SUCCESS)
1085 {
1086 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1087 setAX(BytesWritten);
1088 }
1089 else
1090 {
1091 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1092 setAX(ErrorCode);
1093 }
1094
1095 break;
1096 }
1097
1098 /* Delete File */
1099 case 0x41:
1100 {
1101 LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1102
1103 if (demFileDelete(FileName) == ERROR_SUCCESS)
1104 {
1105 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1106 /*
1107 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
1108 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
1109 */
1110 setAL(FileName[0] - 'A');
1111 }
1112 else
1113 {
1114 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1115 setAX(GetLastError());
1116 }
1117
1118 break;
1119 }
1120
1121 /* Seek File */
1122 case 0x42:
1123 {
1124 DWORD NewLocation;
1125 WORD ErrorCode = DosSeekFile(getBX(),
1126 MAKELONG(getDX(), getCX()),
1127 getAL(),
1128 &NewLocation);
1129
1130 if (ErrorCode == ERROR_SUCCESS)
1131 {
1132 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1133
1134 /* Return the new offset in DX:AX */
1135 setDX(HIWORD(NewLocation));
1136 setAX(LOWORD(NewLocation));
1137 }
1138 else
1139 {
1140 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1141 setAX(ErrorCode);
1142 }
1143
1144 break;
1145 }
1146
1147 /* Get/Set File Attributes */
1148 case 0x43:
1149 {
1150 DWORD Attributes;
1151 LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1152
1153 if (getAL() == 0x00)
1154 {
1155 /* Get the attributes */
1156 Attributes = GetFileAttributesA(FileName);
1157
1158 /* Check if it failed */
1159 if (Attributes == INVALID_FILE_ATTRIBUTES)
1160 {
1161 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1162 setAX(GetLastError());
1163 }
1164 else
1165 {
1166 /* Return the attributes that DOS can understand */
1167 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1168 setCX(Attributes & 0x00FF);
1169 }
1170 }
1171 else if (getAL() == 0x01)
1172 {
1173 /* Try to set the attributes */
1174 if (SetFileAttributesA(FileName, getCL()))
1175 {
1176 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1177 }
1178 else
1179 {
1180 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1181 setAX(GetLastError());
1182 }
1183 }
1184 else
1185 {
1186 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1187 setAX(ERROR_INVALID_FUNCTION);
1188 }
1189
1190 break;
1191 }
1192
1193 /* IOCTL */
1194 case 0x44:
1195 {
1196 WORD Length = getCX();
1197
1198 if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length))
1199 {
1200 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1201 setAX(Length);
1202 }
1203 else
1204 {
1205 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1206 setAX(DosLastError);
1207 }
1208
1209 break;
1210 }
1211
1212 /* Duplicate Handle */
1213 case 0x45:
1214 {
1215 WORD NewHandle = DosDuplicateHandle(getBX());
1216
1217 if (NewHandle != INVALID_DOS_HANDLE)
1218 {
1219 setAX(NewHandle);
1220 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1221 }
1222 else
1223 {
1224 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1225 setAX(DosLastError);
1226 }
1227
1228 break;
1229 }
1230
1231 /* Force Duplicate Handle */
1232 case 0x46:
1233 {
1234 if (DosForceDuplicateHandle(getBX(), getCX()))
1235 {
1236 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1237 }
1238 else
1239 {
1240 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1241 setAX(ERROR_INVALID_HANDLE);
1242 }
1243
1244 break;
1245 }
1246
1247 /* Get Current Directory */
1248 case 0x47:
1249 {
1250 BYTE DriveNumber = getDL();
1251 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI());
1252
1253 /* Get the real drive number */
1254 if (DriveNumber == 0)
1255 {
1256 DriveNumber = CurrentDrive;
1257 }
1258 else
1259 {
1260 /* Decrement DriveNumber since it was 1-based */
1261 DriveNumber--;
1262 }
1263
1264 if (DriveNumber <= LastDrive - 'A')
1265 {
1266 /*
1267 * Copy the current directory into the target buffer.
1268 * It doesn't contain the drive letter and the backslash.
1269 */
1270 strncpy(String, CurrentDirectories[DriveNumber], DOS_DIR_LENGTH);
1271 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1272 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
1273 }
1274 else
1275 {
1276 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1277 setAX(ERROR_INVALID_DRIVE);
1278 }
1279
1280 break;
1281 }
1282
1283 /* Allocate Memory */
1284 case 0x48:
1285 {
1286 WORD MaxAvailable = 0;
1287 WORD Segment = DosAllocateMemory(getBX(), &MaxAvailable);
1288
1289 if (Segment != 0)
1290 {
1291 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1292 setAX(Segment);
1293 }
1294 else
1295 {
1296 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1297 setAX(DosLastError);
1298 setBX(MaxAvailable);
1299 }
1300
1301 break;
1302 }
1303
1304 /* Free Memory */
1305 case 0x49:
1306 {
1307 if (DosFreeMemory(getES()))
1308 {
1309 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1310 }
1311 else
1312 {
1313 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1314 setAX(ERROR_ARENA_TRASHED);
1315 }
1316
1317 break;
1318 }
1319
1320 /* Resize Memory Block */
1321 case 0x4A:
1322 {
1323 WORD Size;
1324
1325 if (DosResizeMemory(getES(), getBX(), &Size))
1326 {
1327 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1328 }
1329 else
1330 {
1331 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1332 setAX(DosLastError);
1333 setBX(Size);
1334 }
1335
1336 break;
1337 }
1338
1339 /* Execute */
1340 case 0x4B:
1341 {
1342 BYTE OrgAL = getAL();
1343 LPSTR ProgramName = SEG_OFF_TO_PTR(getDS(), getDX());
1344 PDOS_EXEC_PARAM_BLOCK ParamBlock = SEG_OFF_TO_PTR(getES(), getBX());
1345 WORD ErrorCode;
1346
1347 if (OrgAL <= DOS_LOAD_OVERLAY)
1348 {
1349 DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)OrgAL;
1350
1351 #ifndef STANDALONE
1352 if (LoadType == DOS_LOAD_AND_EXECUTE)
1353 {
1354 /* Create a new process */
1355 ErrorCode = DosCreateProcess(ProgramName, ParamBlock);
1356 }
1357 else
1358 #endif
1359 {
1360 /* Just load an executable */
1361 ErrorCode = DosLoadExecutable(LoadType,
1362 ProgramName,
1363 ParamBlock,
1364 NULL,
1365 NULL);
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(getDX(), getCX()), MAKELONG(getDI(), getSI())))
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(getDX(), getCX()), MAKELONG(getDI(), getSI())))
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 DosInt27h(LPWORD Stack)
1821 {
1822 DosTerminateProcess(getCS(), 0, (getDX() + 0x0F) >> 4);
1823 }
1824
1825 VOID WINAPI DosFastConOut(LPWORD Stack)
1826 {
1827 /*
1828 * This is the DOS 2+ Fast Console Output Interrupt.
1829 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
1830 *
1831 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
1832 * for more information.
1833 */
1834
1835 /* Save AX and BX */
1836 USHORT AX = getAX();
1837 USHORT BX = getBX();
1838
1839 /*
1840 * Set the parameters:
1841 * AL contains the character to print (already set),
1842 * BL contains the character attribute,
1843 * BH contains the video page to use.
1844 */
1845 setBL(DOS_CHAR_ATTRIBUTE);
1846 setBH(Bda->VideoPage);
1847
1848 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
1849 setAH(0x0E);
1850 Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT);
1851
1852 /* Restore AX and BX */
1853 setBX(BX);
1854 setAX(AX);
1855 }
1856
1857 VOID WINAPI DosInt2Fh(LPWORD Stack)
1858 {
1859 switch (getAH())
1860 {
1861 /* Extended Memory Specification */
1862 case 0x43:
1863 {
1864 DWORD DriverEntry;
1865 if (!XmsGetDriverEntry(&DriverEntry)) break;
1866
1867 if (getAL() == 0x00)
1868 {
1869 /* The driver is loaded */
1870 setAL(0x80);
1871 }
1872 else if (getAL() == 0x10)
1873 {
1874 setES(HIWORD(DriverEntry));
1875 setBX(LOWORD(DriverEntry));
1876 }
1877 else
1878 {
1879 DPRINT1("Unknown DOS XMS Function: INT 0x2F, AH = 43h, AL = %xh\n", getAL());
1880 }
1881
1882 break;
1883 }
1884
1885 default:
1886 {
1887 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
1888 getAH(), getAL());
1889 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1890 }
1891 }
1892 }
1893
1894 BOOLEAN DosKRNLInitialize(VOID)
1895 {
1896 #if 1
1897
1898 UCHAR i;
1899 CHAR CurrentDirectory[MAX_PATH];
1900 CHAR DosDirectory[DOS_DIR_LENGTH];
1901 LPSTR Path;
1902 PDOS_SFT Sft;
1903
1904 const BYTE NullDriverRoutine[] = {
1905 /* Strategy routine entry */
1906 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
1907 0xC7,
1908 0x47,
1909 FIELD_OFFSET(DOS_REQUEST_HEADER, Status),
1910 LOBYTE(DOS_DEVSTAT_DONE),
1911 HIBYTE(DOS_DEVSTAT_DONE),
1912
1913 /* Interrupt routine entry */
1914 0xCB, // retf
1915 };
1916
1917 FILE *Stream;
1918 WCHAR Buffer[256];
1919
1920 /* Setup the InDOS flag */
1921 InDos = (PBYTE)FAR_POINTER(INDOS_POINTER);
1922 *InDos = 0;
1923
1924 /* Clear the current directory buffer */
1925 RtlZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
1926
1927 /* Get the current directory */
1928 if (!GetCurrentDirectoryA(MAX_PATH, CurrentDirectory))
1929 {
1930 // TODO: Use some kind of default path?
1931 return FALSE;
1932 }
1933
1934 /* Convert that to a DOS path */
1935 if (!GetShortPathNameA(CurrentDirectory, DosDirectory, DOS_DIR_LENGTH))
1936 {
1937 // TODO: Use some kind of default path?
1938 return FALSE;
1939 }
1940
1941 /* Set the drive */
1942 CurrentDrive = DosDirectory[0] - 'A';
1943
1944 /* Get the directory part of the path */
1945 Path = strchr(DosDirectory, '\\');
1946 if (Path != NULL)
1947 {
1948 /* Skip the backslash */
1949 Path++;
1950 }
1951
1952 /* Set the directory */
1953 if (Path != NULL)
1954 {
1955 strncpy(CurrentDirectories[CurrentDrive], Path, DOS_DIR_LENGTH);
1956 }
1957
1958 /* Read CONFIG.SYS */
1959 Stream = _wfopen(DOS_CONFIG_PATH, L"r");
1960 if (Stream != NULL)
1961 {
1962 while (fgetws(Buffer, 256, Stream))
1963 {
1964 // TODO: Parse the line
1965 }
1966 fclose(Stream);
1967 }
1968
1969 /* Initialize the list of lists */
1970 SysVars = (PDOS_SYSVARS)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT, 0);
1971 RtlZeroMemory(SysVars, sizeof(DOS_SYSVARS));
1972 SysVars->FirstMcb = FIRST_MCB_SEGMENT;
1973 SysVars->FirstSft = MAKELONG(MASTER_SFT_OFFSET, DOS_DATA_SEGMENT);
1974
1975 /* Initialize the NUL device driver */
1976 SysVars->NullDevice.Link = 0xFFFFFFFF;
1977 SysVars->NullDevice.DeviceAttributes = DOS_DEVATTR_NUL | DOS_DEVATTR_CHARACTER;
1978 SysVars->NullDevice.StrategyRoutine = FIELD_OFFSET(DOS_SYSVARS, NullDriverRoutine);
1979 SysVars->NullDevice.InterruptRoutine = SysVars->NullDevice.StrategyRoutine + 6;
1980 RtlFillMemory(SysVars->NullDevice.DeviceName,
1981 sizeof(SysVars->NullDevice.DeviceName),
1982 ' ');
1983 RtlCopyMemory(SysVars->NullDevice.DeviceName, "NUL", strlen("NUL"));
1984 RtlCopyMemory(SysVars->NullDriverRoutine,
1985 NullDriverRoutine,
1986 sizeof(NullDriverRoutine));
1987
1988 /* Initialize the SFT */
1989 Sft = (PDOS_SFT)FAR_POINTER(SysVars->FirstSft);
1990 Sft->Link = 0xFFFFFFFF;
1991 Sft->NumDescriptors = DOS_SFT_SIZE;
1992
1993 for (i = 0; i < Sft->NumDescriptors; i++)
1994 {
1995 /* Clear the file descriptor entry */
1996 RtlZeroMemory(&Sft->FileDescriptors[i], sizeof(DOS_FILE_DESCRIPTOR));
1997 }
1998
1999 #endif
2000
2001 /* Initialize the callback context */
2002 InitializeContext(&DosContext, DOS_CODE_SEGMENT, 0x0000);
2003
2004 /* Register the DOS 32-bit Interrupts */
2005 RegisterDosInt32(0x20, DosInt20h );
2006 RegisterDosInt32(0x21, DosInt21h );
2007 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2008 RegisterDosInt32(0x23, DosBreakInterrupt); // Ctrl-C / Ctrl-Break
2009 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2010 RegisterDosInt32(0x27, DosInt27h ); // Terminate and Stay Resident
2011 RegisterDosInt32(0x29, DosFastConOut ); // DOS 2+ Fast Console Output
2012 RegisterDosInt32(0x2F, DosInt2Fh );
2013
2014 /* Unimplemented DOS interrupts */
2015 RegisterDosInt32(0x2A, NULL); // Network - Installation Check
2016
2017 /* Load the CON driver */
2018 ConDrvInitialize();
2019
2020 /* Load the XMS driver (HIMEM) */
2021 XmsInitialize();
2022
2023 /* Load the EMS driver */
2024 if (!EmsDrvInitialize(EMS_TOTAL_PAGES))
2025 {
2026 DPRINT1("Could not initialize EMS. EMS will not be available.\n"
2027 "Try reducing the number of EMS pages.\n");
2028 }
2029
2030 return TRUE;
2031 }
2032
2033 /* EOF */