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