xref: /reactos/subsystems/mvdm/dos/command.S (revision 19b18ce2)
1/*
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
6 */
7
8/* INCLUDES *******************************************************************/
9
10#include <asm.inc>
11#include "asmxtras.inc"
12#include <isvbop.inc>
13
14#define NDEBUG
15
16/* DEFINES ********************************************************************/
17
18#define MAX_PATH            260
19#define DOS_CMDLINE_LENGTH  127
20
21#define DOS_VERSION     HEX(0005)   // MAKEWORD(5, 00)
22#define NTDOS_VERSION   HEX(3205)   // MAKEWORD(5, 50)
23
24#define SYSTEM_PSP      HEX(08)
25
26#define BOP                 .byte HEX(C4), HEX(C4),
27// #define BOP_START_DOS       HEX(2C)
28#define BOP_CMD             HEX(54)
29
30/**** PSP MEMBERS ****/
31#define PSP_VAR(x)  es:[x]
32#define PSP         HEX(0000)
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]
37
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)]
41
42
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.
46
47/* NEXT_CMD structure */
48STRUCT(NEXT_CMD, 2)
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)
66ENDS(NEXT_CMD)
67
68/* DOS_EXEC_PARAM_BLOCK structure */
69STRUCT(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)
77ENDS(DOS_EXEC_PARAM_BLOCK)
78
79
80
81/* RESIDENT CODE AND DATA *****************************************************/
82
83.code16
84// .org HEX(0100)
85ASSUME CS:.text, DS:.text, ES:.text
86
87
88/* CODE *******************************/
89
90EntryPoint:
91    jmp Main
92.align 2
93
94ResidentMain:
95    /*
96     * Relocate our stack.
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.
99     *
100     * FIXME: enlarge it for pushing the needed Load&Exec data.
101     */
102    cli
103    mov ax, ds
104    mov ss, ax
105    mov bp, offset BaseStack
106    lea sp, [bp + (1 + DOS_CMDLINE_LENGTH) + MAX_PATH + 255]
107    sti
108
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
114    mov ah, HEX(4A)
115    int HEX(21)
116
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).
120    jz Run
121    cmp word ptr OldParentPsp, SYSTEM_PSP   // Check whether our parent is SYSTEM
122    je Run
123
124#ifndef NDEBUG
125/********************************/
126    mov dx, offset Msg1
127    mov ah, HEX(09)
128    int HEX(21)
129/********************************/
130#endif
131
132    BOP BOP_CMD, HEX(0A)        // Start 32-bit COMSPEC
133    jnc Quit
134
135    /* Loop for new commands to run */
136Run:
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
144
145    /* Wait for the next command */
146#ifndef NDEBUG
147/********************************/
148    mov dx, offset Msg2
149    mov ah, HEX(09)
150    int HEX(21)
151/********************************/
152#endif
153
154    // FIXME: Initialize memory with structure for holding CmdLine etc...
155//  mov ds, seg NextCmd
156    mov dx, offset NextCmd
157    BOP BOP_CMD, HEX(01)
158    /* Quit if we shell-out */
159    jc Quit
160
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
166
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 !!
174    mov ax, HEX(4B00)
175    int HEX(21)
176    popa  // Restore the registers
177    // FIXME: Restore also SS !!
178
179    /* Retrieve and set its exit code. Also detect whether
180     * we need to continue or whether we need to quit. */
181    // xor ax, ax
182    // mov ah, HEX(4D)
183    mov ax, HEX(4D00)
184    int HEX(21)
185    /* Send exit code back to NTVDM */
186    mov dx, ax
187    BOP BOP_CMD, HEX(0B)
188
189    /* If we don't shell-out, go and get a new app! */
190    jc Run
191
192    mov al, HEX(00) // ERROR_SUCCESS
193
194Quit:
195    mov bl, al // Save AL in BL
196
197#ifndef NDEBUG
198/********************************/
199    cmp al, HEX(0A)
200    jne XXXX
201    mov dx, offset Msg3
202    mov ah, HEX(09)
203    int HEX(21)
204XXXX:
205/********************************/
206#endif
207
208#ifndef NDEBUG
209    /* Say bye-bye */
210//  mov ds, seg QuitMsg
211    mov dx, offset QuitMsg
212    mov ah, HEX(09)
213    int HEX(21)
214#endif
215
216    /* Restore our old parent PSP */
217    mov ax, word ptr OldParentPsp
218    mov PSP_VAR(ParentPsp), ax
219
220    mov al, bl // Restore AL from BL
221
222Exit:
223    /* Return to caller (with possible error code in AL) */
224    mov ah, HEX(4C)
225    int HEX(21)
226    int 3
227
228    /* Does not return */
229
230/* DATA *******************************/
231
232#ifndef NDEBUG
233QuitMsg:
234    .ascii "Bye bye!", CR, LF, "$"
235
236/********************************/
237Msg1: .ascii "Starting COMSPEC...", CR, LF, "$"
238Msg2: .ascii "Waiting for new command...", CR, LF, "$"
239Msg3: .ascii "Bad environment!", CR, LF, "$"
240/********************************/
241#endif
242
243OldParentPsp:   .word 0
244CurrentPsp:     .word 0
245
246// BOP_CMD, HEX(01) "Get a new app to start" structure
247VAR_STRUCT(NextCmd, NEXT_CMD)
248
249// DOS INT 21h, AH=4Bh "Load and Execute" structure
250VAR_STRUCT(DosLoadExec, DOS_EXEC_PARAM_BLOCK)
251
252// Blank FCB blocks needed for DOS INT 21h, AH=4Bh
253Fcb1:
254    .byte 0
255    .space 11, ' '
256    .space 25, 0
257Fcb2:
258    .byte 0
259    .space 11, ' '
260    .space 25, 0
261
262// The stack resides at the end of the resident code+data
263// and it overwrites the transient part.
264BaseStack:
265
266
267/* TRANSIENT CODE AND DATA ****************************************************/
268
269/* DATA *******************************/
270
271#ifndef NDEBUG
272WelcomeMsg:
273    .ascii "ReactOS DOS32 Command", CR, LF, \
274           "Copyright (C) ReactOS Team 2015" , CR, LF, "$"
275#endif
276
277VerErrMsg:
278    .ascii "Incorrect DOS version", CR, LF, "$"
279
280/* CODE *******************************/
281
282.align 2
283
284Main:
285    /* Setup segment registers */
286    mov ax, cs  // cs contains the PSP segment on entry
287    mov ds, ax
288    mov es, ax
289//  mov fs, ax
290//  mov gs, ax
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. */
293
294    /*
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).
299     */
300    mov ax, HEX(3000)
301    int HEX(21)
302    cmp ax, DOS_VERSION     // AH:AL contains minor:major version number
303    jne VerErr
304
305    mov ax, HEX(3306)
306    int HEX(21)
307    cmp bx, NTDOS_VERSION   // BH:BL contains minor:major version number
308    je Continue
309
310VerErr:
311    /* Display wrong version error message and exit */
312//  mov ds, seg VerErrMsg
313    mov dx, offset VerErrMsg
314    mov ah, HEX(09)
315    int HEX(21)
316    jmp Exit
317
318Continue:
319    /* Save our PSP */
320    mov word ptr CurrentPsp, cs
321/*
322 * The DOS way:
323 *
324 * mov ah, HEX(51) // DOS 2+ internal, or HEX(62) since DOS 3+
325 * int HEX(21)
326 * mov word ptr CurrentPsp, bx
327 */
328
329    /* Save our old parent PSP */
330    mov ax, PSP_VAR(ParentPsp)
331    mov word ptr OldParentPsp, ax
332
333    /* Give us shell privileges: set our PSP as our new parent */
334    mov ax, word ptr CurrentPsp
335    mov PSP_VAR(ParentPsp), ax
336
337#ifndef NDEBUG
338    /* Say hello */
339//  mov ds, seg WelcomeMsg
340    mov dx, offset WelcomeMsg
341    mov ah, HEX(09)
342    int HEX(21)
343#endif
344
345    /* Jump to resident code */
346    jmp ResidentMain
347
348.endcode16
349END
350
351/* EOF */
352