        page ,132
        title   Localizable code for resident COMMAND
;
;                        IBM Corporation
;                        Copyright (C) IBM Corporation 1991-1992
;                        All Rights Reserved.

;
;       Revision History
;       ================
;       M003    SR      07/16/90        Added routines Lh_Off, Lh_Unlink &
;                               Lh_OffUnlink for UMB support
;
;       M009    SR      08/01/90        Rewrote Lh_OffUnlink to restore the
;                               initial UMB state. Removed Lh_off
;                               and Lh_Unlink.
;
ifdef   DOSV

;       I50-50AT        91-08-01        A. Togami       J5.00         ;dbcs50at;
;       Adapation                                                     ;dbcs50at;
;               include BILINCMD.ASM to display bilingual message.    ;dbcs50at;
;       Internal Fix                                                  ;dbcs50at;
;               enlarge message buffer size for bilingual message.    ;dbcs50at;
;       Internal Fix                                                  ;dbcs50at;
;               flush DBCS tail byte at ASKEND prompt.                ;dbcs50at;

;       I61-50AT        93-07-28        A. Togami       J6.10         ;dbcs50at;
;       I63-50AT        94-02-02        A. Togami       J6.30         ;dbcs50at;
;       Maintenance (merged with J5.00 version)                       ;dbcs50at;

endif

.xlist
.xcref
        include dossym.inc
        include syscall.inc
        include filemode.inc
        include pdb.inc
        include mult.inc
        include doscntry.inc
        include devsym.inc
        include comsw.asm
        include comseg.asm
        include comequ.asm
        include resmsg.equ
        include arena.inc               ; M003
.list
.cref


DATARES segment public byte
        extrn   Abort_Char:byte
        extrn   BadFatMsg:byte
        extrn   BadFatSubst:byte
        extrn   Batch_Abort:byte
        extrn   BlkDevErr:byte
        extrn   BlkDevErrRw:byte
        extrn   BlkDevErrSubst:byte
        extrn   CDevAt:byte
        extrn   CharDevErr:byte
        extrn   CharDevErrRw:byte
        extrn   CharDevErrSubst:byte
        extrn   ComSpec:byte
        extrn   Crit_Err_Info:byte
        extrn   CritMsgPtrs:word
        extrn   Dbcs_Vector_Addr:dword
        extrn   DevName:byte
        extrn   DrvLet:byte
        extrn   EndBatMes:byte
        extrn   ErrCd_24:word
        extrn   ErrType:byte
        extrn   Fail_Char:byte
        extrn   fFail:byte
        extrn   ForFlag:byte
        extrn   Ignore_Char:byte
        extrn   InitFlag:byte
        extrn   In_Batch:byte
        extrn   Int2fHandler:dword
        extrn   Loading:byte
        extrn   MsgBuffer:byte
        extrn   MsgPtrLists:dword
        extrn   MRead:byte
        extrn   MWrite:byte
        extrn   NeedVol:dword
        extrn   NeedVolMsg:byte
        extrn   NeedVolSubst:byte
        extrn   Newlin:byte
        extrn   No_Char:byte
        extrn   NUMEXTMSGS:abs
        extrn   NUMPARSMSGS:abs
        extrn   OldErrNo:word
        extrn   Parent:word
        extrn   ParsMsgPtrs:word
        extrn   Patricide:byte
        extrn   PermCom:byte
        extrn   Retry_Char:byte
        extrn   Req_Abort:byte
        extrn   Req_End:byte
        extrn   Req_Fail:byte
        extrn   Req_Ignore:byte
        extrn   Req_Retry:byte
        extrn   ResMsgEnd:word
        extrn   PipeFlag:byte
        extrn   SingleCom:word
        extrn   VolName:byte
        extrn   Yes_Char:byte

        extrn   OldDS:word
        extrn   Int2f_Entry:dword

DATARES ends


CODERES segment public byte

        extrn   GetComDsk2:near

        public  AskEnd
        public  Crlf
        public  DskErr
        public  MsgInt2fHandler
        public  MsgRetriever
        public  RPrint

        ifdef   DBCS
        public  ITestKanj
        endif

;       Bugbug: Move rest of PUBLIC declarations up here.

        assume  cs:CODERES,ds:NOTHING,es:NOTHING,ss:NOTHING



;***    AskEnd - ask user to confirm batch file termination
;
;       Confirm with user before freeing batch ...
;
;       ENTRY   nothing
;
;       EXIT    CY = set if batch termination is confirmed
;
;               CY = clear if batch should continue
;
;       USED    AX,DX,...

;       Bugbug: move this to transient, copy to batch segment.
;       Bugbug: or move it to command1 1st.

;       Bugbug: No_Char and Yes_Char should be constants.

