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