mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
351 lines
9.1 KiB
ArmAsm
351 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 */
|