AskEnd  proc

        assume  ds:DATARES

        mov     dx,offset DATARES:EndBatMes     ; DX = message #
        call    RPrint
        mov     ax,(STD_CON_INPUT_FLUSH shl 8) + STD_CON_INPUT
        int     21h
ifdef   DOSV                            ; ### if DOSV ###             ;dbcs50at;
;               flush DBCS tail byte at ASKEND prompt.                ;dbcs50at;
        call    testkanjr                                             ;dbcs50at;
        jz      @f                                                    ;dbcs50at;
                mov     ax, std_con_input shl 8                       ;dbcs50at;
                int     021h                                          ;dbcs50at;
                xor     ax, ax                                        ;dbcs50at;
        @@:                                                           ;dbcs50at;
endif                                                                 ;dbcs50at;
        call    CharToUpper                     ; change to upper case
        cmp     al,No_Char
        je      aeRet                           ; answer is no (CY is clear)
        cmp     al,Yes_Char
        jne     AskEnd                          ; invalid response, try again
        stc                                     ; answer is yes
aeRet:  ret

AskEnd  endp


;***    DskErr - critical error handler
;
;       Default critical error handler unless user intercepts int 24h.
;
;       ENTRY   int 24h
;
;       EXIT
;
;       USED
;
;       EFFECTS

;
;SR;
; The stub is going to push the old ds value and the resident data segment
;onto the stack in that order. Get it off the stack
;

DskErr  proc    far

        assume  ds:NOTHING,es:NOTHING,ss:NOTHING

        pop     ds                      ;ds = DATARES
        assume  ds:DATARES
        pop     OldDS                   ;save old ds value

        sti
        push    es
        push    si
        push    cx
        push    di
        push    cx
        push    ax

        push    ds                      ;save our data segment
        pop     es                      ;es = DATARES

        mov     ds,bp
        assume  ds:nothing

        mov     ax,[si].SDEVATT
        mov     es:CDevAt,ah
        mov     di,offset DATARES:DevName
        mov     cx,8
        add     si,SDEVNAME     ; save device name (even for block device)

        cld
        rep     movsb
        pop     ax
        pop     cx
        pop     di

;       Stack still contains DS and ES.

;SR;
; We need ds = DATARES for SavHand
;
        push    es
        pop     ds
        assume  ds:DATARES

        invoke  SavHand         ; save user's stdin/out, set to our stderr

        push    dx
        call    Crlf
        pop     dx

;       Bugbug: rename Crit_Err_Info to CritErrAH?

        mov     Crit_Err_Info,ah        ; save critical error flags

;       Compute and save ASCII drive letter (nonsense for char devices)

        add     al,'A'
        mov     DrvLet,al

;       Bugbug: These labels are awful.  Change, especially 'NoHardE'.

        test    ah,80h
        jz      NoHardE                 ; it's a disk-device error
        test    CDevAt,DEVTYP shr 8
        jnz     NoHardE                 ; it's a character device
        jmp     FatErr                  ; it's a FAT error

NoHardE:
        mov     si,offset DATARES:MRead  ; SI = "read" msg #
        test    ah,1
        jz      SavMes                    ; it's a read error
        mov     si,offset DATARES:MWrite ; SI = "write" msg #

SavMes:
        mov     OldErrNo,di             ; save critical error code

;       Bugbug: don't need to save/restore all here?
        push    es
; VCB - Only save registers which are changed
;       push    ds                      ; GetExtendedError likes to STOMP
;       push    bp
;       push    si
;       push    dx
        push    cx
        push    bx
        mov     ah,GetExtendedError     ; get extended error info
        int     21h
        pop     bx
        pop     cx
; VCB - Only restore registers which are changed
;       pop     dx
;       pop     si
;       pop     bp
;       pop     ds
        mov     word ptr NeedVol,di     ; save possible ptr to volume label
        mov     word ptr NeedVol+2,es
        pop     es

;       Bugbug: AX has extended error code, so no need to zero AH?

;       xor     ah,ah
        mov     di,ax                   ; DI = error code

; Bugbug:  somewhat obsolete documentation?
;
; DI is now the correct error code.  Classify things to see what we are
; allowed to report.  We convert DI into a 0-based index into a message table.
; This presumes that the int 24 errors (oldstyle) and new errors (sharing and
; the like) are contiguous.
;

;       Bugbug: simplify following code by cmp'ing instead of sub'ing.
;       Check use of ErrCd_24, though.

        sub     di,ERROR_WRITE_PROTECT
        jae     HavCod

;       Bugbug  wouldn't it be better to display the original error msg,
;       even though it's not a critical error?

        mov     di,ERROR_GEN_FAILURE - ERROR_WRITE_PROTECT
;
; DI now has the mapped error code.  Old style errors are:
;   FOOBAR <read|writ>ing drive ZZ.
; New style errors are:
;   FOOBAR
; We need to figure out which the particular error belongs to.
;

