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