Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / subsystems / mvdm / dos / command.S
diff --git a/subsystems/mvdm/dos/command.S b/subsystems/mvdm/dos/command.S
new file mode 100644 (file)
index 0000000..2aa6b53
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * 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 <asm.inc>
+#include "asmxtras.inc"
+#include <isvbop.inc>
+
+#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 */