Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2695 lines
75 KiB

page ,132
title COMMAND Initialization
;/*
; * Microsoft Confidential
; * Copyright (C) Microsoft Corporation 1991
; * All Rights Reserved.
; */
;
; Revision History
; ================
; M002 SR 07/15/90 Resize right at the start because
; Carousel depends on it.
;
; M004 SR 07/17/90 Initialization reworked so that
; transient is now moved at EndInit.
; The old approach assumed that the
; biggest block is the one currently
; loaded in, an assumption not true
; for UMBs.
;
; M005 SR 07/20/90 Numerous hacks for Carousel
; 1. Set CurrentPDB to ours at start
; 2. Normalize cs:ip of int 2fh hook
; so that cs is different.
;
; M013 SR 08/06/90 Fixed Setup_res_end & Move_res_code
; to use new GetVersion call that
; returns info about whether DOS is in
; HMA or not.
;
; M015 SR 08/09/90 Increased default environment size to
; 256 bytes from 160 bytes
;
; M026 SR 9/12/90 Fixed environment trashing on second
; Command if new comspec is given.
;
; M030 SR 10/3/90 Before calling int 2fh 4a02h, set di
; to 0ffffh so that we are ok if no one
; answers this int 2fh.
;
; M042 SR 12/13/90 Bug #4660. Changed setup_res_end to
; take care of the dummy segment that
; adds a para to the resident size.
;
.xlist
.xcref
include comsw.asm
include dossym.inc
include pdb.inc
include mult.inc
include syscall.inc
include doscntry.inc
include comseg.asm
include comequ.asm
include resmsg.equ
include envdata.asm
include xmm.inc
include cmdsvc.inc
include dossvc.inc
.list
.cref
ENVBIG equ 32768
ENVSML equ 256 ;Increased to 256 ; M015
KOREA_COUNTRY_CODE equ 82
CODERES segment public byte
extrn ContC :near
extrn DskErr :near
extrn Int_2e :near
extrn LodCom :near
extrn MsgInt2fHandler :far
extrn SetVect :near
extrn ChkSum :near
extrn CrLf :near
extrn LoadCom :near
extrn RPrint :near
ifdef BILINGUAL
extrn RPrint@ :near
endif ; BILINGUAL
extrn EndCode :byte
extrn StartCode :byte
ifdef DBCS
extrn ItestKanj :near
endif
extrn BadMemErr :near
CODERES ends
DATARES segment public byte
extrn Abort_Char :byte
extrn AccDen :byte
extrn Append_State :word
extrn Batch :word
extrn Com_Fcb1 :dword
extrn Com_Fcb2 :dword
extrn Com_Ptr :dword
extrn ComDrv :byte
extrn ComSpec :byte
extrn ComSpec_End :word
extrn Crit_Msg_Off :word
extrn Crit_Msg_Seg :word
extrn DataResEnd :byte
extrn Dbcs_Vector_Addr :word
extrn EchoFlag :byte
extrn EnvirSeg :word
extrn ExtMsgEnd :byte
extrn fFail :byte
extrn FUCase_addr :word
extrn InitFlag :byte
extrn Int2fHandler :dword
extrn Io_Save :word
extrn Io_Stderr :byte
extrn LTpa :word
extrn MemSiz :word
extrn MySeg :word
extrn MySeg1 :word
extrn MySeg2 :word
extrn MySeg3 :word
extrn Nest :word
extrn OldTerm :dword
extrn Parent :word
extrn ParseMes_Ptr :word
extrn ParsMsgPtrs :word
extrn PermCom :byte
extrn PutBackDrv :byte
extrn PutBackComSpec :byte
extrn RDirChar :byte
extrn Res_Tpa :word
extrn ResMsgEnd :word
extrn RSwitChar :byte
extrn SingleCom :word
extrn KSwitchFlag :byte
extrn Sum :word
extrn TrnSeg :word
extrn TrnMvFlg :byte
extrn ResSize :word
extrn RStack :word
extrn ComInHMA :byte ;flag set if in HMA
extrn XMMCallAddr :dword ;far call address to XMM
EXTRN SCS_CMDPROMPT :BYTE
EXTRN SCS_DOSONLY :BYTE
;
;All far pointers to resident routines that are to be patched
;
extrn Int2f_Entry :dword
extrn Int2e_Entry :dword
extrn Ctrlc_Entry :dword
extrn CritErr_Entry :dword
extrn Int2f_Trap :near
extrn Int2e_Trap :near
extrn Ctrlc_Trap :near
extrn CritErr_Trap :near
extrn LodCom_Trap :near
EXTRN SCS_PAUSE:BYTE ; yst
extrn EndInit :near
extrn Carousel_i2f_Hook :byte ; M005
extrn SCS_REENTERED :byte
extrn SCS_FIRSTCOM :byte
ifdef BETA3WARN
%out Take this out before we ship
extrn Beta3Warned:byte
endif
DATARES ends
;;ENVIRONMENT segment public para ; default COMMAND environment
;;
;; extrn EComSpec :byte
;; extrn EnvirEnd :byte
;; extrn PathString :byte
;;
;;ENVIRONMENT ends
TAIL segment public para
extrn TranStart :word
TAIL ends
TRANCODE segment public byte
extrn DatInit :far
TRANCODE ends
TRANDATA segment
extrn TranDataEnd :byte
TRANDATA ends
TRANSPACE segment public byte
extrn TranSpaceEnd :byte
TRANSPACE ends
; *******************************************************************
; START OF INIT PORTION
; This code is deallocated after initialization.
INIT SEGMENT PUBLIC PARA
;NTVDM not used extrn AutoBat :byte
extrn BadComAccMsg :byte
extrn BadComLkMsg :byte
extrn Badcspfl :byte
extrn BadVerMsg :byte
extrn Chuckenv :byte
extrn Command_?_syn :byte
extrn Command_c_syn :byte
extrn Command_d_syn :byte
extrn Command_e_syn :byte
extrn Command_f_syn :byte
extrn Command_k_syn :byte
extrn Command_l_syn :byte
extrn Command_l2_syn :byte
extrn Command_m_syn :byte
extrn Command_u_syn :byte
extrn Command_p_syn :byte
extrn Command_y_syn :byte
extrn Command_z_syn :byte
extrn Comnd1_syn :word
extrn Comnd1_addr :dword
extrn ComSpect :byte
extrn ComspString :byte
extrn CopyrightMsg :byte
extrn Dswitch :byte
;; extrn Ecomloc :word
extrn EnvMax :word
extrn EnvSiz :word
extrn EqualSign :byte
extrn Eswitch :byte
extrn Ext_msg :byte
extrn HelpMsgs:word
extrn InitAdd :dword
extrn InitEnd :word
extrn Init_Parse :dword
extrn Internat_Info :byte
; NTVDM not used extrn KautoBat :byte
extrn Lcasea :byte
extrn Lcasez :byte
extrn Num_positionals :word
extrn OldEnv :word
extrn Old_parse_ptr :word
extrn OutEnvMsg :byte
extrn Parse_command :byte
extrn PrdAttm :byte
extrn ResetEnv :word
extrn Scswitch :byte
extrn Skswitch :byte
extrn Space :byte
extrn Triage_Add :dword
extrn TrnSize :word
extrn Ucasea :byte
extrn UsedEnv :word
extrn PathString :byte
extrn ComspString :byte
extrn Reloc_Table :word
extrn FirstCom :byte
extrn ResJmpTable :dword
extrn TriageError :near
extrn NUM_RELOC_ENTRIES :abs
extrn DevFlag :byte
extrn PathFlag :byte
PUBLIC ConProc
PUBLIC Init_ContC_SpecialCase
assume cs:ResGroup,ds:ResGroup,es:ResGroup,ss:ResGroup
org 0
ZERO = $
ConProc:
mov sp,offset ResGroup:RStack ; must be first instruction
;
; M005; We need to set the PSP to us right at start because Carousel needs
; M005; to be lied to and it does not set PSP when it transfers control to
; M005; us after loading us as an overlay. By setting PSP, we ensure that
; M005; command.com is also not lied to.
;
mov ah,SET_CURRENT_PDB ; M005
mov bx,es ; M005
int 21h ; M005
mov ah,GET_VERSION
int 21h
cmp ax,EXPECTED_VERSION
je OkDos ; DOS version is ok
mov dx,offset ResGroup:BadVerMsg ; DX = ptr to msg
call RPrint
mov ax,es
cmp es:PDB_Parent_Pid,ax ; if COMMAND is own parent,
Here: jnz GoExit ; loop forever
SVC SVC_DEMEXITVDM
GoExit:
int 20h ; otherwise, exit
okdos:
;
; Calculate and save the end of the INIT segment (which is also
; the beginning of TRANGROUP).
;
mov dx,offset resgroup:TranStart+15 ;eg get end of init code
mov cl,4 ;eg change to paragraphs
shr dx,cl ;eg
mov ax,cs ;eg get current segment
add ax,dx ;eg calculate segment of end of init
mov InitEnd,ax ;eg save this
SAVE <SI,BP>
xor si,si
xor bp,bp
mov al,5 ; query cmdprompt
mov ah,setdpb
int 21h
mov byte ptr [scs_cmdprompt],al ; ds is resseg
mov al,7 ; query DOSONLY bit
mov ah,setdpb
int 21h
mov byte ptr [scs_dosonly],al ; ds is resseg
RESTORE <BP,SI>
;
; Check for /? on the command line. If found, display help text
; and exit.
;
; NOTE: this routine may terminate the program, never returning.
;
call CheckHelp
call CheckZSwitch
;SR;
; We have to patch the segment values for the various interrupt entry points.
;This is because we need to have the default addresses of the handlers in our
;stub before the relocation is done. These values will then be changed once
;the resident is relocated
;
call patch_segs
;
; Turn APPEND off during initialization processing
;
mov ax,APPENDINSTALL ; see if append installed
int 2fh ;
cmp al,0 ; append installed?
je set_msg_addr ; no - continue
mov ax,APPENDDOS ; see if append DOS version right
int 2fh ;
cmp ax,-1 ; append version correct?
jne set_msg_addr ; no - continue
mov ax,APPENDGETSTATE ; Get the state of Append
int 2fh ;
mov Append_State,bx ; save append state
xor bx,bx ; clear out state
mov ax,APPENDSETSTATE ; Set the state of Append
int 2fh ; set everything off
set_msg_addr:
mov di,offset resgroup:DataresEnd ; get address of resident end
mov ResMsgEnd,di ; save it
call get_XMMAddr ;get XMM call address
;
;Check if this is the first instance of command.com. If not, we just exit
;this routine without moving any code.
;After the int 2fh, ds:si points at the resident jump table in the previous
;stub. We just have to copy this over
;
mov ax,GET_COMMAND_STATE
int 2fh
assume ds:nothing
or ax,ax
jnz first_com ;this is the first instance
ifdef BETA3WARN
%out Take this out before we ship
mov es:Beta3Warned, 0ffh
endif
mov word ptr es:ResJmpTable,si ;save old stub jump table
mov word ptr es:ResJmpTable+2,ds
jmp short init_cntry
first_com:
mov es:FirstCom,1 ;indicate first command.com
mov SCS_FIRSTCOM,1
init_cntry:
push es
pop ds
assume ds:RESGROUP
mov ah,GETEXTCNTRY ; get extended country info
mov al,4 ; get file ucase table
mov dx,-1 ;
mov bx,-1 ;
mov cx,5 ; number of bytes we want
mov di,offset resgroup:Fucase_addr ; buffer for address
int 21h ;
; Bugbug: conditionalize dbcs_vector stuff?
push ds ;
mov ax, (ECS_CALL shl 8) or GETLEADBTBL ;
int 21h ;
mov bx,ds ; get segment to bx
pop ds ;
mov Dbcs_vector_addr,si ; save address of
mov Dbcs_vector_addr+2,bx ; dbcs vector
mov ax,word ptr ds:PDB_Parent_Pid ; Init PARENT so we can exit
mov Parent,ax ; correctly.
mov ax,word ptr ds:Pdb_Exit
mov word ptr OldTerm,ax
mov ax,word ptr ds:Pdb_Exit+2
mov word ptr Oldterm+2,ax
mov ax,offset ResGroup:EndCode + 15
mov cl,4 ; ax = size of resident part of
shr ax,cl ; command in paragraphs. Add
mov cx,cs ; this to CS and you get the
add ax,cx ; segment of the TPA.
mov Res_tpa, ax ; Temporarily save the TPA segment
and ax, 0f000h
add ax, 01000h ; Round up to next 64K boundary
jnc TpaSet ; Memory wrap if carry set
mov ax, Res_tpa
TpaSet:
mov Ltpa,ax ; Good enough for the moment
mov ax,word ptr ds:PDB_Block_Len ; ax = # of paras given to command
mov Myseg1,ds ; These 3 variables are used as part of
mov Myseg2,ds ; 3 long ptrs that the transient will
mov Myseg,ds ; use to call resident routines.
mov Myseg3,ds ; segment of msg retriever routine
mov Memsiz,ax ; Needed for execing other programs
; M002;
; M002; First reallocate the COMMAND size to its memory image
; M002;
push ax ; M002
mov bx,offset RESGROUP:TranStart ; M002
add bx,offset TRANGROUP:TranSpaceEnd; M002
add bx,15 ; M002; round up the size
mov cl,4 ; M002
shr bx,cl ;size of command.com ; M002
mov ah,SETBLOCK ;free all memory above pgm ; M002
int 21h ; M002
pop ax ; M002
;
; Compute maximum size of environment
;
mov EnvMax,(Environsiz + 15) / 16 + (EnvMaximum-zero + 15)/16 - 1
;
; Compute minimum size of environment
;
mov EnvSiz, ENVSML / 16
mov dx,offset TranGroup:Transpaceend + 15 ; dx = size of transient
mov cl,4 ; in paragraphs.
shr dx,cl
mov Trnsize,dx ;eg save size of transient in paragraphs
sub ax,dx ; max seg addr - # para's needed for transient
mov Trnseg,ax ; = seg addr to load the transient at.
mov ax,ds:PDB_Environ ; ax = environment segment
or ax,ax ; if there is no environment segment,
jz buildenv ; go compute one.
inc byte ptr Chuckenv ; flag no new environseg to set up
jmp short environpassed ; otherwise one was passed to us.
buildenv: ; (this label isn't very accurate)
;
;We allocate a buffer here just large enough to hold the 'PATH=' and
;the COMSPEC. After parsing, we will allocate an environment of the right
;size and free this buffer. We need this buffer because we no longer have an
;ENVIRONMENT segment but need a place to store the COMSPEC which can be
;given on the command line before we know the environment size. This routine
;will not return in case of an allocation error. It will either exit or hang
;depending on whether or not this is the first COMMAND.COM or not.
;
call alloc_env ;try to allocate buffer
;; mov ax,offset ResGroup:PathString ; compute the segment of the
;; mov cl,4 ; environment and put it in
;; shr ax,cl ; ax.
;; mov dx,ds
;; add ax,dx
environpassed:
mov Envirseg,ax ; save the environment's segment and
mov es,ax ; load into es.
assume es:nothing
gottheenvir:
;
; initialize the command drive
;
mov ah,GET_DEFAULT_DRIVE
int 21h
inc al
mov Comdrv,al
mov al,byte ptr ds:Fcb ; al = default drive number for command
or al,al
jz nocomdrv ; no drive specified
mov ah,':'
mov Comdrv,al
add al,40h ; convert number to uppercase character
std
cmp byte ptr Chuckenv,0 ; if a new environment is being built,
jnz notwidenv ; move the default comspec string in it
push ds ; 2 bytes to make room for a drivespec.
push es ; the drivespec is in ax and is copied
pop ds ; on to the front of the string.
mov di,Env_Ecomspec + ENVIRONSIZ2 - 1 ;eg
mov si,Env_Ecomspec + ENVIRONSIZ2 - 3 ;eg
mov cx,ENVIRONSIZ2 - 2
rep movsb
pop ds
mov word ptr es:Env_Ecomspec,ax
notwidenv:
cld ; add the drivespec to the string
; NTVDM not used
; mov word ptr AutoBat,ax ; used to reference autoexec.bat
; mov word ptr KautoBat,ax ; used to reference kautoexe.bat 3/3/kk
nocomdrv:
call setvect ; set interrupt vectors 22h, 23h, & 24h
;*********************************
; parsing starts here
;*********************************
push cs ; get local segment
push cs ; into ds,es
pop ds ;
pop es ;
assume ds:ResGroup,es:ResGroup ;
mov si,80h ; get command line
lodsb ; get length of line
mov di,si ; get line position in di
xor ah,ah ; ax = length of command line
;
; insure that the command line correctly ends with a cr
;
add di,ax ; go to end of command line
mov byte ptr [di],0dh ; insert a carriage return
xor cx,cx ; clear cx
mov Num_positionals,cx ; initialize positionals
;
; Scan the command line looking for the parameters
;
Parse_command_line:
mov di,offset ResGroup:Parse_Command ; Get address of parse_command
mov cx,Num_positionals ; Get number of positionals
xor dx,dx ; clear dx
mov Old_parse_ptr,si ; save position before calling parser
call init_parse ; call parser
mov Num_positionals,cx ; Save number of positionals
cmp ax,END_OF_LINE ; are we at end of line?
jnz SkipArgdsDoneJmp
jmp ArgsDoneJ3 ; yes - exit
SkipArgdsDoneJmp:
cmp ax,RESULT_NO_ERROR ; did an error occur
jz parse_cont ; no - continue
;
; Before issuing error message - make sure switch is not /C or /K
;
parse_line_error:
push si ; save line position
push ax ; save error number
cmp ax,BADSWT_PTR ; Was error invalid switch?
jnz parse_line_error_disp ; No - just issue message
mov di,si ; Get terminating pointer in DI
mov si,Old_parse_ptr ; Get starting pointer in SI
init_chk_delim:
cmp si,di ; at end of parsed parameter?
jz parse_line_error_disp ; Yes - just display message
lodsb ;
cmp al,Space ; Skip blank spaces
jz init_chk_delim ;
cmp al,TAB_CHR ; Skip tab characters
jz init_chk_delim ;
cmp al,Rswitchar ; Switch?
jnz parse_line_error_disp ; No - just issue message
lodsb ; Get the char after the switch
ifdef DBCS
call ItestKanj ; Is it DBCS?
jnz parse_line_error_disp ; Yes - can't be /C or /K
endif
call iupconv ; upper case it
cmp al,Scswitch ; it is /C?
jz AltSetCSwitch
cmp al,Skswitch ; it is /K?
jz AltSetKSwitch
jmp parse_line_error_disp ;
AltSetCSwitch:
pop dx ; even up stack
pop dx ; even up stack
jmp setSSwitch ; Yes - go set COMMAND /C
AltSetKSwitch:
pop dx ; even up stack
pop dx ; even up stack
jmp setKSwitch ; Yes - go set COMMAND /K
parse_line_error_disp:
pop ax ; restore error number
pop si ; restore line position
mov dx,ax ; get message number
call RPrintParse
call CrLf
jmp short Parse_command_line ; continue parsing
parse_cont:
;
; See if a switch was entered
;
; Bugbug: See if Comnd1_Syn can be moved into a reg. before the compare
;
; Several stub switches have been added below in order to provide improved
; compatibility with applications using unsupported switches in command.com
; The stubs generally do not do anything, but, they keep command.com
; from erroring out. In many cases, this is enough to keep the app working.
;
; STUB SWITCHES
; /LOW Force command.com to keep resident data in LOW memory.
; (does nothing)
; /Y Step through batch file specified by /c or /k
; (does nothing)
; /L:nnnn internal buffers size
; (does nothing)
; /U:nnn input buffers size
; (does nothing)
; /K command run batch file specified by command and continue
; turns on /P switch if not already specified.
;
cmp Comnd1_Syn,offset ResGroup:Command_f_syn ; was /F entered?
jnz NoFSwitch
jmp SetFSwitch ; yes go set fail switch
NoFSwitch:
cmp Comnd1_Syn,offset resgroup:Command_p_syn ; was /P entered?
jnz NoPSwitch
jmp SetPSwitch ; yes go set up PERMCOM
NoPSwitch:
cmp Comnd1_Syn,offset resgroup:Command_d_syn ; was /D entered?
jnz NoDSwitch
jmp SetDSwitch ; yes go set date switch
NoDSwitch:
cmp Comnd1_Syn,offset resgroup:Command_c_syn ; was /C entered?
jnz NoSSwitch
jmp SetSSwitch ; yes go set up SINGLECOM
NoSSwitch:
cmp Comnd1_Syn,offset resgroup:Command_k_syn ; was /K entered?
jnz NoKSwitch
jmp SetKSwitch ; yes go set up environment
NoKSwitch:
cmp Comnd1_Syn,offset resgroup:Command_y_syn ; was /Y entered?
jnz NoYSwitch
jmp SetYSwitch ; yes go set up environment
NoYSwitch:
cmp Comnd1_Syn,offset resgroup:Command_e_syn ; was /E entered?
jnz NoESwitch
jmp SetESwitch ; yes go set up environment
NoESwitch:
cmp Comnd1_Syn,offset resgroup:Command_l_syn ; was /LOW entered?
jnz NoLSwitch
jmp SetLSwitch ; yes go set up message flag
NoLSwitch:
cmp Comnd1_Syn,offset resgroup:Command_l2_syn ; was /L entered?
jnz NoL2Switch
jmp SetL2Switch ; yes go set up environment
NoL2Switch:
cmp Comnd1_Syn,offset resgroup:Command_m_syn ; was /MSG entered?
jnz NoMSwitch
jmp SetMSwitch ; yes go set up message flag
NoMSwitch:
cmp Comnd1_Syn,offset resgroup:Command_u_syn ; was /U entered?
jnz NoUSwitch
jmp SetUSwitch ; yes go set up message flag
NoUSwitch:
jmp ChkOtherArgs ; Must be something else
ArgsdoneJ3: ; long jump needed
jmp ArgsDone ;
SetFSwitch:
cmp fFail,-1 ; has fail switch been set?
jnz failok ; no - set it
mov ax,Moreargs_ptr ; set up too many arguments
jmp parse_line_error ; go issue error message
failok:
mov fFail,-1 ; fail all INT 24s.
jmp Parse_command_line ;
SetPSwitch:
;
; We have a permanent COMMAND switch /P. Flag this and stash the
; termination address.
;
cmp PermCom,0 ; has /p switch been set?
jz permcomok ; no - set it
mov ax,moreargs_ptr ; set up too many arguments
jmp parse_line_error ; go issue error message
permcomok:
inc PermCom
mov word ptr OldTerm,offset DATARES:LodCom_Trap
mov word ptr OldTerm+2,ds
;
; make sure that we display the date and time. if the flag was not
; initialized, set it to indicate yes, do prompt.
;
cmp Comnd1_Syn,offset resgroup:Command_k_syn
jnz RealPSwitch
jmp ArgsDone
RealPSwitch:
cmp byte ptr PrdAttm,-1
jnz Parse_command_line_jmp ; keep parsing
mov byte ptr PrdAttm,0 ; if not set explicit, set to prompt
Parse_command_line_jmp: ;
jmp parse_command_line ; keep parsing
ArgsDoneJump:
jmp ArgsDone
SetDSwitch:
;
; Flag no date/time prompting.
;
cmp Dswitch,0 ; has /D switch been set?
jz setdateok ; no - set it
mov ax,Moreargs_ptr ; set up too many arguments
jmp parse_line_error ; go issue error message
setdateok:
inc Dswitch ; indicate /D entered
mov byte ptr PrdAttm,1 ; user explicitly says no date time
jmp Parse_command_line ; continue parsing
SetYSwitch:
;
; Y switch is ignored. Present to keep apps that actually use it in a
; static create process or something from having problems
; (important after a win9xupg.)
;
jmp Parse_command_line ; continue parsing
SetKSwitch:
;
; Treat this just like /C, just keep going
;
mov KSwitchFlag,1
jmp SetSSwitch
;
; Look for environment-size setting switch
;
; The environment size is represented in decimal bytes and is
; converted into pargraphs (rounded up to the next paragraph).
;
SetSSwitch:
;
; Set up pointer to command line, flag no date/time and turn off singlecom.
;
mov SingleCom,si ; point to the rest of the command line
mov Permcom,0 ; a singlecom must not be a permcom
mov byte ptr PrdAttm,1 ; no date or time either, explicit
jmp ArgsDone
;
; Look for environment-size setting switch
;
; The environment size is represented in decimal bytes and is
; converted into pargraphs (rounded up to the next paragraph).
;
SetESwitch:
cmp Eswitch,0 ; has fail switch been set?
jz eswitchok ; no - set it
mov ax,Moreargs_ptr ; set up too many arguments
jmp Parse_line_error ; go issue error message
eswitchok:
inc Eswitch ; indicate /E entered
mov di,offset ResGroup:Comnd1_Addr ; get number returned
mov bx,word ptr [di] ; into bx
add bx, 0fh ; Round up to next paragraph
mov cl,4 ; convert to pargraphs
shr bx, cl ; by right 4
mov EnvSiz,BX ; EnvSiz is in paragraphs
jmp Parse_command_line ; continue parsing command line
SetL2Switch:
Jmp Parse_Command_Line
SetMSwitch:
cmp Ext_msg,SET_EXTENDED_MSG ; has /MSG switch been set?
jnz setMswitchok ; no - set it
mov ax,Moreargs_ptr ; set up too many arguments
jmp Parse_line_error ; go issue error message
setMswitchok:
mov Ext_msg,SET_EXTENDED_MSG ; set /MSG switch
jmp Parse_command_line ; keep parsing
SetLSwitch:
jmp Parse_Command_Line
SetUSwitch:
Jmp Parse_Command_Line
ArgsDoneJ:
jmp ArgsDone
;
; We have a non-switch character here.
;
ChkOtherArgs:
push ds ;
push si ; save place in command line
lds si,Comnd1_Addr ; get address of filespec
assume ds:nothing ;
mov dx,si ; put in dx also
mov ax,(OPEN shl 8) or 2 ; Read and write
int 21h
jc ChkSrchSpec ; Wasn't a file
mov bx,ax
mov ax,IOCTL shl 8
int 21h
test dl,80h
jnz IsaDevice
BadSetCon: ;
mov ah,CLOSE ; Close initial handle, wasn't a device
int 21h
jmp short ChkSrchSpec
IsaDevice:
xor dh,dh
or dl,3 ; Make sure has CON attributes
mov ax,(IOCTL shl 8) or 1
int 21h
jc BadSetCon ; Can't set attributes - quit
mov dx,bx ; Save new handle
cmp es:DevFlag,1
jz DevErr
push cx
mov cx,3
xor bx,bx
rcclloop: ; Close 0,1 and 2
mov ah,CLOSE
int 21h
inc bx
loop rcclloop
mov bx,dx ; New device handle
mov ah,XDUP
int 21h ; Dup to 0
mov ah,XDUP
int 21h ; Dup to 1
mov ah,XDUP
int 21h ; Dup to 2
mov ah,CLOSE
int 21h ; Close initial handle
pop cx
pop si ; restore position of command line
pop ds ;
;SR;
; Register the fact that we already have redirected the output and cannot do
;it again
;
inc es:DevFlag ;
jmp Parse_command_line ; continue parsing
DevErr:
pop si
pop ds
mov dx,1
call RPrintParse ;"Too many parameters"
call CrLf
jmp Parse_command_line
ChkSrchSpec: ; Not a device, so must be directory spec
cmp es:PathFlag,1 ;already set COMSPEC?
jz DevErr ;yes, error
inc es:PathFlag ;mark that we have a path
;
;We do not have an ENVIRONMENT segment any more so we need to allocate a
;temporary buffer to hold the COMSPEC. Later, this buffer will be freed and
;its contents moved to the final location of the environment. If there was
;no environment passed before, then a buffer has already been allocated and
;so we check for this case. Fortunately, Chuckenv would have been previously
;set if we had been passed an environment.
;
mov ax,EnvirSeg
cmp byte ptr Chuckenv,1 ;passed environment?
mov byte ptr Chuckenv,0 ;ignore passed environment
jne no_alloc ;no, default, dont allocate
;
;We have to override the passed environment. Allocate a buffer for use now.
;This buffer will later be replaced by a proper environment
;
call alloc_env ;allocate env buffer
;; mov ax,offset ResGroup:PathString ; Figure environment pointer
;; mov cl,4
;; shr ax,cl
;; mov dx,cs ;
;; add ax,dx
mov EnvirSeg,ax
no_alloc:
mov es,ax
assume es:nothing
push si ; remember location of file
xor cx,cx ; clear cx for counting
countloop:
lodsb ; get a character
inc cx ; increment counter
cmp al,END_OF_LINE_OUT ; are we at end of line?
jnz countloop ; no - keep counting
mov al,Space
dec si ; move back one
mov byte ptr [si],al ; put a space at end of line
pop si ; get location back
mov di,Env_Ecomspec ; get location of COMSPEC
ComtrLoop:
lodsb
dec cx
cmp al,Space
jz SetComsr
stosb
ifdef DBCS
xor ah,ah
endif
jcxz setcomsr
ifdef DBCS
push ds ; Make sure we have
push cs ; local DS for
pop ds ; ItestKanj
call ItestKanj
pop ds ; restore parser ds
jz ComtrLoop
dec cx
movsb
inc ah
jcxz setcomsr
endif
jmp short comtrloop
setcomsr:
push cx
push cs ; Get local segment
pop ds ;
assume ds:ResGroup ;
push ds
mov si,offset ResGroup:ComSpect
mov cx,14
mov al,es:[di-1]
ifdef DBCS
or ah,ah
jnz iNotRoot ; Last char was KANJI second byte, might be '\'
endif
cmp al,RDirChar
jnz iNotRoot
inc si ; Don't make a double /
dec cx
iNotRoot:
rep movsb
mov dx,Env_Ecomspec ; Now lets make sure its good!
push es
pop ds
mov ax,OPEN shl 8
int 21h ; Open COMMAND.COM
pop ds
jc SetComsrBad ; No COMMAND.COM here
mov bx,ax ; Handle
mov ah,CLOSE
int 21h ; Close COMMAND.COM
SetComsrRet:
pop cx
pop si
pop ds ;
assume ds:ResGroup ;
ArgsDoneJ2:
push cs ; Make sure local ES is
pop es ; restored
jmp Parse_command_line ; continue parsing command line
SetComsrBad:
mov dx,offset ResGroup:BadComlkMsg ; dx = ptr to msg
; Note: we're about to make a near call to TriageError, which
; lives in a different segment and group. Some linkers will
; generate a warning like "Possible fix-up overflow". We're
; ok, though, because we all fit in 64 KB and, at init time,
; we're still all together.
call triageError
cmp ax, 65
jnz doprt
mov dx,offset ResGroup:BadComaccMsg ; dx = ptr to msg
DoPrt:
call RPrint
mov si,offset ResGroup:ComSpect
mov di,Env_Ecomspec
mov cx,14
rep movsb ; get my default back
jmp short SetComsrRet
;*********************************
; Parsing Ends Here
;*********************************
ArgsDone:
mov es,EnvirSeg ; get environment back
assume es:nothing ;
cmp PermCom,0
jz ComReturns
push es ; Save environment pointer
mov ah,SET_CURRENT_PDB
mov bx,ds
mov es,bx
int 21h ; current process is me
mov di,PDB_EXIT ; Diddle the addresses in my header
mov ax,offset DATARES:LodCom_Trap
stosw
mov ax,ds
stosw
mov ax,offset DATARES:Ctrlc_Trap
stosw
mov ax,ds
stosw
mov ax,offset DATARES:CritErr_Trap
stosw
mov ax,ds
stosw
mov word ptr ds:PDB_Parent_Pid,ds ; Parent is me forever
mov dx,offset DATARES:Int2e_Trap
mov ax,(SET_INTERRUPT_VECTOR shl 8) or 02eh
int 21h ;set magic interrupt
pop es ;Remember environment
ComReturns:
mov ax,word ptr ds:PDB_Parent_Pid
mov Parent,ax ; Save parent
mov word ptr ds:PDB_Parent_Pid,ds ; Parent is me
mov ax,word ptr ds:PDB_Jfn_Table
mov Io_save,ax ; Get the default stdin and out
mov al,byte ptr ds:[PDB_Jfn_Table+2]
mov Io_Stderr,al ; Get the default stdin and out
mov word ptr Com_ptr+2,ds ; set all these to resident
mov word ptr Com_fcb1+2,ds
mov word ptr Com_fcb2+2,ds
mov di,offset ResGroup:ComSpec
mov si,Env_Ecomspec
cmp byte ptr Chuckenv,0
mov ax,ds ; Xchg es,ds
push es
pop ds
mov es,ax
jz CopyComsp ; All set up for copy
push cs
pop ds
mov si,offset ResGroup:ComspString
push es
push di
call IfindE
mov si,di
push es
pop ds
pop di
pop es
jnc CopyComsp
ComSpecNofnd:
mov si,Env_Ecomspec ;
push cs
pop ds
assume es:ResGroup
CopyComsp:
mov es:PutBackComSpec.SubstPtr,di ; Save ptr to beginning of comspec path
cmp byte ptr [si+1],':' ; Is there a drive specifier in comspec
jnz CopyComspLoop ; If not, do not skip over first 2 bytes
add es:PutBackComSpec.SubstPtr,2
CopyComspLoop:
lodsb
stosb
or al,al
jnz CopyComspLoop
mov es:Comspec_end,di ; Save ptr to end of comspec path
dec es:Comspec_end
mov ah,es:comdrv
add ah,'A'-1
mov es:PutBackDrv,ah ; save drive letter
call setup_for_messages ; set up parse and extended error messages
;SR;
;The routine below sets up the exact resident size of COMMAND. If this is not
;the first COMMAND, then the resident code is not duplicated and the resident
;size is just the data. If we are the first COMMAND, it checks if we are to
;be loaded into HIMEM. If not, then the resident size includes the code and
;the data otherwise it is just the data.
;
call Setup_res_end ;put resident size in ResSize
push cs
pop ds
assume ds:RESGROUP
;; if we are the permanent one, tell ntvdm the real comspec.
cmp PermCom, 0
je comspec_to_32_done
push ax
mov dx, offset RESGROUP:ComSpec
CMDSVC SVC_CMDCOMSPEC
mov SCS_PAUSE,al ; yst 4-5-93
pop ax
comspec_to_32_done:
;; mov bx,ResMsgEnd ; get end of resident
;; add bx,15
;; mov cl,4
;; shr bx,cl
Public EnvMaximum
EnvMaximum:
;;;
;;; NOTE: The transient has to loaded directly after shrinking to the
;;; resident size.
;;; There is an assumption made when loading the transient that it
;;; still intact after the resident portion.
;;; If any other ALLOC/DEALLOC/SETBLOCK operations are performed
;;; inbetween, then there is a real good chance that the non-resident
;;; portion will be overwritten by arena information.
;SR;
;Do not shrink to the resident now. We will then be running in deallocated
;memory and BAAADNESS! can ensue.
;
;; mov ah,SETBLOCK
;; int 21h ; shrink to the resident only
;
; Load in the transient and compute the checksum. We may do this in one of
; two ways: First, cheat and use the transient loading code that exists in
; the resident piece. This may be OK except that it will hit the disk.
;
; But we do not need to hit the disk! The transient is already loaded but is
; in the wrong place. We need to block transfer it up to the correct spot.
;
;
; M004; Start of changes
;
;
; Compute checksum right now before we can get corrupted and save it
;
mov si,offset RESGROUP:TranStart
add si,100h
mov cx,offset TRANGROUP:TranDataEnd - 100H
cld
shr cx,1
xor dx,dx
Ichksum:
lodsw
add dx,ax
adc dx,0
loop Ichksum
mov Sum,dx ;store checksum
;
; M004; End of changes
;
cmp byte ptr PrdAttm,0 ;eg
jnz NoBatchSeg ;eg don't do autoexec or date time
;
; allocate batch segment for d:/autoexec.bat + no arguments
;
; NTVDM temp name of the batch file may be up to 63 bytes, plus NULL
; mov bx,((SIZE BatchSegment) + 15 + 1 + 0fh)/16 ;eg
mov bx,((SIZE BatchSegment) + 64 + 0fh)/16 ;eg
mov ah,ALLOC ;eg
int 21h ;eg
jc NoBatchSeg ;eg didn't allocate - pretend no batch
mov Batch,ax ;eg save batch segment
nobatchseg:
;; mov bx, 0ffffh ; get size of largest block for env
;; mov ah, ALLOC
;; int 21h
;;
;;; only allocate maximum 64k worth of environment
;;
;; sub bx,TrnSize ;eg subtract # of transient paragraphs
;; sub bx,128 ;eg make sure we have 2k left
;; mov EnvMax, bx
;; cmp bx, 4096 ; 64k = 4096 paragraphs
;; jb maxok
;; mov bx, 4096-1
;; mov EnvMax, bx
;;maxok:
;;
;; mov ah, ALLOC ; get max size
;; int 21h
;;
mov bx,EnvirSeg ;g get old environment segment
mov OldEnv,bx ;g save it
mov UsedEnv,0 ;g initialize env size counter
mov ds,bx
assume ds:nothing
;; mov EnvirSeg,ax
;;;Bugbug: There is no need to initialize es at this point-- no string moves
;; mov es,ax
xor si,si
mov di,si
;SR;
;This is the maximum allowed size for the environment
;
mov bx,4096 - 1 ; max. allowed env. size
mov EnvMax,bx
shl bx,1
shl bx,1
shl bx,1
shl bx,1
mov EnvMax, bx ; convert envmax to bytes
dec bx ; dec by one to leave room for double 0
xor dx,dx ; use dx to indicate that there was
; no environment size error.
public NxtStr
NxtStr:
call GetStrLen ; get the size of the current env string
;Bugbug: Can use ss here to address UsedEnv
push ds ;g get addressability to environment
push cs ;g counter
pop ds ;g
assume ds:ResGroup
add UsedEnv,cx ;g add the string length to env size
pop ds ;g
assume ds:nothing
cmp cx,1 ; end of environment was encountered.
jz EnvExit
sub bx,cx
jae OkCpyStr ; can't fit in all of enviroment.
inc dx ; out of env space msg must be displayed
jmp short EnvExit
OkCpyStr:
jmp NxtStr
EnvExit:
push cs
pop ds
assume ds:ResGroup
or dx,dx ; dx will be non-zero if error
jz EnvNoErr
mov dx,offset ResGroup:OutEnvMsg ; dx = ptr to msg
call RPrint
EnvNoErr:
mov ax,EnvSiz ;env size previously set
mov cl,4
shl ax,cl ;get size in bytes
cmp ax,UsedEnv ;is it a new env?
ja st_envsize ;yes, store the size
mov ax,UsedEnv
add ax,15 ;round up
st_envsize:
shr ax,cl
mov EnvSiz,ax ;store env size needed(paras)
;;;
;;; bx now has the left over size of the maximum environment
;;; we want to shrink the environment down to the minimum size
;;; set the environment size to max(envsiz,env used)
;; mov cx, EnvMax
;; sub cx, bx ; cx now has the environment used
;; add cx, 16 ; round up to next paragraph
;; shr cx, 1
;; shr cx, 1
;; shr cx, 1
;; shr cx, 1
;; cmp cx, EnvSiz ; is environment used > envsiz
;; jb EnvSet
;; mov EnvSiz, cx
;;EnvSet:
;; mov bx, EnvSiz ; set environment to size needed
;; mov ax,es ;eg get environment segment
;; add ax,bx ;eg add number of environment paragraphs
;; cmp ax,InitEnd ;eg does this go past end of init?
;; ja EnvSetOk ;eg yes - do the setblock
;; mov ax,es ;eg no - get back the environment segment
;; mov bx,InitEnd ;eg get the segment at end of init
;; sub bx,ax ;eg setblock envir segment to end of init code
;; mov ResetEnv,1 ;eg set flag so we know to set envir later
;;
;;envsetok:
;; mov ah, setblock
;; int 21h
ifndef NEC_98
cmp SCS_FIRSTCOM,1
jz nophead ; don't print header for first instance
else ;NEC_98
endif ;NEC_98
cmp SCS_CMDPROMPT,1
je nophead
cmp SingleCom,0
jnz nophead ; don't print header if singlecom
mov dx,offset ResGroup:CopyrightMsg ; dx = ptr to msg
call RPrint
nophead:
cmp Batch,0 ;eg did we set up a batch segment?
jnz DoDate ;eg yes - go initialize it
jmp NoDttm ; don't do autoexec or date time
;
; allocate batch segment for d:/autoexec.bat + no arguments
;
DoDate:
mov ax,Batch ;eg get batch segment
mov EchoFlag,3 ; set batch echo
mov Nest,1 ; g set nest flag to 1 batch
mov es,ax
;
; initialize the segment
;
xor di,di
mov al,BATCHTYPE
stosb
mov al,1 ; g initialize echo for batch exit
stosb ; g
;SR;
; Hosebag! This guy does not use the struct fields to init the BatchSegment
;
xor ax,ax ; initialize to zero
stosb ; clear out BatchEOF
stosw ; g batch segment of last job - batlast
stosw ; g segment for for
stosb ; g for flag
stosw ; position in file - batseek
stosw
;
; clean out the parameters
;
mov ax,-1 ; initialize to no parameters
mov cx,10
rep stosw
;
; NTVDM Get temp file name from 32 bit command.lib
;
if 0
;
; decide whether we should grab the default drive
;
cmp byte ptr AutoBat,0
jnz NoAutSet
mov ah,GET_DEFAULT_DRIVE
int 21h
add al,Ucasea
mov AutoBat,al
mov KautoBat,al ; 3/3/kk
NoAutSet:
;
; copy in the batch file name (including nul)
;
mov si,offset ResGroup:AutoBat
mov cx,8
rep movsw
movsb ; move in carriage return to terminate string
endif
push es
pop ds
assume ds:nothing
mov dx, di
CMDSVC SVC_GETAUTOEXECBAT
;NTVDM not used mov dx,offset ResGroup:AutoBat
mov ax,OPEN shl 8
int 21h ; see if autoexec.bat exists
push cs
pop ds
assume ds:ResGroup
jc NoAbat
mov bx,ax
mov ah,CLOSE
int 21h
ifndef NEC_98
jmp Drv0 ; go process autoexec
else ;NEC_98
jmp short Drv0 ; NEC01 91/07/30 Warning Error Del
endif ;NEC_98
noabat:
push ax
call Setup_Seg
mov word ptr Triage_Add+2,ax
pop ax
call Triage_Add
cmp ax, 65
jz AccDenErr ; was network access denied
; NTVDM we don't do kautoexe.bat
if 0
; If AUTOEXEC.BAT is not found, then check for KAUTOEXE.BAT. Changed
; by Ellen to check only when in Korea. The country information
; returned will overlay the old parse data area, but we don't care
; since we won't need the parse information or country information.
; We only care about the country code returned in BX.
mov dx,offset ResGroup:Internat_Info ; set up internat vars
mov ax,INTERNATIONAL shl 8 ; get country dependent info
int 21h ;
jc NoKabat ; error - don't bother with it
cmp bx,KOREA_COUNTRY_CODE ; are we speaking korean?
jnz OpenErr ; no, don't check for kautoexe
mov di,BatFile ; 3/3/kk
mov si,offset ResGroup:KautoBat ; another trial to do 3/3/kk
mov cx,8 ; auto execution for the 3/3/kk
rep movsw ; non-english country 3/3/kk
movsb ; move in carraige return to terminate string
mov dx,offset ResGroup:KautoBat ; 3/3/kk
mov ax,OPEN shl 8 ; 3/3/kk
int 21h ; see if kautoexe.bat exists 3/3/kk
jc NoKabat ; 3/3/kk
mov bx,ax ; 3/3/kk
mov ah,CLOSE ; 3/3/kk
int 21h ; 3/3/kk
jmp short Drv0 ; 3/3/kk
NoKabat: ; 3/3/kk
call Triage_Add ; get extended error
cmp ax, 65 ; network access denied?
jnz OpenErr ; no - go deallocate batch
endif
AccDenErr: ; yes - put out message
mov dx,offset ResGroup:AccDen ; dx = ptr to msg
call RPrint
OpenErr:
mov es,Batch ; not found--turn off batch job
mov ah,DEALLOC
int 21h
mov Batch,0 ; after dealloc in case of ^c
mov EchoFlag,1
mov Nest,0 ;g indicate no batch in progress
DoDttm:
mov ax,offset TranGroup:Datinit
mov word ptr InitAdd,ax
;;;M004 mov ax,TrnSeg
;
; M004; We cant use TrnSeg now because it is not initialized. We now that
; M004; the transient starts on a para boundary at the label TranStart.
; M004; We use TranStart to get the start of the transient segment.
;
mov ax,offset RESGROUP:TranStart ; M004
mov cl,4 ; M004
shr ax,cl ; get relative seg ; M004
mov cx,cs
add ax,cx ; ax = transient seg ; M004
mov word ptr InitAdd+2,ax
; call dword ptr InitAdd
NoDttm:
Copyright:
public Copyright
; Bugbug: remove Copyright label.
;if IBMVER
; cmp SingleCom,0
; jnz Drv0 ; don't print header if singlecom
; mov dx,offset ResGroup:CopyrightMsg ; dx = ptr to msg
; call RPrint
;endif
Drv0: ; Reset APPEND state
push ds ; save data segment
push cs ; Get local segment into DS
pop ds ;
mov ax,APPENDSETSTATE ; Set the state of Append
mov bx,Append_State ; back to the original state
int 2fh ;
pop ds ; get data segment back
;
;Check FirstCom set previously to see if this is the first instance of
;command.com. If not, we do not move command.com. Instead, we copy over the
;jump table from the previous stub to the current stub.
;
cmp FirstCom,1 ;first command.com?
jz move_code ;yes, move it
push es
push ds
push ds
pop es
mov di,offset DATARES:Int2f_Entry
mov ds,word ptr es:ResJmpTable+2 ;get segment address
mov si,word ptr es:ResJmpTable ;get offset address
mov cx,NUM_RELOC_ENTRIES ;number of dword ptrs
shl cx,1
shl cx,1 ;size of table in bytes
cld
rep movsb ;copy the jump table
;
;Check if the resident code is in HMA. We assume that it is in HMA if its
;code segment > 0f000h. If in HMA, we set the ComInHMA flag
;
cmp es:[di-2],0f000h ;is resident code in HMA?
jb res_low ;no, dont set flag
mov es:ComInHMA,1 ;indicate code in HMA
res_low:
pop ds
pop es
jmp short finish_init
;
;Now, we can move the resident code to its final location, either to HIMEM
;or to overlay the messages in the data segment if the user has not used the
;/msg switch.
;
move_code:
call Move_res_code ;move the code
finish_init:
jmp RESGROUP:EndInit ;g finish initializing
;
; Get length of string pointed to by DS:SI. Length includes NULL.
; Length is returned in CX
;
GetStrLen:
xor cx,cx
NxtChar:
lodsb
inc cx
or al,al
jnz NxtChar
ret
;
; If the transient has been loaded in TranSeg, then we need to use that
; segment for calls to routines in the transient area. Otherwise, the current
; code segment is used
; Segment returned in AX.
;
Setup_Seg:
mov ax,TrnSeg
cmp TrnMvFlg, 1 ; Has transient portion been moved
jz setup_end
push bx
mov bx,cs
mov ax,offset ResGroup:TranStart
shr ax,1
shr ax,1
shr ax,1
shr ax,1
add ax,bx
pop bx
setup_end:
ret
;*** RPrintParse - display parse error message
;
; ENTRY DX = parse error #
;
; EXIT nothing
;
; USED flags
;
; EFFECTS
; Message is displayed on stdout.
RPrintParse proc
assume ds:ResGroup,ss:ResGroup
push dx ; preserve DX
xchg bx,dx ; bx = parse error #
; dx = saved BX
ifdef BILINGUAL
push ax
push bx
mov ax,4f01h ; get code page
xor bx,bx
int 2fh
ifdef JAPAN
cmp bx,932
endif ; JAPAN
ifdef KOREA
cmp bx,949
endif ; KOREA
ifdef TAIWAN
cmp bx,950
endif ; TAIWAN
ifdef PRC
cmp bx,936
endif ; TAIWAN
pop bx
pop ax
jz @f ; if DBCS code page
add bx,11
@@:
endif ; BILINGUAL
dec bx ; bx = parse error index, from 0
shl bx,1 ; bx = offset in word table
mov bx,ParsMsgPtrs[bx] ; bx = ptr to error msg
xchg bx,dx ; dx = ptr to error msg
; bx = restored
ifdef BILINGUAL
call RPrint@ ; print the message
else ; !BILINGUAL
call RPrint ; print the message
endif ; !BILINGUAL
pop dx ; restore DX
ret
RPrintParse endp
IfindE:
call ifind ; find the name
jc ifind2 ; carry means not found
jmp short Iscasb1 ; scan for = sign
;
; on return of find1, es:di points to beginning of name
;
ifind:
cld
call Icount0 ; cx = length of name
mov es,EnvirSeg
xor di,di
ifind1:
push cx
push si
push di
Ifind11:
lodsb
ifdef DBCS
call ItestKanj
jz NotKanj4
dec si
lodsw
inc di
inc di
cmp ax,es:[di-2]
jnz Ifind12
dec cx
loop Ifind11
jmp short Ifind12
NotKanj4:
endif
call IupConv
inc di
cmp al,es:[di-1]
jnz Ifind12
loop Ifind11
Ifind12:
pop di
pop si
pop cx
jz Ifind2
push cx
call Iscasb2 ; scan for a nul
pop cx
cmp byte ptr es:[di],0
jnz Ifind1
stc ; indicate not found
Ifind2:
ret
Icount0:
push ds
pop es
mov di,si
push di ; count number of chars until "="
call Iscasb1
jmp short Icountx
push di ; count number of chars until nul
call Iscasb2
Icountx:
pop cx
sub di,cx
xchg di,cx
ret
Iscasb1:
mov al,Equalsign ; scan for an =
jmp short Iscasbx
Iscasb2:
xor al,al ; scan for a nul
Iscasbx:
mov cx,100h
repnz scasb
ret
; ****************************************************************
; *
; * ROUTINE: IUPCONV (ADDED BY EMG 4.00)
; *
; * FUNCTION: This routine returns the upper case equivalent of
; * the character in AL from the file upper case table
; * in DOS if character if above ascii 128, else
; * subtracts 20H if between "a" and "z".
; *
; * INPUT: DS set to resident
; * AL char to be upper cased
; * FUCASE_ADDR set to the file upper case table
; *
; * OUTPUT: AL upper cased character
; *
; ****************************************************************
IupConv proc near
assume ds:ResGroup ;
cmp al,80h ; see if char is > ascii 128
jb other_fucase ; no - upper case math
sub al,80h ; only upper 128 chars in table
push ds ;
push bx ;
lds bx,dword ptr fucase_addr+1 ; get table address
add bx,2 ; skip over first word
xlat ds:byte ptr [bx] ; convert to upper case
pop bx ;
pop ds ;
jmp short iupconv_end ; we finished - exit
other_fucase: ;
cmp al,Lcasea ; if between "a" and "z",
jb iupconv_end ; subtract 20h to get
cmp al,Lcasez ; upper case equivalent.
ja iupconv_end ;
sub al,20h ; Change lower-case to upper
iupconv_end: ;
ret
IupConv endp ;
init_contc_specialcase:
; This routine is called if control-C
add sp,6 ; is type during the date/time prompt
push si ; at initialization time. The desired
mov si,dx ; response is to make it look like the
mov word ptr [si+1],0d00h ; user typed <CR> by "popping" the
pop si ; INT 21h stuff off the stack, putting
iret ; a <CR> in the user's buffer, and
; returning directly to the user.
; In this case the user is TCODE.
; ****************************************************************
; *
; * ROUTINE: Setup_for_messages
; *
; * FUNCTION: Sets up system for PARSE and EXTENDED ERROR
; * messages as follows:
; *
; * IF /P and /MSG are entered
; * keep PARSE and EXTENDED ERRORS in memory
; * ELSE IF /P is entered
; * use PARSE and EXTENDED ERRORS on disk
; * remove PARSE ERRORS from memory
; * ELSE
; * remove PARSE ERRORS from memory
; * ENDIF
; *
; * INPUT: PERMCOM Set up with user input
; * EXT_MSG Set up with user input
; * System set up to retain PARSE ERRORS
; *
; * OUTPUT: registers unchanged
; *
; ****************************************************************
setup_for_messages proc near
push bx
push ds ; save data segment
push es ; save environment segment
push ax ;
push dx ;
push di ;
mov ax,cs ; get local segment to ES and DS
mov ds,ax ;
mov es,ax ;
cmp PermCom,0 ; was permcom set?
jz no_permcom ; No - don't worry about messages
;* We're permanent. Install our message services int 2f handler.
push es
mov ax,(GET_INTERRUPT_VECTOR shl 8) or 2Fh
int 21h
mov word ptr Int2fHandler,bx
mov word ptr Int2fHandler+2,es
pop es
; DS = RESGROUP seg addr
;
; M005; We will not hook int 2fh on any command.com other than the first.
; M005; Carousel loads as a permanent command.com and when we exit Carousel,
; M005; it just wipes our arena out. So, int 2fh is still hooked and the
; M005; first int 2fh call after exit from Carousel (from the DOS terminate
; M005; call) goes off into space.
;
cmp FirstCom,0 ; M005
je no_msg_hook ; M005
;
; M005; !!!SLIMIEST CAROUSEL HACK OFF ALL!!!
; M005; Carousel plays around with the interrupt vector tables. He saves it
; M005; before loading a new command.com. Then, it takes hold of the current
; M005; command.com's PSP and then looks at all interrupt vectors whose
; M005; segment matches the command.com PSP and then updates these segments
; M005; to the new command.com's PSP in his saved vector table. Whenever we
; M005; we pop into his menu, he puts this saved table into the vector table.
; M005; If we now quit, Carousel just wipes out command.com's arena and then
; M005; issues a terminate. Unfortunately, the int 2fh vector is pointing at
; M005; the command.com that was wiped out and so the next int 2fh call will
; M005; bomb. To prevent Carousel from doing this clever(1**$$#) patching, we
; M005; renormalize our int 2fh pointer so that its cs is not the same as the
; M005; command.com PSP. Now, he does no such patching and our int 2fh vector
; M005; remains nice and happy. The renormalized pointer points at a far
; M005; jump to the actual int 2fh entry point.
;
push ds ; M005
mov dx,offset DATARES:Carousel_i2f_Hook ; M005
sub dx,10h ; renormalize offset; M005
mov ax,ds ; M005
inc ax ; Relocated cs ; M005
mov ds,ax ; M005
mov ax,(SET_INTERRUPT_VECTOR shl 8) or 2Fh
int 21h
pop ds ; M005
mov word ptr Carousel_i2f_Hook+3,ds ; M005
; patch in the cs for jump
no_msg_hook: ; M005
cmp Ext_Msg,SET_EXTENDED_MSG
jne short permcom_end ; no /msg - exit
permcom_slash_msg: ; Keep messages in memory
mov di,offset ResGroup:ExtMsgEnd ; get address of resident end
mov ResMsgEnd,di ; save it
jmp short permcom_end ; exit
no_permcom:
cmp Ext_msg,SET_EXTENDED_MSG ; was /msg specified?
jnz permcom_end ; no - no error
mov dx,LessArgs_Ptr ; get message number for "Required parameter missing"
call RPrintParse
permcom_end:
pop di ;
pop dx ;
pop ax ;
pop es ; get environment back
pop ds ;
pop bx
ret ;
setup_for_messages endp
;*** CheckHelp - print help text and exit if /? is on command line
;
; ENTRY command-line tail at 81h
;
; EXIT return if /? not found
; terminate if /? found
;
; USED AX,BX,CX,DX,SI,DI
;
; EFFECTS Help text displayed if /? found on command line
CheckHelp proc
assume cs:RESGROUP,ds:RESGROUP,es:RESGROUP,ss:RESGROUP
mov si,81h ; DS:SI = ptr to command-line tail
mov di,offset RESGROUP:Parse_Command
; ES:DI = ptr to primary parse block
xor cx,cx ; CX = # positional param's found
xor dx,dx ; DX will be ptr to result buffer
chParse:
call Init_Parse ; call system parser
cmp ax,END_OF_LINE
je chRet ; end of command line, no /? found
cmp ax,RESULT_NO_ERROR
je chWhich ; valid syntax element found
jmp chParse ; go parse more
chWhich:
cmp Comnd1_Syn,offset RESGROUP:Command_?_Syn
je chHelp ; /? found - display help & exit
cmp Comnd1_Syn,offset RESGROUP:Command_C_Syn
je chRet ; /c found - ignore rest of line
cmp Comnd1_Syn,offset RESGROUP:Command_K_Syn
je chRet ; /k found - ignore rest of line
cmp Comnd1_Syn,offset RESGROUP:Command_z_Syn
je chRet ; /c found - ignore rest of line
jmp chParse ; anything else - ignore, keep looking
chHelp:
mov si,offset RESGROUP:HelpMsgs ; SI = ptr to msg ptr list
chHelpNext:
lodsw ; AX = ptr to msg
or ax,ax
jz chHelpDone ; end of list - all done
mov dx,ax ; DX = ptr to msg
call RPrint ; display msg
jmp chHelpNext ; go do next msg
chHelpDone:
int 20h ; terminate program
chRet: ret
CheckHelp endp
st_invalid macro
mov ax,0ffffh
push ax
push ax
endm
st_valid macro
push bx
push cx
endm
CleanForStd macro
add sp,12
endm
;*** CheckZSwitch - Handle special /Z switch of command.com.
; This switch has been added for SCS functionality
; such that DOS apps can exec non-dos apps and
; still leave DOS in a consistant state such that
; it can be re-entered.
;
; ENTRY command-line tail at 81h
;
; EXIT non-dos binary is execed and command.com terminates after
; non-dos binary completes.
;
; EFFECTS
CheckZSwitch proc
assume cs:RESGROUP,ds:RESGROUP,es:RESGROUP,ss:RESGROUP
mov si,81h ; DS:SI = ptr to command-line tail
mov di,offset RESGROUP:Parse_Command
; ES:DI = ptr to primary parse block
xor cx,cx ; CX = # positional param's found
xor dx,dx ; DX will be ptr to result buffer
czParse:
call Init_Parse ; call system parser
cmp ax,END_OF_LINE
je czRet ; end of command line, no /? found
cmp ax,RESULT_NO_ERROR
je czWhich ; valid syntax element found
jmp short czRet
czWhich:
cmp Comnd1_Syn,offset RESGROUP:Command_z_Syn
je czSwitch
jmp short czRet
czSwitch:
push bp
xor bx,bx
mov si,bx
mov bp,bx
mov ax,5303h
int 21h
jnc st_stdin
st_invalid
jmp short go_stdout
st_stdin:
st_valid
go_stdout:
mov bx,1
mov ax,5303h
int 21h
jnc st_stdout
st_invalid
jmp short go_stderr
st_stdout:
st_valid
go_stderr:
mov bx,2
mov ax,5303h
int 21h
jnc st_stderr
st_invalid
jmp short std_done
st_stderr:
st_valid
std_done:
mov ah,19h
int 21h
mov si,84h ; 84h because that is where the real
; command starts (i.e. 7/z\bfoo.exe)
mov bx,ds:2ch
mov bp,sp
push es
mov es,bx
xor ah,ah ; no "cmd /c"
CMDSVC SVC_CMDEXEC ; Exec non-dos binary
pop es ; ds:si is command tail (DOS_style)
; es is env segment
; al = current drive
; ss:bp = std handles
; returns al = return code
lahf
CleanForStd
pop bp
sahf
jc czReEnter
mov ah,4ch
int 21h
czRet: ret
czReEnter:
xor ah,ah
mov [SCS_REENTERED],3
mov byte ptr ds:80h,0
ret
CheckZSwitch endp
;***** Setup_res_end -- This routine determines the resident size of COMMAND.
; It determines based on 2 factors:
; 1. Is this is the first COMMAND?
; 2. Is COMMAND to be loaded into HIMEM?
; The strategy works as follows:
;
; if ( First COMMAND)
; then if (COMMAND in HIMEM)
; ResSize = resident_data;
; else
; ResSize = resident_data + resident_code;
; else
; ResSize = resident_data;
;
; Int 2fh calls have been added to determine whether or not we are the first
;COMMAND and whether DOS is in HIMEM.
;
; ENTRY: ResMsgEnd = resident size of data in paras
;
; EXIT: ResSize = resident size in low memory
;
; REGISTERS AFFECTED: ax,cx,dx
;
; NTVDM use diff al value so we don't confuse dos 5.0
; NTVDM command.com GET_COMMAND_STATE equ 5500h
GET_COMMAND_STATE equ 5501h
GET_HMA_ADDR equ 4a02h
Setup_res_end proc near
push ds
mov ax,cs
mov ds,ax ;ds = RESGROUP
assume ds:RESGROUP
mov cx,ResMsgEnd ;set resident size = data
;M042 -- Begin changes
;If messages are to be kept behind, we need to round up the messages to
;the next para boundary. This is because we have a dummy segment between the
;data and the resident code segment so that the code segment starts on a
;para boundary
;
cmp cx,offset RESGROUP: ExtMsgEnd ;messages to be resident?
jne calc_res ;no, continue
add cx,15 ;round up
and cx,0fff0h
calc_res:
;
;M042 -- End changes
;
xor ax,ax
cmp FirstCom,1 ;is it first command.com?
jnz not_first ;no, do not keep code
;
;We issue a version check call with al=01 to detect if DOS is in HMA. If so,
;bit 4 of dh is set
;
push bx
push cx
mov ax,(SET_CTRL_C_TRAPPING shl 8) or 06h ;is DOS in HIMEM? ;M013
int 21h
pop cx
;bugbug: remove version check after testing
cmp bl,5 ;bl has true version ; M013
jb oldver
xor ax,ax
and dh,10h ;is DOS in HMA ; M013
pop bx
jnz not_first ;DOS in HIMEM, code not
; resident
mov ax,offset CODERES: EndCode ;size of code in bytes
not_first:
;
;Note that ax = 0(side effect of int 2fh), if the code is not to be retained
;
add cx,ax
add cx,15 ;round up to next para
shr cx,1
shr cx,1
shr cx,1
shr cx,1 ;ax = para size of res code
mov ResSize,cx ;store resident size
pop ds
assume ds:nothing
ret
;bugbug: remove this code (for version independent COMMAND) after testing
oldver:
pop bx
mov ax,offset CODERES: EndCode ;size of code in bytes
jmp short not_first
setup_res_end endp
;*** Move_res_code -- This routine moves the resident code to its final
; location. We check if DOS is in HIMEM. If so, we try to load ourselves
; in HIMEM. If we fail, then we remain low and update ResSize to reflect
; the correct resident size. When remaining low, we have to check if we
; need to overlay the messages part of the data segment which is determined
; by the /msg switch.
;
; ENTRY: ResMsgEnd = end of resident data
;
; EXIT: The resident code is either up high or in its final location
; down low.
;
; REGISTERS AFFECTED: ax,bx,cx,dx,si,di
;
Move_res_code proc near
push ds
push es
mov ax,cs
mov ds,ax
assume ds:RESGROUP
mov ax,(SET_CTRL_C_TRAPPING shl 8) or 06h ; M013
int 21h ;DOS in HIMEM?
and dh,10h ; M013
jnz move_high ;yes, move code high
;
;Check if messages have been discarded or not
;
load_low:
push ds
pop es ;es = RESGROUP
mov di,ResMsgEnd ;end offset in DATARES
mov bx,offset RESGROUP: ExtMsgEnd ;end offset of messages
cmp di,bx ;are messages to be kept?
jz no_move ;yes, dont move code
jmp short setup_move ;es:di points at dest.
move_high:
;
;We have to call DOS to get the load address in HIMEM for COMMAND
;We pass in bx the number of bytes we need
;
mov bx,offset CODERES: EndCode
;M030;
; Set di=0ffffh so that we load low in case no one answers this int 2fh
;
mov di,0ffffh ;DT - in case no-one handles
;this ; M030
mov ax,GET_HMA_ADDR
int 2fh
;
;If the offset = 0xffff, then no HMA available
;
cmp di,0ffffh ;HMA available?
mov ComInHMA,1 ;assume command.com in HMA
jnz setup_move ;no error, es:di = memory
mov ComInHMA,0 ;could not load in HMA
;
;Zero means that we do not have enough HIMEM. Remain low and update
;ResSize to reflect this
;
mov cx,ResMsgEnd ;size of data in bytes
mov ax,offset CODERES: EndCode ;size of code in bytes
add cx,ax
add cx,15 ;round up to next para
shr cx,1
shr cx,1
shr cx,1
shr cx,1 ;ax = para size of res code
mov ResSize,cx ;store resident size
jmp short load_low ;let code remain low
no_move:
mov cl,4
add di,0fh
and di,0fff0h ;round it to a para offset
jmp short patch_up
setup_move:
mov si,offset RESGROUP: StartCode
mov cx,offset CODERES: EndCode ;cx = bytes to move
cld
push di ;need di for patching offset
rep movsb
pop di
patch_up:
call patch_stub
pop es
pop ds
assume ds:nothing
ret
Move_res_code endp
;*** Alloc_env -- This routine allocates the temporary environment for the
; Init code to initialize the COMSPEC. This is not a complete environment.
; Later on, at EndInit time, a proper sized environment is allocated and
; the contents of this temporary environment are copied to it. This routine
; will not be called in case a valid environment is passed to command.com
;
; ENTRY: None
;
; EXIT: ax = segment of allocated environment segment
;
; REGISTERS AFFECTED: ax,bx,cx
;
Alloc_env proc near
push ds
push es
push si
push di
;
;Allocate default environment size
;
mov bx,SIZE Environment / 16 ;temp env size in paras
mov ah,ALLOC
int 21h
jc init_nomem ;insufficient memory, error
mov es,ax
assume es:nothing ;es = temp env segment
xor di,di
mov ax,di
;
;First clear out the entire buffer
;
mov cx,SIZE Environment
rep stosb
;
;Init. the path string (PATH=) first
;
push ss ; M026
pop ds ; ds = RESGROUP ; M026
assume ds:RESGROUP ; M026
mov si,offset RESGROUP: PathString ;ds:si = "PATH=\0"
mov di,Env_Pathstring ;offset of Path in env seg
pathlp:
lodsb
stosb
or al,al ;end of string?
jnz pathlp ;no, continue transfer
;
;Init. the Comspec string
;
mov si,offset RESGROUP: ComspString ;"COMSPEC=\COMMAND.COM\0"
mov di,Env_Comstring ;location of Comspec string
comsplp:
lodsb
stosb
or al,al ;end of string?
jnz comsplp
mov ax,es ;return env seg in ax
pop di
pop si
pop es
pop ds
assume ds:nothing
ret
init_nomem:
;
;We call the error routine from here. This routine never returns. It either
;terminates COMMAND with error( if it is not the first invocation ) or hangs
;the system ( if it is the first COMMAND.COM ).
;
call alloc_error
Alloc_env endp
;*** Alloc_error: This routine just jumps to the actual label where we
; check if this is a permanent or secondary command.com and take the
; appropriate action.
;
; ENTRY: ds = RESGROUP = DATARES
;
; EXIT: None - does not return
;
; REGISTERS AFFECTED: Does not matter
;
public Alloc_error
Alloc_error proc near
jmp RESGROUP:BadMemErr
Alloc_error endp
;*** Patch_stub -- This routine patches in the segment and offset values in
; the stub table of the various entry points in the resident code segment.
; Some of them are interrupt entry points and some of them are entries from
; the transient to the resident code segment.
;
; ENTRY: ds = RESGROUP
; es:di = segment:offset of final location of resident code
;
; EXIT: All segments and offsets patched into the stub table
;
; REGISTERS AFFECTED: ax, bx, cx, dx, si, di
;
;
Patch_stub proc near
assume ds:RESGROUP
push es
mov bx,es ;bx = resident code segment
mov dx,di
mov di,offset DATARES:Int2f_Entry
mov si,offset RESGROUP:Reloc_Table
push ds
pop es ;es = RESGROUP = DATARES
;
;bx:dx = segment:offset of resident code segment
;es:di = entry point table in stub
;ds:si = offset table in INIT segment -- offsets of code entry points now
;
mov cx,NUM_RELOC_ENTRIES ;number of entry points
patchlp:
lodsw ;get current offset
add ax,dx ;offset it by code seg location
stosw ;store offset
mov ax,bx
stosw ;store segment
loop patchlp
pop es
ret
Patch_stub endp
;*** Patch_segs -- This routine patches the segment values in the dword
; pointers that the stub uses to jump to the actual handler. These values
; are temporarily needed to handle these interrupts if they occur before
; the resident is relocated to its final position and all the addresses of
; the handlers have been updated.
;
; ENTRY: es = PSP segment = code segment
;
; EXIT: Current segment values patched into the jump table in the
; stub.
;
; REGISTERS AFFECTED: ax, cx, di
;
Patch_segs proc near
mov di,offset RESGROUP:Int2f_Entry
mov cx,4 ;we have to patch 4 handlers
add di,2
mov ax,es
pseglp:
stosw ;store the segment value
add di,2 ;skip the next offset value
loop pseglp
ret
Patch_segs endp
;*** get_XMMAddr -- This routine gets the call address for the XMM driver
; by issuing the appropriate int 2fh. This is stored in a stub variable
; and is used by the stub when we have to jump to the resident in HMA
;
; ENTRY: ds = RESGROUP
;
; EXIT: XMMCallAddr = XMM driver far call address
;
; REGISTERS AFFECTED:
;
get_XMMAddr proc near
assume ds:RESGROUP
push es
mov ax,XMM_MULTIPLEX SHL 8 + XMM_INSTALL_CHECK
int 2Fh
cmp al,80h ; Q: installed
jne short cXMMexit ; N: set error, quit
;
; get the XMM control functions entry point, save it, we
; need to call it later.
;
mov ax,XMM_MULTIPLEX SHL 8 + XMM_FUNCTION_ADDR
int 2Fh
mov word ptr [XMMCallAddr], bx
mov word ptr [XMMCallAddr+2],es
cXMMexit:
pop es
ret ; done
get_XMMAddr endp
; IRename
; Input : ds:dx - Existing File
; ds:di - NewFile
; Output None
;
IRename proc near
mov ah,56h
push ds
pop es
int 21h
ret
IRename endp
; IAccess
; Input : ds:dx - file name
; Output CY Set if file not found
; CY Clear if file found
IAccess proc near
mov ax,4300h
int 21h
ret
IAccess endp
; IDelete
; Input ds:dx - file to delete
; Output None
IDelete proc near
mov ah,41h
int 21h
ret
IDelete endp
INIT ENDS
END