2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: VDM DOS Kernel
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
15 /* PRIVATE VARIABLES **********************************************************/
17 static WORD CurrentPsp
= SYSTEM_PSP
;
18 static DWORD DiskTransferArea
;
20 /* PRIVATE FUNCTIONS **********************************************************/
22 static VOID
DosCombineFreeBlocks(WORD StartBlock
)
24 PDOS_MCB CurrentMcb
= SEGMENT_TO_MCB(StartBlock
), NextMcb
;
26 /* If this is the last block or it's not free, quit */
27 if (CurrentMcb
->BlockType
== 'Z' || CurrentMcb
->OwnerPsp
!= 0) return;
31 /* Get a pointer to the next MCB */
32 NextMcb
= SEGMENT_TO_MCB(StartBlock
+ CurrentMcb
->Size
+ 1);
34 /* Check if the next MCB is free */
35 if (NextMcb
->OwnerPsp
== 0)
38 CurrentMcb
->Size
+= NextMcb
->Size
+ 1;
39 CurrentMcb
->BlockType
= NextMcb
->BlockType
;
40 NextMcb
->BlockType
= 'I';
44 /* No more adjoining free blocks */
50 static WORD
DosCopyEnvironmentBlock(WORD SourceSegment
)
52 PCHAR Ptr
, SourceBuffer
, DestBuffer
= NULL
;
56 Ptr
= SourceBuffer
= (PCHAR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(SourceSegment
, 0));
58 /* Calculate the size of the environment block */
61 TotalSize
+= strlen(Ptr
) + 1;
62 Ptr
+= strlen(Ptr
) + 1;
66 /* Allocate the memory for the environment block */
67 DestSegment
= DosAllocateMemory((TotalSize
+ 0x0F) >> 4, NULL
);
68 if (!DestSegment
) return 0;
72 DestBuffer
= (PCHAR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(DestSegment
, 0));
76 strcpy(DestBuffer
, Ptr
);
78 /* Advance to the next string */
79 Ptr
+= strlen(Ptr
) + 1;
80 DestBuffer
+= strlen(Ptr
) + 1;
83 /* Set the final zero */
89 static VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
91 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
93 /* Just set the owner */
94 Mcb
->OwnerPsp
= NewOwner
;
97 /* PUBLIC FUNCTIONS ***********************************************************/
99 WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
101 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
102 PDOS_MCB CurrentMcb
, NextMcb
;
106 /* Get a pointer to the MCB */
107 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
109 /* Make sure it's valid */
110 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
115 /* Only check free blocks */
116 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
118 /* Combine this free block with adjoining free blocks */
119 DosCombineFreeBlocks(Segment
);
121 /* Update the maximum block size */
122 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
124 /* Check if this block is big enough */
125 if (CurrentMcb
->Size
< Size
) goto Next
;
127 /* It is, update the smallest found so far */
128 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
134 /* If this was the last MCB in the chain, quit */
135 if (CurrentMcb
->BlockType
== 'Z') break;
137 /* Otherwise, update the segment and continue */
138 Segment
+= CurrentMcb
->Size
+ 1;
141 /* If we didn't find a free block, return 0 */
144 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
148 /* Get a pointer to the MCB */
149 CurrentMcb
= SEGMENT_TO_MCB(Result
);
151 /* Check if the block is larger than requested */
152 if (CurrentMcb
->Size
> Size
)
154 /* It is, split it into two blocks */
155 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
157 /* Initialize the new MCB structure */
158 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
159 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
160 NextMcb
->OwnerPsp
= 0;
162 /* Update the current block */
163 CurrentMcb
->BlockType
= 'M';
164 CurrentMcb
->Size
= Size
;
167 /* Take ownership of the block */
168 CurrentMcb
->OwnerPsp
= CurrentPsp
;
170 /* Return the segment of the data portion of the block */
174 BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
176 BOOLEAN Success
= TRUE
;
177 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
178 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
180 /* Make sure this is a valid, allocated block */
181 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
187 ReturnSize
= Mcb
->Size
;
189 /* Check if we need to expand or contract the block */
190 if (NewSize
> Mcb
->Size
)
192 /* We can't expand the last block */
193 if (Mcb
->BlockType
!= 'M')
199 /* Get the pointer and segment of the next MCB */
200 NextSegment
= Segment
+ Mcb
->Size
+ 1;
201 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
203 /* Make sure the next segment is free */
204 if (NextMcb
->OwnerPsp
!= 0)
210 /* Combine this free block with adjoining free blocks */
211 DosCombineFreeBlocks(NextSegment
);
213 /* Set the maximum possible size of the block */
214 ReturnSize
+= NextMcb
->Size
+ 1;
216 /* Maximize the current block */
217 Mcb
->Size
= ReturnSize
;
218 Mcb
->BlockType
= NextMcb
->BlockType
;
220 /* Invalidate the next block */
221 NextMcb
->BlockType
= 'I';
223 /* Check if the block is larger than requested */
224 if (Mcb
->Size
> NewSize
)
226 /* It is, split it into two blocks */
227 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
229 /* Initialize the new MCB structure */
230 NextMcb
->BlockType
= Mcb
->BlockType
;
231 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
232 NextMcb
->OwnerPsp
= 0;
234 /* Update the current block */
235 Mcb
->BlockType
= 'M';
239 else if (NewSize
< Mcb
->Size
)
241 /* Just split the block */
242 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
243 NextMcb
->BlockType
= Mcb
->BlockType
;
244 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
245 NextMcb
->OwnerPsp
= 0;
248 Mcb
->BlockType
= 'M';
253 /* Check if the operation failed */
256 /* Return the maximum possible size */
257 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
263 BOOLEAN
DosFreeMemory(WORD BlockData
)
265 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
267 /* Make sure the MCB is valid */
268 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') return FALSE
;
270 /* Mark the block as free */
276 WORD
DosCreateFile(LPCSTR FilePath
)
278 // TODO: NOT IMPLEMENTED
282 WORD
DosOpenFile(LPCSTR FilePath
)
284 // TODO: NOT IMPLEMENTED
288 VOID
DosInitializePsp(WORD PspSegment
, LPCSTR CommandLine
, WORD ProgramSize
, WORD Environment
)
291 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(PspSegment
);
292 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
294 ZeroMemory(PspBlock
, sizeof(DOS_PSP
));
296 /* Set the exit interrupt */
297 PspBlock
->Exit
[0] = 0xCD; // int 0x20
298 PspBlock
->Exit
[1] = 0x20;
300 /* Set the program size */
301 PspBlock
->MemSize
= ProgramSize
;
303 /* Save the interrupt vectors */
304 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
305 PspBlock
->BreakAddress
= IntVecTable
[0x23];
306 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
308 /* Set the parent PSP */
309 PspBlock
->ParentPsp
= CurrentPsp
;
311 /* Initialize the handle table */
312 for (i
= 0; i
< 20; i
++) PspBlock
->HandleTable
[i
] = 0xFF;
316 PspBlock
->EnvBlock
= Environment
;
318 /* Set the handle table pointers to the internal handle table */
319 PspBlock
->HandleTableSize
= 20;
320 PspBlock
->HandleTablePtr
= MAKELONG(0x18, PspSegment
);
322 /* Set the DOS version */
323 PspBlock
->DosVersion
= DOS_VERSION
;
325 /* Set the far call opcodes */
326 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
327 PspBlock
->FarCall
[1] = 0x21;
328 PspBlock
->FarCall
[2] = 0xCB; // retf
330 /* Set the command line */
331 PspBlock
->CommandLineSize
= strlen(CommandLine
);
332 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, PspBlock
->CommandLineSize
);
333 PspBlock
->CommandLine
[PspBlock
->CommandLineSize
] = '\r';
336 BOOLEAN
DosCreateProcess(LPCSTR CommandLine
, WORD EnvBlock
)
338 BOOLEAN Success
= FALSE
, AllocatedEnvBlock
= FALSE
;
339 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
340 LPBYTE Address
= NULL
;
341 LPSTR ProgramFilePath
, Parameters
[128];
342 CHAR CommandLineCopy
[128];
344 WORD i
, Segment
= 0, FileSize
, ExeSize
;
345 PIMAGE_DOS_HEADER Header
;
346 PDWORD RelocationTable
;
349 /* Save a copy of the command line */
350 strcpy(CommandLineCopy
, CommandLine
);
352 /* Get the file name of the executable */
353 ProgramFilePath
= strtok(CommandLineCopy
, " \t");
355 /* Load the parameters in the local array */
356 while ((ParamCount
< 256)
357 && ((Parameters
[ParamCount
] = strtok(NULL
, " \t")) != NULL
))
362 /* Open a handle to the executable */
363 FileHandle
= CreateFileA(ProgramFilePath
,
368 FILE_ATTRIBUTE_NORMAL
,
370 if (FileHandle
== INVALID_HANDLE_VALUE
) goto Cleanup
;
372 /* Get the file size */
373 FileSize
= GetFileSize(FileHandle
, NULL
);
375 /* Create a mapping object for the file */
376 FileMapping
= CreateFileMapping(FileHandle
,
382 if (FileMapping
== NULL
) goto Cleanup
;
384 /* Map the file into memory */
385 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
386 if (Address
== NULL
) goto Cleanup
;
388 /* Did we get an environment segment? */
391 /* Set a flag to know if the environment block was allocated here */
392 AllocatedEnvBlock
= TRUE
;
394 /* No, copy the one from the parent */
395 EnvBlock
= DosCopyEnvironmentBlock((CurrentPsp
!= SYSTEM_PSP
)
396 ? SEGMENT_TO_PSP(CurrentPsp
)->EnvBlock
400 /* Check if this is an EXE file or a COM file */
401 if (Address
[0] == 'M' && Address
[1] == 'Z')
405 /* Get the MZ header */
406 Header
= (PIMAGE_DOS_HEADER
)Address
;
408 // TODO: Verify checksum and executable!
410 /* Get the base size of the file, in paragraphs (rounded up) */
411 ExeSize
= (((Header
->e_cp
- 1) << 8) + Header
->e_cblp
+ 0x0F) >> 4;
413 /* Loop from the maximum to the minimum number of extra paragraphs */
414 for (i
= Header
->e_maxalloc
; i
>= Header
->e_minalloc
; i
--)
416 /* Try to allocate that much memory */
417 Segment
= DosAllocateMemory(ExeSize
+ (sizeof(DOS_PSP
) >> 4) + i
, NULL
);
418 if (Segment
!= 0) break;
421 /* Check if at least the lowest allocation was successful */
422 if (Segment
== 0) goto Cleanup
;
424 /* Initialize the PSP */
425 DosInitializePsp(Segment
,
426 CommandLine
, ExeSize
+ (sizeof(DOS_PSP
) >> 4) + i
,
429 /* The process owns its own memory */
430 DosChangeMemoryOwner(Segment
, Segment
);
431 DosChangeMemoryOwner(EnvBlock
, Segment
);
433 /* Copy the program to Segment:0100 */
434 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
435 + TO_LINEAR(Segment
, 0x100)),
436 Address
+ (Header
->e_cparhdr
<< 4),
437 FileSize
- (Header
->e_cparhdr
<< 4));
439 /* Get the relocation table */
440 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
442 /* Perform relocations */
443 for (i
= 0; i
< Header
->e_crlc
; i
++)
445 /* Get a pointer to the word that needs to be patched */
446 RelocWord
= (PWORD
)((ULONG_PTR
)BaseAddress
447 + TO_LINEAR(Segment
+ HIWORD(RelocationTable
[i
]),
448 0x100 + LOWORD(RelocationTable
[i
])));
450 /* Add the number of the EXE segment to it */
451 *RelocWord
+= Segment
+ (sizeof(DOS_PSP
) >> 4);
454 /* Set the initial segment registers */
455 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
456 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
458 /* Set the stack to the location from the header */
459 EmulatorSetStack(Segment
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_ss
,
463 CurrentPsp
= Segment
;
464 DiskTransferArea
= MAKELONG(0x80, Segment
);
465 EmulatorExecute(Segment
+ Header
->e_cs
+ (sizeof(DOS_PSP
) >> 4),
474 /* Allocate memory for the whole program and the PSP */
475 Segment
= DosAllocateMemory((FileSize
+ sizeof(DOS_PSP
)) >> 4, NULL
);
476 if (Segment
== 0) goto Cleanup
;
478 /* Copy the program to Segment:0100 */
479 RtlCopyMemory((PVOID
)((ULONG_PTR
)BaseAddress
480 + TO_LINEAR(Segment
, 0x100)),
484 /* Initialize the PSP */
485 DosInitializePsp(Segment
,
487 (FileSize
+ sizeof(DOS_PSP
)) >> 4,
490 /* Set the initial segment registers */
491 EmulatorSetRegister(EMULATOR_REG_DS
, Segment
);
492 EmulatorSetRegister(EMULATOR_REG_ES
, Segment
);
494 /* Set the stack to the last word of the segment */
495 EmulatorSetStack(Segment
, 0xFFFE);
498 CurrentPsp
= Segment
;
499 DiskTransferArea
= MAKELONG(0x80, Segment
);
500 EmulatorExecute(Segment
, 0x100);
508 /* It was not successful, cleanup the DOS memory */
509 if (AllocatedEnvBlock
) DosFreeMemory(EnvBlock
);
510 if (Segment
) DosFreeMemory(Segment
);
514 if (Address
!= NULL
) UnmapViewOfFile(Address
);
516 /* Close the file mapping object */
517 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
519 /* Close the file handle */
520 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
525 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
)
527 WORD McbSegment
= FIRST_MCB_SEGMENT
;
529 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
530 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
532 /* Check if this PSP is it's own parent */
533 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
535 // TODO: Close all handles opened by the process
537 /* Free the memory used by the process */
540 /* Get a pointer to the MCB */
541 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
543 /* Make sure the MCB is valid */
544 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!='Z') break;
546 /* If this block was allocated by the process, free it */
547 if (CurrentMcb
->OwnerPsp
== Psp
) DosFreeMemory(McbSegment
);
549 /* If this was the last block, quit */
550 if (CurrentMcb
->BlockType
== 'Z') break;
552 /* Update the segment and continue */
553 McbSegment
+= CurrentMcb
->Size
+ 1;
557 /* Restore the interrupt vectors */
558 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
559 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
560 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
562 /* Update the current PSP */
563 if (Psp
== CurrentPsp
)
565 CurrentPsp
= PspBlock
->ParentPsp
;
566 if (CurrentPsp
== SYSTEM_PSP
) VdmRunning
= FALSE
;
569 /* Return control to the parent process */
570 EmulatorExecute(HIWORD(PspBlock
->TerminateAddress
),
571 LOWORD(PspBlock
->TerminateAddress
));
574 CHAR
DosReadCharacter()
576 // TODO: STDIN can be redirected under DOS 2.0+
579 /* A zero value for the character indicates a special key */
580 do Character
= BiosGetCharacter();
586 VOID
DosPrintCharacter(CHAR Character
)
588 // TODO: STDOUT can be redirected under DOS 2.0+
589 if (Character
== '\r') Character
= '\n';
593 VOID
DosInt20h(WORD CodeSegment
)
595 /* This is the exit interrupt */
596 DosTerminateProcess(CodeSegment
, 0);
599 VOID
DosInt21h(WORD CodeSegment
)
603 SYSTEMTIME SystemTime
;
605 PDOS_INPUT_BUFFER InputBuffer
;
606 DWORD Eax
= EmulatorGetRegister(EMULATOR_REG_AX
);
607 DWORD Ecx
= EmulatorGetRegister(EMULATOR_REG_CX
);
608 DWORD Edx
= EmulatorGetRegister(EMULATOR_REG_DX
);
609 DWORD Ebx
= EmulatorGetRegister(EMULATOR_REG_BX
);
610 WORD DataSegment
= EmulatorGetRegister(EMULATOR_REG_DS
);
611 WORD ExtSegment
= EmulatorGetRegister(EMULATOR_REG_ES
);
613 /* Check the value in the AH register */
616 /* Terminate Program */
619 DosTerminateProcess(CodeSegment
, 0);
623 /* Read Character And Echo */
626 Character
= DosReadCharacter();
627 DosPrintCharacter(Character
);
628 EmulatorSetRegister(EMULATOR_REG_AX
, (Eax
& 0xFFFFFF00) | Character
);
632 /* Print Character */
635 DosPrintCharacter(LOBYTE(Edx
));
639 /* Read Character Without Echo */
642 EmulatorSetRegister(EMULATOR_REG_AX
,
643 (Eax
& 0xFFFFFF00) | DosReadCharacter());
650 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
651 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
653 while ((*String
) != '$')
655 DosPrintCharacter(*String
);
662 /* Read Buffered Input */
665 InputBuffer
= (PDOS_INPUT_BUFFER
)((ULONG_PTR
)BaseAddress
666 + TO_LINEAR(DataSegment
,
669 InputBuffer
->Length
= 0;
670 for (i
= 0; i
< InputBuffer
->MaxLength
; i
++)
672 Character
= DosReadCharacter();
673 DosPrintCharacter(Character
);
674 InputBuffer
->Buffer
[InputBuffer
->Length
] = Character
;
675 if (Character
== '\r') break;
676 InputBuffer
->Length
++;
682 /* Set Disk Transfer Area */
685 DiskTransferArea
= MAKELONG(LOWORD(Edx
), DataSegment
);
689 /* Set Interrupt Vector */
692 DWORD FarPointer
= MAKELONG(LOWORD(Edx
), DataSegment
);
694 /* Write the new far pointer to the IDT */
695 ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)] = FarPointer
;
700 /* Get system date */
703 GetLocalTime(&SystemTime
);
704 EmulatorSetRegister(EMULATOR_REG_CX
,
705 (Ecx
& 0xFFFF0000) | SystemTime
.wYear
);
706 EmulatorSetRegister(EMULATOR_REG_DX
,
708 | (SystemTime
.wMonth
<< 8)
710 EmulatorSetRegister(EMULATOR_REG_AX
,
711 (Eax
& 0xFFFFFF00) | SystemTime
.wDayOfWeek
);
715 /* Set system date */
718 GetLocalTime(&SystemTime
);
719 SystemTime
.wYear
= LOWORD(Ecx
);
720 SystemTime
.wMonth
= HIBYTE(Edx
);
721 SystemTime
.wDay
= LOBYTE(Edx
);
723 if (SetLocalTime(&SystemTime
))
726 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
731 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
737 /* Get system time */
740 GetLocalTime(&SystemTime
);
741 EmulatorSetRegister(EMULATOR_REG_CX
,
743 | (SystemTime
.wHour
<< 8)
744 | SystemTime
.wMinute
);
745 EmulatorSetRegister(EMULATOR_REG_DX
,
747 | (SystemTime
.wSecond
<< 8)
748 | (SystemTime
.wMilliseconds
/ 10));
752 /* Set system time */
755 GetLocalTime(&SystemTime
);
756 SystemTime
.wHour
= HIBYTE(Ecx
);
757 SystemTime
.wMinute
= LOBYTE(Ecx
);
758 SystemTime
.wSecond
= HIBYTE(Edx
);
759 SystemTime
.wMilliseconds
= LOBYTE(Edx
) * 10;
761 if (SetLocalTime(&SystemTime
))
764 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
& 0xFFFFFF00);
769 EmulatorSetRegister(EMULATOR_REG_AX
, Eax
| 0xFF);
775 /* Get Disk Transfer Area */
778 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(DiskTransferArea
));
779 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(DiskTransferArea
));
784 /* Get DOS Version */
787 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(CurrentPsp
);
789 EmulatorSetRegister(EMULATOR_REG_AX
, PspBlock
->DosVersion
);
793 /* Get Interrupt Vector */
796 DWORD FarPointer
= ((PDWORD
)BaseAddress
)[LOBYTE(Eax
)];
798 /* Read the address from the IDT into ES:BX */
799 EmulatorSetRegister(EMULATOR_REG_ES
, HIWORD(FarPointer
));
800 EmulatorSetRegister(EMULATOR_REG_BX
, LOWORD(FarPointer
));
805 /* Create Directory */
808 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
809 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
811 if (CreateDirectoryA(String
, NULL
))
813 EmulatorClearFlag(EMULATOR_FLAG_CF
);
817 EmulatorSetFlag(EMULATOR_FLAG_CF
);
818 EmulatorSetRegister(EMULATOR_REG_AX
,
819 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
825 /* Remove Directory */
828 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
829 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
831 if (RemoveDirectoryA(String
))
833 EmulatorClearFlag(EMULATOR_FLAG_CF
);
837 EmulatorSetFlag(EMULATOR_FLAG_CF
);
838 EmulatorSetRegister(EMULATOR_REG_AX
,
839 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
846 /* Set Current Directory */
849 String
= (PCHAR
)((ULONG_PTR
)BaseAddress
850 + TO_LINEAR(DataSegment
, LOWORD(Edx
)));
852 if (SetCurrentDirectoryA(String
))
854 EmulatorClearFlag(EMULATOR_FLAG_CF
);
858 EmulatorSetFlag(EMULATOR_FLAG_CF
);
859 EmulatorSetRegister(EMULATOR_REG_AX
,
860 (Eax
& 0xFFFF0000) | LOWORD(GetLastError()));
866 /* Allocate Memory */
869 WORD MaxAvailable
= 0;
870 WORD Segment
= DosAllocateMemory(LOWORD(Ebx
), &MaxAvailable
);
874 EmulatorSetRegister(EMULATOR_REG_AX
, Segment
);
875 EmulatorSetRegister(EMULATOR_REG_BX
, MaxAvailable
);
876 EmulatorClearFlag(EMULATOR_FLAG_CF
);
878 else EmulatorSetFlag(EMULATOR_FLAG_CF
);
886 if (DosFreeMemory(ExtSegment
))
888 EmulatorClearFlag(EMULATOR_FLAG_CF
);
890 else EmulatorSetFlag(EMULATOR_FLAG_CF
);
895 /* Resize Memory Block */
900 if (DosResizeMemory(ExtSegment
, LOWORD(Ebx
), &Size
))
902 EmulatorClearFlag(EMULATOR_FLAG_CF
);
906 EmulatorSetFlag(EMULATOR_FLAG_CF
);
907 EmulatorSetRegister(EMULATOR_REG_BX
, Size
);
913 /* Terminate With Return Code */
916 DosTerminateProcess(CurrentPsp
, LOBYTE(Eax
));
923 DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax
));
924 EmulatorSetFlag(EMULATOR_FLAG_CF
);
929 VOID
DosBreakInterrupt()
934 BOOLEAN
DosInitialize()
936 PDOS_MCB Mcb
= SEGMENT_TO_MCB(FIRST_MCB_SEGMENT
);
939 LPWSTR SourcePtr
, Environment
;
941 LPSTR DestPtr
= (LPSTR
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(SYSTEM_ENV_BLOCK
, 0));
944 /* Initialize the MCB */
945 Mcb
->BlockType
= 'Z';
946 Mcb
->Size
= (WORD
)USER_MEMORY_SIZE
;
949 /* Get the environment strings */
950 SourcePtr
= Environment
= GetEnvironmentStringsW();
951 if (Environment
== NULL
) return FALSE
;
953 /* Fill the DOS system environment block */
956 /* Get the size of the ASCII string */
957 AsciiSize
= WideCharToMultiByte(CP_ACP
,
966 /* Allocate memory for the ASCII string */
967 AsciiString
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, AsciiSize
);
968 if (AsciiString
== NULL
)
970 FreeEnvironmentStringsW(Environment
);
974 /* Convert to ASCII */
975 WideCharToMultiByte(CP_ACP
,
984 /* Copy the string into DOS memory */
985 strcpy(DestPtr
, AsciiString
);
987 /* Free the memory */
988 HeapFree(GetProcessHeap(), 0, AsciiString
);
990 /* Move to the next string */
991 SourcePtr
+= wcslen(SourcePtr
) + 1;
992 DestPtr
+= strlen(AsciiString
) + 1;
995 /* Free the memory allocated for environment strings */
996 FreeEnvironmentStringsW(Environment
);
998 /* Read CONFIG.SYS */
999 Stream
= _wfopen(DOS_CONFIG_PATH
, L
"r");
1002 while (fgetws(Buffer
, 256, Stream
))
1004 // TODO: Parse the line