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
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
|
|
|