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