; ; This version of COMMAND is divided into three distinct parts. First is the ; resident portion, which includes handlers for interrupts 22H (terminate), ; 23H (Cntrl-C), 24H (fatal error), and 27H (stay resident); it also has code ; to test and, if necessary, reload the transient portion. Following the ; resident is the init code, which is overwritten after use. Then comes the ; transient portion, which includes all command processing (whether internal ; or external). The transient portion loads at the end of physical memory, ; and it may be overlayed by programs that need as much memory as possible. ; When the resident portion of command regains control from a user program, a ; checksum is performed on the transient portion to see if it must be ; reloaded. Thus programs which do not need maximum memory will save the time ; required to reload COMMAND when they terminate. ; ; REV 1.17 ; 05/19/82 Fixed bug in BADEXE error (relocation error must return to ; resident since the EXELOAD may have overwritten the transient. ; REV 1.18 ; 05/21/82 IBM version always looks on drive A ; MSVER always looks on default drive ; ; REV 1.19 ; 06/03/82 Drive spec now entered in command line ; 06/07/82 Added VER command (print DOS version number) and VOL command ; (print volume label) ; REV 1.20 ; 06/09/82 Prints "directory" after directories ; 06/13/82 MKDIR, CHDIR, PWD, RMDIR added ; REV 1.50 ; Some code for new 2.0 DOS, sort of HACKey. Not enough time to ; do it right. ; REV 1.70 ; EXEC used to fork off new processes ; REV 1.80 ; C switch for single command execution ; REV 1.90 ; Batch uses XENIX ; Rev 2.00 ; Lots of neato stuff ; IBM 2.00 level ; Rev 2.01 ; 'D' switch for date time suppression ; Rev 2.02 ; Default userpath is NUL rather than BIN ; same as IBM ; COMMAND split into pieces ; Rev 2.10 ; INTERNATIONAL SUPPORT ; Rev 2.11 COMMAND split into more pieces INCLUDE DOSSYM.ASM INCLUDE DEVSYM.ASM INCLUDE COMSW.ASM INCLUDE COMEQU.ASM CODERES SEGMENT PUBLIC CODERES ENDS DATARES SEGMENT PUBLIC BYTE EXTRN COMBAD:BYTE,NEEDCOM:BYTE,DRVMSG:BYTE EXTRN DEFMSG:BYTE,PROMPT:BYTE,EXECEMES:BYTE,EXEBAD:BYTE EXTRN TOOBIG:BYTE,NOCOM:BYTE,RBADNAM:BYTE,INT_2E_RET:DWORD EXTRN NOHANDMES:BYTE,BMEMMES:BYTE,HALTMES:BYTE,FRETMES:BYTE EXTRN PARENT:WORD,HANDLE01:WORD,LOADING:BYTE,BATCH:WORD EXTRN TRNSEG:WORD,COMDRV:BYTE,MEMSIZ:WORD,SUM:WORD,EXTCOM:BYTE EXTRN IO_SAVE:WORD,PERMCOM:BYTE,SINGLECOM:WORD,VERVAL:WORD EXTRN PIPEFLAG:BYTE,SAVE_PDB:WORD,COMSPEC:BYTE,TRANS:WORD EXTRN TRANVARS:BYTE,LTPA:WORD,RSWITCHAR:BYTE,RDIRCHAR:BYTE EXTRN RETCODE:WORD,FORFLAG:BYTE IF IBMVER EXTRN SYS_CALL:DWORD,ZEXEC:WORD,EXESEG:WORD,EXESUM:WORD EXTRN USER_SS:WORD,USER_SP:WORD ENDIF DATARES ENDS ENVIRONMENT SEGMENT PUBLIC PARA ; Default COMMAND environment ENVIRONMENT ENDS INIT SEGMENT PUBLIC PARA EXTRN CONPROC:NEAR 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,HEADCALL:DWORD TRANSPACE ENDS TRANTAIL SEGMENT PUBLIC PARA TRANTAIL ENDS ZEXEC_CODE SEGMENT PUBLIC PARA ZEXEC_CODE ENDS ZEXEC_DATA SEGMENT PUBLIC BYTE ZEXEC_DATA ENDS RESGROUP GROUP CODERES,DATARES,ENVIRONMENT,INIT,TAIL TRANGROUP GROUP TRANCODE,TRANDATA,TRANSPACE,TRANTAIL EGROUP GROUP ZEXEC_CODE,ZEXEC_DATA ENVIRONMENT SEGMENT PUBLIC PARA ; Default COMMAND environment PUBLIC ECOMSPEC,ENVIREND,PATHSTRING ORG 0 ENVARENA DB 10H DUP (?) ; Pad for mem arena PATHSTRING DB "PATH=" USERPATH LABEL BYTE DB 0 ; Null path DB "COMSPEC=" ECOMSPEC DB "/COMMAND.COM" DB 134 DUP (0) ENVIREND LABEL BYTE ENVIRONSIZ EQU $-PATHSTRING ENVIRONSIZ2 EQU $-ECOMSPEC ENVIRONMENT ENDS ; START OF RESIDENT PORTION CODERES SEGMENT PUBLIC PUBLIC GETCOMDSK2,LODCOM,THEADFIX,CONTCTERM,LOADCOM,INT_2E,LODCOM1 PUBLIC CHKSUM,SETVECT,EXT_EXEC,TREMCHECK,RESTHAND,CONTC,RSTACK PUBLIC SAVHAND IF IBMVER PUBLIC EXECHK,SYSCALL,EXEC_WAIT ENDIF ASSUME CS:RESGROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING EXTRN RPRINT:NEAR,ASKEND:NEAR,DSKERR:NEAR ORG 0 ZERO = $ ORG 100H PROGSTART: JMP RESGROUP:CONPROC DB (80H - 3) DUP (?) RSTACK LABEL WORD IF IBMVER SYSCALL: CMP AH,EXEC JZ do_exec JMP DWORD PTR [SYS_CALL] do_exec: PUSH ES PUSH DS PUSH BP PUSH DI PUSH SI PUSH DX PUSH CX PUSH BX PUSH AX MOV [user_ss],SS MOV [user_sp],SP ; ; are we running on RSTACK already? ; PUSH CS POP BX ; BX <- CS PUSH SS POP AX ; AX <- SS CMP AX,BX ; IF AX == BX then no stack switch! JZ Get_mem MOV SS,BX ASSUME SS:RESGROUP MOV SP,OFFSET RESGROUP:RSTACK Get_mem: MOV BX,0FFFFH ; allocate all of memory MOV AH,ALLOC INT int_command MOV AX,OFFSET EGROUP:ZEXECDATAEND + 15 MOV CL,4 SHR AX,CL MOV CX,AX ; Save in CX CMP BX,AX ; enough for EXEC? JB EXECMER ; nope... cry MOV AH,ALLOC INT int_command JC EXECMER ; Memory arenas probably trashed ADD BX,AX MOV [MEMSIZ],BX SUB BX,CX MOV [EXESEG],BX ; exec MOV ES,AX MOV AH,DEALLOC INT int_command PUSH CS POP DS ASSUME DS:RESGROUP CALL EXECHK CMP DX,[EXESUM] JZ HAVEXEC ; EXEC OK MOV DX,OFFSET RESGROUP:COMSPEC MOV AX,OPEN SHL 8 INT int_command ; Open COMMAND.COM JC EXECMER MOV BX,AX ; Handle MOV DX,OFFSET RESGROUP:TRANSTART ADD DX,OFFSET TRANGROUP:EXECSTART - 100H XOR CX,CX ; Seek loc MOV AX,LSEEK SHL 8 INT int_command MOV CX,OFFSET EGROUP:ZEXECCODEEND MOV DS,[EXESEG] ASSUME DS:NOTHING MOV AH,READ INT int_command PUSH AX MOV AH,CLOSE INT int_command ; Close COMMAND.COM POP CX CMP CX,OFFSET EGROUP:ZEXECCODEEND JNZ EXECMER ; Size matched CALL EXECHK CMP DX,[EXESUM] JNZ EXECMER HAVEXEC: MOV [LOADING],0 ; Flag to DSKERR CALL DWORD PTR [ZEXEC] JMP SHORT EXECRET execmer: LDS SI,DWORD PTR [user_Sp] MOV [SI.user_AX],exec_not_enough_memory PUSH [SI.user_F] POPF STC PUSHF POP [SI.user_F] execret: MOV SS,[user_SS] ASSUME SS:NOTHING MOV SP,[user_SP] POP AX ; PUSH ES POP BX ; PUSH DS POP CX ; PUSH BP POP DX ; PUSH DI POP SI ; PUSH SI POP DI ; PUSH DX POP BP ; PUSH CX POP DS ; PUSH BX POP ES ; PUSH AX IRET EXECHK: ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING PUSH DS MOV DS,[EXESEG] MOV CX,OFFSET EGROUP:ZEXECCODEEND XOR SI,SI JMP CHECK_SUM ENDIF EXEC_ERR: ; Select the correct error message MOV DX,OFFSET RESGROUP:RBADNAM CMP AX,exec_file_not_found JZ GOTEXECEMES CMP AX,error_access_denied JZ GOTEXECEMES MOV DX,OFFSET RESGROUP:TOOBIG CMP AX,exec_not_enough_memory JZ GOTEXECEMES MOV DX,OFFSET RESGROUP:EXEBAD CMP AX,exec_bad_format JZ GOTEXECEMES MOV DX,OFFSET RESGROUP:EXECEMES GOTEXECEMES: PUSH CS POP DS CALL RPRINT JMP SHORT NOEXEC EXT_EXEC: ; ; we are now running in free space. anything we do from here ; on may get trashed. Move the stack (also in free space) to ; allocated space because since EXEC restores the stack, ; somebody may trash what is on the stack. ; MOV CX,CS MOV SS,CX MOV SP,OFFSET RESGROUP:RSTACK ; ; Oops!! We have to make sure that the EXEC code doesn't blop a newstack! ; ; INT int_command ; Do the EXEC JC EXEC_ERR ; EXEC failed EXEC_WAIT: MOV AH,WAIT INT int_command ; Get the return code MOV [RETCODE],AX NOEXEC: JMP LODCOM CONTC: STI MOV AX,CS MOV DS,AX ASSUME DS:RESGROUP MOV AH,DISK_RESET INT int_command ; Reset disks in case files were open TEST [BATCH],-1 JZ CONTCTERM JMP ASKEND ; See if user wants to terminate batch CONTCTERM: XOR BP,BP ; Indicate no read MOV [FORFLAG],0 ; Turn off for processing MOV [PIPEFLAG],0 ; Turn off any pipe CMP [SINGLECOM],0 ; See if we need to set SINGLECOM JZ NOSETSING MOV [SINGLECOM],-1 ; Cause termination on pipe, batch, for NOSETSING: CMP [EXTCOM],0 JNZ DODAB ; Internal ^C JMP LODCOM1 DODAB: STC ; Tell DOS to abort ZZY PROC FAR RET ; Leave flags on stack ZZY ENDP BADMEMERR: ; Allocation error loading transient MOV DX,OFFSET RESGROUP:BMEMMES FATALC: PUSH CS POP DS CALL RPRINT CMP [PERMCOM],0 JZ FATALRET CMP [SINGLECOM],0 ; If PERMCOM and SINGLECOM JNZ FATALRET ; Must take INT_2E exit MOV DX,OFFSET RESGROUP:HALTMES CALL RPRINT STALL: JMP STALL ; Crash the system nicely FATALRET: MOV DX,OFFSET RESGROUP:FRETMES CALL RPRINT FATALRET2: CMP [PERMCOM],0 ; If we get here and PERMCOM, JNZ RET_2E ; must be INT_2E IF IBM LDS DX,DWORD PTR [SYS_CALL] ASSUME DS:NOTHING MOV AX,(SET_INTERRUPT_VECTOR SHL 8) + INT_COMMAND INT int_command ENDIF MOV AX,[PARENT] MOV WORD PTR CS:[PDB_Parent_PID],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,[LTPA] 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 MOV AX,OFFSET TRANGROUP:TRANSPACEEND + 15 MOV CL,4 SHR AX,CL IF IBM PUSH AX MOV AX,OFFSET EGROUP:ZEXECDATAEND + 15 MOV CL,4 SHR AX,CL POP CX ADD AX,CX ENDIF ADD AX,20H CMP BX,AX ; Is less than 512 byte buffer worth it? JNC MEMOK BADMEMERRJ: JMP BADMEMERR ; Not enough memory MEMOK: MOV AH,ALLOC INT int_command JC BADMEMERRJ ; Memory arenas probably trashed MOV [EXTCOM],0 ; Flag not to ALLOC again MOV [LTPA],AX ; New TPA is base just allocated ADD BX,AX MOV [MEMSIZ],BX MOV AX,OFFSET TRANGROUP:TRANSPACEEND + 15 MOV CL,4 SHR AX,CL IF IBM PUSH AX MOV AX,OFFSET EGROUP:ZEXECDATAEND + 15 MOV CL,4 SHR AX,CL POP CX ADD AX,CX ENDIF 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 SETVECT IF IBMVER CALL EXECHK ; Check exe loader CMP DX,[EXESUM] JNZ BOGUS_COM ENDIF CALL CHKSUM ; Check the transient CMP DX,[SUM] JZ HAVCOM ; Transient OK BOGUS_COM: MOV [LOADING],1 ; Flag DSKERR routine CALL LOADCOM CHKSAME: IF IBMVER CALL EXECHK CMP DX,[EXESUM] JNZ ALSO_BOGUS ENDIF 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 [RDIRCHAR],'\' ; 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,8 REP MOVSW ; Transfer INFO to transient MOV AX,[MEMSIZ] MOV WORD PTR DS:[PDB_block_len],AX ; Adjust my own header JMP DWORD PTR [TRANS] ; Far call to REMCHECK for TRANSIENT TREMCHECK PROC FAR CALL REMCHECK RET TREMCHECK ENDP REMCHECK: ;All registers preserved. Returns zero if media removable, NZ if fixed ; AL is drive (0=DEF, 1=A,...) IF IBM PUSH AX OR AL,AL JNZ GOTDRV2 MOV AH,GET_DEFAULT_DRIVE INT int_command INC AL ;A=1 GOTDRV2: PUSH BX MOV BL,AL INT 11H ;IBM EQUIP CALL ROL AL,1 ROL AL,1 AND AL,3 JNZ NOT_SINGLE INC AL NOT_SINGLE: INC AL ; AL is now MAX floppy # CMP BL,AL POP BX JBE SETREM ; Is an IBM floppy and so is removable OR AL,AL ; Know AL is non-zero JMP SHORT SETNREM SETREM: ELSE PUSH AX ENDIF XOR AX,AX ;Zero IF IBM SETNREM: ENDIF POP AX RET ; Far call to HEADFIX for TRANSIENT THEADFIX PROC FAR CALL HEADFIX RET THEADFIX ENDP HEADFIX: 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 RET 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 MOV AX,WORD PTR DS:[PDB_JFN_Table] MOV [HANDLE01],AX ; Save user's stdin, stdout MOV AL,DS:[PDB_JFN_Table+2] MOV AH,AL MOV WORD PTR DS:[PDB_JFN_Table],AX ; Dup stderr POP AX POP BX POP DS RET 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 MOV WORD PTR DS:[PDB_JFN_Table],AX ; Stuff his old 0 and 1 POP AX POP BX POP DS RET ASSUME DS:RESGROUP,SS:RESGROUP HOPELESS: MOV DX,OFFSET RESGROUP:NOCOM JMP FATALC GETCOMDSK: MOV DX,OFFSET RESGROUP:NEEDCOM GETCOMDSK3: MOV AL,[COMDRV] CALL REMCHECK JNZ HOPELESS ;Non-removable media CALL RPRINT MOV DX,OFFSET RESGROUP:DRVMSG CMP [COMDRV],0 JNZ GETCOM1 MOV DX,OFFSET RESGROUP:DEFMSG GETCOM1: CALL RPRINT MOV DX,OFFSET RESGROUP:PROMPT CALL RPRINT CALL GetRawFlushedByte RET ; 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,open_too_many_open_files JNZ TRYDOOPEN MOV DX,OFFSET RESGROUP:NOHANDMES JMP FATALC ; Fatal, will never find a handle TRYDOOPEN: CALL GETCOMDSK JMP SHORT 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 IF IBM ADD CX,15 AND CX,0FFF0H ADD CX,OFFSET EGROUP:ZEXECCODEEND ENDIF 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 JZ RET10 ; Size matched WRONGCOM: MOV DX,OFFSET RESGROUP:COMBAD CALL GETCOMDSK3 JMP SHORT 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 LOOP CHK POP DS RET10: RET SETVECT: ; Set useful vectors MOV DX,OFFSET RESGROUP:LODCOM MOV AX,(SET_INTERRUPT_VECTOR SHL 8) OR 22H ; Set Terminate address INT int_command MOV DX,OFFSET RESGROUP:CONTC MOV AX,(SET_INTERRUPT_VECTOR SHL 8) OR 23H ; Set Ctrl-C address INT int_command MOV DX,OFFSET RESGROUP:DSKERR MOV AX,(SET_INTERRUPT_VECTOR SHL 8) OR 24H ; Set Hard Disk Error address INT int_command RET 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 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 IF IBMVER INCLUDE EXEC.ASM ENDIF END PROGSTART