HavCod:
        mov     ErrType,0               ; assume old style
        cmp     di,ERROR_FCB_UNAVAILABLE - ERROR_WRITE_PROTECT
        je      SetStyle
        cmp     di,ERROR_SHARING_BUFFER_EXCEEDED - ERROR_WRITE_PROTECT
        jne     GotStyle

SetStyle:
;       Bugbug: use INC
        mov     ErrType,1               ; must be new type

GotStyle:
        mov     [ErrCd_24],di
        cmp     di,ERROR_HANDLE_DISK_FULL - ERROR_WRITE_PROTECT
                                                ; If the error message is unknown
        jbe     NormalError                     ;  redirector, continue.  Otherwise,
;
; We do not know how to handle this error.  Ask IFSFUNC if she knows
; how to handle things
;

;input to IFSFUNC:    AL=1
;                     BX=extended error number
;
;output from IFSFUNC: AL=error type (0 or 1)
;                        0=<message> error (read/writ)ing (drive/device) xxx
;                          Abort, Retry, Ignore
;                        1=<message>
;                          Abort, Retry, Ignore
;                     ES:DI=pointer to message text
;                     carry set=>no message

        mov     di,ax                   ; retrieve correct extended error...
        mov     ax,0500h                ; is the redir there?
        int     2fh
        cmp     al,0ffh
        jne     NoHandler               ; no, go to NoHandler
        push    bx
        mov     bx,di                   ; get ErrType and ptr to error msg
        mov     ax,0501h
        int     2fh
        pop     bx
        jc      NoHandler

;       Bugbug: need to record error type?
        mov     ErrType,al
        push    ds
        push    es
        pop     ds
        mov     dx,di
        mov     cx,-1                   ; find end of msg
        xor     al,al

        cld
        repnz   scasb

;       Bugbug: we can do better than this.

        mov     byte ptr [di-1],'$'
        mov     ah,STD_CON_STRING_OUTPUT        ; print the message
        int     21h
        mov     byte ptr [di-1],0               ; restore terminal byte

        pop     ds                              ; clean up and continue
        jmp     short CheckErrType

;*      Redir isn't available or doesn't recognize the error.
;       Restore regs to unextended error.

NoHandler:
        mov     ErrType,0
;       Bugbug: won't this break, since we add error_write_protect back in?
        mov     di,OldErrNo
        mov     ErrCd_24,di

NormalError:
        add     di,ERROR_WRITE_PROTECT
        xchg    di,dx                   ; may need dx later
        call    RPrintCrit              ; print error type

CheckErrType:
        cmp     ErrType,0               ; Check error style...
        je      ContOld
        call    CrLf                    ; if new style then done printing
        jmp     short Ask

ContOld:
ifdef   DOSV                            ; ### if DOSV ###             ;dbcs50at;
;               include BILINCMD.ASM to display bilingual message.    ;dbcs50at;
        push    cx                                                    ;dbcs50at;
                xor     cx, cx                                        ;dbcs50at;
                lodsb                                                 ;dbcs50at;
                mov     cl, al                                        ;dbcs50at;
                call    adjust_message_address                        ;dbcs50at;
        pop     cx                                                    ;dbcs50at;
else                                    ; ### else not DOSV ###       ;dbcs50at;
        inc     si                      ; DS:SI = ptr to asciiz string
endif                                   ; ### endif DOSV ###          ;dbcs50at;

;       Bugbug: combine some of the following two sections?

        test    [CDevAt],DEVTYP shr 8
        jz      BlkErr
        mov     dx,offset DATARES:CharDevErr      ; DX = ptr to device message
        mov     CharDevErrRw.SubstPtr,si          ; point to read/write string
        mov     si,offset DATARES:CharDevErrSubst; SI = ptr to subst block

        call    RPrint                          ; print the message
        jmp     short Ask                       ; don't ralph on command

BlkErr:
        mov     dx,offset DATARES:BlkDevErr       ; DX = error msg #
        mov     BlkDevErrRw.SubstPtr,si           ; "reading","writing" ptr
        mov     si,offset DATARES:BlkDevErrSubst ; SI = ptr to subst block
        call    RPrint

        cmp     Loading,0
        jz      Ask
        invoke  RestHand
        jmp     GetComDsk2              ; if error loading COMMAND, re-prompt

Ask:
        cmp     [ErrCd_24],15           ; error 15 has an extra message
        jne     Not15                   ; not error 15

;*      For error 15, tell the user which volume/serial # are needed.

        push    cx

;       Bugbug: does this push/pop need to be done?
        push    ds
        pop     es
        lds     si,NeedVol
        assume  ds:NOTHING
        push    di
        mov     di,offset DATARES:VolName
        mov     cx,16                   ; copy volume name & serial #
        cld
        rep     movsb
        pop     di
        push    es
        pop     ds
        pop     cx
        assume  ds:DATARES
        mov     dx,offset DATARES:NeedVolMsg    ; DX = ptr to msg
        mov     si,offset DATARES:NeedVolSubst  ; DS:SI = ptr to subst block
        call    RPrint
