Files
MS-DOS/v4.0/src/CMD/COMMAND/COMMAND2.ASM
T

620 lines
14 KiB
NASM

page 80,132
; SCCSID = @(#)command2.asm 4.3 85/10/16
; SCCSID = @(#)command2.asm 4.3 85/10/16
TITLE COMMAND2 - resident code for COMMAND.COM part II
NAME COMMAND2
.XCREF
.XLIST
INCLUDE DOSSYM.INC
INCLUDE comsw.asm
INCLUDE comequ.asm
INCLUDE resmsg.equ ;AN000;
.LIST
.CREF
tokenized = FALSE
CODERES SEGMENT PUBLIC BYTE ;AC000;
CODERES ENDS
DATARES SEGMENT PUBLIC BYTE
EXTRN append_state:word ;AN020;
EXTRN append_flag:byte ;AN020;
EXTRN COMDRV:BYTE
EXTRN comprmt1_block:byte ;AN000;
EXTRN comprmt1_subst:byte ;AN000;
EXTRN COMSPEC:BYTE
EXTRN cpdrv:byte
EXTRN envirseg:word
EXTRN EXTCOM:BYTE
EXTRN HANDLE01:WORD
EXTRN InitFlag:BYTE
EXTRN INT_2E_RET:DWORD ;AC000;
EXTRN IO_SAVE:WORD
EXTRN LOADING:BYTE
EXTRN LTPA:WORD
EXTRN MEMSIZ:WORD
EXTRN number_subst:byte ;AN000;
EXTRN OldTerm:DWORD ;AC000;
EXTRN PARENT:WORD ;AC000;
EXTRN PERMCOM:BYTE
EXTRN RDIRCHAR:BYTE
EXTRN RES_TPA:WORD
EXTRN RETCODE:WORD
EXTRN rsrc_xa_seg:word ;AN030;
EXTRN RSWITCHAR:BYTE
EXTRN SAVE_PDB:WORD
EXTRN SINGLECOM:WORD
EXTRN SUM:WORD
EXTRN TRANS:WORD
EXTRN TranVarEnd:BYTE
EXTRN TRANVARS:BYTE
EXTRN TRNSEG:WORD
EXTRN VERVAL:WORD
DATARES ENDS
BATARENA SEGMENT PUBLIC PARA ;AC000;
BATARENA ENDS
BATSEG SEGMENT PUBLIC PARA ;AC000;
BATSEG ENDS
ENVARENA SEGMENT PUBLIC PARA ;AC000;
ENVARENA ENDS
ENVIRONMENT SEGMENT PUBLIC PARA ; Default COMMAND environment
ENVIRONMENT ENDS
INIT SEGMENT PUBLIC PARA
EXTRN envsiz:word
EXTRN oldenv:word
EXTRN resetenv:byte
EXTRN usedenv:word
INIT ENDS
TAIL SEGMENT PUBLIC PARA
TAIL ENDS
TRANCODE SEGMENT PUBLIC PARA
TRANCODE ENDS
TRANDATA SEGMENT PUBLIC BYTE
EXTRN TRANDATAEND:BYTE
TRANDATA ENDS
TRANSPACE SEGMENT PUBLIC BYTE
EXTRN TRANSPACEEND:BYTE
EXTRN HEADCALL:DWORD
TRANSPACE ENDS
TRANTAIL SEGMENT PUBLIC PARA
TRANTAIL ENDS
RESGROUP GROUP CODERES,DATARES,BATARENA,BATSEG,ENVARENA,ENVIRONMENT,INIT,TAIL
TRANGROUP GROUP TRANCODE,TRANDATA,TRANSPACE,TRANTAIL
; START OF RESIDENT PORTION
CODERES SEGMENT PUBLIC BYTE ;AC000;
PUBLIC CHKSUM
PUBLIC endinit
PUBLIC GETCOMDSK2
PUBLIC INT_2E
PUBLIC LOADCOM
PUBLIC LODCOM
PUBLIC LODCOM1
PUBLIC RESTHAND
PUBLIC SAVHAND
PUBLIC SETVECT
PUBLIC THEADFIX
PUBLIC TREMCHECK
PUBLIC tjmp
ASSUME CS:RESGROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
EXTRN contc:near
EXTRN DSKERR:NEAR
EXTRN rstack:word
;
; If we cannot allocate enough memory for the transient or there was some
; other allocation error, we display a message and then die.
;
BADMEMERR: ; Allocation error loading transient
MOV DX,BMEMMES ;AC000; get message number
FATALC:
PUSH CS
POP DS
ASSUME DS:ResGroup
invoke RPRINT
;
; If this is NOT a permanent (top-level) COMMAND, then we exit; we can't do
; anything else!
;
CMP PERMCOM,0
JZ FATALRET
;
; We are a permanent command. If we are in the process of the magic interrupt
; (Singlecom) then exit too.
;
CMP SINGLECOM,0 ; If PERMCOM and SINGLECOM
JNZ FATALRET ; Must take INT_2E exit
;
; Permanent command. We can't do ANYthing except halt.
;
MOV DX,HALTMES ;AC000; get message number
invoke RPRINT
STI
STALL:
JMP STALL ; Crash the system nicely
FATALRET:
MOV DX,FRETMES ;AC000; get message number
invoke RPRINT
FATALRET2:
CMP [PERMCOM],0 ; If we get here and PERMCOM,
JNZ RET_2E ; must be INT_2E
invoke reset_msg_pointers ;AN000; reset critical & parse error messages
MOV AX,[PARENT]
MOV WORD PTR CS:[PDB_Parent_PID],AX
MOV AX,WORD PTR OldTerm
MOV WORD PTR CS:[PDB_Exit],AX
MOV AX,WORD PTR OldTerm+2
MOV WORD PTR CS:[PDB_Exit+2],AX
MOV AX,(EXIT SHL 8) ; Return to lower level
INT int_command
RET_2E:
PUSH CS
POP DS
ASSUME DS:RESGROUP,ES:NOTHING,SS:NOTHING
MOV [SINGLECOM],0 ; Turn off singlecom
MOV ES,[RES_TPA]
MOV AH,DEALLOC
INT int_command ; Free up space used by transient
MOV BX,[SAVE_PDB]
MOV AH,SET_CURRENT_PDB
INT int_command ; Current process is user
MOV AX,[RETCODE]
CMP [EXTCOM],0
JNZ GOTECODE
XOR AX,AX ; Internals always return 0
GOTECODE:
MOV [EXTCOM],1 ; Force external
JMP [INT_2E_RET] ;"IRET"
INT_2E: ; Magic command executer
ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
POP WORD PTR [INT_2E_RET]
POP WORD PTR [INT_2E_RET+2] ;Get return address
POP AX ;Chuck flags
PUSH CS
POP ES
MOV DI,80H
MOV CX,64
REP MOVSW
MOV AH,GET_CURRENT_PDB
INT int_command ; Get user's header
MOV [SAVE_PDB],BX
MOV AH,SET_CURRENT_PDB
MOV BX,CS
INT int_command ; Current process is me
MOV [SINGLECOM],81H
MOV [EXTCOM],1 ; Make sure this case forced
LODCOM: ; Termination handler
CMP [EXTCOM],0
jz lodcom1 ; if internal, memory already allocated
mov bx,0ffffh
MOV AH,ALLOC
INT int_command
CALL SetSize
ADD AX,20H
CMP BX,AX ; Is less than 512 byte buffer worth it?
JNC MEMOK
BADMEMERRJ:
JMP BADMEMERR ; Not enough memory
; SetSize - get transient size in paragraphs
Procedure SetSize,NEAR
MOV AX,OFFSET TRANGROUP:TRANSPACEEND + 15
MOV CL,4
SHR AX,CL
return
EndProc SetSize
MEMOK:
MOV AH,ALLOC
INT int_command
JC BADMEMERRJ ; Memory arenas probably trashed
MOV [EXTCOM],0 ; Flag not to ALLOC again
MOV [RES_TPA], AX ; Save current TPA segment
AND AX, 0F000H
ADD AX, 01000H ; Round up to next 64K boundary
JC BAD_TPA ; Memory wrap if carry set
; Make sure that new boundary is within allocated range
MOV DX, [RES_TPA]
ADD DX, BX ; Compute maximum address
CMP DX, AX ; Is 64K address out of range?
JBE BAD_TPA
; Must have 64K of usable space.
SUB DX, AX ; Compute the usable space
CMP DX, 01000H ; Is space >= 64K ?
JAE LTPASET
BAD_TPA:
MOV AX, [RES_TPA]
LTPASET:
MOV [LTPA],AX ; Usable TPA is 64k buffer aligned
MOV AX, [RES_TPA] ; Actual TPA is buffer allocated
ADD BX,AX
MOV [MEMSIZ],BX
CALL SetSize
SUB BX,AX
MOV [TRNSEG],BX ; Transient starts here
LODCOM1:
MOV AX,CS
MOV SS,AX
ASSUME SS:RESGROUP
MOV SP,OFFSET RESGROUP:RSTACK
MOV DS,AX
ASSUME DS:RESGROUP
CALL HEADFIX ; Make sure files closed stdin and stdout restored
XOR BP,BP ; Flag command ok
MOV AX,-1
XCHG AX,[VERVAL]
CMP AX,-1
JZ NOSETVER
MOV AH,SET_VERIFY_ON_WRITE ; AL has correct value
INT int_command
NOSETVER:
CMP [SINGLECOM],-1
JNZ NOSNG
JMP FATALRET2 ; We have finished the single command
NOSNG:
CALL CHKSUM ; Check the transient
CMP DX,[SUM]
JZ HAVCOM ; Transient OK
BOGUS_COM:
MOV [LOADING],1 ; Flag DSKERR routine
CALL LOADCOM
CHKSAME:
CALL CHKSUM
CMP DX,[SUM]
JZ HAVCOM ; Same COMMAND
ALSO_BOGUS:
CALL WRONGCOM
JMP SHORT CHKSAME
HAVCOM:
MOV AX,CHAR_OPER SHL 8
INT int_command
MOV [RSWITCHAR],DL
CMP DL,'/'
JNZ USESLASH
mov cl,'\'
MOV [RDIRCHAR],cl ; Select alt path separator
USESLASH:
MOV [LOADING],0 ; Flag to DSKERR
MOV SI,OFFSET RESGROUP:TRANVARS
MOV DI,OFFSET TRANGROUP:HEADCALL
MOV ES,[TRNSEG]
CLD
MOV CX,OFFSET ResGroup:TranVarEnd
SUB CX,SI
REP MOVSB ; Transfer INFO to transient
MOV AX,[MEMSIZ]
MOV WORD PTR DS:[PDB_block_len],AX ; Adjust my own header
; Just a public label so this spot can be found easily.
tjmp:
JMP DWORD PTR [TRANS]
; Far call to REMCHECK for TRANSIENT
TREMCHECK PROC FAR
CALL REMCHECK
RET
TREMCHECK ENDP
REMCHECK:
;All registers preserved. Returns ZF set if media removable, NZ if fixed
; AL is drive (0=DEF, 1=A,...).
SaveReg <AX,BX>
MOV BX,AX
MOV AX,(IOCTL SHL 8) + 8
INT 21h
jnc RCcont ; If an error occurred, assume the media
or ax,ax ; is NON-removable.
; AX contains the non-zero error code
; from the INT 21, so the OR AX,AX sets
; Non-zero. This behavior makes Network
; drives appear to be non-removable.
jmp SHORT ResRegs
RCcont:
AND AX,1
NOT AX
ResRegs:
RestoreReg <BX,AX>
return
; Far call to HEADFIX for TRANSIENT
THEADFIX PROC FAR
CALL HEADFIX
RET
THEADFIX ENDP
HEADFIX:
CALL SETVECT
XOR BX,BX ; Clean up header
MOV CX,[IO_SAVE]
MOV DX,WORD PTR DS:[PDB_JFN_Table]
CMP CL,DL
JZ CHK1 ; Stdin matches
MOV AH,CLOSE
INT int_command
MOV DS:[PDB_JFN_Table],CL ; Restore stdin
CHK1:
INC BX
CMP CH,DH ; Stdout matches
JZ CHKOTHERHAND
MOV AH,CLOSE
INT int_command
MOV DS:[PDB_JFN_Table+1],CH ; Restore stdout
CHKOTHERHAND:
ADD BX,4 ; Skip 2,3,4
MOV CX,FilPerProc - 5 ; Already done 0,1,2,3,4
CLOSELOOP:
MOV AH,CLOSE
INT int_command
INC BX
LOOP CLOSELOOP
push ds ;AN020; save data segment
push cs ;AN020; Get local segment into DS
pop ds ;AN020;
cmp append_flag,-1 ;AN020; Do we need to reset APPEND?
jnz append_fix_end ;AN030; no - just exit
mov ax,AppendSetState ;AN020; Set the state of Append
mov bx,Append_state ;AN020; back to the original state
int 2fh ;AN020;
mov append_flag,0 ;AN020; Set append flag to invalid
append_fix_end: ;AN030;
cmp [rsrc_xa_seg],no_xa_seg ;AN030; Is there any active XA segment?
jz xa_fix_end ;AN030; no - exit
push es ;AN030; Yes - deallocate it
mov es,rsrc_xa_seg ;AN030;
mov ax,(Dealloc SHL 8) ;AN030; Deallocate memory call
int int_command ;AN030;
pop es ;AN030;
mov [rsrc_xa_seg],no_xa_seg ;AN030; reset to no segment
xa_fix_end:
pop ds ;AN020; get data segment back
return
SAVHAND:
ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
PUSH DS
PUSH BX ; Set stdin to sterr, stdout to stderr
PUSH AX
MOV AH,GET_CURRENT_PDB
INT int_command ; Get user's header
MOV DS,BX
LDS BX,DS:[PDB_JFN_POINTER] ; get pointer to JFN table...
MOV AX,WORD PTR DS:[BX]
MOV [HANDLE01],AX ; Save user's stdin, stdout
MOV AL,CS:[PDB_JFN_Table+2] ; get COMMAND stderr
MOV AH,AL
MOV WORD PTR DS:[BX],AX ; Dup stderr
POP AX
POP BX
POP DS
return
ASSUME DS:RESGROUP
GETCOMDSK2:
CALL GETCOMDSK
JMP LODCOM1 ; Memory already allocated
RESTHAND:
PUSH DS
PUSH BX ; Restore stdin, stdout to user
PUSH AX
MOV AH,GET_CURRENT_PDB
INT int_command ; Point to user's header
MOV AX,[HANDLE01]
MOV DS,BX
ASSUME DS:NOTHING
LDS BX,DS:[PDB_JFN_POINTER] ; get pointer to JFN table...
MOV WORD PTR DS:[BX],AX ; Stuff his old 0 and 1
POP AX
POP BX
POP DS
return
ASSUME DS:RESGROUP,SS:RESGROUP
HOPELESS:
MOV DX,COMBAD ;AC000;
JMP FATALC
GETCOMDSK:
mov al,[comdrv]
CALL REMCHECK
jNZ HOPELESS ;Non-removable media
getcomdsk3:
cmp dx,combad ;AC000;
jnz getcomdsk4
mov dx,combad ;AN000; get message number
invoke RPRINT ; Say command is invalid
getcomdsk4:
cmp [cpdrv],0 ;g is there a drive in the comspec?
jnz users_drive ;g yes - use it
mov ah,Get_default_drive ;g use default drive
int 21h ;g
add al,"A" ;g convert to ascii
mov [cpdrv],al ;g put in message to print out
users_drive: ;g
mov dx,comprmt1 ;AC000; Prompt for diskette containing command
IF tokenized
or byte ptr [si],80h
endif
MOV AL,COMPRMT1_SUBST ;AN000; get number of substitutions
MOV SI,OFFSET RESGROUP:COMPRMT1_BLOCK ;AN000; get address of subst block
MOV NUMBER_SUBST,AL ;AN000;
invoke rprint
if tokenized
and byte ptr [si],NOT 80h
endif
mov dx,prompt ;AN047; Tell the user to strike a key
invoke rprint ;AN047;
CALL GetRawFlushedByte
return
; flush world and get raw input
GetRawFlushedByte:
MOV AX,(STD_CON_INPUT_FLUSH SHL 8) OR RAW_CON_INPUT
INT int_command ; Get char without testing or echo
MOV AX,(STD_CON_INPUT_FLUSH SHL 8) + 0
INT int_command
return
LOADCOM: ; Load in transient
INC BP ; Flag command read
MOV DX,OFFSET RESGROUP:COMSPEC
MOV AX,OPEN SHL 8
INT int_command ; Open COMMAND.COM
JNC READCOM
CMP AX,error_too_many_open_files
JNZ TRYDOOPEN
MOV DX,NOHANDMES ;AC000;
JMP FATALC ; Fatal, will never find a handle
TRYDOOPEN:
CALL GETCOMDSK
JMP LOADCOM
READCOM:
MOV BX,AX ; Handle
MOV DX,OFFSET RESGROUP:TRANSTART
XOR CX,CX ; Seek loc
MOV AX,LSEEK SHL 8
INT int_command
JC WRONGCOM1
MOV CX,OFFSET TRANGROUP:TRANSPACEEND - 100H
PUSH DS
MOV DS,[TRNSEG]
ASSUME DS:NOTHING
MOV DX,100H
MOV AH,READ
INT int_command
POP DS
ASSUME DS:RESGROUP
WRONGCOM1:
PUSHF
PUSH AX
MOV AH,CLOSE
INT int_command ; Close COMMAND.COM
POP AX
POPF
JC WRONGCOM ; If error on READ
CMP AX,CX
retz ; Size matched
WRONGCOM:
MOV DX,COMBAD ;AC000;
CALL GETCOMDSK
JMP LOADCOM ; Try again
CHKSUM: ; Compute transient checksum
PUSH DS
MOV DS,[TRNSEG]
MOV SI,100H
MOV CX,OFFSET TRANGROUP:TranDataEnd - 100H
CHECK_SUM:
CLD
SHR CX,1
XOR DX,DX
CHK:
LODSW
ADD DX,AX
ADC DX,0
LOOP CHK
POP DS
return
SETVECT: ; Set useful vectors
MOV DX,OFFSET RESGROUP:LODCOM
MOV AX,(SET_INTERRUPT_VECTOR SHL 8) OR 22H
MOV WORD PTR DS:[PDB_EXIT],DX
MOV WORD PTR DS:[PDB_EXIT+2],DS
INT int_command
MOV DX,OFFSET RESGROUP:CONTC
INC AL
INT int_command
MOV DX,OFFSET RESGROUP:DSKERR
INC AL
INT int_command
return
;
; This routine moves the environment to a newly allocated segment
; at the end of initialization
;
ENDINIT:
push ds ;g save segments
push es ;g
push cs ;g get resident segment to DS
pop ds ;g
ASSUME DS:RESGROUP
mov cx,usedenv ;g get number of bytes to move
mov es,envirseg ;g get target environment segment
ASSUME ES:NOTHING
mov DS:[PDB_environ],es ;g put new environment in my header ;AM067;
mov ds,oldenv ;g source environment segment ;AM067;
ASSUME DS:NOTHING ;AM067;
xor si,si ;g set up offsets to start of segments ;AM067;
xor di,di ;g ;AM067;
cld ;g make sure we move the right way! ;AM067;
rep movsb ;g move it ;AM067;
xor ax,ax ;g ;AM067;
stosb ;g make sure there are double 0 at end ;AM067;
cmp resetenv,1 ;eg Do we need to setblock to env end?
jnz noreset ;eg no - we already did it
mov bx,envsiz ;eg get size of environment in paragraphs
push es ;eg save environment - just to make sure
mov ah,SETBLOCK ;eg
int int_command ;eg
pop es ;eg
noreset:
mov InitFlag,FALSE ;AC042; Turn off init flag
pop es ;g
pop ds ;g
jmp lodcom ;g allocate transient
CODERES ENDS
; This TAIL segment is used to produce a PARA aligned label in the resident
; group which is the location where the transient segments will be loaded
; initial.
TAIL SEGMENT PUBLIC PARA
ORG 0
PUBLIC TranStart
TRANSTART LABEL WORD
TAIL ENDS
; This TAIL segment is used to produce a PARA aligned label in the transient
; group which is the location where the exec segments will be loaded
; initial.
TRANTAIL SEGMENT PUBLIC PARA
ORG 0
EXECSTART LABEL WORD
TRANTAIL ENDS
END