[NTVDM]
[reactos.git] / subsystems / ntvdm / dos.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: dos.c
5 * PURPOSE: VDM DOS Kernel
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "dos.h"
14 #include "bios.h"
15 #include "emulator.h"
16
17 /* PRIVATE VARIABLES **********************************************************/
18
19 static WORD CurrentPsp = SYSTEM_PSP;
20 static WORD DosLastError = 0;
21 static DWORD DiskTransferArea;
22 static BYTE CurrentDrive;
23 static CHAR LastDrive = 'E';
24 static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH];
25 static HANDLE DosSystemFileTable[DOS_SFT_SIZE];
26 static WORD DosSftRefCount[DOS_SFT_SIZE];
27 static BYTE DosAllocStrategy = DOS_ALLOC_BEST_FIT;
28 static BOOLEAN DosUmbLinked = FALSE;
29
30 /* PRIVATE FUNCTIONS **********************************************************/
31
32 /* Taken from base/shell/cmd/console.c */
33 static BOOL IsConsoleHandle(HANDLE hHandle)
34 {
35 DWORD dwMode;
36
37 /* Check whether the handle may be that of a console... */
38 if ((GetFileType(hHandle) & FILE_TYPE_CHAR) == 0) return FALSE;
39
40 /*
41 * It may be. Perform another test... The idea comes from the
42 * MSDN description of the WriteConsole API:
43 *
44 * "WriteConsole fails if it is used with a standard handle
45 * that is redirected to a file. If an application processes
46 * multilingual output that can be redirected, determine whether
47 * the output handle is a console handle (one method is to call
48 * the GetConsoleMode function and check whether it succeeds).
49 * If the handle is a console handle, call WriteConsole. If the
50 * handle is not a console handle, the output is redirected and
51 * you should call WriteFile to perform the I/O."
52 */
53 return GetConsoleMode(hHandle, &dwMode);
54 }
55
56 static VOID DosCombineFreeBlocks(WORD StartBlock)
57 {
58 PDOS_MCB CurrentMcb = SEGMENT_TO_MCB(StartBlock), NextMcb;
59
60 /* If this is the last block or it's not free, quit */
61 if (CurrentMcb->BlockType == 'Z' || CurrentMcb->OwnerPsp != 0) return;
62
63 while (TRUE)
64 {
65 /* Get a pointer to the next MCB */
66 NextMcb = SEGMENT_TO_MCB(StartBlock + CurrentMcb->Size + 1);
67
68 /* Check if the next MCB is free */
69 if (NextMcb->OwnerPsp == 0)
70 {
71 /* Combine them */
72 CurrentMcb->Size += NextMcb->Size + 1;
73 CurrentMcb->BlockType = NextMcb->BlockType;
74 NextMcb->BlockType = 'I';
75 }
76 else
77 {
78 /* No more adjoining free blocks */
79 break;
80 }
81 }
82 }
83
84 static WORD DosCopyEnvironmentBlock(WORD SourceSegment, LPCSTR ProgramName)
85 {
86 PCHAR Ptr, SourceBuffer, DestBuffer = NULL;
87 ULONG TotalSize = 0;
88 WORD DestSegment;
89
90 Ptr = SourceBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(SourceSegment, 0));
91
92 /* Calculate the size of the environment block */
93 while (*Ptr)
94 {
95 TotalSize += strlen(Ptr) + 1;
96 Ptr += strlen(Ptr) + 1;
97 }
98 TotalSize++;
99
100 /* Add the string buffer size */
101 TotalSize += strlen(ProgramName) + 1;
102
103 /* Allocate the memory for the environment block */
104 DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
105 if (!DestSegment) return 0;
106
107 Ptr = SourceBuffer;
108
109 DestBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(DestSegment, 0));
110 while (*Ptr)
111 {
112 /* Copy the string */
113 strcpy(DestBuffer, Ptr);
114
115 /* Advance to the next string */
116 DestBuffer += strlen(Ptr);
117 Ptr += strlen(Ptr) + 1;
118
119 /* Put a zero after the string */
120 *(DestBuffer++) = 0;
121 }
122
123 /* Set the final zero */
124 *(DestBuffer++) = 0;
125
126 /* Copy the program name after the environment block */
127 strcpy(DestBuffer, ProgramName);
128
129 return DestSegment;
130 }
131
132 static VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner)
133 {
134 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1);
135
136 /* Just set the owner */
137 Mcb->OwnerPsp = NewOwner;
138 }
139
140 static WORD DosOpenHandle(HANDLE Handle)
141 {
142 BYTE i;
143 WORD DosHandle;
144 PDOS_PSP PspBlock;
145 LPBYTE HandleTable;
146
147 /* The system PSP has no handle table */
148 if (CurrentPsp == SYSTEM_PSP) return INVALID_DOS_HANDLE;
149
150 /* Get a pointer to the handle table */
151 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
152 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
153
154 /* Find a free entry in the JFT */
155 for (DosHandle = 0; DosHandle < PspBlock->HandleTableSize; DosHandle++)
156 {
157 if (HandleTable[DosHandle] == 0xFF) break;
158 }
159
160 /* If there are no free entries, fail */
161 if (DosHandle == PspBlock->HandleTableSize) return INVALID_DOS_HANDLE;
162
163 /* Check if the handle is already in the SFT */
164 for (i = 0; i < DOS_SFT_SIZE; i++)
165 {
166 /* Check if this is the same handle */
167 if (DosSystemFileTable[i] != Handle) continue;
168
169 /* Already in the table, reference it */
170 DosSftRefCount[i]++;
171
172 /* Set the JFT entry to that SFT index */
173 HandleTable[DosHandle] = i;
174
175 /* Return the new handle */
176 return DosHandle;
177 }
178
179 /* Add the handle to the SFT */
180 for (i = 0; i < DOS_SFT_SIZE; i++)
181 {
182 /* Make sure this is an empty table entry */
183 if (DosSystemFileTable[i] != INVALID_HANDLE_VALUE) continue;
184
185 /* Initialize the empty table entry */
186 DosSystemFileTable[i] = Handle;
187 DosSftRefCount[i] = 1;
188
189 /* Set the JFT entry to that SFT index */
190 HandleTable[DosHandle] = i;
191
192 /* Return the new handle */
193 return DosHandle;
194 }
195
196 /* The SFT is full */
197 return INVALID_DOS_HANDLE;
198 }
199
200 static HANDLE DosGetRealHandle(WORD DosHandle)
201 {
202 PDOS_PSP PspBlock;
203 LPBYTE HandleTable;
204
205 /* The system PSP has no handle table */
206 if (CurrentPsp == SYSTEM_PSP) return INVALID_HANDLE_VALUE;
207
208 /* Get a pointer to the handle table */
209 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
210 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
211
212 /* Make sure the handle is open */
213 if (HandleTable[DosHandle] == 0xFF) return INVALID_HANDLE_VALUE;
214
215 /* Return the Win32 handle */
216 return DosSystemFileTable[HandleTable[DosHandle]];
217 }
218
219 static VOID DosCopyHandleTable(LPBYTE DestinationTable)
220 {
221 INT i;
222 PDOS_PSP PspBlock;
223 LPBYTE SourceTable;
224
225 /* Clear the table first */
226 for (i = 0; i < 20; i++) DestinationTable[i] = 0xFF;
227
228 /* Check if this is the initial process */
229 if (CurrentPsp == SYSTEM_PSP)
230 {
231 /* Set up the standard I/O devices */
232 for (i = 0; i <= 2; i++)
233 {
234 /* Set the index in the SFT */
235 DestinationTable[i] = (BYTE)i;
236
237 /* Increase the reference count */
238 DosSftRefCount[i]++;
239 }
240
241 /* Done */
242 return;
243 }
244
245 /* Get the parent PSP block and handle table */
246 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
247 SourceTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
248
249 /* Copy the first 20 handles into the new table */
250 for (i = 0; i < 20; i++)
251 {
252 DestinationTable[i] = SourceTable[i];
253
254 /* Increase the reference count */
255 DosSftRefCount[SourceTable[i]]++;
256 }
257 }
258
259 /* PUBLIC FUNCTIONS ***********************************************************/
260
261 WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable)
262 {
263 WORD Result = 0, Segment = FIRST_MCB_SEGMENT, MaxSize = 0;
264 PDOS_MCB CurrentMcb, NextMcb;
265 BOOLEAN SearchUmb = FALSE;
266
267 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size);
268
269 if (DosUmbLinked && (DosAllocStrategy & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW)))
270 {
271 /* Search UMB first */
272 Segment = UMB_START_SEGMENT;
273 SearchUmb = TRUE;
274 }
275
276 while (TRUE)
277 {
278 /* Get a pointer to the MCB */
279 CurrentMcb = SEGMENT_TO_MCB(Segment);
280
281 /* Make sure it's valid */
282 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z')
283 {
284 DPRINT("The DOS memory arena is corrupted!\n");
285 DosLastError = ERROR_ARENA_TRASHED;
286 return 0;
287 }
288
289 /* Only check free blocks */
290 if (CurrentMcb->OwnerPsp != 0) goto Next;
291
292 /* Combine this free block with adjoining free blocks */
293 DosCombineFreeBlocks(Segment);
294
295 /* Update the maximum block size */
296 if (CurrentMcb->Size > MaxSize) MaxSize = CurrentMcb->Size;
297
298 /* Check if this block is big enough */
299 if (CurrentMcb->Size < Size) goto Next;
300
301 switch (DosAllocStrategy & 0x3F)
302 {
303 case DOS_ALLOC_FIRST_FIT:
304 {
305 /* For first fit, stop immediately */
306 Result = Segment;
307 goto Done;
308 }
309
310 case DOS_ALLOC_BEST_FIT:
311 {
312 /* For best fit, update the smallest block found so far */
313 if ((Result == 0) || (CurrentMcb->Size < SEGMENT_TO_MCB(Result)->Size))
314 {
315 Result = Segment;
316 }
317
318 break;
319 }
320
321 case DOS_ALLOC_LAST_FIT:
322 {
323 /* For last fit, make the current block the result, but keep searching */
324 Result = Segment;
325 break;
326 }
327 }
328
329 Next:
330 /* If this was the last MCB in the chain, quit */
331 if (CurrentMcb->BlockType == 'Z')
332 {
333 /* Check if nothing was found while searching through UMBs */
334 if ((Result == 0) && SearchUmb && (DosAllocStrategy & DOS_ALLOC_HIGH_LOW))
335 {
336 /* Search low memory */
337 Segment = FIRST_MCB_SEGMENT;
338 continue;
339 }
340
341 break;
342 }
343
344 /* Otherwise, update the segment and continue */
345 Segment += CurrentMcb->Size + 1;
346 }
347
348 Done:
349
350 /* If we didn't find a free block, return 0 */
351 if (Result == 0)
352 {
353 DosLastError = ERROR_NOT_ENOUGH_MEMORY;
354 if (MaxAvailable) *MaxAvailable = MaxSize;
355 return 0;
356 }
357
358 /* Get a pointer to the MCB */
359 CurrentMcb = SEGMENT_TO_MCB(Result);
360
361 /* Check if the block is larger than requested */
362 if (CurrentMcb->Size > Size)
363 {
364 /* It is, split it into two blocks */
365 NextMcb = SEGMENT_TO_MCB(Result + Size + 1);
366
367 /* Initialize the new MCB structure */
368 NextMcb->BlockType = CurrentMcb->BlockType;
369 NextMcb->Size = CurrentMcb->Size - Size - 1;
370 NextMcb->OwnerPsp = 0;
371
372 /* Update the current block */
373 CurrentMcb->BlockType = 'M';
374 CurrentMcb->Size = Size;
375 }
376
377 /* Take ownership of the block */
378 CurrentMcb->OwnerPsp = CurrentPsp;
379
380 /* Return the segment of the data portion of the block */
381 return Result + 1;
382 }
383
384 BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable)
385 {
386 BOOLEAN Success = TRUE;
387 WORD Segment = BlockData - 1, ReturnSize = 0, NextSegment;
388 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment), NextMcb;
389
390 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
391 BlockData,
392 NewSize);
393
394 /* Make sure this is a valid, allocated block */
395 if ((Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') || Mcb->OwnerPsp == 0)
396 {
397 Success = FALSE;
398 DosLastError = ERROR_INVALID_HANDLE;
399 goto Done;
400 }
401
402 ReturnSize = Mcb->Size;
403
404 /* Check if we need to expand or contract the block */
405 if (NewSize > Mcb->Size)
406 {
407 /* We can't expand the last block */
408 if (Mcb->BlockType != 'M')
409 {
410 Success = FALSE;
411 goto Done;
412 }
413
414 /* Get the pointer and segment of the next MCB */
415 NextSegment = Segment + Mcb->Size + 1;
416 NextMcb = SEGMENT_TO_MCB(NextSegment);
417
418 /* Make sure the next segment is free */
419 if (NextMcb->OwnerPsp != 0)
420 {
421 DPRINT("Cannot expand memory block: next segment is not free!\n");
422 DosLastError = ERROR_NOT_ENOUGH_MEMORY;
423 Success = FALSE;
424 goto Done;
425 }
426
427 /* Combine this free block with adjoining free blocks */
428 DosCombineFreeBlocks(NextSegment);
429
430 /* Set the maximum possible size of the block */
431 ReturnSize += NextMcb->Size + 1;
432
433 /* Maximize the current block */
434 Mcb->Size = ReturnSize;
435 Mcb->BlockType = NextMcb->BlockType;
436
437 /* Invalidate the next block */
438 NextMcb->BlockType = 'I';
439
440 /* Check if the block is larger than requested */
441 if (Mcb->Size > NewSize)
442 {
443 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
444 Mcb->Size,
445 NewSize);
446
447 /* It is, split it into two blocks */
448 NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
449
450 /* Initialize the new MCB structure */
451 NextMcb->BlockType = Mcb->BlockType;
452 NextMcb->Size = Mcb->Size - NewSize - 1;
453 NextMcb->OwnerPsp = 0;
454
455 /* Update the current block */
456 Mcb->BlockType = 'M';
457 Mcb->Size = NewSize;
458 }
459 }
460 else if (NewSize < Mcb->Size)
461 {
462 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
463 Mcb->Size,
464 NewSize);
465
466 /* Just split the block */
467 NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
468 NextMcb->BlockType = Mcb->BlockType;
469 NextMcb->Size = Mcb->Size - NewSize - 1;
470 NextMcb->OwnerPsp = 0;
471
472 /* Update the MCB */
473 Mcb->BlockType = 'M';
474 Mcb->Size = NewSize;
475 }
476
477 Done:
478 /* Check if the operation failed */
479 if (!Success)
480 {
481 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
482 ReturnSize);
483
484 /* Return the maximum possible size */
485 if (MaxAvailable) *MaxAvailable = ReturnSize;
486 }
487
488 return Success;
489 }
490
491 BOOLEAN DosFreeMemory(WORD BlockData)
492 {
493 PDOS_MCB Mcb = SEGMENT_TO_MCB(BlockData - 1);
494
495 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData);
496
497 /* Make sure the MCB is valid */
498 if (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z')
499 {
500 DPRINT("MCB block type '%c' not valid!\n", Mcb->BlockType);
501 return FALSE;
502 }
503
504 /* Mark the block as free */
505 Mcb->OwnerPsp = 0;
506
507 return TRUE;
508 }
509
510 BOOLEAN DosLinkUmb(VOID)
511 {
512 DWORD Segment = FIRST_MCB_SEGMENT;
513 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment);
514
515 DPRINT("Linking UMB\n");
516
517 /* Check if UMBs are already linked */
518 if (DosUmbLinked) return FALSE;
519
520 /* Find the last block */
521 while ((Mcb->BlockType == 'M') && (Segment <= 0xFFFF))
522 {
523 Segment += Mcb->Size + 1;
524 Mcb = SEGMENT_TO_MCB(Segment);
525 }
526
527 /* Make sure it's valid */
528 if (Mcb->BlockType != 'Z') return FALSE;
529
530 /* Connect the MCB with the UMB chain */
531 Mcb->BlockType = 'M';
532
533 DosUmbLinked = TRUE;
534 return TRUE;
535 }
536
537 BOOLEAN DosUnlinkUmb(VOID)
538 {
539 DWORD Segment = FIRST_MCB_SEGMENT;
540 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment);
541
542 DPRINT("Unlinking UMB\n");
543
544 /* Check if UMBs are already unlinked */
545 if (!DosUmbLinked) return FALSE;
546
547 /* Find the block preceding the MCB that links it with the UMB chain */
548 while (Segment <= 0xFFFF)
549 {
550 if ((Segment + Mcb->Size) == (FIRST_MCB_SEGMENT + USER_MEMORY_SIZE))
551 {
552 /* This is the last non-UMB segment */
553 break;
554 }
555
556 /* Advance to the next MCB */
557 Segment += Mcb->Size + 1;
558 Mcb = SEGMENT_TO_MCB(Segment);
559 }
560
561 /* Mark the MCB as the last MCB */
562 Mcb->BlockType = 'Z';
563
564 DosUmbLinked = FALSE;
565 return TRUE;
566 }
567
568 WORD DosCreateFile(LPWORD Handle, LPCSTR FilePath, WORD Attributes)
569 {
570 HANDLE FileHandle;
571 WORD DosHandle;
572
573 DPRINT("DosCreateFile: FilePath \"%s\", Attributes 0x%04X\n",
574 FilePath,
575 Attributes);
576
577 /* Create the file */
578 FileHandle = CreateFileA(FilePath,
579 GENERIC_READ | GENERIC_WRITE,
580 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
581 NULL,
582 CREATE_ALWAYS,
583 Attributes,
584 NULL);
585
586 if (FileHandle == INVALID_HANDLE_VALUE)
587 {
588 /* Return the error code */
589 return (WORD)GetLastError();
590 }
591
592 /* Open the DOS handle */
593 DosHandle = DosOpenHandle(FileHandle);
594
595 if (DosHandle == INVALID_DOS_HANDLE)
596 {
597 /* Close the handle */
598 CloseHandle(FileHandle);
599
600 /* Return the error code */
601 return ERROR_TOO_MANY_OPEN_FILES;
602 }
603
604 /* It was successful */
605 *Handle = DosHandle;
606 return ERROR_SUCCESS;
607 }
608
609 WORD DosOpenFile(LPWORD Handle, LPCSTR FilePath, BYTE AccessMode)
610 {
611 HANDLE FileHandle;
612 ACCESS_MASK Access = 0;
613 WORD DosHandle;
614
615 DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n",
616 FilePath,
617 AccessMode);
618
619 /* Parse the access mode */
620 switch (AccessMode & 3)
621 {
622 case 0:
623 {
624 /* Read-only */
625 Access = GENERIC_READ;
626 break;
627 }
628
629 case 1:
630 {
631 /* Write only */
632 Access = GENERIC_WRITE;
633 break;
634 }
635
636 case 2:
637 {
638 /* Read and write */
639 Access = GENERIC_READ | GENERIC_WRITE;
640 break;
641 }
642
643 default:
644 {
645 /* Invalid */
646 return ERROR_INVALID_PARAMETER;
647 }
648 }
649
650 /* Open the file */
651 FileHandle = CreateFileA(FilePath,
652 Access,
653 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
654 NULL,
655 OPEN_EXISTING,
656 FILE_ATTRIBUTE_NORMAL,
657 NULL);
658
659 if (FileHandle == INVALID_HANDLE_VALUE)
660 {
661 /* Return the error code */
662 return (WORD)GetLastError();
663 }
664
665 /* Open the DOS handle */
666 DosHandle = DosOpenHandle(FileHandle);
667
668 if (DosHandle == INVALID_DOS_HANDLE)
669 {
670 /* Close the handle */
671 CloseHandle(FileHandle);
672
673 /* Return the error code */
674 return ERROR_TOO_MANY_OPEN_FILES;
675 }
676
677 /* It was successful */
678 *Handle = DosHandle;
679 return ERROR_SUCCESS;
680 }
681
682 WORD DosReadFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesRead)
683 {
684 WORD Result = ERROR_SUCCESS;
685 DWORD BytesRead32 = 0;
686 HANDLE Handle = DosGetRealHandle(FileHandle);
687
688 DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle, Count);
689
690 /* Make sure the handle is valid */
691 if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
692
693 /* Read the file */
694 if (!ReadFile(Handle, Buffer, Count, &BytesRead32, NULL))
695 {
696 /* Store the error code */
697 Result = (WORD)GetLastError();
698 }
699
700 /* The number of bytes read is always 16-bit */
701 *BytesRead = LOWORD(BytesRead32);
702
703 /* Return the error code */
704 return Result;
705 }
706
707 WORD DosWriteFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesWritten)
708 {
709 WORD Result = ERROR_SUCCESS;
710 DWORD BytesWritten32 = 0;
711 HANDLE Handle = DosGetRealHandle(FileHandle);
712 WORD i;
713
714 DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n",
715 FileHandle,
716 Count);
717
718 /* Make sure the handle is valid */
719 if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
720
721 if (IsConsoleHandle(Handle))
722 {
723 for (i = 0; i < Count; i++)
724 {
725 /* Call the BIOS to print the character */
726 BiosPrintCharacter(((LPBYTE)Buffer)[i], DOS_CHAR_ATTRIBUTE, Bda->VideoPage);
727 BytesWritten32++;
728 }
729 }
730 else
731 {
732 /* Write the file */
733 if (!WriteFile(Handle, Buffer, Count, &BytesWritten32, NULL))
734 {
735 /* Store the error code */
736 Result = (WORD)GetLastError();
737 }
738 }
739
740 /* The number of bytes written is always 16-bit */
741 *BytesWritten = LOWORD(BytesWritten32);
742
743 /* Return the error code */
744 return Result;
745 }
746
747 WORD DosSeekFile(WORD FileHandle, LONG Offset, BYTE Origin, LPDWORD NewOffset)
748 {
749 WORD Result = ERROR_SUCCESS;
750 DWORD FilePointer;
751 HANDLE Handle = DosGetRealHandle(FileHandle);
752
753 DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n",
754 FileHandle,
755 Offset,
756 Origin);
757
758 /* Make sure the handle is valid */
759 if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
760
761 /* Check if the origin is valid */
762 if (Origin != FILE_BEGIN && Origin != FILE_CURRENT && Origin != FILE_END)
763 {
764 return ERROR_INVALID_FUNCTION;
765 }
766
767 /* Move the file pointer */
768 FilePointer = SetFilePointer(Handle, Offset, NULL, Origin);
769
770 /* Check if there's a possibility the operation failed */
771 if (FilePointer == INVALID_SET_FILE_POINTER)
772 {
773 /* Get the real error code */
774 Result = (WORD)GetLastError();
775 }
776
777 if (Result != ERROR_SUCCESS)
778 {
779 /* The operation did fail */
780 return Result;
781 }
782
783 /* Return the file pointer, if requested */
784 if (NewOffset) *NewOffset = FilePointer;
785
786 /* Return success */
787 return ERROR_SUCCESS;
788 }
789
790 BOOLEAN DosDuplicateHandle(WORD OldHandle, WORD NewHandle)
791 {
792 BYTE SftIndex;
793 PDOS_PSP PspBlock;
794 LPBYTE HandleTable;
795
796 DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
797 OldHandle,
798 NewHandle);
799
800 /* The system PSP has no handle table */
801 if (CurrentPsp == SYSTEM_PSP) return FALSE;
802
803 /* Get a pointer to the handle table */
804 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
805 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
806
807 /* Make sure the old handle is open */
808 if (HandleTable[OldHandle] == 0xFF) return FALSE;
809
810 /* Check if the new handle is open */
811 if (HandleTable[NewHandle] != 0xFF)
812 {
813 /* Close it */
814 DosCloseHandle(NewHandle);
815 }
816
817 /* Increment the reference count of the SFT entry */
818 SftIndex = HandleTable[OldHandle];
819 DosSftRefCount[SftIndex]++;
820
821 /* Make the new handle point to that SFT entry */
822 HandleTable[NewHandle] = SftIndex;
823
824 /* Return success */
825 return TRUE;
826 }
827
828 BOOLEAN DosCloseHandle(WORD DosHandle)
829 {
830 BYTE SftIndex;
831 PDOS_PSP PspBlock;
832 LPBYTE HandleTable;
833
834 DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle);
835
836 /* The system PSP has no handle table */
837 if (CurrentPsp == SYSTEM_PSP) return FALSE;
838
839 /* Get a pointer to the handle table */
840 PspBlock = SEGMENT_TO_PSP(CurrentPsp);
841 HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
842
843 /* Make sure the handle is open */
844 if (HandleTable[DosHandle] == 0xFF) return FALSE;
845
846 /* Decrement the reference count of the SFT entry */
847 SftIndex = HandleTable[DosHandle];
848 DosSftRefCount[SftIndex]--;
849
850 /* Check if the reference count fell to zero */
851 if (!DosSftRefCount[SftIndex])
852 {
853 /* Close the file, it's no longer needed */
854 CloseHandle(DosSystemFileTable[SftIndex]);
855
856 /* Clear the handle */
857 DosSystemFileTable[SftIndex] = INVALID_HANDLE_VALUE;
858 }
859
860 /* Clear the entry in the JFT */
861 HandleTable[DosHandle] = 0xFF;
862
863 return TRUE;
864 }
865
866 BOOLEAN DosChangeDrive(BYTE Drive)
867 {
868 WCHAR DirectoryPath[DOS_CMDLINE_LENGTH];
869
870 /* Make sure the drive exists */
871 if (Drive > (LastDrive - 'A')) return FALSE;
872
873 /* Find the path to the new current directory */
874 swprintf(DirectoryPath, L"%c\\%S", Drive + 'A', CurrentDirectories[Drive]);
875
876 /* Change the current directory of the process */
877 if (!SetCurrentDirectory(DirectoryPath)) return FALSE;
878
879 /* Set the current drive */
880 CurrentDrive = Drive;
881
882 /* Return success */
883 return TRUE;
884 }
885
886 BOOLEAN DosChangeDirectory(LPSTR Directory)
887 {
888 BYTE DriveNumber;
889 DWORD Attributes;
890 LPSTR Path;
891
892 /* Make sure the directory path is not too long */
893 if (strlen(Directory) >= DOS_DIR_LENGTH)
894 {
895 DosLastError = ERROR_PATH_NOT_FOUND;
896 return FALSE;
897 }
898
899 /* Get the drive number */
900 DriveNumber = Directory[0] - 'A';
901
902 /* Make sure the drive exists */
903 if (DriveNumber > (LastDrive - 'A'))
904 {
905 DosLastError = ERROR_PATH_NOT_FOUND;
906 return FALSE;
907 }
908
909 /* Get the file attributes */
910 Attributes = GetFileAttributesA(Directory);
911
912 /* Make sure the path exists and is a directory */
913 if ((Attributes == INVALID_FILE_ATTRIBUTES)
914 || !(Attributes & FILE_ATTRIBUTE_DIRECTORY))
915 {
916 DosLastError = ERROR_PATH_NOT_FOUND;
917 return FALSE;
918 }
919
920 /* Check if this is the current drive */
921 if (DriveNumber == CurrentDrive)
922 {
923 /* Change the directory */
924 if (!SetCurrentDirectoryA(Directory))
925 {
926 DosLastError = LOWORD(GetLastError());
927 return FALSE;
928 }
929 }
930
931 /* Get the directory part of the path */
932 Path = strchr(Directory, '\\');
933 if (Path != NULL)
934 {
935 /* Skip the backslash */
936 Path++;
937 }
938
939 /* Set the directory for the drive */
940 if (Path != NULL) strcpy(CurrentDirectories[DriveNumber], Path);
941 else strcpy(CurrentDirectories[DriveNumber], "");
942
943 /* Return success */
944 return TRUE;
945 }
946
947 VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment)
948 {
949 PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment);
950 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
951
952 ZeroMemory(PspBlock, sizeof(DOS_PSP));
953
954 /* Set the exit interrupt */
955 PspBlock->Exit[0] = 0xCD; // int 0x20
956 PspBlock->Exit[1] = 0x20;
957
958 /* Set the number of the last paragraph */
959 PspBlock->LastParagraph = PspSegment + ProgramSize - 1;
960
961 /* Save the interrupt vectors */
962 PspBlock->TerminateAddress = IntVecTable[0x22];
963 PspBlock->BreakAddress = IntVecTable[0x23];
964 PspBlock->CriticalAddress = IntVecTable[0x24];
965
966 /* Set the parent PSP */
967 PspBlock->ParentPsp = CurrentPsp;
968
969 /* Copy the parent handle table */
970 DosCopyHandleTable(PspBlock->HandleTable);
971
972 /* Set the environment block */
973 PspBlock->EnvBlock = Environment;
974
975 /* Set the handle table pointers to the internal handle table */
976 PspBlock->HandleTableSize = 20;
977 PspBlock->HandleTablePtr = MAKELONG(0x18, PspSegment);
978
979 /* Set the DOS version */
980 PspBlock->DosVersion = DOS_VERSION;
981
982 /* Set the far call opcodes */
983 PspBlock->FarCall[0] = 0xCD; // int 0x21
984 PspBlock->FarCall[1] = 0x21;
985 PspBlock->FarCall[2] = 0xCB; // retf
986
987 /* Set the command line */
988 PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
989 RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
990 PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
991 }
992
993 BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
994 {
995 BOOLEAN Success = FALSE, AllocatedEnvBlock = FALSE;
996 HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
997 LPBYTE Address = NULL;
998 LPSTR ProgramFilePath, Parameters[256];
999 CHAR CommandLineCopy[DOS_CMDLINE_LENGTH];
1000 INT ParamCount = 0;
1001 WORD Segment = 0;
1002 WORD MaxAllocSize;
1003 DWORD i, FileSize, ExeSize;
1004 PIMAGE_DOS_HEADER Header;
1005 PDWORD RelocationTable;
1006 PWORD RelocWord;
1007
1008 DPRINT("DosCreateProcess: CommandLine \"%s\", EnvBlock 0x%04X\n",
1009 CommandLine,
1010 EnvBlock);
1011
1012 /* Save a copy of the command line */
1013 strcpy(CommandLineCopy, CommandLine);
1014
1015 // FIXME: Improve parsing (especially: "some_path\with spaces\program.exe" options)
1016
1017 /* Get the file name of the executable */
1018 ProgramFilePath = strtok(CommandLineCopy, " \t");
1019
1020 /* Load the parameters in the local array */
1021 while ((ParamCount < sizeof(Parameters)/sizeof(Parameters[0]))
1022 && ((Parameters[ParamCount] = strtok(NULL, " \t")) != NULL))
1023 {
1024 ParamCount++;
1025 }
1026
1027 /* Open a handle to the executable */
1028 FileHandle = CreateFileA(ProgramFilePath,
1029 GENERIC_READ,
1030 0,
1031 NULL,
1032 OPEN_EXISTING,
1033 FILE_ATTRIBUTE_NORMAL,
1034 NULL);
1035 if (FileHandle == INVALID_HANDLE_VALUE) goto Cleanup;
1036
1037 /* Get the file size */
1038 FileSize = GetFileSize(FileHandle, NULL);
1039
1040 /* Create a mapping object for the file */
1041 FileMapping = CreateFileMapping(FileHandle,
1042 NULL,
1043 PAGE_READONLY,
1044 0,
1045 0,
1046 NULL);
1047 if (FileMapping == NULL) goto Cleanup;
1048
1049 /* Map the file into memory */
1050 Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
1051 if (Address == NULL) goto Cleanup;
1052
1053 /* Did we get an environment segment? */
1054 if (!EnvBlock)
1055 {
1056 /* Set a flag to know if the environment block was allocated here */
1057 AllocatedEnvBlock = TRUE;
1058
1059 /* No, copy the one from the parent */
1060 EnvBlock = DosCopyEnvironmentBlock((CurrentPsp != SYSTEM_PSP)
1061 ? SEGMENT_TO_PSP(CurrentPsp)->EnvBlock
1062 : SYSTEM_ENV_BLOCK,
1063 ProgramFilePath);
1064 }
1065
1066 /* Check if this is an EXE file or a COM file */
1067 if (Address[0] == 'M' && Address[1] == 'Z')
1068 {
1069 /* EXE file */
1070
1071 /* Get the MZ header */
1072 Header = (PIMAGE_DOS_HEADER)Address;
1073
1074 /* Get the base size of the file, in paragraphs (rounded up) */
1075 ExeSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
1076
1077 /* Add the PSP size, in paragraphs */
1078 ExeSize += sizeof(DOS_PSP) >> 4;
1079
1080 /* Add the maximum size that should be allocated */
1081 ExeSize += Header->e_maxalloc;
1082
1083 /* Make sure it does not pass 0xFFFF */
1084 if (ExeSize > 0xFFFF) ExeSize = 0xFFFF;
1085
1086 /* Reduce the size one by one until the allocation is successful */
1087 for (i = Header->e_maxalloc; i >= Header->e_minalloc; i--, ExeSize--)
1088 {
1089 /* Try to allocate that much memory */
1090 Segment = DosAllocateMemory((WORD)ExeSize, NULL);
1091 if (Segment != 0) break;
1092 }
1093
1094 /* Check if at least the lowest allocation was successful */
1095 if (Segment == 0) goto Cleanup;
1096
1097 /* Initialize the PSP */
1098 DosInitializePsp(Segment,
1099 CommandLine,
1100 (WORD)ExeSize,
1101 EnvBlock);
1102
1103 /* The process owns its own memory */
1104 DosChangeMemoryOwner(Segment, Segment);
1105 DosChangeMemoryOwner(EnvBlock, Segment);
1106
1107 /* Copy the program to Segment:0100 */
1108 RtlCopyMemory((PVOID)((ULONG_PTR)BaseAddress
1109 + TO_LINEAR(Segment, 0x100)),
1110 Address + (Header->e_cparhdr << 4),
1111 min(FileSize - (Header->e_cparhdr << 4),
1112 (ExeSize << 4) - sizeof(DOS_PSP)));
1113
1114 /* Get the relocation table */
1115 RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
1116
1117 /* Perform relocations */
1118 for (i = 0; i < Header->e_crlc; i++)
1119 {
1120 /* Get a pointer to the word that needs to be patched */
1121 RelocWord = (PWORD)((ULONG_PTR)BaseAddress
1122 + TO_LINEAR(Segment + HIWORD(RelocationTable[i]),
1123 0x100 + LOWORD(RelocationTable[i])));
1124
1125 /* Add the number of the EXE segment to it */
1126 *RelocWord += Segment + (sizeof(DOS_PSP) >> 4);
1127 }
1128
1129 /* Set the initial segment registers */
1130 EmulatorSetRegister(EMULATOR_REG_DS, Segment);
1131 EmulatorSetRegister(EMULATOR_REG_ES, Segment);
1132
1133 /* Set the stack to the location from the header */
1134 EmulatorSetStack(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss,
1135 Header->e_sp);
1136
1137 /* Execute */
1138 CurrentPsp = Segment;
1139 DiskTransferArea = MAKELONG(0x80, Segment);
1140 EmulatorExecute(Segment + Header->e_cs + (sizeof(DOS_PSP) >> 4),
1141 Header->e_ip);
1142
1143 Success = TRUE;
1144 }
1145 else
1146 {
1147 /* COM file */
1148
1149 /* Find the maximum amount of memory that can be allocated */
1150 DosAllocateMemory(0xFFFF, &MaxAllocSize);
1151
1152 /* Make sure it's enough for the whole program and the PSP */
1153 if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP))) goto Cleanup;
1154
1155 /* Allocate all of it */
1156 Segment = DosAllocateMemory(MaxAllocSize, NULL);
1157 if (Segment == 0) goto Cleanup;
1158
1159 /* The process owns its own memory */
1160 DosChangeMemoryOwner(Segment, Segment);
1161 DosChangeMemoryOwner(EnvBlock, Segment);
1162
1163 /* Copy the program to Segment:0100 */
1164 RtlCopyMemory((PVOID)((ULONG_PTR)BaseAddress
1165 + TO_LINEAR(Segment, 0x100)),
1166 Address,
1167 FileSize);
1168
1169 /* Initialize the PSP */
1170 DosInitializePsp(Segment,
1171 CommandLine,
1172 (WORD)((FileSize + sizeof(DOS_PSP)) >> 4),
1173 EnvBlock);
1174
1175 /* Set the initial segment registers */
1176 EmulatorSetRegister(EMULATOR_REG_DS, Segment);
1177 EmulatorSetRegister(EMULATOR_REG_ES, Segment);
1178
1179 /* Set the stack to the last word of the segment */
1180 EmulatorSetStack(Segment, 0xFFFE);
1181
1182 /* Execute */
1183 CurrentPsp = Segment;
1184 DiskTransferArea = MAKELONG(0x80, Segment);
1185 EmulatorExecute(Segment, 0x100);
1186
1187 Success = TRUE;
1188 }
1189
1190 Cleanup:
1191 if (!Success)
1192 {
1193 /* It was not successful, cleanup the DOS memory */
1194 if (AllocatedEnvBlock) DosFreeMemory(EnvBlock);
1195 if (Segment) DosFreeMemory(Segment);
1196 }
1197
1198 /* Unmap the file*/
1199 if (Address != NULL) UnmapViewOfFile(Address);
1200
1201 /* Close the file mapping object */
1202 if (FileMapping != NULL) CloseHandle(FileMapping);
1203
1204 /* Close the file handle */
1205 if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
1206
1207 return Success;
1208 }
1209
1210 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
1211 {
1212 WORD i;
1213 WORD McbSegment = FIRST_MCB_SEGMENT;
1214 PDOS_MCB CurrentMcb;
1215 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
1216 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
1217
1218 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
1219 Psp,
1220 ReturnCode);
1221
1222 /* Check if this PSP is it's own parent */
1223 if (PspBlock->ParentPsp == Psp) goto Done;
1224
1225 for (i = 0; i < PspBlock->HandleTableSize; i++)
1226 {
1227 /* Close the handle */
1228 DosCloseHandle(i);
1229 }
1230
1231 /* Free the memory used by the process */
1232 while (TRUE)
1233 {
1234 /* Get a pointer to the MCB */
1235 CurrentMcb = SEGMENT_TO_MCB(McbSegment);
1236
1237 /* Make sure the MCB is valid */
1238 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType !='Z') break;
1239
1240 /* If this block was allocated by the process, free it */
1241 if (CurrentMcb->OwnerPsp == Psp) DosFreeMemory(McbSegment + 1);
1242
1243 /* If this was the last block, quit */
1244 if (CurrentMcb->BlockType == 'Z') break;
1245
1246 /* Update the segment and continue */
1247 McbSegment += CurrentMcb->Size + 1;
1248 }
1249
1250 Done:
1251 /* Restore the interrupt vectors */
1252 IntVecTable[0x22] = PspBlock->TerminateAddress;
1253 IntVecTable[0x23] = PspBlock->BreakAddress;
1254 IntVecTable[0x24] = PspBlock->CriticalAddress;
1255
1256 /* Update the current PSP */
1257 if (Psp == CurrentPsp)
1258 {
1259 CurrentPsp = PspBlock->ParentPsp;
1260 if (CurrentPsp == SYSTEM_PSP) VdmRunning = FALSE;
1261 }
1262
1263 /* Return control to the parent process */
1264 EmulatorExecute(HIWORD(PspBlock->TerminateAddress),
1265 LOWORD(PspBlock->TerminateAddress));
1266 }
1267
1268 CHAR DosReadCharacter(VOID)
1269 {
1270 CHAR Character = '\0';
1271 WORD BytesRead;
1272
1273 if (IsConsoleHandle(DosGetRealHandle(DOS_INPUT_HANDLE)))
1274 {
1275 /* Call the BIOS */
1276 Character = LOBYTE(BiosGetCharacter());
1277 }
1278 else
1279 {
1280 /* Use the file reading function */
1281 DosReadFile(DOS_INPUT_HANDLE, &Character, sizeof(CHAR), &BytesRead);
1282 }
1283
1284 return Character;
1285 }
1286
1287 BOOLEAN DosCheckInput(VOID)
1288 {
1289 HANDLE Handle = DosGetRealHandle(DOS_INPUT_HANDLE);
1290
1291 if (IsConsoleHandle(Handle))
1292 {
1293 /* Call the BIOS */
1294 return (BiosPeekCharacter() != 0xFFFF);
1295 }
1296 else
1297 {
1298 DWORD FileSizeHigh;
1299 DWORD FileSize = GetFileSize(Handle, &FileSizeHigh);
1300 LONG LocationHigh = 0;
1301 DWORD Location = SetFilePointer(Handle, 0, &LocationHigh, FILE_CURRENT);
1302
1303 return ((Location != FileSize) || (LocationHigh != FileSizeHigh));
1304 }
1305 }
1306
1307 VOID DosPrintCharacter(CHAR Character)
1308 {
1309 WORD BytesWritten;
1310
1311 /* Use the file writing function */
1312 DosWriteFile(DOS_OUTPUT_HANDLE, &Character, sizeof(CHAR), &BytesWritten);
1313 }
1314
1315 BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
1316 {
1317 HANDLE Handle = DosGetRealHandle(FileHandle);
1318
1319 if (Handle == INVALID_HANDLE_VALUE)
1320 {
1321 /* Doesn't exist */
1322 DosLastError = ERROR_FILE_NOT_FOUND;
1323 return FALSE;
1324 }
1325
1326 switch (ControlCode)
1327 {
1328 /* Get Device Information */
1329 case 0x00:
1330 {
1331 WORD InfoWord = 0;
1332
1333 if (Handle == DosSystemFileTable[0])
1334 {
1335 /* Console input */
1336 InfoWord |= 1 << 0;
1337 }
1338 else if (Handle == DosSystemFileTable[1])
1339 {
1340 /* Console output */
1341 InfoWord |= 1 << 1;
1342 }
1343
1344 /* It is a character device */
1345 InfoWord |= 1 << 7;
1346
1347 /* Return the device information word */
1348 EmulatorSetRegister(EMULATOR_REG_DX, InfoWord);
1349
1350 return TRUE;
1351 }
1352
1353 /* Unsupported control code */
1354 default:
1355 {
1356 DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode);
1357
1358 DosLastError = ERROR_INVALID_PARAMETER;
1359 return FALSE;
1360 }
1361 }
1362 }
1363
1364 VOID DosInt20h(LPWORD Stack)
1365 {
1366 /* This is the exit interrupt */
1367 DosTerminateProcess(Stack[STACK_CS], 0);
1368 }
1369
1370 VOID DosInt21h(LPWORD Stack)
1371 {
1372 CHAR Character;
1373 SYSTEMTIME SystemTime;
1374 PCHAR String;
1375 PDOS_INPUT_BUFFER InputBuffer;
1376 DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
1377 DWORD Ecx = EmulatorGetRegister(EMULATOR_REG_CX);
1378 DWORD Edx = EmulatorGetRegister(EMULATOR_REG_DX);
1379 DWORD Ebx = EmulatorGetRegister(EMULATOR_REG_BX);
1380 WORD DataSegment = (WORD)EmulatorGetRegister(EMULATOR_REG_DS);
1381 WORD ExtSegment = (WORD)EmulatorGetRegister(EMULATOR_REG_ES);
1382
1383 /* Check the value in the AH register */
1384 switch (HIBYTE(Eax))
1385 {
1386 /* Terminate Program */
1387 case 0x00:
1388 {
1389 DosTerminateProcess(Stack[STACK_CS], 0);
1390 break;
1391 }
1392
1393 /* Read Character And Echo */
1394 case 0x01:
1395 {
1396 Character = DosReadCharacter();
1397 DosPrintCharacter(Character);
1398
1399 if (!EmulatorGetFlag(EMULATOR_FLAG_CF))
1400 {
1401 EmulatorSetRegister(EMULATOR_REG_AX, (Eax & 0xFFFFFF00) | Character);
1402 }
1403
1404 break;
1405 }
1406
1407 /* Print Character */
1408 case 0x02:
1409 {
1410 DosPrintCharacter(LOBYTE(Edx));
1411 break;
1412 }
1413
1414 /* Direct Console I/O */
1415 case 0x06:
1416 {
1417 BYTE Character = LOBYTE(Edx);
1418
1419 if (Character != 0xFF)
1420 {
1421 /* Output */
1422 DosPrintCharacter(Character);
1423 }
1424 else
1425 {
1426 /* Input */
1427 Eax &= 0xFFFFFF00;
1428
1429 if (DosCheckInput())
1430 {
1431 Eax |= DosReadCharacter();
1432 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
1433 }
1434 else
1435 {
1436 /* No character available */
1437 Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF;
1438 }
1439 }
1440
1441 break;
1442 }
1443
1444 /* Read Character Without Echo */
1445 case 0x07:
1446 case 0x08:
1447 {
1448 Character = DosReadCharacter();
1449
1450 if (!EmulatorGetFlag(EMULATOR_FLAG_CF))
1451 {
1452 EmulatorSetRegister(EMULATOR_REG_AX, (Eax & 0xFFFFFF00) | Character);
1453 }
1454
1455 break;
1456 }
1457
1458 /* Print String */
1459 case 0x09:
1460 {
1461 String = (PCHAR)((ULONG_PTR)BaseAddress
1462 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1463
1464 while ((*String) != '$')
1465 {
1466 DosPrintCharacter(*String);
1467 String++;
1468 }
1469
1470 break;
1471 }
1472
1473 /* Read Buffered Input */
1474 case 0x0A:
1475 {
1476 InputBuffer = (PDOS_INPUT_BUFFER)((ULONG_PTR)BaseAddress
1477 + TO_LINEAR(DataSegment,
1478 LOWORD(Edx)));
1479
1480 while (Stack[STACK_COUNTER] < InputBuffer->MaxLength)
1481 {
1482 /* Try to read a character */
1483 Character = DosReadCharacter();
1484
1485 /* If it's not ready yet, let the BOP repeat */
1486 if (EmulatorGetFlag(EMULATOR_FLAG_CF)) break;
1487
1488 /* Echo the character and append it to the buffer */
1489 DosPrintCharacter(Character);
1490 InputBuffer->Buffer[Stack[STACK_COUNTER]] = Character;
1491
1492 if (Character == '\r') break;
1493 Stack[STACK_COUNTER]++;
1494 }
1495
1496 /* Update the length */
1497 InputBuffer->Length = Stack[STACK_COUNTER];
1498
1499 break;
1500 }
1501
1502 /* Get STDIN Status */
1503 case 0x0B:
1504 {
1505 if (DosCheckInput()) Eax |= 0xFF;
1506 else Eax &= 0xFFFFFF00;
1507
1508 EmulatorSetRegister(EMULATOR_REG_AX, Eax);
1509
1510 break;
1511 }
1512
1513 /* Set Default Drive */
1514 case 0x0E:
1515 {
1516 DosChangeDrive(LOBYTE(Edx));
1517 EmulatorSetRegister(EMULATOR_REG_AX,
1518 (Eax & 0xFFFFFF00) | (LastDrive - 'A' + 1));
1519
1520 break;
1521 }
1522
1523 /* Get Default Drive */
1524 case 0x19:
1525 {
1526 EmulatorSetRegister(EMULATOR_REG_AX,
1527 (Eax & 0xFFFFFF00) | CurrentDrive);
1528
1529 break;
1530 }
1531
1532 /* Set Disk Transfer Area */
1533 case 0x1A:
1534 {
1535 DiskTransferArea = MAKELONG(LOWORD(Edx), DataSegment);
1536 break;
1537 }
1538
1539 /* Set Interrupt Vector */
1540 case 0x25:
1541 {
1542 DWORD FarPointer = MAKELONG(LOWORD(Edx), DataSegment);
1543
1544 /* Write the new far pointer to the IDT */
1545 ((PDWORD)BaseAddress)[LOBYTE(Eax)] = FarPointer;
1546
1547 break;
1548 }
1549
1550 /* Get system date */
1551 case 0x2A:
1552 {
1553 GetLocalTime(&SystemTime);
1554 EmulatorSetRegister(EMULATOR_REG_CX,
1555 (Ecx & 0xFFFF0000) | SystemTime.wYear);
1556 EmulatorSetRegister(EMULATOR_REG_DX,
1557 (Edx & 0xFFFF0000)
1558 | (SystemTime.wMonth << 8)
1559 | SystemTime.wDay);
1560 EmulatorSetRegister(EMULATOR_REG_AX,
1561 (Eax & 0xFFFFFF00) | SystemTime.wDayOfWeek);
1562 break;
1563 }
1564
1565 /* Set system date */
1566 case 0x2B:
1567 {
1568 GetLocalTime(&SystemTime);
1569 SystemTime.wYear = LOWORD(Ecx);
1570 SystemTime.wMonth = HIBYTE(Edx);
1571 SystemTime.wDay = LOBYTE(Edx);
1572
1573 if (SetLocalTime(&SystemTime))
1574 {
1575 /* Return success */
1576 EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
1577 }
1578 else
1579 {
1580 /* Return failure */
1581 EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
1582 }
1583
1584 break;
1585 }
1586
1587 /* Get system time */
1588 case 0x2C:
1589 {
1590 GetLocalTime(&SystemTime);
1591 EmulatorSetRegister(EMULATOR_REG_CX,
1592 (Ecx & 0xFFFF0000)
1593 | (SystemTime.wHour << 8)
1594 | SystemTime.wMinute);
1595 EmulatorSetRegister(EMULATOR_REG_DX,
1596 (Edx & 0xFFFF0000)
1597 | (SystemTime.wSecond << 8)
1598 | (SystemTime.wMilliseconds / 10));
1599 break;
1600 }
1601
1602 /* Set system time */
1603 case 0x2D:
1604 {
1605 GetLocalTime(&SystemTime);
1606 SystemTime.wHour = HIBYTE(Ecx);
1607 SystemTime.wMinute = LOBYTE(Ecx);
1608 SystemTime.wSecond = HIBYTE(Edx);
1609 SystemTime.wMilliseconds = LOBYTE(Edx) * 10;
1610
1611 if (SetLocalTime(&SystemTime))
1612 {
1613 /* Return success */
1614 EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
1615 }
1616 else
1617 {
1618 /* Return failure */
1619 EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
1620 }
1621
1622 break;
1623 }
1624
1625 /* Get Disk Transfer Area */
1626 case 0x2F:
1627 {
1628 EmulatorSetRegister(EMULATOR_REG_ES, HIWORD(DiskTransferArea));
1629 EmulatorSetRegister(EMULATOR_REG_BX, LOWORD(DiskTransferArea));
1630
1631 break;
1632 }
1633
1634 /* Get DOS Version */
1635 case 0x30:
1636 {
1637 PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
1638
1639 EmulatorSetRegister(EMULATOR_REG_AX, PspBlock->DosVersion);
1640 break;
1641 }
1642
1643 /* Get Interrupt Vector */
1644 case 0x35:
1645 {
1646 DWORD FarPointer = ((PDWORD)BaseAddress)[LOBYTE(Eax)];
1647
1648 /* Read the address from the IDT into ES:BX */
1649 EmulatorSetRegister(EMULATOR_REG_ES, HIWORD(FarPointer));
1650 EmulatorSetRegister(EMULATOR_REG_BX, LOWORD(FarPointer));
1651
1652 break;
1653 }
1654
1655 /* Create Directory */
1656 case 0x39:
1657 {
1658 String = (PCHAR)((ULONG_PTR)BaseAddress
1659 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1660
1661 if (CreateDirectoryA(String, NULL))
1662 {
1663 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1664 }
1665 else
1666 {
1667 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1668 EmulatorSetRegister(EMULATOR_REG_AX,
1669 (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
1670 }
1671
1672 break;
1673 }
1674
1675 /* Remove Directory */
1676 case 0x3A:
1677 {
1678 String = (PCHAR)((ULONG_PTR)BaseAddress
1679 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1680
1681 if (RemoveDirectoryA(String))
1682 {
1683 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1684 }
1685 else
1686 {
1687 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1688 EmulatorSetRegister(EMULATOR_REG_AX,
1689 (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
1690 }
1691
1692
1693 break;
1694 }
1695
1696 /* Set Current Directory */
1697 case 0x3B:
1698 {
1699 String = (PCHAR)((ULONG_PTR)BaseAddress
1700 + TO_LINEAR(DataSegment, LOWORD(Edx)));
1701
1702 if (DosChangeDirectory(String))
1703 {
1704 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1705 }
1706 else
1707 {
1708 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1709 EmulatorSetRegister(EMULATOR_REG_AX,
1710 (Eax & 0xFFFF0000) | DosLastError);
1711 }
1712
1713 break;
1714 }
1715
1716 /* Create File */
1717 case 0x3C:
1718 {
1719 WORD FileHandle;
1720 WORD ErrorCode = DosCreateFile(&FileHandle,
1721 (LPCSTR)(ULONG_PTR)BaseAddress
1722 + TO_LINEAR(DataSegment, LOWORD(Edx)),
1723 LOWORD(Ecx));
1724
1725 if (ErrorCode == 0)
1726 {
1727 /* Clear CF */
1728 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1729
1730 /* Return the handle in AX */
1731 EmulatorSetRegister(EMULATOR_REG_AX,
1732 (Eax & 0xFFFF0000) | FileHandle);
1733 }
1734 else
1735 {
1736 /* Set CF */
1737 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1738
1739 /* Return the error code in AX */
1740 EmulatorSetRegister(EMULATOR_REG_AX,
1741 (Eax & 0xFFFF0000) | ErrorCode);
1742 }
1743
1744 break;
1745 }
1746
1747 /* Open File */
1748 case 0x3D:
1749 {
1750 WORD FileHandle;
1751 WORD ErrorCode = DosOpenFile(&FileHandle,
1752 (LPCSTR)(ULONG_PTR)BaseAddress
1753 + TO_LINEAR(DataSegment, LOWORD(Edx)),
1754 LOBYTE(Eax));
1755
1756 if (ErrorCode == 0)
1757 {
1758 /* Clear CF */
1759 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1760
1761 /* Return the handle in AX */
1762 EmulatorSetRegister(EMULATOR_REG_AX,
1763 (Eax & 0xFFFF0000) | FileHandle);
1764 }
1765 else
1766 {
1767 /* Set CF */
1768 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1769
1770 /* Return the error code in AX */
1771 EmulatorSetRegister(EMULATOR_REG_AX,
1772 (Eax & 0xFFFF0000) | ErrorCode);
1773 }
1774
1775 break;
1776 }
1777
1778 /* Close File */
1779 case 0x3E:
1780 {
1781 if (DosCloseHandle(LOWORD(Ebx)))
1782 {
1783 /* Clear CF */
1784 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1785 }
1786 else
1787 {
1788 /* Set CF */
1789 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1790
1791 /* Return the error code in AX */
1792 EmulatorSetRegister(EMULATOR_REG_AX,
1793 (Eax & 0xFFFF0000) | ERROR_INVALID_HANDLE);
1794 }
1795
1796 break;
1797 }
1798
1799 /* Read File */
1800 case 0x3F:
1801 {
1802 WORD Handle = LOWORD(Ebx);
1803 LPBYTE Buffer = (LPBYTE)((ULONG_PTR)BaseAddress + TO_LINEAR(DataSegment, LOWORD(Edx)));
1804 WORD Count = LOWORD(Ecx);
1805 WORD BytesRead = 0;
1806 WORD ErrorCode = ERROR_SUCCESS;
1807
1808 if (IsConsoleHandle(DosGetRealHandle(Handle)))
1809 {
1810 while (Stack[STACK_COUNTER] < Count)
1811 {
1812 /* Read a character from the BIOS */
1813 Buffer[Stack[STACK_COUNTER]] = LOBYTE(BiosGetCharacter()); // FIXME: Security checks!
1814
1815 /* Stop if the BOP needs to be repeated */
1816 if (EmulatorGetFlag(EMULATOR_FLAG_CF)) break;
1817
1818 /* Increment the counter */
1819 Stack[STACK_COUNTER]++;
1820 }
1821
1822 if (Stack[STACK_COUNTER] < Count) ErrorCode = ERROR_NOT_READY;
1823 else BytesRead = Count;
1824 }
1825 else
1826 {
1827 /* Use the file reading function */
1828 ErrorCode = DosReadFile(Handle, Buffer, Count, &BytesRead);
1829 }
1830
1831 if (ErrorCode == 0)
1832 {
1833 /* Clear CF */
1834 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1835
1836 /* Return the number of bytes read in AX */
1837 EmulatorSetRegister(EMULATOR_REG_AX,
1838 (Eax & 0xFFFF0000) | BytesRead);
1839 }
1840 else if (ErrorCode != ERROR_NOT_READY)
1841 {
1842 /* Set CF */
1843 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1844
1845 /* Return the error code in AX */
1846 EmulatorSetRegister(EMULATOR_REG_AX,
1847 (Eax & 0xFFFF0000) | ErrorCode);
1848 }
1849 break;
1850 }
1851
1852 /* Write File */
1853 case 0x40:
1854 {
1855 WORD BytesWritten = 0;
1856 WORD ErrorCode = DosWriteFile(LOWORD(Ebx),
1857 (LPVOID)((ULONG_PTR)BaseAddress
1858 + TO_LINEAR(DataSegment, LOWORD(Edx))),
1859 LOWORD(Ecx),
1860 &BytesWritten);
1861
1862 if (ErrorCode == 0)
1863 {
1864 /* Clear CF */
1865 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1866
1867 /* Return the number of bytes written in AX */
1868 EmulatorSetRegister(EMULATOR_REG_AX,
1869 (Eax & 0xFFFF0000) | BytesWritten);
1870 }
1871 else
1872 {
1873 /* Set CF */
1874 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1875
1876 /* Return the error code in AX */
1877 EmulatorSetRegister(EMULATOR_REG_AX,
1878 (Eax & 0xFFFF0000) | ErrorCode);
1879 }
1880
1881 break;
1882 }
1883
1884 /* Delete File */
1885 case 0x41:
1886 {
1887 LPSTR FileName = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(DataSegment, Edx));
1888
1889 /* Call the API function */
1890 if (DeleteFileA(FileName)) Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1891 else
1892 {
1893 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1894 EmulatorSetRegister(EMULATOR_REG_AX, GetLastError());
1895 }
1896
1897 break;
1898 }
1899
1900 /* Seek File */
1901 case 0x42:
1902 {
1903 DWORD NewLocation;
1904 WORD ErrorCode = DosSeekFile(LOWORD(Ebx),
1905 MAKELONG(LOWORD(Edx), LOWORD(Ecx)),
1906 LOBYTE(Eax),
1907 &NewLocation);
1908
1909 if (ErrorCode == 0)
1910 {
1911 /* Clear CF */
1912 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1913
1914 /* Return the new offset in DX:AX */
1915 EmulatorSetRegister(EMULATOR_REG_DX,
1916 (Edx & 0xFFFF0000) | HIWORD(NewLocation));
1917 EmulatorSetRegister(EMULATOR_REG_AX,
1918 (Eax & 0xFFFF0000) | LOWORD(NewLocation));
1919 }
1920 else
1921 {
1922 /* Set CF */
1923 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1924
1925 /* Return the error code in AX */
1926 EmulatorSetRegister(EMULATOR_REG_AX,
1927 (Eax & 0xFFFF0000) | ErrorCode);
1928 }
1929
1930 break;
1931 }
1932
1933 /* Get/Set File Attributes */
1934 case 0x43:
1935 {
1936 DWORD Attributes;
1937 LPSTR FileName = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(DataSegment, Edx));
1938
1939 if (LOBYTE(Eax) == 0x00)
1940 {
1941 /* Get the attributes */
1942 Attributes = GetFileAttributesA(FileName);
1943
1944 /* Check if it failed */
1945 if (Attributes == INVALID_FILE_ATTRIBUTES)
1946 {
1947 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1948 EmulatorSetRegister(EMULATOR_REG_AX, GetLastError());
1949
1950 break;
1951 }
1952
1953 /* Return the attributes that DOS can understand */
1954 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1955 EmulatorSetRegister(EMULATOR_REG_CX,
1956 (Ecx & 0xFFFFFF00) | LOBYTE(Attributes));
1957 }
1958 else if (LOBYTE(Eax) == 0x01)
1959 {
1960 /* Try to set the attributes */
1961 if (SetFileAttributesA(FileName, LOBYTE(Ecx)))
1962 {
1963 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1964 }
1965 else
1966 {
1967 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1968 EmulatorSetRegister(EMULATOR_REG_AX, GetLastError());
1969 }
1970 }
1971 else
1972 {
1973 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1974 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_FUNCTION);
1975 }
1976
1977 break;
1978 }
1979
1980 /* IOCTL */
1981 case 0x44:
1982 {
1983 if (DosHandleIoctl(LOBYTE(Eax), LOWORD(Ebx)))
1984 {
1985 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
1986 }
1987 else
1988 {
1989 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
1990 EmulatorSetRegister(EMULATOR_REG_AX, DosLastError);
1991 }
1992
1993 break;
1994 }
1995
1996 /* Duplicate Handle */
1997 case 0x45:
1998 {
1999 WORD NewHandle;
2000 HANDLE Handle = DosGetRealHandle(LOWORD(Ebx));
2001
2002 if (Handle != INVALID_HANDLE_VALUE)
2003 {
2004 /* The handle is invalid */
2005 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2006 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_HANDLE);
2007
2008 break;
2009 }
2010
2011 /* Open a new handle to the same entry */
2012 NewHandle = DosOpenHandle(Handle);
2013
2014 if (NewHandle == INVALID_DOS_HANDLE)
2015 {
2016 /* Too many files open */
2017 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2018 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_TOO_MANY_OPEN_FILES);
2019
2020 break;
2021 }
2022
2023 /* Return the result */
2024 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2025 EmulatorSetRegister(EMULATOR_REG_AX, NewHandle);
2026
2027 break;
2028 }
2029
2030 /* Force Duplicate Handle */
2031 case 0x46:
2032 {
2033 if (DosDuplicateHandle(LOWORD(Ebx), LOWORD(Ecx)))
2034 {
2035 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2036 }
2037 else
2038 {
2039 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2040 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_HANDLE);
2041 }
2042
2043 break;
2044 }
2045
2046 /* Allocate Memory */
2047 case 0x48:
2048 {
2049 WORD MaxAvailable = 0;
2050 WORD Segment = DosAllocateMemory(LOWORD(Ebx), &MaxAvailable);
2051
2052 if (Segment != 0)
2053 {
2054 EmulatorSetRegister(EMULATOR_REG_AX, Segment);
2055 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2056 }
2057 else
2058 {
2059 EmulatorSetRegister(EMULATOR_REG_AX, DosLastError);
2060 EmulatorSetRegister(EMULATOR_REG_BX, MaxAvailable);
2061 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2062 }
2063
2064 break;
2065 }
2066
2067 /* Free Memory */
2068 case 0x49:
2069 {
2070 if (DosFreeMemory(ExtSegment))
2071 {
2072 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2073 }
2074 else
2075 {
2076 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_ARENA_TRASHED);
2077 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2078 }
2079
2080 break;
2081 }
2082
2083 /* Resize Memory Block */
2084 case 0x4A:
2085 {
2086 WORD Size;
2087
2088 if (DosResizeMemory(ExtSegment, LOWORD(Ebx), &Size))
2089 {
2090 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2091 }
2092 else
2093 {
2094 EmulatorSetRegister(EMULATOR_REG_AX, DosLastError);
2095 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2096 EmulatorSetRegister(EMULATOR_REG_BX, Size);
2097 }
2098
2099 break;
2100 }
2101
2102 /* Terminate With Return Code */
2103 case 0x4C:
2104 {
2105 DosTerminateProcess(CurrentPsp, LOBYTE(Eax));
2106 break;
2107 }
2108
2109 /* Get Current Process */
2110 case 0x51:
2111 {
2112 EmulatorSetRegister(EMULATOR_REG_BX, CurrentPsp);
2113
2114 break;
2115 }
2116
2117 /* Get/Set Memory Management Options */
2118 case 0x58:
2119 {
2120 if (LOBYTE(Eax) == 0x00)
2121 {
2122 /* Get allocation strategy */
2123
2124 EmulatorSetRegister(EMULATOR_REG_AX, DosAllocStrategy);
2125 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2126 }
2127 else if (LOBYTE(Eax) == 0x01)
2128 {
2129 /* Set allocation strategy */
2130
2131 if ((LOBYTE(Ebx) & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
2132 == (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
2133 {
2134 /* Can't set both */
2135 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_PARAMETER);
2136 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2137 break;
2138 }
2139
2140 if ((LOBYTE(Ebx) & 0x3F) > DOS_ALLOC_LAST_FIT)
2141 {
2142 /* Invalid allocation strategy */
2143 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_PARAMETER);
2144 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2145 break;
2146 }
2147
2148 DosAllocStrategy = LOBYTE(Ebx);
2149 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2150 }
2151 else if (LOBYTE(Eax) == 0x02)
2152 {
2153 /* Get UMB link state */
2154
2155 Eax &= 0xFFFFFF00;
2156 if (DosUmbLinked) Eax |= 1;
2157 EmulatorSetRegister(EMULATOR_REG_AX, Eax);
2158 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2159 }
2160 else if (LOBYTE(Eax) == 0x03)
2161 {
2162 /* Set UMB link state */
2163
2164 if (Ebx) DosLinkUmb();
2165 else DosUnlinkUmb();
2166 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
2167 }
2168 else
2169 {
2170 /* Invalid or unsupported function */
2171
2172 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2173 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_FUNCTION);
2174 }
2175
2176 break;
2177 }
2178
2179 /* Unsupported */
2180 default:
2181 {
2182 DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax));
2183 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
2184 }
2185 }
2186 }
2187
2188 VOID DosBreakInterrupt(LPWORD Stack)
2189 {
2190 UNREFERENCED_PARAMETER(Stack);
2191
2192 VdmRunning = FALSE;
2193 }
2194
2195 BOOLEAN DosInitialize(VOID)
2196 {
2197 BYTE i;
2198 PDOS_MCB Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT);
2199 FILE *Stream;
2200 WCHAR Buffer[256];
2201 LPWSTR SourcePtr, Environment;
2202 LPSTR AsciiString;
2203 LPSTR DestPtr = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(SYSTEM_ENV_BLOCK, 0));
2204 DWORD AsciiSize;
2205 CHAR CurrentDirectory[MAX_PATH];
2206 CHAR DosDirectory[DOS_DIR_LENGTH];
2207 LPSTR Path;
2208
2209 /* Initialize the MCB */
2210 Mcb->BlockType = 'Z';
2211 Mcb->Size = USER_MEMORY_SIZE;
2212 Mcb->OwnerPsp = 0;
2213
2214 /* Initialize the link MCB to the UMB area */
2215 Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT + USER_MEMORY_SIZE + 1);
2216 Mcb->BlockType = 'M';
2217 Mcb->Size = UMB_START_SEGMENT - FIRST_MCB_SEGMENT - USER_MEMORY_SIZE - 2;
2218 Mcb->OwnerPsp = SYSTEM_PSP;
2219
2220 /* Initialize the UMB area */
2221 Mcb = SEGMENT_TO_MCB(UMB_START_SEGMENT);
2222 Mcb->BlockType = 'Z';
2223 Mcb->Size = UMB_END_SEGMENT - UMB_START_SEGMENT;
2224 Mcb->OwnerPsp = 0;
2225
2226 /* Get the environment strings */
2227 SourcePtr = Environment = GetEnvironmentStringsW();
2228 if (Environment == NULL) return FALSE;
2229
2230 /* Fill the DOS system environment block */
2231 while (*SourcePtr)
2232 {
2233 /* Get the size of the ASCII string */
2234 AsciiSize = WideCharToMultiByte(CP_ACP,
2235 0,
2236 SourcePtr,
2237 -1,
2238 NULL,
2239 0,
2240 NULL,
2241 NULL);
2242
2243 /* Allocate memory for the ASCII string */
2244 AsciiString = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, AsciiSize);
2245 if (AsciiString == NULL)
2246 {
2247 FreeEnvironmentStringsW(Environment);
2248 return FALSE;
2249 }
2250
2251 /* Convert to ASCII */
2252 WideCharToMultiByte(CP_ACP,
2253 0,
2254 SourcePtr,
2255 -1,
2256 AsciiString,
2257 AsciiSize,
2258 NULL,
2259 NULL);
2260
2261 /* Copy the string into DOS memory */
2262 strcpy(DestPtr, AsciiString);
2263
2264 /* Move to the next string */
2265 SourcePtr += wcslen(SourcePtr) + 1;
2266 DestPtr += strlen(AsciiString);
2267 *(DestPtr++) = 0;
2268
2269 /* Free the memory */
2270 HeapFree(GetProcessHeap(), 0, AsciiString);
2271 }
2272 *DestPtr = 0;
2273
2274 /* Free the memory allocated for environment strings */
2275 FreeEnvironmentStringsW(Environment);
2276
2277 /* Clear the current directory buffer */
2278 ZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
2279
2280 /* Get the current directory */
2281 if (!GetCurrentDirectoryA(MAX_PATH, CurrentDirectory))
2282 {
2283 // TODO: Use some kind of default path?
2284 return FALSE;
2285 }
2286
2287 /* Convert that to a DOS path */
2288 if (!GetShortPathNameA(CurrentDirectory, DosDirectory, DOS_DIR_LENGTH))
2289 {
2290 // TODO: Use some kind of default path?
2291 return FALSE;
2292 }
2293
2294 /* Set the drive */
2295 CurrentDrive = DosDirectory[0] - 'A';
2296
2297 /* Get the path */
2298 Path = strchr(DosDirectory, '\\');
2299 if (Path != NULL)
2300 {
2301 /* Skip the backslash */
2302 Path++;
2303 }
2304
2305 /* Set the directory */
2306 if (Path != NULL) strcpy(CurrentDirectories[CurrentDrive], Path);
2307
2308 /* Read CONFIG.SYS */
2309 Stream = _wfopen(DOS_CONFIG_PATH, L"r");
2310 if (Stream != NULL)
2311 {
2312 while (fgetws(Buffer, 256, Stream))
2313 {
2314 // TODO: Parse the line
2315 }
2316 fclose(Stream);
2317 }
2318
2319 /* Initialize the SFT */
2320 for (i = 0; i < DOS_SFT_SIZE; i++)
2321 {
2322 DosSystemFileTable[i] = INVALID_HANDLE_VALUE;
2323 DosSftRefCount[i] = 0;
2324 }
2325
2326 /* Get handles to standard I/O devices */
2327 DosSystemFileTable[0] = GetStdHandle(STD_INPUT_HANDLE);
2328 DosSystemFileTable[1] = GetStdHandle(STD_OUTPUT_HANDLE);
2329 DosSystemFileTable[2] = GetStdHandle(STD_ERROR_HANDLE);
2330
2331 return TRUE;
2332 }
2333
2334 /* EOF */