Not15:

;*      Print abort, retry, ignore, fail message.
;       Print only options that are valid.

;       Bugbug: sizzle this.

        mov     dx,offset DATARES:Req_Abort
        call    RPrint
        test    Crit_Err_Info,RETRY_ALLOWED
        jz      Try_Ignore
        mov     dx,offset DATARES:Req_Retry
        call    RPrint

Try_Ignore:
        test    Crit_Err_Info,IGNORE_ALLOWED
        jz      Try_Fail
        mov     dx,offset DATARES:Req_Ignore
        call    RPrint

Try_Fail:
        test    Crit_Err_Info,FAIL_ALLOWED
        jz      Term_Question
        mov     dx,offset DATARES:Req_Fail
        call    RPrint

Term_Question:
        mov     dx,offset DATARES:Req_End
        call    RPrint

;       If the /f switch was given, we fail all requests.

        test    fFail,-1
        jz      DoPrompt
        mov     ah,3                            ; signal fail
        jmp     EExit

DoPrompt:
        mov     ax,(STD_CON_INPUT_FLUSH shl 8) + STD_CON_INPUT
        int     21h                             ; get response


;       Bugbug: can Kanji code be conditional?

        ifdef   DBCS

        invoke  TestKanjR                       ; 3/3/KK
        jz      NotKanj                         ; 3/3/KK
        mov     ax,(STD_CON_INPUT shl 8)        ; eat the 2nd byte of ECS code  3/3/KK
        int     21h                             ; 3/3/KK
        call    Crlf                            ; 3/3/KK
        jmp     SHORT Ask                       ; 3/3/KK              ;AC000;
NotKanj:                                        ; 3/3/KK

        endif

        call    Crlf
        call    CharToUpper                     ; convert to upper case
        mov     ah,0                            ; return code for ignore
        test    Crit_Err_Info,IGNORE_ALLOWED    ; is ignore allowed?
        jz      user_retry
        cmp     al,Ignore_Char                  ; ignore?
        jz      EExitJ

;       Bugbug: optimize following code.

User_Retry:
        inc     ah                              ; return code for retry
        test    Crit_Err_Info,RETRY_ALLOWED     ; is retry allowed?
        jz      User_Abort
        cmp     al,Retry_Char                   ; retry?
        jz      EExitJ

User_Abort:
        inc     ah                              ; return code for abort
                                                ;  (abort always allowed)
        cmp     al,Abort_Char                   ; abort?
        jz      Abort_Process                   ; exit user program
        inc     ah                              ; return code for fail
        test    Crit_Err_Info,FAIL_ALLOWED      ; is fail allowed?
        jz      AskJ
        cmp     al,Fail_Char                    ; fail?
        jz      EExitJ

AskJ:
        jmp     Ask

EExitJ:
        jmp short EExit

Abort_Process:
        test    InitFlag,INITINIT               ; COMMAND init interrupted?
        jz      AbortCont                       ; no, handle it normally
        cmp     PermCom,0                       ; are we top level process?
        jz      JustExit                        ; yes, just exit

IFDEF   ROMDOS
        test    Crit_Err_Info,FAIL_ALLOWED
        jz      No_Hope
        mov     ah, 3
        jmp     short EExit
No_Hope:
ENDIF
        mov     dx,offset DATARES:Patricide     ; no, load ptr to error msg
        call    RPrint                          ; print it

DeadInTheWater:
        jmp     SHORT DeadInTheWater    ; loop until the user reboots ;AC000;

JustExit:
        assume  ds:DATARES
        mov     ax,Parent                       ; load real parent pid
        mov     word ptr ds:Pdb_Parent_Pid,ax   ; put it back where it belongs
        mov     ax,(EXIT shl 8) or 255
        int     21h

AbortCont:
        test    byte ptr In_Batch,-1    ; Are we accessing a batch file?
        jz      Not_Batch_Abort
        mov     byte ptr Batch_Abort,1  ; set flag for abort

Not_Batch_Abort:
        mov     dl,PipeFlag
        invoke  ResPipeOff
        or      dl,dl
        je      CheckForA
        cmp     SingleCom,0
        je      CheckForA
        mov     SingleCom,-1                    ; make sure singlecom exits

CheckForA:
        cmp     ErrCd_24,0                      ; write protect?
        je      AbortFor
        cmp     ErrCd_24,2                      ; drive not ready?
        jne     EExit                           ; don't abort the FOR

abortfor:
        mov     ForFlag,0                       ; abort a FOR in progress
        cmp     SingleCom,0
        je      EExit
        mov     SingleCom,-1                    ; make sure singlecom exits

EExit:
        mov     al,ah
        mov     dx,di

