[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
772 break;
773 }
774
775 /* Get Interrupt Vector */
776 case 0x35:
777 {
778 DWORD FarPointer = ((PDWORD)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 DWORD ReturnAddress = MAKELONG(Stack[STACK_IP], Stack[STACK_CS]);
1346 WORD ErrorCode;
1347
1348 if (OrgAL <= DOS_LOAD_OVERLAY)
1349 {
1350 DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)OrgAL;
1351
1352 #ifndef STANDALONE
1353 if (LoadType == DOS_LOAD_AND_EXECUTE)
1354 {
1355 /* Create a new process */
1356 ErrorCode = DosCreateProcess(ProgramName,
1357 ParamBlock,
1358 ReturnAddress);
1359 }
1360 else
1361 #endif
1362 {
1363 /* Just load an executable */
1364 ErrorCode = DosLoadExecutable(LoadType,
1365 ProgramName,
1366 ParamBlock,
1367 NULL,
1368 NULL,
1369 ReturnAddress);
1370 }
1371 }
1372 else if (OrgAL == 0x05)
1373 {
1374 // http://www.ctyme.com/intr/rb-2942.htm
1375 DPRINT1("Set execution state is UNIMPLEMENTED\n");
1376 ErrorCode = ERROR_CALL_NOT_IMPLEMENTED;
1377 }
1378 else
1379 {
1380 ErrorCode = ERROR_INVALID_FUNCTION;
1381 }
1382
1383 if (ErrorCode == ERROR_SUCCESS)
1384 {
1385 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1386 }
1387 else
1388 {
1389 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1390 setAX(ErrorCode);
1391 }
1392
1393 break;
1394 }
1395
1396 /* Terminate With Return Code */
1397 case 0x4C:
1398 {
1399 DosTerminateProcess(CurrentPsp, getAL(), 0);
1400 break;
1401 }
1402
1403 /* Get Return Code (ERRORLEVEL) */
1404 case 0x4D:
1405 {
1406 /*
1407 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
1408 * DosErrorLevel is cleared after being read by this function.
1409 */
1410 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1411 setAX(DosErrorLevel);
1412 DosErrorLevel = 0x0000; // Clear it
1413 break;
1414 }
1415
1416 /* Find First File */
1417 case 0x4E:
1418 {
1419 WORD Result = (WORD)demFileFindFirst(FAR_POINTER(DiskTransferArea),
1420 SEG_OFF_TO_PTR(getDS(), getDX()),
1421 getCX());
1422
1423 setAX(Result);
1424
1425 if (Result == ERROR_SUCCESS)
1426 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1427 else
1428 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1429
1430 break;
1431 }
1432
1433 /* Find Next File */
1434 case 0x4F:
1435 {
1436 WORD Result = (WORD)demFileFindNext(FAR_POINTER(DiskTransferArea));
1437
1438 setAX(Result);
1439
1440 if (Result == ERROR_SUCCESS)
1441 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1442 else
1443 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1444
1445 break;
1446 }
1447
1448 /* Internal - Set Current Process ID (Set PSP Address) */
1449 case 0x50:
1450 {
1451 DosSetProcessContext(getBX());
1452 break;
1453 }
1454
1455 /* Internal - Get Current Process ID (Get PSP Address) */
1456 case 0x51:
1457 /* Get Current PSP Address */
1458 case 0x62:
1459 {
1460 /*
1461 * Undocumented AH=51h is identical to the documented AH=62h.
1462 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
1463 * and http://www.ctyme.com/intr/rb-3140.htm
1464 * for more information.
1465 */
1466 setBX(CurrentPsp);
1467 break;
1468 }
1469
1470 /* Internal - Get "List of lists" (SYSVARS) */
1471 case 0x52:
1472 {
1473 /*
1474 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
1475 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
1476 * for more information.
1477 */
1478
1479 /* Return the DOS "list of lists" in ES:BX */
1480 setES(DOS_DATA_SEGMENT);
1481 setBX(FIELD_OFFSET(DOS_SYSVARS, FirstDpb));
1482
1483 break;
1484 }
1485
1486 /* Create Child PSP */
1487 case 0x55:
1488 {
1489 DosCreatePsp(getDX(), getSI());
1490 break;
1491 }
1492
1493 /* Rename File */
1494 case 0x56:
1495 {
1496 LPSTR ExistingFileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1497 LPSTR NewFileName = (LPSTR)SEG_OFF_TO_PTR(getES(), getDI());
1498
1499 /*
1500 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
1501 * for more information.
1502 */
1503
1504 if (MoveFileA(ExistingFileName, NewFileName))
1505 {
1506 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1507 }
1508 else
1509 {
1510 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1511 setAX(GetLastError());
1512 }
1513
1514 break;
1515 }
1516
1517 /* Get/Set Memory Management Options */
1518 case 0x58:
1519 {
1520 if (getAL() == 0x00)
1521 {
1522 /* Get allocation strategy */
1523 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1524 setAX(DosAllocStrategy);
1525 }
1526 else if (getAL() == 0x01)
1527 {
1528 /* Set allocation strategy */
1529
1530 if ((getBL() & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
1531 == (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
1532 {
1533 /* Can't set both */
1534 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1535 setAX(ERROR_INVALID_PARAMETER);
1536 break;
1537 }
1538
1539 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT)
1540 {
1541 /* Invalid allocation strategy */
1542 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1543 setAX(ERROR_INVALID_PARAMETER);
1544 break;
1545 }
1546
1547 DosAllocStrategy = getBL();
1548 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1549 }
1550 else if (getAL() == 0x02)
1551 {
1552 /* Get UMB link state */
1553 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1554 setAL(DosUmbLinked ? 0x01 : 0x00);
1555 }
1556 else if (getAL() == 0x03)
1557 {
1558 /* Set UMB link state */
1559 if (getBX()) DosLinkUmb();
1560 else DosUnlinkUmb();
1561 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1562 }
1563 else
1564 {
1565 /* Invalid or unsupported function */
1566 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1567 setAX(ERROR_INVALID_FUNCTION);
1568 }
1569
1570 break;
1571 }
1572
1573 /* Get Extended Error Information */
1574 case 0x59:
1575 {
1576 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
1577 getBX());
1578 break;
1579 }
1580
1581 /* Create Temporary File */
1582 case 0x5A:
1583 {
1584 LPSTR PathName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1585 LPSTR FileName = PathName; // The buffer for the path and the full file name is the same.
1586 UINT uRetVal;
1587 WORD FileHandle;
1588 WORD ErrorCode;
1589
1590 /*
1591 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
1592 * for more information.
1593 */
1594
1595 // FIXME: Check for buffer validity?
1596 // It should be a ASCIZ path ending with a '\' + 13 zero bytes
1597 // to receive the generated filename.
1598
1599 /* First create the temporary file */
1600 uRetVal = GetTempFileNameA(PathName, NULL, 0, FileName);
1601 if (uRetVal == 0)
1602 {
1603 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1604 setAX(GetLastError());
1605 break;
1606 }
1607
1608 /* Now try to open it in read/write access */
1609 ErrorCode = DosOpenFile(&FileHandle, FileName, 2);
1610 if (ErrorCode == ERROR_SUCCESS)
1611 {
1612 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1613 setAX(FileHandle);
1614 }
1615 else
1616 {
1617 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1618 setAX(ErrorCode);
1619 }
1620
1621 break;
1622 }
1623
1624 /* Create New File */
1625 case 0x5B:
1626 {
1627 WORD FileHandle;
1628 WORD ErrorCode = DosCreateFile(&FileHandle,
1629 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
1630 CREATE_NEW,
1631 getCX());
1632
1633 if (ErrorCode == ERROR_SUCCESS)
1634 {
1635 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1636 setAX(FileHandle);
1637 }
1638 else
1639 {
1640 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1641 setAX(ErrorCode);
1642 }
1643
1644 break;
1645 }
1646
1647 /* Lock/Unlock Region of File */
1648 case 0x5C:
1649 {
1650 if (getAL() == 0x00)
1651 {
1652 /* Lock region of file */
1653 if (DosLockFile(getBX(), MAKELONG(getCX(), getDX()), MAKELONG(getSI(), getDI())))
1654 {
1655 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1656 }
1657 else
1658 {
1659 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1660 setAX(DosLastError);
1661 }
1662 }
1663 else if (getAL() == 0x01)
1664 {
1665 /* Unlock region of file */
1666 if (DosUnlockFile(getBX(), MAKELONG(getCX(), getDX()), MAKELONG(getSI(), getDI())))
1667 {
1668 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1669 }
1670 else
1671 {
1672 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1673 setAX(DosLastError);
1674 }
1675 }
1676 else
1677 {
1678 /* Invalid subfunction */
1679 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1680 setAX(ERROR_INVALID_FUNCTION);
1681 }
1682
1683 break;
1684 }
1685
1686 /* Canonicalize File Name or Path */
1687 case 0x60:
1688 {
1689 /*
1690 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
1691 * for more information.
1692 */
1693
1694 /*
1695 * We suppose that the DOS app gave to us a valid
1696 * 128-byte long buffer for the canonicalized name.
1697 */
1698 DWORD dwRetVal = GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
1699 128,
1700 SEG_OFF_TO_PTR(getES(), getDI()),
1701 NULL);
1702 if (dwRetVal == 0)
1703 {
1704 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1705 setAX(GetLastError());
1706 }
1707 else
1708 {
1709 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1710 setAX(0x0000);
1711 }
1712
1713 // FIXME: Convert the full path name into short version.
1714 // We cannot reliably use GetShortPathName, because it fails
1715 // if the path name given doesn't exist. However this DOS
1716 // function AH=60h should be able to work even for non-existing
1717 // path and file names.
1718
1719 break;
1720 }
1721
1722 /* Set Handle Count */
1723 case 0x67:
1724 {
1725 if (!DosResizeHandleTable(getBX()))
1726 {
1727 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1728 setAX(DosLastError);
1729 }
1730 else Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1731
1732 break;
1733 }
1734
1735 /* Commit File */
1736 case 0x68:
1737 case 0x6A:
1738 {
1739 /*
1740 * Function 6Ah is identical to function 68h,
1741 * and sets AH to 68h if success.
1742 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
1743 * for more information.
1744 */
1745 setAH(0x68);
1746
1747 if (DosFlushFileBuffers(getBX()))
1748 {
1749 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1750 }
1751 else
1752 {
1753 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1754 setAX(GetLastError());
1755 }
1756
1757 break;
1758 }
1759
1760 /* Extended Open/Create */
1761 case 0x6C:
1762 {
1763 WORD FileHandle;
1764 WORD CreationStatus;
1765 WORD ErrorCode;
1766
1767 /* Check for AL == 00 */
1768 if (getAL() != 0x00)
1769 {
1770 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1771 setAX(ERROR_INVALID_FUNCTION);
1772 break;
1773 }
1774
1775 /*
1776 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
1777 * for the full detailed description.
1778 *
1779 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
1780 */
1781
1782 ErrorCode = DosCreateFileEx(&FileHandle,
1783 &CreationStatus,
1784 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getSI()),
1785 getBL(),
1786 getDL(),
1787 getCX());
1788
1789 if (ErrorCode == ERROR_SUCCESS)
1790 {
1791 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1792 setCX(CreationStatus);
1793 setAX(FileHandle);
1794 }
1795 else
1796 {
1797 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1798 setAX(ErrorCode);
1799 }
1800
1801 break;
1802 }
1803
1804 /* Unsupported */
1805 default:
1806 {
1807 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
1808 getAH(), getAL());
1809
1810 setAL(0); // Some functions expect AL to be 0 when it's not supported.
1811 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1812 }
1813 }
1814
1815 (*InDos)--;
1816 }
1817
1818 VOID WINAPI DosBreakInterrupt(LPWORD Stack)
1819 {
1820 /* Set CF to terminate the running process */
1821 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1822 }
1823
1824 VOID WINAPI DosInt27h(LPWORD Stack)
1825 {
1826 DosTerminateProcess(getCS(), 0, (getDX() + 0x0F) >> 4);
1827 }
1828
1829 VOID WINAPI DosFastConOut(LPWORD Stack)
1830 {
1831 /*
1832 * This is the DOS 2+ Fast Console Output Interrupt.
1833 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
1834 *
1835 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
1836 * for more information.
1837 */
1838
1839 /* Save AX and BX */
1840 USHORT AX = getAX();
1841 USHORT BX = getBX();
1842
1843 /*
1844 * Set the parameters:
1845 * AL contains the character to print (already set),
1846 * BL contains the character attribute,
1847 * BH contains the video page to use.
1848 */
1849 setBL(DOS_CHAR_ATTRIBUTE);
1850 setBH(Bda->VideoPage);
1851
1852 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
1853 setAH(0x0E);
1854 Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT);
1855
1856 /* Restore AX and BX */
1857 setBX(BX);
1858 setAX(AX);
1859 }
1860
1861 VOID WINAPI DosInt2Fh(LPWORD Stack)
1862 {
1863 switch (getAH())
1864 {
1865 /* Extended Memory Specification */
1866 case 0x43:
1867 {
1868 DWORD DriverEntry;
1869 if (!XmsGetDriverEntry(&DriverEntry)) break;
1870
1871 if (getAL() == 0x00)
1872 {
1873 /* The driver is loaded */
1874 setAL(0x80);
1875 }
1876 else if (getAL() == 0x10)
1877 {
1878 setES(HIWORD(DriverEntry));
1879 setBX(LOWORD(DriverEntry));
1880 }
1881 else
1882 {
1883 DPRINT1("Unknown DOS XMS Function: INT 0x2F, AH = 43h, AL = %xh\n", getAL());
1884 }
1885
1886 break;
1887 }
1888
1889 default:
1890 {
1891 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
1892 getAH(), getAL());
1893 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1894 }
1895 }
1896 }
1897
1898 BOOLEAN DosKRNLInitialize(VOID)
1899 {
1900 #if 1
1901
1902 UCHAR i;
1903 CHAR CurrentDirectory[MAX_PATH];
1904 CHAR DosDirectory[DOS_DIR_LENGTH];
1905 LPSTR Path;
1906 PDOS_SFT Sft;
1907
1908 const BYTE NullDriverRoutine[] = {
1909 /* Strategy routine entry */
1910 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
1911 0xC7,
1912 0x47,
1913 FIELD_OFFSET(DOS_REQUEST_HEADER, Status),
1914 LOBYTE(DOS_DEVSTAT_DONE),
1915 HIBYTE(DOS_DEVSTAT_DONE),
1916
1917 /* Interrupt routine entry */
1918 0xCB, // retf
1919 };
1920
1921 FILE *Stream;
1922 WCHAR Buffer[256];
1923
1924 /* Setup the InDOS flag */
1925 InDos = (PBYTE)FAR_POINTER(INDOS_POINTER);
1926 *InDos = 0;
1927
1928 /* Clear the current directory buffer */
1929 RtlZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
1930
1931 /* Get the current directory */
1932 if (!GetCurrentDirectoryA(MAX_PATH, CurrentDirectory))
1933 {
1934 // TODO: Use some kind of default path?
1935 return FALSE;
1936 }
1937
1938 /* Convert that to a DOS path */
1939 if (!GetShortPathNameA(CurrentDirectory, DosDirectory, DOS_DIR_LENGTH))
1940 {
1941 // TODO: Use some kind of default path?
1942 return FALSE;
1943 }
1944
1945 /* Set the drive */
1946 CurrentDrive = DosDirectory[0] - 'A';
1947
1948 /* Get the directory part of the path */
1949 Path = strchr(DosDirectory, '\\');
1950 if (Path != NULL)
1951 {
1952 /* Skip the backslash */
1953 Path++;
1954 }
1955
1956 /* Set the directory */
1957 if (Path != NULL)
1958 {
1959 strncpy(CurrentDirectories[CurrentDrive], Path, DOS_DIR_LENGTH);
1960 }
1961
1962 /* Read CONFIG.SYS */
1963 Stream = _wfopen(DOS_CONFIG_PATH, L"r");
1964 if (Stream != NULL)
1965 {
1966 while (fgetws(Buffer, 256, Stream))
1967 {
1968 // TODO: Parse the line
1969 }
1970 fclose(Stream);
1971 }
1972
1973 /* Initialize the list of lists */
1974 SysVars = (PDOS_SYSVARS)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT, 0);
1975 RtlZeroMemory(SysVars, sizeof(DOS_SYSVARS));
1976 SysVars->FirstMcb = FIRST_MCB_SEGMENT;
1977 SysVars->FirstSft = MAKELONG(MASTER_SFT_OFFSET, DOS_DATA_SEGMENT);
1978
1979 /* Initialize the NUL device driver */
1980 SysVars->NullDevice.Link = 0xFFFFFFFF;
1981 SysVars->NullDevice.DeviceAttributes = DOS_DEVATTR_NUL | DOS_DEVATTR_CHARACTER;
1982 SysVars->NullDevice.StrategyRoutine = FIELD_OFFSET(DOS_SYSVARS, NullDriverRoutine);
1983 SysVars->NullDevice.InterruptRoutine = SysVars->NullDevice.StrategyRoutine + 6;
1984 RtlFillMemory(SysVars->NullDevice.DeviceName,
1985 sizeof(SysVars->NullDevice.DeviceName),
1986 ' ');
1987 RtlCopyMemory(SysVars->NullDevice.DeviceName, "NUL", strlen("NUL"));
1988 RtlCopyMemory(SysVars->NullDriverRoutine,
1989 NullDriverRoutine,
1990 sizeof(NullDriverRoutine));
1991
1992 /* Initialize the SFT */
1993 Sft = (PDOS_SFT)FAR_POINTER(SysVars->FirstSft);
1994 Sft->Link = 0xFFFFFFFF;
1995 Sft->NumDescriptors = DOS_SFT_SIZE;
1996
1997 for (i = 0; i < Sft->NumDescriptors; i++)
1998 {
1999 /* Clear the file descriptor entry */
2000 RtlZeroMemory(&Sft->FileDescriptors[i], sizeof(DOS_FILE_DESCRIPTOR));
2001 }
2002
2003 #endif
2004
2005 /* Initialize the callback context */
2006 InitializeContext(&DosContext, 0x0070, 0x0000);
2007
2008 /* Register the DOS 32-bit Interrupts */
2009 RegisterDosInt32(0x20, DosInt20h );
2010 RegisterDosInt32(0x21, DosInt21h );
2011 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2012 RegisterDosInt32(0x23, DosBreakInterrupt); // Ctrl-C / Ctrl-Break
2013 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2014 RegisterDosInt32(0x27, DosInt27h ); // Terminate and Stay Resident
2015 RegisterDosInt32(0x29, DosFastConOut ); // DOS 2+ Fast Console Output
2016 RegisterDosInt32(0x2F, DosInt2Fh );
2017
2018 /* Load the EMS driver */
2019 if (!EmsDrvInitialize(EMS_TOTAL_PAGES))
2020 {
2021 DPRINT1("Could not initialize EMS. EMS will not be available.\n"
2022 "Try reducing the number of EMS pages.\n");
2023 }
2024
2025 /* Load the XMS driver (HIMEM) */
2026 XmsInitialize();
2027
2028 /* Load the CON driver */
2029 ConDrvInitialize();
2030
2031 return TRUE;
2032 }
2033
2034 /* EOF */