10ae350c40d99536856b987ef83b01abe87f902b
[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 #include "ntvdm.h"
10
11 WORD CurrentPsp = SYSTEM_PSP, LastError = 0;
12
13 static VOID DosCombineFreeBlocks(WORD StartBlock)
14 {
15 PDOS_MCB CurrentMcb = SEGMENT_TO_MCB(StartBlock), NextMcb;
16
17 /* If this is the last block or it's not free, quit */
18 if (CurrentMcb->BlockType == 'Z' || CurrentMcb->OwnerPsp != 0) return;
19
20 while (TRUE)
21 {
22 /* Get a pointer to the next MCB */
23 NextMcb = SEGMENT_TO_MCB(StartBlock + CurrentMcb->Size + 1);
24
25 /* Check if the next MCB is free */
26 if (NextMcb->OwnerPsp == 0)
27 {
28 /* Combine them */
29 CurrentMcb->Size += NextMcb->Size + 1;
30 CurrentMcb->BlockType = NextMcb->BlockType;
31 NextMcb->BlockType = 'I';
32 }
33 else
34 {
35 /* No more adjoining free blocks */
36 break;
37 }
38 }
39 }
40
41 static WORD DosCopyEnvironmentBlock(WORD SourceSegment)
42 {
43 PCHAR Ptr, SourceBuffer, DestBuffer = NULL;
44 ULONG TotalSize = 0;
45 WORD DestSegment;
46
47 Ptr = SourceBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(SourceSegment, 0));
48
49 /* Calculate the size of the environment block */
50 while (*Ptr)
51 {
52 TotalSize += strlen(Ptr) + 1;
53 Ptr += strlen(Ptr) + 1;
54 }
55 TotalSize++;
56
57 /* Allocate the memory for the environment block */
58 DestSegment = DosAllocateMemory((TotalSize + 0x0F) >> 4, NULL);
59 if (!DestSegment) return 0;
60
61 Ptr = SourceBuffer;
62
63 DestBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(DestSegment, 0));
64 while (*Ptr)
65 {
66 /* Copy the string */
67 strcpy(DestBuffer, Ptr);
68
69 /* Advance to the next string */
70 Ptr += strlen(Ptr) + 1;
71 DestBuffer += strlen(Ptr) + 1;
72 }
73
74 /* Set the final zero */
75 *DestBuffer = 0;
76
77 return DestSegment;
78 }
79
80 WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable)
81 {
82 WORD Result = 0, Segment = FIRST_MCB_SEGMENT, MaxSize = 0;
83 PDOS_MCB CurrentMcb, NextMcb;
84
85 while (TRUE)
86 {
87 /* Get a pointer to the MCB */
88 CurrentMcb = SEGMENT_TO_MCB(Segment);
89
90 /* Make sure it's valid */
91 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z')
92 {
93 return 0;
94 }
95
96 /* Only check free blocks */
97 if (CurrentMcb->OwnerPsp != 0) goto Next;
98
99 /* Combine this free block with adjoining free blocks */
100 DosCombineFreeBlocks(Segment);
101
102 /* Update the maximum block size */
103 if (CurrentMcb->Size > MaxSize) MaxSize = CurrentMcb->Size;
104
105 /* Check if this block is big enough */
106 if (CurrentMcb->Size < Size) goto Next;
107
108 /* It is, update the smallest found so far */
109 if ((Result == 0) || (CurrentMcb->Size < SEGMENT_TO_MCB(Result)->Size))
110 {
111 Result = Segment;
112 }
113
114 Next:
115 /* If this was the last MCB in the chain, quit */
116 if (CurrentMcb->BlockType == 'Z') break;
117
118 /* Otherwise, update the segment and continue */
119 Segment += CurrentMcb->Size + 1;
120 }
121
122 /* If we didn't find a free block, return 0 */
123 if (Result == 0)
124 {
125 if (MaxAvailable) *MaxAvailable = MaxSize;
126 return 0;
127 }
128
129 /* Get a pointer to the MCB */
130 CurrentMcb = SEGMENT_TO_MCB(Result);
131
132 /* Check if the block is larger than requested */
133 if (CurrentMcb->Size > Size)
134 {
135 /* It is, split it into two blocks */
136 NextMcb = SEGMENT_TO_MCB(Result + Size + 1);
137
138 /* Initialize the new MCB structure */
139 NextMcb->BlockType = CurrentMcb->BlockType;
140 NextMcb->Size = CurrentMcb->Size - Size - 1;
141 NextMcb->OwnerPsp = 0;
142
143 /* Update the current block */
144 CurrentMcb->BlockType = 'M';
145 CurrentMcb->Size = Size;
146 }
147
148 /* Take ownership of the block */
149 CurrentMcb->OwnerPsp = CurrentPsp;
150
151 /* Return the segment of the data portion of the block */
152 return Result + 1;
153 }
154
155 WORD DosResizeMemory(WORD BlockData, WORD NewSize)
156 {
157 WORD Segment = BlockData - 1, ReturnSize = 0, NextSegment;
158 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment), NextMcb;
159
160 /* Make sure this is a valid, allocated block */
161 if ((Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') || Mcb->OwnerPsp == 0)
162 {
163 return 0;
164 }
165
166 /* Check if need to expand or contract the block */
167 if (NewSize > Mcb->Size)
168 {
169 /* We can't expand the last block */
170 if (Mcb->BlockType != 'M') return Mcb->Size;
171
172 ReturnSize = Mcb->Size;
173
174 /* Get the pointer and segment of the next MCB */
175 NextSegment = Segment + Mcb->Size + 1;
176 NextMcb = SEGMENT_TO_MCB(NextSegment);
177
178 /* Make sure the next segment is free */
179 if (NextMcb->OwnerPsp != 0) return Mcb->Size;
180
181 /* Combine this free block with adjoining free blocks */
182 DosCombineFreeBlocks(NextSegment);
183
184 /* Set the maximum possible size of the block */
185 ReturnSize += NextMcb->Size + 1;
186
187 /* Maximize the current block */
188 Mcb->Size = ReturnSize;
189 Mcb->BlockType = NextMcb->BlockType;
190
191 /* Invalidate the next block */
192 NextMcb->BlockType = 'I';
193
194 /* Check if the block is larger than requested */
195 if (Mcb->Size > NewSize)
196 {
197 /* It is, split it into two blocks */
198 NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
199
200 /* Initialize the new MCB structure */
201 NextMcb->BlockType = Mcb->BlockType;
202 NextMcb->Size = Mcb->Size - NewSize - 1;
203 NextMcb->OwnerPsp = 0;
204
205 /* Update the current block */
206 Mcb->BlockType = 'M';
207 Mcb->Size = NewSize;
208 }
209 }
210 else if (NewSize < Mcb->Size)
211 {
212 /* Just split the block */
213 NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
214 NextMcb->BlockType = Mcb->BlockType;
215 NextMcb->Size = Mcb->Size - NewSize - 1;
216 NextMcb->OwnerPsp = 0;
217
218 /* Update the MCB */
219 Mcb->BlockType = 'M';
220 Mcb->Size = NewSize;
221 }
222
223 return ReturnSize;
224 }
225
226 BOOLEAN DosFreeMemory(WORD BlockData)
227 {
228 PDOS_MCB Mcb = SEGMENT_TO_MCB(BlockData - 1);
229
230 /* Make sure the MCB is valid */
231 if (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') return FALSE;
232
233 /* Mark the block as free */
234 Mcb->OwnerPsp = 0;
235
236 return TRUE;
237 }
238
239 WORD DosCreateFile(LPCSTR FilePath)
240 {
241 // TODO: NOT IMPLEMENTED
242 return 0;
243 }
244
245 WORD DosOpenFile(LPCSTR FilePath)
246 {
247 // TODO: NOT IMPLEMENTED
248 return 0;
249 }
250
251 VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment)
252 {
253 INT i;
254 PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment);
255 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
256
257 ZeroMemory(PspBlock, sizeof(DOS_PSP));
258
259 /* Set the exit interrupt */
260 PspBlock->Exit[0] = 0xCD; // int 0x20
261 PspBlock->Exit[1] = 0x20;
262
263 /* Set the program size */
264 PspBlock->MemSize = ProgramSize;
265
266 /* Save the interrupt vectors */
267 PspBlock->TerminateAddress = IntVecTable[0x22];
268 PspBlock->BreakAddress = IntVecTable[0x23];
269 PspBlock->CriticalAddress = IntVecTable[0x24];
270
271 /* Set the parent PSP */
272 PspBlock->ParentPsp = CurrentPsp;
273
274 /* Initialize the handle table */
275 for (i = 0; i < 20; i++) PspBlock->HandleTable[i] = 0xFF;
276
277 /* Did we get an environment segment? */
278 if (!Environment)
279 {
280 /* No, copy the one from the parent */
281 Environment = DosCopyEnvironmentBlock((CurrentPsp != SYSTEM_PSP)
282 ? SEGMENT_TO_PSP(CurrentPsp)->EnvBlock
283 : SYSTEM_ENV_BLOCK);
284 }
285
286 PspBlock->EnvBlock = Environment;
287
288 /* Set the handle table pointers to the internal handle table */
289 PspBlock->HandleTableSize = 20;
290 PspBlock->HandleTablePtr = MAKELONG(0x18, PspSegment);
291
292 /* Set the DOS version */
293 PspBlock->DosVersion = DOS_VERSION;
294
295 /* Set the far call opcodes */
296 PspBlock->FarCall[0] = 0xCD; // int 0x21
297 PspBlock->FarCall[1] = 0x21;
298 PspBlock->FarCall[2] = 0xCB; // retf
299
300 /* Set the command line */
301 PspBlock->CommandLineSize = strlen(CommandLine);
302 RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
303 PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
304 }
305
306 BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
307 {
308 BOOLEAN Success = FALSE;
309 HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
310 LPBYTE Address = NULL;
311 LPSTR ProgramFilePath, Parameters[128];
312 CHAR CommandLineCopy[128];
313 INT ParamCount = 0;
314 WORD i, Segment, FileSize, ExeSize;
315 PIMAGE_DOS_HEADER Header;
316 PDWORD RelocationTable;
317 PWORD RelocWord;
318
319 /* Save a copy of the command line */
320 strcpy(CommandLineCopy, CommandLine);
321
322 /* Get the file name of the executable */
323 ProgramFilePath = strtok(CommandLineCopy, " \t");
324
325 /* Load the parameters in the local array */
326 while ((ParamCount < 256)
327 && ((Parameters[ParamCount] = strtok(NULL, " \t")) != NULL))
328 {
329 ParamCount++;
330 }
331
332 /* Open a handle to the executable */
333 FileHandle = CreateFileA(ProgramFilePath,
334 GENERIC_READ,
335 0,
336 NULL,
337 OPEN_EXISTING,
338 FILE_ATTRIBUTE_NORMAL,
339 NULL);
340 if (FileHandle == INVALID_HANDLE_VALUE) goto Cleanup;
341
342 /* Get the file size */
343 FileSize = GetFileSize(FileHandle, NULL);
344
345 /* Create a mapping object for the file */
346 FileMapping = CreateFileMapping(FileHandle,
347 NULL,
348 PAGE_READONLY,
349 0,
350 0,
351 NULL);
352 if (FileMapping == NULL) goto Cleanup;
353
354 /* Map the file into memory */
355 Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
356 if (Address == NULL) goto Cleanup;
357
358 /* Check if this is an EXE file or a COM file */
359 if (Address[0] == 'M' && Address[1] == 'Z')
360 {
361 /* EXE file */
362
363 /* Get the MZ header */
364 Header = (PIMAGE_DOS_HEADER)Address;
365
366 // TODO: Verify checksum and executable!
367
368 /* Get the base size of the file, in paragraphs (rounded up) */
369 ExeSize = (((Header->e_cp - 1) << 8) + Header->e_cblp + 0x0F) >> 4;
370
371 /* Loop from the maximum to the minimum number of extra paragraphs */
372 for (i = Header->e_maxalloc; i >= Header->e_minalloc; i--)
373 {
374 /* Try to allocate that much memory */
375 Segment = DosAllocateMemory(ExeSize + (sizeof(DOS_PSP) >> 4) + i, NULL);
376 if (Segment != 0) break;
377 }
378
379 /* Check if at least the lowest allocation was successful */
380 if (Segment == 0) goto Cleanup;
381
382 /* Initialize the PSP */
383 DosInitializePsp(Segment,
384 CommandLine, ExeSize + (sizeof(DOS_PSP) >> 4) + i,
385 EnvBlock);
386
387 /* Copy the program to Segment:0100 */
388 RtlCopyMemory((PVOID)((ULONG_PTR)BaseAddress
389 + TO_LINEAR(Segment, 0x100)),
390 Address + (Header->e_cparhdr << 4),
391 FileSize - (Header->e_cparhdr << 4));
392
393 /* Get the relocation table */
394 RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
395
396 /* Perform relocations */
397 for (i = 0; i < Header->e_crlc; i++)
398 {
399 /* Get a pointer to the word that needs to be patched */
400 RelocWord = (PWORD)((ULONG_PTR)BaseAddress
401 + TO_LINEAR(Segment + HIWORD(RelocationTable[i]),
402 0x100 + LOWORD(RelocationTable[i])));
403
404 /* Add the number of the EXE segment to it */
405 *RelocWord += Segment + (sizeof(DOS_PSP) >> 4);
406 }
407
408 /* Set the initial segment registers */
409 EmulatorSetRegister(EMULATOR_REG_DS, Segment);
410 EmulatorSetRegister(EMULATOR_REG_ES, Segment);
411
412 /* Set the stack to the location from the header */
413 EmulatorSetStack(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss,
414 Header->e_sp);
415
416 /* Execute */
417 CurrentPsp = Segment;
418 EmulatorExecute(Segment + Header->e_cs, sizeof(DOS_PSP) + Header->e_ip);
419
420 Success = TRUE;
421 }
422 else
423 {
424 /* COM file */
425
426 /* Allocate memory for the whole program and the PSP */
427 Segment = DosAllocateMemory((FileSize + sizeof(DOS_PSP)) >> 4, NULL);
428 if (Segment == 0) goto Cleanup;
429
430 /* Copy the program to Segment:0100 */
431 RtlCopyMemory((PVOID)((ULONG_PTR)BaseAddress
432 + TO_LINEAR(Segment, 0x100)),
433 Address,
434 FileSize);
435
436 /* Initialize the PSP */
437 DosInitializePsp(Segment,
438 CommandLine,
439 (FileSize + sizeof(DOS_PSP)) >> 4,
440 EnvBlock);
441
442 /* Set the initial segment registers */
443 EmulatorSetRegister(EMULATOR_REG_DS, Segment);
444 EmulatorSetRegister(EMULATOR_REG_ES, Segment);
445
446 /* Set the stack to the last word of the segment */
447 EmulatorSetStack(Segment, 0xFFFE);
448
449 /* Execute */
450 CurrentPsp = Segment;
451 EmulatorExecute(Segment, 0x100);
452
453 Success = TRUE;
454 }
455
456 Cleanup:
457 /* Unmap the file*/
458 if (Address != NULL) UnmapViewOfFile(Address);
459
460 /* Close the file mapping object */
461 if (FileMapping != NULL) CloseHandle(FileMapping);
462
463 /* Close the file handle */
464 if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
465
466 return Success;
467 }
468
469 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
470 {
471 WORD McbSegment = FIRST_MCB_SEGMENT;
472 PDOS_MCB CurrentMcb;
473 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
474 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
475
476 /* Check if this PSP is it's own parent */
477 if (PspBlock->ParentPsp == Psp) goto Done;
478
479 // TODO: Close all handles opened by the process
480
481 /* Free the memory used by the process */
482 while (TRUE)
483 {
484 /* Get a pointer to the MCB */
485 CurrentMcb = SEGMENT_TO_MCB(McbSegment);
486
487 /* Make sure the MCB is valid */
488 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType !='Z') break;
489
490 /* If this block was allocated by the process, free it */
491 if (CurrentMcb->OwnerPsp == Psp) DosFreeMemory(McbSegment);
492
493 /* If this was the last block, quit */
494 if (CurrentMcb->BlockType == 'Z') break;
495
496 /* Update the segment and continue */
497 McbSegment += CurrentMcb->Size + 1;
498 }
499
500 Done:
501 /* Restore the interrupt vectors */
502 IntVecTable[0x22] = PspBlock->TerminateAddress;
503 IntVecTable[0x23] = PspBlock->BreakAddress;
504 IntVecTable[0x24] = PspBlock->CriticalAddress;
505
506 /* Update the current PSP */
507 if (Psp == CurrentPsp)
508 {
509 CurrentPsp = PspBlock->ParentPsp;
510 if (CurrentPsp == SYSTEM_PSP) VdmRunning = FALSE;
511 }
512
513 /* Return control to the parent process */
514 EmulatorExecute(HIWORD(PspBlock->TerminateAddress),
515 LOWORD(PspBlock->TerminateAddress));
516 }
517
518 CHAR DosReadCharacter()
519 {
520 // TODO: STDIN can be redirected under DOS 2.0+
521 return _getch();
522 }
523
524 VOID DosPrintCharacter(CHAR Character)
525 {
526 // TODO: STDOUT can be redirected under DOS 2.0+
527 if (Character == '\r') Character = '\n';
528 putchar(Character);
529 }
530
531 VOID DosInt20h(WORD CodeSegment)
532 {
533 /* This is the exit interrupt */
534 DosTerminateProcess(CodeSegment, 0);
535 }
536
537 VOID DosInt21h(WORD CodeSegment)
538 {
539 INT i;
540 CHAR Character;
541 SYSTEMTIME SystemTime;
542 PCHAR String;
543 PDOS_INPUT_BUFFER InputBuffer;
544 DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
545 DWORD Ecx = EmulatorGetRegister(EMULATOR_REG_CX);
546 DWORD Edx = EmulatorGetRegister(EMULATOR_REG_DX);
547 DWORD Ebx = EmulatorGetRegister(EMULATOR_REG_BX);
548 WORD DataSegment = EmulatorGetRegister(EMULATOR_REG_DS);
549 WORD ExtSegment = EmulatorGetRegister(EMULATOR_REG_ES);
550
551 /* Check the value in the AH register */
552 switch (HIBYTE(Eax))
553 {
554 /* Terminate Program */
555 case 0x00:
556 {
557 DosTerminateProcess(CodeSegment, 0);
558 break;
559 }
560
561 /* Read Character And Echo */
562 case 0x01:
563 {
564 Character = DosReadCharacter();
565 DosPrintCharacter(Character);
566 EmulatorSetRegister(EMULATOR_REG_AX, (Eax & 0xFFFFFF00) | Character);
567 break;
568 }
569
570 /* Print Character */
571 case 0x02:
572 {
573 DosPrintCharacter(LOBYTE(Edx));
574 break;
575 }
576
577 /* Read Character Without Echo */
578 case 0x08:
579 {
580 EmulatorSetRegister(EMULATOR_REG_AX,
581 (Eax & 0xFFFFFF00) | DosReadCharacter());
582 break;
583 }
584
585 /* Print String */
586 case 0x09:
587 {
588 String = (PCHAR)((ULONG_PTR)BaseAddress
589 + TO_LINEAR(DataSegment, LOWORD(Edx)));
590
591 while ((*String) != '$')
592 {
593 DosPrintCharacter(*String);
594 String++;
595 }
596
597 break;
598 }
599
600 /* Read Buffered Input */
601 case 0x0A:
602 {
603 InputBuffer = (PDOS_INPUT_BUFFER)((ULONG_PTR)BaseAddress
604 + TO_LINEAR(DataSegment,
605 LOWORD(Edx)));
606
607 InputBuffer->Length = 0;
608 for (i = 0; i < InputBuffer->MaxLength; i ++)
609 {
610 Character = DosReadCharacter();
611 DosPrintCharacter(Character);
612 InputBuffer->Buffer[InputBuffer->Length] = Character;
613 if (Character == '\r') break;
614 InputBuffer->Length++;
615 }
616
617 break;
618 }
619
620 /* Get system date */
621 case 0x2A:
622 {
623 GetLocalTime(&SystemTime);
624 EmulatorSetRegister(EMULATOR_REG_CX,
625 (Ecx & 0xFFFF0000) | SystemTime.wYear);
626 EmulatorSetRegister(EMULATOR_REG_DX,
627 (Edx & 0xFFFF0000)
628 | (SystemTime.wMonth << 8)
629 | SystemTime.wDay);
630 EmulatorSetRegister(EMULATOR_REG_AX,
631 (Eax & 0xFFFFFF00) | SystemTime.wDayOfWeek);
632 break;
633 }
634
635 /* Set system date */
636 case 0x2B:
637 {
638 GetLocalTime(&SystemTime);
639 SystemTime.wYear = LOWORD(Ecx);
640 SystemTime.wMonth = HIBYTE(Edx);
641 SystemTime.wDay = LOBYTE(Edx);
642
643 if (SetLocalTime(&SystemTime))
644 {
645 /* Return success */
646 EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
647 }
648 else
649 {
650 /* Return failure */
651 EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
652 }
653
654 break;
655 }
656
657 /* Get system time */
658 case 0x2C:
659 {
660 GetLocalTime(&SystemTime);
661 EmulatorSetRegister(EMULATOR_REG_CX,
662 (Ecx & 0xFFFF0000)
663 | (SystemTime.wHour << 8)
664 | SystemTime.wMinute);
665 EmulatorSetRegister(EMULATOR_REG_DX,
666 (Edx & 0xFFFF0000)
667 | (SystemTime.wSecond << 8)
668 | (SystemTime.wMilliseconds / 10));
669 break;
670 }
671
672 /* Set system time */
673 case 0x2D:
674 {
675 GetLocalTime(&SystemTime);
676 SystemTime.wHour = HIBYTE(Ecx);
677 SystemTime.wMinute = LOBYTE(Ecx);
678 SystemTime.wSecond = HIBYTE(Edx);
679 SystemTime.wMilliseconds = LOBYTE(Edx) * 10;
680
681 if (SetLocalTime(&SystemTime))
682 {
683 /* Return success */
684 EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
685 }
686 else
687 {
688 /* Return failure */
689 EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
690 }
691
692 break;
693 }
694
695 /* Create Directory */
696 case 0x39:
697 {
698 String = (PCHAR)((ULONG_PTR)BaseAddress
699 + TO_LINEAR(DataSegment, LOWORD(Edx)));
700
701 if (CreateDirectoryA(String, NULL))
702 {
703 EmulatorClearFlag(EMULATOR_FLAG_CF);
704 }
705 else
706 {
707 EmulatorSetFlag(EMULATOR_FLAG_CF);
708 EmulatorSetRegister(EMULATOR_REG_AX,
709 (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
710 }
711
712 break;
713 }
714
715 /* Remove Directory */
716 case 0x3A:
717 {
718 String = (PCHAR)((ULONG_PTR)BaseAddress
719 + TO_LINEAR(DataSegment, LOWORD(Edx)));
720
721 if (RemoveDirectoryA(String))
722 {
723 EmulatorClearFlag(EMULATOR_FLAG_CF);
724 }
725 else
726 {
727 EmulatorSetFlag(EMULATOR_FLAG_CF);
728 EmulatorSetRegister(EMULATOR_REG_AX,
729 (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
730 }
731
732
733 break;
734 }
735
736 /* Set Current Directory */
737 case 0x3B:
738 {
739 String = (PCHAR)((ULONG_PTR)BaseAddress
740 + TO_LINEAR(DataSegment, LOWORD(Edx)));
741
742 if (SetCurrentDirectoryA(String))
743 {
744 EmulatorClearFlag(EMULATOR_FLAG_CF);
745 }
746 else
747 {
748 EmulatorSetFlag(EMULATOR_FLAG_CF);
749 EmulatorSetRegister(EMULATOR_REG_AX,
750 (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
751 }
752
753 break;
754 }
755
756 /* Allocate Memory */
757 case 0x48:
758 {
759 WORD MaxAvailable = 0;
760 WORD Segment = DosAllocateMemory(LOWORD(Ebx), &MaxAvailable);
761
762 if (Segment != 0)
763 {
764 EmulatorSetRegister(EMULATOR_REG_AX, Segment);
765 EmulatorSetRegister(EMULATOR_REG_BX, MaxAvailable);
766 EmulatorClearFlag(EMULATOR_FLAG_CF);
767 }
768 else EmulatorSetFlag(EMULATOR_FLAG_CF);
769
770 break;
771 }
772
773 /* Free Memory */
774 case 0x49:
775 {
776 if (DosFreeMemory(ExtSegment))
777 {
778 EmulatorClearFlag(EMULATOR_FLAG_CF);
779 }
780 else EmulatorSetFlag(EMULATOR_FLAG_CF);
781
782 break;
783 }
784
785 /* Resize Memory Block */
786 case 0x4A:
787 {
788 WORD Size = DosResizeMemory(ExtSegment, LOWORD(Ebx));
789
790 if (Size != 0)
791 {
792 EmulatorSetRegister(EMULATOR_REG_BX, Size);
793 EmulatorClearFlag(EMULATOR_FLAG_CF);
794 }
795 else EmulatorSetFlag(EMULATOR_FLAG_CF);
796
797 break;
798 }
799
800 /* Terminate With Return Code */
801 case 0x4C:
802 {
803 DosTerminateProcess(CurrentPsp, LOBYTE(Eax));
804 break;
805 }
806
807 /* Unsupported */
808 default:
809 {
810 EmulatorSetFlag(EMULATOR_FLAG_CF);
811 }
812 }
813 }
814
815 VOID DosBreakInterrupt()
816 {
817 VdmRunning = FALSE;
818 }
819
820 BOOLEAN DosInitialize()
821 {
822 PDOS_MCB Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT);
823 FILE *Stream;
824 WCHAR Buffer[256];
825 LPWSTR SourcePtr, Environment;
826 LPSTR AsciiString;
827 LPSTR DestPtr = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(SYSTEM_ENV_BLOCK, 0));
828 DWORD AsciiSize;
829
830 /* Initialize the MCB */
831 Mcb->BlockType = 'Z';
832 Mcb->Size = (WORD)USER_MEMORY_SIZE;
833 Mcb->OwnerPsp = 0;
834
835 /* Get the environment strings */
836 SourcePtr = Environment = GetEnvironmentStringsW();
837 if (Environment == NULL) return FALSE;
838
839 /* Fill the DOS system environment block */
840 while (*SourcePtr)
841 {
842 /* Get the size of the ASCII string */
843 AsciiSize = WideCharToMultiByte(CP_ACP,
844 0,
845 SourcePtr,
846 -1,
847 NULL,
848 0,
849 NULL,
850 NULL);
851
852 /* Allocate memory for the ASCII string */
853 AsciiString = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, AsciiSize);
854 if (AsciiString == NULL)
855 {
856 FreeEnvironmentStringsW(Environment);
857 return FALSE;
858 }
859
860 /* Convert to ASCII */
861 WideCharToMultiByte(CP_ACP,
862 0,
863 SourcePtr,
864 -1,
865 AsciiString,
866 AsciiSize,
867 NULL,
868 NULL);
869
870 /* Copy the string into DOS memory */
871 strcpy(DestPtr, AsciiString);
872
873 /* Free the memory */
874 HeapFree(GetProcessHeap(), 0, AsciiString);
875
876 /* Move to the next string */
877 SourcePtr += wcslen(SourcePtr) + 1;
878 DestPtr += strlen(AsciiString) + 1;
879 }
880
881 /* Free the memory allocated for environment strings */
882 FreeEnvironmentStringsW(Environment);
883
884 /* Read CONFIG.SYS */
885 Stream = _wfopen(DOS_CONFIG_PATH, L"r");
886 if (Stream != NULL)
887 {
888 while (fgetws(Buffer, 256, Stream))
889 {
890 // TODO: Parse the line
891 }
892 fclose(Stream);
893 }
894
895 return TRUE;
896 }
897
898 /* EOF */