RestHd:
        invoke  RestHand
        pop     cx
        pop     si                              ; restore registers
        pop     es

        mov     ds,OldDS                        ;restore old value of ds
        assume  ds:nothing

        iret

FatErr:
        mov     dx,offset DATARES:BadFatMsg
        mov     si,offset DATARES:BadFatSubst
        call    RPrint

        mov     al,2                            ; abort
        jmp     SHORT RestHd                                          ;AC000;

DskErr  endp




;***    RPrint - print message
;***    Crlf - display cr/lf
;
;       ENTRY   DS:DX = ptr to count byte, followed by message text
;               DS:SI = ptr to 1st substitution block for this msg, if any
;               variable fields related to substitution blocks are set
;
;       EXIT    nothing
;
;       USED    flags
;
;       EFFECTS
;         Message is displayed on stdout.
;
;       NOTE
;         Number of substitutions (%1, %2,...) in message text must not
;           be greater than number of substition blocks present.


Crlf:
        mov     dx,offset DATARES:Newlin        ; cheap newline

RPrint  proc

        assume  ds:DATARES,ss:DATARES

;       Bugbug: do we need to save all reg's?

        push    si                      ; preserve registers
        push    ax
        push    bx
        push    cx
        push    dx

        mov     bx,si                   ; DS:BX = ptr to subst block
        mov     si,dx                   ; DS:SI = ptr to count byte
        lodsb                           ; AL = message length
                                        ; DS:SI = ptr to message text
        xor     cx,cx
        mov     cl,al                   ; CX = message length
        jcxz    rpRet
ifdef   DOSV                            ; ### if DOSV ###             ;dbcs50at;
        call    adjust_message_address                                ;dbcs50at;
        jcxz    rpret                                                 ;dbcs50at;
endif                                   ; ### endif DOSV ###          ;dbcs50at;

        call    RDispMsg

rpRet:  pop     dx
        pop     cx
        pop     bx
        pop     ax
        pop     si
        ret

RPrint  endp


ifdef   DOSV                            ; ### if DOSV ###             ;dbcs50at;
;               include BILINCMD.ASM to display bilingual message.    ;dbcs50at;
        include BILINCMD.ASM                                          ;dbcs50at;
endif                                   ; ### endif DOSV ###          ;dbcs50at;


;***    RPrintCrit - print critical error message
;
;       ENTRY   DX = extended error # (19-39)
;
;       EXIT    nothing
;
;       USED    flags
;
;       EFFECTS
;         Message is displayed on stdout

RPrintCrit      proc

        assume  ds:DATARES,ss:DATARES

        push    dx                      ; preserve DX
        xchg    bx,dx                   ; BX = extended error #
                                        ; DX = saved BX
        sub     bx,19                   ; BX = critical error index, from 0
        shl     bx,1                    ; BX = offset in word table
        mov     bx,CritMsgPtrs[bx]      ; BX = ptr to error msg
        xchg    bx,dx                   ; DX = ptr to error msg
                                        ; BX = restored
        call    RPrint                  ; print the message
        pop     dx                      ; restore DX
        ret

RPrintCrit      endp




;***    RDispMsg - display message
;
;       Display message, with substitutions, for RPrint.
;
;       ENTRY   DS:SI = ptr to message text
;               CX = message length
;               DS:BX = ptr to substitution block, if any
;
;       EXIT    nothing
;
;       USED    AX,CX,DX,SI

RDispMsg        proc

        assume  ds:DATARES,ss:DATARES

rdNextChar:
        lodsb                           ; AL = next char
        cmp     al,'%'
        jne     rdOutChar               ; not a substitution
        mov     dl,ds:[si]              ; DL = possible '1' - '9'
        sub     dl,'1'                  ; DL = 0 - 8 = '1' - '9'
        cmp     dl,9
        jae     rdOutChar               ; not a substitution

;*      A substitution code %1 - %9 has been encountered.
;       DL = 0-8, indicating %1-%9
;       DS:BX = ptr to substitution block

        call    SubstMsg                ; display the substitution
        inc     si                      ; SI = ptr past %n
        dec     cx                      ; count extra character in %n
        jmp     short rdCharDone

;*      Normal character output.

rdOutChar:
        mov     dl,al                   ; DL = char
        mov     ah,2                    ; AH = DOS Character Output code
        int     21h                     ; call DOS
rdCharDone:
        loop    rdNextChar
        ret

RDispMsg        endp




;***    SubstMsg - display message substitution
;
;       Display a substitution string within a message.
;       Substitution can be a char, an ASCIIZ string, or
;       a word to be displayed as hex digits.
;
;       ENTRY   DL = substitution index 0-8 (for codes %1-%9)
;               DS:BX = ptr to substitution block
;
;       EXIT    nothing
;
;       USED    AX,DX

