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