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