SubstMsg        proc

        assume  ds:DATARES,ss:DATARES

        push    bx                      ; preserve BX
        push    cx                      ; preserve CX

        mov     al,size SUBST           ; AL = size of substitution block
        mul     dl                      ; AX = offset of desired subst block
        add     bx,ax                   ; DS:BX = ptr to desired subst block

        mov     al,[bx].SubstType       ; AX = substitution type flag
        mov     bx,[bx].SubstPtr        ; BX = ptr to char, str, or hex value

;       AL = 1, 2, or 3 for char, string, or hex type

        dec     al
        jz      smChar
        dec     al
        jz      smStr

;*      Hex number substitution.

        mov     ax,ds:[bx]              ; AX = word value
        mov     cx,4                    ; CX = # digits to display
smDigit:
        rol     ax,1
        rol     ax,1
        rol     ax,1
        rol     ax,1                    ; AL<3:0> = next digit

        push    ax                      ; save other digits
        and     al,0Fh                  ; AL = binary digit
        add     al,'0'                  ; AL = ascii digit if 0-9
        cmp     al,'9'
        jbe     @F                      ; it's 0-9
        add     al,'A' - '0' - 10       ; AL = ascii digit A-F
@@:
        mov     dl,al                   ; DL = ascii digit
        mov     ah,2
        int     21h                     ; output the ascii digit
        pop     ax                      ; restore all digits

        loop    smDigit
        jmp     short smRet

;*      Char substitution.

smChar:
        mov     dl,ds:[bx]              ; DL = char to output
        mov     ah,2
        int     21h
        jmp     short smRet

;*      String substitution.

smStr:
        mov     dl,ds:[bx]              ; DL = next char
        or      dl,dl
        jz      smRet                   ; null char - we're done
        mov     ah,2
        int     21h                     ; display char
        inc     bx                      ; DS:BX = ptr to next char
        jmp     SHORT smStr                                           ;AC000;

smRet:  pop     cx
        pop     bx
        ret

SubstMsg        endp




;***    CharToUpper - convert character to uppercase
;
;       ENTRY   AL = char
;
;       EXIT    AL = uppercase char
;
;       USED    AX

CharToUpper     proc

        assume  ds:DATARES

        push    ax              ; put char on stack as arg to int 2F
        mov     ax,1213h        ; AX = DOS int 2F 'Convert Char to Uppercase'
        int     2Fh
        inc     sp              ; throw away old char on stack
        inc     sp
        ret

CharToUpper     endp





        ifdef   DBCS

;***    ITestKanj - DBCS lead byte check

ITestKanj:
TestKanjR:                              ; 3/3/KK
        push    ds
        push    si
        push    ax
        lds     si,Dbcs_Vector_Addr

ktLop:
        cmp     word ptr ds:[si],0      ; end of Lead Byte Table
        je      NotLead
        pop     ax
        push    ax
        cmp     al, byte ptr ds:[si]
        jb      NotLead
        inc     si
        cmp     al, byte ptr ds:[si]
        jbe     IsLead
        inc     si
        jmp     short ktLop             ; try another range

NotLead:
        xor     ax,ax                   ; set zero
        jmp     short ktRet

Islead:
        xor     ax,ax                   ; reset zero
        inc     ax

ktRet:
        pop     ax
        pop     si
        pop     ds
        ret

        endif




;***    MsgInt2fHandler - int 2f handler for message retrieval
;
;       ENTRY   If we handle it -
;                 AX = ((MULTDOS shl 8) or MESSAGE_2F) = 122Eh
;                 DL = operation =
;                    0 = get extended error messages
;                    1 = set extended error messages
;                    2 = get parse error messages
;                    3 = set parse error messages
;                    4 = get critical error messages
;                    5 = set critical error messages
;                    6 = get file system error messages
;                    7 = set file system error messages
;                    8 = get disk retriever routine
;                    9 = set disk retriever routine
;                 ES:DI = address for 'set' operations
;
;       EXIT    ES:DI = ptr to list of message ptrs, for 'get' operations
;
;       NOTE
;         This handler replaces the one that used to reside in DOS.
;         'Set' operations are ignored.
;         'File system error messages' are not supported.

;SR;
; At the int 2fh entry point we push the old ds value and the resident data
;segment address. Get them off the stack
;

MsgInt2fHandler proc    far

        assume  cs:CODERES,ds:NOTHING,es:NOTHING,ss:NOTHING

        pop     ds                      ;ds = DATARES
        assume  ds:DATARES

        cmp     ax,(MULTDOS shl 8) or MESSAGE_2F
        je      miOurs                  ; it's ours

        ifndef  ROMDOS
        cmp     ax,GET_COMMAND_STATE    ;is it first COMMAND query?
        else
        cmp     ax,GET_ROMCOMMAND_STATE ;is it first ROM COMMAND query?
        endif   ;ROMDOS

        je      fcOurs

