/* * COPYRIGHT: GPL - See COPYING in the top level directory * PROJECT: ReactOS Virtual DOS Machine * FILE: command.S * PURPOSE: DOS32 command.com for NTVDM * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) */ // // See http://www.ganino.com/games/scripts/dos-6.0%20source%20code%3F/dev/smartdrv/umbload.asm // and https://books.google.fr/books?id=rtmJgtfaxz8C&pg=PA256&lpg=PA256&dq=int+21+4b+program+exec+block&source=bl&ots=OfAF7qFwfl&sig=PrW73CE1dzFm3rwrTsYZkC77U4Y&hl=en&sa=X&redir_esc=y#v=onepage&q=int%2021%204b%20program%20exec%20block&f=false // /* INCLUDES *******************************************************************/ #include #include "asmxtras.inc" #include #define NDEBUG /* DEFINES ********************************************************************/ #define MAX_PATH 260 #define DOS_CMDLINE_LENGTH 127 #define DOS_VERSION HEX(0005) // MAKEWORD(5, 00) #define NTDOS_VERSION HEX(3205) // MAKEWORD(5, 50) #define SYSTEM_PSP HEX(08) #define BOP .byte HEX(C4), HEX(C4), // #define BOP_START_DOS HEX(2C) #define BOP_CMD HEX(54) /**** PSP MEMBERS ****/ #define PSP_VAR(x) es:[x] #define PSP HEX(0000) #define ParentPsp HEX(0016) // word #define EnvBlock HEX(002C) // word #define CmdLineLen HEX(0080) // byte #define CmdLineStr HEX(0081) // byte[DOS_CMDLINE_LENGTH] /**** DATA stored inside the stack ****/ #define CmdLine BaseStack // Command line for the program to be started #define PgmName [CmdLine + (1 + DOS_CMDLINE_LENGTH)] // WARNING! Using the VAL(x) macro doesn't work with GCC/GAS (because it inserts // a spurious space in front of the parameter when the macro is expanded). // So that: 'VAL(foo)' is expanded to: '\ foo', instead of '\foo' as one would expect. /* NEXT_CMD structure */ STRUCT(NEXT_CMD, 2) FIELD_DECL(EnvBlockSeg, word, 0) FIELD_DECL(EnvBlockLen, word, 0) FIELD_DECL(CurDrive , word, 0) FIELD_DECL(NumDrives , word, 1) FIELD_DECL(CmdLineSeg , word, 0) FIELD_DECL(CmdLineOff , word, 0) FIELD_DECL(Unknown0 , word, 2) FIELD_DECL(ExitCode , word, 3) FIELD_DECL(Unknown1 , word, 4) FIELD_DECL(Unknown2 , long, 5) FIELD_DECL(CodePage , word, 6) FIELD_DECL(Unknown3 , word, 7) FIELD_DECL(Unknown4 , word, 8) FIELD_DECL(AppNameSeg , word, 0) FIELD_DECL(AppNameOff , word, 0) FIELD_DECL(AppNameLen , word, 0) FIELD_DECL(Flags , word, 0) ENDS(NEXT_CMD) /* DOS_EXEC_PARAM_BLOCK structure */ STRUCT(DOS_EXEC_PARAM_BLOCK, 2) FIELD_DECL(EnvSeg, word, 0) // Use parent's environment (ours) FIELD_DECL(CmdLineOff, word, OFF(CmdLine)) FIELD_DECL(CmdLineSeg, word, 0) FIELD_DECL(Fcb1Off, word, OFF(Fcb1)) FIELD_DECL(Fcb1Seg, word, 0) FIELD_DECL(Fcb2Off, word, OFF(Fcb2)) FIELD_DECL(Fcb2Seg, word, 0) ENDS(DOS_EXEC_PARAM_BLOCK) /* RESIDENT CODE AND DATA *****************************************************/ .code16 // .org HEX(0100) ASSUME CS:.text, DS:.text, ES:.text /* CODE *******************************/ EntryPoint: jmp Main .align 2 ResidentMain: /* * Relocate our stack. * Our local stack is big enough for holding the command line and the path * to the executable to start, plus a full DOS_REGISTER_STATE and for one pusha 16-bit. * * FIXME: enlarge it for pushing the needed Load&Exec data. */ cli mov ax, ds mov ss, ax mov bp, offset BaseStack lea sp, [bp + (1 + DOS_CMDLINE_LENGTH) + MAX_PATH + 255] sti /* Resize ourselves */ mov bx, sp // Get size in bytes... // sub bx, offset PSP_VAR(PSP) add bx, HEX(0F) // (for rounding to the next paragraph) shr bx, 4 // ... then the number of paragraphs mov ah, HEX(4A) int HEX(21) /* Check whether we need to start the 32-bit command interpreter */ BOP BOP_CMD, HEX(10) // Check whether we were started from a new console (SessionId != 0) test al, al // and we are not reentering (32-bit process starting a 16-bit process). jz Run cmp word ptr OldParentPsp, SYSTEM_PSP // Check whether our parent is SYSTEM je Run #ifndef NDEBUG /********************************/ mov dx, offset Msg1 mov ah, HEX(09) int HEX(21) /********************************/ #endif BOP BOP_CMD, HEX(0A) // Start 32-bit COMSPEC jnc Quit /* Loop for new commands to run */ Run: /* Initialize the NextCmd structure */ mov word ptr FIELD(NextCmd, EnvBlockSeg), ds mov word ptr FIELD(NextCmd, EnvBlockLen), 0 // FIXME mov word ptr FIELD(NextCmd, CmdLineSeg), ds mov word ptr FIELD(NextCmd, CmdLineOff), offset CmdLine mov word ptr FIELD(NextCmd, AppNameSeg), ds mov word ptr FIELD(NextCmd, AppNameOff), offset PgmName /* Wait for the next command */ #ifndef NDEBUG /********************************/ mov dx, offset Msg2 mov ah, HEX(09) int HEX(21) /********************************/ #endif // FIXME: Initialize memory with structure for holding CmdLine etc... // mov ds, seg NextCmd mov dx, offset NextCmd BOP BOP_CMD, HEX(01) /* Quit if we shell-out */ jc Quit /* Initialize the DosLoadExec structure */ // mov word ptr FIELD(DosLoadExec, EnvSeg), 0 mov word ptr FIELD(DosLoadExec, CmdLineSeg), ds mov word ptr FIELD(DosLoadExec, Fcb1Seg), ds mov word ptr FIELD(DosLoadExec, Fcb2Seg), ds /* Run the command */ mov ds, word ptr FIELD(NextCmd, AppNameSeg) mov dx, word ptr FIELD(NextCmd, AppNameOff) // mov es, seg DosLoadExec mov bx, offset DosLoadExec pusha // Save the registers in case stuff go wrong // FIXME: Save also SS !! mov ax, HEX(4B00) int HEX(21) popa // Restore the registers // FIXME: Restore also SS !! /* Retrieve and set its exit code. Also detect whether * we need to continue or whether we need to quit. */ // xor ax, ax // mov ah, HEX(4D) mov ax, HEX(4D00) int HEX(21) /* Send exit code back to NTVDM */ mov dx, ax BOP BOP_CMD, HEX(0B) /* If we don't shell-out, go and get a new app! */ jc Run mov al, HEX(00) // ERROR_SUCCESS Quit: mov bl, al // Save AL in BL #ifndef NDEBUG /********************************/ cmp al, HEX(0A) jne XXXX mov dx, offset Msg3 mov ah, HEX(09) int HEX(21) XXXX: /********************************/ #endif #ifndef NDEBUG /* Say bye-bye */ // mov ds, seg QuitMsg mov dx, offset QuitMsg mov ah, HEX(09) int HEX(21) #endif /* Restore our old parent PSP */ mov ax, word ptr OldParentPsp mov PSP_VAR(ParentPsp), ax mov al, bl // Restore AL from BL Exit: /* Return to caller (with possible error code in AL) */ mov ah, HEX(4C) int HEX(21) int 3 /* Does not return */ /* DATA *******************************/ #ifndef NDEBUG QuitMsg: .ascii "Bye bye!", CR, LF, "$" /********************************/ Msg1: .ascii "Starting COMSPEC...", CR, LF, "$" Msg2: .ascii "Waiting for new command...", CR, LF, "$" Msg3: .ascii "Bad environment!", CR, LF, "$" /********************************/ #endif OldParentPsp: .word 0 CurrentPsp: .word 0 // BOP_CMD, HEX(01) "Get a new app to start" structure VAR_STRUCT(NextCmd, NEXT_CMD) // DOS INT 21h, AH=4Bh "Load and Execute" structure VAR_STRUCT(DosLoadExec, DOS_EXEC_PARAM_BLOCK) // Blank FCB blocks needed for DOS INT 21h, AH=4Bh Fcb1: .byte 0 .space 11, ' ' .space 25, 0 Fcb2: .byte 0 .space 11, ' ' .space 25, 0 // The stack resides at the end of the resident code+data // and it overwrites the transient part. BaseStack: /* TRANSIENT CODE AND DATA ****************************************************/ /* DATA *******************************/ #ifndef NDEBUG WelcomeMsg: .ascii "ReactOS DOS32 Command", CR, LF, \ "Copyright (C) ReactOS Team 2015" , CR, LF, "$" #endif VerErrMsg: .ascii "Incorrect DOS version", CR, LF, "$" /* CODE *******************************/ .align 2 Main: /* Setup segment registers */ mov ax, cs // cs contains the PSP segment on entry mov ds, ax mov es, ax // mov fs, ax // mov gs, ax /* Stack is set to cs:FFFE down to cs:09xx by the DOS. * We will need to relocate it before we resize ourselves. */ /* * Verify DOS version: * - Check whether we are on DOS 5+ (subject to SETVER); * - If so, check our real DOS version and see * whether we are running on NTVDM (version 5.50). */ mov ax, HEX(3000) int HEX(21) cmp ax, DOS_VERSION // AH:AL contains minor:major version number jne VerErr mov ax, HEX(3306) int HEX(21) cmp bx, NTDOS_VERSION // BH:BL contains minor:major version number je Continue VerErr: /* Display wrong version error message and exit */ // mov ds, seg VerErrMsg mov dx, offset VerErrMsg mov ah, HEX(09) int HEX(21) jmp Exit Continue: /* Save our PSP */ mov word ptr CurrentPsp, cs /* * The DOS way: * * mov ah, HEX(51) // DOS 2+ internal, or HEX(62) since DOS 3+ * int HEX(21) * mov word ptr CurrentPsp, bx */ /* Save our old parent PSP */ mov ax, PSP_VAR(ParentPsp) mov word ptr OldParentPsp, ax /* Give us shell privileges: set our PSP as our new parent */ mov ax, word ptr CurrentPsp mov PSP_VAR(ParentPsp), ax #ifndef NDEBUG /* Say hello */ // mov ds, seg WelcomeMsg mov dx, offset WelcomeMsg mov ah, HEX(09) int HEX(21) #endif /* Jump to resident code */ jmp ResidentMain .endcode16 END /* EOF */