084f027f1c56f49bfbc6c66b078ea316ef32a387
[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 /* Get Free Disk Space */
836 case 0x36:
837 {
838 CHAR RootPath[3] = "X:\\";
839 DWORD SectorsPerCluster;
840 DWORD BytesPerSector;
841 DWORD NumberOfFreeClusters;
842 DWORD TotalNumberOfClusters;
843
844 if (getDL() == 0) RootPath[0] = 'A' + CurrentDrive;
845 else RootPath[0] = 'A' + getDL() - 1;
846
847 if (GetDiskFreeSpaceA(RootPath,
848 &SectorsPerCluster,
849 &BytesPerSector,
850 &NumberOfFreeClusters,
851 &TotalNumberOfClusters))
852 {
853 setAX(LOWORD(SectorsPerCluster));
854 setCX(LOWORD(BytesPerSector));
855 setBX(LOWORD(NumberOfFreeClusters));
856 setDX(LOWORD(TotalNumberOfClusters));
857 }
858 else
859 {
860 /* Error */
861 setAX(0xFFFF);
862 }
863
864 break;
865 }
866
867 /* SWITCH character - AVAILDEV */
868 case 0x37:
869 {
870 if (getAL() == 0x00)
871 {
872 /*
873 * DOS 2+ - "SWITCHAR" - GET SWITCH CHARACTER
874 * This setting is ignored by MS-DOS 4.0+.
875 * MS-DOS 5+ always return AL=00h/DL=2Fh.
876 * See Ralf Brown: http://www.ctyme.com/intr/rb-2752.htm
877 * for more information.
878 */
879 setDL('/');
880 setAL(0x00);
881 }
882 else if (getAL() == 0x01)
883 {
884 /*
885 * DOS 2+ - "SWITCHAR" - SET SWITCH CHARACTER
886 * This setting is ignored by MS-DOS 5+.
887 * See Ralf Brown: http://www.ctyme.com/intr/rb-2753.htm
888 * for more information.
889 */
890 // getDL();
891 setAL(0xFF);
892 }
893 else if (getAL() == 0x02)
894 {
895 /*
896 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
897 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
898 * for more information.
899 */
900 // setDL();
901 setAL(0xFF);
902 }
903 else if (getAL() == 0x03)
904 {
905 /*
906 * DOS 2.x and 3.3+ only - "AVAILDEV" - SPECIFY \DEV\ PREFIX USE
907 * See Ralf Brown: http://www.ctyme.com/intr/rb-2754.htm
908 * for more information.
909 */
910 // getDL();
911 setAL(0xFF);
912 }
913 else
914 {
915 /* Invalid subfunction */
916 setAL(0xFF);
917 }
918
919 break;
920 }
921
922 /* Get/Set Country-dependent Information */
923 case 0x38:
924 {
925 CountryCodeBuffer = (PDOS_COUNTRY_CODE_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
926
927 if (getAL() == 0x00)
928 {
929 /* Get */
930 Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDATE,
931 &CountryCodeBuffer->TimeFormat,
932 sizeof(CountryCodeBuffer->TimeFormat) / sizeof(TCHAR));
933 if (Return == 0)
934 {
935 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
936 setAX(LOWORD(GetLastError()));
937 break;
938 }
939
940 Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SCURRENCY,
941 &CountryCodeBuffer->CurrencySymbol,
942 sizeof(CountryCodeBuffer->CurrencySymbol) / sizeof(TCHAR));
943 if (Return == 0)
944 {
945 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
946 setAX(LOWORD(GetLastError()));
947 break;
948 }
949
950 Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND,
951 &CountryCodeBuffer->ThousandSep,
952 sizeof(CountryCodeBuffer->ThousandSep) / sizeof(TCHAR));
953 if (Return == 0)
954 {
955 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
956 setAX(LOWORD(GetLastError()));
957 break;
958 }
959
960 Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
961 &CountryCodeBuffer->DecimalSep,
962 sizeof(CountryCodeBuffer->DecimalSep) / sizeof(TCHAR));
963 if (Return == 0)
964 {
965 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
966 setAX(LOWORD(GetLastError()));
967 break;
968 }
969
970 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
971 }
972 else
973 {
974 // TODO: NOT IMPLEMENTED
975 UNIMPLEMENTED;
976 }
977
978 break;
979 }
980
981 /* Create Directory */
982 case 0x39:
983 {
984 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
985
986 if (CreateDirectoryA(String, NULL))
987 {
988 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
989 }
990 else
991 {
992 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
993 setAX(LOWORD(GetLastError()));
994 }
995
996 break;
997 }
998
999 /* Remove Directory */
1000 case 0x3A:
1001 {
1002 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
1003
1004 if (RemoveDirectoryA(String))
1005 {
1006 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1007 }
1008 else
1009 {
1010 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1011 setAX(LOWORD(GetLastError()));
1012 }
1013
1014 break;
1015 }
1016
1017 /* Set Current Directory */
1018 case 0x3B:
1019 {
1020 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
1021
1022 if (DosChangeDirectory(String))
1023 {
1024 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1025 }
1026 else
1027 {
1028 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1029 setAX(DosLastError);
1030 }
1031
1032 break;
1033 }
1034
1035 /* Create or Truncate File */
1036 case 0x3C:
1037 {
1038 WORD FileHandle;
1039 WORD ErrorCode = DosCreateFile(&FileHandle,
1040 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
1041 CREATE_ALWAYS,
1042 getCX());
1043
1044 if (ErrorCode == ERROR_SUCCESS)
1045 {
1046 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1047 setAX(FileHandle);
1048 }
1049 else
1050 {
1051 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1052 setAX(ErrorCode);
1053 }
1054
1055 break;
1056 }
1057
1058 /* Open File or Device */
1059 case 0x3D:
1060 {
1061 WORD FileHandle;
1062 LPCSTR FileName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1063 WORD ErrorCode = DosOpenFile(&FileHandle, FileName, getAL());
1064
1065 if (ErrorCode == ERROR_SUCCESS)
1066 {
1067 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1068 setAX(FileHandle);
1069 }
1070 else
1071 {
1072 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1073 setAX(ErrorCode);
1074 }
1075
1076 break;
1077 }
1078
1079 /* Close File or Device */
1080 case 0x3E:
1081 {
1082 if (DosCloseHandle(getBX()))
1083 {
1084 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1085 }
1086 else
1087 {
1088 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1089 setAX(ERROR_INVALID_HANDLE);
1090 }
1091
1092 break;
1093 }
1094
1095 /* Read from File or Device */
1096 case 0x3F:
1097 {
1098 WORD BytesRead = 0;
1099 WORD ErrorCode;
1100
1101 DPRINT("DosReadFile(0x%04X)\n", getBX());
1102
1103 DoEcho = TRUE;
1104 ErrorCode = DosReadFile(getBX(),
1105 MAKELONG(getDX(), getDS()),
1106 getCX(),
1107 &BytesRead);
1108 DoEcho = FALSE;
1109
1110 if (ErrorCode == ERROR_SUCCESS)
1111 {
1112 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1113 setAX(BytesRead);
1114 }
1115 else if (ErrorCode != ERROR_NOT_READY)
1116 {
1117 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1118 setAX(ErrorCode);
1119 }
1120
1121 break;
1122 }
1123
1124 /* Write to File or Device */
1125 case 0x40:
1126 {
1127 WORD BytesWritten = 0;
1128 WORD ErrorCode = DosWriteFile(getBX(),
1129 MAKELONG(getDX(), getDS()),
1130 getCX(),
1131 &BytesWritten);
1132
1133 if (ErrorCode == ERROR_SUCCESS)
1134 {
1135 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1136 setAX(BytesWritten);
1137 }
1138 else
1139 {
1140 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1141 setAX(ErrorCode);
1142 }
1143
1144 break;
1145 }
1146
1147 /* Delete File */
1148 case 0x41:
1149 {
1150 LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1151
1152 if (demFileDelete(FileName) == ERROR_SUCCESS)
1153 {
1154 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1155 /*
1156 * See Ralf Brown: http://www.ctyme.com/intr/rb-2797.htm
1157 * "AX destroyed (DOS 3.3) AL seems to be drive of deleted file."
1158 */
1159 setAL(FileName[0] - 'A');
1160 }
1161 else
1162 {
1163 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1164 setAX(GetLastError());
1165 }
1166
1167 break;
1168 }
1169
1170 /* Seek File */
1171 case 0x42:
1172 {
1173 DWORD NewLocation;
1174 WORD ErrorCode = DosSeekFile(getBX(),
1175 MAKELONG(getDX(), getCX()),
1176 getAL(),
1177 &NewLocation);
1178
1179 if (ErrorCode == ERROR_SUCCESS)
1180 {
1181 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1182
1183 /* Return the new offset in DX:AX */
1184 setDX(HIWORD(NewLocation));
1185 setAX(LOWORD(NewLocation));
1186 }
1187 else
1188 {
1189 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1190 setAX(ErrorCode);
1191 }
1192
1193 break;
1194 }
1195
1196 /* Get/Set File Attributes */
1197 case 0x43:
1198 {
1199 DWORD Attributes;
1200 LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1201
1202 if (getAL() == 0x00)
1203 {
1204 /* Get the attributes */
1205 Attributes = GetFileAttributesA(FileName);
1206
1207 /* Check if it failed */
1208 if (Attributes == INVALID_FILE_ATTRIBUTES)
1209 {
1210 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1211 setAX(GetLastError());
1212 }
1213 else
1214 {
1215 /* Return the attributes that DOS can understand */
1216 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1217 setCX(Attributes & 0x00FF);
1218 }
1219 }
1220 else if (getAL() == 0x01)
1221 {
1222 /* Try to set the attributes */
1223 if (SetFileAttributesA(FileName, getCL()))
1224 {
1225 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1226 }
1227 else
1228 {
1229 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1230 setAX(GetLastError());
1231 }
1232 }
1233 else
1234 {
1235 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1236 setAX(ERROR_INVALID_FUNCTION);
1237 }
1238
1239 break;
1240 }
1241
1242 /* IOCTL */
1243 case 0x44:
1244 {
1245 WORD Length = getCX();
1246
1247 if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length))
1248 {
1249 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1250 setAX(Length);
1251 }
1252 else
1253 {
1254 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1255 setAX(DosLastError);
1256 }
1257
1258 break;
1259 }
1260
1261 /* Duplicate Handle */
1262 case 0x45:
1263 {
1264 WORD NewHandle = DosDuplicateHandle(getBX());
1265
1266 if (NewHandle != INVALID_DOS_HANDLE)
1267 {
1268 setAX(NewHandle);
1269 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1270 }
1271 else
1272 {
1273 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1274 setAX(DosLastError);
1275 }
1276
1277 break;
1278 }
1279
1280 /* Force Duplicate Handle */
1281 case 0x46:
1282 {
1283 if (DosForceDuplicateHandle(getBX(), getCX()))
1284 {
1285 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1286 }
1287 else
1288 {
1289 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1290 setAX(ERROR_INVALID_HANDLE);
1291 }
1292
1293 break;
1294 }
1295
1296 /* Get Current Directory */
1297 case 0x47:
1298 {
1299 BYTE DriveNumber = getDL();
1300 String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI());
1301
1302 /* Get the real drive number */
1303 if (DriveNumber == 0)
1304 {
1305 DriveNumber = CurrentDrive;
1306 }
1307 else
1308 {
1309 /* Decrement DriveNumber since it was 1-based */
1310 DriveNumber--;
1311 }
1312
1313 if (DriveNumber <= LastDrive - 'A')
1314 {
1315 /*
1316 * Copy the current directory into the target buffer.
1317 * It doesn't contain the drive letter and the backslash.
1318 */
1319 strncpy(String, CurrentDirectories[DriveNumber], DOS_DIR_LENGTH);
1320 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1321 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
1322 }
1323 else
1324 {
1325 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1326 setAX(ERROR_INVALID_DRIVE);
1327 }
1328
1329 break;
1330 }
1331
1332 /* Allocate Memory */
1333 case 0x48:
1334 {
1335 WORD MaxAvailable = 0;
1336 WORD Segment = DosAllocateMemory(getBX(), &MaxAvailable);
1337
1338 if (Segment != 0)
1339 {
1340 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1341 setAX(Segment);
1342 }
1343 else
1344 {
1345 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1346 setAX(DosLastError);
1347 setBX(MaxAvailable);
1348 }
1349
1350 break;
1351 }
1352
1353 /* Free Memory */
1354 case 0x49:
1355 {
1356 if (DosFreeMemory(getES()))
1357 {
1358 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1359 }
1360 else
1361 {
1362 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1363 setAX(ERROR_ARENA_TRASHED);
1364 }
1365
1366 break;
1367 }
1368
1369 /* Resize Memory Block */
1370 case 0x4A:
1371 {
1372 WORD Size;
1373
1374 if (DosResizeMemory(getES(), getBX(), &Size))
1375 {
1376 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1377 }
1378 else
1379 {
1380 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1381 setAX(DosLastError);
1382 setBX(Size);
1383 }
1384
1385 break;
1386 }
1387
1388 /* Execute */
1389 case 0x4B:
1390 {
1391 DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)getAL();
1392 LPSTR ProgramName = SEG_OFF_TO_PTR(getDS(), getDX());
1393 PDOS_EXEC_PARAM_BLOCK ParamBlock = SEG_OFF_TO_PTR(getES(), getBX());
1394 DWORD ReturnAddress = MAKELONG(Stack[STACK_IP], Stack[STACK_CS]);
1395 WORD ErrorCode;
1396
1397 #ifndef STANDALONE
1398 if (LoadType == DOS_LOAD_AND_EXECUTE)
1399 {
1400 /* Create a new process */
1401 ErrorCode = DosCreateProcess(ProgramName, ParamBlock, ReturnAddress);
1402 }
1403 else
1404 {
1405 #else
1406 {
1407 #endif
1408 /* Just load an executable */
1409 ErrorCode = DosLoadExecutable(LoadType,
1410 ProgramName,
1411 ParamBlock,
1412 NULL,
1413 NULL,
1414 ReturnAddress);
1415 }
1416
1417 if (ErrorCode == ERROR_SUCCESS)
1418 {
1419 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1420 }
1421 else
1422 {
1423 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1424 setAX(ErrorCode);
1425 }
1426
1427 break;
1428 }
1429
1430 /* Terminate With Return Code */
1431 case 0x4C:
1432 {
1433 DosTerminateProcess(CurrentPsp, getAL(), 0);
1434 break;
1435 }
1436
1437 /* Get Return Code (ERRORLEVEL) */
1438 case 0x4D:
1439 {
1440 /*
1441 * According to Ralf Brown: http://www.ctyme.com/intr/rb-2976.htm
1442 * DosErrorLevel is cleared after being read by this function.
1443 */
1444 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1445 setAX(DosErrorLevel);
1446 DosErrorLevel = 0x0000; // Clear it
1447 break;
1448 }
1449
1450 /* Find First File */
1451 case 0x4E:
1452 {
1453 WORD Result = (WORD)demFileFindFirst(FAR_POINTER(DiskTransferArea),
1454 SEG_OFF_TO_PTR(getDS(), getDX()),
1455 getCX());
1456
1457 setAX(Result);
1458
1459 if (Result == ERROR_SUCCESS)
1460 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1461 else
1462 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1463
1464 break;
1465 }
1466
1467 /* Find Next File */
1468 case 0x4F:
1469 {
1470 WORD Result = (WORD)demFileFindNext(FAR_POINTER(DiskTransferArea));
1471
1472 setAX(Result);
1473
1474 if (Result == ERROR_SUCCESS)
1475 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1476 else
1477 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1478
1479 break;
1480 }
1481
1482 /* Internal - Set Current Process ID (Set PSP Address) */
1483 case 0x50:
1484 {
1485 DosSetProcessContext(getBX());
1486 break;
1487 }
1488
1489 /* Internal - Get Current Process ID (Get PSP Address) */
1490 case 0x51:
1491 /* Get Current PSP Address */
1492 case 0x62:
1493 {
1494 /*
1495 * Undocumented AH=51h is identical to the documented AH=62h.
1496 * See Ralf Brown: http://www.ctyme.com/intr/rb-2982.htm
1497 * and http://www.ctyme.com/intr/rb-3140.htm
1498 * for more information.
1499 */
1500 setBX(CurrentPsp);
1501 break;
1502 }
1503
1504 /* Internal - Get "List of lists" (SYSVARS) */
1505 case 0x52:
1506 {
1507 /*
1508 * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
1509 * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
1510 * for more information.
1511 */
1512
1513 /* Return the DOS "list of lists" in ES:BX */
1514 setES(DOS_DATA_SEGMENT);
1515 setBX(FIELD_OFFSET(DOS_SYSVARS, FirstDpb));
1516
1517 break;
1518 }
1519
1520 /* Create Child PSP */
1521 case 0x55:
1522 {
1523 DosCreatePsp(getDX(), getSI());
1524 break;
1525 }
1526
1527 /* Rename File */
1528 case 0x56:
1529 {
1530 LPSTR ExistingFileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1531 LPSTR NewFileName = (LPSTR)SEG_OFF_TO_PTR(getES(), getDI());
1532
1533 /*
1534 * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
1535 * for more information.
1536 */
1537
1538 if (MoveFileA(ExistingFileName, NewFileName))
1539 {
1540 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1541 }
1542 else
1543 {
1544 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1545 setAX(GetLastError());
1546 }
1547
1548 break;
1549 }
1550
1551 /* Get/Set Memory Management Options */
1552 case 0x58:
1553 {
1554 if (getAL() == 0x00)
1555 {
1556 /* Get allocation strategy */
1557 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1558 setAX(DosAllocStrategy);
1559 }
1560 else if (getAL() == 0x01)
1561 {
1562 /* Set allocation strategy */
1563
1564 if ((getBL() & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
1565 == (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
1566 {
1567 /* Can't set both */
1568 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1569 setAX(ERROR_INVALID_PARAMETER);
1570 break;
1571 }
1572
1573 if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT)
1574 {
1575 /* Invalid allocation strategy */
1576 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1577 setAX(ERROR_INVALID_PARAMETER);
1578 break;
1579 }
1580
1581 DosAllocStrategy = getBL();
1582 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1583 }
1584 else if (getAL() == 0x02)
1585 {
1586 /* Get UMB link state */
1587 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1588 setAL(DosUmbLinked ? 0x01 : 0x00);
1589 }
1590 else if (getAL() == 0x03)
1591 {
1592 /* Set UMB link state */
1593 if (getBX()) DosLinkUmb();
1594 else DosUnlinkUmb();
1595 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1596 }
1597 else
1598 {
1599 /* Invalid or unsupported function */
1600 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1601 setAX(ERROR_INVALID_FUNCTION);
1602 }
1603
1604 break;
1605 }
1606
1607 /* Get Extended Error Information */
1608 case 0x59:
1609 {
1610 DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
1611 getBX());
1612 break;
1613 }
1614
1615 /* Create Temporary File */
1616 case 0x5A:
1617 {
1618 LPSTR PathName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
1619 LPSTR FileName = PathName; // The buffer for the path and the full file name is the same.
1620 UINT uRetVal;
1621 WORD FileHandle;
1622 WORD ErrorCode;
1623
1624 /*
1625 * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
1626 * for more information.
1627 */
1628
1629 // FIXME: Check for buffer validity?
1630 // It should be a ASCIZ path ending with a '\' + 13 zero bytes
1631 // to receive the generated filename.
1632
1633 /* First create the temporary file */
1634 uRetVal = GetTempFileNameA(PathName, NULL, 0, FileName);
1635 if (uRetVal == 0)
1636 {
1637 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1638 setAX(GetLastError());
1639 break;
1640 }
1641
1642 /* Now try to open it in read/write access */
1643 ErrorCode = DosOpenFile(&FileHandle, FileName, 2);
1644 if (ErrorCode == ERROR_SUCCESS)
1645 {
1646 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1647 setAX(FileHandle);
1648 }
1649 else
1650 {
1651 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1652 setAX(ErrorCode);
1653 }
1654
1655 break;
1656 }
1657
1658 /* Create New File */
1659 case 0x5B:
1660 {
1661 WORD FileHandle;
1662 WORD ErrorCode = DosCreateFile(&FileHandle,
1663 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
1664 CREATE_NEW,
1665 getCX());
1666
1667 if (ErrorCode == ERROR_SUCCESS)
1668 {
1669 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1670 setAX(FileHandle);
1671 }
1672 else
1673 {
1674 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1675 setAX(ErrorCode);
1676 }
1677
1678 break;
1679 }
1680
1681 /* Lock/Unlock Region of File */
1682 case 0x5C:
1683 {
1684 if (getAL() == 0x00)
1685 {
1686 /* Lock region of file */
1687 if (DosLockFile(getBX(), MAKELONG(getCX(), getDX()), MAKELONG(getSI(), getDI())))
1688 {
1689 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1690 }
1691 else
1692 {
1693 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1694 setAX(DosLastError);
1695 }
1696 }
1697 else if (getAL() == 0x01)
1698 {
1699 /* Unlock region of file */
1700 if (DosUnlockFile(getBX(), MAKELONG(getCX(), getDX()), MAKELONG(getSI(), getDI())))
1701 {
1702 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1703 }
1704 else
1705 {
1706 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1707 setAX(DosLastError);
1708 }
1709 }
1710 else
1711 {
1712 /* Invalid subfunction */
1713 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1714 setAX(ERROR_INVALID_FUNCTION);
1715 }
1716
1717 break;
1718 }
1719
1720 /* Canonicalize File Name or Path */
1721 case 0x60:
1722 {
1723 /*
1724 * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
1725 * for more information.
1726 */
1727
1728 /*
1729 * We suppose that the DOS app gave to us a valid
1730 * 128-byte long buffer for the canonicalized name.
1731 */
1732 DWORD dwRetVal = GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
1733 128,
1734 SEG_OFF_TO_PTR(getES(), getDI()),
1735 NULL);
1736 if (dwRetVal == 0)
1737 {
1738 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1739 setAX(GetLastError());
1740 }
1741 else
1742 {
1743 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1744 setAX(0x0000);
1745 }
1746
1747 // FIXME: Convert the full path name into short version.
1748 // We cannot reliably use GetShortPathName, because it fails
1749 // if the path name given doesn't exist. However this DOS
1750 // function AH=60h should be able to work even for non-existing
1751 // path and file names.
1752
1753 break;
1754 }
1755
1756 /* Set Handle Count */
1757 case 0x67:
1758 {
1759 if (!DosResizeHandleTable(getBX()))
1760 {
1761 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1762 setAX(DosLastError);
1763 }
1764 else Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1765
1766 break;
1767 }
1768
1769 /* Commit File */
1770 case 0x68:
1771 case 0x6A:
1772 {
1773 /*
1774 * Function 6Ah is identical to function 68h,
1775 * and sets AH to 68h if success.
1776 * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
1777 * for more information.
1778 */
1779 setAH(0x68);
1780
1781 if (DosFlushFileBuffers(getBX()))
1782 {
1783 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1784 }
1785 else
1786 {
1787 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1788 setAX(GetLastError());
1789 }
1790
1791 break;
1792 }
1793
1794 /* Extended Open/Create */
1795 case 0x6C:
1796 {
1797 WORD FileHandle;
1798 WORD CreationStatus;
1799 WORD ErrorCode;
1800
1801 /* Check for AL == 00 */
1802 if (getAL() != 0x00)
1803 {
1804 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1805 setAX(ERROR_INVALID_FUNCTION);
1806 break;
1807 }
1808
1809 /*
1810 * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
1811 * for the full detailed description.
1812 *
1813 * WARNING: BH contains some extended flags that are NOT SUPPORTED.
1814 */
1815
1816 ErrorCode = DosCreateFileEx(&FileHandle,
1817 &CreationStatus,
1818 (LPCSTR)SEG_OFF_TO_PTR(getDS(), getSI()),
1819 getBL(),
1820 getDL(),
1821 getCX());
1822
1823 if (ErrorCode == ERROR_SUCCESS)
1824 {
1825 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1826 setCX(CreationStatus);
1827 setAX(FileHandle);
1828 }
1829 else
1830 {
1831 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1832 setAX(ErrorCode);
1833 }
1834
1835 break;
1836 }
1837
1838 /* Unsupported */
1839 default:
1840 {
1841 DPRINT1("DOS Function INT 0x21, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
1842 getAH(), getAL());
1843
1844 setAL(0); // Some functions expect AL to be 0 when it's not supported.
1845 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1846 }
1847 }
1848
1849 (*InDos)--;
1850 }
1851
1852 VOID WINAPI DosBreakInterrupt(LPWORD Stack)
1853 {
1854 /* Set CF to terminate the running process */
1855 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1856 }
1857
1858 VOID WINAPI DosFastConOut(LPWORD Stack)
1859 {
1860 /*
1861 * This is the DOS 2+ Fast Console Output Interrupt.
1862 * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
1863 *
1864 * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
1865 * for more information.
1866 */
1867
1868 /* Save AX and BX */
1869 USHORT AX = getAX();
1870 USHORT BX = getBX();
1871
1872 /*
1873 * Set the parameters:
1874 * AL contains the character to print (already set),
1875 * BL contains the character attribute,
1876 * BH contains the video page to use.
1877 */
1878 setBL(DOS_CHAR_ATTRIBUTE);
1879 setBH(Bda->VideoPage);
1880
1881 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
1882 setAH(0x0E);
1883 Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT);
1884
1885 /* Restore AX and BX */
1886 setBX(BX);
1887 setAX(AX);
1888 }
1889
1890 VOID WINAPI DosInt2Fh(LPWORD Stack)
1891 {
1892 switch (getAH())
1893 {
1894 /* Extended Memory Specification */
1895 case 0x43:
1896 {
1897 DWORD DriverEntry;
1898 if (!XmsGetDriverEntry(&DriverEntry)) break;
1899
1900 if (getAL() == 0x00)
1901 {
1902 /* The driver is loaded */
1903 setAL(0x80);
1904 }
1905 else if (getAL() == 0x10)
1906 {
1907 setES(HIWORD(DriverEntry));
1908 setBX(LOWORD(DriverEntry));
1909 }
1910 else
1911 {
1912 DPRINT1("Unknown DOS XMS Function: INT 0x2F, AH = 43h, AL = %xh\n", getAL());
1913 }
1914
1915 break;
1916 }
1917
1918 default:
1919 {
1920 DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
1921 getAH(), getAL());
1922 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1923 }
1924 }
1925 }
1926
1927 BOOLEAN DosKRNLInitialize(VOID)
1928 {
1929 #if 1
1930
1931 UCHAR i;
1932 CHAR CurrentDirectory[MAX_PATH];
1933 CHAR DosDirectory[DOS_DIR_LENGTH];
1934 LPSTR Path;
1935 PDOS_SFT Sft;
1936
1937 const BYTE NullDriverRoutine[] = {
1938 /* Strategy routine entry */
1939 0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
1940 0xC7,
1941 0x47,
1942 FIELD_OFFSET(DOS_REQUEST_HEADER, Status),
1943 LOBYTE(DOS_DEVSTAT_DONE),
1944 HIBYTE(DOS_DEVSTAT_DONE),
1945
1946 /* Interrupt routine entry */
1947 0xCB, // retf
1948 };
1949
1950 FILE *Stream;
1951 WCHAR Buffer[256];
1952
1953 /* Setup the InDOS flag */
1954 InDos = (PBYTE)FAR_POINTER(INDOS_POINTER);
1955 *InDos = 0;
1956
1957 /* Clear the current directory buffer */
1958 RtlZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
1959
1960 /* Get the current directory */
1961 if (!GetCurrentDirectoryA(MAX_PATH, CurrentDirectory))
1962 {
1963 // TODO: Use some kind of default path?
1964 return FALSE;
1965 }
1966
1967 /* Convert that to a DOS path */
1968 if (!GetShortPathNameA(CurrentDirectory, DosDirectory, DOS_DIR_LENGTH))
1969 {
1970 // TODO: Use some kind of default path?
1971 return FALSE;
1972 }
1973
1974 /* Set the drive */
1975 CurrentDrive = DosDirectory[0] - 'A';
1976
1977 /* Get the directory part of the path */
1978 Path = strchr(DosDirectory, '\\');
1979 if (Path != NULL)
1980 {
1981 /* Skip the backslash */
1982 Path++;
1983 }
1984
1985 /* Set the directory */
1986 if (Path != NULL)
1987 {
1988 strncpy(CurrentDirectories[CurrentDrive], Path, DOS_DIR_LENGTH);
1989 }
1990
1991 /* Read CONFIG.SYS */
1992 Stream = _wfopen(DOS_CONFIG_PATH, L"r");
1993 if (Stream != NULL)
1994 {
1995 while (fgetws(Buffer, 256, Stream))
1996 {
1997 // TODO: Parse the line
1998 }
1999 fclose(Stream);
2000 }
2001
2002 /* Initialize the list of lists */
2003 SysVars = (PDOS_SYSVARS)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT, 0);
2004 RtlZeroMemory(SysVars, sizeof(DOS_SYSVARS));
2005 SysVars->FirstMcb = FIRST_MCB_SEGMENT;
2006 SysVars->FirstSft = MAKELONG(MASTER_SFT_OFFSET, DOS_DATA_SEGMENT);
2007
2008 /* Initialize the NUL device driver */
2009 SysVars->NullDevice.Link = 0xFFFFFFFF;
2010 SysVars->NullDevice.DeviceAttributes = DOS_DEVATTR_NUL | DOS_DEVATTR_CHARACTER;
2011 SysVars->NullDevice.StrategyRoutine = FIELD_OFFSET(DOS_SYSVARS, NullDriverRoutine);
2012 SysVars->NullDevice.InterruptRoutine = SysVars->NullDevice.StrategyRoutine + 6;
2013 RtlFillMemory(SysVars->NullDevice.DeviceName,
2014 sizeof(SysVars->NullDevice.DeviceName),
2015 ' ');
2016 RtlCopyMemory(SysVars->NullDevice.DeviceName, "NUL", strlen("NUL"));
2017 RtlCopyMemory(SysVars->NullDriverRoutine,
2018 NullDriverRoutine,
2019 sizeof(NullDriverRoutine));
2020
2021 /* Initialize the SFT */
2022 Sft = (PDOS_SFT)FAR_POINTER(SysVars->FirstSft);
2023 Sft->Link = 0xFFFFFFFF;
2024 Sft->NumDescriptors = DOS_SFT_SIZE;
2025
2026 for (i = 0; i < Sft->NumDescriptors; i++)
2027 {
2028 /* Clear the file descriptor entry */
2029 RtlZeroMemory(&Sft->FileDescriptors[i], sizeof(DOS_FILE_DESCRIPTOR));
2030 }
2031
2032 #endif
2033
2034 /* Initialize the callback context */
2035 InitializeContext(&DosContext, 0x0070, 0x0000);
2036
2037 /* Register the DOS 32-bit Interrupts */
2038 RegisterDosInt32(0x20, DosInt20h );
2039 RegisterDosInt32(0x21, DosInt21h );
2040 // RegisterDosInt32(0x22, DosInt22h ); // Termination
2041 RegisterDosInt32(0x23, DosBreakInterrupt); // Ctrl-C / Ctrl-Break
2042 // RegisterDosInt32(0x24, DosInt24h ); // Critical Error
2043 RegisterDosInt32(0x29, DosFastConOut ); // DOS 2+ Fast Console Output
2044 RegisterDosInt32(0x2F, DosInt2Fh );
2045
2046 /* Load the EMS driver */
2047 if (!EmsDrvInitialize(EMS_TOTAL_PAGES))
2048 {
2049 DPRINT1("Could not initialize EMS. EMS will not be available.\n"
2050 "Try reducing the number of EMS pages.\n");
2051 }
2052
2053 /* Load the XMS driver (HIMEM) */
2054 XmsInitialize();
2055
2056 /* Load the CON driver */
2057 ConDrvInitialize();
2058
2059 return TRUE;
2060 }
2061
2062 /* EOF */