;SR;
; We cannot do a far jump any more because cs cannot be used. Push the cs:ip
;onto the stack and do a far return to jump to the next 2fh handler.
;Our old ds is on the stack. We need to restore it but we cannot lose the
;current value of ds as it points at the data segment. So we do some kinky
;stack manipulations.
;
        push    ax
        push    ax                      ;create 2 words on stack for retf

        push    bp
        push    ax

        mov     bp,sp                   ;bp can be used to address stack
;
;Swap the old ds value with the second dummy word on the stack. Now, we can
;do a 'pop ds' at the end to restore our ds
;
        mov     ax,[bp+8]               ;ax = old ds value
        mov     [bp+4],ax

        mov     ax,word ptr ds:Int2fHandler+2
        mov     [bp+8],ax               ;put segment address
        mov     ax,word ptr ds:Int2fHandler
        mov     [bp+6],ax               ;put offset address


        pop     ax
        pop     bp
        pop     ds

        retf                            ;chain on to next handler

fcOurs:
;
;We have to clear ax, and return in ds:si a pointer to the stub jump table
;
        pop     ax                      ;discard ds currently on stack
        push    ds                      ;store our data segment

        mov     si,offset DATARES:Int2f_Entry ;start of table

        xor     ax,ax                   ;indicate COMMAND present
        jmp     short miRet             ;return to caller


miOurs:
        test    dl,1
        jnz     miRet                   ; ignore 'set' operations

        push    bx                      ; preserve BX
        mov     bx,dx
        xor     bh,bh                   ; BX = index in word table
        shl     bx,1                    ; BX = index in dword table
        les     di,MsgPtrLists[bx]              ; ES:DI = ptr to msg ptr list
        pop     bx                      ; restore BX

miRet:
        pop     ds
        assume  ds:nothing

        iret

MsgInt2fHandler endp




;***    MsgRetriever - message retrieval routine for utilities
;
;       Address of this routine is passed to utility programs via
;       message services int 2f.  We try to find the desired message
;       in memory or in our disk image.
;
;       ENTRY   AX = message #
;               DI = offset in RESGROUP of msg ptr list
;               ComSpec = asciiz pathname to our disk image
;
;       EXIT    CY clear for success
;               ES:DI = ptr to count byte, followed by message text
;
;               CY set for failure
;               ES,DI undefined
;
;       USED    flags
;
;       NOTE
;         The message # in AX is used to compute an offset into
;         the message ptr list pointed to by DI.  The lists must
;         start with message # 1 and proceed through consecutive
;         message #'s.
;
;         It is assumed that the msg ptr list is either ParsMsgPtrs or
;         ExtMsgPtrs.  We use NUMPARSEMSGS and NUMEXTMSGS to check for
;         valid message #.  ;M033
;
;         List positions with no corresponding message text are
;         indicated by null pointers, which this routine detects.

;SR; This routine will be called directly by the utilities. So, we have
; trap for it in the stub. The stub pushes the old value of ds and the
; DATARES value on the stack. We get them off the stack to setup ds here
;

MsgRetriever    proc    far

        assume  cs:CODERES,ds:NOTHING,es:NOTHING,ss:NOTHING

        pop     ds                      ;ds = DATARES
        assume  ds:DATARES

        push    ax                      ; preserve registers
        push    bx
        push    cx
        push    dx
        push    si


        push    ds                      ; get es from ds
        pop     es                      ; ES = DATARES seg addr

;       Begin modification M033.

;       Make sure msg # is valid.
;       Assume msg ptr list is either ParsMsgPtrs or ExtMsgPtrs.

        mov     bx,NUMPARSMSGS          ; BX = # parse error msgs in list
        cmp     di,offset DATARES:ParsMsgPtrs
        je      @f                      ; it's ParsMsgPtrs
        mov     bx,NUMEXTMSGS           ; BX = # extended error msgs in list
@@:     cmp     bx,ax
        jc      mrRet                   ; msg # too high, return carry

;       Msg # is valid.

;       End modification M033.

        dec     ax
        shl     ax,1                    ; AX = offset into msg ptr list
        add     di,ax                   ; DI = ptr to msg ptr

        cmp     di,ResMsgEnd
        jb      mrInMem                 ; ptr (and message) in memory

;*      Retrieve message from disk (or ROM) image.
;       Read once to get the ptr to the message, then again for the message.

ifndef  ROMDOS

        mov     si,offset DATARES:ComSpec       ; DS:SI = ptr to pathname
        mov     dx,EXT_EXISTS_OPEN              ; DX = 'open existing file'
        mov     bx,INT_24_ERROR                 ; BX = 'fail on crit error'
        mov     ax,EXTOPEN shl 8                ; AX = 'Extended Open File'
        int     21h                             ; call DOS
        jc      mrRet                           ; return failure

        mov     bx,ax                           ; BX = file handle
        mov     dx,di                           ; DX = ptr to msg ptr
        xor     si,si                           ; SI = read count
