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