        page ,132
;       SCCSID = @(#)tcode.asm  1.1 85/05/14
;       SCCSID = @(#)tcode.asm  1.1 85/05/14
TITLE   Part1 COMMAND Transient Routines
;/*
; *                      Microsoft Confidential
; *                      Copyright (C) Microsoft Corporation 1991
; *                      All Rights Reserved.
; */

;
;       Revision History
;       ================
;
;       M025    SR      9/12/90         Removed calls to SetStdInOn,SetStdInOff
;                               SetStdOutOn & SetStdOutOff.
;


.xlist
.xcref
        include comsw.asm
        include dossym.inc
        include syscall.inc
        include comseg.asm
        include comequ.asm
        include cmdsvc.inc
        include mult.inc
        include vint.inc
.list
.cref

Prompt32        equ     1
Start16         equ     0
Return16        equ     1
DOSONLY_YES     equ     1
FOR_TSR         equ     0
FOR_SHELLOUT    equ     1


CODERES         SEGMENT PUBLIC BYTE     ;AC000;
        EXTRN   EXEC_WAIT:NEAR
CODERES ENDS

DATARES         SEGMENT PUBLIC BYTE     ;AC000;
        EXTRN   BATCH:WORD
        EXTRN   CALL_BATCH_FLAG:byte
        EXTRN   CALL_FLAG:BYTE
        EXTRN   ECHOFLAG:BYTE
        EXTRN   envirseg:word
        EXTRN   EXTCOM:BYTE
        EXTRN   FORFLAG:BYTE
        EXTRN   IFFLAG:BYTE
        EXTRN   next_batch:word
        EXTRN   nullflag:byte
        EXTRN   PIPEFILES:BYTE
        EXTRN   PIPEFLAG:BYTE
        EXTRN   RE_OUT_APP:BYTE
        EXTRN   RE_OUTSTR:BYTE
        EXTRN   RESTDIR:BYTE
        EXTRN   SINGLECOM:WORD
        EXTRN   VERVAL:WORD
	EXTRN	SCS_Is_First:BYTE
	EXTRN	SCS_PAUSE:BYTE
        EXTRN   SCS_REENTERED:BYTE
        EXTRN   SCS_FIRSTCOM:BYTE
        EXTRN   SCS_CMDPROMPT:BYTE
        EXTRN   SCS_DOSONLY:BYTE
        EXTRN   SCS_PROMPT16:BYTE
        EXTRN   SCS_FIRSTTSR:BYTE
        EXTRN   RES_RDRINFO:DWORD
        EXTRN   RES_BATSTATUS:BYTE
        EXTRN   crit_msg_off:word       ;AC000;
        EXTRN   crit_msg_seg:word       ;AC000;
        EXTRN   OldTerm:DWORD
        EXTRN   PARENT:WORD
        extrn   RetCode:word
        extrn   Io_Save:word
        extrn   Io_Stderr:byte
        extrn   RES_TPA:WORD            ; YST
	extrn	LTPA:WORD		; YST
DATARES ENDS

TRANDATA        SEGMENT PUBLIC BYTE     ;AC000;
        EXTRN   BadNam_Ptr:word         ;AC000;
        EXTRN   comspec:byte
        EXTRN   NT_INTRNL_CMND:byte
TRANDATA        ENDS

TRANSPACE       SEGMENT PUBLIC BYTE     ;AC000;
        EXTRN   APPEND_EXEC:BYTE        ;AN041;
        EXTRN   ARG1S:WORD
        EXTRN   ARG2S:WORD
        EXTRN   ARGTS:WORD
        EXTRN   BYTCNT:WORD
        EXTRN   COMBUF:BYTE
        EXTRN   COMSW:WORD
        EXTRN   CURDRV:BYTE
        EXTRN   HEADCALL:DWORD
        EXTRN   IDLEN:BYTE
        EXTRN   INTERNATVARS:BYTE
        EXTRN   PARM1:BYTE
        EXTRN   PARM2:BYTE
        EXTRN   RE_INSTR:BYTE
        EXTRN   RESSEG:WORD
        EXTRN   SPECDRV:BYTE
        EXTRN   STACK:WORD
        EXTRN   SWITCHAR:BYTE
        EXTRN   TPA:WORD
        EXTRN   UCOMBUF:BYTE
        EXTRN   USERDIR1:BYTE
        IF  IBM
        EXTRN   ROM_CALL:BYTE
        EXTRN   ROM_CS:WORD
        EXTRN   ROM_IP:WORD
        ENDIF
        EXTRN   ENV_PTR_SEG:WORD
        EXTRN   ENV_SIZE:WORD
        EXTRN   SCS_TSREXIT:WORD
        EXTRN   CMD_PTR_SEG:WORD
        EXTRN   CMD_PTR_OFF:WORD
        EXTRN   CMD_SIZE:WORD
        EXTRN   SCS_EXIT_CODE:WORD
        EXTRN   SCS_RDRINFO:DWORD
        EXTRN   SCS_BATSTATUS:DWORD
        EXTRN   SCS_NUM_DRIVES:WORD
        EXTRN   SCS_CUR_DRIVE:WORD
        EXTRN   SCS_CODEPAGE:WORD
        EXTRN   SCS_STD_HANDLE:WORD
        EXTRN   SCS_STD_BITS:BYTE
        EXTRN   EXECPATH_SEG:WORD
        EXTRN   EXECPATH_OFF:WORD
        EXTRN   EXECPATH_SIZE:WORD
        EXTRN   EXECPATH:BYTE

        EXTRN   TRAN_TPA:WORD
        extrn   transpaceend:byte       ; (YST)

TRANSPACE       ENDS

; (YST)
TAIL segment public para
extrn TranStart:word
TAIL ENDS
; End of (YST)


; ********************************************************************
; START OF TRANSIENT PORTION
; This code is loaded at the end of memory and may be overwritten by
; memory-intensive user programs.

TRANCODE        SEGMENT PUBLIC BYTE     ;AC000;

ASSUME  CS:TRANGROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING

        EXTRN   $EXIT:NEAR
        EXTRN   DRVBAD:NEAR
        EXTRN   EXTERNAL:NEAR
        EXTRN   FNDCOM:NEAR
        EXTRN   FORPROC:NEAR
        EXTRN   PIPEPROC:NEAR
        EXTRN   PIPEPROCSTRT:NEAR
        EXTRN   GETENVSIZ:near

        PUBLIC  COMMAND
        PUBLIC  DOCOM
        PUBLIC  DOCOM1
        PUBLIC  NOPIPEPROC
        PUBLIC  TCOMMAND

        IF  IBM
        PUBLIC  ROM_EXEC
        PUBLIC  ROM_SCAN
        ENDIF


; 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

NLSFUNC_installed       equ    0ffh
KEYB16_installed        equ    0ffh         ; (YST)
CHECK_KEYB16            equ    0AD80h       ; (YST)
set_global_cp           equ    2
get_global_cp           equ    1


        ORG     0
ZERO    =       $

        ORG     100H                            ; Allow for 100H parameter area

SETDRV:
        MOV     AH,SET_DEFAULT_DRIVE
        INT     21h
;
; TCOMMAND is the recycle point in COMMAND.  Nothing is known here.
; No registers (CS:IP) no flags, nothing.
;

TCOMMAND:
        MOV     DS,[RESSEG]
ASSUME  DS:RESGROUP
        MOV     AX,-1
        XCHG    AX,[VERVAL]
        CMP     AX,-1
        JZ      NOSETVER2
        MOV     AH,SET_VERIFY_ON_WRITE          ; AL has correct value
        INT     21h

NOSETVER2:
        CALL    [HEADCALL]                      ; Make sure header fixed
        XOR     BP,BP                           ; Flag transient not read
        CMP     [SINGLECOM],-1
        JNZ     COMMAND

$EXITPREP:
        PUSH    CS
        POP     DS
        JMP     $EXIT                           ; Have finished the single command
ASSUME  DS:NOTHING
;
; Main entry point from resident portion.
;
;   If BP <> 0, then we have just loaded transient portion otherwise we are
;   just beginning the processing of another command.
;

COMMAND:

;
; We are not always sure of the state of the world at this time.  We presume
; worst case and initialize the relevant registers: segments and stack.
;
        ASSUME  CS:TRANGROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
        CLD
        MOV     AX,CS
        FCLI
        MOV     SS,AX
ASSUME  SS:TRANGROUP
        MOV     SP,OFFSET TRANGROUP:STACK
        FSTI
        MOV     ES,AX
        MOV     DS,AX                           ;AN000; set DS to transient
ASSUME  ES:TRANGROUP,DS:TRANGROUP               ;AC000;
        invoke  TSYSLOADMSG                     ;AN000; preload messages
        mov     append_exec,0                   ;AN041; set internal append state off

        MOV     DS,[RESSEG]
ASSUME  DS:RESGROUP

        MOV     [UCOMBUF],COMBUFLEN             ; Init UCOMBUF
        MOV     [COMBUF],COMBUFLEN              ; Init COMBUF (Autoexec doing DATE)

        mov     [EXECPATH_SIZE], 0              ; ntvdm execpath extended

;
; If we have just loaded the transient, then we do NOT need to initialize the
; command buffer.  ????  DO WE NEED TO RESTORE THE USERS DIRECTORY ????  I
; guess not:  the only circumstances in which we reload the command processor
; is after a transient program execution.  In this case, we let the current
; directory lie where it may.
;
        OR      BP,BP                           ; See if just read
        JZ      TESTRDIR                        ; Not read, check user directory
        MOV     WORD PTR [UCOMBUF+1],0D01H      ; Reset buffer
        JMP     SHORT NOSETBUF

TESTRDIR:
        CMP     [RESTDIR],0
        JZ      NOSETBUF                        ; User directory OK
        PUSH    DS
;
; We have an unusual situation to handle.  The user *may* have changed his
; directory as a result of an internal command that got aborted.  Restoring it
; twice may not help us:  the problem may never go away.  We just attempt it
; once and give up.
;
        MOV     [RESTDIR],0                     ; Flag users dirs OK
        PUSH    CS
        POP     DS
ASSUME  DS:TRANGROUP
        MOV     DX,OFFSET TRANGROUP:USERDIR1
        MOV     AH,CHDIR
        INT     21h                     ; Restore users directory
        POP     DS
ASSUME  DS:RESGROUP

NOSETBUF:
        CMP     [PIPEFILES],0
        JZ      NOPCLOSE                        ; Don't bother if they don't exist
        CMP     [PIPEFLAG],0
        JNZ     NOPCLOSE                        ; Don't del if still piping
        INVOKE  PIPEDEL

NOPCLOSE:
        MOV     [EXTCOM],0                      ; Flag internal command
        MOV     AX,CS                           ; Get segment we're in
        MOV     DS,AX
ASSUME  DS:TRANGROUP

        PUSH    AX
        MOV     DX,OFFSET TRANGROUP:INTERNATVARS
        MOV     AX,INTERNATIONAL SHL 8
        INT     21H
        POP     AX
        SUB     AX,[TPA]                        ; AX=size of TPA in paragraphs
        PUSH    BX
        MOV     BX,16
        MUL     BX                              ; DX:AX=size of TPA in bytes
        POP     BX
        OR      DX,DX                           ; See if over 64K
        JZ      SAVSIZ                          ; OK if not
        MOV     AX,-1                           ; If so, limit to 65535 bytes

SAVSIZ:
;
; AX is the number of bytes free in the buffer between the resident and the
; transient with a maximum of 64K-1.  We round this down to a multiple of 512.
;
        CMP     AX,512
        JBE     GotSize
        AND     AX,0FE00h                       ; NOT 511 = NOT 1FF

GotSize:
        MOV     [BYTCNT],AX                     ; Max no. of bytes that can be buffered
        MOV     DS,[RESSEG]                     ; All batch work must use resident seg.
ASSUME  DS:RESGROUP

        TEST    [ECHOFLAG],1
        JZ      GETCOM                          ; Don't do the CRLF
        INVOKE  SINGLETEST
        JB      GETCOM
        TEST    [PIPEFLAG],-1
        JNZ     GETCOM
        TEST    [FORFLAG],-1                    ; G  Don't print prompt in FOR
        JNZ     GETCOM                          ; G
        TEST    [BATCH], -1                     ; G  Don't print prompt if in batch
        JNZ     GETCOM                          ; G
;       INVOKE  CRLF2

GETCOM:
        MOV     CALL_FLAG,0                     ; G Reset call flags
        MOV     CALL_BATCH_FLAG,0               ; G
        MOV     AH,GET_DEFAULT_DRIVE
        INT     21h
        MOV     [CURDRV],AL
        TEST    [PIPEFLAG],-1                   ; Pipe has highest presedence
        JZ      NOPIPE
        JMP     PIPEPROC                        ; Continue the pipeline

NOPIPE:
        TEST    [ECHOFLAG],1
        JZ      NOPDRV                          ; No prompt if echo off
        INVOKE  SINGLETEST
        JB      NOPDRV
        TEST    [FORFLAG],-1                    ; G  Don't print prompt in FOR
        JNZ     NOPDRV                          ; G
        TEST    [BATCH], -1                     ; G  Don't print prompt if in batch
        JNZ     TESTFORBAT                      ; G
;       INVOKE  PRINT_PROMPT                    ; Prompt the user

NOPDRV:
        TEST    [FORFLAG],-1                    ; FOR has next highest precedence
        JZ      TESTFORbat
        JMP     FORPROC                         ; Continue the FOR

TESTFORBAT:
        MOV     [RE_INSTR],0                    ; Turn redirection back off
        MOV     [RE_OUTSTR],0
        MOV     [RE_OUT_APP],0
        MOV     IFFlag,0                        ; no more ifs...
        TEST    [BATCH],-1                      ; Batch has lowest precedence
        JZ      ISNOBAT

;       Bugbug:         MULT_SHELL_GET no longer used?
        push    es                              ;AN000; save ES
        push    ds                              ;AN000; save DS
        mov     ax,mult_shell_get               ;AN000; check to see if SHELL has command
        mov     es,[batch]                      ;AN000; get batch segment
        mov     di,batfile                      ;AN000; get batch file name
        push    cs                              ;AN000; get local segment to DS
        pop     ds                              ;AN000;
        mov     dx,offset trangroup:combuf      ;AN000; pass communications buffer
        int     2fh                             ;AN000; call the shell
        cmp     al,shell_action                 ;AN000; does shell have a commmand?
        pop     ds                              ;AN000; restore DS
        pop     es                              ;AN000; restore ES
        jz      jdocom1                         ;AN000; yes - go process command

        PUSH    DS                              ;G
        INVOKE  READBAT                         ; Continue BATCH
        POP     DS                              ;G
        mov     nullflag,0                      ;G reset no command flag
        TEST    [BATCH],-1                      ;G
        JNZ     JDOCOM1                         ;G if batch still in progress continue
        MOV     BX,NEXT_BATCH                   ;G
        CMP     BX,0                            ;G see if there is a new batch file
        JZ      JDOCOM1                         ;G no - go do command
        MOV     BATCH,BX                        ;G get segment of next batch file
        MOV     NEXT_BATCH,0                    ;G reset next batch
JDOCOM1:
        PUSH    CS                              ;G
        POP     DS                              ;G
        JMP     DoCom1                          ; echoing already done

ISNOBAT:
        CMP     [SINGLECOM],0
        JZ      REGCOM
        MOV     SI,-1
        XCHG    SI,[SINGLECOM]
        MOV     DI,OFFSET TRANGROUP:COMBUF + 2
        XOR     CX,CX

SINGLELOOP:
        LODSB
        STOSB
        INC     CX
        CMP     AL,0DH
        JNZ     SINGLELOOP
        DEC     CX
        PUSH    CS
        POP     DS
ASSUME  DS:TRANGROUP
        MOV     [COMBUF + 1],CL
;
; do NOT issue a trailing CRLF...
;
        JMP     DOCOM1

;
; We have a normal command.
; Printers are a bizarre quantity.  Sometimes they are a stream and
; sometimes they aren't.  At this point, we automatically close all spool
; files and turn on truncation mode.
;

REGCOM:
        MOV     AX,(ServerCall SHL 8) + 9
        INT     21h
        MOV     AX,(ServerCall SHL 8) + 8
        MOV     DL,1
        INT     21h

        MOV     DS,cs:[RESSEG]
        ASSUME  DS:RESGROUP
        cmp     byte ptr [SCS_FIRSTCOM],0
        jnz     first_inst                      ;this is the first instance

        jmp     DoReEnter
DRE_RET:
        MOV     DS,cs:[RESSEG]
        ASSUME  DS:RESGROUP
        xor     ax,ax
	jmp	short do_again

first_inst:
	mov	[SCS_PAUSE],0			; yst 4-5-93
        cmp     [SCS_Is_First],1
        jne     cont_scs
        mov     [SCS_Is_First],0

        SAVE    <SI,BP>
        xor     si,si
        xor     bp,bp
        mov     al,2
        mov     ah,setdpb
        int     21h                             ; resets the TSR bit
        RESTORE <BP,SI>
        xor     ax,ax
        jmp     short do_again

cont_scs:
        SAVE    <AX,SI,BP>
        xor     si,si
        xor     bp,bp
        mov     al,2
        mov     ah,setdpb
        int     21h                             ; resets the TSR bit
        RESTORE <BP,SI,AX>
        jnc     cont_scs2

cont_tsr:
        jmp     tsr_loop
tsr_loop_ret:
        jmp     short cont_scs1

cont_scs2:
        cmp     byte ptr [scs_firsttsr],1
        je      cont_scs1
        jmp     short cont_tsr

cont_scs1:
;       call    free_con

        MOV     DS,cs:[RESSEG]
        ASSUME  DS:RESGROUP
	mov	ax,[retcode]

do_again:
        MOV     DS,cs:[RESSEG]
	ASSUME	DS:RESGROUP
	push	es
	mov	es,[envirseg]
	mov	[ENV_PTR_SEG],es
	call	GETENVSIZ
	pop	es
	push	cs
	pop	ds
	ASSUME	DS:TRANGROUP
	mov	[ENV_SIZE],cx

;; williamh - Jan 11 1993
;; nt expects 16bits exit code while DOS has only 8 bits
;; clear the high byte so that things won't go wrong
	xor	ah, ah


        MOV     [SCS_EXIT_CODE],ax
        mov     ah,19h
        int     21h
        xor     ah,ah
        mov     [SCS_CUR_DRIVE], ax     ; a= 0 , b = 1 etc
	mov	[CMD_SIZE],COMBUFLEN
	MOV	DX,OFFSET TRANGROUP:UCOMBUF

if 0
;       Try to read interactive command line via DOSKey.
;       If that fails, use DOS Buffered Keyboard Input.

        mov     ax,4810h                ; AX = DOSKey Read Line function
        int     2fh
        or      ax,ax
        jz      GotCom                  ; DOSKey gave us a command line
else
        mov     [CMD_PTR_SEG],ds
        mov     [CMD_PTR_OFF],dx

        mov     dx,OFFSET TRANGROUP:EXECPATH
        mov     [EXECPATH_SEG], ds
        mov     [EXECPATH_OFF], dx
        mov     [EXECPATH_SIZE], EXECPATHLEN

        push    es
        mov     es,cs:[RESSEG]
        ASSUME  es:RESGROUP
        mov     dx,word ptr es:[RES_RDRINFO]
        mov     word ptr ds:[SCS_RDRINFO],dx
        mov     dx,word ptr es:[RES_RDRINFO+2]
        mov     word ptr ds:[SCS_RDRINFO+2],dx
        pop     es
        ASSUME  ES:TRANGROUP
        MOV     DX,OFFSET TRANGROUP:ENV_PTR_SEG


        CMDSVC  SVC_CMDGETNEXTCMD               ; DS:DX is the buffer
        jnc     run_cmd

; If carry is set that means our enviornment buffer was smaller. So get
; a big enough buffer and get the command again.
        cmp     ax,32*1024      ; Env size cannot be more than 32k.
        jae     op_fail
        push    ax              ; the new size in bytes for env in bytes
        invoke  FREE_TPA
        pop     bx
        MOV     CL,4
        SHR     BX,CL           ; Convert back to paragraphs
        inc     bx              ; an extra para
        push    es
        push    ds
        MOV     DS,cs:[RESSEG]
        ASSUME  DS:RESGROUP
        mov     es,[envirseg]
        pop     ds
        ASSUME  DS:TRANGROUP
        MOV     CX,ES           ;AN056; Get environment segment
        ADD     CX,BX           ;AN056; Add in size of environment
        ADD     CX,020H         ;AN056; Add in some TPA
        MOV     AX,CS           ;AN056; Get the transient segment
        CMP     CX,AX           ;AN056; Are we hitting the transient?
        JNB     blk_xxx         ;AN056; Yes - don't do it!!!

        push    bx
        MOV     AH,SETBLOCK
        INT     21h
        pop     bx
        jnc     blk_xxx

        mov     ah,ALLOC
        int     21h
        jc      blk_xxx
        push    ax

        mov     ah,DEALLOC
        int     21h

        pop     ax
        push    ds
        MOV     DS,cs:[RESSEG]
        ASSUME  DS:RESGROUP
        mov     [envirseg],ax
        pop     ds
        ASSUME  DS:TRANGROUP
        clc
blk_xxx:
        lahf
        push    ax
        MOV     ES,cs:[RESSEG]
        invoke  ALLOC_TPA
        pop     ax
        sahf
        JC      op_fail1
        pop     es
        xor     ax,ax           ; error code zero, its important
        jmp     do_again
op_fail1:
        pop     es
op_fail:
        mov     ax,error_not_enough_memory
        jmp     do_again

run_cmd:
        push    es
        mov     es,cs:[RESSEG]
        ASSUME  es:RESGROUP
        mov     ax,word ptr ds:[SCS_RDRINFO]
        mov     word ptr es:[RES_RDRINFO],ax
        mov     ax,word ptr ds:[SCS_RDRINFO+2]
        mov     word ptr es:[RES_RDRINFO+2],ax
        mov     ax,word ptr ds:[SCS_BATSTATUS]
        mov     byte ptr es:[RES_BATSTATUS],al
        pop     es
        ASSUME  ES:TRANGROUP
        test    [SCS_STD_HANDLE],ALL_HANDLES
        jz      no_rdr
        test    [SCS_STD_HANDLE],MASK_STDIN
        jz      scs_std_out
        xor     cx,cx                           ; CX = HANDLE_STDIN
        call    alloc_con
        jnc     scs_std_out
rdr_err:
        call    free_con
        mov     ax,error_not_enough_memory
        jmp     do_again

scs_std_out:
        ; Make Sure Stdout is checked before stderr. Its very important.
        test    [SCS_STD_HANDLE],MASK_STDOUT
        jz      scs_std_err
        mov     cx,HANDLE_STDOUT
        call    alloc_con
        jc      rdr_err

scs_std_err:
        test    [SCS_STD_HANDLE],MASK_STDERR
        jz      no_rdr
        mov     cx,HANDLE_STDERR
        call    alloc_con
        jc      rdr_err

no_rdr:
        mov     ah,GetSetCdPg
        mov     al,1
        int     21h
        jc      cdpg_done
        cmp     bx,[SCS_CODEPAGE]
        jz      cdpg_done

        mov     ah,NLSFUNC                      ; see if NLSFUNC installed
        mov     al,0                            ;
        int     2fh                             ;
        cmp     al,NLSFUNC_installed            ;
        jnz     no_nlsf_msg                     ; NO NLSFUNC ; Print message
;       jz      got_nls                         ; Yes - continue
;       mov     dx,offset trangroup:NLSFUNC_ptr ; no - set up error message
;       jmp     short cp_error                  ; error exit

        mov     bx,[SCS_CODEPAGE]               ;SCS Code page
        mov     ah,getsetcdpg                   ;set global code page function
        mov     al,set_global_cp                ;minor - set
        int     21h
        jnc     cdpg_done                       ;no error - exit
nlsf_failed:
; BUGBUG Sudeepb 28-Apr-1992 Putup a message saying NLSFunc failed
no_nlsf_msg:
; BUGBUG Sudeepb 28-Apr-1992 Putup a message saying NLSFunc not installed

cdpg_done:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;							      ;;
;; (YST) 08-Jan-1993 Checking and installing  16-bit KEYB.COM ;;
;;							      ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

       jmp     YST_beg

;; Data for KB16

KEY_LINE db	128 dup (0)	   ; "d:\nt\system32\kb16.com", 0
YST_ARG  db	128 dup (0)	   ; 32, " XX,,d:\nt\system32\keyboard.sys", 0DH


YPAR     dw     0
         dd     0
         dw     005ch, 0
         dw     006ch, 0

KEEP_SS  dw     0
KEEP_SP  dw     0

YST_beg:

      SAVE <BX, ES, DS>			       ; save all regs for INT 2FH

      mov     ax, CHECK_KEYB16		       ; see if KEYB16 installed
      int     2fh
      xor     dx,dx
      cmp     al, KEYB16_installed
      jne     keyb_cont			       ; KEYB16 not installed, DX == 0
      inc     dx			       ; installed DX == 1

keyb_cont:
      RESTORE <DS, ES, BX>

      push    ds
      push    cs
      pop     ds

      mov     si, offset KEY_LINE	       ; name of KB16.COM
      mov     cx, offset YST_ARG	       ; command line

      CMDSVC	SVC_GETKBDLAYOUT	; Call 32-bit API for checking
					; and installing correct layout
      or      dx,dx			; if DX != 0 after BOP then we need
      pop     ds			; to install 16-bit KEYB.COM
      jnz     run_keyb

      jmp     End_keyb			; No installation of KB16.COM

run_keyb:

;; This part run KB16.COM

      SAVE    <DS, BX>

      PUSH    ES			; free TPA for running KB16
      MOV     ES,[TRAN_TPA]
      MOV     AH,DEALLOC
      INT     21h			; Now running in "free" space

      push    cs
      pop     ds
      push    cs
      pop     es

      mov     dx, offset KEY_LINE	; file name
      mov     YPAR+0, 0000H		; keep current enviroment
      mov     YPAR+2, offset YST_ARG	; arguments (options) for KB16.COM
      mov     YPAR+4, ds
      mov     bx, offset YPAR

      mov     KEEP_SS, ss		; Peter Norton suggests to keep
      mov     KEEP_SP, sp		; SS and SP

      mov     ah, 4bh
      xor     al, al

      int     21h			; RUN!

      mov     ss, cs:KEEP_SS		; Restore SS and SP
      mov     sp, cs:KEEP_SP

      POP     ES

      SAVE	<BP>			; We need to restore TSR bit
      xor	si,si			; for running next app.
      xor	bp,bp			; use "undocumented" call.
      mov	al,2
      mov	ah,setdpb
      int	21h			; resets the TSR bit
      RESTORE	<BP>


; Allocate transient again after runnig KB16.
; Copied from TBATCH.ASM
; Modify AX,BX,DX,flags
;

        ASSUME DS:TRANGROUP,ES:RESGROUP

        PUSH    ES
        MOV     ES,cs:[RESSEG]
        MOV     BX,0FFFFH                       ; Re-allocate the transient
        MOV     AH,ALLOC
        INT     21h
        PUSH    BX                              ; Save size of block
        MOV     AH,ALLOC
        INT     21h
;
; Attempt to align TPA on 64K boundary
;
        POP     BX                              ; Restore size of block
        MOV     [RES_TPA], AX                   ; Save segment to beginning of block
        MOV     [TRAN_TPA], AX
;
; Is the segment already aligned on a 64K boundary
;
        MOV     DX, AX                          ; Save segment
        AND     AX, 0FFFH                       ; Test if above boundary
        JNZ     Calc_TPA
        MOV     AX, DX
        AND     AX, 0F000H                      ; Test if multiple of 64K
        JNZ     NOROUND

Calc_TPA:
        MOV     AX, DX
        AND     AX, 0F000H
        ADD     AX, 01000H                      ; Round up to next 64K boundary
        JC      NOROUND                         ; 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?
        JB      NOROUND
;
; Make sure that we won't overwrite the transient
;
        MOV     BX, CS                          ; CS is beginning of transient
        CMP     BX, AX
        JB      NOROUND
;
; The area from the 64K boundary to the beginning of the transient must
; be at least 64K.
;
        SUB     BX, AX
        CMP     BX, 4096                        ; Size greater than 64K?
        JAE     ROUNDDONE

NOROUND:
        MOV     AX, [RES_TPA]

ROUNDDONE:
        MOV     [LTPA],AX                       ; Re-compute everything
        MOV     [TPA],AX
        MOV     BX,AX
        MOV     AX,CS
        SUB     AX,BX
        PUSH    BX
        MOV     BX,16
        MUL     BX
        POP     BX
        OR      DX,DX
        JZ      SAVSIZ2
        MOV     AX,-1

SAVSIZ2:
;
; AX is the number of bytes free in the buffer between the resident and the
; transient with a maximum of 64K-1.  We round this down to a multiple of 512.
;
        CMP     AX,512
        JBE     GotSize1
        AND     AX,0FE00h                       ; NOT 511 = NOT 1FF

GotSize1:
        MOV     [BYTCNT],AX
        POP     ES


; End  Alloc_TPA


        RESTORE <BX, DS>

        CMDSVC  SVC_CMDINITCONSOLE   ; make sure console is turned on

End_keyb:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;								    ;;
;; End of (YST) 08-Jan-1993 Checking and installing 16-bit KEYB.COM ;;
;;								    ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



        mov     dx,[SCS_CUR_DRIVE]
        mov     [curdrv],dl
        mov     ah,0eh
        int     21h
        push    cs
        pop     ds
        MOV     DX,OFFSET TRANGROUP:UCOMBUF

endif

GotCom:
        MOV     CL,[UCOMBUF]
        XOR     CH,CH
        ADD     CX,3
        MOV     SI,OFFSET TRANGROUP:UCOMBUF
        MOV     DI,OFFSET TRANGROUP:COMBUF
        REP     MOVSB                           ; Transfer it to the cooked buffer

;---------------

transpace   segment
    extrn   arg:byte                            ; the arg structure!
transpace   ends
;---------------


DOCOM:
;       INVOKE  CRLF2

DOCOM1:
        INVOKE  PRESCAN                         ; Cook the input buffer
        JZ      NOPIPEPROC
        JMP     PIPEPROCSTRT                    ; Fire up the pipe

nullcomj:
        jmp     nullcom

NOPIPEPROC:
        invoke  parseline
        jnc     OkParse                         ; user error?  or maybe we goofed?

BadParse:
        PUSH    CS
        POP     DS
        MOV     DX,OFFSET TRANGROUP:BADNAM_ptr
        INVOKE  std_eprintf
        JMP     TCOMMAND

OkParse:
        test    arg.argv[0].argflags, MASK wildcard
        jnz     BadParse                        ; ambiguous commands not allowed
        cmp     arg.argvcnt, 0                  ; there WAS a command, wasn't there?
        jz      nullcomj
        cmp     arg.argv[0].arglen, 0           ; probably an unnecessary check...
        jz      nullcomj                        ; guarantees argv[0] at least x<NULL>

        MOV     SI,OFFSET TRANGROUP:COMBUF+2
        MOV     DI,OFFSET TRANGROUP:IDLEN
        MOV     AX,(PARSE_FILE_DESCRIPTOR SHL 8) OR 01H ; Make FCB with blank scan-off
        INT     21h
        mov     BX, arg.argv[0].argpointer
        cmp     WORD PTR [BX], 05c5ch           ; leading "\\" means UNC name
        je      IsUncName
        cmp     BYTE PTR [BX+1],':'             ; was a drive specified?
        jne     short drvgd                     ; no, use default of zero...

        mov     DL, BYTE PTR [BX]               ; pick-up drive letter
        and     DL, NOT 20H                     ; uppercase the sucker
        sub     DL, capital_A                   ; convert it to a drive number, A=0

        CMP     AL,-1                           ; See what PARSE said about our drive letter.
        JZ      drvbadj2                        ; It was invalid.

IsUncName:
        mov     DI, arg.argv[0].argstartel
        cmp     BYTE PTR [DI], 0                ; is there actually a command there?
        jnz     drvgd                           ; if not, we have:  "d:", "d:\", "d:/"
        jmp     setdrv                          ; and set drive to new drive spec

drvbadj2:
        jmp     drvbad

DRVGD:
        MOV     AL,[DI]
        MOV     [SPECDRV],AL
        MOV     AL,' '
        MOV     CX,9
        INC     DI
        REPNE   SCASB                           ; Count no. of letters in command name
        MOV     AL,8
        SUB     AL,CL
        MOV     [IDLEN],AL                      ; IDLEN is truly the length
        MOV     DI,81H
        PUSH    SI

        mov     si, OFFSET TRANGROUP:COMBUF+2   ; Skip over all leading delims
        invoke  scanoff
;SR;
; We are going to skip over the first char always. The logic is that the
;command tail can never start from the first character. The code below is
;trying to figure out the command tail and copy it to the command line
;buffer in the PSP. However, if the first character happens to be a switch
;character and the user given command line is a full 128 bytes, we try to
;copy 128 bytes to the PSP while it can take only 127 chars. This extra
;char overwrites the code and leads to a crash on future commands.
;

        inc     si

do_skipcom:
        lodsb                                   ; move command line pointer over
        invoke  delim                           ; pathname -- have to do it ourselves
        jz      do_skipped                      ; 'cause parse_file_descriptor is dumb
        cmp     AL, 0DH                         ; can't always depend on argv[0].arglen
        jz      do_skipped                      ; to be the same length as the user-
        cmp     AL, [SWITCHAR]                  ; specified command string
        jnz     do_skipcom

do_skipped:
        dec     SI
        XOR     CX,CX

COMTAIL:
        LODSB
        STOSB                                   ; Move command tail to 80H
        CMP     AL,13
        LOOPNZ  COMTAIL
        DEC     DI
        MOV     BP,DI
        NOT     CL
        MOV     BYTE PTR DS:[80H],CL
        POP     SI

;-----
; Some of these comments are sadly at odds with this brave new code.
;-----
; If the command has 0 parameters must check here for
; any switches that might be present.
; SI -> first character after the command.

        mov     DI, arg.argv[0].argsw_word
        mov     [COMSW], DI                     ; ah yes, the old addressing mode problem...
        mov     SI, arg.argv[1 * SIZE argv_ele].argpointer  ; s = argv[1];
        OR      SI,SI                           ;   if (s == NULL)
        JNZ     DoParse
        MOV     SI,BP                           ;       s = bp; (buffer end)

DoParse:
        MOV     DI,FCB
        MOV     AX,(PARSE_FILE_DESCRIPTOR SHL 8) OR 01H
        INT     21h
        MOV     [PARM1],AL                      ; Save result of parse

        mov     DI, arg.argv[1*SIZE argv_ele].argsw_word
        mov     [ARG1S], DI
        mov     SI, arg.argv[2*SIZE argv_ele].argpointer    ; s = argv[2];
        OR      SI,SI                           ;   if (s == NULL)
        JNZ     DoParse2
        MOV     SI,BP                           ;       s = bp; (bufend)1

DoParse2:
        MOV     DI,FCB+10H
        MOV     AX,(PARSE_FILE_DESCRIPTOR SHL 8) OR 01H
        INT     21h                     ; Parse file name
        MOV     [PARM2],AL                      ; Save result

        mov     DI, arg.argv[2*SIZE argv_ele].argsw_word
        mov     [ARG2S], DI
        mov     DI, arg.argv[0].argsw_word
        not     DI                              ; ARGTS doesn't include the flags
        and     DI, arg.argswinfo               ; from COMSW...
        mov     [ARGTS], DI

        MOV     AL,[IDLEN]
        MOV     DL,[SPECDRV]
        or      DL, DL                          ; if a drive was specified...
        jnz     externalj1                      ; it MUST be external, by this time
        dec     al                              ; (I don't know why -- old code did it)
        jmp     fndcom                          ; otherwise, check internal com table

externalj1:
        jmp     external

nullcom:
        mov     [EXECPATH_SIZE], 0
        MOV     DS,cs:[RESSEG]
ASSUME  DS:RESGROUP
        TEST    [BATCH], -1                     ;G Are we in a batch file?
        JZ      nosetflag                       ;G only set flag if in batch
        mov     nullflag,nullcommand            ;G set flag to indicate no command

nosetflag:
        CMP     [SINGLECOM],-1
        JZ      EXITJ
        JMP     GETCOM

EXITJ:
        JMP     $EXITPREP

IF IBM
        include vector.inc
        include pdb.inc
        include arena.inc
        include mshalo.asm
ENDIF

std_invalid macro
        mov     ax,0ffffh
        push    ax
        push    ax
endm

std_valid macro
        push    bx
        push    cx
endm

CleanForStd macro
        add     sp,12
endm

DoReEnter:
        ASSUME  DS:RESGROUP

        cmp     byte ptr [scs_reentered],3
        jne     reent_chk

; Control comes here after command /z ran a non-dos binary and
; we re-entered. We just make scs_reentered as 2 indicating that on next
; time we need to return the exit code to 32bit side.

        mov     byte ptr [scs_reentered],2
        jmp     reent

reent_chk:
        cmp     byte ptr [scs_reentered],2
        jne     reent_cont
        jmp     retcode32

reent_cont:
        cmp     byte ptr [scs_reentered],0
        je      exec_comspec
        jmp     reent_ret

; Control comes here when we have to exec either command.com or
; cmd.exe. This decision depends on scs_cmdprompt. Here we
; mark scs_reentered state as 1 indicating on return to handle
; exit code.

exec_comspec:

        mov     byte ptr [scs_reentered],1
        cmp     byte ptr [scs_cmdprompt],Prompt32
        je      do32bitprompt
        mov     al,Start16      ; parameter to put prompt
        mov     ah,FOR_SHELLOUT
        call    Do16BitPrompt       ; ds is resseg here on return its transseg
        or      al,al               ; return al=0 to run 16bit binary al=1 getnextvdmcommand
        jz      dos_only
        jmp     reent
dos_only:
        jmp     GotCom              ; go run through int21/exec

do32BitPrompt:
        SAVE    <bp,bx,si>
        xor     bx,bx
        mov     si,bx
        mov     bp,bx
        mov     ax,5303h
        int     21h
        jnc     st_stdin
        std_invalid
        jmp     short go_stdout
st_stdin:
        std_valid
go_stdout:
        mov     bx,1
        mov     ax,5303h
        int     21h
        jnc     st_stdout
        std_invalid
        jmp     short go_stderr
st_stdout:
        std_valid
go_stderr:
        mov     bx,2
        mov     ax,5303h
        int     21h
        jnc     st_stderr
        std_invalid
        jmp     short std_done
st_stderr:
        std_valid
std_done:
        mov     bp,sp
        push    es
        mov     es,[envirseg]
        mov     ah,19h
        int     21h
        CMDSVC  SVC_EXECCOMSPEC32
        pop     es
        mov     bp,ax
        lahf
        CleanForStd
        sahf
        mov     ax,bp
        RESTORE <si,bx,bp>

        jc      reent

        jmp     reent_exit

reent_ret:
        ; ds is already set to RESSEG
        cmp     byte ptr [scs_cmdprompt],Prompt32
        je      retcode32
        mov     al,return16
        mov     ah,FOR_SHELLOUT
        call    Do16BitPrompt       ; ds = resseg , on return its transseg
        or      al,al               ; return al=0 to run 16bit binary al=1 getnextvdmcommand
        jnz     reent
        jmp     dos_only

retcode32:
        mov     dx,ds:[retcode]
        mov     ah,19h
        int     21h                             ; al = cur drive
        mov     cx,word ptr ds:[RES_RDRINFO]
        mov     bx,word ptr ds:[RES_RDRINFO+2]  ; bx:cx - rdrinfo
        CMDSVC  SVC_RETURNEXITCODE
        mov     ds:[retcode],0
        jnc     reent_exit
reent:
        push    cs
        pop     ds
        assume  ds:trangroup
        mov     [scs_tsrexit],0
        jmp     DRE_RET

reent_exit:
ASSUME  DS:RESGROUP
        push    ax
        mov     ax,(multdos shl 8 or message_2f);AN060; reset parse message pointers
        mov     dl,set_critical_msg             ;AN000; set up critical error message address
        mov     di,crit_msg_off                 ;AN000; old offset of critical messages
        mov     es,crit_msg_seg                 ;AN000; old segment of critical messages
        int     2fh                             ;AN000; go set it

        push    cs
        pop     ds
assume  ds:trangroup                            ;AN000;

; Restore user directory if the restore flag is set. RestUDir1 checks for
; this, restores user dir if flag is set and resets the flag.

        invoke  RestUDir1               ;restore user dir if needed ;M040
        MOV     ES,cs:[RESSEG]

assume  es:resgroup

        MOV     AX,[PARENT]
        MOV     WORD PTR ES:[PDB_Parent_PID],AX
        MOV     AX,WORD PTR OldTerm
        MOV     WORD PTR ES:[PDB_Exit],AX
        MOV     AX,WORD PTR OldTerm+2
        MOV     WORD PTR ES:[PDB_Exit+2],AX

        PUSH    ES
        MOV     ES,[TRAN_TPA]
        MOV     AH,DEALLOC
        INT     21h                     ; Now running in "free" space
        POP     ES

        pop     ax
        MOV     AH,Exit
        INT     21h

; Entry al = Start16 means  put the command.com prompt, get the command
;            return16 means return the exit code if needed
;       ah = FOR_SHELLOUT means came from shellout code (DoReEnter)
;       ah = FOR_TSR      means came from TSR (tsr_loop)
;
; Exit  al = 0 means run 16bit binary
;       al = 1 means do getnextvdmcommand
;       if came with FOR_TSR, then additionaly
;       ah = 1 means returning on exit command
;       ah = 0 means otherwise

Do16BitPrompt:
        push    es
        push    ax
        mov     es,cs:[RESSEG]                  ; es is resident group
        ASSUME  ES:resgroup
	PUSH	CS
        POP     DS                              ; Need local segment to point to buffer
        assume  ds:trangroup
        cmp     al,Start16
        jne     d16_ret

d16_loop:
        INVOKE  CRLF2
        INVOKE  PRINT_PROMPT                    ; put the prompt
	MOV	DX,OFFSET TRANGROUP:UCOMBUF

        mov     ah,STD_CON_STRING_INPUT         ; AH = Buffered Keyboard Input
        int     21h                             ; call DOS

        push    si
        mov     si,dx
        inc     si
        inc     si
        INVOKE  SCANOFF                         ; eat leading delimeters

        cmp     byte ptr ds:[si],0dh            ; special case CR
        jne     d16_gotcom
        pop     si
        jmp     d16_loop

d16_gotcom:
        INVOKE  CRLF2
        call    check_command                   ; check for exit and cd
        pop     si
        jnc     d16_run

        or      al,al
        jz      d16_exit

d16_dosonly:
        mov     byte ptr es:[scs_prompt16],0
        pop     ax
        xor     ax,ax           ; go run the command
        pop     es
        ret

d16_run:
        cmp     byte ptr es:[scs_dosonly], DOSONLY_YES
        je      d16_dosonly

        push    es
        push    bp
        push    si
        mov     ax,0ffffh
        push    ax              ; no standard handle to pass
        push    ax
        push    ax
        push    ax
        push    ax
        push    ax
        mov     bp,sp           ; ss:bp is standard handles
        mov     es,es:[envirseg]
        mov     si,dx
        add     si,2            ; first two bytes are the count, after that real command
        mov     ah,19h
        int     21h             ; al = cur drive
        mov     ah,1            ; do cmd /c
        CMDSVC  SVC_CMDEXEC     ; Exec through cmd
        lahf
        add     sp,12           ; recover std handle space
        pop     si
        pop     bp
        pop     es
        sahf

        jnc    d16_loop         ; command completed, go put the prompt

        ; carry set means re-entered
d16_retback:
        mov     byte ptr es:[scs_prompt16],1   ; mark that on return we have
                                            ; to go to 32bit with retcode
        pop     ax
        mov     ax,1                        ; go do getnextvdmcommand
        pop     es
        ret

d16_ret:
        cmp     byte ptr es:[scs_prompt16],0   ; mark 0 to mean to come back
                                            ; and put prompt fro next command
        je      d16_loop


d16_return32:
        push    dx
        mov     dx,es:[retcode]
        mov     ah,19h
        int     21h                             ; al = cur drive
        mov     cx,word ptr es:[RES_RDRINFO]
        mov     bx,word ptr es:[RES_RDRINFO+2]  ; bx:cx - rdrinfo
        CMDSVC  SVC_RETURNEXITCODE
        pop     dx
        mov     es:[retcode],0
        jc      d16_retback
        mov     byte ptr es:[scs_prompt16],0
        jmp     d16_loop


d16_exit:
        ; exit command was given, turn off the lights

        pop     ax
        cmp     ah,FOR_TSR
        je      d16_exittsr
        jmp     reent_exit

d16_exittsr:
        mov     ah,1
        pop     es
        ret


; check if the typed command is one of the commands in NT_INTRL_CMND, if
; so return carry set plus al = 0 if the command was "exit".
;
; input ds:si is the command buffer
; si can be trashed.

check_command:

        SAVE    <CX,DI,ES>

        cmp     byte ptr ds:[si+1],':'      ; special case drive change i.e C:
        jne     cc_0
        mov     al,byte ptr ds:[si+2]
        cmp     al,0dh                      ; DELIM for some reason does'nt
        je      ok_delim                    ; include 0d
        invoke  DELIM
        jnz     no_match
ok_delim:
        mov     al,1                        ; Not exit command
ok_xt:
        stc                                 ; Carry means command found
        jmp     short cc_ret

no_match:
        clc                                 ; Carry clear, command not found
cc_ret:
        RESTORE <ES,DI,CX>
        ret

cc_0:
        push    si
        xor     cx,cx

        ; Convert the source string to upper case and get the length
cc_1:
        mov     al,byte ptr ds:[si]
        invoke  DELIM
        jz      go_look
        invoke  MOREDELIM
        jz      go_look

        INVOKE  UPCONV
        mov     byte ptr ds:[si],al
        inc     si
        inc     cx
        jmp     short cc_1

go_look:
        pop     si
        jcxz    no_match                            ; zero length, go fail
        mov     di, OFFSET TRANGROUP:NT_INTRNL_CMND
        push    cs
        pop     es

        ; search through the commands in the table
cc_5:
        push    si
        push    di
        push    cx
        cmp     cl, byte ptr es:[di]
        jne     try_next
        inc     di
        repz    cmpsb
        jnz     try_next
        jmp     short cc_found

try_next:
        pop     cx
        pop     di
        pop     si
        mov     al,byte ptr es:[di]
        or      al,al
        jz      no_match
        xor     ah,ah
        add     ax,di
        mov     di,ax
        add     di,2
        jmp     short cc_5

cc_found:
        mov     al, byte ptr es:[di]                ; Is it exit command
        pop     cx
        pop     di
        pop     si
        jmp     short ok_xt

;
; Input:    AL is character to classify
; Output:   Z set if delimiter
;	    NZ set otherwise
; Registers modified: none
;

MOREDELIM:
        cmp     al,0dh
        retz
        cmp     al,'/'
        retz
        cmp     al,'\'
        retz
        cmp     al,'.'
        retz
        cmp     al,'<'
        retz
        cmp     al,'>'
        retz
        cmp     al,'|'
        retz
        cmp     al,'"'
        retz
        cmp     al,'+'
        retz
        cmp     al,':'
        retz
        cmp     al,';'
        retz
        cmp     al,'['
        retz
        cmp     al,']'
        return



free_con:
        push    ds
        MOV     DS,cs:[RESSEG]
        ASSUME  DS:RESGROUP
        xor     bx,bx                           ; BX = handle = 0
        mov     cx,Io_Save                      ; CX = original stdin, stdout
        mov     dx,word ptr ds:Pdb_Jfn_Table    ; DX = current stdin, stdout
        cmp     cl,dl
        je      Chk1                    ; stdin matches
        mov     ah,CLOSE
        int     21h                     ; close stdin
        mov     ds:Pdb_Jfn_Table,cl     ; restore stdin
Chk1:
        inc     bx                      ; BX = handle = 1
        cmp     ch,dh
        je      Chk2                    ; stdout matches
        mov     ah,CLOSE
        int     21h                     ; close stdout
        mov     ds:Pdb_Jfn_Table+1,ch   ; restore stdout
Chk2:
        inc     bx                      ; BX = handle = 2
        mov     dl,byte ptr ds:[Pdb_Jfn_Table+2]        ; Dl = current stderr
        mov     cl,Io_Stderr            ; Cl = original stderr
        cmp     dl,cl
        je      chk_x                   ; stderr matches
        mov     ah,CLOSE
        int     21h                     ; close stderr
        mov     ds:Pdb_Jfn_Table+2,cl   ; restore stderr
chk_x:
        pop     ds
        ret

alloc_con:
	SAVE	<DX,SI,BP,BX,DS>
        MOV     DS,cs:[RESSEG]
        ASSUME  DS:RESGROUP
        push    cx
        pop     ax
        mov     bx, offset Pdb_Jfn_Table
        add     bx,ax
        mov     al,byte ptr ds:[bx]
        push    ax
        push    bx
        mov     byte ptr ds:[bx],0ffh

        mov     bx,word ptr ds:[RES_RDRINFO]
        mov     ax,word ptr ds:[RES_RDRINFO+2]

        CMDSVC  SVC_GETSTDHANDLE                ; std hanlde in bx:cx
        jc      alloc_err
;; bx:cx  = nt file handle
;; dx:ax = file size
	push	di
	mov	di, ax
        xor     si,si
        xor     bp,bp
        mov     al,0                            ; free original console
        mov     ah,setdpb
        int     21h
	pop	di
        jc      alloc_err
        pop     bx
        pop     ax
	RESTORE 	<DS,BX,BP,SI,DX>
        ret

alloc_err:
        pop     bx
        pop     ax
        mov     byte ptr ds:[bx],al
	RESTORE 	<DS,BX,BP,SI,DX>
        ret


; ds for tsr_loop is resseg on entry on exit transseg

        ASSUME  DS:RESGROUP

tsr_loop:
        cmp     byte ptr ds:[scs_cmdprompt],Prompt32
        je      tsr_ret

        cmp     byte ptr ds:[res_batstatus],0
        je      short tsr_nobat
        cmp     byte ptr ds:[scs_firsttsr],1
        jne     short tsr_retcode
        jmp     tsr_ret

tsr_nobat:

        CMDSVC  SVC_GETSTARTINFO     ; return al = 1, if vdm has to
                                    ; terminate on app exit.
        or      al,al
        jne     tsr_ret

        cmp     byte ptr [scs_firsttsr],1
        jne     tsr_retcode

        cmp     word ptr ds:[RES_RDRINFO],0
        je      tsr_nordr
        cmp     word ptr ds:[RES_RDRINFO+2],0
        jne     tsr_ret
tsr_nordr:
        mov     byte ptr ds:[scs_firsttsr],0
        mov     al,Start16          ; parameter to put prompt
        mov     ah,FOR_TSR
        call    Do16BitPrompt       ; ds is resseg here on return its transseg
        or      ah,ah
        jnz     tsr_exit
        or      al,al               ; return al=0 to run 16bit binary al=1 getnextvdmcommand
        jz      tsr_dosonly
        jmp     tsr_ret
tsr_dosonly:
        jmp     GotCom              ; go run through int21/exec

tsr_retcode:
        mov     al,return16         ; parameter to retun exit code if needed
        mov     ah,FOR_TSR
        call    Do16BitPrompt       ; ds = resseg , on return its transseg
        or      ah,ah
        jnz     tsr_exit
        or      al,al               ; return al=0 to run 16bit binary al=1 getnextvdmcommand
        jnz     tsr_ret
        jmp     GotCom

tsr_exit:
        push    cs
        pop     ds
        assume  ds:trangroup
        mov     [scs_tsrexit],1
tsr_ret:
        jmp     tsr_loop_ret


TRANCODE        ENDS
        END
