2 * PROJECT: ReactOS Virtual DOS Machine
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: DOS32 command.com for NTVDM
5 * COPYRIGHT: Copyright 2015-2018 Hermes Belusca-Maito
8 /* INCLUDES *******************************************************************/
11 #include "asmxtras.inc"
16 /* DEFINES ********************************************************************/
19 #define DOS_CMDLINE_LENGTH 127
21 #define DOS_VERSION HEX(0005) // MAKEWORD(5, 00)
22 #define NTDOS_VERSION HEX(3205) // MAKEWORD(5, 50)
24 #define SYSTEM_PSP HEX(08)
26 #define BOP .byte HEX(C4), HEX(C4),
27 // #define BOP_START_DOS HEX(2C)
28 #define BOP_CMD HEX(54)
30 /**** PSP MEMBERS ****/
31 #define PSP_VAR(x) es:[x]
33 #define ParentPsp HEX(0016) // word
34 #define EnvBlock HEX(002C) // word
35 #define CmdLineLen HEX(0080) // byte
36 #define CmdLineStr HEX(0081) // byte[DOS_CMDLINE_LENGTH]
38 /**** DATA stored inside the stack ****/
39 #define CmdLine BaseStack // Command line for the program to be started
40 #define PgmName [CmdLine + (1 + DOS_CMDLINE_LENGTH)]
43 // WARNING! Using the VAL(x) macro doesn't work with GCC/GAS (because it inserts
44 // a spurious space in front of the parameter when the macro is expanded).
45 // So that: 'VAL(foo)' is expanded to: '\ foo', instead of '\foo' as one would expect.
47 /* NEXT_CMD structure */
49 FIELD_DECL(EnvBlockSeg, word, 0)
50 FIELD_DECL(EnvBlockLen, word, 0)
51 FIELD_DECL(CurDrive , word, 0)
52 FIELD_DECL(NumDrives , word, 1)
53 FIELD_DECL(CmdLineSeg , word, 0)
54 FIELD_DECL(CmdLineOff , word, 0)
55 FIELD_DECL(Unknown0 , word, 2)
56 FIELD_DECL(ExitCode , word, 3)
57 FIELD_DECL(Unknown1 , word, 4)
58 FIELD_DECL(Unknown2 , long, 5)
59 FIELD_DECL(CodePage , word, 6)
60 FIELD_DECL(Unknown3 , word, 7)
61 FIELD_DECL(Unknown4 , word, 8)
62 FIELD_DECL(AppNameSeg , word, 0)
63 FIELD_DECL(AppNameOff , word, 0)
64 FIELD_DECL(AppNameLen , word, 0)
65 FIELD_DECL(Flags , word, 0)
68 /* DOS_EXEC_PARAM_BLOCK structure */
69 STRUCT(DOS_EXEC_PARAM_BLOCK, 2)
70 FIELD_DECL(EnvSeg, word, 0) // Use parent's environment (ours)
71 FIELD_DECL(CmdLineOff, word, OFF(CmdLine))
72 FIELD_DECL(CmdLineSeg, word, 0)
73 FIELD_DECL(Fcb1Off, word, OFF(Fcb1))
74 FIELD_DECL(Fcb1Seg, word, 0)
75 FIELD_DECL(Fcb2Off, word, OFF(Fcb2))
76 FIELD_DECL(Fcb2Seg, word, 0)
77 ENDS(DOS_EXEC_PARAM_BLOCK)
81 /* RESIDENT CODE AND DATA *****************************************************/
85 ASSUME CS:.text, DS:.text, ES:.text
88 /* CODE *******************************/
97 * Our local stack is big enough for holding the command line and the path
98 * to the executable to start, plus a full DOS_REGISTER_STATE and for one pusha 16-bit.
100 * FIXME: enlarge it for pushing the needed Load&Exec data.
105 mov bp, offset BaseStack
106 lea sp, [bp + (1 + DOS_CMDLINE_LENGTH) + MAX_PATH + 255]
109 /* Resize ourselves */
110 mov bx, sp // Get size in bytes...
111 // sub bx, offset PSP_VAR(PSP)
112 add bx, HEX(0F) // (for rounding to the next paragraph)
113 shr bx, 4 // ... then the number of paragraphs
117 /* Check whether we need to start the 32-bit command interpreter */
118 BOP BOP_CMD, HEX(10) // Check whether we were started from a new console (SessionId != 0)
119 test al, al // and we are not reentering (32-bit process starting a 16-bit process).
121 cmp word ptr OldParentPsp, SYSTEM_PSP // Check whether our parent is SYSTEM
125 /********************************/
129 /********************************/
132 BOP BOP_CMD, HEX(0A) // Start 32-bit COMSPEC
135 /* Loop for new commands to run */
137 /* Initialize the NextCmd structure */
138 mov word ptr FIELD(NextCmd, EnvBlockSeg), ds
139 mov word ptr FIELD(NextCmd, EnvBlockLen), 0 // FIXME
140 mov word ptr FIELD(NextCmd, CmdLineSeg), ds
141 mov word ptr FIELD(NextCmd, CmdLineOff), offset CmdLine
142 mov word ptr FIELD(NextCmd, AppNameSeg), ds
143 mov word ptr FIELD(NextCmd, AppNameOff), offset PgmName
145 /* Wait for the next command */
147 /********************************/
151 /********************************/
154 // FIXME: Initialize memory with structure for holding CmdLine etc...
155 // mov ds, seg NextCmd
156 mov dx, offset NextCmd
158 /* Quit if we shell-out */
161 /* Initialize the DosLoadExec structure */
162 // mov word ptr FIELD(DosLoadExec, EnvSeg), 0
163 mov word ptr FIELD(DosLoadExec, CmdLineSeg), ds
164 mov word ptr FIELD(DosLoadExec, Fcb1Seg), ds
165 mov word ptr FIELD(DosLoadExec, Fcb2Seg), ds
167 /* Run the command */
168 mov ds, word ptr FIELD(NextCmd, AppNameSeg)
169 mov dx, word ptr FIELD(NextCmd, AppNameOff)
170 // mov es, seg DosLoadExec
171 mov bx, offset DosLoadExec
172 pusha // Save the registers in case stuff go wrong
173 // FIXME: Save also SS !!
176 popa // Restore the registers
177 // FIXME: Restore also SS !!
179 /* Retrieve and set its exit code. Also detect whether
180 * we need to continue or whether we need to quit. */
185 /* Send exit code back to NTVDM */
189 /* If we don't shell-out, go and get a new app! */
192 mov al, HEX(00) // ERROR_SUCCESS
195 mov bl, al // Save AL in BL
198 /********************************/
205 /********************************/
210 // mov ds, seg QuitMsg
211 mov dx, offset QuitMsg
216 /* Restore our old parent PSP */
217 mov ax, word ptr OldParentPsp
218 mov PSP_VAR(ParentPsp), ax
220 mov al, bl // Restore AL from BL
223 /* Return to caller (with possible error code in AL) */
228 /* Does not return */
230 /* DATA *******************************/
234 .ascii "Bye bye!", CR, LF, "$"
236 /********************************/
237 Msg1: .ascii "Starting COMSPEC...", CR, LF, "$"
238 Msg2: .ascii "Waiting for new command...", CR, LF, "$"
239 Msg3: .ascii "Bad environment!", CR, LF, "$"
240 /********************************/
243 OldParentPsp: .word 0
246 // BOP_CMD, HEX(01) "Get a new app to start" structure
247 VAR_STRUCT(NextCmd, NEXT_CMD)
249 // DOS INT 21h, AH=4Bh "Load and Execute" structure
250 VAR_STRUCT(DosLoadExec, DOS_EXEC_PARAM_BLOCK)
252 // Blank FCB blocks needed for DOS INT 21h, AH=4Bh
262 // The stack resides at the end of the resident code+data
263 // and it overwrites the transient part.
267 /* TRANSIENT CODE AND DATA ****************************************************/
269 /* DATA *******************************/
273 .ascii "ReactOS DOS32 Command", CR, LF, \
274 "Copyright (C) ReactOS Team 2015" , CR, LF, "$"
278 .ascii "Incorrect DOS version", CR, LF, "$"
280 /* CODE *******************************/
285 /* Setup segment registers */
286 mov ax, cs // cs contains the PSP segment on entry
291 /* Stack is set to cs:FFFE down to cs:09xx by the DOS.
292 * We will need to relocate it before we resize ourselves. */
295 * Verify DOS version:
296 * - Check whether we are on DOS 5+ (subject to SETVER);
297 * - If so, check our real DOS version and see
298 * whether we are running on NTVDM (version 5.50).
302 cmp ax, DOS_VERSION // AH:AL contains minor:major version number
307 cmp bx, NTDOS_VERSION // BH:BL contains minor:major version number
311 /* Display wrong version error message and exit */
312 // mov ds, seg VerErrMsg
313 mov dx, offset VerErrMsg
320 mov word ptr CurrentPsp, cs
324 * mov ah, HEX(51) // DOS 2+ internal, or HEX(62) since DOS 3+
326 * mov word ptr CurrentPsp, bx
329 /* Save our old parent PSP */
330 mov ax, PSP_VAR(ParentPsp)
331 mov word ptr OldParentPsp, ax
333 /* Give us shell privileges: set our PSP as our new parent */
334 mov ax, word ptr CurrentPsp
335 mov PSP_VAR(ParentPsp), ax
339 // mov ds, seg WelcomeMsg
340 mov dx, offset WelcomeMsg
345 /* Jump to resident code */