reactos/subsystems/mvdm/dos/command.S
2018-03-25 16:19:29 +02:00

352 lines
9.1 KiB
ArmAsm

/*
* PROJECT: ReactOS Virtual DOS Machine
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* PURPOSE: DOS32 command.com for NTVDM
* COPYRIGHT: Copyright 2015-2018 Hermes Belusca-Maito
*/
/* 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 */