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