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