mrRead:
        sub     dx,100h                         ; DX = LSW of file offset
        xor     cx,cx                           ; CX = MSW of file offset
        mov     ax,LSEEK shl 8                  ; AX = 'Set File Pointer'
        int     21h                             ; call DOS
        jc      mrCloseFile                     ; handle error

        mov     dx,offset DATARES:MsgBuffer     ; DS:DX = input buffer
ifdef   DOSV                            ; ### if DOSV ###             ;dbcs50at;
;               enlarge message buffer size for bilingual message.    ;dbcs50at;
        extrn   LenMsgOrPathBuf : abs                                 ;dbcs50at;
        mov     cx, LenMsgOrPathBuf             ; CX = # bytes to read
else                                    ; ### else not DOSV ###       ;dbcs50at;
        mov     cx,64                           ; CX = # bytes to read
endif                                   ; ### endif DOSV ###          ;dbcs50at;
        mov     ah,READ                         ; AH = 'Read File'
        int     21h                             ; call DOS
        jc      mrCloseFile                     ; handle error

        or      si,si                           ; (CY cleared)
        jnz     mrCloseFile                     ; 2nd time thru - we're done
        inc     si                              ; mark one read done
        mov     dx,word ptr MsgBuffer           ; DX = ptr to message
        or      dx,dx
        jnz     mrRead                          ; go read the message
        stc                                     ; null ptr found- no msg

mrCloseFile:
        pushf                           ; save success/failure (CY)
        mov     ah,CLOSE                ; AH = 'Close File'
        int     21h                     ; call DOS
;       Bugbug: should we avoid this popf?
        popf                            ; CY = success/failure
        mov     di,dx                   ; ES:DI = ptr to msg, if successful
        jmp     short mrRet             ; we're done

else    ;ROMDOS

;       DI = ptr to msg ptr

        mov     si,di                   ; SI = ptr to msg ptr
        sub     si,100h                 ; SI = offset into image of msg ptr
        mov     cx,2                    ; CX = # bytes to copy from image

;       ASSUME ES:NOTHING is still in effect.

        push    ds
        pop     es                              ; ES = DATARES seg addr
        mov     di,offset DATARES:MsgBuffer     ; ES:DI = ptr to buffer
        invoke  LoadFromROM                     ; copy msg ptr from ROM
        mov     si,word ptr MsgBuffer           ; SI = ptr to message
        or      si,si
        jz      mrNoMsg                 ; null ptr- no message text

        sub     si,100h                 ; SI = offset into image of msg
        mov     cx,64                   ; CX = # bytes to copy from image
        mov     di,offset DATARES:MsgBuffer
        invoke  LoadFromROM
        clc                                     ; success
        mov     di,offset DATARES:MsgBuffer     ; ES:DI = ptr to msg
        jmp     short mrRet

mrNoMsg:
        stc
        jmp     short mrRet
        assume  es:NOTHING

endif   ;ROMDOS


;*      Message ptr is in memory.
;       If ptr is in memory, assume message is in memory (/msg).

mrInMem:
        mov     di,es:[di]              ; ES:DI = ptr to msg
        or      di,di                   ; (CY cleared)
        jnz     mrRet                   ; found message
        stc                             ; null ptr found- no message

mrRet:
        pop     si                      ;restore all registers
        pop     dx
        pop     cx
        pop     bx
        pop     ax

        pop     ds
        assume  ds:nothing

        ret

MsgRetriever    endp

;
; M003; Start of changes for UMB support
;


;***    Lh_OffUnlink -- Restore allocation strat and link state
;
;       ENTRY   al = Saved alloc strat and link state
;                       b0 = 1 if alloc strat to restore is HighFirst
;                       b1 = 1 if link state to restore is Linked
;
;       EXIT    None
;
;       USED    ax, bx, cx
;
;

public  Lh_OffUnlink
Lh_OffUnlink    proc    far

        mov     ch,al
        mov     cl,al
        mov     ax,(ALLOCOPER shl 8) OR 0
        int     21h
        mov     bx,ax
        ror     cl,1                            ;b7 = HighFirst bit
        and     cl,80h                          ;mask off b6-b0
        and     bl,7fh                          ;mask off HighFirst bit
        or      bl,cl                           ;set HighFirst bit state
        mov     ax,(ALLOCOPER shl 8) OR 1
        int     21h                             ;set alloc strat

        mov     bl,ch
        shr     bl,1
        xor     bh,bh                           ;bx = linkstate
        mov     ax,(ALLOCOPER shl 8) OR 3
        int     21h                             ;set linkstate

        ret

Lh_OffUnlink    endp

;
; M003; End of changes for UMB support
;

public  EndCode
EndCode label   byte

CODERES ends
        end
